diff --git a/src/common/config.cpp b/src/common/config.cpp index 16d9e5724..5c6cc944c 100644 --- a/src/common/config.cpp +++ b/src/common/config.cpp @@ -107,6 +107,7 @@ static bool showBackgroundImage = true; static bool isFullscreen = false; static std::string fullscreenMode = "Windowed"; static bool isHDRAllowed = false; +static bool showLabelsUnderIcons = true; // Language u32 m_language = 1; // english @@ -176,6 +177,14 @@ bool getIsFullscreen() { return isFullscreen; } +bool getShowLabelsUnderIcons() { + return showLabelsUnderIcons; +} + +bool setShowLabelsUnderIcons() { + return false; +} + std::string getFullscreenMode() { return fullscreenMode; } @@ -427,6 +436,9 @@ void setVblankDiv(u32 value) { void setIsFullscreen(bool enable) { isFullscreen = enable; } +void setShowLabelsUnderIcons(bool enable) { + showLabelsUnderIcons = enable; +} void setFullscreenMode(std::string mode) { fullscreenMode = mode; diff --git a/src/common/config.h b/src/common/config.h index 1025e9956..3a0bf252c 100644 --- a/src/common/config.h +++ b/src/common/config.h @@ -26,6 +26,8 @@ bool GetLoadGameSizeEnabled(); std::filesystem::path GetSaveDataPath(); void setLoadGameSizeEnabled(bool enable); bool getIsFullscreen(); +bool getShowLabelsUnderIcons(); +bool setShowLabelsUnderIcons(); std::string getFullscreenMode(); bool isNeoModeConsole(); bool isDevKitConsole(); diff --git a/src/images/fullscreen_icon.png b/src/images/fullscreen_icon.png new file mode 100644 index 000000000..327a30df5 Binary files /dev/null and b/src/images/fullscreen_icon.png differ diff --git a/src/images/refresh_icon.png b/src/images/refresh_icon.png index 00fe69c20..1e549e101 100644 Binary files a/src/images/refresh_icon.png and b/src/images/refresh_icon.png differ diff --git a/src/qt_gui/main_window.cpp b/src/qt_gui/main_window.cpp index caed3c3c5..a26953752 100644 --- a/src/qt_gui/main_window.cpp +++ b/src/qt_gui/main_window.cpp @@ -1,6 +1,8 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include "SDL3/SDL_events.h" + #include #include #include @@ -132,41 +134,118 @@ void MainWindow::CreateActions() { m_theme_act_group->addAction(ui->setThemeOled); } -void MainWindow::AddUiWidgets() { - // add toolbar widgets +void MainWindow::PauseGame() { + SDL_Event event; + SDL_memset(&event, 0, sizeof(event)); + event.type = SDL_EVENT_TOGGLE_PAUSE; + SDL_PushEvent(&event); +} +void MainWindow::toggleLabelsUnderIcons() { + bool showLabels = ui->toggleLabelsAct->isChecked(); + Config::setShowLabelsUnderIcons(); + UpdateToolbarLabels(); +} + +void MainWindow::toggleFullscreen() { + SDL_Event event; + SDL_memset(&event, 0, sizeof(event)); + event.type = SDL_EVENT_TOGGLE_FULLSCREEN; + + SDL_PushEvent(&event); + + SDL_Event check_event; + while (SDL_PollEvent(&check_event)) { + SDL_PushEvent(&check_event); + } +} + +void MainWindow::AddUiWidgets() { + // Add toolbar widgets QApplication::setStyle("Fusion"); ui->toolBar->setObjectName("mw_toolbar"); - // Detect background color - QColor bgColor = palette().color(QPalette::Window); - QString textColor = (bgColor.lightness() > 128) ? "#000" : "#fff"; + ui->toolBar->clear(); - ui->playButton->setToolTip( - QString("Play").arg(textColor)); - ui->pauseButton->setToolTip( - QString("Pause").arg(textColor)); - ui->stopButton->setToolTip( - QString("Stop").arg(textColor)); - ui->settingsButton->setToolTip( - QString("Config").arg(textColor)); - ui->controllerButton->setToolTip( - QString("Pads").arg(textColor)); - ui->keyboardButton->setToolTip( - QString("KBM").arg(textColor)); - ui->refreshButton->setToolTip( - QString("RefreshList").arg(textColor)); + QWidget* toolbarContainer = new QWidget(this); + QHBoxLayout* mainLayout = new QHBoxLayout(toolbarContainer); + mainLayout->setContentsMargins(5, 5, 5, 5); + mainLayout->setSpacing(15); - ui->toolBar->addWidget(ui->playButton); - ui->toolBar->addWidget(ui->pauseButton); - ui->toolBar->addWidget(ui->stopButton); - ui->toolBar->addWidget(ui->settingsButton); - ui->toolBar->addWidget(ui->controllerButton); - ui->toolBar->addWidget(ui->keyboardButton); - ui->toolBar->addWidget(ui->refreshButton); + bool showLabels = ui->toggleLabelsAct->isChecked(); - ui->toolBar->addWidget(ui->sizeSliderContainer); - ui->toolBar->addWidget(ui->mw_searchbar); + auto createButtonWithLabel = [&](QPushButton* button, const QString& labelText) { + QWidget* container = new QWidget(this); + QVBoxLayout* layout = new QVBoxLayout(container); + layout->setAlignment(Qt::AlignHCenter | Qt::AlignBottom); + layout->setContentsMargins(0, 0, 0, 0); + + layout->addWidget(button); + + if (ui->toggleLabelsAct->isChecked()) { + QLabel* label = new QLabel(labelText, this); + label->setAlignment(Qt::AlignCenter); + layout->addWidget(label); + } else { + + button->setToolTip( + QString("%2") + .arg(palette().color(QPalette::Window).lightness() > 128 ? "#000" : "#000", + labelText)); + } + + container->setLayout(layout); + return container; + }; + + QWidget* buttonGroup = new QWidget(this); + QHBoxLayout* buttonLayout = new QHBoxLayout(buttonGroup); + + auto createLine = [this]() { + QFrame* line = new QFrame(this); + line->setFrameShape(QFrame::VLine); + line->setFrameShadow(QFrame::Sunken); + line->setFixedWidth(2); + return line; + }; + + buttonLayout->setContentsMargins(0, 0, 0, 0); + buttonLayout->setSpacing(15); + + buttonLayout->addWidget(createButtonWithLabel(ui->playButton, tr("Play"))); + buttonLayout->addWidget(createButtonWithLabel(ui->pauseButton, tr("Pause"))); + buttonLayout->addWidget(createButtonWithLabel(ui->stopButton, tr("Stop"))); + buttonLayout->addWidget(createLine()); + + buttonLayout->addWidget(createButtonWithLabel(ui->settingsButton, tr("Settings"))); + buttonLayout->addWidget(createButtonWithLabel(ui->fullscreenButton, tr("Full Screen"))); + buttonLayout->addWidget(createLine()); + + buttonLayout->addWidget(createButtonWithLabel(ui->controllerButton, tr("Controllers"))); + buttonLayout->addWidget(createButtonWithLabel(ui->keyboardButton, tr("Keyboard"))); + buttonLayout->addWidget(createButtonWithLabel(ui->refreshButton, tr("Refresh List"))); + buttonLayout->addWidget(createLine()); + + QWidget* searchSliderContainer = new QWidget(this); + QHBoxLayout* searchSliderLayout = new QHBoxLayout(searchSliderContainer); + searchSliderLayout->setContentsMargins(0, 0, 0, 0); + searchSliderLayout->setSpacing(10); + + searchSliderLayout->addWidget(ui->sizeSliderContainer); + + searchSliderLayout->addWidget(ui->mw_searchbar); + + searchSliderContainer->setLayout(searchSliderLayout); + + mainLayout->addWidget(buttonGroup); + mainLayout->addWidget(searchSliderContainer); + + toolbarContainer->setLayout(mainLayout); + ui->toolBar->addWidget(toolbarContainer); +} + +void MainWindow::UpdateToolbarLabels() { + AddUiWidgets(); } void MainWindow::CreateDockWindows() { @@ -271,6 +350,8 @@ void MainWindow::CreateConnects() { connect(ui->refreshButton, &QPushButton::clicked, this, &MainWindow::RefreshGameTable); connect(ui->showGameListAct, &QAction::triggered, this, &MainWindow::ShowGameList); connect(this, &MainWindow::ExtractionFinished, this, &MainWindow::RefreshGameTable); + connect(ui->toggleLabelsAct, &QAction::toggled, this, &MainWindow::toggleLabelsUnderIcons); + connect(ui->fullscreenButton, &QPushButton::clicked, this, &MainWindow::toggleFullscreen); connect(ui->sizeSlider, &QSlider::valueChanged, this, [this](int value) { if (isTableList) { @@ -294,6 +375,7 @@ void MainWindow::CreateConnects() { }); connect(ui->playButton, &QPushButton::clicked, this, &MainWindow::StartGame); + connect(ui->pauseButton, &QPushButton::clicked, this, &MainWindow::PauseGame); connect(m_game_grid_frame.get(), &QTableWidget::cellDoubleClicked, this, &MainWindow::StartGame); connect(m_game_list_frame.get(), &QTableWidget::cellDoubleClicked, this, @@ -1236,6 +1318,7 @@ void MainWindow::SetUiIcons(bool isWhite) { ui->stopButton->setIcon(RecolorIcon(ui->stopButton->icon(), isWhite)); ui->refreshButton->setIcon(RecolorIcon(ui->refreshButton->icon(), isWhite)); ui->settingsButton->setIcon(RecolorIcon(ui->settingsButton->icon(), isWhite)); + ui->fullscreenButton->setIcon(RecolorIcon(ui->fullscreenButton->icon(), isWhite)); ui->controllerButton->setIcon(RecolorIcon(ui->controllerButton->icon(), isWhite)); ui->keyboardButton->setIcon(RecolorIcon(ui->keyboardButton->icon(), isWhite)); ui->refreshGameListAct->setIcon(RecolorIcon(ui->refreshGameListAct->icon(), isWhite)); diff --git a/src/qt_gui/main_window.h b/src/qt_gui/main_window.h index 5ac56e44c..54127cb2d 100644 --- a/src/qt_gui/main_window.h +++ b/src/qt_gui/main_window.h @@ -38,6 +38,7 @@ public: void InstallDragDropPkg(std::filesystem::path file, int pkgNum, int nPkg); void InstallDirectory(); void StartGame(); + void PauseGame(); private Q_SLOTS: void ConfigureGuiFromSettings(); @@ -47,15 +48,19 @@ private Q_SLOTS: void RefreshGameTable(); void HandleResize(QResizeEvent* event); void OnLanguageChanged(const std::string& locale); + void toggleLabelsUnderIcons(); private: Ui_MainWindow* ui; void AddUiWidgets(); + void UpdateToolbarLabels(); void CreateActions(); + void toggleFullscreen(); void CreateRecentGameActions(); void CreateDockWindows(); void GetPhysicalDevices(); void LoadGameLists(); + #ifdef ENABLE_UPDATER void CheckUpdateMain(bool checkSave); #endif @@ -70,6 +75,7 @@ private: void PlayBackgroundMusic(); QIcon RecolorIcon(const QIcon& icon, bool isWhite); void StartEmulator(std::filesystem::path); + bool is_paused = false; bool isIconBlack = false; bool isTableList = true; bool isGameRunning = false; diff --git a/src/qt_gui/main_window_ui.h b/src/qt_gui/main_window_ui.h index 246c2afd6..bb16560ad 100644 --- a/src/qt_gui/main_window_ui.h +++ b/src/qt_gui/main_window_ui.h @@ -20,6 +20,7 @@ public: QAction* setIconSizeSmallAct; QAction* setIconSizeMediumAct; QAction* setIconSizeLargeAct; + QAction* toggleLabelsAct; QAction* setlistModeListAct; QAction* setlistModeGridAct; QAction* setlistElfAct; @@ -50,6 +51,7 @@ public: QPushButton* settingsButton; QPushButton* controllerButton; QPushButton* keyboardButton; + QPushButton* fullscreenButton; QWidget* sizeSliderContainer; QHBoxLayout* sizeSliderContainer_layout; @@ -105,6 +107,14 @@ public: refreshGameListAct = new QAction(MainWindow); refreshGameListAct->setObjectName("refreshGameListAct"); refreshGameListAct->setIcon(QIcon(":images/refresh_icon.png")); + + toggleLabelsAct = new QAction(MainWindow); + toggleLabelsAct->setObjectName("toggleLabelsAct"); + toggleLabelsAct->setText( + QCoreApplication::translate("MainWindow", "Show Labels Under Icons")); + toggleLabelsAct->setCheckable(true); + toggleLabelsAct->setChecked(Config::getShowLabelsUnderIcons()); + setIconSizeTinyAct = new QAction(MainWindow); setIconSizeTinyAct->setObjectName("setIconSizeTinyAct"); setIconSizeTinyAct->setCheckable(true); @@ -211,11 +221,15 @@ public: refreshButton = new QPushButton(centralWidget); refreshButton->setFlat(true); refreshButton->setIcon(QIcon(":images/refresh_icon.png")); - refreshButton->setIconSize(QSize(32, 32)); + refreshButton->setIconSize(QSize(38, 38)); + fullscreenButton = new QPushButton(centralWidget); + fullscreenButton->setFlat(true); + fullscreenButton->setIcon(QIcon(":images/fullscreen_icon.png")); + fullscreenButton->setIconSize(QSize(38, 38)); settingsButton = new QPushButton(centralWidget); settingsButton->setFlat(true); settingsButton->setIcon(QIcon(":images/settings_icon.png")); - settingsButton->setIconSize(QSize(44, 44)); + settingsButton->setIconSize(QSize(42, 42)); controllerButton = new QPushButton(centralWidget); controllerButton->setFlat(true); controllerButton->setIcon(QIcon(":images/controller_icon.png")); @@ -304,6 +318,7 @@ public: menuView->addAction(refreshGameListAct); menuView->addAction(menuGame_List_Mode->menuAction()); menuView->addAction(menuGame_List_Icons->menuAction()); + menuView->addAction(toggleLabelsAct); menuView->addAction(menuThemes->menuAction()); menuThemes->addAction(setThemeDark); menuThemes->addAction(setThemeLight); diff --git a/src/sdl_window.cpp b/src/sdl_window.cpp index 80d196147..5746d2d7f 100644 --- a/src/sdl_window.cpp +++ b/src/sdl_window.cpp @@ -11,6 +11,7 @@ #include "common/config.h" #include "common/elf_info.h" #include "common/version.h" +#include "core/debug_state.h" #include "core/libraries/kernel/time.h" #include "core/libraries/pad/pad.h" #include "imgui/renderer/imgui_core.h" @@ -254,9 +255,10 @@ static Uint32 SDLCALL PollController(void* userdata, SDL_TimerID timer_id, Uint3 return controller->Poll(); } -WindowSDL::WindowSDL(s32 width_, s32 height_, Input::GameController* controller_, +WindowSDL::WindowSDL(s32 width, s32 height, Input::GameController* controller, std::string_view window_title) - : width{width_}, height{height_}, controller{controller_} { + : width(width), height(height), controller(controller), is_open(true), is_shown(true), + is_paused(false) { if (!SDL_SetHint(SDL_HINT_APP_NAME, "shadPS4")) { UNREACHABLE_MSG("Failed to set SDL window hint: {}", SDL_GetError()); } @@ -333,7 +335,6 @@ WindowSDL::WindowSDL(s32 width_, s32 height_, Input::GameController* controller_ WindowSDL::~WindowSDL() = default; void WindowSDL::WaitEvent() { - // Called on main thread SDL_Event event; if (!SDL_WaitEvent(&event)) { @@ -343,7 +344,7 @@ void WindowSDL::WaitEvent() { if (ImGui::Core::ProcessEvent(&event)) { return; } - + bool is_game_running = true; switch (event.type) { case SDL_EVENT_WINDOW_RESIZED: case SDL_EVENT_WINDOW_MAXIMIZED: @@ -379,8 +380,6 @@ void WindowSDL::WaitEvent() { case SDL_EVENT_GAMEPAD_AXIS_MOTION: OnGamepadEvent(&event); break; - // i really would have appreciated ANY KIND OF DOCUMENTATION ON THIS - // AND IT DOESN'T EVEN USE PROPER ENUMS case SDL_EVENT_GAMEPAD_SENSOR_UPDATE: switch ((SDL_SensorType)event.gsensor.sensor) { case SDL_SENSOR_GYRO: @@ -396,7 +395,24 @@ void WindowSDL::WaitEvent() { case SDL_EVENT_QUIT: is_open = false; break; - default: + case SDL_EVENT_TOGGLE_FULLSCREEN: { + if (SDL_GetWindowFlags(window) & SDL_WINDOW_FULLSCREEN) { + SDL_SetWindowFullscreen(window, 0); + } else { + SDL_SetWindowFullscreen(window, SDL_WINDOW_FULLSCREEN); + } + break; + } + case SDL_EVENT_TOGGLE_PAUSE: + SDL_Log("Received SDL_EVENT_TOGGLE_PAUSE"); + is_paused = !is_paused; + if (is_paused) { + SDL_Log("Game Paused"); + DebugState.PauseGuestThreads(); + } else { + SDL_Log("Game Resumed"); + DebugState.ResumeGuestThreads(); + } break; } } diff --git a/src/sdl_window.h b/src/sdl_window.h index 03ba0797b..2b1505767 100644 --- a/src/sdl_window.h +++ b/src/sdl_window.h @@ -7,6 +7,8 @@ #include "core/libraries/pad/pad.h" #include "input/controller.h" #include "string" +#define SDL_EVENT_TOGGLE_FULLSCREEN (SDL_EVENT_USER + 1) +#define SDL_EVENT_TOGGLE_PAUSE (SDL_EVENT_USER + 2) struct SDL_Window; struct SDL_Gamepad; @@ -106,6 +108,7 @@ private: SDL_Window* window{}; bool is_shown{}; bool is_open{true}; + bool is_paused = false; }; } // namespace Frontend diff --git a/src/shadps4.qrc b/src/shadps4.qrc index 340756f5c..c37e7d5de 100644 --- a/src/shadps4.qrc +++ b/src/shadps4.qrc @@ -34,5 +34,6 @@ images/ps4_controller.png images/keyboard_icon.png images/KBM.png + images/fullscreen_icon.png