diff --git a/CMakeModules/CopyYuzuQt5Deps.cmake b/CMakeModules/CopyYuzuQt5Deps.cmake index 4fef666594..1e9810bba2 100644 --- a/CMakeModules/CopyYuzuQt5Deps.cmake +++ b/CMakeModules/CopyYuzuQt5Deps.cmake @@ -45,5 +45,8 @@ function(copy_yuzu_Qt5_deps target_dir) windows_copy_files(yuzu ${Qt5_PLATFORMS_DIR} ${PLATFORMS} qwindows$<$:d>.*) windows_copy_files(yuzu ${Qt5_STYLES_DIR} ${STYLES} qwindowsvistastyle$<$:d>.*) - windows_copy_files(yuzu ${Qt5_IMAGEFORMATS_DIR} ${IMAGEFORMATS} qjpeg$<$:d>.*) + windows_copy_files(yuzu ${Qt5_IMAGEFORMATS_DIR} ${IMAGEFORMATS} + qjpeg$<$:d>.* + qgif$<$:d>.* + ) endfunction(copy_yuzu_Qt5_deps) diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt index 1f852df4bc..4cab599b4d 100644 --- a/src/yuzu/CMakeLists.txt +++ b/src/yuzu/CMakeLists.txt @@ -68,6 +68,8 @@ add_executable(yuzu game_list_p.h game_list_worker.cpp game_list_worker.h + loading_screen.cpp + loading_screen.h hotkeys.cpp hotkeys.h main.cpp @@ -102,9 +104,10 @@ set(UIS configuration/configure_system.ui configuration/configure_touchscreen_advanced.ui configuration/configure_web.ui - hotkeys.ui - main.ui compatdb.ui + hotkeys.ui + loading_screen.ui + main.ui ) file(GLOB COMPAT_LIST diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp index 40db7a5e92..f74cb693a4 100644 --- a/src/yuzu/bootmanager.cpp +++ b/src/yuzu/bootmanager.cpp @@ -3,9 +3,7 @@ #include #include #include - #include - #include "common/microprofile.h" #include "common/scm_rev.h" #include "core/core.h" @@ -17,6 +15,7 @@ #include "video_core/renderer_base.h" #include "video_core/video_core.h" #include "yuzu/bootmanager.h" +#include "yuzu/main.h" EmuThread::EmuThread(GRenderWindow* render_window) : render_window(render_window) {} @@ -114,6 +113,8 @@ GRenderWindow::GRenderWindow(QWidget* parent, EmuThread* emu_thread) InputCommon::Init(); InputCommon::StartJoystickEventHandler(); + connect(this, &GRenderWindow::FirstFrameDisplayed, static_cast(parent), + &GMainWindow::OnLoadComplete); } GRenderWindow::~GRenderWindow() { @@ -141,6 +142,10 @@ void GRenderWindow::SwapBuffers() { child->makeCurrent(); child->swapBuffers(); + if (!first_frame) { + emit FirstFrameDisplayed(); + first_frame = true; + } } void GRenderWindow::MakeCurrent() { @@ -309,6 +314,8 @@ void GRenderWindow::InitRenderTarget() { delete layout(); } + first_frame = false; + // TODO: One of these flags might be interesting: WA_OpaquePaintEvent, WA_NoBackground, // WA_DontShowOnScreen, WA_DeleteOnClose QGLFormat fmt; diff --git a/src/yuzu/bootmanager.h b/src/yuzu/bootmanager.h index 4e30282155..d1f37e5032 100644 --- a/src/yuzu/bootmanager.h +++ b/src/yuzu/bootmanager.h @@ -152,6 +152,7 @@ public slots: signals: /// Emitted when the window is closed void Closed(); + void FirstFrameDisplayed(); private: std::pair ScaleTouch(const QPointF pos) const; @@ -171,6 +172,8 @@ private: /// Temporary storage of the screenshot taken QImage screenshot_image; + bool first_frame = false; + protected: void showEvent(QShowEvent* event) override; }; diff --git a/src/yuzu/loading_screen.cpp b/src/yuzu/loading_screen.cpp new file mode 100644 index 0000000000..f2d3214f6f --- /dev/null +++ b/src/yuzu/loading_screen.cpp @@ -0,0 +1,71 @@ +// Copyright 2019 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "common/logging/log.h" +#include "core/loader/loader.h" +#include "ui_loading_screen.h" +#include "yuzu/loading_screen.h" + +LoadingScreen::LoadingScreen(QWidget* parent) + : QWidget(parent), ui(std::make_unique()) { + ui->setupUi(this); + // Progress bar is hidden until we have a use for it. + ui->progress_bar->hide(); +} + +LoadingScreen::~LoadingScreen() = default; + +void LoadingScreen::Prepare(Loader::AppLoader& loader) { + std::vector buffer; + if (loader.ReadBanner(buffer) == Loader::ResultStatus::Success) { + backing_mem = + std::make_unique(reinterpret_cast(buffer.data()), buffer.size()); + backing_buf = std::make_unique(backing_mem.get()); + backing_buf->open(QIODevice::ReadOnly); + animation = std::make_unique(backing_buf.get(), QByteArray("GIF")); + animation->start(); + ui->banner->setMovie(animation.get()); + buffer.clear(); + } + if (loader.ReadLogo(buffer) == Loader::ResultStatus::Success) { + QPixmap map; + map.loadFromData(buffer.data(), buffer.size()); + ui->logo->setPixmap(map); + } +} + +void LoadingScreen::OnLoadProgress(std::size_t value, std::size_t total) { + if (total != previous_total) { + ui->progress_bar->setMaximum(total); + previous_total = total; + } + ui->progress_bar->setValue(value); +} + +void LoadingScreen::paintEvent(QPaintEvent* event) { + QStyleOption opt; + opt.init(this); + QPainter p(this); + style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); + QWidget::paintEvent(event); +} + +void LoadingScreen::Clear() { + animation.reset(); + backing_buf.reset(); + backing_mem.reset(); +} diff --git a/src/yuzu/loading_screen.h b/src/yuzu/loading_screen.h new file mode 100644 index 0000000000..ffcaa260df --- /dev/null +++ b/src/yuzu/loading_screen.h @@ -0,0 +1,50 @@ +// Copyright 2019 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include +#include + +namespace Loader { +class AppLoader; +} + +namespace Ui { +class LoadingScreen; +} + +class QBuffer; +class QByteArray; +class QMovie; + +class LoadingScreen : public QWidget { + Q_OBJECT + +public: + explicit LoadingScreen(QWidget* parent = nullptr); + + ~LoadingScreen(); + + /// Call before showing the loading screen to load the widgets with the logo and banner for the + /// currently loaded application. + void Prepare(Loader::AppLoader& loader); + + /// After the loading screen is hidden, the owner of this class can call this to clean up any + /// used resources such as the logo and banner. + void Clear(); + + // In order to use a custom widget with a stylesheet, you need to override the paintEvent + // See https://wiki.qt.io/How_to_Change_the_Background_Color_of_QWidget + void paintEvent(QPaintEvent* event) override; + + void OnLoadProgress(std::size_t value, std::size_t total); + +private: + std::unique_ptr animation; + std::unique_ptr backing_buf; + std::unique_ptr backing_mem; + std::unique_ptr ui; + std::size_t previous_total = 0; +}; diff --git a/src/yuzu/loading_screen.ui b/src/yuzu/loading_screen.ui new file mode 100644 index 0000000000..00579b6709 --- /dev/null +++ b/src/yuzu/loading_screen.ui @@ -0,0 +1,79 @@ + + + LoadingScreen + + + + 0 + 0 + 746 + 495 + + + + background-color: rgb(0, 0, 0); + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + 30 + + + + + + + + + font-size: 26px; + + + 0 + + + Loading Shaders %v out of %m + + + + + + + + + background-color: black; + + + + + + 30 + + + + + + + + diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index f564de9948..68bfa23ab1 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -92,6 +92,7 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual #include "yuzu/game_list.h" #include "yuzu/game_list_p.h" #include "yuzu/hotkeys.h" +#include "yuzu/loading_screen.h" #include "yuzu/main.h" #include "yuzu/ui_settings.h" @@ -411,6 +412,10 @@ void GMainWindow::InitializeWidgets() { game_list = new GameList(vfs, this); ui.horizontalLayout->addWidget(game_list); + loading_screen = new LoadingScreen(this); + loading_screen->hide(); + ui.horizontalLayout->addWidget(loading_screen); + // Create status bar message_label = new QLabel(); // Configured separately for left alignment @@ -897,8 +902,9 @@ void GMainWindow::BootGame(const QString& filename) { .arg(Common::g_build_fullname, Common::g_scm_branch, Common::g_scm_desc, QString::fromStdString(title_name))); - render_window->show(); - render_window->setFocus(); + loading_screen->Prepare(Core::System::GetInstance().GetAppLoader()); + loading_screen->show(); + loading_screen->setFocus(); emulation_running = true; if (ui.action_Fullscreen->isChecked()) { @@ -932,6 +938,8 @@ void GMainWindow::ShutdownGame() { ui.action_Load_Amiibo->setEnabled(false); ui.action_Capture_Screenshot->setEnabled(false); render_window->hide(); + loading_screen->hide(); + loading_screen->Clear(); game_list->show(); game_list->setFilterFocus(); setWindowTitle(QString("yuzu %1| %2-%3") @@ -1505,6 +1513,13 @@ void GMainWindow::OnStopGame() { ShutdownGame(); } +void GMainWindow::OnLoadComplete() { + loading_screen->hide(); + loading_screen->Clear(); + render_window->show(); + render_window->setFocus(); +} + void GMainWindow::OnMenuReportCompatibility() { if (!Settings::values.yuzu_token.empty() && !Settings::values.yuzu_username.empty()) { CompatDB compatdb{this}; @@ -1771,9 +1786,8 @@ void GMainWindow::OnReinitializeKeys(ReinitializeKeyBehavior behavior) { this, tr("Confirm Key Rederivation"), tr("You are about to force rederive all of your keys. \nIf you do not know what this " "means or what you are doing, \nthis is a potentially destructive action. \nPlease " - "make " - "sure this is what you want \nand optionally make backups.\n\nThis will delete your " - "autogenerated key files and re-run the key derivation module."), + "make sure this is what you want \nand optionally make backups.\n\nThis will delete " + "your autogenerated key files and re-run the key derivation module."), QMessageBox::StandardButtons{QMessageBox::Ok, QMessageBox::Cancel}); if (res == QMessageBox::Cancel) @@ -1818,7 +1832,7 @@ void GMainWindow::OnReinitializeKeys(ReinitializeKeyBehavior behavior) { errors + tr("

You can get all of these and dump all of your games easily by " "following the " - "quickstart guide. Alternatively, you can use another method of dumping " + "quickstart guide. Alternatively, you can use another method of dumping" "to obtain all of your keys.")); } diff --git a/src/yuzu/main.h b/src/yuzu/main.h index 2d705ad54d..e07c892cf2 100644 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h @@ -25,6 +25,7 @@ class GImageInfo; class GraphicsBreakPointsWidget; class GraphicsSurfaceWidget; class GRenderWindow; +class LoadingScreen; class MicroProfileDialog; class ProfilerWidget; class QLabel; @@ -109,10 +110,10 @@ signals: void WebBrowserFinishedBrowsing(); public slots: + void OnLoadComplete(); 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: @@ -212,6 +213,7 @@ private: GRenderWindow* render_window; GameList* game_list; + LoadingScreen* loading_screen; // Status bar elements QLabel* message_label = nullptr;