qthost.h (11167B)
1 // SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com> 2 // SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0) 3 4 #pragma once 5 6 #include "qtutils.h" 7 8 #include "core/game_list.h" 9 #include "core/host.h" 10 #include "core/system.h" 11 #include "core/types.h" 12 13 #include "util/gpu_device.h" 14 #include "util/input_manager.h" 15 16 #include <QtCore/QByteArray> 17 #include <QtCore/QMetaType> 18 #include <QtCore/QObject> 19 #include <QtCore/QSemaphore> 20 #include <QtCore/QString> 21 #include <QtCore/QThread> 22 #include <QtGui/QIcon> 23 24 #include <atomic> 25 #include <functional> 26 #include <map> 27 #include <memory> 28 #include <mutex> 29 #include <optional> 30 #include <string> 31 #include <utility> 32 #include <vector> 33 34 class ByteStream; 35 36 class QActionGroup; 37 class QEventLoop; 38 class QMenu; 39 class QWidget; 40 class QTimer; 41 class QTranslator; 42 43 class INISettingsInterface; 44 45 class GPUDevice; 46 47 class MainWindow; 48 class DisplayWidget; 49 50 namespace Achievements { 51 enum class LoginRequestReason; 52 } 53 54 Q_DECLARE_METATYPE(std::optional<bool>); 55 Q_DECLARE_METATYPE(std::shared_ptr<SystemBootParameters>); 56 57 class EmuThread : public QThread 58 { 59 Q_OBJECT 60 61 public: 62 /// This class is a scoped lock on the system, which prevents it from running while 63 /// the object exists. Its purpose is to be used for blocking/modal popup boxes, 64 /// where the VM needs to exit fullscreen temporarily. 65 class SystemLock 66 { 67 public: 68 SystemLock(SystemLock&& lock); 69 SystemLock(const SystemLock&) = delete; 70 ~SystemLock(); 71 72 /// Cancels any pending unpause/fullscreen transition. 73 /// Call when you're going to destroy the system anyway. 74 void cancelResume(); 75 76 private: 77 SystemLock(bool was_paused, bool was_fullscreen); 78 friend EmuThread; 79 80 bool m_was_paused; 81 bool m_was_fullscreen; 82 }; 83 84 public: 85 explicit EmuThread(QThread* ui_thread); 86 ~EmuThread(); 87 88 static void start(); 89 static void stop(); 90 91 ALWAYS_INLINE bool isOnThread() const { return QThread::currentThread() == this; } 92 ALWAYS_INLINE bool isOnUIThread() const { return QThread::currentThread() == m_ui_thread; } 93 94 ALWAYS_INLINE QEventLoop* getEventLoop() const { return m_event_loop; } 95 96 ALWAYS_INLINE bool isFullscreen() const { return m_is_fullscreen; } 97 ALWAYS_INLINE bool isRenderingToMain() const { return m_is_rendering_to_main; } 98 ALWAYS_INLINE bool isSurfaceless() const { return m_is_surfaceless; } 99 ALWAYS_INLINE bool isRunningFullscreenUI() const { return m_run_fullscreen_ui; } 100 101 std::optional<WindowInfo> acquireRenderWindow(bool recreate_window); 102 void connectDisplaySignals(DisplayWidget* widget); 103 void releaseRenderWindow(); 104 105 void startBackgroundControllerPollTimer(); 106 void stopBackgroundControllerPollTimer(); 107 void wakeThread(); 108 109 bool shouldRenderToMain() const; 110 void loadSettings(SettingsInterface& si); 111 void checkForSettingsChanges(const Settings& old_settings); 112 113 void bootOrLoadState(std::string path); 114 115 void updatePerformanceCounters(); 116 void resetPerformanceCounters(); 117 118 /// Locks the system by pausing it, while a popup dialog is displayed. 119 /// This version is **only** for the system thread. UI thread should use the MainWindow variant. 120 SystemLock pauseAndLockSystem(); 121 122 Q_SIGNALS: 123 void errorReported(const QString& title, const QString& message); 124 bool messageConfirmed(const QString& title, const QString& message); 125 void statusMessage(const QString& message); 126 void debuggerMessageReported(const QString& message); 127 void settingsResetToDefault(bool system, bool controller); 128 void onInputDevicesEnumerated(const std::vector<std::pair<std::string, std::string>>& devices); 129 void onInputDeviceConnected(const std::string& identifier, const std::string& device_name); 130 void onInputDeviceDisconnected(const std::string& identifier); 131 void onVibrationMotorsEnumerated(const QList<InputBindingKey>& motors); 132 void systemStarting(); 133 void systemStarted(); 134 void systemDestroyed(); 135 void systemPaused(); 136 void systemResumed(); 137 void gameListRefreshed(); 138 std::optional<WindowInfo> onAcquireRenderWindowRequested(bool recreate_window, bool fullscreen, bool render_to_main, 139 bool surfaceless, bool use_main_window_pos); 140 void onResizeRenderWindowRequested(qint32 width, qint32 height); 141 void onReleaseRenderWindowRequested(); 142 void focusDisplayWidgetRequested(); 143 void runningGameChanged(const QString& filename, const QString& game_serial, const QString& game_title); 144 void inputProfileLoaded(); 145 void mouseModeRequested(bool relative, bool hide_cursor); 146 void fullscreenUIStateChange(bool running); 147 void achievementsLoginRequested(Achievements::LoginRequestReason reason); 148 void achievementsRefreshed(quint32 id, const QString& game_info_string); 149 void achievementsChallengeModeChanged(bool enabled); 150 void cheatEnabled(quint32 index, bool enabled); 151 void mediaCaptureStarted(); 152 void mediaCaptureStopped(); 153 154 /// Big Picture UI requests. 155 void onCoverDownloaderOpenRequested(); 156 157 public Q_SLOTS: 158 void setDefaultSettings(bool system = true, bool controller = true); 159 void applySettings(bool display_osd_messages = false); 160 void reloadGameSettings(bool display_osd_messages = false); 161 void updateEmuFolders(); 162 void updateControllerSettings(); 163 void reloadInputSources(); 164 void reloadInputBindings(); 165 void reloadInputDevices(); 166 void closeInputSources(); 167 void enumerateInputDevices(); 168 void enumerateVibrationMotors(); 169 void startFullscreenUI(); 170 void stopFullscreenUI(); 171 void bootSystem(std::shared_ptr<SystemBootParameters> params); 172 void resumeSystemFromMostRecentState(); 173 void shutdownSystem(bool save_state, bool check_memcard_busy); 174 void resetSystem(bool check_memcard_busy); 175 void setSystemPaused(bool paused, bool wait_until_paused = false); 176 void changeDisc(const QString& new_disc_filename, bool reset_system, bool check_memcard_busy); 177 void changeDiscFromPlaylist(quint32 index); 178 void loadState(const QString& filename); 179 void loadState(bool global, qint32 slot); 180 void saveState(const QString& filename, bool block_until_done = false); 181 void saveState(bool global, qint32 slot, bool block_until_done = false); 182 void undoLoadState(); 183 void setAudioOutputVolume(int volume, int fast_forward_volume); 184 void setAudioOutputMuted(bool muted); 185 void startDumpingAudio(); 186 void stopDumpingAudio(); 187 void singleStepCPU(); 188 void dumpRAM(const QString& filename); 189 void dumpVRAM(const QString& filename); 190 void dumpSPURAM(const QString& filename); 191 void saveScreenshot(); 192 void redrawDisplayWindow(); 193 void toggleFullscreen(); 194 void setFullscreen(bool fullscreen, bool allow_render_to_main); 195 void setSurfaceless(bool surfaceless); 196 void requestDisplaySize(float scale); 197 void setCheatEnabled(quint32 index, bool enabled); 198 void applyCheat(quint32 index); 199 void reloadPostProcessingShaders(); 200 void updatePostProcessingSettings(); 201 void clearInputBindStateFromSource(InputBindingKey key); 202 203 private Q_SLOTS: 204 void stopInThread(); 205 void onDisplayWindowMouseButtonEvent(int button, bool pressed); 206 void onDisplayWindowMouseWheelEvent(const QPoint& delta_angle); 207 void onDisplayWindowResized(int width, int height, float scale); 208 void onDisplayWindowKeyEvent(int key, bool pressed); 209 void onDisplayWindowTextEntered(const QString& text); 210 void doBackgroundControllerPoll(); 211 void runOnEmuThread(std::function<void()> callback); 212 213 protected: 214 void run() override; 215 216 private: 217 using InputButtonHandler = std::function<void(bool)>; 218 using InputAxisHandler = std::function<void(float)>; 219 220 void createBackgroundControllerPollTimer(); 221 void destroyBackgroundControllerPollTimer(); 222 void setInitialState(std::optional<bool> override_fullscreen); 223 void confirmActionIfMemoryCardBusy(const QString& action, bool cancel_resume_on_accept, 224 std::function<void(bool)> callback) const; 225 226 QThread* m_ui_thread; 227 QSemaphore m_started_semaphore; 228 QEventLoop* m_event_loop = nullptr; 229 QTimer* m_background_controller_polling_timer = nullptr; 230 231 bool m_shutdown_flag = false; 232 bool m_run_fullscreen_ui = false; 233 bool m_is_rendering_to_main = false; 234 bool m_is_fullscreen = false; 235 bool m_is_exclusive_fullscreen = false; 236 bool m_lost_exclusive_fullscreen = false; 237 bool m_is_surfaceless = false; 238 bool m_save_state_on_shutdown = false; 239 240 bool m_was_paused_by_focus_loss = false; 241 242 float m_last_speed = std::numeric_limits<float>::infinity(); 243 float m_last_game_fps = std::numeric_limits<float>::infinity(); 244 float m_last_video_fps = std::numeric_limits<float>::infinity(); 245 u32 m_last_render_width = std::numeric_limits<u32>::max(); 246 u32 m_last_render_height = std::numeric_limits<u32>::max(); 247 RenderAPI m_last_render_api = RenderAPI::None; 248 bool m_last_hardware_renderer = false; 249 }; 250 251 extern EmuThread* g_emu_thread; 252 253 namespace QtHost { 254 /// Default theme name for the platform. 255 const char* GetDefaultThemeName(); 256 257 /// Default language for the platform. 258 const char* GetDefaultLanguage(); 259 260 /// Sets application theme according to settings. 261 void UpdateApplicationTheme(); 262 263 /// Returns true if the application theme is using dark colours. 264 bool IsDarkApplicationTheme(); 265 266 /// Sets the icon theme, based on the current style (light/dark). 267 void SetIconThemeFromStyle(); 268 269 /// Sets batch mode (exit after game shutdown). 270 bool InBatchMode(); 271 272 /// Sets NoGUI mode (implys batch mode, does not display main window, exits on shutdown). 273 bool InNoGUIMode(); 274 275 /// Executes a function on the UI thread. 276 void RunOnUIThread(const std::function<void()>& func, bool block = false); 277 278 /// Default language for the platform. 279 const char* GetDefaultLanguage(); 280 281 /// Call when the language changes. 282 void UpdateApplicationLanguage(QWidget* dialog_parent); 283 284 /// Returns the application name and version, optionally including debug/devel config indicator. 285 QString GetAppNameAndVersion(); 286 287 /// Returns the debug/devel config indicator. 288 QString GetAppConfigSuffix(); 289 290 /// Returns the main application icon. 291 const QIcon& GetAppIcon(); 292 293 /// Returns the base path for resources. This may be : prefixed, if we're using embedded resources. 294 QString GetResourcesBasePath(); 295 296 /// Returns the base settings interface. Should lock before manipulating. 297 INISettingsInterface* GetBaseSettingsInterface(); 298 299 /// Saves a game settings interface. 300 bool SaveGameSettings(SettingsInterface* sif, bool delete_if_empty); 301 302 /// Downloads the specified URL to the provided path. 303 bool DownloadFile(QWidget* parent, const QString& title, std::string url, const char* path); 304 305 /// Downloads the specified URL, and extracts the specified file from a zip to a provided path. 306 bool DownloadFileFromZip(QWidget* parent, const QString& title, std::string url, const char* zip_filename, 307 const char* output_path); 308 309 /// Thread-safe settings access. 310 void QueueSettingsSave(); 311 312 /// Returns true if the debug menu and functionality should be shown. 313 bool ShouldShowDebugOptions(); 314 315 /// VM state, safe to access on UI thread. 316 bool IsSystemValid(); 317 bool IsSystemPaused(); 318 319 /// Accessors for game information. 320 const QString& GetCurrentGameTitle(); 321 const QString& GetCurrentGameSerial(); 322 const QString& GetCurrentGamePath(); 323 } // namespace QtHost