Merge branch 'github-main' into r3d-spice2x
Continuous Integration / Build (push) Failing after 5m3s
Continuous Integration / Build (push) Failing after 5m3s
This commit is contained in:
@@ -58,17 +58,18 @@ namespace clipboard {
|
||||
return;
|
||||
}
|
||||
|
||||
// If the screenshot button is print screen, the OpenClipboard call seems to fail often if we only
|
||||
// call it once, probably due to a race condition. So, we can try calling a lot until we can open it.
|
||||
// print screen key leaves the OS briefly holding the clipboard, so retry
|
||||
// spinning without yielding starves the thread we are waiting on and looks like a hang
|
||||
bool clipboard_open = false;
|
||||
for (int i = 0; i < 1000000; i++) {
|
||||
for (int i = 0; i < 100; i++) {
|
||||
if (OpenClipboard(nullptr)) {
|
||||
clipboard_open = true;
|
||||
break;
|
||||
}
|
||||
Sleep(5);
|
||||
}
|
||||
if (!clipboard_open) {
|
||||
log_warning("clipboard", "Failed to open clipboard");
|
||||
log_warning("clipboard", "failed to open clipboard");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
+24
-55
@@ -25,10 +25,8 @@ static double CARD_INSERT_TIME[2] = {0, 0};
|
||||
static double CARD_INSERT_TIMEOUT = 2.0;
|
||||
static char CARD_INSERT_UID[2][8] = {{0}, {0}};
|
||||
static char CARD_INSERT_UID_ENABLE[2] = {false, false};
|
||||
static int COIN_STOCK = 0;
|
||||
static bool COIN_BLOCK = false;
|
||||
static std::thread *COIN_INPUT_THREAD;
|
||||
static bool COIN_INPUT_THREAD_ACTIVE = false;
|
||||
static std::atomic_int COIN_STOCK {0};
|
||||
static std::atomic_bool COIN_BLOCK {false};
|
||||
static uint16_t KEYPAD_STATE[] = {0, 0};
|
||||
static uint16_t KEYPAD_STATE_OVERRIDES[] = {0, 0};
|
||||
static uint16_t KEYPAD_STATE_OVERRIDES_BT5[] = {0, 0};
|
||||
@@ -292,78 +290,49 @@ bool eamuse_card_insert_consume(int active_count, int unit_id) {
|
||||
}
|
||||
|
||||
bool eamuse_coin_get_block() {
|
||||
return COIN_BLOCK;
|
||||
return COIN_BLOCK.load(std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
void eamuse_coin_set_block(bool block) {
|
||||
COIN_BLOCK = block;
|
||||
COIN_BLOCK.store(block, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
int eamuse_coin_get_stock() {
|
||||
return COIN_STOCK;
|
||||
return COIN_STOCK.load(std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
void eamuse_coin_set_stock(int amount) {
|
||||
COIN_STOCK = amount;
|
||||
COIN_STOCK.store(amount, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
bool eamuse_coin_consume(int amount) {
|
||||
if (COIN_STOCK < amount) {
|
||||
return false;
|
||||
} else {
|
||||
COIN_STOCK -= amount;
|
||||
return true;
|
||||
auto stock = COIN_STOCK.load(std::memory_order_relaxed);
|
||||
while (stock >= amount) {
|
||||
if (COIN_STOCK.compare_exchange_weak(
|
||||
stock,
|
||||
stock - amount,
|
||||
std::memory_order_relaxed)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int eamuse_coin_consume_stock() {
|
||||
int stock = COIN_STOCK;
|
||||
COIN_STOCK = 0;
|
||||
return stock;
|
||||
return COIN_STOCK.exchange(0, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
int eamuse_coin_add() {
|
||||
return ++COIN_STOCK;
|
||||
return COIN_STOCK.fetch_add(1, std::memory_order_relaxed) + 1;
|
||||
}
|
||||
|
||||
void eamuse_coin_start_thread() {
|
||||
|
||||
// set active
|
||||
COIN_INPUT_THREAD_ACTIVE = true;
|
||||
|
||||
// create thread
|
||||
COIN_INPUT_THREAD = new std::thread([]() {
|
||||
auto overlay_buttons = games::get_buttons_overlay(eamuse_get_game());
|
||||
static bool COIN_INPUT_KEY_STATE = false;
|
||||
while (COIN_INPUT_THREAD_ACTIVE) {
|
||||
|
||||
// check input key
|
||||
if (overlay_buttons && GameAPI::Buttons::getState(RI_MGR, overlay_buttons->at(
|
||||
games::OverlayButtons::InsertCoin))) {
|
||||
if (!COIN_INPUT_KEY_STATE) {
|
||||
if (COIN_BLOCK)
|
||||
log_info("eamuse", "coin inserted while blocked");
|
||||
else {
|
||||
log_info("eamuse", "coin insert");
|
||||
COIN_STOCK++;
|
||||
}
|
||||
}
|
||||
COIN_INPUT_KEY_STATE = true;
|
||||
} else {
|
||||
COIN_INPUT_KEY_STATE = false;
|
||||
}
|
||||
|
||||
// once every two frames
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1000 / 30));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void eamuse_coin_stop_thread() {
|
||||
COIN_INPUT_THREAD_ACTIVE = false;
|
||||
COIN_INPUT_THREAD->join();
|
||||
delete COIN_INPUT_THREAD;
|
||||
COIN_INPUT_THREAD = nullptr;
|
||||
void eamuse_coin_insert() {
|
||||
if (COIN_BLOCK.load(std::memory_order_relaxed)) {
|
||||
log_info("eamuse", "coin inserted while blocked");
|
||||
} else {
|
||||
log_info("eamuse", "coin insert");
|
||||
COIN_STOCK.fetch_add(1, std::memory_order_relaxed);
|
||||
}
|
||||
}
|
||||
|
||||
void eamuse_pin_macro_start_thread() {
|
||||
|
||||
@@ -58,9 +58,7 @@ bool eamuse_coin_consume(int amount);
|
||||
int eamuse_coin_consume_stock();
|
||||
|
||||
int eamuse_coin_add();
|
||||
|
||||
void eamuse_coin_start_thread();
|
||||
void eamuse_coin_stop_thread();
|
||||
void eamuse_coin_insert();
|
||||
|
||||
void eamuse_pin_macro_start_thread();
|
||||
void eamuse_pin_macro_stop_thread();
|
||||
|
||||
@@ -0,0 +1,154 @@
|
||||
#include "hotkeys.h"
|
||||
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <mutex>
|
||||
#include <stop_token>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include "games/io.h"
|
||||
#include "launcher/superexit.h"
|
||||
#include "misc/eamuse.h"
|
||||
#include "overlay/overlay.h"
|
||||
#include "rawinput/rawinput.h"
|
||||
#include "util/logging.h"
|
||||
|
||||
namespace hotkeys {
|
||||
|
||||
namespace {
|
||||
|
||||
// 8 ms targets short screenshot pulses; sleep_for may use a coarser scheduler
|
||||
// interval during early boot or when process timer adjustments are disabled
|
||||
constexpr auto MIN_SAMPLE_INTERVAL = std::chrono::milliseconds(8);
|
||||
|
||||
std::atomic_bool SCREENSHOT_PENDING {false};
|
||||
std::mutex INPUT_MUTEX;
|
||||
bool INPUT_ENABLED = false;
|
||||
bool RAW_INPUT_ENABLED = false;
|
||||
std::jthread WORKER;
|
||||
|
||||
bool read_button(std::vector<Button> *buttons, size_t index) {
|
||||
// getState retains each binding's focus, modifier, inversion, and debounce policy
|
||||
return RI_MGR && buttons && index < buttons->size() &&
|
||||
GameAPI::Buttons::getState(RI_MGR, buttons->at(index));
|
||||
}
|
||||
|
||||
bool read_alt_f4() {
|
||||
// preserve both legacy detection paths whenever raw input is available
|
||||
bool pressed = (GetAsyncKeyState(VK_MENU) & 0x8000) != 0 &&
|
||||
(GetAsyncKeyState(VK_F4) & 0x8000) != 0;
|
||||
if (!RAW_INPUT_ENABLED || !RI_MGR) {
|
||||
return pressed;
|
||||
}
|
||||
return pressed || RI_MGR->keyboard_combo_pressed(VK_MENU, VK_F4);
|
||||
}
|
||||
|
||||
bool rising_edge(bool current, bool &previous) {
|
||||
const bool edge = current && !previous;
|
||||
previous = current;
|
||||
return edge;
|
||||
}
|
||||
|
||||
void run(std::stop_token stop_token) {
|
||||
bool screenshot_previous = false;
|
||||
bool coin_previous = false;
|
||||
|
||||
while (!stop_token.stop_requested()) {
|
||||
bool coin_edge = false;
|
||||
bool super_exit_current = false;
|
||||
bool alt_f4_current = false;
|
||||
|
||||
{
|
||||
// lifecycle functions hold this mutex until raw-input polling is complete
|
||||
std::lock_guard<std::mutex> lock(INPUT_MUTEX);
|
||||
if (INPUT_ENABLED || RAW_INPUT_ENABLED) {
|
||||
auto *buttons = games::get_buttons_overlay(eamuse_get_game());
|
||||
const bool screenshot_down = INPUT_ENABLED && read_button(
|
||||
buttons, games::OverlayButtons::Screenshot);
|
||||
const bool coin_current = INPUT_ENABLED && read_button(
|
||||
buttons, games::OverlayButtons::InsertCoin);
|
||||
const bool super_exit_down = RAW_INPUT_ENABLED && read_button(
|
||||
buttons, games::OverlayButtons::SuperExit);
|
||||
|
||||
// global_hotkeys_triggered takes OVERLAY_MUTEX, then the overlay's
|
||||
// hotkeys_mutex; its button reads may then take device mutexes. polling
|
||||
// every mapped-input tick is intentional so HotkeyToggle releases cannot
|
||||
// be missed when the render thread stalls.
|
||||
const bool gate_active = overlay::global_hotkeys_triggered();
|
||||
const bool screenshot_current = screenshot_down && gate_active;
|
||||
super_exit_current = super_exit_down && gate_active;
|
||||
|
||||
if (rising_edge(screenshot_current, screenshot_previous)) {
|
||||
SCREENSHOT_PENDING.store(true, std::memory_order_relaxed);
|
||||
}
|
||||
coin_edge = rising_edge(coin_current, coin_previous);
|
||||
} else {
|
||||
screenshot_previous = false;
|
||||
coin_previous = false;
|
||||
}
|
||||
|
||||
alt_f4_current = read_alt_f4();
|
||||
}
|
||||
|
||||
if (coin_edge) {
|
||||
eamuse_coin_insert();
|
||||
}
|
||||
|
||||
// pass held state so returning focus can exit without another key press
|
||||
superexit::handle_hotkeys(alt_f4_current, super_exit_current);
|
||||
|
||||
std::this_thread::sleep_for(MIN_SAMPLE_INTERVAL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void start() {
|
||||
if (WORKER.joinable()) {
|
||||
return;
|
||||
}
|
||||
|
||||
SCREENSHOT_PENDING.store(false, std::memory_order_relaxed);
|
||||
WORKER = std::jthread(run);
|
||||
log_info("hotkeys", "sampler started");
|
||||
}
|
||||
|
||||
void enable_raw_input() {
|
||||
std::lock_guard<std::mutex> lock(INPUT_MUTEX);
|
||||
RAW_INPUT_ENABLED = true;
|
||||
}
|
||||
|
||||
void enable_input() {
|
||||
std::lock_guard<std::mutex> lock(INPUT_MUTEX);
|
||||
SCREENSHOT_PENDING.store(false, std::memory_order_relaxed);
|
||||
INPUT_ENABLED = true;
|
||||
log_info("hotkeys", "configured input enabled");
|
||||
}
|
||||
|
||||
void disable_input() {
|
||||
std::lock_guard<std::mutex> lock(INPUT_MUTEX);
|
||||
INPUT_ENABLED = false;
|
||||
SCREENSHOT_PENDING.store(false, std::memory_order_relaxed);
|
||||
log_info("hotkeys", "configured input disabled");
|
||||
}
|
||||
|
||||
void disable_raw_input() {
|
||||
std::lock_guard<std::mutex> lock(INPUT_MUTEX);
|
||||
RAW_INPUT_ENABLED = false;
|
||||
}
|
||||
|
||||
void stop() {
|
||||
if (!WORKER.joinable()) {
|
||||
return;
|
||||
}
|
||||
WORKER.request_stop();
|
||||
WORKER.join();
|
||||
log_info("hotkeys", "sampler stopped");
|
||||
}
|
||||
|
||||
bool consume_screenshot() {
|
||||
return SCREENSHOT_PENDING.exchange(false, std::memory_order_relaxed);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
namespace hotkeys {
|
||||
|
||||
// ALT+F4 monitoring spans boot and teardown; configured actions are enabled separately
|
||||
void start();
|
||||
void enable_raw_input();
|
||||
void enable_input();
|
||||
void disable_input();
|
||||
void disable_raw_input();
|
||||
void stop();
|
||||
bool consume_screenshot();
|
||||
}
|
||||
Reference in New Issue
Block a user