mirror of
https://github.com/spice2x/spice2x.github.io.git
synced 2026-08-01 22:30:42 -07:00
api: check for window focus when accepting input (#317)
## Link to GitHub Issue, if one exists Fixes #139 ## Description of change Add an option for detecting window focus when accepting input. The new default will be to have the following: * naive bindings will check for window focus * rawinput binds are not affected, will continue to be accepted regardless of focus Other options (always / never choices) can be picked by the user to tweak the behavior if needed. Clean up usage of `0xff` constant to represent invalid naive vkey. ## Testing Still in progress, especially in regards to performance. On a i7-9700 system, 10000 calls to `superexit::has_focus()` takes 250-350 microseconds, so this should not be a big deal at all.
This commit is contained in:
+34
-4
@@ -1,5 +1,8 @@
|
|||||||
#include "api.h"
|
#include "api.h"
|
||||||
|
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
|
#include "launcher/superexit.h"
|
||||||
#include "rawinput/rawinput.h"
|
#include "rawinput/rawinput.h"
|
||||||
#include "rawinput/piuio.h"
|
#include "rawinput/piuio.h"
|
||||||
#include "util/time.h"
|
#include "util/time.h"
|
||||||
@@ -64,18 +67,29 @@ GameAPI::Buttons::State GameAPI::Buttons::getState(rawinput::RawInputManager *ma
|
|||||||
auto current_button = &_button;
|
auto current_button = &_button;
|
||||||
auto alternatives = check_alts ? ¤t_button->getAlternatives() : nullptr;
|
auto alternatives = check_alts ? ¤t_button->getAlternatives() : nullptr;
|
||||||
unsigned int button_count = 0;
|
unsigned int button_count = 0;
|
||||||
|
std::optional<bool> window_has_focus;
|
||||||
while (true) {
|
while (true) {
|
||||||
|
|
||||||
// naive behavior
|
// naive behavior
|
||||||
if (current_button->isNaive()) {
|
if (current_button->isNaive()) {
|
||||||
|
GameAPI::Buttons::State state;
|
||||||
|
auto vkey = current_button->getVKey();
|
||||||
|
|
||||||
|
// check for focus
|
||||||
|
if (vkey != INVALID_VKEY && rawinput::NAIVE_REQUIRE_FOCUS) {
|
||||||
|
if (!window_has_focus.has_value()) {
|
||||||
|
window_has_focus = superexit::has_focus();
|
||||||
|
}
|
||||||
|
if (!window_has_focus.value()) {
|
||||||
|
vkey = INVALID_VKEY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// read
|
// read
|
||||||
auto vkey = current_button->getVKey();
|
if (vkey == INVALID_VKEY) {
|
||||||
GameAPI::Buttons::State state;
|
|
||||||
if (vkey == 0xFF) {
|
|
||||||
state = BUTTON_NOT_PRESSED;
|
state = BUTTON_NOT_PRESSED;
|
||||||
} else {
|
} else {
|
||||||
state = (GetAsyncKeyState(current_button->getVKey()) & 0x8000) ? BUTTON_PRESSED : BUTTON_NOT_PRESSED;
|
state = (GetAsyncKeyState(vkey) & 0x8000) ? BUTTON_PRESSED : BUTTON_NOT_PRESSED;
|
||||||
}
|
}
|
||||||
|
|
||||||
// invert
|
// invert
|
||||||
@@ -106,6 +120,16 @@ GameAPI::Buttons::State GameAPI::Buttons::getState(rawinput::RawInputManager *ma
|
|||||||
auto &devid = current_button->getDeviceIdentifier();
|
auto &devid = current_button->getDeviceIdentifier();
|
||||||
auto device = manager->devices_get(devid, false); // TODO: fix to update only
|
auto device = manager->devices_get(devid, false); // TODO: fix to update only
|
||||||
|
|
||||||
|
// check for focus
|
||||||
|
if (device && rawinput::RAWINPUT_REQUIRE_FOCUS) {
|
||||||
|
if (!window_has_focus.has_value()) {
|
||||||
|
window_has_focus = superexit::has_focus();
|
||||||
|
}
|
||||||
|
if (!window_has_focus.value()) {
|
||||||
|
device = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// get state if device was marked as updated
|
// get state if device was marked as updated
|
||||||
GameAPI::Buttons::State state = current_button->getLastState();
|
GameAPI::Buttons::State state = current_button->getLastState();
|
||||||
double *last_up = nullptr;
|
double *last_up = nullptr;
|
||||||
@@ -571,6 +595,12 @@ float GameAPI::Analogs::getState(rawinput::Device *device, Analog &analog) {
|
|||||||
switch (device->type) {
|
switch (device->type) {
|
||||||
case rawinput::MOUSE: {
|
case rawinput::MOUSE: {
|
||||||
|
|
||||||
|
// check for focus
|
||||||
|
if (rawinput::NAIVE_REQUIRE_FOCUS && !superexit::has_focus()) {
|
||||||
|
value = analog.getLastState();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
// get mouse position
|
// get mouse position
|
||||||
auto mouse = device->mouseInfo;
|
auto mouse = device->mouseInfo;
|
||||||
long pos;
|
long pos;
|
||||||
|
|||||||
@@ -284,7 +284,7 @@ std::string Button::getDisplayString(rawinput::RawInputManager* manager) {
|
|||||||
std::string vKeyString = fmt::format("{:#x}", vKey);
|
std::string vKeyString = fmt::format("{:#x}", vKey);
|
||||||
|
|
||||||
// device must be existing
|
// device must be existing
|
||||||
if (this->device_identifier.empty() && vKey == 0xFF) {
|
if (this->device_identifier.empty() && vKey == INVALID_VKEY) {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -33,12 +33,14 @@ enum ButtonAnalogType {
|
|||||||
|
|
||||||
extern const char *ButtonAnalogTypeStr[];
|
extern const char *ButtonAnalogTypeStr[];
|
||||||
|
|
||||||
|
constexpr unsigned short INVALID_VKEY = UINT16_C(0xFF);
|
||||||
|
|
||||||
class Button {
|
class Button {
|
||||||
private:
|
private:
|
||||||
std::vector<Button> alternatives;
|
std::vector<Button> alternatives;
|
||||||
std::string name;
|
std::string name;
|
||||||
std::string device_identifier = "";
|
std::string device_identifier = "";
|
||||||
unsigned short vKey = 0xFF;
|
unsigned short vKey = INVALID_VKEY;
|
||||||
ButtonAnalogType analog_type = BAT_NONE;
|
ButtonAnalogType analog_type = BAT_NONE;
|
||||||
double debounce_up = 0.0;
|
double debounce_up = 0.0;
|
||||||
double debounce_down = 0.0;
|
double debounce_down = 0.0;
|
||||||
@@ -68,12 +70,12 @@ public:
|
|||||||
if (this->override_enabled) {
|
if (this->override_enabled) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (this->vKey != 0xFF) {
|
if (this->vKey != INVALID_VKEY) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto &alternative : this->alternatives) {
|
for (auto &alternative : this->alternatives) {
|
||||||
if (alternative.vKey != 0xFF) {
|
if (alternative.vKey != INVALID_VKEY) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -82,7 +84,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
inline void clearBindings() {
|
inline void clearBindings() {
|
||||||
vKey = 0xFF;
|
vKey = INVALID_VKEY;
|
||||||
alternatives.clear();
|
alternatives.clear();
|
||||||
device_identifier = "";
|
device_identifier = "";
|
||||||
analog_type = BAT_NONE;
|
analog_type = BAT_NONE;
|
||||||
|
|||||||
@@ -1077,6 +1077,17 @@ int main_implementation(int argc, char *argv[]) {
|
|||||||
if (options[launcher::Options::spice2x_EnableSMXDedicab].value_bool()) {
|
if (options[launcher::Options::spice2x_EnableSMXDedicab].value_bool()) {
|
||||||
rawinput::ENABLE_SMX_DEDICAB = true;
|
rawinput::ENABLE_SMX_DEDICAB = true;
|
||||||
}
|
}
|
||||||
|
if (options[launcher::Options::InputRequiresFocus].is_active()) {
|
||||||
|
const auto &text = options[launcher::Options::InputRequiresFocus].value_text();
|
||||||
|
if (text == "always") {
|
||||||
|
rawinput::NAIVE_REQUIRE_FOCUS = true;
|
||||||
|
rawinput::RAWINPUT_REQUIRE_FOCUS = true;
|
||||||
|
} else if (text == "never") {
|
||||||
|
rawinput::NAIVE_REQUIRE_FOCUS = false;
|
||||||
|
rawinput::RAWINPUT_REQUIRE_FOCUS = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (options[launcher::Options::MidiAlgoVer].is_active()) {
|
if (options[launcher::Options::MidiAlgoVer].is_active()) {
|
||||||
if (options[launcher::Options::MidiAlgoVer].value_text() == "legacy") {
|
if (options[launcher::Options::MidiAlgoVer].value_text() == "legacy") {
|
||||||
midi_algo = rawinput::MidiNoteAlgorithm::LEGACY;
|
midi_algo = rawinput::MidiNoteAlgorithm::LEGACY;
|
||||||
|
|||||||
@@ -2170,6 +2170,23 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
|||||||
.setting_name = "20",
|
.setting_name = "20",
|
||||||
.category = "I/O Options",
|
.category = "I/O Options",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
// InputRequiresFocus
|
||||||
|
.title = "Input Requires Focus",
|
||||||
|
.name = "inputfocus",
|
||||||
|
.desc =
|
||||||
|
"Determine input behavior when game window is not in focus.\n\n"
|
||||||
|
"never: allow input regardless of focus\n"
|
||||||
|
"naive (default): only naive bindings check for window focus, rawinput binds are always allowed\n"
|
||||||
|
"always: only process input when window is in focus",
|
||||||
|
.type = OptionType::Enum,
|
||||||
|
.category = "I/O Options",
|
||||||
|
.elements = {
|
||||||
|
{"never", ""},
|
||||||
|
{"naive", ""},
|
||||||
|
{"always", ""},
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
// NostalgiaPoke
|
// NostalgiaPoke
|
||||||
.title = "Nost Screen Poke",
|
.title = "Nost Screen Poke",
|
||||||
|
|||||||
@@ -233,6 +233,7 @@ namespace launcher {
|
|||||||
IIDXRecQuality,
|
IIDXRecQuality,
|
||||||
MidiAlgoVer,
|
MidiAlgoVer,
|
||||||
MidiNoteSustain,
|
MidiNoteSustain,
|
||||||
|
InputRequiresFocus,
|
||||||
NostalgiaPoke,
|
NostalgiaPoke,
|
||||||
ForceBackBufferCount,
|
ForceBackBufferCount,
|
||||||
SDVXWindowedSubscreenSize,
|
SDVXWindowedSubscreenSize,
|
||||||
|
|||||||
@@ -28,6 +28,8 @@ namespace rawinput {
|
|||||||
int TOUCHSCREEN_RANGE_X = 0;
|
int TOUCHSCREEN_RANGE_X = 0;
|
||||||
int TOUCHSCREEN_RANGE_Y = 0;
|
int TOUCHSCREEN_RANGE_Y = 0;
|
||||||
bool DUMP_HID_DEVICES_TO_LOG = false;
|
bool DUMP_HID_DEVICES_TO_LOG = false;
|
||||||
|
bool NAIVE_REQUIRE_FOCUS = true;
|
||||||
|
bool RAWINPUT_REQUIRE_FOCUS = false;
|
||||||
|
|
||||||
// set this to something slightly longer than 16.67ms (60Hz) so that I/O can pick it up
|
// set this to something slightly longer than 16.67ms (60Hz) so that I/O can pick it up
|
||||||
// this may need to be adjusted for each game in the future if there is a game that polls less
|
// this may need to be adjusted for each game in the future if there is a game that polls less
|
||||||
|
|||||||
@@ -28,6 +28,9 @@ namespace rawinput {
|
|||||||
|
|
||||||
extern uint32_t MIDI_NOTE_SUSTAIN;
|
extern uint32_t MIDI_NOTE_SUSTAIN;
|
||||||
|
|
||||||
|
extern bool NAIVE_REQUIRE_FOCUS;
|
||||||
|
extern bool RAWINPUT_REQUIRE_FOCUS;
|
||||||
|
|
||||||
struct DeviceCallback {
|
struct DeviceCallback {
|
||||||
void *data;
|
void *data;
|
||||||
std::function<void(void*, Device*)> f;
|
std::function<void(void*, Device*)> f;
|
||||||
|
|||||||
Reference in New Issue
Block a user