Files
spice2x.github.io/src/spice2x/games/iidx/poke.cpp
T
bicarus 5cabd026ae misc: various performance clean up (#844)
## Link to GitHub Issue or related Pull Request, if one exists
n/a

## Description of change

- Replace unnecessary precision timers with standard sleeps to reduce
wakeups and background CPU usage.
- Reuse buffers in raw input, touchscreen, and HID output paths to
eliminate steady-state allocations.
- Pre-index HID button groups and correctly process batched HID reports.

No functional changes.

## Testing
2026-07-28 21:05:32 -07:00

227 lines
9.9 KiB
C++

#include "poke.h"
#include <chrono>
#include <thread>
#include "windows.h"
#include "games/io.h"
#include "games/iidx/iidx.h"
#include "hooks/graphics/graphics.h"
#include "launcher/shutdown.h"
#include "misc/eamuse.h"
#include "touch/native/inject.h"
#include "touch/touch.h"
#include "util/logging.h"
namespace games::iidx::poke {
static std::thread *THREAD = nullptr;
static bool THREAD_RUNNING = false;
struct KeypadMapping {
char character;
uint16_t state;
};
static KeypadMapping KEYPAD_MAPPINGS[] = {
{ '0', 1 << EAM_IO_KEYPAD_0 },
{ '1', 1 << EAM_IO_KEYPAD_1 },
{ '2', 1 << EAM_IO_KEYPAD_2 },
{ '3', 1 << EAM_IO_KEYPAD_3 },
{ '4', 1 << EAM_IO_KEYPAD_4 },
{ '5', 1 << EAM_IO_KEYPAD_5 },
{ '6', 1 << EAM_IO_KEYPAD_6 },
{ '7', 1 << EAM_IO_KEYPAD_7 },
{ '8', 1 << EAM_IO_KEYPAD_8 },
{ '9', 1 << EAM_IO_KEYPAD_9 },
{ 'A', 1 << EAM_IO_KEYPAD_00 },
// Touch panel keypad does not have the decimal key.
// This will be used for toggling the keypad instead
{ 'D', 1 << EAM_IO_KEYPAD_DECIMAL },
};
const int IIDX_KEYPAD_BUTTON_SIZE = 90;
const int IIDX_KEYPAD_GAP = 15;
const int IIDX_KEYPAD_TOP = 570;
const int IIDX_KEYPAD_LEFT_1P = 90;
const int IIDX_KEYPAD_LEFT_2P = 1530;
// <KeypadID>/<Key> mapped to 1080p coordinate space
static const std::unordered_map<std::string, int> IIDX_KEYPAD_POSITION_X {
{"0/0", (int)(IIDX_KEYPAD_LEFT_1P + IIDX_KEYPAD_BUTTON_SIZE * 0.5)},
{"0/1", (int)(IIDX_KEYPAD_LEFT_1P + IIDX_KEYPAD_BUTTON_SIZE * 0.5)},
{"0/2", (int)(IIDX_KEYPAD_LEFT_1P + IIDX_KEYPAD_BUTTON_SIZE * 1.5 + IIDX_KEYPAD_GAP)},
{"0/3", (int)(IIDX_KEYPAD_LEFT_1P + IIDX_KEYPAD_BUTTON_SIZE * 2.5 + IIDX_KEYPAD_GAP * 2)},
{"0/4", (int)(IIDX_KEYPAD_LEFT_1P + IIDX_KEYPAD_BUTTON_SIZE * 0.5)},
{"0/5", (int)(IIDX_KEYPAD_LEFT_1P + IIDX_KEYPAD_BUTTON_SIZE * 1.5 + IIDX_KEYPAD_GAP)},
{"0/6", (int)(IIDX_KEYPAD_LEFT_1P + IIDX_KEYPAD_BUTTON_SIZE * 2.5 + IIDX_KEYPAD_GAP * 2)},
{"0/7", (int)(IIDX_KEYPAD_LEFT_1P + IIDX_KEYPAD_BUTTON_SIZE * 0.5)},
{"0/8", (int)(IIDX_KEYPAD_LEFT_1P + IIDX_KEYPAD_BUTTON_SIZE * 1.5 + IIDX_KEYPAD_GAP)},
{"0/9", (int)(IIDX_KEYPAD_LEFT_1P + IIDX_KEYPAD_BUTTON_SIZE * 2.5 + IIDX_KEYPAD_GAP * 2)},
{"0/A", (int)(IIDX_KEYPAD_LEFT_1P + IIDX_KEYPAD_BUTTON_SIZE * 1.5 + IIDX_KEYPAD_GAP)},
{"0/D", 60},
{"1/0", (int)(IIDX_KEYPAD_LEFT_2P + IIDX_KEYPAD_BUTTON_SIZE * 0.5)},
{"1/1", (int)(IIDX_KEYPAD_LEFT_2P + IIDX_KEYPAD_BUTTON_SIZE * 0.5)},
{"1/2", (int)(IIDX_KEYPAD_LEFT_2P + IIDX_KEYPAD_BUTTON_SIZE * 1.5 + IIDX_KEYPAD_GAP)},
{"1/3", (int)(IIDX_KEYPAD_LEFT_2P + IIDX_KEYPAD_BUTTON_SIZE * 2.5 + IIDX_KEYPAD_GAP * 2)},
{"1/4", (int)(IIDX_KEYPAD_LEFT_2P + IIDX_KEYPAD_BUTTON_SIZE * 0.5)},
{"1/5", (int)(IIDX_KEYPAD_LEFT_2P + IIDX_KEYPAD_BUTTON_SIZE * 1.5 + IIDX_KEYPAD_GAP)},
{"1/6", (int)(IIDX_KEYPAD_LEFT_2P + IIDX_KEYPAD_BUTTON_SIZE * 2.5 + IIDX_KEYPAD_GAP * 2)},
{"1/7", (int)(IIDX_KEYPAD_LEFT_2P + IIDX_KEYPAD_BUTTON_SIZE * 0.5)},
{"1/8", (int)(IIDX_KEYPAD_LEFT_2P + IIDX_KEYPAD_BUTTON_SIZE * 1.5 + IIDX_KEYPAD_GAP)},
{"1/9", (int)(IIDX_KEYPAD_LEFT_2P + IIDX_KEYPAD_BUTTON_SIZE * 2.5 + IIDX_KEYPAD_GAP * 2)},
{"1/A", (int)(IIDX_KEYPAD_LEFT_2P + IIDX_KEYPAD_BUTTON_SIZE * 1.5 + IIDX_KEYPAD_GAP)},
{"1/D", 1920 - 60},
};
static const std::unordered_map<std::string, int> IIDX_KEYPAD_POSITION_Y {
{"0/0", (int)(IIDX_KEYPAD_TOP + IIDX_KEYPAD_GAP * 3 + IIDX_KEYPAD_BUTTON_SIZE * 3.5)},
{"0/1", (int)(IIDX_KEYPAD_TOP + IIDX_KEYPAD_GAP * 2 + IIDX_KEYPAD_BUTTON_SIZE * 2.5)},
{"0/2", (int)(IIDX_KEYPAD_TOP + IIDX_KEYPAD_GAP * 2 + IIDX_KEYPAD_BUTTON_SIZE * 2.5)},
{"0/3", (int)(IIDX_KEYPAD_TOP + IIDX_KEYPAD_GAP * 2 + IIDX_KEYPAD_BUTTON_SIZE * 2.5)},
{"0/4", (int)(IIDX_KEYPAD_TOP + IIDX_KEYPAD_GAP + IIDX_KEYPAD_BUTTON_SIZE * 1.5)},
{"0/5", (int)(IIDX_KEYPAD_TOP + IIDX_KEYPAD_GAP + IIDX_KEYPAD_BUTTON_SIZE * 1.5)},
{"0/6", (int)(IIDX_KEYPAD_TOP + IIDX_KEYPAD_GAP + IIDX_KEYPAD_BUTTON_SIZE * 1.5)},
{"0/7", (int)(IIDX_KEYPAD_TOP + IIDX_KEYPAD_BUTTON_SIZE * 0.5)},
{"0/8", (int)(IIDX_KEYPAD_TOP + IIDX_KEYPAD_BUTTON_SIZE * 0.5)},
{"0/9", (int)(IIDX_KEYPAD_TOP + IIDX_KEYPAD_BUTTON_SIZE * 0.5)},
{"0/A", (int)(IIDX_KEYPAD_TOP + IIDX_KEYPAD_GAP * 3 + IIDX_KEYPAD_BUTTON_SIZE * 3.5)},
{"0/D", 50},
{"1/0", (int)(IIDX_KEYPAD_TOP + IIDX_KEYPAD_GAP * 3 + IIDX_KEYPAD_BUTTON_SIZE * 3.5)},
{"1/1", (int)(IIDX_KEYPAD_TOP + IIDX_KEYPAD_GAP * 2 + IIDX_KEYPAD_BUTTON_SIZE * 2.5)},
{"1/2", (int)(IIDX_KEYPAD_TOP + IIDX_KEYPAD_GAP * 2 + IIDX_KEYPAD_BUTTON_SIZE * 2.5)},
{"1/3", (int)(IIDX_KEYPAD_TOP + IIDX_KEYPAD_GAP * 2 + IIDX_KEYPAD_BUTTON_SIZE * 2.5)},
{"1/4", (int)(IIDX_KEYPAD_TOP + IIDX_KEYPAD_GAP + IIDX_KEYPAD_BUTTON_SIZE * 1.5)},
{"1/5", (int)(IIDX_KEYPAD_TOP + IIDX_KEYPAD_GAP + IIDX_KEYPAD_BUTTON_SIZE * 1.5)},
{"1/6", (int)(IIDX_KEYPAD_TOP + IIDX_KEYPAD_GAP + IIDX_KEYPAD_BUTTON_SIZE * 1.5)},
{"1/7", (int)(IIDX_KEYPAD_TOP + IIDX_KEYPAD_BUTTON_SIZE * 0.5)},
{"1/8", (int)(IIDX_KEYPAD_TOP + IIDX_KEYPAD_BUTTON_SIZE * 0.5)},
{"1/9", (int)(IIDX_KEYPAD_TOP + IIDX_KEYPAD_BUTTON_SIZE * 0.5)},
{"1/A", (int)(IIDX_KEYPAD_TOP + IIDX_KEYPAD_GAP * 3 + IIDX_KEYPAD_BUTTON_SIZE * 3.5)},
{"1/D", 50},
};
static void inject_native_touch_points(const std::vector<TouchPoint> &touch_points, bool down) {
if (touch_points.empty()) {
return;
}
const auto &touch = touch_points.front();
nativetouch::inject::inject_synthetic_touch({ touch.x, touch.y }, down);
}
void enable() {
// check if already running
if (THREAD)
return;
if (!games::iidx::NATIVE_TOUCH) {
log_warning("poke", "keypad touch emulation requires native touch; disabled");
return;
}
// create new thread
THREAD_RUNNING = true;
THREAD = new std::thread([] {
// log
log_info("poke", "enabled");
std::vector<TouchPoint> touch_points;
std::vector<uint16_t> last_keypad_state = {0, 0};
// set variable to false to stop
while (THREAD_RUNNING) {
// clean up touch from last frame
if (touch_points.size() > 0) {
inject_native_touch_points(touch_points, false);
touch_points.clear();
}
for (int unit = 0; unit < 2; unit++) {
// get keypad state
auto state = eamuse_get_keypad_state(unit);
if (state != 0) {
// add keys
for (auto &mapping : KEYPAD_MAPPINGS) {
if (state & mapping.state) {
if (last_keypad_state[unit] & mapping.state) {
// log_warning("poke", "ignoring hold {} {}", unit, mapping.character);
continue;
}
std::string handle = fmt::format("{0}/{1}", unit, mapping.character);
auto x_iter = IIDX_KEYPAD_POSITION_X.find(handle);
auto y_iter = IIDX_KEYPAD_POSITION_Y.find(handle);
if (x_iter != IIDX_KEYPAD_POSITION_X.end() && y_iter != IIDX_KEYPAD_POSITION_Y.end()) {
DWORD touch_id = (DWORD)(0xFFFFFF * unit + mapping.character);
float x = x_iter->second / 1920.0;
float y = y_iter->second / 1080.0;
if (GRAPHICS_WINDOWED) {
if (SPICETOUCH_TOUCH_WIDTH <= 0 || SPICETOUCH_TOUCH_HEIGHT <= 0) {
continue;
}
x = SPICETOUCH_TOUCH_X + x * SPICETOUCH_TOUCH_WIDTH;
y = SPICETOUCH_TOUCH_Y + y * SPICETOUCH_TOUCH_HEIGHT;
} else {
x = x_iter->second;
y = y_iter->second;
}
TouchPoint tp {
.id = touch_id,
.x = (LONG)x,
.y = (LONG)y,
.mouse = true,
};
touch_points.emplace_back(tp);
// log_warning("poke", "coords: {} {}", to_string(tp.x), to_string(tp.y));
}
}
} // for all keys
} // if state != 0
last_keypad_state[unit] = state;
} // for all units
if (touch_points.size() > 0) {
inject_native_touch_points(touch_points, true);
}
// slow down
std::this_thread::sleep_for(std::chrono::milliseconds(50));
}
if (!touch_points.empty()) {
inject_native_touch_points(touch_points, false);
}
return nullptr;
});
}
void disable() {
if (!THREAD) {
return;
}
// stop old thread
THREAD_RUNNING = false;
THREAD->join();
// delete thread
delete THREAD;
THREAD = nullptr;
// log
log_info("poke", "disabled");
}
}