diff options
-rw-r--r-- | scripts/src/emu.lua | 2 | ||||
-rw-r--r-- | src/emu/input.cpp | 921 | ||||
-rw-r--r-- | src/emu/input.h | 239 | ||||
-rw-r--r-- | src/emu/inputdev.cpp | 947 | ||||
-rw-r--r-- | src/emu/inputdev.h | 400 | ||||
-rw-r--r-- | src/emu/ioport.cpp | 44 | ||||
-rw-r--r-- | src/osd/modules/input/input_common.h | 2 |
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> |