summaryrefslogtreecommitdiff
path: root/src/yuzu
diff options
context:
space:
mode:
Diffstat (limited to 'src/yuzu')
-rw-r--r--src/yuzu/applets/qt_controller.cpp1
-rw-r--r--src/yuzu/configuration/config.cpp3
-rw-r--r--src/yuzu/configuration/config.h2
-rw-r--r--src/yuzu/configuration/configure_audio.cpp10
-rw-r--r--src/yuzu/configuration/configure_camera.h2
-rw-r--r--src/yuzu/configuration/configure_input.cpp2
-rw-r--r--src/yuzu/configuration/configure_input.h2
-rw-r--r--src/yuzu/configuration/configure_input_advanced.cpp8
-rw-r--r--src/yuzu/configuration/configure_input_advanced.h8
-rw-r--r--src/yuzu/configuration/configure_input_player.cpp36
-rw-r--r--src/yuzu/configuration/configure_input_player.h2
-rw-r--r--src/yuzu/configuration/configure_input_player.ui389
-rw-r--r--src/yuzu/configuration/configure_input_player_widget.cpp45
-rw-r--r--src/yuzu/configuration/configure_per_game.h2
-rw-r--r--src/yuzu/configuration/configure_profile_manager.cpp6
-rw-r--r--src/yuzu/configuration/configure_ringcon.h2
-rw-r--r--src/yuzu/configuration/configure_tas.h2
-rw-r--r--src/yuzu/configuration/configure_touchscreen_advanced.h2
-rw-r--r--src/yuzu/game_list.cpp6
-rw-r--r--src/yuzu/main.cpp496
-rw-r--r--src/yuzu/main.h22
-rw-r--r--src/yuzu/main.ui22
-rw-r--r--src/yuzu/uisettings.h10
-rw-r--r--src/yuzu/util/util.cpp15
-rw-r--r--src/yuzu/util/util.h3
-rw-r--r--src/yuzu/vk_device_info.cpp1
26 files changed, 690 insertions, 409 deletions
diff --git a/src/yuzu/applets/qt_controller.cpp b/src/yuzu/applets/qt_controller.cpp
index 515cb7ce6..9e5319716 100644
--- a/src/yuzu/applets/qt_controller.cpp
+++ b/src/yuzu/applets/qt_controller.cpp
@@ -13,7 +13,6 @@
#include "core/hid/hid_core.h"
#include "core/hid/hid_types.h"
#include "core/hle/service/hid/controllers/npad.h"
-#include "core/hle/service/hid/hid.h"
#include "core/hle/service/sm/sm.h"
#include "ui_qt_controller.h"
#include "yuzu/applets/qt_controller.h"
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index baa3e55f3..c0ae6468b 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -35,6 +35,7 @@ const std::array<int, Settings::NativeButton::NumButtons> Config::default_button
Qt::Key_G, Qt::Key_Q, Qt::Key_E, Qt::Key_R, Qt::Key_T,
Qt::Key_M, Qt::Key_N, Qt::Key_Left, Qt::Key_Up, Qt::Key_Right,
Qt::Key_Down, Qt::Key_Q, Qt::Key_E, 0, 0,
+ Qt::Key_Q, Qt::Key_E,
};
const std::array<int, Settings::NativeMotion::NumMotions> Config::default_motions = {
@@ -360,6 +361,7 @@ void Config::ReadAudioValues() {
qt_config->beginGroup(QStringLiteral("Audio"));
ReadCategory(Settings::Category::Audio);
+ ReadCategory(Settings::Category::UiAudio);
qt_config->endGroup();
}
@@ -900,6 +902,7 @@ void Config::SaveAudioValues() {
qt_config->beginGroup(QStringLiteral("Audio"));
WriteCategory(Settings::Category::Audio);
+ WriteCategory(Settings::Category::UiAudio);
qt_config->endGroup();
}
diff --git a/src/yuzu/configuration/config.h b/src/yuzu/configuration/config.h
index 74ec4f771..1589ba057 100644
--- a/src/yuzu/configuration/config.h
+++ b/src/yuzu/configuration/config.h
@@ -1,4 +1,4 @@
-// SPDX-FileCopyrightText: 2014 Citra Emulator Project
+// SPDX-FileCopyrightText: 2014 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/yuzu/configuration/configure_audio.cpp b/src/yuzu/configuration/configure_audio.cpp
index 81dd51ad3..9b6ef47a7 100644
--- a/src/yuzu/configuration/configure_audio.cpp
+++ b/src/yuzu/configuration/configure_audio.cpp
@@ -38,17 +38,21 @@ void ConfigureAudio::Setup(const ConfigurationShared::Builder& builder) {
std::map<u32, QWidget*> hold;
- auto push = [&](Settings::Category category) {
+ auto push_settings = [&](Settings::Category category) {
for (auto* setting : Settings::values.linkage.by_category[category]) {
settings.push_back(setting);
}
+ };
+
+ auto push_ui_settings = [&](Settings::Category category) {
for (auto* setting : UISettings::values.linkage.by_category[category]) {
settings.push_back(setting);
}
};
- push(Settings::Category::Audio);
- push(Settings::Category::SystemAudio);
+ push_settings(Settings::Category::Audio);
+ push_settings(Settings::Category::SystemAudio);
+ push_ui_settings(Settings::Category::UiAudio);
for (auto* setting : settings) {
auto* widget = builder.BuildWidget(setting, apply_funcs);
diff --git a/src/yuzu/configuration/configure_camera.h b/src/yuzu/configuration/configure_camera.h
index 9a90512b3..3d822da7b 100644
--- a/src/yuzu/configuration/configure_camera.h
+++ b/src/yuzu/configuration/configure_camera.h
@@ -1,4 +1,4 @@
-// Text : Copyright 2022 yuzu Emulator Project
+// Text : Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
diff --git a/src/yuzu/configuration/configure_input.cpp b/src/yuzu/configuration/configure_input.cpp
index 3dcad2701..02e23cce6 100644
--- a/src/yuzu/configuration/configure_input.cpp
+++ b/src/yuzu/configuration/configure_input.cpp
@@ -152,7 +152,7 @@ void ConfigureInput::Initialize(InputCommon::InputSubsystem* input_subsystem,
connect(player_controllers[0], &ConfigureInputPlayer::HandheldStateChanged,
[this](bool is_handheld) { UpdateDockedState(is_handheld); });
- advanced = new ConfigureInputAdvanced(this);
+ advanced = new ConfigureInputAdvanced(hid_core, this);
ui->tabAdvanced->setLayout(new QHBoxLayout(ui->tabAdvanced));
ui->tabAdvanced->layout()->addWidget(advanced);
diff --git a/src/yuzu/configuration/configure_input.h b/src/yuzu/configuration/configure_input.h
index 136cd3a0a..beb503dae 100644
--- a/src/yuzu/configuration/configure_input.h
+++ b/src/yuzu/configuration/configure_input.h
@@ -1,4 +1,4 @@
-// SPDX-FileCopyrightText: 2016 Citra Emulator Project
+// SPDX-FileCopyrightText: 2016 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/yuzu/configuration/configure_input_advanced.cpp b/src/yuzu/configuration/configure_input_advanced.cpp
index 3cfd5d439..441cea3f6 100644
--- a/src/yuzu/configuration/configure_input_advanced.cpp
+++ b/src/yuzu/configuration/configure_input_advanced.cpp
@@ -4,11 +4,13 @@
#include <QColorDialog>
#include "common/settings.h"
#include "core/core.h"
+#include "core/hid/emulated_controller.h"
+#include "core/hid/hid_core.h"
#include "ui_configure_input_advanced.h"
#include "yuzu/configuration/configure_input_advanced.h"
-ConfigureInputAdvanced::ConfigureInputAdvanced(QWidget* parent)
- : QWidget(parent), ui(std::make_unique<Ui::ConfigureInputAdvanced>()) {
+ConfigureInputAdvanced::ConfigureInputAdvanced(Core::HID::HIDCore& hid_core_, QWidget* parent)
+ : QWidget(parent), ui(std::make_unique<Ui::ConfigureInputAdvanced>()), hid_core{hid_core_} {
ui->setupUi(this);
controllers_color_buttons = {{
@@ -123,6 +125,8 @@ void ConfigureInputAdvanced::ApplyConfiguration() {
player.button_color_left = colors[1];
player.body_color_right = colors[2];
player.button_color_right = colors[3];
+
+ hid_core.GetEmulatedControllerByIndex(player_idx)->ReloadColorsFromSettings();
}
Settings::values.debug_pad_enabled = ui->debug_enabled->isChecked();
diff --git a/src/yuzu/configuration/configure_input_advanced.h b/src/yuzu/configuration/configure_input_advanced.h
index fc1230284..41f822c4a 100644
--- a/src/yuzu/configuration/configure_input_advanced.h
+++ b/src/yuzu/configuration/configure_input_advanced.h
@@ -14,11 +14,15 @@ namespace Ui {
class ConfigureInputAdvanced;
}
+namespace Core::HID {
+class HIDCore;
+} // namespace Core::HID
+
class ConfigureInputAdvanced : public QWidget {
Q_OBJECT
public:
- explicit ConfigureInputAdvanced(QWidget* parent = nullptr);
+ explicit ConfigureInputAdvanced(Core::HID::HIDCore& hid_core_, QWidget* parent = nullptr);
~ConfigureInputAdvanced() override;
void ApplyConfiguration();
@@ -44,4 +48,6 @@ private:
std::array<std::array<QColor, 4>, 8> controllers_colors;
std::array<std::array<QPushButton*, 4>, 8> controllers_color_buttons;
+
+ Core::HID::HIDCore& hid_core;
};
diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp
index 576f5b571..9259e2a5d 100644
--- a/src/yuzu/configuration/configure_input_player.cpp
+++ b/src/yuzu/configuration/configure_input_player.cpp
@@ -322,11 +322,12 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
setFocusPolicy(Qt::ClickFocus);
button_map = {
- ui->buttonA, ui->buttonB, ui->buttonX, ui->buttonY,
- ui->buttonLStick, ui->buttonRStick, ui->buttonL, ui->buttonR,
- ui->buttonZL, ui->buttonZR, ui->buttonPlus, ui->buttonMinus,
- ui->buttonDpadLeft, ui->buttonDpadUp, ui->buttonDpadRight, ui->buttonDpadDown,
- ui->buttonSL, ui->buttonSR, ui->buttonHome, ui->buttonScreenshot,
+ ui->buttonA, ui->buttonB, ui->buttonX, ui->buttonY,
+ ui->buttonLStick, ui->buttonRStick, ui->buttonL, ui->buttonR,
+ ui->buttonZL, ui->buttonZR, ui->buttonPlus, ui->buttonMinus,
+ ui->buttonDpadLeft, ui->buttonDpadUp, ui->buttonDpadRight, ui->buttonDpadDown,
+ ui->buttonSLLeft, ui->buttonSRLeft, ui->buttonHome, ui->buttonScreenshot,
+ ui->buttonSLRight, ui->buttonSRRight,
};
analog_map_buttons = {{
@@ -1181,10 +1182,13 @@ void ConfigureInputPlayer::UpdateControllerAvailableButtons() {
// List of all the widgets that will be hidden by any of the following layouts that need
// "unhidden" after the controller type changes
- const std::array<QWidget*, 11> layout_show = {
- ui->buttonShoulderButtonsSLSR,
+ const std::array<QWidget*, 14> layout_show = {
+ ui->buttonShoulderButtonsSLSRLeft,
+ ui->buttonShoulderButtonsSLSRRight,
ui->horizontalSpacerShoulderButtonsWidget,
ui->horizontalSpacerShoulderButtonsWidget2,
+ ui->horizontalSpacerShoulderButtonsWidget3,
+ ui->horizontalSpacerShoulderButtonsWidget4,
ui->buttonShoulderButtonsLeft,
ui->buttonMiscButtonsMinusScreenshot,
ui->bottomLeft,
@@ -1202,16 +1206,19 @@ void ConfigureInputPlayer::UpdateControllerAvailableButtons() {
std::vector<QWidget*> layout_hidden;
switch (layout) {
case Core::HID::NpadStyleIndex::ProController:
- case Core::HID::NpadStyleIndex::JoyconDual:
case Core::HID::NpadStyleIndex::Handheld:
layout_hidden = {
- ui->buttonShoulderButtonsSLSR,
+ ui->buttonShoulderButtonsSLSRLeft,
+ ui->buttonShoulderButtonsSLSRRight,
ui->horizontalSpacerShoulderButtonsWidget2,
+ ui->horizontalSpacerShoulderButtonsWidget4,
};
break;
case Core::HID::NpadStyleIndex::JoyconLeft:
layout_hidden = {
+ ui->buttonShoulderButtonsSLSRRight,
ui->horizontalSpacerShoulderButtonsWidget2,
+ ui->horizontalSpacerShoulderButtonsWidget3,
ui->buttonShoulderButtonsRight,
ui->buttonMiscButtonsPlusHome,
ui->bottomRight,
@@ -1219,16 +1226,17 @@ void ConfigureInputPlayer::UpdateControllerAvailableButtons() {
break;
case Core::HID::NpadStyleIndex::JoyconRight:
layout_hidden = {
- ui->horizontalSpacerShoulderButtonsWidget,
- ui->buttonShoulderButtonsLeft,
- ui->buttonMiscButtonsMinusScreenshot,
- ui->bottomLeft,
+ ui->buttonShoulderButtonsSLSRLeft, ui->horizontalSpacerShoulderButtonsWidget,
+ ui->horizontalSpacerShoulderButtonsWidget4, ui->buttonShoulderButtonsLeft,
+ ui->buttonMiscButtonsMinusScreenshot, ui->bottomLeft,
};
break;
case Core::HID::NpadStyleIndex::GameCube:
layout_hidden = {
- ui->buttonShoulderButtonsSLSR,
+ ui->buttonShoulderButtonsSLSRLeft,
+ ui->buttonShoulderButtonsSLSRRight,
ui->horizontalSpacerShoulderButtonsWidget2,
+ ui->horizontalSpacerShoulderButtonsWidget4,
ui->buttonMiscButtonsMinusGroup,
ui->buttonMiscButtonsScreenshotGroup,
};
diff --git a/src/yuzu/configuration/configure_input_player.h b/src/yuzu/configuration/configure_input_player.h
index d3255d2b4..fda09e925 100644
--- a/src/yuzu/configuration/configure_input_player.h
+++ b/src/yuzu/configuration/configure_input_player.h
@@ -1,4 +1,4 @@
-// SPDX-FileCopyrightText: 2016 Citra Emulator Project
+// SPDX-FileCopyrightText: 2016 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/yuzu/configuration/configure_input_player.ui b/src/yuzu/configuration/configure_input_player.ui
index 611a79477..5518cccd1 100644
--- a/src/yuzu/configuration/configure_input_player.ui
+++ b/src/yuzu/configuration/configure_input_player.ui
@@ -1208,6 +1208,159 @@
<property name="spacing">
<number>3</number>
</property>
+ <item>
+ <widget class="QWidget" name="buttonShoulderButtonsSLSRLeft" native="true">
+ <layout class="QVBoxLayout" name="buttonShoulderButtonsSLSRLeftVerticalLayout">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item alignment="Qt::AlignHCenter">
+ <widget class="QGroupBox" name="buttonShoulderButtonsSLLeftGroup">
+ <property name="title">
+ <string>SL</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <layout class="QVBoxLayout" name="buttonShoulderButtonsSLLeftVerticalLayout">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="buttonSLLeft">
+ <property name="minimumSize">
+ <size>
+ <width>68</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>68</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 68px;</string>
+ </property>
+ <property name="text">
+ <string>SL</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item alignment="Qt::AlignHCenter">
+ <widget class="QGroupBox" name="buttonShoulderButtonsSRLeftGroup">
+ <property name="title">
+ <string>SR</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <layout class="QVBoxLayout" name="buttonShoulderButtonsSRLeftVerticalLayout">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="buttonSRLeft">
+ <property name="minimumSize">
+ <size>
+ <width>68</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>68</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 68px;</string>
+ </property>
+ <property name="text">
+ <string>SR</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QWidget" name="horizontalSpacerShoulderButtonsWidget4" native="true">
+ <layout class="QHBoxLayout" name="horizontalSpacerShoulderButtonsWidget4Layout">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <spacer name="horizontalSpacerShoulderButtons5">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>0</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ </item>
<item>
<widget class="QWidget" name="buttonShoulderButtonsLeft" native="true">
<layout class="QVBoxLayout" name="buttonShoulderButtonsLeftVerticalLayout">
@@ -1830,125 +1983,125 @@
</layout>
</widget>
</item>
- <item>
- <widget class="QWidget" name="buttonShoulderButtonsSLSR" native="true">
- <layout class="QVBoxLayout" name="buttonShoulderButtonsSLSRVerticalLayout">
- <property name="spacing">
- <number>0</number>
- </property>
- <property name="leftMargin">
- <number>0</number>
- </property>
- <property name="topMargin">
- <number>0</number>
- </property>
- <property name="rightMargin">
- <number>0</number>
- </property>
- <property name="bottomMargin">
- <number>0</number>
- </property>
- <item alignment="Qt::AlignHCenter">
- <widget class="QGroupBox" name="buttonShoulderButtonsSLGroup">
- <property name="title">
- <string>SL</string>
- </property>
- <property name="alignment">
- <set>Qt::AlignCenter</set>
- </property>
- <layout class="QVBoxLayout" name="buttonShoulderButtonsSLVerticalLayout">
- <property name="spacing">
- <number>3</number>
- </property>
- <property name="leftMargin">
- <number>3</number>
- </property>
- <property name="topMargin">
- <number>3</number>
- </property>
- <property name="rightMargin">
- <number>3</number>
- </property>
- <property name="bottomMargin">
- <number>3</number>
- </property>
- <item>
- <widget class="QPushButton" name="buttonSL">
- <property name="minimumSize">
- <size>
- <width>68</width>
- <height>0</height>
- </size>
- </property>
- <property name="maximumSize">
- <size>
- <width>68</width>
- <height>16777215</height>
- </size>
- </property>
- <property name="styleSheet">
- <string notr="true">min-width: 68px;</string>
- </property>
- <property name="text">
- <string>SL</string>
- </property>
- </widget>
- </item>
- </layout>
- </widget>
- </item>
- <item alignment="Qt::AlignHCenter">
- <widget class="QGroupBox" name="buttonShoulderButtonsSRGroup">
- <property name="title">
- <string>SR</string>
- </property>
- <property name="alignment">
- <set>Qt::AlignCenter</set>
- </property>
- <layout class="QVBoxLayout" name="buttonShoulderButtonsSRVerticalLayout">
- <property name="spacing">
- <number>3</number>
- </property>
- <property name="leftMargin">
- <number>3</number>
- </property>
- <property name="topMargin">
- <number>3</number>
- </property>
- <property name="rightMargin">
- <number>3</number>
- </property>
- <property name="bottomMargin">
- <number>3</number>
- </property>
- <item>
- <widget class="QPushButton" name="buttonSR">
- <property name="minimumSize">
- <size>
- <width>68</width>
- <height>0</height>
- </size>
- </property>
- <property name="maximumSize">
- <size>
- <width>68</width>
- <height>16777215</height>
- </size>
- </property>
- <property name="styleSheet">
- <string notr="true">min-width: 68px;</string>
- </property>
- <property name="text">
- <string>SR</string>
- </property>
- </widget>
- </item>
+ <item>
+ <widget class="QWidget" name="buttonShoulderButtonsSLSRRight" native="true">
+ <layout class="QVBoxLayout" name="buttonShoulderButtonsSLSRRightVerticalLayout">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item alignment="Qt::AlignHCenter">
+ <widget class="QGroupBox" name="buttonShoulderButtonsSLRightGroup">
+ <property name="title">
+ <string>SL</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <layout class="QVBoxLayout" name="buttonShoulderButtonsSLRightVerticalLayout">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="buttonSLRight">
+ <property name="minimumSize">
+ <size>
+ <width>68</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>68</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 68px;</string>
+ </property>
+ <property name="text">
+ <string>SL</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item alignment="Qt::AlignHCenter">
+ <widget class="QGroupBox" name="buttonShoulderButtonsSRRightGroup">
+ <property name="title">
+ <string>SR</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <layout class="QVBoxLayout" name="buttonShoulderButtonsSRRightVerticalLayout">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="buttonSRRight">
+ <property name="minimumSize">
+ <size>
+ <width>68</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>68</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 68px;</string>
+ </property>
+ <property name="text">
+ <string>SR</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
</layout>
- </widget>
- </item>
- </layout>
- </widget>
- </item>
+ </widget>
+ </item>
</layout>
</item>
<item>
diff --git a/src/yuzu/configuration/configure_input_player_widget.cpp b/src/yuzu/configuration/configure_input_player_widget.cpp
index a188eef92..550cff9a0 100644
--- a/src/yuzu/configuration/configure_input_player_widget.cpp
+++ b/src/yuzu/configuration/configure_input_player_widget.cpp
@@ -297,8 +297,8 @@ void PlayerControlPreview::DrawLeftController(QPainter& p, const QPointF center)
// Sideview SL and SR buttons
button_color = colors.slider_button;
- DrawRoundButton(p, center + QPoint(59, 52), button_values[SR], 5, 12, Direction::Left);
- DrawRoundButton(p, center + QPoint(59, -69), button_values[SL], 5, 12, Direction::Left);
+ DrawRoundButton(p, center + QPoint(59, 52), button_values[SRLeft], 5, 12, Direction::Left);
+ DrawRoundButton(p, center + QPoint(59, -69), button_values[SLLeft], 5, 12, Direction::Left);
DrawLeftBody(p, center);
@@ -353,8 +353,10 @@ void PlayerControlPreview::DrawLeftController(QPainter& p, const QPointF center)
// SR and SL buttons
p.setPen(colors.outline);
button_color = colors.slider_button;
- DrawRoundButton(p, center + QPoint(155, 52), button_values[SR], 5.2f, 12, Direction::None, 4);
- DrawRoundButton(p, center + QPoint(155, -69), button_values[SL], 5.2f, 12, Direction::None, 4);
+ DrawRoundButton(p, center + QPoint(155, 52), button_values[SRLeft], 5.2f, 12, Direction::None,
+ 4);
+ DrawRoundButton(p, center + QPoint(155, -69), button_values[SLLeft], 5.2f, 12, Direction::None,
+ 4);
// SR and SL text
p.setPen(colors.transparent);
@@ -428,8 +430,10 @@ void PlayerControlPreview::DrawRightController(QPainter& p, const QPointF center
// Sideview SL and SR buttons
button_color = colors.slider_button;
- DrawRoundButton(p, center + QPoint(-59, 52), button_values[SL], 5, 11, Direction::Right);
- DrawRoundButton(p, center + QPoint(-59, -69), button_values[SR], 5, 11, Direction::Right);
+ DrawRoundButton(p, center + QPoint(-59, 52), button_values[SLRight], 5, 11,
+ Direction::Right);
+ DrawRoundButton(p, center + QPoint(-59, -69), button_values[SRRight], 5, 11,
+ Direction::Right);
DrawRightBody(p, center);
@@ -484,8 +488,10 @@ void PlayerControlPreview::DrawRightController(QPainter& p, const QPointF center
// SR and SL buttons
p.setPen(colors.outline);
button_color = colors.slider_button;
- DrawRoundButton(p, center + QPoint(-155, 52), button_values[SL], 5, 12, Direction::None, 4.0f);
- DrawRoundButton(p, center + QPoint(-155, -69), button_values[SR], 5, 12, Direction::None, 4.0f);
+ DrawRoundButton(p, center + QPoint(-155, 52), button_values[SLRight], 5, 12, Direction::None,
+ 4.0f);
+ DrawRoundButton(p, center + QPoint(-155, -69), button_values[SRRight], 5, 12, Direction::None,
+ 4.0f);
// SR and SL text
p.setPen(colors.transparent);
@@ -557,6 +563,19 @@ void PlayerControlPreview::DrawDualController(QPainter& p, const QPointF center)
DrawRoundButton(p, center + QPoint(-154, -72), button_values[Minus], 7, 4, Direction::Up,
1);
+ // Left SR and SL sideview buttons
+ button_color = colors.slider_button;
+ DrawRoundButton(p, center + QPoint(-20, -62), button_values[SLLeft], 4, 11,
+ Direction::Left);
+ DrawRoundButton(p, center + QPoint(-20, 47), button_values[SRLeft], 4, 11, Direction::Left);
+
+ // Right SR and SL sideview buttons
+ button_color = colors.slider_button;
+ DrawRoundButton(p, center + QPoint(20, 47), button_values[SLRight], 4, 11,
+ Direction::Right);
+ DrawRoundButton(p, center + QPoint(20, -62), button_values[SRRight], 4, 11,
+ Direction::Right);
+
DrawDualBody(p, center);
// Right trigger top view
@@ -1792,16 +1811,6 @@ void PlayerControlPreview::DrawDualBody(QPainter& p, const QPointF center) {
p.setBrush(colors.right);
DrawPolygon(p, qright_joycon_topview);
- // Right SR and SL sideview buttons
- p.setPen(colors.outline);
- p.setBrush(colors.slider_button);
- DrawRoundRectangle(p, center + QPoint(19, 47), 7, 22, 1);
- DrawRoundRectangle(p, center + QPoint(19, -62), 7, 22, 1);
-
- // Left SR and SL sideview buttons
- DrawRoundRectangle(p, center + QPoint(-19, 47), 7, 22, 1);
- DrawRoundRectangle(p, center + QPoint(-19, -62), 7, 22, 1);
-
// Right Sideview body
p.setBrush(colors.slider);
DrawPolygon(p, qright_joycon_slider);
diff --git a/src/yuzu/configuration/configure_per_game.h b/src/yuzu/configuration/configure_per_game.h
index 1a727f32c..cc2513001 100644
--- a/src/yuzu/configuration/configure_per_game.h
+++ b/src/yuzu/configuration/configure_per_game.h
@@ -1,4 +1,4 @@
-// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
+// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/yuzu/configuration/configure_profile_manager.cpp b/src/yuzu/configuration/configure_profile_manager.cpp
index a47089988..6d2219bf5 100644
--- a/src/yuzu/configuration/configure_profile_manager.cpp
+++ b/src/yuzu/configuration/configure_profile_manager.cpp
@@ -306,10 +306,10 @@ void ConfigureProfileManager::SetUserImage() {
return;
}
- // Some games crash when the profile image is too big. Resize any image bigger than 256x256
+ // Profile image must be 256x256
QImage image(image_path);
- if (image.width() > 256 || image.height() > 256) {
- image = image.scaled(256, 256, Qt::KeepAspectRatio);
+ if (image.width() != 256 || image.height() != 256) {
+ image = image.scaled(256, 256, Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation);
if (!image.save(image_path)) {
QMessageBox::warning(this, tr("Error resizing user image"),
tr("Unable to resize image"));
diff --git a/src/yuzu/configuration/configure_ringcon.h b/src/yuzu/configuration/configure_ringcon.h
index b23c27906..6fd95e2b8 100644
--- a/src/yuzu/configuration/configure_ringcon.h
+++ b/src/yuzu/configuration/configure_ringcon.h
@@ -1,4 +1,4 @@
-// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/yuzu/configuration/configure_tas.h b/src/yuzu/configuration/configure_tas.h
index 4a6b0ba4e..a91891906 100644
--- a/src/yuzu/configuration/configure_tas.h
+++ b/src/yuzu/configuration/configure_tas.h
@@ -1,4 +1,4 @@
-// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/yuzu/configuration/configure_touchscreen_advanced.h b/src/yuzu/configuration/configure_touchscreen_advanced.h
index 034dc0d46..b6fdffdc8 100644
--- a/src/yuzu/configuration/configure_touchscreen_advanced.h
+++ b/src/yuzu/configuration/configure_touchscreen_advanced.h
@@ -1,4 +1,4 @@
-// SPDX-FileCopyrightText: 2016 Citra Emulator Project
+// SPDX-FileCopyrightText: 2016 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp
index 7e7d8e252..f294dc23d 100644
--- a/src/yuzu/game_list.cpp
+++ b/src/yuzu/game_list.cpp
@@ -567,9 +567,10 @@ void GameList::AddGamePopup(QMenu& context_menu, u64 program_id, const std::stri
QAction* verify_integrity = context_menu.addAction(tr("Verify Integrity"));
QAction* copy_tid = context_menu.addAction(tr("Copy Title ID to Clipboard"));
QAction* navigate_to_gamedb_entry = context_menu.addAction(tr("Navigate to GameDB entry"));
+// TODO: Implement shortcut creation for macOS
+#if !defined(__APPLE__)
QMenu* shortcut_menu = context_menu.addMenu(tr("Create Shortcut"));
QAction* create_desktop_shortcut = shortcut_menu->addAction(tr("Add to Desktop"));
-#ifndef WIN32
QAction* create_applications_menu_shortcut =
shortcut_menu->addAction(tr("Add to Applications Menu"));
#endif
@@ -647,10 +648,11 @@ void GameList::AddGamePopup(QMenu& context_menu, u64 program_id, const std::stri
connect(navigate_to_gamedb_entry, &QAction::triggered, [this, program_id]() {
emit NavigateToGamedbEntryRequested(program_id, compatibility_list);
});
+// TODO: Implement shortcut creation for macOS
+#if !defined(__APPLE__)
connect(create_desktop_shortcut, &QAction::triggered, [this, program_id, path]() {
emit CreateShortcut(program_id, path, GameListShortcutTarget::Desktop);
});
-#ifndef WIN32
connect(create_applications_menu_shortcut, &QAction::triggered, [this, program_id, path]() {
emit CreateShortcut(program_id, path, GameListShortcutTarget::Applications);
});
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index 91aba118a..f22db233b 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -10,6 +10,7 @@
#include <thread>
#include "core/loader/nca.h"
#include "core/tools/renderdoc.h"
+
#ifdef __APPLE__
#include <unistd.h> // for chdir
#endif
@@ -1064,12 +1065,6 @@ void GMainWindow::InitializeWidgets() {
volume_slider->setObjectName(QStringLiteral("volume_slider"));
volume_slider->setMaximum(200);
volume_slider->setPageStep(5);
- connect(volume_slider, &QSlider::valueChanged, this, [this](int percentage) {
- Settings::values.audio_muted = false;
- const auto volume = static_cast<u8>(percentage);
- Settings::values.volume.SetValue(volume);
- UpdateVolumeUI();
- });
volume_popup->layout()->addWidget(volume_slider);
volume_button = new VolumeButton();
@@ -1077,6 +1072,12 @@ void GMainWindow::InitializeWidgets() {
volume_button->setFocusPolicy(Qt::NoFocus);
volume_button->setCheckable(true);
UpdateVolumeUI();
+ connect(volume_slider, &QSlider::valueChanged, this, [this](int percentage) {
+ Settings::values.audio_muted = false;
+ const auto volume = static_cast<u8>(percentage);
+ Settings::values.volume.SetValue(volume);
+ UpdateVolumeUI();
+ });
connect(volume_button, &QPushButton::clicked, this, [&] {
UpdateVolumeUI();
volume_popup->setVisible(!volume_popup->isVisible());
@@ -1574,6 +1575,7 @@ void GMainWindow::ConnectMenuEvents() {
connect_menu(ui->action_Load_Cabinet_Formatter,
[this]() { OnCabinet(Service::NFP::CabinetMode::StartFormatter); });
connect_menu(ui->action_Load_Mii_Edit, &GMainWindow::OnMiiEdit);
+ connect_menu(ui->action_Open_Controller_Menu, &GMainWindow::OnOpenControllerMenu);
connect_menu(ui->action_Capture_Screenshot, &GMainWindow::OnCaptureScreenshot);
// TAS
@@ -1601,14 +1603,13 @@ void GMainWindow::UpdateMenuState() {
ui->action_Pause,
};
- const std::array applet_actions{
- ui->action_Load_Album,
- ui->action_Load_Cabinet_Nickname_Owner,
- ui->action_Load_Cabinet_Eraser,
- ui->action_Load_Cabinet_Restorer,
- ui->action_Load_Cabinet_Formatter,
- ui->action_Load_Mii_Edit,
- };
+ const std::array applet_actions{ui->action_Load_Album,
+ ui->action_Load_Cabinet_Nickname_Owner,
+ ui->action_Load_Cabinet_Eraser,
+ ui->action_Load_Cabinet_Restorer,
+ ui->action_Load_Cabinet_Formatter,
+ ui->action_Load_Mii_Edit,
+ ui->action_Open_Controller_Menu};
for (QAction* action : running_actions) {
action->setEnabled(emulation_running);
@@ -1908,7 +1909,11 @@ void GMainWindow::ConfigureFilesystemProvider(const std::string& filepath) {
void GMainWindow::BootGame(const QString& filename, u64 program_id, std::size_t program_index,
StartGameType type, AmLaunchType launch_type) {
LOG_INFO(Frontend, "yuzu starting...");
- StoreRecentFile(filename); // Put the filename on top of the list
+
+ if (program_id == 0 ||
+ program_id > static_cast<u64>(Service::AM::Applets::AppletProgramId::MaxProgramId)) {
+ StoreRecentFile(filename); // Put the filename on top of the list
+ }
// Save configurations
UpdateUISettings();
@@ -2843,170 +2848,259 @@ void GMainWindow::OnGameListNavigateToGamedbEntry(u64 program_id,
QDesktopServices::openUrl(QUrl(QStringLiteral("https://yuzu-emu.org/game/") + directory));
}
-void GMainWindow::OnGameListCreateShortcut(u64 program_id, const std::string& game_path,
- GameListShortcutTarget target) {
- // Get path to yuzu executable
- const QStringList args = QApplication::arguments();
- std::filesystem::path yuzu_command = args[0].toStdString();
-
- // If relative path, make it an absolute path
- if (yuzu_command.c_str()[0] == '.') {
- yuzu_command = Common::FS::GetCurrentDir() / yuzu_command;
+bool GMainWindow::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) try {
+#if defined(__linux__) || defined(__FreeBSD__) // Linux and FreeBSD
+ std::filesystem::path shortcut_path_full = shortcut_path / (name + ".desktop");
+ std::ofstream shortcut_stream(shortcut_path_full, std::ios::binary | std::ios::trunc);
+ if (!shortcut_stream.is_open()) {
+ LOG_ERROR(Frontend, "Failed to create shortcut");
+ return false;
}
-
-#if defined(__linux__)
- // Warn once if we are making a shortcut to a volatile AppImage
- const std::string appimage_ending =
- std::string(Common::g_scm_rev).substr(0, 9).append(".AppImage");
- if (yuzu_command.string().ends_with(appimage_ending) &&
- !UISettings::values.shortcut_already_warned) {
- if (QMessageBox::warning(this, tr("Create Shortcut"),
- tr("This will create a shortcut to the current AppImage. This may "
- "not work well if you update. Continue?"),
- QMessageBox::StandardButton::Ok |
- QMessageBox::StandardButton::Cancel) ==
- QMessageBox::StandardButton::Cancel) {
- return;
+ // TODO: Migrate fmt::print to std::print in futures STD C++ 23.
+ fmt::print(shortcut_stream, "[Desktop Entry]\n");
+ fmt::print(shortcut_stream, "Type=Application\n");
+ fmt::print(shortcut_stream, "Version=1.0\n");
+ fmt::print(shortcut_stream, "Name={}\n", name);
+ if (!comment.empty()) {
+ fmt::print(shortcut_stream, "Comment={}\n", comment);
+ }
+ if (std::filesystem::is_regular_file(icon_path)) {
+ fmt::print(shortcut_stream, "Icon={}\n", icon_path.string());
+ }
+ fmt::print(shortcut_stream, "TryExec={}\n", command.string());
+ fmt::print(shortcut_stream, "Exec={} {}\n", command.string(), arguments);
+ if (!categories.empty()) {
+ fmt::print(shortcut_stream, "Categories={}\n", categories);
+ }
+ if (!keywords.empty()) {
+ fmt::print(shortcut_stream, "Keywords={}\n", keywords);
+ }
+ return true;
+#elif defined(_WIN32) // Windows
+ HRESULT hr = CoInitialize(nullptr);
+ if (FAILED(hr)) {
+ LOG_ERROR(Frontend, "CoInitialize failed");
+ return false;
+ }
+ SCOPE_EXIT({ CoUninitialize(); });
+ IShellLinkW* ps1 = nullptr;
+ IPersistFile* persist_file = nullptr;
+ SCOPE_EXIT({
+ if (persist_file != nullptr) {
+ persist_file->Release();
}
- UISettings::values.shortcut_already_warned = true;
+ if (ps1 != nullptr) {
+ ps1->Release();
+ }
+ });
+ HRESULT hres = CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER, IID_IShellLinkW,
+ reinterpret_cast<void**>(&ps1));
+ if (FAILED(hres)) {
+ LOG_ERROR(Frontend, "Failed to create IShellLinkW instance");
+ return false;
}
-#endif // __linux__
-
- std::filesystem::path target_directory{};
-
- switch (target) {
- case GameListShortcutTarget::Desktop: {
- const QString desktop_path =
- QStandardPaths::writableLocation(QStandardPaths::DesktopLocation);
- target_directory = desktop_path.toUtf8().toStdString();
- break;
+ hres = ps1->SetPath(command.c_str());
+ if (FAILED(hres)) {
+ LOG_ERROR(Frontend, "Failed to set path");
+ return false;
}
- case GameListShortcutTarget::Applications: {
- const QString applications_path =
- QStandardPaths::writableLocation(QStandardPaths::ApplicationsLocation);
- if (applications_path.isEmpty()) {
- const char* home = std::getenv("HOME");
- if (home != nullptr) {
- target_directory = std::filesystem::path(home) / ".local/share/applications";
- }
- } else {
- target_directory = applications_path.toUtf8().toStdString();
+ if (!arguments.empty()) {
+ hres = ps1->SetArguments(Common::UTF8ToUTF16W(arguments).data());
+ if (FAILED(hres)) {
+ LOG_ERROR(Frontend, "Failed to set arguments");
+ return false;
}
- break;
}
- default:
- return;
+ if (!comment.empty()) {
+ hres = ps1->SetDescription(Common::UTF8ToUTF16W(comment).data());
+ if (FAILED(hres)) {
+ LOG_ERROR(Frontend, "Failed to set description");
+ return false;
+ }
}
-
- const QDir dir(QString::fromStdString(target_directory.generic_string()));
- if (!dir.exists()) {
- QMessageBox::critical(this, tr("Create Shortcut"),
- tr("Cannot create shortcut. Path \"%1\" does not exist.")
- .arg(QString::fromStdString(target_directory.generic_string())),
- QMessageBox::StandardButton::Ok);
- return;
+ if (std::filesystem::is_regular_file(icon_path)) {
+ hres = ps1->SetIconLocation(icon_path.c_str(), 0);
+ if (FAILED(hres)) {
+ LOG_ERROR(Frontend, "Failed to set icon location");
+ return false;
+ }
}
+ hres = ps1->QueryInterface(IID_IPersistFile, reinterpret_cast<void**>(&persist_file));
+ if (FAILED(hres)) {
+ LOG_ERROR(Frontend, "Failed to get IPersistFile interface");
+ return false;
+ }
+ hres = persist_file->Save(std::filesystem::path{shortcut_path / (name + ".lnk")}.c_str(), TRUE);
+ if (FAILED(hres)) {
+ LOG_ERROR(Frontend, "Failed to save shortcut");
+ return false;
+ }
+ return true;
+#else // Unsupported platform
+ return false;
+#endif
+} catch (const std::exception& e) {
+ LOG_ERROR(Frontend, "Failed to create shortcut: {}", e.what());
+ return false;
+}
+// Messages in pre-defined message boxes for less code spaghetti
+bool GMainWindow::CreateShortcutMessagesGUI(QWidget* parent, int imsg, const QString& game_title) {
+ int result = 0;
+ QMessageBox::StandardButtons buttons;
+ switch (imsg) {
+ case GMainWindow::CREATE_SHORTCUT_MSGBOX_FULLSCREEN_YES:
+ buttons = QMessageBox::Yes | QMessageBox::No;
+ result =
+ QMessageBox::information(parent, tr("Create Shortcut"),
+ tr("Do you want to launch the game in fullscreen?"), buttons);
+ return result == QMessageBox::Yes;
+ case GMainWindow::CREATE_SHORTCUT_MSGBOX_SUCCESS:
+ QMessageBox::information(parent, tr("Create Shortcut"),
+ tr("Successfully created a shortcut to %1").arg(game_title));
+ return false;
+ case GMainWindow::CREATE_SHORTCUT_MSGBOX_APPVOLATILE_WARNING:
+ buttons = QMessageBox::StandardButton::Ok | QMessageBox::StandardButton::Cancel;
+ result =
+ QMessageBox::warning(this, tr("Create Shortcut"),
+ tr("This will create a shortcut to the current AppImage. This may "
+ "not work well if you update. Continue?"),
+ buttons);
+ return result == QMessageBox::Ok;
+ default:
+ buttons = QMessageBox::Ok;
+ QMessageBox::critical(parent, tr("Create Shortcut"),
+ tr("Failed to create a shortcut to %1").arg(game_title), buttons);
+ return false;
+ }
+}
- const std::string game_file_name = std::filesystem::path(game_path).filename().string();
- // Determine full paths for icon and shortcut
-#if defined(__linux__) || defined(__FreeBSD__) || defined(__APPLE__)
- const char* home = std::getenv("HOME");
- const std::filesystem::path home_path = (home == nullptr ? "~" : home);
- const char* xdg_data_home = std::getenv("XDG_DATA_HOME");
-
- std::filesystem::path system_icons_path =
- (xdg_data_home == nullptr ? home_path / ".local/share/"
- : std::filesystem::path(xdg_data_home)) /
- "icons/hicolor/256x256";
- if (!Common::FS::CreateDirs(system_icons_path)) {
+bool GMainWindow::MakeShortcutIcoPath(const u64 program_id, const std::string_view game_file_name,
+ std::filesystem::path& out_icon_path) {
+ // Get path to Yuzu icons directory & icon extension
+ std::string ico_extension = "png";
+#if defined(_WIN32)
+ out_icon_path = Common::FS::GetYuzuPath(Common::FS::YuzuPath::IconsDir);
+ ico_extension = "ico";
+#elif defined(__linux__) || defined(__FreeBSD__)
+ out_icon_path = Common::FS::GetDataDirectory("XDG_DATA_HOME") / "icons/hicolor/256x256";
+#endif
+ // Create icons directory if it doesn't exist
+ if (!Common::FS::CreateDirs(out_icon_path)) {
QMessageBox::critical(
this, tr("Create Icon"),
tr("Cannot create icon file. Path \"%1\" does not exist and cannot be created.")
- .arg(QString::fromStdString(system_icons_path)),
+ .arg(QString::fromStdString(out_icon_path.string())),
QMessageBox::StandardButton::Ok);
- return;
+ out_icon_path.clear();
+ return false;
}
- std::filesystem::path icon_path =
- system_icons_path / (program_id == 0 ? fmt::format("yuzu-{}.png", game_file_name)
- : fmt::format("yuzu-{:016X}.png", program_id));
- const std::filesystem::path shortcut_path =
- target_directory / (program_id == 0 ? fmt::format("yuzu-{}.desktop", game_file_name)
- : fmt::format("yuzu-{:016X}.desktop", program_id));
-#elif defined(WIN32)
- std::filesystem::path icons_path =
- Common::FS::GetYuzuPathString(Common::FS::YuzuPath::IconsDir);
- std::filesystem::path icon_path =
- icons_path / ((program_id == 0 ? fmt::format("yuzu-{}.ico", game_file_name)
- : fmt::format("yuzu-{:016X}.ico", program_id)));
-#else
- std::string icon_extension;
-#endif
- // Get title from game file
- const FileSys::PatchManager pm{program_id, system->GetFileSystemController(),
- system->GetContentProvider()};
- const auto control = pm.GetControlMetadata();
- const auto loader = Loader::GetLoader(*system, vfs->OpenFile(game_path, FileSys::Mode::Read));
-
- std::string title{fmt::format("{:016X}", program_id)};
-
- if (control.first != nullptr) {
- title = control.first->GetApplicationName();
- } else {
- loader->ReadTitle(title);
- }
+ // Create icon file path
+ out_icon_path /= (program_id == 0 ? fmt::format("yuzu-{}.{}", game_file_name, ico_extension)
+ : fmt::format("yuzu-{:016X}.{}", program_id, ico_extension));
+ return true;
+}
- // Get icon from game file
- std::vector<u8> icon_image_file{};
- if (control.second != nullptr) {
- icon_image_file = control.second->ReadAllBytes();
- } else if (loader->ReadIcon(icon_image_file) != Loader::ResultStatus::Success) {
- LOG_WARNING(Frontend, "Could not read icon from {:s}", game_path);
+void GMainWindow::OnGameListCreateShortcut(u64 program_id, const std::string& game_path,
+ GameListShortcutTarget target) {
+ std::string game_title;
+ QString qt_game_title;
+ std::filesystem::path out_icon_path;
+ // Get path to yuzu executable
+ const QStringList args = QApplication::arguments();
+ std::filesystem::path yuzu_command = args[0].toStdString();
+ // If relative path, make it an absolute path
+ if (yuzu_command.c_str()[0] == '.') {
+ yuzu_command = Common::FS::GetCurrentDir() / yuzu_command;
}
-
- QImage icon_data =
- QImage::fromData(icon_image_file.data(), static_cast<int>(icon_image_file.size()));
-#if defined(__linux__) || defined(__FreeBSD__) || defined(__APPLE__)
- // Convert and write the icon as a PNG
- if (!icon_data.save(QString::fromStdString(icon_path.string()))) {
- LOG_ERROR(Frontend, "Could not write icon as PNG to file");
+ // Shortcut path
+ std::filesystem::path shortcut_path{};
+ if (target == GameListShortcutTarget::Desktop) {
+ shortcut_path =
+ QStandardPaths::writableLocation(QStandardPaths::DesktopLocation).toStdString();
+ } else if (target == GameListShortcutTarget::Applications) {
+ shortcut_path =
+ QStandardPaths::writableLocation(QStandardPaths::ApplicationsLocation).toStdString();
+ }
+ // Icon path and title
+ if (std::filesystem::exists(shortcut_path)) {
+ // Get title from game file
+ const FileSys::PatchManager pm{program_id, system->GetFileSystemController(),
+ system->GetContentProvider()};
+ const auto control = pm.GetControlMetadata();
+ const auto loader =
+ Loader::GetLoader(*system, vfs->OpenFile(game_path, FileSys::Mode::Read));
+ game_title = fmt::format("{:016X}", program_id);
+ if (control.first != nullptr) {
+ game_title = control.first->GetApplicationName();
+ } else {
+ loader->ReadTitle(game_title);
+ }
+ // Delete illegal characters from title
+ const std::string illegal_chars = "<>:\"/\\|?*.";
+ for (auto it = game_title.rbegin(); it != game_title.rend(); ++it) {
+ if (illegal_chars.find(*it) != std::string::npos) {
+ game_title.erase(it.base() - 1);
+ }
+ }
+ qt_game_title = QString::fromStdString(game_title);
+ // Get icon from game file
+ std::vector<u8> icon_image_file{};
+ if (control.second != nullptr) {
+ icon_image_file = control.second->ReadAllBytes();
+ } else if (loader->ReadIcon(icon_image_file) != Loader::ResultStatus::Success) {
+ LOG_WARNING(Frontend, "Could not read icon from {:s}", game_path);
+ }
+ QImage icon_data =
+ QImage::fromData(icon_image_file.data(), static_cast<int>(icon_image_file.size()));
+ if (GMainWindow::MakeShortcutIcoPath(program_id, game_title, out_icon_path)) {
+ if (!SaveIconToFile(out_icon_path, icon_data)) {
+ LOG_ERROR(Frontend, "Could not write icon to file");
+ }
+ }
} else {
- LOG_INFO(Frontend, "Wrote an icon to {}", icon_path.string());
- }
-#elif defined(WIN32)
- if (!SaveIconToFile(icon_path.string(), icon_data)) {
- LOG_ERROR(Frontend, "Could not write icon to file");
+ GMainWindow::CreateShortcutMessagesGUI(this, GMainWindow::CREATE_SHORTCUT_MSGBOX_ERROR,
+ qt_game_title);
+ LOG_ERROR(Frontend, "Invalid shortcut target");
return;
}
+#if defined(__linux__)
+ // Special case for AppImages
+ // Warn once if we are making a shortcut to a volatile AppImage
+ const std::string appimage_ending =
+ std::string(Common::g_scm_rev).substr(0, 9).append(".AppImage");
+ if (yuzu_command.string().ends_with(appimage_ending) &&
+ !UISettings::values.shortcut_already_warned) {
+ if (GMainWindow::CreateShortcutMessagesGUI(
+ this, GMainWindow::CREATE_SHORTCUT_MSGBOX_APPVOLATILE_WARNING, qt_game_title)) {
+ return;
+ }
+ UISettings::values.shortcut_already_warned = true;
+ }
#endif // __linux__
-
-#ifdef _WIN32
- // Replace characters that are illegal in Windows filenames by a dash
- const std::string illegal_chars = "<>:\"/\\|?*";
- for (char c : illegal_chars) {
- std::replace(title.begin(), title.end(), c, '_');
+ // Create shortcut
+ std::string arguments = fmt::format("-g \"{:s}\"", game_path);
+ if (GMainWindow::CreateShortcutMessagesGUI(
+ this, GMainWindow::CREATE_SHORTCUT_MSGBOX_FULLSCREEN_YES, qt_game_title)) {
+ arguments = "-f " + arguments;
}
- const std::filesystem::path shortcut_path = target_directory / (title + ".lnk").c_str();
-#endif
-
- const std::string comment =
- tr("Start %1 with the yuzu Emulator").arg(QString::fromStdString(title)).toStdString();
- const std::string arguments = fmt::format("-g \"{:s}\"", game_path);
+ const std::string comment = fmt::format("Start {:s} with the yuzu Emulator", game_title);
const std::string categories = "Game;Emulator;Qt;";
const std::string keywords = "Switch;Nintendo;";
- if (!CreateShortcut(shortcut_path.string(), title, comment, icon_path.string(),
- yuzu_command.string(), arguments, categories, keywords)) {
- QMessageBox::critical(this, tr("Create Shortcut"),
- tr("Failed to create a shortcut at %1")
- .arg(QString::fromStdString(shortcut_path.string())));
+ if (GMainWindow::CreateShortcutLink(shortcut_path, comment, out_icon_path, yuzu_command,
+ arguments, categories, keywords, game_title)) {
+ GMainWindow::CreateShortcutMessagesGUI(this, GMainWindow::CREATE_SHORTCUT_MSGBOX_SUCCESS,
+ qt_game_title);
return;
}
-
- LOG_INFO(Frontend, "Wrote a shortcut to {}", shortcut_path.string());
- QMessageBox::information(
- this, tr("Create Shortcut"),
- tr("Successfully created a shortcut to %1").arg(QString::fromStdString(title)));
+ GMainWindow::CreateShortcutMessagesGUI(this, GMainWindow::CREATE_SHORTCUT_MSGBOX_ERROR,
+ qt_game_title);
}
void GMainWindow::OnGameListOpenDirectory(const QString& directory) {
@@ -4001,66 +4095,6 @@ void GMainWindow::OpenPerGameConfiguration(u64 title_id, const std::string& file
}
}
-bool GMainWindow::CreateShortcut(const std::string& shortcut_path, const std::string& title,
- const std::string& comment, const std::string& icon_path,
- const std::string& command, const std::string& arguments,
- const std::string& categories, const std::string& keywords) {
-#if defined(__linux__) || defined(__FreeBSD__) || defined(__APPLE__)
- // This desktop file template was writing referencing
- // https://specifications.freedesktop.org/desktop-entry-spec/desktop-entry-spec-1.0.html
- std::string shortcut_contents{};
- shortcut_contents.append("[Desktop Entry]\n");
- shortcut_contents.append("Type=Application\n");
- shortcut_contents.append("Version=1.0\n");
- shortcut_contents.append(fmt::format("Name={:s}\n", title));
- shortcut_contents.append(fmt::format("Comment={:s}\n", comment));
- shortcut_contents.append(fmt::format("Icon={:s}\n", icon_path));
- shortcut_contents.append(fmt::format("TryExec={:s}\n", command));
- shortcut_contents.append(fmt::format("Exec={:s} {:s}\n", command, arguments));
- shortcut_contents.append(fmt::format("Categories={:s}\n", categories));
- shortcut_contents.append(fmt::format("Keywords={:s}\n", keywords));
-
- std::ofstream shortcut_stream(shortcut_path);
- if (!shortcut_stream.is_open()) {
- LOG_WARNING(Common, "Failed to create file {:s}", shortcut_path);
- return false;
- }
- shortcut_stream << shortcut_contents;
- shortcut_stream.close();
-
- return true;
-#elif defined(WIN32)
- IShellLinkW* shell_link;
- auto hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLinkW,
- (void**)&shell_link);
- if (FAILED(hres)) {
- return false;
- }
- shell_link->SetPath(
- Common::UTF8ToUTF16W(command).data()); // Path to the object we are referring to
- shell_link->SetArguments(Common::UTF8ToUTF16W(arguments).data());
- shell_link->SetDescription(Common::UTF8ToUTF16W(comment).data());
- shell_link->SetIconLocation(Common::UTF8ToUTF16W(icon_path).data(), 0);
-
- IPersistFile* persist_file;
- hres = shell_link->QueryInterface(IID_IPersistFile, (void**)&persist_file);
- if (FAILED(hres)) {
- return false;
- }
-
- hres = persist_file->Save(Common::UTF8ToUTF16W(shortcut_path).data(), TRUE);
- if (FAILED(hres)) {
- return false;
- }
-
- persist_file->Release();
- shell_link->Release();
-
- return true;
-#endif
- return false;
-}
-
void GMainWindow::OnLoadAmiibo() {
if (emu_thread == nullptr || !emu_thread->IsRunning()) {
return;
@@ -4099,7 +4133,6 @@ void GMainWindow::OnLoadAmiibo() {
bool GMainWindow::question(QWidget* parent, const QString& title, const QString& text,
QMessageBox::StandardButtons buttons,
QMessageBox::StandardButton defaultButton) {
-
QMessageBox* box_dialog = new QMessageBox(parent);
box_dialog->setWindowTitle(title);
box_dialog->setText(text);
@@ -4273,7 +4306,7 @@ void GMainWindow::OnToggleStatusBar() {
}
void GMainWindow::OnAlbum() {
- constexpr u64 AlbumId = 0x010000000000100Dull;
+ constexpr u64 AlbumId = static_cast<u64>(Service::AM::Applets::AppletProgramId::PhotoViewer);
auto bis_system = system->GetFileSystemController().GetSystemNANDContents();
if (!bis_system) {
QMessageBox::warning(this, tr("No firmware available"),
@@ -4292,11 +4325,11 @@ void GMainWindow::OnAlbum() {
const auto filename = QString::fromStdString(album_nca->GetFullPath());
UISettings::values.roms_path = QFileInfo(filename).path();
- BootGame(filename);
+ BootGame(filename, AlbumId);
}
void GMainWindow::OnCabinet(Service::NFP::CabinetMode mode) {
- constexpr u64 CabinetId = 0x0100000000001002ull;
+ constexpr u64 CabinetId = static_cast<u64>(Service::AM::Applets::AppletProgramId::Cabinet);
auto bis_system = system->GetFileSystemController().GetSystemNANDContents();
if (!bis_system) {
QMessageBox::warning(this, tr("No firmware available"),
@@ -4316,11 +4349,11 @@ void GMainWindow::OnCabinet(Service::NFP::CabinetMode mode) {
const auto filename = QString::fromStdString(cabinet_nca->GetFullPath());
UISettings::values.roms_path = QFileInfo(filename).path();
- BootGame(filename);
+ BootGame(filename, CabinetId);
}
void GMainWindow::OnMiiEdit() {
- constexpr u64 MiiEditId = 0x0100000000001009ull;
+ constexpr u64 MiiEditId = static_cast<u64>(Service::AM::Applets::AppletProgramId::MiiEdit);
auto bis_system = system->GetFileSystemController().GetSystemNANDContents();
if (!bis_system) {
QMessageBox::warning(this, tr("No firmware available"),
@@ -4339,7 +4372,32 @@ void GMainWindow::OnMiiEdit() {
const auto filename = QString::fromStdString((mii_applet_nca->GetFullPath()));
UISettings::values.roms_path = QFileInfo(filename).path();
- BootGame(filename);
+ BootGame(filename, MiiEditId);
+}
+
+void GMainWindow::OnOpenControllerMenu() {
+ constexpr u64 ControllerAppletId =
+ static_cast<u64>(Service::AM::Applets::AppletProgramId::Controller);
+ auto bis_system = system->GetFileSystemController().GetSystemNANDContents();
+ if (!bis_system) {
+ QMessageBox::warning(this, tr("No firmware available"),
+ tr("Please install the firmware to use the Controller Menu."));
+ return;
+ }
+
+ auto controller_applet_nca =
+ bis_system->GetEntry(ControllerAppletId, FileSys::ContentRecordType::Program);
+ if (!controller_applet_nca) {
+ QMessageBox::warning(this, tr("Controller Applet"),
+ tr("Controller Menu is not available. Please reinstall firmware."));
+ return;
+ }
+
+ system->GetAppletManager().SetCurrentAppletId(Service::AM::Applets::AppletId::Controller);
+
+ const auto filename = QString::fromStdString((controller_applet_nca->GetFullPath()));
+ UISettings::values.roms_path = QFileInfo(filename).path();
+ BootGame(filename, ControllerAppletId);
}
void GMainWindow::OnCaptureScreenshot() {
diff --git a/src/yuzu/main.h b/src/yuzu/main.h
index f9c6efe4f..49ee1e1d2 100644
--- a/src/yuzu/main.h
+++ b/src/yuzu/main.h
@@ -6,6 +6,7 @@
#include <memory>
#include <optional>
+#include <filesystem>
#include <QMainWindow>
#include <QMessageBox>
#include <QPushButton>
@@ -174,6 +175,13 @@ class GMainWindow : public QMainWindow {
UI_EMU_STOPPING,
};
+ enum {
+ CREATE_SHORTCUT_MSGBOX_FULLSCREEN_YES,
+ CREATE_SHORTCUT_MSGBOX_SUCCESS,
+ CREATE_SHORTCUT_MSGBOX_ERROR,
+ CREATE_SHORTCUT_MSGBOX_APPVOLATILE_WARNING,
+ };
+
public:
void filterBarSetChecked(bool state);
void UpdateUITheme();
@@ -402,6 +410,7 @@ private slots:
void OnAlbum();
void OnCabinet(Service::NFP::CabinetMode mode);
void OnMiiEdit();
+ void OnOpenControllerMenu();
void OnCaptureScreenshot();
void OnReinitializeKeys(ReinitializeKeyBehavior behavior);
void OnLanguageChanged(const QString& locale);
@@ -456,11 +465,14 @@ private:
bool ConfirmShutdownGame();
QString GetTasStateDescription() const;
- bool CreateShortcut(const std::string& shortcut_path, const std::string& title,
- const std::string& comment, const std::string& icon_path,
- const std::string& command, const std::string& arguments,
- const std::string& categories, const std::string& keywords);
-
+ bool CreateShortcutMessagesGUI(QWidget* parent, int imsg, const QString& game_title);
+ bool MakeShortcutIcoPath(const u64 program_id, const std::string_view game_file_name,
+ 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);
/**
* Mimic the behavior of QMessageBox::question but link controller navigation to the dialog
* The only difference is that it returns a boolean.
diff --git a/src/yuzu/main.ui b/src/yuzu/main.ui
index 88684ffb5..e53f9951e 100644
--- a/src/yuzu/main.ui
+++ b/src/yuzu/main.ui
@@ -25,7 +25,7 @@
</property>
<widget class="QWidget" name="centralwidget">
<layout class="QHBoxLayout" name="horizontalLayout">
- <property name="margin">
+ <property name="margin" stdset="0">
<number>0</number>
</property>
</layout>
@@ -36,7 +36,7 @@
<x>0</x>
<y>0</y>
<width>1280</width>
- <height>26</height>
+ <height>21</height>
</rect>
</property>
<widget class="QMenu" name="menu_File">
@@ -162,6 +162,7 @@
<addaction name="menu_cabinet_applet"/>
<addaction name="action_Load_Album"/>
<addaction name="action_Load_Mii_Edit"/>
+ <addaction name="action_Open_Controller_Menu"/>
<addaction name="separator"/>
<addaction name="action_Capture_Screenshot"/>
<addaction name="menuTAS"/>
@@ -382,9 +383,9 @@
</property>
</action>
<action name="action_Load_Album">
- <property name="text">
- <string>Open &amp;Album</string>
- </property>
+ <property name="text">
+ <string>Open &amp;Album</string>
+ </property>
</action>
<action name="action_Load_Cabinet_Nickname_Owner">
<property name="text">
@@ -407,9 +408,9 @@
</property>
</action>
<action name="action_Load_Mii_Edit">
- <property name="text">
- <string>Open &amp;Mii Editor</string>
- </property>
+ <property name="text">
+ <string>Open &amp;Mii Editor</string>
+ </property>
</action>
<action name="action_Configure_Tas">
<property name="text">
@@ -454,6 +455,11 @@
<string>R&amp;ecord</string>
</property>
</action>
+ <action name="action_Open_Controller_Menu">
+ <property name="text">
+ <string>Open &amp;Controller Menu</string>
+ </property>
+ </action>
</widget>
<resources>
<include location="yuzu.qrc"/>
diff --git a/src/yuzu/uisettings.h b/src/yuzu/uisettings.h
index 77d992c54..3485a6347 100644
--- a/src/yuzu/uisettings.h
+++ b/src/yuzu/uisettings.h
@@ -109,9 +109,13 @@ struct Values {
Settings::Specialization::Default,
true,
true};
- Setting<bool> mute_when_in_background{
- linkage, false, "muteWhenInBackground", Category::Audio, Settings::Specialization::Default,
- true, true};
+ Setting<bool> mute_when_in_background{linkage,
+ false,
+ "muteWhenInBackground",
+ Category::UiAudio,
+ Settings::Specialization::Default,
+ true,
+ true};
Setting<bool> hide_mouse{
linkage, true, "hideInactiveMouse", Category::UiGeneral, Settings::Specialization::Default,
true, true};
diff --git a/src/yuzu/util/util.cpp b/src/yuzu/util/util.cpp
index f2854c8ec..e22cf84bf 100644
--- a/src/yuzu/util/util.cpp
+++ b/src/yuzu/util/util.cpp
@@ -4,7 +4,10 @@
#include <array>
#include <cmath>
#include <QPainter>
+
+#include "common/logging/log.h"
#include "yuzu/util/util.h"
+
#ifdef _WIN32
#include <windows.h>
#include "common/fs/file.h"
@@ -42,7 +45,7 @@ QPixmap CreateCirclePixmapFromColor(const QColor& color) {
return circle_pixmap;
}
-bool SaveIconToFile(const std::string_view path, const QImage& image) {
+bool SaveIconToFile(const std::filesystem::path& icon_path, const QImage& image) {
#if defined(WIN32)
#pragma pack(push, 2)
struct IconDir {
@@ -73,7 +76,7 @@ bool SaveIconToFile(const std::string_view path, const QImage& image) {
.id_count = static_cast<WORD>(scale_sizes.size()),
};
- Common::FS::IOFile icon_file(path, Common::FS::FileAccessMode::Write,
+ Common::FS::IOFile icon_file(icon_path.string(), Common::FS::FileAccessMode::Write,
Common::FS::FileType::BinaryFile);
if (!icon_file.IsOpen()) {
return false;
@@ -135,6 +138,14 @@ bool SaveIconToFile(const std::string_view path, const QImage& image) {
icon_file.Close();
return true;
+#elif defined(__linux__) || defined(__FreeBSD__)
+ // Convert and write the icon as a PNG
+ if (!image.save(QString::fromStdString(icon_path.string()))) {
+ LOG_ERROR(Frontend, "Could not write icon as PNG to file");
+ } else {
+ LOG_INFO(Frontend, "Wrote an icon to {}", icon_path.string());
+ }
+ return true;
#else
return false;
#endif
diff --git a/src/yuzu/util/util.h b/src/yuzu/util/util.h
index 09c14ce3f..4094cf6c2 100644
--- a/src/yuzu/util/util.h
+++ b/src/yuzu/util/util.h
@@ -3,6 +3,7 @@
#pragma once
+#include <filesystem>
#include <QFont>
#include <QString>
@@ -25,4 +26,4 @@
* @param image The image to save
* @return bool If the operation succeeded
*/
-[[nodiscard]] bool SaveIconToFile(const std::string_view path, const QImage& image);
+[[nodiscard]] bool SaveIconToFile(const std::filesystem::path& icon_path, const QImage& image);
diff --git a/src/yuzu/vk_device_info.cpp b/src/yuzu/vk_device_info.cpp
index 92f10d315..ab0d39c25 100644
--- a/src/yuzu/vk_device_info.cpp
+++ b/src/yuzu/vk_device_info.cpp
@@ -31,6 +31,7 @@ void PopulateRecords(std::vector<Record>& records, QWindow* window) try {
// Create a test window with a Vulkan surface type for checking present modes.
QWindow test_window(window);
test_window.setSurfaceType(QWindow::VulkanSurface);
+ test_window.create();
auto wsi = QtCommon::GetWindowSystemInfo(&test_window);
vk::InstanceDispatch dld;