Merge pull request #2290 from ergo720/hw_passthrough
Add support for original xbox gamepads and SBC hardware via USB passthrough with libusb
This commit is contained in:
commit
f7042be933
|
@ -35,3 +35,7 @@
|
|||
path = import/SDL2
|
||||
url = https://github.com/libsdl-org/SDL
|
||||
shallow = true
|
||||
[submodule "import/libusb"]
|
||||
path = import/libusb
|
||||
url = https://github.com/libusb/libusb
|
||||
shallow = true
|
||||
|
|
|
@ -41,6 +41,8 @@ add_subdirectory("${CMAKE_CURRENT_LIST_DIR}/projects/libtom")
|
|||
|
||||
add_subdirectory("${CMAKE_CURRENT_LIST_DIR}/projects/imgui")
|
||||
|
||||
add_subdirectory("${CMAKE_CURRENT_LIST_DIR}/projects/libusb")
|
||||
|
||||
# Split the files into group for which project is likely
|
||||
# going to be used for both header and source files.
|
||||
# Then move only specific project files into their
|
||||
|
@ -61,6 +63,7 @@ file (GLOB CXBXR_HEADER_COMMON
|
|||
"${CXBXR_ROOT_DIR}/src/common/input/DInputKeyboardCodes.h"
|
||||
"${CXBXR_ROOT_DIR}/src/common/input/DInputKeyboardMouse.h"
|
||||
"${CXBXR_ROOT_DIR}/src/common/input/layout_xbox_device.h"
|
||||
"${CXBXR_ROOT_DIR}/src/common/input/LibusbDevice.h"
|
||||
"${CXBXR_ROOT_DIR}/src/common/input/InputDevice.h"
|
||||
"${CXBXR_ROOT_DIR}/src/common/input/InputManager.h"
|
||||
"${CXBXR_ROOT_DIR}/src/common/input/SdlJoystick.h"
|
||||
|
@ -102,6 +105,7 @@ file (GLOB CXBXR_HEADER_GUIv1
|
|||
"${CXBXR_ROOT_DIR}/src/common/input/EmuDevice.h"
|
||||
"${CXBXR_ROOT_DIR}/src/common/input/InputWindow.h"
|
||||
"${CXBXR_ROOT_DIR}/src/gui/controllers/DlgDukeControllerConfig.h"
|
||||
"${CXBXR_ROOT_DIR}/src/gui/controllers/DlgLibusbControllerConfig.h"
|
||||
"${CXBXR_ROOT_DIR}/src/gui/controllers/DlgSBControllerConfig.h"
|
||||
"${CXBXR_ROOT_DIR}/src/gui/DlgAbout.h"
|
||||
"${CXBXR_ROOT_DIR}/src/gui/DlgAudioConfig.h"
|
||||
|
@ -234,6 +238,7 @@ file (GLOB CXBXR_SOURCE_COMMON
|
|||
"${CXBXR_ROOT_DIR}/src/common/input/DInputKeyboardMouse.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/common/input/InputDevice.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/common/input/InputManager.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/common/input/LibusbDevice.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/common/input/SdlJoystick.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/common/input/XInputPad.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/common/input/RawDevice.cpp"
|
||||
|
@ -268,6 +273,7 @@ file (GLOB CXBXR_SOURCE_GUIv1
|
|||
"${CXBXR_ROOT_DIR}/src/common/input/EmuDevice.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/common/input/InputWindow.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/gui/controllers/DlgDukeControllerConfig.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/gui/controllers/DlgLibusbControllerConfig.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/gui/controllers/DlgSBControllerConfig.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/gui/DlgAbout.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/gui/DlgAudioConfig.cpp"
|
||||
|
@ -423,7 +429,7 @@ add_subdirectory("${CMAKE_CURRENT_LIST_DIR}/projects/cxbxr-emu")
|
|||
set(cxbxr_INSTALL_files "COPYING" "README.md")
|
||||
|
||||
# Cxbx-Reloaded project with third-party libraries
|
||||
set_target_properties(cxbx cxbxr-ldr cxbxr-emu misc-batch SDL2 subhook libXbSymbolDatabase libtommath libtomcrypt imgui
|
||||
set_target_properties(cxbx cxbxr-ldr cxbxr-emu misc-batch SDL2 subhook libXbSymbolDatabase libtommath libtomcrypt imgui libusb
|
||||
PROPERTIES FOLDER Cxbx-Reloaded
|
||||
)
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@ Cxbx-Reloaded is an emulator for running Microsoft Xbox (and eventually, Chihiro
|
|||
* [32-bit (x86) Visual C++ 2019 Redistributable](https://aka.ms/vs/16/release/vc_redist.x86.exe)
|
||||
* [Npcap *(used for network emulation)*](https://nmap.org/npcap/#download)
|
||||
* Make sure to enable winpcap compatibility mode!
|
||||
* WinUSB compliant driver *(optional, only needed for USB pass-through of original xbox controllers and the steel battalion controller)*
|
||||
|
||||
### Wine
|
||||
**NOTICE: Please use the latest stable release version of Wine. If it does not work for you, then roll back to Wine 5.0.3 which is the last known working version.**
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
Subproject commit 8d7616886e062f58d0de12db2acd5d81e06240bb
|
|
@ -12,9 +12,10 @@ include_directories(
|
|||
"${CXBXR_ROOT_DIR}/src/common"
|
||||
"${CXBXR_ROOT_DIR}/src/common/Win32"
|
||||
"${CXBXR_ROOT_DIR}/import/OpenXDK/include"
|
||||
"${CXBXR_ROOT_DIR}/import/DirectX9/include"
|
||||
"${CXBXR_ROOT_DIR}/import/distorm/include"
|
||||
"${CXBXR_ROOT_DIR}/import/glew-2.0.0/include"
|
||||
"${CXBXR_ROOT_DIR}/import/DirectX9/include"
|
||||
"${CXBXR_ROOT_DIR}/import/libusb/libusb"
|
||||
"${CXBXR_ROOT_DIR}/import/simpleini"
|
||||
"${CXBXR_ROOT_DIR}/import/winpcap/Include"
|
||||
"${CXBXR_ROOT_DIR}/import/xxHash"
|
||||
|
@ -193,6 +194,7 @@ target_link_libraries(cxbx
|
|||
libtomcrypt
|
||||
SDL2
|
||||
imgui
|
||||
libusb
|
||||
|
||||
${WINS_LIB}
|
||||
)
|
||||
|
|
|
@ -15,9 +15,10 @@ include_directories(
|
|||
"${CXBXR_ROOT_DIR}/src/common"
|
||||
"${CXBXR_ROOT_DIR}/src/common/Win32"
|
||||
"${CXBXR_ROOT_DIR}/import/OpenXDK/include"
|
||||
"${CXBXR_ROOT_DIR}/import/DirectX9/include"
|
||||
"${CXBXR_ROOT_DIR}/import/distorm/include"
|
||||
"${CXBXR_ROOT_DIR}/import/glew-2.0.0/include"
|
||||
"${CXBXR_ROOT_DIR}/import/DirectX9/include"
|
||||
"${CXBXR_ROOT_DIR}/import/libusb/libusb"
|
||||
"${CXBXR_ROOT_DIR}/import/simpleini"
|
||||
"${CXBXR_ROOT_DIR}/import/winpcap/Include"
|
||||
"${CXBXR_ROOT_DIR}/import/xxHash"
|
||||
|
@ -169,6 +170,7 @@ target_link_libraries(cxbxr-emu
|
|||
libtomcrypt
|
||||
SDL2
|
||||
imgui
|
||||
libusb
|
||||
|
||||
${WINS_LIB}
|
||||
)
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
cmake_minimum_required (VERSION 3.8)
|
||||
project(libusb LANGUAGES CXX)
|
||||
# Since libusb doesn't have CMake, we'll make an interface project here.
|
||||
|
||||
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
|
||||
add_compile_definitions(
|
||||
_CRT_SECURE_NO_WARNINGS
|
||||
_CRT_NONSTDC_NO_DEPRECATE
|
||||
)
|
||||
endif()
|
||||
|
||||
include_directories(
|
||||
"${CXBXR_ROOT_DIR}/import/libusb/msvc"
|
||||
"${CXBXR_ROOT_DIR}/import/libusb/libusb")
|
||||
|
||||
file (GLOB HEADERS
|
||||
"${CXBXR_ROOT_DIR}/import/libusb/libusb/libusb.h"
|
||||
"${CXBXR_ROOT_DIR}/import/libusb/libusb/libusbi.h"
|
||||
"${CXBXR_ROOT_DIR}/import/libusb/libusb/version.h"
|
||||
"${CXBXR_ROOT_DIR}/import/libusb/libusb/version_nano.h"
|
||||
"${CXBXR_ROOT_DIR}/import/libusb/libusb/msvc/config.h"
|
||||
"${CXBXR_ROOT_DIR}/import/libusb/libusb/os/events_windows.h"
|
||||
"${CXBXR_ROOT_DIR}/import/libusb/libusb/os/threads_windows.h"
|
||||
"${CXBXR_ROOT_DIR}/import/libusb/libusb/os/windows_common.h"
|
||||
"${CXBXR_ROOT_DIR}/import/libusb/libusb/os/windows_usbdk.h"
|
||||
"${CXBXR_ROOT_DIR}/import/libusb/libusb/os/windows_winusb.h"
|
||||
)
|
||||
|
||||
file (GLOB SOURCES
|
||||
"${CXBXR_ROOT_DIR}/import/libusb/libusb/core.c"
|
||||
"${CXBXR_ROOT_DIR}/import/libusb/libusb/descriptor.c"
|
||||
"${CXBXR_ROOT_DIR}/import/libusb/libusb/hotplug.c"
|
||||
"${CXBXR_ROOT_DIR}/import/libusb/libusb/io.c"
|
||||
"${CXBXR_ROOT_DIR}/import/libusb/libusb/strerror.c"
|
||||
"${CXBXR_ROOT_DIR}/import/libusb/libusb/sync.c"
|
||||
"${CXBXR_ROOT_DIR}/import/libusb/libusb/os/events_windows.c"
|
||||
"${CXBXR_ROOT_DIR}/import/libusb/libusb/os/threads_windows.c"
|
||||
"${CXBXR_ROOT_DIR}/import/libusb/libusb/os/windows_common.c"
|
||||
"${CXBXR_ROOT_DIR}/import/libusb/libusb/os/windows_usbdk.c"
|
||||
"${CXBXR_ROOT_DIR}/import/libusb/libusb/os/windows_winusb.c"
|
||||
)
|
||||
|
||||
source_group(TREE ${CXBXR_ROOT_DIR}/import/libusb/libusb PREFIX header FILES ${HEADERS})
|
||||
|
||||
source_group(TREE ${CXBXR_ROOT_DIR}/import/libusb/libusb PREFIX source FILES ${SOURCES})
|
||||
|
||||
add_library(${PROJECT_NAME} ${HEADERS} ${SOURCES})
|
||||
|
||||
target_include_directories(${PROJECT_NAME}
|
||||
PUBLIC "${CXBXR_ROOT_DIR}/import/libusb"
|
||||
)
|
|
@ -82,6 +82,7 @@ const char* g_EnumModules2String[to_underlying(CXBXR_MODULE::MAX)] = {
|
|||
"XMO ",
|
||||
"RINP ",
|
||||
"JVS ",
|
||||
"LIBUSB ",
|
||||
"KRNL ",
|
||||
"LOG ",
|
||||
"XBOX ",
|
||||
|
|
|
@ -90,6 +90,7 @@ typedef enum class _CXBXR_MODULE: unsigned int {
|
|||
XMO,
|
||||
RINP,
|
||||
JVS,
|
||||
LIBUSB,
|
||||
// kernel
|
||||
KRNL,
|
||||
LOG,
|
||||
|
|
|
@ -785,19 +785,21 @@ void Settings::SyncToEmulator()
|
|||
g_EmuShared->SetInputSlotTypeSettings(&m_input_port[i].SlotType[SLOT_BOTTOM], i, SLOT_BOTTOM);
|
||||
if (m_input_port[i].Type != to_underlying(XBOX_INPUT_DEVICE::DEVICE_INVALID)) {
|
||||
g_EmuShared->SetInputDevNameSettings(m_input_port[i].DeviceName.c_str(), i);
|
||||
auto it = std::find_if(m_input_profiles[m_input_port[i].Type].begin(),
|
||||
m_input_profiles[m_input_port[i].Type].end(), [this, i](const auto& profile) {
|
||||
if (profile.ProfileName == m_input_port[i].ProfileName) {
|
||||
return true;
|
||||
if (m_input_port[i].Type < to_underlying(XBOX_INPUT_DEVICE::DEVICE_MAX)) {
|
||||
auto it = std::find_if(m_input_profiles[m_input_port[i].Type].begin(),
|
||||
m_input_profiles[m_input_port[i].Type].end(), [this, i](const auto &profile) {
|
||||
if (profile.ProfileName == m_input_port[i].ProfileName) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
if (it != m_input_profiles[m_input_port[i].Type].end()) {
|
||||
char controls_name[HIGHEST_NUM_BUTTONS][HOST_BUTTON_NAME_LENGTH];
|
||||
for (int index = 0; index < dev_num_buttons[m_input_port[i].Type]; index++) {
|
||||
strncpy(controls_name[index], it->ControlList[index].c_str(), 30);
|
||||
}
|
||||
return false;
|
||||
});
|
||||
if (it != m_input_profiles[m_input_port[i].Type].end()) {
|
||||
char controls_name[HIGHEST_NUM_BUTTONS][HOST_BUTTON_NAME_LENGTH];
|
||||
for (int index = 0; index < dev_num_buttons[m_input_port[i].Type]; index++) {
|
||||
strncpy(controls_name[index], it->ControlList[index].c_str(), 30);
|
||||
g_EmuShared->SetInputBindingsSettings(controls_name, dev_num_buttons[m_input_port[i].Type], i);
|
||||
}
|
||||
g_EmuShared->SetInputBindingsSettings(controls_name, dev_num_buttons[m_input_port[i].Type], i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -77,6 +77,14 @@ std::string GetInputDeviceName(int dev_type)
|
|||
str = "Arcade joystick";
|
||||
break;
|
||||
|
||||
case to_underlying(XBOX_INPUT_DEVICE::HW_STEEL_BATTALION_CONTROLLER):
|
||||
str = "Passthrough steel battalion controller";
|
||||
break;
|
||||
|
||||
case to_underlying(XBOX_INPUT_DEVICE::HW_XBOX_CONTROLLER):
|
||||
str = "Passthrough original xbox gamepad";
|
||||
break;
|
||||
|
||||
case to_underlying(XBOX_INPUT_DEVICE::DEVICE_INVALID):
|
||||
str = "None";
|
||||
break;
|
||||
|
|
|
@ -56,6 +56,9 @@ typedef enum class _XBOX_INPUT_DEVICE : int {
|
|||
STEEL_BATTALION_CONTROLLER,
|
||||
ARCADE_STICK,
|
||||
DEVICE_MAX,
|
||||
// Devices with the HW_ prefix (= hardware) indicate a real xbox device. Always add these after DEVICE_MAX
|
||||
HW_STEEL_BATTALION_CONTROLLER,
|
||||
HW_XBOX_CONTROLLER,
|
||||
}
|
||||
XBOX_INPUT_DEVICE;
|
||||
|
||||
|
@ -120,6 +123,8 @@ public:
|
|||
bool GetPort(std::string_view Port) const;
|
||||
// sets the port this device is attached to
|
||||
void SetPort(std::string_view Port, bool Connect);
|
||||
// retuns true if it is a libusb device, false otherwise
|
||||
virtual bool IsLibusb() const { return false; };
|
||||
|
||||
|
||||
protected:
|
||||
|
|
|
@ -41,6 +41,7 @@
|
|||
#include "XInputPad.h"
|
||||
#include "RawDevice.h"
|
||||
#include "DInputKeyboardMouse.h"
|
||||
#include "LibusbDevice.h"
|
||||
#include "InputManager.h"
|
||||
#include "..\devices\usb\XidGamepad.h"
|
||||
#include "core\kernel\exports\EmuKrnl.h" // For EmuLog
|
||||
|
@ -85,17 +86,19 @@ void InputDeviceManager::Initialize(bool is_gui, HWND hwnd)
|
|||
|
||||
XInput::Init(m_Mtx);
|
||||
RawInput::Init(m_Mtx, is_gui, m_hwnd);
|
||||
Libusb::Init(m_Mtx);
|
||||
Sdl::Init(m_Mtx, m_Cv, is_gui);
|
||||
});
|
||||
|
||||
m_Cv.wait(lck, []() {
|
||||
return (Sdl::InitStatus != Sdl::NOT_INIT) &&
|
||||
(XInput::InitStatus != XInput::NOT_INIT) &&
|
||||
(RawInput::InitStatus != RawInput::NOT_INIT);
|
||||
(RawInput::InitStatus != RawInput::NOT_INIT) &&
|
||||
(Libusb::InitStatus != Libusb::NOT_INIT);
|
||||
});
|
||||
lck.unlock();
|
||||
|
||||
if (Sdl::InitStatus < 0 || XInput::InitStatus < 0 || RawInput::InitStatus < 0) {
|
||||
if (Sdl::InitStatus < 0 || XInput::InitStatus < 0 || RawInput::InitStatus < 0 || Libusb::InitStatus < 0) {
|
||||
CxbxrKrnlAbort("Failed to initialize input subsystem! Consult debug log for more information");
|
||||
}
|
||||
|
||||
|
@ -131,6 +134,8 @@ void InputDeviceManager::Initialize(bool is_gui, HWND hwnd)
|
|||
|
||||
case to_underlying(XBOX_INPUT_DEVICE::ARCADE_STICK):
|
||||
case to_underlying(XBOX_INPUT_DEVICE::STEEL_BATTALION_CONTROLLER):
|
||||
case to_underlying(XBOX_INPUT_DEVICE::HW_XBOX_CONTROLLER):
|
||||
case to_underlying(XBOX_INPUT_DEVICE::HW_STEEL_BATTALION_CONTROLLER):
|
||||
ConstructHleInputDevice(&g_devs[CTRL_OFFSET + i], nullptr, type, port);
|
||||
BindHostDevice(type, port);
|
||||
break;
|
||||
|
@ -162,6 +167,7 @@ void InputDeviceManager::Shutdown()
|
|||
|
||||
XInput::DeInit();
|
||||
RawInput::DeInit();
|
||||
Libusb::DeInit();
|
||||
Sdl::DeInit(m_PollingThread);
|
||||
}
|
||||
|
||||
|
@ -313,6 +319,19 @@ void InputDeviceManager::BindHostDevice(int type, std::string_view port)
|
|||
// MUs don't have any host devices bound, so we just return
|
||||
return;
|
||||
}
|
||||
else if ((type == to_underlying(XBOX_INPUT_DEVICE::HW_XBOX_CONTROLLER)) ||
|
||||
(type == to_underlying(XBOX_INPUT_DEVICE::HW_STEEL_BATTALION_CONTROLLER))) {
|
||||
// libusb devices don't have any profiles, but we still need to attach them to the xbox port
|
||||
char dev_name[50];
|
||||
int port_num, slot;
|
||||
PortStr2Int(port, &port_num, &slot);
|
||||
g_EmuShared->GetInputDevNameSettings(dev_name, port_num);
|
||||
auto dev = FindDevice(std::string(dev_name));
|
||||
if (dev != nullptr) {
|
||||
dev->SetPort(port, true);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
char dev_name[50];
|
||||
char dev_control_names[HIGHEST_NUM_BUTTONS][HOST_BUTTON_NAME_LENGTH];
|
||||
|
@ -343,8 +362,6 @@ void InputDeviceManager::BindHostDevice(int type, std::string_view port)
|
|||
bool InputDeviceManager::UpdateXboxPortInput(int port, void* buffer, int direction, int type)
|
||||
{
|
||||
assert(direction == DIRECTION_IN || direction == DIRECTION_OUT);
|
||||
assert(type > to_underlying(XBOX_INPUT_DEVICE::DEVICE_INVALID) &&
|
||||
type < to_underlying(XBOX_INPUT_DEVICE::DEVICE_MAX));
|
||||
bool has_changed = false;
|
||||
|
||||
// First check if ImGui is focus, then ignore any input update occur.
|
||||
|
@ -371,6 +388,12 @@ bool InputDeviceManager::UpdateXboxPortInput(int port, void* buffer, int directi
|
|||
assert(0);
|
||||
break;
|
||||
|
||||
case to_underlying(XBOX_INPUT_DEVICE::HW_XBOX_CONTROLLER):
|
||||
case to_underlying(XBOX_INPUT_DEVICE::HW_STEEL_BATTALION_CONTROLLER):
|
||||
has_changed = UpdateInputHw(dev, buffer, direction);
|
||||
m_Mtx.unlock();
|
||||
return has_changed;
|
||||
|
||||
case to_underlying(XBOX_INPUT_DEVICE::LIGHT_GUN):
|
||||
case to_underlying(XBOX_INPUT_DEVICE::STEERING_WHEEL):
|
||||
case to_underlying(XBOX_INPUT_DEVICE::IR_DONGLE):
|
||||
|
@ -397,8 +420,7 @@ bool InputDeviceManager::UpdateInputXpad(std::shared_ptr<InputDevice>& Device, v
|
|||
return false;
|
||||
}
|
||||
|
||||
//XpadInput* in_buf = reinterpret_cast<XpadInput*>(static_cast<uint8_t*>(Buffer) + 2); lle usb
|
||||
XpadInput *in_buf = static_cast<XpadInput *>(Buffer);
|
||||
XpadInput* in_buf = reinterpret_cast<XpadInput*>(static_cast<uint8_t*>(Buffer) + XID_PACKET_HEADER);
|
||||
for (int i = 0; i < 8; i++) {
|
||||
ControlState state = (bindings[i] != nullptr) ? dynamic_cast<InputDevice::Input*>(bindings[i])->GetState() : 0.0;
|
||||
if (state) {
|
||||
|
@ -441,8 +463,7 @@ bool InputDeviceManager::UpdateInputXpad(std::shared_ptr<InputDevice>& Device, v
|
|||
}
|
||||
else {
|
||||
if (bindings[24] != nullptr) {
|
||||
//XpadOutput* out_buf = reinterpret_cast<XpadOutput*>(static_cast<uint8_t*>(Buffer) + 2); lle usb
|
||||
XpadOutput* out_buf = reinterpret_cast<XpadOutput*>(Buffer);
|
||||
XpadOutput* out_buf = reinterpret_cast<XpadOutput*>(static_cast<uint8_t*>(Buffer) + XID_PACKET_HEADER);
|
||||
dynamic_cast<InputDevice::Output*>(bindings[24])->SetState(out_buf->left_actuator_strength / static_cast<ControlState>(0xFFFF),
|
||||
out_buf->right_actuator_strength / static_cast<ControlState>(0xFFFF));
|
||||
}
|
||||
|
@ -475,7 +496,7 @@ bool InputDeviceManager::UpdateInputSBC(std::shared_ptr<InputDevice>& Device, vo
|
|||
// 9 -> GearLever Up
|
||||
// 10 -> GearLever Down
|
||||
static uint16_t last_in_state[XBOX_NUM_PORTS] = { 0, 0, 0, 0 };
|
||||
SBCInput *in_buf = static_cast<SBCInput *>(Buffer);
|
||||
SBCInput *in_buf = reinterpret_cast<SBCInput *>(static_cast<uint8_t *>(Buffer) + XID_PACKET_HEADER);
|
||||
for (int i = 0; i < 4; i++) {
|
||||
ControlState state = (bindings[i] != nullptr) ? dynamic_cast<InputDevice::Input *>(bindings[i])->GetState() : 0.0;
|
||||
if (state) {
|
||||
|
@ -607,6 +628,11 @@ bool InputDeviceManager::UpdateInputSBC(std::shared_ptr<InputDevice>& Device, vo
|
|||
return true;
|
||||
}
|
||||
|
||||
bool InputDeviceManager::UpdateInputHw(std::shared_ptr<InputDevice> &Device, void *Buffer, int Direction)
|
||||
{
|
||||
return dynamic_cast<Libusb::LibusbDevice *>(Device.get())->ExecuteIo(Buffer, Direction);
|
||||
}
|
||||
|
||||
void InputDeviceManager::RefreshDevices()
|
||||
{
|
||||
std::unique_lock<std::mutex> lck(m_Mtx);
|
||||
|
@ -616,6 +642,7 @@ void InputDeviceManager::RefreshDevices()
|
|||
XInput::PopulateDevices();
|
||||
DInput::PopulateDevices();
|
||||
Sdl::PopulateDevices();
|
||||
Libusb::PopulateDevices();
|
||||
lck.lock();
|
||||
m_Cv.wait(lck, []() {
|
||||
return Sdl::PopulateOK;
|
||||
|
@ -628,13 +655,15 @@ void InputDeviceManager::RefreshDevices()
|
|||
}
|
||||
}
|
||||
|
||||
std::vector<std::string> InputDeviceManager::GetDeviceList() const
|
||||
std::vector<std::string> InputDeviceManager::GetDeviceList(std::function<bool(const InputDevice *)> Callback) const
|
||||
{
|
||||
std::vector<std::string> dev_list;
|
||||
std::lock_guard<std::mutex> lck(m_Mtx);
|
||||
|
||||
std::for_each(m_Devices.begin(), m_Devices.end(), [&dev_list](const auto& Device) {
|
||||
dev_list.push_back(Device->GetQualifiedName());
|
||||
std::for_each(m_Devices.begin(), m_Devices.end(), [&dev_list, &Callback](const auto& Device) {
|
||||
if (Callback(Device.get())) {
|
||||
dev_list.push_back(Device->GetQualifiedName());
|
||||
}
|
||||
});
|
||||
|
||||
return dev_list;
|
||||
|
@ -719,7 +748,7 @@ void InputDeviceManager::HotplugHandler(bool is_sdl)
|
|||
std::unique_lock<std::mutex> lck(m_Mtx);
|
||||
|
||||
auto it = std::remove_if(m_Devices.begin(), m_Devices.end(), [](const auto &Device) {
|
||||
if (StrStartsWith(Device->GetAPI(), "XInput")) {
|
||||
if (Device->IsLibusb() || StrStartsWith(Device->GetAPI(), "XInput")) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
@ -730,6 +759,9 @@ void InputDeviceManager::HotplugHandler(bool is_sdl)
|
|||
|
||||
lck.unlock();
|
||||
XInput::PopulateDevices();
|
||||
// When this was written, libusb did not yet support device hotplug on Windows, as documented in this issue https://github.com/libusb/libusb/issues/86.
|
||||
// So we add the below call here. This will only work if rawinput detects the libusb device.
|
||||
Libusb::PopulateDevices();
|
||||
}
|
||||
|
||||
for (int port = PORT_1; port <= PORT_4; ++port) {
|
||||
|
|
|
@ -52,6 +52,8 @@
|
|||
#define MU_OFFSET 4
|
||||
#define MAX_DEVS (XBOX_NUM_PORTS + XBOX_CTRL_NUM_SLOTS * XBOX_NUM_PORTS)
|
||||
|
||||
#define XID_PACKET_HEADER 2
|
||||
|
||||
extern int dev_num_buttons[to_underlying(XBOX_INPUT_DEVICE::DEVICE_MAX)];
|
||||
|
||||
inline XBOX_INPUT_DEVICE input_support_list[] = {
|
||||
|
@ -60,6 +62,8 @@ inline XBOX_INPUT_DEVICE input_support_list[] = {
|
|||
XBOX_INPUT_DEVICE::MS_CONTROLLER_S,
|
||||
XBOX_INPUT_DEVICE::STEEL_BATTALION_CONTROLLER,
|
||||
XBOX_INPUT_DEVICE::ARCADE_STICK,
|
||||
XBOX_INPUT_DEVICE::HW_XBOX_CONTROLLER,
|
||||
XBOX_INPUT_DEVICE::HW_STEEL_BATTALION_CONTROLLER,
|
||||
};
|
||||
|
||||
inline XBOX_INPUT_DEVICE slot_support_list[] = {
|
||||
|
@ -71,7 +75,19 @@ inline XBOX_INPUT_DEVICE slot_support_list[] = {
|
|||
|
||||
#pragma pack(1)
|
||||
|
||||
// xpad in/out buffers stripped of the first two bytes
|
||||
// Class-specific xid descriptor, used by libusb
|
||||
struct XidDesc {
|
||||
uint8_t bLength;
|
||||
uint8_t bDescriptorType;
|
||||
uint16_t bcdXid;
|
||||
uint8_t bType;
|
||||
uint8_t bSubType;
|
||||
uint8_t bMaxInputReportSize;
|
||||
uint8_t bMaxOutputReportSize;
|
||||
uint16_t wAlternateProductIds[4];
|
||||
};
|
||||
|
||||
// xpad in/out buffers stripped of the first two bytes as used by xinput
|
||||
struct XpadInput {
|
||||
uint16_t wButtons;
|
||||
uint8_t bAnalogButtons[8];
|
||||
|
@ -86,6 +102,20 @@ struct XpadOutput {
|
|||
uint16_t right_actuator_strength;
|
||||
};
|
||||
|
||||
// xpad in/out buffers as used by xid
|
||||
struct XidGamepadInput {
|
||||
uint8_t bReportId;
|
||||
uint8_t bLength;
|
||||
XpadInput InBuffer;
|
||||
};
|
||||
|
||||
struct XidGamepadOutput {
|
||||
uint8_t bReportId;
|
||||
uint8_t bLength;
|
||||
XpadOutput OutBuffer;
|
||||
};
|
||||
|
||||
// same as above, but for the SBC
|
||||
struct SBCInput {
|
||||
uint16_t wButtons[3];
|
||||
uint8_t bPad1;
|
||||
|
@ -112,11 +142,23 @@ struct SBCOutput {
|
|||
uint8_t LedState[20];
|
||||
};
|
||||
|
||||
struct XidSBCInput {
|
||||
uint8_t bReportId;
|
||||
uint8_t bLength;
|
||||
SBCInput InBuffer;
|
||||
};
|
||||
|
||||
struct XidSBCOutput {
|
||||
uint8_t bReportId;
|
||||
uint8_t bLength;
|
||||
SBCOutput OutBuffer;
|
||||
};
|
||||
|
||||
#pragma pack()
|
||||
|
||||
union InputBuff {
|
||||
XpadInput ctrl;
|
||||
SBCInput sbc;
|
||||
XidGamepadInput ctrl;
|
||||
XidSBCInput sbc;
|
||||
};
|
||||
|
||||
struct DeviceInfo {
|
||||
|
@ -159,7 +201,7 @@ public:
|
|||
// update device list
|
||||
void RefreshDevices();
|
||||
// get the name of the devices currently detected
|
||||
std::vector<std::string> GetDeviceList() const;
|
||||
std::vector<std::string> GetDeviceList(std::function<bool(const InputDevice *)> Callback) const;
|
||||
// find device from its gui name
|
||||
std::shared_ptr<InputDevice> FindDevice(const std::string& QualifiedName) const;
|
||||
// find device from its sdl id
|
||||
|
@ -179,6 +221,8 @@ private:
|
|||
bool UpdateInputXpad(std::shared_ptr<InputDevice>& Device, void* Buffer, int Direction, const std::string &Port);
|
||||
// update input for a Steel Battalion controller
|
||||
bool UpdateInputSBC(std::shared_ptr<InputDevice>& Device, void* Buffer, int Direction, int Port_num, const std::string &Port);
|
||||
// update input for a passthrough xbox device
|
||||
bool UpdateInputHw(std::shared_ptr<InputDevice> &Device, void *Buffer, int Direction);
|
||||
// bind a host device to an emulated device
|
||||
void BindHostDevice(int type, std::string_view port);
|
||||
// connect a device to the emulated machine
|
||||
|
|
|
@ -78,16 +78,22 @@ void InputWindow::UpdateDeviceList()
|
|||
g_InputDeviceManager.RefreshDevices();
|
||||
|
||||
// Populate device list
|
||||
LRESULT num_devices = SendMessage(m_hwnd_device_list, CB_GETCOUNT, 0, 0);
|
||||
for (int i = 0; i < num_devices; i++) {
|
||||
for (int i = 0; i < m_num_devices; i++) {
|
||||
SendMessage(m_hwnd_device_list, CB_DELETESTRING, 0, 0);
|
||||
}
|
||||
|
||||
std::vector<std::string> dev_list = g_InputDeviceManager.GetDeviceList();
|
||||
// Add everything but libusb devices
|
||||
std::vector<std::string> dev_list = g_InputDeviceManager.GetDeviceList([](const auto &Device) {
|
||||
if (Device->IsLibusb()) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
for (const auto& str : dev_list) {
|
||||
SendMessage(m_hwnd_device_list, CB_ADDSTRING, 0, reinterpret_cast<LPARAM>(str.c_str()));
|
||||
++m_num_devices;
|
||||
}
|
||||
if (!dev_list.empty()) {
|
||||
if (m_num_devices) {
|
||||
SendMessage(m_hwnd_device_list, CB_SETCURSEL, 0, 0);
|
||||
}
|
||||
|
||||
|
@ -310,9 +316,15 @@ void InputWindow::LoadDefaultProfile()
|
|||
|
||||
void InputWindow::UpdateCurrentDevice()
|
||||
{
|
||||
char device_name[50];
|
||||
SendMessage(m_hwnd_device_list, WM_GETTEXT, sizeof(device_name), reinterpret_cast<LPARAM>(device_name));
|
||||
m_host_dev = device_name;
|
||||
if (m_num_devices) {
|
||||
char device_name[50];
|
||||
SendMessage(m_hwnd_device_list, WM_GETTEXT, sizeof(device_name), reinterpret_cast<LPARAM>(device_name));
|
||||
m_host_dev = device_name;
|
||||
}
|
||||
else {
|
||||
m_host_dev = "";
|
||||
}
|
||||
|
||||
EnableDefaultButton();
|
||||
}
|
||||
|
||||
|
|
|
@ -55,7 +55,7 @@ class InputWindow
|
|||
public:
|
||||
virtual void Initialize(HWND hwnd, int port_num, int dev_type) = 0;
|
||||
~InputWindow();
|
||||
void UpdateDeviceList();
|
||||
virtual void UpdateDeviceList();
|
||||
void BindButton(int ControlID);
|
||||
virtual void ClearBindings() = 0;
|
||||
virtual void UpdateProfile(const std::string& name, int command);
|
||||
|
@ -84,6 +84,8 @@ protected:
|
|||
HWND m_hwnd_device_list;
|
||||
// handle of the profile list combobox
|
||||
HWND m_hwnd_profile_list;
|
||||
// number of devices displayed in the device list combobox
|
||||
int m_num_devices;
|
||||
// type of the device
|
||||
int m_dev_type;
|
||||
// num of buttons of device under configuration
|
||||
|
@ -137,3 +139,21 @@ public:
|
|||
private:
|
||||
int EnableDefaultButton() override;
|
||||
};
|
||||
|
||||
class LibusbInputWindow : public InputWindow
|
||||
{
|
||||
public:
|
||||
~LibusbInputWindow();
|
||||
void Initialize(HWND hwnd, int port_num, int dev_type) override;
|
||||
void ClearBindings() override;
|
||||
void SaveSlotConfig() override;
|
||||
void UpdateDeviceList() override;
|
||||
void TestInput();
|
||||
|
||||
|
||||
private:
|
||||
int EnableDefaultButton() override;
|
||||
|
||||
// handle of the test button
|
||||
HWND m_hwnd_device_test;
|
||||
};
|
||||
|
|
|
@ -0,0 +1,316 @@
|
|||
// This is an open source non-commercial project. Dear PVS-Studio, please check it.
|
||||
// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
|
||||
// ******************************************************************
|
||||
// *
|
||||
// * This file is part of the Cxbx project.
|
||||
// *
|
||||
// * Cxbx and Cxbe are free software; you can redistribute them
|
||||
// * and/or modify them under the terms of the GNU General Public
|
||||
// * License as published by the Free Software Foundation; either
|
||||
// * version 2 of the license, or (at your option) any later version.
|
||||
// *
|
||||
// * 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 for more details.
|
||||
// *
|
||||
// * You should have recieved a copy of the GNU General Public License
|
||||
// * along with this program; see the file COPYING.
|
||||
// * If not, write to the Free Software Foundation, Inc.,
|
||||
// * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA.
|
||||
// *
|
||||
// * (c) 2021 ergo720
|
||||
// *
|
||||
// * All rights reserved
|
||||
// *
|
||||
// ******************************************************************
|
||||
|
||||
#define LOG_PREFIX CXBXR_MODULE::LIBUSB
|
||||
|
||||
#include "LibusbDevice.h"
|
||||
#include "InputManager.h"
|
||||
#include "core\kernel\support\Emu.h"
|
||||
#include "core\hle\XAPI\Xapi.h"
|
||||
|
||||
// Sanity check: ensure that our libusb version is high enough for libusb_get_device_descriptor to succeed and to pass nullptr to libusb_interrupt_transfer
|
||||
static_assert(LIBUSB_API_VERSION >= 0x01000105);
|
||||
|
||||
|
||||
namespace Libusb
|
||||
{
|
||||
int InitStatus = NOT_INIT;
|
||||
|
||||
// These come from here https://github.com/xboxdrv/xboxdrv/blob/ac6ebb1228962220482ea03743cadbe18754246c/src/xpad_device.cpp#L29
|
||||
static constexpr uint16_t SupportedDevices_VidPid[][2] = { // vid, pid
|
||||
0x0d2f, 0x0002,
|
||||
0x045e, 0x0202,
|
||||
0x045e, 0x0285,
|
||||
0x045e, 0x0287,
|
||||
0x045e, 0x0289,
|
||||
0x046d, 0xca84,
|
||||
0x046d, 0xca88,
|
||||
0x05fd, 0x1007,
|
||||
0x05fd, 0x107a,
|
||||
0x0738, 0x4516,
|
||||
0x0738, 0x4522,
|
||||
0x0738, 0x4526,
|
||||
0x0738, 0x4536,
|
||||
0x0738, 0x4556,
|
||||
0x0c12, 0x8802,
|
||||
0x0c12, 0x8810,
|
||||
0x0c12, 0x9902,
|
||||
0x0e4c, 0x1097,
|
||||
0x0e4c, 0x2390,
|
||||
0x0e6f, 0x0003,
|
||||
0x0e6f, 0x0005,
|
||||
0x0e6f, 0x0006,
|
||||
0x0f30, 0x0202,
|
||||
0x0f30, 0x8888,
|
||||
0x102c, 0xff0c,
|
||||
0x044f, 0x0f07,
|
||||
0x0e8f, 0x3008,
|
||||
};
|
||||
|
||||
static constexpr const char *SupportedDevices_Name[] = {
|
||||
"Andamiro Pump It Up pad",
|
||||
"Microsoft X-Box pad v1 (US)",
|
||||
"Microsoft X-Box pad (Japan)",
|
||||
"Microsoft Xbox Controller S",
|
||||
"Microsoft X-Box pad v2 (US)",
|
||||
"Logitech Xbox Cordless Controller",
|
||||
"Logitech Compact Controller for Xbox",
|
||||
"Mad Catz Controller (unverified)",
|
||||
"InterAct 'PowerPad Pro' X-Box pad (Germany)",
|
||||
"Mad Catz Control Pad",
|
||||
"Mad Catz LumiCON",
|
||||
"Mad Catz Control Pad Pro",
|
||||
"Mad Catz MicroCON",
|
||||
"Mad Catz Lynx Wireless Controller",
|
||||
"Zeroplus Xbox Controller",
|
||||
"Zeroplus Xbox Controller",
|
||||
"HAMA VibraX - *FAULTY HARDWARE*",
|
||||
"Radica Gamester Controller",
|
||||
"Radica Games Jtech Controller",
|
||||
"Logic3 Freebird wireless Controller",
|
||||
"Eclipse wireless Controller",
|
||||
"Edge wireless Controller",
|
||||
"Joytech Advanced Controller",
|
||||
"BigBen XBMiniPad Controller",
|
||||
"Joytech Wireless Advanced Controller",
|
||||
"Thrustmaster, Inc. Controller",
|
||||
"Generic xbox control (dealextreme)",
|
||||
};
|
||||
|
||||
static_assert(ARRAY_SIZE(SupportedDevices_VidPid) == ARRAY_SIZE(SupportedDevices_Name));
|
||||
|
||||
void Init(std::mutex &Mtx)
|
||||
{
|
||||
std::unique_lock<std::mutex> lck(Mtx);
|
||||
|
||||
// We only use a single libusb session per cxbxr process, so we do not need to use a libusb context
|
||||
if (libusb_init(nullptr) != 0) {
|
||||
EmuLog(LOG_LEVEL::ERROR2, "Failed to initialize Libusb!");
|
||||
InitStatus = INIT_ERROR;
|
||||
return;
|
||||
}
|
||||
|
||||
InitStatus = INIT_SUCCESS;
|
||||
}
|
||||
|
||||
void DeInit()
|
||||
{
|
||||
InitStatus = NOT_INIT;
|
||||
libusb_exit(nullptr);
|
||||
}
|
||||
|
||||
void PopulateDevices()
|
||||
{
|
||||
// NOTE: the libusb docs say that the list is always appended with a NULL element at the end
|
||||
libusb_device **List;
|
||||
ssize_t DevicesConnected = libusb_get_device_list(nullptr, &List) - 1;
|
||||
if (DevicesConnected < 0) {
|
||||
EmuLog(LOG_LEVEL::ERROR2, "Failed to enumerate devices. The error was: %s", libusb_strerror(DevicesConnected));
|
||||
return;
|
||||
}
|
||||
|
||||
for (ssize_t i = 0; i < DevicesConnected; ++i) {
|
||||
libusb_device *LibusbDev = List[i];
|
||||
libusb_device_descriptor Desc;
|
||||
libusb_get_device_descriptor(LibusbDev, &Desc); // always succeeds when LIBUSB_API_VERSION >= 0x01000102
|
||||
auto Device = std::make_shared<LibusbDevice>(&Desc, LibusbDev);
|
||||
if (Device->IsLibusb()) {
|
||||
g_InputDeviceManager.AddDevice(std::move(Device));
|
||||
}
|
||||
}
|
||||
|
||||
libusb_free_device_list(List, 1);
|
||||
}
|
||||
|
||||
void GetDeviceChanges()
|
||||
{
|
||||
g_InputDeviceManager.RemoveDevice([](const auto &Device) {
|
||||
return Device->IsLibusb();
|
||||
});
|
||||
PopulateDevices();
|
||||
}
|
||||
|
||||
LibusbDevice::LibusbDevice(libusb_device_descriptor *Desc, libusb_device *Dev)
|
||||
{
|
||||
m_Type = XBOX_INPUT_DEVICE::DEVICE_INVALID;
|
||||
|
||||
// The SBC's VID and PID are taken from https://xboxdevwiki.net/Xbox_Input_Devices#Steel_Battalion_Controller
|
||||
if ((Desc->idVendor == 0x0a7b) && (Desc->idProduct == 0xd000)) {
|
||||
m_Type = XBOX_INPUT_DEVICE::HW_STEEL_BATTALION_CONTROLLER;
|
||||
m_UcType = XINPUT_DEVTYPE_STEELBATTALION;
|
||||
m_UcSubType = XINPUT_DEVSUBTYPE_GC_GAMEPAD;
|
||||
m_Name = "Steel battalion controller";
|
||||
m_BufferInSize = sizeof(XidSBCInput);
|
||||
m_BufferOutSize = sizeof(XidSBCOutput);
|
||||
assert(Desc->bcdUSB == 0x110); // must be a usb 1.1 device
|
||||
}
|
||||
else {
|
||||
for (size_t i = 0; i < ARRAY_SIZE(SupportedDevices_VidPid); ++i) {
|
||||
if ((Desc->idVendor = SupportedDevices_VidPid[i][0]) && (Desc->idProduct == SupportedDevices_VidPid[i][1])) {
|
||||
m_Type = XBOX_INPUT_DEVICE::HW_XBOX_CONTROLLER;
|
||||
m_UcType = XINPUT_DEVTYPE_GAMEPAD;
|
||||
m_UcSubType = XINPUT_DEVSUBTYPE_GC_GAMEPAD;
|
||||
m_Name = SupportedDevices_Name[i];
|
||||
m_BufferInSize = sizeof(XidGamepadInput);
|
||||
m_BufferOutSize = sizeof(XidGamepadOutput);
|
||||
assert(Desc->bcdUSB == 0x110); // must be a usb 1.1 device
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (m_Type == XBOX_INPUT_DEVICE::DEVICE_INVALID) { return; }
|
||||
|
||||
// Duke, S and SBC have 1 configuration, 1 interface and 2 endpoints (input and output) and use the default alternate setting zero.
|
||||
// The code below assumes that third-party controllers follow suit.
|
||||
if (libusb_open(Dev, &m_hDev) == 0) {
|
||||
libusb_config_descriptor *ConfDesc;
|
||||
if (libusb_get_active_config_descriptor(Dev, &ConfDesc) == 0) {
|
||||
if (ConfDesc->bNumInterfaces == 1) {
|
||||
auto Iface = ConfDesc->interface[0];
|
||||
if (Iface.num_altsetting == 1) {
|
||||
auto Setting = Iface.altsetting[0];
|
||||
m_IfaceNum = Setting.bInterfaceNumber;
|
||||
if (Setting.bNumEndpoints >= 1) {
|
||||
m_HasEndpointOut = false;
|
||||
for (uint8_t i = 0; i < Setting.bNumEndpoints; ++i) {
|
||||
auto Endpoint = Setting.endpoint[i];
|
||||
if (Endpoint.bEndpointAddress & 0x80) {
|
||||
m_EndpointIn = Endpoint.bEndpointAddress;
|
||||
m_IntervalIn = Endpoint.bInterval;
|
||||
}
|
||||
else {
|
||||
// third-party controllers that don't support rumble probably won't have an out endpoint
|
||||
m_EndpointOut = Endpoint.bEndpointAddress;
|
||||
m_IntervalOut = Endpoint.bInterval;
|
||||
m_HasEndpointOut = true;
|
||||
}
|
||||
}
|
||||
EmuLog(LOG_LEVEL::INFO, "Out endpoint %s", m_HasEndpointOut ? "present" : "not present");
|
||||
if (int err = libusb_claim_interface(m_hDev, m_IfaceNum) != 0) {
|
||||
EmuLog(LOG_LEVEL::INFO, "Rejected device %s because libusb could not claim its interface. The error was: %s",
|
||||
m_Name.c_str(), libusb_strerror(err));
|
||||
m_Type = XBOX_INPUT_DEVICE::DEVICE_INVALID;
|
||||
}
|
||||
else {
|
||||
// Grab the xid descriptor so that we can report real type/subtype values back to the title when it calls XInputGetCapabilities
|
||||
XidDesc XidDesc;
|
||||
if (libusb_control_transfer(m_hDev, 0xC1, 6, 0x4200, m_IfaceNum, reinterpret_cast<uint8_t *>(&XidDesc), sizeof(XidDesc), m_IntervalIn)
|
||||
== sizeof(XidDesc)) { // submit a GET_DESCRIPTOR request
|
||||
|
||||
// Dump the xid descriptor to the log
|
||||
EmuLog(LOG_LEVEL::INFO, "Xid descriptor dump:\nbLength: %#010X\nbDescriptorType: %#010X\nbcdXid: %#010X\nbType: %#010X\n"
|
||||
"bSubType: %#010X\nbMaxInputReportSize: %#010X\nbMaxOutputReportSize: %#010X\nwAlternateProductIds[0]: %#010X\n"
|
||||
"wAlternateProductIds[1]: %#010X\nwAlternateProductIds[2]: %#010X\nwAlternateProductIds[3]: %#010X\n",
|
||||
XidDesc.bLength, XidDesc.bDescriptorType, XidDesc.bcdXid, XidDesc.bType, XidDesc.bSubType, XidDesc.bMaxInputReportSize, XidDesc.bMaxOutputReportSize,
|
||||
XidDesc.wAlternateProductIds[0], XidDesc.wAlternateProductIds[1], XidDesc.wAlternateProductIds[2], XidDesc.wAlternateProductIds[3]);
|
||||
|
||||
if (XidDesc.bDescriptorType == 0x42) {
|
||||
m_UcType = XidDesc.bType;
|
||||
m_UcSubType = XidDesc.bSubType;
|
||||
}
|
||||
else {
|
||||
EmuLog(LOG_LEVEL::INFO, "The xid descriptor for device %s reported an unexpected descriptor type, assuming a default subtype", m_Name);
|
||||
}
|
||||
}
|
||||
else {
|
||||
EmuLog(LOG_LEVEL::INFO, "Could not retrieve the xid descriptor for device %s, assuming a default subtype", m_Name);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
EmuLog(LOG_LEVEL::INFO, "Rejected device %s because of unexpected number of endpoints, bNumEndpoints: %d",
|
||||
m_Name.c_str(), Setting.bNumEndpoints);
|
||||
m_Type = XBOX_INPUT_DEVICE::DEVICE_INVALID;
|
||||
}
|
||||
}
|
||||
else {
|
||||
EmuLog(LOG_LEVEL::INFO, "Rejected device %s because of unexpected number of alternative settings, num_altsetting: %d",
|
||||
m_Name.c_str(), Iface.num_altsetting);
|
||||
m_Type = XBOX_INPUT_DEVICE::DEVICE_INVALID;
|
||||
}
|
||||
}
|
||||
else {
|
||||
EmuLog(LOG_LEVEL::INFO, "Rejected device %s because of unexpected number of interfaces, bNumInterfaces: %d",
|
||||
m_Name.c_str(), ConfDesc->bNumInterfaces);
|
||||
m_Type = XBOX_INPUT_DEVICE::DEVICE_INVALID;
|
||||
}
|
||||
libusb_free_config_descriptor(ConfDesc);
|
||||
}
|
||||
}
|
||||
|
||||
if (m_Type == XBOX_INPUT_DEVICE::DEVICE_INVALID) { libusb_close(m_hDev); }
|
||||
}
|
||||
|
||||
LibusbDevice::~LibusbDevice()
|
||||
{
|
||||
if (m_Type != XBOX_INPUT_DEVICE::DEVICE_INVALID) {
|
||||
libusb_release_interface(m_hDev, m_IfaceNum);
|
||||
libusb_close(m_hDev);
|
||||
}
|
||||
}
|
||||
|
||||
bool LibusbDevice::UpdateInput()
|
||||
{
|
||||
// Dummy, it should never be called. It's only here to override the pure function UpdateInput in InputDevice
|
||||
assert(0);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool LibusbDevice::ExecuteIo(void *Buffer, int Direction)
|
||||
{
|
||||
// NOTE: a SET_REPORT control transfer to the SBC doesn't seem to work, the parameters might not be appropriate for it... So, we use
|
||||
// the interrupt pipes for everything instead
|
||||
*static_cast<uint8_t *>(Buffer) = 0; // write bReportId
|
||||
if (Direction == DIRECTION_IN) {
|
||||
*(static_cast<uint8_t *>(Buffer) + 1) = m_BufferInSize; // write bLength
|
||||
if (libusb_interrupt_transfer(m_hDev, m_EndpointIn, static_cast<uint8_t *>(Buffer), m_BufferInSize, nullptr, m_IntervalIn) != 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (m_HasEndpointOut) {
|
||||
*(static_cast<uint8_t *>(Buffer) + 1) = m_BufferOutSize; // write bLength
|
||||
if (libusb_interrupt_transfer(m_hDev, m_EndpointOut, static_cast<uint8_t *>(Buffer), m_BufferOutSize, nullptr, m_IntervalOut) != 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string LibusbDevice::GetDeviceName() const
|
||||
{
|
||||
return m_Name;
|
||||
}
|
||||
|
||||
std::string LibusbDevice::GetAPI() const
|
||||
{
|
||||
return "Libusb";
|
||||
}
|
||||
}
|
|
@ -0,0 +1,91 @@
|
|||
// This is an open source non-commercial project. Dear PVS-Studio, please check it.
|
||||
// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
|
||||
// ******************************************************************
|
||||
// *
|
||||
// * This file is part of the Cxbx project.
|
||||
// *
|
||||
// * Cxbx and Cxbe are free software; you can redistribute them
|
||||
// * and/or modify them under the terms of the GNU General Public
|
||||
// * License as published by the Free Software Foundation; either
|
||||
// * version 2 of the license, or (at your option) any later version.
|
||||
// *
|
||||
// * 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 for more details.
|
||||
// *
|
||||
// * You should have recieved a copy of the GNU General Public License
|
||||
// * along with this program; see the file COPYING.
|
||||
// * If not, write to the Free Software Foundation, Inc.,
|
||||
// * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA.
|
||||
// *
|
||||
// * (c) 2021 ergo720
|
||||
// *
|
||||
// * All rights reserved
|
||||
// *
|
||||
// ******************************************************************
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "InputDevice.h"
|
||||
// Suppress warning in libusb about zero sized array
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable: 4200)
|
||||
#include "libusb.h"
|
||||
#pragma warning(pop)
|
||||
|
||||
|
||||
namespace Libusb
|
||||
{
|
||||
typedef enum _INIT_STATUS : int
|
||||
{
|
||||
NOT_INIT = -2,
|
||||
INIT_ERROR,
|
||||
INIT_SUCCESS,
|
||||
}
|
||||
INIT_STATUS;
|
||||
|
||||
extern int InitStatus;
|
||||
|
||||
// initialize Libusb
|
||||
void Init(std::mutex &Mtx);
|
||||
// shutdown Libusb
|
||||
void DeInit();
|
||||
// refresh the device list in response to a refresh command from the input GUI
|
||||
void PopulateDevices();
|
||||
// update the device list
|
||||
void GetDeviceChanges();
|
||||
|
||||
class LibusbDevice : public InputDevice
|
||||
{
|
||||
public:
|
||||
~LibusbDevice();
|
||||
bool UpdateInput() override;
|
||||
bool ExecuteIo(void *Buffer, int Direction);
|
||||
|
||||
LibusbDevice(libusb_device_descriptor *Desc, libusb_device *Dev);
|
||||
|
||||
std::string GetDeviceName() const override;
|
||||
std::string GetAPI() const override;
|
||||
bool IsLibusb() const override { return m_Type != XBOX_INPUT_DEVICE::DEVICE_INVALID; }
|
||||
XBOX_INPUT_DEVICE GetLibusbType() const { return m_Type; }
|
||||
uint8_t GetUcType() { return m_UcType; }
|
||||
uint8_t GetUcSubType() { return m_UcSubType; }
|
||||
|
||||
|
||||
private:
|
||||
XBOX_INPUT_DEVICE m_Type;
|
||||
uint8_t m_UcType;
|
||||
uint8_t m_UcSubType;
|
||||
std::string m_Name;
|
||||
libusb_device_handle *m_hDev;
|
||||
unsigned char m_EndpointIn;
|
||||
unsigned char m_EndpointOut;
|
||||
uint8_t m_IntervalIn;
|
||||
uint8_t m_IntervalOut;
|
||||
uint8_t m_BufferInSize;
|
||||
uint8_t m_BufferOutSize;
|
||||
uint8_t m_IfaceNum;
|
||||
bool m_HasEndpointOut;
|
||||
};
|
||||
}
|
|
@ -41,6 +41,7 @@
|
|||
#include "SdlJoystick.h"
|
||||
#include "XInputPad.h"
|
||||
#include "DInputKeyboardMouse.h"
|
||||
#include "LibusbDevice.h"
|
||||
#include "InputManager.h"
|
||||
|
||||
// These values are those used by Dolphin!
|
||||
|
@ -156,6 +157,7 @@ namespace Sdl
|
|||
else {
|
||||
XInput::GetDeviceChanges();
|
||||
DInput::GetDeviceChanges();
|
||||
Libusb::GetDeviceChanges();
|
||||
std::string port = std::to_string(*static_cast<int *>(Event.user.data1));
|
||||
int port_num, slot;
|
||||
PortStr2Int(port, &port_num, &slot);
|
||||
|
|
|
@ -33,6 +33,7 @@
|
|||
#include "common\input\SdlJoystick.h"
|
||||
#include "common\input\InputManager.h"
|
||||
#include <Shlwapi.h>
|
||||
#include "common\input\LibusbDevice.h" // include this after Shlwapi.h or else it causes an error
|
||||
#include "core\kernel\init\CxbxKrnl.h"
|
||||
#include "Logging.h"
|
||||
#include "core\kernel\support\Emu.h"
|
||||
|
@ -113,14 +114,16 @@ bool operator==(xbox::PXPP_DEVICE_TYPE XppType, XBOX_INPUT_DEVICE XidType)
|
|||
{
|
||||
case XBOX_INPUT_DEVICE::MS_CONTROLLER_DUKE:
|
||||
case XBOX_INPUT_DEVICE::MS_CONTROLLER_S:
|
||||
case XBOX_INPUT_DEVICE::ARCADE_STICK: {
|
||||
case XBOX_INPUT_DEVICE::ARCADE_STICK:
|
||||
case XBOX_INPUT_DEVICE::HW_XBOX_CONTROLLER: {
|
||||
if (XppType == g_DeviceType_Gamepad) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case XBOX_INPUT_DEVICE::STEEL_BATTALION_CONTROLLER: {
|
||||
case XBOX_INPUT_DEVICE::STEEL_BATTALION_CONTROLLER:
|
||||
case XBOX_INPUT_DEVICE::HW_STEEL_BATTALION_CONTROLLER: {
|
||||
if (XppType == g_DeviceType_SBC) {
|
||||
return true;
|
||||
}
|
||||
|
@ -152,10 +155,12 @@ void UpdateXppState(DeviceState *dev, XBOX_INPUT_DEVICE type, std::string_view p
|
|||
case XBOX_INPUT_DEVICE::MS_CONTROLLER_DUKE:
|
||||
case XBOX_INPUT_DEVICE::MS_CONTROLLER_S:
|
||||
case XBOX_INPUT_DEVICE::ARCADE_STICK:
|
||||
case XBOX_INPUT_DEVICE::HW_XBOX_CONTROLLER:
|
||||
xpp = g_DeviceType_Gamepad;
|
||||
break;
|
||||
|
||||
case XBOX_INPUT_DEVICE::STEEL_BATTALION_CONTROLLER:
|
||||
case XBOX_INPUT_DEVICE::HW_STEEL_BATTALION_CONTROLLER:
|
||||
xpp = g_DeviceType_SBC;
|
||||
break;
|
||||
|
||||
|
@ -208,6 +213,7 @@ void ConstructHleInputDevice(DeviceState *dev, DeviceState *upstream, int type,
|
|||
g_bIsDevicesEmulating = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// Set up common device state
|
||||
int port_num, slot;
|
||||
PortStr2Int(port, &port_num, &slot);
|
||||
|
@ -223,12 +229,22 @@ void ConstructHleInputDevice(DeviceState *dev, DeviceState *upstream, int type,
|
|||
switch (type)
|
||||
{
|
||||
case to_underlying(XBOX_INPUT_DEVICE::MS_CONTROLLER_DUKE):
|
||||
case to_underlying(XBOX_INPUT_DEVICE::HW_XBOX_CONTROLLER):
|
||||
dev->type = XBOX_INPUT_DEVICE::MS_CONTROLLER_DUKE;
|
||||
dev->info.bAutoPollDefault = true;
|
||||
dev->info.ucType = XINPUT_DEVTYPE_GAMEPAD;
|
||||
dev->info.ucSubType = XINPUT_DEVSUBTYPE_GC_GAMEPAD;
|
||||
dev->info.ucInputStateSize = sizeof(XpadInput);
|
||||
dev->info.ucFeedbackSize = sizeof(XpadOutput);
|
||||
if (type == to_underlying(XBOX_INPUT_DEVICE::HW_XBOX_CONTROLLER)) {
|
||||
dev->type = XBOX_INPUT_DEVICE::HW_XBOX_CONTROLLER;
|
||||
char dev_name[50];
|
||||
g_EmuShared->GetInputDevNameSettings(dev_name, port_num);
|
||||
if (auto Device = g_InputDeviceManager.FindDevice(std::string(dev_name))) {
|
||||
dev->info.ucType = dynamic_cast<Libusb::LibusbDevice *>(Device.get())->GetUcType();
|
||||
dev->info.ucSubType = dynamic_cast<Libusb::LibusbDevice *>(Device.get())->GetUcSubType();
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case to_underlying(XBOX_INPUT_DEVICE::MS_CONTROLLER_S):
|
||||
|
@ -241,15 +257,25 @@ void ConstructHleInputDevice(DeviceState *dev, DeviceState *upstream, int type,
|
|||
break;
|
||||
|
||||
case to_underlying(XBOX_INPUT_DEVICE::STEEL_BATTALION_CONTROLLER):
|
||||
case to_underlying(XBOX_INPUT_DEVICE::HW_STEEL_BATTALION_CONTROLLER):
|
||||
dev->type = XBOX_INPUT_DEVICE::STEEL_BATTALION_CONTROLLER;
|
||||
dev->info.buff.sbc.ucGearLever = 8;
|
||||
dev->info.buff.sbc.sAimingX = static_cast<uint8_t>(0x7F);
|
||||
dev->info.buff.sbc.sAimingY = static_cast<uint8_t>(0x7F);
|
||||
dev->info.buff.sbc.InBuffer.ucGearLever = 8;
|
||||
dev->info.buff.sbc.InBuffer.sAimingX = static_cast<uint8_t>(0x7F);
|
||||
dev->info.buff.sbc.InBuffer.sAimingY = static_cast<uint8_t>(0x7F);
|
||||
dev->info.bAutoPollDefault = true;
|
||||
dev->info.ucType = XINPUT_DEVTYPE_STEELBATTALION;
|
||||
dev->info.ucSubType = XINPUT_DEVSUBTYPE_GC_GAMEPAD_ALT;
|
||||
dev->info.ucSubType = XINPUT_DEVSUBTYPE_GC_GAMEPAD;
|
||||
dev->info.ucInputStateSize = sizeof(SBCInput);
|
||||
dev->info.ucFeedbackSize = sizeof(SBCOutput);
|
||||
if (type == to_underlying(XBOX_INPUT_DEVICE::HW_STEEL_BATTALION_CONTROLLER)) {
|
||||
dev->type = XBOX_INPUT_DEVICE::HW_STEEL_BATTALION_CONTROLLER;
|
||||
char dev_name[50];
|
||||
g_EmuShared->GetInputDevNameSettings(dev_name, port_num);
|
||||
if (auto Device = g_InputDeviceManager.FindDevice(std::string(dev_name))) {
|
||||
dev->info.ucType = dynamic_cast<Libusb::LibusbDevice *>(Device.get())->GetUcType();
|
||||
dev->info.ucSubType = dynamic_cast<Libusb::LibusbDevice *>(Device.get())->GetUcSubType();
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case to_underlying(XBOX_INPUT_DEVICE::ARCADE_STICK):
|
||||
|
@ -305,7 +331,7 @@ void DestructHleInputDevice(DeviceState *dev)
|
|||
dev->info.ucInputStateSize = 0;
|
||||
dev->info.ucFeedbackSize = 0;
|
||||
dev->info.dwPacketNumber = 0;
|
||||
std::memset(&dev->info.buff.ctrl, 0, sizeof(XpadInput));
|
||||
std::memset(&dev->info.buff.ctrl.InBuffer, 0, sizeof(XpadInput));
|
||||
break;
|
||||
|
||||
case XBOX_INPUT_DEVICE::STEEL_BATTALION_CONTROLLER:
|
||||
|
@ -315,7 +341,7 @@ void DestructHleInputDevice(DeviceState *dev)
|
|||
dev->info.ucInputStateSize = 0;
|
||||
dev->info.ucFeedbackSize = 0;
|
||||
dev->info.dwPacketNumber = 0;
|
||||
std::memset(&dev->info.buff.sbc, 0, sizeof(SBCInput));
|
||||
std::memset(&dev->info.buff.sbc.InBuffer, 0, sizeof(SBCInput));
|
||||
break;
|
||||
|
||||
case XBOX_INPUT_DEVICE::ARCADE_STICK:
|
||||
|
@ -325,7 +351,7 @@ void DestructHleInputDevice(DeviceState *dev)
|
|||
dev->info.ucInputStateSize = 0;
|
||||
dev->info.ucFeedbackSize = 0;
|
||||
dev->info.dwPacketNumber = 0;
|
||||
std::memset(&dev->info.buff.ctrl, 0, sizeof(XpadInput));
|
||||
std::memset(&dev->info.buff.ctrl.InBuffer, 0, sizeof(XpadInput));
|
||||
break;
|
||||
|
||||
case XBOX_INPUT_DEVICE::MEMORY_UNIT: {
|
||||
|
@ -454,7 +480,7 @@ xbox::dword_xt CxbxImpl_XInputHandler(xbox::HANDLE hDevice, xbox::PXINPUT_STATE
|
|||
}
|
||||
|
||||
if constexpr (!IsXInputPoll) {
|
||||
std::memcpy((void *)&pState->Gamepad, &g_devs[port].info.buff, g_devs[port].info.ucInputStateSize);
|
||||
std::memcpy((void *)&pState->Gamepad, reinterpret_cast<uint8_t *>(&g_devs[port].info.buff) + 2, g_devs[port].info.ucInputStateSize);
|
||||
pState->dwPacketNumber = g_devs[port].info.dwPacketNumber;
|
||||
}
|
||||
|
||||
|
@ -747,7 +773,7 @@ xbox::dword_xt WINAPI xbox::EMUPATCH(XInputSetState)
|
|||
int port = dev->port_idx;
|
||||
if (g_devs[port].info.hHandle == hDevice && !g_devs[port].bPendingRemoval) {
|
||||
pFeedback->Header.dwStatus = ERROR_IO_PENDING;
|
||||
g_InputDeviceManager.UpdateXboxPortInput(port, (void*)&pFeedback->Rumble, DIRECTION_OUT, to_underlying(g_devs[port].type));
|
||||
g_InputDeviceManager.UpdateXboxPortInput(port, (void*)&pFeedback->Header.bReportId, DIRECTION_OUT, to_underlying(g_devs[port].type));
|
||||
pFeedback->Header.dwStatus = ERROR_SUCCESS;
|
||||
if (pFeedback->Header.hEvent != NULL &&
|
||||
ObReferenceObjectByHandle(pFeedback->Header.hEvent, &xbox::ExEventObjectType, (PVOID*)&pFeedback->Header.IoCompletedEvent) == ERROR_SUCCESS) {
|
||||
|
|
|
@ -214,7 +214,6 @@ XINPUT_CAPABILITIES, *PXINPUT_CAPABILITIES;
|
|||
// ******************************************************************
|
||||
//general GAMEPAD uses subtype 0x01.
|
||||
#define XINPUT_DEVSUBTYPE_GC_GAMEPAD 0x01
|
||||
//SteelBatallion controller uses subtype 0x02
|
||||
#define XINPUT_DEVSUBTYPE_GC_GAMEPAD_ALT 0x02
|
||||
#define XINPUT_DEVSUBTYPE_GC_WHEEL 0x10
|
||||
#define XINPUT_DEVSUBTYPE_GC_ARCADE_STICK 0x20
|
||||
|
@ -243,16 +242,18 @@ XINPUT_STATE, *PXINPUT_STATE;
|
|||
// ******************************************************************
|
||||
// * XINPUT_FEEDBACK_HEADER
|
||||
// ******************************************************************
|
||||
#include "AlignPrefix1.h"
|
||||
#pragma pack(1)
|
||||
typedef struct _XINPUT_FEEDBACK_HEADER
|
||||
{
|
||||
xbox::dword_xt dwStatus;
|
||||
xbox::dword_xt dwStatus;
|
||||
HANDLE OPTIONAL hEvent;
|
||||
xbox::byte_xt Unknown1[4];
|
||||
xbox::byte_xt Unknown1[4];
|
||||
PVOID IoCompletedEvent; // PKEVENT really
|
||||
xbox::byte_xt Unknown2[50];
|
||||
xbox::byte_xt Unknown2[48];
|
||||
xbox::byte_xt bReportId;
|
||||
xbox::byte_xt bLength;
|
||||
}
|
||||
#include "AlignPosfix1.h"
|
||||
|
||||
XINPUT_FEEDBACK_HEADER, *PXINPUT_FEEDBACK_HEADER;
|
||||
|
||||
// ******************************************************************
|
||||
|
@ -268,7 +269,7 @@ typedef struct _XINPUT_FEEDBACK
|
|||
};
|
||||
}
|
||||
XINPUT_FEEDBACK, *PXINPUT_FEEDBACK;
|
||||
|
||||
#pragma pack()
|
||||
// ******************************************************************
|
||||
// * RTL_HEAP_PARAMETERS
|
||||
// ******************************************************************
|
||||
|
|
|
@ -78,18 +78,6 @@ XidGamepad* g_XidControllerObjArray[4];
|
|||
|
||||
#pragma pack(1)
|
||||
|
||||
/* Class-specific xid descriptor */
|
||||
struct XIDDesc {
|
||||
uint8_t bLength;
|
||||
uint8_t bDescriptorType;
|
||||
uint16_t bcdXid;
|
||||
uint8_t bType;
|
||||
uint8_t bSubType;
|
||||
uint8_t bMaxInputReportSize;
|
||||
uint8_t bMaxOutputReportSize;
|
||||
uint16_t wAlternateProductIds[4];
|
||||
};
|
||||
|
||||
/* Struct used by the Get_Report request -> button's state */
|
||||
struct XIDGamepadReport {
|
||||
uint8_t bReportId;
|
||||
|
@ -116,7 +104,7 @@ struct USBXIDState {
|
|||
XboxDeviceState dev; // gamepad device status
|
||||
USBEndpoint* intr; // interrupt endpoint of the gamepad
|
||||
|
||||
const XIDDesc* xid_desc; // xid-specific descriptor
|
||||
const XidDesc* xid_desc; // xid-specific descriptor
|
||||
|
||||
XIDGamepadReport in_state; // Get_Report struct
|
||||
XIDGamepadReport in_state_capabilities; // Get_Capabilities struct (in)
|
||||
|
@ -192,7 +180,7 @@ static const USBDesc desc_xbox_gamepad = {
|
|||
&desc_device_xbox_gamepad
|
||||
};
|
||||
|
||||
static const XIDDesc desc_xid_xbox_gamepad = {
|
||||
static const XidDesc desc_xid_xbox_gamepad = {
|
||||
0x10, // bLength
|
||||
USB_DT_XID, // bDescriptorType
|
||||
0x100, // bcdXid
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
#include "windows.h"
|
||||
#include "controllers/DlgDukeControllerConfig.h"
|
||||
#include "controllers/DlgSBControllerConfig.h"
|
||||
#include "controllers/DlgLibusbControllerConfig.h"
|
||||
#include "resource/ResCxbx.h"
|
||||
#include "input\InputManager.h"
|
||||
#include "Logging.h"
|
||||
|
@ -43,6 +44,7 @@ LRESULT CALLBACK WindowsCtrlSubProcNumericFilter(HWND hWnd, UINT uMsg, WPARAM wP
|
|||
HWND g_ChildWnd = NULL;
|
||||
static bool g_bHasOptChanges = false;
|
||||
static bool g_bHasInputChanges[4] = { false, false, false, false };
|
||||
static int g_ConfiguredDeviceType[4];
|
||||
|
||||
|
||||
void SyncInputSettings(int port_num, int dev_type, bool is_opt)
|
||||
|
@ -59,19 +61,23 @@ void SyncInputSettings(int port_num, int dev_type, bool is_opt)
|
|||
std::string profile_name = g_Settings->m_input_port[port_num].ProfileName;
|
||||
|
||||
g_EmuShared->SetInputDevNameSettings(dev_name.c_str(), port_num);
|
||||
auto it = std::find_if(g_Settings->m_input_profiles[dev_type].begin(),
|
||||
g_Settings->m_input_profiles[dev_type].end(), [&profile_name](const auto &profile) {
|
||||
if (profile.ProfileName == profile_name) {
|
||||
return true;
|
||||
// passthrough devices don't have profiles so skip those
|
||||
if ((dev_type != to_underlying(XBOX_INPUT_DEVICE::HW_XBOX_CONTROLLER)) &&
|
||||
(dev_type != to_underlying(XBOX_INPUT_DEVICE::HW_STEEL_BATTALION_CONTROLLER))) {
|
||||
auto it = std::find_if(g_Settings->m_input_profiles[dev_type].begin(),
|
||||
g_Settings->m_input_profiles[dev_type].end(), [&profile_name](const auto &profile) {
|
||||
if (profile.ProfileName == profile_name) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
if (it != g_Settings->m_input_profiles[dev_type].end()) {
|
||||
char controls_name[HIGHEST_NUM_BUTTONS][HOST_BUTTON_NAME_LENGTH];
|
||||
for (int index = 0; index < dev_num_buttons[dev_type]; index++) {
|
||||
strncpy(controls_name[index], it->ControlList[index].c_str(), 30);
|
||||
}
|
||||
return false;
|
||||
});
|
||||
if (it != g_Settings->m_input_profiles[dev_type].end()) {
|
||||
char controls_name[HIGHEST_NUM_BUTTONS][HOST_BUTTON_NAME_LENGTH];
|
||||
for (int index = 0; index < dev_num_buttons[dev_type]; index++) {
|
||||
strncpy(controls_name[index], it->ControlList[index].c_str(), 30);
|
||||
g_EmuShared->SetInputBindingsSettings(controls_name, dev_num_buttons[dev_type], port_num);
|
||||
}
|
||||
g_EmuShared->SetInputBindingsSettings(controls_name, dev_num_buttons[dev_type], port_num);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -139,12 +145,12 @@ INT_PTR CALLBACK DlgInputConfigProc(HWND hWndDlg, UINT uMsg, WPARAM wParam, LPAR
|
|||
}
|
||||
SendMessage(GetDlgItem(hWndDlg, IDC_IGNORE_KBMO_UNFOCUS), BM_SETCHECK, static_cast<WPARAM>(g_Settings->m_input_general.IgnoreKbMoUnfocus), 0);
|
||||
|
||||
// Reset option/input changes flag
|
||||
// Reset option/input changes flag and configured dev type
|
||||
g_bHasOptChanges = false;
|
||||
g_bHasInputChanges[0] = false;
|
||||
g_bHasInputChanges[1] = false;
|
||||
g_bHasInputChanges[2] = false;
|
||||
g_bHasInputChanges[3] = false;
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
g_bHasInputChanges[i] = false;
|
||||
g_ConfiguredDeviceType[i] = g_Settings->m_input_port[i].Type;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -160,14 +166,12 @@ INT_PTR CALLBACK DlgInputConfigProc(HWND hWndDlg, UINT uMsg, WPARAM wParam, LPAR
|
|||
if (g_bHasInputChanges[port]) {
|
||||
HWND hHandle = GetDlgItem(hWndDlg, IDC_DEVICE_PORT1 + port);
|
||||
int DeviceType = SendMessage(hHandle, CB_GETITEMDATA, SendMessage(hHandle, CB_GETCURSEL, 0, 0), 0);
|
||||
g_Settings->m_input_port[port].Type = DeviceType;
|
||||
if (DeviceType != to_underlying(XBOX_INPUT_DEVICE::MS_CONTROLLER_DUKE) &&
|
||||
DeviceType != to_underlying(XBOX_INPUT_DEVICE::MS_CONTROLLER_S)) {
|
||||
// Forcefully set the child devices to none. This will happen if the user sets MUs in the controller dialog but
|
||||
// then they set the parent device to a device that cannot support them in the input dialog
|
||||
g_Settings->m_input_port[port].SlotType[SLOT_TOP] = to_underlying(XBOX_INPUT_DEVICE::DEVICE_INVALID);
|
||||
g_Settings->m_input_port[port].SlotType[SLOT_BOTTOM] = to_underlying(XBOX_INPUT_DEVICE::DEVICE_INVALID);
|
||||
// Handle the case where the user has configured the inputs for a device type, but then they changed it to a different one later
|
||||
if ((DeviceType != g_ConfiguredDeviceType[port]) && (DeviceType != to_underlying(XBOX_INPUT_DEVICE::DEVICE_INVALID))) {
|
||||
DeviceType = g_ConfiguredDeviceType[port];
|
||||
EmuLogEx(CXBXR_MODULE::GUI, LOG_LEVEL::WARNING, "Mismatch between configured and current device, reverting to the original configured one.");
|
||||
}
|
||||
g_Settings->m_input_port[port].Type = DeviceType;
|
||||
SyncInputSettings(port, DeviceType, false);
|
||||
}
|
||||
}
|
||||
|
@ -196,8 +200,6 @@ INT_PTR CALLBACK DlgInputConfigProc(HWND hWndDlg, UINT uMsg, WPARAM wParam, LPAR
|
|||
assert(port != -1);
|
||||
HWND hHandle = GetDlgItem(hWndDlg, IDC_DEVICE_PORT1 + port);
|
||||
int DeviceType = SendMessage(hHandle, CB_GETITEMDATA, SendMessage(hHandle, CB_GETCURSEL, 0, 0), 0);
|
||||
assert(DeviceType > to_underlying(XBOX_INPUT_DEVICE::DEVICE_INVALID) &&
|
||||
DeviceType < to_underlying(XBOX_INPUT_DEVICE::DEVICE_MAX));
|
||||
|
||||
switch (DeviceType)
|
||||
{
|
||||
|
@ -215,10 +217,18 @@ INT_PTR CALLBACK DlgInputConfigProc(HWND hWndDlg, UINT uMsg, WPARAM wParam, LPAR
|
|||
}
|
||||
break;
|
||||
|
||||
case to_underlying(XBOX_INPUT_DEVICE::HW_XBOX_CONTROLLER):
|
||||
case to_underlying(XBOX_INPUT_DEVICE::HW_STEEL_BATTALION_CONTROLLER): {
|
||||
DialogBoxParam(GetModuleHandle(nullptr), MAKEINTRESOURCE(IDD_LIBUSB_CFG), hWndDlg, DlgLibUsbControllerConfigProc,
|
||||
(DeviceType << 8) | port);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
g_ConfiguredDeviceType[port] = DeviceType;
|
||||
g_bHasInputChanges[port] = true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -84,6 +84,7 @@ static int g_DlgIndexes[] = {
|
|||
IDC_LOG_XMO,
|
||||
IDC_LOG_RINP,
|
||||
IDC_LOG_JVS,
|
||||
IDC_LOG_LIBUSB,
|
||||
// Kernel
|
||||
IDC_LOG_KRNL,
|
||||
IDC_LOG_LOG,
|
||||
|
@ -386,6 +387,7 @@ INT_PTR CALLBACK DlgLogConfigProc(HWND hWndDlg, UINT uMsg, WPARAM wParam, LPARAM
|
|||
case IDC_LOG_DINP:
|
||||
case IDC_LOG_RINP:
|
||||
case IDC_LOG_XINP:
|
||||
case IDC_LOG_LIBUSB:
|
||||
case IDC_LOG_JVS:
|
||||
case IDC_LOG_SDL:
|
||||
case IDC_LOG_FILE:
|
||||
|
|
|
@ -56,6 +56,7 @@ void DukeInputWindow::Initialize(HWND hwnd, int port_num, int dev_type)
|
|||
m_port_num = port_num;
|
||||
m_bHasChanges = false;
|
||||
m_bIsBinding = false;
|
||||
m_num_devices = 0;
|
||||
|
||||
// Set window icon
|
||||
SetClassLong(m_hwnd_window, GCL_HICON, (LONG)LoadIcon(GetModuleHandle(nullptr), MAKEINTRESOURCE(IDI_CXBX)));
|
||||
|
|
|
@ -0,0 +1,219 @@
|
|||
// This is an open source non-commercial project. Dear PVS-Studio, please check it.
|
||||
// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
|
||||
// ******************************************************************
|
||||
// *
|
||||
// * This file is part of the Cxbx project.
|
||||
// *
|
||||
// * Cxbx and Cxbe are free software; you can redistribute them
|
||||
// * and/or modify them under the terms of the GNU General Public
|
||||
// * License as published by the Free Software Foundation; either
|
||||
// * version 2 of the license, or (at your option) any later version.
|
||||
// *
|
||||
// * 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 for more details.
|
||||
// *
|
||||
// * You should have recieved a copy of the GNU General Public License
|
||||
// * along with this program; see the file COPYING.
|
||||
// * If not, write to the Free Software Foundation, Inc.,
|
||||
// * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA.
|
||||
// *
|
||||
// * (c) 2021 ergo720
|
||||
// *
|
||||
// * All rights reserved
|
||||
// *
|
||||
// ******************************************************************
|
||||
|
||||
#define LOG_PREFIX CXBXR_MODULE::GUI
|
||||
|
||||
#include "Windows.h"
|
||||
#include "gui\resource\ResCxbx.h"
|
||||
#include "input\InputWindow.h"
|
||||
#include "common\input\LibusbDevice.h"
|
||||
|
||||
|
||||
static LibusbInputWindow *g_InputWindow = nullptr;
|
||||
|
||||
LibusbInputWindow::~LibusbInputWindow()
|
||||
{
|
||||
g_Settings->m_input_port[m_port_num].DeviceName = m_host_dev;
|
||||
g_Settings->m_input_port[m_port_num].ProfileName = "";
|
||||
g_Settings->m_input_port[m_port_num].SlotType[SLOT_TOP] = to_underlying(XBOX_INPUT_DEVICE::DEVICE_INVALID);
|
||||
g_Settings->m_input_port[m_port_num].SlotType[SLOT_BOTTOM] = to_underlying(XBOX_INPUT_DEVICE::DEVICE_INVALID);
|
||||
}
|
||||
|
||||
void LibusbInputWindow::Initialize(HWND hwnd, int port_num, int dev_type)
|
||||
{
|
||||
// Save window/device specific variables
|
||||
m_hwnd_window = hwnd;
|
||||
m_hwnd_device_list = GetDlgItem(m_hwnd_window, IDC_LIBUSB_LIST);
|
||||
m_hwnd_device_test = GetDlgItem(m_hwnd_window, IDC_LIBUSB_TEST);
|
||||
m_dev_type = dev_type;
|
||||
m_port_num = port_num;
|
||||
m_bIsBinding = false;
|
||||
m_num_devices = 0;
|
||||
m_DeviceConfig = nullptr;
|
||||
|
||||
// Set window icon
|
||||
SetClassLong(m_hwnd_window, GCL_HICON, (LONG)LoadIcon(GetModuleHandle(nullptr), MAKEINTRESOURCE(IDI_CXBX)));
|
||||
|
||||
// Set window title
|
||||
std::string title = (GetInputDeviceName(m_dev_type) + " at port ");
|
||||
SendMessage(m_hwnd_window, WM_SETTEXT, 0,
|
||||
reinterpret_cast<LPARAM>((title + PortUserFormat(std::to_string(m_port_num))).c_str()));
|
||||
|
||||
// Enumerate devices
|
||||
UpdateDeviceList();
|
||||
}
|
||||
|
||||
void LibusbInputWindow::ClearBindings()
|
||||
{
|
||||
// There are no profiles for libusb devices, so this is a nop
|
||||
}
|
||||
|
||||
void LibusbInputWindow::SaveSlotConfig()
|
||||
{
|
||||
// There are no slots for libusb devices, so this is a nop
|
||||
}
|
||||
|
||||
int LibusbInputWindow::EnableDefaultButton()
|
||||
{
|
||||
// The libusb window does not have a default button, so we return a dummy value here
|
||||
return -1;
|
||||
}
|
||||
|
||||
void LibusbInputWindow::UpdateDeviceList()
|
||||
{
|
||||
g_InputDeviceManager.RefreshDevices();
|
||||
|
||||
// Populate device list
|
||||
for (int i = 0; i < m_num_devices; i++) {
|
||||
SendMessage(m_hwnd_device_list, CB_DELETESTRING, 0, 0);
|
||||
}
|
||||
|
||||
// Add only libusb devices and only those that match the type we are configuring
|
||||
std::vector<std::string> dev_list = g_InputDeviceManager.GetDeviceList([this](const auto &Device) {
|
||||
if (Device->IsLibusb()) {
|
||||
if (to_underlying(dynamic_cast<const Libusb::LibusbDevice *>(Device)->GetLibusbType()) == m_dev_type) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
});
|
||||
for (const auto &str : dev_list) {
|
||||
SendMessage(m_hwnd_device_list, CB_ADDSTRING, 0, reinterpret_cast<LPARAM>(str.c_str()));
|
||||
++m_num_devices;
|
||||
}
|
||||
if (m_num_devices) {
|
||||
SendMessage(m_hwnd_device_list, CB_SETCURSEL, 0, 0);
|
||||
}
|
||||
|
||||
UpdateCurrentDevice();
|
||||
}
|
||||
|
||||
void LibusbInputWindow::TestInput()
|
||||
{
|
||||
using namespace std::chrono;
|
||||
|
||||
// Check if binding thread is still active
|
||||
if (m_bIsBinding) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto dev = g_InputDeviceManager.FindDevice(m_host_dev);
|
||||
if (dev != nullptr) {
|
||||
m_bIsBinding = true;
|
||||
|
||||
// Don't block the message processing loop
|
||||
std::thread([this, dev]() {
|
||||
auto LibusbDev = dynamic_cast<Libusb::LibusbDevice *>(dev.get());
|
||||
EnableWindow(m_hwnd_window, FALSE);
|
||||
SendMessage(m_hwnd_device_test, WM_SETTEXT, 0, reinterpret_cast<LPARAM>("..."));
|
||||
auto now = system_clock::now();
|
||||
auto timeout = now + milliseconds(INPUT_TIMEOUT);
|
||||
InputBuff buffer[2];
|
||||
std::memset(buffer, 0, 2 * sizeof(InputBuff));
|
||||
bool detect = false;
|
||||
while (now <= timeout) {
|
||||
LibusbDev->ExecuteIo(buffer, DIRECTION_IN);
|
||||
if (std::memcmp(reinterpret_cast<uint8_t *>(buffer) + XID_PACKET_HEADER,
|
||||
reinterpret_cast<uint8_t *>(&buffer[1]) + XID_PACKET_HEADER,
|
||||
sizeof(InputBuff) - XID_PACKET_HEADER)) {
|
||||
detect = true;
|
||||
break;
|
||||
}
|
||||
std::this_thread::sleep_for(milliseconds(10));
|
||||
now += milliseconds(10);
|
||||
}
|
||||
if (detect) {
|
||||
SendMessage(m_hwnd_device_test, WM_SETTEXT, 0, reinterpret_cast<LPARAM>("Ok!"));
|
||||
}
|
||||
else {
|
||||
SendMessage(m_hwnd_device_test, WM_SETTEXT, 0, reinterpret_cast<LPARAM>("Fail!"));
|
||||
}
|
||||
std::this_thread::sleep_for(milliseconds(2000));
|
||||
SendMessage(m_hwnd_device_test, WM_SETTEXT, 0, reinterpret_cast<LPARAM>("Test"));
|
||||
EnableWindow(m_hwnd_window, TRUE);
|
||||
m_bIsBinding = false;
|
||||
}).detach();
|
||||
}
|
||||
}
|
||||
|
||||
INT_PTR CALLBACK DlgLibUsbControllerConfigProc(HWND hWndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
switch (uMsg)
|
||||
{
|
||||
case WM_INITDIALOG:
|
||||
{
|
||||
int port_num = lParam & 0xFF;
|
||||
int dev_type = (lParam & 0xFF00) >> 8;
|
||||
|
||||
// Ensure that port_num is a valid xbox port
|
||||
assert(port_num >= PORT_1 && port_num <= PORT_4);
|
||||
|
||||
// Ensure that the controller type is valid
|
||||
assert(dev_type == to_underlying(XBOX_INPUT_DEVICE::HW_XBOX_CONTROLLER) ||
|
||||
dev_type == to_underlying(XBOX_INPUT_DEVICE::HW_STEEL_BATTALION_CONTROLLER));
|
||||
|
||||
g_InputWindow = new LibusbInputWindow;
|
||||
g_InputWindow->Initialize(hWndDlg, port_num, dev_type);
|
||||
}
|
||||
break;
|
||||
|
||||
case WM_CLOSE:
|
||||
{
|
||||
delete g_InputWindow;
|
||||
g_InputWindow = nullptr;
|
||||
EndDialog(hWndDlg, wParam);
|
||||
}
|
||||
break;
|
||||
|
||||
case WM_COMMAND:
|
||||
{
|
||||
switch (LOWORD(wParam))
|
||||
{
|
||||
case IDC_LIBUSB_LIST:
|
||||
if (HIWORD(wParam) == CBN_SELCHANGE) {
|
||||
g_InputWindow->UpdateCurrentDevice();
|
||||
}
|
||||
break;
|
||||
|
||||
case IDC_REFRESH_DEVICES:
|
||||
if (HIWORD(wParam) == BN_CLICKED) {
|
||||
g_InputWindow->UpdateDeviceList();
|
||||
}
|
||||
break;
|
||||
|
||||
case IDC_LIBUSB_TEST:
|
||||
if (HIWORD(wParam) == BN_CLICKED) {
|
||||
g_InputWindow->TestInput();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
// This is an open source non-commercial project. Dear PVS-Studio, please check it.
|
||||
// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
|
||||
// ******************************************************************
|
||||
// *
|
||||
// * This file is part of the Cxbx project.
|
||||
// *
|
||||
// * Cxbx and Cxbe are free software; you can redistribute them
|
||||
// * and/or modify them under the terms of the GNU General Public
|
||||
// * License as published by the Free Software Foundation; either
|
||||
// * version 2 of the license, or (at your option) any later version.
|
||||
// *
|
||||
// * 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 for more details.
|
||||
// *
|
||||
// * You should have recieved a copy of the GNU General Public License
|
||||
// * along with this program; see the file COPYING.
|
||||
// * If not, write to the Free Software Foundation, Inc.,
|
||||
// * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA.
|
||||
// *
|
||||
// * (c) 2021 ergo720
|
||||
// *
|
||||
// * All rights reserved
|
||||
// *
|
||||
// ******************************************************************
|
||||
|
||||
#pragma once
|
||||
|
||||
INT_PTR CALLBACK DlgLibUsbControllerConfigProc(HWND hWndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
|
|
@ -47,6 +47,7 @@ void SbcInputWindow::Initialize(HWND hwnd, int port_num, int dev_type)
|
|||
m_port_num = port_num;
|
||||
m_bHasChanges = false;
|
||||
m_bIsBinding = false;
|
||||
m_num_devices = 0;
|
||||
|
||||
// Set window icon
|
||||
SetClassLong(m_hwnd_window, GCL_HICON, (LONG)LoadIcon(GetModuleHandle(nullptr), MAKEINTRESOURCE(IDI_CXBX)));
|
||||
|
|
|
@ -29,6 +29,7 @@ GUIDELINES DESIGNINFO
|
|||
BEGIN
|
||||
IDD_INPUT_CFG, DIALOG
|
||||
BEGIN
|
||||
BOTTOMMARGIN, 188
|
||||
END
|
||||
|
||||
IDD_XID_DUKE_CFG, DIALOG
|
||||
|
@ -90,25 +91,25 @@ END
|
|||
// Dialog
|
||||
//
|
||||
|
||||
IDD_INPUT_CFG DIALOGEX 0, 0, 243, 188
|
||||
IDD_INPUT_CFG DIALOGEX 0, 0, 265, 192
|
||||
STYLE DS_SETFONT | DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU
|
||||
CAPTION "Cxbx-Reloaded : Input Configuration"
|
||||
FONT 8, "Verdana", 0, 0, 0x1
|
||||
BEGIN
|
||||
GROUPBOX "Xbox device configuration",IDC_DEVICE,13,10,217,103,WS_GROUP,WS_EX_CLIENTEDGE
|
||||
COMBOBOX IDC_DEVICE_PORT1,50,25,110,14,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
|
||||
COMBOBOX IDC_DEVICE_PORT2,50,46,110,14,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
|
||||
COMBOBOX IDC_DEVICE_PORT3,50,67,110,14,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
|
||||
COMBOBOX IDC_DEVICE_PORT4,50,88,110,14,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
|
||||
PUSHBUTTON "Configure",IDC_CONFIGURE_PORT1,166,25,50,14,BS_FLAT
|
||||
PUSHBUTTON "Configure",IDC_CONFIGURE_PORT2,166,46,50,14,BS_FLAT
|
||||
PUSHBUTTON "Configure",IDC_CONFIGURE_PORT3,166,67,50,14,BS_FLAT
|
||||
PUSHBUTTON "Configure",IDC_CONFIGURE_PORT4,166,88,50,14,BS_FLAT
|
||||
GROUPBOX "Xbox device configuration",IDC_DEVICE,13,10,240,103,WS_GROUP,WS_EX_CLIENTEDGE
|
||||
COMBOBOX IDC_DEVICE_PORT1,50,25,134,14,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
|
||||
COMBOBOX IDC_DEVICE_PORT2,50,46,134,14,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
|
||||
COMBOBOX IDC_DEVICE_PORT3,50,67,134,14,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
|
||||
COMBOBOX IDC_DEVICE_PORT4,50,88,134,14,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
|
||||
PUSHBUTTON "Configure",IDC_CONFIGURE_PORT1,192,25,50,14,BS_FLAT
|
||||
PUSHBUTTON "Configure",IDC_CONFIGURE_PORT2,192,46,50,14,BS_FLAT
|
||||
PUSHBUTTON "Configure",IDC_CONFIGURE_PORT3,192,67,50,14,BS_FLAT
|
||||
PUSHBUTTON "Configure",IDC_CONFIGURE_PORT4,192,88,50,14,BS_FLAT
|
||||
LTEXT "Port 1",IDC_STATIC,23,27,20,10
|
||||
LTEXT "Port 2",IDC_STATIC,23,48,20,10
|
||||
LTEXT "Port 3",IDC_STATIC,23,69,20,10
|
||||
LTEXT "Port 4",IDC_STATIC,23,90,20,10
|
||||
GROUPBOX "Options",IDC_INPUT_OPTIONS,13,119,217,64,WS_GROUP,WS_EX_CLIENTEDGE
|
||||
GROUPBOX "Options",IDC_INPUT_OPTIONS,13,119,240,64,WS_GROUP,WS_EX_CLIENTEDGE
|
||||
EDITTEXT IDC_MOUSE_RANGE,95,129,121,14
|
||||
EDITTEXT IDC_WHEEL_RANGE,95,147,121,14
|
||||
LTEXT "Mouse axis range",IDC_STATIC,23,129,69,14,SS_CENTERIMAGE
|
||||
|
@ -201,6 +202,15 @@ BEGIN
|
|||
PUSHBUTTON "Test",IDC_RUMBLE_TEST,95,11,45,14,BS_FLAT
|
||||
END
|
||||
|
||||
IDD_LIBUSB_CFG DIALOGEX 0, 0, 250, 35
|
||||
STYLE DS_SETFONT | DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU
|
||||
FONT 8, "Verdana", 0, 0, 0x1
|
||||
BEGIN
|
||||
COMBOBOX IDC_LIBUSB_LIST,15,11,120,15,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
|
||||
PUSHBUTTON "Refresh",IDC_REFRESH_DEVICES,140,11,45,14,BS_FLAT
|
||||
PUSHBUTTON "Test",IDC_LIBUSB_TEST,190,11,45,14,BS_FLAT
|
||||
END
|
||||
|
||||
IDD_SBC_CFG DIALOGEX 0, 0, 854, 280
|
||||
STYLE DS_SETFONT | DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU
|
||||
FONT 8, "Verdana", 0, 0, 0x1
|
||||
|
@ -508,6 +518,7 @@ BEGIN
|
|||
CONTROL "VSHCACHE",IDC_LOG_VSHCACHE,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,68,140,53,10
|
||||
CONTROL "RINP",IDC_LOG_RINP,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,202,195,32,10
|
||||
CONTROL "JVS",IDC_LOG_JVS,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,38,206,28,10
|
||||
CONTROL "LIBUSB",IDC_LOG_LIBUSB,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,80,206,41,10
|
||||
END
|
||||
|
||||
IDD_ABOUT DIALOGEX 0, 0, 310, 177
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#define IDD_XID_DUKE_CFG 134
|
||||
#define IDD_RUMBLE_CFG 135
|
||||
#define IDD_NETWORK_CFG 136
|
||||
#define IDD_LIBUSB_CFG 137
|
||||
#define IDC_LOG_CANCEL 892
|
||||
#define IDC_LOG_ACCEPT 893
|
||||
#define IDC_LOG_ENABLE_GENERAL 894
|
||||
|
@ -94,6 +95,7 @@
|
|||
#define IDC_LOG_VSHCACHE 962
|
||||
#define IDC_LOG_RINP 963
|
||||
#define IDC_LOG_JVS 964
|
||||
#define IDC_LOG_LIBUSB 965
|
||||
#define IDC_DEVICE_LIST_TOP_SLOT 995
|
||||
#define IDC_DEVICE_LIST_BOTTOM_SLOT 996
|
||||
#define IDC_DEVICE_TOP_SLOT 997
|
||||
|
@ -271,6 +273,8 @@
|
|||
#define IDC_SIGHT_CHANGE_NEGX 1320
|
||||
#define IDC_SIGHT_CHANGE_NEGY 1321
|
||||
#define IDC_SIGHT_CHANGE_POSX 1322
|
||||
#define IDC_LIBUSB_LIST 1323
|
||||
#define IDC_LIBUSB_TEST 1324
|
||||
#define ID_FILE_EXIT 40005
|
||||
#define ID_HELP_ABOUT 40008
|
||||
#define ID_EMULATION_START 40009
|
||||
|
|
Loading…
Reference in New Issue