Add "Separate Windows" LayoutOption (#6177)
This commit is contained in:
parent
4f715b6718
commit
f44c95d638
24 changed files with 358 additions and 124 deletions
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
||||
/**
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue