summaryrefslogtreecommitdiffstatshomepage
path: root/src/frontend/mame/ui/inputmap.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/frontend/mame/ui/inputmap.cpp')
-rw-r--r--src/frontend/mame/ui/inputmap.cpp1208
1 files changed, 449 insertions, 759 deletions
diff --git a/src/frontend/mame/ui/inputmap.cpp b/src/frontend/mame/ui/inputmap.cpp
index eb90215520e..b135780a08f 100644
--- a/src/frontend/mame/ui/inputmap.cpp
+++ b/src/frontend/mame/ui/inputmap.cpp
@@ -9,145 +9,140 @@
*********************************************************************/
#include "emu.h"
+#include "ui/inputmap.h"
#include "uiinput.h"
#include "ui/ui.h"
-#include "ui/menu.h"
-#include "ui/inputmap.h"
#include <algorithm>
namespace ui {
-/***************************************************************************
- CONSTANTS
-***************************************************************************/
-
-#define MAX_PHYSICAL_DIPS 10
-#define MAX_INPUT_PORTS 32
-#define MAX_BITS_PER_PORT 32
-
-/* DIP switch rendering parameters */
-#define DIP_SWITCH_HEIGHT 0.05f
-#define DIP_SWITCH_SPACING 0.01f
-#define SINGLE_TOGGLE_SWITCH_FIELD_WIDTH 0.025f
-#define SINGLE_TOGGLE_SWITCH_WIDTH 0.020f
-/* make the switch 80% of the width space and 1/2 of the switch height */
-#define PERCENTAGE_OF_HALF_FIELD_USED 0.80f
-#define SINGLE_TOGGLE_SWITCH_HEIGHT ((DIP_SWITCH_HEIGHT / 2) * PERCENTAGE_OF_HALF_FIELD_USED)
-
-
/*-------------------------------------------------
- menu_input_groups_populate - populate the
- input groups menu
+ menu_input_groups - handle the input groups
+ menu
-------------------------------------------------*/
menu_input_groups::menu_input_groups(mame_ui_manager &mui, render_container &container) : menu(mui, container)
{
+ set_heading(_("Input Assignments (general)"));
}
-void menu_input_groups::populate(float &customtop, float &custombottom)
+menu_input_groups::~menu_input_groups()
{
- int player;
+}
- /* build up the menu */
- item_append(_("User Interface"), "", 0, (void *)(IPG_UI + 1));
- for (player = 0; player < MAX_PLAYERS; player++)
+void menu_input_groups::populate()
+{
+ // build up the menu
+ item_append(_("User Interface"), 0, (void *)uintptr_t(IPG_UI + 1));
+ for (int player = 0; player < MAX_PLAYERS; player++)
{
- auto s = string_format("Player %d Controls", player + 1);
- item_append(s, "", 0, (void *)(uintptr_t)(IPG_PLAYER1 + player + 1));
+ auto s = string_format(_("Player %1$d Controls"), player + 1);
+ item_append(s, 0, (void *)uintptr_t(IPG_PLAYER1 + player + 1));
}
- item_append(_("Other Controls"), "", 0, (void *)(uintptr_t)(IPG_OTHER + 1));
+ item_append(_("Other Controls"), 0, (void *)uintptr_t(IPG_OTHER + 1));
+ item_append(menu_item_type::SEPARATOR);
}
-menu_input_groups::~menu_input_groups()
+bool menu_input_groups::handle(event const *ev)
{
-}
-
-/*-------------------------------------------------
- menu_input_groups - handle the input groups
- menu
--------------------------------------------------*/
+ if (ev && (ev->iptkey == IPT_UI_SELECT))
+ {
+ menu::stack_push<menu_input_general>(
+ ui(),
+ container(),
+ int(uintptr_t(ev->itemref) - 1),
+ util::string_format(_("Input Assignments (%1$s)"), ev->item->text()));
+ }
-void menu_input_groups::handle()
-{
- /* process the menu */
- const event *menu_event = process(0);
- if (menu_event != nullptr && menu_event->iptkey == IPT_UI_SELECT)
- menu::stack_push<menu_input_general>(ui(), container(), int((long long)(menu_event->itemref)-1));
+ return false;
}
-
/*-------------------------------------------------
menu_input_general - handle the general
input menu
-------------------------------------------------*/
-menu_input_general::menu_input_general(mame_ui_manager &mui, render_container &container, int _group) : menu_input(mui, container)
+menu_input_general::menu_input_general(mame_ui_manager &mui, render_container &container, int _group, std::string &&heading)
+ : menu_input(mui, container)
+ , group(_group)
{
- group = _group;
+ set_heading(std::move(heading));
}
-void menu_input_general::populate(float &customtop, float &custombottom)
+menu_input_general::~menu_input_general()
{
- input_item_data *itemlist = nullptr;
+}
- /* iterate over the input ports and add menu items */
- for (input_type_entry &entry : machine().ioport().types())
+void menu_input_general::menu_activated()
+{
+ // scripts can change settings out from under us
+ reset(reset_options::REMEMBER_POSITION);
+}
- /* add if we match the group and we have a valid name */
- if (entry.group() == group && entry.name() != nullptr && entry.name()[0] != 0)
- {
- input_seq_type seqtype;
+void menu_input_general::populate()
+{
+ if (data.empty())
+ {
+ assert(!pollingitem);
- /* loop over all sequence types */
- for (seqtype = SEQ_TYPE_STANDARD; seqtype < SEQ_TYPE_TOTAL; ++seqtype)
+ // iterate over the input ports and add menu items
+ for (const input_type_entry &entry : machine().ioport().types())
+ {
+ // add if we match the group and we have a valid name
+ if (entry.group() == group)
{
- /* build an entry for the standard sequence */
- input_item_data *item = (input_item_data *)m_pool_alloc(sizeof(*item));
- memset(item, 0, sizeof(*item));
- item->ref = &entry;
- if(pollingitem && pollingref == &entry && pollingseq == seqtype)
- pollingitem = item;
- item->seqtype = seqtype;
- item->seq = machine().ioport().type_seq(entry.type(), entry.player(), seqtype);
- item->defseq = &entry.defseq(seqtype);
- item->group = entry.group();
- item->type = ioport_manager::type_is_analog(entry.type()) ? (INPUT_TYPE_ANALOG + seqtype) : INPUT_TYPE_DIGITAL;
- item->is_optional = false;
- item->name = entry.name();
- item->owner_name = nullptr;
- item->next = itemlist;
- itemlist = item;
-
- /* stop after one, unless we're analog */
- if (item->type == INPUT_TYPE_DIGITAL)
- break;
+ std::string name = entry.name();
+ if (!name.empty())
+ {
+ // loop over all sequence types
+ for (input_seq_type seqtype = SEQ_TYPE_STANDARD; seqtype < SEQ_TYPE_TOTAL; ++seqtype)
+ {
+ // build an entry for the standard sequence
+ input_item_data &item(data.emplace_back());
+ item.ref = &entry;
+ item.seqtype = seqtype;
+ item.seq = machine().ioport().type_seq(entry.type(), entry.player(), seqtype);
+ item.defseq = &entry.defseq(seqtype);
+ item.group = entry.group();
+ item.type = ioport_manager::type_is_analog(entry.type()) ? (INPUT_TYPE_ANALOG + seqtype) : INPUT_TYPE_DIGITAL;
+ item.is_optional = false;
+ item.name = name;
+ item.owner = nullptr;
+
+ // stop after one, unless we're analog
+ if (item.type == INPUT_TYPE_DIGITAL)
+ break;
+ }
+ }
}
}
-
-
- // first count the number of items
- int numitems = 0;
- for (input_item_data const *item = itemlist; item != nullptr; item = item->next)
- numitems++;
-
- // now allocate an array of items and fill it up
- std::vector<input_item_data *> itemarray(numitems);
- int curitem = numitems;
- for (input_item_data *item = itemlist; item != nullptr; item = item->next)
- itemarray[--curitem] = item;
+ }
+ else
+ {
+ for (input_item_data &item : data)
+ {
+ const input_type_entry &entry(*reinterpret_cast<const input_type_entry *>(item.ref));
+ item.seq = machine().ioport().type_seq(entry.type(), entry.player(), item.seqtype);
+ }
+ }
// populate the menu in a standard fashion
- populate_sorted(std::move(itemarray));
+ populate_sorted();
+ item_append(menu_item_type::SEPARATOR);
}
-menu_input_general::~menu_input_general()
+void menu_input_general::update_input(input_item_data &seqchangeditem)
{
+ const input_type_entry &entry = *reinterpret_cast<const input_type_entry *>(seqchangeditem.ref);
+ machine().ioport().set_type_seq(entry.type(), entry.player(), seqchangeditem.seqtype, seqchangeditem.seq);
+ seqchangeditem.seq = machine().ioport().type_seq(entry.type(), entry.player(), seqchangeditem.seqtype);
}
+
/*-------------------------------------------------
menu_input_specific - handle the game-specific
input menu
@@ -155,115 +150,155 @@ menu_input_general::~menu_input_general()
menu_input_specific::menu_input_specific(mame_ui_manager &mui, render_container &container) : menu_input(mui, container)
{
+ set_heading(_("Input Assignments (this system)"));
+}
+
+menu_input_specific::~menu_input_specific()
+{
}
-void menu_input_specific::populate(float &customtop, float &custombottom)
+void menu_input_specific::menu_activated()
{
- input_item_data *itemlist = nullptr;
+ // scripts can change settings out from under us
+ assert(!pollingitem);
+ data.clear();
+ reset(reset_options::REMEMBER_POSITION);
+}
- /* iterate over the input ports and add menu items */
- for (auto &port : machine().ioport().ports())
+void menu_input_specific::populate()
+{
+ if (data.empty())
{
- for (ioport_field &field : port.second->fields())
- {
- ioport_type_class type_class = field.type_class();
+ assert(!pollingitem);
- /* add if we match the group and we have a valid name */
- if (field.enabled() && (type_class == INPUT_CLASS_CONTROLLER || type_class == INPUT_CLASS_MISC || type_class == INPUT_CLASS_KEYBOARD))
+ // iterate over the input ports and add menu items
+ for (auto &port : machine().ioport().ports())
+ {
+ for (ioport_field &field : port.second->fields())
{
- /* loop over all sequence types */
- for (input_seq_type seqtype = SEQ_TYPE_STANDARD; seqtype < SEQ_TYPE_TOTAL; ++seqtype)
+ const ioport_type_class type_class = field.type_class();
+
+ // add if it's enabled and it's a system-specific class
+ if (field.enabled() && (type_class == INPUT_CLASS_CONTROLLER || type_class == INPUT_CLASS_MISC || type_class == INPUT_CLASS_KEYBOARD))
{
- /* build an entry for the standard sequence */
- input_item_data *item = (input_item_data *)m_pool_alloc(sizeof(*item));
- memset(item, 0, sizeof(*item));
- item->ref = &field;
- item->seqtype = seqtype;
- if(pollingitem && pollingref == item->ref && pollingseq == seqtype)
- pollingitem = item;
- item->seq = field.seq(seqtype);
- item->defseq = &field.defseq(seqtype);
- item->group = machine().ioport().type_group(field.type(), field.player());
- item->type = field.is_analog() ? (INPUT_TYPE_ANALOG + seqtype) : INPUT_TYPE_DIGITAL;
- item->is_optional = field.optional();
- item->name = field.name();
- item->owner_name = field.device().tag();
- item->next = itemlist;
- itemlist = item;
-
- /* stop after one, unless we're analog */
- if (item->type == INPUT_TYPE_DIGITAL)
- break;
+ // loop over all sequence types
+ for (input_seq_type seqtype = SEQ_TYPE_STANDARD; seqtype < SEQ_TYPE_TOTAL; ++seqtype)
+ {
+ // build an entry for the standard sequence
+ input_item_data &item(data.emplace_back());
+ item.ref = &field;
+ item.seqtype = seqtype;
+ item.seq = field.seq(seqtype);
+ item.defseq = &field.defseq(seqtype);
+ item.group = machine().ioport().type_group(field.type(), field.player());
+ item.type = field.is_analog() ? (INPUT_TYPE_ANALOG + seqtype) : INPUT_TYPE_DIGITAL;
+ item.is_optional = field.optional();
+ item.name = field.name();
+ item.owner = &field.device();
+
+ // stop after one, unless we're analog
+ if (item.type == INPUT_TYPE_DIGITAL)
+ break;
+ }
}
}
}
- }
- // first count the number of items
- int numitems = 0;
- for (input_item_data const *item = itemlist; item != nullptr; item = item->next)
- numitems++;
-
- // now allocate an array of items and fill it up
- std::vector<input_item_data *> itemarray(numitems);
- int curitem = 0;
- for (input_item_data *item = itemlist; item != nullptr; item = item->next)
- itemarray[curitem++] = item;
-
- // sort it
- std::sort(itemarray.begin(), itemarray.end(), [](const input_item_data *i1, const input_item_data *i2) {
- int cmp = strcmp(i1->owner_name, i2->owner_name);
- if (cmp < 0)
- return true;
- if (cmp > 0)
- return false;
- if (i1->group < i2->group)
- return true;
- if (i1->group > i2->group)
- return false;
- const ioport_field &field1 = *reinterpret_cast<const ioport_field *>(i1->ref);
- const ioport_field &field2 = *reinterpret_cast<const ioport_field *>(i2->ref);
- if (field1.type() < field2.type())
- return true;
- if (field1.type() > field2.type())
- return false;
- std::vector<char32_t> codes1 = field1.keyboard_codes(0);
- std::vector<char32_t> codes2 = field2.keyboard_codes(0);
- if (!codes1.empty() && (codes2.empty() || codes1[0] < codes2[0]))
- return true;
- if (!codes2.empty() && (codes1.empty() || codes1[0] > codes2[0]))
- return false;
- cmp = strcmp(i1->name, i2->name);
- if (cmp < 0)
- return true;
- if (cmp > 0)
- return false;
- return i1->type < i2->type;
- });
+ // sort it
+ std::sort(
+ data.begin(),
+ data.end(),
+ [] (const input_item_data &i1, const input_item_data &i2)
+ {
+ int cmp = strcmp(i1.owner->tag(), i2.owner->tag());
+ if (cmp < 0)
+ return true;
+ if (cmp > 0)
+ return false;
+ if (i1.group < i2.group)
+ return true;
+ if (i1.group > i2.group)
+ return false;
+ const ioport_field &field1 = *reinterpret_cast<const ioport_field *>(i1.ref);
+ const ioport_field &field2 = *reinterpret_cast<const ioport_field *>(i2.ref);
+ if (field1.type() < field2.type())
+ return true;
+ if (field1.type() > field2.type())
+ return false;
+ std::vector<char32_t> codes1 = field1.keyboard_codes(0);
+ std::vector<char32_t> codes2 = field2.keyboard_codes(0);
+ if (!codes1.empty() && (codes2.empty() || codes1[0] < codes2[0]))
+ return true;
+ if (!codes2.empty() && (codes1.empty() || codes1[0] > codes2[0]))
+ return false;
+ cmp = i1.name.compare(i2.name);
+ if (cmp < 0)
+ return true;
+ if (cmp > 0)
+ return false;
+ return i1.type < i2.type;
+ });
+ }
+ else
+ {
+ for (input_item_data &item : data)
+ {
+ const ioport_field &field(*reinterpret_cast<const ioport_field *>(item.ref));
+ item.seq = field.seq(item.seqtype);
+ }
+ }
// populate the menu in a standard fashion
- populate_sorted(std::move(itemarray));
+ if (!data.empty())
+ populate_sorted();
+ else
+ item_append(_("[no assignable inputs are enabled]"), FLAG_DISABLE, nullptr);
+
+ item_append(menu_item_type::SEPARATOR);
}
-menu_input_specific::~menu_input_specific()
+void menu_input_specific::update_input(input_item_data &seqchangeditem)
{
+ ioport_field::user_settings settings;
+
+ // yeah, the const_cast is naughty, but we know we stored a non-const reference in it
+ ioport_field const &field(*reinterpret_cast<ioport_field const *>(seqchangeditem.ref));
+ field.get_user_settings(settings);
+ settings.seq[seqchangeditem.seqtype] = seqchangeditem.seq;
+ if (seqchangeditem.seq.is_default())
+ settings.cfg[seqchangeditem.seqtype].clear();
+ else if (!seqchangeditem.seq.length())
+ settings.cfg[seqchangeditem.seqtype] = "NONE";
+ else
+ settings.cfg[seqchangeditem.seqtype] = machine().input().seq_to_tokens(seqchangeditem.seq);
+ const_cast<ioport_field &>(field).set_user_settings(settings);
+ seqchangeditem.seq = field.seq(seqchangeditem.seqtype);
}
+
/*-------------------------------------------------
menu_input - display a menu for inputs
-------------------------------------------------*/
-menu_input::menu_input(mame_ui_manager &mui, render_container &container) : menu(mui, container), record_next(false)
+
+menu_input::menu_input(mame_ui_manager &mui, render_container &container)
+ : menu(mui, container)
+ , data()
+ , pollingitem(nullptr)
+ , seq_poll()
+ , errormsg()
+ , erroritem(nullptr)
+ , lastitem(nullptr)
+ , record_next(false)
+ , modified_ticks(0)
{
- lastitem = nullptr;
- pollingitem = nullptr;
- pollingref = nullptr;
- pollingseq = SEQ_TYPE_STANDARD;
+ set_process_flags(PROCESS_LR_ALWAYS);
}
menu_input::~menu_input()
{
}
+
/*-------------------------------------------------
toggle_none_default - toggle between "NONE"
and the default item
@@ -271,665 +306,320 @@ menu_input::~menu_input()
void menu_input::toggle_none_default(input_seq &selected_seq, input_seq &original_seq, const input_seq &selected_defseq)
{
- /* if we used to be "none", toggle to the default value */
- if (original_seq.length() == 0)
- selected_seq = selected_defseq;
-
- /* otherwise, toggle to "none" */
- else
+ if (original_seq.empty()) // if we used to be "none", toggle to the default value
+ selected_seq.set_default();
+ else // otherwise, toggle to "none"
selected_seq.reset();
}
-void menu_input::handle()
-{
- input_item_data *seqchangeditem = nullptr;
- const event *menu_event;
- int invalidate = false;
-
- /* process the menu */
- menu_event = process((pollingitem != nullptr) ? PROCESS_NOKEYS : 0);
-
- /* if we are polling, handle as a special case */
- if (pollingitem != nullptr)
- {
- input_item_data *item = pollingitem;
-
- /* if UI_CANCEL is pressed, abort */
- if (machine().ui_input().pressed(IPT_UI_CANCEL))
- {
- pollingitem = nullptr;
- record_next = false;
- toggle_none_default(item->seq, starting_seq, *item->defseq);
- seqchangeditem = item;
- }
-
- /* poll again; if finished, update the sequence */
- if (machine().input().seq_poll())
- {
- pollingitem = nullptr;
- record_next = true;
- item->seq = machine().input().seq_poll_final();
- seqchangeditem = item;
- }
- }
- /* otherwise, handle the events */
- else if (menu_event != nullptr && menu_event->itemref != nullptr)
- {
- input_item_data *item = (input_item_data *)menu_event->itemref;
- switch (menu_event->iptkey)
- {
- /* an item was selected: begin polling */
- case IPT_UI_SELECT:
- pollingitem = item;
- lastitem = item;
- starting_seq = item->seq;
- machine().input().seq_poll_start((item->type == INPUT_TYPE_ANALOG) ? ITEM_CLASS_ABSOLUTE : ITEM_CLASS_SWITCH, record_next ? &item->seq : nullptr);
- invalidate = true;
- break;
-
- /* if the clear key was pressed, reset the selected item */
- case IPT_UI_CLEAR:
- toggle_none_default(item->seq, item->seq, *item->defseq);
- record_next = false;
- seqchangeditem = item;
- break;
- }
-
- /* if the selection changed, reset the "record next" flag */
- if (item != lastitem)
- record_next = false;
- lastitem = item;
- }
-
- /* if the sequence changed, update it */
- if (seqchangeditem != nullptr)
- {
- update_input(seqchangeditem);
-
- /* invalidate the menu to force an update */
- invalidate = true;
- }
-
- /* if the menu is invalidated, clear it now */
- if (invalidate)
- {
- pollingref = nullptr;
- if (pollingitem != nullptr)
- {
- pollingref = pollingitem->ref;
- pollingseq = pollingitem->seqtype;
- }
- reset(reset_options::REMEMBER_POSITION);
- }
-}
-
-void menu_input_general::update_input(struct input_item_data *seqchangeditem)
-{
- const input_type_entry *entry = (const input_type_entry *)seqchangeditem->ref;
- machine().ioport().set_type_seq(entry->type(), entry->player(), seqchangeditem->seqtype, seqchangeditem->seq);
-}
-
-void menu_input_specific::update_input(struct input_item_data *seqchangeditem)
+void menu_input::recompute_metrics(uint32_t width, uint32_t height, float aspect)
{
- ioport_field::user_settings settings;
+ menu::recompute_metrics(width, height, aspect);
- ((ioport_field *)seqchangeditem->ref)->get_user_settings(settings);
- settings.seq[seqchangeditem->seqtype] = seqchangeditem->seq;
- ((ioport_field *)seqchangeditem->ref)->set_user_settings(settings);
+ // leave space for showing the input sequence below the menu
+ set_custom_space(0.0F, 2.0F * line_height() + 3.0F * tb_border());
}
-//-------------------------------------------------
-// populate_sorted - take a sorted list of
-// input_item_data objects and build up the
-// menu from them
-//-------------------------------------------------
-
-void menu_input::populate_sorted(std::vector<input_item_data *> &&itemarray)
+void menu_input::custom_render(uint32_t flags, void *selectedref, float top, float bottom, float origx1, float origy1, float origx2, float origy2)
{
- const char *nameformat[INPUT_TYPE_TOTAL] = { nullptr };
- std::string subtext;
- std::string prev_owner;
- bool first_entry = true;
-
- /* create a mini lookup table for name format based on type */
- nameformat[INPUT_TYPE_DIGITAL] = "%s";
- nameformat[INPUT_TYPE_ANALOG] = "%s Analog";
- nameformat[INPUT_TYPE_ANALOG_INC] = "%s Analog Inc";
- nameformat[INPUT_TYPE_ANALOG_DEC] = "%s Analog Dec";
-
- /* build the menu */
- for (input_item_data *item : itemarray)
+ if (pollingitem)
{
- uint32_t flags = 0;
-
- /* generate the name of the item itself, based off the base name and the type */
- assert(nameformat[item->type] != nullptr);
-
- if (item->owner_name && strcmp(item->owner_name, prev_owner.c_str()) != 0)
+ const std::string seqname = machine().input().seq_name(seq_poll->sequence());
+ char const *const text[] = { seqname.c_str() };
+ draw_text_box(
+ std::begin(text), std::end(text),
+ origx1, origx2, origy2 + tb_border(), origy2 + bottom,
+ text_layout::text_justify::CENTER, text_layout::word_wrapping::NEVER, false,
+ ui().colors().text_color(), ui().colors().background_color());
+ }
+ else
+ {
+ if (erroritem && (selectedref != erroritem))
{
- if (first_entry)
- first_entry = false;
- else
- item_append(menu_item_type::SEPARATOR);
- item_append(string_format("[root%s]", item->owner_name), "", 0, nullptr);
- prev_owner.assign(item->owner_name);
+ errormsg.clear();
+ erroritem = nullptr;
}
- std::string text = string_format(nameformat[item->type], item->name);
- if (item->is_optional)
- text = "(" + text + ")";
-
- /* if we're polling this item, use some spaces with left/right arrows */
- if (pollingref == item->ref && pollingseq == item->seqtype)
+ if (erroritem)
{
- subtext.assign(" ");
- flags |= FLAG_LEFT_ARROW | FLAG_RIGHT_ARROW;
+ char const *const text[] = { errormsg.c_str() };
+ draw_text_box(
+ std::begin(text), std::end(text),
+ origx1, origx2, origy2 + tb_border(), origy2 + bottom,
+ text_layout::text_justify::CENTER, text_layout::word_wrapping::NEVER, false,
+ ui().colors().text_color(), UI_RED_COLOR);
}
-
- /* otherwise, generate the sequence name and invert it if different from the default */
- else
+ else if (selectedref)
{
- subtext = machine().input().seq_name(item->seq);
- flags |= (item->seq != *item->defseq) ? FLAG_INVERT : 0;
+ const input_item_data &item = *reinterpret_cast<input_item_data *>(selectedref);
+ if ((INPUT_TYPE_ANALOG != item.type) && machine().input().seq_pressed(item.seq))
+ {
+ char const *const text[] = { _("Pressed") };
+ draw_text_box(
+ std::begin(text), std::end(text),
+ origx1, origx2, origy2 + tb_border(), origy2 + bottom,
+ text_layout::text_justify::CENTER, text_layout::word_wrapping::NEVER, false,
+ ui().colors().text_color(), ui().colors().background_color());
+ }
+ else
+ {
+ char const *const text[] = {
+ record_next ? appendprompt.c_str() : assignprompt.c_str(),
+ (!item.seq.empty() || item.defseq->empty()) ? clearprompt.c_str() : defaultprompt.c_str() };
+ draw_text_box(
+ std::begin(text), std::end(text),
+ origx1, origx2, origy2 + tb_border(), origy2 + bottom,
+ text_layout::text_justify::CENTER, text_layout::word_wrapping::NEVER, false,
+ ui().colors().text_color(), ui().colors().background_color());
+ }
}
-
- /* add the item */
- item_append(text, subtext, flags, item);
}
}
-
-/*-------------------------------------------------
- menu_settings_dip_switches - handle the DIP
- switches menu
--------------------------------------------------*/
-
-menu_settings_dip_switches::menu_settings_dip_switches(mame_ui_manager &mui, render_container &container) : menu_settings(mui, container, IPT_DIPSWITCH)
+bool menu_input::handle(event const *ev)
{
-}
-
-menu_settings_dip_switches::~menu_settings_dip_switches()
-{
-}
-
-/*-------------------------------------------------
- menu_settings_driver_config - handle the
- driver config menu
--------------------------------------------------*/
-
-menu_settings_driver_config::menu_settings_driver_config(mame_ui_manager &mui, render_container &container) : menu_settings(mui, container, IPT_CONFIG)
-{
-}
-
-menu_settings_driver_config::~menu_settings_driver_config()
-{
-}
-
-/*-------------------------------------------------
- menu_settings_common - handle one of the
- switches menus
--------------------------------------------------*/
+ input_item_data *seqchangeditem = nullptr;
+ bool invalidate = false;
+ bool redraw = false;
-void menu_settings::handle()
-{
// process the menu
- const event *menu_event = process(0);
-
- // handle events
- if (menu_event != nullptr && menu_event->itemref != nullptr)
+ if (pollingitem)
{
- // reset
- if ((uintptr_t)menu_event->itemref == 1)
+ // if we are polling, handle as a special case
+ input_item_data *const item = pollingitem;
+
+ // prevent race condition between ui_input().pressed() and poll()
+ if (modified_ticks == 0 && seq_poll->modified())
+ modified_ticks = osd_ticks();
+
+ if (machine().ui_input().pressed(IPT_UI_CANCEL))
{
- if (menu_event->iptkey == IPT_UI_SELECT)
- machine().schedule_hard_reset();
+ // if UI_CANCEL is pressed, abort and abandon changes
+ pollingitem = nullptr;
+ set_process_flags(PROCESS_LR_ALWAYS);
+ invalidate = true;
+ seq_poll.reset();
+ machine().ui_input().reset();
}
- // actual settings
- else
+ else if (seq_poll->poll()) // poll again; if finished, update the sequence
{
- ioport_field *field = (ioport_field *)menu_event->itemref;
- ioport_field::user_settings settings;
- int changed = false;
-
- switch (menu_event->iptkey)
+ pollingitem = nullptr;
+ set_process_flags(PROCESS_LR_ALWAYS);
+ if (seq_poll->valid())
+ {
+ record_next = true;
+ item->seq = seq_poll->sequence();
+ seqchangeditem = item;
+ }
+ else
{
- /* if selected, reset to default value */
- case IPT_UI_SELECT:
- field->get_user_settings(settings);
- settings.value = field->defvalue();
- field->set_user_settings(settings);
- changed = true;
- break;
-
- /* left goes to previous setting */
- case IPT_UI_LEFT:
- field->select_previous_setting();
- changed = true;
- break;
-
- /* right goes to next setting */
- case IPT_UI_RIGHT:
- field->select_next_setting();
- changed = true;
- break;
+ // entered invalid sequence - abandon change
+ invalidate = true;
+ errormsg = _("Invalid combination entered");
+ erroritem = item;
}
-
- /* if anything changed, rebuild the menu, trying to stay on the same field */
- if (changed)
- reset(reset_options::REMEMBER_REF);
+ seq_poll.reset();
+ machine().ui_input().reset();
+ }
+ else
+ {
+ // always redraw to ensure it updates as soon as possible in response to changes
+ redraw = true;
}
}
-}
-
-
-/*-------------------------------------------------
- menu_settings_populate - populate one of the
- switches menus
--------------------------------------------------*/
-
-menu_settings::menu_settings(mame_ui_manager &mui, render_container &container, uint32_t _type) : menu(mui, container), diplist(nullptr), dipcount(0)
-{
- type = _type;
-}
+ else if (ev && ev->itemref)
+ {
+ // otherwise, handle the events
+ input_item_data &item = *reinterpret_cast<input_item_data *>(ev->itemref);
+ input_item_data *newsel = &item;
+ switch (ev->iptkey)
+ {
+ case IPT_UI_SELECT: // an item was selected: begin polling
+ set_process_flags(PROCESS_NOKEYS);
+ errormsg.clear();
+ erroritem = nullptr;
+ modified_ticks = 0;
+ pollingitem = &item;
+ lastitem = &item;
+ starting_seq = item.seq;
+ if (INPUT_TYPE_ANALOG == item.type)
+ seq_poll.reset(new axis_sequence_poller(machine().input()));
+ else
+ seq_poll.reset(new switch_sequence_poller(machine().input()));
+ if (record_next)
+ seq_poll->start(item.seq);
+ else
+ seq_poll->start();
+ invalidate = true;
+ break;
+
+ case IPT_UI_CLEAR: // if the clear key was pressed, reset the selected item
+ errormsg.clear();
+ erroritem = nullptr;
+ toggle_none_default(item.seq, item.seq, *item.defseq);
+ record_next = false;
+ seqchangeditem = &item;
+ break;
-void menu_settings::populate(float &customtop, float &custombottom)
-{
- dip_descriptor **diplist_tailptr;
- std::string prev_owner;
- bool first_entry = true;
-
- /* reset the dip switch tracking */
- dipcount = 0;
- diplist = nullptr;
- diplist_tailptr = &diplist;
-
- /* loop over input ports and set up the current values */
- for (auto &port : machine().ioport().ports())
- for (ioport_field &field : port.second->fields())
- if (field.type() == type && field.enabled())
+ case IPT_UI_PREV_GROUP:
{
- if (!field.settings().empty())
+ auto current = std::distance(data.data(), &item);
+ bool found_break = false;
+ while (0 < current)
{
- uint32_t flags = 0;
-
- /* set the left/right flags appropriately */
- if (field.has_previous_setting())
- flags |= FLAG_LEFT_ARROW;
- if (field.has_next_setting())
- flags |= FLAG_RIGHT_ARROW;
-
- /* add the menu item */
- if (strcmp(field.device().tag(), prev_owner.c_str()) != 0)
+ if (!found_break)
{
- if (first_entry)
- first_entry = false;
- else
- item_append(menu_item_type::SEPARATOR);
- string_format("[root%s]", field.device().tag());
- item_append(string_format("[root%s]", field.device().tag()), "", 0, nullptr);
- prev_owner.assign(field.device().tag());
+ if (data[--current].owner != item.owner)
+ found_break = true;
+ }
+ else if (data[current].owner != data[current - 1].owner)
+ {
+ newsel = &data[current];
+ set_selection(newsel);
+ set_top_line(selected_index() - 1);
+ break;
+ }
+ else
+ {
+ --current;
+ }
+ if (found_break && !current)
+ {
+ newsel = &data[current];
+ set_selection(newsel);
+ set_top_line(selected_index() - 1);
+ break;
}
-
- item_append(field.name(), field.setting_name(), flags, (void *)&field);
}
+ }
+ break;
- /* for DIP switches, build up the model */
- if (type == IPT_DIPSWITCH && !field.diplocations().empty())
+ case IPT_UI_NEXT_GROUP:
+ {
+ auto current = std::distance(data.data(), &item);
+ while (data.size() > ++current)
{
- ioport_field::user_settings settings;
- uint32_t accummask = field.mask();
-
- /* get current settings */
- field.get_user_settings(settings);
-
- /* iterate over each bit in the field */
- for (const ioport_diplocation &diploc : field.diplocations())
+ if (data[current].owner != item.owner)
{
- uint32_t mask = accummask & ~(accummask - 1);
- dip_descriptor *dip;
-
- /* find the matching switch name */
- for (dip = diplist; dip != nullptr; dip = dip->next)
- if (dip->owner == &field.device() && strcmp(dip->name, diploc.name()) == 0)
- break;
-
- /* allocate new if none */
- if (dip == nullptr)
- {
- dip = (dip_descriptor *)m_pool_alloc(sizeof(*dip));
- dip->next = nullptr;
- dip->name = diploc.name();
- dip->owner = &field.device();
- dip->mask = dip->state = 0;
- *diplist_tailptr = dip;
- diplist_tailptr = &dip->next;
- dipcount++;
- }
-
- /* apply the bits */
- dip->mask |= 1 << (diploc.number() - 1);
- if (((settings.value & mask) != 0 && !diploc.inverted()) || ((settings.value & mask) == 0 && diploc.inverted()))
- dip->state |= 1 << (diploc.number() - 1);
-
- /* clear the relevant bit in the accumulated mask */
- accummask &= ~mask;
+ newsel = &data[current];
+ set_selection(newsel);
+ set_top_line(selected_index() - 1);
+ break;
}
}
}
- if (type == IPT_DIPSWITCH)
- custombottom = dipcount ? dipcount * (DIP_SWITCH_HEIGHT + DIP_SWITCH_SPACING) + DIP_SWITCH_SPACING : 0;
-
- item_append(menu_item_type::SEPARATOR);
- item_append(_("Reset"), "", 0, (void *)1);
-}
-
-menu_settings::~menu_settings()
-{
-}
-
-/*-------------------------------------------------
- menu_settings_custom_render - perform our special
- rendering
--------------------------------------------------*/
-
-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
- if (bottom == 0)
- return;
-
- // add borders
- y1 = y2 + ui().box_tb_border();
- y2 = y1 + bottom;
-
- // draw extra menu area
- ui().draw_outlined_box(container(), x1, y1, x2, y2, ui().colors().background_color());
- y1 += (float)DIP_SWITCH_SPACING;
-
- // iterate over DIP switches
- for (dip_descriptor *dip = diplist; dip != nullptr; dip = dip->next)
- {
- uint32_t selectedmask = 0;
+ break;
+ }
- // determine the mask of selected bits
- if ((uintptr_t)selectedref != 1)
+ // if the selection changed, reset the "record next" flag
+ if (newsel != lastitem)
{
- ioport_field *field = (ioport_field *)selectedref;
-
- if (field != nullptr && !field->diplocations().empty())
- for (const ioport_diplocation &diploc : field->diplocations())
- if (dip->owner == &field->device() && strcmp(dip->name, diploc.name()) == 0)
- selectedmask |= 1 << (diploc.number() - 1);
+ if (erroritem)
+ {
+ errormsg.clear();
+ erroritem = nullptr;
+ }
+ record_next = false;
+ lastitem = &item;
+ redraw = true;
}
- // draw one switch
- custom_render_one(x1, y1, x2, y1 + DIP_SWITCH_HEIGHT, dip, selectedmask);
- y1 += (float)(DIP_SWITCH_SPACING + DIP_SWITCH_HEIGHT);
+ // flip between set and append
+ // not very discoverable, but with the prompt it isn't completely opaque
+ if ((IPT_UI_LEFT == ev->iptkey) || (IPT_UI_RIGHT == ev->iptkey))
+ {
+ if (erroritem)
+ {
+ errormsg.clear();
+ erroritem = nullptr;
+ }
+ else if (record_next || !item.seq.empty())
+ {
+ record_next = !record_next;
+ }
+ redraw = true;
+ }
}
-}
-
-
-/*-------------------------------------------------
- menu_settings_custom_render_one - draw a single
- DIP switch
--------------------------------------------------*/
-void menu_settings_dip_switches::custom_render_one(float x1, float y1, float x2, float y2, const dip_descriptor *dip, uint32_t selectedmask)
-{
- float switch_field_width = SINGLE_TOGGLE_SWITCH_FIELD_WIDTH * container().manager().ui_aspect();
- float switch_width = SINGLE_TOGGLE_SWITCH_WIDTH * container().manager().ui_aspect();
- int numtoggles, toggle;
- float switch_toggle_gap;
- float y1_off, y1_on;
-
- /* determine the number of toggles in the DIP */
- numtoggles = 32 - count_leading_zeros(dip->mask);
-
- /* center based on the number of switches */
- x1 += (x2 - x1 - numtoggles * switch_field_width) / 2;
-
- /* draw the dip switch name */
- ui().draw_text_full(container(),
- dip->name,
- 0,
- y1 + (DIP_SWITCH_HEIGHT - ui().target_font_height()) / 2,
- x1 - ui().get_string_width(" "),
- ui::text_layout::RIGHT,
- ui::text_layout::NEVER,
- mame_ui_manager::NORMAL,
- ui().colors().text_color(),
- PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA),
- nullptr ,
- nullptr);
-
- /* compute top and bottom for on and off positions */
- switch_toggle_gap = ((DIP_SWITCH_HEIGHT/2) - SINGLE_TOGGLE_SWITCH_HEIGHT)/2;
- y1_off = y1 + UI_LINE_WIDTH + switch_toggle_gap;
- y1_on = y1 + DIP_SWITCH_HEIGHT/2 + switch_toggle_gap;
-
- /* iterate over toggles */
- for (toggle = 0; toggle < numtoggles; toggle++)
+ // if the sequence changed, update it
+ if (seqchangeditem)
{
- float innerx1;
+ update_input(*seqchangeditem);
- /* first outline the switch */
- ui().draw_outlined_box(container(), x1, y1, x1 + switch_field_width, y2, ui().colors().background_color());
-
- /* compute x1/x2 for the inner filled in switch */
- innerx1 = x1 + (switch_field_width - switch_width) / 2;
+ // invalidate the menu to force an update
+ invalidate = true;
+ }
- /* see if the switch is actually used */
- if (dip->mask & (1 << toggle))
- {
- float innery1 = (dip->state & (1 << toggle)) ? y1_on : y1_off;
- container().add_rect(innerx1, innery1, innerx1 + switch_width, innery1 + SINGLE_TOGGLE_SWITCH_HEIGHT,
- (selectedmask & (1 << toggle)) ? ui().colors().dipsw_color() : ui().colors().text_color(),
- PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
- }
- else
- {
- container().add_rect(innerx1, y1_off, innerx1 + switch_width, y1_on + SINGLE_TOGGLE_SWITCH_HEIGHT,
- ui().colors().unavailable_color(),
- PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
- }
+ // if the menu is invalidated, clear it now
+ if (invalidate)
+ reset(reset_options::REMEMBER_POSITION);
- /* advance to the next switch */
- x1 += switch_field_width;
- }
+ return redraw && !invalidate;
}
-/*-------------------------------------------------
- menu_analog - handle the analog settings menu
--------------------------------------------------*/
+//-------------------------------------------------
+// populate_sorted - take a sorted list of
+// input_item_data objects and build up the
+// menu from them
+//-------------------------------------------------
-void menu_analog::handle()
+void menu_input::populate_sorted()
{
- /* process the menu */
- const event *menu_event = process(PROCESS_LR_REPEAT);
+ const char *nameformat[INPUT_TYPE_TOTAL] = { nullptr };
- /* handle events */
- if (menu_event != nullptr && menu_event->itemref != nullptr)
+ // create a mini lookup table for name format based on type
+ nameformat[INPUT_TYPE_DIGITAL] = "%1$s";
+ nameformat[INPUT_TYPE_ANALOG] = _("input-name", "%1$s Analog");
+ nameformat[INPUT_TYPE_ANALOG_INC] = _("input-name", "%1$s Analog Inc");
+ nameformat[INPUT_TYPE_ANALOG_DEC] = _("input-name", "%1$s Analog Dec");
+
+ // build the menu
+ std::string text, subtext;
+ const device_t *prev_owner = nullptr;
+ for (input_item_data &item : data)
{
- analog_item_data *data = (analog_item_data *)menu_event->itemref;
- int newval = data->cur;
+ // generate the name of the item itself, based off the base name and the type
+ assert(nameformat[item.type] != nullptr);
- switch (menu_event->iptkey)
+ if (item.owner && (item.owner != prev_owner))
{
- /* if selected, reset to default value */
- case IPT_UI_SELECT:
- newval = data->defvalue;
- break;
-
- /* left decrements */
- case IPT_UI_LEFT:
- newval -= machine().input().code_pressed(KEYCODE_LSHIFT) ? 10 : 1;
- break;
-
- /* right increments */
- case IPT_UI_RIGHT:
- newval += machine().input().code_pressed(KEYCODE_LSHIFT) ? 10 : 1;
- break;
+ if (item.owner->owner())
+ item_append(string_format(_("%1$s [root%2$s]"), item.owner->type().fullname(), item.owner->tag()), FLAG_UI_HEADING | FLAG_DISABLE, nullptr);
+ else
+ item_append(string_format(_("[root%1$s]"), item.owner->tag()), FLAG_UI_HEADING | FLAG_DISABLE, nullptr);
+ prev_owner = item.owner;
}
- /* clamp to range */
- if (newval < data->min)
- newval = data->min;
- if (newval > data->max)
- newval = data->max;
+ text = string_format(nameformat[item.type], item.name);
+ if (item.is_optional)
+ text = "(" + text + ")";
- /* if things changed, update */
- if (newval != data->cur)
+ uint32_t flags = 0;
+ if (&item == pollingitem)
{
- ioport_field::user_settings settings;
-
- /* get the settings and set the new value */
- data->field->get_user_settings(settings);
- switch (data->type)
- {
- case ANALOG_ITEM_KEYSPEED: settings.delta = newval; break;
- case ANALOG_ITEM_CENTERSPEED: settings.centerdelta = newval; break;
- case ANALOG_ITEM_REVERSE: settings.reverse = newval; break;
- case ANALOG_ITEM_SENSITIVITY: settings.sensitivity = newval; break;
- }
- data->field->set_user_settings(settings);
-
- /* rebuild the menu */
- reset(reset_options::REMEMBER_POSITION);
+ // if we're polling this item, use some spaces with left/right arrows
+ subtext = " ";
+ flags |= FLAG_LEFT_ARROW | FLAG_RIGHT_ARROW;
+ }
+ else
+ {
+ // otherwise, generate the sequence name and invert it if different from the default
+ subtext = machine().input().seq_name(item.seq);
+ flags |= (item.seq != *item.defseq) ? FLAG_INVERT : 0;
}
- }
-}
-
-
-/*-------------------------------------------------
- menu_analog_populate - populate the analog
- settings menu
--------------------------------------------------*/
-
-menu_analog::menu_analog(mame_ui_manager &mui, render_container &container) : menu(mui, container)
-{
-}
-
-void menu_analog::populate(float &customtop, float &custombottom)
-{
- std::string prev_owner;
- bool first_entry = true;
-
- /* loop over input ports and add the items */
- for (auto &port : machine().ioport().ports())
- for (ioport_field &field : port.second->fields())
- if (field.is_analog() && field.enabled())
- {
- ioport_field::user_settings settings;
- int use_autocenter = false;
- int type;
-
- /* based on the type, determine if we enable autocenter */
- switch (field.type())
- {
- case IPT_POSITIONAL:
- case IPT_POSITIONAL_V:
- if (field.analog_wraps())
- break;
-
- case IPT_AD_STICK_X:
- case IPT_AD_STICK_Y:
- case IPT_AD_STICK_Z:
- case IPT_PADDLE:
- case IPT_PADDLE_V:
- case IPT_PEDAL:
- case IPT_PEDAL2:
- case IPT_PEDAL3:
- use_autocenter = true;
- break;
-
- default:
- break;
- }
-
- /* get the user settings */
- field.get_user_settings(settings);
- /* iterate over types */
- for (type = 0; type < ANALOG_ITEM_COUNT; type++)
- if (type != ANALOG_ITEM_CENTERSPEED || use_autocenter)
- {
- analog_item_data *data;
- uint32_t flags = 0;
- std::string text;
- std::string subtext;
- if (strcmp(field.device().tag(), prev_owner.c_str()) != 0)
- {
- if (first_entry)
- first_entry = false;
- else
- item_append(menu_item_type::SEPARATOR);
- item_append(string_format("[root%s]", field.device().tag()), "", 0, nullptr);
- prev_owner.assign(field.device().tag());
- }
-
- /* allocate a data item for tracking what this menu item refers to */
- data = (analog_item_data *)m_pool_alloc(sizeof(*data));
- data->field = &field;
- data->type = type;
-
- /* determine the properties of this item */
- switch (type)
- {
- default:
- case ANALOG_ITEM_KEYSPEED:
- text = string_format("%s Digital Speed", field.name());
- subtext = string_format("%d", settings.delta);
- data->min = 0;
- data->max = 255;
- data->cur = settings.delta;
- data->defvalue = field.delta();
- break;
-
- case ANALOG_ITEM_CENTERSPEED:
- text = string_format("%s Autocenter Speed", field.name());
- subtext = string_format("%d", settings.centerdelta);
- data->min = 0;
- data->max = 255;
- data->cur = settings.centerdelta;
- data->defvalue = field.centerdelta();
- break;
-
- case ANALOG_ITEM_REVERSE:
- text = string_format("%s Reverse", field.name());
- subtext.assign(settings.reverse ? "On" : "Off");
- data->min = 0;
- data->max = 1;
- data->cur = settings.reverse;
- data->defvalue = field.analog_reverse();
- break;
-
- case ANALOG_ITEM_SENSITIVITY:
- text = string_format("%s Sensitivity", field.name());
- subtext = string_format("%d", settings.sensitivity);
- data->min = 1;
- data->max = 255;
- data->cur = settings.sensitivity;
- data->defvalue = field.sensitivity();
- break;
- }
-
- /* put on arrows */
- if (data->cur > data->min)
- flags |= FLAG_LEFT_ARROW;
- if (data->cur < data->max)
- flags |= FLAG_RIGHT_ARROW;
-
- /* append a menu item */
- item_append(std::move(text), std::move(subtext), flags, data);
- }
- }
-}
+ // add the item
+ item_append(std::move(text), std::move(subtext), flags, &item);
+ }
-menu_analog::~menu_analog()
-{
+ // pre-format messages
+ 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));
}
} // namespace ui