// 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) { } 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); } // 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 -------------------------------------------------*/ void menu_video_targets::handle(event const *ev) { if (ev && (ev->iptkey == IPT_UI_SELECT)) { render_target *const target = reinterpret_cast(ev->itemref); menu::stack_push( ui(), container(), std::string(selected_item().text()), *target, &machine().video().snapshot_target() == target); } } /*------------------------------------------------- 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, bool snapshot) : menu(mui, container) , m_target(target) , m_title() , m_show_title(false) , m_snapshot(snapshot) { if (!m_snapshot || !machine().video().snap_native()) { set_selected_index(target.view()); reset(reset_options::REMEMBER_POSITION); } } menu_video_options::menu_video_options( mame_ui_manager &mui, render_container &container, std::string &&title, render_target &target, bool snapshot) : menu(mui, container) , m_target(target) , m_title(std::move(title)) , m_show_title(true) , m_snapshot(snapshot) { if (!m_snapshot || !machine().video().snap_native()) { set_selected_index(target.view() + 2); reset(reset_options::REMEMBER_POSITION); } } menu_video_options::~menu_video_options() { } void menu_video_options::populate(float &customtop, float &custombottom) { uintptr_t ref; // add title if requested if (m_show_title) { item_append(m_title, FLAG_DISABLE, nullptr); item_append(menu_item_type::SEPARATOR); } // 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(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(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(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(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(ITEM_UNEVENSTRETCH)); // keep aspect item_append_on_off(_("Maintain Aspect Ratio"), m_target.keepaspect(), 0, reinterpret_cast(ITEM_KEEPASPECT)); } item_append(menu_item_type::SEPARATOR); } /*------------------------------------------------- menu_video_options - handle the video options menu -------------------------------------------------*/ void menu_video_options::handle(event const *ev) { auto const lockout_popup([this] () { machine().popmessage(_("Cannot change options while recording!")); }); 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(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(ev->itemref) >= ITEM_VIEW_FIRST) { if (snap_lockout) return lockout_popup(); if (ev->iptkey == IPT_UI_SELECT) { m_target.set_view(reinterpret_cast(ev->itemref) - ITEM_VIEW_FIRST); changed = true; } } else if (reinterpret_cast(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(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); } } // namespace ui