mirror of
https://git.citron-emu.org/citron/emulator
synced 2025-12-21 11:33:35 +00:00
Merge branch 'feat/multiplayer-room-overlay' into 'main'
feat(multiplayer): Add in-game multiplayer room overlay See merge request citron/emulator!112
This commit is contained in:
@@ -223,6 +223,8 @@ add_executable(citron
|
|||||||
util/overlay_dialog.ui
|
util/overlay_dialog.ui
|
||||||
util/performance_overlay.cpp
|
util/performance_overlay.cpp
|
||||||
util/performance_overlay.h
|
util/performance_overlay.h
|
||||||
|
util/multiplayer_room_overlay.cpp
|
||||||
|
util/multiplayer_room_overlay.h
|
||||||
util/vram_overlay.cpp
|
util/vram_overlay.cpp
|
||||||
util/vram_overlay.h
|
util/vram_overlay.h
|
||||||
util/sequence_dialog/sequence_dialog.cpp
|
util/sequence_dialog/sequence_dialog.cpp
|
||||||
|
|||||||
@@ -180,6 +180,7 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
|
|||||||
#endif
|
#endif
|
||||||
#include "citron/util/clickable_label.h"
|
#include "citron/util/clickable_label.h"
|
||||||
#include "citron/util/performance_overlay.h"
|
#include "citron/util/performance_overlay.h"
|
||||||
|
#include "citron/util/multiplayer_room_overlay.h"
|
||||||
#include "citron/util/vram_overlay.h"
|
#include "citron/util/vram_overlay.h"
|
||||||
#include "citron/vk_device_info.h"
|
#include "citron/vk_device_info.h"
|
||||||
|
|
||||||
@@ -1095,6 +1096,12 @@ void GMainWindow::InitializeWidgets() {
|
|||||||
performance_overlay = new PerformanceOverlay(this);
|
performance_overlay = new PerformanceOverlay(this);
|
||||||
performance_overlay->hide();
|
performance_overlay->hide();
|
||||||
|
|
||||||
|
multiplayer_room_overlay = new MultiplayerRoomOverlay(this);
|
||||||
|
multiplayer_room_overlay->hide();
|
||||||
|
|
||||||
|
connect(this, &GMainWindow::EmulationStarting, multiplayer_room_overlay, &MultiplayerRoomOverlay::OnEmulationStarting);
|
||||||
|
connect(this, &GMainWindow::EmulationStopping, multiplayer_room_overlay, &MultiplayerRoomOverlay::OnEmulationStopping);
|
||||||
|
|
||||||
vram_overlay = new VramOverlay(this);
|
vram_overlay = new VramOverlay(this);
|
||||||
vram_overlay->hide();
|
vram_overlay->hide();
|
||||||
|
|
||||||
@@ -1379,6 +1386,7 @@ void GMainWindow::InitializeHotkeys() {
|
|||||||
LinkActionShortcut(ui->action_Show_Status_Bar, QStringLiteral("Toggle Status Bar"));
|
LinkActionShortcut(ui->action_Show_Status_Bar, QStringLiteral("Toggle Status Bar"));
|
||||||
LinkActionShortcut(ui->action_Show_Performance_Overlay, QStringLiteral("Toggle Performance Overlay"));
|
LinkActionShortcut(ui->action_Show_Performance_Overlay, QStringLiteral("Toggle Performance Overlay"));
|
||||||
LinkActionShortcut(ui->action_Show_Vram_Overlay, QStringLiteral("Toggle VRAM Overlay"));
|
LinkActionShortcut(ui->action_Show_Vram_Overlay, QStringLiteral("Toggle VRAM Overlay"));
|
||||||
|
LinkActionShortcut(ui->action_Show_Multiplayer_Room_Overlay, QStringLiteral("Toggle Multiplayer Room Overlay"));
|
||||||
LinkActionShortcut(ui->action_Fullscreen, QStringLiteral("Fullscreen"));
|
LinkActionShortcut(ui->action_Fullscreen, QStringLiteral("Fullscreen"));
|
||||||
LinkActionShortcut(ui->action_Capture_Screenshot, QStringLiteral("Capture Screenshot"));
|
LinkActionShortcut(ui->action_Capture_Screenshot, QStringLiteral("Capture Screenshot"));
|
||||||
LinkActionShortcut(ui->action_TAS_Start, QStringLiteral("TAS Start/Stop"), true);
|
LinkActionShortcut(ui->action_TAS_Start, QStringLiteral("TAS Start/Stop"), true);
|
||||||
@@ -1608,6 +1616,7 @@ void GMainWindow::ConnectMenuEvents() {
|
|||||||
connect_menu(ui->action_Show_Filter_Bar, &GMainWindow::OnToggleFilterBar);
|
connect_menu(ui->action_Show_Filter_Bar, &GMainWindow::OnToggleFilterBar);
|
||||||
connect_menu(ui->action_Show_Status_Bar, &GMainWindow::OnToggleStatusBar);
|
connect_menu(ui->action_Show_Status_Bar, &GMainWindow::OnToggleStatusBar);
|
||||||
connect_menu(ui->action_Show_Performance_Overlay, &GMainWindow::OnTogglePerformanceOverlay);
|
connect_menu(ui->action_Show_Performance_Overlay, &GMainWindow::OnTogglePerformanceOverlay);
|
||||||
|
connect_menu(ui->action_Show_Multiplayer_Room_Overlay, &GMainWindow::OnToggleMultiplayerRoomOverlay);
|
||||||
connect_menu(ui->action_Show_Vram_Overlay, &GMainWindow::OnToggleVramOverlay);
|
connect_menu(ui->action_Show_Vram_Overlay, &GMainWindow::OnToggleVramOverlay);
|
||||||
connect_menu(ui->action_Toggle_Grid_View, &GMainWindow::OnToggleGridView);
|
connect_menu(ui->action_Toggle_Grid_View, &GMainWindow::OnToggleGridView);
|
||||||
|
|
||||||
@@ -4930,6 +4939,16 @@ void GMainWindow::OnTogglePerformanceOverlay() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GMainWindow::OnToggleMultiplayerRoomOverlay() {
|
||||||
|
if (multiplayer_room_overlay) {
|
||||||
|
const bool is_checked = ui->action_Show_Multiplayer_Room_Overlay->isChecked();
|
||||||
|
multiplayer_room_overlay->SetVisible(is_checked);
|
||||||
|
|
||||||
|
// We will add a setting for this later if needed.
|
||||||
|
// UISettings::values.show_multiplayer_room_overlay = is_checked;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void GMainWindow::OnToggleVramOverlay() {
|
void GMainWindow::OnToggleVramOverlay() {
|
||||||
if (vram_overlay) {
|
if (vram_overlay) {
|
||||||
const bool is_checked = ui->action_Show_Vram_Overlay->isChecked();
|
const bool is_checked = ui->action_Show_Vram_Overlay->isChecked();
|
||||||
|
|||||||
@@ -6,14 +6,12 @@
|
|||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <QMainWindow>
|
#include <QMainWindow>
|
||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
#include <QPushButton>
|
#include <QPushButton>
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
#include <QTranslator>
|
#include <QTranslator>
|
||||||
|
|
||||||
#include "common/announce_multiplayer_room.h"
|
#include "common/announce_multiplayer_room.h"
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "configuration/qt_config.h"
|
#include "configuration/qt_config.h"
|
||||||
@@ -39,11 +37,9 @@ class LoadingScreen;
|
|||||||
class MicroProfileDialog;
|
class MicroProfileDialog;
|
||||||
class OverlayDialog;
|
class OverlayDialog;
|
||||||
class PerformanceOverlay;
|
class PerformanceOverlay;
|
||||||
|
class MultiplayerRoomOverlay;
|
||||||
class VramOverlay;
|
class VramOverlay;
|
||||||
class ProfilerWidget;
|
class ProfilerWidget;
|
||||||
|
|
||||||
// Forward declaration
|
|
||||||
class PerformanceOverlay;
|
|
||||||
class ControllerDialog;
|
class ControllerDialog;
|
||||||
class QLabel;
|
class QLabel;
|
||||||
class MultiplayerState;
|
class MultiplayerState;
|
||||||
@@ -58,7 +54,6 @@ enum class GameListShortcutTarget;
|
|||||||
enum class DumpRomFSTarget;
|
enum class DumpRomFSTarget;
|
||||||
enum class InstalledEntryType;
|
enum class InstalledEntryType;
|
||||||
class GameListPlaceholder;
|
class GameListPlaceholder;
|
||||||
|
|
||||||
class QtAmiiboSettingsDialog;
|
class QtAmiiboSettingsDialog;
|
||||||
class QtControllerSelectorDialog;
|
class QtControllerSelectorDialog;
|
||||||
class QtProfileSelectionDialog;
|
class QtProfileSelectionDialog;
|
||||||
@@ -66,75 +61,21 @@ class QtSoftwareKeyboardDialog;
|
|||||||
class QtNXWebEngineView;
|
class QtNXWebEngineView;
|
||||||
class UpdaterDialog;
|
class UpdaterDialog;
|
||||||
|
|
||||||
enum class StartGameType {
|
enum class StartGameType { Normal, Global };
|
||||||
Normal, // Can use custom configuration
|
|
||||||
Global, // Only uses global configuration
|
|
||||||
};
|
|
||||||
|
|
||||||
namespace Core {
|
namespace Core { enum class SystemResultStatus : u32; class System; }
|
||||||
enum class SystemResultStatus : u32;
|
namespace Core::Frontend { struct CabinetParameters; struct ControllerParameters; struct InlineAppearParameters; struct InlineTextParameters; struct KeyboardInitializeParameters; struct ProfileSelectParameters; }
|
||||||
class System;
|
namespace DiscordRPC { class DiscordInterface; }
|
||||||
} // namespace Core
|
namespace PlayTime { class PlayTimeManager; }
|
||||||
|
namespace FileSys { class ContentProvider; class ManualContentProvider; class VfsFilesystem; }
|
||||||
namespace Core::Frontend {
|
namespace InputCommon { class InputSubsystem; }
|
||||||
struct CabinetParameters;
|
namespace Service::AM { struct FrontendAppletParameters; enum class AppletId : u32; }
|
||||||
struct ControllerParameters;
|
namespace Service::AM::Frontend { enum class SwkbdResult : u32; enum class SwkbdTextCheckResult : u32; enum class SwkbdReplyType : u32; enum class WebExitReason : u32; }
|
||||||
struct InlineAppearParameters;
|
namespace Service::NFC { class NfcDevice; }
|
||||||
struct InlineTextParameters;
|
namespace Service::NFP { enum class CabinetMode : u8; }
|
||||||
struct KeyboardInitializeParameters;
|
namespace Ui { class MainWindow; }
|
||||||
struct ProfileSelectParameters;
|
enum class EmulatedDirectoryTarget { NAND, SDMC };
|
||||||
} // namespace Core::Frontend
|
namespace VkDeviceInfo { class Record; }
|
||||||
|
|
||||||
namespace DiscordRPC {
|
|
||||||
class DiscordInterface;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace PlayTime {
|
|
||||||
class PlayTimeManager;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace FileSys {
|
|
||||||
class ContentProvider;
|
|
||||||
class ManualContentProvider;
|
|
||||||
class VfsFilesystem;
|
|
||||||
} // namespace FileSys
|
|
||||||
|
|
||||||
namespace InputCommon {
|
|
||||||
class InputSubsystem;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace Service::AM {
|
|
||||||
struct FrontendAppletParameters;
|
|
||||||
enum class AppletId : u32;
|
|
||||||
} // namespace Service::AM
|
|
||||||
|
|
||||||
namespace Service::AM::Frontend {
|
|
||||||
enum class SwkbdResult : u32;
|
|
||||||
enum class SwkbdTextCheckResult : u32;
|
|
||||||
enum class SwkbdReplyType : u32;
|
|
||||||
enum class WebExitReason : u32;
|
|
||||||
} // namespace Service::AM::Frontend
|
|
||||||
|
|
||||||
namespace Service::NFC {
|
|
||||||
class NfcDevice;
|
|
||||||
} // namespace Service::NFC
|
|
||||||
|
|
||||||
namespace Service::NFP {
|
|
||||||
enum class CabinetMode : u8;
|
|
||||||
} // namespace Service::NFP
|
|
||||||
|
|
||||||
namespace Ui {
|
|
||||||
class MainWindow;
|
|
||||||
}
|
|
||||||
|
|
||||||
enum class EmulatedDirectoryTarget {
|
|
||||||
NAND,
|
|
||||||
SDMC,
|
|
||||||
};
|
|
||||||
|
|
||||||
namespace VkDeviceInfo {
|
|
||||||
class Record;
|
|
||||||
}
|
|
||||||
|
|
||||||
class VolumeButton : public QPushButton {
|
class VolumeButton : public QPushButton {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
@@ -142,16 +83,12 @@ public:
|
|||||||
explicit VolumeButton(QWidget* parent = nullptr) : QPushButton(parent), scroll_multiplier(1) {
|
explicit VolumeButton(QWidget* parent = nullptr) : QPushButton(parent), scroll_multiplier(1) {
|
||||||
connect(&scroll_timer, &QTimer::timeout, this, &VolumeButton::ResetMultiplier);
|
connect(&scroll_timer, &QTimer::timeout, this, &VolumeButton::ResetMultiplier);
|
||||||
}
|
}
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void VolumeChanged();
|
void VolumeChanged();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void wheelEvent(QWheelEvent* event) override;
|
void wheelEvent(QWheelEvent* event) override;
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void ResetMultiplier();
|
void ResetMultiplier();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int scroll_multiplier;
|
int scroll_multiplier;
|
||||||
QTimer scroll_timer;
|
QTimer scroll_timer;
|
||||||
@@ -160,94 +97,45 @@ private:
|
|||||||
|
|
||||||
class GMainWindow : public QMainWindow {
|
class GMainWindow : public QMainWindow {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
/// Max number of recently loaded items to keep track of
|
|
||||||
static const int max_recent_files_item = 10;
|
static const int max_recent_files_item = 10;
|
||||||
|
|
||||||
friend class PerformanceOverlay;
|
friend class PerformanceOverlay;
|
||||||
friend class VramOverlay;
|
friend class VramOverlay;
|
||||||
|
enum { CREATE_SHORTCUT_MSGBOX_FULLSCREEN_YES, CREATE_SHORTCUT_MSGBOX_SUCCESS, CREATE_SHORTCUT_MSGBOX_ERROR, CREATE_SHORTCUT_MSGBOX_APPVOLATILE_WARNING };
|
||||||
enum {
|
|
||||||
CREATE_SHORTCUT_MSGBOX_FULLSCREEN_YES,
|
|
||||||
CREATE_SHORTCUT_MSGBOX_SUCCESS,
|
|
||||||
CREATE_SHORTCUT_MSGBOX_ERROR,
|
|
||||||
CREATE_SHORTCUT_MSGBOX_APPVOLATILE_WARNING,
|
|
||||||
};
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void filterBarSetChecked(bool state);
|
void filterBarSetChecked(bool state);
|
||||||
void UpdateUITheme();
|
void UpdateUITheme();
|
||||||
explicit GMainWindow(std::unique_ptr<QtConfig> config_, bool has_broken_vulkan);
|
explicit GMainWindow(std::unique_ptr<QtConfig> config_, bool has_broken_vulkan);
|
||||||
~GMainWindow() override;
|
~GMainWindow() override;
|
||||||
|
|
||||||
bool DropAction(QDropEvent* event);
|
bool DropAction(QDropEvent* event);
|
||||||
void AcceptDropEvent(QDropEvent* event);
|
void AcceptDropEvent(QDropEvent* event);
|
||||||
|
MultiplayerState* GetMultiplayerState() { return multiplayer_state; }
|
||||||
/**
|
bool IsEmulationRunning() const { return emulation_running; }
|
||||||
* This is the new function to provide access to the MultiplayerState.
|
|
||||||
*/
|
|
||||||
MultiplayerState* GetMultiplayerState() {
|
|
||||||
return multiplayer_state;
|
|
||||||
}
|
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
|
|
||||||
/**
|
|
||||||
* Signal that is emitted when a new EmuThread has been created and an emulation session is
|
|
||||||
* about to start. At this time, the core system emulation has been initialized, and all
|
|
||||||
* emulation handles and memory should be valid.
|
|
||||||
*
|
|
||||||
* @param emu_thread Pointer to the newly created EmuThread (to be used by widgets that need to
|
|
||||||
* access/change emulation state).
|
|
||||||
*/
|
|
||||||
void EmulationStarting(EmuThread* emu_thread);
|
void EmulationStarting(EmuThread* emu_thread);
|
||||||
|
|
||||||
/**
|
|
||||||
* Signal that is emitted when emulation is about to stop. At this time, the EmuThread and core
|
|
||||||
* system emulation handles and memory are still valid, but are about become invalid.
|
|
||||||
*/
|
|
||||||
void EmulationStopping();
|
void EmulationStopping();
|
||||||
|
|
||||||
// Signal that tells widgets to update icons to use the current theme
|
|
||||||
void UpdateThemedIcons();
|
void UpdateThemedIcons();
|
||||||
|
|
||||||
void UpdateInstallProgress();
|
void UpdateInstallProgress();
|
||||||
|
|
||||||
void AmiiboSettingsFinished(bool is_success, const std::string& name);
|
void AmiiboSettingsFinished(bool is_success, const std::string& name);
|
||||||
|
|
||||||
void ControllerSelectorReconfigureFinished(bool is_success);
|
void ControllerSelectorReconfigureFinished(bool is_success);
|
||||||
|
|
||||||
void ErrorDisplayFinished();
|
void ErrorDisplayFinished();
|
||||||
|
|
||||||
void ProfileSelectorFinishedSelection(std::optional<Common::UUID> uuid);
|
void ProfileSelectorFinishedSelection(std::optional<Common::UUID> uuid);
|
||||||
|
void SoftwareKeyboardSubmitNormalText(Service::AM::Frontend::SwkbdResult result, std::u16string submitted_text, bool confirmed);
|
||||||
void SoftwareKeyboardSubmitNormalText(Service::AM::Frontend::SwkbdResult result,
|
void SoftwareKeyboardSubmitInlineText(Service::AM::Frontend::SwkbdReplyType reply_type, std::u16string submitted_text, s32 cursor_position);
|
||||||
std::u16string submitted_text, bool confirmed);
|
|
||||||
void SoftwareKeyboardSubmitInlineText(Service::AM::Frontend::SwkbdReplyType reply_type,
|
|
||||||
std::u16string submitted_text, s32 cursor_position);
|
|
||||||
|
|
||||||
void WebBrowserExtractOfflineRomFS();
|
void WebBrowserExtractOfflineRomFS();
|
||||||
void WebBrowserClosed(Service::AM::Frontend::WebExitReason exit_reason, std::string last_url);
|
void WebBrowserClosed(Service::AM::Frontend::WebExitReason exit_reason, std::string last_url);
|
||||||
|
|
||||||
void SigInterrupt();
|
void SigInterrupt();
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void OnLoadComplete();
|
void OnLoadComplete();
|
||||||
void OnExecuteProgram(std::size_t program_index);
|
void OnExecuteProgram(std::size_t program_index);
|
||||||
void OnExit();
|
void OnExit();
|
||||||
void OnSaveConfig();
|
void OnSaveConfig();
|
||||||
void AmiiboSettingsShowDialog(const Core::Frontend::CabinetParameters& parameters,
|
void AmiiboSettingsShowDialog(const Core::Frontend::CabinetParameters& parameters, std::shared_ptr<Service::NFC::NfcDevice> nfp_device);
|
||||||
std::shared_ptr<Service::NFC::NfcDevice> nfp_device);
|
|
||||||
void AmiiboSettingsRequestExit();
|
void AmiiboSettingsRequestExit();
|
||||||
void ControllerSelectorReconfigureControllers(
|
void ControllerSelectorReconfigureControllers(const Core::Frontend::ControllerParameters& parameters);
|
||||||
const Core::Frontend::ControllerParameters& parameters);
|
|
||||||
void ControllerSelectorRequestExit();
|
void ControllerSelectorRequestExit();
|
||||||
void SoftwareKeyboardInitialize(
|
void SoftwareKeyboardInitialize(bool is_inline, Core::Frontend::KeyboardInitializeParameters initialize_parameters);
|
||||||
bool is_inline, Core::Frontend::KeyboardInitializeParameters initialize_parameters);
|
|
||||||
void SoftwareKeyboardShowNormal();
|
void SoftwareKeyboardShowNormal();
|
||||||
void SoftwareKeyboardShowTextCheck(
|
void SoftwareKeyboardShowTextCheck(Service::AM::Frontend::SwkbdTextCheckResult text_check_result, std::u16string text_check_message);
|
||||||
Service::AM::Frontend::SwkbdTextCheckResult text_check_result,
|
|
||||||
std::u16string text_check_message);
|
|
||||||
void SoftwareKeyboardShowInline(Core::Frontend::InlineAppearParameters appear_parameters);
|
void SoftwareKeyboardShowInline(Core::Frontend::InlineAppearParameters appear_parameters);
|
||||||
void SoftwareKeyboardHideInline();
|
void SoftwareKeyboardHideInline();
|
||||||
void SoftwareKeyboardInlineTextChanged(Core::Frontend::InlineTextParameters text_parameters);
|
void SoftwareKeyboardInlineTextChanged(Core::Frontend::InlineTextParameters text_parameters);
|
||||||
@@ -256,98 +144,51 @@ public slots:
|
|||||||
void ErrorDisplayRequestExit();
|
void ErrorDisplayRequestExit();
|
||||||
void ProfileSelectorSelectProfile(const Core::Frontend::ProfileSelectParameters& parameters);
|
void ProfileSelectorSelectProfile(const Core::Frontend::ProfileSelectParameters& parameters);
|
||||||
void ProfileSelectorRequestExit();
|
void ProfileSelectorRequestExit();
|
||||||
void WebBrowserOpenWebPage(const std::string& main_url, const std::string& additional_args,
|
void WebBrowserOpenWebPage(const std::string& main_url, const std::string& additional_args, bool is_local);
|
||||||
bool is_local);
|
|
||||||
void WebBrowserRequestExit();
|
void WebBrowserRequestExit();
|
||||||
void OnAppFocusStateChanged(Qt::ApplicationState state);
|
void OnAppFocusStateChanged(Qt::ApplicationState state);
|
||||||
void OnTasStateChanged();
|
void OnTasStateChanged();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/// Updates an action's shortcut and text to reflect an updated hotkey from the hotkey registry.
|
void LinkActionShortcut(QAction* action, const QString& action_name, const bool tas_allowed = false);
|
||||||
void LinkActionShortcut(QAction* action, const QString& action_name,
|
|
||||||
const bool tas_allowed = false);
|
|
||||||
|
|
||||||
void RegisterMetaTypes();
|
void RegisterMetaTypes();
|
||||||
void RegisterAutoloaderContents();
|
void RegisterAutoloaderContents();
|
||||||
|
|
||||||
void InitializeWidgets();
|
void InitializeWidgets();
|
||||||
void InitializeDebugWidgets();
|
void InitializeDebugWidgets();
|
||||||
void InitializeRecentFileMenuActions();
|
void InitializeRecentFileMenuActions();
|
||||||
|
|
||||||
void SetDefaultUIGeometry();
|
void SetDefaultUIGeometry();
|
||||||
void RestoreUIState();
|
void RestoreUIState();
|
||||||
|
|
||||||
void ConnectWidgetEvents();
|
void ConnectWidgetEvents();
|
||||||
void ConnectMenuEvents();
|
void ConnectMenuEvents();
|
||||||
void UpdateMenuState();
|
void UpdateMenuState();
|
||||||
|
|
||||||
void SetupPrepareForSleep();
|
void SetupPrepareForSleep();
|
||||||
|
|
||||||
void PreventOSSleep();
|
void PreventOSSleep();
|
||||||
void AllowOSSleep();
|
void AllowOSSleep();
|
||||||
|
|
||||||
bool LoadROM(const QString& filename, Service::AM::FrontendAppletParameters params);
|
bool LoadROM(const QString& filename, Service::AM::FrontendAppletParameters params);
|
||||||
void BootGame(const QString& filename, Service::AM::FrontendAppletParameters params,
|
void BootGame(const QString& filename, Service::AM::FrontendAppletParameters params, StartGameType with_config = StartGameType::Normal);
|
||||||
StartGameType with_config = StartGameType::Normal);
|
|
||||||
void BootGameFromList(const QString& filename, StartGameType with_config);
|
void BootGameFromList(const QString& filename, StartGameType with_config);
|
||||||
void ShutdownGame();
|
void ShutdownGame();
|
||||||
|
|
||||||
void ShowTelemetryCallout();
|
void ShowTelemetryCallout();
|
||||||
void SetDiscordEnabled(bool state);
|
void SetDiscordEnabled(bool state);
|
||||||
void LoadAmiibo(const QString& filename);
|
void LoadAmiibo(const QString& filename);
|
||||||
|
|
||||||
bool SelectAndSetCurrentUser(const Core::Frontend::ProfileSelectParameters& parameters);
|
bool SelectAndSetCurrentUser(const Core::Frontend::ProfileSelectParameters& parameters);
|
||||||
|
|
||||||
/**
|
|
||||||
* Stores the filename in the recently loaded files list.
|
|
||||||
* The new filename is stored at the beginning of the recently loaded files list.
|
|
||||||
* After inserting the new entry, duplicates are removed meaning that if
|
|
||||||
* this was inserted from \a OnMenuRecentFile(), the entry will be put on top
|
|
||||||
* and remove from its previous position.
|
|
||||||
*
|
|
||||||
* Finally, this function calls \a UpdateRecentFiles() to update the UI.
|
|
||||||
*
|
|
||||||
* @param filename the filename to store
|
|
||||||
*/
|
|
||||||
void StoreRecentFile(const QString& filename);
|
void StoreRecentFile(const QString& filename);
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates the recent files menu.
|
|
||||||
* Menu entries are rebuilt from the configuration file.
|
|
||||||
* If there is no entry in the menu, the menu is greyed out.
|
|
||||||
*/
|
|
||||||
void UpdateRecentFiles();
|
void UpdateRecentFiles();
|
||||||
|
|
||||||
/**
|
|
||||||
* If the emulation is running,
|
|
||||||
* asks the user if he really want to close the emulator
|
|
||||||
*
|
|
||||||
* @return true if the user confirmed
|
|
||||||
*/
|
|
||||||
bool ConfirmClose();
|
bool ConfirmClose();
|
||||||
bool ConfirmChangeGame();
|
bool ConfirmChangeGame();
|
||||||
bool ConfirmForceLockedExit();
|
bool ConfirmForceLockedExit();
|
||||||
void RequestGameExit();
|
void RequestGameExit();
|
||||||
void changeEvent(QEvent* event) override;
|
void changeEvent(QEvent* event) override;
|
||||||
void closeEvent(QCloseEvent* event) override;
|
void closeEvent(QCloseEvent* event) override;
|
||||||
|
std::string CreateTASFramesString(std::array<size_t, InputCommon::TasInput::PLAYER_NUMBER> frames) const;
|
||||||
std::string CreateTASFramesString(
|
|
||||||
std::array<size_t, InputCommon::TasInput::PLAYER_NUMBER> frames) const;
|
|
||||||
|
|
||||||
#ifdef __unix__
|
#ifdef __unix__
|
||||||
void SetupSigInterrupts();
|
void SetupSigInterrupts();
|
||||||
static void HandleSigInterrupt(int);
|
static void HandleSigInterrupt(int);
|
||||||
void OnSigInterruptNotifierActivated();
|
void OnSigInterruptNotifierActivated();
|
||||||
void SetGamemodeEnabled(bool state);
|
void SetGamemodeEnabled(bool state);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
Service::AM::FrontendAppletParameters ApplicationAppletParameters();
|
Service::AM::FrontendAppletParameters ApplicationAppletParameters();
|
||||||
Service::AM::FrontendAppletParameters LibraryAppletParameters(u64 program_id,
|
Service::AM::FrontendAppletParameters LibraryAppletParameters(u64 program_id, Service::AM::AppletId applet_id);
|
||||||
Service::AM::AppletId applet_id);
|
|
||||||
|
|
||||||
// This will hold and provide all discovered Autoloader content.
|
|
||||||
std::unique_ptr<FileSys::ManualContentProvider> autoloader_provider;
|
std::unique_ptr<FileSys::ManualContentProvider> autoloader_provider;
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void OnStartGame();
|
void OnStartGame();
|
||||||
void OnRestartGame();
|
void OnRestartGame();
|
||||||
@@ -357,22 +198,17 @@ private slots:
|
|||||||
void OnPrepareForSleep(bool prepare_sleep);
|
void OnPrepareForSleep(bool prepare_sleep);
|
||||||
void OnMenuReportCompatibility();
|
void OnMenuReportCompatibility();
|
||||||
void OnOpenSupport();
|
void OnOpenSupport();
|
||||||
/// Called whenever a user selects a game in the game list widget.
|
|
||||||
void OnGameListLoadFile(QString game_path, u64 program_id);
|
void OnGameListLoadFile(QString game_path, u64 program_id);
|
||||||
void OnGameListOpenFolder(u64 program_id, GameListOpenTarget target,
|
void OnGameListOpenFolder(u64 program_id, GameListOpenTarget target, const std::string& game_path);
|
||||||
const std::string& game_path);
|
|
||||||
void OnTransferableShaderCacheOpenFile(u64 program_id);
|
void OnTransferableShaderCacheOpenFile(u64 program_id);
|
||||||
void OnGameListRemoveInstalledEntry(u64 program_id, InstalledEntryType type);
|
void OnGameListRemoveInstalledEntry(u64 program_id, InstalledEntryType type);
|
||||||
void OnGameListRemoveFile(u64 program_id, GameListRemoveTarget target,
|
void OnGameListRemoveFile(u64 program_id, GameListRemoveTarget target, const std::string& game_path);
|
||||||
const std::string& game_path);
|
|
||||||
void OnGameListRemovePlayTimeData(u64 program_id);
|
void OnGameListRemovePlayTimeData(u64 program_id);
|
||||||
void OnGameListDumpRomFS(u64 program_id, const std::string& game_path, DumpRomFSTarget target);
|
void OnGameListDumpRomFS(u64 program_id, const std::string& game_path, DumpRomFSTarget target);
|
||||||
void OnGameListVerifyIntegrity(const std::string& game_path);
|
void OnGameListVerifyIntegrity(const std::string& game_path);
|
||||||
void OnGameListCopyTID(u64 program_id);
|
void OnGameListCopyTID(u64 program_id);
|
||||||
void OnGameListNavigateToGamedbEntry(u64 program_id,
|
void OnGameListNavigateToGamedbEntry(u64 program_id, const CompatibilityList& compatibility_list);
|
||||||
const CompatibilityList& compatibility_list);
|
void OnGameListCreateShortcut(u64 program_id, const std::string& game_path, GameListShortcutTarget target);
|
||||||
void OnGameListCreateShortcut(u64 program_id, const std::string& game_path,
|
|
||||||
GameListShortcutTarget target);
|
|
||||||
void OnGameListOpenDirectory(const QString& directory);
|
void OnGameListOpenDirectory(const QString& directory);
|
||||||
void OnGameListAddDirectory();
|
void OnGameListAddDirectory();
|
||||||
void OnGameListShowList(bool show);
|
void OnGameListShowList(bool show);
|
||||||
@@ -411,16 +247,13 @@ private slots:
|
|||||||
void OnToggleGridView();
|
void OnToggleGridView();
|
||||||
void OnToggleStatusBar();
|
void OnToggleStatusBar();
|
||||||
void OnTogglePerformanceOverlay();
|
void OnTogglePerformanceOverlay();
|
||||||
|
void OnToggleMultiplayerRoomOverlay();
|
||||||
void OnToggleVramOverlay();
|
void OnToggleVramOverlay();
|
||||||
void OnDisplayTitleBars(bool);
|
void OnDisplayTitleBars(bool);
|
||||||
|
|
||||||
// Performance overlay access methods
|
|
||||||
double GetCurrentFPS() const;
|
double GetCurrentFPS() const;
|
||||||
double GetCurrentFrameTime() const;
|
double GetCurrentFrameTime() const;
|
||||||
u32 GetShadersBuilding() const;
|
u32 GetShadersBuilding() const;
|
||||||
double GetEmulationSpeed() const;
|
double GetEmulationSpeed() const;
|
||||||
|
|
||||||
// VRAM overlay access methods
|
|
||||||
u64 GetTotalVram() const;
|
u64 GetTotalVram() const;
|
||||||
u64 GetUsedVram() const;
|
u64 GetUsedVram() const;
|
||||||
u64 GetBufferMemoryUsage() const;
|
u64 GetBufferMemoryUsage() const;
|
||||||
@@ -448,7 +281,6 @@ private slots:
|
|||||||
void OnShutdownBeginDialog();
|
void OnShutdownBeginDialog();
|
||||||
void OnEmulationStopped();
|
void OnEmulationStopped();
|
||||||
void OnEmulationStopTimeExpired();
|
void OnEmulationStopTimeExpired();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QString GetGameListErrorRemoving(InstalledEntryType type) const;
|
QString GetGameListErrorRemoving(InstalledEntryType type) const;
|
||||||
void RemoveBaseContent(u64 program_id, InstalledEntryType type);
|
void RemoveBaseContent(u64 program_id, InstalledEntryType type);
|
||||||
@@ -460,12 +292,10 @@ private:
|
|||||||
void RemoveCustomConfiguration(u64 program_id, const std::string& game_path);
|
void RemoveCustomConfiguration(u64 program_id, const std::string& game_path);
|
||||||
void RemovePlayTimeData(u64 program_id);
|
void RemovePlayTimeData(u64 program_id);
|
||||||
void RemoveCacheStorage(u64 program_id);
|
void RemoveCacheStorage(u64 program_id);
|
||||||
bool SelectRomFSDumpTarget(const FileSys::ContentProvider&, u64 program_id,
|
bool SelectRomFSDumpTarget(const FileSys::ContentProvider&, u64 program_id, u64* selected_title_id, u8* selected_content_record_type);
|
||||||
u64* selected_title_id, u8* selected_content_record_type);
|
|
||||||
ContentManager::InstallResult InstallNCA(const QString& filename);
|
ContentManager::InstallResult InstallNCA(const QString& filename);
|
||||||
void MigrateConfigFiles();
|
void MigrateConfigFiles();
|
||||||
void UpdateWindowTitle(std::string_view title_name = {}, std::string_view title_version = {},
|
void UpdateWindowTitle(std::string_view title_name = {}, std::string_view title_version = {}, std::string_view gpu_vendor = {});
|
||||||
std::string_view gpu_vendor = {});
|
|
||||||
void UpdateDockedButton();
|
void UpdateDockedButton();
|
||||||
void UpdateAPIText();
|
void UpdateAPIText();
|
||||||
void UpdateFilterText();
|
void UpdateFilterText();
|
||||||
@@ -485,54 +315,28 @@ private:
|
|||||||
bool CheckFirmwarePresence();
|
bool CheckFirmwarePresence();
|
||||||
void SetFirmwareVersion();
|
void SetFirmwareVersion();
|
||||||
void ConfigureFilesystemProvider(const std::string& filepath);
|
void ConfigureFilesystemProvider(const std::string& filepath);
|
||||||
/**
|
|
||||||
* Open (or not) the right confirm dialog based on current setting and game exit lock
|
|
||||||
* @returns true if the player confirmed or the settings do no require it
|
|
||||||
*/
|
|
||||||
bool ConfirmShutdownGame();
|
bool ConfirmShutdownGame();
|
||||||
|
|
||||||
QString GetTasStateDescription() const;
|
QString GetTasStateDescription() const;
|
||||||
bool CreateShortcutMessagesGUI(QWidget* parent, int imsg, const QString& game_title);
|
bool CreateShortcutMessagesGUI(QWidget* parent, int imsg, const QString& game_title);
|
||||||
bool MakeShortcutIcoPath(const u64 program_id, const std::string_view game_file_name,
|
bool MakeShortcutIcoPath(const u64 program_id, const std::string_view game_file_name, std::filesystem::path& out_icon_path);
|
||||||
std::filesystem::path& out_icon_path);
|
bool CreateShortcutLink(const std::filesystem::path& shortcut_path, const std::string& comment, const std::filesystem::path& icon_path, const std::filesystem::path& command, const std::string& arguments, const std::string& categories, const std::string& keywords, const std::string& name);
|
||||||
bool CreateShortcutLink(const std::filesystem::path& shortcut_path, const std::string& comment,
|
bool question(QWidget* parent, const QString& title, const QString& text, QMessageBox::StandardButtons buttons = QMessageBox::StandardButtons(QMessageBox::Yes | QMessageBox::No), QMessageBox::StandardButton defaultButton = QMessageBox::NoButton);
|
||||||
const std::filesystem::path& icon_path,
|
|
||||||
const std::filesystem::path& command, const std::string& arguments,
|
|
||||||
const std::string& categories, const std::string& keywords,
|
|
||||||
const std::string& name);
|
|
||||||
/**
|
|
||||||
* Mimic the behavior of QMessageBox::question but link controller navigation to the dialog
|
|
||||||
* The only difference is that it returns a boolean.
|
|
||||||
*
|
|
||||||
* @returns true if buttons contains QMessageBox::Yes and the user clicks on the "Yes" button.
|
|
||||||
*/
|
|
||||||
bool question(QWidget* parent, const QString& title, const QString& text,
|
|
||||||
QMessageBox::StandardButtons buttons =
|
|
||||||
QMessageBox::StandardButtons(QMessageBox::Yes | QMessageBox::No),
|
|
||||||
QMessageBox::StandardButton defaultButton = QMessageBox::NoButton);
|
|
||||||
|
|
||||||
std::unique_ptr<Ui::MainWindow> ui;
|
std::unique_ptr<Ui::MainWindow> ui;
|
||||||
|
|
||||||
std::unique_ptr<Core::System> system;
|
std::unique_ptr<Core::System> system;
|
||||||
std::unique_ptr<DiscordRPC::DiscordInterface> discord_rpc;
|
std::unique_ptr<DiscordRPC::DiscordInterface> discord_rpc;
|
||||||
std::unique_ptr<PlayTime::PlayTimeManager> play_time_manager;
|
std::unique_ptr<PlayTime::PlayTimeManager> play_time_manager;
|
||||||
std::shared_ptr<InputCommon::InputSubsystem> input_subsystem;
|
std::shared_ptr<InputCommon::InputSubsystem> input_subsystem;
|
||||||
|
|
||||||
MultiplayerState* multiplayer_state = nullptr;
|
MultiplayerState* multiplayer_state = nullptr;
|
||||||
|
|
||||||
GRenderWindow* render_window;
|
GRenderWindow* render_window;
|
||||||
GameList* game_list;
|
GameList* game_list;
|
||||||
LoadingScreen* loading_screen;
|
LoadingScreen* loading_screen;
|
||||||
QTimer shutdown_timer;
|
QTimer shutdown_timer;
|
||||||
OverlayDialog* shutdown_dialog{};
|
OverlayDialog* shutdown_dialog{};
|
||||||
PerformanceOverlay* performance_overlay{};
|
PerformanceOverlay* performance_overlay{};
|
||||||
|
MultiplayerRoomOverlay* multiplayer_room_overlay{};
|
||||||
VramOverlay* vram_overlay{};
|
VramOverlay* vram_overlay{};
|
||||||
|
|
||||||
GameListPlaceholder* game_list_placeholder;
|
GameListPlaceholder* game_list_placeholder;
|
||||||
|
|
||||||
std::vector<VkDeviceInfo::Record> vk_device_records;
|
std::vector<VkDeviceInfo::Record> vk_device_records;
|
||||||
|
|
||||||
// Status bar elements
|
|
||||||
QLabel* message_label = nullptr;
|
QLabel* message_label = nullptr;
|
||||||
QLabel* shader_building_label = nullptr;
|
QLabel* shader_building_label = nullptr;
|
||||||
QLabel* res_scale_label = nullptr;
|
QLabel* res_scale_label = nullptr;
|
||||||
@@ -550,72 +354,42 @@ private:
|
|||||||
QWidget* volume_popup = nullptr;
|
QWidget* volume_popup = nullptr;
|
||||||
QSlider* volume_slider = nullptr;
|
QSlider* volume_slider = nullptr;
|
||||||
QTimer status_bar_update_timer;
|
QTimer status_bar_update_timer;
|
||||||
|
|
||||||
std::unique_ptr<QtConfig> config;
|
std::unique_ptr<QtConfig> config;
|
||||||
|
|
||||||
// Whether emulation is currently running in citron.
|
|
||||||
bool emulation_running = false;
|
bool emulation_running = false;
|
||||||
std::unique_ptr<EmuThread> emu_thread;
|
std::unique_ptr<EmuThread> emu_thread;
|
||||||
// The path to the game currently running
|
|
||||||
QString current_game_path;
|
QString current_game_path;
|
||||||
// Whether a user was set on the command line (skips UserSelector if it's forced to show up)
|
|
||||||
bool user_flag_cmd_line = false;
|
bool user_flag_cmd_line = false;
|
||||||
|
|
||||||
bool auto_paused = false;
|
bool auto_paused = false;
|
||||||
bool auto_muted = false;
|
bool auto_muted = false;
|
||||||
QTimer mouse_hide_timer;
|
QTimer mouse_hide_timer;
|
||||||
QTimer update_input_timer;
|
QTimer update_input_timer;
|
||||||
|
|
||||||
QString startup_icon_theme;
|
QString startup_icon_theme;
|
||||||
bool os_dark_mode = false;
|
bool os_dark_mode = false;
|
||||||
|
|
||||||
// FS
|
|
||||||
std::shared_ptr<FileSys::VfsFilesystem> vfs;
|
std::shared_ptr<FileSys::VfsFilesystem> vfs;
|
||||||
std::unique_ptr<FileSys::ManualContentProvider> provider;
|
std::unique_ptr<FileSys::ManualContentProvider> provider;
|
||||||
|
|
||||||
// Debugger panes
|
|
||||||
ProfilerWidget* profilerWidget;
|
ProfilerWidget* profilerWidget;
|
||||||
MicroProfileDialog* microProfileDialog;
|
MicroProfileDialog* microProfileDialog;
|
||||||
WaitTreeWidget* waitTreeWidget;
|
WaitTreeWidget* waitTreeWidget;
|
||||||
ControllerDialog* controller_dialog;
|
ControllerDialog* controller_dialog;
|
||||||
|
|
||||||
QAction* actions_recent_files[max_recent_files_item];
|
QAction* actions_recent_files[max_recent_files_item];
|
||||||
|
|
||||||
// stores default icon theme search paths for the platform
|
|
||||||
QStringList default_theme_paths;
|
QStringList default_theme_paths;
|
||||||
|
|
||||||
HotkeyRegistry hotkey_registry;
|
HotkeyRegistry hotkey_registry;
|
||||||
|
|
||||||
QTranslator translator;
|
QTranslator translator;
|
||||||
|
|
||||||
// Install progress dialog
|
|
||||||
QProgressDialog* install_progress;
|
QProgressDialog* install_progress;
|
||||||
|
|
||||||
// Last game booted, used for multi-process apps
|
|
||||||
QString last_filename_booted;
|
QString last_filename_booted;
|
||||||
|
|
||||||
// Applets
|
|
||||||
QtAmiiboSettingsDialog* cabinet_applet = nullptr;
|
QtAmiiboSettingsDialog* cabinet_applet = nullptr;
|
||||||
QtControllerSelectorDialog* controller_applet = nullptr;
|
QtControllerSelectorDialog* controller_applet = nullptr;
|
||||||
QtProfileSelectionDialog* profile_select_applet = nullptr;
|
QtProfileSelectionDialog* profile_select_applet = nullptr;
|
||||||
QDialog* error_applet = nullptr;
|
QDialog* error_applet = nullptr;
|
||||||
QtSoftwareKeyboardDialog* software_keyboard = nullptr;
|
QtSoftwareKeyboardDialog* software_keyboard = nullptr;
|
||||||
QtNXWebEngineView* web_applet = nullptr;
|
QtNXWebEngineView* web_applet = nullptr;
|
||||||
|
|
||||||
// True if amiibo file select is visible
|
|
||||||
bool is_amiibo_file_select_active{};
|
bool is_amiibo_file_select_active{};
|
||||||
|
|
||||||
// True if load file select is visible
|
|
||||||
bool is_load_file_select_active{};
|
bool is_load_file_select_active{};
|
||||||
|
|
||||||
// True if TAS recording dialog is visible
|
|
||||||
bool is_tas_recording_dialog_active{};
|
bool is_tas_recording_dialog_active{};
|
||||||
|
|
||||||
#ifdef __unix__
|
#ifdef __unix__
|
||||||
QSocketNotifier* sig_interrupt_notifier;
|
QSocketNotifier* sig_interrupt_notifier;
|
||||||
static std::array<int, 3> sig_interrupt_fds;
|
static std::array<int, 3> sig_interrupt_fds;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void dropEvent(QDropEvent* event) override;
|
void dropEvent(QDropEvent* event) override;
|
||||||
void dragEnterEvent(QDragEnterEvent* event) override;
|
void dragEnterEvent(QDragEnterEvent* event) override;
|
||||||
|
|||||||
@@ -128,6 +128,7 @@
|
|||||||
<addaction name="action_Show_Status_Bar"/>
|
<addaction name="action_Show_Status_Bar"/>
|
||||||
<addaction name="action_Show_Performance_Overlay"/>
|
<addaction name="action_Show_Performance_Overlay"/>
|
||||||
<addaction name="action_Show_Vram_Overlay"/>
|
<addaction name="action_Show_Vram_Overlay"/>
|
||||||
|
<addaction name="action_Show_Multiplayer_Room_Overlay"/>
|
||||||
<addaction name="action_Toggle_Grid_View"/>
|
<addaction name="action_Toggle_Grid_View"/>
|
||||||
<addaction name="separator"/>
|
<addaction name="separator"/>
|
||||||
<addaction name="menu_Reset_Window_Size"/>
|
<addaction name="menu_Reset_Window_Size"/>
|
||||||
@@ -335,6 +336,17 @@
|
|||||||
<string>Show VRAM Monitor</string>
|
<string>Show VRAM Monitor</string>
|
||||||
</property>
|
</property>
|
||||||
</action>
|
</action>
|
||||||
|
<action name="action_Show_Multiplayer_Room_Overlay">
|
||||||
|
<property name="checkable">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Show &Multiplayer Room Overlay</string>
|
||||||
|
</property>
|
||||||
|
<property name="iconText">
|
||||||
|
<string>Show Multiplayer Room Overlay</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
<action name="action_Toggle_Grid_View">
|
<action name="action_Toggle_Grid_View">
|
||||||
<property name="checkable">
|
<property name="checkable">
|
||||||
<bool>true</bool>
|
<bool>true</bool>
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2018 Citra Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2018 Citra Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-FileCopyrightText: Copyright 2025 citron Emulator Project
|
||||||
|
// SPDX-FileCopyrightText: GPL-2.0-or-later
|
||||||
|
|
||||||
#include <QAction>
|
#include <QAction>
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
@@ -334,3 +335,8 @@ void MultiplayerState::UpdateGameList(QStandardItemModel* game_list) {
|
|||||||
host_room->UpdateGameList(game_list);
|
host_room->UpdateGameList(game_list);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NEW: Definition for our check
|
||||||
|
bool MultiplayerState::IsClientRoomVisible() const {
|
||||||
|
return client_room && client_room->isVisible();
|
||||||
|
}
|
||||||
|
|||||||
@@ -34,20 +34,12 @@ public:
|
|||||||
QAction* show_room, Core::System& system_);
|
QAction* show_room, Core::System& system_);
|
||||||
~MultiplayerState();
|
~MultiplayerState();
|
||||||
|
|
||||||
/**
|
|
||||||
* This is the new function to safely access the multiplayer session.
|
|
||||||
*/
|
|
||||||
std::shared_ptr<Core::AnnounceMultiplayerSession> GetSession() {
|
std::shared_ptr<Core::AnnounceMultiplayerSession> GetSession() {
|
||||||
return announce_multiplayer_session;
|
return announce_multiplayer_session;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Close all open multiplayer related dialogs
|
|
||||||
*/
|
|
||||||
void Close();
|
void Close();
|
||||||
|
|
||||||
void SetNotificationStatus(NotificationStatus state);
|
void SetNotificationStatus(NotificationStatus state);
|
||||||
|
|
||||||
void UpdateNotificationStatus();
|
void UpdateNotificationStatus();
|
||||||
|
|
||||||
ClickableLabel* GetStatusText() const {
|
ClickableLabel* GetStatusText() const {
|
||||||
@@ -60,18 +52,14 @@ public:
|
|||||||
|
|
||||||
void retranslateUi();
|
void retranslateUi();
|
||||||
|
|
||||||
/**
|
Network::RoomNetwork& GetRoomNetwork() {
|
||||||
* Whether a public room is being hosted or not.
|
return room_network;
|
||||||
* When this is true, Web Services configuration should be disabled.
|
}
|
||||||
*/
|
|
||||||
|
bool IsClientRoomVisible() const; // NEW: Declaration for our check
|
||||||
|
|
||||||
bool IsHostingPublicRoom() const;
|
bool IsHostingPublicRoom() const;
|
||||||
|
|
||||||
void UpdateCredentials();
|
void UpdateCredentials();
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates the multiplayer dialogs with a new game list model.
|
|
||||||
* This model should be the original model of the game list.
|
|
||||||
*/
|
|
||||||
void UpdateGameList(QStandardItemModel* game_list);
|
void UpdateGameList(QStandardItemModel* game_list);
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
|
|||||||
235
src/citron/util/multiplayer_room_overlay.cpp
Normal file
235
src/citron/util/multiplayer_room_overlay.cpp
Normal file
@@ -0,0 +1,235 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2025 citron Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#include <QApplication>
|
||||||
|
#include <QPainter>
|
||||||
|
#include <QPainterPath>
|
||||||
|
#include <QScreen>
|
||||||
|
#include <QTimer>
|
||||||
|
#include <QMouseEvent>
|
||||||
|
#include <QWindow>
|
||||||
|
#include <QSizeGrip>
|
||||||
|
#include <QGridLayout>
|
||||||
|
|
||||||
|
#include "citron/main.h"
|
||||||
|
#include "citron/bootmanager.h"
|
||||||
|
#include "citron/util/multiplayer_room_overlay.h"
|
||||||
|
#include "network/network.h"
|
||||||
|
#include "network/room.h"
|
||||||
|
|
||||||
|
MultiplayerRoomOverlay::MultiplayerRoomOverlay(GMainWindow* parent)
|
||||||
|
: QWidget(parent), main_window(parent) {
|
||||||
|
|
||||||
|
setAttribute(Qt::WA_TranslucentBackground, true);
|
||||||
|
setWindowFlags(Qt::FramelessWindowHint | Qt::Tool | Qt::WindowStaysOnTopHint);
|
||||||
|
setFocusPolicy(Qt::ClickFocus);
|
||||||
|
|
||||||
|
background_color = QColor(20, 20, 20, 180);
|
||||||
|
border_color = QColor(60, 60, 60, 120);
|
||||||
|
|
||||||
|
main_layout = new QGridLayout(this);
|
||||||
|
main_layout->setContentsMargins(padding, padding, 0, 0); // No margins on bottom/right for grip
|
||||||
|
main_layout->setSpacing(6);
|
||||||
|
|
||||||
|
players_online_label = new QLabel(this);
|
||||||
|
chat_room_widget = new ChatRoom(this);
|
||||||
|
size_grip = new QSizeGrip(this);
|
||||||
|
|
||||||
|
players_online_label->setFont(QFont(QString::fromUtf8("Segoe UI"), 10, QFont::Bold));
|
||||||
|
players_online_label->setStyleSheet(QString::fromUtf8("color: #E0E0E0;"));
|
||||||
|
players_online_label->setText(QString::fromUtf8("Players Online: 0"));
|
||||||
|
players_online_label->setAttribute(Qt::WA_TransparentForMouseEvents, true);
|
||||||
|
|
||||||
|
size_grip->setFixedSize(16, 16);
|
||||||
|
|
||||||
|
if (main_window) {
|
||||||
|
GRenderWindow* render_window = main_window->findChild<GRenderWindow*>();
|
||||||
|
if (render_window) {
|
||||||
|
render_window->installEventFilter(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
main_layout->addWidget(players_online_label, 0, 0, 1, 2);
|
||||||
|
main_layout->addWidget(chat_room_widget, 1, 0, 1, 2);
|
||||||
|
main_layout->addWidget(size_grip, 1, 1, 1, 1, Qt::AlignBottom | Qt::AlignRight);
|
||||||
|
|
||||||
|
main_layout->setRowStretch(1, 1);
|
||||||
|
main_layout->setColumnStretch(0, 1);
|
||||||
|
|
||||||
|
setLayout(main_layout);
|
||||||
|
|
||||||
|
update_timer.setSingleShot(false);
|
||||||
|
connect(&update_timer, &QTimer::timeout, this, &MultiplayerRoomOverlay::UpdateRoomData);
|
||||||
|
|
||||||
|
setMinimumSize(280, 220);
|
||||||
|
resize(320, 280);
|
||||||
|
UpdatePosition();
|
||||||
|
}
|
||||||
|
|
||||||
|
MultiplayerRoomOverlay::~MultiplayerRoomOverlay() {
|
||||||
|
DisconnectFromRoom();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MultiplayerRoomOverlay::OnEmulationStarting() {
|
||||||
|
// When emulation starts, resume updates if we are visible.
|
||||||
|
if (is_visible) {
|
||||||
|
ConnectToRoom();
|
||||||
|
update_timer.start(500);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MultiplayerRoomOverlay::OnEmulationStopping() {
|
||||||
|
// CRASH FIX: When emulation stops, immediately disconnect from network objects.
|
||||||
|
update_timer.stop();
|
||||||
|
DisconnectFromRoom();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MultiplayerRoomOverlay::SetVisible(bool visible) {
|
||||||
|
if (is_visible == visible) return;
|
||||||
|
is_visible = visible;
|
||||||
|
|
||||||
|
if (visible) {
|
||||||
|
show();
|
||||||
|
// Only start connecting and updating if emulation is running.
|
||||||
|
if (main_window && main_window->IsEmulationRunning()) {
|
||||||
|
ConnectToRoom();
|
||||||
|
update_timer.start(500);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
hide();
|
||||||
|
update_timer.stop();
|
||||||
|
DisconnectFromRoom();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MultiplayerRoomOverlay::paintEvent(QPaintEvent* event) {
|
||||||
|
Q_UNUSED(event)
|
||||||
|
QPainter painter(this);
|
||||||
|
painter.setRenderHint(QPainter::Antialiasing, true);
|
||||||
|
QPainterPath background_path;
|
||||||
|
background_path.addRoundedRect(rect(), corner_radius, corner_radius);
|
||||||
|
painter.fillPath(background_path, background_color);
|
||||||
|
painter.setPen(QPen(border_color, border_width));
|
||||||
|
painter.drawPath(background_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MultiplayerRoomOverlay::resizeEvent(QResizeEvent* event) { QWidget::resizeEvent(event); if (!has_been_moved) UpdatePosition(); }
|
||||||
|
bool MultiplayerRoomOverlay::eventFilter(QObject* watched, QEvent* event) { if (event->type() == QEvent::MouseButtonPress) { if (chat_room_widget->hasFocus()) { chat_room_widget->clearFocus(); } } return QObject::eventFilter(watched, event); }
|
||||||
|
|
||||||
|
#if defined(Q_OS_LINUX)
|
||||||
|
void MultiplayerRoomOverlay::mousePressEvent(QMouseEvent* event) {
|
||||||
|
if (event->button() == Qt::LeftButton) {
|
||||||
|
if (size_grip->geometry().contains(event->pos())) {
|
||||||
|
// Let the size grip handle the event
|
||||||
|
} else if (!childAt(event->pos()) || childAt(event->pos()) == this) {
|
||||||
|
if (windowHandle()) windowHandle()->startSystemMove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
QWidget::mousePressEvent(event);
|
||||||
|
}
|
||||||
|
void MultiplayerRoomOverlay::mouseMoveEvent(QMouseEvent* event) { QWidget::mouseMoveEvent(event); }
|
||||||
|
#else
|
||||||
|
void MultiplayerRoomOverlay::mousePressEvent(QMouseEvent* event) {
|
||||||
|
if (event->button() == Qt::LeftButton) {
|
||||||
|
if (size_grip->geometry().contains(event->pos())) {
|
||||||
|
// Let the size grip handle the event
|
||||||
|
} else if (!childAt(event->pos()) || childAt(event->pos()) == this) {
|
||||||
|
is_dragging = true;
|
||||||
|
drag_start_pos = event->globalPosition().toPoint();
|
||||||
|
widget_start_pos = this->pos();
|
||||||
|
setCursor(Qt::ClosedHandCursor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
QWidget::mousePressEvent(event);
|
||||||
|
}
|
||||||
|
void MultiplayerRoomOverlay::mouseMoveEvent(QMouseEvent* event) {
|
||||||
|
if (is_dragging) {
|
||||||
|
QPoint delta = event->globalPosition().toPoint() - drag_start_pos;
|
||||||
|
move(widget_start_pos + delta);
|
||||||
|
}
|
||||||
|
QWidget::mouseMoveEvent(event); // Corrected typo here
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void MultiplayerRoomOverlay::mouseReleaseEvent(QMouseEvent* event) {
|
||||||
|
if (event->button() == Qt::LeftButton && is_dragging) {
|
||||||
|
is_dragging = false;
|
||||||
|
has_been_moved = true;
|
||||||
|
setCursor(Qt::ArrowCursor);
|
||||||
|
}
|
||||||
|
QWidget::mouseReleaseEvent(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MultiplayerRoomOverlay::ConnectToRoom() {
|
||||||
|
if (!main_window) return;
|
||||||
|
multiplayer_state = main_window->GetMultiplayerState();
|
||||||
|
if (!multiplayer_state) return;
|
||||||
|
|
||||||
|
if (multiplayer_state->IsClientRoomVisible()) {
|
||||||
|
chat_room_widget->setEnabled(false);
|
||||||
|
chat_room_widget->Clear();
|
||||||
|
chat_room_widget->AppendStatusMessage(tr("Chat available in main window."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
chat_room_widget->setEnabled(true);
|
||||||
|
auto& room_network = multiplayer_state->GetRoomNetwork();
|
||||||
|
room_member = room_network.GetRoomMember().lock();
|
||||||
|
|
||||||
|
if (room_member) {
|
||||||
|
chat_room_widget->Initialize(&room_network);
|
||||||
|
} else {
|
||||||
|
chat_room_widget->Clear();
|
||||||
|
chat_room_widget->AppendStatusMessage(tr("Not connected to a room."));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MultiplayerRoomOverlay::DisconnectFromRoom() {
|
||||||
|
chat_room_widget->Clear();
|
||||||
|
room_member.reset();
|
||||||
|
multiplayer_state = nullptr;
|
||||||
|
players_online_label->setText(QString::fromUtf8("Players Online: 0"));
|
||||||
|
}
|
||||||
|
|
||||||
|
void MultiplayerRoomOverlay::UpdateRoomData() {
|
||||||
|
if (!multiplayer_state) {
|
||||||
|
ConnectToRoom();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (multiplayer_state->IsClientRoomVisible()) {
|
||||||
|
if (chat_room_widget->isEnabled()) {
|
||||||
|
chat_room_widget->setEnabled(false);
|
||||||
|
chat_room_widget->Clear();
|
||||||
|
chat_room_widget->AppendStatusMessage(tr("Chat available in main window."));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!chat_room_widget->isEnabled()) {
|
||||||
|
ConnectToRoom();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (room_member && room_member->GetState() >= Network::RoomMember::State::Joined) {
|
||||||
|
const auto& members = room_member->GetMemberInformation();
|
||||||
|
QString label_text = QString::fromStdString("Players Online: <span style='color: #4CAF50;'>%1</span>").arg(members.size());
|
||||||
|
players_online_label->setText(label_text);
|
||||||
|
if (chat_room_widget->isEnabled()) {
|
||||||
|
chat_room_widget->SetPlayerList(members);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
players_online_label->setText(QString::fromUtf8("Players Online: 0"));
|
||||||
|
if (!room_member && !multiplayer_state->IsClientRoomVisible()) {
|
||||||
|
chat_room_widget->Clear();
|
||||||
|
chat_room_widget->AppendStatusMessage(tr("Not connected to a room."));
|
||||||
|
ConnectToRoom();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MultiplayerRoomOverlay::UpdatePosition() {
|
||||||
|
if (!main_window) return;
|
||||||
|
if (!has_been_moved) {
|
||||||
|
QPoint main_window_pos = main_window->mapToGlobal(QPoint(0, 0));
|
||||||
|
move(main_window_pos.x() + main_window->width() - this->width() - 10, main_window_pos.y() + 10);
|
||||||
|
}
|
||||||
|
}
|
||||||
77
src/citron/util/multiplayer_room_overlay.h
Normal file
77
src/citron/util/multiplayer_room_overlay.h
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2025 citron Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QWidget>
|
||||||
|
#include <QTimer>
|
||||||
|
#include <QPainter>
|
||||||
|
#include <QLabel>
|
||||||
|
#include <QGridLayout>
|
||||||
|
|
||||||
|
#include "citron/multiplayer/state.h"
|
||||||
|
#include "citron/multiplayer/chat_room.h"
|
||||||
|
|
||||||
|
class GMainWindow;
|
||||||
|
class QSizeGrip;
|
||||||
|
|
||||||
|
class MultiplayerRoomOverlay : public QWidget {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit MultiplayerRoomOverlay(GMainWindow* parent);
|
||||||
|
~MultiplayerRoomOverlay() override;
|
||||||
|
|
||||||
|
void SetVisible(bool visible);
|
||||||
|
bool IsVisible() const { return is_visible; }
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
// These slots are connected to the main window to prevent crashes.
|
||||||
|
void OnEmulationStarting();
|
||||||
|
void OnEmulationStopping();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void paintEvent(QPaintEvent* event) override;
|
||||||
|
void resizeEvent(QResizeEvent* event) override;
|
||||||
|
void mousePressEvent(QMouseEvent* event) override;
|
||||||
|
void mouseMoveEvent(QMouseEvent* event) override;
|
||||||
|
void mouseReleaseEvent(QMouseEvent* event) override;
|
||||||
|
bool eventFilter(QObject* watched, QEvent* event) override;
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void UpdateRoomData();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void UpdatePosition();
|
||||||
|
void ConnectToRoom();
|
||||||
|
void DisconnectFromRoom();
|
||||||
|
|
||||||
|
GMainWindow* main_window;
|
||||||
|
QTimer update_timer;
|
||||||
|
|
||||||
|
// UI Elements
|
||||||
|
QLabel* players_online_label;
|
||||||
|
ChatRoom* chat_room_widget;
|
||||||
|
QGridLayout* main_layout;
|
||||||
|
QSizeGrip* size_grip;
|
||||||
|
|
||||||
|
// Network and Data
|
||||||
|
MultiplayerState* multiplayer_state = nullptr;
|
||||||
|
std::shared_ptr<Network::RoomMember> room_member;
|
||||||
|
|
||||||
|
// Display settings
|
||||||
|
bool is_visible = false;
|
||||||
|
QColor background_color;
|
||||||
|
QColor border_color;
|
||||||
|
|
||||||
|
// Layout
|
||||||
|
int padding = 12;
|
||||||
|
int border_width = 1;
|
||||||
|
int corner_radius = 10;
|
||||||
|
|
||||||
|
// Drag functionality
|
||||||
|
bool is_dragging = false;
|
||||||
|
bool has_been_moved = false;
|
||||||
|
QPoint drag_start_pos;
|
||||||
|
QPoint widget_start_pos;
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user