2024-04-10 03:56:49 +00:00
|
|
|
// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com>
|
2022-12-04 11:03:45 +00:00
|
|
|
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
|
|
|
|
|
2019-12-31 06:17:17 +00:00
|
|
|
#pragma once
|
2023-08-13 03:42:02 +00:00
|
|
|
|
2023-11-06 13:01:38 +00:00
|
|
|
#include "controllersettingswindow.h"
|
2023-08-13 03:42:02 +00:00
|
|
|
#include "displaywidget.h"
|
2023-11-06 13:01:38 +00:00
|
|
|
#include "settingswindow.h"
|
2023-08-13 03:42:02 +00:00
|
|
|
#include "ui_mainwindow.h"
|
|
|
|
|
|
|
|
#include "core/types.h"
|
|
|
|
|
|
|
|
#include "util/window_info.h"
|
|
|
|
|
2019-12-31 06:17:17 +00:00
|
|
|
#include <QtCore/QThread>
|
|
|
|
#include <QtWidgets/QLabel>
|
|
|
|
#include <QtWidgets/QMainWindow>
|
2023-02-28 18:53:31 +00:00
|
|
|
#include <QtWidgets/QMenu>
|
2021-07-17 12:27:07 +00:00
|
|
|
#include <QtWidgets/QStackedWidget>
|
2019-12-31 06:17:17 +00:00
|
|
|
#include <memory>
|
2022-11-18 08:14:39 +00:00
|
|
|
#include <optional>
|
2019-12-31 06:17:17 +00:00
|
|
|
|
2020-01-24 04:49:47 +00:00
|
|
|
class QLabel;
|
2020-02-15 15:14:28 +00:00
|
|
|
class QThread;
|
2022-07-11 13:03:29 +00:00
|
|
|
class QProgressBar;
|
2020-01-24 04:49:47 +00:00
|
|
|
|
2024-04-10 03:56:49 +00:00
|
|
|
class MainWindow;
|
2019-12-31 06:17:17 +00:00
|
|
|
class GameListWidget;
|
2022-07-11 13:03:29 +00:00
|
|
|
class EmuThread;
|
2020-08-06 12:08:22 +00:00
|
|
|
class AutoUpdaterDialog;
|
2023-11-29 08:47:38 +00:00
|
|
|
class MemoryCardEditorWindow;
|
2024-04-10 03:56:49 +00:00
|
|
|
class CheatManagerWindow;
|
2020-12-16 15:18:13 +00:00
|
|
|
class DebuggerWindow;
|
2024-04-10 03:56:49 +00:00
|
|
|
class MemoryScannerWindow;
|
2019-12-31 06:17:17 +00:00
|
|
|
|
2024-04-21 10:28:20 +00:00
|
|
|
struct SystemBootParameters;
|
|
|
|
|
2023-08-13 03:42:02 +00:00
|
|
|
class GPUDevice;
|
2023-09-07 10:13:48 +00:00
|
|
|
namespace Achievements {
|
|
|
|
enum class LoginRequestReason;
|
|
|
|
}
|
2022-07-11 13:03:29 +00:00
|
|
|
namespace GameList {
|
|
|
|
struct Entry;
|
|
|
|
}
|
2020-03-02 01:08:16 +00:00
|
|
|
|
2020-01-24 04:50:56 +00:00
|
|
|
class MainWindow final : public QMainWindow
|
2019-12-31 06:17:17 +00:00
|
|
|
{
|
|
|
|
Q_OBJECT
|
|
|
|
|
|
|
|
public:
|
2022-07-11 13:03:29 +00:00
|
|
|
/// 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();
|
2019-12-31 06:17:17 +00:00
|
|
|
~MainWindow();
|
|
|
|
|
2022-07-24 13:17:22 +00:00
|
|
|
/// Sets application theme according to settings.
|
|
|
|
static void updateApplicationTheme();
|
|
|
|
|
2020-08-06 12:08:22 +00:00
|
|
|
/// Performs update check if enabled in settings.
|
|
|
|
void startupUpdateCheck();
|
|
|
|
|
2020-11-10 13:24:07 +00:00
|
|
|
/// Opens memory card editor with the specified paths.
|
|
|
|
void openMemoryCardEditor(const QString& card_a_path, const QString& card_b_path);
|
|
|
|
|
2022-07-11 13:03:29 +00:00
|
|
|
/// Locks the system by pausing it, while a popup dialog is displayed.
|
|
|
|
SystemLock pauseAndLockSystem();
|
|
|
|
|
2024-03-22 16:26:50 +00:00
|
|
|
/// Force quits the application.
|
|
|
|
void quit();
|
|
|
|
|
2022-07-11 13:03:29 +00:00
|
|
|
/// 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; }
|
2021-03-03 09:14:01 +00:00
|
|
|
|
2024-04-10 03:56:49 +00:00
|
|
|
/// Accessors for child windows.
|
|
|
|
CheatManagerWindow* getCheatManagerWindow() const { return m_cheat_manager_window; }
|
|
|
|
|
2020-08-18 14:01:57 +00:00
|
|
|
public Q_SLOTS:
|
|
|
|
/// Updates debug menu visibility (hides if disabled).
|
|
|
|
void updateDebugMenuVisibility();
|
|
|
|
|
2022-07-11 13:03:29 +00:00
|
|
|
void refreshGameList(bool invalidate_cache);
|
|
|
|
void cancelGameListRefresh();
|
|
|
|
|
|
|
|
void runOnUIThread(const std::function<void()>& func);
|
2023-02-05 03:43:00 +00:00
|
|
|
bool requestShutdown(bool allow_confirm = true, bool allow_save_to_state = true, bool save_state = true);
|
|
|
|
void requestExit(bool allow_confirm = true);
|
2022-07-11 13:03:29 +00:00
|
|
|
void checkForSettingChanges();
|
2023-11-26 14:15:55 +00:00
|
|
|
std::optional<WindowInfo> getWindowInfo();
|
2022-07-11 13:03:29 +00:00
|
|
|
|
2020-12-21 16:47:48 +00:00
|
|
|
void checkForUpdates(bool display_message);
|
2023-11-29 10:26:36 +00:00
|
|
|
void recreate();
|
2020-12-21 16:47:48 +00:00
|
|
|
|
2022-07-11 13:03:29 +00:00
|
|
|
void* getNativeWindowId();
|
|
|
|
|
2020-01-06 06:27:39 +00:00
|
|
|
private Q_SLOTS:
|
2022-07-11 13:03:29 +00:00
|
|
|
void reportError(const QString& title, const QString& message);
|
|
|
|
bool confirmMessage(const QString& title, const QString& message);
|
2024-05-14 05:52:43 +00:00
|
|
|
void onStatusMessage(const QString& message);
|
2023-08-13 03:42:02 +00:00
|
|
|
|
|
|
|
std::optional<WindowInfo> acquireRenderWindow(bool recreate_window, bool fullscreen, bool render_to_main,
|
|
|
|
bool surfaceless, bool use_main_window_pos);
|
|
|
|
void displayResizeRequested(qint32 width, qint32 height);
|
|
|
|
void releaseRenderWindow();
|
2020-02-26 09:26:14 +00:00
|
|
|
void focusDisplayWidget();
|
2020-12-27 04:08:13 +00:00
|
|
|
void onMouseModeRequested(bool relative_mode, bool hide_cursor);
|
2020-06-05 17:44:57 +00:00
|
|
|
|
2023-11-06 13:01:38 +00:00
|
|
|
void onSettingsResetToDefault(bool system, bool controller);
|
2022-07-11 13:03:29 +00:00
|
|
|
void onSystemStarting();
|
|
|
|
void onSystemStarted();
|
|
|
|
void onSystemDestroyed();
|
|
|
|
void onSystemPaused();
|
|
|
|
void onSystemResumed();
|
2022-10-05 08:29:08 +00:00
|
|
|
void onRunningGameChanged(const QString& filename, const QString& game_serial, const QString& game_title);
|
2023-09-07 10:13:48 +00:00
|
|
|
void onAchievementsLoginRequested(Achievements::LoginRequestReason reason);
|
2023-09-18 12:29:47 +00:00
|
|
|
void onAchievementsChallengeModeChanged(bool enabled);
|
2020-12-04 14:16:22 +00:00
|
|
|
void onApplicationStateChanged(Qt::ApplicationState state);
|
2019-12-31 06:17:17 +00:00
|
|
|
|
2021-07-24 08:38:54 +00:00
|
|
|
void onStartFileActionTriggered();
|
2019-12-31 06:17:17 +00:00
|
|
|
void onStartDiscActionTriggered();
|
2020-03-02 01:08:16 +00:00
|
|
|
void onStartBIOSActionTriggered();
|
2020-01-24 04:50:40 +00:00
|
|
|
void onChangeDiscFromFileActionTriggered();
|
|
|
|
void onChangeDiscFromGameListActionTriggered();
|
2021-07-27 08:23:11 +00:00
|
|
|
void onChangeDiscFromDeviceActionTriggered();
|
2021-03-27 04:14:52 +00:00
|
|
|
void onChangeDiscMenuAboutToShow();
|
|
|
|
void onChangeDiscMenuAboutToHide();
|
2021-07-04 04:46:46 +00:00
|
|
|
void onLoadStateMenuAboutToShow();
|
|
|
|
void onSaveStateMenuAboutToShow();
|
2024-03-03 03:43:04 +00:00
|
|
|
void onCheatsActionTriggered();
|
2020-09-09 13:44:21 +00:00
|
|
|
void onCheatsMenuAboutToShow();
|
2023-09-05 10:12:54 +00:00
|
|
|
void onStartFullscreenUITriggered();
|
|
|
|
void onFullscreenUIStateChange(bool running);
|
2020-05-19 16:32:19 +00:00
|
|
|
void onRemoveDiscActionTriggered();
|
2020-08-20 13:39:29 +00:00
|
|
|
void onViewToolbarActionToggled(bool checked);
|
2021-02-09 13:56:24 +00:00
|
|
|
void onViewLockToolbarActionToggled(bool checked);
|
2020-08-20 13:39:29 +00:00
|
|
|
void onViewStatusBarActionToggled(bool checked);
|
|
|
|
void onViewGameListActionTriggered();
|
2020-09-23 14:02:13 +00:00
|
|
|
void onViewGameGridActionTriggered();
|
2020-08-20 13:39:29 +00:00
|
|
|
void onViewSystemDisplayTriggered();
|
2020-10-13 13:20:50 +00:00
|
|
|
void onViewGamePropertiesActionTriggered();
|
2019-12-31 06:17:17 +00:00
|
|
|
void onGitHubRepositoryActionTriggered();
|
|
|
|
void onIssueTrackerActionTriggered();
|
2020-04-07 03:54:20 +00:00
|
|
|
void onDiscordServerActionTriggered();
|
2019-12-31 06:17:17 +00:00
|
|
|
void onAboutActionTriggered();
|
2020-09-14 08:25:51 +00:00
|
|
|
void onCheckForUpdatesActionTriggered();
|
2020-09-18 14:28:07 +00:00
|
|
|
void onToolsMemoryCardEditorTriggered();
|
2024-04-10 03:56:49 +00:00
|
|
|
void onToolsMemoryScannerTriggered();
|
2022-09-13 10:29:17 +00:00
|
|
|
void onToolsCoverDownloaderTriggered();
|
2020-09-23 14:19:54 +00:00
|
|
|
void onToolsOpenDataDirectoryTriggered();
|
2023-02-28 18:53:31 +00:00
|
|
|
void onSettingsTriggeredFromToolbar();
|
2019-12-31 06:17:17 +00:00
|
|
|
|
2022-07-11 13:03:29 +00:00
|
|
|
void onGameListRefreshComplete();
|
|
|
|
void onGameListRefreshProgress(const QString& status, int current, int total);
|
|
|
|
void onGameListSelectionChanged();
|
|
|
|
void onGameListEntryActivated();
|
|
|
|
void onGameListEntryContextMenuRequested(const QPoint& point);
|
2020-03-02 01:08:16 +00:00
|
|
|
|
2020-08-06 12:08:22 +00:00
|
|
|
void onUpdateCheckComplete();
|
|
|
|
|
2024-03-13 03:54:33 +00:00
|
|
|
void openCheatManager();
|
2020-12-26 14:40:28 +00:00
|
|
|
void openCPUDebugger();
|
|
|
|
|
2020-01-24 04:50:56 +00:00
|
|
|
protected:
|
2022-07-11 13:03:29 +00:00
|
|
|
void showEvent(QShowEvent* event) override;
|
2020-01-24 04:50:56 +00:00
|
|
|
void closeEvent(QCloseEvent* event) override;
|
2020-03-22 11:50:49 +00:00
|
|
|
void changeEvent(QEvent* event) override;
|
2021-03-11 16:50:17 +00:00
|
|
|
void dragEnterEvent(QDragEnterEvent* event) override;
|
|
|
|
void dropEvent(QDropEvent* event) override;
|
2023-09-30 04:40:50 +00:00
|
|
|
void moveEvent(QMoveEvent* event) override;
|
|
|
|
void resizeEvent(QResizeEvent* event) override;
|
2020-01-24 04:50:56 +00:00
|
|
|
|
2022-10-23 04:09:54 +00:00
|
|
|
#ifdef _WIN32
|
|
|
|
bool nativeEvent(const QByteArray& eventType, void* message, qintptr* result) override;
|
|
|
|
#endif
|
|
|
|
|
2019-12-31 06:17:17 +00:00
|
|
|
private:
|
2022-07-24 13:17:22 +00:00
|
|
|
static void setStyleFromSettings();
|
|
|
|
static void setIconThemeFromSettings();
|
2023-09-30 04:40:50 +00:00
|
|
|
|
|
|
|
/// Initializes the window. Call once at startup.
|
|
|
|
void initialize();
|
|
|
|
|
2019-12-31 06:17:17 +00:00
|
|
|
void setupAdditionalUi();
|
|
|
|
void connectSignals();
|
2022-07-11 13:03:29 +00:00
|
|
|
|
2021-03-03 09:14:01 +00:00
|
|
|
void updateEmulationActions(bool starting, bool running, bool cheevos_challenge_mode);
|
2022-07-11 13:03:29 +00:00
|
|
|
void updateStatusBarWidgetVisibility();
|
|
|
|
void updateWindowTitle();
|
|
|
|
void updateWindowState(bool force_visible = false);
|
2024-03-03 03:43:04 +00:00
|
|
|
void updateCheatActionsVisibility();
|
2022-07-11 13:03:29 +00:00
|
|
|
|
|
|
|
void setProgressBar(int current, int total);
|
|
|
|
void clearProgressBar();
|
|
|
|
|
|
|
|
QWidget* getContentParent();
|
|
|
|
QWidget* getDisplayContainer() const;
|
2020-09-23 14:02:13 +00:00
|
|
|
bool isShowingGameList() const;
|
2022-07-11 13:03:29 +00:00
|
|
|
bool isRenderingFullscreen() const;
|
|
|
|
bool isRenderingToMain() const;
|
|
|
|
bool shouldHideMouseCursor() const;
|
|
|
|
bool shouldHideMainWindow() const;
|
|
|
|
|
2019-12-31 06:17:17 +00:00
|
|
|
void switchToGameListView();
|
|
|
|
void switchToEmulationView();
|
2024-04-05 09:21:56 +00:00
|
|
|
void saveStateToConfig();
|
|
|
|
void restoreStateFromConfig();
|
2020-10-03 02:10:11 +00:00
|
|
|
void saveDisplayWindowGeometryToConfig();
|
|
|
|
void restoreDisplayWindowGeometryFromConfig();
|
2023-08-13 03:42:02 +00:00
|
|
|
void createDisplayWidget(bool fullscreen, bool render_to_main, bool use_main_window_pos);
|
2022-07-11 13:03:29 +00:00
|
|
|
void destroyDisplayWidget(bool show_game_list);
|
2022-08-10 06:32:55 +00:00
|
|
|
void updateDisplayWidgetCursor();
|
2022-12-20 11:45:01 +00:00
|
|
|
void updateDisplayRelatedActions(bool has_surface, bool render_to_main, bool fullscreen);
|
2022-07-11 13:03:29 +00:00
|
|
|
|
2023-11-06 13:01:38 +00:00
|
|
|
SettingsWindow* getSettingsDialog();
|
2022-07-11 13:03:29 +00:00
|
|
|
void doSettings(const char* category = nullptr);
|
|
|
|
|
2023-11-06 13:01:38 +00:00
|
|
|
void doControllerSettings(ControllerSettingsWindow::Category category = ControllerSettingsWindow::Category::Count);
|
2022-07-11 13:03:29 +00:00
|
|
|
|
2020-02-05 08:43:25 +00:00
|
|
|
void updateDebugMenuCPUExecutionMode();
|
2020-01-24 04:49:51 +00:00
|
|
|
void updateDebugMenuGPURenderer();
|
2020-10-09 07:52:05 +00:00
|
|
|
void updateDebugMenuCropMode();
|
2021-08-15 04:23:14 +00:00
|
|
|
void updateMenuSelectedTheme();
|
2021-07-27 08:23:11 +00:00
|
|
|
std::string getDeviceDiscPath(const QString& title);
|
2022-07-11 13:03:29 +00:00
|
|
|
void setGameListEntryCoverImage(const GameList::Entry* entry);
|
2023-01-11 10:21:27 +00:00
|
|
|
void clearGameListEntryPlayTime(const GameList::Entry* entry);
|
2022-07-24 13:17:22 +00:00
|
|
|
void setTheme(const QString& theme);
|
2023-02-05 05:00:51 +00:00
|
|
|
void updateTheme();
|
2023-09-16 14:22:39 +00:00
|
|
|
void reloadThemeSpecificImages();
|
2023-11-06 13:01:38 +00:00
|
|
|
void destroySubWindows();
|
2019-12-31 06:17:17 +00:00
|
|
|
|
2022-10-23 04:09:54 +00:00
|
|
|
void registerForDeviceNotifications();
|
|
|
|
void unregisterForDeviceNotifications();
|
|
|
|
|
2022-07-11 13:03:29 +00:00
|
|
|
/// Fills menu with save state info and handlers.
|
|
|
|
void populateGameListContextMenu(const GameList::Entry* entry, QWidget* parent_window, QMenu* menu);
|
|
|
|
|
2022-10-05 08:29:08 +00:00
|
|
|
void populateLoadStateMenu(const char* game_serial, QMenu* menu);
|
|
|
|
void populateSaveStateMenu(const char* game_serial, QMenu* menu);
|
2022-07-11 13:03:29 +00:00
|
|
|
|
|
|
|
/// Fills menu with the current playlist entries. The disc index is marked as checked.
|
|
|
|
void populateChangeDiscSubImageMenu(QMenu* menu, QActionGroup* action_group);
|
|
|
|
|
|
|
|
/// Fills menu with the current cheat options.
|
|
|
|
void populateCheatsMenu(QMenu* menu);
|
|
|
|
|
2024-05-18 05:16:54 +00:00
|
|
|
const GameList::Entry* resolveDiscSetEntry(const GameList::Entry* entry,
|
|
|
|
std::unique_lock<std::recursive_mutex>& lock);
|
2024-04-21 10:28:20 +00:00
|
|
|
std::shared_ptr<SystemBootParameters> getSystemBootParameters(std::string file);
|
2022-07-11 13:03:29 +00:00
|
|
|
std::optional<bool> promptForResumeState(const std::string& save_state_path);
|
|
|
|
void startFile(std::string path, std::optional<std::string> save_path, std::optional<bool> fast_boot);
|
|
|
|
void startFileOrChangeDisc(const QString& path);
|
|
|
|
void promptForDiscChange(const QString& path);
|
|
|
|
|
2019-12-31 06:17:17 +00:00
|
|
|
Ui::MainWindow m_ui;
|
|
|
|
|
|
|
|
GameListWidget* m_game_list_widget = nullptr;
|
2020-04-22 11:13:51 +00:00
|
|
|
|
2022-07-11 13:03:29 +00:00
|
|
|
DisplayWidget* m_display_widget = nullptr;
|
|
|
|
DisplayContainer* m_display_container = nullptr;
|
2019-12-31 06:17:17 +00:00
|
|
|
|
2022-07-11 13:03:29 +00:00
|
|
|
QProgressBar* m_status_progress_widget = nullptr;
|
2021-03-28 03:47:10 +00:00
|
|
|
QLabel* m_status_renderer_widget = nullptr;
|
2022-07-11 13:03:29 +00:00
|
|
|
QLabel* m_status_fps_widget = nullptr;
|
|
|
|
QLabel* m_status_vps_widget = nullptr;
|
2021-03-28 03:47:10 +00:00
|
|
|
QLabel* m_status_resolution_widget = nullptr;
|
2020-01-24 04:49:47 +00:00
|
|
|
|
2023-02-28 18:53:31 +00:00
|
|
|
QMenu* m_settings_toolbar_menu = nullptr;
|
|
|
|
|
2023-11-06 13:01:38 +00:00
|
|
|
SettingsWindow* m_settings_window = nullptr;
|
|
|
|
ControllerSettingsWindow* m_controller_settings_window = nullptr;
|
2022-07-11 13:03:29 +00:00
|
|
|
|
2020-08-06 12:08:22 +00:00
|
|
|
AutoUpdaterDialog* m_auto_updater_dialog = nullptr;
|
2023-11-29 08:47:38 +00:00
|
|
|
MemoryCardEditorWindow* m_memory_card_editor_window = nullptr;
|
2024-04-10 03:56:49 +00:00
|
|
|
CheatManagerWindow* m_cheat_manager_window = nullptr;
|
2020-12-16 15:18:13 +00:00
|
|
|
DebuggerWindow* m_debugger_window = nullptr;
|
2024-04-10 03:56:49 +00:00
|
|
|
MemoryScannerWindow* m_memory_scanner_window = nullptr;
|
2019-12-31 06:17:17 +00:00
|
|
|
|
2020-12-04 14:16:22 +00:00
|
|
|
bool m_was_paused_by_focus_loss = false;
|
2020-12-26 14:40:28 +00:00
|
|
|
bool m_open_debugger_on_start = false;
|
2020-12-27 04:08:13 +00:00
|
|
|
bool m_relative_mouse_mode = false;
|
2022-08-10 06:32:55 +00:00
|
|
|
bool m_hide_mouse_cursor = false;
|
2020-12-17 17:32:29 +00:00
|
|
|
|
2022-07-11 13:03:29 +00:00
|
|
|
bool m_display_created = 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;
|
|
|
|
|
2022-10-23 04:09:54 +00:00
|
|
|
#ifdef _WIN32
|
|
|
|
void* m_device_notification_handle = nullptr;
|
|
|
|
#endif
|
2019-12-31 06:17:17 +00:00
|
|
|
};
|
2022-07-11 13:03:29 +00:00
|
|
|
|
|
|
|
extern MainWindow* g_main_window;
|