summaryrefslogtreecommitdiffstatshomepage
path: root/trunk/src/emu/input.c
diff options
context:
space:
mode:
Diffstat (limited to 'trunk/src/emu/input.c')
-rw-r--r--trunk/src/emu/input.c2431
1 files changed, 2431 insertions, 0 deletions
diff --git a/trunk/src/emu/input.c b/trunk/src/emu/input.c
new file mode 100644
index 00000000000..3f6181d0125
--- /dev/null
+++ b/trunk/src/emu/input.c
@@ -0,0 +1,2431 @@
+/***************************************************************************
+
+ input.c
+
+ Handle input from the user.
+
+****************************************************************************
+
+ Copyright Aaron Giles
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are
+ met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+ * Neither the name 'MAME' nor the names of its contributors may be
+ used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY AARON GILES ''AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ DISCLAIMED. IN NO EVENT SHALL AARON GILES BE LIABLE FOR ANY DIRECT,
+ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+
+****************************************************************************
+
+ To do:
+ * auto-selecting joystick configs
+ * per-joystick configs?
+ * test half-axis selections
+ * add input test menu
+ * get rid of osd_customize_inputport_list
+
+***************************************************************************/
+
+#include "emu.h"
+#include "emuopts.h"
+
+
+
+//**************************************************************************
+// CONSTANTS
+//**************************************************************************
+
+// invalid memory value for axis polling
+const INT32 INVALID_AXIS_VALUE = 0x7fffffff;
+
+// additional expanded input codes for sequences
+const input_code input_seq::end_code(DEVICE_CLASS_INTERNAL, 0, ITEM_CLASS_INVALID, ITEM_MODIFIER_NONE, ITEM_ID_SEQ_END);
+const input_code input_seq::default_code(DEVICE_CLASS_INTERNAL, 0, ITEM_CLASS_INVALID, ITEM_MODIFIER_NONE, ITEM_ID_SEQ_DEFAULT);
+const input_code input_seq::not_code(DEVICE_CLASS_INTERNAL, 0, ITEM_CLASS_INVALID, ITEM_MODIFIER_NONE, ITEM_ID_SEQ_NOT);
+const input_code input_seq::or_code(DEVICE_CLASS_INTERNAL, 0, ITEM_CLASS_INVALID, ITEM_MODIFIER_NONE, ITEM_ID_SEQ_OR);
+
+// constant sequences
+const input_seq input_seq::empty_seq;
+
+
+
+//**************************************************************************
+// TYPE DEFINITIONS
+//**************************************************************************
+
+// ======================> 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);
+ virtual ~input_device_item() { }
+
+public:
+ // getters
+ input_device &device() const { return m_device; }
+ input_manager &manager() const { return m_device.manager(); }
+ running_machine &machine() const { return m_device.machine(); }
+ const char *name() const { return m_name; }
+ 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; }
+ INT32 current() const { return m_current; }
+ INT32 memory() const { return m_memory; }
+
+ // helpers
+ INT32 update_value() { return m_current = (*m_getstate)(m_device.internal(), m_internal); }
+ void set_memory(INT32 value) { m_memory = value; }
+
+ // readers
+ virtual INT32 read_as_switch(input_item_modifier modifier) = 0;
+ virtual INT32 read_as_relative(input_item_modifier modifier) = 0;
+ virtual INT32 read_as_absolute(input_item_modifier modifier) = 0;
+
+protected:
+ // internal state
+ input_device & m_device; // reference to our owning device
+ astring 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
+ astring m_token; // tokenized name for non-standard items
+
+ // live state
+ INT32 m_current; // current raw value
+ INT32 m_memory; // "memory" value, to remember where we started during polling
+};
+
+
+// ======================> 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 read_as_switch(input_item_modifier modifier);
+ virtual INT32 read_as_relative(input_item_modifier modifier);
+ virtual INT32 read_as_absolute(input_item_modifier modifier);
+
+ // steadykey helper
+ bool steadykey_changed();
+ void steadykey_update_to_current() { m_steadykey = m_current; }
+
+private:
+ // internal state
+ INT32 m_steadykey; // the live steadykey state
+ INT32 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 read_as_switch(input_item_modifier modifier);
+ virtual INT32 read_as_relative(input_item_modifier modifier);
+ virtual INT32 read_as_absolute(input_item_modifier modifier);
+};
+
+
+// ======================> 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 read_as_switch(input_item_modifier modifier);
+ virtual INT32 read_as_relative(input_item_modifier modifier);
+ virtual INT32 read_as_absolute(input_item_modifier modifier);
+};
+
+
+// ======================> code_string_table
+
+// simple class to match codes to strings
+struct code_string_table
+{
+ UINT32 operator[](const char *string) const
+ {
+ for (const code_string_table *current = this; current->m_code != ~0; current++)
+ if (strcmp(current->m_string, string) == 0)
+ return current->m_code;
+ return ~0;
+ }
+
+ const char *operator[](UINT32 code) const
+ {
+ for (const code_string_table *current = this; current->m_code != ~0; current++)
+ if (current->m_code == code)
+ return current->m_string;
+ return NULL;
+ }
+
+ UINT32 m_code;
+ const char * m_string;
+};
+
+
+
+//**************************************************************************
+// TOKEN/STRING TABLES
+//**************************************************************************
+
+// token strings for device classes
+static const code_string_table devclass_token_table[] =
+{
+ { DEVICE_CLASS_KEYBOARD, "KEYCODE" },
+ { DEVICE_CLASS_MOUSE, "MOUSECODE" },
+ { DEVICE_CLASS_LIGHTGUN, "GUNCODE" },
+ { DEVICE_CLASS_JOYSTICK, "JOYCODE" },
+ { ~0, "UNKCODE" }
+};
+
+// friendly strings for device classes
+static const code_string_table devclass_string_table[] =
+{
+ { DEVICE_CLASS_KEYBOARD, "Kbd" },
+ { DEVICE_CLASS_MOUSE, "Mouse" },
+ { DEVICE_CLASS_LIGHTGUN, "Gun" },
+ { DEVICE_CLASS_JOYSTICK, "Joy" },
+ { ~0, "Unk" }
+};
+
+// token strings for item modifiers
+static const code_string_table modifier_token_table[] =
+{
+ { ITEM_MODIFIER_POS, "POS" },
+ { ITEM_MODIFIER_NEG, "NEG" },
+ { ITEM_MODIFIER_LEFT, "LEFT" },
+ { ITEM_MODIFIER_RIGHT, "RIGHT" },
+ { ITEM_MODIFIER_UP, "UP" },
+ { ITEM_MODIFIER_DOWN, "DOWN" },
+ { ~0, "" }
+};
+
+// friendly strings for item modifiers
+static const code_string_table modifier_string_table[] =
+{
+ { ITEM_MODIFIER_POS, "+" },
+ { ITEM_MODIFIER_NEG, "-" },
+ { ITEM_MODIFIER_LEFT, "Left" },
+ { ITEM_MODIFIER_RIGHT, "Right" },
+ { ITEM_MODIFIER_UP, "Up" },
+ { ITEM_MODIFIER_DOWN, "Down" },
+ { ~0, "" }
+};
+
+// token strings for item classes
+static const code_string_table itemclass_token_table[] =
+{
+ { ITEM_CLASS_SWITCH, "SWITCH" },
+ { ITEM_CLASS_ABSOLUTE, "ABSOLUTE" },
+ { ITEM_CLASS_RELATIVE, "RELATIVE" },
+ { ~0, "" }
+};
+
+// token strings for standard item ids
+static const code_string_table itemid_token_table[] =
+{
+ // standard keyboard codes
+ { ITEM_ID_A, "A" },
+ { ITEM_ID_B, "B" },
+ { ITEM_ID_C, "C" },
+ { ITEM_ID_D, "D" },
+ { ITEM_ID_E, "E" },
+ { ITEM_ID_F, "F" },
+ { ITEM_ID_G, "G" },
+ { ITEM_ID_H, "H" },
+ { ITEM_ID_I, "I" },
+ { ITEM_ID_J, "J" },
+ { ITEM_ID_K, "K" },
+ { ITEM_ID_L, "L" },
+ { ITEM_ID_M, "M" },
+ { ITEM_ID_N, "N" },
+ { ITEM_ID_O, "O" },
+ { ITEM_ID_P, "P" },
+ { ITEM_ID_Q, "Q" },
+ { ITEM_ID_R, "R" },
+ { ITEM_ID_S, "S" },
+ { ITEM_ID_T, "T" },
+ { ITEM_ID_U, "U" },
+ { ITEM_ID_V, "V" },
+ { ITEM_ID_W, "W" },
+ { ITEM_ID_X, "X" },
+ { ITEM_ID_Y, "Y" },
+ { ITEM_ID_Z, "Z" },
+ { ITEM_ID_0, "0" },
+ { ITEM_ID_1, "1" },
+ { ITEM_ID_2, "2" },
+ { ITEM_ID_3, "3" },
+ { ITEM_ID_4, "4" },
+ { ITEM_ID_5, "5" },
+ { ITEM_ID_6, "6" },
+ { ITEM_ID_7, "7" },
+ { ITEM_ID_8, "8" },
+ { ITEM_ID_9, "9" },
+ { ITEM_ID_F1, "F1" },
+ { ITEM_ID_F2, "F2" },
+ { ITEM_ID_F3, "F3" },
+ { ITEM_ID_F4, "F4" },
+ { ITEM_ID_F5, "F5" },
+ { ITEM_ID_F6, "F6" },
+ { ITEM_ID_F7, "F7" },
+ { ITEM_ID_F8, "F8" },
+ { ITEM_ID_F9, "F9" },
+ { ITEM_ID_F10, "F10" },
+ { ITEM_ID_F11, "F11" },
+ { ITEM_ID_F12, "F12" },
+ { ITEM_ID_F13, "F13" },
+ { ITEM_ID_F14, "F14" },
+ { ITEM_ID_F15, "F15" },
+ { ITEM_ID_ESC, "ESC" },
+ { ITEM_ID_TILDE, "TILDE" },
+ { ITEM_ID_MINUS, "MINUS" },
+ { ITEM_ID_EQUALS, "EQUALS" },
+ { ITEM_ID_BACKSPACE, "BACKSPACE" },
+ { ITEM_ID_TAB, "TAB" },
+ { ITEM_ID_OPENBRACE, "OPENBRACE" },
+ { ITEM_ID_CLOSEBRACE, "CLOSEBRACE" },
+ { ITEM_ID_ENTER, "ENTER" },
+ { ITEM_ID_COLON, "COLON" },
+ { ITEM_ID_QUOTE, "QUOTE" },
+ { ITEM_ID_BACKSLASH, "BACKSLASH" },
+ { ITEM_ID_BACKSLASH2, "BACKSLASH2" },
+ { ITEM_ID_COMMA, "COMMA" },
+ { ITEM_ID_STOP, "STOP" },
+ { ITEM_ID_SLASH, "SLASH" },
+ { ITEM_ID_SPACE, "SPACE" },
+ { ITEM_ID_INSERT, "INSERT" },
+ { ITEM_ID_DEL, "DEL" },
+ { ITEM_ID_HOME, "HOME" },
+ { ITEM_ID_END, "END" },
+ { ITEM_ID_PGUP, "PGUP" },
+ { ITEM_ID_PGDN, "PGDN" },
+ { ITEM_ID_LEFT, "LEFT" },
+ { ITEM_ID_RIGHT, "RIGHT" },
+ { ITEM_ID_UP, "UP" },
+ { ITEM_ID_DOWN, "DOWN" },
+ { ITEM_ID_0_PAD, "0PAD" },
+ { ITEM_ID_1_PAD, "1PAD" },
+ { ITEM_ID_2_PAD, "2PAD" },
+ { ITEM_ID_3_PAD, "3PAD" },
+ { ITEM_ID_4_PAD, "4PAD" },
+ { ITEM_ID_5_PAD, "5PAD" },
+ { ITEM_ID_6_PAD, "6PAD" },
+ { ITEM_ID_7_PAD, "7PAD" },
+ { ITEM_ID_8_PAD, "8PAD" },
+ { ITEM_ID_9_PAD, "9PAD" },
+ { ITEM_ID_SLASH_PAD, "SLASHPAD" },
+ { ITEM_ID_ASTERISK, "ASTERISK" },
+ { ITEM_ID_MINUS_PAD, "MINUSPAD" },
+ { ITEM_ID_PLUS_PAD, "PLUSPAD" },
+ { ITEM_ID_DEL_PAD, "DELPAD" },
+ { ITEM_ID_ENTER_PAD, "ENTERPAD" },
+ { ITEM_ID_PRTSCR, "PRTSCR" },
+ { ITEM_ID_PAUSE, "PAUSE" },
+ { ITEM_ID_LSHIFT, "LSHIFT" },
+ { ITEM_ID_RSHIFT, "RSHIFT" },
+ { ITEM_ID_LCONTROL, "LCONTROL" },
+ { ITEM_ID_RCONTROL, "RCONTROL" },
+ { ITEM_ID_LALT, "LALT" },
+ { ITEM_ID_RALT, "RALT" },
+ { ITEM_ID_SCRLOCK, "SCRLOCK" },
+ { ITEM_ID_NUMLOCK, "NUMLOCK" },
+ { ITEM_ID_CAPSLOCK, "CAPSLOCK" },
+ { ITEM_ID_LWIN, "LWIN" },
+ { ITEM_ID_RWIN, "RWIN" },
+ { ITEM_ID_MENU, "MENU" },
+ { ITEM_ID_CANCEL, "CANCEL" },
+
+ // standard mouse/joystick/gun codes
+ { ITEM_ID_XAXIS, "XAXIS" },
+ { ITEM_ID_YAXIS, "YAXIS" },
+ { ITEM_ID_ZAXIS, "ZAXIS" },
+ { ITEM_ID_RXAXIS, "RXAXIS" },
+ { ITEM_ID_RYAXIS, "RYAXIS" },
+ { ITEM_ID_RZAXIS, "RZAXIS" },
+ { ITEM_ID_SLIDER1, "SLIDER1" },
+ { ITEM_ID_SLIDER2, "SLIDER2" },
+ { ITEM_ID_BUTTON1, "BUTTON1" },
+ { ITEM_ID_BUTTON2, "BUTTON2" },
+ { ITEM_ID_BUTTON3, "BUTTON3" },
+ { ITEM_ID_BUTTON4, "BUTTON4" },
+ { ITEM_ID_BUTTON5, "BUTTON5" },
+ { ITEM_ID_BUTTON6, "BUTTON6" },
+ { ITEM_ID_BUTTON7, "BUTTON7" },
+ { ITEM_ID_BUTTON8, "BUTTON8" },
+ { ITEM_ID_BUTTON9, "BUTTON9" },
+ { ITEM_ID_BUTTON10, "BUTTON10" },
+ { ITEM_ID_BUTTON11, "BUTTON11" },
+ { ITEM_ID_BUTTON12, "BUTTON12" },
+ { ITEM_ID_BUTTON13, "BUTTON13" },
+ { ITEM_ID_BUTTON14, "BUTTON14" },
+ { ITEM_ID_BUTTON15, "BUTTON15" },
+ { ITEM_ID_BUTTON16, "BUTTON16" },
+ { ITEM_ID_START, "START" },
+ { ITEM_ID_SELECT, "SELECT" },
+
+ // Hats
+ { ITEM_ID_HAT1UP, "HAT1UP" },
+ { ITEM_ID_HAT1DOWN, "HAT1DOWN" },
+ { ITEM_ID_HAT1LEFT, "HAT1LEFT" },
+ { ITEM_ID_HAT1RIGHT, "HAT1RIGHT" },
+ { ITEM_ID_HAT2UP, "HAT2UP" },
+ { ITEM_ID_HAT2DOWN, "HAT2DOWN" },
+ { ITEM_ID_HAT2LEFT, "HAT2LEFT" },
+ { ITEM_ID_HAT2RIGHT, "HAT2RIGHT" },
+ { ITEM_ID_HAT3UP, "HAT3UP" },
+ { ITEM_ID_HAT3DOWN, "HAT3DOWN" },
+ { ITEM_ID_HAT3LEFT, "HAT3LEFT" },
+ { ITEM_ID_HAT3RIGHT, "HAT3RIGHT" },
+ { ITEM_ID_HAT4UP, "HAT4UP" },
+ { ITEM_ID_HAT4DOWN, "HAT4DOWN" },
+ { ITEM_ID_HAT4LEFT, "HAT4LEFT" },
+ { ITEM_ID_HAT4RIGHT, "HAT4RIGHT" },
+
+ // Additional IDs
+ { ITEM_ID_ADD_SWITCH1, "ADDSW1" },
+ { ITEM_ID_ADD_SWITCH2, "ADDSW2" },
+ { ITEM_ID_ADD_SWITCH3, "ADDSW3" },
+ { ITEM_ID_ADD_SWITCH4, "ADDSW4" },
+ { ITEM_ID_ADD_SWITCH5, "ADDSW5" },
+ { ITEM_ID_ADD_SWITCH6, "ADDSW6" },
+ { ITEM_ID_ADD_SWITCH7, "ADDSW7" },
+ { ITEM_ID_ADD_SWITCH8, "ADDSW8" },
+ { ITEM_ID_ADD_SWITCH9, "ADDSW9" },
+ { ITEM_ID_ADD_SWITCH10, "ADDSW10" },
+ { ITEM_ID_ADD_SWITCH11, "ADDSW11" },
+ { ITEM_ID_ADD_SWITCH12, "ADDSW12" },
+ { ITEM_ID_ADD_SWITCH13, "ADDSW13" },
+ { ITEM_ID_ADD_SWITCH14, "ADDSW14" },
+ { ITEM_ID_ADD_SWITCH15, "ADDSW15" },
+ { ITEM_ID_ADD_SWITCH16, "ADDSW16" },
+ { ITEM_ID_ADD_ABSOLUTE1, "ADDAXIS1" },
+ { ITEM_ID_ADD_ABSOLUTE2, "ADDAXIS2" },
+ { ITEM_ID_ADD_ABSOLUTE3, "ADDAXIS3" },
+ { ITEM_ID_ADD_ABSOLUTE4, "ADDAXIS4" },
+ { ITEM_ID_ADD_ABSOLUTE5, "ADDAXIS5" },
+ { ITEM_ID_ADD_ABSOLUTE6, "ADDAXIS6" },
+ { ITEM_ID_ADD_ABSOLUTE7, "ADDAXIS7" },
+ { ITEM_ID_ADD_ABSOLUTE8, "ADDAXIS8" },
+ { ITEM_ID_ADD_ABSOLUTE9, "ADDAXIS9" },
+ { ITEM_ID_ADD_ABSOLUTE10,"ADDAXIS10" },
+ { ITEM_ID_ADD_ABSOLUTE11,"ADDAXIS11" },
+ { ITEM_ID_ADD_ABSOLUTE12,"ADDAXIS12" },
+ { ITEM_ID_ADD_ABSOLUTE13,"ADDAXIS13" },
+ { ITEM_ID_ADD_ABSOLUTE14,"ADDAXIS14" },
+ { ITEM_ID_ADD_ABSOLUTE15,"ADDAXIS15" },
+ { ITEM_ID_ADD_ABSOLUTE16,"ADDAXIS16" },
+ { ITEM_ID_ADD_RELATIVE1, "ADDREL1" },
+ { ITEM_ID_ADD_RELATIVE2, "ADDREL2" },
+ { ITEM_ID_ADD_RELATIVE3, "ADDREL3" },
+ { ITEM_ID_ADD_RELATIVE4, "ADDREL4" },
+ { ITEM_ID_ADD_RELATIVE5, "ADDREL5" },
+ { ITEM_ID_ADD_RELATIVE6, "ADDREL6" },
+ { ITEM_ID_ADD_RELATIVE7, "ADDREL7" },
+ { ITEM_ID_ADD_RELATIVE8, "ADDREL8" },
+ { ITEM_ID_ADD_RELATIVE9, "ADDREL9" },
+ { ITEM_ID_ADD_RELATIVE10,"ADDREL10" },
+ { ITEM_ID_ADD_RELATIVE11,"ADDREL11" },
+ { ITEM_ID_ADD_RELATIVE12,"ADDREL12" },
+ { ITEM_ID_ADD_RELATIVE13,"ADDREL13" },
+ { ITEM_ID_ADD_RELATIVE14,"ADDREL14" },
+ { ITEM_ID_ADD_RELATIVE15,"ADDREL15" },
+ { ITEM_ID_ADD_RELATIVE16,"ADDREL16" },
+
+ { ~0, NULL }
+};
+
+
+
+//**************************************************************************
+// GLOBAL VARIABLES
+//**************************************************************************
+
+// standard joystick mappings
+const char joystick_map_8way[] = "7778...4445";
+const char joystick_map_4way_sticky[] = "s8.4s8.44s8.4445";
+const char joystick_map_4way_diagonal[] = "4444s8888..444458888.444555888.ss5.222555666.222256666.2222s6666.2222s6666";
+
+
+
+//**************************************************************************
+// 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 *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 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 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 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 == NULL)
+ 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
+//-------------------------------------------------
+
+const char *joystick_map::to_string(astring &string) const
+{
+ string.printf("%s\n", m_origstring.cstr());
+ for (int rownum = 0; rownum < 9; rownum++)
+ {
+ string.catprintf(" ");
+ for (int colnum = 0; colnum < 9; colnum++)
+ switch (m_map[rownum][colnum])
+ {
+ case JOYSTICK_MAP_UP | JOYSTICK_MAP_LEFT: string.catprintf("7"); break;
+ case JOYSTICK_MAP_UP: string.catprintf("8"); break;
+ case JOYSTICK_MAP_UP | JOYSTICK_MAP_RIGHT: string.catprintf("9"); break;
+ case JOYSTICK_MAP_LEFT: string.catprintf("4"); break;
+ case JOYSTICK_MAP_NEUTRAL: string.catprintf("5"); break;
+ case JOYSTICK_MAP_RIGHT: string.catprintf("6"); break;
+ case JOYSTICK_MAP_DOWN | JOYSTICK_MAP_LEFT: string.catprintf("1"); break;
+ case JOYSTICK_MAP_DOWN: string.catprintf("2"); break;
+ case JOYSTICK_MAP_DOWN | JOYSTICK_MAP_RIGHT:string.catprintf("3"); break;
+ case JOYSTICK_MAP_STICKY: string.catprintf("s"); break;
+ default: string.catprintf("?"); break;
+ }
+
+ string.catprintf("\n");
+ }
+ return string;
+}
+
+
+//-------------------------------------------------
+// update - update the state of the joystick
+// map based on the given X/Y axis values
+//-------------------------------------------------
+
+UINT8 joystick_map::update(INT32 xaxisval, INT32 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 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 != NULL);
+ m_internal = ((device.devclass() & 0xf) << 28) | ((device.devindex() & 0xff) << 20) | ((item->itemclass() & 0xf) << 16) | (ITEM_MODIFIER_NONE << 12) | (item->itemid() & 0xfff);
+}
+
+
+
+//**************************************************************************
+// INPUT SEQ
+//**************************************************************************
+
+//-------------------------------------------------
+// operator+= - append a code to the end of an
+// input sequence
+//-------------------------------------------------
+
+input_seq &input_seq::operator+=(input_code code)
+{
+ // if not enough room, return FALSE
+ int curlength = length();
+ if (curlength < ARRAY_LENGTH(m_code) - 1)
+ {
+ m_code[curlength++] = code;
+ m_code[curlength] = end_code;
+ }
+ return *this;
+}
+
+
+//-------------------------------------------------
+// operator|= - append a code to a sequence; if
+// the sequence is non-empty, insert an OR
+// before the new code
+//-------------------------------------------------
+
+input_seq &input_seq::operator|=(input_code code)
+{
+ // overwrite end/default with the new code
+ if (m_code[0] == end_code || m_code[0] == default_code)
+ m_code[0] = code;
+
+ // otherwise, append an OR token and then the new code
+ else
+ {
+ *this += or_code;
+ *this += code;
+ }
+ return *this;
+}
+
+
+//-------------------------------------------------
+// length - return the length of the sequence
+//-------------------------------------------------
+
+int input_seq::length() const
+{
+ // find the end token; error if none found
+ for (int seqnum = 0; seqnum < ARRAY_LENGTH(m_code); seqnum++)
+ if (m_code[seqnum] == end_code)
+ return seqnum;
+ return ARRAY_LENGTH(m_code);
+}
+
+
+//-------------------------------------------------
+// is_valid - return true if a given sequence is
+// valid
+//-------------------------------------------------
+
+bool input_seq::is_valid() const
+{
+ // "default" can only be of length 1
+ if (m_code[0] == default_code)
+ return (length() == 1);
+
+ // scan the sequence for valid codes
+ input_item_class lastclass = ITEM_CLASS_INVALID;
+ input_code lastcode = INPUT_CODE_INVALID;
+ int positive_code_count = 0;
+ for (int seqnum = 0; seqnum < ARRAY_LENGTH(m_code); seqnum++)
+ {
+ // invalid codes are never permitted
+ input_code code = m_code[seqnum];
+ if (code == INPUT_CODE_INVALID)
+ return false;
+
+ // if we hit an OR or the end, validate the previous chunk
+ if (code == or_code || code == end_code)
+ {
+ // must be at least one positive code
+ if (positive_code_count == 0)
+ return false;
+
+ // last code must not have been an internal code
+ if (lastcode.internal())
+ return false;
+
+ // if this is the end, we're ok
+ if (code == end_code)
+ return true;
+
+ // reset the state for the next chunk
+ positive_code_count = 0;
+ lastclass = ITEM_CLASS_INVALID;
+ }
+
+ // if we hit a NOT, make sure we don't have a double
+ else if (code == not_code)
+ {
+ if (lastcode == not_code)
+ return false;
+ }
+
+ // anything else
+ else
+ {
+ // count positive codes
+ if (lastcode != not_code)
+ positive_code_count++;
+
+ // non-switch items can't have a NOT
+ input_item_class itemclass = code.item_class();
+ if (itemclass != ITEM_CLASS_SWITCH && lastcode == not_code)
+ return FALSE;
+
+ // absolute/relative items must all be the same class
+ if ((lastclass == ITEM_CLASS_ABSOLUTE && itemclass != ITEM_CLASS_ABSOLUTE) ||
+ (lastclass == ITEM_CLASS_RELATIVE && itemclass != ITEM_CLASS_RELATIVE))
+ return false;
+ }
+
+ // remember the last code
+ lastcode = code;
+ }
+
+ // if we got here, we were missing an END token; fail
+ return false;
+}
+
+
+//-------------------------------------------------
+// set - directly set up to the first 7 codes
+//-------------------------------------------------
+
+void input_seq::set(input_code code0, input_code code1, input_code code2, input_code code3, input_code code4, input_code code5, input_code code6)
+{
+ m_code[0] = code0;
+ m_code[1] = code1;
+ m_code[2] = code2;
+ m_code[3] = code3;
+ m_code[4] = code4;
+ m_code[5] = code5;
+ m_code[6] = code6;
+ for (int codenum = 7; codenum < ARRAY_LENGTH(m_code); codenum++)
+ m_code[codenum] = end_code;
+}
+
+
+//-------------------------------------------------
+// backspace - "backspace" over the last entry in
+// a sequence
+//-------------------------------------------------
+
+void input_seq::backspace()
+{
+ // if we have at least one entry, remove it
+ int curlength = length();
+ if (curlength > 0)
+ m_code[curlength - 1] = end_code;
+}
+
+
+//-------------------------------------------------
+// replace - replace all instances of oldcode
+// with newcode in a sequence
+//-------------------------------------------------
+
+void input_seq::replace(input_code oldcode, input_code newcode)
+{
+ for (int codenum = 0; codenum < ARRAY_LENGTH(m_code); codenum++)
+ if (m_code[codenum] == oldcode)
+ m_code[codenum] = newcode;
+}
+
+
+
+//**************************************************************************
+// INPUT DEVICE
+//**************************************************************************
+
+//-------------------------------------------------
+// input_device - constructor
+//-------------------------------------------------
+
+input_device::input_device(input_class &_class, int devindex, const char *name, void *internal)
+ : m_class(_class),
+ m_name(name),
+ m_devindex(devindex),
+ m_maxitem(input_item_id(0)),
+ m_internal(internal),
+ m_joystick_deadzone((INT32)(_class.manager().machine().options().joystick_deadzone() * INPUT_ABSOLUTE_MAX)),
+ m_joystick_saturation((INT32)(_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())
+{
+ // reset the items
+ memset(m_item, 0, sizeof(m_item));
+
+ // 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
+ astring tempstr;
+ if (!m_joymap.parse(mapstring))
+ {
+ mame_printf_error("Invalid joystick map: %s\n", mapstring);
+ m_joymap.parse(joystick_map_8way);
+ }
+ else if (mapstring != joystick_map_8way)
+ mame_printf_verbose("Input: Default joystick map = %s", m_joymap.to_string(tempstr));
+ }
+}
+
+
+//-------------------------------------------------
+// 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 != NULL);
+ assert(itemid > ITEM_ID_INVALID && itemid < ITEM_ID_MAXIMUM);
+ assert(getstate != NULL);
+
+ // 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] == NULL)
+ break;
+ assert(itemid <= ITEM_ID_ABSOLUTE_MAXIMUM);
+
+ // make sure we don't have any overlap
+ assert(m_item[itemid] == NULL);
+
+ // determine the class and create the appropriate item class
+ input_device_item *item = NULL;
+ switch (m_class.standard_item_class(originalid))
+ {
+ case ITEM_CLASS_SWITCH:
+ item = auto_alloc(machine(), input_device_switch_item(*this, name, internal, itemid, getstate));
+ break;
+
+ case ITEM_CLASS_RELATIVE:
+ item = auto_alloc(machine(), input_device_relative_item(*this, name, internal, itemid, getstate));
+ break;
+
+ case ITEM_CLASS_ABSOLUTE:
+ item = auto_alloc(machine(), input_device_absolute_item(*this, name, internal, itemid, getstate));
+ break;
+
+ default:
+ assert(false);
+ }
+
+ // assign the new slot and update the maximum
+ m_item[itemid] = item;
+ m_maxitem = MAX(m_maxitem, itemid);
+ return itemid;
+}
+
+
+//-------------------------------------------------
+// apply_deadzone_and_saturation - apply global
+// deadzone and saturation parameters to an
+// absolute value
+//-------------------------------------------------
+
+INT32 input_device::apply_deadzone_and_saturation(INT32 result) const
+{
+ // 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)(result - m_joystick_deadzone) * (INT64)INPUT_ABSOLUTE_MAX / (INT64)(m_joystick_saturation - m_joystick_deadzone);
+
+ // re-apply sign and return
+ return negative ? -result : result;
+}
+
+
+//-------------------------------------------------
+// apply_steadykey - apply steadykey option if
+// enabled
+//-------------------------------------------------
+
+void input_device::apply_steadykey() const
+{
+ // ignore if not enabled
+ if (!m_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 <= m_maxitem; itemid++)
+ {
+ input_device_item *item = m_item[itemid];
+ if (item != NULL && item->itemclass() == ITEM_CLASS_SWITCH)
+ if (downcast<input_device_switch_item *>(item)->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 <= m_maxitem; itemid++)
+ {
+ input_device_item *item = m_item[itemid];
+ if (item != NULL && item->itemclass() == ITEM_CLASS_SWITCH)
+ downcast<input_device_switch_item *>(item)->steadykey_update_to_current();
+ }
+}
+
+
+
+//**************************************************************************
+// 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)
+{
+ memset(m_device, 0, sizeof(m_device));
+
+ // 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, void *internal)
+{
+ // find the next empty index
+ int devindex;
+ for (devindex = 0; devindex < DEVICE_INDEX_MAXIMUM; devindex++)
+ if (m_device[devindex] == NULL)
+ break;
+
+ // call through
+ return add_device(devindex, name, internal);
+}
+
+input_device *input_class::add_device(int devindex, const char *name, void *internal)
+{
+ assert_always(machine().phase() == MACHINE_PHASE_INIT, "Can only call input_class::add_device at init time!");
+ assert(name != NULL);
+ assert(devindex >= 0 && devindex < DEVICE_INDEX_MAXIMUM);
+ assert(m_device[devindex] == NULL);
+
+ // allocate a new device
+ input_device *device = m_device[devindex] = auto_alloc(machine(), input_device(*this, devindex, name, internal));
+
+ // update the maximum index found
+ m_maxindex = MAX(m_maxindex, devindex);
+
+ mame_printf_verbose("Input: Adding %s #%d: %s\n", (*devclass_string_table)[m_devclass], devindex, name);
+ return device;
+}
+
+
+//-------------------------------------------------
+// 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;
+}
+
+
+//-------------------------------------------------
+// frame_callback - per-frame callback for various
+// bookkeeping
+//-------------------------------------------------
+
+void input_class::frame_callback()
+{
+ // iterate over all devices in our class
+ for (int devnum = 0; devnum <= m_maxindex; devnum++)
+ if (m_device[devnum] != NULL)
+ 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;
+}
+
+
+//-------------------------------------------------
+// code_value - return the value of a given
+// input code
+//-------------------------------------------------
+
+INT32 input_manager::code_value(input_code code)
+{
+ g_profiler.start(PROFILER_INPUT);
+ INT32 result = 0;
+
+ // dummy loop to allow clean early exits
+ do
+ {
+ // return 0 for any invalid devices
+ input_device *device = device_from_code(code);
+ if (device == NULL)
+ break;
+
+ // also return 0 if the device class is disabled
+ input_class &devclass = *m_class[code.device_class()];
+ if (!devclass.enabled())
+ break;
+
+ // if this is not a multi device, only return data for item 0 and iterate over all
+ int startindex = code.device_index();
+ int stopindex = startindex;
+ if (!devclass.multi())
+ {
+ if (startindex != 0)
+ break;
+ stopindex = devclass.maxindex();
+ }
+
+ // iterate over all device indices
+ input_item_class targetclass = code.item_class();
+ for (int curindex = startindex; curindex <= stopindex; curindex++)
+ {
+ // lookup the item for the appropriate index
+ code.set_device_index(curindex);
+ input_device_item *item = item_from_code(code);
+ if (item == NULL)
+ continue;
+
+ // process items according to their native type
+ switch (targetclass)
+ {
+ case ITEM_CLASS_ABSOLUTE:
+ if (result == 0)
+ result = item->read_as_absolute(code.item_modifier());
+ break;
+
+ case ITEM_CLASS_RELATIVE:
+ result += item->read_as_relative(code.item_modifier());
+ break;
+
+ case ITEM_CLASS_SWITCH:
+ result |= item->read_as_switch(code.item_modifier());
+ break;
+
+ default:
+ break;
+ }
+ }
+ } while (0);
+
+ // stop the profiler before exiting
+ g_profiler.stop();
+ return result;
+}
+
+
+//-------------------------------------------------
+// code_pressed_once - return non-zero if a given
+// input code has transitioned from off to on
+// since the last call
+//-------------------------------------------------
+
+bool input_manager::code_pressed_once(input_code code)
+{
+ // look for the code in the memory
+ bool curvalue = code_pressed(code);
+ int empty = -1;
+ for (int memnum = 0; memnum < ARRAY_LENGTH(m_switch_memory); memnum++)
+ {
+ // were we previous pressed on the last time through here?
+ if (m_switch_memory[memnum] == code)
+ {
+ // if no longer pressed, clear entry
+ if (curvalue == false)
+ m_switch_memory[memnum] = INPUT_CODE_INVALID;
+
+ // always return false
+ return false;
+ }
+
+ // remember the first empty entry
+ if (empty == -1 && m_switch_memory[memnum] == INPUT_CODE_INVALID)
+ empty = memnum;
+ }
+
+ // if we get here, we were not previously pressed; if still not pressed, return 0
+ if (curvalue == false)
+ return false;
+
+ // otherwise, add ourself to the memory and return 1
+ assert(empty != -1);
+ if (empty != -1)
+ m_switch_memory[empty] = code;
+ return true;
+}
+
+
+//-------------------------------------------------
+// reset_polling - reset memories in preparation
+// for polling
+//-------------------------------------------------
+
+void input_manager::reset_polling()
+{
+ // reset switch memory
+ reset_memory();
+
+ // iterate over device classes and devices
+ for (input_device_class devclass = DEVICE_CLASS_FIRST_VALID; devclass <= DEVICE_CLASS_LAST_VALID; devclass++)
+ for (int devnum = 0; devnum <= m_class[devclass]->maxindex(); devnum++)
+ {
+ // fetch the device; ignore if NULL
+ input_device *device = m_class[devclass]->device(devnum);
+ if (device == NULL)
+ continue;
+
+ // iterate over items within each device
+ for (input_item_id itemid = ITEM_ID_FIRST_VALID; itemid <= device->maxitem(); itemid++)
+ {
+ // for any non-switch items, set memory equal to the current value
+ input_device_item *item = device->item(itemid);
+ if (item != NULL && item->itemclass() != ITEM_CLASS_SWITCH)
+ item->set_memory(code_value(input_code(*device, itemid)));
+ }
+ }
+}
+
+
+//-------------------------------------------------
+// poll_switches - poll for any input
+//-------------------------------------------------
+
+input_code input_manager::poll_switches()
+{
+ // iterate over device classes and devices
+ for (input_device_class devclass = DEVICE_CLASS_FIRST_VALID; devclass <= DEVICE_CLASS_LAST_VALID; devclass++)
+ for (int devnum = 0; devnum <= m_class[devclass]->maxindex(); devnum++)
+ {
+ // fetch the device; ignore if NULL
+ input_device *device = m_class[devclass]->device(devnum);
+ if (device == NULL)
+ continue;
+
+ // iterate over items within each device
+ for (input_item_id itemid = ITEM_ID_FIRST_VALID; itemid <= device->maxitem(); itemid++)
+ {
+ input_device_item *item = device->item(itemid);
+ if (item != NULL)
+ {
+ input_code code(*device, itemid);
+
+ // if the item is natively a switch, poll it
+ if (item->itemclass() == ITEM_CLASS_SWITCH)
+ {
+ if (code_pressed_once(code))
+ return code;
+ else
+ continue;
+ }
+
+ // skip if there is not enough axis movement
+ if (!code_check_axis(*item, code))
+ continue;
+
+ // otherwise, poll axes digitally
+ code.set_item_class(ITEM_CLASS_SWITCH);
+
+ // if this is a joystick X axis, check with left/right modifiers
+ if (devclass == DEVICE_CLASS_JOYSTICK && code.item_id() == ITEM_ID_XAXIS)
+ {
+ code.set_item_modifier(ITEM_MODIFIER_LEFT);
+ if (code_pressed_once(code))
+ return code;
+ code.set_item_modifier(ITEM_MODIFIER_RIGHT);
+ if (code_pressed_once(code))
+ return code;
+ }
+
+ // if this is a joystick Y axis, check with up/down modifiers
+ else if (devclass == DEVICE_CLASS_JOYSTICK && code.item_id() == ITEM_ID_YAXIS)
+ {
+ code.set_item_modifier(ITEM_MODIFIER_UP);
+ if (code_pressed_once(code))
+ return code;
+ code.set_item_modifier(ITEM_MODIFIER_DOWN);
+ if (code_pressed_once(code))
+ return code;
+ }
+
+ // any other axis, check with pos/neg modifiers
+ else
+ {
+ code.set_item_modifier(ITEM_MODIFIER_POS);
+ if (code_pressed_once(code))
+ return code;
+ code.set_item_modifier(ITEM_MODIFIER_NEG);
+ if (code_pressed_once(code))
+ return code;
+ }
+ }
+ }
+ }
+
+ // if nothing, return an invalid code
+ return INPUT_CODE_INVALID;
+}
+
+
+//-------------------------------------------------
+// poll_keyboard_switches - poll for any
+// keyboard-specific input
+//-------------------------------------------------
+
+input_code input_manager::poll_keyboard_switches()
+{
+ // iterate over devices within each class
+ for (int devnum = 0; devnum < m_keyboard_class.maxindex(); devnum++)
+ {
+ // fetch the device; ignore if NULL
+ input_device *device = m_keyboard_class.device(devnum);
+ if (device == NULL)
+ continue;
+
+ // iterate over items within each device
+ for (input_item_id itemid = ITEM_ID_FIRST_VALID; itemid <= device->maxitem(); itemid++)
+ {
+ input_device_item *item = device->item(itemid);
+ if (item != NULL && item->itemclass() == ITEM_CLASS_SWITCH)
+ {
+ input_code code(*device, itemid);
+ if (code_pressed_once(code))
+ return code;
+ }
+ }
+ }
+
+ // if nothing, return an invalid code
+ return INPUT_CODE_INVALID;
+}
+
+
+//-------------------------------------------------
+// code_check_axis - see if axis has moved far
+// enough to trigger a read when polling
+//-------------------------------------------------
+
+bool input_manager::code_check_axis(input_device_item &item, input_code code)
+{
+ // if we've already reported this one, don't bother
+ if (item.memory() == INVALID_AXIS_VALUE)
+ return false;
+
+ // ignore min/max for lightguns
+ // so the selection will not be affected by a gun going out of range
+ INT32 curval = code_value(code);
+ if (code.device_class() == DEVICE_CLASS_LIGHTGUN &&
+ (code.item_id() == ITEM_ID_XAXIS || code.item_id() == ITEM_ID_YAXIS) &&
+ (curval == INPUT_ABSOLUTE_MAX || curval == INPUT_ABSOLUTE_MIN))
+ return false;
+
+ // compute the diff against memory
+ INT32 diff = curval - item.memory();
+ if (diff < 0)
+ diff = -diff;
+
+ // for absolute axes, look for 25% of maximum
+ if (item.itemclass() == ITEM_CLASS_ABSOLUTE && diff > (INPUT_ABSOLUTE_MAX - INPUT_ABSOLUTE_MIN) / 4)
+ {
+ item.set_memory(INVALID_AXIS_VALUE);
+ return true;
+ }
+
+ // for relative axes, look for ~20 pixels movement
+ if (item.itemclass() == ITEM_CLASS_RELATIVE && diff > 20 * INPUT_RELATIVE_PER_PIXEL)
+ {
+ item.set_memory(INVALID_AXIS_VALUE);
+ return true;
+ }
+ return false;
+}
+
+
+//-------------------------------------------------
+// poll_axes - poll for any input
+//-------------------------------------------------
+
+input_code input_manager::poll_axes()
+{
+ // iterate over device classes and devices
+ for (input_device_class devclass = DEVICE_CLASS_FIRST_VALID; devclass <= DEVICE_CLASS_LAST_VALID; devclass++)
+ for (int devnum = 0; devnum <= m_class[devclass]->maxindex(); devnum++)
+ {
+ // fetch the device; ignore if NULL
+ input_device *device = m_class[devclass]->device(devnum);
+ if (device == NULL)
+ continue;
+
+ // iterate over items within each device
+ for (input_item_id itemid = ITEM_ID_FIRST_VALID; itemid <= device->maxitem(); itemid++)
+ {
+ input_device_item *item = device->item(itemid);
+ if (item != NULL && item->itemclass() != ITEM_CLASS_SWITCH)
+ {
+ input_code code(*device, itemid);
+ if (code_check_axis(*item, code))
+ return code;
+ }
+ }
+ }
+
+ // if nothing, return an invalid code
+ return INPUT_CODE_INVALID;
+}
+
+
+//-------------------------------------------------
+// device_from_code - given an input_code return
+// a pointer to the associated device
+//-------------------------------------------------
+
+input_device *input_manager::device_from_code(input_code code) const
+{
+ // if the class is valid, return the appropriate device pointer
+ input_device_class devclass = code.device_class();
+ if (devclass >= DEVICE_CLASS_FIRST_VALID && devclass <= DEVICE_CLASS_LAST_VALID)
+ return m_class[devclass]->device(code.device_index());
+
+ // otherwise, return NULL
+ return NULL;
+}
+
+
+//-------------------------------------------------
+// item_from_code - given an input_code return
+// a pointer to the appropriate input_device_item
+//-------------------------------------------------
+
+input_device_item *input_manager::item_from_code(input_code code) const
+{
+ // first get the device; if none, then we don't have an item
+ input_device *device = device_from_code(code);
+ if (device == NULL)
+ return NULL;
+
+ // then return the device's item
+ return device->item(code.item_id());
+}
+
+
+//-------------------------------------------------
+// reset_memory - reset the array of memory for
+// pressed switches
+//-------------------------------------------------
+
+void input_manager::reset_memory()
+{
+ // reset all entries in switch memory to invalid
+ for (int memnum = 0; memnum < ARRAY_LENGTH(m_switch_memory); memnum++)
+ m_switch_memory[memnum] = INPUT_CODE_INVALID;
+}
+
+
+//-------------------------------------------------
+// code_from_itemid - translates an input_item_id
+// to an input_code
+//-------------------------------------------------
+
+input_code input_manager::code_from_itemid(input_item_id itemid) const
+{
+ // iterate over device classes and devices
+ for (input_device_class devclass = DEVICE_CLASS_FIRST_VALID; devclass <= DEVICE_CLASS_LAST_VALID; devclass++)
+ for (int devnum = 0; devnum <= m_class[devclass]->maxindex(); devnum++)
+ {
+ input_device *device = m_class[devclass]->device(devnum);
+ if (device != NULL && device->item(itemid) != NULL)
+ return input_code(*device, itemid);
+ }
+
+ return INPUT_CODE_INVALID;
+}
+
+
+//-------------------------------------------------
+// code_name - convert an input code into a
+// friendly name
+//-------------------------------------------------
+
+const char *input_manager::code_name(astring &string, input_code code) const
+{
+ string.reset();
+
+ // if nothing there, return an empty string
+ input_device_item *item = item_from_code(code);
+ if (item == NULL)
+ return string;
+
+ // determine the devclass part
+ const char *devclass = (*devclass_string_table)[code.device_class()];
+
+ // determine the devindex part
+ astring devindex;
+ devindex.printf("%d", code.device_index() + 1);
+
+ // if we're unifying all devices, don't display a number
+ if (!m_class[code.device_class()]->multi())
+ devindex.reset();
+
+ // 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)
+ {
+ devclass = "";
+ devindex.reset();
+ }
+
+ // devcode part comes from the item name
+ const char *devcode = item->name();
+
+ // determine the modifier part
+ const char *modifier = (*modifier_string_table)[code.item_modifier()];
+
+ // devcode is redundant with joystick switch left/right/up/down
+ if (device_class == DEVICE_CLASS_JOYSTICK && code.item_class() == ITEM_CLASS_SWITCH)
+ if (code.item_modifier() >= ITEM_MODIFIER_LEFT && code.item_modifier() <= ITEM_MODIFIER_DOWN)
+ devcode = "";
+
+ // concatenate the strings
+ string.cpy(devclass);
+ if (devindex)
+ string.cat(" ").cat(devindex);
+ if (devcode[0] != 0)
+ string.cat(" ").cat(devcode);
+ if (modifier != NULL)
+ string.cat(" ").cat(modifier);
+
+ // delete any leading spaces
+ return string.trimspace();
+}
+
+
+//-------------------------------------------------
+// code_to_token - create a token for a given code
+//-------------------------------------------------
+
+const char *input_manager::code_to_token(astring &string, input_code code) const
+{
+ // determine the devclass part
+ const char *devclass = (*devclass_token_table)[code.device_class()];
+
+ // determine the devindex part; keyboard 0 doesn't show an index
+ astring devindex;
+ devindex.printf("%d", code.device_index() + 1);
+ if (code.device_class() == DEVICE_CLASS_KEYBOARD && code.device_index() == 0)
+ devindex.reset();
+
+ // determine the itemid part; look up in the table if we don't have a token
+ input_device_item *item = item_from_code(code);
+ const char *devcode = (item != NULL) ? item->token() : "UNKNOWN";
+
+ // determine the modifier part
+ const char *modifier = (*modifier_token_table)[code.item_modifier()];
+
+ // determine the itemclass part; if we match the native class, we don't include this
+ const char *itemclass = "";
+ if (item == NULL || item->itemclass() != code.item_class())
+ itemclass = (*itemclass_token_table)[code.item_class()];
+
+ // concatenate the strings
+ string.cpy(devclass);
+ if (devindex)
+ string.cat("_").cat(devindex);
+ if (devcode[0] != 0)
+ string.cat("_").cat(devcode);
+ if (modifier != NULL)
+ string.cat("_").cat(modifier);
+ if (itemclass[0] != 0)
+ string.cat("_").cat(itemclass);
+ return string;
+}
+
+
+//-------------------------------------------------
+// code_from_token - extract an input code from a
+// token
+//-------------------------------------------------
+
+input_code input_manager::code_from_token(const char *_token)
+{
+ // copy the token and break it into pieces
+ astring token[6];
+ int numtokens;
+ for (numtokens = 0; numtokens < ARRAY_LENGTH(token); )
+ {
+ // make a token up to the next underscore
+ char *score = (char *)strchr(_token, '_');
+ token[numtokens++].cpy(_token, (score == NULL) ? strlen(_token) : (score - _token));
+
+ // if we hit the end, we're done, else advance our pointer
+ if (score == NULL)
+ break;
+ _token = score + 1;
+ }
+
+ // first token should be the devclass
+ int curtok = 0;
+ input_device_class devclass = input_device_class((*devclass_token_table)[token[curtok++].cstr()]);
+ if (devclass == ~0)
+ return INPUT_CODE_INVALID;
+
+ // second token might be index; look for number
+ int devindex = 0;
+ if (numtokens > 2 && sscanf(token[curtok], "%d", &devindex) == 1)
+ {
+ curtok++;
+ devindex--;
+ }
+ if (curtok >= numtokens)
+ return INPUT_CODE_INVALID;
+
+ // next token is the item ID
+ input_item_id itemid = input_item_id((*itemid_token_table)[token[curtok].cstr()]);
+ bool standard = (itemid != ~0);
+
+ // if we're a standard code, default the itemclass based on it
+ input_item_class itemclass = ITEM_CLASS_INVALID;
+ if (standard)
+ itemclass = m_class[devclass]->standard_item_class(itemid);
+
+ // otherwise, keep parsing
+ else
+ {
+ // if this is an invalid device, we have nothing to look up
+ input_device *device = m_class[devclass]->device(devindex);
+ if (device == NULL)
+ return INPUT_CODE_INVALID;
+
+ // if not a standard code, look it up in the device specific codes
+ for (itemid = ITEM_ID_FIRST_VALID; itemid <= device->maxitem(); itemid++)
+ {
+ input_device_item *item = device->item(itemid);
+ if (item != NULL && token[curtok].cmp(item->token()) == 0)
+ {
+ // take the itemclass from the item
+ itemclass = item->itemclass();
+ break;
+ }
+ }
+
+ // bail on fail
+ if (itemid > device->maxitem())
+ return INPUT_CODE_INVALID;
+ }
+ curtok++;
+
+ // if we have another token, it is probably a modifier
+ input_item_modifier modifier = ITEM_MODIFIER_NONE;
+ if (curtok < numtokens)
+ {
+ modifier = input_item_modifier((*modifier_token_table)[token[curtok].cstr()]);
+ if (modifier != ~0)
+ curtok++;
+ else
+ modifier = ITEM_MODIFIER_NONE;
+ }
+
+ // if we have another token, it is the item class
+ if (curtok < numtokens)
+ {
+ UINT32 temp = (*itemclass_token_table)[token[curtok].cstr()];
+ if (temp != ~0)
+ {
+ curtok++;
+ itemclass = input_item_class(temp);
+ }
+ }
+
+ // we should have consumed all tokens
+ if (curtok != numtokens)
+ return INPUT_CODE_INVALID;
+
+ // assemble the final code
+ return input_code(devclass, devindex, itemclass, modifier, itemid);
+}
+
+
+//-------------------------------------------------
+// seq_pressed - return true if the given sequence
+// of switch inputs is "pressed"
+//-------------------------------------------------
+
+bool input_manager::seq_pressed(const input_seq &seq)
+{
+ // iterate over all of the codes
+ bool result = false;
+ bool invert = false;
+ bool first = true;
+ for (int codenum = 0; ; codenum++)
+ {
+ // handle NOT
+ input_code code = seq[codenum];
+ if (code == input_seq::not_code)
+ invert = true;
+
+ // handle OR and END
+ else if (code == input_seq::or_code || code == input_seq::end_code)
+ {
+ // if we have a positive result from the previous set, we're done
+ if (result || code == input_seq::end_code)
+ break;
+
+ // otherwise, reset our state
+ result = false;
+ invert = false;
+ first = true;
+ }
+
+ // handle everything else as a series of ANDs
+ else
+ {
+ // if this is the first in the sequence, result is set equal
+ if (first)
+ result = code_pressed(code) ^ invert;
+
+ // further values are ANDed
+ else if (result)
+ result &= code_pressed(code) ^ invert;
+
+ // no longer first, and clear the invert flag
+ first = invert = false;
+ }
+ }
+
+ // return the result if we queried at least one switch
+ return result;
+}
+
+
+//-------------------------------------------------
+// seq_axis_value - return the value of an axis
+// defined in an input sequence
+//-------------------------------------------------
+
+INT32 input_manager::seq_axis_value(const input_seq &seq, input_item_class &itemclass)
+{
+ // start with no valid classes
+ input_item_class itemclasszero = ITEM_CLASS_INVALID;
+ itemclass = ITEM_CLASS_INVALID;
+
+ // iterate over all of the codes
+ INT32 result = 0;
+ bool invert = false;
+ bool enable = true;
+ for (int codenum = 0; ; codenum++)
+ {
+ // handle NOT
+ input_code code = seq[codenum];
+ if (code == input_seq::not_code)
+ invert = true;
+
+ // handle OR and END
+ else if (code == input_seq::or_code || code == input_seq::end_code)
+ {
+ // if we have a positive result from the previous set, we're done
+ if (itemclass != ITEM_CLASS_INVALID || code == input_seq::end_code)
+ break;
+
+ // otherwise, reset our state
+ result = 0;
+ invert = false;
+ enable = true;
+ }
+
+ // handle everything else only if we're still enabled
+ else if (enable)
+ {
+ // switch codes serve as enables
+ if (code.item_class() == ITEM_CLASS_SWITCH)
+ {
+ // AND against previous digital codes
+ if (enable)
+ enable &= code_pressed(code) ^ invert;
+ }
+
+ // non-switch codes are analog values
+ else
+ {
+ INT32 value = code_value(code);
+
+ // if we got a 0 value, don't do anything except remember the first type
+ if (value == 0)
+ {
+ if (itemclasszero == ITEM_CLASS_INVALID)
+ itemclasszero = code.item_class();
+ }
+
+ // non-zero absolute values stick
+ else if (code.item_class() == ITEM_CLASS_ABSOLUTE)
+ {
+ itemclass = ITEM_CLASS_ABSOLUTE;
+ result = value;
+ }
+
+ // non-zero relative values accumulate
+ else if (code.item_class() == ITEM_CLASS_RELATIVE)
+ {
+ itemclass = ITEM_CLASS_RELATIVE;
+ result += value;
+ }
+ }
+
+ // clear the invert flag
+ invert = false;
+ }
+ }
+
+ // if the caller wants to know the type, provide it
+ if (result == 0)
+ itemclass = itemclasszero;
+ return result;
+}
+
+
+//-------------------------------------------------
+// seq_poll_start - begin polling for a new
+// sequence of the given itemclass
+//-------------------------------------------------
+
+void input_manager::seq_poll_start(input_item_class itemclass, const input_seq *startseq)
+{
+ assert(itemclass == ITEM_CLASS_SWITCH || itemclass == ITEM_CLASS_ABSOLUTE || itemclass == ITEM_CLASS_RELATIVE);
+
+ // reset the recording count and the clock
+ m_poll_seq_last_ticks = 0;
+ m_poll_seq_class = itemclass;
+ m_poll_seq.reset();
+
+ // grab the starting sequence to append to, and append an OR
+ if (startseq != NULL)
+ {
+ m_poll_seq = *startseq;
+ if (m_poll_seq.length() > 0)
+ m_poll_seq += input_seq::or_code;
+ }
+
+ // flush out any goobers
+ reset_polling();
+ input_code dummycode = KEYCODE_ENTER;
+ while (dummycode != INPUT_CODE_INVALID)
+ dummycode = (m_poll_seq_class == ITEM_CLASS_SWITCH) ? poll_switches() : poll_axes();
+}
+
+
+//-------------------------------------------------
+// input_seq_poll - continue polling
+//-------------------------------------------------
+
+bool input_manager::seq_poll()
+{
+ int curlen = m_poll_seq.length();
+ input_code lastcode = m_poll_seq[curlen - 1];
+
+ // switch case: see if we have a new code to process
+ input_code newcode;
+ if (m_poll_seq_class == ITEM_CLASS_SWITCH)
+ {
+ newcode = poll_switches();
+ if (newcode != INPUT_CODE_INVALID)
+ {
+ // if code is duplicate, toggle the NOT state on the code
+ if (curlen > 0 && newcode == lastcode)
+ {
+ // back up over the existing code
+ m_poll_seq.backspace();
+
+ // if there was a NOT preceding it, delete it as well, otherwise append a fresh one
+ if (m_poll_seq[curlen - 2] == input_seq::not_code)
+ m_poll_seq.backspace();
+ else
+ m_poll_seq += input_seq::not_code;
+ }
+ }
+ }
+
+ // absolute/relative case: see if we have an analog change of sufficient amount
+ else
+ {
+ bool has_or = false;
+ if (lastcode == input_seq::or_code)
+ {
+ lastcode = m_poll_seq[curlen - 2];
+ has_or = true;
+ }
+ newcode = poll_axes();
+
+ // if the last code doesn't match absolute/relative of this code, ignore the new one
+ if ((lastcode.item_class() == ITEM_CLASS_ABSOLUTE && newcode.item_class() != ITEM_CLASS_ABSOLUTE) ||
+ (lastcode.item_class() == ITEM_CLASS_RELATIVE && newcode.item_class() != ITEM_CLASS_RELATIVE))
+ newcode = INPUT_CODE_INVALID;
+
+ // if the new code is valid, check for half-axis toggles on absolute controls
+ if (newcode != INPUT_CODE_INVALID && curlen > 0 && newcode.item_class() == ITEM_CLASS_ABSOLUTE)
+ {
+ input_code last_nomodifier = lastcode;
+ last_nomodifier.set_item_modifier(ITEM_MODIFIER_NONE);
+ if (newcode == last_nomodifier)
+ {
+ // increment the modifier, wrapping back to none
+ switch (lastcode.item_modifier())
+ {
+ case ITEM_MODIFIER_NONE: newcode.set_item_modifier(ITEM_MODIFIER_POS); break;
+ case ITEM_MODIFIER_POS: newcode.set_item_modifier(ITEM_MODIFIER_NEG); break;
+ default:
+ case ITEM_MODIFIER_NEG: newcode.set_item_modifier(ITEM_MODIFIER_NONE); break;
+ }
+
+ // back up over the previous code so we can re-append
+ if (has_or)
+ m_poll_seq.backspace();
+ m_poll_seq.backspace();
+ }
+ }
+ }
+
+ // if we got a new code to append it, append it and reset the timer
+ if (newcode != INPUT_CODE_INVALID)
+ {
+ m_poll_seq += newcode;
+ m_poll_seq_last_ticks = osd_ticks();
+ }
+
+ // if we're recorded at least one item and 2/3 of a second has passed, we're done
+ if (m_poll_seq_last_ticks != 0 && osd_ticks() > m_poll_seq_last_ticks + osd_ticks_per_second() * 2 / 3)
+ {
+ // if the final result is invalid, reset to nothing
+ if (!m_poll_seq.is_valid())
+ m_poll_seq.reset();
+
+ // return true to indicate that we are finished
+ return true;
+ }
+
+ // return false to indicate we are still polling
+ return false;
+}
+
+
+//-------------------------------------------------
+// seq_name - generate the friendly name of a
+// sequence
+//-------------------------------------------------
+
+const char *input_manager::seq_name(astring &string, const input_seq &seq) const
+{
+ // make a copy of our sequence, removing any invalid bits
+ input_code clean_codes[sizeof(seq) / sizeof(input_code)];
+ int clean_index = 0;
+ astring codestr;
+ for (int codenum = 0; seq[codenum] != input_seq::end_code; codenum++)
+ {
+ // if this is a code item which is not valid, don't copy it and remove any preceding ORs/NOTs
+ input_code code = seq[codenum];
+ if (!code.internal() && strlen(code_name(codestr, code)) == 0)
+ {
+ while (clean_index > 0 && clean_codes[clean_index - 1].internal())
+ clean_index--;
+ }
+ else if (clean_index > 0 || !code.internal())
+ clean_codes[clean_index++] = code;
+ }
+
+ // special case: empty
+ if (clean_index == 0)
+ return string.cpy((seq.length() == 0) ? "None" : "n/a");
+
+ // start with an empty buffer
+ string.reset();
+
+ // loop until we hit the end
+ for (int codenum = 0; codenum < clean_index; codenum++)
+ {
+ // append a space if not the first code
+ if (codenum != 0)
+ string.cat(" ");
+
+ // handle OR/NOT codes here
+ input_code code = clean_codes[codenum];
+ if (code == input_seq::or_code)
+ string.cat("or");
+ else if (code == input_seq::not_code)
+ string.cat("not");
+
+ // otherwise, assume it is an input code and ask the input system to generate it
+ else
+ string.cat(code_name(codestr, code));
+ }
+ return string;
+}
+
+
+//-------------------------------------------------
+// seq_to_tokens - generate the tokenized form of
+// a sequence
+//-------------------------------------------------
+
+const char *input_manager::seq_to_tokens(astring &string, const input_seq &seq) const
+{
+ // start with an empty buffer
+ string.reset();
+
+ // loop until we hit the end
+ astring codestr;
+ for (int codenum = 0; seq[codenum] != input_seq::end_code; codenum++)
+ {
+ // append a space if not the first code
+ if (codenum != 0)
+ string.cat(" ");
+
+ // handle OR/NOT codes here
+ input_code code = seq[codenum];
+ if (code == input_seq::or_code)
+ string.cat("OR");
+ else if (code == input_seq::not_code)
+ string.cat("NOT");
+ else if (code == input_seq::default_code)
+ string.cat("DEFAULT");
+
+ // otherwise, assume it is an input code and ask the input system to generate it
+ else
+ string.cat(code_to_token(codestr, code));
+ }
+ return string;
+}
+
+
+//-------------------------------------------------
+// seq_from_tokens - generate the tokenized form
+// of a sequence
+//-------------------------------------------------
+
+void input_manager::seq_from_tokens(input_seq &seq, const char *string)
+{
+ // start with a blank sequence
+ seq.reset();
+
+ // loop until we're done
+ astring strcopy = string;
+ char *str = const_cast<char *>(strcopy.cstr());
+ while (1)
+ {
+ // trim any leading spaces
+ while (*str != 0 && isspace((UINT8)*str))
+ str++;
+
+ // bail if we're done
+ if (*str == 0)
+ return;
+
+ // find the end of the token and make it upper-case along the way
+ char *strtemp;
+ for (strtemp = str; *strtemp != 0 && !isspace((UINT8)*strtemp); strtemp++)
+ *strtemp = toupper((UINT8)*strtemp);
+ char origspace = *strtemp;
+ *strtemp = 0;
+
+ // look for common stuff
+ input_code code;
+ if (strcmp(str, "OR") == 0)
+ code = input_seq::or_code;
+ else if (strcmp(str, "NOT") == 0)
+ code = input_seq::not_code;
+ else if (strcmp(str, "DEFAULT") == 0)
+ code = input_seq::default_code;
+ else
+ code = code_from_token(str);
+
+ // translate and add to the sequence
+ seq += code;
+
+ // advance
+ if (origspace == 0)
+ return;
+ str = strtemp + 1;
+ }
+}
+
+
+//-------------------------------------------------
+// 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;
+
+ astring tempstr;
+ mame_printf_verbose("Input: Changing default joystick map = %s", map.to_string(tempstr));
+
+ // 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 != NULL)
+ 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] != NULL)
+ m_token.cpy((*itemid_token_table)[itemid]);
+
+ // otherwise, create a tokenized name
+ else
+ m_token.cpy(name).makeupper().delchr(' ').delchr('_');
+}
+
+
+
+//**************************************************************************
+// 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 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 != NULL)
+ 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 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 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 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 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 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 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 input_device_absolute_item::read_as_switch(input_item_modifier modifier)
+{
+ // start with the current value
+ INT32 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 != NULL && yaxis_item != NULL)
+ {
+ // 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 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 input_device_absolute_item::read_as_absolute(input_item_modifier modifier)
+{
+ // start with the current value
+ INT32 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 != NULL && 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 = MAX(result, 0) * 2 + INPUT_ABSOLUTE_MIN;
+ if (modifier == ITEM_MODIFIER_NEG)
+ result = MAX(-result, 0) * 2 + INPUT_ABSOLUTE_MIN;
+ return result;
+}