summaryrefslogtreecommitdiffstatshomepage
path: root/src/frontend/mame/ui/ui.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/frontend/mame/ui/ui.cpp')
-rw-r--r--src/frontend/mame/ui/ui.cpp2738
1 files changed, 2738 insertions, 0 deletions
diff --git a/src/frontend/mame/ui/ui.cpp b/src/frontend/mame/ui/ui.cpp
new file mode 100644
index 00000000000..173eff6ad9c
--- /dev/null
+++ b/src/frontend/mame/ui/ui.cpp
@@ -0,0 +1,2738 @@
+// license:BSD-3-Clause
+// copyright-holders:Nicola Salmoria, Aaron Giles, Nathan Woods
+/*********************************************************************
+
+ ui.cpp
+
+ Functions used to handle MAME's user interface.
+
+*********************************************************************/
+
+#include "emu.h"
+#include "mame.h"
+#include "emuopts.h"
+#include "mameopts.h"
+#include "video/vector.h"
+#include "machine/laserdsc.h"
+#include "render.h"
+#include "luaengine.h"
+#include "cheat.h"
+#include "rendfont.h"
+#include "uiinput.h"
+#include "ui/ui.h"
+#include "ui/menu.h"
+#include "ui/mainmenu.h"
+#include "ui/filemngr.h"
+#include "ui/sliders.h"
+#include "ui/viewgfx.h"
+#include "imagedev/cassette.h"
+#include "image.h"
+
+
+/***************************************************************************
+ CONSTANTS
+***************************************************************************/
+
+enum
+{
+ LOADSAVE_NONE,
+ LOADSAVE_LOAD,
+ LOADSAVE_SAVE
+};
+
+#define MAX_SAVED_STATE_JOYSTICK 4
+
+
+/***************************************************************************
+ LOCAL VARIABLES
+***************************************************************************/
+
+// list of natural keyboard keys that are not associated with UI_EVENT_CHARs
+static const input_item_id non_char_keys[] =
+{
+ ITEM_ID_ESC,
+ ITEM_ID_F1,
+ ITEM_ID_F2,
+ ITEM_ID_F3,
+ ITEM_ID_F4,
+ ITEM_ID_F5,
+ ITEM_ID_F6,
+ ITEM_ID_F7,
+ ITEM_ID_F8,
+ ITEM_ID_F9,
+ ITEM_ID_F10,
+ ITEM_ID_F11,
+ ITEM_ID_F12,
+ ITEM_ID_NUMLOCK,
+ ITEM_ID_0_PAD,
+ ITEM_ID_1_PAD,
+ ITEM_ID_2_PAD,
+ ITEM_ID_3_PAD,
+ ITEM_ID_4_PAD,
+ ITEM_ID_5_PAD,
+ ITEM_ID_6_PAD,
+ ITEM_ID_7_PAD,
+ ITEM_ID_8_PAD,
+ ITEM_ID_9_PAD,
+ ITEM_ID_DEL_PAD,
+ ITEM_ID_PLUS_PAD,
+ ITEM_ID_MINUS_PAD,
+ ITEM_ID_INSERT,
+ ITEM_ID_DEL,
+ ITEM_ID_HOME,
+ ITEM_ID_END,
+ ITEM_ID_PGUP,
+ ITEM_ID_PGDN,
+ ITEM_ID_UP,
+ ITEM_ID_DOWN,
+ ITEM_ID_LEFT,
+ ITEM_ID_RIGHT,
+ ITEM_ID_PAUSE,
+ ITEM_ID_CANCEL
+};
+
+static const char *s_color_list[] = {
+ OPTION_UI_BORDER_COLOR,
+ OPTION_UI_BACKGROUND_COLOR,
+ OPTION_UI_GFXVIEWER_BG_COLOR,
+ OPTION_UI_UNAVAILABLE_COLOR,
+ OPTION_UI_TEXT_COLOR,
+ OPTION_UI_TEXT_BG_COLOR,
+ OPTION_UI_SUBITEM_COLOR,
+ OPTION_UI_CLONE_COLOR,
+ OPTION_UI_SELECTED_COLOR,
+ OPTION_UI_SELECTED_BG_COLOR,
+ OPTION_UI_MOUSEOVER_COLOR,
+ OPTION_UI_MOUSEOVER_BG_COLOR,
+ OPTION_UI_MOUSEDOWN_COLOR,
+ OPTION_UI_MOUSEDOWN_BG_COLOR,
+ OPTION_UI_DIPSW_COLOR,
+ OPTION_UI_SLIDER_COLOR
+};
+
+/***************************************************************************
+ GLOBAL VARIABLES
+***************************************************************************/
+
+// messagebox buffer
+std::string mame_ui_manager::messagebox_text;
+std::string mame_ui_manager::messagebox_poptext;
+rgb_t mame_ui_manager::messagebox_backcolor;
+
+// slider info
+std::vector<ui_menu_item> mame_ui_manager::slider_list;
+slider_state *mame_ui_manager::slider_current;
+
+
+/***************************************************************************
+ FUNCTION PROTOTYPES
+***************************************************************************/
+
+// slider controls
+static slider_state *slider_alloc(running_machine &machine, const char *title, INT32 minval, INT32 defval, INT32 maxval, INT32 incval, slider_update update, void *arg);
+static INT32 slider_volume(running_machine &machine, void *arg, int id, std::string *str, INT32 newval);
+static INT32 slider_mixervol(running_machine &machine, void *arg, int id, std::string *str, INT32 newval);
+static INT32 slider_adjuster(running_machine &machine, void *arg, int id, std::string *str, INT32 newval);
+static INT32 slider_overclock(running_machine &machine, void *arg, int id, std::string *str, INT32 newval);
+static INT32 slider_refresh(running_machine &machine, void *arg, int id, std::string *str, INT32 newval);
+static INT32 slider_brightness(running_machine &machine, void *arg, int id, std::string *str, INT32 newval);
+static INT32 slider_contrast(running_machine &machine, void *arg, int id, std::string *str, INT32 newval);
+static INT32 slider_gamma(running_machine &machine, void *arg, int id, std::string *str, INT32 newval);
+static INT32 slider_xscale(running_machine &machine, void *arg, int id, std::string *str, INT32 newval);
+static INT32 slider_yscale(running_machine &machine, void *arg, int id, std::string *str, INT32 newval);
+static INT32 slider_xoffset(running_machine &machine, void *arg, int id, std::string *str, INT32 newval);
+static INT32 slider_yoffset(running_machine &machine, void *arg, int id, std::string *str, INT32 newval);
+static INT32 slider_overxscale(running_machine &machine, void *arg, int id, std::string *str, INT32 newval);
+static INT32 slider_overyscale(running_machine &machine, void *arg, int id, std::string *str, INT32 newval);
+static INT32 slider_overxoffset(running_machine &machine, void *arg, int id, std::string *str, INT32 newval);
+static INT32 slider_overyoffset(running_machine &machine, void *arg, int id, std::string *str, INT32 newval);
+static INT32 slider_flicker(running_machine &machine, void *arg, int id, std::string *str, INT32 newval);
+static INT32 slider_beam_width_min(running_machine &machine, void *arg, int id, std::string *str, INT32 newval);
+static INT32 slider_beam_width_max(running_machine &machine, void *arg, int id, std::string *str, INT32 newval);
+static INT32 slider_beam_intensity_weight(running_machine &machine, void *arg, int id, std::string *str, INT32 newval);
+static std::string slider_get_screen_desc(screen_device &screen);
+#ifdef MAME_DEBUG
+static INT32 slider_crossscale(running_machine &machine, void *arg, int id, std::string *str, INT32 newval);
+static INT32 slider_crossoffset(running_machine &machine, void *arg, int id, std::string *str, INT32 newval);
+#endif
+
+
+/***************************************************************************
+ INLINE FUNCTIONS
+***************************************************************************/
+
+//-------------------------------------------------
+// load ui options
+//-------------------------------------------------
+
+static void load_ui_options(running_machine &machine)
+{
+ // parse the file
+ std::string error;
+ // attempt to open the output file
+ emu_file file(machine.options().ini_path(), OPEN_FLAG_READ);
+ if (file.open("ui.ini") == osd_file::error::NONE)
+ {
+ bool result = mame_machine_manager::instance()->ui().options().parse_ini_file((util::core_file&)file, OPTION_PRIORITY_MAME_INI, OPTION_PRIORITY_DRIVER_INI, error);
+ if (!result)
+ osd_printf_error("**Error loading ui.ini**");
+ }
+}
+
+//-------------------------------------------------
+// is_breakable_char - is a given unicode
+// character a possible line break?
+//-------------------------------------------------
+
+static inline int is_breakable_char(unicode_char ch)
+{
+ // regular spaces and hyphens are breakable
+ if (ch == ' ' || ch == '-')
+ return TRUE;
+
+ // In the following character sets, any character is breakable:
+ // Hiragana (3040-309F)
+ // Katakana (30A0-30FF)
+ // Bopomofo (3100-312F)
+ // Hangul Compatibility Jamo (3130-318F)
+ // Kanbun (3190-319F)
+ // Bopomofo Extended (31A0-31BF)
+ // CJK Strokes (31C0-31EF)
+ // Katakana Phonetic Extensions (31F0-31FF)
+ // Enclosed CJK Letters and Months (3200-32FF)
+ // CJK Compatibility (3300-33FF)
+ // CJK Unified Ideographs Extension A (3400-4DBF)
+ // Yijing Hexagram Symbols (4DC0-4DFF)
+ // CJK Unified Ideographs (4E00-9FFF)
+ if (ch >= 0x3040 && ch <= 0x9fff)
+ return TRUE;
+
+ // Hangul Syllables (AC00-D7AF) are breakable
+ if (ch >= 0xac00 && ch <= 0xd7af)
+ return TRUE;
+
+ // CJK Compatibility Ideographs (F900-FAFF) are breakable
+ if (ch >= 0xf900 && ch <= 0xfaff)
+ return TRUE;
+
+ return FALSE;
+}
+
+
+
+/***************************************************************************
+ CORE IMPLEMENTATION
+***************************************************************************/
+
+static const UINT32 mouse_bitmap[32*32] =
+{
+ 0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,
+ 0x09a46f30,0x81ac7c43,0x24af8049,0x00ad7d45,0x00a8753a,0x00a46f30,0x009f6725,0x009b611c,0x00985b14,0x0095560d,0x00935308,0x00915004,0x00904e02,0x008f4e01,0x008f4d00,0x008f4d00,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,
+ 0x00a16a29,0xa2aa783d,0xffbb864a,0xc0b0824c,0x5aaf7f48,0x09ac7b42,0x00a9773c,0x00a67134,0x00a26b2b,0x009e6522,0x009a5e19,0x00965911,0x0094550b,0x00925207,0x00915004,0x008f4e01,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,
+ 0x009a5e18,0x39a06827,0xffb97c34,0xffe8993c,0xffc88940,0xedac7c43,0x93ad7c44,0x2dac7c43,0x00ab793f,0x00a87438,0x00a46f30,0x00a06827,0x009c611d,0x00985c15,0x0095570e,0x00935309,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,
+ 0x00935308,0x00965810,0xcc9a5e19,0xffe78a21,0xfffb9929,0xfff49931,0xffd88e39,0xffb9813f,0xc9ac7c43,0x66ad7c44,0x0cac7a41,0x00a9773c,0x00a67134,0x00a26b2b,0x009e6522,0x009a5e19,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,
+ 0x008f4e01,0x00904e02,0x60925106,0xffba670a,0xfff88b11,0xfff98f19,0xfff99422,0xfff9982b,0xffe89434,0xffc9883c,0xf3ac7a41,0x9cad7c44,0x39ac7c43,0x00ab7a40,0x00a87539,0x00a56f31,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,
+ 0x008e4d00,0x008e4d00,0x098e4d00,0xea8f4d00,0xffee7f03,0xfff68407,0xfff6870d,0xfff78b15,0xfff78f1d,0xfff79426,0xfff49730,0xffd98d38,0xffbc823f,0xd2ac7c43,0x6fad7c44,0x12ac7b42,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,
+ 0x008e4d00,0x008e4d00,0x008e4c00,0x8a8e4c00,0xffc46800,0xfff37e00,0xfff37f02,0xfff38106,0xfff3830a,0xfff48711,0xfff48b19,0xfff58f21,0xfff5942b,0xffe79134,0xffcb863b,0xf9ac7a41,0xa5ac7c43,0x3fac7c43,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,
+ 0x008e4d00,0x008e4d00,0x008e4c00,0x218d4c00,0xfc8e4c00,0xffee7a00,0xfff07c00,0xfff17c00,0xfff17d02,0xfff17e04,0xfff18008,0xfff2830d,0xfff28614,0xfff38a1c,0xfff38f25,0xfff2932e,0xffd98b37,0xffbc813e,0xdbac7c43,0x78ad7c44,0x15ac7b42,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,
+ 0x008e4d00,0x008e4d00,0x008e4d00,0x008e4c00,0xb18d4c00,0xffcf6b00,0xffed7900,0xffed7900,0xffee7900,0xffee7a01,0xffee7a01,0xffee7b03,0xffee7c06,0xffef7e0a,0xffef8110,0xfff08618,0xfff08a20,0xfff18f2a,0xffe78f33,0xffcc863b,0xfcab7a40,0xaeac7c43,0x4bac7c43,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,
+ 0x008f4d00,0x008e4d00,0x008e4d00,0x008e4c00,0x488d4c00,0xffa85800,0xffe97500,0xffea7600,0xffea7600,0xffeb7600,0xffeb7600,0xffeb7600,0xffeb7701,0xffeb7702,0xffeb7804,0xffec7a07,0xffec7d0d,0xffec8013,0xffed851c,0xffee8a25,0xffee8f2e,0xffd98937,0xffbe813d,0xe4ab7a40,0x81ab7a40,0x1ba9763b,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,
+ 0x008f4d00,0x008e4d00,0x008e4d00,0x008e4c00,0x008d4c00,0xdb8d4c00,0xffd86c00,0xffe77300,0xffe77300,0xffe87300,0xffe87300,0xffe87300,0xffe87300,0xffe87300,0xffe87401,0xffe87401,0xffe87503,0xffe97606,0xffe9780a,0xffe97c10,0xffea7f16,0xffeb831d,0xffeb8623,0xffe48426,0xffc67725,0xffa5661f,0xb7985c15,0x54935309,0x038e4d00,0x00ffffff,0x00ffffff,0x00ffffff,
+ 0x008f4d00,0x008e4d00,0x008e4d00,0x008e4d00,0x008e4c00,0x6f8d4c00,0xffb25b00,0xffe36f00,0xffe47000,0xffe47000,0xffe57000,0xffe57000,0xffe57000,0xffe57000,0xffe57000,0xffe57000,0xffe57000,0xffe57000,0xffe57101,0xffe57000,0xffe47000,0xffe16e00,0xffde6c00,0xffd86900,0xffd06600,0xffc76200,0xffaa5500,0xff8a4800,0xea743f00,0x5a7a4200,0x00ffffff,0x00ffffff,
+ 0x008f4d00,0x008f4d00,0x008e4d00,0x008e4d00,0x008e4c00,0x0f8d4c00,0xf38d4c00,0xffdc6a00,0xffe16d00,0xffe16d00,0xffe26d00,0xffe26d00,0xffe26d00,0xffe26d00,0xffe26d00,0xffe16d00,0xffe06c00,0xffde6b00,0xffd96900,0xffd16500,0xffc76000,0xffb95900,0xffab5200,0xff9c4b00,0xff894300,0xff6b3600,0xf9512c00,0xa5542d00,0x3c5e3200,0x00ffffff,0x00ffffff,0x00ffffff,
+ 0x008f4d00,0x008f4d00,0x008e4d00,0x008e4d00,0x008e4c00,0x008d4c00,0x968d4c00,0xffbc5d00,0xffde6a00,0xffde6a00,0xffde6a00,0xffdf6a00,0xffdf6a00,0xffdf6a00,0xffde6a00,0xffdc6800,0xffd66600,0xffcc6100,0xffbf5b00,0xffaf5300,0xff9d4a00,0xff8a4200,0xff6d3500,0xff502900,0xe7402300,0x7b3f2200,0x15442500,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,
+ 0x008f4d00,0x008f4d00,0x008f4d00,0x008e4d00,0x008e4d00,0x008e4c00,0x2a8d4c00,0xff9b5000,0xffda6600,0xffdb6700,0xffdb6700,0xffdc6700,0xffdc6700,0xffdb6700,0xffd96500,0xffd16200,0xffc25b00,0xffad5100,0xff974700,0xff7f3c00,0xff602f00,0xff472500,0xbd3d2100,0x513d2100,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,
+ 0x008f4d00,0x008f4d00,0x008f4d00,0x008e4d00,0x008e4d00,0x008e4c00,0x008e4c00,0xc08d4c00,0xffc35c00,0xffd76300,0xffd76300,0xffd86300,0xffd86300,0xffd76300,0xffd06000,0xffc05800,0xffa54c00,0xff7f3b00,0xff582c00,0xf03f2200,0x903c2000,0x2a3e2100,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,
+ 0x008f4d00,0x008f4d00,0x008f4d00,0x008f4d00,0x008e4d00,0x008e4d00,0x008e4c00,0x548d4c00,0xffa55200,0xffd35f00,0xffd46000,0xffd46000,0xffd46000,0xffd25e00,0xffc65900,0xffac4e00,0xff833c00,0xe7472600,0x693c2000,0x0c3d2100,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,
+ 0x008f4d00,0x008f4d00,0x008f4d00,0x008f4d00,0x008e4d00,0x008e4d00,0x008e4c00,0x038d4c00,0xe48d4c00,0xffc95a00,0xffd15d00,0xffd15d00,0xffd15d00,0xffcb5a00,0xffb95200,0xff984300,0xff5f2e00,0x723f2200,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,
+ 0x008f4d00,0x008f4d00,0x008f4d00,0x008f4d00,0x008e4d00,0x008e4d00,0x008e4d00,0x008e4c00,0x7b8d4c00,0xffad5200,0xffce5a00,0xffce5a00,0xffcd5900,0xffc35500,0xffaa4a00,0xff853a00,0xf9472600,0x15432400,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,
+ 0x008f4d00,0x008f4d00,0x008f4d00,0x008f4d00,0x008f4d00,0x008e4d00,0x008e4d00,0x008e4c00,0x188d4c00,0xf98e4c00,0xffc95600,0xffcb5700,0xffc75500,0xffb94f00,0xff9b4200,0xff6c3100,0xab442500,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,
+ 0x008f4d00,0x008f4d00,0x008f4d00,0x008f4d00,0x008f4d00,0x008e4d00,0x008e4d00,0x008e4d00,0x008e4c00,0xa58d4c00,0xffb35000,0xffc75300,0xffc05000,0xffac4800,0xff8b3a00,0xff542a00,0x45462500,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,
+ 0x008f4d00,0x008f4d00,0x008f4d00,0x008f4d00,0x008f4d00,0x008f4d00,0x008e4d00,0x008e4d00,0x008e4c00,0x398d4c00,0xff994d00,0xffc24f00,0xffb74b00,0xff9e4000,0xff763200,0xde472600,0x03492800,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,
+ 0x008f4d00,0x008f4d00,0x008f4d00,0x008f4d00,0x008f4d00,0x008f4d00,0x008e4d00,0x008e4d00,0x008e4c00,0x008e4c00,0xcf8d4c00,0xffb24b00,0xffab4500,0xff8d3900,0xff5e2b00,0x7e452500,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,
+ 0x008f4d00,0x008f4d00,0x008f4d00,0x008f4d00,0x008f4d00,0x008f4d00,0x008e4d00,0x008e4d00,0x008e4d00,0x008e4c00,0x638d4c00,0xff984800,0xffa03f00,0xff7e3200,0xfc492800,0x1b472600,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,
+ 0x008f4d00,0x008f4d00,0x008f4d00,0x008f4d00,0x008f4d00,0x008f4d00,0x008f4d00,0x008e4d00,0x008e4d00,0x008e4c00,0x098b4b00,0xed824600,0xff903800,0xff692c00,0xb4462600,0x004c2900,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,
+ 0x008f4d00,0x008f4d00,0x008f4d00,0x008f4d00,0x008f4d00,0x008f4d00,0x008f4d00,0x008e4d00,0x008e4d00,0x008e4c00,0x008a4a00,0x8a7e4400,0xff793500,0xff572900,0x51472600,0x00542d00,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,
+ 0x008f4d00,0x008f4d00,0x008f4d00,0x008f4d00,0x008f4d00,0x008f4d00,0x008f4d00,0x008f4d00,0x008e4d00,0x008d4c00,0x00884900,0x247a4200,0xfc633500,0xe74f2a00,0x034d2900,0x005e3300,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,
+ 0x008f4d00,0x008f4d00,0x008f4d00,0x008f4d00,0x008f4d00,0x008f4d00,0x008f4d00,0x008f4d00,0x008e4d00,0x008d4c00,0x00884900,0x00794100,0xb4643600,0x87552e00,0x00593000,0x006b3900,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,
+ 0x008f4d00,0x008f4d00,0x008f4d00,0x008f4d00,0x008f4d00,0x008f4d00,0x008f4d00,0x008f4d00,0x008f4d00,0x008d4c00,0x00884900,0x007c4300,0x486d3b00,0x24643600,0x00693800,0x00774000,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,
+ 0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,
+ 0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff,0x00ffffff
+};
+
+
+//-------------------------------------------------
+// ctor - set up the user interface
+//-------------------------------------------------
+
+mame_ui_manager::mame_ui_manager(running_machine &machine)
+ : ui_manager(machine)
+{
+}
+
+void mame_ui_manager::init()
+{
+ load_ui_options(machine());
+ // initialize the other UI bits
+ ui_menu::init(machine());
+ ui_gfx_init(machine());
+
+ // reset instance variables
+ m_font = nullptr;
+ m_handler_callback = nullptr;
+ m_handler_param = 0;
+ m_single_step = false;
+ m_showfps = false;
+ m_showfps_end = 0;
+ m_show_profiler = false;
+ m_popup_text_end = 0;
+ m_mouse_arrow_texture = nullptr;
+ m_load_save_hold = false;
+
+ get_font_rows(&machine());
+ decode_ui_color(0, &machine());
+
+ // more initialization
+ set_handler(handler_messagebox, 0);
+ m_non_char_keys_down = std::make_unique<UINT8[]>((ARRAY_LENGTH(non_char_keys) + 7) / 8);
+ m_mouse_show = machine().system().flags & MACHINE_CLICKABLE_ARTWORK ? true : false;
+
+ // request a callback upon exiting
+ machine().add_notifier(MACHINE_NOTIFY_EXIT, machine_notify_delegate(FUNC(mame_ui_manager::exit), this));
+
+ // retrieve options
+ m_use_natural_keyboard = machine().options().natural_keyboard();
+ bitmap_argb32 *ui_mouse_bitmap = auto_alloc(machine(), bitmap_argb32(32, 32));
+ UINT32 *dst = &ui_mouse_bitmap->pix32(0);
+ memcpy(dst,mouse_bitmap,32*32*sizeof(UINT32));
+ m_mouse_arrow_texture = machine().render().texture_alloc();
+ m_mouse_arrow_texture->set_bitmap(*ui_mouse_bitmap, ui_mouse_bitmap->cliprect(), TEXFORMAT_ARGB32);
+}
+
+
+//-------------------------------------------------
+// exit - clean up ourselves on exit
+//-------------------------------------------------
+
+void mame_ui_manager::exit()
+{
+ // free the mouse texture
+ machine().render().texture_free(m_mouse_arrow_texture);
+ m_mouse_arrow_texture = nullptr;
+
+ // free the font
+ if (m_font != nullptr)
+ {
+ machine().render().font_free(m_font);
+ m_font = nullptr;
+ }
+}
+
+
+//-------------------------------------------------
+// initialize - initialize ui lists
+//-------------------------------------------------
+
+void mame_ui_manager::initialize(running_machine &machine)
+{
+ // initialize the on-screen display system
+ slider_list = slider_init(machine);
+ if (slider_list.size() > 0)
+ {
+ slider_current = reinterpret_cast<slider_state *>(slider_list[0].ref);
+ }
+ else
+ {
+ slider_current = nullptr;
+ }
+}
+
+
+//-------------------------------------------------
+// set_handler - set a callback/parameter
+// pair for the current UI handler
+//-------------------------------------------------
+
+UINT32 mame_ui_manager::set_handler(ui_callback callback, UINT32 param)
+{
+ m_handler_callback = callback;
+ m_handler_param = param;
+ return param;
+}
+
+
+//-------------------------------------------------
+// display_startup_screens - display the
+// various startup screens
+//-------------------------------------------------
+
+void mame_ui_manager::display_startup_screens(bool first_time)
+{
+ const int maxstate = 3;
+ int str = machine().options().seconds_to_run();
+ bool show_gameinfo = !machine().options().skip_gameinfo();
+ bool show_warnings = true, show_mandatory_fileman = true;
+ int state;
+
+ // disable everything if we are using -str for 300 or fewer seconds, or if we're the empty driver,
+ // or if we are debugging
+ if (!first_time || (str > 0 && str < 60*5) || &machine().system() == &GAME_NAME(___empty) || (machine().debug_flags & DEBUG_FLAG_ENABLED) != 0)
+ show_gameinfo = show_warnings = show_mandatory_fileman = FALSE;
+
+ #if defined(EMSCRIPTEN)
+ // also disable for the JavaScript port since the startup screens do not run asynchronously
+ show_gameinfo = show_warnings = FALSE;
+ #endif
+
+ // loop over states
+ set_handler(handler_ingame, 0);
+ for (state = 0; state < maxstate && !machine().scheduled_event_pending() && !ui_menu::stack_has_special_main_menu(); state++)
+ {
+ // default to standard colors
+ messagebox_backcolor = UI_BACKGROUND_COLOR;
+
+ // pick the next state
+ switch (state)
+ {
+ case 0:
+ if (show_warnings && warnings_string(messagebox_text).length() > 0)
+ {
+ set_handler(handler_messagebox_anykey, 0);
+ if (machine().system().flags & (MACHINE_WRONG_COLORS | MACHINE_IMPERFECT_COLORS | MACHINE_REQUIRES_ARTWORK | MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_KEYBOARD | MACHINE_NO_SOUND))
+ messagebox_backcolor = UI_YELLOW_COLOR;
+ if (machine().system().flags & (MACHINE_NOT_WORKING | MACHINE_UNEMULATED_PROTECTION | MACHINE_MECHANICAL))
+ messagebox_backcolor = UI_RED_COLOR;
+ }
+ break;
+
+ case 1:
+ if (show_gameinfo && game_info_astring(messagebox_text).length() > 0)
+ set_handler(handler_messagebox_anykey, 0);
+ break;
+
+ case 2:
+ if (show_mandatory_fileman && machine().image().mandatory_scan(messagebox_text).length() > 0)
+ {
+ std::string warning;
+ warning.assign(_("This driver requires images to be loaded in the following device(s): ")).append(messagebox_text.substr(0, messagebox_text.length() - 2));
+ ui_menu_file_manager::force_file_manager(machine(), &machine().render().ui_container(), warning.c_str());
+ }
+ break;
+ }
+
+ // clear the input memory
+ machine().input().reset_polling();
+ while (machine().input().poll_switches() != INPUT_CODE_INVALID) { }
+
+ // loop while we have a handler
+ while (m_handler_callback != handler_ingame && !machine().scheduled_event_pending() && !ui_menu::stack_has_special_main_menu())
+ {
+ machine().video().frame_update();
+ }
+
+ // clear the handler and force an update
+ set_handler(handler_ingame, 0);
+ machine().video().frame_update();
+ }
+
+ // if we're the empty driver, force the menus on
+ if (ui_menu::stack_has_special_main_menu())
+ set_handler(ui_menu::ui_handler, 0);
+}
+
+
+//-------------------------------------------------
+// set_startup_text - set the text to display
+// at startup
+//-------------------------------------------------
+
+void mame_ui_manager::set_startup_text(const char *text, bool force)
+{
+ static osd_ticks_t lastupdatetime = 0;
+ osd_ticks_t curtime = osd_ticks();
+
+ // copy in the new text
+ messagebox_text.assign(text);
+ messagebox_backcolor = UI_BACKGROUND_COLOR;
+
+ // don't update more than 4 times/second
+ if (force || (curtime - lastupdatetime) > osd_ticks_per_second() / 4)
+ {
+ lastupdatetime = curtime;
+ machine().video().frame_update();
+ }
+}
+
+
+//-------------------------------------------------
+// update_and_render - update the UI and
+// render it; called by video.c
+//-------------------------------------------------
+
+void mame_ui_manager::update_and_render(render_container *container)
+{
+ // always start clean
+ container->empty();
+
+ // if we're paused, dim the whole screen
+ if (machine().phase() >= MACHINE_PHASE_RESET && (single_step() || machine().paused()))
+ {
+ int alpha = (1.0f - machine().options().pause_brightness()) * 255.0f;
+ if (ui_menu::stack_has_special_main_menu())
+ alpha = 255;
+ if (alpha > 255)
+ alpha = 255;
+ if (alpha >= 0)
+ container->add_rect(0.0f, 0.0f, 1.0f, 1.0f, rgb_t(alpha,0x00,0x00,0x00), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
+ }
+
+ // render any cheat stuff at the bottom
+ if (machine().phase() >= MACHINE_PHASE_RESET)
+ {
+ mame_machine_manager::instance()->lua()->on_frame_done();
+ mame_machine_manager::instance()->cheat().render_text(*container);
+ }
+
+ // call the current UI handler
+ assert(m_handler_callback != nullptr);
+ m_handler_param = (*m_handler_callback)(machine(), container, m_handler_param);
+
+ // display any popup messages
+ if (osd_ticks() < m_popup_text_end)
+ draw_text_box(container, messagebox_poptext.c_str(), JUSTIFY_CENTER, 0.5f, 0.9f, messagebox_backcolor);
+ else
+ m_popup_text_end = 0;
+
+ // display the internal mouse cursor
+ if (m_mouse_show || (is_menu_active() && machine().options().ui_mouse()))
+ {
+ INT32 mouse_target_x, mouse_target_y;
+ bool mouse_button;
+ render_target *mouse_target = machine().ui_input().find_mouse(&mouse_target_x, &mouse_target_y, &mouse_button);
+
+ if (mouse_target != nullptr)
+ {
+ float mouse_y=-1,mouse_x=-1;
+ if (mouse_target->map_point_container(mouse_target_x, mouse_target_y, *container, mouse_x, mouse_y))
+ {
+ const float cursor_size = 0.6 * mame_machine_manager::instance()->ui().get_line_height();
+ container->add_quad(mouse_x, mouse_y, mouse_x + cursor_size*container->manager().ui_aspect(container), mouse_y + cursor_size, UI_TEXT_COLOR, m_mouse_arrow_texture, PRIMFLAG_ANTIALIAS(1) | PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
+ }
+ }
+ }
+
+ // cancel takes us back to the ingame handler
+ if (m_handler_param == UI_HANDLER_CANCEL)
+ set_handler(handler_ingame, 0);
+}
+
+
+//-------------------------------------------------
+// get_font - return the UI font
+//-------------------------------------------------
+
+render_font *mame_ui_manager::get_font()
+{
+ // allocate the font and messagebox string
+ if (m_font == nullptr)
+ m_font = machine().render().font_alloc(machine().options().ui_font());
+ return m_font;
+}
+
+
+//-------------------------------------------------
+// get_line_height - return the current height
+// of a line
+//-------------------------------------------------
+
+float mame_ui_manager::get_line_height()
+{
+ INT32 raw_font_pixel_height = get_font()->pixel_height();
+ render_target &ui_target = machine().render().ui_target();
+ INT32 target_pixel_height = ui_target.height();
+ float one_to_one_line_height;
+ float scale_factor;
+
+ // compute the font pixel height at the nominal size
+ one_to_one_line_height = (float)raw_font_pixel_height / (float)target_pixel_height;
+
+ // determine the scale factor
+ scale_factor = UI_TARGET_FONT_HEIGHT / one_to_one_line_height;
+
+ // if our font is small-ish, do integral scaling
+ if (raw_font_pixel_height < 24)
+ {
+ // do we want to scale smaller? only do so if we exceed the threshold
+ if (scale_factor <= 1.0f)
+ {
+ if (one_to_one_line_height < UI_MAX_FONT_HEIGHT || raw_font_pixel_height < 12)
+ scale_factor = 1.0f;
+ }
+
+ // otherwise, just ensure an integral scale factor
+ else
+ scale_factor = floor(scale_factor);
+ }
+
+ // otherwise, just make sure we hit an even number of pixels
+ else
+ {
+ INT32 height = scale_factor * one_to_one_line_height * (float)target_pixel_height;
+ scale_factor = (float)height / (one_to_one_line_height * (float)target_pixel_height);
+ }
+
+ return scale_factor * one_to_one_line_height;
+}
+
+
+//-------------------------------------------------
+// get_char_width - return the width of a
+// single character
+//-------------------------------------------------
+
+float mame_ui_manager::get_char_width(unicode_char ch)
+{
+ return get_font()->char_width(get_line_height(), machine().render().ui_aspect(), ch);
+}
+
+
+//-------------------------------------------------
+// get_string_width - return the width of a
+// character string
+//-------------------------------------------------
+
+float mame_ui_manager::get_string_width(const char *s, float text_size)
+{
+ return get_font()->utf8string_width(get_line_height() * text_size, machine().render().ui_aspect(), s);
+}
+
+
+//-------------------------------------------------
+// draw_outlined_box - add primitives to draw
+// an outlined box with the given background
+// color
+//-------------------------------------------------
+
+void mame_ui_manager::draw_outlined_box(render_container *container, float x0, float y0, float x1, float y1, rgb_t backcolor)
+{
+ draw_outlined_box(container, x0, y0, x1, y1, UI_BORDER_COLOR, backcolor);
+}
+
+
+//-------------------------------------------------
+// draw_outlined_box - add primitives to draw
+// an outlined box with the given background
+// color
+//-------------------------------------------------
+
+void mame_ui_manager::draw_outlined_box(render_container *container, float x0, float y0, float x1, float y1, rgb_t fgcolor, rgb_t bgcolor)
+{
+ container->add_rect(x0, y0, x1, y1, bgcolor, PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
+ container->add_line(x0, y0, x1, y0, UI_LINE_WIDTH, fgcolor, PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
+ container->add_line(x1, y0, x1, y1, UI_LINE_WIDTH, fgcolor, PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
+ container->add_line(x1, y1, x0, y1, UI_LINE_WIDTH, fgcolor, PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
+ container->add_line(x0, y1, x0, y0, UI_LINE_WIDTH, fgcolor, PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
+}
+
+
+//-------------------------------------------------
+// draw_text - simple text renderer
+//-------------------------------------------------
+
+void mame_ui_manager::draw_text(render_container *container, const char *buf, float x, float y)
+{
+ draw_text_full(container, buf, x, y, 1.0f - x, JUSTIFY_LEFT, WRAP_WORD, DRAW_NORMAL, UI_TEXT_COLOR, UI_TEXT_BG_COLOR, nullptr, nullptr);
+}
+
+
+//-------------------------------------------------
+// draw_text_full - full featured text
+// renderer with word wrapping, justification,
+// and full size computation
+//-------------------------------------------------
+
+void mame_ui_manager::draw_text_full(render_container *container, const char *origs, float x, float y, float origwrapwidth, int justify, int wrap, int draw, rgb_t fgcolor, rgb_t bgcolor, float *totalwidth, float *totalheight, float text_size)
+{
+ float lineheight = get_line_height() * text_size;
+ const char *ends = origs + strlen(origs);
+ float wrapwidth = origwrapwidth;
+ const char *s = origs;
+ const char *linestart;
+ float cury = y;
+ float maxwidth = 0;
+ float aspect = machine().render().ui_aspect(container);
+
+ // if we don't want wrapping, guarantee a huge wrapwidth
+ if (wrap == WRAP_NEVER)
+ wrapwidth = 1000000.0f;
+ if (wrapwidth <= 0)
+ return;
+
+ // loop over lines
+ while (*s != 0)
+ {
+ const char *lastbreak = nullptr;
+ int line_justify = justify;
+ unicode_char schar;
+ int scharcount;
+ float lastbreak_width = 0;
+ float curwidth = 0;
+ float curx = x;
+
+ // get the current character
+ scharcount = uchar_from_utf8(&schar, s, ends - s);
+ if (scharcount == -1)
+ break;
+
+ // if the line starts with a tab character, center it regardless
+ if (schar == '\t')
+ {
+ s += scharcount;
+ line_justify = JUSTIFY_CENTER;
+ }
+
+ // remember the starting position of the line
+ linestart = s;
+
+ // loop while we have characters and are less than the wrapwidth
+ while (*s != 0 && curwidth <= wrapwidth)
+ {
+ float chwidth;
+
+ // get the current chcaracter
+ scharcount = uchar_from_utf8(&schar, s, ends - s);
+ if (scharcount == -1)
+ break;
+
+ // if we hit a newline, stop immediately
+ if (schar == '\n')
+ break;
+
+ // get the width of this character
+ chwidth = get_font()->char_width(lineheight, aspect, schar);
+
+ // if we hit a space, remember the location and width *without* the space
+ if (schar == ' ')
+ {
+ lastbreak = s;
+ lastbreak_width = curwidth;
+ }
+
+ // add the width of this character and advance
+ curwidth += chwidth;
+ s += scharcount;
+
+ // if we hit any non-space breakable character, remember the location and width
+ // *with* the breakable character
+ if (schar != ' ' && is_breakable_char(schar) && curwidth <= wrapwidth)
+ {
+ lastbreak = s;
+ lastbreak_width = curwidth;
+ }
+ }
+
+ // if we accumulated too much for the current width, we need to back off
+ if (curwidth > wrapwidth)
+ {
+ // if we're word wrapping, back up to the last break if we can
+ if (wrap == WRAP_WORD)
+ {
+ // if we hit a break, back up to there with the appropriate width
+ if (lastbreak != nullptr)
+ {
+ s = lastbreak;
+ curwidth = lastbreak_width;
+ }
+
+ // if we didn't hit a break, back up one character
+ else if (s > linestart)
+ {
+ // get the previous character
+ s = (const char *)utf8_previous_char(s);
+ scharcount = uchar_from_utf8(&schar, s, ends - s);
+ if (scharcount == -1)
+ break;
+
+ curwidth -= get_font()->char_width(lineheight, aspect, schar);
+ // if back to 0, there is no space to draw even a single char
+ if (curwidth <= 0)
+ break;
+ }
+ }
+
+ // if we're truncating, make sure we have enough space for the ...
+ else if (wrap == WRAP_TRUNCATE)
+ {
+ // add in the width of the ...
+ curwidth += 3.0f * get_font()->char_width(lineheight, aspect, '.');
+
+ // while we are above the wrap width, back up one character
+ while (curwidth > wrapwidth && s > linestart)
+ {
+ // get the previous character
+ s = (const char *)utf8_previous_char(s);
+ scharcount = uchar_from_utf8(&schar, s, ends - s);
+ if (scharcount == -1)
+ break;
+
+ curwidth -= get_font()->char_width(lineheight, aspect, schar);
+ }
+ }
+ }
+
+ // align according to the justfication
+ if (line_justify == JUSTIFY_CENTER)
+ curx += (origwrapwidth - curwidth) * 0.5f;
+ else if (line_justify == JUSTIFY_RIGHT)
+ curx += origwrapwidth - curwidth;
+
+ // track the maximum width of any given line
+ if (curwidth > maxwidth)
+ maxwidth = curwidth;
+
+ // if opaque, add a black box
+ if (draw == DRAW_OPAQUE)
+ container->add_rect(curx, cury, curx + curwidth, cury + lineheight, bgcolor, PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
+
+ // loop from the line start and add the characters
+ while (linestart < s)
+ {
+ // get the current character
+ unicode_char linechar;
+ int linecharcount = uchar_from_utf8(&linechar, linestart, ends - linestart);
+ if (linecharcount == -1)
+ break;
+
+ if (draw != DRAW_NONE)
+ {
+ container->add_char(curx, cury, lineheight, aspect, fgcolor, *get_font(), linechar);
+ curx += get_font()->char_width(lineheight, aspect, linechar);
+ }
+ linestart += linecharcount;
+ }
+
+ // append ellipses if needed
+ if (wrap == WRAP_TRUNCATE && *s != 0 && draw != DRAW_NONE)
+ {
+ container->add_char(curx, cury, lineheight, aspect, fgcolor, *get_font(), '.');
+ curx += get_font()->char_width(lineheight, aspect, '.');
+ container->add_char(curx, cury, lineheight, aspect, fgcolor, *get_font(), '.');
+ curx += get_font()->char_width(lineheight, aspect, '.');
+ container->add_char(curx, cury, lineheight, aspect, fgcolor, *get_font(), '.');
+ curx += get_font()->char_width(lineheight, aspect, '.');
+ }
+
+ // if we're not word-wrapping, we're done
+ if (wrap != WRAP_WORD)
+ break;
+
+ // advance by a row
+ cury += lineheight;
+
+ // skip past any spaces at the beginning of the next line
+ scharcount = uchar_from_utf8(&schar, s, ends - s);
+ if (scharcount == -1)
+ break;
+
+ if (schar == '\n')
+ s += scharcount;
+ else
+ while (*s && isspace(schar))
+ {
+ s += scharcount;
+ scharcount = uchar_from_utf8(&schar, s, ends - s);
+ if (scharcount == -1)
+ break;
+ }
+ }
+
+ // report the width and height of the resulting space
+ if (totalwidth)
+ *totalwidth = maxwidth;
+ if (totalheight)
+ *totalheight = cury - y;
+}
+
+
+//-------------------------------------------------
+// draw_text_box - draw a multiline text
+// message with a box around it
+//-------------------------------------------------
+
+void mame_ui_manager::draw_text_box(render_container *container, const char *text, int justify, float xpos, float ypos, rgb_t backcolor)
+{
+ float line_height = get_line_height();
+ float max_width = 2.0f * ((xpos <= 0.5f) ? xpos : 1.0f - xpos) - 2.0f * UI_BOX_LR_BORDER;
+ float target_width = max_width;
+ float target_height = line_height;
+ float target_x = 0, target_y = 0;
+ float last_target_height = 0;
+
+ // limit this iteration to a finite number of passes
+ for (int pass = 0; pass < 5; pass++)
+ {
+ // determine the target location
+ target_x = xpos - 0.5f * target_width;
+ target_y = ypos - 0.5f * target_height;
+
+ // make sure we stay on-screen
+ if (target_x < UI_BOX_LR_BORDER)
+ target_x = UI_BOX_LR_BORDER;
+ if (target_x + target_width + UI_BOX_LR_BORDER > 1.0f)
+ target_x = 1.0f - UI_BOX_LR_BORDER - target_width;
+ if (target_y < UI_BOX_TB_BORDER)
+ target_y = UI_BOX_TB_BORDER;
+ if (target_y + target_height + UI_BOX_TB_BORDER > 1.0f)
+ target_y = 1.0f - UI_BOX_TB_BORDER - target_height;
+
+ // compute the multi-line target width/height
+ draw_text_full(container, text, target_x, target_y, target_width + 0.00001f,
+ justify, WRAP_WORD, DRAW_NONE, UI_TEXT_COLOR, UI_TEXT_BG_COLOR, &target_width, &target_height);
+ if (target_height > 1.0f - 2.0f * UI_BOX_TB_BORDER)
+ target_height = floorf((1.0f - 2.0f * UI_BOX_TB_BORDER) / line_height) * line_height;
+
+ // if we match our last value, we're done
+ if (target_height == last_target_height)
+ break;
+ last_target_height = target_height;
+ }
+
+ // add a box around that
+ draw_outlined_box(container, target_x - UI_BOX_LR_BORDER,
+ target_y - UI_BOX_TB_BORDER,
+ target_x + target_width + UI_BOX_LR_BORDER,
+ target_y + target_height + UI_BOX_TB_BORDER, backcolor);
+ draw_text_full(container, text, target_x, target_y, target_width + 0.00001f,
+ justify, WRAP_WORD, DRAW_NORMAL, UI_TEXT_COLOR, UI_TEXT_BG_COLOR, nullptr, nullptr);
+}
+
+
+//-------------------------------------------------
+// draw_message_window - draw a multiline text
+// message with a box around it
+//-------------------------------------------------
+
+void mame_ui_manager::draw_message_window(render_container *container, const char *text)
+{
+ draw_text_box(container, text, JUSTIFY_LEFT, 0.5f, 0.5f, UI_BACKGROUND_COLOR);
+}
+
+
+//-------------------------------------------------
+// show_fps_temp - show the FPS counter for
+// a specific period of time
+//-------------------------------------------------
+
+void mame_ui_manager::show_fps_temp(double seconds)
+{
+ if (!m_showfps)
+ m_showfps_end = osd_ticks() + seconds * osd_ticks_per_second();
+}
+
+
+//-------------------------------------------------
+// set_show_fps - show/hide the FPS counter
+//-------------------------------------------------
+
+void mame_ui_manager::set_show_fps(bool show)
+{
+ m_showfps = show;
+ if (!show)
+ {
+ m_showfps = 0;
+ m_showfps_end = 0;
+ }
+}
+
+
+//-------------------------------------------------
+// show_fps - return the current FPS
+// counter visibility state
+//-------------------------------------------------
+
+bool mame_ui_manager::show_fps() const
+{
+ return m_showfps || (m_showfps_end != 0);
+}
+
+
+//-------------------------------------------------
+// show_fps_counter
+//-------------------------------------------------
+
+bool mame_ui_manager::show_fps_counter()
+{
+ bool result = m_showfps || osd_ticks() < m_showfps_end;
+ if (!result)
+ m_showfps_end = 0;
+ return result;
+}
+
+
+//-------------------------------------------------
+// set_show_profiler - show/hide the profiler
+//-------------------------------------------------
+
+void mame_ui_manager::set_show_profiler(bool show)
+{
+ m_show_profiler = show;
+ g_profiler.enable(show);
+}
+
+
+//-------------------------------------------------
+// show_profiler - return the current
+// profiler visibility state
+//-------------------------------------------------
+
+bool mame_ui_manager::show_profiler() const
+{
+ return m_show_profiler;
+}
+
+
+//-------------------------------------------------
+// show_menu - show the menus
+//-------------------------------------------------
+
+void mame_ui_manager::show_menu()
+{
+ set_handler(ui_menu::ui_handler, 0);
+}
+
+
+//-------------------------------------------------
+// show_mouse - change mouse status
+//-------------------------------------------------
+
+void mame_ui_manager::show_mouse(bool status)
+{
+ m_mouse_show = status;
+}
+
+
+//-------------------------------------------------
+// is_menu_active - return true if the menu
+// UI handler is active
+//-------------------------------------------------
+
+bool mame_ui_manager::is_menu_active(void)
+{
+ return (m_handler_callback == ui_menu::ui_handler);
+}
+
+
+/***************************************************************************
+ TEXT GENERATORS
+***************************************************************************/
+
+//-------------------------------------------------
+// warnings_string - print the warning flags
+// text to the given buffer
+//-------------------------------------------------
+
+std::string &mame_ui_manager::warnings_string(std::string &str)
+{
+#define WARNING_FLAGS ( MACHINE_NOT_WORKING | \
+ MACHINE_UNEMULATED_PROTECTION | \
+ MACHINE_MECHANICAL | \
+ MACHINE_WRONG_COLORS | \
+ MACHINE_IMPERFECT_COLORS | \
+ MACHINE_REQUIRES_ARTWORK | \
+ MACHINE_NO_SOUND | \
+ MACHINE_IMPERFECT_SOUND | \
+ MACHINE_IMPERFECT_GRAPHICS | \
+ MACHINE_IMPERFECT_KEYBOARD | \
+ MACHINE_NO_COCKTAIL| \
+ MACHINE_IS_INCOMPLETE| \
+ MACHINE_NO_SOUND_HW )
+
+ str.clear();
+
+ // if no warnings, nothing to return
+ if (machine().rom_load().warnings() == 0 && machine().rom_load().knownbad() == 0 && !(machine().system().flags & WARNING_FLAGS) && machine().rom_load().software_load_warnings_message().length() == 0)
+ return str;
+
+ // add a warning if any ROMs were loaded with warnings
+ if (machine().rom_load().warnings() > 0)
+ {
+ str.append(_("One or more ROMs/CHDs for this machine are incorrect. The machine may not run correctly.\n"));
+ if (machine().system().flags & WARNING_FLAGS)
+ str.append("\n");
+ }
+
+ if (machine().rom_load().software_load_warnings_message().length()>0) {
+ str.append(machine().rom_load().software_load_warnings_message());
+ if (machine().system().flags & WARNING_FLAGS)
+ str.append("\n");
+ }
+ // if we have at least one warning flag, print the general header
+ if ((machine().system().flags & WARNING_FLAGS) || machine().rom_load().knownbad() > 0)
+ {
+ str.append(_("There are known problems with this machine\n\n"));
+
+ // add a warning if any ROMs are flagged BAD_DUMP/NO_DUMP
+ if (machine().rom_load().knownbad() > 0) {
+ str.append(_("One or more ROMs/CHDs for this machine have not been correctly dumped.\n"));
+ }
+ // add one line per warning flag
+ if (machine().system().flags & MACHINE_IMPERFECT_KEYBOARD)
+ str.append(_("The keyboard emulation may not be 100% accurate.\n"));
+ if (machine().system().flags & MACHINE_IMPERFECT_COLORS)
+ str.append(_("The colors aren't 100% accurate.\n"));
+ if (machine().system().flags & MACHINE_WRONG_COLORS)
+ str.append(_("The colors are completely wrong.\n"));
+ if (machine().system().flags & MACHINE_IMPERFECT_GRAPHICS)
+ str.append(_("The video emulation isn't 100% accurate.\n"));
+ if (machine().system().flags & MACHINE_IMPERFECT_SOUND)
+ str.append(_("The sound emulation isn't 100% accurate.\n"));
+ if (machine().system().flags & MACHINE_NO_SOUND) {
+ str.append(_("The machine lacks sound.\n"));
+ }
+ if (machine().system().flags & MACHINE_NO_COCKTAIL)
+ str.append(_("Screen flipping in cocktail mode is not supported.\n"));
+
+ // check if external artwork is present before displaying this warning?
+ if (machine().system().flags & MACHINE_REQUIRES_ARTWORK) {
+ str.append(_("The machine requires external artwork files\n"));
+ }
+
+ if (machine().system().flags & MACHINE_IS_INCOMPLETE )
+ {
+ str.append(_("This machine was never completed. It may exhibit strange behavior or missing elements that are not bugs in the emulation.\n"));
+ }
+
+ if (machine().system().flags & MACHINE_NO_SOUND_HW )
+ {
+ str.append(_("This machine has no sound hardware, MAME will produce no sounds, this is expected behaviour.\n"));
+ }
+
+ // if there's a NOT WORKING, UNEMULATED PROTECTION or GAME MECHANICAL warning, make it stronger
+ if (machine().system().flags & (MACHINE_NOT_WORKING | MACHINE_UNEMULATED_PROTECTION | MACHINE_MECHANICAL))
+ {
+ // add the strings for these warnings
+ if (machine().system().flags & MACHINE_UNEMULATED_PROTECTION) {
+ str.append(_("The machine has protection which isn't fully emulated.\n"));
+ }
+ if (machine().system().flags & MACHINE_NOT_WORKING) {
+ str.append(_("\nTHIS MACHINE DOESN'T WORK. The emulation for this machine is not yet complete. "
+ "There is nothing you can do to fix this problem except wait for the developers to improve the emulation.\n"));
+ }
+ if (machine().system().flags & MACHINE_MECHANICAL) {
+ str.append(_("\nCertain elements of this machine cannot be emulated as it requires actual physical interaction or consists of mechanical devices. "
+ "It is not possible to fully play this machine.\n"));
+ }
+
+ // find the parent of this driver
+ driver_enumerator drivlist(machine().options());
+ int maindrv = drivlist.find(machine().system());
+ int clone_of = drivlist.non_bios_clone(maindrv);
+ if (clone_of != -1)
+ maindrv = clone_of;
+
+ // scan the driver list for any working clones and add them
+ bool foundworking = false;
+ while (drivlist.next())
+ if (drivlist.current() == maindrv || drivlist.clone() == maindrv)
+ if ((drivlist.driver().flags & (MACHINE_NOT_WORKING | MACHINE_UNEMULATED_PROTECTION | MACHINE_MECHANICAL)) == 0)
+ {
+ // this one works, add a header and display the name of the clone
+ if (!foundworking) {
+ str.append(_("\n\nThere are working clones of this machine: "));
+ }
+ else
+ str.append(", ");
+ str.append(drivlist.driver().name);
+ foundworking = true;
+ }
+
+ if (foundworking)
+ str.append("\n");
+ }
+ }
+
+ // add the 'press OK' string
+ str.append(_("\n\nPress any key to continue"));
+ return str;
+}
+
+
+//-------------------------------------------------
+// game_info_std::string - populate an allocated
+// string with the game info text
+//-------------------------------------------------
+
+std::string &mame_ui_manager::game_info_astring(std::string &str)
+{
+ std::ostringstream buf;
+
+ // print description, manufacturer, and CPU:
+ util::stream_format(buf, _("%1$s\n%2$s %3$s\nDriver: %4$s\n\nCPU:\n"),
+ machine().system().description,
+ machine().system().year,
+ machine().system().manufacturer,
+ core_filename_extract_base(machine().system().source_file));
+
+ // loop over all CPUs
+ execute_interface_iterator execiter(machine().root_device());
+ std::unordered_set<std::string> exectags;
+ for (device_execute_interface &exec : execiter)
+ {
+ if (!exectags.insert(exec.device().tag()).second)
+ continue;
+ // get cpu specific clock that takes internal multiplier/dividers into account
+ int clock = exec.device().clock();
+
+ // count how many identical CPUs we have
+ int count = 1;
+ const char *name = exec.device().name();
+ for (device_execute_interface &scan : execiter)
+ {
+ if (exec.device().type() == scan.device().type() && strcmp(name, scan.device().name()) == 0 && exec.device().clock() == scan.device().clock())
+ if (exectags.insert(scan.device().tag()).second)
+ count++;
+ }
+
+ // if more than one, prepend a #x in front of the CPU name
+ // display clock in kHz or MHz
+ util::stream_format(buf,
+ (count > 1) ? "%1$d" UTF8_MULTIPLY "%2$s %3$d.%4$0*5$d%6$s\n" : "%2$s %3$d.%4$0*5$d%6$s\n",
+ count,
+ name,
+ (clock >= 1000000) ? (clock / 1000000) : (clock / 1000),
+ (clock >= 1000000) ? (clock % 1000000) : (clock % 1000),
+ (clock >= 1000000) ? 6 : 3,
+ (clock >= 1000000) ? _("MHz") : _("kHz"));
+ }
+
+ // loop over all sound chips
+ sound_interface_iterator snditer(machine().root_device());
+ std::unordered_set<std::string> soundtags;
+ bool found_sound = false;
+ for (device_sound_interface &sound : snditer)
+ {
+ if (!soundtags.insert(sound.device().tag()).second)
+ continue;
+
+ // append the Sound: string
+ if (!found_sound)
+ buf << _("\nSound:\n");
+ found_sound = true;
+
+ // count how many identical sound chips we have
+ int count = 1;
+ for (device_sound_interface &scan : snditer)
+ {
+ if (sound.device().type() == scan.device().type() && sound.device().clock() == scan.device().clock())
+ if (soundtags.insert(scan.device().tag()).second)
+ count++;
+ }
+
+ // if more than one, prepend a #x in front of the CPU name
+ // display clock in kHz or MHz
+ int clock = sound.device().clock();
+ util::stream_format(buf,
+ (count > 1)
+ ? ((clock != 0) ? "%1$d" UTF8_MULTIPLY "%2$s %3$d.%4$0*5$d%6$s\n" : "%1$d" UTF8_MULTIPLY "%2$s\n")
+ : ((clock != 0) ? "%2$s %3$d.%4$0*5$d%6$s\n" : "%2$s\n"),
+ count,
+ sound.device().name(),
+ (clock >= 1000000) ? (clock / 1000000) : (clock / 1000),
+ (clock >= 1000000) ? (clock % 1000000) : (clock % 1000),
+ (clock >= 1000000) ? 6 : 3,
+ (clock >= 1000000) ? _("MHz") : _("kHz"));
+ }
+
+ // display screen information
+ buf << _("\nVideo:\n");
+ screen_device_iterator scriter(machine().root_device());
+ int scrcount = scriter.count();
+ if (scrcount == 0)
+ buf << _("None\n");
+ else
+ {
+ for (screen_device &screen : scriter)
+ {
+ std::string detail;
+ if (screen.screen_type() == SCREEN_TYPE_VECTOR)
+ detail = _("Vector");
+ else
+ {
+ const rectangle &visarea = screen.visible_area();
+ detail = string_format("%d " UTF8_MULTIPLY " %d (%s) %f" UTF8_NBSP "Hz",
+ visarea.width(), visarea.height(),
+ (machine().system().flags & ORIENTATION_SWAP_XY) ? "V" : "H",
+ ATTOSECONDS_TO_HZ(screen.frame_period().attoseconds()));
+ }
+
+ util::stream_format(buf,
+ (scrcount > 1) ? _("%1$s: %2$s\n") : _("%2$s\n"),
+ slider_get_screen_desc(screen), detail);
+ }
+ }
+
+ return str = buf.str();
+}
+
+
+
+/***************************************************************************
+ UI HANDLERS
+***************************************************************************/
+
+//-------------------------------------------------
+// handler_messagebox - displays the current
+// messagebox_text string but handles no input
+//-------------------------------------------------
+
+UINT32 mame_ui_manager::handler_messagebox(running_machine &machine, render_container *container, UINT32 state)
+{
+ mame_machine_manager::instance()->ui().draw_text_box(container, messagebox_text.c_str(), JUSTIFY_LEFT, 0.5f, 0.5f, messagebox_backcolor);
+ return 0;
+}
+
+
+//-------------------------------------------------
+// handler_messagebox_anykey - displays the
+// current messagebox_text string and waits for
+// any keypress
+//-------------------------------------------------
+
+UINT32 mame_ui_manager::handler_messagebox_anykey(running_machine &machine, render_container *container, UINT32 state)
+{
+ // draw a standard message window
+ mame_machine_manager::instance()->ui().draw_text_box(container, messagebox_text.c_str(), JUSTIFY_LEFT, 0.5f, 0.5f, messagebox_backcolor);
+
+ // if the user cancels, exit out completely
+ if (machine.ui_input().pressed(IPT_UI_CANCEL))
+ {
+ machine.schedule_exit();
+ state = UI_HANDLER_CANCEL;
+ }
+
+ // if any key is pressed, just exit
+ else if (machine.input().poll_switches() != INPUT_CODE_INVALID)
+ state = UI_HANDLER_CANCEL;
+
+ return state;
+}
+
+
+//-------------------------------------------------
+// process_natural_keyboard - processes any
+// natural keyboard input
+//-------------------------------------------------
+
+void mame_ui_manager::process_natural_keyboard()
+{
+ ui_event event;
+ int i, pressed;
+ input_item_id itemid;
+ input_code code;
+ UINT8 *key_down_ptr;
+ UINT8 key_down_mask;
+
+ // loop while we have interesting events
+ while (machine().ui_input().pop_event(&event))
+ {
+ // if this was a UI_EVENT_CHAR event, post it
+ if (event.event_type == UI_EVENT_CHAR)
+ machine().ioport().natkeyboard().post(event.ch);
+ }
+
+ // process natural keyboard keys that don't get UI_EVENT_CHARs
+ for (i = 0; i < ARRAY_LENGTH(non_char_keys); i++)
+ {
+ // identify this keycode
+ itemid = non_char_keys[i];
+ code = machine().input().code_from_itemid(itemid);
+
+ // ...and determine if it is pressed
+ pressed = machine().input().code_pressed(code);
+
+ // figure out whey we are in the key_down map
+ key_down_ptr = &m_non_char_keys_down[i / 8];
+ key_down_mask = 1 << (i % 8);
+
+ if (pressed && !(*key_down_ptr & key_down_mask))
+ {
+ // this key is now down
+ *key_down_ptr |= key_down_mask;
+
+ // post the key
+ machine().ioport().natkeyboard().post(UCHAR_MAMEKEY_BEGIN + code.item_id());
+ }
+ else if (!pressed && (*key_down_ptr & key_down_mask))
+ {
+ // this key is now up
+ *key_down_ptr &= ~key_down_mask;
+ }
+ }
+}
+
+
+//-------------------------------------------------
+// increase_frameskip
+//-------------------------------------------------
+
+void mame_ui_manager::increase_frameskip()
+{
+ // get the current value and increment it
+ int newframeskip = machine().video().frameskip() + 1;
+ if (newframeskip > MAX_FRAMESKIP)
+ newframeskip = -1;
+ machine().video().set_frameskip(newframeskip);
+
+ // display the FPS counter for 2 seconds
+ mame_machine_manager::instance()->ui().show_fps_temp(2.0);
+}
+
+
+//-------------------------------------------------
+// decrease_frameskip
+//-------------------------------------------------
+
+void mame_ui_manager::decrease_frameskip()
+{
+ // get the current value and decrement it
+ int newframeskip = machine().video().frameskip() - 1;
+ if (newframeskip < -1)
+ newframeskip = MAX_FRAMESKIP;
+ machine().video().set_frameskip(newframeskip);
+
+ // display the FPS counter for 2 seconds
+ mame_machine_manager::instance()->ui().show_fps_temp(2.0);
+}
+
+
+//-------------------------------------------------
+// can_paste
+//-------------------------------------------------
+
+bool mame_ui_manager::can_paste()
+{
+ // retrieve the clipboard text
+ char *text = osd_get_clipboard_text();
+
+ // free the string if allocated
+ if (text != nullptr)
+ osd_free(text);
+
+ // did we have text?
+ return text != nullptr;
+}
+
+
+//-------------------------------------------------
+// paste - does a paste from the keyboard
+//-------------------------------------------------
+
+void mame_ui_manager::paste()
+{
+ // retrieve the clipboard text
+ char *text = osd_get_clipboard_text();
+
+ // was a result returned?
+ if (text != nullptr)
+ {
+ // post the text
+ machine().ioport().natkeyboard().post_utf8(text);
+
+ // free the string
+ osd_free(text);
+ }
+}
+
+
+//-------------------------------------------------
+// image_handler_ingame - execute display
+// callback function for each image device
+//-------------------------------------------------
+
+void mame_ui_manager::image_handler_ingame()
+{
+ // run display routine for devices
+ if (machine().phase() == MACHINE_PHASE_RUNNING)
+ for (device_image_interface &image : image_interface_iterator(machine().root_device()))
+ image.call_display();
+}
+
+
+#define ANIMATION_FPS 1
+#define ANIMATION_FRAMES 4
+
+void mame_ui_manager::image_display(const device_type &type, device_image_interface *image)
+{
+ if (type == CASSETTE)
+ {
+ cassette_image_device *cass = dynamic_cast<cassette_image_device *>(image);
+ if (cass != nullptr)
+ {
+ char buf[65];
+ float x, y;
+ int n;
+ double position, length;
+ cassette_state uistate;
+ static const UINT8 shapes[8] = { 0x2d, 0x5c, 0x7c, 0x2f, 0x2d, 0x20, 0x20, 0x20 };
+
+ /* figure out where we are in the cassette */
+ position = cass->get_position();
+ length = cass->get_length();
+ uistate = (cassette_state)(cass->get_state() & CASSETTE_MASK_UISTATE);
+
+ /* choose a location on the screen */
+ x = 0.2f;
+ y = 0.5f;
+
+ y += cassette_device_iterator(machine().root_device()).indexof(*cass);
+
+ y *= get_line_height() + 2.0f * UI_BOX_TB_BORDER;
+ /* choose which frame of the animation we are at */
+ n = ((int)position / ANIMATION_FPS) % ANIMATION_FRAMES;
+ /* Since you can have anything in a BDF file, we will use crude ascii characters instead */
+ snprintf(buf, ARRAY_LENGTH(buf), "%c%c %c %02d:%02d (%04d) [%02d:%02d (%04d)]",
+ shapes[n], /* cassette icon left */
+ shapes[n | 4], /* cassette icon right */
+ (uistate == CASSETTE_PLAY) ? 0x50 : 0x52, /* play (P) or record (R) */
+ ((int)position / 60),
+ ((int)position % 60),
+ (int)position,
+ ((int)length / 60),
+ ((int)length % 60),
+ (int)length);
+
+ // draw the cassette
+ draw_text_box(&machine().render().ui_container(), buf, JUSTIFY_LEFT, x, y, UI_BACKGROUND_COLOR);
+ }
+ }
+}
+
+//-------------------------------------------------
+// handler_ingame - in-game handler takes care
+// of the standard keypresses
+//-------------------------------------------------
+
+UINT32 mame_ui_manager::handler_ingame(running_machine &machine, render_container *container, UINT32 state)
+{
+ bool is_paused = machine.paused();
+
+ // first draw the FPS counter
+ if (mame_machine_manager::instance()->ui().show_fps_counter())
+ {
+ mame_machine_manager::instance()->ui().draw_text_full(container, machine.video().speed_text().c_str(), 0.0f, 0.0f, 1.0f,
+ JUSTIFY_RIGHT, WRAP_WORD, DRAW_OPAQUE, ARGB_WHITE, ARGB_BLACK, nullptr, nullptr);
+ }
+
+ // Show the duration of current part (intro or gameplay or extra)
+ if (mame_machine_manager::instance()->ui().show_timecode_counter()) {
+ std::string tempstring;
+ mame_machine_manager::instance()->ui().draw_text_full(container, machine.video().timecode_text(tempstring).c_str(), 0.0f, 0.0f, 1.0f,
+ JUSTIFY_RIGHT, WRAP_WORD, DRAW_OPAQUE, rgb_t(0xf0,0xf0,0x10,0x10), ARGB_BLACK, NULL, NULL);
+ }
+ // Show the total time elapsed for the video preview (all parts intro, gameplay, extras)
+ if (mame_machine_manager::instance()->ui().show_timecode_total()) {
+ std::string tempstring;
+ mame_machine_manager::instance()->ui().draw_text_full(container, machine.video().timecode_total_text(tempstring).c_str(), 0.0f, 0.0f, 1.0f,
+ JUSTIFY_LEFT, WRAP_WORD, DRAW_OPAQUE, rgb_t(0xf0,0x10,0xf0,0x10), ARGB_BLACK, NULL, NULL);
+ }
+
+
+ // draw the profiler if visible
+ if (mame_machine_manager::instance()->ui().show_profiler())
+ {
+ const char *text = g_profiler.text(machine);
+ mame_machine_manager::instance()->ui().draw_text_full(container, text, 0.0f, 0.0f, 1.0f, JUSTIFY_LEFT, WRAP_WORD, DRAW_OPAQUE, ARGB_WHITE, ARGB_BLACK, nullptr, nullptr);
+ }
+
+ // if we're single-stepping, pause now
+ if (mame_machine_manager::instance()->ui().single_step())
+ {
+ machine.pause();
+ mame_machine_manager::instance()->ui().set_single_step(false);
+ }
+
+ // determine if we should disable the rest of the UI
+ bool ui_disabled = (machine.ioport().has_keyboard() && !machine.ui_active());
+
+ // is ScrLk UI toggling applicable here?
+ if (machine.ioport().has_keyboard())
+ {
+ // are we toggling the UI with ScrLk?
+ if (machine.ui_input().pressed(IPT_UI_TOGGLE_UI))
+ {
+ // toggle the UI
+ machine.set_ui_active(!machine.ui_active());
+
+ // display a popup indicating the new status
+ if (machine.ui_active())
+ {
+ mame_machine_manager::instance()->ui().popup_time(2, "%s\n%s\n%s\n%s\n%s\n%s\n",
+ _("Keyboard Emulation Status"),
+ "-------------------------",
+ _("Mode: PARTIAL Emulation"),
+ _("UI: Enabled"),
+ "-------------------------",
+ _("**Use ScrLock to toggle**"));
+ }
+ else
+ {
+ mame_machine_manager::instance()->ui().popup_time(2, "%s\n%s\n%s\n%s\n%s\n%s\n",
+ _("Keyboard Emulation Status"),
+ "-------------------------",
+ _("Mode: FULL Emulation"),
+ _("UI: Disabled"),
+ "-------------------------",
+ _("**Use ScrLock to toggle**"));
+ }
+ }
+ }
+
+ // is the natural keyboard enabled?
+ if (mame_machine_manager::instance()->ui().use_natural_keyboard() && (machine.phase() == MACHINE_PHASE_RUNNING))
+ mame_machine_manager::instance()->ui().process_natural_keyboard();
+
+ if (!ui_disabled)
+ {
+ // paste command
+ if (machine.ui_input().pressed(IPT_UI_PASTE))
+ mame_machine_manager::instance()->ui().paste();
+ }
+
+ mame_machine_manager::instance()->ui().image_handler_ingame();
+
+ // handle a save input timecode request
+ if (machine.ui_input().pressed(IPT_UI_TIMECODE))
+ machine.video().save_input_timecode();
+
+ if (ui_disabled) return ui_disabled;
+
+ if (machine.ui_input().pressed(IPT_UI_CANCEL))
+ {
+ mame_machine_manager::instance()->ui().request_quit();
+ return 0;
+ }
+
+ // turn on menus if requested
+ if (machine.ui_input().pressed(IPT_UI_CONFIGURE))
+ return mame_machine_manager::instance()->ui().set_handler(ui_menu::ui_handler, 0);
+
+ // if the on-screen display isn't up and the user has toggled it, turn it on
+ if ((machine.debug_flags & DEBUG_FLAG_ENABLED) == 0 && machine.ui_input().pressed(IPT_UI_ON_SCREEN_DISPLAY))
+ return mame_machine_manager::instance()->ui().set_handler(ui_menu_sliders::ui_handler, 1);
+
+ // handle a reset request
+ if (machine.ui_input().pressed(IPT_UI_RESET_MACHINE))
+ machine.schedule_hard_reset();
+ if (machine.ui_input().pressed(IPT_UI_SOFT_RESET))
+ machine.schedule_soft_reset();
+
+ // handle a request to display graphics/palette
+ if (machine.ui_input().pressed(IPT_UI_SHOW_GFX))
+ {
+ if (!is_paused)
+ machine.pause();
+ return mame_machine_manager::instance()->ui().set_handler(ui_gfx_ui_handler, is_paused);
+ }
+
+ // handle a tape control key
+ if (machine.ui_input().pressed(IPT_UI_TAPE_START))
+ {
+ for (cassette_image_device &cass : cassette_device_iterator(machine.root_device()))
+ {
+ cass.change_state(CASSETTE_PLAY, CASSETTE_MASK_UISTATE);
+ return 0;
+ }
+ }
+ if (machine.ui_input().pressed(IPT_UI_TAPE_STOP))
+ {
+ for (cassette_image_device &cass : cassette_device_iterator(machine.root_device()))
+ {
+ cass.change_state(CASSETTE_STOPPED, CASSETTE_MASK_UISTATE);
+ return 0;
+ }
+ }
+
+ // handle a save state request
+ if (machine.ui_input().pressed(IPT_UI_SAVE_STATE))
+ {
+ machine.pause();
+ mame_machine_manager::instance()->ui().m_load_save_hold = true;
+ return mame_machine_manager::instance()->ui().set_handler(handler_load_save, LOADSAVE_SAVE);
+ }
+
+ // handle a load state request
+ if (machine.ui_input().pressed(IPT_UI_LOAD_STATE))
+ {
+ machine.pause();
+ mame_machine_manager::instance()->ui().m_load_save_hold = true;
+ return mame_machine_manager::instance()->ui().set_handler(handler_load_save, LOADSAVE_LOAD);
+ }
+
+ // handle a save snapshot request
+ if (machine.ui_input().pressed(IPT_UI_SNAPSHOT))
+ machine.video().save_active_screen_snapshots();
+
+ // toggle pause
+ if (machine.ui_input().pressed(IPT_UI_PAUSE))
+ machine.toggle_pause();
+
+ // pause single step
+ if (machine.ui_input().pressed(IPT_UI_PAUSE_SINGLE))
+ {
+ mame_machine_manager::instance()->ui().set_single_step(true);
+ machine.resume();
+ }
+
+ // handle a toggle cheats request
+ if (machine.ui_input().pressed(IPT_UI_TOGGLE_CHEAT))
+ mame_machine_manager::instance()->cheat().set_enable(!mame_machine_manager::instance()->cheat().enabled());
+
+ // toggle movie recording
+ if (machine.ui_input().pressed(IPT_UI_RECORD_MOVIE))
+ machine.video().toggle_record_movie();
+
+ // toggle profiler display
+ if (machine.ui_input().pressed(IPT_UI_SHOW_PROFILER))
+ mame_machine_manager::instance()->ui().set_show_profiler(!mame_machine_manager::instance()->ui().show_profiler());
+
+ // toggle FPS display
+ if (machine.ui_input().pressed(IPT_UI_SHOW_FPS))
+ mame_machine_manager::instance()->ui().set_show_fps(!mame_machine_manager::instance()->ui().show_fps());
+
+ // increment frameskip?
+ if (machine.ui_input().pressed(IPT_UI_FRAMESKIP_INC))
+ mame_machine_manager::instance()->ui().increase_frameskip();
+
+ // decrement frameskip?
+ if (machine.ui_input().pressed(IPT_UI_FRAMESKIP_DEC))
+ mame_machine_manager::instance()->ui().decrease_frameskip();
+
+ // toggle throttle?
+ if (machine.ui_input().pressed(IPT_UI_THROTTLE))
+ machine.video().toggle_throttle();
+
+ // toggle autofire
+ if (machine.ui_input().pressed(IPT_UI_TOGGLE_AUTOFIRE))
+ {
+ if (!machine.options().cheat())
+ {
+ machine.popmessage(_("Autofire can't be enabled"));
+ }
+ else
+ {
+ bool autofire_toggle = machine.ioport().get_autofire_toggle();
+ machine.ioport().set_autofire_toggle(!autofire_toggle);
+ machine.popmessage("Autofire %s", autofire_toggle ? _("Enabled") : _("Disabled"));
+ }
+ }
+
+ // check for fast forward
+ if (machine.ioport().type_pressed(IPT_UI_FAST_FORWARD))
+ {
+ machine.video().set_fastforward(true);
+ mame_machine_manager::instance()->ui().show_fps_temp(0.5);
+ }
+ else
+ machine.video().set_fastforward(false);
+
+ return 0;
+}
+
+
+//-------------------------------------------------
+// handler_load_save - leads the user through
+// specifying a game to save or load
+//-------------------------------------------------
+
+UINT32 mame_ui_manager::handler_load_save(running_machine &machine, render_container *container, UINT32 state)
+{
+ char filename[20];
+ char file = 0;
+
+ // if we're not in the middle of anything, skip
+ if (state == LOADSAVE_NONE)
+ return 0;
+
+ // okay, we're waiting for a key to select a slot; display a message
+ if (state == LOADSAVE_SAVE)
+ mame_machine_manager::instance()->ui().draw_message_window(container, _("Select position to save to"));
+ else
+ mame_machine_manager::instance()->ui().draw_message_window(container, _("Select position to load from"));
+
+ // if load/save state sequence is still being pressed, do not read the filename yet
+ if (mame_machine_manager::instance()->ui().m_load_save_hold) {
+ bool seq_in_progress = false;
+ const input_seq &load_save_seq = state == LOADSAVE_SAVE ?
+ machine.ioport().type_seq(IPT_UI_SAVE_STATE) :
+ machine.ioport().type_seq(IPT_UI_LOAD_STATE);
+
+ for (int i = 0; i < load_save_seq.length(); i++)
+ if (machine.input().code_pressed_once(load_save_seq[i]))
+ seq_in_progress = true;
+
+ if (seq_in_progress)
+ return state;
+ else
+ mame_machine_manager::instance()->ui().m_load_save_hold = false;
+ }
+
+ // check for cancel key
+ if (machine.ui_input().pressed(IPT_UI_CANCEL))
+ {
+ // display a popup indicating things were cancelled
+ if (state == LOADSAVE_SAVE)
+ machine.popmessage(_("Save cancelled"));
+ else
+ machine.popmessage(_("Load cancelled"));
+
+ // reset the state
+ machine.resume();
+ return UI_HANDLER_CANCEL;
+ }
+
+ // check for A-Z or 0-9
+ for (input_item_id id = ITEM_ID_A; id <= ITEM_ID_Z; ++id)
+ if (machine.input().code_pressed_once(input_code(DEVICE_CLASS_KEYBOARD, 0, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, id)))
+ file = id - ITEM_ID_A + 'a';
+ if (file == 0)
+ for (input_item_id id = ITEM_ID_0; id <= ITEM_ID_9; ++id)
+ if (machine.input().code_pressed_once(input_code(DEVICE_CLASS_KEYBOARD, 0, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, id)))
+ file = id - ITEM_ID_0 + '0';
+ if (file == 0)
+ for (input_item_id id = ITEM_ID_0_PAD; id <= ITEM_ID_9_PAD; ++id)
+ if (machine.input().code_pressed_once(input_code(DEVICE_CLASS_KEYBOARD, 0, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, id)))
+ file = id - ITEM_ID_0_PAD + '0';
+ if (file == 0)
+ {
+ bool found = false;
+
+ for (int joy_index = 0; joy_index <= MAX_SAVED_STATE_JOYSTICK; joy_index++)
+ for (input_item_id id = ITEM_ID_BUTTON1; id <= ITEM_ID_BUTTON32; ++id)
+ if (machine.input().code_pressed_once(input_code(DEVICE_CLASS_JOYSTICK, joy_index, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, id)))
+ {
+ snprintf(filename, sizeof(filename), "joy%i-%i", joy_index, id - ITEM_ID_BUTTON1 + 1);
+ found = true;
+ break;
+ }
+
+ if (!found)
+ return state;
+ }
+ else
+ {
+ sprintf(filename, "%c", file);
+ }
+
+ // display a popup indicating that the save will proceed
+ if (state == LOADSAVE_SAVE)
+ {
+ machine.popmessage(_("Save to position %s"), filename);
+ machine.schedule_save(filename);
+ }
+ else
+ {
+ machine.popmessage(_("Load from position %s"), filename);
+ machine.schedule_load(filename);
+ }
+
+ // avoid handling the name of the save state slot as a seperate input
+ machine.ui_input().mark_all_as_pressed();
+
+ // remove the pause and reset the state
+ machine.resume();
+ return UI_HANDLER_CANCEL;
+}
+
+
+//-------------------------------------------------
+// request_quit
+//-------------------------------------------------
+
+void mame_ui_manager::request_quit()
+{
+ if (!machine().options().confirm_quit())
+ machine().schedule_exit();
+ else
+ set_handler(handler_confirm_quit, 0);
+}
+
+
+//-------------------------------------------------
+// handler_confirm_quit - leads the user through
+// confirming quit emulation
+//-------------------------------------------------
+
+UINT32 mame_ui_manager::handler_confirm_quit(running_machine &machine, render_container *container, UINT32 state)
+{
+ // 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));
+
+ // 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));
+
+ // assemble the quit message
+ 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,
+ ui_cancel_text);
+
+ mame_machine_manager::instance()->ui().draw_text_box(container, quit_message.c_str(), JUSTIFY_CENTER, 0.5f, 0.5f, UI_RED_COLOR);
+ machine.pause();
+
+ // if the user press ENTER, quit the game
+ if (machine.ui_input().pressed(IPT_UI_SELECT))
+ machine.schedule_exit();
+
+ // if the user press ESC, just continue
+ else if (machine.ui_input().pressed(IPT_UI_CANCEL))
+ {
+ machine.resume();
+ state = UI_HANDLER_CANCEL;
+ }
+
+ return state;
+}
+
+
+/***************************************************************************
+ SLIDER CONTROLS
+***************************************************************************/
+
+//-------------------------------------------------
+// ui_get_slider_list - get the list of sliders
+//-------------------------------------------------
+
+std::vector<ui_menu_item>& mame_ui_manager::get_slider_list(void)
+{
+ return slider_list;
+}
+
+
+//-------------------------------------------------
+// slider_alloc - allocate a new slider entry
+//-------------------------------------------------
+
+static slider_state *slider_alloc(running_machine &machine, const char *title, INT32 minval, INT32 defval, INT32 maxval, INT32 incval, slider_update update, void *arg)
+{
+ int size = sizeof(slider_state) + strlen(title);
+ slider_state *state = (slider_state *)auto_alloc_array_clear(machine, UINT8, size);
+
+ state->minval = minval;
+ state->defval = defval;
+ state->maxval = maxval;
+ state->incval = incval;
+ state->update = update;
+ state->arg = arg;
+ state->id = -1;
+ strcpy(state->description, title);
+
+ return state;
+}
+
+
+//----------------------------------------------------------
+// mame_ui_manager::slider_init - initialize the list of slider
+// controls
+//----------------------------------------------------------
+
+std::vector<ui_menu_item> mame_ui_manager::slider_init(running_machine &machine)
+{
+ std::vector<slider_state *> sliders;
+
+ // add overall volume
+ sliders.push_back(slider_alloc(machine, _("Master Volume"), -32, 0, 0, 1, slider_volume, nullptr));
+
+ // add per-channel volume
+ mixer_input info;
+ for (int item = 0; machine.sound().indexed_mixer_input(item, info); item++)
+ {
+ INT32 maxval = 2000;
+ INT32 defval = 1000;
+
+ std::string str = string_format(_("%1$s Volume"), info.stream->input_name(info.inputnum));
+ sliders.push_back(slider_alloc(machine, str.c_str(), 0, defval, maxval, 20, slider_mixervol, (void *)(FPTR)item));
+ }
+
+ // add analog adjusters
+ for (ioport_port &port : machine.ioport().ports())
+ {
+ for (ioport_field &field : port.fields())
+ {
+ if (field.type() == IPT_ADJUSTER)
+ {
+ sliders.push_back(slider_alloc(machine, field.name(), field.minval(), field.defvalue(), field.maxval(), 1, slider_adjuster, (void *)&field));
+ }
+ }
+ }
+
+ // add CPU overclocking (cheat only)
+ if (machine.options().cheat())
+ {
+ for (device_execute_interface &exec : execute_interface_iterator(machine.root_device()))
+ {
+ void *param = (void *)&exec.device();
+ std::string str = string_format(_("Overclock CPU %1$s"), exec.device().tag());
+ sliders.push_back(slider_alloc(machine, str.c_str(), 10, 1000, 2000, 1, slider_overclock, param));
+ }
+ }
+
+ // add screen parameters
+ screen_device_iterator scriter(machine.root_device());
+ for (screen_device &screen : scriter)
+ {
+ int defxscale = floor(screen.xscale() * 1000.0f + 0.5f);
+ int defyscale = floor(screen.yscale() * 1000.0f + 0.5f);
+ int defxoffset = floor(screen.xoffset() * 1000.0f + 0.5f);
+ int defyoffset = floor(screen.yoffset() * 1000.0f + 0.5f);
+ void *param = (void *)&screen;
+ std::string screen_desc = slider_get_screen_desc(screen);
+
+ // add refresh rate tweaker
+ if (machine.options().cheat())
+ {
+ std::string str = string_format(_("%1$s Refresh Rate"), screen_desc);
+ sliders.push_back(slider_alloc(machine, str.c_str(), -10000, 0, 10000, 1000, slider_refresh, param));
+ }
+
+ // add standard brightness/contrast/gamma controls per-screen
+ std::string str = string_format(_("%1$s Brightness"), screen_desc);
+ sliders.push_back(slider_alloc(machine, str.c_str(), 100, 1000, 2000, 10, slider_brightness, param));
+ str = string_format(_("%1$s Contrast"), screen_desc);
+ sliders.push_back(slider_alloc(machine, str.c_str(), 100, 1000, 2000, 50, slider_contrast, param));
+ str = string_format(_("%1$s Gamma"), screen_desc);
+ sliders.push_back(slider_alloc(machine, str.c_str(), 100, 1000, 3000, 50, slider_gamma, param));
+
+ // add scale and offset controls per-screen
+ str = string_format(_("%1$s Horiz Stretch"), screen_desc);
+ sliders.push_back(slider_alloc(machine, str.c_str(), 500, defxscale, 1500, 2, slider_xscale, param));
+ str = string_format(_("%1$s Horiz Position"), screen_desc);
+ sliders.push_back(slider_alloc(machine, str.c_str(), -500, defxoffset, 500, 2, slider_xoffset, param));
+ str = string_format(_("%1$s Vert Stretch"), screen_desc);
+ sliders.push_back(slider_alloc(machine, str.c_str(), 500, defyscale, 1500, 2, slider_yscale, param));
+ str = string_format(_("%1$s Vert Position"), screen_desc);
+ sliders.push_back(slider_alloc(machine, str.c_str(), -500, defyoffset, 500, 2, slider_yoffset, param));
+ }
+
+ for (laserdisc_device &laserdisc : laserdisc_device_iterator(machine.root_device()))
+ {
+ if (laserdisc.overlay_configured())
+ {
+ laserdisc_overlay_config config;
+ laserdisc.get_overlay_config(config);
+ int defxscale = floor(config.m_overscalex * 1000.0f + 0.5f);
+ int defyscale = floor(config.m_overscaley * 1000.0f + 0.5f);
+ int defxoffset = floor(config.m_overposx * 1000.0f + 0.5f);
+ int defyoffset = floor(config.m_overposy * 1000.0f + 0.5f);
+ void *param = (void *)&laserdisc;
+
+ // add scale and offset controls per-overlay
+ std::string str = string_format(_("Laserdisc '%1$s' Horiz Stretch"), laserdisc.tag());
+ sliders.push_back(slider_alloc(machine, str.c_str(), 500, (defxscale == 0) ? 1000 : defxscale, 1500, 2, slider_overxscale, param));
+ str = string_format(_("Laserdisc '%1$s' Horiz Position"), laserdisc.tag());
+ sliders.push_back(slider_alloc(machine, str.c_str(), -500, defxoffset, 500, 2, slider_overxoffset, param));
+ str = string_format(_("Laserdisc '%1$s' Vert Stretch"), laserdisc.tag());
+ sliders.push_back(slider_alloc(machine, str.c_str(), 500, (defyscale == 0) ? 1000 : defyscale, 1500, 2, slider_overyscale, param));
+ str = string_format(_("Laserdisc '%1$s' Vert Position"), laserdisc.tag());
+ sliders.push_back(slider_alloc(machine, str.c_str(), -500, defyoffset, 500, 2, slider_overyoffset, param));
+ }
+ }
+
+ for (screen_device &screen : scriter)
+ {
+ if (screen.screen_type() == SCREEN_TYPE_VECTOR)
+ {
+ // add vector control
+ sliders.push_back(slider_alloc(machine, _("Vector Flicker"), 0, 0, 1000, 10, slider_flicker, nullptr));
+ sliders.push_back(slider_alloc(machine, _("Beam Width Minimum"), 1, 100, 1000, 1, slider_beam_width_min, nullptr));
+ sliders.push_back(slider_alloc(machine, _("Beam Width Maximum"), 1, 100, 1000, 1, slider_beam_width_max, nullptr));
+ sliders.push_back(slider_alloc(machine, _("Beam Intensity Weight"), -1000, 0, 1000, 10, slider_beam_intensity_weight, nullptr));
+ break;
+ }
+ }
+
+#ifdef MAME_DEBUG
+ // add crosshair adjusters
+ for (ioport_port &port : machine.ioport().ports())
+ {
+ for (ioport_field &field : port.fields())
+ {
+ if (field.crosshair_axis() != CROSSHAIR_AXIS_NONE && field.player() == 0)
+ {
+ std::string str = string_format(_("Crosshair Scale %1$s"), (field.crosshair_axis() == CROSSHAIR_AXIS_X) ? _("X") : _("Y"));
+ sliders.push_back(slider_alloc(machine, str.c_str(), -3000, 1000, 3000, 100, slider_crossscale, (void *)&field));
+ str = string_format(_("Crosshair Offset %1$s"), (field.crosshair_axis() == CROSSHAIR_AXIS_X) ? _("X") : _("Y"));
+ sliders.push_back(slider_alloc(machine, str.c_str(), -3000, 0, 3000, 100, slider_crossoffset, (void *)&field));
+ }
+ }
+ }
+#endif
+
+ std::vector<ui_menu_item> items;
+ for (slider_state *slider : sliders)
+ {
+ ui_menu_item item;
+ item.text = slider->description;
+ item.subtext = "";
+ item.flags = 0;
+ item.ref = slider;
+ item.type = ui_menu_item_type::SLIDER;
+ items.push_back(item);
+ }
+
+ return items;
+}
+
+
+//-------------------------------------------------
+// slider_volume - global volume slider callback
+//-------------------------------------------------
+
+static INT32 slider_volume(running_machine &machine, void *arg, int id, std::string *str, INT32 newval)
+{
+ if (newval != SLIDER_NOCHANGE)
+ machine.sound().set_attenuation(newval);
+ if (str)
+ *str = string_format(_("%1$3ddB"), machine.sound().attenuation());
+ return machine.sound().attenuation();
+}
+
+
+//-------------------------------------------------
+// slider_mixervol - single channel volume
+// slider callback
+//-------------------------------------------------
+
+static INT32 slider_mixervol(running_machine &machine, void *arg, int id, std::string *str, INT32 newval)
+{
+ mixer_input info;
+ if (!machine.sound().indexed_mixer_input((FPTR)arg, info))
+ return 0;
+ if (newval != SLIDER_NOCHANGE)
+ {
+ INT32 curval = floor(info.stream->user_gain(info.inputnum) * 1000.0f + 0.5f);
+ if (newval > curval && (newval - curval) <= 4) newval += 4; // round up on increment
+ info.stream->set_user_gain(info.inputnum, (float)newval * 0.001f);
+ }
+ if (str)
+ *str = string_format("%4.2f", info.stream->user_gain(info.inputnum));
+ return floorf(info.stream->user_gain(info.inputnum) * 1000.0f + 0.5f);
+}
+
+
+//-------------------------------------------------
+// slider_adjuster - analog adjuster slider
+// callback
+//-------------------------------------------------
+
+static INT32 slider_adjuster(running_machine &machine, void *arg, int id, std::string *str, INT32 newval)
+{
+ ioport_field *field = (ioport_field *)arg;
+ ioport_field::user_settings settings;
+
+ field->get_user_settings(settings);
+ if (newval != SLIDER_NOCHANGE)
+ {
+ settings.value = newval;
+ field->set_user_settings(settings);
+ }
+ if (str)
+ *str = string_format(_("%1$d%%"), settings.value);
+ return settings.value;
+}
+
+
+//-------------------------------------------------
+// slider_overclock - CPU overclocker slider
+// callback
+//-------------------------------------------------
+
+static INT32 slider_overclock(running_machine &machine, void *arg, int id, std::string *str, INT32 newval)
+{
+ device_t *cpu = (device_t *)arg;
+ if (newval != SLIDER_NOCHANGE)
+ cpu->set_clock_scale((float)newval * 0.001f);
+ if (str)
+ *str = string_format(_("%1$3.0f%%"), floor(cpu->clock_scale() * 100.0 + 0.5));
+ return floor(cpu->clock_scale() * 1000.0 + 0.5);
+}
+
+
+//-------------------------------------------------
+// slider_refresh - refresh rate slider callback
+//-------------------------------------------------
+
+static INT32 slider_refresh(running_machine &machine, void *arg, int id, std::string *str, INT32 newval)
+{
+ screen_device *screen = reinterpret_cast<screen_device *>(arg);
+ double defrefresh = ATTOSECONDS_TO_HZ(screen->refresh_attoseconds());
+ double refresh;
+
+ if (newval != SLIDER_NOCHANGE)
+ {
+ int width = screen->width();
+ int height = screen->height();
+ const rectangle &visarea = screen->visible_area();
+ screen->configure(width, height, visarea, HZ_TO_ATTOSECONDS(defrefresh + (double)newval * 0.001));
+ }
+ if (str)
+ *str = string_format(_("%1$.3ffps"), ATTOSECONDS_TO_HZ(machine.first_screen()->frame_period().attoseconds()));
+ refresh = ATTOSECONDS_TO_HZ(machine.first_screen()->frame_period().attoseconds());
+ return floor((refresh - defrefresh) * 1000.0 + 0.5);
+}
+
+
+//-------------------------------------------------
+// slider_brightness - screen brightness slider
+// callback
+//-------------------------------------------------
+
+static INT32 slider_brightness(running_machine &machine, void *arg, int id, std::string *str, INT32 newval)
+{
+ screen_device *screen = reinterpret_cast<screen_device *>(arg);
+ render_container::user_settings settings;
+
+ screen->container().get_user_settings(settings);
+ if (newval != SLIDER_NOCHANGE)
+ {
+ settings.m_brightness = (float)newval * 0.001f;
+ screen->container().set_user_settings(settings);
+ }
+ if (str)
+ *str = string_format(_("%1$.3f"), settings.m_brightness);
+ return floor(settings.m_brightness * 1000.0f + 0.5f);
+}
+
+
+//-------------------------------------------------
+// slider_contrast - screen contrast slider
+// callback
+//-------------------------------------------------
+
+static INT32 slider_contrast(running_machine &machine, void *arg, int id, std::string *str, INT32 newval)
+{
+ screen_device *screen = reinterpret_cast<screen_device *>(arg);
+ render_container::user_settings settings;
+
+ screen->container().get_user_settings(settings);
+ if (newval != SLIDER_NOCHANGE)
+ {
+ settings.m_contrast = (float)newval * 0.001f;
+ screen->container().set_user_settings(settings);
+ }
+ if (str)
+ *str = string_format(_("%1$.3f"), settings.m_contrast);
+ return floor(settings.m_contrast * 1000.0f + 0.5f);
+}
+
+
+//-------------------------------------------------
+// slider_gamma - screen gamma slider callback
+//-------------------------------------------------
+
+static INT32 slider_gamma(running_machine &machine, void *arg, int id, std::string *str, INT32 newval)
+{
+ screen_device *screen = reinterpret_cast<screen_device *>(arg);
+ render_container::user_settings settings;
+
+ screen->container().get_user_settings(settings);
+ if (newval != SLIDER_NOCHANGE)
+ {
+ settings.m_gamma = (float)newval * 0.001f;
+ screen->container().set_user_settings(settings);
+ }
+ if (str)
+ *str = string_format(_("%1$.3f"), settings.m_gamma);
+ return floor(settings.m_gamma * 1000.0f + 0.5f);
+}
+
+
+//-------------------------------------------------
+// slider_xscale - screen horizontal scale slider
+// callback
+//-------------------------------------------------
+
+static INT32 slider_xscale(running_machine &machine, void *arg, int id, std::string *str, INT32 newval)
+{
+ screen_device *screen = reinterpret_cast<screen_device *>(arg);
+ render_container::user_settings settings;
+
+ screen->container().get_user_settings(settings);
+ if (newval != SLIDER_NOCHANGE)
+ {
+ settings.m_xscale = (float)newval * 0.001f;
+ screen->container().set_user_settings(settings);
+ }
+ if (str)
+ *str = string_format(_("%1$.3f"), settings.m_xscale);
+ return floor(settings.m_xscale * 1000.0f + 0.5f);
+}
+
+
+//-------------------------------------------------
+// slider_yscale - screen vertical scale slider
+// callback
+//-------------------------------------------------
+
+static INT32 slider_yscale(running_machine &machine, void *arg, int id, std::string *str, INT32 newval)
+{
+ screen_device *screen = reinterpret_cast<screen_device *>(arg);
+ render_container::user_settings settings;
+
+ screen->container().get_user_settings(settings);
+ if (newval != SLIDER_NOCHANGE)
+ {
+ settings.m_yscale = (float)newval * 0.001f;
+ screen->container().set_user_settings(settings);
+ }
+ if (str)
+ *str = string_format(_("%1$.3f"), settings.m_yscale);
+ return floor(settings.m_yscale * 1000.0f + 0.5f);
+}
+
+
+//-------------------------------------------------
+// slider_xoffset - screen horizontal position
+// slider callback
+//-------------------------------------------------
+
+static INT32 slider_xoffset(running_machine &machine, void *arg, int id, std::string *str, INT32 newval)
+{
+ screen_device *screen = reinterpret_cast<screen_device *>(arg);
+ render_container::user_settings settings;
+
+ screen->container().get_user_settings(settings);
+ if (newval != SLIDER_NOCHANGE)
+ {
+ settings.m_xoffset = (float)newval * 0.001f;
+ screen->container().set_user_settings(settings);
+ }
+ if (str)
+ *str = string_format(_("%1$.3f"), settings.m_xoffset);
+ return floor(settings.m_xoffset * 1000.0f + 0.5f);
+}
+
+
+//-------------------------------------------------
+// slider_yoffset - screen vertical position
+// slider callback
+//-------------------------------------------------
+
+static INT32 slider_yoffset(running_machine &machine, void *arg, int id, std::string *str, INT32 newval)
+{
+ screen_device *screen = reinterpret_cast<screen_device *>(arg);
+ render_container::user_settings settings;
+
+ screen->container().get_user_settings(settings);
+ if (newval != SLIDER_NOCHANGE)
+ {
+ settings.m_yoffset = (float)newval * 0.001f;
+ screen->container().set_user_settings(settings);
+ }
+ if (str)
+ *str = string_format(_("%1$.3f"), settings.m_yoffset);
+ return floor(settings.m_yoffset * 1000.0f + 0.5f);
+}
+
+
+//-------------------------------------------------
+// slider_overxscale - screen horizontal scale slider
+// callback
+//-------------------------------------------------
+
+static INT32 slider_overxscale(running_machine &machine, void *arg, int id, std::string *str, INT32 newval)
+{
+ laserdisc_device *laserdisc = (laserdisc_device *)arg;
+ laserdisc_overlay_config settings;
+
+ laserdisc->get_overlay_config(settings);
+ if (newval != SLIDER_NOCHANGE)
+ {
+ settings.m_overscalex = (float)newval * 0.001f;
+ laserdisc->set_overlay_config(settings);
+ }
+ if (str)
+ *str = string_format(_("%1$.3f"), settings.m_overscalex);
+ return floor(settings.m_overscalex * 1000.0f + 0.5f);
+}
+
+
+//-------------------------------------------------
+// slider_overyscale - screen vertical scale slider
+// callback
+//-------------------------------------------------
+
+static INT32 slider_overyscale(running_machine &machine, void *arg, int id, std::string *str, INT32 newval)
+{
+ laserdisc_device *laserdisc = (laserdisc_device *)arg;
+ laserdisc_overlay_config settings;
+
+ laserdisc->get_overlay_config(settings);
+ if (newval != SLIDER_NOCHANGE)
+ {
+ settings.m_overscaley = (float)newval * 0.001f;
+ laserdisc->set_overlay_config(settings);
+ }
+ if (str)
+ *str = string_format(_("%1$.3f"), settings.m_overscaley);
+ return floor(settings.m_overscaley * 1000.0f + 0.5f);
+}
+
+
+//-------------------------------------------------
+// slider_overxoffset - screen horizontal position
+// slider callback
+//-------------------------------------------------
+
+static INT32 slider_overxoffset(running_machine &machine, void *arg, int id, std::string *str, INT32 newval)
+{
+ laserdisc_device *laserdisc = (laserdisc_device *)arg;
+ laserdisc_overlay_config settings;
+
+ laserdisc->get_overlay_config(settings);
+ if (newval != SLIDER_NOCHANGE)
+ {
+ settings.m_overposx = (float)newval * 0.001f;
+ laserdisc->set_overlay_config(settings);
+ }
+ if (str)
+ *str = string_format(_("%1$.3f"), settings.m_overposx);
+ return floor(settings.m_overposx * 1000.0f + 0.5f);
+}
+
+
+//-------------------------------------------------
+// slider_overyoffset - screen vertical position
+// slider callback
+//-------------------------------------------------
+
+static INT32 slider_overyoffset(running_machine &machine, void *arg, int id, std::string *str, INT32 newval)
+{
+ laserdisc_device *laserdisc = (laserdisc_device *)arg;
+ laserdisc_overlay_config settings;
+
+ laserdisc->get_overlay_config(settings);
+ if (newval != SLIDER_NOCHANGE)
+ {
+ settings.m_overposy = (float)newval * 0.001f;
+ laserdisc->set_overlay_config(settings);
+ }
+ if (str)
+ *str = string_format(_("%1$.3f"), settings.m_overposy);
+ return floor(settings.m_overposy * 1000.0f + 0.5f);
+}
+
+
+//-------------------------------------------------
+// slider_flicker - vector flicker slider
+// callback
+//-------------------------------------------------
+
+static INT32 slider_flicker(running_machine &machine, void *arg, int id, std::string *str, INT32 newval)
+{
+ if (newval != SLIDER_NOCHANGE)
+ vector_options::s_flicker = (float)newval * 0.001f;
+ if (str)
+ *str = string_format(_("%1$1.2f"), vector_options::s_flicker);
+ return floor(vector_options::s_flicker * 1000.0f + 0.5f);
+}
+
+
+//-------------------------------------------------
+// slider_beam_width_min - minimum vector beam width slider
+// callback
+//-------------------------------------------------
+
+static INT32 slider_beam_width_min(running_machine &machine, void *arg, int id, std::string *str, INT32 newval)
+{
+ if (newval != SLIDER_NOCHANGE)
+ vector_options::s_beam_width_min = MIN((float)newval * 0.01f, vector_options::s_beam_width_max);
+ if (str != nullptr)
+ *str = string_format(_("%1$1.2f"), vector_options::s_beam_width_min);
+ return floor(vector_options::s_beam_width_min * 100.0f + 0.5f);
+}
+
+
+//-------------------------------------------------
+// slider_beam_width_max - maximum vector beam width slider
+// callback
+//-------------------------------------------------
+
+static INT32 slider_beam_width_max(running_machine &machine, void *arg, int id, std::string *str, INT32 newval)
+{
+ if (newval != SLIDER_NOCHANGE)
+ vector_options::s_beam_width_max = MAX((float)newval * 0.01f, vector_options::s_beam_width_min);
+ if (str != nullptr)
+ *str = string_format(_("%1$1.2f"), vector_options::s_beam_width_max);
+ return floor(vector_options::s_beam_width_max * 100.0f + 0.5f);
+}
+
+
+//-------------------------------------------------
+// slider_beam_intensity_weight - vector beam intensity weight slider
+// callback
+//-------------------------------------------------
+
+static INT32 slider_beam_intensity_weight(running_machine &machine, void *arg, int id, std::string *str, INT32 newval)
+{
+ if (newval != SLIDER_NOCHANGE)
+ vector_options::s_beam_intensity_weight = (float)newval * 0.001f;
+ if (str != nullptr)
+ *str = string_format(_("%1$1.2f"), vector_options::s_beam_intensity_weight);
+ return floor(vector_options::s_beam_intensity_weight * 1000.0f + 0.5f);
+}
+
+
+//-------------------------------------------------
+// slider_get_screen_desc - returns the
+// description for a given screen
+//-------------------------------------------------
+
+static std::string slider_get_screen_desc(screen_device &screen)
+{
+ if (screen_device_iterator(screen.machine().root_device()).count() > 1)
+ return string_format(_("Screen '%1$s'"), screen.tag());
+ else
+ return _("Screen");
+}
+
+//-------------------------------------------------
+// slider_crossscale - crosshair scale slider
+// callback
+//-------------------------------------------------
+
+#ifdef MAME_DEBUG
+static INT32 slider_crossscale(running_machine &machine, void *arg, int id, std::string *str, INT32 newval)
+{
+ ioport_field *field = (ioport_field *)arg;
+
+ if (newval != SLIDER_NOCHANGE)
+ field->set_crosshair_scale(float(newval) * 0.001);
+ if (str)
+ *str = string_format((field->crosshair_axis() == CROSSHAIR_AXIS_X) ? _("Crosshair Scale X %1$1.3f") : _("Crosshair Scale Y %1$1.3f"), float(newval) * 0.001f);
+ return floor(field->crosshair_scale() * 1000.0f + 0.5f);
+}
+#endif
+
+
+//-------------------------------------------------
+// slider_crossoffset - crosshair scale slider
+// callback
+//-------------------------------------------------
+
+#ifdef MAME_DEBUG
+static INT32 slider_crossoffset(running_machine &machine, void *arg, int id, std::string *str, INT32 newval)
+{
+ ioport_field *field = (ioport_field *)arg;
+
+ if (newval != SLIDER_NOCHANGE)
+ field->set_crosshair_offset(float(newval) * 0.001f);
+ if (str)
+ *str = string_format((field->crosshair_axis() == CROSSHAIR_AXIS_X) ? _("Crosshair Offset X %1$1.3f") : _("Crosshair Offset Y %1$1.3f"), float(newval) * 0.001f);
+ return field->crosshair_offset();
+}
+#endif
+
+//-------------------------------------------------
+// set_use_natural_keyboard - specifies
+// whether the natural keyboard is active
+//-------------------------------------------------
+
+void mame_ui_manager::set_use_natural_keyboard(bool use_natural_keyboard)
+{
+ m_use_natural_keyboard = use_natural_keyboard;
+ std::string error;
+ machine().options().set_value(OPTION_NATURAL_KEYBOARD, use_natural_keyboard, OPTION_PRIORITY_CMDLINE, error);
+ assert(error.empty());
+}
+
+//-------------------------------------------------
+// wrap_text
+//-------------------------------------------------
+
+int mame_ui_manager::wrap_text(render_container *container, const char *origs, float x, float y, float origwrapwidth, std::vector<int> &xstart, std::vector<int> &xend, float text_size)
+{
+ float lineheight = get_line_height() * text_size;
+ const char *ends = origs + strlen(origs);
+ float wrapwidth = origwrapwidth;
+ const char *s = origs;
+ const char *linestart;
+ float maxwidth = 0;
+ float aspect = machine().render().ui_aspect(container);
+ int count = 0;
+
+ // loop over lines
+ while (*s != 0)
+ {
+ const char *lastbreak = nullptr;
+ unicode_char schar;
+ int scharcount;
+ float lastbreak_width = 0;
+ float curwidth = 0;
+
+ // get the current character
+ scharcount = uchar_from_utf8(&schar, s, ends - s);
+ if (scharcount == -1)
+ break;
+
+ // remember the starting position of the line
+ linestart = s;
+
+ // loop while we have characters and are less than the wrapwidth
+ while (*s != 0 && curwidth <= wrapwidth)
+ {
+ float chwidth;
+
+ // get the current chcaracter
+ scharcount = uchar_from_utf8(&schar, s, ends - s);
+ if (scharcount == -1)
+ break;
+
+ // if we hit a newline, stop immediately
+ if (schar == '\n')
+ break;
+
+ // get the width of this character
+ chwidth = get_font()->char_width(lineheight, aspect, schar);
+
+ // if we hit a space, remember the location and width *without* the space
+ if (schar == ' ')
+ {
+ lastbreak = s;
+ lastbreak_width = curwidth;
+ }
+
+ // add the width of this character and advance
+ curwidth += chwidth;
+ s += scharcount;
+
+ // if we hit any non-space breakable character, remember the location and width
+ // *with* the breakable character
+ if (schar != ' ' && is_breakable_char(schar) && curwidth <= wrapwidth)
+ {
+ lastbreak = s;
+ lastbreak_width = curwidth;
+ }
+ }
+
+ // if we accumulated too much for the current width, we need to back off
+ if (curwidth > wrapwidth)
+ {
+ // if we hit a break, back up to there with the appropriate width
+ if (lastbreak != nullptr)
+ {
+ s = lastbreak;
+ curwidth = lastbreak_width;
+ }
+
+ // if we didn't hit a break, back up one character
+ else if (s > linestart)
+ {
+ // get the previous character
+ s = (const char *)utf8_previous_char(s);
+ scharcount = uchar_from_utf8(&schar, s, ends - s);
+ if (scharcount == -1)
+ break;
+
+ curwidth -= get_font()->char_width(lineheight, aspect, schar);
+ }
+ }
+
+ // track the maximum width of any given line
+ if (curwidth > maxwidth)
+ maxwidth = curwidth;
+
+ xstart.push_back(linestart - origs);
+ xend.push_back(s - origs);
+
+ // loop from the line start and add the characters
+ while (linestart < s)
+ {
+ // get the current character
+ unicode_char linechar;
+ int linecharcount = uchar_from_utf8(&linechar, linestart, ends - linestart);
+ if (linecharcount == -1)
+ break;
+ linestart += linecharcount;
+ }
+
+ // advance by a row
+ count++;
+
+ // skip past any spaces at the beginning of the next line
+ scharcount = uchar_from_utf8(&schar, s, ends - s);
+ if (scharcount == -1)
+ break;
+
+ if (schar == '\n')
+ s += scharcount;
+ else
+ while (*s && isspace(schar))
+ {
+ s += scharcount;
+ scharcount = uchar_from_utf8(&schar, s, ends - s);
+ if (scharcount == -1)
+ break;
+ }
+ }
+ return count;
+}
+
+//-------------------------------------------------
+// draw_textured_box - add primitives to
+// draw an outlined box with the given
+// textured background and line color
+//-------------------------------------------------
+
+void mame_ui_manager::draw_textured_box(render_container *container, float x0, float y0, float x1, float y1, rgb_t backcolor, rgb_t linecolor, render_texture *texture, UINT32 flags)
+{
+ container->add_quad(x0, y0, x1, y1, backcolor, texture, flags);
+ container->add_line(x0, y0, x1, y0, UI_LINE_WIDTH, linecolor, PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
+ container->add_line(x1, y0, x1, y1, UI_LINE_WIDTH, linecolor, PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
+ container->add_line(x1, y1, x0, y1, UI_LINE_WIDTH, linecolor, PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
+ container->add_line(x0, y1, x0, y0, UI_LINE_WIDTH, linecolor, PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
+}
+
+//-------------------------------------------------
+// decode UI color options
+//-------------------------------------------------
+
+rgb_t decode_ui_color(int id, running_machine *machine)
+{
+ static rgb_t color[ARRAY_LENGTH(s_color_list)];
+
+ if (machine != nullptr) {
+ ui_options option;
+ for (int x = 0; x < ARRAY_LENGTH(s_color_list); ++x) {
+ const char *o_default = option.value(s_color_list[x]);
+ const char *s_option = mame_machine_manager::instance()->ui().options().value(s_color_list[x]);
+ int len = strlen(s_option);
+ if (len != 8)
+ color[x] = rgb_t((UINT32)strtoul(o_default, nullptr, 16));
+ else
+ color[x] = rgb_t((UINT32)strtoul(s_option, nullptr, 16));
+ }
+ }
+ return color[id];
+}
+
+//-------------------------------------------------
+// get font rows from options
+//-------------------------------------------------
+
+int get_font_rows(running_machine *machine)
+{
+ static int value;
+
+ return ((machine != nullptr) ? value = mame_machine_manager::instance()->ui().options().font_rows() : value);
+}
+
+void mame_ui_manager::popup_time_string(int seconds, std::string message)
+{
+ // extract the text
+ messagebox_poptext = message;
+ messagebox_backcolor = UI_BACKGROUND_COLOR;
+
+ // set a timer
+ m_popup_text_end = osd_ticks() + osd_ticks_per_second() * seconds;
+}