diff --git a/src/citron/main.cpp b/src/citron/main.cpp index b96b1e109..f1ac094d8 100644 --- a/src/citron/main.cpp +++ b/src/citron/main.cpp @@ -1159,9 +1159,6 @@ void GMainWindow::InitializeWidgets() { 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->hide(); @@ -1353,6 +1350,25 @@ void GMainWindow::InitializeWidgets() { statusBar()->setVisible(true); setStyleSheet(QStringLiteral("QStatusBar::item{border: none;}")); + + const bool is_gamescope = !qgetenv("GAMESCOPE_WIDTH").isEmpty() || qgetenv("XDG_CURRENT_DESKTOP") == "gamescope"; + if (is_gamescope) { + statusBar()->setSizeGripEnabled(true); + + this->menuBar()->setNativeMenuBar(false); + + this->setContentsMargins(0, 0, 0, 0); + this->layout()->setContentsMargins(0, 0, 0, 0); + this->layout()->setSpacing(0); + + ui->horizontalLayout->setContentsMargins(0, 0, 0, 0); + ui->horizontalLayout->setSpacing(0); + + statusBar()->setStyleSheet(QStringLiteral( + "QStatusBar::item { border: none; }" + "QSizeGrip { width: 20px; height: 20px; margin: 2px; }" + )); + } } void GMainWindow::InitializeDebugWidgets() { @@ -1483,6 +1499,12 @@ void GMainWindow::InitializeHotkeys() { } void GMainWindow::SetDefaultUIGeometry() { + const bool is_gamescope = !qgetenv("GAMESCOPE_WIDTH").isEmpty() || qgetenv("XDG_CURRENT_DESKTOP") == "gamescope"; + + if (is_gamescope) { + this->resize(1280, 800); + return; + } // geometry: 53% of the window contents are in the upper screen half, 47% in the lower half const QRect screenRect = QGuiApplication::primaryScreen()->geometry(); @@ -1495,15 +1517,25 @@ void GMainWindow::SetDefaultUIGeometry() { } void GMainWindow::RestoreUIState() { + const bool is_gamescope = !qgetenv("GAMESCOPE_WIDTH").isEmpty() || qgetenv("XDG_CURRENT_DESKTOP") == "gamescope"; + setWindowFlags(windowFlags() & ~Qt::FramelessWindowHint); - restoreGeometry(UISettings::values.geometry); + + if (!is_gamescope) { + restoreGeometry(UISettings::values.geometry); + } + // Work-around because the games list isn't supposed to be full screen if (isFullScreen()) { showNormal(); } restoreState(UISettings::values.state); - render_window->setWindowFlags(render_window->windowFlags() & ~Qt::FramelessWindowHint); - render_window->restoreGeometry(UISettings::values.renderwindow_geometry); + + if (!is_gamescope) { + render_window->setWindowFlags(render_window->windowFlags() & ~Qt::FramelessWindowHint); + render_window->restoreGeometry(UISettings::values.renderwindow_geometry); + } + #if MICROPROFILE_ENABLED microProfileDialog->restoreGeometry(UISettings::values.microprofile_geometry); microProfileDialog->setVisible(UISettings::values.microprofile_visible.GetValue()); @@ -1528,13 +1560,12 @@ void GMainWindow::RestoreUIState() { ui->action_Show_Status_Bar->setChecked(UISettings::values.show_status_bar.GetValue()); statusBar()->setVisible(ui->action_Show_Status_Bar->isChecked()); - // Force the performance overlay to be off on startup + // Force overlays off on startup ui->action_Show_Performance_Overlay->setChecked(false); if (performance_overlay) { performance_overlay->SetVisible(false); } - // Force the VRAM overlay to be off on startup ui->action_Show_Vram_Overlay->setChecked(false); if (vram_overlay) { vram_overlay->SetVisible(false); @@ -4015,14 +4046,16 @@ void GMainWindow::ShowFullscreen() { } void GMainWindow::HideFullscreen() { + const bool is_gamescope = !qgetenv("GAMESCOPE_WIDTH").isEmpty() || qgetenv("XDG_CURRENT_DESKTOP") == "gamescope"; + if (ui->action_Single_Window_Mode->isChecked()) { if (UsingExclusiveFullscreen()) { showNormal(); - restoreGeometry(UISettings::values.geometry); + if (!is_gamescope) restoreGeometry(UISettings::values.geometry); } else { hide(); setWindowFlags(windowFlags() & ~Qt::FramelessWindowHint); - restoreGeometry(UISettings::values.geometry); + if (!is_gamescope) restoreGeometry(UISettings::values.geometry); raise(); show(); } @@ -4032,15 +4065,18 @@ void GMainWindow::HideFullscreen() { } else { if (UsingExclusiveFullscreen()) { render_window->showNormal(); - render_window->restoreGeometry(UISettings::values.renderwindow_geometry); + if (!is_gamescope) render_window->restoreGeometry(UISettings::values.renderwindow_geometry); } else { render_window->hide(); render_window->setWindowFlags(windowFlags() & ~Qt::FramelessWindowHint); - render_window->restoreGeometry(UISettings::values.renderwindow_geometry); + if (!is_gamescope) render_window->restoreGeometry(UISettings::values.renderwindow_geometry); render_window->raise(); render_window->show(); } } + + if (is_gamescope) { + } } void GMainWindow::ToggleWindowMode() { @@ -4069,9 +4105,14 @@ void GMainWindow::ToggleWindowMode() { } void GMainWindow::ResetWindowSize(u32 width, u32 height) { + const bool is_gamescope = !qgetenv("GAMESCOPE_WIDTH").isEmpty() || qgetenv("XDG_CURRENT_DESKTOP") == "gamescope"; + if (is_gamescope) { + return; + } + const auto aspect_ratio = Layout::EmulationAspectRatio( static_cast(Settings::values.aspect_ratio.GetValue()), - static_cast(height) / width); + static_cast(height) / width); if (!ui->action_Single_Window_Mode->isChecked()) { render_window->resize(height / aspect_ratio, height); } else { @@ -5564,10 +5605,14 @@ void GMainWindow::UpdateStatusButtons() { } void GMainWindow::UpdateUISettings() { - if (!ui->action_Fullscreen->isChecked()) { + const bool is_gamescope = !qgetenv("GAMESCOPE_WIDTH").isEmpty() || qgetenv("XDG_CURRENT_DESKTOP") == "gamescope"; + + // Only save/restore geometry if we are NOT in gamescope to prevent resolution bugs + if (!ui->action_Fullscreen->isChecked() && !is_gamescope) { UISettings::values.geometry = saveGeometry(); UISettings::values.renderwindow_geometry = render_window->saveGeometry(); } + UISettings::values.state = saveState(); #if MICROPROFILE_ENABLED UISettings::values.microprofile_geometry = microProfileDialog->saveGeometry(); @@ -6096,13 +6141,9 @@ static void SetHighDPIAttributes() { int main(int argc, char* argv[]) { // Set environment variables for AppImage compatibility - // This must be done before the QApplication is created. const bool is_appimage = !qgetenv("APPIMAGE").isEmpty(); if (is_appimage) { - // Fixes Wayland crash with NVIDIA drivers by disabling explicit sync. qputenv("QT_WAYLAND_DISABLE_EXPLICIT_SYNC", "1"); - - // Tell the bundled OpenSSL where to find the bundled certificates. const QDir app_dir(QCoreApplication::applicationDirPath()); const QString certs_path = app_dir.filePath(QString::fromLatin1("../etc/ssl/certs")); qputenv("SSL_CERT_DIR", certs_path.toUtf8()); @@ -6133,45 +6174,42 @@ int main(int argc, char* argv[]) { Common::ConfigureNvidiaEnvironmentFlags(); - // Init settings params QCoreApplication::setOrganizationName(QStringLiteral("citron team")); QCoreApplication::setApplicationName(QStringLiteral("citron")); #ifdef _WIN32 - // Increases the maximum open file limit to 8192 _setmaxstdio(8192); #endif #ifdef __APPLE__ - // If you start a bundle (binary) on OSX without the Terminal, the working directory is "/". - // But since we require the working directory to be the executable path for the location of - // the user folder in the Qt Frontend, we need to cd into that working directory const auto bin_path = Common::FS::GetBundleDirectory() / ".."; chdir(Common::FS::PathToUTF8String(bin_path).c_str()); #endif #ifdef __linux__ - // Set the DISPLAY variable in order to open web browsers - // TODO (lat9nq): Find a better solution for AppImages to start external applications if (QString::fromLocal8Bit(qgetenv("DISPLAY")).isEmpty()) { qputenv("DISPLAY", ":0"); } - - // Fix the Wayland appId. This needs to match the name of the .desktop file without the .desktop - // suffix. QGuiApplication::setDesktopFileName(QStringLiteral("org.citron_emu.citron")); #endif SetHighDPIAttributes(); #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) - // Disables the "?" button on all dialogs. Disabled by default on Qt6. QCoreApplication::setAttribute(Qt::AA_DisableWindowContextHelpButton); #endif - // Enables the core to make the qt created contexts current on std::threads QCoreApplication::setAttribute(Qt::AA_DontCheckOpenGLContextThreadAffinity); + const bool is_gamescope = !qgetenv("GAMESCOPE_WIDTH").isEmpty() || qgetenv("XDG_CURRENT_DESKTOP") == "gamescope"; + if (is_gamescope) { + qputenv("QT_ENABLE_HIGHDPI_SCALING", "0"); + qputenv("QT_SCALE_FACTOR", "1"); + qputenv("QT_AUTO_SCREEN_SCALE_FACTOR", "0"); + qputenv("QT_FONT_DPI", "96"); + QCoreApplication::setAttribute(Qt::AA_DontUseNativeMenuBar); + } + QApplication app(argc, argv); #ifdef __linux__ if (QGuiApplication::platformName().startsWith(QStringLiteral("wayland"))) { @@ -6179,7 +6217,7 @@ int main(int argc, char* argv[]) { } #endif - #ifdef CITRON_USE_AUTO_UPDATER +#ifdef CITRON_USE_AUTO_UPDATER // Check for and apply staged updates before starting the main application std::filesystem::path app_dir = std::filesystem::path(QCoreApplication::applicationDirPath().toStdString()); @@ -6211,6 +6249,7 @@ int main(int argc, char* argv[]) { OverrideWindowsFont(); #endif + // Workaround for QTBUG-85409, for Suzhou numerals the number 1 is actually \u3021 // so we can see if we get \u3008 instead // TL;DR all other number formats are consecutive in unicode code points @@ -6222,21 +6261,31 @@ int main(int argc, char* argv[]) { } #endif - // Qt changes the locale and causes issues in float conversion using std::to_string() when - // generating shaders setlocale(LC_ALL, "C"); GMainWindow main_window{std::move(config), has_broken_vulkan}; - app.setStyle(new RainbowStyle(app.style())); main_window.show(); + if (is_gamescope) { + QTimer::singleShot(200, &main_window, [&main_window]() { + main_window.showMaximized(); + + if (main_window.layout()) { + main_window.layout()->activate(); + } + + main_window.update(); + main_window.raise(); + main_window.activateWindow(); + }); + } + QObject::connect(&app, &QGuiApplication::applicationStateChanged, &main_window, &GMainWindow::OnAppFocusStateChanged); int result = app.exec(); - detached_tasks.WaitForAllTasks(); return result; }