diff options
Diffstat (limited to 'src/emu/rendlay.cpp')
-rw-r--r-- | src/emu/rendlay.cpp | 321 |
1 files changed, 231 insertions, 90 deletions
diff --git a/src/emu/rendlay.cpp b/src/emu/rendlay.cpp index 4db27e47762..1d4a8f23265 100644 --- a/src/emu/rendlay.cpp +++ b/src/emu/rendlay.cpp @@ -518,6 +518,39 @@ private: return expand(str, str + strlen(str)); } + int parse_int(char const *begin, char const *end, int defvalue) + { + std::istringstream stream; + stream.imbue(f_portable_locale); + int result; + if (begin[0] == '$') + { + stream.str(std::string(begin + 1, end)); + unsigned uvalue; + stream >> std::hex >> uvalue; + result = int(uvalue); + } + else if ((begin[0] == '0') && ((begin[1] == 'x') || (begin[1] == 'X'))) + { + stream.str(std::string(begin + 2, end)); + unsigned uvalue; + stream >> std::hex >> uvalue; + result = int(uvalue); + } + else if (begin[0] == '#') + { + stream.str(std::string(begin + 1, end)); + stream >> result; + } + else + { + stream.str(std::string(begin, end)); + stream >> result; + } + + return stream ? result : defvalue; + } + std::string parameter_name(util::xml::data_node const &node) { char const *const attrib(node.get_attribute_string("name", nullptr)); @@ -547,8 +580,15 @@ private: bool m_cached = false; public: - explicit layout_environment(device_t &device) : m_device(device) { } - explicit layout_environment(layout_environment &next) : m_device(next.m_device), m_next(&next) { } + explicit layout_environment(device_t &device) + : m_device(device) + { + } + explicit layout_environment(layout_environment &next) + : m_device(next.m_device) + , m_next(&next) + { + } layout_environment(layout_environment const &) = delete; device_t &device() { return m_device; } @@ -706,35 +746,7 @@ public: // similar to what XML nodes do std::pair<char const *, char const *> const expanded(expand(attrib)); - std::istringstream stream; - stream.imbue(f_portable_locale); - int result; - if (expanded.first[0] == '$') - { - stream.str(std::string(expanded.first + 1, expanded.second)); - unsigned uvalue; - stream >> std::hex >> uvalue; - result = int(uvalue); - } - else if ((expanded.first[0] == '0') && ((expanded.first[1] == 'x') || (expanded.first[1] == 'X'))) - { - stream.str(std::string(expanded.first + 2, expanded.second)); - unsigned uvalue; - stream >> std::hex >> uvalue; - result = int(uvalue); - } - else if (expanded.first[0] == '#') - { - stream.str(std::string(expanded.first + 1, expanded.second)); - stream >> result; - } - else - { - stream.str(std::string(expanded.first, expanded.second)); - stream >> result; - } - - return stream ? result : defvalue; + return parse_int(expanded.first, expanded.second, defvalue); } float get_attribute_float(util::xml::data_node const &node, char const *name, float defvalue) @@ -751,6 +763,23 @@ public: return (stream >> result) ? result : defvalue; } + bool get_attribute_bool(util::xml::data_node const &node, char const *name, bool defvalue) + { + char const *const attrib(node.get_attribute_string(name, nullptr)); + if (!attrib) + return defvalue; + + // first try yes/no strings + std::pair<char const *, char const *> const expanded(expand(attrib)); + if (!std::strcmp("yes", expanded.first) || !std::strcmp("true", expanded.first)) + return true; + if (!std::strcmp("no", expanded.first) || !std::strcmp("false", expanded.first)) + return false; + + // fall back to integer parsing + return parse_int(expanded.first, expanded.second, defvalue ? 1 : 0) != 0; + } + void parse_bounds(util::xml::data_node const *node, render_bounds &result) { // default to unit rectangle @@ -826,16 +855,50 @@ public: case 270: result = ROT270; break; default: throw layout_syntax_error(util::string_format("invalid rotate attribute %d", rotate)); } - if (!std::strcmp("yes", get_attribute_string(*node, "swapxy", "no"))) + if (get_attribute_bool(*node, "swapxy", false)) result ^= ORIENTATION_SWAP_XY; - if (!std::strcmp("yes", get_attribute_string(*node, "flipx", "no"))) + if (get_attribute_bool(*node, "flipx", false)) result ^= ORIENTATION_FLIP_X; - if (!std::strcmp("yes", get_attribute_string(*node, "flipy", "no"))) + if (get_attribute_bool(*node, "flipy", false)) result ^= ORIENTATION_FLIP_Y; return result; } }; + +class view_environment : public layout_environment +{ +private: + view_environment *const m_next_view = nullptr; + char const *const m_name; + u32 const m_visibility_mask = 0U; + unsigned m_next_visibility_bit = 0U; + +public: + view_environment(layout_environment &next, char const *name) + : layout_environment(next) + , m_name(name) + { + } + view_environment(view_environment &next, bool visibility) + : layout_environment(next) + , m_next_view(&next) + , m_name(next.m_name) + , m_visibility_mask(next.m_visibility_mask | (u32(visibility ? 1 : 0) << next.m_next_visibility_bit)) + , m_next_visibility_bit(next.m_next_visibility_bit + (visibility ? 1 : 0)) + { + if (32U < m_next_visibility_bit) + throw layout_syntax_error(util::string_format("view '%s' contains too many visibility toggles", m_name)); + } + ~view_environment() + { + if (m_next_view) + m_next_view->m_next_visibility_bit = m_next_visibility_bit; + } + + u32 visibility_mask() const { return m_visibility_mask; } +}; + } } } // namespace emu::render::detail @@ -1157,6 +1220,13 @@ void layout_group::resolve_bounds( local.increment_parameters(); } } + else if (!strcmp(itemnode->get_name(), "vistoggle")) + { + if (!env.get_attribute_string(*itemnode, "name", nullptr)) + throw layout_syntax_error("vistoggle must have name attribute"); + environment local(env); + resolve_bounds(local, *itemnode, groupmap, seen, empty, false, true); + } else { throw layout_syntax_error(util::string_format("unknown group element %s", itemnode->get_name())); @@ -2955,23 +3025,70 @@ struct layout_view::layer_lists { item_list backdrops, screens, overlays, bezels //------------------------------------------------- layout_view::layout_view( - environment &env, + layout_environment &env, util::xml::data_node const &viewnode, element_map &elemmap, group_map &groupmap) : m_name(make_name(env, viewnode)) - , m_aspect(1.0f) - , m_scraspect(1.0f) + , m_effaspect(1.0f) , m_items() , m_has_art(false) + , m_defvismask(0U) { // parse the layout m_expbounds.x0 = m_expbounds.y0 = m_expbounds.x1 = m_expbounds.y1 = 0; - environment local(env); + view_environment local(env, m_name.c_str()); layer_lists layers; local.set_parameter("viewname", std::string(m_name)); add_items(layers, local, viewnode, elemmap, groupmap, ROT0, identity_transform, render_color{ 1.0F, 1.0F, 1.0F, 1.0F }, true, false, true); + // can't support legacy layers and modern visibility toggles at the same time + if (!m_vistoggles.empty() && (!layers.backdrops.empty() || !layers.overlays.empty() || !layers.bezels.empty() || !layers.cpanels.empty() || !layers.marquees.empty())) + throw layout_syntax_error("view contains visibility toggles as well as legacy backdrop, overlay, bezel, cpanel and/or marquee elements"); + + // create visibility toggles for legacy layers + u32 mask(1U); + if (!layers.backdrops.empty()) + { + m_vistoggles.emplace_back("Backdrops", mask); + for (item &backdrop : layers.backdrops) + backdrop.m_visibility_mask = mask; + m_defvismask |= mask; + mask <<= 1; + } + if (!layers.overlays.empty()) + { + m_vistoggles.emplace_back("Overlays", mask); + for (item &overlay : layers.overlays) + overlay.m_visibility_mask = mask; + m_defvismask |= mask; + mask <<= 1; + } + if (!layers.bezels.empty()) + { + m_vistoggles.emplace_back("Bezels", mask); + for (item &bezel : layers.bezels) + bezel.m_visibility_mask = mask; + m_defvismask |= mask; + mask <<= 1; + } + if (!layers.cpanels.empty()) + { + m_vistoggles.emplace_back("Control Panels", mask); + for (item &cpanel : layers.cpanels) + cpanel.m_visibility_mask = mask; + m_defvismask |= mask; + mask <<= 1; + } + if (!layers.marquees.empty()) + { + m_vistoggles.emplace_back("Backdrops", mask); + for (item &marquee : layers.marquees) + marquee.m_visibility_mask = mask; + m_defvismask |= mask; + mask <<= 1; + } + // deal with legacy element groupings if (!layers.overlays.empty() || (layers.backdrops.size() <= 1)) { @@ -3002,7 +3119,7 @@ layout_view::layout_view( } // calculate metrics - recompute(render_layer_config()); + recompute(default_visibility_mask(), false); for (group_map::value_type &group : groupmap) group.second.set_bounds_unresolved(); } @@ -3024,7 +3141,7 @@ layout_view::~layout_view() bool layout_view::has_screen(screen_device &screen) const { - return std::find_if(m_screens.begin(), m_screens.end(), [&screen](auto const &scr) { return &scr.get() == &screen; }) != m_screens.end(); + return std::find_if(m_screens.begin(), m_screens.end(), [&screen] (auto const &scr) { return &scr.get() == &screen; }) != m_screens.end(); } @@ -3033,36 +3150,39 @@ bool layout_view::has_screen(screen_device &screen) const // ratio of a view and all of its contained items //------------------------------------------------- -void layout_view::recompute(render_layer_config layerconfig) +void layout_view::recompute(u32 visibility_mask, bool zoom_to_screen) { // reset the bounds - m_bounds.x0 = m_bounds.y0 = m_bounds.x1 = m_bounds.y1 = 0.0f; - m_scrbounds.x0 = m_scrbounds.y0 = m_scrbounds.x1 = m_scrbounds.y1 = 0.0f; + render_bounds scrbounds{ 0.0f, 0.0f, 0.0f, 0.0f }; + m_bounds = scrbounds; m_screens.clear(); - // loop over all layers + // loop over items and filter by visibility mask bool first = true; bool scrfirst = true; for (item &curitem : m_items) { - // accumulate bounds - if (first) - m_bounds = curitem.m_rawbounds; - else - union_render_bounds(m_bounds, curitem.m_rawbounds); - first = false; - - // accumulate screen bounds - if (curitem.m_screen) + if ((visibility_mask & curitem.visibility_mask()) == curitem.visibility_mask()) { - if (scrfirst) - m_scrbounds = curitem.m_rawbounds; + // accumulate bounds + if (first) + m_bounds = curitem.m_rawbounds; else - union_render_bounds(m_scrbounds, curitem.m_rawbounds); - scrfirst = false; + union_render_bounds(m_bounds, curitem.m_rawbounds); + first = false; - // accumulate the screens in use while we're scanning - m_screens.emplace_back(*curitem.m_screen); + // accumulate visible screens and their bounds bounds + if (curitem.m_screen) + { + if (scrfirst) + scrbounds = curitem.m_rawbounds; + else + union_render_bounds(scrbounds, curitem.m_rawbounds); + scrfirst = false; + + // accumulate the screens in use while we're scanning + m_screens.emplace_back(*curitem.m_screen); + } } } @@ -3070,36 +3190,29 @@ void layout_view::recompute(render_layer_config layerconfig) if (m_expbounds.x1 > m_expbounds.x0) m_bounds = m_expbounds; - // if we're handling things normally, the target bounds are (0,0)-(1,1) render_bounds target_bounds; - if (!layerconfig.zoom_to_screen() || m_screens.empty()) + if (!zoom_to_screen || scrfirst) { - // compute the aspect ratio of the view - m_aspect = (m_bounds.x1 - m_bounds.x0) / (m_bounds.y1 - m_bounds.y0); - + // if we're handling things normally, the target bounds are (0,0)-(1,1) + m_effaspect = ((m_bounds.x1 > m_bounds.x0) && (m_bounds.y1 > m_bounds.y0)) ? m_bounds.aspect() : 1.0f; target_bounds.x0 = target_bounds.y0 = 0.0f; target_bounds.x1 = target_bounds.y1 = 1.0f; } - - // if we're cropping, we want the screen area to fill (0,0)-(1,1) else { - // compute the aspect ratio of the screen - m_scraspect = (m_scrbounds.x1 - m_scrbounds.x0) / (m_scrbounds.y1 - m_scrbounds.y0); - - float targwidth = (m_bounds.x1 - m_bounds.x0) / (m_scrbounds.x1 - m_scrbounds.x0); - float targheight = (m_bounds.y1 - m_bounds.y0) / (m_scrbounds.y1 - m_scrbounds.y0); - target_bounds.x0 = (m_bounds.x0 - m_scrbounds.x0) / (m_bounds.x1 - m_bounds.x0) * targwidth; - target_bounds.y0 = (m_bounds.y0 - m_scrbounds.y0) / (m_bounds.y1 - m_bounds.y0) * targheight; - target_bounds.x1 = target_bounds.x0 + targwidth; - target_bounds.y1 = target_bounds.y0 + targheight; + // if we're cropping, we want the screen area to fill (0,0)-(1,1) + m_effaspect = ((scrbounds.x1 > scrbounds.x0) && (scrbounds.y1 > scrbounds.y0)) ? scrbounds.aspect() : 1.0f; + target_bounds.x0 = (m_bounds.x0 - scrbounds.x0) / scrbounds.width(); + target_bounds.y0 = (m_bounds.y0 - scrbounds.y0) / scrbounds.height(); + target_bounds.x1 = target_bounds.x0 + (m_bounds.width() / scrbounds.width()); + target_bounds.y1 = target_bounds.y0 + (m_bounds.height() / scrbounds.height()); } // determine the scale/offset for normalization - float xoffs = m_bounds.x0; - float yoffs = m_bounds.y0; - float xscale = (target_bounds.x1 - target_bounds.x0) / (m_bounds.x1 - m_bounds.x0); - float yscale = (target_bounds.y1 - target_bounds.y0) / (m_bounds.y1 - m_bounds.y0); + float const xoffs = m_bounds.x0; + float const yoffs = m_bounds.y0; + float const xscale = (target_bounds.x1 - target_bounds.x0) / (m_bounds.x1 - m_bounds.x0); + float const yscale = (target_bounds.y1 - target_bounds.y0) / (m_bounds.y1 - m_bounds.y0); // normalize all the item bounds for (item &curitem : items()) @@ -3129,7 +3242,7 @@ void layout_view::resolve_tags() void layout_view::add_items( layer_lists &layers, - environment &env, + view_environment &env, util::xml::data_node const &parentnode, element_map &elemmap, group_map &groupmap, @@ -3202,7 +3315,7 @@ void layout_view::add_items( { char const *ref(env.get_attribute_string(*itemnode, "ref", nullptr)); if (!ref) - throw layout_syntax_error("nested group must have ref attribute"); + throw layout_syntax_error("group instantiation must have ref attribute"); group_map::iterator const found(groupmap.find(ref)); if (groupmap.end() == found) @@ -3225,7 +3338,7 @@ void layout_view::add_items( grouptrans = found->second.make_transform(grouporient, trans); } - environment local(env); + view_environment local(env, false); add_items( layers, local, @@ -3244,13 +3357,23 @@ void layout_view::add_items( int const count(env.get_attribute_int(*itemnode, "count", -1)); if (0 >= count) throw layout_syntax_error("repeat must have positive integer count attribute"); - environment local(env); + view_environment local(env, false); for (int i = 0; count > i; ++i) { add_items(layers, local, *itemnode, elemmap, groupmap, orientation, trans, color, false, true, !i); local.increment_parameters(); } } + else if (!strcmp(itemnode->get_name(), "vistoggle")) + { + char const *name(env.get_attribute_string(*itemnode, "name", nullptr)); + if (!name) + throw layout_syntax_error("vistoggle must have name attribute"); + m_defvismask |= u32(env.get_attribute_bool(*itemnode, "enabled", true) ? 1 : 0) << m_vistoggles.size(); // TODO: make this less hacky + view_environment local(env, true); + m_vistoggles.emplace_back(name, local.visibility_mask()); + add_items(layers, local, *itemnode, elemmap, groupmap, orientation, trans, color, false, false, true); + } else { throw layout_syntax_error(util::string_format("unknown view item %s", itemnode->get_name())); @@ -3264,7 +3387,7 @@ void layout_view::add_items( } } -std::string layout_view::make_name(environment &env, util::xml::data_node const &viewnode) +std::string layout_view::make_name(layout_environment &env, util::xml::data_node const &viewnode) { char const *const name(env.get_attribute_string(viewnode, "name", nullptr)); if (!name) @@ -3294,7 +3417,7 @@ std::string layout_view::make_name(environment &env, util::xml::data_node const //------------------------------------------------- layout_view::item::item( - environment &env, + view_environment &env, util::xml::data_node const &itemnode, element_map &elemmap, int orientation, @@ -3314,6 +3437,7 @@ layout_view::item::item( , m_rawbounds(make_bounds(env, itemnode, trans)) , m_color(render_color_multiply(env.parse_color(itemnode.get_child("color")), color)) , m_blend_mode(get_blend_mode(env, itemnode)) + , m_visibility_mask(env.visibility_mask()) { // fetch common data int index = env.get_attribute_int(itemnode, "index", -1); @@ -3435,7 +3559,7 @@ void layout_view::item::resolve_tags() // find_element - find element definition //--------------------------------------------- -layout_element *layout_view::item::find_element(environment &env, util::xml::data_node const &itemnode, element_map &elemmap) +layout_element *layout_view::item::find_element(view_environment &env, util::xml::data_node const &itemnode, element_map &elemmap) { char const *const name(env.get_attribute_string(itemnode, !strcmp(itemnode.get_name(), "element") ? "ref" : "element", nullptr)); if (!name) @@ -3455,7 +3579,7 @@ layout_element *layout_view::item::find_element(environment &env, util::xml::dat //--------------------------------------------- render_bounds layout_view::item::make_bounds( - environment &env, + view_environment &env, util::xml::data_node const &itemnode, layout_group::transform const &trans) { @@ -3474,7 +3598,7 @@ render_bounds layout_view::item::make_bounds( // make_input_tag - get absolute input tag //--------------------------------------------- -std::string layout_view::item::make_input_tag(environment &env, util::xml::data_node const &itemnode) +std::string layout_view::item::make_input_tag(view_environment &env, util::xml::data_node const &itemnode) { char const *tag(env.get_attribute_string(itemnode, "inputtag", nullptr)); return tag ? env.device().subtag(tag) : std::string(); @@ -3485,7 +3609,7 @@ std::string layout_view::item::make_input_tag(environment &env, util::xml::data_ // get_blend_mode - explicit or implicit blend //--------------------------------------------- -int layout_view::item::get_blend_mode(environment &env, util::xml::data_node const &itemnode) +int layout_view::item::get_blend_mode(view_environment &env, util::xml::data_node const &itemnode) { // see if there's a blend mode attribute char const *const mode(env.get_attribute_string(itemnode, "blend", nullptr)); @@ -3515,6 +3639,23 @@ int layout_view::item::get_blend_mode(environment &env, util::xml::data_node con //************************************************************************** +// LAYOUT VIEW VISIBILITY TOGGLE +//************************************************************************** + +//------------------------------------------------- +// visibility_toggle - constructor +//------------------------------------------------- + +layout_view::visibility_toggle::visibility_toggle(std::string &&name, u32 mask) + : m_name(std::move(name)) + , m_mask(mask) +{ + assert(mask); +} + + + +//************************************************************************** // LAYOUT FILE //************************************************************************** @@ -3548,7 +3689,7 @@ layout_file::layout_file(device_t &device, util::xml::data_node const &rootnode, for (util::xml::data_node const *viewnode = mamelayoutnode->get_child("view"); viewnode != nullptr; viewnode = viewnode->get_next_sibling("view")) { // the trouble with allowing errors to propagate here is that it wreaks havoc with screenless systems that use a terminal by default - // e.g. intlc44 and intlc440 have a terminal on the tty port by default and have a view with the front panel with the terminal screen + // e.g. intlc44 and intlc440 have a terminal on the TTY port by default and have a view with the front panel with the terminal screen // however, they have a second view with just the front panel which is very useful if you're using e.g. -tty null_modem with a socket // if the error is allowed to propagate, the entire layout is dropped so you can't select the useful view try |