early-access version 1255
This commit is contained in:
81
src/core/frontend/applets/controller.cpp
Executable file
81
src/core/frontend/applets/controller.cpp
Executable file
@@ -0,0 +1,81 @@
|
||||
// Copyright 2020 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/frontend/applets/controller.h"
|
||||
#include "core/hle/service/hid/controllers/npad.h"
|
||||
#include "core/hle/service/hid/hid.h"
|
||||
#include "core/hle/service/sm/sm.h"
|
||||
|
||||
namespace Core::Frontend {
|
||||
|
||||
ControllerApplet::~ControllerApplet() = default;
|
||||
|
||||
DefaultControllerApplet::DefaultControllerApplet(Service::SM::ServiceManager& service_manager_)
|
||||
: service_manager{service_manager_} {}
|
||||
|
||||
DefaultControllerApplet::~DefaultControllerApplet() = default;
|
||||
|
||||
void DefaultControllerApplet::ReconfigureControllers(std::function<void()> callback,
|
||||
const ControllerParameters& parameters) const {
|
||||
LOG_INFO(Service_HID, "called, deducing the best configuration based on the given parameters!");
|
||||
|
||||
auto& npad =
|
||||
service_manager.GetService<Service::HID::Hid>("hid")
|
||||
->GetAppletResource()
|
||||
->GetController<Service::HID::Controller_NPad>(Service::HID::HidController::NPad);
|
||||
|
||||
auto& players = Settings::values.players.GetValue();
|
||||
|
||||
const std::size_t min_supported_players =
|
||||
parameters.enable_single_mode ? 1 : parameters.min_players;
|
||||
|
||||
// Disconnect Handheld first.
|
||||
npad.DisconnectNpadAtIndex(8);
|
||||
|
||||
// Deduce the best configuration based on the input parameters.
|
||||
for (std::size_t index = 0; index < players.size() - 2; ++index) {
|
||||
// First, disconnect all controllers regardless of the value of keep_controllers_connected.
|
||||
// This makes it easy to connect the desired controllers.
|
||||
npad.DisconnectNpadAtIndex(index);
|
||||
|
||||
// Only connect the minimum number of required players.
|
||||
if (index >= min_supported_players) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Connect controllers based on the following priority list from highest to lowest priority:
|
||||
// Pro Controller -> Dual Joycons -> Left Joycon/Right Joycon -> Handheld
|
||||
if (parameters.allow_pro_controller) {
|
||||
npad.AddNewControllerAt(
|
||||
npad.MapSettingsTypeToNPad(Settings::ControllerType::ProController), index);
|
||||
} else if (parameters.allow_dual_joycons) {
|
||||
npad.AddNewControllerAt(
|
||||
npad.MapSettingsTypeToNPad(Settings::ControllerType::DualJoyconDetached), index);
|
||||
} else if (parameters.allow_left_joycon && parameters.allow_right_joycon) {
|
||||
// Assign left joycons to even player indices and right joycons to odd player indices.
|
||||
// We do this since Captain Toad Treasure Tracker expects a left joycon for Player 1 and
|
||||
// a right Joycon for Player 2 in 2 Player Assist mode.
|
||||
if (index % 2 == 0) {
|
||||
npad.AddNewControllerAt(
|
||||
npad.MapSettingsTypeToNPad(Settings::ControllerType::LeftJoycon), index);
|
||||
} else {
|
||||
npad.AddNewControllerAt(
|
||||
npad.MapSettingsTypeToNPad(Settings::ControllerType::RightJoycon), index);
|
||||
}
|
||||
} else if (index == 0 && parameters.enable_single_mode && parameters.allow_handheld &&
|
||||
!Settings::values.use_docked_mode.GetValue()) {
|
||||
// We should *never* reach here under any normal circumstances.
|
||||
npad.AddNewControllerAt(npad.MapSettingsTypeToNPad(Settings::ControllerType::Handheld),
|
||||
index);
|
||||
} else {
|
||||
UNREACHABLE_MSG("Unable to add a new controller based on the given parameters!");
|
||||
}
|
||||
}
|
||||
|
||||
callback();
|
||||
}
|
||||
|
||||
} // namespace Core::Frontend
|
56
src/core/frontend/applets/controller.h
Executable file
56
src/core/frontend/applets/controller.h
Executable file
@@ -0,0 +1,56 @@
|
||||
// Copyright 2020 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Service::SM {
|
||||
class ServiceManager;
|
||||
}
|
||||
|
||||
namespace Core::Frontend {
|
||||
|
||||
using BorderColor = std::array<u8, 4>;
|
||||
using ExplainText = std::array<char, 0x81>;
|
||||
|
||||
struct ControllerParameters {
|
||||
s8 min_players{};
|
||||
s8 max_players{};
|
||||
bool keep_controllers_connected{};
|
||||
bool enable_single_mode{};
|
||||
bool enable_border_color{};
|
||||
std::vector<BorderColor> border_colors{};
|
||||
bool enable_explain_text{};
|
||||
std::vector<ExplainText> explain_text{};
|
||||
bool allow_pro_controller{};
|
||||
bool allow_handheld{};
|
||||
bool allow_dual_joycons{};
|
||||
bool allow_left_joycon{};
|
||||
bool allow_right_joycon{};
|
||||
};
|
||||
|
||||
class ControllerApplet {
|
||||
public:
|
||||
virtual ~ControllerApplet();
|
||||
|
||||
virtual void ReconfigureControllers(std::function<void()> callback,
|
||||
const ControllerParameters& parameters) const = 0;
|
||||
};
|
||||
|
||||
class DefaultControllerApplet final : public ControllerApplet {
|
||||
public:
|
||||
explicit DefaultControllerApplet(Service::SM::ServiceManager& service_manager_);
|
||||
~DefaultControllerApplet() override;
|
||||
|
||||
void ReconfigureControllers(std::function<void()> callback,
|
||||
const ControllerParameters& parameters) const override;
|
||||
|
||||
private:
|
||||
Service::SM::ServiceManager& service_manager;
|
||||
};
|
||||
|
||||
} // namespace Core::Frontend
|
35
src/core/frontend/applets/error.cpp
Executable file
35
src/core/frontend/applets/error.cpp
Executable file
@@ -0,0 +1,35 @@
|
||||
// Copyright 2019 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "common/logging/log.h"
|
||||
#include "core/frontend/applets/error.h"
|
||||
|
||||
namespace Core::Frontend {
|
||||
|
||||
ErrorApplet::~ErrorApplet() = default;
|
||||
|
||||
void DefaultErrorApplet::ShowError(ResultCode error, std::function<void()> finished) const {
|
||||
LOG_CRITICAL(Service_Fatal, "Application requested error display: {:04}-{:04} (raw={:08X})",
|
||||
error.module.Value(), error.description.Value(), error.raw);
|
||||
}
|
||||
|
||||
void DefaultErrorApplet::ShowErrorWithTimestamp(ResultCode error, std::chrono::seconds time,
|
||||
std::function<void()> finished) const {
|
||||
LOG_CRITICAL(
|
||||
Service_Fatal,
|
||||
"Application requested error display: {:04X}-{:04X} (raw={:08X}) with timestamp={:016X}",
|
||||
error.module.Value(), error.description.Value(), error.raw, time.count());
|
||||
}
|
||||
|
||||
void DefaultErrorApplet::ShowCustomErrorText(ResultCode error, std::string main_text,
|
||||
std::string detail_text,
|
||||
std::function<void()> finished) const {
|
||||
LOG_CRITICAL(Service_Fatal,
|
||||
"Application requested custom error with error_code={:04X}-{:04X} (raw={:08X})",
|
||||
error.module.Value(), error.description.Value(), error.raw);
|
||||
LOG_CRITICAL(Service_Fatal, " Main Text: {}", main_text);
|
||||
LOG_CRITICAL(Service_Fatal, " Detail Text: {}", detail_text);
|
||||
}
|
||||
|
||||
} // namespace Core::Frontend
|
37
src/core/frontend/applets/error.h
Executable file
37
src/core/frontend/applets/error.h
Executable file
@@ -0,0 +1,37 @@
|
||||
// Copyright 2019 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <chrono>
|
||||
#include <functional>
|
||||
|
||||
#include "core/hle/result.h"
|
||||
|
||||
namespace Core::Frontend {
|
||||
|
||||
class ErrorApplet {
|
||||
public:
|
||||
virtual ~ErrorApplet();
|
||||
|
||||
virtual void ShowError(ResultCode error, std::function<void()> finished) const = 0;
|
||||
|
||||
virtual void ShowErrorWithTimestamp(ResultCode error, std::chrono::seconds time,
|
||||
std::function<void()> finished) const = 0;
|
||||
|
||||
virtual void ShowCustomErrorText(ResultCode error, std::string dialog_text,
|
||||
std::string fullscreen_text,
|
||||
std::function<void()> finished) const = 0;
|
||||
};
|
||||
|
||||
class DefaultErrorApplet final : public ErrorApplet {
|
||||
public:
|
||||
void ShowError(ResultCode error, std::function<void()> finished) const override;
|
||||
void ShowErrorWithTimestamp(ResultCode error, std::chrono::seconds time,
|
||||
std::function<void()> finished) const override;
|
||||
void ShowCustomErrorText(ResultCode error, std::string main_text, std::string detail_text,
|
||||
std::function<void()> finished) const override;
|
||||
};
|
||||
|
||||
} // namespace Core::Frontend
|
56
src/core/frontend/applets/general_frontend.cpp
Executable file
56
src/core/frontend/applets/general_frontend.cpp
Executable file
@@ -0,0 +1,56 @@
|
||||
// Copyright 2019 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "common/logging/log.h"
|
||||
#include "core/frontend/applets/general_frontend.h"
|
||||
|
||||
namespace Core::Frontend {
|
||||
|
||||
ParentalControlsApplet::~ParentalControlsApplet() = default;
|
||||
|
||||
DefaultParentalControlsApplet::~DefaultParentalControlsApplet() = default;
|
||||
|
||||
void DefaultParentalControlsApplet::VerifyPIN(std::function<void(bool)> finished,
|
||||
bool suspend_future_verification_temporarily) {
|
||||
LOG_INFO(Service_AM,
|
||||
"Application requested frontend to verify PIN (normal), "
|
||||
"suspend_future_verification_temporarily={}, verifying as correct.",
|
||||
suspend_future_verification_temporarily);
|
||||
finished(true);
|
||||
}
|
||||
|
||||
void DefaultParentalControlsApplet::VerifyPINForSettings(std::function<void(bool)> finished) {
|
||||
LOG_INFO(Service_AM,
|
||||
"Application requested frontend to verify PIN (settings), verifying as correct.");
|
||||
finished(true);
|
||||
}
|
||||
|
||||
void DefaultParentalControlsApplet::RegisterPIN(std::function<void()> finished) {
|
||||
LOG_INFO(Service_AM, "Application requested frontend to register new PIN");
|
||||
finished();
|
||||
}
|
||||
|
||||
void DefaultParentalControlsApplet::ChangePIN(std::function<void()> finished) {
|
||||
LOG_INFO(Service_AM, "Application requested frontend to change PIN to new value");
|
||||
finished();
|
||||
}
|
||||
|
||||
PhotoViewerApplet::~PhotoViewerApplet() = default;
|
||||
|
||||
DefaultPhotoViewerApplet::~DefaultPhotoViewerApplet() = default;
|
||||
|
||||
void DefaultPhotoViewerApplet::ShowPhotosForApplication(u64 title_id,
|
||||
std::function<void()> finished) const {
|
||||
LOG_INFO(Service_AM,
|
||||
"Application requested frontend to display stored photos for title_id={:016X}",
|
||||
title_id);
|
||||
finished();
|
||||
}
|
||||
|
||||
void DefaultPhotoViewerApplet::ShowAllPhotos(std::function<void()> finished) const {
|
||||
LOG_INFO(Service_AM, "Application requested frontend to display all stored photos.");
|
||||
finished();
|
||||
}
|
||||
|
||||
} // namespace Core::Frontend
|
61
src/core/frontend/applets/general_frontend.h
Executable file
61
src/core/frontend/applets/general_frontend.h
Executable file
@@ -0,0 +1,61 @@
|
||||
// Copyright 2019 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <optional>
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Core::Frontend {
|
||||
|
||||
class ParentalControlsApplet {
|
||||
public:
|
||||
virtual ~ParentalControlsApplet();
|
||||
|
||||
// Prompts the user to enter a PIN and calls the callback with whether or not it matches the
|
||||
// correct PIN. If the bool is passed, and the PIN was recently entered correctly, the frontend
|
||||
// should not prompt and simply return true.
|
||||
virtual void VerifyPIN(std::function<void(bool)> finished,
|
||||
bool suspend_future_verification_temporarily) = 0;
|
||||
|
||||
// Prompts the user to enter a PIN and calls the callback for correctness. Frontends can
|
||||
// optionally alert the user that this is to change parental controls settings.
|
||||
virtual void VerifyPINForSettings(std::function<void(bool)> finished) = 0;
|
||||
|
||||
// Prompts the user to create a new PIN for pctl and stores it with the service.
|
||||
virtual void RegisterPIN(std::function<void()> finished) = 0;
|
||||
|
||||
// Prompts the user to verify the current PIN and then store a new one into pctl.
|
||||
virtual void ChangePIN(std::function<void()> finished) = 0;
|
||||
};
|
||||
|
||||
class DefaultParentalControlsApplet final : public ParentalControlsApplet {
|
||||
public:
|
||||
~DefaultParentalControlsApplet() override;
|
||||
|
||||
void VerifyPIN(std::function<void(bool)> finished,
|
||||
bool suspend_future_verification_temporarily) override;
|
||||
void VerifyPINForSettings(std::function<void(bool)> finished) override;
|
||||
void RegisterPIN(std::function<void()> finished) override;
|
||||
void ChangePIN(std::function<void()> finished) override;
|
||||
};
|
||||
|
||||
class PhotoViewerApplet {
|
||||
public:
|
||||
virtual ~PhotoViewerApplet();
|
||||
|
||||
virtual void ShowPhotosForApplication(u64 title_id, std::function<void()> finished) const = 0;
|
||||
virtual void ShowAllPhotos(std::function<void()> finished) const = 0;
|
||||
};
|
||||
|
||||
class DefaultPhotoViewerApplet final : public PhotoViewerApplet {
|
||||
public:
|
||||
~DefaultPhotoViewerApplet() override;
|
||||
|
||||
void ShowPhotosForApplication(u64 title_id, std::function<void()> finished) const override;
|
||||
void ShowAllPhotos(std::function<void()> finished) const override;
|
||||
};
|
||||
|
||||
} // namespace Core::Frontend
|
20
src/core/frontend/applets/profile_select.cpp
Executable file
20
src/core/frontend/applets/profile_select.cpp
Executable file
@@ -0,0 +1,20 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "core/frontend/applets/profile_select.h"
|
||||
#include "core/hle/service/acc/profile_manager.h"
|
||||
#include "core/settings.h"
|
||||
|
||||
namespace Core::Frontend {
|
||||
|
||||
ProfileSelectApplet::~ProfileSelectApplet() = default;
|
||||
|
||||
void DefaultProfileSelectApplet::SelectProfile(
|
||||
std::function<void(std::optional<Common::UUID>)> callback) const {
|
||||
Service::Account::ProfileManager manager;
|
||||
callback(manager.GetUser(Settings::values.current_user).value_or(Common::UUID{}));
|
||||
LOG_INFO(Service_ACC, "called, selecting current user instead of prompting...");
|
||||
}
|
||||
|
||||
} // namespace Core::Frontend
|
25
src/core/frontend/applets/profile_select.h
Executable file
25
src/core/frontend/applets/profile_select.h
Executable file
@@ -0,0 +1,25 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <optional>
|
||||
#include "common/uuid.h"
|
||||
|
||||
namespace Core::Frontend {
|
||||
|
||||
class ProfileSelectApplet {
|
||||
public:
|
||||
virtual ~ProfileSelectApplet();
|
||||
|
||||
virtual void SelectProfile(std::function<void(std::optional<Common::UUID>)> callback) const = 0;
|
||||
};
|
||||
|
||||
class DefaultProfileSelectApplet final : public ProfileSelectApplet {
|
||||
public:
|
||||
void SelectProfile(std::function<void(std::optional<Common::UUID>)> callback) const override;
|
||||
};
|
||||
|
||||
} // namespace Core::Frontend
|
29
src/core/frontend/applets/software_keyboard.cpp
Executable file
29
src/core/frontend/applets/software_keyboard.cpp
Executable file
@@ -0,0 +1,29 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "common/logging/backend.h"
|
||||
#include "common/string_util.h"
|
||||
#include "core/frontend/applets/software_keyboard.h"
|
||||
|
||||
namespace Core::Frontend {
|
||||
SoftwareKeyboardApplet::~SoftwareKeyboardApplet() = default;
|
||||
|
||||
void DefaultSoftwareKeyboardApplet::RequestText(
|
||||
std::function<void(std::optional<std::u16string>)> out,
|
||||
SoftwareKeyboardParameters parameters) const {
|
||||
if (parameters.initial_text.empty())
|
||||
out(u"yuzu");
|
||||
|
||||
out(parameters.initial_text);
|
||||
}
|
||||
|
||||
void DefaultSoftwareKeyboardApplet::SendTextCheckDialog(
|
||||
std::u16string error_message, std::function<void()> finished_check) const {
|
||||
LOG_WARNING(Service_AM,
|
||||
"(STUBBED) called - Default fallback software keyboard does not support text "
|
||||
"check! (error_message={})",
|
||||
Common::UTF16ToUTF8(error_message));
|
||||
finished_check();
|
||||
}
|
||||
} // namespace Core::Frontend
|
54
src/core/frontend/applets/software_keyboard.h
Executable file
54
src/core/frontend/applets/software_keyboard.h
Executable file
@@ -0,0 +1,54 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include "common/bit_field.h"
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Core::Frontend {
|
||||
struct SoftwareKeyboardParameters {
|
||||
std::u16string submit_text;
|
||||
std::u16string header_text;
|
||||
std::u16string sub_text;
|
||||
std::u16string guide_text;
|
||||
std::u16string initial_text;
|
||||
std::size_t max_length;
|
||||
bool password;
|
||||
bool cursor_at_beginning;
|
||||
|
||||
union {
|
||||
u8 value;
|
||||
|
||||
BitField<1, 1, u8> disable_space;
|
||||
BitField<2, 1, u8> disable_address;
|
||||
BitField<3, 1, u8> disable_percent;
|
||||
BitField<4, 1, u8> disable_slash;
|
||||
BitField<6, 1, u8> disable_number;
|
||||
BitField<7, 1, u8> disable_download_code;
|
||||
};
|
||||
};
|
||||
|
||||
class SoftwareKeyboardApplet {
|
||||
public:
|
||||
virtual ~SoftwareKeyboardApplet();
|
||||
|
||||
virtual void RequestText(std::function<void(std::optional<std::u16string>)> out,
|
||||
SoftwareKeyboardParameters parameters) const = 0;
|
||||
virtual void SendTextCheckDialog(std::u16string error_message,
|
||||
std::function<void()> finished_check) const = 0;
|
||||
};
|
||||
|
||||
class DefaultSoftwareKeyboardApplet final : public SoftwareKeyboardApplet {
|
||||
public:
|
||||
void RequestText(std::function<void(std::optional<std::u16string>)> out,
|
||||
SoftwareKeyboardParameters parameters) const override;
|
||||
void SendTextCheckDialog(std::u16string error_message,
|
||||
std::function<void()> finished_check) const override;
|
||||
};
|
||||
|
||||
} // namespace Core::Frontend
|
32
src/core/frontend/applets/web_browser.cpp
Executable file
32
src/core/frontend/applets/web_browser.cpp
Executable file
@@ -0,0 +1,32 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "common/logging/log.h"
|
||||
#include "core/frontend/applets/web_browser.h"
|
||||
|
||||
namespace Core::Frontend {
|
||||
|
||||
WebBrowserApplet::~WebBrowserApplet() = default;
|
||||
|
||||
DefaultWebBrowserApplet::~DefaultWebBrowserApplet() = default;
|
||||
|
||||
void DefaultWebBrowserApplet::OpenLocalWebPage(
|
||||
std::string_view local_url, std::function<void()> extract_romfs_callback,
|
||||
std::function<void(Service::AM::Applets::WebExitReason, std::string)> callback) const {
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called, backend requested to open local web page at {}",
|
||||
local_url);
|
||||
|
||||
callback(Service::AM::Applets::WebExitReason::WindowClosed, "http://localhost/");
|
||||
}
|
||||
|
||||
void DefaultWebBrowserApplet::OpenExternalWebPage(
|
||||
std::string_view external_url,
|
||||
std::function<void(Service::AM::Applets::WebExitReason, std::string)> callback) const {
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called, backend requested to open external web page at {}",
|
||||
external_url);
|
||||
|
||||
callback(Service::AM::Applets::WebExitReason::WindowClosed, "http://localhost/");
|
||||
}
|
||||
|
||||
} // namespace Core::Frontend
|
40
src/core/frontend/applets/web_browser.h
Executable file
40
src/core/frontend/applets/web_browser.h
Executable file
@@ -0,0 +1,40 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <string_view>
|
||||
|
||||
#include "core/hle/service/am/applets/web_types.h"
|
||||
|
||||
namespace Core::Frontend {
|
||||
|
||||
class WebBrowserApplet {
|
||||
public:
|
||||
virtual ~WebBrowserApplet();
|
||||
|
||||
virtual void OpenLocalWebPage(
|
||||
std::string_view local_url, std::function<void()> extract_romfs_callback,
|
||||
std::function<void(Service::AM::Applets::WebExitReason, std::string)> callback) const = 0;
|
||||
|
||||
virtual void OpenExternalWebPage(
|
||||
std::string_view external_url,
|
||||
std::function<void(Service::AM::Applets::WebExitReason, std::string)> callback) const = 0;
|
||||
};
|
||||
|
||||
class DefaultWebBrowserApplet final : public WebBrowserApplet {
|
||||
public:
|
||||
~DefaultWebBrowserApplet() override;
|
||||
|
||||
void OpenLocalWebPage(std::string_view local_url, std::function<void()> extract_romfs_callback,
|
||||
std::function<void(Service::AM::Applets::WebExitReason, std::string)>
|
||||
callback) const override;
|
||||
|
||||
void OpenExternalWebPage(std::string_view external_url,
|
||||
std::function<void(Service::AM::Applets::WebExitReason, std::string)>
|
||||
callback) const override;
|
||||
};
|
||||
|
||||
} // namespace Core::Frontend
|
118
src/core/frontend/emu_window.cpp
Executable file
118
src/core/frontend/emu_window.cpp
Executable file
@@ -0,0 +1,118 @@
|
||||
// Copyright 2014 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <cmath>
|
||||
#include <mutex>
|
||||
#include "core/frontend/emu_window.h"
|
||||
#include "core/frontend/input.h"
|
||||
#include "core/settings.h"
|
||||
|
||||
namespace Core::Frontend {
|
||||
|
||||
GraphicsContext::~GraphicsContext() = default;
|
||||
|
||||
class EmuWindow::TouchState : public Input::Factory<Input::TouchDevice>,
|
||||
public std::enable_shared_from_this<TouchState> {
|
||||
public:
|
||||
std::unique_ptr<Input::TouchDevice> Create(const Common::ParamPackage&) override {
|
||||
return std::make_unique<Device>(shared_from_this());
|
||||
}
|
||||
|
||||
std::mutex mutex;
|
||||
|
||||
bool touch_pressed = false; ///< True if touchpad area is currently pressed, otherwise false
|
||||
|
||||
float touch_x = 0.0f; ///< Touchpad X-position
|
||||
float touch_y = 0.0f; ///< Touchpad Y-position
|
||||
|
||||
private:
|
||||
class Device : public Input::TouchDevice {
|
||||
public:
|
||||
explicit Device(std::weak_ptr<TouchState>&& touch_state) : touch_state(touch_state) {}
|
||||
std::tuple<float, float, bool> GetStatus() const override {
|
||||
if (auto state = touch_state.lock()) {
|
||||
std::lock_guard guard{state->mutex};
|
||||
return std::make_tuple(state->touch_x, state->touch_y, state->touch_pressed);
|
||||
}
|
||||
return std::make_tuple(0.0f, 0.0f, false);
|
||||
}
|
||||
|
||||
private:
|
||||
std::weak_ptr<TouchState> touch_state;
|
||||
};
|
||||
};
|
||||
|
||||
EmuWindow::EmuWindow() {
|
||||
// TODO: Find a better place to set this.
|
||||
config.min_client_area_size =
|
||||
std::make_pair(Layout::MinimumSize::Width, Layout::MinimumSize::Height);
|
||||
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");
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @param framebuffer_x Framebuffer x-coordinate to check
|
||||
* @param framebuffer_y Framebuffer y-coordinate to check
|
||||
* @return True if the coordinates are within the touchpad, otherwise false
|
||||
*/
|
||||
static bool IsWithinTouchscreen(const Layout::FramebufferLayout& layout, unsigned framebuffer_x,
|
||||
unsigned framebuffer_y) {
|
||||
return (framebuffer_y >= layout.screen.top && framebuffer_y < layout.screen.bottom &&
|
||||
framebuffer_x >= layout.screen.left && framebuffer_x < layout.screen.right);
|
||||
}
|
||||
|
||||
std::tuple<unsigned, unsigned> EmuWindow::ClipToTouchScreen(unsigned new_x, unsigned new_y) const {
|
||||
new_x = std::max(new_x, framebuffer_layout.screen.left);
|
||||
new_x = std::min(new_x, framebuffer_layout.screen.right - 1);
|
||||
|
||||
new_y = std::max(new_y, framebuffer_layout.screen.top);
|
||||
new_y = std::min(new_y, framebuffer_layout.screen.bottom - 1);
|
||||
|
||||
return std::make_tuple(new_x, new_y);
|
||||
}
|
||||
|
||||
void EmuWindow::TouchPressed(unsigned framebuffer_x, unsigned framebuffer_y) {
|
||||
if (!IsWithinTouchscreen(framebuffer_layout, framebuffer_x, framebuffer_y))
|
||||
return;
|
||||
|
||||
std::lock_guard guard{touch_state->mutex};
|
||||
touch_state->touch_x =
|
||||
static_cast<float>(framebuffer_x - framebuffer_layout.screen.left) /
|
||||
static_cast<float>(framebuffer_layout.screen.right - framebuffer_layout.screen.left);
|
||||
touch_state->touch_y =
|
||||
static_cast<float>(framebuffer_y - framebuffer_layout.screen.top) /
|
||||
static_cast<float>(framebuffer_layout.screen.bottom - framebuffer_layout.screen.top);
|
||||
|
||||
touch_state->touch_pressed = true;
|
||||
}
|
||||
|
||||
void EmuWindow::TouchReleased() {
|
||||
std::lock_guard guard{touch_state->mutex};
|
||||
touch_state->touch_pressed = false;
|
||||
touch_state->touch_x = 0;
|
||||
touch_state->touch_y = 0;
|
||||
}
|
||||
|
||||
void EmuWindow::TouchMoved(unsigned framebuffer_x, unsigned framebuffer_y) {
|
||||
if (!touch_state->touch_pressed)
|
||||
return;
|
||||
|
||||
if (!IsWithinTouchscreen(framebuffer_layout, framebuffer_x, framebuffer_y))
|
||||
std::tie(framebuffer_x, framebuffer_y) = ClipToTouchScreen(framebuffer_x, framebuffer_y);
|
||||
|
||||
TouchPressed(framebuffer_x, framebuffer_y);
|
||||
}
|
||||
|
||||
void EmuWindow::UpdateCurrentFramebufferLayout(unsigned width, unsigned height) {
|
||||
NotifyFramebufferLayoutChanged(Layout::DefaultFrameLayout(width, height));
|
||||
}
|
||||
|
||||
} // namespace Core::Frontend
|
241
src/core/frontend/emu_window.h
Executable file
241
src/core/frontend/emu_window.h
Executable file
@@ -0,0 +1,241 @@
|
||||
// Copyright 2014 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <tuple>
|
||||
#include <utility>
|
||||
#include "common/common_types.h"
|
||||
#include "core/frontend/framebuffer_layout.h"
|
||||
|
||||
namespace Core::Frontend {
|
||||
|
||||
/// Information for the Graphics Backends signifying what type of screen pointer is in
|
||||
/// WindowInformation
|
||||
enum class WindowSystemType {
|
||||
Headless,
|
||||
Windows,
|
||||
X11,
|
||||
Wayland,
|
||||
};
|
||||
|
||||
/**
|
||||
* Represents a drawing context that supports graphics operations.
|
||||
*/
|
||||
class GraphicsContext {
|
||||
public:
|
||||
virtual ~GraphicsContext();
|
||||
|
||||
/// Inform the driver to swap the front/back buffers and present the current image
|
||||
virtual void SwapBuffers() {}
|
||||
|
||||
/// Makes the graphics context current for the caller thread
|
||||
virtual void MakeCurrent() {}
|
||||
|
||||
/// Releases (dunno if this is the "right" word) the context from the caller thread
|
||||
virtual void DoneCurrent() {}
|
||||
|
||||
class Scoped {
|
||||
public:
|
||||
[[nodiscard]] explicit Scoped(GraphicsContext& context_) : context(context_) {
|
||||
context.MakeCurrent();
|
||||
}
|
||||
~Scoped() {
|
||||
context.DoneCurrent();
|
||||
}
|
||||
|
||||
private:
|
||||
GraphicsContext& context;
|
||||
};
|
||||
|
||||
/// Calls MakeCurrent on the context and calls DoneCurrent when the scope for the returned value
|
||||
/// ends
|
||||
[[nodiscard]] Scoped Acquire() {
|
||||
return Scoped{*this};
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Abstraction class used to provide an interface between emulation code and the frontend
|
||||
* (e.g. SDL, QGLWidget, GLFW, etc...).
|
||||
*
|
||||
* Design notes on the interaction between EmuWindow and the emulation core:
|
||||
* - Generally, decisions on anything visible to the user should be left up to the GUI.
|
||||
* For example, the emulation core should not try to dictate some window title or size.
|
||||
* This stuff is not the core's business and only causes problems with regards to thread-safety
|
||||
* anyway.
|
||||
* - Under certain circumstances, it may be desirable for the core to politely request the GUI
|
||||
* to set e.g. a minimum window size. However, the GUI should always be free to ignore any
|
||||
* such hints.
|
||||
* - EmuWindow may expose some of its state as read-only to the emulation core, however care
|
||||
* should be taken to make sure the provided information is self-consistent. This requires
|
||||
* some sort of synchronization (most of this is still a TODO).
|
||||
* - DO NOT TREAT THIS CLASS AS A GUI TOOLKIT ABSTRACTION LAYER. That's not what it is. Please
|
||||
* re-read the upper points again and think about it if you don't see this.
|
||||
*/
|
||||
class EmuWindow {
|
||||
public:
|
||||
/// 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;
|
||||
};
|
||||
|
||||
/// Data describing host window system information
|
||||
struct WindowSystemInfo {
|
||||
// Window system type. Determines which GL context or Vulkan WSI is used.
|
||||
WindowSystemType type = WindowSystemType::Headless;
|
||||
|
||||
// Connection to a display server. This is used on X11 and Wayland platforms.
|
||||
void* display_connection = nullptr;
|
||||
|
||||
// Render surface. This is a pointer to the native window handle, which depends
|
||||
// on the platform. e.g. HWND for Windows, Window for X11. If the surface is
|
||||
// set to nullptr, the video backend will run in headless mode.
|
||||
void* render_surface = nullptr;
|
||||
|
||||
// Scale of the render surface. For hidpi systems, this will be >1.
|
||||
float render_surface_scale = 1.0f;
|
||||
};
|
||||
|
||||
/// Called from GPU thread when a frame is displayed.
|
||||
virtual void OnFrameDisplayed() {}
|
||||
|
||||
/**
|
||||
* Returns a GraphicsContext that the frontend provides to be used for rendering.
|
||||
*/
|
||||
virtual std::unique_ptr<GraphicsContext> CreateSharedContext() const = 0;
|
||||
|
||||
/// Returns if window is shown (not minimized)
|
||||
virtual bool IsShown() const = 0;
|
||||
|
||||
/**
|
||||
* Signal that a touch pressed event has occurred (e.g. mouse click pressed)
|
||||
* @param framebuffer_x Framebuffer x-coordinate that was pressed
|
||||
* @param framebuffer_y Framebuffer y-coordinate that was pressed
|
||||
*/
|
||||
void TouchPressed(unsigned framebuffer_x, unsigned framebuffer_y);
|
||||
|
||||
/// Signal that a touch released event has occurred (e.g. mouse click released)
|
||||
void TouchReleased();
|
||||
|
||||
/**
|
||||
* Signal that a touch movement event has occurred (e.g. mouse was moved over the emu window)
|
||||
* @param framebuffer_x Framebuffer x-coordinate
|
||||
* @param framebuffer_y Framebuffer y-coordinate
|
||||
*/
|
||||
void TouchMoved(unsigned framebuffer_x, unsigned framebuffer_y);
|
||||
|
||||
/**
|
||||
* Returns currently active configuration.
|
||||
* @note Accesses to the returned object need not be consistent because it may be modified in
|
||||
* another thread
|
||||
*/
|
||||
const WindowConfig& GetActiveConfig() const {
|
||||
return active_config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Requests the internal configuration to be replaced by the specified argument at some point in
|
||||
* the future.
|
||||
* @note This method is thread-safe, because it delays configuration changes to the GUI event
|
||||
* loop. Hence there is no guarantee on when the requested configuration will be active.
|
||||
*/
|
||||
void SetConfig(const WindowConfig& val) {
|
||||
config = val;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns system information about the drawing area.
|
||||
*/
|
||||
const WindowSystemInfo& GetWindowInfo() const {
|
||||
return window_info;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the framebuffer layout (width, height, and screen regions)
|
||||
* @note This method is thread-safe
|
||||
*/
|
||||
const Layout::FramebufferLayout& GetFramebufferLayout() const {
|
||||
return framebuffer_layout;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience method to update the current frame layout
|
||||
* Read from the current settings to determine which layout to use.
|
||||
*/
|
||||
void UpdateCurrentFramebufferLayout(unsigned width, unsigned height);
|
||||
|
||||
protected:
|
||||
explicit EmuWindow();
|
||||
virtual ~EmuWindow();
|
||||
|
||||
/**
|
||||
* Processes any pending configuration changes from the last SetConfig call.
|
||||
* This method invokes OnMinimalClientAreaChangeRequest if the corresponding configuration
|
||||
* field changed.
|
||||
* @note Implementations will usually want to call this from the GUI thread.
|
||||
* @todo Actually call this in existing implementations.
|
||||
*/
|
||||
void ProcessConfigurationChanges() {
|
||||
// TODO: For proper thread safety, we should eventually implement a proper
|
||||
// multiple-writer/single-reader queue...
|
||||
|
||||
if (config.min_client_area_size != active_config.min_client_area_size) {
|
||||
OnMinimalClientAreaChangeRequest(config.min_client_area_size);
|
||||
config.min_client_area_size = active_config.min_client_area_size;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update framebuffer layout with the given parameter.
|
||||
* @note EmuWindow implementations will usually use this in window resize event handlers.
|
||||
*/
|
||||
void NotifyFramebufferLayoutChanged(const Layout::FramebufferLayout& layout) {
|
||||
framebuffer_layout = layout;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update internal client area size with the given parameter.
|
||||
* @note EmuWindow implementations will usually use this in window resize event handlers.
|
||||
*/
|
||||
void NotifyClientAreaSizeChanged(const std::pair<unsigned, unsigned>& size) {
|
||||
client_area_width = size.first;
|
||||
client_area_height = size.second;
|
||||
}
|
||||
|
||||
WindowSystemInfo window_info;
|
||||
|
||||
private:
|
||||
/**
|
||||
* Handler called when the minimal client area was requested to be changed via SetConfig.
|
||||
* For the request to be honored, EmuWindow implementations will usually reimplement this
|
||||
* function.
|
||||
*/
|
||||
virtual void OnMinimalClientAreaChangeRequest(std::pair<unsigned, unsigned>) {
|
||||
// By default, ignore this request and do nothing.
|
||||
}
|
||||
|
||||
Layout::FramebufferLayout framebuffer_layout; ///< Current framebuffer layout
|
||||
|
||||
unsigned client_area_width; ///< Current client width, should be set by window impl.
|
||||
unsigned client_area_height; ///< Current client height, should be set by window impl.
|
||||
|
||||
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;
|
||||
|
||||
/**
|
||||
* Clip the provided coordinates to be inside the touchscreen area.
|
||||
*/
|
||||
std::tuple<unsigned, unsigned> ClipToTouchScreen(unsigned new_x, unsigned new_y) const;
|
||||
};
|
||||
|
||||
} // namespace Core::Frontend
|
76
src/core/frontend/framebuffer_layout.cpp
Executable file
76
src/core/frontend/framebuffer_layout.cpp
Executable file
@@ -0,0 +1,76 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <cmath>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "core/frontend/framebuffer_layout.h"
|
||||
#include "core/settings.h"
|
||||
|
||||
namespace Layout {
|
||||
|
||||
// Finds the largest size subrectangle contained in window area that is confined to the aspect ratio
|
||||
template <class T>
|
||||
static Common::Rectangle<T> MaxRectangle(Common::Rectangle<T> window_area,
|
||||
float screen_aspect_ratio) {
|
||||
const float scale = std::min(static_cast<float>(window_area.GetWidth()),
|
||||
static_cast<float>(window_area.GetHeight()) / screen_aspect_ratio);
|
||||
return Common::Rectangle<T>{0, 0, static_cast<T>(std::round(scale)),
|
||||
static_cast<T>(std::round(scale * screen_aspect_ratio))};
|
||||
}
|
||||
|
||||
FramebufferLayout DefaultFrameLayout(u32 width, u32 height) {
|
||||
ASSERT(width > 0);
|
||||
ASSERT(height > 0);
|
||||
// The drawing code needs at least somewhat valid values for both screens
|
||||
// so just calculate them both even if the other isn't showing.
|
||||
FramebufferLayout res{width, height, false, {}};
|
||||
|
||||
const float window_aspect_ratio = static_cast<float>(height) / static_cast<float>(width);
|
||||
const float emulation_aspect_ratio = EmulationAspectRatio(
|
||||
static_cast<AspectRatio>(Settings::values.aspect_ratio.GetValue()), window_aspect_ratio);
|
||||
|
||||
const Common::Rectangle<u32> screen_window_area{0, 0, width, height};
|
||||
Common::Rectangle<u32> screen = MaxRectangle(screen_window_area, emulation_aspect_ratio);
|
||||
|
||||
if (window_aspect_ratio < emulation_aspect_ratio) {
|
||||
screen = screen.TranslateX((screen_window_area.GetWidth() - screen.GetWidth()) / 2);
|
||||
} else {
|
||||
screen = screen.TranslateY((height - screen.GetHeight()) / 2);
|
||||
}
|
||||
|
||||
res.screen = screen;
|
||||
return res;
|
||||
}
|
||||
|
||||
FramebufferLayout FrameLayoutFromResolutionScale(u32 res_scale) {
|
||||
u32 width, height;
|
||||
|
||||
if (Settings::values.use_docked_mode.GetValue()) {
|
||||
width = ScreenDocked::Width * res_scale;
|
||||
height = ScreenDocked::Height * res_scale;
|
||||
} else {
|
||||
width = ScreenUndocked::Width * res_scale;
|
||||
height = ScreenUndocked::Height * res_scale;
|
||||
}
|
||||
|
||||
return DefaultFrameLayout(width, height);
|
||||
}
|
||||
|
||||
float EmulationAspectRatio(AspectRatio aspect, float window_aspect_ratio) {
|
||||
switch (aspect) {
|
||||
case AspectRatio::Default:
|
||||
return static_cast<float>(ScreenUndocked::Height) / ScreenUndocked::Width;
|
||||
case AspectRatio::R4_3:
|
||||
return 3.0f / 4.0f;
|
||||
case AspectRatio::R21_9:
|
||||
return 9.0f / 21.0f;
|
||||
case AspectRatio::StretchToWindow:
|
||||
return window_aspect_ratio;
|
||||
default:
|
||||
return static_cast<float>(ScreenUndocked::Height) / ScreenUndocked::Width;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Layout
|
73
src/core/frontend/framebuffer_layout.h
Executable file
73
src/core/frontend/framebuffer_layout.h
Executable file
@@ -0,0 +1,73 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "common/math_util.h"
|
||||
|
||||
namespace Layout {
|
||||
|
||||
namespace MinimumSize {
|
||||
constexpr u32 Width = 640;
|
||||
constexpr u32 Height = 360;
|
||||
} // namespace MinimumSize
|
||||
|
||||
namespace ScreenUndocked {
|
||||
constexpr u32 Width = 1280;
|
||||
constexpr u32 Height = 720;
|
||||
} // namespace ScreenUndocked
|
||||
|
||||
namespace ScreenDocked {
|
||||
constexpr u32 Width = 1920;
|
||||
constexpr u32 Height = 1080;
|
||||
} // namespace ScreenDocked
|
||||
|
||||
enum class AspectRatio {
|
||||
Default,
|
||||
R4_3,
|
||||
R21_9,
|
||||
StretchToWindow,
|
||||
};
|
||||
|
||||
/// Describes the layout of the window framebuffer
|
||||
struct FramebufferLayout {
|
||||
u32 width{ScreenUndocked::Width};
|
||||
u32 height{ScreenUndocked::Height};
|
||||
bool is_srgb{};
|
||||
|
||||
Common::Rectangle<u32> screen;
|
||||
|
||||
/**
|
||||
* Returns the ration of pixel size of the screen, compared to the native size of the undocked
|
||||
* Switch screen.
|
||||
*/
|
||||
float GetScalingRatio() const {
|
||||
return static_cast<float>(screen.GetWidth()) / ScreenUndocked::Width;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Factory method for constructing a default FramebufferLayout
|
||||
* @param width Window framebuffer width in pixels
|
||||
* @param height Window framebuffer height in pixels
|
||||
* @return Newly created FramebufferLayout object with default screen regions initialized
|
||||
*/
|
||||
FramebufferLayout DefaultFrameLayout(u32 width, u32 height);
|
||||
|
||||
/**
|
||||
* Convenience method to get frame layout by resolution scale
|
||||
* @param res_scale resolution scale factor
|
||||
*/
|
||||
FramebufferLayout FrameLayoutFromResolutionScale(u32 res_scale);
|
||||
|
||||
/**
|
||||
* Convenience method to determine emulation aspect ratio
|
||||
* @param aspect Represents the index of aspect ratio stored in Settings::values.aspect_ratio
|
||||
* @param window_aspect_ratio Current window aspect ratio
|
||||
* @return Emulation render window aspect ratio
|
||||
*/
|
||||
float EmulationAspectRatio(AspectRatio aspect, float window_aspect_ratio);
|
||||
|
||||
} // namespace Layout
|
183
src/core/frontend/input.h
Executable file
183
src/core/frontend/input.h
Executable file
@@ -0,0 +1,183 @@
|
||||
// Copyright 2017 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include "common/logging/log.h"
|
||||
#include "common/param_package.h"
|
||||
#include "common/vector_math.h"
|
||||
|
||||
namespace Input {
|
||||
|
||||
enum class AnalogDirection : u8 {
|
||||
RIGHT,
|
||||
LEFT,
|
||||
UP,
|
||||
DOWN,
|
||||
};
|
||||
|
||||
/// An abstract class template for an input device (a button, an analog input, etc.).
|
||||
template <typename StatusType>
|
||||
class InputDevice {
|
||||
public:
|
||||
virtual ~InputDevice() = default;
|
||||
virtual StatusType GetStatus() const {
|
||||
return {};
|
||||
}
|
||||
virtual bool GetAnalogDirectionStatus([[maybe_unused]] AnalogDirection direction) const {
|
||||
return {};
|
||||
}
|
||||
virtual bool SetRumblePlay([[maybe_unused]] f32 amp_low, [[maybe_unused]] f32 freq_low,
|
||||
[[maybe_unused]] f32 amp_high,
|
||||
[[maybe_unused]] f32 freq_high) const {
|
||||
return {};
|
||||
}
|
||||
};
|
||||
|
||||
/// An abstract class template for a factory that can create input devices.
|
||||
template <typename InputDeviceType>
|
||||
class Factory {
|
||||
public:
|
||||
virtual ~Factory() = default;
|
||||
virtual std::unique_ptr<InputDeviceType> Create(const Common::ParamPackage&) = 0;
|
||||
};
|
||||
|
||||
namespace Impl {
|
||||
|
||||
template <typename InputDeviceType>
|
||||
using FactoryListType = std::unordered_map<std::string, std::shared_ptr<Factory<InputDeviceType>>>;
|
||||
|
||||
template <typename InputDeviceType>
|
||||
struct FactoryList {
|
||||
static FactoryListType<InputDeviceType> list;
|
||||
};
|
||||
|
||||
template <typename InputDeviceType>
|
||||
FactoryListType<InputDeviceType> FactoryList<InputDeviceType>::list;
|
||||
|
||||
} // namespace Impl
|
||||
|
||||
/**
|
||||
* Registers an input device factory.
|
||||
* @tparam InputDeviceType the type of input devices the factory can create
|
||||
* @param name the name of the factory. Will be used to match the "engine" parameter when creating
|
||||
* a device
|
||||
* @param factory the factory object to register
|
||||
*/
|
||||
template <typename InputDeviceType>
|
||||
void RegisterFactory(const std::string& name, std::shared_ptr<Factory<InputDeviceType>> factory) {
|
||||
auto pair = std::make_pair(name, std::move(factory));
|
||||
if (!Impl::FactoryList<InputDeviceType>::list.insert(std::move(pair)).second) {
|
||||
LOG_ERROR(Input, "Factory '{}' already registered", name);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregisters an input device factory.
|
||||
* @tparam InputDeviceType the type of input devices the factory can create
|
||||
* @param name the name of the factory to unregister
|
||||
*/
|
||||
template <typename InputDeviceType>
|
||||
void UnregisterFactory(const std::string& name) {
|
||||
if (Impl::FactoryList<InputDeviceType>::list.erase(name) == 0) {
|
||||
LOG_ERROR(Input, "Factory '{}' not registered", name);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an input device from given paramters.
|
||||
* @tparam InputDeviceType the type of input devices to create
|
||||
* @param params a serialized ParamPackage string contains all parameters for creating the device
|
||||
*/
|
||||
template <typename InputDeviceType>
|
||||
std::unique_ptr<InputDeviceType> CreateDevice(const std::string& params) {
|
||||
const Common::ParamPackage package(params);
|
||||
const std::string engine = package.Get("engine", "null");
|
||||
const auto& factory_list = Impl::FactoryList<InputDeviceType>::list;
|
||||
const auto pair = factory_list.find(engine);
|
||||
if (pair == factory_list.end()) {
|
||||
if (engine != "null") {
|
||||
LOG_ERROR(Input, "Unknown engine name: {}", engine);
|
||||
}
|
||||
return std::make_unique<InputDeviceType>();
|
||||
}
|
||||
return pair->second->Create(package);
|
||||
}
|
||||
|
||||
/**
|
||||
* A button device is an input device that returns bool as status.
|
||||
* true for pressed; false for released.
|
||||
*/
|
||||
using ButtonDevice = InputDevice<bool>;
|
||||
|
||||
/**
|
||||
* An analog device is an input device that returns a tuple of x and y coordinates as status. The
|
||||
* coordinates are within the unit circle. x+ is defined as right direction, and y+ is defined as up
|
||||
* direction
|
||||
*/
|
||||
using AnalogDevice = InputDevice<std::tuple<float, float>>;
|
||||
|
||||
/**
|
||||
* A vibration device is an input device that returns an unsigned byte as status.
|
||||
* It represents whether the vibration device supports vibration or not.
|
||||
* If the status returns 1, it supports vibration. Otherwise, it does not support vibration.
|
||||
*/
|
||||
using VibrationDevice = InputDevice<u8>;
|
||||
|
||||
/**
|
||||
* A motion status is an object that returns a tuple of accelerometer state vector,
|
||||
* gyroscope state vector, rotation state vector and orientation state matrix.
|
||||
*
|
||||
* For both vectors:
|
||||
* x+ is the same direction as RIGHT on D-pad.
|
||||
* y+ is normal to the touch screen, pointing outward.
|
||||
* z+ is the same direction as UP on D-pad.
|
||||
*
|
||||
* For accelerometer state vector
|
||||
* Units: g (gravitational acceleration)
|
||||
*
|
||||
* For gyroscope state vector:
|
||||
* Orientation is determined by right-hand rule.
|
||||
* Units: deg/sec
|
||||
*
|
||||
* For rotation state vector
|
||||
* Units: rotations
|
||||
*
|
||||
* For orientation state matrix
|
||||
* x vector
|
||||
* y vector
|
||||
* z vector
|
||||
*/
|
||||
using MotionStatus = std::tuple<Common::Vec3<float>, Common::Vec3<float>, Common::Vec3<float>,
|
||||
std::array<Common::Vec3f, 3>>;
|
||||
|
||||
/**
|
||||
* A motion device is an input device that returns a motion status object
|
||||
*/
|
||||
using MotionDevice = InputDevice<MotionStatus>;
|
||||
|
||||
/**
|
||||
* A touch status is an object that returns a tuple of two floats and a bool. The floats are
|
||||
* x and y coordinates in the range 0.0 - 1.0, and the bool indicates whether it is pressed.
|
||||
*/
|
||||
using TouchStatus = std::tuple<float, float, bool>;
|
||||
|
||||
/**
|
||||
* A touch device is an input device that returns a touch status object
|
||||
*/
|
||||
using TouchDevice = InputDevice<TouchStatus>;
|
||||
|
||||
/**
|
||||
* A mouse device is an input device that returns a tuple of two floats and four ints.
|
||||
* The first two floats are X and Y device coordinates of the mouse (from 0-1).
|
||||
* The s32s are the mouse wheel.
|
||||
*/
|
||||
using MouseDevice = InputDevice<std::tuple<float, float, s32, s32>>;
|
||||
|
||||
} // namespace Input
|
45
src/core/frontend/input_interpreter.cpp
Executable file
45
src/core/frontend/input_interpreter.cpp
Executable file
@@ -0,0 +1,45 @@
|
||||
// Copyright 2020 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "core/core.h"
|
||||
#include "core/frontend/input_interpreter.h"
|
||||
#include "core/hle/service/hid/controllers/npad.h"
|
||||
#include "core/hle/service/hid/hid.h"
|
||||
#include "core/hle/service/sm/sm.h"
|
||||
|
||||
InputInterpreter::InputInterpreter(Core::System& system)
|
||||
: npad{system.ServiceManager()
|
||||
.GetService<Service::HID::Hid>("hid")
|
||||
->GetAppletResource()
|
||||
->GetController<Service::HID::Controller_NPad>(Service::HID::HidController::NPad)} {}
|
||||
|
||||
InputInterpreter::~InputInterpreter() = default;
|
||||
|
||||
void InputInterpreter::PollInput() {
|
||||
const u32 button_state = npad.GetAndResetPressState();
|
||||
|
||||
previous_index = current_index;
|
||||
current_index = (current_index + 1) % button_states.size();
|
||||
|
||||
button_states[current_index] = button_state;
|
||||
}
|
||||
|
||||
bool InputInterpreter::IsButtonPressedOnce(HIDButton button) const {
|
||||
const bool current_press =
|
||||
(button_states[current_index] & (1U << static_cast<u8>(button))) != 0;
|
||||
const bool previous_press =
|
||||
(button_states[previous_index] & (1U << static_cast<u8>(button))) != 0;
|
||||
|
||||
return current_press && !previous_press;
|
||||
}
|
||||
|
||||
bool InputInterpreter::IsButtonHeld(HIDButton button) const {
|
||||
u32 held_buttons{button_states[0]};
|
||||
|
||||
for (std::size_t i = 1; i < button_states.size(); ++i) {
|
||||
held_buttons &= button_states[i];
|
||||
}
|
||||
|
||||
return (held_buttons & (1U << static_cast<u8>(button))) != 0;
|
||||
}
|
120
src/core/frontend/input_interpreter.h
Executable file
120
src/core/frontend/input_interpreter.h
Executable file
@@ -0,0 +1,120 @@
|
||||
// Copyright 2020 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
namespace Service::HID {
|
||||
class Controller_NPad;
|
||||
}
|
||||
|
||||
enum class HIDButton : u8 {
|
||||
A,
|
||||
B,
|
||||
X,
|
||||
Y,
|
||||
LStick,
|
||||
RStick,
|
||||
L,
|
||||
R,
|
||||
ZL,
|
||||
ZR,
|
||||
Plus,
|
||||
Minus,
|
||||
|
||||
DLeft,
|
||||
DUp,
|
||||
DRight,
|
||||
DDown,
|
||||
|
||||
LStickLeft,
|
||||
LStickUp,
|
||||
LStickRight,
|
||||
LStickDown,
|
||||
|
||||
RStickLeft,
|
||||
RStickUp,
|
||||
RStickRight,
|
||||
RStickDown,
|
||||
|
||||
LeftSL,
|
||||
LeftSR,
|
||||
|
||||
RightSL,
|
||||
RightSR,
|
||||
};
|
||||
|
||||
/**
|
||||
* The InputInterpreter class interfaces with HID to retrieve button press states.
|
||||
* Input is intended to be polled every 50ms so that a button is considered to be
|
||||
* held down after 400ms has elapsed since the initial button press and subsequent
|
||||
* repeated presses occur every 50ms.
|
||||
*/
|
||||
class InputInterpreter {
|
||||
public:
|
||||
explicit InputInterpreter(Core::System& system);
|
||||
virtual ~InputInterpreter();
|
||||
|
||||
/// Gets a button state from HID and inserts it into the array of button states.
|
||||
void PollInput();
|
||||
|
||||
/**
|
||||
* The specified button is considered to be pressed once
|
||||
* if it is currently pressed and not pressed previously.
|
||||
*
|
||||
* @param button The button to check.
|
||||
*
|
||||
* @returns True when the button is pressed once.
|
||||
*/
|
||||
[[nodiscard]] bool IsButtonPressedOnce(HIDButton button) const;
|
||||
|
||||
/**
|
||||
* Checks whether any of the buttons in the parameter list is pressed once.
|
||||
*
|
||||
* @tparam HIDButton The buttons to check.
|
||||
*
|
||||
* @returns True when at least one of the buttons is pressed once.
|
||||
*/
|
||||
template <HIDButton... T>
|
||||
[[nodiscard]] bool IsAnyButtonPressedOnce() {
|
||||
return (IsButtonPressedOnce(T) || ...);
|
||||
}
|
||||
|
||||
/**
|
||||
* The specified button is considered to be held down if it is pressed in all 9 button states.
|
||||
*
|
||||
* @param button The button to check.
|
||||
*
|
||||
* @returns True when the button is held down.
|
||||
*/
|
||||
[[nodiscard]] bool IsButtonHeld(HIDButton button) const;
|
||||
|
||||
/**
|
||||
* Checks whether any of the buttons in the parameter list is held down.
|
||||
*
|
||||
* @tparam HIDButton The buttons to check.
|
||||
*
|
||||
* @returns True when at least one of the buttons is held down.
|
||||
*/
|
||||
template <HIDButton... T>
|
||||
[[nodiscard]] bool IsAnyButtonHeld() {
|
||||
return (IsButtonHeld(T) || ...);
|
||||
}
|
||||
|
||||
private:
|
||||
Service::HID::Controller_NPad& npad;
|
||||
|
||||
/// Stores 9 consecutive button states polled from HID.
|
||||
std::array<u32, 9> button_states{};
|
||||
|
||||
std::size_t previous_index{};
|
||||
std::size_t current_index{};
|
||||
};
|
Reference in New Issue
Block a user