mame/src/emu/input.c
Aaron Giles eb539cce9d Many casts added to the core files, and various other tweaks
to make them compile as either C or C++.
2009-03-12 07:43:03 +00:00

1738 lines
54 KiB
C

/***************************************************************************
input.c
Handle input from the user.
Copyright Nicola Salmoria and the MAME Team.
Visit http://mamedev.org for licensing and usage restrictions.
****************************************************************************
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 "input.h"
#include "mame.h"
#include "emuopts.h"
#include "profiler.h"
/***************************************************************************
CONSTANTS
***************************************************************************/
/* joystick mapping codes */
#define JOYSTICK_MAP_NEUTRAL 0x00
#define JOYSTICK_MAP_LEFT 0x01
#define JOYSTICK_MAP_RIGHT 0x02
#define JOYSTICK_MAP_UP 0x04
#define JOYSTICK_MAP_DOWN 0x08
#define JOYSTICK_MAP_STICKY 0x0f
/* the largest number of tracked pressed switches for memory */
#define MAX_PRESSED_SWITCHES 64
/* invalid memory value for axis polling */
#define INVALID_AXIS_VALUE 0x7fffffff
/***************************************************************************
TYPE DEFINITIONS
***************************************************************************/
/* single device item */
typedef struct _input_device_item input_device_item;
struct _input_device_item
{
input_device_class devclass; /* device class of parent item */
int devindex; /* device index of parent item */
astring * name; /* string name of item */
astring * token; /* tokenized name for non-standard items */
void * internal; /* internal callback pointer */
input_item_class itemclass; /* class of the item */
input_item_id itemid; /* originally specified item id */
item_get_state_func getstate; /* get state callback */
INT32 current; /* current raw value */
INT32 memory; /* "memory" value, to remember where we started during polling */
INT32 oncelatch; /* latched "once" value, cleared after each read */
/* keyboard information */
INT32 steadykey; /* the live steadykey state */
INT32 oldkey; /* old live state */
};
/* a 9x9 joystick map */
typedef struct _joystick_map joystick_map;
struct _joystick_map
{
UINT8 map[9][9]; /* 9x9 grid */
};
/* a single input device */
struct _input_device
{
running_machine * machine; /* machine we are attached to */
astring * name; /* string name of device */
input_device_class devclass; /* class of this device */
int devindex; /* device index of this device */
input_device_item * item[ITEM_ID_ABSOLUTE_MAXIMUM]; /* array of pointers to items */
input_item_id maxitem; /* maximum item index */
void * internal; /* internal callback pointer */
/* joystick information */
joystick_map joymap; /* joystick map for this device */
UINT8 lastmap; /* last joystick map value for this device */
};
/* a list of devices as an array */
typedef struct _input_device_list input_device_list;
struct _input_device_list
{
input_device * list; /* the array */
int count; /* elements in the array */
UINT8 enabled; /* is this class enabled? */
UINT8 multi; /* are multiple instances of this class allowed? */
};
/* code <-> string matching */
typedef struct _code_string_table code_string_table;
struct _code_string_table
{
UINT32 code;
const char * 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
***************************************************************************/
/* array of devices for each class */
static input_device_list device_list[DEVICE_CLASS_MAXIMUM];
static input_code code_pressed_memory[MAX_PRESSED_SWITCHES];
/* device configuration */
static UINT8 steadykey_enabled;
static UINT8 lightgun_reload_button;
static const char * joystick_map_default;
static INT32 joystick_deadzone;
static INT32 joystick_saturation;
/* 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";
/***************************************************************************
FUNCTION PROTOTYPES
***************************************************************************/
static void input_frame(running_machine *machine);
static input_device_item *input_code_item(input_code code);
static INT32 convert_absolute_value(input_code code, input_device_item *item);
static INT32 convert_relative_value(input_code code, input_device_item *item);
static INT32 convert_switch_value(input_code code, input_device_item *item);
static INT32 apply_deadzone_and_saturation(input_code code, INT32 result);
static int joystick_map_parse(const char *mapstring, joystick_map *map);
static void joystick_map_print(const char *header, const char *origstring, const joystick_map *map);
/***************************************************************************
INLINE FUNCTIONS
***************************************************************************/
/*-------------------------------------------------
input_code_device - given an input_code return
a pointer to the associated device
-------------------------------------------------*/
INLINE input_device *input_code_device(input_code code)
{
/* if the class is valid... */
input_device_class devclass = INPUT_CODE_DEVCLASS(code);
if (devclass > DEVICE_CLASS_INVALID && devclass < DEVICE_CLASS_MAXIMUM)
{
/* ...and the index is valid for that class, return a pointer to the device */
int devindex = INPUT_CODE_DEVINDEX(code);
if (devindex < device_list[devclass].count)
return &device_list[devclass].list[devindex];
}
/* otherwise, return NULL */
return NULL;
}
/*-------------------------------------------------
device_item_to_code - convert a device/item
pair to a standard code
-------------------------------------------------*/
INLINE input_code device_item_to_code(input_device *device, input_item_id itemid)
{
int devindex = device->devindex;
assert(devindex < device_list[device->devclass].count);
assert(itemid < ITEM_ID_ABSOLUTE_MAXIMUM);
assert(device->item[itemid] != NULL);
return INPUT_CODE(device->devclass, devindex, device->item[itemid]->itemclass, ITEM_MODIFIER_NONE, itemid);
}
/*-------------------------------------------------
input_item_standard_class - return the class
of a standard item
-------------------------------------------------*/
INLINE input_item_class input_item_standard_class(input_device_class devclass, 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 (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;
}
/*-------------------------------------------------
input_item_update_value - update the value
of an input item
-------------------------------------------------*/
INLINE void input_item_update_value(input_device_item *item)
{
item->current = (*item->getstate)(device_list[item->devclass].list[item->devindex].internal, item->internal);
}
/*-------------------------------------------------
code_pressed_memory_reset - reset the array
of memory for pressed switches
-------------------------------------------------*/
INLINE void code_pressed_memory_reset(void)
{
int memnum;
for (memnum = 0; memnum < MAX_PRESSED_SWITCHES; memnum++)
code_pressed_memory[memnum] = INPUT_CODE_INVALID;
}
/*-------------------------------------------------
string_to_code - convert a string to a code
via table lookup
-------------------------------------------------*/
INLINE UINT32 string_to_code(const code_string_table *table, const char *string)
{
/* find a matching string */
for ( ; table->code != ~0; table++)
if (strcmp(string, table->string) == 0)
return table->code;
/* on failure, return ~0 */
return ~0;
}
/*-------------------------------------------------
code_to_string - convert a code to a string
via table lookup
-------------------------------------------------*/
INLINE const char *code_to_string(const code_string_table *table, UINT32 code)
{
/* find a matching code, or */
for ( ; table->code != ~0; table++)
if (table->code == code)
return table->string;
/* return the default string */
return table->string;
}
/***************************************************************************
INITIALIZATION
***************************************************************************/
/*-------------------------------------------------
input_init - initialize the input lists
-------------------------------------------------*/
void input_init(running_machine *machine)
{
joystick_map map;
/* reset the device lists */
memset(device_list, 0, sizeof(device_list));
/* reset code memory */
code_pressed_memory_reset();
/* request a per-frame callback for bookkeeping */
add_frame_callback(machine, input_frame);
/* read input enable options */
device_list[DEVICE_CLASS_KEYBOARD].enabled = TRUE;
device_list[DEVICE_CLASS_MOUSE].enabled = options_get_bool(mame_options(), OPTION_MOUSE);
device_list[DEVICE_CLASS_LIGHTGUN].enabled = options_get_bool(mame_options(), OPTION_LIGHTGUN);
device_list[DEVICE_CLASS_JOYSTICK].enabled = options_get_bool(mame_options(), OPTION_JOYSTICK);
/* read input device multi options */
device_list[DEVICE_CLASS_KEYBOARD].multi = options_get_bool(mame_options(), OPTION_MULTIKEYBOARD);
device_list[DEVICE_CLASS_MOUSE].multi = options_get_bool(mame_options(), OPTION_MULTIMOUSE);
device_list[DEVICE_CLASS_LIGHTGUN].multi = TRUE;
device_list[DEVICE_CLASS_JOYSTICK].multi = TRUE;
/* read other input options */
steadykey_enabled = options_get_bool(mame_options(), OPTION_STEADYKEY);
lightgun_reload_button = options_get_bool(mame_options(), OPTION_OFFSCREEN_RELOAD);
joystick_deadzone = (INT32)(options_get_float(mame_options(), OPTION_JOYSTICK_DEADZONE) * INPUT_ABSOLUTE_MAX);
joystick_saturation = (INT32)(options_get_float(mame_options(), OPTION_JOYSTICK_SATURATION) * INPUT_ABSOLUTE_MAX);
/* get the default joystick map */
joystick_map_default = options_get_string(mame_options(), OPTION_JOYSTICK_MAP);
if (joystick_map_default[0] == 0 || strcmp(joystick_map_default, "auto") == 0)
joystick_map_default = joystick_map_8way;
if (!joystick_map_parse(joystick_map_default, &map))
mame_printf_error("Invalid joystick map: %s\n", joystick_map_default);
else if (joystick_map_default != joystick_map_8way)
joystick_map_print("Input: Default joystick map", joystick_map_default, &map);
}
/*-------------------------------------------------
input_device_class_enable - enable or disable
a device class
-------------------------------------------------*/
void input_device_class_enable(input_device_class devclass, UINT8 enable)
{
assert(devclass > DEVICE_CLASS_INVALID && devclass < DEVICE_CLASS_MAXIMUM);
device_list[devclass].enabled = enable;
}
/*-------------------------------------------------
input_enable_device_class - is a device class
enabled?
-------------------------------------------------*/
UINT8 input_device_class_enabled(input_device_class devclass)
{
assert(devclass > DEVICE_CLASS_INVALID && devclass < DEVICE_CLASS_MAXIMUM);
return device_list[devclass].enabled;
}
/*-------------------------------------------------
input_device_set_joystick_map - set the
joystick map for a device
-------------------------------------------------*/
int input_device_set_joystick_map(int devindex, const char *mapstring)
{
int startindex = devindex;
int stopindex = devindex;
joystick_map map;
int joynum;
/* parse the map */
if (!joystick_map_parse(mapstring, &map))
return FALSE;
/* devindex -1 means set the same for all */
if (devindex == -1)
{
startindex = 0;
stopindex = device_list[DEVICE_CLASS_JOYSTICK].count - 1;
joystick_map_print("Input: Changing default joystick map", mapstring, &map);
}
/* ignore if out of range */
else if (devindex >= device_list[DEVICE_CLASS_JOYSTICK].count)
return TRUE;
/* iterate over joysticks and set the map */
for (joynum = startindex; joynum <= stopindex; joynum++)
device_list[DEVICE_CLASS_JOYSTICK].list[joynum].joymap = map;
return TRUE;
}
/*-------------------------------------------------
input_frame - per-frame callback for various
bookkeeping
-------------------------------------------------*/
static void input_frame(running_machine *machine)
{
/* if steadykey is enabled, do processing here */
if (steadykey_enabled)
{
int devnum;
/* iterate over keyboards */
for (devnum = 0; devnum < device_list[DEVICE_CLASS_KEYBOARD].count; devnum++)
{
input_device *device = &device_list[DEVICE_CLASS_KEYBOARD].list[devnum];
input_item_id itemid;
int changed = FALSE;
/* update the state of all the keys and see if any changed state */
for (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_item_update_value(item);
if ((item->current ^ item->oldkey) & 1)
{
changed = TRUE;
/* if the keypress was missed, turn it on for one frame */
if (((item->current | item->steadykey) & 1) == 0)
item->steadykey = 1;
}
}
}
/* if the keyboard state is stable, copy it over */
for (itemid = ITEM_ID_FIRST_VALID; itemid <= device->maxitem; itemid++)
{
input_device_item *item = device->item[itemid];
if (item != NULL && item->itemclass == ITEM_CLASS_SWITCH)
{
if (!changed)
item->steadykey = item->current;
item->oldkey = item->current;
}
}
}
}
}
/***************************************************************************
OSD CONFIGURATION AND ACCESS
***************************************************************************/
/*-------------------------------------------------
input_device_add - add a new input device
-------------------------------------------------*/
input_device *input_device_add(running_machine *machine, input_device_class devclass, const char *name, void *internal)
{
input_device_list *devlist = &device_list[devclass];
input_device *device;
assert_always(mame_get_phase(machine) == MAME_PHASE_INIT, "Can only call input_device_add at init time!");
assert(name != NULL);
assert(devclass != DEVICE_CLASS_INVALID && devclass < DEVICE_CLASS_MAXIMUM);
/* allocate a new device */
devlist->list = (input_device *)auto_realloc(devlist->list, (devlist->count + 1) * sizeof(devlist->list[0]));
device = &devlist->list[devlist->count++];
memset(device, 0, sizeof(*device));
/* fill in the data */
device->machine = machine;
device->name = astring_cpyc(auto_astring_alloc(), name);
device->devclass = devclass;
device->devindex = devlist->count - 1;
device->internal = internal;
/* default to 8-way map for joysticks */
if (devclass == DEVICE_CLASS_JOYSTICK)
{
joystick_map_parse(joystick_map_default, &device->joymap);
device->lastmap = JOYSTICK_MAP_NEUTRAL;
}
mame_printf_verbose("Input: Adding %s #%d: %s\n", code_to_string(devclass_string_table, devclass), devlist->count, astring_c(device->name));
return device;
}
/*-------------------------------------------------
input_device_item_add - add a new item to an
input device
-------------------------------------------------*/
void input_device_item_add(input_device *device, const char *name, void *internal, input_item_id itemid, item_get_state_func getstate)
{
input_device_item *item;
input_item_id itemid_std = itemid;
assert_always(mame_get_phase(device->machine) == MAME_PHASE_INIT, "Can only call input_device_item_add 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 */
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 (device->item[itemid] == NULL)
break;
assert(itemid <= ITEM_ID_ABSOLUTE_MAXIMUM);
/* make sure we don't have any overlap */
assert(device->item[itemid] == NULL);
/* allocate a new item and copy data into it */
item = (input_device_item *)auto_malloc(sizeof(*item));
memset(item, 0, sizeof(*item));
device->item[itemid] = item;
device->maxitem = MAX(device->maxitem, itemid);
/* copy in the data passed in from the item list */
item->devclass = device->devclass;
item->devindex = device->devindex;
item->name = astring_cpyc(auto_astring_alloc(), name);
item->token = NULL;
item->internal = internal;
item->itemclass = input_item_standard_class(device->devclass, itemid_std);
item->itemid = itemid;
item->getstate = getstate;
/* if we're custom, create a tokenized name */
if (itemid > ITEM_ID_MAXIMUM)
{
/* copy the item name, removing spaces/underscores and making all caps */
item->token = astring_toupper(astring_cpyc(auto_astring_alloc(), name));
astring_delchr(item->token, ' ');
astring_delchr(item->token, '_');
}
/* otherwise, make sure we have a valid standard token */
else
{
assert(code_to_string(itemid_token_table, itemid) != NULL);
}
}
/***************************************************************************
STATE QUERIES
***************************************************************************/
/*-------------------------------------------------
input_code_value - return the value of a
given input code
-------------------------------------------------*/
INT32 input_code_value(input_code code)
{
input_device_class devclass = INPUT_CODE_DEVCLASS(code);
int startindex = INPUT_CODE_DEVINDEX(code);
int stopindex = startindex;
INT32 result = 0;
int curindex;
profiler_mark(PROFILER_INPUT);
/* return 0 for any disabled or invalid device classes */
if (devclass <= DEVICE_CLASS_INVALID || devclass >= DEVICE_CLASS_MAXIMUM || !device_list[devclass].enabled)
goto exit;
if (startindex >= device_list[devclass].count)
goto exit;
/* if this is not a multi device, only return data for item 0 */
if (!device_list[devclass].multi)
{
if (startindex != 0)
goto exit;
/* otherwise, iterate over all */
startindex = 0;
stopindex = device_list[devclass].count - 1;
}
/* iterate over all indices */
for (curindex = startindex; curindex <= stopindex; curindex++)
{
/* lookup the item for the appropriate index */
input_device_item *item = input_code_item(INPUT_CODE_SET_DEVINDEX(code, curindex));
if (item == NULL)
continue;
/* update the value of this item */
input_item_update_value(item);
/* process items according to their native type */
switch (item->itemclass)
{
case ITEM_CLASS_ABSOLUTE:
result = convert_absolute_value(code, item);
break;
case ITEM_CLASS_RELATIVE:
result += convert_relative_value(code, item);
break;
case ITEM_CLASS_SWITCH:
result |= convert_switch_value(code, item);
break;
default:
break;
}
}
exit:
profiler_mark(PROFILER_END);
return result;
}
/*-------------------------------------------------
input_code_pressed - return non-zero if a
given input code has been pressed
-------------------------------------------------*/
int input_code_pressed(input_code code)
{
return (input_code_value(code) != 0);
}
/*-------------------------------------------------
input_code_pressed_once - return non-zero if a
given input code has transitioned from off to
on since the last call
-------------------------------------------------*/
int input_code_pressed_once(input_code code)
{
int curvalue = input_code_pressed(code);
int memnum, empty = -1;
/* look for the code in the memory */
for (memnum = 0; memnum < MAX_PRESSED_SWITCHES; memnum++)
{
/* were we previous pressed on the last time through here? */
if (code_pressed_memory[memnum] == code)
{
/* if no longer pressed, clear entry */
if (!curvalue)
code_pressed_memory[memnum] = INPUT_CODE_INVALID;
/* always return 0 */
return 0;
}
/* remember the first empty entry */
if (empty == -1 && code_pressed_memory[memnum] == INPUT_CODE_INVALID)
empty = memnum;
}
/* if we get here, we were not previously pressed; if still not pressed, return 0 */
if (!curvalue)
return 0;
/* otherwise, add ourself to the memory and return 1 */
assert(empty != -1);
if (empty != -1)
code_pressed_memory[empty] = code;
return 1;
}
/*-------------------------------------------------
input_code_from_input_item_id - translates
an input_item_id to an input_code
-------------------------------------------------*/
input_code input_code_from_input_item_id(input_item_id itemid)
{
input_device_class devclass;
/* iterate over device classes and devices */
for (devclass = DEVICE_CLASS_FIRST_VALID; devclass < DEVICE_CLASS_MAXIMUM; devclass++)
{
input_device_list *devlist = &device_list[devclass];
int devnum;
/* iterate over devices within each class */
for (devnum = 0; devnum < devlist->count; devnum++)
{
input_device *device = &devlist->list[devnum];
if (device->item[itemid] != NULL)
return device_item_to_code(device, itemid);
}
}
return 0;
}
/*-------------------------------------------------
input_code_poll_switches - poll for any input
-------------------------------------------------*/
input_code input_code_poll_switches(int reset)
{
input_device_class devclass;
/* if resetting memory, do it now */
if (reset)
code_pressed_memory_reset();
/* iterate over device classes and devices */
for (devclass = DEVICE_CLASS_FIRST_VALID; devclass < DEVICE_CLASS_MAXIMUM; devclass++)
{
input_device_list *devlist = &device_list[devclass];
int devnum;
/* iterate over devices within each class */
for (devnum = 0; devnum < devlist->count; devnum++)
{
input_device *device = &devlist->list[devnum];
input_item_id itemid;
/* iterate over items within each device */
for (itemid = ITEM_ID_FIRST_VALID; itemid <= device->maxitem; itemid++)
{
input_device_item *item = device->item[itemid];
if (item != NULL)
{
input_code code = device_item_to_code(device, itemid);
/* if the item is natively a switch, poll it */
if (item->itemclass == ITEM_CLASS_SWITCH)
{
if (input_code_pressed_once(code))
return code;
}
/* otherwise, poll axes digitally */
else
{
code = INPUT_CODE_SET_ITEMCLASS(code, ITEM_CLASS_SWITCH);
/* if this is a joystick X axis, check with left/right modifiers */
if (devclass == DEVICE_CLASS_JOYSTICK && INPUT_CODE_ITEMID(code) == ITEM_ID_XAXIS)
{
code = INPUT_CODE_SET_MODIFIER(code, ITEM_MODIFIER_LEFT);
if (input_code_pressed_once(code))
return code;
code = INPUT_CODE_SET_MODIFIER(code, ITEM_MODIFIER_RIGHT);
if (input_code_pressed_once(code))
return code;
}
/* if this is a joystick Y axis, check with up/down modifiers */
else if (devclass == DEVICE_CLASS_JOYSTICK && INPUT_CODE_ITEMID(code) == ITEM_ID_YAXIS)
{
code = INPUT_CODE_SET_MODIFIER(code, ITEM_MODIFIER_UP);
if (input_code_pressed_once(code))
return code;
code = INPUT_CODE_SET_MODIFIER(code, ITEM_MODIFIER_DOWN);
if (input_code_pressed_once(code))
return code;
}
/* any other axis, check with pos/neg modifiers */
else
{
code = INPUT_CODE_SET_MODIFIER(code, ITEM_MODIFIER_POS);
if (input_code_pressed_once(code))
return code;
code = INPUT_CODE_SET_MODIFIER(code, ITEM_MODIFIER_NEG);
if (input_code_pressed_once(code))
return code;
}
}
}
}
}
}
/* if nothing, return an invalid code */
return INPUT_CODE_INVALID;
}
/*-------------------------------------------------
input_code_poll_keyboard_switches - poll for
any keyboard-specific input
-------------------------------------------------*/
input_code input_code_poll_keyboard_switches(int reset)
{
input_device_list *devlist = &device_list[DEVICE_CLASS_KEYBOARD];
int devnum;
/* if resetting memory, do it now */
if (reset)
code_pressed_memory_reset();
/* iterate over devices within each class */
for (devnum = 0; devnum < devlist->count; devnum++)
{
input_device *device = &devlist->list[devnum];
input_item_id itemid;
/* iterate over items within each device */
for (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_item_to_code(device, itemid);
if (input_code_pressed_once(code))
return code;
}
}
}
/* if nothing, return an invalid code */
return INPUT_CODE_INVALID;
}
/*-------------------------------------------------
input_code_poll_axes - poll for any input
-------------------------------------------------*/
input_code input_code_poll_axes(int reset)
{
input_device_class devclass;
/* iterate over device classes and devices */
for (devclass = DEVICE_CLASS_FIRST_VALID; devclass < DEVICE_CLASS_MAXIMUM; devclass++)
{
input_device_list *devlist = &device_list[devclass];
int devnum;
/* iterate over devices within each class */
for (devnum = 0; devnum < devlist->count; devnum++)
{
input_device *device = &devlist->list[devnum];
input_item_id itemid;
/* iterate over items within each device */
for (itemid = ITEM_ID_FIRST_VALID; itemid <= device->maxitem; itemid++)
{
input_device_item *item = device->item[itemid];
if (item != NULL)
{
input_code code = device_item_to_code(device, itemid);
INT32 curval, diff;
/* skip any switches or already-reported axes */
if (item->itemclass == ITEM_CLASS_SWITCH)
continue;
/* poll the current value and reset the memory */
curval = input_code_value(code);
if (reset)
item->memory = curval;
/* if we've already reported this one, don't bother */
if (item->memory == INVALID_AXIS_VALUE)
continue;
/* compute the diff against memory */
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->memory = INVALID_AXIS_VALUE;
return code;
}
/* for relative axes, look for ~20 pixels movement */
if (item->itemclass == ITEM_CLASS_RELATIVE && diff > 20 * INPUT_RELATIVE_PER_PIXEL)
{
item->memory = INVALID_AXIS_VALUE;
return code;
}
}
}
}
}
/* if nothing, return an invalid code */
return INPUT_CODE_INVALID;
}
/***************************************************************************
STRINGS AND TOKENIZATION
***************************************************************************/
/*-------------------------------------------------
input_code_name - convert an input code into
a friendly name
-------------------------------------------------*/
astring *input_code_name(astring *string, input_code code)
{
input_device_item *item = input_code_item(code);
const char *devclass;
const char *devcode;
const char *modifier;
char devindex[10];
/* if nothing there, return an empty string */
if (item == NULL)
return astring_reset(string);
/* determine the devclass part */
devclass = code_to_string(devclass_string_table, INPUT_CODE_DEVCLASS(code));
/* determine the devindex part */
sprintf(devindex, "%d", INPUT_CODE_DEVINDEX(code) + 1);
/* if we're unifying all devices, don't display a number */
if (!device_list[INPUT_CODE_DEVCLASS(code)].multi)
devindex[0] = 0;
/* keyboard 0 doesn't show a class or index if it is the only one */
if (item->devclass == DEVICE_CLASS_KEYBOARD && device_list[DEVICE_CLASS_KEYBOARD].count == 1)
{
devclass = "";
devindex[0] = 0;
}
/* devcode part comes from the item name */
devcode = astring_c(item->name);
/* determine the modifier part */
modifier = code_to_string(modifier_string_table, INPUT_CODE_MODIFIER(code));
/* devcode is redundant with joystick switch left/right/up/down */
if (item->devclass == DEVICE_CLASS_JOYSTICK && INPUT_CODE_ITEMCLASS(code) == ITEM_CLASS_SWITCH)
if (INPUT_CODE_MODIFIER(code) >= ITEM_MODIFIER_LEFT && INPUT_CODE_MODIFIER(code) <= ITEM_MODIFIER_DOWN)
devcode = "";
/* concatenate the strings */
astring_cpyc(string, devclass);
if (devindex[0] != 0)
astring_catc(astring_catc(string, " "), devindex);
if (devcode[0] != 0)
astring_catc(astring_catc(string, " "), devcode);
if (modifier[0] != 0)
astring_catc(astring_catc(string, " "), modifier);
/* delete any leading spaces */
return astring_trimspace(string);
}
/*-------------------------------------------------
input_code_to_token - create a token for
a given code
-------------------------------------------------*/
astring *input_code_to_token(astring *string, input_code code)
{
input_device_item *item = input_code_item(code);
const char *devclass;
const char *devcode;
const char *itemclass;
const char *modifier;
char devindex[10];
/* determine the devclass part */
devclass = code_to_string(devclass_token_table, INPUT_CODE_DEVCLASS(code));
/* determine the devindex part; keyboard 0 doesn't show an index */
sprintf(devindex, "%d", INPUT_CODE_DEVINDEX(code) + 1);
if (INPUT_CODE_DEVCLASS(code) == DEVICE_CLASS_KEYBOARD && INPUT_CODE_DEVINDEX(code) == 0)
devindex[0] = 0;
/* determine the itemid part; look up in the table if we don't have a token */
if (item != NULL && item->token != NULL)
devcode = astring_c(item->token);
else
devcode = code_to_string(itemid_token_table, INPUT_CODE_ITEMID(code));
assert(devcode != NULL);
/* determine the modifier part */
modifier = code_to_string(modifier_token_table, INPUT_CODE_MODIFIER(code));
/* determine the itemclass part; if we match the native class, we don't include this */
if (item != NULL && item->itemclass == INPUT_CODE_ITEMCLASS(code))
itemclass = "";
else
itemclass = code_to_string(itemclass_token_table, INPUT_CODE_ITEMCLASS(code));
/* concatenate the strings */
astring_cpyc(string, devclass);
if (devindex[0] != 0)
astring_catc(astring_catc(string, "_"), devindex);
if (devcode[0] != 0)
astring_catc(astring_catc(string, "_"), devcode);
if (modifier[0] != 0)
astring_catc(astring_catc(string, "_"), modifier);
if (itemclass[0] != 0)
astring_catc(astring_catc(string, "_"), itemclass);
return string;
}
/*-------------------------------------------------
input_code_from_token - extract an input
code from a token
-------------------------------------------------*/
input_code input_code_from_token(const char *_token)
{
UINT32 devclass, itemid, devindex, modifier, standard;
UINT32 itemclass = ITEM_CLASS_INVALID;
input_code code = INPUT_CODE_INVALID;
astring *token[6] = { NULL };
int numtokens, curtok;
/* copy the token and break it into pieces */
for (numtokens = 0; numtokens < ARRAY_LENGTH(token); )
{
/* make a token up to the next underscore */
char *score = (char *)strchr(_token, '_');
token[numtokens++] = astring_dupch(_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 */
curtok = 0;
devclass = string_to_code(devclass_token_table, astring_c(token[curtok++]));
if (devclass == ~0)
goto exit;
/* second token might be index; look for number */
devindex = 0;
if (numtokens > 2 && sscanf(astring_c(token[curtok]), "%d", &devindex) == 1)
{
curtok++;
devindex--;
}
if (curtok >= numtokens)
goto exit;
/* next token is the item ID */
itemid = string_to_code(itemid_token_table, astring_c(token[curtok]));
standard = (itemid != ~0);
/* if we're a standard code, default the itemclass based on it */
if (standard)
itemclass = input_item_standard_class((input_device_class)devclass, (input_item_id)itemid);
/* otherwise, keep parsing */
else
{
input_device *device;
/* if this is an invalid device, we have nothing to look up */
if (devindex >= device_list[devclass].count)
goto exit;
device = &device_list[devclass].list[devindex];
/* 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 && item->token != NULL && astring_cmp(token[curtok], item->token) == 0)
{
/* take the itemclass from the item */
itemclass = item->itemclass;
break;
}
}
/* bail on fail */
if (itemid > device->maxitem)
goto exit;
}
curtok++;
/* if we have another token, it is probably a modifier */
modifier = ITEM_MODIFIER_NONE;
if (curtok < numtokens)
{
modifier = string_to_code(modifier_token_table, astring_c(token[curtok]));
if (modifier != ~0)
curtok++;
else
modifier = ITEM_MODIFIER_NONE;
}
/* if we have another token, it is the item class */
if (curtok < numtokens)
{
UINT32 temp = string_to_code(itemclass_token_table, astring_c(token[curtok]));
if (temp != ~0)
{
curtok++;
itemclass = temp;
}
}
/* we should have consumed all tokens */
if (curtok != numtokens)
goto exit;
/* assemble the final code */
code = INPUT_CODE(devclass, devindex, itemclass, modifier, itemid);
exit:
for (curtok = 0; curtok < ARRAY_LENGTH(token); curtok++)
if (token[curtok] != NULL)
astring_free(token[curtok]);
return code;
}
/***************************************************************************
INTERNAL FUNCTIONS
***************************************************************************/
/*-------------------------------------------------
input_code_item - get a pointer to the item
from the code
-------------------------------------------------*/
static input_device_item *input_code_item(input_code code)
{
input_device *device = input_code_device(code);
input_item_id itemid;
/* if no device, we fail */
if (device == NULL)
return NULL;
/* if the devcode is in range, return a pointer to the item */
itemid = INPUT_CODE_ITEMID(code);
if (itemid < ARRAY_LENGTH(device->item))
return device->item[itemid];
/* otherwise fail with NULL */
return NULL;
}
/*-------------------------------------------------
convert_absolute_value - convert an absolute
value into the class specified by code
-------------------------------------------------*/
static INT32 convert_absolute_value(input_code code, input_device_item *item)
{
/* make sure values are valid */
assert(item->current >= INPUT_ABSOLUTE_MIN && item->current <= INPUT_ABSOLUTE_MAX);
/* relative value conversion not supported */
if (INPUT_CODE_ITEMCLASS(code) == ITEM_CLASS_RELATIVE)
return 0;
/* if we want the absolute value, process it according to the modifier field */
else if (INPUT_CODE_ITEMCLASS(code) == ITEM_CLASS_ABSOLUTE)
{
input_item_modifier modifier = INPUT_CODE_MODIFIER(code);
INT32 result = apply_deadzone_and_saturation(code, item->current);
/* if we're doing a lightgun reload hack, override the value */
if (lightgun_reload_button && item->devclass == DEVICE_CLASS_LIGHTGUN)
{
input_device_item *button2_item = device_list[item->devclass].list[item->devindex].item[ITEM_ID_BUTTON2];
if (button2_item != NULL)
{
/* if it is pressed, return (min,max) */
input_item_update_value(button2_item);
if (button2_item->current)
result = (INPUT_CODE_ITEMID(code) == ITEM_ID_XAXIS) ? INPUT_ABSOLUTE_MIN : INPUT_ABSOLUTE_MAX;
}
}
/* standard axis: apply deadzone and saturation */
if (modifier == ITEM_MODIFIER_NONE)
return result;
/* positive/negative: scale to full axis */
else if (modifier == ITEM_MODIFIER_POS)
return MAX(result, 0) * 2 + INPUT_ABSOLUTE_MIN;
else if (modifier == ITEM_MODIFIER_NEG)
return MAX(-result, 0) * 2 + INPUT_ABSOLUTE_MIN;
}
/* if we want a switch value, process it according to the modifier field */
else if (INPUT_CODE_ITEMCLASS(code) == ITEM_CLASS_SWITCH)
{
input_item_modifier modifier = INPUT_CODE_MODIFIER(code);
/* left/right/up/down: if this is a joystick, fetch the paired X/Y axis values and convert */
if (modifier >= ITEM_MODIFIER_LEFT && modifier <= ITEM_MODIFIER_DOWN && item->devclass == DEVICE_CLASS_JOYSTICK)
{
input_device *device = &device_list[item->devclass].list[item->devindex];
input_device_item *xaxis_item = device->item[ITEM_ID_XAXIS];
input_device_item *yaxis_item = device->item[ITEM_ID_YAXIS];
if (xaxis_item != NULL && yaxis_item != NULL)
{
INT32 xaxisval, yaxisval;
UINT8 mapval;
/* determine which item we didn't update, and update it */
assert(item == xaxis_item || item == yaxis_item);
input_item_update_value((item == xaxis_item) ? yaxis_item : xaxis_item);
/* now map the X and Y axes to a 9x9 grid */
xaxisval = ((xaxis_item->current - INPUT_ABSOLUTE_MIN) * 9) / (INPUT_ABSOLUTE_MAX - INPUT_ABSOLUTE_MIN + 1);
yaxisval = ((yaxis_item->current - INPUT_ABSOLUTE_MIN) * 9) / (INPUT_ABSOLUTE_MAX - INPUT_ABSOLUTE_MIN + 1);
mapval = device->joymap.map[yaxisval][xaxisval];
/* handle stickiness */
if (mapval == JOYSTICK_MAP_STICKY)
mapval = device->lastmap;
else
device->lastmap = mapval;
/* return based on whether the appropriate bit is set */
return (mapval >> (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 (apply_deadzone_and_saturation(code, item->current) > 0);
else if (modifier == ITEM_MODIFIER_NEG || modifier == ITEM_MODIFIER_LEFT || modifier == ITEM_MODIFIER_UP)
return (apply_deadzone_and_saturation(code, item->current) < 0);
}
return 0;
}
/*-------------------------------------------------
convert_relative_value - convert a relative
value into the class specified by code
-------------------------------------------------*/
static INT32 convert_relative_value(input_code code, input_device_item *item)
{
/* if we want relative values, take it as-is */
if (INPUT_CODE_ITEMCLASS(code) == ITEM_CLASS_RELATIVE)
return item->current;
/* absolute value conversion not supported */
else if (INPUT_CODE_ITEMCLASS(code) == ITEM_CLASS_ABSOLUTE)
return 0;
/* if we want a switch value, process it according to the modifier field */
else if (INPUT_CODE_ITEMCLASS(code) == ITEM_CLASS_SWITCH)
{
input_item_modifier modifier = INPUT_CODE_MODIFIER(code);
/* positive/negative: TRUE if non-zero in either direction */
if (modifier == ITEM_MODIFIER_POS || modifier == ITEM_MODIFIER_RIGHT || modifier == ITEM_MODIFIER_DOWN)
return (item->current > 0);
else if (modifier == ITEM_MODIFIER_NEG || modifier == ITEM_MODIFIER_LEFT || modifier == ITEM_MODIFIER_UP)
return (item->current < 0);
}
return 0;
}
/*-------------------------------------------------
convert_switch_value - convert a switch
value into the class specified by code
-------------------------------------------------*/
static INT32 convert_switch_value(input_code code, input_device_item *item)
{
/* only a switch is supported */
if (INPUT_CODE_ITEMCLASS(code) == ITEM_CLASS_SWITCH)
{
/* if we're doing a lightgun reload hack, button 1 and 2 operate differently */
if (lightgun_reload_button && item->devclass == DEVICE_CLASS_LIGHTGUN)
{
/* button 1 is pressed if either button 1 or 2 are active */
if (INPUT_CODE_ITEMID(code) == ITEM_ID_BUTTON1)
{
input_device_item *button2_item = device_list[item->devclass].list[item->devindex].item[ITEM_ID_BUTTON2];
if (button2_item != NULL)
{
input_item_update_value(button2_item);
return item->current | button2_item->current;
}
}
/* button 2 is never officially pressed */
if (INPUT_CODE_ITEMID(code) == ITEM_ID_BUTTON2)
return 0;
}
/* steadykey for keyboards */
if (steadykey_enabled && item->devclass == DEVICE_CLASS_KEYBOARD)
return item->steadykey;
/* everything else is just the current value as-is */
return item->current;
}
return 0;
}
/*-------------------------------------------------
apply_deadzone_and_saturation - apply global
deadzone and saturation parameters to an
absolute value
-------------------------------------------------*/
static INT32 apply_deadzone_and_saturation(input_code code, INT32 result)
{
int negative = FALSE;
/* ignore if not a joystick */
if (INPUT_CODE_DEVCLASS(code) != DEVICE_CLASS_JOYSTICK)
return result;
/* properties are symmetric */
if (result < 0)
{
negative = TRUE;
result = -result;
}
/* if in the deadzone, return 0 */
if (result < joystick_deadzone)
result = 0;
/* if saturated, return the max */
else if (result > joystick_saturation)
result = INPUT_ABSOLUTE_MAX;
/* otherwise, scale */
else
result = (INT64)(result - joystick_deadzone) * (INT64)INPUT_ABSOLUTE_MAX / (INT64)(joystick_saturation - joystick_deadzone);
/* apply sign and return */
return negative ? -result : result;
}
/*-------------------------------------------------
joystick_map_parse - parse a string into
a joystick map
-------------------------------------------------*/
static int joystick_map_parse(const char *mapstring, joystick_map *map)
{
int rownum, colnum;
/* iterate over rows */
for (rownum = 0; rownum < 9; rownum++)
{
/* if we're done, copy from another row */
if (*mapstring == 0 || *mapstring == '.')
{
int symmetric = (rownum >= 5 && *mapstring == 0);
const UINT8 *srcrow = &map->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 (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);
map->map[rownum][colnum] = val;
}
}
/* otherwise, parse this column */
else
{
for (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 == '.'))
{
int symmetric = (colnum >= 5);
UINT8 val = map->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);
map->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;
map->map[rownum][colnum] = charmap[ptr - validchars];
}
}
}
/* if we ended with a period, advance to the next row */
if (*mapstring == '.')
mapstring++;
}
return TRUE;
}
/*-------------------------------------------------
joystick_map_print - print a joystick map via
the verbose output
-------------------------------------------------*/
static void joystick_map_print(const char *header, const char *origstring, const joystick_map *map)
{
int rownum, colnum;
mame_printf_verbose("%s = %s\n", header, origstring);
for (rownum = 0; rownum < 9; rownum++)
{
mame_printf_verbose(" ");
for (colnum = 0; colnum < 9; colnum++)
switch (map->map[rownum][colnum])
{
case JOYSTICK_MAP_UP | JOYSTICK_MAP_LEFT: mame_printf_verbose("7"); break;
case JOYSTICK_MAP_UP: mame_printf_verbose("8"); break;
case JOYSTICK_MAP_UP | JOYSTICK_MAP_RIGHT: mame_printf_verbose("9"); break;
case JOYSTICK_MAP_LEFT: mame_printf_verbose("4"); break;
case JOYSTICK_MAP_NEUTRAL: mame_printf_verbose("5"); break;
case JOYSTICK_MAP_RIGHT: mame_printf_verbose("6"); break;
case JOYSTICK_MAP_DOWN | JOYSTICK_MAP_LEFT: mame_printf_verbose("1"); break;
case JOYSTICK_MAP_DOWN: mame_printf_verbose("2"); break;
case JOYSTICK_MAP_DOWN | JOYSTICK_MAP_RIGHT:mame_printf_verbose("3"); break;
case JOYSTICK_MAP_STICKY: mame_printf_verbose("s"); break;
default: mame_printf_verbose("?"); break;
}
mame_printf_verbose("\n");
}
}