dinput: Remove legacy joystick backends.

Signed-off-by: Rémi Bernon <rbernon@codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
Rémi Bernon 2021-10-11 10:10:57 +02:00 committed by Alexandre Julliard
parent 7011685e1e
commit adfee25b45
15 changed files with 6 additions and 5856 deletions

4
configure vendored
View File

@ -731,7 +731,6 @@ SYSTEMCONFIGURATION_LIBS
APPKIT_LIBS
CORESERVICES_LIBS
APPLICATIONSERVICES_LIBS
FORCEFEEDBACK_LIBS
IOKIT_LIBS
COREFOUNDATION_LIBS
OBJCPP
@ -8764,8 +8763,6 @@ ac_compiler_gnu=$ac_cv_c_compiler_gnu
IOKIT_LIBS="-framework IOKit -framework CoreFoundation"
FORCEFEEDBACK_LIBS="-framework ForceFeedback -framework CoreFoundation"
APPLICATIONSERVICES_LIBS="-framework ApplicationServices"
CORESERVICES_LIBS="-framework CoreServices"
@ -19615,7 +19612,6 @@ ALL_VARS_RULES="I386_LIBS = $I386_LIBS
OPENGL_LIBS = $OPENGL_LIBS
COREFOUNDATION_LIBS = $COREFOUNDATION_LIBS
IOKIT_LIBS = $IOKIT_LIBS
FORCEFEEDBACK_LIBS = $FORCEFEEDBACK_LIBS
APPLICATIONSERVICES_LIBS = $APPLICATIONSERVICES_LIBS
CORESERVICES_LIBS = $CORESERVICES_LIBS
APPKIT_LIBS = $APPKIT_LIBS

View File

@ -740,7 +740,6 @@ case $host_os in
dnl declare needed frameworks
AC_SUBST(COREFOUNDATION_LIBS,"-framework CoreFoundation")
AC_SUBST(IOKIT_LIBS,"-framework IOKit -framework CoreFoundation")
AC_SUBST(FORCEFEEDBACK_LIBS,"-framework ForceFeedback -framework CoreFoundation")
AC_SUBST(APPLICATIONSERVICES_LIBS,"-framework ApplicationServices")
AC_SUBST(CORESERVICES_LIBS,"-framework CoreServices")
AC_SUBST(APPKIT_LIBS,"-framework AppKit")

View File

@ -2,7 +2,6 @@ MODULE = dinput.dll
IMPORTLIB = dinput
IMPORTS = dinput dxguid uuid comctl32 ole32 user32 advapi32 hid setupapi
EXTRADEFS = -DDIRECTINPUT_VERSION=0x0700
EXTRALIBS = $(IOKIT_LIBS) $(FORCEFEEDBACK_LIBS)
EXTRADLLFLAGS = -mcygwin
@ -12,12 +11,7 @@ C_SRCS = \
data_formats.c \
device.c \
dinput_main.c \
effect_linuxinput.c \
joystick.c \
joystick_hid.c \
joystick_linux.c \
joystick_linuxinput.c \
joystick_osx.c \
keyboard.c \
mouse.c

View File

@ -187,11 +187,6 @@ void _dump_DIPROPHEADER(LPCDIPROPHEADER diph) {
}
}
void _dump_OBJECTINSTANCEA(const DIDEVICEOBJECTINSTANCEA *ddoi) {
TRACE(" - enumerating : %s ('%s') - %2d - 0x%08x - %s - 0x%x\n",
debugstr_guid(&ddoi->guidType), _dump_dinput_GUID(&ddoi->guidType), ddoi->dwOfs, ddoi->dwType, ddoi->tszName, ddoi->dwFlags);
}
void _dump_OBJECTINSTANCEW(const DIDEVICEOBJECTINSTANCEW *ddoi) {
TRACE(" - enumerating : %s ('%s'), - %2d - 0x%08x - %s - 0x%x\n",
debugstr_guid(&ddoi->guidType), _dump_dinput_GUID(&ddoi->guidType), ddoi->dwOfs, ddoi->dwType, debugstr_w(ddoi->tszName), ddoi->dwFlags);
@ -437,7 +432,7 @@ void fill_DataFormat(void *out, DWORD size, const void *in, const DataFormat *df
}
}
void release_DataFormat(DataFormat * format)
static void release_DataFormat( DataFormat *format )
{
TRACE("Deleting DataFormat: %p\n", format);
@ -637,20 +632,7 @@ static int verify_offset(const DataFormat *df, int offset)
return -1;
}
/* find an object by its offset in a data format */
static int offset_to_object(const DataFormat *df, int offset)
{
int i;
if (!df->offsets) return -1;
for (i = 0; i < df->wine_df->dwNumObjs; i++)
if (df->offsets[i] == offset) return i;
return -1;
}
int id_to_object(LPCDIDATAFORMAT df, int id)
static int id_to_object( LPCDIDATAFORMAT df, int id )
{
int i;
@ -669,18 +651,6 @@ static int id_to_offset(const DataFormat *df, int id)
return obj >= 0 && df->offsets ? df->offsets[obj] : -1;
}
int find_property(const DataFormat *df, LPCDIPROPHEADER ph)
{
switch (ph->dwHow)
{
case DIPH_BYID: return id_to_object(df->wine_df, ph->dwObj);
case DIPH_BYOFFSET: return offset_to_object(df, ph->dwObj);
}
FIXME("Unhandled ph->dwHow=='%04X'\n", (unsigned int)ph->dwHow);
return -1;
}
static DWORD semantic_to_obj_id(IDirectInputDeviceImpl* This, DWORD dwSemantic)
{
DWORD type = (0x0000ff00 & dwSemantic) >> 8;

View File

@ -102,43 +102,16 @@ extern BOOL device_instance_is_disabled( DIDEVICEINSTANCEW *instance, BOOL *over
/* Routines to do DataFormat / WineFormat conversions */
extern void fill_DataFormat(void *out, DWORD size, const void *in, const DataFormat *df) DECLSPEC_HIDDEN;
extern void release_DataFormat(DataFormat *df) DECLSPEC_HIDDEN;
extern void queue_event( IDirectInputDevice8W *iface, int inst_id, DWORD data, DWORD time, DWORD seq ) DECLSPEC_HIDDEN;
/* Helper functions to work with data format */
extern int id_to_object(LPCDIDATAFORMAT df, int id) DECLSPEC_HIDDEN;
extern int find_property(const DataFormat *df, LPCDIPROPHEADER ph) DECLSPEC_HIDDEN;
/* Common joystick stuff */
typedef struct
{
LONG lDevMin;
LONG lDevMax;
LONG lMin;
LONG lMax;
LONG lDeadZone;
LONG lSaturation;
} ObjProps;
extern DWORD joystick_map_pov(const POINTL *p) DECLSPEC_HIDDEN;
extern LONG joystick_map_axis(ObjProps *props, int val) DECLSPEC_HIDDEN;
typedef struct
{
struct list entry;
LPDIRECTINPUTEFFECT ref;
} effect_list_item;
extern const GUID dinput_pidvid_guid DECLSPEC_HIDDEN;
/* Various debug tools */
extern void _dump_DIPROPHEADER(LPCDIPROPHEADER diph) DECLSPEC_HIDDEN;
extern void _dump_OBJECTINSTANCEA(const DIDEVICEOBJECTINSTANCEA *ddoi) DECLSPEC_HIDDEN;
extern void _dump_OBJECTINSTANCEW(const DIDEVICEOBJECTINSTANCEW *ddoi) DECLSPEC_HIDDEN;
extern void _dump_DIDATAFORMAT(const DIDATAFORMAT *df) DECLSPEC_HIDDEN;
extern const char *_dump_dinput_GUID(const GUID *guid) DECLSPEC_HIDDEN;
extern LPDIOBJECTDATAFORMAT dataformat_to_odf_by_type(LPCDIDATAFORMAT df, int n, DWORD type) DECLSPEC_HIDDEN;
extern HRESULT _build_action_map(LPDIRECTINPUTDEVICE8W iface, LPDIACTIONFORMATW lpdiaf, LPCWSTR lpszUserName, DWORD dwFlags, DWORD devMask, LPCDIDATAFORMAT df) DECLSPEC_HIDDEN;
extern HRESULT _set_action_map(LPDIRECTINPUTDEVICE8W iface, LPDIACTIONFORMATW lpdiaf, LPCWSTR lpszUserName, DWORD dwFlags, LPCDIDATAFORMAT df) DECLSPEC_HIDDEN;

View File

@ -78,9 +78,6 @@ static const struct dinput_device *dinput_devices[] =
{
&mouse_device,
&keyboard_device,
&joystick_linuxinput_device,
&joystick_linux_device,
&joystick_osx_device,
&joystick_hid_device,
};

View File

@ -68,9 +68,6 @@ struct DevicePlayer {
extern const struct dinput_device mouse_device DECLSPEC_HIDDEN;
extern const struct dinput_device keyboard_device DECLSPEC_HIDDEN;
extern const struct dinput_device joystick_hid_device DECLSPEC_HIDDEN;
extern const struct dinput_device joystick_linux_device DECLSPEC_HIDDEN;
extern const struct dinput_device joystick_linuxinput_device DECLSPEC_HIDDEN;
extern const struct dinput_device joystick_osx_device DECLSPEC_HIDDEN;
extern void dinput_hooks_acquire_device(LPDIRECTINPUTDEVICE8W iface);
extern void dinput_hooks_unacquire_device(LPDIRECTINPUTDEVICE8W iface);

View File

@ -1,906 +0,0 @@
/* DirectInput Linux Event Device Effect
*
* Copyright 2005 Daniel Remenak
*
* Thanks to Google's Summer of Code Program (2005)
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include "config.h"
#ifdef HAVE_STRUCT_FF_EFFECT_DIRECTION
#include <stdarg.h>
#include <string.h>
#ifdef HAVE_LINUX_INPUT_H
# include <linux/input.h>
# undef SW_MAX
#endif
#include <limits.h>
#include <errno.h>
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif
#include <math.h>
#include "wine/debug.h"
#include "wine/unicode.h"
#include "windef.h"
#include "winbase.h"
#include "winerror.h"
#include "dinput.h"
#include "device_private.h"
#include "joystick_private.h"
WINE_DEFAULT_DEBUG_CHANNEL(dinput);
static const IDirectInputEffectVtbl LinuxInputEffectVtbl;
typedef struct LinuxInputEffectImpl LinuxInputEffectImpl;
struct LinuxInputEffectImpl
{
IDirectInputEffect IDirectInputEffect_iface;
LONG ref;
GUID guid;
struct ff_effect effect; /* Effect data */
int gain; /* Effect gain */
BOOL first_axis_is_x;
int* fd; /* Parent device */
struct list *entry; /* Entry into the parent's list of effects */
};
static inline LinuxInputEffectImpl *impl_from_IDirectInputEffect(IDirectInputEffect *iface)
{
return CONTAINING_RECORD(iface, LinuxInputEffectImpl, IDirectInputEffect_iface);
}
static double ff_effect_direction_to_rad(unsigned int dir)
{
return (dir & 0xffff) * M_PI / 0x8000;
}
static void ff_dump_effect(struct ff_effect *effect)
{
const char *type = "(Unknown)", *length = "INFINITE";
struct ff_envelope *env = NULL;
double angle;
#define FE(x) case x: type = #x; break
switch (effect->type)
{
FE(FF_RUMBLE);
FE(FF_PERIODIC);
FE(FF_CONSTANT);
FE(FF_SPRING);
FE(FF_FRICTION);
FE(FF_DAMPER);
FE(FF_INERTIA);
FE(FF_RAMP);
}
#undef FE
/* rotate so 0 points right */
angle = 360 - ff_effect_direction_to_rad(effect->direction + 0xc000) * 180 / M_PI;
if (effect->replay.length)
length = wine_dbg_sprintf("%u ms", effect->replay.length);
TRACE("type 0x%x %s, id %d, direction 0x%x (source angle %.2f), time length %s, start delay %u ms\n",
effect->type, type, effect->id, effect->direction, angle, length, effect->replay.delay);
if (effect->trigger.button || effect->trigger.interval)
TRACE(" -> trigger button %u, re-trigger interval %u ms\n",
effect->trigger.button, effect->trigger.interval);
if (effect->type == FF_PERIODIC)
{
struct ff_periodic_effect *per = &effect->u.periodic;
const char *wave = "(Unknown)";
#define FE(x) case x: wave = #x; break
switch (per->waveform)
{
FE(FF_SQUARE);
FE(FF_TRIANGLE);
FE(FF_SINE);
FE(FF_SAW_UP);
FE(FF_SAW_DOWN);
FE(FF_CUSTOM);
}
#undef FE
angle = ff_effect_direction_to_rad(per->phase) * 180 / M_PI;
TRACE(" -> waveform 0x%x %s, period %u ms, magnitude %d, offset %d, phase 0x%x (angle %.2f), custom len %d\n",
per->waveform, wave, per->period, per->magnitude, per->offset, per->phase, angle, per->custom_len);
env = &per->envelope;
}
else if (effect->type == FF_CONSTANT)
{
struct ff_constant_effect *cons = &effect->u.constant;
TRACE(" -> level %d\n", cons->level);
env = &cons->envelope;
}
else if (effect->type == FF_RAMP)
{
struct ff_ramp_effect *ramp = &effect->u.ramp;
TRACE(" -> start/end level %d/%d\n", ramp->start_level, ramp->end_level);
env = &ramp->envelope;
}
else if (effect->type == FF_RUMBLE)
{
struct ff_rumble_effect *rumble = &effect->u.rumble;
TRACE(" -> strong/weak magnitude %u/%u\n", rumble->strong_magnitude, rumble->weak_magnitude);
}
else if (effect->type == FF_SPRING || effect->type == FF_FRICTION ||
effect->type == FF_DAMPER || effect->type == FF_INERTIA)
{
struct ff_condition_effect *cond = effect->u.condition;
int i;
for (i = 0; i < 2; i++)
{
/* format numbers here to make them align correctly */
TRACE(" -> [%d] right/left saturation %5u/%5u, right/left coefficient %5d/%5d,"
" deadband %5u, center %5d\n", i, cond[i].right_saturation, cond[i].left_saturation,
cond[i].right_coeff, cond[i].left_coeff, cond[i].deadband, cond[i].center);
}
}
if (env)
TRACE(" -> envelope attack length(ms)/level %u/%u, fade length(ms)/level %u/%u\n",
env->attack_length, env->attack_level, env->fade_length, env->fade_level);
}
/******************************************************************************
* LinuxInputEffectImpl
*/
static ULONG WINAPI LinuxInputEffectImpl_AddRef(
LPDIRECTINPUTEFFECT iface)
{
LinuxInputEffectImpl *This = impl_from_IDirectInputEffect(iface);
ULONG ref = InterlockedIncrement(&This->ref);
TRACE( "(%p) ref %d\n", This, ref );
return ref;
}
static HRESULT WINAPI LinuxInputEffectImpl_Download(
LPDIRECTINPUTEFFECT iface)
{
LinuxInputEffectImpl *This = impl_from_IDirectInputEffect(iface);
int ret, old_effect_id;
TRACE("(this=%p)\n", This);
ff_dump_effect(&This->effect);
old_effect_id = This->effect.id;
if (ioctl(*(This->fd), EVIOCSFF, &This->effect) != -1)
return DI_OK;
/* Linux kernel < 3.14 has a bug that incorrectly assigns an effect ID even
* on error, restore it here if that is the case. */
This->effect.id = old_effect_id;
switch (errno)
{
case EINVAL:
ret = DIERR_INVALIDPARAM;
break;
case ENOSPC:
ret = DIERR_DEVICEFULL;
break;
case ENOMEM:
ret = DIERR_OUTOFMEMORY;
break;
default:
ret = DIERR_INPUTLOST;
break;
}
TRACE("Could not upload effect to fd %d, errno %d \"%s\", returning 0x%x.\n",
*This->fd, errno, strerror(errno), ret);
return ret;
}
static HRESULT WINAPI LinuxInputEffectImpl_Escape(
LPDIRECTINPUTEFFECT iface,
LPDIEFFESCAPE pesc)
{
WARN("(this=%p,%p): invalid: no hardware-specific escape codes in this"
" driver!\n", iface, pesc);
return DI_OK;
}
static HRESULT WINAPI LinuxInputEffectImpl_GetEffectGuid(
LPDIRECTINPUTEFFECT iface,
LPGUID pguid)
{
LinuxInputEffectImpl *This = impl_from_IDirectInputEffect(iface);
TRACE("(this=%p,%p)\n", This, pguid);
*pguid = This->guid;
return DI_OK;
}
static HRESULT WINAPI LinuxInputEffectImpl_GetEffectStatus(
LPDIRECTINPUTEFFECT iface,
LPDWORD pdwFlags)
{
LinuxInputEffectImpl *This = impl_from_IDirectInputEffect(iface);
TRACE("(this=%p,%p)\n", This, pdwFlags);
if (!pdwFlags)
return E_POINTER;
if (This->effect.id == -1)
return DIERR_NOTDOWNLOADED;
/* linux sends the effect status through an event.
* that event is trapped by our parent joystick driver
* and there is no clean way to pass it back to us. */
FIXME("Not enough information to provide a status.\n");
(*pdwFlags) = 0;
return DI_OK;
}
static HRESULT WINAPI LinuxInputEffectImpl_GetParameters(
LPDIRECTINPUTEFFECT iface,
LPDIEFFECT peff,
DWORD dwFlags)
{
HRESULT diErr = DI_OK;
LinuxInputEffectImpl *This = impl_from_IDirectInputEffect(iface);
TRACE("(this=%p,%p,%d)\n", This, peff, dwFlags);
/* Major conversion factors are:
* times: millisecond (linux) -> microsecond (windows) (x * 1000)
* forces: scale 0x7FFF (linux) -> scale 10000 (windows) approx ((x / 33) * 10)
* angles: scale 0x7FFF (linux) -> scale 35999 (windows) approx ((x / 33) * 36)
* angle bases: 0 -> -y (down) (linux) -> 0 -> +x (right) (windows)
*/
if (dwFlags & DIEP_AXES) {
if (peff->cAxes < 2 /* linuxinput effects always use 2 axes, x and y */)
diErr = DIERR_MOREDATA;
peff->cAxes = 2;
if (diErr)
return diErr;
else {
peff->rgdwAxes[0] = DIJOFS_X;
peff->rgdwAxes[1] = DIJOFS_Y;
}
}
if (dwFlags & DIEP_DIRECTION) {
if (peff->cAxes < 2)
diErr = DIERR_MOREDATA;
peff->cAxes = 2;
if (diErr)
return diErr;
else {
if (peff->dwFlags & DIEFF_CARTESIAN) {
/* rotate so 0 points right */
double angle = ff_effect_direction_to_rad(This->effect.direction + 0xc000);
peff->rglDirection[0] = sin(angle) * 1000;
peff->rglDirection[1] = -cos(angle) * 1000;
} else {
/* Polar and spherical coordinates are the same for two or less
* axes.
* Note that we also use this case if NO flags are marked.
* According to MSDN, we should return the direction in the
* format that it was specified in, if no flags are marked.
*/
peff->rglDirection[0] = (This->effect.direction / 33) * 36 + 9000;
if (peff->rglDirection[0] > 35999)
peff->rglDirection[0] -= 35999;
}
}
}
if (dwFlags & DIEP_DURATION)
{
if (!This->effect.replay.length) /* infinite for the linux driver */
peff->dwDuration = INFINITE;
else
peff->dwDuration = (DWORD)This->effect.replay.length * 1000;
}
if (dwFlags & DIEP_ENVELOPE) {
struct ff_envelope* env;
if (This->effect.type == FF_CONSTANT) env = &This->effect.u.constant.envelope;
else if (This->effect.type == FF_PERIODIC) env = &This->effect.u.periodic.envelope;
else if (This->effect.type == FF_RAMP) env = &This->effect.u.ramp.envelope;
else env = NULL;
if (env == NULL) {
peff->lpEnvelope = NULL;
} else if (peff->lpEnvelope == NULL) {
return DIERR_INVALIDPARAM;
} else {
peff->lpEnvelope->dwAttackLevel = (env->attack_level / 33) * 10;
peff->lpEnvelope->dwAttackTime = env->attack_length * 1000;
peff->lpEnvelope->dwFadeLevel = (env->fade_level / 33) * 10;
peff->lpEnvelope->dwFadeTime = env->fade_length * 1000;
}
}
if (dwFlags & DIEP_GAIN) {
peff->dwGain = This->gain * 10000 / 0xFFFF;
}
if (dwFlags & DIEP_SAMPLEPERIOD) {
/* the linux input ff driver has no support for setting
* the playback sample period. 0 means default. */
peff->dwSamplePeriod = 0;
}
if ((dwFlags & DIEP_STARTDELAY) && peff->dwSize > sizeof(DIEFFECT_DX5))
peff->dwStartDelay = This->effect.replay.delay * 1000;
if (dwFlags & DIEP_TRIGGERBUTTON) {
FIXME("LinuxInput button mapping needs redoing; for now, assuming we're using an actual joystick.\n");
peff->dwTriggerButton = DIJOFS_BUTTON(This->effect.trigger.button - BTN_JOYSTICK);
}
if (dwFlags & DIEP_TRIGGERREPEATINTERVAL) {
peff->dwTriggerRepeatInterval = This->effect.trigger.interval * 1000;
}
if (dwFlags & DIEP_TYPESPECIFICPARAMS) {
DWORD expectedsize = 0;
if (This->effect.type == FF_PERIODIC) {
expectedsize = sizeof(DIPERIODIC);
} else if (This->effect.type == FF_CONSTANT) {
expectedsize = sizeof(DICONSTANTFORCE);
} else if (This->effect.type == FF_SPRING
|| This->effect.type == FF_FRICTION
|| This->effect.type == FF_INERTIA
|| This->effect.type == FF_DAMPER) {
expectedsize = sizeof(DICONDITION) * 2;
} else if (This->effect.type == FF_RAMP) {
expectedsize = sizeof(DIRAMPFORCE);
}
if (expectedsize > peff->cbTypeSpecificParams)
diErr = DIERR_MOREDATA;
peff->cbTypeSpecificParams = expectedsize;
if (diErr)
return diErr;
else {
if (This->effect.type == FF_PERIODIC) {
LPDIPERIODIC tsp = peff->lpvTypeSpecificParams;
tsp->dwMagnitude = (This->effect.u.periodic.magnitude / 33) * 10;
tsp->lOffset = (This->effect.u.periodic.offset / 33) * 10;
tsp->dwPhase = (This->effect.u.periodic.phase / 33) * 36;
tsp->dwPeriod = (This->effect.u.periodic.period * 1000);
} else if (This->effect.type == FF_CONSTANT) {
LPDICONSTANTFORCE tsp = peff->lpvTypeSpecificParams;
tsp->lMagnitude = (This->effect.u.constant.level / 33) * 10;
} else if (This->effect.type == FF_SPRING
|| This->effect.type == FF_FRICTION
|| This->effect.type == FF_INERTIA
|| This->effect.type == FF_DAMPER) {
LPDICONDITION tsp = peff->lpvTypeSpecificParams;
int i;
for (i = 0; i < 2; ++i) {
tsp[i].lOffset = (This->effect.u.condition[i].center / 33) * 10;
tsp[i].lPositiveCoefficient = (This->effect.u.condition[i].right_coeff / 33) * 10;
tsp[i].lNegativeCoefficient = (This->effect.u.condition[i].left_coeff / 33) * 10;
tsp[i].dwPositiveSaturation = (This->effect.u.condition[i].right_saturation / 33) * 10;
tsp[i].dwNegativeSaturation = (This->effect.u.condition[i].left_saturation / 33) * 10;
tsp[i].lDeadBand = (This->effect.u.condition[i].deadband / 33) * 10;
}
} else if (This->effect.type == FF_RAMP) {
LPDIRAMPFORCE tsp = peff->lpvTypeSpecificParams;
tsp->lStart = (This->effect.u.ramp.start_level / 33) * 10;
tsp->lEnd = (This->effect.u.ramp.end_level / 33) * 10;
}
}
}
return diErr;
}
static HRESULT WINAPI LinuxInputEffectImpl_Initialize(
LPDIRECTINPUTEFFECT iface,
HINSTANCE hinst,
DWORD dwVersion,
REFGUID rguid)
{
FIXME("(this=%p,%p,%d,%s): stub!\n",
iface, hinst, dwVersion, debugstr_guid(rguid));
return DI_OK;
}
static HRESULT WINAPI LinuxInputEffectImpl_QueryInterface(
LPDIRECTINPUTEFFECT iface,
REFIID riid,
void **ppvObject)
{
LinuxInputEffectImpl *This = impl_from_IDirectInputEffect(iface);
TRACE("(this=%p,%s,%p)\n", This, debugstr_guid(riid), ppvObject);
if (IsEqualGUID(&IID_IUnknown, riid) ||
IsEqualGUID(&IID_IDirectInputEffect, riid)) {
LinuxInputEffectImpl_AddRef(iface);
*ppvObject = This;
return 0;
}
TRACE("Unsupported interface!\n");
return E_FAIL;
}
static HRESULT WINAPI LinuxInputEffectImpl_Start(
LPDIRECTINPUTEFFECT iface,
DWORD dwIterations,
DWORD dwFlags)
{
struct input_event event;
LinuxInputEffectImpl *This = impl_from_IDirectInputEffect(iface);
TRACE("(this=%p,%d,%d)\n", This, dwIterations, dwFlags);
if (!(dwFlags & DIES_NODOWNLOAD)) {
/* Download the effect if necessary */
if (This->effect.id == -1) {
HRESULT res = LinuxInputEffectImpl_Download(iface);
if (res != DI_OK)
return res;
}
}
if (dwFlags & DIES_SOLO) {
FIXME("Solo mode requested: should be stopping all effects here!\n");
}
event.type = EV_FF;
event.code = This->effect.id;
event.value = min( dwIterations, INT_MAX );
if (write(*(This->fd), &event, sizeof(event)) == -1) {
FIXME("Unable to write event. Assuming device disconnected.\n");
return DIERR_INPUTLOST;
}
return DI_OK;
}
static HRESULT WINAPI LinuxInputEffectImpl_SetParameters(
LPDIRECTINPUTEFFECT iface,
LPCDIEFFECT peff,
DWORD dwFlags)
{
LinuxInputEffectImpl *This = impl_from_IDirectInputEffect(iface);
DWORD type = typeFromGUID(&This->guid);
HRESULT retval = DI_OK;
TRACE("(this=%p,%p,%d)\n", This, peff, dwFlags);
dump_DIEFFECT(peff, &This->guid, dwFlags);
if (!dwFlags)
return DI_NOEFFECT;
if (dwFlags & DIEP_AXES) {
if (!(peff->rgdwAxes))
return DIERR_INVALIDPARAM;
/* the linux input effect system only supports one or two axes */
if (peff->cAxes > 2)
return DIERR_INVALIDPARAM;
else if (peff->cAxes < 1)
return DIERR_INCOMPLETEEFFECT;
This->first_axis_is_x = peff->rgdwAxes[0] == DIJOFS_X;
}
/* some of this may look funky, but it's 'cause the linux driver and directx have
* different opinions about which way direction "0" is. directx has 0 along the x
* axis (left), linux has it along the y axis (down). */
if (dwFlags & DIEP_DIRECTION) {
if (!(peff->rglDirection))
return DIERR_INVALIDPARAM;
if (peff->cAxes == 1) {
if (peff->dwFlags & DIEFF_CARTESIAN) {
if (dwFlags & DIEP_AXES) {
if (peff->rgdwAxes[0] == DIJOFS_X && peff->rglDirection[0] >= 0)
This->effect.direction = 0x4000;
else if (peff->rgdwAxes[0] == DIJOFS_X && peff->rglDirection[0] < 0)
This->effect.direction = 0xC000;
else if (peff->rgdwAxes[0] == DIJOFS_Y && peff->rglDirection[0] >= 0)
This->effect.direction = 0;
else if (peff->rgdwAxes[0] == DIJOFS_Y && peff->rglDirection[0] < 0)
This->effect.direction = 0x8000;
}
} else {
/* one-axis effects must use cartesian coords */
return DIERR_INVALIDPARAM;
}
}
/* two axes */
else
{
if (peff->dwFlags & DIEFF_CARTESIAN)
{
LONG x, y;
if (This->first_axis_is_x)
{
x = peff->rglDirection[0];
y = peff->rglDirection[1];
}
else
{
x = peff->rglDirection[1];
y = peff->rglDirection[0];
}
This->effect.direction = (unsigned int)((M_PI / 2 + atan2(y, x)) * 0x8000 / M_PI);
}
else
{
/* Polar and spherical are the same for 2 axes */
/* Precision is important here, so we do double math with exact constants */
This->effect.direction = (unsigned int)(((double)peff->rglDirection[0] / 18000) * 0x8000);
}
}
}
if (dwFlags & DIEP_DURATION)
{
if (peff->dwDuration == INFINITE)
This->effect.replay.length = 0; /* infinite for the linux driver */
else if(peff->dwDuration > 1000)
This->effect.replay.length = peff->dwDuration / 1000;
else
This->effect.replay.length = 1;
}
if (dwFlags & DIEP_ENVELOPE)
{
struct ff_envelope* env;
if (This->effect.type == FF_CONSTANT)
env = &This->effect.u.constant.envelope;
else if (This->effect.type == FF_PERIODIC)
env = &This->effect.u.periodic.envelope;
else if (This->effect.type == FF_RAMP)
env = &This->effect.u.ramp.envelope;
else
env = NULL;
/* copy the envelope if it is present and the linux effect supports it */
if (peff->lpEnvelope && env)
{
env->attack_length = peff->lpEnvelope->dwAttackTime / 1000;
env->attack_level = (peff->lpEnvelope->dwAttackLevel / 10) * 32;
env->fade_length = peff->lpEnvelope->dwFadeTime / 1000;
env->fade_level = (peff->lpEnvelope->dwFadeLevel / 10) * 32;
}
/* if the dinput envelope is NULL we will clear the linux envelope */
else if (env)
{
env->attack_length = 0;
env->attack_level = 0;
env->fade_length = 0;
env->fade_level = 0;
}
else if(peff->lpEnvelope)
{
if(peff->lpEnvelope->dwAttackTime || peff->lpEnvelope->dwAttackLevel ||
peff->lpEnvelope->dwFadeTime || peff->lpEnvelope->dwFadeLevel)
WARN("Ignoring dinput envelope not supported in the linux effect\n");
}
}
/* Gain and Sample Period settings are not supported by the linux
* event system */
if (dwFlags & DIEP_GAIN) {
This->gain = 0xFFFF * peff->dwGain / 10000;
TRACE("Effect gain requested but no effect gain functionality present.\n");
}
if (dwFlags & DIEP_SAMPLEPERIOD)
TRACE("Sample period requested but no sample period functionality present.\n");
if (dwFlags & DIEP_STARTDELAY)
if ((dwFlags & DIEP_STARTDELAY) && peff->dwSize > sizeof(DIEFFECT_DX5))
This->effect.replay.delay = peff->dwStartDelay / 1000;
if (dwFlags & DIEP_TRIGGERBUTTON) {
if (peff->dwTriggerButton != -1) {
FIXME("Linuxinput button mapping needs redoing, assuming we're using a joystick.\n");
FIXME("Trigger button translation not yet implemented!\n");
}
This->effect.trigger.button = 0;
}
if (dwFlags & DIEP_TRIGGERREPEATINTERVAL)
This->effect.trigger.interval = peff->dwTriggerRepeatInterval / 1000;
if (dwFlags & DIEP_TYPESPECIFICPARAMS)
{
if (!(peff->lpvTypeSpecificParams))
return DIERR_INVALIDPARAM;
if (type == DIEFT_PERIODIC)
{
DIPERIODIC *tsp;
if (peff->cbTypeSpecificParams != sizeof(DIPERIODIC))
return DIERR_INVALIDPARAM;
tsp = peff->lpvTypeSpecificParams;
This->effect.u.periodic.magnitude = (tsp->dwMagnitude / 10) * 32;
This->effect.u.periodic.offset = (tsp->lOffset / 10) * 32;
/* phase ranges from 0 - 35999 in dinput and 0 - 65535 on Linux */
This->effect.u.periodic.phase = (tsp->dwPhase / 36) * 65;
/* dinput uses microseconds, Linux uses milliseconds */
if (tsp->dwPeriod <= 1000)
This->effect.u.periodic.period = 1;
else
This->effect.u.periodic.period = tsp->dwPeriod / 1000;
}
else if (type == DIEFT_CONSTANTFORCE)
{
LPCDICONSTANTFORCE tsp;
if (peff->cbTypeSpecificParams != sizeof(DICONSTANTFORCE))
return DIERR_INVALIDPARAM;
tsp = peff->lpvTypeSpecificParams;
This->effect.u.constant.level = (max(min(tsp->lMagnitude, 10000), -10000) / 10) * 32;
} else if (type == DIEFT_RAMPFORCE) {
LPCDIRAMPFORCE tsp;
if (peff->cbTypeSpecificParams != sizeof(DIRAMPFORCE))
return DIERR_INVALIDPARAM;
tsp = peff->lpvTypeSpecificParams;
This->effect.u.ramp.start_level = (tsp->lStart / 10) * 32;
This->effect.u.ramp.end_level = (tsp->lEnd / 10) * 32;
}
else if (type == DIEFT_CONDITION)
{
DICONDITION *tsp = peff->lpvTypeSpecificParams;
struct ff_condition_effect *cond = This->effect.u.condition;
int i, j, sources;
double factor[2];
if (peff->cbTypeSpecificParams == sizeof(DICONDITION))
{
/* One condition block. This needs to be rotated to direction,
* and expanded to separate x and y conditions. Ensures 0 points right */
double angle = ff_effect_direction_to_rad(This->effect.direction + 0xc000);
factor[0] = sin(angle);
factor[1] = -cos(angle);
sources = 1;
}
else if (peff->cbTypeSpecificParams == 2 * sizeof(DICONDITION))
{
/* Direct parameter copy without changes */
factor[0] = factor[1] = 1;
sources = 2;
}
else
return DIERR_INVALIDPARAM;
for (i = j = 0; i < 2; ++i)
{
cond[i].center = (int)(factor[i] * (tsp[j].lOffset / 10) * 32);
cond[i].right_coeff = (int)(factor[i] * (tsp[j].lPositiveCoefficient / 10) * 32);
cond[i].left_coeff = (int)(factor[i] * (tsp[j].lNegativeCoefficient / 10) * 32);
cond[i].right_saturation = (int)(factor[i] * (tsp[j].dwPositiveSaturation / 10) * 65);
cond[i].left_saturation = (int)(factor[i] * (tsp[j].dwNegativeSaturation / 10) * 65);
cond[i].deadband = (int)(factor[i] * (tsp[j].lDeadBand / 10) * 32);
if (sources == 2)
j++;
}
}
else
{
FIXME("Custom force types are not supported\n");
return DIERR_INVALIDPARAM;
}
}
if (!(dwFlags & DIEP_NODOWNLOAD))
retval = LinuxInputEffectImpl_Download(iface);
if (retval != DI_OK)
return DI_DOWNLOADSKIPPED;
if (dwFlags & DIEP_NORESTART)
TRACE("DIEP_NORESTART: not handled (we have no control of that).\n");
if (dwFlags & DIEP_START)
retval = LinuxInputEffectImpl_Start(iface, 1, 0);
if (retval != DI_OK)
return retval;
return DI_OK;
}
static HRESULT WINAPI LinuxInputEffectImpl_Stop(
LPDIRECTINPUTEFFECT iface)
{
struct input_event event;
LinuxInputEffectImpl *This = impl_from_IDirectInputEffect(iface);
TRACE("(this=%p)\n", This);
event.type = EV_FF;
event.code = This->effect.id;
event.value = 0;
/* we don't care about the success or failure of this call */
write(*(This->fd), &event, sizeof(event));
return DI_OK;
}
static HRESULT WINAPI LinuxInputEffectImpl_Unload(
LPDIRECTINPUTEFFECT iface)
{
LinuxInputEffectImpl *This = impl_from_IDirectInputEffect(iface);
TRACE("(this=%p)\n", This);
/* Erase the downloaded effect */
if (ioctl(*(This->fd), EVIOCRMFF, This->effect.id) == -1)
return DIERR_INVALIDPARAM;
/* Mark the effect as deallocated */
This->effect.id = -1;
return DI_OK;
}
static ULONG WINAPI LinuxInputEffectImpl_Release(LPDIRECTINPUTEFFECT iface)
{
LinuxInputEffectImpl *This = impl_from_IDirectInputEffect(iface);
ULONG ref = InterlockedDecrement(&(This->ref));
TRACE( "(%p) ref %d\n", This, ref );
if (ref == 0)
{
LinuxInputEffectImpl_Stop(iface);
LinuxInputEffectImpl_Unload(iface);
list_remove(This->entry);
HeapFree(GetProcessHeap(), 0, LIST_ENTRY(This->entry, effect_list_item, entry));
HeapFree(GetProcessHeap(), 0, This);
}
return ref;
}
/******************************************************************************
* LinuxInputEffect
*/
DECLSPEC_HIDDEN HRESULT linuxinput_create_effect(
int* fd,
REFGUID rguid,
struct list *parent_list_entry,
LPDIRECTINPUTEFFECT* peff)
{
LinuxInputEffectImpl* newEffect = HeapAlloc(GetProcessHeap(),
HEAP_ZERO_MEMORY, sizeof(LinuxInputEffectImpl));
DWORD type = typeFromGUID(rguid);
newEffect->IDirectInputEffect_iface.lpVtbl = &LinuxInputEffectVtbl;
newEffect->ref = 1;
newEffect->guid = *rguid;
newEffect->fd = fd;
newEffect->gain = 0xFFFF;
/* set the type. this cannot be changed over the effect's life. */
switch (type) {
case DIEFT_PERIODIC:
newEffect->effect.type = FF_PERIODIC;
if (IsEqualGUID(rguid, &GUID_Sine)) {
newEffect->effect.u.periodic.waveform = FF_SINE;
} else if (IsEqualGUID(rguid, &GUID_Triangle)) {
newEffect->effect.u.periodic.waveform = FF_TRIANGLE;
} else if (IsEqualGUID(rguid, &GUID_Square)) {
newEffect->effect.u.periodic.waveform = FF_SQUARE;
} else if (IsEqualGUID(rguid, &GUID_SawtoothUp)) {
newEffect->effect.u.periodic.waveform = FF_SAW_UP;
} else if (IsEqualGUID(rguid, &GUID_SawtoothDown)) {
newEffect->effect.u.periodic.waveform = FF_SAW_DOWN;
}
break;
case DIEFT_CONSTANTFORCE:
newEffect->effect.type = FF_CONSTANT;
break;
case DIEFT_RAMPFORCE:
newEffect->effect.type = FF_RAMP;
break;
case DIEFT_CONDITION:
if (IsEqualGUID(rguid, &GUID_Spring)) {
newEffect->effect.type = FF_SPRING;
} else if (IsEqualGUID(rguid, &GUID_Friction)) {
newEffect->effect.type = FF_FRICTION;
} else if (IsEqualGUID(rguid, &GUID_Inertia)) {
newEffect->effect.type = FF_INERTIA;
} else if (IsEqualGUID(rguid, &GUID_Damper)) {
newEffect->effect.type = FF_DAMPER;
}
break;
case DIEFT_CUSTOMFORCE:
FIXME("Custom forces are not supported.\n");
HeapFree(GetProcessHeap(), 0, newEffect);
return DIERR_INVALIDPARAM;
default:
FIXME("Unknown force type 0x%x.\n", type);
HeapFree(GetProcessHeap(), 0, newEffect);
return DIERR_INVALIDPARAM;
}
/* mark as non-uploaded */
newEffect->effect.id = -1;
newEffect->entry = parent_list_entry;
*peff = &newEffect->IDirectInputEffect_iface;
TRACE("Creating linux input system effect (%p) with guid %s\n",
*peff, _dump_dinput_GUID(rguid));
return DI_OK;
}
DECLSPEC_HIDDEN HRESULT linuxinput_get_info_W(
int fd,
REFGUID rguid,
LPDIEFFECTINFOW info)
{
DWORD type = typeFromGUID(rguid);
TRACE("(%d, %s, %p) type=%d\n", fd, _dump_dinput_GUID(rguid), info, type);
if (!info) return E_POINTER;
if (info->dwSize != sizeof(DIEFFECTINFOW)) return DIERR_INVALIDPARAM;
info->guid = *rguid;
info->dwEffType = type;
/* the event device API does not support querying for all these things
* therefore we assume that we have support for them
* that's not as dangerous as it sounds, since drivers are allowed to
* ignore parameters they claim to support anyway */
info->dwEffType |= DIEFT_DEADBAND | DIEFT_FFATTACK | DIEFT_FFFADE
| DIEFT_POSNEGCOEFFICIENTS | DIEFT_POSNEGSATURATION
| DIEFT_SATURATION | DIEFT_STARTDELAY;
/* again, assume we have support for everything */
info->dwStaticParams = DIEP_ALLPARAMS;
info->dwDynamicParams = info->dwStaticParams;
/* yes, this is windows behavior (print the GUID_Name for name) */
MultiByteToWideChar(CP_ACP, 0, _dump_dinput_GUID(rguid), -1,
info->tszName, MAX_PATH);
return DI_OK;
}
static const IDirectInputEffectVtbl LinuxInputEffectVtbl = {
LinuxInputEffectImpl_QueryInterface,
LinuxInputEffectImpl_AddRef,
LinuxInputEffectImpl_Release,
LinuxInputEffectImpl_Initialize,
LinuxInputEffectImpl_GetEffectGuid,
LinuxInputEffectImpl_GetParameters,
LinuxInputEffectImpl_SetParameters,
LinuxInputEffectImpl_Start,
LinuxInputEffectImpl_Stop,
LinuxInputEffectImpl_GetEffectStatus,
LinuxInputEffectImpl_Download,
LinuxInputEffectImpl_Unload,
LinuxInputEffectImpl_Escape
};
#endif /* HAVE_STRUCT_FF_EFFECT_DIRECTION */

View File

@ -1,962 +0,0 @@
/* DirectInput Generic Joystick device
*
* Copyright 1998 Marcus Meissner
* Copyright 1998,1999 Lionel Ulmer
* Copyright 2000-2001 TransGaming Technologies Inc.
* Copyright 2009 Aric Stewart, CodeWeavers
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
/*
* To Do:
* dead zone
* force feedback
*/
#include <stdio.h>
#include "joystick_private.h"
#include "wine/debug.h"
#include "winreg.h"
WINE_DEFAULT_DEBUG_CHANNEL(dinput);
#define VID_MICROSOFT 0x045e
static const WORD PID_XBOX_CONTROLLERS[] = {
0x0202, /* Xbox Controller */
0x0285, /* Xbox Controller S */
0x0289, /* Xbox Controller S */
0x028e, /* Xbox360 Controller */
0x028f, /* Xbox360 Wireless Controller */
0x02d1, /* Xbox One Controller */
0x02dd, /* Xbox One Controller (Covert Forces/Firmware 2015) */
0x02e0, /* Xbox One X Controller */
0x02e3, /* Xbox One Elite Controller */
0x02e6, /* Wireless XBox Controller Dongle */
0x02ea, /* Xbox One S Controller */
0x02fd, /* Xbox One S Controller (Firmware 2017) */
0x0719, /* Xbox 360 Wireless Adapter */
};
static inline JoystickGenericImpl *impl_from_IDirectInputDevice8W(IDirectInputDevice8W *iface)
{
return CONTAINING_RECORD(CONTAINING_RECORD(iface, IDirectInputDeviceImpl, IDirectInputDevice8W_iface), JoystickGenericImpl, base);
}
DWORD typeFromGUID(REFGUID guid)
{
if (IsEqualGUID(guid, &GUID_ConstantForce)) {
return DIEFT_CONSTANTFORCE;
} else if (IsEqualGUID(guid, &GUID_Square)
|| IsEqualGUID(guid, &GUID_Sine)
|| IsEqualGUID(guid, &GUID_Triangle)
|| IsEqualGUID(guid, &GUID_SawtoothUp)
|| IsEqualGUID(guid, &GUID_SawtoothDown)) {
return DIEFT_PERIODIC;
} else if (IsEqualGUID(guid, &GUID_RampForce)) {
return DIEFT_RAMPFORCE;
} else if (IsEqualGUID(guid, &GUID_Spring)
|| IsEqualGUID(guid, &GUID_Damper)
|| IsEqualGUID(guid, &GUID_Inertia)
|| IsEqualGUID(guid, &GUID_Friction)) {
return DIEFT_CONDITION;
} else if (IsEqualGUID(guid, &GUID_CustomForce)) {
return DIEFT_CUSTOMFORCE;
} else {
WARN("GUID (%s) is not a known force type\n", _dump_dinput_GUID(guid));
return 0;
}
}
DWORD get_device_type(DWORD version, BOOL is_joystick)
{
if (is_joystick)
return version >= 0x0800 ? DI8DEVTYPE_JOYSTICK | (DI8DEVTYPEJOYSTICK_STANDARD << 8) :
DIDEVTYPE_JOYSTICK | (DIDEVTYPEJOYSTICK_TRADITIONAL << 8);
return version >= 0x0800 ? DI8DEVTYPE_GAMEPAD | (DI8DEVTYPEJOYSTICK_STANDARD << 8) :
DIDEVTYPE_JOYSTICK | (DIDEVTYPEJOYSTICK_GAMEPAD << 8);
}
static void _dump_DIEFFECT_flags(DWORD dwFlags)
{
if (TRACE_ON(dinput)) {
unsigned int i;
static const struct {
DWORD mask;
const char *name;
} flags[] = {
#define FE(x) { x, #x}
FE(DIEFF_CARTESIAN),
FE(DIEFF_OBJECTIDS),
FE(DIEFF_OBJECTOFFSETS),
FE(DIEFF_POLAR),
FE(DIEFF_SPHERICAL)
#undef FE
};
for (i = 0; i < ARRAY_SIZE(flags); i++)
if (flags[i].mask & dwFlags)
TRACE("%s ", flags[i].name);
TRACE("\n");
}
}
static void _dump_DIENVELOPE(LPCDIENVELOPE env)
{
if (env->dwSize != sizeof(DIENVELOPE)) {
WARN("Non-standard DIENVELOPE structure size %d.\n", env->dwSize);
}
TRACE("Envelope has attack (level: %d time: %d), fade (level: %d time: %d)\n",
env->dwAttackLevel, env->dwAttackTime, env->dwFadeLevel, env->dwFadeTime);
}
static void _dump_DICONSTANTFORCE(LPCDICONSTANTFORCE frc)
{
TRACE("Constant force has magnitude %d\n", frc->lMagnitude);
}
static void _dump_DIPERIODIC(LPCDIPERIODIC frc)
{
TRACE("Periodic force has magnitude %d, offset %d, phase %d, period %d\n",
frc->dwMagnitude, frc->lOffset, frc->dwPhase, frc->dwPeriod);
}
static void _dump_DIRAMPFORCE(LPCDIRAMPFORCE frc)
{
TRACE("Ramp force has start %d, end %d\n",
frc->lStart, frc->lEnd);
}
static void _dump_DICONDITION(LPCDICONDITION frc)
{
TRACE("Condition has offset %d, pos/neg coefficients %d and %d, pos/neg saturations %d and %d, deadband %d\n",
frc->lOffset, frc->lPositiveCoefficient, frc->lNegativeCoefficient,
frc->dwPositiveSaturation, frc->dwNegativeSaturation, frc->lDeadBand);
}
static void _dump_DICUSTOMFORCE(LPCDICUSTOMFORCE frc)
{
unsigned int i;
TRACE("Custom force uses %d channels, sample period %d. Has %d samples at %p.\n",
frc->cChannels, frc->dwSamplePeriod, frc->cSamples, frc->rglForceData);
if (frc->cSamples % frc->cChannels != 0)
WARN("Custom force has a non-integral samples-per-channel count!\n");
if (TRACE_ON(dinput)) {
TRACE("Custom force data (time aligned, axes in order):\n");
for (i = 1; i <= frc->cSamples; ++i) {
TRACE("%d ", frc->rglForceData[i]);
if (i % frc->cChannels == 0)
TRACE("\n");
}
}
}
void dump_DIEFFECT(LPCDIEFFECT eff, REFGUID guid, DWORD dwFlags)
{
DWORD type = typeFromGUID(guid);
unsigned int i;
TRACE("Dumping DIEFFECT structure:\n");
TRACE(" - dwSize: %d\n", eff->dwSize);
if ((eff->dwSize != sizeof(DIEFFECT)) && (eff->dwSize != sizeof(DIEFFECT_DX5))) {
WARN("Non-standard DIEFFECT structure size %d\n", eff->dwSize);
}
TRACE(" - dwFlags: %d\n", eff->dwFlags);
TRACE(" ");
_dump_DIEFFECT_flags(eff->dwFlags);
TRACE(" - dwDuration: %d\n", eff->dwDuration);
TRACE(" - dwGain: %d\n", eff->dwGain);
if (eff->dwGain > 10000)
WARN("dwGain is out of range (>10,000)\n");
TRACE(" - dwTriggerButton: %d\n", eff->dwTriggerButton);
TRACE(" - dwTriggerRepeatInterval: %d\n", eff->dwTriggerRepeatInterval);
TRACE(" - rglDirection: %p\n", eff->rglDirection);
if (dwFlags & DIEP_DIRECTION && eff->rglDirection) {
TRACE(" ");
for (i = 0; i < eff->cAxes; ++i)
TRACE("%d ", eff->rglDirection[i]);
TRACE("\n");
}
TRACE(" - cbTypeSpecificParams: %d\n", eff->cbTypeSpecificParams);
TRACE(" - lpvTypeSpecificParams: %p\n", eff->lpvTypeSpecificParams);
/* Only trace some members if dwFlags indicates they have data */
if (dwFlags & DIEP_AXES) {
TRACE(" - cAxes: %d\n", eff->cAxes);
TRACE(" - rgdwAxes: %p\n", eff->rgdwAxes);
if (TRACE_ON(dinput) && eff->rgdwAxes) {
TRACE(" ");
for (i = 0; i < eff->cAxes; ++i)
TRACE("%d ", eff->rgdwAxes[i]);
TRACE("\n");
}
}
if (dwFlags & DIEP_ENVELOPE) {
TRACE(" - lpEnvelope: %p\n", eff->lpEnvelope);
if (eff->lpEnvelope != NULL)
_dump_DIENVELOPE(eff->lpEnvelope);
}
if (eff->dwSize > sizeof(DIEFFECT_DX5))
TRACE(" - dwStartDelay: %d\n", eff->dwStartDelay);
if (type == DIEFT_CONSTANTFORCE) {
if (eff->cbTypeSpecificParams != sizeof(DICONSTANTFORCE)) {
WARN("Effect claims to be a constant force but the type-specific params are the wrong size!\n");
} else if (!eff->lpvTypeSpecificParams) {
WARN("Size of type-specific params is correct but pointer is NULL!\n");
} else {
_dump_DICONSTANTFORCE(eff->lpvTypeSpecificParams);
}
} else if (type == DIEFT_PERIODIC) {
if (eff->cbTypeSpecificParams != sizeof(DIPERIODIC)) {
WARN("Effect claims to be a periodic force but the type-specific params are the wrong size!\n");
} else if (!eff->lpvTypeSpecificParams) {
WARN("Size of type-specific params is correct but pointer is NULL!\n");
} else {
_dump_DIPERIODIC(eff->lpvTypeSpecificParams);
}
} else if (type == DIEFT_RAMPFORCE) {
if (eff->cbTypeSpecificParams != sizeof(DIRAMPFORCE)) {
WARN("Effect claims to be a ramp force but the type-specific params are the wrong size!\n");
} else if (!eff->lpvTypeSpecificParams) {
WARN("Size of type-specific params is correct but pointer is NULL!\n");
} else {
_dump_DIRAMPFORCE(eff->lpvTypeSpecificParams);
}
} else if (type == DIEFT_CONDITION) {
if (eff->cbTypeSpecificParams == sizeof(DICONDITION) && eff->lpvTypeSpecificParams) {
_dump_DICONDITION(eff->lpvTypeSpecificParams);
} else if (eff->cbTypeSpecificParams == 2 * sizeof(DICONDITION) && eff->lpvTypeSpecificParams) {
DICONDITION *condition = eff->lpvTypeSpecificParams;
_dump_DICONDITION(&condition[0]);
_dump_DICONDITION(&condition[1]);
} else {
WARN("Effect claims to be a condition but the type-specific params are the wrong size or NULL!\n");
}
} else if (type == DIEFT_CUSTOMFORCE) {
if (eff->cbTypeSpecificParams != sizeof(DICUSTOMFORCE)) {
WARN("Effect claims to be a custom force but the type-specific params are the wrong size!\n");
} else if (!eff->lpvTypeSpecificParams) {
WARN("Size of type-specific params is correct but pointer is NULL!\n");
} else {
_dump_DICUSTOMFORCE(eff->lpvTypeSpecificParams);
}
}
}
BOOL device_disabled_registry(const char* name)
{
DIDEVICEINSTANCEW instance;
MultiByteToWideChar( CP_ACP, 0, name, -1, instance.tszInstanceName, MAX_PATH );
return device_instance_is_disabled( &instance, NULL );
}
BOOL is_xinput_device(const DIDEVCAPS *devcaps, WORD vid, WORD pid)
{
int i;
if (vid == VID_MICROSOFT)
{
for (i = 0; i < ARRAY_SIZE(PID_XBOX_CONTROLLERS); i++)
if (pid == PID_XBOX_CONTROLLERS[i]) return TRUE;
}
return (devcaps->dwAxes == 6 && devcaps->dwButtons >= 14);
}
/******************************************************************************
* SetProperty : change input device properties
*/
HRESULT WINAPI JoystickWGenericImpl_SetProperty(LPDIRECTINPUTDEVICE8W iface, REFGUID rguid, LPCDIPROPHEADER ph)
{
JoystickGenericImpl *This = impl_from_IDirectInputDevice8W(iface);
DWORD i;
ObjProps remap_props;
TRACE("(%p,%s,%p)\n",This,debugstr_guid(rguid),ph);
if (ph == NULL) {
WARN("invalid parameter: ph == NULL\n");
return DIERR_INVALIDPARAM;
}
if (TRACE_ON(dinput))
_dump_DIPROPHEADER(ph);
if (IS_DIPROP(rguid)) {
switch (LOWORD(rguid)) {
case (DWORD_PTR)DIPROP_RANGE: {
LPCDIPROPRANGE pr = (LPCDIPROPRANGE)ph;
if (ph->dwHow == DIPH_DEVICE) {
/* Many games poll the joystick immediately after setting the range
* for calibration purposes, so the old values need to be remapped
* to the new range before it does so */
TRACE("proprange(%d,%d) all\n", pr->lMin, pr->lMax);
for (i = 0; i < This->base.data_format.wine_df->dwNumObjs; i++) {
remap_props.lDevMin = This->props[i].lMin;
remap_props.lDevMax = This->props[i].lMax;
remap_props.lDeadZone = This->props[i].lDeadZone;
remap_props.lSaturation = This->props[i].lSaturation;
remap_props.lMin = pr->lMin;
remap_props.lMax = pr->lMax;
switch (This->base.data_format.wine_df->rgodf[i].dwOfs) {
case DIJOFS_X : This->js.lX = joystick_map_axis(&remap_props, This->js.lX); break;
case DIJOFS_Y : This->js.lY = joystick_map_axis(&remap_props, This->js.lY); break;
case DIJOFS_Z : This->js.lZ = joystick_map_axis(&remap_props, This->js.lZ); break;
case DIJOFS_RX : This->js.lRx = joystick_map_axis(&remap_props, This->js.lRx); break;
case DIJOFS_RY : This->js.lRy = joystick_map_axis(&remap_props, This->js.lRy); break;
case DIJOFS_RZ : This->js.lRz = joystick_map_axis(&remap_props, This->js.lRz); break;
case DIJOFS_SLIDER(0): This->js.rglSlider[0] = joystick_map_axis(&remap_props, This->js.rglSlider[0]); break;
case DIJOFS_SLIDER(1): This->js.rglSlider[1] = joystick_map_axis(&remap_props, This->js.rglSlider[1]); break;
default: break;
}
This->props[i].lMin = pr->lMin;
This->props[i].lMax = pr->lMax;
}
} else {
int obj = find_property(&This->base.data_format, ph);
TRACE("proprange(%d,%d) obj=%d\n", pr->lMin, pr->lMax, obj);
if (obj >= 0) {
remap_props.lDevMin = This->props[obj].lMin;
remap_props.lDevMax = This->props[obj].lMax;
remap_props.lDeadZone = This->props[obj].lDeadZone;
remap_props.lSaturation = This->props[obj].lSaturation;
remap_props.lMin = pr->lMin;
remap_props.lMax = pr->lMax;
switch (This->base.data_format.wine_df->rgodf[obj].dwOfs) {
case DIJOFS_X : This->js.lX = joystick_map_axis(&remap_props, This->js.lX); break;
case DIJOFS_Y : This->js.lY = joystick_map_axis(&remap_props, This->js.lY); break;
case DIJOFS_Z : This->js.lZ = joystick_map_axis(&remap_props, This->js.lZ); break;
case DIJOFS_RX : This->js.lRx = joystick_map_axis(&remap_props, This->js.lRx); break;
case DIJOFS_RY : This->js.lRy = joystick_map_axis(&remap_props, This->js.lRy); break;
case DIJOFS_RZ : This->js.lRz = joystick_map_axis(&remap_props, This->js.lRz); break;
case DIJOFS_SLIDER(0): This->js.rglSlider[0] = joystick_map_axis(&remap_props, This->js.rglSlider[0]); break;
case DIJOFS_SLIDER(1): This->js.rglSlider[1] = joystick_map_axis(&remap_props, This->js.rglSlider[1]); break;
default: break;
}
This->props[obj].lMin = pr->lMin;
This->props[obj].lMax = pr->lMax;
return DI_OK;
}
}
break;
}
case (DWORD_PTR)DIPROP_DEADZONE: {
LPCDIPROPDWORD pd = (LPCDIPROPDWORD)ph;
if (ph->dwHow == DIPH_DEVICE) {
TRACE("deadzone(%d) all\n", pd->dwData);
for (i = 0; i < This->base.data_format.wine_df->dwNumObjs; i++)
This->props[i].lDeadZone = pd->dwData;
} else {
int obj = find_property(&This->base.data_format, ph);
TRACE("deadzone(%d) obj=%d\n", pd->dwData, obj);
if (obj >= 0) {
This->props[obj].lDeadZone = pd->dwData;
return DI_OK;
}
}
break;
}
case (DWORD_PTR)DIPROP_SATURATION: {
LPCDIPROPDWORD pd = (LPCDIPROPDWORD)ph;
if (ph->dwHow == DIPH_DEVICE) {
TRACE("saturation(%d) all\n", pd->dwData);
for (i = 0; i < This->base.data_format.wine_df->dwNumObjs; i++)
This->props[i].lSaturation = pd->dwData;
} else {
int obj = find_property(&This->base.data_format, ph);
TRACE("saturation(%d) obj=%d\n", pd->dwData, obj);
if (obj >= 0) {
This->props[obj].lSaturation = pd->dwData;
return DI_OK;
}
}
break;
}
case (DWORD_PTR)DIPROP_CALIBRATIONMODE: {
LPCDIPROPDWORD pd = (LPCDIPROPDWORD)ph;
FIXME("DIPROP_CALIBRATIONMODE(%d)\n", pd->dwData);
break;
}
default:
return IDirectInputDevice2WImpl_SetProperty(iface, rguid, ph);
}
}
return DI_OK;
}
#define DEBUG_TYPE(x) case (x): str = #x; break
void _dump_DIDEVCAPS(const DIDEVCAPS *lpDIDevCaps)
{
int type = GET_DIDEVICE_TYPE(lpDIDevCaps->dwDevType);
const char *str, *hid = "";
TRACE("dwSize: %d\n", lpDIDevCaps->dwSize);
TRACE("dwFlags: %08x\n", lpDIDevCaps->dwFlags);
switch(type)
{
/* Direct X <= 7 definitions */
DEBUG_TYPE(DIDEVTYPE_DEVICE);
DEBUG_TYPE(DIDEVTYPE_MOUSE);
DEBUG_TYPE(DIDEVTYPE_KEYBOARD);
DEBUG_TYPE(DIDEVTYPE_JOYSTICK);
/* Direct X >= 8 definitions */
DEBUG_TYPE(DI8DEVTYPE_DEVICE);
DEBUG_TYPE(DI8DEVTYPE_MOUSE);
DEBUG_TYPE(DI8DEVTYPE_KEYBOARD);
DEBUG_TYPE(DI8DEVTYPE_JOYSTICK);
DEBUG_TYPE(DI8DEVTYPE_GAMEPAD);
DEBUG_TYPE(DI8DEVTYPE_DRIVING);
DEBUG_TYPE(DI8DEVTYPE_FLIGHT);
DEBUG_TYPE(DI8DEVTYPE_1STPERSON);
DEBUG_TYPE(DI8DEVTYPE_DEVICECTRL);
DEBUG_TYPE(DI8DEVTYPE_SCREENPOINTER);
DEBUG_TYPE(DI8DEVTYPE_REMOTE);
DEBUG_TYPE(DI8DEVTYPE_SUPPLEMENTAL);
default: str = "UNKNOWN";
}
if (lpDIDevCaps->dwDevType & DIDEVTYPE_HID)
hid = " (HID)";
TRACE("dwDevType: %08x %s%s\n", lpDIDevCaps->dwDevType, str, hid);
TRACE("dwAxes: %d\n", lpDIDevCaps->dwAxes);
TRACE("dwButtons: %d\n", lpDIDevCaps->dwButtons);
TRACE("dwPOVs: %d\n", lpDIDevCaps->dwPOVs);
if (lpDIDevCaps->dwSize > sizeof(DIDEVCAPS_DX3)) {
TRACE("dwFFSamplePeriod: %d\n", lpDIDevCaps->dwFFSamplePeriod);
TRACE("dwFFMinTimeResolution: %d\n", lpDIDevCaps->dwFFMinTimeResolution);
TRACE("dwFirmwareRevision: %d\n", lpDIDevCaps->dwFirmwareRevision);
TRACE("dwHardwareRevision: %d\n", lpDIDevCaps->dwHardwareRevision);
TRACE("dwFFDriverVersion: %d\n", lpDIDevCaps->dwFFDriverVersion);
}
}
#undef DEBUG_TYPE
HRESULT WINAPI JoystickWGenericImpl_GetCapabilities(LPDIRECTINPUTDEVICE8W iface, LPDIDEVCAPS lpDIDevCaps)
{
JoystickGenericImpl *This = impl_from_IDirectInputDevice8W(iface);
int size;
TRACE("%p->(%p)\n",This,lpDIDevCaps);
if (lpDIDevCaps == NULL) {
WARN("invalid pointer\n");
return E_POINTER;
}
size = lpDIDevCaps->dwSize;
if (!(size == sizeof(DIDEVCAPS) || size == sizeof(DIDEVCAPS_DX3))) {
WARN("invalid parameter\n");
return DIERR_INVALIDPARAM;
}
CopyMemory(lpDIDevCaps, &This->devcaps, size);
lpDIDevCaps->dwSize = size;
if (TRACE_ON(dinput))
_dump_DIDEVCAPS(lpDIDevCaps);
return DI_OK;
}
ULONG WINAPI JoystickWGenericImpl_Release(LPDIRECTINPUTDEVICE8W iface)
{
JoystickGenericImpl *This = impl_from_IDirectInputDevice8W(iface);
void *axis_map = This->axis_map;
ULONG res = IDirectInputDevice2WImpl_Release(iface);
if (!res) HeapFree(GetProcessHeap(), 0, axis_map);
return res;
}
/******************************************************************************
* GetObjectInfo : get object info
*/
HRESULT WINAPI JoystickWGenericImpl_GetObjectInfo(LPDIRECTINPUTDEVICE8W iface,
LPDIDEVICEOBJECTINSTANCEW pdidoi, DWORD dwObj, DWORD dwHow)
{
static const WCHAR axisW[] = {'A','x','i','s',' ','%','d',0};
static const WCHAR povW[] = {'P','O','V',' ','%','d',0};
static const WCHAR buttonW[] = {'B','u','t','t','o','n',' ','%','d',0};
HRESULT res;
res = IDirectInputDevice2WImpl_GetObjectInfo(iface, pdidoi, dwObj, dwHow);
if (res != DI_OK) return res;
if (pdidoi->dwType & DIDFT_AXIS) {
sprintfW(pdidoi->tszName, axisW, DIDFT_GETINSTANCE(pdidoi->dwType));
pdidoi->dwFlags |= DIDOI_ASPECTPOSITION;
} else if (pdidoi->dwType & DIDFT_POV)
sprintfW(pdidoi->tszName, povW, DIDFT_GETINSTANCE(pdidoi->dwType));
else if (pdidoi->dwType & DIDFT_BUTTON)
sprintfW(pdidoi->tszName, buttonW, DIDFT_GETINSTANCE(pdidoi->dwType));
_dump_OBJECTINSTANCEW(pdidoi);
return res;
}
/******************************************************************************
* GetProperty : get input device properties
*/
HRESULT WINAPI JoystickWGenericImpl_GetProperty(LPDIRECTINPUTDEVICE8W iface, REFGUID rguid, LPDIPROPHEADER pdiph)
{
JoystickGenericImpl *This = impl_from_IDirectInputDevice8W(iface);
TRACE("(%p,%s,%p)\n", This, debugstr_guid(rguid), pdiph);
if (TRACE_ON(dinput))
_dump_DIPROPHEADER(pdiph);
if (IS_DIPROP(rguid)) {
switch (LOWORD(rguid)) {
case (DWORD_PTR) DIPROP_RANGE: {
LPDIPROPRANGE pr = (LPDIPROPRANGE)pdiph;
int obj = find_property(&This->base.data_format, pdiph);
/* The app is querying the current range of the axis
* return the lMin and lMax values */
if (obj >= 0) {
pr->lMin = This->props[obj].lMin;
pr->lMax = This->props[obj].lMax;
TRACE("range(%d, %d) obj=%d\n", pr->lMin, pr->lMax, obj);
return DI_OK;
}
break;
}
case (DWORD_PTR) DIPROP_DEADZONE: {
LPDIPROPDWORD pd = (LPDIPROPDWORD)pdiph;
int obj = find_property(&This->base.data_format, pdiph);
if (obj >= 0) {
pd->dwData = This->props[obj].lDeadZone;
TRACE("deadzone(%d) obj=%d\n", pd->dwData, obj);
return DI_OK;
}
break;
}
case (DWORD_PTR) DIPROP_SATURATION: {
LPDIPROPDWORD pd = (LPDIPROPDWORD)pdiph;
int obj = find_property(&This->base.data_format, pdiph);
if (obj >= 0) {
pd->dwData = This->props[obj].lSaturation;
TRACE("saturation(%d) obj=%d\n", pd->dwData, obj);
return DI_OK;
}
break;
}
case (DWORD_PTR) DIPROP_PRODUCTNAME:
case (DWORD_PTR) DIPROP_INSTANCENAME: {
DIPROPSTRING *ps = (DIPROPSTRING*) pdiph;
DIDEVICEINSTANCEW didev;
didev.dwSize = sizeof(didev);
IDirectInputDevice_GetDeviceInfo(iface, &didev);
if (LOWORD(rguid) == (DWORD_PTR) DIPROP_PRODUCTNAME)
lstrcpynW(ps->wsz, didev.tszProductName, MAX_PATH);
else
lstrcpynW(ps->wsz, didev.tszInstanceName, MAX_PATH);
return DI_OK;
}
default:
return IDirectInputDevice2WImpl_GetProperty(iface, rguid, pdiph);
}
}
return DI_OK;
}
/******************************************************************************
* GetDeviceInfo : get information about a device's identity
*/
HRESULT WINAPI JoystickWGenericImpl_GetDeviceInfo(
LPDIRECTINPUTDEVICE8W iface,
LPDIDEVICEINSTANCEW pdidi)
{
JoystickGenericImpl *This = impl_from_IDirectInputDevice8W(iface);
CHAR buffer[MAX_PATH];
DIPROPDWORD pd;
DWORD index = 0;
TRACE("(%p,%p)\n", iface, pdidi);
if ((pdidi->dwSize != sizeof(DIDEVICEINSTANCE_DX3W)) &&
(pdidi->dwSize != sizeof(DIDEVICEINSTANCEW))) {
WARN("invalid parameter: pdidi->dwSize = %d\n", pdidi->dwSize);
return DIERR_INVALIDPARAM;
}
/* Try to get joystick index */
pd.diph.dwSize = sizeof(pd);
pd.diph.dwHeaderSize = sizeof(pd.diph);
pd.diph.dwObj = 0;
pd.diph.dwHow = DIPH_DEVICE;
if (SUCCEEDED(IDirectInputDevice2_GetProperty(iface, DIPROP_JOYSTICKID, &pd.diph)))
index = pd.dwData;
/* Return joystick */
pdidi->guidInstance = This->base.guid;
pdidi->guidProduct = This->guidProduct;
/* we only support traditional joysticks for now */
pdidi->dwDevType = This->devcaps.dwDevType;
snprintf(buffer, sizeof(buffer), "Joystick %d", index);
MultiByteToWideChar(CP_ACP, 0, buffer, -1, pdidi->tszInstanceName, MAX_PATH);
MultiByteToWideChar(CP_ACP, 0, This->name, -1, pdidi->tszProductName, MAX_PATH);
if (pdidi->dwSize > sizeof(DIDEVICEINSTANCE_DX3W)) {
pdidi->guidFFDriver = GUID_NULL;
pdidi->wUsagePage = 0;
pdidi->wUsage = 0;
}
return DI_OK;
}
HRESULT WINAPI JoystickWGenericImpl_Poll(LPDIRECTINPUTDEVICE8W iface)
{
JoystickGenericImpl *This = impl_from_IDirectInputDevice8W(iface);
TRACE("(%p)\n",This);
if (!This->base.acquired) {
WARN("not acquired\n");
return DIERR_NOTACQUIRED;
}
This->joy_polldev( iface );
return DI_OK;
}
/******************************************************************************
* GetDeviceState : returns the "state" of the joystick.
*
*/
HRESULT WINAPI JoystickWGenericImpl_GetDeviceState(LPDIRECTINPUTDEVICE8W iface, DWORD len, LPVOID ptr)
{
JoystickGenericImpl *This = impl_from_IDirectInputDevice8W(iface);
TRACE("(%p,0x%08x,%p)\n", This, len, ptr);
if (!This->base.acquired) {
WARN("not acquired\n");
return DIERR_NOTACQUIRED;
}
/* update joystick state */
This->joy_polldev( iface );
/* convert and copy data to user supplied buffer */
fill_DataFormat(ptr, len, &This->js, &This->base.data_format);
return DI_OK;
}
HRESULT WINAPI JoystickWGenericImpl_BuildActionMap(LPDIRECTINPUTDEVICE8W iface,
LPDIACTIONFORMATW lpdiaf,
LPCWSTR lpszUserName,
DWORD dwFlags)
{
static const DWORD object_types[] = { DIDFT_AXIS, DIDFT_BUTTON };
static const DWORD type_map[] = { DIDFT_RELAXIS, DIDFT_PSHBUTTON };
JoystickGenericImpl *This = impl_from_IDirectInputDevice8W(iface);
unsigned int i, j;
BOOL has_actions = FALSE;
FIXME("(%p)->(%p,%s,%08x): semi-stub !\n", This, lpdiaf, debugstr_w(lpszUserName), dwFlags);
for (i=0; i < lpdiaf->dwNumActions; i++)
{
DWORD inst = (0x000000ff & (lpdiaf->rgoAction[i].dwSemantic)) - 1;
DWORD type = 0x000000ff & (lpdiaf->rgoAction[i].dwSemantic >> 8);
DWORD genre = 0xff000000 & lpdiaf->rgoAction[i].dwSemantic;
/* Don't touch a user configured action */
if (lpdiaf->rgoAction[i].dwHow == DIAH_USERCONFIG) continue;
/* Only consider actions of the right genre */
if (lpdiaf->dwGenre != genre && genre != DIGENRE_ANY) continue;
for (j = 0; j < ARRAY_SIZE(object_types); j++)
{
if (type & object_types[j])
{
/* Ensure that the object exists */
LPDIOBJECTDATAFORMAT odf = dataformat_to_odf_by_type(This->base.data_format.wine_df, inst, object_types[j]);
if (odf != NULL)
{
lpdiaf->rgoAction[i].dwObjID = type_map[j] | (0x0000ff00 & (inst << 8));
lpdiaf->rgoAction[i].guidInstance = This->base.guid;
lpdiaf->rgoAction[i].dwHow = DIAH_DEFAULT;
has_actions = TRUE;
/* No need to try other types if the action was already mapped */
break;
}
}
}
}
if (!has_actions) return DI_NOEFFECT;
return IDirectInputDevice8WImpl_BuildActionMap(iface, lpdiaf, lpszUserName, dwFlags);
}
HRESULT WINAPI JoystickWGenericImpl_SetActionMap(LPDIRECTINPUTDEVICE8W iface,
LPDIACTIONFORMATW lpdiaf,
LPCWSTR lpszUserName,
DWORD dwFlags)
{
JoystickGenericImpl *This = impl_from_IDirectInputDevice8W(iface);
FIXME("(%p)->(%p,%s,%08x): semi-stub !\n", This, lpdiaf, debugstr_w(lpszUserName), dwFlags);
return _set_action_map(iface, lpdiaf, lpszUserName, dwFlags, This->base.data_format.wine_df);
}
/*
* This maps the read value (from the input event) to a value in the
* 'wanted' range.
* Notes:
* Dead zone is in % multiplied by a 100 (range 0..10000)
*/
LONG joystick_map_axis(ObjProps *props, int val)
{
LONG ret;
LONG dead_zone = MulDiv( props->lDeadZone, props->lDevMax - props->lDevMin, 10000 );
LONG dev_range = props->lDevMax - props->lDevMin - dead_zone;
/* Center input */
val -= (props->lDevMin + props->lDevMax) / 2;
/* Remove dead zone */
if (abs( val ) <= dead_zone / 2)
val = 0;
else
val = val < 0 ? val + dead_zone / 2 : val - dead_zone / 2;
/* Scale and map the value from the device range into the required range */
ret = MulDiv( val, props->lMax - props->lMin, dev_range ) +
(props->lMin + props->lMax) / 2;
/* Clamp in case or rounding errors */
if (ret > props->lMax) ret = props->lMax;
else if (ret < props->lMin) ret = props->lMin;
TRACE( "(%d <%d> %d) -> (%d <%d> %d): val=%d ret=%d\n",
props->lDevMin, dead_zone, props->lDevMax,
props->lMin, props->lDeadZone, props->lMax,
val, ret );
return ret;
}
/*
* Maps POV x & y event values to a DX "clock" position:
* 0
* 31500 4500
* 27000 -1 9000
* 22500 13500
* 18000
*/
DWORD joystick_map_pov(const POINTL *p)
{
if (p->x > 0)
return p->y < 0 ? 4500 : !p->y ? 9000 : 13500;
else if (p->x < 0)
return p->y < 0 ? 31500 : !p->y ? 27000 : 22500;
else
return p->y < 0 ? 0 : !p->y ? -1 : 18000;
}
static DWORD get_config_key_a( HKEY defkey, HKEY appkey, const char *name, char *buffer, DWORD size )
{
if (appkey && !RegQueryValueExA( appkey, name, 0, NULL, (LPBYTE)buffer, &size )) return 0;
if (defkey && !RegQueryValueExA( defkey, name, 0, NULL, (LPBYTE)buffer, &size )) return 0;
return ERROR_FILE_NOT_FOUND;
}
/*
* Setup the dinput options.
*/
HRESULT setup_dinput_options(JoystickGenericImpl *This, const int *default_axis_map)
{
char buffer[MAX_PATH+16];
HKEY hkey, appkey;
int tokens = 0;
int axis = 0;
int pov = 0;
get_app_key(&hkey, &appkey);
/* get options */
if (!get_config_key_a( hkey, appkey, "DefaultDeadZone", buffer, sizeof(buffer) ))
{
This->deadzone = atoi(buffer);
TRACE("setting default deadzone to: \"%s\" %d\n", buffer, This->deadzone);
}
This->axis_map = HeapAlloc(GetProcessHeap(), 0, This->device_axis_count * sizeof(int));
if (!This->axis_map) return DIERR_OUTOFMEMORY;
if (!get_config_key_a( hkey, appkey, This->name, buffer, sizeof(buffer) ))
{
static const char *axis_names[] = {"X", "Y", "Z", "Rx", "Ry", "Rz",
"Slider1", "Slider2",
"POV1", "POV2", "POV3", "POV4"};
const char *delim = ",";
char * ptr;
TRACE("\"%s\" = \"%s\"\n", This->name, buffer);
if ((ptr = strtok(buffer, delim)) != NULL)
{
do
{
int i;
for (i = 0; i < ARRAY_SIZE(axis_names); i++)
{
if (!strcmp(ptr, axis_names[i]))
{
if (!strncmp(ptr, "POV", 3))
{
if (pov >= 4)
{
WARN("Only 4 POVs supported - ignoring extra\n");
i = -1;
}
else
{
/* Pov takes two axes */
This->axis_map[tokens++] = i;
pov++;
}
}
else
{
if (axis >= 8)
{
FIXME("Only 8 Axes supported - ignoring extra\n");
i = -1;
}
else
axis++;
}
break;
}
}
if (i == ARRAY_SIZE(axis_names))
{
ERR("invalid joystick axis type: \"%s\"\n", ptr);
i = -1;
}
This->axis_map[tokens] = i;
tokens++;
} while ((ptr = strtok(NULL, delim)) != NULL);
if (tokens != This->device_axis_count)
{
ERR("not all joystick axes mapped: %d axes(%d,%d), %d arguments\n",
This->device_axis_count, axis, pov, tokens);
while (tokens < This->device_axis_count)
{
This->axis_map[tokens] = -1;
tokens++;
}
}
}
}
else
{
int i;
if (default_axis_map)
{
/* Use default mapping from the driver */
for (i = 0; i < This->device_axis_count; i++)
{
This->axis_map[i] = default_axis_map[i];
tokens = default_axis_map[i];
if (tokens < 0)
continue;
if (tokens < 8)
axis++;
else if (tokens < 15)
{
i++;
pov++;
This->axis_map[i] = default_axis_map[i];
}
}
}
else
{
/* No config - set default mapping. */
for (i = 0; i < This->device_axis_count; i++)
{
if (i < 8)
This->axis_map[i] = axis++;
else if (i < 15)
{
This->axis_map[i++] = 8 + pov;
This->axis_map[i ] = 8 + pov++;
}
else
This->axis_map[i] = -1;
}
}
}
This->devcaps.dwAxes = axis;
This->devcaps.dwPOVs = pov;
if (appkey) RegCloseKey(appkey);
if (hkey) RegCloseKey(hkey);
return DI_OK;
}

View File

@ -37,16 +37,16 @@
#include "dinput.h"
#include "setupapi.h"
#include "wine/debug.h"
#include "wine/hid.h"
#include "dinput_private.h"
#include "device_private.h"
#include "joystick_private.h"
#include "initguid.h"
#include "devpkey.h"
#include "wine/unicode.h"
#include "wine/debug.h"
#include "wine/hid.h"
WINE_DEFAULT_DEBUG_CHANNEL(dinput);
DEFINE_GUID( GUID_DEVINTERFACE_WINEXINPUT,0x6c53d5fd,0x6480,0x440f,0xb6,0x18,0x47,0x67,0x50,0xc5,0xe1,0xa6 );

View File

@ -1,831 +0,0 @@
/* DirectInput Joystick device
*
* Copyright 1998 Marcus Meissner
* Copyright 1998,1999 Lionel Ulmer
* Copyright 2000-2001 TransGaming Technologies Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
/*
* To Do:
* dead zone
* force feedback
*/
#include "config.h"
#include "wine/port.h"
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif
#ifdef HAVE_SYS_TIME_H
# include <sys/time.h>
#endif
#include <fcntl.h>
#ifdef HAVE_SYS_IOCTL_H
# include <sys/ioctl.h>
#endif
#include <errno.h>
#ifdef HAVE_LINUX_IOCTL_H
# include <linux/ioctl.h>
#endif
#ifdef HAVE_LINUX_JOYSTICK_H
# include <linux/joystick.h>
# undef SW_MAX
#endif
#ifdef HAVE_SYS_POLL_H
# include <sys/poll.h>
#endif
#include "wine/debug.h"
#include "wine/unicode.h"
#include "windef.h"
#include "winbase.h"
#include "winerror.h"
#include "devguid.h"
#include "dinput.h"
#include "dinput_private.h"
#include "device_private.h"
#include "joystick_private.h"
#ifdef HAVE_LINUX_22_JOYSTICK_API
WINE_DEFAULT_DEBUG_CHANNEL(dinput);
#define JOYDEV_NEW "/dev/input/js"
#define JOYDEV_OLD "/dev/js"
#define JOYDEVDRIVER " (js)"
struct JoyDev
{
char device[MAX_PATH];
char name[MAX_PATH];
GUID guid_product;
BYTE axis_count;
BYTE button_count;
int *dev_axes_map;
WORD vendor_id, product_id, bus_type;
BOOL is_joystick;
};
typedef struct JoystickImpl JoystickImpl;
static const IDirectInputDevice8WVtbl JoystickWvt;
struct JoystickImpl
{
struct JoystickGenericImpl generic;
struct JoyDev *joydev;
/* joystick private */
int joyfd;
POINTL povs[4];
};
static inline JoystickImpl *impl_from_IDirectInputDevice8W(IDirectInputDevice8W *iface)
{
return CONTAINING_RECORD(CONTAINING_RECORD(CONTAINING_RECORD(iface, IDirectInputDeviceImpl, IDirectInputDevice8W_iface),
JoystickGenericImpl, base), JoystickImpl, generic);
}
static const GUID DInput_Wine_Joystick_GUID = { /* 9e573ed9-7734-11d2-8d4a-23903fb6bdf7 */
0x9e573ed9,
0x7734,
0x11d2,
{0x8d, 0x4a, 0x23, 0x90, 0x3f, 0xb6, 0xbd, 0xf7}
};
#define MAX_JOYSTICKS 64
static INT joystick_devices_count = -1;
static struct JoyDev *joystick_devices;
static void joy_polldev( IDirectInputDevice8W *iface );
#define SYS_PATH_FORMAT "/sys/class/input/js%d/device/id/%s"
static BOOL read_sys_id_variable(int index, const char *property, WORD *value)
{
char sys_path[sizeof(SYS_PATH_FORMAT) + 16], id_str[5];
int sys_fd;
BOOL ret = FALSE;
sprintf(sys_path, SYS_PATH_FORMAT, index, property);
if ((sys_fd = open(sys_path, O_RDONLY)) != -1)
{
if (read(sys_fd, id_str, 4) == 4)
{
id_str[4] = '\0';
*value = strtol(id_str, NULL, 16);
ret = TRUE;
}
close(sys_fd);
}
return ret;
}
#undef SYS_PATH_FORMAT
static INT find_joystick_devices(void)
{
INT i;
if (joystick_devices_count != -1) return joystick_devices_count;
joystick_devices_count = 0;
for (i = 0; i < MAX_JOYSTICKS; i++)
{
int fd;
struct JoyDev joydev, *new_joydevs;
BYTE axes_map[ABS_MAX + 1];
SHORT btn_map[KEY_MAX - BTN_MISC + 1];
BOOL non_js = FALSE;
snprintf(joydev.device, sizeof(joydev.device), "%s%d", JOYDEV_NEW, i);
if ((fd = open(joydev.device, O_RDONLY)) == -1)
{
snprintf(joydev.device, sizeof(joydev.device), "%s%d", JOYDEV_OLD, i);
if ((fd = open(joydev.device, O_RDONLY)) == -1) continue;
}
strcpy(joydev.name, "Wine Joystick");
#if defined(JSIOCGNAME)
if (ioctl(fd, JSIOCGNAME(sizeof(joydev.name) - sizeof(JOYDEVDRIVER)), joydev.name) < 0)
WARN("ioctl(%s,JSIOCGNAME) failed: %s\n", joydev.device, strerror(errno));
#endif
/* Append driver name */
strcat(joydev.name, JOYDEVDRIVER);
if (device_disabled_registry(joydev.name)) {
close(fd);
continue;
}
#ifdef JSIOCGAXES
if (ioctl(fd, JSIOCGAXES, &joydev.axis_count) < 0)
{
WARN("ioctl(%s,JSIOCGAXES) failed: %s, defaulting to 2\n", joydev.device, strerror(errno));
joydev.axis_count = 2;
}
#else
WARN("reading number of joystick axes unsupported in this platform, defaulting to 2\n");
joydev.axis_count = 2;
#endif
#ifdef JSIOCGBUTTONS
if (ioctl(fd, JSIOCGBUTTONS, &joydev.button_count) < 0)
{
WARN("ioctl(%s,JSIOCGBUTTONS) failed: %s, defaulting to 2\n", joydev.device, strerror(errno));
joydev.button_count = 2;
}
#else
WARN("reading number of joystick buttons unsupported in this platform, defaulting to 2\n");
joydev.button_count = 2;
#endif
joydev.is_joystick = FALSE;
if (ioctl(fd, JSIOCGBTNMAP, btn_map) < 0)
{
WARN("ioctl(%s,JSIOCGBTNMAP) failed: %s\n", joydev.device, strerror(errno));
}
else
{
INT j;
/* in lieu of properly reporting HID usage, detect presence of
* "joystick buttons" and report those devices as joysticks instead of
* gamepads */
for (j = 0; !joydev.is_joystick && j < joydev.button_count; j++)
{
switch (btn_map[j])
{
case BTN_TRIGGER:
case BTN_THUMB:
case BTN_THUMB2:
case BTN_TOP:
case BTN_TOP2:
case BTN_PINKIE:
case BTN_BASE:
case BTN_BASE2:
case BTN_BASE3:
case BTN_BASE4:
case BTN_BASE5:
case BTN_BASE6:
case BTN_DEAD:
joydev.is_joystick = TRUE;
break;
case BTN_MOUSE:
case BTN_STYLUS:
non_js = TRUE;
break;
default:
break;
}
}
}
if(non_js)
{
TRACE("Non-joystick detected. Skipping\n");
close(fd);
continue;
}
if (ioctl(fd, JSIOCGAXMAP, axes_map) < 0)
{
WARN("ioctl(%s,JSIOCGAXMAP) failed: %s\n", joydev.device, strerror(errno));
joydev.dev_axes_map = NULL;
}
else
if ((joydev.dev_axes_map = HeapAlloc(GetProcessHeap(), 0, joydev.axis_count * sizeof(int))))
{
INT j, found_axes = 0;
/* Remap to DI numbers */
for (j = 0; j < joydev.axis_count; j++)
{
if (axes_map[j] < 8)
{
/* Axis match 1-to-1 */
joydev.dev_axes_map[j] = j;
found_axes++;
}
else if (axes_map[j] <= 10)
{
/* Axes 8 through 10 are Wheel, Gas and Brake,
* remap to 0, 1 and 2
*/
joydev.dev_axes_map[j] = axes_map[j] - 8;
found_axes++;
}
else if (axes_map[j] == 16 ||
axes_map[j] == 17)
{
/* POV axis */
joydev.dev_axes_map[j] = 8;
found_axes++;
}
else
joydev.dev_axes_map[j] = -1;
}
/* If no axes were configured but there are axes assume a 1-to-1 (wii controller) */
if (joydev.axis_count && !found_axes)
{
int axes_limit = min(joydev.axis_count, 8); /* generic driver limit */
ERR("Incoherent joystick data, advertised %d axes, detected 0. Assuming 1-to-1.\n",
joydev.axis_count);
for (j = 0; j < axes_limit; j++)
joydev.dev_axes_map[j] = j;
joydev.axis_count = axes_limit;
}
}
/* Find vendor_id and product_id in sysfs */
joydev.vendor_id = 0;
joydev.product_id = 0;
read_sys_id_variable(i, "vendor", &joydev.vendor_id);
read_sys_id_variable(i, "product", &joydev.product_id);
read_sys_id_variable(i, "bustype", &joydev.bus_type);
if (joydev.vendor_id == 0 || joydev.product_id == 0)
{
joydev.guid_product = DInput_Wine_Joystick_GUID;
}
else
{
/* Concatenate product_id with vendor_id to mimic Windows behaviour */
joydev.guid_product = dinput_pidvid_guid;
joydev.guid_product.Data1 = MAKELONG(joydev.vendor_id, joydev.product_id);
}
close(fd);
if (!joystick_devices_count)
new_joydevs = HeapAlloc(GetProcessHeap(), 0, sizeof(struct JoyDev));
else
new_joydevs = HeapReAlloc(GetProcessHeap(), 0, joystick_devices,
(joystick_devices_count + 1) * sizeof(struct JoyDev));
if (!new_joydevs) continue;
TRACE("Found a joystick on %s: %s\n with %d axes and %d buttons\n", joydev.device,
joydev.name, joydev.axis_count, joydev.button_count);
joystick_devices = new_joydevs;
joystick_devices[joystick_devices_count++] = joydev;
}
return joystick_devices_count;
}
static void fill_joystick_dideviceinstanceW(LPDIDEVICEINSTANCEW lpddi, DWORD version, int id)
{
DWORD dwSize = lpddi->dwSize;
TRACE("%d %p\n", dwSize, lpddi);
memset(lpddi, 0, dwSize);
/* Return joystick */
lpddi->dwSize = dwSize;
lpddi->guidInstance = DInput_Wine_Joystick_GUID;
lpddi->guidInstance.Data3 = id;
lpddi->guidProduct = joystick_devices[id].guid_product;
lpddi->dwDevType = get_device_type(version, joystick_devices[id].is_joystick);
/* Assume the joystick as HID if it is attached to USB bus and has a valid VID/PID */
if (joystick_devices[id].bus_type == BUS_USB &&
joystick_devices[id].vendor_id && joystick_devices[id].product_id)
{
lpddi->dwDevType |= DIDEVTYPE_HID;
lpddi->wUsagePage = 0x01; /* Desktop */
if (joystick_devices[id].is_joystick)
lpddi->wUsage = 0x04; /* Joystick */
else
lpddi->wUsage = 0x05; /* Game Pad */
}
MultiByteToWideChar(CP_ACP, 0, joystick_devices[id].name, -1, lpddi->tszInstanceName, MAX_PATH);
MultiByteToWideChar(CP_ACP, 0, joystick_devices[id].name, -1, lpddi->tszProductName, MAX_PATH);
lpddi->guidFFDriver = GUID_NULL;
}
static HRESULT joydev_enum_device(DWORD dwDevType, DWORD dwFlags, LPDIDEVICEINSTANCEW lpddi, DWORD version, int id)
{
int fd = -1;
if (id >= find_joystick_devices()) return E_FAIL;
if (dwFlags & DIEDFL_FORCEFEEDBACK) {
WARN("force feedback not supported\n");
return S_FALSE;
}
if ((dwDevType == 0) ||
((dwDevType == DIDEVTYPE_JOYSTICK) && (version >= 0x0300 && version < 0x0800)) ||
(((dwDevType == DI8DEVCLASS_GAMECTRL) || (dwDevType == DI8DEVTYPE_JOYSTICK)) && (version >= 0x0800))) {
/* check whether we have a joystick */
if ((fd = open(joystick_devices[id].device, O_RDONLY)) == -1)
{
WARN("open(%s, O_RDONLY) failed: %s\n", joystick_devices[id].device, strerror(errno));
return S_FALSE;
}
fill_joystick_dideviceinstanceW( lpddi, version, id );
close(fd);
TRACE("Enumerating the linux Joystick device: %s (%s)\n", joystick_devices[id].device, joystick_devices[id].name);
return S_OK;
}
return S_FALSE;
}
static HRESULT alloc_device( REFGUID rguid, IDirectInputImpl *dinput, JoystickImpl **out, unsigned short index )
{
DWORD i;
JoystickImpl* newDevice;
HRESULT hr;
LPDIDATAFORMAT df = NULL;
int idx = 0;
DIDEVICEINSTANCEW ddi;
TRACE( "%s %p %p %hu\n", debugstr_guid( rguid ), dinput, out, index );
if (FAILED(hr = direct_input_device_alloc( sizeof(JoystickImpl), &JoystickWvt, rguid, dinput, (void **)&newDevice )))
return hr;
df = newDevice->generic.base.data_format.wine_df;
newDevice->generic.base.crit.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": JoystickImpl*->generic.base.crit");
newDevice->joydev = &joystick_devices[index];
newDevice->joyfd = -1;
newDevice->generic.guidInstance = DInput_Wine_Joystick_GUID;
newDevice->generic.guidInstance.Data3 = index;
newDevice->generic.guidProduct = DInput_Wine_Joystick_GUID;
newDevice->generic.joy_polldev = joy_polldev;
newDevice->generic.name = newDevice->joydev->name;
newDevice->generic.device_axis_count = newDevice->joydev->axis_count;
newDevice->generic.devcaps.dwButtons = newDevice->joydev->button_count;
if (newDevice->generic.devcaps.dwButtons > 128)
{
WARN("Can't support %d buttons. Clamping down to 128\n", newDevice->generic.devcaps.dwButtons);
newDevice->generic.devcaps.dwButtons = 128;
}
/* setup_dinput_options may change these */
newDevice->generic.deadzone = 0;
/* do any user specified configuration */
hr = setup_dinput_options(&newDevice->generic, newDevice->joydev->dev_axes_map);
if (hr != DI_OK)
goto FAILED1;
/* Create copy of default data format */
memcpy(df, &c_dfDIJoystick2, c_dfDIJoystick2.dwSize);
df->dwNumObjs = newDevice->generic.devcaps.dwAxes + newDevice->generic.devcaps.dwPOVs + newDevice->generic.devcaps.dwButtons;
if (!(df->rgodf = HeapAlloc(GetProcessHeap(), 0, df->dwNumObjs * df->dwObjSize))) goto FAILED;
for (i = 0; i < newDevice->generic.device_axis_count; i++)
{
int wine_obj = newDevice->generic.axis_map[i];
if (wine_obj < 0) continue;
memcpy(&df->rgodf[idx], &c_dfDIJoystick2.rgodf[wine_obj], df->dwObjSize);
if (wine_obj < 8)
df->rgodf[idx++].dwType = DIDFT_MAKEINSTANCE(wine_obj) | DIDFT_ABSAXIS;
else
{
df->rgodf[idx++].dwType = DIDFT_MAKEINSTANCE(wine_obj - 8) | DIDFT_POV;
i++; /* POV takes 2 axes */
}
}
for (i = 0; i < newDevice->generic.devcaps.dwButtons; i++)
{
memcpy(&df->rgodf[idx], &c_dfDIJoystick2.rgodf[i + 12], df->dwObjSize);
df->rgodf[idx ].pguid = &GUID_Button;
df->rgodf[idx++].dwType = DIDFT_MAKEINSTANCE(i) | DIDFT_PSHBUTTON;
}
/* initialize default properties */
for (i = 0; i < c_dfDIJoystick2.dwNumObjs; i++) {
newDevice->generic.props[i].lDevMin = -32767;
newDevice->generic.props[i].lDevMax = +32767;
newDevice->generic.props[i].lMin = 0;
newDevice->generic.props[i].lMax = 0xffff;
newDevice->generic.props[i].lDeadZone = newDevice->generic.deadzone; /* % * 1000 */
newDevice->generic.props[i].lSaturation = 0;
}
newDevice->generic.devcaps.dwSize = sizeof(newDevice->generic.devcaps);
newDevice->generic.devcaps.dwFlags = DIDC_ATTACHED;
ddi.dwSize = sizeof(ddi);
fill_joystick_dideviceinstanceW(&ddi, newDevice->generic.base.dinput->dwVersion, index);
newDevice->generic.devcaps.dwDevType = ddi.dwDevType;
newDevice->generic.devcaps.dwFFSamplePeriod = 0;
newDevice->generic.devcaps.dwFFMinTimeResolution = 0;
newDevice->generic.devcaps.dwFirmwareRevision = 0;
newDevice->generic.devcaps.dwHardwareRevision = 0;
newDevice->generic.devcaps.dwFFDriverVersion = 0;
if (TRACE_ON(dinput)) {
_dump_DIDATAFORMAT(newDevice->generic.base.data_format.wine_df);
for (i = 0; i < (newDevice->generic.device_axis_count); i++)
TRACE("axis_map[%d] = %d\n", i, newDevice->generic.axis_map[i]);
_dump_DIDEVCAPS(&newDevice->generic.devcaps);
}
*out = newDevice;
return DI_OK;
FAILED:
hr = DIERR_OUTOFMEMORY;
FAILED1:
if (df) HeapFree(GetProcessHeap(), 0, df->rgodf);
HeapFree(GetProcessHeap(), 0, df);
release_DataFormat(&newDevice->generic.base.data_format);
HeapFree(GetProcessHeap(),0,newDevice->generic.axis_map);
HeapFree(GetProcessHeap(),0,newDevice);
return hr;
}
/******************************************************************************
* get_joystick_index : Get the joystick index from a given GUID
*/
static unsigned short get_joystick_index(REFGUID guid)
{
GUID wine_joystick = DInput_Wine_Joystick_GUID;
GUID dev_guid = *guid;
INT i;
wine_joystick.Data3 = 0;
dev_guid.Data3 = 0;
/* for the standard joystick GUID use index 0 */
if(IsEqualGUID(&GUID_Joystick,guid)) return 0;
/* for the wine joystick GUIDs use the index stored in Data3 */
if(IsEqualGUID(&wine_joystick, &dev_guid)) return guid->Data3;
for(i = 0; i < joystick_devices_count; i++)
if(IsEqualGUID(&joystick_devices[i].guid_product, guid)) return i;
return MAX_JOYSTICKS;
}
static HRESULT joydev_create_device( IDirectInputImpl *dinput, REFGUID rguid, IDirectInputDevice8W **out )
{
unsigned short index;
TRACE( "%p %s %p\n", dinput, debugstr_guid( rguid ), out );
find_joystick_devices();
*out = NULL;
if ((index = get_joystick_index(rguid)) < MAX_JOYSTICKS &&
joystick_devices_count && index < joystick_devices_count)
{
JoystickImpl *This;
HRESULT hr;
if (FAILED(hr = alloc_device( rguid, dinput, &This, index ))) return hr;
TRACE( "Created a Joystick device (%p)\n", This );
*out = &This->generic.base.IDirectInputDevice8W_iface;
return hr;
}
return DIERR_DEVICENOTREG;
}
#undef MAX_JOYSTICKS
const struct dinput_device joystick_linux_device = {
"Wine Linux joystick driver",
joydev_enum_device,
joydev_create_device
};
/******************************************************************************
* Acquire : gets exclusive control of the joystick
*/
static HRESULT WINAPI JoystickLinuxWImpl_Acquire(LPDIRECTINPUTDEVICE8W iface)
{
JoystickImpl *This = impl_from_IDirectInputDevice8W(iface);
HRESULT res;
TRACE("(%p)\n",This);
res = IDirectInputDevice2WImpl_Acquire(iface);
if (res != DI_OK)
return res;
/* open the joystick device */
if (This->joyfd==-1) {
TRACE("opening joystick device %s\n", This->joydev->device);
This->joyfd = open(This->joydev->device, O_RDONLY);
if (This->joyfd==-1) {
ERR("open(%s) failed: %s\n", This->joydev->device, strerror(errno));
IDirectInputDevice2WImpl_Unacquire(iface);
return DIERR_NOTFOUND;
}
}
return DI_OK;
}
/******************************************************************************
* GetProperty : get input device properties
*/
static HRESULT WINAPI JoystickLinuxWImpl_GetProperty(LPDIRECTINPUTDEVICE8W iface, REFGUID rguid, LPDIPROPHEADER pdiph)
{
JoystickImpl *This = impl_from_IDirectInputDevice8W(iface);
TRACE("(%p)->(%s,%p)\n", This, debugstr_guid(rguid), pdiph);
_dump_DIPROPHEADER(pdiph);
if (!IS_DIPROP(rguid)) return DI_OK;
switch (LOWORD(rguid)) {
case (DWORD_PTR) DIPROP_VIDPID:
{
LPDIPROPDWORD pd = (LPDIPROPDWORD)pdiph;
if (!This->joydev->product_id || !This->joydev->vendor_id)
return DIERR_UNSUPPORTED;
pd->dwData = MAKELONG(This->joydev->vendor_id, This->joydev->product_id);
TRACE("DIPROP_VIDPID(%08x)\n", pd->dwData);
break;
}
case (DWORD_PTR) DIPROP_JOYSTICKID:
{
LPDIPROPDWORD pd = (LPDIPROPDWORD)pdiph;
pd->dwData = get_joystick_index(&This->generic.base.guid);
TRACE("DIPROP_JOYSTICKID(%d)\n", pd->dwData);
break;
}
case (DWORD_PTR) DIPROP_GUIDANDPATH:
{
static const WCHAR formatW[] = {'\\','\\','?','\\','h','i','d','#','v','i','d','_','%','0','4','x','&',
'p','i','d','_','%','0','4','x','&','%','s','_','%','h','u',0};
static const WCHAR miW[] = {'m','i',0};
static const WCHAR igW[] = {'i','g',0};
BOOL is_gamepad;
LPDIPROPGUIDANDPATH pd = (LPDIPROPGUIDANDPATH)pdiph;
WORD vid = This->joydev->vendor_id;
WORD pid = This->joydev->product_id;
if (!pid || !vid)
return DIERR_UNSUPPORTED;
is_gamepad = is_xinput_device(&This->generic.devcaps, vid, pid);
pd->guidClass = GUID_DEVCLASS_HIDCLASS;
sprintfW(pd->wszPath, formatW, vid, pid, is_gamepad ? igW : miW, get_joystick_index(&This->generic.base.guid));
TRACE("DIPROP_GUIDANDPATH(%s, %s): returning fake path\n", debugstr_guid(&pd->guidClass), debugstr_w(pd->wszPath));
break;
}
default:
return JoystickWGenericImpl_GetProperty(iface, rguid, pdiph);
}
return DI_OK;
}
/******************************************************************************
* GetDeviceInfo : get information about a device's identity
*/
static HRESULT WINAPI JoystickLinuxWImpl_GetDeviceInfo(LPDIRECTINPUTDEVICE8W iface, LPDIDEVICEINSTANCEW ddi)
{
JoystickImpl *This = impl_from_IDirectInputDevice8W(iface);
TRACE("(%p) %p\n", This, ddi);
if (ddi == NULL) return E_POINTER;
if ((ddi->dwSize != sizeof(DIDEVICEINSTANCE_DX3W)) &&
(ddi->dwSize != sizeof(DIDEVICEINSTANCEW)))
return DIERR_INVALIDPARAM;
fill_joystick_dideviceinstanceW( ddi, This->generic.base.dinput->dwVersion,
get_joystick_index(&This->generic.base.guid) );
ddi->guidInstance = This->generic.base.guid;
return DI_OK;
}
/******************************************************************************
* Unacquire : frees the joystick
*/
static HRESULT WINAPI JoystickLinuxWImpl_Unacquire(LPDIRECTINPUTDEVICE8W iface)
{
JoystickImpl *This = impl_from_IDirectInputDevice8W(iface);
HRESULT res;
TRACE("(%p)\n",This);
res = IDirectInputDevice2WImpl_Unacquire(iface);
if (res != DI_OK)
return res;
if (This->joyfd!=-1) {
TRACE("closing joystick device\n");
close(This->joyfd);
This->joyfd = -1;
return DI_OK;
}
return DI_NOEFFECT;
}
static void joy_polldev( IDirectInputDevice8W *iface )
{
struct pollfd plfd;
struct js_event jse;
JoystickImpl *This = impl_from_IDirectInputDevice8W( iface );
TRACE("(%p)\n", This);
if (This->joyfd==-1) {
WARN("no device\n");
return;
}
while (1)
{
LONG value;
int inst_id = -1;
plfd.fd = This->joyfd;
plfd.events = POLLIN;
if (poll(&plfd,1,0) != 1)
return;
/* we have one event, so we can read */
if (sizeof(jse)!=read(This->joyfd,&jse,sizeof(jse))) {
return;
}
TRACE("js_event: type 0x%x, number %d, value %d\n",
jse.type,jse.number,jse.value);
if (jse.type & JS_EVENT_BUTTON)
{
if (jse.number >= This->generic.devcaps.dwButtons) return;
inst_id = DIDFT_MAKEINSTANCE(jse.number) | DIDFT_PSHBUTTON;
This->generic.js.rgbButtons[jse.number] = value = jse.value ? 0x80 : 0x00;
}
else if (jse.type & JS_EVENT_AXIS)
{
int number = This->generic.axis_map[jse.number]; /* wine format object index */
if (number < 0) return;
inst_id = number < 8 ? DIDFT_MAKEINSTANCE(number) | DIDFT_ABSAXIS :
DIDFT_MAKEINSTANCE(number - 8) | DIDFT_POV;
value = joystick_map_axis(&This->generic.props[id_to_object(This->generic.base.data_format.wine_df, inst_id)], jse.value);
TRACE("changing axis %d => %d\n", jse.number, number);
switch (number)
{
case 0: This->generic.js.lX = value; break;
case 1: This->generic.js.lY = value; break;
case 2: This->generic.js.lZ = value; break;
case 3: This->generic.js.lRx = value; break;
case 4: This->generic.js.lRy = value; break;
case 5: This->generic.js.lRz = value; break;
case 6: This->generic.js.rglSlider[0] = value; break;
case 7: This->generic.js.rglSlider[1] = value; break;
case 8: case 9: case 10: case 11:
{
int idx = number - 8;
if (jse.number % 2)
This->povs[idx].y = jse.value;
else
This->povs[idx].x = jse.value;
This->generic.js.rgdwPOV[idx] = value = joystick_map_pov(&This->povs[idx]);
break;
}
default:
WARN("axis %d not supported\n", number);
}
}
if (inst_id >= 0)
{
queue_event(iface, inst_id, value, GetCurrentTime(), This->generic.base.dinput->evsequence++);
if (This->generic.base.hEvent) SetEvent( This->generic.base.hEvent );
}
}
}
static const IDirectInputDevice8WVtbl JoystickWvt =
{
IDirectInputDevice2WImpl_QueryInterface,
IDirectInputDevice2WImpl_AddRef,
JoystickWGenericImpl_Release,
JoystickWGenericImpl_GetCapabilities,
IDirectInputDevice2WImpl_EnumObjects,
JoystickLinuxWImpl_GetProperty,
JoystickWGenericImpl_SetProperty,
JoystickLinuxWImpl_Acquire,
JoystickLinuxWImpl_Unacquire,
JoystickWGenericImpl_GetDeviceState,
IDirectInputDevice2WImpl_GetDeviceData,
IDirectInputDevice2WImpl_SetDataFormat,
IDirectInputDevice2WImpl_SetEventNotification,
IDirectInputDevice2WImpl_SetCooperativeLevel,
JoystickWGenericImpl_GetObjectInfo,
JoystickLinuxWImpl_GetDeviceInfo,
IDirectInputDevice2WImpl_RunControlPanel,
IDirectInputDevice2WImpl_Initialize,
IDirectInputDevice2WImpl_CreateEffect,
IDirectInputDevice2WImpl_EnumEffects,
IDirectInputDevice2WImpl_GetEffectInfo,
IDirectInputDevice2WImpl_GetForceFeedbackState,
IDirectInputDevice2WImpl_SendForceFeedbackCommand,
IDirectInputDevice2WImpl_EnumCreatedEffectObjects,
IDirectInputDevice2WImpl_Escape,
JoystickWGenericImpl_Poll,
IDirectInputDevice2WImpl_SendDeviceData,
IDirectInputDevice7WImpl_EnumEffectsInFile,
IDirectInputDevice7WImpl_WriteEffectToFile,
JoystickWGenericImpl_BuildActionMap,
JoystickWGenericImpl_SetActionMap,
IDirectInputDevice8WImpl_GetImageInfo
};
#else /* HAVE_LINUX_22_JOYSTICK_API */
const struct dinput_device joystick_linux_device = {
"Wine Linux joystick driver",
NULL,
NULL,
};
#endif /* HAVE_LINUX_22_JOYSTICK_API */

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,91 +0,0 @@
/*
* Copyright 2009, Aric Stewart, CodeWeavers
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#ifndef __WINE_DLLS_DINPUT_JOYSTICK_PRIVATE_H
#define __WINE_DLLS_DINPUT_JOYSTICK_PRIVATE_H
#include <stdarg.h>
#include "windef.h"
#include "winbase.h"
#include "dinput.h"
#include "wine/list.h"
#include "wine/unicode.h"
#include "dinput_private.h"
#include "device_private.h"
/* Number of objects in the default data format */
#define MAX_PROPS 164
struct JoystickGenericImpl;
typedef void joy_polldev_handler( IDirectInputDevice8W *iface );
typedef struct JoystickGenericImpl
{
struct IDirectInputDeviceImpl base;
ObjProps props[MAX_PROPS];
DIDEVCAPS devcaps;
DIJOYSTATE2 js; /* wine data */
GUID guidProduct;
GUID guidInstance;
char *name;
int device_axis_count; /* Total number of axes in the device */
int *axis_map; /* User axes remapping */
LONG deadzone; /* Default dead-zone */
joy_polldev_handler *joy_polldev;
} JoystickGenericImpl;
LONG joystick_map_axis(ObjProps *props, int val) DECLSPEC_HIDDEN;
HRESULT setup_dinput_options(JoystickGenericImpl *This, const int *default_axis_map) DECLSPEC_HIDDEN;
DWORD joystick_map_pov(const POINTL *p) DECLSPEC_HIDDEN;
BOOL device_disabled_registry(const char* name) DECLSPEC_HIDDEN;
BOOL device_instance_is_disabled( DIDEVICEINSTANCEW *instance, BOOL *override ) DECLSPEC_HIDDEN;
ULONG WINAPI JoystickWGenericImpl_Release(LPDIRECTINPUTDEVICE8W iface);
HRESULT WINAPI JoystickWGenericImpl_GetObjectInfo(LPDIRECTINPUTDEVICE8W iface,
LPDIDEVICEOBJECTINSTANCEW pdidoi, DWORD dwObj, DWORD dwHow) DECLSPEC_HIDDEN;
HRESULT WINAPI JoystickWGenericImpl_GetProperty(LPDIRECTINPUTDEVICE8W iface, REFGUID rguid, LPDIPROPHEADER pdiph) DECLSPEC_HIDDEN;
HRESULT WINAPI JoystickWGenericImpl_GetCapabilities(LPDIRECTINPUTDEVICE8W iface, LPDIDEVCAPS lpDIDevCaps) DECLSPEC_HIDDEN;
void _dump_DIDEVCAPS(const DIDEVCAPS *lpDIDevCaps) DECLSPEC_HIDDEN;
HRESULT WINAPI JoystickWGenericImpl_SetProperty(LPDIRECTINPUTDEVICE8W iface, REFGUID rguid, LPCDIPROPHEADER ph) DECLSPEC_HIDDEN;
HRESULT WINAPI JoystickWGenericImpl_GetDeviceInfo( LPDIRECTINPUTDEVICE8W iface,
LPDIDEVICEINSTANCEW pdidi) DECLSPEC_HIDDEN;
HRESULT WINAPI JoystickWGenericImpl_Poll(LPDIRECTINPUTDEVICE8W iface) DECLSPEC_HIDDEN;
HRESULT WINAPI JoystickWGenericImpl_GetDeviceState(LPDIRECTINPUTDEVICE8W iface, DWORD len, LPVOID ptr) DECLSPEC_HIDDEN;
HRESULT WINAPI JoystickWGenericImpl_BuildActionMap(LPDIRECTINPUTDEVICE8W iface, LPDIACTIONFORMATW lpdiaf, LPCWSTR lpszUserName, DWORD dwFlags) DECLSPEC_HIDDEN;
HRESULT WINAPI JoystickWGenericImpl_SetActionMap(LPDIRECTINPUTDEVICE8W iface, LPDIACTIONFORMATW lpdiaf, LPCWSTR lpszUserName, DWORD dwFlags) DECLSPEC_HIDDEN;
DWORD typeFromGUID(REFGUID guid) DECLSPEC_HIDDEN;
void dump_DIEFFECT(LPCDIEFFECT eff, REFGUID guid, DWORD dwFlags) DECLSPEC_HIDDEN;
BOOL is_xinput_device(const DIDEVCAPS *devcaps, WORD vid, WORD pid) DECLSPEC_HIDDEN;
#endif /* __WINE_DLLS_DINPUT_JOYSTICK_PRIVATE_H */

View File

@ -2,7 +2,6 @@ MODULE = dinput8.dll
IMPORTLIB = dinput8
IMPORTS = dinput8 dxguid uuid comctl32 ole32 user32 advapi32 hid setupapi
EXTRADEFS = -DDIRECTINPUT_VERSION=0x0800
EXTRALIBS = $(IOKIT_LIBS) $(FORCEFEEDBACK_LIBS)
PARENTSRC = ../dinput
EXTRADLLFLAGS = -mcygwin
@ -13,12 +12,7 @@ C_SRCS = \
data_formats.c \
device.c \
dinput_main.c \
effect_linuxinput.c \
joystick.c \
joystick_hid.c \
joystick_linux.c \
joystick_linuxinput.c \
joystick_osx.c \
keyboard.c \
mouse.c