mirror of
https://github.com/holub/mame
synced 2025-04-23 08:49:55 +03:00
UI input mapping menu updates:
* When a switch-type input is selected, show feedback when it's pressed * If an invalid code is entered (e.g. only negatives) abandon the change rather than cycling default/none * If an invalid code is entered display a message until the user takes some other action input.cpp updates: * constexpr crusade on input_code and input_seq and some very slight optimisation to input_seq * seq_poll* is a frontend function and had no business being in the core, so it's a utility class now * seq_poll* now exposes a bit more detail, enabling improved interaction on the UI inputs menu * global state is reduced a little, but the poll_* functions are still members of the input manager with global state (nw) The Lua engine has been updated in a way that maintains source compatibility with existing Lua scripts. This is less than ideal, but it minimises impact. Ideally someone (possibly me) will be able to expose the input sequence poller helper properly. I tested the changes with the cheat and autofire plugins and I was able to assign sequences. However I found two issues: it's seems impossible to assign a more complex sequence than a single key/button in the autofire plugin (i.e. no AND or NOT conditions, I confirmed this is pre-existing, not a regression), and in both the cheat and autofire plugins I found it a bit unwieldy trying to enter a complex sequence without live feedback of the sequence as it's built (this was also applicable to MAME's own input mapping menu until I added the live display yesterday).
This commit is contained in:
parent
f5d23f7036
commit
6c61aa61d2
@ -63,8 +63,10 @@ files {
|
||||
MAME_DIR .. "src/frontend/mame/cheat.h",
|
||||
MAME_DIR .. "src/frontend/mame/clifront.cpp",
|
||||
MAME_DIR .. "src/frontend/mame/clifront.h",
|
||||
MAME_DIR .. "src/frontend/mame/info.cpp",
|
||||
MAME_DIR .. "src/frontend/mame/info.h",
|
||||
MAME_DIR .. "src/frontend/mame/infoxml.cpp",
|
||||
MAME_DIR .. "src/frontend/mame/infoxml.h",
|
||||
MAME_DIR .. "src/frontend/mame/iptseqpoll.cpp",
|
||||
MAME_DIR .. "src/frontend/mame/iptseqpoll.h",
|
||||
MAME_DIR .. "src/frontend/mame/language.cpp",
|
||||
MAME_DIR .. "src/frontend/mame/language.h",
|
||||
MAME_DIR .. "src/frontend/mame/luaengine.cpp",
|
||||
|
@ -30,10 +30,10 @@
|
||||
const s32 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);
|
||||
constexpr input_code input_seq::end_code;
|
||||
constexpr input_code input_seq::default_code;
|
||||
constexpr input_code input_seq::not_code;
|
||||
constexpr input_code input_seq::or_code;
|
||||
|
||||
// constant sequences
|
||||
const input_seq input_seq::empty_seq;
|
||||
@ -377,14 +377,15 @@ static const code_string_table itemid_token_table[] =
|
||||
// input sequence
|
||||
//-------------------------------------------------
|
||||
|
||||
input_seq &input_seq::operator+=(input_code code)
|
||||
input_seq &input_seq::operator+=(input_code code) noexcept
|
||||
{
|
||||
// if not enough room, return false
|
||||
int curlength = length();
|
||||
if (curlength < ARRAY_LENGTH(m_code) - 1)
|
||||
const int curlength = length();
|
||||
if (curlength < m_code.size())
|
||||
{
|
||||
m_code[curlength++] = code;
|
||||
m_code[curlength] = end_code;
|
||||
m_code[curlength] = code;
|
||||
if ((curlength + 1) < m_code.size())
|
||||
m_code[curlength + 1] = end_code;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
@ -396,17 +397,25 @@ input_seq &input_seq::operator+=(input_code code)
|
||||
// before the new code
|
||||
//-------------------------------------------------
|
||||
|
||||
input_seq &input_seq::operator|=(input_code code)
|
||||
input_seq &input_seq::operator|=(input_code code) noexcept
|
||||
{
|
||||
// overwrite end/default with the new code
|
||||
if (m_code[0] == end_code || m_code[0] == default_code)
|
||||
if (m_code[0] == default_code)
|
||||
{
|
||||
m_code[0] = code;
|
||||
|
||||
// otherwise, append an OR token and then the new code
|
||||
m_code[1] = end_code;
|
||||
}
|
||||
else
|
||||
{
|
||||
*this += or_code;
|
||||
*this += code;
|
||||
// otherwise, append an OR token and then the new code
|
||||
const int curlength = length();
|
||||
if ((curlength + 1) < m_code.size())
|
||||
{
|
||||
m_code[curlength] = or_code;
|
||||
m_code[curlength + 1] = code;
|
||||
if ((curlength + 2) < m_code.size())
|
||||
m_code[curlength + 2] = end_code;
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
@ -416,13 +425,13 @@ input_seq &input_seq::operator|=(input_code code)
|
||||
// length - return the length of the sequence
|
||||
//-------------------------------------------------
|
||||
|
||||
int input_seq::length() const
|
||||
int input_seq::length() const noexcept
|
||||
{
|
||||
// find the end token; error if none found
|
||||
for (int seqnum = 0; seqnum < ARRAY_LENGTH(m_code); seqnum++)
|
||||
for (int seqnum = 0; seqnum < m_code.size(); seqnum++)
|
||||
if (m_code[seqnum] == end_code)
|
||||
return seqnum;
|
||||
return ARRAY_LENGTH(m_code);
|
||||
return m_code.size();
|
||||
}
|
||||
|
||||
|
||||
@ -431,17 +440,17 @@ int input_seq::length() const
|
||||
// valid
|
||||
//-------------------------------------------------
|
||||
|
||||
bool input_seq::is_valid() const
|
||||
bool input_seq::is_valid() const noexcept
|
||||
{
|
||||
// "default" can only be of length 1
|
||||
if (m_code[0] == default_code)
|
||||
return (length() == 1);
|
||||
return m_code[1] == end_code;
|
||||
|
||||
// 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 (auto code : m_code)
|
||||
for (input_code code : m_code)
|
||||
{
|
||||
// invalid codes are never permitted
|
||||
if (code == INPUT_CODE_INVALID)
|
||||
@ -451,7 +460,7 @@ bool input_seq::is_valid() const
|
||||
if (code == or_code || code == end_code)
|
||||
{
|
||||
// must be at least one positive code
|
||||
if (positive_code_count == 0)
|
||||
if (!positive_code_count)
|
||||
return false;
|
||||
|
||||
// last code must not have been an internal code
|
||||
@ -466,15 +475,12 @@ bool input_seq::is_valid() const
|
||||
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 we hit a NOT, make sure we don't have a double
|
||||
if (lastcode == not_code)
|
||||
return false;
|
||||
}
|
||||
|
||||
// anything else
|
||||
else
|
||||
{
|
||||
// count positive codes
|
||||
@ -501,33 +507,15 @@ bool input_seq::is_valid() const
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// 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()
|
||||
void input_seq::backspace() noexcept
|
||||
{
|
||||
// if we have at least one entry, remove it
|
||||
int curlength = length();
|
||||
const int curlength = length();
|
||||
if (curlength > 0)
|
||||
m_code[curlength - 1] = end_code;
|
||||
}
|
||||
@ -538,9 +526,9 @@ void input_seq::backspace()
|
||||
// with newcode in a sequence
|
||||
//-------------------------------------------------
|
||||
|
||||
void input_seq::replace(input_code oldcode, input_code newcode)
|
||||
void input_seq::replace(input_code oldcode, input_code newcode) noexcept
|
||||
{
|
||||
for (auto & elem : m_code)
|
||||
for (input_code &elem : m_code)
|
||||
if (elem == oldcode)
|
||||
elem = newcode;
|
||||
}
|
||||
@ -555,10 +543,7 @@ void input_seq::replace(input_code oldcode, input_code newcode)
|
||||
// input_manager - constructor
|
||||
//-------------------------------------------------
|
||||
|
||||
input_manager::input_manager(running_machine &machine)
|
||||
: m_machine(machine),
|
||||
m_poll_seq_last_ticks(0),
|
||||
m_poll_seq_class(ITEM_CLASS_SWITCH)
|
||||
input_manager::input_manager(running_machine &machine) : m_machine(machine)
|
||||
{
|
||||
// reset code memory
|
||||
reset_memory();
|
||||
@ -1338,129 +1323,6 @@ s32 input_manager::seq_axis_value(const input_seq &seq, input_item_class &itemcl
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// 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 != nullptr)
|
||||
{
|
||||
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()
|
||||
{
|
||||
const int curlen = m_poll_seq.length();
|
||||
input_code lastcode = m_poll_seq[curlen - 1];
|
||||
|
||||
input_code newcode;
|
||||
if (m_poll_seq_class == ITEM_CLASS_SWITCH)
|
||||
{
|
||||
// switch case: see if we have a new code to process
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// absolute/relative case: see if we have an analog change of sufficient amount
|
||||
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_clean - clean the sequence, removing
|
||||
// any invalid bits
|
||||
@ -1505,7 +1367,7 @@ std::string input_manager::seq_name(const input_seq &seq) const
|
||||
|
||||
// special case: empty
|
||||
if (cleaned_seq[0] == input_seq::end_code)
|
||||
return std::string((seq.length() == 0) ? "None" : "n/a");
|
||||
return std::string(seq.empty() ? "None" : "n/a");
|
||||
|
||||
// start with an empty buffer
|
||||
std::string str;
|
||||
|
131
src/emu/input.h
131
src/emu/input.h
@ -17,6 +17,10 @@
|
||||
#ifndef MAME_EMU_INPUT_H
|
||||
#define MAME_EMU_INPUT_H
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <iterator>
|
||||
|
||||
|
||||
//**************************************************************************
|
||||
// CONSTANTS
|
||||
@ -358,7 +362,12 @@ class input_code
|
||||
{
|
||||
public:
|
||||
// construction/destruction
|
||||
input_code(input_device_class devclass = DEVICE_CLASS_INVALID, int devindex = 0, input_item_class itemclass = ITEM_CLASS_INVALID, input_item_modifier modifier = ITEM_MODIFIER_NONE, input_item_id itemid = ITEM_ID_INVALID)
|
||||
constexpr input_code(
|
||||
input_device_class devclass = DEVICE_CLASS_INVALID,
|
||||
int devindex = 0,
|
||||
input_item_class itemclass = ITEM_CLASS_INVALID,
|
||||
input_item_modifier modifier = ITEM_MODIFIER_NONE,
|
||||
input_item_id itemid = ITEM_ID_INVALID) noexcept
|
||||
: m_internal(((devclass & 0xf) << 28) | ((devindex & 0xff) << 20) | ((itemclass & 0xf) << 16) | ((modifier & 0xf) << 12) | (itemid & 0xfff))
|
||||
{
|
||||
assert(devclass >= 0 && devclass < DEVICE_CLASS_MAXIMUM);
|
||||
@ -367,30 +376,48 @@ public:
|
||||
assert(modifier >= 0 && modifier < ITEM_MODIFIER_MAXIMUM);
|
||||
assert(itemid >= 0 && itemid < ITEM_ID_ABSOLUTE_MAXIMUM);
|
||||
}
|
||||
input_code(const input_code &src)
|
||||
: m_internal(src.m_internal) { }
|
||||
constexpr input_code(const input_code &src) noexcept = default;
|
||||
|
||||
// operators
|
||||
bool operator==(const input_code &rhs) const { return m_internal == rhs.m_internal; }
|
||||
bool operator!=(const input_code &rhs) const { return m_internal != rhs.m_internal; }
|
||||
constexpr bool operator==(const input_code &rhs) const noexcept { return m_internal == rhs.m_internal; }
|
||||
constexpr bool operator!=(const input_code &rhs) const noexcept { return m_internal != rhs.m_internal; }
|
||||
|
||||
// getters
|
||||
bool internal() const { return device_class() == DEVICE_CLASS_INTERNAL; }
|
||||
input_device_class device_class() const { return input_device_class((m_internal >> 28) & 0xf); }
|
||||
int device_index() const { return ((m_internal >> 20) & 0xff); }
|
||||
input_item_class item_class() const { return input_item_class((m_internal >> 16) & 0xf); }
|
||||
input_item_modifier item_modifier() const { return input_item_modifier((m_internal >> 12) & 0xf); }
|
||||
input_item_id item_id() const { return input_item_id(m_internal & 0xfff); }
|
||||
constexpr bool internal() const noexcept { return device_class() == DEVICE_CLASS_INTERNAL; }
|
||||
constexpr input_device_class device_class() const noexcept { return input_device_class((m_internal >> 28) & 0xf); }
|
||||
constexpr int device_index() const noexcept { return ((m_internal >> 20) & 0xff); }
|
||||
constexpr input_item_class item_class() const noexcept { return input_item_class((m_internal >> 16) & 0xf); }
|
||||
constexpr input_item_modifier item_modifier() const noexcept { return input_item_modifier((m_internal >> 12) & 0xf); }
|
||||
constexpr input_item_id item_id() const noexcept { return input_item_id(m_internal & 0xfff); }
|
||||
|
||||
// setters
|
||||
void set_device_class(input_device_class devclass) { assert(devclass >= 0 && devclass <= 0xf); m_internal = (m_internal & ~(0xf << 28)) | ((devclass & 0xf) << 28); }
|
||||
void set_device_index(int devindex) { assert(devindex >= 0 && devindex <= 0xff); m_internal = (m_internal & ~(0xff << 20)) | ((devindex & 0xff) << 20); }
|
||||
void set_item_class(input_item_class itemclass) { assert(itemclass >= 0 && itemclass <= 0xf); m_internal = (m_internal & ~(0xf << 16)) | ((itemclass & 0xf) << 16); }
|
||||
void set_item_modifier(input_item_modifier modifier) { assert(modifier >= 0 && modifier <= 0xf); m_internal = (m_internal & ~(0xf << 12)) | ((modifier & 0xf) << 12); }
|
||||
void set_item_id(input_item_id itemid) { assert(itemid >= 0 && itemid <= 0xfff); m_internal = (m_internal & ~0xfff) | (itemid & 0xfff); }
|
||||
void set_device_class(input_device_class devclass) noexcept
|
||||
{
|
||||
assert(devclass >= 0 && devclass <= 0xf);
|
||||
m_internal = (m_internal & ~(0xf << 28)) | ((devclass & 0xf) << 28);
|
||||
}
|
||||
void set_device_index(int devindex) noexcept
|
||||
{
|
||||
assert(devindex >= 0 && devindex <= 0xff);
|
||||
m_internal = (m_internal & ~(0xff << 20)) | ((devindex & 0xff) << 20);
|
||||
}
|
||||
void set_item_class(input_item_class itemclass) noexcept
|
||||
{
|
||||
assert(itemclass >= 0 && itemclass <= 0xf);
|
||||
m_internal = (m_internal & ~(0xf << 16)) | ((itemclass & 0xf) << 16);
|
||||
}
|
||||
void set_item_modifier(input_item_modifier modifier) noexcept
|
||||
{
|
||||
assert(modifier >= 0 && modifier <= 0xf);
|
||||
m_internal = (m_internal & ~(0xf << 12)) | ((modifier & 0xf) << 12);
|
||||
}
|
||||
void set_item_id(input_item_id itemid) noexcept
|
||||
{
|
||||
assert(itemid >= 0 && itemid <= 0xfff);
|
||||
m_internal = (m_internal & ~0xfff) | (itemid & 0xfff);
|
||||
}
|
||||
|
||||
private:
|
||||
// internal state
|
||||
u32 m_internal;
|
||||
};
|
||||
|
||||
@ -402,41 +429,59 @@ class input_seq
|
||||
{
|
||||
public:
|
||||
// construction/destruction
|
||||
input_seq(input_code code0 = input_seq::end_code, input_code code1 = input_seq::end_code, input_code code2 = input_seq::end_code, input_code code3 = input_seq::end_code, input_code code4 = input_seq::end_code, input_code code5 = input_seq::end_code, input_code code6 = input_seq::end_code)
|
||||
{ set(code0, code1, code2, code3, code4, code5, code6); }
|
||||
input_seq(const input_seq &rhs) { memcpy(m_code, rhs.m_code, sizeof(m_code)); }
|
||||
template <typename... T> constexpr input_seq(input_code code_0 = end_code, T... code_n) noexcept
|
||||
{
|
||||
set(code_0, code_n...);
|
||||
}
|
||||
constexpr input_seq(const input_seq &rhs) noexcept = default;
|
||||
|
||||
// operators
|
||||
bool operator==(const input_seq &rhs) const { return (memcmp(m_code, rhs.m_code, sizeof(m_code)) == 0); }
|
||||
bool operator!=(const input_seq &rhs) const { return (memcmp(m_code, rhs.m_code, sizeof(m_code)) != 0); }
|
||||
input_code operator[](int index) const { return (index >= 0 && index < ARRAY_LENGTH(m_code)) ? m_code[index] : input_seq::end_code; }
|
||||
input_seq &operator+=(input_code code);
|
||||
input_seq &operator|=(input_code code);
|
||||
bool operator==(const input_seq &rhs) const noexcept { return m_code == rhs.m_code; }
|
||||
bool operator!=(const input_seq &rhs) const noexcept { return m_code != rhs.m_code; }
|
||||
constexpr input_code operator[](int index) const noexcept { return (index >= 0 && index < m_code.size()) ? m_code[index] : end_code; }
|
||||
input_seq &operator+=(input_code code) noexcept;
|
||||
input_seq &operator|=(input_code code) noexcept;
|
||||
|
||||
// getters
|
||||
int length() const;
|
||||
bool is_valid() const;
|
||||
bool is_default() const { return m_code[0] == default_code; }
|
||||
constexpr bool empty() const noexcept { return m_code[0] == end_code; }
|
||||
constexpr int max_size() const noexcept { return std::tuple_size<decltype(m_code)>::value; }
|
||||
int length() const noexcept;
|
||||
bool is_valid() const noexcept;
|
||||
constexpr bool is_default() const noexcept { return m_code[0] == default_code; }
|
||||
|
||||
// setters
|
||||
void set(input_code code0 = input_seq::end_code, input_code code1 = input_seq::end_code, input_code code2 = input_seq::end_code, input_code code3 = input_seq::end_code, input_code code4 = input_seq::end_code, input_code code5 = input_seq::end_code, input_code code6 = input_seq::end_code);
|
||||
void reset() { set(); }
|
||||
void set_default() { set(default_code); }
|
||||
void backspace();
|
||||
void replace(input_code oldcode, input_code newcode);
|
||||
template <typename... T> void set(input_code code_0, T... code_n) noexcept
|
||||
{
|
||||
static_assert(sizeof...(T) < std::tuple_size<decltype(m_code)>::value, "too many codes for input_seq");
|
||||
set<0>(code_0, code_n...);
|
||||
}
|
||||
void reset() noexcept { set(end_code); }
|
||||
void set_default() noexcept { set(default_code); }
|
||||
void backspace() noexcept;
|
||||
void replace(input_code oldcode, input_code newcode) noexcept;
|
||||
|
||||
// constant codes used in sequences
|
||||
static const input_code end_code;
|
||||
static const input_code default_code;
|
||||
static const input_code not_code;
|
||||
static const input_code or_code;
|
||||
static constexpr input_code end_code { DEVICE_CLASS_INTERNAL, 0, ITEM_CLASS_INVALID, ITEM_MODIFIER_NONE, ITEM_ID_SEQ_END };
|
||||
static constexpr input_code default_code { DEVICE_CLASS_INTERNAL, 0, ITEM_CLASS_INVALID, ITEM_MODIFIER_NONE, ITEM_ID_SEQ_DEFAULT };
|
||||
static constexpr input_code not_code { DEVICE_CLASS_INTERNAL, 0, ITEM_CLASS_INVALID, ITEM_MODIFIER_NONE, ITEM_ID_SEQ_NOT };
|
||||
static constexpr input_code or_code { DEVICE_CLASS_INTERNAL, 0, ITEM_CLASS_INVALID, ITEM_MODIFIER_NONE, ITEM_ID_SEQ_OR };
|
||||
|
||||
// constant sequences
|
||||
static const input_seq empty_seq;
|
||||
|
||||
private:
|
||||
template <unsigned N> void set() noexcept
|
||||
{
|
||||
std::fill(std::next(m_code.begin(), N), m_code.end(), end_code);
|
||||
}
|
||||
template <unsigned N, typename... T> void set(input_code code_0, T... code_n) noexcept
|
||||
{
|
||||
m_code[N] = code_0;
|
||||
set<N + 1>(code_n...);
|
||||
}
|
||||
|
||||
// internal state
|
||||
input_code m_code[16];
|
||||
std::array<input_code, 16> m_code;
|
||||
};
|
||||
|
||||
|
||||
@ -478,11 +523,6 @@ public:
|
||||
bool seq_pressed(const input_seq &seq);
|
||||
s32 seq_axis_value(const input_seq &seq, input_item_class &itemclass);
|
||||
|
||||
// input sequence polling
|
||||
void seq_poll_start(input_item_class itemclass, const input_seq *startseq = nullptr);
|
||||
bool seq_poll();
|
||||
const input_seq &seq_poll_final() const { return m_poll_seq; }
|
||||
|
||||
// input sequence helpers
|
||||
input_seq seq_clean(const input_seq &seq) const;
|
||||
std::string seq_name(const input_seq &seq) const;
|
||||
@ -503,11 +543,6 @@ private:
|
||||
|
||||
// classes
|
||||
std::array<std::unique_ptr<input_class>, DEVICE_CLASS_MAXIMUM> m_class;
|
||||
|
||||
// sequence polling state
|
||||
input_seq m_poll_seq;
|
||||
osd_ticks_t m_poll_seq_last_ticks;
|
||||
input_item_class m_poll_seq_class;
|
||||
};
|
||||
|
||||
|
||||
|
@ -2133,7 +2133,7 @@ void ioport_manager::load_config(config_type cfg_type, util::xml::data_node cons
|
||||
if (seqtype != -1 && seqnode->get_value() != nullptr)
|
||||
{
|
||||
if (strcmp(seqnode->get_value(), "NONE") == 0)
|
||||
newseq[seqtype].set();
|
||||
newseq[seqtype].reset();
|
||||
else
|
||||
machine().input().seq_from_tokens(newseq[seqtype], seqnode->get_value());
|
||||
}
|
||||
|
@ -9,28 +9,31 @@
|
||||
***************************************************************************/
|
||||
|
||||
#include "emu.h"
|
||||
#include "luaengine.h"
|
||||
#include "mame.h"
|
||||
#include "chd.h"
|
||||
#include "emuopts.h"
|
||||
#include "mameopts.h"
|
||||
#include "audit.h"
|
||||
#include "info.h"
|
||||
#include "romload.h"
|
||||
#include "unzip.h"
|
||||
#include "validity.h"
|
||||
#include "sound/samples.h"
|
||||
#include "clifront.h"
|
||||
#include "xmlfile.h"
|
||||
#include "media_ident.h"
|
||||
|
||||
#include "osdepend.h"
|
||||
#include "softlist_dev.h"
|
||||
|
||||
#include "ui/moptions.h"
|
||||
|
||||
#include "audit.h"
|
||||
#include "infoxml.h"
|
||||
#include "language.h"
|
||||
#include "luaengine.h"
|
||||
#include "mame.h"
|
||||
#include "media_ident.h"
|
||||
#include "pluginopts.h"
|
||||
|
||||
#include "emuopts.h"
|
||||
#include "mameopts.h"
|
||||
#include "romload.h"
|
||||
#include "softlist_dev.h"
|
||||
#include "validity.h"
|
||||
#include "sound/samples.h"
|
||||
|
||||
#include "chd.h"
|
||||
#include "unzip.h"
|
||||
#include "xmlfile.h"
|
||||
|
||||
#include "osdepend.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <new>
|
||||
#include <ctype.h>
|
||||
|
@ -9,8 +9,8 @@
|
||||
***************************************************************************/
|
||||
|
||||
#include "emu.h"
|
||||
#include "infoxml.h"
|
||||
|
||||
#include "info.h"
|
||||
#include "mameopts.h"
|
||||
|
||||
#include "machine/ram.h"
|
@ -8,19 +8,17 @@
|
||||
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef MAME_FRONTEND_MAME_INFO_H
|
||||
#define MAME_FRONTEND_MAME_INFO_H
|
||||
#ifndef MAME_FRONTEND_MAME_INFOXML_H
|
||||
#define MAME_FRONTEND_MAME_INFOXML_H
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "emuopts.h"
|
||||
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
|
||||
|
||||
class driver_enumerator;
|
||||
|
||||
|
||||
//**************************************************************************
|
||||
// FUNCTION PROTOTYPES
|
||||
//**************************************************************************
|
||||
@ -42,4 +40,4 @@ private:
|
||||
bool m_dtd;
|
||||
};
|
||||
|
||||
#endif // MAME_FRONTEND_MAME_INFO_H
|
||||
#endif // MAME_FRONTEND_MAME_INFOXML_H
|
144
src/frontend/mame/iptseqpoll.cpp
Normal file
144
src/frontend/mame/iptseqpoll.cpp
Normal file
@ -0,0 +1,144 @@
|
||||
// license:BSD-3-Clause
|
||||
// copyright-holders:Vas Crabb,Aaron Giles
|
||||
|
||||
#include "emu.h"
|
||||
#include "iptseqpoll.h"
|
||||
|
||||
#include <cassert>
|
||||
|
||||
|
||||
input_sequence_poller::input_sequence_poller(input_manager &manager) noexcept :
|
||||
m_manager(manager),
|
||||
m_sequence(),
|
||||
m_class(ITEM_CLASS_INVALID),
|
||||
m_last_ticks(0),
|
||||
m_modified(false)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void input_sequence_poller::start(input_item_class itemclass)
|
||||
{
|
||||
m_sequence.reset();
|
||||
do_start(itemclass);
|
||||
}
|
||||
|
||||
|
||||
void input_sequence_poller::start(input_item_class itemclass, input_seq const &startseq)
|
||||
{
|
||||
// grab the starting sequence to append to, and append an OR if it isn't empty
|
||||
m_sequence = startseq;
|
||||
if (input_seq::end_code != m_sequence[0])
|
||||
m_sequence += input_seq::or_code;
|
||||
|
||||
do_start(itemclass);
|
||||
}
|
||||
|
||||
|
||||
bool input_sequence_poller::poll()
|
||||
{
|
||||
assert((ITEM_CLASS_SWITCH == m_class) || (ITEM_CLASS_ABSOLUTE == m_class) || (ITEM_CLASS_RELATIVE == m_class));
|
||||
int const curlen = m_sequence.length();
|
||||
input_code lastcode = m_sequence[curlen - 1];
|
||||
|
||||
input_code newcode;
|
||||
if (ITEM_CLASS_SWITCH == m_class)
|
||||
{
|
||||
// switch case: see if we have a new code to process
|
||||
newcode = m_manager.poll_switches();
|
||||
if (INPUT_CODE_INVALID != newcode)
|
||||
{
|
||||
// if code is duplicate, toggle the NOT state on the code
|
||||
if (curlen && (newcode == lastcode))
|
||||
{
|
||||
// back up over the existing code
|
||||
m_sequence.backspace();
|
||||
|
||||
// if there was a NOT preceding it, delete it as well, otherwise append a fresh one
|
||||
if (m_sequence[curlen - 2] == input_seq::not_code)
|
||||
m_sequence.backspace();
|
||||
else
|
||||
m_sequence += input_seq::not_code;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// absolute/relative case: see if we have an analog change of sufficient amount
|
||||
bool const has_or = input_seq::or_code == lastcode;
|
||||
if (has_or)
|
||||
lastcode = m_sequence[curlen - 2];
|
||||
newcode = m_manager.poll_axes();
|
||||
|
||||
// if the last code doesn't match absolute/relative of this code, ignore the new one
|
||||
input_item_class const lastclass = lastcode.item_class();
|
||||
input_item_class const newclass = newcode.item_class();
|
||||
if (((ITEM_CLASS_ABSOLUTE == lastclass) && (ITEM_CLASS_ABSOLUTE != newclass)) ||
|
||||
((ITEM_CLASS_RELATIVE == lastclass) && (ITEM_CLASS_RELATIVE != newclass)))
|
||||
newcode = INPUT_CODE_INVALID;
|
||||
|
||||
// if the new code is valid, check for half-axis toggles on absolute controls
|
||||
if ((INPUT_CODE_INVALID != newcode) && curlen && (ITEM_CLASS_ABSOLUTE == newclass))
|
||||
{
|
||||
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_sequence.backspace();
|
||||
m_sequence.backspace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if we got a new code to append it, append it and reset the timer
|
||||
osd_ticks_t const newticks = osd_ticks();
|
||||
if (INPUT_CODE_INVALID != newcode)
|
||||
{
|
||||
m_sequence += newcode;
|
||||
m_last_ticks = newticks;
|
||||
m_modified = true;
|
||||
}
|
||||
|
||||
// if we're recorded at least one item and 2/3 of a second has passed, we're done
|
||||
if (m_last_ticks && ((m_last_ticks + (osd_ticks_per_second() * 2 / 3)) < newticks))
|
||||
{
|
||||
m_class = ITEM_CLASS_INVALID;
|
||||
return true;
|
||||
}
|
||||
|
||||
// return false to indicate we are still polling
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void input_sequence_poller::do_start(input_item_class itemclass)
|
||||
{
|
||||
assert((ITEM_CLASS_SWITCH == itemclass) || (ITEM_CLASS_ABSOLUTE == itemclass) || (ITEM_CLASS_RELATIVE == itemclass));
|
||||
|
||||
// reset the recording count and the clock
|
||||
m_class = itemclass;
|
||||
m_last_ticks = 0;
|
||||
m_modified = false;
|
||||
|
||||
// wait for any inputs that are already active to be released
|
||||
m_manager.reset_polling();
|
||||
for (input_code dummycode = KEYCODE_ENTER; INPUT_CODE_INVALID != dummycode; )
|
||||
dummycode = (ITEM_CLASS_SWITCH == itemclass) ? m_manager.poll_switches() : m_manager.poll_axes();
|
||||
}
|
39
src/frontend/mame/iptseqpoll.h
Normal file
39
src/frontend/mame/iptseqpoll.h
Normal file
@ -0,0 +1,39 @@
|
||||
// license:BSD-3-Clause
|
||||
// copyright-holders:Vas Crabb,Aaron Giles
|
||||
/***************************************************************************
|
||||
|
||||
iptseqpoll.h
|
||||
|
||||
Helper for letting the user select input sequences.
|
||||
|
||||
***************************************************************************/
|
||||
#ifndef MAME_FRONTEND_IPTSEQPOLL_H
|
||||
#define MAME_FRONTEND_IPTSEQPOLL_H
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
class input_sequence_poller
|
||||
{
|
||||
public:
|
||||
input_sequence_poller(input_manager &manager) noexcept;
|
||||
|
||||
void start(input_item_class itemclass);
|
||||
void start(input_item_class itemclass, input_seq const &startseq);
|
||||
bool poll();
|
||||
|
||||
input_seq const &sequence() const noexcept { return m_sequence; }
|
||||
bool valid() const noexcept { return m_sequence.is_valid(); }
|
||||
bool modified() const noexcept { return m_modified; }
|
||||
|
||||
private:
|
||||
void do_start(input_item_class itemclass);
|
||||
|
||||
input_manager &m_manager;
|
||||
input_seq m_sequence;
|
||||
input_item_class m_class;
|
||||
osd_ticks_t m_last_ticks;
|
||||
bool m_modified;
|
||||
};
|
||||
|
||||
#endif // MAME_FRONTEND_IPTSEQPOLL_H
|
@ -624,6 +624,13 @@ bool lua_engine::menu_callback(const std::string &menu, int index, const std::st
|
||||
return ret;
|
||||
}
|
||||
|
||||
void lua_engine::set_machine(running_machine *machine)
|
||||
{
|
||||
if (!machine || (machine != m_machine))
|
||||
m_seq_poll.reset();
|
||||
m_machine = machine;
|
||||
}
|
||||
|
||||
int lua_engine::enumerate_functions(const char *id, std::function<bool(const sol::protected_function &func)> &&callback)
|
||||
{
|
||||
int count = 0;
|
||||
@ -2085,7 +2092,10 @@ void lua_engine::initialize()
|
||||
input_type.set("seq_to_tokens", [](input_manager &input, sol::user<input_seq> seq) { return input.seq_to_tokens(seq); });
|
||||
input_type.set("seq_name", [](input_manager &input, sol::user<input_seq> seq) { return input.seq_name(seq); });
|
||||
input_type.set("seq_clean", [](input_manager &input, sol::user<input_seq> seq) { input_seq cleaned_seq = input.seq_clean(seq); return sol::make_user(cleaned_seq); });
|
||||
input_type.set("seq_poll_start", [](input_manager &input, const char *cls_string, sol::object seq) {
|
||||
input_type.set("seq_poll_start", [this](input_manager &input, const char *cls_string, sol::object seq) {
|
||||
if (!m_seq_poll)
|
||||
m_seq_poll.reset(new input_sequence_poller(input));
|
||||
|
||||
input_item_class cls;
|
||||
if (!strcmp(cls_string, "switch"))
|
||||
cls = ITEM_CLASS_SWITCH;
|
||||
@ -2098,13 +2108,17 @@ void lua_engine::initialize()
|
||||
else
|
||||
cls = ITEM_CLASS_INVALID;
|
||||
|
||||
input_seq *start = nullptr;
|
||||
if(seq.is<sol::user<input_seq>>())
|
||||
start = &seq.as<sol::user<input_seq>>();
|
||||
input.seq_poll_start(cls, start);
|
||||
if (seq.is<sol::user<input_seq>>())
|
||||
m_seq_poll->start(cls, seq.as<sol::user<input_seq>>());
|
||||
else
|
||||
m_seq_poll->start(cls);
|
||||
});
|
||||
input_type.set("seq_poll", [this](input_manager &input) {
|
||||
return m_seq_poll->poll();
|
||||
});
|
||||
input_type.set("seq_poll_final", [this](input_manager &input) {
|
||||
return sol::make_user(m_seq_poll->valid() ? m_seq_poll->sequence() : input_seq());
|
||||
});
|
||||
input_type.set("seq_poll", &input_manager::seq_poll);
|
||||
input_type.set("seq_poll_final", [](input_manager &input) { return sol::make_user(input.seq_poll_final()); });
|
||||
input_type.set("device_classes", sol::property([this](input_manager &input) {
|
||||
sol::table result = sol().create_table();
|
||||
for (input_device_class devclass_id = DEVICE_CLASS_FIRST_VALID; devclass_id <= DEVICE_CLASS_LAST_VALID; devclass_id++)
|
||||
|
@ -7,22 +7,21 @@
|
||||
Controls execution of the core MAME system.
|
||||
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef MAME_FRONTEND_MAME_LUAENGINE_H
|
||||
#define MAME_FRONTEND_MAME_LUAENGINE_H
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef __EMU_H__
|
||||
#error Dont include this file directly; include emu.h instead.
|
||||
#endif
|
||||
#include "iptseqpoll.h"
|
||||
|
||||
#include <condition_variable>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
|
||||
#if defined(__GNUC__) && (__GNUC__ > 6)
|
||||
#pragma GCC diagnostic ignored "-Wnoexcept-type"
|
||||
#endif
|
||||
|
||||
#include <map>
|
||||
#include <condition_variable>
|
||||
#define SOL_SAFE_USERTYPE
|
||||
//#define SOL_CHECK_ARGUMENTS
|
||||
#include "sol2/sol.hpp"
|
||||
@ -45,7 +44,7 @@ public:
|
||||
void menu_populate(const std::string &menu, std::vector<std::tuple<std::string, std::string, std::string>> &menu_list);
|
||||
bool menu_callback(const std::string &menu, int index, const std::string &event);
|
||||
|
||||
void set_machine(running_machine *machine) { m_machine = machine; }
|
||||
void set_machine(running_machine *machine);
|
||||
std::vector<std::string> &get_menu() { return m_menu; }
|
||||
void attach_notifiers();
|
||||
void on_frame_done();
|
||||
@ -110,11 +109,13 @@ public:
|
||||
}
|
||||
|
||||
sol::state_view &sol() const { return *m_sol_state; }
|
||||
|
||||
private:
|
||||
// internal state
|
||||
lua_State *m_lua_state;
|
||||
std::unique_ptr<sol::state_view> m_sol_state;
|
||||
running_machine *m_machine;
|
||||
std::unique_ptr<input_sequence_poller> m_seq_poll;
|
||||
|
||||
std::vector<std::string> m_menu;
|
||||
|
||||
|
@ -10,7 +10,9 @@
|
||||
#ifndef MAME_FRONTEND_MEDIA_IDENT_H
|
||||
#define MAME_FRONTEND_MEDIA_IDENT_H
|
||||
|
||||
#include "drivenum.h"
|
||||
#include "romload.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
|
||||
|
@ -239,6 +239,9 @@ menu_input::menu_input(mame_ui_manager &mui, render_container &container)
|
||||
: menu(mui, container)
|
||||
, data()
|
||||
, pollingitem(nullptr)
|
||||
, seq_poll(machine().input())
|
||||
, errormsg()
|
||||
, erroritem(nullptr)
|
||||
, lastitem(nullptr)
|
||||
, record_next(false)
|
||||
{
|
||||
@ -255,12 +258,9 @@ menu_input::~menu_input()
|
||||
|
||||
void menu_input::toggle_none_default(input_seq &selected_seq, input_seq &original_seq, const input_seq &selected_defseq)
|
||||
{
|
||||
/* if we used to be "none", toggle to the default value */
|
||||
if (original_seq.length() == 0)
|
||||
if (original_seq.empty()) // if we used to be "none", toggle to the default value
|
||||
selected_seq = selected_defseq;
|
||||
|
||||
/* otherwise, toggle to "none" */
|
||||
else
|
||||
else // otherwise, toggle to "none"
|
||||
selected_seq.reset();
|
||||
}
|
||||
|
||||
@ -268,8 +268,7 @@ void menu_input::custom_render(void *selectedref, float top, float bottom, float
|
||||
{
|
||||
if (pollingitem)
|
||||
{
|
||||
const std::string seqname = machine().input().seq_name(
|
||||
machine().input().seq_poll_final()); // relying on the fact that this exposes the sequence polled so far and has no side effects
|
||||
const std::string seqname = machine().input().seq_name(seq_poll.sequence());
|
||||
char const *const text[] = { seqname.c_str() };
|
||||
draw_text_box(
|
||||
std::begin(text), std::end(text),
|
||||
@ -277,6 +276,37 @@ void menu_input::custom_render(void *selectedref, float top, float bottom, float
|
||||
ui::text_layout::CENTER, ui::text_layout::NEVER, false,
|
||||
ui().colors().text_color(), ui().colors().background_color(), 1.0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (erroritem && (selectedref != erroritem))
|
||||
{
|
||||
errormsg.clear();
|
||||
erroritem = nullptr;
|
||||
}
|
||||
|
||||
if (erroritem)
|
||||
{
|
||||
char const *const text[] = { errormsg.c_str() };
|
||||
draw_text_box(
|
||||
std::begin(text), std::end(text),
|
||||
x1, x2, y2 + ui().box_tb_border(), y2 + bottom,
|
||||
ui::text_layout::CENTER, ui::text_layout::NEVER, false,
|
||||
ui().colors().text_color(), UI_RED_COLOR, 1.0f);
|
||||
}
|
||||
else if (selectedref)
|
||||
{
|
||||
const input_item_data &item = *reinterpret_cast<input_item_data *>(selectedref);
|
||||
if ((INPUT_TYPE_ANALOG != item.type) && machine().input().seq_pressed(item.seq))
|
||||
{
|
||||
char const *const text[] = { _("Pressed") };
|
||||
draw_text_box(
|
||||
std::begin(text), std::end(text),
|
||||
x1, x2, y2 + ui().box_tb_border(), y2 + bottom,
|
||||
ui::text_layout::CENTER, ui::text_layout::NEVER, false,
|
||||
ui().colors().text_color(), ui().colors().background_color(), 1.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void menu_input::handle()
|
||||
@ -289,58 +319,82 @@ void menu_input::handle()
|
||||
if (pollingitem)
|
||||
{
|
||||
// if we are polling, handle as a special case
|
||||
input_item_data *item = pollingitem;
|
||||
input_item_data *const item = pollingitem;
|
||||
|
||||
if (machine().ui_input().pressed(IPT_UI_CANCEL))
|
||||
{
|
||||
// if UI_CANCEL is pressed, abort
|
||||
pollingitem = nullptr;
|
||||
if (machine().input().seq_poll_final() == init_poll_seq)
|
||||
if (!seq_poll.modified())
|
||||
{
|
||||
// cancelled immediately - toggle between default and none
|
||||
record_next = false;
|
||||
toggle_none_default(item->seq, starting_seq, *item->defseq);
|
||||
seqchangeditem = item;
|
||||
}
|
||||
else
|
||||
{
|
||||
// entered something before cancelling - abandon change
|
||||
invalidate = true;
|
||||
}
|
||||
}
|
||||
else if (machine().input().seq_poll())
|
||||
else if (seq_poll.poll()) // poll again; if finished, update the sequence
|
||||
{
|
||||
// poll again; if finished, update the sequence
|
||||
pollingitem = nullptr;
|
||||
record_next = true;
|
||||
item->seq = machine().input().seq_poll_final();
|
||||
seqchangeditem = item;
|
||||
if (seq_poll.valid())
|
||||
{
|
||||
record_next = true;
|
||||
item->seq = seq_poll.sequence();
|
||||
seqchangeditem = item;
|
||||
}
|
||||
else
|
||||
{
|
||||
// entered invalid sequence - abandon change
|
||||
invalidate = true;
|
||||
errormsg = _("Invalid sequence entered");
|
||||
erroritem = item;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (menu_event != nullptr && menu_event->itemref != nullptr)
|
||||
else if (menu_event && menu_event->itemref)
|
||||
{
|
||||
// otherwise, handle the events
|
||||
input_item_data *item = (input_item_data *)menu_event->itemref;
|
||||
input_item_data &item = *reinterpret_cast<input_item_data *>(menu_event->itemref);
|
||||
switch (menu_event->iptkey)
|
||||
{
|
||||
case IPT_UI_SELECT: // an item was selected: begin polling
|
||||
pollingitem = item;
|
||||
lastitem = item;
|
||||
starting_seq = item->seq;
|
||||
machine().input().seq_poll_start((item->type == INPUT_TYPE_ANALOG) ? ITEM_CLASS_ABSOLUTE : ITEM_CLASS_SWITCH, record_next ? &item->seq : nullptr);
|
||||
init_poll_seq = machine().input().seq_poll_final();
|
||||
errormsg.clear();
|
||||
erroritem = nullptr;
|
||||
pollingitem = &item;
|
||||
lastitem = &item;
|
||||
starting_seq = item.seq;
|
||||
if (record_next)
|
||||
seq_poll.start((item.type == INPUT_TYPE_ANALOG) ? ITEM_CLASS_ABSOLUTE : ITEM_CLASS_SWITCH, item.seq);
|
||||
else
|
||||
seq_poll.start((item.type == INPUT_TYPE_ANALOG) ? ITEM_CLASS_ABSOLUTE : ITEM_CLASS_SWITCH);
|
||||
invalidate = true;
|
||||
break;
|
||||
|
||||
case IPT_UI_CLEAR: // if the clear key was pressed, reset the selected item
|
||||
toggle_none_default(item->seq, item->seq, *item->defseq);
|
||||
errormsg.clear();
|
||||
erroritem = nullptr;
|
||||
toggle_none_default(item.seq, item.seq, *item.defseq);
|
||||
record_next = false;
|
||||
seqchangeditem = item;
|
||||
seqchangeditem = &item;
|
||||
break;
|
||||
}
|
||||
|
||||
// if the selection changed, reset the "record next" flag
|
||||
if (item != lastitem)
|
||||
if (&item != lastitem)
|
||||
{
|
||||
if (erroritem)
|
||||
{
|
||||
errormsg.clear();
|
||||
erroritem = nullptr;
|
||||
}
|
||||
record_next = false;
|
||||
lastitem = item;
|
||||
lastitem = &item;
|
||||
}
|
||||
}
|
||||
|
||||
// if the sequence changed, update it
|
||||
|
@ -13,7 +13,9 @@
|
||||
#pragma once
|
||||
|
||||
#include "ui/menu.h"
|
||||
#include "iptseqpoll.h"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
|
||||
@ -30,6 +32,7 @@ private:
|
||||
virtual void handle() override;
|
||||
};
|
||||
|
||||
|
||||
class menu_input : public menu
|
||||
{
|
||||
public:
|
||||
@ -63,20 +66,23 @@ protected:
|
||||
void populate_sorted(float &customtop, float &custombottom);
|
||||
void toggle_none_default(input_seq &selected_seq, input_seq &original_seq, const input_seq &selected_defseq);
|
||||
|
||||
data_vector data;
|
||||
input_item_data * pollingitem;
|
||||
data_vector data;
|
||||
input_item_data *pollingitem;
|
||||
|
||||
private:
|
||||
input_item_data * lastitem;
|
||||
bool record_next;
|
||||
input_seq starting_seq;
|
||||
input_seq init_poll_seq;
|
||||
input_sequence_poller seq_poll;
|
||||
std::string errormsg;
|
||||
input_item_data *erroritem;
|
||||
input_item_data *lastitem;
|
||||
bool record_next;
|
||||
input_seq starting_seq;
|
||||
|
||||
virtual void custom_render(void *selectedref, float top, float bottom, float x1, float y1, float x2, float y2) override;
|
||||
virtual void handle() override;
|
||||
virtual void update_input(input_item_data &seqchangeditem) = 0;
|
||||
};
|
||||
|
||||
|
||||
class menu_input_general : public menu_input
|
||||
{
|
||||
public:
|
||||
@ -90,6 +96,7 @@ private:
|
||||
const int group;
|
||||
};
|
||||
|
||||
|
||||
class menu_input_specific : public menu_input
|
||||
{
|
||||
public:
|
||||
|
@ -16,7 +16,9 @@
|
||||
#include "ui/submenu.h"
|
||||
#include "ui/ui.h"
|
||||
|
||||
#include "infoxml.h"
|
||||
#include "mame.h"
|
||||
|
||||
#include "osdnet.h"
|
||||
#include "mameopts.h"
|
||||
#include "pluginopts.h"
|
||||
@ -26,8 +28,6 @@
|
||||
|
||||
#include "uiinput.h"
|
||||
|
||||
#include "../info.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <fstream>
|
||||
|
@ -20,17 +20,17 @@
|
||||
#include "ui/selsoft.h"
|
||||
#include "ui/ui.h"
|
||||
|
||||
#include "../info.h"
|
||||
#include "infoxml.h"
|
||||
#include "luaengine.h"
|
||||
#include "mame.h"
|
||||
|
||||
#include "audit.h"
|
||||
#include "drivenum.h"
|
||||
#include "emuopts.h"
|
||||
#include "mame.h"
|
||||
#include "rendutil.h"
|
||||
#include "romload.h"
|
||||
#include "softlist_dev.h"
|
||||
#include "uiinput.h"
|
||||
#include "luaengine.h"
|
||||
|
||||
#include <atomic>
|
||||
#include <condition_variable>
|
||||
|
Loading…
Reference in New Issue
Block a user