Add "Separate Windows" LayoutOption (#6177)

This commit is contained in:
Ameer J 2022-11-17 10:37:30 -05:00 committed by GitHub
parent 4f715b6718
commit f44c95d638
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
24 changed files with 358 additions and 124 deletions

View file

@ -247,7 +247,8 @@ System::ResultStatus System::SingleStep() {
return RunLoop(false);
}
System::ResultStatus System::Load(Frontend::EmuWindow& emu_window, const std::string& filepath) {
System::ResultStatus System::Load(Frontend::EmuWindow& emu_window, const std::string& filepath,
Frontend::EmuWindow* secondary_window) {
FileUtil::SetCurrentRomPath(filepath);
app_loader = Loader::GetLoader(filepath);
if (!app_loader) {
@ -278,7 +279,8 @@ System::ResultStatus System::Load(Frontend::EmuWindow& emu_window, const std::st
if (Settings::values.is_new_3ds) {
num_cores = 4;
}
ResultStatus init_result{Init(emu_window, *system_mode.first, *n3ds_mode.first, num_cores)};
ResultStatus init_result{
Init(emu_window, secondary_window, *system_mode.first, *n3ds_mode.first, num_cores)};
if (init_result != ResultStatus::Success) {
LOG_CRITICAL(Core, "Failed to initialize system (Error {})!",
static_cast<u32>(init_result));
@ -324,6 +326,7 @@ System::ResultStatus System::Load(Frontend::EmuWindow& emu_window, const std::st
status = ResultStatus::Success;
m_emu_window = &emu_window;
m_secondary_window = secondary_window;
m_filepath = filepath;
self_delete_pending = false;
@ -355,8 +358,9 @@ void System::Reschedule() {
}
}
System::ResultStatus System::Init(Frontend::EmuWindow& emu_window, u32 system_mode, u8 n3ds_mode,
u32 num_cores) {
System::ResultStatus System::Init(Frontend::EmuWindow& emu_window,
Frontend::EmuWindow* secondary_window, u32 system_mode,
u8 n3ds_mode, u32 num_cores) {
LOG_DEBUG(HW_Memory, "initialized OK");
memory = std::make_unique<Memory::MemorySystem>();
@ -420,7 +424,7 @@ System::ResultStatus System::Init(Frontend::EmuWindow& emu_window, u32 system_mo
video_dumper = std::make_unique<VideoDumper::NullBackend>();
#endif
VideoCore::ResultStatus result = VideoCore::Init(emu_window, *memory);
VideoCore::ResultStatus result = VideoCore::Init(emu_window, secondary_window, *memory);
if (result != VideoCore::ResultStatus::Success) {
switch (result) {
case VideoCore::ResultStatus::ErrorGenericDrivers:
@ -586,7 +590,8 @@ void System::Reset() {
}
// Reload the system with the same setting
[[maybe_unused]] const System::ResultStatus result = Load(*m_emu_window, m_filepath);
[[maybe_unused]] const System::ResultStatus result =
Load(*m_emu_window, m_filepath, m_secondary_window);
// Restore the deliver arg.
if (auto apt = Service::APT::GetModule(*this)) {
@ -611,8 +616,8 @@ void System::serialize(Archive& ar, const unsigned int file_version) {
// Re-initialize everything like it was before
auto system_mode = this->app_loader->LoadKernelSystemMode();
auto n3ds_mode = this->app_loader->LoadKernelN3dsMode();
[[maybe_unused]] const System::ResultStatus result =
Init(*m_emu_window, *system_mode.first, *n3ds_mode.first, num_cores);
[[maybe_unused]] const System::ResultStatus result = Init(
*m_emu_window, m_secondary_window, *system_mode.first, *n3ds_mode.first, num_cores);
}
// flush on save, don't flush on load

View file

@ -143,7 +143,8 @@ public:
* @param filepath String path to the executable application to load on the host file system.
* @returns ResultStatus code, indicating if the operation succeeded.
*/
[[nodiscard]] ResultStatus Load(Frontend::EmuWindow& emu_window, const std::string& filepath);
[[nodiscard]] ResultStatus Load(Frontend::EmuWindow& emu_window, const std::string& filepath,
Frontend::EmuWindow* secondary_window = {});
/**
* Indicates if the emulated system is powered on (all subsystems initialized and able to run an
@ -324,8 +325,9 @@ private:
* @param system_mode The system mode.
* @return ResultStatus code, indicating if the operation succeeded.
*/
[[nodiscard]] ResultStatus Init(Frontend::EmuWindow& emu_window, u32 system_mode, u8 n3ds_mode,
u32 num_cores);
[[nodiscard]] ResultStatus Init(Frontend::EmuWindow& emu_window,
Frontend::EmuWindow* secondary_window, u32 system_mode,
u8 n3ds_mode, u32 num_cores);
/// Reschedule the core emulation
void Reschedule();
@ -385,6 +387,7 @@ private:
std::string status_details = "";
/// Saved variables for reset
Frontend::EmuWindow* m_emu_window;
Frontend::EmuWindow* m_secondary_window;
std::string m_filepath;
std::string m_chainloadpath;
u64 title_id;

View file

@ -4,12 +4,13 @@
#include <cmath>
#include <mutex>
#include "core/3ds.h"
#include "core/frontend/emu_window.h"
#include "core/frontend/input.h"
#include "core/settings.h"
namespace Frontend {
/// We need a global touch state that is shared across the different window instances
static std::weak_ptr<EmuWindow::TouchState> global_touch_state;
GraphicsContext::~GraphicsContext() = default;
@ -45,18 +46,14 @@ private:
};
EmuWindow::EmuWindow() {
// TODO: Find a better place to set this.
config.min_client_area_size =
std::make_pair(Core::kScreenTopWidth, Core::kScreenTopHeight + Core::kScreenBottomHeight);
active_config = config;
touch_state = std::make_shared<TouchState>();
Input::RegisterFactory<Input::TouchDevice>("emu_window", touch_state);
}
EmuWindow::~EmuWindow() {
Input::UnregisterFactory<Input::TouchDevice>("emu_window");
CreateTouchState();
};
EmuWindow::EmuWindow(bool is_secondary_) : is_secondary{is_secondary_} {
CreateTouchState();
}
EmuWindow::~EmuWindow() = default;
/**
* Check if the given x/y coordinates are within the touchpad specified by the framebuffer layout
* @param layout FramebufferLayout object describing the framebuffer size and screen positions
@ -111,6 +108,15 @@ std::tuple<unsigned, unsigned> EmuWindow::ClipToTouchScreen(unsigned new_x, unsi
return std::make_tuple(new_x, new_y);
}
void EmuWindow::CreateTouchState() {
if (touch_state = global_touch_state.lock()) {
return;
}
touch_state = std::make_shared<TouchState>();
Input::RegisterFactory<Input::TouchDevice>("emu_window", touch_state);
global_touch_state = touch_state;
}
bool EmuWindow::TouchPressed(unsigned framebuffer_x, unsigned framebuffer_y) {
if (!IsWithinTouchscreen(framebuffer_layout, framebuffer_x, framebuffer_y))
return false;
@ -194,6 +200,12 @@ void EmuWindow::UpdateCurrentFramebufferLayout(unsigned width, unsigned height,
layout = Layout::SideFrameLayout(width, height, Settings::values.swap_screen,
Settings::values.upright_screen);
break;
#ifndef ANDROID
case Settings::LayoutOption::SeparateWindows:
layout = Layout::SeparateWindowsLayout(width, height, is_secondary,
Settings::values.upright_screen);
break;
#endif
case Settings::LayoutOption::MobilePortrait:
layout = Layout::MobilePortraitFrameLayout(width, height, Settings::values.swap_screen);
break;

View file

@ -7,7 +7,9 @@
#include <memory>
#include <tuple>
#include <utility>
#include "common/common_types.h"
#include "core/3ds.h"
#include "core/frontend/framebuffer_layout.h"
namespace Frontend {
@ -87,12 +89,15 @@ public:
*/
class EmuWindow : public GraphicsContext {
public:
class TouchState;
/// Data structure to store emuwindow configuration
struct WindowConfig {
bool fullscreen = false;
int res_width = 0;
int res_height = 0;
std::pair<unsigned, unsigned> min_client_area_size;
std::pair<unsigned, unsigned> min_client_area_size{
Core::kScreenTopWidth, Core::kScreenTopHeight + Core::kScreenBottomHeight};
};
/// Polls window events
@ -177,6 +182,7 @@ public:
protected:
EmuWindow();
EmuWindow(bool is_secondary);
virtual ~EmuWindow();
/**
@ -204,6 +210,8 @@ protected:
framebuffer_layout = layout;
}
bool is_secondary{};
private:
/**
* Handler called when the minimal client area was requested to be changed via SetConfig.
@ -214,13 +222,14 @@ private:
// By default, ignore this request and do nothing.
}
void CreateTouchState();
Layout::FramebufferLayout framebuffer_layout; ///< Current framebuffer layout
WindowConfig config; ///< Internal configuration (changes pending for being applied in
/// ProcessConfigurationChanges)
WindowConfig active_config; ///< Internal active configuration
WindowConfig config{}; ///< Internal configuration (changes pending for being applied in
/// ProcessConfigurationChanges)
WindowConfig active_config{}; ///< Internal active configuration
class TouchState;
std::shared_ptr<TouchState> touch_state;
/**

View file

@ -342,6 +342,13 @@ FramebufferLayout SideFrameLayout(u32 width, u32 height, bool swapped, bool upri
return res;
}
FramebufferLayout SeparateWindowsLayout(u32 width, u32 height, bool is_secondary, bool upright) {
// When is_secondary is true, we disable the top screen, and enable the bottom screen.
// The same logic is found in the SingleFrameLayout using the is_swapped bool.
is_secondary = Settings::values.swap_screen ? !is_secondary : is_secondary;
return SingleFrameLayout(width, height, is_secondary, upright);
}
FramebufferLayout CustomFrameLayout(u32 width, u32 height) {
ASSERT(width > 0);
ASSERT(height > 0);
@ -360,7 +367,7 @@ FramebufferLayout CustomFrameLayout(u32 width, u32 height) {
return res;
}
FramebufferLayout FrameLayoutFromResolutionScale(u32 res_scale) {
FramebufferLayout FrameLayoutFromResolutionScale(u32 res_scale, bool is_secondary) {
FramebufferLayout layout;
if (Settings::values.custom_layout == true) {
layout = CustomFrameLayout(
@ -370,8 +377,13 @@ FramebufferLayout FrameLayoutFromResolutionScale(u32 res_scale) {
int width, height;
switch (Settings::values.layout_option) {
case Settings::LayoutOption::SingleScreen:
#ifndef ANDROID
case Settings::LayoutOption::SeparateWindows:
#endif
{
const bool swap_screens = is_secondary || Settings::values.swap_screen;
if (Settings::values.upright_screen) {
if (Settings::values.swap_screen) {
if (swap_screens) {
width = Core::kScreenBottomHeight * res_scale;
height = Core::kScreenBottomWidth * res_scale;
} else {
@ -379,7 +391,7 @@ FramebufferLayout FrameLayoutFromResolutionScale(u32 res_scale) {
height = Core::kScreenTopWidth * res_scale;
}
} else {
if (Settings::values.swap_screen) {
if (swap_screens) {
width = Core::kScreenBottomWidth * res_scale;
height = Core::kScreenBottomHeight * res_scale;
} else {
@ -387,9 +399,10 @@ FramebufferLayout FrameLayoutFromResolutionScale(u32 res_scale) {
height = Core::kScreenTopHeight * res_scale;
}
}
layout = SingleFrameLayout(width, height, Settings::values.swap_screen,
Settings::values.upright_screen);
layout =
SingleFrameLayout(width, height, swap_screens, Settings::values.upright_screen);
break;
}
case Settings::LayoutOption::LargeScreen:
if (Settings::values.upright_screen) {
if (Settings::values.swap_screen) {
@ -544,6 +557,9 @@ std::pair<unsigned, unsigned> GetMinimumSizeFromLayout(Settings::LayoutOption la
switch (layout) {
case Settings::LayoutOption::SingleScreen:
#ifndef ANDROID
case Settings::LayoutOption::SeparateWindows:
#endif
min_width = Settings::values.swap_screen ? Core::kScreenBottomWidth : Core::kScreenTopWidth;
min_height = Core::kScreenBottomHeight;
break;

View file

@ -98,6 +98,16 @@ FramebufferLayout LargeFrameLayout(u32 width, u32 height, bool is_swapped, bool
*/
FramebufferLayout SideFrameLayout(u32 width, u32 height, bool is_swapped, bool upright);
/**
* Factory method for constructing a Frame with the Top screen and bottom
* screen on separate windows
* @param width Window framebuffer width in pixels
* @param height Window framebuffer height in pixels
* @param is_secondary if true, the bottom screen will be enabled instead of the top screen
* @return Newly created FramebufferLayout object with default screen regions initialized
*/
FramebufferLayout SeparateWindowsLayout(u32 width, u32 height, bool is_secondary, bool upright);
/**
* Factory method for constructing a custom FramebufferLayout
* @param width Window framebuffer width in pixels
@ -111,7 +121,7 @@ FramebufferLayout CustomFrameLayout(u32 width, u32 height);
* Read from the current settings to determine which layout to use.
* @param res_scale resolution scale factor
*/
FramebufferLayout FrameLayoutFromResolutionScale(u32 res_scale);
FramebufferLayout FrameLayoutFromResolutionScale(u32 res_scale, bool is_secondary = false);
/**
* Convenience method for transforming a frame layout when using Cardboard VR

View file

@ -24,7 +24,9 @@ enum class LayoutOption {
SingleScreen,
LargeScreen,
SideScreen,
#ifndef ANDROID
SeparateWindows,
#endif
// Similiar to default, but better for mobile devices in portrait mode. Top screen in clamped to
// the top of the frame, and the bottom screen is enlarged to match the top screen.
MobilePortrait,