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.cpp696
1 files changed, 496 insertions, 200 deletions
diff --git a/src/emu/render.cpp b/src/emu/render.cpp
index 1f9918539d5..571f4e79084 100644
--- a/src/emu/render.cpp
+++ b/src/emu/render.cpp
@@ -41,6 +41,7 @@
#include "corestr.h"
#include "emuopts.h"
+#include "fileio.h"
#include "rendfont.h"
#include "rendlay.h"
#include "rendutil.h"
@@ -56,6 +57,7 @@
#include "util/xmlfile.h"
#include <algorithm>
+#include <limits>
@@ -98,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
@@ -300,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;
}
@@ -352,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;
}
@@ -430,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;
@@ -690,7 +729,7 @@ const rgb_t *render_container::bcg_lookup_table(int texformat, u32 &out_length,
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];
@@ -880,19 +919,21 @@ 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_ui_container(ui)
, m_curview(0U)
, m_flags(flags)
, m_listindex(0)
@@ -1012,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;
}
@@ -1027,9 +1068,12 @@ void render_target::set_view(unsigned viewindex)
{
if (m_views.size() > viewindex)
{
+ 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());
}
}
@@ -1047,6 +1091,229 @@ void render_target::set_max_texture_size(int maxwidth, int maxheight)
//-------------------------------------------------
+// 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
+//-------------------------------------------------
+
+void render_target::pointer_left(
+ osd::ui_event_handler::pointer type,
+ u16 ptrid,
+ u16 device,
+ s32 x,
+ s32 y,
+ u32 released,
+ s16 clicks)
+{
+ auto const target_f(map_point_layout(x, y));
+ current_view().pointer_left(type, ptrid, device, target_f.first, target_f.second, released, clicks);
+
+ // store the updated state if relevant
+ if (m_pointers.size() > ptrid)
+ {
+ m_pointers[ptrid].newpos = std::make_pair(std::numeric_limits<float>::min(), std::numeric_limits<float>::min());
+ m_pointers[ptrid].newbuttons = 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)
+ {
+ 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)
+ {
+ 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();
+ }
+}
+
+
+//-------------------------------------------------
+// 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()))
+ {
+ --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;
+ }
+ }
+
+ // check for moving across vertical edges
+ if (y < m_pointers[ptr].oldpos.second)
+ {
+ while (edges.second && (y < y_edges[edges.second - 1].position()))
+ {
+ --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;
+ }
+
+ // 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;
+ }
+}
+
+
+//-------------------------------------------------
// set_visibility_toggle - show or hide selected
// parts of a view
//-------------------------------------------------
@@ -1058,7 +1325,7 @@ void render_target::set_visibility_toggle(unsigned index, bool 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());
+ update_layer_config();
current_view().preload();
}
@@ -1070,28 +1337,29 @@ void render_target::set_visibility_toggle(unsigned index, bool enable)
unsigned render_target::configured_view(const char *viewname, int targetindex, int numtargets)
{
- layout_view *view = nullptr;
-
// 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; !view && (m_views.size() > i); ++i)
+ for (unsigned i = 0; m_views.size() > i; ++i)
+ {
if (!core_strnicmp(m_views[i].first.name().c_str(), viewname, viewlen))
- view = &m_views[i].first;
+ 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 (!view && !screens.empty())
+ 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)
{
@@ -1109,22 +1377,21 @@ unsigned render_target::configured_view(const char *viewname, int targetindex, i
}
}
}
+ if (view)
+ return view_index(*view);
}
// otherwise, find the first view that has all the screens
- if (!view)
+ for (unsigned i = 0; m_views.size() > i; ++i)
{
- for (unsigned i = 0; !view && (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())
- view = &curview;
- }
+ 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;
}
}
- // make sure it's a valid view
- return view ? view_index(*view) : 0;
+ // default to the first view
+ return 0;
}
@@ -1197,63 +1464,103 @@ void render_target::compute_visible_area(s32 target_width, s32 target_height, fl
// 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;
+ // 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)
- {
- bool is_rotated = (m_manager.machine().system().flags & ORIENTATION_SWAP_XY) ^ (target_orientation & ORIENTATION_SWAP_XY);
- scale_mode = is_rotated ? SCALE_FRACTIONAL_Y : SCALE_FRACTIONAL_X;
- }
+ 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;
- // first compute scale factors to fit the screen
- float xscale = (float)target_width / src_width;
- float yscale = (float)target_height / src_height;
+ // 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);
- // apply aspect correction
- if (m_keepaspect)
- {
- if (target_aspect > src_aspect)
- xscale *= src_aspect / target_aspect;
- else
- yscale *= target_aspect / src_aspect;
- }
+ // 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;
- bool x_fits = render_round_nearest(xscale) * src_width <= target_width;
- bool y_fits = render_round_nearest(yscale) * src_height <= target_height;
+ // 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);
- // compute integer scale factors
- float integer_x = std::max(1.0f, float(m_int_overscan || x_fits ? render_round_nearest(xscale) : floor(xscale)));
- float integer_y = std::max(1.0f, float(m_int_overscan || y_fits ? render_round_nearest(yscale) : floor(yscale)));
- // check if we have user defined scale factors, if so use them instead
- integer_x = m_int_scale_x > 0 ? m_int_scale_x : integer_x;
- integer_y = m_int_scale_y > 0 ? m_int_scale_y : integer_y;
+ // 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;
- // now apply desired scale mode
- if (scale_mode == SCALE_FRACTIONAL_X)
+ // 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)
{
- if (m_keepaspect) xscale *= integer_y / yscale;
- yscale = integer_y;
+ 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;
}
- else if (scale_mode == SCALE_FRACTIONAL_Y)
- {
- if (m_keepaspect) yscale *= integer_x / xscale;
- xscale = integer_x;
- }
- else
+
+ // 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)
{
- xscale = integer_x;
- yscale = integer_y;
+ // 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))};
+
+ 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)));
+ }
+
+ // 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;
+
+ // 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;
}
}
@@ -1338,10 +1645,6 @@ 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)
- m_base_view = &current_view();
-
// switch to the next primitive list
render_primitive_list &list = m_primlist[m_listindex];
m_listindex = (m_listindex + 1) % std::size(m_primlist);
@@ -1415,8 +1718,8 @@ render_primitive_list &render_target::get_primitives()
}
}
- // 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;
@@ -1429,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
@@ -1450,7 +1753,7 @@ bool render_target::map_point_container(s32 target_x, s32 target_y, render_conta
std::pair<float, float> target_f(map_point_internal(target_x, target_y));
// explicitly check for the UI container
- if (&container == &m_manager.ui_container())
+ if (&container == m_ui_container)
{
// this hit test went against the UI container
if ((target_f.first >= 0.0f) && (target_f.first < 1.0f) && (target_f.second >= 0.0f) && (target_f.second < 1.0f))
@@ -1497,74 +1800,6 @@ bool render_target::map_point_container(s32 target_x, s32 target_y, render_conta
//-------------------------------------------------
-// map_point_input - attempts to map a point on
-// the specified render_target to an input port
-// field, 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)
-{
- std::pair<float, float> target_f(map_point_internal(target_x, target_y));
- 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);
-
- auto const &items(current_view().interactive_items());
- m_hit_test.resize(items.size() * 2);
- std::fill(m_hit_test.begin(), m_hit_test.end(), false);
-
- for (auto const &edge : current_view().interactive_edges_x())
- {
- if ((edge.position() > target_f.first) || ((edge.position() == target_f.first) && edge.trailing()))
- break;
- else
- m_hit_test[edge.index()] = !edge.trailing();
- }
-
- for (auto const &edge : current_view().interactive_edges_y())
- {
- if ((edge.position() > target_f.second) || ((edge.position() == target_f.second) && edge.trailing()))
- break;
- else
- m_hit_test[items.size() + edge.index()] = !edge.trailing();
- }
-
- for (unsigned i = 0; items.size() > i; ++i)
- {
- if (m_hit_test[i] && m_hit_test[items.size() + i])
- {
- layout_view_item &item(items[i]);
- render_bounds const bounds(item.bounds());
- if (bounds.includes(target_f.first, target_f.second))
- {
- if (item.has_input())
- {
- // point successfully mapped
- std::tie(input_port, input_mask) = item.input_tag_and_mask();
- input_x = (target_f.first - bounds.x0) / bounds.width();
- input_y = (target_f.second - bounds.y0) / bounds.height();
- return true;
- }
- else
- {
- break;
- }
- }
- }
- }
-
- // default to point not mapped
- input_port = nullptr;
- input_mask = 0;
- input_x = input_y = -1.0f;
- return false;
-}
-
-
-//-------------------------------------------------
// invalidate_all - if any of our primitive lists
// contain a reference to the given pointer,
// clear them
@@ -1593,7 +1828,7 @@ void render_target::resolve_tags()
for (layout_file &file : m_filelist)
file.resolve_tags();
- current_view().recompute(visibility_mask(), m_layerconfig.zoom_to_screen());
+ update_layer_config();
current_view().preload();
}
@@ -1605,7 +1840,10 @@ void render_target::resolve_tags()
void render_target::update_layer_config()
{
+ 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());
}
@@ -1668,14 +1906,6 @@ void render_target::load_additional_layout_files(const char *basename, bool have
else
m_external_artwork = true;
- // if a default view has been specified, use that as a fallback
- 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);
while (0 <= cloneof)
@@ -1693,6 +1923,14 @@ void render_target::load_additional_layout_files(const char *basename, bool have
cloneof = driver_list::clone(parent);
}
+ // if a default view has been specified, use that as a fallback
+ 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); });
+
have_artwork |= m_external_artwork;
// Use fallback artwork if defined and no artwork has been found yet
@@ -2080,11 +2318,10 @@ bool render_target::load_layout_file(const char *dirname, const internal_layout
size_t decompressed = 0;
do
{
- size_t actual;
- std::error_condition const err = inflater->read(
+ auto const [err, actual] = read(
+ *inflater,
&tempout[decompressed],
- layout_data.decompressed_size - decompressed,
- actual);
+ layout_data.decompressed_size - decompressed);
decompressed += actual;
if (err)
{
@@ -2177,7 +2414,7 @@ bool render_target::load_layout_file(device_t &device, util::xml::data_node cons
{
m_filelist.emplace_back(device, rootnode, searchpath, dirname);
}
- catch (emu_fatalerror &err)
+ catch (emu_fatalerror const &err)
{
osd_printf_warning("%s\n", err.what());
return false;
@@ -2313,11 +2550,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;
@@ -2350,7 +2587,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))
@@ -2410,7 +2647,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))
@@ -2426,7 +2663,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;
@@ -2551,7 +2788,7 @@ void render_target::add_element_primitives(render_primitive_list &list, const ob
}
// add to the list or free if we're clipped out
- bool const clipped = render_clip_quad(&prim->bounds, &cliprect, &prim->texcoords);
+ bool const clipped = render_clip_quad(prim->bounds, cliprect, &prim->texcoords);
list.append_or_return(*prim, clipped);
}
}
@@ -2582,6 +2819,25 @@ std::pair<float, float> render_target::map_point_internal(s32 target_x, s32 targ
//-------------------------------------------------
+// map_point_layout - map point from screen
+// coordinates to layout coordinates
+//-------------------------------------------------
+
+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;
+}
+
+
+//-------------------------------------------------
// view_name - return the name of the indexed
// view, or nullptr if it doesn't exist
//-------------------------------------------------
@@ -2613,30 +2869,40 @@ 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)
{
+ // 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
- int const zoom = targetnode.get_attribute_int("zoom", -1);
+ int const zoom = targetnode->get_attribute_int("zoom", -1);
if (zoom == 0 || zoom == 1)
set_zoom_to_screen(zoom);
// apply orientation
- int rotate = targetnode.get_attribute_int("rotate", -1);
+ int rotate = targetnode->get_attribute_int("rotate", -1);
if (rotate != -1)
{
if (rotate == 90)
@@ -2650,17 +2916,16 @@ void render_target::config_load(util::xml::data_node const &targetnode)
set_orientation(orientation_add(rotate, orientation()));
// apply the opposite orientation to the UI
- if (is_ui_target())
+ if (m_ui_container)
{
- render_container &ui_container = m_manager.ui_container();
- render_container::user_settings settings = ui_container.get_user_settings();
+ render_container::user_settings settings = m_ui_container->get_user_settings();
settings.m_orientation = orientation_add(orientation_reverse(rotate), settings.m_orientation);
- ui_container.set_user_settings(settings);
+ 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"))
+ 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)
@@ -2695,6 +2960,8 @@ void render_target::config_load(util::xml::data_node const &targetnode)
{
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());
}
}
}
@@ -3056,7 +3323,6 @@ render_manager::render_manager(running_machine &machine)
, m_ui_target(nullptr)
, m_live_textures(0)
, m_texture_id(0)
- , m_ui_container(std::make_unique<render_container>(*this))
{
// register callbacks
machine.configuration().config_register(
@@ -3077,7 +3343,7 @@ render_manager::render_manager(running_machine &machine)
render_manager::~render_manager()
{
// free all the containers since they may own textures
- m_ui_container.reset();
+ m_ui_containers.clear();
m_screen_container_list.clear();
// better not be any outstanding textures when we die
@@ -3133,12 +3399,14 @@ float render_manager::max_update_rate() const
render_target *render_manager::target_alloc(const internal_layout *layoutfile, u32 flags)
{
- return &m_targetlist.append(*new 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(*new 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));
}
@@ -3175,35 +3443,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.get() || 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
@@ -3211,12 +3500,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);
}
@@ -3297,8 +3581,20 @@ void render_manager::resolve_tags()
void render_manager::config_load(config_type cfg_type, config_level cfg_level, util::xml::data_node const *parentnode)
{
// we only care about system-specific configuration with matching nodes
- if ((cfg_type != config_type::SYSTEM) || !parentnode)
+ 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;
+ }
+ else if ((cfg_type != config_type::SYSTEM) || !parentnode)
+ {
+ return;
+ }
// check the UI target
util::xml::data_node const *const uinode = parentnode->get_child("interface");
@@ -3314,7 +3610,7 @@ void render_manager::config_load(config_type cfg_type, config_level cfg_level, u
{
render_target *const target = target_by_index(targetnode->get_attribute_int("index", -1));
if (target && !target->hidden())
- target->config_load(*targetnode);
+ target->config_load(targetnode);
}
// iterate over screen nodes