diff --git a/docs/rh-log.txt b/docs/rh-log.txt index c0396b4ff..bbb8d79ad 100644 --- a/docs/rh-log.txt +++ b/docs/rh-log.txt @@ -1,4 +1,19 @@ -July 13, 2009 (Changes by Graf Zahl) +July 14, 2009 +- Split the joystick menu into two parts: A top level with general options + and a list of all attached controllers, and a second level for configuring + an individual controller. +- Fixed: Pressing Up at the top of a menu with more lines than fit on screen + would find an incorrect bottom position if the menu had a custom top height. +- Added the cvars joy_dinput, joy_ps2raw, and joy_xinput to enable/disable + specific game controller input systems independant of each other. +- Device change broadcasts are now sent to the Doom event queue, so + device scanning can be handled in one common place. +- Added a fast version of IsXInputDevice that uses the Raw Input device + list, because querying WMI for this information is painfully slow. +- Added support for compiling with FMOD Ex 4.26+ and running the game + with an older DLL. This combination will now produce sound. + +July 13, 2009 (Changes by Graf Zahl) - added submission for raising master/children/siblings. - added submission for no decals on wall option. - removed some useless code from SpawnMissile function. diff --git a/src/d_event.h b/src/d_event.h index 88a117429..d5e6c8e10 100644 --- a/src/d_event.h +++ b/src/d_event.h @@ -38,7 +38,8 @@ enum EGenericEvent EV_KeyDown, // data1: scan code, data2: Qwerty ASCII code EV_KeyUp, // same EV_Mouse, // x, y: mouse movement deltas - EV_GUI_Event // subtype specifies actual event + EV_GUI_Event, // subtype specifies actual event + EV_DeviceChange,// a device has been connected or removed }; // Event structure. diff --git a/src/d_main.cpp b/src/d_main.cpp index bfe7092a3..e4d6fb9a2 100644 --- a/src/d_main.cpp +++ b/src/d_main.cpp @@ -101,6 +101,7 @@ #include "v_palette.h" #include "m_cheat.h" #include "compatibility.h" +#include "m_joy.h" EXTERN_CVAR(Bool, hud_althud) void DrawHUD(); @@ -240,6 +241,8 @@ void D_ProcessEvents (void) for (; eventtail != eventhead ; eventtail = (eventtail+1)&(MAXEVENTS-1)) { ev = &events[eventtail]; + if (ev->type == EV_DeviceChange) + UpdateJoystickMenu(I_UpdateDeviceList()); if (C_Responder (ev)) continue; // console ate the event if (M_Responder (ev)) @@ -260,6 +263,11 @@ void D_ProcessEvents (void) void D_PostEvent (const event_t *ev) { + // Do not post duplicate consecutive EV_DeviceChange events. + if (ev->type == EV_DeviceChange && events[eventhead].type == EV_DeviceChange) + { + return; + } events[eventhead] = *ev; if (ev->type == EV_Mouse && !testpolymost && !paused && menuactive == MENU_Off && ConsoleState != c_down && ConsoleState != c_falling) diff --git a/src/m_joy.cpp b/src/m_joy.cpp index 8da19e068..73689993a 100644 --- a/src/m_joy.cpp +++ b/src/m_joy.cpp @@ -2,6 +2,7 @@ #include "m_joy.h" #include "gameconfigfile.h" +#include "d_event.h" // MACROS ------------------------------------------------------------------ @@ -15,6 +16,23 @@ // EXTERNAL DATA DECLARATIONS ---------------------------------------------- +EXTERN_CVAR(Bool, joy_ps2raw) +EXTERN_CVAR(Bool, joy_dinput) +EXTERN_CVAR(Bool, joy_xinput) + +// PUBLIC DATA DEFINITIONS ------------------------------------------------- + +CUSTOM_CVAR(Bool, use_joystick, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG|CVAR_NOINITCALL) +{ +#ifdef _WIN32 + joy_ps2raw.Callback(); + joy_dinput.Callback(); + joy_xinput.Callback(); +#endif +} + +// PRIVATE DATA DEFINITIONS ------------------------------------------------ + // CODE -------------------------------------------------------------------- //========================================================================== @@ -142,3 +160,80 @@ void M_SaveJoystickConfig(IJoystickConfig *joy) } } } + + +//=========================================================================== +// +// Joy_RemoveDeadZone +// +//=========================================================================== + +double Joy_RemoveDeadZone(double axisval, double deadzone, BYTE *buttons) +{ + // Cancel out deadzone. + if (fabs(axisval) < deadzone) + { + axisval = 0; + *buttons = 0; + } + // Make the dead zone the new 0. + else if (axisval < 0) + { + axisval = (axisval + deadzone) / (1.0 - deadzone); + *buttons = 2; // button minus + } + else + { + axisval = (axisval - deadzone) / (1.0 - deadzone); + *buttons = 1; // button plus + } + return axisval; +} + +//=========================================================================== +// +// Joy_GenerateButtonEvents +// +// Provided two bitmasks for a set of buttons, generates events to reflect +// any changes from the old to new set, where base is the key for bit 0, +// base+1 is the key for bit 1, etc. +// +//=========================================================================== + +void Joy_GenerateButtonEvents(int oldbuttons, int newbuttons, int numbuttons, int base) +{ + int changed = oldbuttons ^ newbuttons; + if (changed != 0) + { + event_t ev = { 0 }; + int mask = 1; + for (int j = 0; j < numbuttons; mask <<= 1, ++j) + { + if (changed & mask) + { + ev.data1 = base + j; + ev.type = (newbuttons & mask) ? EV_KeyDown : EV_KeyUp; + D_PostEvent(&ev); + } + } + } +} + +void Joy_GenerateButtonEvents(int oldbuttons, int newbuttons, int numbuttons, const int *keys) +{ + int changed = oldbuttons ^ newbuttons; + if (changed != 0) + { + event_t ev = { 0 }; + int mask = 1; + for (int j = 0; j < numbuttons; mask <<= 1, ++j) + { + if (changed & mask) + { + ev.data1 = keys[j]; + ev.type = (newbuttons & mask) ? EV_KeyDown : EV_KeyUp; + D_PostEvent(&ev); + } + } + } +} diff --git a/src/m_joy.h b/src/m_joy.h index ce773b323..6c8920ed0 100644 --- a/src/m_joy.h +++ b/src/m_joy.h @@ -3,6 +3,7 @@ #include "doomtype.h" #include "tarray.h" +#include "c_cvars.h" enum EJoyAxis { @@ -43,11 +44,18 @@ struct NOVTABLE IJoystickConfig virtual FString GetIdentifier() = 0; }; +EXTERN_CVAR(Bool, use_joystick); + bool M_LoadJoystickConfig(IJoystickConfig *joy); void M_SaveJoystickConfig(IJoystickConfig *joy); +void Joy_GenerateButtonEvents(int oldbuttons, int newbuttons, int numbuttons, int base); +void Joy_GenerateButtonEvents(int oldbuttons, int newbuttons, int numbuttons, const int *keys); +double Joy_RemoveDeadZone(double axisval, double deadzone, BYTE *buttons); + // These ought to be provided by a system-specific i_input.cpp. void I_GetAxes(float axes[NUM_JOYAXIS]); void I_GetJoysticks(TArray &sticks); +IJoystickConfig *I_UpdateDeviceList(); #endif diff --git a/src/m_menu.h b/src/m_menu.h index 76c3fd5bd..1324498b0 100644 --- a/src/m_menu.h +++ b/src/m_menu.h @@ -94,6 +94,7 @@ typedef enum { rightmore, safemore, rsafemore, + joymore, slider, absslider, inverter, @@ -101,7 +102,6 @@ typedef enum { discretes, cdiscrete, ediscrete, - discrete_joy, control, screenres, bitflag, @@ -158,7 +158,6 @@ struct menuitem_t struct value_t *values; struct valuestring_t *valuestrings; struct valueenum_t *enumvalues; - TArray *joyvalues; char *command; void (*cfunc)(FBaseCVar *cvar, float newval); void (*mfunc)(void); diff --git a/src/m_options.cpp b/src/m_options.cpp index b0e0ff015..ea6aff8d8 100644 --- a/src/m_options.cpp +++ b/src/m_options.cpp @@ -290,34 +290,26 @@ menu_t MouseMenu = * *=======================================*/ -#define SELECTED_JOYSTICK (Joysticks[JoystickItems[1].a.joyselection]) -EXTERN_CVAR (Bool, use_joystick) +EXTERN_CVAR(Bool, use_joystick) +EXTERN_CVAR(Bool, joy_ps2raw) +EXTERN_CVAR(Bool, joy_dinput) +EXTERN_CVAR(Bool, joy_xinput) -#if 0 -EXTERN_CVAR (Float, joy_speedmultiplier) -EXTERN_CVAR (Int, joy_xaxis) -EXTERN_CVAR (Int, joy_yaxis) -EXTERN_CVAR (Int, joy_zaxis) -EXTERN_CVAR (Int, joy_xrot) -EXTERN_CVAR (Int, joy_yrot) -EXTERN_CVAR (Int, joy_zrot) -EXTERN_CVAR (Int, joy_slider) -EXTERN_CVAR (Int, joy_dial) -EXTERN_CVAR (Float, joy_xthreshold) -EXTERN_CVAR (Float, joy_ythreshold) -EXTERN_CVAR (Float, joy_zthreshold) -EXTERN_CVAR (Float, joy_xrotthreshold) -EXTERN_CVAR (Float, joy_yrotthreshold) -EXTERN_CVAR (Float, joy_zrotthreshold) -EXTERN_CVAR (Float, joy_sliderthreshold) -EXTERN_CVAR (Float, joy_dialthreshold) -EXTERN_CVAR (Float, joy_yawspeed) -EXTERN_CVAR (Float, joy_pitchspeed) -EXTERN_CVAR (Float, joy_forwardspeed) -EXTERN_CVAR (Float, joy_sidespeed) -EXTERN_CVAR (Float, joy_upspeed) -EXTERN_CVAR (GUID, joy_guid) -#endif +static TArray Joysticks; +static TArray JoystickItems; + +menu_t JoystickMenu = +{ + "CONTROLLER OPTIONS", +}; + +/*======================================= + * + * Joystick Config Menu + * + *=======================================*/ + +IJoystickConfig *SELECTED_JOYSTICK; static value_t JoyAxisMapNames[6] = { @@ -335,13 +327,11 @@ static value_t Inversion[2] = { 1.0, "Inverted" } }; -static TArray Joysticks; +static TArray JoystickConfigItems; -static TArray JoystickItems; - -menu_t JoystickMenu = +menu_t JoystickConfigMenu = { - "JOYSTICK OPTIONS", + "CONFIGURE CONTROLLER", }; @@ -1519,7 +1509,7 @@ static void CalcIndent (menu_t *menu) { item = menu->items + i; if (item->type != whitetext && item->type != redtext && item->type != screenres && - (item->type != discrete || !item->c.discretecenter)) + item->type != joymore && (item->type != discrete || !item->c.discretecenter)) { thiswidth = SmallFont->StringWidth (item->label); if (thiswidth > widest) @@ -1694,6 +1684,10 @@ void M_OptDrawer () { indent = 160; } + else if (item->type == joymore) + { + indent = 4; + } else { indent = CurrentMenu->indent; @@ -1703,19 +1697,19 @@ void M_OptDrawer () { FString somestring; const char *label; - if (item->type != discrete_joy) + if (item->type != joymore) { label = item->label; } else { - if (item->e.joyvalues->Size() == 0) + if (Joysticks.Size() == 0) { label = "No devices connected"; } else { - somestring = (*item->e.joyvalues)[item->a.joyselection]->GetName(); + somestring = Joysticks[item->a.joyselection]->GetName(); label = somestring; } } @@ -1728,6 +1722,11 @@ void M_OptDrawer () color = MoreColor; break; + case joymore: + x = 20; + color = MoreColor; + break; + case numberedmore: case rsafemore: case rightmore: @@ -1735,13 +1734,6 @@ void M_OptDrawer () color = item->type != rightmore ? CR_GREEN : MoreColor; break; - case discrete_joy: - x = 160 - width / 2; - // Move cursor to the left of this item. - indent = x - 14; - color = ValueColor; - break; - case redtext: x = 160 - width / 2; color = LabelColor; @@ -2222,7 +2214,11 @@ void M_OptResponder (event_t *ev) int maxitems, rowheight; // Figure out how many lines of text fit on the menu - if (BigFont && CurrentMenu->texttitle) + if (CurrentMenu->y != 0) + { + maxitems = CurrentMenu->y; + } + else if (BigFont && CurrentMenu->texttitle) { maxitems = 15 + BigFont->GetHeight (); } @@ -2457,15 +2453,6 @@ void M_OptResponder (event_t *ev) S_Sound (CHAN_VOICE | CHAN_UI, "menu/change", 1, ATTN_NONE); break; - case discrete_joy: - if (--item->a.joyselection < 0) - { - item->a.joyselection = item->e.joyvalues->Size() - 1; - } - UpdateJoystickMenu(NULL); - S_Sound(CHAN_VOICE|CHAN_UI, "menu/change", 1, ATTN_NONE); - break; - case inverter: value = item->a.cvar->GetGenericRep (CVAR_Float); value.Float = -value.Float; @@ -2663,15 +2650,6 @@ void M_OptResponder (event_t *ev) S_Sound (CHAN_VOICE | CHAN_UI, "menu/change", 1, ATTN_NONE); break; - case discrete_joy: - if ((unsigned)++item->a.joyselection >= item->e.joyvalues->Size()) - { - item->a.joyselection = 0; - } - UpdateJoystickMenu(NULL); - S_Sound(CHAN_VOICE | CHAN_UI, "menu/change", 1, ATTN_NONE); - break; - case inverter: value = item->a.cvar->GetGenericRep (CVAR_Float); value.Float = -value.Float; @@ -2775,6 +2753,7 @@ void M_OptResponder (event_t *ev) item->type == numberedmore || item->type == rightmore || item->type == rsafemore || + item->type == joymore || item->type == safemore) && item->e.mfunc) { @@ -3094,21 +3073,114 @@ CCMD (menu_mouse) MouseOptions (); } +static void DrawJoystickConfigMenuHeader() +{ + FString joyname = SELECTED_JOYSTICK->GetName(); + screen->DrawText(BigFont, gameinfo.gametype & GAME_DoomChex ? CR_RED : CR_UNTRANSLATED, + 160-BigFont->StringWidth(CurrentMenu->texttitle)/2, 5, + CurrentMenu->texttitle, DTA_Clean, true, TAG_DONE); + screen->DrawText(SmallFont, gameinfo.gametype & GAME_DoomChex ? CR_RED : CR_UNTRANSLATED, + 160-SmallFont->StringWidth(joyname)/2, 8 + BigFont->GetHeight(), + joyname, DTA_Clean, true, TAG_DONE); +} + +static void UpdateJoystickConfigMenu(IJoystickConfig *joy) +{ + int i; + menuitem_t item = { whitetext }; + + JoystickConfigItems.Clear(); + if (joy == NULL) + { + item.type = redtext; + item.label = "Invalid controller specified for menu"; + JoystickConfigItems.Push(item); + } + else + { + SELECTED_JOYSTICK = joy; + + item.type = joy_sens; + item.label = "Overall sensitivity"; + item.b.min = 0.5f; + item.c.max = 2.f; + item.d.step = 0.2f; + JoystickConfigItems.Push(item); + + item.type = redtext; + item.label = " "; + JoystickConfigItems.Push(item); + + item.type = whitetext; + if (joy->GetNumAxes() > 0) + { + item.label = "Axis Configuration"; + JoystickConfigItems.Push(item); + + for (i = 0; i < joy->GetNumAxes(); ++i) + { + item.type = redtext; + item.label = " "; + JoystickConfigItems.Push(item); + + item.type = joy_map; + item.label = joy->GetAxisName(i); + item.a.joyselection = i; + item.b.numvalues = countof(JoyAxisMapNames); + item.e.values = JoyAxisMapNames; + JoystickConfigItems.Push(item); + + item.type = joy_slider; + item.label = "Sensitivity"; + item.b.min = 0; + item.c.max = 4; + item.d.step = 0.2f; + item.e.joyslidernum = 0; + JoystickConfigItems.Push(item); + + item.type = joy_inverter; + item.label = "Invert"; + JoystickConfigItems.Push(item); + + item.type = joy_slider; + item.label = "Dead Zone"; + item.b.position = 1; + item.c.max = 0.9f; + item.d.step = 0.05f; + item.e.joyslidernum = 1; + JoystickConfigItems.Push(item); + } + } + else + { + item.label = "No configurable axes"; + JoystickConfigItems.Push(item); + } + } + JoystickConfigMenu.items = &JoystickConfigItems[0]; + JoystickConfigMenu.numitems = JoystickConfigItems.Size(); + JoystickConfigMenu.lastOn = 0; + JoystickConfigMenu.scrollpos = 0; + JoystickConfigMenu.y = 25 + BigFont->GetHeight(); + JoystickConfigMenu.PreDraw = DrawJoystickConfigMenuHeader; + if (screen != NULL) + { + CalcIndent(&JoystickConfigMenu); + } +} + +static void StartJoystickConfigMenu() +{ + UpdateJoystickConfigMenu(Joysticks[JoystickItems[JoystickMenu.lastOn].a.joyselection]); + M_SwitchMenu(&JoystickConfigMenu); +} + void UpdateJoystickMenu(IJoystickConfig *selected) { int i; menuitem_t item = { whitetext }; - int itemnum; - IJoystickConfig *joy; + int itemnum = -1; - if (JoystickItems.Size() > 1) - { - itemnum = JoystickItems[1].a.joyselection; - } - else - { - itemnum = 0; - } JoystickItems.Clear(); I_GetJoysticks(Joysticks); if ((unsigned)itemnum >= Joysticks.Size()) @@ -3126,162 +3198,95 @@ void UpdateJoystickMenu(IJoystickConfig *selected) } } } + item.type = discrete; + item.label = "Enable controller support"; + item.a.cvar = &use_joystick; + item.b.numvalues = 2; + item.e.values = YesNo; + JoystickItems.Push(item); + +#ifdef _WIN32 + item.label = "Enable DirectInput controllers"; + item.a.cvar = &joy_dinput; + JoystickItems.Push(item); + + item.label = "Enable XInput controllers"; + item.a.cvar = &joy_xinput; + JoystickItems.Push(item); + + item.label = "Enable raw PlayStation 2 adapters"; + item.a.cvar = &joy_ps2raw; + JoystickItems.Push(item); +#endif + + item.type = redtext; + item.label = " "; + JoystickItems.Push(item); + if (Joysticks.Size() == 0) { item.type = redtext; - item.label = "No joysticks connected"; + item.label = "No controllers detected"; JoystickItems.Push(item); + if (!use_joystick) + { + item.type = whitetext; + item.label = "Controller support must be"; + JoystickItems.Push(item); + + item.label = "enabled to detect any"; + JoystickItems.Push(item); + } } else { - item.type = discrete; - item.label = "Enable joystick"; - item.a.cvar = &use_joystick; - item.b.numvalues = 2; - item.e.values = YesNo; + item.label = "Configure controllers:"; JoystickItems.Push(item); - item.type = discrete_joy; - item.label = "Change settings for"; - item.a.joyselection = itemnum; - item.e.joyvalues = &Joysticks; - JoystickItems.Push(item); - - item.type = joy_sens; - item.label = "Overall sensitivity"; - item.b.min = 0.5f; - item.c.max = 2.f; - item.d.step = 0.2f; - JoystickItems.Push(item); - - item.type = redtext; - item.label = " "; - JoystickItems.Push(item); - - joy = Joysticks[itemnum]; - if (joy->GetNumAxes() > 0) + item.type = joymore; + item.e.mfunc = StartJoystickConfigMenu; + for (int i = 0; i < (int)Joysticks.Size(); ++i) { - item.type = whitetext; - item.label = "Axis Configuration"; + item.a.joyselection = i; + if (i == itemnum) + { + JoystickMenu.lastOn = JoystickItems.Size(); + } JoystickItems.Push(item); - - for (i = 0; i < joy->GetNumAxes(); ++i) - { - item.type = redtext; - item.label = " "; - JoystickItems.Push(item); - - item.type = joy_map; - item.label = joy->GetAxisName(i); - item.a.joyselection = i; - item.b.numvalues = countof(JoyAxisMapNames); - item.e.values = JoyAxisMapNames; - JoystickItems.Push(item); - - item.type = joy_slider; - item.label = "Sensitivity"; - item.b.min = 0; - item.c.max = 4; - item.d.step = 0.2f; - item.e.joyslidernum = 0; - JoystickItems.Push(item); - - item.type = joy_inverter; - item.label = "Invert"; - JoystickItems.Push(item); - - item.type = joy_slider; - item.label = "Dead Zone"; - item.b.position = 1; - item.c.max = 0.9f; - item.d.step = 0.05f; - item.e.joyslidernum = 1; - JoystickItems.Push(item); - } } } -#if 0 - { - JoystickItems[0].type = discrete; - JoystickItems[0].label = "Enable joystick"; - - JoystickItems[1].b.numvalues = float(JoystickNames.Size()); - JoystickItems[1].e.guidvalues = &JoystickNames[0]; - - line = 5; - - for (i = 0; i < 8; ++i) - { - if (JoyAxisNames[i] != NULL) - { - JoystickItems[line].label = JoyAxisNames[i]; - JoystickItems[line].type = discrete; - JoystickItems[line].a.intcvar = cvars[i]; - JoystickItems[line].b.numvalues = 6.f; - JoystickItems[line].d.graycheck = NULL; - JoystickItems[line].e.values = JoyAxisMapNames; - line++; - } - } - - JoystickItems[line].type = redtext; - JoystickItems[line].label = " "; - line++; - - JoystickItems[line].type = whitetext; - JoystickItems[line].label = "Axis Sensitivity"; - line++; - - for (i = 0; i < 5; ++i) - { - JoystickItems[line].type = absslider; - JoystickItems[line].label = JoyAxisMapNames[i+1].name; - JoystickItems[line].a.cvar = cvars2[i]; - JoystickItems[line].b.min = 0.0; - JoystickItems[line].c.max = 4.0; - JoystickItems[line].d.step = 0.2f; - line++; - - JoystickItems[line].type = inverter; - JoystickItems[line].label = JoyAxisMapNames[i+1].name; - JoystickItems[line].a.cvar = cvars2[i]; - JoystickItems[line].e.values = Inversion; - line++; - } - - JoystickItems[line].type = redtext; - JoystickItems[line].label = " "; - line++; - - JoystickItems[line].type = whitetext; - JoystickItems[line].label = "Axis Dead Zones"; - line++; - - for (i = 0; i < 8; ++i) - { - if (JoyAxisNames[i] != NULL) - { - JoystickItems[line].label = JoyAxisNames[i]; - JoystickItems[line].type = slider; - JoystickItems[line].a.cvar = cvars3[i]; - JoystickItems[line].b.min = 0.0; - JoystickItems[line].c.max = 0.9f; - JoystickItems[line].d.step = 0.05f; - line++; - } - } - } -#endif JoystickMenu.items = &JoystickItems[0]; JoystickMenu.numitems = JoystickItems.Size(); if (JoystickMenu.lastOn >= JoystickMenu.numitems) { JoystickMenu.lastOn = JoystickMenu.numitems - 1; } + if (CurrentMenu == &JoystickMenu && CurrentItem >= JoystickMenu.numitems) + { + CurrentItem = JoystickMenu.lastOn; + } if (screen != NULL) { CalcIndent(&JoystickMenu); } + + // If the joystick config menu is open, close it if the device it's + // open for is gone. + for (i = 0; (unsigned)i < Joysticks.Size(); ++i) + { + if (Joysticks[i] == SELECTED_JOYSTICK) + { + break; + } + } + if (i == Joysticks.Size()) + { + SELECTED_JOYSTICK = NULL; + if (CurrentMenu == &JoystickConfigMenu) + { + M_PopMenuStack(); + } + } } static void JoystickOptions () diff --git a/src/sdl/i_input.cpp b/src/sdl/i_input.cpp index 7867dd2eb..f35dcf32d 100644 --- a/src/sdl/i_input.cpp +++ b/src/sdl/i_input.cpp @@ -474,3 +474,8 @@ void I_GetAxes(float axes[NUM_JOYAXIS]) axes[i] = 0; } } + +IJoystickConfig *I_UpdateDeviceList() +{ + return NULL; +} diff --git a/src/sound/fmodsound.cpp b/src/sound/fmodsound.cpp index 85da84265..a1fc50070 100644 --- a/src/sound/fmodsound.cpp +++ b/src/sound/fmodsound.cpp @@ -1179,6 +1179,8 @@ void FMODSoundRenderer::PrintStatus() unsigned int bufferlength; int numbuffers; + Printf ("Loaded FMOD version: "TEXTCOLOR_GREEN"%x.%02x.%02x\n", ActiveFMODVersion >> 16, + (ActiveFMODVersion >> 8) & 255, ActiveFMODVersion & 255); if (FMOD_OK == Sys->getOutput(&output)) { Printf ("Output type: "TEXTCOLOR_GREEN"%s\n", Enum_NameForNum(OutputNames, output)); @@ -1361,11 +1363,12 @@ SoundStream *FMODSoundRenderer::CreateStream (SoundStreamCallback callback, int FMODStreamCapsule *capsule; FMOD::Sound *sound; FMOD_RESULT result; - FMOD_CREATESOUNDEXINFO exinfo = { sizeof(exinfo), }; + FMOD_CREATESOUNDEXINFO exinfo; FMOD_MODE mode; int sample_shift; int channel_shift; - + + InitCreateSoundExInfo(&exinfo); capsule = new FMODStreamCapsule (userdata, callback, this); mode = FMOD_2D | FMOD_OPENUSER | FMOD_LOOP_NORMAL | FMOD_SOFTWARE | FMOD_CREATESTREAM | FMOD_OPENONLY; @@ -1434,11 +1437,12 @@ SoundStream *FMODSoundRenderer::CreateStream (SoundStreamCallback callback, int SoundStream *FMODSoundRenderer::OpenStream(const char *filename_or_data, int flags, int offset, int length) { FMOD_MODE mode; - FMOD_CREATESOUNDEXINFO exinfo = { sizeof(exinfo), }; + FMOD_CREATESOUNDEXINFO exinfo; FMOD::Sound *stream; FMOD_RESULT result; bool url; + InitCreateSoundExInfo(&exinfo); mode = FMOD_SOFTWARE | FMOD_2D | FMOD_CREATESTREAM; if (flags & SoundStream::Loop) { @@ -2129,11 +2133,12 @@ void FMODSoundRenderer::UpdateSounds() SoundHandle FMODSoundRenderer::LoadSoundRaw(BYTE *sfxdata, int length, int frequency, int channels, int bits) { - FMOD_CREATESOUNDEXINFO exinfo = { sizeof(exinfo), }; + FMOD_CREATESOUNDEXINFO exinfo; SoundHandle retval = { NULL }; if (length == 0) return retval; + InitCreateSoundExInfo(&exinfo); exinfo.length = length; exinfo.numchannels = channels; exinfo.defaultfrequency = frequency; @@ -2184,11 +2189,12 @@ SoundHandle FMODSoundRenderer::LoadSoundRaw(BYTE *sfxdata, int length, int frequ SoundHandle FMODSoundRenderer::LoadSound(BYTE *sfxdata, int length) { - FMOD_CREATESOUNDEXINFO exinfo = { sizeof(exinfo), }; + FMOD_CREATESOUNDEXINFO exinfo; SoundHandle retval = { NULL }; if (length == 0) return retval; + InitCreateSoundExInfo(&exinfo); exinfo.length = length; const FMOD_MODE samplemode = FMOD_3D | FMOD_OPENMEMORY | FMOD_SOFTWARE; @@ -2623,7 +2629,7 @@ void FMODSoundRenderer::DrawSpectrum(float *spectrumarray, int x, int y, int wid short *FMODSoundRenderer::DecodeSample(int outlen, const void *coded, int sizebytes, ECodecType type) { - FMOD_CREATESOUNDEXINFO exinfo = { sizeof(exinfo), }; + FMOD_CREATESOUNDEXINFO exinfo; FMOD::Sound *sound; FMOD_SOUND_FORMAT format; int channels; @@ -2631,6 +2637,7 @@ short *FMODSoundRenderer::DecodeSample(int outlen, const void *coded, int sizeby FMOD_RESULT result; short *outbuf; + InitCreateSoundExInfo(&exinfo); if (type == CODEC_Vorbis) { exinfo.suggestedsoundtype = FMOD_SOUND_TYPE_OGGVORBIS; @@ -2667,3 +2674,28 @@ short *FMODSoundRenderer::DecodeSample(int outlen, const void *coded, int sizeby } return outbuf; } + +//========================================================================== +// +// FMODSoundRenderer :: InitCreateSoundExInfo +// +// Allow for compiling with 4.26 APIs while still running with older DLLs. +// +//========================================================================== + +void FMODSoundRenderer::InitCreateSoundExInfo(FMOD_CREATESOUNDEXINFO *exinfo) const +{ +#if FMOD_VERSION >= 0x42600 + if (ActiveFMODVersion < 0x42600) + { + // This parameter was added for 4.26.00, and trying to pass it to older + // DLLs will fail. + exinfo->cbsize = myoffsetof(FMOD_CREATESOUNDEXINFO, ignoresetfilesystem); + } + else +#endif + { + exinfo->cbsize = sizeof(exinfo); + } + memset((BYTE *)exinfo + sizeof(exinfo->cbsize), 0, exinfo->cbsize - sizeof(exinfo->cbsize)); +} diff --git a/src/sound/fmodsound.h b/src/sound/fmodsound.h index 5e4677364..2fe497365 100644 --- a/src/sound/fmodsound.h +++ b/src/sound/fmodsound.h @@ -74,6 +74,7 @@ private: FMOD_MODE SetChanHeadSettings(SoundListener *listener, FMOD::Channel *chan, const FVector3 &pos, bool areasound, FMOD_MODE oldmode) const; bool ReconnectSFXReverbUnit(); + void InitCreateSoundExInfo(FMOD_CREATESOUNDEXINFO *exinfo) const; bool Init (); void Shutdown (); diff --git a/src/win32/i_dijoy.cpp b/src/win32/i_dijoy.cpp index dc2b205f5..f9eda133d 100644 --- a/src/win32/i_dijoy.cpp +++ b/src/win32/i_dijoy.cpp @@ -9,7 +9,6 @@ #include #endif #include -#include #include #define USE_WINDOWS_DWORD @@ -28,6 +27,7 @@ #include "cmdlib.h" #include "v_text.h" #include "m_argv.h" +#include "rawinput.h" // WBEMIDL BITS -- because w32api doesn't have this, either ----------------- @@ -208,9 +208,9 @@ public: bool GetDevice(); void ProcessInput(); - bool WndProcHook(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam, LRESULT *result); void AddAxes(float axes[NUM_JOYAXIS]); void GetDevices(TArray &sticks); + IJoystickConfig *Rescan(); protected: struct Enumerator @@ -225,6 +225,8 @@ protected: static BOOL CALLBACK EnumCallback(LPCDIDEVICEINSTANCE lpddi, LPVOID pvRef); static int STACK_ARGS NameSort(const void *a, const void *b); static bool IsXInputDevice(const GUID *guid); + static bool IsXInputDeviceFast(const GUID *guid); + static bool IsXInputDeviceSlow(const GUID *guid); }; // EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- @@ -245,8 +247,12 @@ extern HWND Window; // PUBLIC DATA DEFINITIONS ------------------------------------------------- -CVAR (Bool, use_joystick, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) -FJoystickCollection *JoyDevices[NUM_JOYDEVICES]; +CUSTOM_CVAR(Bool, joy_dinput, true, CVAR_GLOBALCONFIG|CVAR_ARCHIVE|CVAR_NOINITCALL) +{ + I_StartupDirectInputJoystick(); + event_t ev = { EV_DeviceChange }; + D_PostEvent(&ev); +} // PRIVATE DATA DEFINITIONS ------------------------------------------------ @@ -981,7 +987,7 @@ FDInputJoystickManager::~FDInputJoystickManager() bool FDInputJoystickManager::GetDevice() { - if (g_pdi == NULL || !use_joystick || Args->CheckParm("-nojoy")) + if (g_pdi == NULL) { return false; } @@ -1041,37 +1047,6 @@ void FDInputJoystickManager::GetDevices(TArray &sticks) } } -//=========================================================================== -// -// FDInputJoystickManager :: WndProcHook -// -// Listen for device change broadcasts and rescan the attached devices -// when they are received. -// -//=========================================================================== - -bool FDInputJoystickManager::WndProcHook(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam, LRESULT *result) -{ - if (message != WM_DEVICECHANGE) - { - return false; - } -#ifdef _DEBUG - char out[64]; - mysnprintf(out, countof(out), "WM_DEVICECHANGE wParam=%d\n", wParam); - OutputDebugString(out); -#endif - if ((wParam != DBT_DEVNODES_CHANGED && - wParam != DBT_DEVICEARRIVAL && - wParam != DBT_CONFIGCHANGED)) - { - return false; - } - UpdateJoystickMenu(EnumDevices()); - // Return false so that other devices can handle this too if they want. - return false; -} - //=========================================================================== // // FDInputJoystickManager :: EnumCallback STATIC @@ -1101,16 +1076,46 @@ BOOL CALLBACK FDInputJoystickManager::EnumCallback(LPCDIDEVICEINSTANCE lpddi, LP // // FDInputJoystickManager :: IsXInputDevice STATIC // -// Pretty much copied straight from the article "XInput and DirectInput". -// -// Enum each PNP device using WMI and check each device ID to see if it -// contains "IG_" (ex. "VID_045E&PID_028E&IG_00"). If it does, then it's an -// XInput device. Unfortunately this information can not be found by just -// using DirectInput. +// Does the DirectInput product GUID correspond to an XInput controller? + +// If the product's device ID contains contains "IG_" +// (ex. "VID_045E&PID_028E&IG_00"), then it is an XInput device. +// Unfortunately this information can not be found by just using DirectInput. // //=========================================================================== bool FDInputJoystickManager::IsXInputDevice(const GUID *guid) +{ + if (MyGetRawInputDeviceList == NULL || MyGetRawInputDeviceInfoA == NULL) + { + return IsXInputDeviceSlow(guid); + } + else + { + return IsXInputDeviceFast(guid); + } +} + +//=========================================================================== +// +// FDInputJoystickManager :: IsXInputDeviceSlow STATIC +// +// Pretty much copied straight from the article "XInput and DirectInput". +// +// Enum each PNP device using WMI and check each device ID. This is +// Microsoft's reference implementation, but it is damn slow. After +// a hardware change, connecting to the WMI server can take nearly three +// seconds, and creating the instance enumerator consistantly takes longer +// than 0.25 seconds. +// +// The XInput DLL can apparently be hacked fairly simply to work with +// Windows 2000, and since Raw Input wasn't introduced until XP, I think +// that's reason enough to keep this version around, despite it being +// so horrendously slow. +// +//=========================================================================== + +bool FDInputJoystickManager::IsXInputDeviceSlow(const GUID *guid) { IWbemLocator *wbemlocator = NULL; IEnumWbemClassObject *enumdevices = NULL; @@ -1201,6 +1206,71 @@ cleanup: return isxinput; } +//=========================================================================== +// +// FDInputJoystickManager :: IsXInputDeviceFast STATIC +// +// The theory of operation is the same as for IsXInputDeviceSlow, except that +// we use the Raw Input device list to find the device ID instead of WMI. +// This generally takes ~0.02 ms on my machine, though it occasionally spikes +// at over 1 ms. Either way, it is a huge order of magnitude faster than WMI. +// (around 10,000 times faster, in fact!) +// +//=========================================================================== + +bool FDInputJoystickManager::IsXInputDeviceFast(const GUID *guid) +{ + UINT nDevices, numDevices; + RAWINPUTDEVICELIST *devices; + UINT i; + bool isxinput = false; + + if (MyGetRawInputDeviceList(NULL, &nDevices, sizeof(RAWINPUTDEVICELIST)) != 0) + { + return false; + } + if ((devices = (RAWINPUTDEVICELIST *)malloc(sizeof(RAWINPUTDEVICELIST) * nDevices)) == NULL) + { + return false; + } + if ((numDevices = MyGetRawInputDeviceList(devices, &nDevices, sizeof(RAWINPUTDEVICELIST))) == (UINT)-1) + { + free(devices); + return false; + } + for (i = 0; i < numDevices; ++i) + { + // I am making the assumption here that all possible XInput devices + // will report themselves as generic HID devices and not as keyboards + // or mice. + if (devices[i].dwType == RIM_TYPEHID) + { + RID_DEVICE_INFO rdi; + UINT cbSize; + + cbSize = rdi.cbSize = sizeof(rdi); + if (MyGetRawInputDeviceInfoA(devices[i].hDevice, RIDI_DEVICEINFO, &rdi, &cbSize) >= 0) + { + if(MAKELONG(rdi.hid.dwVendorId, rdi.hid.dwProductId) == guid->Data1) + { + char name[256]; + UINT namelen = countof(name); + UINT reslen; + + reslen = MyGetRawInputDeviceInfoA(devices[i].hDevice, RIDI_DEVICENAME, name, &namelen); + if (reslen != (UINT)-1) + { + isxinput = (strstr(name, "IG_") != NULL); + break; + } + } + } + } + } + free(devices); + return isxinput; +} + //=========================================================================== // // FDInputJoystickManager :: NameSort STATIC @@ -1319,6 +1389,21 @@ FDInputJoystick *FDInputJoystickManager::EnumDevices() return newone; } +//=========================================================================== +// +// FDInputJoystickManager :: Rescan +// +// Used by the joy_ps2raw and joy_xinput callbacks to rescan the +// DirectInput devices, since their presence or absence can affect the +// results of the scan. +// +//=========================================================================== + +IJoystickConfig *FDInputJoystickManager::Rescan() +{ + return EnumDevices(); +} + //=========================================================================== // // I_StartupDirectInputJoystick @@ -1327,85 +1412,24 @@ FDInputJoystick *FDInputJoystickManager::EnumDevices() void I_StartupDirectInputJoystick() { - FJoystickCollection *joys = new FDInputJoystickManager; - if (joys->GetDevice()) + if (!joy_dinput || !use_joystick || Args->CheckParm("-nojoy")) { - JoyDevices[INPUT_DIJoy] = joys; - } -} - -//=========================================================================== -// -// Joy_RemoveDeadZone -// -//=========================================================================== - -double Joy_RemoveDeadZone(double axisval, double deadzone, BYTE *buttons) -{ - // Cancel out deadzone. - if (fabs(axisval) < deadzone) - { - axisval = 0; - *buttons = 0; - } - // Make the dead zone the new 0. - else if (axisval < 0) - { - axisval = (axisval + deadzone) / (1.0 - deadzone); - *buttons = 2; // button minus + if (JoyDevices[INPUT_DIJoy] != NULL) + { + delete JoyDevices[INPUT_DIJoy]; + JoyDevices[INPUT_DIJoy] = NULL; + UpdateJoystickMenu(NULL); + } } else { - axisval = (axisval - deadzone) / (1.0 - deadzone); - *buttons = 1; // button plus - } - return axisval; -} - -//=========================================================================== -// -// Joy_GenerateButtonEvents -// -// Provided two bitmasks for a set of buttons, generates events to reflect -// any changes from the old to new set, where base is the key for bit 0, -// base+1 is the key for bit 1, etc. -// -//=========================================================================== - -void Joy_GenerateButtonEvents(int oldbuttons, int newbuttons, int numbuttons, int base) -{ - int changed = oldbuttons ^ newbuttons; - if (changed != 0) - { - event_t ev = { 0 }; - int mask = 1; - for (int j = 0; j < numbuttons; mask <<= 1, ++j) + if (JoyDevices[INPUT_DIJoy] == NULL) { - if (changed & mask) + FJoystickCollection *joys = new FDInputJoystickManager; + if (joys->GetDevice()) { - ev.data1 = base + j; - ev.type = (newbuttons & mask) ? EV_KeyDown : EV_KeyUp; - D_PostEvent(&ev); + JoyDevices[INPUT_DIJoy] = joys; } } } } - -void Joy_GenerateButtonEvents(int oldbuttons, int newbuttons, int numbuttons, const int *keys) -{ - int changed = oldbuttons ^ newbuttons; - if (changed != 0) - { - event_t ev = { 0 }; - int mask = 1; - for (int j = 0; j < numbuttons; mask <<= 1, ++j) - { - if (changed & mask) - { - ev.data1 = keys[j]; - ev.type = (newbuttons & mask) ? EV_KeyDown : EV_KeyUp; - D_PostEvent(&ev); - } - } - } -} \ No newline at end of file diff --git a/src/win32/i_input.cpp b/src/win32/i_input.cpp index 7968ad4e3..c77e445ac 100644 --- a/src/win32/i_input.cpp +++ b/src/win32/i_input.cpp @@ -47,6 +47,7 @@ #endif #include #include +#include #include #include @@ -118,7 +119,8 @@ #endif static void FindRawInputFunctions(); -BOOL DI_InitJoy (void); +FJoystickCollection *JoyDevices[NUM_JOYDEVICES]; + extern HINSTANCE g_hInst; extern DWORD SessionID; @@ -551,6 +553,16 @@ LRESULT CALLBACK WndProc (HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) case WM_ERASEBKGND: return true; + case WM_DEVICECHANGE: + if (wParam == DBT_DEVNODES_CHANGED || + wParam == DBT_DEVICEARRIVAL || + wParam == DBT_CONFIGCHANGED) + { + event_t ev = { EV_DeviceChange }; + D_PostEvent(&ev); + } + return DefWindowProc (hWnd, message, wParam, lParam); + default: return DefWindowProc (hWnd, message, wParam, lParam); } @@ -765,6 +777,24 @@ void I_GetJoysticks(TArray &sticks) } } +// If a new controller was added, returns a pointer to it. +IJoystickConfig *I_UpdateDeviceList() +{ + IJoystickConfig *newone = NULL; + for (int i = 0; i < NUM_JOYDEVICES; ++i) + { + if (JoyDevices[i] != NULL) + { + IJoystickConfig *thisnewone = JoyDevices[i]->Rescan(); + if (newone == NULL) + { + newone = thisnewone; + } + } + } + return newone; +} + void I_PutInClipboard (const char *str) { if (str == NULL || !OpenClipboard (Window)) diff --git a/src/win32/i_input.h b/src/win32/i_input.h index c1e35c2ac..1c21d78c3 100644 --- a/src/win32/i_input.h +++ b/src/win32/i_input.h @@ -115,6 +115,7 @@ class NOVTABLE FJoystickCollection : public FInputDevice public: virtual void AddAxes(float axes[NUM_JOYAXIS]) = 0; virtual void GetDevices(TArray &sticks) = 0; + virtual IJoystickConfig *Rescan() = 0; }; enum @@ -135,10 +136,6 @@ void I_StartupDirectInputJoystick(); void I_StartupRawPS2(); bool I_IsPS2Adapter(DWORD vidpid); -void Joy_GenerateButtonEvents(int oldbuttons, int newbuttons, int numbuttons, int base); -void Joy_GenerateButtonEvents(int oldbuttons, int newbuttons, int numbuttons, const int *keys); -double Joy_RemoveDeadZone(double axisval, double deadzone, BYTE *buttons); - // USB HID usage page numbers #define HID_GENERIC_DESKTOP_PAGE 0x01 #define HID_SIMULATION_CONTROLS_PAGE 0x02 diff --git a/src/win32/i_rawps2.cpp b/src/win32/i_rawps2.cpp index 375f4803e..64034cbbe 100644 --- a/src/win32/i_rawps2.cpp +++ b/src/win32/i_rawps2.cpp @@ -5,7 +5,6 @@ #include #include #include -#include #define USE_WINDOWS_DWORD #include "i_input.h" @@ -150,12 +149,11 @@ public: bool GetDevice(); bool ProcessRawInput(RAWINPUT *raw, int code); - bool WndProcHook(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam, LRESULT *result); void AddAxes(float axes[NUM_JOYAXIS]); void GetDevices(TArray &sticks); + IJoystickConfig *Rescan(); protected: - HMODULE XInputDLL; TArray Devices; bool Registered; @@ -200,6 +198,13 @@ extern HWND Window; // PUBLIC DATA DEFINITIONS ------------------------------------------------- +CUSTOM_CVAR(Bool, joy_ps2raw, true, CVAR_GLOBALCONFIG|CVAR_ARCHIVE|CVAR_NOINITCALL) +{ + I_StartupRawPS2(); + event_t ev = { EV_DeviceChange }; + D_PostEvent(&ev); +} + // PRIVATE DATA DEFINITIONS ------------------------------------------------ static const int ButtonKeys[16] = @@ -938,37 +943,6 @@ void FRawPS2Manager::GetDevices(TArray &sticks) } } -//=========================================================================== -// -// FRawPS2Manager :: WndProcHook -// -// Listen for device change broadcasts and rescan the attached devices -// when they are received. -// -//=========================================================================== - -bool FRawPS2Manager::WndProcHook(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam, LRESULT *result) -{ - if (message != WM_DEVICECHANGE) - { - return false; - } -#ifdef _DEBUG - char out[64]; - mysnprintf(out, countof(out), "WM_DEVICECHANGE wParam=%d\n", wParam); - OutputDebugString(out); -#endif - if ((wParam != DBT_DEVNODES_CHANGED && - wParam != DBT_DEVICEARRIVAL && - wParam != DBT_CONFIGCHANGED)) - { - return false; - } - UpdateJoystickMenu(EnumDevices()); - // Return false so that other devices can handle this too if they want. - return false; -} - //=========================================================================== // // FRawPS2Manager :: ProcessRawInput @@ -994,6 +968,17 @@ bool FRawPS2Manager::ProcessRawInput(RAWINPUT *raw, int code) return false; } +//=========================================================================== +// +// FRawPS2Manager :: Rescan +// +//=========================================================================== + +IJoystickConfig *FRawPS2Manager::Rescan() +{ + return EnumDevices(); +} + //=========================================================================== // // FRawPS2Manager :: EnumDevices @@ -1292,10 +1277,25 @@ void FRawPS2Manager::DoRegister() void I_StartupRawPS2() { - FJoystickCollection *joys = new FRawPS2Manager; - if (joys->GetDevice()) + if (!joy_ps2raw || !use_joystick || Args->CheckParm("-nojoy")) { - JoyDevices[INPUT_RawPS2] = joys; + if (JoyDevices[INPUT_RawPS2] != NULL) + { + delete JoyDevices[INPUT_RawPS2]; + JoyDevices[INPUT_RawPS2] = NULL; + UpdateJoystickMenu(NULL); + } + } + else + { + if (JoyDevices[INPUT_RawPS2] == NULL) + { + FJoystickCollection *joys = new FRawPS2Manager; + if (joys->GetDevice()) + { + JoyDevices[INPUT_RawPS2] = joys; + } + } } } diff --git a/src/win32/i_xinput.cpp b/src/win32/i_xinput.cpp index 18d1f6736..2a1767089 100644 --- a/src/win32/i_xinput.cpp +++ b/src/win32/i_xinput.cpp @@ -120,6 +120,7 @@ public: bool WndProcHook(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam, LRESULT *result); void AddAxes(float axes[NUM_JOYAXIS]); void GetDevices(TArray &sticks); + IJoystickConfig *Rescan(); protected: HMODULE XInputDLL; @@ -136,6 +137,13 @@ protected: // PUBLIC DATA DEFINITIONS ------------------------------------------------- +CUSTOM_CVAR(Bool, joy_xinput, true, CVAR_GLOBALCONFIG|CVAR_ARCHIVE|CVAR_NOINITCALL) +{ + I_StartupXInput(); + event_t ev = { EV_DeviceChange }; + D_PostEvent(&ev); +} + // PRIVATE DATA DEFINITIONS ------------------------------------------------ static XInputGetStateType InputGetState; @@ -734,6 +742,17 @@ bool FXInputManager::WndProcHook(HWND hWnd, UINT message, WPARAM wParam, LPARAM return false; } +//=========================================================================== +// +// FXInputManager :: Rescan +// +//=========================================================================== + +IJoystickConfig *FXInputManager::Rescan() +{ + return NULL; +} + //=========================================================================== // // I_StartupXInput @@ -742,10 +761,25 @@ bool FXInputManager::WndProcHook(HWND hWnd, UINT message, WPARAM wParam, LPARAM void I_StartupXInput() { - FJoystickCollection *joys = new FXInputManager; - if (joys->GetDevice()) + if (!joy_xinput || !use_joystick || Args->CheckParm("-nojoy")) { - JoyDevices[INPUT_XInput] = joys; + if (JoyDevices[INPUT_XInput] != NULL) + { + delete JoyDevices[INPUT_XInput]; + JoyDevices[INPUT_XInput] = NULL; + UpdateJoystickMenu(NULL); + } + } + else + { + if (JoyDevices[INPUT_XInput] == NULL) + { + FJoystickCollection *joys = new FXInputManager; + if (joys->GetDevice()) + { + JoyDevices[INPUT_XInput] = joys; + } + } } }