diff --git a/rpcs3/Emu/Cell/Modules/cellMusicSelectionContext.cpp b/rpcs3/Emu/Cell/Modules/cellMusicSelectionContext.cpp index 3f3b62b4d7..270786ed72 100644 --- a/rpcs3/Emu/Cell/Modules/cellMusicSelectionContext.cpp +++ b/rpcs3/Emu/Cell/Modules/cellMusicSelectionContext.cpp @@ -302,7 +302,7 @@ u32 music_selection_context::step_track(bool next) // Play the previous track. Start with the last track if we reached the start of the playlist. if (current_track == 0) { - current_track = playlist.size() - 1; + current_track = ::narrow(playlist.size() - 1); } else { diff --git a/rpcs3/Emu/NP/rpcn_config.cpp b/rpcs3/Emu/NP/rpcn_config.cpp index fa623decb5..d2c8fb3f1a 100644 --- a/rpcs3/Emu/NP/rpcn_config.cpp +++ b/rpcs3/Emu/NP/rpcn_config.cpp @@ -65,7 +65,7 @@ std::string cfg_rpcn::generate_npid() const char list_chars[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'}; - std::srand(time(nullptr)); + std::srand(static_cast(time(nullptr))); for (int i = 0; i < 10; i++) { diff --git a/rpcs3/rpcs3.vcxproj b/rpcs3/rpcs3.vcxproj index b3550f04b3..2fdc11a5c1 100644 --- a/rpcs3/rpcs3.vcxproj +++ b/rpcs3/rpcs3.vcxproj @@ -362,6 +362,15 @@ true + + true + + + true + + + true + true @@ -587,6 +596,15 @@ true + + true + + + true + + + true + true @@ -665,6 +683,9 @@ + + + @@ -903,6 +924,7 @@ + $(QTDIR)\bin\moc.exe;%(FullPath) @@ -1237,6 +1259,36 @@ .\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_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -DNDEBUG -DQT_WINEXTRAS_LIB -DQT_CONCURRENT_LIB -D%(PreprocessorDefinitions) "-I.\..\3rdparty\flatbuffers\include" "-I.\..\3rdparty\wolfssl\wolfssl" "-I.\..\3rdparty\curl\curl\include" "-I.\..\3rdparty\libusb\libusb\libusb" "-I$(VULKAN_SDK)\Include" "-I.\..\3rdparty\XAudio2Redist\include" "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtANGLE" "-I$(QTDIR)\include\QtCore" "-I.\release" "-I$(QTDIR)\mkspecs\win32-msvc2015" "-I.\QTGeneratedFiles\$(ConfigurationName)" "-I.\QTGeneratedFiles" "-I$(QTDIR)\include\QtWinExtras" "-I$(QTDIR)\include\QtConcurrent" + + $(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 -DWIN32_LEAN_AND_MEAN -DHAVE_VULKAN -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -DQT_WINEXTRAS_LIB -DQT_CONCURRENT_LIB -DQT_MULTIMEDIA_LIB -DQT_MULTIMEDIAWIDGETS_LIB -DQT_SVG_LIB -D%(PreprocessorDefinitions) "-I.\..\3rdparty\SoundTouch\soundtouch\include" "-I.\..\3rdparty\cubeb\extra" "-I.\..\3rdparty\cubeb\cubeb\include" "-I.\..\3rdparty\flatbuffers\include" "-I.\..\3rdparty\wolfssl\wolfssl" "-I.\..\3rdparty\curl\curl\include" "-I.\..\3rdparty\libusb\libusb\libusb" "-I$(VULKAN_SDK)\Include" "-I.\..\3rdparty\XAudio2Redist\include" "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtANGLE" "-I$(QTDIR)\include\QtCore" "-I.\debug" "-I$(QTDIR)\mkspecs\win32-msvc2015" "-I.\QTGeneratedFiles\$(ConfigurationName)" "-I.\QTGeneratedFiles" "-I$(QTDIR)\include\QtWinExtras" "-I$(QTDIR)\include\QtConcurrent" "-I$(QTDIR)\include\QtMultimedia" "-I$(QTDIR)\include\QtMultimediaWidgets" "-I$(QTDIR)\include\QtSvg" + $(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 -DWIN32_LEAN_AND_MEAN -DHAVE_VULKAN -DWITH_DISCORD_RPC -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -DNDEBUG -DQT_WINEXTRAS_LIB -DQT_CONCURRENT_LIB -DQT_MULTIMEDIA_LIB -DQT_MULTIMEDIAWIDGETS_LIB -DQT_SVG_LIB -D%(PreprocessorDefinitions) "-I.\..\3rdparty\SoundTouch\soundtouch\include" "-I.\..\3rdparty\cubeb\extra" "-I.\..\3rdparty\cubeb\cubeb\include" "-I.\..\3rdparty\flatbuffers\include" "-I.\..\3rdparty\wolfssl\wolfssl" "-I.\..\3rdparty\curl\curl\include" "-I.\..\3rdparty\libusb\libusb\libusb" "-I$(VULKAN_SDK)\Include" "-I.\..\3rdparty\XAudio2Redist\include" "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtANGLE" "-I$(QTDIR)\include\QtCore" "-I.\release" "-I$(QTDIR)\mkspecs\win32-msvc2015" "-I.\QTGeneratedFiles\$(ConfigurationName)" "-I.\QTGeneratedFiles" "-I$(QTDIR)\include\QtWinExtras" "-I$(QTDIR)\include\QtConcurrent" "-I$(QTDIR)\include\QtMultimedia" "-I$(QTDIR)\include\QtMultimediaWidgets" "-I$(QTDIR)\include\QtSvg" + + + $(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 -DWIN32_LEAN_AND_MEAN -DHAVE_VULKAN -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -DQT_WINEXTRAS_LIB -DQT_CONCURRENT_LIB -DQT_MULTIMEDIA_LIB -DQT_MULTIMEDIAWIDGETS_LIB -DQT_SVG_LIB -D%(PreprocessorDefinitions) "-I.\..\3rdparty\SoundTouch\soundtouch\include" "-I.\..\3rdparty\cubeb\extra" "-I.\..\3rdparty\cubeb\cubeb\include" "-I.\..\3rdparty\flatbuffers\include" "-I.\..\3rdparty\wolfssl\wolfssl" "-I.\..\3rdparty\curl\curl\include" "-I.\..\3rdparty\libusb\libusb\libusb" "-I$(VULKAN_SDK)\Include" "-I.\..\3rdparty\XAudio2Redist\include" "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtANGLE" "-I$(QTDIR)\include\QtCore" "-I.\debug" "-I$(QTDIR)\mkspecs\win32-msvc2015" "-I.\QTGeneratedFiles\$(ConfigurationName)" "-I.\QTGeneratedFiles" "-I$(QTDIR)\include\QtWinExtras" "-I$(QTDIR)\include\QtConcurrent" "-I$(QTDIR)\include\QtMultimedia" "-I$(QTDIR)\include\QtMultimediaWidgets" "-I$(QTDIR)\include\QtSvg" + $(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 -DWIN32_LEAN_AND_MEAN -DHAVE_VULKAN -DWITH_DISCORD_RPC -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -DNDEBUG -DQT_WINEXTRAS_LIB -DQT_CONCURRENT_LIB -DQT_MULTIMEDIA_LIB -DQT_MULTIMEDIAWIDGETS_LIB -DQT_SVG_LIB -D%(PreprocessorDefinitions) "-I.\..\3rdparty\SoundTouch\soundtouch\include" "-I.\..\3rdparty\cubeb\extra" "-I.\..\3rdparty\cubeb\cubeb\include" "-I.\..\3rdparty\flatbuffers\include" "-I.\..\3rdparty\wolfssl\wolfssl" "-I.\..\3rdparty\curl\curl\include" "-I.\..\3rdparty\libusb\libusb\libusb" "-I$(VULKAN_SDK)\Include" "-I.\..\3rdparty\XAudio2Redist\include" "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtANGLE" "-I$(QTDIR)\include\QtCore" "-I.\release" "-I$(QTDIR)\mkspecs\win32-msvc2015" "-I.\QTGeneratedFiles\$(ConfigurationName)" "-I.\QTGeneratedFiles" "-I$(QTDIR)\include\QtWinExtras" "-I$(QTDIR)\include\QtConcurrent" "-I$(QTDIR)\include\QtMultimedia" "-I$(QTDIR)\include\QtMultimediaWidgets" "-I$(QTDIR)\include\QtSvg" + + + $(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 -DWIN32_LEAN_AND_MEAN -DHAVE_VULKAN -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -DQT_WINEXTRAS_LIB -DQT_CONCURRENT_LIB -DQT_MULTIMEDIA_LIB -DQT_MULTIMEDIAWIDGETS_LIB -DQT_SVG_LIB -D%(PreprocessorDefinitions) "-I.\..\3rdparty\SoundTouch\soundtouch\include" "-I.\..\3rdparty\cubeb\extra" "-I.\..\3rdparty\cubeb\cubeb\include" "-I.\..\3rdparty\flatbuffers\include" "-I.\..\3rdparty\wolfssl\wolfssl" "-I.\..\3rdparty\curl\curl\include" "-I.\..\3rdparty\libusb\libusb\libusb" "-I$(VULKAN_SDK)\Include" "-I.\..\3rdparty\XAudio2Redist\include" "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtANGLE" "-I$(QTDIR)\include\QtCore" "-I.\debug" "-I$(QTDIR)\mkspecs\win32-msvc2015" "-I.\QTGeneratedFiles\$(ConfigurationName)" "-I.\QTGeneratedFiles" "-I$(QTDIR)\include\QtWinExtras" "-I$(QTDIR)\include\QtConcurrent" "-I$(QTDIR)\include\QtMultimedia" "-I$(QTDIR)\include\QtMultimediaWidgets" "-I$(QTDIR)\include\QtSvg" + $(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 -DWIN32_LEAN_AND_MEAN -DHAVE_VULKAN -DWITH_DISCORD_RPC -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -DNDEBUG -DQT_WINEXTRAS_LIB -DQT_CONCURRENT_LIB -DQT_MULTIMEDIA_LIB -DQT_MULTIMEDIAWIDGETS_LIB -DQT_SVG_LIB -D%(PreprocessorDefinitions) "-I.\..\3rdparty\SoundTouch\soundtouch\include" "-I.\..\3rdparty\cubeb\extra" "-I.\..\3rdparty\cubeb\cubeb\include" "-I.\..\3rdparty\flatbuffers\include" "-I.\..\3rdparty\wolfssl\wolfssl" "-I.\..\3rdparty\curl\curl\include" "-I.\..\3rdparty\libusb\libusb\libusb" "-I$(VULKAN_SDK)\Include" "-I.\..\3rdparty\XAudio2Redist\include" "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtANGLE" "-I$(QTDIR)\include\QtCore" "-I.\release" "-I$(QTDIR)\mkspecs\win32-msvc2015" "-I.\QTGeneratedFiles\$(ConfigurationName)" "-I.\QTGeneratedFiles" "-I$(QTDIR)\include\QtWinExtras" "-I$(QTDIR)\include\QtConcurrent" "-I$(QTDIR)\include\QtMultimedia" "-I$(QTDIR)\include\QtMultimediaWidgets" "-I$(QTDIR)\include\QtSvg" + @@ -1651,6 +1703,16 @@ .\QTGeneratedFiles\ui_%(Filename).h;%(Outputs) "$(QTDIR)\bin\uic.exe" -o ".\QTGeneratedFiles\ui_%(Filename).h" "%(FullPath)" + + $(QTDIR)\bin\uic.exe;%(AdditionalInputs) + Uic%27ing %(Identity)... + .\QTGeneratedFiles\ui_%(Filename).h;%(Outputs) + "$(QTDIR)\bin\uic.exe" -o ".\QTGeneratedFiles\ui_%(Filename).h" "%(FullPath)" + $(QTDIR)\bin\uic.exe;%(AdditionalInputs) + Uic%27ing %(Identity)... + .\QTGeneratedFiles\ui_%(Filename).h;%(Outputs) + "$(QTDIR)\bin\uic.exe" -o ".\QTGeneratedFiles\ui_%(Filename).h" "%(FullPath)" + diff --git a/rpcs3/rpcs3.vcxproj.filters b/rpcs3/rpcs3.vcxproj.filters index 1d2af89d15..0f973f9846 100644 --- a/rpcs3/rpcs3.vcxproj.filters +++ b/rpcs3/rpcs3.vcxproj.filters @@ -145,6 +145,9 @@ {00fa44cd-ccae-48d9-8541-5f3b2b26a845} + + {1b83df2f-bb74-4bc9-87f1-ab2f3889bcc9} + @@ -891,6 +894,33 @@ Generated Files\Release + + Gui\shortcuts + + + Generated Files\Debug + + + Generated Files\Release + + + Gui\shortcuts + + + Generated Files\Debug + + + Generated Files\Release + + + Gui\shortcuts + + + Generated Files\Debug + + + Generated Files\Release + @@ -1058,6 +1088,9 @@ Io\SDL + + Generated Files + @@ -1318,6 +1351,18 @@ Gui\log + + Gui\shortcuts + + + Gui\shortcuts + + + Gui\shortcuts + + + Form Files + diff --git a/rpcs3/rpcs3qt/CMakeLists.txt b/rpcs3/rpcs3qt/CMakeLists.txt index 32d63b4eaf..f5eb6bd050 100644 --- a/rpcs3/rpcs3qt/CMakeLists.txt +++ b/rpcs3/rpcs3qt/CMakeLists.txt @@ -74,6 +74,9 @@ set(SRC_FILES settings.cpp settings_dialog.cpp shortcut_utils.cpp + shortcut_dialog.cpp + shortcut_handler.cpp + shortcut_settings.cpp skylander_dialog.cpp syntax_highlighter.cpp tooltips.cpp @@ -102,6 +105,7 @@ set(UI_FILES patch_creator_dialog.ui patch_manager_dialog.ui settings_dialog.ui + shortcut_dialog.ui welcome_dialog.ui ) diff --git a/rpcs3/rpcs3qt/gs_frame.cpp b/rpcs3/rpcs3qt/gs_frame.cpp index 65f801139f..63c6afe3f5 100644 --- a/rpcs3/rpcs3qt/gs_frame.cpp +++ b/rpcs3/rpcs3qt/gs_frame.cpp @@ -124,6 +124,10 @@ gs_frame::gs_frame(QScreen* screen, const QRect& geometry, const QIcon& appIcon, setVisibility(startup_visibility); create(); + // TODO: enable in Qt6 + //m_shortcut_handler = new shortcut_handler(gui::shortcuts::shortcut_handler_id::game_window, this, m_gui_settings); + //connect(m_shortcut_handler, &shortcut_handler::shortcut_activated, this, &gs_frame::handle_shortcut); + // Change cursor when in fullscreen. connect(this, &QWindow::visibilityChanged, this, [this](QWindow::Visibility visibility) { @@ -138,7 +142,7 @@ gs_frame::gs_frame(QScreen* screen, const QRect& geometry, const QIcon& appIcon, }); // Configure the mouse hide on idle timer - connect(&m_mousehide_timer, &QTimer::timeout, this, &gs_frame::MouseHideTimeout); + connect(&m_mousehide_timer, &QTimer::timeout, this, &gs_frame::mouse_hide_timeout); m_mousehide_timer.setSingleShot(true); #ifdef _WIN32 @@ -220,6 +224,7 @@ void gs_frame::showEvent(QShowEvent *event) QWindow::showEvent(event); } +// TODO: remove when shortcuts are properly hooked up (also check keyboard_pad_handler::processKeyEvent) void gs_frame::keyPressEvent(QKeyEvent *keyEvent) { if (keyEvent->isAutoRepeat()) @@ -236,38 +241,117 @@ void gs_frame::keyPressEvent(QKeyEvent *keyEvent) { if (keyEvent->modifiers() == Qt::AltModifier) { - static int count = 0; - mark_log.success("Made forced mark %d in log", ++count); - return; + handle_shortcut(gui::shortcuts::shortcut::gw_log_mark, {}); + break; } else if (keyEvent->modifiers() == Qt::ControlModifier) { - toggle_mouselock(); - return; + handle_shortcut(gui::shortcuts::shortcut::gw_mouse_lock, {}); + break; } break; } case Qt::Key_Return: { if (keyEvent->modifiers() == Qt::AltModifier) - { - toggle_fullscreen(); - return; - } + handle_shortcut(gui::shortcuts::shortcut::gw_toggle_fullscreen, {}); break; } case Qt::Key_Escape: { - if (visibility() == FullScreen && !m_disable_kb_hotkeys) - { - toggle_fullscreen(); - return; - } + handle_shortcut(gui::shortcuts::shortcut::gw_exit_fullscreen, {}); break; } case Qt::Key_P: { - if (keyEvent->modifiers() == Qt::ControlModifier && !m_disable_kb_hotkeys) + if (keyEvent->modifiers() == Qt::ControlModifier) + handle_shortcut(gui::shortcuts::shortcut::gw_pause_play, {}); + break; + } + case Qt::Key_S: + { + if (keyEvent->modifiers() == Qt::ControlModifier) + handle_shortcut(gui::shortcuts::shortcut::gw_savestate, {}); + break; + } + case Qt::Key_R: + { + if (keyEvent->modifiers() == Qt::ControlModifier) + handle_shortcut(gui::shortcuts::shortcut::gw_restart, {}); + break; + } + case Qt::Key_C: + { + if (keyEvent->modifiers() == Qt::AltModifier) + handle_shortcut(gui::shortcuts::shortcut::gw_rsx_capture, {}); + break; + } + case Qt::Key_F10: + { + if (keyEvent->modifiers() == Qt::ControlModifier) + handle_shortcut(gui::shortcuts::shortcut::gw_frame_limit, {}); + break; + } + case Qt::Key_F11: + { + handle_shortcut(gui::shortcuts::shortcut::gw_toggle_recording, {}); + break; + } + case Qt::Key_F12: + { + handle_shortcut(gui::shortcuts::shortcut::gw_screenshot, {}); + break; + } + default: + { + break; + } + } +} + +void gs_frame::handle_shortcut(gui::shortcuts::shortcut shortcut_key, const QKeySequence& key_sequence) +{ + gui_log.notice("Game window registered shortcut: %s (%s)", shortcut_key, key_sequence.toString().toStdString()); + + switch (shortcut_key) + { + case gui::shortcuts::shortcut::gw_toggle_fullscreen: + { + toggle_fullscreen(); + break; + } + case gui::shortcuts::shortcut::gw_exit_fullscreen: + { + if (visibility() == FullScreen && !m_disable_kb_hotkeys) + { + toggle_fullscreen(); + } + break; + } + case gui::shortcuts::shortcut::gw_log_mark: + { + static int count = 0; + mark_log.success("Made forced mark %d in log", ++count); + break; + } + case gui::shortcuts::shortcut::gw_mouse_lock: + { + toggle_mouselock(); + break; + } + case gui::shortcuts::shortcut::gw_screenshot: + { + screenshot_toggle = true; + break; + } + case gui::shortcuts::shortcut::gw_toggle_recording: + { + toggle_recording(); + break; + } + case gui::shortcuts::shortcut::gw_pause_play: + { + if (!m_disable_kb_hotkeys) { switch (Emu.GetStatus()) { @@ -290,18 +374,9 @@ void gs_frame::keyPressEvent(QKeyEvent *keyEvent) } break; } - case Qt::Key_S: + case gui::shortcuts::shortcut::gw_restart: { - if (keyEvent->modifiers() == Qt::ControlModifier && !m_disable_kb_hotkeys) - { - Emu.Kill(false, true); - return; - } - break; - } - case Qt::Key_R: - { - if (keyEvent->modifiers() == Qt::ControlModifier && !m_disable_kb_hotkeys) + if (!m_disable_kb_hotkeys) { if (Emu.IsStopped()) { @@ -314,133 +389,34 @@ void gs_frame::keyPressEvent(QKeyEvent *keyEvent) } break; } - case Qt::Key_C: + case gui::shortcuts::shortcut::gw_savestate: { - if (keyEvent->modifiers() == Qt::AltModifier && !m_disable_kb_hotkeys) + if (!m_disable_kb_hotkeys) + { + Emu.Kill(false, true); + return; + } + break; + } + case gui::shortcuts::shortcut::gw_rsx_capture: + { + if (!m_disable_kb_hotkeys) { g_user_asked_for_frame_capture = true; - return; } break; } - case Qt::Key_F10: + case gui::shortcuts::shortcut::gw_frame_limit: { - if (keyEvent->modifiers() == Qt::ControlModifier) - { - g_disable_frame_limit = !g_disable_frame_limit; - gui_log.warning("%s boost mode", g_disable_frame_limit.load() ? "Enabled" : "Disabled"); - return; - } - break; - } - case Qt::Key_F11: - { - utils::video_provider& video_provider = g_fxo->get(); - - if (g_recording_mode == recording_mode::cell) - { - gui_log.warning("A video recorder is already in use by cell. Regular recording can not proceed."); - m_video_encoder->stop(); - break; - } - - if (g_recording_mode.exchange(recording_mode::stopped) == recording_mode::rpcs3) - { - m_video_encoder->stop(); - - if (!video_provider.set_image_sink(nullptr, recording_mode::rpcs3)) - { - gui_log.warning("The video provider could not release the image sink. A sink with higher priority must have been set."); - } - - // Play a sound - if (const std::string sound_path = fs::get_config_dir() + "sounds/snd_recording.wav"; fs::is_file(sound_path)) - { - QSound::play(qstr(sound_path)); - } - else - { - QApplication::beep(); - } - - ensure(m_video_encoder->path().starts_with(fs::get_config_dir())); - const std::string shortpath = m_video_encoder->path().substr(fs::get_config_dir().size() - 1); // -1 for / - rsx::overlays::queue_message(tr("Recording saved: %0").arg(QString::fromStdString(shortpath)).toStdString()); - } - else - { - m_video_encoder->stop(); - - const std::string& id = Emu.GetTitleID(); - std::string video_path = fs::get_config_dir() + "recordings/"; - if (!id.empty()) - { - video_path += id + "/"; - } - - if (!fs::create_path(video_path) && fs::g_tls_error != fs::error::exist) - { - screenshot_log.error("Failed to create recordings path \"%s\" : %s", video_path, fs::g_tls_error); - break; - } - - if (!id.empty()) - { - video_path += id + "_"; - } - - video_path += "recording_" + date_time::current_time_narrow<'_'>() + ".mp4"; - - utils::video_encoder::frame_format output_format{}; - output_format.av_pixel_format = static_cast(g_cfg_recording.pixel_format.get()); - output_format.width = g_cfg_recording.width; - output_format.height = g_cfg_recording.height; - output_format.pitch = g_cfg_recording.width * 4; - - m_video_encoder->set_path(video_path); - m_video_encoder->set_framerate(g_cfg_recording.framerate); - m_video_encoder->set_video_bitrate(g_cfg_recording.video_bps); - m_video_encoder->set_video_codec(g_cfg_recording.video_codec); - m_video_encoder->set_max_b_frames(g_cfg_recording.max_b_frames); - m_video_encoder->set_gop_size(g_cfg_recording.gop_size); - m_video_encoder->set_output_format(output_format); - m_video_encoder->set_sample_rate(0); // TODO - m_video_encoder->set_audio_bitrate(0); // TODO - m_video_encoder->set_audio_codec(0); // TODO - m_video_encoder->encode(); - - if (m_video_encoder->has_error) - { - rsx::overlays::queue_message(tr("Recording not possible").toStdString()); - m_video_encoder->stop(); - break; - } - - if (!video_provider.set_image_sink(m_video_encoder, recording_mode::rpcs3)) - { - gui_log.warning("The video provider could not set the image sink. A sink with higher priority must have been set."); - rsx::overlays::queue_message(tr("Recording not possible").toStdString()); - m_video_encoder->stop(); - break; - } - - video_provider.set_pause_time(0); - - g_recording_mode = recording_mode::rpcs3; - - rsx::overlays::queue_message(tr("Recording started").toStdString()); - } - - break; - } - case Qt::Key_F12: - { - screenshot_toggle = true; + g_disable_frame_limit = !g_disable_frame_limit; + gui_log.warning("%s boost mode", g_disable_frame_limit.load() ? "Enabled" : "Disabled"); break; } default: + { break; } + } } void gs_frame::toggle_fullscreen() @@ -466,6 +442,105 @@ void gs_frame::toggle_fullscreen() }); } +void gs_frame::toggle_recording() +{ + utils::video_provider& video_provider = g_fxo->get(); + + if (g_recording_mode == recording_mode::cell) + { + gui_log.warning("A video recorder is already in use by cell. Regular recording can not proceed."); + m_video_encoder->stop(); + return; + } + + if (g_recording_mode.exchange(recording_mode::stopped) == recording_mode::rpcs3) + { + m_video_encoder->stop(); + + if (!video_provider.set_image_sink(nullptr, recording_mode::rpcs3)) + { + gui_log.warning("The video provider could not release the image sink. A sink with higher priority must have been set."); + } + + // Play a sound + if (const std::string sound_path = fs::get_config_dir() + "sounds/snd_recording.wav"; fs::is_file(sound_path)) + { + QSound::play(qstr(sound_path)); + } + else + { + QApplication::beep(); + } + + ensure(m_video_encoder->path().starts_with(fs::get_config_dir())); + const std::string shortpath = m_video_encoder->path().substr(fs::get_config_dir().size() - 1); // -1 for / + rsx::overlays::queue_message(tr("Recording saved: %0").arg(QString::fromStdString(shortpath)).toStdString()); + } + else + { + m_video_encoder->stop(); + + const std::string& id = Emu.GetTitleID(); + std::string video_path = fs::get_config_dir() + "recordings/"; + if (!id.empty()) + { + video_path += id + "/"; + } + + if (!fs::create_path(video_path) && fs::g_tls_error != fs::error::exist) + { + screenshot_log.error("Failed to create recordings path \"%s\" : %s", video_path, fs::g_tls_error); + return; + } + + if (!id.empty()) + { + video_path += id + "_"; + } + + video_path += "recording_" + date_time::current_time_narrow<'_'>() + ".mp4"; + + utils::video_encoder::frame_format output_format{}; + output_format.av_pixel_format = static_cast(g_cfg_recording.pixel_format.get()); + output_format.width = g_cfg_recording.width; + output_format.height = g_cfg_recording.height; + output_format.pitch = g_cfg_recording.width * 4; + + m_video_encoder->set_path(video_path); + m_video_encoder->set_framerate(g_cfg_recording.framerate); + m_video_encoder->set_video_bitrate(g_cfg_recording.video_bps); + m_video_encoder->set_video_codec(g_cfg_recording.video_codec); + m_video_encoder->set_max_b_frames(g_cfg_recording.max_b_frames); + m_video_encoder->set_gop_size(g_cfg_recording.gop_size); + m_video_encoder->set_output_format(output_format); + m_video_encoder->set_sample_rate(0); // TODO + m_video_encoder->set_audio_bitrate(0); // TODO + m_video_encoder->set_audio_codec(0); // TODO + m_video_encoder->encode(); + + if (m_video_encoder->has_error) + { + rsx::overlays::queue_message(tr("Recording not possible").toStdString()); + m_video_encoder->stop(); + return; + } + + if (!video_provider.set_image_sink(m_video_encoder, recording_mode::rpcs3)) + { + gui_log.warning("The video provider could not set the image sink. A sink with higher priority must have been set."); + rsx::overlays::queue_message(tr("Recording not possible").toStdString()); + m_video_encoder->stop(); + return; + } + + video_provider.set_pause_time(0); + + g_recording_mode = recording_mode::rpcs3; + + rsx::overlays::queue_message(tr("Recording started").toStdString()); + } +} + void gs_frame::toggle_mouselock() { // first we toggle the value @@ -985,7 +1060,7 @@ void gs_frame::handle_cursor(QWindow::Visibility visibility, bool from_event, bo update_cursor(); } -void gs_frame::MouseHideTimeout() +void gs_frame::mouse_hide_timeout() { // Our idle timeout occured, so we update the cursor if (m_hide_mouse_after_idletime && m_show_mouse) diff --git a/rpcs3/rpcs3qt/gs_frame.h b/rpcs3/rpcs3qt/gs_frame.h index 06bdc5eec3..89645dd867 100644 --- a/rpcs3/rpcs3qt/gs_frame.h +++ b/rpcs3/rpcs3qt/gs_frame.h @@ -1,5 +1,6 @@ #pragma once +#include "shortcut_handler.h" #include "util/types.hpp" #include "util/atomic.hpp" #include "util/media_utils.h" @@ -105,10 +106,12 @@ protected: private: void hide_on_close(); + void toggle_recording(); void toggle_mouselock(); void update_cursor(); void handle_cursor(QWindow::Visibility visibility, bool from_event, bool start_idle_timer); private Q_SLOTS: - void MouseHideTimeout(); + void mouse_hide_timeout(); + void handle_shortcut(gui::shortcuts::shortcut shortcut_key, const QKeySequence& key_sequence); }; diff --git a/rpcs3/rpcs3qt/gui_settings.h b/rpcs3/rpcs3qt/gui_settings.h index 39c6493743..fad84cd8fc 100644 --- a/rpcs3/rpcs3qt/gui_settings.h +++ b/rpcs3/rpcs3qt/gui_settings.h @@ -116,6 +116,7 @@ namespace gui const QString pad_settings = "PadSettings"; const QString config = "Config"; const QString log_viewer = "LogViewer"; + const QString sc = "Shortcuts"; const QString update_on = "true"; const QString update_off = "false"; @@ -258,6 +259,8 @@ namespace gui const gui_save lv_show_timestamps = gui_save(log_viewer, "show_timestamps", true); const gui_save lv_show_threads = gui_save(log_viewer, "show_threads", true); const gui_save lv_log_levels = gui_save(log_viewer, "log_levels", 0b11111111u); + + const gui_save sc_shortcuts = gui_save(sc, "shortcuts", QVariantMap()); } /** Class for GUI settings.. diff --git a/rpcs3/rpcs3qt/main_window.cpp b/rpcs3/rpcs3qt/main_window.cpp index 54723a5656..c875a23ba1 100644 --- a/rpcs3/rpcs3qt/main_window.cpp +++ b/rpcs3/rpcs3qt/main_window.cpp @@ -32,6 +32,7 @@ #include "ipc_settings_dialog.h" #include "shortcut_utils.h" #include "config_checker.h" +#include "shortcut_dialog.h" #include #include @@ -159,6 +160,9 @@ bool main_window::Init([[maybe_unused]] bool with_cli_boot) } } + m_shortcut_handler = new shortcut_handler(gui::shortcuts::shortcut_handler_id::main_window, this, m_gui_settings); + connect(m_shortcut_handler, &shortcut_handler::shortcut_activated, this, &main_window::handle_shortcut); + show(); // needs to be done before creating the thumbnail toolbar // enable play options if a recent game exists @@ -329,6 +333,63 @@ void main_window::ResizeIcons(int index) m_game_list_frame->ResizeIcons(index); } +void main_window::handle_shortcut(gui::shortcuts::shortcut shortcut_key, const QKeySequence& key_sequence) +{ + gui_log.notice("Main window registered shortcut: %s (%s)", shortcut_key, key_sequence.toString().toStdString()); + + const system_state status = Emu.GetStatus(); + + switch (shortcut_key) + { + case gui::shortcuts::shortcut::mw_toggle_fullscreen: + { + ui->toolbar_fullscreen->trigger(); + break; + } + case gui::shortcuts::shortcut::mw_exit_fullscreen: + { + if (isFullScreen()) + ui->toolbar_fullscreen->trigger(); + break; + } + case gui::shortcuts::shortcut::mw_refresh: + { + m_game_list_frame->Refresh(true); + break; + } + case gui::shortcuts::shortcut::mw_pause: + { + if (status == system_state::running) + Emu.Pause(); + break; + } + case gui::shortcuts::shortcut::mw_restart: + { + if (status == system_state::paused) + Emu.Resume(); + else if (status == system_state::ready) + Emu.Run(true); + break; + } + case gui::shortcuts::shortcut::mw_start: + { + if (!Emu.GetBoot().empty()) + Emu.Restart(); + break; + } + case gui::shortcuts::shortcut::mw_stop: + { + if (status != system_state::stopped) + Emu.GracefulShutdown(false, true); + break; + } + default: + { + break; + } + } +} + void main_window::OnPlayOrPause() { gui_log.notice("User triggered OnPlayOrPause"); @@ -941,7 +1002,6 @@ void main_window::HandlePackageInstallation(QStringList file_paths) pdlg.SetValue(pdlg.maximum()); std::this_thread::sleep_for(100ms); - if (true) { m_game_list_frame->Refresh(true); @@ -962,7 +1022,6 @@ void main_window::HandlePackageInstallation(QStringList file_paths) bootable_paths_installed[bootable_paths[index]] = packages[index].title_id; } - if (true) { pdlg.hide(); @@ -1684,7 +1743,6 @@ void main_window::OnEmuRun(bool /*start_playtime*/) const m_thumb_playPause->setIcon(m_icon_thumb_pause); #endif ui->sysPauseAct->setText(tr("&Pause")); - ui->sysPauseAct->setShortcut(QKeySequence("Ctrl+P")); ui->sysPauseAct->setIcon(m_icon_pause); ui->toolbar_start->setIcon(m_icon_pause); ui->toolbar_start->setText(tr("Pause")); @@ -1708,7 +1766,6 @@ void main_window::OnEmuResume() const m_thumb_playPause->setIcon(m_icon_thumb_pause); #endif ui->sysPauseAct->setText(tr("&Pause")); - ui->sysPauseAct->setShortcut(QKeySequence("Ctrl+P")); ui->sysPauseAct->setIcon(m_icon_pause); ui->toolbar_start->setIcon(m_icon_pause); ui->toolbar_start->setText(tr("Pause")); @@ -1726,7 +1783,6 @@ void main_window::OnEmuPause() const m_thumb_playPause->setIcon(m_icon_thumb_play); #endif ui->sysPauseAct->setText(tr("&Resume")); - ui->sysPauseAct->setShortcut(QKeySequence("Ctrl+R")); ui->sysPauseAct->setIcon(m_icon_play); ui->toolbar_start->setIcon(m_icon_play); ui->toolbar_start->setText(tr("Play")); @@ -1747,7 +1803,6 @@ void main_window::OnEmuStop() m_debugger_frame->UpdateUI(); ui->sysPauseAct->setText(Emu.IsReady() ? tr("&Play") : tr("&Resume")); - ui->sysPauseAct->setShortcut(QKeySequence("Ctrl+R")); ui->sysPauseAct->setIcon(m_icon_play); #ifdef _WIN32 m_thumb_playPause->setToolTip(play_tooltip); @@ -1808,7 +1863,6 @@ void main_window::OnEmuReady() const m_thumb_playPause->setIcon(m_icon_thumb_play); #endif ui->sysPauseAct->setText(Emu.IsReady() ? tr("&Play") : tr("&Resume")); - ui->sysPauseAct->setShortcut(QKeySequence("Ctrl+R")); ui->sysPauseAct->setIcon(m_icon_play); ui->toolbar_start->setIcon(m_icon_play); ui->toolbar_start->setText(tr("Play")); @@ -2286,6 +2340,13 @@ void main_window::CreateConnects() connect(ui->confEmuAct, &QAction::triggered, this, [open_settings]() { open_settings(7); }); connect(ui->confGuiAct, &QAction::triggered, this, [open_settings]() { open_settings(8); }); + connect(ui->confShortcutsAct, &QAction::triggered, [this]() + { + shortcut_dialog dlg(m_gui_settings, this); + connect(&dlg, &shortcut_dialog::saved, m_shortcut_handler, &shortcut_handler::update); + dlg.exec(); + }); + const auto open_pad_settings = [this] { pad_settings_dialog dlg(m_gui_settings, this); @@ -2996,23 +3057,6 @@ void main_window::CreateFirmwareCache() } } -void main_window::keyPressEvent(QKeyEvent *keyEvent) -{ - if (keyEvent->isAutoRepeat()) - { - return; - } - - if (((keyEvent->modifiers() & Qt::AltModifier) && keyEvent->key() == Qt::Key_Return) || (isFullScreen() && keyEvent->key() == Qt::Key_Escape)) - { - ui->toolbar_fullscreen->trigger(); - } - else if ((keyEvent->modifiers() & Qt::ControlModifier) && keyEvent->key() == Qt::Key_F5) - { - m_game_list_frame->Refresh(true); - } -} - void main_window::mouseDoubleClickEvent(QMouseEvent *event) { if (isFullScreen()) diff --git a/rpcs3/rpcs3qt/main_window.h b/rpcs3/rpcs3qt/main_window.h index 16b15b8978..da6ba42436 100644 --- a/rpcs3/rpcs3qt/main_window.h +++ b/rpcs3/rpcs3qt/main_window.h @@ -12,6 +12,7 @@ #include "update_manager.h" #include "settings.h" +#include "shortcut_handler.h" #include "Emu/System.h" #include @@ -127,9 +128,10 @@ private Q_SLOTS: void RemoveFirmwareCache(); void CreateFirmwareCache(); + void handle_shortcut(gui::shortcuts::shortcut shortcut_key, const QKeySequence& key_sequence); + protected: void closeEvent(QCloseEvent *event) override; - void keyPressEvent(QKeyEvent *keyEvent) override; void mouseDoubleClickEvent(QMouseEvent *event) override; void dropEvent(QDropEvent* event) override; void dragEnterEvent(QDragEnterEvent* event) override; @@ -187,4 +189,6 @@ private: update_manager m_updater; QAction* m_download_menu_action = nullptr; + + shortcut_handler* m_shortcut_handler = nullptr; }; diff --git a/rpcs3/rpcs3qt/main_window.ui b/rpcs3/rpcs3qt/main_window.ui index 2eb7379348..356a39eeb5 100644 --- a/rpcs3/rpcs3qt/main_window.ui +++ b/rpcs3/rpcs3qt/main_window.ui @@ -246,6 +246,9 @@ + + + @@ -1239,11 +1242,15 @@ Check Config + + + Shortcuts + + - diff --git a/rpcs3/rpcs3qt/shortcut_dialog.cpp b/rpcs3/rpcs3qt/shortcut_dialog.cpp new file mode 100644 index 0000000000..47a6551e41 --- /dev/null +++ b/rpcs3/rpcs3qt/shortcut_dialog.cpp @@ -0,0 +1,103 @@ +#include "shortcut_dialog.h" +#include "ui_shortcut_dialog.h" +#include "shortcut_settings.h" + +#include +#include +#include +#include +#include + +shortcut_dialog::shortcut_dialog(const std::shared_ptr gui_settings, QWidget* parent) + : QDialog(parent), ui(new Ui::shortcut_dialog), m_gui_settings(gui_settings) +{ + ui->setupUi(this); + + connect(ui->buttonBox, &QDialogButtonBox::rejected, this, &shortcut_dialog::reject); + connect(ui->buttonBox, &QDialogButtonBox::clicked, [this](QAbstractButton* button) + { + if (button == ui->buttonBox->button(QDialogButtonBox::Save)) + { + save(); + accept(); + } + else if (button == ui->buttonBox->button(QDialogButtonBox::Apply)) + { + save(); + } + }); + + shortcut_settings sc_settings{}; + + for (const auto& [shortcut_key, shortcut] : sc_settings.shortcut_map) + { + const QKeySequence key_sequence = sc_settings.get_key_sequence(shortcut, gui_settings); + + QLabel* label = new QLabel(shortcut.localized_name); + + QKeySequenceEdit* key_sequence_edit = new QKeySequenceEdit; + key_sequence_edit->setObjectName(shortcut.name); + key_sequence_edit->setMinimumWidth(label->sizeHint().width()); + key_sequence_edit->setKeySequence(key_sequence); + + m_values[shortcut.name] = key_sequence.toString(); + + connect(key_sequence_edit, &QKeySequenceEdit::keySequenceChanged, this, &shortcut_dialog::handle_change); + + QHBoxLayout* shortcut_layout = new QHBoxLayout; + shortcut_layout->addWidget(label); + shortcut_layout->addWidget(key_sequence_edit); + shortcut_layout->setStretch(0, 1); + shortcut_layout->setStretch(1, 1); + + const auto add_layout = [](QVBoxLayout* layout, QHBoxLayout* shortcut_layout) + { + layout->insertLayout(layout->count() - 1, shortcut_layout); // count() - 1 to ignore the vertical spacer + }; + + switch (shortcut.handler_id) + { + case gui::shortcuts::shortcut_handler_id::game_window: + { + add_layout(ui->game_window_layout, shortcut_layout); + break; + } + case gui::shortcuts::shortcut_handler_id::main_window: + { + add_layout(ui->main_window_layout, shortcut_layout); + break; + } + } + } + + const int min_width = std::max( + { + ui->main_window_group_box->sizeHint().width(), + ui->game_window_group_box->sizeHint().width(), + }); + + ui->main_window_group_box->setMinimumWidth(min_width); + ui->game_window_group_box->setMinimumWidth(min_width); +} + +shortcut_dialog::~shortcut_dialog() +{ + delete ui; +} + +void shortcut_dialog::save() +{ + shortcut_settings sc_settings{}; + + for (const auto& entry : m_values) + { + m_gui_settings->SetValue(sc_settings.get_shortcut_gui_save(entry.first), entry.second); + } + + Q_EMIT saved(); +} + +void shortcut_dialog::handle_change(const QKeySequence& keySequence) +{ + m_values[sender()->objectName()] = keySequence.toString(); +} diff --git a/rpcs3/rpcs3qt/shortcut_dialog.h b/rpcs3/rpcs3qt/shortcut_dialog.h new file mode 100644 index 0000000000..b13ddb94ff --- /dev/null +++ b/rpcs3/rpcs3qt/shortcut_dialog.h @@ -0,0 +1,32 @@ +#pragma once + +#include "gui_settings.h" + +#include + +namespace Ui +{ + class shortcut_dialog; +} + +class shortcut_dialog : public QDialog +{ + Q_OBJECT + +public: + explicit shortcut_dialog(const std::shared_ptr gui_settings, QWidget* parent = nullptr); + ~shortcut_dialog(); + +Q_SIGNALS: + void saved(); + +private: + void save(); + + Ui::shortcut_dialog* ui; + std::shared_ptr m_gui_settings; + std::map m_values; + +private Q_SLOTS: + void handle_change(const QKeySequence& keySequence); +}; diff --git a/rpcs3/rpcs3qt/shortcut_dialog.ui b/rpcs3/rpcs3qt/shortcut_dialog.ui new file mode 100644 index 0000000000..84b2672722 --- /dev/null +++ b/rpcs3/rpcs3qt/shortcut_dialog.ui @@ -0,0 +1,85 @@ + + + shortcut_dialog + + + + 0 + 0 + 822 + 364 + + + + Shortcuts + + + + + + + + Main Window Shortcuts + + + + + + Qt::Vertical + + + QSizePolicy::MinimumExpanding + + + + 0 + 0 + + + + + + + + + + + false + + + Game Window Shortcuts + + + + + + Qt::Vertical + + + QSizePolicy::MinimumExpanding + + + + 0 + 0 + + + + + + + + + + + + + QDialogButtonBox::Apply|QDialogButtonBox::Cancel|QDialogButtonBox::Save + + + + + + + + diff --git a/rpcs3/rpcs3qt/shortcut_handler.cpp b/rpcs3/rpcs3qt/shortcut_handler.cpp new file mode 100644 index 0000000000..178b4d28de --- /dev/null +++ b/rpcs3/rpcs3qt/shortcut_handler.cpp @@ -0,0 +1,77 @@ +#include "stdafx.h" +#include "shortcut_handler.h" +#include "Emu/System.h" + +LOG_CHANNEL(shortcut_log, "Shortcuts"); + +shortcut_handler::shortcut_handler(gui::shortcuts::shortcut_handler_id handler_id, QWidget* parent, const std::shared_ptr& gui_settings) + : QObject(parent), m_handler_id(handler_id), m_gui_settings(gui_settings) +{ + // Initialize shortcuts + shortcut_settings sc_settings{}; + + for (const auto& [shortcut_key, info] : sc_settings.shortcut_map) + { + // Skip shortcuts that weren't meant for this handler + if (handler_id != info.handler_id) + { + continue; + } + + const QKeySequence key_sequence = sc_settings.get_key_sequence(info, gui_settings); + QShortcut* shortcut = new QShortcut(key_sequence, parent); + shortcut->setAutoRepeat(false); + + shortcut_key_info key_info{}; + key_info.shortcut = shortcut; + key_info.info = info; + key_info.key_sequence = key_sequence; + + m_shortcuts[shortcut_key] = key_info; + + connect(shortcut, &QShortcut::activated, this, [this, key = shortcut_key]() + { + handle_shortcut(key, m_shortcuts[key].key_sequence); + }); + connect(shortcut, &QShortcut::activatedAmbiguously, this, [this, key = shortcut_key]() + { + // TODO: do not allow same shortcuts and remove this connect + // activatedAmbiguously will trigger if you have the same key sequence for several shortcuts + const QKeySequence& key_sequence = m_shortcuts[key].key_sequence; + shortcut_log.error("Shortcut activated ambiguously: %s (%s)", key, key_sequence.toString().toStdString()); + handle_shortcut(key, key_sequence); + }); + } +} + +void shortcut_handler::update() +{ + shortcut_log.notice("Updating shortcuts"); + + shortcut_settings sc_settings{}; + + for (const auto& [shortcut_key, info] : sc_settings.shortcut_map) + { + // Skip shortcuts that weren't meant for this handler + if (m_handler_id != info.handler_id || !m_shortcuts.contains(shortcut_key)) + { + continue; + } + + const QKeySequence key_sequence = sc_settings.get_key_sequence(info, m_gui_settings); + + shortcut_key_info& key_info = m_shortcuts[shortcut_key]; + key_info.key_sequence = key_sequence; + if (key_info.shortcut) + { + key_info.shortcut->setKey(key_sequence); + } + } +} + +void shortcut_handler::handle_shortcut(gui::shortcuts::shortcut shortcut_key, const QKeySequence& key_sequence) +{ + shortcut_log.notice("Shortcut pressed: %s (%s)", shortcut_key, key_sequence.toString().toStdString()); + + Q_EMIT shortcut_activated(shortcut_key, key_sequence); +} diff --git a/rpcs3/rpcs3qt/shortcut_handler.h b/rpcs3/rpcs3qt/shortcut_handler.h new file mode 100644 index 0000000000..c0f6597dce --- /dev/null +++ b/rpcs3/rpcs3qt/shortcut_handler.h @@ -0,0 +1,37 @@ +#pragma once + +#include "gui_settings.h" +#include "shortcut_settings.h" + +#include +#include + +#include + +class shortcut_handler : public QObject +{ + Q_OBJECT + +public: + shortcut_handler(gui::shortcuts::shortcut_handler_id handler_id, QWidget* parent, const std::shared_ptr& gui_settings); + +Q_SIGNALS: + void shortcut_activated(gui::shortcuts::shortcut shortcut_key, const QKeySequence& key_sequence); + +public Q_SLOTS: + void update(); + +private: + void handle_shortcut(gui::shortcuts::shortcut shortcut_key, const QKeySequence& key_sequence); + + gui::shortcuts::shortcut_handler_id m_handler_id; + std::shared_ptr m_gui_settings; + + struct shortcut_key_info + { + QShortcut* shortcut = nullptr; + QKeySequence key_sequence{}; + shortcut_info info{}; + }; + std::map m_shortcuts; +}; diff --git a/rpcs3/rpcs3qt/shortcut_settings.cpp b/rpcs3/rpcs3qt/shortcut_settings.cpp new file mode 100644 index 0000000000..b90daa494b --- /dev/null +++ b/rpcs3/rpcs3qt/shortcut_settings.cpp @@ -0,0 +1,90 @@ +#include "shortcut_settings.h" + +using namespace gui::shortcuts; + +template <> +void fmt_class_string::format(std::string& out, u64 arg) +{ + format_enum(out, arg, [](gui::shortcuts::shortcut value) + { + switch (value) + { + case shortcut::mw_start: return "mw_start"; + case shortcut::mw_stop: return "mw_stop"; + case shortcut::mw_pause: return "mw_pause"; + case shortcut::mw_restart: return "mw_restart"; + case shortcut::mw_toggle_fullscreen: return "mw_toggle_fullscreen"; + case shortcut::mw_exit_fullscreen: return "mw_exit_fullscreen"; + case shortcut::mw_refresh: return "mw_refresh"; + case shortcut::gw_toggle_fullscreen: return "gw_toggle_fullscreen"; + case shortcut::gw_exit_fullscreen: return "gw_exit_fullscreen"; + case shortcut::gw_log_mark: return "gw_log_mark"; + case shortcut::gw_mouse_lock: return "gw_mouse_lock"; + case shortcut::gw_screenshot: return "gw_screenshot"; + case shortcut::gw_toggle_recording: return "gw_toggle_recording"; + case shortcut::gw_pause_play: return "gw_pause_play"; + case shortcut::gw_savestate: return "gw_savestate"; + case shortcut::gw_restart: return "gw_restart"; + case shortcut::gw_rsx_capture: return "gw_rsx_capture"; + case shortcut::gw_frame_limit: return "gw_frame_limit"; + case shortcut::count: return "count"; + }; + + return unknown; + }); +} + +shortcut_settings::shortcut_settings() + : shortcut_map({ + { shortcut::mw_start, shortcut_info{ "main_window_start", tr("Start"), "Ctrl+E", shortcut_handler_id::main_window } }, + { shortcut::mw_stop, shortcut_info{ "main_window_stop", tr("Stop"), "Ctrl+S", shortcut_handler_id::main_window } }, + { shortcut::mw_pause, shortcut_info{ "main_window_pause", tr("Pause"), "Ctrl+P", shortcut_handler_id::main_window } }, + { shortcut::mw_restart, shortcut_info{ "main_window_restart", tr("Restart"), "Ctrl+R", shortcut_handler_id::main_window } }, + { shortcut::mw_toggle_fullscreen, shortcut_info{ "main_window_toggle_fullscreen", tr("Toggle Fullscreen"), "Alt+Return", shortcut_handler_id::main_window } }, + { shortcut::mw_exit_fullscreen, shortcut_info{ "main_window_exit_fullscreen", tr("Exit Fullscreen"), "Esc", shortcut_handler_id::main_window } }, + { shortcut::mw_refresh, shortcut_info{ "main_window_refresh", tr("Refresh"), "Ctrl+F5", shortcut_handler_id::main_window } }, + { shortcut::gw_toggle_fullscreen, shortcut_info{ "game_window_toggle_fullscreen", tr("Toggle Fullscreen"), "Alt+Return", shortcut_handler_id::game_window } }, + { shortcut::gw_exit_fullscreen, shortcut_info{ "game_window_exit_fullscreen", tr("Exit Fullscreen"), "Esc", shortcut_handler_id::game_window } }, + { shortcut::gw_log_mark, shortcut_info{ "game_window_log_mark", tr("Add Log Mark"), "Alt+L", shortcut_handler_id::game_window } }, + { shortcut::gw_mouse_lock, shortcut_info{ "game_window_mouse_lock", tr("Mouse lock"), "Ctrl+L", shortcut_handler_id::game_window } }, + { shortcut::gw_toggle_recording, shortcut_info{ "game_window_toggle_recording", tr("Start/Stop Recording"), "F11", shortcut_handler_id::game_window } }, + { shortcut::gw_screenshot, shortcut_info{ "game_window_screenshot", tr("Screenshot"), "F12", shortcut_handler_id::game_window } }, + { shortcut::gw_pause_play, shortcut_info{ "game_window_pause_play", tr("Pause/Play"), "Ctrl+P", shortcut_handler_id::game_window } }, + { shortcut::gw_savestate, shortcut_info{ "game_window_savestate", tr("Savestate"), "Ctrl+S", shortcut_handler_id::game_window } }, + { shortcut::gw_restart, shortcut_info{ "game_window_restart", tr("Restart"), "Ctrl+R", shortcut_handler_id::game_window } }, + { shortcut::gw_rsx_capture, shortcut_info{ "game_window_rsx_capture", tr("RSX Capture"), "Alt+C", shortcut_handler_id::game_window } }, + { shortcut::gw_frame_limit, shortcut_info{ "game_window_gw_frame_limit", tr("Toggle Framelimit"), "Ctrl+F10", shortcut_handler_id::game_window } }, + }) +{ +} + +shortcut_settings::~shortcut_settings() +{ +} + +gui_save shortcut_settings::get_shortcut_gui_save(const QString& shortcut_name) +{ + const auto it = std::find_if(shortcut_map.begin(), shortcut_map.end(), [&](const auto& entry) { return entry.second.name == shortcut_name; }); + + if (it != shortcut_map.cend()) + { + return gui_save(gui::sc, it->second.name, it->second.key_sequence); + } + + return gui_save(); +} + +QKeySequence shortcut_settings::get_key_sequence(const shortcut_info& entry, const std::shared_ptr& gui_settings) +{ + const QString saved_value = gui_settings->GetValue(get_shortcut_gui_save(entry.name)).toString(); + + QKeySequence key_sequence = QKeySequence::fromString(saved_value); + + if (key_sequence.isEmpty()) + { + // Use the default shortcut if no shortcut was configured + key_sequence = QKeySequence::fromString(entry.key_sequence); + } + + return key_sequence; +} diff --git a/rpcs3/rpcs3qt/shortcut_settings.h b/rpcs3/rpcs3qt/shortcut_settings.h new file mode 100644 index 0000000000..cd87fd5df4 --- /dev/null +++ b/rpcs3/rpcs3qt/shortcut_settings.h @@ -0,0 +1,65 @@ +#pragma once + +#include "gui_settings.h" + +#include + +namespace gui +{ + namespace shortcuts + { + enum class shortcut_handler_id : int + { + main_window, + game_window, + }; + + enum class shortcut : int + { + mw_start, + mw_stop, + mw_pause, + mw_restart, + mw_toggle_fullscreen, + mw_exit_fullscreen, + mw_refresh, + + gw_toggle_fullscreen, + gw_exit_fullscreen, + gw_log_mark, + gw_mouse_lock, + gw_screenshot, + gw_toggle_recording, + gw_pause_play, + gw_savestate, + gw_restart, + gw_rsx_capture, + gw_frame_limit, + + count + }; + } +} + +struct shortcut_info +{ + QString name; + QString localized_name; + QString key_sequence; + gui::shortcuts::shortcut_handler_id handler_id{}; +}; + +class shortcut_settings : public QObject +{ + Q_OBJECT + +public: + shortcut_settings(); + ~shortcut_settings(); + + const std::map shortcut_map; + + gui_save get_shortcut_gui_save(const QString& shortcut_name); + + QKeySequence get_key_sequence(const shortcut_info& entry, const std::shared_ptr& gui_settings); +};