diff --git a/Windows/ControlMapping.cpp b/Windows/ControlMapping.cpp new file mode 100644 index 0000000000..acb884a835 --- /dev/null +++ b/Windows/ControlMapping.cpp @@ -0,0 +1,223 @@ +// Copyright (c) 2012- PPSSPP Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0 or later versions. + +// This program 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 General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#include "ControlMapping.h" +#include "DinputDevice.h" +#include "XinputDevice.h" + +extern unsigned int key_pad_map[]; +extern unsigned short analog_ctrl_map[]; +extern unsigned int dinput_ctrl_map[]; +extern unsigned int xinput_ctrl_map[]; + +#define DINPUT_CAST(inst) static_cast>(inst) + +inline UINT* ControlMapping::GetDeviceButtonsMap(UINT curDevice) +{ + switch (curDevice) + { + case CONTROLS_KEYBOARD_INDEX: + return pButtonsMap; + case CONTROLS_DIRECT_INPUT_INDEX: + return pButtonsMap + buttonsCount; + case CONTROLS_XINPUT_INDEX: + return pButtonsMap + (buttonsCount * 2); + } + return NULL; +} + +ControlMapping * ControlMapping::CreateInstance(UINT nButtons) +{ + if (nButtons != (key_pad_map_size / sizeof(key_pad_map[0]) / 2)) + return FALSE; + if (nButtons != (dinput_ctrl_map_size / sizeof(dinput_ctrl_map[0]) / 2)) + return FALSE; + if (nButtons != (xinput_ctrl_map_size / sizeof(xinput_ctrl_map[0]) / 2)) + return FALSE; + if (4 != (analog_ctrl_map_size / sizeof(analog_ctrl_map[0]) / 2)) + return FALSE; + auto inst = new ControlMapping(nButtons); + if (!inst->pButtonsMap) + return FALSE; + return inst; +} + +ControlMapping::ControlMapping(UINT nButtons) : + currentDevicePage(-1), + currentButtonIndex(-1), + pButtonsMap(0), + buttonsCount(nButtons), + dinput(0), + xinput(0) +{ + dinput = std::shared_ptr(new DinputDevice()); + xinput = std::shared_ptr(new XinputDevice()); + + pButtonsMap = new UINT[3 * nButtons]; + ZeroMemory(pButtonsMap, sizeof(UINT) * CONTROLS_DEVICE_NUM * nButtons); + for (int i = 0; i < nButtons; i++) { + *(GetDeviceButtonsMap(CONTROLS_KEYBOARD_INDEX) + i) = key_pad_map[i * 2]; + *(GetDeviceButtonsMap(CONTROLS_DIRECT_INPUT_INDEX) + i) = dinput_ctrl_map[i * 2]; + *(GetDeviceButtonsMap(CONTROLS_XINPUT_INDEX) + i) = xinput_ctrl_map[i * 2]; + } + + ZeroMemory(&keyboardAnalogMap, sizeof(keyboardAnalogMap)); + for (int i = 0; i < 4; i++) { + keyboardAnalogMap[i] = analog_ctrl_map[i * 2]; + } +} + +ControlMapping::~ControlMapping() +{ + if (pButtonsMap) { + delete [] pButtonsMap; + pButtonsMap = NULL; + } +} + +void ControlMapping::UpdateState() +{ + rawState.button = -1; + switch(currentDevicePage) + { + case CONTROLS_KEYBOARD_INDEX: // keyboard + { + ; // leave it to KeyboardProc. + } + break; + case CONTROLS_DIRECT_INPUT_INDEX: // directinput + { + + dinput->UpdateRawStateSingle(rawState); + UINT newButton = (rawState.button != rawState.prevButton && rawState.prevButton == -1) + ? rawState.button : -1; + if (newButton != -1) { + SetBindCode(newButton); + } + } + break; + case CONTROLS_XINPUT_INDEX: // xinput + { + xinput->UpdateRawStateSingle(rawState); + UINT newButton = (rawState.button != rawState.prevButton && rawState.prevButton == -1) + ? rawState.button : -1; + if (newButton != -1) { + SetBindCode(newButton); + } + } + break; + } + rawState.prevButton = rawState.button; +} + +void ControlMapping::BindToDevices() +{ + for (int i = 0; i < buttonsCount; i++) { + key_pad_map[i * 2] = *(GetDeviceButtonsMap(CONTROLS_KEYBOARD_INDEX) + i); + dinput_ctrl_map[i * 2] = *(GetDeviceButtonsMap(CONTROLS_DIRECT_INPUT_INDEX) + i); + xinput_ctrl_map[i * 2] = *(GetDeviceButtonsMap(CONTROLS_XINPUT_INDEX) + i); + } + for (int i = 0; i < 4; i++) { + analog_ctrl_map[i * 2] = keyboardAnalogMap[i]; + } +} + +void ControlMapping::SetBindCode(UINT newCode) +{ + SetBindCode(newCode, currentDevicePage, currentButtonIndex); +} + +void ControlMapping::SetBindCode(UINT newCode, UINT buttonIdx) +{ + SetBindCode(newCode, currentDevicePage, buttonIdx); +} + +void ControlMapping::SetBindCode(UINT newCode, UINT deviceIdx, UINT buttonIdx) +{ + if (deviceIdx < CONTROLS_DEVICE_NUM && buttonIdx < buttonsCount) + *(GetDeviceButtonsMap(deviceIdx) + buttonIdx) = newCode; +} + +UINT ControlMapping::GetBindCode(UINT deviceIdx, UINT buttonIdx) +{ + if (deviceIdx < CONTROLS_DEVICE_NUM && buttonIdx < buttonsCount) + return *(GetDeviceButtonsMap(deviceIdx) + buttonIdx); + return -1; +} + +UINT ControlMapping::GetBindCode(UINT buttonIdx) +{ + return GetBindCode(currentDevicePage, buttonIdx); +} + +UINT ControlMapping::GetBindCode() +{ + return GetBindCode(currentDevicePage, currentButtonIndex); +} + +UINT ControlMapping::GetTargetDevice() +{ + return currentDevicePage; +} + +void ControlMapping::SetTargetDevice(UINT deviceIdx) +{ + rawState.prevButton = -1; + currentDevicePage = deviceIdx; +} + +UINT ControlMapping::GetTargetButton() +{ + return currentButtonIndex; +} + +void ControlMapping::SetTargetButton(UINT buttonIdx) +{ + currentButtonIndex = buttonIdx; +} + +bool saveControlsToFile() { + FILE *wfp = fopen("PPSSPPControls.dat", "wb"); + if (!wfp) + return false; + + fwrite(key_pad_map, 1, key_pad_map_size, wfp); + fwrite(analog_ctrl_map, 1, analog_ctrl_map_size, wfp); + fwrite(dinput_ctrl_map, 1, dinput_ctrl_map_size, wfp); + fwrite(xinput_ctrl_map, 1, xinput_ctrl_map_size, wfp); + fclose(wfp); + return true; +} + +bool loadControlsFromFile() { + FILE *rfp = fopen("PPSSPPControls.dat", "rb"); + if (!rfp) + return false; + fseek(rfp, 0, SEEK_END); + fpos_t fsize = 0; + fgetpos(rfp, &fsize); + if (fsize != (key_pad_map_size + analog_ctrl_map_size + dinput_ctrl_map_size + xinput_ctrl_map_size)) + return false; + fseek(rfp, 0, SEEK_SET); + fread(key_pad_map, 1, key_pad_map_size, rfp); + fread(analog_ctrl_map, 1, analog_ctrl_map_size, rfp); + fread(dinput_ctrl_map, 1, dinput_ctrl_map_size, rfp); + fread(xinput_ctrl_map, 1, xinput_ctrl_map_size, rfp); + fclose(rfp); + return true; +} + diff --git a/Windows/ControlMapping.h b/Windows/ControlMapping.h new file mode 100644 index 0000000000..9bea829760 --- /dev/null +++ b/Windows/ControlMapping.h @@ -0,0 +1,86 @@ +// Copyright (c) 2012- PPSSPP Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0 or later versions. + +// This program 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 General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#pragma once + +#include "InputDevice.h" + +struct RawInputState +{ + UINT button; + UINT prevButton; + RawInputState() { + button = 0; + prevButton = 0; + } +}; + +#define POV_CODE_UP 0x0100 +#define POV_CODE_DOWN 0x0200 +#define POV_CODE_LEFT 0x0400 +#define POV_CODE_RIGHT 0x0800 +#define XBOX_CODE_LEFTTRIGER 0x00010000 +#define XBOX_CODE_RIGHTTRIGER 0x00020000 + +#define CONTROLS_KEYBOARD_INDEX 0 +#define CONTROLS_DIRECT_INPUT_INDEX 1 +#define CONTROLS_XINPUT_INDEX 2 +#define CONTROLS_DEVICE_NUM 3 + +extern bool saveControlsToFile(); +extern bool loadControlsFromFile(); + +extern const unsigned int xinput_ctrl_map_size; +extern const unsigned int key_pad_map_size; +extern const unsigned int analog_ctrl_map_size; +extern const unsigned int dinput_ctrl_map_size; + +struct RawInputState; +class DinputDevice; +class XinputDevice; + +class ControlMapping +{ +public: + static ControlMapping * CreateInstance(UINT nButtons); + virtual ~ControlMapping(); + void UpdateState(); + void BindToDevices(); + void SetTargetDevice(UINT deviceIdx); + UINT GetTargetDevice(); + void SetTargetButton(UINT buttonIdx); + UINT GetTargetButton(); + void SetBindCode(UINT newCode); + UINT GetBindCode(); + UINT GetBindCode(UINT buttonIdx); + UINT GetBindCode(UINT deviceIdx, UINT buttonIdx); + +private: + ControlMapping(UINT nButtons); + ControlMapping(); + inline UINT* GetDeviceButtonsMap(UINT curDevice); + void SetBindCode(UINT newCode, UINT buttonIdx); + void SetBindCode(UINT newCode, UINT deviceIdx, UINT buttonIdx); + std::shared_ptr dinput; + std::shared_ptr xinput; + UINT currentDevicePage; + UINT currentButtonIndex; + UINT buttonsCount; + RawInputState rawState; + UINT *pButtonsMap; + USHORT keyboardAnalogMap[4]; +}; diff --git a/Windows/DinputDevice.cpp b/Windows/DinputDevice.cpp index 65d67c8b7a..9fcc510bd9 100644 --- a/Windows/DinputDevice.cpp +++ b/Windows/DinputDevice.cpp @@ -16,24 +16,36 @@ // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. #include +#include "Core/HLE/sceCtrl.h" #include "DinputDevice.h" +#include "ControlMapping.h" #include "Core/Config.h" #include "input/input_state.h" #include "Core/Reporting.h" #include "Xinput.h" #pragma comment(lib,"dinput8.lib") -static const unsigned int dinput_ctrl_map[] = { - 9, PAD_BUTTON_START, +unsigned int dinput_ctrl_map[] = { + 11, PAD_BUTTON_MENU, // Open PauseScreen + 10, PAD_BUTTON_BACK, // Toggle PauseScreen & Back Setting Page + 1, PAD_BUTTON_A, // Cross = XBOX-A + 2, PAD_BUTTON_B, // Circle = XBOX-B + 0, PAD_BUTTON_X, // Square = XBOX-X + 3, PAD_BUTTON_Y, // Triangle = XBOX-Y 8, PAD_BUTTON_SELECT, - 4, PAD_BUTTON_LBUMPER, - 5, PAD_BUTTON_RBUMPER, - 1, PAD_BUTTON_A, - 2, PAD_BUTTON_B, - 0, PAD_BUTTON_X, - 3, PAD_BUTTON_Y, + 9, PAD_BUTTON_START, + 4, PAD_BUTTON_LBUMPER, // LTrigger = XBOX-LBumper + 5, PAD_BUTTON_RBUMPER, // RTrigger = XBOX-RBumper + 6, PAD_BUTTON_LEFT_THUMB, // Turbo + 7, PAD_BUTTON_RIGHT_THUMB, // Open PauseScreen + POV_CODE_UP, PAD_BUTTON_UP, + POV_CODE_DOWN, PAD_BUTTON_DOWN, + POV_CODE_LEFT, PAD_BUTTON_LEFT, + POV_CODE_RIGHT, PAD_BUTTON_RIGHT, }; +const unsigned int dinput_ctrl_map_size = sizeof(dinput_ctrl_map); + #define DIFF (JOY_POVRIGHT - JOY_POVFORWARD) / 2 #define JOY_POVFORWARD_RIGHT JOY_POVFORWARD + DIFF #define JOY_POVRIGHT_BACKWARD JOY_POVRIGHT + DIFF @@ -119,6 +131,16 @@ DinputDevice::~DinputDevice() } } +static inline int getPadCodeFromVirtualPovCode(unsigned int povCode) +{ + int mergedCode = 0; + for (int i = 0; i < dinput_ctrl_map_size / sizeof(dinput_ctrl_map[0]); i += 2) { + if (dinput_ctrl_map[i] > 0xFF && dinput_ctrl_map[i] & povCode) + mergedCode |= dinput_ctrl_map[i + 1]; + } + return mergedCode; +} + int DinputDevice::UpdateState(InputState &input_state) { if (g_Config.iForceInputDevice == 0) return -1; @@ -134,17 +156,16 @@ int DinputDevice::UpdateState(InputState &input_state) if(FAILED(pJoystick->GetDeviceState(sizeof(DIJOYSTATE2), &js))) return -1; - switch (js.rgdwPOV[0]) { - case JOY_POVFORWARD: input_state.pad_buttons |= PAD_BUTTON_UP; break; - case JOY_POVBACKWARD: input_state.pad_buttons |= PAD_BUTTON_DOWN; break; - case JOY_POVLEFT: input_state.pad_buttons |= PAD_BUTTON_LEFT; break; - case JOY_POVRIGHT: input_state.pad_buttons |= PAD_BUTTON_RIGHT; break; - case JOY_POVFORWARD_RIGHT: input_state.pad_buttons |= (PAD_BUTTON_UP | PAD_BUTTON_RIGHT); break; - case JOY_POVRIGHT_BACKWARD: input_state.pad_buttons |= (PAD_BUTTON_RIGHT | PAD_BUTTON_DOWN); break; - case JOY_POVBACKWARD_LEFT: input_state.pad_buttons |= (PAD_BUTTON_DOWN | PAD_BUTTON_LEFT); break; - case JOY_POVLEFT_FORWARD: input_state.pad_buttons |= (PAD_BUTTON_LEFT | PAD_BUTTON_UP); break; + case JOY_POVFORWARD: input_state.pad_buttons |= getPadCodeFromVirtualPovCode(POV_CODE_UP); break; + case JOY_POVBACKWARD: input_state.pad_buttons |= getPadCodeFromVirtualPovCode(POV_CODE_DOWN); break; + case JOY_POVLEFT: input_state.pad_buttons |= getPadCodeFromVirtualPovCode(POV_CODE_LEFT); break; + case JOY_POVRIGHT: input_state.pad_buttons |= getPadCodeFromVirtualPovCode(POV_CODE_RIGHT); break; + case JOY_POVFORWARD_RIGHT: input_state.pad_buttons |= getPadCodeFromVirtualPovCode(POV_CODE_UP | POV_CODE_RIGHT); break; + case JOY_POVRIGHT_BACKWARD: input_state.pad_buttons |= getPadCodeFromVirtualPovCode(POV_CODE_RIGHT | POV_CODE_DOWN); break; + case JOY_POVBACKWARD_LEFT: input_state.pad_buttons |= getPadCodeFromVirtualPovCode(POV_CODE_DOWN | POV_CODE_LEFT); break; + case JOY_POVLEFT_FORWARD: input_state.pad_buttons |= getPadCodeFromVirtualPovCode(POV_CODE_LEFT | POV_CODE_UP); break; } if (analog) @@ -155,7 +176,8 @@ int DinputDevice::UpdateState(InputState &input_state) for (u8 i = 0; i < sizeof(dinput_ctrl_map)/sizeof(dinput_ctrl_map[0]); i += 2) { - if (js.rgbButtons[dinput_ctrl_map[i]] & 0x80) + // DIJOYSTATE2 supported 128 buttons. for exclude the Virtual POV_CODE bit fields. + if (dinput_ctrl_map[i] < DIRECTINPUT_RGBBUTTONS_MAX && js.rgbButtons[dinput_ctrl_map[i]] & 0x80) { input_state.pad_buttons |= dinput_ctrl_map[i+1]; } @@ -164,3 +186,34 @@ int DinputDevice::UpdateState(InputState &input_state) return UPDATESTATE_SKIP_PAD; } +int DinputDevice::UpdateRawStateSingle(RawInputState &rawState) +{ + if (g_Config.iForceInputDevice == 0) return FALSE; + if (!pJoystick) return FALSE; + + DIJOYSTATE2 js; + + if (FAILED(pJoystick->Poll())) + { + if(pJoystick->Acquire() == DIERR_INPUTLOST) + return FALSE; + } + + if(FAILED(pJoystick->GetDeviceState(sizeof(DIJOYSTATE2), &js))) + return -1; + switch (js.rgdwPOV[0]) + { + case JOY_POVFORWARD: rawState.button = POV_CODE_UP; return TRUE; + case JOY_POVBACKWARD: rawState.button = POV_CODE_DOWN; return TRUE; + case JOY_POVLEFT: rawState.button = POV_CODE_LEFT; return TRUE; + case JOY_POVRIGHT: rawState.button = POV_CODE_RIGHT; return TRUE; + } + + for (int i = 0; i < DIRECTINPUT_RGBBUTTONS_MAX; i++) { + if (js.rgbButtons[i] & 0x80) { + rawState.button = i; + return TRUE; + } + } + return FALSE; +} \ No newline at end of file diff --git a/Windows/DinputDevice.h b/Windows/DinputDevice.h index dfff31e552..0ba4330573 100644 --- a/Windows/DinputDevice.h +++ b/Windows/DinputDevice.h @@ -18,9 +18,12 @@ #pragma once #include #define DIRECTINPUT_VERSION 0x0800 +#define DIRECTINPUT_RGBBUTTONS_MAX 128 #include "InputDevice.h" #include "dinput.h" +struct RawInputState; + class DinputDevice : public InputDevice { @@ -29,9 +32,9 @@ public: ~DinputDevice(); virtual int UpdateState(InputState &input_state); virtual bool IsPad() { return true; } + int UpdateRawStateSingle(RawInputState &rawState); private: LPDIRECTINPUT8 pDI; LPDIRECTINPUTDEVICE8 pJoystick; bool analog; }; - diff --git a/Windows/KeyboardDevice.cpp b/Windows/KeyboardDevice.cpp index 9d72541a46..50eb0c2f6b 100644 --- a/Windows/KeyboardDevice.cpp +++ b/Windows/KeyboardDevice.cpp @@ -1,4 +1,5 @@ #include "input/input_state.h" +#include "ControlMapping.h" #include "Windows/WndMainWindow.h" #include "KeyboardDevice.h" #include "../Common/CommonTypes.h" @@ -6,21 +7,24 @@ #include "WinUser.h" unsigned int key_pad_map[] = { - VK_TAB, PAD_BUTTON_UNTHROTTLE, - VK_SPACE, PAD_BUTTON_START, - 'V', PAD_BUTTON_SELECT, + VK_ESCAPE,PAD_BUTTON_MENU, // Open PauseScreen + VK_BACK, PAD_BUTTON_BACK, // Toggle PauseScreen & Back Setting Page + 'Z', PAD_BUTTON_A, + 'X', PAD_BUTTON_B, 'A', PAD_BUTTON_X, 'S', PAD_BUTTON_Y, - 'X', PAD_BUTTON_B, - 'Z', PAD_BUTTON_A, + 'V', PAD_BUTTON_SELECT, + VK_SPACE, PAD_BUTTON_START, 'Q', PAD_BUTTON_LBUMPER, 'W', PAD_BUTTON_RBUMPER, + VK_TAB, PAD_BUTTON_LEFT_THUMB, // Turbo + VK_PAUSE, PAD_BUTTON_RIGHT_THUMB, // Open PauseScreen VK_UP, PAD_BUTTON_UP, VK_DOWN, PAD_BUTTON_DOWN, VK_LEFT, PAD_BUTTON_LEFT, VK_RIGHT, PAD_BUTTON_RIGHT, - VK_F3, PAD_BUTTON_LEFT_THUMB, }; +const unsigned int key_pad_map_size = sizeof(key_pad_map); unsigned short analog_ctrl_map[] = { 'I', CTRL_UP, @@ -28,6 +32,7 @@ unsigned short analog_ctrl_map[] = { 'J', CTRL_LEFT, 'L', CTRL_RIGHT, }; +const unsigned int analog_ctrl_map_size = sizeof(analog_ctrl_map); int KeyboardDevice::UpdateState(InputState &input_state) { if (MainWindow::GetHWND() != GetForegroundWindow()) return -1; @@ -190,22 +195,3 @@ const char * getVirtualKeyName(unsigned char key) return 0; } -bool saveControlsToFile() { - FILE *wfp = fopen("PPSSPPControls.dat", "wb"); - if (!wfp) - return false; - fwrite(key_pad_map, 1, sizeof(key_pad_map), wfp); - fwrite(analog_ctrl_map, 1, sizeof(analog_ctrl_map), wfp); - fclose(wfp); - return true; -} - -bool loadControlsFromFile() { - FILE *rfp = fopen("PPSSPPControls.dat", "rb"); - if (!rfp) - return false; - fread(key_pad_map, 1, sizeof(key_pad_map), rfp); - fread(analog_ctrl_map, 1, sizeof(analog_ctrl_map), rfp); - fclose(rfp); - return true; -} diff --git a/Windows/PPSSPP.vcxproj b/Windows/PPSSPP.vcxproj index 0a47f37212..a2c134224a 100644 --- a/Windows/PPSSPP.vcxproj +++ b/Windows/PPSSPP.vcxproj @@ -251,6 +251,7 @@ + @@ -288,6 +289,7 @@ + diff --git a/Windows/PPSSPP.vcxproj.filters b/Windows/PPSSPP.vcxproj.filters index 12832db0f3..1ca00ab3a8 100644 --- a/Windows/PPSSPP.vcxproj.filters +++ b/Windows/PPSSPP.vcxproj.filters @@ -98,6 +98,9 @@ Windows\Input + + Windows\Input + @@ -173,6 +176,9 @@ Windows\Input + + Windows\Input + diff --git a/Windows/WndMainWindow.cpp b/Windows/WndMainWindow.cpp index 8a51366293..efddffe1a4 100644 --- a/Windows/WndMainWindow.cpp +++ b/Windows/WndMainWindow.cpp @@ -35,6 +35,7 @@ #include "GPU/GPUState.h" #include "native/image/png_load.h" #include "GPU/GLES/TextureScaler.h" +#include "ControlMapping.h" #ifdef THEMES #include "XPTheme.h" @@ -46,13 +47,10 @@ BOOL g_bFullScreen = FALSE; static RECT g_normalRC = {0}; extern InputState input_state; +extern const char * getVirtualKeyName(unsigned char key); +extern const char * getXinputButtonName(unsigned int button); #define TIMER_CURSORUPDATE 1 #define CURSORUPDATE_INTERVAL_MS 50 -extern unsigned short analog_ctrl_map[]; -extern unsigned int key_pad_map[]; -extern const char * getVirtualKeyName(unsigned char key); -extern bool saveControlsToFile(); -extern bool loadControlsFromFile(); namespace MainWindow { @@ -922,30 +920,48 @@ namespace MainWindow return FALSE; } +#define CONTROLS_IDC_EDIT_BIGIN IDC_EDIT_KEY_MENU +#define CONTROLS_IDC_EDIT_END IDC_EDIT_KEY_ANALOG_RIGHT +#define CONTROLS_BUTTONS_COUNT IDC_EDIT_KEYRIGHT - CONTROLS_IDC_EDIT_BIGIN + 1 +#define CONTROLS_BUTTONNAME_MAX 16 +// for controls dialog device polling and bind update. +#define TIMER_CONTROLS_BINDUPDATE 1 +#define BINDUPDATE_INTERVAL_MS 50 + static const char *controllist[] = { - "TURBO MODE\tHold TAB", - "Start\tSpace", - "Select\tV", - "Square\tA", - "Triangle\tS", - "Circle\tX", - "Cross\tZ", - "Left Trigger\tQ", - "Right Trigger\tW", - "Up\tArrow Up", - "Down\tArrow Down", - "Left\tArrow Left", - "Right\tArrow Right", - "Analog Up\tI", - "Analog Down\tK", - "Analog Left\tJ", - "Analog Right\tL", - "Rapid Fire\tShift", + "Menu", // Open PauseScreen + "Back", // Toggle PauseScreen & Back Setting Page. + "Triangle", + "Rectangle", + "Cross", + "Circle", + "Select", + "Start", + "Left Trigger", + "Right Trigger", + "Turbo", // LBUMPER (Turbo) + "Reserved", // RBUMPER (Open PauseScreen) + "Up", + "Down", + "Left", + "Right", + "LY+", + "LY-", + "LX-", + "LX+", }; - static HHOOK pKeydownHook; - static const int control_map_size = IDC_EDIT_KEY_ANALOG_RIGHT - IDC_EDIT_KEY_TURBO + 1; - static u8 control_map[control_map_size]; + struct ControlsDlgState { + HHOOK pKeydownHook; + HBITMAP hbmPspImage; + HWND hCtrlTab; + UINT_PTR timerId; + WNDPROC orgPSPImageProc; + ControlMapping *pCtrlMap; + HWND hStaticPspImage; + }; + static ControlsDlgState *pCtrlDlgState; + RECT getRedrawRect(HWND hWnd) { RECT rc; HWND hDlg = GetParent(hWnd); @@ -962,23 +978,25 @@ namespace MainWindow LRESULT CALLBACK KeyboardProc(int nCode, WPARAM wParam, LPARAM lParam) { - HWND hEdit = GetFocus(); - UINT nCtrlID = GetDlgCtrlID(hEdit); - if (nCtrlID < IDC_EDIT_KEY_TURBO || nCtrlID > IDC_EDIT_KEY_ANALOG_RIGHT) { - return CallNextHookEx(pKeydownHook, nCode, wParam, lParam); - } - if (!(lParam&(1<<31))) { - // key down - HWND hDlg = GetParent(hEdit); - const char *str = getVirtualKeyName(wParam); - if (str) { - control_map[nCtrlID - IDC_EDIT_KEY_TURBO] = wParam; - SetWindowTextA(hEdit, str); - RECT rc = getRedrawRect(hEdit); - InvalidateRect(hDlg, &rc, false); + if (pCtrlDlgState->pCtrlMap->GetTargetDevice() == CONTROLS_KEYBOARD_INDEX) { + HWND hEdit = GetFocus(); + UINT nCtrlID = GetDlgCtrlID(hEdit); + if (nCtrlID < CONTROLS_IDC_EDIT_BIGIN || nCtrlID > CONTROLS_IDC_EDIT_END) { + return CallNextHookEx(pCtrlDlgState->pKeydownHook, nCode, wParam, lParam); + } + if (!(lParam&(1<<31))) { + // key down + HWND hDlg = GetParent(hEdit); + const char *str = getVirtualKeyName(wParam); + if (str) { + pCtrlDlgState->pCtrlMap->SetBindCode(wParam); + SetWindowTextA(hEdit, str); + RECT rc = getRedrawRect(hEdit); + InvalidateRect(hDlg, &rc, false); + } + else + MessageBoxA(hDlg, "Not supported!", "controller", MB_OK); } - else - MessageBoxA(hDlg, "Not supported!", "controller", MB_OK); } return 1; } @@ -1011,22 +1029,85 @@ namespace MainWindow DeleteObject(hCompDC); } + // Draw background image of Controls Dialog (pspmode.png) by use static control. + LRESULT CALLBACK PSPImageProc(HWND hStatic, UINT message, WPARAM wParam, LPARAM lParam) + { + + switch(message) { + case WM_PAINT: + { + PAINTSTRUCT pst; + HDC hdc = BeginPaint(hStatic, &pst); + + BITMAP bm; + GetObject(pCtrlDlgState->hbmPspImage, sizeof(BITMAP), &bm); + BitBlt(pCtrlDlgState->hbmPspImage, hdc, 0, 0, bm.bmWidth, bm.bmHeight, 0 , 0); + EndPaint(hStatic, &pst); + + return TRUE; + } + default: + break; + } + return CallWindowProc(pCtrlDlgState->orgPSPImageProc, hStatic, message, wParam, lParam); + } + // Message handler for control box. LRESULT CALLBACK Controls(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) - { - static HBITMAP hbm = 0; + { switch (message) { case WM_INITDIALOG: W32Util::CenterWindow(hDlg); { + // IDC_EDIT_xxx is need continuous value to IDC_EDIT_KEY_ANALOG_RIGHT from IDC_EDIT_KEY_MENU. + // it is total 16. + // it is need the same order as the dinput_ctrl_map(and xinput/keyboard). + if (CONTROLS_BUTTONS_COUNT != 16) { + char mes[100]; + snprintf(mes, 100, "CONTROLS_BUTTONS_COUNT(%d) is need 16.", CONTROLS_BUTTONS_COUNT); + MessageBoxA(hDlg, mes, "Controls dialog init error.", MB_OK); + } + pCtrlDlgState = new ControlsDlgState(); + ZeroMemory(pCtrlDlgState, sizeof(ControlsDlgState)); + pCtrlDlgState->pCtrlMap = ControlMapping::CreateInstance(CONTROLS_BUTTONS_COUNT); + if (!pCtrlDlgState->pCtrlMap) { + MessageBoxA(hDlg, "Cannot Created ControlMapping instance.", "Controls dialog init error.", MB_OK); + } + pCtrlDlgState->pCtrlMap->SetTargetDevice(CONTROLS_KEYBOARD_INDEX); + + pCtrlDlgState->hCtrlTab = GetDlgItem(hDlg, IDC_TAB_INPUT_DEVICE); + TCITEM tcItem; + ZeroMemory(&tcItem, sizeof(tcItem)); + tcItem.mask = TCIF_TEXT; + tcItem.dwState = 0; + tcItem.pszText = "Keyboard"; + tcItem.cchTextMax = (int)strlen(tcItem.pszText)+1; + tcItem.iImage = 0; + TabCtrl_InsertItem(pCtrlDlgState->hCtrlTab, TabCtrl_GetItemCount(pCtrlDlgState->hCtrlTab),&tcItem); + tcItem.pszText = "DirectInput"; + tcItem.cchTextMax = (int)strlen(tcItem.pszText)+1; + TabCtrl_InsertItem(pCtrlDlgState->hCtrlTab, TabCtrl_GetItemCount(pCtrlDlgState->hCtrlTab),&tcItem); + tcItem.pszText = "XInput"; + tcItem.cchTextMax = (int)strlen(tcItem.pszText)+1; + TabCtrl_InsertItem(pCtrlDlgState->hCtrlTab, TabCtrl_GetItemCount(pCtrlDlgState->hCtrlTab),&tcItem); + int tp_w = 0, tp_h = 0; // TODO: connect to keyboard device instead { + HBITMAP hResBM = LoadImageFromResource(hInst, MAKEINTRESOURCE(IDB_IMAGE_PSP), "IMAGE"); - HDC hDC = GetDC(hDlg); - RECT clientRect; + pCtrlDlgState->hStaticPspImage = GetDlgItem(hDlg,IDC_STATIC_IMAGE_PSP); + RECT clientRect, tabPageRect, imgRect; + GetClientRect(hDlg, &clientRect); - HBITMAP hMemBM = CreateCompatibleBitmap(hDC, clientRect.right, clientRect.bottom); + memcpy(&tabPageRect, &clientRect, sizeof(RECT)); + TabCtrl_AdjustRect(pCtrlDlgState->hCtrlTab, FALSE, &tabPageRect); + tp_w = tabPageRect.right - tabPageRect.left; + tp_h = tabPageRect.bottom - tabPageRect.top; + MoveWindow(pCtrlDlgState->hStaticPspImage, tabPageRect.left, tabPageRect.top, tp_w, tp_h, FALSE); + + HDC hDC = GetDC(pCtrlDlgState->hStaticPspImage); + HBITMAP hMemBM = CreateCompatibleBitmap(hDC, tp_w, tp_h); HDC hResDC = CreateCompatibleDC(hDC); HDC hMemDC = CreateCompatibleDC(hDC); SelectObject(hResDC, hResBM); @@ -1035,24 +1116,28 @@ namespace MainWindow BITMAP bm; GetObject(hResBM, sizeof(BITMAP), &bm); SetStretchBltMode(hMemDC, HALFTONE); - StretchBlt(hMemDC, 0, 0, clientRect.right, clientRect.bottom, hResDC, 0, 0, bm.bmWidth, bm.bmHeight, SRCCOPY); - if (hbm) - DeleteObject(hbm); - hbm = hMemBM; + float scaleX = (float)bm.bmWidth / clientRect.right; + float scaleY = (float)bm.bmHeight / clientRect.bottom; + imgRect.left = (int)(tabPageRect.left * scaleX); + imgRect.top = (int)(tabPageRect.top * scaleY); + imgRect.right= (int)(bm.bmWidth - ((clientRect.right - tabPageRect.right) * scaleX)); + imgRect.bottom = (int)(bm.bmHeight - ((clientRect.bottom - tabPageRect.bottom) * scaleY)); + StretchBlt(hMemDC, 0, 0, tp_w, tp_h, hResDC, imgRect.left, imgRect.top, + imgRect.right - imgRect.left, imgRect.bottom - imgRect.top, SRCCOPY); + if (pCtrlDlgState->hbmPspImage) + DeleteObject(pCtrlDlgState->hbmPspImage); + pCtrlDlgState->hbmPspImage = hMemBM; DeleteDC(hMemDC); DeleteDC(hResDC); - ReleaseDC(hDlg, hDC); + ReleaseDC(pCtrlDlgState->hStaticPspImage, hDC); DeleteObject(hResBM); } - int key_pad_size = (IDC_EDIT_KEYRIGHT - IDC_EDIT_KEY_TURBO + 1); - for (u32 i = 0; i <= IDC_EDIT_KEY_ANALOG_RIGHT - IDC_EDIT_KEY_TURBO; i++) { - HWND hEdit = GetDlgItem(hDlg, IDC_EDIT_KEY_TURBO + i); - if (IDC_EDIT_KEY_TURBO + i <= IDC_EDIT_KEYRIGHT) - control_map[i] = key_pad_map[i * 2]; - else - control_map[i] = analog_ctrl_map[(i - key_pad_size) * 2]; - SetWindowTextA(hEdit, getVirtualKeyName(control_map[i])); + + for (u32 i = 0; i <= CONTROLS_IDC_EDIT_END - CONTROLS_IDC_EDIT_BIGIN; i++) { + HWND hEdit = GetDlgItem(hDlg, CONTROLS_IDC_EDIT_BIGIN + i); + SetWindowTextA(hEdit, getVirtualKeyName(pCtrlDlgState->pCtrlMap->GetBindCode(CONTROLS_KEYBOARD_INDEX, i))); + } ComboBox_AddString(GetDlgItem(hDlg, IDC_FORCE_INPUT_DEVICE), "None"); ComboBox_AddString(GetDlgItem(hDlg, IDC_FORCE_INPUT_DEVICE), "XInput"); @@ -1065,28 +1150,171 @@ namespace MainWindow { ComboBox_SetCurSel(GetDlgItem(hDlg, IDC_FORCE_INPUT_DEVICE), (g_Config.iForceInputDevice + 1)); } + pCtrlDlgState->orgPSPImageProc = (WNDPROC)GetWindowLong(pCtrlDlgState->hStaticPspImage, GWL_WNDPROC); + SetWindowLong(pCtrlDlgState->hStaticPspImage, GWL_WNDPROC, (LONG)PSPImageProc); DWORD dwThreadID = GetWindowThreadProcessId(hDlg, NULL); - pKeydownHook = SetWindowsHookEx(WH_KEYBOARD,KeyboardProc, NULL, dwThreadID); + pCtrlDlgState->pKeydownHook = SetWindowsHookEx(WH_KEYBOARD,KeyboardProc, NULL, dwThreadID); + + pCtrlDlgState->timerId = SetTimer(hDlg, TIMER_CONTROLS_BINDUPDATE, BINDUPDATE_INTERVAL_MS, 0); } return TRUE; + case WM_TIMER: + { + if (wParam == TIMER_CONTROLS_BINDUPDATE && + pCtrlDlgState->pCtrlMap->GetTargetDevice() != CONTROLS_KEYBOARD_INDEX) { + HWND hEdit = GetFocus(); + UINT nCtrlID = GetDlgCtrlID(hEdit); + if (nCtrlID < CONTROLS_IDC_EDIT_BIGIN || nCtrlID > IDC_EDIT_KEYRIGHT) { + break; + } + // device polling and update. + int prevButton = pCtrlDlgState->pCtrlMap->GetBindCode(); + pCtrlDlgState->pCtrlMap->UpdateState(); + char str[CONTROLS_BUTTONNAME_MAX]; + ZeroMemory(str, CONTROLS_BUTTONNAME_MAX * sizeof(char)); + int buttonCode = pCtrlDlgState->pCtrlMap->GetBindCode(); + if (buttonCode == -1 || prevButton == buttonCode) + break; + + switch(pCtrlDlgState->pCtrlMap->GetTargetDevice()) + { + case CONTROLS_KEYBOARD_INDEX: + { + ; // leave it to KeyboardProc. + } + break; + case CONTROLS_DIRECT_INPUT_INDEX: + { + if (buttonCode > 0xFF) { + int n = 1; + for (int i = buttonCode >> 8; i > 1; i >>= 1) { + n++; + } + snprintf(str, CONTROLS_BUTTONNAME_MAX, "%s", + controllist[(IDC_EDIT_KEYUP - CONTROLS_IDC_EDIT_BIGIN - 1) + n]); + } else { + snprintf(str, CONTROLS_BUTTONNAME_MAX, "%d", buttonCode + 1); + } + SetWindowTextA(hEdit, str); + RECT rc = getRedrawRect(hEdit); + InvalidateRect(hDlg, &rc, FALSE); + } + break; + case CONTROLS_XINPUT_INDEX: + { + SetWindowText(hEdit, getXinputButtonName(buttonCode)); + RECT rc = getRedrawRect(hEdit); + InvalidateRect(hDlg, &rc, FALSE); + } + break; + } + } + } + break; + case WM_NOTIFY: + { + switch (((NMHDR *)lParam)->code) + { + case TCN_SELCHANGE: + { + int cursel = TabCtrl_GetCurSel(pCtrlDlgState->hCtrlTab); + pCtrlDlgState->pCtrlMap->SetTargetDevice(cursel); + switch (cursel) + { + case CONTROLS_KEYBOARD_INDEX: + { + for (u32 i = 0; i <= CONTROLS_IDC_EDIT_END - CONTROLS_IDC_EDIT_BIGIN; i++) { + HWND hEdit = GetDlgItem(hDlg, CONTROLS_IDC_EDIT_BIGIN + i); + if (i >= IDC_EDIT_KEY_ANALOG_UP - CONTROLS_IDC_EDIT_BIGIN) { + Edit_SetReadOnly(hEdit, FALSE); + } + SetWindowTextA(hEdit, getVirtualKeyName(pCtrlDlgState->pCtrlMap->GetBindCode(i))); + } + InvalidateRect(hDlg, 0, 0); + } + break; + case CONTROLS_DIRECT_INPUT_INDEX: + { + + for (u32 i = 0; i <= CONTROLS_IDC_EDIT_END - CONTROLS_IDC_EDIT_BIGIN; i++) { + HWND hEdit = GetDlgItem(hDlg, CONTROLS_IDC_EDIT_BIGIN + i); + char str[16]; + if (i >= IDC_EDIT_KEYUP - CONTROLS_IDC_EDIT_BIGIN) { + if (i >= IDC_EDIT_KEY_ANALOG_UP - CONTROLS_IDC_EDIT_BIGIN) { + Edit_SetReadOnly(hEdit, TRUE); + SetWindowTextA(hEdit, controllist[i]); + } else { + int n = 1; + int buttonCode = pCtrlDlgState->pCtrlMap->GetBindCode(i); + for (int i = buttonCode >> 8; i > 1; i >>= 1) { + n++; + } + snprintf(str, CONTROLS_BUTTONNAME_MAX, "%s", + controllist[(IDC_EDIT_KEYUP - CONTROLS_IDC_EDIT_BIGIN - 1) + n]); + SetWindowTextA(hEdit, str); + } + continue; + } + snprintf(str, CONTROLS_BUTTONNAME_MAX, "%d", pCtrlDlgState->pCtrlMap->GetBindCode(i) + 1); + SetWindowTextA(hEdit, str); + } + InvalidateRect(hDlg, 0, 0); + } + break; + case CONTROLS_XINPUT_INDEX: + { + for (u32 i = 0; i <= CONTROLS_IDC_EDIT_END - CONTROLS_IDC_EDIT_BIGIN; i++) { + HWND hEdit = GetDlgItem(hDlg, CONTROLS_IDC_EDIT_BIGIN + i); + if (i >= IDC_EDIT_KEY_ANALOG_UP - CONTROLS_IDC_EDIT_BIGIN) { + Edit_SetReadOnly(hEdit, TRUE); + SetWindowTextA(hEdit, controllist[i]); + continue; + } + u32 button = pCtrlDlgState->pCtrlMap->GetBindCode(i); + if (button == 0) { + SetWindowTextA(hEdit, "Disabled"); + } else { + SetWindowTextA(hEdit, getXinputButtonName(button)); + } + } + InvalidateRect(hDlg, 0, 0); + } + break; + default: + break; + } // pCtrlDlgState->curDevice + } // TCN_SELCHANGING: + break; + default: + break; + } // ((NMHDR *)lParam)->code + } // WM_NOTIFY: + break; case WM_PAINT: { - PAINTSTRUCT pst; - HDC hdc = BeginPaint(hDlg, &pst); - BITMAP bm; - GetObject(hbm, sizeof(BITMAP), &bm); - int width = bm.bmWidth; - int height = bm.bmHeight; - BitBlt(hbm, hdc, 0, 0, width, height, 0 , 0); - EndPaint(hDlg, &pst); - return TRUE; + return DefWindowProc(hDlg, message, wParam, lParam); } case WM_CTLCOLORSTATIC: { HDC hdc=(HDC)wParam; + HWND hCtrl = (HWND)lParam; SetBkMode(hdc, TRANSPARENT); + int ctrlId = GetDlgCtrlID(hCtrl); + if (ctrlId >= IDC_EDIT_KEY_ANALOG_UP && ctrlId <= IDC_EDIT_KEY_ANALOG_RIGHT) { + SetTextColor(hdc, RGB(128,128,128)); + RECT rc = getRedrawRect(hCtrl); + TabCtrl_AdjustRect(pCtrlDlgState->hCtrlTab, TRUE, &rc); + RECT clientrc; + GetClientRect(hCtrl, &clientrc); + TabCtrl_AdjustRect(pCtrlDlgState->hCtrlTab, TRUE, &clientrc); + BitBlt(pCtrlDlgState->hbmPspImage, hdc, 0, 0, rc.right - rc.left, rc.bottom - rc.top, rc.left, rc.top); + char str[11]; + GetWindowTextA(hCtrl, str, 10); + DrawTextA(hdc, str, (int)strlen(str), &clientrc, DT_CENTER|DT_SINGLELINE); + } return (LRESULT)GetStockObject(NULL_BRUSH); } + case WM_CTLCOLOREDIT: { if ((HWND)lParam == GetDlgItem(hDlg, IDC_FORCE_INPUT_DEVICE)) @@ -1096,9 +1324,11 @@ namespace MainWindow SetTextColor(hdc, RGB(255, 0, 0)); HWND hEdit = (HWND)lParam; RECT rc = getRedrawRect(hEdit); + TabCtrl_AdjustRect(pCtrlDlgState->hCtrlTab, TRUE, &rc); RECT clientrc; GetClientRect(hEdit, &clientrc); - BitBlt(hbm, hdc, 0, 0, rc.right - rc.left, rc.bottom - rc.top, rc.left, rc.top); + TabCtrl_AdjustRect(pCtrlDlgState->hCtrlTab, TRUE, &clientrc); + BitBlt(pCtrlDlgState->hbmPspImage, hdc, 0, 0, rc.right - rc.left, rc.bottom - rc.top, rc.left, rc.top); char str[11]; GetWindowTextA(hEdit, str, 10); DrawTextA(hdc, str, (int)strlen(str), &clientrc, DT_CENTER|DT_SINGLELINE); @@ -1109,22 +1339,31 @@ namespace MainWindow { if (LOWORD(wParam) == IDOK) { g_Config.iForceInputDevice = (ComboBox_GetCurSel(GetDlgItem(hDlg, IDC_FORCE_INPUT_DEVICE)) - 1); - int key_pad_size = (IDC_EDIT_KEYRIGHT - IDC_EDIT_KEY_TURBO + 1); - for (u32 i = 0; i <= IDC_EDIT_KEY_ANALOG_RIGHT - IDC_EDIT_KEY_TURBO; i++) { - if (IDC_EDIT_KEY_TURBO + i <= IDC_EDIT_KEYRIGHT) - key_pad_map[i * 2] = control_map[i]; - else - analog_ctrl_map[(i - key_pad_size) * 2] = control_map[i]; - } + pCtrlDlgState->pCtrlMap->BindToDevices(); saveControlsToFile(); } - UnhookWindowsHookEx(pKeydownHook); + UnhookWindowsHookEx(pCtrlDlgState->pKeydownHook); + KillTimer(hDlg, pCtrlDlgState->timerId); + SetWindowLong(pCtrlDlgState->hStaticPspImage, GWL_WNDPROC, (LONG)pCtrlDlgState->orgPSPImageProc); EndDialog(hDlg, LOWORD(wParam)); - if (hbm) { - DeleteObject(hbm); - hbm = 0; + if (pCtrlDlgState->hbmPspImage) { + DeleteObject(pCtrlDlgState->hbmPspImage); + pCtrlDlgState->hbmPspImage = 0; + } + if (pCtrlDlgState) { + delete pCtrlDlgState; + pCtrlDlgState = NULL; } return TRUE; + } else if (LOWORD(wParam) >= CONTROLS_IDC_EDIT_BIGIN && + LOWORD(wParam) <= IDC_EDIT_KEYRIGHT && + HIWORD(wParam) == EN_SETFOCUS) { + // send about buttonsMap-index of current focus Edit-Control to ControlMapping instance. + UINT nCtrlID = LOWORD(wParam); + if (nCtrlID < CONTROLS_IDC_EDIT_BIGIN || nCtrlID > IDC_EDIT_KEYRIGHT) { + break; + } + pCtrlDlgState->pCtrlMap->SetTargetButton(nCtrlID - CONTROLS_IDC_EDIT_BIGIN); } break; } diff --git a/Windows/XinputDevice.cpp b/Windows/XinputDevice.cpp index 63be6da75e..9088a9c25c 100644 --- a/Windows/XinputDevice.cpp +++ b/Windows/XinputDevice.cpp @@ -1,12 +1,36 @@ #include -#include "XinputDevice.h" #include "Core/Config.h" #include "input/input_state.h" +#include "XinputDevice.h" +#include "ControlMapping.h" + #ifndef XUSER_MAX_COUNT #define XUSER_MAX_COUNT 4 #endif +// Yes, this maps more than the PSP has, but that's fine as this lets us +// map buttons to extra functionality like speedup. +unsigned int xinput_ctrl_map[] = { + XBOX_CODE_LEFTTRIGER, PAD_BUTTON_MENU, + XBOX_CODE_RIGHTTRIGER, PAD_BUTTON_BACK, + XINPUT_GAMEPAD_A, PAD_BUTTON_A, + XINPUT_GAMEPAD_B, PAD_BUTTON_B, + XINPUT_GAMEPAD_X, PAD_BUTTON_X, + XINPUT_GAMEPAD_Y, PAD_BUTTON_Y, + XINPUT_GAMEPAD_BACK, PAD_BUTTON_SELECT, + XINPUT_GAMEPAD_START, PAD_BUTTON_START, + XINPUT_GAMEPAD_LEFT_SHOULDER, PAD_BUTTON_LBUMPER, + XINPUT_GAMEPAD_RIGHT_SHOULDER, PAD_BUTTON_RBUMPER, + XINPUT_GAMEPAD_LEFT_THUMB, PAD_BUTTON_LEFT_THUMB, // Turbo + XINPUT_GAMEPAD_RIGHT_THUMB, PAD_BUTTON_RIGHT_THUMB, // Open PauseScreen + XINPUT_GAMEPAD_DPAD_UP, PAD_BUTTON_UP, + XINPUT_GAMEPAD_DPAD_DOWN, PAD_BUTTON_DOWN, + XINPUT_GAMEPAD_DPAD_LEFT, PAD_BUTTON_LEFT, + XINPUT_GAMEPAD_DPAD_RIGHT, PAD_BUTTON_RIGHT, +}; +static const unsigned int xinput_ctrl_map_size = sizeof(xinput_ctrl_map); + XinputDevice::XinputDevice() { ZeroMemory( &this->prevState, sizeof(this->prevState) ); this->check_delay = 0; @@ -95,34 +119,93 @@ static Stick NormalizedDeadzoneFilter(short x, short y) { return left; } -// Yes, this maps more than the PSP has, but that's fine as this lets us -// map buttons to extra functionality like speedup. -static const unsigned int xinput_ctrl_map[] = { - XINPUT_GAMEPAD_DPAD_UP, PAD_BUTTON_UP, - XINPUT_GAMEPAD_DPAD_DOWN, PAD_BUTTON_DOWN, - XINPUT_GAMEPAD_DPAD_LEFT, PAD_BUTTON_LEFT, - XINPUT_GAMEPAD_DPAD_RIGHT, PAD_BUTTON_RIGHT, - XINPUT_GAMEPAD_START, PAD_BUTTON_START, - XINPUT_GAMEPAD_BACK, PAD_BUTTON_SELECT, - XINPUT_GAMEPAD_LEFT_SHOULDER, PAD_BUTTON_LBUMPER, - XINPUT_GAMEPAD_RIGHT_SHOULDER, PAD_BUTTON_RBUMPER, - XINPUT_GAMEPAD_A, PAD_BUTTON_A, - XINPUT_GAMEPAD_B, PAD_BUTTON_B, - XINPUT_GAMEPAD_X, PAD_BUTTON_X, - XINPUT_GAMEPAD_Y, PAD_BUTTON_Y, - XINPUT_GAMEPAD_LEFT_THUMB, PAD_BUTTON_LEFT_THUMB, - XINPUT_GAMEPAD_RIGHT_THUMB, PAD_BUTTON_RIGHT_THUMB, + + +struct xinput_button_name { + unsigned int button; + char name[10]; }; -static inline u32 CtrlForXinput(int xinput) { - for (int i = 0; i < sizeof(xinput_ctrl_map)/sizeof(xinput_ctrl_map[0]); i += 2) - if (xinput_ctrl_map[i] == xinput) return (u32) xinput_ctrl_map[i+1]; +const xinput_button_name xinput_name_map[] = { + {XBOX_CODE_LEFTTRIGER, "LT"}, + {XBOX_CODE_RIGHTTRIGER, "RT"}, + {XINPUT_GAMEPAD_A, "A"}, + {XINPUT_GAMEPAD_B, "B"}, + {XINPUT_GAMEPAD_X, "X"}, + {XINPUT_GAMEPAD_Y, "Y"}, + {XINPUT_GAMEPAD_BACK, "Back"}, + {XINPUT_GAMEPAD_START, "Start"}, + {XINPUT_GAMEPAD_LEFT_SHOULDER, "LB"}, + {XINPUT_GAMEPAD_RIGHT_SHOULDER, "RB"}, + {XINPUT_GAMEPAD_LEFT_THUMB, "LThumb"}, + {XINPUT_GAMEPAD_RIGHT_THUMB, "RThumb"}, + {XINPUT_GAMEPAD_DPAD_UP, "Up"}, + {XINPUT_GAMEPAD_DPAD_DOWN, "Down"}, + {XINPUT_GAMEPAD_DPAD_LEFT, "Left"}, + {XINPUT_GAMEPAD_DPAD_RIGHT, "Right"}, +}; +static const int xbutton_name_map_size = sizeof(xinput_name_map) / sizeof(xinput_button_name); + +const char * getXinputButtonName(unsigned int button) { + for (int i = 0; i < xbutton_name_map_size; i++) { + if (xinput_name_map[i].button == button) + return xinput_name_map[i].name; + } return 0; } void XinputDevice::ApplyDiff(XINPUT_STATE &state, InputState &input_state) { - for (int i = 1; i < USHRT_MAX; i <<= 1) { - if (state.Gamepad.wButtons & i) - input_state.pad_buttons |= CtrlForXinput(i); + // Skip XBOX_CODE_LIFT/RIGHT TRIGER. it's virtual code for Mapping. + for (int i = 0; i < sizeof(xinput_ctrl_map)/sizeof(xinput_ctrl_map[0]); i += 2) { + if (state.Gamepad.wButtons & (WORD)(xinput_ctrl_map[i])) { + input_state.pad_buttons |= (int)xinput_ctrl_map[i + 1]; + } + // Effective use of triggers that are not used. + if (xinput_ctrl_map[i] == XBOX_CODE_LEFTTRIGER && + state.Gamepad.bLeftTrigger > XINPUT_GAMEPAD_TRIGGER_THRESHOLD) { + input_state.pad_buttons |= xinput_ctrl_map[i + 1]; + } + if (xinput_ctrl_map[i] == XBOX_CODE_RIGHTTRIGER && + state.Gamepad.bRightTrigger > XINPUT_GAMEPAD_TRIGGER_THRESHOLD) { + input_state.pad_buttons |= xinput_ctrl_map[i + 1]; + } + } -} \ No newline at end of file +} +int XinputDevice::UpdateRawStateSingle(RawInputState &rawState) +{ + if (g_Config.iForceInputDevice > 0) return -1; + + XINPUT_STATE state; + ZeroMemory( &state, sizeof(XINPUT_STATE) ); + + DWORD dwResult; + if (this->gamepad_idx >= 0) + dwResult = XInputGetState( this->gamepad_idx, &state ); + else { + // use the first gamepad that responds + for (int i = 0; i < XUSER_MAX_COUNT; i++) { + dwResult = XInputGetState( i, &state ); + if (dwResult == ERROR_SUCCESS) { + this->gamepad_idx = i; + break; + } + } + } + + if ( dwResult == ERROR_SUCCESS ) { + for (UINT bit = XINPUT_GAMEPAD_DPAD_UP; bit <= XINPUT_GAMEPAD_Y; bit <<= 1) { + if (state.Gamepad.wButtons & bit) { + rawState.button = bit; + break; + } + } + if (state.Gamepad.bLeftTrigger > XINPUT_GAMEPAD_TRIGGER_THRESHOLD) { + rawState.button = XBOX_CODE_LEFTTRIGER; + } else if (state.Gamepad.bRightTrigger > XINPUT_GAMEPAD_TRIGGER_THRESHOLD) { + rawState.button = XBOX_CODE_RIGHTTRIGER; + } + return TRUE; + } + return FALSE; +} diff --git a/Windows/XinputDevice.h b/Windows/XinputDevice.h index 8e464de520..6b5f44e7bc 100644 --- a/Windows/XinputDevice.h +++ b/Windows/XinputDevice.h @@ -2,6 +2,8 @@ #include "InputDevice.h" #include "Xinput.h" +struct RawInputState; + class XinputDevice : public InputDevice { @@ -9,6 +11,7 @@ public: XinputDevice(); virtual int UpdateState(InputState &input_state); virtual bool IsPad() { return true; } + int UpdateRawStateSingle(RawInputState &rawState); private: void ApplyDiff(XINPUT_STATE &state, InputState &input_state); int gamepad_idx; diff --git a/Windows/ppsspp.rc b/Windows/ppsspp.rc index e309227e77..be5d713fff 100644 Binary files a/Windows/ppsspp.rc and b/Windows/ppsspp.rc differ diff --git a/Windows/resource.h b/Windows/resource.h index ae11d8f207..a476ec8269 100644 Binary files a/Windows/resource.h and b/Windows/resource.h differ