// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin // SPDX-License-Identifier: CC-BY-NC-ND-4.0 #pragma once #include "qtutils.h" #include "core/game_list.h" #include "core/host.h" #include "core/input_types.h" #include "core/system.h" #include "core/types.h" #include "util/gpu_device.h" #include "util/imgui_manager.h" #include "util/input_manager.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include class QActionGroup; class QEventLoop; class QMenu; class QWidget; class QTimer; class QTranslator; class INISettingsInterface; enum class MediaCaptureMode : u8; enum class RenderAPI : u8; class GPUDevice; class GPUBackend; class MainWindow; class DisplayWidget; class InputDeviceListModel; namespace Host { enum class NumberFormatType : u8; } namespace Achievements { enum class LoginRequestReason; } Q_DECLARE_METATYPE(std::optional); Q_DECLARE_METATYPE(std::shared_ptr); class CoreThread : public QThread { Q_OBJECT public: CoreThread(); ~CoreThread(); void stop(); ALWAYS_INLINE QEventLoop* getEventLoop() const { return m_event_loop; } ALWAYS_INLINE InputDeviceListModel* getInputDeviceListModel() const { return m_input_device_list_model.get(); } ALWAYS_INLINE bool isFullscreenUIStarted() const { return m_is_fullscreen_ui_started; } std::optional acquireRenderWindow(RenderAPI render_api, bool fullscreen, bool exclusive_fullscreen, Error* error); void connectRenderWindowSignals(DisplayWidget* widget); void releaseRenderWindow(); void startIdleUpdateTimer(); void stopIdleUpdateTimer(); void updateIdleTimerInterval(); void wakeThread(); void updatePerformanceCounters(const GPUBackend* gpu_backend); void resetPerformanceCounters(); /// Queues an input event for an additional render window to the emu thread. void queueAuxiliaryRenderWindowInputEvent(Host::AuxiliaryRenderWindowUserData userdata, Host::AuxiliaryRenderWindowEvent event, Host::AuxiliaryRenderWindowEventParam param1 = {}, Host::AuxiliaryRenderWindowEventParam param2 = {}, Host::AuxiliaryRenderWindowEventParam param3 = {}); Q_SIGNALS: void errorReported(const QString& title, const QString& message); void statusMessage(const QString& message); void settingsResetToDefault(bool host, bool system, bool controller); void settingsReloaded(); void systemStarting(); void systemStarted(); void systemStopping(); void systemDestroyed(); void systemPaused(); void systemResumed(); void systemGameChanged(const QString& path, const QString& game_serial, const QString& game_title); void systemUndoStateAvailabilityChanged(bool available, quint64 timestamp); void gameListRowsChanged(const QList& rows_changed); std::optional onAcquireRenderWindowRequested(RenderAPI render_api, bool fullscreen, bool exclusive_fullscreen, Error* error); void onResizeRenderWindowRequested(qint32 width, qint32 height); void onReleaseRenderWindowRequested(); void inputProfileLoaded(); void mouseModeRequested(bool relative, bool hide_cursor, bool ignore_double_click); void fullscreenUIStartedOrStopped(bool running); void achievementsLoginRequested(Achievements::LoginRequestReason reason); void achievementsLoginSuccess(const QString& username, quint32 points, quint32 casual_points, quint32 unread_messages); void achievementsActiveChanged(bool active); void achievementsHardcoreModeChanged(bool enabled); void mediaCaptureStarted(MediaCaptureMode mode); void mediaCaptureStopped(); bool onCreateAuxiliaryRenderWindow(RenderAPI render_api, qint32 x, qint32 y, quint32 width, quint32 height, const QString& title, const QString& icon_name, Host::AuxiliaryRenderWindowUserData userdata, Host::AuxiliaryRenderWindowHandle* handle, WindowInfo* wi, Error* error); void onDestroyAuxiliaryRenderWindow(Host::AuxiliaryRenderWindowHandle handle, QPoint* pos, QSize* size); public: void setDefaultSettings(bool host, bool system, bool controller); void applySettings(bool display_osd_messages = false); void reloadGameSettings(bool display_osd_messages = false); void reloadInputProfile(bool display_osd_messages = false); void reloadCheats(bool reload_files, bool reload_enabled_list, bool verbose, bool verbose_if_changed); void updateEmuFolders(); void updateControllerSettings(); void reloadInputBindings(); void startFullscreenUI(); void stopFullscreenUI(); void exitFullscreenUI(); void bootSystem(std::shared_ptr params); void bootOrSwitchNonDisc(const QString& path); void shutdownSystem(bool save_state, bool check_safety); void resetSystem(bool check_memcard_busy); void setSystemPaused(bool paused); void changeDisc(const QString& new_disc_path, bool reset_system, bool check_memcard_busy); void changeDiscFromPlaylist(quint32 index); void setLidState(bool manual_control, bool manual_state); void loadState(const QString& path); void loadState(bool global, qint32 slot); void saveState(const QString& path); void saveState(bool global, qint32 slot); void undoLoadState(); void setAudioOutputVolume(int volume, int fast_forward_volume); void setAudioOutputMuted(bool muted); void singleStepCPU(); void dumpRAM(const QString& path); void dumpVRAM(const QString& path); void dumpSPURAM(const QString& path); void saveScreenshot(); void applicationStateChanged(Qt::ApplicationState state); void redrawRenderWindow(); void toggleFullscreen(); void setFullscreen(bool fullscreen); void setFullscreenWithCompletionHandler(bool fullscreen, std::function completion_handler); void recreateRenderWindow(); void requestDisplaySize(float scale); void applyCheat(const QString& name); void reloadPostProcessingShaders(); void updatePostProcessingSettings(bool display, bool internal, bool force_reload); void reloadTextureReplacements(); void captureGPUFrameDump(); void startControllerTest(); void openGamePropertiesForCurrentGame(const QString& category = {}); void setHTTPDownloaderActive(bool active); void setVideoThreadRunIdle(bool active); void updateFullscreenUITheme(); void runOnCoreThread(const std::function& callback); protected: void run() override; private: int getBackgroundControllerPollInterval() const; void stopInThread(); void onRenderWindowMouseMoveAbsoluteEvent(float x, float y); void onRenderWindowMouseMoveRelativeEvent(float dx, float dy); void onRenderWindowMouseButtonEvent(int button, bool pressed); void onRenderWindowMouseWheelEvent(float dx, float dy); void onRenderWindowResized(int width, int height, float scale, float refresh_rate); void onRenderWindowKeyEvent(int key, bool pressed); void onRenderWindowTextEntered(const QString& text); void processAuxiliaryRenderWindowInputEvent(void* userdata, quint32 event, quint32 param1, quint32 param2, quint32 param3); void createIdleUpdateTimer(); void destroyIdleUpdateTimer(); void bootOrLoadState(std::string path); void confirmActionWithSafetyCheck(const QString& action, bool check_achievements, bool cancel_resume_on_accept, std::function callback) const; QThread* m_ui_thread; QEventLoop* m_event_loop = nullptr; QTimer* m_idle_update_timer = nullptr; std::unique_ptr m_input_device_list_model; bool m_shutdown_flag = false; bool m_http_downloader_active = false; bool m_video_thread_run_idle = false; bool m_is_fullscreen_ui_started = false; bool m_was_paused_by_focus_loss = false; RenderAPI m_last_render_api = RenderAPI::None; bool m_last_hardware_renderer = false; float m_last_speed = std::numeric_limits::infinity(); float m_last_game_fps = std::numeric_limits::infinity(); float m_last_video_fps = std::numeric_limits::infinity(); u32 m_last_render_width = std::numeric_limits::max(); u32 m_last_render_height = std::numeric_limits::max(); }; class InputDeviceListModel final : public QAbstractListModel { Q_OBJECT public: struct Device { InputBindingKey key; QString identifier; QString display_name; }; struct Effect { InputBindingInfo::Type type; InputBindingKey key; std::string name; std::string display_name; }; using DeviceList = QList; using EffectList = QList; explicit InputDeviceListModel(QObject* parent = nullptr); ~InputDeviceListModel() override; // Safe to access on UI thread. ALWAYS_INLINE const DeviceList& getDeviceList() const { return m_devices; } ALWAYS_INLINE const EffectList& getEffectList() const { return m_effects; } /// Returns the device name for the specified key, or an empty string if not found. QString getDeviceName(const InputBindingKey& key); /// Returns whether any effects are available for the specified type. bool hasEffectsOfType(InputBindingInfo::Type type); int rowCount(const QModelIndex& parent = QModelIndex()) const override; QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; // NOTE: Should only be called on core thread. void enumerateDevices(); void onDeviceConnected(const InputBindingKey& key, const QString& identifier, const QString& device_name, const EffectList& effects); void onDeviceDisconnected(const InputBindingKey& key, const QString& identifier); static QIcon getIconForKey(const InputBindingKey& key); private: void resetLists(const DeviceList& devices, const EffectList& motors); DeviceList m_devices; EffectList m_effects; }; class QtAsyncTask : public QObject { Q_OBJECT public: using CompletionCallback = std::function; using WorkCallback = std::function; ~QtAsyncTask(); static void create(QObject* owner, WorkCallback callback); Q_SIGNALS: void completed(QtAsyncTask* self); private: explicit QtAsyncTask(WorkCallback callback); std::variant m_callback; }; extern CoreThread* g_core_thread; namespace QtHost { /// Returns the locale to use for date/time formatting, etc. const QLocale& GetApplicationLocale(); /// Default theme name for the platform. const char* GetDefaultThemeName(); /// Sets application theme according to settings. void UpdateApplicationTheme(); /// Returns true if the application theme is using dark colours. bool IsDarkApplicationTheme(); /// Returns true if the application theme is using stylesheet overrides. bool HasGlobalStylesheet(); /// Sets the icon theme, based on the current style (light/dark). void UpdateThemeOnStyleChange(); /// Returns true if the specified theme name uses style sheets. bool IsStylesheetTheme(std::string_view theme_name); /// Returns a list of custom themes available on the system. QStringList GetCustomThemeList(); /// Sets batch mode (exit after game shutdown). bool InBatchMode(); /// Sets NoGUI mode (implies batch mode, does not display main window, exits on shutdown). bool InNoGUIMode(); /// Returns true if display widgets need to wrapped in a container, thanks to Wayland stupidity. bool IsDisplayWidgetContainerNeeded(); /// Call when the language changes. void UpdateApplicationLanguage(QWidget* dialog_parent); /// Returns the application name and version, optionally including debug/devel config indicator. QString GetAppNameAndVersion(); /// Returns the debug/devel config indicator. QString GetAppConfigSuffix(); /// Returns the main application icon. const QIcon& GetAppIcon(); /// Returns a higher resolution logo for the application. QPixmap GetAppLogo(); /// Returns the base path for resources. This may be : prefixed, if we're using embedded resources. QString GetResourcesBasePath(); /// Returns the path to the specified resource. std::string GetResourcePath(std::string_view name, bool allow_override); QString GetResourceQPath(std::string_view name, bool allow_override); /// Reads an embedded resource file to a string. std::optional ReadResourceFileToString(std::string_view name, bool allow_override, Error* error); /// Returns the font family for the bundled Roboto font. const QStringList& GetRobotoFontFamilies(); /// Returns the font for the bundled fixed-width font. const QFont& GetFixedFont(); /// Saves a game settings interface. bool SaveGameSettings(SettingsInterface* sif, bool delete_if_empty); /// Formats a number according to the current locale. QString FormatNumber(Host::NumberFormatType type, s64 value); QString FormatNumber(Host::NumberFormatType type, double value); /// Downloads the specified URL to the provided path. void DownloadFile(QWidget* parent, std::string url, std::string path, std::function completion_callback); /// Thread-safe settings access. void QueueSettingsSave(); /// Returns true if the debug menu and functionality should be shown. bool ShouldShowDebugOptions(); /// VM state, safe to access on UI thread. bool IsSystemValid(); bool IsSystemValidOrStarting(); bool IsSystemPaused(); /// Returns true if fullscreen UI is requested. bool IsFullscreenUIStarted(); /// Returns true if any lock is in place. bool IsSystemLocked(); /// Accessors for game information. const QString& GetCurrentGameTitle(); const QString& GetCurrentGameSerial(); const QString& GetCurrentGamePath(); #ifdef _WIN32 /// Window corner rounding on Windows. bool AreWindowRoundedCornersDisabled(); #endif } // namespace QtHost