Add support for mouse input as cursor position relative to the rendering window
This commit is contained in:
parent
bf6d8b804f
commit
240dc03dd5
|
@ -947,12 +947,9 @@ void Settings::RemoveLegacyConfigs(unsigned int CurrentRevision)
|
|||
std::string current_section = std::string(section_input_port) + std::to_string(port_num);
|
||||
std::string device_name = m_si.GetValue(current_section.c_str(), sect_input_port.device, "");
|
||||
|
||||
// NOTE: with C++20, this can be simplified by simply calling device_name.ends_with()
|
||||
if (device_name.length() >= kb_str.length()) {
|
||||
if (device_name.compare(device_name.length() - kb_str.length(), kb_str.length(), kb_str) == 0) {
|
||||
device_name += "Mouse";
|
||||
m_si.SetValue(current_section.c_str(), sect_input_port.device, device_name.c_str(), nullptr, true);
|
||||
}
|
||||
if (StrEndsWith(device_name, kb_str)) {
|
||||
device_name += "Mouse";
|
||||
m_si.SetValue(current_section.c_str(), sect_input_port.device, device_name.c_str(), nullptr, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -63,10 +63,16 @@ LRESULT CALLBACK ButtonDukeSubclassProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPA
|
|||
|
||||
case WM_RBUTTONDOWN: {
|
||||
Button *button = reinterpret_cast<Button *>(dwRefData);
|
||||
button->ClearText();
|
||||
static_cast<DukeInputWindow *>(button->GetWnd())->UpdateProfile(std::string(), BUTTON_CLEAR);
|
||||
if (button->GetId() == IDC_SET_MOTOR) {
|
||||
static_cast<DukeInputWindow *>(button->GetWnd())->UpdateProfile(std::string(), RUMBLE_CLEAR);
|
||||
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();
|
||||
static_cast<DukeInputWindow *>(button->GetWnd())->UpdateProfile(std::string(), BUTTON_CLEAR);
|
||||
if (button->GetId() == IDC_SET_MOTOR) {
|
||||
static_cast<DukeInputWindow *>(button->GetWnd())->UpdateProfile(std::string(), RUMBLE_CLEAR);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
@ -88,8 +94,14 @@ LRESULT CALLBACK ButtonSbcSubclassProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPAR
|
|||
|
||||
case WM_RBUTTONDOWN: {
|
||||
Button *button = reinterpret_cast<Button *>(dwRefData);
|
||||
button->ClearText();
|
||||
static_cast<SbcInputWindow *>(button->GetWnd())->UpdateProfile(std::string(), BUTTON_CLEAR);
|
||||
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();
|
||||
static_cast<SbcInputWindow *>(button->GetWnd())->UpdateProfile(std::string(), BUTTON_CLEAR);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
|
|
|
@ -137,6 +137,12 @@ namespace DInput
|
|||
AddInput(new Axis(i, ax, (2 == i) ? mo_wheel_range_neg : mo_axis_range_neg));
|
||||
AddInput(new Axis(i, ax, (2 == i) ? mo_wheel_range_pos : mo_axis_range_pos));
|
||||
}
|
||||
|
||||
// mouse cursor
|
||||
AddInput(new Cursor(0, m_state_in.cursor.x, -1.0));
|
||||
AddInput(new Cursor(0, m_state_in.cursor.x, 1.0));
|
||||
AddInput(new Cursor(1, m_state_in.cursor.y, -1.0));
|
||||
AddInput(new Cursor(1, m_state_in.cursor.y, 1.0));
|
||||
}
|
||||
|
||||
KeyboardMouse::~KeyboardMouse()
|
||||
|
@ -178,6 +184,7 @@ namespace DInput
|
|||
}
|
||||
|
||||
std::memcpy(m_state_in.mouse.rgbButtons, tmp_mouse.rgbButtons, sizeof(m_state_in.mouse.rgbButtons));
|
||||
UpdateCursorPosition();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -185,6 +192,21 @@ namespace DInput
|
|||
return false;
|
||||
}
|
||||
|
||||
void KeyboardMouse::UpdateCursorPosition()
|
||||
{
|
||||
POINT point = {};
|
||||
GetCursorPos(&point);
|
||||
ScreenToClient(m_hwnd, &point);
|
||||
RECT rect;
|
||||
GetClientRect(m_hwnd, &rect);
|
||||
const auto width = std::max(rect.right - rect.left, 1l);
|
||||
const auto height = std::max(rect.bottom - rect.top, 1l);
|
||||
|
||||
// Convert the window client coordinates to normalized device coordinates
|
||||
m_state_in.cursor.x = ((2 * static_cast<ControlState>(point.x)) / width) - 1;
|
||||
m_state_in.cursor.y = ((-2 * static_cast<ControlState>(point.y)) / height) + 1;
|
||||
}
|
||||
|
||||
std::string KeyboardMouse::GetDeviceName() const
|
||||
{
|
||||
return "KeyboardMouse";
|
||||
|
@ -213,6 +235,14 @@ namespace DInput
|
|||
return tmpstr;
|
||||
}
|
||||
|
||||
std::string KeyboardMouse::Cursor::GetName() const
|
||||
{
|
||||
static char tmpstr[] = "Cursor ..";
|
||||
tmpstr[7] = (char)('X' + m_index);
|
||||
tmpstr[8] = (m_range == 1.0 ? '+' : '-');
|
||||
return tmpstr;
|
||||
}
|
||||
|
||||
ControlState KeyboardMouse::Key::GetState() const
|
||||
{
|
||||
return (m_key != 0);
|
||||
|
@ -227,4 +257,9 @@ namespace DInput
|
|||
{
|
||||
return std::clamp(ControlState(m_axis) / m_range, 0.0, 1.0);
|
||||
}
|
||||
|
||||
ControlState KeyboardMouse::Cursor::GetState() const
|
||||
{
|
||||
return std::max(0.0, m_cursor / m_range);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -51,6 +51,7 @@ namespace DInput
|
|||
std::string GetDeviceName() const override;
|
||||
std::string GetAPI() const override;
|
||||
bool UpdateInput() override;
|
||||
void SetHwnd(HWND hwnd) { m_hwnd = hwnd; }
|
||||
|
||||
|
||||
private:
|
||||
|
@ -78,6 +79,7 @@ namespace DInput
|
|||
const uint8_t m_index;
|
||||
};
|
||||
|
||||
// gives mouse input based on relative motion
|
||||
class Axis : public Input
|
||||
{
|
||||
public:
|
||||
|
@ -91,14 +93,37 @@ namespace DInput
|
|||
const uint8_t m_index;
|
||||
};
|
||||
|
||||
// gives mouse input based on cursor position relative to the rendering window
|
||||
class Cursor : public Input
|
||||
{
|
||||
public:
|
||||
Cursor(uint8_t index, const ControlState &cursor, const ControlState range)
|
||||
: m_cursor(cursor), m_index(index), m_range(range) {}
|
||||
std::string GetName() const override;
|
||||
ControlState GetState() const override;
|
||||
bool IsDetectable() const override { return false; }
|
||||
|
||||
private:
|
||||
const ControlState &m_cursor;
|
||||
const ControlState m_range;
|
||||
const uint8_t m_index;
|
||||
};
|
||||
|
||||
struct State
|
||||
{
|
||||
BYTE keyboard[256];
|
||||
DIMOUSESTATE2 mouse;
|
||||
struct
|
||||
{
|
||||
ControlState x, y;
|
||||
} cursor;
|
||||
};
|
||||
|
||||
void UpdateCursorPosition();
|
||||
|
||||
const LPDIRECTINPUTDEVICE8 m_kb_device;
|
||||
const LPDIRECTINPUTDEVICE8 m_mo_device;
|
||||
State m_state_in;
|
||||
HWND m_hwnd;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -93,6 +93,7 @@ public:
|
|||
{
|
||||
public:
|
||||
virtual ControlState GetState() const = 0;
|
||||
virtual bool IsDetectable() const { return true; };
|
||||
};
|
||||
|
||||
class Output : public IoControl
|
||||
|
|
|
@ -71,11 +71,12 @@ extern CXBX_CONTROLLER_HOST_BRIDGE g_XboxControllerHostBridge[4]; // hle xinput
|
|||
|
||||
InputDeviceManager g_InputDeviceManager;
|
||||
|
||||
void InputDeviceManager::Initialize(bool is_gui)
|
||||
void InputDeviceManager::Initialize(bool is_gui, HWND hwnd)
|
||||
{
|
||||
// Sdl::Init must be called last since it blocks when it succeeds
|
||||
std::unique_lock<std::mutex> lck(m_Mtx);
|
||||
m_bPendingShutdown = false;
|
||||
m_hwnd = hwnd;
|
||||
|
||||
m_PollingThread = std::thread([this, is_gui]() {
|
||||
XInput::Init(m_Mtx);
|
||||
|
@ -623,6 +624,12 @@ void InputDeviceManager::RefreshDevices()
|
|||
m_Cv.wait(lck, []() {
|
||||
return Sdl::SdlPopulateOK;
|
||||
});
|
||||
for (auto &dev : m_Devices) {
|
||||
if (StrStartsWith(dev->GetDeviceName(), "KeyboardMouse")) {
|
||||
static_cast<DInput::KeyboardMouse *>(dev.get())->SetHwnd(m_hwnd);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::string> InputDeviceManager::GetDeviceList() const
|
||||
|
|
|
@ -123,7 +123,7 @@ CXBX_CONTROLLER_HOST_BRIDGE, *PCXBX_CONTROLLER_HOST_BRIDGE;
|
|||
class InputDeviceManager
|
||||
{
|
||||
public:
|
||||
void Initialize(bool is_gui);
|
||||
void Initialize(bool is_gui, HWND hwnd);
|
||||
void Shutdown();
|
||||
// read/write the input/output from/to the device attached to the supplied xbox port
|
||||
bool UpdateXboxPortInput(int usb_port, void* Buffer, int Direction, int xid_type);
|
||||
|
@ -169,6 +169,8 @@ private:
|
|||
std::thread m_PollingThread;
|
||||
// used to indicate that the manager is shutting down
|
||||
bool m_bPendingShutdown;
|
||||
// handle of the rendering or the input gui window
|
||||
HWND m_hwnd;
|
||||
};
|
||||
|
||||
extern InputDeviceManager g_InputDeviceManager;
|
||||
|
|
|
@ -121,7 +121,7 @@ InputDevice::Input* InputWindow::DetectInput(InputDevice* const Device, int ms)
|
|||
Device->UpdateInput();
|
||||
std::vector<bool>::iterator state = initial_states.begin();
|
||||
for (; i != e && s == e; i++, state++) {
|
||||
if ((*i)->GetState() > INPUT_DETECT_THRESHOLD) {
|
||||
if ((*i)->IsDetectable() && ((*i)->GetState() > INPUT_DETECT_THRESHOLD)) {
|
||||
if (*state == false) {
|
||||
// input was not initially pressed or it was but released afterwards
|
||||
s = i;
|
||||
|
@ -290,3 +290,65 @@ void InputWindow::UpdateCurrentDevice()
|
|||
m_host_dev = device_name;
|
||||
EnableDefaultButton();
|
||||
}
|
||||
|
||||
void InputWindow::SwapMoCursorAxis(Button *button)
|
||||
{
|
||||
// Axis X+ <-> Cursor X+
|
||||
// Axis X- <-> Cursor X-
|
||||
// Axis Y+ <-> Cursor Y-
|
||||
// Axis Y- <-> Cursor Y+
|
||||
if (StrEndsWith(m_host_dev, "KeyboardMouse")) {
|
||||
assert(button != nullptr);
|
||||
char control_name[HOST_BUTTON_NAME_LENGTH];
|
||||
button->GetText(control_name, sizeof(control_name));
|
||||
if (StrStartsWith(control_name, "Axis")) {
|
||||
switch (control_name[5])
|
||||
{
|
||||
case 'X':
|
||||
if (control_name[6] == '+') {
|
||||
button->UpdateText("Cursor X+");
|
||||
}
|
||||
else {
|
||||
button->UpdateText("Cursor X-");
|
||||
}
|
||||
break;
|
||||
|
||||
case 'Y':
|
||||
if (control_name[6] == '+') {
|
||||
button->UpdateText("Cursor Y-");
|
||||
}
|
||||
else {
|
||||
button->UpdateText("Cursor Y+");
|
||||
}
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (StrStartsWith(control_name, "Cursor")) {
|
||||
switch (control_name[7])
|
||||
{
|
||||
case 'X':
|
||||
if (control_name[8] == '+') {
|
||||
button->UpdateText("Axis X+");
|
||||
}
|
||||
else {
|
||||
button->UpdateText("Axis X-");
|
||||
}
|
||||
break;
|
||||
|
||||
case 'Y':
|
||||
if (control_name[8] == '+') {
|
||||
button->UpdateText("Axis Y-");
|
||||
}
|
||||
else {
|
||||
button->UpdateText("Axis Y+");
|
||||
}
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -39,6 +39,7 @@
|
|||
#define RUMBLE_TEST 6
|
||||
#define RUMBLE_CLEAR 7
|
||||
#define BUTTON_CLEAR 8
|
||||
#define BUTTON_SWAP 9
|
||||
|
||||
#define XINPUT_DEFAULT 0
|
||||
#define DINPUT_DEFAULT 1
|
||||
|
@ -60,6 +61,7 @@ public:
|
|||
virtual void UpdateProfile(const std::string& name, int command) = 0;
|
||||
void UpdateCurrentDevice();
|
||||
bool IsProfileSaved();
|
||||
void SwapMoCursorAxis(Button *button);
|
||||
|
||||
|
||||
protected:
|
||||
|
|
|
@ -280,3 +280,27 @@ std::string StripQuotes(const std::string& str)
|
|||
{
|
||||
return StripChars(str, "\"");
|
||||
}
|
||||
|
||||
// NOTE: with C++20, this can be replaced by simply calling full_str.ends_with()
|
||||
bool StrEndsWith(const std::string &full_str, const std::string &substr)
|
||||
{
|
||||
if (full_str.length() >= substr.length()) {
|
||||
if (full_str.compare(full_str.length() - substr.length(), substr.length(), substr) == 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// NOTE: with C++20, this can be replaced by simply calling full_str.starts_with()
|
||||
bool StrStartsWith(const std::string &full_str, const std::string &substr)
|
||||
{
|
||||
if (!full_str.empty()) {
|
||||
if (full_str.rfind(substr, 0) == 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -67,6 +67,8 @@ bool Memory_RW(void* Addr, void* Buf, size_t Num, bool bIsWrite);
|
|||
void unix2dos(std::string& string);
|
||||
std::string StripSpaces(const std::string& str);
|
||||
std::string StripQuotes(const std::string& str);
|
||||
bool StrEndsWith(const std::string &full_str, const std::string &substr);
|
||||
bool StrStartsWith(const std::string &full_str, const std::string &substr);
|
||||
|
||||
// Retrieves the underlying integer value of a scoped enumerator. It allows to avoid using static_cast every time
|
||||
template <typename E>
|
||||
|
|
|
@ -1497,7 +1497,7 @@ __declspec(noreturn) void CxbxKrnlInit
|
|||
// Read Xbox video mode from the SMC, store it in HalBootSMCVideoMode
|
||||
xbox::HalReadSMBusValue(SMBUS_ADDRESS_SYSTEM_MICRO_CONTROLLER, SMC_COMMAND_AV_PACK, FALSE, (xbox::PULONG)&xbox::HalBootSMCVideoMode);
|
||||
|
||||
g_InputDeviceManager.Initialize(false);
|
||||
g_InputDeviceManager.Initialize(false, g_hEmuWindow);
|
||||
|
||||
// Now the hardware devices exist, couple the EEPROM buffer to it's device
|
||||
g_EEPROM->SetEEPROM((uint8_t*)EEPROM);
|
||||
|
|
|
@ -98,7 +98,7 @@ void UpdateInputOpt(HWND hwnd)
|
|||
|
||||
void ShowInputConfig(HWND hwnd, HWND ChildWnd)
|
||||
{
|
||||
g_InputDeviceManager.Initialize(true);
|
||||
g_InputDeviceManager.Initialize(true, hwnd);
|
||||
g_ChildWnd = ChildWnd;
|
||||
|
||||
// Show dialog box
|
||||
|
|
|
@ -196,7 +196,8 @@ void DukeInputWindow::UpdateProfile(const std::string &name, int command)
|
|||
}
|
||||
break;
|
||||
|
||||
case BUTTON_CLEAR: {
|
||||
case BUTTON_CLEAR:
|
||||
case BUTTON_SWAP: {
|
||||
m_bHasChanges = true;
|
||||
}
|
||||
break;
|
||||
|
|
|
@ -100,7 +100,8 @@ void SbcInputWindow::UpdateProfile(const std::string &name, int command)
|
|||
}
|
||||
break;
|
||||
|
||||
case BUTTON_CLEAR: {
|
||||
case BUTTON_CLEAR:
|
||||
case BUTTON_SWAP: {
|
||||
m_bHasChanges = true;
|
||||
}
|
||||
break;
|
||||
|
|
Loading…
Reference in New Issue