// license:BSD-3-Clause
// copyright-holders:Aaron Giles
/***************************************************************************
inputdev.cpp
Input devices, items and device classes.
***************************************************************************/
#include "emu.h"
#include "inputdev.h"
#include "emuopts.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 s32 read_as_switch(input_item_modifier modifier) override;
virtual s32 read_as_relative(input_item_modifier modifier) override;
virtual s32 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
s32 m_steadykey; // the live steadykey state
s32 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 s32 read_as_switch(input_item_modifier modifier) override;
virtual s32 read_as_relative(input_item_modifier modifier) override;
virtual s32 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 s32 read_as_switch(input_item_modifier modifier) override;
virtual s32 read_as_relative(input_item_modifier modifier) override;
virtual s32 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 u8 *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++)
{
u8 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);
u8 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 u8 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
//-------------------------------------------------
u8 joystick_map::update(s32 xaxisval, s32 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);
u8 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)
{
if (machine().phase() != machine_phase::INIT)
throw emu_fatalerror("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(s32(manager.machine().options().joystick_deadzone() * INPUT_ABSOLUTE_MAX)),
m_joystick_saturation(s32(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());
}
//-------------------------------------------------
// adjust_absolute_value - apply joystick device
// deadzone and saturation parameters to an
// absolute value
//-------------------------------------------------
s32 input_device_joystick::adjust_absolute_value(s32 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 = s64(result - m_joystick_deadzone) * s64(INPUT_ABSOLUTE_MAX) / s64(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)
{
if (machine().phase() != machine_phase::INIT)
throw emu_fatalerror("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(&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());
// 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
//-------------------------------------------------
s32 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
//-------------------------------------------------
s32 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
//-------------------------------------------------
s32 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()
{
s32 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
//-------------------------------------------------
s32 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
//-------------------------------------------------
s32 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
//-------------------------------------------------
s32 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
//-------------------------------------------------
s32 input_device_absolute_item::read_as_switch(input_item_modifier modifier)
{
// start with the current value
s32 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
//-------------------------------------------------
s32 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
//-------------------------------------------------
s32 input_device_absolute_item::read_as_absolute(input_item_modifier modifier)
{
// start with the current value
s32 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;
}