From 34cbb3a25e65201f2884d54c78ef96b6a3997ad9 Mon Sep 17 00:00:00 2001 From: Vas Crabb Date: Sat, 12 Sep 2020 02:03:47 +1000 Subject: emu/render.cpp: Apply target orientation when mapping points to layout elements. Addresses MT07375. Also increase efficiency when layouts contain large numbers of non-interactive elements (e.g. thousands of matrix display dots). Also includes work in progress for future improvements. --- src/emu/render.cpp | 191 +++++++++++++++++++++++++++++++--------------------- src/emu/render.h | 89 ++++++++++++++++++------ src/emu/rendlay.cpp | 111 ++++++++++++++++++++++-------- 3 files changed, 267 insertions(+), 124 deletions(-) diff --git a/src/emu/render.cpp b/src/emu/render.cpp index 5a22b919e1c..221ce9fc929 100644 --- a/src/emu/render.cpp +++ b/src/emu/render.cpp @@ -1447,21 +1447,115 @@ 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 target_f(map_point_internal(target_x, target_y)); + + // explicitly check for the UI container + if (&container == &m_manager.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)) + { + // this point was successfully mapped + container_x = float(target_x) / m_width; + container_y = float(target_y) / m_height; + return true; + } + } + 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().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) + { + layout_view::item &item(*found); + if (item.bounds().includes(target_f.first, target_f.second)) + { + // point successfully mapped + container_x = (target_f.first - item.bounds().x0) / item.bounds().width(); + container_y = (target_f.second - item.bounds().y0) / item.bounds().height(); + return true; + } + } + } + + // default to point not mapped + container_x = container_y = -1.0f; + return false; } //------------------------------------------------- // map_point_input - attempts to map a point on -// the specified render_target to the specified -// container, if possible +// 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) { - return map_point_internal(target_x, target_y, nullptr, input_x, input_y, input_port, input_mask); + std::pair 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]); + if (item.has_input()) + { + // point successfully mapped + input_port = item.input_tag_and_mask(input_mask); + input_x = (target_f.first - item.bounds().x0) / item.bounds().width(); + input_y = (target_f.second - item.bounds().y0) / item.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; } @@ -1528,6 +1622,8 @@ void render_target::resolve_tags() for (layout_view &view : file.views()) { view.resolve_tags(); + if (¤t_view() == &view) + view.recompute(visibility_mask(), m_layerconfig.zoom_to_screen()); } } } @@ -2468,7 +2564,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 render_target::map_point_internal(s32 target_x, s32 target_y) { // compute the visible width/height s32 viswidth, visheight; @@ -2476,65 +2572,14 @@ 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 : current_view().items()) - { - if ((visibility_mask() & item.visibility_mask()) != item.visibility_mask()) - continue; - - bool checkit; - - // if we're looking for a particular container, verify that we have the right one - 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; - } - } - - return false; + 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); } @@ -2556,18 +2601,12 @@ layout_view *render_target::view_by_index(unsigned 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.get() == &targetview) + return index; + } return 0; } diff --git a/src/emu/render.h b/src/emu/render.h index 8aff6b73af9..b0d0df82d3c 100644 --- a/src/emu/render.h +++ b/src/emu/render.h @@ -55,6 +55,7 @@ #include #include #include +#include #include #include #include @@ -184,6 +185,7 @@ struct render_bounds constexpr float width() const { return x1 - x0; } constexpr float height() const { return y1 - y0; } constexpr float aspect() const { return width() / height(); } + constexpr bool includes(float x, float y) const { return (x >= x0) && (x <= x1) && (y >= y0) && (y <= y1); } }; @@ -738,7 +740,7 @@ public: using view_environment = emu::render::detail::view_environment; using element_map = std::unordered_map; using group_map = std::unordered_map; - using render_screen_list = std::list>; + using screen_ref_vector = std::vector >; /// \brief A single item in a view /// @@ -769,8 +771,11 @@ public: u32 visibility_mask() const { return m_visibility_mask; } int orientation() const { return m_orientation; } render_container *screen_container(running_machine &machine) const; + + // interactivity bool has_input() const { return bool(m_input_port); } ioport_port *input_tag_and_mask(ioport_value &mask) const { mask = m_input_mask; return m_input_port; }; + bool clickthrough() const { return m_clickthrough; } // fetch state based on configured source int state() const; @@ -778,34 +783,37 @@ public: // resolve tags, if any void resolve_tags(); - // setters - void set_blend_mode(int mode) { m_blend_mode = mode; } - private: static layout_element *find_element(view_environment &env, util::xml::data_node const &itemnode, element_map &elemmap); static render_bounds make_bounds(view_environment &env, util::xml::data_node const &itemnode, layout_group::transform const &trans); static std::string make_input_tag(view_environment &env, util::xml::data_node const &itemnode); static int get_blend_mode(view_environment &env, util::xml::data_node const &itemnode); + static unsigned get_input_shift(ioport_value mask); // internal state layout_element *const m_element; // pointer to the associated element (non-screens only) output_finder<> m_output; // associated output bool const m_have_output; // whether we actually have an output - std::string const m_input_tag; // input tag of this item ioport_port * m_input_port; // input port of this item ioport_field const * m_input_field; // input port field of this item ioport_value const m_input_mask; // input mask of this item - u8 m_input_shift; // input mask rightshift for raw (trailing 0s) + u8 const m_input_shift; // input mask rightshift for raw (trailing 0s) bool const m_input_raw; // get raw data from input port + bool m_clickthrough; // should click pass through to lower elements screen_device * m_screen; // pointer to screen int m_orientation; // orientation of this item render_bounds m_bounds; // bounds of the item - render_bounds const m_rawbounds; // raw (original) bounds of the item render_color m_color; // color of the item int m_blend_mode; // blending mode to use when drawing u32 m_visibility_mask; // combined mask of parent visibility groups + + // cold items + std::string const m_input_tag; // input tag of this item + render_bounds const m_rawbounds; // raw (original) bounds of the item + bool const m_has_clickthrough; // whether clickthrough was explicitly configured }; using item_list = std::list; + using item_ref_vector = std::vector >; /// \brief A subset of items in a view that can be hidden or shown /// @@ -831,6 +839,36 @@ public: }; using visibility_toggle_vector = std::vector; + /// \brief An edge of an item in a view + class edge + { + public: + // construction/destruction + constexpr edge(unsigned index, float position, bool trailing) + : m_index(index) + , m_position(position) + , m_trailing(trailing) + { + } + + // getters + constexpr unsigned index() const { return m_index; } + constexpr float position() const { return m_position; } + constexpr bool trailing() const { return m_trailing; } + + // comparison + constexpr bool operator<(edge const &that) const + { + return std::make_tuple(m_position, m_trailing, m_index) < std::make_tuple(that.m_position, that.m_trailing, that.m_index); + } + + private: + unsigned m_index; // index of item in some collection + float m_position; // position of edge on given axis + bool m_trailing; // false for edge at lower position on axis + }; + using edge_vector = std::vector; + // construction/destruction layout_view( layout_environment &env, @@ -842,14 +880,18 @@ public: // getters item_list &items() { return m_items; } const std::string &name() const { return m_name; } - const render_screen_list &screens() const { return m_screens; } size_t screen_count() const { return m_screens.size(); } float effective_aspect() const { return m_effaspect; } const render_bounds &bounds() const { return m_bounds; } bool has_screen(screen_device &screen) const; - bool has_art() const { return m_has_art; } - u32 default_visibility_mask() const { return m_defvismask; } + const item_ref_vector &screen_items() const { return m_screen_items; } + const item_ref_vector &interactive_items() const { return m_interactive_items; } + const edge_vector &interactive_edges_x() const { return m_interactive_edges_x; } + const edge_vector &interactive_edges_y() const { return m_interactive_edges_y; } + const screen_ref_vector &screens() const { return m_screens; } const visibility_toggle_vector &visibility_toggles() const { return m_vistoggles; } + u32 default_visibility_mask() const { return m_defvismask; } + bool has_art() const { return m_has_art; } // operations void recompute(u32 visibility_mask, bool zoom_to_screens); @@ -877,15 +919,21 @@ private: static std::string make_name(layout_environment &env, util::xml::data_node const &viewnode); // internal state - std::string m_name; // name of the layout - render_screen_list m_screens; // list screens visible in current configuration - float m_effaspect; // X/Y of the layout in current configuration - render_bounds m_bounds; // computed bounds of the view in current configuration - render_bounds m_expbounds; // explicit bounds of the view - item_list m_items; // list of layout items - bool m_has_art; // true if the layout contains non-screen elements - u32 m_defvismask; // default visibility mask - visibility_toggle_vector m_vistoggles; + std::string m_name; // name of the layout + float m_effaspect; // X/Y of the layout in current configuration + render_bounds m_bounds; // computed bounds of the view in current configuration + item_list m_items; // list of layout items + item_ref_vector m_screen_items; // visible items that represent screens to draw + item_ref_vector m_interactive_items;// visible items that can accept pointer input + edge_vector m_interactive_edges_x; + edge_vector m_interactive_edges_y; + screen_ref_vector m_screens; // list screens visible in current configuration + + // cold items + visibility_toggle_vector m_vistoggles; // collections of items that can be shown/hidden + render_bounds m_expbounds; // explicit bounds of the view + u32 m_defvismask; // default visibility mask + bool m_has_art; // true if the layout contains non-screen elements }; @@ -1026,7 +1074,7 @@ private: bool load_layout_file(device_t &device, const char *dirname, util::xml::data_node const &rootnode); void add_container_primitives(render_primitive_list &list, const object_transform &root_xform, const object_transform &xform, render_container &container, int blendmode); void add_element_primitives(render_primitive_list &list, const object_transform &xform, layout_element &element, int state, int blendmode); - bool 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 map_point_internal(s32 target_x, s32 target_y); // config callbacks void config_load(util::xml::data_node const &targetnode); @@ -1067,6 +1115,7 @@ private: float m_max_refresh; // maximum refresh rate, 0 or if none int m_orientation; // orientation render_layer_config m_layerconfig; // layer configuration + std::vector m_hit_test; // used when mapping points to inputs layout_view * m_base_view; // the view at the time of first frame int m_base_orientation; // the orientation at the time of first frame render_layer_config m_base_layerconfig; // the layer configuration at the time of first frame diff --git a/src/emu/rendlay.cpp b/src/emu/rendlay.cpp index 7d9eed28bf3..72c50a3db3b 100644 --- a/src/emu/rendlay.cpp +++ b/src/emu/rendlay.cpp @@ -33,8 +33,9 @@ #include #define LOG_GROUP_BOUNDS_RESOLUTION (1U << 1) +#define LOG_INTERACTIVE_ITEMS (1U << 2) -//#define VERBOSE (LOG_GROUP_BOUNDS_RESOLUTION) +//#define VERBOSE (LOG_GROUP_BOUNDS_RESOLUTION | LOG_INTERACTIVE_ITEMS) #define LOG_OUTPUT_FUNC osd_printf_verbose #include "logmacro.h" @@ -3060,8 +3061,8 @@ layout_view::layout_view( : m_name(make_name(env, viewnode)) , m_effaspect(1.0f) , m_items() - , m_has_art(false) , m_defvismask(0U) + , m_has_art(false) { // parse the layout m_expbounds.x0 = m_expbounds.y0 = m_expbounds.x1 = m_expbounds.y1 = 0; @@ -3122,7 +3123,7 @@ layout_view::layout_view( { // screens (-1) + overlays (RGB multiply) + backdrop (add) + bezels (alpha) + cpanels (alpha) + marquees (alpha) for (item &backdrop : layers.backdrops) - backdrop.set_blend_mode(BLENDMODE_ADD); + backdrop.m_blend_mode = BLENDMODE_ADD; m_items.splice(m_items.end(), layers.screens); m_items.splice(m_items.end(), layers.overlays); m_items.splice(m_items.end(), layers.backdrops); @@ -3137,7 +3138,7 @@ layout_view::layout_view( for (item &screen : layers.screens) { if (screen.blend_mode() == -1) - screen.set_blend_mode(BLENDMODE_ADD); + screen.m_blend_mode = BLENDMODE_ADD; } m_items.splice(m_items.end(), layers.backdrops); m_items.splice(m_items.end(), layers.screens); @@ -3180,9 +3181,13 @@ bool layout_view::has_screen(screen_device &screen) const void layout_view::recompute(u32 visibility_mask, bool zoom_to_screen) { - // reset the bounds + // reset the bounds and collected active items render_bounds scrbounds{ 0.0f, 0.0f, 0.0f, 0.0f }; m_bounds = scrbounds; + m_screen_items.clear(); + m_interactive_items.clear(); + m_interactive_edges_x.clear(); + m_interactive_edges_y.clear(); m_screens.clear(); // loop over items and filter by visibility mask @@ -3200,7 +3205,7 @@ void layout_view::recompute(u32 visibility_mask, bool zoom_to_screen) first = false; // accumulate visible screens and their bounds bounds - if (curitem.m_screen) + if (curitem.screen()) { if (scrfirst) scrbounds = curitem.m_rawbounds; @@ -3208,9 +3213,14 @@ void layout_view::recompute(u32 visibility_mask, bool zoom_to_screen) union_render_bounds(scrbounds, curitem.m_rawbounds); scrfirst = false; - // accumulate the screens in use while we're scanning - m_screens.emplace_back(*curitem.m_screen); + // accumulate active screens + m_screen_items.emplace_back(curitem); + m_screens.emplace_back(*curitem.screen()); } + + // accumulate interactive elements + if (!curitem.clickthrough() || curitem.has_input()) + m_interactive_items.emplace_back(curitem); } } @@ -3239,8 +3249,8 @@ void layout_view::recompute(u32 visibility_mask, bool zoom_to_screen) // determine the scale/offset for normalization float const xoffs = m_bounds.x0; float const yoffs = m_bounds.y0; - float const xscale = (target_bounds.x1 - target_bounds.x0) / (m_bounds.x1 - m_bounds.x0); - float const yscale = (target_bounds.y1 - target_bounds.y0) / (m_bounds.y1 - m_bounds.y0); + float const xscale = target_bounds.width() / m_bounds.width(); + float const yscale = target_bounds.height() / m_bounds.height(); // normalize all the item bounds for (item &curitem : items()) @@ -3250,6 +3260,32 @@ void layout_view::recompute(u32 visibility_mask, bool zoom_to_screen) curitem.m_bounds.y0 = target_bounds.y0 + (curitem.m_rawbounds.y0 - yoffs) * yscale; curitem.m_bounds.y1 = target_bounds.y0 + (curitem.m_rawbounds.y1 - yoffs) * yscale; } + + // sort edges of interactive items + LOGMASKED(LOG_INTERACTIVE_ITEMS, "Recalculated view '%s' with %u interactive items\n", + name(), m_interactive_items.size()); + m_interactive_edges_x.reserve(m_interactive_items.size() * 2); + m_interactive_edges_y.reserve(m_interactive_items.size() * 2); + for (unsigned i = 0; m_interactive_items.size() > i; ++i) + { + item &curitem(m_interactive_items[i]); + LOGMASKED(LOG_INTERACTIVE_ITEMS, "%u: (%s %s %s %s) hasinput=%s clickthrough=%s\n", + i, curitem.bounds().x0, curitem.bounds().y0, curitem.bounds().x1, curitem.bounds().y1, curitem.has_input(), curitem.clickthrough()); + m_interactive_edges_x.emplace_back(i, curitem.bounds().x0, false); + m_interactive_edges_x.emplace_back(i, curitem.bounds().x1, true); + m_interactive_edges_y.emplace_back(i, curitem.bounds().y0, false); + m_interactive_edges_y.emplace_back(i, curitem.bounds().y1, true); + } + std::sort(m_interactive_edges_x.begin(), m_interactive_edges_x.end()); + std::sort(m_interactive_edges_y.begin(), m_interactive_edges_y.end()); + + if (VERBOSE & LOG_INTERACTIVE_ITEMS) + { + for (edge const &e : m_interactive_edges_x) + LOGMASKED(LOG_INTERACTIVE_ITEMS, "x=%s %c%u\n", e.position(), e.trailing() ? ']' : '[', e.index()); + for (edge const &e : m_interactive_edges_y) + LOGMASKED(LOG_INTERACTIVE_ITEMS, "y=%s %c%u\n", e.position(), e.trailing() ? ']' : '[', e.index()); + } } @@ -3469,25 +3505,25 @@ layout_view::item::item( : m_element(find_element(env, itemnode, elemmap)) , m_output(env.device(), env.get_attribute_string(itemnode, "name", "")) , m_have_output(env.get_attribute_string(itemnode, "name", "")[0]) - , m_input_tag(make_input_tag(env, itemnode)) , m_input_port(nullptr) , m_input_field(nullptr) , m_input_mask(env.get_attribute_int(itemnode, "inputmask", 0)) - , m_input_shift(0) - , m_input_raw(0 != env.get_attribute_int(itemnode, "inputraw", 0)) + , m_input_shift(get_input_shift(m_input_mask)) + , m_input_raw(env.get_attribute_bool(itemnode, "inputraw", 0)) + , m_clickthrough(env.get_attribute_bool(itemnode, "clickthrough", "yes")) , m_screen(nullptr) , m_orientation(orientation_add(env.parse_orientation(itemnode.get_child("orientation")), orientation)) - , m_rawbounds(make_bounds(env, itemnode, trans)) , m_color(render_color_multiply(env.parse_color(itemnode.get_child("color")), color)) , m_blend_mode(get_blend_mode(env, itemnode)) , m_visibility_mask(env.visibility_mask()) + , m_input_tag(make_input_tag(env, itemnode)) + , m_rawbounds(make_bounds(env, itemnode, trans)) + , m_has_clickthrough(env.get_attribute_string(itemnode, "clickthrough", "")[0]) { // fetch common data int index = env.get_attribute_int(itemnode, "index", -1); if (index != -1) m_screen = screen_device_iterator(env.machine().root_device()).byindex(index); - for (u32 mask = m_input_mask; (mask != 0) && (~mask & 1); mask >>= 1) - m_input_shift++; // sanity checks if (strcmp(itemnode.get_name(), "screen") == 0) @@ -3544,24 +3580,22 @@ int layout_view::item::state() const // if configured to track an output, fetch its value return m_output; } - else if (!m_input_tag.empty()) + else if (m_input_port) { // if configured to an input, fetch the input value - if (m_input_port) + if (m_input_raw) { - if (m_input_raw) - { - return (m_input_port->read() & m_input_mask) >> m_input_shift; - } - else - { - ioport_field const *const field(m_input_field ? m_input_field : m_input_port->field(m_input_mask)); - if (field) - return ((m_input_port->read() ^ field->defvalue()) & m_input_mask) ? 1 : 0; - } + return (m_input_port->read() & m_input_mask) >> m_input_shift; + } + else + { + ioport_field const *const field(m_input_field ? m_input_field : m_input_port->field(m_input_mask)); + if (field) + return ((m_input_port->read() ^ field->defvalue()) & m_input_mask) ? 1 : 0; } } + // default to zero return 0; } @@ -3584,6 +3618,7 @@ void layout_view::item::resolve_tags() m_input_port = m_element->machine().root_device().ioport(m_input_tag); if (m_input_port) { + // if there's a matching unconditional field, cache it for (ioport_field &field : m_input_port->fields()) { if (field.mask() & m_input_mask) @@ -3593,6 +3628,10 @@ void layout_view::item::resolve_tags() break; } } + + // if clickthrough isn't explicitly configured, having an I/O port implies false + if (!m_has_clickthrough) + m_clickthrough = false; } } } @@ -3680,6 +3719,22 @@ int layout_view::item::get_blend_mode(view_environment &env, util::xml::data_nod } +//--------------------------------------------- +// get_input_shift - shift to right-align LSB +//--------------------------------------------- + +unsigned layout_view::item::get_input_shift(ioport_value mask) +{ + unsigned result(0U); + while (mask && !BIT(mask, 0)) + { + ++result; + mask >>= 1; + } + return result; +} + + //************************************************************************** // LAYOUT VIEW VISIBILITY TOGGLE -- cgit v1.2.3