- --
Viewing Issue Advanced Details
[ Jump to Notes ]
ID | Category [?] | Severity [?] | Reproducibility | Date Submitted | Last Update |
---|---|---|---|---|---|
04469 | Interface | Feature | N/A | Aug 24, 2011, 14:54 | Aug 28, 2011, 15:56 |
Tester | NekoEd | View Status | Public | Platform | |
Assigned To | Resolution | Won't fix | OS | ||
Status [?] | Closed | Driver | |||
Version | 0.143u3 | Fixed in Version | Build | ||
Fixed in Git Commit | Github Pull Request # | ||||
Summary | 04469: UI: New mini UI feature for mechanical games | ||||
Description | I have altered uimenu.c to cause games with the "GAME_MECHANICAL" flag to display that they are not working due to unemulated mechanical dependencies rather than merely "NOT WORKING". Please forgive the attached entirety of uimenu.c as I forgot to save an old version first for patching. A search for "Mechanical Dependency" will quickly locate the small amount of added code. | ||||
Steps To Reproduce | |||||
Additional Information | |||||
Github Commit | |||||
Flags | |||||
Regression Version | |||||
Affected Sets / Systems | UI | ||||
Attached Files
|
uimenu.c (134,146 bytes) Aug 24, 2011, 14:54 Uploaded by BarnacleEd uimenu.c with mechanical note [Show Content] [Hide Content]/********************************************************************* uimenu.c Internal MAME menus for the 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 "ui.h" #include "rendutil.h" #include "cheat.h" #include "uiimage.h" #include "uiinput.h" #include "uimenu.h" #include "audit.h" #include "crsshair.h" #include <ctype.h> #include "imagedev/cassette.h" #include "imagedev/bitbngr.h" /*************************************************************************** CONSTANTS ***************************************************************************/ #define UI_MENU_POOL_SIZE 65536 #define UI_MENU_ALLOC_ITEMS 256 #define VISIBLE_GAMES_IN_LIST 15 #define MAX_PHYSICAL_DIPS 10 #define MAX_INPUT_PORTS 32 #define MAX_BITS_PER_PORT 32 /* DIP switch rendering parameters */ #define DIP_SWITCH_HEIGHT 0.05f #define DIP_SWITCH_SPACING 0.01 #define SINGLE_TOGGLE_SWITCH_FIELD_WIDTH 0.025f #define SINGLE_TOGGLE_SWITCH_WIDTH 0.020f /* make the switch 80% of the width space and 1/2 of the switch height */ #define PERCENTAGE_OF_HALF_FIELD_USED 0.80f #define SINGLE_TOGGLE_SWITCH_HEIGHT ((DIP_SWITCH_HEIGHT / 2) * PERCENTAGE_OF_HALF_FIELD_USED) enum { INPUT_TYPE_DIGITAL = 0, INPUT_TYPE_ANALOG = 1, INPUT_TYPE_ANALOG_DEC = INPUT_TYPE_ANALOG + SEQ_TYPE_DECREMENT, INPUT_TYPE_ANALOG_INC = INPUT_TYPE_ANALOG + SEQ_TYPE_INCREMENT, INPUT_TYPE_TOTAL = INPUT_TYPE_ANALOG + SEQ_TYPE_TOTAL }; enum { ANALOG_ITEM_KEYSPEED = 0, ANALOG_ITEM_CENTERSPEED, ANALOG_ITEM_REVERSE, ANALOG_ITEM_SENSITIVITY, ANALOG_ITEM_COUNT }; enum { MEMCARD_ITEM_SELECT = 1, MEMCARD_ITEM_LOAD, MEMCARD_ITEM_EJECT, MEMCARD_ITEM_CREATE }; enum { VIDEO_ITEM_ROTATE = 0x80000000, VIDEO_ITEM_BACKDROPS, VIDEO_ITEM_OVERLAYS, VIDEO_ITEM_BEZELS, VIDEO_ITEM_CPANELS, VIDEO_ITEM_MARQUEES, VIDEO_ITEM_ZOOM, VIDEO_ITEM_VIEW }; enum { CROSSHAIR_ITEM_VIS = 0, CROSSHAIR_ITEM_PIC, CROSSHAIR_ITEM_AUTO_TIME }; /*************************************************************************** TYPE DEFINITIONS ***************************************************************************/ typedef struct _ui_menu_pool ui_menu_pool; struct _ui_menu_pool { ui_menu_pool * next; /* chain to next one */ UINT8 * top; /* top of the pool */ UINT8 * end; /* end of the pool */ }; typedef struct _ui_menu_item ui_menu_item; struct _ui_menu_item { const char * text; const char * subtext; UINT32 flags; void * ref; }; class ui_menu { public: ui_menu(running_machine &machine) : m_machine(machine) { } running_machine &machine() const { return m_machine; } render_container * container; /* render_container we render to */ ui_menu_handler_func handler; /* handler callback */ void * parameter; /* parameter */ ui_menu_event menu_event; /* the UI menu_event that occurred */ ui_menu * parent; /* pointer to parent menu */ void * state; /* menu-specific state */ ui_menu_destroy_state_func destroy_state; /* destroy state callback */ int resetpos; /* reset position */ void * resetref; /* reset reference */ int selected; /* which item is selected */ int hover; /* which item is being hovered over */ int visitems; /* number of visible items */ int numitems; /* number of items in the menu */ int allocitems; /* allocated size of array */ ui_menu_item * item; /* pointer to array of items */ ui_menu_custom_func custom; /* callback for custom rendering */ float customtop; /* amount of extra height to add at the top */ float custombottom; /* amount of extra height to add at the bottom */ ui_menu_pool * pool; /* list of memory pools */ private: running_machine & m_machine; /* machine we are attached to */ }; /* internal input menu item data */ typedef struct _input_item_data input_item_data; struct _input_item_data { input_item_data * next; /* pointer to next item in the list */ const void * ref; /* reference to type description for global inputs or field for game inputs */ input_seq_type seqtype; /* sequence type */ input_seq seq; /* copy of the live sequence */ const input_seq * defseq; /* pointer to the default sequence */ const char * name; /* pointer to the base name of the item */ UINT16 sortorder; /* sorting information */ UINT8 type; /* type of port */ }; /* internal analog menu item data */ typedef struct _analog_item_data analog_item_data; struct _analog_item_data { input_field_config *field; int type; int min, max; int cur; int defvalue; }; /* DIP switch descriptor */ typedef struct _dip_descriptor dip_descriptor; struct _dip_descriptor { dip_descriptor * next; const char * name; UINT32 mask; UINT32 state; }; /* extended settings menu state */ typedef struct _settings_menu_state settings_menu_state; struct _settings_menu_state { dip_descriptor * diplist; }; /* extended input menu state */ typedef struct _input_menu_state input_menu_state; struct _input_menu_state { UINT16 last_sortorder; const void * pollingref; input_item_data * pollingitem; UINT8 record_next; input_seq starting_seq; }; /* extended select game menu state */ typedef struct _select_game_state select_game_state; struct _select_game_state { UINT8 error; UINT8 rerandomize; char search[40]; int matchlist[VISIBLE_GAMES_IN_LIST]; const game_driver * driverlist[1]; }; /* internal crosshair menu item data */ typedef struct _crosshair_item_data crosshair_item_data; struct _crosshair_item_data { UINT8 type; UINT8 player; UINT8 min, max; UINT8 cur; UINT8 defvalue; char last_name[CROSSHAIR_PIC_NAME_LENGTH + 1]; char next_name[CROSSHAIR_PIC_NAME_LENGTH + 1]; }; /*************************************************************************** GLOBAL VARIABLES ***************************************************************************/ /* menu states */ static ui_menu *menu_stack; static ui_menu *menu_free; static bitmap_t *hilight_bitmap; static render_texture *hilight_texture; static render_texture *arrow_texture; static const char priortext[] = "Return to Prior Menu"; static const char backtext[] = "Return to " CAPSTARTGAMENOUN; static const char exittext[] = "Exit"; // temporary hack until this is C++-ified static driver_enumerator *drivlist; /*************************************************************************** FUNCTION PROTOTYPES ***************************************************************************/ static void ui_menu_exit(running_machine &machine); /* internal menu processing */ static void ui_menu_draw(running_machine &machine, ui_menu *menu, int customonly); static void ui_menu_draw_text_box(ui_menu *menu); static void ui_menu_handle_events(ui_menu *menu); static void ui_menu_handle_keys(ui_menu *menu, UINT32 flags); static void ui_menu_validate_selection(ui_menu *menu, int scandir); static void ui_menu_clear_free_list(running_machine &machine); /* menu handlers */ static void menu_main(running_machine &machine, ui_menu *menu, void *parameter, void *state); static void menu_main_populate(running_machine &machine, ui_menu *menu, void *state); static void menu_input_groups(running_machine &machine, ui_menu *menu, void *parameter, void *state); static void menu_input_groups_populate(running_machine &machine, ui_menu *menu, void *state); static void menu_input_general(running_machine &machine, ui_menu *menu, void *parameter, void *state); static void menu_input_general_populate(running_machine &machine, ui_menu *menu, input_menu_state *menustate, int group); static void menu_input_specific(running_machine &machine, ui_menu *menu, void *parameter, void *state); static void menu_input_specific_populate(running_machine &machine, ui_menu *menu, input_menu_state *menustate); static void menu_input_common(running_machine &machine, ui_menu *menu, void *parameter, void *state); static int CLIB_DECL menu_input_compare_items(const void *i1, const void *i2); static void menu_input_populate_and_sort(running_machine &machine, ui_menu *menu, input_item_data *itemlist, input_menu_state *menustate); static void menu_settings_dip_switches(running_machine &machine, ui_menu *menu, void *parameter, void *state); static void menu_settings_driver_config(running_machine &machine, ui_menu *menu, void *parameter, void *state); static void menu_settings_categories(running_machine &machine, ui_menu *menu, void *parameter, void *state); static void menu_settings_common(running_machine &machine, ui_menu *menu, void *state, UINT32 type); static void menu_settings_populate(running_machine &machine, ui_menu *menu, settings_menu_state *menustate, UINT32 type); static void menu_analog(running_machine &machine, ui_menu *menu, void *parameter, void *state); static void menu_analog_populate(running_machine &machine, ui_menu *menu); static void menu_bookkeeping(running_machine &machine, ui_menu *menu, void *parameter, void *state); static void menu_bookkeeping_populate(running_machine &machine, ui_menu *menu, attotime *curtime); static void menu_game_info(running_machine &machine, ui_menu *menu, void *parameter, void *state); static void menu_cheat(running_machine &machine, ui_menu *menu, void *parameter, void *state); static void menu_cheat_populate(running_machine &machine, ui_menu *menu); static void menu_memory_card(running_machine &machine, ui_menu *menu, void *parameter, void *state); static void menu_memory_card_populate(running_machine &machine, ui_menu *menu, int cardnum); static void menu_sliders(running_machine &machine, ui_menu *menu, void *parameter, void *state); static void menu_sliders_populate(running_machine &machine, ui_menu *menu, int menuless_mode); static void menu_sliders_custom_render(running_machine &machine, ui_menu *menu, void *state, void *selectedref, float top, float bottom, float x1, float y1, float x2, float y2); static void menu_video_targets(running_machine &machine, ui_menu *menu, void *parameter, void *state); static void menu_video_targets_populate(running_machine &machine, ui_menu *menu); static void menu_video_options(running_machine &machine, ui_menu *menu, void *parameter, void *state); static void menu_video_options_populate(running_machine &machine, ui_menu *menu, render_target *target); static void menu_crosshair(running_machine &machine, ui_menu *menu, void *parameter, void *state); static void menu_crosshair_populate(running_machine &machine, ui_menu *menu); static void menu_quit_game(running_machine &machine, ui_menu *menu, void *parameter, void *state); static void menu_select_game(running_machine &machine, ui_menu *menu, void *parameter, void *state); static void menu_select_game_populate(running_machine &machine, ui_menu *menu, select_game_state *menustate); static void menu_select_game_build_driver_list(ui_menu *menu, select_game_state *menustate); static void menu_select_game_custom_render(running_machine &machine, ui_menu *menu, void *state, void *selectedref, float top, float bottom, float x, float y, float x2, float y2); /* menu helpers */ static void menu_render_triangle(bitmap_t &dest, const bitmap_t &source, const rectangle &sbounds, void *param); static void menu_settings_custom_render_one(render_container *container, float x1, float y1, float x2, float y2, const dip_descriptor *dip, UINT32 selectedmask); static void menu_settings_custom_render(running_machine &machine, ui_menu *menu, void *state, void *selectedref, float top, float bottom, float x, float y, float x2, float y2); /*************************************************************************** INLINE FUNCTIONS ***************************************************************************/ /*------------------------------------------------- get_field_default_seq - return a pointer to the default sequence for the given field -------------------------------------------------*/ INLINE const input_seq &get_field_default_seq(input_field_config *field, input_seq_type seqtype) { if (field->seq[seqtype].is_default()) return input_type_seq(field->machine(), field->type, field->player, seqtype); else return field->seq[seqtype]; } /*------------------------------------------------- toggle_none_default - toggle between "NONE" and the default item -------------------------------------------------*/ INLINE void toggle_none_default(input_seq &selected_seq, input_seq &original_seq, const input_seq &selected_defseq) { /* if we used to be "none", toggle to the default value */ if (original_seq.length() == 0) selected_seq = selected_defseq; /* otherwise, toggle to "none" */ else selected_seq.reset(); } /*------------------------------------------------- item_is_selectable - return TRUE if the given item is selectable -------------------------------------------------*/ INLINE int item_is_selectable(const ui_menu_item *item) { return ((item->flags & (MENU_FLAG_MULTILINE | MENU_FLAG_DISABLE)) == 0 && strcmp(item->text, MENU_SEPARATOR_ITEM) != 0); } /*------------------------------------------------- exclusive_input_pressed - return TRUE if the given key is pressed and we haven't already reported a key -------------------------------------------------*/ INLINE int exclusive_input_pressed(ui_menu *menu, int key, int repeat) { if (menu->menu_event.iptkey == IPT_INVALID && ui_input_pressed_repeat(menu->machine(), key, repeat)) { menu->menu_event.iptkey = key; return TRUE; } return FALSE; } /*************************************************************************** CORE SYSTEM MANAGEMENT ***************************************************************************/ /*------------------------------------------------- ui_menu_init - initialize the menu system -------------------------------------------------*/ void ui_menu_init(running_machine &machine) { int x; /* initialize the menu stack */ ui_menu_stack_reset(machine); /* create a texture for hilighting items */ hilight_bitmap = auto_bitmap_alloc(machine, 256, 1, BITMAP_FORMAT_ARGB32); for (x = 0; x < 256; x++) { int alpha = 0xff; if (x < 25) alpha = 0xff * x / 25; if (x > 256 - 25) alpha = 0xff * (255 - x) / 25; *BITMAP_ADDR32(hilight_bitmap, 0, x) = MAKE_ARGB(alpha,0xff,0xff,0xff); } hilight_texture = machine.render().texture_alloc(); hilight_texture->set_bitmap(hilight_bitmap, NULL, TEXFORMAT_ARGB32); /* create a texture for arrow icons */ arrow_texture = machine.render().texture_alloc(menu_render_triangle); /* add an exit callback to free memory */ machine.add_notifier(MACHINE_NOTIFY_EXIT, machine_notify_delegate(FUNC(ui_menu_exit), &machine)); } /*------------------------------------------------- ui_menu_exit - clean up after ourselves -------------------------------------------------*/ static void ui_menu_exit(running_machine &machine) { /* free menus */ ui_menu_stack_reset(machine); ui_menu_clear_free_list(machine); /* free textures */ machine.render().texture_free(hilight_texture); machine.render().texture_free(arrow_texture); if (drivlist) { global_free(drivlist); drivlist = NULL; } } /*************************************************************************** CORE MENU MANAGEMENT ***************************************************************************/ /*------------------------------------------------- ui_menu_alloc - allocate a new menu -------------------------------------------------*/ ui_menu *ui_menu_alloc(running_machine &machine, render_container *container, ui_menu_handler_func handler, void *parameter) { ui_menu *menu; /* allocate and clear memory for the menu and the state */ menu = auto_alloc_clear(machine, ui_menu(machine)); /* initialize the state */ menu->container = container; menu->handler = handler; menu->parameter = parameter; /* reset the menu (adds a default entry) */ ui_menu_reset(menu, UI_MENU_RESET_SELECT_FIRST); return menu; } /*------------------------------------------------- ui_menu_free - free a menu -------------------------------------------------*/ void ui_menu_free(ui_menu *menu) { /* free the pools */ while (menu->pool != NULL) { ui_menu_pool *pool = menu->pool; menu->pool = pool->next; auto_free(menu->machine(), pool); } /* free the item array */ if (menu->item != NULL) auto_free(menu->machine(), menu->item); /* free the state */ if (menu->state != NULL) { if (menu->destroy_state != NULL) (*menu->destroy_state)(menu, menu->state); auto_free(menu->machine(), menu->state); } /* free the menu */ auto_free(menu->machine(), menu); } /*------------------------------------------------- ui_menu_reset - free all items in the menu, and all memory allocated from the memory pool -------------------------------------------------*/ void ui_menu_reset(ui_menu *menu, ui_menu_reset_options options) { ui_menu_pool *pool; /* based on the reset option, set the reset info */ menu->resetpos = 0; menu->resetref = NULL; if (options == UI_MENU_RESET_REMEMBER_POSITION) menu->resetpos = menu->selected; else if (options == UI_MENU_RESET_REMEMBER_REF) menu->resetref = menu->item[menu->selected].ref; /* reset all the pools and the numitems back to 0 */ for (pool = menu->pool; pool != NULL; pool = pool->next) pool->top = (UINT8 *)(pool + 1); menu->numitems = 0; menu->visitems = 0; menu->selected = 0; /* add an item to return */ if (menu->parent == NULL) ui_menu_item_append(menu, backtext, NULL, 0, NULL); else if (menu->parent->handler == menu_quit_game) ui_menu_item_append(menu, exittext, NULL, 0, NULL); else ui_menu_item_append(menu, priortext, NULL, 0, NULL); } /*------------------------------------------------- ui_menu_populated - returns TRUE if the menu has any non-default items in it -------------------------------------------------*/ int ui_menu_populated(ui_menu *menu) { return (menu->numitems > 1); } /*------------------------------------------------- ui_menu_item_append - append a new item to the end of the menu -------------------------------------------------*/ void ui_menu_item_append(ui_menu *menu, const char *text, const char *subtext, UINT32 flags, void *ref) { ui_menu_item *item; int index; /* only allow multiline as the first item */ if ((flags & MENU_FLAG_MULTILINE) != 0) assert(menu->numitems == 1); /* only allow a single multi-line item */ else if (menu->numitems >= 2) assert((menu->item[0].flags & MENU_FLAG_MULTILINE) == 0); /* realloc the item array if necessary */ if (menu->numitems >= menu->allocitems) { int olditems = menu->allocitems; menu->allocitems += UI_MENU_ALLOC_ITEMS; ui_menu_item *newitems = auto_alloc_array(menu->machine(), ui_menu_item, menu->allocitems); for (int itemnum = 0; itemnum < olditems; itemnum++) newitems[itemnum] = menu->item[itemnum]; auto_free(menu->machine(), menu->item); menu->item = newitems; } index = menu->numitems++; /* copy the previous last item to the next one */ if (index != 0) { index--; menu->item[index + 1] = menu->item[index]; } /* allocate a new item and populte it */ item = &menu->item[index]; item->text = (text != NULL) ? ui_menu_pool_strdup(menu, text) : NULL; item->subtext = (subtext != NULL) ? ui_menu_pool_strdup(menu, subtext) : NULL; item->flags = flags; item->ref = ref; /* update the selection if we need to */ if (menu->resetpos == index || (menu->resetref != NULL && menu->resetref == ref)) menu->selected = index; if (menu->resetpos == menu->numitems - 1) menu->selected = menu->numitems - 1; } /*------------------------------------------------- ui_menu_process - process a menu, drawing it and returning any interesting events -------------------------------------------------*/ const ui_menu_event *ui_menu_process(running_machine &machine, ui_menu *menu, UINT32 flags) { /* reset the menu_event */ menu->menu_event.iptkey = IPT_INVALID; /* first make sure our selection is valid */ ui_menu_validate_selection(menu, 1); /* draw the menu */ if (menu->numitems > 1 && (menu->item[0].flags & MENU_FLAG_MULTILINE) != 0) ui_menu_draw_text_box(menu); else ui_menu_draw(machine, menu, (flags & UI_MENU_PROCESS_CUSTOM_ONLY) != 0); /* process input */ if (!(flags & UI_MENU_PROCESS_NOKEYS)) { /* read events */ ui_menu_handle_events(menu); /* handle the keys if we don't already have an menu_event */ if (menu->menu_event.iptkey == IPT_INVALID) ui_menu_handle_keys(menu, flags); } /* update the selected item in the menu_event */ if (menu->menu_event.iptkey != IPT_INVALID && menu->selected >= 0 && menu->selected < menu->numitems) { menu->menu_event.itemref = menu->item[menu->selected].ref; return &menu->menu_event; } return NULL; } /*------------------------------------------------- ui_menu_set_custom_render - configure the menu for custom rendering -------------------------------------------------*/ void ui_menu_set_custom_render(ui_menu *menu, ui_menu_custom_func custom, float top, float bottom) { menu->custom = custom; menu->customtop = top; menu->custombottom = bottom; } /*------------------------------------------------- ui_menu_alloc_state - allocate permanent memory to represent the menu's state -------------------------------------------------*/ void *ui_menu_alloc_state(ui_menu *menu, size_t size, ui_menu_destroy_state_func destroy_state) { if (menu->state != NULL) { if (menu->destroy_state != NULL) (*menu->destroy_state)(menu, menu->state); auto_free(menu->machine(), menu->state); } menu->state = auto_alloc_array_clear(menu->machine(), UINT8, size); menu->destroy_state = destroy_state; return menu->state; } /*------------------------------------------------- ui_menu_pool_alloc - allocate temporary memory from the menu's memory pool -------------------------------------------------*/ void *ui_menu_pool_alloc(ui_menu *menu, size_t size) { ui_menu_pool *pool; assert(size < UI_MENU_POOL_SIZE); /* find a pool with enough room */ for (pool = menu->pool; pool != NULL; pool = pool->next) if (pool->end - pool->top >= size) { void *result = pool->top; pool->top += size; return result; } /* allocate a new pool */ pool = (ui_menu_pool *)auto_alloc_array_clear(menu->machine(), UINT8, sizeof(*pool) + UI_MENU_POOL_SIZE); /* wire it up */ pool->next = menu->pool; menu->pool = pool; pool->top = (UINT8 *)(pool + 1); pool->end = pool->top + UI_MENU_POOL_SIZE; return ui_menu_pool_alloc(menu, size); } /*------------------------------------------------- ui_menu_pool_strdup - make a temporary string copy in the menu's memory pool -------------------------------------------------*/ const char *ui_menu_pool_strdup(ui_menu *menu, const char *string) { return strcpy((char *)ui_menu_pool_alloc(menu, strlen(string) + 1), string); } /*------------------------------------------------- ui_menu_get_selection - retrieves the index of the currently selected menu item -------------------------------------------------*/ void *ui_menu_get_selection(ui_menu *menu) { return (menu->selected >= 0 && menu->selected < menu->numitems) ? menu->item[menu->selected].ref : NULL; } /*------------------------------------------------- ui_menu_set_selection - changes the index of the currently selected menu item -------------------------------------------------*/ void ui_menu_set_selection(ui_menu *menu, void *selected_itemref) { int itemnum; menu->selected = -1; for (itemnum = 0; itemnum < menu->numitems; itemnum++) if (menu->item[itemnum].ref == selected_itemref) { menu->selected = itemnum; break; } } /*************************************************************************** INTERNAL MENU PROCESSING ***************************************************************************/ /*------------------------------------------------- ui_menu_draw - draw a menu -------------------------------------------------*/ static void ui_menu_draw(running_machine &machine, ui_menu *menu, int customonly) { float line_height = ui_get_line_height(machine); float lr_arrow_width = 0.4f * line_height * machine.render().ui_aspect(); float ud_arrow_width = line_height * machine.render().ui_aspect(); float gutter_width = lr_arrow_width * 1.3f; float x1, y1, x2, y2; float effective_width, effective_left; float visible_width, visible_main_menu_height; float visible_extra_menu_height = 0; float visible_top, visible_left; int selected_subitem_too_big = FALSE; int visible_lines; int top_line; int itemnum, linenum; int mouse_hit, mouse_button; render_target *mouse_target; INT32 mouse_target_x, mouse_target_y; float mouse_x = -1, mouse_y = -1; /* compute the width and height of the full menu */ visible_width = 0; visible_main_menu_height = 0; for (itemnum = 0; itemnum < menu->numitems; itemnum++) { const ui_menu_item *item = &menu->item[itemnum]; float total_width; /* compute width of left hand side */ total_width = gutter_width + ui_get_string_width(machine, item->text) + gutter_width; /* add in width of right hand side */ if (item->subtext) total_width += 2.0f * gutter_width + ui_get_string_width(machine, item->subtext); /* track the maximum */ if (total_width > visible_width) visible_width = total_width; /* track the height as well */ visible_main_menu_height += line_height; } /* account for extra space at the top and bottom */ visible_extra_menu_height = menu->customtop + menu->custombottom; /* add a little bit of slop for rounding */ visible_width += 0.01f; visible_main_menu_height += 0.01f; /* if we are too wide or too tall, clamp it down */ if (visible_width + 2.0f * UI_BOX_LR_BORDER > 1.0f) visible_width = 1.0f - 2.0f * UI_BOX_LR_BORDER; /* if the menu and extra menu won't fit, take away part of the regular menu, it will scroll */ if (visible_main_menu_height + visible_extra_menu_height + 2.0f * UI_BOX_TB_BORDER > 1.0f) visible_main_menu_height = 1.0f - 2.0f * UI_BOX_TB_BORDER - visible_extra_menu_height; visible_lines = floor(visible_main_menu_height / line_height); visible_main_menu_height = (float)visible_lines * line_height; /* compute top/left of inner menu area by centering */ visible_left = (1.0f - visible_width) * 0.5f; visible_top = (1.0f - (visible_main_menu_height + visible_extra_menu_height)) * 0.5f; /* if the menu is at the bottom of the extra, adjust */ visible_top += menu->customtop; /* first add us a box */ x1 = visible_left - UI_BOX_LR_BORDER; y1 = visible_top - UI_BOX_TB_BORDER; x2 = visible_left + visible_width + UI_BOX_LR_BORDER; y2 = visible_top + visible_main_menu_height + UI_BOX_TB_BORDER; if (!customonly) ui_draw_outlined_box(menu->container, x1, y1, x2, y2, UI_BACKGROUND_COLOR); /* determine the first visible line based on the current selection */ top_line = menu->selected - visible_lines / 2; if (top_line < 0) top_line = 0; if (top_line + visible_lines >= menu->numitems) top_line = menu->numitems - visible_lines; /* determine effective positions taking into account the hilighting arrows */ effective_width = visible_width - 2.0f * gutter_width; effective_left = visible_left + gutter_width; /* locate mouse */ mouse_hit = FALSE; mouse_button = FALSE; if (!customonly) { mouse_target = ui_input_find_mouse(machine, &mouse_target_x, &mouse_target_y, &mouse_button); if (mouse_target != NULL) if (mouse_target->map_point_container(mouse_target_x, mouse_target_y, *menu->container, mouse_x, mouse_y)) mouse_hit = TRUE; } /* loop over visible lines */ menu->hover = menu->numitems + 1; if (!customonly) for (linenum = 0; linenum < visible_lines; linenum++) { float line_y = visible_top + (float)linenum * line_height; itemnum = top_line + linenum; const ui_menu_item *item = &menu->item[itemnum]; const char *itemtext = item->text; rgb_t fgcolor = UI_TEXT_COLOR; rgb_t bgcolor = UI_TEXT_BG_COLOR; rgb_t fgcolor2 = UI_SUBITEM_COLOR; rgb_t fgcolor3 = UI_CLONE_COLOR; float line_x0 = x1 + 0.5f * UI_LINE_WIDTH; float line_y0 = line_y; float line_x1 = x2 - 0.5f * UI_LINE_WIDTH; float line_y1 = line_y + line_height; /* set the hover if this is our item */ if (mouse_hit && line_x0 <= mouse_x && line_x1 > mouse_x && line_y0 <= mouse_y && line_y1 > mouse_y && item_is_selectable(item)) menu->hover = itemnum; /* if we're selected, draw with a different background */ if (itemnum == menu->selected) { fgcolor = UI_SELECTED_COLOR; bgcolor = UI_SELECTED_BG_COLOR; fgcolor2 = UI_SELECTED_COLOR; fgcolor3 = UI_SELECTED_COLOR; } /* else if the mouse is over this item, draw with a different background */ else if (itemnum == menu->hover) { fgcolor = UI_MOUSEOVER_COLOR; bgcolor = UI_MOUSEOVER_BG_COLOR; fgcolor2 = UI_MOUSEOVER_COLOR; fgcolor3 = UI_MOUSEOVER_COLOR; } /* if we have some background hilighting to do, add a quad behind everything else */ if (bgcolor != UI_TEXT_BG_COLOR) menu->container->add_quad(line_x0, line_y0, line_x1, line_y1, bgcolor, hilight_texture, PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA) | PRIMFLAG_TEXWRAP(TRUE)); /* if we're on the top line, display the up arrow */ if (linenum == 0 && top_line != 0) { menu->container->add_quad( 0.5f * (x1 + x2) - 0.5f * ud_arrow_width, line_y + 0.25f * line_height, 0.5f * (x1 + x2) + 0.5f * ud_arrow_width, line_y + 0.75f * line_height, fgcolor, arrow_texture, PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA) | PRIMFLAG_TEXORIENT(ROT0)); if (menu->hover == itemnum) menu->hover = -2; } /* if we're on the bottom line, display the down arrow */ else if (linenum == visible_lines - 1 && itemnum != menu->numitems - 1) { menu->container->add_quad( 0.5f * (x1 + x2) - 0.5f * ud_arrow_width, line_y + 0.25f * line_height, 0.5f * (x1 + x2) + 0.5f * ud_arrow_width, line_y + 0.75f * line_height, fgcolor, arrow_texture, PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA) | PRIMFLAG_TEXORIENT(ROT0 ^ ORIENTATION_FLIP_Y)); if (menu->hover == itemnum) menu->hover = -1; } /* if we're just a divider, draw a line */ else if (strcmp(itemtext, MENU_SEPARATOR_ITEM) == 0) menu->container->add_line(visible_left, line_y + 0.5f * line_height, visible_left + visible_width, line_y + 0.5f * line_height, UI_LINE_WIDTH, UI_BORDER_COLOR, PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA)); /* if we don't have a subitem, just draw the string centered */ else if (item->subtext == NULL) ui_draw_text_full(menu->container, itemtext, effective_left, line_y, effective_width, JUSTIFY_CENTER, WRAP_TRUNCATE, DRAW_NORMAL, fgcolor, bgcolor, NULL, NULL); /* otherwise, draw the item on the left and the subitem text on the right */ else { int subitem_invert = item->flags & MENU_FLAG_INVERT; const char *subitem_text = item->subtext; float item_width, subitem_width; /* draw the left-side text */ ui_draw_text_full(menu->container, itemtext, effective_left, line_y, effective_width, JUSTIFY_LEFT, WRAP_TRUNCATE, DRAW_NORMAL, fgcolor, bgcolor, &item_width, NULL); /* give 2 spaces worth of padding */ item_width += 2.0f * gutter_width; /* if the subitem doesn't fit here, display dots */ if (ui_get_string_width(machine, subitem_text) > effective_width - item_width) { subitem_text = "..."; if (itemnum == menu->selected) selected_subitem_too_big = TRUE; } /* draw the subitem right-justified */ ui_draw_text_full(menu->container, subitem_text, effective_left + item_width, line_y, effective_width - item_width, JUSTIFY_RIGHT, WRAP_TRUNCATE, DRAW_NORMAL, subitem_invert ? fgcolor3 : fgcolor2, bgcolor, &subitem_width, NULL); /* apply arrows */ if (itemnum == menu->selected && (item->flags & MENU_FLAG_LEFT_ARROW)) { menu->container->add_quad( effective_left + effective_width - subitem_width - gutter_width, line_y + 0.1f * line_height, effective_left + effective_width - subitem_width - gutter_width + lr_arrow_width, line_y + 0.9f * line_height, fgcolor, arrow_texture, PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA) | PRIMFLAG_TEXORIENT(ROT90 ^ ORIENTATION_FLIP_X)); } if (itemnum == menu->selected && (item->flags & MENU_FLAG_RIGHT_ARROW)) { menu->container->add_quad( effective_left + effective_width + gutter_width - lr_arrow_width, line_y + 0.1f * line_height, effective_left + effective_width + gutter_width, line_y + 0.9f * line_height, fgcolor, arrow_texture, PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA) | PRIMFLAG_TEXORIENT(ROT90)); } } } /* if the selected subitem is too big, display it in a separate offset box */ if (selected_subitem_too_big) { const ui_menu_item *item = &menu->item[menu->selected]; int subitem_invert = item->flags & MENU_FLAG_INVERT; linenum = menu->selected - top_line; float line_y = visible_top + (float)linenum * line_height; float target_width, target_height; float target_x, target_y; /* compute the multi-line target width/height */ ui_draw_text_full(menu->container, item->subtext, 0, 0, visible_width * 0.75f, JUSTIFY_RIGHT, WRAP_WORD, DRAW_NONE, ARGB_WHITE, ARGB_BLACK, &target_width, &target_height); /* determine the target location */ target_x = visible_left + visible_width - target_width - UI_BOX_LR_BORDER; target_y = line_y + line_height + UI_BOX_TB_BORDER; if (target_y + target_height + UI_BOX_TB_BORDER > visible_main_menu_height) target_y = line_y - target_height - UI_BOX_TB_BORDER; /* add a box around that */ ui_draw_outlined_box(menu->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, subitem_invert ? UI_SELECTED_BG_COLOR : UI_BACKGROUND_COLOR); ui_draw_text_full(menu->container, item->subtext, target_x, target_y, target_width, JUSTIFY_RIGHT, WRAP_WORD, DRAW_NORMAL, UI_SELECTED_COLOR, UI_SELECTED_BG_COLOR, NULL, NULL); } /* if there is something special to add, do it by calling the passed routine */ if (menu->custom != NULL) { void *selectedref = (menu->selected >= 0 && menu->selected < menu->numitems) ? menu->item[menu->selected].ref : NULL; (*menu->custom)(menu->machine(), menu, menu->state, selectedref, menu->customtop, menu->custombottom, x1, y1, x2, y2); } /* return the number of visible lines, minus 1 for top arrow and 1 for bottom arrow */ menu->visitems = visible_lines - (top_line != 0) - (top_line + visible_lines != menu->numitems); } /*------------------------------------------------- ui_menu_draw_text_box - draw a multiline word-wrapped text box with a menu item at the bottom -------------------------------------------------*/ static void ui_menu_draw_text_box(ui_menu *menu) { const char *text = menu->item[0].text; const char *backtext = menu->item[1].text; float line_height = ui_get_line_height(menu->machine()); float lr_arrow_width = 0.4f * line_height * menu->machine().render().ui_aspect(); float gutter_width = lr_arrow_width; float target_width, target_height, prior_width; float target_x, target_y; /* compute the multi-line target width/height */ ui_draw_text_full(menu->container, text, 0, 0, 1.0f - 2.0f * UI_BOX_LR_BORDER - 2.0f * gutter_width, JUSTIFY_LEFT, WRAP_WORD, DRAW_NONE, ARGB_WHITE, ARGB_BLACK, &target_width, &target_height); target_height += 2.0f * line_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; /* maximum against "return to prior menu" text */ prior_width = ui_get_string_width(menu->machine(), backtext) + 2.0f * gutter_width; target_width = MAX(target_width, prior_width); /* determine the target location */ target_x = 0.5f - 0.5f * target_width; target_y = 0.5f - 0.5f * target_height; /* make sure we stay on-screen */ if (target_x < UI_BOX_LR_BORDER + gutter_width) target_x = UI_BOX_LR_BORDER + gutter_width; if (target_x + target_width + gutter_width + UI_BOX_LR_BORDER > 1.0f) target_x = 1.0f - UI_BOX_LR_BORDER - gutter_width - 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; /* add a box around that */ ui_draw_outlined_box(menu->container, target_x - UI_BOX_LR_BORDER - gutter_width, target_y - UI_BOX_TB_BORDER, target_x + target_width + gutter_width + UI_BOX_LR_BORDER, target_y + target_height + UI_BOX_TB_BORDER, (menu->item[0].flags & MENU_FLAG_REDTEXT) ? UI_RED_COLOR : UI_BACKGROUND_COLOR); ui_draw_text_full(menu->container, text, target_x, target_y, target_width, JUSTIFY_LEFT, WRAP_WORD, DRAW_NORMAL, UI_TEXT_COLOR, UI_TEXT_BG_COLOR, NULL, NULL); /* draw the "return to prior menu" text with a hilight behind it */ menu->container->add_quad( target_x + 0.5f * UI_LINE_WIDTH, target_y + target_height - line_height, target_x + target_width - 0.5f * UI_LINE_WIDTH, target_y + target_height, UI_SELECTED_BG_COLOR, hilight_texture, PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA) | PRIMFLAG_TEXWRAP(TRUE)); ui_draw_text_full(menu->container, backtext, target_x, target_y + target_height - line_height, target_width, JUSTIFY_CENTER, WRAP_TRUNCATE, DRAW_NORMAL, UI_SELECTED_COLOR, UI_SELECTED_BG_COLOR, NULL, NULL); /* artificially set the hover to the last item so a double-click exits */ menu->hover = menu->numitems - 1; } /*------------------------------------------------- ui_menu_handle_events - generically handle input events for a menu -------------------------------------------------*/ static void ui_menu_handle_events(ui_menu *menu) { int stop = FALSE; ui_event menu_event; /* loop while we have interesting events */ while (ui_input_pop_event(menu->machine(), &menu_event) && !stop) { switch (menu_event.event_type) { /* if we are hovering over a valid item, select it with a single click */ case UI_EVENT_MOUSE_DOWN: if (menu->hover >= 0 && menu->hover < menu->numitems) menu->selected = menu->hover; else if (menu->hover == -2) { menu->selected -= menu->visitems - 1; ui_menu_validate_selection(menu, 1); } else if (menu->hover == -1) { menu->selected += menu->visitems - 1; ui_menu_validate_selection(menu, 1); } break; /* if we are hovering over a valid item, fake a UI_SELECT with a double-click */ case UI_EVENT_MOUSE_DOUBLE_CLICK: if (menu->hover >= 0 && menu->hover < menu->numitems) { menu->selected = menu->hover; if (menu_event.event_type == UI_EVENT_MOUSE_DOUBLE_CLICK) { menu->menu_event.iptkey = IPT_UI_SELECT; if (menu->selected == menu->numitems - 1) { menu->menu_event.iptkey = IPT_UI_CANCEL; ui_menu_stack_pop(menu->machine()); } } stop = TRUE; } break; /* translate CHAR events into specials */ case UI_EVENT_CHAR: menu->menu_event.iptkey = IPT_SPECIAL; menu->menu_event.unichar = menu_event.ch; stop = TRUE; break; /* ignore everything else */ default: break; } } } /*------------------------------------------------- ui_menu_handle_keys - generically handle keys for a menu -------------------------------------------------*/ static void ui_menu_handle_keys(ui_menu *menu, UINT32 flags) { int ignorepause = ui_menu_is_force_game_select(); int ignoreright = FALSE; int ignoreleft = FALSE; int code; /* bail if no items */ if (menu->numitems == 0) return; /* if we hit select, return TRUE or pop the stack, depending on the item */ if (exclusive_input_pressed(menu, IPT_UI_SELECT, 0)) { if (menu->selected == menu->numitems - 1) { menu->menu_event.iptkey = IPT_UI_CANCEL; ui_menu_stack_pop(menu->machine()); } return; } /* hitting cancel also pops the stack */ if (exclusive_input_pressed(menu, IPT_UI_CANCEL, 0)) { ui_menu_stack_pop(menu->machine()); return; } /* validate the current selection */ ui_menu_validate_selection(menu, 1); /* swallow left/right keys if they are not appropriate */ ignoreleft = ((menu->item[menu->selected].flags & MENU_FLAG_LEFT_ARROW) == 0); ignoreright = ((menu->item[menu->selected].flags & MENU_FLAG_RIGHT_ARROW) == 0); /* accept left/right keys as-is with repeat */ if (!ignoreleft && exclusive_input_pressed(menu, IPT_UI_LEFT, (flags & UI_MENU_PROCESS_LR_REPEAT) ? 6 : 0)) return; if (!ignoreright && exclusive_input_pressed(menu, IPT_UI_RIGHT, (flags & UI_MENU_PROCESS_LR_REPEAT) ? 6 : 0)) return; /* up backs up by one item */ if (exclusive_input_pressed(menu, IPT_UI_UP, 6)) { menu->selected = (menu->selected + menu->numitems - 1) % menu->numitems; ui_menu_validate_selection(menu, -1); } /* down advances by one item */ if (exclusive_input_pressed(menu, IPT_UI_DOWN, 6)) { menu->selected = (menu->selected + 1) % menu->numitems; ui_menu_validate_selection(menu, 1); } /* page up backs up by visitems */ if (exclusive_input_pressed(menu, IPT_UI_PAGE_UP, 6)) { menu->selected -= menu->visitems - 1; ui_menu_validate_selection(menu, 1); } /* page down advances by visitems */ if (exclusive_input_pressed(menu, IPT_UI_PAGE_DOWN, 6)) { menu->selected += menu->visitems - 1; ui_menu_validate_selection(menu, -1); } /* home goes to the start */ if (exclusive_input_pressed(menu, IPT_UI_HOME, 0)) { menu->selected = 0; ui_menu_validate_selection(menu, 1); } /* end goes to the last */ if (exclusive_input_pressed(menu, IPT_UI_END, 0)) { menu->selected = menu->numitems - 1; ui_menu_validate_selection(menu, -1); } /* pause enables/disables pause */ if (!ignorepause && exclusive_input_pressed(menu, IPT_UI_PAUSE, 0)) { if (menu->machine().paused()) menu->machine().resume(); else menu->machine().pause(); } /* handle a toggle cheats request */ if (ui_input_pressed_repeat(menu->machine(), IPT_UI_TOGGLE_CHEAT, 0)) menu->machine().cheat().set_enable(!menu->machine().cheat().enabled()); /* see if any other UI keys are pressed */ if (menu->menu_event.iptkey == IPT_INVALID) for (code = __ipt_ui_start; code <= __ipt_ui_end; code++) { if (code == IPT_UI_CONFIGURE || (code == IPT_UI_LEFT && ignoreleft) || (code == IPT_UI_RIGHT && ignoreright) || (code == IPT_UI_PAUSE && ignorepause)) continue; if (exclusive_input_pressed(menu, code, 0)) break; } } /*------------------------------------------------- ui_menu_validate_selection - validate the current selection and ensure it is on a correct item -------------------------------------------------*/ static void ui_menu_validate_selection(ui_menu *menu, int scandir) { /* clamp to be in range */ if (menu->selected < 0) menu->selected = 0; else if (menu->selected >= menu->numitems) menu->selected = menu->numitems - 1; /* skip past unselectable items */ while (!item_is_selectable(&menu->item[menu->selected])) menu->selected = (menu->selected + menu->numitems + scandir) % menu->numitems; } /*------------------------------------------------- ui_menu_clear_free_list - clear out anything accumulated in the free list -------------------------------------------------*/ static void ui_menu_clear_free_list(running_machine &machine) { while (menu_free != NULL) { ui_menu *menu = menu_free; menu_free = menu->parent; ui_menu_free(menu); } } /*************************************************************************** MENU STACK MANAGEMENT ***************************************************************************/ /*------------------------------------------------- ui_menu_stack_reset - reset the menu stack -------------------------------------------------*/ void ui_menu_stack_reset(running_machine &machine) { while (menu_stack != NULL) ui_menu_stack_pop(machine); } /*------------------------------------------------- ui_menu_stack_push - push a new menu onto the stack -------------------------------------------------*/ void ui_menu_stack_push(ui_menu *menu) { menu->parent = menu_stack; menu_stack = menu; ui_menu_reset(menu, UI_MENU_RESET_SELECT_FIRST); ui_input_reset(menu->machine()); } /*------------------------------------------------- ui_menu_stack_pop - pop a menu from the stack -------------------------------------------------*/ void ui_menu_stack_pop(running_machine &machine) { if (menu_stack != NULL) { ui_menu *menu = menu_stack; menu_stack = menu->parent; menu->parent = menu_free; menu_free = menu; ui_input_reset(machine); } } /*************************************************************************** UI SYSTEM INTERACTION ***************************************************************************/ /*------------------------------------------------- ui_menu_ui_handler - displays the current menu and calls the menu handler -------------------------------------------------*/ UINT32 ui_menu_ui_handler(running_machine &machine, render_container *container, UINT32 state) { /* if we have no menus stacked up, start with the main menu */ if (menu_stack == NULL) ui_menu_stack_push(ui_menu_alloc(machine, container, menu_main, NULL)); /* update the menu state */ if (menu_stack != NULL) { ui_menu *menu = menu_stack; (*menu->handler)(machine, menu, menu->parameter, menu->state); } /* clear up anything pending to be released */ ui_menu_clear_free_list(machine); /* if the menus are to be hidden, return a cancel here */ if ((ui_input_pressed(machine, IPT_UI_CONFIGURE) && !ui_menu_is_force_game_select()) || menu_stack == NULL) return UI_HANDLER_CANCEL; return 0; } /*------------------------------------------------- ui_slider_ui_handler - pushes the slider menu on the stack and hands off to the standard menu handler -------------------------------------------------*/ UINT32 ui_slider_ui_handler(running_machine &machine, render_container *container, UINT32 state) { UINT32 result; /* if this is the first call, push the sliders menu */ if (state) ui_menu_stack_push(ui_menu_alloc(machine, container, menu_sliders, (void *)1)); /* handle standard menus */ result = ui_menu_ui_handler(machine, container, state); /* if we are cancelled, pop the sliders menu */ if (result == UI_HANDLER_CANCEL) ui_menu_stack_pop(machine); return (menu_stack != NULL && menu_stack->handler == menu_sliders && menu_stack->parameter != NULL) ? 0 : UI_HANDLER_CANCEL; } /*------------------------------------------------- ui_menu_force_game_select - force the game select menu to be visible and inescapable -------------------------------------------------*/ void ui_menu_force_game_select(running_machine &machine, render_container *container) { char *gamename = (char *)machine.options().system_name(); /* reset the menu stack */ ui_menu_stack_reset(machine); /* add the quit entry followed by the game select entry */ ui_menu_stack_push(ui_menu_alloc(machine, container, menu_quit_game, NULL)); ui_menu_stack_push(ui_menu_alloc(machine, container, menu_select_game, gamename)); /* force the menus on */ ui_show_menu(); /* make sure MAME is paused */ machine.pause(); } /*------------------------------------------------- ui_menu_is_force_game_select - return true if we are currently in "force game select" mode -------------------------------------------------*/ int ui_menu_is_force_game_select(void) { ui_menu *menu; for (menu = menu_stack; menu != NULL; menu = menu->parent) if (menu->handler == menu_quit_game && menu->parent == NULL) return TRUE; return FALSE; } /*************************************************************************** MENU HANDLERS ***************************************************************************/ /*------------------------------------------------- menu_main - handle the main menu -------------------------------------------------*/ static void menu_main(running_machine &machine, ui_menu *menu, void *parameter, void *state) { const ui_menu_event *menu_event; /* if the menu isn't built, populate now */ if (!ui_menu_populated(menu)) menu_main_populate(machine, menu, state); /* process the menu */ menu_event = ui_menu_process(machine, menu, 0); if (menu_event != NULL && menu_event->iptkey == IPT_UI_SELECT) ui_menu_stack_push(ui_menu_alloc(machine, menu->container, (ui_menu_handler_func)menu_event->itemref, NULL)); } /*------------------------------------------------- ui_menu_keyboard_mode - menu that -------------------------------------------------*/ static void ui_menu_keyboard_mode(running_machine &machine, ui_menu *menu, void *parameter, void *state) { const ui_menu_event *menu_event; int natural = ui_get_use_natural_keyboard(machine); /* if the menu isn't built, populate now */ if (!ui_menu_populated(menu)) { ui_menu_item_append(menu, "Keyboard Mode:", natural ? "Natural" : "Emulated", natural ? MENU_FLAG_LEFT_ARROW : MENU_FLAG_RIGHT_ARROW, NULL); } /* process the menu */ menu_event = ui_menu_process(machine, menu, 0); if (menu_event != NULL) { if (menu_event->iptkey == IPT_UI_LEFT || menu_event->iptkey == IPT_UI_RIGHT) { ui_set_use_natural_keyboard(machine, natural ^ TRUE); ui_menu_reset(menu, UI_MENU_RESET_REMEMBER_REF); } } } /*------------------------------------------------- ui_slot_get_current_index - returns -------------------------------------------------*/ int ui_slot_get_current_index(running_machine &machine, device_slot_interface *slot) { const char *current = machine.options().value(slot->device().tag()); const slot_interface* intf = slot->get_slot_interfaces(); int val = -1; for (int i = 0; intf[i].name != NULL; i++) { if (strcmp(current, intf[i].name) == 0) val = i; } return val; } /*------------------------------------------------- ui_slot_get_length - returns -------------------------------------------------*/ int ui_slot_get_length(running_machine &machine, device_slot_interface *slot) { const slot_interface* intf = slot->get_slot_interfaces(); int val = 0; for (int i = 0; intf[i].name != NULL; i++) val++; return val; } /*------------------------------------------------- ui_slot_get_next - returns -------------------------------------------------*/ const char *ui_slot_get_next(running_machine &machine, device_slot_interface *slot) { int idx = ui_slot_get_current_index(machine, slot) + 1; if (idx==ui_slot_get_length(machine,slot)) return ""; return slot->get_slot_interfaces()[idx].name; } /*------------------------------------------------- ui_slot_get_prev - returns -------------------------------------------------*/ const char *ui_slot_get_prev(running_machine &machine, device_slot_interface *slot) { int idx = ui_slot_get_current_index(machine, slot) - 1; if (idx==-1) return ""; if (idx==-2) idx = ui_slot_get_length(machine,slot) -1; if (idx==-1) return ""; return slot->get_slot_interfaces()[idx].name; } /*------------------------------------------------- ui_get_slot_device - returns -------------------------------------------------*/ const char *ui_get_slot_device(running_machine &machine, device_slot_interface *slot) { return machine.options().value(slot->device().tag()); } /*------------------------------------------------- ui_set_use_natural_keyboard - specifies whether the natural keyboard is active -------------------------------------------------*/ void ui_set_slot_device(running_machine &machine, device_slot_interface *slot, const char *val) { astring error; machine.options().set_value(slot->device().tag(), val, OPTION_PRIORITY_CMDLINE, error); assert(!error); } /*------------------------------------------------- menu_slot_devices_populate - populates the main slot device menu -------------------------------------------------*/ static void menu_slot_devices_populate(running_machine &machine, ui_menu *menu, void *state) { device_slot_interface *slot = NULL; /* cycle through all devices for this system */ for (bool gotone = machine.devicelist().first(slot); gotone; gotone = slot->next(slot)) { /* record the menu item */ const char *title = ui_get_slot_device(machine,slot); ui_menu_item_append(menu, slot->device().tag(), strcmp(title,"")==0 ? "------" : title, MENU_FLAG_LEFT_ARROW | MENU_FLAG_RIGHT_ARROW, (void *)slot); } ui_menu_item_append(menu, MENU_SEPARATOR_ITEM, NULL, 0, NULL); ui_menu_item_append(menu, "Reset", NULL, 0, NULL); } /*------------------------------------------------- ui_menu_slot_devices - menu that -------------------------------------------------*/ static void ui_menu_slot_devices(running_machine &machine, ui_menu *menu, void *parameter, void *state) { const ui_menu_event *menu_event; /* if the menu isn't built, populate now */ if (!ui_menu_populated(menu)) { menu_slot_devices_populate(machine, menu, state); } /* process the menu */ menu_event = ui_menu_process(machine, menu, 0); if (menu_event != NULL && menu_event->itemref != NULL) { if (menu_event->iptkey == IPT_UI_LEFT || menu_event->iptkey == IPT_UI_RIGHT) { device_slot_interface *slot = (device_slot_interface *)menu_event->itemref; const char *val = (menu_event->iptkey == IPT_UI_LEFT) ? ui_slot_get_prev(machine,slot) : ui_slot_get_next(machine,slot); ui_set_slot_device(machine,slot,val); ui_menu_reset(menu, UI_MENU_RESET_REMEMBER_REF); } } else if (menu_event != NULL && menu_event->iptkey == IPT_UI_SELECT) { machine.schedule_hard_reset(); } } /*------------------------------------------------- menu_main_populate - populate the main menu -------------------------------------------------*/ static void menu_main_populate(running_machine &machine, ui_menu *menu, void *state) { input_field_config *field; input_port_config *port; int has_categories = FALSE; int has_configs = FALSE; int has_analog = FALSE; int has_dips = FALSE; /* scan the input port array to see what options we need to enable */ 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_DIPSWITCH) has_dips = TRUE; if (field->type == IPT_CONFIG) has_configs = TRUE; if (field->category > 0) has_categories = TRUE; if (input_type_is_analog(field->type)) has_analog = TRUE; } /* add input menu items */ ui_menu_item_append(menu, "Input (general)", NULL, 0, (void *)menu_input_groups); ui_menu_item_append(menu, "Input (this " CAPSTARTGAMENOUN ")", NULL, 0, (void *)menu_input_specific); /* add optional input-related menus */ if (has_dips) ui_menu_item_append(menu, "Dip Switches", NULL, 0, (void *)menu_settings_dip_switches); if (has_configs) ui_menu_item_append(menu, "Driver Configuration", NULL, 0, (void *)menu_settings_driver_config); if (has_categories) ui_menu_item_append(menu, "Categories", NULL, 0, (void *)menu_settings_categories); if (has_analog) ui_menu_item_append(menu, "Analog Controls", NULL, 0, (void *)menu_analog); /* add bookkeeping menu */ ui_menu_item_append(menu, "Bookkeeping Info", NULL, 0, (void *)menu_bookkeeping); /* add game info menu */ ui_menu_item_append(menu, CAPSTARTGAMENOUN " Information", NULL, 0, (void *)menu_game_info); device_image_interface *image = NULL; if (machine.devicelist().first(image)) { /* add image info menu */ ui_menu_item_append(menu, "Image Information", NULL, 0, (void*)ui_image_menu_image_info); /* add file manager menu */ ui_menu_item_append(menu, "File Manager", NULL, 0, (void*)ui_image_menu_file_manager); /* add tape control menu */ if (machine.devicelist().first(CASSETTE)) ui_menu_item_append(menu, "Tape Control", NULL, 0, (void*)ui_mess_menu_tape_control); /* add bitbanger control menu */ if (machine.devicelist().first(BITBANGER)) ui_menu_item_append(menu, "Bitbanger Control", NULL, 0, (void*)ui_mess_menu_bitbanger_control); } device_slot_interface *slot = NULL; if (machine.devicelist().first(slot)) { /* add image info menu */ ui_menu_item_append(menu, "Slot Devices", NULL, 0, (void*)ui_menu_slot_devices); } /* add keyboard mode menu */ if (input_machine_has_keyboard(machine) && inputx_can_post(machine)) ui_menu_item_append(menu, "Keyboard Mode", NULL, 0, (void *)ui_menu_keyboard_mode); /* add sliders menu */ ui_menu_item_append(menu, "Slider Controls", NULL, 0, (void *)menu_sliders); /* add video options menu */ ui_menu_item_append(menu, "Video Options", NULL, 0, (machine.render().target_by_index(1) != NULL) ? (void *)menu_video_targets : (void *)menu_video_options); /* add crosshair options menu */ if (crosshair_get_usage(machine)) ui_menu_item_append(menu, "Crosshair Options", NULL, 0, (void *)menu_crosshair); /* add cheat menu */ if (machine.options().cheat() && machine.cheat().first() != NULL) ui_menu_item_append(menu, "Cheat", NULL, 0, (void *)menu_cheat); /* add memory card menu */ if (machine.config().m_memcard_handler != NULL) ui_menu_item_append(menu, "Memory Card", NULL, 0, (void *)menu_memory_card); /* add reset and exit menus */ ui_menu_item_append(menu, "Select New " CAPSTARTGAMENOUN, NULL, 0, (void *)menu_select_game); } /*------------------------------------------------- menu_input_groups - handle the input groups menu -------------------------------------------------*/ static void menu_input_groups(running_machine &machine, ui_menu *menu, void *parameter, void *state) { const ui_menu_event *menu_event; /* if the menu isn't built, populate now */ if (!ui_menu_populated(menu)) menu_input_groups_populate(machine, menu, state); /* process the menu */ menu_event = ui_menu_process(machine, menu, 0); if (menu_event != NULL && menu_event->iptkey == IPT_UI_SELECT) ui_menu_stack_push(ui_menu_alloc(machine, menu->container, menu_input_general, menu_event->itemref)); } /*------------------------------------------------- menu_input_groups_populate - populate the input groups menu -------------------------------------------------*/ static void menu_input_groups_populate(running_machine &machine, ui_menu *menu, void *state) { int player; /* build up the menu */ ui_menu_item_append(menu, "User Interface", NULL, 0, (void *)(IPG_UI + 1)); for (player = 0; player < MAX_PLAYERS; player++) { char buffer[40]; sprintf(buffer, "Player %d Controls", player + 1); ui_menu_item_append(menu, buffer, NULL, 0, (void *)(FPTR)(IPG_PLAYER1 + player + 1)); } ui_menu_item_append(menu, "Other Controls", NULL, 0, (void *)(FPTR)(IPG_OTHER + 1)); } /*------------------------------------------------- menu_input_general - handle the general input menu -------------------------------------------------*/ static void menu_input_general(running_machine &machine, ui_menu *menu, void *parameter, void *state) { menu_input_common(machine, menu, parameter, state); } /*------------------------------------------------- menu_input_general_populate - populate the general input menu -------------------------------------------------*/ static void menu_input_general_populate(running_machine &machine, ui_menu *menu, input_menu_state *menustate, int group) { input_item_data *itemlist = NULL; int suborder[SEQ_TYPE_TOTAL]; astring tempstring; int sortorder = 1; /* create a mini lookup table for sort order based on sequence type */ suborder[SEQ_TYPE_STANDARD] = 0; suborder[SEQ_TYPE_DECREMENT] = 1; suborder[SEQ_TYPE_INCREMENT] = 2; /* iterate over the input ports and add menu items */ for (input_type_entry *entry = input_type_list(machine).first(); entry != NULL; entry = entry->next()) /* add if we match the group and we have a valid name */ if (entry->group == group && entry->name != NULL && entry->name[0] != 0) { input_seq_type seqtype; /* loop over all sequence types */ sortorder++; for (seqtype = SEQ_TYPE_STANDARD; seqtype < SEQ_TYPE_TOTAL; seqtype++) { /* build an entry for the standard sequence */ input_item_data *item = (input_item_data *)ui_menu_pool_alloc(menu, sizeof(*item)); memset(item, 0, sizeof(*item)); item->ref = entry; item->seqtype = seqtype; item->seq = input_type_seq(machine, entry->type, entry->player, seqtype); item->defseq = &entry->defseq[seqtype]; item->sortorder = sortorder * 4 + suborder[seqtype]; item->type = input_type_is_analog(entry->type) ? (INPUT_TYPE_ANALOG + seqtype) : INPUT_TYPE_DIGITAL; item->name = entry->name; item->next = itemlist; itemlist = item; /* stop after one, unless we're analog */ if (item->type == INPUT_TYPE_DIGITAL) break; } } /* sort and populate the menu in a standard fashion */ menu_input_populate_and_sort(machine, menu, itemlist, menustate); } /*------------------------------------------------- menu_input_specific - handle the game-specific input menu -------------------------------------------------*/ static void menu_input_specific(running_machine &machine, ui_menu *menu, void *parameter, void *state) { menu_input_common(machine, menu, NULL, state); } /*------------------------------------------------- menu_input_specific_populate - populate the input menu with game-specific items -------------------------------------------------*/ static void menu_input_specific_populate(running_machine &machine, ui_menu *menu, input_menu_state *menustate) { input_item_data *itemlist = NULL; input_field_config *field; input_port_config *port; int suborder[SEQ_TYPE_TOTAL]; astring tempstring; /* create a mini lookup table for sort order based on sequence type */ suborder[SEQ_TYPE_STANDARD] = 0; suborder[SEQ_TYPE_DECREMENT] = 1; suborder[SEQ_TYPE_INCREMENT] = 2; /* iterate over the input ports and add menu items */ for (port = machine.m_portlist.first(); port != NULL; port = port->next()) for (field = port->fieldlist().first(); field != NULL; field = field->next()) { const char *name = input_field_name(field); /* add if we match the group and we have a valid name */ if (name != NULL && input_condition_true(machine, &field->condition, port->owner()) && (field->category == 0 || input_category_active(machine, field->category)) && ((field->type == IPT_OTHER && field->name != NULL) || input_type_group(machine, field->type, field->player) != IPG_INVALID)) { input_seq_type seqtype; UINT16 sortorder; /* determine the sorting order */ if (field->type >= IPT_START1 && field->type <= __ipt_analog_end) sortorder = (field->type << 2) | (field->player << 12); else sortorder = field->type | 0xf000; /* loop over all sequence types */ for (seqtype = SEQ_TYPE_STANDARD; seqtype < SEQ_TYPE_TOTAL; seqtype++) { /* build an entry for the standard sequence */ input_item_data *item = (input_item_data *)ui_menu_pool_alloc(menu, sizeof(*item)); memset(item, 0, sizeof(*item)); item->ref = field; item->seqtype = seqtype; item->seq = input_field_seq(field, seqtype); item->defseq = &get_field_default_seq(field, seqtype); item->sortorder = sortorder + suborder[seqtype]; item->type = input_type_is_analog(field->type) ? (INPUT_TYPE_ANALOG + seqtype) : INPUT_TYPE_DIGITAL; item->name = name; item->next = itemlist; itemlist = item; /* stop after one, unless we're analog */ if (item->type == INPUT_TYPE_DIGITAL) break; } } } /* sort and populate the menu in a standard fashion */ menu_input_populate_and_sort(machine, menu, itemlist, menustate); } /*------------------------------------------------- menu_input - display a menu for inputs -------------------------------------------------*/ static void menu_input_common(running_machine &machine, ui_menu *menu, void *parameter, void *state) { input_item_data *seqchangeditem = NULL; input_menu_state *menustate; const ui_menu_event *menu_event; int invalidate = FALSE; /* if no state, allocate now */ if (state == NULL) state = ui_menu_alloc_state(menu, sizeof(*menustate), NULL); menustate = (input_menu_state *)state; /* if the menu isn't built, populate now */ if (!ui_menu_populated(menu)) { if (parameter != NULL) menu_input_general_populate(machine, menu, menustate, (FPTR)parameter - 1); else menu_input_specific_populate(machine, menu, menustate); } /* process the menu */ menu_event = ui_menu_process(machine, menu, (menustate->pollingitem != NULL) ? UI_MENU_PROCESS_NOKEYS : 0); /* if we are polling, handle as a special case */ if (menustate->pollingitem != NULL) { input_item_data *item = menustate->pollingitem; input_seq newseq; /* if UI_CANCEL is pressed, abort */ if (ui_input_pressed(machine, IPT_UI_CANCEL)) { menustate->pollingitem = NULL; menustate->record_next = FALSE; toggle_none_default(item->seq, menustate->starting_seq, *item->defseq); seqchangeditem = item; } /* poll again; if finished, update the sequence */ if (machine.input().seq_poll()) { menustate->pollingitem = NULL; menustate->record_next = TRUE; item->seq = machine.input().seq_poll_final(); seqchangeditem = item; } } /* otherwise, handle the events */ else if (menu_event != NULL && menu_event->itemref != NULL) { input_item_data *item = (input_item_data *)menu_event->itemref; switch (menu_event->iptkey) { /* an item was selected: begin polling */ case IPT_UI_SELECT: menustate->pollingitem = item; menustate->last_sortorder = item->sortorder; menustate->starting_seq = item->seq; machine.input().seq_poll_start((item->type == INPUT_TYPE_ANALOG) ? ITEM_CLASS_ABSOLUTE : ITEM_CLASS_SWITCH, menustate->record_next ? &item->seq : NULL); invalidate = TRUE; break; /* if the clear key was pressed, reset the selected item */ case IPT_UI_CLEAR: toggle_none_default(item->seq, item->seq, *item->defseq); menustate->record_next = FALSE; seqchangeditem = item; break; } /* if the selection changed, reset the "record next" flag */ if (item->sortorder != menustate->last_sortorder) menustate->record_next = FALSE; menustate->last_sortorder = item->sortorder; } /* if the sequence changed, update it */ if (seqchangeditem != NULL) { /* update a general input */ if (parameter != NULL) { const input_type_entry *entry = (const input_type_entry *)seqchangeditem->ref; input_type_set_seq(machine, entry->type, entry->player, seqchangeditem->seqtype, &seqchangeditem->seq); } /* update a game-specific input */ else { input_field_user_settings settings; input_field_get_user_settings((input_field_config *)seqchangeditem->ref, &settings); settings.seq[seqchangeditem->seqtype] = seqchangeditem->seq; input_field_set_user_settings((input_field_config *)seqchangeditem->ref, &settings); } /* invalidate the menu to force an update */ invalidate = TRUE; } /* if the menu is invalidated, clear it now */ if (invalidate) { menustate->pollingref = NULL; if (menustate->pollingitem != NULL) menustate->pollingref = menustate->pollingitem->ref; ui_menu_reset(menu, UI_MENU_RESET_REMEMBER_POSITION); } } /*------------------------------------------------- menu_input_compare_items - compare two items for quicksort -------------------------------------------------*/ static int menu_input_compare_items(const void *i1, const void *i2) { const input_item_data * const *data1 = (const input_item_data * const *)i1; const input_item_data * const *data2 = (const input_item_data * const *)i2; if ((*data1)->sortorder < (*data2)->sortorder) return -1; if ((*data1)->sortorder > (*data2)->sortorder) return 1; return 0; } /*------------------------------------------------- menu_input_populate_and_sort - take a list of input_item_data objects and build up the menu from them -------------------------------------------------*/ static void menu_input_populate_and_sort(running_machine &machine, ui_menu *menu, input_item_data *itemlist, input_menu_state *menustate) { const char *nameformat[INPUT_TYPE_TOTAL] = { 0 }; input_item_data **itemarray, *item; int numitems = 0, curitem; astring subtext; astring text; /* create a mini lookup table for name format based on type */ nameformat[INPUT_TYPE_DIGITAL] = "%s"; nameformat[INPUT_TYPE_ANALOG] = "%s Analog"; nameformat[INPUT_TYPE_ANALOG_INC] = "%s Analog Inc"; nameformat[INPUT_TYPE_ANALOG_DEC] = "%s Analog Dec"; /* first count the number of items */ for (item = itemlist; item != NULL; item = item->next) numitems++; /* now allocate an array of items and fill it up */ itemarray = (input_item_data **)ui_menu_pool_alloc(menu, sizeof(*itemarray) * numitems); for (item = itemlist, curitem = 0; item != NULL; item = item->next) itemarray[curitem++] = item; /* sort it */ qsort(itemarray, numitems, sizeof(*itemarray), menu_input_compare_items); /* build the menu */ for (curitem = 0; curitem < numitems; curitem++) { UINT32 flags = 0; /* generate the name of the item itself, based off the base name and the type */ item = itemarray[curitem]; assert(nameformat[item->type] != NULL); text.printf(nameformat[item->type], item->name); /* if we're polling this item, use some spaces with left/right arrows */ if (menustate->pollingref == item->ref) { subtext.cpy(" "); flags |= MENU_FLAG_LEFT_ARROW | MENU_FLAG_RIGHT_ARROW; } /* otherwise, generate the sequence name and invert it if different from the default */ else { machine.input().seq_name(subtext, item->seq); flags |= (item->seq != *item->defseq) ? MENU_FLAG_INVERT : 0; } /* add the item */ ui_menu_item_append(menu, text, subtext, flags, item); } } /*------------------------------------------------- menu_settings_dip_switches - handle the DIP switches menu -------------------------------------------------*/ static void menu_settings_dip_switches(running_machine &machine, ui_menu *menu, void *parameter, void *state) { menu_settings_common(machine, menu, state, IPT_DIPSWITCH); } /*------------------------------------------------- menu_settings_driver_config - handle the driver config menu -------------------------------------------------*/ static void menu_settings_driver_config(running_machine &machine, ui_menu *menu, void *parameter, void *state) { menu_settings_common(machine, menu, state, IPT_CONFIG); } /*------------------------------------------------- menu_settings_categories - handle the categories menu -------------------------------------------------*/ static void menu_settings_categories(running_machine &machine, ui_menu *menu, void *parameter, void *state) { menu_settings_common(machine, menu, state, IPT_CATEGORY); } /*------------------------------------------------- menu_settings_common - handle one of the switches menus -------------------------------------------------*/ static void menu_settings_common(running_machine &machine, ui_menu *menu, void *state, UINT32 type) { settings_menu_state *menustate; const ui_menu_event *menu_event; /* if no state, allocate now */ if (state == NULL) state = ui_menu_alloc_state(menu, sizeof(*menustate), NULL); menustate = (settings_menu_state *)state; /* if the menu isn't built, populate now */ if (!ui_menu_populated(menu)) menu_settings_populate(machine, menu, menustate, type); /* process the menu */ menu_event = ui_menu_process(machine, menu, 0); /* handle events */ if (menu_event != NULL && menu_event->itemref != NULL) { input_field_config *field = (input_field_config *)menu_event->itemref; input_field_user_settings settings; int changed = FALSE; switch (menu_event->iptkey) { /* if selected, reset to default value */ case IPT_UI_SELECT: input_field_get_user_settings(field, &settings); settings.value = field->defvalue; input_field_set_user_settings(field, &settings); changed = TRUE; break; /* left goes to previous setting */ case IPT_UI_LEFT: input_field_select_previous_setting(field); changed = TRUE; break; /* right goes to next setting */ case IPT_UI_RIGHT: input_field_select_next_setting(field); changed = TRUE; break; } /* if anything changed, rebuild the menu, trying to stay on the same field */ if (changed) ui_menu_reset(menu, UI_MENU_RESET_REMEMBER_REF); } } /*------------------------------------------------- menu_settings_populate - populate one of the switches menus -------------------------------------------------*/ static void menu_settings_populate(running_machine &machine, ui_menu *menu, settings_menu_state *menustate, UINT32 type) { input_field_config *field; input_port_config *port; dip_descriptor **diplist_tailptr; int dipcount = 0; /* reset the dip switch tracking */ menustate->diplist = NULL; diplist_tailptr = &menustate->diplist; /* loop over input ports and set up the current values */ for (port = machine.m_portlist.first(); port != NULL; port = port->next()) for (field = port->fieldlist().first(); field != NULL; field = field->next()) if (field->type == type && input_condition_true(machine, &field->condition, port->owner())) { UINT32 flags = 0; /* set the left/right flags appropriately */ if (input_field_has_previous_setting(field)) flags |= MENU_FLAG_LEFT_ARROW; if (input_field_has_next_setting(field)) flags |= MENU_FLAG_RIGHT_ARROW; /* add the menu item */ ui_menu_item_append(menu, input_field_name(field), input_field_setting_name(field), flags, (void *)field); /* for DIP switches, build up the model */ if (type == IPT_DIPSWITCH && field->diploclist().count() != 0) { const input_field_diplocation *diploc; input_field_user_settings settings; UINT32 accummask = field->mask; /* get current settings */ input_field_get_user_settings(field, &settings); /* iterate over each bit in the field */ for (diploc = field->diploclist().first(); diploc != NULL; diploc = diploc->next()) { UINT32 mask = accummask & ~(accummask - 1); dip_descriptor *dip; /* find the matching switch name */ for (dip = menustate->diplist; dip != NULL; dip = dip->next) if (strcmp(dip->name, diploc->swname) == 0) break; /* allocate new if none */ if (dip == NULL) { dip = (dip_descriptor *)ui_menu_pool_alloc(menu, sizeof(*dip)); dip->next = NULL; dip->name = diploc->swname; dip->mask = dip->state = 0; *diplist_tailptr = dip; diplist_tailptr = &dip->next; dipcount++; } /* apply the bits */ dip->mask |= 1 << (diploc->swnum - 1); if (((settings.value & mask) != 0 && !diploc->invert) || ((settings.value & mask) == 0 && diploc->invert)) dip->state |= 1 << (diploc->swnum - 1); /* clear the relevant bit in the accumulated mask */ accummask &= ~mask; } } } /* configure the extra menu */ if (type == IPT_DIPSWITCH && menustate->diplist != NULL) ui_menu_set_custom_render(menu, menu_settings_custom_render, 0.0f, dipcount * (DIP_SWITCH_HEIGHT + DIP_SWITCH_SPACING) + DIP_SWITCH_SPACING); } /*------------------------------------------------- menu_settings_custom_render - perform our special rendering -------------------------------------------------*/ static void menu_settings_custom_render(running_machine &machine, ui_menu *menu, void *state, void *selectedref, float top, float bottom, float x1, float y1, float x2, float y2) { input_field_config *field = (input_field_config *)selectedref; settings_menu_state *menustate = (settings_menu_state *)state; dip_descriptor *dip; /* add borders */ y1 = y2 + UI_BOX_TB_BORDER; y2 = y1 + bottom; /* draw extra menu area */ ui_draw_outlined_box(menu->container, x1, y1, x2, y2, UI_BACKGROUND_COLOR); y1 += (float)DIP_SWITCH_SPACING; /* iterate over DIP switches */ for (dip = menustate->diplist; dip != NULL; dip = dip->next) { const input_field_diplocation *diploc; UINT32 selectedmask = 0; /* determine the mask of selected bits */ if (field != NULL) for (diploc = field->diploclist().first(); diploc != NULL; diploc = diploc->next()) if (strcmp(dip->name, diploc->swname) == 0) selectedmask |= 1 << (diploc->swnum - 1); /* draw one switch */ menu_settings_custom_render_one(menu->container, x1, y1, x2, y1 + DIP_SWITCH_HEIGHT, dip, selectedmask); y1 += (float)(DIP_SWITCH_SPACING + DIP_SWITCH_HEIGHT); } } /*------------------------------------------------- menu_settings_custom_render_one - draw a single DIP switch -------------------------------------------------*/ static void menu_settings_custom_render_one(render_container *container, float x1, float y1, float x2, float y2, const dip_descriptor *dip, UINT32 selectedmask) { float switch_field_width = SINGLE_TOGGLE_SWITCH_FIELD_WIDTH * container->manager().ui_aspect(); float switch_width = SINGLE_TOGGLE_SWITCH_WIDTH * container->manager().ui_aspect(); int numtoggles, toggle; float switch_toggle_gap; float y1_off, y1_on; /* determine the number of toggles in the DIP */ numtoggles = 32 - count_leading_zeros(dip->mask); /* center based on the number of switches */ x1 += (x2 - x1 - numtoggles * switch_field_width) / 2; /* draw the dip switch name */ ui_draw_text_full( container, dip->name, 0, y1 + (DIP_SWITCH_HEIGHT - UI_TARGET_FONT_HEIGHT) / 2, x1 - ui_get_string_width(container->manager().machine(), " "), JUSTIFY_RIGHT, WRAP_NEVER, DRAW_NORMAL, UI_TEXT_COLOR, PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA), NULL , NULL); /* compute top and bottom for on and off positions */ switch_toggle_gap = ((DIP_SWITCH_HEIGHT/2) - SINGLE_TOGGLE_SWITCH_HEIGHT)/2; y1_off = y1 + UI_LINE_WIDTH + switch_toggle_gap; y1_on = y1 + DIP_SWITCH_HEIGHT/2 + switch_toggle_gap; /* iterate over toggles */ for (toggle = 0; toggle < numtoggles; toggle++) { float innerx1; /* first outline the switch */ ui_draw_outlined_box(container, x1, y1, x1 + switch_field_width, y2, UI_BACKGROUND_COLOR); /* compute x1/x2 for the inner filled in switch */ innerx1 = x1 + (switch_field_width - switch_width) / 2; /* see if the switch is actually used */ if (dip->mask & (1 << toggle)) { float innery1 = (dip->state & (1 << toggle)) ? y1_on : y1_off; container->add_rect(innerx1, innery1, innerx1 + switch_width, innery1 + SINGLE_TOGGLE_SWITCH_HEIGHT, (selectedmask & (1 << toggle)) ? UI_DIPSW_COLOR : UI_TEXT_COLOR, PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA)); } else { container->add_rect(innerx1, y1_off, innerx1 + switch_width, y1_on + SINGLE_TOGGLE_SWITCH_HEIGHT, UI_UNAVAILABLE_COLOR, PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA)); } /* advance to the next switch */ x1 += switch_field_width; } } /*------------------------------------------------- menu_analog - handle the analog settings menu -------------------------------------------------*/ static void menu_analog(running_machine &machine, ui_menu *menu, void *parameter, void *state) { const ui_menu_event *menu_event; /* if the menu isn't built, populate now */ if (!ui_menu_populated(menu)) menu_analog_populate(machine, menu); /* process the menu */ menu_event = ui_menu_process(machine, menu, UI_MENU_PROCESS_LR_REPEAT); /* handle events */ if (menu_event != NULL && menu_event->itemref != NULL) { analog_item_data *data = (analog_item_data *)menu_event->itemref; int newval = data->cur; switch (menu_event->iptkey) { /* if selected, reset to default value */ case IPT_UI_SELECT: newval = data->defvalue; break; /* left decrements */ case IPT_UI_LEFT: newval -= machine.input().code_pressed(KEYCODE_LSHIFT) ? 10 : 1; break; /* right increments */ case IPT_UI_RIGHT: newval += machine.input().code_pressed(KEYCODE_LSHIFT) ? 10 : 1; break; } /* clamp to range */ if (newval < data->min) newval = data->min; if (newval > data->max) newval = data->max; /* if things changed, update */ if (newval != data->cur) { input_field_user_settings settings; /* get the settings and set the new value */ input_field_get_user_settings(data->field, &settings); switch (data->type) { case ANALOG_ITEM_KEYSPEED: settings.delta = newval; break; case ANALOG_ITEM_CENTERSPEED: settings.centerdelta = newval; break; case ANALOG_ITEM_REVERSE: settings.reverse = newval; break; case ANALOG_ITEM_SENSITIVITY: settings.sensitivity = newval; break; } input_field_set_user_settings(data->field, &settings); /* rebuild the menu */ ui_menu_reset(menu, UI_MENU_RESET_REMEMBER_POSITION); } } } /*------------------------------------------------- menu_analog_populate - populate the analog settings menu -------------------------------------------------*/ static void menu_analog_populate(running_machine &machine, ui_menu *menu) { input_field_config *field; input_port_config *port; astring subtext; astring text; /* loop over input ports and add the items */ for (port = machine.m_portlist.first(); port != NULL; port = port->next()) for (field = port->fieldlist().first(); field != NULL; field = field->next()) if (input_type_is_analog(field->type) && input_condition_true(machine, &field->condition, port->owner())) { input_field_user_settings settings; int use_autocenter = FALSE; int type; /* based on the type, determine if we enable autocenter */ switch (field->type) { case IPT_POSITIONAL: case IPT_POSITIONAL_V: if (field->flags & ANALOG_FLAG_WRAPS) break; case IPT_PEDAL: case IPT_PEDAL2: case IPT_PEDAL3: case IPT_PADDLE: case IPT_PADDLE_V: case IPT_AD_STICK_X: case IPT_AD_STICK_Y: case IPT_AD_STICK_Z: use_autocenter = TRUE; break; } /* get the user settings */ input_field_get_user_settings(field, &settings); /* iterate over types */ for (type = 0; type < ANALOG_ITEM_COUNT; type++) if (type != ANALOG_ITEM_CENTERSPEED || use_autocenter) { analog_item_data *data; UINT32 flags = 0; /* allocate a data item for tracking what this menu item refers to */ data = (analog_item_data *)ui_menu_pool_alloc(menu, sizeof(*data)); data->field = field; data->type = type; /* determine the properties of this item */ switch (type) { default: case ANALOG_ITEM_KEYSPEED: text.printf("%s Digital Speed", input_field_name(field)); subtext.printf("%d", settings.delta); data->min = 0; data->max = 255; data->cur = settings.delta; data->defvalue = field->delta; break; case ANALOG_ITEM_CENTERSPEED: text.printf("%s Autocenter Speed", input_field_name(field)); subtext.printf("%d", settings.centerdelta); data->min = 0; data->max = 255; data->cur = settings.centerdelta; data->defvalue = field->centerdelta; break; case ANALOG_ITEM_REVERSE: text.printf("%s Reverse", input_field_name(field)); subtext.cpy(settings.reverse ? "On" : "Off"); data->min = 0; data->max = 1; data->cur = settings.reverse; data->defvalue = ((field->flags & ANALOG_FLAG_REVERSE) != 0); break; case ANALOG_ITEM_SENSITIVITY: text.printf("%s Sensitivity", input_field_name(field)); subtext.printf("%d", settings.sensitivity); data->min = 1; data->max = 255; data->cur = settings.sensitivity; data->defvalue = field->sensitivity; break; } /* put on arrows */ if (data->cur > data->min) flags |= MENU_FLAG_LEFT_ARROW; if (data->cur < data->max) flags |= MENU_FLAG_RIGHT_ARROW; /* append a menu item */ ui_menu_item_append(menu, text, subtext, flags, data); } } } /*------------------------------------------------- menu_bookkeeping - handle the bookkeeping information menu -------------------------------------------------*/ static void menu_bookkeeping(running_machine &machine, ui_menu *menu, void *parameter, void *state) { attotime *prevtime; attotime curtime; /* if no state, allocate some */ if (state == NULL) state = ui_menu_alloc_state(menu, sizeof(*prevtime), NULL); prevtime = (attotime *)state; /* if the time has rolled over another second, regenerate */ curtime = machine.time(); if (prevtime->seconds != curtime.seconds) { ui_menu_reset(menu, UI_MENU_RESET_SELECT_FIRST); *prevtime = curtime; menu_bookkeeping_populate(machine, menu, prevtime); } /* process the menu */ ui_menu_process(machine, menu, 0); } /*------------------------------------------------- menu_bookkeeping - handle the bookkeeping information menu -------------------------------------------------*/ static void menu_bookkeeping_populate(running_machine &machine, ui_menu *menu, attotime *curtime) { int tickets = get_dispensed_tickets(machine); astring tempstring; int ctrnum; /* show total time first */ if (curtime->seconds >= 60 * 60) tempstring.catprintf("Uptime: %d:%02d:%02d\n\n", curtime->seconds / (60*60), (curtime->seconds / 60) % 60, curtime->seconds % 60); else tempstring.catprintf("Uptime: %d:%02d\n\n", (curtime->seconds / 60) % 60, curtime->seconds % 60); /* show tickets at the top */ if (tickets > 0) tempstring.catprintf("Tickets dispensed: %d\n\n", tickets); /* loop over coin counters */ for (ctrnum = 0; ctrnum < COIN_COUNTERS; ctrnum++) { int count = coin_counter_get_count(machine, ctrnum); /* display the coin counter number */ tempstring.catprintf("Coin %c: ", ctrnum + 'A'); /* display how many coins */ if (count == 0) tempstring.cat("NA"); else tempstring.catprintf("%d", count); /* display whether or not we are locked out */ if (coin_lockout_get_state(machine, ctrnum)) tempstring.cat(" (locked)"); tempstring.cat("\n"); } /* append the single item */ ui_menu_item_append(menu, tempstring, NULL, MENU_FLAG_MULTILINE, NULL); } /*------------------------------------------------- menu_game_info - handle the game information menu -------------------------------------------------*/ static void menu_game_info(running_machine &machine, ui_menu *menu, void *parameter, void *state) { /* if the menu isn't built, populate now */ if (!ui_menu_populated(menu)) { astring tempstring; ui_menu_item_append(menu, game_info_astring(machine, tempstring), NULL, MENU_FLAG_MULTILINE, NULL); } /* process the menu */ ui_menu_process(machine, menu, 0); } /*------------------------------------------------- menu_cheat - handle the cheat menu -------------------------------------------------*/ static void menu_cheat(running_machine &machine, ui_menu *menu, void *parameter, void *state) { const ui_menu_event *menu_event; /* if the menu isn't built, populate now */ if (!ui_menu_populated(menu)) menu_cheat_populate(machine, menu); /* process the menu */ menu_event = ui_menu_process(machine, menu, UI_MENU_PROCESS_LR_REPEAT); /* handle events */ if (menu_event != NULL && menu_event->itemref != NULL) { bool changed = false; /* clear cheat comment on any movement or keypress */ popmessage(NULL); /* handle reset all + reset all cheats for reload all option */ if ((FPTR)menu_event->itemref < 3 && menu_event->iptkey == IPT_UI_SELECT) { for (cheat_entry *curcheat = machine.cheat().first(); curcheat != NULL; curcheat = curcheat->next()) if (curcheat->select_default_state()) changed = true; } /* handle individual cheats */ else if ((FPTR)menu_event->itemref > 2) { cheat_entry *curcheat = reinterpret_cast<cheat_entry *>(menu_event->itemref); const char *string; switch (menu_event->iptkey) { /* if selected, activate a oneshot */ case IPT_UI_SELECT: changed = curcheat->activate(); break; /* if cleared, reset to default value */ case IPT_UI_CLEAR: changed = curcheat->select_default_state(); break; /* left decrements */ case IPT_UI_LEFT: changed = curcheat->select_previous_state(); break; /* right increments */ case IPT_UI_RIGHT: changed = curcheat->select_next_state(); break; /* bring up display comment if one exists */ case IPT_UI_DISPLAY_COMMENT: case IPT_UI_UP: case IPT_UI_DOWN: string = curcheat->comment(); if (string != NULL && string[0] != 0) popmessage("Cheat Comment:\n%s", string); break; } } /* handle reload all */ if ((FPTR)menu_event->itemref == 2 && menu_event->iptkey == IPT_UI_SELECT) { /* re-init cheat engine and thus reload cheats/cheats have already been turned off by here */ machine.cheat().reload(); /* display the reloaded cheats */ ui_menu_reset(menu, UI_MENU_RESET_REMEMBER_REF); popmessage("All cheats reloaded"); } /* if things changed, update */ if (changed) ui_menu_reset(menu, UI_MENU_RESET_REMEMBER_REF); } } /*------------------------------------------------- menu_cheat_populate - populate the cheat menu -------------------------------------------------*/ static void menu_cheat_populate(running_machine &machine, ui_menu *menu) { /* iterate over cheats */ astring text; astring subtext; for (cheat_entry *curcheat = machine.cheat().first(); curcheat != NULL; curcheat = curcheat->next()) { UINT32 flags; curcheat->menu_text(text, subtext, flags); ui_menu_item_append(menu, text, subtext, flags, curcheat); } /* add a separator */ ui_menu_item_append(menu, MENU_SEPARATOR_ITEM, NULL, 0, NULL); /* add a reset all option */ ui_menu_item_append(menu, "Reset All", NULL, 0, (void *)1); /* add a reload all cheats option */ ui_menu_item_append(menu, "Reload All", NULL, 0, (void *)2); } /*------------------------------------------------- menu_memory_card - handle the memory card menu -------------------------------------------------*/ static void menu_memory_card(running_machine &machine, ui_menu *menu, void *parameter, void *state) { const ui_menu_event *menu_event; int *cardnum; /* if no state, allocate some */ if (state == NULL) state = ui_menu_alloc_state(menu, sizeof(*cardnum), NULL); cardnum = (int *)state; /* if the menu isn't built, populate now */ if (!ui_menu_populated(menu)) menu_memory_card_populate(machine, menu, *cardnum); /* process the menu */ menu_event = ui_menu_process(machine, menu, UI_MENU_PROCESS_LR_REPEAT); /* if something was selected, act on it */ if (menu_event != NULL && menu_event->itemref != NULL) { FPTR item = (FPTR)menu_event->itemref; /* select executes actions on some of the items */ if (menu_event->iptkey == IPT_UI_SELECT) { switch (item) { /* handle card loading; if we succeed, clear the menus */ case MEMCARD_ITEM_LOAD: if (memcard_insert(menu->machine(), *cardnum) == 0) { popmessage("Memory card loaded"); ui_menu_stack_reset(menu->machine()); } else popmessage("Error loading memory card"); break; /* handle card ejecting */ case MEMCARD_ITEM_EJECT: memcard_eject(menu->machine()); popmessage("Memory card ejected"); break; /* handle card creating */ case MEMCARD_ITEM_CREATE: if (memcard_create(menu->machine(), *cardnum, FALSE) == 0) popmessage("Memory card created"); else popmessage("Error creating memory card\n(Card may already exist)"); break; } } /* the select item has extra keys */ else if (item == MEMCARD_ITEM_SELECT) { switch (menu_event->iptkey) { /* left decrements the card number */ case IPT_UI_LEFT: *cardnum -= 1; ui_menu_reset(menu, UI_MENU_RESET_REMEMBER_REF); break; /* right decrements the card number */ case IPT_UI_RIGHT: *cardnum += 1; ui_menu_reset(menu, UI_MENU_RESET_REMEMBER_REF); break; } } } } /*------------------------------------------------- menu_memory_card_populate - populate the memory card menu -------------------------------------------------*/ static void menu_memory_card_populate(running_machine &machine, ui_menu *menu, int cardnum) { char tempstring[20]; UINT32 flags = 0; /* add the card select menu */ sprintf(tempstring, "%d", cardnum); if (cardnum > 0) flags |= MENU_FLAG_LEFT_ARROW; if (cardnum < 1000) flags |= MENU_FLAG_RIGHT_ARROW; ui_menu_item_append(menu, "Card Number:", tempstring, flags, (void *)MEMCARD_ITEM_SELECT); /* add the remaining items */ ui_menu_item_append(menu, "Load Selected Card", NULL, 0, (void *)MEMCARD_ITEM_LOAD); if (memcard_present(machine) != -1) ui_menu_item_append(menu, "Eject Current Card", NULL, 0, (void *)MEMCARD_ITEM_EJECT); ui_menu_item_append(menu, "Create New Card", NULL, 0, (void *)MEMCARD_ITEM_CREATE); } /*------------------------------------------------- menu_sliders - handle the sliders menu -------------------------------------------------*/ static void menu_sliders(running_machine &machine, ui_menu *menu, void *parameter, void *state) { int menuless_mode = (parameter != NULL); const ui_menu_event *menu_event; UINT8 *hidden = (UINT8 *)state; /* if no state, allocate some */ if (hidden == NULL) hidden = (UINT8 *)ui_menu_alloc_state(menu, sizeof(*hidden), NULL); if (menuless_mode) *hidden = TRUE; /* if the menu isn't built, populate now */ if (!ui_menu_populated(menu)) menu_sliders_populate(machine, menu, menuless_mode); /* process the menu */ menu_event = ui_menu_process(machine, menu, UI_MENU_PROCESS_LR_REPEAT | (*hidden ? UI_MENU_PROCESS_CUSTOM_ONLY : 0)); if (menu_event != NULL) { /* handle keys if there is a valid item selected */ if (menu_event->itemref != NULL) { const slider_state *slider = (const slider_state *)menu_event->itemref; INT32 curvalue = (*slider->update)(machine, slider->arg, NULL, SLIDER_NOCHANGE); INT32 increment = 0; switch (menu_event->iptkey) { /* toggle visibility */ case IPT_UI_ON_SCREEN_DISPLAY: if (menuless_mode) ui_menu_stack_pop(machine); else *hidden = !*hidden; break; /* decrease value */ case IPT_UI_LEFT: if (machine.input().code_pressed(KEYCODE_LALT) || machine.input().code_pressed(KEYCODE_RALT)) increment = -1; else if (machine.input().code_pressed(KEYCODE_LSHIFT) || machine.input().code_pressed(KEYCODE_RSHIFT)) increment = (slider->incval > 10) ? -(slider->incval / 10) : -1; else if (machine.input().code_pressed(KEYCODE_LCONTROL) || machine.input().code_pressed(KEYCODE_RCONTROL)) increment = -slider->incval * 10; else increment = -slider->incval; break; /* increase value */ case IPT_UI_RIGHT: if (machine.input().code_pressed(KEYCODE_LALT) || machine.input().code_pressed(KEYCODE_RALT)) increment = 1; else if (machine.input().code_pressed(KEYCODE_LSHIFT) || machine.input().code_pressed(KEYCODE_RSHIFT)) increment = (slider->incval > 10) ? (slider->incval / 10) : 1; else if (machine.input().code_pressed(KEYCODE_LCONTROL) || machine.input().code_pressed(KEYCODE_RCONTROL)) increment = slider->incval * 10; else increment = slider->incval; break; /* restore default */ case IPT_UI_SELECT: increment = slider->defval - curvalue; break; } /* handle any changes */ if (increment != 0) { INT32 newvalue = curvalue + increment; /* clamp within bounds */ if (newvalue < slider->minval) newvalue = slider->minval; if (newvalue > slider->maxval) newvalue = slider->maxval; /* update the slider and recompute the menu */ (*slider->update)(machine, slider->arg, NULL, newvalue); ui_menu_reset(menu, UI_MENU_RESET_REMEMBER_REF); } } /* if we are selecting an invalid item and we are hidden, skip to the next one */ else if (*hidden) { /* if we got here via up or page up, select the previous item */ if (menu_event->iptkey == IPT_UI_UP || menu_event->iptkey == IPT_UI_PAGE_UP) { menu->selected = (menu->selected + menu->numitems - 1) % menu->numitems; ui_menu_validate_selection(menu, -1); } /* otherwise select the next item */ else if (menu_event->iptkey == IPT_UI_DOWN || menu_event->iptkey == IPT_UI_PAGE_DOWN) { menu->selected = (menu->selected + 1) % menu->numitems; ui_menu_validate_selection(menu, 1); } } } } /*------------------------------------------------- menu_sliders_populate - populate the sliders menu -------------------------------------------------*/ static void menu_sliders_populate(running_machine &machine, ui_menu *menu, int menuless_mode) { const slider_state *curslider; astring tempstring; /* add all sliders */ for (curslider = ui_get_slider_list(); curslider != NULL; curslider = curslider->next) { INT32 curval = (*curslider->update)(machine, curslider->arg, &tempstring, SLIDER_NOCHANGE); UINT32 flags = 0; if (curval > curslider->minval) flags |= MENU_FLAG_LEFT_ARROW; if (curval < curslider->maxval) flags |= MENU_FLAG_RIGHT_ARROW; ui_menu_item_append(menu, curslider->description, tempstring, flags, (void *)curslider); if (menuless_mode) break; } /* add all sliders */ for (curslider = (slider_state*)machine.osd().get_slider_list(); curslider != NULL; curslider = curslider->next) { INT32 curval = (*curslider->update)(machine, curslider->arg, &tempstring, SLIDER_NOCHANGE); UINT32 flags = 0; if (curval > curslider->minval) flags |= MENU_FLAG_LEFT_ARROW; if (curval < curslider->maxval) flags |= MENU_FLAG_RIGHT_ARROW; ui_menu_item_append(menu, curslider->description, tempstring, flags, (void *)curslider); } ui_menu_set_custom_render(menu, menu_sliders_custom_render, 0.0f, 2.0f * ui_get_line_height(machine) + 2.0f * UI_BOX_TB_BORDER); } /*------------------------------------------------- menu_sliders_custom_render - perform our special rendering -------------------------------------------------*/ static void menu_sliders_custom_render(running_machine &machine, ui_menu *menu, void *state, void *selectedref, float top, float bottom, float x1, float y1, float x2, float y2) { const slider_state *curslider = (const slider_state *)selectedref; if (curslider != NULL) { float bar_left, bar_area_top, bar_width, bar_area_height, bar_top, bar_bottom, default_x, current_x; float line_height = ui_get_line_height(machine); float percentage, default_percentage; astring tempstring; float text_height; INT32 curval; /* determine the current value and text */ curval = (*curslider->update)(machine, curslider->arg, &tempstring, SLIDER_NOCHANGE); /* compute the current and default percentages */ percentage = (float)(curval - curslider->minval) / (float)(curslider->maxval - curslider->minval); default_percentage = (float)(curslider->defval - curslider->minval) / (float)(curslider->maxval - curslider->minval); /* assemble the the text */ tempstring.ins(0, " ").ins(0, curslider->description); /* move us to the bottom of the screen, and expand to full width */ y2 = 1.0f - UI_BOX_TB_BORDER; y1 = y2 - bottom; x1 = UI_BOX_LR_BORDER; x2 = 1.0f - UI_BOX_LR_BORDER; /* draw extra menu area */ ui_draw_outlined_box(menu->container, x1, y1, x2, y2, UI_BACKGROUND_COLOR); y1 += UI_BOX_TB_BORDER; /* determine the text height */ ui_draw_text_full(menu->container, tempstring, 0, 0, x2 - x1 - 2.0f * UI_BOX_LR_BORDER, JUSTIFY_CENTER, WRAP_TRUNCATE, DRAW_NONE, ARGB_WHITE, ARGB_BLACK, NULL, &text_height); /* draw the thermometer */ bar_left = x1 + UI_BOX_LR_BORDER; bar_area_top = y1; bar_width = x2 - x1 - 2.0f * UI_BOX_LR_BORDER; bar_area_height = line_height; /* compute positions */ bar_top = bar_area_top + 0.125f * bar_area_height; bar_bottom = bar_area_top + 0.875f * bar_area_height; default_x = bar_left + bar_width * default_percentage; current_x = bar_left + bar_width * percentage; /* fill in the percentage */ menu->container->add_rect(bar_left, bar_top, current_x, bar_bottom, UI_SLIDER_COLOR, PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA)); /* draw the top and bottom lines */ menu->container->add_line(bar_left, bar_top, bar_left + bar_width, bar_top, UI_LINE_WIDTH, UI_BORDER_COLOR, PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA)); menu->container->add_line(bar_left, bar_bottom, bar_left + bar_width, bar_bottom, UI_LINE_WIDTH, UI_BORDER_COLOR, PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA)); /* draw default marker */ menu->container->add_line(default_x, bar_area_top, default_x, bar_top, UI_LINE_WIDTH, UI_BORDER_COLOR, PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA)); menu->container->add_line(default_x, bar_bottom, default_x, bar_area_top + bar_area_height, UI_LINE_WIDTH, UI_BORDER_COLOR, PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA)); /* draw the actual text */ ui_draw_text_full(menu->container, tempstring, x1 + UI_BOX_LR_BORDER, y1 + line_height, x2 - x1 - 2.0f * UI_BOX_LR_BORDER, JUSTIFY_CENTER, WRAP_WORD, DRAW_NORMAL, UI_TEXT_COLOR, UI_TEXT_BG_COLOR, NULL, &text_height); } } /*------------------------------------------------- menu_video_targets - handle the video targets menu -------------------------------------------------*/ static void menu_video_targets(running_machine &machine, ui_menu *menu, void *parameter, void *state) { const ui_menu_event *menu_event; /* if the menu isn't built, populate now */ if (!ui_menu_populated(menu)) menu_video_targets_populate(machine, menu); /* process the menu */ menu_event = ui_menu_process(machine, menu, 0); if (menu_event != NULL && menu_event->iptkey == IPT_UI_SELECT) ui_menu_stack_push(ui_menu_alloc(machine, menu->container, menu_video_options, menu_event->itemref)); } /*------------------------------------------------- menu_video_targets_populate - populate the video targets menu -------------------------------------------------*/ static void menu_video_targets_populate(running_machine &machine, ui_menu *menu) { int targetnum; /* find the targets */ for (targetnum = 0; ; targetnum++) { render_target *target = machine.render().target_by_index(targetnum); char buffer[40]; /* stop when we run out */ if (target == NULL) break; /* add a menu item */ sprintf(buffer, "Screen #%d", targetnum); ui_menu_item_append(menu, buffer, NULL, 0, target); } } /*------------------------------------------------- menu_video_options - handle the video options menu -------------------------------------------------*/ static void menu_video_options(running_machine &machine, ui_menu *menu, void *parameter, void *state) { render_target *target = (parameter != NULL) ? (render_target *)parameter : machine.render().first_target(); const ui_menu_event *menu_event; int changed = FALSE; /* if the menu isn't built, populate now */ if (!ui_menu_populated(menu)) menu_video_options_populate(machine, menu, target); /* process the menu */ menu_event = ui_menu_process(machine, menu, 0); if (menu_event != NULL && menu_event->itemref != NULL) { switch ((FPTR)menu_event->itemref) { /* rotate adds rotation depending on the direction */ case VIDEO_ITEM_ROTATE: if (menu_event->iptkey == IPT_UI_LEFT || menu_event->iptkey == IPT_UI_RIGHT) { int delta = (menu_event->iptkey == IPT_UI_LEFT) ? ROT270 : ROT90; target->set_orientation(orientation_add(delta, target->orientation())); if (target->is_ui_target()) { render_container::user_settings settings; menu->container->get_user_settings(settings); settings.m_orientation = orientation_add(delta ^ ROT180, settings.m_orientation); menu->container->set_user_settings(settings); } changed = TRUE; } break; /* layer config bitmasks handle left/right keys the same (toggle) */ case VIDEO_ITEM_BACKDROPS: if (menu_event->iptkey == IPT_UI_LEFT || menu_event->iptkey == IPT_UI_RIGHT) { target->set_backdrops_enabled(!target->backdrops_enabled()); changed = TRUE; } break; case VIDEO_ITEM_OVERLAYS: if (menu_event->iptkey == IPT_UI_LEFT || menu_event->iptkey == IPT_UI_RIGHT) { target->set_overlays_enabled(!target->overlays_enabled()); changed = TRUE; } break; case VIDEO_ITEM_BEZELS: if (menu_event->iptkey == IPT_UI_LEFT || menu_event->iptkey == IPT_UI_RIGHT) { target->set_bezels_enabled(!target->bezels_enabled()); changed = TRUE; } break; case VIDEO_ITEM_CPANELS: if (menu_event->iptkey == IPT_UI_LEFT || menu_event->iptkey == IPT_UI_RIGHT) { target->set_cpanels_enabled(!target->cpanels_enabled()); changed = TRUE; } break; case VIDEO_ITEM_MARQUEES: if (menu_event->iptkey == IPT_UI_LEFT || menu_event->iptkey == IPT_UI_RIGHT) { target->set_marquees_enabled(!target->marquees_enabled()); changed = TRUE; } break; case VIDEO_ITEM_ZOOM: if (menu_event->iptkey == IPT_UI_LEFT || menu_event->iptkey == IPT_UI_RIGHT) { target->set_zoom_to_screen(!target->zoom_to_screen()); changed = TRUE; } break; /* anything else is a view item */ default: if (menu_event->iptkey == IPT_UI_SELECT && (int)(FPTR)menu_event->itemref >= VIDEO_ITEM_VIEW) { target->set_view((FPTR)menu_event->itemref - VIDEO_ITEM_VIEW); changed = TRUE; } break; } } /* if something changed, rebuild the menu */ if (changed) ui_menu_reset(menu, UI_MENU_RESET_REMEMBER_REF); } /*------------------------------------------------- menu_video_options_populate - populate the video options menu -------------------------------------------------*/ static void menu_video_options_populate(running_machine &machine, ui_menu *menu, render_target *target) { const char *subtext = ""; astring tempstring; int viewnum; int enabled; /* add items for each view */ for (viewnum = 0; ; viewnum++) { const char *name = target->view_name(viewnum); if (name == NULL) break; /* create a string for the item, replacing underscores with spaces */ tempstring.cpy(name).replace(0, "_", " "); ui_menu_item_append(menu, tempstring, NULL, 0, (void *)(FPTR)(VIDEO_ITEM_VIEW + viewnum)); } /* add a separator */ ui_menu_item_append(menu, MENU_SEPARATOR_ITEM, NULL, 0, NULL); /* add a rotate item */ switch (target->orientation()) { case ROT0: subtext = "None"; break; case ROT90: subtext = "CW 90" UTF8_DEGREES; break; case ROT180: subtext = "180" UTF8_DEGREES; break; case ROT270: subtext = "CCW 90" UTF8_DEGREES; break; } ui_menu_item_append(menu, "Rotate", subtext, MENU_FLAG_LEFT_ARROW | MENU_FLAG_RIGHT_ARROW, (void *)VIDEO_ITEM_ROTATE); /* backdrop item */ enabled = target->backdrops_enabled(); ui_menu_item_append(menu, "Backdrops", enabled ? "Enabled" : "Disabled", enabled ? MENU_FLAG_LEFT_ARROW : MENU_FLAG_RIGHT_ARROW, (void *)VIDEO_ITEM_BACKDROPS); /* overlay item */ enabled = target->overlays_enabled(); ui_menu_item_append(menu, "Overlays", enabled ? "Enabled" : "Disabled", enabled ? MENU_FLAG_LEFT_ARROW : MENU_FLAG_RIGHT_ARROW, (void *)VIDEO_ITEM_OVERLAYS); /* bezel item */ enabled = target->bezels_enabled(); ui_menu_item_append(menu, "Bezels", enabled ? "Enabled" : "Disabled", enabled ? MENU_FLAG_LEFT_ARROW : MENU_FLAG_RIGHT_ARROW, (void *)VIDEO_ITEM_BEZELS); /* cpanel item */ enabled = target->cpanels_enabled(); ui_menu_item_append(menu, "CPanels", enabled ? "Enabled" : "Disabled", enabled ? MENU_FLAG_LEFT_ARROW : MENU_FLAG_RIGHT_ARROW, (void *)VIDEO_ITEM_CPANELS); /* marquee item */ enabled = target->marquees_enabled(); ui_menu_item_append(menu, "Marquees", enabled ? "Enabled" : "Disabled", enabled ? MENU_FLAG_LEFT_ARROW : MENU_FLAG_RIGHT_ARROW, (void *)VIDEO_ITEM_MARQUEES); /* cropping */ enabled = target->zoom_to_screen(); ui_menu_item_append(menu, "View", enabled ? "Cropped" : "Full", enabled ? MENU_FLAG_RIGHT_ARROW : MENU_FLAG_LEFT_ARROW, (void *)VIDEO_ITEM_ZOOM); } /*------------------------------------------------- menu_crosshair - handle the crosshair settings menu -------------------------------------------------*/ static void menu_crosshair(running_machine &machine, ui_menu *menu, void *parameter, void *state) { const ui_menu_event *menu_event; /* if the menu isn't built, populate now */ if (!ui_menu_populated(menu)) menu_crosshair_populate(machine, menu); /* process the menu */ menu_event = ui_menu_process(machine, menu, UI_MENU_PROCESS_LR_REPEAT); /* handle events */ if (menu_event != NULL && menu_event->itemref != NULL) { crosshair_user_settings settings; crosshair_item_data *data = (crosshair_item_data *)menu_event->itemref; int changed = FALSE; //int set_def = FALSE; int newval = data->cur; /* retreive the user settings */ crosshair_get_user_settings(machine, data->player, &settings); switch (menu_event->iptkey) { /* if selected, reset to default value */ case IPT_UI_SELECT: newval = data->defvalue; //set_def = TRUE; break; /* left decrements */ case IPT_UI_LEFT: newval -= machine.input().code_pressed(KEYCODE_LSHIFT) ? 10 : 1; break; /* right increments */ case IPT_UI_RIGHT: newval += machine.input().code_pressed(KEYCODE_LSHIFT) ? 10 : 1; break; } /* clamp to range */ if (newval < data->min) newval = data->min; if (newval > data->max) newval = data->max; /* if things changed, update */ if (newval != data->cur) { switch (data->type) { /* visibility state */ case CROSSHAIR_ITEM_VIS: settings.mode = newval; changed = TRUE; break; /* auto time */ case CROSSHAIR_ITEM_AUTO_TIME: settings.auto_time = newval; changed = TRUE; break; } } /* crosshair graphic name */ if (data->type == CROSSHAIR_ITEM_PIC) { if (menu_event->iptkey == IPT_UI_SELECT) { /* clear the name string to reset to default crosshair */ settings.name[0] = 0; changed = TRUE; } else if (menu_event->iptkey == IPT_UI_LEFT) { strcpy(settings.name, data->last_name); changed = TRUE; } else if (menu_event->iptkey == IPT_UI_RIGHT) { strcpy(settings.name, data->next_name); changed = TRUE; } } if (changed) { /* save the user settings */ crosshair_set_user_settings(machine, data->player, &settings); /* rebuild the menu */ ui_menu_reset(menu, UI_MENU_RESET_REMEMBER_POSITION); } } } /*------------------------------------------------- menu_crosshair_populate - populate the crosshair settings menu -------------------------------------------------*/ static void menu_crosshair_populate(running_machine &machine, ui_menu *menu) { crosshair_user_settings settings; crosshair_item_data *data; char temp_text[16]; int player; UINT8 use_auto = FALSE; UINT32 flags = 0; /* loop over player and add the manual items */ for (player = 0; player < MAX_PLAYERS; player++) { /* get the user settings */ crosshair_get_user_settings(machine, player, &settings); /* add menu items for usable crosshairs */ if (settings.used) { /* Make sure to keep these matched to the CROSSHAIR_VISIBILITY_xxx types */ static const char *const vis_text[] = { "Off", "On", "Auto" }; /* track if we need the auto time menu */ if (settings.mode == CROSSHAIR_VISIBILITY_AUTO) use_auto = TRUE; /* CROSSHAIR_ITEM_VIS - allocate a data item and fill it */ data = (crosshair_item_data *)ui_menu_pool_alloc(menu, sizeof(*data)); data->type = CROSSHAIR_ITEM_VIS; data->player = player; data->min = CROSSHAIR_VISIBILITY_OFF; data->max = CROSSHAIR_VISIBILITY_AUTO; data->defvalue = CROSSHAIR_VISIBILITY_DEFAULT; data->cur = settings.mode; /* put on arrows */ if (data->cur > data->min) flags |= MENU_FLAG_LEFT_ARROW; if (data->cur < data->max) flags |= MENU_FLAG_RIGHT_ARROW; /* add CROSSHAIR_ITEM_VIS menu */ sprintf(temp_text, "P%d Visibility", player + 1); ui_menu_item_append(menu, temp_text, vis_text[settings.mode], flags, data); /* CROSSHAIR_ITEM_PIC - allocate a data item and fill it */ data = (crosshair_item_data *)ui_menu_pool_alloc(menu, sizeof(*data)); data->type = CROSSHAIR_ITEM_PIC; data->player = player; data->last_name[0] = 0; /* other data item not used by this menu */ /* search for crosshair graphics */ /* open a path to the crosshairs */ file_enumerator path(machine.options().crosshair_path()); const osd_directory_entry *dir; /* reset search flags */ int using_default = FALSE; int finished = FALSE; int found = FALSE; /* if we are using the default, then we just need to find the first in the list */ if (strlen(settings.name) == 0) using_default = TRUE; /* look for the current name, then remember the name before */ /* and find the next name */ while (((dir = path.next()) != NULL) && !finished) { int length = strlen(dir->name); /* look for files ending in .png with a name not larger then 9 chars*/ if ((length > 4) && (length <= CROSSHAIR_PIC_NAME_LENGTH + 4) && dir->name[length - 4] == '.' && tolower((UINT8)dir->name[length - 3]) == 'p' && tolower((UINT8)dir->name[length - 2]) == 'n' && tolower((UINT8)dir->name[length - 1]) == 'g') { /* remove .png from length */ length -= 4; if (found || using_default) { /* get the next name */ strncpy(data->next_name, dir->name, length); data->next_name[length] = 0; finished = TRUE; } else if (!strncmp(dir->name, settings.name, length)) { /* we found the current name */ /* so loop once more to find the next name */ found = TRUE; } else /* remember last name */ /* we will do it here in case files get added to the directory */ { strncpy(data->last_name, dir->name, length); data->last_name[length] = 0; } } } /* if name not found then next item is DEFAULT */ if (!found && !using_default) { data->next_name[0] = 0; finished = TRUE; } /* setup the selection flags */ flags = 0; if (finished) flags |= MENU_FLAG_RIGHT_ARROW; if (found) flags |= MENU_FLAG_LEFT_ARROW; /* add CROSSHAIR_ITEM_PIC menu */ sprintf(temp_text, "P%d Crosshair", player + 1); ui_menu_item_append(menu, temp_text, using_default ? "DEFAULT" : settings.name, flags, data); } } if (use_auto) { /* any player can be used to get the autotime */ crosshair_get_user_settings(machine, 0, &settings); /* CROSSHAIR_ITEM_AUTO_TIME - allocate a data item and fill it */ data = (crosshair_item_data *)ui_menu_pool_alloc(menu, sizeof(*data)); data->type = CROSSHAIR_ITEM_AUTO_TIME; data->min = CROSSHAIR_VISIBILITY_AUTOTIME_MIN; data->max = CROSSHAIR_VISIBILITY_AUTOTIME_MAX; data->defvalue = CROSSHAIR_VISIBILITY_AUTOTIME_DEFAULT; data->cur = settings.auto_time; /* put on arrows in visible menu */ if (data->cur > data->min) flags |= MENU_FLAG_LEFT_ARROW; if (data->cur < data->max) flags |= MENU_FLAG_RIGHT_ARROW; /* add CROSSHAIR_ITEM_AUTO_TIME menu */ sprintf(temp_text, "%d", settings.auto_time); ui_menu_item_append(menu, "Visible Delay", temp_text, flags, data); } // else // /* leave a blank filler line when not in auto time so size does not rescale */ // ui_menu_item_append(menu, "", "", NULL, NULL); } /*------------------------------------------------- menu_quit_game - handle the "menu" for quitting the game -------------------------------------------------*/ static void menu_quit_game(running_machine &machine, ui_menu *menu, void *parameter, void *state) { /* request a reset */ machine.schedule_exit(); /* reset the menu stack */ ui_menu_stack_reset(machine); } /*------------------------------------------------- menu_select_game - handle the game select menu -------------------------------------------------*/ static void menu_select_game(running_machine &machine, ui_menu *menu, void *parameter, void *state) { select_game_state *menustate; const ui_menu_event *menu_event; /* if no state, allocate some */ if (state == NULL) { state = ui_menu_alloc_state(menu, sizeof(*menustate) + sizeof(menustate->driverlist) * driver_list::total(), NULL); if (parameter != NULL) strcpy(((select_game_state *)state)->search, (const char *)parameter); ((select_game_state *)state)->matchlist[0] = -1; } menustate = (select_game_state *)state; /* if the menu isn't built, populate now */ if (!ui_menu_populated(menu)) menu_select_game_populate(machine, menu, menustate); /* ignore pause keys by swallowing them before we process the menu */ ui_input_pressed(machine, IPT_UI_PAUSE); /* process the menu */ menu_event = ui_menu_process(machine, menu, 0); if (menu_event != NULL && menu_event->itemref != NULL) { /* reset the error on any future menu_event */ if (menustate->error) menustate->error = FALSE; /* handle selections */ else if (menu_event->iptkey == IPT_UI_SELECT) { const game_driver *driver = (const game_driver *)menu_event->itemref; /* special case for configure inputs */ if ((FPTR)driver == 1) ui_menu_stack_push(ui_menu_alloc(menu->machine(), menu->container, menu_input_groups, NULL)); /* anything else is a driver */ else { // audit the game first to see if we're going to work driver_enumerator enumerator(machine.options(), *driver); enumerator.next(); media_auditor auditor(enumerator); media_auditor::summary summary = auditor.audit_media(AUDIT_VALIDATE_FAST); // if everything looks good, schedule the new driver if (summary == media_auditor::CORRECT || summary == media_auditor::BEST_AVAILABLE) { machine.schedule_new_driver(*driver); ui_menu_stack_reset(machine); } // otherwise, display an error else { ui_menu_reset(menu, UI_MENU_RESET_REMEMBER_REF); menustate->error = TRUE; } } } /* escape pressed with non-empty text clears the text */ else if (menu_event->iptkey == IPT_UI_CANCEL && menustate->search[0] != 0) { /* since we have already been popped, we must recreate ourself from scratch */ ui_menu_stack_push(ui_menu_alloc(menu->machine(), menu->container, menu_select_game, NULL)); } /* typed characters append to the buffer */ else if (menu_event->iptkey == IPT_SPECIAL) { int buflen = strlen(menustate->search); /* if it's a backspace and we can handle it, do so */ if ((menu_event->unichar == 8 || menu_event->unichar == 0x7f) && buflen > 0) { *(char *)utf8_previous_char(&menustate->search[buflen]) = 0; menustate->rerandomize = TRUE; ui_menu_reset(menu, UI_MENU_RESET_SELECT_FIRST); } /* if it's any other key and we're not maxed out, update */ else if (menu_event->unichar >= ' ' && menu_event->unichar < 0x7f) { buflen += utf8_from_uchar(&menustate->search[buflen], ARRAY_LENGTH(menustate->search) - buflen, menu_event->unichar); menustate->search[buflen] = 0; ui_menu_reset(menu, UI_MENU_RESET_SELECT_FIRST); } } } /* if we're in an error state, overlay an error message */ if (menustate->error) ui_draw_text_box(menu->container, "The selected game is missing one or more required ROM or CHD images. " "Please select a different game.\n\nPress any key to continue.", JUSTIFY_CENTER, 0.5f, 0.5f, UI_RED_COLOR); } /*------------------------------------------------- menu_select_game_populate - populate the game select menu -------------------------------------------------*/ static void menu_select_game_populate(running_machine &machine, ui_menu *menu, select_game_state *menustate) { int matchcount; int curitem; /* update our driver list if necessary */ if (menustate->driverlist[0] == NULL) menu_select_game_build_driver_list(menu, menustate); for (curitem = matchcount = 0; menustate->driverlist[curitem] != NULL && matchcount < VISIBLE_GAMES_IN_LIST; curitem++) if (!(menustate->driverlist[curitem]->flags & GAME_NO_STANDALONE)) matchcount++; /* if nothing there, add a single multiline item and return */ if (matchcount == 0) { ui_menu_item_append(menu, "No "GAMESNOUN" found. Please check the rompath specified in the "CONFIGNAME".ini file.\n\n" "If this is your first time using "APPNAME", please see the config.txt file in " "the docs directory for information on configuring "APPNAME".", NULL, MENU_FLAG_MULTILINE | MENU_FLAG_REDTEXT, NULL); return; } /* otherwise, rebuild the match list */ assert(drivlist != NULL); if (menustate->search[0] != 0 || menustate->matchlist[0] == -1 || menustate->rerandomize) drivlist->find_approximate_matches(menustate->search, matchcount, menustate->matchlist); menustate->rerandomize = FALSE; /* iterate over entries */ for (curitem = 0; curitem < matchcount; curitem++) { int curmatch = menustate->matchlist[curitem]; if (curmatch != -1) { int cloneof = drivlist->non_bios_clone(curmatch); ui_menu_item_append(menu, drivlist->driver(curmatch).name, drivlist->driver(curmatch).description, (cloneof == -1) ? 0 : MENU_FLAG_INVERT, (void *)&drivlist->driver(curmatch)); } } /* if we're forced into this, allow general input configuration as well */ if (ui_menu_is_force_game_select()) { ui_menu_item_append(menu, MENU_SEPARATOR_ITEM, NULL, 0, NULL); ui_menu_item_append(menu, "Configure General Inputs", NULL, 0, (void *)1); } /* configure the custom rendering */ ui_menu_set_custom_render(menu, menu_select_game_custom_render, ui_get_line_height(machine) + 3.0f * UI_BOX_TB_BORDER, 4.0f * ui_get_line_height(machine) + 3.0f * UI_BOX_TB_BORDER); } /*------------------------------------------------- menu_select_game_build_driver_list - build a list of available drivers -------------------------------------------------*/ static void menu_select_game_build_driver_list(ui_menu *menu, select_game_state *menustate) { // start with an empty list // hack alert: use new directly here to avoid reporting this one-time static memory as unfreed if (drivlist == NULL) drivlist = global_alloc(driver_enumerator(menu->machine().options())); drivlist->exclude_all(); /* open a path to the ROMs and find them in the array */ file_enumerator path(menu->machine().options().media_path()); const osd_directory_entry *dir; /* iterate while we get new objects */ while ((dir = path.next()) != NULL) { char drivername[50]; char *dst = drivername; const char *src; /* build a name for it */ for (src = dir->name; *src != 0 && *src != '.' && dst < &drivername[ARRAY_LENGTH(drivername) - 1]; src++) *dst++ = tolower((UINT8)*src); *dst = 0; int drivnum = drivlist->find(drivername); if (drivnum != -1) drivlist->include(drivnum); } /* now build the final list */ drivlist->reset(); int listnum = 0; while (drivlist->next()) menustate->driverlist[listnum++] = &drivlist->driver(); /* NULL-terminate */ menustate->driverlist[listnum] = NULL; } /*------------------------------------------------- menu_select_game_custom_render - perform our special rendering -------------------------------------------------*/ static void menu_select_game_custom_render(running_machine &machine, ui_menu *menu, void *state, void *selectedref, float top, float bottom, float origx1, float origy1, float origx2, float origy2) { select_game_state *menustate = (select_game_state *)state; const game_driver *driver; float width, maxwidth; float x1, y1, x2, y2; char tempbuf[4][256]; rgb_t color; int line; /* display the current typeahead */ if (menustate->search[0] != 0) sprintf(&tempbuf[0][0], "Type name or select: %s_", menustate->search); else sprintf(&tempbuf[0][0], "Type name or select: (random)"); /* get the size of the text */ ui_draw_text_full(menu->container, &tempbuf[0][0], 0.0f, 0.0f, 1.0f, JUSTIFY_CENTER, WRAP_TRUNCATE, DRAW_NONE, ARGB_WHITE, ARGB_BLACK, &width, NULL); width += 2 * UI_BOX_LR_BORDER; maxwidth = MAX(width, origx2 - origx1); /* compute our bounds */ x1 = 0.5f - 0.5f * maxwidth; x2 = x1 + maxwidth; y1 = origy1 - top; y2 = origy1 - UI_BOX_TB_BORDER; /* draw a box */ ui_draw_outlined_box(menu->container, x1, y1, x2, y2, UI_BACKGROUND_COLOR); /* take off the borders */ x1 += UI_BOX_LR_BORDER; x2 -= UI_BOX_LR_BORDER; y1 += UI_BOX_TB_BORDER; y2 -= UI_BOX_TB_BORDER; /* draw the text within it */ ui_draw_text_full(menu->container, &tempbuf[0][0], x1, y1, x2 - x1, JUSTIFY_CENTER, WRAP_TRUNCATE, DRAW_NORMAL, UI_TEXT_COLOR, UI_TEXT_BG_COLOR, NULL, NULL); /* determine the text to render below */ driver = ((FPTR)selectedref > 1) ? (const game_driver *)selectedref : NULL; if ((FPTR)driver > 1) { const char *gfxstat, *soundstat; /* first line is game name */ sprintf(&tempbuf[0][0], "%-.100s", driver->description); /* next line is year, manufacturer */ sprintf(&tempbuf[1][0], "%s, %-.100s", driver->year, driver->manufacturer); /* next line is overall driver status */ if (driver->flags & GAME_NOT_WORKING) { if (driver->flags & GAME_MECHANICAL) strcpy(&tempbuf[2][0], "Overall: Mechanical Dependency"); else strcpy(&tempbuf[2][0], "Overall: NOT WORKING"); } else if (driver->flags & GAME_UNEMULATED_PROTECTION) strcpy(&tempbuf[2][0], "Overall: Unemulated Protection"); else strcpy(&tempbuf[2][0], "Overall: Working"); /* next line is graphics, sound status */ if (driver->flags & (GAME_IMPERFECT_GRAPHICS | GAME_WRONG_COLORS | GAME_IMPERFECT_COLORS)) gfxstat = "Imperfect"; else gfxstat = "OK"; if (driver->flags & GAME_NO_SOUND) soundstat = "Unimplemented"; else if (driver->flags & GAME_IMPERFECT_SOUND) soundstat = "Imperfect"; else soundstat = "OK"; sprintf(&tempbuf[3][0], "Gfx: %s, Sound: %s", gfxstat, soundstat); } else { const char *s = COPYRIGHT; line = 0; int col = 0; /* first line is version string */ sprintf(&tempbuf[line++][0], "%s %s", APPLONGNAME, build_version); /* output message */ while (line < ARRAY_LENGTH(tempbuf)) { if (*s == 0 || *s == '\n') { tempbuf[line++][col] = 0; col = 0; } else tempbuf[line][col++] = *s; if (*s != 0) s++; } } /* get the size of the text */ maxwidth = origx2 - origx1; for (line = 0; line < 4; line++) { ui_draw_text_full(menu->container, &tempbuf[line][0], 0.0f, 0.0f, 1.0f, JUSTIFY_CENTER, WRAP_TRUNCATE, DRAW_NONE, ARGB_WHITE, ARGB_BLACK, &width, NULL); width += 2 * UI_BOX_LR_BORDER; maxwidth = MAX(maxwidth, width); } /* compute our bounds */ x1 = 0.5f - 0.5f * maxwidth; x2 = x1 + maxwidth; y1 = origy2 + UI_BOX_TB_BORDER; y2 = origy2 + bottom; /* draw a box */ color = UI_BACKGROUND_COLOR; if (driver != NULL) color = UI_GREEN_COLOR; if (driver != NULL && (driver->flags & (GAME_IMPERFECT_GRAPHICS | GAME_WRONG_COLORS | GAME_IMPERFECT_COLORS | GAME_NO_SOUND | GAME_IMPERFECT_SOUND)) != 0) color = UI_YELLOW_COLOR; if (driver != NULL && (driver->flags & (GAME_NOT_WORKING | GAME_UNEMULATED_PROTECTION)) != 0) color = UI_RED_COLOR; ui_draw_outlined_box(menu->container, x1, y1, x2, y2, color); /* take off the borders */ x1 += UI_BOX_LR_BORDER; x2 -= UI_BOX_LR_BORDER; y1 += UI_BOX_TB_BORDER; y2 -= UI_BOX_TB_BORDER; /* draw all lines */ for (line = 0; line < 4; line++) { ui_draw_text_full(menu->container, &tempbuf[line][0], x1, y1, x2 - x1, JUSTIFY_CENTER, WRAP_TRUNCATE, DRAW_NORMAL, UI_TEXT_COLOR, UI_TEXT_BG_COLOR, NULL, NULL); y1 += ui_get_line_height(machine); } } /*************************************************************************** MENU HELPERS ***************************************************************************/ /*------------------------------------------------- menu_render_triangle - render a triangle that is used for up/down arrows and left/right indicators -------------------------------------------------*/ static void menu_render_triangle(bitmap_t &dest, const bitmap_t &source, const rectangle &sbounds, void *param) { int halfwidth = dest.width / 2; int height = dest.height; int x, y; /* start with all-transparent */ bitmap_fill(&dest, NULL, MAKE_ARGB(0x00,0x00,0x00,0x00)); /* render from the tip to the bottom */ for (y = 0; y < height; y++) { int linewidth = (y * (halfwidth - 1) + (height / 2)) * 255 * 2 / height; UINT32 *target = BITMAP_ADDR32(&dest, y, halfwidth); /* don't antialias if height < 12 */ if (dest.height < 12) { int pixels = (linewidth + 254) / 255; if (pixels % 2 == 0) pixels++; linewidth = pixels * 255; } /* loop while we still have data to generate */ for (x = 0; linewidth > 0; x++) { int dalpha; /* first colum we only consume one pixel */ if (x == 0) { dalpha = MIN(0xff, linewidth); target[x] = MAKE_ARGB(dalpha,0xff,0xff,0xff); } /* remaining columns consume two pixels, one on each side */ else { dalpha = MIN(0x1fe, linewidth); target[x] = target[-x] = MAKE_ARGB(dalpha/2,0xff,0xff,0xff); } /* account for the weight we consumed */ linewidth -= dalpha; } } } | ||||
Relationships
There are no relationship linked to this issue. |
Notes
3
No.07730
Fujix Administrator
Aug 24, 2011, 15:09
|
??? Why did you post your source update here ??? Why do you use the bug report form for your personal source update? How come? Why don't you post it to mamedev.org? |
---|---|
No.07731
NekoEd Senior Tester
Aug 24, 2011, 15:47
edited on: Aug 24, 2011, 16:19 |
Woah, calm down. I've been busy lately, hundreds of thoughts going through my mind, please excuse me if I forgot; it was certainly not my intent to upset or annoy you or anyone else. I know MAME Testers is an important project, but if you're getting stressed, take a little breather. That's how I came up with my update here, in fact. As for future updates, I'll remember to submit them directly to mamedev,org from now on. |
No.07741
Haze Senior Tester
Aug 28, 2011, 15:56
|
I'm not sure I really see the point. In the case of the Fruit Machines 'Mechanical' isn't the sticking point, they're not really any more mechanical than a force feedback steering wheel, and I think all hell would break loose if we started tagging all the games with FFB wheels or moving cabinets as mechanical. The whole warning system needs a revamp anyway, the current wording is NOT always clear, because it makes it sound like the only real problem in many cases is the sound / graphics rather than the sound / graphics being just part of the problem. The games will require artwork to be fully playable once the drivers are complete, but there is already a flag for that (although the warning doesn't currently go away if external art is found / used) Overall, I wouldn't care too much, NOT WORKING is not working, anybody really curious as to why can read the driver notes. |