diff --git a/docs/source/luascript/ref-core.rst b/docs/source/luascript/ref-core.rst index f761b20f5de..7b72d90ebdb 100644 --- a/docs/source/luascript/ref-core.rst +++ b/docs/source/luascript/ref-core.rst @@ -587,9 +587,6 @@ driver.is_bios_root (read-only) driver.requires_artwork (read-only) A Boolean indicating whether the system requires external artwork to be usable. -driver.clickable_artwork (read-only) - A Boolean indicating whether the system requires clickable artwork features - to be usable. driver.unofficial (read-only) A Boolean indicating whether this is an unofficial but common user modification to a system. diff --git a/docs/source/luascript/ref-render.rst b/docs/source/luascript/ref-render.rst index eee432660dd..7cc1400b3cb 100644 --- a/docs/source/luascript/ref-render.rst +++ b/docs/source/luascript/ref-render.rst @@ -754,6 +754,10 @@ manager.machine.video.snapshot_target Properties ~~~~~~~~~~ +target.ui_container (read-only) + The :ref:`render container ` for drawing user + interface elements over this render target, or ``nil`` for hidden render + targets (targets that are not shown to the user directly). target.index (read-only) The 1-based index of the render target. This has O(n) complexity. target.width (read-only) @@ -810,6 +814,9 @@ Instantiation manager.machine.render.ui_container Gets the render container used to draw the user interface, including menus, sliders and pop-up messages. +manager.machine.render.targets[index].ui_container + Gets the render container used to draw user interface elements over a + particular render target. manager.machine.screens[tag].container Gets the render container used to draw a given screen. @@ -1145,10 +1152,16 @@ view.bounds (read-only) effective bounds of the view in its current configuration. The coordinates are in view units, which are arbitrary but assumed to have square aspect ratio. -view.has_art +view.has_art (read-only) A Boolean indicating whether the view has any non-screen items, including items that are not visible because the user has hidden the item collection that they belong to. +view.show_pointers (read/write) + A Boolean that sets whether mouse and pon pointers should be displayed for + the view. +view.hide_inactive_pointers (read/write) + A Boolean that sets whether mouse pointers for the view should be hidden + after a period of inactivity. .. _luascript-ref-renderlayitem: diff --git a/docs/source/techspecs/layout_files.rst b/docs/source/techspecs/layout_files.rst index 297521bf945..a014756a32b 100644 --- a/docs/source/techspecs/layout_files.rst +++ b/docs/source/techspecs/layout_files.rst @@ -690,6 +690,11 @@ element. This means a view can reference elements and groups that appear after it in the file, and parameters from the enclosing scope will have their final values from the end of the ``mamelayout`` element. +A ``view`` element may have a ``showpointers`` attribute to set whether mouse +and pen pointers should be shown for the view. If present, the value must be +either ``yes`` or ``no``. If the ``showpointers`` attribute is not present, pen +and mouse pointers are shown for views that contain items bound to I/O ports. + The following child elements are allowed inside a ``view`` element: bounds diff --git a/scripts/build/complay.py b/scripts/build/complay.py index f0e330240a1..6519a01de06 100755 --- a/scripts/build/complay.py +++ b/scripts/build/complay.py @@ -409,6 +409,7 @@ class LayoutChecker(Minifyer): self.views[attrs['name']] = self.format_location() elif not self.VARPATTERN.match(attrs['name']): self.handle_error('Element view has duplicate name "%s" (previous %s)' % (attrs['name'], self.views[attrs['name']])) + self.check_bool_attribute(name, attrs, 'showpointers', None) self.handlers.append((self.groupViewStartHandler, self.groupViewEndHandler)) self.variable_scopes.append({ }) self.item_ids = { } diff --git a/src/emu/rendlay.cpp b/src/emu/rendlay.cpp index de17b5dae6f..be5413f99ae 100644 --- a/src/emu/rendlay.cpp +++ b/src/emu/rendlay.cpp @@ -4000,7 +4000,17 @@ layout_view::layout_view( , m_elemmap(elemmap) , m_defvismask(0U) , m_has_art(false) + , m_show_ptr(false) + , m_ptr_time_out(true) // FIXME: add attribute for this + , m_exp_show_ptr(-1) { + // check for explicit pointer display setting + if (viewnode.get_attribute_string_ptr("showpointers")) + { + m_show_ptr = env.get_attribute_bool(viewnode, "showpointers", false); + m_exp_show_ptr = m_show_ptr ? 1 : 0; + } + // parse the layout m_expbounds.x0 = m_expbounds.y0 = m_expbounds.x1 = m_expbounds.y1 = 0; view_environment local(env, m_name.c_str()); @@ -4178,6 +4188,7 @@ void layout_view::recompute(u32 visibility_mask, bool zoom_to_screen) // loop over items and filter by visibility mask bool first = true; bool scrfirst = true; + bool haveinput = false; for (item &curitem : m_items) { if ((visibility_mask & curitem.visibility_mask()) == curitem.visibility_mask()) @@ -4209,9 +4220,15 @@ void layout_view::recompute(u32 visibility_mask, bool zoom_to_screen) // accumulate interactive elements if (!curitem.clickthrough() || curitem.has_input()) m_interactive_items.emplace_back(curitem); + if (curitem.has_input()) + haveinput = true; } } + // if show pointers isn't explicitly, update it based on visible items + if (0 > m_exp_show_ptr) + m_show_ptr = haveinput; + // if we have an explicit bounds, override it if (m_expbounds.x1 > m_expbounds.x0) m_bounds = m_expbounds; @@ -4282,6 +4299,29 @@ void layout_view::recompute(u32 visibility_mask, bool zoom_to_screen) } +//------------------------------------------------- +// set_show_pointers - set whether pointers +// should be displayed +//------------------------------------------------- + +void layout_view::set_show_pointers(bool value) noexcept +{ + m_show_ptr = value; + m_exp_show_ptr = value ? 1 : 0; +} + + +//------------------------------------------------- +// set_pointers_time_out - set whether pointers +// should be hidden after inactivity +//------------------------------------------------- + +void layout_view::set_hide_inactive_pointers(bool value) noexcept +{ + m_ptr_time_out = value; +} + + //------------------------------------------------- // set_prepare_items_callback - set handler called // before adding items to render target diff --git a/src/emu/rendlay.h b/src/emu/rendlay.h index 7edeb4b40c5..23431e7fd92 100644 --- a/src/emu/rendlay.h +++ b/src/emu/rendlay.h @@ -527,6 +527,12 @@ public: 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; } + bool show_pointers() const { return m_show_ptr; } + bool hide_inactive_pointers() const { return m_ptr_time_out; } + + // setters + void set_show_pointers(bool value) noexcept; + void set_hide_inactive_pointers(bool value) noexcept ATTR_COLD; // set handlers void set_prepare_items_callback(prepare_items_delegate &&handler) ATTR_COLD; @@ -621,6 +627,9 @@ private: 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 + bool m_show_ptr; // whether pointers should be displayed + bool m_ptr_time_out; // whether pointers should be hidden after inactivity + s8 m_exp_show_ptr; // explicitly configured pointer visibility }; diff --git a/src/frontend/mame/luaengine.cpp b/src/frontend/mame/luaengine.cpp index c97cc2988bc..6aa2d3840ec 100644 --- a/src/frontend/mame/luaengine.cpp +++ b/src/frontend/mame/luaengine.cpp @@ -1502,7 +1502,6 @@ void lua_engine::initialize() game_driver_type["no_cocktail"] = sol::property([] (game_driver const &driver) { return (driver.flags & machine_flags::NO_COCKTAIL) != 0; }); game_driver_type["is_bios_root"] = sol::property([] (game_driver const &driver) { return (driver.flags & machine_flags::IS_BIOS_ROOT) != 0; }); game_driver_type["requires_artwork"] = sol::property([] (game_driver const &driver) { return (driver.flags & machine_flags::REQUIRES_ARTWORK) != 0; }); - game_driver_type["clickable_artwork"] = sol::property([] (game_driver const &driver) { return (driver.flags & machine_flags::CLICKABLE_ARTWORK) != 0; }); game_driver_type["unofficial"] = sol::property([] (game_driver const &driver) { return (driver.flags & machine_flags::UNOFFICIAL) != 0; }); game_driver_type["no_sound_hw"] = sol::property([] (game_driver const &driver) { return (driver.flags & machine_flags::NO_SOUND_HW) != 0; }); game_driver_type["mechanical"] = sol::property([] (game_driver const &driver) { return (driver.flags & machine_flags::MECHANICAL) != 0; }); diff --git a/src/frontend/mame/luaengine_render.cpp b/src/frontend/mame/luaengine_render.cpp index 3fd0bfdba10..8f931f91dac 100644 --- a/src/frontend/mame/luaengine_render.cpp +++ b/src/frontend/mame/luaengine_render.cpp @@ -994,6 +994,8 @@ void lua_engine::initialize_render(sol::table &emu) layout_view_type["effective_aspect"] = sol::property(&layout_view::effective_aspect); layout_view_type["bounds"] = sol::property(&layout_view::bounds); layout_view_type["has_art"] = sol::property(&layout_view::has_art); + layout_view_type["show_pointers"] = sol::property(&layout_view::show_pointers, &layout_view::set_show_pointers); + layout_view_type["hide_inactive_pointers"] = sol::property(&layout_view::hide_inactive_pointers, &layout_view::set_hide_inactive_pointers); auto layout_view_item_type = sol().registry().new_usertype("layout_item", sol::no_constructor); @@ -1100,6 +1102,7 @@ void lua_engine::initialize_render(sol::table &emu) auto target_type = sol().registry().new_usertype("target", sol::no_constructor); + target_type["ui_container"] = sol::property(&render_target::ui_container); target_type["index"] = sol::property([] (render_target const &t) { return t.index() + 1; }); target_type["width"] = sol::property(&render_target::width); target_type["height"] = sol::property(&render_target::height); diff --git a/src/frontend/mame/ui/selmenu.cpp b/src/frontend/mame/ui/selmenu.cpp index 7791aaff8d1..a5645e79ca8 100644 --- a/src/frontend/mame/ui/selmenu.cpp +++ b/src/frontend/mame/ui/selmenu.cpp @@ -4202,7 +4202,6 @@ void menu_select_launch::general_info(ui_system_info const *system, game_driver str << ((flags.machine_flags() & machine_flags::MECHANICAL) ? _("Mechanical System\tYes\n") : _("Mechanical System\tNo\n")); str << ((flags.machine_flags() & machine_flags::REQUIRES_ARTWORK) ? _("Requires Artwork\tYes\n") : _("Requires Artwork\tNo\n")); - str << ((flags.machine_flags() & machine_flags::CLICKABLE_ARTWORK) ? _("Requires Clickable Artwork\tYes\n") : _("Requires Clickable Artwork\tNo\n")); if (flags.machine_flags() & machine_flags::NO_COCKTAIL) str << _("Support Cocktail\tNo\n"); str << ((flags.machine_flags() & machine_flags::IS_BIOS_ROOT) ? _("System is BIOS\tYes\n") : _("System is BIOS\tNo\n")); diff --git a/src/frontend/mame/ui/ui.cpp b/src/frontend/mame/ui/ui.cpp index a9a4fd5ceed..0c4af7b8ba5 100644 --- a/src/frontend/mame/ui/ui.cpp +++ b/src/frontend/mame/ui/ui.cpp @@ -38,6 +38,7 @@ #include "render.h" #include "cheat.h" #include "rendfont.h" +#include "rendlay.h" #include "romload.h" #include "screen.h" #include "speaker.h" @@ -165,7 +166,13 @@ enum class mame_ui_manager::ui_callback_type : int struct mame_ui_manager::active_pointer { - active_pointer(ui_event const &event) : target(event.target), type(event.pointer_type), ptrid(event.pointer_id), x(-1.0F), y(-1.0F) + active_pointer(ui_event const &event) + : target(event.target) + , updated(std::chrono::steady_clock::time_point::min()) + , type(event.pointer_type) + , ptrid(event.pointer_id) + , x(-1.0F) + , y(-1.0F) { } @@ -175,6 +182,7 @@ struct mame_ui_manager::active_pointer } render_target *target; + std::chrono::steady_clock::time_point updated; osd::ui_event_handler::pointer type; u16 ptrid; float x, y; @@ -198,8 +206,6 @@ mame_ui_manager::mame_ui_manager(running_machine &machine) , m_popup_text_end(0) , m_mouse_bitmap(32, 32) , m_mouse_arrow_texture(nullptr) - , m_mouse_show(false) - , m_update_pointers(false) , m_pointers_changed(false) , m_target_font_height(0) , m_has_warnings(false) @@ -240,7 +246,6 @@ void mame_ui_manager::init() return 0; })); m_non_char_keys_down = std::make_unique((std::size(non_char_keys) + 7) / 8); - m_mouse_show = machine().system().flags & machine_flags::CLICKABLE_ARTWORK ? true : false; // request notification callbacks machine().add_notifier(MACHINE_NOTIFY_FRAME, machine_notify_delegate(&mame_ui_manager::frame_update, this)); @@ -733,7 +738,7 @@ bool mame_ui_manager::update_and_render(render_container &container) // display the internal pointers bool const pointer_update = m_pointers_changed; m_pointers_changed = false; - if (m_mouse_show || (is_menu_active() && machine().options().ui_mouse())) + if (!is_menu_active() || machine().options().ui_mouse()) { const float cursor_size = 0.6 * get_line_height(); for (auto const &pointer : m_display_pointers) @@ -1128,12 +1133,12 @@ void mame_ui_manager::process_ui_events() { if (osd::ui_event_handler::pointer::TOUCH != event.pointer_type) { - m_update_pointers = true; auto pos(std::lower_bound(m_active_pointers.begin(), m_active_pointers.end(), std::make_pair(event.target, event.pointer_id))); if ((m_active_pointers.end() == pos) || (pos->target != event.target) || (pos->ptrid != event.pointer_id)) pos = m_active_pointers.emplace(pos, event); else assert(pos->type == event.pointer_type); + pos->updated = std::chrono::steady_clock::now(); event.target->map_point_container(event.pointer_x, event.pointer_y, *event.target->ui_container(), pos->x, pos->y); } @@ -1150,10 +1155,7 @@ void mame_ui_manager::process_ui_events() { auto const pos(std::lower_bound(m_active_pointers.begin(), m_active_pointers.end(), std::make_pair(event.target, event.pointer_id))); if (m_active_pointers.end() != pos) - { - m_update_pointers = true; m_active_pointers.erase(pos); - } event.target->pointer_left( event.pointer_type, event.pointer_id, event.pointer_device, @@ -1168,10 +1170,7 @@ void mame_ui_manager::process_ui_events() { auto const pos(std::lower_bound(m_active_pointers.begin(), m_active_pointers.end(), std::make_pair(event.target, event.pointer_id))); if (m_active_pointers.end() != pos) - { - m_update_pointers = true; m_active_pointers.erase(pos); - } event.target->pointer_aborted( event.pointer_type, event.pointer_id, event.pointer_device, @@ -1379,15 +1378,20 @@ uint32_t mame_ui_manager::handler_ingame(render_container &container) // process UI events and update pointers if necessary process_ui_events(); - if (m_update_pointers) + display_pointer_vector pointers; + pointers.reserve(m_active_pointers.size()); + auto const now(std::chrono::steady_clock::now()); + for (auto const &pointer : m_active_pointers) { - display_pointer_vector pointers; - pointers.reserve(m_active_pointers.size()); - for (auto const &pointer : m_active_pointers) - pointers.emplace_back(display_pointer{ *pointer.target, pointer.type, pointer.x, pointer.y }); - set_pointers(pointers.begin(), pointers.end()); - m_update_pointers = false; + layout_view const &view(pointer.target->current_view()); + if (view.show_pointers()) + { + // TODO: make timeout configurable + if (!view.hide_inactive_pointers() || (osd::ui_event_handler::pointer::PEN == pointer.type) || ((now - pointer.updated) <= std::chrono::seconds(3))) + pointers.emplace_back(display_pointer{ *pointer.target, pointer.type, pointer.x, pointer.y }); + } } + set_pointers(pointers.begin(), pointers.end()); if (!ui_disabled) { @@ -2400,6 +2404,12 @@ bool mame_ui_manager::set_ui_event_handler(std::function &&handler) if (ui_callback_type::GENERAL != m_handler_callback_type) return false; + for (auto *target = machine().render().first_target(); target; target = target->next()) + { + if (!target->hidden()) + target->forget_pointers(); + } + set_handler( ui_callback_type::CUSTOM, handler_callback_func( diff --git a/src/frontend/mame/ui/ui.h b/src/frontend/mame/ui/ui.h index 231a6de15cb..d213d861f34 100644 --- a/src/frontend/mame/ui/ui.h +++ b/src/frontend/mame/ui/ui.h @@ -298,8 +298,6 @@ private: display_pointer_vector m_display_pointers; bitmap_argb32 m_mouse_bitmap; render_texture * m_mouse_arrow_texture; - bool m_mouse_show; - bool m_update_pointers; bool m_pointers_changed; ui_options m_ui_options;