// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin // SPDX-License-Identifier: CC-BY-NC-ND-4.0 #pragma once #include "controllersettingswindow.h" #include "displaywidget.h" #include "settingswindow.h" #include "ui_mainwindow.h" #include "core/types.h" #include "util/imgui_manager.h" #include "util/window_info.h" #include #include #include #include #include #include #include class QLabel; class QThread; class QProgressBar; class QShortcut; class MainWindow; class GameListWidget; class EmuThread; class AutoUpdaterDialog; class MemoryCardEditorWindow; class DebuggerWindow; class MemoryScannerWindow; struct SystemBootParameters; enum class RenderAPI : u8; class GPUDevice; namespace Achievements { enum class LoginRequestReason; } namespace GameList { struct Entry; } class MainWindow final : public QMainWindow { Q_OBJECT public: /// This class is a scoped lock on the system, which prevents it from running while /// the object exists. Its purpose is to be used for blocking/modal popup boxes, /// where the VM needs to exit fullscreen temporarily. class SystemLock { public: SystemLock(SystemLock&& lock); SystemLock(const SystemLock&) = delete; ~SystemLock(); /// Returns the parent widget, which can be used for any popup dialogs. ALWAYS_INLINE QWidget* getDialogParent() const { return m_dialog_parent; } /// Cancels any pending unpause/fullscreen transition. /// Call when you're going to destroy the system anyway. void cancelResume(); private: SystemLock(QWidget* dialog_parent, bool was_paused, bool was_fullscreen); friend MainWindow; QWidget* m_dialog_parent; bool m_was_paused; bool m_was_fullscreen; }; public: explicit MainWindow(); ~MainWindow(); /// Disable createPopupMenu(), the menu is bogus. QMenu* createPopupMenu() override; /// Performs update check if enabled in settings. void startupUpdateCheck(); /// Opens memory card editor with the specified paths. void openMemoryCardEditor(const QString& card_a_path, const QString& card_b_path); /// Locks the system by pausing it, while a popup dialog is displayed. SystemLock pauseAndLockSystem(); /// Force quits the application. void quit(); /// Accessors for the status bar widgets, updated by the emulation thread. ALWAYS_INLINE QLabel* getStatusRendererWidget() const { return m_status_renderer_widget; } ALWAYS_INLINE QLabel* getStatusResolutionWidget() const { return m_status_resolution_widget; } ALWAYS_INLINE QLabel* getStatusFPSWidget() const { return m_status_fps_widget; } ALWAYS_INLINE QLabel* getStatusVPSWidget() const { return m_status_vps_widget; } /// Opens the editor for a specific input profile. void openInputProfileEditor(const std::string_view name); /// Returns pointer to settings window. SettingsWindow* getSettingsWindow(); ControllerSettingsWindow* getControllerSettingsWindow(); public Q_SLOTS: /// Updates debug menu visibility (hides if disabled). void updateDebugMenuVisibility(); void refreshGameList(bool invalidate_cache); void refreshGameListModel(); void cancelGameListRefresh(); void runOnUIThread(const std::function& func); bool requestShutdown(bool allow_confirm, bool allow_save_to_state, bool save_state, bool check_memcard_busy); void requestExit(bool allow_confirm = true); void checkForSettingChanges(); std::optional getWindowInfo(); void checkForUpdates(bool display_message); void recreate(); void* getNativeWindowId(); private Q_SLOTS: void reportError(const QString& title, const QString& message); bool confirmMessage(const QString& title, const QString& message); void onStatusMessage(const QString& message); std::optional acquireRenderWindow(RenderAPI render_api, bool fullscreen, bool exclusive_fullscreen, bool render_to_main, bool surfaceless, bool use_main_window_pos, Error* error); void displayResizeRequested(qint32 width, qint32 height); void releaseRenderWindow(); void focusDisplayWidget(); void onMouseModeRequested(bool relative_mode, bool hide_cursor); void onSettingsResetToDefault(bool system, bool controller); void onSystemStarting(); void onSystemStarted(); void onSystemDestroyed(); void onSystemPaused(); void onSystemResumed(); void onRunningGameChanged(const QString& filename, const QString& game_serial, const QString& game_title); void onMediaCaptureStarted(); void onMediaCaptureStopped(); void onAchievementsLoginRequested(Achievements::LoginRequestReason reason); void onAchievementsLoginSuccess(const QString& username, quint32 points, quint32 sc_points, quint32 unread_messages); void onAchievementsHardcoreModeChanged(bool enabled); 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); void onApplicationStateChanged(Qt::ApplicationState state); void onToolbarContextMenuRequested(const QPoint& pos); void onStartFileActionTriggered(); void onStartDiscActionTriggered(); void onStartBIOSActionTriggered(); void onChangeDiscFromFileActionTriggered(); void onChangeDiscFromGameListActionTriggered(); void onChangeDiscFromDeviceActionTriggered(); void onChangeDiscMenuAboutToShow(); void onLoadStateMenuAboutToShow(); void onSaveStateMenuAboutToShow(); void onCheatsActionTriggered(); void onCheatsMenuAboutToShow(); void onStartFullscreenUITriggered(); void onFullscreenUIStartedOrStopped(bool running); void onRemoveDiscActionTriggered(); void onScanForNewGamesTriggered(); void onViewToolbarActionToggled(bool checked); void onViewLockToolbarActionToggled(bool checked); void onViewStatusBarActionToggled(bool checked); void onViewGameListActionTriggered(); void onViewGameGridActionTriggered(); void onViewSystemDisplayTriggered(); void onViewGameGridZoomInActionTriggered(); void onViewGameGridZoomOutActionTriggered(); void onGitHubRepositoryActionTriggered(); void onIssueTrackerActionTriggered(); void onDiscordServerActionTriggered(); void onAboutActionTriggered(); void onCheckForUpdatesActionTriggered(); void onToolsMemoryCardEditorTriggered(); void onToolsMemoryScannerTriggered(); void onToolsISOBrowserTriggered(); void onToolsCoverDownloaderTriggered(); void onToolsMediaCaptureToggled(bool checked); void onToolsOpenDataDirectoryTriggered(); void onToolsOpenTextureDirectoryTriggered(); void onSettingsTriggeredFromToolbar(); void onSettingsControllerProfilesTriggered(); void onGameListRefreshComplete(); void onGameListRefreshProgress(const QString& status, int current, int total); void onGameListLayoutChanged(); void onGameListSelectionChanged(); void onGameListEntryActivated(); void onGameListEntryContextMenuRequested(const QPoint& point); void onUpdateCheckComplete(); void onRAIntegrationMenuChanged(); void onDebugLogChannelsMenuAboutToShow(); void openCPUDebugger(); protected: void showEvent(QShowEvent* event) override; void closeEvent(QCloseEvent* event) override; void changeEvent(QEvent* event) override; void dragEnterEvent(QDragEnterEvent* event) override; void dropEvent(QDropEvent* event) override; void moveEvent(QMoveEvent* event) override; void resizeEvent(QResizeEvent* event) override; #ifdef _WIN32 bool nativeEvent(const QByteArray& eventType, void* message, qintptr* result) override; #endif private: /// Initializes the window. Call once at startup. void initialize(); void setupAdditionalUi(); void connectSignals(); void updateToolbarActions(); void updateEmulationActions(bool starting, bool running, bool cheevos_challenge_mode); void updateShortcutActions(bool starting); void updateStatusBarWidgetVisibility(); void updateWindowTitle(); void updateWindowState(bool force_visible = false); void setProgressBar(int current, int total); void clearProgressBar(); QWidget* getDisplayContainer() const; bool isShowingGameList() const; bool isRenderingFullscreen() const; bool isRenderingToMain() const; bool shouldHideMouseCursor() const; bool shouldHideMainWindow() const; void switchToGameListView(); void switchToEmulationView(); void saveStateToConfig(); void restoreStateFromConfig(); void saveDisplayWindowGeometryToConfig(); void restoreDisplayWindowGeometryFromConfig(); void createDisplayWidget(bool fullscreen, bool render_to_main, bool use_main_window_pos); void destroyDisplayWidget(bool show_game_list); void updateDisplayWidgetCursor(); void updateDisplayRelatedActions(bool has_surface, bool render_to_main, bool fullscreen); void doSettings(const char* category = nullptr); void openGamePropertiesForCurrentGame(const char* category = nullptr); void doControllerSettings(ControllerSettingsWindow::Category category = ControllerSettingsWindow::Category::Count); void onViewChangeGameListBackgroundTriggered(); void onViewClearGameListBackgroundTriggered(); std::string getDeviceDiscPath(const QString& title); void setGameListEntryCoverImage(const GameList::Entry* entry); void clearGameListEntryPlayTime(const GameList::Entry* entry); void updateTheme(); void reloadThemeSpecificImages(); void onSettingsThemeChanged(); void destroySubWindows(); void registerForDeviceNotifications(); void unregisterForDeviceNotifications(); void notifyRAIntegrationOfWindowChange(); /// Fills menu with save state info and handlers. void populateGameListContextMenu(const GameList::Entry* entry, QWidget* parent_window, QMenu* menu); void populateLoadStateMenu(std::string_view game_serial, QMenu* menu); void populateSaveStateMenu(std::string_view game_serial, QMenu* menu); const GameList::Entry* resolveDiscSetEntry(const GameList::Entry* entry, std::unique_lock& lock); std::shared_ptr getSystemBootParameters(std::string file); std::optional promptForResumeState(const std::string& save_state_path); void startFile(std::string path, std::optional save_path, std::optional fast_boot); void startFileOrChangeDisc(const QString& path); void promptForDiscChange(const QString& path); Ui::MainWindow m_ui; GameListWidget* m_game_list_widget = nullptr; DisplayWidget* m_display_widget = nullptr; DisplayContainer* m_display_container = nullptr; QProgressBar* m_status_progress_widget = nullptr; QLabel* m_status_renderer_widget = nullptr; QLabel* m_status_fps_widget = nullptr; QLabel* m_status_vps_widget = nullptr; QLabel* m_status_resolution_widget = nullptr; QMenu* m_settings_toolbar_menu = nullptr; struct { QShortcut* open_file = nullptr; QShortcut* game_list_refresh = nullptr; QShortcut* game_list_search = nullptr; QShortcut* game_grid_zoom_in = nullptr; QShortcut* game_grid_zoom_out = nullptr; } m_shortcuts; SettingsWindow* m_settings_window = nullptr; ControllerSettingsWindow* m_controller_settings_window = nullptr; ControllerSettingsWindow* m_input_profile_editor_window = nullptr; AutoUpdaterDialog* m_auto_updater_dialog = nullptr; MemoryCardEditorWindow* m_memory_card_editor_window = nullptr; DebuggerWindow* m_debugger_window = nullptr; MemoryScannerWindow* m_memory_scanner_window = nullptr; bool m_was_paused_by_focus_loss = false; bool m_relative_mouse_mode = false; bool m_hide_mouse_cursor = false; bool m_display_created = false; bool m_exclusive_fullscreen_requested = false; bool m_save_states_invalidated = false; bool m_was_paused_on_surface_loss = false; bool m_was_disc_change_request = false; bool m_is_closing = false; #ifdef _WIN32 void* m_device_notification_handle = nullptr; #endif #ifdef RC_CLIENT_SUPPORTS_RAINTEGRATION QMenu* m_raintegration_menu = nullptr; #endif }; extern MainWindow* g_main_window;