From 45da3be40edd71195b7aac633187fc81956e3150 Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Mon, 24 Dec 2018 16:23:31 -0500 Subject: [PATCH] main: Add main window integrations for QtWebBrowserApplet --- src/yuzu/CMakeLists.txt | 7 ++ src/yuzu/main.cpp | 151 ++++++++++++++++++++++++++++++++++++++++ src/yuzu/main.h | 10 +++ 3 files changed, 168 insertions(+) diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt index 17ecaafde6..c6378bce9a 100644 --- a/src/yuzu/CMakeLists.txt +++ b/src/yuzu/CMakeLists.txt @@ -11,6 +11,8 @@ add_executable(yuzu applets/profile_select.h applets/software_keyboard.cpp applets/software_keyboard.h + applets/web_browser.cpp + applets/web_browser.h bootmanager.cpp bootmanager.h compatibility_list.cpp @@ -154,6 +156,11 @@ if (USE_DISCORD_PRESENCE) target_compile_definitions(yuzu PRIVATE -DUSE_DISCORD_PRESENCE) endif() +if (YUZU_USE_QT_WEB_ENGINE) + target_link_libraries(yuzu PRIVATE Qt5::WebEngineCore Qt5::WebEngineWidgets) + target_compile_definitions(yuzu PRIVATE -DYUZU_USE_QT_WEB_ENGINE) +endif () + if(UNIX AND NOT APPLE) install(TARGETS yuzu RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}/bin") endif() diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 01a0f94ab6..18dc93c95b 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -10,11 +10,14 @@ // VFS includes must be before glad as they will conflict with Windows file api, which uses defines. #include "applets/profile_select.h" #include "applets/software_keyboard.h" +#include "applets/web_browser.h" #include "configuration/configure_per_general.h" #include "core/file_sys/vfs.h" #include "core/file_sys/vfs_real.h" #include "core/hle/service/acc/profile_manager.h" #include "core/hle/service/am/applets/applets.h" +#include "core/hle/service/hid/controllers/npad.h" +#include "core/hle/service/hid/hid.h" // These are wrappers to avoid the calls to CreateDirectory and CreateFile because of the Windows // defines. @@ -96,6 +99,14 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual #include "yuzu/discord_impl.h" #endif +#ifdef YUZU_USE_QT_WEB_ENGINE +#include +#include +#include +#include +#include +#endif + #ifdef QT_STATICPLUGIN Q_IMPORT_PLUGIN(QWindowsIntegrationPlugin); #endif @@ -252,6 +263,144 @@ void GMainWindow::SoftwareKeyboardInvokeCheckDialog(std::u16string error_message emit SoftwareKeyboardFinishedCheckDialog(); } +#ifdef YUZU_USE_QT_WEB_ENGINE + +void GMainWindow::WebBrowserOpenPage(std::string_view filename, std::string_view additional_args) { + NXInputWebEngineView web_browser_view(this); + + // Scope to contain the QProgressDialog for initalization + { + QProgressDialog progress(this); + progress.setMinimumDuration(200); + progress.setLabelText(tr("Loading Web Applet...")); + progress.setRange(0, 4); + progress.setValue(0); + progress.show(); + + auto future = QtConcurrent::run([this] { emit WebBrowserUnpackRomFS(); }); + + while (!future.isFinished()) + QApplication::processEvents(); + + progress.setValue(1); + + // Load the special shim script to handle input and exit. + QWebEngineScript nx_shim; + nx_shim.setSourceCode(GetNXShimInjectionScript()); + nx_shim.setWorldId(QWebEngineScript::MainWorld); + nx_shim.setName("nx_inject.js"); + nx_shim.setInjectionPoint(QWebEngineScript::DocumentCreation); + nx_shim.setRunsOnSubFrames(true); + web_browser_view.page()->profile()->scripts()->insert(nx_shim); + + web_browser_view.load( + QUrl(QUrl::fromLocalFile(QString::fromStdString(std::string(filename))).toString() + + QString::fromStdString(std::string(additional_args)))); + + progress.setValue(2); + + render_window->hide(); + web_browser_view.setFocus(); + + const auto& layout = render_window->GetFramebufferLayout(); + web_browser_view.resize(layout.screen.GetWidth(), layout.screen.GetHeight()); + web_browser_view.move(layout.screen.left, layout.screen.top + menuBar()->height()); + web_browser_view.setZoomFactor(static_cast(layout.screen.GetWidth()) / + Layout::ScreenUndocked::Width); + web_browser_view.settings()->setAttribute( + QWebEngineSettings::LocalContentCanAccessRemoteUrls, true); + + web_browser_view.show(); + + progress.setValue(3); + + QApplication::processEvents(); + + progress.setValue(4); + } + + bool finished = false; + QAction* exit_action = new QAction(tr("Exit Web Applet"), this); + connect(exit_action, &QAction::triggered, this, [&finished] { finished = true; }); + ui.menubar->addAction(exit_action); + + auto& npad = + Core::System::GetInstance() + .ServiceManager() + .GetService("hid") + ->GetAppletResource() + ->GetController(Service::HID::HidController::NPad); + + const auto fire_js_keypress = [&web_browser_view](u32 key_code) { + web_browser_view.page()->runJavaScript( + QStringLiteral("document.dispatchEvent(new KeyboardEvent('keydown', {'key': %1}));") + .arg(QString::fromStdString(std::to_string(key_code)))); + }; + + bool running_exit_check = false; + while (!finished) { + QApplication::processEvents(); + + if (!running_exit_check) { + web_browser_view.page()->runJavaScript(QStringLiteral("applet_done;"), + [&](const QVariant& res) { + running_exit_check = false; + if (res.toBool()) + finished = true; + }); + running_exit_check = true; + } + + const auto input = npad.GetPressState(); + for (std::size_t i = 0; i < Settings::NativeButton::NumButtons; ++i) { + if ((input & (1 << i)) != 0) { + LOG_DEBUG(Frontend, "firing input for button id={:02X}", i); + web_browser_view.page()->runJavaScript( + QStringLiteral("yuzu_key_callbacks[%1]();").arg(i)); + } + } + + if (input & 0x00888000) // RStick Down | LStick Down | DPad Down + fire_js_keypress(40); // Down Arrow Key + else if (input & 0x00444000) // RStick Right | LStick Right | DPad Right + fire_js_keypress(39); // Right Arrow Key + else if (input & 0x00222000) // RStick Up | LStick Up | DPad Up + fire_js_keypress(38); // Up Arrow Key + else if (input & 0x00111000) // RStick Left | LStick Left | DPad Left + fire_js_keypress(37); // Left Arrow Key + else if (input & 0x00000001) // A Button + fire_js_keypress(13); // Enter Key + } + + web_browser_view.hide(); + render_window->show(); + render_window->setFocus(); + ui.menubar->removeAction(exit_action); + + // Needed to update render window focus/show and remove menubar action + QApplication::processEvents(); + emit WebBrowserFinishedBrowsing(); +} + +#else + +void GMainWindow::WebBrowserOpenPage(std::string_view filename, std::string_view additional_args) { + QMessageBox::warning( + this, tr("Web Applet"), + tr("This version of yuzu was built without QtWebEngine support, meaning that yuzu cannot " + "properly display the game manual or web page requested."), + QMessageBox::Ok, QMessageBox::Ok); + + LOG_INFO(Frontend, + "(STUBBED) called - Missing QtWebEngine dependency needed to open website page at " + "'{}' with arguments '{}'!", + filename, additional_args); + + emit WebBrowserFinishedBrowsing(); +} + +#endif + void GMainWindow::InitializeWidgets() { #ifdef YUZU_ENABLE_COMPATIBILITY_REPORTING ui.action_Report_Compatibility->setVisible(true); @@ -612,6 +761,7 @@ bool GMainWindow::LoadROM(const QString& filename) { system.SetProfileSelector(std::make_unique(*this)); system.SetSoftwareKeyboard(std::make_unique(*this)); + system.SetWebBrowser(std::make_unique(*this)); const Core::System::ResultStatus result{system.Load(*render_window, filename.toStdString())}; @@ -1315,6 +1465,7 @@ void GMainWindow::OnStartGame() { qRegisterMetaType("Core::System::ResultStatus"); qRegisterMetaType("std::string"); qRegisterMetaType>("std::optional"); + qRegisterMetaType("std::string_view"); connect(emu_thread.get(), &EmuThread::ErrorThrown, this, &GMainWindow::OnCoreError); diff --git a/src/yuzu/main.h b/src/yuzu/main.h index 4e37f6a2d4..3af5fa1f37 100644 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h @@ -26,6 +26,7 @@ class GraphicsSurfaceWidget; class GRenderWindow; class MicroProfileDialog; class ProfilerWidget; +class QLabel; class WaitTreeWidget; enum class GameListOpenTarget; @@ -38,6 +39,10 @@ class RegisteredCacheUnion; class VfsFilesystem; } // namespace FileSys +namespace Service::Account { +struct UUID; +} // namespace Service::Account + namespace Tegra { class DebugContext; } @@ -103,11 +108,16 @@ signals: void SoftwareKeyboardFinishedText(std::optional text); void SoftwareKeyboardFinishedCheckDialog(); + void WebBrowserUnpackRomFS(); + void WebBrowserFinishedBrowsing(); + public slots: void ProfileSelectorSelectProfile(); void SoftwareKeyboardGetText(const Core::Frontend::SoftwareKeyboardParameters& parameters); void SoftwareKeyboardInvokeCheckDialog(std::u16string error_message); + void WebBrowserOpenPage(std::string_view filename, std::string_view arguments); + private: void InitializeWidgets(); void InitializeDebugWidgets();