diff --git a/config_spec.yml b/config_spec.yml index c43f5732f8..8410264e61 100644 --- a/config_spec.yml +++ b/config_spec.yml @@ -138,8 +138,12 @@ display: default: true fit: type: enum - values: [center, scale, scale_16_9, scale_4_3, stretch] + values: [center, scale, stretch] default: scale + aspect_ratio: + type: enum + values: [native, auto, 4x3, 16x9] + default: auto scale: type: integer default: 1 diff --git a/dtc b/dtc index 85e5d83984..b6910bec11 160000 --- a/dtc +++ b/dtc @@ -1 +1 @@ -Subproject commit 85e5d839847af54efab170f2b1331b2a6421e647 +Subproject commit b6910bec11614980a21e46fbccc35934b671bd81 diff --git a/hw/xbox/acpi_xbox.c b/hw/xbox/acpi_xbox.c index 39b1dc5e87..3ff0e16f54 100644 --- a/hw/xbox/acpi_xbox.c +++ b/hw/xbox/acpi_xbox.c @@ -30,6 +30,7 @@ #include "hw/xbox/xbox_pci.h" #include "hw/xbox/acpi_xbox.h" #include "migration/vmstate.h" +#include "ui/xemu-widescreen.h" // #define DEBUG #ifdef DEBUG @@ -44,6 +45,8 @@ #define XBOX_PM_GPIO_BASE 0xC0 #define XBOX_PM_GPIO_LEN 26 +#define XBOX_PM_GPIO_ASPECT_RATIO 0x16 + static int field_pin; static uint64_t xbox_pm_gpio_read(void *opaque, hwaddr addr, unsigned width) @@ -66,6 +69,12 @@ static void xbox_pm_gpio_write(void *opaque, hwaddr addr, uint64_t val, unsigned width) { XBOX_DPRINTF("pm gpio write [0x%llx] = 0x%llx\n", addr, val); + + if (addr == XBOX_PM_GPIO_ASPECT_RATIO) { + xemu_set_widescreen(val == 5); + } + + // FIXME: Add GPIO to VM state } static const MemoryRegionOps xbox_pm_gpio_ops = { diff --git a/meson b/meson index 776acd2a80..3a9b285a55 160000 --- a/meson +++ b/meson @@ -1 +1 @@ -Subproject commit 776acd2a805c9b42b4f0375150977df42130317f +Subproject commit 3a9b285a55b91b53b2acda987192274352ecb5be diff --git a/ui/meson.build b/ui/meson.build index 8306d0857b..9b54489518 100644 --- a/ui/meson.build +++ b/ui/meson.build @@ -28,6 +28,7 @@ xemu_ss.add(files( 'xemu-data.c', 'xemu-snapshots.c', 'xemu-thumbnail.cc', + 'xemu-widescreen.c', )) subdir('xui') diff --git a/ui/xemu-widescreen.c b/ui/xemu-widescreen.c new file mode 100644 index 0000000000..bde81a3853 --- /dev/null +++ b/ui/xemu-widescreen.c @@ -0,0 +1,37 @@ +/* + * xemu wide screen handler + * + * Copyright (c) 2023 Matt Borgerson + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "xemu-widescreen.h" + +static bool g_widescreen = false; + +void xemu_set_widescreen(bool widescreen) +{ + g_widescreen = widescreen; +} + +bool xemu_get_widescreen(void) +{ + return g_widescreen; +} diff --git a/ui/xemu-widescreen.h b/ui/xemu-widescreen.h new file mode 100644 index 0000000000..02da430ef9 --- /dev/null +++ b/ui/xemu-widescreen.h @@ -0,0 +1,40 @@ +/* + * xemu wide screen handler + * + * Copyright (c) 2023 Matt Borgerson + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef XEMU_WIDESCREEN +#define XEMU_WIDESCREEN + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +void xemu_set_widescreen(bool widescreen); +bool xemu_get_widescreen(void); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/ui/xui/gl-helpers.cc b/ui/xui/gl-helpers.cc index 12e7eb6077..4155ff2097 100644 --- a/ui/xui/gl-helpers.cc +++ b/ui/xui/gl-helpers.cc @@ -28,6 +28,7 @@ #include "ui/shader/xemu-logo-frag.h" #include "data/xemu_64x64.png.h" #include "notifications.hh" +#include "ui/xemu-widescreen.h" Fbo *controller_fbo, *logo_fbo; @@ -706,6 +707,21 @@ void RenderFramebuffer(GLint tex, int width, int height, bool flip, float scale[ } } +float GetDisplayAspectRatio(int width, int height) +{ + switch (g_config.display.ui.aspect_ratio) { + case CONFIG_DISPLAY_UI_ASPECT_RATIO_NATIVE: + return (float)width/(float)height; + case CONFIG_DISPLAY_UI_ASPECT_RATIO_16X9: + return 16.0f/9.0f; + case CONFIG_DISPLAY_UI_ASPECT_RATIO_4X3: + return 4.0f/3.0f; + case CONFIG_DISPLAY_UI_ASPECT_RATIO_AUTO: + default: + return xemu_get_widescreen() ? 16.0f/9.0f : 4.0f/3.0f; + } +} + void RenderFramebuffer(GLint tex, int width, int height, bool flip) { int tw, th; @@ -723,20 +739,11 @@ void RenderFramebuffer(GLint tex, int width, int height, bool flip) scale[1] = 1.0; } else if (g_config.display.ui.fit == CONFIG_DISPLAY_UI_FIT_CENTER) { // Centered - scale[0] = (float)tw/(float)width; + float t_ratio = GetDisplayAspectRatio(tw, th); + scale[0] = t_ratio*(float)th/(float)width; scale[1] = (float)th/(float)height; } else { - float t_ratio; - if (g_config.display.ui.fit == CONFIG_DISPLAY_UI_FIT_SCALE_16_9) { - // Scale to fit window using a fixed 16:9 aspect ratio - t_ratio = 16.0f/9.0f; - } else if (g_config.display.ui.fit == CONFIG_DISPLAY_UI_FIT_SCALE_4_3) { - t_ratio = 4.0f/3.0f; - } else { - // Scale to fit, preserving framebuffer aspect ratio - t_ratio = (float)tw/(float)th; - } - + float t_ratio = GetDisplayAspectRatio(tw, th); float w_ratio = (float)width/(float)height; if (w_ratio >= t_ratio) { scale[0] = t_ratio/w_ratio; @@ -759,11 +766,7 @@ bool RenderFramebufferToPng(GLuint tex, bool flip, std::vector &png, in glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &width); glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &height); - if (g_config.display.ui.fit == CONFIG_DISPLAY_UI_FIT_SCALE_16_9) { - width = height * (16.0f / 9.0f); - } else if (g_config.display.ui.fit == CONFIG_DISPLAY_UI_FIT_SCALE_4_3) { - width = height * (4.0f / 3.0f); - } + width = height * GetDisplayAspectRatio(width, height); if (!max_width) max_width = width; if (!max_height) max_height = height; diff --git a/ui/xui/main-menu.cc b/ui/xui/main-menu.cc index c14c4bde47..2940d10760 100644 --- a/ui/xui/main-menu.cc +++ b/ui/xui/main-menu.cc @@ -340,10 +340,14 @@ void MainMenuDisplayView::Draw() ChevronCombo("Display mode", &g_config.display.ui.fit, "Center\0" "Scale\0" - "Scale (Widescreen 16:9)\0" - "Scale (4:3)\0" "Stretch\0", "Select how the framebuffer should fit or scale into the window"); + ChevronCombo("Aspect ratio", &g_config.display.ui.aspect_ratio, + "Native\0" + "Auto (Default)\0" + "4:3\0" + "16:9\0", + "Select the displayed aspect ratio"); } void MainMenuAudioView::Draw() diff --git a/ui/xui/menubar.cc b/ui/xui/menubar.cc index 71c2eab69a..9ce8a4a0c7 100644 --- a/ui/xui/menubar.cc +++ b/ui/xui/menubar.cc @@ -191,11 +191,12 @@ void ShowMainMenu() } ImGui::Combo("Display Mode", &g_config.display.ui.fit, - "Center\0Scale\0Scale (Widescreen 16:9)\0Scale " - "(4:3)\0Stretch\0"); + "Center\0Scale\0Stretch\0"); ImGui::SameLine(); HelpMarker("Controls how the rendered content should be scaled " "into the window"); + ImGui::Combo("Aspect Ratio", &g_config.display.ui.aspect_ratio, + "Native\0Auto\0""4:3\0""16:9\0"); if (ImGui::MenuItem("Fullscreen", SHORTCUT_MENU_TEXT(Alt + F), xemu_is_fullscreen(), true)) { xemu_toggle_fullscreen(); diff --git a/ui/xui/popup-menu.cc b/ui/xui/popup-menu.cc index cf6fc3f130..dcaf5e9b94 100644 --- a/ui/xui/popup-menu.cc +++ b/ui/xui/popup-menu.cc @@ -258,7 +258,7 @@ public: bool DrawItems(PopupMenuItemDelegate &nav) override { const char *values[] = { - "Center", "Scale", "Scale (Widescreen 16:9)", "Scale (4:3)", "Stretch" + "Center", "Scale", "Stretch" }; for (int i = 0; i < CONFIG_DISPLAY_UI_FIT__COUNT; i++) { @@ -272,11 +272,34 @@ public: } }; +class AspectRatioPopupMenu : public virtual PopupMenu { +public: + bool DrawItems(PopupMenuItemDelegate &nav) override + { + const char *values[] = { + "Native", + "Auto (Default)", + "4:3", + "16:9" + }; + + for (int i = 0; i < CONFIG_DISPLAY_UI_ASPECT_RATIO__COUNT; i++) { + bool selected = g_config.display.ui.aspect_ratio == i; + if (m_focus && selected) ImGui::SetKeyboardFocusHere(); + if (PopupMenuCheck(values[i], "", selected)) + g_config.display.ui.aspect_ratio = i; + } + + return false; + } +}; + extern MainMenuScene g_main_menu; class SettingsPopupMenu : public virtual PopupMenu { protected: DisplayModePopupMenu display_mode; + AspectRatioPopupMenu aspect_ratio; public: bool DrawItems(PopupMenuItemDelegate &nav) override @@ -295,6 +318,10 @@ public: nav.PushFocus(); nav.PushMenu(display_mode); } + if (PopupMenuSubmenuButton("Aspect Ratio", ICON_FA_EXPAND)) { + nav.PushFocus(); + nav.PushMenu(aspect_ratio); + } if (PopupMenuButton("Snapshots...", ICON_FA_CLOCK_ROTATE_LEFT)) { nav.ClearMenuStack(); g_scene_mgr.PushScene(g_main_menu);