summaryrefslogtreecommitdiff
path: root/src/yuzu/main.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/yuzu/main.cpp')
-rw-r--r--src/yuzu/main.cpp170
1 files changed, 148 insertions, 22 deletions
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index b460020b1..ef91ef19c 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -1,6 +1,5 @@
-// Copyright 2014 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: 2014 Citra Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <cinttypes>
#include <clocale>
@@ -9,6 +8,10 @@
#ifdef __APPLE__
#include <unistd.h> // for chdir
#endif
+#ifdef __linux__
+#include <csignal>
+#include <sys/socket.h>
+#endif
// VFS includes must be before glad as they will conflict with Windows file api, which uses defines.
#include "applets/qt_controller.h"
@@ -32,6 +35,7 @@
#include "core/hle/service/am/applet_ae.h"
#include "core/hle/service/am/applet_oe.h"
#include "core/hle/service/am/applets/applets.h"
+#include "yuzu/multiplayer/state.h"
#include "yuzu/util/controller_navigation.h"
// These are wrappers to avoid the calls to CreateDirectory and CreateFile because of the Windows
@@ -115,7 +119,6 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
#include "video_core/shader_notify.h"
#include "yuzu/about_dialog.h"
#include "yuzu/bootmanager.h"
-#include "yuzu/check_vulkan.h"
#include "yuzu/compatdb.h"
#include "yuzu/compatibility_list.h"
#include "yuzu/configuration/config.h"
@@ -131,7 +134,9 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
#include "yuzu/install_dialog.h"
#include "yuzu/loading_screen.h"
#include "yuzu/main.h"
+#include "yuzu/startup_checks.h"
#include "yuzu/uisettings.h"
+#include "yuzu/util/clickable_label.h"
using namespace Common::Literals;
@@ -252,12 +257,16 @@ static QString PrettyProductName() {
return QSysInfo::prettyProductName();
}
-GMainWindow::GMainWindow()
+GMainWindow::GMainWindow(bool has_broken_vulkan)
: ui{std::make_unique<Ui::MainWindow>()}, system{std::make_unique<Core::System>()},
input_subsystem{std::make_shared<InputCommon::InputSubsystem>()},
config{std::make_unique<Config>(*system)},
vfs{std::make_shared<FileSys::RealVfsFilesystem>()},
provider{std::make_unique<FileSys::ManualContentProvider>()} {
+#ifdef __linux__
+ SetupSigInterrupts();
+#endif
+
Common::Log::Initialize();
LoadTranslation();
@@ -271,6 +280,8 @@ GMainWindow::GMainWindow()
SetDiscordEnabled(UISettings::values.enable_discord_presence.GetValue());
discord_rpc->Update();
+ system->GetRoomNetwork().Init();
+
RegisterMetaTypes();
InitializeWidgets();
@@ -352,17 +363,15 @@ GMainWindow::GMainWindow()
MigrateConfigFiles();
- if (!CheckVulkan()) {
- config->Save();
+ if (has_broken_vulkan) {
+ UISettings::values.has_broken_vulkan = true;
+
+ QMessageBox::warning(this, tr("Broken Vulkan Installation Detected"),
+ tr("Vulkan initialization failed during boot.<br><br>Click <a "
+ "href='https://yuzu-emu.org/wiki/faq/"
+ "#yuzu-starts-with-the-error-broken-vulkan-installation-detected'>"
+ "here for instructions to fix the issue</a>."));
- QMessageBox::warning(
- this, tr("Broken Vulkan Installation Detected"),
- tr("Vulkan initialization failed on the previous boot.<br><br>Click <a "
- "href='https://yuzu-emu.org/wiki/faq/"
- "#yuzu-starts-with-the-error-broken-vulkan-installation-detected'>here for "
- "instructions to fix the issue</a>."));
- }
- if (UISettings::values.has_broken_vulkan) {
Settings::values.renderer_backend = Settings::RendererBackend::OpenGL;
renderer_status_button->setDisabled(true);
@@ -461,6 +470,13 @@ GMainWindow::~GMainWindow() {
if (render_window->parent() == nullptr) {
delete render_window;
}
+
+ system->GetRoomNetwork().Shutdown();
+
+#ifdef __linux__
+ ::close(sig_interrupt_fds[0]);
+ ::close(sig_interrupt_fds[1]);
+#endif
}
void GMainWindow::RegisterMetaTypes() {
@@ -824,6 +840,10 @@ void GMainWindow::InitializeWidgets() {
}
});
+ multiplayer_state = new MultiplayerState(this, game_list->GetModel(), ui->action_Leave_Room,
+ ui->action_Show_Room, system->GetRoomNetwork());
+ multiplayer_state->setVisible(false);
+
// Create status bar
message_label = new QLabel();
// Configured separately for left alignment
@@ -856,6 +876,10 @@ void GMainWindow::InitializeWidgets() {
statusBar()->addPermanentWidget(label);
}
+ // TODO (flTobi): Add the widget when multiplayer is fully implemented
+ // statusBar()->addPermanentWidget(multiplayer_state->GetStatusText(), 0);
+ // statusBar()->addPermanentWidget(multiplayer_state->GetStatusIcon(), 0);
+
tas_label = new QLabel();
tas_label->setObjectName(QStringLiteral("TASlabel"));
tas_label->setFocusPolicy(Qt::NoFocus);
@@ -1059,7 +1083,7 @@ void GMainWindow::InitializeHotkeys() {
Settings::values.volume.SetValue(static_cast<u8>(new_volume));
});
connect_shortcut(QStringLiteral("Toggle Framerate Limit"), [] {
- Settings::values.disable_fps_limit.SetValue(!Settings::values.disable_fps_limit.GetValue());
+ Settings::values.use_speed_limit.SetValue(!Settings::values.use_speed_limit.GetValue());
});
connect_shortcut(QStringLiteral("Toggle Mouse Panning"), [&] {
Settings::values.mouse_panning = !Settings::values.mouse_panning;
@@ -1131,6 +1155,7 @@ void GMainWindow::OnAppFocusStateChanged(Qt::ApplicationState state) {
OnPauseGame();
} else if (!emu_thread->IsRunning() && auto_paused && state == Qt::ApplicationActive) {
auto_paused = false;
+ RequestGameResume();
OnStartGame();
}
}
@@ -1164,6 +1189,8 @@ void GMainWindow::ConnectWidgetEvents() {
connect(game_list_placeholder, &GameListPlaceholder::AddDirectory, this,
&GMainWindow::OnGameListAddDirectory);
connect(game_list, &GameList::ShowList, this, &GMainWindow::OnGameListShowList);
+ connect(game_list, &GameList::PopulatingCompleted,
+ [this] { multiplayer_state->UpdateGameList(game_list->GetModel()); });
connect(game_list, &GameList::OpenPerGameGeneralRequested, this,
&GMainWindow::OnGameListOpenPerGameProperties);
@@ -1181,6 +1208,9 @@ void GMainWindow::ConnectWidgetEvents() {
connect(this, &GMainWindow::EmulationStopping, this, &GMainWindow::SoftwareKeyboardExit);
connect(&status_bar_update_timer, &QTimer::timeout, this, &GMainWindow::UpdateStatusBar);
+
+ connect(this, &GMainWindow::UpdateThemedIcons, multiplayer_state,
+ &MultiplayerState::UpdateThemedIcons);
}
void GMainWindow::ConnectMenuEvents() {
@@ -1224,6 +1254,18 @@ void GMainWindow::ConnectMenuEvents() {
ui->action_Reset_Window_Size_900,
ui->action_Reset_Window_Size_1080});
+ // Multiplayer
+ connect(ui->action_View_Lobby, &QAction::triggered, multiplayer_state,
+ &MultiplayerState::OnViewLobby);
+ connect(ui->action_Start_Room, &QAction::triggered, multiplayer_state,
+ &MultiplayerState::OnCreateRoom);
+ connect(ui->action_Leave_Room, &QAction::triggered, multiplayer_state,
+ &MultiplayerState::OnCloseRoom);
+ connect(ui->action_Connect_To_Room, &QAction::triggered, multiplayer_state,
+ &MultiplayerState::OnDirectConnectToRoom);
+ connect(ui->action_Show_Room, &QAction::triggered, multiplayer_state,
+ &MultiplayerState::OnOpenNetworkRoom);
+
// Tools
connect_menu(ui->action_Rederive, std::bind(&GMainWindow::OnReinitializeKeys, this,
ReinitializeKeyBehavior::Warning));
@@ -1324,6 +1366,52 @@ static void ReleaseWakeLockLinux(QDBusObjectPath lock) {
QString::fromLatin1("org.freedesktop.portal.Request"));
unlocker.call(QString::fromLatin1("Close"));
}
+
+std::array<int, 3> GMainWindow::sig_interrupt_fds{0, 0, 0};
+
+void GMainWindow::SetupSigInterrupts() {
+ if (sig_interrupt_fds[2] == 1) {
+ return;
+ }
+ socketpair(AF_UNIX, SOCK_STREAM, 0, sig_interrupt_fds.data());
+ sig_interrupt_fds[2] = 1;
+
+ struct sigaction sa;
+ sa.sa_handler = &GMainWindow::HandleSigInterrupt;
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = SA_RESETHAND;
+ sigaction(SIGINT, &sa, nullptr);
+ sigaction(SIGTERM, &sa, nullptr);
+
+ sig_interrupt_notifier = new QSocketNotifier(sig_interrupt_fds[1], QSocketNotifier::Read, this);
+ connect(sig_interrupt_notifier, &QSocketNotifier::activated, this,
+ &GMainWindow::OnSigInterruptNotifierActivated);
+ connect(this, &GMainWindow::SigInterrupt, this, &GMainWindow::close);
+}
+
+void GMainWindow::HandleSigInterrupt(int sig) {
+ if (sig == SIGINT) {
+ exit(1);
+ }
+
+ // Calling into Qt directly from a signal handler is not safe,
+ // so wake up a QSocketNotifier with this hacky write call instead.
+ char a = 1;
+ int ret = write(sig_interrupt_fds[0], &a, sizeof(a));
+ (void)ret;
+}
+
+void GMainWindow::OnSigInterruptNotifierActivated() {
+ sig_interrupt_notifier->setEnabled(false);
+
+ char a;
+ int ret = read(sig_interrupt_fds[1], &a, sizeof(a));
+ (void)ret;
+
+ sig_interrupt_notifier->setEnabled(true);
+
+ emit SigInterrupt();
+}
#endif // __linux__
void GMainWindow::PreventOSSleep() {
@@ -1483,9 +1571,6 @@ void GMainWindow::BootGame(const QString& filename, u64 program_id, std::size_t
Config per_game_config(*system, config_file_name, Config::ConfigType::PerGameConfig);
}
- // Disable fps limit toggle when booting a new title
- Settings::values.disable_fps_limit.SetValue(false);
-
// Save configurations
UpdateUISettings();
game_list->SaveInterfaceLayout();
@@ -1500,6 +1585,8 @@ void GMainWindow::BootGame(const QString& filename, u64 program_id, std::size_t
if (!LoadROM(filename, program_id, program_index))
return;
+ system->SetShuttingDown(false);
+
// Create and start the emulation thread
emu_thread = std::make_unique<EmuThread>(*system);
emit EmulationStarting(emu_thread.get());
@@ -1542,6 +1629,8 @@ void GMainWindow::BootGame(const QString& filename, u64 program_id, std::size_t
mouse_hide_timer.start();
}
+ render_window->InitializeCamera();
+
std::string title_name;
std::string title_version;
const auto res = system->GetGameName(title_name);
@@ -1590,6 +1679,7 @@ void GMainWindow::ShutdownGame() {
AllowOSSleep();
+ system->SetShuttingDown(true);
system->DetachDebugger();
discord_rpc->Pause();
emu_thread->RequestStop();
@@ -1622,6 +1712,7 @@ void GMainWindow::ShutdownGame() {
tas_label->clear();
input_subsystem->GetTas()->Stop();
OnTasStateChanged();
+ render_window->FinalizeCamera();
// Enable all controllers
system->HIDCore().SetSupportedStyleTag({Core::HID::NpadStyleSet::All});
@@ -2573,6 +2664,7 @@ void GMainWindow::OnPauseContinueGame() {
if (emu_thread->IsRunning()) {
OnPauseGame();
} else {
+ RequestGameResume();
OnStartGame();
}
}
@@ -2780,7 +2872,8 @@ void GMainWindow::OnConfigure() {
const bool old_discord_presence = UISettings::values.enable_discord_presence.GetValue();
Settings::SetConfiguringGlobal(true);
- ConfigureDialog configure_dialog(this, hotkey_registry, input_subsystem.get(), *system);
+ ConfigureDialog configure_dialog(this, hotkey_registry, input_subsystem.get(), *system,
+ !multiplayer_state->IsHostingPublicRoom());
connect(&configure_dialog, &ConfigureDialog::LanguageChanged, this,
&GMainWindow::OnLanguageChanged);
@@ -2837,6 +2930,11 @@ void GMainWindow::OnConfigure() {
if (UISettings::values.enable_discord_presence.GetValue() != old_discord_presence) {
SetDiscordEnabled(UISettings::values.enable_discord_presence.GetValue());
}
+
+ if (!multiplayer_state->IsHostingPublicRoom()) {
+ multiplayer_state->UpdateCredentials();
+ }
+
emit UpdateThemedIcons();
const auto reload = UISettings::values.is_game_list_reload_pending.exchange(false);
@@ -2860,6 +2958,12 @@ void GMainWindow::OnConfigure() {
mouse_hide_timer.start();
}
+ // Restart camera config
+ if (emulation_running) {
+ render_window->FinalizeCamera();
+ render_window->InitializeCamera();
+ }
+
if (!UISettings::values.has_broken_vulkan) {
renderer_status_button->setEnabled(!emulation_running);
}
@@ -3277,7 +3381,7 @@ void GMainWindow::UpdateStatusBar() {
} else {
emu_speed_label->setText(tr("Speed: %1%").arg(results.emulation_speed * 100.0, 0, 'f', 0));
}
- if (Settings::values.disable_fps_limit) {
+ if (!Settings::values.use_speed_limit) {
game_fps_label->setText(
tr("Game: %1 FPS (Unlocked)").arg(results.average_game_fps, 0, 'f', 0));
} else {
@@ -3651,6 +3755,7 @@ void GMainWindow::closeEvent(QCloseEvent* event) {
}
render_window->close();
+ multiplayer_state->Close();
QWidget::closeEvent(event);
}
@@ -3752,6 +3857,21 @@ void GMainWindow::RequestGameExit() {
}
}
+void GMainWindow::RequestGameResume() {
+ auto& sm{system->ServiceManager()};
+ auto applet_oe = sm.GetService<Service::AM::AppletOE>("appletOE");
+ auto applet_ae = sm.GetService<Service::AM::AppletAE>("appletAE");
+
+ if (applet_oe != nullptr) {
+ applet_oe->GetMessageQueue()->RequestResume();
+ return;
+ }
+
+ if (applet_ae != nullptr) {
+ applet_ae->GetMessageQueue()->RequestResume();
+ }
+}
+
void GMainWindow::filterBarSetChecked(bool state) {
ui->action_Show_Filter_Bar->setChecked(state);
emit(OnToggleFilterBar());
@@ -3832,6 +3952,7 @@ void GMainWindow::OnLanguageChanged(const QString& locale) {
UISettings::values.language = locale;
LoadTranslation();
ui->retranslateUi(this);
+ multiplayer_state->retranslateUi();
UpdateWindowTitle();
}
@@ -3853,6 +3974,11 @@ void GMainWindow::SetDiscordEnabled([[maybe_unused]] bool state) {
#endif
int main(int argc, char* argv[]) {
+ bool has_broken_vulkan = false;
+ if (StartupChecks(argv[0], &has_broken_vulkan)) {
+ return 0;
+ }
+
Common::DetachedTasks detached_tasks;
MicroProfileOnThreadCreate("Frontend");
SCOPE_EXIT({ MicroProfileShutdown(); });
@@ -3892,7 +4018,7 @@ int main(int argc, char* argv[]) {
// generating shaders
setlocale(LC_ALL, "C");
- GMainWindow main_window{};
+ GMainWindow main_window{has_broken_vulkan};
// After settings have been loaded by GMainWindow, apply the filter
main_window.show();