summaryrefslogtreecommitdiffstats
path: root/src/emu/render.cpp
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/render.cpp
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/render.cpp')
-rw-r--r--src/emu/render.cpp316
1 files changed, 196 insertions, 120 deletions
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();
}