diff --git a/src/citra_qt/configuration/configure_graphics.ui b/src/citra_qt/configuration/configure_graphics.ui index 228f2a8695..b340149d52 100644 --- a/src/citra_qt/configuration/configure_graphics.ui +++ b/src/citra_qt/configuration/configure_graphics.ui @@ -146,17 +146,22 @@ - Default + Default - Single Screen + Single Screen - Large Screen + Large Screen + + + + + Side by Side diff --git a/src/core/frontend/emu_window.cpp b/src/core/frontend/emu_window.cpp index 60b20d4e2c..54fa5c7fa7 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 d2d02f9ff4..e9f778fcb6 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(Core::kScreenTopHeight) / + (Core::kScreenTopWidth + Core::kScreenBottomWidth); + float window_aspect_ratio = static_cast(height) / width; + MathUtil::Rectangle screen_window_area{0, 0, width, height}; + // Find largest Rectangle that can fit in the window size with the given aspect ratio + MathUtil::Rectangle screen_rect = + maxRectangle(screen_window_area, emulation_aspect_ratio); + // Find sizes of top and bottom screen + MathUtil::Rectangle top_screen = maxRectangle(screen_rect, TOP_SCREEN_ASPECT_RATIO); + MathUtil::Rectangle 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 9a7738969b..4983cf103d 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 d4f0429d14..efcf1267d0 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 7e15b119b7..ca657719aa 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 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