summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
-rw-r--r--scripts/src/emu.lua2
-rw-r--r--src/emu/input.cpp921
-rw-r--r--src/emu/input.h239
-rw-r--r--src/emu/inputdev.cpp947
-rw-r--r--src/emu/inputdev.h400
-rw-r--r--src/emu/ioport.cpp44
-rw-r--r--src/osd/modules/input/input_common.h2
7 files changed, 1420 insertions, 1135 deletions
diff --git a/scripts/src/emu.lua b/scripts/src/emu.lua
index 138f3411204..88bcc24f792 100644
--- a/scripts/src/emu.lua
+++ b/scripts/src/emu.lua
@@ -123,6 +123,8 @@ files {
MAME_DIR .. "src/emu/image.h",
MAME_DIR .. "src/emu/input.cpp",
MAME_DIR .. "src/emu/input.h",
+ MAME_DIR .. "src/emu/inputdev.cpp",
+ MAME_DIR .. "src/emu/inputdev.h",
MAME_DIR .. "src/emu/ioport.cpp",
MAME_DIR .. "src/emu/ioport.h",
MAME_DIR .. "src/emu/inpttype.h",
diff --git a/src/emu/input.cpp b/src/emu/input.cpp
index 2abcfd9dac7..91ff3350750 100644
--- a/src/emu/input.cpp
+++ b/src/emu/input.cpp
@@ -18,7 +18,7 @@
***************************************************************************/
#include "emu.h"
-#include "emuopts.h"
+#include "inputdev.h"
@@ -44,63 +44,6 @@ const input_seq input_seq::empty_seq;
// TYPE DEFINITIONS
//**************************************************************************
-// ======================> input_device_switch_item
-
-// derived input item representing a switch input
-class input_device_switch_item : public input_device_item
-{
-public:
- // construction/destruction
- input_device_switch_item(input_device &device, const char *name, void *internal, input_item_id itemid, item_get_state_func getstate);
-
- // readers
- virtual int32_t read_as_switch(input_item_modifier modifier) override;
- virtual int32_t read_as_relative(input_item_modifier modifier) override;
- virtual int32_t read_as_absolute(input_item_modifier modifier) override;
-
- // steadykey helper
- bool steadykey_changed();
- void steadykey_update_to_current() { m_steadykey = m_current; }
-
-private:
- // internal state
- int32_t m_steadykey; // the live steadykey state
- int32_t m_oldkey; // old live state
-};
-
-
-// ======================> input_device_switch_item
-
-// derived input item representing a relative axis input
-class input_device_relative_item : public input_device_item
-{
-public:
- // construction/destruction
- input_device_relative_item(input_device &device, const char *name, void *internal, input_item_id itemid, item_get_state_func getstate);
-
- // readers
- virtual int32_t read_as_switch(input_item_modifier modifier) override;
- virtual int32_t read_as_relative(input_item_modifier modifier) override;
- virtual int32_t read_as_absolute(input_item_modifier modifier) override;
-};
-
-
-// ======================> input_device_switch_item
-
-// derived input item representing an absolute axis input
-class input_device_absolute_item : public input_device_item
-{
-public:
- // construction/destruction
- input_device_absolute_item(input_device &device, const char *name, void *internal, input_item_id itemid, item_get_state_func getstate);
-
- // readers
- virtual int32_t read_as_switch(input_item_modifier modifier) override;
- virtual int32_t read_as_relative(input_item_modifier modifier) override;
- virtual int32_t read_as_absolute(input_item_modifier modifier) override;
-};
-
-
// ======================> code_string_table
// simple class to match codes to strings
@@ -426,191 +369,6 @@ static const code_string_table itemid_token_table[] =
//**************************************************************************
-// GLOBAL VARIABLES
-//**************************************************************************
-
-// standard joystick mappings
-const char joystick_map_8way[] = "7778...4445";
-const char joystick_map_4way_diagonal[] = "4444s8888..444458888.444555888.ss5.222555666.222256666.2222s6666.2222s6666";
-// const char joystick_map_4way_sticky[] = "s8.4s8.44s8.4445";
-
-
-//**************************************************************************
-// JOYSTICK MAP
-//**************************************************************************
-
-//-------------------------------------------------
-// joystick_map - constructor
-//-------------------------------------------------
-
-joystick_map::joystick_map()
- : m_lastmap(JOYSTICK_MAP_NEUTRAL)
-{
- // parse the standard 8-way map as default
- parse(joystick_map_8way);
-}
-
-
-//-------------------------------------------------
-// parse - parse a string into a joystick map
-//-------------------------------------------------
-
-bool joystick_map::parse(const char *mapstring)
-{
- // save a copy of the original string
- m_origstring = mapstring;
-
- // iterate over rows
- for (int rownum = 0; rownum < 9; rownum++)
- {
- // if we're done, copy from another row
- if (*mapstring == 0 || *mapstring == '.')
- {
- bool symmetric = (rownum >= 5 && *mapstring == 0);
- const uint8_t *srcrow = &m_map[symmetric ? (8 - rownum) : (rownum - 1)][0];
-
- // if this is row 0, we don't have a source row -- invalid
- if (rownum == 0)
- return false;
-
- // copy from the srcrow, applying up/down symmetry if in the bottom half
- for (int colnum = 0; colnum < 9; colnum++)
- {
- uint8_t val = srcrow[colnum];
- if (symmetric)
- val = (val & (JOYSTICK_MAP_LEFT | JOYSTICK_MAP_RIGHT)) | ((val & JOYSTICK_MAP_UP) << 1) | ((val & JOYSTICK_MAP_DOWN) >> 1);
- m_map[rownum][colnum] = val;
- }
- }
-
- // otherwise, parse this column
- else
- {
- for (int colnum = 0; colnum < 9; colnum++)
- {
- // if we're at the end of row, copy previous to the middle, then apply left/right symmetry
- if (colnum > 0 && (*mapstring == 0 || *mapstring == '.'))
- {
- bool symmetric = (colnum >= 5);
- uint8_t val = m_map[rownum][symmetric ? (8 - colnum) : (colnum - 1)];
- if (symmetric)
- val = (val & (JOYSTICK_MAP_UP | JOYSTICK_MAP_DOWN)) | ((val & JOYSTICK_MAP_LEFT) << 1) | ((val & JOYSTICK_MAP_RIGHT) >> 1);
- m_map[rownum][colnum] = val;
- }
-
- // otherwise, convert the character to its value
- else
- {
- static const uint8_t charmap[] =
- {
- JOYSTICK_MAP_UP | JOYSTICK_MAP_LEFT,
- JOYSTICK_MAP_UP,
- JOYSTICK_MAP_UP | JOYSTICK_MAP_RIGHT,
- JOYSTICK_MAP_LEFT,
- JOYSTICK_MAP_NEUTRAL,
- JOYSTICK_MAP_RIGHT,
- JOYSTICK_MAP_DOWN | JOYSTICK_MAP_LEFT,
- JOYSTICK_MAP_DOWN,
- JOYSTICK_MAP_DOWN | JOYSTICK_MAP_RIGHT,
- JOYSTICK_MAP_STICKY
- };
- static const char validchars[] = "789456123s";
- const char *ptr = strchr(validchars, *mapstring++);
-
- // invalid characters exit immediately
- if (ptr == nullptr)
- return false;
- m_map[rownum][colnum] = charmap[ptr - validchars];
- }
- }
- }
-
- // if we ended with a period, advance to the next row
- if (*mapstring == '.')
- mapstring++;
- }
- return true;
-}
-
-
-//-------------------------------------------------
-// to_string - output the map as a string for
-// friendly display
-//-------------------------------------------------
-
-std::string joystick_map::to_string() const
-{
- std::string str(m_origstring);
- str.append("\n");
- for (auto & elem : m_map)
- {
- str.append(" ");
- for (auto & elem_colnum : elem)
- switch (elem_colnum)
- {
- case JOYSTICK_MAP_UP | JOYSTICK_MAP_LEFT: str.append("7"); break;
- case JOYSTICK_MAP_UP: str.append("8"); break;
- case JOYSTICK_MAP_UP | JOYSTICK_MAP_RIGHT: str.append("9"); break;
- case JOYSTICK_MAP_LEFT: str.append("4"); break;
- case JOYSTICK_MAP_NEUTRAL: str.append("5"); break;
- case JOYSTICK_MAP_RIGHT: str.append("6"); break;
- case JOYSTICK_MAP_DOWN | JOYSTICK_MAP_LEFT: str.append("1"); break;
- case JOYSTICK_MAP_DOWN: str.append("2"); break;
- case JOYSTICK_MAP_DOWN | JOYSTICK_MAP_RIGHT:str.append("3"); break;
- case JOYSTICK_MAP_STICKY: str.append("s"); break;
- default: str.append("?"); break;
- }
-
- str.append("\n");
- }
- return str;
-}
-
-
-//-------------------------------------------------
-// update - update the state of the joystick
-// map based on the given X/Y axis values
-//-------------------------------------------------
-
-uint8_t joystick_map::update(int32_t xaxisval, int32_t yaxisval)
-{
- // now map the X and Y axes to a 9x9 grid using the raw values
- xaxisval = ((xaxisval - INPUT_ABSOLUTE_MIN) * 9) / (INPUT_ABSOLUTE_MAX - INPUT_ABSOLUTE_MIN + 1);
- yaxisval = ((yaxisval - INPUT_ABSOLUTE_MIN) * 9) / (INPUT_ABSOLUTE_MAX - INPUT_ABSOLUTE_MIN + 1);
- uint8_t mapval = m_map[yaxisval][xaxisval];
-
- // handle stickiness
- if (mapval == JOYSTICK_MAP_STICKY)
- mapval = m_lastmap;
- else
- m_lastmap = mapval;
-
- // return based on whether the appropriate bit is set
- return mapval;
-}
-
-
-
-//**************************************************************************
-// INPUT CODE
-//**************************************************************************
-
-//-------------------------------------------------
-// input_code - construct an input code from a
-// device/item pair
-//-------------------------------------------------
-
-input_code::input_code(input_device &device, input_item_id itemid)
-{
- assert(itemid < ITEM_ID_ABSOLUTE_MAXIMUM);
- input_device_item *item = device.item(itemid);
- assert(item != nullptr);
- m_internal = ((device.devclass() & 0xf) << 28) | ((device.devindex() & 0xff) << 20) | ((item->itemclass() & 0xf) << 16) | (ITEM_MODIFIER_NONE << 12) | (item->itemid() & 0xfff);
-}
-
-
-
-//**************************************************************************
// INPUT SEQ
//**************************************************************************
@@ -791,326 +549,43 @@ void input_seq::replace(input_code oldcode, input_code newcode)
//**************************************************************************
-// INPUT DEVICE
+// INPUT MANAGER
//**************************************************************************
//-------------------------------------------------
-// input_device - constructor
-//-------------------------------------------------
-
-input_device::input_device(input_class &_class, int devindex, const char *name, const char *id, void *internal)
- : m_class(_class),
- m_name(name),
- m_id(id),
- m_devindex(devindex),
- m_maxitem(input_item_id(0)),
- m_internal(internal),
- m_joystick_deadzone((int32_t)(_class.manager().machine().options().joystick_deadzone() * INPUT_ABSOLUTE_MAX)),
- m_joystick_saturation((int32_t)(_class.manager().machine().options().joystick_saturation() * INPUT_ABSOLUTE_MAX)),
- m_steadykey_enabled(_class.manager().machine().options().steadykey()),
- m_lightgun_reload_button(_class.manager().machine().options().offscreen_reload())
-{
- // additional work for joysticks
- if (devclass() == DEVICE_CLASS_JOYSTICK)
- {
- // get the default joystick map
- const char *mapstring = machine().options().joystick_map();
- if (mapstring[0] == 0 || strcmp(mapstring, "auto") == 0)
- mapstring = joystick_map_8way;
-
- // parse it
- if (!m_joymap.parse(mapstring))
- {
- osd_printf_error("Invalid joystick map: %s\n", mapstring);
- m_joymap.parse(joystick_map_8way);
- }
- else if (mapstring != joystick_map_8way)
- osd_printf_verbose("Input: Default joystick map = %s\n", m_joymap.to_string().c_str());
- }
-}
-
-
-//-------------------------------------------------
-// add_item - add a new item to an input device
-//-------------------------------------------------
-
-input_item_id input_device::add_item(const char *name, input_item_id itemid, item_get_state_func getstate, void *internal)
-{
- assert_always(machine().phase() == MACHINE_PHASE_INIT, "Can only call input_device::add_item at init time!");
- assert(name != nullptr);
- assert(itemid > ITEM_ID_INVALID && itemid < ITEM_ID_MAXIMUM);
- assert(getstate != nullptr);
-
- // if we have a generic ID, pick a new internal one
- input_item_id originalid = itemid;
- if (itemid >= ITEM_ID_OTHER_SWITCH && itemid <= ITEM_ID_OTHER_AXIS_RELATIVE)
- for (itemid = (input_item_id)(ITEM_ID_MAXIMUM + 1); itemid <= ITEM_ID_ABSOLUTE_MAXIMUM; ++itemid)
- if (m_item[itemid] == nullptr)
- break;
- assert(itemid <= ITEM_ID_ABSOLUTE_MAXIMUM);
-
- // make sure we don't have any overlap
- assert(m_item[itemid] == nullptr);
-
- // determine the class and create the appropriate item class
- switch (m_class.standard_item_class(originalid))
- {
- case ITEM_CLASS_SWITCH:
- m_item[itemid] = std::make_unique<input_device_switch_item>(*this, name, internal, itemid, getstate);
- break;
-
- case ITEM_CLASS_RELATIVE:
- m_item[itemid] = std::make_unique<input_device_relative_item>(*this, name, internal, itemid, getstate);
- break;
-
- case ITEM_CLASS_ABSOLUTE:
- m_item[itemid] = std::make_unique<input_device_absolute_item>(*this, name, internal, itemid, getstate);
- break;
-
- default:
- m_item[itemid] = nullptr;
- assert(false);
- }
-
- // assign the new slot and update the maximum
- m_maxitem = std::max(m_maxitem, itemid);
- return itemid;
-}
-
-
-//-------------------------------------------------
-// apply_deadzone_and_saturation - apply global
-// deadzone and saturation parameters to an
-// absolute value
+// input_manager - constructor
//-------------------------------------------------
-int32_t input_device::apply_deadzone_and_saturation(int32_t result) const
+input_manager::input_manager(running_machine &machine)
+ : m_machine(machine),
+ m_poll_seq_last_ticks(0),
+ m_poll_seq_class(ITEM_CLASS_SWITCH)
{
- // ignore for non-joysticks
- if (devclass() != DEVICE_CLASS_JOYSTICK)
- return result;
-
- // properties are symmetric
- bool negative = false;
- if (result < 0)
- {
- negative = true;
- result = -result;
- }
-
- // if in the deadzone, return 0
- if (result < m_joystick_deadzone)
- result = 0;
-
- // if saturated, return the max
- else if (result > m_joystick_saturation)
- result = INPUT_ABSOLUTE_MAX;
-
- // otherwise, scale
- else
- result = (int64_t)(result - m_joystick_deadzone) * (int64_t)INPUT_ABSOLUTE_MAX / (int64_t)(m_joystick_saturation - m_joystick_deadzone);
-
- // re-apply sign and return
- return negative ? -result : result;
-}
-
-
-//-------------------------------------------------
-// apply_steadykey - apply steadykey option if
-// enabled
-//-------------------------------------------------
+ // reset code memory
+ reset_memory();
-void input_device::apply_steadykey() const
-{
- // ignore if not enabled
- if (!m_steadykey_enabled)
- return;
+ // create pointers for the classes
+ m_class[DEVICE_CLASS_KEYBOARD] = std::make_unique<input_class_keyboard>(*this);
+ m_class[DEVICE_CLASS_MOUSE] = std::make_unique<input_class_mouse>(*this);
+ m_class[DEVICE_CLASS_LIGHTGUN] = std::make_unique<input_class_lightgun>(*this);
+ m_class[DEVICE_CLASS_JOYSTICK] = std::make_unique<input_class_joystick>(*this);
- // update the state of all the keys and see if any changed state
- bool anything_changed = false;
- for (input_item_id itemid = ITEM_ID_FIRST_VALID; itemid <= m_maxitem; ++itemid)
+#ifdef MAME_DEBUG
+ for (input_device_class devclass = DEVICE_CLASS_FIRST_VALID; devclass <= DEVICE_CLASS_LAST_VALID; ++devclass)
{
- input_device_item *item = m_item[itemid].get();
- if (item != nullptr && item->itemclass() == ITEM_CLASS_SWITCH)
- if (downcast<input_device_switch_item *>(item)->steadykey_changed())
- anything_changed = true;
+ assert(m_class[devclass] != nullptr);
+ assert(m_class[devclass]->devclass() == devclass);
}
-
- // if the keyboard state is stable, flush the current state
- if (!anything_changed)
- for (input_item_id itemid = ITEM_ID_FIRST_VALID; itemid <= m_maxitem; ++itemid)
- {
- input_device_item *item = m_item[itemid].get();
- if (item != nullptr && item->itemclass() == ITEM_CLASS_SWITCH)
- downcast<input_device_switch_item *>(item)->steadykey_update_to_current();
- }
-}
-
-//-------------------------------------------------
-// match_device_id - match device id via
-// substring search
-//-------------------------------------------------
-
-bool input_device::match_device_id(const char *deviceid)
-{
- std::string deviceidupper(deviceid);
- std::string idupper(m_id);
-
- strmakeupper(deviceidupper);
- strmakeupper(idupper);
-
- return std::string::npos == idupper.find(deviceidupper) ? false : true;
-}
-
-
-//**************************************************************************
-// INPUT CLASS
-//**************************************************************************
-
-//-------------------------------------------------
-// input_class - constructor
-//-------------------------------------------------
-
-input_class::input_class(input_manager &manager, input_device_class devclass, bool enabled, bool multi)
- : m_manager(manager),
- m_devclass(devclass),
- m_maxindex(0),
- m_enabled(enabled),
- m_multi(multi)
-{
- // request a per-frame callback for the keyboard class
- if (devclass == DEVICE_CLASS_KEYBOARD)
- machine().add_notifier(MACHINE_NOTIFY_FRAME, machine_notify_delegate(FUNC(input_class::frame_callback), this));
-}
-
-
-//-------------------------------------------------
-// add_device - add a new input device
-//-------------------------------------------------
-
-input_device *input_class::add_device(const char *name, const char *id, void *internal)
-{
- // find the next empty index
- int devindex;
- for (devindex = 0; devindex < DEVICE_INDEX_MAXIMUM; devindex++)
- if (m_device[devindex] == nullptr)
- break;
-
- // call through
- return add_device(devindex, name, id, internal);
-}
-
-input_device *input_class::add_device(int devindex, const char *name, const char *id, void *internal)
-{
- assert_always(machine().phase() == MACHINE_PHASE_INIT, "Can only call input_class::add_device at init time!");
- assert(name != nullptr);
- assert(id != nullptr);
- assert(devindex >= 0 && devindex < DEVICE_INDEX_MAXIMUM);
- assert(m_device[devindex] == nullptr);
-
- // allocate a new device
- m_device[devindex] = std::make_unique<input_device>(*this, devindex, name, id, internal);
-
- // update the maximum index found
- m_maxindex = std::max(m_maxindex, devindex);
-
- if (0 == strlen(id))
- osd_printf_verbose("Input: Adding %s #%d: %s\n", (*devclass_string_table)[m_devclass], devindex, name);
- else
- osd_printf_verbose("Input: Adding %s #%d: %s (device id: %s)\n", (*devclass_string_table)[m_devclass], devindex, name, id);
-
- return m_device[devindex].get();
-}
-
-
-//-------------------------------------------------
-// standard_item_class - return the class of a
-// standard item
-//-------------------------------------------------
-
-input_item_class input_class::standard_item_class(input_item_id itemid)
-{
- // most everything standard is a switch, apart from the axes
- if (itemid == ITEM_ID_OTHER_SWITCH || itemid < ITEM_ID_XAXIS || (itemid > ITEM_ID_SLIDER2 && itemid < ITEM_ID_ADD_ABSOLUTE1))
- return ITEM_CLASS_SWITCH;
-
- // standard mouse axes are relative
- else if (m_devclass == DEVICE_CLASS_MOUSE || itemid == ITEM_ID_OTHER_AXIS_RELATIVE || (itemid >= ITEM_ID_ADD_RELATIVE1 && itemid <= ITEM_ID_ADD_RELATIVE16))
- return ITEM_CLASS_RELATIVE;
-
- // all other standard axes are absolute
- else
- return ITEM_CLASS_ABSOLUTE;
-}
-
-
-//-------------------------------------------------
-// remap_device_index - remaps device index by
-// mapping oldindex to newindex
-//-------------------------------------------------
-
-void input_class::remap_device_index(int oldindex, int newindex)
-{
- assert(oldindex >= 0 && oldindex < DEVICE_INDEX_MAXIMUM);
- assert(newindex >= 0 && newindex < DEVICE_INDEX_MAXIMUM);
-
- // swap indexes in m_device array
- m_device[oldindex].swap(m_device[newindex]);
-
- // update device indexes
- if (nullptr != m_device[oldindex].get())
- m_device[oldindex]->set_devindex(oldindex);
-
- if (nullptr != m_device[newindex].get())
- m_device[newindex]->set_devindex(newindex);
-
- // update the maximum index found, since newindex may
- // exceed current m_maxindex
- m_maxindex = std::max(m_maxindex, newindex);
+#endif
}
//-------------------------------------------------
-// frame_callback - per-frame callback for various
-// bookkeeping
+// input_manager - destructor
//-------------------------------------------------
-void input_class::frame_callback()
+input_manager::~input_manager()
{
- // iterate over all devices in our class
- for (int devnum = 0; devnum <= m_maxindex; devnum++)
- if (m_device[devnum] != nullptr)
- m_device[devnum]->apply_steadykey();
-}
-
-
-
-//**************************************************************************
-// INPUT MANAGER
-//**************************************************************************
-
-//-------------------------------------------------
-// input_manager - constructor
-//-------------------------------------------------
-
-input_manager::input_manager(running_machine &machine)
- : m_machine(machine),
- m_keyboard_class(*this, DEVICE_CLASS_KEYBOARD, true, machine.options().multi_keyboard()),
- m_mouse_class(*this, DEVICE_CLASS_MOUSE, machine.options().mouse(), machine.options().multi_mouse()),
- m_joystick_class(*this, DEVICE_CLASS_JOYSTICK, machine.options().joystick(), true),
- m_lightgun_class(*this, DEVICE_CLASS_LIGHTGUN, machine.options().lightgun(), true),
- m_poll_seq_last_ticks(0),
- m_poll_seq_class(ITEM_CLASS_SWITCH)
-{
- // reset code memory
- reset_memory();
-
- // create pointers for the classes
- memset(m_class, 0, sizeof(m_class));
- m_class[DEVICE_CLASS_KEYBOARD] = &m_keyboard_class;
- m_class[DEVICE_CLASS_MOUSE] = &m_mouse_class;
- m_class[DEVICE_CLASS_JOYSTICK] = &m_joystick_class;
- m_class[DEVICE_CLASS_LIGHTGUN] = &m_lightgun_class;
}
@@ -1251,7 +726,7 @@ void input_manager::reset_polling()
// 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(input_code(*device, itemid)));
+ item->set_memory(code_value(item->code()));
}
}
}
@@ -1278,7 +753,7 @@ input_code input_manager::poll_switches()
input_device_item *item = device->item(itemid);
if (item != nullptr)
{
- input_code code(*device, itemid);
+ input_code code = item->code();
// if the item is natively a switch, poll it
if (item->itemclass() == ITEM_CLASS_SWITCH)
@@ -1345,10 +820,11 @@ input_code input_manager::poll_switches()
input_code input_manager::poll_keyboard_switches()
{
// iterate over devices within each class
- for (int devnum = 0; devnum < m_keyboard_class.maxindex(); devnum++)
+ 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 = m_keyboard_class.device(devnum);
+ input_device *device = keyboard_class.device(devnum);
if (device == nullptr)
continue;
@@ -1358,7 +834,7 @@ input_code input_manager::poll_keyboard_switches()
input_device_item *item = device->item(itemid);
if (item != nullptr && item->itemclass() == ITEM_CLASS_SWITCH)
{
- input_code code(*device, itemid);
+ input_code code = item->code();
if (code_pressed_once(code))
return code;
}
@@ -1432,7 +908,7 @@ input_code input_manager::poll_axes()
input_device_item *item = device->item(itemid);
if (item != nullptr && item->itemclass() != ITEM_CLASS_SWITCH)
{
- input_code code(*device, itemid);
+ input_code code = item->code();
if (code_check_axis(*item, code))
return code;
}
@@ -1503,8 +979,12 @@ input_code input_manager::code_from_itemid(input_item_id itemid) const
for (int devnum = 0; devnum <= m_class[devclass]->maxindex(); devnum++)
{
input_device *device = m_class[devclass]->device(devnum);
- if (device != nullptr && device->item(itemid) != nullptr)
- return input_code(*device, itemid);
+ if (device != nullptr)
+ {
+ input_device_item *item = device->item(itemid);
+ if (item != nullptr)
+ return item->code();
+ }
}
return INPUT_CODE_INVALID;
@@ -1535,7 +1015,7 @@ std::string input_manager::code_name(input_code code) const
// 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_keyboard_class.maxindex() == 0)
+ if (device_class == DEVICE_CLASS_KEYBOARD && m_class[device_class]->maxindex() == 0)
{
devclass = "";
devindex.clear();
@@ -1712,6 +1192,17 @@ input_code input_manager::code_from_token(const char *_token)
//-------------------------------------------------
+// standard_token - return the standard token for
+// the given input item ID
+//-------------------------------------------------
+
+const char *input_manager::standard_token(input_item_id itemid) const
+{
+ return itemid <= ITEM_ID_MAXIMUM ? (*itemid_token_table)[itemid] : nullptr;
+}
+
+
+//-------------------------------------------------
// seq_pressed - return true if the given sequence
// of switch inputs is "pressed"
//-------------------------------------------------
@@ -2157,7 +1648,7 @@ bool input_manager::map_device_to_controller(const devicemap_table_type *devicem
return false;
// enumerate through devices and look for a match
- input_class *input_devclass = m_class[devclass];
+ input_class *input_devclass = m_class[devclass].get();
for (int devnum = 0; devnum <= input_devclass->maxindex(); devnum++)
{
input_device *device = input_devclass->device(devnum);
@@ -2165,7 +1656,7 @@ bool input_manager::map_device_to_controller(const devicemap_table_type *devicem
{
// remap devindex
input_devclass->remap_device_index(device->devindex(), devindex);
- osd_printf_verbose("Input: Remapped %s #%d: %s (device id: %s)\n", (*devclass_string_table)[input_devclass->devclass()], devindex, device->name(), device->id());
+ osd_printf_verbose("Input: Remapped %s #%d: %s (device id: %s)\n", input_devclass->name(), devindex, device->name(), device->id());
break;
}
@@ -2174,319 +1665,3 @@ bool input_manager::map_device_to_controller(const devicemap_table_type *devicem
return true;
}
-
-
-//-------------------------------------------------
-// set_global_joystick_map - set the joystick map
-// for all devices
-//-------------------------------------------------
-
-bool input_manager::set_global_joystick_map(const char *mapstring)
-{
- // parse the map
- joystick_map map;
- if (!map.parse(mapstring))
- return false;
-
- osd_printf_verbose("Input: Changing default joystick map = %s\n", map.to_string().c_str());
-
- // iterate over joysticks and set the map
- for (int joynum = 0; joynum <= m_joystick_class.maxindex(); joynum++)
- {
- input_device *device = m_joystick_class.device(joynum);
- if (device != nullptr)
- device->set_joystick_map(map);
- }
- return true;
-}
-
-
-
-//**************************************************************************
-// INPUT DEVICE ITEM
-//**************************************************************************
-
-//-------------------------------------------------
-// input_device_item - constructor
-//-------------------------------------------------
-
-input_device_item::input_device_item(input_device &device, const char *name, void *internal, input_item_id itemid, item_get_state_func getstate, input_item_class itemclass)
- : m_device(device),
- m_name(name),
- m_internal(internal),
- m_itemid(itemid),
- m_itemclass(itemclass),
- m_getstate(getstate),
- m_current(0),
- m_memory(0)
-{
- // use a standard token name for know item IDs
- if (itemid <= ITEM_ID_MAXIMUM && (*itemid_token_table)[itemid] != nullptr)
- m_token.assign((*itemid_token_table)[itemid]);
-
- // otherwise, create a tokenized name
- else {
- m_token.assign(name);
- strmakeupper(m_token);
- strdelchr(m_token, ' ');
- strdelchr(m_token, '_');
- }
-}
-
-
-//-------------------------------------------------
-// input_device_item - destructor
-//-------------------------------------------------
-
-input_device_item::~input_device_item()
-{
-}
-
-
-
-//**************************************************************************
-// INPUT DEVICE SWITCH ITEM
-//**************************************************************************
-
-//-------------------------------------------------
-// input_device_switch_item - constructor
-//-------------------------------------------------
-
-input_device_switch_item::input_device_switch_item(input_device &device, const char *name, void *internal, input_item_id itemid, item_get_state_func getstate)
- : input_device_item(device, name, internal, itemid, getstate, ITEM_CLASS_SWITCH),
- m_steadykey(0),
- m_oldkey(0)
-{
-}
-
-
-//-------------------------------------------------
-// read_as_switch - return the raw switch value,
-// modified as necessary
-//-------------------------------------------------
-
-int32_t input_device_switch_item::read_as_switch(input_item_modifier modifier)
-{
- // if we're doing a lightgun reload hack, button 1 and 2 operate differently
- input_device_class devclass = m_device.devclass();
- if (devclass == DEVICE_CLASS_LIGHTGUN && m_device.lightgun_reload_button())
- {
- // button 1 is pressed if either button 1 or 2 are active
- if (m_itemid == ITEM_ID_BUTTON1)
- {
- input_device_item *button2_item = m_device.item(ITEM_ID_BUTTON2);
- if (button2_item != nullptr)
- return button2_item->update_value() | update_value();
- }
-
- // button 2 is never officially pressed
- if (m_itemid == ITEM_ID_BUTTON2)
- return 0;
- }
-
- // steadykey for keyboards
- if (devclass == DEVICE_CLASS_KEYBOARD && m_device.steadykey_enabled())
- return m_steadykey;
-
- // everything else is just the current value as-is
- return update_value();
-}
-
-
-//-------------------------------------------------
-// read_as_relative - return the switch input as
-// a relative axis value
-//-------------------------------------------------
-
-int32_t input_device_switch_item::read_as_relative(input_item_modifier modifier)
-{
- // no translation to relative
- return 0;
-}
-
-
-//-------------------------------------------------
-// read_as_absolute - return the switch input as
-// an absolute axis value
-//-------------------------------------------------
-
-int32_t input_device_switch_item::read_as_absolute(input_item_modifier modifier)
-{
- // no translation to absolute
- return 0;
-}
-
-
-//-------------------------------------------------
-// steadykey_changed - update for steadykey
-// behavior, returning true if the current state
-// has changed since the last call
-//-------------------------------------------------
-
-bool input_device_switch_item::steadykey_changed()
-{
- int32_t old = m_oldkey;
- m_oldkey = update_value();
- if (((m_current ^ old) & 1) == 0)
- return false;
-
- // if the keypress was missed, turn it on for one frame
- if (((m_current | m_steadykey) & 1) == 0)
- m_steadykey = 1;
- return true;
-}
-
-
-
-//**************************************************************************
-// INPUT DEVICE RELATIVE ITEM
-//**************************************************************************
-
-//-------------------------------------------------
-// input_device_relative_item - constructor
-//-------------------------------------------------
-
-input_device_relative_item::input_device_relative_item(input_device &device, const char *name, void *internal, input_item_id itemid, item_get_state_func getstate)
- : input_device_item(device, name, internal, itemid, getstate, ITEM_CLASS_RELATIVE)
-{
-}
-
-
-//-------------------------------------------------
-// read_as_switch - return the relative value as
-// a switch result based on the modifier
-//-------------------------------------------------
-
-int32_t input_device_relative_item::read_as_switch(input_item_modifier modifier)
-{
- // process according to modifiers
- if (modifier == ITEM_MODIFIER_POS || modifier == ITEM_MODIFIER_RIGHT || modifier == ITEM_MODIFIER_DOWN)
- return (update_value() > 0);
- else if (modifier == ITEM_MODIFIER_NEG || modifier == ITEM_MODIFIER_LEFT || modifier == ITEM_MODIFIER_UP)
- return (update_value() < 0);
-
- // all other cases just return 0
- return 0;
-}
-
-
-//-------------------------------------------------
-// read_as_relative - return the relative input
-// as a relative axis value
-//-------------------------------------------------
-
-int32_t input_device_relative_item::read_as_relative(input_item_modifier modifier)
-{
- // just return directly
- return update_value();
-}
-
-
-//-------------------------------------------------
-// read_as_absolute - return the relative input
-// as an absolute axis value
-//-------------------------------------------------
-
-int32_t input_device_relative_item::read_as_absolute(input_item_modifier modifier)
-{
- // no translation to absolute
- return 0;
-}
-
-
-
-//**************************************************************************
-// INPUT DEVICE ABSOLUTE ITEM
-//**************************************************************************
-
-//-------------------------------------------------
-// input_device_absolute_item - constructor
-//-------------------------------------------------
-
-input_device_absolute_item::input_device_absolute_item(input_device &device, const char *name, void *internal, input_item_id itemid, item_get_state_func getstate)
- : input_device_item(device, name, internal, itemid, getstate, ITEM_CLASS_ABSOLUTE)
-{
-}
-
-
-//-------------------------------------------------
-// read_as_switch - return the absolute value as
-// a switch result based on the modifier
-//-------------------------------------------------
-
-int32_t input_device_absolute_item::read_as_switch(input_item_modifier modifier)
-{
- // start with the current value
- int32_t result = m_device.apply_deadzone_and_saturation(update_value());
- assert(result >= INPUT_ABSOLUTE_MIN && result <= INPUT_ABSOLUTE_MAX);
-
- // left/right/up/down: if this is a joystick, fetch the paired X/Y axis values and convert
- if (m_device.devclass() == DEVICE_CLASS_JOYSTICK && modifier >= ITEM_MODIFIER_LEFT && modifier <= ITEM_MODIFIER_DOWN)
- {
- input_device_item *xaxis_item = m_device.item(ITEM_ID_XAXIS);
- input_device_item *yaxis_item = m_device.item(ITEM_ID_YAXIS);
- if (xaxis_item != nullptr && yaxis_item != nullptr)
- {
- // determine which item we didn't update, and update it
- assert(this == xaxis_item || this == yaxis_item);
- if (this == xaxis_item)
- yaxis_item->update_value();
- else
- xaxis_item->update_value();
-
- // now map the X and Y axes to a 9x9 grid using the raw values
- return (m_device.joymap().update(xaxis_item->current(), yaxis_item->current()) >> (modifier - ITEM_MODIFIER_LEFT)) & 1;
- }
- }
-
- // positive/negative: true if past the deadzone in either direction
- if (modifier == ITEM_MODIFIER_POS || modifier == ITEM_MODIFIER_RIGHT || modifier == ITEM_MODIFIER_DOWN)
- return (result > 0);
- else if (modifier == ITEM_MODIFIER_NEG || modifier == ITEM_MODIFIER_LEFT || modifier == ITEM_MODIFIER_UP)
- return (result < 0);
-
- // all other cases just return 0
- return 0;
-}
-
-
-//-------------------------------------------------
-// read_as_relative - return the absolute input
-// as a relative axis value
-//-------------------------------------------------
-
-int32_t input_device_absolute_item::read_as_relative(input_item_modifier modifier)
-{
- // no translation to relative
- return 0;
-}
-
-
-//-------------------------------------------------
-// read_as_absolute - return the absolute input
-// as an absolute axis value, with appropriate
-// tweaks
-//-------------------------------------------------
-
-int32_t input_device_absolute_item::read_as_absolute(input_item_modifier modifier)
-{
- // start with the current value
- int32_t result = m_device.apply_deadzone_and_saturation(update_value());
- assert(result >= INPUT_ABSOLUTE_MIN && result <= INPUT_ABSOLUTE_MAX);
-
- // if we're doing a lightgun reload hack, override the value
- if (m_device.devclass() == DEVICE_CLASS_LIGHTGUN && m_device.lightgun_reload_button())
- {
- // if it is pressed, return (min,max)
- input_device_item *button2_item = m_device.item(ITEM_ID_BUTTON2);
- if (button2_item != nullptr && button2_item->update_value())
- result = (m_itemid == ITEM_ID_XAXIS) ? INPUT_ABSOLUTE_MIN : INPUT_ABSOLUTE_MAX;
- }
-
- // positive/negative: scale to full axis
- if (modifier == ITEM_MODIFIER_POS)
- result = std::max(result, 0) * 2 + INPUT_ABSOLUTE_MIN;
- if (modifier == ITEM_MODIFIER_NEG)
- result = std::max(-result, 0) * 2 + INPUT_ABSOLUTE_MIN;
- return result;
-}
diff --git a/src/emu/input.h b/src/emu/input.h
index 42392871470..e8ed5aac5a3 100644
--- a/src/emu/input.h
+++ b/src/emu/input.h
@@ -350,61 +350,11 @@ DECLARE_ENUM_OPERATORS(input_item_id)
class input_device_item;
class input_device;
class input_class;
-class input_manager;
-// callback for getting the value of an item on a device
-typedef int32_t (*item_get_state_func)(void *device_internal, void *item_internal);
-
// controller alias table typedef
typedef std::map<std::string, std::string> devicemap_table_type;
-// ======================> joystick_map
-
-// a 9x9 joystick map
-class joystick_map
-{
-public:
- // construction/destruction
- joystick_map();
- joystick_map(const joystick_map &src) { copy(src); }
-
- // operators
- joystick_map &operator=(const joystick_map &src) { if (this != &src) copy(src); return *this; }
-
- // parse from a string
- bool parse(const char *mapstring);
-
- // create a friendly string
- std::string to_string() const;
-
- // update the state of a live map
- uint8_t update(int32_t xaxisval, int32_t yaxisval);
-
- // joystick mapping codes
- static const uint8_t JOYSTICK_MAP_NEUTRAL = 0x00;
- static const uint8_t JOYSTICK_MAP_LEFT = 0x01;
- static const uint8_t JOYSTICK_MAP_RIGHT = 0x02;
- static const uint8_t JOYSTICK_MAP_UP = 0x04;
- static const uint8_t JOYSTICK_MAP_DOWN = 0x08;
- static const uint8_t JOYSTICK_MAP_STICKY = 0x0f;
-
-private:
- // internal helpers
- void copy(const joystick_map &src)
- {
- memcpy(m_map, src.m_map, sizeof(m_map));
- m_lastmap = JOYSTICK_MAP_NEUTRAL;
- m_origstring = src.m_origstring;
- }
-
- // internal state
- uint8_t m_map[9][9]; // 9x9 grid
- uint8_t m_lastmap; // last value returned (for sticky tracking)
- std::string m_origstring; // originally parsed string
-};
-
-
// ======================> input_code
// a combined code that describes a particular input on a particular device
@@ -423,7 +373,6 @@ public:
}
input_code(const input_code &src)
: m_internal(src.m_internal) { }
- input_code(input_device &device, input_item_id itemid);
// operators
bool operator==(const input_code &rhs) const { return m_internal == rhs.m_internal; }
@@ -495,155 +444,6 @@ private:
};
-// ======================> input_device_item
-
-// a single item on an input device
-class input_device_item
-{
-protected:
- // construction/destruction
- input_device_item(input_device &device, const char *name, void *internal, input_item_id itemid, item_get_state_func getstate, input_item_class itemclass);
-
-public:
- virtual ~input_device_item();
-
- // getters
- input_device &device() const { return m_device; }
- input_manager &manager() const;
- running_machine &machine() const;
- const char *name() const { return m_name.c_str(); }
- void *internal() const { return m_internal; }
- input_item_id itemid() const { return m_itemid; }
- input_item_class itemclass() const { return m_itemclass; }
- const char *token() const { return m_token.c_str(); }
- int32_t current() const { return m_current; }
- int32_t memory() const { return m_memory; }
-
- // helpers
- int32_t update_value();
- void set_memory(int32_t value) { m_memory = value; }
-
- // readers
- virtual int32_t read_as_switch(input_item_modifier modifier) = 0;
- virtual int32_t read_as_relative(input_item_modifier modifier) = 0;
- virtual int32_t read_as_absolute(input_item_modifier modifier) = 0;
-
-protected:
- // internal state
- input_device & m_device; // reference to our owning device
- std::string m_name; // string name of item
- void * m_internal; // internal callback pointer
- input_item_id m_itemid; // originally specified item id
- input_item_class m_itemclass; // class of the item
- item_get_state_func m_getstate; // get state callback
- std::string m_token; // tokenized name for non-standard items
-
- // live state
- int32_t m_current; // current raw value
- int32_t m_memory; // "memory" value, to remember where we started during polling
-};
-
-
-// ======================> input_device
-
-// a logical device of a given class that can provide input
-class input_device
-{
- friend class input_class;
-
-public:
- // construction/destruction
- input_device(input_class &_class, int _devindex, const char *_name, const char *_id, void *_internal);
- // getters
- input_class &device_class() const { return m_class; }
- input_manager &manager() const;
- running_machine &machine() const;
- input_device_class devclass() const;
- const char *name() const { return m_name.c_str(); }
- const char *id() const { return m_id.c_str(); }
- int devindex() const { return m_devindex; }
- input_device_item *item(input_item_id index) const { return m_item[index].get(); }
- input_item_id maxitem() const { return m_maxitem; }
- void *internal() const { return m_internal; }
- joystick_map &joymap() { return m_joymap; }
- bool steadykey_enabled() const { return m_steadykey_enabled; }
- bool lightgun_reload_button() const { return m_lightgun_reload_button; }
-
- // setters
- void set_devindex(int devindex) { m_devindex = devindex; }
-
- // item management
- input_item_id add_item(const char *name, input_item_id itemid, item_get_state_func getstate, void *internal = nullptr);
- void set_joystick_map(const joystick_map &map) { m_joymap = map; }
-
- // helpers
- int32_t apply_deadzone_and_saturation(int32_t value) const;
- void apply_steadykey() const;
- bool match_device_id(const char * deviceid);
-
-private:
- // internal state
- input_class & m_class; // reference to our class
- std::string m_name; // string name of device
- std::string m_id; // id of device
- int m_devindex; // device index of this device
- std::unique_ptr<input_device_item> m_item[ITEM_ID_ABSOLUTE_MAXIMUM+1]; // array of pointers to items
- input_item_id m_maxitem; // maximum item index
- void * m_internal; // internal callback pointer
-
- // joystick information
- joystick_map m_joymap; // joystick map for this device
- int32_t m_joystick_deadzone; // deadzone for joystick
- int32_t m_joystick_saturation; // saturation position for joystick
- bool m_steadykey_enabled; // steadykey enabled for keyboards
- bool m_lightgun_reload_button; // lightgun reload hack
-};
-
-
-// ======================> input_class
-
-// a class of device that provides input
-class input_class
-{
-public:
- // construction/destruction
- input_class(input_manager &manager, input_device_class devclass, bool enabled = false, bool multi = false);
-
- // getters
- input_manager &manager() const { return m_manager; }
- running_machine &machine() const;
- input_device *device(int index) const { return (index <= m_maxindex) ? m_device[index].get() : nullptr; }
- input_device_class devclass() const { return m_devclass; }
- int maxindex() const { return m_maxindex; }
- bool enabled() const { return m_enabled; }
- bool multi() const { return m_multi; }
-
- // setters
- void enable(bool state = true) { m_enabled = state; }
- void set_multi(bool multi = true) { m_multi = multi; }
-
- // device management
- input_device *add_device(const char *name, const char *id, void *internal = nullptr);
- input_device *add_device(int devindex, const char *name, const char *id, void *internal = nullptr);
-
- // misc helpers
- input_item_class standard_item_class(input_item_id itemid);
- void remap_device_index(int oldindex, int newindex);
-
-private:
- // internal helpers
- void frame_callback();
-
- // internal state
- input_manager & m_manager; // reference to our manager
- std::unique_ptr<input_device> m_device[DEVICE_INDEX_MAXIMUM]; // array of devices in this class
- input_device_class m_devclass; // our device class
- int m_maxindex; // maximum populated index
- bool m_enabled; // is this class enabled?
- bool m_multi; // are multiple instances of this class allowed?
-};
-
-
// ======================> input_manager
// global machine-level information about devices
@@ -652,10 +452,11 @@ class input_manager
public:
// construction/destruction
input_manager(running_machine &machine);
+ ~input_manager();
// getters
running_machine &machine() const { return m_machine; }
- input_class &device_class(input_device_class devclass) { assert(devclass < ARRAY_LENGTH(m_class)); assert(m_class[devclass] != nullptr); return *m_class[devclass]; }
+ input_class &device_class(input_device_class devclass) { assert(devclass >= DEVICE_CLASS_FIRST_VALID && devclass <= DEVICE_CLASS_LAST_VALID); return *m_class[devclass]; }
// input code readers
int32_t code_value(input_code code);
@@ -675,6 +476,7 @@ public:
std::string code_name(input_code code) const;
std::string code_to_token(input_code code) const;
input_code code_from_token(const char *_token);
+ const char *standard_token(input_item_id itemid) const;
// input sequence readers
bool seq_pressed(const input_seq &seq);
@@ -691,7 +493,6 @@ public:
void seq_from_tokens(input_seq &seq, const char *_token);
// misc
- bool set_global_joystick_map(const char *mapstring);
bool map_device_to_controller(const devicemap_table_type *devicemap_table = nullptr);
private:
@@ -704,11 +505,7 @@ private:
input_code m_switch_memory[64];
// classes
- input_class m_keyboard_class;
- input_class m_mouse_class;
- input_class m_joystick_class;
- input_class m_lightgun_class;
- input_class * m_class[DEVICE_CLASS_MAXIMUM];
+ std::array<std::unique_ptr<input_class>, DEVICE_CLASS_MAXIMUM> m_class;
// sequence polling state
input_seq m_poll_seq;
@@ -1165,32 +962,4 @@ private:
-//**************************************************************************
-// GLOBAL VARIABLES
-//**************************************************************************
-
-// joystick maps
-extern const char joystick_map_8way[];
-extern const char joystick_map_4way_diagonal[];
-
-
-//**************************************************************************
-// INLINE FUNCTIONS
-//**************************************************************************
-
-// input_device_item helpers
-inline input_manager &input_device_item::manager() const { return m_device.manager(); }
-inline running_machine &input_device_item::machine() const { return m_device.machine(); }
-inline int32_t input_device_item::update_value() { return m_current = (*m_getstate)(m_device.internal(), m_internal); }
-
-// input_device helpers
-inline input_manager &input_device::manager() const { return m_class.manager(); }
-inline running_machine &input_device::machine() const { return m_class.machine(); }
-inline input_device_class input_device::devclass() const { return m_class.devclass(); }
-
-// input_class helpers
-inline running_machine &input_class::machine() const { return m_manager.machine(); }
-
-
-
#endif // __INPUT_H__
diff --git a/src/emu/inputdev.cpp b/src/emu/inputdev.cpp
new file mode 100644
index 00000000000..cf56387578f
--- /dev/null
+++ b/src/emu/inputdev.cpp
@@ -0,0 +1,947 @@
+// license:BSD-3-Clause
+// copyright-holders:Aaron Giles
+/***************************************************************************
+
+ inputdev.cpp
+
+ Input devices, items and device classes.
+
+***************************************************************************/
+
+#include "emu.h"
+#include "emuopts.h"
+#include "inputdev.h"
+
+
+//**************************************************************************
+// TYPE DEFINITIONS
+//**************************************************************************
+
+// ======================> input_device_switch_item
+
+// derived input item representing a switch input
+class input_device_switch_item : public input_device_item
+{
+public:
+ // construction/destruction
+ input_device_switch_item(input_device &device, const char *name, void *internal, input_item_id itemid, item_get_state_func getstate);
+
+ // readers
+ virtual int32_t read_as_switch(input_item_modifier modifier) override;
+ virtual int32_t read_as_relative(input_item_modifier modifier) override;
+ virtual int32_t read_as_absolute(input_item_modifier modifier) override;
+
+ // steadykey helper
+ bool steadykey_changed();
+ void steadykey_update_to_current() { m_steadykey = m_current; }
+
+private:
+ // internal state
+ int32_t m_steadykey; // the live steadykey state
+ int32_t m_oldkey; // old live state
+};
+
+
+// ======================> input_device_switch_item
+
+// derived input item representing a relative axis input
+class input_device_relative_item : public input_device_item
+{
+public:
+ // construction/destruction
+ input_device_relative_item(input_device &device, const char *name, void *internal, input_item_id itemid, item_get_state_func getstate);
+
+ // readers
+ virtual int32_t read_as_switch(input_item_modifier modifier) override;
+ virtual int32_t read_as_relative(input_item_modifier modifier) override;
+ virtual int32_t read_as_absolute(input_item_modifier modifier) override;
+};
+
+
+// ======================> input_device_switch_item
+
+// derived input item representing an absolute axis input
+class input_device_absolute_item : public input_device_item
+{
+public:
+ // construction/destruction
+ input_device_absolute_item(input_device &device, const char *name, void *internal, input_item_id itemid, item_get_state_func getstate);
+
+ // readers
+ virtual int32_t read_as_switch(input_item_modifier modifier) override;
+ virtual int32_t read_as_relative(input_item_modifier modifier) override;
+ virtual int32_t read_as_absolute(input_item_modifier modifier) override;
+};
+
+
+//**************************************************************************
+// GLOBAL VARIABLES
+//**************************************************************************
+
+// standard joystick mappings
+const char input_class_joystick::map_8way[] = "7778...4445";
+const char input_class_joystick::map_4way_diagonal[] = "4444s8888..444458888.444555888.ss5.222555666.222256666.2222s6666.2222s6666";
+// const char input_class_joystick::map_4way_sticky[] = "s8.4s8.44s8.4445";
+
+
+//**************************************************************************
+// JOYSTICK MAP
+//**************************************************************************
+
+//-------------------------------------------------
+// joystick_map - constructor
+//-------------------------------------------------
+
+joystick_map::joystick_map()
+ : m_lastmap(JOYSTICK_MAP_NEUTRAL)
+{
+ // parse the standard 8-way map as default
+ parse(input_class_joystick::map_8way);
+}
+
+
+//-------------------------------------------------
+// parse - parse a string into a joystick map
+//-------------------------------------------------
+
+bool joystick_map::parse(const char *mapstring)
+{
+ // save a copy of the original string
+ m_origstring = mapstring;
+
+ // iterate over rows
+ for (int rownum = 0; rownum < 9; rownum++)
+ {
+ // if we're done, copy from another row
+ if (*mapstring == 0 || *mapstring == '.')
+ {
+ bool symmetric = (rownum >= 5 && *mapstring == 0);
+ const uint8_t *srcrow = &m_map[symmetric ? (8 - rownum) : (rownum - 1)][0];
+
+ // if this is row 0, we don't have a source row -- invalid
+ if (rownum == 0)
+ return false;
+
+ // copy from the srcrow, applying up/down symmetry if in the bottom half
+ for (int colnum = 0; colnum < 9; colnum++)
+ {
+ uint8_t val = srcrow[colnum];
+ if (symmetric)
+ val = (val & (JOYSTICK_MAP_LEFT | JOYSTICK_MAP_RIGHT)) | ((val & JOYSTICK_MAP_UP) << 1) | ((val & JOYSTICK_MAP_DOWN) >> 1);
+ m_map[rownum][colnum] = val;
+ }
+ }
+
+ // otherwise, parse this column
+ else
+ {
+ for (int colnum = 0; colnum < 9; colnum++)
+ {
+ // if we're at the end of row, copy previous to the middle, then apply left/right symmetry
+ if (colnum > 0 && (*mapstring == 0 || *mapstring == '.'))
+ {
+ bool symmetric = (colnum >= 5);
+ uint8_t val = m_map[rownum][symmetric ? (8 - colnum) : (colnum - 1)];
+ if (symmetric)
+ val = (val & (JOYSTICK_MAP_UP | JOYSTICK_MAP_DOWN)) | ((val & JOYSTICK_MAP_LEFT) << 1) | ((val & JOYSTICK_MAP_RIGHT) >> 1);
+ m_map[rownum][colnum] = val;
+ }
+
+ // otherwise, convert the character to its value
+ else
+ {
+ static const uint8_t charmap[] =
+ {
+ JOYSTICK_MAP_UP | JOYSTICK_MAP_LEFT,
+ JOYSTICK_MAP_UP,
+ JOYSTICK_MAP_UP | JOYSTICK_MAP_RIGHT,
+ JOYSTICK_MAP_LEFT,
+ JOYSTICK_MAP_NEUTRAL,
+ JOYSTICK_MAP_RIGHT,
+ JOYSTICK_MAP_DOWN | JOYSTICK_MAP_LEFT,
+ JOYSTICK_MAP_DOWN,
+ JOYSTICK_MAP_DOWN | JOYSTICK_MAP_RIGHT,
+ JOYSTICK_MAP_STICKY
+ };
+ static const char validchars[] = "789456123s";
+ const char *ptr = strchr(validchars, *mapstring++);
+
+ // invalid characters exit immediately
+ if (ptr == nullptr)
+ return false;
+ m_map[rownum][colnum] = charmap[ptr - validchars];
+ }
+ }
+ }
+
+ // if we ended with a period, advance to the next row
+ if (*mapstring == '.')
+ mapstring++;
+ }
+ return true;
+}
+
+
+//-------------------------------------------------
+// to_string - output the map as a string for
+// friendly display
+//-------------------------------------------------
+
+std::string joystick_map::to_string() const
+{
+ std::string str(m_origstring);
+ str.append("\n");
+ for (auto & elem : m_map)
+ {
+ str.append(" ");
+ for (auto & elem_colnum : elem)
+ switch (elem_colnum)
+ {
+ case JOYSTICK_MAP_UP | JOYSTICK_MAP_LEFT: str.append("7"); break;
+ case JOYSTICK_MAP_UP: str.append("8"); break;
+ case JOYSTICK_MAP_UP | JOYSTICK_MAP_RIGHT: str.append("9"); break;
+ case JOYSTICK_MAP_LEFT: str.append("4"); break;
+ case JOYSTICK_MAP_NEUTRAL: str.append("5"); break;
+ case JOYSTICK_MAP_RIGHT: str.append("6"); break;
+ case JOYSTICK_MAP_DOWN | JOYSTICK_MAP_LEFT: str.append("1"); break;
+ case JOYSTICK_MAP_DOWN: str.append("2"); break;
+ case JOYSTICK_MAP_DOWN | JOYSTICK_MAP_RIGHT:str.append("3"); break;
+ case JOYSTICK_MAP_STICKY: str.append("s"); break;
+ default: str.append("?"); break;
+ }
+
+ str.append("\n");
+ }
+ return str;
+}
+
+
+//-------------------------------------------------
+// update - update the state of the joystick
+// map based on the given X/Y axis values
+//-------------------------------------------------
+
+uint8_t joystick_map::update(int32_t xaxisval, int32_t yaxisval)
+{
+ // now map the X and Y axes to a 9x9 grid using the raw values
+ xaxisval = ((xaxisval - INPUT_ABSOLUTE_MIN) * 9) / (INPUT_ABSOLUTE_MAX - INPUT_ABSOLUTE_MIN + 1);
+ yaxisval = ((yaxisval - INPUT_ABSOLUTE_MIN) * 9) / (INPUT_ABSOLUTE_MAX - INPUT_ABSOLUTE_MIN + 1);
+ uint8_t mapval = m_map[yaxisval][xaxisval];
+
+ // handle stickiness
+ if (mapval == JOYSTICK_MAP_STICKY)
+ mapval = m_lastmap;
+ else
+ m_lastmap = mapval;
+
+ // return based on whether the appropriate bit is set
+ return mapval;
+}
+
+
+
+//**************************************************************************
+// INPUT DEVICE
+//**************************************************************************
+
+//-------------------------------------------------
+// input_device - constructor
+//-------------------------------------------------
+
+input_device::input_device(input_manager &manager, const char *name, const char *id, void *internal)
+ : m_manager(manager),
+ m_name(name),
+ m_id(id),
+ m_devindex(-1),
+ m_maxitem(input_item_id(0)),
+ m_internal(internal),
+ m_steadykey_enabled(manager.machine().options().steadykey()),
+ m_lightgun_reload_button(manager.machine().options().offscreen_reload())
+{
+}
+
+
+//-------------------------------------------------
+// input_device - destructor
+//-------------------------------------------------
+
+input_device::~input_device()
+{
+}
+
+
+//-------------------------------------------------
+// add_item - add a new item to an input device
+//-------------------------------------------------
+
+input_item_id input_device::add_item(const char *name, input_item_id itemid, item_get_state_func getstate, void *internal)
+{
+ assert_always(machine().phase() == MACHINE_PHASE_INIT, "Can only call input_device::add_item at init time!");
+ assert(name != nullptr);
+ assert(itemid > ITEM_ID_INVALID && itemid < ITEM_ID_MAXIMUM);
+ assert(getstate != nullptr);
+
+ // if we have a generic ID, pick a new internal one
+ input_item_id originalid = itemid;
+ if (itemid >= ITEM_ID_OTHER_SWITCH && itemid <= ITEM_ID_OTHER_AXIS_RELATIVE)
+ for (itemid = (input_item_id)(ITEM_ID_MAXIMUM + 1); itemid <= ITEM_ID_ABSOLUTE_MAXIMUM; ++itemid)
+ if (m_item[itemid] == nullptr)
+ break;
+ assert(itemid <= ITEM_ID_ABSOLUTE_MAXIMUM);
+
+ // make sure we don't have any overlap
+ assert(m_item[itemid] == nullptr);
+
+ // determine the class and create the appropriate item class
+ switch (m_manager.device_class(devclass()).standard_item_class(originalid))
+ {
+ case ITEM_CLASS_SWITCH:
+ m_item[itemid] = std::make_unique<input_device_switch_item>(*this, name, internal, itemid, getstate);
+ break;
+
+ case ITEM_CLASS_RELATIVE:
+ m_item[itemid] = std::make_unique<input_device_relative_item>(*this, name, internal, itemid, getstate);
+ break;
+
+ case ITEM_CLASS_ABSOLUTE:
+ m_item[itemid] = std::make_unique<input_device_absolute_item>(*this, name, internal, itemid, getstate);
+ break;
+
+ default:
+ m_item[itemid] = nullptr;
+ assert(false);
+ }
+
+ // assign the new slot and update the maximum
+ m_maxitem = std::max(m_maxitem, itemid);
+ return itemid;
+}
+
+
+//-------------------------------------------------
+// match_device_id - match device id via
+// substring search
+//-------------------------------------------------
+
+bool input_device::match_device_id(const char *deviceid)
+{
+ std::string deviceidupper(deviceid);
+ std::string idupper(m_id);
+
+ strmakeupper(deviceidupper);
+ strmakeupper(idupper);
+
+ return std::string::npos == idupper.find(deviceidupper) ? false : true;
+}
+
+
+//**************************************************************************
+// SPECIFIC INPUT DEVICES
+//**************************************************************************
+
+//-------------------------------------------------
+// input_device_keyboard - constructor
+//-------------------------------------------------
+
+input_device_keyboard::input_device_keyboard(input_manager &manager, const char *_name, const char *_id, void *_internal)
+ : input_device(manager, _name, _id, _internal)
+{
+}
+
+
+//-------------------------------------------------
+// apply_steadykey - apply steadykey option
+//-------------------------------------------------
+
+void input_device_keyboard::apply_steadykey() const
+{
+ // ignore if not enabled
+ if (!steadykey_enabled())
+ return;
+
+ // update the state of all the keys and see if any changed state
+ bool anything_changed = false;
+ for (input_item_id itemid = ITEM_ID_FIRST_VALID; itemid <= maxitem(); ++itemid)
+ {
+ input_device_item *itm = item(itemid);
+ if (itm != nullptr && itm->itemclass() == ITEM_CLASS_SWITCH)
+ if (downcast<input_device_switch_item &>(*itm).steadykey_changed())
+ anything_changed = true;
+ }
+
+ // if the keyboard state is stable, flush the current state
+ if (!anything_changed)
+ for (input_item_id itemid = ITEM_ID_FIRST_VALID; itemid <= maxitem(); ++itemid)
+ {
+ input_device_item *itm = item(itemid);
+ if (itm != nullptr && itm->itemclass() == ITEM_CLASS_SWITCH)
+ downcast<input_device_switch_item &>(*itm).steadykey_update_to_current();
+ }
+}
+
+
+//-------------------------------------------------
+// input_device_mouse - constructor
+//-------------------------------------------------
+
+input_device_mouse::input_device_mouse(input_manager &manager, const char *_name, const char *_id, void *_internal)
+ : input_device(manager, _name, _id, _internal)
+{
+}
+
+
+//-------------------------------------------------
+// input_device_lightgun - constructor
+//-------------------------------------------------
+
+input_device_lightgun::input_device_lightgun(input_manager &manager, const char *_name, const char *_id, void *_internal)
+ : input_device(manager, _name, _id, _internal)
+{
+}
+
+
+//-------------------------------------------------
+// input_device_joystick - constructor
+//-------------------------------------------------
+
+input_device_joystick::input_device_joystick(input_manager &manager, const char *_name, const char *_id, void *_internal)
+ : input_device(manager, _name, _id, _internal),
+ m_joystick_deadzone((int32_t)(manager.machine().options().joystick_deadzone() * INPUT_ABSOLUTE_MAX)),
+ m_joystick_saturation((int32_t)(manager.machine().options().joystick_saturation() * INPUT_ABSOLUTE_MAX))
+{
+ // get the default joystick map
+ const char *mapstring = machine().options().joystick_map();
+ if (mapstring[0] == 0 || strcmp(mapstring, "auto") == 0)
+ mapstring = input_class_joystick::map_8way;
+
+ // parse it
+ if (!m_joymap.parse(mapstring))
+ {
+ osd_printf_error("Invalid joystick map: %s\n", mapstring);
+ m_joymap.parse(input_class_joystick::map_8way);
+ }
+ else if (mapstring != input_class_joystick::map_8way)
+ osd_printf_verbose("Input: Default joystick map = %s\n", m_joymap.to_string().c_str());
+}
+
+
+//-------------------------------------------------
+// adjust_absolute_value - apply joystick device
+// deadzone and saturation parameters to an
+// absolute value
+//-------------------------------------------------
+
+int32_t input_device_joystick::adjust_absolute_value(int32_t result) const
+{
+ // properties are symmetric
+ bool negative = false;
+ if (result < 0)
+ {
+ negative = true;
+ result = -result;
+ }
+
+ // if in the deadzone, return 0
+ if (result < m_joystick_deadzone)
+ result = 0;
+
+ // if saturated, return the max
+ else if (result > m_joystick_saturation)
+ result = INPUT_ABSOLUTE_MAX;
+
+ // otherwise, scale
+ else
+ result = (int64_t)(result - m_joystick_deadzone) * (int64_t)INPUT_ABSOLUTE_MAX / (int64_t)(m_joystick_saturation - m_joystick_deadzone);
+
+ // re-apply sign and return
+ return negative ? -result : result;
+}
+
+
+//**************************************************************************
+// INPUT CLASS
+//**************************************************************************
+
+//-------------------------------------------------
+// input_class - constructor
+//-------------------------------------------------
+
+input_class::input_class(input_manager &manager, input_device_class devclass, const char *name, bool enabled, bool multi)
+ : m_manager(manager),
+ m_devclass(devclass),
+ m_name(name),
+ m_maxindex(0),
+ m_enabled(enabled),
+ m_multi(multi)
+{
+ assert(m_name != nullptr);
+}
+
+
+//-------------------------------------------------
+// input_class - destructor
+//-------------------------------------------------
+
+input_class::~input_class()
+{
+}
+
+
+//-------------------------------------------------
+// add_device - add a new input device
+//-------------------------------------------------
+
+input_device *input_class::add_device(const char *name, const char *id, void *internal)
+{
+ assert_always(machine().phase() == MACHINE_PHASE_INIT, "Can only call input_class::add_device at init time!");
+ assert(name != nullptr);
+ assert(id != nullptr);
+
+ // allocate a new device and add it to the index
+ return add_device(make_device(name, id, internal));
+}
+
+input_device *input_class::add_device(std::unique_ptr<input_device> &&new_device)
+{
+ assert(new_device->devclass() == m_devclass);
+
+ // find the next empty index
+ for (int devindex = 0; devindex < DEVICE_INDEX_MAXIMUM; devindex++)
+ if (m_device[devindex] == nullptr)
+ {
+ // update the device and maximum index found
+ new_device->set_devindex(devindex);
+ m_maxindex = std::max(m_maxindex, devindex);
+
+ if (new_device->id()[0] == 0)
+ osd_printf_verbose("Input: Adding %s #%d: %s\n", m_name, devindex, new_device->name());
+ else
+ osd_printf_verbose("Input: Adding %s #%d: %s (device id: %s)\n", m_name, devindex, new_device->name(), new_device->id());
+
+ m_device[devindex] = std::move(new_device);
+ return m_device[devindex].get();
+ }
+
+ throw emu_fatalerror("Input: Too many %s devices\n", m_name);
+}
+
+
+//-------------------------------------------------
+// standard_item_class - return the class of a
+// standard item
+//-------------------------------------------------
+
+input_item_class input_class::standard_item_class(input_item_id itemid) const
+{
+ // most everything standard is a switch, apart from the axes
+ if (itemid == ITEM_ID_OTHER_SWITCH || itemid < ITEM_ID_XAXIS || (itemid > ITEM_ID_SLIDER2 && itemid < ITEM_ID_ADD_ABSOLUTE1))
+ return ITEM_CLASS_SWITCH;
+
+ // standard mouse axes are relative
+ else if (m_devclass == DEVICE_CLASS_MOUSE || itemid == ITEM_ID_OTHER_AXIS_RELATIVE || (itemid >= ITEM_ID_ADD_RELATIVE1 && itemid <= ITEM_ID_ADD_RELATIVE16))
+ return ITEM_CLASS_RELATIVE;
+
+ // all other standard axes are absolute
+ else
+ return ITEM_CLASS_ABSOLUTE;
+}
+
+
+//-------------------------------------------------
+// remap_device_index - remaps device index by
+// mapping oldindex to newindex
+//-------------------------------------------------
+
+void input_class::remap_device_index(int oldindex, int newindex)
+{
+ assert(oldindex >= 0 && oldindex < DEVICE_INDEX_MAXIMUM);
+ assert(newindex >= 0 && newindex < DEVICE_INDEX_MAXIMUM);
+
+ // swap indexes in m_device array
+ m_device[oldindex].swap(m_device[newindex]);
+
+ // update device indexes
+ if (nullptr != m_device[oldindex].get())
+ m_device[oldindex]->set_devindex(oldindex);
+
+ if (nullptr != m_device[newindex].get())
+ m_device[newindex]->set_devindex(newindex);
+
+ // update the maximum index found, since newindex may
+ // exceed current m_maxindex
+ m_maxindex = std::max(m_maxindex, newindex);
+}
+
+
+//**************************************************************************
+// SPECIFIC INPUT CLASSES
+//**************************************************************************
+
+//-------------------------------------------------
+// input_class_keyboard - constructor
+//-------------------------------------------------
+
+input_class_keyboard::input_class_keyboard(input_manager &manager)
+ : input_class(manager, DEVICE_CLASS_KEYBOARD, "keyboard", true, manager.machine().options().multi_keyboard())
+{
+ // request a per-frame callback for the keyboard class
+ machine().add_notifier(MACHINE_NOTIFY_FRAME, machine_notify_delegate(FUNC(input_class_keyboard::frame_callback), this));
+}
+
+
+//-------------------------------------------------
+// frame_callback - per-frame callback for various
+// bookkeeping
+//-------------------------------------------------
+
+void input_class_keyboard::frame_callback()
+{
+ // iterate over all devices in our class
+ for (int devnum = 0; devnum <= maxindex(); devnum++)
+ if (device(devnum) != nullptr)
+ downcast<input_device_keyboard &>(*device(devnum)).apply_steadykey();
+}
+
+
+//-------------------------------------------------
+// input_class_mouse - constructor
+//-------------------------------------------------
+
+input_class_mouse::input_class_mouse(input_manager &manager)
+ : input_class(manager, DEVICE_CLASS_MOUSE, "mouse", manager.machine().options().mouse(), manager.machine().options().multi_mouse())
+{
+}
+
+
+//-------------------------------------------------
+// input_class_lightgun - constructor
+//-------------------------------------------------
+
+input_class_lightgun::input_class_lightgun(input_manager &manager)
+ : input_class(manager, DEVICE_CLASS_LIGHTGUN, "lightgun", manager.machine().options().lightgun(), true)
+{
+}
+
+
+//-------------------------------------------------
+// input_class_joystick - constructor
+//-------------------------------------------------
+
+input_class_joystick::input_class_joystick(input_manager &manager)
+ : input_class(manager, DEVICE_CLASS_JOYSTICK, "joystick", manager.machine().options().joystick(), true)
+{
+}
+
+
+//-------------------------------------------------
+// set_global_joystick_map - set the map for all
+// joysticks
+//-------------------------------------------------
+
+bool input_class_joystick::set_global_joystick_map(const char *mapstring)
+{
+ // parse the map
+ joystick_map map;
+ if (!map.parse(mapstring))
+ return false;
+
+ osd_printf_verbose("Input: Changing default joystick map = %s\n", map.to_string().c_str());
+
+ // iterate over joysticks and set the map
+ for (int joynum = 0; joynum <= maxindex(); joynum++)
+ if (device(joynum) != nullptr)
+ downcast<input_device_joystick &>(*device(joynum)).set_joystick_map(map);
+ return true;
+}
+
+
+//**************************************************************************
+// INPUT DEVICE ITEM
+//**************************************************************************
+
+//-------------------------------------------------
+// input_device_item - constructor
+//-------------------------------------------------
+
+input_device_item::input_device_item(input_device &device, const char *name, void *internal, input_item_id itemid, item_get_state_func getstate, input_item_class itemclass)
+ : m_device(device),
+ m_name(name),
+ m_internal(internal),
+ m_itemid(itemid),
+ m_itemclass(itemclass),
+ m_getstate(getstate),
+ m_current(0),
+ m_memory(0)
+{
+ // use a standard token name for know item IDs
+ const char *standard_token = manager().standard_token(itemid);
+ if (standard_token != nullptr)
+ m_token.assign(standard_token);
+
+ // otherwise, create a tokenized name
+ else {
+ m_token.assign(name);
+ strmakeupper(m_token);
+ strdelchr(m_token, ' ');
+ strdelchr(m_token, '_');
+ }
+}
+
+
+//-------------------------------------------------
+// input_device_item - destructor
+//-------------------------------------------------
+
+input_device_item::~input_device_item()
+{
+}
+
+
+
+//**************************************************************************
+// INPUT DEVICE SWITCH ITEM
+//**************************************************************************
+
+//-------------------------------------------------
+// input_device_switch_item - constructor
+//-------------------------------------------------
+
+input_device_switch_item::input_device_switch_item(input_device &device, const char *name, void *internal, input_item_id itemid, item_get_state_func getstate)
+ : input_device_item(device, name, internal, itemid, getstate, ITEM_CLASS_SWITCH),
+ m_steadykey(0),
+ m_oldkey(0)
+{
+}
+
+
+//-------------------------------------------------
+// read_as_switch - return the raw switch value,
+// modified as necessary
+//-------------------------------------------------
+
+int32_t input_device_switch_item::read_as_switch(input_item_modifier modifier)
+{
+ // if we're doing a lightgun reload hack, button 1 and 2 operate differently
+ input_device_class devclass = m_device.devclass();
+ if (devclass == DEVICE_CLASS_LIGHTGUN && m_device.lightgun_reload_button())
+ {
+ // button 1 is pressed if either button 1 or 2 are active
+ if (m_itemid == ITEM_ID_BUTTON1)
+ {
+ input_device_item *button2_item = m_device.item(ITEM_ID_BUTTON2);
+ if (button2_item != nullptr)
+ return button2_item->update_value() | update_value();
+ }
+
+ // button 2 is never officially pressed
+ if (m_itemid == ITEM_ID_BUTTON2)
+ return 0;
+ }
+
+ // steadykey for keyboards
+ if (devclass == DEVICE_CLASS_KEYBOARD && m_device.steadykey_enabled())
+ return m_steadykey;
+
+ // everything else is just the current value as-is
+ return update_value();
+}
+
+
+//-------------------------------------------------
+// read_as_relative - return the switch input as
+// a relative axis value
+//-------------------------------------------------
+
+int32_t input_device_switch_item::read_as_relative(input_item_modifier modifier)
+{
+ // no translation to relative
+ return 0;
+}
+
+
+//-------------------------------------------------
+// read_as_absolute - return the switch input as
+// an absolute axis value
+//-------------------------------------------------
+
+int32_t input_device_switch_item::read_as_absolute(input_item_modifier modifier)
+{
+ // no translation to absolute
+ return 0;
+}
+
+
+//-------------------------------------------------
+// steadykey_changed - update for steadykey
+// behavior, returning true if the current state
+// has changed since the last call
+//-------------------------------------------------
+
+bool input_device_switch_item::steadykey_changed()
+{
+ int32_t old = m_oldkey;
+ m_oldkey = update_value();
+ if (((m_current ^ old) & 1) == 0)
+ return false;
+
+ // if the keypress was missed, turn it on for one frame
+ if (((m_current | m_steadykey) & 1) == 0)
+ m_steadykey = 1;
+ return true;
+}
+
+
+
+//**************************************************************************
+// INPUT DEVICE RELATIVE ITEM
+//**************************************************************************
+
+//-------------------------------------------------
+// input_device_relative_item - constructor
+//-------------------------------------------------
+
+input_device_relative_item::input_device_relative_item(input_device &device, const char *name, void *internal, input_item_id itemid, item_get_state_func getstate)
+ : input_device_item(device, name, internal, itemid, getstate, ITEM_CLASS_RELATIVE)
+{
+}
+
+
+//-------------------------------------------------
+// read_as_switch - return the relative value as
+// a switch result based on the modifier
+//-------------------------------------------------
+
+int32_t input_device_relative_item::read_as_switch(input_item_modifier modifier)
+{
+ // process according to modifiers
+ if (modifier == ITEM_MODIFIER_POS || modifier == ITEM_MODIFIER_RIGHT || modifier == ITEM_MODIFIER_DOWN)
+ return (update_value() > 0);
+ else if (modifier == ITEM_MODIFIER_NEG || modifier == ITEM_MODIFIER_LEFT || modifier == ITEM_MODIFIER_UP)
+ return (update_value() < 0);
+
+ // all other cases just return 0
+ return 0;
+}
+
+
+//-------------------------------------------------
+// read_as_relative - return the relative input
+// as a relative axis value
+//-------------------------------------------------
+
+int32_t input_device_relative_item::read_as_relative(input_item_modifier modifier)
+{
+ // just return directly
+ return update_value();
+}
+
+
+//-------------------------------------------------
+// read_as_absolute - return the relative input
+// as an absolute axis value
+//-------------------------------------------------
+
+int32_t input_device_relative_item::read_as_absolute(input_item_modifier modifier)
+{
+ // no translation to absolute
+ return 0;
+}
+
+
+
+//**************************************************************************
+// INPUT DEVICE ABSOLUTE ITEM
+//**************************************************************************
+
+//-------------------------------------------------
+// input_device_absolute_item - constructor
+//-------------------------------------------------
+
+input_device_absolute_item::input_device_absolute_item(input_device &device, const char *name, void *internal, input_item_id itemid, item_get_state_func getstate)
+ : input_device_item(device, name, internal, itemid, getstate, ITEM_CLASS_ABSOLUTE)
+{
+}
+
+
+//-------------------------------------------------
+// read_as_switch - return the absolute value as
+// a switch result based on the modifier
+//-------------------------------------------------
+
+int32_t input_device_absolute_item::read_as_switch(input_item_modifier modifier)
+{
+ // start with the current value
+ int32_t result = m_device.adjust_absolute(update_value());
+ assert(result >= INPUT_ABSOLUTE_MIN && result <= INPUT_ABSOLUTE_MAX);
+
+ // left/right/up/down: if this is a joystick, fetch the paired X/Y axis values and convert
+ if (m_device.devclass() == DEVICE_CLASS_JOYSTICK && modifier >= ITEM_MODIFIER_LEFT && modifier <= ITEM_MODIFIER_DOWN)
+ {
+ input_device_item *xaxis_item = m_device.item(ITEM_ID_XAXIS);
+ input_device_item *yaxis_item = m_device.item(ITEM_ID_YAXIS);
+ if (xaxis_item != nullptr && yaxis_item != nullptr)
+ {
+ // determine which item we didn't update, and update it
+ assert(this == xaxis_item || this == yaxis_item);
+ if (this == xaxis_item)
+ yaxis_item->update_value();
+ else
+ xaxis_item->update_value();
+
+ // now map the X and Y axes to a 9x9 grid using the raw values
+ joystick_map &joymap = downcast<input_device_joystick &>(m_device).joymap();
+ return (joymap.update(xaxis_item->current(), yaxis_item->current()) >> (modifier - ITEM_MODIFIER_LEFT)) & 1;
+ }
+ }
+
+ // positive/negative: true if past the deadzone in either direction
+ if (modifier == ITEM_MODIFIER_POS || modifier == ITEM_MODIFIER_RIGHT || modifier == ITEM_MODIFIER_DOWN)
+ return (result > 0);
+ else if (modifier == ITEM_MODIFIER_NEG || modifier == ITEM_MODIFIER_LEFT || modifier == ITEM_MODIFIER_UP)
+ return (result < 0);
+
+ // all other cases just return 0
+ return 0;
+}
+
+
+//-------------------------------------------------
+// read_as_relative - return the absolute input
+// as a relative axis value
+//-------------------------------------------------
+
+int32_t input_device_absolute_item::read_as_relative(input_item_modifier modifier)
+{
+ // no translation to relative
+ return 0;
+}
+
+
+//-------------------------------------------------
+// read_as_absolute - return the absolute input
+// as an absolute axis value, with appropriate
+// tweaks
+//-------------------------------------------------
+
+int32_t input_device_absolute_item::read_as_absolute(input_item_modifier modifier)
+{
+ // start with the current value
+ int32_t result = m_device.adjust_absolute(update_value());
+ assert(result >= INPUT_ABSOLUTE_MIN && result <= INPUT_ABSOLUTE_MAX);
+
+ // if we're doing a lightgun reload hack, override the value
+ if (m_device.devclass() == DEVICE_CLASS_LIGHTGUN && m_device.lightgun_reload_button())
+ {
+ // if it is pressed, return (min,max)
+ input_device_item *button2_item = m_device.item(ITEM_ID_BUTTON2);
+ if (button2_item != nullptr && button2_item->update_value())
+ result = (m_itemid == ITEM_ID_XAXIS) ? INPUT_ABSOLUTE_MIN : INPUT_ABSOLUTE_MAX;
+ }
+
+ // positive/negative: scale to full axis
+ if (modifier == ITEM_MODIFIER_POS)
+ result = std::max(result, 0) * 2 + INPUT_ABSOLUTE_MIN;
+ if (modifier == ITEM_MODIFIER_NEG)
+ result = std::max(-result, 0) * 2 + INPUT_ABSOLUTE_MIN;
+ return result;
+}
diff --git a/src/emu/inputdev.h b/src/emu/inputdev.h
new file mode 100644
index 00000000000..5b4adcd84f3
--- /dev/null
+++ b/src/emu/inputdev.h
@@ -0,0 +1,400 @@
+// license:BSD-3-Clause
+// copyright-holders:Aaron Giles
+/***************************************************************************
+
+ inputdev.h
+
+ Input devices, items and device classes.
+
+***************************************************************************/
+
+#pragma once
+
+#ifndef EMU_INPUTDEV_H
+#define EMU_INPUTDEV_H
+
+
+//**************************************************************************
+// TYPE DEFINITIONS
+//**************************************************************************
+
+// forward declarations
+class input_device;
+class input_class;
+class input_manager;
+
+
+// callback for getting the value of an item on a device
+typedef int32_t (*item_get_state_func)(void *device_internal, void *item_internal);
+
+// ======================> joystick_map
+
+// a 9x9 joystick map
+class joystick_map
+{
+public:
+ // construction/destruction
+ joystick_map();
+ joystick_map(const joystick_map &src) { copy(src); }
+
+ // operators
+ joystick_map &operator=(const joystick_map &src) { if (this != &src) copy(src); return *this; }
+
+ // parse from a string
+ bool parse(const char *mapstring);
+
+ // create a friendly string
+ std::string to_string() const;
+
+ // update the state of a live map
+ uint8_t update(int32_t xaxisval, int32_t yaxisval);
+
+ // joystick mapping codes
+ static const uint8_t JOYSTICK_MAP_NEUTRAL = 0x00;
+ static const uint8_t JOYSTICK_MAP_LEFT = 0x01;
+ static const uint8_t JOYSTICK_MAP_RIGHT = 0x02;
+ static const uint8_t JOYSTICK_MAP_UP = 0x04;
+ static const uint8_t JOYSTICK_MAP_DOWN = 0x08;
+ static const uint8_t JOYSTICK_MAP_STICKY = 0x0f;
+
+private:
+ // internal helpers
+ void copy(const joystick_map &src)
+ {
+ memcpy(m_map, src.m_map, sizeof(m_map));
+ m_lastmap = JOYSTICK_MAP_NEUTRAL;
+ m_origstring = src.m_origstring;
+ }
+
+ // internal state
+ uint8_t m_map[9][9]; // 9x9 grid
+ uint8_t m_lastmap; // last value returned (for sticky tracking)
+ std::string m_origstring; // originally parsed string
+};
+
+
+// ======================> input_device_item
+
+// a single item on an input device
+class input_device_item
+{
+protected:
+ // construction/destruction
+ input_device_item(input_device &device, const char *name, void *internal, input_item_id itemid, item_get_state_func getstate, input_item_class itemclass);
+
+public:
+ virtual ~input_device_item();
+
+ // getters
+ input_device &device() const { return m_device; }
+ input_manager &manager() const;
+ running_machine &machine() const;
+ const char *name() const { return m_name.c_str(); }
+ void *internal() const { return m_internal; }
+ input_item_id itemid() const { return m_itemid; }
+ input_item_class itemclass() const { return m_itemclass; }
+ input_code code() const;
+ const char *token() const { return m_token.c_str(); }
+ int32_t current() const { return m_current; }
+ int32_t memory() const { return m_memory; }
+
+ // helpers
+ int32_t update_value();
+ void set_memory(int32_t value) { m_memory = value; }
+
+ // readers
+ virtual int32_t read_as_switch(input_item_modifier modifier) = 0;
+ virtual int32_t read_as_relative(input_item_modifier modifier) = 0;
+ virtual int32_t read_as_absolute(input_item_modifier modifier) = 0;
+
+protected:
+ // internal state
+ input_device & m_device; // reference to our owning device
+ std::string m_name; // string name of item
+ void * m_internal; // internal callback pointer
+ input_item_id m_itemid; // originally specified item id
+ input_item_class m_itemclass; // class of the item
+ item_get_state_func m_getstate; // get state callback
+ std::string m_token; // tokenized name for non-standard items
+
+ // live state
+ int32_t m_current; // current raw value
+ int32_t m_memory; // "memory" value, to remember where we started during polling
+};
+
+
+// ======================> input_device
+
+// a logical device of a given class that can provide input
+class input_device
+{
+ friend class input_class;
+
+public:
+ // construction/destruction
+ input_device(input_manager &manager, const char *_name, const char *_id, void *_internal);
+ virtual ~input_device();
+
+ // getters
+ input_manager &manager() const { return m_manager; }
+ running_machine &machine() const { return m_manager.machine(); }
+ input_device_class devclass() const { return device_class(); }
+ const char *name() const { return m_name.c_str(); }
+ const char *id() const { return m_id.c_str(); }
+ int devindex() const { return m_devindex; }
+ input_device_item *item(input_item_id index) const { return m_item[index].get(); }
+ input_item_id maxitem() const { return m_maxitem; }
+ void *internal() const { return m_internal; }
+ bool steadykey_enabled() const { return m_steadykey_enabled; }
+ bool lightgun_reload_button() const { return m_lightgun_reload_button; }
+
+ // setters
+ void set_devindex(int devindex) { m_devindex = devindex; }
+
+ // item management
+ input_item_id add_item(const char *name, input_item_id itemid, item_get_state_func getstate, void *internal = nullptr);
+
+ // helpers
+ int32_t adjust_absolute(int32_t value) const { return adjust_absolute_value(value); }
+ bool match_device_id(const char *deviceid);
+
+protected:
+ // specific overrides
+ virtual input_device_class device_class() const = 0;
+ virtual int32_t adjust_absolute_value(int32_t value) const { return value; }
+
+private:
+ // internal state
+ input_manager & m_manager; // reference to input manager
+ std::string m_name; // string name of device
+ std::string m_id; // id of device
+ int m_devindex; // device index of this device
+ std::unique_ptr<input_device_item> m_item[ITEM_ID_ABSOLUTE_MAXIMUM+1]; // array of pointers to items
+ input_item_id m_maxitem; // maximum item index
+ void * m_internal; // internal callback pointer
+
+ bool m_steadykey_enabled; // steadykey enabled for keyboards
+ bool m_lightgun_reload_button; // lightgun reload hack
+};
+
+
+// ======================> input_device_keyboard
+
+class input_device_keyboard : public input_device
+{
+public:
+ // construction/destruction
+ input_device_keyboard(input_manager &manager, const char *_name, const char *_id, void *_internal);
+
+ // helpers
+ void apply_steadykey() const;
+
+protected:
+ // specific overrides
+ virtual input_device_class device_class() const override { return DEVICE_CLASS_KEYBOARD; }
+};
+
+
+// ======================> input_device_mouse
+
+class input_device_mouse : public input_device
+{
+public:
+ // construction/destruction
+ input_device_mouse(input_manager &manager, const char *_name, const char *_id, void *_internal);
+
+protected:
+ // specific overrides
+ virtual input_device_class device_class() const override { return DEVICE_CLASS_MOUSE; }
+};
+
+
+// ======================> input_device_lightgun
+
+class input_device_lightgun : public input_device
+{
+public:
+ // construction/destruction
+ input_device_lightgun(input_manager &manager, const char *_name, const char *_id, void *_internal);
+
+protected:
+ // specific overrides
+ virtual input_device_class device_class() const override { return DEVICE_CLASS_LIGHTGUN; }
+};
+
+
+// ======================> input_device_joystick
+
+// a logical device of a given class that can provide input
+class input_device_joystick : public input_device
+{
+public:
+ // construction/destruction
+ input_device_joystick(input_manager &manager, const char *_name, const char *_id, void *_internal);
+
+ // getters
+ joystick_map &joymap() { return m_joymap; }
+
+ // item management
+ void set_joystick_map(const joystick_map &map) { m_joymap = map; }
+
+protected:
+ // specific overrides
+ virtual input_device_class device_class() const override { return DEVICE_CLASS_JOYSTICK; }
+ virtual int32_t adjust_absolute_value(int32_t value) const override;
+
+private:
+ // joystick information
+ joystick_map m_joymap; // joystick map for this device
+ int32_t m_joystick_deadzone; // deadzone for joystick
+ int32_t m_joystick_saturation; // saturation position for joystick
+};
+
+
+// ======================> input_class
+
+// a class of device that provides input
+class input_class
+{
+public:
+ // construction/destruction
+ input_class(input_manager &manager, input_device_class devclass, const char *name, bool enabled = false, bool multi = false);
+ virtual ~input_class();
+
+ // getters
+ input_manager &manager() const { return m_manager; }
+ running_machine &machine() const { return m_manager.machine(); }
+ input_device *device(int index) const { return (index <= m_maxindex) ? m_device[index].get() : nullptr; }
+ input_device_class devclass() const { return m_devclass; }
+ const char *name() const { return m_name; }
+ int maxindex() const { return m_maxindex; }
+ bool enabled() const { return m_enabled; }
+ bool multi() const { return m_multi; }
+
+ // setters
+ void enable(bool state = true) { m_enabled = state; }
+ void set_multi(bool multi = true) { m_multi = multi; }
+
+ // device management
+ input_device *add_device(const char *name, const char *id, void *internal = nullptr);
+ input_device *add_device(std::unique_ptr<input_device> &&new_device);
+
+ // misc helpers
+ input_item_class standard_item_class(input_item_id itemid) const;
+ void remap_device_index(int oldindex, int newindex);
+
+protected:
+ // specific overrides
+ virtual std::unique_ptr<input_device> make_device(const char *name, const char *id, void *internal) = 0;
+
+private:
+ // indexing helpers
+ int newindex(input_device &device);
+
+ // internal state
+ input_manager & m_manager; // reference to our manager
+ std::unique_ptr<input_device> m_device[DEVICE_INDEX_MAXIMUM]; // array of devices in this class
+ input_device_class m_devclass; // our device class
+ const char * m_name; // name of class (used for option settings)
+ int m_maxindex; // maximum populated index
+ bool m_enabled; // is this class enabled?
+ bool m_multi; // are multiple instances of this class allowed?
+};
+
+
+// ======================> input_class_keyboard
+
+// class of device that provides keyboard input
+class input_class_keyboard : public input_class
+{
+public:
+ // construction/destruction
+ input_class_keyboard(input_manager &manager);
+
+protected:
+ // specific overrides
+ virtual std::unique_ptr<input_device> make_device(const char *name, const char *id, void *internal) override
+ {
+ return std::make_unique<input_device_keyboard>(manager(), name, id, internal);
+ }
+
+private:
+ // internal helpers
+ void frame_callback();
+};
+
+
+// ======================> input_class_mouse
+
+// class of device that provides mouse input
+class input_class_mouse : public input_class
+{
+public:
+ // construction/destruction
+ input_class_mouse(input_manager &manager);
+
+protected:
+ // specific overrides
+ virtual std::unique_ptr<input_device> make_device(const char *name, const char *id, void *internal) override
+ {
+ return std::make_unique<input_device_mouse>(manager(), name, id, internal);
+ }
+};
+
+
+// ======================> input_class_lightgun
+
+// class of device that provides lightgun input
+class input_class_lightgun : public input_class
+{
+public:
+ // construction/destruction
+ input_class_lightgun(input_manager &manager);
+
+protected:
+ // specific overrides
+ virtual std::unique_ptr<input_device> make_device(const char *name, const char *id, void *internal) override
+ {
+ return std::make_unique<input_device_lightgun>(manager(), name, id, internal);
+ }
+};
+
+
+// ======================> input_class_joystick
+
+// class of device that provides joystick input
+class input_class_joystick : public input_class
+{
+public:
+ // construction/destruction
+ input_class_joystick(input_manager &manager);
+
+ // misc
+ bool set_global_joystick_map(const char *mapstring);
+
+ // standard joystick maps
+ static const char map_8way[];
+ static const char map_4way_diagonal[];
+
+protected:
+ // specific overrides
+ virtual std::unique_ptr<input_device> make_device(const char *name, const char *id, void *internal) override
+ {
+ return std::make_unique<input_device_joystick>(manager(), name, id, internal);
+ }
+};
+
+
+//**************************************************************************
+// INLINE FUNCTIONS
+//**************************************************************************
+
+// input_device_item helpers
+inline input_manager &input_device_item::manager() const { return m_device.manager(); }
+inline running_machine &input_device_item::machine() const { return m_device.machine(); }
+inline input_code input_device_item::code() const { return input_code(m_device.devclass(), m_device.devindex(), m_itemclass, ITEM_MODIFIER_NONE, m_itemid); }
+inline int32_t input_device_item::update_value() { return m_current = (*m_getstate)(m_device.internal(), m_internal); }
+
+
+
+#endif // EMU_INPUTDEV_H
diff --git a/src/emu/ioport.cpp b/src/emu/ioport.cpp
index 84710e23453..aca8b006e5d 100644
--- a/src/emu/ioport.cpp
+++ b/src/emu/ioport.cpp
@@ -96,6 +96,7 @@
#include "xmlfile.h"
#include "profiler.h"
#include "ui/uimain.h"
+#include "inputdev.h"
#include "natkeyboard.h"
#include "osdepend.h"
@@ -1737,7 +1738,8 @@ time_t ioport_manager::initialize()
for (ioport_field &field : port.second->fields())
if (field.live().joystick != nullptr && field.rotated())
{
- machine().input().set_global_joystick_map(joystick_map_4way_diagonal);
+ input_class_joystick &devclass = downcast<input_class_joystick &>(machine().input().device_class(DEVICE_CLASS_JOYSTICK));
+ devclass.set_global_joystick_map(input_class_joystick::map_4way_diagonal);
break;
}
@@ -1790,45 +1792,33 @@ void ioport_manager::init_autoselect_devices(int type1, int type2, int type3, co
{
// if nothing specified, ignore the option
const char *stemp = machine().options().value(option);
- if (stemp[0] == 0)
+ if (stemp[0] == 0 || strcmp(stemp, "none") == 0)
return;
// extract valid strings
- const char *autostring = "keyboard";
- input_device_class autoenable = DEVICE_CLASS_KEYBOARD;
- if (strcmp(stemp, "mouse") == 0)
- {
- autoenable = DEVICE_CLASS_MOUSE;
- autostring = "mouse";
- }
- else if (strcmp(stemp, "joystick") == 0)
- {
- autoenable = DEVICE_CLASS_JOYSTICK;
- autostring = "joystick";
- }
- else if (strcmp(stemp, "lightgun") == 0)
- {
- autoenable = DEVICE_CLASS_LIGHTGUN;
- autostring = "lightgun";
- }
- else if (strcmp(stemp, "none") == 0)
+ input_class *autoenable_class = nullptr;
+ for (input_device_class devclass = DEVICE_CLASS_FIRST_VALID; devclass <= DEVICE_CLASS_LAST_VALID; ++devclass)
+ if (strcmp(stemp, machine().input().device_class(devclass).name()) == 0)
+ {
+ autoenable_class = &machine().input().device_class(devclass);
+ break;
+ }
+ if (autoenable_class == nullptr)
{
- // nothing specified
- return;
- }
- else if (strcmp(stemp, "keyboard") != 0)
osd_printf_error("Invalid %s value %s; reverting to keyboard\n", option, stemp);
+ autoenable_class = &machine().input().device_class(DEVICE_CLASS_KEYBOARD);
+ }
// only scan the list if we haven't already enabled this class of control
- if (!m_portlist.empty() && !machine().input().device_class(autoenable).enabled())
+ if (!autoenable_class->enabled())
for (auto &port : m_portlist)
for (ioport_field &field : port.second->fields())
// if this port type is in use, apply the autoselect criteria
if ((type1 != 0 && field.type() == type1) || (type2 != 0 && field.type() == type2) || (type3 != 0 && field.type() == type3))
{
- osd_printf_verbose("Input: Autoenabling %s due to presence of a %s\n", autostring, ananame);
- machine().input().device_class(autoenable).enable();
+ osd_printf_verbose("Input: Autoenabling %s due to presence of a %s\n", autoenable_class->name(), ananame);
+ autoenable_class->enable();
break;
}
}
diff --git a/src/osd/modules/input/input_common.h b/src/osd/modules/input/input_common.h
index 1fc495e44ce..e5250f4786f 100644
--- a/src/osd/modules/input/input_common.h
+++ b/src/osd/modules/input/input_common.h
@@ -13,6 +13,8 @@
#include "input_module.h"
+#include "inputdev.h"
+
#include <chrono>
#include <memory>
#include <mutex>