[HID] file-pad: implement GetKeystroke -- menus do not read GetState
Some checks failed
Orchestrator / Commit Message Validation (push) Has been skipped
Orchestrator / Lint (push) Failing after 2m3s
Orchestrator / Windows (x86-64) (push) Has been skipped
Orchestrator / Linux (x86-64) (push) Has been skipped
Orchestrator / Create Release (push) Has been skipped
Some checks failed
Orchestrator / Commit Message Validation (push) Has been skipped
Orchestrator / Lint (push) Failing after 2m3s
Orchestrator / Windows (x86-64) (push) Has been skipped
Orchestrator / Linux (x86-64) (push) Has been skipped
Orchestrator / Create Release (push) Has been skipped
The pad looked correct and did nothing. Its own log showed A arriving, the emulator sat on "PRESS (A) BUTTON", and the title never advanced. Cause: 360 front-ends poll XamInputGetKeystrokeEx, not XamInputGetState. This title imports both, and its menus use the keystroke path; the driver returned X_ERROR_EMPTY there, so every scripted press went into the void while GetState faithfully reported a button nobody asked about. Implement it edge-triggered, one event per call: KEYUPs for everything released first, then KEYDOWNs, matching the SDL driver's ordering (so a thumb transition clears before it sets). Deliberately NO auto-repeat -- scripted input wants exactly one event per press, and repeat is precisely what makes menu steps overshoot. Bits without a virtual key (guide, unused) are swallowed rather than re-offered forever. Verified on the real game: title -> main menu -> EXTRAS driven entirely from the pad file, with `[file-pad] keystroke vk=5800 down/up` in the log for each press.
This commit is contained in:
@@ -49,6 +49,7 @@
|
|||||||
#include "xenia/base/cvar.h"
|
#include "xenia/base/cvar.h"
|
||||||
#include "xenia/base/logging.h"
|
#include "xenia/base/logging.h"
|
||||||
#include "xenia/hid/input_driver.h"
|
#include "xenia/hid/input_driver.h"
|
||||||
|
#include "xenia/ui/virtual_key.h"
|
||||||
|
|
||||||
DECLARE_string(pad_file);
|
DECLARE_string(pad_file);
|
||||||
|
|
||||||
@@ -129,8 +130,79 @@ class FileInputDriver final : public InputDriver {
|
|||||||
return user_index == 0 ? X_ERROR_SUCCESS : X_ERROR_DEVICE_NOT_CONNECTED;
|
return user_index == 0 ? X_ERROR_SUCCESS : X_ERROR_DEVICE_NOT_CONNECTED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Menus do NOT read the pad through GetState. "PRESS (A) BUTTON" and most
|
||||||
|
// 360 front-ends poll XamInputGetKeystrokeEx, so a driver that only answers
|
||||||
|
// GetState looks completely dead on a title screen while its own log happily
|
||||||
|
// shows the button arriving. Returning X_ERROR_EMPTY here is what made the
|
||||||
|
// first scripted run press A into the void.
|
||||||
|
//
|
||||||
|
// One event per call, edge triggered: KEYUPs for everything released, then
|
||||||
|
// KEYDOWNs for everything pressed, exactly as the SDL driver orders them.
|
||||||
|
// Deliberately NO auto-repeat — scripted input wants precisely one event per
|
||||||
|
// press, and repeat is what makes menu steps overshoot.
|
||||||
X_RESULT GetKeystroke(uint32_t user_index, uint32_t flags,
|
X_RESULT GetKeystroke(uint32_t user_index, uint32_t flags,
|
||||||
X_INPUT_KEYSTROKE* out_keystroke) override {
|
X_INPUT_KEYSTROKE* out_keystroke) override {
|
||||||
|
const bool user_any = user_index == 0xFF || user_index == 0xFFFFFFFFu;
|
||||||
|
if (!user_any && user_index != 0) {
|
||||||
|
return X_ERROR_DEVICE_NOT_CONNECTED;
|
||||||
|
}
|
||||||
|
if (!out_keystroke) {
|
||||||
|
return X_ERROR_BAD_ARGUMENTS;
|
||||||
|
}
|
||||||
|
Refresh();
|
||||||
|
|
||||||
|
// Bit index in X_INPUT_GAMEPAD::buttons -> virtual key. Order matters: it is
|
||||||
|
// the order multiple simultaneous changes are reported in.
|
||||||
|
static constexpr uint16_t kVk[16] = {
|
||||||
|
uint16_t(ui::VirtualKey::kXInputPadDpadUp),
|
||||||
|
uint16_t(ui::VirtualKey::kXInputPadDpadDown),
|
||||||
|
uint16_t(ui::VirtualKey::kXInputPadDpadLeft),
|
||||||
|
uint16_t(ui::VirtualKey::kXInputPadDpadRight),
|
||||||
|
uint16_t(ui::VirtualKey::kXInputPadStart),
|
||||||
|
uint16_t(ui::VirtualKey::kXInputPadBack),
|
||||||
|
uint16_t(ui::VirtualKey::kXInputPadLThumbPress),
|
||||||
|
uint16_t(ui::VirtualKey::kXInputPadRThumbPress),
|
||||||
|
uint16_t(ui::VirtualKey::kXInputPadLShoulder),
|
||||||
|
uint16_t(ui::VirtualKey::kXInputPadRShoulder),
|
||||||
|
0, /* guide */
|
||||||
|
0, /* unused */
|
||||||
|
uint16_t(ui::VirtualKey::kXInputPadA),
|
||||||
|
uint16_t(ui::VirtualKey::kXInputPadB),
|
||||||
|
uint16_t(ui::VirtualKey::kXInputPadX),
|
||||||
|
uint16_t(ui::VirtualKey::kXInputPadY),
|
||||||
|
};
|
||||||
|
|
||||||
|
const uint16_t changed = static_cast<uint16_t>(buttons_ ^ reported_);
|
||||||
|
if (!changed) {
|
||||||
|
return X_ERROR_EMPTY;
|
||||||
|
}
|
||||||
|
for (int pass = 0; pass < 2; ++pass) {
|
||||||
|
const bool clear_pass = pass == 0;
|
||||||
|
for (uint8_t i = 0; i < 16; ++i) {
|
||||||
|
const uint16_t bit = static_cast<uint16_t>(1u << i);
|
||||||
|
if (!(changed & bit) || kVk[i] == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const bool pressed = (buttons_ & bit) != 0;
|
||||||
|
if (clear_pass == pressed) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
reported_ = static_cast<uint16_t>(pressed ? (reported_ | bit)
|
||||||
|
: (reported_ & ~bit));
|
||||||
|
out_keystroke->virtual_key = kVk[i];
|
||||||
|
out_keystroke->unicode = 0;
|
||||||
|
out_keystroke->flags =
|
||||||
|
pressed ? X_INPUT_KEYSTROKE_KEYDOWN : X_INPUT_KEYSTROKE_KEYUP;
|
||||||
|
out_keystroke->user_index = 0;
|
||||||
|
out_keystroke->hid_code = 0;
|
||||||
|
XELOGI("[file-pad] keystroke vk={:04X} {}", kVk[i],
|
||||||
|
pressed ? "down" : "up");
|
||||||
|
return X_ERROR_SUCCESS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Only bits without a virtual key changed (guide/unused): swallow them so
|
||||||
|
// the caller is not asked again forever.
|
||||||
|
reported_ = buttons_;
|
||||||
return X_ERROR_EMPTY;
|
return X_ERROR_EMPTY;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -249,6 +321,8 @@ class FileInputDriver final : public InputDriver {
|
|||||||
uint8_t lt_ = 0, rt_ = 0;
|
uint8_t lt_ = 0, rt_ = 0;
|
||||||
int16_t lx_ = 0, ly_ = 0, rx_ = 0, ry_ = 0;
|
int16_t lx_ = 0, ly_ = 0, rx_ = 0, ry_ = 0;
|
||||||
uint32_t packet_ = 1;
|
uint32_t packet_ = 1;
|
||||||
|
// Buttons already reported through GetKeystroke; the edge detector's memory.
|
||||||
|
uint16_t reported_ = 0;
|
||||||
bool present_ = false;
|
bool present_ = false;
|
||||||
time_t mtime_sec_ = 0;
|
time_t mtime_sec_ = 0;
|
||||||
long mtime_nsec_ = -1;
|
long mtime_nsec_ = -1;
|
||||||
|
|||||||
Reference in New Issue
Block a user