summaryrefslogtreecommitdiffstatshomepage
path: root/src/emu/input.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/emu/input.cpp')
-rw-r--r--src/emu/input.cpp466
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;
}