summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.ci/scripts/common/pre-upload.sh2
-rwxr-xr-x.ci/scripts/linux/docker.sh15
-rwxr-xr-x.ci/scripts/linux/upload.sh5
-rw-r--r--dist/qt_themes/default/style.qss4
-rw-r--r--dist/qt_themes/qdarkstyle/style.qss4
-rw-r--r--dist/qt_themes/qdarkstyle_midnight_blue/style.qss4
-rw-r--r--externals/nx_tzdb/CMakeLists.txt2
m---------externals/nx_tzdb/tzdb_to_nx0
-rw-r--r--src/audio_core/adsp/apps/audio_renderer/audio_renderer.cpp9
-rw-r--r--src/audio_core/adsp/apps/audio_renderer/audio_renderer.h2
-rw-r--r--src/video_core/buffer_cache/buffer_cache.h46
-rw-r--r--src/video_core/renderer_opengl/gl_texture_cache.cpp14
-rw-r--r--src/video_core/renderer_vulkan/vk_texture_cache.cpp16
-rw-r--r--src/video_core/vulkan_common/vulkan_memory_allocator.cpp8
-rw-r--r--src/yuzu/applets/qt_controller.cpp39
-rw-r--r--src/yuzu/applets/qt_controller.h6
-rw-r--r--src/yuzu/applets/qt_controller.ui54
-rw-r--r--src/yuzu/util/util.cpp81
18 files changed, 233 insertions, 78 deletions
diff --git a/.ci/scripts/common/pre-upload.sh b/.ci/scripts/common/pre-upload.sh
index 705362a3c..3583f9840 100644
--- a/.ci/scripts/common/pre-upload.sh
+++ b/.ci/scripts/common/pre-upload.sh
@@ -5,6 +5,6 @@
GITDATE="`git show -s --date=short --format='%ad' | sed 's/-//g'`"
GITREV="`git show -s --format='%h'`"
-ARTIFACTS_DIR="artifacts"
+ARTIFACTS_DIR="$PWD/artifacts"
mkdir -p "${ARTIFACTS_DIR}/"
diff --git a/.ci/scripts/linux/docker.sh b/.ci/scripts/linux/docker.sh
index e5d83d4b9..a16577b27 100755
--- a/.ci/scripts/linux/docker.sh
+++ b/.ci/scripts/linux/docker.sh
@@ -11,7 +11,7 @@ ccache -s
mkdir build || true && cd build
cmake .. \
-DBoost_USE_STATIC_LIBS=ON \
- -DCMAKE_BUILD_TYPE=Release \
+ -DCMAKE_BUILD_TYPE=RelWithDebInfo \
-DCMAKE_CXX_FLAGS="-march=x86-64-v2" \
-DCMAKE_CXX_COMPILER=/usr/lib/ccache/g++ \
-DCMAKE_C_COMPILER=/usr/lib/ccache/gcc \
@@ -31,6 +31,19 @@ ccache -s
ctest -VV -C Release
+# Separate debug symbols from specified executables
+for EXE in yuzu; do
+ EXE_PATH="bin/$EXE"
+ # Copy debug symbols out
+ objcopy --only-keep-debug $EXE_PATH $EXE_PATH.debug
+ # Add debug link and strip debug symbols
+ objcopy -g --add-gnu-debuglink=$EXE_PATH.debug $EXE_PATH $EXE_PATH.out
+ # Overwrite original with stripped copy
+ mv $EXE_PATH.out $EXE_PATH
+done
+# Strip debug symbols from all executables
+find bin/ -type f -not -regex '.*.debug' -exec strip -g {} ';'
+
DESTDIR="$PWD/AppDir" ninja install
rm -vf AppDir/usr/bin/yuzu-cmd AppDir/usr/bin/yuzu-tester
diff --git a/.ci/scripts/linux/upload.sh b/.ci/scripts/linux/upload.sh
index e0f336427..fbb2d9c1b 100755
--- a/.ci/scripts/linux/upload.sh
+++ b/.ci/scripts/linux/upload.sh
@@ -59,4 +59,9 @@ if [ "${RELEASE_NAME}" = "mainline" ] || [ "${RELEASE_NAME}" = "early-access" ];
cp "build/${APPIMAGE_NAME}" "${DIR_NAME}/yuzu-${RELEASE_NAME}.AppImage"
fi
+# Copy debug symbols to artifacts
+cd build/bin
+tar $COMPRESSION_FLAGS "${ARTIFACTS_DIR}/${REV_NAME}-debug.tar.xz" *.debug
+cd -
+
. .ci/scripts/common/post-upload.sh
diff --git a/dist/qt_themes/default/style.qss b/dist/qt_themes/default/style.qss
index 79960ee0c..921950c6c 100644
--- a/dist/qt_themes/default/style.qss
+++ b/dist/qt_themes/default/style.qss
@@ -120,6 +120,10 @@ QWidget#connectedControllers {
background: transparent;
}
+QWidget#closeButtons {
+ background: transparent;
+}
+
QWidget#playersSupported,
QWidget#controllersSupported,
QWidget#controllerSupported1,
diff --git a/dist/qt_themes/qdarkstyle/style.qss b/dist/qt_themes/qdarkstyle/style.qss
index 63a636ae6..328ac942f 100644
--- a/dist/qt_themes/qdarkstyle/style.qss
+++ b/dist/qt_themes/qdarkstyle/style.qss
@@ -1380,6 +1380,10 @@ QWidget#connectedControllers {
background: transparent;
}
+QWidget#closeButtons {
+ background: transparent;
+}
+
QWidget#playersSupported,
QWidget#controllersSupported,
QWidget#controllerSupported1,
diff --git a/dist/qt_themes/qdarkstyle_midnight_blue/style.qss b/dist/qt_themes/qdarkstyle_midnight_blue/style.qss
index 0c53115f6..eb0889b13 100644
--- a/dist/qt_themes/qdarkstyle_midnight_blue/style.qss
+++ b/dist/qt_themes/qdarkstyle_midnight_blue/style.qss
@@ -2305,6 +2305,10 @@ QWidget#connectedControllers {
background: transparent;
}
+QWidget#closeButtons {
+ background: transparent;
+}
+
QWidget#playersSupported,
QWidget#controllersSupported,
QWidget#controllerSupported1,
diff --git a/externals/nx_tzdb/CMakeLists.txt b/externals/nx_tzdb/CMakeLists.txt
index 593786250..0fad24642 100644
--- a/externals/nx_tzdb/CMakeLists.txt
+++ b/externals/nx_tzdb/CMakeLists.txt
@@ -27,7 +27,7 @@ if (CMAKE_SYSTEM_NAME STREQUAL "Windows" OR ANDROID)
set(CAN_BUILD_NX_TZDB false)
endif()
-set(NX_TZDB_VERSION "220816")
+set(NX_TZDB_VERSION "221202")
set(NX_TZDB_ARCHIVE "${CMAKE_CURRENT_BINARY_DIR}/${NX_TZDB_VERSION}.zip")
set(NX_TZDB_ROMFS_DIR "${CMAKE_CURRENT_BINARY_DIR}/nx_tzdb")
diff --git a/externals/nx_tzdb/tzdb_to_nx b/externals/nx_tzdb/tzdb_to_nx
-Subproject 212afa2394a74226dcf1b7996a570aae17debb6
+Subproject 0d17dd066d91f954a4c89d46dcb067eead6b1e4
diff --git a/src/audio_core/adsp/apps/audio_renderer/audio_renderer.cpp b/src/audio_core/adsp/apps/audio_renderer/audio_renderer.cpp
index 972d5e45b..ef301d8b4 100644
--- a/src/audio_core/adsp/apps/audio_renderer/audio_renderer.cpp
+++ b/src/audio_core/adsp/apps/audio_renderer/audio_renderer.cpp
@@ -77,6 +77,7 @@ void AudioRenderer::Wait() {
"{}, got {}",
Message::RenderResponse, msg);
}
+ PostDSPClearCommandBuffer();
}
void AudioRenderer::Send(Direction dir, u32 message) {
@@ -96,6 +97,14 @@ void AudioRenderer::SetCommandBuffer(s32 session_id, CpuAddr buffer, u64 size, u
command_buffers[session_id].reset_buffer = reset;
}
+void AudioRenderer::PostDSPClearCommandBuffer() noexcept {
+ for (auto& buffer : command_buffers) {
+ buffer.buffer = 0;
+ buffer.size = 0;
+ buffer.reset_buffer = false;
+ }
+}
+
u32 AudioRenderer::GetRemainCommandCount(s32 session_id) const noexcept {
return command_buffers[session_id].remaining_command_count;
}
diff --git a/src/audio_core/adsp/apps/audio_renderer/audio_renderer.h b/src/audio_core/adsp/apps/audio_renderer/audio_renderer.h
index 85874d88a..57b89d9fe 100644
--- a/src/audio_core/adsp/apps/audio_renderer/audio_renderer.h
+++ b/src/audio_core/adsp/apps/audio_renderer/audio_renderer.h
@@ -85,6 +85,8 @@ private:
*/
void CreateSinkStreams();
+ void PostDSPClearCommandBuffer() noexcept;
+
/// Core system
Core::System& system;
/// The output sink the AudioRenderer will send samples to
diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h
index 9e90c587c..9b2698fad 100644
--- a/src/video_core/buffer_cache/buffer_cache.h
+++ b/src/video_core/buffer_cache/buffer_cache.h
@@ -544,7 +544,7 @@ void BufferCache<P>::CommitAsyncFlushesHigh() {
it++;
}
- boost::container::small_vector<std::pair<BufferCopy, BufferId>, 1> downloads;
+ boost::container::small_vector<std::pair<BufferCopy, BufferId>, 16> downloads;
u64 total_size_bytes = 0;
u64 largest_copy = 0;
for (const IntervalSet& intervals : committed_ranges) {
@@ -914,6 +914,11 @@ void BufferCache<P>::BindHostGraphicsStorageBuffers(size_t stage) {
const u32 offset = buffer.Offset(binding.cpu_addr);
const bool is_written = ((channel_state->written_storage_buffers[stage] >> index) & 1) != 0;
+
+ if (is_written) {
+ MarkWrittenBuffer(binding.buffer_id, binding.cpu_addr, size);
+ }
+
if constexpr (NEEDS_BIND_STORAGE_INDEX) {
runtime.BindStorageBuffer(stage, binding_index, buffer, offset, size, is_written);
++binding_index;
@@ -931,6 +936,11 @@ void BufferCache<P>::BindHostGraphicsTextureBuffers(size_t stage) {
const u32 size = binding.size;
SynchronizeBuffer(buffer, binding.cpu_addr, size);
+ const bool is_written = ((channel_state->written_texture_buffers[stage] >> index) & 1) != 0;
+ if (is_written) {
+ MarkWrittenBuffer(binding.buffer_id, binding.cpu_addr, size);
+ }
+
const u32 offset = buffer.Offset(binding.cpu_addr);
const PixelFormat format = binding.format;
if constexpr (SEPARATE_IMAGE_BUFFERS_BINDINGS) {
@@ -962,6 +972,8 @@ void BufferCache<P>::BindHostTransformFeedbackBuffers() {
const u32 size = binding.size;
SynchronizeBuffer(buffer, binding.cpu_addr, size);
+ MarkWrittenBuffer(binding.buffer_id, binding.cpu_addr, size);
+
const u32 offset = buffer.Offset(binding.cpu_addr);
host_bindings.buffers.push_back(&buffer);
host_bindings.offsets.push_back(offset);
@@ -1011,6 +1023,11 @@ void BufferCache<P>::BindHostComputeStorageBuffers() {
const u32 offset = buffer.Offset(binding.cpu_addr);
const bool is_written =
((channel_state->written_compute_storage_buffers >> index) & 1) != 0;
+
+ if (is_written) {
+ MarkWrittenBuffer(binding.buffer_id, binding.cpu_addr, size);
+ }
+
if constexpr (NEEDS_BIND_STORAGE_INDEX) {
runtime.BindComputeStorageBuffer(binding_index, buffer, offset, size, is_written);
++binding_index;
@@ -1028,6 +1045,12 @@ void BufferCache<P>::BindHostComputeTextureBuffers() {
const u32 size = binding.size;
SynchronizeBuffer(buffer, binding.cpu_addr, size);
+ const bool is_written =
+ ((channel_state->written_compute_texture_buffers >> index) & 1) != 0;
+ if (is_written) {
+ MarkWrittenBuffer(binding.buffer_id, binding.cpu_addr, size);
+ }
+
const u32 offset = buffer.Offset(binding.cpu_addr);
const PixelFormat format = binding.format;
if constexpr (SEPARATE_IMAGE_BUFFERS_BINDINGS) {
@@ -1201,16 +1224,11 @@ void BufferCache<P>::UpdateUniformBuffers(size_t stage) {
template <class P>
void BufferCache<P>::UpdateStorageBuffers(size_t stage) {
- const u32 written_mask = channel_state->written_storage_buffers[stage];
ForEachEnabledBit(channel_state->enabled_storage_buffers[stage], [&](u32 index) {
// Resolve buffer
Binding& binding = channel_state->storage_buffers[stage][index];
const BufferId buffer_id = FindBuffer(binding.cpu_addr, binding.size);
binding.buffer_id = buffer_id;
- // Mark buffer as written if needed
- if (((written_mask >> index) & 1) != 0) {
- MarkWrittenBuffer(buffer_id, binding.cpu_addr, binding.size);
- }
});
}
@@ -1219,10 +1237,6 @@ void BufferCache<P>::UpdateTextureBuffers(size_t stage) {
ForEachEnabledBit(channel_state->enabled_texture_buffers[stage], [&](u32 index) {
Binding& binding = channel_state->texture_buffers[stage][index];
binding.buffer_id = FindBuffer(binding.cpu_addr, binding.size);
- // Mark buffer as written if needed
- if (((channel_state->written_texture_buffers[stage] >> index) & 1) != 0) {
- MarkWrittenBuffer(binding.buffer_id, binding.cpu_addr, binding.size);
- }
});
}
@@ -1252,7 +1266,6 @@ void BufferCache<P>::UpdateTransformFeedbackBuffer(u32 index) {
.size = size,
.buffer_id = buffer_id,
};
- MarkWrittenBuffer(buffer_id, *cpu_addr, size);
}
template <class P>
@@ -1279,10 +1292,6 @@ void BufferCache<P>::UpdateComputeStorageBuffers() {
// Resolve buffer
Binding& binding = channel_state->compute_storage_buffers[index];
binding.buffer_id = FindBuffer(binding.cpu_addr, binding.size);
- // Mark as written if needed
- if (((channel_state->written_compute_storage_buffers >> index) & 1) != 0) {
- MarkWrittenBuffer(binding.buffer_id, binding.cpu_addr, binding.size);
- }
});
}
@@ -1291,18 +1300,11 @@ void BufferCache<P>::UpdateComputeTextureBuffers() {
ForEachEnabledBit(channel_state->enabled_compute_texture_buffers, [&](u32 index) {
Binding& binding = channel_state->compute_texture_buffers[index];
binding.buffer_id = FindBuffer(binding.cpu_addr, binding.size);
- // Mark as written if needed
- if (((channel_state->written_compute_texture_buffers >> index) & 1) != 0) {
- MarkWrittenBuffer(binding.buffer_id, binding.cpu_addr, binding.size);
- }
});
}
template <class P>
void BufferCache<P>::MarkWrittenBuffer(BufferId buffer_id, VAddr cpu_addr, u32 size) {
- if (memory_tracker.IsRegionCpuModified(cpu_addr, size)) {
- SynchronizeBuffer(slot_buffers[buffer_id], cpu_addr, size);
- }
memory_tracker.MarkRegionAsGpuModified(cpu_addr, size);
const IntervalType base_interval{cpu_addr, cpu_addr + size};
diff --git a/src/video_core/renderer_opengl/gl_texture_cache.cpp b/src/video_core/renderer_opengl/gl_texture_cache.cpp
index 9cafd2983..512eef575 100644
--- a/src/video_core/renderer_opengl/gl_texture_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_texture_cache.cpp
@@ -1048,6 +1048,10 @@ void Image::Scale(bool up_scale) {
}
bool Image::ScaleUp(bool ignore) {
+ const auto& resolution = runtime->resolution;
+ if (!resolution.active) {
+ return false;
+ }
if (True(flags & ImageFlagBits::Rescaled)) {
return false;
}
@@ -1060,9 +1064,6 @@ bool Image::ScaleUp(bool ignore) {
return false;
}
flags |= ImageFlagBits::Rescaled;
- if (!runtime->resolution.active) {
- return false;
- }
has_scaled = true;
if (ignore) {
current_texture = upscaled_backup.handle;
@@ -1073,13 +1074,14 @@ bool Image::ScaleUp(bool ignore) {
}
bool Image::ScaleDown(bool ignore) {
- if (False(flags & ImageFlagBits::Rescaled)) {
+ const auto& resolution = runtime->resolution;
+ if (!resolution.active) {
return false;
}
- flags &= ~ImageFlagBits::Rescaled;
- if (!runtime->resolution.active) {
+ if (False(flags & ImageFlagBits::Rescaled)) {
return false;
}
+ flags &= ~ImageFlagBits::Rescaled;
if (ignore) {
current_texture = texture.handle;
return true;
diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp
index 54bfeccb6..93773a69f 100644
--- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp
@@ -1530,15 +1530,15 @@ bool Image::IsRescaled() const noexcept {
}
bool Image::ScaleUp(bool ignore) {
+ const auto& resolution = runtime->resolution;
+ if (!resolution.active) {
+ return false;
+ }
if (True(flags & ImageFlagBits::Rescaled)) {
return false;
}
ASSERT(info.type != ImageType::Linear);
flags |= ImageFlagBits::Rescaled;
- const auto& resolution = runtime->resolution;
- if (!resolution.active) {
- return false;
- }
has_scaled = true;
if (!scaled_image) {
const bool is_2d = info.type == ImageType::e2D;
@@ -1567,15 +1567,15 @@ bool Image::ScaleUp(bool ignore) {
}
bool Image::ScaleDown(bool ignore) {
+ const auto& resolution = runtime->resolution;
+ if (!resolution.active) {
+ return false;
+ }
if (False(flags & ImageFlagBits::Rescaled)) {
return false;
}
ASSERT(info.type != ImageType::Linear);
flags &= ~ImageFlagBits::Rescaled;
- const auto& resolution = runtime->resolution;
- if (!resolution.active) {
- return false;
- }
current_image = *original_image;
if (ignore) {
return true;
diff --git a/src/video_core/vulkan_common/vulkan_memory_allocator.cpp b/src/video_core/vulkan_common/vulkan_memory_allocator.cpp
index 82767fdf0..8dd1667f3 100644
--- a/src/video_core/vulkan_common/vulkan_memory_allocator.cpp
+++ b/src/video_core/vulkan_common/vulkan_memory_allocator.cpp
@@ -66,9 +66,10 @@ struct Range {
switch (usage) {
case MemoryUsage::Upload:
case MemoryUsage::Stream:
- return VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT;
+ return VMA_ALLOCATION_CREATE_MAPPED_BIT |
+ VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT;
case MemoryUsage::Download:
- return VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT;
+ return VMA_ALLOCATION_CREATE_MAPPED_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT;
case MemoryUsage::DeviceLocal:
return {};
}
@@ -252,8 +253,7 @@ vk::Image MemoryAllocator::CreateImage(const VkImageCreateInfo& ci) const {
vk::Buffer MemoryAllocator::CreateBuffer(const VkBufferCreateInfo& ci, MemoryUsage usage) const {
const VmaAllocationCreateInfo alloc_ci = {
- .flags = VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT | VMA_ALLOCATION_CREATE_MAPPED_BIT |
- MemoryUsageVmaFlags(usage),
+ .flags = VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT | MemoryUsageVmaFlags(usage),
.usage = MemoryUsageVma(usage),
.requiredFlags = 0,
.preferredFlags = MemoryUsagePreferedVmaFlags(usage),
diff --git a/src/yuzu/applets/qt_controller.cpp b/src/yuzu/applets/qt_controller.cpp
index d15559518..ca0e14fad 100644
--- a/src/yuzu/applets/qt_controller.cpp
+++ b/src/yuzu/applets/qt_controller.cpp
@@ -23,6 +23,7 @@
#include "yuzu/configuration/configure_vibration.h"
#include "yuzu/configuration/input_profiles.h"
#include "yuzu/main.h"
+#include "yuzu/util/controller_navigation.h"
namespace {
@@ -132,6 +133,8 @@ QtControllerSelectorDialog::QtControllerSelectorDialog(
ui->checkboxPlayer7Connected, ui->checkboxPlayer8Connected,
};
+ ui->labelError->setVisible(false);
+
// Setup/load everything prior to setting up connections.
// This avoids unintentionally changing the states of elements while loading them in.
SetSupportedControllers();
@@ -143,6 +146,8 @@ QtControllerSelectorDialog::QtControllerSelectorDialog(
LoadConfiguration();
+ controller_navigation = new ControllerNavigation(system.HIDCore(), this);
+
for (std::size_t i = 0; i < NUM_PLAYERS; ++i) {
SetExplainText(i);
UpdateControllerIcon(i);
@@ -151,6 +156,8 @@ QtControllerSelectorDialog::QtControllerSelectorDialog(
connect(player_groupboxes[i], &QGroupBox::toggled, [this, i](bool checked) {
if (checked) {
+ // Hide eventual error message about number of controllers
+ ui->labelError->setVisible(false);
for (std::size_t index = 0; index <= i; ++index) {
connected_controller_checkboxes[index]->setChecked(checked);
}
@@ -199,6 +206,12 @@ QtControllerSelectorDialog::QtControllerSelectorDialog(
connect(ui->buttonBox, &QDialogButtonBox::accepted, this,
&QtControllerSelectorDialog::ApplyConfiguration);
+ connect(controller_navigation, &ControllerNavigation::TriggerKeyboardEvent,
+ [this](Qt::Key key) {
+ QKeyEvent* event = new QKeyEvent(QEvent::KeyPress, key, Qt::NoModifier);
+ QCoreApplication::postEvent(this, event);
+ });
+
// Enhancement: Check if the parameters have already been met before disconnecting controllers.
// If all the parameters are met AND only allows a single player,
// stop the constructor here as we do not need to continue.
@@ -217,6 +230,7 @@ QtControllerSelectorDialog::QtControllerSelectorDialog(
}
QtControllerSelectorDialog::~QtControllerSelectorDialog() {
+ controller_navigation->UnloadController();
system.HIDCore().DisableAllControllerConfiguration();
}
@@ -291,6 +305,31 @@ void QtControllerSelectorDialog::CallConfigureInputProfileDialog() {
dialog.exec();
}
+void QtControllerSelectorDialog::keyPressEvent(QKeyEvent* evt) {
+ const auto num_connected_players = static_cast<int>(
+ std::count_if(player_groupboxes.begin(), player_groupboxes.end(),
+ [](const QGroupBox* player) { return player->isChecked(); }));
+
+ const auto min_supported_players = parameters.enable_single_mode ? 1 : parameters.min_players;
+ const auto max_supported_players = parameters.enable_single_mode ? 1 : parameters.max_players;
+
+ if ((evt->key() == Qt::Key_Enter || evt->key() == Qt::Key_Return) && !parameters_met) {
+ // Display error message when trying to validate using "Enter" and "OK" button is disabled
+ ui->labelError->setVisible(true);
+ return;
+ } else if (evt->key() == Qt::Key_Left && num_connected_players > min_supported_players) {
+ // Remove a player if possible
+ connected_controller_checkboxes[num_connected_players - 1]->setChecked(false);
+ return;
+ } else if (evt->key() == Qt::Key_Right && num_connected_players < max_supported_players) {
+ // Add a player, if possible
+ ui->labelError->setVisible(false);
+ connected_controller_checkboxes[num_connected_players]->setChecked(true);
+ return;
+ }
+ QDialog::keyPressEvent(evt);
+}
+
bool QtControllerSelectorDialog::CheckIfParametersMet() {
// Here, we check and validate the current configuration against all applicable parameters.
const auto num_connected_players = static_cast<int>(
diff --git a/src/yuzu/applets/qt_controller.h b/src/yuzu/applets/qt_controller.h
index 2fdc35857..7f0673d06 100644
--- a/src/yuzu/applets/qt_controller.h
+++ b/src/yuzu/applets/qt_controller.h
@@ -34,6 +34,8 @@ class HIDCore;
enum class NpadStyleIndex : u8;
} // namespace Core::HID
+class ControllerNavigation;
+
class QtControllerSelectorDialog final : public QDialog {
Q_OBJECT
@@ -46,6 +48,8 @@ public:
int exec() override;
+ void keyPressEvent(QKeyEvent* evt) override;
+
private:
// Applies the current configuration.
void ApplyConfiguration();
@@ -110,6 +114,8 @@ private:
Core::System& system;
+ ControllerNavigation* controller_navigation = nullptr;
+
// This is true if and only if all parameters are met. Otherwise, this is false.
// This determines whether the "OK" button can be clicked to exit the applet.
bool parameters_met{false};
diff --git a/src/yuzu/applets/qt_controller.ui b/src/yuzu/applets/qt_controller.ui
index 729e921ee..6f7cb3c13 100644
--- a/src/yuzu/applets/qt_controller.ui
+++ b/src/yuzu/applets/qt_controller.ui
@@ -2624,13 +2624,53 @@
</spacer>
</item>
<item alignment="Qt::AlignBottom">
- <widget class="QDialogButtonBox" name="buttonBox">
- <property name="enabled">
- <bool>true</bool>
- </property>
- <property name="standardButtons">
- <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
- </property>
+ <widget class="QWidget" name="closeButtons" native="true">
+ <layout class="QVBoxLayout" name="verticalLayout_46">
+ <property name="spacing">
+ <number>7</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>
+ <widget class="QLabel" name="labelError">
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">QLabel { color : red; }</string>
+ </property>
+ <property name="text">
+ <string>Not enough controllers</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QDialogButtonBox" name="buttonBox">
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="standardButtons">
+ <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
</widget>
</item>
</layout>
diff --git a/src/yuzu/util/util.cpp b/src/yuzu/util/util.cpp
index 61cf00176..f2854c8ec 100644
--- a/src/yuzu/util/util.cpp
+++ b/src/yuzu/util/util.cpp
@@ -63,25 +63,15 @@ bool SaveIconToFile(const std::string_view path, const QImage& image) {
};
#pragma pack(pop)
- QImage source_image = image.convertToFormat(QImage::Format_RGB32);
+ const QImage source_image = image.convertToFormat(QImage::Format_RGB32);
+ constexpr std::array<int, 7> scale_sizes{256, 128, 64, 48, 32, 24, 16};
constexpr int bytes_per_pixel = 4;
- const int image_size = source_image.width() * source_image.height() * bytes_per_pixel;
-
- BITMAPINFOHEADER info_header{};
- info_header.biSize = sizeof(BITMAPINFOHEADER), info_header.biWidth = source_image.width(),
- info_header.biHeight = source_image.height() * 2, info_header.biPlanes = 1,
- info_header.biBitCount = bytes_per_pixel * 8, info_header.biCompression = BI_RGB;
-
- const IconDir icon_dir{.id_reserved = 0, .id_type = 1, .id_count = 1};
- const IconDirEntry icon_entry{.width = static_cast<BYTE>(source_image.width()),
- .height = static_cast<BYTE>(source_image.height() * 2),
- .color_count = 0,
- .reserved = 0,
- .planes = 1,
- .bit_count = bytes_per_pixel * 8,
- .bytes_in_res =
- static_cast<DWORD>(sizeof(BITMAPINFOHEADER) + image_size),
- .image_offset = sizeof(IconDir) + sizeof(IconDirEntry)};
+
+ const IconDir icon_dir{
+ .id_reserved = 0,
+ .id_type = 1,
+ .id_count = static_cast<WORD>(scale_sizes.size()),
+ };
Common::FS::IOFile icon_file(path, Common::FS::FileAccessMode::Write,
Common::FS::FileType::BinaryFile);
@@ -92,20 +82,55 @@ bool SaveIconToFile(const std::string_view path, const QImage& image) {
if (!icon_file.Write(icon_dir)) {
return false;
}
- if (!icon_file.Write(icon_entry)) {
- return false;
- }
- if (!icon_file.Write(info_header)) {
- return false;
+
+ std::size_t image_offset = sizeof(IconDir) + (sizeof(IconDirEntry) * scale_sizes.size());
+ for (std::size_t i = 0; i < scale_sizes.size(); i++) {
+ const int image_size = scale_sizes[i] * scale_sizes[i] * bytes_per_pixel;
+ const IconDirEntry icon_entry{
+ .width = static_cast<BYTE>(scale_sizes[i]),
+ .height = static_cast<BYTE>(scale_sizes[i]),
+ .color_count = 0,
+ .reserved = 0,
+ .planes = 1,
+ .bit_count = bytes_per_pixel * 8,
+ .bytes_in_res = static_cast<DWORD>(sizeof(BITMAPINFOHEADER) + image_size),
+ .image_offset = static_cast<DWORD>(image_offset),
+ };
+ image_offset += icon_entry.bytes_in_res;
+ if (!icon_file.Write(icon_entry)) {
+ return false;
+ }
}
- for (int y = 0; y < image.height(); y++) {
- const auto* line = source_image.scanLine(source_image.height() - 1 - y);
- std::vector<u8> line_data(source_image.width() * bytes_per_pixel);
- std::memcpy(line_data.data(), line, line_data.size());
- if (!icon_file.Write(line_data)) {
+ for (std::size_t i = 0; i < scale_sizes.size(); i++) {
+ const QImage scaled_image = source_image.scaled(
+ scale_sizes[i], scale_sizes[i], Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
+ const BITMAPINFOHEADER info_header{
+ .biSize = sizeof(BITMAPINFOHEADER),
+ .biWidth = scaled_image.width(),
+ .biHeight = scaled_image.height() * 2,
+ .biPlanes = 1,
+ .biBitCount = bytes_per_pixel * 8,
+ .biCompression = BI_RGB,
+ .biSizeImage{},
+ .biXPelsPerMeter{},
+ .biYPelsPerMeter{},
+ .biClrUsed{},
+ .biClrImportant{},
+ };
+
+ if (!icon_file.Write(info_header)) {
return false;
}
+
+ for (int y = 0; y < scaled_image.height(); y++) {
+ const auto* line = scaled_image.scanLine(scaled_image.height() - 1 - y);
+ std::vector<u8> line_data(scaled_image.width() * bytes_per_pixel);
+ std::memcpy(line_data.data(), line, line_data.size());
+ if (!icon_file.Write(line_data)) {
+ return false;
+ }
+ }
}
icon_file.Close();