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:
ergo720 2021-11-08 11:46:59 +01:00 committed by GitHub
commit f7042be933
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
30 changed files with 1001 additions and 107 deletions

4
.gitmodules vendored
View File

@ -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

View File

@ -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
)

View File

@ -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.**

1
import/libusb vendored Submodule

@ -0,0 +1 @@
Subproject commit 8d7616886e062f58d0de12db2acd5d81e06240bb

View File

@ -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}
)

View File

@ -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}
)

View File

@ -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"
)

View File

@ -82,6 +82,7 @@ const char* g_EnumModules2String[to_underlying(CXBXR_MODULE::MAX)] = {
"XMO ",
"RINP ",
"JVS ",
"LIBUSB ",
"KRNL ",
"LOG ",
"XBOX ",

View File

@ -90,6 +90,7 @@ typedef enum class _CXBXR_MODULE: unsigned int {
XMO,
RINP,
JVS,
LIBUSB,
// kernel
KRNL,
LOG,

View File

@ -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);
}
}
}

View File

@ -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;

View File

@ -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:

View File

@ -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) {

View File

@ -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

View File

@ -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();
}

View File

@ -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;
};

View File

@ -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";
}
}

View File

@ -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;
};
}

View File

@ -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);

View File

@ -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) {

View File

@ -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
// ******************************************************************

View File

@ -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

View File

@ -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;
}
}

View File

@ -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:

View 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)));

View File

@ -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;
}

View File

@ -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);

View File

@ -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)));

View File

@ -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

View File

@ -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