diff options
Diffstat (limited to 'src/emu/input.cpp')
-rw-r--r-- | src/emu/input.cpp | 466 |
1 files changed, 159 insertions, 307 deletions
diff --git a/src/emu/input.cpp b/src/emu/input.cpp index 5c49d1c2dab..9e82f290bf7 100644 --- a/src/emu/input.cpp +++ b/src/emu/input.cpp @@ -24,20 +24,7 @@ -//************************************************************************** -// CONSTANTS -//************************************************************************** - -// additional expanded input codes for sequences -constexpr input_code input_seq::end_code; -constexpr input_code input_seq::default_code; -constexpr input_code input_seq::not_code; -constexpr input_code input_seq::or_code; - -// constant sequences -const input_seq input_seq::empty_seq; - - +namespace { //************************************************************************** // TYPE DEFINITIONS @@ -64,8 +51,8 @@ struct code_string_table return nullptr; } - u32 m_code; - const char * m_string; + u32 m_code; + const char * m_string; }; @@ -75,7 +62,7 @@ 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" }, @@ -85,7 +72,7 @@ static const code_string_table devclass_token_table[] = }; // 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" }, @@ -95,7 +82,7 @@ static const code_string_table devclass_string_table[] = }; // token strings for item modifiers -static const code_string_table modifier_token_table[] = +const code_string_table modifier_token_table[] = { { ITEM_MODIFIER_REVERSE, "REVERSE" }, { ITEM_MODIFIER_POS, "POS" }, @@ -108,7 +95,7 @@ static const code_string_table modifier_token_table[] = }; // friendly strings for item modifiers -static const code_string_table modifier_string_table[] = +const code_string_table modifier_string_table[] = { { ITEM_MODIFIER_REVERSE, "Reverse" }, { ITEM_MODIFIER_POS, "+" }, @@ -121,7 +108,7 @@ static const code_string_table modifier_string_table[] = }; // 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" }, @@ -130,7 +117,7 @@ static const code_string_table itemclass_token_table[] = }; // 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" }, @@ -370,183 +357,44 @@ static const code_string_table itemid_token_table[] = //************************************************************************** -// INPUT SEQ +// UTILITY FUNCTIONS //************************************************************************** -//------------------------------------------------- -// operator+= - append a code to the end of an -// input sequence -//------------------------------------------------- - -input_seq &input_seq::operator+=(input_code code) noexcept -{ - // if not enough room, return false - const int curlength = length(); - if (curlength < m_code.size()) - { - m_code[curlength] = code; - if ((curlength + 1) < m_code.size()) - m_code[curlength + 1] = end_code; - } - 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) noexcept +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) { - // overwrite end/default with the new code - if (m_code[0] == default_code) + if (!value) { - m_code[0] = code; - m_code[1] = end_code; + // track the highest-priority zero + if ((ITEM_CLASS_ABSOLUTE == valueclasszero) || (ITEM_CLASS_INVALID == resultclasszero)) + resultclasszero = valueclasszero; } - else + else if (ITEM_CLASS_ABSOLUTE == valueclass) { - // otherwise, append an OR token and then the new code - const int curlength = length(); - if ((curlength + 1) < m_code.size()) - { - m_code[curlength] = or_code; - m_code[curlength + 1] = code; - if ((curlength + 2) < m_code.size()) - m_code[curlength + 2] = end_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 noexcept -{ - // find the end token; error if none found - for (int seqnum = 0; seqnum < m_code.size(); seqnum++) - if (m_code[seqnum] == end_code) - return seqnum; - return m_code.size(); -} - - -//------------------------------------------------- -// is_valid - return true if a given sequence is -// valid -//------------------------------------------------- - -bool input_seq::is_valid() const noexcept -{ - // "default" can only be of length 1 - if (m_code[0] == default_code) - return m_code[1] == end_code; - - // scan the sequence for valid codes - input_item_class lastclass = ITEM_CLASS_INVALID; - input_code lastcode = INPUT_CODE_INVALID; - decltype(m_code) positive_codes; - decltype(m_code) negative_codes; - auto positive_codes_end = positive_codes.begin(); - auto negative_codes_end = negative_codes.begin(); - for (input_code 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) + // relative values accumulate + if (resultclass != ITEM_CLASS_ABSOLUTE) { - // must be at least one positive code - if (positive_codes.begin() == positive_codes_end) - 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_codes_end = positive_codes.begin(); - negative_codes_end = negative_codes.begin(); - lastclass = ITEM_CLASS_INVALID; - } - else if (code == not_code) - { - // if we hit a NOT, make sure we don't have a double - if (lastcode == not_code) - return false; - } - else - { - // track positive codes, and don't allow positive and negative for the same code - if (lastcode != not_code) - { - *positive_codes_end++ = code; - if (std::find(negative_codes.begin(), negative_codes_end, code) != negative_codes_end) - return false; - } - else - { - *negative_codes_end++ = code; - if (std::find(positive_codes.begin(), positive_codes_end, code) != positive_codes_end) - return false; - } - - // 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; + result += value; + resultclass = ITEM_CLASS_RELATIVE; } - - // remember the last code - lastcode = code; } - - // if we got here, we were missing an END token; fail - return false; -} - - -//------------------------------------------------- -// backspace - "backspace" over the last entry in -// a sequence -//------------------------------------------------- - -void input_seq::backspace() noexcept -{ - // if we have at least one entry, remove it - const 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) noexcept -{ - for (input_code &elem : m_code) - if (elem == oldcode) - elem = newcode; -} +} // anonymous namespace @@ -589,48 +437,67 @@ 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) { @@ -651,10 +518,8 @@ s32 input_manager::code_value(input_code code) break; } } - } while (0); + } - // stop the profiler before exiting - g_profiler.stop(); return result; } @@ -779,50 +644,45 @@ 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(); - } + // determine the devclass part + str = (*devclass_string_table)[code.device_class()]; - // devcode part comes from the item name - std::string_view devcode = item->name(); - - // 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 = std::string_view(); + // 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.empty()) - 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 - return std::string(strtrimspace(str)); + return str; } @@ -860,7 +720,7 @@ std::string input_manager::code_to_token(input_code code) const 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); @@ -1046,91 +906,83 @@ 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++) { - input_code code = seq[codenum]; + input_code const code = seq[codenum]; if (code == input_seq::not_code) { - // handle NOT + // handle NOT - invert the next code invert = true; } else if (code == input_seq::end_code) { - // handle END + // 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 - - // reset invert and enable for the next group + // 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; } 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; - // FIXME: need to clear current group value if enable became false - // you can't create a sequence where this matters using the internal UI, - // but you can by editing a CFG file (or controller config file) + 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; + } + } } else { // non-switch codes are analog values - 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(); - } - else if (code.item_class() == ITEM_CLASS_ABSOLUTE) - { - // non-zero absolute values override relative values - if (itemclass == ITEM_CLASS_ABSOLUTE) - result += value; - else - result = value; - itemclass = ITEM_CLASS_ABSOLUTE; - } - else if (code.item_class() == ITEM_CLASS_RELATIVE) - { - // non-zero relative values accumulate in the absence of absolute values - if (itemclass != ITEM_CLASS_ABSOLUTE) - { - result += value; - itemclass = ITEM_CLASS_RELATIVE; - } - } + 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; } } - // saturate mixed absolute values - if (itemclass == ITEM_CLASS_ABSOLUTE) - result = std::clamp(result, INPUT_ABSOLUTE_MIN, INPUT_ABSOLUTE_MAX); - - // 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; } @@ -1150,7 +1002,7 @@ input_seq input_manager::seq_clean(const input_seq &seq) const { // 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 && clean_codes[clean_index - 1].internal()) { @@ -1379,7 +1231,7 @@ bool input_manager::map_device_to_controller(const devicemap_table &table) { // 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; } |