mirror of
https://gitea.tendokyu.moe/Hay1tsme/segatools.git
synced 2026-09-13 14:46:32 -05:00
Fix mai2 memory leak via LED 15070 rate-limiting and enhance touch handling (#101)
## Overview This PR addresses the severe memory leak and performance issues observed in `mai2`, while also introducing improvements to the touch emulation logic. ## Root Cause Analysis As discussed previously, the root cause of the `mai2` memory leak is not a global `segatools` buffer bug. Instead, the game aggressively spams overlapped empty reads specifically on the `LED 15070` UART. On real hardware, the serial driver naturally throttles this. Under emulation, without a throttle, it hits approximately **260kHz of empty async reads**, which causes the memory usage to explode. ## The Fix Instead of introducing complex locking mechanisms and condition variables globally in `uart.c`, this PR applies a targeted fix: * Added a local `Sleep(1)` directly in `common/board/led15070.c` to rate-limit empty reads on the LED path. * Because this is isolated to LED communications, it completely resolves the memory leak without introducing any lag, livelocks, or overhead to other critical inputs. ## Additional Changes in this PR Alongside the memory leak fix, this PR includes a few touch-related improvements (as touch emulation was reviewed during the debugging process): * Enhanced touch input handling and improved auto-scan state management. * Implemented IOCTL handling for touch input to properly manage communication status. ## Testing * **mai2:** Tested successfully on multiple machines. The memory leak is completely gone, and the game runs smoothly. * **chusan:** Tested to ensure no regressions. Sliders and inputs work flawlessly without the lag. Reviewed-on: https://gitea.tendokyu.moe/TeamTofuShop/segatools/pulls/101 Co-authored-by: Gl0w1amp <gl0w1amp@noreply.gitea.tendokyu.moe> Co-committed-by: Gl0w1amp <gl0w1amp@noreply.gitea.tendokyu.moe>
This commit is contained in:
@@ -244,6 +244,12 @@ static HRESULT led15070_handle_irp_locked(int board, struct irp *irp)
|
||||
}
|
||||
}
|
||||
|
||||
if (irp->op == IRP_OP_READ) {
|
||||
if (irp->ovl != NULL && boarduart->readable.pos == 0) {
|
||||
Sleep(1);
|
||||
}
|
||||
}
|
||||
|
||||
hr = uart_handle_irp(boarduart, irp);
|
||||
|
||||
if (FAILED(hr) || irp->op != IRP_OP_WRITE) {
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
#include <windows.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "hooklib/fdshark.h"
|
||||
#include "hooklib/reg.h"
|
||||
@@ -10,6 +13,59 @@
|
||||
#include "util/dprintf.h"
|
||||
#include "util/dump.h"
|
||||
|
||||
static HRESULT touch_handle_irp(struct irp *irp);
|
||||
static HRESULT touch_handle_irp_locked(
|
||||
struct irp *irp,
|
||||
struct uart *uart);
|
||||
static HRESULT touch_handle_read(struct irp *irp, struct uart *uart);
|
||||
static HRESULT touch_handle_ioctl(struct irp *irp, struct uart *uart);
|
||||
static void touch_complete_pending_read(struct uart *uart);
|
||||
static void touch_release_pending_read(struct uart *uart);
|
||||
static void touch_clear_auto_scan(struct uart *uart);
|
||||
static bool touch_read_available(struct uart *uart);
|
||||
static size_t touch_current_depth(struct uart *uart);
|
||||
static void touch_shift_read(struct uart *uart, struct iobuf *read);
|
||||
static HRESULT touch_enqueue(
|
||||
struct uart *uart,
|
||||
CONDITION_VARIABLE *cv,
|
||||
const void *bytes,
|
||||
size_t nbytes);
|
||||
static HRESULT touch_enqueue_reply(
|
||||
struct uart *uart,
|
||||
CONDITION_VARIABLE *cv,
|
||||
uint8_t side,
|
||||
uint8_t sensor,
|
||||
uint8_t command,
|
||||
uint8_t value);
|
||||
static void touch_auto_scan(const uint8_t player, const uint8_t state[7]);
|
||||
|
||||
struct touch_pending_read {
|
||||
OVERLAPPED *ovl;
|
||||
uint8_t *bytes;
|
||||
size_t nbytes;
|
||||
};
|
||||
|
||||
enum {
|
||||
touch_pending_reads_capacity = 4096,
|
||||
};
|
||||
|
||||
struct touch_pending_reads {
|
||||
struct touch_pending_read entries[touch_pending_reads_capacity];
|
||||
size_t head;
|
||||
size_t count;
|
||||
};
|
||||
|
||||
struct touch_auto_scan_state {
|
||||
uint8_t frame[9];
|
||||
size_t pos;
|
||||
bool valid;
|
||||
};
|
||||
|
||||
enum {
|
||||
touch_status_pending = 0x00000103UL,
|
||||
touch_status_success = 0x00000000UL,
|
||||
};
|
||||
|
||||
|
||||
static HRESULT read_reg_touch_1p(void *bytes, uint32_t *nbytes)
|
||||
{
|
||||
@@ -53,17 +109,38 @@ const char *sensor_to_str(uint8_t sensor)
|
||||
}
|
||||
|
||||
static CRITICAL_SECTION touch_1p_lock;
|
||||
static CONDITION_VARIABLE touch_1p_cv;
|
||||
static struct touch_pending_reads touch_1p_pending_reads;
|
||||
static struct touch_auto_scan_state touch_1p_auto_scan;
|
||||
static struct uart touch_1p_uart;
|
||||
static uint8_t touch_1p_written_bytes[64];
|
||||
static uint8_t touch_1p_readable_bytes[64];
|
||||
static uint8_t touch_1p_readable_bytes[1024];
|
||||
static bool touch_1p_status = false;
|
||||
|
||||
static CRITICAL_SECTION touch_2p_lock;
|
||||
static CONDITION_VARIABLE touch_2p_cv;
|
||||
static struct touch_pending_reads touch_2p_pending_reads;
|
||||
static struct touch_auto_scan_state touch_2p_auto_scan;
|
||||
static struct uart touch_2p_uart;
|
||||
static uint8_t touch_2p_written_bytes[64];
|
||||
static uint8_t touch_2p_readable_bytes[64];
|
||||
static uint8_t touch_2p_readable_bytes[1024];
|
||||
static bool touch_2p_status = false;
|
||||
|
||||
static struct touch_pending_reads *touch_get_pending_reads(struct uart *uart)
|
||||
{
|
||||
return uart->port_no == 3 ? &touch_1p_pending_reads : &touch_2p_pending_reads;
|
||||
}
|
||||
|
||||
static bool *touch_get_status_flag(struct uart *uart)
|
||||
{
|
||||
return uart->port_no == 3 ? &touch_1p_status : &touch_2p_status;
|
||||
}
|
||||
|
||||
static struct touch_auto_scan_state *touch_get_auto_scan(struct uart *uart)
|
||||
{
|
||||
return uart->port_no == 3 ? &touch_1p_auto_scan : &touch_2p_auto_scan;
|
||||
}
|
||||
|
||||
HRESULT touch_hook_init(const struct touch_config *cfg)
|
||||
{
|
||||
assert(cfg != NULL);
|
||||
@@ -85,6 +162,7 @@ HRESULT touch_hook_init(const struct touch_config *cfg)
|
||||
dprintf("Mai2 touch 1P: Init.\n");
|
||||
|
||||
InitializeCriticalSection(&touch_1p_lock);
|
||||
InitializeConditionVariable(&touch_1p_cv);
|
||||
uart_init(&touch_1p_uart, 3);
|
||||
touch_1p_uart.written.bytes = touch_1p_written_bytes;
|
||||
touch_1p_uart.written.nbytes = sizeof(touch_1p_written_bytes);
|
||||
@@ -97,6 +175,7 @@ HRESULT touch_hook_init(const struct touch_config *cfg)
|
||||
dprintf("Mai2 touch 2P: Init.\n");
|
||||
|
||||
InitializeCriticalSection(&touch_2p_lock);
|
||||
InitializeConditionVariable(&touch_2p_cv);
|
||||
uart_init(&touch_2p_uart, 4);
|
||||
touch_2p_uart.written.bytes = touch_2p_written_bytes;
|
||||
touch_2p_uart.written.nbytes = sizeof(touch_2p_written_bytes);
|
||||
@@ -133,12 +212,16 @@ static HRESULT touch_handle_irp(struct irp *irp)
|
||||
return hr;
|
||||
}
|
||||
|
||||
static HRESULT touch_handle_irp_locked(struct irp *irp, struct uart *uart)
|
||||
static HRESULT touch_handle_irp_locked(
|
||||
struct irp *irp,
|
||||
struct uart *uart)
|
||||
{
|
||||
HRESULT hr;
|
||||
|
||||
if (irp->op == IRP_OP_OPEN)
|
||||
{
|
||||
touch_clear_auto_scan(uart);
|
||||
touch_release_pending_read(uart);
|
||||
dprintf("Mai2 touch port %d: Starting backend\n", uart->port_no);
|
||||
hr = mai2_dll.touch_init(touch_auto_scan);
|
||||
|
||||
@@ -149,10 +232,23 @@ static HRESULT touch_handle_irp_locked(struct irp *irp, struct uart *uart)
|
||||
}
|
||||
}
|
||||
|
||||
if (irp->op == IRP_OP_READ) {
|
||||
return touch_handle_read(irp, uart);
|
||||
}
|
||||
|
||||
if (irp->op == IRP_OP_IOCTL) {
|
||||
return touch_handle_ioctl(irp, uart);
|
||||
}
|
||||
|
||||
hr = uart_handle_irp(uart, irp);
|
||||
|
||||
if (FAILED(hr) || irp->op != IRP_OP_WRITE)
|
||||
{
|
||||
if (irp->op == IRP_OP_CLOSE) {
|
||||
*touch_get_status_flag(uart) = false;
|
||||
touch_clear_auto_scan(uart);
|
||||
touch_release_pending_read(uart);
|
||||
}
|
||||
return hr;
|
||||
}
|
||||
|
||||
@@ -160,9 +256,18 @@ static HRESULT touch_handle_irp_locked(struct irp *irp, struct uart *uart)
|
||||
dprintf("Mai2 touch port %d WRITE:\n", uart->port_no);
|
||||
dump_iobuf(&uart->written);
|
||||
#endif
|
||||
|
||||
if (uart->written.pos < 6) {
|
||||
dprintf("Mai2 touch port %d: Short write (%u bytes)\n",
|
||||
uart->port_no,
|
||||
(unsigned int) uart->written.pos);
|
||||
uart->written.pos = 0;
|
||||
|
||||
return HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
|
||||
}
|
||||
|
||||
uint8_t port_no = uart->port_no;
|
||||
uint8_t *src = uart->written.bytes;
|
||||
uint8_t *dest = uart->readable.bytes;
|
||||
|
||||
switch (src[3])
|
||||
{
|
||||
@@ -175,18 +280,16 @@ static HRESULT touch_handle_irp_locked(struct irp *irp, struct uart *uart)
|
||||
assert(mai2_dll.touch_update != NULL);
|
||||
if (port_no == 3)
|
||||
{
|
||||
EnterCriticalSection(&touch_1p_lock);
|
||||
touch_1p_status = false;
|
||||
mai2_dll.touch_update(touch_1p_status, touch_2p_status);
|
||||
LeaveCriticalSection(&touch_1p_lock);
|
||||
}
|
||||
else
|
||||
{
|
||||
EnterCriticalSection(&touch_2p_lock);
|
||||
touch_2p_status = false;
|
||||
mai2_dll.touch_update(touch_1p_status, touch_2p_status);
|
||||
LeaveCriticalSection(&touch_2p_lock);
|
||||
}
|
||||
touch_clear_auto_scan(uart);
|
||||
touch_release_pending_read(uart);
|
||||
break;
|
||||
|
||||
case commandSTAT: // Exit Conditioning mode and resume sending touch data.
|
||||
@@ -194,17 +297,13 @@ static HRESULT touch_handle_irp_locked(struct irp *irp, struct uart *uart)
|
||||
assert(mai2_dll.touch_update != NULL);
|
||||
if (port_no == 3)
|
||||
{
|
||||
EnterCriticalSection(&touch_1p_lock);
|
||||
touch_1p_status = true;
|
||||
mai2_dll.touch_update(touch_1p_status, touch_2p_status);
|
||||
LeaveCriticalSection(&touch_1p_lock);
|
||||
}
|
||||
else
|
||||
{
|
||||
EnterCriticalSection(&touch_2p_lock);
|
||||
touch_2p_status = true;
|
||||
mai2_dll.touch_update(touch_1p_status, touch_2p_status);
|
||||
LeaveCriticalSection(&touch_2p_lock);
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -212,28 +311,40 @@ static HRESULT touch_handle_irp_locked(struct irp *irp, struct uart *uart)
|
||||
#if defined(LOG_MAI2_TOUCH)
|
||||
dprintf("Mai2 touch side %c: set sensor %s ratio to %d\n", src[1], sensor_to_str(src[2]), src[4]);
|
||||
#endif
|
||||
dest[0] = res_start;
|
||||
dest[1] = src[1]; // L,R
|
||||
dest[2] = src[2]; // sensor
|
||||
dest[3] = commandRatio;
|
||||
dest[4] = src[4]; // Ratio
|
||||
dest[5] = res_end;
|
||||
uart->readable.pos = 6;
|
||||
// The Ratio is fixed at 0x72 and does not need to be sent to mai2io for processing.
|
||||
hr = touch_enqueue_reply(
|
||||
uart,
|
||||
uart == &touch_1p_uart ? &touch_1p_cv : &touch_2p_cv,
|
||||
src[1],
|
||||
src[2],
|
||||
commandRatio,
|
||||
src[4]);
|
||||
break;
|
||||
|
||||
case commandSens:
|
||||
#if defined(LOG_MAI2_TOUCH)
|
||||
dprintf("Mai2 touch side %c: set sensor %s sensitivity to %d\n", src[1], sensor_to_str(src[2]), src[4]);
|
||||
#endif
|
||||
dest[0] = res_start;
|
||||
dest[1] = src[1]; // L,R
|
||||
dest[2] = src[2]; // sensor
|
||||
dest[3] = commandSens;
|
||||
dest[4] = src[4]; // Sensitivity
|
||||
dest[5] = res_end;
|
||||
uart->readable.pos = 6;
|
||||
mai2_dll.touch_set_sens(dest);
|
||||
assert(mai2_dll.touch_set_sens != NULL);
|
||||
hr = touch_enqueue_reply(
|
||||
uart,
|
||||
uart == &touch_1p_uart ? &touch_1p_cv : &touch_2p_cv,
|
||||
src[1],
|
||||
src[2],
|
||||
commandSens,
|
||||
src[4]);
|
||||
|
||||
if (SUCCEEDED(hr)) {
|
||||
uint8_t sens_bytes[6];
|
||||
|
||||
sens_bytes[0] = res_start;
|
||||
sens_bytes[1] = src[1];
|
||||
sens_bytes[2] = src[2];
|
||||
sens_bytes[3] = commandSens;
|
||||
sens_bytes[4] = src[4];
|
||||
sens_bytes[5] = res_end;
|
||||
mai2_dll.touch_set_sens(sens_bytes);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -249,11 +360,276 @@ static HRESULT touch_handle_irp_locked(struct irp *irp, struct uart *uart)
|
||||
return hr;
|
||||
}
|
||||
|
||||
static HRESULT touch_handle_read(struct irp *irp, struct uart *uart)
|
||||
{
|
||||
struct touch_pending_reads *pending_reads;
|
||||
struct touch_pending_read *pending;
|
||||
bool *status;
|
||||
size_t tail;
|
||||
|
||||
pending_reads = touch_get_pending_reads(uart);
|
||||
status = touch_get_status_flag(uart);
|
||||
|
||||
if (!touch_read_available(uart)) {
|
||||
if (irp->ovl != NULL &&
|
||||
*status &&
|
||||
pending_reads->count < touch_pending_reads_capacity) {
|
||||
tail = (pending_reads->head + pending_reads->count) %
|
||||
touch_pending_reads_capacity;
|
||||
pending = &pending_reads->entries[tail];
|
||||
pending_reads->count++;
|
||||
|
||||
pending->ovl = irp->ovl;
|
||||
pending->bytes = irp->read.bytes;
|
||||
pending->nbytes = irp->read.nbytes;
|
||||
pending->ovl->Internal = touch_status_pending;
|
||||
pending->ovl->InternalHigh = 0;
|
||||
|
||||
if (pending->ovl->hEvent != NULL) {
|
||||
ResetEvent(pending->ovl->hEvent);
|
||||
}
|
||||
|
||||
return HRESULT_FROM_WIN32(ERROR_IO_PENDING);
|
||||
}
|
||||
return E_PENDING;
|
||||
}
|
||||
|
||||
touch_shift_read(uart, &irp->read);
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static HRESULT touch_handle_ioctl(struct irp *irp, struct uart *uart)
|
||||
{
|
||||
if (irp->ioctl == IOCTL_SERIAL_GET_COMMSTATUS) {
|
||||
uart->status.AmountInInQueue = (ULONG) touch_current_depth(uart);
|
||||
uart->status.AmountInOutQueue = uart->written.pos;
|
||||
|
||||
return iobuf_write(&irp->read, &uart->status, sizeof(uart->status));
|
||||
}
|
||||
|
||||
return uart_handle_irp(uart, irp);
|
||||
}
|
||||
|
||||
static void touch_complete_pending_read(struct uart *uart)
|
||||
{
|
||||
struct touch_pending_reads *pending_reads;
|
||||
struct touch_pending_read *pending;
|
||||
struct iobuf read;
|
||||
OVERLAPPED *ovl;
|
||||
HANDLE event;
|
||||
|
||||
pending_reads = touch_get_pending_reads(uart);
|
||||
|
||||
if (pending_reads->count == 0 || !touch_read_available(uart)) {
|
||||
return;
|
||||
}
|
||||
|
||||
pending = &pending_reads->entries[pending_reads->head];
|
||||
|
||||
read.bytes = pending->bytes;
|
||||
read.nbytes = pending->nbytes;
|
||||
read.pos = 0;
|
||||
|
||||
touch_shift_read(uart, &read);
|
||||
|
||||
ovl = pending->ovl;
|
||||
memset(pending, 0, sizeof(*pending));
|
||||
pending_reads->head = (pending_reads->head + 1) %
|
||||
touch_pending_reads_capacity;
|
||||
pending_reads->count--;
|
||||
|
||||
ovl->InternalHigh = (ULONG_PTR) read.pos;
|
||||
|
||||
event = ovl->hEvent;
|
||||
MemoryBarrier();
|
||||
ovl->Internal = touch_status_success;
|
||||
|
||||
if (event != NULL) {
|
||||
SetEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
static void touch_release_pending_read(struct uart *uart)
|
||||
{
|
||||
struct touch_pending_reads *pending_reads;
|
||||
struct touch_pending_read *pending;
|
||||
OVERLAPPED *ovl;
|
||||
HANDLE event;
|
||||
size_t i;
|
||||
|
||||
pending_reads = touch_get_pending_reads(uart);
|
||||
|
||||
for (i = 0; i < pending_reads->count; i++) {
|
||||
pending = &pending_reads->entries[
|
||||
(pending_reads->head + i) % touch_pending_reads_capacity];
|
||||
|
||||
if (pending->ovl == NULL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ovl = pending->ovl;
|
||||
ovl->InternalHigh = 0;
|
||||
event = ovl->hEvent;
|
||||
MemoryBarrier();
|
||||
ovl->Internal = touch_status_success;
|
||||
|
||||
if (event != NULL) {
|
||||
SetEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
memset(pending_reads, 0, sizeof(*pending_reads));
|
||||
}
|
||||
|
||||
static void touch_clear_auto_scan(struct uart *uart)
|
||||
{
|
||||
struct touch_auto_scan_state *auto_scan;
|
||||
|
||||
auto_scan = touch_get_auto_scan(uart);
|
||||
auto_scan->pos = 0;
|
||||
auto_scan->valid = false;
|
||||
}
|
||||
|
||||
static bool touch_read_available(struct uart *uart)
|
||||
{
|
||||
return uart->readable.pos > 0 || touch_get_auto_scan(uart)->valid;
|
||||
}
|
||||
|
||||
static size_t touch_current_depth(struct uart *uart)
|
||||
{
|
||||
size_t depth;
|
||||
struct touch_auto_scan_state *auto_scan;
|
||||
|
||||
depth = uart->readable.pos;
|
||||
auto_scan = touch_get_auto_scan(uart);
|
||||
|
||||
if (auto_scan->valid) {
|
||||
depth += sizeof(auto_scan->frame) - auto_scan->pos;
|
||||
}
|
||||
|
||||
return depth;
|
||||
}
|
||||
|
||||
static void touch_shift_read(struct uart *uart, struct iobuf *read)
|
||||
{
|
||||
struct touch_auto_scan_state *auto_scan;
|
||||
size_t read_avail;
|
||||
size_t frame_avail;
|
||||
size_t chunksz;
|
||||
|
||||
if (uart->readable.pos > 0) {
|
||||
iobuf_shift(read, &uart->readable);
|
||||
return;
|
||||
}
|
||||
|
||||
auto_scan = touch_get_auto_scan(uart);
|
||||
|
||||
if (!auto_scan->valid) {
|
||||
return;
|
||||
}
|
||||
|
||||
read_avail = read->nbytes - read->pos;
|
||||
frame_avail = sizeof(auto_scan->frame) - auto_scan->pos;
|
||||
chunksz = read_avail < frame_avail ? read_avail : frame_avail;
|
||||
|
||||
memcpy(&read->bytes[read->pos],
|
||||
&auto_scan->frame[auto_scan->pos],
|
||||
chunksz);
|
||||
read->pos += chunksz;
|
||||
auto_scan->pos += chunksz;
|
||||
|
||||
if (auto_scan->pos == sizeof(auto_scan->frame)) {
|
||||
auto_scan->pos = 0;
|
||||
auto_scan->valid = false;
|
||||
}
|
||||
}
|
||||
|
||||
static HRESULT touch_enqueue(
|
||||
struct uart *uart,
|
||||
CONDITION_VARIABLE *cv,
|
||||
const void *bytes,
|
||||
size_t nbytes)
|
||||
{
|
||||
HRESULT hr;
|
||||
|
||||
hr = iobuf_write(&uart->readable, bytes, nbytes);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
dprintf("Mai2 touch port %d: RX queue overflow (%u/%u bytes)\n",
|
||||
uart->port_no,
|
||||
(unsigned int) uart->readable.pos,
|
||||
(unsigned int) uart->readable.nbytes);
|
||||
} else {
|
||||
while (touch_read_available(uart) &&
|
||||
touch_get_pending_reads(uart)->count > 0) {
|
||||
touch_complete_pending_read(uart);
|
||||
}
|
||||
WakeConditionVariable(cv);
|
||||
}
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
static HRESULT touch_enqueue_reply(
|
||||
struct uart *uart,
|
||||
CONDITION_VARIABLE *cv,
|
||||
uint8_t side,
|
||||
uint8_t sensor,
|
||||
uint8_t command,
|
||||
uint8_t value)
|
||||
{
|
||||
uint8_t reply[6];
|
||||
|
||||
reply[0] = res_start;
|
||||
reply[1] = side;
|
||||
reply[2] = sensor;
|
||||
reply[3] = command;
|
||||
reply[4] = value;
|
||||
reply[5] = res_end;
|
||||
|
||||
return touch_enqueue(uart, cv, reply, sizeof(reply));
|
||||
}
|
||||
|
||||
static void touch_auto_scan(const uint8_t player, const uint8_t state[7])
|
||||
{
|
||||
struct uart *touch_uart = player == 1 ? &touch_1p_uart : &touch_2p_uart;
|
||||
touch_uart->readable.bytes[0] = res_start;
|
||||
memcpy(&touch_uart->readable.bytes[1], state, 7);
|
||||
touch_uart->readable.bytes[8] = res_end;
|
||||
touch_uart->readable.pos = 9;
|
||||
struct touch_auto_scan_state *auto_scan;
|
||||
struct uart *touch_uart;
|
||||
CRITICAL_SECTION *touch_lock;
|
||||
CONDITION_VARIABLE *touch_cv;
|
||||
uint8_t frame[9];
|
||||
|
||||
if (player == 1) {
|
||||
touch_uart = &touch_1p_uart;
|
||||
touch_lock = &touch_1p_lock;
|
||||
touch_cv = &touch_1p_cv;
|
||||
} else {
|
||||
touch_uart = &touch_2p_uart;
|
||||
touch_lock = &touch_2p_lock;
|
||||
touch_cv = &touch_2p_cv;
|
||||
}
|
||||
|
||||
if (touch_uart->readable.bytes == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
frame[0] = res_start;
|
||||
memcpy(&frame[1], state, 7);
|
||||
frame[8] = res_end;
|
||||
|
||||
EnterCriticalSection(touch_lock);
|
||||
if (!*touch_get_status_flag(touch_uart)) {
|
||||
LeaveCriticalSection(touch_lock);
|
||||
return;
|
||||
}
|
||||
auto_scan = touch_get_auto_scan(touch_uart);
|
||||
memcpy(auto_scan->frame, frame, sizeof(frame));
|
||||
auto_scan->pos = 0;
|
||||
auto_scan->valid = true;
|
||||
while (touch_read_available(touch_uart) &&
|
||||
touch_get_pending_reads(touch_uart)->count > 0) {
|
||||
touch_complete_pending_read(touch_uart);
|
||||
}
|
||||
WakeConditionVariable(touch_cv);
|
||||
LeaveCriticalSection(touch_lock);
|
||||
}
|
||||
|
||||
@@ -29,10 +29,3 @@ extern const char *sensor_map[34];
|
||||
const char *sensor_to_str(uint8_t sensor);
|
||||
|
||||
HRESULT touch_hook_init(const struct touch_config *cfg);
|
||||
static HRESULT touch_handle_irp(struct irp *irp);
|
||||
static HRESULT touch_handle_irp_locked(struct irp *irp, struct uart *uart);
|
||||
|
||||
/* Called in mai2io to send touch data.
|
||||
Similar to chuni slider_res_auto_scan, but the host does not require periodic updates.
|
||||
Touch data is sent only when there is a change. */
|
||||
static void touch_auto_scan(const uint8_t player, const uint8_t state[7]);
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include <limits.h>
|
||||
#include <process.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "mai2hook/touch.h"
|
||||
#include "mai2io/config.h"
|
||||
@@ -160,34 +161,30 @@ void mai2_io_touch_set_sens(uint8_t *bytes) {
|
||||
}
|
||||
|
||||
void mai2_io_touch_update(bool player1, bool player2) {
|
||||
if (mai2_io_cfg.debug_input_1p) {
|
||||
if (player1 && mai2_io_touch_1p_thread == NULL) {
|
||||
mai2_io_touch_1p_thread = (HANDLE)_beginthreadex(
|
||||
NULL, 0, mai2_io_touch_1p_thread_proc, _callback, 0, NULL);
|
||||
} else if (!player1 && mai2_io_touch_1p_thread != NULL) {
|
||||
mai2_io_touch_1p_stop_flag = true;
|
||||
if (player1 && mai2_io_touch_1p_thread == NULL) {
|
||||
mai2_io_touch_1p_thread = (HANDLE)_beginthreadex(
|
||||
NULL, 0, mai2_io_touch_1p_thread_proc, _callback, 0, NULL);
|
||||
} else if (!player1 && mai2_io_touch_1p_thread != NULL) {
|
||||
mai2_io_touch_1p_stop_flag = true;
|
||||
|
||||
WaitForSingleObject(mai2_io_touch_1p_thread, INFINITE);
|
||||
CloseHandle(mai2_io_touch_1p_thread);
|
||||
mai2_io_touch_1p_thread = NULL;
|
||||
WaitForSingleObject(mai2_io_touch_1p_thread, INFINITE);
|
||||
CloseHandle(mai2_io_touch_1p_thread);
|
||||
mai2_io_touch_1p_thread = NULL;
|
||||
|
||||
mai2_io_touch_1p_stop_flag = false;
|
||||
}
|
||||
mai2_io_touch_1p_stop_flag = false;
|
||||
}
|
||||
|
||||
if (mai2_io_cfg.debug_input_2p) {
|
||||
if (player2 && mai2_io_touch_2p_thread == NULL) {
|
||||
mai2_io_touch_2p_thread = (HANDLE)_beginthreadex(
|
||||
NULL, 0, mai2_io_touch_2p_thread_proc, _callback, 0, NULL);
|
||||
} else if (!player2 && mai2_io_touch_2p_thread != NULL) {
|
||||
mai2_io_touch_2p_stop_flag = true;
|
||||
if (player2 && mai2_io_touch_2p_thread == NULL) {
|
||||
mai2_io_touch_2p_thread = (HANDLE)_beginthreadex(
|
||||
NULL, 0, mai2_io_touch_2p_thread_proc, _callback, 0, NULL);
|
||||
} else if (!player2 && mai2_io_touch_2p_thread != NULL) {
|
||||
mai2_io_touch_2p_stop_flag = true;
|
||||
|
||||
WaitForSingleObject(mai2_io_touch_2p_thread, INFINITE);
|
||||
CloseHandle(mai2_io_touch_2p_thread);
|
||||
mai2_io_touch_2p_thread = NULL;
|
||||
WaitForSingleObject(mai2_io_touch_2p_thread, INFINITE);
|
||||
CloseHandle(mai2_io_touch_2p_thread);
|
||||
mai2_io_touch_2p_thread = NULL;
|
||||
|
||||
mai2_io_touch_2p_stop_flag = false;
|
||||
}
|
||||
mai2_io_touch_2p_stop_flag = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -197,14 +194,18 @@ static unsigned int __stdcall mai2_io_touch_1p_thread_proc(void *ctx) {
|
||||
while (!mai2_io_touch_1p_stop_flag) {
|
||||
uint8_t state[7] = {0, 0, 0, 0, 0, 0, 0};
|
||||
|
||||
for (int i = 0; i < 34; i++) {
|
||||
if (GetAsyncKeyState(mai2_io_cfg.vk_1p_touch[i])) {
|
||||
int byteIndex = i / 5;
|
||||
int bitIndex = i % 5;
|
||||
state[byteIndex] |= (1 << bitIndex);
|
||||
if (mai2_io_cfg.debug_input_1p) {
|
||||
for (int i = 0; i < 34; i++) {
|
||||
if (GetAsyncKeyState(mai2_io_cfg.vk_1p_touch[i])) {
|
||||
int byteIndex = i / 5;
|
||||
int bitIndex = i % 5;
|
||||
state[byteIndex] |= (1 << bitIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
callback(1, state);
|
||||
|
||||
Sleep(1);
|
||||
}
|
||||
return 0;
|
||||
@@ -216,14 +217,18 @@ static unsigned int __stdcall mai2_io_touch_2p_thread_proc(void *ctx) {
|
||||
while (!mai2_io_touch_2p_stop_flag) {
|
||||
uint8_t state[7] = {0, 0, 0, 0, 0, 0, 0};
|
||||
|
||||
for (int i = 0; i < 34; i++) {
|
||||
if (GetAsyncKeyState(mai2_io_cfg.vk_2p_touch[i])) {
|
||||
int byteIndex = i / 5;
|
||||
int bitIndex = i % 5;
|
||||
state[byteIndex] |= (1 << bitIndex);
|
||||
if (mai2_io_cfg.debug_input_2p) {
|
||||
for (int i = 0; i < 34; i++) {
|
||||
if (GetAsyncKeyState(mai2_io_cfg.vk_2p_touch[i])) {
|
||||
int byteIndex = i / 5;
|
||||
int bitIndex = i % 5;
|
||||
state[byteIndex] |= (1 << bitIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
callback(2, state);
|
||||
|
||||
Sleep(1);
|
||||
}
|
||||
return 0;
|
||||
|
||||
@@ -120,7 +120,8 @@ void mai2_io_touch_set_sens(uint8_t *bytes);
|
||||
*
|
||||
* This function determines whether the game is ready to accept touch input based on the states of player 1 and player 2.
|
||||
* If the game is ready, it creates or stops the corresponding threads to handle touch data for each player.
|
||||
* Whether or not threads are created for each player is controlled by `mai2_io_cfg.debug_input_1p` and `mai2_io_cfg.debug_input_2p` configuration.
|
||||
* The `debug_input_1p` and `debug_input_2p` settings only control whether keyboard input is mapped into touch bits.
|
||||
* When a player is active, the thread continues to send idle all-zero frames even with debug input disabled.
|
||||
*
|
||||
* @param player1 If `true`, indicates the game is ready to accept touch data from player 1, `false` means the game is not ready.
|
||||
* @param player2 If `true`, indicates the game is ready to accept touch data from player 2, `false` means the game is not ready.
|
||||
|
||||
Reference in New Issue
Block a user