remove old files
This commit is contained in:
@@ -1,695 +0,0 @@
|
||||
// Copyright 2020 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <algorithm>
|
||||
#include <thread>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/string_util.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hle/lock.h"
|
||||
#include "core/hle/service/hid/controllers/npad.h"
|
||||
#include "core/hle/service/hid/hid.h"
|
||||
#include "core/hle/service/sm/sm.h"
|
||||
#include "ui_controller.h"
|
||||
#include "yuzu/applets/controller.h"
|
||||
#include "yuzu/configuration/configure_input.h"
|
||||
#include "yuzu/configuration/configure_input_profile_dialog.h"
|
||||
#include "yuzu/configuration/configure_motion_touch.h"
|
||||
#include "yuzu/configuration/configure_vibration.h"
|
||||
#include "yuzu/configuration/input_profiles.h"
|
||||
#include "yuzu/main.h"
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr std::size_t HANDHELD_INDEX = 8;
|
||||
|
||||
constexpr std::array<std::array<bool, 4>, 8> led_patterns{{
|
||||
{true, false, false, false},
|
||||
{true, true, false, false},
|
||||
{true, true, true, false},
|
||||
{true, true, true, true},
|
||||
{true, false, false, true},
|
||||
{true, false, true, false},
|
||||
{true, false, true, true},
|
||||
{false, true, true, false},
|
||||
}};
|
||||
|
||||
void UpdateController(Settings::ControllerType controller_type, std::size_t npad_index,
|
||||
bool connected) {
|
||||
Core::System& system{Core::System::GetInstance()};
|
||||
|
||||
if (!system.IsPoweredOn()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Service::SM::ServiceManager& sm = system.ServiceManager();
|
||||
|
||||
auto& npad =
|
||||
sm.GetService<Service::HID::Hid>("hid")
|
||||
->GetAppletResource()
|
||||
->GetController<Service::HID::Controller_NPad>(Service::HID::HidController::NPad);
|
||||
|
||||
npad.UpdateControllerAt(npad.MapSettingsTypeToNPad(controller_type), npad_index, connected);
|
||||
}
|
||||
|
||||
// Returns true if the given controller type is compatible with the given parameters.
|
||||
bool IsControllerCompatible(Settings::ControllerType controller_type,
|
||||
Core::Frontend::ControllerParameters parameters) {
|
||||
switch (controller_type) {
|
||||
case Settings::ControllerType::ProController:
|
||||
return parameters.allow_pro_controller;
|
||||
case Settings::ControllerType::DualJoyconDetached:
|
||||
return parameters.allow_dual_joycons;
|
||||
case Settings::ControllerType::LeftJoycon:
|
||||
return parameters.allow_left_joycon;
|
||||
case Settings::ControllerType::RightJoycon:
|
||||
return parameters.allow_right_joycon;
|
||||
case Settings::ControllerType::Handheld:
|
||||
return parameters.enable_single_mode && parameters.allow_handheld;
|
||||
case Settings::ControllerType::GameCube:
|
||||
return parameters.allow_gamecube_controller;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
QtControllerSelectorDialog::QtControllerSelectorDialog(
|
||||
QWidget* parent, Core::Frontend::ControllerParameters parameters_,
|
||||
InputCommon::InputSubsystem* input_subsystem_)
|
||||
: QDialog(parent), ui(std::make_unique<Ui::QtControllerSelectorDialog>()),
|
||||
parameters(std::move(parameters_)), input_subsystem{input_subsystem_},
|
||||
input_profiles(std::make_unique<InputProfiles>()) {
|
||||
ui->setupUi(this);
|
||||
|
||||
player_widgets = {
|
||||
ui->widgetPlayer1, ui->widgetPlayer2, ui->widgetPlayer3, ui->widgetPlayer4,
|
||||
ui->widgetPlayer5, ui->widgetPlayer6, ui->widgetPlayer7, ui->widgetPlayer8,
|
||||
};
|
||||
|
||||
player_groupboxes = {
|
||||
ui->groupPlayer1Connected, ui->groupPlayer2Connected, ui->groupPlayer3Connected,
|
||||
ui->groupPlayer4Connected, ui->groupPlayer5Connected, ui->groupPlayer6Connected,
|
||||
ui->groupPlayer7Connected, ui->groupPlayer8Connected,
|
||||
};
|
||||
|
||||
connected_controller_icons = {
|
||||
ui->controllerPlayer1, ui->controllerPlayer2, ui->controllerPlayer3, ui->controllerPlayer4,
|
||||
ui->controllerPlayer5, ui->controllerPlayer6, ui->controllerPlayer7, ui->controllerPlayer8,
|
||||
};
|
||||
|
||||
led_patterns_boxes = {{
|
||||
{ui->checkboxPlayer1LED1, ui->checkboxPlayer1LED2, ui->checkboxPlayer1LED3,
|
||||
ui->checkboxPlayer1LED4},
|
||||
{ui->checkboxPlayer2LED1, ui->checkboxPlayer2LED2, ui->checkboxPlayer2LED3,
|
||||
ui->checkboxPlayer2LED4},
|
||||
{ui->checkboxPlayer3LED1, ui->checkboxPlayer3LED2, ui->checkboxPlayer3LED3,
|
||||
ui->checkboxPlayer3LED4},
|
||||
{ui->checkboxPlayer4LED1, ui->checkboxPlayer4LED2, ui->checkboxPlayer4LED3,
|
||||
ui->checkboxPlayer4LED4},
|
||||
{ui->checkboxPlayer5LED1, ui->checkboxPlayer5LED2, ui->checkboxPlayer5LED3,
|
||||
ui->checkboxPlayer5LED4},
|
||||
{ui->checkboxPlayer6LED1, ui->checkboxPlayer6LED2, ui->checkboxPlayer6LED3,
|
||||
ui->checkboxPlayer6LED4},
|
||||
{ui->checkboxPlayer7LED1, ui->checkboxPlayer7LED2, ui->checkboxPlayer7LED3,
|
||||
ui->checkboxPlayer7LED4},
|
||||
{ui->checkboxPlayer8LED1, ui->checkboxPlayer8LED2, ui->checkboxPlayer8LED3,
|
||||
ui->checkboxPlayer8LED4},
|
||||
}};
|
||||
|
||||
explain_text_labels = {
|
||||
ui->labelPlayer1Explain, ui->labelPlayer2Explain, ui->labelPlayer3Explain,
|
||||
ui->labelPlayer4Explain, ui->labelPlayer5Explain, ui->labelPlayer6Explain,
|
||||
ui->labelPlayer7Explain, ui->labelPlayer8Explain,
|
||||
};
|
||||
|
||||
emulated_controllers = {
|
||||
ui->comboPlayer1Emulated, ui->comboPlayer2Emulated, ui->comboPlayer3Emulated,
|
||||
ui->comboPlayer4Emulated, ui->comboPlayer5Emulated, ui->comboPlayer6Emulated,
|
||||
ui->comboPlayer7Emulated, ui->comboPlayer8Emulated,
|
||||
};
|
||||
|
||||
player_labels = {
|
||||
ui->labelPlayer1, ui->labelPlayer2, ui->labelPlayer3, ui->labelPlayer4,
|
||||
ui->labelPlayer5, ui->labelPlayer6, ui->labelPlayer7, ui->labelPlayer8,
|
||||
};
|
||||
|
||||
connected_controller_labels = {
|
||||
ui->labelConnectedPlayer1, ui->labelConnectedPlayer2, ui->labelConnectedPlayer3,
|
||||
ui->labelConnectedPlayer4, ui->labelConnectedPlayer5, ui->labelConnectedPlayer6,
|
||||
ui->labelConnectedPlayer7, ui->labelConnectedPlayer8,
|
||||
};
|
||||
|
||||
connected_controller_checkboxes = {
|
||||
ui->checkboxPlayer1Connected, ui->checkboxPlayer2Connected, ui->checkboxPlayer3Connected,
|
||||
ui->checkboxPlayer4Connected, ui->checkboxPlayer5Connected, ui->checkboxPlayer6Connected,
|
||||
ui->checkboxPlayer7Connected, ui->checkboxPlayer8Connected,
|
||||
};
|
||||
|
||||
// Setup/load everything prior to setting up connections.
|
||||
// This avoids unintentionally changing the states of elements while loading them in.
|
||||
SetSupportedControllers();
|
||||
DisableUnsupportedPlayers();
|
||||
|
||||
for (std::size_t player_index = 0; player_index < NUM_PLAYERS; ++player_index) {
|
||||
SetEmulatedControllers(player_index);
|
||||
}
|
||||
|
||||
LoadConfiguration();
|
||||
|
||||
for (std::size_t i = 0; i < NUM_PLAYERS; ++i) {
|
||||
SetExplainText(i);
|
||||
UpdateControllerIcon(i);
|
||||
UpdateLEDPattern(i);
|
||||
UpdateBorderColor(i);
|
||||
|
||||
connect(player_groupboxes[i], &QGroupBox::toggled, [this, i](bool checked) {
|
||||
if (checked) {
|
||||
for (std::size_t index = 0; index <= i; ++index) {
|
||||
connected_controller_checkboxes[index]->setChecked(checked);
|
||||
}
|
||||
} else {
|
||||
for (std::size_t index = i; index < NUM_PLAYERS; ++index) {
|
||||
connected_controller_checkboxes[index]->setChecked(checked);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
connect(emulated_controllers[i], qOverload<int>(&QComboBox::currentIndexChanged),
|
||||
[this, i](int) {
|
||||
UpdateControllerIcon(i);
|
||||
UpdateControllerState(i);
|
||||
UpdateLEDPattern(i);
|
||||
CheckIfParametersMet();
|
||||
});
|
||||
|
||||
connect(connected_controller_checkboxes[i], &QCheckBox::stateChanged, [this, i](int state) {
|
||||
player_groupboxes[i]->setChecked(state == Qt::Checked);
|
||||
UpdateControllerIcon(i);
|
||||
UpdateControllerState(i);
|
||||
UpdateLEDPattern(i);
|
||||
UpdateBorderColor(i);
|
||||
CheckIfParametersMet();
|
||||
});
|
||||
|
||||
if (i == 0) {
|
||||
connect(emulated_controllers[i], qOverload<int>(&QComboBox::currentIndexChanged),
|
||||
[this, i](int index) {
|
||||
UpdateDockedState(GetControllerTypeFromIndex(index, i) ==
|
||||
Settings::ControllerType::Handheld);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
connect(ui->vibrationButton, &QPushButton::clicked, this,
|
||||
&QtControllerSelectorDialog::CallConfigureVibrationDialog);
|
||||
|
||||
connect(ui->motionButton, &QPushButton::clicked, this,
|
||||
&QtControllerSelectorDialog::CallConfigureMotionTouchDialog);
|
||||
|
||||
connect(ui->inputConfigButton, &QPushButton::clicked, this,
|
||||
&QtControllerSelectorDialog::CallConfigureInputProfileDialog);
|
||||
|
||||
connect(ui->buttonBox, &QDialogButtonBox::accepted, this,
|
||||
&QtControllerSelectorDialog::ApplyConfiguration);
|
||||
|
||||
// Enhancement: Check if the parameters have already been met before disconnecting controllers.
|
||||
// If all the parameters are met AND only allows a single player,
|
||||
// stop the constructor here as we do not need to continue.
|
||||
if (CheckIfParametersMet() && parameters.enable_single_mode) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If keep_controllers_connected is false, forcefully disconnect all controllers
|
||||
if (!parameters.keep_controllers_connected) {
|
||||
for (auto player : player_groupboxes) {
|
||||
player->setChecked(false);
|
||||
}
|
||||
}
|
||||
|
||||
resize(0, 0);
|
||||
}
|
||||
|
||||
QtControllerSelectorDialog::~QtControllerSelectorDialog() = default;
|
||||
|
||||
int QtControllerSelectorDialog::exec() {
|
||||
if (parameters_met && parameters.enable_single_mode) {
|
||||
return QDialog::Accepted;
|
||||
}
|
||||
return QDialog::exec();
|
||||
}
|
||||
|
||||
void QtControllerSelectorDialog::ApplyConfiguration() {
|
||||
const bool pre_docked_mode = Settings::values.use_docked_mode.GetValue();
|
||||
Settings::values.use_docked_mode.SetValue(ui->radioDocked->isChecked());
|
||||
OnDockedModeChanged(pre_docked_mode, Settings::values.use_docked_mode.GetValue());
|
||||
|
||||
Settings::values.vibration_enabled.SetValue(ui->vibrationGroup->isChecked());
|
||||
Settings::values.motion_enabled.SetValue(ui->motionGroup->isChecked());
|
||||
}
|
||||
|
||||
void QtControllerSelectorDialog::LoadConfiguration() {
|
||||
for (std::size_t index = 0; index < NUM_PLAYERS; ++index) {
|
||||
const auto connected =
|
||||
Settings::values.players.GetValue()[index].connected ||
|
||||
(index == 0 && Settings::values.players.GetValue()[HANDHELD_INDEX].connected);
|
||||
player_groupboxes[index]->setChecked(connected);
|
||||
connected_controller_checkboxes[index]->setChecked(connected);
|
||||
emulated_controllers[index]->setCurrentIndex(GetIndexFromControllerType(
|
||||
Settings::values.players.GetValue()[index].controller_type, index));
|
||||
}
|
||||
|
||||
UpdateDockedState(Settings::values.players.GetValue()[HANDHELD_INDEX].connected);
|
||||
|
||||
ui->vibrationGroup->setChecked(Settings::values.vibration_enabled.GetValue());
|
||||
ui->motionGroup->setChecked(Settings::values.motion_enabled.GetValue());
|
||||
}
|
||||
|
||||
void QtControllerSelectorDialog::CallConfigureVibrationDialog() {
|
||||
ConfigureVibration dialog(this);
|
||||
|
||||
dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint |
|
||||
Qt::WindowSystemMenuHint);
|
||||
dialog.setWindowModality(Qt::WindowModal);
|
||||
|
||||
if (dialog.exec() == QDialog::Accepted) {
|
||||
dialog.ApplyConfiguration();
|
||||
}
|
||||
}
|
||||
|
||||
void QtControllerSelectorDialog::CallConfigureMotionTouchDialog() {
|
||||
ConfigureMotionTouch dialog(this, input_subsystem);
|
||||
|
||||
dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint |
|
||||
Qt::WindowSystemMenuHint);
|
||||
dialog.setWindowModality(Qt::WindowModal);
|
||||
|
||||
if (dialog.exec() == QDialog::Accepted) {
|
||||
dialog.ApplyConfiguration();
|
||||
}
|
||||
}
|
||||
|
||||
void QtControllerSelectorDialog::CallConfigureInputProfileDialog() {
|
||||
ConfigureInputProfileDialog dialog(this, input_subsystem, input_profiles.get());
|
||||
|
||||
dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint |
|
||||
Qt::WindowSystemMenuHint);
|
||||
dialog.setWindowModality(Qt::WindowModal);
|
||||
dialog.exec();
|
||||
}
|
||||
|
||||
bool QtControllerSelectorDialog::CheckIfParametersMet() {
|
||||
// Here, we check and validate the current configuration against all applicable parameters.
|
||||
const auto num_connected_players = static_cast<int>(
|
||||
std::count_if(player_groupboxes.begin(), player_groupboxes.end(),
|
||||
[this](const QGroupBox* player) { return player->isChecked(); }));
|
||||
|
||||
const auto min_supported_players = parameters.enable_single_mode ? 1 : parameters.min_players;
|
||||
const auto max_supported_players = parameters.enable_single_mode ? 1 : parameters.max_players;
|
||||
|
||||
// First, check against the number of connected players.
|
||||
if (num_connected_players < min_supported_players ||
|
||||
num_connected_players > max_supported_players) {
|
||||
parameters_met = false;
|
||||
ui->buttonBox->setEnabled(parameters_met);
|
||||
return parameters_met;
|
||||
}
|
||||
|
||||
// Next, check against all connected controllers.
|
||||
const auto all_controllers_compatible = [this] {
|
||||
for (std::size_t index = 0; index < NUM_PLAYERS; ++index) {
|
||||
// Skip controllers that are not used, we only care about the currently connected ones.
|
||||
if (!player_groupboxes[index]->isChecked() || !player_groupboxes[index]->isEnabled()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto compatible = IsControllerCompatible(
|
||||
GetControllerTypeFromIndex(emulated_controllers[index]->currentIndex(), index),
|
||||
parameters);
|
||||
|
||||
// If any controller is found to be incompatible, return false early.
|
||||
if (!compatible) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Reaching here means all currently connected controllers are compatible.
|
||||
return true;
|
||||
}();
|
||||
|
||||
parameters_met = all_controllers_compatible;
|
||||
ui->buttonBox->setEnabled(parameters_met);
|
||||
return parameters_met;
|
||||
}
|
||||
|
||||
void QtControllerSelectorDialog::SetSupportedControllers() {
|
||||
const QString theme = [] {
|
||||
if (QIcon::themeName().contains(QStringLiteral("dark"))) {
|
||||
return QStringLiteral("_dark");
|
||||
} else if (QIcon::themeName().contains(QStringLiteral("midnight"))) {
|
||||
return QStringLiteral("_midnight");
|
||||
} else {
|
||||
return QString{};
|
||||
}
|
||||
}();
|
||||
|
||||
if (parameters.enable_single_mode && parameters.allow_handheld) {
|
||||
ui->controllerSupported1->setStyleSheet(
|
||||
QStringLiteral("image: url(:/controller/applet_handheld%0); ").arg(theme));
|
||||
} else {
|
||||
ui->controllerSupported1->setStyleSheet(
|
||||
QStringLiteral("image: url(:/controller/applet_handheld%0_disabled); ").arg(theme));
|
||||
}
|
||||
|
||||
if (parameters.allow_dual_joycons) {
|
||||
ui->controllerSupported2->setStyleSheet(
|
||||
QStringLiteral("image: url(:/controller/applet_dual_joycon%0); ").arg(theme));
|
||||
} else {
|
||||
ui->controllerSupported2->setStyleSheet(
|
||||
QStringLiteral("image: url(:/controller/applet_dual_joycon%0_disabled); ").arg(theme));
|
||||
}
|
||||
|
||||
if (parameters.allow_left_joycon) {
|
||||
ui->controllerSupported3->setStyleSheet(
|
||||
QStringLiteral("image: url(:/controller/applet_joycon_left%0); ").arg(theme));
|
||||
} else {
|
||||
ui->controllerSupported3->setStyleSheet(
|
||||
QStringLiteral("image: url(:/controller/applet_joycon_left%0_disabled); ").arg(theme));
|
||||
}
|
||||
|
||||
if (parameters.allow_right_joycon) {
|
||||
ui->controllerSupported4->setStyleSheet(
|
||||
QStringLiteral("image: url(:/controller/applet_joycon_right%0); ").arg(theme));
|
||||
} else {
|
||||
ui->controllerSupported4->setStyleSheet(
|
||||
QStringLiteral("image: url(:/controller/applet_joycon_right%0_disabled); ").arg(theme));
|
||||
}
|
||||
|
||||
if (parameters.allow_pro_controller || parameters.allow_gamecube_controller) {
|
||||
ui->controllerSupported5->setStyleSheet(
|
||||
QStringLiteral("image: url(:/controller/applet_pro_controller%0); ").arg(theme));
|
||||
} else {
|
||||
ui->controllerSupported5->setStyleSheet(
|
||||
QStringLiteral("image: url(:/controller/applet_pro_controller%0_disabled); ")
|
||||
.arg(theme));
|
||||
}
|
||||
|
||||
// enable_single_mode overrides min_players and max_players.
|
||||
if (parameters.enable_single_mode) {
|
||||
ui->numberSupportedLabel->setText(QStringLiteral("1"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (parameters.min_players == parameters.max_players) {
|
||||
ui->numberSupportedLabel->setText(QStringLiteral("%1").arg(parameters.max_players));
|
||||
} else {
|
||||
ui->numberSupportedLabel->setText(
|
||||
QStringLiteral("%1 - %2").arg(parameters.min_players).arg(parameters.max_players));
|
||||
}
|
||||
}
|
||||
|
||||
void QtControllerSelectorDialog::SetEmulatedControllers(std::size_t player_index) {
|
||||
auto& pairs = index_controller_type_pairs[player_index];
|
||||
|
||||
pairs.clear();
|
||||
emulated_controllers[player_index]->clear();
|
||||
|
||||
pairs.emplace_back(emulated_controllers[player_index]->count(),
|
||||
Settings::ControllerType::ProController);
|
||||
emulated_controllers[player_index]->addItem(tr("Pro Controller"));
|
||||
|
||||
pairs.emplace_back(emulated_controllers[player_index]->count(),
|
||||
Settings::ControllerType::DualJoyconDetached);
|
||||
emulated_controllers[player_index]->addItem(tr("Dual Joycons"));
|
||||
|
||||
pairs.emplace_back(emulated_controllers[player_index]->count(),
|
||||
Settings::ControllerType::LeftJoycon);
|
||||
emulated_controllers[player_index]->addItem(tr("Left Joycon"));
|
||||
|
||||
pairs.emplace_back(emulated_controllers[player_index]->count(),
|
||||
Settings::ControllerType::RightJoycon);
|
||||
emulated_controllers[player_index]->addItem(tr("Right Joycon"));
|
||||
|
||||
if (player_index == 0) {
|
||||
pairs.emplace_back(emulated_controllers[player_index]->count(),
|
||||
Settings::ControllerType::Handheld);
|
||||
emulated_controllers[player_index]->addItem(tr("Handheld"));
|
||||
}
|
||||
|
||||
pairs.emplace_back(emulated_controllers[player_index]->count(),
|
||||
Settings::ControllerType::GameCube);
|
||||
emulated_controllers[player_index]->addItem(tr("GameCube Controller"));
|
||||
}
|
||||
|
||||
Settings::ControllerType QtControllerSelectorDialog::GetControllerTypeFromIndex(
|
||||
int index, std::size_t player_index) const {
|
||||
const auto& pairs = index_controller_type_pairs[player_index];
|
||||
|
||||
const auto it = std::find_if(pairs.begin(), pairs.end(),
|
||||
[index](const auto& pair) { return pair.first == index; });
|
||||
|
||||
if (it == pairs.end()) {
|
||||
return Settings::ControllerType::ProController;
|
||||
}
|
||||
|
||||
return it->second;
|
||||
}
|
||||
|
||||
int QtControllerSelectorDialog::GetIndexFromControllerType(Settings::ControllerType type,
|
||||
std::size_t player_index) const {
|
||||
const auto& pairs = index_controller_type_pairs[player_index];
|
||||
|
||||
const auto it = std::find_if(pairs.begin(), pairs.end(),
|
||||
[type](const auto& pair) { return pair.second == type; });
|
||||
|
||||
if (it == pairs.end()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return it->first;
|
||||
}
|
||||
|
||||
void QtControllerSelectorDialog::UpdateControllerIcon(std::size_t player_index) {
|
||||
if (!player_groupboxes[player_index]->isChecked()) {
|
||||
connected_controller_icons[player_index]->setStyleSheet(QString{});
|
||||
player_labels[player_index]->show();
|
||||
return;
|
||||
}
|
||||
|
||||
const QString stylesheet = [this, player_index] {
|
||||
switch (GetControllerTypeFromIndex(emulated_controllers[player_index]->currentIndex(),
|
||||
player_index)) {
|
||||
case Settings::ControllerType::ProController:
|
||||
case Settings::ControllerType::GameCube:
|
||||
return QStringLiteral("image: url(:/controller/applet_pro_controller%0); ");
|
||||
case Settings::ControllerType::DualJoyconDetached:
|
||||
return QStringLiteral("image: url(:/controller/applet_dual_joycon%0); ");
|
||||
case Settings::ControllerType::LeftJoycon:
|
||||
return QStringLiteral("image: url(:/controller/applet_joycon_left%0); ");
|
||||
case Settings::ControllerType::RightJoycon:
|
||||
return QStringLiteral("image: url(:/controller/applet_joycon_right%0); ");
|
||||
case Settings::ControllerType::Handheld:
|
||||
return QStringLiteral("image: url(:/controller/applet_handheld%0); ");
|
||||
default:
|
||||
return QString{};
|
||||
}
|
||||
}();
|
||||
|
||||
if (stylesheet.isEmpty()) {
|
||||
connected_controller_icons[player_index]->setStyleSheet(QString{});
|
||||
player_labels[player_index]->show();
|
||||
return;
|
||||
}
|
||||
|
||||
const QString theme = [] {
|
||||
if (QIcon::themeName().contains(QStringLiteral("dark"))) {
|
||||
return QStringLiteral("_dark");
|
||||
} else if (QIcon::themeName().contains(QStringLiteral("midnight"))) {
|
||||
return QStringLiteral("_midnight");
|
||||
} else {
|
||||
return QString{};
|
||||
}
|
||||
}();
|
||||
|
||||
connected_controller_icons[player_index]->setStyleSheet(stylesheet.arg(theme));
|
||||
player_labels[player_index]->hide();
|
||||
}
|
||||
|
||||
void QtControllerSelectorDialog::UpdateControllerState(std::size_t player_index) {
|
||||
auto& player = Settings::values.players.GetValue()[player_index];
|
||||
|
||||
const auto controller_type = GetControllerTypeFromIndex(
|
||||
emulated_controllers[player_index]->currentIndex(), player_index);
|
||||
const auto player_connected = player_groupboxes[player_index]->isChecked() &&
|
||||
controller_type != Settings::ControllerType::Handheld;
|
||||
|
||||
if (player.controller_type == controller_type && player.connected == player_connected) {
|
||||
// Set vibration devices in the event that the input device has changed.
|
||||
ConfigureVibration::SetVibrationDevices(player_index);
|
||||
return;
|
||||
}
|
||||
|
||||
// Disconnect the controller first.
|
||||
UpdateController(controller_type, player_index, false);
|
||||
|
||||
player.controller_type = controller_type;
|
||||
player.connected = player_connected;
|
||||
|
||||
ConfigureVibration::SetVibrationDevices(player_index);
|
||||
|
||||
// Handheld
|
||||
if (player_index == 0) {
|
||||
auto& handheld = Settings::values.players.GetValue()[HANDHELD_INDEX];
|
||||
if (controller_type == Settings::ControllerType::Handheld) {
|
||||
handheld = player;
|
||||
}
|
||||
handheld.connected = player_groupboxes[player_index]->isChecked() &&
|
||||
controller_type == Settings::ControllerType::Handheld;
|
||||
UpdateController(Settings::ControllerType::Handheld, 8, handheld.connected);
|
||||
}
|
||||
|
||||
if (!player.connected) {
|
||||
return;
|
||||
}
|
||||
|
||||
// This emulates a delay between disconnecting and reconnecting controllers as some games
|
||||
// do not respond to a change in controller type if it was instantaneous.
|
||||
using namespace std::chrono_literals;
|
||||
std::this_thread::sleep_for(60ms);
|
||||
|
||||
UpdateController(controller_type, player_index, player_connected);
|
||||
}
|
||||
|
||||
void QtControllerSelectorDialog::UpdateLEDPattern(std::size_t player_index) {
|
||||
if (!player_groupboxes[player_index]->isChecked() ||
|
||||
GetControllerTypeFromIndex(emulated_controllers[player_index]->currentIndex(),
|
||||
player_index) == Settings::ControllerType::Handheld) {
|
||||
led_patterns_boxes[player_index][0]->setChecked(false);
|
||||
led_patterns_boxes[player_index][1]->setChecked(false);
|
||||
led_patterns_boxes[player_index][2]->setChecked(false);
|
||||
led_patterns_boxes[player_index][3]->setChecked(false);
|
||||
return;
|
||||
}
|
||||
|
||||
led_patterns_boxes[player_index][0]->setChecked(led_patterns[player_index][0]);
|
||||
led_patterns_boxes[player_index][1]->setChecked(led_patterns[player_index][1]);
|
||||
led_patterns_boxes[player_index][2]->setChecked(led_patterns[player_index][2]);
|
||||
led_patterns_boxes[player_index][3]->setChecked(led_patterns[player_index][3]);
|
||||
}
|
||||
|
||||
void QtControllerSelectorDialog::UpdateBorderColor(std::size_t player_index) {
|
||||
if (!parameters.enable_border_color ||
|
||||
player_index >= static_cast<std::size_t>(parameters.max_players) ||
|
||||
player_groupboxes[player_index]->styleSheet().contains(QStringLiteral("QGroupBox"))) {
|
||||
return;
|
||||
}
|
||||
|
||||
player_groupboxes[player_index]->setStyleSheet(
|
||||
player_groupboxes[player_index]->styleSheet().append(
|
||||
QStringLiteral("QGroupBox#groupPlayer%1Connected:checked "
|
||||
"{ border: 1px solid rgba(%2, %3, %4, %5); }")
|
||||
.arg(player_index + 1)
|
||||
.arg(parameters.border_colors[player_index][0])
|
||||
.arg(parameters.border_colors[player_index][1])
|
||||
.arg(parameters.border_colors[player_index][2])
|
||||
.arg(parameters.border_colors[player_index][3])));
|
||||
}
|
||||
|
||||
void QtControllerSelectorDialog::SetExplainText(std::size_t player_index) {
|
||||
if (!parameters.enable_explain_text ||
|
||||
player_index >= static_cast<std::size_t>(parameters.max_players)) {
|
||||
return;
|
||||
}
|
||||
|
||||
explain_text_labels[player_index]->setText(QString::fromStdString(
|
||||
Common::StringFromFixedZeroTerminatedBuffer(parameters.explain_text[player_index].data(),
|
||||
parameters.explain_text[player_index].size())));
|
||||
}
|
||||
|
||||
void QtControllerSelectorDialog::UpdateDockedState(bool is_handheld) {
|
||||
// Disallow changing the console mode if the controller type is handheld.
|
||||
ui->radioDocked->setEnabled(!is_handheld);
|
||||
ui->radioUndocked->setEnabled(!is_handheld);
|
||||
|
||||
ui->radioDocked->setChecked(Settings::values.use_docked_mode.GetValue());
|
||||
ui->radioUndocked->setChecked(!Settings::values.use_docked_mode.GetValue());
|
||||
|
||||
// Also force into undocked mode if the controller type is handheld.
|
||||
if (is_handheld) {
|
||||
ui->radioUndocked->setChecked(true);
|
||||
}
|
||||
}
|
||||
|
||||
void QtControllerSelectorDialog::DisableUnsupportedPlayers() {
|
||||
const auto max_supported_players = parameters.enable_single_mode ? 1 : parameters.max_players;
|
||||
|
||||
switch (max_supported_players) {
|
||||
case 0:
|
||||
default:
|
||||
UNREACHABLE();
|
||||
return;
|
||||
case 1:
|
||||
ui->widgetSpacer->hide();
|
||||
ui->widgetSpacer2->hide();
|
||||
ui->widgetSpacer3->hide();
|
||||
ui->widgetSpacer4->hide();
|
||||
break;
|
||||
case 2:
|
||||
ui->widgetSpacer->hide();
|
||||
ui->widgetSpacer2->hide();
|
||||
ui->widgetSpacer3->hide();
|
||||
break;
|
||||
case 3:
|
||||
ui->widgetSpacer->hide();
|
||||
ui->widgetSpacer2->hide();
|
||||
break;
|
||||
case 4:
|
||||
ui->widgetSpacer->hide();
|
||||
break;
|
||||
case 5:
|
||||
case 6:
|
||||
case 7:
|
||||
case 8:
|
||||
break;
|
||||
}
|
||||
|
||||
for (std::size_t index = max_supported_players; index < NUM_PLAYERS; ++index) {
|
||||
// Disconnect any unsupported players here and disable or hide them if applicable.
|
||||
Settings::values.players.GetValue()[index].connected = false;
|
||||
UpdateController(Settings::values.players.GetValue()[index].controller_type, index, false);
|
||||
// Hide the player widgets when max_supported_controllers is less than or equal to 4.
|
||||
if (max_supported_players <= 4) {
|
||||
player_widgets[index]->hide();
|
||||
}
|
||||
|
||||
// Disable and hide the following to prevent these from interaction.
|
||||
player_widgets[index]->setDisabled(true);
|
||||
connected_controller_checkboxes[index]->setDisabled(true);
|
||||
connected_controller_labels[index]->hide();
|
||||
connected_controller_checkboxes[index]->hide();
|
||||
}
|
||||
}
|
||||
|
||||
QtControllerSelector::QtControllerSelector(GMainWindow& parent) {
|
||||
connect(this, &QtControllerSelector::MainWindowReconfigureControllers, &parent,
|
||||
&GMainWindow::ControllerSelectorReconfigureControllers, Qt::QueuedConnection);
|
||||
connect(&parent, &GMainWindow::ControllerSelectorReconfigureFinished, this,
|
||||
&QtControllerSelector::MainWindowReconfigureFinished, Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
QtControllerSelector::~QtControllerSelector() = default;
|
||||
|
||||
void QtControllerSelector::ReconfigureControllers(
|
||||
std::function<void()> callback_, const Core::Frontend::ControllerParameters& parameters) const {
|
||||
callback = std::move(callback_);
|
||||
emit MainWindowReconfigureControllers(parameters);
|
||||
}
|
||||
|
||||
void QtControllerSelector::MainWindowReconfigureFinished() {
|
||||
// Acquire the HLE mutex
|
||||
std::lock_guard lock(HLE::g_hle_lock);
|
||||
callback();
|
||||
}
|
||||
@@ -1,164 +0,0 @@
|
||||
// Copyright 2020 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <memory>
|
||||
#include <QDialog>
|
||||
#include "core/frontend/applets/controller.h"
|
||||
|
||||
class GMainWindow;
|
||||
class QCheckBox;
|
||||
class QComboBox;
|
||||
class QDialogButtonBox;
|
||||
class QGroupBox;
|
||||
class QLabel;
|
||||
|
||||
class InputProfiles;
|
||||
|
||||
namespace InputCommon {
|
||||
class InputSubsystem;
|
||||
}
|
||||
|
||||
namespace Settings {
|
||||
enum class ControllerType;
|
||||
}
|
||||
|
||||
namespace Ui {
|
||||
class QtControllerSelectorDialog;
|
||||
}
|
||||
|
||||
class QtControllerSelectorDialog final : public QDialog {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit QtControllerSelectorDialog(QWidget* parent,
|
||||
Core::Frontend::ControllerParameters parameters_,
|
||||
InputCommon::InputSubsystem* input_subsystem_);
|
||||
~QtControllerSelectorDialog() override;
|
||||
|
||||
int exec() override;
|
||||
|
||||
private:
|
||||
// Applies the current configuration.
|
||||
void ApplyConfiguration();
|
||||
|
||||
// Loads the current input configuration into the frontend applet.
|
||||
void LoadConfiguration();
|
||||
|
||||
// Initializes the "Configure Vibration" Dialog.
|
||||
void CallConfigureVibrationDialog();
|
||||
|
||||
// Initializes the "Configure Motion / Touch" Dialog.
|
||||
void CallConfigureMotionTouchDialog();
|
||||
|
||||
// Initializes the "Create Input Profile" Dialog.
|
||||
void CallConfigureInputProfileDialog();
|
||||
|
||||
// Checks the current configuration against the given parameters.
|
||||
// This sets and returns the value of parameters_met.
|
||||
bool CheckIfParametersMet();
|
||||
|
||||
// Sets the controller icons for "Supported Controller Types".
|
||||
void SetSupportedControllers();
|
||||
|
||||
// Sets the emulated controllers per player.
|
||||
void SetEmulatedControllers(std::size_t player_index);
|
||||
|
||||
// Gets the Controller Type for a given controller combobox index per player.
|
||||
Settings::ControllerType GetControllerTypeFromIndex(int index, std::size_t player_index) const;
|
||||
|
||||
// Gets the controller combobox index for a given Controller Type per player.
|
||||
int GetIndexFromControllerType(Settings::ControllerType type, std::size_t player_index) const;
|
||||
|
||||
// Updates the controller icons per player.
|
||||
void UpdateControllerIcon(std::size_t player_index);
|
||||
|
||||
// Updates the controller state (type and connection status) per player.
|
||||
void UpdateControllerState(std::size_t player_index);
|
||||
|
||||
// Updates the LED pattern per player.
|
||||
void UpdateLEDPattern(std::size_t player_index);
|
||||
|
||||
// Updates the border color per player.
|
||||
void UpdateBorderColor(std::size_t player_index);
|
||||
|
||||
// Sets the "Explain Text" per player.
|
||||
void SetExplainText(std::size_t player_index);
|
||||
|
||||
// Updates the console mode.
|
||||
void UpdateDockedState(bool is_handheld);
|
||||
|
||||
// Disables and disconnects unsupported players based on the given parameters.
|
||||
void DisableUnsupportedPlayers();
|
||||
|
||||
std::unique_ptr<Ui::QtControllerSelectorDialog> ui;
|
||||
|
||||
// Parameters sent in from the backend HLE applet.
|
||||
Core::Frontend::ControllerParameters parameters;
|
||||
|
||||
InputCommon::InputSubsystem* input_subsystem;
|
||||
|
||||
std::unique_ptr<InputProfiles> input_profiles;
|
||||
|
||||
// This is true if and only if all parameters are met. Otherwise, this is false.
|
||||
// This determines whether the "OK" button can be clicked to exit the applet.
|
||||
bool parameters_met{false};
|
||||
|
||||
static constexpr std::size_t NUM_PLAYERS = 8;
|
||||
|
||||
// Widgets encapsulating the groupboxes and comboboxes per player.
|
||||
std::array<QWidget*, NUM_PLAYERS> player_widgets;
|
||||
|
||||
// Groupboxes encapsulating the controller icons and LED patterns per player.
|
||||
std::array<QGroupBox*, NUM_PLAYERS> player_groupboxes;
|
||||
|
||||
// Icons for currently connected controllers/players.
|
||||
std::array<QWidget*, NUM_PLAYERS> connected_controller_icons;
|
||||
|
||||
// Labels that represent the player numbers in place of the controller icons.
|
||||
std::array<QLabel*, NUM_PLAYERS> player_labels;
|
||||
|
||||
// LED patterns for currently connected controllers/players.
|
||||
std::array<std::array<QCheckBox*, 4>, NUM_PLAYERS> led_patterns_boxes;
|
||||
|
||||
// Labels representing additional information known as "Explain Text" per player.
|
||||
std::array<QLabel*, NUM_PLAYERS> explain_text_labels;
|
||||
|
||||
// Comboboxes with a list of emulated controllers per player.
|
||||
std::array<QComboBox*, NUM_PLAYERS> emulated_controllers;
|
||||
|
||||
/// Pairs of emulated controller index and Controller Type enum per player.
|
||||
std::array<std::vector<std::pair<int, Settings::ControllerType>>, NUM_PLAYERS>
|
||||
index_controller_type_pairs;
|
||||
|
||||
// Labels representing the number of connected controllers
|
||||
// above the "Connected Controllers" checkboxes.
|
||||
std::array<QLabel*, NUM_PLAYERS> connected_controller_labels;
|
||||
|
||||
// Checkboxes representing the "Connected Controllers".
|
||||
std::array<QCheckBox*, NUM_PLAYERS> connected_controller_checkboxes;
|
||||
};
|
||||
|
||||
class QtControllerSelector final : public QObject, public Core::Frontend::ControllerApplet {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit QtControllerSelector(GMainWindow& parent);
|
||||
~QtControllerSelector() override;
|
||||
|
||||
void ReconfigureControllers(
|
||||
std::function<void()> callback_,
|
||||
const Core::Frontend::ControllerParameters& parameters) const override;
|
||||
|
||||
signals:
|
||||
void MainWindowReconfigureControllers(
|
||||
const Core::Frontend::ControllerParameters& parameters) const;
|
||||
|
||||
private:
|
||||
void MainWindowReconfigureFinished();
|
||||
|
||||
mutable std::function<void()> callback;
|
||||
};
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,63 +0,0 @@
|
||||
// Copyright 2019 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <QDateTime>
|
||||
#include "core/hle/lock.h"
|
||||
#include "yuzu/applets/error.h"
|
||||
#include "yuzu/main.h"
|
||||
|
||||
QtErrorDisplay::QtErrorDisplay(GMainWindow& parent) {
|
||||
connect(this, &QtErrorDisplay::MainWindowDisplayError, &parent,
|
||||
&GMainWindow::ErrorDisplayDisplayError, Qt::QueuedConnection);
|
||||
connect(&parent, &GMainWindow::ErrorDisplayFinished, this,
|
||||
&QtErrorDisplay::MainWindowFinishedError, Qt::DirectConnection);
|
||||
}
|
||||
|
||||
QtErrorDisplay::~QtErrorDisplay() = default;
|
||||
|
||||
void QtErrorDisplay::ShowError(ResultCode error, std::function<void()> finished) const {
|
||||
callback = std::move(finished);
|
||||
emit MainWindowDisplayError(
|
||||
tr("Error Code: %1-%2 (0x%3)")
|
||||
.arg(static_cast<u32>(error.module.Value()) + 2000, 4, 10, QChar::fromLatin1('0'))
|
||||
.arg(error.description, 4, 10, QChar::fromLatin1('0'))
|
||||
.arg(error.raw, 8, 16, QChar::fromLatin1('0')),
|
||||
tr("An error has occurred.\nPlease try again or contact the developer of the software."));
|
||||
}
|
||||
|
||||
void QtErrorDisplay::ShowErrorWithTimestamp(ResultCode error, std::chrono::seconds time,
|
||||
std::function<void()> finished) const {
|
||||
callback = std::move(finished);
|
||||
|
||||
const QDateTime date_time = QDateTime::fromSecsSinceEpoch(time.count());
|
||||
emit MainWindowDisplayError(
|
||||
tr("Error Code: %1-%2 (0x%3)")
|
||||
.arg(static_cast<u32>(error.module.Value()) + 2000, 4, 10, QChar::fromLatin1('0'))
|
||||
.arg(error.description, 4, 10, QChar::fromLatin1('0'))
|
||||
.arg(error.raw, 8, 16, QChar::fromLatin1('0')),
|
||||
tr("An error occurred on %1 at %2.\nPlease try again or contact the developer of the "
|
||||
"software.")
|
||||
.arg(date_time.toString(QStringLiteral("dddd, MMMM d, yyyy")))
|
||||
.arg(date_time.toString(QStringLiteral("h:mm:ss A"))));
|
||||
}
|
||||
|
||||
void QtErrorDisplay::ShowCustomErrorText(ResultCode error, std::string dialog_text,
|
||||
std::string fullscreen_text,
|
||||
std::function<void()> finished) const {
|
||||
callback = std::move(finished);
|
||||
emit MainWindowDisplayError(
|
||||
tr("Error Code: %1-%2 (0x%3)")
|
||||
.arg(static_cast<u32>(error.module.Value()) + 2000, 4, 10, QChar::fromLatin1('0'))
|
||||
.arg(error.description, 4, 10, QChar::fromLatin1('0'))
|
||||
.arg(error.raw, 8, 16, QChar::fromLatin1('0')),
|
||||
tr("An error has occurred.\n\n%1\n\n%2")
|
||||
.arg(QString::fromStdString(dialog_text))
|
||||
.arg(QString::fromStdString(fullscreen_text)));
|
||||
}
|
||||
|
||||
void QtErrorDisplay::MainWindowFinishedError() {
|
||||
// Acquire the HLE mutex
|
||||
std::lock_guard lock{HLE::g_hle_lock};
|
||||
callback();
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
// Copyright 2019 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QObject>
|
||||
|
||||
#include "core/frontend/applets/error.h"
|
||||
|
||||
class GMainWindow;
|
||||
|
||||
class QtErrorDisplay final : public QObject, public Core::Frontend::ErrorApplet {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit QtErrorDisplay(GMainWindow& parent);
|
||||
~QtErrorDisplay() override;
|
||||
|
||||
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 dialog_text, std::string fullscreen_text,
|
||||
std::function<void()> finished) const override;
|
||||
|
||||
signals:
|
||||
void MainWindowDisplayError(QString error_code, QString error_text) const;
|
||||
|
||||
private:
|
||||
void MainWindowFinishedError();
|
||||
|
||||
mutable std::function<void()> callback;
|
||||
};
|
||||
@@ -1,163 +0,0 @@
|
||||
// Copyright 2018 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <mutex>
|
||||
#include <QDialogButtonBox>
|
||||
#include <QHeaderView>
|
||||
#include <QLabel>
|
||||
#include <QLineEdit>
|
||||
#include <QScrollArea>
|
||||
#include <QStandardItemModel>
|
||||
#include <QVBoxLayout>
|
||||
#include "common/fs/path_util.h"
|
||||
#include "common/string_util.h"
|
||||
#include "core/constants.h"
|
||||
#include "core/hle/lock.h"
|
||||
#include "yuzu/applets/profile_select.h"
|
||||
#include "yuzu/main.h"
|
||||
|
||||
namespace {
|
||||
QString FormatUserEntryText(const QString& username, Common::UUID uuid) {
|
||||
return QtProfileSelectionDialog::tr(
|
||||
"%1\n%2", "%1 is the profile username, %2 is the formatted UUID (e.g. "
|
||||
"00112233-4455-6677-8899-AABBCCDDEEFF))")
|
||||
.arg(username, QString::fromStdString(uuid.FormatSwitch()));
|
||||
}
|
||||
|
||||
QString GetImagePath(Common::UUID uuid) {
|
||||
const auto path =
|
||||
Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir) /
|
||||
fmt::format("system/save/8000000000000010/su/avators/{}.jpg", uuid.FormatSwitch());
|
||||
return QString::fromStdString(Common::FS::PathToUTF8String(path));
|
||||
}
|
||||
|
||||
QPixmap GetIcon(Common::UUID uuid) {
|
||||
QPixmap icon{GetImagePath(uuid)};
|
||||
|
||||
if (!icon) {
|
||||
icon.fill(Qt::black);
|
||||
icon.loadFromData(Core::Constants::ACCOUNT_BACKUP_JPEG.data(),
|
||||
static_cast<u32>(Core::Constants::ACCOUNT_BACKUP_JPEG.size()));
|
||||
}
|
||||
|
||||
return icon.scaled(64, 64, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
QtProfileSelectionDialog::QtProfileSelectionDialog(QWidget* parent)
|
||||
: QDialog(parent), profile_manager(std::make_unique<Service::Account::ProfileManager>()) {
|
||||
outer_layout = new QVBoxLayout;
|
||||
|
||||
instruction_label = new QLabel(tr("Select a user:"));
|
||||
|
||||
scroll_area = new QScrollArea;
|
||||
|
||||
buttons = new QDialogButtonBox(QDialogButtonBox::Cancel | QDialogButtonBox::Ok);
|
||||
connect(buttons, &QDialogButtonBox::accepted, this, &QtProfileSelectionDialog::accept);
|
||||
connect(buttons, &QDialogButtonBox::rejected, this, &QtProfileSelectionDialog::reject);
|
||||
|
||||
outer_layout->addWidget(instruction_label);
|
||||
outer_layout->addWidget(scroll_area);
|
||||
outer_layout->addWidget(buttons);
|
||||
|
||||
layout = new QVBoxLayout;
|
||||
tree_view = new QTreeView;
|
||||
item_model = new QStandardItemModel(tree_view);
|
||||
tree_view->setModel(item_model);
|
||||
|
||||
tree_view->setAlternatingRowColors(true);
|
||||
tree_view->setSelectionMode(QHeaderView::SingleSelection);
|
||||
tree_view->setSelectionBehavior(QHeaderView::SelectRows);
|
||||
tree_view->setVerticalScrollMode(QHeaderView::ScrollPerPixel);
|
||||
tree_view->setHorizontalScrollMode(QHeaderView::ScrollPerPixel);
|
||||
tree_view->setSortingEnabled(true);
|
||||
tree_view->setEditTriggers(QHeaderView::NoEditTriggers);
|
||||
tree_view->setUniformRowHeights(true);
|
||||
tree_view->setIconSize({64, 64});
|
||||
tree_view->setContextMenuPolicy(Qt::NoContextMenu);
|
||||
|
||||
item_model->insertColumns(0, 1);
|
||||
item_model->setHeaderData(0, Qt::Horizontal, tr("Users"));
|
||||
|
||||
// We must register all custom types with the Qt Automoc system so that we are able to use it
|
||||
// with signals/slots. In this case, QList falls under the umbrella of custom types.
|
||||
qRegisterMetaType<QList<QStandardItem*>>("QList<QStandardItem*>");
|
||||
|
||||
layout->setContentsMargins(0, 0, 0, 0);
|
||||
layout->setSpacing(0);
|
||||
layout->addWidget(tree_view);
|
||||
|
||||
scroll_area->setLayout(layout);
|
||||
|
||||
connect(tree_view, &QTreeView::clicked, this, &QtProfileSelectionDialog::SelectUser);
|
||||
|
||||
const auto& profiles = profile_manager->GetAllUsers();
|
||||
for (const auto& user : profiles) {
|
||||
Service::Account::ProfileBase profile{};
|
||||
if (!profile_manager->GetProfileBase(user, profile))
|
||||
continue;
|
||||
|
||||
const auto username = Common::StringFromFixedZeroTerminatedBuffer(
|
||||
reinterpret_cast<const char*>(profile.username.data()), profile.username.size());
|
||||
|
||||
list_items.push_back(QList<QStandardItem*>{new QStandardItem{
|
||||
GetIcon(user), FormatUserEntryText(QString::fromStdString(username), user)}});
|
||||
}
|
||||
|
||||
for (const auto& item : list_items)
|
||||
item_model->appendRow(item);
|
||||
|
||||
setLayout(outer_layout);
|
||||
setWindowTitle(tr("Profile Selector"));
|
||||
resize(550, 400);
|
||||
}
|
||||
|
||||
QtProfileSelectionDialog::~QtProfileSelectionDialog() = default;
|
||||
|
||||
int QtProfileSelectionDialog::exec() {
|
||||
// Skip profile selection when there's only one.
|
||||
if (profile_manager->GetUserCount() == 1) {
|
||||
user_index = 0;
|
||||
return QDialog::Accepted;
|
||||
}
|
||||
return QDialog::exec();
|
||||
}
|
||||
|
||||
void QtProfileSelectionDialog::accept() {
|
||||
QDialog::accept();
|
||||
}
|
||||
|
||||
void QtProfileSelectionDialog::reject() {
|
||||
user_index = 0;
|
||||
QDialog::reject();
|
||||
}
|
||||
|
||||
int QtProfileSelectionDialog::GetIndex() const {
|
||||
return user_index;
|
||||
}
|
||||
|
||||
void QtProfileSelectionDialog::SelectUser(const QModelIndex& index) {
|
||||
user_index = index.row();
|
||||
}
|
||||
|
||||
QtProfileSelector::QtProfileSelector(GMainWindow& parent) {
|
||||
connect(this, &QtProfileSelector::MainWindowSelectProfile, &parent,
|
||||
&GMainWindow::ProfileSelectorSelectProfile, Qt::QueuedConnection);
|
||||
connect(&parent, &GMainWindow::ProfileSelectorFinishedSelection, this,
|
||||
&QtProfileSelector::MainWindowFinishedSelection, Qt::DirectConnection);
|
||||
}
|
||||
|
||||
QtProfileSelector::~QtProfileSelector() = default;
|
||||
|
||||
void QtProfileSelector::SelectProfile(
|
||||
std::function<void(std::optional<Common::UUID>)> callback_) const {
|
||||
callback = std::move(callback_);
|
||||
emit MainWindowSelectProfile();
|
||||
}
|
||||
|
||||
void QtProfileSelector::MainWindowFinishedSelection(std::optional<Common::UUID> uuid) {
|
||||
// Acquire the HLE mutex
|
||||
std::lock_guard lock{HLE::g_hle_lock};
|
||||
callback(uuid);
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
// Copyright 2018 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <QDialog>
|
||||
#include <QList>
|
||||
#include <QTreeView>
|
||||
#include "core/frontend/applets/profile_select.h"
|
||||
#include "core/hle/service/acc/profile_manager.h"
|
||||
|
||||
class GMainWindow;
|
||||
class QDialogButtonBox;
|
||||
class QGraphicsScene;
|
||||
class QLabel;
|
||||
class QScrollArea;
|
||||
class QStandardItem;
|
||||
class QStandardItemModel;
|
||||
class QVBoxLayout;
|
||||
|
||||
class QtProfileSelectionDialog final : public QDialog {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit QtProfileSelectionDialog(QWidget* parent);
|
||||
~QtProfileSelectionDialog() override;
|
||||
|
||||
int exec() override;
|
||||
void accept() override;
|
||||
void reject() override;
|
||||
|
||||
int GetIndex() const;
|
||||
|
||||
private:
|
||||
void SelectUser(const QModelIndex& index);
|
||||
|
||||
int user_index = 0;
|
||||
|
||||
QVBoxLayout* layout;
|
||||
QTreeView* tree_view;
|
||||
QStandardItemModel* item_model;
|
||||
QGraphicsScene* scene;
|
||||
|
||||
std::vector<QList<QStandardItem*>> list_items;
|
||||
|
||||
QVBoxLayout* outer_layout;
|
||||
QLabel* instruction_label;
|
||||
QScrollArea* scroll_area;
|
||||
QDialogButtonBox* buttons;
|
||||
|
||||
std::unique_ptr<Service::Account::ProfileManager> profile_manager;
|
||||
};
|
||||
|
||||
class QtProfileSelector final : public QObject, public Core::Frontend::ProfileSelectApplet {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit QtProfileSelector(GMainWindow& parent);
|
||||
~QtProfileSelector() override;
|
||||
|
||||
void SelectProfile(std::function<void(std::optional<Common::UUID>)> callback_) const override;
|
||||
|
||||
signals:
|
||||
void MainWindowSelectProfile() const;
|
||||
|
||||
private:
|
||||
void MainWindowFinishedSelection(std::optional<Common::UUID> uuid);
|
||||
|
||||
mutable std::function<void(std::optional<Common::UUID>)> callback;
|
||||
};
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,285 +0,0 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
|
||||
#include <QDialog>
|
||||
#include <QValidator>
|
||||
|
||||
#include "core/frontend/applets/software_keyboard.h"
|
||||
|
||||
enum class HIDButton : u8;
|
||||
|
||||
class InputInterpreter;
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
namespace Ui {
|
||||
class QtSoftwareKeyboardDialog;
|
||||
}
|
||||
|
||||
class GMainWindow;
|
||||
|
||||
class QtSoftwareKeyboardDialog final : public QDialog {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
QtSoftwareKeyboardDialog(QWidget* parent, Core::System& system_, bool is_inline_,
|
||||
Core::Frontend::KeyboardInitializeParameters initialize_parameters_);
|
||||
~QtSoftwareKeyboardDialog() override;
|
||||
|
||||
void ShowNormalKeyboard(QPoint pos, QSize size);
|
||||
|
||||
void ShowTextCheckDialog(Service::AM::Applets::SwkbdTextCheckResult text_check_result,
|
||||
std::u16string text_check_message);
|
||||
|
||||
void ShowInlineKeyboard(Core::Frontend::InlineAppearParameters appear_parameters, QPoint pos,
|
||||
QSize size);
|
||||
|
||||
void HideInlineKeyboard();
|
||||
|
||||
void InlineTextChanged(Core::Frontend::InlineTextParameters text_parameters);
|
||||
|
||||
void ExitKeyboard();
|
||||
|
||||
signals:
|
||||
void SubmitNormalText(Service::AM::Applets::SwkbdResult result,
|
||||
std::u16string submitted_text) const;
|
||||
|
||||
void SubmitInlineText(Service::AM::Applets::SwkbdReplyType reply_type,
|
||||
std::u16string submitted_text, s32 cursor_position) const;
|
||||
|
||||
public slots:
|
||||
void open() override;
|
||||
void reject() override;
|
||||
|
||||
protected:
|
||||
/// We override the keyPressEvent for inputting text into the inline software keyboard.
|
||||
void keyPressEvent(QKeyEvent* event) override;
|
||||
|
||||
private:
|
||||
enum class Direction {
|
||||
Left,
|
||||
Up,
|
||||
Right,
|
||||
Down,
|
||||
};
|
||||
|
||||
enum class BottomOSKIndex {
|
||||
LowerCase,
|
||||
UpperCase,
|
||||
NumberPad,
|
||||
};
|
||||
|
||||
/**
|
||||
* Moves and resizes the window to a specified position and size.
|
||||
*
|
||||
* @param pos Top-left window position
|
||||
* @param size Window size
|
||||
*/
|
||||
void MoveAndResizeWindow(QPoint pos, QSize size);
|
||||
|
||||
/**
|
||||
* Rescales all keyboard elements to account for High DPI displays.
|
||||
*
|
||||
* @param width Window width
|
||||
* @param height Window height
|
||||
* @param dpi_scale Display scaling factor
|
||||
*/
|
||||
void RescaleKeyboardElements(float width, float height, float dpi_scale);
|
||||
|
||||
/// Sets the keyboard type based on initialize_parameters.
|
||||
void SetKeyboardType();
|
||||
|
||||
/// Sets the password mode based on initialize_parameters.
|
||||
void SetPasswordMode();
|
||||
|
||||
/// Sets the text draw type based on initialize_parameters.
|
||||
void SetTextDrawType();
|
||||
|
||||
/// Sets the controller image at the bottom left of the software keyboard.
|
||||
void SetControllerImage();
|
||||
|
||||
/// Disables buttons based on initialize_parameters.
|
||||
void DisableKeyboardButtons();
|
||||
|
||||
/// Changes whether the backspace or/and ok buttons should be enabled or disabled.
|
||||
void SetBackspaceOkEnabled();
|
||||
|
||||
/**
|
||||
* Validates the input text sent in based on the parameters in initialize_parameters.
|
||||
*
|
||||
* @param input_text Input text
|
||||
*
|
||||
* @returns True if the input text is valid, false otherwise.
|
||||
*/
|
||||
bool ValidateInputText(const QString& input_text);
|
||||
|
||||
/// Switches between LowerCase and UpperCase (Shift and Caps Lock)
|
||||
void ChangeBottomOSKIndex();
|
||||
|
||||
/// Processes a keyboard button click from the UI as normal keyboard input.
|
||||
void NormalKeyboardButtonClicked(QPushButton* button);
|
||||
|
||||
/// Processes a keyboard button click from the UI as inline keyboard input.
|
||||
void InlineKeyboardButtonClicked(QPushButton* button);
|
||||
|
||||
/**
|
||||
* Inserts a string of arbitrary length into the current_text at the current cursor position.
|
||||
* This is only used for the inline software keyboard.
|
||||
*/
|
||||
void InlineTextInsertString(std::u16string_view string);
|
||||
|
||||
/// Setup the mouse hover workaround for "focusing" buttons. This should only be called once.
|
||||
void SetupMouseHover();
|
||||
|
||||
/**
|
||||
* Handles button presses and converts them into keyboard input.
|
||||
*
|
||||
* @tparam HIDButton The list of buttons that can be converted into keyboard input.
|
||||
*/
|
||||
template <HIDButton... T>
|
||||
void HandleButtonPressedOnce();
|
||||
|
||||
/**
|
||||
* Handles button holds and converts them into keyboard input.
|
||||
*
|
||||
* @tparam HIDButton The list of buttons that can be converted into keyboard input.
|
||||
*/
|
||||
template <HIDButton... T>
|
||||
void HandleButtonHold();
|
||||
|
||||
/**
|
||||
* Translates a button press to focus or click a keyboard button.
|
||||
*
|
||||
* @param button The button press to process.
|
||||
*/
|
||||
void TranslateButtonPress(HIDButton button);
|
||||
|
||||
/**
|
||||
* Moves the focus of a button in a certain direction.
|
||||
*
|
||||
* @param direction The direction to move.
|
||||
*/
|
||||
void MoveButtonDirection(Direction direction);
|
||||
|
||||
/**
|
||||
* Moves the text cursor in a certain direction.
|
||||
*
|
||||
* @param direction The direction to move.
|
||||
*/
|
||||
void MoveTextCursorDirection(Direction direction);
|
||||
|
||||
void StartInputThread();
|
||||
void StopInputThread();
|
||||
|
||||
/// The thread where input is being polled and processed.
|
||||
void InputThread();
|
||||
|
||||
std::unique_ptr<Ui::QtSoftwareKeyboardDialog> ui;
|
||||
|
||||
Core::System& system;
|
||||
|
||||
// True if it is the inline software keyboard.
|
||||
bool is_inline;
|
||||
|
||||
// Common software keyboard initialize parameters.
|
||||
Core::Frontend::KeyboardInitializeParameters initialize_parameters;
|
||||
|
||||
// Used only by the inline software keyboard since the QLineEdit or QTextEdit is hidden.
|
||||
std::u16string current_text;
|
||||
s32 cursor_position{0};
|
||||
|
||||
static constexpr std::size_t NUM_ROWS_NORMAL = 5;
|
||||
static constexpr std::size_t NUM_COLUMNS_NORMAL = 12;
|
||||
static constexpr std::size_t NUM_ROWS_NUMPAD = 4;
|
||||
static constexpr std::size_t NUM_COLUMNS_NUMPAD = 4;
|
||||
|
||||
// Stores the normal keyboard layout.
|
||||
std::array<std::array<std::array<QPushButton*, NUM_COLUMNS_NORMAL>, NUM_ROWS_NORMAL>, 2>
|
||||
keyboard_buttons;
|
||||
// Stores the numberpad keyboard layout.
|
||||
std::array<std::array<QPushButton*, NUM_COLUMNS_NUMPAD>, NUM_ROWS_NUMPAD> numberpad_buttons;
|
||||
|
||||
// Contains a set of all buttons used in keyboard_buttons and numberpad_buttons.
|
||||
std::array<QPushButton*, 110> all_buttons;
|
||||
|
||||
std::size_t row{0};
|
||||
std::size_t column{0};
|
||||
|
||||
BottomOSKIndex bottom_osk_index{BottomOSKIndex::LowerCase};
|
||||
std::atomic<bool> caps_lock_enabled{false};
|
||||
|
||||
std::unique_ptr<InputInterpreter> input_interpreter;
|
||||
|
||||
std::thread input_thread;
|
||||
|
||||
std::atomic<bool> input_thread_running{};
|
||||
};
|
||||
|
||||
class QtSoftwareKeyboard final : public QObject, public Core::Frontend::SoftwareKeyboardApplet {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit QtSoftwareKeyboard(GMainWindow& parent);
|
||||
~QtSoftwareKeyboard() override;
|
||||
|
||||
void InitializeKeyboard(
|
||||
bool is_inline, Core::Frontend::KeyboardInitializeParameters initialize_parameters,
|
||||
std::function<void(Service::AM::Applets::SwkbdResult, std::u16string)>
|
||||
submit_normal_callback_,
|
||||
std::function<void(Service::AM::Applets::SwkbdReplyType, std::u16string, s32)>
|
||||
submit_inline_callback_) override;
|
||||
|
||||
void ShowNormalKeyboard() const override;
|
||||
|
||||
void ShowTextCheckDialog(Service::AM::Applets::SwkbdTextCheckResult text_check_result,
|
||||
std::u16string text_check_message) const override;
|
||||
|
||||
void ShowInlineKeyboard(
|
||||
Core::Frontend::InlineAppearParameters appear_parameters) const override;
|
||||
|
||||
void HideInlineKeyboard() const override;
|
||||
|
||||
void InlineTextChanged(Core::Frontend::InlineTextParameters text_parameters) const override;
|
||||
|
||||
void ExitKeyboard() const override;
|
||||
|
||||
signals:
|
||||
void MainWindowInitializeKeyboard(
|
||||
bool is_inline, Core::Frontend::KeyboardInitializeParameters initialize_parameters) const;
|
||||
|
||||
void MainWindowShowNormalKeyboard() const;
|
||||
|
||||
void MainWindowShowTextCheckDialog(Service::AM::Applets::SwkbdTextCheckResult text_check_result,
|
||||
std::u16string text_check_message) const;
|
||||
|
||||
void MainWindowShowInlineKeyboard(
|
||||
Core::Frontend::InlineAppearParameters appear_parameters) const;
|
||||
|
||||
void MainWindowHideInlineKeyboard() const;
|
||||
|
||||
void MainWindowInlineTextChanged(Core::Frontend::InlineTextParameters text_parameters) const;
|
||||
|
||||
void MainWindowExitKeyboard() const;
|
||||
|
||||
private:
|
||||
void SubmitNormalText(Service::AM::Applets::SwkbdResult result,
|
||||
std::u16string submitted_text) const;
|
||||
|
||||
void SubmitInlineText(Service::AM::Applets::SwkbdReplyType reply_type,
|
||||
std::u16string submitted_text, s32 cursor_position) const;
|
||||
|
||||
mutable std::function<void(Service::AM::Applets::SwkbdResult, std::u16string)>
|
||||
submit_normal_callback;
|
||||
mutable std::function<void(Service::AM::Applets::SwkbdReplyType, std::u16string, s32)>
|
||||
submit_inline_callback;
|
||||
};
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,417 +0,0 @@
|
||||
// Copyright 2020 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#ifdef YUZU_USE_QT_WEB_ENGINE
|
||||
#include <QKeyEvent>
|
||||
|
||||
#include <QWebEngineProfile>
|
||||
#include <QWebEngineScript>
|
||||
#include <QWebEngineScriptCollection>
|
||||
#include <QWebEngineSettings>
|
||||
#include <QWebEngineUrlScheme>
|
||||
#endif
|
||||
|
||||
#include "common/fs/path_util.h"
|
||||
#include "core/core.h"
|
||||
#include "core/frontend/input_interpreter.h"
|
||||
#include "input_common/keyboard.h"
|
||||
#include "input_common/main.h"
|
||||
#include "yuzu/applets/web_browser.h"
|
||||
#include "yuzu/applets/web_browser_scripts.h"
|
||||
#include "yuzu/main.h"
|
||||
#include "yuzu/util/url_request_interceptor.h"
|
||||
|
||||
#ifdef YUZU_USE_QT_WEB_ENGINE
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr int HIDButtonToKey(HIDButton button) {
|
||||
switch (button) {
|
||||
case HIDButton::DLeft:
|
||||
case HIDButton::LStickLeft:
|
||||
return Qt::Key_Left;
|
||||
case HIDButton::DUp:
|
||||
case HIDButton::LStickUp:
|
||||
return Qt::Key_Up;
|
||||
case HIDButton::DRight:
|
||||
case HIDButton::LStickRight:
|
||||
return Qt::Key_Right;
|
||||
case HIDButton::DDown:
|
||||
case HIDButton::LStickDown:
|
||||
return Qt::Key_Down;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
} // Anonymous namespace
|
||||
|
||||
QtNXWebEngineView::QtNXWebEngineView(QWidget* parent, Core::System& system,
|
||||
InputCommon::InputSubsystem* input_subsystem_)
|
||||
: QWebEngineView(parent), input_subsystem{input_subsystem_},
|
||||
url_interceptor(std::make_unique<UrlRequestInterceptor>()),
|
||||
input_interpreter(std::make_unique<InputInterpreter>(system)),
|
||||
default_profile{QWebEngineProfile::defaultProfile()},
|
||||
global_settings{QWebEngineSettings::globalSettings()} {
|
||||
QWebEngineScript gamepad;
|
||||
QWebEngineScript window_nx;
|
||||
|
||||
gamepad.setName(QStringLiteral("gamepad_script.js"));
|
||||
window_nx.setName(QStringLiteral("window_nx_script.js"));
|
||||
|
||||
gamepad.setSourceCode(QString::fromStdString(GAMEPAD_SCRIPT));
|
||||
window_nx.setSourceCode(QString::fromStdString(WINDOW_NX_SCRIPT));
|
||||
|
||||
gamepad.setInjectionPoint(QWebEngineScript::DocumentCreation);
|
||||
window_nx.setInjectionPoint(QWebEngineScript::DocumentCreation);
|
||||
|
||||
gamepad.setWorldId(QWebEngineScript::MainWorld);
|
||||
window_nx.setWorldId(QWebEngineScript::MainWorld);
|
||||
|
||||
gamepad.setRunsOnSubFrames(true);
|
||||
window_nx.setRunsOnSubFrames(true);
|
||||
|
||||
default_profile->scripts()->insert(gamepad);
|
||||
default_profile->scripts()->insert(window_nx);
|
||||
|
||||
default_profile->setRequestInterceptor(url_interceptor.get());
|
||||
|
||||
global_settings->setAttribute(QWebEngineSettings::LocalContentCanAccessRemoteUrls, true);
|
||||
global_settings->setAttribute(QWebEngineSettings::FullScreenSupportEnabled, true);
|
||||
global_settings->setAttribute(QWebEngineSettings::AllowRunningInsecureContent, true);
|
||||
global_settings->setAttribute(QWebEngineSettings::FocusOnNavigationEnabled, true);
|
||||
global_settings->setAttribute(QWebEngineSettings::AllowWindowActivationFromJavaScript, true);
|
||||
global_settings->setAttribute(QWebEngineSettings::ShowScrollBars, false);
|
||||
|
||||
global_settings->setFontFamily(QWebEngineSettings::StandardFont, QStringLiteral("Roboto"));
|
||||
|
||||
connect(
|
||||
page(), &QWebEnginePage::windowCloseRequested, page(),
|
||||
[this] {
|
||||
if (page()->url() == url_interceptor->GetRequestedURL()) {
|
||||
SetFinished(true);
|
||||
SetExitReason(Service::AM::Applets::WebExitReason::WindowClosed);
|
||||
}
|
||||
},
|
||||
Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
QtNXWebEngineView::~QtNXWebEngineView() {
|
||||
SetFinished(true);
|
||||
StopInputThread();
|
||||
}
|
||||
|
||||
void QtNXWebEngineView::LoadLocalWebPage(const std::string& main_url,
|
||||
const std::string& additional_args) {
|
||||
is_local = true;
|
||||
|
||||
LoadExtractedFonts();
|
||||
SetUserAgent(UserAgent::WebApplet);
|
||||
SetFinished(false);
|
||||
SetExitReason(Service::AM::Applets::WebExitReason::EndButtonPressed);
|
||||
SetLastURL("http://localhost/");
|
||||
StartInputThread();
|
||||
|
||||
load(QUrl(QUrl::fromLocalFile(QString::fromStdString(main_url)).toString() +
|
||||
QString::fromStdString(additional_args)));
|
||||
}
|
||||
|
||||
void QtNXWebEngineView::LoadExternalWebPage(const std::string& main_url,
|
||||
const std::string& additional_args) {
|
||||
is_local = false;
|
||||
|
||||
SetUserAgent(UserAgent::WebApplet);
|
||||
SetFinished(false);
|
||||
SetExitReason(Service::AM::Applets::WebExitReason::EndButtonPressed);
|
||||
SetLastURL("http://localhost/");
|
||||
StartInputThread();
|
||||
|
||||
load(QUrl(QString::fromStdString(main_url) + QString::fromStdString(additional_args)));
|
||||
}
|
||||
|
||||
void QtNXWebEngineView::SetUserAgent(UserAgent user_agent) {
|
||||
const QString user_agent_str = [user_agent] {
|
||||
switch (user_agent) {
|
||||
case UserAgent::WebApplet:
|
||||
default:
|
||||
return QStringLiteral("WebApplet");
|
||||
case UserAgent::ShopN:
|
||||
return QStringLiteral("ShopN");
|
||||
case UserAgent::LoginApplet:
|
||||
return QStringLiteral("LoginApplet");
|
||||
case UserAgent::ShareApplet:
|
||||
return QStringLiteral("ShareApplet");
|
||||
case UserAgent::LobbyApplet:
|
||||
return QStringLiteral("LobbyApplet");
|
||||
case UserAgent::WifiWebAuthApplet:
|
||||
return QStringLiteral("WifiWebAuthApplet");
|
||||
}
|
||||
}();
|
||||
|
||||
QWebEngineProfile::defaultProfile()->setHttpUserAgent(
|
||||
QStringLiteral("Mozilla/5.0 (Nintendo Switch; %1) AppleWebKit/606.4 "
|
||||
"(KHTML, like Gecko) NF/6.0.1.15.4 NintendoBrowser/5.1.0.20389")
|
||||
.arg(user_agent_str));
|
||||
}
|
||||
|
||||
bool QtNXWebEngineView::IsFinished() const {
|
||||
return finished;
|
||||
}
|
||||
|
||||
void QtNXWebEngineView::SetFinished(bool finished_) {
|
||||
finished = finished_;
|
||||
}
|
||||
|
||||
Service::AM::Applets::WebExitReason QtNXWebEngineView::GetExitReason() const {
|
||||
return exit_reason;
|
||||
}
|
||||
|
||||
void QtNXWebEngineView::SetExitReason(Service::AM::Applets::WebExitReason exit_reason_) {
|
||||
exit_reason = exit_reason_;
|
||||
}
|
||||
|
||||
const std::string& QtNXWebEngineView::GetLastURL() const {
|
||||
return last_url;
|
||||
}
|
||||
|
||||
void QtNXWebEngineView::SetLastURL(std::string last_url_) {
|
||||
last_url = std::move(last_url_);
|
||||
}
|
||||
|
||||
QString QtNXWebEngineView::GetCurrentURL() const {
|
||||
return url_interceptor->GetRequestedURL().toString();
|
||||
}
|
||||
|
||||
void QtNXWebEngineView::hide() {
|
||||
SetFinished(true);
|
||||
StopInputThread();
|
||||
|
||||
QWidget::hide();
|
||||
}
|
||||
|
||||
void QtNXWebEngineView::keyPressEvent(QKeyEvent* event) {
|
||||
if (is_local) {
|
||||
input_subsystem->GetKeyboard()->PressKey(event->key());
|
||||
}
|
||||
}
|
||||
|
||||
void QtNXWebEngineView::keyReleaseEvent(QKeyEvent* event) {
|
||||
if (is_local) {
|
||||
input_subsystem->GetKeyboard()->ReleaseKey(event->key());
|
||||
}
|
||||
}
|
||||
|
||||
template <HIDButton... T>
|
||||
void QtNXWebEngineView::HandleWindowFooterButtonPressedOnce() {
|
||||
const auto f = [this](HIDButton button) {
|
||||
if (input_interpreter->IsButtonPressedOnce(button)) {
|
||||
page()->runJavaScript(
|
||||
QStringLiteral("yuzu_key_callbacks[%1] == null;").arg(static_cast<u8>(button)),
|
||||
[&](const QVariant& variant) {
|
||||
if (variant.toBool()) {
|
||||
switch (button) {
|
||||
case HIDButton::A:
|
||||
SendMultipleKeyPressEvents<Qt::Key_A, Qt::Key_Space, Qt::Key_Return>();
|
||||
break;
|
||||
case HIDButton::B:
|
||||
SendKeyPressEvent(Qt::Key_B);
|
||||
break;
|
||||
case HIDButton::X:
|
||||
SendKeyPressEvent(Qt::Key_X);
|
||||
break;
|
||||
case HIDButton::Y:
|
||||
SendKeyPressEvent(Qt::Key_Y);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
page()->runJavaScript(
|
||||
QStringLiteral("if (yuzu_key_callbacks[%1] != null) { yuzu_key_callbacks[%1](); }")
|
||||
.arg(static_cast<u8>(button)));
|
||||
}
|
||||
};
|
||||
|
||||
(f(T), ...);
|
||||
}
|
||||
|
||||
template <HIDButton... T>
|
||||
void QtNXWebEngineView::HandleWindowKeyButtonPressedOnce() {
|
||||
const auto f = [this](HIDButton button) {
|
||||
if (input_interpreter->IsButtonPressedOnce(button)) {
|
||||
SendKeyPressEvent(HIDButtonToKey(button));
|
||||
}
|
||||
};
|
||||
|
||||
(f(T), ...);
|
||||
}
|
||||
|
||||
template <HIDButton... T>
|
||||
void QtNXWebEngineView::HandleWindowKeyButtonHold() {
|
||||
const auto f = [this](HIDButton button) {
|
||||
if (input_interpreter->IsButtonHeld(button)) {
|
||||
SendKeyPressEvent(HIDButtonToKey(button));
|
||||
}
|
||||
};
|
||||
|
||||
(f(T), ...);
|
||||
}
|
||||
|
||||
void QtNXWebEngineView::SendKeyPressEvent(int key) {
|
||||
if (key == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
QCoreApplication::postEvent(focusProxy(),
|
||||
new QKeyEvent(QKeyEvent::KeyPress, key, Qt::NoModifier));
|
||||
QCoreApplication::postEvent(focusProxy(),
|
||||
new QKeyEvent(QKeyEvent::KeyRelease, key, Qt::NoModifier));
|
||||
}
|
||||
|
||||
void QtNXWebEngineView::StartInputThread() {
|
||||
if (input_thread_running) {
|
||||
return;
|
||||
}
|
||||
|
||||
input_thread_running = true;
|
||||
input_thread = std::thread(&QtNXWebEngineView::InputThread, this);
|
||||
}
|
||||
|
||||
void QtNXWebEngineView::StopInputThread() {
|
||||
if (is_local) {
|
||||
QWidget::releaseKeyboard();
|
||||
}
|
||||
|
||||
input_thread_running = false;
|
||||
if (input_thread.joinable()) {
|
||||
input_thread.join();
|
||||
}
|
||||
}
|
||||
|
||||
void QtNXWebEngineView::InputThread() {
|
||||
// Wait for 1 second before allowing any inputs to be processed.
|
||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||
|
||||
if (is_local) {
|
||||
QWidget::grabKeyboard();
|
||||
}
|
||||
|
||||
while (input_thread_running) {
|
||||
input_interpreter->PollInput();
|
||||
|
||||
HandleWindowFooterButtonPressedOnce<HIDButton::A, HIDButton::B, HIDButton::X, HIDButton::Y,
|
||||
HIDButton::L, HIDButton::R>();
|
||||
|
||||
HandleWindowKeyButtonPressedOnce<HIDButton::DLeft, HIDButton::DUp, HIDButton::DRight,
|
||||
HIDButton::DDown, HIDButton::LStickLeft,
|
||||
HIDButton::LStickUp, HIDButton::LStickRight,
|
||||
HIDButton::LStickDown>();
|
||||
|
||||
HandleWindowKeyButtonHold<HIDButton::DLeft, HIDButton::DUp, HIDButton::DRight,
|
||||
HIDButton::DDown, HIDButton::LStickLeft, HIDButton::LStickUp,
|
||||
HIDButton::LStickRight, HIDButton::LStickDown>();
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
||||
}
|
||||
}
|
||||
|
||||
void QtNXWebEngineView::LoadExtractedFonts() {
|
||||
QWebEngineScript nx_font_css;
|
||||
QWebEngineScript load_nx_font;
|
||||
|
||||
auto fonts_dir_str = Common::FS::PathToUTF8String(
|
||||
Common::FS::GetYuzuPath(Common::FS::YuzuPath::CacheDir) / "fonts/");
|
||||
|
||||
std::replace(fonts_dir_str.begin(), fonts_dir_str.end(), '\\', '/');
|
||||
|
||||
const auto fonts_dir = QString::fromStdString(fonts_dir_str);
|
||||
|
||||
nx_font_css.setName(QStringLiteral("nx_font_css.js"));
|
||||
load_nx_font.setName(QStringLiteral("load_nx_font.js"));
|
||||
|
||||
nx_font_css.setSourceCode(
|
||||
QString::fromStdString(NX_FONT_CSS)
|
||||
.arg(fonts_dir + QStringLiteral("FontStandard.ttf"))
|
||||
.arg(fonts_dir + QStringLiteral("FontChineseSimplified.ttf"))
|
||||
.arg(fonts_dir + QStringLiteral("FontExtendedChineseSimplified.ttf"))
|
||||
.arg(fonts_dir + QStringLiteral("FontChineseTraditional.ttf"))
|
||||
.arg(fonts_dir + QStringLiteral("FontKorean.ttf"))
|
||||
.arg(fonts_dir + QStringLiteral("FontNintendoExtended.ttf"))
|
||||
.arg(fonts_dir + QStringLiteral("FontNintendoExtended2.ttf")));
|
||||
load_nx_font.setSourceCode(QString::fromStdString(LOAD_NX_FONT));
|
||||
|
||||
nx_font_css.setInjectionPoint(QWebEngineScript::DocumentReady);
|
||||
load_nx_font.setInjectionPoint(QWebEngineScript::Deferred);
|
||||
|
||||
nx_font_css.setWorldId(QWebEngineScript::MainWorld);
|
||||
load_nx_font.setWorldId(QWebEngineScript::MainWorld);
|
||||
|
||||
nx_font_css.setRunsOnSubFrames(true);
|
||||
load_nx_font.setRunsOnSubFrames(true);
|
||||
|
||||
default_profile->scripts()->insert(nx_font_css);
|
||||
default_profile->scripts()->insert(load_nx_font);
|
||||
|
||||
connect(
|
||||
url_interceptor.get(), &UrlRequestInterceptor::FrameChanged, url_interceptor.get(),
|
||||
[this] {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
||||
page()->runJavaScript(QString::fromStdString(LOAD_NX_FONT));
|
||||
},
|
||||
Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
QtWebBrowser::QtWebBrowser(GMainWindow& main_window) {
|
||||
connect(this, &QtWebBrowser::MainWindowOpenWebPage, &main_window,
|
||||
&GMainWindow::WebBrowserOpenWebPage, Qt::QueuedConnection);
|
||||
connect(&main_window, &GMainWindow::WebBrowserExtractOfflineRomFS, this,
|
||||
&QtWebBrowser::MainWindowExtractOfflineRomFS, Qt::QueuedConnection);
|
||||
connect(&main_window, &GMainWindow::WebBrowserClosed, this,
|
||||
&QtWebBrowser::MainWindowWebBrowserClosed, Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
QtWebBrowser::~QtWebBrowser() = default;
|
||||
|
||||
void QtWebBrowser::OpenLocalWebPage(
|
||||
const std::string& local_url, std::function<void()> extract_romfs_callback_,
|
||||
std::function<void(Service::AM::Applets::WebExitReason, std::string)> callback_) const {
|
||||
extract_romfs_callback = std::move(extract_romfs_callback_);
|
||||
callback = std::move(callback_);
|
||||
|
||||
const auto index = local_url.find('?');
|
||||
|
||||
if (index == std::string::npos) {
|
||||
emit MainWindowOpenWebPage(local_url, "", true);
|
||||
} else {
|
||||
emit MainWindowOpenWebPage(local_url.substr(0, index), local_url.substr(index), true);
|
||||
}
|
||||
}
|
||||
|
||||
void QtWebBrowser::OpenExternalWebPage(
|
||||
const std::string& external_url,
|
||||
std::function<void(Service::AM::Applets::WebExitReason, std::string)> callback_) const {
|
||||
callback = std::move(callback_);
|
||||
|
||||
const auto index = external_url.find('?');
|
||||
|
||||
if (index == std::string::npos) {
|
||||
emit MainWindowOpenWebPage(external_url, "", false);
|
||||
} else {
|
||||
emit MainWindowOpenWebPage(external_url.substr(0, index), external_url.substr(index),
|
||||
false);
|
||||
}
|
||||
}
|
||||
|
||||
void QtWebBrowser::MainWindowExtractOfflineRomFS() {
|
||||
extract_romfs_callback();
|
||||
}
|
||||
|
||||
void QtWebBrowser::MainWindowWebBrowserClosed(Service::AM::Applets::WebExitReason exit_reason,
|
||||
std::string last_url) {
|
||||
callback(exit_reason, last_url);
|
||||
}
|
||||
@@ -1,218 +0,0 @@
|
||||
// Copyright 2020 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
|
||||
#include <QObject>
|
||||
|
||||
#ifdef YUZU_USE_QT_WEB_ENGINE
|
||||
#include <QWebEngineView>
|
||||
#endif
|
||||
|
||||
#include "core/frontend/applets/web_browser.h"
|
||||
|
||||
enum class HIDButton : u8;
|
||||
|
||||
class GMainWindow;
|
||||
class InputInterpreter;
|
||||
class UrlRequestInterceptor;
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
namespace InputCommon {
|
||||
class InputSubsystem;
|
||||
}
|
||||
|
||||
#ifdef YUZU_USE_QT_WEB_ENGINE
|
||||
|
||||
enum class UserAgent {
|
||||
WebApplet,
|
||||
ShopN,
|
||||
LoginApplet,
|
||||
ShareApplet,
|
||||
LobbyApplet,
|
||||
WifiWebAuthApplet,
|
||||
};
|
||||
|
||||
class QWebEngineProfile;
|
||||
class QWebEngineSettings;
|
||||
|
||||
class QtNXWebEngineView : public QWebEngineView {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit QtNXWebEngineView(QWidget* parent, Core::System& system,
|
||||
InputCommon::InputSubsystem* input_subsystem_);
|
||||
~QtNXWebEngineView() override;
|
||||
|
||||
/**
|
||||
* Loads a HTML document that exists locally. Cannot be used to load external websites.
|
||||
*
|
||||
* @param main_url The url to the file.
|
||||
* @param additional_args Additional arguments appended to the main url.
|
||||
*/
|
||||
void LoadLocalWebPage(const std::string& main_url, const std::string& additional_args);
|
||||
|
||||
/**
|
||||
* Loads an external website. Cannot be used to load local urls.
|
||||
*
|
||||
* @param main_url The url to the website.
|
||||
* @param additional_args Additional arguments appended to the main url.
|
||||
*/
|
||||
void LoadExternalWebPage(const std::string& main_url, const std::string& additional_args);
|
||||
|
||||
/**
|
||||
* Sets the background color of the web page.
|
||||
*
|
||||
* @param color The color to set.
|
||||
*/
|
||||
void SetBackgroundColor(QColor color);
|
||||
|
||||
/**
|
||||
* Sets the user agent of the web browser.
|
||||
*
|
||||
* @param user_agent The user agent enum.
|
||||
*/
|
||||
void SetUserAgent(UserAgent user_agent);
|
||||
|
||||
[[nodiscard]] bool IsFinished() const;
|
||||
void SetFinished(bool finished_);
|
||||
|
||||
[[nodiscard]] Service::AM::Applets::WebExitReason GetExitReason() const;
|
||||
void SetExitReason(Service::AM::Applets::WebExitReason exit_reason_);
|
||||
|
||||
[[nodiscard]] const std::string& GetLastURL() const;
|
||||
void SetLastURL(std::string last_url_);
|
||||
|
||||
/**
|
||||
* This gets the current URL that has been requested by the webpage.
|
||||
* This only applies to the main frame. Sub frames and other resources are ignored.
|
||||
*
|
||||
* @return Currently requested URL
|
||||
*/
|
||||
[[nodiscard]] QString GetCurrentURL() const;
|
||||
|
||||
public slots:
|
||||
void hide();
|
||||
|
||||
protected:
|
||||
void keyPressEvent(QKeyEvent* event) override;
|
||||
void keyReleaseEvent(QKeyEvent* event) override;
|
||||
|
||||
private:
|
||||
/**
|
||||
* Handles button presses to execute functions assigned in yuzu_key_callbacks.
|
||||
* yuzu_key_callbacks contains specialized functions for the buttons in the window footer
|
||||
* that can be overriden by games to achieve desired functionality.
|
||||
*
|
||||
* @tparam HIDButton The list of buttons contained in yuzu_key_callbacks
|
||||
*/
|
||||
template <HIDButton... T>
|
||||
void HandleWindowFooterButtonPressedOnce();
|
||||
|
||||
/**
|
||||
* Handles button presses and converts them into keyboard input.
|
||||
* This should only be used to convert D-Pad or Analog Stick input into arrow keys.
|
||||
*
|
||||
* @tparam HIDButton The list of buttons that can be converted into keyboard input.
|
||||
*/
|
||||
template <HIDButton... T>
|
||||
void HandleWindowKeyButtonPressedOnce();
|
||||
|
||||
/**
|
||||
* Handles button holds and converts them into keyboard input.
|
||||
* This should only be used to convert D-Pad or Analog Stick input into arrow keys.
|
||||
*
|
||||
* @tparam HIDButton The list of buttons that can be converted into keyboard input.
|
||||
*/
|
||||
template <HIDButton... T>
|
||||
void HandleWindowKeyButtonHold();
|
||||
|
||||
/**
|
||||
* Sends a key press event to QWebEngineView.
|
||||
*
|
||||
* @param key Qt key code.
|
||||
*/
|
||||
void SendKeyPressEvent(int key);
|
||||
|
||||
/**
|
||||
* Sends multiple key press events to QWebEngineView.
|
||||
*
|
||||
* @tparam int Qt key code.
|
||||
*/
|
||||
template <int... T>
|
||||
void SendMultipleKeyPressEvents() {
|
||||
(SendKeyPressEvent(T), ...);
|
||||
}
|
||||
|
||||
void StartInputThread();
|
||||
void StopInputThread();
|
||||
|
||||
/// The thread where input is being polled and processed.
|
||||
void InputThread();
|
||||
|
||||
/// Loads the extracted fonts using JavaScript.
|
||||
void LoadExtractedFonts();
|
||||
|
||||
InputCommon::InputSubsystem* input_subsystem;
|
||||
|
||||
std::unique_ptr<UrlRequestInterceptor> url_interceptor;
|
||||
|
||||
std::unique_ptr<InputInterpreter> input_interpreter;
|
||||
|
||||
std::thread input_thread;
|
||||
|
||||
std::atomic<bool> input_thread_running{};
|
||||
|
||||
std::atomic<bool> finished{};
|
||||
|
||||
Service::AM::Applets::WebExitReason exit_reason{
|
||||
Service::AM::Applets::WebExitReason::EndButtonPressed};
|
||||
|
||||
std::string last_url{"http://localhost/"};
|
||||
|
||||
bool is_local{};
|
||||
|
||||
QWebEngineProfile* default_profile;
|
||||
QWebEngineSettings* global_settings;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
class QtWebBrowser final : public QObject, public Core::Frontend::WebBrowserApplet {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit QtWebBrowser(GMainWindow& parent);
|
||||
~QtWebBrowser() override;
|
||||
|
||||
void OpenLocalWebPage(const std::string& local_url,
|
||||
std::function<void()> extract_romfs_callback_,
|
||||
std::function<void(Service::AM::Applets::WebExitReason, std::string)>
|
||||
callback_) const override;
|
||||
|
||||
void OpenExternalWebPage(const std::string& external_url,
|
||||
std::function<void(Service::AM::Applets::WebExitReason, std::string)>
|
||||
callback_) const override;
|
||||
|
||||
signals:
|
||||
void MainWindowOpenWebPage(const std::string& main_url, const std::string& additional_args,
|
||||
bool is_local) const;
|
||||
|
||||
private:
|
||||
void MainWindowExtractOfflineRomFS();
|
||||
|
||||
void MainWindowWebBrowserClosed(Service::AM::Applets::WebExitReason exit_reason,
|
||||
std::string last_url);
|
||||
|
||||
mutable std::function<void()> extract_romfs_callback;
|
||||
|
||||
mutable std::function<void(Service::AM::Applets::WebExitReason, std::string)> callback;
|
||||
};
|
||||
@@ -1,193 +0,0 @@
|
||||
// Copyright 2020 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
constexpr char NX_FONT_CSS[] = R"(
|
||||
(function() {
|
||||
css = document.createElement('style');
|
||||
css.type = 'text/css';
|
||||
css.id = 'nx_font';
|
||||
css.innerText = `
|
||||
/* FontStandard */
|
||||
@font-face {
|
||||
font-family: 'FontStandard';
|
||||
src: url('%1') format('truetype');
|
||||
}
|
||||
|
||||
/* FontChineseSimplified */
|
||||
@font-face {
|
||||
font-family: 'FontChineseSimplified';
|
||||
src: url('%2') format('truetype');
|
||||
}
|
||||
|
||||
/* FontExtendedChineseSimplified */
|
||||
@font-face {
|
||||
font-family: 'FontExtendedChineseSimplified';
|
||||
src: url('%3') format('truetype');
|
||||
}
|
||||
|
||||
/* FontChineseTraditional */
|
||||
@font-face {
|
||||
font-family: 'FontChineseTraditional';
|
||||
src: url('%4') format('truetype');
|
||||
}
|
||||
|
||||
/* FontKorean */
|
||||
@font-face {
|
||||
font-family: 'FontKorean';
|
||||
src: url('%5') format('truetype');
|
||||
}
|
||||
|
||||
/* FontNintendoExtended */
|
||||
@font-face {
|
||||
font-family: 'NintendoExt003';
|
||||
src: url('%6') format('truetype');
|
||||
}
|
||||
|
||||
/* FontNintendoExtended2 */
|
||||
@font-face {
|
||||
font-family: 'NintendoExt003';
|
||||
src: url('%7') format('truetype');
|
||||
}
|
||||
`;
|
||||
|
||||
document.head.appendChild(css);
|
||||
})();
|
||||
)";
|
||||
|
||||
constexpr char LOAD_NX_FONT[] = R"(
|
||||
(function() {
|
||||
var elements = document.querySelectorAll("*");
|
||||
|
||||
for (var i = 0; i < elements.length; i++) {
|
||||
var style = window.getComputedStyle(elements[i], null);
|
||||
if (style.fontFamily.includes("Arial") || style.fontFamily.includes("Calibri") ||
|
||||
style.fontFamily.includes("Century") || style.fontFamily.includes("Times New Roman")) {
|
||||
elements[i].style.fontFamily = "FontStandard, FontChineseSimplified, FontExtendedChineseSimplified, FontChineseTraditional, FontKorean, NintendoExt003";
|
||||
} else {
|
||||
elements[i].style.fontFamily = style.fontFamily + ", FontStandard, FontChineseSimplified, FontExtendedChineseSimplified, FontChineseTraditional, FontKorean, NintendoExt003";
|
||||
}
|
||||
}
|
||||
})();
|
||||
)";
|
||||
|
||||
constexpr char GAMEPAD_SCRIPT[] = R"(
|
||||
window.addEventListener("gamepadconnected", function(e) {
|
||||
console.log("Gamepad connected at index %d: %s. %d buttons, %d axes.",
|
||||
e.gamepad.index, e.gamepad.id, e.gamepad.buttons.length, e.gamepad.axes.length);
|
||||
});
|
||||
|
||||
window.addEventListener("gamepaddisconnected", function(e) {
|
||||
console.log("Gamepad disconnected from index %d: %s", e.gamepad.index, e.gamepad.id);
|
||||
});
|
||||
)";
|
||||
|
||||
constexpr char WINDOW_NX_SCRIPT[] = R"(
|
||||
var end_applet = false;
|
||||
var yuzu_key_callbacks = [];
|
||||
|
||||
(function() {
|
||||
class WindowNX {
|
||||
constructor() {
|
||||
yuzu_key_callbacks[1] = function() { window.history.back(); };
|
||||
yuzu_key_callbacks[2] = function() { window.nx.endApplet(); };
|
||||
}
|
||||
|
||||
addEventListener(type, listener, options) {
|
||||
console.log("nx.addEventListener called, type=%s", type);
|
||||
|
||||
window.addEventListener(type, listener, options);
|
||||
}
|
||||
|
||||
endApplet() {
|
||||
console.log("nx.endApplet called");
|
||||
|
||||
end_applet = true;
|
||||
}
|
||||
|
||||
playSystemSe(system_se) {
|
||||
console.log("nx.playSystemSe is not implemented, system_se=%s", system_se);
|
||||
}
|
||||
|
||||
sendMessage(message) {
|
||||
console.log("nx.sendMessage is not implemented, message=%s", message);
|
||||
}
|
||||
|
||||
setCursorScrollSpeed(scroll_speed) {
|
||||
console.log("nx.setCursorScrollSpeed is not implemented, scroll_speed=%d", scroll_speed);
|
||||
}
|
||||
}
|
||||
|
||||
class WindowNXFooter {
|
||||
setAssign(key, label, func, option) {
|
||||
console.log("nx.footer.setAssign called, key=%s", key);
|
||||
|
||||
switch (key) {
|
||||
case "A":
|
||||
yuzu_key_callbacks[0] = func;
|
||||
break;
|
||||
case "B":
|
||||
yuzu_key_callbacks[1] = func;
|
||||
break;
|
||||
case "X":
|
||||
yuzu_key_callbacks[2] = func;
|
||||
break;
|
||||
case "Y":
|
||||
yuzu_key_callbacks[3] = func;
|
||||
break;
|
||||
case "L":
|
||||
yuzu_key_callbacks[6] = func;
|
||||
break;
|
||||
case "R":
|
||||
yuzu_key_callbacks[7] = func;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
setFixed(kind) {
|
||||
console.log("nx.footer.setFixed is not implemented, kind=%s", kind);
|
||||
}
|
||||
|
||||
unsetAssign(key) {
|
||||
console.log("nx.footer.unsetAssign called, key=%s", key);
|
||||
|
||||
switch (key) {
|
||||
case "A":
|
||||
yuzu_key_callbacks[0] = function() {};
|
||||
break;
|
||||
case "B":
|
||||
yuzu_key_callbacks[1] = function() {};
|
||||
break;
|
||||
case "X":
|
||||
yuzu_key_callbacks[2] = function() {};
|
||||
break;
|
||||
case "Y":
|
||||
yuzu_key_callbacks[3] = function() {};
|
||||
break;
|
||||
case "L":
|
||||
yuzu_key_callbacks[6] = function() {};
|
||||
break;
|
||||
case "R":
|
||||
yuzu_key_callbacks[7] = function() {};
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class WindowNXPlayReport {
|
||||
incrementCounter(counter_id) {
|
||||
console.log("nx.playReport.incrementCounter is not implemented, counter_id=%d", counter_id);
|
||||
}
|
||||
|
||||
setCounterSetIdentifier(counter_id) {
|
||||
console.log("nx.playReport.setCounterSetIdentifier is not implemented, counter_id=%d", counter_id);
|
||||
}
|
||||
}
|
||||
|
||||
window.nx = new WindowNX();
|
||||
window.nx.footer = new WindowNXFooter();
|
||||
window.nx.playReport = new WindowNXPlayReport();
|
||||
})();
|
||||
)";
|
||||
Reference in New Issue
Block a user