mirror of
https://github.com/spice2x/spice2x.github.io.git
synced 2026-08-01 22:30:42 -07:00
sdk: update cpp sample for DDR, fix DDR MDXF polling when api/sdk is providing input (#679)
## Link to GitHub Issue or related Pull Request, if one exists #676 #452 ## Description of change When spice SDK or API sets or clears button and analog override, we don't tell MDXF about it, so it may fall behind. This is a special case for DDR MDXF I/O emulation. For Naive/Xinput I think we are getting lucky because they usually also generate WM_INPUT events. Update the C++ SDK sample to provide more interesting example of emulating DDR arrows with the keyboard. ## Testing
This commit is contained in:
@@ -6,6 +6,7 @@
|
|||||||
#include "launcher/launcher.h"
|
#include "launcher/launcher.h"
|
||||||
#include "games/io.h"
|
#include "games/io.h"
|
||||||
#include "util/utils.h"
|
#include "util/utils.h"
|
||||||
|
#include "acio/mdxf/mdxf_poll.h"
|
||||||
|
|
||||||
using namespace std::placeholders;
|
using namespace std::placeholders;
|
||||||
using namespace rapidjson;
|
using namespace rapidjson;
|
||||||
@@ -104,6 +105,7 @@ namespace api::modules {
|
|||||||
for (auto &analog : *this->analogs) {
|
for (auto &analog : *this->analogs) {
|
||||||
analog.override_enabled = false;
|
analog.override_enabled = false;
|
||||||
}
|
}
|
||||||
|
mdxf_poll(true);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -148,6 +150,7 @@ namespace api::modules {
|
|||||||
if (analog.getName() == name) {
|
if (analog.getName() == name) {
|
||||||
analog.override_state = CLAMP(state, 0.f, 1.f);
|
analog.override_state = CLAMP(state, 0.f, 1.f);
|
||||||
analog.override_enabled = true;
|
analog.override_enabled = true;
|
||||||
|
mdxf_poll(true);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -167,6 +170,7 @@ namespace api::modules {
|
|||||||
for (auto &analog : *this->analogs) {
|
for (auto &analog : *this->analogs) {
|
||||||
if (analog.getName() == name) {
|
if (analog.getName() == name) {
|
||||||
analog.override_enabled = false;
|
analog.override_enabled = false;
|
||||||
|
mdxf_poll(true);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
#include "launcher/launcher.h"
|
#include "launcher/launcher.h"
|
||||||
#include "games/io.h"
|
#include "games/io.h"
|
||||||
#include "util/utils.h"
|
#include "util/utils.h"
|
||||||
|
#include "acio/mdxf/mdxf_poll.h"
|
||||||
|
|
||||||
using namespace std::placeholders;
|
using namespace std::placeholders;
|
||||||
using namespace rapidjson;
|
using namespace rapidjson;
|
||||||
@@ -107,6 +108,7 @@ namespace api::modules {
|
|||||||
for (auto &button : *this->buttons) {
|
for (auto &button : *this->buttons) {
|
||||||
button.override_enabled = false;
|
button.override_enabled = false;
|
||||||
}
|
}
|
||||||
|
mdxf_poll(true);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -153,6 +155,7 @@ namespace api::modules {
|
|||||||
GameAPI::Buttons::BUTTON_PRESSED : GameAPI::Buttons::BUTTON_NOT_PRESSED;
|
GameAPI::Buttons::BUTTON_PRESSED : GameAPI::Buttons::BUTTON_NOT_PRESSED;
|
||||||
button.override_velocity = CLAMP(state, 0.f, 1.f);
|
button.override_velocity = CLAMP(state, 0.f, 1.f);
|
||||||
button.override_enabled = true;
|
button.override_enabled = true;
|
||||||
|
mdxf_poll(true);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -172,6 +175,7 @@ namespace api::modules {
|
|||||||
for (auto &button : *this->buttons) {
|
for (auto &button : *this->buttons) {
|
||||||
if (button.getName() == name) {
|
if (button.getName() == name) {
|
||||||
button.override_enabled = false;
|
button.override_enabled = false;
|
||||||
|
mdxf_poll(true);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,30 +1,25 @@
|
|||||||
|
#include <format>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
#include "sdk/include/spicesdk.h"
|
#include "sdk/include/spicesdk.h"
|
||||||
#include "sdk/include/spicesdk_io.h"
|
#include "sdk/include/spicesdk_io.h"
|
||||||
|
|
||||||
|
// this sample assumes that the game is DDR
|
||||||
|
|
||||||
|
// convenience wrapper for logging; feel free to add other levels
|
||||||
#define LOG_INFO(message) spice.log(SPICE_SDK_LOG_LEVEL_INFO, "sample_v0_cpp", message)
|
#define LOG_INFO(message) spice.log(SPICE_SDK_LOG_LEVEL_INFO, "sample_v0_cpp", message)
|
||||||
|
|
||||||
static SPICE_SDK_V0 spice = {};
|
static SPICE_SDK_V0 spice = {};
|
||||||
|
|
||||||
static spice_sdk_destroy_callback_func destroy_callback;
|
static spice_sdk_destroy_callback_func destroy_callback;
|
||||||
static std::jthread worker_thread;
|
static std::jthread worker_thread;
|
||||||
|
static void worker_thread_main(std::stop_token stop_token);
|
||||||
|
|
||||||
static void worker_thread_main(std::stop_token stop_token) {
|
// main entry point into the DLL
|
||||||
while (!stop_token.stop_requested()) {
|
// spice executable will call into this shortly after the game is partially
|
||||||
|
// initialized (but not quite fully booted just yet)
|
||||||
static bool state = false;
|
|
||||||
bool new_state;
|
|
||||||
spice.get_button(IIDX_Button_P1_Start, &new_state, nullptr);
|
|
||||||
if (!state && new_state) {
|
|
||||||
LOG_INFO("user pressed P1 Start button!");
|
|
||||||
}
|
|
||||||
state = new_state;
|
|
||||||
|
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SPICE_SDK_ENTRY_POINT
|
SPICE_SDK_ENTRY_POINT
|
||||||
spice_sdk_entry_point(
|
spice_sdk_entry_point(
|
||||||
spice_sdk_init_func *init
|
spice_sdk_init_func *init
|
||||||
@@ -32,13 +27,22 @@ spice_sdk_entry_point(
|
|||||||
{
|
{
|
||||||
SPICE_SDK_STATUS_CODE status;
|
SPICE_SDK_STATUS_CODE status;
|
||||||
|
|
||||||
|
// SPICE_SDK_V0 must be zeroed AND the size field must be set
|
||||||
spice.size = sizeof(spice);
|
spice.size = sizeof(spice);
|
||||||
|
|
||||||
|
// initialize the SDK - can be called any time, but we'll do it here
|
||||||
status = init(0, destroy_callback, &spice);
|
status = init(0, destroy_callback, &spice);
|
||||||
|
|
||||||
|
// always check for return value; failure could be usage error,
|
||||||
|
// or the spice executable doesn't support this SDK version
|
||||||
|
// check the log messages from sdk:
|
||||||
if (status != SPICE_SDK_STATUS_SUCCESS) {
|
if (status != SPICE_SDK_STATUS_SUCCESS) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG_INFO("plugin loaded");
|
LOG_INFO("plugin loaded");
|
||||||
|
|
||||||
|
// spin up a worker thread
|
||||||
worker_thread = std::jthread(worker_thread_main);
|
worker_thread = std::jthread(worker_thread_main);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@@ -55,3 +59,40 @@ destroy_callback(
|
|||||||
worker_thread.join();
|
worker_thread.join();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct ArrowButton {
|
||||||
|
int key;
|
||||||
|
SPICE_SDK_DDR_BUTTONS button;
|
||||||
|
const char* name;
|
||||||
|
bool previous_state;
|
||||||
|
};
|
||||||
|
|
||||||
|
static ArrowButton arrow_buttons[] = {
|
||||||
|
{ VK_UP, DDR_Button_P1_PANEL_UP, "UP", false },
|
||||||
|
{ VK_DOWN, DDR_Button_P1_PANEL_DOWN, "DOWN", false },
|
||||||
|
{ VK_LEFT, DDR_Button_P1_PANEL_LEFT, "LEFT", false },
|
||||||
|
{ VK_RIGHT, DDR_Button_P1_PANEL_RIGHT, "RIGHT", false },
|
||||||
|
};
|
||||||
|
|
||||||
|
// worker thread for I/O
|
||||||
|
static void worker_thread_main(std::stop_token stop_token) {
|
||||||
|
while (!stop_token.stop_requested()) {
|
||||||
|
for (auto& arrow : arrow_buttons) {
|
||||||
|
// check for ctrl + arrow keys and trigger p1 pad arrows
|
||||||
|
if (((GetAsyncKeyState(VK_CONTROL) & 0x8000) != 0) &&
|
||||||
|
((GetAsyncKeyState(arrow.key) & 0x8000) != 0)) {
|
||||||
|
spice.set_button(arrow.button, true, 1.f);
|
||||||
|
|
||||||
|
if (!arrow.previous_state) {
|
||||||
|
LOG_INFO(std::format("let me hear you say: {}", arrow.name).c_str());
|
||||||
|
arrow.previous_state = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
spice.set_button(arrow.button, false, 0.f);
|
||||||
|
arrow.previous_state = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
#include "touch/touch.h"
|
#include "touch/touch.h"
|
||||||
#include "util/logging.h"
|
#include "util/logging.h"
|
||||||
#include "util/utils.h"
|
#include "util/utils.h"
|
||||||
|
#include "acio/mdxf/mdxf_poll.h"
|
||||||
|
|
||||||
namespace sdk {
|
namespace sdk {
|
||||||
|
|
||||||
@@ -323,6 +324,7 @@ sdk_set_button (
|
|||||||
button.override_velocity = velocity;
|
button.override_velocity = velocity;
|
||||||
}
|
}
|
||||||
button.override_enabled = pressed;
|
button.override_enabled = pressed;
|
||||||
|
mdxf_poll(true);
|
||||||
return SPICE_SDK_STATUS_SUCCESS;
|
return SPICE_SDK_STATUS_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -381,6 +383,7 @@ sdk_set_analog (
|
|||||||
analog.override_state = value;
|
analog.override_state = value;
|
||||||
}
|
}
|
||||||
analog.override_enabled = override_active;
|
analog.override_enabled = override_active;
|
||||||
|
mdxf_poll(true);
|
||||||
return SPICE_SDK_STATUS_SUCCESS;
|
return SPICE_SDK_STATUS_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user