// license:BSD-3-Clause
// copyright-holders:Nicola Salmoria, Aaron Giles, Nathan Woods
/*********************************************************************
ui/videoopt.cpp
Internal menus for video options
*********************************************************************/
#include "emu.h"
#include "ui/videoopt.h"
#include "rendlay.h"
#include "rendutil.h"
namespace ui {
namespace {
constexpr uintptr_t ITEM_ROTATE = 0x00000100;
constexpr uintptr_t ITEM_ZOOM = 0x00000101;
constexpr uintptr_t ITEM_UNEVENSTRETCH = 0x00000102;
constexpr uintptr_t ITEM_KEEPASPECT = 0x00000103;
constexpr uintptr_t ITEM_TOGGLE_FIRST = 0x00000200;
constexpr uintptr_t ITEM_VIEW_FIRST = 0x00000300;
} // anonymous namespace
/*-------------------------------------------------
menu_video_targets_populate - populate the
video targets menu
-------------------------------------------------*/
menu_video_targets::menu_video_targets(mame_ui_manager &mui, render_container &container)
: menu(mui, container)
{
set_heading(_("Video Options"));
}
menu_video_targets::~menu_video_targets()
{
}
void menu_video_targets::populate()
{
// 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);
}
// add option for snapshot target
item_append("Snapshot", 0, &machine().video().snapshot_target());
item_append(menu_item_type::SEPARATOR);
}
/*-------------------------------------------------
menu_video_targets - handle the video targets
menu
-------------------------------------------------*/
bool menu_video_targets::handle(event const *ev)
{
if (ev && (ev->iptkey == IPT_UI_SELECT))
{
render_target *const target = reinterpret_cast<render_target *>(ev->itemref);
menu::stack_push<menu_video_options>(
ui(),
container(),
std::string(selected_item().text()),
*target,
&machine().video().snapshot_target() == target);
}
return false;
}
/*-------------------------------------------------
menu_video_options_populate - populate the
video options menu
-------------------------------------------------*/
menu_video_options::menu_video_options(
mame_ui_manager &mui,
render_container &container,
std::string_view title,
render_target &target,
bool snapshot)
: menu(mui, container)
, m_target(target)
, m_snapshot(snapshot)
{
set_heading(util::string_format(_("Video Options: %1$s"), title));
if (!m_snapshot || !machine().video().snap_native())
{
set_selected_index(target.view());
reset(reset_options::REMEMBER_POSITION);
}
}
menu_video_options::~menu_video_options()
{
}
void menu_video_options::populate()
{
uintptr_t ref;
// add items for each view
if (!m_snapshot || !machine().video().snap_native())
{
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
layout_view const &curview = m_target.current_view();
auto const &toggles = curview.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());
item_append_on_off(toggle->name(), enabled, eclipsed ? (FLAG_INVERT | FLAG_DISABLE) : 0U, 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 = u8"CW 90°"; break;
case ROT180: subtext = u8"180°"; break;
case ROT270: subtext = u8"CCW 90°"; break;
}
item_append(_("Rotate"), subtext, FLAG_LEFT_ARROW | FLAG_RIGHT_ARROW, reinterpret_cast<void *>(ITEM_ROTATE));
// cropping
bool const canzoom(curview.has_art() && !curview.visible_screens().empty());
item_append_on_off(_("Zoom to Screen Area"), m_target.zoom_to_screen(), canzoom ? 0U : (FLAG_INVERT | FLAG_DISABLE), reinterpret_cast<void *>(ITEM_ZOOM));
if (!m_snapshot)
{
// uneven stretch
switch (m_target.scale_mode())
{
case SCALE_FRACTIONAL:
subtext = _("On");
break;
case SCALE_FRACTIONAL_X:
subtext = _("X Only");
break;
case SCALE_FRACTIONAL_Y:
subtext = _("Y Only");
break;
case SCALE_FRACTIONAL_AUTO:
subtext = _("X or Y (Auto)");
break;
case SCALE_INTEGER:
subtext = _("Off");
break;
}
item_append(_("Non-Integer Scaling"), subtext, FLAG_LEFT_ARROW | FLAG_RIGHT_ARROW, reinterpret_cast<void *>(ITEM_UNEVENSTRETCH));
// keep aspect
item_append_on_off(_("Maintain Aspect Ratio"), m_target.keepaspect(), 0, reinterpret_cast<void *>(ITEM_KEEPASPECT));
}
item_append(menu_item_type::SEPARATOR);
}
/*-------------------------------------------------
menu_video_options - handle the video options
menu
-------------------------------------------------*/
bool menu_video_options::handle(event const *ev)
{
auto const lockout_popup(
[this] ()
{
machine().popmessage(_("Cannot change options while recording!"));
return true;
});
bool const snap_lockout(m_snapshot && machine().video().is_recording());
bool changed(false);
// process the menu
if (ev && uintptr_t(ev->itemref))
{
switch (reinterpret_cast<uintptr_t>(ev->itemref))
{
// rotate adds rotation depending on the direction
case ITEM_ROTATE:
if (ev->iptkey == IPT_UI_LEFT || ev->iptkey == IPT_UI_RIGHT)
{
if (snap_lockout)
return lockout_popup();
int const delta((ev->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.m_orientation = orientation_add(delta ^ ROT180, settings.m_orientation);
container().set_user_settings(settings);
}
changed = true;
}
break;
// layer config bitmasks handle left/right keys the same (toggle)
case ITEM_ZOOM:
if ((ev->iptkey == IPT_UI_LEFT) || (ev->iptkey == IPT_UI_RIGHT))
{
if (snap_lockout)
return lockout_popup();
m_target.set_zoom_to_screen(ev->iptkey == IPT_UI_RIGHT);
changed = true;
}
break;
// non-integer scaling: rotate through options
case ITEM_UNEVENSTRETCH:
if (ev->iptkey == IPT_UI_LEFT)
{
if (snap_lockout)
return lockout_popup();
switch (m_target.scale_mode())
{
case SCALE_FRACTIONAL:
m_target.set_scale_mode(SCALE_INTEGER);
break;
case SCALE_FRACTIONAL_X:
m_target.set_scale_mode(SCALE_FRACTIONAL);
break;
case SCALE_FRACTIONAL_Y:
m_target.set_scale_mode(SCALE_FRACTIONAL_X);
break;
case SCALE_FRACTIONAL_AUTO:
m_target.set_scale_mode(SCALE_FRACTIONAL_Y);
break;
case SCALE_INTEGER:
m_target.set_scale_mode(SCALE_FRACTIONAL_AUTO);
break;
}
changed = true;
}
else if (ev->iptkey == IPT_UI_RIGHT)
{
if (snap_lockout)
return lockout_popup();
switch (m_target.scale_mode())
{
case SCALE_FRACTIONAL:
m_target.set_scale_mode(SCALE_FRACTIONAL_X);
break;
case SCALE_FRACTIONAL_X:
m_target.set_scale_mode(SCALE_FRACTIONAL_Y);
break;
case SCALE_FRACTIONAL_Y:
m_target.set_scale_mode(SCALE_FRACTIONAL_AUTO);
break;
case SCALE_FRACTIONAL_AUTO:
m_target.set_scale_mode(SCALE_INTEGER);
break;
case SCALE_INTEGER:
m_target.set_scale_mode(SCALE_FRACTIONAL);
break;
}
changed = true;
}
break;
// keep aspect handles left/right keys the same (toggle)
case ITEM_KEEPASPECT:
if ((ev->iptkey == IPT_UI_LEFT) || (ev->iptkey == IPT_UI_RIGHT))
{
if (snap_lockout)
return lockout_popup();
m_target.set_keepaspect(ev->iptkey == IPT_UI_RIGHT);
changed = true;
}
break;
// anything else is a view item
default:
if (reinterpret_cast<uintptr_t>(ev->itemref) >= ITEM_VIEW_FIRST)
{
if (snap_lockout)
return lockout_popup();
if (ev->iptkey == IPT_UI_SELECT)
{
m_target.set_view(reinterpret_cast<uintptr_t>(ev->itemref) - ITEM_VIEW_FIRST);
changed = true;
}
}
else if (reinterpret_cast<uintptr_t>(ev->itemref) >= ITEM_TOGGLE_FIRST)
{
if (snap_lockout)
return lockout_popup();
if ((ev->iptkey == IPT_UI_LEFT) || (ev->iptkey == IPT_UI_RIGHT))
{
m_target.set_visibility_toggle(reinterpret_cast<uintptr_t>(ev->itemref) - ITEM_TOGGLE_FIRST, ev->iptkey == IPT_UI_RIGHT);
changed = true;
}
}
break;
}
}
// if something changed, rebuild the menu
if (changed)
reset(reset_options::REMEMBER_REF);
return false;
}
} // namespace ui