summaryrefslogtreecommitdiffstatshomepage
path: root/src/emu/render.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/emu/render.cpp')
-rw-r--r--src/emu/render.cpp1680
1 files changed, 1032 insertions, 648 deletions
diff --git a/src/emu/render.cpp b/src/emu/render.cpp
index 58d2c2961c9..545827262bb 100644
--- a/src/emu/render.cpp
+++ b/src/emu/render.cpp
@@ -2,7 +2,7 @@
// copyright-holders:Aaron Giles
/***************************************************************************
- render.c
+ render.cpp
Core rendering system.
@@ -37,20 +37,27 @@
***************************************************************************/
#include "emu.h"
-#include "emuopts.h"
#include "render.h"
+
+#include "corestr.h"
+#include "emuopts.h"
+#include "fileio.h"
#include "rendfont.h"
#include "rendlay.h"
#include "rendutil.h"
#include "config.h"
#include "drivenum.h"
-#include "xmlfile.h"
+#include "layout/generic.h"
+
#include "ui/uimain.h"
-#include <zlib.h>
+#include "util/ioprocsfilter.h"
+#include "util/language.h"
+#include "util/path.h"
+#include "util/xmlfile.h"
#include <algorithm>
-#include <functional>
+#include <limits>
@@ -93,6 +100,40 @@ struct render_target::object_transform
};
+struct render_target::pointer_info
+{
+ pointer_info() noexcept
+ : type(osd::ui_event_handler::pointer::UNKNOWN)
+ , oldpos(std::numeric_limits<float>::min(), std::numeric_limits<float>::min())
+ , newpos(std::numeric_limits<float>::min(), std::numeric_limits<float>::min())
+ , oldbuttons(0U)
+ , newbuttons(0U)
+ , edges(0U, 0U)
+ {
+ }
+
+ osd::ui_event_handler::pointer type;
+ std::pair<float, float> oldpos;
+ std::pair<float, float> newpos;
+ u32 oldbuttons, newbuttons;
+ std::pair<size_t, size_t> edges;
+};
+
+
+struct render_target::hit_test
+{
+ hit_test() noexcept
+ : inbounds(0U, 0U)
+ , hit(0U)
+ {
+ }
+
+ std::pair<u64, u64> inbounds;
+ u64 hit;
+};
+
+
+
//**************************************************************************
// GLOBAL VARIABLES
@@ -295,7 +336,8 @@ render_texture::render_texture()
m_curseq(0)
{
m_sbounds.set(0, -1, 0, -1);
- memset(m_scaled, 0, sizeof(m_scaled));
+ for (auto &elem : m_scaled)
+ elem.seqid = 0;
}
@@ -335,11 +377,10 @@ void render_texture::reset(render_manager &manager, texture_scaler_func scaler,
void render_texture::release()
{
// free all scaled versions
- for (auto & elem : m_scaled)
+ for (auto &elem : m_scaled)
{
- m_manager->invalidate_all(elem.bitmap);
- global_free(elem.bitmap);
- elem.bitmap = nullptr;
+ m_manager->invalidate_all(elem.bitmap.get());
+ elem.bitmap.reset();
elem.seqid = 0;
}
@@ -348,6 +389,7 @@ void render_texture::release()
m_bitmap = nullptr;
m_sbounds.set(0, -1, 0, -1);
m_format = TEXFORMAT_ARGB32;
+ m_scaler = nullptr;
m_curseq = 0;
}
@@ -376,12 +418,9 @@ void render_texture::set_bitmap(bitmap_t &bitmap, const rectangle &sbounds, text
// invalidate all scaled versions
for (auto & elem : m_scaled)
{
- if (elem.bitmap != nullptr)
- {
- m_manager->invalidate_all(elem.bitmap);
- global_free(elem.bitmap);
- }
- elem.bitmap = nullptr;
+ if (elem.bitmap)
+ m_manager->invalidate_all(elem.bitmap.get());
+ elem.bitmap.reset();
elem.seqid = 0;
}
}
@@ -429,6 +468,7 @@ void render_texture::get_scaled(u32 dwidth, u32 dheight, render_texinfo &texinfo
texinfo.base = m_bitmap->raw_pixptr(m_sbounds.top(), m_sbounds.left());
texinfo.rowpixels = m_bitmap->rowpixels();
texinfo.width = swidth;
+ texinfo.width_margin = m_sbounds.left();
texinfo.height = sheight;
// palette will be set later
texinfo.seqid = ++m_curseq;
@@ -442,7 +482,7 @@ void render_texture::get_scaled(u32 dwidth, u32 dheight, render_texinfo &texinfo
// is it a size we already have?
scaled_texture *scaled = nullptr;
int scalenum;
- for (scalenum = 0; scalenum < ARRAY_LENGTH(m_scaled); scalenum++)
+ for (scalenum = 0; scalenum < std::size(m_scaled); scalenum++)
{
scaled = &m_scaled[scalenum];
@@ -452,27 +492,27 @@ void render_texture::get_scaled(u32 dwidth, u32 dheight, render_texinfo &texinfo
}
// did we get one?
- if (scalenum == ARRAY_LENGTH(m_scaled))
+ if (scalenum == std::size(m_scaled))
{
int lowest = -1;
// didn't find one -- take the entry with the lowest seqnum
- for (scalenum = 0; scalenum < ARRAY_LENGTH(m_scaled); scalenum++)
- if ((lowest == -1 || m_scaled[scalenum].seqid < m_scaled[lowest].seqid) && !primlist.has_reference(m_scaled[scalenum].bitmap))
+ for (scalenum = 0; scalenum < std::size(m_scaled); scalenum++)
+ if ((lowest == -1 || m_scaled[scalenum].seqid < m_scaled[lowest].seqid) && !primlist.has_reference(m_scaled[scalenum].bitmap.get()))
lowest = scalenum;
if (-1 == lowest)
throw emu_fatalerror("render_texture::get_scaled: Too many live texture instances!");
// throw out any existing entries
scaled = &m_scaled[lowest];
- if (scaled->bitmap != nullptr)
+ if (scaled->bitmap)
{
- m_manager->invalidate_all(scaled->bitmap);
- global_free(scaled->bitmap);
+ m_manager->invalidate_all(scaled->bitmap.get());
+ scaled->bitmap.reset();
}
// allocate a new bitmap
- scaled->bitmap = global_alloc(bitmap_argb32(dwidth, dheight));
+ scaled->bitmap = std::make_unique<bitmap_argb32>(dwidth, dheight);
scaled->seqid = ++m_curseq;
// let the scaler do the work
@@ -480,8 +520,8 @@ void render_texture::get_scaled(u32 dwidth, u32 dheight, render_texinfo &texinfo
}
// finally fill out the new info
- primlist.add_reference(scaled->bitmap);
- texinfo.base = &scaled->bitmap->pix32(0);
+ primlist.add_reference(scaled->bitmap.get());
+ texinfo.base = &scaled->bitmap->pix(0);
texinfo.rowpixels = scaled->bitmap->rowpixels();
texinfo.width = dwidth;
texinfo.height = dheight;
@@ -496,7 +536,7 @@ void render_texture::get_scaled(u32 dwidth, u32 dheight, render_texinfo &texinfo
// palette for a texture
//-------------------------------------------------
-const rgb_t *render_texture::get_adjusted_palette(render_container &container)
+const rgb_t *render_texture::get_adjusted_palette(render_container &container, u32 &out_length)
{
// override the palette with our adjusted palette
switch (m_format)
@@ -506,7 +546,7 @@ const rgb_t *render_texture::get_adjusted_palette(render_container &container)
assert(m_bitmap->palette() != nullptr);
// return our adjusted palette
- return container.bcg_lookup_table(m_format, m_bitmap->palette());
+ return container.bcg_lookup_table(m_format, out_length, m_bitmap->palette());
case TEXFORMAT_RGB32:
case TEXFORMAT_ARGB32:
@@ -515,7 +555,7 @@ const rgb_t *render_texture::get_adjusted_palette(render_container &container)
// if no adjustment necessary, return nullptr
if (!container.has_brightness_contrast_gamma_changes())
return nullptr;
- return container.bcg_lookup_table(m_format);
+ return container.bcg_lookup_table(m_format, out_length);
default:
assert(false);
@@ -535,8 +575,7 @@ const rgb_t *render_texture::get_adjusted_palette(render_container &container)
//-------------------------------------------------
render_container::render_container(render_manager &manager, screen_device *screen)
- : m_next(nullptr)
- , m_manager(manager)
+ : m_manager(manager)
, m_screen(screen)
, m_overlaybitmap(nullptr)
, m_overlaytexture(nullptr)
@@ -679,7 +718,7 @@ float render_container::apply_brightness_contrast_gamma_fp(float value)
// given texture mode
//-------------------------------------------------
-const rgb_t *render_container::bcg_lookup_table(int texformat, palette_t *palette)
+const rgb_t *render_container::bcg_lookup_table(int texformat, u32 &out_length, palette_t *palette)
{
switch (texformat)
{
@@ -690,15 +729,18 @@ const rgb_t *render_container::bcg_lookup_table(int texformat, palette_t *palett
m_bcglookup.resize(palette->max_index());
recompute_lookups();
}
- assert (palette == &m_palclient->palette());
+ assert(palette == &m_palclient->palette());
+ out_length = palette->max_index();
return &m_bcglookup[0];
case TEXFORMAT_RGB32:
case TEXFORMAT_ARGB32:
case TEXFORMAT_YUY16:
+ out_length = std::size(m_bcglookup256);
return m_bcglookup256;
default:
+ out_length = 0;
return nullptr;
}
}
@@ -713,8 +755,8 @@ void render_container::overlay_scale(bitmap_argb32 &dest, bitmap_argb32 &source,
// simply replicate the source bitmap over the target
for (int y = 0; y < dest.height(); y++)
{
- u32 *src = &source.pix32(y % source.height());
- u32 *dst = &dest.pix32(y);
+ u32 const *const src = &source.pix(y % source.height());
+ u32 *dst = &dest.pix(y);
int sx = 0;
// loop over columns
@@ -877,25 +919,31 @@ render_container::user_settings::user_settings()
// render_target - constructor
//-------------------------------------------------
-render_target::render_target(render_manager &manager, const internal_layout *layoutfile, u32 flags)
- : render_target(manager, layoutfile, flags, CONSTRUCTOR_IMPL)
+render_target::render_target(render_manager &manager, render_container *ui, const internal_layout *layoutfile, u32 flags)
+ : render_target(manager, ui, layoutfile, flags, CONSTRUCTOR_IMPL)
{
}
-render_target::render_target(render_manager &manager, util::xml::data_node const &layout, u32 flags)
- : render_target(manager, layout, flags, CONSTRUCTOR_IMPL)
+render_target::render_target(render_manager &manager, render_container *ui, util::xml::data_node const &layout, u32 flags)
+ : render_target(manager, ui, layout, flags, CONSTRUCTOR_IMPL)
{
}
-template <typename T> render_target::render_target(render_manager &manager, T &&layout, u32 flags, constructor_impl_t)
+template <typename T>
+render_target::render_target(render_manager &manager, render_container *ui, T &&layout, u32 flags, constructor_impl_t)
: m_next(nullptr)
, m_manager(manager)
- , m_curview(nullptr)
+ , m_ui_container(ui)
+ , m_curview(0U)
, m_flags(flags)
, m_listindex(0)
, m_width(640)
, m_height(480)
+ , m_keepaspect(false)
+ , m_int_overscan(false)
, m_pixel_aspect(0.0f)
+ , m_int_scale_x(0)
+ , m_int_scale_y(0)
, m_max_refresh(0)
, m_orientation(0)
, m_base_view(nullptr)
@@ -903,25 +951,31 @@ template <typename T> render_target::render_target(render_manager &manager, T &&
, m_maxtexwidth(65536)
, m_maxtexheight(65536)
, m_transform_container(true)
+ , m_external_artwork(false)
{
// determine the base layer configuration based on options
m_base_layerconfig.set_zoom_to_screen(manager.machine().options().artwork_crop());
// aspect and scale options
- m_keepaspect = (manager.machine().options().keep_aspect() && !(flags & RENDER_CREATE_HIDDEN));
- m_int_overscan = manager.machine().options().int_overscan();
- m_int_scale_x = manager.machine().options().int_scale_x();
- m_int_scale_y = manager.machine().options().int_scale_y();
- if (m_manager.machine().options().auto_stretch_xy())
- m_scale_mode = SCALE_FRACTIONAL_AUTO;
- else if (manager.machine().options().uneven_stretch_x())
- m_scale_mode = SCALE_FRACTIONAL_X;
- else if (manager.machine().options().uneven_stretch_y())
- m_scale_mode = SCALE_FRACTIONAL_Y;
- else if (manager.machine().options().uneven_stretch())
- m_scale_mode = SCALE_FRACTIONAL;
+ if (!(flags & RENDER_CREATE_HIDDEN))
+ {
+ m_keepaspect = manager.machine().options().keep_aspect();
+ m_int_overscan = manager.machine().options().int_overscan();
+ m_int_scale_x = manager.machine().options().int_scale_x();
+ m_int_scale_y = manager.machine().options().int_scale_y();
+ if (m_manager.machine().options().auto_stretch_xy())
+ m_scale_mode = SCALE_FRACTIONAL_AUTO;
+ else if (manager.machine().options().uneven_stretch_x())
+ m_scale_mode = SCALE_FRACTIONAL_X;
+ else if (manager.machine().options().uneven_stretch_y())
+ m_scale_mode = SCALE_FRACTIONAL_Y;
+ else if (manager.machine().options().uneven_stretch())
+ m_scale_mode = SCALE_FRACTIONAL;
+ else
+ m_scale_mode = SCALE_INTEGER;
+ }
else
- m_scale_mode = SCALE_INTEGER;
+ m_scale_mode = SCALE_FRACTIONAL;
// determine the base orientation based on options
if (!manager.machine().options().rotate())
@@ -945,6 +999,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);
@@ -995,9 +1053,9 @@ void render_target::set_bounds(s32 width, s32 height, float pixel_aspect)
m_width = width;
m_height = height;
m_bounds.x0 = m_bounds.y0 = 0;
- m_bounds.x1 = (float)width;
- m_bounds.y1 = (float)height;
- m_pixel_aspect = pixel_aspect != 0.0? pixel_aspect : 1.0;
+ m_bounds.x1 = float(width);
+ m_bounds.y1 = float(height);
+ m_pixel_aspect = pixel_aspect != 0.0F ? pixel_aspect : 1.0F;
}
@@ -1006,13 +1064,16 @@ 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);
+ forget_pointers();
+ m_curview = viewindex;
+ current_view().recompute(visibility_mask(), m_layerconfig.zoom_to_screen());
+ current_view().preload();
+ m_clickable_items.clear();
+ m_clickable_items.resize(current_view().interactive_items().size());
}
}
@@ -1030,100 +1091,317 @@ void render_target::set_max_texture_size(int maxwidth, int maxheight)
//-------------------------------------------------
-// configured_view - select a view for this
-// target based on the configuration parameters
+// pointer_updated - pointer activity within
+// target
+//-------------------------------------------------
+
+void render_target::pointer_updated(
+ osd::ui_event_handler::pointer type,
+ u16 ptrid,
+ u16 device,
+ s32 x,
+ s32 y,
+ u32 buttons,
+ u32 pressed,
+ u32 released,
+ s16 clicks)
+{
+ auto const target_f(map_point_layout(x, y));
+ current_view().pointer_updated(type, ptrid, device, target_f.first, target_f.second, buttons, pressed, released, clicks);
+
+ // 64 pointers ought to be enough for anyone
+ if (64 <= ptrid)
+ return;
+
+ // just store the updated pointer state
+ if (m_pointers.size() <= ptrid)
+ m_pointers.resize(ptrid + 1);
+ m_pointers[ptrid].type = type;
+ m_pointers[ptrid].newpos = target_f;
+ m_pointers[ptrid].newbuttons = buttons;
+}
+
+
+//-------------------------------------------------
+// pointer_left - pointer left target normally
//-------------------------------------------------
-int render_target::configured_view(const char *viewname, int targetindex, int numtargets)
+void render_target::pointer_left(
+ osd::ui_event_handler::pointer type,
+ u16 ptrid,
+ u16 device,
+ s32 x,
+ s32 y,
+ u32 released,
+ s16 clicks)
{
- layout_view *view = nullptr;
- int viewindex;
+ auto const target_f(map_point_layout(x, y));
+ current_view().pointer_left(type, ptrid, device, target_f.first, target_f.second, released, clicks);
- // auto view just selects the nth view
- if (strcmp(viewname, "auto") != 0)
+ // store the updated state if relevant
+ if (m_pointers.size() > ptrid)
{
- // 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;
+ m_pointers[ptrid].newpos = std::make_pair(std::numeric_limits<float>::min(), std::numeric_limits<float>::min());
+ m_pointers[ptrid].newbuttons = 0;
}
+}
- // 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)
+
+//-------------------------------------------------
+// pointer_aborted - pointer left target
+// abnormally
+//-------------------------------------------------
+
+void render_target::pointer_aborted(
+ osd::ui_event_handler::pointer type,
+ u16 ptrid,
+ u16 device,
+ s32 x,
+ s32 y,
+ u32 released,
+ s16 clicks)
+{
+ // let layout scripts handle pointer input
+ auto const target_f(map_point_layout(x, y));
+ current_view().pointer_aborted(type, ptrid, device, target_f.first, target_f.second, released, clicks);
+
+ // store the updated state if relevant
+ if (m_pointers.size() > ptrid)
{
- // if we have enough targets to be one per screen, assign in order
- if (numtargets >= scrcount)
+ m_pointers[ptrid].newpos = std::make_pair(std::numeric_limits<float>::min(), std::numeric_limits<float>::min());
+ m_pointers[ptrid].newbuttons = 0;
+ }
+}
+
+
+//-------------------------------------------------
+// forget_pointers - stop processing pointer
+// input
+//-------------------------------------------------
+
+void render_target::forget_pointers()
+{
+ current_view().forget_pointers();
+ m_pointers.clear();
+ for (size_t i = 0; m_clickable_items.size() > i; ++i)
+ {
+ if (m_clickable_items[i].hit)
{
- int ourindex = index() % scrcount;
- screen_device *screen = iter.byindex(ourindex);
+ layout_view_item const &item(current_view().interactive_items()[i]);
+ auto const [port, mask] = item.input_tag_and_mask();
+ ioport_field *const field(port ? port->field(mask) : nullptr);
+ if (field)
+ field->set_value(0);
+ }
+ m_clickable_items[i] = hit_test();
+ }
+}
- // find the first view with this screen and this screen only
- for (view = view_by_index(viewindex = 0); view != nullptr; view = view_by_index(++viewindex))
+
+//-------------------------------------------------
+// update_pointer_fields - update inputs for new
+// pointer state
+//-------------------------------------------------
+
+void render_target::update_pointer_fields()
+{
+ auto const &x_edges(current_view().interactive_edges_x());
+ auto const &y_edges(current_view().interactive_edges_y());
+
+ // update items bounds intersection checks for pointers
+ for (size_t ptr = 0; m_pointers.size() > ptr; ++ptr)
+ {
+ auto const [x, y] = m_pointers[ptr].newpos;
+ auto edges = m_pointers[ptr].edges;
+
+ // check for moving across horizontal edges
+ if (x < m_pointers[ptr].oldpos.first)
+ {
+ while (edges.first && (x < x_edges[edges.first - 1].position()))
{
- const render_screen_list &viewscreens = view->screens();
- if (viewscreens.count() == 0)
- {
- view = nullptr;
- break;
- }
- else if (viewscreens.count() == viewscreens.contains(*screen))
- break;
+ --edges.first;
+ auto const &edge(x_edges[edges.first]);
+ if (edge.trailing())
+ m_clickable_items[edge.index()].inbounds.first |= u64(1) << ptr;
+ else
+ m_clickable_items[edge.index()].inbounds.first &= ~(u64(1) << ptr);
+ }
+ }
+ else if (x > m_pointers[ptr].oldpos.first)
+ {
+ while ((x_edges.size() > edges.first) && (x >= x_edges[edges.first].position()))
+ {
+ auto const &edge(x_edges[edges.first]);
+ if (edge.trailing())
+ m_clickable_items[edge.index()].inbounds.first &= ~(u64(1) << ptr);
+ else
+ m_clickable_items[edge.index()].inbounds.first |= u64(1) << ptr;
+ ++edges.first;
}
}
- // otherwise, find the first view that has all the screens
- if (view == nullptr)
+ // check for moving across vertical edges
+ if (y < m_pointers[ptr].oldpos.second)
{
- for (view = view_by_index(viewindex = 0); view != nullptr; view = view_by_index(++viewindex))
+ while (edges.second && (y < y_edges[edges.second - 1].position()))
{
- render_screen_list const &viewscreens(view->screens());
- if (viewscreens.count() >= scrcount)
- {
- bool screen_missing(false);
- for (screen_device &screen : iter)
- {
- if (!viewscreens.contains(screen))
- {
- screen_missing = true;
- break;
- }
- }
- if (!screen_missing)
- break;
- }
+ --edges.second;
+ auto const &edge(y_edges[edges.second]);
+ if (edge.trailing())
+ m_clickable_items[edge.index()].inbounds.second |= u64(1) << ptr;
+ else
+ m_clickable_items[edge.index()].inbounds.second &= ~(u64(1) << ptr);
}
}
+ else if (y > m_pointers[ptr].oldpos.second)
+ {
+ while ((y_edges.size() > edges.second) && (y >= y_edges[edges.second].position()))
+ {
+ auto const &edge(y_edges[edges.second]);
+ if (edge.trailing())
+ m_clickable_items[edge.index()].inbounds.second &= ~(u64(1) << ptr);
+ else
+ m_clickable_items[edge.index()].inbounds.second |= u64(1) << ptr;
+ ++edges.second;
+ }
+ }
+
+ // update the pointer's state
+ m_pointers[ptr].oldpos = m_pointers[ptr].newpos;
+ m_pointers[ptr].oldbuttons = m_pointers[ptr].newbuttons;
+ m_pointers[ptr].edges = edges;
}
- // make sure it's a valid view
- return (view != nullptr) ? view_index(*view) : 0;
+ // update item hit states
+ u64 obscured(0U);
+ for (size_t i = 0; m_clickable_items.size() > i; ++i)
+ {
+ layout_view_item const &item(current_view().interactive_items()[i]);
+ u64 const inbounds(m_clickable_items[i].inbounds.first & m_clickable_items[i].inbounds.second);
+ u64 hit(m_clickable_items[i].hit);
+ for (unsigned ptr = 0; m_pointers.size() > ptr; ++ptr)
+ {
+ pointer_info const &pointer(m_pointers[ptr]);
+ bool const prefilter(BIT(~obscured & inbounds, ptr));
+ if (!prefilter || !BIT(pointer.newbuttons, 0) || !item.bounds().includes(pointer.newpos.first, pointer.newpos.second))
+ {
+ hit &= ~(u64(1) << ptr);
+ }
+ else
+ {
+ hit |= u64(1) << ptr;
+ if (!item.clickthrough())
+ obscured |= u64(1) << ptr;
+ }
+ }
+
+ // update field state
+ if (bool(hit) != bool(m_clickable_items[i].hit))
+ {
+ auto const [port, mask] = item.input_tag_and_mask();
+ ioport_field *const field(port ? port->field(mask) : nullptr);
+ if (field)
+ {
+ if (hit)
+ field->set_value(1);
+ else
+ field->clear_value();
+ }
+ }
+ m_clickable_items[i].hit = hit;
+ }
}
//-------------------------------------------------
-// view_name - return the name of the given view
+// set_visibility_toggle - show or hide selected
+// parts of a view
//-------------------------------------------------
-const char *render_target::view_name(int viewindex)
+void render_target::set_visibility_toggle(unsigned index, bool enable)
{
- layout_view const *const view = view_by_index(viewindex);
- return view ? view->name().c_str() : nullptr;
+ assert(current_view().visibility_toggles().size() > index);
+ if (enable)
+ m_views[m_curview].second |= u32(1) << index;
+ else
+ m_views[m_curview].second &= ~(u32(1) << index);
+ update_layer_config();
+ current_view().preload();
}
//-------------------------------------------------
-// render_target_get_view_screens - return a
-// bitmask of which screens are visible on a
-// given view
+// configured_view - select a view for this
+// target based on the configuration parameters
//-------------------------------------------------
-const render_screen_list &render_target::view_screens(int viewindex)
+unsigned render_target::configured_view(const char *viewname, int targetindex, int numtargets)
{
- layout_view *view = view_by_index(viewindex);
- return (view != nullptr) ? view->screens() : s_empty_screen_list;
+ // if it isn't "auto" or an empty string, try to match it as a view name prefix
+ if (viewname && *viewname && strcmp(viewname, "auto"))
+ {
+ // scan for a matching view name
+ size_t const viewlen = strlen(viewname);
+ for (unsigned i = 0; m_views.size() > i; ++i)
+ {
+ if (!core_strnicmp(m_views[i].first.name().c_str(), viewname, viewlen))
+ return i;
+ }
+ }
+
+ // if we don't have a match, default to the nth view
+ std::vector<std::reference_wrapper<screen_device> > screens;
+ for (screen_device &screen : screen_device_enumerator(m_manager.machine().root_device()))
+ screens.push_back(screen);
+ if (!screens.empty())
+ {
+ // if we have enough targets to be one per screen, assign in order
+ if (numtargets >= screens.size())
+ {
+ // find the first view with this screen and this screen only
+ layout_view *view = nullptr;
+ screen_device const &screen = screens[index() % screens.size()];
+ for (unsigned i = 0; !view && (m_views.size() > i); ++i)
+ {
+ for (layout_view_item &viewitem : m_views[i].first.items())
+ {
+ screen_device const *const viewscreen(viewitem.screen());
+ if (viewscreen == &screen)
+ {
+ view = &m_views[i].first;
+ }
+ else if (viewscreen)
+ {
+ view = nullptr;
+ break;
+ }
+ }
+ }
+ if (view)
+ return view_index(*view);
+ }
+
+ // otherwise, find the first view that has all the screens
+ for (unsigned i = 0; m_views.size() > i; ++i)
+ {
+ layout_view &curview = m_views[i].first;
+ if (std::find_if(screens.begin(), screens.end(), [&curview] (screen_device &screen) { return !curview.has_screen(screen); }) == screens.end())
+ return i;
+ }
+ }
+
+ // default to the first view
+ return 0;
+}
+
+
+//-------------------------------------------------
+// view_name - return the name of the given view
+//-------------------------------------------------
+
+const char *render_target::view_name(unsigned viewindex)
+{
+ return (m_views.size() > viewindex) ? m_views[viewindex].first.name().c_str() : nullptr;
}
@@ -1146,7 +1424,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
@@ -1182,49 +1460,107 @@ 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)
- src_aspect = 1.0 / src_aspect;
+ src_aspect = 1.0f / src_aspect;
- // get target aspect
- float target_aspect = (float)target_width / (float)target_height * target_pixel_aspect;
- bool target_is_portrait = (target_aspect < 1.0f);
+ // we need the ratio of target to source aspect
+ float aspect_ratio = m_keepaspect ? (float)target_width / (float)target_height * target_pixel_aspect / src_aspect : 1.0f;
+
+ // first compute (a, b) scale factors to fit the screen
+ float a = (float)target_width / src_width;
+ float b = (float)target_height / src_height;
// apply automatic axial stretching if required
int scale_mode = m_scale_mode;
- if (m_scale_mode == SCALE_FRACTIONAL_AUTO)
+ if (scale_mode == SCALE_FRACTIONAL_AUTO)
+ scale_mode = (m_manager.machine().system().flags & ORIENTATION_SWAP_XY) ^ (target_orientation & ORIENTATION_SWAP_XY) ?
+ SCALE_FRACTIONAL_Y : SCALE_FRACTIONAL_X;
+
+ // determine the scaling method for each axis
+ bool a_is_fract = (scale_mode == SCALE_FRACTIONAL_X || scale_mode == SCALE_FRACTIONAL);
+ bool b_is_fract = (scale_mode == SCALE_FRACTIONAL_Y || scale_mode == SCALE_FRACTIONAL);
+
+ // check if we have user defined scale factors, if so use them instead, but only on integer axes
+ int a_user = a_is_fract ? 0 : m_int_scale_x;
+ int b_user = b_is_fract ? 0 : m_int_scale_y;
+
+ // we allow overscan either explicitely or if integer scale factors are forced by user
+ bool int_overscan = m_int_overscan || (m_keepaspect && (a_user != 0 || b_user != 0));
+ float a_max = std::max(a, (float)a_user);
+ float b_max = std::max(b, (float)b_user);
+
+
+ // get the usable bounding box considering the type of scaling for each axis
+ float usable_aspect = (a_is_fract ? a : std::max(1.0f, floorf(a))) * src_width /
+ ((b_is_fract ? b : std::max(1.0f, floorf(b))) * src_height) * target_pixel_aspect;
+
+ // depending on the relative shape between target and source, let's define 'a' and 'b' so that:
+ // * a is the leader axis (first to hit a boundary)
+ // * b is the follower axis
+ if (usable_aspect > src_aspect)
{
- bool is_rotated = (m_manager.machine().system().flags & ORIENTATION_SWAP_XY) ^ (target_orientation & ORIENTATION_SWAP_XY);
- scale_mode = is_rotated ^ target_is_portrait ? SCALE_FRACTIONAL_Y : SCALE_FRACTIONAL_X;
+ std::swap(a, b);
+ std::swap(a_user, b_user);
+ std::swap(a_is_fract, b_is_fract);
+ std::swap(a_max, b_max);
+ aspect_ratio = 1.0f / aspect_ratio;
}
- // determine the scale mode for each axis
- bool x_is_integer = !((!target_is_portrait && scale_mode == SCALE_FRACTIONAL_X) || (target_is_portrait && scale_mode == SCALE_FRACTIONAL_Y));
- bool y_is_integer = !((target_is_portrait && scale_mode == SCALE_FRACTIONAL_X) || (!target_is_portrait && scale_mode == SCALE_FRACTIONAL_Y));
+ // now find an (a, b) pair that best fits our boundaries and scale options
+ float a_best = 1.0f, b_best = 1.0f;
+ float diff = 1000;
+
+ // fill (a0, a1) range
+ float u = a_user == 0 ? a : (float)a_user;
+ float a_range[] = {a_is_fract ? u : std::max(1.0f, floorf(u)), a_is_fract ? u : std::max(1.0f, roundf(u))};
+
+ for (float aa : a_range)
+ {
+ // apply aspect correction to 'b' axis if needed, considering resulting 'a' borders
+ float ba = b * (m_keepaspect ? aspect_ratio * (aa / a) : 1.0f);
+
+ // fill (b0, b1) range
+ float v = b_user == 0 ? ba : (float)b_user;
+ float b_range[] = {b_is_fract ? v : std::max(1.0f, floorf(v)), b_is_fract ? v : std::max(1.0f, roundf(v))};
- // first compute scale factors to fit the screen
- float xscale = (float)target_width / src_width;
- float yscale = (float)target_height / src_height;
- float maxxscale = std::max(1.0f, float(m_int_overscan ? render_round_nearest(xscale) : floor(xscale)));
- float maxyscale = std::max(1.0f, float(m_int_overscan ? render_round_nearest(yscale) : floor(yscale)));
+ for (float bb : b_range)
+ {
+ // we may need to propagate proportions back to 'a' axis
+ float ab = aa;
+ if (m_keepaspect && a_user == 0)
+ {
+ if (a_is_fract) ab *= (bb / ba);
+ else if (b_user != 0) ab = std::max(1.0f, roundf(ab * (bb / ba)));
+ }
- // now apply desired scale mode and aspect correction
- if (m_keepaspect && target_aspect > src_aspect) xscale *= src_aspect / target_aspect * (maxyscale / yscale);
- if (m_keepaspect && target_aspect < src_aspect) yscale *= target_aspect / src_aspect * (maxxscale / xscale);
- if (x_is_integer) xscale = std::min(maxxscale, std::max(1.0f, render_round_nearest(xscale)));
- if (y_is_integer) yscale = std::min(maxyscale, std::max(1.0f, render_round_nearest(yscale)));
+ // if overscan isn't allowed, discard values that exceed the usable bounding box, except a minimum of 1.0f
+ if (!int_overscan && ((ab > a_max && bb > 1.0f) || (bb > b_max && ab > 1.0f)))
+ continue;
+
+ // score the result
+ float new_diff = fabsf(aspect_ratio * (a / b) - (ab / bb));
+
+ if (new_diff <= diff)
+ {
+ diff = new_diff;
+ a_best = ab;
+ b_best = bb;
+ }
+ }
+ }
+ a = a_best;
+ b = b_best;
- // check if we have user defined scale factors, if so use them instead
- int user_scale_x = target_is_portrait? m_int_scale_y : m_int_scale_x;
- int user_scale_y = target_is_portrait? m_int_scale_x : m_int_scale_y;
- xscale = user_scale_x > 0 ? user_scale_x : xscale;
- yscale = user_scale_y > 0 ? user_scale_y : yscale;
+ // restore orientation
+ if (usable_aspect > src_aspect)
+ std::swap(a, b);
// set the final width/height
- visible_width = render_round_nearest(src_width * xscale);
- visible_height = render_round_nearest(src_height * yscale);
+ visible_width = render_round_nearest(src_width * a);
+ visible_height = render_round_nearest(src_height * b);
break;
}
}
@@ -1251,16 +1587,16 @@ 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())
+ screen_device const *const screen = curitem.screen();
+ if (screen)
{
// use a hard-coded default visible area for vector screens
- screen_device *const screen = curitem.screen();
const rectangle vectorvis(0, 639, 0, 479);
const rectangle &visarea = (screen->screen_type() == SCREEN_TYPE_VECTOR) ? vectorvis : screen->visible_area();
@@ -1309,13 +1645,9 @@ 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;
-
// switch to the next primitive list
render_primitive_list &list = m_primlist[m_listindex];
- m_listindex = (m_listindex + 1) % ARRAY_LENGTH(m_primlist);
+ m_listindex = (m_listindex + 1) % std::size(m_primlist);
list.acquire_lock();
// free any previous primitives
@@ -1335,9 +1667,11 @@ 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
+ current_view().prepare_items();
+ for (layout_view_item &curitem : current_view().visible_items())
{
// first apply orientation to the bounds
render_bounds bounds = curitem.bounds();
@@ -1350,27 +1684,24 @@ render_primitive_list &render_target::get_primitives()
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.color = curitem.color() * root_xform.color;
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)
+ 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());
+ add_element_primitives(list, item_xform, curitem);
}
-
- // 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->bounds.set_xy(0.0f, 0.0f, (float)m_width, (float)m_height);
prim->full_bounds = prim->bounds;
- set_render_color(&prim->color, 1.0f, 1.0f, 1.0f, 1.0f);
+ prim->color.set(1.0f, 0.1f, 0.1f, 0.1f);
prim->texture.base = nullptr;
prim->flags = PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA);
list.append(*prim);
@@ -1378,34 +1709,17 @@ render_primitive_list &render_target::get_primitives()
if (m_width > 1 && m_height > 1)
{
prim = list.alloc(render_primitive::QUAD);
- set_render_bounds_xy(prim->bounds, 1.0f, 1.0f, (float)(m_width - 1), (float)(m_height - 1));
+ prim->bounds.set_xy(1.0f, 1.0f, float(m_width - 1), float(m_height - 1));
prim->full_bounds = prim->bounds;
- set_render_color(&prim->color, 1.0f, 0.0f, 0.0f, 0.0f);
+ prim->color.set(1.0f, 0.0f, 0.0f, 0.0f);
prim->texture.base = nullptr;
prim->flags = PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA);
list.append(*prim);
}
}
- // process the debug containers
- for (render_container &debug : m_debug_containers)
- {
- object_transform ui_xform;
- ui_xform.xoffs = 0;
- ui_xform.yoffs = 0;
- ui_xform.xscale = (float)m_width;
- ui_xform.yscale = (float)m_height;
- ui_xform.color.r = ui_xform.color.g = ui_xform.color.b = 1.0f;
- ui_xform.color.a = 0.9f;
- ui_xform.orientation = m_orientation;
- ui_xform.no_center = true;
-
- // add UI elements
- add_container_primitives(list, root_xform, ui_xform, debug, BLENDMODE_ALPHA);
- }
-
- // process the UI if we are the UI target
- if (is_ui_target())
+ // process UI elements if applicable
+ if (m_ui_container)
{
// compute the transform for the UI
object_transform ui_xform;
@@ -1418,7 +1732,7 @@ render_primitive_list &render_target::get_primitives()
ui_xform.no_center = false;
// add UI elements
- add_container_primitives(list, root_xform, ui_xform, m_manager.ui_container(), BLENDMODE_ALPHA);
+ add_container_primitives(list, root_xform, ui_xform, *m_ui_container, BLENDMODE_ALPHA);
}
// optimize the list before handing it off
@@ -1436,21 +1750,45 @@ render_primitive_list &render_target::get_primitives()
bool render_target::map_point_container(s32 target_x, s32 target_y, render_container &container, float &container_x, float &container_y)
{
- ioport_port *input_port;
- ioport_value input_mask;
- return map_point_internal(target_x, target_y, &container, container_x, container_y, input_port, input_mask);
-}
+ std::pair<float, float> target_f(map_point_internal(target_x, target_y));
+ // explicitly check for the UI container
+ if (&container == m_ui_container)
+ {
+ // this hit test went against the UI container
+ container_x = float(target_x) / m_width;
+ container_y = float(target_y) / m_height;
+ return (target_f.first >= 0.0f) && (target_f.first < 1.0f) && (target_f.second >= 0.0f) && (target_f.second < 1.0f);
+ }
+ else
+ {
+ if (m_orientation & ORIENTATION_FLIP_X)
+ target_f.first = 1.0f - target_f.first;
+ if (m_orientation & ORIENTATION_FLIP_Y)
+ target_f.second = 1.0f - target_f.second;
+ if (m_orientation & ORIENTATION_SWAP_XY)
+ std::swap(target_f.first, target_f.second);
+
+ // try to find the right container
+ auto const &items(current_view().visible_screen_items());
+ auto const found(std::find_if(
+ items.begin(),
+ items.end(),
+ [&container] (layout_view_item &item) { return &item.screen()->container() == &container; }));
+ if (items.end() != found)
+ {
+ // point successfully mapped
+ layout_view_item &item(*found);
+ render_bounds const bounds(item.bounds());
+ container_x = (target_f.first - bounds.x0) / bounds.width();
+ container_y = (target_f.second - bounds.y0) / bounds.height();
+ return bounds.includes(target_f.first, target_f.second);
+ }
+ }
-//-------------------------------------------------
-// map_point_input - attempts to map a point on
-// the specified render_target to the specified
-// container, if possible
-//-------------------------------------------------
-
-bool render_target::map_point_input(s32 target_x, s32 target_y, ioport_port *&input_port, ioport_value &input_mask, float &input_x, float &input_y)
-{
- return map_point_internal(target_x, target_y, nullptr, input_x, input_y, input_port, input_mask);;
+ // default to point not mapped
+ container_x = container_y = -1.0f;
+ return false;
}
@@ -1475,50 +1813,16 @@ void render_target::invalidate_all(void *refptr)
//-------------------------------------------------
-// debug_alloc - allocate a container for a debug
-// view
-//-------------------------------------------------
-
-render_container *render_target::debug_alloc()
-{
- return &m_debug_containers.append(*m_manager.container_alloc());
-}
-
-
-//-------------------------------------------------
-// debug_free - free a container for a debug view
-//-------------------------------------------------
-
-void render_target::debug_free(render_container &container)
-{
- m_debug_containers.remove(container);
-}
-
-
-//-------------------------------------------------
-// debug_append - move a debug view container to
-// the end of the list
-//-------------------------------------------------
-
-void render_target::debug_append(render_container &container)
-{
- m_debug_containers.append(m_debug_containers.detach(container));
-}
-
-
-//-------------------------------------------------
// resolve_tags - resolve tag lookups
//-------------------------------------------------
void render_target::resolve_tags()
{
for (layout_file &file : m_filelist)
- {
- for (layout_view &view : file.views())
- {
- view.resolve_tags();
- }
- }
+ file.resolve_tags();
+
+ update_layer_config();
+ current_view().preload();
}
@@ -1529,7 +1833,10 @@ void render_target::resolve_tags()
void render_target::update_layer_config()
{
- m_curview->recompute(m_layerconfig);
+ forget_pointers();
+ current_view().recompute(visibility_mask(), m_layerconfig.zoom_to_screen());
+ m_clickable_items.clear();
+ m_clickable_items.resize(current_view().interactive_items().size());
}
@@ -1540,96 +1847,97 @@ void render_target::update_layer_config()
void render_target::load_layout_files(const internal_layout *layoutfile, bool singlefile)
{
- bool have_artwork = false;
+ bool have_artwork = false;
// if there's an explicit file, load that first
- const char *basename = m_manager.machine().basename();
+ const std::string &basename = m_manager.machine().basename();
if (layoutfile)
- have_artwork |= load_layout_file(basename, *layoutfile);
+ have_artwork |= load_layout_file(basename.c_str(), *layoutfile);
// if we're only loading this file, we know our final result
if (!singlefile)
- load_additional_layout_files(basename, have_artwork);
+ load_additional_layout_files(basename.c_str(), have_artwork);
}
void render_target::load_layout_files(util::xml::data_node const &rootnode, bool singlefile)
{
- bool have_artwork = false;
+ bool have_artwork = false;
// if there's an explicit file, load that first
- const char *basename = m_manager.machine().basename();
- have_artwork |= load_layout_file(m_manager.machine().root_device(), basename, rootnode);
+ const std::string &basename = m_manager.machine().basename();
+ have_artwork |= load_layout_file(m_manager.machine().root_device(), rootnode, m_manager.machine().options().art_path(), basename.c_str());
// if we're only loading this file, we know our final result
if (!singlefile)
- load_additional_layout_files(basename, have_artwork);
+ load_additional_layout_files(basename.c_str(), have_artwork);
}
void render_target::load_additional_layout_files(const char *basename, bool have_artwork)
{
- bool have_default = false;
- bool have_override = false;
+ using util::lang_translate;
+
+ m_external_artwork = false;
// if override_artwork defined, load that and skip artwork other than default
- if (m_manager.machine().options().override_artwork())
+ const char *const override_art = m_manager.machine().options().override_artwork();
+ if (override_art && *override_art)
{
- if (load_layout_file(m_manager.machine().options().override_artwork(), m_manager.machine().options().override_artwork()))
- have_override = true;
- else if (load_layout_file(m_manager.machine().options().override_artwork(), "default"))
- have_override = true;
+ if (load_layout_file(override_art, override_art))
+ m_external_artwork = true;
+ else if (load_layout_file(override_art, "default"))
+ m_external_artwork = true;
}
const game_driver &system = m_manager.machine().system();
// Skip if override_artwork has found artwork
- if (!have_override)
+ if (!m_external_artwork)
{
-
// try to load a file based on the driver name
if (!load_layout_file(basename, system.name))
- have_artwork |= load_layout_file(basename, "default");
+ m_external_artwork |= load_layout_file(basename, "default");
else
- have_artwork = true;
+ m_external_artwork = true;
+
+ // try to load another file based on the parent driver name
+ int cloneof = driver_list::clone(system);
+ while (0 <= cloneof)
+ {
+ if (!m_external_artwork || driver_list::driver(cloneof).flags & MACHINE_IS_BIOS_ROOT)
+ {
+ if (!load_layout_file(driver_list::driver(cloneof).name, driver_list::driver(cloneof).name))
+ m_external_artwork |= load_layout_file(driver_list::driver(cloneof).name, "default");
+ else
+ m_external_artwork = true;
+ }
+
+ // Check the parent of the parent to cover bios based artwork
+ const game_driver &parent(driver_list::driver(cloneof));
+ cloneof = driver_list::clone(parent);
+ }
// if a default view has been specified, use that as a fallback
- if (system.default_layout != nullptr)
+ bool have_default = false;
+ if (system.default_layout)
have_default |= load_layout_file(nullptr, *system.default_layout);
m_manager.machine().config().apply_default_layouts(
[this, &have_default] (device_t &dev, internal_layout const &layout)
{ have_default |= load_layout_file(nullptr, layout, &dev); });
- // try to load another file based on the parent driver name
- int cloneof = driver_list::clone(system);
- if (cloneof != -1)
- {
- if (!load_layout_file(driver_list::driver(cloneof).name, driver_list::driver(cloneof).name))
- have_artwork |= load_layout_file(driver_list::driver(cloneof).name, "default");
- else
- have_artwork = true;
- }
+ have_artwork |= m_external_artwork;
- // Check the parent of the parent to cover bios based artwork
- if (cloneof != -1) {
- const game_driver &clone(driver_list::driver(cloneof));
- int cloneofclone = driver_list::clone(clone);
- if (cloneofclone != -1 && cloneofclone != cloneof)
+ // Use fallback artwork if defined and no artwork has been found yet
+ if (!have_artwork)
+ {
+ const char *const fallback_art = m_manager.machine().options().fallback_artwork();
+ if (fallback_art && *fallback_art)
{
- if (!load_layout_file(driver_list::driver(cloneofclone).name, driver_list::driver(cloneofclone).name))
- have_artwork |= load_layout_file(driver_list::driver(cloneofclone).name, "default");
+ if (!load_layout_file(fallback_art, fallback_art))
+ have_artwork |= load_layout_file(fallback_art, "default");
else
have_artwork = true;
}
}
-
- // Use fallback artwork if defined and no artwork has been found yet
- if (!have_artwork && m_manager.machine().options().fallback_artwork())
- {
- if (!load_layout_file(m_manager.machine().options().fallback_artwork(), m_manager.machine().options().fallback_artwork()))
- have_artwork |= load_layout_file(m_manager.machine().options().fallback_artwork(), "default");
- else
- have_artwork = true;
- }
-
}
// local screen info to avoid repeated code
@@ -1673,13 +1981,24 @@ void render_target::load_additional_layout_files(const char *basename, bool have
bool m_rotated;
std::pair<unsigned, unsigned> m_physical, m_native;
};
- screen_device_iterator iter(m_manager.machine().root_device());
+ screen_device_enumerator iter(m_manager.machine().root_device());
std::vector<screen_info> const screens(std::begin(iter), std::end(iter));
+ // need this because views aren't fully set up yet
+ auto const nth_view =
+ [this] (unsigned n) -> layout_view *
+ {
+ for (layout_file &file : m_filelist)
+ for (layout_view &view : file.views())
+ if (!(m_flags & RENDER_CREATE_NO_ART) || !view.has_art())
+ if (n-- == 0)
+ return &view;
+ return nullptr;
+ };
if (screens.empty()) // ensure the fallback view for systems with no screens is loaded if necessary
{
- if (!view_by_index(0))
+ if (!nth_view(0))
{
load_layout_file(nullptr, layout_noscreens);
if (m_filelist.empty())
@@ -1704,10 +2023,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(
+ _("view-name", "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??");
@@ -1731,10 +2049,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(
+ _("view-name", "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??");
@@ -1755,7 +2072,7 @@ void render_target::load_additional_layout_files(const char *basename, bool have
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", "Cocktail");
+ viewnode->set_attribute("name", _("view-name", "Cocktail"));
util::xml::data_node *const mirrornode(viewnode->add_child("screen", nullptr));
if (!mirrornode)
@@ -1792,23 +2109,19 @@ void render_target::load_additional_layout_files(const char *basename, bool have
{
need_tiles = true;
int viewindex(0);
- for (layout_view *view = view_by_index(viewindex); need_tiles && view; view = view_by_index(++viewindex))
+ for (layout_view *view = nth_view(viewindex); need_tiles && view; view = nth_view(++viewindex))
{
- render_screen_list const &viewscreens(view->screens());
- if (viewscreens.count() >= screens.size())
+ bool screen_missing(false);
+ for (screen_device &screen : iter)
{
- bool screen_missing(false);
- for (screen_device &screen : iter)
+ if (!view->has_screen(screen))
{
- if (!viewscreens.contains(screen))
- {
- screen_missing = true;
- break;
- }
+ screen_missing = true;
+ break;
}
- if (!screen_missing)
- need_tiles = false;
}
+ if (!screen_missing)
+ need_tiles = false;
}
}
if (need_tiles)
@@ -1852,7 +2165,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)
{
@@ -1879,10 +2192,10 @@ void render_target::load_additional_layout_files(const char *basename, bool have
};
// generate linear views
- generate_view("Left-to-Right", screens.size(), false, [] (unsigned x, unsigned y) { return x; });
- generate_view("Left-to-Right (Gapless)", screens.size(), true, [] (unsigned x, unsigned y) { return x; });
- generate_view("Top-to-Bottom", 1U, false, [] (unsigned x, unsigned y) { return y; });
- generate_view("Top-to-Bottom (Gapless)", 1U, true, [] (unsigned x, unsigned y) { return y; });
+ generate_view(_("view-name", "Left-to-Right"), screens.size(), false, [] (unsigned x, unsigned y) { return x; });
+ generate_view(_("view-name", "Left-to-Right (Gapless)"), screens.size(), true, [] (unsigned x, unsigned y) { return x; });
+ generate_view(_("view-name", "Top-to-Bottom"), 1U, false, [] (unsigned x, unsigned y) { return y; });
+ generate_view(_("view-name", "Top-to-Bottom (Gapless)"), 1U, true, [] (unsigned x, unsigned y) { return y; });
// generate fake cocktail view for systems with two screens
if (screens.size() == 2U)
@@ -1894,7 +2207,7 @@ void render_target::load_additional_layout_files(const char *basename, bool have
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", "Cocktail");
+ viewnode->set_attribute("name", _("view-name", "Cocktail"));
util::xml::data_node *const mirrornode(viewnode->add_child("screen", nullptr));
if (!mirrornode)
@@ -1933,7 +2246,7 @@ void render_target::load_additional_layout_files(const char *basename, bool have
if (!remainder || (((majdim + 1) / 2) <= remainder))
{
generate_view(
- util::string_format("%1$u\xC3\x97%2$u Left-to-Right, Top-to-Bottom", majdim, mindim).c_str(),
+ util::string_format(_("view-name", u8"%1$u×%2$u Left-to-Right, Top-to-Bottom"), majdim, mindim).c_str(),
majdim,
false,
[&screens, majdim] (unsigned x, unsigned y)
@@ -1942,7 +2255,7 @@ void render_target::load_additional_layout_files(const char *basename, bool have
return (screens.size() > i) ? int(i) : -1;
});
generate_view(
- util::string_format("%1$u\xC3\x97%2$u Left-to-Right, Top-to-Bottom (Gapless)", majdim, mindim).c_str(),
+ util::string_format(_("view-name", u8"%1$u×%2$u Left-to-Right, Top-to-Bottom (Gapless)"), majdim, mindim).c_str(),
majdim,
true,
[&screens, majdim] (unsigned x, unsigned y)
@@ -1951,7 +2264,7 @@ void render_target::load_additional_layout_files(const char *basename, bool have
return (screens.size() > i) ? int(i) : -1;
});
generate_view(
- util::string_format("%1$u\xC3\x97%2$u Top-to-Bottom, Left-to-Right", mindim, majdim).c_str(),
+ util::string_format(_("view-name", u8"%1$u×%2$u Top-to-Bottom, Left-to-Right"), mindim, majdim).c_str(),
mindim,
false,
[&screens, majdim] (unsigned x, unsigned y)
@@ -1960,7 +2273,7 @@ void render_target::load_additional_layout_files(const char *basename, bool have
return (screens.size() > i) ? int(i) : -1;
});
generate_view(
- util::string_format("%1$u\xC3\x97%2$u Top-to-Bottom, Left-to-Right (Gapless)", mindim, majdim).c_str(),
+ util::string_format(_("view-name", u8"%1$u×%2$u Top-to-Bottom, Left-to-Right (Gapless)"), mindim, majdim).c_str(),
mindim,
true,
[&screens, majdim] (unsigned x, unsigned y)
@@ -1973,7 +2286,7 @@ void render_target::load_additional_layout_files(const char *basename, bool have
}
// try to parse it
- if (!load_layout_file(m_manager.machine().root_device(), nullptr, *root))
+ if (!load_layout_file(m_manager.machine().root_device(), *root, m_manager.machine().options().art_path(), nullptr))
throw emu_fatalerror("Couldn't parse generated layout??");
}
}
@@ -1987,87 +2300,93 @@ void render_target::load_additional_layout_files(const char *basename, bool have
bool render_target::load_layout_file(const char *dirname, const internal_layout &layout_data, device_t *device)
{
// +1 to ensure data is terminated for XML parser
- auto tempout = make_unique_clear<u8 []>(layout_data.decompressed_size + 1);
-
- z_stream stream;
- int zerr;
-
- /* initialize the stream */
- memset(&stream, 0, sizeof(stream));
- stream.next_out = tempout.get();
- stream.avail_out = layout_data.decompressed_size;
-
-
- zerr = inflateInit(&stream);
- if (zerr != Z_OK)
- {
- fatalerror("could not inflateInit");
- return false;
- }
-
- /* decompress this chunk */
- stream.next_in = (unsigned char *)layout_data.data;
- stream.avail_in = layout_data.compressed_size;
- zerr = inflate(&stream, Z_NO_FLUSH);
-
- /* stop at the end of the stream */
- if (zerr == Z_STREAM_END)
- {
- // OK
- }
- else if (zerr != Z_OK)
+ std::unique_ptr<u8 []> tempout(new (std::nothrow) u8 [layout_data.decompressed_size + 1]);
+ auto inflater(util::zlib_read(util::ram_read(layout_data.data, layout_data.compressed_size), 8192));
+ if (!tempout || !inflater)
{
- fatalerror("decompression error\n");
+ osd_printf_error("render_target::load_layout_file: not enough memory to decompress layout\n");
return false;
}
- /* clean up */
- zerr = inflateEnd(&stream);
- if (zerr != Z_OK)
+ size_t decompressed = 0;
+ do
{
- fatalerror("inflateEnd error\n");
- return false;
+ auto const [err, actual] = read(
+ *inflater,
+ &tempout[decompressed],
+ layout_data.decompressed_size - decompressed);
+ decompressed += actual;
+ if (err)
+ {
+ osd_printf_error(
+ "render_target::load_layout_file: error decompressing layout (%s:%d %s)\n",
+ err.category().name(),
+ err.value(),
+ err.message());
+ return false;
+ }
+ if (!actual && (layout_data.decompressed_size < decompressed))
+ {
+ osd_printf_warning(
+ "render_target::load_layout_file: expected %u bytes of decompressed data but only got %u\n",
+ layout_data.decompressed_size,
+ decompressed);
+ break;
+ }
}
+ while (layout_data.decompressed_size > decompressed);
+ inflater.reset();
+ tempout[decompressed] = 0U;
util::xml::file::ptr rootnode(util::xml::file::string_read(reinterpret_cast<char const *>(tempout.get()), nullptr));
tempout.reset();
// if we didn't get a properly-formatted XML file, record a warning and exit
- if (!load_layout_file(device ? *device : m_manager.machine().root_device(), dirname, *rootnode))
+ if (!load_layout_file(device ? *device : m_manager.machine().root_device(), *rootnode, m_manager.machine().options().art_path(), dirname))
{
- osd_printf_warning("Improperly formatted XML string, ignoring\n");
+ osd_printf_warning("render_target::load_layout_file: Improperly formatted XML string, ignoring\n");
return false;
}
- else
- {
- return true;
- }
+
+ return true;
}
bool render_target::load_layout_file(const char *dirname, const char *filename)
{
// build the path and optionally prepend the directory
- std::string fname = std::string(filename).append(".lay");
+ std::string fname;
if (dirname)
- fname.insert(0, PATH_SEPARATOR).insert(0, dirname);
-
- // attempt to open the file; bail if we can't
- emu_file layoutfile(m_manager.machine().options().art_path(), OPEN_FLAG_READ);
- osd_file::error const filerr(layoutfile.open(fname.c_str()));
- if (filerr != osd_file::error::NONE)
- return false;
+ fname.append(dirname).append(PATH_SEPARATOR);
+ fname.append(filename).append(".lay");
- // read the file
+ // attempt to open matching files
util::xml::parse_options parseopt;
util::xml::parse_error parseerr;
parseopt.error = &parseerr;
- util::xml::file::ptr rootnode(util::xml::file::read(layoutfile, &parseopt));
- if (!rootnode)
+ emu_file layoutfile(m_manager.machine().options().art_path(), OPEN_FLAG_READ);
+ layoutfile.set_restrict_to_mediapath(1);
+ bool result(false);
+ for (std::error_condition filerr = layoutfile.open(fname); !filerr; filerr = layoutfile.open_next())
{
- if (parseerr.error_message)
+ // read the file and parse as XML
+ util::xml::file::ptr const rootnode(util::xml::file::read(layoutfile, &parseopt));
+ if (rootnode)
+ {
+ // extract directory name from location of layout file
+ std::string artdir(layoutfile.fullpath());
+ auto const dirsep(std::find_if(artdir.rbegin(), artdir.rend(), &util::is_directory_separator));
+ artdir.erase(dirsep.base(), artdir.end());
+
+ // record a warning if we didn't get a properly-formatted XML file
+ if (!load_layout_file(m_manager.machine().root_device(), *rootnode, nullptr, artdir.c_str()))
+ osd_printf_warning("Improperly formatted XML layout file '%s', ignoring\n", filename);
+ else
+ result = true;
+ }
+ else if (parseerr.error_message)
{
osd_printf_warning(
- "Error parsing XML file '%s' at line %d column %d: %s, ignoring\n",
+ "Error parsing XML layout file '%s' at line %d column %d: %s, ignoring\n",
filename,
parseerr.error_line,
parseerr.error_column,
@@ -2075,37 +2394,25 @@ bool render_target::load_layout_file(const char *dirname, const char *filename)
}
else
{
- osd_printf_warning("Error parsing XML file '%s', ignorning\n", filename);
+ osd_printf_warning("Error parsing XML layout file '%s', ignorning\n", filename);
}
- return false;
- }
-
- // if we didn't get a properly-formatted XML file, record a warning and exit
- if (!load_layout_file(m_manager.machine().root_device(), dirname, *rootnode))
- {
- osd_printf_warning("Improperly formatted XML file '%s', ignoring\n", filename);
- return false;
- }
- else
- {
- return true;
}
+ return result;
}
-bool render_target::load_layout_file(device_t &device, const char *dirname, util::xml::data_node const &rootnode)
+bool render_target::load_layout_file(device_t &device, util::xml::data_node const &rootnode, const char *searchpath, const char *dirname)
{
// parse and catch any errors
try
{
- m_filelist.emplace_back(device, rootnode, dirname);
+ m_filelist.emplace_back(device, rootnode, searchpath, dirname);
}
- catch (emu_fatalerror &)
+ catch (emu_fatalerror const &err)
{
+ osd_printf_warning("%s\n", err.what());
return false;
}
- emulator_info::layout_file_cb(rootnode);
-
return true;
}
@@ -2126,7 +2433,7 @@ void render_target::add_container_primitives(render_primitive_list &list, const
cliprect.y0 = xform.yoffs;
cliprect.x1 = xform.xoffs + xform.xscale;
cliprect.y1 = xform.yoffs + xform.yscale;
- sect_render_bounds(cliprect, m_bounds);
+ cliprect &= m_bounds;
float root_xoffs = root_xform.xoffs + fabsf(root_xform.xscale - xform.xscale) * 0.5f;
float root_yoffs = root_xform.yoffs + fabsf(root_xform.yscale - xform.yscale) * 0.5f;
@@ -2136,7 +2443,7 @@ void render_target::add_container_primitives(render_primitive_list &list, const
root_cliprect.y0 = root_yoffs;
root_cliprect.x1 = root_xoffs + root_xform.xscale;
root_cliprect.y1 = root_yoffs + root_xform.yscale;
- sect_render_bounds(root_cliprect, m_bounds);
+ root_cliprect &= m_bounds;
// compute the container transform
object_transform container_xform;
@@ -2236,11 +2543,11 @@ void render_target::add_container_primitives(render_primitive_list &list, const
// clip the primitive
if (!m_transform_container && PRIMFLAG_GET_VECTOR(curitem.flags()))
{
- clipped = render_clip_line(&prim->bounds, &root_cliprect);
+ clipped = render_clip_line(prim->bounds, root_cliprect);
}
else
{
- clipped = render_clip_line(&prim->bounds, &cliprect);
+ clipped = render_clip_line(prim->bounds, cliprect);
}
break;
@@ -2267,13 +2574,13 @@ void render_target::add_container_primitives(render_primitive_list &list, const
curitem.texture()->get_scaled(width, height, prim->texture, list, curitem.flags());
// set the palette
- prim->texture.palette = curitem.texture()->get_adjusted_palette(container);
+ prim->texture.palette = curitem.texture()->get_adjusted_palette(container, prim->texture.palette_length);
// determine UV coordinates
prim->texcoords = oriented_texcoords[finalorient];
// apply clipping
- clipped = render_clip_quad(&prim->bounds, &cliprect, &prim->texcoords);
+ clipped = render_clip_quad(prim->bounds, cliprect, &prim->texcoords);
// apply the final orientation from the quad flags and then build up the final flags
prim->flags |= (curitem.flags() & ~(PRIMFLAG_TEXORIENT_MASK | PRIMFLAG_BLENDMODE_MASK | PRIMFLAG_TEXFORMAT_MASK))
@@ -2333,7 +2640,7 @@ void render_target::add_container_primitives(render_primitive_list &list, const
prim->texcoords = oriented_texcoords[finalorient];
// apply clipping
- clipped = render_clip_quad(&prim->bounds, &cliprect, &prim->texcoords);
+ clipped = render_clip_quad(prim->bounds, cliprect, &prim->texcoords);
// apply the final orientation from the quad flags and then build up the final flags
prim->flags |= (curitem.flags() & ~(PRIMFLAG_TEXORIENT_MASK | PRIMFLAG_BLENDMODE_MASK | PRIMFLAG_TEXFORMAT_MASK))
@@ -2349,7 +2656,7 @@ void render_target::add_container_primitives(render_primitive_list &list, const
| PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA);
// apply clipping
- clipped = render_clip_quad(&prim->bounds, &cliprect, nullptr);
+ clipped = render_clip_quad(prim->bounds, cliprect, nullptr);
}
}
break;
@@ -2366,7 +2673,7 @@ void render_target::add_container_primitives(render_primitive_list &list, const
// allocate a primitive
render_primitive *prim = list.alloc(render_primitive::QUAD);
- set_render_bounds_wh(prim->bounds, xform.xoffs, xform.yoffs, xform.xscale, xform.yscale);
+ prim->bounds.set_wh(xform.xoffs, xform.yoffs, xform.xscale, xform.yscale);
prim->full_bounds = prim->bounds;
prim->color = container_xform.color;
width = render_round_nearest(prim->bounds.x1) - render_round_nearest(prim->bounds.x0);
@@ -2395,51 +2702,86 @@ void render_target::add_container_primitives(render_primitive_list &list, const
// for an element in the current state
//-------------------------------------------------
-void render_target::add_element_primitives(render_primitive_list &list, const object_transform &xform, layout_element &element, int state, int blendmode)
+void render_target::add_element_primitives(render_primitive_list &list, const object_transform &xform, layout_view_item &item)
{
- // if we're out of range, bail
- if (state > element.maxstate())
- return;
- if (state < 0)
- state = 0;
+ layout_element &element(*item.element());
+ int const blendmode(item.blend_mode());
+
+ // limit state range to non-negative values
+ int const state((std::max)(item.element_state(), 0));
// get a pointer to the relevant texture
render_texture *texture = element.state_texture(state);
- if (texture != nullptr)
+ if (texture)
{
render_primitive *prim = list.alloc(render_primitive::QUAD);
// configure the basics
prim->color = xform.color;
- prim->flags = PRIMFLAG_TEXORIENT(xform.orientation) | PRIMFLAG_BLENDMODE(blendmode) | PRIMFLAG_TEXFORMAT(texture->format());
+ prim->flags =
+ PRIMFLAG_TEXORIENT(xform.orientation) |
+ PRIMFLAG_TEXFORMAT(texture->format()) |
+ PRIMFLAG_BLENDMODE(blendmode) |
+ PRIMFLAG_TEXWRAP((item.scroll_wrap_x() || item.scroll_wrap_y()) ? 1 : 0);
// compute the bounds
- s32 width = render_round_nearest(xform.xscale);
- s32 height = render_round_nearest(xform.yscale);
- set_render_bounds_wh(prim->bounds, render_round_nearest(xform.xoffs), render_round_nearest(xform.yoffs), (float) width, (float) height);
+ float const primwidth(render_round_nearest(xform.xscale));
+ float const primheight(render_round_nearest(xform.yscale));
+ prim->bounds.set_wh(render_round_nearest(xform.xoffs), render_round_nearest(xform.yoffs), primwidth, primheight);
prim->full_bounds = prim->bounds;
- if (xform.orientation & ORIENTATION_SWAP_XY)
- std::swap(width, height);
- width = std::min(width, m_maxtexwidth);
- height = std::min(height, m_maxtexheight);
// get the scaled texture and append it
-
- texture->get_scaled(width, height, prim->texture, list, prim->flags);
+ float const xsize(item.scroll_size_x());
+ float const ysize(item.scroll_size_y());
+ s32 texwidth = render_round_nearest(((xform.orientation & ORIENTATION_SWAP_XY) ? primheight : primwidth) / xsize);
+ s32 texheight = render_round_nearest(((xform.orientation & ORIENTATION_SWAP_XY) ? primwidth : primheight) / ysize);
+ texwidth = (std::min)(texwidth, m_maxtexwidth);
+ texheight = (std::min)(texheight, m_maxtexheight);
+ texture->get_scaled(texwidth, texheight, prim->texture, list, prim->flags);
// compute the clip rect
- render_bounds cliprect;
- cliprect.x0 = render_round_nearest(xform.xoffs);
- cliprect.y0 = render_round_nearest(xform.yoffs);
- cliprect.x1 = render_round_nearest(xform.xoffs + xform.xscale);
- cliprect.y1 = render_round_nearest(xform.yoffs + xform.yscale);
- sect_render_bounds(cliprect, m_bounds);
+ render_bounds cliprect = prim->bounds & m_bounds;
// determine UV coordinates and apply clipping
- prim->texcoords = oriented_texcoords[xform.orientation];
- bool clipped = render_clip_quad(&prim->bounds, &cliprect, &prim->texcoords);
+ float const xwindow((xform.orientation & ORIENTATION_SWAP_XY) ? primheight : primwidth);
+ float const ywindow((xform.orientation & ORIENTATION_SWAP_XY) ? primwidth : primheight);
+ float const xrange(float(texwidth) - (item.scroll_wrap_x() ? 0.0f : xwindow));
+ float const yrange(float(texheight) - (item.scroll_wrap_y() ? 0.0f : ywindow));
+ float const xoffset(render_round_nearest(item.scroll_pos_x() * xrange) / float(texwidth));
+ float const yoffset(render_round_nearest(item.scroll_pos_y() * yrange) / float(texheight));
+ float const xend(xoffset + (xwindow / float(texwidth)));
+ float const yend(yoffset + (ywindow / float(texheight)));
+ switch (xform.orientation)
+ {
+ default:
+ case 0:
+ prim->texcoords = render_quad_texuv{ { xoffset, yoffset }, { xend, yoffset }, { xoffset, yend }, { xend, yend } };
+ break;
+ case ORIENTATION_FLIP_X:
+ prim->texcoords = render_quad_texuv{ { xend, yoffset }, { xoffset, yoffset }, { xend, yend }, { xoffset, yend } };
+ break;
+ case ORIENTATION_FLIP_Y:
+ prim->texcoords = render_quad_texuv{ { xoffset, yend }, { xend, yend }, { xoffset, yoffset }, { xend, yoffset } };
+ break;
+ case ORIENTATION_FLIP_X | ORIENTATION_FLIP_Y:
+ prim->texcoords = render_quad_texuv{ { xend, yend }, { xoffset, yend }, { xend, yoffset }, { xoffset, yoffset } };
+ break;
+ case ORIENTATION_SWAP_XY:
+ prim->texcoords = render_quad_texuv{ { xoffset, yoffset }, { xoffset, yend }, { xend, yoffset }, { xend, yend } };
+ break;
+ case ORIENTATION_SWAP_XY | ORIENTATION_FLIP_X:
+ prim->texcoords = render_quad_texuv{ { xoffset, yend }, { xoffset, yoffset }, { xend, yend }, { xend, yoffset } };
+ break;
+ case ORIENTATION_SWAP_XY | ORIENTATION_FLIP_Y:
+ prim->texcoords = render_quad_texuv{ { xend, yoffset }, { xend, yend }, { xoffset, yoffset }, { xoffset, yend } };
+ break;
+ case ORIENTATION_SWAP_XY | ORIENTATION_FLIP_X | ORIENTATION_FLIP_Y:
+ prim->texcoords = render_quad_texuv{ { xend, yend }, { xend, yoffset }, { xoffset, yend }, { xoffset, yoffset } };
+ break;
+ }
// add to the list or free if we're clipped out
+ bool const clipped = render_clip_quad(prim->bounds, cliprect, &prim->texcoords);
list.append_or_return(*prim, clipped);
}
}
@@ -2450,7 +2792,7 @@ void render_target::add_element_primitives(render_primitive_list &list, const ob
// mapping points
//-------------------------------------------------
-bool render_target::map_point_internal(s32 target_x, s32 target_y, render_container *container, float &mapped_x, float &mapped_y, ioport_port *&mapped_input_port, ioport_value &mapped_input_mask)
+std::pair<float, float> render_target::map_point_internal(s32 target_x, s32 target_y)
{
// compute the visible width/height
s32 viswidth, visheight;
@@ -2458,62 +2800,33 @@ bool render_target::map_point_internal(s32 target_x, s32 target_y, render_contai
// create a root transform for the target
object_transform root_xform;
- root_xform.xoffs = (float)(m_width - viswidth) / 2;
- root_xform.yoffs = (float)(m_height - visheight) / 2;
-
- // default to point not mapped
- mapped_x = -1.0;
- mapped_y = -1.0;
- mapped_input_port = nullptr;
- mapped_input_mask = 0;
+ root_xform.xoffs = float(m_width - viswidth) / 2;
+ root_xform.yoffs = float(m_height - visheight) / 2;
// convert target coordinates to float
- float target_fx = (float)(target_x - root_xform.xoffs) / viswidth;
- float target_fy = (float)(target_y - root_xform.yoffs) / visheight;
- if (m_manager.machine().ui().is_menu_active())
- {
- target_fx = (float)target_x / m_width;
- target_fy = (float)target_y / m_height;
- }
- // explicitly check for the UI container
- if (container != nullptr && container == &m_manager.ui_container())
- {
- // this hit test went against the UI container
- if (target_fx >= 0.0f && target_fx < 1.0f && target_fy >= 0.0f && target_fy < 1.0f)
- {
- // this point was successfully mapped
- mapped_x = (float)target_x / m_width;
- mapped_y = (float)target_y / m_height;
- return true;
- }
- return false;
- }
-
- // iterate over items in the view
- for (layout_view::item &item : m_curview->items())
- {
- bool checkit;
+ if (!m_manager.machine().ui().is_menu_active())
+ return std::make_pair(float(target_x - root_xform.xoffs) / viswidth, float(target_y - root_xform.yoffs) / visheight);
+ else
+ return std::make_pair(float(target_x) / m_width, float(target_y) / m_height);
+}
- // if we're looking for a particular container, verify that we have the right one
- if (container != nullptr)
- checkit = (item.screen() != nullptr && &item.screen()->container() == container);
- // otherwise, assume we're looking for an input
- else
- checkit = item.has_input();
-
- // this target is worth looking at; now check the point
- if (checkit && target_fx >= item.bounds().x0 && target_fx < item.bounds().x1 && target_fy >= item.bounds().y0 && target_fy < item.bounds().y1)
- {
- // point successfully mapped
- mapped_x = (target_fx - item.bounds().x0) / (item.bounds().x1 - item.bounds().x0);
- mapped_y = (target_fy - item.bounds().y0) / (item.bounds().y1 - item.bounds().y0);
- mapped_input_port = item.input_tag_and_mask(mapped_input_mask);
- return true;
- }
- }
+//-------------------------------------------------
+// map_point_layout - map point from screen
+// coordinates to layout coordinates
+//-------------------------------------------------
- return false;
+std::pair<float, float> render_target::map_point_layout(s32 target_x, s32 target_y)
+{
+ using std::swap;
+ std::pair<float, float> result(map_point_internal(target_x, target_y));
+ if (m_orientation & ORIENTATION_FLIP_X)
+ result.first = 1.0f - result.first;
+ if (m_orientation & ORIENTATION_FLIP_Y)
+ result.second = 1.0f - result.second;
+ if (m_orientation & ORIENTATION_SWAP_XY)
+ swap(result.first, result.second);
+ return result;
}
@@ -2522,15 +2835,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 : nullptr;
}
@@ -2541,18 +2848,12 @@ layout_view *render_target::view_by_index(int index)
int render_target::view_index(layout_view &targetview) const
{
- // find the first named match
- int index = 0;
-
- // scan the list of views within each layout, skipping those that don't apply
- for (layout_file const &file : m_filelist)
- for (layout_view const &view : file.views())
- if (!(m_flags & RENDER_CREATE_NO_ART) || !view.has_art())
- {
- if (&targetview == &view)
- return index;
- index++;
- }
+ // return index of view, or zero if not found
+ for (int index = 0; m_views.size() > index; ++index)
+ {
+ if (&m_views[index].first == &targetview)
+ return index;
+ }
return 0;
}
@@ -2561,53 +2862,99 @@ int render_target::view_index(layout_view &targetview) const
// config_load - process config information
//-------------------------------------------------
-void render_target::config_load(util::xml::data_node const &targetnode)
+void render_target::config_load(util::xml::data_node const *targetnode)
{
- int tmpint;
+ // remember the view selected via command line and INI options
+ if (!m_base_view)
+ m_base_view = &current_view();
+
+ // bail if no configuration
+ if (!targetnode)
+ return;
+
+ // TODO: consider option priority - command line should take precedence over CFG
+ // not practical at the moment because view selection options are in the OSD layer
// find the view
- const char *viewname = targetnode.get_attribute_string("view", nullptr);
- if (viewname != nullptr)
- for (int viewnum = 0; viewnum < 1000; viewnum++)
+ const char *viewname = targetnode->get_attribute_string("view", nullptr);
+ if (viewname)
+ {
+ for (unsigned viewnum = 0; m_views.size() > viewnum; viewnum++)
{
- const char *testname = view_name(viewnum);
- if (testname == nullptr)
- break;
- if (!strcmp(viewname, testname))
+ if (!strcmp(viewname, view_name(viewnum)))
{
set_view(viewnum);
break;
}
}
+ }
// 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())
+ if (m_ui_container)
{
- render_container::user_settings settings;
- render_container &ui_container = m_manager.ui_container();
+ render_container::user_settings settings = m_ui_container->get_user_settings();
+ settings.m_orientation = orientation_add(orientation_reverse(rotate), settings.m_orientation);
+ m_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.name() == viewname; });
+ if (m_views.end() == view)
+ continue;
- ui_container.get_user_settings(settings);
- settings.m_orientation = orientation_add(orientation_reverse(tmpint), settings.m_orientation);
- ui_container.set_user_settings(settings);
+ for (util::xml::data_node const *vistogglenode = viewnode->get_child("collection"); vistogglenode; vistogglenode = vistogglenode->get_next_sibling("collection"))
+ {
+ char const *const vistogglename = vistogglenode->get_attribute_string("name", nullptr);
+ if (!vistogglename)
+ continue;
+
+ auto const &vistoggles = view->first.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("visible", -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));
+ }
+ }
+
+ if (&current_view() == &view->first)
+ {
+ current_view().recompute(visibility_mask(), m_layerconfig.zoom_to_screen());
+ current_view().preload();
+ m_clickable_items.clear();
+ m_clickable_items.resize(current_view().interactive_items().size());
}
}
}
@@ -2626,9 +2973,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;
}
@@ -2654,6 +3001,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.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.visibility_toggles())
+ {
+ if (BIT(defvismask, i) != BIT(view.second, i))
+ {
+ if (!viewnode)
+ {
+ viewnode = targetnode.add_child("view", nullptr);
+ viewnode->set_attribute("name", view.first.name().c_str());
+ }
+ util::xml::data_node *const vistogglenode = viewnode->add_child("collection", nullptr);
+ vistogglenode->set_attribute("name", toggle.name().c_str());
+ vistogglenode->set_attribute_int("visible", BIT(view.second, i));
+ changed = true;
+ }
+ ++i;
+ }
+ }
+ }
+
return changed;
}
@@ -2843,9 +3217,9 @@ void render_target::add_clear_extents(render_primitive_list &list)
if (x1 - x0 > 0)
{
render_primitive *prim = list.alloc(render_primitive::QUAD);
- set_render_bounds_xy(prim->bounds, (float)x0, (float)y0, (float)x1, (float)y1);
+ prim->bounds.set_xy(float(x0), float(y0), float(x1), float(y1));
prim->full_bounds = prim->bounds;
- set_render_color(&prim->color, 1.0f, 0.0f, 0.0f, 0.0f);
+ prim->color.set(1.0f, 0.0f, 0.0f, 0.0f);
prim->texture.base = nullptr;
prim->flags = PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA);
clearlist.append(*prim);
@@ -2899,7 +3273,7 @@ void render_target::add_clear_and_optimize_primitive_list(render_primitive_list
if (PRIMFLAG_GET_BLENDMODE(prim.flags) == BLENDMODE_RGB_MULTIPLY)
{
// RGB multiply will multiply against 0, leaving nothing
- set_render_color(&prim.color, 1.0f, 0.0f, 0.0f, 0.0f);
+ prim.color.set(1.0f, 0.0f, 0.0f, 0.0f);
prim.texture.base = nullptr;
prim.flags = (prim.flags & ~PRIMFLAG_BLENDMODE_MASK) | PRIMFLAG_BLENDMODE(BLENDMODE_NONE);
}
@@ -2938,18 +3312,20 @@ done:
//-------------------------------------------------
render_manager::render_manager(running_machine &machine)
- : m_machine(machine),
- m_ui_target(nullptr),
- m_live_textures(0),
- m_texture_id(0),
- m_ui_container(global_alloc(render_container(*this)))
+ : m_machine(machine)
+ , m_ui_target(nullptr)
+ , m_live_textures(0)
+ , m_texture_id(0)
{
// register callbacks
- machine.configuration().config_register("video", config_load_delegate(&render_manager::config_load, this), config_save_delegate(&render_manager::config_save, this));
+ machine.configuration().config_register(
+ "video",
+ configuration_manager::load_delegate(&render_manager::config_load, this),
+ configuration_manager::save_delegate(&render_manager::config_save, this));
// create one container per screen
- for (screen_device &screen : screen_device_iterator(machine.root_device()))
- screen.set_container(*container_alloc(&screen));
+ for (screen_device &screen : screen_device_enumerator(machine.root_device()))
+ screen.set_container(m_screen_container_list.emplace_back(*this, &screen));
}
@@ -2960,8 +3336,8 @@ render_manager::render_manager(running_machine &machine)
render_manager::~render_manager()
{
// free all the containers since they may own textures
- container_free(m_ui_container);
- m_screen_container_list.reset();
+ m_ui_containers.clear();
+ m_screen_container_list.clear();
// better not be any outstanding textures when we die
assert(m_live_textures == 0);
@@ -2975,9 +3351,15 @@ render_manager::~render_manager()
bool render_manager::is_live(screen_device &screen) const
{
// iterate over all live targets and or together their screen masks
- for (render_target &target : m_targetlist)
- if (!target.hidden() && target.view_screens(target.view()).contains(screen))
- return true;
+ for (render_target const &target : m_targetlist)
+ {
+ if (!target.hidden())
+ {
+ layout_view const *view = &target.current_view();
+ if (view->has_visible_screen(screen))
+ return true;
+ }
+ }
return false;
}
@@ -3010,12 +3392,14 @@ float render_manager::max_update_rate() const
render_target *render_manager::target_alloc(const internal_layout *layoutfile, u32 flags)
{
- return &m_targetlist.append(*global_alloc(render_target(*this, layoutfile, flags)));
+ render_container *const ui = (flags & RENDER_CREATE_HIDDEN) ? nullptr : &m_ui_containers.emplace_back(*this);
+ return &m_targetlist.append(*new render_target(*this, ui, layoutfile, flags));
}
render_target *render_manager::target_alloc(util::xml::data_node const &layout, u32 flags)
{
- return &m_targetlist.append(*global_alloc(render_target(*this, layout, flags)));
+ render_container *const ui = (flags & RENDER_CREATE_HIDDEN) ? nullptr : &m_ui_containers.emplace_back(*this);
+ return &m_targetlist.append(*new render_target(*this, ui, layout, flags));
}
@@ -3052,35 +3436,56 @@ render_target *render_manager::target_by_index(int index) const
float render_manager::ui_aspect(render_container *rc)
{
- int orient;
+ // work out if this is a UI container
+ render_target *target = nullptr;
+ if (!rc)
+ {
+ target = &ui_target();
+ rc = target->ui_container();
+ assert(rc);
+ }
+ else
+ {
+ for (render_target &t : m_targetlist)
+ {
+ if (t.ui_container() == rc)
+ {
+ target = &t;
+ break;
+ }
+ }
+ }
+
float aspect;
- if (rc == m_ui_container || rc == nullptr) {
- // ui container, aggregated multi-screen target
+ if (target)
+ {
+ // UI container, aggregated multi-screen target
- orient = orientation_add(m_ui_target->orientation(), m_ui_container->orientation());
// based on the orientation of the target, compute height/width or width/height
+ int const orient = orientation_add(target->orientation(), rc->orientation());
if (!(orient & ORIENTATION_SWAP_XY))
- aspect = (float)m_ui_target->height() / (float)m_ui_target->width();
+ aspect = float(target->height()) / float(target->width());
else
- aspect = (float)m_ui_target->width() / (float)m_ui_target->height();
+ aspect = float(target->width()) / float(target->height());
// if we have a valid pixel aspect, apply that and return
- if (m_ui_target->pixel_aspect() != 0.0f)
+ if (target->pixel_aspect() != 0.0f)
{
- float pixel_aspect = m_ui_target->pixel_aspect();
+ float pixel_aspect = target->pixel_aspect();
if (orient & ORIENTATION_SWAP_XY)
pixel_aspect = 1.0f / pixel_aspect;
return aspect /= pixel_aspect;
}
-
- } else {
+ }
+ else
+ {
// single screen container
- orient = rc->orientation();
// based on the orientation of the target, compute height/width or width/height
+ int const orient = rc->orientation();
if (!(orient & ORIENTATION_SWAP_XY))
aspect = (float)rc->screen()->visible_area().height() / (float)rc->screen()->visible_area().width();
else
@@ -3088,12 +3493,7 @@ float render_manager::ui_aspect(render_container *rc)
}
// clamp for extreme proportions
- if (aspect < 0.66f)
- aspect = 0.66f;
- if (aspect > 1.5f)
- aspect = 1.5f;
-
- return aspect;
+ return std::clamp(aspect, 0.66f, 1.5f);
}
@@ -3132,19 +3532,9 @@ void render_manager::texture_free(render_texture *texture)
// font_alloc - allocate a new font instance
//-------------------------------------------------
-render_font *render_manager::font_alloc(const char *filename)
-{
- return global_alloc(render_font(*this, filename));
-}
-
-
-//-------------------------------------------------
-// font_free - release a font instance
-//-------------------------------------------------
-
-void render_manager::font_free(render_font *font)
+std::unique_ptr<render_font> render_manager::font_alloc(const char *filename)
{
- global_free(font);
+ return std::unique_ptr<render_font>(new render_font(*this, filename));
}
@@ -3177,83 +3567,72 @@ void render_manager::resolve_tags()
//-------------------------------------------------
-// container_alloc - allocate a new container
-//-------------------------------------------------
-
-render_container *render_manager::container_alloc(screen_device *screen)
-{
- auto container = global_alloc(render_container(*this, screen));
- if (screen != nullptr)
- m_screen_container_list.append(*container);
- return container;
-}
-
-
-//-------------------------------------------------
-// container_free - release a container
-//-------------------------------------------------
-
-void render_manager::container_free(render_container *container)
-{
- m_screen_container_list.remove(*container);
-}
-
-
-//-------------------------------------------------
// config_load - read and apply data from the
// configuration file
//-------------------------------------------------
-void render_manager::config_load(config_type cfg_type, util::xml::data_node const *parentnode)
+void render_manager::config_load(config_type cfg_type, config_level cfg_level, util::xml::data_node const *parentnode)
{
- // we only care about game files
- if (cfg_type != config_type::GAME)
+ // we only care about system-specific configuration with matching nodes
+ if (cfg_type == config_type::DEFAULT)
+ {
+ // let the targets stabilise themselves
+ for (render_target &target : m_targetlist)
+ {
+ if (!target.hidden())
+ target.config_load(nullptr);
+ }
return;
-
- // might not have any data
- if (parentnode == nullptr)
+ }
+ else if ((cfg_type != config_type::SYSTEM) || !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)
- target->config_load(*targetnode);
+ render_target *const target = target_by_index(targetnode->get_attribute_int("index", -1));
+ if (target && !target->hidden())
+ 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);
- render_container *container = m_screen_container_list.find(index);
- render_container::user_settings settings;
-
- // fetch current settings
- container->get_user_settings(settings);
-
- // fetch color controls
- settings.m_brightness = screennode->get_attribute_float("brightness", settings.m_brightness);
- settings.m_contrast = screennode->get_attribute_float("contrast", settings.m_contrast);
- settings.m_gamma = screennode->get_attribute_float("gamma", settings.m_gamma);
-
- // fetch positioning controls
- settings.m_xoffset = screennode->get_attribute_float("hoffset", settings.m_xoffset);
- settings.m_xscale = screennode->get_attribute_float("hstretch", settings.m_xscale);
- settings.m_yoffset = screennode->get_attribute_float("voffset", settings.m_yoffset);
- settings.m_yscale = screennode->get_attribute_float("vstretch", settings.m_yscale);
+ int const index = screennode->get_attribute_int("index", -1);
+ render_container *container = nullptr;
+ if (index >= 0 && index < m_screen_container_list.size())
+ container = &*std::next(m_screen_container_list.begin(), index);
- // set the new values
- container->set_user_settings(settings);
+ if (container != nullptr)
+ {
+ // fetch current settings
+ render_container::user_settings settings = container->get_user_settings();
+
+ // fetch color controls
+ settings.m_brightness = screennode->get_attribute_float("brightness", settings.m_brightness);
+ settings.m_contrast = screennode->get_attribute_float("contrast", settings.m_contrast);
+ settings.m_gamma = screennode->get_attribute_float("gamma", settings.m_gamma);
+
+ // fetch positioning controls
+ settings.m_xoffset = screennode->get_attribute_float("hoffset", settings.m_xoffset);
+ settings.m_xscale = screennode->get_attribute_float("hstretch", settings.m_xscale);
+ settings.m_yoffset = screennode->get_attribute_float("voffset", settings.m_yoffset);
+ settings.m_yscale = screennode->get_attribute_float("vstretch", settings.m_yscale);
+
+ // set the new values
+ container->set_user_settings(settings);
+ }
}
}
@@ -3265,8 +3644,8 @@ void render_manager::config_load(config_type cfg_type, util::xml::data_node cons
void render_manager::config_save(config_type cfg_type, util::xml::data_node *parentnode)
{
- // we only care about game files
- if (cfg_type != config_type::GAME)
+ // we only save system-specific configuration
+ if (cfg_type != config_type::SYSTEM)
return;
// write out the interface target
@@ -3279,22 +3658,26 @@ void render_manager::config_save(config_type cfg_type, util::xml::data_node *par
}
// iterate over targets
- for (int targetnum = 0; targetnum < 1000; targetnum++)
+ for (int targetnum = 0; ; ++targetnum)
{
// get this target and break when we fail
render_target *target = target_by_index(targetnum);
- if (target == nullptr)
+ if (!target)
+ {
break;
-
- // create a node
- util::xml::data_node *const targetnode = parentnode->add_child("target", nullptr);
- if (targetnode != nullptr && !target->config_save(*targetnode))
- targetnode->delete_node();
+ }
+ else if (!target->hidden())
+ {
+ // create a node
+ util::xml::data_node *const targetnode = parentnode->add_child("target", nullptr);
+ if (targetnode && !target->config_save(*targetnode))
+ targetnode->delete_node();
+ }
}
// iterate over screen containers
int scrnum = 0;
- for (render_container *container = m_screen_container_list.first(); container != nullptr; container = container->next(), scrnum++)
+ for (render_container &container : m_screen_container_list)
{
// create a node
util::xml::data_node *const screennode = parentnode->add_child("screen", nullptr);
@@ -3305,8 +3688,7 @@ void render_manager::config_save(config_type cfg_type, util::xml::data_node *par
// output the basics
screennode->set_attribute_int("index", scrnum);
- render_container::user_settings settings;
- container->get_user_settings(settings);
+ render_container::user_settings const settings = container.get_user_settings();
// output the color controls
if (settings.m_brightness != machine().options().brightness())
@@ -3356,5 +3738,7 @@ void render_manager::config_save(config_type cfg_type, util::xml::data_node *par
if (!changed)
screennode->delete_node();
}
+
+ scrnum++;
}
}