summaryrefslogtreecommitdiffstatshomepage
path: root/src/frontend/mame
diff options
context:
space:
mode:
author Vas Crabb <vas@vastheman.com>2021-10-15 09:42:35 +1100
committer Vas Crabb <vas@vastheman.com>2021-10-15 10:12:56 +1100
commit22bc3486c3ef8f2230e99daf49785a89ac8a1182 (patch)
tree4f10f7386b7b0a32c9d560395399e8b7632df34c /src/frontend/mame
parent3e8763bea5a93f3139204fcb8a0c74c2ac364af5 (diff)
More user experience improvements:
frontend: Made it possible to cancel a media audit while it's in progress. Also made the media audit multi-threaded so it's faster. frontend: Made the DIP switches in the DIP switch preview clickable. frontend: Made the system and software selection menus leave focus on the same system when clearing the search rather than jumping to the first item. Also fixed a couple of bugs in the logic for keeping the selected item visible. frontend: Fixed a few places that weren't showing localised system names. frontend: Made UI Cancel clear a search in the file manager the same way it does on the system and sofware selection menus. frontend: Made it possible for plugin menus to handle UI Cancel more naturally, backing up to the previous plugin menu rather than dropping straight back to the list of plugins. Updated the autofire, cheat and cheatfind plugins, and fixed a few other issues in the cheatfind plugin. debugger: Made the mount and unmount commands accept instance names as well as brief instance names. Also updated another page of debugger documentation.
Diffstat (limited to 'src/frontend/mame')
-rw-r--r--src/frontend/mame/ui/auditmenu.cpp146
-rw-r--r--src/frontend/mame/ui/auditmenu.h19
-rw-r--r--src/frontend/mame/ui/confswitch.cpp83
-rw-r--r--src/frontend/mame/ui/confswitch.h13
-rw-r--r--src/frontend/mame/ui/custui.cpp11
-rw-r--r--src/frontend/mame/ui/datmenu.cpp20
-rw-r--r--src/frontend/mame/ui/datmenu.h8
-rw-r--r--src/frontend/mame/ui/dirmenu.cpp1
-rw-r--r--src/frontend/mame/ui/dirmenu.h2
-rw-r--r--src/frontend/mame/ui/filesel.cpp26
-rw-r--r--src/frontend/mame/ui/filesel.h1
-rw-r--r--src/frontend/mame/ui/info.cpp8
-rw-r--r--src/frontend/mame/ui/inputmap.cpp8
-rw-r--r--src/frontend/mame/ui/menu.cpp6
-rw-r--r--src/frontend/mame/ui/menu.h4
-rw-r--r--src/frontend/mame/ui/miscmenu.cpp48
-rw-r--r--src/frontend/mame/ui/miscmenu.h17
-rw-r--r--src/frontend/mame/ui/pluginopt.cpp18
-rw-r--r--src/frontend/mame/ui/pluginopt.h13
-rw-r--r--src/frontend/mame/ui/selector.cpp2
-rw-r--r--src/frontend/mame/ui/selector.h2
-rw-r--r--src/frontend/mame/ui/selgame.cpp493
-rw-r--r--src/frontend/mame/ui/selgame.h9
-rw-r--r--src/frontend/mame/ui/selmenu.cpp25
-rw-r--r--src/frontend/mame/ui/selmenu.h9
-rw-r--r--src/frontend/mame/ui/selsoft.cpp39
-rw-r--r--src/frontend/mame/ui/selsoft.h8
-rw-r--r--src/frontend/mame/ui/simpleselgame.h2
-rw-r--r--src/frontend/mame/ui/state.cpp4
-rw-r--r--src/frontend/mame/ui/swlist.cpp44
-rw-r--r--src/frontend/mame/ui/swlist.h8
-rw-r--r--src/frontend/mame/ui/systemlist.cpp414
-rw-r--r--src/frontend/mame/ui/systemlist.h122
-rw-r--r--src/frontend/mame/ui/ui.cpp26
-rw-r--r--src/frontend/mame/ui/ui.h3
-rw-r--r--src/frontend/mame/ui/utils.cpp2
36 files changed, 942 insertions, 722 deletions
diff --git a/src/frontend/mame/ui/auditmenu.cpp b/src/frontend/mame/ui/auditmenu.cpp
index aa6ffed6a7b..7fda02764a0 100644
--- a/src/frontend/mame/ui/auditmenu.cpp
+++ b/src/frontend/mame/ui/auditmenu.cpp
@@ -11,13 +11,19 @@
#include "emu.h"
#include "ui/auditmenu.h"
+#include "ui/systemlist.h"
#include "ui/ui.h"
#include "audit.h"
-#include "corestr.h"
+
#include "drivenum.h"
+#include "uiinput.h"
+
+#include "util/corestr.h"
#include <numeric>
+#include <sstream>
+#include <thread>
extern const char UI_VERSION_TAG[];
@@ -32,19 +38,20 @@ void *const ITEMREF_START_FAST = reinterpret_cast<void *>(std::uintptr_t(2));
} // anonymous namespace
-menu_audit::menu_audit(mame_ui_manager &mui, render_container &container, std::vector<ui_system_info> &availablesorted)
+menu_audit::menu_audit(mame_ui_manager &mui, render_container &container)
: menu(mui, container)
- , m_worker_thread()
+ , m_availablesorted(system_list::instance().sorted_list())
, m_unavailable(
std::accumulate(
- availablesorted.begin(),
- availablesorted.end(),
+ m_availablesorted.begin(),
+ m_availablesorted.end(),
std::size_t(0),
[] (std::size_t n, ui_system_info const &info) { return n + (info.available ? 0 : 1); }))
- , m_availablesorted(availablesorted)
+ , m_future()
, m_audited(0)
, m_current(nullptr)
- , m_phase(phase::CONSENT)
+ , m_cancel(false)
+ , m_phase(phase::CONFIRMATION)
{
std::string filename(emulator_info::get_configname());
filename += "_avail.ini";
@@ -53,13 +60,20 @@ menu_audit::menu_audit(mame_ui_manager &mui, render_container &container, std::v
menu_audit::~menu_audit()
{
+ m_cancel.store(true);
+ for (auto &future : m_future)
+ {
+ if (future.valid())
+ future.wait();
+ }
}
+
void menu_audit::custom_render(void *selectedref, float top, float bottom, float x, float y, float x2, float y2)
{
switch (m_phase)
{
- case phase::CONSENT:
+ case phase::CONFIRMATION:
if ((ITEMREF_START_FAST == selectedref) || (ITEMREF_START_FULL == selectedref))
{
draw_text_box(
@@ -74,21 +88,46 @@ void menu_audit::custom_render(void *selectedref, float top, float bottom, float
{
// there's a race here between the total audited being updated and the next driver pointer being loaded
// it doesn't matter because we redraw on every frame anyway so it sorts itself out very quickly
- game_driver const *const driver(m_current.load());
+ ui_system_info const *const system(m_current.load());
std::size_t const audited(m_audited.load());
std::size_t const total(m_fast ? m_unavailable : m_availablesorted.size());
- std::string const text(
- util::string_format(
- _("Auditing ROMs for machine %2$u of %3$u...\n%1$s"),
- driver ? driver->type.fullname() : "",
- (std::min)(audited + 1, total),
- total));
- ui().draw_text_box(container(), text, ui::text_layout::CENTER, 0.5f, 0.5f, UI_GREEN_COLOR);
+ std::ostringstream text;
+ util::stream_format(text,
+ _("Auditing ROMs for machine %2$u of %3$u...\n%1$s"),
+ system ? std::string_view(system->description) : std::string_view(),
+ (std::min)(audited + 1, total),
+ total);
+ text << '\n' << m_prompt;
+ ui().draw_text_box(
+ container(),
+ std::move(text).str(),
+ ui::text_layout::CENTER,
+ 0.5f, 0.5f,
+ ui().colors().background_color());
}
break;
+
+ case phase::CANCELLATION:
+ ui().draw_text_box(
+ container(),
+ util::string_format(
+ _("Cancel audit?\n\nPress %1$s to cancel\nPress %2$s to continue"),
+ ui().get_general_input_setting(IPT_UI_SELECT),
+ ui().get_general_input_setting(IPT_UI_CANCEL)),
+ ui::text_layout::CENTER,
+ 0.5f, 0.5f,
+ UI_RED_COLOR);
+ break;
}
}
+
+bool menu_audit::custom_ui_cancel()
+{
+ return m_phase != phase::CONFIRMATION;
+}
+
+
void menu_audit::populate(float &customtop, float &custombottom)
{
if (m_unavailable && (m_availablesorted.size() != m_unavailable))
@@ -102,7 +141,7 @@ void menu_audit::handle()
{
switch (m_phase)
{
- case phase::CONSENT:
+ case phase::CONFIRMATION:
{
event const *const menu_event(process(0));
if (menu_event && (IPT_UI_SELECT == menu_event->iptkey))
@@ -111,40 +150,62 @@ void menu_audit::handle()
{
m_phase = phase::AUDIT;
m_fast = ITEMREF_START_FAST == menu_event->itemref;
- m_worker_thread = std::thread(
- [this] ()
- {
- if (m_fast)
- audit_fast();
- else
- audit_all();
- });
+ m_prompt = util::string_format(_("Press %1$s to cancel\n"), ui().get_general_input_setting(IPT_UI_CANCEL));
+ m_future.resize(std::thread::hardware_concurrency());
+ for (auto &future : m_future)
+ future = std::async(std::launch::async, [this] () { return do_audit(); });
}
}
}
break;
case phase::AUDIT:
+ case phase::CANCELLATION:
process(PROCESS_CUSTOM_ONLY | PROCESS_NOINPUT);
- if (m_audited.load() >= (m_fast ? m_unavailable : m_availablesorted.size()))
+ if ((m_audited.load() >= (m_fast ? m_unavailable : m_availablesorted.size())) || m_cancel.load())
{
- m_worker_thread.join();
- save_available_machines();
- reset_parent(reset_options::SELECT_FIRST);
+ bool done(true);
+ for (auto &future : m_future)
+ done = future.get() && done;
+ m_future.clear();
+ if (done)
+ {
+ save_available_machines();
+ reset_parent(reset_options::SELECT_FIRST);
+ }
stack_pop();
}
+ else if (machine().ui_input().pressed(IPT_UI_CANCEL))
+ {
+ if (phase::AUDIT == m_phase)
+ m_phase = phase::CANCELLATION;
+ else
+ m_phase = phase::AUDIT;
+ }
+ else if ((phase::CANCELLATION == m_phase) && machine().ui_input().pressed(IPT_UI_SELECT))
+ {
+ m_cancel.store(true);
+ }
break;
}
}
-void menu_audit::audit_fast()
+bool menu_audit::do_audit()
{
- for (ui_system_info &info : m_availablesorted)
+ while (true)
{
- if (!info.available)
+ std::size_t const i(m_audited.fetch_add(1));
+ if (m_availablesorted.size() <= i)
+ return true;
+
+ ui_system_info &info(m_availablesorted[i]);
+ if (!m_fast || !info.available)
{
- m_current.store(info.driver);
+ if (m_cancel.load())
+ return false;
+
+ m_current.store(&info);
driver_enumerator enumerator(machine().options(), info.driver->name);
enumerator.next();
media_auditor auditor(enumerator);
@@ -158,25 +219,6 @@ void menu_audit::audit_fast()
}
}
-void menu_audit::audit_all()
-{
- driver_enumerator enumerator(machine().options());
- media_auditor auditor(enumerator);
- std::vector<bool> available(driver_list::total(), false);
- while (enumerator.next())
- {
- m_current.store(&enumerator.driver());
- media_auditor::summary const summary(auditor.audit_media(AUDIT_VALIDATE_FAST));
-
- // if everything looks good, include the driver
- available[enumerator.current()] = (summary == media_auditor::CORRECT) || (summary == media_auditor::BEST_AVAILABLE) || (summary == media_auditor::NONE_NEEDED);
- ++m_audited;
- }
-
- for (ui_system_info &info : m_availablesorted)
- info.available = available[info.index];
-}
-
void menu_audit::save_available_machines()
{
// attempt to open the output file
diff --git a/src/frontend/mame/ui/auditmenu.h b/src/frontend/mame/ui/auditmenu.h
index a9734e71709..6536c3a19aa 100644
--- a/src/frontend/mame/ui/auditmenu.h
+++ b/src/frontend/mame/ui/auditmenu.h
@@ -16,7 +16,7 @@
#include "ui/utils.h"
#include <atomic>
-#include <thread>
+#include <future>
#include <vector>
@@ -25,28 +25,29 @@ namespace ui {
class menu_audit : public menu
{
public:
- menu_audit(mame_ui_manager &mui, render_container &container, std::vector<ui_system_info> &availablesorted);
+ menu_audit(mame_ui_manager &mui, render_container &container);
virtual ~menu_audit() override;
protected:
virtual void custom_render(void *selectedref, float top, float bottom, float x, float y, float x2, float y2) override;
+ virtual bool custom_ui_cancel() override;
private:
- enum class phase { CONSENT, AUDIT };
+ enum class phase { CONFIRMATION, AUDIT, CANCELLATION };
virtual void populate(float &customtop, float &custombottom) override;
virtual void handle() override;
- void audit_fast();
- void audit_all();
+ bool do_audit();
void save_available_machines();
- std::thread m_worker_thread;
- std::size_t const m_unavailable;
std::string m_prompt;
- std::vector<ui_system_info> &m_availablesorted;
+ std::vector<std::reference_wrapper<ui_system_info> > const &m_availablesorted;
+ std::size_t const m_unavailable;
+ std::vector<std::future<bool> > m_future;
std::atomic<std::size_t> m_audited;
- std::atomic<game_driver const *> m_current;
+ std::atomic<ui_system_info const *> m_current;
+ std::atomic<bool> m_cancel;
phase m_phase;
bool m_fast;
};
diff --git a/src/frontend/mame/ui/confswitch.cpp b/src/frontend/mame/ui/confswitch.cpp
index ee15d6d5ad5..b162b57efe5 100644
--- a/src/frontend/mame/ui/confswitch.cpp
+++ b/src/frontend/mame/ui/confswitch.cpp
@@ -51,6 +51,7 @@ menu_confswitch::switch_group_descriptor::switch_group_descriptor(ioport_field c
, mask(0U)
, state(0U)
{
+ std::fill(std::begin(toggles), std::end(toggles), toggle{ nullptr, 0U });
}
@@ -146,8 +147,10 @@ void menu_confswitch::populate(float &customtop, float &custombottom)
++m_active_switch_groups;
// apply the bits
- group.mask |= uint32_t(1) << (loc.number() - 1);
ioport_value const mask(accummask & ~(accummask - 1));
+ group.toggles[loc.number() - 1].field = &field;
+ group.toggles[loc.number() - 1].mask = mask;
+ group.mask |= uint32_t(1) << (loc.number() - 1);
if (((settings.value & mask) && !loc.inverted()) || (!(settings.value & mask) && loc.inverted()))
group.state |= uint32_t(1) << (loc.number() - 1);
@@ -254,7 +257,12 @@ void menu_confswitch::find_fields()
menu_settings_dip_switches::menu_settings_dip_switches(mame_ui_manager &mui, render_container &container)
: menu_confswitch(mui, container, IPT_DIPSWITCH)
+ , m_switch_group_y()
, m_visible_switch_groups(0U)
+ , m_single_width(0.0f)
+ , m_nub_width(0.0f)
+ , m_first_nub(0.0f)
+ , m_clickable_height(0.0f)
{
}
@@ -266,15 +274,15 @@ menu_settings_dip_switches::~menu_settings_dip_switches()
void menu_settings_dip_switches::custom_render(void *selectedref, float top, float bottom, float x1, float y1, float x2, float y2)
{
- // catch if no diploc has to be drawn
+ // catch if no DIP locations have to be drawn
if (!m_visible_switch_groups)
return;
// calculate optimal width
float const aspect(machine().render().ui_aspect(&container()));
float const lineheight(ui().get_line_height());
- float const singlewidth(lineheight * SINGLE_TOGGLE_SWITCH_FIELD_WIDTH * aspect);
float const maxwidth(1.0f - (ui().box_lr_border() * 2.0f * aspect));
+ m_single_width = (lineheight * SINGLE_TOGGLE_SWITCH_FIELD_WIDTH * aspect);
float width(0.0f);
unsigned maxswitches(0U);
for (switch_group_descriptor const &group : switch_groups())
@@ -283,30 +291,33 @@ void menu_settings_dip_switches::custom_render(void *selectedref, float top, flo
{
maxswitches = (std::max)(group.switch_count(), maxswitches);
float const namewidth(ui().get_string_width(group.name));
- float const switchwidth(singlewidth * maxswitches);
+ float const switchwidth(m_single_width * maxswitches);
width = (std::min)((std::max)(namewidth + switchwidth + (lineheight * aspect), width), maxwidth);
}
}
// draw extra menu area
float const boxwidth((std::max)(width + (ui().box_lr_border() * 2.0f * aspect), x2 - x1));
- float const boxleft((1.0f - boxwidth) / 2.0f);
+ float const boxleft((1.0f - boxwidth) * 0.5f);
ui().draw_outlined_box(container(), boxleft, y2 + ui().box_tb_border(), boxleft + boxwidth, y2 + bottom, ui().colors().background_color());
// calculate centred layout
- float const nameleft((1.0f - width) / 2.0f);
- float const switchleft(nameleft + width - (singlewidth * maxswitches));
- float const namewidth(width - (singlewidth * maxswitches) - (lineheight * aspect));
+ float const nameleft((1.0f - width) * 0.5f);
+ float const switchleft(nameleft + width - (m_single_width * maxswitches));
+ float const namewidth(width - (m_single_width * maxswitches) - (lineheight * aspect));
// iterate over switch groups
ioport_field *const field((uintptr_t(selectedref) != 1U) ? reinterpret_cast<ioport_field *>(selectedref) : nullptr);
float const nubheight(lineheight * SINGLE_TOGGLE_SWITCH_HEIGHT);
- float const nubwidth(lineheight * SINGLE_TOGGLE_SWITCH_WIDTH * aspect);
- float const ygap(lineheight * ((DIP_SWITCH_HEIGHT / 2) - SINGLE_TOGGLE_SWITCH_HEIGHT) / 2);
- float const xgap((singlewidth + (UI_LINE_WIDTH / 2) - nubwidth) / 2);
+ m_nub_width = lineheight * SINGLE_TOGGLE_SWITCH_WIDTH * aspect;
+ float const ygap(lineheight * ((DIP_SWITCH_HEIGHT * 0.5f) - SINGLE_TOGGLE_SWITCH_HEIGHT) * 0.5f);
+ float const xgap((m_single_width + (UI_LINE_WIDTH * 0.5f) - m_nub_width) * 0.5f);
+ m_first_nub = switchleft + xgap;
+ m_clickable_height = (lineheight * DIP_SWITCH_HEIGHT) - (ygap * 2.0f);
unsigned line(0U);
- for (switch_group_descriptor const &group : switch_groups())
+ for (unsigned n = 0; switch_groups().size() > n; ++n)
{
+ switch_group_descriptor const &group(switch_groups()[n]);
if (group.mask)
{
// determine the mask of selected bits
@@ -332,34 +343,35 @@ void menu_settings_dip_switches::custom_render(void *selectedref, float top, flo
unsigned const cnt(group.switch_count());
ui().draw_outlined_box(
container(),
- switchleft, liney, switchleft + (singlewidth * cnt), switchbottom,
+ switchleft, liney, switchleft + (m_single_width * cnt), switchbottom,
ui().colors().background_color());
for (unsigned i = 1; cnt > i; ++i)
{
container().add_line(
- switchleft + (singlewidth * i), liney, switchleft + (singlewidth * i), switchbottom,
+ switchleft + (m_single_width * i), liney, switchleft + (m_single_width * i), switchbottom,
UI_LINE_WIDTH, ui().colors().text_color(), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
}
// compute top and bottom for on and off positions
float const yoff(liney + UI_LINE_WIDTH + ygap);
float const yon(switchbottom - UI_LINE_WIDTH - ygap - nubheight);
+ m_switch_group_y[n] = yoff;
// draw the switch nubs
for (unsigned toggle = 0; cnt > toggle; ++toggle)
{
- float const nubleft(switchleft + (singlewidth * toggle) + xgap);
+ float const nubleft(switchleft + (m_single_width * toggle) + xgap);
if (BIT(group.mask, toggle))
{
float const nubtop(BIT(group.state, toggle) ? yon : yoff);
container().add_rect(
- nubleft, nubtop, nubleft + nubwidth, nubtop + nubheight,
+ nubleft, nubtop, nubleft + m_nub_width, nubtop + nubheight,
BIT(selectedmask, toggle) ? ui().colors().dipsw_color() : ui().colors().text_color(), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
}
else
{
container().add_rect(
- nubleft, yoff, nubleft + nubwidth, yon + nubheight,
+ nubleft, yoff, nubleft + m_nub_width, yon + nubheight,
ui().colors().unavailable_color(), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
}
}
@@ -372,6 +384,42 @@ void menu_settings_dip_switches::custom_render(void *selectedref, float top, flo
}
+bool menu_settings_dip_switches::custom_mouse_down()
+{
+ if (!m_visible_switch_groups || (get_mouse_x() < m_first_nub))
+ return false;
+
+ float const x(get_mouse_x() - m_first_nub);
+ float const y(get_mouse_y());
+ for (unsigned n = 0U; switch_groups().size() > n; ++n)
+ {
+ switch_group_descriptor const &group(switch_groups()[n]);
+ if (group.mask && (y >= m_switch_group_y[n]) && (y < (m_switch_group_y[n] + m_clickable_height)))
+ {
+ unsigned const cnt(group.switch_count());
+ for (unsigned i = 0U; cnt > i; ++i)
+ {
+ if (BIT(group.mask, i))
+ {
+ float const xstart(float(i) * m_single_width);
+ if ((x >= xstart) && (x < (xstart + m_nub_width)))
+ {
+ ioport_field::user_settings settings;
+ group.toggles[i].field->get_user_settings(settings);
+ settings.value ^= group.toggles[i].mask;
+ group.toggles[i].field->set_user_settings(settings);
+ reset(reset_options::REMEMBER_REF);
+ return true;
+ }
+ }
+ }
+ }
+ }
+
+ return false;
+}
+
+
void menu_settings_dip_switches::populate(float &customtop, float &custombottom)
{
// let the base class add items
@@ -380,6 +428,7 @@ void menu_settings_dip_switches::populate(float &customtop, float &custombottom)
// use up to about 70% of height for DIP switch display
if (active_switch_groups())
{
+ m_switch_group_y.resize(switch_groups().size());
float const lineheight(ui().get_line_height());
float const groupheight(DIP_SWITCH_HEIGHT * lineheight);
float const groupspacing(DIP_SWITCH_SPACING * lineheight);
diff --git a/src/frontend/mame/ui/confswitch.h b/src/frontend/mame/ui/confswitch.h
index 0c4506abbf2..5ece14ae4c1 100644
--- a/src/frontend/mame/ui/confswitch.h
+++ b/src/frontend/mame/ui/confswitch.h
@@ -33,6 +33,12 @@ protected:
struct switch_group_descriptor
{
+ struct toggle
+ {
+ ioport_field *field;
+ ioport_value mask;
+ };
+
switch_group_descriptor(ioport_field const &f, ioport_diplocation const &loc) noexcept;
bool matches(ioport_field const &f, ioport_diplocation const &loc) const noexcept;
@@ -40,6 +46,7 @@ protected:
char const *name;
std::reference_wrapper<device_t> owner;
+ toggle toggles[32];
uint32_t mask;
uint32_t state;
};
@@ -75,11 +82,17 @@ public:
protected:
virtual void custom_render(void *selectedref, float top, float bottom, float x, float y, float x2, float y2) override;
+ virtual bool custom_mouse_down() override;
private:
virtual void populate(float &customtop, float &custombottom) override;
+ std::vector<float> m_switch_group_y;
unsigned m_visible_switch_groups;
+ float m_single_width;
+ float m_nub_width;
+ float m_first_nub;
+ float m_clickable_height;
};
diff --git a/src/frontend/mame/ui/custui.cpp b/src/frontend/mame/ui/custui.cpp
index e100067bb46..94dbc8a898d 100644
--- a/src/frontend/mame/ui/custui.cpp
+++ b/src/frontend/mame/ui/custui.cpp
@@ -618,7 +618,7 @@ void menu_colors_ui::custom_render(void *selectedref, float top, float bottom, f
// bottom text
// get the text for 'UI Select'
- std::string const bottomtext[] = { util::string_format(_("Double-click or press %1$s to change the color value"), machine().input().seq_name(machine().ioport().type_seq(IPT_UI_SELECT, 0, SEQ_TYPE_STANDARD))) };
+ std::string const bottomtext[] = { util::string_format(_("Double-click or press %1$s to change the color value"), ui().get_general_input_setting(IPT_UI_SELECT)) };
draw_text_box(
std::begin(bottomtext), std::end(bottomtext),
origx1, origx2, origy2 + ui().box_tb_border(), origy2 + bottom,
@@ -839,13 +839,12 @@ void menu_rgb_ui::handle()
void menu_rgb_ui::populate(float &customtop, float &custombottom)
{
// set filter arrow
- uint32_t arrow_flags = FLAG_LEFT_ARROW | FLAG_RIGHT_ARROW;
std::string s_text = std::string(m_search).append("_");
item_append(_("ARGB Settings"), FLAG_DISABLE | FLAG_UI_HEADING, nullptr);
if (m_lock_ref != RGB_ALPHA)
{
- arrow_flags = get_arrow_flags<uint8_t>(0, 255, m_color->a());
+ uint32_t arrow_flags = get_arrow_flags<uint8_t>(0, 255, m_color->a());
item_append(_("color-channel", "Alpha"), string_format("%3u", m_color->a()), arrow_flags, (void *)(uintptr_t)RGB_ALPHA);
}
else
@@ -853,7 +852,7 @@ void menu_rgb_ui::populate(float &customtop, float &custombottom)
if (m_lock_ref != RGB_RED)
{
- arrow_flags = get_arrow_flags<uint8_t>(0, 255, m_color->r());
+ uint32_t arrow_flags = get_arrow_flags<uint8_t>(0, 255, m_color->r());
item_append(_("color-channel", "Red"), string_format("%3u", m_color->r()), arrow_flags, (void *)(uintptr_t)RGB_RED);
}
else
@@ -861,7 +860,7 @@ void menu_rgb_ui::populate(float &customtop, float &custombottom)
if (m_lock_ref != RGB_GREEN)
{
- arrow_flags = get_arrow_flags<uint8_t>(0, 255, m_color->g());
+ uint32_t arrow_flags = get_arrow_flags<uint8_t>(0, 255, m_color->g());
item_append(_("color-channel", "Green"), string_format("%3u", m_color->g()), arrow_flags, (void *)(uintptr_t)RGB_GREEN);
}
else
@@ -869,7 +868,7 @@ void menu_rgb_ui::populate(float &customtop, float &custombottom)
if (m_lock_ref != RGB_BLUE)
{
- arrow_flags = get_arrow_flags<uint8_t>(0, 255, m_color->b());
+ uint32_t arrow_flags = get_arrow_flags<uint8_t>(0, 255, m_color->b());
item_append(_("color-channel", "Blue"), string_format("%3u", m_color->b()), arrow_flags, (void *)(uintptr_t)RGB_BLUE);
}
else
diff --git a/src/frontend/mame/ui/datmenu.cpp b/src/frontend/mame/ui/datmenu.cpp
index f28651afdb5..a707c76459e 100644
--- a/src/frontend/mame/ui/datmenu.cpp
+++ b/src/frontend/mame/ui/datmenu.cpp
@@ -9,18 +9,22 @@
*********************************************************************/
#include "emu.h"
+#include "ui/datmenu.h"
+#include "ui/systemlist.h"
#include "ui/ui.h"
-#include "ui/datmenu.h"
#include "ui/utils.h"
+#include "luaengine.h"
#include "mame.h"
+
+#include "drivenum.h"
#include "rendfont.h"
#include "softlist.h"
#include "uiinput.h"
-#include "luaengine.h"
#include <cmath>
+#include <string_view>
namespace ui {
@@ -29,10 +33,10 @@ namespace ui {
// ctor / dtor
//-------------------------------------------------
-menu_dats_view::menu_dats_view(mame_ui_manager &mui, render_container &container, const game_driver *driver)
+menu_dats_view::menu_dats_view(mame_ui_manager &mui, render_container &container, const ui_system_info *system)
: menu(mui, container)
, m_actual(0)
- , m_driver((driver == nullptr) ? &mui.machine().system() : driver)
+ , m_system(!system ? &system_list::instance().systems()[driver_list::find(mui.machine().system().name)] : system)
, m_swinfo(nullptr)
, m_issoft(false)
@@ -48,7 +52,7 @@ menu_dats_view::menu_dats_view(mame_ui_manager &mui, render_container &container
}
}
std::vector<std::string> lua_list;
- if (mame_machine_manager::instance()->lua()->call_plugin("data_list", driver ? driver->name : "", lua_list))
+ if (mame_machine_manager::instance()->lua()->call_plugin("data_list", system ? system->driver->name : "", lua_list))
{
int count = 0;
for (std::string& item : lua_list)
@@ -65,10 +69,10 @@ menu_dats_view::menu_dats_view(mame_ui_manager &mui, render_container &container
// ctor
//-------------------------------------------------
-menu_dats_view::menu_dats_view(mame_ui_manager &mui, render_container &container, const ui_software_info *swinfo, const game_driver *driver)
+menu_dats_view::menu_dats_view(mame_ui_manager &mui, render_container &container, const ui_software_info *swinfo, const ui_system_info *system)
: menu(mui, container)
, m_actual(0)
- , m_driver((driver == nullptr) ? &mui.machine().system() : driver)
+ , m_system(!system ? &system_list::instance().systems()[driver_list::find(mui.machine().system().name)] : system)
, m_swinfo(swinfo)
, m_list(swinfo->listname)
, m_short(swinfo->shortname)
@@ -295,7 +299,7 @@ void menu_dats_view::custom_render(void *selectedref, float top, float bottom, f
{
float maxwidth = origx2 - origx1;
float width;
- std::string driver = (m_issoft == true) ? m_swinfo->longname : m_driver->type.fullname();
+ std::string_view driver = m_issoft ? m_swinfo->longname : m_system->description;
float const lr_border = ui().box_lr_border() * machine().render().ui_aspect(&container());
ui().draw_text_full(container(), driver, 0.0f, 0.0f, 1.0f, ui::text_layout::CENTER, ui::text_layout::TRUNCATE,
diff --git a/src/frontend/mame/ui/datmenu.h b/src/frontend/mame/ui/datmenu.h
index 0c7fd9c22e7..45de4f423a5 100644
--- a/src/frontend/mame/ui/datmenu.h
+++ b/src/frontend/mame/ui/datmenu.h
@@ -21,6 +21,8 @@
struct ui_software_info;
+struct ui_system_info;
+
namespace ui {
@@ -31,8 +33,8 @@ namespace ui {
class menu_dats_view : public menu
{
public:
- menu_dats_view(mame_ui_manager &mui, render_container &container, const ui_software_info *swinfo, const game_driver *driver = nullptr);
- menu_dats_view(mame_ui_manager &mui, render_container &container, const game_driver *driver = nullptr);
+ menu_dats_view(mame_ui_manager &mui, render_container &container, const ui_software_info *swinfo, const ui_system_info *system = nullptr);
+ menu_dats_view(mame_ui_manager &mui, render_container &container, const ui_system_info *system = nullptr);
virtual ~menu_dats_view() override;
protected:
@@ -47,7 +49,7 @@ private:
virtual void handle() override;
int m_actual;
- const game_driver *m_driver;
+ const ui_system_info *m_system;
const ui_software_info *m_swinfo;
std::string m_list, m_short, m_long, m_parent;
void get_data();
diff --git a/src/frontend/mame/ui/dirmenu.cpp b/src/frontend/mame/ui/dirmenu.cpp
index 1fe6682b123..a361bfd325f 100644
--- a/src/frontend/mame/ui/dirmenu.cpp
+++ b/src/frontend/mame/ui/dirmenu.cpp
@@ -22,6 +22,7 @@
namespace ui {
+
static int ADDING = 1;
static int CHANGE = 2;
diff --git a/src/frontend/mame/ui/dirmenu.h b/src/frontend/mame/ui/dirmenu.h
index 7280d4ef571..5d794319b7c 100644
--- a/src/frontend/mame/ui/dirmenu.h
+++ b/src/frontend/mame/ui/dirmenu.h
@@ -100,7 +100,7 @@ public:
protected:
virtual void custom_render(void *selectedref, float top, float bottom, float x, float y, float x2, float y2) override;
- virtual bool menu_has_search_active() override { return !m_search.empty(); }
+ virtual bool custom_ui_cancel() override { return !m_search.empty(); }
private:
virtual void populate(float &customtop, float &custombottom) override;
diff --git a/src/frontend/mame/ui/filesel.cpp b/src/frontend/mame/ui/filesel.cpp
index 41ecac49c16..1ddabffbb23 100644
--- a/src/frontend/mame/ui/filesel.cpp
+++ b/src/frontend/mame/ui/filesel.cpp
@@ -347,7 +347,7 @@ void menu_file_selector::type_search_char(char32_t ch)
std::string const current(m_filename);
if (input_character(m_filename, ch, uchar_is_printable))
{
- ui().popup_time(ERROR_MESSAGE_TIME, "%s", m_filename.c_str());
+ ui().popup_time(ERROR_MESSAGE_TIME, "%s", m_filename);
file_selector_entry const *const cur_selected(reinterpret_cast<file_selector_entry const *>(get_selection_ref()));
@@ -482,17 +482,9 @@ void menu_file_selector::handle()
{
// process the menu
event const *const event = process(0);
- if (event && event->itemref)
+ if (event)
{
- // handle selections
- if (event->iptkey == IPT_UI_SELECT)
- {
- select_item(*reinterpret_cast<file_selector_entry const *>(event->itemref));
-
- // reset the char buffer when pressing IPT_UI_SELECT
- m_filename.clear();
- }
- else if (event->iptkey == IPT_SPECIAL)
+ if (event->iptkey == IPT_SPECIAL)
{
// if it's any other key and we're not maxed out, update
type_search_char(event->unichar);
@@ -500,6 +492,18 @@ void menu_file_selector::handle()
else if (event->iptkey == IPT_UI_CANCEL)
{
// reset the char buffer also in this case
+ if (!m_filename.empty())
+ {
+ m_filename.clear();
+ ui().popup_time(ERROR_MESSAGE_TIME, "%s", m_filename);
+ }
+ }
+ else if (event->itemref && (event->iptkey == IPT_UI_SELECT))
+ {
+ // handle selections
+ select_item(*reinterpret_cast<file_selector_entry const *>(event->itemref));
+
+ // reset the char buffer when pressing IPT_UI_SELECT
m_filename.clear();
}
}
diff --git a/src/frontend/mame/ui/filesel.h b/src/frontend/mame/ui/filesel.h
index 768cf1ba55e..8eefce9462a 100644
--- a/src/frontend/mame/ui/filesel.h
+++ b/src/frontend/mame/ui/filesel.h
@@ -45,6 +45,7 @@ public:
protected:
virtual void custom_render(void *selectedref, float top, float bottom, float x, float y, float x2, float y2) override;
+ virtual bool custom_ui_cancel() override { return !m_filename.empty(); }
virtual bool custom_mouse_down() override;
private:
diff --git a/src/frontend/mame/ui/info.cpp b/src/frontend/mame/ui/info.cpp
index e3f557ed54e..0dc8b8979e9 100644
--- a/src/frontend/mame/ui/info.cpp
+++ b/src/frontend/mame/ui/info.cpp
@@ -9,8 +9,9 @@
***************************************************************************/
#include "emu.h"
-
#include "ui/info.h"
+
+#include "ui/systemlist.h"
#include "ui/ui.h"
#include "drivenum.h"
@@ -337,7 +338,7 @@ std::string machine_info::game_info_string() const
// print description, manufacturer, and CPU:
util::stream_format(buf, _("%1$s\n%2$s %3$s\nDriver: %4$s\n\nCPU:\n"),
- m_machine.system().type.fullname(),
+ system_list::instance().systems()[driver_list::find(m_machine.system().name)].description,
m_machine.system().year,
m_machine.system().manufacturer,
core_filename_extract_base(m_machine.system().type.source()));
@@ -538,7 +539,8 @@ menu_image_info::~menu_image_info()
void menu_image_info::populate(float &customtop, float &custombottom)
{
- item_append(machine().system().type.fullname(), FLAG_DISABLE, nullptr);
+ ui_system_info const &system(system_list::instance().systems()[driver_list::find(machine().system().name)]);
+ item_append(system.description, FLAG_DISABLE, nullptr);
item_append(std::string(), FLAG_DISABLE, nullptr);
for (device_image_interface &image : image_interface_enumerator(machine().root_device()))
diff --git a/src/frontend/mame/ui/inputmap.cpp b/src/frontend/mame/ui/inputmap.cpp
index 0f4c269b92b..4d2c2c9bb8d 100644
--- a/src/frontend/mame/ui/inputmap.cpp
+++ b/src/frontend/mame/ui/inputmap.cpp
@@ -510,10 +510,10 @@ void menu_input::populate_sorted(float &customtop, float &custombottom)
}
// pre-format messages
- assignprompt = util::string_format(_("Press %1$s to set\n"), machine().input().seq_name(machine().ioport().type_seq(IPT_UI_SELECT)));
- appendprompt = util::string_format(_("Press %1$s to append\n"), machine().input().seq_name(machine().ioport().type_seq(IPT_UI_SELECT)));
- clearprompt = util::string_format(_("Press %1$s to clear\n"), machine().input().seq_name(machine().ioport().type_seq(IPT_UI_CLEAR)));
- defaultprompt = util::string_format(_("Press %1$s to restore default\n"), machine().input().seq_name(machine().ioport().type_seq(IPT_UI_CLEAR)));
+ assignprompt = util::string_format(_("Press %1$s to set\n"), ui().get_general_input_setting(IPT_UI_SELECT));
+ appendprompt = util::string_format(_("Press %1$s to append\n"), ui().get_general_input_setting(IPT_UI_SELECT));
+ clearprompt = util::string_format(_("Press %1$s to clear\n"), ui().get_general_input_setting(IPT_UI_CLEAR));
+ defaultprompt = util::string_format(_("Press %1$s to restore default\n"), ui().get_general_input_setting(IPT_UI_CLEAR));
// leave space for showing the input sequence below the menu
custombottom = 2.0f * ui().get_line_height() + 3.0f * ui().box_tb_border();
diff --git a/src/frontend/mame/ui/menu.cpp b/src/frontend/mame/ui/menu.cpp
index d3caec1b2c2..5b2bb73b7ad 100644
--- a/src/frontend/mame/ui/menu.cpp
+++ b/src/frontend/mame/ui/menu.cpp
@@ -515,10 +515,12 @@ void menu::draw(uint32_t flags)
if (top_line < 0 || is_first_selected())
top_line = 0;
- if (m_selected >= (top_line + m_visible_lines))
+ else if (m_selected >= (top_line + m_visible_lines))
top_line = m_selected - (m_visible_lines / 2);
if ((top_line > (m_items.size() - m_visible_lines)) || is_last_selected())
top_line = m_items.size() - m_visible_lines;
+ else if (m_selected >= (top_line + m_visible_lines - 2))
+ top_line = m_selected - m_visible_lines + ((m_selected == (m_items.size() - 1)) ? 1: 2);
// if scrolling, show arrows
bool const show_top_arrow((m_items.size() > m_visible_lines) && !first_item_visible());
@@ -1013,7 +1015,7 @@ void menu::handle_keys(uint32_t flags, int &iptkey)
// hitting cancel also pops the stack
if (exclusive_input_pressed(iptkey, IPT_UI_CANCEL, 0))
{
- if (!menu_has_search_active())
+ if (!custom_ui_cancel())
stack_pop();
return;
}
diff --git a/src/frontend/mame/ui/menu.h b/src/frontend/mame/ui/menu.h
index b509bff49ed..e0f5cbf9f0d 100644
--- a/src/frontend/mame/ui/menu.h
+++ b/src/frontend/mame/ui/menu.h
@@ -276,11 +276,9 @@ protected:
// overridable event handling
virtual void handle_events(uint32_t flags, event &ev);
virtual void handle_keys(uint32_t flags, int &iptkey);
+ virtual bool custom_ui_cancel() { return false; }
virtual bool custom_mouse_down() { return false; }
- // test if search is active
- virtual bool menu_has_search_active() { return false; }
-
static bool is_selectable(menu_item const &item)
{
return ((item.flags & (menu::FLAG_MULTILINE | menu::FLAG_DISABLE)) == 0 && item.type != menu_item_type::SEPARATOR);
diff --git a/src/frontend/mame/ui/miscmenu.cpp b/src/frontend/mame/ui/miscmenu.cpp
index b7b26f62ee0..09c63d591c8 100644
--- a/src/frontend/mame/ui/miscmenu.cpp
+++ b/src/frontend/mame/ui/miscmenu.cpp
@@ -706,41 +706,19 @@ menu_machine_configure::menu_machine_configure(
ui_system_info const &info,
std::function<void (bool, bool)> &&handler,
float x0, float y0)
- : menu_machine_configure(mui, container, info.description.c_str(), *info.driver, std::move(handler), x0, y0)
-{
-}
-
-menu_machine_configure::menu_machine_configure(
- mame_ui_manager &mui,
- render_container &container,
- game_driver const &drv,
- std::function<void (bool, bool)> &&handler,
- float x0, float y0)
- : menu_machine_configure(mui, container, drv.type.fullname(), drv, std::move(handler), x0, y0)
-{
-}
-
-menu_machine_configure::menu_machine_configure(
- mame_ui_manager &mui,
- render_container &container,
- char const *description,
- game_driver const &drv,
- std::function<void (bool, bool)> &&handler,
- float x0, float y0)
: menu(mui, container)
, m_handler(std::move(handler))
- , m_description(description)
- , m_drv(drv)
+ , m_sys(info)
, m_x0(x0)
, m_y0(y0)
, m_curbios(0)
- , m_was_favorite(mame_machine_manager::instance()->favorite().is_favorite_system(drv))
+ , m_was_favorite(mame_machine_manager::instance()->favorite().is_favorite_system(*info.driver))
, m_want_favorite(m_was_favorite)
{
// parse the INI file
std::ostringstream error;
osd_setup_osd_specific_emu_options(m_opts);
- mame_options::parse_standard_inis(m_opts, error, &m_drv);
+ mame_options::parse_standard_inis(m_opts, error, m_sys.driver);
setup_bios();
}
@@ -749,9 +727,9 @@ menu_machine_configure::~menu_machine_configure()
if (m_was_favorite != m_want_favorite)
{
if (m_want_favorite)
- mame_machine_manager::instance()->favorite().add_favorite_system(m_drv);
+ mame_machine_manager::instance()->favorite().add_favorite_system(*m_sys.driver);
else
- mame_machine_manager::instance()->favorite().remove_favorite_system(m_drv);
+ mame_machine_manager::instance()->favorite().remove_favorite_system(*m_sys.driver);
}
if (m_handler)
@@ -775,7 +753,7 @@ void menu_machine_configure::handle()
{
case SAVE:
{
- const std::string filename(m_drv.name);
+ const std::string filename(m_sys.driver->name);
emu_file file(machine().options().ini_path(), OPEN_FLAG_WRITE | OPEN_FLAG_CREATE);
std::error_condition const filerr = file.open(filename + ".ini");
if (!filerr)
@@ -796,15 +774,15 @@ void menu_machine_configure::handle()
break;
case CONTROLLER:
if (menu_event->iptkey == IPT_UI_SELECT)
- menu::stack_push<submenu>(ui(), container(), submenu::control_options(), &m_drv, &m_opts);
+ menu::stack_push<submenu>(ui(), container(), submenu::control_options(), m_sys.driver, &m_opts);
break;
case VIDEO:
if (menu_event->iptkey == IPT_UI_SELECT)
- menu::stack_push<submenu>(ui(), container(), submenu::video_options(), &m_drv, &m_opts);
+ menu::stack_push<submenu>(ui(), container(), submenu::video_options(), m_sys.driver, &m_opts);
break;
case ADVANCED:
if (menu_event->iptkey == IPT_UI_SELECT)
- menu::stack_push<submenu>(ui(), container(), submenu::advanced_options(), &m_drv, &m_opts);
+ menu::stack_push<submenu>(ui(), container(), submenu::advanced_options(), m_sys.driver, &m_opts);
break;
default:
break;
@@ -858,7 +836,7 @@ void menu_machine_configure::populate(float &customtop, float &custombottom)
void menu_machine_configure::custom_render(void *selectedref, float top, float bottom, float origx1, float origy1, float origx2, float origy2)
{
- char const *const text[] = { _("Configure Machine:"), m_description };
+ char const *const text[] = { _("Configure Machine:"), m_sys.description.c_str() };
draw_text_box(
std::begin(text), std::end(text),
origx1, origx2, origy1 - top, origy1 - ui().box_tb_border(),
@@ -868,19 +846,19 @@ void menu_machine_configure::custom_render(void *selectedref, float top, float b
void menu_machine_configure::setup_bios()
{
- if (!m_drv.rom)
+ if (!m_sys.driver->rom)
return;
std::string specbios(m_opts.bios());
char const *default_name(nullptr);
- for (tiny_rom_entry const *rom = m_drv.rom; !ROMENTRY_ISEND(rom); ++rom)
+ for (tiny_rom_entry const *rom = m_sys.driver->rom; !ROMENTRY_ISEND(rom); ++rom)
{
if (ROMENTRY_ISDEFAULT_BIOS(rom))
default_name = rom->name;
}
std::size_t bios_count = 0;
- for (romload::system_bios const &bios : romload::entries(m_drv.rom).get_system_bioses())
+ for (romload::system_bios const &bios : romload::entries(m_sys.driver->rom).get_system_bioses())
{
std::string name(bios.get_description());
u32 const bios_flags(bios.get_value());
diff --git a/src/frontend/mame/ui/miscmenu.h b/src/frontend/mame/ui/miscmenu.h
index 5fdf7fb8a8e..78ed68cc35a 100644
--- a/src/frontend/mame/ui/miscmenu.h
+++ b/src/frontend/mame/ui/miscmenu.h
@@ -136,12 +136,6 @@ public:
ui_system_info const &info,
std::function<void (bool, bool)> &&handler = nullptr,
float x0 = 0.0f, float y0 = 0.0f);
- menu_machine_configure(
- mame_ui_manager &mui,
- render_container &container,
- game_driver const &drv,
- std::function<void (bool, bool)> &&handler = nullptr,
- float x0 = 0.0f, float y0 = 0.0f);
virtual ~menu_machine_configure();
protected:
@@ -162,22 +156,13 @@ private:
LAST = ADVANCED
};
- menu_machine_configure(
- mame_ui_manager &mui,
- render_container &container,
- char const *description,
- game_driver const &drv,
- std::function<void (bool, bool)> &&handler,
- float x0, float y0);
-
virtual void populate(float &customtop, float &custombottom) override;
virtual void handle() override;
void setup_bios();
std::function<void (bool, bool)> const m_handler;
- char const *const m_description;
- game_driver const &m_drv;
+ ui_system_info const &m_sys;
emu_options m_opts;
float const m_x0;
float const m_y0;
diff --git a/src/frontend/mame/ui/pluginopt.cpp b/src/frontend/mame/ui/pluginopt.cpp
index 90522bc48ae..83faaa43603 100644
--- a/src/frontend/mame/ui/pluginopt.cpp
+++ b/src/frontend/mame/ui/pluginopt.cpp
@@ -9,12 +9,12 @@
*********************************************************************/
#include "emu.h"
+#include "pluginopt.h"
-#include "ui/pluginopt.h"
#include "ui/utils.h"
-#include "mame.h"
#include "luaengine.h"
+#include "mame.h"
namespace ui {
@@ -23,7 +23,7 @@ void menu_plugin::handle()
{
const event *menu_event = process(0);
- if (menu_event != nullptr && menu_event->itemref != nullptr)
+ if (menu_event && menu_event->itemref)
{
if (menu_event->iptkey == IPT_UI_SELECT)
menu::stack_push<menu_plugin_opt>(ui(), container(), (char *)menu_event->itemref);
@@ -31,8 +31,8 @@ void menu_plugin::handle()
}
menu_plugin::menu_plugin(mame_ui_manager &mui, render_container &container) :
- menu(mui, container),
- m_plugins(mame_machine_manager::instance()->lua()->get_menu())
+ menu(mui, container),
+ m_plugins(mame_machine_manager::instance()->lua()->get_menu())
{
}
@@ -72,10 +72,10 @@ void menu_plugin_opt::handle()
{
const event *menu_event = process(0);
- if (menu_event != nullptr && (uintptr_t)menu_event->itemref)
+ if (menu_event && uintptr_t(menu_event->itemref))
{
std::string key;
- switch(menu_event->iptkey)
+ switch (menu_event->iptkey)
{
case IPT_UI_UP:
key = "up";
@@ -107,8 +107,10 @@ void menu_plugin_opt::handle()
default:
return;
}
- if(mame_machine_manager::instance()->lua()->menu_callback(m_menu, (uintptr_t)menu_event->itemref, key))
+ if (mame_machine_manager::instance()->lua()->menu_callback(m_menu, uintptr_t(menu_event->itemref), key))
reset(reset_options::REMEMBER_REF);
+ else if (menu_event->iptkey == IPT_UI_CANCEL)
+ stack_pop();
}
}
diff --git a/src/frontend/mame/ui/pluginopt.h b/src/frontend/mame/ui/pluginopt.h
index 5a4b457654b..da2da131792 100644
--- a/src/frontend/mame/ui/pluginopt.h
+++ b/src/frontend/mame/ui/pluginopt.h
@@ -7,20 +7,20 @@
Internal menu for the plugin interface.
***************************************************************************/
-
-#pragma once
-
#ifndef MAME_FRONTEND_UI_PLUGINOPT_H
#define MAME_FRONTEND_UI_PLUGINOPT_H
-#include "ui/ui.h"
+#pragma once
+
#include "ui/menu.h"
+#include "ui/ui.h"
#include <string>
#include <vector>
namespace ui {
+
class menu_plugin : public menu
{
public:
@@ -43,6 +43,9 @@ public:
menu_plugin_opt(mame_ui_manager &mui, render_container &container, char *menu);
virtual ~menu_plugin_opt();
+protected:
+ virtual bool custom_ui_cancel() override { return true; }
+
private:
virtual void populate(float &customtop, float &custombottom) override;
virtual void handle() override;
@@ -52,4 +55,4 @@ private:
} // namespace ui
-#endif /* MAME_FRONTEND_UI_PLUGINOPT_H */
+#endif // MAME_FRONTEND_UI_PLUGINOPT_H
diff --git a/src/frontend/mame/ui/selector.cpp b/src/frontend/mame/ui/selector.cpp
index 58db5db28ae..4f7751713c3 100644
--- a/src/frontend/mame/ui/selector.cpp
+++ b/src/frontend/mame/ui/selector.cpp
@@ -123,7 +123,7 @@ void menu_selector::custom_render(void *selectedref, float top, float bottom, fl
ui().colors().text_color(), UI_GREEN_COLOR, 1.0f);
// get the text for 'UI Select'
- tempbuf[0] = string_format(_("Double-click or press %1$s to select"), machine().input().seq_name(machine().ioport().type_seq(IPT_UI_SELECT, 0, SEQ_TYPE_STANDARD)));
+ tempbuf[0] = string_format(_("Double-click or press %1$s to select"), ui().get_general_input_setting(IPT_UI_SELECT));
draw_text_box(
std::begin(tempbuf), std::end(tempbuf),
origx1, origx2, origy2 + ui().box_tb_border(), origy2 + bottom,
diff --git a/src/frontend/mame/ui/selector.h b/src/frontend/mame/ui/selector.h
index f3921536267..1f53a244bfb 100644
--- a/src/frontend/mame/ui/selector.h
+++ b/src/frontend/mame/ui/selector.h
@@ -35,7 +35,7 @@ public:
protected:
virtual void custom_render(void *selectedref, float top, float bottom, float x, float y, float x2, float y2) override;
- virtual bool menu_has_search_active() override { return !m_search.empty(); }
+ virtual bool custom_ui_cancel() override { return !m_search.empty(); }
private:
enum { VISIBLE_GAMES_IN_SEARCH = 200 };
diff --git a/src/frontend/mame/ui/selgame.cpp b/src/frontend/mame/ui/selgame.cpp
index 348a1874a8b..0589c7fd20c 100644
--- a/src/frontend/mame/ui/selgame.cpp
+++ b/src/frontend/mame/ui/selgame.cpp
@@ -18,6 +18,7 @@
#include "ui/optsmenu.h"
#include "ui/selector.h"
#include "ui/selsoft.h"
+#include "ui/systemlist.h"
#include "ui/ui.h"
#include "infoxml.h"
@@ -33,15 +34,9 @@
#include "uiinput.h"
#include "unicode.h"
-#include <atomic>
-#include <condition_variable>
#include <cstring>
#include <iterator>
-#include <locale>
#include <memory>
-#include <mutex>
-#include <string_view>
-#include <thread>
extern const char UI_VERSION_TAG[];
@@ -54,429 +49,6 @@ constexpr uint32_t FLAGS_UI = ui::menu::FLAG_LEFT_ARROW | ui::menu::FLAG_RIGHT_A
} // anonymous namespace
-class menu_select_game::persistent_data
-{
-public:
- enum available : unsigned
- {
- AVAIL_NONE = 0U,
- AVAIL_SORTED_LIST = 1U << 0,
- AVAIL_BIOS_COUNT = 1U << 1,
- AVAIL_UCS_SHORTNAME = 1U << 2,
- AVAIL_UCS_DESCRIPTION = 1U << 3,
- AVAIL_UCS_MANUF_DESC = 1U << 4,
- AVAIL_UCS_DFLT_DESC = 1U << 5,
- AVAIL_UCS_MANUF_DFLT_DESC = 1U << 6,
- AVAIL_FILTER_DATA = 1U << 7
- };
-
- ~persistent_data()
- {
- if (m_thread)
- m_thread->join();
- }
-
- void cache_data(ui_options const &options)
- {
- std::unique_lock<std::mutex> lock(m_mutex);
- if (!m_started)
- {
- m_started = true;
- m_thread = std::make_unique<std::thread>(
- [this, datpath = std::string(options.history_path()), titles = std::string(options.system_names())]
- {
- do_cache_data(datpath, titles);
- });
- }
- }
-
- void reset_cache()
- {
- if (m_thread)
- m_thread->join();
- std::unique_lock<std::mutex> lock(m_mutex);
- m_thread.reset();
- m_started = false;
- m_available = AVAIL_NONE;
- m_sorted_list.clear();
- m_filter_data = machine_filter_data();
- m_bios_count = 0U;
- }
-
- bool is_available(available desired)
- {
- return (m_available.load(std::memory_order_acquire) & desired) == desired;
- }
-
- void wait_available(available desired)
- {
- if (!is_available(desired))
- {
- assert(m_started);
- std::unique_lock<std::mutex> lock(m_mutex);
- m_condition.wait(lock, [this, desired] () { return is_available(desired); });
- }
- }
-
- std::vector<ui_system_info> &sorted_list()
- {
- wait_available(AVAIL_SORTED_LIST);
- return m_sorted_list;
- }
-
- int bios_count()
- {
- wait_available(AVAIL_BIOS_COUNT);
- return m_bios_count;
- }
-
- bool unavailable_systems()
- {
- wait_available(AVAIL_SORTED_LIST);
- return std::find_if(m_sorted_list.begin(), m_sorted_list.end(), [] (ui_system_info const &info) { return !info.available; }) != m_sorted_list.end();
- }
-
- machine_filter_data &filter_data()
- {
- wait_available(AVAIL_FILTER_DATA);
- return m_filter_data;
- }
-
- static persistent_data &instance()
- {
- static persistent_data data;
- return data;
- }
-
-private:
- persistent_data()
- : m_started(false)
- , m_available(AVAIL_NONE)
- , m_bios_count(0)
- {
- }
-
- void notify_available(available value)
- {
- std::unique_lock<std::mutex> lock(m_mutex);
- m_available.fetch_or(value, std::memory_order_release);
- m_condition.notify_all();
- }
-
- void do_cache_data(std::string const &datpath, std::string const &titles)
- {
- // try to open the titles file for optimisation reasons
- emu_file titles_file(datpath, OPEN_FLAG_READ);
- bool const try_titles(!titles.empty() && !titles_file.open(titles));
-
- // generate full list - initially ordered by shortname
- populate_list(!try_titles);
-
- // notify that BIOS count is valie
- notify_available(AVAIL_BIOS_COUNT);
-
- // try to load localised descriptions
- if (try_titles)
- {
- load_titles(titles_file);
-
- // populate parent descriptions while still ordered by shortname
- // already done on the first pass if built-in titles are used
- populate_parents();
- }
-
- // get rid of the "empty" driver - we don't need positions to line up any more
- m_sorted_list.erase(m_sorted_list.begin() + driver_list::find(GAME_NAME(___empty)));
-
- // sort drivers and notify
- std::collate<wchar_t> const &coll = std::use_facet<std::collate<wchar_t> >(std::locale());
- auto const compare_names =
- [&coll] (std::wstring const &wx, std::wstring const &wy) -> bool
- {
- return 0 > coll.compare(wx.data(), wx.data() + wx.size(), wy.data(), wy.data() + wy.size());
- };
- std::stable_sort(
- m_sorted_list.begin(),
- m_sorted_list.end(),
- [&compare_names] (ui_system_info const &lhs, ui_system_info const &rhs)
- {
- game_driver const &x(*lhs.driver);
- game_driver const &y(*rhs.driver);
-
- if (!lhs.is_clone && !rhs.is_clone)
- {
- return compare_names(
- lhs.reading_description.empty() ? wstring_from_utf8(lhs.description) : lhs.reading_description,
- rhs.reading_description.empty() ? wstring_from_utf8(rhs.description) : rhs.reading_description);
- }
- else if (lhs.is_clone && rhs.is_clone)
- {
- if (!std::strcmp(x.parent, y.parent))
- {
- return compare_names(
- lhs.reading_description.empty() ? wstring_from_utf8(lhs.description) : lhs.reading_description,
- rhs.reading_description.empty() ? wstring_from_utf8(rhs.description) : rhs.reading_description);
- }
- else
- {
- return compare_names(
- lhs.reading_parent.empty() ? wstring_from_utf8(lhs.parent) : lhs.reading_parent,
- rhs.reading_parent.empty() ? wstring_from_utf8(rhs.parent) : rhs.reading_parent);
- }
- }
- else if (!lhs.is_clone && rhs.is_clone)
- {
- if (!std::strcmp(x.name, y.parent))
- {
- return true;
- }
- else
- {
- return compare_names(
- lhs.reading_description.empty() ? wstring_from_utf8(lhs.description) : lhs.reading_description,
- rhs.reading_parent.empty() ? wstring_from_utf8(rhs.parent) : rhs.reading_parent);
- }
- }
- else
- {
- if (!std::strcmp(x.parent, y.name))
- {
- return false;
- }
- else
- {
- return compare_names(
- lhs.reading_parent.empty() ? wstring_from_utf8(lhs.parent) : lhs.reading_parent,
- rhs.reading_description.empty() ? wstring_from_utf8(rhs.description) : rhs.reading_description);
- }
- }
- });
- notify_available(AVAIL_SORTED_LIST);
-
- // sort manufacturers and years
- m_filter_data.finalise();
- notify_available(AVAIL_FILTER_DATA);
-
- // convert shortnames to UCS-4
- for (ui_system_info &info : m_sorted_list)
- info.ucs_shortname = ustr_from_utf8(normalize_unicode(info.driver->name, unicode_normalization_form::D, true));
- notify_available(AVAIL_UCS_SHORTNAME);
-
- // convert descriptions to UCS-4
- for (ui_system_info &info : m_sorted_list)
- info.ucs_description = ustr_from_utf8(normalize_unicode(info.description, unicode_normalization_form::D, true));
- notify_available(AVAIL_UCS_DESCRIPTION);
-
- // convert "<manufacturer> <description>" to UCS-4
- std::string buf;
- for (ui_system_info &info : m_sorted_list)
- {
- buf.assign(info.driver->manufacturer);
- buf.append(1, ' ');
- buf.append(info.description);
- info.ucs_manufacturer_description = ustr_from_utf8(normalize_unicode(buf, unicode_normalization_form::D, true));
- }
- notify_available(AVAIL_UCS_MANUF_DESC);
-
- // convert default descriptions to UCS-4
- if (try_titles)
- {
- for (ui_system_info &info : m_sorted_list)
- {
- std::string_view const fullname(info.driver->type.fullname());
- if (info.description != fullname)
- info.ucs_default_description = ustr_from_utf8(normalize_unicode(fullname, unicode_normalization_form::D, true));
- }
- }
- notify_available(AVAIL_UCS_DFLT_DESC);
-
- // convert "<manufacturer> <default description>" to UCS-4
- if (try_titles)
- {
- for (ui_system_info &info : m_sorted_list)
- {
- std::string_view const fullname(info.driver->type.fullname());
- if (info.description != fullname)
- {
- buf.assign(info.driver->manufacturer);
- buf.append(1, ' ');
- buf.append(fullname);
- info.ucs_manufacturer_default_description = ustr_from_utf8(normalize_unicode(buf, unicode_normalization_form::D, true));
- }
- }
- }
- notify_available(AVAIL_UCS_MANUF_DFLT_DESC);
- }
-
- void populate_list(bool copydesc)
- {
- m_sorted_list.reserve(driver_list::total());
- std::unordered_set<std::string> manufacturers, years;
- for (int x = 0; x < driver_list::total(); ++x)
- {
- game_driver const &driver(driver_list::driver(x));
- ui_system_info &ins(m_sorted_list.emplace_back(driver, x, false));
- if (&driver != &GAME_NAME(___empty))
- {
- if (driver.flags & machine_flags::IS_BIOS_ROOT)
- ++m_bios_count;
-
- if ((driver.parent[0] != '0') || driver.parent[1])
- {
- auto const parentindex(driver_list::find(driver.parent));
- if (copydesc)
- {
- if (0 <= parentindex)
- {
- game_driver const &parentdriver(driver_list::driver(parentindex));
- ins.is_clone = !(parentdriver.flags & machine_flags::IS_BIOS_ROOT);
- ins.parent = parentdriver.type.fullname();
- }
- else
- {
- ins.is_clone = false;
- ins.parent = driver.parent;
- }
- }
- else
- {
- ins.is_clone = (0 <= parentindex) && !(driver_list::driver(parentindex).flags & machine_flags::IS_BIOS_ROOT);
- }
- }
-
- if (copydesc)
- ins.description = driver.type.fullname();
-
- m_filter_data.add_manufacturer(driver.manufacturer);
- m_filter_data.add_year(driver.year);
- }
- }
- }
-
- void load_titles(util::core_file &file)
- {
- char readbuf[1024];
- std::string convbuf;
- while (file.gets(readbuf, std::size(readbuf)))
- {
- // shortname, description, and description reading separated by tab
- auto const eoln(
- std::find_if(
- std::begin(readbuf),
- std::end(readbuf),
- [] (char ch) { return !ch || ('\n' == ch) || ('\r' == ch); }));
- auto const split(std::find(std::begin(readbuf), eoln, '\t'));
- if (eoln == split)
- continue;
- std::string_view const shortname(readbuf, split - readbuf);
-
- // find matching system - still sorted by shortname at this point
- auto const found(
- std::lower_bound(
- m_sorted_list.begin(),
- m_sorted_list.end(),
- shortname,
- [] (ui_system_info const &a, std::string_view const &b)
- {
- return a.driver->name < b;
- }));
- if ((m_sorted_list.end() == found) || (shortname != found->driver->name))
- {
- //osd_printf_verbose("System '%s' not found\n", shortname); very spammy for single-driver builds
- continue;
- }
-
- // find the end of the description
- auto const descstart(std::next(split));
- auto const descend(std::find(descstart, eoln, '\t'));
- auto const description(strtrimspace(std::string_view(descstart, descend - descstart)));
- if (description.empty())
- {
- osd_printf_warning("Empty translated description for system '%s'\n", shortname);
- }
- else if (!found->description.empty())
- {
- osd_printf_warning(
- "Multiple translated descriptions for system '%s' ('%s' and '%s')\n",
- shortname,
- found->description,
- description);
- }
- else
- {
- found->description = description;
- }
-
- // populate the reading if it's present
- if (eoln == descend)
- continue;
- auto const readstart(std::next(descend));
- auto const readend(std::find(readstart, eoln, '\t'));
- auto const reading(strtrimspace(std::string_view(readstart, readend - readstart)));
- if (reading.empty())
- {
- osd_printf_warning("Empty translated description reading for system '%s'\n", shortname);
- }
- else
- {
- found->reading_description = wstring_from_utf8(reading);
- found->ucs_reading_description = ustr_from_utf8(normalize_unicode(reading, unicode_normalization_form::D, true));
- convbuf.assign(found->driver->manufacturer);
- convbuf.append(1, ' ');
- convbuf.append(reading);
- found->ucs_manufacturer_reading_description = ustr_from_utf8(normalize_unicode(convbuf, unicode_normalization_form::D, true));
- }
- }
-
- // fill in untranslated descriptions
- for (ui_system_info &info : m_sorted_list)
- {
- if (info.description.empty())
- info.description = info.driver->type.fullname();
- }
- }
-
- void populate_parents()
- {
- for (ui_system_info &info : m_sorted_list)
- {
- if ((info.driver->parent[0] != '0') || info.driver->parent[1])
- {
- auto const found(
- std::lower_bound(
- m_sorted_list.begin(),
- m_sorted_list.end(),
- std::string_view(info.driver->parent),
- [] (ui_system_info const &a, std::string_view const &b)
- {
- return a.driver->name < b;
- }));
- if (m_sorted_list.end() != found)
- {
- info.parent = found->description;
- info.reading_parent = found->reading_description;
- }
- else
- {
- info.parent = info.driver->parent;
- }
- }
- }
- }
-
- // synchronisation
- std::mutex m_mutex;
- std::condition_variable m_condition;
- std::unique_ptr<std::thread> m_thread;
- std::atomic<bool> m_started;
- std::atomic<unsigned> m_available;
-
- // data
- std::vector<ui_system_info> m_sorted_list;
- machine_filter_data m_filter_data;
- int m_bios_count;
-};
-
bool menu_select_game::s_first_start = true;
@@ -486,12 +58,12 @@ bool menu_select_game::s_first_start = true;
menu_select_game::menu_select_game(mame_ui_manager &mui, render_container &container, const char *gamename)
: menu_select_launch(mui, container, false)
- , m_persistent_data(persistent_data::instance())
+ , m_persistent_data(system_list::instance())
, m_icons(MAX_ICONS_RENDER)
, m_icon_paths()
, m_displaylist()
, m_searchlist()
- , m_searched_fields(persistent_data::AVAIL_NONE)
+ , m_searched_fields(system_list::AVAIL_NONE)
, m_populated_favorites(false)
{
std::string error_string, last_filter, sub_filter;
@@ -587,7 +159,7 @@ void menu_select_game::handle()
if (ui_globals::reset)
{
// dumb workaround for not being able to add an exit notifier
- struct cache_reset { ~cache_reset() { persistent_data::instance().reset_cache(); } };
+ struct cache_reset { ~cache_reset() { system_list::instance().reset_cache(); } };
ui().get_session_data<cache_reset, cache_reset>();
ui_globals::reset = false;
@@ -667,7 +239,8 @@ void menu_select_game::handle()
if (!m_populated_favorites)
{
menu::stack_push<menu_machine_configure>(
- ui(), container(),
+ ui(),
+ container(),
*reinterpret_cast<ui_system_info const *>(m_prev_selected),
nullptr,
menu_event->mouse.x0, menu_event->mouse.y0);
@@ -675,9 +248,11 @@ void menu_select_game::handle()
else
{
ui_software_info *sw = reinterpret_cast<ui_software_info *>(m_prev_selected);
+ ui_system_info const &sys = m_persistent_data.systems()[driver_list::find(sw->driver->name)];
menu::stack_push<menu_machine_configure>(
- ui(), container(),
- *sw->driver,
+ ui(),
+ container(),
+ sys,
[this, empty = sw->startempty] (bool fav, bool changed)
{
if (changed)
@@ -743,7 +318,7 @@ void menu_select_game::handle()
break;
case IPT_UI_AUDIT:
- menu::stack_push<menu_audit>(ui(), container(), m_persistent_data.sorted_list());
+ menu::stack_push<menu_audit>(ui(), container());
break;
}
}
@@ -796,7 +371,7 @@ void menu_select_game::populate(float &customtop, float &custombottom)
else
{
// if filter is set on category, build category list
- std::vector<ui_system_info> const &sorted(m_persistent_data.sorted_list());
+ auto const &sorted(m_persistent_data.sorted_list());
if (!flt)
std::copy(sorted.begin(), sorted.end(), std::back_inserter(m_displaylist));
else
@@ -1083,10 +658,11 @@ void menu_select_game::inkey_select_favorite(const event *menu_event)
if (m_prev_selected)
{
ui_software_info *swinfo = reinterpret_cast<ui_software_info *>(m_prev_selected);
+ ui_system_info const &sysinfo = m_persistent_data.systems()[driver_list::find(swinfo->driver->name)];
menu::stack_push<menu_machine_configure>(
ui(),
container(),
- *swinfo->driver,
+ sysinfo,
[this, empty = swinfo->startempty] (bool fav, bool changed)
{
if (changed)
@@ -1105,7 +681,8 @@ void menu_select_game::inkey_select_favorite(const event *menu_event)
{
if (!swlistdev.get_info().empty())
{
- menu::stack_push<menu_select_software>(ui(), container(), *ui_swinfo->driver);
+ ui_system_info const &system(m_persistent_data.systems()[driver_list::find(ui_swinfo->driver->name)]);
+ menu::stack_push<menu_select_software>(ui(), container(), system);
return;
}
}
@@ -1217,7 +794,7 @@ void menu_select_game::populate_search()
// ensure search list is populated
if (m_searchlist.empty())
{
- std::vector<ui_system_info> const &sorted(m_persistent_data.sorted_list());
+ auto const &sorted(m_persistent_data.sorted_list());
m_searchlist.reserve(sorted.size());
for (ui_system_info const &info : sorted)
m_searchlist.emplace_back(1.0, std::ref(info));
@@ -1227,16 +804,16 @@ void menu_select_game::populate_search()
const std::u32string ucs_search(ustr_from_utf8(normalize_unicode(m_search, unicode_normalization_form::D, true)));
// check available search data
- if (m_persistent_data.is_available(persistent_data::AVAIL_UCS_SHORTNAME))
- m_searched_fields |= persistent_data::AVAIL_UCS_SHORTNAME;
- if (m_persistent_data.is_available(persistent_data::AVAIL_UCS_DESCRIPTION))
- m_searched_fields |= persistent_data::AVAIL_UCS_DESCRIPTION;
- if (m_persistent_data.is_available(persistent_data::AVAIL_UCS_MANUF_DESC))
- m_searched_fields |= persistent_data::AVAIL_UCS_MANUF_DESC;
- if (m_persistent_data.is_available(persistent_data::AVAIL_UCS_DFLT_DESC))
- m_searched_fields |= persistent_data::AVAIL_UCS_DFLT_DESC;
- if (m_persistent_data.is_available(persistent_data::AVAIL_UCS_MANUF_DFLT_DESC))
- m_searched_fields |= persistent_data::AVAIL_UCS_MANUF_DFLT_DESC;
+ if (m_persistent_data.is_available(system_list::AVAIL_UCS_SHORTNAME))
+ m_searched_fields |= system_list::AVAIL_UCS_SHORTNAME;
+ if (m_persistent_data.is_available(system_list::AVAIL_UCS_DESCRIPTION))
+ m_searched_fields |= system_list::AVAIL_UCS_DESCRIPTION;
+ if (m_persistent_data.is_available(system_list::AVAIL_UCS_MANUF_DESC))
+ m_searched_fields |= system_list::AVAIL_UCS_MANUF_DESC;
+ if (m_persistent_data.is_available(system_list::AVAIL_UCS_DFLT_DESC))
+ m_searched_fields |= system_list::AVAIL_UCS_DFLT_DESC;
+ if (m_persistent_data.is_available(system_list::AVAIL_UCS_MANUF_DFLT_DESC))
+ m_searched_fields |= system_list::AVAIL_UCS_MANUF_DFLT_DESC;
for (std::pair<double, std::reference_wrapper<ui_system_info const> > &info : m_searchlist)
{
@@ -1244,7 +821,7 @@ void menu_select_game::populate_search()
ui_system_info const &sys(info.second);
// match shortnames
- if (m_searched_fields & persistent_data::AVAIL_UCS_SHORTNAME)
+ if (m_searched_fields & system_list::AVAIL_UCS_SHORTNAME)
info.first = util::edit_distance(ucs_search, sys.ucs_shortname);
// match reading
@@ -1258,20 +835,20 @@ void menu_select_game::populate_search()
}
// match descriptions
- if (info.first && (m_searched_fields & persistent_data::AVAIL_UCS_DESCRIPTION))
+ if (info.first && (m_searched_fields & system_list::AVAIL_UCS_DESCRIPTION))
info.first = (std::min)(util::edit_distance(ucs_search, sys.ucs_description), info.first);
// match "<manufacturer> <description>"
- if (info.first && (m_searched_fields & persistent_data::AVAIL_UCS_MANUF_DESC))
+ if (info.first && (m_searched_fields & system_list::AVAIL_UCS_MANUF_DESC))
info.first = (std::min)(util::edit_distance(ucs_search, sys.ucs_manufacturer_description), info.first);
// match default description
- if (info.first && (m_searched_fields & persistent_data::AVAIL_UCS_DFLT_DESC) && !sys.ucs_default_description.empty())
+ if (info.first && (m_searched_fields & system_list::AVAIL_UCS_DFLT_DESC) && !sys.ucs_default_description.empty())
{
info.first = (std::min)(util::edit_distance(ucs_search, sys.ucs_default_description), info.first);
// match "<manufacturer> <default description>"
- if (info.first && (m_searched_fields & persistent_data::AVAIL_UCS_MANUF_DFLT_DESC))
+ if (info.first && (m_searched_fields & system_list::AVAIL_UCS_MANUF_DFLT_DESC))
info.first = (std::min)(util::edit_distance(ucs_search, sys.ucs_manufacturer_default_description), info.first);
}
}
@@ -1456,7 +1033,7 @@ void menu_select_game::get_selection(ui_software_info const *&software, ui_syste
if (m_populated_favorites)
{
software = reinterpret_cast<ui_software_info const *>(get_selection_ptr());
- system = nullptr;
+ system = &m_persistent_data.systems()[driver_list::find(software->driver->name)];
}
else
{
@@ -1492,10 +1069,10 @@ void menu_select_game::make_topbox_text(std::string &line0, std::string &line1,
}
-std::string menu_select_game::make_software_description(ui_software_info const &software) const
+std::string menu_select_game::make_software_description(ui_software_info const &software, ui_system_info const *system) const
{
// first line is system
- return string_format(_("System: %1$-.100s"), software.driver->type.fullname()); // TODO: localise description
+ return string_format(_("System: %1$-.100s"), system->description);
}
diff --git a/src/frontend/mame/ui/selgame.h b/src/frontend/mame/ui/selgame.h
index 23127fa8c14..8b54024ece1 100644
--- a/src/frontend/mame/ui/selgame.h
+++ b/src/frontend/mame/ui/selgame.h
@@ -20,6 +20,9 @@
namespace ui {
+class system_list;
+
+
class menu_select_game : public menu_select_launch
{
public:
@@ -38,9 +41,7 @@ private:
using icon_cache = texture_lru<game_driver const *>;
- class persistent_data;
-
- persistent_data &m_persistent_data;
+ system_list &m_persistent_data;
icon_cache m_icons;
std::string m_icon_paths;
std::vector<std::reference_wrapper<ui_system_info const> > m_displaylist;
@@ -64,7 +65,7 @@ private:
// text for main top/bottom panels
virtual void make_topbox_text(std::string &line0, std::string &line1, std::string &line2) const override;
- virtual std::string make_software_description(ui_software_info const &software) const override;
+ virtual std::string make_software_description(ui_software_info const &software, ui_system_info const *system) const override;
// filter navigation
virtual void filter_selected() override;
diff --git a/src/frontend/mame/ui/selmenu.cpp b/src/frontend/mame/ui/selmenu.cpp
index f755fd1d4e2..ba9facc7d25 100644
--- a/src/frontend/mame/ui/selmenu.cpp
+++ b/src/frontend/mame/ui/selmenu.cpp
@@ -671,7 +671,7 @@ void menu_select_launch::custom_render(void *selectedref, float top, float botto
isstar = mame_machine_manager::instance()->favorite().is_favorite_system_software(*swinfo);
// first line is long name or system
- tempbuf[0] = make_software_description(*swinfo);
+ tempbuf[0] = make_software_description(*swinfo, system);
// next line is year, publisher
tempbuf[1] = string_format(_("%1$s, %2$-.100s"), swinfo->year, swinfo->publisher);
@@ -857,14 +857,14 @@ void menu_select_launch::inkey_dats()
if (software)
{
if (software->startempty && mame_machine_manager::instance()->lua()->call_plugin_check<const char *>("data_list", software->driver->name, true))
- menu::stack_push<menu_dats_view>(ui(), container(), software->driver);
+ menu::stack_push<menu_dats_view>(ui(), container(), system);
else if (mame_machine_manager::instance()->lua()->call_plugin_check<const char *>("data_list", std::string(software->shortname).append(1, ',').append(software->listname).c_str()) || !software->infotext.empty())
menu::stack_push<menu_dats_view>(ui(), container(), software);
}
else if (system)
{
if (mame_machine_manager::instance()->lua()->call_plugin_check<const char *>("data_list", system->driver->name, true))
- menu::stack_push<menu_dats_view>(ui(), container(), system->driver);
+ menu::stack_push<menu_dats_view>(ui(), container(), system);
}
}
@@ -1435,11 +1435,11 @@ void menu_select_launch::handle_keys(uint32_t flags, int &iptkey)
{
// dismiss error
}
- else if (menu_has_search_active())
+ else if (!m_search.empty())
{
// escape pressed with non-empty search text clears it
m_search.clear();
- reset(reset_options::SELECT_FIRST);
+ reset(reset_options::REMEMBER_REF);
}
else
{
@@ -1960,12 +1960,22 @@ void menu_select_launch::draw(uint32_t flags)
float line = visible_top + (float(m_visible_lines) * line_height);
ui().draw_outlined_box(container(), x1, y1, x2, y2, ui().colors().background_color());
+ // make sure the selection
if (m_available_items < m_visible_lines)
m_visible_lines = m_available_items;
if (top_line < 0 || is_first_selected())
+ {
top_line = 0;
- if (selected_index() < m_available_items && top_line + m_visible_lines >= m_available_items)
- top_line = m_available_items - m_visible_lines;
+ }
+ else if (selected_index() < m_available_items)
+ {
+ if (selected_index() >= (top_line + m_visible_lines))
+ top_line = selected_index() - (m_visible_lines / 2);
+ if ((top_line + m_visible_lines) >= m_available_items)
+ top_line = m_available_items - m_visible_lines;
+ else if (selected_index() >= (top_line + m_visible_lines - 2))
+ top_line = selected_index() - m_visible_lines + ((selected_index() == (m_available_items - 1)) ? 1: 2);
+ }
// determine effective positions taking into account the hilighting arrows
float effective_width = visible_width - 2.0f * gutter_width;
@@ -2934,7 +2944,6 @@ void menu_select_launch::general_info(ui_system_info const *system, game_driver
int cloneof = driver_list::non_bios_clone(driver);
if (0 <= cloneof)
{
- // TODO: this shows the non-localised parent name in some situations
util::stream_format(
str,
_("Driver is Clone of\t%1$-.100s\n"),
diff --git a/src/frontend/mame/ui/selmenu.h b/src/frontend/mame/ui/selmenu.h
index e6576711ab7..9a32b33b6db 100644
--- a/src/frontend/mame/ui/selmenu.h
+++ b/src/frontend/mame/ui/selmenu.h
@@ -273,15 +273,10 @@ private:
void get_title_search(std::string &title, std::string &search);
- // handle keys
+ // event handling
virtual void handle_keys(uint32_t flags, int &iptkey) override;
-
- // handle mouse
virtual void handle_events(uint32_t flags, event &ev) override;
- // live search active?
- virtual bool menu_has_search_active() override { return !m_search.empty(); }
-
// draw game list
virtual void draw(uint32_t flags) override;
@@ -297,7 +292,7 @@ private:
// text for main top/bottom panels
virtual void make_topbox_text(std::string &line0, std::string &line1, std::string &line2) const = 0;
- virtual std::string make_software_description(ui_software_info const &software) const = 0;
+ virtual std::string make_software_description(ui_software_info const &software, ui_system_info const *system) const = 0;
// filter navigation
virtual void filter_selected() = 0;
diff --git a/src/frontend/mame/ui/selsoft.cpp b/src/frontend/mame/ui/selsoft.cpp
index 98d28b535e5..05ce1302a70 100644
--- a/src/frontend/mame/ui/selsoft.cpp
+++ b/src/frontend/mame/ui/selsoft.cpp
@@ -86,9 +86,9 @@ public:
, m_searchlist()
{
// add start empty item
- m_swinfo.emplace_back(menu.m_driver);
+ m_swinfo.emplace_back(*menu.m_system.driver);
- machine_config config(menu.m_driver, menu.machine().options());
+ machine_config config(*menu.m_system.driver, menu.machine().options());
// see if any media devices require an image to be loaded
m_has_empty_start = true;
@@ -156,7 +156,7 @@ public:
if (instance_name && type_name)
{
// add to collection and try to resolve parent if applicable
- auto const ins(m_swinfo.emplace(m_swinfo.end(), swinfo, part, menu.m_driver, swlist.list_name(), instance_name, type_name));
+ auto const ins(m_swinfo.emplace(m_swinfo.end(), swinfo, part, *menu.m_system.driver, swlist.list_name(), instance_name, type_name));
if (!swinfo.parentname().empty())
{
if ((parentnames.end() == prevparent) || (swinfo.parentname() != prevparent->first))
@@ -279,7 +279,7 @@ public:
// load custom filters info from file
emu_file file(menu.ui().options().ui_path(), OPEN_FLAG_READ);
- if (!file.open(util::string_format("custom_%s_filter.ini", menu.m_driver.name)))
+ if (!file.open(util::string_format("custom_%s_filter.ini", menu.m_system.driver->name)))
{
software_filter::ptr flt(software_filter::create(file, m_filter_data));
if (flt)
@@ -361,31 +361,16 @@ private:
// ctor
//-------------------------------------------------
-menu_select_software::menu_select_software(mame_ui_manager &mui, render_container &container, game_driver const &driver)
- : menu_select_software(mui, container, driver.type.fullname(), driver)
-{
-}
-
menu_select_software::menu_select_software(mame_ui_manager &mui, render_container &container, ui_system_info const &system)
- : menu_select_software(mui, container, system.description.c_str(), *system.driver)
-{
-}
-
-menu_select_software::menu_select_software(
- mame_ui_manager &mui,
- render_container &container,
- char const *description,
- game_driver const &driver)
: menu_select_launch(mui, container, true)
, m_icon_paths()
- , m_description(description)
- , m_driver(driver)
+ , m_system(system)
, m_displaylist()
{
reselect_last::reselect(false);
using machine_data_cache = util::lru_cache_map<game_driver const *, std::shared_ptr<machine_data> >;
- auto &cached(mui.get_session_data<menu_select_software, machine_data_cache>(8)[&driver]);
+ auto &cached(mui.get_session_data<menu_select_software, machine_data_cache>(8)[system.driver]);
if (!cached)
cached = std::make_shared<machine_data>(*this);
m_data = cached;
@@ -712,7 +697,7 @@ render_texture *menu_select_software::get_icon_texture(int linenum, void *select
void menu_select_software::get_selection(ui_software_info const *&software, ui_system_info const *&system) const
{
software = reinterpret_cast<ui_software_info const *>(get_selection_ptr());
- system = nullptr;
+ system = &m_system;
}
@@ -721,7 +706,7 @@ void menu_select_software::make_topbox_text(std::string &line0, std::string &lin
// determine the text for the header
int vis_item = !m_search.empty() ? m_available_items : (m_available_items - 1);
line0 = string_format(_("%1$s %2$s ( %3$d / %4$d software packages )"), emulator_info::get_appname(), bare_build_version, vis_item, m_data->swinfo().size() - 1);
- line1 = string_format(_("Driver: \"%1$s\" software list "), m_description);
+ line1 = string_format(_("Driver: \"%1$s\" software list "), m_system.description);
software_filter const *const it(m_data->current_filter());
char const *const filter(it ? it->filter_text() : nullptr);
@@ -732,10 +717,10 @@ void menu_select_software::make_topbox_text(std::string &line0, std::string &lin
}
-std::string menu_select_software::make_software_description(ui_software_info const &software) const
+std::string menu_select_software::make_software_description(ui_software_info const &software, ui_system_info const *system) const
{
- // first line is long name
- return string_format(_("%1$-.100s"), software.longname);
+ // show list/item to make it less confusing when there are multiple lists mixed
+ return string_format(_("Software list/item: %1$s:%2$s"), software.listname, software.shortname);
}
@@ -752,7 +737,7 @@ void menu_select_software::filter_selected()
if (software_filter::CUSTOM == new_type)
{
emu_file file(ui().options().ui_path(), OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS);
- if (!file.open(util::string_format("custom_%s_filter.ini", m_driver.name)))
+ if (!file.open(util::string_format("custom_%s_filter.ini", m_system.driver->name)))
{
filter.save_ini(file, 0);
file.close();
diff --git a/src/frontend/mame/ui/selsoft.h b/src/frontend/mame/ui/selsoft.h
index 370bcd0c6f1..d3b2d88f71b 100644
--- a/src/frontend/mame/ui/selsoft.h
+++ b/src/frontend/mame/ui/selsoft.h
@@ -29,7 +29,6 @@ namespace ui {
class menu_select_software : public menu_select_launch
{
public:
- menu_select_software(mame_ui_manager &mui, render_container &container, game_driver const &driver);
menu_select_software(mame_ui_manager &mui, render_container &container, ui_system_info const &system);
virtual ~menu_select_software() override;
@@ -40,8 +39,6 @@ private:
struct search_item;
class machine_data;
- menu_select_software(mame_ui_manager &mui, render_container &container, char const *description, game_driver const &driver);
-
virtual void populate(float &customtop, float &custombottom) override;
virtual void handle() override;
@@ -54,7 +51,7 @@ private:
// text for main top/bottom panels
virtual void make_topbox_text(std::string &line0, std::string &line1, std::string &line2) const override;
- virtual std::string make_software_description(ui_software_info const &software) const override;
+ virtual std::string make_software_description(ui_software_info const &software, ui_system_info const *system) const override;
// filter navigation
virtual void filter_selected() override;
@@ -66,8 +63,7 @@ private:
void inkey_select(const event *menu_event);
std::map<std::string, std::string> m_icon_paths;
- char const *const m_description;
- game_driver const &m_driver;
+ ui_system_info const &m_system;
std::shared_ptr<machine_data> m_data;
std::vector<std::reference_wrapper<ui_software_info const> > m_displaylist;
diff --git a/src/frontend/mame/ui/simpleselgame.h b/src/frontend/mame/ui/simpleselgame.h
index e56dac8185b..7a581b5ea32 100644
--- a/src/frontend/mame/ui/simpleselgame.h
+++ b/src/frontend/mame/ui/simpleselgame.h
@@ -30,7 +30,7 @@ public:
protected:
virtual void custom_render(void *selectedref, float top, float bottom, float x, float y, float x2, float y2) override;
- virtual bool menu_has_search_active() override { return !m_search.empty(); }
+ virtual bool custom_ui_cancel() override { return !m_search.empty(); }
private:
enum { VISIBLE_GAMES_IN_LIST = 15 };
diff --git a/src/frontend/mame/ui/state.cpp b/src/frontend/mame/ui/state.cpp
index 6bb0b9ccdc5..98655ffa23e 100644
--- a/src/frontend/mame/ui/state.cpp
+++ b/src/frontend/mame/ui/state.cpp
@@ -264,8 +264,8 @@ void menu_load_save_state_base::handle()
m_confirm_prompt = util::string_format(
_("Delete saved state %1$s?\nPress %2$s to delete\nPress %3$s to cancel"),
m_confirm_delete->visible_name(),
- machine().input().seq_name(machine().ioport().type_seq(IPT_UI_SELECT)),
- machine().input().seq_name(machine().ioport().type_seq(IPT_UI_CANCEL)));
+ ui().get_general_input_setting(IPT_UI_SELECT),
+ ui().get_general_input_setting(IPT_UI_CANCEL));
}
}
else if (!m_confirm_delete)
diff --git a/src/frontend/mame/ui/swlist.cpp b/src/frontend/mame/ui/swlist.cpp
index c1650654e5a..ac7235c1b1c 100644
--- a/src/frontend/mame/ui/swlist.cpp
+++ b/src/frontend/mame/ui/swlist.cpp
@@ -244,25 +244,31 @@ void menu_software_list::handle()
// process the menu
const event *event = process(0);
- if (event && event->itemref)
+ if (event)
{
- if (event->itemref == ITEMREF_SWITCH_ITEM_ORDERING && event->iptkey == IPT_UI_SELECT)
+ if (event->iptkey == IPT_UI_SELECT)
{
- m_ordered_by_shortname = !m_ordered_by_shortname;
+ if (event->itemref == ITEMREF_SWITCH_ITEM_ORDERING)
+ {
+ m_ordered_by_shortname = !m_ordered_by_shortname;
- // reset the char buffer if we change ordering criterion
- m_search.clear();
+ // reset the char buffer if we change ordering criterion
+ m_search.clear();
- // reload the menu with the new order
- reset(reset_options::REMEMBER_REF);
- machine().popmessage(_("Switched Order: entries now ordered by %s"), m_ordered_by_shortname ? _("shortname") : _("description"));
- }
- // handle selections
- else if (event->iptkey == IPT_UI_SELECT)
- {
- entry_info *info = (entry_info *) event->itemref;
- m_result = info->short_name;
- stack_pop();
+ // reload the menu with the new order
+ reset(reset_options::REMEMBER_REF);
+ machine().popmessage(
+ m_ordered_by_shortname
+ ? _("Switched Order: entries now ordered by shortname")
+ : _("Switched Order: entries now ordered by description"));
+ }
+ else if (event->itemref)
+ {
+ // handle selections
+ entry_info *info = (entry_info *) event->itemref;
+ m_result = info->short_name;
+ stack_pop();
+ }
}
else if (event->iptkey == IPT_SPECIAL)
{
@@ -312,9 +318,11 @@ void menu_software_list::handle()
else if (event->iptkey == IPT_UI_CANCEL)
{
// reset the char buffer also in this case
- m_search.clear();
- m_result = m_search;
- stack_pop();
+ if (!m_search.empty())
+ {
+ m_search.clear();
+ ui().popup_time(ERROR_MESSAGE_TIME, "%s", m_search);
+ }
}
}
}
diff --git a/src/frontend/mame/ui/swlist.h b/src/frontend/mame/ui/swlist.h
index 3a4ade44c4e..b62ec167b02 100644
--- a/src/frontend/mame/ui/swlist.h
+++ b/src/frontend/mame/ui/swlist.h
@@ -63,8 +63,9 @@ class menu_software_list : public menu
public:
menu_software_list(mame_ui_manager &mui, render_container &container, software_list_device *swlist, const char *interface, std::string &result);
virtual ~menu_software_list() override;
- virtual void populate(float &customtop, float &custombottom) override;
- virtual void handle() override;
+
+protected:
+ virtual bool custom_ui_cancel() override { return !m_search.empty(); }
private:
struct entry_info
@@ -87,6 +88,9 @@ private:
std::string m_search;
bool m_ordered_by_shortname;
+ virtual void populate(float &customtop, float &custombottom) override;
+ virtual void handle() override;
+
// functions
void append_software_entry(const software_info &swinfo);
};
diff --git a/src/frontend/mame/ui/systemlist.cpp b/src/frontend/mame/ui/systemlist.cpp
new file mode 100644
index 00000000000..258df48b0b2
--- /dev/null
+++ b/src/frontend/mame/ui/systemlist.cpp
@@ -0,0 +1,414 @@
+// license:BSD-3-Clause
+// copyright-holders:Vas Crabb
+/***************************************************************************
+
+ ui/systemlist.h
+
+ Persistent system list data.
+
+***************************************************************************/
+
+#include "emu.h"
+#include "ui/systemlist.h"
+
+#include "ui/moptions.h"
+
+#include "drivenum.h"
+
+#include "util/corestr.h"
+#include "util/unicode.h"
+
+#include <algorithm>
+#include <cassert>
+#include <locale>
+#include <string_view>
+
+
+namespace ui {
+
+void system_list::cache_data(ui_options const &options)
+{
+ std::unique_lock<std::mutex> lock(m_mutex);
+ if (!m_started)
+ {
+ m_started = true;
+ m_thread = std::make_unique<std::thread>(
+ [this, datpath = std::string(options.history_path()), titles = std::string(options.system_names())]
+ {
+ do_cache_data(datpath, titles);
+ });
+ }
+}
+
+
+void system_list::reset_cache()
+{
+ std::unique_lock<std::mutex> lock(m_mutex);
+ if (m_thread)
+ m_thread->join();
+ m_thread.reset();
+ m_started = false;
+ m_available = AVAIL_NONE;
+ m_systems.clear();
+ m_sorted_list.clear();
+ m_filter_data = machine_filter_data();
+ m_bios_count = 0;
+}
+
+
+void system_list::wait_available(available desired)
+{
+ if (!is_available(desired))
+ {
+ assert(m_started);
+ std::unique_lock<std::mutex> lock(m_mutex);
+ if (!is_available(desired))
+ m_condition.wait(lock, [this, desired] () { return is_available(desired); });
+ }
+}
+
+
+system_list &system_list::instance()
+{
+ static system_list data;
+ return data;
+}
+
+
+system_list::system_list()
+ : m_started(false)
+ , m_available(AVAIL_NONE)
+ , m_bios_count(0)
+{
+}
+
+
+system_list::~system_list()
+{
+ if (m_thread)
+ m_thread->join();
+}
+
+
+void system_list::notify_available(available value)
+{
+ std::unique_lock<std::mutex> lock(m_mutex);
+ m_available.fetch_or(value, std::memory_order_release);
+ m_condition.notify_all();
+}
+
+
+void system_list::do_cache_data(std::string const &datpath, std::string const &titles)
+{
+ // try to open the titles file for optimisation reasons
+ emu_file titles_file(datpath, OPEN_FLAG_READ);
+ bool const try_titles(!titles.empty() && !titles_file.open(titles));
+
+ // generate full list - initially ordered by shortname
+ populate_list(!try_titles);
+
+ // notify that BIOS count is valie
+ notify_available(AVAIL_BIOS_COUNT);
+
+ // try to load localised descriptions
+ if (try_titles)
+ {
+ load_titles(titles_file);
+
+ // populate parent descriptions while still ordered by shortname
+ // already done on the first pass if built-in titles are used
+ populate_parents();
+ }
+
+ // system names are finalised now
+ notify_available(AVAIL_SYSTEM_NAMES);
+
+ // get rid of the "empty" driver - we don't need positions to line up any more
+ m_sorted_list.reserve(m_systems.size() - 1);
+ auto const empty(driver_list::find(GAME_NAME(___empty)));
+ for (ui_system_info &info : m_systems)
+ {
+ if (info.index != empty)
+ m_sorted_list.emplace_back(info);
+ }
+
+ // sort drivers and notify
+ std::collate<wchar_t> const &coll = std::use_facet<std::collate<wchar_t> >(std::locale());
+ auto const compare_names =
+ [&coll] (std::wstring const &wx, std::wstring const &wy) -> bool
+ {
+ return 0 > coll.compare(wx.data(), wx.data() + wx.size(), wy.data(), wy.data() + wy.size());
+ };
+ std::stable_sort(
+ m_sorted_list.begin(),
+ m_sorted_list.end(),
+ [&compare_names] (ui_system_info const &lhs, ui_system_info const &rhs)
+ {
+ game_driver const &x(*lhs.driver);
+ game_driver const &y(*rhs.driver);
+
+ if (!lhs.is_clone && !rhs.is_clone)
+ {
+ return compare_names(
+ lhs.reading_description.empty() ? wstring_from_utf8(lhs.description) : lhs.reading_description,
+ rhs.reading_description.empty() ? wstring_from_utf8(rhs.description) : rhs.reading_description);
+ }
+ else if (lhs.is_clone && rhs.is_clone)
+ {
+ if (!std::strcmp(x.parent, y.parent))
+ {
+ return compare_names(
+ lhs.reading_description.empty() ? wstring_from_utf8(lhs.description) : lhs.reading_description,
+ rhs.reading_description.empty() ? wstring_from_utf8(rhs.description) : rhs.reading_description);
+ }
+ else
+ {
+ return compare_names(
+ lhs.reading_parent.empty() ? wstring_from_utf8(lhs.parent) : lhs.reading_parent,
+ rhs.reading_parent.empty() ? wstring_from_utf8(rhs.parent) : rhs.reading_parent);
+ }
+ }
+ else if (!lhs.is_clone && rhs.is_clone)
+ {
+ if (!std::strcmp(x.name, y.parent))
+ {
+ return true;
+ }
+ else
+ {
+ return compare_names(
+ lhs.reading_description.empty() ? wstring_from_utf8(lhs.description) : lhs.reading_description,
+ rhs.reading_parent.empty() ? wstring_from_utf8(rhs.parent) : rhs.reading_parent);
+ }
+ }
+ else
+ {
+ if (!std::strcmp(x.parent, y.name))
+ {
+ return false;
+ }
+ else
+ {
+ return compare_names(
+ lhs.reading_parent.empty() ? wstring_from_utf8(lhs.parent) : lhs.reading_parent,
+ rhs.reading_description.empty() ? wstring_from_utf8(rhs.description) : rhs.reading_description);
+ }
+ }
+ });
+ notify_available(AVAIL_SORTED_LIST);
+
+ // sort manufacturers and years
+ m_filter_data.finalise();
+ notify_available(AVAIL_FILTER_DATA);
+
+ // convert shortnames to UCS-4
+ for (ui_system_info &info : m_sorted_list)
+ info.ucs_shortname = ustr_from_utf8(normalize_unicode(info.driver->name, unicode_normalization_form::D, true));
+ notify_available(AVAIL_UCS_SHORTNAME);
+
+ // convert descriptions to UCS-4
+ for (ui_system_info &info : m_sorted_list)
+ info.ucs_description = ustr_from_utf8(normalize_unicode(info.description, unicode_normalization_form::D, true));
+ notify_available(AVAIL_UCS_DESCRIPTION);
+
+ // convert "<manufacturer> <description>" to UCS-4
+ std::string buf;
+ for (ui_system_info &info : m_sorted_list)
+ {
+ buf.assign(info.driver->manufacturer);
+ buf.append(1, ' ');
+ buf.append(info.description);
+ info.ucs_manufacturer_description = ustr_from_utf8(normalize_unicode(buf, unicode_normalization_form::D, true));
+ }
+ notify_available(AVAIL_UCS_MANUF_DESC);
+
+ // convert default descriptions to UCS-4
+ if (try_titles)
+ {
+ for (ui_system_info &info : m_sorted_list)
+ {
+ std::string_view const fullname(info.driver->type.fullname());
+ if (info.description != fullname)
+ info.ucs_default_description = ustr_from_utf8(normalize_unicode(fullname, unicode_normalization_form::D, true));
+ }
+ }
+ notify_available(AVAIL_UCS_DFLT_DESC);
+
+ // convert "<manufacturer> <default description>" to UCS-4
+ if (try_titles)
+ {
+ for (ui_system_info &info : m_sorted_list)
+ {
+ std::string_view const fullname(info.driver->type.fullname());
+ if (info.description != fullname)
+ {
+ buf.assign(info.driver->manufacturer);
+ buf.append(1, ' ');
+ buf.append(fullname);
+ info.ucs_manufacturer_default_description = ustr_from_utf8(normalize_unicode(buf, unicode_normalization_form::D, true));
+ }
+ }
+ }
+ notify_available(AVAIL_UCS_MANUF_DFLT_DESC);
+}
+
+
+void system_list::populate_list(bool copydesc)
+{
+ m_systems.reserve(driver_list::total());
+ std::unordered_set<std::string> manufacturers, years;
+ for (int x = 0; x < driver_list::total(); ++x)
+ {
+ game_driver const &driver(driver_list::driver(x));
+ ui_system_info &ins(m_systems.emplace_back(driver, x, false));
+ if (&driver != &GAME_NAME(___empty))
+ {
+ if (driver.flags & machine_flags::IS_BIOS_ROOT)
+ ++m_bios_count;
+
+ if ((driver.parent[0] != '0') || driver.parent[1])
+ {
+ auto const parentindex(driver_list::find(driver.parent));
+ if (copydesc)
+ {
+ if (0 <= parentindex)
+ {
+ game_driver const &parentdriver(driver_list::driver(parentindex));
+ ins.is_clone = !(parentdriver.flags & machine_flags::IS_BIOS_ROOT);
+ ins.parent = parentdriver.type.fullname();
+ }
+ else
+ {
+ ins.is_clone = false;
+ ins.parent = driver.parent;
+ }
+ }
+ else
+ {
+ ins.is_clone = (0 <= parentindex) && !(driver_list::driver(parentindex).flags & machine_flags::IS_BIOS_ROOT);
+ }
+ }
+
+ if (copydesc)
+ ins.description = driver.type.fullname();
+
+ m_filter_data.add_manufacturer(driver.manufacturer);
+ m_filter_data.add_year(driver.year);
+ }
+ }
+}
+
+
+void system_list::load_titles(util::core_file &file)
+{
+ char readbuf[1024];
+ std::string convbuf;
+ while (file.gets(readbuf, std::size(readbuf)))
+ {
+ // shortname, description, and description reading separated by tab
+ auto const eoln(
+ std::find_if(
+ std::begin(readbuf),
+ std::end(readbuf),
+ [] (char ch) { return !ch || ('\n' == ch) || ('\r' == ch); }));
+ auto const split(std::find(std::begin(readbuf), eoln, '\t'));
+ if (eoln == split)
+ continue;
+ std::string_view const shortname(readbuf, split - readbuf);
+
+ // find matching system - still sorted by shortname at this point
+ auto const found(
+ std::lower_bound(
+ m_systems.begin(),
+ m_systems.end(),
+ shortname,
+ [] (ui_system_info const &a, std::string_view const &b)
+ {
+ return a.driver->name < b;
+ }));
+ if ((m_systems.end() == found) || (shortname != found->driver->name))
+ {
+ //osd_printf_verbose("System '%s' not found\n", shortname); very spammy for single-driver builds
+ continue;
+ }
+
+ // find the end of the description
+ auto const descstart(std::next(split));
+ auto const descend(std::find(descstart, eoln, '\t'));
+ auto const description(strtrimspace(std::string_view(descstart, descend - descstart)));
+ if (description.empty())
+ {
+ osd_printf_warning("Empty translated description for system '%s'\n", shortname);
+ }
+ else if (!found->description.empty())
+ {
+ osd_printf_warning(
+ "Multiple translated descriptions for system '%s' ('%s' and '%s')\n",
+ shortname,
+ found->description,
+ description);
+ }
+ else
+ {
+ found->description = description;
+ }
+
+ // populate the reading if it's present
+ if (eoln == descend)
+ continue;
+ auto const readstart(std::next(descend));
+ auto const readend(std::find(readstart, eoln, '\t'));
+ auto const reading(strtrimspace(std::string_view(readstart, readend - readstart)));
+ if (reading.empty())
+ {
+ osd_printf_warning("Empty translated description reading for system '%s'\n", shortname);
+ }
+ else
+ {
+ found->reading_description = wstring_from_utf8(reading);
+ found->ucs_reading_description = ustr_from_utf8(normalize_unicode(reading, unicode_normalization_form::D, true));
+ convbuf.assign(found->driver->manufacturer);
+ convbuf.append(1, ' ');
+ convbuf.append(reading);
+ found->ucs_manufacturer_reading_description = ustr_from_utf8(normalize_unicode(convbuf, unicode_normalization_form::D, true));
+ }
+ }
+
+ // fill in untranslated descriptions
+ for (ui_system_info &info : m_systems)
+ {
+ if (info.description.empty())
+ info.description = info.driver->type.fullname();
+ }
+}
+
+
+void system_list::populate_parents()
+{
+ for (ui_system_info &info : m_systems)
+ {
+ if ((info.driver->parent[0] != '0') || info.driver->parent[1])
+ {
+ auto const found(
+ std::lower_bound(
+ m_systems.begin(),
+ m_systems.end(),
+ std::string_view(info.driver->parent),
+ [] (ui_system_info const &a, std::string_view const &b)
+ {
+ return a.driver->name < b;
+ }));
+ if (m_systems.end() != found)
+ {
+ info.parent = found->description;
+ info.reading_parent = found->reading_description;
+ }
+ else
+ {
+ info.parent = info.driver->parent;
+ }
+ }
+ }
+}
+
+} // namespace ui
diff --git a/src/frontend/mame/ui/systemlist.h b/src/frontend/mame/ui/systemlist.h
new file mode 100644
index 00000000000..6c3ee17dad7
--- /dev/null
+++ b/src/frontend/mame/ui/systemlist.h
@@ -0,0 +1,122 @@
+// license:BSD-3-Clause
+// copyright-holders:Vas Crabb
+/***************************************************************************
+
+ ui/systemlist.h
+
+ Persistent system list data.
+
+***************************************************************************/
+#ifndef MAME_FRONTEND_UI_SYSTEMLIST_H
+#define MAME_FRONTEND_UI_SYSTEMLIST_H
+
+#pragma once
+
+#include "ui/utils.h"
+
+#include <atomic>
+#include <condition_variable>
+#include <functional>
+#include <memory>
+#include <mutex>
+#include <string>
+#include <thread>
+#include <vector>
+
+
+class ui_options;
+
+
+namespace ui {
+
+class system_list
+{
+public:
+ enum available : unsigned
+ {
+ AVAIL_NONE = 0U,
+ AVAIL_SYSTEM_NAMES = 1U << 0,
+ AVAIL_SORTED_LIST = 1U << 1,
+ AVAIL_BIOS_COUNT = 1U << 2,
+ AVAIL_UCS_SHORTNAME = 1U << 3,
+ AVAIL_UCS_DESCRIPTION = 1U << 4,
+ AVAIL_UCS_MANUF_DESC = 1U << 5,
+ AVAIL_UCS_DFLT_DESC = 1U << 6,
+ AVAIL_UCS_MANUF_DFLT_DESC = 1U << 7,
+ AVAIL_FILTER_DATA = 1U << 8
+ };
+
+ using system_vector = std::vector<ui_system_info>;
+ using system_reference = std::reference_wrapper<ui_system_info>;
+ using system_reference_vector = std::vector<system_reference>;
+
+ void cache_data(ui_options const &options);
+
+ void reset_cache();
+
+ bool is_available(available desired) const
+ {
+ return (m_available.load(std::memory_order_acquire) & desired) == desired;
+ }
+
+ void wait_available(available desired);
+
+ system_vector const &systems()
+ {
+ wait_available(AVAIL_SYSTEM_NAMES);
+ return m_systems;
+ }
+
+ system_reference_vector const &sorted_list()
+ {
+ wait_available(AVAIL_SORTED_LIST);
+ return m_sorted_list;
+ }
+
+ int bios_count()
+ {
+ wait_available(AVAIL_BIOS_COUNT);
+ return m_bios_count;
+ }
+
+ bool unavailable_systems()
+ {
+ wait_available(AVAIL_SORTED_LIST);
+ return std::find_if(m_sorted_list.begin(), m_sorted_list.end(), [] (ui_system_info const &info) { return !info.available; }) != m_sorted_list.end();
+ }
+
+ machine_filter_data &filter_data()
+ {
+ wait_available(AVAIL_FILTER_DATA);
+ return m_filter_data;
+ }
+
+ static system_list &instance();
+
+private:
+ system_list();
+ ~system_list();
+
+ void notify_available(available value);
+ void do_cache_data(std::string const &datpath, std::string const &titles);
+ void populate_list(bool copydesc);
+ void load_titles(util::core_file &file);
+ void populate_parents();
+
+ // synchronisation
+ std::mutex m_mutex;
+ std::condition_variable m_condition;
+ std::unique_ptr<std::thread> m_thread;
+ std::atomic<bool> m_started;
+ std::atomic<unsigned> m_available;
+
+ // data
+ system_vector m_systems;
+ system_reference_vector m_sorted_list;
+ machine_filter_data m_filter_data;
+ int m_bios_count;
+};
+
+} // namespace ui
+
+#endif // MAME_FRONTEND_UI_SYSTEMLIST_H
diff --git a/src/frontend/mame/ui/ui.cpp b/src/frontend/mame/ui/ui.cpp
index 2e5f999da08..c2f50d8c33a 100644
--- a/src/frontend/mame/ui/ui.cpp
+++ b/src/frontend/mame/ui/ui.cpp
@@ -21,6 +21,7 @@
#include "ui/menu.h"
#include "ui/sliders.h"
#include "ui/state.h"
+#include "ui/systemlist.h"
#include "ui/viewgfx.h"
#include "imagedev/cassette.h"
@@ -194,6 +195,9 @@ void mame_ui_manager::init()
{
load_ui_options();
+ // start loading system names as early as possible
+ ui::system_list::instance().cache_data(options());
+
// initialize the other UI bits
ui::menu::init(machine(), options());
ui_gfx_init(machine());
@@ -1259,7 +1263,7 @@ uint32_t mame_ui_manager::handler_ingame(render_container &container)
machine().set_ui_active(!machine().ui_active());
// display a popup indicating the new status
- std::string const name = machine().input().seq_name(machine().ioport().type_seq(IPT_UI_TOGGLE_UI));
+ std::string const name = get_general_input_setting(IPT_UI_TOGGLE_UI);
if (machine().ui_active())
popup_time(2, _("UI controls enabled\nUse %1$s to toggle"), name);
else
@@ -1450,13 +1454,14 @@ uint32_t mame_ui_manager::handler_confirm_quit(render_container &container)
uint32_t state = 0;
// get the text for 'UI Select'
- std::string ui_select_text = machine().input().seq_name(machine().ioport().type_seq(IPT_UI_SELECT, 0, SEQ_TYPE_STANDARD));
+ std::string ui_select_text = get_general_input_setting(IPT_UI_SELECT);
// get the text for 'UI Cancel'
- std::string ui_cancel_text = machine().input().seq_name(machine().ioport().type_seq(IPT_UI_CANCEL, 0, SEQ_TYPE_STANDARD));
+ std::string ui_cancel_text = get_general_input_setting(IPT_UI_CANCEL);
// assemble the quit message
- std::string quit_message = string_format(_("Are you sure you want to quit?\n\n"
+ std::string quit_message = string_format(
+ _("Are you sure you want to quit?\n\n"
"Press ''%1$s'' to quit,\n"
"Press ''%2$s'' to return to emulation."),
ui_select_text,
@@ -2252,6 +2257,19 @@ void mame_ui_manager::menu_reset()
ui::menu::stack_reset(machine());
}
+
+//-------------------------------------------------
+// get_general_input_setting - get the current
+// default setting for an input type (useful for
+// prompting the user)
+//-------------------------------------------------
+
+std::string mame_ui_manager::get_general_input_setting(ioport_type type, int player, input_seq_type seqtype)
+{
+ return machine().input().seq_name(machine().ioport().type_seq(type, player, seqtype));
+}
+
+
void ui_colors::refresh(const ui_options &options)
{
m_border_color = options.border_color();
diff --git a/src/frontend/mame/ui/ui.h b/src/frontend/mame/ui/ui.h
index 44727703a20..4cd6292a764 100644
--- a/src/frontend/mame/ui/ui.h
+++ b/src/frontend/mame/ui/ui.h
@@ -230,6 +230,9 @@ public:
return *result;
}
+ // helper for getting a general input setting - used for instruction text
+ std::string get_general_input_setting(ioport_type type, int player = 0, input_seq_type seqtype = SEQ_TYPE_STANDARD);
+
private:
using handler_callback_func = std::function<uint32_t (render_container &)>;
using device_feature_set = std::set<std::pair<std::string, std::string> >;
diff --git a/src/frontend/mame/ui/utils.cpp b/src/frontend/mame/ui/utils.cpp
index c8d65fbe665..95002ce6482 100644
--- a/src/frontend/mame/ui/utils.cpp
+++ b/src/frontend/mame/ui/utils.cpp
@@ -2043,6 +2043,8 @@ ui_software_info::ui_software_info(
, available(false)
{
// show the list/item here
+ infotext.append(longname);
+ infotext.append("\t\n\n");
infotext.append(_("swlist-info", "Software list/item"));
infotext.append(1, '\n');
infotext.append(listname);