diff --git a/src/citra_qt/configuration/configure_graphics.ui b/src/citra_qt/configuration/configure_graphics.ui index 228f2a8695b99dbc23f5cb8a8980d37afdcad130..b340149d52222a45b2e7a5f2c4ea301ae4b52a6f 100644 --- a/src/citra_qt/configuration/configure_graphics.ui +++ b/src/citra_qt/configuration/configure_graphics.ui @@ -146,17 +146,22 @@ <widget class="QComboBox" name="layout_combobox"> <item> <property name="text"> - <string notr="true">Default</string> + <string>Default</string> </property> </item> <item> <property name="text"> - <string notr="true">Single Screen</string> + <string>Single Screen</string> </property> </item> <item> <property name="text"> - <string notr="true">Large Screen</string> + <string>Large Screen</string> + </property> + </item> + <item> + <property name="text"> + <string>Side by Side</string> </property> </item> </widget> diff --git a/src/core/frontend/emu_window.cpp b/src/core/frontend/emu_window.cpp index 60b20d4e2cdbb48cdc11b9226ef46b22b455c683..54fa5c7fa72d4a5dbf38a1aba574193f2db4c256 100644 --- a/src/core/frontend/emu_window.cpp +++ b/src/core/frontend/emu_window.cpp @@ -74,6 +74,9 @@ void EmuWindow::UpdateCurrentFramebufferLayout(unsigned width, unsigned height) case Settings::LayoutOption::LargeScreen: layout = Layout::LargeFrameLayout(width, height, Settings::values.swap_screen); break; + case Settings::LayoutOption::SideScreen: + layout = Layout::SideFrameLayout(width, height, Settings::values.swap_screen); + break; case Settings::LayoutOption::Default: default: layout = Layout::DefaultFrameLayout(width, height, Settings::values.swap_screen); diff --git a/src/core/frontend/framebuffer_layout.cpp b/src/core/frontend/framebuffer_layout.cpp index d2d02f9ff4dac9ea86102dd5d50e942a6bc875dd..e9f778fcb6615e5a8dc9d90b80ef44d4861cbca3 100644 --- a/src/core/frontend/framebuffer_layout.cpp +++ b/src/core/frontend/framebuffer_layout.cpp @@ -141,6 +141,40 @@ FramebufferLayout LargeFrameLayout(unsigned width, unsigned height, bool swapped return res; } +FramebufferLayout SideFrameLayout(unsigned width, unsigned height, bool swapped) { + ASSERT(width > 0); + ASSERT(height > 0); + + FramebufferLayout res{width, height, true, true, {}, {}}; + // Aspect ratio of both screens side by side + const float emulation_aspect_ratio = static_cast<float>(Core::kScreenTopHeight) / + (Core::kScreenTopWidth + Core::kScreenBottomWidth); + float window_aspect_ratio = static_cast<float>(height) / width; + MathUtil::Rectangle<unsigned> screen_window_area{0, 0, width, height}; + // Find largest Rectangle that can fit in the window size with the given aspect ratio + MathUtil::Rectangle<unsigned> screen_rect = + maxRectangle(screen_window_area, emulation_aspect_ratio); + // Find sizes of top and bottom screen + MathUtil::Rectangle<unsigned> top_screen = maxRectangle(screen_rect, TOP_SCREEN_ASPECT_RATIO); + MathUtil::Rectangle<unsigned> bot_screen = maxRectangle(screen_rect, BOT_SCREEN_ASPECT_RATIO); + + if (window_aspect_ratio < emulation_aspect_ratio) { + // Apply borders to the left and right sides of the window. + u32 shift_horizontal = (screen_window_area.GetWidth() - screen_rect.GetWidth()) / 2; + top_screen = top_screen.TranslateX(shift_horizontal); + bot_screen = bot_screen.TranslateX(shift_horizontal); + } else { + // Window is narrower than the emulation content => apply borders to the top and bottom + u32 shift_vertical = (screen_window_area.GetHeight() - screen_rect.GetHeight()) / 2; + top_screen = top_screen.TranslateY(shift_vertical); + bot_screen = bot_screen.TranslateY(shift_vertical); + } + // Move the top screen to the right if we are swapped. + res.top_screen = swapped ? top_screen.TranslateX(bot_screen.GetWidth()) : top_screen; + res.bottom_screen = swapped ? bot_screen : bot_screen.TranslateX(top_screen.GetWidth()); + return res; +} + FramebufferLayout CustomFrameLayout(unsigned width, unsigned height) { ASSERT(width > 0); ASSERT(height > 0); @@ -158,4 +192,4 @@ FramebufferLayout CustomFrameLayout(unsigned width, unsigned height) { res.bottom_screen = bot_screen; return res; } -} +} // namespace Layout diff --git a/src/core/frontend/framebuffer_layout.h b/src/core/frontend/framebuffer_layout.h index 9a7738969b9d2cc062c7a2d2661a37e9d2f3bcdb..4983cf103d4d1f2e31c2540f03a0c92f72bf879d 100644 --- a/src/core/frontend/framebuffer_layout.h +++ b/src/core/frontend/framebuffer_layout.h @@ -53,6 +53,17 @@ FramebufferLayout SingleFrameLayout(unsigned width, unsigned height, bool is_swa */ FramebufferLayout LargeFrameLayout(unsigned width, unsigned height, bool is_swapped); +/** +* Factory method for constructing a Frame with the Top screen and bottom +* screen side by side +* This is useful for devices with small screens, like the GPDWin +* @param width Window framebuffer width in pixels +* @param height Window framebuffer height in pixels +* @param is_swapped if true, the bottom screen will be the left display +* @return Newly created FramebufferLayout object with default screen regions initialized +*/ +FramebufferLayout SideFrameLayout(unsigned width, unsigned height, bool is_swapped); + /** * Factory method for constructing a custom FramebufferLayout * @param width Window framebuffer width in pixels diff --git a/src/core/settings.cpp b/src/core/settings.cpp index d4f0429d14885672803543e0d56801dd3a57b1b8..efcf1267d0de5407ebe82cb012a0cbcd44e16057 100644 --- a/src/core/settings.cpp +++ b/src/core/settings.cpp @@ -36,4 +36,4 @@ void Apply() { Service::IR::ReloadInputDevices(); } -} // namespace +} // namespace Settings diff --git a/src/core/settings.h b/src/core/settings.h index 7e15b119b7180a050f1dba71a4ec197bdafad356..ca657719aa09f448730bc29bc72ed883399e50b5 100644 --- a/src/core/settings.h +++ b/src/core/settings.h @@ -15,6 +15,7 @@ enum class LayoutOption { Default, SingleScreen, LargeScreen, + SideScreen, }; namespace NativeButton { @@ -70,7 +71,7 @@ enum Values { static const std::array<const char*, NumAnalogs> mapping = {{ "circle_pad", "c_stick", }}; -} // namespace NumAnalog +} // namespace NativeAnalog struct Values { // CheckNew3DS @@ -137,4 +138,4 @@ struct Values { static constexpr int REGION_VALUE_AUTO_SELECT = -1; void Apply(); -} +} // namespace Settings