/********************************************************************* 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 /*************************************************************************** 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); /* 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, ui_exit); /* 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 */ input_code_poll_switches(machine, TRUE); while (input_code_poll_switches(machine, FALSE) != 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.m_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.m_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 gotone = exec->next(scan); gotone; gotone = 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.m_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 && (input_code_pressed_once(machine, KEYCODE_O) || ui_input_pressed(machine, IPT_UI_LEFT))) state++; /* a 'K' or right joystick exits the state */ else if (state == 1 && (input_code_pressed_once(machine, 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 (input_code_poll_switches(machine, FALSE) != 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 = input_code_from_input_item_id(machine, itemid); /* ...and determine if it is pressed */ pressed = input_code_pressed(machine, 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); } 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.m_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)) machine.schedule_exit(); /* 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 && (input_code_pressed(machine, KEYCODE_LSHIFT) || input_code_pressed(machine, 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 (code = KEYCODE_A; code <= (input_code)KEYCODE_Z; code++) if (input_code_pressed_once(machine, code)) file = code - KEYCODE_A + 'a'; if (file == 0) for (code = KEYCODE_0; code <= (input_code)KEYCODE_9; code++) if (input_code_pressed_once(machine, code)) file = code - KEYCODE_0 + '0'; if (file == 0) for (code = KEYCODE_0_PAD; code <= (input_code)KEYCODE_9_PAD; code++) if (input_code_pressed_once(machine, code)) file = code - KEYCODE_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; } /*************************************************************************** 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) { const input_field_config *field; const 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; 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.m_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->config().xscale() * 1000.0f + 0.5f); int defyscale = floor(screen->config().yscale() * 1000.0f + 0.5f); int defxoffset = floor(screen->config().xoffset() * 1000.0f + 0.5f); int defyoffset = floor(screen->config().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.m_devicelist.first(LASERDISC); device != NULL; device = device->typenext()) { const laserdisc_config *config = (const laserdisc_config *)downcast(device->baseconfig()).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; 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(arg); double defrefresh = ATTOSECONDS_TO_HZ(screen->config().refresh()); 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(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(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(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(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(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(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(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().m_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) { int ldcount = laserdisc->machine().m_devicelist.count(LASERDISC); static char descbuf[256]; if (ldcount > 1) sprintf(descbuf, "Laserdisc '%s'", laserdisc->tag()); else 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); }