-emu/ioport.cpp: Fixed "MAME lost my joystick settings!!!1!11!

-plugins: Gave the timecode plugin an option to count emulated frames.
This commit is contained in:
Vas Crabb 2021-11-03 05:18:06 +11:00
parent e1863f8c6a
commit dd43f801a1
7 changed files with 272 additions and 161 deletions

View File

@ -12,19 +12,22 @@ local timecode = exports
function timecode.startplugin()
local file -- the timecode log file
local enabled -- whether timecode recording is enabled
local write -- whether to record a timecode on the next emulated frame
local text -- name of current part
local frame_count -- emulated frame counter
local start_frame -- start frame count for current part
local start_time -- start time for current part
local total_time -- total time of parts so far this session
local count -- current timecode number
local show_counter -- whether to show elapsed time since last timecode
local show_total -- whether to show the total time of parts
local frame_mode -- 0 to count frames, 1 to assume 60 Hz
local hotkey_seq -- input sequence to record timecode
local hotkey_pressed -- whether the hotkey was pressed on the last frame update
local hotkey_cfg -- configuration string for the hotkey
local item_framemode -- menu index of frame mode item
local item_hotkey -- menu index of hotkey item
local commonui -- common UI helpers
local hotkey_poller -- helper for configuring hotkey
@ -35,48 +38,114 @@ function timecode.startplugin()
end
local function set_default_hotkey()
hotkey_seq = manager.machine.input:seq_from_tokens('KEYCODE_F12 NOT KEYCODE_LSHIFT NOT KEYCODE_RSHIFT NOT KEYCODE_LALT NOT KEYCODE_RALT')
hotkey_cfg = nil
end
local function load_settings()
-- set defaults
frame_mode = 1
set_default_hotkey()
-- try to open configuration file
local cfgname = get_settings_path() .. 'plugin.cfg'
local cfgfile = io.open(cfgname, 'r')
if not cfgfile then
return -- probably harmless, configuration just doesn't exist yet
end
-- parse settings as JSON
local json = require('json')
local settings = json.parse(cfgfile:read('a'))
cfgfile:close()
if not settings then
emu.print_error(string.format('Error loading timecode recorder settings: error parsing file "%s" as JSON', cfgname))
return
end
-- recover frame mode
local count_frames = settings.count_frames
if count_frames ~= nil then
frame_mode = count_frames and 0 or 1
end
-- recover hotkey assignment
hotkey_cfg = settings.hotkey
if hotkey_cfg then
local seq = manager.machine.input:seq_from_tokens(hotkey_cfg)
if seq then
hotkey_seq = seq
end
end
end
local function save_settings()
local path = get_settings_path()
local attr = lfs.attributes(path)
if not attr then
lfs.mkdir(path)
elseif attr.mode ~= 'directory' then
emu.print_error(string.format('Error saving timecode recorder settings: "%s" is not a directory', path))
return
end
local json = require('json')
local settings = { count_frames = frame_mode == 0 }
if hotkey_cfg then
settings.hotkey = hotkey_cfg
end
local data = json.stringify(settings, { indent = true })
local cfgname = path .. 'plugin.cfg'
local cfgfile = io.open(cfgname, 'w')
if not cfgfile then
emu.print_error(string.format('Error saving timecode recorder settings: error opening file "%s" for writing', cfgname))
return
end
cfgfile:write(data)
cfgfile:close()
end
local function process_frame()
if (not manager.machine.paused) and file and write then
if (not file) or manager.machine.paused then
return
end
if write then
write = false
count = count + 1
show_total = true
-- milliseconds from beginning of playback
-- time from beginning of playback in milliseconds, HH:MM:SS.fff and frames
local curtime = manager.machine.time
local cursec = curtime.seconds
local msec_start = (cursec * 1000) + curtime.msec
local sec_start = curtime.seconds
local msec_start = (sec_start * 1000) + curtime.msec
local msec_start_str = string.format('%015d', msec_start)
-- display the timecode
local curtime_str = string.format(
'%02d:%02d:%02d.%03d',
cursec // (60 * 60),
(cursec // 60) % 60,
cursec % 60,
sec_start // (60 * 60),
(sec_start // 60) % 60,
sec_start % 60,
msec_start % 1000)
local frame_start_str = string.format('%015d', (frame_mode == 0) and frame_count or (msec_start * 60 // 1000))
-- milliseconds from previous timecode
-- elapsed from previous timecode in milliseconds, HH:MM:SS.fff and frames
local elapsed = curtime - start_time
local elapsedsec = elapsed.seconds
local msec_elapsed = (elapsedsec * 1000) + elapsed.msec
local sec_elapsed = elapsed.seconds
local msec_elapsed = (sec_elapsed * 1000) + elapsed.msec
local msec_elapsed_str = string.format('%015d', msec_elapsed)
-- elapsed from previous timecode
start_time = curtime
local elapsed_str = string.format(
'%02d:%02d:%02d.%03d',
elapsedsec // (60 * 60),
(elapsedsec // 60) % 60,
elapsedsec % 60,
sec_elapsed // (60 * 60),
(sec_elapsed // 60) % 60,
sec_elapsed % 60,
msec_elapsed % 1000)
local frame_elapsed_str = string.format('%015d', (frame_mode == 0) and (frame_count - start_frame) or (msec_elapsed * 60 // 1000))
-- number of frames from beginning of playback
-- TODO: should this account for actual frame rate rather than assuming 60fps?
local frame_start_str = string.format('%015d', msec_start * 60 // 1000)
-- number of frames from previous timecode
-- TODO: should this account for actual frame rate rather than assuming 60fps?
local frame_elapsed_str = string.format('%015d', msec_elapsed * 60 // 1000)
-- update start of part
start_frame = frame_count
start_time = curtime
local message
local key
@ -125,6 +194,7 @@ function timecode.startplugin()
msec_start_str, msec_elapsed_str,
frame_start_str, frame_elapsed_str))
end
frame_count = frame_count + 1
end
@ -147,7 +217,7 @@ function timecode.startplugin()
total_str = string.format(_p('plugin-timecode', 'TOTAL %02d:%02d '), (total // 60) % 60, total % 60)
machine.render.ui_container:draw_text('left', 0, total_str, 0xf010f010, 0xff000000)
end
if enabled then
if file then
local pressed = machine.input:seq_pressed(hotkey_seq)
if (not hotkey_pressed) and pressed then
write = true
@ -158,35 +228,15 @@ function timecode.startplugin()
local function start()
hotkey_seq = manager.machine.input:seq_from_tokens('KEYCODE_F12 NOT KEYCODE_LSHIFT NOT KEYCODE_RSHIFT NOT KEYCODE_LALT NOT KEYCODE_RALT')
-- try to load configuration
local cfgname = get_settings_path() .. 'plugin.cfg'
local cfgfile = io.open(cfgname, 'r')
if cfgfile then
local json = require('json')
local settings = json.parse(cfgfile:read('a'))
cfgfile:close()
if not settings then
emu.print_error(string.format('Error loading timecode recorder settings: error parsing file "%s" as JSON', cfgname))
else
hotkey_cfg = settings.hotkey
if hotkey_cfg then
local seq = manager.machine.input:seq_from_tokens(hotkey_cfg)
if seq then
hotkey_seq = seq
end
end
end
end
file = nil
show_counter = false
show_total = false
load_settings()
-- only do timecode recording if we're doing input recording
local options = manager.machine.options.entries
local filename = options.record:value()
enabled = #filename > 0
show_counter = false
show_total = false
if enabled then
if #filename > 0 then
filename = filename .. '.timecode'
emu.print_info(string.format('Record input timecode file: %s', filename))
file = emu.file(options.input_directory:value(), 0x0e) -- FIXME: magic number for flags
@ -194,10 +244,12 @@ function timecode.startplugin()
if openerr then
-- TODO: this used to throw a fatal error and log the error description
emu.print_error('Failed to open file for input timecode recording')
enabled = false
file = nil
else
write = false
text = ''
frame_count = 0
start_frame = 0
start_time = emu.attotime()
total_time = emu.attotime()
count = 0
@ -231,27 +283,7 @@ function timecode.startplugin()
end
-- try to save settings
local path = get_settings_path()
local attr = lfs.attributes(path)
if not attr then
lfs.mkdir(path)
elseif attr.mode ~= 'directory' then
emu.print_error(string.format('Error saving timecode recorder settings: "%s" is not a directory', path))
return
end
if hotkey_cfg then
local json = require('json')
local settings = { hotkey = hotkey_cfg }
local data = json.stringify(settings, { indent = true })
local cfgname = path .. 'plugin.cfg'
local cfgfile = io.open(cfgname, 'w')
if not cfgfile then
emu.print_error(string.format('Error saving timecode recorder settings: error opening file "%s" for writing', cfgname))
return
end
cfgfile:write(data)
cfgfile:close()
end
save_settings()
end
@ -265,12 +297,22 @@ function timecode.startplugin()
hotkey_poller = nil
return true
end
elseif (index == item_hotkey) and (event == 'select') then
if not commonui then
commonui = require('commonui')
elseif index == item_framemode then
if (event == 'select') or (event == 'left') or (event == 'right') then
frame_mode = (frame_mode ~= 0) and 0 or 1
return true
end
elseif index == item_hotkey then
if event == 'select' then
if not commonui then
commonui = require('commonui')
end
hotkey_poller = commonui.switch_polling_helper()
return true
elseif event == 'clear' then
set_default_hotkey()
return true
end
hotkey_poller = commonui.switch_polling_helper()
return true
end
return false
end
@ -280,8 +322,14 @@ function timecode.startplugin()
local result = { }
table.insert(result, { _p('plugin-timecode', 'Timecode Recorder'), '', 'off' })
table.insert(result, { '---', '', '' })
local frame_mode_val = (frame_mode > 0) and _p('plugin-timecode', 'Assume 60 Hz') or _p('plugins-timecode', 'Count emulated frames')
table.insert(result, { _p('plugin-timecode', 'Frame numbers'), frame_mode_val, (frame_mode > 0) and 'l' or 'r' })
item_framemode = #result
table.insert(result, { _p('plugin-timecode', 'Hotkey'), manager.machine.input:seq_name(hotkey_seq), hotkey_poller and 'lr' or '' })
item_hotkey = #result
if hotkey_poller then
return hotkey_poller:overlay(result)
else

View File

@ -695,7 +695,7 @@ const char *ioport_field::name() const
const input_seq &ioport_field::seq(input_seq_type seqtype) const noexcept
{
// if no live state, return default
if (m_live == nullptr)
if (!m_live)
return defseq(seqtype);
// if the sequence is the special default code, return the expanded default value
@ -736,7 +736,7 @@ void ioport_field::set_defseq(input_seq_type seqtype, const input_seq &newseq)
m_seq[seqtype] = newseq;
// also update live state unless previously customized
if (m_live != nullptr && !was_changed)
if (m_live && !was_changed)
m_live->seq[seqtype] = newseq;
}
@ -875,14 +875,18 @@ std::string ioport_field::key_name(int which) const
// settings for the given input field
//-------------------------------------------------
void ioport_field::get_user_settings(user_settings &settings) const noexcept
void ioport_field::get_user_settings(user_settings &settings) const
{
// zap the entire structure
settings = user_settings();
// copy the basics
for (input_seq_type seqtype = SEQ_TYPE_STANDARD; seqtype < SEQ_TYPE_TOTAL; ++seqtype)
{
settings.seq[seqtype] = seq(seqtype);
if (m_live)
settings.cfg[seqtype] = m_live->cfg[seqtype];
}
// if there's a list of settings or we're an adjuster, copy the current value
if (!m_settinglist.empty() || m_type == IPT_ADJUSTER)
@ -909,16 +913,16 @@ void ioport_field::get_user_settings(user_settings &settings) const noexcept
// settings for the given input field
//-------------------------------------------------
void ioport_field::set_user_settings(const user_settings &settings) noexcept
void ioport_field::set_user_settings(const user_settings &settings)
{
// copy the basics
for (input_seq_type seqtype = SEQ_TYPE_STANDARD; seqtype < SEQ_TYPE_TOTAL; ++seqtype)
{
const input_seq &defseq = manager().type_seq(m_type, m_player, input_seq_type(seqtype));
if (defseq == settings.seq[seqtype])
if (settings.seq[seqtype].is_default())
m_live->seq[seqtype].set_default();
else
m_live->seq[seqtype] = settings.seq[seqtype];
m_live->cfg[seqtype] = settings.cfg[seqtype];
}
// if there's a list of settings or we're an adjuster, copy the current value
@ -1898,7 +1902,21 @@ void ioport_manager::set_type_seq(ioport_type type, int player, input_seq_type s
{
input_type_entry *const entry = m_type_to_entry[type][player];
if (entry)
entry->set_seq(seqtype, newseq);
{
if (newseq.is_default())
{
entry->set_seq(seqtype, entry->defseq(seqtype));
entry->set_cfg(seqtype, "");
}
else
{
entry->set_seq(seqtype, newseq);
if (!newseq.length())
entry->set_cfg(seqtype, "NONE");
else
entry->set_cfg(seqtype, machine().input().seq_to_tokens(newseq));
}
}
}
@ -2086,9 +2104,12 @@ void ioport_manager::load_config(config_type cfg_type, config_level cfg_level, u
int type = token_to_input_type(portnode->get_attribute_string("type", ""), player);
// initialize sequences to invalid defaults
input_seq newseq[SEQ_TYPE_TOTAL];
for (input_seq_type seqtype = SEQ_TYPE_STANDARD; seqtype < SEQ_TYPE_TOTAL; ++seqtype)
newseq[seqtype].set(INPUT_CODE_INVALID);
std::pair<input_seq, char const *> newseq[SEQ_TYPE_TOTAL];
for (auto &seq : newseq)
{
seq.first.set(INPUT_CODE_INVALID);
seq.second = "";
}
// loop over new sequences
for (util::xml::data_node const *seqnode = portnode->get_child("newseq"); seqnode; seqnode = seqnode->get_next_sibling("newseq"))
@ -2098,19 +2119,28 @@ void ioport_manager::load_config(config_type cfg_type, config_level cfg_level, u
if ((seqtype != -1) && seqnode->get_value())
{
if (!strcmp(seqnode->get_value(), "NONE"))
newseq[seqtype].reset();
newseq[seqtype].first.reset();
else
machine().input().seq_from_tokens(newseq[seqtype], seqnode->get_value());
machine().input().seq_from_tokens(newseq[seqtype].first, seqnode->get_value());
newseq[seqtype].second = seqnode->get_value();
}
}
// load into the appropriate place for the config type/level
if (config_type::SYSTEM == cfg_type)
{
load_system_config(*portnode, type, player, newseq);
}
else if ((config_type::CONTROLLER == cfg_type) && (config_level::DEFAULT != cfg_level))
{
for (auto &seq : newseq)
seq.second = "";
load_controller_config(*portnode, type, player, newseq);
}
else
{
load_default_config(type, player, newseq);
}
}
// after applying the controller config, push that back into the backup, since that is
@ -2230,7 +2260,10 @@ void ioport_manager::load_remap_table(util::xml::data_node const &parentnode)
// to defaults for all systems
//-------------------------------------------------
bool ioport_manager::load_default_config(int type, int player, const input_seq (&newseq)[SEQ_TYPE_TOTAL])
bool ioport_manager::load_default_config(
int type,
int player,
const std::pair<input_seq, char const *> (&newseq)[SEQ_TYPE_TOTAL])
{
// find a matching port in the list
for (input_type_entry &entry : m_typelist)
@ -2238,8 +2271,11 @@ bool ioport_manager::load_default_config(int type, int player, const input_seq (
if (entry.type() == type && entry.player() == player)
{
for (input_seq_type seqtype = SEQ_TYPE_STANDARD; seqtype < SEQ_TYPE_TOTAL; ++seqtype)
if (newseq[seqtype][0] != INPUT_CODE_INVALID)
entry.set_seq(seqtype, newseq[seqtype]);
{
if (newseq[seqtype].first[0] != INPUT_CODE_INVALID)
entry.set_seq(seqtype, newseq[seqtype].first);
entry.set_cfg(seqtype, newseq[seqtype].second);
}
return true;
}
}
@ -2252,7 +2288,11 @@ bool ioport_manager::load_default_config(int type, int player, const input_seq (
// profile settings to defaults
//-------------------------------------------------
bool ioport_manager::load_controller_config(util::xml::data_node const &portnode, int type, int player, const input_seq (&newseq)[SEQ_TYPE_TOTAL])
bool ioport_manager::load_controller_config(
util::xml::data_node const &portnode,
int type,
int player,
const std::pair<input_seq, char const *> (&newseq)[SEQ_TYPE_TOTAL])
{
// without a tag, apply to the defaults for all systems
char const *const tag = portnode.get_attribute_string("tag", nullptr);
@ -2278,8 +2318,8 @@ bool ioport_manager::load_controller_config(util::xml::data_node const &portnode
// if a sequence was specified, override the developer-specified default for the field
for (input_seq_type seqtype = SEQ_TYPE_STANDARD; seqtype < SEQ_TYPE_TOTAL; ++seqtype)
{
if (newseq[seqtype][0] != INPUT_CODE_INVALID)
field.set_defseq(seqtype, newseq[seqtype]);
if (newseq[seqtype].first[0] != INPUT_CODE_INVALID)
field.set_defseq(seqtype, newseq[seqtype].first);
}
// fetch configurable attributes
@ -2342,7 +2382,11 @@ bool ioport_manager::load_controller_config(util::xml::data_node const &portnode
// configuration for the current system
//-------------------------------------------------
void ioport_manager::load_system_config(util::xml::data_node const &portnode, int type, int player, const input_seq (&newseq)[SEQ_TYPE_TOTAL])
void ioport_manager::load_system_config(
util::xml::data_node const &portnode,
int type,
int player,
const std::pair<input_seq, char const *> (&newseq)[SEQ_TYPE_TOTAL])
{
// system-specific configuration should always apply by port/field
char const *const tag = portnode.get_attribute_string("tag", nullptr);
@ -2364,8 +2408,9 @@ void ioport_manager::load_system_config(util::xml::data_node const &portnode, in
// if a sequence was specified, copy it in
for (input_seq_type seqtype = SEQ_TYPE_STANDARD; seqtype < SEQ_TYPE_TOTAL; ++seqtype)
{
if (newseq[seqtype][0] != INPUT_CODE_INVALID)
field.live().seq[seqtype] = newseq[seqtype];
if (newseq[seqtype].first[0] != INPUT_CODE_INVALID)
field.live().seq[seqtype] = newseq[seqtype].first;
field.live().cfg[seqtype] = newseq[seqtype].second;
}
// fetch configurable attributes
@ -2460,27 +2505,6 @@ void ioport_manager::save_config(config_type cfg_type, util::xml::data_node *par
}
//-------------------------------------------------
// save_sequence - add a node for an input
// sequence
//-------------------------------------------------
void ioport_manager::save_sequence(util::xml::data_node &parentnode, input_seq_type type, ioport_type porttype, const input_seq &seq)
{
// get the string for the sequence
std::string seqstring;
if (seq.length() == 0)
seqstring.assign("NONE");
else
seqstring = machine().input().seq_to_tokens(seq);
// add the new node
util::xml::data_node *const seqnode = parentnode.add_child("newseq", seqstring.c_str());
if (seqnode != nullptr)
seqnode->set_attribute("type", seqtypestrings[type]);
}
//-------------------------------------------------
// save_this_input_field_type - determine if the
// given port type is worth saving
@ -2519,7 +2543,7 @@ void ioport_manager::save_default_inputs(util::xml::data_node &parentnode)
// see if any of the sequences have changed
input_seq_type seqtype;
for (seqtype = SEQ_TYPE_STANDARD; seqtype < SEQ_TYPE_TOTAL; ++seqtype)
if (entry.seq(seqtype) != entry.defseq(seqtype))
if (!entry.cfg(seqtype).empty())
break;
// if so, we need to add a node
@ -2527,15 +2551,21 @@ void ioport_manager::save_default_inputs(util::xml::data_node &parentnode)
{
// add a new port node
util::xml::data_node *const portnode = parentnode.add_child("port", nullptr);
if (portnode != nullptr)
if (portnode)
{
// add the port information and attributes
portnode->set_attribute("type", input_type_to_token(entry.type(), entry.player()).c_str());
// add only the sequences that have changed from the defaults
for (input_seq_type type = SEQ_TYPE_STANDARD; type < SEQ_TYPE_TOTAL; ++type)
if (entry.seq(type) != entry.defseq(type))
save_sequence(*portnode, type, entry.type(), entry.seq(type));
{
if (!entry.cfg(type).empty())
{
util::xml::data_node *const seqnode = portnode->add_child("newseq", entry.cfg(type).c_str());
if (seqnode)
seqnode->set_attribute("type", seqtypestrings[type]);
}
}
}
}
}
@ -2566,22 +2596,22 @@ void ioport_manager::save_game_inputs(util::xml::data_node &parentnode)
{
// determine if we changed
bool changed = false;
for (input_seq_type seqtype = SEQ_TYPE_STANDARD; seqtype < SEQ_TYPE_TOTAL; ++seqtype)
changed |= (field.seq(seqtype) != field.defseq(seqtype));
for (input_seq_type seqtype = SEQ_TYPE_STANDARD; (seqtype < SEQ_TYPE_TOTAL) && !changed; ++seqtype)
changed = !field.live().cfg[seqtype].empty();
if (!field.is_analog())
{
// non-analog changes
changed |= ((field.live().value & field.mask()) != (field.defvalue() & field.mask()));
changed |= (field.live().toggle != field.toggle());
changed = changed || ((field.live().value & field.mask()) != (field.defvalue() & field.mask()));
changed = changed || (field.live().toggle != field.toggle());
}
else
{
// analog changes
changed |= (field.live().analog->m_delta != field.delta());
changed |= (field.live().analog->m_centerdelta != field.centerdelta());
changed |= (field.live().analog->m_sensitivity != field.sensitivity());
changed |= (field.live().analog->m_reverse != field.analog_reverse());
changed = changed || (field.live().analog->m_delta != field.delta());
changed = changed || (field.live().analog->m_centerdelta != field.centerdelta());
changed = changed || (field.live().analog->m_sensitivity != field.sensitivity());
changed = changed || (field.live().analog->m_reverse != field.analog_reverse());
}
// if we did change, add a new node
@ -2599,8 +2629,14 @@ void ioport_manager::save_game_inputs(util::xml::data_node &parentnode)
// add sequences if changed
for (input_seq_type seqtype = SEQ_TYPE_STANDARD; seqtype < SEQ_TYPE_TOTAL; ++seqtype)
if (field.seq(seqtype) != field.defseq(seqtype))
save_sequence(*portnode, seqtype, field.type(), field.seq(seqtype));
{
if (!field.live().cfg[seqtype].empty())
{
util::xml::data_node *const seqnode = portnode->add_child("newseq", field.live().cfg[seqtype].c_str());
if (seqnode)
seqnode->set_attribute("type", seqtypestrings[seqtype]);
}
}
if (!field.is_analog())
{

View File

@ -802,22 +802,25 @@ public:
input_seq &defseq(input_seq_type seqtype = SEQ_TYPE_STANDARD) noexcept { return m_defseq[seqtype]; }
const input_seq &defseq(input_seq_type seqtype = SEQ_TYPE_STANDARD) const noexcept { return m_defseq[seqtype]; }
const input_seq &seq(input_seq_type seqtype = SEQ_TYPE_STANDARD) const noexcept { return m_seq[seqtype]; }
const std::string &cfg(input_seq_type seqtype = SEQ_TYPE_STANDARD) const noexcept { return m_cfg[seqtype]; }
// setters
void restore_default_seq() noexcept;
void set_seq(input_seq_type seqtype, const input_seq &seq) noexcept { m_seq[seqtype] = seq; }
template <typename... T> void set_cfg(input_seq_type seqtype, T &&... cfg) { m_cfg[seqtype].assign(std::forward<T>(cfg)...); }
void replace_code(input_code oldcode, input_code newcode) noexcept;
void configure_osd(const char *token, const char *name) noexcept;
private:
// internal state
ioport_type m_type; // IPT_* for this entry
ioport_group m_group; // which group the port belongs to
u8 m_player; // player number (0 is player 1)
const char * m_token; // token used to store settings
const char * m_name; // user-friendly name
std::array<input_seq, SEQ_TYPE_TOTAL> m_defseq; // default input sequence
std::array<input_seq, SEQ_TYPE_TOTAL> m_seq; // currently configured sequences
ioport_type m_type; // IPT_* for this entry
ioport_group m_group; // which group the port belongs to
u8 m_player; // player number (0 is player 1)
const char * m_token; // token used to store settings
const char * m_name; // user-friendly name
std::array<input_seq, SEQ_TYPE_TOTAL> m_defseq; // default input sequence
std::array<input_seq, SEQ_TYPE_TOTAL> m_seq; // currently configured sequences
std::array<std::string, SEQ_TYPE_TOTAL> m_cfg; // configuration strings
};
@ -1087,14 +1090,15 @@ public:
{
ioport_value value = 0; // for DIP switches
input_seq seq[SEQ_TYPE_TOTAL]; // sequences of all types
std::string cfg[SEQ_TYPE_TOTAL]; // configuration strings of all types
s32 sensitivity = 0; // for analog controls
s32 delta = 0; // for analog controls
s32 centerdelta = 0; // for analog controls
bool reverse = false; // for analog controls
bool toggle = false; // for non-analog controls
};
void get_user_settings(user_settings &settings) const noexcept;
void set_user_settings(const user_settings &settings) noexcept;
void get_user_settings(user_settings &settings) const;
void set_user_settings(const user_settings &settings);
private:
void expand_diplocation(const char *location, std::string &errorbuf);
@ -1163,6 +1167,7 @@ struct ioport_field_live
digital_joystick::direction_t joydir; // digital joystick direction index
bool lockout; // user lockout
std::string name; // overridden name
std::string cfg[SEQ_TYPE_TOTAL];// configuration strings
};
@ -1405,12 +1410,11 @@ private:
void load_config(config_type cfg_type, config_level cfg_level, util::xml::data_node const *parentnode);
void load_remap_table(util::xml::data_node const &parentnode);
bool load_default_config(int type, int player, const input_seq (&newseq)[SEQ_TYPE_TOTAL]);
bool load_controller_config(util::xml::data_node const &portnode, int type, int player, const input_seq (&newseq)[SEQ_TYPE_TOTAL]);
void load_system_config(util::xml::data_node const &portnode, int type, int player, const input_seq (&newseq)[SEQ_TYPE_TOTAL]);
bool load_default_config(int type, int player, const std::pair<input_seq, char const *> (&newseq)[SEQ_TYPE_TOTAL]);
bool load_controller_config(util::xml::data_node const &portnode, int type, int player, const std::pair<input_seq, char const *> (&newseq)[SEQ_TYPE_TOTAL]);
void load_system_config(util::xml::data_node const &portnode, int type, int player, const std::pair<input_seq, char const *> (&newseq)[SEQ_TYPE_TOTAL]);
void save_config(config_type cfg_type, util::xml::data_node *parentnode);
void save_sequence(util::xml::data_node &parentnode, input_seq_type type, ioport_type porttype, const input_seq &seq);
bool save_this_input_field_type(ioport_type type);
void save_default_inputs(util::xml::data_node &parentnode);
void save_game_inputs(util::xml::data_node &parentnode);

View File

@ -300,8 +300,8 @@ bool input_sequence_poller::poll()
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))
// if we've recorded at least one item and one second has passed, we're done
if (m_last_ticks && ((m_last_ticks + osd_ticks_per_second()) < newticks))
return true;
// return false to indicate we are still polling

View File

@ -244,6 +244,12 @@ void lua_engine::initialize_input(sol::table &emu)
ioport_field::user_settings settings;
f.get_user_settings(settings);
settings.seq[seq_type] = seq;
if (seq.is_default())
settings.cfg[seq_type].clear();
else if (!seq.length())
settings.cfg[seq_type] = "NONE";
else
settings.cfg[seq_type] = f.port().device().machine().input().seq_to_tokens(seq);
f.set_user_settings(settings);
};
ioport_field_type["input_seq"] =

View File

@ -120,6 +120,7 @@ void menu_input_general::update_input(input_item_data &seqchangeditem)
{
const input_type_entry &entry = *reinterpret_cast<const input_type_entry *>(seqchangeditem.ref);
machine().ioport().set_type_seq(entry.type(), entry.player(), seqchangeditem.seqtype, seqchangeditem.seq);
seqchangeditem.seq = machine().ioport().type_seq(entry.type(), entry.player(), seqchangeditem.seqtype);
}
@ -233,9 +234,17 @@ void menu_input_specific::update_input(input_item_data &seqchangeditem)
ioport_field::user_settings settings;
// yeah, the const_cast is naughty, but we know we stored a non-const reference in it
reinterpret_cast<const ioport_field *>(seqchangeditem.ref)->get_user_settings(settings);
ioport_field const &field(*reinterpret_cast<ioport_field const *>(seqchangeditem.ref));
field.get_user_settings(settings);
settings.seq[seqchangeditem.seqtype] = seqchangeditem.seq;
reinterpret_cast<ioport_field *>(const_cast<void *>(seqchangeditem.ref))->set_user_settings(settings);
if (seqchangeditem.seq.is_default())
settings.cfg[seqchangeditem.seqtype].clear();
else if (!seqchangeditem.seq.length())
settings.cfg[seqchangeditem.seqtype] = "NONE";
else
settings.cfg[seqchangeditem.seqtype] = machine().input().seq_to_tokens(seqchangeditem.seq);
const_cast<ioport_field &>(field).set_user_settings(settings);
seqchangeditem.seq = field.seq(seqchangeditem.seqtype);
}
@ -276,7 +285,7 @@ void menu_input::menu_activated()
void menu_input::toggle_none_default(input_seq &selected_seq, input_seq &original_seq, const input_seq &selected_defseq)
{
if (original_seq.empty()) // if we used to be "none", toggle to the default value
selected_seq = selected_defseq;
selected_seq.set_default();
else // otherwise, toggle to "none"
selected_seq.reset();
}
@ -425,12 +434,6 @@ void menu_input::handle(event const *ev)
seqchangeditem = &item;
break;
case IPT_UI_LEFT: // flip between set and append
case IPT_UI_RIGHT: // not very discoverable, but with the prompt it isn't completely opaque
if (record_next || !item.seq.empty())
record_next = !record_next;
break;
case IPT_UI_PREV_GROUP:
{
auto current = std::distance(data.data(), &item);
@ -492,6 +495,21 @@ void menu_input::handle(event const *ev)
record_next = false;
lastitem = &item;
}
// flip between set and append
// not very discoverable, but with the prompt it isn't completely opaque
if ((IPT_UI_LEFT == ev->iptkey) || (IPT_UI_RIGHT == ev->iptkey))
{
if (erroritem)
{
errormsg.clear();
erroritem = nullptr;
}
else if (record_next || !item.seq.empty())
{
record_next = !record_next;
}
}
}
// if the sequence changed, update it

View File

@ -504,8 +504,7 @@ void menu::draw(uint32_t flags)
visible_main_menu_height += 0.01f;
// if we are too wide or too tall, clamp it down
if (visible_width + 2.0f * lr_border > 1.0f)
visible_width = 1.0f - 2.0f * lr_border;
visible_width = std::min(visible_width, 1.0f - ((lr_border + (aspect * UI_LINE_WIDTH)) * 2.0f));
// if the menu and extra menu won't fit, take away part of the regular menu, it will scroll
if (visible_main_menu_height + visible_extra_menu_height + 2.0f * ui().box_tb_border() > 1.0f)