diff options
Diffstat (limited to 'src/yuzu/applets')
-rw-r--r-- | src/yuzu/applets/profile_select.cpp | 168 | ||||
-rw-r--r-- | src/yuzu/applets/profile_select.h | 73 | ||||
-rw-r--r-- | src/yuzu/applets/web_browser.cpp | 113 | ||||
-rw-r--r-- | src/yuzu/applets/web_browser.h | 52 |
4 files changed, 406 insertions, 0 deletions
diff --git a/src/yuzu/applets/profile_select.cpp b/src/yuzu/applets/profile_select.cpp new file mode 100644 index 000000000..5c1b65a2c --- /dev/null +++ b/src/yuzu/applets/profile_select.cpp @@ -0,0 +1,168 @@ +// Copyright 2018 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <mutex> +#include <QDialogButtonBox> +#include <QLabel> +#include <QLineEdit> +#include <QScrollArea> +#include <QStandardItemModel> +#include <QVBoxLayout> +#include "common/file_util.h" +#include "common/string_util.h" +#include "core/hle/lock.h" +#include "yuzu/applets/profile_select.h" +#include "yuzu/main.h" + +// Same backup JPEG used by acc IProfile::GetImage if no jpeg found +constexpr std::array<u8, 107> backup_jpeg{ + 0xff, 0xd8, 0xff, 0xdb, 0x00, 0x43, 0x00, 0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x03, 0x02, 0x02, + 0x02, 0x03, 0x03, 0x03, 0x03, 0x04, 0x06, 0x04, 0x04, 0x04, 0x04, 0x04, 0x08, 0x06, 0x06, 0x05, + 0x06, 0x09, 0x08, 0x0a, 0x0a, 0x09, 0x08, 0x09, 0x09, 0x0a, 0x0c, 0x0f, 0x0c, 0x0a, 0x0b, 0x0e, + 0x0b, 0x09, 0x09, 0x0d, 0x11, 0x0d, 0x0e, 0x0f, 0x10, 0x10, 0x11, 0x10, 0x0a, 0x0c, 0x12, 0x13, + 0x12, 0x10, 0x13, 0x0f, 0x10, 0x10, 0x10, 0xff, 0xc9, 0x00, 0x0b, 0x08, 0x00, 0x01, 0x00, 0x01, + 0x01, 0x01, 0x11, 0x00, 0xff, 0xcc, 0x00, 0x06, 0x00, 0x10, 0x10, 0x05, 0xff, 0xda, 0x00, 0x08, + 0x01, 0x01, 0x00, 0x00, 0x3f, 0x00, 0xd2, 0xcf, 0x20, 0xff, 0xd9, +}; + +QString FormatUserEntryText(const QString& username, Service::Account::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(Service::Account::UUID uuid) { + const auto path = FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + + "/system/save/8000000000000010/su/avators/" + uuid.FormatSwitch() + ".jpg"; + return QString::fromStdString(path); +} + +QPixmap GetIcon(Service::Account::UUID uuid) { + QPixmap icon{GetImagePath(uuid)}; + + if (!icon) { + icon.fill(Qt::black); + icon.loadFromData(backup_jpeg.data(), static_cast<u32>(backup_jpeg.size())); + } + + return icon.scaled(64, 64, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); +} + +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; + buttons->addButton(tr("Cancel"), QDialogButtonBox::RejectRole); + buttons->addButton(tr("OK"), QDialogButtonBox::AcceptRole); + + 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, "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 umbrells 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; + +void QtProfileSelectionDialog::accept() { + ok = true; + QDialog::accept(); +} + +void QtProfileSelectionDialog::reject() { + ok = false; + user_index = 0; + QDialog::reject(); +} + +bool QtProfileSelectionDialog::GetStatus() const { + return ok; +} + +u32 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<Service::Account::UUID>)> callback) const { + this->callback = std::move(callback); + emit MainWindowSelectProfile(); +} + +void QtProfileSelector::MainWindowFinishedSelection(std::optional<Service::Account::UUID> uuid) { + // Acquire the HLE mutex + std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock); + callback(uuid); +} diff --git a/src/yuzu/applets/profile_select.h b/src/yuzu/applets/profile_select.h new file mode 100644 index 000000000..868573324 --- /dev/null +++ b/src/yuzu/applets/profile_select.h @@ -0,0 +1,73 @@ +// 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 "core/frontend/applets/profile_select.h" + +class GMainWindow; +class QDialogButtonBox; +class QGraphicsScene; +class QLabel; +class QScrollArea; +class QStandardItem; +class QStandardItemModel; +class QTreeView; +class QVBoxLayout; + +class QtProfileSelectionDialog final : public QDialog { + Q_OBJECT + +public: + explicit QtProfileSelectionDialog(QWidget* parent); + ~QtProfileSelectionDialog() override; + + void accept() override; + void reject() override; + + bool GetStatus() const; + u32 GetIndex() const; + +private: + bool ok = false; + u32 user_index = 0; + + void SelectUser(const QModelIndex& index); + + 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<Service::Account::UUID>)> callback) const override; + +signals: + void MainWindowSelectProfile() const; + +private: + void MainWindowFinishedSelection(std::optional<Service::Account::UUID> uuid); + + mutable std::function<void(std::optional<Service::Account::UUID>)> callback; +}; diff --git a/src/yuzu/applets/web_browser.cpp b/src/yuzu/applets/web_browser.cpp new file mode 100644 index 000000000..6a9138d53 --- /dev/null +++ b/src/yuzu/applets/web_browser.cpp @@ -0,0 +1,113 @@ +// Copyright 2018 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <mutex> + +#include <QKeyEvent> + +#include "core/hle/lock.h" +#include "yuzu/applets/web_browser.h" +#include "yuzu/main.h" + +#ifdef YUZU_USE_QT_WEB_ENGINE + +constexpr char NX_SHIM_INJECT_SCRIPT[] = R"( + window.nx = {}; + window.nx.playReport = {}; + window.nx.playReport.setCounterSetIdentifier = function () { + console.log("nx.playReport.setCounterSetIdentifier called - unimplemented"); + }; + + window.nx.playReport.incrementCounter = function () { + console.log("nx.playReport.incrementCounter called - unimplemented"); + }; + + window.nx.footer = {}; + window.nx.footer.unsetAssign = function () { + console.log("nx.footer.unsetAssign called - unimplemented"); + }; + + var yuzu_key_callbacks = []; + window.nx.footer.setAssign = function(key, discard1, func, discard2) { + 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; + } + }; + + var applet_done = false; + window.nx.endApplet = function() { + applet_done = true; + }; +)"; + +QString GetNXShimInjectionScript() { + return QString::fromStdString(NX_SHIM_INJECT_SCRIPT); +} + +NXInputWebEngineView::NXInputWebEngineView(QWidget* parent) : QWebEngineView(parent) {} + +void NXInputWebEngineView::keyPressEvent(QKeyEvent* event) { + parent()->event(event); +} + +void NXInputWebEngineView::keyReleaseEvent(QKeyEvent* event) { + parent()->event(event); +} + +#endif + +QtWebBrowser::QtWebBrowser(GMainWindow& main_window) { + connect(this, &QtWebBrowser::MainWindowOpenPage, &main_window, &GMainWindow::WebBrowserOpenPage, + Qt::QueuedConnection); + connect(&main_window, &GMainWindow::WebBrowserUnpackRomFS, this, + &QtWebBrowser::MainWindowUnpackRomFS, Qt::QueuedConnection); + connect(&main_window, &GMainWindow::WebBrowserFinishedBrowsing, this, + &QtWebBrowser::MainWindowFinishedBrowsing, Qt::QueuedConnection); +} + +QtWebBrowser::~QtWebBrowser() = default; + +void QtWebBrowser::OpenPage(std::string_view url, std::function<void()> unpack_romfs_callback, + std::function<void()> finished_callback) { + this->unpack_romfs_callback = std::move(unpack_romfs_callback); + this->finished_callback = std::move(finished_callback); + + const auto index = url.find('?'); + if (index == std::string::npos) { + emit MainWindowOpenPage(url, ""); + } else { + const auto front = url.substr(0, index); + const auto back = url.substr(index); + emit MainWindowOpenPage(front, back); + } +} + +void QtWebBrowser::MainWindowUnpackRomFS() { + // Acquire the HLE mutex + std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock); + unpack_romfs_callback(); +} + +void QtWebBrowser::MainWindowFinishedBrowsing() { + // Acquire the HLE mutex + std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock); + finished_callback(); +} diff --git a/src/yuzu/applets/web_browser.h b/src/yuzu/applets/web_browser.h new file mode 100644 index 000000000..1a3d67353 --- /dev/null +++ b/src/yuzu/applets/web_browser.h @@ -0,0 +1,52 @@ +// Copyright 2018 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <functional> +#include <QObject> + +#ifdef YUZU_USE_QT_WEB_ENGINE +#include <QWebEngineView> +#endif + +#include "core/frontend/applets/web_browser.h" + +class GMainWindow; + +#ifdef YUZU_USE_QT_WEB_ENGINE + +QString GetNXShimInjectionScript(); + +class NXInputWebEngineView : public QWebEngineView { +public: + explicit NXInputWebEngineView(QWidget* parent = nullptr); + +protected: + void keyPressEvent(QKeyEvent* event) override; + void keyReleaseEvent(QKeyEvent* event) override; +}; + +#endif + +class QtWebBrowser final : public QObject, public Core::Frontend::WebBrowserApplet { + Q_OBJECT + +public: + explicit QtWebBrowser(GMainWindow& main_window); + ~QtWebBrowser() override; + + void OpenPage(std::string_view url, std::function<void()> unpack_romfs_callback, + std::function<void()> finished_callback) override; + +signals: + void MainWindowOpenPage(std::string_view filename, std::string_view additional_args) const; + +private: + void MainWindowUnpackRomFS(); + void MainWindowFinishedBrowsing(); + + std::function<void()> unpack_romfs_callback; + std::function<void()> finished_callback; +}; |