-emu/ioport.cpp: Fixed some default setting handling issues.

* Issues were unlikely to actually manifest unless you use controller
  configuration files to change specific system input defaults.

-src/emu/output.h: Added size accessor to multi-element output finder.

* std::size will work on the top rank of an outut finder now.  Sorry for
  hitting emu.h again so soon.

-lua: Exposed a couple more input related things.

* Exposed constructor and a few methods on input_seq required for
  scripts to properly clear assignments or restore default settings.
* Exposed ioport_manager::set_type_seq which is required to configure
  general input assignments properly.
* Removed unnecessary use of sol::overload in favour of optional
  parameters.
* Updated documentation and also fixed a few errors.

-docs: Added description for axis setting assignments.
This commit is contained in:
Vas Crabb 2021-11-04 03:11:29 +11:00
parent d29287e092
commit 13612bbe0f
7 changed files with 187 additions and 79 deletions

View File

@ -121,11 +121,11 @@ Properties
^^^^^^^^^^ ^^^^^^^^^^
t.is_zero (read-only) t.is_zero (read-only)
Whether the value represents no elapsed time. A Boolean indicating whether the value represents no elapsed time.
t.is_never (read-only) t.is_never (read-only)
Whether the value is greater than the maximum number of whole seconds that A Boolean indicating whether the value is greater than the maximum number of
can be represented (treated as an unreachable time in the future or whole seconds that can be represented (treated as an unreachable time in the
overflow). future or overflow).
t.attoseconds (read-only) t.attoseconds (read-only)
The fraction seconds portion of the interval in attoseconds. The fraction seconds portion of the interval in attoseconds.
t.seconds (read-only) t.seconds (read-only)
@ -517,7 +517,8 @@ ui:set_aggressive_input_focus(enable)
On some platforms, this controls whether MAME should accept input focus in On some platforms, this controls whether MAME should accept input focus in
more situations than when its windows have UI focus. more situations than when its windows have UI focus.
ui:get_general_input_setting(type, [player]) ui:get_general_input_setting(type, [player])
Gets a description of the configured input sequence for the specified input Gets a description of the configured
:ref:`input sequence <luareference-input-iptseq>` for the specified input
type and player suitable for using in prompts. The input type is an type and player suitable for using in prompts. The input type is an
enumerated value. The player number is a zero-based index. If the player enumerated value. The player number is a zero-based index. If the player
number is not supplied, it is assumed to be zero. number is not supplied, it is assumed to be zero.
@ -1660,14 +1661,21 @@ ioport:type_group(type, player)
This should be called with values obtained from I/O port fields to provide This should be called with values obtained from I/O port fields to provide
canonical grouping in an input configuration UI. canonical grouping in an input configuration UI.
ioport:type_seq(type, [player], [seqtype]) ioport:type_seq(type, [player], [seqtype])
Get the configured input sequence for the specified input type, player Get the configured :ref:`input sequence <luareference-input-iptseq>` for the
number and sequence type. The input type is an enumerated value. The specified input type, player number and sequence type. The input type is an
player number is a zero-based index. If the player number is not supplied, enumerated value. The player number is a zero-based index. If the player
it is assumed to be zero. If the sequence type is supplied, it must be number is not supplied, it is assumed to be zero. If the sequence type is
``"standard"``, ``"increment"`` or ``"decrement"``; if it is not supplied, supplied, it must be ``"standard"``, ``"increment"`` or ``"decrement"``; if
it is assumed to be ``"standard"``. it is not supplied, it is assumed to be ``"standard"``.
This provides access to general input configuration. This provides access to general input configuration.
ioport:set_type_seq(type, player, seqtype, seq)
Set the configured :ref:`input sequence <luareference-input-iptseq>` for the
specified input type, player number and sequence type. The input type is an
enumerated value. The player number is a zero-based index. The sequence
type must be ``"standard"``, ``"increment"`` or ``"decrement"``.
This allows general input configuration to be set.
ioport:token_to_input_type(string) ioport:token_to_input_type(string)
Returns the input type and player number for the specified input type token. Returns the input type and player number for the specified input type token.
ioport:input_type_to_token(type, [player]) ioport:input_type_to_token(type, [player])
@ -1852,21 +1860,24 @@ field:set_value(value)
compared to zero to determine whether the field should be active; for compared to zero to determine whether the field should be active; for
analog fields, the value must be right-aligned and in the correct range. analog fields, the value must be right-aligned and in the correct range.
field:set_input_seq(seqtype, seq) field:set_input_seq(seqtype, seq)
Set the input sequence for the specified sequence type. This is used to Set the :ref:`input sequence <luareference-input-iptseq>` for the
configure per-machine input settings. The sequence type must be specified sequence type. This is used to configure per-machine input
``"standard"``, ``"increment"`` or ``"decrement"``. settings. The sequence type must be ``"standard"``, ``"increment"`` or
``"decrement"``.
field:input_seq(seq_type) field:input_seq(seq_type)
Get the configured input sequence for the specified sequence type. This Get the configured :ref:`input sequence <luareference-input-iptseq>` for the
gets per-machine input settings. The sequence type must be ``"standard"``, specified sequence type. This gets per-machine input assignments. The
``"increment"`` or ``"decrement"``. sequence type must be ``"standard"``, ``"increment"`` or ``"decrement"``.
field:set_default_input_seq(seq_type, seq) field:set_default_input_seq(seq_type, seq)
Set the default input sequence for the specified sequence type. This is Set the default :ref:`input sequence <luareference-input-iptseq>` for the
used to configure general input settings. The sequence type must be specified sequence type. This overrides the default input assignment for a
``"standard"``, ``"increment"`` or ``"decrement"``. specific input. The sequence type must be ``"standard"``, ``"increment"``
or ``"decrement"``.
field:default_input_seq(seq_type) field:default_input_seq(seq_type)
Gets the default input sequence for the specified sequence type. This is Gets the default :ref:`input sequence <luareference-input-iptseq>` for the
gets general input settings. The sequence type must be ``"standard"``, specified sequence type. If the default assignment is not overridden, this
``"increment"`` or ``"decrement"``. gets the general input assignment. The sequence type must be
``"standard"``, ``"increment"`` or ``"decrement"``.
field:keyboard_codes(shift) field:keyboard_codes(shift)
Gets a table of characters corresponding to the field for the specified Gets a table of characters corresponding to the field for the specified
shift state. The shift state is a bit mask of active shift keys. shift state. The shift state is a bit mask of active shift keys.
@ -2009,18 +2020,20 @@ input:code_from_token(token)
Convert a token string to an input code. Returns the invalid input code if Convert a token string to an input code. Returns the invalid input code if
the token is not valid or belongs to an input device that is not present. the token is not valid or belongs to an input device that is not present.
input:seq_pressed(seq) input:seq_pressed(seq)
Returns a Boolen indicating whether the supplied input sequence is currently Returns a Boolean indicating whether the supplied
pressed. :ref:`input sequence <luareference-input-iptseq>` is currently pressed.
input:seq_clean(seq) input:seq_clean(seq)
Remove invalid elements from the supplied input sequence. Returns the new, Remove invalid elements from the supplied
cleaned input sequence. :ref:`input sequence <luareference-input-iptseq>`. Returns the new, cleaned
input sequence.
input:seq_name(seq) input:seq_name(seq)
Get display text for an inptu sequence. Get display text for an :ref:`input sequence <luareference-input-iptseq>`.
input:seq_to_tokens(seq) input:seq_to_tokens(seq)
Convert an input sequence to a token string. This should be used when Convert an :ref:`input sequence <luareference-input-iptseq>` to a token
saving configuration. string. This should be used when saving configuration.
input:seq_from_tokens(tokens) input:seq_from_tokens(tokens)
Convert a token string to an input sequence. This should be used when Convert a token string to an
:ref:`input sequence <luareference-input-iptseq>`. This should be used when
loading configuration. loading configuration.
input:axis_code_poller() input:axis_code_poller()
Returns an :ref:`input code poller <luareference-input-codepoll>` for Returns an :ref:`input code poller <luareference-input-codepoll>` for
@ -2034,10 +2047,12 @@ input:keyboard_code_poller()
devices. devices.
input:axis_sequence_poller() input:axis_sequence_poller()
Returns an :ref:`input sequence poller <luareference-input-seqpoll>` for Returns an :ref:`input sequence poller <luareference-input-seqpoll>` for
obtaining an input sequence for configuring an analog input. obtaining an :ref:`input sequence <luareference-input-iptseq>` for
configuring an analog input.
input:axis_sequence_poller() input:axis_sequence_poller()
Returns an :ref:`input sequence poller <luareference-input-seqpoll>` for Returns an :ref:`input sequence poller <luareference-input-seqpoll>` for
obtaining an input sequence for configuring a digital input. obtaining an :ref:`input sequence <luareference-input-iptseq>` for
configuring a digital input.
Properties Properties
^^^^^^^^^^ ^^^^^^^^^^
@ -2112,14 +2127,60 @@ Properties
^^^^^^^^^^ ^^^^^^^^^^
poller.sequence (read-only) poller.sequence (read-only)
The current input sequence. This is updated while polling. It is possible The current :ref:`input sequence <luareference-input-iptseq>`. This is
for the sequence to become invalid. updated while polling. It is possible for the sequence to become invalid.
poller.valid (read-only) poller.valid (read-only)
A Boolean indicating whether the current input sequence is valid. A Boolean indicating whether the current input sequence is valid.
poller.modified (read-only) poller.modified (read-only)
A Boolean indicating whether the sequence was changed by any user input A Boolean indicating whether the sequence was changed by any user input
since starting polling. since starting polling.
.. _luareference-input-iptseq:
Input sequence
~~~~~~~~~~~~~~
Wraps MAMEs ``input_seq`` class, representing a combination of host inputs that
can be read or assigned to an emulated input. Input sequences can be
manipulated using :ref:`input manager <luareference-input-inputman>` methods.
Use an :ref:`input sequence poller <luareference-input-seqpoll>` to obtain an
input sequence from the user.
Instantiation
^^^^^^^^^^^^^
emu.input_seq()
Creates an empty input sequence.
emu.input_seq(seq)
Creates a copy of an existing input sequence.
Methods
^^^^^^^
seq:reset()
Clears the input sequence, removing all items.
seq:set_default()
Sets the input sequence to a single item containing the metavalue specifying
that the default setting should be used.
Properties
^^^^^^^^^^
seq.empty (read-only)
A Boolean indicating whether the input sequence is empty (contains no items,
indicating an unassigned input).
seq.length (read-only)
The number of items in the input sequence.
seq.is_valid (read-only)
A Boolean indicating whether the input sequence is a valid. To be valid, it
must contain at least one item, all items must be valid codes, all product
groups must contain at least one item that is not negated, and items
referring to absolute and relative axes must not be mixed within a product
group.
seq.is_default (read-only)
A Boolean indicating whether the input sequence specifies that the default
setting should be used.
.. _luareference-input-devclass: .. _luareference-input-devclass:
Host input device class Host input device class

View File

@ -267,6 +267,47 @@ axis of the left analog stick on your controller, you *should not* assign either
the **Steering Wheel Analog Inc** or **Steering Wheel Analog Dec** setting to the **Steering Wheel Analog Inc** or **Steering Wheel Analog Dec** setting to
the X axis of the same analog stick. the X axis of the same analog stick.
You can assign one or more analog axes to the axis setting for an emulated
analog input. When multiple axes are assigned to an axis setting using **or**
operations, only the first axis that is not in the neutral position will take
effect. For example suppose for Atari Star Wars you assign the **AD Stick X
Analog** axis setting to **Joy 1 LSX or Joy 1 RSX** on an Xbox-style controller.
You will be able to control the emulated X axis using the X axis of the left
stick. If the left stick is in the neutral position (centred) on the X axis,
you will be able to control the emulated X axis using the X axis of the right
stick; however, if the left stick is *not* centred on the X axis, the X axis of
the right stick will be ignored.
MAME allows you to assign either the full range of an axis or the range on one
side of the neutral position (a *half axis*) to an axis setting. Assigning a
half axis is usually used for pedals or other absolute inputs where the neutral
position is at one end of the input range. For example suppose for **Ridge
Racer** you assign the **Brake Pedal Analog** setting to the portion of a
vertical joystick axis below the neutral position. If the joystick is at or
above the neutral position vertically, the brake pedal will be released; if the
joystick is below the neutral position vertically, the brake pedal will be
applied proportionally. Half axes are displayed as the name of the axis
followed by a plus or minus sign (**+** or **-**). Plus refers to the portion
of the axis below or to the right of the neutral position; minus refers to the
portion of the axis above or to the left of the neutral position. For pedal
or analog trigger controls, the active range is treated as being above the
neutral position (the half axis indicated by a minus sign).
When you select an axis setting, MAME will wait for you to enter an input:
* Move an analog control to assign it to the axis setting.
* When appending to a setting, move the last assigned analog control to cycle
between the full range of the axis and the portion of the axis on either side
of the neutral position.
* When appending to a setting, move an analog control other than the last
assigned control to add an **or** operation.
* Pressing **UI Cancel** (**Escape** by default) *before* activating an analog
axis input clears the setting or restores the default assignment.
* Pressing **UI Cancel** *after* activating analog axis input leaves the setting
unchanged.
* The new setting is shown below the menu. Wait one second after activating an
input to accept the new setting.
To adjust sensitivity, auto-centring speed and inversion settings for emulated To adjust sensitivity, auto-centring speed and inversion settings for emulated
analog inputs, or to see how they respond to your settings, select **Analog analog inputs, or to see how they respond to your settings, select **Analog
Controls** from the main menu during emulation. Settings for emulated analog Controls** from the main menu during emulation. Settings for emulated analog
@ -285,8 +326,9 @@ Each emulated input has four settings on the **Analog Controls** menu:
increment/decrement settings. increment/decrement settings.
* The *auto-centering speed* setting controls how fast the input value returns * The *auto-centering speed* setting controls how fast the input value returns
to the neutral state when the controls assigned to the increment/decrement to the neutral state when the controls assigned to the increment/decrement
settings are released. settings are released. Setting it to zero (**0**) will result in the value
* The **reverse** setting allows the direction of the emulated inputs response not automatically returning to the neutral position.
* The *reverse* setting allows the direction of the emulated inputs response
to controls to be inverted. This applies to controls assigned to the axis to controls to be inverted. This applies to controls assigned to the axis
setting *and* the increment/decrement settings. setting *and* the increment/decrement settings.
* The *sensitivity* setting adjusts the input values response to the control * The *sensitivity* setting adjusts the input values response to the control

View File

@ -469,7 +469,7 @@ bool input_seq::is_valid() const noexcept
if (lastcode.internal()) if (lastcode.internal())
return false; return false;
// if this is the end, we're ok // if this is the end, we're OK
if (code == end_code) if (code == end_code)
return true; return true;

View File

@ -705,16 +705,12 @@ const char *ioport_field::name() const
const input_seq &ioport_field::seq(input_seq_type seqtype) const noexcept const input_seq &ioport_field::seq(input_seq_type seqtype) const noexcept
{ {
// if no live state, return default // if the sequence is not the special default code, return it
if (!m_live) if (m_live && !m_live->seq[seqtype].is_default())
return defseq(seqtype); return m_live->seq[seqtype];
// if the sequence is the special default code, return the expanded default value // otherwise return the default sequence
if (m_live->seq[seqtype].is_default()) return defseq(seqtype);
return manager().type_seq(m_type, m_player, seqtype);
// otherwise, return the sequence as-is
return m_live->seq[seqtype];
} }
@ -741,14 +737,8 @@ const input_seq &ioport_field::defseq(input_seq_type seqtype) const noexcept
void ioport_field::set_defseq(input_seq_type seqtype, const input_seq &newseq) void ioport_field::set_defseq(input_seq_type seqtype, const input_seq &newseq)
{ {
const bool was_changed = seq(seqtype) != defseq(seqtype);
// set the new sequence // set the new sequence
m_seq[seqtype] = newseq; m_seq[seqtype] = newseq;
// also update live state unless previously customized
if (m_live && !was_changed)
m_live->seq[seqtype] = newseq;
} }

View File

@ -109,6 +109,7 @@ public:
auto &operator[](unsigned n) { return m_proxies[n]; } auto &operator[](unsigned n) { return m_proxies[n]; }
auto &operator[](unsigned n) const { return m_proxies[n]; } auto &operator[](unsigned n) const { return m_proxies[n]; }
auto size() const { return std::size(m_proxies); }
auto begin() { return std::begin(m_proxies); } auto begin() { return std::begin(m_proxies); }
auto end() { return std::end(m_proxies); } auto end() { return std::end(m_proxies); }
auto begin() const { return std::begin(m_proxies); } auto begin() const { return std::begin(m_proxies); }

View File

@ -448,31 +448,26 @@ template <typename T, size_t SIZE>
class lua_engine::enum_parser class lua_engine::enum_parser
{ {
public: public:
constexpr enum_parser(std::initializer_list<std::pair<const char *, T>> values) constexpr enum_parser(std::initializer_list<std::pair<std::string_view, T> > values)
{ {
if (values.size() != SIZE) if (values.size() != SIZE)
throw false && "size template argument incorrectly specified"; throw false && "size template argument incorrectly specified";
std::copy(values.begin(), values.end(), m_map.begin()); std::copy(values.begin(), values.end(), m_map.begin());
} }
T operator()(const char *text) const T operator()(std::string_view text) const
{ {
auto iter = std::find_if( auto iter = std::find_if(
m_map.begin() + 1, m_map.begin() + 1,
m_map.end(), m_map.end(),
[text](const auto &x) { return !strcmp(text, x.first); }); [&text] (const auto &x) { return text == x.first; });
if (iter == m_map.end()) if (iter == m_map.end())
iter = m_map.begin(); iter = m_map.begin();
return iter->second; return iter->second;
} }
T operator()(const std::string &text) const
{
return (*this)(text.c_str());
}
private: private:
std::array<std::pair<const char *, T>, SIZE> m_map; std::array<std::pair<std::string_view, T>, SIZE> m_map;
}; };

View File

@ -151,24 +151,32 @@ void lua_engine::initialize_input(sol::table &emu)
ioport_manager_type["type_group"] = sol::overload( ioport_manager_type["type_group"] = sol::overload(
&ioport_manager::type_group, &ioport_manager::type_group,
[] (ioport_manager &im, ioport_type type) { return im.type_group(type, 0); }); [] (ioport_manager &im, ioport_type type) { return im.type_group(type, 0); });
ioport_manager_type["type_seq"] = sol::overload( ioport_manager_type["type_seq"] =
[] (ioport_manager &im, ioport_type type, int player, char const *seq_type_string) [] (ioport_manager &im, ioport_type type, std::optional<int> player, std::optional<char const *> seq_type_string)
{ {
input_seq_type seq_type = s_seq_type_parser(seq_type_string); if (!player)
return im.type_seq(type, player, seq_type); player = 0;
}, input_seq_type seq_type = seq_type_string ? s_seq_type_parser(*seq_type_string) : SEQ_TYPE_STANDARD;
[] (ioport_manager &im, ioport_type type, int player) { return im.type_seq(type, player, SEQ_TYPE_STANDARD); }, return im.type_seq(type, *player, seq_type);
[] (ioport_manager &im, ioport_type type) { return im.type_seq(type, 0, SEQ_TYPE_STANDARD); }); };
ioport_manager_type["set_type_seq"] =
[] (ioport_manager &im, ioport_type type, std::optional<int> player, std::optional<char const *> seq_type_string, input_seq const &seq)
{
if (!player)
player = 0;
input_seq_type seq_type = seq_type_string ? s_seq_type_parser(*seq_type_string) : SEQ_TYPE_STANDARD;
im.set_type_seq(type, *player, seq_type, seq);
};
ioport_manager_type["token_to_input_type"] = ioport_manager_type["token_to_input_type"] =
[] (ioport_manager &im, std::string const &string) [] (ioport_manager &im, std::string const &string)
{ {
int player; int player;
ioport_type const type = im.token_to_input_type(string.c_str(), player); ioport_type const type = im.token_to_input_type(string.c_str(), player);
return std::make_tuple(type, player); return std::make_tuple(type, player);
}; };
ioport_manager_type["input_type_to_token"] = sol::overload( ioport_manager_type["input_type_to_token"] = sol::overload(
&ioport_manager::input_type_to_token, &ioport_manager::input_type_to_token,
[] (ioport_manager &im, ioport_type type) { return im.input_type_to_token(type, 0); }); [] (ioport_manager &im, ioport_type type) { return im.input_type_to_token(type, 0); });
ioport_manager_type["ports"] = sol::property([] (ioport_manager &im) { return tag_object_ptr_map<ioport_list>(im.ports()); }); ioport_manager_type["ports"] = sol::property([] (ioport_manager &im) { return tag_object_ptr_map<ioport_list>(im.ports()); });
@ -402,6 +410,17 @@ void lua_engine::initialize_input(sol::table &emu)
seqpoll_type["modified"] = sol::property(&input_sequence_poller::modified); seqpoll_type["modified"] = sol::property(&input_sequence_poller::modified);
auto iptseq_type = emu.new_usertype<input_seq>(
"input_seq",
sol::call_constructor, sol::constructors<input_seq(), input_seq(input_seq const &)>());
iptseq_type["reset"] = &input_seq::reset;
iptseq_type["set_default"] = &input_seq::set_default;
iptseq_type["empty"] = sol::property(&input_seq::empty);
iptseq_type["length"] = sol::property(&input_seq::length);
iptseq_type["is_valid"] = sol::property(&input_seq::is_valid);
iptseq_type["is_default"] = sol::property(&input_seq::is_default);
auto input_class_type = sol().registry().new_usertype<input_class>("input_class", sol::no_constructor); auto input_class_type = sol().registry().new_usertype<input_class>("input_class", sol::no_constructor);
input_class_type["name"] = sol::property(&input_class::name); input_class_type["name"] = sol::property(&input_class::name);
input_class_type["enabled"] = sol::property(&input_class::enabled); input_class_type["enabled"] = sol::property(&input_class::enabled);