summaryrefslogtreecommitdiffstatshomepage
path: root/src/emu/inputdev.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/emu/inputdev.cpp')
-rw-r--r--src/emu/inputdev.cpp947
1 files changed, 947 insertions, 0 deletions
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;
+}