Merge pull request #2294 from ergo720/lightgun
Added support for the lightgun input device
This commit is contained in:
commit
c9fe07ab98
|
@ -104,9 +104,10 @@ file (GLOB CXBXR_HEADER_GUIv1
|
|||
"${CXBXR_ROOT_DIR}/src/common/input/Button.h"
|
||||
"${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/input/DlgDukeControllerConfig.h"
|
||||
"${CXBXR_ROOT_DIR}/src/gui/input/DlgLibusbControllerConfig.h"
|
||||
"${CXBXR_ROOT_DIR}/src/gui/input/DlgLightgunConfig.h"
|
||||
"${CXBXR_ROOT_DIR}/src/gui/input/DlgSBControllerConfig.h"
|
||||
"${CXBXR_ROOT_DIR}/src/gui/DlgAbout.h"
|
||||
"${CXBXR_ROOT_DIR}/src/gui/DlgAudioConfig.h"
|
||||
"${CXBXR_ROOT_DIR}/src/gui/DlgInputConfig.h"
|
||||
|
@ -272,9 +273,10 @@ file (GLOB CXBXR_SOURCE_GUIv1
|
|||
"${CXBXR_ROOT_DIR}/src/common/input/Button.cpp"
|
||||
"${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/input/DlgDukeControllerConfig.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/gui/input/DlgLibusbControllerConfig.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/gui/input/DlgLightgunConfig.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/gui/input/DlgSBControllerConfig.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/gui/DlgAbout.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/gui/DlgAudioConfig.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/gui/DlgInputConfig.cpp"
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit 0298a65f228b0c76915bb115d0134675777a0e3d
|
||||
Subproject commit 02353e8aa552f3db60804626e29838406f206443
|
|
@ -516,6 +516,10 @@ bool Settings::LoadConfig()
|
|||
lambda(dev_num_buttons[device], button_sbc_names);
|
||||
break;
|
||||
|
||||
case to_underlying(XBOX_INPUT_DEVICE::LIGHTGUN):
|
||||
lambda(dev_num_buttons[device], button_lightgun_names);
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -688,6 +692,9 @@ bool Settings::Save(std::string file_path)
|
|||
lambda(dev_num_buttons[device], button_sbc_names);
|
||||
break;
|
||||
|
||||
case to_underlying(XBOX_INPUT_DEVICE::LIGHTGUN):
|
||||
lambda(dev_num_buttons[device], button_lightgun_names);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -78,7 +78,6 @@ LRESULT CALLBACK ButtonDukeSubclassProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPA
|
|||
Button *button = reinterpret_cast<Button *>(dwRefData);
|
||||
if (wParam & MK_SHIFT) {
|
||||
static_cast<DukeInputWindow *>(button->GetWnd())->SwapMoCursorAxis(button);
|
||||
static_cast<DukeInputWindow *>(button->GetWnd())->UpdateProfile(std::string(), BUTTON_SWAP);
|
||||
}
|
||||
else if (!(wParam & ~MK_RBUTTON)) {
|
||||
button->ClearText();
|
||||
|
@ -99,7 +98,7 @@ LRESULT CALLBACK ButtonSbcSubclassProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPAR
|
|||
{
|
||||
switch (uMsg)
|
||||
{
|
||||
// Remove the window subclass when this window is destroyed
|
||||
// Remove the window subclass when this window is destroyed
|
||||
case WM_NCDESTROY: {
|
||||
RemoveWindowSubclass(hWnd, ButtonSbcSubclassProc, uIdSubclass);
|
||||
}
|
||||
|
@ -109,7 +108,6 @@ LRESULT CALLBACK ButtonSbcSubclassProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPAR
|
|||
Button *button = reinterpret_cast<Button *>(dwRefData);
|
||||
if (wParam & MK_SHIFT) {
|
||||
static_cast<SbcInputWindow *>(button->GetWnd())->SwapMoCursorAxis(button);
|
||||
static_cast<SbcInputWindow *>(button->GetWnd())->UpdateProfile(std::string(), BUTTON_SWAP);
|
||||
}
|
||||
else if (!(wParam & ~MK_RBUTTON)) {
|
||||
button->ClearText();
|
||||
|
@ -122,3 +120,25 @@ LRESULT CALLBACK ButtonSbcSubclassProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPAR
|
|||
|
||||
return DefSubclassProc(hWnd, uMsg, wParam, lParam);
|
||||
}
|
||||
|
||||
LRESULT CALLBACK ButtonLightgunSubclassProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
|
||||
{
|
||||
switch (uMsg)
|
||||
{
|
||||
// Remove the window subclass when this window is destroyed
|
||||
case WM_NCDESTROY: {
|
||||
RemoveWindowSubclass(hWnd, ButtonSbcSubclassProc, uIdSubclass);
|
||||
}
|
||||
break;
|
||||
|
||||
case WM_RBUTTONDOWN: {
|
||||
Button *button = reinterpret_cast<Button *>(dwRefData);
|
||||
button->ClearText();
|
||||
static_cast<LightgunInputWindow *>(button->GetWnd())->UpdateProfile(std::string(), BUTTON_CLEAR);
|
||||
}
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
return DefSubclassProc(hWnd, uMsg, wParam, lParam);
|
||||
}
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
#include <Commctrl.h>
|
||||
#include <string>
|
||||
|
||||
#define LIGHTGUN_NUM_BUTTONS 17
|
||||
#define XBOX_CTRL_NUM_BUTTONS 25
|
||||
#define SBC_NUM_BUTTONS 56
|
||||
#define HIGHEST_NUM_BUTTONS SBC_NUM_BUTTONS
|
||||
|
@ -62,3 +63,4 @@ private:
|
|||
|
||||
LRESULT CALLBACK ButtonDukeSubclassProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData);
|
||||
LRESULT CALLBACK ButtonSbcSubclassProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData);
|
||||
LRESULT CALLBACK ButtonLightgunSubclassProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData);
|
||||
|
|
|
@ -31,7 +31,8 @@
|
|||
#include "gui/resource/ResCxbx.h"
|
||||
|
||||
|
||||
static char *tooltip_text = "Left-click: start input detection\nRight-click: clear binding\nShift + right-click: toggle mouse input mode";
|
||||
static char *tooltip_text_toggle = "Left-click: start input detection\nRight-click: clear binding\nShift + right-click: toggle mouse input mode";
|
||||
static char *tooltip_text_no_toggle = "Left-click: start input detection\nRight-click: clear binding";
|
||||
|
||||
EmuDevice::EmuDevice(int type, HWND hwnd, void *wnd)
|
||||
{
|
||||
|
@ -45,7 +46,7 @@ EmuDevice::EmuDevice(int type, HWND hwnd, void *wnd)
|
|||
case to_underlying(XBOX_INPUT_DEVICE::ARCADE_STICK): {
|
||||
for (size_t i = 0; i < ARRAY_SIZE(button_xbox_ctrl_id); i++) {
|
||||
m_buttons.push_back(new Button(button_xbox_ctrl_id[i], i, hwnd, wnd));
|
||||
m_buttons.back()->AddTooltip(m_hwnd, m_tooltip_hwnd, tooltip_text);
|
||||
m_buttons.back()->AddTooltip(m_hwnd, m_tooltip_hwnd, tooltip_text_toggle);
|
||||
|
||||
// Install the subclass for the button control
|
||||
SetWindowSubclass(GetDlgItem(hwnd, button_xbox_ctrl_id[i]), ButtonDukeSubclassProc, 0, reinterpret_cast<DWORD_PTR>(m_buttons[i]));
|
||||
|
@ -56,7 +57,7 @@ EmuDevice::EmuDevice(int type, HWND hwnd, void *wnd)
|
|||
case to_underlying(XBOX_INPUT_DEVICE::STEEL_BATTALION_CONTROLLER): {
|
||||
for (size_t i = 0; i < ARRAY_SIZE(button_sbc_id); i++) {
|
||||
m_buttons.push_back(new Button(button_sbc_id[i], i, hwnd, wnd));
|
||||
m_buttons.back()->AddTooltip(m_hwnd, m_tooltip_hwnd, tooltip_text);
|
||||
m_buttons.back()->AddTooltip(m_hwnd, m_tooltip_hwnd, tooltip_text_toggle);
|
||||
|
||||
// Install the subclass for the button control
|
||||
SetWindowSubclass(GetDlgItem(hwnd, button_sbc_id[i]), ButtonSbcSubclassProc, 0, reinterpret_cast<DWORD_PTR>(m_buttons[i]));
|
||||
|
@ -64,6 +65,17 @@ EmuDevice::EmuDevice(int type, HWND hwnd, void *wnd)
|
|||
}
|
||||
break;
|
||||
|
||||
case to_underlying(XBOX_INPUT_DEVICE::LIGHTGUN): {
|
||||
for (size_t i = 0; i < ARRAY_SIZE(button_lightgun_id); i++) {
|
||||
m_buttons.push_back(new Button(button_lightgun_id[i], i, hwnd, wnd));
|
||||
m_buttons.back()->AddTooltip(m_hwnd, m_tooltip_hwnd, tooltip_text_no_toggle);
|
||||
|
||||
// Install the subclass for the button control
|
||||
SetWindowSubclass(GetDlgItem(hwnd, button_lightgun_id[i]), ButtonLightgunSubclassProc, 0, reinterpret_cast<DWORD_PTR>(m_buttons[i]));
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -122,3 +134,4 @@ void EmuDevice::CreateTooltipWindow()
|
|||
}
|
||||
|
||||
template void EmuDevice::BindDefault(const std::array<const char*, XBOX_CTRL_NUM_BUTTONS>& arr);
|
||||
template void EmuDevice::BindDefault(const std::array<const char *, LIGHTGUN_NUM_BUTTONS> &arr);
|
||||
|
|
|
@ -54,3 +54,4 @@ private:
|
|||
};
|
||||
|
||||
extern template void EmuDevice::BindDefault(const std::array<const char *, XBOX_CTRL_NUM_BUTTONS> &arr);
|
||||
extern template void EmuDevice::BindDefault(const std::array<const char *, LIGHTGUN_NUM_BUTTONS> &arr);
|
||||
|
|
|
@ -53,8 +53,8 @@ std::string GetInputDeviceName(int dev_type)
|
|||
str = "MS Gamepad S";
|
||||
break;
|
||||
|
||||
case to_underlying(XBOX_INPUT_DEVICE::LIGHT_GUN):
|
||||
str = "Light gun";
|
||||
case to_underlying(XBOX_INPUT_DEVICE::LIGHTGUN):
|
||||
str = "EMS TopGun II";
|
||||
break;
|
||||
|
||||
case to_underlying(XBOX_INPUT_DEVICE::STEERING_WHEEL):
|
||||
|
@ -178,7 +178,7 @@ const auto InputDevice::FindPort(std::string_view Port) const
|
|||
});
|
||||
}
|
||||
|
||||
void InputDevice::SetPort(std::string_view Port, bool Connect)
|
||||
void InputDevice::SetPort2(std::string_view Port, bool Connect)
|
||||
{
|
||||
if (Connect) {
|
||||
m_XboxPort.emplace_back(Port);
|
||||
|
|
|
@ -49,7 +49,7 @@ typedef enum class _XBOX_INPUT_DEVICE : int {
|
|||
DEVICE_INVALID = -1,
|
||||
MS_CONTROLLER_DUKE,
|
||||
MS_CONTROLLER_S,
|
||||
LIGHT_GUN,
|
||||
LIGHTGUN,
|
||||
STEERING_WHEEL,
|
||||
MEMORY_UNIT,
|
||||
IR_DONGLE,
|
||||
|
@ -122,7 +122,8 @@ public:
|
|||
// retrieves the port this device is attached to
|
||||
bool GetPort(std::string_view Port) const;
|
||||
// sets the port this device is attached to
|
||||
void SetPort(std::string_view Port, bool Connect);
|
||||
// NOTE: using SetPort2 to avoid a collision with the SetPort macro provided by Windows headers
|
||||
void SetPort2(std::string_view Port, bool Connect);
|
||||
// retuns true if it is a libusb device, false otherwise
|
||||
virtual bool IsLibusb() const { return false; };
|
||||
|
||||
|
|
|
@ -57,7 +57,7 @@
|
|||
int dev_num_buttons[to_underlying(XBOX_INPUT_DEVICE::DEVICE_MAX)] = {
|
||||
XBOX_CTRL_NUM_BUTTONS, // MS_CONTROLLER_DUKE
|
||||
XBOX_CTRL_NUM_BUTTONS, // MS_CONTROLLER_S
|
||||
0,
|
||||
LIGHTGUN_NUM_BUTTONS, // LIGHTGUN
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
|
@ -132,6 +132,7 @@ void InputDeviceManager::Initialize(bool is_gui, HWND hwnd)
|
|||
}
|
||||
break;
|
||||
|
||||
case to_underlying(XBOX_INPUT_DEVICE::LIGHTGUN):
|
||||
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):
|
||||
|
@ -272,7 +273,7 @@ void InputDeviceManager::UpdateDevices(std::string_view port, bool ack)
|
|||
else {
|
||||
auto host_dev = g_InputDeviceManager.FindDevice(port);
|
||||
if (host_dev != nullptr) {
|
||||
host_dev->SetPort(port, false);
|
||||
host_dev->SetPort2(port, false);
|
||||
}
|
||||
if (type != to_underlying(XBOX_INPUT_DEVICE::DEVICE_INVALID)) {
|
||||
if (type != to_underlying(dev->type)) {
|
||||
|
@ -309,7 +310,7 @@ void InputDeviceManager::DisconnectDevice(DeviceState *dev, std::string_view por
|
|||
}
|
||||
auto host_dev = g_InputDeviceManager.FindDevice(port);
|
||||
if (host_dev != nullptr) {
|
||||
host_dev->SetPort(port, false);
|
||||
host_dev->SetPort2(port, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -328,7 +329,7 @@ void InputDeviceManager::BindHostDevice(int type, std::string_view port)
|
|||
g_EmuShared->GetInputDevNameSettings(dev_name, port_num);
|
||||
auto dev = FindDevice(std::string(dev_name));
|
||||
if (dev != nullptr) {
|
||||
dev->SetPort(port, true);
|
||||
dev->SetPort2(port, true);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
@ -355,7 +356,7 @@ void InputDeviceManager::BindHostDevice(int type, std::string_view port)
|
|||
});
|
||||
dev->SetBindings(index, (it != controls.end()) ? *it : nullptr, port_str);
|
||||
}
|
||||
dev->SetPort(port, true);
|
||||
dev->SetPort2(port, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -379,6 +380,11 @@ bool InputDeviceManager::UpdateXboxPortInput(int port, void* buffer, int directi
|
|||
m_Mtx.unlock();
|
||||
return has_changed;
|
||||
|
||||
case to_underlying(XBOX_INPUT_DEVICE::LIGHTGUN):
|
||||
has_changed = UpdateInputLightgun(dev, buffer, direction, port, port_str);
|
||||
m_Mtx.unlock();
|
||||
return has_changed;
|
||||
|
||||
case to_underlying(XBOX_INPUT_DEVICE::STEEL_BATTALION_CONTROLLER):
|
||||
has_changed = UpdateInputSBC(dev, buffer, direction, port, port_str);
|
||||
m_Mtx.unlock();
|
||||
|
@ -394,7 +400,6 @@ bool InputDeviceManager::UpdateXboxPortInput(int port, void* buffer, int directi
|
|||
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):
|
||||
EmuLog(LOG_LEVEL::ERROR2, "An unsupported device is attached at port %d! The device was %s",
|
||||
|
@ -472,6 +477,130 @@ bool InputDeviceManager::UpdateInputXpad(std::shared_ptr<InputDevice>& Device, v
|
|||
return true;
|
||||
}
|
||||
|
||||
bool InputDeviceManager::UpdateInputLightgun(std::shared_ptr<InputDevice> &Device, void *Buffer, int Direction, int Port_num, const std::string &Port)
|
||||
{
|
||||
std::map<int, InputDevice::IoControl *> bindings = Device->GetBindings(Port);
|
||||
assert(bindings.size() == static_cast<size_t>(dev_num_buttons[to_underlying(XBOX_INPUT_DEVICE::LIGHTGUN)]));
|
||||
|
||||
// NOTE: the output state is not supported
|
||||
if (Direction == DIRECTION_IN) {
|
||||
if (!Device->UpdateInput()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// We change the toggle buttons only when a press -> release input transaction is completed
|
||||
// 0 -> Turbo left
|
||||
// 1 -> Turbo right
|
||||
// 2 -> Laser
|
||||
XpadInput *in_buf = reinterpret_cast<XpadInput *>(static_cast<uint8_t *>(Buffer) + XID_PACKET_HEADER);
|
||||
g_devs[Port_num].info.ligthgun.last_turbo = g_devs[Port_num].info.ligthgun.turbo;
|
||||
for (int i = 14, j = 0; i < 17; i++, j++) {
|
||||
ControlState state = (bindings[i] != nullptr) ? dynamic_cast<InputDevice::Input *>(bindings[i])->GetState() : 0.0;
|
||||
uint8_t curr_state = static_cast<uint8_t>(!!state);
|
||||
if ((~curr_state) & ((g_devs[Port_num].info.ligthgun.last_in_state >> j) & 1)) {
|
||||
switch (j)
|
||||
{
|
||||
case 0:
|
||||
if (g_devs[Port_num].info.ligthgun.turbo != 2) {
|
||||
g_devs[Port_num].info.ligthgun.turbo += 1;
|
||||
}
|
||||
break;
|
||||
|
||||
case 1:
|
||||
if (g_devs[Port_num].info.ligthgun.turbo != 0) {
|
||||
g_devs[Port_num].info.ligthgun.turbo -= 1;
|
||||
}
|
||||
break;
|
||||
|
||||
case 2:
|
||||
g_devs[Port_num].info.ligthgun.laser ^= 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
(g_devs[Port_num].info.ligthgun.last_in_state &= ~(1 << j)) |= (curr_state << j);
|
||||
}
|
||||
|
||||
in_buf->wButtons = XINPUT_LIGHTGUN_ONSCREEN;
|
||||
for (int i = 0; i < 6; i++) {
|
||||
ControlState state = (bindings[i] != nullptr) ? dynamic_cast<InputDevice::Input *>(bindings[i])->GetState() : 0.0;
|
||||
if (state) {
|
||||
in_buf->wButtons |= (1 << i);
|
||||
}
|
||||
else {
|
||||
in_buf->wButtons &= ~(1 << i);
|
||||
}
|
||||
}
|
||||
|
||||
if (g_devs[Port_num].info.ligthgun.turbo) {
|
||||
ControlState trigger_state = (bindings[6] != nullptr) ? dynamic_cast<InputDevice::Input *>(bindings[6])->GetState() : 0.0;
|
||||
if (trigger_state) {
|
||||
// Turbo mode 1
|
||||
in_buf->bAnalogButtons[0] ^= 0xFF;
|
||||
int start_idx = 7;
|
||||
if (g_devs[Port_num].info.ligthgun.turbo == 2) {
|
||||
// Turbo mode 2
|
||||
start_idx = 8;
|
||||
++g_devs[Port_num].info.ligthgun.turbo_delay;
|
||||
if (g_devs[Port_num].info.ligthgun.last_turbo != g_devs[Port_num].info.ligthgun.turbo) {
|
||||
g_devs[Port_num].info.ligthgun.turbo_delay = 0;
|
||||
}
|
||||
if (g_devs[Port_num].info.ligthgun.turbo_delay == LIGHTGUN_GRIP_DELAY) {
|
||||
in_buf->bAnalogButtons[1] ^= 0xFF;
|
||||
g_devs[Port_num].info.ligthgun.turbo_delay = 0;
|
||||
}
|
||||
}
|
||||
for (int i = start_idx, j = start_idx - 6; i < 10; i++, j++) {
|
||||
ControlState state = (bindings[i] != nullptr) ? dynamic_cast<InputDevice::Input *>(bindings[i])->GetState() : 0.0;
|
||||
in_buf->bAnalogButtons[j] = state ? 0xFF : 0;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Turbo active but trigger not pressed
|
||||
in_buf->bAnalogButtons[0] = 0;
|
||||
for (int i = 7, j = 1; i < 10; i++, j++) {
|
||||
ControlState state = (bindings[i] != nullptr) ? dynamic_cast<InputDevice::Input *>(bindings[i])->GetState() : 0.0;
|
||||
in_buf->bAnalogButtons[j] = state ? 0xFF : 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Turbo mode 0 (no turbo)
|
||||
for (int i = 6, j = 0; i < 10; i++, j++) {
|
||||
ControlState state = (bindings[i] != nullptr) ? dynamic_cast<InputDevice::Input *>(bindings[i])->GetState() : 0.0;
|
||||
in_buf->bAnalogButtons[j] = state ? 0xFF : 0;
|
||||
}
|
||||
}
|
||||
in_buf->bAnalogButtons[4] = 0;
|
||||
in_buf->bAnalogButtons[5] = 0;
|
||||
in_buf->bAnalogButtons[6] = 0;
|
||||
in_buf->bAnalogButtons[7] = 0;
|
||||
|
||||
for (int i = 10; i < 14; i += 2) {
|
||||
ControlState state_plus = (bindings[i] != nullptr) ? dynamic_cast<InputDevice::Input *>(bindings[i])->GetState() : 0.0;
|
||||
ControlState state_minus = (bindings[i + 1] != nullptr) ? dynamic_cast<InputDevice::Input *>(bindings[i + 1])->GetState() : 0.0;
|
||||
ControlState state = state_plus ? state_plus * 0x7FFF : state_minus ? -state_minus * 0x8000 : 0.0;
|
||||
switch (i)
|
||||
{
|
||||
case 10: {
|
||||
xbox::short_xt offset = std::abs(state) > 16383.0 ? g_devs[Port_num].info.ligthgun.offset_upp_x : g_devs[Port_num].info.ligthgun.offset_x;
|
||||
in_buf->sThumbLX = static_cast<int16_t>(state) + offset;
|
||||
}
|
||||
break;
|
||||
|
||||
case 12: {
|
||||
xbox::short_xt offset = std::abs(state) > 16383.0 ? g_devs[Port_num].info.ligthgun.offset_upp_y : g_devs[Port_num].info.ligthgun.offset_y;
|
||||
in_buf->sThumbLY = static_cast<int16_t>(state) + offset;
|
||||
}
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
in_buf->sThumbRX = in_buf->sThumbRY = 0;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool InputDeviceManager::UpdateInputSBC(std::shared_ptr<InputDevice>& Device, void* Buffer, int Direction, int Port_num, const std::string &Port)
|
||||
{
|
||||
std::map<int, InputDevice::IoControl*> bindings = Device->GetBindings(Port);
|
||||
|
@ -495,7 +624,6 @@ bool InputDeviceManager::UpdateInputSBC(std::shared_ptr<InputDevice>& Device, vo
|
|||
// 8 -> TunerDial Down
|
||||
// 9 -> GearLever Up
|
||||
// 10 -> GearLever Down
|
||||
static uint16_t last_in_state[XBOX_NUM_PORTS] = { 0, 0, 0, 0 };
|
||||
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;
|
||||
|
@ -511,10 +639,10 @@ bool InputDeviceManager::UpdateInputSBC(std::shared_ptr<InputDevice>& Device, vo
|
|||
for (int i = 4, j = 0; i < 6; i++, j++) {
|
||||
ControlState state = (bindings[i] != nullptr) ? dynamic_cast<InputDevice::Input *>(bindings[i])->GetState() : 0.0;
|
||||
uint16_t curr_in_state = static_cast<uint16_t>(!!state);
|
||||
if ((~curr_in_state) & ((last_in_state[Port_num] >> j) & 1)) {
|
||||
if ((~curr_in_state) & ((g_devs[Port_num].info.sbc.last_in_state >> j) & 1)) {
|
||||
in_buf->wButtons[0] ^= (1 << i);
|
||||
}
|
||||
(last_in_state[Port_num] &= ~(1 << j)) |= (curr_in_state << j);
|
||||
(g_devs[Port_num].info.sbc.last_in_state &= ~(1 << j)) |= (curr_in_state << j);
|
||||
}
|
||||
|
||||
for (int i = 6; i < 34; i++) {
|
||||
|
@ -531,10 +659,10 @@ bool InputDeviceManager::UpdateInputSBC(std::shared_ptr<InputDevice>& Device, vo
|
|||
for (int i = 34, j = 2; i < 39; i++, j++) {
|
||||
ControlState state = (bindings[i] != nullptr) ? dynamic_cast<InputDevice::Input *>(bindings[i])->GetState() : 0.0;
|
||||
uint16_t curr_in_state = static_cast<uint16_t>(!!state);
|
||||
if ((~curr_in_state) & ((last_in_state[Port_num] >> j) & 1)) {
|
||||
if ((~curr_in_state) & ((g_devs[Port_num].info.sbc.last_in_state >> j) & 1)) {
|
||||
in_buf->wButtons[2] ^= (1 << (i % 16));
|
||||
}
|
||||
(last_in_state[Port_num] &= ~(1 << j)) |= (curr_in_state << j);
|
||||
(g_devs[Port_num].info.sbc.last_in_state &= ~(1 << j)) |= (curr_in_state << j);
|
||||
}
|
||||
|
||||
for (int i = 39; i < 49; i += 2) {
|
||||
|
@ -593,7 +721,7 @@ bool InputDeviceManager::UpdateInputSBC(std::shared_ptr<InputDevice>& Device, vo
|
|||
for (int i = 52, j = 7; i < 56; i++, j++) {
|
||||
ControlState state = (bindings[i] != nullptr) ? dynamic_cast<InputDevice::Input *>(bindings[i])->GetState() : 0.0;
|
||||
uint16_t curr_in_state = static_cast<uint16_t>(!!state);
|
||||
if ((~curr_in_state) & ((last_in_state[Port_num] >> j) & 1)) {
|
||||
if ((~curr_in_state) & ((g_devs[Port_num].info.sbc.last_in_state >> j) & 1)) {
|
||||
switch (i)
|
||||
{
|
||||
case 52:
|
||||
|
@ -621,7 +749,7 @@ bool InputDeviceManager::UpdateInputSBC(std::shared_ptr<InputDevice>& Device, vo
|
|||
break;
|
||||
}
|
||||
}
|
||||
(last_in_state[Port_num] &= ~(1 << j)) |= (curr_in_state << j);
|
||||
(g_devs[Port_num].info.sbc.last_in_state &= ~(1 << j)) |= (curr_in_state << j);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -772,3 +900,35 @@ void InputDeviceManager::HotplugHandler(bool is_sdl)
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
ImVec2 InputDeviceManager::CalcLaserPos(int port)
|
||||
{
|
||||
static ImVec2 laser_coord[XBOX_NUM_PORTS] = { {0, 0}, {0, 0}, {0, 0}, {0, 0} };
|
||||
|
||||
// If somebody else is currently holding the lock, we won't wait and instead we report the last known laser position
|
||||
if (m_Mtx.try_lock()) {
|
||||
|
||||
// NOTE: even when switching to faux fullscreen, imgui will still use the original window size. Because of this, we only need to do this once.
|
||||
// If in the future the above is fixed, then this code will have to recalculate the new window size after a resizing has occured.
|
||||
static long width, height = -1;
|
||||
if (height == -1) {
|
||||
|
||||
RECT rect;
|
||||
GetClientRect(m_hwnd, &rect);
|
||||
width = std::max(rect.right - rect.left, 1l);
|
||||
height = std::max(rect.bottom - rect.top, 1l);
|
||||
}
|
||||
|
||||
// We convert the laser input coordinates given by xinput (in the sThumbLXY members of XpadInput) with linear interpolation y = y0 + (x - x0) * (y1 - y0) / (x1 - x0)
|
||||
// For laser_x x0 = -32768; x1 = 32767; y0 = 0; y1 = width
|
||||
// For laser_y x0 = -32768; x1 = 32767; y0 = height; y1 = 0
|
||||
int16_t laser_x = g_devs[port].info.buff.ctrl.InBuffer.sThumbLX;
|
||||
int16_t laser_y = g_devs[port].info.buff.ctrl.InBuffer.sThumbLY;
|
||||
laser_coord[port].x = ((laser_x + 32768) * width) / 65535.0f;
|
||||
laser_coord[port].y = height - ((laser_y + 32768) * height) / 65535.0f;
|
||||
|
||||
m_Mtx.unlock();
|
||||
}
|
||||
|
||||
return laser_coord[port];
|
||||
}
|
||||
|
|
|
@ -31,11 +31,7 @@
|
|||
#include <thread>
|
||||
#include "InputDevice.h"
|
||||
#include "EmuDevice.h"
|
||||
|
||||
// Prevent a collision with the SetPort provided by Windows
|
||||
#ifdef WIN32
|
||||
#undef SetPort
|
||||
#endif
|
||||
#include <imgui.h>
|
||||
|
||||
#define PORT_INVALID -1
|
||||
#define PORT_1 0
|
||||
|
@ -54,12 +50,15 @@
|
|||
|
||||
#define XID_PACKET_HEADER 2
|
||||
|
||||
#define LIGHTGUN_GRIP_DELAY 30
|
||||
|
||||
extern int dev_num_buttons[to_underlying(XBOX_INPUT_DEVICE::DEVICE_MAX)];
|
||||
|
||||
inline XBOX_INPUT_DEVICE input_support_list[] = {
|
||||
XBOX_INPUT_DEVICE::DEVICE_INVALID,
|
||||
XBOX_INPUT_DEVICE::MS_CONTROLLER_DUKE,
|
||||
XBOX_INPUT_DEVICE::MS_CONTROLLER_S,
|
||||
XBOX_INPUT_DEVICE::LIGHTGUN,
|
||||
XBOX_INPUT_DEVICE::STEEL_BATTALION_CONTROLLER,
|
||||
XBOX_INPUT_DEVICE::ARCADE_STICK,
|
||||
XBOX_INPUT_DEVICE::HW_XBOX_CONTROLLER,
|
||||
|
@ -156,6 +155,22 @@ struct XidSBCOutput {
|
|||
|
||||
#pragma pack()
|
||||
|
||||
struct LightGunData {
|
||||
xbox::short_xt offset_x;
|
||||
xbox::short_xt offset_y;
|
||||
xbox::short_xt offset_upp_x;
|
||||
xbox::short_xt offset_upp_y;
|
||||
uint8_t last_in_state;
|
||||
uint8_t last_turbo;
|
||||
uint8_t turbo_delay;
|
||||
uint8_t turbo;
|
||||
uint8_t laser;
|
||||
};
|
||||
|
||||
struct SbcData {
|
||||
uint16_t last_in_state;
|
||||
};
|
||||
|
||||
union InputBuff {
|
||||
XidGamepadInput ctrl;
|
||||
XidSBCInput sbc;
|
||||
|
@ -169,8 +184,13 @@ struct DeviceInfo {
|
|||
uint8_t ucSubType; // xapi subtype
|
||||
uint8_t ucInputStateSize; // input state size in bytes, does not include dwPacketNumber
|
||||
uint8_t ucFeedbackSize; // feedback size in bytes, does not include FeedbackHeader
|
||||
uint32_t dwPacketNumber;
|
||||
InputBuff buff;
|
||||
uint32_t dwPacketNumber; // increases by one when the input state changes
|
||||
InputBuff buff; // input buffer
|
||||
// device-specific additional data
|
||||
union {
|
||||
LightGunData ligthgun;
|
||||
SbcData sbc;
|
||||
};
|
||||
};
|
||||
|
||||
struct DeviceState {
|
||||
|
@ -214,11 +234,15 @@ public:
|
|||
void UpdateOpt(bool is_gui);
|
||||
// device hotplug event handler
|
||||
void HotplugHandler(bool is_sdl);
|
||||
// converts xinput -> screen coordinates to display the lightgun laser on the rendering window
|
||||
ImVec2 CalcLaserPos(int port);
|
||||
|
||||
|
||||
private:
|
||||
// update input for an xbox controller
|
||||
bool UpdateInputXpad(std::shared_ptr<InputDevice>& Device, void* Buffer, int Direction, const std::string &Port);
|
||||
// update input for a lightgun
|
||||
bool UpdateInputLightgun(std::shared_ptr<InputDevice> &Device, void *Buffer, int Direction, int Port_num, 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
|
||||
|
|
|
@ -150,7 +150,23 @@ InputDevice::Input* InputWindow::DetectInput(InputDevice* const Device, int ms)
|
|||
return nullptr; // no input
|
||||
}
|
||||
|
||||
void InputWindow::BindButton(int ControlID)
|
||||
int InputWindow::EnableDefaultButton()
|
||||
{
|
||||
if (std::strncmp(m_host_dev.c_str(), "XInput", std::strlen("XInput")) == 0) {
|
||||
EnableWindow(m_hwnd_default, TRUE);
|
||||
return XINPUT_DEFAULT;
|
||||
}
|
||||
else if (std::strncmp(m_host_dev.c_str(), "DInput", std::strlen("DInput")) == 0) {
|
||||
EnableWindow(m_hwnd_default, TRUE);
|
||||
return DINPUT_DEFAULT;
|
||||
}
|
||||
else {
|
||||
EnableWindow(m_hwnd_default, FALSE);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
void InputWindow::BindButton(int ControlID, bool auto_swap)
|
||||
{
|
||||
// Check if binding thread is still active
|
||||
// testcase: spacebar and enter keys; without this fix will cause repeat binding result.
|
||||
|
@ -163,7 +179,7 @@ void InputWindow::BindButton(int ControlID)
|
|||
m_bIsBinding = true;
|
||||
|
||||
// Don't block the message processing loop
|
||||
std::thread([this, dev, ControlID]() {
|
||||
std::thread([this, dev, ControlID, auto_swap]() {
|
||||
EnableWindow(m_hwnd_window, FALSE);
|
||||
char current_text[HOST_BUTTON_NAME_LENGTH];
|
||||
Button* xbox_button = m_DeviceConfig->FindButtonById(ControlID);
|
||||
|
@ -173,6 +189,9 @@ void InputWindow::BindButton(int ControlID)
|
|||
InputDevice::Input* dev_button = fut.get();
|
||||
if (dev_button) {
|
||||
xbox_button->UpdateText(dev_button->GetName().c_str());
|
||||
if (auto_swap) {
|
||||
SwapMoCursorAxis(xbox_button);
|
||||
}
|
||||
m_bHasChanges = true;
|
||||
}
|
||||
else {
|
||||
|
@ -201,7 +220,6 @@ void InputWindow::UpdateProfile(const std::string &name, int command)
|
|||
break;
|
||||
|
||||
case BUTTON_CLEAR:
|
||||
case BUTTON_SWAP:
|
||||
m_bHasChanges = true;
|
||||
break;
|
||||
}
|
||||
|
@ -348,6 +366,7 @@ void InputWindow::SwapMoCursorAxis(Button *button)
|
|||
else {
|
||||
button->UpdateText("Cursor X-");
|
||||
}
|
||||
m_bHasChanges = true;
|
||||
break;
|
||||
|
||||
case 'Y':
|
||||
|
@ -357,6 +376,7 @@ void InputWindow::SwapMoCursorAxis(Button *button)
|
|||
else {
|
||||
button->UpdateText("Cursor Y+");
|
||||
}
|
||||
m_bHasChanges = true;
|
||||
break;
|
||||
|
||||
}
|
||||
|
@ -374,6 +394,7 @@ void InputWindow::SwapMoCursorAxis(Button *button)
|
|||
else {
|
||||
button->UpdateText("Axis X-");
|
||||
}
|
||||
m_bHasChanges = true;
|
||||
break;
|
||||
|
||||
case 'Y':
|
||||
|
@ -383,6 +404,7 @@ void InputWindow::SwapMoCursorAxis(Button *button)
|
|||
else {
|
||||
button->UpdateText("Axis Y+");
|
||||
}
|
||||
m_bHasChanges = true;
|
||||
break;
|
||||
|
||||
}
|
||||
|
|
|
@ -39,7 +39,6 @@
|
|||
#define RUMBLE_TEST 6
|
||||
#define RUMBLE_CLEAR 7
|
||||
#define BUTTON_CLEAR 8
|
||||
#define BUTTON_SWAP 9
|
||||
|
||||
#define XINPUT_DEFAULT 0
|
||||
#define DINPUT_DEFAULT 1
|
||||
|
@ -56,7 +55,7 @@ public:
|
|||
virtual void Initialize(HWND hwnd, int port_num, int dev_type) = 0;
|
||||
~InputWindow();
|
||||
virtual void UpdateDeviceList();
|
||||
void BindButton(int ControlID);
|
||||
void BindButton(int ControlID, bool auto_swap = false);
|
||||
virtual void ClearBindings() = 0;
|
||||
virtual void UpdateProfile(const std::string& name, int command);
|
||||
void UpdateCurrentDevice();
|
||||
|
@ -73,8 +72,8 @@ protected:
|
|||
void DeleteProfile(const std::string& name);
|
||||
void OverwriteProfile(const std::string& name);
|
||||
void LoadDefaultProfile();
|
||||
virtual int EnableDefaultButton() = 0;
|
||||
virtual void SaveSlotConfig() = 0;
|
||||
virtual int EnableDefaultButton();
|
||||
|
||||
|
||||
// xbox device under configuration
|
||||
EmuDevice* m_DeviceConfig;
|
||||
|
@ -84,6 +83,8 @@ protected:
|
|||
HWND m_hwnd_device_list;
|
||||
// handle of the profile list combobox
|
||||
HWND m_hwnd_profile_list;
|
||||
// handle of the default bindings button
|
||||
HWND m_hwnd_default;
|
||||
// number of devices displayed in the device list combobox
|
||||
int m_num_devices;
|
||||
// type of the device
|
||||
|
@ -109,15 +110,12 @@ public:
|
|||
void BindDefault();
|
||||
void ClearBindings() override;
|
||||
void UpdateProfile(const std::string &name, int command) override;
|
||||
void SaveSlotConfig() override;
|
||||
void SaveSlotConfig();
|
||||
|
||||
|
||||
private:
|
||||
int EnableDefaultButton() override;
|
||||
void DetectOutput(int ms);
|
||||
|
||||
// handle of the default bindings button
|
||||
HWND m_hwnd_default;
|
||||
// handle of the rumble window
|
||||
HWND m_hwnd_rumble;
|
||||
// handle of the rumble combobox
|
||||
|
@ -132,8 +130,8 @@ class SbcInputWindow : public InputWindow
|
|||
{
|
||||
public:
|
||||
void Initialize(HWND hwnd, int port_num, int dev_type) override;
|
||||
~SbcInputWindow();
|
||||
void ClearBindings() override;
|
||||
void SaveSlotConfig() override;
|
||||
|
||||
|
||||
private:
|
||||
|
@ -143,10 +141,9 @@ private:
|
|||
class LibusbInputWindow : public InputWindow
|
||||
{
|
||||
public:
|
||||
~LibusbInputWindow();
|
||||
void Initialize(HWND hwnd, int port_num, int dev_type) override;
|
||||
~LibusbInputWindow();
|
||||
void ClearBindings() override;
|
||||
void SaveSlotConfig() override;
|
||||
void UpdateDeviceList() override;
|
||||
void TestInput();
|
||||
|
||||
|
@ -157,3 +154,12 @@ private:
|
|||
// handle of the test button
|
||||
HWND m_hwnd_device_test;
|
||||
};
|
||||
|
||||
class LightgunInputWindow : public InputWindow
|
||||
{
|
||||
public:
|
||||
void Initialize(HWND hwnd, int port_num, int dev_type) override;
|
||||
~LightgunInputWindow();
|
||||
void BindDefault();
|
||||
void ClearBindings() override;
|
||||
};
|
||||
|
|
|
@ -118,6 +118,27 @@ inline int button_sbc_id[SBC_NUM_BUTTONS] = {
|
|||
IDC_GEAR_UP,
|
||||
IDC_GEAR_DOWN,
|
||||
};
|
||||
|
||||
// Must have the same button order as defined in button_lightgun_names
|
||||
inline int button_lightgun_id[LIGHTGUN_NUM_BUTTONS] = {
|
||||
IDC_LG_STICK_UP,
|
||||
IDC_LG_STICK_DOWN,
|
||||
IDC_LG_STICK_LEFT,
|
||||
IDC_LG_STICK_RIGHT,
|
||||
IDC_LG_START,
|
||||
IDC_LG_SEBA,
|
||||
IDC_LG_TRIGGER,
|
||||
IDC_LG_GRIP,
|
||||
IDC_LG_A,
|
||||
IDC_LG_B,
|
||||
IDC_LG_AIM_POSX,
|
||||
IDC_LG_AIM_NEGX,
|
||||
IDC_LG_AIM_POSY,
|
||||
IDC_LG_AIM_NEGY,
|
||||
IDC_TURBO_LEFT,
|
||||
IDC_TURBO_RIGHT,
|
||||
IDC_LASER,
|
||||
};
|
||||
#endif
|
||||
|
||||
inline constexpr const char* button_xbox_ctrl_names[XBOX_CTRL_NUM_BUTTONS] = {
|
||||
|
@ -207,6 +228,26 @@ inline constexpr const char *button_sbc_names[SBC_NUM_BUTTONS] = {
|
|||
"GearLever Down",
|
||||
};
|
||||
|
||||
inline constexpr const char *button_lightgun_names[LIGHTGUN_NUM_BUTTONS] = {
|
||||
"Stick Up",
|
||||
"Stick Down",
|
||||
"Stick Left",
|
||||
"Stick Right",
|
||||
"START",
|
||||
"SE/BA",
|
||||
"Trigger",
|
||||
"Grip",
|
||||
"A",
|
||||
"B",
|
||||
"Aim X+",
|
||||
"Aim X-",
|
||||
"Aim Y+",
|
||||
"Aim Y-",
|
||||
"Turbo Left",
|
||||
"Turbo Right",
|
||||
"Laser",
|
||||
};
|
||||
|
||||
constexpr bool check_button_name_size(unsigned max_num_buttons)
|
||||
{
|
||||
switch (max_num_buttons)
|
||||
|
|
|
@ -152,6 +152,7 @@ EmuShared::EmuShared()
|
|||
m_bEmulating_status = false;
|
||||
m_bFirstLaunch = false;
|
||||
m_bClipCursor = false;
|
||||
m_LightgunLaser = 0xF; // laser on by default on all ports
|
||||
|
||||
std::memset(m_DeviceControlNames, '\0', sizeof(m_DeviceControlNames));
|
||||
std::memset(m_DeviceName, '\0', sizeof(m_DeviceName));
|
||||
|
|
|
@ -257,6 +257,12 @@ class EmuShared : public Mutex
|
|||
void GetClipCursorFlag(bool *value) { Lock(); *value = m_bClipCursor; Unlock(); }
|
||||
void SetClipCursorFlag(const bool value) { Lock(); m_bClipCursor = value; Unlock(); }
|
||||
|
||||
// ******************************************************************
|
||||
// * LightgunLaser flag Accessors
|
||||
// ******************************************************************
|
||||
void GetLightgunLaser(uint8_t *value, int port) { Lock(); *value = (m_LightgunLaser >> port) & 1; Unlock(); }
|
||||
void SetLightgunLaser(const uint8_t *value, int port) { Lock(); (m_LightgunLaser &= ~(1 << port)) |= ((*value) << port); Unlock(); }
|
||||
|
||||
// ******************************************************************
|
||||
// * ImGui Accessors
|
||||
// ******************************************************************
|
||||
|
@ -364,6 +370,7 @@ class EmuShared : public Mutex
|
|||
#else
|
||||
unsigned int m_Reserved;
|
||||
#endif
|
||||
uint8_t m_LightgunLaser;
|
||||
bool m_bFirstLaunch;
|
||||
bool m_bClipCursor;
|
||||
unsigned int m_dwKrnlProcID; // Only used for kernel mode level.
|
||||
|
|
|
@ -14,6 +14,13 @@
|
|||
#include "core/kernel/init/CxbxKrnl.h"
|
||||
#include "core/hle/D3D8/XbVertexBuffer.h"
|
||||
|
||||
const ImColor ImGuiVideo::m_laser_col[4] = {
|
||||
ImColor(ImVec4(1.0f, 0.0f, 0.0f, 1.0f)), // player1: red
|
||||
ImColor(ImVec4(0.0f, 1.0f, 0.0f, 1.0f)), // player2: green
|
||||
ImColor(ImVec4(0.0f, 0.0f, 1.0f, 1.0f)), // player3: blue
|
||||
ImColor(ImVec4(1.0f, 1.0f, 0.0f, 1.0f)) // player4: yellow
|
||||
};
|
||||
|
||||
bool ImGuiVideo::Initialize()
|
||||
{
|
||||
g_EmuShared->GetImGuiVideoWindows(&m_windows);
|
||||
|
@ -46,4 +53,13 @@ void ImGuiVideo::DrawWidgets(bool is_focus, ImGuiWindowFlags input_handler)
|
|||
ImGui::End();
|
||||
}
|
||||
}
|
||||
|
||||
// Render the lightgun laser
|
||||
for (int port = PORT_1; port < XBOX_NUM_PORTS; ++port) {
|
||||
if (g_devs[port].type == XBOX_INPUT_DEVICE::LIGHTGUN && g_devs[port].info.ligthgun.laser) {
|
||||
ImGui::Begin("Laser", nullptr, ImGuiWindowFlags_NoBackground | ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_NoDecoration);
|
||||
ImGui::GetForegroundDrawList()->AddCircleFilled(g_InputDeviceManager.CalcLaserPos(port), 5, m_laser_col[port], 0);
|
||||
ImGui::End();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,4 +20,5 @@ public:
|
|||
|
||||
protected:
|
||||
imgui_video_windows m_windows;
|
||||
static const ImColor m_laser_col[4];
|
||||
};
|
||||
|
|
|
@ -346,6 +346,7 @@ std::map<const std::string, const xbox_patch_t> g_PatchTable = {
|
|||
PATCH_ENTRY("XInputGetState", xbox::EMUPATCH(XInputGetState), PATCH_HLE_OHCI),
|
||||
PATCH_ENTRY("XInputOpen", xbox::EMUPATCH(XInputOpen), PATCH_HLE_OHCI),
|
||||
PATCH_ENTRY("XInputPoll", xbox::EMUPATCH(XInputPoll), PATCH_HLE_OHCI),
|
||||
PATCH_ENTRY("XInputSetLightgunCalibration", xbox::EMUPATCH(XInputSetLightgunCalibration), PATCH_HLE_OHCI),
|
||||
PATCH_ENTRY("XInputSetState", xbox::EMUPATCH(XInputSetState), PATCH_HLE_OHCI),
|
||||
|
||||
// XAPI
|
||||
|
|
|
@ -59,7 +59,7 @@ std::atomic<bool> g_bIsDevicesEmulating = false;
|
|||
std::atomic<bool> g_bXppGuard = false;
|
||||
|
||||
// Allocate enough memory for the max number of devices we can support simultaneously
|
||||
// 4 duke / S / sbc / arcade joystick (mutually exclusive) + 8 memory units
|
||||
// 4 duke / S / sbc / arcade joystick / lightgun (mutually exclusive) + 8 memory units
|
||||
DeviceState g_devs[MAX_DEVS];
|
||||
|
||||
xbox::ulong_xt g_Mounted_MUs = 0;
|
||||
|
@ -114,6 +114,7 @@ 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::LIGHTGUN:
|
||||
case XBOX_INPUT_DEVICE::ARCADE_STICK:
|
||||
case XBOX_INPUT_DEVICE::HW_XBOX_CONTROLLER: {
|
||||
if (XppType == g_DeviceType_Gamepad) {
|
||||
|
@ -137,7 +138,6 @@ bool operator==(xbox::PXPP_DEVICE_TYPE XppType, XBOX_INPUT_DEVICE XidType)
|
|||
}
|
||||
break;
|
||||
|
||||
case XBOX_INPUT_DEVICE::LIGHT_GUN:
|
||||
case XBOX_INPUT_DEVICE::STEERING_WHEEL:
|
||||
case XBOX_INPUT_DEVICE::IR_DONGLE:
|
||||
default:
|
||||
|
@ -154,6 +154,7 @@ 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::LIGHTGUN:
|
||||
case XBOX_INPUT_DEVICE::ARCADE_STICK:
|
||||
case XBOX_INPUT_DEVICE::HW_XBOX_CONTROLLER:
|
||||
xpp = g_DeviceType_Gamepad;
|
||||
|
@ -256,6 +257,20 @@ void ConstructHleInputDevice(DeviceState *dev, DeviceState *upstream, int type,
|
|||
dev->info.ucFeedbackSize = sizeof(XpadOutput);
|
||||
break;
|
||||
|
||||
case to_underlying(XBOX_INPUT_DEVICE::LIGHTGUN):
|
||||
dev->type = XBOX_INPUT_DEVICE::LIGHTGUN;
|
||||
dev->info.bAutoPollDefault = true;
|
||||
dev->info.ucType = XINPUT_DEVTYPE_GAMEPAD;
|
||||
dev->info.ucSubType = XINPUT_DEVSUBTYPE_GC_LIGHTGUN;
|
||||
dev->info.ucInputStateSize = sizeof(XpadInput);
|
||||
dev->info.ucFeedbackSize = sizeof(XpadOutput);
|
||||
dev->info.ligthgun.offset_x = dev->info.ligthgun.offset_y = 0;
|
||||
dev->info.ligthgun.offset_upp_x = dev->info.ligthgun.offset_upp_x = 0;
|
||||
dev->info.ligthgun.last_in_state = dev->info.ligthgun.turbo_delay = 0;
|
||||
dev->info.ligthgun.turbo = dev->info.ligthgun.last_turbo = 0;
|
||||
g_EmuShared->GetLightgunLaser(&dev->info.ligthgun.laser, port_num);
|
||||
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;
|
||||
|
@ -267,6 +282,7 @@ void ConstructHleInputDevice(DeviceState *dev, DeviceState *upstream, int type,
|
|||
dev->info.ucSubType = XINPUT_DEVSUBTYPE_GC_GAMEPAD;
|
||||
dev->info.ucInputStateSize = sizeof(SBCInput);
|
||||
dev->info.ucFeedbackSize = sizeof(SBCOutput);
|
||||
dev->info.sbc.last_in_state = 0;
|
||||
if (type == to_underlying(XBOX_INPUT_DEVICE::HW_STEEL_BATTALION_CONTROLLER)) {
|
||||
dev->type = XBOX_INPUT_DEVICE::HW_STEEL_BATTALION_CONTROLLER;
|
||||
char dev_name[50];
|
||||
|
@ -325,6 +341,7 @@ void DestructHleInputDevice(DeviceState *dev)
|
|||
{
|
||||
case XBOX_INPUT_DEVICE::MS_CONTROLLER_DUKE:
|
||||
case XBOX_INPUT_DEVICE::MS_CONTROLLER_S:
|
||||
case XBOX_INPUT_DEVICE::LIGHTGUN:
|
||||
dev->info.bAutoPollDefault = false;
|
||||
dev->info.ucType = 0;
|
||||
dev->info.ucSubType = 0;
|
||||
|
@ -480,7 +497,7 @@ xbox::dword_xt CxbxImpl_XInputHandler(xbox::HANDLE hDevice, xbox::PXINPUT_STATE
|
|||
}
|
||||
|
||||
if constexpr (!IsXInputPoll) {
|
||||
std::memcpy((void *)&pState->Gamepad, reinterpret_cast<uint8_t *>(&g_devs[port].info.buff) + 2, g_devs[port].info.ucInputStateSize);
|
||||
std::memcpy((void *)&pState->Gamepad, reinterpret_cast<uint8_t *>(&g_devs[port].info.buff) + XID_PACKET_HEADER, g_devs[port].info.ucInputStateSize);
|
||||
pState->dwPacketNumber = g_devs[port].info.dwPacketNumber;
|
||||
}
|
||||
|
||||
|
@ -787,6 +804,88 @@ xbox::dword_xt WINAPI xbox::EMUPATCH(XInputSetState)
|
|||
RETURN(pFeedback->Header.dwStatus);
|
||||
}
|
||||
|
||||
// ******************************************************************
|
||||
// * patch: XGetDeviceEnumerationStatus
|
||||
// ******************************************************************
|
||||
xbox::dword_xt WINAPI xbox::EMUPATCH(XGetDeviceEnumerationStatus)()
|
||||
{
|
||||
LOG_FUNC();
|
||||
|
||||
dword_xt ret = (g_bIsDevicesInitializing || g_bIsDevicesEmulating) ? XDEVICE_ENUMERATION_BUSY : XDEVICE_ENUMERATION_IDLE;
|
||||
|
||||
RETURN(ret);
|
||||
}
|
||||
|
||||
// ******************************************************************
|
||||
// * patch: XInputGetDeviceDescription
|
||||
// ******************************************************************
|
||||
xbox::dword_xt WINAPI xbox::EMUPATCH(XInputGetDeviceDescription)
|
||||
(
|
||||
HANDLE hDevice,
|
||||
PXINPUT_DEVICE_DESCRIPTION pDescription
|
||||
)
|
||||
{
|
||||
LOG_FUNC_BEGIN
|
||||
LOG_FUNC_ARG(hDevice)
|
||||
LOG_FUNC_ARG(pDescription)
|
||||
LOG_FUNC_END;
|
||||
|
||||
dword_xt ret;
|
||||
int port = static_cast<DeviceState *>(hDevice)->port_idx;
|
||||
if (g_devs[port].info.hHandle == hDevice && !g_devs[port].bPendingRemoval) {
|
||||
if (g_devs[port].type == XBOX_INPUT_DEVICE::LIGHTGUN) {
|
||||
// These values are those reported in the device descriptor for the EMS TopGun II documented in the xboxdevwiki
|
||||
pDescription->wVendorID = 0x0b9a;
|
||||
pDescription->wProductID = 0x016b;
|
||||
pDescription->wVersion = 0x457;
|
||||
ret = ERROR_SUCCESS;
|
||||
}
|
||||
else {
|
||||
// NOTE: Phantasy star online also calls this on the keyboard device
|
||||
ret = ERROR_NOT_SUPPORTED;
|
||||
}
|
||||
}
|
||||
else {
|
||||
ret = ERROR_DEVICE_NOT_CONNECTED;
|
||||
}
|
||||
|
||||
RETURN(ret);
|
||||
}
|
||||
|
||||
// ******************************************************************
|
||||
// * patch: XInputSetLightgunCalibration
|
||||
// ******************************************************************
|
||||
xbox::dword_xt WINAPI xbox::EMUPATCH(XInputSetLightgunCalibration)
|
||||
(
|
||||
HANDLE hDevice,
|
||||
PXINPUT_LIGHTGUN_CALIBRATION_OFFSETS pCalibrationOffsets
|
||||
)
|
||||
{
|
||||
LOG_FUNC_BEGIN
|
||||
LOG_FUNC_ARG(hDevice)
|
||||
LOG_FUNC_ARG(pCalibrationOffsets)
|
||||
LOG_FUNC_END;
|
||||
|
||||
dword_xt ret;
|
||||
int port = static_cast<DeviceState *>(hDevice)->port_idx;
|
||||
if (g_devs[port].info.hHandle == hDevice && !g_devs[port].bPendingRemoval) {
|
||||
if (g_devs[port].type == XBOX_INPUT_DEVICE::LIGHTGUN) {
|
||||
g_devs[port].info.ligthgun.offset_x = pCalibrationOffsets->wCenterX;
|
||||
g_devs[port].info.ligthgun.offset_y = pCalibrationOffsets->wCenterY;
|
||||
g_devs[port].info.ligthgun.offset_upp_x = pCalibrationOffsets->wUpperLeftX;
|
||||
g_devs[port].info.ligthgun.offset_upp_y = pCalibrationOffsets->wUpperLeftY;
|
||||
ret = ERROR_SUCCESS;
|
||||
}
|
||||
else {
|
||||
ret = ERROR_NOT_SUPPORTED;
|
||||
}
|
||||
}
|
||||
else {
|
||||
ret = ERROR_DEVICE_NOT_CONNECTED;
|
||||
}
|
||||
|
||||
RETURN(ret);
|
||||
}
|
||||
|
||||
// ******************************************************************
|
||||
// * patch: SetThreadPriorityBoost
|
||||
|
@ -1394,42 +1493,6 @@ xbox::dword_xt WINAPI xbox::EMUPATCH(XMountMUA)
|
|||
RETURN(ERROR_SUCCESS);
|
||||
}
|
||||
|
||||
// ******************************************************************
|
||||
// * patch: XGetDeviceEnumerationStatus
|
||||
// ******************************************************************
|
||||
xbox::dword_xt WINAPI xbox::EMUPATCH(XGetDeviceEnumerationStatus)()
|
||||
{
|
||||
|
||||
|
||||
LOG_FUNC();
|
||||
|
||||
dword_xt ret = (g_bIsDevicesInitializing || g_bIsDevicesEmulating) ? XDEVICE_ENUMERATION_BUSY : XDEVICE_ENUMERATION_IDLE;
|
||||
|
||||
RETURN(ret);
|
||||
}
|
||||
|
||||
// ******************************************************************
|
||||
// * patch: XInputGetDeviceDescription
|
||||
// ******************************************************************
|
||||
xbox::dword_xt WINAPI xbox::EMUPATCH(XInputGetDeviceDescription)
|
||||
(
|
||||
HANDLE hDevice,
|
||||
PVOID pDescription
|
||||
)
|
||||
{
|
||||
|
||||
|
||||
LOG_FUNC_BEGIN
|
||||
LOG_FUNC_ARG(hDevice)
|
||||
LOG_FUNC_ARG(pDescription)
|
||||
LOG_FUNC_END;
|
||||
|
||||
// TODO: Lightgun support?
|
||||
LOG_UNIMPLEMENTED();
|
||||
|
||||
RETURN(ERROR_NOT_SUPPORTED); // ERROR_DEVICE_NOT_CONNECTED;
|
||||
}
|
||||
|
||||
// ******************************************************************
|
||||
// * patch: XMountMURootA
|
||||
// ******************************************************************
|
||||
|
|
|
@ -270,6 +270,35 @@ typedef struct _XINPUT_FEEDBACK
|
|||
}
|
||||
XINPUT_FEEDBACK, *PXINPUT_FEEDBACK;
|
||||
#pragma pack()
|
||||
|
||||
// ******************************************************************
|
||||
// * XINPUT_DEVICE_DESCRIPTION
|
||||
// ******************************************************************
|
||||
typedef struct _XINPUT_DEVICE_DESCRIPTION
|
||||
{
|
||||
xbox::ushort_xt wVendorID;
|
||||
xbox::ushort_xt wProductID;
|
||||
xbox::ushort_xt wVersion;
|
||||
}
|
||||
XINPUT_DEVICE_DESCRIPTION, *PXINPUT_DEVICE_DESCRIPTION;
|
||||
|
||||
// ******************************************************************
|
||||
// * XINPUT_LIGHTGUN_CALIBRATION_OFFSETS
|
||||
// ******************************************************************
|
||||
typedef struct _XINPUT_LIGHTGUN_CALIBRATION_OFFSETS
|
||||
{
|
||||
xbox::short_xt wCenterX;
|
||||
xbox::short_xt wCenterY;
|
||||
xbox::short_xt wUpperLeftX;
|
||||
xbox::short_xt wUpperLeftY;
|
||||
}
|
||||
XINPUT_LIGHTGUN_CALIBRATION_OFFSETS, *PXINPUT_LIGHTGUN_CALIBRATION_OFFSETS;
|
||||
|
||||
// ******************************************************************
|
||||
// * Lightgun-specific flags
|
||||
// ******************************************************************
|
||||
#define XINPUT_LIGHTGUN_ONSCREEN 0x2000
|
||||
|
||||
// ******************************************************************
|
||||
// * RTL_HEAP_PARAMETERS
|
||||
// ******************************************************************
|
||||
|
@ -439,6 +468,28 @@ xbox::dword_xt WINAPI EMUPATCH(XInputSetState)
|
|||
IN OUT PXINPUT_FEEDBACK pFeedback
|
||||
);
|
||||
|
||||
// ******************************************************************
|
||||
// * patch: XGetDeviceEnumerationStatus
|
||||
// ******************************************************************
|
||||
xbox::dword_xt WINAPI EMUPATCH(XGetDeviceEnumerationStatus)();
|
||||
|
||||
// ******************************************************************
|
||||
// * patch: XInputGetDeviceDescription
|
||||
// ******************************************************************
|
||||
xbox::dword_xt WINAPI EMUPATCH(XInputGetDeviceDescription)
|
||||
(
|
||||
HANDLE hDevice,
|
||||
PXINPUT_DEVICE_DESCRIPTION pDescription
|
||||
);
|
||||
|
||||
// ******************************************************************
|
||||
// * patch: XInputSetLightgunCalibration
|
||||
// ******************************************************************
|
||||
xbox::dword_xt WINAPI EMUPATCH(XInputSetLightgunCalibration)
|
||||
(
|
||||
HANDLE hDevice,
|
||||
PXINPUT_LIGHTGUN_CALIBRATION_OFFSETS pCalibrationOffsets
|
||||
);
|
||||
|
||||
// ******************************************************************
|
||||
// * patch: CreateMutex
|
||||
|
@ -732,25 +783,11 @@ xbox::bool_xt WINAPI EMUPATCH(MoveFileA)
|
|||
LPCSTR lpNewFileName
|
||||
);
|
||||
|
||||
// ******************************************************************
|
||||
// * patch: XGetDeviceEnumerationStatus
|
||||
// ******************************************************************
|
||||
xbox::dword_xt WINAPI EMUPATCH(XGetDeviceEnumerationStatus)();
|
||||
|
||||
// ******************************************************************
|
||||
// * patch: SwitchToThread
|
||||
// ******************************************************************
|
||||
xbox::bool_xt WINAPI EMUPATCH(SwitchToThread)();
|
||||
|
||||
// ******************************************************************
|
||||
// * patch: XInputGetDeviceDescription
|
||||
// ******************************************************************
|
||||
xbox::dword_xt WINAPI EMUPATCH(XInputGetDeviceDescription)
|
||||
(
|
||||
HANDLE hDevice,
|
||||
PVOID pDescription
|
||||
);
|
||||
|
||||
// ******************************************************************
|
||||
// * patch: ReadFileEx
|
||||
// ******************************************************************
|
||||
|
|
|
@ -535,6 +535,12 @@ XBSYSAPI EXPORTNUM(49) xbox::void_xt DECLSPEC_NORETURN NTAPI xbox::HalReturnToFi
|
|||
#endif
|
||||
}
|
||||
|
||||
// Save lightgun laser status
|
||||
for (int port = PORT_1; port < XBOX_NUM_PORTS; ++port) {
|
||||
if (g_devs[port].type == XBOX_INPUT_DEVICE::LIGHTGUN) {
|
||||
g_EmuShared->SetLightgunLaser(&g_devs[port].info.ligthgun.laser, port);
|
||||
}
|
||||
}
|
||||
|
||||
std::string TitlePath = xbox::LaunchDataPage->Header.szLaunchPath;
|
||||
|
||||
|
|
|
@ -26,9 +26,11 @@
|
|||
// ******************************************************************
|
||||
|
||||
#include "windows.h"
|
||||
#include "controllers/DlgDukeControllerConfig.h"
|
||||
#include "controllers/DlgSBControllerConfig.h"
|
||||
#include "controllers/DlgLibusbControllerConfig.h"
|
||||
#include "input/DlgDukeControllerConfig.h"
|
||||
#include "input/DlgSBControllerConfig.h"
|
||||
#include "input/DlgLibusbControllerConfig.h"
|
||||
#include "input/DlgLightgunConfig.h"
|
||||
#include "DlgInputConfig.h"
|
||||
#include "resource/ResCxbx.h"
|
||||
#include "input\InputManager.h"
|
||||
#include "Logging.h"
|
||||
|
@ -211,6 +213,12 @@ INT_PTR CALLBACK DlgInputConfigProc(HWND hWndDlg, UINT uMsg, WPARAM wParam, LPAR
|
|||
}
|
||||
break;
|
||||
|
||||
case to_underlying(XBOX_INPUT_DEVICE::LIGHTGUN): {
|
||||
DialogBoxParam(GetModuleHandle(nullptr), MAKEINTRESOURCE(IDD_LIGHTGUN_CFG), hWndDlg, DlgLightgunConfigProc,
|
||||
(DeviceType << 8) | port);
|
||||
}
|
||||
break;
|
||||
|
||||
case to_underlying(XBOX_INPUT_DEVICE::STEEL_BATTALION_CONTROLLER): {
|
||||
DialogBoxParam(GetModuleHandle(nullptr), MAKEINTRESOURCE(IDD_SBC_CFG), hWndDlg, DlgSBControllerConfigProc,
|
||||
(DeviceType << 8) | port);
|
||||
|
|
|
@ -181,22 +181,6 @@ void DukeInputWindow::BindDefault()
|
|||
}
|
||||
}
|
||||
|
||||
int DukeInputWindow::EnableDefaultButton()
|
||||
{
|
||||
if (std::strncmp(m_host_dev.c_str(), "XInput", std::strlen("XInput")) == 0) {
|
||||
EnableWindow(m_hwnd_default, TRUE);
|
||||
return XINPUT_DEFAULT;
|
||||
}
|
||||
else if (std::strncmp(m_host_dev.c_str(), "DInput", std::strlen("DInput")) == 0) {
|
||||
EnableWindow(m_hwnd_default, TRUE);
|
||||
return DINPUT_DEFAULT;
|
||||
}
|
||||
else {
|
||||
EnableWindow(m_hwnd_default, FALSE);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
void DukeInputWindow::ClearBindings()
|
||||
{
|
||||
m_DeviceConfig->ClearButtons();
|
|
@ -72,11 +72,6 @@ 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
|
|
@ -0,0 +1,223 @@
|
|||
// 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 "gui\DlgInputConfig.h"
|
||||
#include "common\Logging.h"
|
||||
|
||||
|
||||
static constexpr std::array<std::array<const char *, LIGHTGUN_NUM_BUTTONS>, 2> button_lightgun_default = { {
|
||||
{ "Pad N", "Pad S", "Pad W", "Pad E", "Start", "Back", "Button A", "Button B", "Button X", "Button Y", "Left X+", "Left X-",
|
||||
"Left Y+", "Left Y-", "Shoulder L", "Shoulder R", "Thumb R" },
|
||||
{ "UP", "DOWN", "LEFT", "RIGHT", "RETURN", "SPACE", "Click 0", "Click 1", "W", "E", "Cursor X+", "Cursor X-", "Cursor Y+", "Cursor Y-", "S", "D", "C"}
|
||||
} };
|
||||
|
||||
static LightgunInputWindow *g_InputWindow = nullptr;
|
||||
|
||||
LightgunInputWindow::~LightgunInputWindow()
|
||||
{
|
||||
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 LightgunInputWindow::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_DEVICE_LIST);
|
||||
m_hwnd_profile_list = GetDlgItem(m_hwnd_window, IDC_PROFILE_NAME);
|
||||
m_hwnd_default = GetDlgItem(m_hwnd_window, IDC_DEFAULT);
|
||||
m_dev_type = dev_type;
|
||||
m_max_num_buttons = dev_num_buttons[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)));
|
||||
|
||||
// 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()));
|
||||
|
||||
// Set the maximum profile name lenght the user can enter in the profile combobox
|
||||
SendMessage(m_hwnd_profile_list, CB_LIMITTEXT, 49, 0);
|
||||
|
||||
// construct emu device
|
||||
m_DeviceConfig = new EmuDevice(m_dev_type, m_hwnd_window, this);
|
||||
|
||||
// Enumerate devices
|
||||
UpdateDeviceList();
|
||||
|
||||
// Load currently saved profile for this port/device type
|
||||
LoadDefaultProfile();
|
||||
|
||||
// Load currently selected host device
|
||||
UpdateCurrentDevice();
|
||||
|
||||
// Install the subclass for the profile combobox
|
||||
SetWindowSubclass(GetWindow(m_hwnd_profile_list, GW_CHILD), ProfileNameSubclassProc, 0, 0);
|
||||
}
|
||||
|
||||
void LightgunInputWindow::ClearBindings()
|
||||
{
|
||||
m_DeviceConfig->ClearButtons();
|
||||
m_bHasChanges = true;
|
||||
}
|
||||
|
||||
void LightgunInputWindow::BindDefault()
|
||||
{
|
||||
int api = EnableDefaultButton();
|
||||
if (api != -1) {
|
||||
m_DeviceConfig->BindDefault<LIGHTGUN_NUM_BUTTONS>(button_lightgun_default[api]);
|
||||
m_bHasChanges = true;
|
||||
}
|
||||
}
|
||||
|
||||
INT_PTR CALLBACK DlgLightgunConfigProc(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::LIGHTGUN));
|
||||
|
||||
g_InputWindow = new LightgunInputWindow;
|
||||
g_InputWindow->Initialize(hWndDlg, port_num, dev_type);
|
||||
}
|
||||
break;
|
||||
|
||||
case WM_CLOSE:
|
||||
{
|
||||
if (g_InputWindow->IsProfileSaved()) {
|
||||
delete g_InputWindow;
|
||||
g_InputWindow = nullptr;
|
||||
EndDialog(hWndDlg, wParam);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case WM_COMMAND:
|
||||
{
|
||||
switch (LOWORD(wParam))
|
||||
{
|
||||
case IDC_DEVICE_LIST: {
|
||||
if (HIWORD(wParam) == CBN_SELCHANGE) {
|
||||
g_InputWindow->UpdateCurrentDevice();
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case IDC_PROFILE_NAME: {
|
||||
if (HIWORD(wParam) == CBN_SELCHANGE) {
|
||||
char name[50];
|
||||
HWND hwnd = GetDlgItem(hWndDlg, IDC_PROFILE_NAME);
|
||||
LRESULT str_idx = SendMessage(hwnd, CB_GETCURSEL, 0, 0);
|
||||
if (str_idx != CB_ERR) {
|
||||
SendMessage(hwnd, CB_GETLBTEXT, str_idx, reinterpret_cast<LPARAM>(name));
|
||||
g_InputWindow->UpdateProfile(std::string(name), PROFILE_LOAD);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case IDC_PROFILE_SAVE:
|
||||
case IDC_PROFILE_DELETE: {
|
||||
if (HIWORD(wParam) == BN_CLICKED) {
|
||||
char name[50];
|
||||
SendMessage(GetDlgItem(hWndDlg, IDC_PROFILE_NAME), WM_GETTEXT,
|
||||
sizeof(name), reinterpret_cast<LPARAM>(name));
|
||||
g_InputWindow->UpdateProfile(std::string(name), (LOWORD(wParam) == IDC_PROFILE_SAVE) ? PROFILE_SAVE : PROFILE_DELETE);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case IDC_DEFAULT: {
|
||||
if (HIWORD(wParam) == BN_CLICKED) {
|
||||
g_InputWindow->BindDefault();
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case IDC_CLEAR: {
|
||||
if (HIWORD(wParam) == BN_CLICKED) {
|
||||
if (PopupQuestionEx(hWndDlg, LOG_LEVEL::WARNING, PopupButtons::YesNo, PopupReturn::No, "Are you sure you want to remove all button bindings?") == PopupReturn::Yes) {
|
||||
g_InputWindow->ClearBindings();
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case IDC_REFRESH_DEVICES: {
|
||||
if (HIWORD(wParam) == BN_CLICKED) {
|
||||
g_InputWindow->UpdateDeviceList();
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case IDC_LG_STICK_UP:
|
||||
case IDC_LG_STICK_DOWN:
|
||||
case IDC_LG_STICK_LEFT:
|
||||
case IDC_LG_STICK_RIGHT:
|
||||
case IDC_LG_START:
|
||||
case IDC_LG_SEBA:
|
||||
case IDC_LG_TRIGGER:
|
||||
case IDC_LG_GRIP:
|
||||
case IDC_LG_A:
|
||||
case IDC_LG_B:
|
||||
case IDC_LG_AIM_POSX:
|
||||
case IDC_LG_AIM_NEGX:
|
||||
case IDC_LG_AIM_POSY:
|
||||
case IDC_LG_AIM_NEGY:
|
||||
case IDC_TURBO_LEFT:
|
||||
case IDC_TURBO_RIGHT:
|
||||
case IDC_LASER: {
|
||||
if (HIWORD(wParam) == BN_CLICKED) {
|
||||
g_InputWindow->BindButton(LOWORD(wParam), true);
|
||||
}
|
||||
}
|
||||
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 DlgLightgunConfigProc(HWND hWndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
|
|
@ -36,6 +36,12 @@
|
|||
|
||||
static SbcInputWindow *g_InputWindow = nullptr;
|
||||
|
||||
SbcInputWindow::~SbcInputWindow()
|
||||
{
|
||||
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 SbcInputWindow::Initialize(HWND hwnd, int port_num, int dev_type)
|
||||
{
|
||||
// Save window/device specific variables
|
||||
|
@ -88,13 +94,6 @@ int SbcInputWindow::EnableDefaultButton()
|
|||
return -1;
|
||||
}
|
||||
|
||||
void SbcInputWindow::SaveSlotConfig()
|
||||
{
|
||||
for (unsigned slot = 0; slot < XBOX_CTRL_NUM_SLOTS; ++slot) {
|
||||
g_Settings->m_input_port[m_port_num].SlotType[slot] = to_underlying(XBOX_INPUT_DEVICE::DEVICE_INVALID);
|
||||
}
|
||||
}
|
||||
|
||||
INT_PTR CALLBACK DlgSBControllerConfigProc(HWND hWndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
switch (uMsg)
|
||||
|
@ -118,7 +117,6 @@ INT_PTR CALLBACK DlgSBControllerConfigProc(HWND hWndDlg, UINT uMsg, WPARAM wPara
|
|||
case WM_CLOSE:
|
||||
{
|
||||
if (g_InputWindow->IsProfileSaved()) {
|
||||
g_InputWindow->SaveSlotConfig();
|
||||
delete g_InputWindow;
|
||||
g_InputWindow = nullptr;
|
||||
EndDialog(hWndDlg, wParam);
|
|
@ -36,6 +36,10 @@ BEGIN
|
|||
BEGIN
|
||||
END
|
||||
|
||||
IDD_LIGHTGUN_CFG, DIALOG
|
||||
BEGIN
|
||||
END
|
||||
|
||||
IDD_SBC_CFG, DIALOG
|
||||
BEGIN
|
||||
BOTTOMMARGIN, 269
|
||||
|
@ -202,6 +206,60 @@ BEGIN
|
|||
PUSHBUTTON "Test",IDC_RUMBLE_TEST,95,11,45,14,BS_FLAT
|
||||
END
|
||||
|
||||
IDD_LIGHTGUN_CFG DIALOGEX 0, 0, 529, 225
|
||||
STYLE DS_SETFONT | DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU
|
||||
FONT 8, "Verdana", 0, 0, 0x1
|
||||
BEGIN
|
||||
GROUPBOX "Device",IDC_DEVICE,12,10,175,35,WS_GROUP
|
||||
COMBOBOX IDC_DEVICE_LIST,21,23,101,15,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
|
||||
PUSHBUTTON "Refresh",IDC_REFRESH_DEVICES,128,23,50,14,BS_FLAT
|
||||
GROUPBOX "Profile",IDC_PROFILE,197,10,320,35,WS_GROUP
|
||||
COMBOBOX IDC_PROFILE_NAME,207,24,194,10,CBS_DROPDOWN | WS_VSCROLL | WS_TABSTOP
|
||||
PUSHBUTTON "Save",IDC_PROFILE_SAVE,405,23,50,14,BS_FLAT
|
||||
PUSHBUTTON "Delete",IDC_PROFILE_DELETE,459,23,50,14,BS_FLAT
|
||||
GROUPBOX "Buttons",IDC_BUTTONS,12,65,121,121,WS_GROUP
|
||||
PUSHBUTTON "",IDC_LG_A,59,110,57,14,BS_FLAT
|
||||
PUSHBUTTON "",IDC_LG_B,59,127,57,14,BS_FLAT
|
||||
PUSHBUTTON "",IDC_LG_TRIGGER,59,76,57,14,BS_FLAT
|
||||
PUSHBUTTON "",IDC_LG_GRIP,59,93,57,14,BS_FLAT
|
||||
PUSHBUTTON "",IDC_LG_SEBA,59,144,57,14,BS_FLAT
|
||||
PUSHBUTTON "",IDC_LG_START,59,161,57,14,BS_FLAT
|
||||
LTEXT "A",IDC_STATIC,28,110,20,14,SS_CENTERIMAGE
|
||||
LTEXT "B",IDC_STATIC,28,127,20,14,SS_CENTERIMAGE
|
||||
LTEXT "Trigger",IDC_STATIC,28,76,27,14,SS_CENTERIMAGE
|
||||
LTEXT "Grip",IDC_STATIC,28,93,20,14,SS_CENTERIMAGE
|
||||
LTEXT "SE/BA",IDC_STATIC,28,144,25,14,SS_CENTERIMAGE
|
||||
LTEXT "START",IDC_STATIC,28,161,27,14,SS_CENTERIMAGE
|
||||
GROUPBOX "Aim",IDC_LSTICK,140,65,121,121,WS_GROUP
|
||||
PUSHBUTTON "",IDC_LG_AIM_POSY,187,75,57,14,BS_FLAT
|
||||
PUSHBUTTON "",IDC_LG_AIM_NEGY,187,93,57,14,BS_FLAT
|
||||
PUSHBUTTON "",IDC_LG_AIM_NEGX,187,111,57,14,BS_FLAT
|
||||
PUSHBUTTON "",IDC_LG_AIM_POSX,187,129,57,14,BS_FLAT
|
||||
LTEXT "Up",IDC_STATIC,156,75,20,14,SS_CENTERIMAGE
|
||||
LTEXT "Down",IDC_STATIC,156,93,20,14,SS_CENTERIMAGE
|
||||
LTEXT "Left",IDC_STATIC,156,111,20,14,SS_CENTERIMAGE
|
||||
LTEXT "Right",IDC_STATIC,156,129,20,14,SS_CENTERIMAGE
|
||||
GROUPBOX "Stick",IDC_DPAD,268,65,121,121,WS_GROUP
|
||||
PUSHBUTTON "",IDC_LG_STICK_UP,315,75,57,14,BS_FLAT
|
||||
PUSHBUTTON "",IDC_LG_STICK_DOWN,315,93,57,14,BS_FLAT
|
||||
PUSHBUTTON "",IDC_LG_STICK_LEFT,315,111,57,14,BS_FLAT
|
||||
PUSHBUTTON "",IDC_LG_STICK_RIGHT,315,129,57,14,BS_FLAT
|
||||
LTEXT "Up",IDC_STATIC,285,75,20,14,SS_CENTERIMAGE
|
||||
LTEXT "Down",IDC_STATIC,285,93,20,14,SS_CENTERIMAGE
|
||||
LTEXT "Left",IDC_STATIC,285,111,20,14,SS_CENTERIMAGE
|
||||
LTEXT "Right",IDC_STATIC,285,129,20,14,SS_CENTERIMAGE
|
||||
PUSHBUTTON "Default Bindings",IDC_DEFAULT,362,200,69,14,BS_FLAT
|
||||
PUSHBUTTON "Clear",IDC_CLEAR,443,200,50,14,BS_FLAT
|
||||
GROUPBOX "Turbo switch",IDC_POWER_SWITCH,396,66,121,52,WS_GROUP
|
||||
PUSHBUTTON "",IDC_TURBO_LEFT,443,76,57,14,BS_FLAT
|
||||
PUSHBUTTON "",IDC_TURBO_RIGHT,443,94,57,14,BS_FLAT
|
||||
LTEXT "Left",IDC_STATIC,412,76,20,14,SS_CENTERIMAGE
|
||||
LTEXT "Right",IDC_STATIC,412,94,20,14,SS_CENTERIMAGE
|
||||
GROUPBOX "Power switch",IDC_TURBO,396,121,121,66,WS_GROUP
|
||||
PUSHBUTTON "",IDC_LASER,443,132,57,14,BS_FLAT
|
||||
LTEXT "Laser",IDC_STATIC,412,132,20,14,SS_CENTERIMAGE
|
||||
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
|
||||
|
@ -590,6 +648,11 @@ BEGIN
|
|||
0
|
||||
END
|
||||
|
||||
IDD_LIGHTGUN_CFG AFX_DIALOG_LAYOUT
|
||||
BEGIN
|
||||
0
|
||||
END
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#define IDD_RUMBLE_CFG 135
|
||||
#define IDD_NETWORK_CFG 136
|
||||
#define IDD_LIBUSB_CFG 137
|
||||
#define IDD_LIGHTGUN_CFG 138
|
||||
#define IDC_LOG_CANCEL 892
|
||||
#define IDC_LOG_ACCEPT 893
|
||||
#define IDC_LOG_ENABLE_GENERAL 894
|
||||
|
@ -275,6 +276,25 @@
|
|||
#define IDC_SIGHT_CHANGE_POSX 1322
|
||||
#define IDC_LIBUSB_LIST 1323
|
||||
#define IDC_LIBUSB_TEST 1324
|
||||
#define IDC_LG_A 1325
|
||||
#define IDC_LG_B 1326
|
||||
#define IDC_LG_TRIGGER 1327
|
||||
#define IDC_LG_GRIP 1328
|
||||
#define IDC_LG_SEBA 1329
|
||||
#define IDC_LG_START 1330
|
||||
#define IDC_LG_AIM_POSY 1331
|
||||
#define IDC_LG_AIM_NEGY 1332
|
||||
#define IDC_LG_AIM_NEGX 1333
|
||||
#define IDC_LG_AIM_POSX 1334
|
||||
#define IDC_LG_STICK_UP 1335
|
||||
#define IDC_LG_STICK_DOWN 1336
|
||||
#define IDC_LG_STICK_LEFT 1337
|
||||
#define IDC_LG_STICK_RIGHT 1338
|
||||
#define IDC_TURBO_LEFT 1339
|
||||
#define IDC_TURBO_RIGHT 1340
|
||||
#define IDC_TURBO 1341
|
||||
#define IDC_POWER_SWITCH 1342
|
||||
#define IDC_LASER 1343
|
||||
#define ID_FILE_EXIT 40005
|
||||
#define ID_HELP_ABOUT 40008
|
||||
#define ID_EMULATION_START 40009
|
||||
|
@ -340,7 +360,7 @@
|
|||
//
|
||||
#ifdef APSTUDIO_INVOKED
|
||||
#ifndef APSTUDIO_READONLY_SYMBOLS
|
||||
#define _APS_NEXT_RESOURCE_VALUE 137
|
||||
#define _APS_NEXT_RESOURCE_VALUE 139
|
||||
#define _APS_NEXT_COMMAND_VALUE 40117
|
||||
#define _APS_NEXT_CONTROL_VALUE 1308
|
||||
#define _APS_NEXT_SYMED_VALUE 109
|
||||
|
|
Loading…
Reference in New Issue