nost: move nostalgia from wintouchemu to native touch hook (#827)
## Link to GitHub Issue or related Pull Request, if one exists #814 ## Description of change Switch over Nostalgia from wintouchemu to native touch hook. Nostalgia has some strict timing requirements (touches must be updated on every acio poll) so this takes a slightly different path to maintain mouse holds. Only a handful of consumers of wintouchemu remain: 1. beatstream - but it's off by default, only enabled as errata for buggy touchscreens, or if the user forces it on (for Show Cursor) 2. gitadora single-window overlay - this is just for mouse so not a big deal. 3. MFC HG mode - not a priority to fix. ## Testing Tested full screen and windowed mode with touch / mouse / poke.
This commit is contained in:
@@ -1,11 +1,7 @@
|
||||
// enable touch functions - set version to windows 7
|
||||
// mingw otherwise doesn't load touch stuff
|
||||
#define _WIN32_WINNT 0x0601
|
||||
|
||||
#include "nost.h"
|
||||
#include "poke.h"
|
||||
#include "hooks/setupapihook.h"
|
||||
#include "misc/wintouchemu.h"
|
||||
#include "touch/native/nativetouchhook.h"
|
||||
#include "avs/game.h"
|
||||
|
||||
namespace games::nost {
|
||||
@@ -43,9 +39,7 @@ namespace games::nost {
|
||||
setupapihook_init(avs::game::DLL_INSTANCE);
|
||||
setupapihook_add(touch_settings);
|
||||
|
||||
// custom touch
|
||||
// nostalgia crashes if you inject touch events too early
|
||||
wintouchemu::hook("nostalgia", avs::game::DLL_INSTANCE, 20);
|
||||
nativetouch::hook(avs::game::DLL_INSTANCE);
|
||||
}
|
||||
|
||||
void NostGame::post_attach() {
|
||||
|
||||
@@ -1,22 +1,24 @@
|
||||
#include "poke.h"
|
||||
|
||||
#include <thread>
|
||||
#include <atomic>
|
||||
#include <optional>
|
||||
#include <thread>
|
||||
|
||||
#include "games/nost/io.h"
|
||||
#include "cfg/api.h"
|
||||
#include "rawinput/rawinput.h"
|
||||
#include "misc/eamuse.h"
|
||||
#include "touch/touch.h"
|
||||
#include "touch/native/inject.h"
|
||||
#include "util/logging.h"
|
||||
#include "util/precise_timer.h"
|
||||
|
||||
namespace games::nost::poke {
|
||||
|
||||
static std::thread *THREAD = nullptr;
|
||||
static volatile bool THREAD_RUNNING = false;
|
||||
|
||||
static const std::unordered_map<int, std::pair<LONG, LONG>> NOST_POKE_NUM {
|
||||
static std::atomic<bool> THREAD_RUNNING { false };
|
||||
|
||||
// positions use game-client pixels, matching the legacy client-sized touch window
|
||||
static const std::unordered_map<int, std::pair<LONG, LONG>> NOST_POKE_KEYPAD {
|
||||
{EAM_IO_KEYPAD_0, {760, 440}},
|
||||
{EAM_IO_KEYPAD_1, {760, 385}},
|
||||
{EAM_IO_KEYPAD_2, {820, 385}},
|
||||
@@ -31,7 +33,8 @@ namespace games::nost::poke {
|
||||
{EAM_IO_KEYPAD_DECIMAL, {880, 440}} // also erase button
|
||||
};
|
||||
|
||||
static const std::unordered_map<games::nost::Buttons::Button, std::pair<LONG, LONG>> NOST_POKE {
|
||||
static const std::unordered_map<
|
||||
games::nost::Buttons::Button, std::pair<LONG, LONG>> NOST_POKE_BUTTONS {
|
||||
{games::nost::Buttons::Button::PokeConfirm, {1100, 525}},
|
||||
{games::nost::Buttons::Button::PokeBack, {340, 100}},
|
||||
{games::nost::Buttons::Button::PokeSong1, {450, 190}},
|
||||
@@ -46,13 +49,8 @@ namespace games::nost::poke {
|
||||
{games::nost::Buttons::Button::PokeDifficulty4, {820, 490}},
|
||||
};
|
||||
|
||||
void clear_touch_points(std::vector<TouchPoint> *touch_points) {
|
||||
std::vector<DWORD> touch_ids;
|
||||
for (auto &touch : *touch_points) {
|
||||
touch_ids.emplace_back(touch.id);
|
||||
}
|
||||
touch_remove_points(&touch_ids);
|
||||
touch_points->clear();
|
||||
static bool inject_poke_position(POINT position, bool down) {
|
||||
return nativetouch::inject::inject_synthetic_touch(position, down);
|
||||
}
|
||||
|
||||
void enable() {
|
||||
@@ -65,124 +63,138 @@ namespace games::nost::poke {
|
||||
THREAD_RUNNING = true;
|
||||
THREAD = new std::thread([] {
|
||||
timeutils::PreciseSleepTimer timer;
|
||||
const DWORD touch_id = (DWORD)(0xFFFFFFFE);
|
||||
|
||||
const int swipe_anim_total_frames = 6;
|
||||
const int swipe_anim_y = 300;
|
||||
|
||||
const int swipe_next_page_x_begin = 1120;
|
||||
const int swipe_next_page_x_end = 1120-120;
|
||||
const int swipe_next_page_x_end = 1120 - 120;
|
||||
|
||||
const int swipe_prev_page_x_begin = 280-120;
|
||||
const int swipe_prev_page_x_begin = 280 - 120;
|
||||
const int swipe_prev_page_x_end = 280;
|
||||
|
||||
int next_page_anim_index = -1;
|
||||
int prev_page_anim_index = -1;
|
||||
std::vector<TouchPoint> touch_points;
|
||||
|
||||
bool touch_release = false;
|
||||
bool contact_active = false;
|
||||
bool release_pending = false;
|
||||
POINT last_position {};
|
||||
std::unordered_map<games::nost::Buttons::Button, bool> button_states;
|
||||
uint16_t last_keypad_state = 0;
|
||||
|
||||
// log
|
||||
log_info("poke", "enabled");
|
||||
|
||||
// set variable to false to stop
|
||||
while (THREAD_RUNNING) {
|
||||
// clean up touch from last frame
|
||||
if (!touch_points.empty()) {
|
||||
if (touch_release) {
|
||||
clear_touch_points(&touch_points);
|
||||
touch_release = false;
|
||||
} else {
|
||||
touch_points.clear();
|
||||
if (release_pending) {
|
||||
if (contact_active) {
|
||||
inject_poke_position(last_position, false);
|
||||
}
|
||||
contact_active = false;
|
||||
release_pending = false;
|
||||
}
|
||||
|
||||
auto &buttons = games::nost::get_buttons();
|
||||
std::optional<POINT> touch_position;
|
||||
bool release_after_touch = false;
|
||||
|
||||
const auto button_triggered = [&](games::nost::Buttons::Button button) {
|
||||
const auto pressed =
|
||||
GameAPI::Buttons::getState(RI_MGR, buttons.at(button));
|
||||
const auto triggered = pressed && !button_states[button];
|
||||
button_states[button] = pressed;
|
||||
return triggered;
|
||||
};
|
||||
|
||||
std::optional<POINT> triggered_touch_position;
|
||||
for (const auto& it : NOST_POKE_BUTTONS) {
|
||||
if (button_triggered(it.first) && !triggered_touch_position.has_value()) {
|
||||
triggered_touch_position = POINT {
|
||||
it.second.first,
|
||||
it.second.second,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
const auto next_triggered =
|
||||
button_triggered(games::nost::Buttons::PokeNextPage);
|
||||
const auto prev_triggered =
|
||||
button_triggered(games::nost::Buttons::PokePrevPage);
|
||||
|
||||
const auto keypad_state = eamuse_get_keypad_state(0);
|
||||
if (!triggered_touch_position.has_value()) {
|
||||
for (const auto& it : NOST_POKE_KEYPAD) {
|
||||
const auto mask = static_cast<uint16_t>(1 << it.first);
|
||||
if ((keypad_state & mask) && !(last_keypad_state & mask)) {
|
||||
triggered_touch_position = POINT {
|
||||
it.second.first,
|
||||
it.second.second,
|
||||
};
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
last_keypad_state = keypad_state;
|
||||
|
||||
if (0 <= next_page_anim_index) {
|
||||
const auto delta =
|
||||
(swipe_next_page_x_end - swipe_next_page_x_begin)
|
||||
* (swipe_anim_total_frames - next_page_anim_index) / swipe_anim_total_frames;
|
||||
|
||||
TouchPoint tp {
|
||||
.id = touch_id,
|
||||
.x = (LONG)swipe_next_page_x_begin + delta,
|
||||
.y = (LONG)swipe_anim_y,
|
||||
.mouse = true,
|
||||
touch_position = POINT {
|
||||
swipe_next_page_x_begin + delta,
|
||||
swipe_anim_y,
|
||||
};
|
||||
touch_points.emplace_back(tp);
|
||||
next_page_anim_index--;
|
||||
if (next_page_anim_index < 0) {
|
||||
touch_release = true;
|
||||
release_after_touch = true;
|
||||
}
|
||||
} else if (0 <= prev_page_anim_index) {
|
||||
const auto delta =
|
||||
(swipe_prev_page_x_end - swipe_prev_page_x_begin)
|
||||
* (swipe_anim_total_frames - prev_page_anim_index) / swipe_anim_total_frames;
|
||||
|
||||
TouchPoint tp {
|
||||
.id = touch_id,
|
||||
.x = (LONG)swipe_prev_page_x_begin + delta,
|
||||
.y = (LONG)swipe_anim_y,
|
||||
.mouse = true,
|
||||
touch_position = POINT {
|
||||
swipe_prev_page_x_begin + delta,
|
||||
swipe_anim_y,
|
||||
};
|
||||
touch_points.emplace_back(tp);
|
||||
prev_page_anim_index--;
|
||||
if (prev_page_anim_index < 0) {
|
||||
touch_release = true;
|
||||
release_after_touch = true;
|
||||
}
|
||||
} else {
|
||||
for (const auto& it : NOST_POKE) {
|
||||
if (GameAPI::Buttons::getState(RI_MGR, buttons.at(it.first))) {
|
||||
TouchPoint tp {
|
||||
.id = touch_id,
|
||||
.x = it.second.first,
|
||||
.y = it.second.second,
|
||||
.mouse = true,
|
||||
};
|
||||
touch_points.emplace_back(tp);
|
||||
touch_release = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!touch_release) {
|
||||
const auto state = eamuse_get_keypad_state(0);
|
||||
if (state) {
|
||||
for (const auto& it : NOST_POKE_NUM) {
|
||||
if (state & (1 << it.first)) {
|
||||
TouchPoint tp {
|
||||
.id = touch_id,
|
||||
.x = it.second.first,
|
||||
.y = it.second.second,
|
||||
.mouse = true,
|
||||
};
|
||||
touch_points.emplace_back(tp);
|
||||
touch_release = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
touch_position = triggered_touch_position;
|
||||
release_after_touch = touch_position.has_value();
|
||||
|
||||
// start animations for next frame
|
||||
if (!touch_release) {
|
||||
if (GameAPI::Buttons::getState(RI_MGR, buttons.at(games::nost::Buttons::PokeNextPage))) {
|
||||
if (!touch_position.has_value()) {
|
||||
if (next_triggered) {
|
||||
next_page_anim_index = swipe_anim_total_frames;
|
||||
}
|
||||
if (GameAPI::Buttons::getState(RI_MGR, buttons.at(games::nost::Buttons::PokePrevPage))) {
|
||||
|
||||
if (prev_triggered) {
|
||||
prev_page_anim_index = swipe_anim_total_frames;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!touch_points.empty()) {
|
||||
touch_write_points(&touch_points);
|
||||
if (touch_position.has_value() &&
|
||||
inject_poke_position(*touch_position, true)) {
|
||||
contact_active = true;
|
||||
last_position = *touch_position;
|
||||
}
|
||||
if (release_after_touch && contact_active) {
|
||||
release_pending = true;
|
||||
}
|
||||
|
||||
// slow down
|
||||
timer.sleep(30);
|
||||
}
|
||||
|
||||
if (contact_active) {
|
||||
inject_poke_position(last_position, false);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user