mirror of
https://github.com/holub/mame
synced 2025-04-23 08:49:55 +03:00
-util/xmlfile: Escape attribute and element content.
The previous behaviour was unintuitive - parsing an XML file and writing it out immediately would produce invalid XML if the file contained any characters that needed escaping. It makes far more sense to escape on writing rather than expecting the user to escape input. -Add preliminary support for visibility toggles to artwork system. This allows the user to show/hide related elements in a view, with nesting. The view can specify whether elements are shown or hidden by default. Settings are saved per host window/screen per view. There is no way to set the initial visibility state on the command line. Legacy "Space Invaders cabinet model" layers are mapped onto visibility toggles. This is not stable yet. In particular, the XML element/attribute names have not been finalised. The new features have not been added to complay.py to prevent them from being used before they're finalised.
This commit is contained in:
parent
080826aac4
commit
94601c77cc
@ -1490,7 +1490,7 @@ bool device_debug::comment_export(util::xml::data_node &curnode)
|
||||
// iterate through the comments
|
||||
for (const auto & elem : m_comment_set)
|
||||
{
|
||||
util::xml::data_node *datanode = curnode.add_child("comment", util::xml::normalize_string(elem.m_text.c_str()));
|
||||
util::xml::data_node *datanode = curnode.add_child("comment", elem.m_text.c_str());
|
||||
if (datanode == nullptr)
|
||||
return false;
|
||||
datanode->set_attribute_int("address", elem.m_address);
|
||||
|
@ -37,20 +37,22 @@
|
||||
***************************************************************************/
|
||||
|
||||
#include "emu.h"
|
||||
#include "emuopts.h"
|
||||
#include "render.h"
|
||||
|
||||
#include "emuopts.h"
|
||||
#include "rendfont.h"
|
||||
#include "rendlay.h"
|
||||
#include "rendutil.h"
|
||||
#include "config.h"
|
||||
#include "drivenum.h"
|
||||
#include "xmlfile.h"
|
||||
|
||||
#include "ui/uimain.h"
|
||||
|
||||
#include "xmlfile.h"
|
||||
|
||||
#include <zlib.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
|
||||
|
||||
|
||||
@ -893,7 +895,7 @@ render_target::render_target(render_manager &manager, util::xml::data_node const
|
||||
template <typename T> render_target::render_target(render_manager &manager, T &&layout, u32 flags, constructor_impl_t)
|
||||
: m_next(nullptr)
|
||||
, m_manager(manager)
|
||||
, m_curview(nullptr)
|
||||
, m_curview(0U)
|
||||
, m_flags(flags)
|
||||
, m_listindex(0)
|
||||
, m_width(640)
|
||||
@ -949,6 +951,10 @@ template <typename T> render_target::render_target(render_manager &manager, T &&
|
||||
|
||||
// load the layout files
|
||||
load_layout_files(std::forward<T>(layout), flags & RENDER_CREATE_SINGLE_FILE);
|
||||
for (layout_file &file : m_filelist)
|
||||
for (layout_view &view : file.views())
|
||||
if (!(m_flags & RENDER_CREATE_NO_ART) || !view.has_art())
|
||||
m_views.emplace_back(view, view.default_visibility_mask());
|
||||
|
||||
// set the current view to the first one
|
||||
set_view(0);
|
||||
@ -1010,13 +1016,12 @@ void render_target::set_bounds(s32 width, s32 height, float pixel_aspect)
|
||||
// a target
|
||||
//-------------------------------------------------
|
||||
|
||||
void render_target::set_view(int viewindex)
|
||||
void render_target::set_view(unsigned viewindex)
|
||||
{
|
||||
layout_view *view = view_by_index(viewindex);
|
||||
if (view)
|
||||
if (m_views.size() > viewindex)
|
||||
{
|
||||
m_curview = view;
|
||||
view->recompute(m_layerconfig);
|
||||
m_curview = viewindex;
|
||||
current_view().recompute(visibility_mask(), m_layerconfig.zoom_to_screen());
|
||||
}
|
||||
}
|
||||
|
||||
@ -1033,39 +1038,55 @@ void render_target::set_max_texture_size(int maxwidth, int maxheight)
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// set_visibility_toggle - show or hide selected
|
||||
// parts of a view
|
||||
//-------------------------------------------------
|
||||
|
||||
void render_target::set_visibility_toggle(unsigned index, bool enable)
|
||||
{
|
||||
assert(visibility_toggles().size() > index);
|
||||
if (enable)
|
||||
m_views[m_curview].second |= u32(1) << index;
|
||||
else
|
||||
m_views[m_curview].second &= ~(u32(1) << index);
|
||||
current_view().recompute(visibility_mask(), m_layerconfig.zoom_to_screen());
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// configured_view - select a view for this
|
||||
// target based on the configuration parameters
|
||||
//-------------------------------------------------
|
||||
|
||||
int render_target::configured_view(const char *viewname, int targetindex, int numtargets)
|
||||
unsigned render_target::configured_view(const char *viewname, int targetindex, int numtargets)
|
||||
{
|
||||
layout_view *view = nullptr;
|
||||
int viewindex;
|
||||
|
||||
// auto view just selects the nth view
|
||||
if (strcmp(viewname, "auto") != 0)
|
||||
{
|
||||
// scan for a matching view name
|
||||
size_t viewlen = strlen(viewname);
|
||||
for (view = view_by_index(viewindex = 0); view != nullptr; view = view_by_index(++viewindex))
|
||||
if (core_strnicmp(view->name().c_str(), viewname, viewlen) == 0)
|
||||
break;
|
||||
for (unsigned i = 0; !view && (m_views.size() > i); ++i)
|
||||
if (!core_strnicmp(m_views[i].first.get().name().c_str(), viewname, viewlen))
|
||||
view = &m_views[i].first.get();
|
||||
}
|
||||
|
||||
// if we don't have a match, default to the nth view
|
||||
screen_device_iterator iter(m_manager.machine().root_device());
|
||||
int scrcount = iter.count();
|
||||
if (view == nullptr && scrcount > 0)
|
||||
std::vector<std::reference_wrapper<screen_device> > screens;
|
||||
for (screen_device &screen : screen_device_iterator(m_manager.machine().root_device()))
|
||||
screens.push_back(screen);
|
||||
if (!view && !screens.empty())
|
||||
{
|
||||
// if we have enough targets to be one per screen, assign in order
|
||||
if (numtargets >= scrcount)
|
||||
if (numtargets >= screens.size())
|
||||
{
|
||||
int ourindex = index() % scrcount;
|
||||
screen_device *screen = iter.byindex(ourindex);
|
||||
assert(screen != nullptr);
|
||||
int const ourindex = index() % screens.size();
|
||||
screen_device &screen = screens[ourindex];
|
||||
|
||||
// find the first view with this screen and this screen only
|
||||
unsigned viewindex;
|
||||
for (view = view_by_index(viewindex = 0); view != nullptr; view = view_by_index(++viewindex))
|
||||
{
|
||||
auto const &viewscreens = view->screens();
|
||||
@ -1074,36 +1095,34 @@ int render_target::configured_view(const char *viewname, int targetindex, int nu
|
||||
view = nullptr;
|
||||
break;
|
||||
}
|
||||
else if (std::find_if(viewscreens.begin(), viewscreens.end(), [&screen](auto const &scr) { return &scr.get() != screen; }) == viewscreens.end())
|
||||
else if (std::find_if(viewscreens.begin(), viewscreens.end(), [&screen] (auto const &scr) { return &scr.get() != &screen; }) == viewscreens.end())
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// otherwise, find the first view that has all the screens
|
||||
if (view == nullptr)
|
||||
if (!view)
|
||||
{
|
||||
for (view = view_by_index(viewindex = 0); view != nullptr; view = view_by_index(++viewindex))
|
||||
for (unsigned i = 0; !view && (m_views.size() > i); ++i)
|
||||
{
|
||||
if (view->screen_count() >= scrcount)
|
||||
view = &m_views[i].first.get();
|
||||
if (view->screen_count() >= screens.size())
|
||||
{
|
||||
bool screen_missing(false);
|
||||
for (screen_device &screen : iter)
|
||||
for (screen_device &screen : screens)
|
||||
{
|
||||
if (!view->has_screen(screen))
|
||||
{
|
||||
screen_missing = true;
|
||||
view = nullptr;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!screen_missing)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// make sure it's a valid view
|
||||
return (view != nullptr) ? view_index(*view) : 0;
|
||||
return view ? view_index(*view) : 0;
|
||||
}
|
||||
|
||||
|
||||
@ -1111,10 +1130,14 @@ int render_target::configured_view(const char *viewname, int targetindex, int nu
|
||||
// view_name - return the name of the given view
|
||||
//-------------------------------------------------
|
||||
|
||||
const char *render_target::view_name(int viewindex)
|
||||
const char *render_target::view_name(unsigned viewindex)
|
||||
{
|
||||
layout_view const *const view = view_by_index(viewindex);
|
||||
return view ? view->name().c_str() : nullptr;
|
||||
return (m_views.size() > viewindex) ? m_views[viewindex].first.get().name().c_str() : nullptr;
|
||||
}
|
||||
|
||||
layout_view::visibility_toggle_vector const &render_target::visibility_toggles()
|
||||
{
|
||||
return current_view().visibility_toggles();
|
||||
}
|
||||
|
||||
|
||||
@ -1137,7 +1160,7 @@ void render_target::compute_visible_area(s32 target_width, s32 target_height, fl
|
||||
if (m_keepaspect)
|
||||
{
|
||||
// start with the aspect ratio of the square pixel layout
|
||||
width = m_curview->effective_aspect(m_layerconfig);
|
||||
width = current_view().effective_aspect();
|
||||
height = 1.0f;
|
||||
|
||||
// first apply target orientation
|
||||
@ -1173,7 +1196,7 @@ void render_target::compute_visible_area(s32 target_width, s32 target_height, fl
|
||||
// get source size and aspect
|
||||
s32 src_width, src_height;
|
||||
compute_minimum_size(src_width, src_height);
|
||||
float src_aspect = m_curview->effective_aspect(m_layerconfig);
|
||||
float src_aspect = current_view().effective_aspect();
|
||||
|
||||
// apply orientation if required
|
||||
if (target_orientation & ORIENTATION_SWAP_XY)
|
||||
@ -1242,11 +1265,11 @@ void render_target::compute_minimum_size(s32 &minwidth, s32 &minheight)
|
||||
return;
|
||||
}
|
||||
|
||||
if (!m_curview)
|
||||
if (m_views.empty())
|
||||
throw emu_fatalerror("Mandatory artwork is missing");
|
||||
|
||||
// scan the current view for all screens
|
||||
for (layout_view::item &curitem : m_curview->items())
|
||||
for (layout_view::item &curitem : current_view().items())
|
||||
{
|
||||
if (curitem.screen())
|
||||
{
|
||||
@ -1301,8 +1324,8 @@ void render_target::compute_minimum_size(s32 &minwidth, s32 &minheight)
|
||||
render_primitive_list &render_target::get_primitives()
|
||||
{
|
||||
// remember the base values if this is the first frame
|
||||
if (m_base_view == nullptr)
|
||||
m_base_view = m_curview;
|
||||
if (!m_base_view)
|
||||
m_base_view = ¤t_view();
|
||||
|
||||
// switch to the next primitive list
|
||||
render_primitive_list &list = m_primlist[m_listindex];
|
||||
@ -1326,38 +1349,42 @@ render_primitive_list &render_target::get_primitives()
|
||||
root_xform.orientation = m_orientation;
|
||||
root_xform.no_center = false;
|
||||
|
||||
// iterate over items in the view, but only if we're running
|
||||
if (m_manager.machine().phase() >= machine_phase::RESET)
|
||||
for (layout_view::item &curitem : m_curview->items())
|
||||
{
|
||||
// we're running - iterate over items in the view
|
||||
for (layout_view::item &curitem : current_view().items())
|
||||
{
|
||||
// first apply orientation to the bounds
|
||||
render_bounds bounds = curitem.bounds();
|
||||
apply_orientation(bounds, root_xform.orientation);
|
||||
normalize_bounds(bounds);
|
||||
if ((visibility_mask() & curitem.visibility_mask()) == curitem.visibility_mask())
|
||||
{
|
||||
// first apply orientation to the bounds
|
||||
render_bounds bounds = curitem.bounds();
|
||||
apply_orientation(bounds, root_xform.orientation);
|
||||
normalize_bounds(bounds);
|
||||
|
||||
// apply the transform to the item
|
||||
object_transform item_xform;
|
||||
item_xform.xoffs = root_xform.xoffs + bounds.x0 * root_xform.xscale;
|
||||
item_xform.yoffs = root_xform.yoffs + bounds.y0 * root_xform.yscale;
|
||||
item_xform.xscale = (bounds.x1 - bounds.x0) * root_xform.xscale;
|
||||
item_xform.yscale = (bounds.y1 - bounds.y0) * root_xform.yscale;
|
||||
item_xform.color.r = curitem.color().r * root_xform.color.r;
|
||||
item_xform.color.g = curitem.color().g * root_xform.color.g;
|
||||
item_xform.color.b = curitem.color().b * root_xform.color.b;
|
||||
item_xform.color.a = curitem.color().a * root_xform.color.a;
|
||||
item_xform.orientation = orientation_add(curitem.orientation(), root_xform.orientation);
|
||||
item_xform.no_center = false;
|
||||
// apply the transform to the item
|
||||
object_transform item_xform;
|
||||
item_xform.xoffs = root_xform.xoffs + bounds.x0 * root_xform.xscale;
|
||||
item_xform.yoffs = root_xform.yoffs + bounds.y0 * root_xform.yscale;
|
||||
item_xform.xscale = (bounds.x1 - bounds.x0) * root_xform.xscale;
|
||||
item_xform.yscale = (bounds.y1 - bounds.y0) * root_xform.yscale;
|
||||
item_xform.color.r = curitem.color().r * root_xform.color.r;
|
||||
item_xform.color.g = curitem.color().g * root_xform.color.g;
|
||||
item_xform.color.b = curitem.color().b * root_xform.color.b;
|
||||
item_xform.color.a = curitem.color().a * root_xform.color.a;
|
||||
item_xform.orientation = orientation_add(curitem.orientation(), root_xform.orientation);
|
||||
item_xform.no_center = false;
|
||||
|
||||
// if there is no associated element, it must be a screen element
|
||||
if (curitem.screen() != nullptr)
|
||||
add_container_primitives(list, root_xform, item_xform, curitem.screen()->container(), curitem.blend_mode());
|
||||
else
|
||||
add_element_primitives(list, item_xform, *curitem.element(), curitem.state(), curitem.blend_mode());
|
||||
// if there is no associated element, it must be a screen element
|
||||
if (curitem.screen())
|
||||
add_container_primitives(list, root_xform, item_xform, curitem.screen()->container(), curitem.blend_mode());
|
||||
else
|
||||
add_element_primitives(list, item_xform, *curitem.element(), curitem.state(), curitem.blend_mode());
|
||||
}
|
||||
}
|
||||
|
||||
// if we are not in the running stage, draw an outer box
|
||||
}
|
||||
else
|
||||
{
|
||||
// if we are not in the running stage, draw an outer box
|
||||
render_primitive *prim = list.alloc(render_primitive::QUAD);
|
||||
set_render_bounds_xy(prim->bounds, 0.0f, 0.0f, (float)m_width, (float)m_height);
|
||||
prim->full_bounds = prim->bounds;
|
||||
@ -1520,7 +1547,7 @@ void render_target::resolve_tags()
|
||||
|
||||
void render_target::update_layer_config()
|
||||
{
|
||||
m_curview->recompute(m_layerconfig);
|
||||
current_view().recompute(visibility_mask(), m_layerconfig.zoom_to_screen());
|
||||
}
|
||||
|
||||
|
||||
@ -1694,10 +1721,9 @@ void render_target::load_additional_layout_files(const char *basename, bool have
|
||||
throw emu_fatalerror("Couldn't create XML node??");
|
||||
viewnode->set_attribute(
|
||||
"name",
|
||||
util::xml::normalize_string(
|
||||
util::string_format(
|
||||
"Screen %1$u Standard (%2$u:%3$u)",
|
||||
i, screens[i].physical_x(), screens[i].physical_y()).c_str()));
|
||||
util::string_format(
|
||||
"Screen %1$u Standard (%2$u:%3$u)",
|
||||
i, screens[i].physical_x(), screens[i].physical_y()).c_str());
|
||||
util::xml::data_node *const screennode(viewnode->add_child("screen", nullptr));
|
||||
if (!screennode)
|
||||
throw emu_fatalerror("Couldn't create XML node??");
|
||||
@ -1721,10 +1747,9 @@ void render_target::load_additional_layout_files(const char *basename, bool have
|
||||
throw emu_fatalerror("Couldn't create XML node??");
|
||||
viewnode->set_attribute(
|
||||
"name",
|
||||
util::xml::normalize_string(
|
||||
util::string_format(
|
||||
"Screen %1$u Pixel Aspect (%2$u:%3$u)",
|
||||
i, screens[i].native_x(), screens[i].native_y()).c_str()));
|
||||
util::string_format(
|
||||
"Screen %1$u Pixel Aspect (%2$u:%3$u)",
|
||||
i, screens[i].native_x(), screens[i].native_y()).c_str());
|
||||
util::xml::data_node *const screennode(viewnode->add_child("screen", nullptr));
|
||||
if (!screennode)
|
||||
throw emu_fatalerror("Couldn't create XML node??");
|
||||
@ -1841,7 +1866,7 @@ void render_target::load_additional_layout_files(const char *basename, bool have
|
||||
util::xml::data_node *viewnode = layoutnode->add_child("view", nullptr);
|
||||
if (!viewnode)
|
||||
throw emu_fatalerror("Couldn't create XML node??");
|
||||
viewnode->set_attribute("name", util::xml::normalize_string(title));
|
||||
viewnode->set_attribute("name", title);
|
||||
float ypos(0.0F);
|
||||
for (unsigned y = 0U; rows > y; ypos += heights[y] + spacing, ++y)
|
||||
{
|
||||
@ -2479,8 +2504,11 @@ bool render_target::map_point_internal(s32 target_x, s32 target_y, render_contai
|
||||
}
|
||||
|
||||
// iterate over items in the view
|
||||
for (layout_view::item &item : m_curview->items())
|
||||
for (layout_view::item &item : current_view().items())
|
||||
{
|
||||
if ((visibility_mask() & item.visibility_mask()) != item.visibility_mask())
|
||||
continue;
|
||||
|
||||
bool checkit;
|
||||
|
||||
// if we're looking for a particular container, verify that we have the right one
|
||||
@ -2511,15 +2539,9 @@ bool render_target::map_point_internal(s32 target_x, s32 target_y, render_contai
|
||||
// view, or nullptr if it doesn't exist
|
||||
//-------------------------------------------------
|
||||
|
||||
layout_view *render_target::view_by_index(int index)
|
||||
layout_view *render_target::view_by_index(unsigned index)
|
||||
{
|
||||
// scan the list of views within each layout, skipping those that don't apply
|
||||
for (layout_file &file : m_filelist)
|
||||
for (layout_view &view : file.views())
|
||||
if (!(m_flags & RENDER_CREATE_NO_ART) || !view.has_art())
|
||||
if (index-- == 0)
|
||||
return &view;
|
||||
return nullptr;
|
||||
return (m_views.size() > index) ? &m_views[index].first.get() : nullptr;
|
||||
}
|
||||
|
||||
|
||||
@ -2552,8 +2574,6 @@ int render_target::view_index(layout_view &targetview) const
|
||||
|
||||
void render_target::config_load(util::xml::data_node const &targetnode)
|
||||
{
|
||||
int tmpint;
|
||||
|
||||
// find the view
|
||||
const char *viewname = targetnode.get_attribute_string("view", nullptr);
|
||||
if (viewname != nullptr)
|
||||
@ -2570,23 +2590,23 @@ void render_target::config_load(util::xml::data_node const &targetnode)
|
||||
}
|
||||
|
||||
// modify the artwork config
|
||||
tmpint = targetnode.get_attribute_int("zoom", -1);
|
||||
if (tmpint == 0 || tmpint == 1)
|
||||
set_zoom_to_screen(tmpint);
|
||||
int const zoom = targetnode.get_attribute_int("zoom", -1);
|
||||
if (zoom == 0 || zoom == 1)
|
||||
set_zoom_to_screen(zoom);
|
||||
|
||||
// apply orientation
|
||||
tmpint = targetnode.get_attribute_int("rotate", -1);
|
||||
if (tmpint != -1)
|
||||
int rotate = targetnode.get_attribute_int("rotate", -1);
|
||||
if (rotate != -1)
|
||||
{
|
||||
if (tmpint == 90)
|
||||
tmpint = ROT90;
|
||||
else if (tmpint == 180)
|
||||
tmpint = ROT180;
|
||||
else if (tmpint == 270)
|
||||
tmpint = ROT270;
|
||||
if (rotate == 90)
|
||||
rotate = ROT90;
|
||||
else if (rotate == 180)
|
||||
rotate = ROT180;
|
||||
else if (rotate == 270)
|
||||
rotate = ROT270;
|
||||
else
|
||||
tmpint = ROT0;
|
||||
set_orientation(orientation_add(tmpint, orientation()));
|
||||
rotate = ROT0;
|
||||
set_orientation(orientation_add(rotate, orientation()));
|
||||
|
||||
// apply the opposite orientation to the UI
|
||||
if (is_ui_target())
|
||||
@ -2595,10 +2615,43 @@ void render_target::config_load(util::xml::data_node const &targetnode)
|
||||
render_container &ui_container = m_manager.ui_container();
|
||||
|
||||
ui_container.get_user_settings(settings);
|
||||
settings.m_orientation = orientation_add(orientation_reverse(tmpint), settings.m_orientation);
|
||||
settings.m_orientation = orientation_add(orientation_reverse(rotate), settings.m_orientation);
|
||||
ui_container.set_user_settings(settings);
|
||||
}
|
||||
}
|
||||
|
||||
// apply per-view settings
|
||||
for (util::xml::data_node const *viewnode = targetnode.get_child("view"); viewnode; viewnode = viewnode->get_next_sibling("view"))
|
||||
{
|
||||
char const *const viewname = viewnode->get_attribute_string("name", nullptr);
|
||||
if (!viewname)
|
||||
continue;
|
||||
|
||||
auto const view = std::find_if(m_views.begin(), m_views.end(), [viewname] (auto const &x) { return x.first.get().name() == viewname; });
|
||||
if (m_views.end() == view)
|
||||
continue;
|
||||
|
||||
for (util::xml::data_node const *vistogglenode = viewnode->get_child("vistoggle"); vistogglenode; vistogglenode = vistogglenode->get_next_sibling("vistoggle"))
|
||||
{
|
||||
char const *const vistogglename = vistogglenode->get_attribute_string("name", nullptr);
|
||||
if (!vistogglename)
|
||||
continue;
|
||||
|
||||
auto const &vistoggles = view->first.get().visibility_toggles();
|
||||
auto const vistoggle = std::find_if(vistoggles.begin(), vistoggles.end(), [vistogglename] (auto const &x) { return x.name() == vistogglename; });
|
||||
if (vistoggles.end() == vistoggle)
|
||||
continue;
|
||||
|
||||
int const enable = vistogglenode->get_attribute_int("enabled", -1);
|
||||
if (0 <= enable)
|
||||
{
|
||||
if (enable)
|
||||
view->second |= u32(1) << std::distance(vistoggles.begin(), vistoggle);
|
||||
else
|
||||
view->second &= ~(u32(1) << std::distance(vistoggles.begin(), vistoggle));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -2615,9 +2668,9 @@ bool render_target::config_save(util::xml::data_node &targetnode)
|
||||
targetnode.set_attribute_int("index", index());
|
||||
|
||||
// output the view
|
||||
if (m_curview != m_base_view)
|
||||
if (¤t_view() != m_base_view)
|
||||
{
|
||||
targetnode.set_attribute("view", m_curview->name().c_str());
|
||||
targetnode.set_attribute("view", current_view().name().c_str());
|
||||
changed = true;
|
||||
}
|
||||
|
||||
@ -2643,6 +2696,33 @@ bool render_target::config_save(util::xml::data_node &targetnode)
|
||||
changed = true;
|
||||
}
|
||||
|
||||
// output layer configuration
|
||||
for (auto const &view : m_views)
|
||||
{
|
||||
u32 const defvismask = view.first.get().default_visibility_mask();
|
||||
if (defvismask != view.second)
|
||||
{
|
||||
util::xml::data_node *viewnode = nullptr;
|
||||
unsigned i = 0;
|
||||
for (layout_view::visibility_toggle const &toggle : view.first.get().visibility_toggles())
|
||||
{
|
||||
if (BIT(defvismask, i) != BIT(view.second, i))
|
||||
{
|
||||
if (!viewnode)
|
||||
{
|
||||
viewnode = targetnode.add_child("view", nullptr);
|
||||
viewnode->set_attribute("name", view.first.get().name().c_str());
|
||||
}
|
||||
util::xml::data_node *const vistogglenode = viewnode->add_child("vistoggle", nullptr);
|
||||
vistogglenode->set_attribute("name", toggle.name().c_str());
|
||||
vistogglenode->set_attribute_int("enabled", BIT(view.second, i));
|
||||
changed = true;
|
||||
}
|
||||
++i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
@ -2968,8 +3048,8 @@ bool render_manager::is_live(screen_device &screen) const
|
||||
{
|
||||
if (!target.hidden())
|
||||
{
|
||||
layout_view const *view = target.current_view();
|
||||
if (view != nullptr && view->has_screen(screen))
|
||||
layout_view const *view = &target.current_view();
|
||||
if (view->has_screen(screen))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -3201,35 +3281,31 @@ void render_manager::container_free(render_container *container)
|
||||
|
||||
void render_manager::config_load(config_type cfg_type, util::xml::data_node const *parentnode)
|
||||
{
|
||||
// we only care about game files
|
||||
if (cfg_type != config_type::GAME)
|
||||
return;
|
||||
|
||||
// might not have any data
|
||||
if (parentnode == nullptr)
|
||||
// we only care about game files with matching nodes
|
||||
if ((cfg_type != config_type::GAME) || !parentnode)
|
||||
return;
|
||||
|
||||
// check the UI target
|
||||
util::xml::data_node const *const uinode = parentnode->get_child("interface");
|
||||
if (uinode != nullptr)
|
||||
if (uinode)
|
||||
{
|
||||
render_target *target = target_by_index(uinode->get_attribute_int("target", 0));
|
||||
if (target != nullptr)
|
||||
render_target *const target = target_by_index(uinode->get_attribute_int("target", 0));
|
||||
if (target)
|
||||
set_ui_target(*target);
|
||||
}
|
||||
|
||||
// iterate over target nodes
|
||||
for (util::xml::data_node const *targetnode = parentnode->get_child("target"); targetnode; targetnode = targetnode->get_next_sibling("target"))
|
||||
{
|
||||
render_target *target = target_by_index(targetnode->get_attribute_int("index", -1));
|
||||
if (target != nullptr)
|
||||
render_target *const target = target_by_index(targetnode->get_attribute_int("index", -1));
|
||||
if (target)
|
||||
target->config_load(*targetnode);
|
||||
}
|
||||
|
||||
// iterate over screen nodes
|
||||
for (util::xml::data_node const *screennode = parentnode->get_child("screen"); screennode; screennode = screennode->get_next_sibling("screen"))
|
||||
{
|
||||
int index = screennode->get_attribute_int("index", -1);
|
||||
int const index = screennode->get_attribute_int("index", -1);
|
||||
render_container *container = m_screen_container_list.find(index);
|
||||
render_container::user_settings settings;
|
||||
|
||||
@ -3283,7 +3359,7 @@ void render_manager::config_save(config_type cfg_type, util::xml::data_node *par
|
||||
|
||||
// create a node
|
||||
util::xml::data_node *const targetnode = parentnode->add_child("target", nullptr);
|
||||
if (targetnode != nullptr && !target->config_save(*targetnode))
|
||||
if (targetnode && !target->config_save(*targetnode))
|
||||
targetnode->delete_node();
|
||||
}
|
||||
|
||||
|
111
src/emu/render.h
111
src/emu/render.h
@ -48,17 +48,24 @@
|
||||
|
||||
#include "screen.h"
|
||||
|
||||
#include <cmath>
|
||||
#include <array>
|
||||
#include <cmath>
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
|
||||
namespace emu { namespace render { namespace detail { class layout_environment; } } }
|
||||
namespace emu { namespace render { namespace detail {
|
||||
|
||||
class layout_environment;
|
||||
class view_environment;
|
||||
|
||||
} } } // namespace emu::render::detail
|
||||
|
||||
|
||||
//**************************************************************************
|
||||
@ -176,6 +183,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(); }
|
||||
};
|
||||
|
||||
|
||||
@ -725,20 +733,17 @@ private:
|
||||
class layout_view
|
||||
{
|
||||
public:
|
||||
using environment = emu::render::detail::layout_environment;
|
||||
using layout_environment = emu::render::detail::layout_environment;
|
||||
using view_environment = emu::render::detail::view_environment;
|
||||
using element_map = std::unordered_map<std::string, layout_element>;
|
||||
using group_map = std::unordered_map<std::string, layout_group>;
|
||||
using render_screen_list = std::list<std::reference_wrapper<screen_device>>;
|
||||
|
||||
/// \brief A single backdrop/screen/overlay/bezel/cpanel/marquee item
|
||||
/// \brief A single item in a view
|
||||
///
|
||||
/// Each view has four lists of view_items, one for each "layer."
|
||||
/// Each view item is specified using floating point coordinates in
|
||||
/// arbitrary units, and is assumed to have square pixels. Each
|
||||
/// view item can control its orientation independently. Each item
|
||||
/// can also have an optional name, and can be set at runtime into
|
||||
/// different "states", which control how the embedded elements are
|
||||
/// displayed.
|
||||
/// Each view has a list of item structures describing the visual
|
||||
/// elements to draw, where they are located, additional blending
|
||||
/// modes, and bindings for inputs and outputs.
|
||||
class item
|
||||
{
|
||||
friend class layout_view;
|
||||
@ -746,7 +751,7 @@ public:
|
||||
public:
|
||||
// construction/destruction
|
||||
item(
|
||||
environment &env,
|
||||
view_environment &env,
|
||||
util::xml::data_node const &itemnode,
|
||||
element_map &elemmap,
|
||||
int orientation,
|
||||
@ -760,6 +765,7 @@ public:
|
||||
const render_bounds &bounds() const { return m_bounds; }
|
||||
const render_color &color() const { return m_color; }
|
||||
int blend_mode() const { return m_blend_mode; }
|
||||
u32 visibility_mask() const { return m_visibility_mask; }
|
||||
int orientation() const { return m_orientation; }
|
||||
render_container *screen_container(running_machine &machine) const;
|
||||
bool has_input() const { return bool(m_input_port); }
|
||||
@ -775,10 +781,10 @@ public:
|
||||
void set_blend_mode(int mode) { m_blend_mode = mode; }
|
||||
|
||||
private:
|
||||
static layout_element *find_element(environment &env, util::xml::data_node const &itemnode, element_map &elemmap);
|
||||
static render_bounds make_bounds(environment &env, util::xml::data_node const &itemnode, layout_group::transform const &trans);
|
||||
static std::string make_input_tag(environment &env, util::xml::data_node const &itemnode);
|
||||
static int get_blend_mode(environment &env, util::xml::data_node const &itemnode);
|
||||
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);
|
||||
|
||||
// internal state
|
||||
layout_element *const m_element; // pointer to the associated element (non-screens only)
|
||||
@ -796,12 +802,37 @@ public:
|
||||
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
|
||||
};
|
||||
using item_list = std::list<item>;
|
||||
|
||||
/// \brief A subset of items in a view that can be hidden or shown
|
||||
///
|
||||
/// Visibility toggles allow the user to show or hide selected parts
|
||||
/// of a view.
|
||||
class visibility_toggle
|
||||
{
|
||||
public:
|
||||
// construction/destruction/assignment
|
||||
visibility_toggle(std::string &&name, u32 mask);
|
||||
visibility_toggle(visibility_toggle const &) = default;
|
||||
visibility_toggle(visibility_toggle &&) = default;
|
||||
visibility_toggle &operator=(visibility_toggle const &) = default;
|
||||
visibility_toggle &operator=(visibility_toggle &&) = default;
|
||||
|
||||
// getters
|
||||
std::string const &name() const { return m_name; }
|
||||
u32 mask() const { return m_mask; }
|
||||
|
||||
private:
|
||||
std::string m_name; // display name for the toggle
|
||||
u32 m_mask; // toggle combination to show
|
||||
};
|
||||
using visibility_toggle_vector = std::vector<visibility_toggle>;
|
||||
|
||||
// construction/destruction
|
||||
layout_view(
|
||||
environment &env,
|
||||
layout_environment &env,
|
||||
util::xml::data_node const &viewnode,
|
||||
element_map &elemmap,
|
||||
group_map &groupmap);
|
||||
@ -810,18 +841,17 @@ public:
|
||||
// getters
|
||||
item_list &items() { return m_items; }
|
||||
const std::string &name() const { return m_name; }
|
||||
const render_bounds &bounds() const { return m_bounds; }
|
||||
const render_bounds &screen_bounds() const { return m_scrbounds; }
|
||||
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; }
|
||||
float effective_aspect(render_layer_config config) const { return (config.zoom_to_screen() && !m_screens.empty()) ? m_scraspect : m_aspect; }
|
||||
u32 default_visibility_mask() const { return m_defvismask; }
|
||||
const visibility_toggle_vector &visibility_toggles() const { return m_vistoggles; }
|
||||
|
||||
// operations
|
||||
void recompute(render_layer_config layerconfig);
|
||||
void recompute(u32 visibility_mask, bool zoom_to_screens);
|
||||
|
||||
// resolve tags, if any
|
||||
void resolve_tags();
|
||||
@ -832,7 +862,7 @@ private:
|
||||
// add items, recursing for groups
|
||||
void add_items(
|
||||
layer_lists &layers,
|
||||
environment &env,
|
||||
view_environment &env,
|
||||
util::xml::data_node const &parentnode,
|
||||
element_map &elemmap,
|
||||
group_map &groupmap,
|
||||
@ -843,18 +873,18 @@ private:
|
||||
bool repeat,
|
||||
bool init);
|
||||
|
||||
static std::string make_name(environment &env, util::xml::data_node const &viewnode);
|
||||
static std::string make_name(layout_environment &env, util::xml::data_node const &viewnode);
|
||||
|
||||
// internal state
|
||||
std::string m_name; // name of the layout
|
||||
float m_aspect; // X/Y of the layout
|
||||
float m_scraspect; // X/Y of the screen areas
|
||||
render_screen_list m_screens; // list of active screens
|
||||
render_bounds m_bounds; // computed bounds of the view
|
||||
render_bounds m_scrbounds; // computed bounds of the screens within the view
|
||||
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;
|
||||
};
|
||||
|
||||
|
||||
@ -920,8 +950,8 @@ public:
|
||||
float max_update_rate() const { return m_max_refresh; }
|
||||
int orientation() const { return m_orientation; }
|
||||
render_layer_config layer_config() const { return m_layerconfig; }
|
||||
layout_view *current_view() const { return m_curview; }
|
||||
int view() const { return view_index(*m_curview); }
|
||||
layout_view ¤t_view() const { return m_views[m_curview].first.get(); }
|
||||
unsigned view() const { return m_curview; }
|
||||
bool external_artwork() const { return m_external_artwork; }
|
||||
bool hidden() const { return ((m_flags & RENDER_CREATE_HIDDEN) != 0); }
|
||||
bool is_ui_target() const;
|
||||
@ -931,7 +961,7 @@ public:
|
||||
void set_bounds(s32 width, s32 height, float pixel_aspect = 0);
|
||||
void set_max_update_rate(float updates_per_second) { m_max_refresh = updates_per_second; }
|
||||
void set_orientation(int orientation) { m_orientation = orientation; }
|
||||
void set_view(int viewindex);
|
||||
void set_view(unsigned viewindex);
|
||||
void set_max_texture_size(int maxwidth, int maxheight);
|
||||
void set_transform_container(bool transform_container) { m_transform_container = transform_container; }
|
||||
void set_keepaspect(bool keepaspect) { m_keepaspect = keepaspect; }
|
||||
@ -940,16 +970,19 @@ public:
|
||||
// layer config getters
|
||||
bool screen_overlay_enabled() const { return m_layerconfig.screen_overlay_enabled(); }
|
||||
bool zoom_to_screen() const { return m_layerconfig.zoom_to_screen(); }
|
||||
u32 visibility_mask() const { return m_views[m_curview].second; }
|
||||
|
||||
// layer config setters
|
||||
void set_visibility_toggle(unsigned index, bool enable);
|
||||
void set_screen_overlay_enabled(bool enable) { m_layerconfig.set_screen_overlay_enabled(enable); update_layer_config(); }
|
||||
void set_zoom_to_screen(bool zoom) { m_layerconfig.set_zoom_to_screen(zoom); update_layer_config(); }
|
||||
|
||||
// view configuration helper
|
||||
int configured_view(const char *viewname, int targetindex, int numtargets);
|
||||
unsigned configured_view(const char *viewname, int targetindex, int numtargets);
|
||||
|
||||
// view information
|
||||
const char *view_name(int viewindex);
|
||||
char const *view_name(unsigned index);
|
||||
layout_view::visibility_toggle_vector const &visibility_toggles();
|
||||
|
||||
// bounds computations
|
||||
void compute_visible_area(s32 target_width, s32 target_height, float target_pixel_aspect, int target_orientation, s32 &visible_width, s32 &visible_height);
|
||||
@ -974,6 +1007,9 @@ public:
|
||||
void resolve_tags();
|
||||
|
||||
private:
|
||||
using view_mask_pair = std::pair<std::reference_wrapper<layout_view>, u32>;
|
||||
using view_mask_vector = std::vector<view_mask_pair>;
|
||||
|
||||
// private classes declared in render.cpp
|
||||
struct object_transform;
|
||||
|
||||
@ -996,7 +1032,7 @@ private:
|
||||
bool config_save(util::xml::data_node &targetnode);
|
||||
|
||||
// view lookups
|
||||
layout_view *view_by_index(int index);
|
||||
layout_view *view_by_index(unsigned index);
|
||||
int view_index(layout_view &view) const;
|
||||
|
||||
// optimized clearing
|
||||
@ -1012,8 +1048,9 @@ private:
|
||||
// internal state
|
||||
render_target * m_next; // link to next target
|
||||
render_manager & m_manager; // reference to our owning manager
|
||||
layout_view * m_curview; // current view
|
||||
std::list<layout_file> m_filelist; // list of layout files
|
||||
view_mask_vector m_views; // views we consider
|
||||
unsigned m_curview; // current view index
|
||||
u32 m_flags; // creation flags
|
||||
render_primitive_list m_primlist[NUM_PRIMLISTS]; // list of primitives
|
||||
int m_listindex; // index of next primlist to use
|
||||
|
@ -518,6 +518,39 @@ private:
|
||||
return expand(str, str + strlen(str));
|
||||
}
|
||||
|
||||
int parse_int(char const *begin, char const *end, int defvalue)
|
||||
{
|
||||
std::istringstream stream;
|
||||
stream.imbue(f_portable_locale);
|
||||
int result;
|
||||
if (begin[0] == '$')
|
||||
{
|
||||
stream.str(std::string(begin + 1, end));
|
||||
unsigned uvalue;
|
||||
stream >> std::hex >> uvalue;
|
||||
result = int(uvalue);
|
||||
}
|
||||
else if ((begin[0] == '0') && ((begin[1] == 'x') || (begin[1] == 'X')))
|
||||
{
|
||||
stream.str(std::string(begin + 2, end));
|
||||
unsigned uvalue;
|
||||
stream >> std::hex >> uvalue;
|
||||
result = int(uvalue);
|
||||
}
|
||||
else if (begin[0] == '#')
|
||||
{
|
||||
stream.str(std::string(begin + 1, end));
|
||||
stream >> result;
|
||||
}
|
||||
else
|
||||
{
|
||||
stream.str(std::string(begin, end));
|
||||
stream >> result;
|
||||
}
|
||||
|
||||
return stream ? result : defvalue;
|
||||
}
|
||||
|
||||
std::string parameter_name(util::xml::data_node const &node)
|
||||
{
|
||||
char const *const attrib(node.get_attribute_string("name", nullptr));
|
||||
@ -547,8 +580,15 @@ private:
|
||||
bool m_cached = false;
|
||||
|
||||
public:
|
||||
explicit layout_environment(device_t &device) : m_device(device) { }
|
||||
explicit layout_environment(layout_environment &next) : m_device(next.m_device), m_next(&next) { }
|
||||
explicit layout_environment(device_t &device)
|
||||
: m_device(device)
|
||||
{
|
||||
}
|
||||
explicit layout_environment(layout_environment &next)
|
||||
: m_device(next.m_device)
|
||||
, m_next(&next)
|
||||
{
|
||||
}
|
||||
layout_environment(layout_environment const &) = delete;
|
||||
|
||||
device_t &device() { return m_device; }
|
||||
@ -706,35 +746,7 @@ public:
|
||||
|
||||
// similar to what XML nodes do
|
||||
std::pair<char const *, char const *> const expanded(expand(attrib));
|
||||
std::istringstream stream;
|
||||
stream.imbue(f_portable_locale);
|
||||
int result;
|
||||
if (expanded.first[0] == '$')
|
||||
{
|
||||
stream.str(std::string(expanded.first + 1, expanded.second));
|
||||
unsigned uvalue;
|
||||
stream >> std::hex >> uvalue;
|
||||
result = int(uvalue);
|
||||
}
|
||||
else if ((expanded.first[0] == '0') && ((expanded.first[1] == 'x') || (expanded.first[1] == 'X')))
|
||||
{
|
||||
stream.str(std::string(expanded.first + 2, expanded.second));
|
||||
unsigned uvalue;
|
||||
stream >> std::hex >> uvalue;
|
||||
result = int(uvalue);
|
||||
}
|
||||
else if (expanded.first[0] == '#')
|
||||
{
|
||||
stream.str(std::string(expanded.first + 1, expanded.second));
|
||||
stream >> result;
|
||||
}
|
||||
else
|
||||
{
|
||||
stream.str(std::string(expanded.first, expanded.second));
|
||||
stream >> result;
|
||||
}
|
||||
|
||||
return stream ? result : defvalue;
|
||||
return parse_int(expanded.first, expanded.second, defvalue);
|
||||
}
|
||||
|
||||
float get_attribute_float(util::xml::data_node const &node, char const *name, float defvalue)
|
||||
@ -751,6 +763,23 @@ public:
|
||||
return (stream >> result) ? result : defvalue;
|
||||
}
|
||||
|
||||
bool get_attribute_bool(util::xml::data_node const &node, char const *name, bool defvalue)
|
||||
{
|
||||
char const *const attrib(node.get_attribute_string(name, nullptr));
|
||||
if (!attrib)
|
||||
return defvalue;
|
||||
|
||||
// first try yes/no strings
|
||||
std::pair<char const *, char const *> const expanded(expand(attrib));
|
||||
if (!std::strcmp("yes", expanded.first) || !std::strcmp("true", expanded.first))
|
||||
return true;
|
||||
if (!std::strcmp("no", expanded.first) || !std::strcmp("false", expanded.first))
|
||||
return false;
|
||||
|
||||
// fall back to integer parsing
|
||||
return parse_int(expanded.first, expanded.second, defvalue ? 1 : 0) != 0;
|
||||
}
|
||||
|
||||
void parse_bounds(util::xml::data_node const *node, render_bounds &result)
|
||||
{
|
||||
// default to unit rectangle
|
||||
@ -826,16 +855,50 @@ public:
|
||||
case 270: result = ROT270; break;
|
||||
default: throw layout_syntax_error(util::string_format("invalid rotate attribute %d", rotate));
|
||||
}
|
||||
if (!std::strcmp("yes", get_attribute_string(*node, "swapxy", "no")))
|
||||
if (get_attribute_bool(*node, "swapxy", false))
|
||||
result ^= ORIENTATION_SWAP_XY;
|
||||
if (!std::strcmp("yes", get_attribute_string(*node, "flipx", "no")))
|
||||
if (get_attribute_bool(*node, "flipx", false))
|
||||
result ^= ORIENTATION_FLIP_X;
|
||||
if (!std::strcmp("yes", get_attribute_string(*node, "flipy", "no")))
|
||||
if (get_attribute_bool(*node, "flipy", false))
|
||||
result ^= ORIENTATION_FLIP_Y;
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class view_environment : public layout_environment
|
||||
{
|
||||
private:
|
||||
view_environment *const m_next_view = nullptr;
|
||||
char const *const m_name;
|
||||
u32 const m_visibility_mask = 0U;
|
||||
unsigned m_next_visibility_bit = 0U;
|
||||
|
||||
public:
|
||||
view_environment(layout_environment &next, char const *name)
|
||||
: layout_environment(next)
|
||||
, m_name(name)
|
||||
{
|
||||
}
|
||||
view_environment(view_environment &next, bool visibility)
|
||||
: layout_environment(next)
|
||||
, m_next_view(&next)
|
||||
, m_name(next.m_name)
|
||||
, m_visibility_mask(next.m_visibility_mask | (u32(visibility ? 1 : 0) << next.m_next_visibility_bit))
|
||||
, m_next_visibility_bit(next.m_next_visibility_bit + (visibility ? 1 : 0))
|
||||
{
|
||||
if (32U < m_next_visibility_bit)
|
||||
throw layout_syntax_error(util::string_format("view '%s' contains too many visibility toggles", m_name));
|
||||
}
|
||||
~view_environment()
|
||||
{
|
||||
if (m_next_view)
|
||||
m_next_view->m_next_visibility_bit = m_next_visibility_bit;
|
||||
}
|
||||
|
||||
u32 visibility_mask() const { return m_visibility_mask; }
|
||||
};
|
||||
|
||||
} } } // namespace emu::render::detail
|
||||
|
||||
|
||||
@ -1157,6 +1220,13 @@ void layout_group::resolve_bounds(
|
||||
local.increment_parameters();
|
||||
}
|
||||
}
|
||||
else if (!strcmp(itemnode->get_name(), "vistoggle"))
|
||||
{
|
||||
if (!env.get_attribute_string(*itemnode, "name", nullptr))
|
||||
throw layout_syntax_error("vistoggle must have name attribute");
|
||||
environment local(env);
|
||||
resolve_bounds(local, *itemnode, groupmap, seen, empty, false, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw layout_syntax_error(util::string_format("unknown group element %s", itemnode->get_name()));
|
||||
@ -2955,23 +3025,70 @@ struct layout_view::layer_lists { item_list backdrops, screens, overlays, bezels
|
||||
//-------------------------------------------------
|
||||
|
||||
layout_view::layout_view(
|
||||
environment &env,
|
||||
layout_environment &env,
|
||||
util::xml::data_node const &viewnode,
|
||||
element_map &elemmap,
|
||||
group_map &groupmap)
|
||||
: m_name(make_name(env, viewnode))
|
||||
, m_aspect(1.0f)
|
||||
, m_scraspect(1.0f)
|
||||
, m_effaspect(1.0f)
|
||||
, m_items()
|
||||
, m_has_art(false)
|
||||
, m_defvismask(0U)
|
||||
{
|
||||
// parse the layout
|
||||
m_expbounds.x0 = m_expbounds.y0 = m_expbounds.x1 = m_expbounds.y1 = 0;
|
||||
environment local(env);
|
||||
view_environment local(env, m_name.c_str());
|
||||
layer_lists layers;
|
||||
local.set_parameter("viewname", std::string(m_name));
|
||||
add_items(layers, local, viewnode, elemmap, groupmap, ROT0, identity_transform, render_color{ 1.0F, 1.0F, 1.0F, 1.0F }, true, false, true);
|
||||
|
||||
// can't support legacy layers and modern visibility toggles at the same time
|
||||
if (!m_vistoggles.empty() && (!layers.backdrops.empty() || !layers.overlays.empty() || !layers.bezels.empty() || !layers.cpanels.empty() || !layers.marquees.empty()))
|
||||
throw layout_syntax_error("view contains visibility toggles as well as legacy backdrop, overlay, bezel, cpanel and/or marquee elements");
|
||||
|
||||
// create visibility toggles for legacy layers
|
||||
u32 mask(1U);
|
||||
if (!layers.backdrops.empty())
|
||||
{
|
||||
m_vistoggles.emplace_back("Backdrops", mask);
|
||||
for (item &backdrop : layers.backdrops)
|
||||
backdrop.m_visibility_mask = mask;
|
||||
m_defvismask |= mask;
|
||||
mask <<= 1;
|
||||
}
|
||||
if (!layers.overlays.empty())
|
||||
{
|
||||
m_vistoggles.emplace_back("Overlays", mask);
|
||||
for (item &overlay : layers.overlays)
|
||||
overlay.m_visibility_mask = mask;
|
||||
m_defvismask |= mask;
|
||||
mask <<= 1;
|
||||
}
|
||||
if (!layers.bezels.empty())
|
||||
{
|
||||
m_vistoggles.emplace_back("Bezels", mask);
|
||||
for (item &bezel : layers.bezels)
|
||||
bezel.m_visibility_mask = mask;
|
||||
m_defvismask |= mask;
|
||||
mask <<= 1;
|
||||
}
|
||||
if (!layers.cpanels.empty())
|
||||
{
|
||||
m_vistoggles.emplace_back("Control Panels", mask);
|
||||
for (item &cpanel : layers.cpanels)
|
||||
cpanel.m_visibility_mask = mask;
|
||||
m_defvismask |= mask;
|
||||
mask <<= 1;
|
||||
}
|
||||
if (!layers.marquees.empty())
|
||||
{
|
||||
m_vistoggles.emplace_back("Backdrops", mask);
|
||||
for (item &marquee : layers.marquees)
|
||||
marquee.m_visibility_mask = mask;
|
||||
m_defvismask |= mask;
|
||||
mask <<= 1;
|
||||
}
|
||||
|
||||
// deal with legacy element groupings
|
||||
if (!layers.overlays.empty() || (layers.backdrops.size() <= 1))
|
||||
{
|
||||
@ -3002,7 +3119,7 @@ layout_view::layout_view(
|
||||
}
|
||||
|
||||
// calculate metrics
|
||||
recompute(render_layer_config());
|
||||
recompute(default_visibility_mask(), false);
|
||||
for (group_map::value_type &group : groupmap)
|
||||
group.second.set_bounds_unresolved();
|
||||
}
|
||||
@ -3024,7 +3141,7 @@ layout_view::~layout_view()
|
||||
|
||||
bool layout_view::has_screen(screen_device &screen) const
|
||||
{
|
||||
return std::find_if(m_screens.begin(), m_screens.end(), [&screen](auto const &scr) { return &scr.get() == &screen; }) != m_screens.end();
|
||||
return std::find_if(m_screens.begin(), m_screens.end(), [&screen] (auto const &scr) { return &scr.get() == &screen; }) != m_screens.end();
|
||||
}
|
||||
|
||||
|
||||
@ -3033,36 +3150,39 @@ bool layout_view::has_screen(screen_device &screen) const
|
||||
// ratio of a view and all of its contained items
|
||||
//-------------------------------------------------
|
||||
|
||||
void layout_view::recompute(render_layer_config layerconfig)
|
||||
void layout_view::recompute(u32 visibility_mask, bool zoom_to_screen)
|
||||
{
|
||||
// reset the bounds
|
||||
m_bounds.x0 = m_bounds.y0 = m_bounds.x1 = m_bounds.y1 = 0.0f;
|
||||
m_scrbounds.x0 = m_scrbounds.y0 = m_scrbounds.x1 = m_scrbounds.y1 = 0.0f;
|
||||
render_bounds scrbounds{ 0.0f, 0.0f, 0.0f, 0.0f };
|
||||
m_bounds = scrbounds;
|
||||
m_screens.clear();
|
||||
|
||||
// loop over all layers
|
||||
// loop over items and filter by visibility mask
|
||||
bool first = true;
|
||||
bool scrfirst = true;
|
||||
for (item &curitem : m_items)
|
||||
{
|
||||
// accumulate bounds
|
||||
if (first)
|
||||
m_bounds = curitem.m_rawbounds;
|
||||
else
|
||||
union_render_bounds(m_bounds, curitem.m_rawbounds);
|
||||
first = false;
|
||||
|
||||
// accumulate screen bounds
|
||||
if (curitem.m_screen)
|
||||
if ((visibility_mask & curitem.visibility_mask()) == curitem.visibility_mask())
|
||||
{
|
||||
if (scrfirst)
|
||||
m_scrbounds = curitem.m_rawbounds;
|
||||
// accumulate bounds
|
||||
if (first)
|
||||
m_bounds = curitem.m_rawbounds;
|
||||
else
|
||||
union_render_bounds(m_scrbounds, curitem.m_rawbounds);
|
||||
scrfirst = false;
|
||||
union_render_bounds(m_bounds, curitem.m_rawbounds);
|
||||
first = false;
|
||||
|
||||
// accumulate the screens in use while we're scanning
|
||||
m_screens.emplace_back(*curitem.m_screen);
|
||||
// accumulate visible screens and their bounds bounds
|
||||
if (curitem.m_screen)
|
||||
{
|
||||
if (scrfirst)
|
||||
scrbounds = curitem.m_rawbounds;
|
||||
else
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -3070,36 +3190,29 @@ void layout_view::recompute(render_layer_config layerconfig)
|
||||
if (m_expbounds.x1 > m_expbounds.x0)
|
||||
m_bounds = m_expbounds;
|
||||
|
||||
// if we're handling things normally, the target bounds are (0,0)-(1,1)
|
||||
render_bounds target_bounds;
|
||||
if (!layerconfig.zoom_to_screen() || m_screens.empty())
|
||||
if (!zoom_to_screen || scrfirst)
|
||||
{
|
||||
// compute the aspect ratio of the view
|
||||
m_aspect = (m_bounds.x1 - m_bounds.x0) / (m_bounds.y1 - m_bounds.y0);
|
||||
|
||||
// if we're handling things normally, the target bounds are (0,0)-(1,1)
|
||||
m_effaspect = ((m_bounds.x1 > m_bounds.x0) && (m_bounds.y1 > m_bounds.y0)) ? m_bounds.aspect() : 1.0f;
|
||||
target_bounds.x0 = target_bounds.y0 = 0.0f;
|
||||
target_bounds.x1 = target_bounds.y1 = 1.0f;
|
||||
}
|
||||
|
||||
// if we're cropping, we want the screen area to fill (0,0)-(1,1)
|
||||
else
|
||||
{
|
||||
// compute the aspect ratio of the screen
|
||||
m_scraspect = (m_scrbounds.x1 - m_scrbounds.x0) / (m_scrbounds.y1 - m_scrbounds.y0);
|
||||
|
||||
float targwidth = (m_bounds.x1 - m_bounds.x0) / (m_scrbounds.x1 - m_scrbounds.x0);
|
||||
float targheight = (m_bounds.y1 - m_bounds.y0) / (m_scrbounds.y1 - m_scrbounds.y0);
|
||||
target_bounds.x0 = (m_bounds.x0 - m_scrbounds.x0) / (m_bounds.x1 - m_bounds.x0) * targwidth;
|
||||
target_bounds.y0 = (m_bounds.y0 - m_scrbounds.y0) / (m_bounds.y1 - m_bounds.y0) * targheight;
|
||||
target_bounds.x1 = target_bounds.x0 + targwidth;
|
||||
target_bounds.y1 = target_bounds.y0 + targheight;
|
||||
// if we're cropping, we want the screen area to fill (0,0)-(1,1)
|
||||
m_effaspect = ((scrbounds.x1 > scrbounds.x0) && (scrbounds.y1 > scrbounds.y0)) ? scrbounds.aspect() : 1.0f;
|
||||
target_bounds.x0 = (m_bounds.x0 - scrbounds.x0) / scrbounds.width();
|
||||
target_bounds.y0 = (m_bounds.y0 - scrbounds.y0) / scrbounds.height();
|
||||
target_bounds.x1 = target_bounds.x0 + (m_bounds.width() / scrbounds.width());
|
||||
target_bounds.y1 = target_bounds.y0 + (m_bounds.height() / scrbounds.height());
|
||||
}
|
||||
|
||||
// determine the scale/offset for normalization
|
||||
float xoffs = m_bounds.x0;
|
||||
float yoffs = m_bounds.y0;
|
||||
float xscale = (target_bounds.x1 - target_bounds.x0) / (m_bounds.x1 - m_bounds.x0);
|
||||
float yscale = (target_bounds.y1 - target_bounds.y0) / (m_bounds.y1 - m_bounds.y0);
|
||||
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);
|
||||
|
||||
// normalize all the item bounds
|
||||
for (item &curitem : items())
|
||||
@ -3129,7 +3242,7 @@ void layout_view::resolve_tags()
|
||||
|
||||
void layout_view::add_items(
|
||||
layer_lists &layers,
|
||||
environment &env,
|
||||
view_environment &env,
|
||||
util::xml::data_node const &parentnode,
|
||||
element_map &elemmap,
|
||||
group_map &groupmap,
|
||||
@ -3202,7 +3315,7 @@ void layout_view::add_items(
|
||||
{
|
||||
char const *ref(env.get_attribute_string(*itemnode, "ref", nullptr));
|
||||
if (!ref)
|
||||
throw layout_syntax_error("nested group must have ref attribute");
|
||||
throw layout_syntax_error("group instantiation must have ref attribute");
|
||||
|
||||
group_map::iterator const found(groupmap.find(ref));
|
||||
if (groupmap.end() == found)
|
||||
@ -3225,7 +3338,7 @@ void layout_view::add_items(
|
||||
grouptrans = found->second.make_transform(grouporient, trans);
|
||||
}
|
||||
|
||||
environment local(env);
|
||||
view_environment local(env, false);
|
||||
add_items(
|
||||
layers,
|
||||
local,
|
||||
@ -3244,13 +3357,23 @@ void layout_view::add_items(
|
||||
int const count(env.get_attribute_int(*itemnode, "count", -1));
|
||||
if (0 >= count)
|
||||
throw layout_syntax_error("repeat must have positive integer count attribute");
|
||||
environment local(env);
|
||||
view_environment local(env, false);
|
||||
for (int i = 0; count > i; ++i)
|
||||
{
|
||||
add_items(layers, local, *itemnode, elemmap, groupmap, orientation, trans, color, false, true, !i);
|
||||
local.increment_parameters();
|
||||
}
|
||||
}
|
||||
else if (!strcmp(itemnode->get_name(), "vistoggle"))
|
||||
{
|
||||
char const *name(env.get_attribute_string(*itemnode, "name", nullptr));
|
||||
if (!name)
|
||||
throw layout_syntax_error("vistoggle must have name attribute");
|
||||
m_defvismask |= u32(env.get_attribute_bool(*itemnode, "enabled", true) ? 1 : 0) << m_vistoggles.size(); // TODO: make this less hacky
|
||||
view_environment local(env, true);
|
||||
m_vistoggles.emplace_back(name, local.visibility_mask());
|
||||
add_items(layers, local, *itemnode, elemmap, groupmap, orientation, trans, color, false, false, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw layout_syntax_error(util::string_format("unknown view item %s", itemnode->get_name()));
|
||||
@ -3264,7 +3387,7 @@ void layout_view::add_items(
|
||||
}
|
||||
}
|
||||
|
||||
std::string layout_view::make_name(environment &env, util::xml::data_node const &viewnode)
|
||||
std::string layout_view::make_name(layout_environment &env, util::xml::data_node const &viewnode)
|
||||
{
|
||||
char const *const name(env.get_attribute_string(viewnode, "name", nullptr));
|
||||
if (!name)
|
||||
@ -3294,7 +3417,7 @@ std::string layout_view::make_name(environment &env, util::xml::data_node const
|
||||
//-------------------------------------------------
|
||||
|
||||
layout_view::item::item(
|
||||
environment &env,
|
||||
view_environment &env,
|
||||
util::xml::data_node const &itemnode,
|
||||
element_map &elemmap,
|
||||
int orientation,
|
||||
@ -3314,6 +3437,7 @@ layout_view::item::item(
|
||||
, 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())
|
||||
{
|
||||
// fetch common data
|
||||
int index = env.get_attribute_int(itemnode, "index", -1);
|
||||
@ -3435,7 +3559,7 @@ void layout_view::item::resolve_tags()
|
||||
// find_element - find element definition
|
||||
//---------------------------------------------
|
||||
|
||||
layout_element *layout_view::item::find_element(environment &env, util::xml::data_node const &itemnode, element_map &elemmap)
|
||||
layout_element *layout_view::item::find_element(view_environment &env, util::xml::data_node const &itemnode, element_map &elemmap)
|
||||
{
|
||||
char const *const name(env.get_attribute_string(itemnode, !strcmp(itemnode.get_name(), "element") ? "ref" : "element", nullptr));
|
||||
if (!name)
|
||||
@ -3455,7 +3579,7 @@ layout_element *layout_view::item::find_element(environment &env, util::xml::dat
|
||||
//---------------------------------------------
|
||||
|
||||
render_bounds layout_view::item::make_bounds(
|
||||
environment &env,
|
||||
view_environment &env,
|
||||
util::xml::data_node const &itemnode,
|
||||
layout_group::transform const &trans)
|
||||
{
|
||||
@ -3474,7 +3598,7 @@ render_bounds layout_view::item::make_bounds(
|
||||
// make_input_tag - get absolute input tag
|
||||
//---------------------------------------------
|
||||
|
||||
std::string layout_view::item::make_input_tag(environment &env, util::xml::data_node const &itemnode)
|
||||
std::string layout_view::item::make_input_tag(view_environment &env, util::xml::data_node const &itemnode)
|
||||
{
|
||||
char const *tag(env.get_attribute_string(itemnode, "inputtag", nullptr));
|
||||
return tag ? env.device().subtag(tag) : std::string();
|
||||
@ -3485,7 +3609,7 @@ std::string layout_view::item::make_input_tag(environment &env, util::xml::data_
|
||||
// get_blend_mode - explicit or implicit blend
|
||||
//---------------------------------------------
|
||||
|
||||
int layout_view::item::get_blend_mode(environment &env, util::xml::data_node const &itemnode)
|
||||
int layout_view::item::get_blend_mode(view_environment &env, util::xml::data_node const &itemnode)
|
||||
{
|
||||
// see if there's a blend mode attribute
|
||||
char const *const mode(env.get_attribute_string(itemnode, "blend", nullptr));
|
||||
@ -3514,6 +3638,23 @@ int layout_view::item::get_blend_mode(environment &env, util::xml::data_node con
|
||||
|
||||
|
||||
|
||||
//**************************************************************************
|
||||
// LAYOUT VIEW VISIBILITY TOGGLE
|
||||
//**************************************************************************
|
||||
|
||||
//-------------------------------------------------
|
||||
// visibility_toggle - constructor
|
||||
//-------------------------------------------------
|
||||
|
||||
layout_view::visibility_toggle::visibility_toggle(std::string &&name, u32 mask)
|
||||
: m_name(std::move(name))
|
||||
, m_mask(mask)
|
||||
{
|
||||
assert(mask);
|
||||
}
|
||||
|
||||
|
||||
|
||||
//**************************************************************************
|
||||
// LAYOUT FILE
|
||||
//**************************************************************************
|
||||
@ -3548,7 +3689,7 @@ layout_file::layout_file(device_t &device, util::xml::data_node const &rootnode,
|
||||
for (util::xml::data_node const *viewnode = mamelayoutnode->get_child("view"); viewnode != nullptr; viewnode = viewnode->get_next_sibling("view"))
|
||||
{
|
||||
// the trouble with allowing errors to propagate here is that it wreaks havoc with screenless systems that use a terminal by default
|
||||
// e.g. intlc44 and intlc440 have a terminal on the tty port by default and have a view with the front panel with the terminal screen
|
||||
// e.g. intlc44 and intlc440 have a terminal on the TTY port by default and have a view with the front panel with the terminal screen
|
||||
// however, they have a second view with just the front panel which is very useful if you're using e.g. -tty null_modem with a socket
|
||||
// if the error is allowed to propagate, the entire layout is dropped so you can't select the useful view
|
||||
try
|
||||
|
@ -138,7 +138,7 @@ video_manager::video_manager(running_machine &machine)
|
||||
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", util::xml::normalize_string(util::string_format("s%1$u", i).c_str()));
|
||||
viewnode->set_attribute("name", util::string_format("s%1$u", i).c_str());
|
||||
util::xml::data_node *const screennode(viewnode->add_child("screen", nullptr));
|
||||
if (!screennode)
|
||||
throw emu_fatalerror("Couldn't create XML node??");
|
||||
|
@ -2495,7 +2495,7 @@ void lua_engine::initialize()
|
||||
|
||||
auto target_type = sol().registry().create_simple_usertype<render_target>("new", sol::no_constructor);
|
||||
target_type.set("view_bounds", [](render_target &rt) {
|
||||
const render_bounds b = rt.current_view()->bounds();
|
||||
const render_bounds b = rt.current_view().bounds();
|
||||
return std::tuple<float, float, float, float>(b.x0, b.x1, b.y0, b.y1);
|
||||
});
|
||||
target_type.set("width", &render_target::width);
|
||||
|
@ -228,7 +228,7 @@ void menu_main::handle()
|
||||
break;
|
||||
|
||||
case VIDEO_OPTIONS:
|
||||
menu::stack_push<menu_video_options>(ui(), container(), machine().render().first_target());
|
||||
menu::stack_push<menu_video_options>(ui(), container(), *machine().render().first_target());
|
||||
break;
|
||||
|
||||
case CROSSHAIR:
|
||||
|
@ -9,24 +9,21 @@
|
||||
*********************************************************************/
|
||||
|
||||
#include "emu.h"
|
||||
|
||||
#include "ui/videoopt.h"
|
||||
|
||||
#include "rendutil.h"
|
||||
|
||||
namespace ui {
|
||||
/*-------------------------------------------------
|
||||
menu_video_targets - handle the video targets
|
||||
menu
|
||||
-------------------------------------------------*/
|
||||
|
||||
void menu_video_targets::handle()
|
||||
{
|
||||
/* process the menu */
|
||||
const event *menu_event = process(0);
|
||||
if (menu_event != nullptr && menu_event->iptkey == IPT_UI_SELECT)
|
||||
menu::stack_push<menu_video_options>(ui(), container(), static_cast<render_target *>(menu_event->itemref));
|
||||
}
|
||||
namespace ui {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr uintptr_t ITEM_ROTATE = 0x00000100;
|
||||
constexpr uintptr_t ITEM_ZOOM = 0x00000101;
|
||||
constexpr uintptr_t ITEM_TOGGLE_FIRST = 0x00000200;
|
||||
constexpr uintptr_t ITEM_VIEW_FIRST = 0x00000300;
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
|
||||
/*-------------------------------------------------
|
||||
@ -34,34 +31,105 @@ void menu_video_targets::handle()
|
||||
video targets menu
|
||||
-------------------------------------------------*/
|
||||
|
||||
menu_video_targets::menu_video_targets(mame_ui_manager &mui, render_container &container) : menu(mui, container)
|
||||
menu_video_targets::menu_video_targets(mame_ui_manager &mui, render_container &container)
|
||||
: menu(mui, container)
|
||||
{
|
||||
}
|
||||
|
||||
void menu_video_targets::populate(float &customtop, float &custombottom)
|
||||
{
|
||||
int targetnum;
|
||||
|
||||
/* find the targets */
|
||||
for (targetnum = 0; ; targetnum++)
|
||||
{
|
||||
render_target *target = machine().render().target_by_index(targetnum);
|
||||
char buffer[40];
|
||||
|
||||
/* stop when we run out */
|
||||
if (target == nullptr)
|
||||
break;
|
||||
|
||||
/* add a menu item */
|
||||
sprintf(buffer, _("Screen #%d"), targetnum);
|
||||
item_append(buffer, "", 0, target);
|
||||
}
|
||||
}
|
||||
|
||||
menu_video_targets::~menu_video_targets()
|
||||
{
|
||||
}
|
||||
|
||||
void menu_video_targets::populate(float &customtop, float &custombottom)
|
||||
{
|
||||
// find the targets
|
||||
for (unsigned targetnum = 0; ; targetnum++)
|
||||
{
|
||||
// stop when we run out
|
||||
render_target *const target = machine().render().target_by_index(targetnum);
|
||||
if (!target)
|
||||
break;
|
||||
|
||||
// add a menu item
|
||||
item_append(util::string_format(_("Screen #%d"), targetnum), "", 0, target);
|
||||
}
|
||||
}
|
||||
|
||||
/*-------------------------------------------------
|
||||
menu_video_targets - handle the video targets
|
||||
menu
|
||||
-------------------------------------------------*/
|
||||
|
||||
void menu_video_targets::handle()
|
||||
{
|
||||
event const *const menu_event = process(0);
|
||||
if (menu_event && (menu_event->iptkey == IPT_UI_SELECT))
|
||||
menu::stack_push<menu_video_options>(ui(), container(), *reinterpret_cast<render_target *>(menu_event->itemref));
|
||||
}
|
||||
|
||||
|
||||
/*-------------------------------------------------
|
||||
menu_video_options_populate - populate the
|
||||
video options menu
|
||||
-------------------------------------------------*/
|
||||
|
||||
menu_video_options::menu_video_options(mame_ui_manager &mui, render_container &container, render_target &target)
|
||||
: menu(mui, container)
|
||||
, m_target(target)
|
||||
{
|
||||
}
|
||||
|
||||
menu_video_options::~menu_video_options()
|
||||
{
|
||||
}
|
||||
|
||||
void menu_video_options::populate(float &customtop, float &custombottom)
|
||||
{
|
||||
uintptr_t ref;
|
||||
|
||||
// add items for each view
|
||||
for (char const *name = m_target.view_name(ref = 0); name; name = m_target.view_name(++ref))
|
||||
item_append(name, "", 0, reinterpret_cast<void *>(ITEM_VIEW_FIRST + ref));
|
||||
item_append(menu_item_type::SEPARATOR);
|
||||
|
||||
// add items for visibility toggles
|
||||
auto const &toggles = m_target.visibility_toggles();
|
||||
if (!toggles.empty())
|
||||
{
|
||||
ref = 0U;
|
||||
auto const current_mask(m_target.visibility_mask());
|
||||
for (auto toggle = toggles.begin(); toggles.end() != toggle; ++toggle, ++ref)
|
||||
{
|
||||
auto const toggle_mask(toggle->mask());
|
||||
bool const enabled(BIT(current_mask, ref));
|
||||
bool eclipsed(false);
|
||||
for (auto it = toggles.begin(); !eclipsed && (toggle != it); ++it)
|
||||
eclipsed = ((current_mask & it->mask()) != it->mask()) && ((toggle_mask & it->mask()) == it->mask());
|
||||
u32 const flags((enabled ? FLAG_LEFT_ARROW : FLAG_RIGHT_ARROW) | (eclipsed ? (FLAG_INVERT | FLAG_DISABLE) : 0U));
|
||||
item_append(toggle->name(), enabled ? _("On") : _("Off"), flags, reinterpret_cast<void *>(ITEM_TOGGLE_FIRST + ref));
|
||||
}
|
||||
item_append(menu_item_type::SEPARATOR);
|
||||
}
|
||||
|
||||
const char *subtext = "";
|
||||
|
||||
// add a rotate item
|
||||
switch (m_target.orientation())
|
||||
{
|
||||
case ROT0: subtext = "None"; break;
|
||||
case ROT90: subtext = "CW 90" UTF8_DEGREES; break;
|
||||
case ROT180: subtext = "180" UTF8_DEGREES; break;
|
||||
case ROT270: subtext = "CCW 90" UTF8_DEGREES; break;
|
||||
}
|
||||
item_append(_("Rotate"), subtext, FLAG_LEFT_ARROW | FLAG_RIGHT_ARROW, reinterpret_cast<void *>(ITEM_ROTATE));
|
||||
|
||||
// cropping
|
||||
int enabled;
|
||||
enabled = m_target.zoom_to_screen();
|
||||
item_append(_("Zoom to Screen Area"), enabled ? _("On") : _("Off"), enabled ? FLAG_LEFT_ARROW : FLAG_RIGHT_ARROW, reinterpret_cast<void *>(ITEM_ZOOM));
|
||||
}
|
||||
|
||||
|
||||
/*-------------------------------------------------
|
||||
menu_video_options - handle the video options
|
||||
menu
|
||||
@ -69,21 +137,21 @@ menu_video_targets::~menu_video_targets()
|
||||
|
||||
void menu_video_options::handle()
|
||||
{
|
||||
bool changed = false;
|
||||
bool changed(false);
|
||||
|
||||
// process the menu
|
||||
const event *menu_event = process(0);
|
||||
if (menu_event != nullptr && menu_event->itemref != nullptr)
|
||||
event const *const menu_event(process(0));
|
||||
if (menu_event && menu_event->itemref)
|
||||
{
|
||||
switch ((uintptr_t)menu_event->itemref)
|
||||
switch (reinterpret_cast<uintptr_t>(menu_event->itemref))
|
||||
{
|
||||
// rotate adds rotation depending on the direction
|
||||
case VIDEO_ITEM_ROTATE:
|
||||
case ITEM_ROTATE:
|
||||
if (menu_event->iptkey == IPT_UI_LEFT || menu_event->iptkey == IPT_UI_RIGHT)
|
||||
{
|
||||
int delta = (menu_event->iptkey == IPT_UI_LEFT) ? ROT270 : ROT90;
|
||||
target->set_orientation(orientation_add(delta, target->orientation()));
|
||||
if (target->is_ui_target())
|
||||
int const delta((menu_event->iptkey == IPT_UI_LEFT) ? ROT270 : ROT90);
|
||||
m_target.set_orientation(orientation_add(delta, m_target.orientation()));
|
||||
if (m_target.is_ui_target())
|
||||
{
|
||||
render_container::user_settings settings;
|
||||
container().get_user_settings(settings);
|
||||
@ -95,20 +163,31 @@ void menu_video_options::handle()
|
||||
break;
|
||||
|
||||
// layer config bitmasks handle left/right keys the same (toggle)
|
||||
case VIDEO_ITEM_ZOOM:
|
||||
if (menu_event->iptkey == IPT_UI_LEFT || menu_event->iptkey == IPT_UI_RIGHT)
|
||||
case ITEM_ZOOM:
|
||||
if ((menu_event->iptkey == IPT_UI_LEFT) || (menu_event->iptkey == IPT_UI_RIGHT))
|
||||
{
|
||||
target->set_zoom_to_screen(!target->zoom_to_screen());
|
||||
m_target.set_zoom_to_screen(menu_event->iptkey == IPT_UI_RIGHT);
|
||||
changed = true;
|
||||
}
|
||||
break;
|
||||
|
||||
// anything else is a view item
|
||||
default:
|
||||
if (menu_event->iptkey == IPT_UI_SELECT && (int)(uintptr_t)menu_event->itemref >= VIDEO_ITEM_VIEW)
|
||||
if (reinterpret_cast<uintptr_t>(menu_event->itemref) >= ITEM_VIEW_FIRST)
|
||||
{
|
||||
target->set_view((uintptr_t)menu_event->itemref - VIDEO_ITEM_VIEW);
|
||||
changed = true;
|
||||
if (menu_event->iptkey == IPT_UI_SELECT)
|
||||
{
|
||||
m_target.set_view(reinterpret_cast<uintptr_t>(menu_event->itemref) - ITEM_VIEW_FIRST);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
else if (reinterpret_cast<uintptr_t>(menu_event->itemref) >= ITEM_TOGGLE_FIRST)
|
||||
{
|
||||
if ((menu_event->iptkey == IPT_UI_LEFT) || (menu_event->iptkey == IPT_UI_RIGHT))
|
||||
{
|
||||
m_target.set_visibility_toggle(reinterpret_cast<uintptr_t>(menu_event->itemref) - ITEM_TOGGLE_FIRST, menu_event->iptkey == IPT_UI_RIGHT);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -119,56 +198,4 @@ void menu_video_options::handle()
|
||||
reset(reset_options::REMEMBER_REF);
|
||||
}
|
||||
|
||||
|
||||
/*-------------------------------------------------
|
||||
menu_video_options_populate - populate the
|
||||
video options menu
|
||||
-------------------------------------------------*/
|
||||
|
||||
menu_video_options::menu_video_options(mame_ui_manager &mui, render_container &container, render_target *_target) : menu(mui, container)
|
||||
{
|
||||
target = _target;
|
||||
}
|
||||
|
||||
void menu_video_options::populate(float &customtop, float &custombottom)
|
||||
{
|
||||
const char *subtext = "";
|
||||
std::string tempstring;
|
||||
int enabled;
|
||||
|
||||
// add items for each view
|
||||
for (int viewnum = 0; ; viewnum++)
|
||||
{
|
||||
const char *name = target->view_name(viewnum);
|
||||
if (name == nullptr)
|
||||
break;
|
||||
|
||||
// create a string for the item, replacing underscores with spaces
|
||||
tempstring.assign(name);
|
||||
strreplace(tempstring, "_", " ");
|
||||
item_append(tempstring, "", 0, (void *)(uintptr_t)(VIDEO_ITEM_VIEW + viewnum));
|
||||
}
|
||||
|
||||
// add a separator
|
||||
item_append(menu_item_type::SEPARATOR);
|
||||
|
||||
// add a rotate item
|
||||
switch (target->orientation())
|
||||
{
|
||||
case ROT0: subtext = "None"; break;
|
||||
case ROT90: subtext = "CW 90" UTF8_DEGREES; break;
|
||||
case ROT180: subtext = "180" UTF8_DEGREES; break;
|
||||
case ROT270: subtext = "CCW 90" UTF8_DEGREES; break;
|
||||
}
|
||||
item_append(_("Rotate"), subtext, FLAG_LEFT_ARROW | FLAG_RIGHT_ARROW, (void *)VIDEO_ITEM_ROTATE);
|
||||
|
||||
// cropping
|
||||
enabled = target->zoom_to_screen();
|
||||
item_append(_("View"), enabled ? _("Cropped") : _("Full"), enabled ? FLAG_RIGHT_ARROW : FLAG_LEFT_ARROW, (void *)VIDEO_ITEM_ZOOM);
|
||||
}
|
||||
|
||||
menu_video_options::~menu_video_options()
|
||||
{
|
||||
}
|
||||
|
||||
} // namespace ui
|
||||
|
@ -7,15 +7,16 @@
|
||||
Internal menus for video options
|
||||
|
||||
***************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef MAME_FRONTEND_UI_VIDEOOPT_H
|
||||
#define MAME_FRONTEND_UI_VIDEOOPT_H
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ui/menu.h"
|
||||
|
||||
|
||||
namespace ui {
|
||||
|
||||
class menu_video_targets : public menu
|
||||
{
|
||||
public:
|
||||
@ -27,25 +28,20 @@ private:
|
||||
virtual void handle() override;
|
||||
};
|
||||
|
||||
|
||||
class menu_video_options : public menu
|
||||
{
|
||||
public:
|
||||
menu_video_options(mame_ui_manager &mui, render_container &container, render_target *target);
|
||||
menu_video_options(mame_ui_manager &mui, render_container &container, render_target &target);
|
||||
virtual ~menu_video_options() override;
|
||||
|
||||
private:
|
||||
enum : uint32_t {
|
||||
VIDEO_ITEM_ROTATE = 0x80000000,
|
||||
VIDEO_ITEM_ZOOM,
|
||||
VIDEO_ITEM_VIEW
|
||||
};
|
||||
|
||||
virtual void populate(float &customtop, float &custombottom) override;
|
||||
virtual void handle() override;
|
||||
|
||||
render_target *target;
|
||||
render_target &m_target;
|
||||
};
|
||||
|
||||
} // namespace ui
|
||||
|
||||
#endif /* MAME_FRONTEND_UI_VIDEOOPT_H */
|
||||
#endif // MAME_FRONTEND_UI_VIDEOOPT_H
|
||||
|
@ -33,6 +33,37 @@ namespace {
|
||||
constexpr unsigned TEMP_BUFFER_SIZE(4096U);
|
||||
std::locale const f_portable_locale("C");
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
UTILITY FUNCTIONS
|
||||
***************************************************************************/
|
||||
|
||||
void write_escaped(core_file &file, std::string const &str)
|
||||
{
|
||||
std::string::size_type pos = 0;
|
||||
while ((str.size() > pos) && (std::string::npos != pos))
|
||||
{
|
||||
std::string::size_type const found = str.find_first_of("\"&<>", pos);
|
||||
if (found != std::string::npos)
|
||||
{
|
||||
file.write(&str[pos], found - pos);
|
||||
switch (str[found])
|
||||
{
|
||||
case '"': file.puts("""); pos = found + 1; break;
|
||||
case '&': file.puts("&"); pos = found + 1; break;
|
||||
case '<': file.puts("<"); pos = found + 1; break;
|
||||
case '>': file.puts(">"); pos = found + 1; break;
|
||||
default: pos = found;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
file.write(&str[pos], str.size() - pos);
|
||||
pos = found;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
|
||||
@ -950,9 +981,13 @@ void data_node::write_recursive(int indent, util::core_file &file) const
|
||||
/* output this tag */
|
||||
file.printf("%*s<%s", indent, "", get_name());
|
||||
|
||||
/* output any attributes */
|
||||
/* output any attributes, escaping as necessary */
|
||||
for (attribute_node const &anode : m_attributes)
|
||||
file.printf(" %s=\"%s\"", anode.name, anode.value);
|
||||
{
|
||||
file.printf(" %s=\"", anode.name);
|
||||
write_escaped(file, anode.value);
|
||||
file.puts("\"");
|
||||
}
|
||||
|
||||
if (!get_first_child() && !get_value())
|
||||
{
|
||||
@ -965,8 +1000,11 @@ void data_node::write_recursive(int indent, util::core_file &file) const
|
||||
file.printf(">\n");
|
||||
|
||||
/* if there is a value, output that here */
|
||||
if (get_value())
|
||||
file.printf("%*s%s\n", indent + 4, "", get_value());
|
||||
if (!m_value.empty())
|
||||
{
|
||||
file.printf("%*s\n", indent + 4, "");
|
||||
write_escaped(file, m_value);
|
||||
}
|
||||
|
||||
/* loop over children and output them as well */
|
||||
if (get_first_child())
|
||||
|
@ -493,7 +493,7 @@ NSString *const MAMESaveDebuggerConfigurationNotification = @"MAMESaveDebuggerCo
|
||||
|
||||
- (void)saveConfigurationToNode:(util::xml::data_node *)node {
|
||||
[super saveConfigurationToNode:node];
|
||||
node->add_child("expression", util::xml::normalize_string([[self expression] UTF8String]));
|
||||
node->add_child("expression", [[self expression] UTF8String]);
|
||||
}
|
||||
|
||||
|
||||
|
@ -81,7 +81,7 @@ void bgfx_chain::process(chain_manager::screen_prim &prim, int view, int screen,
|
||||
screen_device_iterator screen_iterator(window.machine().root_device());
|
||||
screen_device* screen_device = screen_iterator.byindex(screen);
|
||||
|
||||
uint16_t screen_count(window.target()->current_view()->screen_count());
|
||||
uint16_t screen_count(window.target()->current_view().screen_count());
|
||||
uint16_t screen_width = prim.m_quad_width;
|
||||
uint16_t screen_height = prim.m_quad_height;
|
||||
uint32_t rotation_type =
|
||||
|
@ -2431,7 +2431,7 @@ void uniform::update()
|
||||
}
|
||||
case CU_SCREEN_COUNT:
|
||||
{
|
||||
int screen_count = win->target()->current_view()->screen_count();
|
||||
int screen_count = win->target()->current_view().screen_count();
|
||||
m_shader->set_int("ScreenCount", screen_count);
|
||||
break;
|
||||
}
|
||||
|
@ -257,6 +257,7 @@ uwp_window_info::uwp_window_info(
|
||||
m_target(nullptr),
|
||||
m_targetview(0),
|
||||
m_targetorient(0),
|
||||
m_targetvismask(0),
|
||||
m_lastclicktime(std::chrono::system_clock::time_point::min()),
|
||||
m_lastclickx(0),
|
||||
m_lastclicky(0),
|
||||
@ -403,6 +404,7 @@ void uwp_window_info::create(running_machine &machine, int index, std::shared_pt
|
||||
window->m_targetview = window->m_target->view();
|
||||
window->m_targetorient = window->m_target->orientation();
|
||||
window->m_targetlayerconfig = window->m_target->layer_config();
|
||||
window->m_targetvismask = window->m_target->visibility_mask();
|
||||
|
||||
// make the window title
|
||||
if (video_config.numscreens == 1)
|
||||
@ -472,20 +474,19 @@ void uwp_window_info::destroy()
|
||||
|
||||
void uwp_window_info::update()
|
||||
{
|
||||
int targetview, targetorient;
|
||||
render_layer_config targetlayerconfig;
|
||||
|
||||
assert(GetCurrentThreadId() == main_threadid);
|
||||
|
||||
// see if the target has changed significantly in window mode
|
||||
targetview = m_target->view();
|
||||
targetorient = m_target->orientation();
|
||||
targetlayerconfig = m_target->layer_config();
|
||||
if (targetview != m_targetview || targetorient != m_targetorient || targetlayerconfig != m_targetlayerconfig)
|
||||
unsigned const targetview = m_target->view();
|
||||
int const targetorient = m_target->orientation();
|
||||
render_layer_config const targetlayerconfig = m_target->layer_config();
|
||||
u32 const targetvismask = m_target->visibility_mask();
|
||||
if (targetview != m_targetview || targetorient != m_targetorient || targetlayerconfig != m_targetlayerconfig || targetvismask != m_targetvismask)
|
||||
{
|
||||
m_targetview = targetview;
|
||||
m_targetorient = targetorient;
|
||||
m_targetlayerconfig = targetlayerconfig;
|
||||
m_targetvismask = targetvismask;
|
||||
|
||||
// in window mode, reminimize/maximize
|
||||
if (!fullscreen())
|
||||
|
@ -101,9 +101,10 @@ public:
|
||||
// rendering info
|
||||
std::mutex m_render_lock;
|
||||
render_target * m_target;
|
||||
int m_targetview;
|
||||
unsigned m_targetview;
|
||||
int m_targetorient;
|
||||
render_layer_config m_targetlayerconfig;
|
||||
u32 m_targetvismask;
|
||||
|
||||
// input info
|
||||
std::chrono::system_clock::time_point m_lastclicktime;
|
||||
|
@ -312,6 +312,7 @@ win_window_info::win_window_info(
|
||||
m_target(nullptr),
|
||||
m_targetview(0),
|
||||
m_targetorient(0),
|
||||
m_targetvismask(0),
|
||||
m_lastclicktime(std::chrono::system_clock::time_point::min()),
|
||||
m_lastclickx(0),
|
||||
m_lastclicky(0),
|
||||
@ -790,6 +791,7 @@ void win_window_info::create(running_machine &machine, int index, std::shared_pt
|
||||
window->m_targetview = window->m_target->view();
|
||||
window->m_targetorient = window->m_target->orientation();
|
||||
window->m_targetlayerconfig = window->m_target->layer_config();
|
||||
window->m_targetvismask = window->m_target->visibility_mask();
|
||||
|
||||
// make the window title
|
||||
if (video_config.numscreens == 1)
|
||||
@ -859,20 +861,19 @@ void win_window_info::destroy()
|
||||
|
||||
void win_window_info::update()
|
||||
{
|
||||
int targetview, targetorient;
|
||||
render_layer_config targetlayerconfig;
|
||||
|
||||
assert(GetCurrentThreadId() == main_threadid);
|
||||
|
||||
// see if the target has changed significantly in window mode
|
||||
targetview = m_target->view();
|
||||
targetorient = m_target->orientation();
|
||||
targetlayerconfig = m_target->layer_config();
|
||||
if (targetview != m_targetview || targetorient != m_targetorient || targetlayerconfig != m_targetlayerconfig)
|
||||
unsigned const targetview = m_target->view();
|
||||
int const targetorient = m_target->orientation();
|
||||
render_layer_config const targetlayerconfig = m_target->layer_config();
|
||||
u32 const targetvismask = m_target->visibility_mask();
|
||||
if (targetview != m_targetview || targetorient != m_targetorient || targetlayerconfig != m_targetlayerconfig || targetvismask != m_targetvismask)
|
||||
{
|
||||
m_targetview = targetview;
|
||||
m_targetorient = targetorient;
|
||||
m_targetlayerconfig = targetlayerconfig;
|
||||
m_targetvismask = targetvismask;
|
||||
|
||||
// in window mode, reminimize/maximize
|
||||
if (!fullscreen())
|
||||
|
@ -122,9 +122,10 @@ public:
|
||||
// rendering info
|
||||
std::mutex m_render_lock;
|
||||
render_target * m_target;
|
||||
int m_targetview;
|
||||
unsigned m_targetview;
|
||||
int m_targetorient;
|
||||
render_layer_config m_targetlayerconfig;
|
||||
u32 m_targetvismask;
|
||||
|
||||
// input info
|
||||
std::chrono::system_clock::time_point m_lastclicktime;
|
||||
|
Loading…
Reference in New Issue
Block a user