diff options
Diffstat (limited to 'src/emu/input.cpp')
-rw-r--r-- | src/emu/input.cpp | 1085 |
1 files changed, 307 insertions, 778 deletions
diff --git a/src/emu/input.cpp b/src/emu/input.cpp index 0265b021e9a..5eeee806f9d 100644 --- a/src/emu/input.cpp +++ b/src/emu/input.cpp @@ -2,7 +2,7 @@ // copyright-holders:Aaron Giles /*************************************************************************** - input.c + input.cpp Handle input from the user. @@ -20,25 +20,11 @@ #include "emu.h" #include "inputdev.h" +#include "corestr.h" -//************************************************************************** -// CONSTANTS -//************************************************************************** - -// invalid memory value for axis polling -const s32 INVALID_AXIS_VALUE = 0x7fffffff; - -// additional expanded input codes for sequences -const input_code input_seq::end_code(DEVICE_CLASS_INTERNAL, 0, ITEM_CLASS_INVALID, ITEM_MODIFIER_NONE, ITEM_ID_SEQ_END); -const input_code input_seq::default_code(DEVICE_CLASS_INTERNAL, 0, ITEM_CLASS_INVALID, ITEM_MODIFIER_NONE, ITEM_ID_SEQ_DEFAULT); -const input_code input_seq::not_code(DEVICE_CLASS_INTERNAL, 0, ITEM_CLASS_INVALID, ITEM_MODIFIER_NONE, ITEM_ID_SEQ_NOT); -const input_code input_seq::or_code(DEVICE_CLASS_INTERNAL, 0, ITEM_CLASS_INVALID, ITEM_MODIFIER_NONE, ITEM_ID_SEQ_OR); - -// constant sequences -const input_seq input_seq::empty_seq; - +namespace { //************************************************************************** // TYPE DEFINITIONS @@ -49,24 +35,26 @@ const input_seq input_seq::empty_seq; // simple class to match codes to strings struct code_string_table { - u32 operator[](const char *string) const + static inline constexpr u32 SENTINEL = ~u32(0); + + u32 operator[](std::string_view string) const { - for (const code_string_table *current = this; current->m_code != ~0; current++) - if (strcmp(current->m_string, string) == 0) + for (const code_string_table *current = this; current->m_code != SENTINEL; current++) + if (current->m_string == string) return current->m_code; - return ~0; + return SENTINEL; } const char *operator[](u32 code) const { - for (const code_string_table *current = this; current->m_code != ~0; current++) + for (const code_string_table *current = this; current->m_code != SENTINEL; current++) if (current->m_code == code) return current->m_string; return nullptr; } - u32 m_code; - const char * m_string; + u32 m_code; + const char * m_string; }; @@ -76,60 +64,62 @@ struct code_string_table //************************************************************************** // token strings for device classes -static const code_string_table devclass_token_table[] = +const code_string_table devclass_token_table[] = { - { DEVICE_CLASS_KEYBOARD, "KEYCODE" }, - { DEVICE_CLASS_MOUSE, "MOUSECODE" }, - { DEVICE_CLASS_LIGHTGUN, "GUNCODE" }, - { DEVICE_CLASS_JOYSTICK, "JOYCODE" }, - { ~0U, "UNKCODE" } + { DEVICE_CLASS_KEYBOARD, "KEYCODE" }, + { DEVICE_CLASS_MOUSE, "MOUSECODE" }, + { DEVICE_CLASS_LIGHTGUN, "GUNCODE" }, + { DEVICE_CLASS_JOYSTICK, "JOYCODE" }, + { code_string_table::SENTINEL, "UNKCODE" } }; // friendly strings for device classes -static const code_string_table devclass_string_table[] = +const code_string_table devclass_string_table[] = { - { DEVICE_CLASS_KEYBOARD, "Kbd" }, - { DEVICE_CLASS_MOUSE, "Mouse" }, - { DEVICE_CLASS_LIGHTGUN, "Gun" }, - { DEVICE_CLASS_JOYSTICK, "Joy" }, - { ~0U, "Unk" } + { DEVICE_CLASS_KEYBOARD, "Kbd" }, + { DEVICE_CLASS_MOUSE, "Mouse" }, + { DEVICE_CLASS_LIGHTGUN, "Gun" }, + { DEVICE_CLASS_JOYSTICK, "Joy" }, + { code_string_table::SENTINEL, "Unk" } }; // token strings for item modifiers -static const code_string_table modifier_token_table[] = +const code_string_table modifier_token_table[] = { - { ITEM_MODIFIER_POS, "POS" }, - { ITEM_MODIFIER_NEG, "NEG" }, - { ITEM_MODIFIER_LEFT, "LEFT" }, - { ITEM_MODIFIER_RIGHT, "RIGHT" }, - { ITEM_MODIFIER_UP, "UP" }, - { ITEM_MODIFIER_DOWN, "DOWN" }, - { ~0U, "" } + { ITEM_MODIFIER_REVERSE, "REVERSE" }, + { ITEM_MODIFIER_POS, "POS" }, + { ITEM_MODIFIER_NEG, "NEG" }, + { ITEM_MODIFIER_LEFT, "LEFT" }, + { ITEM_MODIFIER_RIGHT, "RIGHT" }, + { ITEM_MODIFIER_UP, "UP" }, + { ITEM_MODIFIER_DOWN, "DOWN" }, + { code_string_table::SENTINEL, "" } }; // friendly strings for item modifiers -static const code_string_table modifier_string_table[] = +const code_string_table modifier_string_table[] = { - { ITEM_MODIFIER_POS, "+" }, - { ITEM_MODIFIER_NEG, "-" }, - { ITEM_MODIFIER_LEFT, "Left" }, - { ITEM_MODIFIER_RIGHT, "Right" }, - { ITEM_MODIFIER_UP, "Up" }, - { ITEM_MODIFIER_DOWN, "Down" }, - { ~0U, "" } + { ITEM_MODIFIER_REVERSE, "Reverse" }, + { ITEM_MODIFIER_POS, "+" }, + { ITEM_MODIFIER_NEG, "-" }, + { ITEM_MODIFIER_LEFT, "Left" }, + { ITEM_MODIFIER_RIGHT, "Right" }, + { ITEM_MODIFIER_UP, "Up" }, + { ITEM_MODIFIER_DOWN, "Down" }, + { code_string_table::SENTINEL, "" } }; // token strings for item classes -static const code_string_table itemclass_token_table[] = +const code_string_table itemclass_token_table[] = { - { ITEM_CLASS_SWITCH, "SWITCH" }, - { ITEM_CLASS_ABSOLUTE, "ABSOLUTE" }, - { ITEM_CLASS_RELATIVE, "RELATIVE" }, - { ~0U, "" } + { ITEM_CLASS_SWITCH, "SWITCH" }, + { ITEM_CLASS_ABSOLUTE, "ABSOLUTE" }, + { ITEM_CLASS_RELATIVE, "RELATIVE" }, + { code_string_table::SENTINEL, "" } }; // token strings for standard item ids -static const code_string_table itemid_token_table[] = +const code_string_table itemid_token_table[] = { // standard keyboard codes { ITEM_ID_A, "A" }, @@ -363,188 +353,50 @@ static const code_string_table itemid_token_table[] = { ITEM_ID_ADD_RELATIVE15,"ADDREL15" }, { ITEM_ID_ADD_RELATIVE16,"ADDREL16" }, - { ~0U, nullptr } + { code_string_table::SENTINEL, nullptr } }; //************************************************************************** -// INPUT SEQ +// UTILITY FUNCTIONS //************************************************************************** -//------------------------------------------------- -// operator+= - append a code to the end of an -// input sequence -//------------------------------------------------- - -input_seq &input_seq::operator+=(input_code code) +inline void accumulate_axis_value( + s32 &result, + input_item_class &resultclass, + input_item_class &resultclasszero, + s32 value, + input_item_class valueclass, + input_item_class valueclasszero) { - // if not enough room, return false - int curlength = length(); - if (curlength < ARRAY_LENGTH(m_code) - 1) + if (!value) { - m_code[curlength++] = code; - m_code[curlength] = end_code; + // track the highest-priority zero + if ((ITEM_CLASS_ABSOLUTE == valueclasszero) || (ITEM_CLASS_INVALID == resultclasszero)) + resultclasszero = valueclasszero; } - return *this; -} - - -//------------------------------------------------- -// operator|= - append a code to a sequence; if -// the sequence is non-empty, insert an OR -// before the new code -//------------------------------------------------- - -input_seq &input_seq::operator|=(input_code code) -{ - // overwrite end/default with the new code - if (m_code[0] == end_code || m_code[0] == default_code) - m_code[0] = code; - - // otherwise, append an OR token and then the new code - else + else if (ITEM_CLASS_ABSOLUTE == valueclass) { - *this += or_code; - *this += code; + // absolute values override relative values + if (ITEM_CLASS_ABSOLUTE == resultclass) + result += value; + else + result = value; + resultclass = ITEM_CLASS_ABSOLUTE; } - return *this; -} - - -//------------------------------------------------- -// length - return the length of the sequence -//------------------------------------------------- - -int input_seq::length() const -{ - // find the end token; error if none found - for (int seqnum = 0; seqnum < ARRAY_LENGTH(m_code); seqnum++) - if (m_code[seqnum] == end_code) - return seqnum; - return ARRAY_LENGTH(m_code); -} - - -//------------------------------------------------- -// is_valid - return true if a given sequence is -// valid -//------------------------------------------------- - -bool input_seq::is_valid() const -{ - // "default" can only be of length 1 - if (m_code[0] == default_code) - return (length() == 1); - - // scan the sequence for valid codes - input_item_class lastclass = ITEM_CLASS_INVALID; - input_code lastcode = INPUT_CODE_INVALID; - int positive_code_count = 0; - for (auto code : m_code) + else if (ITEM_CLASS_RELATIVE == valueclass) { - // invalid codes are never permitted - - if (code == INPUT_CODE_INVALID) - return false; - - // if we hit an OR or the end, validate the previous chunk - if (code == or_code || code == end_code) - { - // must be at least one positive code - if (positive_code_count == 0) - return false; - - // last code must not have been an internal code - if (lastcode.internal()) - return false; - - // if this is the end, we're ok - if (code == end_code) - return true; - - // reset the state for the next chunk - positive_code_count = 0; - lastclass = ITEM_CLASS_INVALID; - } - - // if we hit a NOT, make sure we don't have a double - else if (code == not_code) + // relative values accumulate + if (resultclass != ITEM_CLASS_ABSOLUTE) { - if (lastcode == not_code) - return false; + result += value; + resultclass = ITEM_CLASS_RELATIVE; } - - // anything else - else - { - // count positive codes - if (lastcode != not_code) - positive_code_count++; - - // non-switch items can't have a NOT - input_item_class itemclass = code.item_class(); - if (itemclass != ITEM_CLASS_SWITCH && lastcode == not_code) - return false; - - // absolute/relative items must all be the same class - if ((lastclass == ITEM_CLASS_ABSOLUTE && itemclass != ITEM_CLASS_ABSOLUTE) || - (lastclass == ITEM_CLASS_RELATIVE && itemclass != ITEM_CLASS_RELATIVE)) - return false; - } - - // remember the last code - lastcode = code; } - - // if we got here, we were missing an END token; fail - return false; -} - - -//------------------------------------------------- -// set - directly set up to the first 7 codes -//------------------------------------------------- - -void input_seq::set(input_code code0, input_code code1, input_code code2, input_code code3, input_code code4, input_code code5, input_code code6) -{ - m_code[0] = code0; - m_code[1] = code1; - m_code[2] = code2; - m_code[3] = code3; - m_code[4] = code4; - m_code[5] = code5; - m_code[6] = code6; - for (int codenum = 7; codenum < ARRAY_LENGTH(m_code); codenum++) - m_code[codenum] = end_code; -} - - -//------------------------------------------------- -// backspace - "backspace" over the last entry in -// a sequence -//------------------------------------------------- - -void input_seq::backspace() -{ - // if we have at least one entry, remove it - int curlength = length(); - if (curlength > 0) - m_code[curlength - 1] = end_code; } - -//------------------------------------------------- -// replace - replace all instances of oldcode -// with newcode in a sequence -//------------------------------------------------- - -void input_seq::replace(input_code oldcode, input_code newcode) -{ - for (auto & elem : m_code) - if (elem == oldcode) - elem = newcode; -} +} // anonymous namespace @@ -556,10 +408,7 @@ void input_seq::replace(input_code oldcode, input_code newcode) // input_manager - constructor //------------------------------------------------- -input_manager::input_manager(running_machine &machine) - : m_machine(machine), - m_poll_seq_last_ticks(0), - m_poll_seq_class(ITEM_CLASS_SWITCH) +input_manager::input_manager(running_machine &machine) : m_machine(machine) { // reset code memory reset_memory(); @@ -590,72 +439,89 @@ input_manager::~input_manager() //------------------------------------------------- +// class_enabled - return whether input device +// class is enabled +//------------------------------------------------- + +bool input_manager::class_enabled(input_device_class devclass) const +{ + assert(devclass >= DEVICE_CLASS_FIRST_VALID && devclass <= DEVICE_CLASS_LAST_VALID); + return m_class[devclass]->enabled(); +} + + +//------------------------------------------------- +// add_device - add a representation of a host +// input device +//------------------------------------------------- + +osd::input_device &input_manager::add_device(input_device_class devclass, std::string_view name, std::string_view id, void *internal) +{ + return device_class(devclass).add_device(name, id, internal); +} + + +//------------------------------------------------- // code_value - return the value of a given // input code //------------------------------------------------- s32 input_manager::code_value(input_code code) { - g_profiler.start(PROFILER_INPUT); - s32 result = 0; - - // dummy loop to allow clean early exits - do + auto profile = g_profiler.start(PROFILER_INPUT); + + // return 0 for any invalid devices + input_device *const device = device_from_code(code); + if (!device) + return 0; + + // also return 0 if the device class is disabled + input_class &devclass = *m_class[code.device_class()]; + if (!devclass.enabled()) + return 0; + + // if this is not a multi device, only return data for item 0 and iterate over all + int startindex = code.device_index(); + int stopindex = startindex; + if (!devclass.multi()) { - // return 0 for any invalid devices - input_device *device = device_from_code(code); - if (device == nullptr) - break; - - // also return 0 if the device class is disabled - input_class &devclass = *m_class[code.device_class()]; - if (!devclass.enabled()) - break; - - // if this is not a multi device, only return data for item 0 and iterate over all - int startindex = code.device_index(); - int stopindex = startindex; - if (!devclass.multi()) - { - if (startindex != 0) - break; - stopindex = devclass.maxindex(); - } + if (startindex != 0) + return 0; + stopindex = devclass.maxindex(); + } - // iterate over all device indices - input_item_class targetclass = code.item_class(); - for (int curindex = startindex; curindex <= stopindex; curindex++) + // iterate over all device indices + s32 result = 0; + input_item_class targetclass = code.item_class(); + for (int curindex = startindex; curindex <= stopindex; curindex++) + { + // lookup the item for the appropriate index + code.set_device_index(curindex); + input_device_item *const item = item_from_code(code); + if (item) { - // lookup the item for the appropriate index - code.set_device_index(curindex); - input_device_item *item = item_from_code(code); - if (item == nullptr) - continue; - // process items according to their native type switch (targetclass) { - case ITEM_CLASS_ABSOLUTE: - if (result == 0) - result = item->read_as_absolute(code.item_modifier()); - break; + case ITEM_CLASS_ABSOLUTE: + if (result == 0) + result = item->read_as_absolute(code.item_modifier()); + break; - case ITEM_CLASS_RELATIVE: - result += item->read_as_relative(code.item_modifier()); - break; + case ITEM_CLASS_RELATIVE: + result += item->read_as_relative(code.item_modifier()); + break; - case ITEM_CLASS_SWITCH: - result |= item->read_as_switch(code.item_modifier()); - break; + case ITEM_CLASS_SWITCH: + result |= item->read_as_switch(code.item_modifier()); + break; - default: - break; + default: + break; } } - } while (0); + } - // stop the profiler before exiting - g_profiler.stop(); return result; } @@ -671,13 +537,13 @@ bool input_manager::code_pressed_once(input_code code) // look for the code in the memory bool curvalue = code_pressed(code); int empty = -1; - for (int memnum = 0; memnum < ARRAY_LENGTH(m_switch_memory); memnum++) + for (int memnum = 0; memnum < std::size(m_switch_memory); memnum++) { // were we previous pressed on the last time through here? if (m_switch_memory[memnum] == code) { // if no longer pressed, clear entry - if (curvalue == false) + if (!curvalue) m_switch_memory[memnum] = INPUT_CODE_INVALID; // always return false @@ -689,11 +555,11 @@ bool input_manager::code_pressed_once(input_code code) empty = memnum; } - // if we get here, we were not previously pressed; if still not pressed, return 0 - if (curvalue == false) + // if we get here, we were not previously pressed; if still not pressed, return false + if (!curvalue) return false; - // otherwise, add ourself to the memory and return 1 + // otherwise, add the code to the memory and return true assert(empty != -1); if (empty != -1) m_switch_memory[empty] = code; @@ -702,225 +568,6 @@ bool input_manager::code_pressed_once(input_code code) //------------------------------------------------- -// reset_polling - reset memories in preparation -// for polling -//------------------------------------------------- - -void input_manager::reset_polling() -{ - // reset switch memory - reset_memory(); - - // iterate over device classes and devices - for (input_device_class devclass = DEVICE_CLASS_FIRST_VALID; devclass <= DEVICE_CLASS_LAST_VALID; ++devclass) - for (int devnum = 0; devnum <= m_class[devclass]->maxindex(); devnum++) - { - // fetch the device; ignore if nullptr - input_device *device = m_class[devclass]->device(devnum); - if (device == nullptr) - continue; - - // iterate over items within each device - for (input_item_id itemid = ITEM_ID_FIRST_VALID; itemid <= device->maxitem(); ++itemid) - { - // for any non-switch items, set memory equal to the current value - input_device_item *item = device->item(itemid); - if (item != nullptr && item->itemclass() != ITEM_CLASS_SWITCH) - item->set_memory(code_value(item->code())); - } - } -} - - -//------------------------------------------------- -// poll_switches - poll for any input -//------------------------------------------------- - -input_code input_manager::poll_switches() -{ - // iterate over device classes and devices - for (input_device_class devclass = DEVICE_CLASS_FIRST_VALID; devclass <= DEVICE_CLASS_LAST_VALID; ++devclass) - for (int devnum = 0; devnum <= m_class[devclass]->maxindex(); devnum++) - { - // fetch the device; ignore if nullptr - input_device *device = m_class[devclass]->device(devnum); - if (device == nullptr) - continue; - - // iterate over items within each device - for (input_item_id itemid = ITEM_ID_FIRST_VALID; itemid <= device->maxitem(); ++itemid) - { - input_device_item *item = device->item(itemid); - if (item != nullptr) - { - input_code code = item->code(); - - // if the item is natively a switch, poll it - if (item->itemclass() == ITEM_CLASS_SWITCH) - { - if (code_pressed_once(code)) - return code; - else - continue; - } - - // skip if there is not enough axis movement - if (!code_check_axis(*item, code)) - continue; - - // otherwise, poll axes digitally - code.set_item_class(ITEM_CLASS_SWITCH); - - // if this is a joystick X axis, check with left/right modifiers - if (devclass == DEVICE_CLASS_JOYSTICK && code.item_id() == ITEM_ID_XAXIS) - { - code.set_item_modifier(ITEM_MODIFIER_LEFT); - if (code_pressed_once(code)) - return code; - code.set_item_modifier(ITEM_MODIFIER_RIGHT); - if (code_pressed_once(code)) - return code; - } - - // if this is a joystick Y axis, check with up/down modifiers - else if (devclass == DEVICE_CLASS_JOYSTICK && code.item_id() == ITEM_ID_YAXIS) - { - code.set_item_modifier(ITEM_MODIFIER_UP); - if (code_pressed_once(code)) - return code; - code.set_item_modifier(ITEM_MODIFIER_DOWN); - if (code_pressed_once(code)) - return code; - } - - // any other axis, check with pos/neg modifiers - else - { - code.set_item_modifier(ITEM_MODIFIER_POS); - if (code_pressed_once(code)) - return code; - code.set_item_modifier(ITEM_MODIFIER_NEG); - if (code_pressed_once(code)) - return code; - } - } - } - } - - // if nothing, return an invalid code - return INPUT_CODE_INVALID; -} - - -//------------------------------------------------- -// poll_keyboard_switches - poll for any -// keyboard-specific input -//------------------------------------------------- - -input_code input_manager::poll_keyboard_switches() -{ - // iterate over devices within each class - input_class &keyboard_class = device_class(DEVICE_CLASS_KEYBOARD); - for (int devnum = 0; devnum < keyboard_class.maxindex(); devnum++) - { - // fetch the device; ignore if nullptr - input_device *device = keyboard_class.device(devnum); - if (device == nullptr) - continue; - - // iterate over items within each device - for (input_item_id itemid = ITEM_ID_FIRST_VALID; itemid <= device->maxitem(); ++itemid) - { - input_device_item *item = device->item(itemid); - if (item != nullptr && item->itemclass() == ITEM_CLASS_SWITCH) - { - input_code code = item->code(); - if (code_pressed_once(code)) - return code; - } - } - } - - // if nothing, return an invalid code - return INPUT_CODE_INVALID; -} - - -//------------------------------------------------- -// code_check_axis - see if axis has moved far -// enough to trigger a read when polling -//------------------------------------------------- - -bool input_manager::code_check_axis(input_device_item &item, input_code code) -{ - // if we've already reported this one, don't bother - if (item.memory() == INVALID_AXIS_VALUE) - return false; - - // ignore min/max for lightguns - // so the selection will not be affected by a gun going out of range - s32 curval = code_value(code); - if (code.device_class() == DEVICE_CLASS_LIGHTGUN && - (code.item_id() == ITEM_ID_XAXIS || code.item_id() == ITEM_ID_YAXIS) && - (curval == INPUT_ABSOLUTE_MAX || curval == INPUT_ABSOLUTE_MIN)) - return false; - - // compute the diff against memory - s32 diff = curval - item.memory(); - if (diff < 0) - diff = -diff; - - // for absolute axes, look for 25% of maximum - if (item.itemclass() == ITEM_CLASS_ABSOLUTE && diff > (INPUT_ABSOLUTE_MAX - INPUT_ABSOLUTE_MIN) / 4) - { - item.set_memory(INVALID_AXIS_VALUE); - return true; - } - - // for relative axes, look for ~20 pixels movement - if (item.itemclass() == ITEM_CLASS_RELATIVE && diff > 20 * INPUT_RELATIVE_PER_PIXEL) - { - item.set_memory(INVALID_AXIS_VALUE); - return true; - } - return false; -} - - -//------------------------------------------------- -// poll_axes - poll for any input -//------------------------------------------------- - -input_code input_manager::poll_axes() -{ - // iterate over device classes and devices - for (input_device_class devclass = DEVICE_CLASS_FIRST_VALID; devclass <= DEVICE_CLASS_LAST_VALID; ++devclass) - for (int devnum = 0; devnum <= m_class[devclass]->maxindex(); devnum++) - { - // fetch the device; ignore if nullptr - input_device *device = m_class[devclass]->device(devnum); - if (device == nullptr) - continue; - - // iterate over items within each device - for (input_item_id itemid = ITEM_ID_FIRST_VALID; itemid <= device->maxitem(); ++itemid) - { - input_device_item *item = device->item(itemid); - if (item != nullptr && item->itemclass() != ITEM_CLASS_SWITCH) - { - input_code code = item->code(); - if (code_check_axis(*item, code)) - return code; - } - } - } - - // if nothing, return an invalid code - return INPUT_CODE_INVALID; -} - - -//------------------------------------------------- // device_from_code - given an input_code return // a pointer to the associated device //------------------------------------------------- @@ -999,50 +646,44 @@ input_code input_manager::code_from_itemid(input_item_id itemid) const std::string input_manager::code_name(input_code code) const { // if nothing there, return an empty string - input_device_item *item = item_from_code(code); - if (item == nullptr) + input_device_item const *const item = item_from_code(code); + if (!item) return std::string(); - // determine the devclass part - const char *devclass = (*devclass_string_table)[code.device_class()]; - - // determine the devindex part - std::string devindex = string_format("%d", code.device_index() + 1); - - // if we're unifying all devices, don't display a number - if (!m_class[code.device_class()]->multi()) - devindex.clear(); + std::string str; // keyboard 0 doesn't show a class or index if it is the only one - input_device_class device_class = item->device().devclass(); - if (device_class == DEVICE_CLASS_KEYBOARD && m_class[device_class]->maxindex() == 0) + input_device_class const device_class = item->device().devclass(); + if ((device_class != DEVICE_CLASS_KEYBOARD) || (m_class[device_class]->maxindex() > 0)) { - devclass = ""; - devindex.clear(); - } - - // devcode part comes from the item name - const char *devcode = item->name(); + // determine the devclass part + str = (*devclass_string_table)[code.device_class()]; - // determine the modifier part - const char *modifier = (*modifier_string_table)[code.item_modifier()]; - - // devcode is redundant with joystick switch left/right/up/down - if (device_class == DEVICE_CLASS_JOYSTICK && code.item_class() == ITEM_CLASS_SWITCH) - if (code.item_modifier() >= ITEM_MODIFIER_LEFT && code.item_modifier() <= ITEM_MODIFIER_DOWN) - devcode = ""; + // if we're unifying all devices, don't display a number + if (m_class[code.device_class()]->multi()) + str.append(util::string_format(" %d ", code.device_index() + 1)); + else + str.append(" "); + } - // concatenate the strings - std::string str(devclass); - if (!devindex.empty()) - str.append(" ").append(devindex); - if (devcode[0] != 0) - str.append(" ").append(devcode); - if (modifier != nullptr) - str.append(" ").append(modifier); + // append item name - redundant with joystick switch left/right/up/down + bool const joydir = + (device_class == DEVICE_CLASS_JOYSTICK) && + (code.item_class() == ITEM_CLASS_SWITCH) && + (code.item_modifier() >= ITEM_MODIFIER_LEFT) && + (code.item_modifier() <= ITEM_MODIFIER_DOWN); + if (joydir) + { + str.append((*modifier_string_table)[code.item_modifier()]); + } + else + { + str.append(item->name()); + char const *const modifier = (*modifier_string_table)[code.item_modifier()]; + if (modifier && *modifier) + str.append(" ").append(modifier); + } - // delete any leading spaces - strtrimspace(str); return str; } @@ -1053,8 +694,12 @@ std::string input_manager::code_name(input_code code) const std::string input_manager::code_to_token(input_code code) const { + using namespace std::literals; + // determine the devclass part const char *devclass = (*devclass_token_table)[code.device_class()]; + if (!devclass) + return "INVALID"; // determine the devindex part; keyboard 0 doesn't show an index std::string devindex = string_format("%d", code.device_index() + 1); @@ -1063,25 +708,25 @@ std::string input_manager::code_to_token(input_code code) const // determine the itemid part; look up in the table if we don't have a token input_device_item *item = item_from_code(code); - const char *devcode = (item != nullptr) ? item->token() : "UNKNOWN"; + std::string_view devcode = item ? item->token() : "UNKNOWN"sv; // determine the modifier part const char *modifier = (*modifier_token_table)[code.item_modifier()]; // determine the itemclass part; if we match the native class, we don't include this const char *itemclass = ""; - if (item == nullptr || item->itemclass() != code.item_class()) + if (!item || (item->itemclass() != code.item_class())) itemclass = (*itemclass_token_table)[code.item_class()]; // concatenate the strings std::string str(devclass); if (!devindex.empty()) str.append("_").append(devindex); - if (devcode[0] != 0) + if (!devcode.empty()) str.append("_").append(devcode); if (modifier != nullptr) str.append("_").append(modifier); - if (itemclass[0] != 0) + if (itemclass != nullptr && itemclass[0] != 0) str.append("_").append(itemclass); return str; } @@ -1092,27 +737,27 @@ std::string input_manager::code_to_token(input_code code) const // token //------------------------------------------------- -input_code input_manager::code_from_token(const char *_token) +input_code input_manager::code_from_token(std::string_view _token) { // copy the token and break it into pieces std::string token[6]; - int numtokens; - for (numtokens = 0; numtokens < ARRAY_LENGTH(token); ) + int numtokens = 0; + while (numtokens < std::size(token)) { // make a token up to the next underscore - char *score = (char *)strchr(_token, '_'); - token[numtokens++].assign(_token, (score == nullptr) ? strlen(_token) : (score - _token)); + std::string_view::size_type score = _token.find('_'); + token[numtokens++].assign(_token, 0, (std::string_view::npos == score) ? _token.length() : score); // if we hit the end, we're done, else advance our pointer - if (score == nullptr) + if (std::string_view::npos == score) break; - _token = score + 1; + _token.remove_prefix(score + 1); } // first token should be the devclass int curtok = 0; - input_device_class devclass = input_device_class((*devclass_token_table)[token[curtok++].c_str()]); - if (devclass == ~input_device_class(0)) + input_device_class const devclass = input_device_class((*devclass_token_table)[token[curtok++]]); + if (devclass == input_device_class(code_string_table::SENTINEL)) return INPUT_CODE_INVALID; // second token might be index; look for number @@ -1126,27 +771,29 @@ input_code input_manager::code_from_token(const char *_token) return INPUT_CODE_INVALID; // next token is the item ID - input_item_id itemid = input_item_id((*itemid_token_table)[token[curtok].c_str()]); - bool standard = (itemid != ~input_item_id(0)); + input_item_id itemid = input_item_id((*itemid_token_table)[token[curtok]]); + bool const standard = (itemid != input_item_id(code_string_table::SENTINEL)); - // if we're a standard code, default the itemclass based on it input_item_class itemclass = ITEM_CLASS_INVALID; if (standard) + { + // if we're a standard code, default the itemclass based on it itemclass = m_class[devclass]->standard_item_class(itemid); - - // otherwise, keep parsing + } else { + // otherwise, keep parsing + // if this is an invalid device, we have nothing to look up input_device *device = m_class[devclass]->device(devindex); - if (device == nullptr) + if (!device) return INPUT_CODE_INVALID; // if not a standard code, look it up in the device specific codes for (itemid = ITEM_ID_FIRST_VALID; itemid <= device->maxitem(); ++itemid) { input_device_item *item = device->item(itemid); - if (item != nullptr && token[curtok].compare(item->token()) == 0) + if (item && !token[curtok].compare(item->token())) { // take the itemclass from the item itemclass = item->itemclass(); @@ -1164,8 +811,8 @@ input_code input_manager::code_from_token(const char *_token) input_item_modifier modifier = ITEM_MODIFIER_NONE; if (curtok < numtokens) { - modifier = input_item_modifier((*modifier_token_table)[token[curtok].c_str()]); - if (modifier != ~input_item_modifier(0)) + modifier = input_item_modifier((*modifier_token_table)[token[curtok]]); + if (modifier != input_item_modifier(code_string_table::SENTINEL)) curtok++; else modifier = ITEM_MODIFIER_NONE; @@ -1174,8 +821,8 @@ input_code input_manager::code_from_token(const char *_token) // if we have another token, it is the item class if (curtok < numtokens) { - u32 temp = (*itemclass_token_table)[token[curtok].c_str()]; - if (temp != ~0) + u32 const temp = (*itemclass_token_table)[token[curtok]]; + if (temp != code_string_table::SENTINEL) { curtok++; itemclass = input_item_class(temp); @@ -1198,7 +845,7 @@ input_code input_manager::code_from_token(const char *_token) const char *input_manager::standard_token(input_item_id itemid) const { - return itemid <= ITEM_ID_MAXIMUM ? (*itemid_token_table)[itemid] : nullptr; + return (itemid <= ITEM_ID_MAXIMUM) ? (*itemid_token_table)[itemid] : nullptr; } @@ -1215,14 +862,16 @@ bool input_manager::seq_pressed(const input_seq &seq) bool first = true; for (int codenum = 0; ; codenum++) { - // handle NOT input_code code = seq[codenum]; if (code == input_seq::not_code) + { + // handle NOT invert = true; - - // handle OR and END + } else if (code == input_seq::or_code || code == input_seq::end_code) { + // handle OR and END + // if we have a positive result from the previous set, we're done if (result || code == input_seq::end_code) break; @@ -1232,10 +881,10 @@ bool input_manager::seq_pressed(const input_seq &seq) invert = false; first = true; } - - // handle everything else as a series of ANDs else { + // handle everything else as a series of ANDs + // if this is the first in the sequence, result is set equal if (first) result = code_pressed(code) ^ invert; @@ -1261,233 +910,117 @@ bool input_manager::seq_pressed(const input_seq &seq) s32 input_manager::seq_axis_value(const input_seq &seq, input_item_class &itemclass) { - // start with no valid classes - input_item_class itemclasszero = ITEM_CLASS_INVALID; + // start with zero result and no valid classes + s32 result = 0; itemclass = ITEM_CLASS_INVALID; + input_item_class itemclasszero = ITEM_CLASS_INVALID; // iterate over all of the codes - s32 result = 0; + s32 groupval = 0; + input_item_class groupclass = ITEM_CLASS_INVALID; + input_item_class groupclasszero = ITEM_CLASS_INVALID; bool invert = false; bool enable = true; for (int codenum = 0; ; codenum++) { - // handle NOT - input_code code = seq[codenum]; + input_code const code = seq[codenum]; if (code == input_seq::not_code) + { + // handle NOT - invert the next code invert = true; - - // handle OR and END - else if (code == input_seq::or_code || code == input_seq::end_code) + } + else if (code == input_seq::end_code) { - // if we have a positive result from the previous set, we're done - if (itemclass != ITEM_CLASS_INVALID || code == input_seq::end_code) - break; - - // otherwise, reset our state - result = 0; + // handle END - commit group and break out of loop + accumulate_axis_value( + result, itemclass, itemclasszero, + groupval, groupclass, groupclasszero); + break; + } + else if (code == input_seq::or_code) + { + // handle OR - commit group and reset for the next group + accumulate_axis_value( + result, itemclass, itemclasszero, + groupval, groupclass, groupclasszero); + groupval = 0; + groupclasszero = ITEM_CLASS_INVALID; + groupclass = ITEM_CLASS_INVALID; invert = false; enable = true; } - - // handle everything else only if we're still enabled else if (enable) { + // handle everything else only if we're still enabled + input_item_class const codeclass = code.item_class(); + // switch codes serve as enables - if (code.item_class() == ITEM_CLASS_SWITCH) + if (ITEM_CLASS_SWITCH == codeclass) { // AND against previous digital codes if (enable) - enable &= code_pressed(code) ^ invert; + { + enable = code_pressed(code) ^ invert; + if (!enable) + { + // clear current group if enable became false - only way out is an OR code + groupval = 0; + groupclasszero = ITEM_CLASS_INVALID; + groupclass = ITEM_CLASS_INVALID; + } + } } - - // non-switch codes are analog values else { - s32 value = code_value(code); - - // if we got a 0 value, don't do anything except remember the first type - if (value == 0) - { - if (itemclasszero == ITEM_CLASS_INVALID) - itemclasszero = code.item_class(); - } - - // non-zero absolute values stick - else if (code.item_class() == ITEM_CLASS_ABSOLUTE) - { - itemclass = ITEM_CLASS_ABSOLUTE; - result = value; - } - - // non-zero relative values accumulate - else if (code.item_class() == ITEM_CLASS_RELATIVE) - { - itemclass = ITEM_CLASS_RELATIVE; - result += value; - } + // non-switch codes are analog values + accumulate_axis_value( + groupval, groupclass, groupclasszero, + code_value(code), codeclass, codeclass); } - // clear the invert flag + // clear the invert flag - it only applies to one item invert = false; } } - // if the caller wants to know the type, provide it - if (result == 0) + // saturate mixed absolute values, report neutral type + if (ITEM_CLASS_ABSOLUTE == itemclass) + result = std::clamp(result, osd::input_device::ABSOLUTE_MIN, osd::input_device::ABSOLUTE_MAX); + else if (ITEM_CLASS_INVALID == itemclass) itemclass = itemclasszero; return result; } //------------------------------------------------- -// seq_poll_start - begin polling for a new -// sequence of the given itemclass -//------------------------------------------------- - -void input_manager::seq_poll_start(input_item_class itemclass, const input_seq *startseq) -{ - assert(itemclass == ITEM_CLASS_SWITCH || itemclass == ITEM_CLASS_ABSOLUTE || itemclass == ITEM_CLASS_RELATIVE); - - // reset the recording count and the clock - m_poll_seq_last_ticks = 0; - m_poll_seq_class = itemclass; - m_poll_seq.reset(); - - // grab the starting sequence to append to, and append an OR - if (startseq != nullptr) - { - m_poll_seq = *startseq; - if (m_poll_seq.length() > 0) - m_poll_seq += input_seq::or_code; - } - - // flush out any goobers - reset_polling(); - input_code dummycode = KEYCODE_ENTER; - while (dummycode != INPUT_CODE_INVALID) - dummycode = (m_poll_seq_class == ITEM_CLASS_SWITCH) ? poll_switches() : poll_axes(); -} - - -//------------------------------------------------- -// input_seq_poll - continue polling -//------------------------------------------------- - -bool input_manager::seq_poll() -{ - int curlen = m_poll_seq.length(); - input_code lastcode = m_poll_seq[curlen - 1]; - - // switch case: see if we have a new code to process - input_code newcode; - if (m_poll_seq_class == ITEM_CLASS_SWITCH) - { - newcode = poll_switches(); - if (newcode != INPUT_CODE_INVALID) - { - // if code is duplicate, toggle the NOT state on the code - if (curlen > 0 && newcode == lastcode) - { - // back up over the existing code - m_poll_seq.backspace(); - - // if there was a NOT preceding it, delete it as well, otherwise append a fresh one - if (m_poll_seq[curlen - 2] == input_seq::not_code) - m_poll_seq.backspace(); - else - m_poll_seq += input_seq::not_code; - } - } - } - - // absolute/relative case: see if we have an analog change of sufficient amount - else - { - bool has_or = false; - if (lastcode == input_seq::or_code) - { - lastcode = m_poll_seq[curlen - 2]; - has_or = true; - } - newcode = poll_axes(); - - // if the last code doesn't match absolute/relative of this code, ignore the new one - if ((lastcode.item_class() == ITEM_CLASS_ABSOLUTE && newcode.item_class() != ITEM_CLASS_ABSOLUTE) || - (lastcode.item_class() == ITEM_CLASS_RELATIVE && newcode.item_class() != ITEM_CLASS_RELATIVE)) - newcode = INPUT_CODE_INVALID; - - // if the new code is valid, check for half-axis toggles on absolute controls - if (newcode != INPUT_CODE_INVALID && curlen > 0 && newcode.item_class() == ITEM_CLASS_ABSOLUTE) - { - input_code last_nomodifier = lastcode; - last_nomodifier.set_item_modifier(ITEM_MODIFIER_NONE); - if (newcode == last_nomodifier) - { - // increment the modifier, wrapping back to none - switch (lastcode.item_modifier()) - { - case ITEM_MODIFIER_NONE: newcode.set_item_modifier(ITEM_MODIFIER_POS); break; - case ITEM_MODIFIER_POS: newcode.set_item_modifier(ITEM_MODIFIER_NEG); break; - default: - case ITEM_MODIFIER_NEG: newcode.set_item_modifier(ITEM_MODIFIER_NONE); break; - } - - // back up over the previous code so we can re-append - if (has_or) - m_poll_seq.backspace(); - m_poll_seq.backspace(); - } - } - } - - // if we got a new code to append it, append it and reset the timer - if (newcode != INPUT_CODE_INVALID) - { - m_poll_seq += newcode; - m_poll_seq_last_ticks = osd_ticks(); - } - - // if we're recorded at least one item and 2/3 of a second has passed, we're done - if (m_poll_seq_last_ticks != 0 && osd_ticks() > m_poll_seq_last_ticks + osd_ticks_per_second() * 2 / 3) - { - // if the final result is invalid, reset to nothing - if (!m_poll_seq.is_valid()) - m_poll_seq.reset(); - - // return true to indicate that we are finished - return true; - } - - // return false to indicate we are still polling - return false; -} - - -//------------------------------------------------- // seq_clean - clean the sequence, removing // any invalid bits //------------------------------------------------- input_seq input_manager::seq_clean(const input_seq &seq) const { + // make a copy of our sequence, removing any invalid bits + input_seq clean_codes; int clean_index = 0; for (int codenum = 0; seq[codenum] != input_seq::end_code; codenum++) { // if this is a code item which is not valid, don't copy it and remove any preceding ORs/NOTs input_code code = seq[codenum]; - if (!code.internal() && code_name(code).empty()) + if (!code.internal() && (((code.device_index() > 0) && !m_class[code.device_class()]->multi()) || !item_from_code(code))) { - while (clean_index > 0 && seq[clean_index - 1].internal()) + while (clean_index > 0 && clean_codes[clean_index - 1].internal()) + { + clean_codes.backspace(); clean_index--; + } } - else if (clean_index > 0 || !code.internal()) + else if (clean_index > 0 || !code.internal() || code == input_seq::not_code) + { + clean_codes += code; clean_index++; + } } - - input_seq cleaned_seq; - for (int i = 0; i < clean_index; i++) - cleaned_seq += seq[i]; - return cleaned_seq; + return clean_codes; } @@ -1503,7 +1036,7 @@ std::string input_manager::seq_name(const input_seq &seq) const // special case: empty if (cleaned_seq[0] == input_seq::end_code) - return std::string((seq.length() == 0) ? "None" : "n/a"); + return std::string(seq.empty() ? "None" : "n/a"); // start with an empty buffer std::string str; @@ -1569,53 +1102,52 @@ std::string input_manager::seq_to_tokens(const input_seq &seq) const // of a sequence //------------------------------------------------- -void input_manager::seq_from_tokens(input_seq &seq, const char *string) +void input_manager::seq_from_tokens(input_seq &seq, std::string_view string) { // start with a blank sequence seq.reset(); // loop until we're done - std::vector<char> strcopy(string, string + std::strlen(string) + 1); - char *str = &strcopy[0]; unsigned operators = 0; while (1) { // trim any leading spaces - while (*str != 0 && isspace(u8(*str))) - str++; + while (!string.empty() && isspace(u8(string[0]))) + string.remove_prefix(1); // bail if we're done - if (*str == 0) + if (string.empty()) return; // find the end of the token and make it upper-case along the way - char *strtemp; - for (strtemp = str; *strtemp != 0 && !isspace(u8(*strtemp)); strtemp++) - *strtemp = toupper(u8(*strtemp)); - char origspace = *strtemp; - *strtemp = 0; + std::string token; + while (!string.empty() && !isspace(u8(string[0]))) + { + token.push_back(toupper(u8(string[0]))); + string.remove_prefix(1); + } // look for common stuff input_code code; bool is_operator; - if (strcmp(str, "OR") == 0) + if (token == "OR") { code = input_seq::or_code; is_operator = true; } - else if (strcmp(str, "NOT") == 0) + else if (token == "NOT") { code = input_seq::not_code; is_operator = true; } - else if (strcmp(str, "DEFAULT") == 0) + else if (token == "DEFAULT") { code = input_seq::default_code; is_operator = false; } else { - code = code_from_token(str); + code = code_from_token(token); is_operator = false; } @@ -1629,7 +1161,7 @@ void input_manager::seq_from_tokens(input_seq &seq, const char *string) { if (code.device_class() < DEVICE_CLASS_FIRST_VALID) { - osd_printf_warning("Input: Dropping invalid input token %s\n", str); + osd_printf_warning("Input: Dropping invalid input token %s\n", token); while (operators) { seq.backspace(); @@ -1644,9 +1176,9 @@ void input_manager::seq_from_tokens(input_seq &seq, const char *string) } // advance - if (origspace == 0) + if (string.empty()) return; - str = strtemp + 1; + string.remove_prefix(1); } } @@ -1655,37 +1187,34 @@ void input_manager::seq_from_tokens(input_seq &seq, const char *string) // controller based on device map table //------------------------------------------------- -bool input_manager::map_device_to_controller(const devicemap_table_type *devicemap_table) +bool input_manager::map_device_to_controller(const devicemap_table &table) { - if (nullptr == devicemap_table) - return true; - - for (devicemap_table_type::const_iterator it = devicemap_table->begin(); it != devicemap_table->end(); it++) + for (const auto &it : table) { - const char *deviceid = it->first.c_str(); - const char *controllername = it->second.c_str(); + std::string_view deviceid = it.first; + std::string_view controllername = it.second; // tokenize the controller name into device class and index (i.e. controller name should be of the form "GUNCODE_1") std::string token[2]; - int numtokens; - const char *_token = controllername; - for (numtokens = 0; numtokens < ARRAY_LENGTH(token); ) + int numtokens = 0; + std::string_view _token = controllername; + while (numtokens < std::size(token)) { // make a token up to the next underscore - char *score = (char *)strchr(_token, '_'); - token[numtokens++].assign(_token, (score == nullptr) ? strlen(_token) : (score - _token)); + std::string_view::size_type score = _token.find('_'); + token[numtokens++].assign(_token, 0, (std::string_view::npos == score) ? _token.length() : score); // if we hit the end, we're done, else advance our pointer - if (score == nullptr) + if (std::string_view::npos == score) break; - _token = score + 1; + _token.remove_prefix(score + 1); } if (2 != numtokens) return false; // first token should be the devclass - input_device_class devclass = input_device_class((*devclass_token_table)[strmakeupper(token[0]).c_str()]); - if (devclass == ~input_device_class(0)) + input_device_class const devclass = input_device_class((*devclass_token_table)[strmakeupper(token[0])]); + if (devclass == input_device_class(code_string_table::SENTINEL)) return false; // second token should be the devindex @@ -1702,11 +1231,11 @@ bool input_manager::map_device_to_controller(const devicemap_table_type *devicem for (int devnum = 0; devnum <= input_devclass->maxindex(); devnum++) { input_device *device = input_devclass->device(devnum); - if (device != nullptr && device->match_device_id(deviceid)) + if (device && device->match_device_id(deviceid)) { // remap devindex input_devclass->remap_device_index(device->devindex(), devindex); - osd_printf_verbose("Input: Remapped %s #%d: %s (device id: %s)\n", input_devclass->name(), devindex, device->name(), device->id()); + osd_printf_verbose("Input: Remapped %s #%d: %s (device id: %s)\n", input_devclass->name(), devindex + 1, device->name(), device->id()); break; } |