summaryrefslogtreecommitdiffstatshomepage
path: root/src/emu
diff options
context:
space:
mode:
author Vas Crabb <vas@vastheman.com>2020-09-06 01:41:44 +1000
committer Vas Crabb <vas@vastheman.com>2020-09-06 01:41:44 +1000
commit94601c77cc5b645a7738d634fb6e668dfd98d6db (patch)
treefeaddeac4cbb07e66f29cd3fd08c508a2a10339a /src/emu
parent080826aac441562e168fbd82eaa6bc27d2bd19a2 (diff)
-util/xmlfile: Escape attribute and element content.
The previous behaviour was unintuitive - parsing an XML file and writing it out immediately would produce invalid XML if the file contained any characters that needed escaping. It makes far more sense to escape on writing rather than expecting the user to escape input. -Add preliminary support for visibility toggles to artwork system. This allows the user to show/hide related elements in a view, with nesting. The view can specify whether elements are shown or hidden by default. Settings are saved per host window/screen per view. There is no way to set the initial visibility state on the command line. Legacy "Space Invaders cabinet model" layers are mapped onto visibility toggles. This is not stable yet. In particular, the XML element/attribute names have not been finalised. The new features have not been added to complay.py to prevent them from being used before they're finalised.
Diffstat (limited to 'src/emu')
-rw-r--r--src/emu/debug/debugcpu.cpp2
-rw-r--r--src/emu/render.cpp316
-rw-r--r--src/emu/render.h111
-rw-r--r--src/emu/rendlay.cpp321
-rw-r--r--src/emu/video.cpp2
5 files changed, 503 insertions, 249 deletions
diff --git a/src/emu/debug/debugcpu.cpp b/src/emu/debug/debugcpu.cpp
index 6dacf7acee1..c3dd9a0c3a6 100644
--- a/src/emu/debug/debugcpu.cpp
+++ b/src/emu/debug/debugcpu.cpp
@@ -1490,7 +1490,7 @@ bool device_debug::comment_export(util::xml::data_node &curnode)
// iterate through the comments
for (const auto & elem : m_comment_set)
{
- util::xml::data_node *datanode = curnode.add_child("comment", util::xml::normalize_string(elem.m_text.c_str()));
+ util::xml::data_node *datanode = curnode.add_child("comment", elem.m_text.c_str());
if (datanode == nullptr)
return false;
datanode->set_attribute_int("address", elem.m_address);
diff --git a/src/emu/render.cpp b/src/emu/render.cpp
index fe62b3c0bf8..cc94bc053da 100644
--- a/src/emu/render.cpp
+++ b/src/emu/render.cpp
@@ -37,20 +37,22 @@
***************************************************************************/
#include "emu.h"
-#include "emuopts.h"
#include "render.h"
+
+#include "emuopts.h"
#include "rendfont.h"
#include "rendlay.h"
#include "rendutil.h"
#include "config.h"
#include "drivenum.h"
-#include "xmlfile.h"
+
#include "ui/uimain.h"
+#include "xmlfile.h"
+
#include <zlib.h>
#include <algorithm>
-#include <functional>
@@ -893,7 +895,7 @@ render_target::render_target(render_manager &manager, util::xml::data_node const
template <typename T> render_target::render_target(render_manager &manager, T &&layout, u32 flags, constructor_impl_t)
: m_next(nullptr)
, m_manager(manager)
- , m_curview(nullptr)
+ , m_curview(0U)
, m_flags(flags)
, m_listindex(0)
, m_width(640)
@@ -949,6 +951,10 @@ template <typename T> render_target::render_target(render_manager &manager, T &&
// load the layout files
load_layout_files(std::forward<T>(layout), flags & RENDER_CREATE_SINGLE_FILE);
+ for (layout_file &file : m_filelist)
+ for (layout_view &view : file.views())
+ if (!(m_flags & RENDER_CREATE_NO_ART) || !view.has_art())
+ m_views.emplace_back(view, view.default_visibility_mask());
// set the current view to the first one
set_view(0);
@@ -1010,13 +1016,12 @@ void render_target::set_bounds(s32 width, s32 height, float pixel_aspect)
// a target
//-------------------------------------------------
-void render_target::set_view(int viewindex)
+void render_target::set_view(unsigned viewindex)
{
- layout_view *view = view_by_index(viewindex);
- if (view)
+ if (m_views.size() > viewindex)
{
- m_curview = view;
- view->recompute(m_layerconfig);
+ m_curview = viewindex;
+ current_view().recompute(visibility_mask(), m_layerconfig.zoom_to_screen());
}
}
@@ -1034,38 +1039,54 @@ void render_target::set_max_texture_size(int maxwidth, int maxheight)
//-------------------------------------------------
+// set_visibility_toggle - show or hide selected
+// parts of a view
+//-------------------------------------------------
+
+void render_target::set_visibility_toggle(unsigned index, bool enable)
+{
+ assert(visibility_toggles().size() > index);
+ if (enable)
+ m_views[m_curview].second |= u32(1) << index;
+ else
+ m_views[m_curview].second &= ~(u32(1) << index);
+ current_view().recompute(visibility_mask(), m_layerconfig.zoom_to_screen());
+}
+
+
+//-------------------------------------------------
// configured_view - select a view for this
// target based on the configuration parameters
//-------------------------------------------------
-int render_target::configured_view(const char *viewname, int targetindex, int numtargets)
+unsigned render_target::configured_view(const char *viewname, int targetindex, int numtargets)
{
layout_view *view = nullptr;
- int viewindex;
// auto view just selects the nth view
if (strcmp(viewname, "auto") != 0)
{
// scan for a matching view name
size_t viewlen = strlen(viewname);
- for (view = view_by_index(viewindex = 0); view != nullptr; view = view_by_index(++viewindex))
- if (core_strnicmp(view->name().c_str(), viewname, viewlen) == 0)
- break;
+ for (unsigned i = 0; !view && (m_views.size() > i); ++i)
+ if (!core_strnicmp(m_views[i].first.get().name().c_str(), viewname, viewlen))
+ view = &m_views[i].first.get();
}
// if we don't have a match, default to the nth view
- screen_device_iterator iter(m_manager.machine().root_device());
- int scrcount = iter.count();
- if (view == nullptr && scrcount > 0)
+ std::vector<std::reference_wrapper<screen_device> > screens;
+ for (screen_device &screen : screen_device_iterator(m_manager.machine().root_device()))
+ screens.push_back(screen);
+ if (!view && !screens.empty())
{
// if we have enough targets to be one per screen, assign in order
- if (numtargets >= scrcount)
+ if (numtargets >= screens.size())
{
- int ourindex = index() % scrcount;
- screen_device *screen = iter.byindex(ourindex);
- assert(screen != nullptr);
+ int const ourindex = index() % screens.size();
+ screen_device &screen = screens[ourindex];
// find the first view with this screen and this screen only
+ unsigned viewindex;
for (view = view_by_index(viewindex = 0); view != nullptr; view = view_by_index(++viewindex))
{
auto const &viewscreens = view->screens();
@@ -1074,36 +1095,34 @@ int render_target::configured_view(const char *viewname, int targetindex, int nu
view = nullptr;
break;
}
- else if (std::find_if(viewscreens.begin(), viewscreens.end(), [&screen](auto const &scr) { return &scr.get() != screen; }) == viewscreens.end())
+ else if (std::find_if(viewscreens.begin(), viewscreens.end(), [&screen] (auto const &scr) { return &scr.get() != &screen; }) == viewscreens.end())
break;
}
}
// otherwise, find the first view that has all the screens
- if (view == nullptr)
+ if (!view)
{
- for (view = view_by_index(viewindex = 0); view != nullptr; view = view_by_index(++viewindex))
+ for (unsigned i = 0; !view && (m_views.size() > i); ++i)
{
- if (view->screen_count() >= scrcount)
+ view = &m_views[i].first.get();
+ if (view->screen_count() >= screens.size())
{
- bool screen_missing(false);
- for (screen_device &screen : iter)
+ for (screen_device &screen : screens)
{
if (!view->has_screen(screen))
{
- screen_missing = true;
+ view = nullptr;
break;
}
}
- if (!screen_missing)
- break;
}
}
}
}
// make sure it's a valid view
- return (view != nullptr) ? view_index(*view) : 0;
+ return view ? view_index(*view) : 0;
}
@@ -1111,10 +1130,14 @@ int render_target::configured_view(const char *viewname, int targetindex, int nu
// view_name - return the name of the given view
//-------------------------------------------------
-const char *render_target::view_name(int viewindex)
+const char *render_target::view_name(unsigned viewindex)
+{
+ return (m_views.size() > viewindex) ? m_views[viewindex].first.get().name().c_str() : nullptr;
+}
+
+layout_view::visibility_toggle_vector const &render_target::visibility_toggles()
{
- layout_view const *const view = view_by_index(viewindex);
- return view ? view->name().c_str() : nullptr;
+ return current_view().visibility_toggles();
}
@@ -1137,7 +1160,7 @@ void render_target::compute_visible_area(s32 target_width, s32 target_height, fl
if (m_keepaspect)
{
// start with the aspect ratio of the square pixel layout
- width = m_curview->effective_aspect(m_layerconfig);
+ width = current_view().effective_aspect();
height = 1.0f;
// first apply target orientation
@@ -1173,7 +1196,7 @@ void render_target::compute_visible_area(s32 target_width, s32 target_height, fl
// get source size and aspect
s32 src_width, src_height;
compute_minimum_size(src_width, src_height);
- float src_aspect = m_curview->effective_aspect(m_layerconfig);
+ float src_aspect = current_view().effective_aspect();
// apply orientation if required
if (target_orientation & ORIENTATION_SWAP_XY)
@@ -1242,11 +1265,11 @@ void render_target::compute_minimum_size(s32 &minwidth, s32 &minheight)
return;
}
- if (!m_curview)
+ if (m_views.empty())
throw emu_fatalerror("Mandatory artwork is missing");
// scan the current view for all screens
- for (layout_view::item &curitem : m_curview->items())
+ for (layout_view::item &curitem : current_view().items())
{
if (curitem.screen())
{
@@ -1301,8 +1324,8 @@ void render_target::compute_minimum_size(s32 &minwidth, s32 &minheight)
render_primitive_list &render_target::get_primitives()
{
// remember the base values if this is the first frame
- if (m_base_view == nullptr)
- m_base_view = m_curview;
+ if (!m_base_view)
+ m_base_view = &current_view();
// switch to the next primitive list
render_primitive_list &list = m_primlist[m_listindex];
@@ -1326,38 +1349,42 @@ render_primitive_list &render_target::get_primitives()
root_xform.orientation = m_orientation;
root_xform.no_center = false;
- // iterate over items in the view, but only if we're running
if (m_manager.machine().phase() >= machine_phase::RESET)
- for (layout_view::item &curitem : m_curview->items())
+ {
+ // we're running - iterate over items in the view
+ for (layout_view::item &curitem : current_view().items())
{
- // first apply orientation to the bounds
- render_bounds bounds = curitem.bounds();
- apply_orientation(bounds, root_xform.orientation);
- normalize_bounds(bounds);
-
- // apply the transform to the item
- object_transform item_xform;
- item_xform.xoffs = root_xform.xoffs + bounds.x0 * root_xform.xscale;
- item_xform.yoffs = root_xform.yoffs + bounds.y0 * root_xform.yscale;
- item_xform.xscale = (bounds.x1 - bounds.x0) * root_xform.xscale;
- item_xform.yscale = (bounds.y1 - bounds.y0) * root_xform.yscale;
- item_xform.color.r = curitem.color().r * root_xform.color.r;
- item_xform.color.g = curitem.color().g * root_xform.color.g;
- item_xform.color.b = curitem.color().b * root_xform.color.b;
- item_xform.color.a = curitem.color().a * root_xform.color.a;
- item_xform.orientation = orientation_add(curitem.orientation(), root_xform.orientation);
- item_xform.no_center = false;
-
- // if there is no associated element, it must be a screen element
- if (curitem.screen() != nullptr)
- add_container_primitives(list, root_xform, item_xform, curitem.screen()->container(), curitem.blend_mode());
- else
- add_element_primitives(list, item_xform, *curitem.element(), curitem.state(), curitem.blend_mode());
+ if ((visibility_mask() & curitem.visibility_mask()) == curitem.visibility_mask())
+ {
+ // first apply orientation to the bounds
+ render_bounds bounds = curitem.bounds();
+ apply_orientation(bounds, root_xform.orientation);
+ normalize_bounds(bounds);
+
+ // apply the transform to the item
+ object_transform item_xform;
+ item_xform.xoffs = root_xform.xoffs + bounds.x0 * root_xform.xscale;
+ item_xform.yoffs = root_xform.yoffs + bounds.y0 * root_xform.yscale;
+ item_xform.xscale = (bounds.x1 - bounds.x0) * root_xform.xscale;
+ item_xform.yscale = (bounds.y1 - bounds.y0) * root_xform.yscale;
+ item_xform.color.r = curitem.color().r * root_xform.color.r;
+ item_xform.color.g = curitem.color().g * root_xform.color.g;
+ item_xform.color.b = curitem.color().b * root_xform.color.b;
+ item_xform.color.a = curitem.color().a * root_xform.color.a;
+ item_xform.orientation = orientation_add(curitem.orientation(), root_xform.orientation);
+ item_xform.no_center = false;
+
+ // if there is no associated element, it must be a screen element
+ if (curitem.screen())
+ add_container_primitives(list, root_xform, item_xform, curitem.screen()->container(), curitem.blend_mode());
+ else
+ add_element_primitives(list, item_xform, *curitem.element(), curitem.state(), curitem.blend_mode());
+ }
}
-
- // if we are not in the running stage, draw an outer box
+ }
else
{
+ // if we are not in the running stage, draw an outer box
render_primitive *prim = list.alloc(render_primitive::QUAD);
set_render_bounds_xy(prim->bounds, 0.0f, 0.0f, (float)m_width, (float)m_height);
prim->full_bounds = prim->bounds;
@@ -1520,7 +1547,7 @@ void render_target::resolve_tags()
void render_target::update_layer_config()
{
- m_curview->recompute(m_layerconfig);
+ current_view().recompute(visibility_mask(), m_layerconfig.zoom_to_screen());
}
@@ -1694,10 +1721,9 @@ void render_target::load_additional_layout_files(const char *basename, bool have
throw emu_fatalerror("Couldn't create XML node??");
viewnode->set_attribute(
"name",
- util::xml::normalize_string(
- util::string_format(
- "Screen %1$u Standard (%2$u:%3$u)",
- i, screens[i].physical_x(), screens[i].physical_y()).c_str()));
+ util::string_format(
+ "Screen %1$u Standard (%2$u:%3$u)",
+ i, screens[i].physical_x(), screens[i].physical_y()).c_str());
util::xml::data_node *const screennode(viewnode->add_child("screen", nullptr));
if (!screennode)
throw emu_fatalerror("Couldn't create XML node??");
@@ -1721,10 +1747,9 @@ void render_target::load_additional_layout_files(const char *basename, bool have
throw emu_fatalerror("Couldn't create XML node??");
viewnode->set_attribute(
"name",
- util::xml::normalize_string(
- util::string_format(
- "Screen %1$u Pixel Aspect (%2$u:%3$u)",
- i, screens[i].native_x(), screens[i].native_y()).c_str()));
+ util::string_format(
+ "Screen %1$u Pixel Aspect (%2$u:%3$u)",
+ i, screens[i].native_x(), screens[i].native_y()).c_str());
util::xml::data_node *const screennode(viewnode->add_child("screen", nullptr));
if (!screennode)
throw emu_fatalerror("Couldn't create XML node??");
@@ -1841,7 +1866,7 @@ void render_target::load_additional_layout_files(const char *basename, bool have
util::xml::data_node *viewnode = layoutnode->add_child("view", nullptr);
if (!viewnode)
throw emu_fatalerror("Couldn't create XML node??");
- viewnode->set_attribute("name", util::xml::normalize_string(title));
+ viewnode->set_attribute("name", title);
float ypos(0.0F);
for (unsigned y = 0U; rows > y; ypos += heights[y] + spacing, ++y)
{
@@ -2479,8 +2504,11 @@ bool render_target::map_point_internal(s32 target_x, s32 target_y, render_contai
}
// iterate over items in the view
- for (layout_view::item &item : m_curview->items())
+ for (layout_view::item &item : current_view().items())
{
+ if ((visibility_mask() & item.visibility_mask()) != item.visibility_mask())
+ continue;
+
bool checkit;
// if we're looking for a particular container, verify that we have the right one
@@ -2511,15 +2539,9 @@ bool render_target::map_point_internal(s32 target_x, s32 target_y, render_contai
// view, or nullptr if it doesn't exist
//-------------------------------------------------
-layout_view *render_target::view_by_index(int index)
+layout_view *render_target::view_by_index(unsigned index)
{
- // scan the list of views within each layout, skipping those that don't apply
- for (layout_file &file : m_filelist)
- for (layout_view &view : file.views())
- if (!(m_flags & RENDER_CREATE_NO_ART) || !view.has_art())
- if (index-- == 0)
- return &view;
- return nullptr;
+ return (m_views.size() > index) ? &m_views[index].first.get() : nullptr;
}
@@ -2552,8 +2574,6 @@ int render_target::view_index(layout_view &targetview) const
void render_target::config_load(util::xml::data_node const &targetnode)
{
- int tmpint;
-
// find the view
const char *viewname = targetnode.get_attribute_string("view", nullptr);
if (viewname != nullptr)
@@ -2570,23 +2590,23 @@ void render_target::config_load(util::xml::data_node const &targetnode)
}
// modify the artwork config
- tmpint = targetnode.get_attribute_int("zoom", -1);
- if (tmpint == 0 || tmpint == 1)
- set_zoom_to_screen(tmpint);
+ int const zoom = targetnode.get_attribute_int("zoom", -1);
+ if (zoom == 0 || zoom == 1)
+ set_zoom_to_screen(zoom);
// apply orientation
- tmpint = targetnode.get_attribute_int("rotate", -1);
- if (tmpint != -1)
- {
- if (tmpint == 90)
- tmpint = ROT90;
- else if (tmpint == 180)
- tmpint = ROT180;
- else if (tmpint == 270)
- tmpint = ROT270;
+ int rotate = targetnode.get_attribute_int("rotate", -1);
+ if (rotate != -1)
+ {
+ if (rotate == 90)
+ rotate = ROT90;
+ else if (rotate == 180)
+ rotate = ROT180;
+ else if (rotate == 270)
+ rotate = ROT270;
else
- tmpint = ROT0;
- set_orientation(orientation_add(tmpint, orientation()));
+ rotate = ROT0;
+ set_orientation(orientation_add(rotate, orientation()));
// apply the opposite orientation to the UI
if (is_ui_target())
@@ -2595,10 +2615,43 @@ void render_target::config_load(util::xml::data_node const &targetnode)
render_container &ui_container = m_manager.ui_container();
ui_container.get_user_settings(settings);
- settings.m_orientation = orientation_add(orientation_reverse(tmpint), settings.m_orientation);
+ settings.m_orientation = orientation_add(orientation_reverse(rotate), settings.m_orientation);
ui_container.set_user_settings(settings);
}
}
+
+ // apply per-view settings
+ for (util::xml::data_node const *viewnode = targetnode.get_child("view"); viewnode; viewnode = viewnode->get_next_sibling("view"))
+ {
+ char const *const viewname = viewnode->get_attribute_string("name", nullptr);
+ if (!viewname)
+ continue;
+
+ auto const view = std::find_if(m_views.begin(), m_views.end(), [viewname] (auto const &x) { return x.first.get().name() == viewname; });
+ if (m_views.end() == view)
+ continue;
+
+ for (util::xml::data_node const *vistogglenode = viewnode->get_child("vistoggle"); vistogglenode; vistogglenode = vistogglenode->get_next_sibling("vistoggle"))
+ {
+ char const *const vistogglename = vistogglenode->get_attribute_string("name", nullptr);
+ if (!vistogglename)
+ continue;
+
+ auto const &vistoggles = view->first.get().visibility_toggles();
+ auto const vistoggle = std::find_if(vistoggles.begin(), vistoggles.end(), [vistogglename] (auto const &x) { return x.name() == vistogglename; });
+ if (vistoggles.end() == vistoggle)
+ continue;
+
+ int const enable = vistogglenode->get_attribute_int("enabled", -1);
+ if (0 <= enable)
+ {
+ if (enable)
+ view->second |= u32(1) << std::distance(vistoggles.begin(), vistoggle);
+ else
+ view->second &= ~(u32(1) << std::distance(vistoggles.begin(), vistoggle));
+ }
+ }
+ }
}
@@ -2615,9 +2668,9 @@ bool render_target::config_save(util::xml::data_node &targetnode)
targetnode.set_attribute_int("index", index());
// output the view
- if (m_curview != m_base_view)
+ if (&current_view() != m_base_view)
{
- targetnode.set_attribute("view", m_curview->name().c_str());
+ targetnode.set_attribute("view", current_view().name().c_str());
changed = true;
}
@@ -2643,6 +2696,33 @@ bool render_target::config_save(util::xml::data_node &targetnode)
changed = true;
}
+ // output layer configuration
+ for (auto const &view : m_views)
+ {
+ u32 const defvismask = view.first.get().default_visibility_mask();
+ if (defvismask != view.second)
+ {
+ util::xml::data_node *viewnode = nullptr;
+ unsigned i = 0;
+ for (layout_view::visibility_toggle const &toggle : view.first.get().visibility_toggles())
+ {
+ if (BIT(defvismask, i) != BIT(view.second, i))
+ {
+ if (!viewnode)
+ {
+ viewnode = targetnode.add_child("view", nullptr);
+ viewnode->set_attribute("name", view.first.get().name().c_str());
+ }
+ util::xml::data_node *const vistogglenode = viewnode->add_child("vistoggle", nullptr);
+ vistogglenode->set_attribute("name", toggle.name().c_str());
+ vistogglenode->set_attribute_int("enabled", BIT(view.second, i));
+ changed = true;
+ }
+ ++i;
+ }
+ }
+ }
+
return changed;
}
@@ -2968,8 +3048,8 @@ bool render_manager::is_live(screen_device &screen) const
{
if (!target.hidden())
{
- layout_view const *view = target.current_view();
- if (view != nullptr && view->has_screen(screen))
+ layout_view const *view = &target.current_view();
+ if (view->has_screen(screen))
return true;
}
}
@@ -3201,35 +3281,31 @@ void render_manager::container_free(render_container *container)
void render_manager::config_load(config_type cfg_type, util::xml::data_node const *parentnode)
{
- // we only care about game files
- if (cfg_type != config_type::GAME)
- return;
-
- // might not have any data
- if (parentnode == nullptr)
+ // we only care about game files with matching nodes
+ if ((cfg_type != config_type::GAME) || !parentnode)
return;
// check the UI target
util::xml::data_node const *const uinode = parentnode->get_child("interface");
- if (uinode != nullptr)
+ if (uinode)
{
- render_target *target = target_by_index(uinode->get_attribute_int("target", 0));
- if (target != nullptr)
+ render_target *const target = target_by_index(uinode->get_attribute_int("target", 0));
+ if (target)
set_ui_target(*target);
}
// iterate over target nodes
for (util::xml::data_node const *targetnode = parentnode->get_child("target"); targetnode; targetnode = targetnode->get_next_sibling("target"))
{
- render_target *target = target_by_index(targetnode->get_attribute_int("index", -1));
- if (target != nullptr)
+ render_target *const target = target_by_index(targetnode->get_attribute_int("index", -1));
+ if (target)
target->config_load(*targetnode);
}
// iterate over screen nodes
for (util::xml::data_node const *screennode = parentnode->get_child("screen"); screennode; screennode = screennode->get_next_sibling("screen"))
{
- int index = screennode->get_attribute_int("index", -1);
+ int const index = screennode->get_attribute_int("index", -1);
render_container *container = m_screen_container_list.find(index);
render_container::user_settings settings;
@@ -3283,7 +3359,7 @@ void render_manager::config_save(config_type cfg_type, util::xml::data_node *par
// create a node
util::xml::data_node *const targetnode = parentnode->add_child("target", nullptr);
- if (targetnode != nullptr && !target->config_save(*targetnode))
+ if (targetnode && !target->config_save(*targetnode))
targetnode->delete_node();
}
diff --git a/src/emu/render.h b/src/emu/render.h
index 0a59d7d894e..d846dce58c1 100644
--- a/src/emu/render.h
+++ b/src/emu/render.h
@@ -48,17 +48,24 @@
#include "screen.h"
-#include <cmath>
#include <array>
+#include <cmath>
+#include <functional>
#include <map>
#include <memory>
#include <mutex>
#include <string>
#include <unordered_map>
+#include <utility>
#include <vector>
-namespace emu { namespace render { namespace detail { class layout_environment; } } }
+namespace emu { namespace render { namespace detail {
+
+class layout_environment;
+class view_environment;
+
+} } } // namespace emu::render::detail
//**************************************************************************
@@ -176,6 +183,7 @@ struct render_bounds
constexpr float width() const { return x1 - x0; }
constexpr float height() const { return y1 - y0; }
+ constexpr float aspect() const { return width() / height(); }
};
@@ -725,20 +733,17 @@ private:
class layout_view
{
public:
- using environment = emu::render::detail::layout_environment;
+ using layout_environment = emu::render::detail::layout_environment;
+ using view_environment = emu::render::detail::view_environment;
using element_map = std::unordered_map<std::string, layout_element>;
using group_map = std::unordered_map<std::string, layout_group>;
using render_screen_list = std::list<std::reference_wrapper<screen_device>>;
- /// \brief A single backdrop/screen/overlay/bezel/cpanel/marquee item
+ /// \brief A single item in a view
///
- /// Each view has four lists of view_items, one for each "layer."
- /// Each view item is specified using floating point coordinates in
- /// arbitrary units, and is assumed to have square pixels. Each
- /// view item can control its orientation independently. Each item
- /// can also have an optional name, and can be set at runtime into
- /// different "states", which control how the embedded elements are
- /// displayed.
+ /// Each view has a list of item structures describing the visual
+ /// elements to draw, where they are located, additional blending
+ /// modes, and bindings for inputs and outputs.
class item
{
friend class layout_view;
@@ -746,7 +751,7 @@ public:
public:
// construction/destruction
item(
- environment &env,
+ view_environment &env,
util::xml::data_node const &itemnode,
element_map &elemmap,
int orientation,
@@ -760,6 +765,7 @@ public:
const render_bounds &bounds() const { return m_bounds; }
const render_color &color() const { return m_color; }
int blend_mode() const { return m_blend_mode; }
+ u32 visibility_mask() const { return m_visibility_mask; }
int orientation() const { return m_orientation; }
render_container *screen_container(running_machine &machine) const;
bool has_input() const { return bool(m_input_port); }
@@ -775,10 +781,10 @@ public:
void set_blend_mode(int mode) { m_blend_mode = mode; }
private:
- static layout_element *find_element(environment &env, util::xml::data_node const &itemnode, element_map &elemmap);
- static render_bounds make_bounds(environment &env, util::xml::data_node const &itemnode, layout_group::transform const &trans);
- static std::string make_input_tag(environment &env, util::xml::data_node const &itemnode);
- static int get_blend_mode(environment &env, util::xml::data_node const &itemnode);
+ static layout_element *find_element(view_environment &env, util::xml::data_node const &itemnode, element_map &elemmap);
+ static render_bounds make_bounds(view_environment &env, util::xml::data_node const &itemnode, layout_group::transform const &trans);
+ static std::string make_input_tag(view_environment &env, util::xml::data_node const &itemnode);
+ static int get_blend_mode(view_environment &env, util::xml::data_node const &itemnode);
// internal state
layout_element *const m_element; // pointer to the associated element (non-screens only)
@@ -796,12 +802,37 @@ public:
render_bounds const m_rawbounds; // raw (original) bounds of the item
render_color m_color; // color of the item
int m_blend_mode; // blending mode to use when drawing
+ u32 m_visibility_mask; // combined mask of parent visibility groups
};
using item_list = std::list<item>;
+ /// \brief A subset of items in a view that can be hidden or shown
+ ///
+ /// Visibility toggles allow the user to show or hide selected parts
+ /// of a view.
+ class visibility_toggle
+ {
+ public:
+ // construction/destruction/assignment
+ visibility_toggle(std::string &&name, u32 mask);
+ visibility_toggle(visibility_toggle const &) = default;
+ visibility_toggle(visibility_toggle &&) = default;
+ visibility_toggle &operator=(visibility_toggle const &) = default;
+ visibility_toggle &operator=(visibility_toggle &&) = default;
+
+ // getters
+ std::string const &name() const { return m_name; }
+ u32 mask() const { return m_mask; }
+
+ private:
+ std::string m_name; // display name for the toggle
+ u32 m_mask; // toggle combination to show
+ };
+ using visibility_toggle_vector = std::vector<visibility_toggle>;
+
// construction/destruction
layout_view(
- environment &env,
+ layout_environment &env,
util::xml::data_node const &viewnode,
element_map &elemmap,
group_map &groupmap);
@@ -810,18 +841,17 @@ public:
// getters
item_list &items() { return m_items; }
const std::string &name() const { return m_name; }
- const render_bounds &bounds() const { return m_bounds; }
- const render_bounds &screen_bounds() const { return m_scrbounds; }
const render_screen_list &screens() const { return m_screens; }
size_t screen_count() const { return m_screens.size(); }
+ float effective_aspect() const { return m_effaspect; }
+ const render_bounds &bounds() const { return m_bounds; }
bool has_screen(screen_device &screen) const;
-
- //
bool has_art() const { return m_has_art; }
- float effective_aspect(render_layer_config config) const { return (config.zoom_to_screen() && !m_screens.empty()) ? m_scraspect : m_aspect; }
+ u32 default_visibility_mask() const { return m_defvismask; }
+ const visibility_toggle_vector &visibility_toggles() const { return m_vistoggles; }
// operations
- void recompute(render_layer_config layerconfig);
+ void recompute(u32 visibility_mask, bool zoom_to_screens);
// resolve tags, if any
void resolve_tags();
@@ -832,7 +862,7 @@ private:
// add items, recursing for groups
void add_items(
layer_lists &layers,
- environment &env,
+ view_environment &env,
util::xml::data_node const &parentnode,
element_map &elemmap,
group_map &groupmap,
@@ -843,18 +873,18 @@ private:
bool repeat,
bool init);
- static std::string make_name(environment &env, util::xml::data_node const &viewnode);
+ static std::string make_name(layout_environment &env, util::xml::data_node const &viewnode);
// internal state
std::string m_name; // name of the layout
- float m_aspect; // X/Y of the layout
- float m_scraspect; // X/Y of the screen areas
- render_screen_list m_screens; // list of active screens
- render_bounds m_bounds; // computed bounds of the view
- render_bounds m_scrbounds; // computed bounds of the screens within the view
+ render_screen_list m_screens; // list screens visible in current configuration
+ float m_effaspect; // X/Y of the layout in current configuration
+ render_bounds m_bounds; // computed bounds of the view in current configuration
render_bounds m_expbounds; // explicit bounds of the view
item_list m_items; // list of layout items
bool m_has_art; // true if the layout contains non-screen elements
+ u32 m_defvismask; // default visibility mask
+ visibility_toggle_vector m_vistoggles;
};
@@ -920,8 +950,8 @@ public:
float max_update_rate() const { return m_max_refresh; }
int orientation() const { return m_orientation; }
render_layer_config layer_config() const { return m_layerconfig; }
- layout_view *current_view() const { return m_curview; }
- int view() const { return view_index(*m_curview); }
+ layout_view &current_view() const { return m_views[m_curview].first.get(); }
+ unsigned view() const { return m_curview; }
bool external_artwork() const { return m_external_artwork; }
bool hidden() const { return ((m_flags & RENDER_CREATE_HIDDEN) != 0); }
bool is_ui_target() const;
@@ -931,7 +961,7 @@ public:
void set_bounds(s32 width, s32 height, float pixel_aspect = 0);
void set_max_update_rate(float updates_per_second) { m_max_refresh = updates_per_second; }
void set_orientation(int orientation) { m_orientation = orientation; }
- void set_view(int viewindex);
+ void set_view(unsigned viewindex);
void set_max_texture_size(int maxwidth, int maxheight);
void set_transform_container(bool transform_container) { m_transform_container = transform_container; }
void set_keepaspect(bool keepaspect) { m_keepaspect = keepaspect; }
@@ -940,16 +970,19 @@ public:
// layer config getters
bool screen_overlay_enabled() const { return m_layerconfig.screen_overlay_enabled(); }
bool zoom_to_screen() const { return m_layerconfig.zoom_to_screen(); }
+ u32 visibility_mask() const { return m_views[m_curview].second; }
// layer config setters
+ void set_visibility_toggle(unsigned index, bool enable);
void set_screen_overlay_enabled(bool enable) { m_layerconfig.set_screen_overlay_enabled(enable); update_layer_config(); }
void set_zoom_to_screen(bool zoom) { m_layerconfig.set_zoom_to_screen(zoom); update_layer_config(); }
// view configuration helper
- int configured_view(const char *viewname, int targetindex, int numtargets);
+ unsigned configured_view(const char *viewname, int targetindex, int numtargets);
// view information
- const char *view_name(int viewindex);
+ char const *view_name(unsigned index);
+ layout_view::visibility_toggle_vector const &visibility_toggles();
// bounds computations
void compute_visible_area(s32 target_width, s32 target_height, float target_pixel_aspect, int target_orientation, s32 &visible_width, s32 &visible_height);
@@ -974,6 +1007,9 @@ public:
void resolve_tags();
private:
+ using view_mask_pair = std::pair<std::reference_wrapper<layout_view>, u32>;
+ using view_mask_vector = std::vector<view_mask_pair>;
+
// private classes declared in render.cpp
struct object_transform;
@@ -996,7 +1032,7 @@ private:
bool config_save(util::xml::data_node &targetnode);
// view lookups
- layout_view *view_by_index(int index);
+ layout_view *view_by_index(unsigned index);
int view_index(layout_view &view) const;
// optimized clearing
@@ -1012,8 +1048,9 @@ private:
// internal state
render_target * m_next; // link to next target
render_manager & m_manager; // reference to our owning manager
- layout_view * m_curview; // current view
std::list<layout_file> m_filelist; // list of layout files
+ view_mask_vector m_views; // views we consider
+ unsigned m_curview; // current view index
u32 m_flags; // creation flags
render_primitive_list m_primlist[NUM_PRIMLISTS]; // list of primitives
int m_listindex; // index of next primlist to use
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
diff --git a/src/emu/video.cpp b/src/emu/video.cpp
index e14ad211f23..e28e6aa1bae 100644
--- a/src/emu/video.cpp
+++ b/src/emu/video.cpp
@@ -138,7 +138,7 @@ video_manager::video_manager(running_machine &machine)
util::xml::data_node *const viewnode(layoutnode->add_child("view", nullptr));
if (!viewnode)
throw emu_fatalerror("Couldn't create XML node??");
- viewnode->set_attribute("name", util::xml::normalize_string(util::string_format("s%1$u", i).c_str()));
+ viewnode->set_attribute("name", util::string_format("s%1$u", i).c_str());
util::xml::data_node *const screennode(viewnode->add_child("screen", nullptr));
if (!screennode)
throw emu_fatalerror("Couldn't create XML node??");