diff --git a/rpcs3/main.cpp b/rpcs3/main.cpp index 501a76a3ac..754c993c5b 100644 --- a/rpcs3/main.cpp +++ b/rpcs3/main.cpp @@ -1,4 +1,4 @@ -// Qt5.2+ frontend implementation for rpcs3. Known to work on Windows, Linux, Mac +// Qt5.10+ frontend implementation for rpcs3. Known to work on Windows, Linux, Mac // by Sacha Refshauge, Megamouse and flash-fire #include @@ -6,11 +6,18 @@ #include #include #include +#include +#include + +#include "rpcs3qt/gui_application.h" #include "rpcs3_app.h" #include "Utilities/sema.h" #ifdef _WIN32 #include +#include "Utilities/dynamic_library.h" +DYNAMIC_IMPORT("ntdll.dll", NtQueryTimerResolution, NTSTATUS(PULONG MinimumResolution, PULONG MaximumResolution, PULONG CurrentResolution)); +DYNAMIC_IMPORT("ntdll.dll", NtSetTimerResolution, NTSTATUS(ULONG DesiredResolution, BOOLEAN SetResolution, PULONG CurrentResolution)); #endif #ifdef __linux__ @@ -90,16 +97,20 @@ static semaphore<> s_qt_mutex{}; std::abort(); } +const char* ARG_NO_GUI = "no-gui"; + +QCoreApplication* createApplication(int& argc, char* argv[]) +{ + for (int i = 1; i < argc; ++i) + if (!qstrcmp(argv[i], ARG_NO_GUI)) + return new rpcs3_app(argc, argv); + return new gui_application(argc, argv); +} + int main(int argc, char** argv) { logs::set_init(); -#if defined(_WIN32) || defined(__APPLE__) - QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); -#else - setenv("QT_AUTO_SCREEN_SCALE_FACTOR", "1", 0); -#endif - #ifdef __linux__ struct ::rlimit rlim; rlim.rlim_cur = 4096; @@ -108,16 +119,10 @@ int main(int argc, char** argv) std::fprintf(stderr, "Failed to set max open file limit (4096)."); #endif - QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); - QCoreApplication::setAttribute(Qt::AA_DisableWindowContextHelpButton); - QCoreApplication::setAttribute(Qt::AA_DontCheckOpenGLContextThreadAffinity); - s_init.unlock(); s_qt_mutex.lock(); - rpcs3_app app(argc, argv); - QCoreApplication::setApplicationVersion(qstr(rpcs3::version.to_string())); - QCoreApplication::setApplicationName("RPCS3"); + QScopedPointer app(createApplication(argc, argv)); // Command line args QCommandLineParser parser; @@ -125,16 +130,46 @@ int main(int argc, char** argv) parser.addPositionalArgument("(S)ELF", "Path for directly executing a (S)ELF"); parser.addPositionalArgument("[Args...]", "Optional args for the executable"); - const QCommandLineOption helpOption = parser.addHelpOption(); + const QCommandLineOption helpOption = parser.addHelpOption(); const QCommandLineOption versionOption = parser.addVersionOption(); - parser.parse(QCoreApplication::arguments()); - parser.process(app); + parser.addOption(QCommandLineOption("no-gui")); + parser.process(app->arguments()); // Don't start up the full rpcs3 gui if we just want the version or help. if (parser.isSet(versionOption) || parser.isSet(helpOption)) return 0; - app.Init(); + app->setApplicationVersion(qstr(rpcs3::version.to_string())); + app->setApplicationName("RPCS3"); + + if (auto gui_app = qobject_cast(app.get())) + { +#if defined(_WIN32) || defined(__APPLE__) + app->setAttribute(Qt::AA_EnableHighDpiScaling); +#else + setenv("QT_AUTO_SCREEN_SCALE_FACTOR", "1", 0); +#endif + app->setAttribute(Qt::AA_UseHighDpiPixmaps); + app->setAttribute(Qt::AA_DisableWindowContextHelpButton); + app->setAttribute(Qt::AA_DontCheckOpenGLContextThreadAffinity); + + gui_app->Init(); + } + else if (auto non_gui_app = qobject_cast(app.get())) + { + non_gui_app->Init(); + } + +#ifdef _WIN32 + // Set 0.5 msec timer resolution for best performance + // - As QT5 timers (QTimer) sets the timer resolution to 1 msec, override it here. + // - Don't bother "unsetting" the timer resolution after the emulator stops as QT5 will still require the timer resolution to be set to 1 msec. + ULONG min_res, max_res, orig_res, new_res; + if (NtQueryTimerResolution(&min_res, &max_res, &orig_res) == 0) + { + NtSetTimerResolution(max_res, TRUE, &new_res); + } +#endif QStringList args = parser.positionalArguments(); @@ -145,9 +180,11 @@ int main(int argc, char** argv) if (args.length() > 1) { + args.removeAll(ARG_NO_GUI); + argv.emplace_back(); - for (int i = 1; i < args.length(); i++) + for (u32 i = 1; i < args.length(); i++) { argv.emplace_back(args[i].toStdString()); } @@ -164,5 +201,7 @@ int main(int argc, char** argv) s_qt_init.unlock(); s_qt_mutex.unlock(); - return QCoreApplication::exec(); + + // run event loop (maybe only needed for the gui application) + return app->exec(); } diff --git a/rpcs3/main_application.cpp b/rpcs3/main_application.cpp new file mode 100644 index 0000000000..3c752f760d --- /dev/null +++ b/rpcs3/main_application.cpp @@ -0,0 +1,137 @@ +#include "main_application.h" + +#include "Emu/Io/Null/NullPadHandler.h" +#include "Emu/Io/Null/NullKeyboardHandler.h" +#include "Emu/Io/Null/NullMouseHandler.h" +#include "Emu/Io/KeyboardHandler.h" +#include "Emu/Io/PadHandler.h" +#include "Emu/Io/MouseHandler.h" +#include "basic_keyboard_handler.h" +#include "basic_mouse_handler.h" +#include "keyboard_pad_handler.h" +#include "ds4_pad_handler.h" +#ifdef _WIN32 +#include "xinput_pad_handler.h" +#include "mm_joystick_handler.h" +#endif +#ifdef HAVE_LIBEVDEV +#include "evdev_joystick_handler.h" +#endif + +#include "Emu/Audio/AudioBackend.h" +#include "Emu/Audio/Null/NullAudioBackend.h" +#include "Emu/Audio/AL/OpenALBackend.h" +#ifdef _WIN32 +#include "Emu/Audio/XAudio2/XAudio2Backend.h" +#endif +#ifdef HAVE_ALSA +#include "Emu/Audio/ALSA/ALSABackend.h" +#endif +#ifdef HAVE_PULSE +#include "Emu/Audio/Pulse/PulseBackend.h" +#endif + +/** Emu.Init() wrapper for user manager */ +bool main_application::InitializeEmulator(const std::string& user, bool force_init) +{ + // try to set a new user + const bool user_was_set = Emu.SetUsr(user); + + // only init the emulation if forced or a user was set + if (user_was_set || force_init) + { + Emu.Init(); + } + + return user_was_set; +} + +/** RPCS3 emulator has functions it desires to call from the GUI at times. Initialize them in here. */ +EmuCallbacks main_application::CreateCallbacks() +{ + EmuCallbacks callbacks; + + callbacks.reset_pads = [this](const std::string& title_id = "") + { + pad::get_current_handler()->Reset(title_id); + }; + callbacks.enable_pads = [this](bool enable) + { + pad::get_current_handler()->SetEnabled(enable); + }; + + callbacks.get_kb_handler = [=]() -> std::shared_ptr + { + switch (keyboard_handler type = g_cfg.io.keyboard) + { + case keyboard_handler::null: return std::make_shared(); + case keyboard_handler::basic: + { + basic_keyboard_handler* ret = new basic_keyboard_handler(); + ret->moveToThread(get_thread()); + ret->SetTargetWindow(m_game_window); + return std::shared_ptr(ret); + } + default: fmt::throw_exception("Invalid keyboard handler: %s", type); + } + }; + + callbacks.get_mouse_handler = [=]() -> std::shared_ptr + { + switch (mouse_handler type = g_cfg.io.mouse) + { + case mouse_handler::null: return std::make_shared(); + case mouse_handler::basic: + { + basic_mouse_handler* ret = new basic_mouse_handler(); + ret->moveToThread(get_thread()); + ret->SetTargetWindow(m_game_window); + return std::shared_ptr(ret); + } + default: fmt::throw_exception("Invalid mouse handler: %s", type); + } + }; + + callbacks.get_pad_handler = [this](const std::string& title_id) -> std::shared_ptr + { + return std::make_shared(get_thread(), m_game_window, title_id); + }; + + callbacks.get_gs_render = []() -> std::shared_ptr + { + switch (video_renderer type = g_cfg.video.renderer) + { + case video_renderer::null: return std::make_shared>("rsx::thread"); + case video_renderer::opengl: return std::make_shared>("rsx::thread"); +#if defined(_WIN32) || defined(HAVE_VULKAN) + case video_renderer::vulkan: return std::make_shared>("rsx::thread"); +#endif +#ifdef _MSC_VER + case video_renderer::dx12: return std::make_shared>("rsx::thread"); +#endif + default: fmt::throw_exception("Invalid video renderer: %s" HERE, type); + } + }; + + callbacks.get_audio = []() -> std::shared_ptr + { + switch (audio_renderer type = g_cfg.audio.renderer) + { + case audio_renderer::null: return std::make_shared(); +#ifdef _WIN32 + case audio_renderer::xaudio: return std::make_shared(); +#endif +#ifdef HAVE_ALSA + case audio_renderer::alsa: return std::make_shared(); +#endif +#ifdef HAVE_PULSE + case audio_renderer::pulse: return std::make_shared(); +#endif + + case audio_renderer::openal: return std::make_shared(); + default: fmt::throw_exception("Invalid audio renderer: %s" HERE, type); + } + }; + + return callbacks; +} diff --git a/rpcs3/main_application.h b/rpcs3/main_application.h new file mode 100644 index 0000000000..84104cd972 --- /dev/null +++ b/rpcs3/main_application.h @@ -0,0 +1,32 @@ +#pragma once + +#include "stdafx.h" + +#include "Emu/System.h" + +#include "Emu/RSX/GSRender.h" +#include "Emu/RSX/Null/NullGSRender.h" +#include "Emu/RSX/GL/GLGSRender.h" +#ifdef _MSC_VER +#include "Emu/RSX/D3D12/D3D12GSRender.h" +#endif +#if defined(_WIN32) || defined(HAVE_VULKAN) +#include "Emu/RSX/VK/VKGSRender.h" +#endif + +#include + +class main_application +{ +public: + virtual void Init() = 0; + + static bool InitializeEmulator(const std::string& user, bool force_init); + +protected: + virtual QThread* get_thread() = 0; + + EmuCallbacks CreateCallbacks(); + + QWindow* m_game_window = nullptr; // (Currently) only needed so that pad handlers have a valid target for event filtering. +}; diff --git a/rpcs3/rpcs3.vcxproj b/rpcs3/rpcs3.vcxproj index ac896e0a47..38f67769df 100644 --- a/rpcs3/rpcs3.vcxproj +++ b/rpcs3/rpcs3.vcxproj @@ -340,6 +340,7 @@ + @@ -412,6 +413,11 @@ true true + + true + true + true + true true @@ -592,6 +598,11 @@ true true + + true + true + true + true true @@ -792,6 +803,11 @@ true true + + true + true + true + true true @@ -972,6 +988,11 @@ true true + + true + true + true + true true @@ -1087,6 +1108,7 @@ + @@ -1335,6 +1357,32 @@ $(QTDIR)\bin\moc.exe;%(FullPath);$(QTDIR)\bin\moc.exe;%(FullPath) $(QTDIR)\bin\moc.exe;%(FullPath);$(QTDIR)\bin\moc.exe;%(FullPath) + + + + + + + + + + + + + + + + + + + + + + + + + + Moc%27ing cg_disasm_window.h... @@ -1585,6 +1633,24 @@ .\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -D_WINDOWS -DUNICODE -DWIN32 -DWIN64 -DQT_OPENGL_LIB -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_QML_LIB -DQT_NETWORK_LIB -DQT_CORE_LIB -DQT_WINEXTRAS_LIB -D%(PreprocessorDefinitions) "-I$(VULKAN_SDK)\Include" "-I.\.." "-I.\..\3rdparty\minidx12\Include" "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtOpenGL" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtANGLE" "-I$(QTDIR)\include\QtQml" "-I$(QTDIR)\include\QtNetwork" "-I$(QTDIR)\include\QtCore" "-I.\debug" "-I$(QTDIR)\mkspecs\win32-msvc2015" "-I.\QTGeneratedFiles\$(ConfigurationName)" "-I.\QTGeneratedFiles" "-I$(QTDIR)\include\QtWinExtras" + + $(QTDIR)\bin\moc.exe;%(FullPath) + Moc%27ing %(Identity)... + .\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp + "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -D_WINDOWS -DUNICODE -DWIN32 -DWIN64 -DWITH_DISCORD_RPC -DQT_NO_DEBUG -DQT_OPENGL_LIB -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_QML_LIB -DQT_NETWORK_LIB -DQT_CORE_LIB -DNDEBUG -DQT_WINEXTRAS_LIB "-DBRANCH=$(BRANCH)" -D%(PreprocessorDefinitions) "-I.\..\3rdparty\libusb\libusb" "-I$(VULKAN_SDK)\Include" "-I.\..\3rdparty\minidx12\Include" "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtOpenGL" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtANGLE" "-I$(QTDIR)\include\QtQml" "-I$(QTDIR)\include\QtNetwork" "-I$(QTDIR)\include\QtCore" "-I.\release" "-I$(QTDIR)\mkspecs\win32-msvc2015" "-I.\QTGeneratedFiles\$(ConfigurationName)" "-I.\QTGeneratedFiles" "-I$(QTDIR)\include\QtWinExtras" + $(QTDIR)\bin\moc.exe;%(FullPath) + Moc%27ing %(Identity)... + .\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp + "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -D_WINDOWS -DUNICODE -DWIN32 -DWIN64 -DQT_OPENGL_LIB -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_QML_LIB -DQT_NETWORK_LIB -DQT_CORE_LIB -DQT_WINEXTRAS_LIB -D%(PreprocessorDefinitions) "-I$(VULKAN_SDK)\Include" "-I.\.." "-I.\..\3rdparty\minidx12\Include" "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtOpenGL" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtANGLE" "-I$(QTDIR)\include\QtQml" "-I$(QTDIR)\include\QtNetwork" "-I$(QTDIR)\include\QtCore" "-I.\debug" "-I$(QTDIR)\mkspecs\win32-msvc2015" "-I.\QTGeneratedFiles\$(ConfigurationName)" "-I.\QTGeneratedFiles" "-I$(QTDIR)\include\QtWinExtras" + $(QTDIR)\bin\moc.exe;%(FullPath) + Moc%27ing %(Identity)... + .\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp + "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -D_WINDOWS -DUNICODE -DWIN32 -DWIN64 -DWITH_DISCORD_RPC -DQT_NO_DEBUG -DQT_OPENGL_LIB -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_QML_LIB -DQT_NETWORK_LIB -DQT_CORE_LIB -DNDEBUG -DQT_WINEXTRAS_LIB -D%(PreprocessorDefinitions) "-I$(VULKAN_SDK)\Include" "-I.\..\3rdparty\minidx12\Include" "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtOpenGL" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtANGLE" "-I$(QTDIR)\include\QtQml" "-I$(QTDIR)\include\QtNetwork" "-I$(QTDIR)\include\QtCore" "-I.\release" "-I$(QTDIR)\mkspecs\win32-msvc2015" "-I.\QTGeneratedFiles\$(ConfigurationName)" "-I.\QTGeneratedFiles" "-I$(QTDIR)\include\QtWinExtras" + $(QTDIR)\bin\moc.exe;%(FullPath) + Moc%27ing %(Identity)... + .\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp + "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -D_WINDOWS -DUNICODE -DWIN32 -DWIN64 -DQT_OPENGL_LIB -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_QML_LIB -DQT_NETWORK_LIB -DQT_CORE_LIB -DQT_WINEXTRAS_LIB -D%(PreprocessorDefinitions) "-I$(VULKAN_SDK)\Include" "-I.\.." "-I.\..\3rdparty\minidx12\Include" "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtOpenGL" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtANGLE" "-I$(QTDIR)\include\QtQml" "-I$(QTDIR)\include\QtNetwork" "-I$(QTDIR)\include\QtCore" "-I.\debug" "-I$(QTDIR)\mkspecs\win32-msvc2015" "-I.\QTGeneratedFiles\$(ConfigurationName)" "-I.\QTGeneratedFiles" "-I$(QTDIR)\include\QtWinExtras" + diff --git a/rpcs3/rpcs3.vcxproj.filters b/rpcs3/rpcs3.vcxproj.filters index 52c7d52255..6f5b0b3eda 100644 --- a/rpcs3/rpcs3.vcxproj.filters +++ b/rpcs3/rpcs3.vcxproj.filters @@ -725,6 +725,24 @@ Io\DS3 + + Gui + + + Generated Files\Release - LLVM + + + Generated Files\Debug + + + Generated Files\Release + + + Generated Files\Debug - LLVM + + + rpcs3 + @@ -957,6 +975,12 @@ Gui\message dialog + + Gui + + + rpcs3 + diff --git a/rpcs3/rpcs3_app.cpp b/rpcs3/rpcs3_app.cpp index 661c00ffe6..f83b78d16d 100644 --- a/rpcs3/rpcs3_app.cpp +++ b/rpcs3/rpcs3_app.cpp @@ -1,137 +1,34 @@ #include "rpcs3_app.h" -#include "rpcs3qt/qt_utils.h" - -#include "rpcs3qt/welcome_dialog.h" - -#ifdef WITH_DISCORD_RPC -#include "rpcs3qt/_discord_utils.h" -#endif - #include "Emu/System.h" -#include "rpcs3qt/gs_frame.h" -#include "rpcs3qt/gl_gs_frame.h" - -#include "rpcs3qt/trophy_notification_helper.h" -#include "rpcs3qt/save_data_dialog.h" - -#include "Emu/Io/Null/NullKeyboardHandler.h" -#include "basic_keyboard_handler.h" - -#include "Emu/Io/Null/NullMouseHandler.h" -#include "basic_mouse_handler.h" - -#include "Emu/Io/Null/NullPadHandler.h" -#include "keyboard_pad_handler.h" -#include "ds4_pad_handler.h" -#ifdef _WIN32 -#include "xinput_pad_handler.h" -#include "mm_joystick_handler.h" -#endif -#ifdef HAVE_LIBEVDEV -#include "evdev_joystick_handler.h" -#endif - -#include "pad_thread.h" - -#include "Emu/RSX/Null/NullGSRender.h" -#include "Emu/RSX/GL/GLGSRender.h" -#include "Emu/Audio/Null/NullAudioBackend.h" -#include "Emu/Audio/AL/OpenALBackend.h" -#ifdef _MSC_VER -#include "Emu/RSX/D3D12/D3D12GSRender.h" -#endif -#if defined(_WIN32) || defined(HAVE_VULKAN) -#include "Emu/RSX/VK/VKGSRender.h" -#endif -#ifdef _WIN32 -#include "Emu/Audio/XAudio2/XAudio2Backend.h" -#endif -#ifdef HAVE_ALSA -#include "Emu/Audio/ALSA/ALSABackend.h" -#endif -#ifdef HAVE_PULSE -#include "Emu/Audio/Pulse/PulseBackend.h" -#endif - -#ifdef _WIN32 -#include "Utilities/dynamic_library.h" -DYNAMIC_IMPORT("ntdll.dll", NtQueryTimerResolution, NTSTATUS(PULONG MinimumResolution, PULONG MaximumResolution, PULONG CurrentResolution)); -DYNAMIC_IMPORT("ntdll.dll", NtSetTimerResolution, NTSTATUS(ULONG DesiredResolution, BOOLEAN SetResolution, PULONG CurrentResolution)); -#endif // For now, a trivial constructor/destructor. May add command line usage later. -rpcs3_app::rpcs3_app(int& argc, char** argv) : QApplication(argc, argv) +rpcs3_app::rpcs3_app(int& argc, char** argv) : QCoreApplication(argc, argv) { } void rpcs3_app::Init() { - setApplicationName("RPCS3"); - setWindowIcon(QIcon(":/rpcs3.ico")); - - guiSettings.reset(new gui_settings()); - emuSettings.reset(new emu_settings()); - // Force init the emulator - InitializeEmulator(guiSettings->GetCurrentUser().toStdString(), true); - - // Create the main window - RPCS3MainWin = new main_window(guiSettings, emuSettings, nullptr); + InitializeEmulator("1", true); // TODO: get user from cli args if possible // Create callbacks from the emulator, which reference the handlers. InitializeCallbacks(); // Create connects to propagate events throughout Gui. InitializeConnects(); - - RPCS3MainWin->Init(); - - if (guiSettings->GetValue(gui::ib_show_welcome).toBool()) - { - welcome_dialog* welcome = new welcome_dialog(); - welcome->exec(); - } -#ifdef WITH_DISCORD_RPC - // Discord Rich Presence Integration - if (guiSettings->GetValue(gui::m_richPresence).toBool()) - { - discord::initialize(); - } -#endif - -#ifdef _WIN32 - // Set 0.5 msec timer resolution for best performance - // - As QT5 timers (QTimer) sets the timer resolution to 1 msec, override it here. - // - Don't bother "unsetting" the timer resolution after the emulator stops as QT5 will still require the timer resolution to be set to 1 msec. - ULONG min_res, max_res, orig_res, new_res; - if (NtQueryTimerResolution(&min_res, &max_res, &orig_res) == 0) - { - NtSetTimerResolution(max_res, TRUE, &new_res); - } -#endif } -/** Emu.Init() wrapper for user manager */ -bool rpcs3_app::InitializeEmulator(const std::string& user, bool force_init) +void rpcs3_app::InitializeConnects() { - // try to set a new user - const bool user_was_set = Emu.SetUsr(user); - - // only init the emulation if forced or a user was set - if (user_was_set || force_init) - { - Emu.Init(); - } - - return user_was_set; + qRegisterMetaType>("std::function"); + connect(this, &rpcs3_app::RequestCallAfter, this, &rpcs3_app::HandleCallAfter); } -/** RPCS3 emulator has functions it desires to call from the GUI at times. Initialize them in here. -*/ +/** RPCS3 emulator has functions it desires to call from the GUI at times. Initialize them in here. */ void rpcs3_app::InitializeCallbacks() { - EmuCallbacks callbacks; + EmuCallbacks callbacks = CreateCallbacks(); callbacks.exit = [this]() { @@ -142,358 +39,24 @@ void rpcs3_app::InitializeCallbacks() RequestCallAfter(std::move(func)); }; - callbacks.reset_pads = [this](const std::string& title_id = "") - { - pad::get_current_handler()->Reset(title_id); - }; - callbacks.enable_pads = [this](bool enable) - { - pad::get_current_handler()->SetEnabled(enable); - }; + callbacks.get_gs_frame = []() -> std::unique_ptr { return std::unique_ptr(); }; + callbacks.get_msg_dialog = []() -> std::shared_ptr { return std::shared_ptr(); }; + callbacks.get_osk_dialog = []() -> std::shared_ptr { return std::shared_ptr(); }; + callbacks.get_save_dialog = []() -> std::unique_ptr { return std::unique_ptr(); }; + callbacks.get_trophy_notification_dialog = []() -> std::unique_ptr { return std::unique_ptr(); }; - callbacks.get_kb_handler = [=]() -> std::shared_ptr - { - switch (keyboard_handler type = g_cfg.io.keyboard) - { - case keyboard_handler::null: return std::make_shared(); - case keyboard_handler::basic: - { - basic_keyboard_handler* ret = new basic_keyboard_handler(); - ret->moveToThread(thread()); - ret->SetTargetWindow(gameWindow); - return std::shared_ptr(ret); - } - default: fmt::throw_exception("Invalid keyboard handler: %s", type); - } - }; - - callbacks.get_mouse_handler = [=]() -> std::shared_ptr - { - switch (mouse_handler type = g_cfg.io.mouse) - { - case mouse_handler::null: return std::make_shared(); - case mouse_handler::basic: - { - basic_mouse_handler* ret = new basic_mouse_handler(); - ret->moveToThread(thread()); - ret->SetTargetWindow(gameWindow); - return std::shared_ptr(ret); - } - default: fmt::throw_exception("Invalid mouse handler: %s", type); - } - }; - - callbacks.get_pad_handler = [this](const std::string& title_id) -> std::shared_ptr - { - return std::make_shared(thread(), gameWindow, title_id); - }; - - callbacks.get_gs_frame = [this]() -> std::unique_ptr - { - extern const std::unordered_map, value_hash> g_video_out_resolution_map; - - const auto size = g_video_out_resolution_map.at(g_cfg.video.resolution); - int w = size.first; - int h = size.second; - - if (guiSettings->GetValue(gui::gs_resize).toBool()) - { - w = guiSettings->GetValue(gui::gs_width).toInt(); - h = guiSettings->GetValue(gui::gs_height).toInt(); - } - - auto frame_geometry = gui::utils::create_centered_window_geometry(RPCS3MainWin->geometry(), w, h); - - gs_frame* frame; - - switch (video_renderer type = g_cfg.video.renderer) - { - case video_renderer::null: - { - frame = new gs_frame("Null", frame_geometry, RPCS3MainWin->GetAppIcon(), guiSettings); - break; - } - case video_renderer::opengl: - { - frame = new gl_gs_frame(frame_geometry, RPCS3MainWin->GetAppIcon(), guiSettings); - break; - } - case video_renderer::vulkan: - { - frame = new gs_frame("Vulkan", frame_geometry, RPCS3MainWin->GetAppIcon(), guiSettings); - break; - } -#ifdef _MSC_VER - case video_renderer::dx12: - { - frame = new gs_frame("DirectX 12", frame_geometry, RPCS3MainWin->GetAppIcon(), guiSettings); - break; - } -#endif - default: - fmt::throw_exception("Invalid video renderer: %s" HERE, type); - } - - gameWindow = frame; - return std::unique_ptr(frame); - }; - - callbacks.get_gs_render = []() -> std::shared_ptr - { - switch (video_renderer type = g_cfg.video.renderer) - { - case video_renderer::null: return std::make_shared>("rsx::thread"); - case video_renderer::opengl: return std::make_shared>("rsx::thread"); -#if defined(_WIN32) || defined(HAVE_VULKAN) - case video_renderer::vulkan: return std::make_shared>("rsx::thread"); -#endif -#ifdef _MSC_VER - case video_renderer::dx12: return std::make_shared>("rsx::thread"); -#endif - default: fmt::throw_exception("Invalid video renderer: %s" HERE, type); - } - }; - - callbacks.get_audio = []() -> std::shared_ptr - { - switch (audio_renderer type = g_cfg.audio.renderer) - { - case audio_renderer::null: return std::make_shared(); -#ifdef _WIN32 - case audio_renderer::xaudio: return std::make_shared(); -#endif -#ifdef HAVE_ALSA - case audio_renderer::alsa: return std::make_shared(); -#endif -#ifdef HAVE_PULSE - case audio_renderer::pulse: return std::make_shared(); -#endif - - case audio_renderer::openal: return std::make_shared(); - default: fmt::throw_exception("Invalid audio renderer: %s" HERE, type); - } - }; - - callbacks.get_msg_dialog = [=]() -> std::shared_ptr - { - return std::make_shared(RPCS3MainWin->windowHandle()); - }; - - callbacks.get_osk_dialog = [=]() -> std::shared_ptr - { - return std::make_shared(); - }; - - callbacks.get_save_dialog = [=]() -> std::unique_ptr - { - return std::make_unique(); - }; - - callbacks.get_trophy_notification_dialog = [=]() -> std::unique_ptr - { - return std::make_unique(gameWindow); - }; - - callbacks.on_run = [=]() { OnEmulatorRun(); }; - callbacks.on_pause = [=]() { OnEmulatorPause(); }; - callbacks.on_resume = [=]() { OnEmulatorResume(); }; - callbacks.on_stop = [=]() { OnEmulatorStop(); }; - callbacks.on_ready = [=]() { OnEmulatorReady(); }; - - callbacks.handle_taskbar_progress = [=](s32 type, s32 value) - { - if (gameWindow) - { - switch (type) - { - case 0: - ((gs_frame*)gameWindow)->progress_reset(value); - break; - case 1: - ((gs_frame*)gameWindow)->progress_increment(value); - break; - case 2: - ((gs_frame*)gameWindow)->progress_set_limit(value); - break; - default: - LOG_FATAL(GENERAL, "Unknown type in handle_taskbar_progress(type=%d, value=%d)", type, value); - break; - } - } - }; + callbacks.on_run = []() {}; + callbacks.on_pause = []() {}; + callbacks.on_resume = []() {}; + callbacks.on_stop = []() {}; + callbacks.on_ready = []() {}; Emu.SetCallbacks(std::move(callbacks)); } -/* - * Initialize connects here. These are used to connect things between UI elements that require the intervention of rpcs3_app. - */ -void rpcs3_app::InitializeConnects() -{ - connect(RPCS3MainWin, &main_window::RequestGlobalStylesheetChange, this, &rpcs3_app::OnChangeStyleSheetRequest); - - qRegisterMetaType >("std::function"); - connect(this, &rpcs3_app::RequestCallAfter, this, &rpcs3_app::HandleCallAfter); - - connect(this, &rpcs3_app::OnEmulatorRun, RPCS3MainWin, &main_window::OnEmuRun); - connect(this, &rpcs3_app::OnEmulatorStop, RPCS3MainWin, &main_window::OnEmuStop); - connect(this, &rpcs3_app::OnEmulatorPause, RPCS3MainWin, &main_window::OnEmuPause); - connect(this, &rpcs3_app::OnEmulatorResume, RPCS3MainWin, &main_window::OnEmuResume); - connect(this, &rpcs3_app::OnEmulatorReady, RPCS3MainWin, &main_window::OnEmuReady); -} - -/* -* Handle a request to change the stylesheet. May consider adding reporting of errors in future. -* Empty string means default. -*/ -void rpcs3_app::OnChangeStyleSheetRequest(const QString& path) -{ - QString style_sheet - ( - // main window toolbar search - "QLineEdit#mw_searchbar { padding: 0 1em; background: #fdfdfd; selection-background-color: #148aff; margin: .8em; color:#000000; }" - - // main window toolbar slider - "QSlider#sizeSlider { color: #505050; background: #F0F0F0; }" - "QSlider#sizeSlider::handle:horizontal { border: 0em smooth rgba(227, 227, 227, 255); border-radius: .58em; background: #404040; width: 1.2em; margin: -.5em 0; }" - "QSlider#sizeSlider::groove:horizontal { border-radius: .15em; background: #5b5b5b; height: .3em; }" - - // main window toolbar - "QToolBar#mw_toolbar { background-color: #F0F0F0; border: none; }" - "QToolBar#mw_toolbar::separator { background-color: rgba(207, 207, 207, 235); width: 0.125em; margin-top: 0.250em; margin-bottom: 0.250em; }" - - // main window toolbar icon color - "QLabel#toolbar_icon_color { color: #5b5b5b; }" - - // thumbnail icon color - "QLabel#thumbnail_icon_color { color: rgba(0, 100, 231, 255); }" - - // game list icon color - "QLabel#gamelist_icon_background_color { color: rgba(240, 240, 240, 255); }" - - // save manager icon color - "QLabel#save_manager_icon_background_color { color: rgba(240, 240, 240, 255); }" - - // trophy manager icon color - "QLabel#trophy_manager_icon_background_color { color: rgba(240, 240, 240, 255); }" - - // tables - "QTableWidget { alternate-background-color: #f2f2f2; background-color: #fff; border: none; }" - "QTableWidget#game_grid { alternate-background-color: #f2f2f2; background-color: #fff; font-weight: 600; font-size: 8pt; font-family: Lucida Grande; color: rgba(51, 51, 51, 255); border: 0em solid white; }" - "QTableView::item { border-left: 0.063em solid white; border-right: 0.063em solid white; padding-left:0.313em; }" - "QTableView::item:selected { background-color: #148aff; color: #fff; }" - "QTableView#game_grid::item:hover:!selected { background-color: #94c9ff; color: #fff; }" - "QTableView#game_grid::item:hover:selected { background-color: #007fff; color: #fff; }" - - // table headers -#if (QT_VERSION < QT_VERSION_CHECK(5,11,0)) - "QHeaderView::section { padding: .5em; border: 0.063em solid #ffffff; }" - "QHeaderView::section:hover { background: #e3e3e3; padding: .5em; border: 0.063em solid #ffffff; }" -#else - "QHeaderView::section { padding-left: .5em; padding-right: .5em; padding-top: .4em; padding-bottom: -.1em; border: 0.063em solid #ffffff; }" - "QHeaderView::section:hover { background: #e3e3e3; padding-left: .5em; padding-right: .5em; padding-top: .4em; padding-bottom: -.1em; border: 0.063em solid #ffffff; }" -#endif - - // dock widget - "QDockWidget{ background: transparent; color: black; }" - "[floating=\"true\"]{ background: white; }" - "QDockWidget::title{ background: #e3e3e3; border: none; padding-top: 0.2em; padding-left: 0.2em; }" - "QDockWidget::close-button, QDockWidget::float-button{ background-color: #e3e3e3; }" - - // log frame tty - "QTextEdit#tty_frame { background-color: #ffffff; }" - "QLabel#tty_text { color: #000000; }" - - // log frame log - "QTextEdit#log_frame { background-color: #ffffff; }" - "QLabel#log_level_always { color: #107896; }" - "QLabel#log_level_fatal { color: #ff00ff; }" - "QLabel#log_level_error { color: #C02F1D; }" - "QLabel#log_level_todo { color: #ff6000; }" - "QLabel#log_level_success { color: #008000; }" - "QLabel#log_level_warning { color: #BA8745; }" - "QLabel#log_level_notice { color: #000000; }" - "QLabel#log_level_trace { color: #808080; }" - "QLabel#log_stack { color: #000000; }" - - // about dialog - "QWidget#header_section { background-color: #ffffff; }" - - // kernel explorer - "QDialog#kernel_explorer { background-color: rgba(240, 240, 240, 255); }" - - // memory viewer - "QDialog#memory_viewer { background-color: rgba(240, 240, 240, 255); }" - "QLabel#memory_viewer_address_panel { color: rgba(75, 135, 150, 255); background-color: rgba(240, 240, 240, 255); }" - "QLabel#memory_viewer_hex_panel { color: #000000; background-color: rgba(240, 240, 240, 255); }" - "QLabel#memory_viewer_ascii_panel { color: #000000; background-color: rgba(240, 240, 240, 255); }" - - // debugger frame - "QLabel#debugger_frame_breakpoint { color: #000000; background-color: #ffff00; }" - "QLabel#debugger_frame_pc { color: #000000; background-color: #00ff00; }" - - // rsx debugger - "QLabel#rsx_debugger_display_buffer { background-color: rgba(240, 240, 240, 255); }" - - // pad settings - "QLabel#l_controller { color: #434343; }" - ); - - QFile file(path); - - // If we can't open the file, try the /share or /Resources folder -#if !defined(_WIN32) -#ifdef __APPLE__ - QString share_dir = QCoreApplication::applicationDirPath() + "/../Resources/"; -#else - QString share_dir = QCoreApplication::applicationDirPath() + "/../share/rpcs3/"; -#endif - QFile share_file(share_dir + "GuiConfigs/" + QFileInfo(file.fileName()).fileName()); -#endif - - if (path == "") - { - setStyleSheet(style_sheet); - } - else if (file.open(QIODevice::ReadOnly | QIODevice::Text)) - { - QString config_dir = qstr(fs::get_config_dir()); - - // Add PS3 fonts - QDirIterator ps3_font_it(qstr(g_cfg.vfs.get_dev_flash() + "data/font/"), QStringList() << "*.ttf", QDir::Files, QDirIterator::Subdirectories); - while (ps3_font_it.hasNext()) - QFontDatabase::addApplicationFont(ps3_font_it.next()); - - // Add custom fonts - QDirIterator custom_font_it(config_dir + "fonts/", QStringList() << "*.ttf", QDir::Files, QDirIterator::Subdirectories); - while (custom_font_it.hasNext()) - QFontDatabase::addApplicationFont(custom_font_it.next()); - - // Set root for stylesheets - QDir::setCurrent(config_dir); - setStyleSheet(file.readAll()); - file.close(); - } -#if !defined(_WIN32) - else if (share_file.open(QIODevice::ReadOnly | QIODevice::Text)) - { - QDir::setCurrent(share_dir); - setStyleSheet(share_file.readAll()); - share_file.close(); - } -#endif - else - { - setStyleSheet(style_sheet); - } - - gui::stylesheet = styleSheet(); - RPCS3MainWin->RepaintGui(); -} - /** - * Using connects avoids timers being unable to be used in a non-qt thread. So, even if this looks stupid to just call func, it's succinct. -*/ + * Using connects avoids timers being unable to be used in a non-qt thread. So, even if this looks stupid to just call func, it's succinct. + */ void rpcs3_app::HandleCallAfter(const std::function& func) { func(); diff --git a/rpcs3/rpcs3_app.h b/rpcs3/rpcs3_app.h index 118b76fabc..781e5d214a 100644 --- a/rpcs3/rpcs3_app.h +++ b/rpcs3/rpcs3_app.h @@ -2,59 +2,35 @@ #include "stdafx.h" -#include "keyboard_pad_handler.h" -#include "basic_keyboard_handler.h" -#include "basic_mouse_handler.h" +#include "main_application.h" -#include "Utilities/Config.h" -#include "Emu/VFS.h" -#include "Emu/RSX/GSRender.h" -#include "Emu/Io/KeyboardHandler.h" -#include "Emu/Io/PadHandler.h" -#include "Emu/Io/MouseHandler.h" -#include "Emu/Audio/AudioBackend.h" - -#include "rpcs3qt/msg_dialog_frame.h" -#include "rpcs3qt/osk_dialog_frame.h" -#include "rpcs3qt/main_window.h" -#include "rpcs3qt/gui_settings.h" -#include "rpcs3qt/emu_settings.h" - -#include -#include +#include /** RPCS3 Application Class * The point of this class is to do application initialization and to hold onto the main window. The main thing I intend this class to do, for now, is to initialize callbacks and the main_window. */ -class rpcs3_app : public QApplication +class rpcs3_app : public QCoreApplication, public main_application { Q_OBJECT public: rpcs3_app(int& argc, char** argv); - /** Call this method before calling app.exec - */ - void Init(); - /** Emu.Init() wrapper for user manager */ - static bool InitializeEmulator(const std::string& user, bool force_init); -Q_SIGNALS: - void OnEmulatorRun(); - void OnEmulatorPause(); - void OnEmulatorResume(); - void OnEmulatorStop(); - void OnEmulatorReady(); - void RequestCallAfter(const std::function& func); -private Q_SLOTS: - void OnChangeStyleSheetRequest(const QString& path); - void HandleCallAfter(const std::function& func); + /** Call this method before calling app.exec */ + void Init() override; + private: void InitializeCallbacks(); void InitializeConnects(); - main_window* RPCS3MainWin = nullptr; + QThread* get_thread() override + { + return thread(); + }; - std::shared_ptr guiSettings; - std::shared_ptr emuSettings; - QWindow* gameWindow = nullptr; //! (Currently) only needed so that pad handlers have a valid target for event filtering. +Q_SIGNALS: + void RequestCallAfter(const std::function& func); + +private Q_SLOTS: + void HandleCallAfter(const std::function& func); }; diff --git a/rpcs3/rpcs3qt/gui_application.cpp b/rpcs3/rpcs3qt/gui_application.cpp new file mode 100644 index 0000000000..017f0ce605 --- /dev/null +++ b/rpcs3/rpcs3qt/gui_application.cpp @@ -0,0 +1,315 @@ +#include "gui_application.h" + +#include "qt_utils.h" +#include "welcome_dialog.h" + +#ifdef WITH_DISCORD_RPC +#include "_discord_utils.h" +#endif + +#include "trophy_notification_helper.h" +#include "save_data_dialog.h" +#include "msg_dialog_frame.h" +#include "osk_dialog_frame.h" + +gui_application::gui_application(int& argc, char** argv) : QApplication(argc, argv) +{ +} + +void gui_application::Init() +{ + setWindowIcon(QIcon(":/rpcs3.ico")); + + m_emu_settings.reset(new emu_settings()); + m_gui_settings.reset(new gui_settings()); + + // Force init the emulator + InitializeEmulator(m_gui_settings->GetCurrentUser().toStdString(), true); + + // Create the main window + m_main_window = new main_window(m_gui_settings, m_emu_settings, nullptr); + + // Create callbacks from the emulator, which reference the handlers. + InitializeCallbacks(); + + // Create connects to propagate events throughout Gui. + InitializeConnects(); + + m_main_window->Init(); + + if (m_gui_settings->GetValue(gui::ib_show_welcome).toBool()) + { + welcome_dialog* welcome = new welcome_dialog(); + welcome->exec(); + } +#ifdef WITH_DISCORD_RPC + // Discord Rich Presence Integration + if (m_gui_settings->GetValue(gui::m_richPresence).toBool()) + { + discord::initialize(); + } +#endif +} + +void gui_application::InitializeConnects() +{ + connect(m_main_window, &main_window::RequestGlobalStylesheetChange, this, &gui_application::OnChangeStyleSheetRequest); + + connect(this, &gui_application::OnEmulatorRun, m_main_window, &main_window::OnEmuRun); + connect(this, &gui_application::OnEmulatorStop, m_main_window, &main_window::OnEmuStop); + connect(this, &gui_application::OnEmulatorPause, m_main_window, &main_window::OnEmuPause); + connect(this, &gui_application::OnEmulatorResume, m_main_window, &main_window::OnEmuResume); + connect(this, &gui_application::OnEmulatorReady, m_main_window, &main_window::OnEmuReady); + + qRegisterMetaType>("std::function"); + connect(this, &gui_application::RequestCallAfter, this, &gui_application::HandleCallAfter); +} + +std::unique_ptr gui_application::get_gs_frame() +{ + extern const std::unordered_map, value_hash> g_video_out_resolution_map; + + const auto size = g_video_out_resolution_map.at(g_cfg.video.resolution); + int w = size.first; + int h = size.second; + + if (m_gui_settings->GetValue(gui::gs_resize).toBool()) + { + w = m_gui_settings->GetValue(gui::gs_width).toInt(); + h = m_gui_settings->GetValue(gui::gs_height).toInt(); + } + + auto frame_geometry = gui::utils::create_centered_window_geometry(m_main_window->geometry(), w, h); + + gs_frame* frame; + + switch (video_renderer type = g_cfg.video.renderer) + { + case video_renderer::null: + { + frame = new gs_frame("Null", frame_geometry, m_main_window->GetAppIcon(), m_gui_settings); + break; + } + case video_renderer::opengl: + { + frame = new gl_gs_frame(frame_geometry, m_main_window->GetAppIcon(), m_gui_settings); + break; + } + case video_renderer::vulkan: + { + frame = new gs_frame("Vulkan", frame_geometry, m_main_window->GetAppIcon(), m_gui_settings); + break; + } +#ifdef _MSC_VER + case video_renderer::dx12: + { + frame = new gs_frame("DirectX 12", frame_geometry, m_main_window->GetAppIcon(), m_gui_settings); + break; + } +#endif + default: fmt::throw_exception("Invalid video renderer: %s" HERE, type); + } + + m_game_window = frame; + return std::unique_ptr(frame); +} + +/** RPCS3 emulator has functions it desires to call from the GUI at times. Initialize them in here. */ +void gui_application::InitializeCallbacks() +{ + EmuCallbacks callbacks = CreateCallbacks(); + + callbacks.exit = [this]() + { + quit(); + }; + callbacks.call_after = [=](std::function func) + { + RequestCallAfter(std::move(func)); + }; + + callbacks.get_gs_frame = [this]() -> std::unique_ptr { return get_gs_frame(); }; + callbacks.get_msg_dialog = [this]() -> std::shared_ptr { return std::make_shared(m_main_window->windowHandle()); }; + callbacks.get_osk_dialog = []() -> std::shared_ptr { return std::make_shared(); }; + callbacks.get_save_dialog = []() -> std::unique_ptr { return std::make_unique(); }; + callbacks.get_trophy_notification_dialog = [this]() -> std::unique_ptr { return std::make_unique(m_game_window); }; + + callbacks.on_run = [=]() { OnEmulatorRun(); }; + callbacks.on_pause = [=]() { OnEmulatorPause(); }; + callbacks.on_resume = [=]() { OnEmulatorResume(); }; + callbacks.on_stop = [=]() { OnEmulatorStop(); }; + callbacks.on_ready = [=]() { OnEmulatorReady(); }; + + callbacks.handle_taskbar_progress = [=](s32 type, s32 value) + { + if (m_game_window) + { + switch (type) + { + case 0: ((gs_frame*)m_game_window)->progress_reset(value); break; + case 1: ((gs_frame*)m_game_window)->progress_increment(value); break; + case 2: ((gs_frame*)m_game_window)->progress_set_limit(value); break; + default: LOG_FATAL(GENERAL, "Unknown type in handle_taskbar_progress(type=%d, value=%d)", type, value); break; + } + } + }; + + Emu.SetCallbacks(std::move(callbacks)); +} + +/* +* Handle a request to change the stylesheet. May consider adding reporting of errors in future. +* Empty string means default. +*/ +void gui_application::OnChangeStyleSheetRequest(const QString& path) +{ + QString style_sheet + ( + // main window toolbar search + "QLineEdit#mw_searchbar { padding: 0 1em; background: #fdfdfd; selection-background-color: #148aff; margin: .8em; color:#000000; }" + + // main window toolbar slider + "QSlider#sizeSlider { color: #505050; background: #F0F0F0; }" + "QSlider#sizeSlider::handle:horizontal { border: 0em smooth rgba(227, 227, 227, 255); border-radius: .58em; background: #404040; width: 1.2em; margin: -.5em 0; }" + "QSlider#sizeSlider::groove:horizontal { border-radius: .15em; background: #5b5b5b; height: .3em; }" + + // main window toolbar + "QToolBar#mw_toolbar { background-color: #F0F0F0; border: none; }" + "QToolBar#mw_toolbar::separator { background-color: rgba(207, 207, 207, 235); width: 0.125em; margin-top: 0.250em; margin-bottom: 0.250em; }" + + // main window toolbar icon color + "QLabel#toolbar_icon_color { color: #5b5b5b; }" + + // thumbnail icon color + "QLabel#thumbnail_icon_color { color: rgba(0, 100, 231, 255); }" + + // game list icon color + "QLabel#gamelist_icon_background_color { color: rgba(240, 240, 240, 255); }" + + // save manager icon color + "QLabel#save_manager_icon_background_color { color: rgba(240, 240, 240, 255); }" + + // trophy manager icon color + "QLabel#trophy_manager_icon_background_color { color: rgba(240, 240, 240, 255); }" + + // tables + "QTableWidget { alternate-background-color: #f2f2f2; background-color: #fff; border: none; }" + "QTableWidget#game_grid { alternate-background-color: #f2f2f2; background-color: #fff; font-weight: 600; font-size: 8pt; font-family: Lucida Grande; color: rgba(51, 51, 51, 255); border: 0em solid white; }" + "QTableView::item { border-left: 0.063em solid white; border-right: 0.063em solid white; padding-left:0.313em; }" + "QTableView::item:selected { background-color: #148aff; color: #fff; }" + "QTableView#game_grid::item:hover:!selected { background-color: #94c9ff; color: #fff; }" + "QTableView#game_grid::item:hover:selected { background-color: #007fff; color: #fff; }" + + // table headers +#if (QT_VERSION < QT_VERSION_CHECK(5,11,0)) + "QHeaderView::section { padding: .5em; border: 0.063em solid #ffffff; }" + "QHeaderView::section:hover { background: #e3e3e3; padding: .5em; border: 0.063em solid #ffffff; }" +#else + "QHeaderView::section { padding-left: .5em; padding-right: .5em; padding-top: .4em; padding-bottom: -.1em; border: 0.063em solid #ffffff; }" + "QHeaderView::section:hover { background: #e3e3e3; padding-left: .5em; padding-right: .5em; padding-top: .4em; padding-bottom: -.1em; border: 0.063em solid #ffffff; }" +#endif + + // dock widget + "QDockWidget{ background: transparent; color: black; }" + "[floating=\"true\"]{ background: white; }" + "QDockWidget::title{ background: #e3e3e3; border: none; padding-top: 0.2em; padding-left: 0.2em; }" + "QDockWidget::close-button, QDockWidget::float-button{ background-color: #e3e3e3; }" + + // log frame tty + "QTextEdit#tty_frame { background-color: #ffffff; }" + "QLabel#tty_text { color: #000000; }" + + // log frame log + "QTextEdit#log_frame { background-color: #ffffff; }" + "QLabel#log_level_always { color: #107896; }" + "QLabel#log_level_fatal { color: #ff00ff; }" + "QLabel#log_level_error { color: #C02F1D; }" + "QLabel#log_level_todo { color: #ff6000; }" + "QLabel#log_level_success { color: #008000; }" + "QLabel#log_level_warning { color: #BA8745; }" + "QLabel#log_level_notice { color: #000000; }" + "QLabel#log_level_trace { color: #808080; }" + "QLabel#log_stack { color: #000000; }" + + // about dialog + "QWidget#header_section { background-color: #ffffff; }" + + // kernel explorer + "QDialog#kernel_explorer { background-color: rgba(240, 240, 240, 255); }" + + // memory viewer + "QDialog#memory_viewer { background-color: rgba(240, 240, 240, 255); }" + "QLabel#memory_viewer_address_panel { color: rgba(75, 135, 150, 255); background-color: rgba(240, 240, 240, 255); }" + "QLabel#memory_viewer_hex_panel { color: #000000; background-color: rgba(240, 240, 240, 255); }" + "QLabel#memory_viewer_ascii_panel { color: #000000; background-color: rgba(240, 240, 240, 255); }" + + // debugger frame + "QLabel#debugger_frame_breakpoint { color: #000000; background-color: #ffff00; }" + "QLabel#debugger_frame_pc { color: #000000; background-color: #00ff00; }" + + // rsx debugger + "QLabel#rsx_debugger_display_buffer { background-color: rgba(240, 240, 240, 255); }" + + // pad settings + "QLabel#l_controller { color: #434343; }" + ); + + QFile file(path); + + // If we can't open the file, try the /share or /Resources folder +#if !defined(_WIN32) +#ifdef __APPLE__ + QString share_dir = QCoreApplication::applicationDirPath() + "/../Resources/"; +#else + QString share_dir = QCoreApplication::applicationDirPath() + "/../share/rpcs3/"; +#endif + QFile share_file(share_dir + "GuiConfigs/" + QFileInfo(file.fileName()).fileName()); +#endif + + if (path == "") + { + setStyleSheet(style_sheet); + } + else if (file.open(QIODevice::ReadOnly | QIODevice::Text)) + { + QString config_dir = qstr(fs::get_config_dir()); + + // Add PS3 fonts + QDirIterator ps3_font_it(qstr(g_cfg.vfs.get_dev_flash() + "data/font/"), QStringList() << "*.ttf", QDir::Files, QDirIterator::Subdirectories); + while (ps3_font_it.hasNext()) + QFontDatabase::addApplicationFont(ps3_font_it.next()); + + // Add custom fonts + QDirIterator custom_font_it(config_dir + "fonts/", QStringList() << "*.ttf", QDir::Files, QDirIterator::Subdirectories); + while (custom_font_it.hasNext()) + QFontDatabase::addApplicationFont(custom_font_it.next()); + + // Set root for stylesheets + QDir::setCurrent(config_dir); + setStyleSheet(file.readAll()); + file.close(); + } +#if !defined(_WIN32) + else if (share_file.open(QIODevice::ReadOnly | QIODevice::Text)) + { + QDir::setCurrent(share_dir); + setStyleSheet(share_file.readAll()); + share_file.close(); + } +#endif + else + { + setStyleSheet(style_sheet); + } + + gui::stylesheet = styleSheet(); + m_main_window->RepaintGui(); +} + +/** + * Using connects avoids timers being unable to be used in a non-qt thread. So, even if this looks stupid to just call func, it's succinct. + */ +void gui_application::HandleCallAfter(const std::function& func) +{ + func(); +} diff --git a/rpcs3/rpcs3qt/gui_application.h b/rpcs3/rpcs3qt/gui_application.h new file mode 100644 index 0000000000..c4b499e52f --- /dev/null +++ b/rpcs3/rpcs3qt/gui_application.h @@ -0,0 +1,55 @@ +#pragma once + +#include "stdafx.h" + +#include "main_application.h" +#include "main_window.h" +#include "emu_settings.h" +#include "gui_settings.h" +#include "gs_frame.h" +#include "gl_gs_frame.h" + +#include +#include +#include + +class gui_application : public QApplication, public main_application +{ + Q_OBJECT +public: + gui_application(int& argc, char** argv); + + /** Call this method before calling app.exec */ + void Init() override; + + std::unique_ptr get_gs_frame(); + + main_window* m_main_window = nullptr; + +private: + QThread* get_thread() override + { + return thread(); + } + + void InitializeCallbacks(); + void InitializeConnects(); + + std::shared_ptr m_emu_settings; + std::shared_ptr m_gui_settings; + +private Q_SLOTS: + void OnChangeStyleSheetRequest(const QString& path); + +Q_SIGNALS: + void OnEmulatorRun(); + void OnEmulatorPause(); + void OnEmulatorResume(); + void OnEmulatorStop(); + void OnEmulatorReady(); + + void RequestCallAfter(const std::function& func); + +private Q_SLOTS: + void HandleCallAfter(const std::function& func); +}; diff --git a/rpcs3/rpcs3qt/user_manager_dialog.cpp b/rpcs3/rpcs3qt/user_manager_dialog.cpp index 11a097379e..295c9b7a1c 100644 --- a/rpcs3/rpcs3qt/user_manager_dialog.cpp +++ b/rpcs3/rpcs3qt/user_manager_dialog.cpp @@ -1,12 +1,13 @@ -#include "user_manager_dialog.h" +#include "user_manager_dialog.h" #include "table_item_delegate.h" -#include "rpcs3_app.h" +#include "main_application.h" #include "Utilities/StrUtil.h" #include #include #include +#include namespace { @@ -362,7 +363,7 @@ void user_manager_dialog::OnUserLogin() const u32 key = GetUserKey(); const std::string new_user = m_user_list[key].GetUserId(); - if (!rpcs3_app::InitializeEmulator(new_user, false)) + if (!main_application::InitializeEmulator(new_user, false)) { LOG_FATAL(GENERAL, "Failed to login user! username=%s key=%d", new_user, key); return;