mirror of
https://github.com/holub/mame
synced 2025-05-12 00:58:53 +03:00
2253 lines
70 KiB
C
2253 lines
70 KiB
C
/*********************************************************************
|
|
|
|
ui.c
|
|
|
|
Functions used to handle MAME's user interface.
|
|
|
|
Copyright Nicola Salmoria and the MAME Team.
|
|
Visit http://mamedev.org for licensing and usage restrictions.
|
|
|
|
*********************************************************************/
|
|
|
|
#include "emu.h"
|
|
#include "emuopts.h"
|
|
#include "video/vector.h"
|
|
#include "machine/laserdsc.h"
|
|
#include "profiler.h"
|
|
#include "render.h"
|
|
#include "cheat.h"
|
|
#include "rendfont.h"
|
|
#include "ui.h"
|
|
#include "uiinput.h"
|
|
#include "uimenu.h"
|
|
#include "uigfx.h"
|
|
#include <ctype.h>
|
|
|
|
|
|
|
|
/***************************************************************************
|
|
CONSTANTS
|
|
***************************************************************************/
|
|
|
|
enum
|
|
{
|
|
LOADSAVE_NONE,
|
|
LOADSAVE_LOAD,
|
|
LOADSAVE_SAVE
|
|
};
|
|
|
|
|
|
/***************************************************************************
|
|
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
|
|
};
|
|
|
|
/***************************************************************************
|
|
GLOBAL VARIABLES
|
|
***************************************************************************/
|
|
|
|
/* font for rendering */
|
|
static render_font *ui_font;
|
|
|
|
/* current UI handler */
|
|
static UINT32 (*ui_handler_callback)(running_machine &, render_container *, UINT32);
|
|
static UINT32 ui_handler_param;
|
|
|
|
/* flag to track single stepping */
|
|
static int single_step;
|
|
|
|
/* FPS counter display */
|
|
static int showfps;
|
|
static osd_ticks_t showfps_end;
|
|
|
|
/* profiler display */
|
|
static int show_profiler;
|
|
|
|
/* popup text display */
|
|
static osd_ticks_t popup_text_end;
|
|
|
|
/* messagebox buffer */
|
|
static astring messagebox_text;
|
|
static rgb_t messagebox_backcolor;
|
|
|
|
/* slider info */
|
|
static slider_state *slider_list;
|
|
static slider_state *slider_current;
|
|
|
|
/* natural keyboard info */
|
|
static int ui_use_natural_keyboard;
|
|
static UINT8 non_char_keys_down[(ARRAY_LENGTH(non_char_keys) + 7) / 8];
|
|
|
|
|
|
/***************************************************************************
|
|
FUNCTION PROTOTYPES
|
|
***************************************************************************/
|
|
|
|
static void ui_exit(running_machine &machine);
|
|
|
|
/* text generators */
|
|
static astring &disclaimer_string(running_machine &machine, astring &buffer);
|
|
static astring &warnings_string(running_machine &machine, astring &buffer);
|
|
|
|
/* UI handlers */
|
|
static UINT32 handler_messagebox(running_machine &machine, render_container *container, UINT32 state);
|
|
static UINT32 handler_messagebox_ok(running_machine &machine, render_container *container, UINT32 state);
|
|
static UINT32 handler_messagebox_anykey(running_machine &machine, render_container *container, UINT32 state);
|
|
static UINT32 handler_ingame(running_machine &machine, render_container *container, UINT32 state);
|
|
static UINT32 handler_load_save(running_machine &machine, render_container *container, UINT32 state);
|
|
static UINT32 handler_confirm_quit(running_machine &machine, render_container *container, UINT32 state);
|
|
|
|
/* 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 slider_state *slider_init(running_machine &machine);
|
|
static INT32 slider_volume(running_machine &machine, void *arg, astring *string, INT32 newval);
|
|
static INT32 slider_mixervol(running_machine &machine, void *arg, astring *string, INT32 newval);
|
|
static INT32 slider_adjuster(running_machine &machine, void *arg, astring *string, INT32 newval);
|
|
static INT32 slider_overclock(running_machine &machine, void *arg, astring *string, INT32 newval);
|
|
static INT32 slider_refresh(running_machine &machine, void *arg, astring *string, INT32 newval);
|
|
static INT32 slider_brightness(running_machine &machine, void *arg, astring *string, INT32 newval);
|
|
static INT32 slider_contrast(running_machine &machine, void *arg, astring *string, INT32 newval);
|
|
static INT32 slider_gamma(running_machine &machine, void *arg, astring *string, INT32 newval);
|
|
static INT32 slider_xscale(running_machine &machine, void *arg, astring *string, INT32 newval);
|
|
static INT32 slider_yscale(running_machine &machine, void *arg, astring *string, INT32 newval);
|
|
static INT32 slider_xoffset(running_machine &machine, void *arg, astring *string, INT32 newval);
|
|
static INT32 slider_yoffset(running_machine &machine, void *arg, astring *string, INT32 newval);
|
|
static INT32 slider_overxscale(running_machine &machine, void *arg, astring *string, INT32 newval);
|
|
static INT32 slider_overyscale(running_machine &machine, void *arg, astring *string, INT32 newval);
|
|
static INT32 slider_overxoffset(running_machine &machine, void *arg, astring *string, INT32 newval);
|
|
static INT32 slider_overyoffset(running_machine &machine, void *arg, astring *string, INT32 newval);
|
|
static INT32 slider_flicker(running_machine &machine, void *arg, astring *string, INT32 newval);
|
|
static INT32 slider_beam(running_machine &machine, void *arg, astring *string, INT32 newval);
|
|
static char *slider_get_screen_desc(screen_device &screen);
|
|
static char *slider_get_laserdisc_desc(device_t *screen);
|
|
#ifdef MAME_DEBUG
|
|
static INT32 slider_crossscale(running_machine &machine, void *arg, astring *string, INT32 newval);
|
|
static INT32 slider_crossoffset(running_machine &machine, void *arg, astring *string, INT32 newval);
|
|
#endif
|
|
|
|
|
|
/***************************************************************************
|
|
INLINE FUNCTIONS
|
|
***************************************************************************/
|
|
|
|
/*-------------------------------------------------
|
|
ui_set_handler - set a callback/parameter
|
|
pair for the current UI handler
|
|
-------------------------------------------------*/
|
|
|
|
INLINE UINT32 ui_set_handler(UINT32 (*callback)(running_machine &, render_container *, UINT32), UINT32 param)
|
|
{
|
|
ui_handler_callback = callback;
|
|
ui_handler_param = param;
|
|
return param;
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
is_breakable_char - is a given unicode
|
|
character a possible line break?
|
|
-------------------------------------------------*/
|
|
|
|
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
|
|
***************************************************************************/
|
|
|
|
/*-------------------------------------------------
|
|
ui_init - set up the user interface
|
|
-------------------------------------------------*/
|
|
|
|
int ui_init(running_machine &machine)
|
|
{
|
|
/* make sure we clean up after ourselves */
|
|
machine.add_notifier(MACHINE_NOTIFY_EXIT, machine_notify_delegate(FUNC(ui_exit), &machine));
|
|
|
|
/* initialize the other UI bits */
|
|
ui_menu_init(machine);
|
|
ui_gfx_init(machine);
|
|
|
|
/* reset globals */
|
|
single_step = FALSE;
|
|
ui_set_handler(handler_messagebox, 0);
|
|
/* retrieve options */
|
|
ui_use_natural_keyboard = machine.options().natural_keyboard();
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
ui_exit - clean up ourselves on exit
|
|
-------------------------------------------------*/
|
|
|
|
static void ui_exit(running_machine &machine)
|
|
{
|
|
/* free the font */
|
|
machine.render().font_free(ui_font);
|
|
ui_font = NULL;
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
ui_display_startup_screens - display the
|
|
various startup screens
|
|
-------------------------------------------------*/
|
|
|
|
int ui_display_startup_screens(running_machine &machine, int first_time, int show_disclaimer)
|
|
{
|
|
const int maxstate = 3;
|
|
int str = machine.options().seconds_to_run();
|
|
int show_gameinfo = !machine.options().skip_gameinfo();
|
|
int show_warnings = 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_disclaimer = FALSE;
|
|
|
|
/* initialize the on-screen display system */
|
|
slider_list = slider_current = slider_init(machine);
|
|
|
|
/* loop over states */
|
|
ui_set_handler(handler_ingame, 0);
|
|
for (state = 0; state < maxstate && !machine.scheduled_event_pending() && !ui_menu_is_force_game_select(); state++)
|
|
{
|
|
/* default to standard colors */
|
|
messagebox_backcolor = UI_BACKGROUND_COLOR;
|
|
|
|
/* pick the next state */
|
|
switch (state)
|
|
{
|
|
case 0:
|
|
if (show_disclaimer && disclaimer_string(machine, messagebox_text).len() > 0)
|
|
ui_set_handler(handler_messagebox_ok, 0);
|
|
break;
|
|
|
|
case 1:
|
|
if (show_warnings && warnings_string(machine, messagebox_text).len() > 0)
|
|
{
|
|
ui_set_handler(handler_messagebox_ok, 0);
|
|
if (machine.system().flags & (GAME_WRONG_COLORS | GAME_IMPERFECT_COLORS | GAME_REQUIRES_ARTWORK | GAME_IMPERFECT_GRAPHICS | GAME_IMPERFECT_SOUND | GAME_NO_SOUND))
|
|
messagebox_backcolor = UI_YELLOW_COLOR;
|
|
if (machine.system().flags & (GAME_NOT_WORKING | GAME_UNEMULATED_PROTECTION | GAME_MECHANICAL))
|
|
messagebox_backcolor = UI_RED_COLOR;
|
|
}
|
|
break;
|
|
|
|
case 2:
|
|
if (show_gameinfo && game_info_astring(machine, messagebox_text).len() > 0)
|
|
ui_set_handler(handler_messagebox_anykey, 0);
|
|
break;
|
|
}
|
|
|
|
/* clear the input memory */
|
|
machine.input().reset_polling();
|
|
while (machine.input().poll_switches() != INPUT_CODE_INVALID) ;
|
|
|
|
/* loop while we have a handler */
|
|
while (ui_handler_callback != handler_ingame && !machine.scheduled_event_pending() && !ui_menu_is_force_game_select())
|
|
machine.video().frame_update();
|
|
|
|
/* clear the handler and force an update */
|
|
ui_set_handler(handler_ingame, 0);
|
|
machine.video().frame_update();
|
|
}
|
|
|
|
/* if we're the empty driver, force the menus on */
|
|
if (ui_menu_is_force_game_select())
|
|
ui_set_handler(ui_menu_ui_handler, 0);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
ui_set_startup_text - set the text to display
|
|
at startup
|
|
-------------------------------------------------*/
|
|
|
|
void ui_set_startup_text(running_machine &machine, const char *text, int force)
|
|
{
|
|
static osd_ticks_t lastupdatetime = 0;
|
|
osd_ticks_t curtime = osd_ticks();
|
|
|
|
/* copy in the new text */
|
|
messagebox_text.cpy(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();
|
|
}
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
ui_update_and_render - update the UI and
|
|
render it; called by video.c
|
|
-------------------------------------------------*/
|
|
|
|
void ui_update_and_render(running_machine &machine, 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_is_force_game_select())
|
|
alpha = 255;
|
|
if (alpha > 255)
|
|
alpha = 255;
|
|
if (alpha >= 0)
|
|
container->add_rect(0.0f, 0.0f, 1.0f, 1.0f, MAKE_ARGB(alpha,0x00,0x00,0x00), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
|
|
}
|
|
|
|
/* render any cheat stuff at the bottom */
|
|
if (machine.phase() >= MACHINE_PHASE_RESET)
|
|
machine.cheat().render_text(*container);
|
|
|
|
/* call the current UI handler */
|
|
assert(ui_handler_callback != NULL);
|
|
ui_handler_param = (*ui_handler_callback)(machine, container, ui_handler_param);
|
|
|
|
/* display any popup messages */
|
|
if (osd_ticks() < popup_text_end)
|
|
ui_draw_text_box(container, messagebox_text, JUSTIFY_CENTER, 0.5f, 0.9f, messagebox_backcolor);
|
|
else
|
|
popup_text_end = 0;
|
|
|
|
/* cancel takes us back to the ingame handler */
|
|
if (ui_handler_param == UI_HANDLER_CANCEL)
|
|
ui_set_handler(handler_ingame, 0);
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
ui_get_font - return the UI font
|
|
-------------------------------------------------*/
|
|
|
|
render_font *ui_get_font(running_machine &machine)
|
|
{
|
|
/* allocate the font and messagebox string */
|
|
if (ui_font == NULL)
|
|
ui_font = machine.render().font_alloc(machine.options().ui_font());
|
|
return ui_font;
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
ui_get_line_height - return the current height
|
|
of a line
|
|
-------------------------------------------------*/
|
|
|
|
float ui_get_line_height(running_machine &machine)
|
|
{
|
|
INT32 raw_font_pixel_height = ui_get_font(machine)->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 threshhold */
|
|
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;
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
ui_get_char_width - return the width of a
|
|
single character
|
|
-------------------------------------------------*/
|
|
|
|
float ui_get_char_width(running_machine &machine, unicode_char ch)
|
|
{
|
|
return ui_get_font(machine)->char_width(ui_get_line_height(machine), machine.render().ui_aspect(), ch);
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
ui_get_string_width - return the width of a
|
|
character string
|
|
-------------------------------------------------*/
|
|
|
|
float ui_get_string_width(running_machine &machine, const char *s)
|
|
{
|
|
return ui_get_font(machine)->utf8string_width(ui_get_line_height(machine), machine.render().ui_aspect(), s);
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
ui_draw_outlined_box - add primitives to draw
|
|
an outlined box with the given background
|
|
color
|
|
-------------------------------------------------*/
|
|
|
|
void ui_draw_outlined_box(render_container *container, float x0, float y0, float x1, float y1, rgb_t backcolor)
|
|
{
|
|
container->add_rect(x0, y0, x1, y1, backcolor, PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
|
|
container->add_line(x0, y0, x1, y0, UI_LINE_WIDTH, UI_BORDER_COLOR, PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
|
|
container->add_line(x1, y0, x1, y1, UI_LINE_WIDTH, UI_BORDER_COLOR, PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
|
|
container->add_line(x1, y1, x0, y1, UI_LINE_WIDTH, UI_BORDER_COLOR, PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
|
|
container->add_line(x0, y1, x0, y0, UI_LINE_WIDTH, UI_BORDER_COLOR, PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
ui_draw_text - simple text renderer
|
|
-------------------------------------------------*/
|
|
|
|
void ui_draw_text(render_container *container, const char *buf, float x, float y)
|
|
{
|
|
ui_draw_text_full(container, buf, x, y, 1.0f - x, JUSTIFY_LEFT, WRAP_WORD, DRAW_NORMAL, UI_TEXT_COLOR, UI_TEXT_BG_COLOR, NULL, NULL);
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
ui_draw_text_full - full featured text
|
|
renderer with word wrapping, justification,
|
|
and full size computation
|
|
-------------------------------------------------*/
|
|
|
|
void ui_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)
|
|
{
|
|
running_machine &machine = container->manager().machine();
|
|
float lineheight = ui_get_line_height(machine);
|
|
const char *ends = origs + strlen(origs);
|
|
float wrapwidth = origwrapwidth;
|
|
const char *s = origs;
|
|
const char *linestart;
|
|
float cury = y;
|
|
float maxwidth = 0;
|
|
|
|
/* 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 = NULL;
|
|
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 = ui_get_char_width(machine, 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 != NULL)
|
|
{
|
|
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 -= ui_get_char_width(machine, schar);
|
|
}
|
|
}
|
|
|
|
/* 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 * ui_get_char_width(machine, '.');
|
|
|
|
/* 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 -= ui_get_char_width(machine, 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, machine.render().ui_aspect(), fgcolor, *ui_get_font(machine), linechar);
|
|
curx += ui_get_char_width(machine, linechar);
|
|
}
|
|
linestart += linecharcount;
|
|
}
|
|
|
|
/* append ellipses if needed */
|
|
if (wrap == WRAP_TRUNCATE && *s != 0 && draw != DRAW_NONE)
|
|
{
|
|
container->add_char(curx, cury, lineheight, machine.render().ui_aspect(), fgcolor, *ui_get_font(machine), '.');
|
|
curx += ui_get_char_width(machine, '.');
|
|
container->add_char(curx, cury, lineheight, machine.render().ui_aspect(), fgcolor, *ui_get_font(machine), '.');
|
|
curx += ui_get_char_width(machine, '.');
|
|
container->add_char(curx, cury, lineheight, machine.render().ui_aspect(), fgcolor, *ui_get_font(machine), '.');
|
|
curx += ui_get_char_width(machine, '.');
|
|
}
|
|
|
|
/* 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;
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
ui_draw_text_box - draw a multiline text
|
|
message with a box around it
|
|
-------------------------------------------------*/
|
|
|
|
void ui_draw_text_box(render_container *container, const char *text, int justify, float xpos, float ypos, rgb_t backcolor)
|
|
{
|
|
float line_height = ui_get_line_height(container->manager().machine());
|
|
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 */
|
|
ui_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 = floor((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 */
|
|
ui_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);
|
|
ui_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, NULL, NULL);
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
ui_popup_time - popup a message for a specific
|
|
amount of time
|
|
-------------------------------------------------*/
|
|
|
|
void CLIB_DECL ui_popup_time(int seconds, const char *text, ...)
|
|
{
|
|
va_list arg;
|
|
|
|
/* extract the text */
|
|
va_start(arg,text);
|
|
messagebox_text.vprintf(text, arg);
|
|
messagebox_backcolor = UI_BACKGROUND_COLOR;
|
|
va_end(arg);
|
|
|
|
/* set a timer */
|
|
popup_text_end = osd_ticks() + osd_ticks_per_second() * seconds;
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
ui_show_fps_temp - show the FPS counter for
|
|
a specific period of time
|
|
-------------------------------------------------*/
|
|
|
|
void ui_show_fps_temp(double seconds)
|
|
{
|
|
if (!showfps)
|
|
showfps_end = osd_ticks() + seconds * osd_ticks_per_second();
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
ui_set_show_fps - show/hide the FPS counter
|
|
-------------------------------------------------*/
|
|
|
|
void ui_set_show_fps(int show)
|
|
{
|
|
showfps = show;
|
|
if (!show)
|
|
{
|
|
showfps = 0;
|
|
showfps_end = 0;
|
|
}
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
ui_get_show_fps - return the current FPS
|
|
counter visibility state
|
|
-------------------------------------------------*/
|
|
|
|
int ui_get_show_fps(void)
|
|
{
|
|
return showfps || (showfps_end != 0);
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
ui_set_show_profiler - show/hide the profiler
|
|
-------------------------------------------------*/
|
|
|
|
void ui_set_show_profiler(int show)
|
|
{
|
|
show_profiler = show;
|
|
g_profiler.enable(show);
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
ui_get_show_profiler - return the current
|
|
profiler visibility state
|
|
-------------------------------------------------*/
|
|
|
|
int ui_get_show_profiler(void)
|
|
{
|
|
return show_profiler;
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
ui_show_menu - show the menus
|
|
-------------------------------------------------*/
|
|
|
|
void ui_show_menu(void)
|
|
{
|
|
ui_set_handler(ui_menu_ui_handler, 0);
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
ui_is_menu_active - return TRUE if the menu
|
|
UI handler is active
|
|
-------------------------------------------------*/
|
|
|
|
int ui_is_menu_active(void)
|
|
{
|
|
return (ui_handler_callback == ui_menu_ui_handler);
|
|
}
|
|
|
|
|
|
|
|
/***************************************************************************
|
|
TEXT GENERATORS
|
|
***************************************************************************/
|
|
|
|
/*-------------------------------------------------
|
|
disclaimer_string - print the disclaimer
|
|
text to the given buffer
|
|
-------------------------------------------------*/
|
|
|
|
static astring &disclaimer_string(running_machine &machine, astring &string)
|
|
{
|
|
string.cpy("Usage of emulators in conjunction with ROMs you don't own is forbidden by copyright law.\n\n");
|
|
string.catprintf("IF YOU ARE NOT LEGALLY ENTITLED TO PLAY \"%s\" ON THIS EMULATOR, PRESS ESC.\n\n", machine.system().description);
|
|
string.cat("Otherwise, type OK or move the joystick left then right to continue");
|
|
return string;
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
warnings_string - print the warning flags
|
|
text to the given buffer
|
|
-------------------------------------------------*/
|
|
|
|
static astring &warnings_string(running_machine &machine, astring &string)
|
|
{
|
|
#define WARNING_FLAGS ( GAME_NOT_WORKING | \
|
|
GAME_UNEMULATED_PROTECTION | \
|
|
GAME_MECHANICAL | \
|
|
GAME_WRONG_COLORS | \
|
|
GAME_IMPERFECT_COLORS | \
|
|
GAME_REQUIRES_ARTWORK | \
|
|
GAME_NO_SOUND | \
|
|
GAME_IMPERFECT_SOUND | \
|
|
GAME_IMPERFECT_GRAPHICS | \
|
|
GAME_NO_COCKTAIL)
|
|
|
|
string.reset();
|
|
|
|
/* if no warnings, nothing to return */
|
|
if (rom_load_warnings(machine) == 0 && rom_load_knownbad(machine) == 0 && !(machine.system().flags & WARNING_FLAGS))
|
|
return string;
|
|
|
|
/* add a warning if any ROMs were loaded with warnings */
|
|
if (rom_load_warnings(machine) > 0)
|
|
{
|
|
string.cat("One or more ROMs/CHDs for this game are incorrect. The " GAMENOUN " may not run correctly.\n");
|
|
if (machine.system().flags & WARNING_FLAGS)
|
|
string.cat("\n");
|
|
}
|
|
|
|
/* if we have at least one warning flag, print the general header */
|
|
if ((machine.system().flags & WARNING_FLAGS) || rom_load_knownbad(machine) > 0)
|
|
{
|
|
string.cat("There are known problems with this " GAMENOUN "\n\n");
|
|
|
|
/* add a warning if any ROMs are flagged BAD_DUMP/NO_DUMP */
|
|
if (rom_load_knownbad(machine) > 0)
|
|
string.cat("One or more ROMs/CHDs for this " GAMENOUN " have not been correctly dumped.\n");
|
|
|
|
/* add one line per warning flag */
|
|
if (input_machine_has_keyboard(machine))
|
|
string.cat("The keyboard emulation may not be 100% accurate.\n");
|
|
if (machine.system().flags & GAME_IMPERFECT_COLORS)
|
|
string.cat("The colors aren't 100% accurate.\n");
|
|
if (machine.system().flags & GAME_WRONG_COLORS)
|
|
string.cat("The colors are completely wrong.\n");
|
|
if (machine.system().flags & GAME_IMPERFECT_GRAPHICS)
|
|
string.cat("The video emulation isn't 100% accurate.\n");
|
|
if (machine.system().flags & GAME_IMPERFECT_SOUND)
|
|
string.cat("The sound emulation isn't 100% accurate.\n");
|
|
if (machine.system().flags & GAME_NO_SOUND)
|
|
string.cat("The game lacks sound.\n");
|
|
if (machine.system().flags & GAME_NO_COCKTAIL)
|
|
string.cat("Screen flipping in cocktail mode is not supported.\n");
|
|
|
|
/* check if external artwork is present before displaying this warning? */
|
|
if (machine.system().flags & GAME_REQUIRES_ARTWORK)
|
|
string.cat("The game requires external artwork files\n");
|
|
|
|
/* if there's a NOT WORKING, UNEMULATED PROTECTION or GAME MECHANICAL warning, make it stronger */
|
|
if (machine.system().flags & (GAME_NOT_WORKING | GAME_UNEMULATED_PROTECTION | GAME_MECHANICAL))
|
|
{
|
|
/* add the strings for these warnings */
|
|
if (machine.system().flags & GAME_UNEMULATED_PROTECTION)
|
|
string.cat("The game has protection which isn't fully emulated.\n");
|
|
if (machine.system().flags & GAME_NOT_WORKING)
|
|
string.cat("\nTHIS " CAPGAMENOUN " DOESN'T WORK. The emulation for this game 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 & GAME_MECHANICAL)
|
|
string.cat("\nCertain elements of this " GAMENOUN " cannot be emulated as it requires actual physical interaction or consists of mechanical devices. "
|
|
"It is not possible to fully play this " GAMENOUN ".\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 & (GAME_NOT_WORKING | GAME_UNEMULATED_PROTECTION | GAME_MECHANICAL)) == 0)
|
|
{
|
|
/* this one works, add a header and display the name of the clone */
|
|
if (!foundworking)
|
|
string.cat("\n\nThere are working clones of this game: ");
|
|
else
|
|
string.cat(", ");
|
|
string.cat(drivlist.driver().name);
|
|
foundworking = true;
|
|
}
|
|
|
|
if (foundworking)
|
|
string.cat("\n");
|
|
}
|
|
}
|
|
|
|
/* add the 'press OK' string */
|
|
string.cat("\n\nType OK or move the joystick left then right to continue");
|
|
return string;
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
game_info_astring - populate an allocated
|
|
string with the game info text
|
|
-------------------------------------------------*/
|
|
|
|
astring &game_info_astring(running_machine &machine, astring &string)
|
|
{
|
|
int scrcount = machine.devicelist().count(SCREEN);
|
|
int found_sound = FALSE;
|
|
|
|
/* print description, manufacturer, and CPU: */
|
|
string.printf("%s\n%s %s\n\nCPU:\n", machine.system().description, machine.system().year, machine.system().manufacturer);
|
|
|
|
/* loop over all CPUs */
|
|
device_execute_interface *exec = NULL;
|
|
for (bool gotone = machine.devicelist().first(exec); gotone; gotone = exec->next(exec))
|
|
{
|
|
/* 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;
|
|
device_execute_interface *scan = NULL;
|
|
for (bool gotanother = exec->next(scan); gotanother; gotanother = scan->next(scan))
|
|
{
|
|
if (exec->device().type() != scan->device().type() || exec->device().clock() != scan->device().clock())
|
|
break;
|
|
count++;
|
|
exec = scan;
|
|
}
|
|
|
|
/* if more than one, prepend a #x in front of the CPU name */
|
|
if (count > 1)
|
|
string.catprintf("%d" UTF8_MULTIPLY, count);
|
|
string.cat(exec->device().name());
|
|
|
|
/* display clock in kHz or MHz */
|
|
if (clock >= 1000000)
|
|
string.catprintf(" %d.%06d" UTF8_NBSP "MHz\n", clock / 1000000, clock % 1000000);
|
|
else
|
|
string.catprintf(" %d.%03d" UTF8_NBSP "kHz\n", clock / 1000, clock % 1000);
|
|
}
|
|
|
|
/* loop over all sound chips */
|
|
device_sound_interface *sound = NULL;
|
|
for (bool gotone = machine.devicelist().first(sound); gotone; gotone = sound->next(sound))
|
|
{
|
|
/* append the Sound: string */
|
|
if (!found_sound)
|
|
string.cat("\nSound:\n");
|
|
found_sound = TRUE;
|
|
|
|
/* count how many identical sound chips we have */
|
|
int count = 1;
|
|
device_sound_interface *scan = NULL;
|
|
for (bool gotanother = sound->next(scan); gotanother; gotanother = scan->next(scan))
|
|
{
|
|
if (sound->device().type() != scan->device().type() || sound->device().clock() != scan->device().clock())
|
|
break;
|
|
count++;
|
|
sound = scan;
|
|
}
|
|
|
|
/* if more than one, prepend a #x in front of the CPU name */
|
|
if (count > 1)
|
|
string.catprintf("%d" UTF8_MULTIPLY, count);
|
|
string.cat(sound->device().name());
|
|
|
|
/* display clock in kHz or MHz */
|
|
int clock = sound->device().clock();
|
|
if (clock >= 1000000)
|
|
string.catprintf(" %d.%06d" UTF8_NBSP "MHz\n", clock / 1000000, clock % 1000000);
|
|
else if (clock != 0)
|
|
string.catprintf(" %d.%03d" UTF8_NBSP "kHz\n", clock / 1000, clock % 1000);
|
|
else
|
|
string.cat("\n");
|
|
}
|
|
|
|
/* display screen information */
|
|
string.cat("\nVideo:\n");
|
|
if (scrcount == 0)
|
|
string.cat("None\n");
|
|
else
|
|
{
|
|
for (screen_device *screen = machine.first_screen(); screen != NULL; screen = screen->next_screen())
|
|
{
|
|
if (scrcount > 1)
|
|
{
|
|
string.cat(slider_get_screen_desc(*screen));
|
|
string.cat(": ");
|
|
}
|
|
|
|
if (screen->screen_type() == SCREEN_TYPE_VECTOR)
|
|
string.cat("Vector\n");
|
|
else
|
|
{
|
|
const rectangle &visarea = screen->visible_area();
|
|
|
|
string.catprintf("%d " UTF8_MULTIPLY " %d (%s) %f" UTF8_NBSP "Hz\n",
|
|
visarea.max_x - visarea.min_x + 1,
|
|
visarea.max_y - visarea.min_y + 1,
|
|
(machine.system().flags & ORIENTATION_SWAP_XY) ? "V" : "H",
|
|
ATTOSECONDS_TO_HZ(screen->frame_period().attoseconds));
|
|
}
|
|
}
|
|
}
|
|
|
|
return string;
|
|
}
|
|
|
|
|
|
|
|
/***************************************************************************
|
|
UI HANDLERS
|
|
***************************************************************************/
|
|
|
|
/*-------------------------------------------------
|
|
handler_messagebox - displays the current
|
|
messagebox_text string but handles no input
|
|
-------------------------------------------------*/
|
|
|
|
static UINT32 handler_messagebox(running_machine &machine, render_container *container, UINT32 state)
|
|
{
|
|
ui_draw_text_box(container, messagebox_text, JUSTIFY_LEFT, 0.5f, 0.5f, messagebox_backcolor);
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
handler_messagebox_ok - displays the current
|
|
messagebox_text string and waits for an OK
|
|
-------------------------------------------------*/
|
|
|
|
static UINT32 handler_messagebox_ok(running_machine &machine, render_container *container, UINT32 state)
|
|
{
|
|
/* draw a standard message window */
|
|
ui_draw_text_box(container, messagebox_text, JUSTIFY_LEFT, 0.5f, 0.5f, messagebox_backcolor);
|
|
|
|
/* an 'O' or left joystick kicks us to the next state */
|
|
if (state == 0 && (machine.input().code_pressed_once(KEYCODE_O) || ui_input_pressed(machine, IPT_UI_LEFT)))
|
|
state++;
|
|
|
|
/* a 'K' or right joystick exits the state */
|
|
else if (state == 1 && (machine.input().code_pressed_once(KEYCODE_K) || ui_input_pressed(machine, IPT_UI_RIGHT)))
|
|
state = UI_HANDLER_CANCEL;
|
|
|
|
/* if the user cancels, exit out completely */
|
|
else if (ui_input_pressed(machine, IPT_UI_CANCEL))
|
|
{
|
|
machine.schedule_exit();
|
|
state = UI_HANDLER_CANCEL;
|
|
}
|
|
|
|
return state;
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
handler_messagebox_anykey - displays the
|
|
current messagebox_text string and waits for
|
|
any keypress
|
|
-------------------------------------------------*/
|
|
|
|
static UINT32 handler_messagebox_anykey(running_machine &machine, render_container *container, UINT32 state)
|
|
{
|
|
/* draw a standard message window */
|
|
ui_draw_text_box(container, messagebox_text, JUSTIFY_LEFT, 0.5f, 0.5f, messagebox_backcolor);
|
|
|
|
/* if the user cancels, exit out completely */
|
|
if (ui_input_pressed(machine, 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
|
|
-------------------------------------------------*/
|
|
|
|
static void process_natural_keyboard(running_machine &machine)
|
|
{
|
|
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 (ui_input_pop_event(machine, &event))
|
|
{
|
|
/* if this was a UI_EVENT_CHAR event, post it */
|
|
if (event.event_type == UI_EVENT_CHAR)
|
|
inputx_postc(machine, 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 = &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 */
|
|
inputx_postc(machine, 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;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*-------------------------------------------------
|
|
ui_paste - does a paste from the keyboard
|
|
-------------------------------------------------*/
|
|
|
|
void ui_paste(running_machine &machine)
|
|
{
|
|
/* retrieve the clipboard text */
|
|
char *text = osd_get_clipboard_text();
|
|
|
|
/* was a result returned? */
|
|
if (text != NULL)
|
|
{
|
|
/* post the text */
|
|
inputx_post_utf8(machine, text);
|
|
|
|
/* free the string */
|
|
osd_free(text);
|
|
}
|
|
}
|
|
|
|
/*-------------------------------------------------
|
|
ui_image_handler_ingame - execute display
|
|
callback function for each image device
|
|
-------------------------------------------------*/
|
|
|
|
void ui_image_handler_ingame(running_machine &machine)
|
|
{
|
|
device_image_interface *image = NULL;
|
|
|
|
/* run display routine for devices */
|
|
if (machine.phase() == MACHINE_PHASE_RUNNING)
|
|
{
|
|
for (bool gotone = machine.devicelist().first(image); gotone; gotone = image->next(image))
|
|
{
|
|
image->call_display();
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
/*-------------------------------------------------
|
|
handler_ingame - in-game handler takes care
|
|
of the standard keypresses
|
|
-------------------------------------------------*/
|
|
|
|
static UINT32 handler_ingame(running_machine &machine, render_container *container, UINT32 state)
|
|
{
|
|
bool is_paused = machine.paused();
|
|
|
|
/* first draw the FPS counter */
|
|
if (showfps || osd_ticks() < showfps_end)
|
|
{
|
|
astring tempstring;
|
|
ui_draw_text_full(container, machine.video().speed_text(tempstring), 0.0f, 0.0f, 1.0f,
|
|
JUSTIFY_RIGHT, WRAP_WORD, DRAW_OPAQUE, ARGB_WHITE, ARGB_BLACK, NULL, NULL);
|
|
}
|
|
else
|
|
showfps_end = 0;
|
|
|
|
/* draw the profiler if visible */
|
|
if (show_profiler)
|
|
{
|
|
astring profilertext;
|
|
g_profiler.text(machine, profilertext);
|
|
ui_draw_text_full(container, profilertext, 0.0f, 0.0f, 1.0f, JUSTIFY_LEFT, WRAP_WORD, DRAW_OPAQUE, ARGB_WHITE, ARGB_BLACK, NULL, NULL);
|
|
}
|
|
|
|
/* if we're single-stepping, pause now */
|
|
if (single_step)
|
|
{
|
|
machine.pause();
|
|
single_step = FALSE;
|
|
}
|
|
|
|
/* determine if we should disable the rest of the UI */
|
|
int ui_disabled = (input_machine_has_keyboard(machine) && !machine.ui_active());
|
|
|
|
/* is ScrLk UI toggling applicable here? */
|
|
if (input_machine_has_keyboard(machine))
|
|
{
|
|
/* are we toggling the UI with ScrLk? */
|
|
if (ui_input_pressed(machine, 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())
|
|
{
|
|
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
|
|
{
|
|
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 (ui_get_use_natural_keyboard(machine) && (machine.phase() == MACHINE_PHASE_RUNNING))
|
|
process_natural_keyboard(machine);
|
|
|
|
if (!ui_disabled)
|
|
{
|
|
/* paste command */
|
|
if (ui_input_pressed(machine, IPT_UI_PASTE))
|
|
ui_paste(machine);
|
|
}
|
|
|
|
ui_image_handler_ingame(machine);
|
|
|
|
if (ui_disabled) return ui_disabled;
|
|
|
|
if (ui_input_pressed(machine, IPT_UI_CANCEL))
|
|
{
|
|
if (!machine.options().confirm_quit())
|
|
machine.schedule_exit();
|
|
else
|
|
return ui_set_handler(handler_confirm_quit, 0);
|
|
}
|
|
|
|
/* turn on menus if requested */
|
|
if (ui_input_pressed(machine, IPT_UI_CONFIGURE))
|
|
return 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 && ui_input_pressed(machine, IPT_UI_ON_SCREEN_DISPLAY))
|
|
return ui_set_handler(ui_slider_ui_handler, 1);
|
|
|
|
/* handle a reset request */
|
|
if (ui_input_pressed(machine, IPT_UI_RESET_MACHINE))
|
|
machine.schedule_hard_reset();
|
|
if (ui_input_pressed(machine, IPT_UI_SOFT_RESET))
|
|
machine.schedule_soft_reset();
|
|
|
|
/* handle a request to display graphics/palette */
|
|
if (ui_input_pressed(machine, IPT_UI_SHOW_GFX))
|
|
{
|
|
if (!is_paused)
|
|
machine.pause();
|
|
return ui_set_handler(ui_gfx_ui_handler, is_paused);
|
|
}
|
|
|
|
/* handle a save state request */
|
|
if (ui_input_pressed(machine, IPT_UI_SAVE_STATE))
|
|
{
|
|
machine.pause();
|
|
return ui_set_handler(handler_load_save, LOADSAVE_SAVE);
|
|
}
|
|
|
|
/* handle a load state request */
|
|
if (ui_input_pressed(machine, IPT_UI_LOAD_STATE))
|
|
{
|
|
machine.pause();
|
|
return ui_set_handler(handler_load_save, LOADSAVE_LOAD);
|
|
}
|
|
|
|
/* handle a save snapshot request */
|
|
if (ui_input_pressed(machine, IPT_UI_SNAPSHOT))
|
|
machine.video().save_active_screen_snapshots();
|
|
|
|
/* toggle pause */
|
|
if (ui_input_pressed(machine, IPT_UI_PAUSE))
|
|
{
|
|
/* with a shift key, it is single step */
|
|
if (is_paused && (machine.input().code_pressed(KEYCODE_LSHIFT) || machine.input().code_pressed(KEYCODE_RSHIFT)))
|
|
{
|
|
single_step = TRUE;
|
|
machine.resume();
|
|
}
|
|
else if (machine.paused())
|
|
machine.resume();
|
|
else
|
|
machine.pause();
|
|
}
|
|
|
|
/* handle a toggle cheats request */
|
|
if (ui_input_pressed(machine, IPT_UI_TOGGLE_CHEAT))
|
|
machine.cheat().set_enable(!machine.cheat().enabled());
|
|
|
|
/* toggle movie recording */
|
|
if (ui_input_pressed(machine, IPT_UI_RECORD_MOVIE))
|
|
{
|
|
if (!machine.video().is_recording())
|
|
{
|
|
machine.video().begin_recording(NULL, video_manager::MF_MNG);
|
|
popmessage("REC START");
|
|
}
|
|
else
|
|
{
|
|
machine.video().end_recording();
|
|
popmessage("REC STOP");
|
|
}
|
|
}
|
|
|
|
/* toggle profiler display */
|
|
if (ui_input_pressed(machine, IPT_UI_SHOW_PROFILER))
|
|
ui_set_show_profiler(!ui_get_show_profiler());
|
|
|
|
/* toggle FPS display */
|
|
if (ui_input_pressed(machine, IPT_UI_SHOW_FPS))
|
|
ui_set_show_fps(!ui_get_show_fps());
|
|
|
|
/* increment frameskip? */
|
|
if (ui_input_pressed(machine, IPT_UI_FRAMESKIP_INC))
|
|
{
|
|
/* 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 */
|
|
ui_show_fps_temp(2.0);
|
|
}
|
|
|
|
/* decrement frameskip? */
|
|
if (ui_input_pressed(machine, IPT_UI_FRAMESKIP_DEC))
|
|
{
|
|
/* 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 */
|
|
ui_show_fps_temp(2.0);
|
|
}
|
|
|
|
/* toggle throttle? */
|
|
if (ui_input_pressed(machine, IPT_UI_THROTTLE))
|
|
machine.video().set_throttled(!machine.video().throttled());
|
|
|
|
/* check for fast forward */
|
|
if (input_type_pressed(machine, IPT_UI_FAST_FORWARD, 0))
|
|
{
|
|
machine.video().set_fastforward(true);
|
|
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
|
|
-------------------------------------------------*/
|
|
|
|
static UINT32 handler_load_save(running_machine &machine, render_container *container, UINT32 state)
|
|
{
|
|
char filename[20];
|
|
input_code code;
|
|
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)
|
|
ui_draw_message_window(container, "Select position to save to");
|
|
else
|
|
ui_draw_message_window(container, "Select position to load from");
|
|
|
|
/* check for cancel key */
|
|
if (ui_input_pressed(machine, IPT_UI_CANCEL))
|
|
{
|
|
/* display a popup indicating things were cancelled */
|
|
if (state == LOADSAVE_SAVE)
|
|
popmessage("Save cancelled");
|
|
else
|
|
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)
|
|
return state;
|
|
|
|
/* display a popup indicating that the save will proceed */
|
|
sprintf(filename, "%c", file);
|
|
if (state == LOADSAVE_SAVE)
|
|
{
|
|
popmessage("Save to position %c", file);
|
|
machine.schedule_save(filename);
|
|
}
|
|
else
|
|
{
|
|
popmessage("Load from position %c", file);
|
|
machine.schedule_load(filename);
|
|
}
|
|
|
|
/* remove the pause and reset the state */
|
|
machine.resume();
|
|
return UI_HANDLER_CANCEL;
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
handler_confirm_quit - leads the user through
|
|
confirming quit emulation
|
|
-------------------------------------------------*/
|
|
|
|
static UINT32 handler_confirm_quit(running_machine &machine, render_container *container, UINT32 state)
|
|
{
|
|
astring quit_message("Are you sure you want to quit?\n\n");
|
|
quit_message.cat("Press ''UI Select'' (default: Enter) to quit,\n");
|
|
quit_message.cat("Press ''UI Cancel'' (default: Esc) to return to emulation.");
|
|
|
|
ui_draw_text_box(container, quit_message, JUSTIFY_CENTER, 0.5f, 0.5f, UI_RED_COLOR);
|
|
machine.pause();
|
|
|
|
/* if the user press ENTER, quit the game */
|
|
if (ui_input_pressed(machine, IPT_UI_SELECT))
|
|
machine.schedule_exit();
|
|
|
|
/* if the user press ESC, just continue */
|
|
else if (ui_input_pressed(machine, IPT_UI_CANCEL))
|
|
{
|
|
machine.resume();
|
|
state = UI_HANDLER_CANCEL;
|
|
}
|
|
|
|
return state;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
SLIDER CONTROLS
|
|
***************************************************************************/
|
|
|
|
/*-------------------------------------------------
|
|
ui_get_slider_list - get the list of sliders
|
|
-------------------------------------------------*/
|
|
|
|
const slider_state *ui_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;
|
|
strcpy(state->description, title);
|
|
|
|
return state;
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
slider_init - initialize the list of slider
|
|
controls
|
|
-------------------------------------------------*/
|
|
|
|
static slider_state *slider_init(running_machine &machine)
|
|
{
|
|
input_field_config *field;
|
|
input_port_config *port;
|
|
device_t *device;
|
|
slider_state *listhead = NULL;
|
|
slider_state **tailptr = &listhead;
|
|
astring string;
|
|
int item;
|
|
|
|
/* add overall volume */
|
|
*tailptr = slider_alloc(machine, "Master Volume", -32, 0, 0, 1, slider_volume, NULL);
|
|
tailptr = &(*tailptr)->next;
|
|
|
|
/* add per-channel volume */
|
|
speaker_input info;
|
|
for (item = 0; machine.sound().indexed_speaker_input(item, info); item++)
|
|
{
|
|
INT32 maxval = 2000;
|
|
INT32 defval = info.stream->initial_input_gain(info.inputnum) * 1000.0f + 0.5f;
|
|
|
|
if (defval > 1000)
|
|
maxval = 2 * defval;
|
|
|
|
info.stream->input_name(info.inputnum, string);
|
|
string.cat(" Volume");
|
|
*tailptr = slider_alloc(machine, string, 0, defval, maxval, 20, slider_mixervol, (void *)(FPTR)item);
|
|
tailptr = &(*tailptr)->next;
|
|
}
|
|
|
|
/* add analog adjusters */
|
|
for (port = machine.m_portlist.first(); port != NULL; port = port->next())
|
|
for (field = port->fieldlist().first(); field != NULL; field = field->next())
|
|
if (field->type == IPT_ADJUSTER)
|
|
{
|
|
void *param = (void *)field;
|
|
*tailptr = slider_alloc(machine, field->name, 0, field->defvalue, 100, 1, slider_adjuster, param);
|
|
tailptr = &(*tailptr)->next;
|
|
}
|
|
|
|
/* add CPU overclocking (cheat only) */
|
|
if (machine.options().cheat())
|
|
{
|
|
device_execute_interface *exec = NULL;
|
|
for (bool gotone = machine.devicelist().first(exec); gotone; gotone = exec->next(exec))
|
|
{
|
|
void *param = (void *)&exec->device();
|
|
string.printf("Overclock CPU %s", exec->device().tag());
|
|
*tailptr = slider_alloc(machine, string, 10, 1000, 2000, 1, slider_overclock, param);
|
|
tailptr = &(*tailptr)->next;
|
|
}
|
|
}
|
|
|
|
/* add screen parameters */
|
|
for (screen_device *screen = machine.first_screen(); screen != NULL; screen = screen->next_screen())
|
|
{
|
|
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;
|
|
|
|
/* add refresh rate tweaker */
|
|
if (machine.options().cheat())
|
|
{
|
|
string.printf("%s Refresh Rate", slider_get_screen_desc(*screen));
|
|
*tailptr = slider_alloc(machine, string, -10000, 0, 10000, 1000, slider_refresh, param);
|
|
tailptr = &(*tailptr)->next;
|
|
}
|
|
|
|
/* add standard brightness/contrast/gamma controls per-screen */
|
|
string.printf("%s Brightness", slider_get_screen_desc(*screen));
|
|
*tailptr = slider_alloc(machine, string, 100, 1000, 2000, 10, slider_brightness, param);
|
|
tailptr = &(*tailptr)->next;
|
|
string.printf("%s Contrast", slider_get_screen_desc(*screen));
|
|
*tailptr = slider_alloc(machine, string, 100, 1000, 2000, 50, slider_contrast, param);
|
|
tailptr = &(*tailptr)->next;
|
|
string.printf("%s Gamma", slider_get_screen_desc(*screen));
|
|
*tailptr = slider_alloc(machine, string, 100, 1000, 3000, 50, slider_gamma, param);
|
|
tailptr = &(*tailptr)->next;
|
|
|
|
/* add scale and offset controls per-screen */
|
|
string.printf("%s Horiz Stretch", slider_get_screen_desc(*screen));
|
|
*tailptr = slider_alloc(machine, string, 500, defxscale, 1500, 2, slider_xscale, param);
|
|
tailptr = &(*tailptr)->next;
|
|
string.printf("%s Horiz Position", slider_get_screen_desc(*screen));
|
|
*tailptr = slider_alloc(machine, string, -500, defxoffset, 500, 2, slider_xoffset, param);
|
|
tailptr = &(*tailptr)->next;
|
|
string.printf("%s Vert Stretch", slider_get_screen_desc(*screen));
|
|
*tailptr = slider_alloc(machine, string, 500, defyscale, 1500, 2, slider_yscale, param);
|
|
tailptr = &(*tailptr)->next;
|
|
string.printf("%s Vert Position", slider_get_screen_desc(*screen));
|
|
*tailptr = slider_alloc(machine, string, -500, defyoffset, 500, 2, slider_yoffset, param);
|
|
tailptr = &(*tailptr)->next;
|
|
}
|
|
|
|
for (device = machine.devicelist().first(); device != NULL; device = device->next())
|
|
if (device_is_laserdisc(device))
|
|
{
|
|
const laserdisc_config *config = (const laserdisc_config *)downcast<const legacy_device_base *>(device)->inline_config();
|
|
if (config->overupdate != NULL)
|
|
{
|
|
int defxscale = floor(config->overscalex * 1000.0f + 0.5f);
|
|
int defyscale = floor(config->overscaley * 1000.0f + 0.5f);
|
|
int defxoffset = floor(config->overposx * 1000.0f + 0.5f);
|
|
int defyoffset = floor(config->overposy * 1000.0f + 0.5f);
|
|
void *param = (void *)device;
|
|
|
|
/* add scale and offset controls per-overlay */
|
|
string.printf("%s Horiz Stretch", slider_get_laserdisc_desc(device));
|
|
*tailptr = slider_alloc(machine, string, 500, (defxscale == 0) ? 1000 : defxscale, 1500, 2, slider_overxscale, param);
|
|
tailptr = &(*tailptr)->next;
|
|
string.printf("%s Horiz Position", slider_get_laserdisc_desc(device));
|
|
*tailptr = slider_alloc(machine, string, -500, defxoffset, 500, 2, slider_overxoffset, param);
|
|
tailptr = &(*tailptr)->next;
|
|
string.printf("%s Vert Stretch", slider_get_laserdisc_desc(device));
|
|
*tailptr = slider_alloc(machine, string, 500, (defyscale == 0) ? 1000 : defyscale, 1500, 2, slider_overyscale, param);
|
|
tailptr = &(*tailptr)->next;
|
|
string.printf("%s Vert Position", slider_get_laserdisc_desc(device));
|
|
*tailptr = slider_alloc(machine, string, -500, defyoffset, 500, 2, slider_overyoffset, param);
|
|
tailptr = &(*tailptr)->next;
|
|
}
|
|
}
|
|
|
|
for (screen_device *screen = machine.first_screen(); screen != NULL; screen = screen->next_screen())
|
|
if (screen->screen_type() == SCREEN_TYPE_VECTOR)
|
|
{
|
|
/* add flicker control */
|
|
*tailptr = slider_alloc(machine, "Vector Flicker", 0, 0, 1000, 10, slider_flicker, NULL);
|
|
tailptr = &(*tailptr)->next;
|
|
*tailptr = slider_alloc(machine, "Beam Width", 10, 100, 1000, 10, slider_beam, NULL);
|
|
tailptr = &(*tailptr)->next;
|
|
break;
|
|
}
|
|
|
|
#ifdef MAME_DEBUG
|
|
/* add crosshair adjusters */
|
|
for (port = machine.m_portlist.first(); port != NULL; port = port->next())
|
|
for (field = port->fieldlist().first(); field != NULL; field = field->next())
|
|
if (field->crossaxis != CROSSHAIR_AXIS_NONE && field->player == 0)
|
|
{
|
|
void *param = (void *)field;
|
|
string.printf("Crosshair Scale %s", (field->crossaxis == CROSSHAIR_AXIS_X) ? "X" : "Y");
|
|
*tailptr = slider_alloc(machine, string, -3000, 1000, 3000, 100, slider_crossscale, param);
|
|
tailptr = &(*tailptr)->next;
|
|
string.printf("Crosshair Offset %s", (field->crossaxis == CROSSHAIR_AXIS_X) ? "X" : "Y");
|
|
*tailptr = slider_alloc(machine, string, -3000, 0, 3000, 100, slider_crossoffset, param);
|
|
tailptr = &(*tailptr)->next;
|
|
}
|
|
#endif
|
|
|
|
return listhead;
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
slider_volume - global volume slider callback
|
|
-------------------------------------------------*/
|
|
|
|
static INT32 slider_volume(running_machine &machine, void *arg, astring *string, INT32 newval)
|
|
{
|
|
if (newval != SLIDER_NOCHANGE)
|
|
machine.sound().set_attenuation(newval);
|
|
if (string != NULL)
|
|
string->printf("%3ddB", machine.sound().attenuation());
|
|
return machine.sound().attenuation();
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
slider_mixervol - single channel volume
|
|
slider callback
|
|
-------------------------------------------------*/
|
|
|
|
static INT32 slider_mixervol(running_machine &machine, void *arg, astring *string, INT32 newval)
|
|
{
|
|
speaker_input info;
|
|
if (!machine.sound().indexed_speaker_input((FPTR)arg, info))
|
|
return 0;
|
|
if (newval != SLIDER_NOCHANGE)
|
|
{
|
|
INT32 curval = floor(info.stream->input_gain(info.inputnum) * 1000.0f + 0.5f);
|
|
if (newval > curval && (newval - curval) <= 4) newval += 4; // round up on increment
|
|
info.stream->set_input_gain(info.inputnum, (float)newval * 0.001f);
|
|
}
|
|
if (string != NULL)
|
|
string->printf("%4.2f", info.stream->input_gain(info.inputnum));
|
|
return floor(info.stream->input_gain(info.inputnum) * 1000.0f + 0.5f);
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
slider_adjuster - analog adjuster slider
|
|
callback
|
|
-------------------------------------------------*/
|
|
|
|
static INT32 slider_adjuster(running_machine &machine, void *arg, astring *string, INT32 newval)
|
|
{
|
|
const input_field_config *field = (const input_field_config *)arg;
|
|
input_field_user_settings settings;
|
|
|
|
input_field_get_user_settings(field, &settings);
|
|
if (newval != SLIDER_NOCHANGE)
|
|
{
|
|
settings.value = newval;
|
|
input_field_set_user_settings(field, &settings);
|
|
}
|
|
if (string != NULL)
|
|
string->printf("%d%%", settings.value);
|
|
return settings.value;
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
slider_overclock - CPU overclocker slider
|
|
callback
|
|
-------------------------------------------------*/
|
|
|
|
static INT32 slider_overclock(running_machine &machine, void *arg, astring *string, INT32 newval)
|
|
{
|
|
device_t *cpu = (device_t *)arg;
|
|
if (newval != SLIDER_NOCHANGE)
|
|
cpu->set_clock_scale((float)newval * 0.001f);
|
|
if (string != NULL)
|
|
string->printf("%3.0f%%", floor(cpu->clock_scale() * 100.0f + 0.5f));
|
|
return floor(cpu->clock_scale() * 1000.0f + 0.5f);
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
slider_refresh - refresh rate slider callback
|
|
-------------------------------------------------*/
|
|
|
|
static INT32 slider_refresh(running_machine &machine, void *arg, astring *string, 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 (string != NULL)
|
|
string->printf("%.3ffps", ATTOSECONDS_TO_HZ(machine.primary_screen->frame_period().attoseconds));
|
|
refresh = ATTOSECONDS_TO_HZ(machine.primary_screen->frame_period().attoseconds);
|
|
return floor((refresh - defrefresh) * 1000.0f + 0.5f);
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
slider_brightness - screen brightness slider
|
|
callback
|
|
-------------------------------------------------*/
|
|
|
|
static INT32 slider_brightness(running_machine &machine, void *arg, astring *string, 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 (string != NULL)
|
|
string->printf("%.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, astring *string, 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 (string != NULL)
|
|
string->printf("%.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, astring *string, 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 (string != NULL)
|
|
string->printf("%.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, astring *string, 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 (string != NULL)
|
|
string->printf("%.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, astring *string, 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 (string != NULL)
|
|
string->printf("%.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, astring *string, 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 (string != NULL)
|
|
string->printf("%.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, astring *string, 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 (string != NULL)
|
|
string->printf("%.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, astring *string, INT32 newval)
|
|
{
|
|
device_t *laserdisc = (device_t *)arg;
|
|
laserdisc_config settings;
|
|
|
|
laserdisc_get_config(laserdisc, &settings);
|
|
if (newval != SLIDER_NOCHANGE)
|
|
{
|
|
settings.overscalex = (float)newval * 0.001f;
|
|
laserdisc_set_config(laserdisc, &settings);
|
|
}
|
|
if (string != NULL)
|
|
string->printf("%.3f", settings.overscalex);
|
|
return floor(settings.overscalex * 1000.0f + 0.5f);
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
slider_overyscale - screen vertical scale slider
|
|
callback
|
|
-------------------------------------------------*/
|
|
|
|
static INT32 slider_overyscale(running_machine &machine, void *arg, astring *string, INT32 newval)
|
|
{
|
|
device_t *laserdisc = (device_t *)arg;
|
|
laserdisc_config settings;
|
|
|
|
laserdisc_get_config(laserdisc, &settings);
|
|
if (newval != SLIDER_NOCHANGE)
|
|
{
|
|
settings.overscaley = (float)newval * 0.001f;
|
|
laserdisc_set_config(laserdisc, &settings);
|
|
}
|
|
if (string != NULL)
|
|
string->printf("%.3f", settings.overscaley);
|
|
return floor(settings.overscaley * 1000.0f + 0.5f);
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
slider_overxoffset - screen horizontal position
|
|
slider callback
|
|
-------------------------------------------------*/
|
|
|
|
static INT32 slider_overxoffset(running_machine &machine, void *arg, astring *string, INT32 newval)
|
|
{
|
|
device_t *laserdisc = (device_t *)arg;
|
|
laserdisc_config settings;
|
|
|
|
laserdisc_get_config(laserdisc, &settings);
|
|
if (newval != SLIDER_NOCHANGE)
|
|
{
|
|
settings.overposx = (float)newval * 0.001f;
|
|
laserdisc_set_config(laserdisc, &settings);
|
|
}
|
|
if (string != NULL)
|
|
string->printf("%.3f", settings.overposx);
|
|
return floor(settings.overposx * 1000.0f + 0.5f);
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
slider_overyoffset - screen vertical position
|
|
slider callback
|
|
-------------------------------------------------*/
|
|
|
|
static INT32 slider_overyoffset(running_machine &machine, void *arg, astring *string, INT32 newval)
|
|
{
|
|
device_t *laserdisc = (device_t *)arg;
|
|
laserdisc_config settings;
|
|
|
|
laserdisc_get_config(laserdisc, &settings);
|
|
if (newval != SLIDER_NOCHANGE)
|
|
{
|
|
settings.overposy = (float)newval * 0.001f;
|
|
laserdisc_set_config(laserdisc, &settings);
|
|
}
|
|
if (string != NULL)
|
|
string->printf("%.3f", settings.overposy);
|
|
return floor(settings.overposy * 1000.0f + 0.5f);
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
slider_flicker - vector flicker slider
|
|
callback
|
|
-------------------------------------------------*/
|
|
|
|
static INT32 slider_flicker(running_machine &machine, void *arg, astring *string, INT32 newval)
|
|
{
|
|
if (newval != SLIDER_NOCHANGE)
|
|
vector_set_flicker((float)newval * 0.1f);
|
|
if (string != NULL)
|
|
string->printf("%1.2f", vector_get_flicker());
|
|
return floor(vector_get_flicker() * 10.0f + 0.5f);
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
slider_beam - vector beam width slider
|
|
callback
|
|
-------------------------------------------------*/
|
|
|
|
static INT32 slider_beam(running_machine &machine, void *arg, astring *string, INT32 newval)
|
|
{
|
|
if (newval != SLIDER_NOCHANGE)
|
|
vector_set_beam((float)newval * 0.01f);
|
|
if (string != NULL)
|
|
string->printf("%1.2f", vector_get_beam());
|
|
return floor(vector_get_beam() * 100.0f + 0.5f);
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
slider_get_screen_desc - returns the
|
|
description for a given screen
|
|
-------------------------------------------------*/
|
|
|
|
static char *slider_get_screen_desc(screen_device &screen)
|
|
{
|
|
int scrcount = screen.machine().devicelist().count(SCREEN);
|
|
static char descbuf[256];
|
|
|
|
if (scrcount > 1)
|
|
sprintf(descbuf, "Screen '%s'", screen.tag());
|
|
else
|
|
strcpy(descbuf, "Screen");
|
|
|
|
return descbuf;
|
|
}
|
|
|
|
/*-------------------------------------------------
|
|
slider_get_laserdisc_desc - returns the
|
|
description for a given laseridsc
|
|
-------------------------------------------------*/
|
|
static char *slider_get_laserdisc_desc(device_t *laserdisc)
|
|
{
|
|
static char descbuf[256];
|
|
for (device_t *device = laserdisc->machine().devicelist().first(); device != NULL; device = device->next())
|
|
if (device_is_laserdisc(device) && device != laserdisc)
|
|
{
|
|
sprintf(descbuf, "Laserdisc '%s'", laserdisc->tag());
|
|
return descbuf;
|
|
}
|
|
|
|
strcpy(descbuf, "Laserdisc");
|
|
return descbuf;
|
|
}
|
|
|
|
/*-------------------------------------------------
|
|
slider_crossscale - crosshair scale slider
|
|
callback
|
|
-------------------------------------------------*/
|
|
|
|
#ifdef MAME_DEBUG
|
|
static INT32 slider_crossscale(running_machine &machine, void *arg, astring *string, INT32 newval)
|
|
{
|
|
input_field_config *field = (input_field_config *)arg;
|
|
|
|
if (newval != SLIDER_NOCHANGE)
|
|
field->crossscale = (float)newval * 0.001f;
|
|
if (string != NULL)
|
|
string->printf("%s %s %1.3f", "Crosshair Scale", (field->crossaxis == CROSSHAIR_AXIS_X) ? "X" : "Y", (float)newval * 0.001f);
|
|
return floor(field->crossscale * 1000.0f + 0.5f);
|
|
}
|
|
#endif
|
|
|
|
|
|
/*-------------------------------------------------
|
|
slider_crossoffset - crosshair scale slider
|
|
callback
|
|
-------------------------------------------------*/
|
|
|
|
#ifdef MAME_DEBUG
|
|
static INT32 slider_crossoffset(running_machine &machine, void *arg, astring *string, INT32 newval)
|
|
{
|
|
input_field_config *field = (input_field_config *)arg;
|
|
|
|
if (newval != SLIDER_NOCHANGE)
|
|
field->crossoffset = (float)newval * 0.001f;
|
|
if (string != NULL)
|
|
string->printf("%s %s %1.3f", "Crosshair Offset", (field->crossaxis == CROSSHAIR_AXIS_X) ? "X" : "Y", (float)newval * 0.001f);
|
|
return field->crossoffset;
|
|
}
|
|
#endif
|
|
|
|
|
|
/*-------------------------------------------------
|
|
ui_get_use_natural_keyboard - returns
|
|
whether the natural keyboard is active
|
|
-------------------------------------------------*/
|
|
|
|
int ui_get_use_natural_keyboard(running_machine &machine)
|
|
{
|
|
return ui_use_natural_keyboard;
|
|
}
|
|
|
|
|
|
|
|
/*-------------------------------------------------
|
|
ui_set_use_natural_keyboard - specifies
|
|
whether the natural keyboard is active
|
|
-------------------------------------------------*/
|
|
|
|
void ui_set_use_natural_keyboard(running_machine &machine, int use_natural_keyboard)
|
|
{
|
|
ui_use_natural_keyboard = use_natural_keyboard;
|
|
astring error;
|
|
machine.options().set_value(OPTION_NATURAL_KEYBOARD, use_natural_keyboard, OPTION_PRIORITY_CMDLINE, error);
|
|
assert(!error);
|
|
}
|
|
|