summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rwxr-xr-x.ci/scripts/format/script.sh39
-rw-r--r--.ci/scripts/linux/exec.sh2
-rw-r--r--.github/workflows/verify.yml8
-rw-r--r--LICENSES/BSD-2-Clause.txt2
-rw-r--r--LICENSES/BSD-3-Clause.txt2
-rw-r--r--LICENSES/MPL-2.0.txt2
-rw-r--r--externals/ffmpeg/CMakeLists.txt2
m---------externals/nx_tzdb/tzdb_to_nx0
-rw-r--r--src/android/app/build.gradle.kts83
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/AbstractDiffAdapter.kt33
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/AbstractListAdapter.kt98
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/AbstractSingleSelectionList.kt105
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/AddonAdapter.kt36
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/AppletAdapter.kt88
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/CabinetLauncherDialogAdapter.kt57
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/DriverAdapter.kt101
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/FolderAdapter.kt36
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt192
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GamePropertiesAdapter.kt28
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/HomeSettingAdapter.kt72
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/InstallableAdapter.kt36
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/LicenseAdapter.kt50
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/SetupAdapter.kt45
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AboutFragment.kt4
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/DriverManagerFragment.kt76
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/model/Driver.kt27
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/model/DriverViewModel.kt129
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/model/SelectableItem.kt9
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt3
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GpuDriverHelper.kt3
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/viewholder/AbstractViewHolder.kt18
-rw-r--r--src/android/app/src/main/jni/native.cpp4
-rw-r--r--src/android/app/src/main/res/layout-w600dp/fragment_about.xml4
-rw-r--r--src/android/app/src/main/res/layout/fragment_about.xml4
-rw-r--r--src/android/app/src/main/res/menu/menu_driver_manager.xml11
-rw-r--r--src/common/page_table.cpp34
-rw-r--r--src/core/cpu_manager.h2
-rw-r--r--src/core/debugger/debugger.cpp39
-rw-r--r--src/core/debugger/gdbstub.cpp110
-rw-r--r--src/core/debugger/gdbstub.h15
-rw-r--r--src/core/frontend/applets/controller.cpp2
-rw-r--r--src/core/hle/kernel/k_memory_block.h14
-rw-r--r--src/core/hle/kernel/k_memory_block_manager.cpp4
-rw-r--r--src/core/hle/kernel/k_memory_block_manager.h4
-rw-r--r--src/core/hle/kernel/k_page_table_base.cpp46
-rw-r--r--src/core/hle/kernel/k_page_table_base.h1
-rw-r--r--src/core/hle/kernel/k_process.cpp6
-rw-r--r--src/core/hle/kernel/k_transfer_memory.cpp8
-rw-r--r--src/core/hle/kernel/kernel.cpp31
-rw-r--r--src/core/hle/kernel/kernel.h6
-rw-r--r--src/core/hle/kernel/svc/svc_process.cpp8
-rw-r--r--src/core/hle/kernel/svc/svc_transfer_memory.cpp4
-rw-r--r--src/core/hle/kernel/svc_types.h4
-rw-r--r--src/core/hle/service/am/applets/applet_profile_select.h2
-rw-r--r--src/core/hle/service/caps/caps_manager.cpp10
-rw-r--r--src/core/hle/service/caps/caps_manager.h4
-rw-r--r--src/core/hle/service/caps/caps_result.h2
-rw-r--r--src/core/hle/service/friend/friend.cpp2
-rw-r--r--src/core/hle/service/glue/arp.cpp7
-rw-r--r--src/core/hle/service/hid/hid.cpp18
-rw-r--r--src/core/hle/service/hid/hid_server.cpp2
-rw-r--r--src/core/hle/service/hid/hidbus.cpp8
-rw-r--r--src/core/hle/service/hid/hidbus.h2
-rw-r--r--src/core/hle/service/hle_ipc.cpp20
-rw-r--r--src/core/hle/service/hle_ipc.h18
-rw-r--r--src/core/hle/service/nfc/common/amiibo_crypto.cpp10
-rw-r--r--src/core/hle/service/nfc/common/device.cpp6
-rw-r--r--src/core/hle/service/nfc/common/device.h2
-rw-r--r--src/core/hle/service/nfp/nfp_types.h14
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h2
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp2
-rw-r--r--src/core/hle/service/nvdrv/nvdata.h2
-rw-r--r--src/core/hle/service/nvnflinger/nvnflinger.cpp3
-rw-r--r--src/core/hle/service/pcv/pcv.cpp10
-rw-r--r--src/core/hle/service/pm/pm.cpp85
-rw-r--r--src/core/hle/service/server_manager.cpp9
-rw-r--r--src/core/hle/service/set/system_settings.cpp2
-rw-r--r--src/core/hle/service/set/system_settings.h4
-rw-r--r--src/core/hle/service/set/system_settings_server.cpp10
-rw-r--r--src/core/hle/service/vi/display/vi_display.cpp13
-rw-r--r--src/core/hle/service/vi/display/vi_display.h12
-rw-r--r--src/core/hle/service/vi/vi.cpp59
-rw-r--r--src/core/hle/service/vi/vi.h2
-rw-r--r--src/hid_core/CMakeLists.txt34
-rw-r--r--src/hid_core/frontend/emulated_controller.cpp22
-rw-r--r--src/hid_core/hid_types.h3
-rw-r--r--src/hid_core/hid_util.h2
-rw-r--r--src/hid_core/resources/abstracted_pad/abstract_battery_handler.cpp197
-rw-r--r--src/hid_core/resources/abstracted_pad/abstract_battery_handler.h49
-rw-r--r--src/hid_core/resources/abstracted_pad/abstract_button_handler.cpp199
-rw-r--r--src/hid_core/resources/abstracted_pad/abstract_button_handler.h75
-rw-r--r--src/hid_core/resources/abstracted_pad/abstract_ir_sensor_handler.cpp126
-rw-r--r--src/hid_core/resources/abstracted_pad/abstract_ir_sensor_handler.h56
-rw-r--r--src/hid_core/resources/abstracted_pad/abstract_led_handler.cpp123
-rw-r--r--src/hid_core/resources/abstracted_pad/abstract_led_handler.h43
-rw-r--r--src/hid_core/resources/abstracted_pad/abstract_mcu_handler.cpp108
-rw-r--r--src/hid_core/resources/abstracted_pad/abstract_mcu_handler.h52
-rw-r--r--src/hid_core/resources/abstracted_pad/abstract_nfc_handler.cpp140
-rw-r--r--src/hid_core/resources/abstracted_pad/abstract_nfc_handler.h57
-rw-r--r--src/hid_core/resources/abstracted_pad/abstract_pad.cpp294
-rw-r--r--src/hid_core/resources/abstracted_pad/abstract_pad.h123
-rw-r--r--src/hid_core/resources/abstracted_pad/abstract_pad_holder.cpp99
-rw-r--r--src/hid_core/resources/abstracted_pad/abstract_pad_holder.h47
-rw-r--r--src/hid_core/resources/abstracted_pad/abstract_palma_handler.cpp47
-rw-r--r--src/hid_core/resources/abstracted_pad/abstract_palma_handler.h37
-rw-r--r--src/hid_core/resources/abstracted_pad/abstract_properties_handler.cpp322
-rw-r--r--src/hid_core/resources/abstracted_pad/abstract_properties_handler.h86
-rw-r--r--src/hid_core/resources/abstracted_pad/abstract_sixaxis_handler.cpp154
-rw-r--r--src/hid_core/resources/abstracted_pad/abstract_sixaxis_handler.h61
-rw-r--r--src/hid_core/resources/abstracted_pad/abstract_vibration_handler.cpp73
-rw-r--r--src/hid_core/resources/abstracted_pad/abstract_vibration_handler.h51
-rw-r--r--src/hid_core/resources/npad/npad.cpp8
-rw-r--r--src/hid_core/resources/npad/npad_data.cpp2
-rw-r--r--src/hid_core/resources/npad/npad_types.h99
-rw-r--r--src/hid_core/resources/npad/npad_vibration.cpp80
-rw-r--r--src/hid_core/resources/npad/npad_vibration.h34
-rw-r--r--src/hid_core/resources/six_axis/six_axis.cpp6
-rw-r--r--src/hid_core/resources/vibration/gc_vibration_device.cpp106
-rw-r--r--src/hid_core/resources/vibration/gc_vibration_device.h31
-rw-r--r--src/hid_core/resources/vibration/n64_vibration_device.cpp80
-rw-r--r--src/hid_core/resources/vibration/n64_vibration_device.h29
-rw-r--r--src/hid_core/resources/vibration/vibration_base.cpp30
-rw-r--r--src/hid_core/resources/vibration/vibration_base.h28
-rw-r--r--src/hid_core/resources/vibration/vibration_device.cpp84
-rw-r--r--src/hid_core/resources/vibration/vibration_device.h35
-rw-r--r--src/yuzu/applets/qt_controller.cpp8
-rw-r--r--src/yuzu/applets/qt_software_keyboard.cpp2
-rw-r--r--src/yuzu/configuration/configure_input_player.cpp18
-rw-r--r--src/yuzu/configuration/configure_input_player_widget.cpp2
-rw-r--r--src/yuzu/main.cpp2
-rw-r--r--src/yuzu/util/controller_navigation.cpp4
131 files changed, 4429 insertions, 1028 deletions
diff --git a/.ci/scripts/format/script.sh b/.ci/scripts/format/script.sh
index 25b0718f0..f9c63dbfa 100755
--- a/.ci/scripts/format/script.sh
+++ b/.ci/scripts/format/script.sh
@@ -3,38 +3,35 @@
# SPDX-FileCopyrightText: 2019 yuzu Emulator Project
# SPDX-License-Identifier: GPL-2.0-or-later
-if grep -nrI '\s$' src *.yml *.txt *.md Doxyfile .gitignore .gitmodules .ci* dist/*.desktop \
- dist/*.svg dist/*.xml; then
+shopt -s nullglob globstar
+
+if git grep -nrI '\s$' src **/*.yml **/*.txt **/*.md Doxyfile .gitignore .gitmodules .ci* dist/*.desktop dist/*.svg dist/*.xml; then
echo Trailing whitespace found, aborting
exit 1
fi
# Default clang-format points to default 3.5 version one
-CLANG_FORMAT=${CLANG_FORMAT:-clang-format-15}
-$CLANG_FORMAT --version
-
-if [ "$TRAVIS_EVENT_TYPE" = "pull_request" ]; then
- # Get list of every file modified in this pull request
- files_to_lint="$(git diff --name-only --diff-filter=ACMRTUXB $TRAVIS_COMMIT_RANGE | grep '^src/[^.]*[.]\(cpp\|h\)$' || true)"
-else
- # Check everything for branch pushes
- files_to_lint="$(find src/ -name '*.cpp' -or -name '*.h')"
-fi
+CLANG_FORMAT="${CLANG_FORMAT:-clang-format-15}"
+"$CLANG_FORMAT" --version
# Turn off tracing for this because it's too verbose
set +x
-for f in $files_to_lint; do
- d=$(diff -u "$f" <($CLANG_FORMAT "$f") || true)
- if ! [ -z "$d" ]; then
- echo "!!! $f not compliant to coding style, here is the fix:"
- echo "$d"
- fail=1
- fi
+# Check everything for branch pushes
+FILES_TO_LINT="$(find src/ -name '*.cpp' -or -name '*.h')"
+
+for f in $FILES_TO_LINT; do
+ echo "$f"
+ "$CLANG_FORMAT" -i "$f"
done
-set -x
+DIFF=$(git -c core.fileMode=false diff)
-if [ "$fail" = 1 ]; then
+if [ ! -z "$DIFF" ]; then
+ echo "!!! Not compliant to coding style, here is the fix:"
+ echo "$DIFF"
exit 1
fi
+
+cd src/android
+./gradlew ktlintCheck
diff --git a/.ci/scripts/linux/exec.sh b/.ci/scripts/linux/exec.sh
index fa3d78cc2..04e2486a1 100644
--- a/.ci/scripts/linux/exec.sh
+++ b/.ci/scripts/linux/exec.sh
@@ -9,7 +9,7 @@ chmod a+x ./.ci/scripts/linux/docker.sh
sudo chown -R 1027 ./
# The environment variables listed below:
-# AZURECIREPO TITLEBARFORMATIDLE TITLEBARFORMATRUNNING DISPLAYVERSION
+# AZURECIREPO TITLEBARFORMATIDLE TITLEBARFORMATRUNNING DISPLAYVERSION
# are requested in src/common/CMakeLists.txt and appear to be provided somewhere in Azure DevOps
docker run -e AZURECIREPO -e TITLEBARFORMATIDLE -e TITLEBARFORMATRUNNING -e DISPLAYVERSION -e ENABLE_COMPATIBILITY_REPORTING -e CCACHE_DIR=/yuzu/ccache -v "$(pwd):/yuzu" -w /yuzu yuzuemu/build-environments:linux-fresh /bin/bash /yuzu/.ci/scripts/linux/docker.sh "$1"
diff --git a/.github/workflows/verify.yml b/.github/workflows/verify.yml
index c073f3f3f..62eb69aeb 100644
--- a/.github/workflows/verify.yml
+++ b/.github/workflows/verify.yml
@@ -13,13 +13,15 @@ jobs:
format:
name: 'verify format'
runs-on: ubuntu-latest
- container:
- image: yuzuemu/build-environments:linux-clang-format
- options: -u 1001
steps:
- uses: actions/checkout@v3
with:
submodules: false
+ - name: set up JDK 17
+ uses: actions/setup-java@v3
+ with:
+ java-version: '17'
+ distribution: 'temurin'
- name: 'Verify Formatting'
run: bash -ex ./.ci/scripts/format/script.sh
build:
diff --git a/LICENSES/BSD-2-Clause.txt b/LICENSES/BSD-2-Clause.txt
index 5f662b354..eb3c575b8 100644
--- a/LICENSES/BSD-2-Clause.txt
+++ b/LICENSES/BSD-2-Clause.txt
@@ -1,4 +1,4 @@
-Copyright (c) <year> <owner>
+Copyright (c) <year> <owner>
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
diff --git a/LICENSES/BSD-3-Clause.txt b/LICENSES/BSD-3-Clause.txt
index ea890afbc..086d3992c 100644
--- a/LICENSES/BSD-3-Clause.txt
+++ b/LICENSES/BSD-3-Clause.txt
@@ -1,4 +1,4 @@
-Copyright (c) <year> <owner>.
+Copyright (c) <year> <owner>.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
diff --git a/LICENSES/MPL-2.0.txt b/LICENSES/MPL-2.0.txt
index 14e2f777f..a612ad981 100644
--- a/LICENSES/MPL-2.0.txt
+++ b/LICENSES/MPL-2.0.txt
@@ -35,7 +35,7 @@ Mozilla Public License Version 2.0
means any form of the work other than Source Code Form.
1.7. "Larger Work"
- means a work that combines Covered Software with other material, in
+ means a work that combines Covered Software with other material, in
a separate file or files, that is not Covered Software.
1.8. "License"
diff --git a/externals/ffmpeg/CMakeLists.txt b/externals/ffmpeg/CMakeLists.txt
index f2886eb6c..543585d4f 100644
--- a/externals/ffmpeg/CMakeLists.txt
+++ b/externals/ffmpeg/CMakeLists.txt
@@ -138,7 +138,7 @@ if (NOT WIN32 AND NOT ANDROID)
--cross-prefix=${TOOLCHAIN}/bin/aarch64-linux-android-
--sysroot=${SYSROOT}
--target-os=android
- --extra-ldflags="--ld-path=${TOOLCHAIN}/bin/ld.lld"
+ --extra-ldflags="--ld-path=${TOOLCHAIN}/bin/ld.lld"
--extra-ldflags="-nostdlib"
)
endif()
diff --git a/externals/nx_tzdb/tzdb_to_nx b/externals/nx_tzdb/tzdb_to_nx
-Subproject f6680093bca30265c161581fd813d4ddd33f1e3
+Subproject 404d39004570a26c734a9d1fa29ab4d63089c59
diff --git a/src/android/app/build.gradle.kts b/src/android/app/build.gradle.kts
index 53aafa08c..06e59d1ac 100644
--- a/src/android/app/build.gradle.kts
+++ b/src/android/app/build.gradle.kts
@@ -188,8 +188,15 @@ tasks.create<Delete>("ktlintReset") {
delete(File(buildDir.path + File.separator + "intermediates/ktLint"))
}
+val showFormatHelp = {
+ logger.lifecycle(
+ "If this check fails, please try running \"gradlew ktlintFormat\" for automatic " +
+ "codestyle fixes"
+ )
+}
+tasks.getByPath("ktlintKotlinScriptCheck").doFirst { showFormatHelp.invoke() }
+tasks.getByPath("ktlintMainSourceSetCheck").doFirst { showFormatHelp.invoke() }
tasks.getByPath("loadKtlintReporters").dependsOn("ktlintReset")
-tasks.getByPath("preBuild").dependsOn("ktlintCheck")
ktlint {
version.set("0.47.1")
@@ -228,71 +235,33 @@ dependencies {
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.0")
}
-fun getGitVersion(): String {
- var versionName = "0.0"
-
- try {
- versionName = ProcessBuilder("git", "describe", "--always", "--long")
+fun runGitCommand(command: List<String>): String {
+ return try {
+ ProcessBuilder(command)
.directory(project.rootDir)
.redirectOutput(ProcessBuilder.Redirect.PIPE)
.redirectError(ProcessBuilder.Redirect.PIPE)
.start().inputStream.bufferedReader().use { it.readText() }
.trim()
- .replace(Regex("(-0)?-[^-]+$"), "")
} catch (e: Exception) {
- logger.error("Cannot find git, defaulting to dummy version number")
+ logger.error("Cannot find git")
+ ""
}
-
- if (System.getenv("GITHUB_ACTIONS") != null) {
- val gitTag = System.getenv("GIT_TAG_NAME")
- versionName = gitTag ?: versionName
- }
-
- return versionName
}
-fun getGitHash(): String {
- try {
- val processBuilder = ProcessBuilder("git", "rev-parse", "--short", "HEAD")
- processBuilder.directory(project.rootDir)
- val process = processBuilder.start()
- val inputStream = process.inputStream
- val errorStream = process.errorStream
- process.waitFor()
-
- return if (process.exitValue() == 0) {
- inputStream.bufferedReader()
- .use { it.readText().trim() } // return the value of gitHash
- } else {
- val errorMessage = errorStream.bufferedReader().use { it.readText().trim() }
- logger.error("Error running git command: $errorMessage")
- "dummy-hash" // return a dummy hash value in case of an error
- }
- } catch (e: Exception) {
- logger.error("$e: Cannot find git, defaulting to dummy build hash")
- return "dummy-hash" // return a dummy hash value in case of an error
+fun getGitVersion(): String {
+ val versionName = if (System.getenv("GITHUB_ACTIONS") != null) {
+ val gitTag = System.getenv("GIT_TAG_NAME") ?: ""
+ gitTag
+ } else {
+ runGitCommand(listOf("git", "describe", "--always", "--long"))
+ .replace(Regex("(-0)?-[^-]+$"), "")
}
+ return versionName.ifEmpty { "0.0" }
}
-fun getBranch(): String {
- try {
- val processBuilder = ProcessBuilder("git", "rev-parse", "--abbrev-ref", "HEAD")
- processBuilder.directory(project.rootDir)
- val process = processBuilder.start()
- val inputStream = process.inputStream
- val errorStream = process.errorStream
- process.waitFor()
-
- return if (process.exitValue() == 0) {
- inputStream.bufferedReader()
- .use { it.readText().trim() } // return the value of gitHash
- } else {
- val errorMessage = errorStream.bufferedReader().use { it.readText().trim() }
- logger.error("Error running git command: $errorMessage")
- "dummy-hash" // return a dummy hash value in case of an error
- }
- } catch (e: Exception) {
- logger.error("$e: Cannot find git, defaulting to dummy build hash")
- return "dummy-hash" // return a dummy hash value in case of an error
- }
-}
+fun getGitHash(): String =
+ runGitCommand(listOf("git", "rev-parse", "--short", "HEAD")).ifEmpty { "dummy-hash" }
+
+fun getBranch(): String =
+ runGitCommand(listOf("git", "rev-parse", "--abbrev-ref", "HEAD")).ifEmpty { "dummy-hash" }
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/AbstractDiffAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/AbstractDiffAdapter.kt
new file mode 100644
index 000000000..f006f9e3d
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/AbstractDiffAdapter.kt
@@ -0,0 +1,33 @@
+// SPDX-FileCopyrightText: 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+package org.yuzu.yuzu_emu.adapters
+
+import android.annotation.SuppressLint
+import androidx.recyclerview.widget.AsyncDifferConfig
+import androidx.recyclerview.widget.DiffUtil
+import androidx.recyclerview.widget.ListAdapter
+import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder
+import androidx.recyclerview.widget.RecyclerView
+
+/**
+ * Generic adapter that implements an [AsyncDifferConfig] and covers some of the basic boilerplate
+ * code used in every [RecyclerView].
+ * Type assigned to [Model] must inherit from [Object] in order to be compared properly.
+ */
+abstract class AbstractDiffAdapter<Model : Any, Holder : AbstractViewHolder<Model>> :
+ ListAdapter<Model, Holder>(AsyncDifferConfig.Builder(DiffCallback<Model>()).build()) {
+ override fun onBindViewHolder(holder: Holder, position: Int) =
+ holder.bind(currentList[position])
+
+ private class DiffCallback<Model> : DiffUtil.ItemCallback<Model>() {
+ override fun areItemsTheSame(oldItem: Model & Any, newItem: Model & Any): Boolean {
+ return oldItem === newItem
+ }
+
+ @SuppressLint("DiffUtilEquals")
+ override fun areContentsTheSame(oldItem: Model & Any, newItem: Model & Any): Boolean {
+ return oldItem == newItem
+ }
+ }
+}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/AbstractListAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/AbstractListAdapter.kt
new file mode 100644
index 000000000..3dfee3d0c
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/AbstractListAdapter.kt
@@ -0,0 +1,98 @@
+// SPDX-FileCopyrightText: 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+package org.yuzu.yuzu_emu.adapters
+
+import android.annotation.SuppressLint
+import androidx.recyclerview.widget.RecyclerView
+import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder
+
+/**
+ * Generic list class meant to take care of basic lists
+ * @param currentList The list to show initially
+ */
+abstract class AbstractListAdapter<Model : Any, Holder : AbstractViewHolder<Model>>(
+ open var currentList: List<Model>
+) : RecyclerView.Adapter<Holder>() {
+ override fun onBindViewHolder(holder: Holder, position: Int) =
+ holder.bind(currentList[position])
+
+ override fun getItemCount(): Int = currentList.size
+
+ /**
+ * Adds an item to [currentList] and notifies the underlying adapter of the change. If no parameter
+ * is passed in for position, [item] is added to the end of the list. Invokes [callback] last.
+ * @param item The item to add to the list
+ * @param position Index where [item] will be added
+ * @param callback Lambda that's called at the end of the list changes and has the added list
+ * position passed in as a parameter
+ */
+ open fun addItem(item: Model, position: Int = -1, callback: ((position: Int) -> Unit)? = null) {
+ val newList = currentList.toMutableList()
+ val positionToUpdate: Int
+ if (position == -1) {
+ newList.add(item)
+ currentList = newList
+ positionToUpdate = currentList.size - 1
+ } else {
+ newList.add(position, item)
+ currentList = newList
+ positionToUpdate = position
+ }
+ onItemAdded(positionToUpdate, callback)
+ }
+
+ protected fun onItemAdded(position: Int, callback: ((Int) -> Unit)? = null) {
+ notifyItemInserted(position)
+ callback?.invoke(position)
+ }
+
+ /**
+ * Replaces the [item] at [position] in the [currentList] and notifies the underlying adapter
+ * of the change. Invokes [callback] last.
+ * @param item New list item
+ * @param position Index where [item] will replace the existing list item
+ * @param callback Lambda that's called at the end of the list changes and has the changed list
+ * position passed in as a parameter
+ */
+ fun changeItem(item: Model, position: Int, callback: ((position: Int) -> Unit)? = null) {
+ val newList = currentList.toMutableList()
+ newList[position] = item
+ currentList = newList
+ onItemChanged(position, callback)
+ }
+
+ protected fun onItemChanged(position: Int, callback: ((Int) -> Unit)? = null) {
+ notifyItemChanged(position)
+ callback?.invoke(position)
+ }
+
+ /**
+ * Removes the list item at [position] in [currentList] and notifies the underlying adapter
+ * of the change. Invokes [callback] last.
+ * @param position Index where the list item will be removed
+ * @param callback Lambda that's called at the end of the list changes and has the removed list
+ * position passed in as a parameter
+ */
+ fun removeItem(position: Int, callback: ((position: Int) -> Unit)? = null) {
+ val newList = currentList.toMutableList()
+ newList.removeAt(position)
+ currentList = newList
+ onItemRemoved(position, callback)
+ }
+
+ protected fun onItemRemoved(position: Int, callback: ((Int) -> Unit)? = null) {
+ notifyItemRemoved(position)
+ callback?.invoke(position)
+ }
+
+ /**
+ * Replaces [currentList] with [newList] and notifies the underlying adapter of the change.
+ * @param newList The new list to replace [currentList]
+ */
+ @SuppressLint("NotifyDataSetChanged")
+ open fun replaceList(newList: List<Model>) {
+ currentList = newList
+ notifyDataSetChanged()
+ }
+}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/AbstractSingleSelectionList.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/AbstractSingleSelectionList.kt
new file mode 100644
index 000000000..52163f9d7
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/AbstractSingleSelectionList.kt
@@ -0,0 +1,105 @@
+// SPDX-FileCopyrightText: 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+package org.yuzu.yuzu_emu.adapters
+
+import org.yuzu.yuzu_emu.model.SelectableItem
+import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder
+
+/**
+ * Generic list class meant to take care of single selection UI updates
+ * @param currentList The list to show initially
+ * @param defaultSelection The default selection to use if no list items are selected by
+ * [SelectableItem.selected] or if the currently selected item is removed from the list
+ */
+abstract class AbstractSingleSelectionList<
+ Model : SelectableItem,
+ Holder : AbstractViewHolder<Model>
+ >(
+ final override var currentList: List<Model>,
+ private val defaultSelection: DefaultSelection = DefaultSelection.Start
+) : AbstractListAdapter<Model, Holder>(currentList) {
+ var selectedItem = getDefaultSelection()
+
+ init {
+ findSelectedItem()
+ }
+
+ /**
+ * Changes the selection state of the [SelectableItem] that was selected and the previously selected
+ * item and notifies the underlying adapter of the change for those items. Invokes [callback] last.
+ * Does nothing if [position] is the same as the currently selected item.
+ * @param position Index of the item that was selected
+ * @param callback Lambda that's called at the end of the list changes and has the selected list
+ * position passed in as a parameter
+ */
+ fun selectItem(position: Int, callback: ((position: Int) -> Unit)? = null) {
+ if (position == selectedItem) {
+ return
+ }
+
+ val previouslySelectedItem = selectedItem
+ selectedItem = position
+ if (currentList.indices.contains(selectedItem)) {
+ currentList[selectedItem].onSelectionStateChanged(true)
+ }
+ if (currentList.indices.contains(previouslySelectedItem)) {
+ currentList[previouslySelectedItem].onSelectionStateChanged(false)
+ }
+ onItemChanged(previouslySelectedItem)
+ onItemChanged(selectedItem)
+ callback?.invoke(position)
+ }
+
+ /**
+ * Removes a given item from the list and notifies the underlying adapter of the change. If the
+ * currently selected item was the item that was removed, the item at the position provided
+ * by [defaultSelection] will be made the new selection. Invokes [callback] last.
+ * @param position Index of the item that was removed
+ * @param callback Lambda that's called at the end of the list changes and has the removed and
+ * selected list positions passed in as parameters
+ */
+ fun removeSelectableItem(
+ position: Int,
+ callback: ((removedPosition: Int, selectedPosition: Int) -> Unit)?
+ ) {
+ removeItem(position)
+ if (position == selectedItem) {
+ selectedItem = getDefaultSelection()
+ currentList[selectedItem].onSelectionStateChanged(true)
+ onItemChanged(selectedItem)
+ } else if (position < selectedItem) {
+ selectedItem--
+ }
+ callback?.invoke(position, selectedItem)
+ }
+
+ override fun addItem(item: Model, position: Int, callback: ((Int) -> Unit)?) {
+ super.addItem(item, position, callback)
+ if (position <= selectedItem && position != -1) {
+ selectedItem++
+ }
+ }
+
+ override fun replaceList(newList: List<Model>) {
+ super.replaceList(newList)
+ findSelectedItem()
+ }
+
+ private fun findSelectedItem() {
+ for (i in currentList.indices) {
+ if (currentList[i].selected) {
+ selectedItem = i
+ break
+ }
+ }
+ }
+
+ private fun getDefaultSelection(): Int =
+ when (defaultSelection) {
+ DefaultSelection.Start -> currentList.indices.first
+ DefaultSelection.End -> currentList.indices.last
+ }
+
+ enum class DefaultSelection { Start, End }
+}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/AddonAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/AddonAdapter.kt
index 15c7ca3c9..94c151325 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/AddonAdapter.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/AddonAdapter.kt
@@ -5,48 +5,28 @@ package org.yuzu.yuzu_emu.adapters
import android.view.LayoutInflater
import android.view.ViewGroup
-import androidx.recyclerview.widget.AsyncDifferConfig
-import androidx.recyclerview.widget.DiffUtil
-import androidx.recyclerview.widget.ListAdapter
-import androidx.recyclerview.widget.RecyclerView
import org.yuzu.yuzu_emu.databinding.ListItemAddonBinding
import org.yuzu.yuzu_emu.model.Addon
+import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder
-class AddonAdapter : ListAdapter<Addon, AddonAdapter.AddonViewHolder>(
- AsyncDifferConfig.Builder(DiffCallback()).build()
-) {
+class AddonAdapter : AbstractDiffAdapter<Addon, AddonAdapter.AddonViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): AddonViewHolder {
ListItemAddonBinding.inflate(LayoutInflater.from(parent.context), parent, false)
.also { return AddonViewHolder(it) }
}
- override fun getItemCount(): Int = currentList.size
-
- override fun onBindViewHolder(holder: AddonViewHolder, position: Int) =
- holder.bind(currentList[position])
-
inner class AddonViewHolder(val binding: ListItemAddonBinding) :
- RecyclerView.ViewHolder(binding.root) {
- fun bind(addon: Addon) {
+ AbstractViewHolder<Addon>(binding) {
+ override fun bind(model: Addon) {
binding.root.setOnClickListener {
binding.addonSwitch.isChecked = !binding.addonSwitch.isChecked
}
- binding.title.text = addon.title
- binding.version.text = addon.version
+ binding.title.text = model.title
+ binding.version.text = model.version
binding.addonSwitch.setOnCheckedChangeListener { _, checked ->
- addon.enabled = checked
+ model.enabled = checked
}
- binding.addonSwitch.isChecked = addon.enabled
- }
- }
-
- private class DiffCallback : DiffUtil.ItemCallback<Addon>() {
- override fun areItemsTheSame(oldItem: Addon, newItem: Addon): Boolean {
- return oldItem == newItem
- }
-
- override fun areContentsTheSame(oldItem: Addon, newItem: Addon): Boolean {
- return oldItem == newItem
+ binding.addonSwitch.isChecked = model.enabled
}
}
}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/AppletAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/AppletAdapter.kt
index 4a05c5be9..41d7f72b8 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/AppletAdapter.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/AppletAdapter.kt
@@ -4,13 +4,11 @@
package org.yuzu.yuzu_emu.adapters
import android.view.LayoutInflater
-import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import androidx.core.content.res.ResourcesCompat
import androidx.fragment.app.FragmentActivity
import androidx.navigation.findNavController
-import androidx.recyclerview.widget.RecyclerView
import org.yuzu.yuzu_emu.HomeNavigationDirections
import org.yuzu.yuzu_emu.NativeLibrary
import org.yuzu.yuzu_emu.R
@@ -19,72 +17,58 @@ import org.yuzu.yuzu_emu.databinding.CardSimpleOutlinedBinding
import org.yuzu.yuzu_emu.model.Applet
import org.yuzu.yuzu_emu.model.AppletInfo
import org.yuzu.yuzu_emu.model.Game
+import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder
-class AppletAdapter(val activity: FragmentActivity, var applets: List<Applet>) :
- RecyclerView.Adapter<AppletAdapter.AppletViewHolder>(),
- View.OnClickListener {
-
+class AppletAdapter(val activity: FragmentActivity, applets: List<Applet>) :
+ AbstractListAdapter<Applet, AppletAdapter.AppletViewHolder>(applets) {
override fun onCreateViewHolder(
parent: ViewGroup,
viewType: Int
): AppletAdapter.AppletViewHolder {
CardSimpleOutlinedBinding.inflate(LayoutInflater.from(parent.context), parent, false)
- .apply { root.setOnClickListener(this@AppletAdapter) }
.also { return AppletViewHolder(it) }
}
- override fun onBindViewHolder(holder: AppletViewHolder, position: Int) =
- holder.bind(applets[position])
-
- override fun getItemCount(): Int = applets.size
-
- override fun onClick(view: View) {
- val applet = (view.tag as AppletViewHolder).applet
- val appletPath = NativeLibrary.getAppletLaunchPath(applet.appletInfo.entryId)
- if (appletPath.isEmpty()) {
- Toast.makeText(
- YuzuApplication.appContext,
- R.string.applets_error_applet,
- Toast.LENGTH_SHORT
- ).show()
- return
- }
-
- if (applet.appletInfo == AppletInfo.Cabinet) {
- view.findNavController()
- .navigate(R.id.action_appletLauncherFragment_to_cabinetLauncherDialogFragment)
- return
- }
-
- NativeLibrary.setCurrentAppletId(applet.appletInfo.appletId)
- val appletGame = Game(
- title = YuzuApplication.appContext.getString(applet.titleId),
- path = appletPath
- )
- val action = HomeNavigationDirections.actionGlobalEmulationActivity(appletGame)
- view.findNavController().navigate(action)
- }
-
inner class AppletViewHolder(val binding: CardSimpleOutlinedBinding) :
- RecyclerView.ViewHolder(binding.root) {
- lateinit var applet: Applet
-
- init {
- itemView.tag = this
- }
-
- fun bind(applet: Applet) {
- this.applet = applet
-
- binding.title.setText(applet.titleId)
- binding.description.setText(applet.descriptionId)
+ AbstractViewHolder<Applet>(binding) {
+ override fun bind(model: Applet) {
+ binding.title.setText(model.titleId)
+ binding.description.setText(model.descriptionId)
binding.icon.setImageDrawable(
ResourcesCompat.getDrawable(
binding.icon.context.resources,
- applet.iconId,
+ model.iconId,
binding.icon.context.theme
)
)
+
+ binding.root.setOnClickListener { onClick(model) }
+ }
+
+ fun onClick(applet: Applet) {
+ val appletPath = NativeLibrary.getAppletLaunchPath(applet.appletInfo.entryId)
+ if (appletPath.isEmpty()) {
+ Toast.makeText(
+ binding.root.context,
+ R.string.applets_error_applet,
+ Toast.LENGTH_SHORT
+ ).show()
+ return
+ }
+
+ if (applet.appletInfo == AppletInfo.Cabinet) {
+ binding.root.findNavController()
+ .navigate(R.id.action_appletLauncherFragment_to_cabinetLauncherDialogFragment)
+ return
+ }
+
+ NativeLibrary.setCurrentAppletId(applet.appletInfo.appletId)
+ val appletGame = Game(
+ title = YuzuApplication.appContext.getString(applet.titleId),
+ path = appletPath
+ )
+ val action = HomeNavigationDirections.actionGlobalEmulationActivity(appletGame)
+ binding.root.findNavController().navigate(action)
}
}
}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/CabinetLauncherDialogAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/CabinetLauncherDialogAdapter.kt
index e7b7c0f2f..a56137148 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/CabinetLauncherDialogAdapter.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/CabinetLauncherDialogAdapter.kt
@@ -4,12 +4,10 @@
package org.yuzu.yuzu_emu.adapters
import android.view.LayoutInflater
-import android.view.View
import android.view.ViewGroup
import androidx.core.content.res.ResourcesCompat
import androidx.fragment.app.Fragment
import androidx.navigation.fragment.findNavController
-import androidx.recyclerview.widget.RecyclerView
import org.yuzu.yuzu_emu.HomeNavigationDirections
import org.yuzu.yuzu_emu.NativeLibrary
import org.yuzu.yuzu_emu.R
@@ -19,54 +17,43 @@ import org.yuzu.yuzu_emu.model.CabinetMode
import org.yuzu.yuzu_emu.adapters.CabinetLauncherDialogAdapter.CabinetModeViewHolder
import org.yuzu.yuzu_emu.model.AppletInfo
import org.yuzu.yuzu_emu.model.Game
+import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder
class CabinetLauncherDialogAdapter(val fragment: Fragment) :
- RecyclerView.Adapter<CabinetModeViewHolder>(),
- View.OnClickListener {
- private val cabinetModes = CabinetMode.values().copyOfRange(1, CabinetMode.values().size)
+ AbstractListAdapter<CabinetMode, CabinetModeViewHolder>(
+ CabinetMode.values().copyOfRange(1, CabinetMode.entries.size).toList()
+ ) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CabinetModeViewHolder {
DialogListItemBinding.inflate(LayoutInflater.from(parent.context), parent, false)
- .apply { root.setOnClickListener(this@CabinetLauncherDialogAdapter) }
.also { return CabinetModeViewHolder(it) }
}
- override fun getItemCount(): Int = cabinetModes.size
-
- override fun onBindViewHolder(holder: CabinetModeViewHolder, position: Int) =
- holder.bind(cabinetModes[position])
-
- override fun onClick(view: View) {
- val mode = (view.tag as CabinetModeViewHolder).cabinetMode
- val appletPath = NativeLibrary.getAppletLaunchPath(AppletInfo.Cabinet.entryId)
- NativeLibrary.setCurrentAppletId(AppletInfo.Cabinet.appletId)
- NativeLibrary.setCabinetMode(mode.id)
- val appletGame = Game(
- title = YuzuApplication.appContext.getString(R.string.cabinet_applet),
- path = appletPath
- )
- val action = HomeNavigationDirections.actionGlobalEmulationActivity(appletGame)
- fragment.findNavController().navigate(action)
- }
-
inner class CabinetModeViewHolder(val binding: DialogListItemBinding) :
- RecyclerView.ViewHolder(binding.root) {
- lateinit var cabinetMode: CabinetMode
-
- init {
- itemView.tag = this
- }
-
- fun bind(cabinetMode: CabinetMode) {
- this.cabinetMode = cabinetMode
+ AbstractViewHolder<CabinetMode>(binding) {
+ override fun bind(model: CabinetMode) {
binding.icon.setImageDrawable(
ResourcesCompat.getDrawable(
binding.icon.context.resources,
- cabinetMode.iconId,
+ model.iconId,
binding.icon.context.theme
)
)
- binding.title.setText(cabinetMode.titleId)
+ binding.title.setText(model.titleId)
+
+ binding.root.setOnClickListener { onClick(model) }
+ }
+
+ private fun onClick(mode: CabinetMode) {
+ val appletPath = NativeLibrary.getAppletLaunchPath(AppletInfo.Cabinet.entryId)
+ NativeLibrary.setCurrentAppletId(AppletInfo.Cabinet.appletId)
+ NativeLibrary.setCabinetMode(mode.id)
+ val appletGame = Game(
+ title = YuzuApplication.appContext.getString(R.string.cabinet_applet),
+ path = appletPath
+ )
+ val action = HomeNavigationDirections.actionGlobalEmulationActivity(appletGame)
+ fragment.findNavController().navigate(action)
}
}
}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/DriverAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/DriverAdapter.kt
index d290a656c..d6f17cf29 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/DriverAdapter.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/DriverAdapter.kt
@@ -7,65 +7,39 @@ import android.text.TextUtils
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
-import androidx.recyclerview.widget.AsyncDifferConfig
-import androidx.recyclerview.widget.DiffUtil
-import androidx.recyclerview.widget.ListAdapter
-import androidx.recyclerview.widget.RecyclerView
-import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.databinding.CardDriverOptionBinding
+import org.yuzu.yuzu_emu.features.settings.model.StringSetting
+import org.yuzu.yuzu_emu.model.Driver
import org.yuzu.yuzu_emu.model.DriverViewModel
-import org.yuzu.yuzu_emu.utils.GpuDriverHelper
-import org.yuzu.yuzu_emu.utils.GpuDriverMetadata
+import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder
class DriverAdapter(private val driverViewModel: DriverViewModel) :
- ListAdapter<Pair<String, GpuDriverMetadata>, DriverAdapter.DriverViewHolder>(
- AsyncDifferConfig.Builder(DiffCallback()).build()
+ AbstractSingleSelectionList<Driver, DriverAdapter.DriverViewHolder>(
+ driverViewModel.driverList.value
) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): DriverViewHolder {
- val binding =
- CardDriverOptionBinding.inflate(LayoutInflater.from(parent.context), parent, false)
- return DriverViewHolder(binding)
- }
-
- override fun getItemCount(): Int = currentList.size
-
- override fun onBindViewHolder(holder: DriverViewHolder, position: Int) =
- holder.bind(currentList[position])
-
- private fun onSelectDriver(position: Int) {
- driverViewModel.setSelectedDriverIndex(position)
- notifyItemChanged(driverViewModel.previouslySelectedDriver)
- notifyItemChanged(driverViewModel.selectedDriver)
- }
-
- private fun onDeleteDriver(driverData: Pair<String, GpuDriverMetadata>, position: Int) {
- if (driverViewModel.selectedDriver > position) {
- driverViewModel.setSelectedDriverIndex(driverViewModel.selectedDriver - 1)
- }
- if (GpuDriverHelper.customDriverSettingData == driverData.second) {
- driverViewModel.setSelectedDriverIndex(0)
- }
- driverViewModel.driversToDelete.add(driverData.first)
- driverViewModel.removeDriver(driverData)
- notifyItemRemoved(position)
- notifyItemChanged(driverViewModel.selectedDriver)
+ CardDriverOptionBinding.inflate(LayoutInflater.from(parent.context), parent, false)
+ .also { return DriverViewHolder(it) }
}
inner class DriverViewHolder(val binding: CardDriverOptionBinding) :
- RecyclerView.ViewHolder(binding.root) {
- private lateinit var driverData: Pair<String, GpuDriverMetadata>
-
- fun bind(driverData: Pair<String, GpuDriverMetadata>) {
- this.driverData = driverData
- val driver = driverData.second
-
+ AbstractViewHolder<Driver>(binding) {
+ override fun bind(model: Driver) {
binding.apply {
- radioButton.isChecked = driverViewModel.selectedDriver == bindingAdapterPosition
+ radioButton.isChecked = model.selected
root.setOnClickListener {
- onSelectDriver(bindingAdapterPosition)
+ selectItem(bindingAdapterPosition) {
+ driverViewModel.onDriverSelected(it)
+ driverViewModel.showClearButton(!StringSetting.DRIVER_PATH.global)
+ }
}
buttonDelete.setOnClickListener {
- onDeleteDriver(driverData, bindingAdapterPosition)
+ removeSelectableItem(
+ bindingAdapterPosition
+ ) { removedPosition: Int, selectedPosition: Int ->
+ driverViewModel.onDriverRemoved(removedPosition, selectedPosition)
+ driverViewModel.showClearButton(!StringSetting.DRIVER_PATH.global)
+ }
}
// Delay marquee by 3s
@@ -80,38 +54,19 @@ class DriverAdapter(private val driverViewModel: DriverViewModel) :
},
3000
)
- if (driver.name == null) {
- title.setText(R.string.system_gpu_driver)
- description.text = ""
- version.text = ""
- version.visibility = View.GONE
- description.visibility = View.GONE
- buttonDelete.visibility = View.GONE
- } else {
- title.text = driver.name
- version.text = driver.version
- description.text = driver.description
+ title.text = model.title
+ version.text = model.version
+ description.text = model.description
+ if (model.description.isNotEmpty()) {
version.visibility = View.VISIBLE
description.visibility = View.VISIBLE
buttonDelete.visibility = View.VISIBLE
+ } else {
+ version.visibility = View.GONE
+ description.visibility = View.GONE
+ buttonDelete.visibility = View.GONE
}
}
}
}
-
- private class DiffCallback : DiffUtil.ItemCallback<Pair<String, GpuDriverMetadata>>() {
- override fun areItemsTheSame(
- oldItem: Pair<String, GpuDriverMetadata>,
- newItem: Pair<String, GpuDriverMetadata>
- ): Boolean {
- return oldItem.first == newItem.first
- }
-
- override fun areContentsTheSame(
- oldItem: Pair<String, GpuDriverMetadata>,
- newItem: Pair<String, GpuDriverMetadata>
- ): Boolean {
- return oldItem.second == newItem.second
- }
- }
}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/FolderAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/FolderAdapter.kt
index ab657a7b9..3d8f0bda8 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/FolderAdapter.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/FolderAdapter.kt
@@ -8,19 +8,14 @@ import android.text.TextUtils
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.fragment.app.FragmentActivity
-import androidx.recyclerview.widget.AsyncDifferConfig
-import androidx.recyclerview.widget.DiffUtil
-import androidx.recyclerview.widget.ListAdapter
-import androidx.recyclerview.widget.RecyclerView
import org.yuzu.yuzu_emu.databinding.CardFolderBinding
import org.yuzu.yuzu_emu.fragments.GameFolderPropertiesDialogFragment
import org.yuzu.yuzu_emu.model.GameDir
import org.yuzu.yuzu_emu.model.GamesViewModel
+import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder
class FolderAdapter(val activity: FragmentActivity, val gamesViewModel: GamesViewModel) :
- ListAdapter<GameDir, FolderAdapter.FolderViewHolder>(
- AsyncDifferConfig.Builder(DiffCallback()).build()
- ) {
+ AbstractDiffAdapter<GameDir, FolderAdapter.FolderViewHolder>() {
override fun onCreateViewHolder(
parent: ViewGroup,
viewType: Int
@@ -29,18 +24,11 @@ class FolderAdapter(val activity: FragmentActivity, val gamesViewModel: GamesVie
.also { return FolderViewHolder(it) }
}
- override fun onBindViewHolder(holder: FolderAdapter.FolderViewHolder, position: Int) =
- holder.bind(currentList[position])
-
inner class FolderViewHolder(val binding: CardFolderBinding) :
- RecyclerView.ViewHolder(binding.root) {
- private lateinit var gameDir: GameDir
-
- fun bind(gameDir: GameDir) {
- this.gameDir = gameDir
-
+ AbstractViewHolder<GameDir>(binding) {
+ override fun bind(model: GameDir) {
binding.apply {
- path.text = Uri.parse(gameDir.uriString).path
+ path.text = Uri.parse(model.uriString).path
path.postDelayed(
{
path.isSelected = true
@@ -50,7 +38,7 @@ class FolderAdapter(val activity: FragmentActivity, val gamesViewModel: GamesVie
)
buttonEdit.setOnClickListener {
- GameFolderPropertiesDialogFragment.newInstance(this@FolderViewHolder.gameDir)
+ GameFolderPropertiesDialogFragment.newInstance(model)
.show(
activity.supportFragmentManager,
GameFolderPropertiesDialogFragment.TAG
@@ -58,19 +46,9 @@ class FolderAdapter(val activity: FragmentActivity, val gamesViewModel: GamesVie
}
buttonDelete.setOnClickListener {
- gamesViewModel.removeFolder(this@FolderViewHolder.gameDir)
+ gamesViewModel.removeFolder(model)
}
}
}
}
-
- private class DiffCallback : DiffUtil.ItemCallback<GameDir>() {
- override fun areItemsTheSame(oldItem: GameDir, newItem: GameDir): Boolean {
- return oldItem == newItem
- }
-
- override fun areContentsTheSame(oldItem: GameDir, newItem: GameDir): Boolean {
- return oldItem == newItem
- }
- }
}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt
index a578f0de8..e26c2e0ab 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt
@@ -9,7 +9,6 @@ import android.graphics.drawable.LayerDrawable
import android.net.Uri
import android.text.TextUtils
import android.view.LayoutInflater
-import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.Toast
@@ -25,10 +24,6 @@ import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope
import androidx.navigation.findNavController
import androidx.preference.PreferenceManager
-import androidx.recyclerview.widget.AsyncDifferConfig
-import androidx.recyclerview.widget.DiffUtil
-import androidx.recyclerview.widget.ListAdapter
-import androidx.recyclerview.widget.RecyclerView
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
@@ -36,122 +31,26 @@ import org.yuzu.yuzu_emu.HomeNavigationDirections
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.YuzuApplication
import org.yuzu.yuzu_emu.activities.EmulationActivity
-import org.yuzu.yuzu_emu.adapters.GameAdapter.GameViewHolder
import org.yuzu.yuzu_emu.databinding.CardGameBinding
import org.yuzu.yuzu_emu.model.Game
import org.yuzu.yuzu_emu.model.GamesViewModel
import org.yuzu.yuzu_emu.utils.GameIconUtils
+import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder
class GameAdapter(private val activity: AppCompatActivity) :
- ListAdapter<Game, GameViewHolder>(AsyncDifferConfig.Builder(DiffCallback()).build()),
- View.OnClickListener,
- View.OnLongClickListener {
+ AbstractDiffAdapter<Game, GameAdapter.GameViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): GameViewHolder {
- // Create a new view.
- val binding = CardGameBinding.inflate(LayoutInflater.from(parent.context), parent, false)
- binding.cardGame.setOnClickListener(this)
- binding.cardGame.setOnLongClickListener(this)
-
- // Use that view to create a ViewHolder.
- return GameViewHolder(binding)
- }
-
- override fun onBindViewHolder(holder: GameViewHolder, position: Int) =
- holder.bind(currentList[position])
-
- override fun getItemCount(): Int = currentList.size
-
- /**
- * Launches the game that was clicked on.
- *
- * @param view The card representing the game the user wants to play.
- */
- override fun onClick(view: View) {
- val holder = view.tag as GameViewHolder
-
- val gameExists = DocumentFile.fromSingleUri(
- YuzuApplication.appContext,
- Uri.parse(holder.game.path)
- )?.exists() == true
- if (!gameExists) {
- Toast.makeText(
- YuzuApplication.appContext,
- R.string.loader_error_file_not_found,
- Toast.LENGTH_LONG
- ).show()
-
- ViewModelProvider(activity)[GamesViewModel::class.java].reloadGames(true)
- return
- }
-
- val preferences = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
- preferences.edit()
- .putLong(
- holder.game.keyLastPlayedTime,
- System.currentTimeMillis()
- )
- .apply()
-
- val openIntent = Intent(YuzuApplication.appContext, EmulationActivity::class.java).apply {
- action = Intent.ACTION_VIEW
- data = Uri.parse(holder.game.path)
- }
-
- activity.lifecycleScope.launch {
- withContext(Dispatchers.IO) {
- val layerDrawable = ResourcesCompat.getDrawable(
- YuzuApplication.appContext.resources,
- R.drawable.shortcut,
- null
- ) as LayerDrawable
- layerDrawable.setDrawableByLayerId(
- R.id.shortcut_foreground,
- GameIconUtils.getGameIcon(activity, holder.game)
- .toDrawable(YuzuApplication.appContext.resources)
- )
- val inset = YuzuApplication.appContext.resources
- .getDimensionPixelSize(R.dimen.icon_inset)
- layerDrawable.setLayerInset(1, inset, inset, inset, inset)
- val shortcut =
- ShortcutInfoCompat.Builder(YuzuApplication.appContext, holder.game.path)
- .setShortLabel(holder.game.title)
- .setIcon(
- IconCompat.createWithAdaptiveBitmap(
- layerDrawable.toBitmap(config = Bitmap.Config.ARGB_8888)
- )
- )
- .setIntent(openIntent)
- .build()
- ShortcutManagerCompat.pushDynamicShortcut(YuzuApplication.appContext, shortcut)
- }
- }
-
- val action = HomeNavigationDirections.actionGlobalEmulationActivity(holder.game, true)
- view.findNavController().navigate(action)
- }
-
- override fun onLongClick(view: View): Boolean {
- val holder = view.tag as GameViewHolder
- val action = HomeNavigationDirections.actionGlobalPerGamePropertiesFragment(holder.game)
- view.findNavController().navigate(action)
- return true
+ CardGameBinding.inflate(LayoutInflater.from(parent.context), parent, false)
+ .also { return GameViewHolder(it) }
}
inner class GameViewHolder(val binding: CardGameBinding) :
- RecyclerView.ViewHolder(binding.root) {
- lateinit var game: Game
-
- init {
- binding.cardGame.tag = this
- }
-
- fun bind(game: Game) {
- this.game = game
-
+ AbstractViewHolder<Game>(binding) {
+ override fun bind(model: Game) {
binding.imageGameScreen.scaleType = ImageView.ScaleType.CENTER_CROP
- GameIconUtils.loadGameIcon(game, binding.imageGameScreen)
+ GameIconUtils.loadGameIcon(model, binding.imageGameScreen)
- binding.textGameTitle.text = game.title.replace("[\\t\\n\\r]+".toRegex(), " ")
+ binding.textGameTitle.text = model.title.replace("[\\t\\n\\r]+".toRegex(), " ")
binding.textGameTitle.postDelayed(
{
@@ -160,16 +59,79 @@ class GameAdapter(private val activity: AppCompatActivity) :
},
3000
)
+
+ binding.cardGame.setOnClickListener { onClick(model) }
+ binding.cardGame.setOnLongClickListener { onLongClick(model) }
}
- }
- private class DiffCallback : DiffUtil.ItemCallback<Game>() {
- override fun areItemsTheSame(oldItem: Game, newItem: Game): Boolean {
- return oldItem == newItem
+ fun onClick(game: Game) {
+ val gameExists = DocumentFile.fromSingleUri(
+ YuzuApplication.appContext,
+ Uri.parse(game.path)
+ )?.exists() == true
+ if (!gameExists) {
+ Toast.makeText(
+ YuzuApplication.appContext,
+ R.string.loader_error_file_not_found,
+ Toast.LENGTH_LONG
+ ).show()
+
+ ViewModelProvider(activity)[GamesViewModel::class.java].reloadGames(true)
+ return
+ }
+
+ val preferences =
+ PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
+ preferences.edit()
+ .putLong(
+ game.keyLastPlayedTime,
+ System.currentTimeMillis()
+ )
+ .apply()
+
+ val openIntent =
+ Intent(YuzuApplication.appContext, EmulationActivity::class.java).apply {
+ action = Intent.ACTION_VIEW
+ data = Uri.parse(game.path)
+ }
+
+ activity.lifecycleScope.launch {
+ withContext(Dispatchers.IO) {
+ val layerDrawable = ResourcesCompat.getDrawable(
+ YuzuApplication.appContext.resources,
+ R.drawable.shortcut,
+ null
+ ) as LayerDrawable
+ layerDrawable.setDrawableByLayerId(
+ R.id.shortcut_foreground,
+ GameIconUtils.getGameIcon(activity, game)
+ .toDrawable(YuzuApplication.appContext.resources)
+ )
+ val inset = YuzuApplication.appContext.resources
+ .getDimensionPixelSize(R.dimen.icon_inset)
+ layerDrawable.setLayerInset(1, inset, inset, inset, inset)
+ val shortcut =
+ ShortcutInfoCompat.Builder(YuzuApplication.appContext, game.path)
+ .setShortLabel(game.title)
+ .setIcon(
+ IconCompat.createWithAdaptiveBitmap(
+ layerDrawable.toBitmap(config = Bitmap.Config.ARGB_8888)
+ )
+ )
+ .setIntent(openIntent)
+ .build()
+ ShortcutManagerCompat.pushDynamicShortcut(YuzuApplication.appContext, shortcut)
+ }
+ }
+
+ val action = HomeNavigationDirections.actionGlobalEmulationActivity(game, true)
+ binding.root.findNavController().navigate(action)
}
- override fun areContentsTheSame(oldItem: Game, newItem: Game): Boolean {
- return oldItem == newItem
+ fun onLongClick(game: Game): Boolean {
+ val action = HomeNavigationDirections.actionGlobalPerGamePropertiesFragment(game)
+ binding.root.findNavController().navigate(action)
+ return true
}
}
}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GamePropertiesAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GamePropertiesAdapter.kt
index 95841d786..0046d5314 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GamePropertiesAdapter.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GamePropertiesAdapter.kt
@@ -12,23 +12,22 @@ import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
-import androidx.recyclerview.widget.RecyclerView
import kotlinx.coroutines.launch
import org.yuzu.yuzu_emu.databinding.CardInstallableIconBinding
import org.yuzu.yuzu_emu.databinding.CardSimpleOutlinedBinding
import org.yuzu.yuzu_emu.model.GameProperty
import org.yuzu.yuzu_emu.model.InstallableProperty
import org.yuzu.yuzu_emu.model.SubmenuProperty
+import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder
class GamePropertiesAdapter(
private val viewLifecycle: LifecycleOwner,
private var properties: List<GameProperty>
-) :
- RecyclerView.Adapter<GamePropertiesAdapter.GamePropertyViewHolder>() {
+) : AbstractListAdapter<GameProperty, AbstractViewHolder<GameProperty>>(properties) {
override fun onCreateViewHolder(
parent: ViewGroup,
viewType: Int
- ): GamePropertyViewHolder {
+ ): AbstractViewHolder<GameProperty> {
val inflater = LayoutInflater.from(parent.context)
return when (viewType) {
PropertyType.Submenu.ordinal -> {
@@ -51,11 +50,6 @@ class GamePropertiesAdapter(
}
}
- override fun getItemCount(): Int = properties.size
-
- override fun onBindViewHolder(holder: GamePropertyViewHolder, position: Int) =
- holder.bind(properties[position])
-
override fun getItemViewType(position: Int): Int {
return when (properties[position]) {
is SubmenuProperty -> PropertyType.Submenu.ordinal
@@ -63,14 +57,10 @@ class GamePropertiesAdapter(
}
}
- sealed class GamePropertyViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
- abstract fun bind(property: GameProperty)
- }
-
inner class SubmenuPropertyViewHolder(val binding: CardSimpleOutlinedBinding) :
- GamePropertyViewHolder(binding.root) {
- override fun bind(property: GameProperty) {
- val submenuProperty = property as SubmenuProperty
+ AbstractViewHolder<GameProperty>(binding) {
+ override fun bind(model: GameProperty) {
+ val submenuProperty = model as SubmenuProperty
binding.root.setOnClickListener {
submenuProperty.action.invoke()
@@ -108,9 +98,9 @@ class GamePropertiesAdapter(
}
inner class InstallablePropertyViewHolder(val binding: CardInstallableIconBinding) :
- GamePropertyViewHolder(binding.root) {
- override fun bind(property: GameProperty) {
- val installableProperty = property as InstallableProperty
+ AbstractViewHolder<GameProperty>(binding) {
+ override fun bind(model: GameProperty) {
+ val installableProperty = model as InstallableProperty
binding.title.setText(installableProperty.titleId)
binding.description.setText(installableProperty.descriptionId)
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/HomeSettingAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/HomeSettingAdapter.kt
index 58ce343f4..b512845d5 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/HomeSettingAdapter.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/HomeSettingAdapter.kt
@@ -14,69 +14,37 @@ import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
-import androidx.recyclerview.widget.RecyclerView
import kotlinx.coroutines.launch
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.databinding.CardHomeOptionBinding
import org.yuzu.yuzu_emu.fragments.MessageDialogFragment
import org.yuzu.yuzu_emu.model.HomeSetting
+import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder
class HomeSettingAdapter(
private val activity: AppCompatActivity,
private val viewLifecycle: LifecycleOwner,
- var options: List<HomeSetting>
-) :
- RecyclerView.Adapter<HomeSettingAdapter.HomeOptionViewHolder>(),
- View.OnClickListener {
+ options: List<HomeSetting>
+) : AbstractListAdapter<HomeSetting, HomeSettingAdapter.HomeOptionViewHolder>(options) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): HomeOptionViewHolder {
- val binding =
- CardHomeOptionBinding.inflate(LayoutInflater.from(parent.context), parent, false)
- binding.root.setOnClickListener(this)
- return HomeOptionViewHolder(binding)
- }
-
- override fun getItemCount(): Int {
- return options.size
- }
-
- override fun onBindViewHolder(holder: HomeOptionViewHolder, position: Int) {
- holder.bind(options[position])
- }
-
- override fun onClick(view: View) {
- val holder = view.tag as HomeOptionViewHolder
- if (holder.option.isEnabled.invoke()) {
- holder.option.onClick.invoke()
- } else {
- MessageDialogFragment.newInstance(
- activity,
- titleId = holder.option.disabledTitleId,
- descriptionId = holder.option.disabledMessageId
- ).show(activity.supportFragmentManager, MessageDialogFragment.TAG)
- }
+ CardHomeOptionBinding.inflate(LayoutInflater.from(parent.context), parent, false)
+ .also { return HomeOptionViewHolder(it) }
}
inner class HomeOptionViewHolder(val binding: CardHomeOptionBinding) :
- RecyclerView.ViewHolder(binding.root) {
- lateinit var option: HomeSetting
-
- init {
- itemView.tag = this
- }
-
- fun bind(option: HomeSetting) {
- this.option = option
- binding.optionTitle.text = activity.resources.getString(option.titleId)
- binding.optionDescription.text = activity.resources.getString(option.descriptionId)
+ AbstractViewHolder<HomeSetting>(binding) {
+ override fun bind(model: HomeSetting) {
+ binding.optionTitle.text = activity.resources.getString(model.titleId)
+ binding.optionDescription.text = activity.resources.getString(model.descriptionId)
binding.optionIcon.setImageDrawable(
ResourcesCompat.getDrawable(
activity.resources,
- option.iconId,
+ model.iconId,
activity.theme
)
)
- when (option.titleId) {
+ when (model.titleId) {
R.string.get_early_access ->
binding.optionLayout.background =
ContextCompat.getDrawable(
@@ -85,7 +53,7 @@ class HomeSettingAdapter(
)
}
- if (!option.isEnabled.invoke()) {
+ if (!model.isEnabled.invoke()) {
binding.optionTitle.alpha = 0.5f
binding.optionDescription.alpha = 0.5f
binding.optionIcon.alpha = 0.5f
@@ -93,7 +61,7 @@ class HomeSettingAdapter(
viewLifecycle.lifecycleScope.launch {
viewLifecycle.repeatOnLifecycle(Lifecycle.State.CREATED) {
- option.details.collect { updateOptionDetails(it) }
+ model.details.collect { updateOptionDetails(it) }
}
}
binding.optionDetail.postDelayed(
@@ -103,6 +71,20 @@ class HomeSettingAdapter(
},
3000
)
+
+ binding.root.setOnClickListener { onClick(model) }
+ }
+
+ private fun onClick(model: HomeSetting) {
+ if (model.isEnabled.invoke()) {
+ model.onClick.invoke()
+ } else {
+ MessageDialogFragment.newInstance(
+ activity,
+ titleId = model.disabledTitleId,
+ descriptionId = model.disabledMessageId
+ ).show(activity.supportFragmentManager, MessageDialogFragment.TAG)
+ }
}
private fun updateOptionDetails(detailString: String) {
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/InstallableAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/InstallableAdapter.kt
index e960fbaab..4218c4e52 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/InstallableAdapter.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/InstallableAdapter.kt
@@ -6,43 +6,33 @@ package org.yuzu.yuzu_emu.adapters
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
-import androidx.recyclerview.widget.RecyclerView
import org.yuzu.yuzu_emu.databinding.CardInstallableBinding
import org.yuzu.yuzu_emu.model.Installable
+import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder
-class InstallableAdapter(private val installables: List<Installable>) :
- RecyclerView.Adapter<InstallableAdapter.InstallableViewHolder>() {
+class InstallableAdapter(installables: List<Installable>) :
+ AbstractListAdapter<Installable, InstallableAdapter.InstallableViewHolder>(installables) {
override fun onCreateViewHolder(
parent: ViewGroup,
viewType: Int
): InstallableAdapter.InstallableViewHolder {
- val binding =
- CardInstallableBinding.inflate(LayoutInflater.from(parent.context), parent, false)
- return InstallableViewHolder(binding)
+ CardInstallableBinding.inflate(LayoutInflater.from(parent.context), parent, false)
+ .also { return InstallableViewHolder(it) }
}
- override fun getItemCount(): Int = installables.size
-
- override fun onBindViewHolder(holder: InstallableAdapter.InstallableViewHolder, position: Int) =
- holder.bind(installables[position])
-
inner class InstallableViewHolder(val binding: CardInstallableBinding) :
- RecyclerView.ViewHolder(binding.root) {
- lateinit var installable: Installable
-
- fun bind(installable: Installable) {
- this.installable = installable
-
- binding.title.setText(installable.titleId)
- binding.description.setText(installable.descriptionId)
+ AbstractViewHolder<Installable>(binding) {
+ override fun bind(model: Installable) {
+ binding.title.setText(model.titleId)
+ binding.description.setText(model.descriptionId)
- if (installable.install != null) {
+ if (model.install != null) {
binding.buttonInstall.visibility = View.VISIBLE
- binding.buttonInstall.setOnClickListener { installable.install.invoke() }
+ binding.buttonInstall.setOnClickListener { model.install.invoke() }
}
- if (installable.export != null) {
+ if (model.export != null) {
binding.buttonExport.visibility = View.VISIBLE
- binding.buttonExport.setOnClickListener { installable.export.invoke() }
+ binding.buttonExport.setOnClickListener { model.export.invoke() }
}
}
}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/LicenseAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/LicenseAdapter.kt
index bc6ff1364..38bb1f96f 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/LicenseAdapter.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/LicenseAdapter.kt
@@ -7,49 +7,33 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.app.AppCompatActivity
-import androidx.recyclerview.widget.RecyclerView
-import androidx.recyclerview.widget.RecyclerView.ViewHolder
-import org.yuzu.yuzu_emu.YuzuApplication
import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding
import org.yuzu.yuzu_emu.fragments.LicenseBottomSheetDialogFragment
import org.yuzu.yuzu_emu.model.License
+import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder
-class LicenseAdapter(private val activity: AppCompatActivity, var licenses: List<License>) :
- RecyclerView.Adapter<LicenseAdapter.LicenseViewHolder>(),
- View.OnClickListener {
+class LicenseAdapter(private val activity: AppCompatActivity, licenses: List<License>) :
+ AbstractListAdapter<License, LicenseAdapter.LicenseViewHolder>(licenses) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): LicenseViewHolder {
- val binding =
- ListItemSettingBinding.inflate(LayoutInflater.from(parent.context), parent, false)
- binding.root.setOnClickListener(this)
- return LicenseViewHolder(binding)
+ ListItemSettingBinding.inflate(LayoutInflater.from(parent.context), parent, false)
+ .also { return LicenseViewHolder(it) }
}
- override fun getItemCount(): Int = licenses.size
+ inner class LicenseViewHolder(val binding: ListItemSettingBinding) :
+ AbstractViewHolder<License>(binding) {
+ override fun bind(model: License) {
+ binding.apply {
+ textSettingName.text = root.context.getString(model.titleId)
+ textSettingDescription.text = root.context.getString(model.descriptionId)
+ textSettingValue.visibility = View.GONE
- override fun onBindViewHolder(holder: LicenseViewHolder, position: Int) {
- holder.bind(licenses[position])
- }
-
- override fun onClick(view: View) {
- val license = (view.tag as LicenseViewHolder).license
- LicenseBottomSheetDialogFragment.newInstance(license)
- .show(activity.supportFragmentManager, LicenseBottomSheetDialogFragment.TAG)
- }
-
- inner class LicenseViewHolder(val binding: ListItemSettingBinding) : ViewHolder(binding.root) {
- lateinit var license: License
-
- init {
- itemView.tag = this
+ root.setOnClickListener { onClick(model) }
+ }
}
- fun bind(license: License) {
- this.license = license
-
- val context = YuzuApplication.appContext
- binding.textSettingName.text = context.getString(license.titleId)
- binding.textSettingDescription.text = context.getString(license.descriptionId)
- binding.textSettingValue.visibility = View.GONE
+ private fun onClick(license: License) {
+ LicenseBottomSheetDialogFragment.newInstance(license)
+ .show(activity.supportFragmentManager, LicenseBottomSheetDialogFragment.TAG)
}
}
}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/SetupAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/SetupAdapter.kt
index 6b46d359e..02118e1a8 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/SetupAdapter.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/SetupAdapter.kt
@@ -10,7 +10,6 @@ import android.view.ViewGroup
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.res.ResourcesCompat
import androidx.lifecycle.ViewModelProvider
-import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.button.MaterialButton
import org.yuzu.yuzu_emu.databinding.PageSetupBinding
import org.yuzu.yuzu_emu.model.HomeViewModel
@@ -18,31 +17,19 @@ import org.yuzu.yuzu_emu.model.SetupCallback
import org.yuzu.yuzu_emu.model.SetupPage
import org.yuzu.yuzu_emu.model.StepState
import org.yuzu.yuzu_emu.utils.ViewUtils
+import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder
-class SetupAdapter(val activity: AppCompatActivity, val pages: List<SetupPage>) :
- RecyclerView.Adapter<SetupAdapter.SetupPageViewHolder>() {
+class SetupAdapter(val activity: AppCompatActivity, pages: List<SetupPage>) :
+ AbstractListAdapter<SetupPage, SetupAdapter.SetupPageViewHolder>(pages) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SetupPageViewHolder {
- val binding = PageSetupBinding.inflate(LayoutInflater.from(parent.context), parent, false)
- return SetupPageViewHolder(binding)
+ PageSetupBinding.inflate(LayoutInflater.from(parent.context), parent, false)
+ .also { return SetupPageViewHolder(it) }
}
- override fun getItemCount(): Int = pages.size
-
- override fun onBindViewHolder(holder: SetupPageViewHolder, position: Int) =
- holder.bind(pages[position])
-
inner class SetupPageViewHolder(val binding: PageSetupBinding) :
- RecyclerView.ViewHolder(binding.root), SetupCallback {
- lateinit var page: SetupPage
-
- init {
- itemView.tag = this
- }
-
- fun bind(page: SetupPage) {
- this.page = page
-
- if (page.stepCompleted.invoke() == StepState.COMPLETE) {
+ AbstractViewHolder<SetupPage>(binding), SetupCallback {
+ override fun bind(model: SetupPage) {
+ if (model.stepCompleted.invoke() == StepState.COMPLETE) {
binding.buttonAction.visibility = View.INVISIBLE
binding.textConfirmation.visibility = View.VISIBLE
}
@@ -50,31 +37,31 @@ class SetupAdapter(val activity: AppCompatActivity, val pages: List<SetupPage>)
binding.icon.setImageDrawable(
ResourcesCompat.getDrawable(
activity.resources,
- page.iconId,
+ model.iconId,
activity.theme
)
)
- binding.textTitle.text = activity.resources.getString(page.titleId)
+ binding.textTitle.text = activity.resources.getString(model.titleId)
binding.textDescription.text =
- Html.fromHtml(activity.resources.getString(page.descriptionId), 0)
+ Html.fromHtml(activity.resources.getString(model.descriptionId), 0)
binding.buttonAction.apply {
- text = activity.resources.getString(page.buttonTextId)
- if (page.buttonIconId != 0) {
+ text = activity.resources.getString(model.buttonTextId)
+ if (model.buttonIconId != 0) {
icon = ResourcesCompat.getDrawable(
activity.resources,
- page.buttonIconId,
+ model.buttonIconId,
activity.theme
)
}
iconGravity =
- if (page.leftAlignedIcon) {
+ if (model.leftAlignedIcon) {
MaterialButton.ICON_GRAVITY_START
} else {
MaterialButton.ICON_GRAVITY_END
}
setOnClickListener {
- page.buttonAction.invoke(this@SetupPageViewHolder)
+ model.buttonAction.invoke(this@SetupPageViewHolder)
}
}
}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AboutFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AboutFragment.kt
index a1620fbb7..5b5f800c1 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AboutFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AboutFragment.kt
@@ -76,8 +76,8 @@ class AboutFragment : Fragment() {
binding.root.findNavController().navigate(R.id.action_aboutFragment_to_licensesFragment)
}
- binding.textBuildHash.text = BuildConfig.GIT_HASH
- binding.buttonBuildHash.setOnClickListener {
+ binding.textVersionName.text = BuildConfig.VERSION_NAME
+ binding.textVersionName.setOnClickListener {
val clipBoard =
requireContext().getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
val clip = ClipData.newPlainText(getString(R.string.build), BuildConfig.GIT_HASH)
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/DriverManagerFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/DriverManagerFragment.kt
index cc71254dc..9dabb9c41 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/DriverManagerFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/DriverManagerFragment.kt
@@ -3,6 +3,7 @@
package org.yuzu.yuzu_emu.fragments
+import android.annotation.SuppressLint
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
@@ -13,20 +14,26 @@ import androidx.core.view.WindowInsetsCompat
import androidx.core.view.updatePadding
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
+import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
+import androidx.lifecycle.repeatOnLifecycle
import androidx.navigation.findNavController
import androidx.navigation.fragment.navArgs
import androidx.recyclerview.widget.GridLayoutManager
import com.google.android.material.transition.MaterialSharedAxis
-import kotlinx.coroutines.flow.collectLatest
+import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.adapters.DriverAdapter
import org.yuzu.yuzu_emu.databinding.FragmentDriverManagerBinding
+import org.yuzu.yuzu_emu.features.settings.model.StringSetting
+import org.yuzu.yuzu_emu.model.Driver.Companion.toDriver
import org.yuzu.yuzu_emu.model.DriverViewModel
import org.yuzu.yuzu_emu.model.HomeViewModel
import org.yuzu.yuzu_emu.utils.FileUtil
import org.yuzu.yuzu_emu.utils.GpuDriverHelper
+import org.yuzu.yuzu_emu.utils.NativeConfig
import java.io.File
import java.io.IOException
@@ -55,12 +62,43 @@ class DriverManagerFragment : Fragment() {
return binding.root
}
+ // This is using the correct scope, lint is just acting up
+ @SuppressLint("UnsafeRepeatOnLifecycleDetector")
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
homeViewModel.setNavigationVisibility(visible = false, animated = true)
homeViewModel.setStatusBarShadeVisibility(visible = false)
driverViewModel.onOpenDriverManager(args.game)
+ if (NativeConfig.isPerGameConfigLoaded()) {
+ binding.toolbarDrivers.inflateMenu(R.menu.menu_driver_manager)
+ driverViewModel.showClearButton(!StringSetting.DRIVER_PATH.global)
+ binding.toolbarDrivers.setOnMenuItemClickListener {
+ when (it.itemId) {
+ R.id.menu_driver_clear -> {
+ StringSetting.DRIVER_PATH.global = true
+ driverViewModel.updateDriverList()
+ (binding.listDrivers.adapter as DriverAdapter)
+ .replaceList(driverViewModel.driverList.value)
+ driverViewModel.showClearButton(false)
+ true
+ }
+
+ else -> false
+ }
+ }
+
+ viewLifecycleOwner.lifecycleScope.apply {
+ launch {
+ repeatOnLifecycle(Lifecycle.State.STARTED) {
+ driverViewModel.showClearButton.collect {
+ binding.toolbarDrivers.menu
+ .findItem(R.id.menu_driver_clear).isVisible = it
+ }
+ }
+ }
+ }
+ }
if (!driverViewModel.isInteractionAllowed.value) {
DriversLoadingDialogFragment().show(
@@ -85,25 +123,6 @@ class DriverManagerFragment : Fragment() {
adapter = DriverAdapter(driverViewModel)
}
- viewLifecycleOwner.lifecycleScope.apply {
- launch {
- driverViewModel.driverList.collectLatest {
- (binding.listDrivers.adapter as DriverAdapter).submitList(it)
- }
- }
- launch {
- driverViewModel.newDriverInstalled.collect {
- if (_binding != null && it) {
- (binding.listDrivers.adapter as DriverAdapter).apply {
- notifyItemChanged(driverViewModel.previouslySelectedDriver)
- notifyItemChanged(driverViewModel.selectedDriver)
- driverViewModel.setNewDriverInstalled(false)
- }
- }
- }
- }
- }
-
setInsets()
}
@@ -160,7 +179,7 @@ class DriverManagerFragment : Fragment() {
false
) {
val driverPath =
- "${GpuDriverHelper.driverStoragePath}/${FileUtil.getFilename(result)}"
+ "${GpuDriverHelper.driverStoragePath}${FileUtil.getFilename(result)}"
val driverFile = File(driverPath)
// Ignore file exceptions when a user selects an invalid zip
@@ -177,12 +196,21 @@ class DriverManagerFragment : Fragment() {
val driverData = GpuDriverHelper.getMetadataFromZip(driverFile)
val driverInList =
- driverViewModel.driverList.value.firstOrNull { it.second == driverData }
+ driverViewModel.driverData.firstOrNull { it.second == driverData }
if (driverInList != null) {
return@newInstance getString(R.string.driver_already_installed)
} else {
- driverViewModel.addDriver(Pair(driverPath, driverData))
- driverViewModel.setNewDriverInstalled(true)
+ driverViewModel.onDriverAdded(Pair(driverPath, driverData))
+ withContext(Dispatchers.Main) {
+ if (_binding != null) {
+ val adapter = binding.listDrivers.adapter as DriverAdapter
+ adapter.addItem(driverData.toDriver())
+ adapter.selectItem(adapter.currentList.indices.last)
+ driverViewModel.showClearButton(!StringSetting.DRIVER_PATH.global)
+ binding.listDrivers
+ .smoothScrollToPosition(adapter.currentList.indices.last)
+ }
+ }
}
return@newInstance Any()
}.show(childFragmentManager, IndeterminateProgressDialogFragment.TAG)
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/Driver.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/Driver.kt
new file mode 100644
index 000000000..de342212a
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/Driver.kt
@@ -0,0 +1,27 @@
+// SPDX-FileCopyrightText: 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+package org.yuzu.yuzu_emu.model
+
+import org.yuzu.yuzu_emu.utils.GpuDriverMetadata
+
+data class Driver(
+ override var selected: Boolean,
+ val title: String,
+ val version: String = "",
+ val description: String = ""
+) : SelectableItem {
+ override fun onSelectionStateChanged(selected: Boolean) {
+ this.selected = selected
+ }
+
+ companion object {
+ fun GpuDriverMetadata.toDriver(selected: Boolean = false): Driver =
+ Driver(
+ selected,
+ this.name ?: "",
+ this.version ?: "",
+ this.description ?: ""
+ )
+ }
+}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/DriverViewModel.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/DriverViewModel.kt
index 76accf8f3..15ae3a42b 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/DriverViewModel.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/DriverViewModel.kt
@@ -9,6 +9,7 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
@@ -17,11 +18,10 @@ import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.YuzuApplication
import org.yuzu.yuzu_emu.features.settings.model.StringSetting
import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile
-import org.yuzu.yuzu_emu.utils.FileUtil
+import org.yuzu.yuzu_emu.model.Driver.Companion.toDriver
import org.yuzu.yuzu_emu.utils.GpuDriverHelper
import org.yuzu.yuzu_emu.utils.GpuDriverMetadata
import org.yuzu.yuzu_emu.utils.NativeConfig
-import java.io.BufferedOutputStream
import java.io.File
class DriverViewModel : ViewModel() {
@@ -38,97 +38,81 @@ class DriverViewModel : ViewModel() {
!loading && ready && !deleting
}.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), initialValue = false)
- private val _driverList = MutableStateFlow(GpuDriverHelper.getDrivers())
- val driverList: StateFlow<MutableList<Pair<String, GpuDriverMetadata>>> get() = _driverList
+ var driverData = GpuDriverHelper.getDrivers()
- var previouslySelectedDriver = 0
- var selectedDriver = -1
+ private val _driverList = MutableStateFlow(emptyList<Driver>())
+ val driverList: StateFlow<List<Driver>> get() = _driverList
// Used for showing which driver is currently installed within the driver manager card
private val _selectedDriverTitle = MutableStateFlow("")
val selectedDriverTitle: StateFlow<String> get() = _selectedDriverTitle
- private val _newDriverInstalled = MutableStateFlow(false)
- val newDriverInstalled: StateFlow<Boolean> get() = _newDriverInstalled
+ private val _showClearButton = MutableStateFlow(false)
+ val showClearButton = _showClearButton.asStateFlow()
- val driversToDelete = mutableListOf<String>()
+ private val driversToDelete = mutableListOf<String>()
init {
- val currentDriverMetadata = GpuDriverHelper.installedCustomDriverData
- findSelectedDriver(currentDriverMetadata)
-
- // If a user had installed a driver before the manager was implemented, this zips
- // the installed driver to UserData/gpu_drivers/CustomDriver.zip so that it can
- // be indexed and exported as expected.
- if (selectedDriver == -1) {
- val driverToSave =
- File(GpuDriverHelper.driverStoragePath, "CustomDriver.zip")
- driverToSave.createNewFile()
- FileUtil.zipFromInternalStorage(
- File(GpuDriverHelper.driverInstallationPath!!),
- GpuDriverHelper.driverInstallationPath!!,
- BufferedOutputStream(driverToSave.outputStream())
- )
- _driverList.value.add(Pair(driverToSave.path, currentDriverMetadata))
- setSelectedDriverIndex(_driverList.value.size - 1)
- }
+ updateDriverList()
+ updateDriverNameForGame(null)
+ }
- // If a user had installed a driver before the config was reworked to be multiplatform,
- // we have save the path of the previously selected driver to the new setting.
- if (StringSetting.DRIVER_PATH.getString(true).isEmpty() && selectedDriver > 0 &&
- StringSetting.DRIVER_PATH.global
- ) {
- StringSetting.DRIVER_PATH.setString(_driverList.value[selectedDriver].first)
- NativeConfig.saveGlobalConfig()
- } else {
- findSelectedDriver(GpuDriverHelper.customDriverSettingData)
+ fun reloadDriverData() {
+ _areDriversLoading.value = true
+ driverData = GpuDriverHelper.getDrivers()
+ updateDriverList()
+ _areDriversLoading.value = false
+ }
+
+ fun updateDriverList() {
+ val selectedDriver = GpuDriverHelper.customDriverSettingData
+ val newDriverList = mutableListOf(
+ Driver(
+ selectedDriver == GpuDriverMetadata(),
+ YuzuApplication.appContext.getString(R.string.system_gpu_driver)
+ )
+ )
+ driverData.forEach {
+ newDriverList.add(it.second.toDriver(it.second == selectedDriver))
}
- updateDriverNameForGame(null)
+ _driverList.value = newDriverList
}
- fun setSelectedDriverIndex(value: Int) {
- if (selectedDriver != -1) {
- previouslySelectedDriver = selectedDriver
+ fun onOpenDriverManager(game: Game?) {
+ if (game != null) {
+ SettingsFile.loadCustomConfig(game)
}
- selectedDriver = value
+ updateDriverList()
}
- fun setNewDriverInstalled(value: Boolean) {
- _newDriverInstalled.value = value
+ fun showClearButton(value: Boolean) {
+ _showClearButton.value = value
}
- fun addDriver(driverData: Pair<String, GpuDriverMetadata>) {
- val driverIndex = _driverList.value.indexOfFirst { it == driverData }
- if (driverIndex == -1) {
- _driverList.value.add(driverData)
- setSelectedDriverIndex(_driverList.value.size - 1)
- _selectedDriverTitle.value = driverData.second.name
- ?: YuzuApplication.appContext.getString(R.string.system_gpu_driver)
+ fun onDriverSelected(position: Int) {
+ if (position == 0) {
+ StringSetting.DRIVER_PATH.setString("")
} else {
- setSelectedDriverIndex(driverIndex)
+ StringSetting.DRIVER_PATH.setString(driverData[position - 1].first)
}
}
- fun removeDriver(driverData: Pair<String, GpuDriverMetadata>) {
- _driverList.value.remove(driverData)
+ fun onDriverRemoved(removedPosition: Int, selectedPosition: Int) {
+ driversToDelete.add(driverData[removedPosition - 1].first)
+ driverData.removeAt(removedPosition - 1)
+ onDriverSelected(selectedPosition)
}
- fun onOpenDriverManager(game: Game?) {
- if (game != null) {
- SettingsFile.loadCustomConfig(game)
- }
-
- val driverPath = StringSetting.DRIVER_PATH.getString()
- if (driverPath.isEmpty()) {
- setSelectedDriverIndex(0)
- } else {
- findSelectedDriver(GpuDriverHelper.getMetadataFromZip(File(driverPath)))
+ fun onDriverAdded(driver: Pair<String, GpuDriverMetadata>) {
+ if (driversToDelete.contains(driver.first)) {
+ driversToDelete.remove(driver.first)
}
+ driverData.add(driver)
+ onDriverSelected(driverData.size)
}
fun onCloseDriverManager(game: Game?) {
_isDeletingDrivers.value = true
- StringSetting.DRIVER_PATH.setString(driverList.value[selectedDriver].first)
updateDriverNameForGame(game)
if (game == null) {
NativeConfig.saveGlobalConfig()
@@ -181,20 +165,6 @@ class DriverViewModel : ViewModel() {
}
}
- private fun findSelectedDriver(currentDriverMetadata: GpuDriverMetadata) {
- if (driverList.value.size == 1) {
- setSelectedDriverIndex(0)
- return
- }
-
- driverList.value.forEachIndexed { i: Int, driver: Pair<String, GpuDriverMetadata> ->
- if (driver.second == currentDriverMetadata) {
- setSelectedDriverIndex(i)
- return
- }
- }
- }
-
fun updateDriverNameForGame(game: Game?) {
if (!GpuDriverHelper.supportsCustomDriverLoading()) {
return
@@ -217,7 +187,6 @@ class DriverViewModel : ViewModel() {
private fun setDriverReady() {
_isDriverReady.value = true
- _selectedDriverTitle.value = GpuDriverHelper.customDriverSettingData.name
- ?: YuzuApplication.appContext.getString(R.string.system_gpu_driver)
+ updateName()
}
}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/SelectableItem.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/SelectableItem.kt
new file mode 100644
index 000000000..11c22d323
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/SelectableItem.kt
@@ -0,0 +1,9 @@
+// SPDX-FileCopyrightText: 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+package org.yuzu.yuzu_emu.model
+
+interface SelectableItem {
+ var selected: Boolean
+ fun onSelectionStateChanged(selected: Boolean)
+}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt
index 622ae996e..644289e25 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt
@@ -41,6 +41,7 @@ import org.yuzu.yuzu_emu.fragments.AddGameFolderDialogFragment
import org.yuzu.yuzu_emu.fragments.IndeterminateProgressDialogFragment
import org.yuzu.yuzu_emu.fragments.MessageDialogFragment
import org.yuzu.yuzu_emu.model.AddonViewModel
+import org.yuzu.yuzu_emu.model.DriverViewModel
import org.yuzu.yuzu_emu.model.GamesViewModel
import org.yuzu.yuzu_emu.model.HomeViewModel
import org.yuzu.yuzu_emu.model.TaskState
@@ -58,6 +59,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
private val gamesViewModel: GamesViewModel by viewModels()
private val taskViewModel: TaskViewModel by viewModels()
private val addonViewModel: AddonViewModel by viewModels()
+ private val driverViewModel: DriverViewModel by viewModels()
override var themeId: Int = 0
@@ -689,6 +691,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
NativeLibrary.initializeSystem(true)
NativeConfig.initializeGlobalConfig()
gamesViewModel.reloadGames(false)
+ driverViewModel.reloadDriverData()
return@newInstance getString(R.string.user_data_import_success)
}.show(supportFragmentManager, IndeterminateProgressDialogFragment.TAG)
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GpuDriverHelper.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GpuDriverHelper.kt
index 685272288..a8f9dcc34 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GpuDriverHelper.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GpuDriverHelper.kt
@@ -62,9 +62,6 @@ object GpuDriverHelper {
?.sortedByDescending { it: Pair<String, GpuDriverMetadata> -> it.second.name }
?.distinct()
?.toMutableList() ?: mutableListOf()
-
- // TODO: Get system driver information
- drivers.add(0, Pair("", GpuDriverMetadata()))
return drivers
}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/viewholder/AbstractViewHolder.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/viewholder/AbstractViewHolder.kt
new file mode 100644
index 000000000..7101ad434
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/viewholder/AbstractViewHolder.kt
@@ -0,0 +1,18 @@
+// SPDX-FileCopyrightText: 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+package org.yuzu.yuzu_emu.viewholder
+
+import androidx.recyclerview.widget.RecyclerView
+import androidx.viewbinding.ViewBinding
+import org.yuzu.yuzu_emu.adapters.AbstractDiffAdapter
+import org.yuzu.yuzu_emu.adapters.AbstractListAdapter
+
+/**
+ * [RecyclerView.ViewHolder] meant to work together with a [AbstractDiffAdapter] or a
+ * [AbstractListAdapter] so we can run [bind] on each list item without needing a manual hookup.
+ */
+abstract class AbstractViewHolder<Model>(binding: ViewBinding) :
+ RecyclerView.ViewHolder(binding.root) {
+ abstract fun bind(model: Model)
+}
diff --git a/src/android/app/src/main/jni/native.cpp b/src/android/app/src/main/jni/native.cpp
index 8017eb58d..ed3b1353a 100644
--- a/src/android/app/src/main/jni/native.cpp
+++ b/src/android/app/src/main/jni/native.cpp
@@ -410,8 +410,8 @@ void EmulationSession::OnGamepadConnectEvent([[maybe_unused]] int index) {
jauto handheld = m_system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Handheld);
if (controller->GetNpadStyleIndex() == Core::HID::NpadStyleIndex::Handheld) {
- handheld->SetNpadStyleIndex(Core::HID::NpadStyleIndex::ProController);
- controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::ProController);
+ handheld->SetNpadStyleIndex(Core::HID::NpadStyleIndex::Fullkey);
+ controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::Fullkey);
handheld->Disconnect();
}
}
diff --git a/src/android/app/src/main/res/layout-w600dp/fragment_about.xml b/src/android/app/src/main/res/layout-w600dp/fragment_about.xml
index a26ffbc73..655e49219 100644
--- a/src/android/app/src/main/res/layout-w600dp/fragment_about.xml
+++ b/src/android/app/src/main/res/layout-w600dp/fragment_about.xml
@@ -147,7 +147,7 @@
android:layout_marginHorizontal="20dp" />
<LinearLayout
- android:id="@+id/button_build_hash"
+ android:id="@+id/button_version_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/selectableItemBackground"
@@ -164,7 +164,7 @@
android:textAlignment="viewStart" />
<com.google.android.material.textview.MaterialTextView
- android:id="@+id/text_build_hash"
+ android:id="@+id/text_version_name"
style="@style/TextAppearance.Material3.BodyMedium"
android:layout_width="match_parent"
android:layout_height="wrap_content"
diff --git a/src/android/app/src/main/res/layout/fragment_about.xml b/src/android/app/src/main/res/layout/fragment_about.xml
index a24f5230e..38090fa50 100644
--- a/src/android/app/src/main/res/layout/fragment_about.xml
+++ b/src/android/app/src/main/res/layout/fragment_about.xml
@@ -148,7 +148,7 @@
android:layout_marginHorizontal="20dp" />
<LinearLayout
- android:id="@+id/button_build_hash"
+ android:id="@+id/button_version_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingVertical="16dp"
@@ -165,7 +165,7 @@
android:text="@string/build" />
<com.google.android.material.textview.MaterialTextView
- android:id="@+id/text_build_hash"
+ android:id="@+id/text_version_name"
style="@style/TextAppearance.Material3.BodyMedium"
android:layout_width="match_parent"
android:layout_height="wrap_content"
diff --git a/src/android/app/src/main/res/menu/menu_driver_manager.xml b/src/android/app/src/main/res/menu/menu_driver_manager.xml
new file mode 100644
index 000000000..dee5d57b6
--- /dev/null
+++ b/src/android/app/src/main/res/menu/menu_driver_manager.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<menu xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto">
+
+ <item
+ android:id="@+id/menu_driver_clear"
+ android:icon="@drawable/ic_clear"
+ android:title="@string/clear"
+ app:showAsAction="always" />
+
+</menu>
diff --git a/src/common/page_table.cpp b/src/common/page_table.cpp
index 166dc3dce..85dc18c11 100644
--- a/src/common/page_table.cpp
+++ b/src/common/page_table.cpp
@@ -2,6 +2,7 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/page_table.h"
+#include "common/scope_exit.h"
namespace Common {
@@ -11,29 +12,10 @@ PageTable::~PageTable() noexcept = default;
bool PageTable::BeginTraversal(TraversalEntry* out_entry, TraversalContext* out_context,
Common::ProcessAddress address) const {
- // Setup invalid defaults.
- out_entry->phys_addr = 0;
- out_entry->block_size = page_size;
- out_context->next_page = 0;
-
- // Validate that we can read the actual entry.
- const auto page = address / page_size;
- if (page >= backing_addr.size()) {
- return false;
- }
-
- // Validate that the entry is mapped.
- const auto phys_addr = backing_addr[page];
- if (phys_addr == 0) {
- return false;
- }
+ out_context->next_offset = GetInteger(address);
+ out_context->next_page = address / page_size;
- // Populate the results.
- out_entry->phys_addr = phys_addr + GetInteger(address);
- out_context->next_page = page + 1;
- out_context->next_offset = GetInteger(address) + page_size;
-
- return true;
+ return this->ContinueTraversal(out_entry, out_context);
}
bool PageTable::ContinueTraversal(TraversalEntry* out_entry, TraversalContext* context) const {
@@ -41,6 +23,12 @@ bool PageTable::ContinueTraversal(TraversalEntry* out_entry, TraversalContext* c
out_entry->phys_addr = 0;
out_entry->block_size = page_size;
+ // Regardless of whether the page was mapped, advance on exit.
+ SCOPE_EXIT({
+ context->next_page += 1;
+ context->next_offset += page_size;
+ });
+
// Validate that we can read the actual entry.
const auto page = context->next_page;
if (page >= backing_addr.size()) {
@@ -55,8 +43,6 @@ bool PageTable::ContinueTraversal(TraversalEntry* out_entry, TraversalContext* c
// Populate the results.
out_entry->phys_addr = phys_addr + context->next_offset;
- context->next_page = page + 1;
- context->next_offset += page_size;
return true;
}
diff --git a/src/core/cpu_manager.h b/src/core/cpu_manager.h
index 0deea9c58..a249dc5f7 100644
--- a/src/core/cpu_manager.h
+++ b/src/core/cpu_manager.h
@@ -64,7 +64,7 @@ public:
return [this] { ShutdownThreadFunction(); };
}
- void PreemptSingleCore(bool from_running_enviroment = true);
+ void PreemptSingleCore(bool from_running_environment = true);
std::size_t CurrentCore() const {
return current_core.load();
diff --git a/src/core/debugger/debugger.cpp b/src/core/debugger/debugger.cpp
index 0e270eb50..e86aae846 100644
--- a/src/core/debugger/debugger.cpp
+++ b/src/core/debugger/debugger.cpp
@@ -114,7 +114,7 @@ public:
}
Kernel::KThread* GetActiveThread() override {
- return state->active_thread;
+ return state->active_thread.GetPointerUnsafe();
}
private:
@@ -147,11 +147,14 @@ private:
std::scoped_lock lk{connection_lock};
+ // Find the process we are going to debug.
+ SetDebugProcess();
+
// Ensure everything is stopped.
PauseEmulation();
// Set up the new frontend.
- frontend = std::make_unique<GDBStub>(*this, system);
+ frontend = std::make_unique<GDBStub>(*this, system, debug_process.GetPointerUnsafe());
// Set the new state. This will tear down any existing state.
state = ConnectionState{
@@ -194,15 +197,20 @@ private:
UpdateActiveThread();
if (state->info.type == SignalType::Watchpoint) {
- frontend->Watchpoint(state->active_thread, *state->info.watchpoint);
+ frontend->Watchpoint(std::addressof(*state->active_thread),
+ *state->info.watchpoint);
} else {
- frontend->Stopped(state->active_thread);
+ frontend->Stopped(std::addressof(*state->active_thread));
}
break;
case SignalType::ShuttingDown:
frontend->ShuttingDown();
+ // Release members.
+ state->active_thread.Reset(nullptr);
+ debug_process.Reset(nullptr);
+
// Wait for emulation to shut down gracefully now.
state->signal_pipe.close();
state->client_socket.shutdown(boost::asio::socket_base::shutdown_both);
@@ -222,7 +230,7 @@ private:
stopped = true;
PauseEmulation();
UpdateActiveThread();
- frontend->Stopped(state->active_thread);
+ frontend->Stopped(state->active_thread.GetPointerUnsafe());
break;
}
case DebuggerAction::Continue:
@@ -232,7 +240,7 @@ private:
MarkResumed([&] {
state->active_thread->SetStepState(Kernel::StepState::StepPending);
state->active_thread->Resume(Kernel::SuspendType::Debug);
- ResumeEmulation(state->active_thread);
+ ResumeEmulation(state->active_thread.GetPointerUnsafe());
});
break;
case DebuggerAction::StepThreadLocked: {
@@ -255,6 +263,7 @@ private:
}
void PauseEmulation() {
+ Kernel::KScopedLightLock ll{debug_process->GetListLock()};
Kernel::KScopedSchedulerLock sl{system.Kernel()};
// Put all threads to sleep on next scheduler round.
@@ -264,6 +273,9 @@ private:
}
void ResumeEmulation(Kernel::KThread* except = nullptr) {
+ Kernel::KScopedLightLock ll{debug_process->GetListLock()};
+ Kernel::KScopedSchedulerLock sl{system.Kernel()};
+
// Wake up all threads.
for (auto& thread : ThreadList()) {
if (std::addressof(thread) == except) {
@@ -277,15 +289,16 @@ private:
template <typename Callback>
void MarkResumed(Callback&& cb) {
- Kernel::KScopedSchedulerLock sl{system.Kernel()};
stopped = false;
cb();
}
void UpdateActiveThread() {
+ Kernel::KScopedLightLock ll{debug_process->GetListLock()};
+
auto& threads{ThreadList()};
for (auto& thread : threads) {
- if (std::addressof(thread) == state->active_thread) {
+ if (std::addressof(thread) == state->active_thread.GetPointerUnsafe()) {
// Thread is still alive, no need to update.
return;
}
@@ -293,12 +306,18 @@ private:
state->active_thread = std::addressof(threads.front());
}
+private:
+ void SetDebugProcess() {
+ debug_process = std::move(system.Kernel().GetProcessList().back());
+ }
+
Kernel::KProcess::ThreadList& ThreadList() {
- return system.ApplicationProcess()->GetThreadList();
+ return debug_process->GetThreadList();
}
private:
System& system;
+ Kernel::KScopedAutoObject<Kernel::KProcess> debug_process;
std::unique_ptr<DebuggerFrontend> frontend;
boost::asio::io_context io_context;
@@ -310,7 +329,7 @@ private:
boost::process::async_pipe signal_pipe;
SignalInfo info;
- Kernel::KThread* active_thread;
+ Kernel::KScopedAutoObject<Kernel::KThread> active_thread;
std::array<u8, 4096> client_data;
bool pipe_data;
};
diff --git a/src/core/debugger/gdbstub.cpp b/src/core/debugger/gdbstub.cpp
index 66e46c4ba..80091cc7e 100644
--- a/src/core/debugger/gdbstub.cpp
+++ b/src/core/debugger/gdbstub.cpp
@@ -108,9 +108,9 @@ static std::string EscapeXML(std::string_view data) {
return escaped;
}
-GDBStub::GDBStub(DebuggerBackend& backend_, Core::System& system_)
- : DebuggerFrontend(backend_), system{system_} {
- if (system.ApplicationProcess()->Is64Bit()) {
+GDBStub::GDBStub(DebuggerBackend& backend_, Core::System& system_, Kernel::KProcess* debug_process_)
+ : DebuggerFrontend(backend_), system{system_}, debug_process{debug_process_} {
+ if (GetProcess()->Is64Bit()) {
arch = std::make_unique<GDBStubA64>();
} else {
arch = std::make_unique<GDBStubA32>();
@@ -276,7 +276,7 @@ void GDBStub::ExecuteCommand(std::string_view packet, std::vector<DebuggerAction
const size_t size{static_cast<size_t>(strtoll(command.data() + sep, nullptr, 16))};
std::vector<u8> mem(size);
- if (system.ApplicationMemory().ReadBlock(addr, mem.data(), size)) {
+ if (GetMemory().ReadBlock(addr, mem.data(), size)) {
// Restore any bytes belonging to replaced instructions.
auto it = replaced_instructions.lower_bound(addr);
for (; it != replaced_instructions.end() && it->first < addr + size; it++) {
@@ -310,8 +310,8 @@ void GDBStub::ExecuteCommand(std::string_view packet, std::vector<DebuggerAction
const auto mem_substr{std::string_view(command).substr(mem_sep)};
const auto mem{Common::HexStringToVector(mem_substr, false)};
- if (system.ApplicationMemory().WriteBlock(addr, mem.data(), size)) {
- Core::InvalidateInstructionCacheRange(system.ApplicationProcess(), addr, size);
+ if (GetMemory().WriteBlock(addr, mem.data(), size)) {
+ Core::InvalidateInstructionCacheRange(GetProcess(), addr, size);
SendReply(GDB_STUB_REPLY_OK);
} else {
SendReply(GDB_STUB_REPLY_ERR);
@@ -353,7 +353,7 @@ void GDBStub::HandleBreakpointInsert(std::string_view command) {
const size_t addr{static_cast<size_t>(strtoll(command.data() + addr_sep, nullptr, 16))};
const size_t size{static_cast<size_t>(strtoll(command.data() + size_sep, nullptr, 16))};
- if (!system.ApplicationMemory().IsValidVirtualAddressRange(addr, size)) {
+ if (!GetMemory().IsValidVirtualAddressRange(addr, size)) {
SendReply(GDB_STUB_REPLY_ERR);
return;
}
@@ -362,22 +362,20 @@ void GDBStub::HandleBreakpointInsert(std::string_view command) {
switch (type) {
case BreakpointType::Software:
- replaced_instructions[addr] = system.ApplicationMemory().Read32(addr);
- system.ApplicationMemory().Write32(addr, arch->BreakpointInstruction());
- Core::InvalidateInstructionCacheRange(system.ApplicationProcess(), addr, sizeof(u32));
+ replaced_instructions[addr] = GetMemory().Read32(addr);
+ GetMemory().Write32(addr, arch->BreakpointInstruction());
+ Core::InvalidateInstructionCacheRange(GetProcess(), addr, sizeof(u32));
success = true;
break;
case BreakpointType::WriteWatch:
- success = system.ApplicationProcess()->InsertWatchpoint(addr, size,
- Kernel::DebugWatchpointType::Write);
+ success = GetProcess()->InsertWatchpoint(addr, size, Kernel::DebugWatchpointType::Write);
break;
case BreakpointType::ReadWatch:
- success = system.ApplicationProcess()->InsertWatchpoint(addr, size,
- Kernel::DebugWatchpointType::Read);
+ success = GetProcess()->InsertWatchpoint(addr, size, Kernel::DebugWatchpointType::Read);
break;
case BreakpointType::AccessWatch:
- success = system.ApplicationProcess()->InsertWatchpoint(
- addr, size, Kernel::DebugWatchpointType::ReadOrWrite);
+ success =
+ GetProcess()->InsertWatchpoint(addr, size, Kernel::DebugWatchpointType::ReadOrWrite);
break;
case BreakpointType::Hardware:
default:
@@ -400,7 +398,7 @@ void GDBStub::HandleBreakpointRemove(std::string_view command) {
const size_t addr{static_cast<size_t>(strtoll(command.data() + addr_sep, nullptr, 16))};
const size_t size{static_cast<size_t>(strtoll(command.data() + size_sep, nullptr, 16))};
- if (!system.ApplicationMemory().IsValidVirtualAddressRange(addr, size)) {
+ if (!GetMemory().IsValidVirtualAddressRange(addr, size)) {
SendReply(GDB_STUB_REPLY_ERR);
return;
}
@@ -411,24 +409,22 @@ void GDBStub::HandleBreakpointRemove(std::string_view command) {
case BreakpointType::Software: {
const auto orig_insn{replaced_instructions.find(addr)};
if (orig_insn != replaced_instructions.end()) {
- system.ApplicationMemory().Write32(addr, orig_insn->second);
- Core::InvalidateInstructionCacheRange(system.ApplicationProcess(), addr, sizeof(u32));
+ GetMemory().Write32(addr, orig_insn->second);
+ Core::InvalidateInstructionCacheRange(GetProcess(), addr, sizeof(u32));
replaced_instructions.erase(addr);
success = true;
}
break;
}
case BreakpointType::WriteWatch:
- success = system.ApplicationProcess()->RemoveWatchpoint(addr, size,
- Kernel::DebugWatchpointType::Write);
+ success = GetProcess()->RemoveWatchpoint(addr, size, Kernel::DebugWatchpointType::Write);
break;
case BreakpointType::ReadWatch:
- success = system.ApplicationProcess()->RemoveWatchpoint(addr, size,
- Kernel::DebugWatchpointType::Read);
+ success = GetProcess()->RemoveWatchpoint(addr, size, Kernel::DebugWatchpointType::Read);
break;
case BreakpointType::AccessWatch:
- success = system.ApplicationProcess()->RemoveWatchpoint(
- addr, size, Kernel::DebugWatchpointType::ReadOrWrite);
+ success =
+ GetProcess()->RemoveWatchpoint(addr, size, Kernel::DebugWatchpointType::ReadOrWrite);
break;
case BreakpointType::Hardware:
default:
@@ -466,10 +462,10 @@ void GDBStub::HandleQuery(std::string_view command) {
const auto target_xml{arch->GetTargetXML()};
SendReply(PaginateBuffer(target_xml, command.substr(30)));
} else if (command.starts_with("Offsets")) {
- const auto main_offset = Core::FindMainModuleEntrypoint(system.ApplicationProcess());
+ const auto main_offset = Core::FindMainModuleEntrypoint(GetProcess());
SendReply(fmt::format("TextSeg={:x}", GetInteger(main_offset)));
} else if (command.starts_with("Xfer:libraries:read::")) {
- auto modules = Core::FindModules(system.ApplicationProcess());
+ auto modules = Core::FindModules(GetProcess());
std::string buffer;
buffer += R"(<?xml version="1.0"?>)";
@@ -483,7 +479,7 @@ void GDBStub::HandleQuery(std::string_view command) {
SendReply(PaginateBuffer(buffer, command.substr(21)));
} else if (command.starts_with("fThreadInfo")) {
// beginning of list
- const auto& threads = system.ApplicationProcess()->GetThreadList();
+ const auto& threads = GetProcess()->GetThreadList();
std::vector<std::string> thread_ids;
for (const auto& thread : threads) {
thread_ids.push_back(fmt::format("{:x}", thread.GetThreadId()));
@@ -497,7 +493,7 @@ void GDBStub::HandleQuery(std::string_view command) {
buffer += R"(<?xml version="1.0"?>)";
buffer += "<threads>";
- const auto& threads = system.ApplicationProcess()->GetThreadList();
+ const auto& threads = GetProcess()->GetThreadList();
for (const auto& thread : threads) {
auto thread_name{Core::GetThreadName(&thread)};
if (!thread_name) {
@@ -559,28 +555,28 @@ void GDBStub::HandleVCont(std::string_view command, std::vector<DebuggerAction>&
}
constexpr std::array<std::pair<const char*, Kernel::Svc::MemoryState>, 22> MemoryStateNames{{
- {"----- Free -----", Kernel::Svc::MemoryState::Free},
- {"Io ", Kernel::Svc::MemoryState::Io},
- {"Static ", Kernel::Svc::MemoryState::Static},
- {"Code ", Kernel::Svc::MemoryState::Code},
- {"CodeData ", Kernel::Svc::MemoryState::CodeData},
- {"Normal ", Kernel::Svc::MemoryState::Normal},
- {"Shared ", Kernel::Svc::MemoryState::Shared},
- {"AliasCode ", Kernel::Svc::MemoryState::AliasCode},
- {"AliasCodeData ", Kernel::Svc::MemoryState::AliasCodeData},
- {"Ipc ", Kernel::Svc::MemoryState::Ipc},
- {"Stack ", Kernel::Svc::MemoryState::Stack},
- {"ThreadLocal ", Kernel::Svc::MemoryState::ThreadLocal},
- {"Transfered ", Kernel::Svc::MemoryState::Transfered},
- {"SharedTransfered", Kernel::Svc::MemoryState::SharedTransfered},
- {"SharedCode ", Kernel::Svc::MemoryState::SharedCode},
- {"Inaccessible ", Kernel::Svc::MemoryState::Inaccessible},
- {"NonSecureIpc ", Kernel::Svc::MemoryState::NonSecureIpc},
- {"NonDeviceIpc ", Kernel::Svc::MemoryState::NonDeviceIpc},
- {"Kernel ", Kernel::Svc::MemoryState::Kernel},
- {"GeneratedCode ", Kernel::Svc::MemoryState::GeneratedCode},
- {"CodeOut ", Kernel::Svc::MemoryState::CodeOut},
- {"Coverage ", Kernel::Svc::MemoryState::Coverage},
+ {"----- Free ------", Kernel::Svc::MemoryState::Free},
+ {"Io ", Kernel::Svc::MemoryState::Io},
+ {"Static ", Kernel::Svc::MemoryState::Static},
+ {"Code ", Kernel::Svc::MemoryState::Code},
+ {"CodeData ", Kernel::Svc::MemoryState::CodeData},
+ {"Normal ", Kernel::Svc::MemoryState::Normal},
+ {"Shared ", Kernel::Svc::MemoryState::Shared},
+ {"AliasCode ", Kernel::Svc::MemoryState::AliasCode},
+ {"AliasCodeData ", Kernel::Svc::MemoryState::AliasCodeData},
+ {"Ipc ", Kernel::Svc::MemoryState::Ipc},
+ {"Stack ", Kernel::Svc::MemoryState::Stack},
+ {"ThreadLocal ", Kernel::Svc::MemoryState::ThreadLocal},
+ {"Transferred ", Kernel::Svc::MemoryState::Transferred},
+ {"SharedTransferred", Kernel::Svc::MemoryState::SharedTransferred},
+ {"SharedCode ", Kernel::Svc::MemoryState::SharedCode},
+ {"Inaccessible ", Kernel::Svc::MemoryState::Inaccessible},
+ {"NonSecureIpc ", Kernel::Svc::MemoryState::NonSecureIpc},
+ {"NonDeviceIpc ", Kernel::Svc::MemoryState::NonDeviceIpc},
+ {"Kernel ", Kernel::Svc::MemoryState::Kernel},
+ {"GeneratedCode ", Kernel::Svc::MemoryState::GeneratedCode},
+ {"CodeOut ", Kernel::Svc::MemoryState::CodeOut},
+ {"Coverage ", Kernel::Svc::MemoryState::Coverage},
}};
static constexpr const char* GetMemoryStateName(Kernel::Svc::MemoryState state) {
@@ -613,7 +609,7 @@ void GDBStub::HandleRcmd(const std::vector<u8>& command) {
std::string_view command_str{reinterpret_cast<const char*>(&command[0]), command.size()};
std::string reply;
- auto* process = system.ApplicationProcess();
+ auto* process = GetProcess();
auto& page_table = process->GetPageTable();
const char* commands = "Commands:\n"
@@ -714,7 +710,7 @@ void GDBStub::HandleRcmd(const std::vector<u8>& command) {
}
Kernel::KThread* GDBStub::GetThreadByID(u64 thread_id) {
- auto& threads{system.ApplicationProcess()->GetThreadList()};
+ auto& threads{GetProcess()->GetThreadList()};
for (auto& thread : threads) {
if (thread.GetThreadId() == thread_id) {
return std::addressof(thread);
@@ -783,4 +779,12 @@ void GDBStub::SendStatus(char status) {
backend.WriteToClient(buf);
}
+Kernel::KProcess* GDBStub::GetProcess() {
+ return debug_process;
+}
+
+Core::Memory::Memory& GDBStub::GetMemory() {
+ return GetProcess()->GetMemory();
+}
+
} // namespace Core
diff --git a/src/core/debugger/gdbstub.h b/src/core/debugger/gdbstub.h
index 368197920..232dcf49f 100644
--- a/src/core/debugger/gdbstub.h
+++ b/src/core/debugger/gdbstub.h
@@ -12,13 +12,22 @@
#include "core/debugger/debugger_interface.h"
#include "core/debugger/gdbstub_arch.h"
+namespace Kernel {
+class KProcess;
+}
+
+namespace Core::Memory {
+class Memory;
+}
+
namespace Core {
class System;
class GDBStub : public DebuggerFrontend {
public:
- explicit GDBStub(DebuggerBackend& backend, Core::System& system);
+ explicit GDBStub(DebuggerBackend& backend, Core::System& system,
+ Kernel::KProcess* debug_process);
~GDBStub() override;
void Connected() override;
@@ -42,8 +51,12 @@ private:
void SendReply(std::string_view data);
void SendStatus(char status);
+ Kernel::KProcess* GetProcess();
+ Core::Memory::Memory& GetMemory();
+
private:
Core::System& system;
+ Kernel::KProcess* debug_process;
std::unique_ptr<GDBStubArch> arch;
std::vector<char> current_command;
std::map<VAddr, u32> replaced_instructions;
diff --git a/src/core/frontend/applets/controller.cpp b/src/core/frontend/applets/controller.cpp
index 34fe23b6a..e04d884ba 100644
--- a/src/core/frontend/applets/controller.cpp
+++ b/src/core/frontend/applets/controller.cpp
@@ -47,7 +47,7 @@ void DefaultControllerApplet::ReconfigureControllers(ReconfigureCallback callbac
// Connect controllers based on the following priority list from highest to lowest priority:
// Pro Controller -> Dual Joycons -> Left Joycon/Right Joycon -> Handheld
if (parameters.allow_pro_controller) {
- controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::ProController);
+ controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::Fullkey);
controller->Connect(true);
} else if (parameters.allow_dual_joycons) {
controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::JoyconDual);
diff --git a/src/core/hle/kernel/k_memory_block.h b/src/core/hle/kernel/k_memory_block.h
index ef3f61321..d2b7e9a66 100644
--- a/src/core/hle/kernel/k_memory_block.h
+++ b/src/core/hle/kernel/k_memory_block.h
@@ -81,12 +81,12 @@ enum class KMemoryState : u32 {
ThreadLocal = static_cast<u32>(Svc::MemoryState::ThreadLocal) | FlagLinearMapped,
- Transfered = static_cast<u32>(Svc::MemoryState::Transfered) | FlagsMisc |
- FlagCanAlignedDeviceMap | FlagCanChangeAttribute | FlagCanUseIpc |
- FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc,
+ Transferred = static_cast<u32>(Svc::MemoryState::Transferred) | FlagsMisc |
+ FlagCanAlignedDeviceMap | FlagCanChangeAttribute | FlagCanUseIpc |
+ FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc,
- SharedTransfered = static_cast<u32>(Svc::MemoryState::SharedTransfered) | FlagsMisc |
- FlagCanAlignedDeviceMap | FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc,
+ SharedTransferred = static_cast<u32>(Svc::MemoryState::SharedTransferred) | FlagsMisc |
+ FlagCanAlignedDeviceMap | FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc,
SharedCode = static_cast<u32>(Svc::MemoryState::SharedCode) | FlagMapped |
FlagReferenceCounted | FlagLinearMapped | FlagCanUseNonSecureIpc |
@@ -130,8 +130,8 @@ static_assert(static_cast<u32>(KMemoryState::AliasCodeData) == 0x0FFFBD09);
static_assert(static_cast<u32>(KMemoryState::Ipc) == 0x045C3C0A);
static_assert(static_cast<u32>(KMemoryState::Stack) == 0x045C3C0B);
static_assert(static_cast<u32>(KMemoryState::ThreadLocal) == 0x0400000C);
-static_assert(static_cast<u32>(KMemoryState::Transfered) == 0x055C3C0D);
-static_assert(static_cast<u32>(KMemoryState::SharedTransfered) == 0x045C380E);
+static_assert(static_cast<u32>(KMemoryState::Transferred) == 0x055C3C0D);
+static_assert(static_cast<u32>(KMemoryState::SharedTransferred) == 0x045C380E);
static_assert(static_cast<u32>(KMemoryState::SharedCode) == 0x0440380F);
static_assert(static_cast<u32>(KMemoryState::Inaccessible) == 0x00000010);
static_assert(static_cast<u32>(KMemoryState::NonSecureIpc) == 0x045C3811);
diff --git a/src/core/hle/kernel/k_memory_block_manager.cpp b/src/core/hle/kernel/k_memory_block_manager.cpp
index 58a1e7216..f08a6e448 100644
--- a/src/core/hle/kernel/k_memory_block_manager.cpp
+++ b/src/core/hle/kernel/k_memory_block_manager.cpp
@@ -28,14 +28,14 @@ Result KMemoryBlockManager::Initialize(KProcessAddress st, KProcessAddress nd,
}
void KMemoryBlockManager::Finalize(KMemoryBlockSlabManager* slab_manager,
- HostUnmapCallback&& host_unmap_callback) {
+ BlockCallback&& block_callback) {
// Erase every block until we have none left.
auto it = m_memory_block_tree.begin();
while (it != m_memory_block_tree.end()) {
KMemoryBlock* block = std::addressof(*it);
it = m_memory_block_tree.erase(it);
+ block_callback(block->GetAddress(), block->GetSize());
slab_manager->Free(block);
- host_unmap_callback(block->GetAddress(), block->GetSize());
}
ASSERT(m_memory_block_tree.empty());
diff --git a/src/core/hle/kernel/k_memory_block_manager.h b/src/core/hle/kernel/k_memory_block_manager.h
index cb7b6f430..377628504 100644
--- a/src/core/hle/kernel/k_memory_block_manager.h
+++ b/src/core/hle/kernel/k_memory_block_manager.h
@@ -85,11 +85,11 @@ public:
public:
KMemoryBlockManager();
- using HostUnmapCallback = std::function<void(Common::ProcessAddress, u64)>;
+ using BlockCallback = std::function<void(Common::ProcessAddress, u64)>;
Result Initialize(KProcessAddress st, KProcessAddress nd,
KMemoryBlockSlabManager* slab_manager);
- void Finalize(KMemoryBlockSlabManager* slab_manager, HostUnmapCallback&& host_unmap_callback);
+ void Finalize(KMemoryBlockSlabManager* slab_manager, BlockCallback&& block_callback);
iterator end() {
return m_memory_block_tree.end();
diff --git a/src/core/hle/kernel/k_page_table_base.cpp b/src/core/hle/kernel/k_page_table_base.cpp
index 8c1549559..3f0a39d33 100644
--- a/src/core/hle/kernel/k_page_table_base.cpp
+++ b/src/core/hle/kernel/k_page_table_base.cpp
@@ -431,15 +431,43 @@ Result KPageTableBase::InitializeForProcess(Svc::CreateProcessFlag as_type, bool
m_memory_block_slab_manager));
}
+Result KPageTableBase::FinalizeProcess() {
+ // Only process tables should be finalized.
+ ASSERT(!this->IsKernel());
+
+ // NOTE: Here Nintendo calls an unknown OnFinalize function.
+ // this->OnFinalize();
+
+ // NOTE: Here Nintendo calls a second unknown OnFinalize function.
+ // this->OnFinalize2();
+
+ // NOTE: Here Nintendo does a page table walk to discover heap pages to free.
+ // We will use the block manager finalization below to free them.
+
+ R_SUCCEED();
+}
+
void KPageTableBase::Finalize() {
- auto HostUnmapCallback = [&](KProcessAddress addr, u64 size) {
- if (Settings::IsFastmemEnabled()) {
+ this->FinalizeProcess();
+
+ auto BlockCallback = [&](KProcessAddress addr, u64 size) {
+ if (m_impl->fastmem_arena) {
m_system.DeviceMemory().buffer.Unmap(GetInteger(addr), size, false);
}
+
+ // Get physical pages.
+ KPageGroup pg(m_kernel, m_block_info_manager);
+ this->MakePageGroup(pg, addr, size / PageSize);
+
+ // Free the pages.
+ pg.CloseAndReset();
};
// Finalize memory blocks.
- m_memory_block_manager.Finalize(m_memory_block_slab_manager, std::move(HostUnmapCallback));
+ {
+ KScopedLightLock lk(m_general_lock);
+ m_memory_block_manager.Finalize(m_memory_block_slab_manager, std::move(BlockCallback));
+ }
// Free any unsafe mapped memory.
if (m_mapped_unsafe_physical_memory) {
@@ -486,8 +514,8 @@ KProcessAddress KPageTableBase::GetRegionAddress(Svc::MemoryState state) const {
case Svc::MemoryState::Shared:
case Svc::MemoryState::AliasCode:
case Svc::MemoryState::AliasCodeData:
- case Svc::MemoryState::Transfered:
- case Svc::MemoryState::SharedTransfered:
+ case Svc::MemoryState::Transferred:
+ case Svc::MemoryState::SharedTransferred:
case Svc::MemoryState::SharedCode:
case Svc::MemoryState::GeneratedCode:
case Svc::MemoryState::CodeOut:
@@ -522,8 +550,8 @@ size_t KPageTableBase::GetRegionSize(Svc::MemoryState state) const {
case Svc::MemoryState::Shared:
case Svc::MemoryState::AliasCode:
case Svc::MemoryState::AliasCodeData:
- case Svc::MemoryState::Transfered:
- case Svc::MemoryState::SharedTransfered:
+ case Svc::MemoryState::Transferred:
+ case Svc::MemoryState::SharedTransferred:
case Svc::MemoryState::SharedCode:
case Svc::MemoryState::GeneratedCode:
case Svc::MemoryState::CodeOut:
@@ -564,8 +592,8 @@ bool KPageTableBase::CanContain(KProcessAddress addr, size_t size, Svc::MemorySt
case Svc::MemoryState::AliasCodeData:
case Svc::MemoryState::Stack:
case Svc::MemoryState::ThreadLocal:
- case Svc::MemoryState::Transfered:
- case Svc::MemoryState::SharedTransfered:
+ case Svc::MemoryState::Transferred:
+ case Svc::MemoryState::SharedTransferred:
case Svc::MemoryState::SharedCode:
case Svc::MemoryState::GeneratedCode:
case Svc::MemoryState::CodeOut:
diff --git a/src/core/hle/kernel/k_page_table_base.h b/src/core/hle/kernel/k_page_table_base.h
index 077cafc96..748419f86 100644
--- a/src/core/hle/kernel/k_page_table_base.h
+++ b/src/core/hle/kernel/k_page_table_base.h
@@ -241,6 +241,7 @@ public:
KResourceLimit* resource_limit, Core::Memory::Memory& memory,
KProcessAddress aslr_space_start);
+ Result FinalizeProcess();
void Finalize();
bool IsKernel() const {
diff --git a/src/core/hle/kernel/k_process.cpp b/src/core/hle/kernel/k_process.cpp
index 068e71dff..ae332a550 100644
--- a/src/core/hle/kernel/k_process.cpp
+++ b/src/core/hle/kernel/k_process.cpp
@@ -171,6 +171,12 @@ void KProcess::Finalize() {
m_resource_limit->Close();
}
+ // Clear expensive resources, as the destructor is not called for guest objects.
+ for (auto& interface : m_arm_interfaces) {
+ interface.reset();
+ }
+ m_exclusive_monitor.reset();
+
// Perform inherited finalization.
KSynchronizationObject::Finalize();
}
diff --git a/src/core/hle/kernel/k_transfer_memory.cpp b/src/core/hle/kernel/k_transfer_memory.cpp
index 0e2e11743..cbb1b02bb 100644
--- a/src/core/hle/kernel/k_transfer_memory.cpp
+++ b/src/core/hle/kernel/k_transfer_memory.cpp
@@ -76,8 +76,8 @@ Result KTransferMemory::Map(KProcessAddress address, size_t size, Svc::MemoryPer
// Map the memory.
const KMemoryState state = (m_owner_perm == Svc::MemoryPermission::None)
- ? KMemoryState::Transfered
- : KMemoryState::SharedTransfered;
+ ? KMemoryState::Transferred
+ : KMemoryState::SharedTransferred;
R_TRY(GetCurrentProcess(m_kernel).GetPageTable().MapPageGroup(
address, *m_page_group, state, KMemoryPermission::UserReadWrite));
@@ -96,8 +96,8 @@ Result KTransferMemory::Unmap(KProcessAddress address, size_t size) {
// Unmap the memory.
const KMemoryState state = (m_owner_perm == Svc::MemoryPermission::None)
- ? KMemoryState::Transfered
- : KMemoryState::SharedTransfered;
+ ? KMemoryState::Transferred
+ : KMemoryState::SharedTransferred;
R_TRY(GetCurrentProcess(m_kernel).GetPageTable().UnmapPageGroup(address, *m_page_group, state));
// Mark ourselves as unmapped.
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index 1030f0c12..f3683cdcc 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -112,7 +112,14 @@ struct KernelCore::Impl {
old_process->Close();
}
- process_list.clear();
+ {
+ std::scoped_lock lk{process_list_lock};
+ for (auto* const process : process_list) {
+ process->Terminate();
+ process->Close();
+ }
+ process_list.clear();
+ }
next_object_id = 0;
next_kernel_process_id = KProcess::InitialProcessIdMin;
@@ -770,6 +777,7 @@ struct KernelCore::Impl {
std::atomic<u64> next_thread_id{1};
// Lists all processes that exist in the current session.
+ std::mutex process_list_lock;
std::vector<KProcess*> process_list;
std::atomic<KProcess*> application_process{};
std::unique_ptr<Kernel::GlobalSchedulerContext> global_scheduler_context;
@@ -869,9 +877,19 @@ KResourceLimit* KernelCore::GetSystemResourceLimit() {
}
void KernelCore::AppendNewProcess(KProcess* process) {
+ process->Open();
+
+ std::scoped_lock lk{impl->process_list_lock};
impl->process_list.push_back(process);
}
+void KernelCore::RemoveProcess(KProcess* process) {
+ std::scoped_lock lk{impl->process_list_lock};
+ if (std::erase(impl->process_list, process)) {
+ process->Close();
+ }
+}
+
void KernelCore::MakeApplicationProcess(KProcess* process) {
impl->MakeApplicationProcess(process);
}
@@ -884,8 +902,15 @@ const KProcess* KernelCore::ApplicationProcess() const {
return impl->application_process;
}
-const std::vector<KProcess*>& KernelCore::GetProcessList() const {
- return impl->process_list;
+std::list<KScopedAutoObject<KProcess>> KernelCore::GetProcessList() {
+ std::list<KScopedAutoObject<KProcess>> processes;
+ std::scoped_lock lk{impl->process_list_lock};
+
+ for (auto* const process : impl->process_list) {
+ processes.emplace_back(process);
+ }
+
+ return processes;
}
Kernel::GlobalSchedulerContext& KernelCore::GlobalSchedulerContext() {
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h
index 5d4102145..8ea5bed1c 100644
--- a/src/core/hle/kernel/kernel.h
+++ b/src/core/hle/kernel/kernel.h
@@ -5,6 +5,7 @@
#include <array>
#include <functional>
+#include <list>
#include <memory>
#include <string>
#include <unordered_map>
@@ -116,8 +117,9 @@ public:
/// Retrieves a shared pointer to the system resource limit instance.
KResourceLimit* GetSystemResourceLimit();
- /// Adds the given shared pointer to an internal list of active processes.
+ /// Adds/removes the given pointer to an internal list of active processes.
void AppendNewProcess(KProcess* process);
+ void RemoveProcess(KProcess* process);
/// Makes the given process the new application process.
void MakeApplicationProcess(KProcess* process);
@@ -129,7 +131,7 @@ public:
const KProcess* ApplicationProcess() const;
/// Retrieves the list of processes.
- const std::vector<KProcess*>& GetProcessList() const;
+ std::list<KScopedAutoObject<KProcess>> GetProcessList();
/// Gets the sole instance of the global scheduler
Kernel::GlobalSchedulerContext& GlobalSchedulerContext();
diff --git a/src/core/hle/kernel/svc/svc_process.cpp b/src/core/hle/kernel/svc/svc_process.cpp
index caa8bee9a..5c3e8829f 100644
--- a/src/core/hle/kernel/svc/svc_process.cpp
+++ b/src/core/hle/kernel/svc/svc_process.cpp
@@ -74,13 +74,15 @@ Result GetProcessList(Core::System& system, s32* out_num_processes, u64 out_proc
}
auto& memory = GetCurrentMemory(kernel);
- const auto& process_list = kernel.GetProcessList();
+ auto process_list = kernel.GetProcessList();
+ auto it = process_list.begin();
+
const auto num_processes = process_list.size();
const auto copy_amount =
std::min(static_cast<std::size_t>(out_process_ids_size), num_processes);
- for (std::size_t i = 0; i < copy_amount; ++i) {
- memory.Write64(out_process_ids, process_list[i]->GetProcessId());
+ for (std::size_t i = 0; i < copy_amount && it != process_list.end(); ++i, ++it) {
+ memory.Write64(out_process_ids, (*it)->GetProcessId());
out_process_ids += sizeof(u64);
}
diff --git a/src/core/hle/kernel/svc/svc_transfer_memory.cpp b/src/core/hle/kernel/svc/svc_transfer_memory.cpp
index 1f97121b3..671bca23f 100644
--- a/src/core/hle/kernel/svc/svc_transfer_memory.cpp
+++ b/src/core/hle/kernel/svc/svc_transfer_memory.cpp
@@ -90,7 +90,7 @@ Result MapTransferMemory(Core::System& system, Handle trmem_handle, uint64_t add
// Verify that the mapping is in range.
R_UNLESS(GetCurrentProcess(system.Kernel())
.GetPageTable()
- .CanContain(address, size, KMemoryState::Transfered),
+ .CanContain(address, size, KMemoryState::Transferred),
ResultInvalidMemoryRegion);
// Map the transfer memory.
@@ -117,7 +117,7 @@ Result UnmapTransferMemory(Core::System& system, Handle trmem_handle, uint64_t a
// Verify that the mapping is in range.
R_UNLESS(GetCurrentProcess(system.Kernel())
.GetPageTable()
- .CanContain(address, size, KMemoryState::Transfered),
+ .CanContain(address, size, KMemoryState::Transferred),
ResultInvalidMemoryRegion);
// Unmap the transfer memory.
diff --git a/src/core/hle/kernel/svc_types.h b/src/core/hle/kernel/svc_types.h
index 50de02e36..ab432ea78 100644
--- a/src/core/hle/kernel/svc_types.h
+++ b/src/core/hle/kernel/svc_types.h
@@ -27,8 +27,8 @@ enum class MemoryState : u32 {
Ipc = 0x0A,
Stack = 0x0B,
ThreadLocal = 0x0C,
- Transfered = 0x0D,
- SharedTransfered = 0x0E,
+ Transferred = 0x0D,
+ SharedTransferred = 0x0E,
SharedCode = 0x0F,
Inaccessible = 0x10,
NonSecureIpc = 0x11,
diff --git a/src/core/hle/service/am/applets/applet_profile_select.h b/src/core/hle/service/am/applets/applet_profile_select.h
index 369f9250f..673eed516 100644
--- a/src/core/hle/service/am/applets/applet_profile_select.h
+++ b/src/core/hle/service/am/applets/applet_profile_select.h
@@ -76,7 +76,7 @@ struct UiSettingsDisplayOptions {
bool is_system_or_launcher;
bool is_registration_permitted;
bool show_skip_button;
- bool aditional_select;
+ bool additional_select;
bool show_user_selector;
bool is_unqualified_user_selectable;
};
diff --git a/src/core/hle/service/caps/caps_manager.cpp b/src/core/hle/service/caps/caps_manager.cpp
index 96b225d5f..261fc204c 100644
--- a/src/core/hle/service/caps/caps_manager.cpp
+++ b/src/core/hle/service/caps/caps_manager.cpp
@@ -85,7 +85,7 @@ Result AlbumManager::GetAlbumFileList(std::vector<AlbumEntry>& out_entries, Albu
}
Result AlbumManager::GetAlbumFileList(std::vector<ApplicationAlbumFileEntry>& out_entries,
- ContentType contex_type, s64 start_posix_time,
+ ContentType content_type, s64 start_posix_time,
s64 end_posix_time, u64 aruid) const {
if (!is_mounted) {
return ResultIsNotMounted;
@@ -94,7 +94,7 @@ Result AlbumManager::GetAlbumFileList(std::vector<ApplicationAlbumFileEntry>& ou
std::vector<ApplicationAlbumEntry> album_entries;
const auto start_date = ConvertToAlbumDateTime(start_posix_time);
const auto end_date = ConvertToAlbumDateTime(end_posix_time);
- const auto result = GetAlbumFileList(album_entries, contex_type, start_date, end_date, aruid);
+ const auto result = GetAlbumFileList(album_entries, content_type, start_date, end_date, aruid);
if (result.IsError()) {
return result;
@@ -113,14 +113,14 @@ Result AlbumManager::GetAlbumFileList(std::vector<ApplicationAlbumFileEntry>& ou
}
Result AlbumManager::GetAlbumFileList(std::vector<ApplicationAlbumEntry>& out_entries,
- ContentType contex_type, AlbumFileDateTime start_date,
+ ContentType content_type, AlbumFileDateTime start_date,
AlbumFileDateTime end_date, u64 aruid) const {
if (!is_mounted) {
return ResultIsNotMounted;
}
for (auto& [file_id, path] : album_files) {
- if (file_id.type != contex_type) {
+ if (file_id.type != content_type) {
continue;
}
if (file_id.date > start_date) {
@@ -139,7 +139,7 @@ Result AlbumManager::GetAlbumFileList(std::vector<ApplicationAlbumEntry>& out_en
.hash{},
.datetime = file_id.date,
.storage = file_id.storage,
- .content = contex_type,
+ .content = content_type,
.unknown = 1,
};
out_entries.push_back(entry);
diff --git a/src/core/hle/service/caps/caps_manager.h b/src/core/hle/service/caps/caps_manager.h
index e20c70c7b..6fd34f589 100644
--- a/src/core/hle/service/caps/caps_manager.h
+++ b/src/core/hle/service/caps/caps_manager.h
@@ -45,10 +45,10 @@ public:
Result GetAlbumFileList(std::vector<AlbumEntry>& out_entries, AlbumStorage storage,
u8 flags) const;
Result GetAlbumFileList(std::vector<ApplicationAlbumFileEntry>& out_entries,
- ContentType contex_type, s64 start_posix_time, s64 end_posix_time,
+ ContentType content_type, s64 start_posix_time, s64 end_posix_time,
u64 aruid) const;
Result GetAlbumFileList(std::vector<ApplicationAlbumEntry>& out_entries,
- ContentType contex_type, AlbumFileDateTime start_date,
+ ContentType content_type, AlbumFileDateTime start_date,
AlbumFileDateTime end_date, u64 aruid) const;
Result GetAutoSavingStorage(bool& out_is_autosaving) const;
Result LoadAlbumScreenShotImage(LoadAlbumScreenShotImageOutput& out_image_output,
diff --git a/src/core/hle/service/caps/caps_result.h b/src/core/hle/service/caps/caps_result.h
index c65e5fb9a..179ae4840 100644
--- a/src/core/hle/service/caps/caps_result.h
+++ b/src/core/hle/service/caps/caps_result.h
@@ -12,7 +12,7 @@ constexpr Result ResultUnknown5(ErrorModule::Capture, 5);
constexpr Result ResultUnknown6(ErrorModule::Capture, 6);
constexpr Result ResultUnknown7(ErrorModule::Capture, 7);
constexpr Result ResultOutOfRange(ErrorModule::Capture, 8);
-constexpr Result ResulInvalidTimestamp(ErrorModule::Capture, 12);
+constexpr Result ResultInvalidTimestamp(ErrorModule::Capture, 12);
constexpr Result ResultInvalidStorage(ErrorModule::Capture, 13);
constexpr Result ResultInvalidFileContents(ErrorModule::Capture, 14);
constexpr Result ResultIsNotMounted(ErrorModule::Capture, 21);
diff --git a/src/core/hle/service/friend/friend.cpp b/src/core/hle/service/friend/friend.cpp
index 0507b14e7..aeb849efa 100644
--- a/src/core/hle/service/friend/friend.cpp
+++ b/src/core/hle/service/friend/friend.cpp
@@ -131,7 +131,7 @@ private:
u8 is_favorite;
u8 same_app;
u8 same_app_played;
- u8 arbitary_app_played;
+ u8 arbitrary_app_played;
u64 group_id;
};
static_assert(sizeof(SizedFriendFilter) == 0x10, "SizedFriendFilter is an invalid size");
diff --git a/src/core/hle/service/glue/arp.cpp b/src/core/hle/service/glue/arp.cpp
index 6f1151b03..1254b6d49 100644
--- a/src/core/hle/service/glue/arp.cpp
+++ b/src/core/hle/service/glue/arp.cpp
@@ -15,9 +15,10 @@
namespace Service::Glue {
namespace {
-std::optional<u64> GetTitleIDForProcessID(const Core::System& system, u64 process_id) {
- const auto& list = system.Kernel().GetProcessList();
- const auto iter = std::find_if(list.begin(), list.end(), [&process_id](const auto& process) {
+std::optional<u64> GetTitleIDForProcessID(Core::System& system, u64 process_id) {
+ auto list = system.Kernel().GetProcessList();
+
+ const auto iter = std::find_if(list.begin(), list.end(), [&process_id](auto& process) {
return process->GetProcessId() == process_id;
});
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp
index fc8a3ab66..4ce0a9834 100644
--- a/src/core/hle/service/hid/hid.cpp
+++ b/src/core/hle/service/hid/hid.cpp
@@ -18,23 +18,21 @@ namespace Service::HID {
void LoopProcess(Core::System& system) {
auto server_manager = std::make_unique<ServerManager>(system);
- std::shared_ptr<ResourceManager> resouce_manager = std::make_shared<ResourceManager>(system);
+ std::shared_ptr<ResourceManager> resource_manager = std::make_shared<ResourceManager>(system);
std::shared_ptr<HidFirmwareSettings> firmware_settings =
std::make_shared<HidFirmwareSettings>();
- // TODO: Remove this hack until this service is emulated properly.
- const auto process_list = system.Kernel().GetProcessList();
- if (!process_list.empty()) {
- resouce_manager->Initialize();
- resouce_manager->RegisterAppletResourceUserId(process_list[0]->GetId(), true);
- }
+ // TODO: Remove this hack when am is emulated properly.
+ resource_manager->Initialize();
+ resource_manager->RegisterAppletResourceUserId(system.ApplicationProcess()->GetProcessId(),
+ true);
server_manager->RegisterNamedService(
- "hid", std::make_shared<IHidServer>(system, resouce_manager, firmware_settings));
+ "hid", std::make_shared<IHidServer>(system, resource_manager, firmware_settings));
server_manager->RegisterNamedService(
- "hid:dbg", std::make_shared<IHidDebugServer>(system, resouce_manager));
+ "hid:dbg", std::make_shared<IHidDebugServer>(system, resource_manager));
server_manager->RegisterNamedService(
- "hid:sys", std::make_shared<IHidSystemServer>(system, resouce_manager));
+ "hid:sys", std::make_shared<IHidSystemServer>(system, resource_manager));
server_manager->RegisterNamedService("hidbus", std::make_shared<HidBus>(system));
diff --git a/src/core/hle/service/hid/hid_server.cpp b/src/core/hle/service/hid/hid_server.cpp
index 74898888a..1951da33b 100644
--- a/src/core/hle/service/hid/hid_server.cpp
+++ b/src/core/hle/service/hid/hid_server.cpp
@@ -1498,7 +1498,7 @@ void IHidServer::GetVibrationDeviceInfo(HLERequestContext& ctx) {
bool check_device_index = false;
switch (vibration_device_handle.npad_type) {
- case Core::HID::NpadStyleIndex::ProController:
+ case Core::HID::NpadStyleIndex::Fullkey:
case Core::HID::NpadStyleIndex::Handheld:
case Core::HID::NpadStyleIndex::JoyconDual:
case Core::HID::NpadStyleIndex::JoyconLeft:
diff --git a/src/core/hle/service/hid/hidbus.cpp b/src/core/hle/service/hid/hidbus.cpp
index 46f503d38..c903ee8b8 100644
--- a/src/core/hle/service/hid/hidbus.cpp
+++ b/src/core/hle/service/hid/hidbus.cpp
@@ -67,7 +67,7 @@ HidBus::~HidBus() {
void HidBus::UpdateHidbus(std::chrono::nanoseconds ns_late) {
if (is_hidbus_enabled) {
for (std::size_t i = 0; i < devices.size(); ++i) {
- if (!devices[i].is_device_initializated) {
+ if (!devices[i].is_device_initialized) {
continue;
}
auto& device = devices[i].device;
@@ -213,7 +213,7 @@ void HidBus::Initialize(HLERequestContext& ctx) {
if (bus_handle_.internal_index == 0 && Settings::values.enable_ring_controller) {
MakeDevice<RingController>(bus_handle_);
- devices[device_index.value()].is_device_initializated = true;
+ devices[device_index.value()].is_device_initialized = true;
devices[device_index.value()].device->ActivateDevice();
cur_entry.is_in_focus = true;
cur_entry.is_connected = true;
@@ -222,7 +222,7 @@ void HidBus::Initialize(HLERequestContext& ctx) {
cur_entry.is_polling_mode = false;
} else {
MakeDevice<HidbusStubbed>(bus_handle_);
- devices[device_index.value()].is_device_initializated = true;
+ devices[device_index.value()].is_device_initialized = true;
cur_entry.is_in_focus = true;
cur_entry.is_connected = false;
cur_entry.is_connected_result = ResultSuccess;
@@ -261,7 +261,7 @@ void HidBus::Finalize(HLERequestContext& ctx) {
const auto entry_index = devices[device_index.value()].handle.internal_index;
auto& cur_entry = hidbus_status.entries[entry_index];
auto& device = devices[device_index.value()].device;
- devices[device_index.value()].is_device_initializated = false;
+ devices[device_index.value()].is_device_initialized = false;
device->DeactivateDevice();
cur_entry.is_in_focus = true;
diff --git a/src/core/hle/service/hid/hidbus.h b/src/core/hle/service/hid/hidbus.h
index 05f62f634..03d9f6863 100644
--- a/src/core/hle/service/hid/hidbus.h
+++ b/src/core/hle/service/hid/hidbus.h
@@ -89,7 +89,7 @@ private:
static_assert(sizeof(HidbusStatusManager) <= 0x1000, "HidbusStatusManager is an invalid size");
struct HidbusDevice {
- bool is_device_initializated{};
+ bool is_device_initialized{};
BusHandle handle{};
std::unique_ptr<HidbusBase> device{nullptr};
};
diff --git a/src/core/hle/service/hle_ipc.cpp b/src/core/hle/service/hle_ipc.cpp
index 39df77e43..3f38ceb03 100644
--- a/src/core/hle/service/hle_ipc.cpp
+++ b/src/core/hle/service/hle_ipc.cpp
@@ -181,22 +181,22 @@ void HLERequestContext::ParseCommandBuffer(u32_le* src_cmdbuf, bool incoming) {
}
}
- buffer_x_desciptors.reserve(command_header->num_buf_x_descriptors);
- buffer_a_desciptors.reserve(command_header->num_buf_a_descriptors);
- buffer_b_desciptors.reserve(command_header->num_buf_b_descriptors);
- buffer_w_desciptors.reserve(command_header->num_buf_w_descriptors);
+ buffer_x_descriptors.reserve(command_header->num_buf_x_descriptors);
+ buffer_a_descriptors.reserve(command_header->num_buf_a_descriptors);
+ buffer_b_descriptors.reserve(command_header->num_buf_b_descriptors);
+ buffer_w_descriptors.reserve(command_header->num_buf_w_descriptors);
for (u32 i = 0; i < command_header->num_buf_x_descriptors; ++i) {
- buffer_x_desciptors.push_back(rp.PopRaw<IPC::BufferDescriptorX>());
+ buffer_x_descriptors.push_back(rp.PopRaw<IPC::BufferDescriptorX>());
}
for (u32 i = 0; i < command_header->num_buf_a_descriptors; ++i) {
- buffer_a_desciptors.push_back(rp.PopRaw<IPC::BufferDescriptorABW>());
+ buffer_a_descriptors.push_back(rp.PopRaw<IPC::BufferDescriptorABW>());
}
for (u32 i = 0; i < command_header->num_buf_b_descriptors; ++i) {
- buffer_b_desciptors.push_back(rp.PopRaw<IPC::BufferDescriptorABW>());
+ buffer_b_descriptors.push_back(rp.PopRaw<IPC::BufferDescriptorABW>());
}
for (u32 i = 0; i < command_header->num_buf_w_descriptors; ++i) {
- buffer_w_desciptors.push_back(rp.PopRaw<IPC::BufferDescriptorABW>());
+ buffer_w_descriptors.push_back(rp.PopRaw<IPC::BufferDescriptorABW>());
}
const auto buffer_c_offset = rp.GetCurrentOffset() + command_header->data_size;
@@ -246,7 +246,7 @@ void HLERequestContext::ParseCommandBuffer(u32_le* src_cmdbuf, bool incoming) {
IPC::CommandHeader::BufferDescriptorCFlag::InlineDescriptor) {
if (command_header->buf_c_descriptor_flags ==
IPC::CommandHeader::BufferDescriptorCFlag::OneDescriptor) {
- buffer_c_desciptors.push_back(rp.PopRaw<IPC::BufferDescriptorC>());
+ buffer_c_descriptors.push_back(rp.PopRaw<IPC::BufferDescriptorC>());
} else {
u32 num_buf_c_descriptors =
static_cast<u32>(command_header->buf_c_descriptor_flags.Value()) - 2;
@@ -256,7 +256,7 @@ void HLERequestContext::ParseCommandBuffer(u32_le* src_cmdbuf, bool incoming) {
ASSERT(num_buf_c_descriptors < 14);
for (u32 i = 0; i < num_buf_c_descriptors; ++i) {
- buffer_c_desciptors.push_back(rp.PopRaw<IPC::BufferDescriptorC>());
+ buffer_c_descriptors.push_back(rp.PopRaw<IPC::BufferDescriptorC>());
}
}
}
diff --git a/src/core/hle/service/hle_ipc.h b/src/core/hle/service/hle_ipc.h
index 40d86943e..440737db5 100644
--- a/src/core/hle/service/hle_ipc.h
+++ b/src/core/hle/service/hle_ipc.h
@@ -232,19 +232,19 @@ public:
}
[[nodiscard]] const std::vector<IPC::BufferDescriptorX>& BufferDescriptorX() const {
- return buffer_x_desciptors;
+ return buffer_x_descriptors;
}
[[nodiscard]] const std::vector<IPC::BufferDescriptorABW>& BufferDescriptorA() const {
- return buffer_a_desciptors;
+ return buffer_a_descriptors;
}
[[nodiscard]] const std::vector<IPC::BufferDescriptorABW>& BufferDescriptorB() const {
- return buffer_b_desciptors;
+ return buffer_b_descriptors;
}
[[nodiscard]] const std::vector<IPC::BufferDescriptorC>& BufferDescriptorC() const {
- return buffer_c_desciptors;
+ return buffer_c_descriptors;
}
[[nodiscard]] const IPC::DomainMessageHeader& GetDomainMessageHeader() const {
@@ -406,11 +406,11 @@ private:
std::optional<IPC::HandleDescriptorHeader> handle_descriptor_header;
std::optional<IPC::DataPayloadHeader> data_payload_header;
std::optional<IPC::DomainMessageHeader> domain_message_header;
- std::vector<IPC::BufferDescriptorX> buffer_x_desciptors;
- std::vector<IPC::BufferDescriptorABW> buffer_a_desciptors;
- std::vector<IPC::BufferDescriptorABW> buffer_b_desciptors;
- std::vector<IPC::BufferDescriptorABW> buffer_w_desciptors;
- std::vector<IPC::BufferDescriptorC> buffer_c_desciptors;
+ std::vector<IPC::BufferDescriptorX> buffer_x_descriptors;
+ std::vector<IPC::BufferDescriptorABW> buffer_a_descriptors;
+ std::vector<IPC::BufferDescriptorABW> buffer_b_descriptors;
+ std::vector<IPC::BufferDescriptorABW> buffer_w_descriptors;
+ std::vector<IPC::BufferDescriptorC> buffer_c_descriptors;
u32_le command{};
u64 pid{};
diff --git a/src/core/hle/service/nfc/common/amiibo_crypto.cpp b/src/core/hle/service/nfc/common/amiibo_crypto.cpp
index 9556e9193..4274a92c9 100644
--- a/src/core/hle/service/nfc/common/amiibo_crypto.cpp
+++ b/src/core/hle/service/nfc/common/amiibo_crypto.cpp
@@ -19,7 +19,7 @@ namespace Service::NFP::AmiiboCrypto {
bool IsAmiiboValid(const EncryptedNTAG215File& ntag_file) {
const auto& amiibo_data = ntag_file.user_memory;
LOG_DEBUG(Service_NFP, "uuid_lock=0x{0:x}", ntag_file.static_lock);
- LOG_DEBUG(Service_NFP, "compability_container=0x{0:x}", ntag_file.compability_container);
+ LOG_DEBUG(Service_NFP, "compatibility_container=0x{0:x}", ntag_file.compatibility_container);
LOG_DEBUG(Service_NFP, "write_count={}", static_cast<u16>(amiibo_data.write_counter));
LOG_DEBUG(Service_NFP, "character_id=0x{0:x}", amiibo_data.model_info.character_id);
@@ -49,7 +49,7 @@ bool IsAmiiboValid(const EncryptedNTAG215File& ntag_file) {
if (ntag_file.static_lock != 0xE00F) {
return false;
}
- if (ntag_file.compability_container != 0xEEFF10F1U) {
+ if (ntag_file.compatibility_container != 0xEEFF10F1U) {
return false;
}
if (amiibo_data.model_info.tag_type != NFC::PackedTagType::Type2) {
@@ -78,7 +78,7 @@ NTAG215File NfcDataToEncodedData(const EncryptedNTAG215File& nfc_data) {
encoded_data.uid_crc_check2 = nfc_data.uuid_crc_check2;
encoded_data.internal_number = nfc_data.internal_number;
encoded_data.static_lock = nfc_data.static_lock;
- encoded_data.compability_container = nfc_data.compability_container;
+ encoded_data.compatibility_container = nfc_data.compatibility_container;
encoded_data.hmac_data = nfc_data.user_memory.hmac_data;
encoded_data.constant_value = nfc_data.user_memory.constant_value;
encoded_data.write_counter = nfc_data.user_memory.write_counter;
@@ -112,7 +112,7 @@ EncryptedNTAG215File EncodedDataToNfcData(const NTAG215File& encoded_data) {
nfc_data.uuid_crc_check2 = encoded_data.uid_crc_check2;
nfc_data.internal_number = encoded_data.internal_number;
nfc_data.static_lock = encoded_data.static_lock;
- nfc_data.compability_container = encoded_data.compability_container;
+ nfc_data.compatibility_container = encoded_data.compatibility_container;
nfc_data.user_memory.hmac_data = encoded_data.hmac_data;
nfc_data.user_memory.constant_value = encoded_data.constant_value;
nfc_data.user_memory.write_counter = encoded_data.write_counter;
@@ -257,7 +257,7 @@ void Cipher(const DerivedKeys& keys, const NTAG215File& in_data, NTAG215File& ou
out_data.uid_crc_check2 = in_data.uid_crc_check2;
out_data.internal_number = in_data.internal_number;
out_data.static_lock = in_data.static_lock;
- out_data.compability_container = in_data.compability_container;
+ out_data.compatibility_container = in_data.compatibility_container;
out_data.constant_value = in_data.constant_value;
out_data.write_counter = in_data.write_counter;
diff --git a/src/core/hle/service/nfc/common/device.cpp b/src/core/hle/service/nfc/common/device.cpp
index b37fb6da3..31cc87acc 100644
--- a/src/core/hle/service/nfc/common/device.cpp
+++ b/src/core/hle/service/nfc/common/device.cpp
@@ -75,7 +75,7 @@ void NfcDevice::NpadUpdate(Core::HID::ControllerTriggerType type) {
return;
}
- if (!is_initalized) {
+ if (!is_initialized) {
return;
}
@@ -207,7 +207,7 @@ void NfcDevice::Initialize() {
return;
}
- is_initalized = npad_device->AddNfcHandle();
+ is_initialized = npad_device->AddNfcHandle();
}
void NfcDevice::Finalize() {
@@ -226,7 +226,7 @@ void NfcDevice::Finalize() {
}
device_state = DeviceState::Unavailable;
- is_initalized = false;
+ is_initialized = false;
}
Result NfcDevice::StartDetection(NfcProtocol allowed_protocol) {
diff --git a/src/core/hle/service/nfc/common/device.h b/src/core/hle/service/nfc/common/device.h
index d8efe25ec..15f9b25da 100644
--- a/src/core/hle/service/nfc/common/device.h
+++ b/src/core/hle/service/nfc/common/device.h
@@ -126,7 +126,7 @@ private:
Kernel::KEvent* deactivate_event = nullptr;
Kernel::KEvent* availability_change_event = nullptr;
- bool is_initalized{};
+ bool is_initialized{};
NfcProtocol allowed_protocols{};
DeviceState device_state{DeviceState::Unavailable};
diff --git a/src/core/hle/service/nfp/nfp_types.h b/src/core/hle/service/nfp/nfp_types.h
index f96d21220..2505eb551 100644
--- a/src/core/hle/service/nfp/nfp_types.h
+++ b/src/core/hle/service/nfp/nfp_types.h
@@ -243,12 +243,12 @@ static_assert(sizeof(EncryptedAmiiboFile) == 0x1F8, "AmiiboFile is an invalid si
struct NTAG215File {
u8 uid_crc_check2;
u8 internal_number;
- u16 static_lock; // Set defined pages as read only
- u32 compability_container; // Defines available memory
- HashData hmac_data; // Hash
- u8 constant_value; // Must be A5
- u16_be write_counter; // Number of times the amiibo has been written?
- u8 amiibo_version; // Amiibo file version
+ u16 static_lock; // Set defined pages as read only
+ u32 compatibility_container; // Defines available memory
+ HashData hmac_data; // Hash
+ u8 constant_value; // Must be A5
+ u16_be write_counter; // Number of times the amiibo has been written?
+ u8 amiibo_version; // Amiibo file version
AmiiboSettings settings;
Service::Mii::Ver3StoreData owner_mii; // Mii data
u64_be application_id; // Game id
@@ -278,7 +278,7 @@ struct EncryptedNTAG215File {
u8 uuid_crc_check2;
u8 internal_number;
u16 static_lock; // Set defined pages as read only
- u32 compability_container; // Defines available memory
+ u32 compatibility_container; // Defines available memory
EncryptedAmiiboFile user_memory; // Writable data
u32 dynamic_lock; // Dynamic lock
u32 CFG0; // Defines memory protected by password
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h
index 932997e75..79a21683d 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h
@@ -90,7 +90,7 @@ private:
u64_le align;
};
};
- static_assert(sizeof(IoctlAllocSpace) == 24, "IoctlInitalizeEx is incorrect size");
+ static_assert(sizeof(IoctlAllocSpace) == 24, "IoctlInitializeEx is incorrect size");
struct IoctlFreeSpace {
u64_le offset{};
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp
index 61a2df121..3e0c96456 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp
@@ -15,7 +15,7 @@ namespace Service::Nvidia::Devices {
nvhost_ctrl_gpu::nvhost_ctrl_gpu(Core::System& system_, EventInterface& events_interface_)
: nvdevice{system_}, events_interface{events_interface_} {
error_notifier_event = events_interface.CreateEvent("CtrlGpuErrorNotifier");
- unknown_event = events_interface.CreateEvent("CtrlGpuUknownEvent");
+ unknown_event = events_interface.CreateEvent("CtrlGpuUnknownEvent");
}
nvhost_ctrl_gpu::~nvhost_ctrl_gpu() {
events_interface.FreeEvent(error_notifier_event);
diff --git a/src/core/hle/service/nvdrv/nvdata.h b/src/core/hle/service/nvdrv/nvdata.h
index 0e2f47075..38f35e79f 100644
--- a/src/core/hle/service/nvdrv/nvdata.h
+++ b/src/core/hle/service/nvdrv/nvdata.h
@@ -51,7 +51,7 @@ enum class NvResult : u32 {
DispNoDisplaysAttached = 0x20003,
DispModeNotSupported = 0x20004,
DispNotFound = 0x20005,
- DispAttachDissallowed = 0x20006,
+ DispAttachDisallowed = 0x20006,
DispTypeNotSupported = 0x20007,
DispAuthenticationFailed = 0x20008,
DispNotAttached = 0x20009,
diff --git a/src/core/hle/service/nvnflinger/nvnflinger.cpp b/src/core/hle/service/nvnflinger/nvnflinger.cpp
index aa8aaa2d9..0469110e8 100644
--- a/src/core/hle/service/nvnflinger/nvnflinger.cpp
+++ b/src/core/hle/service/nvnflinger/nvnflinger.cpp
@@ -223,7 +223,8 @@ Result Nvnflinger::FindVsyncEvent(Kernel::KReadableEvent** out_vsync_event, u64
return VI::ResultNotFound;
}
- return display->GetVSyncEvent(out_vsync_event);
+ *out_vsync_event = display->GetVSyncEvent();
+ return ResultSuccess;
}
VI::Display* Nvnflinger::FindDisplay(u64 display_id) {
diff --git a/src/core/hle/service/pcv/pcv.cpp b/src/core/hle/service/pcv/pcv.cpp
index c13ffa6f6..3d0f2aeb7 100644
--- a/src/core/hle/service/pcv/pcv.cpp
+++ b/src/core/hle/service/pcv/pcv.cpp
@@ -54,8 +54,8 @@ public:
class IClkrstSession final : public ServiceFramework<IClkrstSession> {
public:
- explicit IClkrstSession(Core::System& system_, DeviceCode deivce_code_)
- : ServiceFramework{system_, "IClkrstSession"}, deivce_code(deivce_code_) {
+ explicit IClkrstSession(Core::System& system_, DeviceCode device_code_)
+ : ServiceFramework{system_, "IClkrstSession"}, device_code(device_code_) {
// clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "SetClockEnabled"},
@@ -93,7 +93,7 @@ private:
rb.Push<u32>(clock_rate);
}
- DeviceCode deivce_code;
+ DeviceCode device_code;
u32 clock_rate{};
};
@@ -118,9 +118,9 @@ private:
void OpenSession(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto device_code = static_cast<DeviceCode>(rp.Pop<u32>());
- const auto unkonwn_input = rp.Pop<u32>();
+ const auto unknown_input = rp.Pop<u32>();
- LOG_DEBUG(Service_PCV, "called, device_code={}, input={}", device_code, unkonwn_input);
+ LOG_DEBUG(Service_PCV, "called, device_code={}, input={}", device_code, unknown_input);
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
diff --git a/src/core/hle/service/pm/pm.cpp b/src/core/hle/service/pm/pm.cpp
index d92499f05..b52468e41 100644
--- a/src/core/hle/service/pm/pm.cpp
+++ b/src/core/hle/service/pm/pm.cpp
@@ -22,27 +22,26 @@ constexpr Result ResultProcessNotFound{ErrorModule::PM, 1};
constexpr u64 NO_PROCESS_FOUND_PID{0};
-std::optional<Kernel::KProcess*> SearchProcessList(
- const std::vector<Kernel::KProcess*>& process_list,
- std::function<bool(Kernel::KProcess*)> predicate) {
+using ProcessList = std::list<Kernel::KScopedAutoObject<Kernel::KProcess>>;
+
+template <typename F>
+Kernel::KScopedAutoObject<Kernel::KProcess> SearchProcessList(ProcessList& process_list,
+ F&& predicate) {
const auto iter = std::find_if(process_list.begin(), process_list.end(), predicate);
if (iter == process_list.end()) {
- return std::nullopt;
+ return nullptr;
}
- return *iter;
+ return iter->GetPointerUnsafe();
}
-void GetApplicationPidGeneric(HLERequestContext& ctx,
- const std::vector<Kernel::KProcess*>& process_list) {
- const auto process = SearchProcessList(process_list, [](const auto& proc) {
- return proc->GetProcessId() == Kernel::KProcess::ProcessIdMin;
- });
+void GetApplicationPidGeneric(HLERequestContext& ctx, ProcessList& process_list) {
+ auto process = SearchProcessList(process_list, [](auto& p) { return p->IsApplication(); });
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(ResultSuccess);
- rb.Push(process.has_value() ? (*process)->GetProcessId() : NO_PROCESS_FOUND_PID);
+ rb.Push(process.IsNull() ? NO_PROCESS_FOUND_PID : process->GetProcessId());
}
} // Anonymous namespace
@@ -80,8 +79,7 @@ private:
class DebugMonitor final : public ServiceFramework<DebugMonitor> {
public:
- explicit DebugMonitor(Core::System& system_)
- : ServiceFramework{system_, "pm:dmnt"}, kernel{system_.Kernel()} {
+ explicit DebugMonitor(Core::System& system_) : ServiceFramework{system_, "pm:dmnt"} {
// clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "GetJitDebugProcessIdList"},
@@ -106,12 +104,11 @@ private:
LOG_DEBUG(Service_PM, "called, program_id={:016X}", program_id);
- const auto process =
- SearchProcessList(kernel.GetProcessList(), [program_id](const auto& proc) {
- return proc->GetProgramId() == program_id;
- });
+ auto list = kernel.GetProcessList();
+ auto process = SearchProcessList(
+ list, [program_id](auto& p) { return p->GetProgramId() == program_id; });
- if (!process.has_value()) {
+ if (process.IsNull()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultProcessNotFound);
return;
@@ -119,12 +116,13 @@ private:
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(ResultSuccess);
- rb.Push((*process)->GetProcessId());
+ rb.Push(process->GetProcessId());
}
void GetApplicationProcessId(HLERequestContext& ctx) {
LOG_DEBUG(Service_PM, "called");
- GetApplicationPidGeneric(ctx, kernel.GetProcessList());
+ auto list = kernel.GetProcessList();
+ GetApplicationPidGeneric(ctx, list);
}
void AtmosphereGetProcessInfo(HLERequestContext& ctx) {
@@ -135,11 +133,10 @@ private:
LOG_WARNING(Service_PM, "(Partial Implementation) called, pid={:016X}", pid);
- const auto process = SearchProcessList(kernel.GetProcessList(), [pid](const auto& proc) {
- return proc->GetProcessId() == pid;
- });
+ auto list = kernel.GetProcessList();
+ auto process = SearchProcessList(list, [pid](auto& p) { return p->GetProcessId() == pid; });
- if (!process.has_value()) {
+ if (process.IsNull()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultProcessNotFound);
return;
@@ -159,7 +156,7 @@ private:
OverrideStatus override_status{};
ProgramLocation program_location{
- .program_id = (*process)->GetProgramId(),
+ .program_id = process->GetProgramId(),
.storage_id = 0,
};
@@ -169,14 +166,11 @@ private:
rb.PushRaw(program_location);
rb.PushRaw(override_status);
}
-
- const Kernel::KernelCore& kernel;
};
class Info final : public ServiceFramework<Info> {
public:
- explicit Info(Core::System& system_, const std::vector<Kernel::KProcess*>& process_list_)
- : ServiceFramework{system_, "pm:info"}, process_list{process_list_} {
+ explicit Info(Core::System& system_) : ServiceFramework{system_, "pm:info"} {
static const FunctionInfo functions[] = {
{0, &Info::GetProgramId, "GetProgramId"},
{65000, &Info::AtmosphereGetProcessId, "AtmosphereGetProcessId"},
@@ -193,11 +187,11 @@ private:
LOG_DEBUG(Service_PM, "called, process_id={:016X}", process_id);
- const auto process = SearchProcessList(process_list, [process_id](const auto& proc) {
- return proc->GetProcessId() == process_id;
- });
+ auto list = kernel.GetProcessList();
+ auto process = SearchProcessList(
+ list, [process_id](auto& p) { return p->GetProcessId() == process_id; });
- if (!process.has_value()) {
+ if (process.IsNull()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultProcessNotFound);
return;
@@ -205,7 +199,7 @@ private:
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(ResultSuccess);
- rb.Push((*process)->GetProgramId());
+ rb.Push(process->GetProgramId());
}
void AtmosphereGetProcessId(HLERequestContext& ctx) {
@@ -214,11 +208,11 @@ private:
LOG_DEBUG(Service_PM, "called, program_id={:016X}", program_id);
- const auto process = SearchProcessList(process_list, [program_id](const auto& proc) {
- return proc->GetProgramId() == program_id;
- });
+ auto list = system.Kernel().GetProcessList();
+ auto process = SearchProcessList(
+ list, [program_id](auto& p) { return p->GetProgramId() == program_id; });
- if (!process.has_value()) {
+ if (process.IsNull()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultProcessNotFound);
return;
@@ -226,16 +220,13 @@ private:
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(ResultSuccess);
- rb.Push((*process)->GetProcessId());
+ rb.Push(process->GetProcessId());
}
-
- const std::vector<Kernel::KProcess*>& process_list;
};
class Shell final : public ServiceFramework<Shell> {
public:
- explicit Shell(Core::System& system_)
- : ServiceFramework{system_, "pm:shell"}, kernel{system_.Kernel()} {
+ explicit Shell(Core::System& system_) : ServiceFramework{system_, "pm:shell"} {
// clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "LaunchProgram"},
@@ -257,10 +248,9 @@ public:
private:
void GetApplicationProcessIdForShell(HLERequestContext& ctx) {
LOG_DEBUG(Service_PM, "called");
- GetApplicationPidGeneric(ctx, kernel.GetProcessList());
+ auto list = kernel.GetProcessList();
+ GetApplicationPidGeneric(ctx, list);
}
-
- const Kernel::KernelCore& kernel;
};
void LoopProcess(Core::System& system) {
@@ -268,8 +258,7 @@ void LoopProcess(Core::System& system) {
server_manager->RegisterNamedService("pm:bm", std::make_shared<BootMode>(system));
server_manager->RegisterNamedService("pm:dmnt", std::make_shared<DebugMonitor>(system));
- server_manager->RegisterNamedService(
- "pm:info", std::make_shared<Info>(system, system.Kernel().GetProcessList()));
+ server_manager->RegisterNamedService("pm:info", std::make_shared<Info>(system));
server_manager->RegisterNamedService("pm:shell", std::make_shared<Shell>(system));
ServerManager::RunServer(std::move(server_manager));
}
diff --git a/src/core/hle/service/server_manager.cpp b/src/core/hle/service/server_manager.cpp
index 15edb23e0..8ef49387d 100644
--- a/src/core/hle/service/server_manager.cpp
+++ b/src/core/hle/service/server_manager.cpp
@@ -256,8 +256,13 @@ Result ServerManager::WaitAndProcessImpl() {
// Wait for a signal.
s32 out_index{-1};
- R_TRY(Kernel::KSynchronizationObject::Wait(m_system.Kernel(), &out_index, wait_objs.data(),
- num_objs, -1));
+ R_TRY_CATCH(Kernel::KSynchronizationObject::Wait(m_system.Kernel(), &out_index,
+ wait_objs.data(), num_objs, -1)) {
+ R_CATCH(Kernel::ResultSessionClosed) {
+ // On session closed, index is updated and we don't want to return an error.
+ }
+ }
+ R_END_TRY_CATCH;
ASSERT(out_index >= 0 && out_index < num_objs);
// Set the output index.
diff --git a/src/core/hle/service/set/system_settings.cpp b/src/core/hle/service/set/system_settings.cpp
index 2723417ad..5977429b2 100644
--- a/src/core/hle/service/set/system_settings.cpp
+++ b/src/core/hle/service/set/system_settings.cpp
@@ -28,7 +28,7 @@ SystemSettings DefaultSystemSettings() {
.cmu_mode = CmuMode::None,
.tv_underscan = {},
.tv_gama = 1.0f,
- .constrast_ratio = 0.5f,
+ .contrast_ratio = 0.5f,
};
settings.initial_launch_settings_packed = {
diff --git a/src/core/hle/service/set/system_settings.h b/src/core/hle/service/set/system_settings.h
index ded2906ad..6ec9e71e7 100644
--- a/src/core/hle/service/set/system_settings.h
+++ b/src/core/hle/service/set/system_settings.h
@@ -208,7 +208,7 @@ struct TvSettings {
CmuMode cmu_mode;
u32 tv_underscan;
f32 tv_gama;
- f32 constrast_ratio;
+ f32 contrast_ratio;
};
static_assert(sizeof(TvSettings) == 0x20, "TvSettings is an invalid size");
@@ -341,7 +341,7 @@ struct SystemSettings {
std::array<u8, 0x3C> reserved_09934;
// nn::settings::system::ErrorReportSharePermission
- ErrorReportSharePermission error_report_share_permssion;
+ ErrorReportSharePermission error_report_share_permission;
std::array<u8, 0x3C> reserved_09974;
diff --git a/src/core/hle/service/set/system_settings_server.cpp b/src/core/hle/service/set/system_settings_server.cpp
index f7ad6193e..af9348522 100644
--- a/src/core/hle/service/set/system_settings_server.cpp
+++ b/src/core/hle/service/set/system_settings_server.cpp
@@ -721,10 +721,10 @@ void ISystemSettingsServer::SetTvSettings(HLERequestContext& ctx) {
SetSaveNeeded();
LOG_INFO(Service_SET,
- "called, flags={}, cmu_mode={}, constrast_ratio={}, hdmi_content_type={}, "
+ "called, flags={}, cmu_mode={}, contrast_ratio={}, hdmi_content_type={}, "
"rgb_range={}, tv_gama={}, tv_resolution={}, tv_underscan={}",
m_system_settings.tv_settings.flags.raw, m_system_settings.tv_settings.cmu_mode,
- m_system_settings.tv_settings.constrast_ratio,
+ m_system_settings.tv_settings.contrast_ratio,
m_system_settings.tv_settings.hdmi_content_type,
m_system_settings.tv_settings.rgb_range, m_system_settings.tv_settings.tv_gama,
m_system_settings.tv_settings.tv_resolution,
@@ -870,10 +870,10 @@ void ISystemSettingsServer::GetInitialLaunchSettings(HLERequestContext& ctx) {
void ISystemSettingsServer::SetInitialLaunchSettings(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
- auto inital_launch_settings = rp.PopRaw<InitialLaunchSettings>();
+ auto initial_launch_settings = rp.PopRaw<InitialLaunchSettings>();
- m_system_settings.initial_launch_settings_packed.flags = inital_launch_settings.flags;
- m_system_settings.initial_launch_settings_packed.timestamp = inital_launch_settings.timestamp;
+ m_system_settings.initial_launch_settings_packed.flags = initial_launch_settings.flags;
+ m_system_settings.initial_launch_settings_packed.timestamp = initial_launch_settings.timestamp;
SetSaveNeeded();
LOG_INFO(Service_SET, "called, flags={}, timestamp={}",
diff --git a/src/core/hle/service/vi/display/vi_display.cpp b/src/core/hle/service/vi/display/vi_display.cpp
index 71ce9be50..e2d9cd98a 100644
--- a/src/core/hle/service/vi/display/vi_display.cpp
+++ b/src/core/hle/service/vi/display/vi_display.cpp
@@ -71,18 +71,7 @@ size_t Display::GetNumLayers() const {
return std::ranges::count_if(layers, [](auto& l) { return l->IsOpen(); });
}
-Result Display::GetVSyncEvent(Kernel::KReadableEvent** out_vsync_event) {
- if (got_vsync_event) {
- return ResultPermissionDenied;
- }
-
- got_vsync_event = true;
-
- *out_vsync_event = GetVSyncEventUnchecked();
- return ResultSuccess;
-}
-
-Kernel::KReadableEvent* Display::GetVSyncEventUnchecked() {
+Kernel::KReadableEvent* Display::GetVSyncEvent() {
return &vsync_event->GetReadableEvent();
}
diff --git a/src/core/hle/service/vi/display/vi_display.h b/src/core/hle/service/vi/display/vi_display.h
index 1d9360b96..7e68ee79b 100644
--- a/src/core/hle/service/vi/display/vi_display.h
+++ b/src/core/hle/service/vi/display/vi_display.h
@@ -74,16 +74,8 @@ public:
std::size_t GetNumLayers() const;
- /**
- * Gets the internal vsync event.
- *
- * @returns The internal Vsync event if it has not yet been retrieved,
- * VI::ResultPermissionDenied otherwise.
- */
- [[nodiscard]] Result GetVSyncEvent(Kernel::KReadableEvent** out_vsync_event);
-
/// Gets the internal vsync event.
- Kernel::KReadableEvent* GetVSyncEventUnchecked();
+ Kernel::KReadableEvent* GetVSyncEvent();
/// Signals the internal vsync event.
void SignalVSyncEvent();
@@ -104,7 +96,6 @@ public:
/// Resets the display for a new connection.
void Reset() {
layers.clear();
- got_vsync_event = false;
}
/// Attempts to find a layer with the given ID.
@@ -133,7 +124,6 @@ private:
std::vector<std::unique_ptr<Layer>> layers;
Kernel::KEvent* vsync_event{};
- bool got_vsync_event{false};
};
} // namespace Service::VI
diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp
index 9ab8788e3..39d5be90d 100644
--- a/src/core/hle/service/vi/vi.cpp
+++ b/src/core/hle/service/vi/vi.cpp
@@ -343,8 +343,8 @@ private:
class IManagerDisplayService final : public ServiceFramework<IManagerDisplayService> {
public:
- explicit IManagerDisplayService(Core::System& system_, Nvnflinger::Nvnflinger& nv_flinger_)
- : ServiceFramework{system_, "IManagerDisplayService"}, nv_flinger{nv_flinger_} {
+ explicit IManagerDisplayService(Core::System& system_, Nvnflinger::Nvnflinger& nvnflinger_)
+ : ServiceFramework{system_, "IManagerDisplayService"}, nvnflinger{nvnflinger_} {
// clang-format off
static const FunctionInfo functions[] = {
{200, nullptr, "AllocateProcessHeapBlock"},
@@ -440,7 +440,7 @@ private:
IPC::RequestParser rp{ctx};
const u64 display = rp.Pop<u64>();
- const Result rc = nv_flinger.CloseDisplay(display) ? ResultSuccess : ResultUnknown;
+ const Result rc = nvnflinger.CloseDisplay(display) ? ResultSuccess : ResultUnknown;
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(rc);
@@ -457,7 +457,7 @@ private:
"(STUBBED) called. unknown=0x{:08X}, display=0x{:016X}, aruid=0x{:016X}",
unknown, display, aruid);
- const auto layer_id = nv_flinger.CreateLayer(display);
+ const auto layer_id = nvnflinger.CreateLayer(display);
if (!layer_id) {
LOG_ERROR(Service_VI, "Layer not found! display=0x{:016X}", display);
IPC::ResponseBuilder rb{ctx, 2};
@@ -494,14 +494,14 @@ private:
rb.Push(ResultSuccess);
}
- Nvnflinger::Nvnflinger& nv_flinger;
+ Nvnflinger::Nvnflinger& nvnflinger;
};
class IApplicationDisplayService final : public ServiceFramework<IApplicationDisplayService> {
public:
- IApplicationDisplayService(Core::System& system_, Nvnflinger::Nvnflinger& nv_flinger_,
+ IApplicationDisplayService(Core::System& system_, Nvnflinger::Nvnflinger& nvnflinger_,
Nvnflinger::HosBinderDriverServer& hos_binder_driver_server_)
- : ServiceFramework{system_, "IApplicationDisplayService"}, nv_flinger{nv_flinger_},
+ : ServiceFramework{system_, "IApplicationDisplayService"}, nvnflinger{nvnflinger_},
hos_binder_driver_server{hos_binder_driver_server_} {
static const FunctionInfo functions[] = {
@@ -564,7 +564,7 @@ private:
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
- rb.PushIpcInterface<ISystemDisplayService>(system, nv_flinger);
+ rb.PushIpcInterface<ISystemDisplayService>(system, nvnflinger);
}
void GetManagerDisplayService(HLERequestContext& ctx) {
@@ -572,7 +572,7 @@ private:
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
- rb.PushIpcInterface<IManagerDisplayService>(system, nv_flinger);
+ rb.PushIpcInterface<IManagerDisplayService>(system, nvnflinger);
}
void GetIndirectDisplayTransactionService(HLERequestContext& ctx) {
@@ -607,7 +607,7 @@ private:
ASSERT_MSG(name == "Default", "Non-default displays aren't supported yet");
- const auto display_id = nv_flinger.OpenDisplay(name);
+ const auto display_id = nvnflinger.OpenDisplay(name);
if (!display_id) {
LOG_ERROR(Service_VI, "Display not found! display_name={}", name);
IPC::ResponseBuilder rb{ctx, 2};
@@ -624,7 +624,7 @@ private:
IPC::RequestParser rp{ctx};
const u64 display_id = rp.Pop<u64>();
- const Result rc = nv_flinger.CloseDisplay(display_id) ? ResultSuccess : ResultUnknown;
+ const Result rc = nvnflinger.CloseDisplay(display_id) ? ResultSuccess : ResultUnknown;
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(rc);
@@ -703,7 +703,7 @@ private:
LOG_DEBUG(Service_VI, "called. layer_id=0x{:016X}, aruid=0x{:016X}", layer_id, aruid);
- const auto display_id = nv_flinger.OpenDisplay(display_name);
+ const auto display_id = nvnflinger.OpenDisplay(display_name);
if (!display_id) {
LOG_ERROR(Service_VI, "Layer not found! layer_id={}", layer_id);
IPC::ResponseBuilder rb{ctx, 2};
@@ -711,7 +711,7 @@ private:
return;
}
- const auto buffer_queue_id = nv_flinger.FindBufferQueueId(*display_id, layer_id);
+ const auto buffer_queue_id = nvnflinger.FindBufferQueueId(*display_id, layer_id);
if (!buffer_queue_id) {
LOG_ERROR(Service_VI, "Buffer queue id not found! display_id={}", *display_id);
IPC::ResponseBuilder rb{ctx, 2};
@@ -719,7 +719,7 @@ private:
return;
}
- nv_flinger.OpenLayer(layer_id);
+ nvnflinger.OpenLayer(layer_id);
android::OutputParcel parcel;
parcel.WriteInterface(NativeWindow{*buffer_queue_id});
@@ -737,7 +737,7 @@ private:
LOG_DEBUG(Service_VI, "called. layer_id=0x{:016X}", layer_id);
- nv_flinger.CloseLayer(layer_id);
+ nvnflinger.CloseLayer(layer_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
@@ -753,7 +753,7 @@ private:
// TODO(Subv): What's the difference between a Stray and a Managed layer?
- const auto layer_id = nv_flinger.CreateLayer(display_id);
+ const auto layer_id = nvnflinger.CreateLayer(display_id);
if (!layer_id) {
LOG_ERROR(Service_VI, "Layer not found! display_id={}", display_id);
IPC::ResponseBuilder rb{ctx, 2};
@@ -761,7 +761,7 @@ private:
return;
}
- const auto buffer_queue_id = nv_flinger.FindBufferQueueId(display_id, *layer_id);
+ const auto buffer_queue_id = nvnflinger.FindBufferQueueId(display_id, *layer_id);
if (!buffer_queue_id) {
LOG_ERROR(Service_VI, "Buffer queue id not found! display_id={}", display_id);
IPC::ResponseBuilder rb{ctx, 2};
@@ -785,7 +785,7 @@ private:
const u64 layer_id = rp.Pop<u64>();
LOG_WARNING(Service_VI, "(STUBBED) called. layer_id=0x{:016X}", layer_id);
- nv_flinger.DestroyLayer(layer_id);
+ nvnflinger.DestroyLayer(layer_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
@@ -798,7 +798,7 @@ private:
LOG_DEBUG(Service_VI, "called. display_id={}", display_id);
Kernel::KReadableEvent* vsync_event{};
- const auto result = nv_flinger.FindVsyncEvent(&vsync_event, display_id);
+ const auto result = nvnflinger.FindVsyncEvent(&vsync_event, display_id);
if (result != ResultSuccess) {
if (result == ResultNotFound) {
LOG_ERROR(Service_VI, "Vsync event was not found for display_id={}", display_id);
@@ -808,6 +808,12 @@ private:
rb.Push(result);
return;
}
+ if (vsync_event_fetched) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(VI::ResultPermissionDenied);
+ return;
+ }
+ vsync_event_fetched = true;
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(ResultSuccess);
@@ -899,8 +905,9 @@ private:
}
}
- Nvnflinger::Nvnflinger& nv_flinger;
+ Nvnflinger::Nvnflinger& nvnflinger;
Nvnflinger::HosBinderDriverServer& hos_binder_driver_server;
+ bool vsync_event_fetched{false};
};
static bool IsValidServiceAccess(Permission permission, Policy policy) {
@@ -916,7 +923,7 @@ static bool IsValidServiceAccess(Permission permission, Policy policy) {
}
void detail::GetDisplayServiceImpl(HLERequestContext& ctx, Core::System& system,
- Nvnflinger::Nvnflinger& nv_flinger,
+ Nvnflinger::Nvnflinger& nvnflinger,
Nvnflinger::HosBinderDriverServer& hos_binder_driver_server,
Permission permission) {
IPC::RequestParser rp{ctx};
@@ -931,19 +938,19 @@ void detail::GetDisplayServiceImpl(HLERequestContext& ctx, Core::System& system,
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
- rb.PushIpcInterface<IApplicationDisplayService>(system, nv_flinger, hos_binder_driver_server);
+ rb.PushIpcInterface<IApplicationDisplayService>(system, nvnflinger, hos_binder_driver_server);
}
-void LoopProcess(Core::System& system, Nvnflinger::Nvnflinger& nv_flinger,
+void LoopProcess(Core::System& system, Nvnflinger::Nvnflinger& nvnflinger,
Nvnflinger::HosBinderDriverServer& hos_binder_driver_server) {
auto server_manager = std::make_unique<ServerManager>(system);
server_manager->RegisterNamedService(
- "vi:m", std::make_shared<VI_M>(system, nv_flinger, hos_binder_driver_server));
+ "vi:m", std::make_shared<VI_M>(system, nvnflinger, hos_binder_driver_server));
server_manager->RegisterNamedService(
- "vi:s", std::make_shared<VI_S>(system, nv_flinger, hos_binder_driver_server));
+ "vi:s", std::make_shared<VI_S>(system, nvnflinger, hos_binder_driver_server));
server_manager->RegisterNamedService(
- "vi:u", std::make_shared<VI_U>(system, nv_flinger, hos_binder_driver_server));
+ "vi:u", std::make_shared<VI_U>(system, nvnflinger, hos_binder_driver_server));
ServerManager::RunServer(std::move(server_manager));
}
diff --git a/src/core/hle/service/vi/vi.h b/src/core/hle/service/vi/vi.h
index a35b62f97..ee4bcbcfa 100644
--- a/src/core/hle/service/vi/vi.h
+++ b/src/core/hle/service/vi/vi.h
@@ -48,7 +48,7 @@ void GetDisplayServiceImpl(HLERequestContext& ctx, Core::System& system,
Permission permission);
} // namespace detail
-void LoopProcess(Core::System& system, Nvnflinger::Nvnflinger& nv_flinger,
+void LoopProcess(Core::System& system, Nvnflinger::Nvnflinger& nvnflinger,
Nvnflinger::HosBinderDriverServer& hos_binder_driver_server);
} // namespace Service::VI
diff --git a/src/hid_core/CMakeLists.txt b/src/hid_core/CMakeLists.txt
index cce4e6857..aa85502b5 100644
--- a/src/hid_core/CMakeLists.txt
+++ b/src/hid_core/CMakeLists.txt
@@ -36,6 +36,30 @@ add_library(hid_core STATIC
irsensor/processor_base.h
irsensor/tera_plugin_processor.cpp
irsensor/tera_plugin_processor.h
+ resources/abstracted_pad/abstract_battery_handler.cpp
+ resources/abstracted_pad/abstract_battery_handler.h
+ resources/abstracted_pad/abstract_button_handler.cpp
+ resources/abstracted_pad/abstract_button_handler.h
+ resources/abstracted_pad/abstract_ir_sensor_handler.cpp
+ resources/abstracted_pad/abstract_ir_sensor_handler.h
+ resources/abstracted_pad/abstract_led_handler.cpp
+ resources/abstracted_pad/abstract_led_handler.h
+ resources/abstracted_pad/abstract_mcu_handler.cpp
+ resources/abstracted_pad/abstract_mcu_handler.h
+ resources/abstracted_pad/abstract_nfc_handler.cpp
+ resources/abstracted_pad/abstract_nfc_handler.h
+ resources/abstracted_pad/abstract_pad.cpp
+ resources/abstracted_pad/abstract_pad.h
+ resources/abstracted_pad/abstract_pad_holder.cpp
+ resources/abstracted_pad/abstract_pad_holder.h
+ resources/abstracted_pad/abstract_palma_handler.cpp
+ resources/abstracted_pad/abstract_palma_handler.h
+ resources/abstracted_pad/abstract_properties_handler.cpp
+ resources/abstracted_pad/abstract_properties_handler.h
+ resources/abstracted_pad/abstract_sixaxis_handler.cpp
+ resources/abstracted_pad/abstract_sixaxis_handler.h
+ resources/abstracted_pad/abstract_vibration_handler.cpp
+ resources/abstracted_pad/abstract_vibration_handler.h
resources/debug_pad/debug_pad.cpp
resources/debug_pad/debug_pad.h
resources/debug_pad/debug_pad_types.h
@@ -56,6 +80,8 @@ add_library(hid_core STATIC
resources/npad/npad_resource.cpp
resources/npad/npad_resource.h
resources/npad/npad_types.h
+ resources/npad/npad_vibration.cpp
+ resources/npad/npad_vibration.h
resources/palma/palma.cpp
resources/palma/palma.h
resources/six_axis/console_six_axis.cpp
@@ -78,6 +104,14 @@ add_library(hid_core STATIC
resources/touch_screen/touch_types.h
resources/unique_pad/unique_pad.cpp
resources/unique_pad/unique_pad.h
+ resources/vibration/gc_vibration_device.h
+ resources/vibration/gc_vibration_device.cpp
+ resources/vibration/n64_vibration_device.h
+ resources/vibration/n64_vibration_device.cpp
+ resources/vibration/vibration_base.h
+ resources/vibration/vibration_base.cpp
+ resources/vibration/vibration_device.h
+ resources/vibration/vibration_device.cpp
resources/applet_resource.cpp
resources/applet_resource.h
resources/controller_base.cpp
diff --git a/src/hid_core/frontend/emulated_controller.cpp b/src/hid_core/frontend/emulated_controller.cpp
index 3d2d1e9f9..a6a96935d 100644
--- a/src/hid_core/frontend/emulated_controller.cpp
+++ b/src/hid_core/frontend/emulated_controller.cpp
@@ -27,7 +27,7 @@ EmulatedController::~EmulatedController() = default;
NpadStyleIndex EmulatedController::MapSettingsTypeToNPad(Settings::ControllerType type) {
switch (type) {
case Settings::ControllerType::ProController:
- return NpadStyleIndex::ProController;
+ return NpadStyleIndex::Fullkey;
case Settings::ControllerType::DualJoyconDetached:
return NpadStyleIndex::JoyconDual;
case Settings::ControllerType::LeftJoycon:
@@ -49,13 +49,13 @@ NpadStyleIndex EmulatedController::MapSettingsTypeToNPad(Settings::ControllerTyp
case Settings::ControllerType::SegaGenesis:
return NpadStyleIndex::SegaGenesis;
default:
- return NpadStyleIndex::ProController;
+ return NpadStyleIndex::Fullkey;
}
}
Settings::ControllerType EmulatedController::MapNPadToSettingsType(NpadStyleIndex type) {
switch (type) {
- case NpadStyleIndex::ProController:
+ case NpadStyleIndex::Fullkey:
return Settings::ControllerType::ProController;
case NpadStyleIndex::JoyconDual:
return Settings::ControllerType::DualJoyconDetached;
@@ -106,7 +106,7 @@ void EmulatedController::ReloadFromSettings() {
SetNpadStyleIndex(MapSettingsTypeToNPad(player.controller_type));
original_npad_type = npad_type;
} else {
- SetNpadStyleIndex(NpadStyleIndex::ProController);
+ SetNpadStyleIndex(NpadStyleIndex::Fullkey);
original_npad_type = npad_type;
}
@@ -1073,7 +1073,7 @@ void EmulatedController::SetColors(const Common::Input::CallbackStatus& callback
.body = GetNpadColor(controller.color_values[index].body),
.button = GetNpadColor(controller.color_values[index].buttons),
};
- if (npad_type == NpadStyleIndex::ProController) {
+ if (npad_type == NpadStyleIndex::Fullkey) {
controller.colors_state.left = {
.body = GetNpadColor(controller.color_values[index].left_grip),
.button = GetNpadColor(controller.color_values[index].buttons),
@@ -1356,7 +1356,7 @@ bool EmulatedController::HasNfc() const {
switch (npad_type) {
case NpadStyleIndex::JoyconRight:
case NpadStyleIndex::JoyconDual:
- case NpadStyleIndex::ProController:
+ case NpadStyleIndex::Fullkey:
case NpadStyleIndex::Handheld:
break;
default:
@@ -1548,7 +1548,7 @@ void EmulatedController::SetSupportedNpadStyleTag(NpadStyleTag supported_styles)
// Fallback Fullkey controllers to Pro controllers
if (IsControllerFullkey() && supported_style_tag.fullkey) {
LOG_WARNING(Service_HID, "Reconnecting controller type {} as Pro controller", npad_type);
- SetNpadStyleIndex(NpadStyleIndex::ProController);
+ SetNpadStyleIndex(NpadStyleIndex::Fullkey);
Connect();
return;
}
@@ -1556,13 +1556,13 @@ void EmulatedController::SetSupportedNpadStyleTag(NpadStyleTag supported_styles)
// Fallback Dual joycon controllers to Pro controllers
if (npad_type == NpadStyleIndex::JoyconDual && supported_style_tag.fullkey) {
LOG_WARNING(Service_HID, "Reconnecting controller type {} as Pro controller", npad_type);
- SetNpadStyleIndex(NpadStyleIndex::ProController);
+ SetNpadStyleIndex(NpadStyleIndex::Fullkey);
Connect();
return;
}
// Fallback Pro controllers to Dual joycon
- if (npad_type == NpadStyleIndex::ProController && supported_style_tag.joycon_dual) {
+ if (npad_type == NpadStyleIndex::Fullkey && supported_style_tag.joycon_dual) {
LOG_WARNING(Service_HID, "Reconnecting controller type {} as Dual Joycons", npad_type);
SetNpadStyleIndex(NpadStyleIndex::JoyconDual);
Connect();
@@ -1577,7 +1577,7 @@ bool EmulatedController::IsControllerFullkey(bool use_temporary_value) const {
std::scoped_lock lock{mutex};
const auto type = is_configuring && use_temporary_value ? tmp_npad_type : npad_type;
switch (type) {
- case NpadStyleIndex::ProController:
+ case NpadStyleIndex::Fullkey:
case NpadStyleIndex::GameCube:
case NpadStyleIndex::NES:
case NpadStyleIndex::SNES:
@@ -1593,7 +1593,7 @@ bool EmulatedController::IsControllerSupported(bool use_temporary_value) const {
std::scoped_lock lock{mutex};
const auto type = is_configuring && use_temporary_value ? tmp_npad_type : npad_type;
switch (type) {
- case NpadStyleIndex::ProController:
+ case NpadStyleIndex::Fullkey:
return supported_style_tag.fullkey.As<bool>();
case NpadStyleIndex::Handheld:
return supported_style_tag.handheld.As<bool>();
diff --git a/src/hid_core/hid_types.h b/src/hid_core/hid_types.h
index a81ed6af0..2c3f02f34 100644
--- a/src/hid_core/hid_types.h
+++ b/src/hid_core/hid_types.h
@@ -220,6 +220,7 @@ enum class NpadIdType : u32 {
};
enum class NpadInterfaceType : u8 {
+ None = 0,
Bluetooth = 1,
Rail = 2,
Usb = 3,
@@ -229,7 +230,7 @@ enum class NpadInterfaceType : u8 {
// This is nn::hid::NpadStyleIndex
enum class NpadStyleIndex : u8 {
None = 0,
- ProController = 3,
+ Fullkey = 3,
Handheld = 4,
HandheldNES = 4,
JoyconDual = 5,
diff --git a/src/hid_core/hid_util.h b/src/hid_core/hid_util.h
index 94ff2d23a..397a87472 100644
--- a/src/hid_core/hid_util.h
+++ b/src/hid_core/hid_util.h
@@ -42,7 +42,7 @@ constexpr Result IsSixaxisHandleValid(const Core::HID::SixAxisSensorHandle& hand
constexpr Result IsVibrationHandleValid(const Core::HID::VibrationDeviceHandle& handle) {
switch (handle.npad_type) {
- case Core::HID::NpadStyleIndex::ProController:
+ case Core::HID::NpadStyleIndex::Fullkey:
case Core::HID::NpadStyleIndex::Handheld:
case Core::HID::NpadStyleIndex::JoyconDual:
case Core::HID::NpadStyleIndex::JoyconLeft:
diff --git a/src/hid_core/resources/abstracted_pad/abstract_battery_handler.cpp b/src/hid_core/resources/abstracted_pad/abstract_battery_handler.cpp
new file mode 100644
index 000000000..62fbbb0a7
--- /dev/null
+++ b/src/hid_core/resources/abstracted_pad/abstract_battery_handler.cpp
@@ -0,0 +1,197 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "core/core_timing.h"
+#include "hid_core/hid_result.h"
+#include "hid_core/hid_util.h"
+#include "hid_core/resources/abstracted_pad/abstract_battery_handler.h"
+#include "hid_core/resources/abstracted_pad/abstract_pad_holder.h"
+#include "hid_core/resources/abstracted_pad/abstract_properties_handler.h"
+#include "hid_core/resources/applet_resource.h"
+#include "hid_core/resources/npad/npad_types.h"
+#include "hid_core/resources/shared_memory_format.h"
+
+namespace Service::HID {
+
+NpadAbstractBatteryHandler::NpadAbstractBatteryHandler() {}
+
+NpadAbstractBatteryHandler::~NpadAbstractBatteryHandler() = default;
+
+void NpadAbstractBatteryHandler::SetAbstractPadHolder(NpadAbstractedPadHolder* holder) {
+ abstract_pad_holder = holder;
+}
+
+void NpadAbstractBatteryHandler::SetAppletResource(AppletResourceHolder* applet_resource) {
+ applet_resource_holder = applet_resource;
+}
+
+void NpadAbstractBatteryHandler::SetPropertiesHandler(NpadAbstractPropertiesHandler* handler) {
+ properties_handler = handler;
+}
+
+Result NpadAbstractBatteryHandler::IncrementRefCounter() {
+ if (ref_counter == std::numeric_limits<s32>::max() - 1) {
+ return ResultNpadHandlerOverflow;
+ }
+ ref_counter++;
+ return ResultSuccess;
+}
+
+Result NpadAbstractBatteryHandler::DecrementRefCounter() {
+ if (ref_counter == 0) {
+ return ResultNpadHandlerNotInitialized;
+ }
+ ref_counter--;
+ return ResultSuccess;
+}
+
+Result NpadAbstractBatteryHandler::UpdateBatteryState(u64 aruid) {
+ const auto npad_index = NpadIdTypeToIndex(properties_handler->GetNpadId());
+ AruidData* aruid_data = applet_resource_holder->applet_resource->GetAruidData(aruid);
+ if (aruid_data == nullptr) {
+ return ResultSuccess;
+ }
+
+ auto& npad_internal_state =
+ aruid_data->shared_memory_format->npad.npad_entry[npad_index].internal_state;
+ auto& system_properties = npad_internal_state.system_properties;
+
+ system_properties.is_charging_joy_dual.Assign(dual_battery.is_charging);
+ system_properties.is_powered_joy_dual.Assign(dual_battery.is_powered);
+ system_properties.is_charging_joy_left.Assign(left_battery.is_charging);
+ system_properties.is_powered_joy_left.Assign(left_battery.is_powered);
+ system_properties.is_charging_joy_right.Assign(right_battery.is_charging);
+ system_properties.is_powered_joy_right.Assign(right_battery.is_powered);
+
+ npad_internal_state.battery_level_dual = dual_battery.battery_level;
+ npad_internal_state.battery_level_left = left_battery.battery_level;
+ npad_internal_state.battery_level_right = right_battery.battery_level;
+
+ return ResultSuccess;
+}
+
+void NpadAbstractBatteryHandler::UpdateBatteryState() {
+ if (ref_counter == 0) {
+ return;
+ }
+ has_new_battery_data = GetNewBatteryState();
+}
+
+bool NpadAbstractBatteryHandler::GetNewBatteryState() {
+ bool has_changed = false;
+ Core::HID::NpadPowerInfo new_dual_battery_state{};
+ Core::HID::NpadPowerInfo new_left_battery_state{};
+ Core::HID::NpadPowerInfo new_right_battery_state{};
+ std::array<IAbstractedPad*, 5> abstract_pads{};
+ const std::size_t count = abstract_pad_holder->GetAbstractedPads(abstract_pads);
+
+ for (std::size_t i = 0; i < count; i++) {
+ auto* abstract_pad = abstract_pads[i];
+ if (!abstract_pad->internal_flags.is_connected) {
+ continue;
+ }
+ const auto power_info = abstract_pad->power_info;
+ if (power_info.battery_level > Core::HID::NpadBatteryLevel::Full) {
+ // Abort
+ continue;
+ }
+
+ const auto style = abstract_pad->assignment_style;
+
+ if (style.is_external_assigned || style.is_handheld_assigned) {
+ new_dual_battery_state = power_info;
+ }
+ if (style.is_external_left_assigned || style.is_handheld_left_assigned) {
+ new_left_battery_state = power_info;
+ }
+ if (style.is_external_right_assigned || style.is_handheld_right_assigned) {
+ new_right_battery_state = power_info;
+ }
+
+ if (abstract_pad->internal_flags.is_battery_low_ovln_required) {
+ if (abstract_pad->interface_type == Core::HID::NpadInterfaceType::Rail) {
+ // TODO
+ }
+ abstract_pad->internal_flags.is_battery_low_ovln_required.Assign(false);
+ }
+ }
+
+ if (dual_battery.battery_level != new_dual_battery_state.battery_level ||
+ dual_battery.is_charging != new_dual_battery_state.is_charging ||
+ dual_battery.is_powered != new_dual_battery_state.is_powered) {
+ has_changed = true;
+ dual_battery = new_dual_battery_state;
+ }
+
+ if (left_battery.battery_level != new_left_battery_state.battery_level ||
+ left_battery.is_charging != new_left_battery_state.is_charging ||
+ left_battery.is_powered != new_left_battery_state.is_powered) {
+ has_changed = true;
+ left_battery = new_left_battery_state;
+ }
+
+ if (right_battery.battery_level != new_right_battery_state.battery_level ||
+ right_battery.is_charging != new_right_battery_state.is_charging ||
+ right_battery.is_powered != new_right_battery_state.is_powered) {
+ has_changed = true;
+ right_battery = new_right_battery_state;
+ }
+
+ return has_changed;
+}
+
+void NpadAbstractBatteryHandler::UpdateCoreBatteryState() {
+ if (ref_counter == 0) {
+ return;
+ }
+ if (!has_new_battery_data) {
+ return;
+ }
+
+ UpdateBatteryState(0);
+}
+
+void NpadAbstractBatteryHandler::InitializeBatteryState(u64 aruid) {
+ UpdateBatteryState(aruid);
+}
+
+bool NpadAbstractBatteryHandler::HasBattery() const {
+ std::array<IAbstractedPad*, 5> abstract_pads{};
+ const std::size_t count = abstract_pad_holder->GetAbstractedPads(abstract_pads);
+
+ for (std::size_t i = 0; i < count; i++) {
+ const auto* abstract_pad = abstract_pads[i];
+ if (!abstract_pad->internal_flags.is_connected) {
+ continue;
+ }
+ return abstract_pad->disabled_feature_set.has_fullkey_battery ||
+ abstract_pad->disabled_feature_set.has_left_right_joy_battery;
+ }
+
+ return false;
+}
+
+void NpadAbstractBatteryHandler::HasLeftRightBattery(bool& has_left, bool& has_right) const {
+ std::array<IAbstractedPad*, 5> abstract_pads{};
+ const std::size_t count = abstract_pad_holder->GetAbstractedPads(abstract_pads);
+
+ has_left = false;
+ has_right = false;
+
+ for (std::size_t i = 0; i < count; i++) {
+ const auto* abstract_pad = abstract_pads[i];
+ if (!abstract_pad->internal_flags.is_connected) {
+ continue;
+ }
+ if (!abstract_pad->disabled_feature_set.has_fullkey_battery &&
+ !abstract_pad->disabled_feature_set.has_left_right_joy_battery) {
+ continue;
+ }
+ has_left = abstract_pad->assignment_style.is_external_left_assigned ||
+ abstract_pad->assignment_style.is_handheld_left_assigned;
+ has_right = abstract_pad->assignment_style.is_external_right_assigned ||
+ abstract_pad->assignment_style.is_handheld_right_assigned;
+ }
+}
+
+} // namespace Service::HID
diff --git a/src/hid_core/resources/abstracted_pad/abstract_battery_handler.h b/src/hid_core/resources/abstracted_pad/abstract_battery_handler.h
new file mode 100644
index 000000000..85ac5eb72
--- /dev/null
+++ b/src/hid_core/resources/abstracted_pad/abstract_battery_handler.h
@@ -0,0 +1,49 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include "common/common_types.h"
+#include "core/hle/result.h"
+#include "hid_core/hid_types.h"
+
+namespace Service::HID {
+struct AppletResourceHolder;
+class NpadAbstractedPadHolder;
+class NpadAbstractPropertiesHandler;
+
+/// Handles Npad request from HID interfaces
+class NpadAbstractBatteryHandler final {
+public:
+ explicit NpadAbstractBatteryHandler();
+ ~NpadAbstractBatteryHandler();
+
+ void SetAbstractPadHolder(NpadAbstractedPadHolder* holder);
+ void SetAppletResource(AppletResourceHolder* applet_resource);
+ void SetPropertiesHandler(NpadAbstractPropertiesHandler* handler);
+
+ Result IncrementRefCounter();
+ Result DecrementRefCounter();
+
+ Result UpdateBatteryState(u64 aruid);
+ void UpdateBatteryState();
+ bool GetNewBatteryState();
+ void UpdateCoreBatteryState();
+ void InitializeBatteryState(u64 aruid);
+
+ bool HasBattery() const;
+ void HasLeftRightBattery(bool& has_left, bool& has_right) const;
+
+private:
+ AppletResourceHolder* applet_resource_holder{nullptr};
+ NpadAbstractedPadHolder* abstract_pad_holder{nullptr};
+ NpadAbstractPropertiesHandler* properties_handler{nullptr};
+
+ s32 ref_counter{};
+ Core::HID::NpadPowerInfo dual_battery{};
+ Core::HID::NpadPowerInfo left_battery{};
+ Core::HID::NpadPowerInfo right_battery{};
+ bool has_new_battery_data{};
+};
+
+} // namespace Service::HID
diff --git a/src/hid_core/resources/abstracted_pad/abstract_button_handler.cpp b/src/hid_core/resources/abstracted_pad/abstract_button_handler.cpp
new file mode 100644
index 000000000..587169433
--- /dev/null
+++ b/src/hid_core/resources/abstracted_pad/abstract_button_handler.cpp
@@ -0,0 +1,199 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "hid_core/hid_result.h"
+#include "hid_core/hid_util.h"
+#include "hid_core/resources/abstracted_pad/abstract_button_handler.h"
+#include "hid_core/resources/abstracted_pad/abstract_pad_holder.h"
+#include "hid_core/resources/abstracted_pad/abstract_properties_handler.h"
+#include "hid_core/resources/applet_resource.h"
+#include "hid_core/resources/npad/npad_resource.h"
+#include "hid_core/resources/npad/npad_types.h"
+#include "hid_core/resources/shared_memory_format.h"
+
+namespace Service::HID {
+
+NpadAbstractButtonHandler::NpadAbstractButtonHandler() {}
+
+NpadAbstractButtonHandler::~NpadAbstractButtonHandler() = default;
+
+void NpadAbstractButtonHandler::SetAbstractPadHolder(NpadAbstractedPadHolder* holder) {
+ abstract_pad_holder = holder;
+}
+
+void NpadAbstractButtonHandler::SetAppletResource(AppletResourceHolder* applet_resource) {
+ applet_resource_holder = applet_resource;
+}
+
+void NpadAbstractButtonHandler::SetPropertiesHandler(NpadAbstractPropertiesHandler* handler) {
+ properties_handler = handler;
+}
+
+Result NpadAbstractButtonHandler::IncrementRefCounter() {
+ if (ref_counter == std::numeric_limits<s32>::max() - 1) {
+ return ResultNpadHandlerOverflow;
+ }
+ ref_counter++;
+ return ResultSuccess;
+}
+
+Result NpadAbstractButtonHandler::DecrementRefCounter() {
+ if (ref_counter == 0) {
+ return ResultNpadHandlerNotInitialized;
+ }
+ ref_counter--;
+ return ResultSuccess;
+}
+
+Result NpadAbstractButtonHandler::UpdateAllButtonWithHomeProtection(u64 aruid) {
+ const Core::HID::NpadIdType npad_id = properties_handler->GetNpadId();
+ auto* data = applet_resource_holder->applet_resource->GetAruidData(aruid);
+
+ if (data == nullptr) {
+ return ResultSuccess;
+ }
+
+ auto& npad_entry = data->shared_memory_format->npad.npad_entry[NpadIdTypeToIndex(npad_id)];
+ UpdateButtonLifo(npad_entry, aruid);
+
+ bool is_home_button_protection_enabled{};
+ const auto result = applet_resource_holder->shared_npad_resource->GetHomeProtectionEnabled(
+ is_home_button_protection_enabled, aruid, npad_id);
+
+ if (result.IsError()) {
+ return ResultSuccess;
+ }
+
+ npad_entry.internal_state.button_properties.is_home_button_protection_enabled.Assign(
+ is_home_button_protection_enabled);
+
+ return ResultSuccess;
+}
+
+void NpadAbstractButtonHandler::UpdateAllButtonLifo() {
+ Core::HID::NpadIdType npad_id = properties_handler->GetNpadId();
+ for (std::size_t i = 0; i < AruidIndexMax; i++) {
+ auto* data = applet_resource_holder->applet_resource->GetAruidDataByIndex(i);
+ auto& npad_entry = data->shared_memory_format->npad.npad_entry[NpadIdTypeToIndex(npad_id)];
+ UpdateButtonLifo(npad_entry, data->aruid);
+ }
+}
+
+void NpadAbstractButtonHandler::UpdateCoreBatteryState() {
+ Core::HID::NpadIdType npad_id = properties_handler->GetNpadId();
+ for (std::size_t i = 0; i < AruidIndexMax; i++) {
+ auto* data = applet_resource_holder->applet_resource->GetAruidDataByIndex(i);
+ auto& npad_entry = data->shared_memory_format->npad.npad_entry[NpadIdTypeToIndex(npad_id)];
+ UpdateButtonLifo(npad_entry, data->aruid);
+ }
+}
+
+void NpadAbstractButtonHandler::UpdateButtonState(u64 aruid) {
+ Core::HID::NpadIdType npad_id = properties_handler->GetNpadId();
+ auto* data = applet_resource_holder->applet_resource->GetAruidData(aruid);
+ if (data == nullptr) {
+ return;
+ }
+ auto& npad_entry = data->shared_memory_format->npad.npad_entry[NpadIdTypeToIndex(npad_id)];
+ UpdateButtonLifo(npad_entry, aruid);
+}
+
+Result NpadAbstractButtonHandler::SetHomeProtection(bool is_enabled, u64 aruid) {
+ const Core::HID::NpadIdType npad_id = properties_handler->GetNpadId();
+ auto result = applet_resource_holder->shared_npad_resource->SetHomeProtectionEnabled(
+ aruid, npad_id, is_enabled);
+ if (result.IsError()) {
+ return result;
+ }
+
+ auto* data = applet_resource_holder->applet_resource->GetAruidData(aruid);
+ if (data == nullptr) {
+ return ResultSuccess;
+ }
+
+ bool is_home_protection_enabled{};
+ result = applet_resource_holder->shared_npad_resource->GetHomeProtectionEnabled(
+ is_home_protection_enabled, aruid, npad_id);
+ if (result.IsError()) {
+ return ResultSuccess;
+ }
+
+ auto& npad_entry = data->shared_memory_format->npad.npad_entry[NpadIdTypeToIndex(npad_id)];
+ npad_entry.internal_state.button_properties.is_home_button_protection_enabled.Assign(
+ is_home_protection_enabled);
+ return ResultSuccess;
+}
+
+bool NpadAbstractButtonHandler::IsButtonPressedOnConsoleMode() {
+ return is_button_pressed_on_console_mode;
+}
+
+void NpadAbstractButtonHandler::EnableCenterClamp() {
+ std::array<IAbstractedPad*, 5> abstract_pads{};
+ const std::size_t count = abstract_pad_holder->GetAbstractedPads(abstract_pads);
+
+ for (std::size_t i = 0; i < count; i++) {
+ auto* abstract_pad = abstract_pads[i];
+ if (!abstract_pad->internal_flags.is_connected) {
+ continue;
+ }
+ abstract_pad->internal_flags.use_center_clamp.Assign(true);
+ }
+}
+
+void NpadAbstractButtonHandler::UpdateButtonLifo(NpadSharedMemoryEntry& shared_memory, u64 aruid) {
+ auto* npad_resource = applet_resource_holder->shared_npad_resource;
+ Core::HID::NpadStyleTag style_tag = {properties_handler->GetStyleSet(aruid)};
+ style_tag.system_ext.Assign(npad_resource->GetActiveData()->GetNpadSystemExtState());
+
+ UpdateNpadFullkeyLifo(style_tag, 0, aruid, shared_memory);
+ UpdateHandheldLifo(style_tag, 1, aruid, shared_memory);
+ UpdateJoyconDualLifo(style_tag, 2, aruid, shared_memory);
+ UpdateJoyconLeftLifo(style_tag, 3, aruid, shared_memory);
+ UpdateJoyconRightLifo(style_tag, 4, aruid, shared_memory);
+ UpdatePalmaLifo(style_tag, 5, aruid, shared_memory);
+ UpdateSystemExtLifo(style_tag, 6, aruid, shared_memory);
+}
+
+void NpadAbstractButtonHandler::UpdateNpadFullkeyLifo(Core::HID::NpadStyleTag style_tag,
+ int style_index, u64 aruid,
+ NpadSharedMemoryEntry& shared_memory) {
+ // TODO
+}
+
+void NpadAbstractButtonHandler::UpdateHandheldLifo(Core::HID::NpadStyleTag style_tag,
+ int style_index, u64 aruid,
+ NpadSharedMemoryEntry& shared_memory) {
+ // TODO
+}
+
+void NpadAbstractButtonHandler::UpdateJoyconDualLifo(Core::HID::NpadStyleTag style_tag,
+ int style_index, u64 aruid,
+ NpadSharedMemoryEntry& shared_memory) {
+ // TODO
+}
+
+void NpadAbstractButtonHandler::UpdateJoyconLeftLifo(Core::HID::NpadStyleTag style_tag,
+ int style_index, u64 aruid,
+ NpadSharedMemoryEntry& shared_memory) {
+ // TODO
+}
+
+void NpadAbstractButtonHandler::UpdateJoyconRightLifo(Core::HID::NpadStyleTag style_tag,
+ int style_index, u64 aruid,
+ NpadSharedMemoryEntry& shared_memory) {
+ // TODO
+}
+
+void NpadAbstractButtonHandler::UpdateSystemExtLifo(Core::HID::NpadStyleTag style_tag,
+ int style_index, u64 aruid,
+ NpadSharedMemoryEntry& shared_memory) {
+ // TODO
+}
+
+void NpadAbstractButtonHandler::UpdatePalmaLifo(Core::HID::NpadStyleTag style_tag, int style_index,
+ u64 aruid, NpadSharedMemoryEntry& shared_memory) {
+ // TODO
+}
+
+} // namespace Service::HID
diff --git a/src/hid_core/resources/abstracted_pad/abstract_button_handler.h b/src/hid_core/resources/abstracted_pad/abstract_button_handler.h
new file mode 100644
index 000000000..01eafe96d
--- /dev/null
+++ b/src/hid_core/resources/abstracted_pad/abstract_button_handler.h
@@ -0,0 +1,75 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include "common/common_types.h"
+#include "core/hle/result.h"
+#include "hid_core/hid_types.h"
+
+namespace Service::HID {
+struct NpadSharedMemoryEntry;
+
+struct AppletResourceHolder;
+class NpadAbstractedPadHolder;
+class NpadAbstractPropertiesHandler;
+
+/// Handles Npad request from HID interfaces
+class NpadAbstractButtonHandler final {
+public:
+ explicit NpadAbstractButtonHandler();
+ ~NpadAbstractButtonHandler();
+
+ void SetAbstractPadHolder(NpadAbstractedPadHolder* holder);
+ void SetAppletResource(AppletResourceHolder* applet_resource);
+ void SetPropertiesHandler(NpadAbstractPropertiesHandler* handler);
+
+ Result IncrementRefCounter();
+ Result DecrementRefCounter();
+
+ Result UpdateAllButtonWithHomeProtection(u64 aruid);
+
+ void UpdateAllButtonLifo();
+ void UpdateCoreBatteryState();
+ void UpdateButtonState(u64 aruid);
+
+ Result SetHomeProtection(bool is_enabled, u64 aruid);
+ bool IsButtonPressedOnConsoleMode();
+ void EnableCenterClamp();
+
+ void UpdateButtonLifo(NpadSharedMemoryEntry& shared_memory, u64 aruid);
+
+ void UpdateNpadFullkeyLifo(Core::HID::NpadStyleTag style_tag, int index, u64 aruid,
+ NpadSharedMemoryEntry& shared_memory);
+ void UpdateHandheldLifo(Core::HID::NpadStyleTag style_tag, int index, u64 aruid,
+ NpadSharedMemoryEntry& shared_memory);
+ void UpdateJoyconDualLifo(Core::HID::NpadStyleTag style_tag, int index, u64 aruid,
+ NpadSharedMemoryEntry& shared_memory);
+ void UpdateJoyconLeftLifo(Core::HID::NpadStyleTag style_tag, int index, u64 aruid,
+ NpadSharedMemoryEntry& shared_memory);
+ void UpdateJoyconRightLifo(Core::HID::NpadStyleTag style_tag, int index, u64 aruid,
+ NpadSharedMemoryEntry& shared_memory);
+ void UpdateSystemExtLifo(Core::HID::NpadStyleTag style_tag, int index, u64 aruid,
+ NpadSharedMemoryEntry& shared_memory);
+ void UpdatePalmaLifo(Core::HID::NpadStyleTag style_tag, int index, u64 aruid,
+ NpadSharedMemoryEntry& shared_memory);
+
+private:
+ struct GcTrigger {
+ float left;
+ float right;
+ };
+
+ AppletResourceHolder* applet_resource_holder{nullptr};
+ NpadAbstractedPadHolder* abstract_pad_holder{nullptr};
+ NpadAbstractPropertiesHandler* properties_handler{nullptr};
+
+ s32 ref_counter{};
+
+ bool is_button_pressed_on_console_mode{};
+
+ u64 gc_sampling_number{};
+ GcTrigger gc_trigger_state{};
+};
+
+} // namespace Service::HID
diff --git a/src/hid_core/resources/abstracted_pad/abstract_ir_sensor_handler.cpp b/src/hid_core/resources/abstracted_pad/abstract_ir_sensor_handler.cpp
new file mode 100644
index 000000000..d4e4181bf
--- /dev/null
+++ b/src/hid_core/resources/abstracted_pad/abstract_ir_sensor_handler.cpp
@@ -0,0 +1,126 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "core/hle/kernel/k_event.h"
+#include "core/hle/kernel/k_readable_event.h"
+#include "hid_core/hid_result.h"
+#include "hid_core/resources/abstracted_pad/abstract_ir_sensor_handler.h"
+#include "hid_core/resources/abstracted_pad/abstract_pad_holder.h"
+#include "hid_core/resources/abstracted_pad/abstract_properties_handler.h"
+#include "hid_core/resources/npad/npad_types.h"
+
+namespace Service::HID {
+
+NpadAbstractIrSensorHandler::NpadAbstractIrSensorHandler() {}
+
+NpadAbstractIrSensorHandler::~NpadAbstractIrSensorHandler() = default;
+
+void NpadAbstractIrSensorHandler::SetAbstractPadHolder(NpadAbstractedPadHolder* holder) {
+ abstract_pad_holder = holder;
+}
+
+void NpadAbstractIrSensorHandler::SetPropertiesHandler(NpadAbstractPropertiesHandler* handler) {
+ properties_handler = handler;
+}
+
+Result NpadAbstractIrSensorHandler::IncrementRefCounter() {
+ if (ref_counter == std::numeric_limits<s32>::max() - 1) {
+ return ResultNpadHandlerOverflow;
+ }
+ ref_counter++;
+ return ResultSuccess;
+}
+
+Result NpadAbstractIrSensorHandler::DecrementRefCounter() {
+ if (ref_counter == 0) {
+ return ResultNpadHandlerNotInitialized;
+ }
+ ref_counter--;
+ return ResultSuccess;
+}
+
+void NpadAbstractIrSensorHandler::UpdateIrSensorState() {
+ const auto previous_state = sensor_state;
+ std::array<IAbstractedPad*, 5> abstract_pads{};
+ const std::size_t count = abstract_pad_holder->GetAbstractedPads(abstract_pads);
+
+ if (count == 0) {
+ sensor_state = NpadIrSensorState::Disabled;
+ if (sensor_state == previous_state) {
+ return;
+ }
+ ir_sensor_event->Signal();
+ return;
+ }
+
+ bool is_found{};
+ for (std::size_t i = 0; i < count; i++) {
+ auto* abstract_pad = abstract_pads[i];
+ if (!abstract_pad->internal_flags.is_connected) {
+ continue;
+ }
+ if (!abstract_pad->disabled_feature_set.has_bluetooth_address) {
+ continue;
+ }
+ is_found = true;
+ xcd_handle = abstract_pad->xcd_handle;
+ }
+
+ if (is_found) {
+ if (sensor_state == NpadIrSensorState::Active) {
+ return;
+ }
+ sensor_state = NpadIrSensorState::Available;
+ if (sensor_state == previous_state) {
+ return;
+ }
+ ir_sensor_event->Signal();
+ return;
+ }
+
+ sensor_state = NpadIrSensorState::Unavailable;
+ if (sensor_state == previous_state) {
+ return;
+ }
+
+ ir_sensor_event->Signal();
+ return;
+}
+
+Result NpadAbstractIrSensorHandler::ActivateIrSensor(bool is_enabled) {
+ if (sensor_state == NpadIrSensorState::Unavailable) {
+ return ResultIrSensorIsNotReady;
+ }
+ if (is_enabled && sensor_state == NpadIrSensorState::Available) {
+ sensor_state = NpadIrSensorState::Active;
+ } else {
+ if (is_enabled) {
+ return ResultSuccess;
+ }
+ if (sensor_state != NpadIrSensorState::Active) {
+ return ResultSuccess;
+ }
+ sensor_state = NpadIrSensorState::Available;
+ }
+ ir_sensor_event->Signal();
+ return ResultSuccess;
+}
+
+Result NpadAbstractIrSensorHandler::GetIrSensorEventHandle(Kernel::KReadableEvent** out_event) {
+ *out_event = &ir_sensor_event->GetReadableEvent();
+ return ResultSuccess;
+}
+
+Result NpadAbstractIrSensorHandler::GetXcdHandleForNpadWithIrSensor(u64& handle) const {
+ if (sensor_state < NpadIrSensorState::Available) {
+ return ResultIrSensorIsNotReady;
+ }
+ handle = xcd_handle;
+ return ResultSuccess;
+}
+
+NpadIrSensorState NpadAbstractIrSensorHandler::GetSensorState() const {
+ return sensor_state;
+}
+
+} // namespace Service::HID
diff --git a/src/hid_core/resources/abstracted_pad/abstract_ir_sensor_handler.h b/src/hid_core/resources/abstracted_pad/abstract_ir_sensor_handler.h
new file mode 100644
index 000000000..fe8e005af
--- /dev/null
+++ b/src/hid_core/resources/abstracted_pad/abstract_ir_sensor_handler.h
@@ -0,0 +1,56 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include "common/common_types.h"
+#include "core/hle/result.h"
+#include "hid_core/hid_types.h"
+
+namespace Kernel {
+class KEvent;
+class KReadableEvent;
+} // namespace Kernel
+
+enum class NpadIrSensorState : u32 {
+ Disabled,
+ Unavailable,
+ Available,
+ Active,
+};
+
+namespace Service::HID {
+class NpadAbstractedPadHolder;
+class NpadAbstractPropertiesHandler;
+
+/// Handles Npad request from HID interfaces
+class NpadAbstractIrSensorHandler final {
+public:
+ explicit NpadAbstractIrSensorHandler();
+ ~NpadAbstractIrSensorHandler();
+
+ void SetAbstractPadHolder(NpadAbstractedPadHolder* holder);
+ void SetPropertiesHandler(NpadAbstractPropertiesHandler* handler);
+
+ Result IncrementRefCounter();
+ Result DecrementRefCounter();
+
+ void UpdateIrSensorState();
+ Result ActivateIrSensor(bool param_2);
+
+ Result GetIrSensorEventHandle(Kernel::KReadableEvent** out_event);
+
+ Result GetXcdHandleForNpadWithIrSensor(u64& handle) const;
+
+ NpadIrSensorState GetSensorState() const;
+
+private:
+ NpadAbstractedPadHolder* abstract_pad_holder{nullptr};
+ NpadAbstractPropertiesHandler* properties_handler{nullptr};
+
+ s32 ref_counter{};
+ Kernel::KEvent* ir_sensor_event{nullptr};
+ u64 xcd_handle{};
+ NpadIrSensorState sensor_state{};
+};
+} // namespace Service::HID
diff --git a/src/hid_core/resources/abstracted_pad/abstract_led_handler.cpp b/src/hid_core/resources/abstracted_pad/abstract_led_handler.cpp
new file mode 100644
index 000000000..0b2bfe88d
--- /dev/null
+++ b/src/hid_core/resources/abstracted_pad/abstract_led_handler.cpp
@@ -0,0 +1,123 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "core/core_timing.h"
+#include "hid_core/hid_result.h"
+#include "hid_core/hid_util.h"
+#include "hid_core/resources/abstracted_pad/abstract_led_handler.h"
+#include "hid_core/resources/abstracted_pad/abstract_pad_holder.h"
+#include "hid_core/resources/abstracted_pad/abstract_properties_handler.h"
+#include "hid_core/resources/applet_resource.h"
+#include "hid_core/resources/npad/npad_types.h"
+
+namespace Service::HID {
+
+NpadAbstractLedHandler::NpadAbstractLedHandler() {}
+
+NpadAbstractLedHandler::~NpadAbstractLedHandler() = default;
+
+void NpadAbstractLedHandler::SetAbstractPadHolder(NpadAbstractedPadHolder* holder) {
+ abstract_pad_holder = holder;
+}
+
+void NpadAbstractLedHandler::SetAppletResource(AppletResourceHolder* applet_resource) {
+ applet_resource_holder = applet_resource;
+}
+
+void NpadAbstractLedHandler::SetPropertiesHandler(NpadAbstractPropertiesHandler* handler) {
+ properties_handler = handler;
+}
+
+Result NpadAbstractLedHandler::IncrementRefCounter() {
+ if (ref_counter == std::numeric_limits<s32>::max() - 1) {
+ return ResultNpadHandlerOverflow;
+ }
+ ref_counter++;
+ return ResultSuccess;
+}
+
+Result NpadAbstractLedHandler::DecrementRefCounter() {
+ if (ref_counter == 0) {
+ return ResultNpadHandlerNotInitialized;
+ }
+ ref_counter--;
+ return ResultSuccess;
+}
+
+void NpadAbstractLedHandler::SetNpadLedHandlerLedPattern() {
+ const auto npad_id = properties_handler->GetNpadId();
+
+ switch (npad_id) {
+ case Core::HID::NpadIdType::Player1:
+ left_pattern = Core::HID::LedPattern{1, 0, 0, 0};
+ break;
+ case Core::HID::NpadIdType::Player2:
+ left_pattern = Core::HID::LedPattern{1, 1, 0, 0};
+ break;
+ case Core::HID::NpadIdType::Player3:
+ left_pattern = Core::HID::LedPattern{1, 1, 1, 0};
+ break;
+ case Core::HID::NpadIdType::Player4:
+ left_pattern = Core::HID::LedPattern{1, 1, 1, 1};
+ break;
+ case Core::HID::NpadIdType::Player5:
+ left_pattern = Core::HID::LedPattern{1, 0, 0, 1};
+ break;
+ case Core::HID::NpadIdType::Player6:
+ left_pattern = Core::HID::LedPattern{1, 0, 1, 0};
+ break;
+ case Core::HID::NpadIdType::Player7:
+ left_pattern = Core::HID::LedPattern{1, 0, 1, 1};
+ break;
+ case Core::HID::NpadIdType::Player8:
+ left_pattern = Core::HID::LedPattern{0, 1, 1, 0};
+ break;
+ case Core::HID::NpadIdType::Other:
+ case Core::HID::NpadIdType::Handheld:
+ left_pattern = Core::HID::LedPattern{0, 0, 0, 0};
+ break;
+ default:
+ ASSERT_MSG(false, "Invalid npad id type");
+ break;
+ }
+
+ switch (npad_id) {
+ case Core::HID::NpadIdType::Player1:
+ right_pattern = Core::HID::LedPattern{0, 0, 0, 1};
+ break;
+ case Core::HID::NpadIdType::Player2:
+ right_pattern = Core::HID::LedPattern{0, 1, 1, 1};
+ break;
+ case Core::HID::NpadIdType::Player3:
+ right_pattern = Core::HID::LedPattern{0, 1, 1, 1};
+ break;
+ case Core::HID::NpadIdType::Player4:
+ right_pattern = Core::HID::LedPattern{1, 1, 1, 1};
+ break;
+ case Core::HID::NpadIdType::Player5:
+ right_pattern = Core::HID::LedPattern{1, 0, 0, 1};
+ break;
+ case Core::HID::NpadIdType::Player6:
+ right_pattern = Core::HID::LedPattern{0, 1, 0, 1};
+ break;
+ case Core::HID::NpadIdType::Player7:
+ right_pattern = Core::HID::LedPattern{1, 1, 0, 1};
+ break;
+ case Core::HID::NpadIdType::Player8:
+ right_pattern = Core::HID::LedPattern{0, 1, 1, 0};
+ break;
+ case Core::HID::NpadIdType::Other:
+ case Core::HID::NpadIdType::Handheld:
+ right_pattern = Core::HID::LedPattern{0, 0, 0, 0};
+ break;
+ default:
+ ASSERT_MSG(false, "Invalid npad id type");
+ break;
+ }
+}
+
+void NpadAbstractLedHandler::SetLedBlinkingDevice(Core::HID::LedPattern pattern) {
+ led_blinking = pattern;
+}
+
+} // namespace Service::HID
diff --git a/src/hid_core/resources/abstracted_pad/abstract_led_handler.h b/src/hid_core/resources/abstracted_pad/abstract_led_handler.h
new file mode 100644
index 000000000..09528129b
--- /dev/null
+++ b/src/hid_core/resources/abstracted_pad/abstract_led_handler.h
@@ -0,0 +1,43 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include "common/common_types.h"
+#include "core/hle/result.h"
+#include "hid_core/hid_types.h"
+
+namespace Service::HID {
+struct AppletResourceHolder;
+class NpadAbstractedPadHolder;
+class NpadAbstractPropertiesHandler;
+
+/// Handles Npad request from HID interfaces
+class NpadAbstractLedHandler final {
+public:
+ explicit NpadAbstractLedHandler();
+ ~NpadAbstractLedHandler();
+
+ void SetAbstractPadHolder(NpadAbstractedPadHolder* holder);
+ void SetAppletResource(AppletResourceHolder* applet_resource);
+ void SetPropertiesHandler(NpadAbstractPropertiesHandler* handler);
+
+ Result IncrementRefCounter();
+ Result DecrementRefCounter();
+
+ void SetNpadLedHandlerLedPattern();
+
+ void SetLedBlinkingDevice(Core::HID::LedPattern pattern);
+
+private:
+ AppletResourceHolder* applet_resource_holder{nullptr};
+ NpadAbstractedPadHolder* abstract_pad_holder{nullptr};
+ NpadAbstractPropertiesHandler* properties_handler{nullptr};
+
+ s32 ref_counter{};
+ Core::HID::LedPattern led_blinking{0, 0, 0, 0};
+ Core::HID::LedPattern left_pattern{0, 0, 0, 0};
+ Core::HID::LedPattern right_pattern{0, 0, 0, 0};
+ u64 led_interval{};
+};
+} // namespace Service::HID
diff --git a/src/hid_core/resources/abstracted_pad/abstract_mcu_handler.cpp b/src/hid_core/resources/abstracted_pad/abstract_mcu_handler.cpp
new file mode 100644
index 000000000..6f35bd95c
--- /dev/null
+++ b/src/hid_core/resources/abstracted_pad/abstract_mcu_handler.cpp
@@ -0,0 +1,108 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "hid_core/hid_result.h"
+#include "hid_core/resources/abstracted_pad/abstract_mcu_handler.h"
+#include "hid_core/resources/abstracted_pad/abstract_pad_holder.h"
+#include "hid_core/resources/abstracted_pad/abstract_properties_handler.h"
+#include "hid_core/resources/npad/npad_types.h"
+
+namespace Service::HID {
+
+NpadAbstractMcuHandler::NpadAbstractMcuHandler() {}
+
+NpadAbstractMcuHandler::~NpadAbstractMcuHandler() = default;
+
+void NpadAbstractMcuHandler::SetAbstractPadHolder(NpadAbstractedPadHolder* holder) {
+ abstract_pad_holder = holder;
+}
+
+void NpadAbstractMcuHandler::SetPropertiesHandler(NpadAbstractPropertiesHandler* handler) {
+ properties_handler = handler;
+}
+
+Result NpadAbstractMcuHandler::IncrementRefCounter() {
+ if (ref_counter == std::numeric_limits<s32>::max() - 1) {
+ return ResultNpadHandlerOverflow;
+ }
+ ref_counter++;
+ return ResultSuccess;
+}
+
+Result NpadAbstractMcuHandler::DecrementRefCounter() {
+ if (ref_counter == 0) {
+ return ResultNpadHandlerNotInitialized;
+ }
+ ref_counter--;
+ return ResultSuccess;
+}
+
+void NpadAbstractMcuHandler::UpdateMcuState() {
+ std::array<IAbstractedPad*, 5> abstract_pads{};
+ const std::size_t count = properties_handler->GetAbstractedPads(abstract_pads);
+
+ if (count == 0) {
+ mcu_holder = {};
+ return;
+ }
+
+ for (std::size_t i = 0; i < count; i++) {
+ auto* abstract_pad = abstract_pads[i];
+ if (!abstract_pad->internal_flags.is_connected) {
+ continue;
+ }
+ if (!abstract_pad->disabled_feature_set.has_left_joy_rail_bus) {
+ if (!abstract_pad->disabled_feature_set.has_left_joy_six_axis_sensor &&
+ !abstract_pad->disabled_feature_set.has_right_joy_six_axis_sensor) {
+ continue;
+ }
+ if (mcu_holder[1].state != NpadMcuState::Active) {
+ mcu_holder[1].state = NpadMcuState::Available;
+ }
+ mcu_holder[1].abstracted_pad = abstract_pad;
+ continue;
+ }
+ if (mcu_holder[0].state != NpadMcuState::Active) {
+ mcu_holder[0].state = NpadMcuState::Available;
+ }
+ mcu_holder[0].abstracted_pad = abstract_pad;
+ }
+}
+
+Result NpadAbstractMcuHandler::GetAbstractedPad(IAbstractedPad** data, u32 mcu_index) {
+ if (mcu_holder[mcu_index].state == NpadMcuState::None ||
+ mcu_holder[mcu_index].abstracted_pad == nullptr) {
+ return ResultMcuIsNotReady;
+ }
+ *data = mcu_holder[mcu_index].abstracted_pad;
+ return ResultSuccess;
+}
+
+NpadMcuState NpadAbstractMcuHandler::GetMcuState(u32 mcu_index) {
+ return mcu_holder[mcu_index].state;
+}
+
+Result NpadAbstractMcuHandler::SetMcuState(bool is_enabled, u32 mcu_index) {
+ NpadMcuState& state = mcu_holder[mcu_index].state;
+
+ if (state == NpadMcuState::None) {
+ return ResultMcuIsNotReady;
+ }
+
+ if ((is_enabled) && (state == NpadMcuState::Available)) {
+ state = NpadMcuState::Active;
+ return ResultSuccess;
+ }
+
+ if (is_enabled) {
+ return ResultSuccess;
+ }
+ if (state != NpadMcuState::Active) {
+ return ResultSuccess;
+ }
+
+ state = NpadMcuState::Available;
+ return ResultSuccess;
+}
+
+} // namespace Service::HID
diff --git a/src/hid_core/resources/abstracted_pad/abstract_mcu_handler.h b/src/hid_core/resources/abstracted_pad/abstract_mcu_handler.h
new file mode 100644
index 000000000..9902dd03a
--- /dev/null
+++ b/src/hid_core/resources/abstracted_pad/abstract_mcu_handler.h
@@ -0,0 +1,52 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include "common/common_types.h"
+#include "core/hle/result.h"
+#include "hid_core/hid_types.h"
+
+namespace Service::HID {
+struct IAbstractedPad;
+class NpadAbstractedPadHolder;
+class NpadAbstractPropertiesHandler;
+
+enum class NpadMcuState : u32 {
+ None,
+ Available,
+ Active,
+};
+
+struct NpadMcuHolder {
+ NpadMcuState state;
+ INSERT_PADDING_BYTES(0x4);
+ IAbstractedPad* abstracted_pad;
+};
+static_assert(sizeof(NpadMcuHolder) == 0x10, "NpadMcuHolder is an invalid size");
+
+/// Handles Npad request from HID interfaces
+class NpadAbstractMcuHandler final {
+public:
+ explicit NpadAbstractMcuHandler();
+ ~NpadAbstractMcuHandler();
+
+ void SetAbstractPadHolder(NpadAbstractedPadHolder* holder);
+ void SetPropertiesHandler(NpadAbstractPropertiesHandler* handler);
+
+ Result IncrementRefCounter();
+ Result DecrementRefCounter();
+
+ void UpdateMcuState();
+ Result GetAbstractedPad(IAbstractedPad** data, u32 mcu_index);
+ NpadMcuState GetMcuState(u32 mcu_index);
+ Result SetMcuState(bool is_enabled, u32 mcu_index);
+
+private:
+ NpadAbstractedPadHolder* abstract_pad_holder{nullptr};
+ NpadAbstractPropertiesHandler* properties_handler{nullptr};
+
+ s32 ref_counter{};
+ std::array<NpadMcuHolder, 2> mcu_holder{};
+};
+} // namespace Service::HID
diff --git a/src/hid_core/resources/abstracted_pad/abstract_nfc_handler.cpp b/src/hid_core/resources/abstracted_pad/abstract_nfc_handler.cpp
new file mode 100644
index 000000000..bd9b79333
--- /dev/null
+++ b/src/hid_core/resources/abstracted_pad/abstract_nfc_handler.cpp
@@ -0,0 +1,140 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "core/hle/kernel/k_event.h"
+#include "core/hle/kernel/k_readable_event.h"
+#include "hid_core/hid_result.h"
+#include "hid_core/resources/abstracted_pad/abstract_nfc_handler.h"
+#include "hid_core/resources/abstracted_pad/abstract_pad_holder.h"
+#include "hid_core/resources/abstracted_pad/abstract_properties_handler.h"
+#include "hid_core/resources/npad/npad_types.h"
+
+namespace Service::HID {
+
+NpadAbstractNfcHandler::NpadAbstractNfcHandler() {}
+
+NpadAbstractNfcHandler::~NpadAbstractNfcHandler() = default;
+
+void NpadAbstractNfcHandler::SetAbstractPadHolder(NpadAbstractedPadHolder* holder) {
+ abstract_pad_holder = holder;
+}
+
+void NpadAbstractNfcHandler::SetPropertiesHandler(NpadAbstractPropertiesHandler* handler) {
+ properties_handler = handler;
+}
+
+Result NpadAbstractNfcHandler::IncrementRefCounter() {
+ if (ref_counter == std::numeric_limits<s32>::max() - 1) {
+ return ResultNpadHandlerOverflow;
+ }
+ ref_counter++;
+ return ResultSuccess;
+}
+
+Result NpadAbstractNfcHandler::DecrementRefCounter() {
+ if (ref_counter == 0) {
+ return ResultNpadHandlerNotInitialized;
+ }
+ ref_counter--;
+ return ResultSuccess;
+}
+
+void NpadAbstractNfcHandler::UpdateNfcState() {
+ std::array<IAbstractedPad*, 5> abstract_pads{};
+ const std::size_t count = properties_handler->GetAbstractedPads(abstract_pads);
+
+ if (count == 0) {
+ if (sensor_state == NpadNfcState::Active) {
+ nfc_activate_event->Signal();
+ }
+ if (sensor_state == NpadNfcState::Unavailable) {
+ return;
+ }
+ sensor_state = NpadNfcState::Unavailable;
+ input_event->Signal();
+ return;
+ }
+
+ bool is_found{};
+ for (std::size_t i = 0; i < count; i++) {
+ auto* abstract_pad = abstract_pads[i];
+ if (!abstract_pad->internal_flags.is_connected) {
+ continue;
+ }
+ if (!abstract_pad->disabled_feature_set.has_nfc) {
+ continue;
+ }
+ is_found = true;
+ xcd_handle = 0;
+ }
+
+ if (is_found) {
+ if (sensor_state == NpadNfcState::Active) {
+ return;
+ }
+ if (sensor_state == NpadNfcState::Available) {
+ return;
+ }
+ sensor_state = NpadNfcState::Available;
+ input_event->Signal();
+ return;
+ }
+
+ if (sensor_state == NpadNfcState::Active) {
+ nfc_activate_event->Signal();
+ }
+ if (sensor_state == NpadNfcState::Unavailable) {
+ return;
+ }
+ sensor_state = NpadNfcState::Unavailable;
+ input_event->Signal();
+ return;
+}
+
+bool NpadAbstractNfcHandler::HasNfcSensor() {
+ return sensor_state != NpadNfcState::Unavailable;
+}
+
+bool NpadAbstractNfcHandler::IsNfcActivated() {
+ return sensor_state == NpadNfcState::Active;
+}
+
+Result NpadAbstractNfcHandler::GetAcquireNfcActivateEventHandle(
+ Kernel::KReadableEvent** out_event) {
+ *out_event = &nfc_activate_event->GetReadableEvent();
+ return ResultSuccess;
+}
+
+void NpadAbstractNfcHandler::SetInputEvent(Kernel::KEvent* event) {
+ input_event = event;
+}
+
+Result NpadAbstractNfcHandler::ActivateNfc(bool is_enabled) {
+ if (sensor_state == NpadNfcState::Active) {
+ return ResultNfcIsNotReady;
+ }
+
+ NpadNfcState new_state = NpadNfcState::Available;
+ if (is_enabled) {
+ new_state = NpadNfcState::Active;
+ }
+ if (sensor_state != new_state) {
+ sensor_state = new_state;
+ nfc_activate_event->Signal();
+ }
+ return ResultSuccess;
+}
+
+Result NpadAbstractNfcHandler::GetXcdHandleWithNfc(u64& out_xcd_handle) const {
+ if (sensor_state == NpadNfcState::Unavailable) {
+ return ResultNfcIsNotReady;
+ }
+ if (xcd_handle == 0) {
+ return ResultNfcXcdHandleIsNotInitialized;
+ }
+
+ out_xcd_handle = xcd_handle;
+ return ResultSuccess;
+}
+
+} // namespace Service::HID
diff --git a/src/hid_core/resources/abstracted_pad/abstract_nfc_handler.h b/src/hid_core/resources/abstracted_pad/abstract_nfc_handler.h
new file mode 100644
index 000000000..0702722a6
--- /dev/null
+++ b/src/hid_core/resources/abstracted_pad/abstract_nfc_handler.h
@@ -0,0 +1,57 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include "common/common_types.h"
+#include "core/hle/result.h"
+#include "hid_core/hid_types.h"
+
+namespace Kernel {
+class KReadableEvent;
+}
+
+enum class NpadNfcState : u32 {
+ Unavailable,
+ Available,
+ Active,
+};
+
+namespace Service::HID {
+class NpadAbstractedPadHolder;
+class NpadAbstractPropertiesHandler;
+
+/// Handles Npad request from HID interfaces
+class NpadAbstractNfcHandler final {
+public:
+ explicit NpadAbstractNfcHandler();
+ ~NpadAbstractNfcHandler();
+
+ void SetAbstractPadHolder(NpadAbstractedPadHolder* holder);
+ void SetPropertiesHandler(NpadAbstractPropertiesHandler* handler);
+
+ Result IncrementRefCounter();
+ Result DecrementRefCounter();
+
+ void UpdateNfcState();
+ bool HasNfcSensor();
+ bool IsNfcActivated();
+
+ Result GetAcquireNfcActivateEventHandle(Kernel::KReadableEvent** out_event);
+ void SetInputEvent(Kernel::KEvent* event);
+
+ Result ActivateNfc(bool is_enabled);
+
+ Result GetXcdHandleWithNfc(u64& out_xcd_handle) const;
+
+private:
+ NpadAbstractedPadHolder* abstract_pad_holder{nullptr};
+ NpadAbstractPropertiesHandler* properties_handler{nullptr};
+
+ s32 ref_counter{};
+ Kernel::KEvent* nfc_activate_event{nullptr};
+ Kernel::KEvent* input_event{nullptr};
+ u64 xcd_handle{};
+ NpadNfcState sensor_state{NpadNfcState::Unavailable};
+};
+} // namespace Service::HID
diff --git a/src/hid_core/resources/abstracted_pad/abstract_pad.cpp b/src/hid_core/resources/abstracted_pad/abstract_pad.cpp
new file mode 100644
index 000000000..2c7691d7c
--- /dev/null
+++ b/src/hid_core/resources/abstracted_pad/abstract_pad.cpp
@@ -0,0 +1,294 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "hid_core/hid_result.h"
+#include "hid_core/resources/abstracted_pad/abstract_pad.h"
+#include "hid_core/resources/applet_resource.h"
+#include "hid_core/resources/npad/npad_types.h"
+
+namespace Service::HID {
+
+AbstractPad::AbstractPad() {}
+
+AbstractPad::~AbstractPad() = default;
+
+void AbstractPad::SetExternals(AppletResourceHolder* applet_resource,
+ CaptureButtonResource* capture_button_resource,
+ HomeButtonResource* home_button_resource,
+ SixAxisResource* sixaxis_resource, PalmaResource* palma_resource,
+ VibrationHandler* vibration) {
+ applet_resource_holder = applet_resource;
+
+ properties_handler.SetAppletResource(applet_resource_holder);
+ properties_handler.SetAbstractPadHolder(&abstract_pad_holder);
+
+ led_handler.SetAppletResource(applet_resource_holder);
+ led_handler.SetAbstractPadHolder(&abstract_pad_holder);
+ led_handler.SetPropertiesHandler(&properties_handler);
+
+ ir_sensor_handler.SetAbstractPadHolder(&abstract_pad_holder);
+ ir_sensor_handler.SetPropertiesHandler(&properties_handler);
+
+ nfc_handler.SetAbstractPadHolder(&abstract_pad_holder);
+ nfc_handler.SetPropertiesHandler(&properties_handler);
+
+ mcu_handler.SetAbstractPadHolder(&abstract_pad_holder);
+ mcu_handler.SetPropertiesHandler(&properties_handler);
+
+ std::array<NpadVibrationDevice*, 2> vibration_devices{&vibration_left, &vibration_right};
+ vibration_handler.SetAppletResource(applet_resource_holder);
+ vibration_handler.SetAbstractPadHolder(&abstract_pad_holder);
+ vibration_handler.SetPropertiesHandler(&properties_handler);
+ vibration_handler.SetN64Vibration(&vibration_n64);
+ vibration_handler.SetVibration(vibration_devices);
+ vibration_handler.SetGcVibration(&vibration_gc);
+
+ sixaxis_handler.SetAppletResource(applet_resource_holder);
+ sixaxis_handler.SetAbstractPadHolder(&abstract_pad_holder);
+ sixaxis_handler.SetPropertiesHandler(&properties_handler);
+ sixaxis_handler.SetSixaxisResource(sixaxis_resource);
+
+ button_handler.SetAppletResource(applet_resource_holder);
+ button_handler.SetAbstractPadHolder(&abstract_pad_holder);
+ button_handler.SetPropertiesHandler(&properties_handler);
+
+ battery_handler.SetAppletResource(applet_resource_holder);
+ battery_handler.SetAbstractPadHolder(&abstract_pad_holder);
+ battery_handler.SetPropertiesHandler(&properties_handler);
+
+ palma_handler.SetAbstractPadHolder(&abstract_pad_holder);
+ palma_handler.SetPropertiesHandler(&properties_handler);
+ palma_handler.SetPalmaResource(palma_resource);
+}
+
+void AbstractPad::SetNpadId(Core::HID::NpadIdType npad_id) {
+ properties_handler.SetNpadId(npad_id);
+}
+
+Result AbstractPad::Activate() {
+ if (ref_counter == std::numeric_limits<s32>::max() - 1) {
+ return ResultNpadHandlerOverflow;
+ }
+
+ if (ref_counter != 0) {
+ ref_counter++;
+ return ResultSuccess;
+ }
+
+ std::size_t stage = 0;
+ Result result = ResultSuccess;
+
+ if (result.IsSuccess()) {
+ stage++;
+ result = properties_handler.IncrementRefCounter();
+ }
+ if (result.IsSuccess()) {
+ stage++;
+ result = led_handler.IncrementRefCounter();
+ }
+ if (result.IsSuccess()) {
+ stage++;
+ result = ir_sensor_handler.IncrementRefCounter();
+ }
+ if (result.IsSuccess()) {
+ stage++;
+ result = mcu_handler.IncrementRefCounter();
+ }
+ if (result.IsSuccess()) {
+ stage++;
+ result = nfc_handler.IncrementRefCounter();
+ }
+ if (result.IsSuccess()) {
+ stage++;
+ result = vibration_handler.IncrementRefCounter();
+ }
+ if (result.IsSuccess()) {
+ stage++;
+ result = sixaxis_handler.IncrementRefCounter();
+ }
+ if (result.IsSuccess()) {
+ stage++;
+ result = button_handler.IncrementRefCounter();
+ }
+ if (result.IsSuccess()) {
+ stage++;
+ result = battery_handler.IncrementRefCounter();
+ }
+ if (result.IsSuccess()) {
+ stage++;
+ result = palma_handler.IncrementRefCounter();
+ }
+
+ if (result.IsSuccess()) {
+ ref_counter++;
+ return result;
+ }
+
+ if (stage > 9) {
+ battery_handler.DecrementRefCounter();
+ }
+ if (stage > 8) {
+ button_handler.DecrementRefCounter();
+ }
+ if (stage > 7) {
+ sixaxis_handler.DecrementRefCounter();
+ }
+ if (stage > 6) {
+ vibration_handler.DecrementRefCounter();
+ }
+ if (stage > 5) {
+ nfc_handler.DecrementRefCounter();
+ }
+ if (stage > 4) {
+ mcu_handler.DecrementRefCounter();
+ }
+ if (stage > 3) {
+ ir_sensor_handler.DecrementRefCounter();
+ }
+ if (stage > 2) {
+ led_handler.DecrementRefCounter();
+ }
+ if (stage > 1) {
+ properties_handler.DecrementRefCounter();
+ }
+ return result;
+}
+
+Result AbstractPad::Deactivate() {
+ if (ref_counter == 0) {
+ return ResultNpadResourceNotInitialized;
+ }
+
+ ref_counter--;
+ battery_handler.DecrementRefCounter();
+ button_handler.DecrementRefCounter();
+ sixaxis_handler.DecrementRefCounter();
+ vibration_handler.DecrementRefCounter();
+ nfc_handler.DecrementRefCounter();
+ ir_sensor_handler.DecrementRefCounter();
+ mcu_handler.DecrementRefCounter();
+ led_handler.DecrementRefCounter();
+ properties_handler.DecrementRefCounter();
+ palma_handler.DecrementRefCounter();
+
+ return ResultSuccess;
+}
+
+Result AbstractPad::ActivateNpad(u64 aruid) {
+ Result result = ResultSuccess;
+ if (result.IsSuccess()) {
+ result = properties_handler.ActivateNpadUnknown0x88(aruid);
+ }
+ if (result.IsSuccess()) {
+ result = sixaxis_handler.UpdateSixAxisState2(aruid);
+ }
+ if (result.IsSuccess()) {
+ result = battery_handler.UpdateBatteryState(aruid);
+ }
+ return result;
+}
+
+NpadAbstractedPadHolder* AbstractPad::GetAbstractedPadHolder() {
+ return &abstract_pad_holder;
+}
+
+NpadAbstractPropertiesHandler* AbstractPad::GetAbstractPropertiesHandler() {
+ return &properties_handler;
+}
+
+NpadAbstractLedHandler* AbstractPad::GetAbstractLedHandler() {
+ return &led_handler;
+}
+
+NpadAbstractIrSensorHandler* AbstractPad::GetAbstractIrSensorHandler() {
+ return &ir_sensor_handler;
+}
+
+NpadAbstractMcuHandler* AbstractPad::GetAbstractMcuHandler() {
+ return &mcu_handler;
+}
+
+NpadAbstractNfcHandler* AbstractPad::GetAbstractNfcHandler() {
+ return &nfc_handler;
+}
+
+NpadAbstractVibrationHandler* AbstractPad::GetAbstractVibrationHandler() {
+ return &vibration_handler;
+}
+
+NpadAbstractSixAxisHandler* AbstractPad::GetAbstractSixAxisHandler() {
+ return &sixaxis_handler;
+}
+
+NpadAbstractButtonHandler* AbstractPad::GetAbstractButtonHandler() {
+ return &button_handler;
+}
+
+NpadAbstractBatteryHandler* AbstractPad::GetAbstractBatteryHandler() {
+ return &battery_handler;
+}
+
+NpadN64VibrationDevice* AbstractPad::GetN64VibrationDevice() {
+ return &vibration_n64;
+}
+
+NpadVibrationDevice* AbstractPad::GetVibrationDevice(Core::HID::DeviceIndex device_index) {
+ if (device_index == Core::HID::DeviceIndex::Right) {
+ return &vibration_right;
+ }
+ return &vibration_left;
+}
+
+void AbstractPad::GetLeftRightVibrationDevice(std::vector<NpadVibrationDevice*> list) {
+ list.emplace_back(&vibration_left);
+ list.emplace_back(&vibration_right);
+}
+
+NpadGcVibrationDevice* AbstractPad::GetGCVibrationDevice() {
+ return &vibration_gc;
+}
+
+Core::HID::NpadIdType AbstractPad::GetLastActiveNpad() {
+ return properties_handler.GetNpadId();
+}
+
+void AbstractPad::UpdateInterfaceType() {
+ if (interface_type != properties_handler.GetInterfaceType()) {
+ Update();
+ }
+ battery_handler.UpdateBatteryState();
+}
+
+void AbstractPad::Update() {
+ properties_handler.UpdateDeviceType();
+ led_handler.SetNpadLedHandlerLedPattern();
+ vibration_handler.UpdateVibrationState();
+ sixaxis_handler.UpdateSixAxisState();
+ nfc_handler.UpdateNfcState();
+ ir_sensor_handler.UpdateIrSensorState();
+ mcu_handler.UpdateMcuState();
+ palma_handler.UpdatePalmaState();
+ battery_handler.UpdateBatteryState();
+ button_handler.EnableCenterClamp();
+
+ interface_type = properties_handler.GetInterfaceType();
+
+ std::scoped_lock lock{*applet_resource_holder->shared_mutex};
+ properties_handler.UpdateAllDeviceProperties();
+ battery_handler.UpdateCoreBatteryState();
+ button_handler.UpdateCoreBatteryState();
+}
+
+void AbstractPad::UpdatePadState() {
+ button_handler.UpdateAllButtonLifo();
+ sixaxis_handler.UpdateSixAxisState();
+ battery_handler.UpdateCoreBatteryState();
+}
+
+void AbstractPad::EnableAppletToGetInput(u64 aruid) {
+ button_handler.UpdateButtonState(aruid);
+ sixaxis_handler.UpdateSixAxisState(aruid);
+ battery_handler.UpdateBatteryState(aruid);
+}
+
+} // namespace Service::HID
diff --git a/src/hid_core/resources/abstracted_pad/abstract_pad.h b/src/hid_core/resources/abstracted_pad/abstract_pad.h
new file mode 100644
index 000000000..cbdf84af7
--- /dev/null
+++ b/src/hid_core/resources/abstracted_pad/abstract_pad.h
@@ -0,0 +1,123 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include <array>
+#include <mutex>
+
+#include "common/common_types.h"
+#include "core/hle/result.h"
+#include "hid_core/hid_types.h"
+#include "hid_core/resources/applet_resource.h"
+#include "hid_core/resources/npad/npad_types.h"
+
+#include "hid_core/resources/abstracted_pad/abstract_battery_handler.h"
+#include "hid_core/resources/abstracted_pad/abstract_button_handler.h"
+#include "hid_core/resources/abstracted_pad/abstract_ir_sensor_handler.h"
+#include "hid_core/resources/abstracted_pad/abstract_led_handler.h"
+#include "hid_core/resources/abstracted_pad/abstract_mcu_handler.h"
+#include "hid_core/resources/abstracted_pad/abstract_nfc_handler.h"
+#include "hid_core/resources/abstracted_pad/abstract_pad_holder.h"
+#include "hid_core/resources/abstracted_pad/abstract_palma_handler.h"
+#include "hid_core/resources/abstracted_pad/abstract_properties_handler.h"
+#include "hid_core/resources/abstracted_pad/abstract_sixaxis_handler.h"
+#include "hid_core/resources/abstracted_pad/abstract_vibration_handler.h"
+#include "hid_core/resources/vibration/gc_vibration_device.h"
+#include "hid_core/resources/vibration/n64_vibration_device.h"
+#include "hid_core/resources/vibration/vibration_device.h"
+
+namespace Service::HID {
+class AppletResource;
+class SixAxisResource;
+class PalmaResource;
+class NPadResource;
+class AbstractPad;
+class NpadLastActiveHandler;
+class NpadIrNfcHandler;
+class UniquePads;
+class NpadPalmaHandler;
+class FirmwareResource;
+class NpadVibration;
+class NpadHighestBattery;
+class NpadGcVibration;
+
+class CaptureButtonResource;
+class HomeButtonResource;
+class VibrationHandler;
+
+struct HandheldConfig;
+
+/// Handles Npad request from HID interfaces
+class AbstractPad final {
+public:
+ explicit AbstractPad();
+ ~AbstractPad();
+
+ void SetExternals(AppletResourceHolder* applet_resource,
+ CaptureButtonResource* capture_button_resource,
+ HomeButtonResource* home_button_resource, SixAxisResource* sixaxis_resource,
+ PalmaResource* palma_resource, VibrationHandler* vibration);
+ void SetNpadId(Core::HID::NpadIdType npad_id);
+
+ Result Activate();
+ Result Deactivate();
+
+ Result ActivateNpad(u64 aruid);
+
+ NpadAbstractedPadHolder* GetAbstractedPadHolder();
+ NpadAbstractPropertiesHandler* GetAbstractPropertiesHandler();
+ NpadAbstractLedHandler* GetAbstractLedHandler();
+ NpadAbstractIrSensorHandler* GetAbstractIrSensorHandler();
+ NpadAbstractMcuHandler* GetAbstractMcuHandler();
+ NpadAbstractNfcHandler* GetAbstractNfcHandler();
+ NpadAbstractVibrationHandler* GetAbstractVibrationHandler();
+ NpadAbstractSixAxisHandler* GetAbstractSixAxisHandler();
+ NpadAbstractButtonHandler* GetAbstractButtonHandler();
+ NpadAbstractBatteryHandler* GetAbstractBatteryHandler();
+
+ NpadN64VibrationDevice* GetN64VibrationDevice();
+ NpadVibrationDevice* GetVibrationDevice(Core::HID::DeviceIndex device_index);
+ void GetLeftRightVibrationDevice(std::vector<NpadVibrationDevice*> list);
+ NpadGcVibrationDevice* GetGCVibrationDevice();
+
+ Core::HID::NpadIdType GetLastActiveNpad();
+ void UpdateInterfaceType();
+ void Update();
+
+ void UpdatePadState();
+ void EnableAppletToGetInput(u64 aruid);
+
+private:
+ AppletResourceHolder* applet_resource_holder{nullptr};
+ NpadAbstractedPadHolder abstract_pad_holder{};
+ NpadAbstractPropertiesHandler properties_handler{};
+ NpadAbstractLedHandler led_handler{};
+ NpadAbstractIrSensorHandler ir_sensor_handler{};
+ NpadAbstractNfcHandler nfc_handler{};
+ NpadAbstractMcuHandler mcu_handler{};
+ NpadAbstractVibrationHandler vibration_handler{};
+ NpadAbstractSixAxisHandler sixaxis_handler{};
+ NpadAbstractButtonHandler button_handler{};
+ NpadAbstractBatteryHandler battery_handler{};
+ NpadAbstractPalmaHandler palma_handler{};
+
+ NpadN64VibrationDevice vibration_n64{};
+ NpadVibrationDevice vibration_left{};
+ NpadVibrationDevice vibration_right{};
+ NpadGcVibrationDevice vibration_gc{};
+
+ // SixAxisConfigHolder fullkey_config;
+ // SixAxisConfigHolder handheld_config;
+ // SixAxisConfigHolder dual_left_config;
+ // SixAxisConfigHolder dual_right_config;
+ // SixAxisConfigHolder left_config;
+ // SixAxisConfigHolder right_config;
+
+ s32 ref_counter{};
+ Core::HID::NpadInterfaceType interface_type{Core::HID::NpadInterfaceType::None};
+};
+
+using FullAbstractPad = std::array<AbstractPad, MaxSupportedNpadIdTypes>;
+
+} // namespace Service::HID
diff --git a/src/hid_core/resources/abstracted_pad/abstract_pad_holder.cpp b/src/hid_core/resources/abstracted_pad/abstract_pad_holder.cpp
new file mode 100644
index 000000000..8334dc34f
--- /dev/null
+++ b/src/hid_core/resources/abstracted_pad/abstract_pad_holder.cpp
@@ -0,0 +1,99 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "hid_core/hid_result.h"
+#include "hid_core/resources/abstracted_pad/abstract_pad_holder.h"
+#include "hid_core/resources/npad/npad_types.h"
+
+namespace Service::HID {
+
+Result NpadAbstractedPadHolder::RegisterAbstractPad(IAbstractedPad* abstracted_pad) {
+ if (list_size >= assignment_list.size()) {
+ return ResultNpadIsNotProController;
+ }
+
+ for (std::size_t i = 0; i < list_size; i++) {
+ if (assignment_list[i].device_type == abstracted_pad->device_type) {
+ return ResultNpadIsNotProController;
+ }
+ }
+
+ assignment_list[list_size] = {
+ .abstracted_pad = abstracted_pad,
+ .device_type = abstracted_pad->device_type,
+ .interface_type = abstracted_pad->interface_type,
+ .controller_id = abstracted_pad->controller_id,
+ };
+
+ list_size++;
+ return ResultSuccess;
+}
+
+void NpadAbstractedPadHolder::RemoveAbstractPadByControllerId(u64 controller_id) {
+ if (list_size == 0) {
+ return;
+ }
+ if (controller_id == 0) {
+ return;
+ }
+ for (std::size_t i = 0; i < list_size; i++) {
+ if (assignment_list[i].controller_id != controller_id) {
+ continue;
+ }
+ for (std::size_t e = i + 1; e < list_size; e++) {
+ assignment_list[e - 1] = assignment_list[e];
+ }
+ list_size--;
+ return;
+ }
+}
+
+void NpadAbstractedPadHolder::DetachAbstractedPad() {
+ while (list_size > 0) {
+ for (std::size_t i = 1; i < list_size; i++) {
+ assignment_list[i - 1] = assignment_list[i];
+ }
+ list_size--;
+ }
+}
+
+u64 NpadAbstractedPadHolder::RemoveAbstractPadByAssignmentStyle(
+ Service::HID::AssignmentStyle assignment_style) {
+ for (std::size_t i = 0; i < list_size; i++) {
+ if ((assignment_style.raw & assignment_list[i].abstracted_pad->assignment_style.raw) == 0) {
+ continue;
+ }
+ for (std::size_t e = i + 1; e < list_size; e++) {
+ assignment_list[e - 1] = assignment_list[e];
+ }
+ list_size--;
+ return list_size;
+ }
+ return list_size;
+}
+
+u32 NpadAbstractedPadHolder::GetAbstractedPads(std::span<IAbstractedPad*> list) const {
+ u32 num_elements = std::min(static_cast<u32>(list.size()), list_size);
+ for (std::size_t i = 0; i < num_elements; i++) {
+ list[i] = assignment_list[i].abstracted_pad;
+ }
+ return num_elements;
+}
+
+void NpadAbstractedPadHolder::SetAssignmentMode(const NpadJoyAssignmentMode& mode) {
+ assignment_mode = mode;
+}
+
+NpadJoyAssignmentMode NpadAbstractedPadHolder::GetAssignmentMode() const {
+ return assignment_mode;
+}
+
+std::size_t NpadAbstractedPadHolder::GetStyleIndexList(
+ std::span<Core::HID::NpadStyleIndex> list) const {
+ for (std::size_t i = 0; i < list_size; i++) {
+ list[i] = assignment_list[i].device_type;
+ }
+ return list_size;
+}
+
+} // namespace Service::HID
diff --git a/src/hid_core/resources/abstracted_pad/abstract_pad_holder.h b/src/hid_core/resources/abstracted_pad/abstract_pad_holder.h
new file mode 100644
index 000000000..fb7f472e8
--- /dev/null
+++ b/src/hid_core/resources/abstracted_pad/abstract_pad_holder.h
@@ -0,0 +1,47 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include <array>
+#include <mutex>
+#include <span>
+
+#include "common/common_types.h"
+#include "core/hle/result.h"
+#include "hid_core/hid_types.h"
+#include "hid_core/resources/npad/npad_types.h"
+
+namespace Service::HID {
+struct IAbstractedPad;
+
+struct AbstractAssignmentHolder {
+ IAbstractedPad* abstracted_pad;
+ Core::HID::NpadStyleIndex device_type;
+ Core::HID::NpadInterfaceType interface_type;
+ INSERT_PADDING_BYTES(0x6);
+ u64 controller_id;
+};
+static_assert(sizeof(AbstractAssignmentHolder) == 0x18,
+ "AbstractAssignmentHolder is an invalid size");
+
+/// This is nn::hid::server::NpadAbstractedPadHolder
+class NpadAbstractedPadHolder final {
+public:
+ Result RegisterAbstractPad(IAbstractedPad* abstracted_pad);
+ void RemoveAbstractPadByControllerId(u64 controller_id);
+ void DetachAbstractedPad();
+ u64 RemoveAbstractPadByAssignmentStyle(Service::HID::AssignmentStyle assignment_style);
+ u32 GetAbstractedPads(std::span<IAbstractedPad*> list) const;
+
+ void SetAssignmentMode(const NpadJoyAssignmentMode& mode);
+ NpadJoyAssignmentMode GetAssignmentMode() const;
+
+ std::size_t GetStyleIndexList(std::span<Core::HID::NpadStyleIndex> list) const;
+
+private:
+ std::array<AbstractAssignmentHolder, 5> assignment_list{};
+ u32 list_size{};
+ NpadJoyAssignmentMode assignment_mode{NpadJoyAssignmentMode::Dual};
+};
+} // namespace Service::HID
diff --git a/src/hid_core/resources/abstracted_pad/abstract_palma_handler.cpp b/src/hid_core/resources/abstracted_pad/abstract_palma_handler.cpp
new file mode 100644
index 000000000..04d276d61
--- /dev/null
+++ b/src/hid_core/resources/abstracted_pad/abstract_palma_handler.cpp
@@ -0,0 +1,47 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "hid_core/hid_result.h"
+#include "hid_core/resources/abstracted_pad/abstract_palma_handler.h"
+#include "hid_core/resources/abstracted_pad/abstract_properties_handler.h"
+
+namespace Service::HID {
+
+NpadAbstractPalmaHandler::NpadAbstractPalmaHandler() {}
+
+NpadAbstractPalmaHandler::~NpadAbstractPalmaHandler() = default;
+
+void NpadAbstractPalmaHandler::SetAbstractPadHolder(NpadAbstractedPadHolder* holder) {
+ abstract_pad_holder = holder;
+}
+
+void NpadAbstractPalmaHandler::SetPropertiesHandler(NpadAbstractPropertiesHandler* handler) {
+ properties_handler = handler;
+ return;
+}
+
+void NpadAbstractPalmaHandler::SetPalmaResource(PalmaResource* resource) {
+ palma_resource = resource;
+}
+
+Result NpadAbstractPalmaHandler::IncrementRefCounter() {
+ if (ref_counter == std::numeric_limits<s32>::max() - 1) {
+ return ResultNpadHandlerOverflow;
+ }
+ ref_counter++;
+ return ResultSuccess;
+}
+
+Result NpadAbstractPalmaHandler::DecrementRefCounter() {
+ if (ref_counter == 0) {
+ return ResultNpadHandlerNotInitialized;
+ }
+ ref_counter--;
+ return ResultSuccess;
+}
+
+void NpadAbstractPalmaHandler::UpdatePalmaState() {
+ // TODO
+}
+
+} // namespace Service::HID
diff --git a/src/hid_core/resources/abstracted_pad/abstract_palma_handler.h b/src/hid_core/resources/abstracted_pad/abstract_palma_handler.h
new file mode 100644
index 000000000..fbd2e67e5
--- /dev/null
+++ b/src/hid_core/resources/abstracted_pad/abstract_palma_handler.h
@@ -0,0 +1,37 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include "common/common_types.h"
+#include "core/hle/result.h"
+#include "hid_core/hid_types.h"
+
+namespace Service::HID {
+class NpadAbstractedPadHolder;
+class NpadAbstractPropertiesHandler;
+class PalmaResource;
+
+class NpadAbstractPalmaHandler final {
+public:
+ explicit NpadAbstractPalmaHandler();
+ ~NpadAbstractPalmaHandler();
+
+ void SetAbstractPadHolder(NpadAbstractedPadHolder* holder);
+ void SetPropertiesHandler(NpadAbstractPropertiesHandler* handler);
+ void SetPalmaResource(PalmaResource* resource);
+
+ Result IncrementRefCounter();
+ Result DecrementRefCounter();
+
+ void UpdatePalmaState();
+
+private:
+ NpadAbstractedPadHolder* abstract_pad_holder{nullptr};
+ NpadAbstractPropertiesHandler* properties_handler{nullptr};
+ PalmaResource* palma_resource{nullptr};
+
+ s32 ref_counter{};
+};
+
+} // namespace Service::HID
diff --git a/src/hid_core/resources/abstracted_pad/abstract_properties_handler.cpp b/src/hid_core/resources/abstracted_pad/abstract_properties_handler.cpp
new file mode 100644
index 000000000..4897a2784
--- /dev/null
+++ b/src/hid_core/resources/abstracted_pad/abstract_properties_handler.cpp
@@ -0,0 +1,322 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "hid_core/hid_util.h"
+#include "hid_core/resources/abstracted_pad/abstract_pad_holder.h"
+#include "hid_core/resources/abstracted_pad/abstract_properties_handler.h"
+#include "hid_core/resources/applet_resource.h"
+#include "hid_core/resources/npad/npad_resource.h"
+#include "hid_core/resources/npad/npad_types.h"
+#include "hid_core/resources/shared_memory_format.h"
+
+namespace Service::HID {
+
+NpadAbstractPropertiesHandler::NpadAbstractPropertiesHandler() {}
+
+NpadAbstractPropertiesHandler::~NpadAbstractPropertiesHandler() = default;
+
+void NpadAbstractPropertiesHandler::SetAbstractPadHolder(NpadAbstractedPadHolder* holder) {
+ abstract_pad_holder = holder;
+ return;
+}
+
+void NpadAbstractPropertiesHandler::SetAppletResource(AppletResourceHolder* applet_resource) {
+ applet_resource_holder = applet_resource;
+ return;
+}
+
+void NpadAbstractPropertiesHandler::SetNpadId(Core::HID::NpadIdType npad_id) {
+ if (!IsNpadIdValid(npad_id)) {
+ ASSERT_MSG(false, "Invalid npad id");
+ }
+
+ npad_id_type = npad_id;
+}
+
+Core::HID::NpadIdType NpadAbstractPropertiesHandler::GetNpadId() const {
+ return npad_id_type;
+}
+
+Result NpadAbstractPropertiesHandler::IncrementRefCounter() {
+ if (ref_counter == std::numeric_limits<s32>::max() - 1) {
+ return ResultNpadHandlerOverflow;
+ }
+
+ if (ref_counter != 0) {
+ ref_counter++;
+ return ResultSuccess;
+ }
+
+ const auto npad_index = NpadIdTypeToIndex(npad_id_type);
+ for (std::size_t aruid_index = 0; aruid_index < AruidIndexMax; aruid_index++) {
+ auto* data = applet_resource_holder->applet_resource->GetAruidData(aruid_index);
+ auto& internal_state =
+ data->shared_memory_format->npad.npad_entry[npad_index].internal_state;
+ if (!data->flag.is_assigned) {
+ continue;
+ }
+ internal_state.fullkey_lifo.buffer_count = 0;
+ internal_state.handheld_lifo.buffer_count = 0;
+ internal_state.joy_dual_lifo.buffer_count = 0;
+ internal_state.joy_left_lifo.buffer_count = 0;
+ internal_state.joy_right_lifo.buffer_count = 0;
+ internal_state.palma_lifo.buffer_count = 0;
+ internal_state.system_ext_lifo.buffer_count = 0;
+ internal_state.gc_trigger_lifo.buffer_count = 0;
+ internal_state.sixaxis_fullkey_lifo.lifo.buffer_count = 0;
+ internal_state.sixaxis_handheld_lifo.lifo.buffer_count = 0;
+ internal_state.sixaxis_dual_left_lifo.lifo.buffer_count = 0;
+ internal_state.sixaxis_dual_right_lifo.lifo.buffer_count = 0;
+ internal_state.sixaxis_left_lifo.lifo.buffer_count = 0;
+ internal_state.sixaxis_right_lifo.lifo.buffer_count = 0;
+
+ internal_state.style_tag = {Core::HID::NpadStyleSet::None};
+ internal_state.assignment_mode = NpadJoyAssignmentMode::Dual;
+ internal_state.joycon_color = {};
+ internal_state.fullkey_color = {};
+
+ internal_state.system_properties.raw = 0;
+ internal_state.button_properties.raw = 0;
+ internal_state.device_type.raw = 0;
+
+ internal_state.battery_level_dual = Core::HID::NpadBatteryLevel::Empty;
+ internal_state.battery_level_left = Core::HID::NpadBatteryLevel::Empty;
+ internal_state.battery_level_right = Core::HID::NpadBatteryLevel::Empty;
+
+ internal_state.applet_footer_type = AppletFooterUiType::None;
+ internal_state.applet_footer_attributes = {};
+ internal_state.lark_type_l_and_main = {};
+ internal_state.lark_type_r = {};
+
+ internal_state.sixaxis_fullkey_properties.is_newly_assigned.Assign(true);
+ internal_state.sixaxis_handheld_properties.is_newly_assigned.Assign(true);
+ internal_state.sixaxis_dual_left_properties.is_newly_assigned.Assign(true);
+ internal_state.sixaxis_dual_right_properties.is_newly_assigned.Assign(true);
+ internal_state.sixaxis_left_properties.is_newly_assigned.Assign(true);
+ internal_state.sixaxis_right_properties.is_newly_assigned.Assign(true);
+ }
+
+ ref_counter++;
+ return ResultSuccess;
+}
+
+Result NpadAbstractPropertiesHandler::DecrementRefCounter() {
+ if (ref_counter == 0) {
+ return ResultNpadHandlerNotInitialized;
+ }
+ ref_counter--;
+ return ResultSuccess;
+}
+
+Result NpadAbstractPropertiesHandler::ActivateNpadUnknown0x88(u64 aruid) {
+ const auto npad_index = NpadIdTypeToIndex(npad_id_type);
+ for (std::size_t aruid_index = 0; aruid_index < AruidIndexMax; aruid_index++) {
+ auto* data = applet_resource_holder->applet_resource->GetAruidData(aruid_index);
+ if (!data->flag.is_assigned || data->aruid != aruid) {
+ continue;
+ }
+ UpdateDeviceProperties(aruid, data->shared_memory_format->npad.npad_entry[npad_index]);
+ return ResultSuccess;
+ }
+ return ResultSuccess;
+}
+
+void NpadAbstractPropertiesHandler::UpdateDeviceType() {
+ // TODO
+}
+
+void NpadAbstractPropertiesHandler::UpdateDeviceColor() {
+ // TODO
+}
+
+void NpadAbstractPropertiesHandler::UpdateFooterAttributes() {
+ // TODO
+}
+
+void NpadAbstractPropertiesHandler::UpdateAllDeviceProperties() {
+ const auto npad_index = NpadIdTypeToIndex(npad_id_type);
+ for (std::size_t aruid_index = 0; aruid_index < AruidIndexMax; aruid_index++) {
+ auto* data = applet_resource_holder->applet_resource->GetAruidData(aruid_index);
+ if (!data->flag.is_assigned) {
+ continue;
+ }
+ auto& npad_entry = data->shared_memory_format->npad.npad_entry[npad_index];
+ UpdateDeviceProperties(data->aruid, npad_entry);
+ }
+}
+
+Core::HID::NpadInterfaceType NpadAbstractPropertiesHandler::GetFullkeyInterfaceType() {
+ std::array<IAbstractedPad*, 5> abstract_pads{};
+ const std::size_t count = abstract_pad_holder->GetAbstractedPads(abstract_pads);
+
+ for (std::size_t i = 0; i < count; i++) {
+ auto* abstract_pad = abstract_pads[i];
+ if (!abstract_pad->internal_flags.is_connected) {
+ continue;
+ }
+ if (abstract_pad->device_type != Core::HID::NpadStyleIndex::Fullkey) {
+ continue;
+ }
+ if (abstract_pad->interface_type >= Core::HID::NpadInterfaceType::Embedded) {
+ // Abort
+ continue;
+ }
+ return abstract_pad->interface_type;
+ }
+
+ return Core::HID::NpadInterfaceType::None;
+}
+
+Core::HID::NpadInterfaceType NpadAbstractPropertiesHandler::GetInterfaceType() {
+ std::array<IAbstractedPad*, 5> abstract_pads{};
+ const std::size_t count = abstract_pad_holder->GetAbstractedPads(abstract_pads);
+
+ for (std::size_t i = 0; i < count; i++) {
+ auto* abstract_pad = abstract_pads[i];
+ if (!abstract_pad->internal_flags.is_connected) {
+ continue;
+ }
+ if (!abstract_pad->disabled_feature_set.has_identification_code) {
+ continue;
+ }
+ if (abstract_pad->interface_type >= Core::HID::NpadInterfaceType::Embedded) {
+ // Abort
+ continue;
+ }
+ return abstract_pad->interface_type;
+ }
+ return Core::HID::NpadInterfaceType::None;
+}
+
+Core::HID::NpadStyleSet NpadAbstractPropertiesHandler::GetStyleSet(u64 aruid) {
+ // TODO
+ return Core::HID::NpadStyleSet::None;
+}
+
+std::size_t NpadAbstractPropertiesHandler::GetAbstractedPadsWithStyleTag(
+ std::span<IAbstractedPad*> list, Core::HID::NpadStyleTag style) {
+ std::array<IAbstractedPad*, 5> abstract_pads{};
+ const std::size_t count = abstract_pad_holder->GetAbstractedPads(abstract_pads);
+
+ if (count == 0) {
+ return count;
+ }
+
+ bool is_supported_style_set{};
+ const auto result = applet_resource_holder->shared_npad_resource->IsSupportedNpadStyleSet(
+ is_supported_style_set, applet_resource_holder->applet_resource->GetActiveAruid());
+
+ if (!is_supported_style_set || result.IsError()) {
+ for (std::size_t i = 0; i < count; i++) {
+ // TODO
+ }
+ return count;
+ }
+
+ std::size_t filtered_count{};
+ for (std::size_t i = 0; i < count; i++) {
+ auto* abstract_pad = abstract_pads[i];
+ const bool is_enabled = true;
+ if (is_enabled) {
+ list[filtered_count] = abstract_pad;
+ filtered_count++;
+ }
+ }
+
+ return filtered_count;
+}
+
+std::size_t NpadAbstractPropertiesHandler::GetAbstractedPads(std::span<IAbstractedPad*> list) {
+ Core::HID::NpadStyleTag style{
+ GetStyleSet(applet_resource_holder->applet_resource->GetActiveAruid())};
+ return GetAbstractedPadsWithStyleTag(list, style);
+}
+
+AppletFooterUiType NpadAbstractPropertiesHandler::GetAppletFooterUiType() {
+ return applet_ui_type.footer;
+}
+
+AppletDetailedUiType NpadAbstractPropertiesHandler::GetAppletDetailedUiType() {
+ return applet_ui_type;
+}
+
+void NpadAbstractPropertiesHandler::UpdateDeviceProperties(u64 aruid,
+ NpadSharedMemoryEntry& internal_state) {
+ // TODO
+}
+
+Core::HID::NpadInterfaceType NpadAbstractPropertiesHandler::GetNpadInterfaceType() {
+ std::array<IAbstractedPad*, 5> abstract_pads{};
+ const std::size_t count = abstract_pad_holder->GetAbstractedPads(abstract_pads);
+
+ for (std::size_t i = 0; i < count; i++) {
+ auto* abstract_pad = abstract_pads[i];
+ if (!abstract_pad->internal_flags.is_connected) {
+ continue;
+ }
+ if (abstract_pad->interface_type >= Core::HID::NpadInterfaceType::Embedded) {
+ // Abort
+ continue;
+ }
+ return abstract_pad->interface_type;
+ }
+
+ return Core::HID::NpadInterfaceType::None;
+}
+
+Result NpadAbstractPropertiesHandler::GetNpadFullKeyGripColor(
+ Core::HID::NpadColor& main_color, Core::HID::NpadColor& sub_color) const {
+ std::array<IAbstractedPad*, 5> abstract_pads{};
+ const std::size_t count = abstract_pad_holder->GetAbstractedPads(abstract_pads);
+
+ if (applet_ui_type.footer != AppletFooterUiType::SwitchProController) {
+ return ResultNpadIsNotProController;
+ }
+
+ for (std::size_t i = 0; i < count; i++) {
+ auto* abstract_pad = abstract_pads[i];
+ if (!abstract_pad->internal_flags.is_connected) {
+ continue;
+ }
+ return ResultSuccess;
+ }
+
+ return ResultNpadIsNotProController;
+}
+
+void NpadAbstractPropertiesHandler::GetNpadLeftRightInterfaceType(
+ Core::HID::NpadInterfaceType& out_left_interface,
+ Core::HID::NpadInterfaceType& out_right_interface) const {
+ out_left_interface = Core::HID::NpadInterfaceType::None;
+ out_right_interface = Core::HID::NpadInterfaceType::None;
+
+ std::array<IAbstractedPad*, 5> abstract_pads{};
+ const std::size_t count = abstract_pad_holder->GetAbstractedPads(abstract_pads);
+
+ for (std::size_t i = 0; i < count; i++) {
+ auto* abstract_pad = abstract_pads[i];
+ if (!abstract_pad->internal_flags.is_connected) {
+ continue;
+ }
+ if (abstract_pad->assignment_style.is_external_left_assigned &&
+ abstract_pad->assignment_style.is_handheld_left_assigned) {
+ if (abstract_pad->interface_type > Core::HID::NpadInterfaceType::Embedded) {
+ // Abort
+ continue;
+ }
+ out_left_interface = abstract_pad->interface_type;
+ continue;
+ }
+ if (abstract_pad->assignment_style.is_external_right_assigned &&
+ abstract_pad->assignment_style.is_handheld_right_assigned) {
+ if (abstract_pad->interface_type > Core::HID::NpadInterfaceType::Embedded) {
+ // Abort
+ continue;
+ }
+ out_right_interface = abstract_pad->interface_type;
+ continue;
+ }
+ }
+}
+
+} // namespace Service::HID
diff --git a/src/hid_core/resources/abstracted_pad/abstract_properties_handler.h b/src/hid_core/resources/abstracted_pad/abstract_properties_handler.h
new file mode 100644
index 000000000..fa6827899
--- /dev/null
+++ b/src/hid_core/resources/abstracted_pad/abstract_properties_handler.h
@@ -0,0 +1,86 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include <span>
+
+#include "common/common_types.h"
+#include "core/hle/result.h"
+#include "hid_core/hid_types.h"
+#include "hid_core/resources/npad/npad_types.h"
+
+namespace Service::HID {
+struct NpadSharedMemoryEntry;
+
+struct AppletResourceHolder;
+class NpadAbstractedPadHolder;
+
+struct ColorProperties {
+ ColorAttribute attribute;
+ Core::HID::NpadControllerColor color;
+ INSERT_PADDING_BYTES(0x4);
+};
+
+/// Handles Npad request from HID interfaces
+class NpadAbstractPropertiesHandler final {
+public:
+ explicit NpadAbstractPropertiesHandler();
+ ~NpadAbstractPropertiesHandler();
+
+ void SetAbstractPadHolder(NpadAbstractedPadHolder* holder);
+ void SetAppletResource(AppletResourceHolder* applet_resource);
+ void SetNpadId(Core::HID::NpadIdType npad_id);
+
+ Core::HID::NpadIdType GetNpadId() const;
+
+ Result IncrementRefCounter();
+ Result DecrementRefCounter();
+
+ Result ActivateNpadUnknown0x88(u64 aruid);
+
+ void UpdateDeviceType();
+ void UpdateDeviceColor();
+ void UpdateFooterAttributes();
+ void UpdateAllDeviceProperties();
+
+ Core::HID::NpadInterfaceType GetFullkeyInterfaceType();
+ Core::HID::NpadInterfaceType GetInterfaceType();
+
+ Core::HID::NpadStyleSet GetStyleSet(u64 aruid);
+ std::size_t GetAbstractedPadsWithStyleTag(std::span<IAbstractedPad*> list,
+ Core::HID::NpadStyleTag style);
+ std::size_t GetAbstractedPads(std::span<IAbstractedPad*> list);
+
+ AppletFooterUiType GetAppletFooterUiType();
+
+ AppletDetailedUiType GetAppletDetailedUiType();
+
+ void UpdateDeviceProperties(u64 aruid, NpadSharedMemoryEntry& internal_state);
+
+ Core::HID::NpadInterfaceType GetNpadInterfaceType();
+
+ Result GetNpadFullKeyGripColor(Core::HID::NpadColor& main_color,
+ Core::HID::NpadColor& sub_color) const;
+
+ void GetNpadLeftRightInterfaceType(Core::HID::NpadInterfaceType& param_2,
+ Core::HID::NpadInterfaceType& param_3) const;
+
+private:
+ AppletResourceHolder* applet_resource_holder{nullptr};
+ NpadAbstractedPadHolder* abstract_pad_holder{nullptr};
+ Core::HID::NpadIdType npad_id_type{Core::HID::NpadIdType::Invalid};
+ s32 ref_counter{};
+ Core::HID::DeviceIndex device_type{};
+ AppletDetailedUiType applet_ui_type{};
+ AppletFooterUiAttributes applet_ui_attributes{};
+ bool is_vertical{};
+ bool is_horizontal{};
+ bool use_plus{};
+ bool use_minus{};
+ bool has_directional_buttons{};
+ ColorProperties fullkey_color{};
+ ColorProperties left_color{};
+ ColorProperties right_color{};
+};
+} // namespace Service::HID
diff --git a/src/hid_core/resources/abstracted_pad/abstract_sixaxis_handler.cpp b/src/hid_core/resources/abstracted_pad/abstract_sixaxis_handler.cpp
new file mode 100644
index 000000000..6d759298e
--- /dev/null
+++ b/src/hid_core/resources/abstracted_pad/abstract_sixaxis_handler.cpp
@@ -0,0 +1,154 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "hid_core/hid_result.h"
+#include "hid_core/hid_util.h"
+#include "hid_core/resources/abstracted_pad/abstract_pad_holder.h"
+#include "hid_core/resources/abstracted_pad/abstract_properties_handler.h"
+#include "hid_core/resources/abstracted_pad/abstract_sixaxis_handler.h"
+#include "hid_core/resources/applet_resource.h"
+#include "hid_core/resources/npad/npad_types.h"
+#include "hid_core/resources/shared_memory_format.h"
+
+namespace Service::HID {
+
+NpadAbstractSixAxisHandler::NpadAbstractSixAxisHandler() {}
+
+NpadAbstractSixAxisHandler::~NpadAbstractSixAxisHandler() = default;
+
+void NpadAbstractSixAxisHandler::SetAbstractPadHolder(NpadAbstractedPadHolder* holder) {
+ abstract_pad_holder = holder;
+}
+
+void NpadAbstractSixAxisHandler::SetAppletResource(AppletResourceHolder* applet_resource) {
+ applet_resource_holder = applet_resource;
+}
+
+void NpadAbstractSixAxisHandler::SetPropertiesHandler(NpadAbstractPropertiesHandler* handler) {
+ properties_handler = handler;
+}
+
+void NpadAbstractSixAxisHandler::SetSixaxisResource(SixAxisResource* resource) {
+ six_axis_resource = resource;
+}
+
+Result NpadAbstractSixAxisHandler::IncrementRefCounter() {
+ if (ref_counter == std::numeric_limits<s32>::max() - 1) {
+ return ResultNpadHandlerOverflow;
+ }
+ ref_counter++;
+ return ResultSuccess;
+}
+
+Result NpadAbstractSixAxisHandler::DecrementRefCounter() {
+ if (ref_counter == 0) {
+ return ResultNpadHandlerNotInitialized;
+ }
+ ref_counter--;
+ return ResultSuccess;
+}
+
+u64 NpadAbstractSixAxisHandler::IsFirmwareUpdateAvailable() {
+ // TODO
+ return false;
+}
+
+Result NpadAbstractSixAxisHandler::UpdateSixAxisState() {
+ Core::HID::NpadIdType npad_id = properties_handler->GetNpadId();
+ for (std::size_t i = 0; i < AruidIndexMax; i++) {
+ auto* data = applet_resource_holder->applet_resource->GetAruidDataByIndex(i);
+ if (data->flag.is_assigned) {
+ continue;
+ }
+ auto& npad_entry = data->shared_memory_format->npad.npad_entry[NpadIdTypeToIndex(npad_id)];
+ UpdateSixaxisInternalState(npad_entry, data->aruid,
+ data->flag.enable_six_axis_sensor.As<bool>());
+ }
+ return ResultSuccess;
+}
+
+Result NpadAbstractSixAxisHandler::UpdateSixAxisState(u64 aruid) {
+ Core::HID::NpadIdType npad_id = properties_handler->GetNpadId();
+ auto* data = applet_resource_holder->applet_resource->GetAruidData(aruid);
+ if (data == nullptr) {
+ return ResultSuccess;
+ }
+ auto& npad_entry = data->shared_memory_format->npad.npad_entry[NpadIdTypeToIndex(npad_id)];
+ UpdateSixaxisInternalState(npad_entry, data->aruid,
+ data->flag.enable_six_axis_sensor.As<bool>());
+ return ResultSuccess;
+}
+
+Result NpadAbstractSixAxisHandler::UpdateSixAxisState2(u64 aruid) {
+ const auto npad_index = NpadIdTypeToIndex(properties_handler->GetNpadId());
+ AruidData* aruid_data = applet_resource_holder->applet_resource->GetAruidData(aruid);
+ if (aruid_data == nullptr) {
+ return ResultSuccess;
+ }
+ auto& npad_internal_state = aruid_data->shared_memory_format->npad.npad_entry[npad_index];
+ UpdateSixaxisInternalState(npad_internal_state, aruid,
+ aruid_data->flag.enable_six_axis_sensor.As<bool>());
+ return ResultSuccess;
+}
+
+void NpadAbstractSixAxisHandler::UpdateSixaxisInternalState(NpadSharedMemoryEntry& npad_entry,
+ u64 aruid, bool is_sensor_enabled) {
+ const Core::HID::NpadStyleTag style_tag{properties_handler->GetStyleSet(aruid)};
+
+ if (!style_tag.palma) {
+ UpdateSixaxisFullkeyLifo(style_tag, npad_entry.internal_state.sixaxis_fullkey_lifo,
+ is_sensor_enabled);
+ } else {
+ UpdateSixAxisPalmaLifo(style_tag, npad_entry.internal_state.sixaxis_fullkey_lifo,
+ is_sensor_enabled);
+ }
+ UpdateSixaxisHandheldLifo(style_tag, npad_entry.internal_state.sixaxis_handheld_lifo,
+ is_sensor_enabled);
+ UpdateSixaxisDualLifo(style_tag, npad_entry.internal_state.sixaxis_dual_left_lifo,
+ is_sensor_enabled);
+ UpdateSixaxisDualLifo(style_tag, npad_entry.internal_state.sixaxis_dual_right_lifo,
+ is_sensor_enabled);
+ UpdateSixaxisLeftLifo(style_tag, npad_entry.internal_state.sixaxis_left_lifo,
+ is_sensor_enabled);
+ UpdateSixaxisRightLifo(style_tag, npad_entry.internal_state.sixaxis_right_lifo,
+ is_sensor_enabled);
+ // TODO: Set sixaxis properties
+}
+
+void NpadAbstractSixAxisHandler::UpdateSixaxisFullkeyLifo(Core::HID::NpadStyleTag style_tag,
+ NpadSixAxisSensorLifo& sensor_lifo,
+ bool is_sensor_enabled) {
+ // TODO
+}
+
+void NpadAbstractSixAxisHandler::UpdateSixAxisPalmaLifo(Core::HID::NpadStyleTag style_tag,
+ NpadSixAxisSensorLifo& sensor_lifo,
+ bool is_sensor_enabled) {
+ // TODO
+}
+
+void NpadAbstractSixAxisHandler::UpdateSixaxisHandheldLifo(Core::HID::NpadStyleTag style_tag,
+ NpadSixAxisSensorLifo& sensor_lifo,
+ bool is_sensor_enabled) {
+ // TODO
+}
+
+void NpadAbstractSixAxisHandler::UpdateSixaxisDualLifo(Core::HID::NpadStyleTag style_tag,
+ NpadSixAxisSensorLifo& sensor_lifo,
+ bool is_sensor_enabled) {
+ // TODO
+}
+
+void NpadAbstractSixAxisHandler::UpdateSixaxisLeftLifo(Core::HID::NpadStyleTag style_tag,
+ NpadSixAxisSensorLifo& sensor_lifo,
+ bool is_sensor_enabled) {
+ // TODO
+}
+
+void NpadAbstractSixAxisHandler::UpdateSixaxisRightLifo(Core::HID::NpadStyleTag style_tag,
+ NpadSixAxisSensorLifo& sensor_lifo,
+ bool is_sensor_enabled) {
+ // TODO
+}
+
+} // namespace Service::HID
diff --git a/src/hid_core/resources/abstracted_pad/abstract_sixaxis_handler.h b/src/hid_core/resources/abstracted_pad/abstract_sixaxis_handler.h
new file mode 100644
index 000000000..9c20459e9
--- /dev/null
+++ b/src/hid_core/resources/abstracted_pad/abstract_sixaxis_handler.h
@@ -0,0 +1,61 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include "common/common_types.h"
+#include "core/hle/result.h"
+#include "hid_core/hid_types.h"
+
+namespace Service::HID {
+class SixAxisResource;
+struct AppletResourceHolder;
+class NpadAbstractedPadHolder;
+class NpadAbstractPropertiesHandler;
+struct NpadSixAxisSensorLifo;
+
+/// Handles Npad request from HID interfaces
+class NpadAbstractSixAxisHandler final {
+public:
+ explicit NpadAbstractSixAxisHandler();
+ ~NpadAbstractSixAxisHandler();
+
+ void SetAbstractPadHolder(NpadAbstractedPadHolder* holder);
+ void SetAppletResource(AppletResourceHolder* applet_resource);
+ void SetPropertiesHandler(NpadAbstractPropertiesHandler* handler);
+ void SetSixaxisResource(SixAxisResource* resource);
+
+ Result IncrementRefCounter();
+ Result DecrementRefCounter();
+
+ u64 IsFirmwareUpdateAvailable();
+
+ Result UpdateSixAxisState();
+ Result UpdateSixAxisState(u64 aruid);
+ Result UpdateSixAxisState2(u64 aruid);
+
+private:
+ void UpdateSixaxisInternalState(NpadSharedMemoryEntry& npad_entry, u64 aruid,
+ bool is_sensor_enabled);
+ void UpdateSixaxisFullkeyLifo(Core::HID::NpadStyleTag style_tag,
+ NpadSixAxisSensorLifo& sensor_lifo, bool is_sensor_enabled);
+ void UpdateSixAxisPalmaLifo(Core::HID::NpadStyleTag style_tag,
+ NpadSixAxisSensorLifo& sensor_lifo, bool is_sensor_enabled);
+ void UpdateSixaxisHandheldLifo(Core::HID::NpadStyleTag style_tag,
+ NpadSixAxisSensorLifo& sensor_lifo, bool is_sensor_enabled);
+ void UpdateSixaxisDualLifo(Core::HID::NpadStyleTag style_tag,
+ NpadSixAxisSensorLifo& sensor_lifo, bool is_sensor_enabled);
+ void UpdateSixaxisLeftLifo(Core::HID::NpadStyleTag style_tag,
+ NpadSixAxisSensorLifo& sensor_lifo, bool is_sensor_enabled);
+ void UpdateSixaxisRightLifo(Core::HID::NpadStyleTag style_tag,
+ NpadSixAxisSensorLifo& sensor_lifo, bool is_sensor_enabled);
+
+ AppletResourceHolder* applet_resource_holder{nullptr};
+ NpadAbstractedPadHolder* abstract_pad_holder{nullptr};
+ NpadAbstractPropertiesHandler* properties_handler{nullptr};
+ SixAxisResource* six_axis_resource{nullptr};
+
+ s32 ref_counter{};
+};
+
+} // namespace Service::HID
diff --git a/src/hid_core/resources/abstracted_pad/abstract_vibration_handler.cpp b/src/hid_core/resources/abstracted_pad/abstract_vibration_handler.cpp
new file mode 100644
index 000000000..a00d6c9de
--- /dev/null
+++ b/src/hid_core/resources/abstracted_pad/abstract_vibration_handler.cpp
@@ -0,0 +1,73 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "hid_core/hid_result.h"
+#include "hid_core/hid_util.h"
+#include "hid_core/resources/abstracted_pad/abstract_pad_holder.h"
+#include "hid_core/resources/abstracted_pad/abstract_properties_handler.h"
+#include "hid_core/resources/abstracted_pad/abstract_vibration_handler.h"
+#include "hid_core/resources/applet_resource.h"
+#include "hid_core/resources/npad/npad_vibration.h"
+#include "hid_core/resources/vibration/gc_vibration_device.h"
+#include "hid_core/resources/vibration/n64_vibration_device.h"
+#include "hid_core/resources/vibration/vibration_device.h"
+
+namespace Service::HID {
+
+NpadAbstractVibrationHandler::NpadAbstractVibrationHandler() {}
+
+NpadAbstractVibrationHandler::~NpadAbstractVibrationHandler() = default;
+
+void NpadAbstractVibrationHandler::SetAbstractPadHolder(NpadAbstractedPadHolder* holder) {
+ abstract_pad_holder = holder;
+}
+
+void NpadAbstractVibrationHandler::SetAppletResource(AppletResourceHolder* applet_resource) {
+ applet_resource_holder = applet_resource;
+}
+
+void NpadAbstractVibrationHandler::SetPropertiesHandler(NpadAbstractPropertiesHandler* handler) {
+ properties_handler = handler;
+}
+
+void NpadAbstractVibrationHandler::SetN64Vibration(NpadN64VibrationDevice* n64_device) {
+ n64_vibration_device = n64_device;
+}
+
+void NpadAbstractVibrationHandler::SetVibration(std::span<NpadVibrationDevice*> device) {
+ for (std::size_t i = 0; i < device.size() && i < vibration_device.size(); i++) {
+ vibration_device[i] = device[i];
+ }
+}
+
+void NpadAbstractVibrationHandler::SetGcVibration(NpadGcVibrationDevice* gc_device) {
+ gc_vibration_device = gc_device;
+}
+
+Result NpadAbstractVibrationHandler::IncrementRefCounter() {
+ if (ref_counter == std::numeric_limits<s32>::max() - 1) {
+ return ResultNpadHandlerOverflow;
+ }
+ ref_counter++;
+ return ResultSuccess;
+}
+
+Result NpadAbstractVibrationHandler::DecrementRefCounter() {
+ if (ref_counter == 0) {
+ return ResultNpadHandlerNotInitialized;
+ }
+ ref_counter--;
+ return ResultSuccess;
+}
+
+void NpadAbstractVibrationHandler::UpdateVibrationState() {
+ const bool is_handheld_hid_enabled =
+ applet_resource_holder->handheld_config->is_handheld_hid_enabled;
+ const bool is_force_handheld_style_vibration =
+ applet_resource_holder->handheld_config->is_force_handheld_style_vibration;
+
+ if (!is_handheld_hid_enabled && is_force_handheld_style_vibration) {
+ // TODO
+ }
+}
+} // namespace Service::HID
diff --git a/src/hid_core/resources/abstracted_pad/abstract_vibration_handler.h b/src/hid_core/resources/abstracted_pad/abstract_vibration_handler.h
new file mode 100644
index 000000000..aeb07ce86
--- /dev/null
+++ b/src/hid_core/resources/abstracted_pad/abstract_vibration_handler.h
@@ -0,0 +1,51 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include <span>
+
+#include "common/common_types.h"
+#include "core/hle/result.h"
+#include "hid_core/hid_types.h"
+
+namespace Service::HID {
+struct AppletResourceHolder;
+class NpadAbstractedPadHolder;
+class NpadAbstractPropertiesHandler;
+class NpadGcVibrationDevice;
+class NpadVibrationDevice;
+class NpadN64VibrationDevice;
+class NpadVibration;
+
+/// Keeps track of battery levels and updates npad battery shared memory values
+class NpadAbstractVibrationHandler final {
+public:
+ explicit NpadAbstractVibrationHandler();
+ ~NpadAbstractVibrationHandler();
+
+ void SetAbstractPadHolder(NpadAbstractedPadHolder* holder);
+ void SetAppletResource(AppletResourceHolder* applet_resource);
+ void SetPropertiesHandler(NpadAbstractPropertiesHandler* handler);
+
+ void SetN64Vibration(NpadN64VibrationDevice* n64_device);
+ void SetVibration(std::span<NpadVibrationDevice*> device);
+ void SetGcVibration(NpadGcVibrationDevice* gc_device);
+
+ Result IncrementRefCounter();
+ Result DecrementRefCounter();
+
+ void UpdateVibrationState();
+
+private:
+ AppletResourceHolder* applet_resource_holder{nullptr};
+ NpadAbstractedPadHolder* abstract_pad_holder{nullptr};
+ NpadAbstractPropertiesHandler* properties_handler{nullptr};
+
+ NpadN64VibrationDevice* n64_vibration_device{nullptr};
+ std::array<NpadVibrationDevice*, 2> vibration_device{};
+ NpadGcVibrationDevice* gc_vibration_device{nullptr};
+ NpadVibration* vibration_handler{nullptr};
+ s32 ref_counter{};
+};
+} // namespace Service::HID
diff --git a/src/hid_core/resources/npad/npad.cpp b/src/hid_core/resources/npad/npad.cpp
index 1f8a0f8ab..97537a2e2 100644
--- a/src/hid_core/resources/npad/npad.cpp
+++ b/src/hid_core/resources/npad/npad.cpp
@@ -193,7 +193,7 @@ void NPad::InitNewlyAddedController(u64 aruid, Core::HID::NpadIdType npad_id) {
case Core::HID::NpadStyleIndex::None:
ASSERT(false);
break;
- case Core::HID::NpadStyleIndex::ProController:
+ case Core::HID::NpadStyleIndex::Fullkey:
shared_memory->fullkey_color.attribute = ColorAttribute::Ok;
shared_memory->fullkey_color.fullkey = body_colors.fullkey;
shared_memory->battery_level_dual = battery_level.dual.battery_level;
@@ -491,7 +491,7 @@ void NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
case Core::HID::NpadStyleIndex::None:
ASSERT(false);
break;
- case Core::HID::NpadStyleIndex::ProController:
+ case Core::HID::NpadStyleIndex::Fullkey:
case Core::HID::NpadStyleIndex::NES:
case Core::HID::NpadStyleIndex::SNES:
case Core::HID::NpadStyleIndex::N64:
@@ -1292,7 +1292,7 @@ Core::HID::SixAxisSensorProperties& NPad::GetSixaxisProperties(
u64 aruid, const Core::HID::SixAxisSensorHandle& sixaxis_handle) {
auto& controller = GetControllerFromHandle(aruid, sixaxis_handle);
switch (sixaxis_handle.npad_type) {
- case Core::HID::NpadStyleIndex::ProController:
+ case Core::HID::NpadStyleIndex::Fullkey:
case Core::HID::NpadStyleIndex::Pokeball:
return controller.shared_memory->sixaxis_fullkey_properties;
case Core::HID::NpadStyleIndex::Handheld:
@@ -1315,7 +1315,7 @@ const Core::HID::SixAxisSensorProperties& NPad::GetSixaxisProperties(
u64 aruid, const Core::HID::SixAxisSensorHandle& sixaxis_handle) const {
const auto& controller = GetControllerFromHandle(aruid, sixaxis_handle);
switch (sixaxis_handle.npad_type) {
- case Core::HID::NpadStyleIndex::ProController:
+ case Core::HID::NpadStyleIndex::Fullkey:
case Core::HID::NpadStyleIndex::Pokeball:
return controller.shared_memory->sixaxis_fullkey_properties;
case Core::HID::NpadStyleIndex::Handheld:
diff --git a/src/hid_core/resources/npad/npad_data.cpp b/src/hid_core/resources/npad/npad_data.cpp
index c7e9760cb..29ad5cb08 100644
--- a/src/hid_core/resources/npad/npad_data.cpp
+++ b/src/hid_core/resources/npad/npad_data.cpp
@@ -151,7 +151,7 @@ Core::HID::NpadStyleSet NPadData::GetSupportedNpadStyleSet() const {
bool NPadData::IsNpadStyleIndexSupported(Core::HID::NpadStyleIndex style_index) const {
Core::HID::NpadStyleTag style = {supported_npad_style_set};
switch (style_index) {
- case Core::HID::NpadStyleIndex::ProController:
+ case Core::HID::NpadStyleIndex::Fullkey:
return style.fullkey.As<bool>();
case Core::HID::NpadStyleIndex::Handheld:
return style.handheld.As<bool>();
diff --git a/src/hid_core/resources/npad/npad_types.h b/src/hid_core/resources/npad/npad_types.h
index a02f9cf16..074dd40cf 100644
--- a/src/hid_core/resources/npad/npad_types.h
+++ b/src/hid_core/resources/npad/npad_types.h
@@ -252,4 +252,103 @@ enum class NpadLagerType : u32 {
U,
};
+// nn::hidtypes::FeatureType
+struct FeatureType {
+ union {
+ u64 raw{};
+ BitField<0, 1, u64> has_left_analog_stick;
+ BitField<1, 1, u64> has_right_analog_stick;
+ BitField<2, 1, u64> has_left_joy_six_axis_sensor;
+ BitField<3, 1, u64> has_right_joy_six_axis_sensor;
+ BitField<4, 1, u64> has_fullkey_joy_six_axis_sensor;
+ BitField<5, 1, u64> has_left_lra_vibration_device;
+ BitField<6, 1, u64> has_right_lra_vibration_device;
+ BitField<7, 1, u64> has_gc_vibration_device;
+ BitField<8, 1, u64> has_erm_vibration_device;
+ BitField<9, 1, u64> has_left_joy_rail_bus;
+ BitField<10, 1, u64> has_right_joy_rail_bus;
+ BitField<11, 1, u64> has_internal_bus;
+ BitField<12, 1, u64> is_palma;
+ BitField<13, 1, u64> has_nfc;
+ BitField<14, 1, u64> has_ir_sensor;
+ BitField<15, 1, u64> is_analog_stick_calibration_supported;
+ BitField<16, 1, u64> is_six_axis_Sensor_user_calibration_supported;
+ BitField<17, 1, u64> has_left_right_joy_battery;
+ BitField<18, 1, u64> has_fullkey_battery;
+ BitField<19, 1, u64> is_disconnect_controller_if_battery_none;
+ BitField<20, 1, u64> has_controller_color;
+ BitField<21, 1, u64> has_grip_color;
+ BitField<22, 1, u64> has_identification_code;
+ BitField<23, 1, u64> has_bluetooth_address;
+ BitField<24, 1, u64> has_mcu;
+ BitField<25, 1, u64> has_notification_led;
+ BitField<26, 1, u64> has_directional_buttons;
+ BitField<27, 1, u64> has_indicator_led;
+ BitField<28, 1, u64> is_button_config_embedded_supported;
+ BitField<29, 1, u64> is_button_config_full_supported;
+ BitField<30, 1, u64> is_button_config_left_supported;
+ BitField<31, 1, u64> is_button_config_right_supported;
+ BitField<32, 1, u64> is_usb_hid_device;
+ BitField<33, 1, u64> is_kuina_device;
+ BitField<34, 1, u64> is_direct_usb_to_bt_switching_device;
+ BitField<35, 1, u64> is_normalize_analog_stick_with_inner_cross;
+ };
+};
+static_assert(sizeof(FeatureType) == 8, "FeatureType is an invalid size");
+
+// This is nn::hid::AssignmentStyle
+struct AssignmentStyle {
+ union {
+ u32 raw{};
+ BitField<0, 1, u32> is_external_assigned;
+ BitField<1, 1, u32> is_external_left_assigned;
+ BitField<2, 1, u32> is_external_right_assigned;
+ BitField<3, 1, u32> is_handheld_assigned;
+ BitField<4, 1, u32> is_handheld_left_assigned;
+ BitField<5, 1, u32> is_handheld_right_assigned;
+ };
+};
+static_assert(sizeof(AssignmentStyle) == 4, "AssignmentStyle is an invalid size");
+
+// This is nn::hid::server::IAbstractedPad::InternalFlags
+struct InternalFlags {
+ union {
+ u32 raw{};
+ BitField<0, 1, u32> is_bound;
+ BitField<1, 1, u32> is_connected;
+ BitField<2, 1, u32> is_battery_low_ovln_required;
+ BitField<3, 1, u32> is_battery_low_ovln_delay_required;
+ BitField<4, 1, u32> is_sample_recieved;
+ BitField<5, 1, u32> is_virtual_input;
+ BitField<6, 1, u32> is_wired;
+ BitField<8, 1, u32> use_center_clamp;
+ BitField<9, 1, u32> has_virtual_six_axis_sensor_acceleration;
+ BitField<10, 1, u32> has_virtual_six_axis_sensor_angle;
+ BitField<11, 1, u32> is_debug_pad;
+ };
+};
+static_assert(sizeof(InternalFlags) == 4, "InternalFlags is an invalid size");
+
+/// This is nn::hid::server::IAbstractedPad
+struct IAbstractedPad {
+ InternalFlags internal_flags;
+ u64 controller_id;
+ u32 controller_number;
+ u64 low_battery_display_delay_time;
+ u64 low_battery_display_delay_interval;
+ FeatureType feature_set;
+ FeatureType disabled_feature_set;
+ AssignmentStyle assignment_style;
+ Core::HID::NpadStyleIndex device_type;
+ Core::HID::NpadInterfaceType interface_type;
+ Core::HID::NpadPowerInfo power_info;
+ u32 pad_state;
+ u32 button_mask;
+ u32 system_button_mask;
+ u8 indicator;
+ std::vector<f32> virtual_six_axis_sensor_acceleration;
+ std::vector<f32> virtual_six_axis_sensor_angle;
+ u64 xcd_handle;
+ u64 color;
+};
} // namespace Service::HID
diff --git a/src/hid_core/resources/npad/npad_vibration.cpp b/src/hid_core/resources/npad/npad_vibration.cpp
new file mode 100644
index 000000000..3bdd55dec
--- /dev/null
+++ b/src/hid_core/resources/npad/npad_vibration.cpp
@@ -0,0 +1,80 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "hid_core/hid_result.h"
+#include "hid_core/resources/npad/npad_vibration.h"
+
+namespace Service::HID {
+
+NpadVibration::NpadVibration() {}
+
+NpadVibration::~NpadVibration() = default;
+
+Result NpadVibration::Activate() {
+ std::scoped_lock lock{mutex};
+
+ const f32 master_volume = 1.0f; // nn::settings::system::GetVibrationMasterVolume();
+ // if (master_volume < 0.0f || master_volume > 1.0f) {
+ // return ResultVibrationStrenghtOutOfRange;
+ // }
+
+ volume = master_volume;
+ return ResultSuccess;
+}
+
+Result NpadVibration::Deactivate() {
+ return ResultSuccess;
+}
+
+Result NpadVibration::SetVibrationMasterVolume(f32 master_volume) {
+ std::scoped_lock lock{mutex};
+
+ if (master_volume < 0.0f && master_volume > 1.0f) {
+ return ResultVibrationStrenghtOutOfRange;
+ }
+
+ volume = master_volume;
+ // nn::settings::system::SetVibrationMasterVolume(master_volume);
+
+ return ResultSuccess;
+}
+
+Result NpadVibration::GetVibrationVolume(f32& out_volume) const {
+ std::scoped_lock lock{mutex};
+ out_volume = volume;
+ return ResultSuccess;
+}
+
+Result NpadVibration::GetVibrationMasterVolume(f32& out_volume) const {
+ std::scoped_lock lock{mutex};
+
+ const f32 master_volume = 1.0f; // nn::settings::system::GetVibrationMasterVolume();
+ // if (master_volume < 0.0f || master_volume > 1.0f) {
+ // return ResultVibrationStrenghtOutOfRange;
+ // }
+
+ out_volume = master_volume;
+ return ResultSuccess;
+}
+
+Result NpadVibration::BeginPermitVibrationSession(u64 aruid) {
+ std::scoped_lock lock{mutex};
+ session_aruid = aruid;
+ volume = 1.0;
+ return ResultSuccess;
+}
+
+Result NpadVibration::EndPermitVibrationSession() {
+ std::scoped_lock lock{mutex};
+
+ const f32 master_volume = 1.0f; // nn::settings::system::GetVibrationMasterVolume();
+ // if (master_volume < 0.0f || master_volume > 1.0f) {
+ // return ResultVibrationStrenghtOutOfRange;
+ // }
+
+ volume = master_volume;
+ session_aruid = 0;
+ return ResultSuccess;
+}
+
+} // namespace Service::HID
diff --git a/src/hid_core/resources/npad/npad_vibration.h b/src/hid_core/resources/npad/npad_vibration.h
new file mode 100644
index 000000000..0748aeffc
--- /dev/null
+++ b/src/hid_core/resources/npad/npad_vibration.h
@@ -0,0 +1,34 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include <mutex>
+
+#include "common/common_types.h"
+#include "core/hle/result.h"
+
+namespace Service::HID {
+
+class NpadVibration final {
+public:
+ explicit NpadVibration();
+ ~NpadVibration();
+
+ Result Activate();
+ Result Deactivate();
+
+ Result SetVibrationMasterVolume(f32 master_volume);
+ Result GetVibrationVolume(f32& out_volume) const;
+ Result GetVibrationMasterVolume(f32& out_volume) const;
+
+ Result BeginPermitVibrationSession(u64 aruid);
+ Result EndPermitVibrationSession();
+
+private:
+ f32 volume{};
+ u64 session_aruid{};
+ mutable std::mutex mutex;
+};
+
+} // namespace Service::HID
diff --git a/src/hid_core/resources/six_axis/six_axis.cpp b/src/hid_core/resources/six_axis/six_axis.cpp
index 8a9677c50..da12d2d5a 100644
--- a/src/hid_core/resources/six_axis/six_axis.cpp
+++ b/src/hid_core/resources/six_axis/six_axis.cpp
@@ -114,7 +114,7 @@ void SixAxis::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
case Core::HID::NpadStyleIndex::None:
ASSERT(false);
break;
- case Core::HID::NpadStyleIndex::ProController:
+ case Core::HID::NpadStyleIndex::Fullkey:
set_motion_state(sixaxis_fullkey_state, motion_state[0]);
break;
case Core::HID::NpadStyleIndex::Handheld:
@@ -345,7 +345,7 @@ SixAxis::SixaxisParameters& SixAxis::GetSixaxisState(
const Core::HID::SixAxisSensorHandle& sixaxis_handle) {
auto& controller = GetControllerFromHandle(sixaxis_handle);
switch (sixaxis_handle.npad_type) {
- case Core::HID::NpadStyleIndex::ProController:
+ case Core::HID::NpadStyleIndex::Fullkey:
case Core::HID::NpadStyleIndex::Pokeball:
return controller.sixaxis_fullkey;
case Core::HID::NpadStyleIndex::Handheld:
@@ -368,7 +368,7 @@ const SixAxis::SixaxisParameters& SixAxis::GetSixaxisState(
const Core::HID::SixAxisSensorHandle& sixaxis_handle) const {
const auto& controller = GetControllerFromHandle(sixaxis_handle);
switch (sixaxis_handle.npad_type) {
- case Core::HID::NpadStyleIndex::ProController:
+ case Core::HID::NpadStyleIndex::Fullkey:
case Core::HID::NpadStyleIndex::Pokeball:
return controller.sixaxis_fullkey;
case Core::HID::NpadStyleIndex::Handheld:
diff --git a/src/hid_core/resources/vibration/gc_vibration_device.cpp b/src/hid_core/resources/vibration/gc_vibration_device.cpp
new file mode 100644
index 000000000..f01f81b9a
--- /dev/null
+++ b/src/hid_core/resources/vibration/gc_vibration_device.cpp
@@ -0,0 +1,106 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "hid_core/hid_result.h"
+#include "hid_core/resources/npad/npad_types.h"
+#include "hid_core/resources/npad/npad_vibration.h"
+#include "hid_core/resources/vibration/gc_vibration_device.h"
+
+namespace Service::HID {
+
+NpadGcVibrationDevice::NpadGcVibrationDevice() {}
+
+Result NpadGcVibrationDevice::IncrementRefCounter() {
+ if (ref_counter == 0 && is_mounted) {
+ f32 volume = 1.0f;
+ const auto result = vibration_handler->GetVibrationVolume(volume);
+ if (result.IsSuccess()) {
+ // TODO: SendVibrationGcErmCommand
+ }
+ }
+ ref_counter++;
+ return ResultSuccess;
+}
+
+Result NpadGcVibrationDevice::DecrementRefCounter() {
+ if (ref_counter == 1 && !is_mounted) {
+ f32 volume = 1.0f;
+ const auto result = vibration_handler->GetVibrationVolume(volume);
+ if (result.IsSuccess()) {
+ // TODO: SendVibrationGcErmCommand
+ }
+ }
+
+ if (ref_counter > 0) {
+ ref_counter--;
+ }
+
+ return ResultSuccess;
+}
+
+Result NpadGcVibrationDevice::SendVibrationGcErmCommand(Core::HID::VibrationGcErmCommand command) {
+ if (!is_mounted) {
+ return ResultSuccess;
+ }
+ f32 volume = 1.0f;
+ const auto result = vibration_handler->GetVibrationVolume(volume);
+ if (result.IsError()) {
+ return result;
+ }
+ if (volume == 0.0) {
+ command = Core::HID::VibrationGcErmCommand::Stop;
+ } else {
+ if (command > Core::HID::VibrationGcErmCommand::StopHard) {
+ // Abort
+ return ResultSuccess;
+ }
+ }
+ // TODO: SendVibrationGcErmCommand
+ return ResultSuccess;
+}
+
+Result NpadGcVibrationDevice::GetActualVibrationGcErmCommand(
+ Core::HID::VibrationGcErmCommand& out_command) {
+ if (!is_mounted) {
+ out_command = Core::HID::VibrationGcErmCommand::Stop;
+ return ResultSuccess;
+ }
+
+ f32 volume = 1.0f;
+ const auto result = vibration_handler->GetVibrationVolume(volume);
+ if (result.IsError()) {
+ return result;
+ }
+ if (volume == 0.0f) {
+ out_command = Core::HID::VibrationGcErmCommand::Stop;
+ return ResultSuccess;
+ }
+
+ // TODO: GetActualVibrationGcErmCommand
+ return ResultSuccess;
+}
+
+Result NpadGcVibrationDevice::SendVibrationNotificationPattern(
+ Core::HID::VibrationGcErmCommand command) {
+ if (!is_mounted) {
+ return ResultSuccess;
+ }
+
+ f32 volume = 1.0f;
+ const auto result = vibration_handler->GetVibrationVolume(volume);
+ if (result.IsError()) {
+ return result;
+ }
+ if (volume <= 0.0f) {
+ command = Core::HID::VibrationGcErmCommand::Stop;
+ }
+ if (command > Core::HID::VibrationGcErmCommand::StopHard) {
+ // Abort
+ return ResultSuccess;
+ }
+
+ // TODO: SendVibrationNotificationPattern
+ return ResultSuccess;
+}
+
+} // namespace Service::HID
diff --git a/src/hid_core/resources/vibration/gc_vibration_device.h b/src/hid_core/resources/vibration/gc_vibration_device.h
new file mode 100644
index 000000000..87abca57d
--- /dev/null
+++ b/src/hid_core/resources/vibration/gc_vibration_device.h
@@ -0,0 +1,31 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include <array>
+#include <mutex>
+
+#include "common/common_types.h"
+#include "core/hle/result.h"
+#include "hid_core/hid_types.h"
+#include "hid_core/resources/npad/npad_types.h"
+#include "hid_core/resources/vibration/vibration_base.h"
+
+namespace Service::HID {
+class NpadVibration;
+
+/// Handles Npad request from HID interfaces
+class NpadGcVibrationDevice final : public NpadVibrationBase {
+public:
+ explicit NpadGcVibrationDevice();
+
+ Result IncrementRefCounter() override;
+ Result DecrementRefCounter() override;
+
+ Result SendVibrationGcErmCommand(Core::HID::VibrationGcErmCommand command);
+
+ Result GetActualVibrationGcErmCommand(Core::HID::VibrationGcErmCommand& out_command);
+ Result SendVibrationNotificationPattern(Core::HID::VibrationGcErmCommand command);
+};
+} // namespace Service::HID
diff --git a/src/hid_core/resources/vibration/n64_vibration_device.cpp b/src/hid_core/resources/vibration/n64_vibration_device.cpp
new file mode 100644
index 000000000..639f87abf
--- /dev/null
+++ b/src/hid_core/resources/vibration/n64_vibration_device.cpp
@@ -0,0 +1,80 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "hid_core/hid_result.h"
+#include "hid_core/resources/npad/npad_types.h"
+#include "hid_core/resources/npad/npad_vibration.h"
+#include "hid_core/resources/vibration/n64_vibration_device.h"
+
+namespace Service::HID {
+
+NpadN64VibrationDevice::NpadN64VibrationDevice() {}
+
+Result NpadN64VibrationDevice::IncrementRefCounter() {
+ if (ref_counter == 0 && is_mounted) {
+ f32 volume = 1.0f;
+ const auto result = vibration_handler->GetVibrationVolume(volume);
+ if (result.IsSuccess()) {
+ // TODO: SendVibrationInBool
+ }
+ }
+
+ ref_counter++;
+ return ResultSuccess;
+}
+
+Result NpadN64VibrationDevice::DecrementRefCounter() {
+ if (ref_counter == 1) {
+ if (!is_mounted) {
+ ref_counter = 0;
+ if (is_mounted != false) {
+ // TODO: SendVibrationInBool
+ }
+ return ResultSuccess;
+ }
+ f32 volume = 1.0f;
+ const auto result = vibration_handler->GetVibrationVolume(volume);
+ if (result.IsSuccess()) {
+ // TODO
+ }
+ }
+
+ if (ref_counter > 0) {
+ ref_counter--;
+ }
+
+ return ResultSuccess;
+}
+
+Result NpadN64VibrationDevice::SendValueInBool(bool is_vibrating) {
+ if (ref_counter < 1) {
+ return ResultVibrationNotInitialized;
+ }
+ if (is_mounted) {
+ f32 volume = 1.0f;
+ const auto result = vibration_handler->GetVibrationVolume(volume);
+ if (result.IsError()) {
+ return result;
+ }
+ // TODO: SendVibrationInBool
+ }
+ return ResultSuccess;
+}
+
+Result NpadN64VibrationDevice::SendVibrationNotificationPattern([[maybe_unused]] u32 pattern) {
+ if (!is_mounted) {
+ return ResultSuccess;
+ }
+ f32 volume = 1.0f;
+ const auto result = vibration_handler->GetVibrationVolume(volume);
+ if (result.IsError()) {
+ return result;
+ }
+ if (volume <= 0.0) {
+ pattern = 0;
+ }
+ // TODO: SendVibrationNotificationPattern
+ return ResultSuccess;
+}
+
+} // namespace Service::HID
diff --git a/src/hid_core/resources/vibration/n64_vibration_device.h b/src/hid_core/resources/vibration/n64_vibration_device.h
new file mode 100644
index 000000000..54e6efc1a
--- /dev/null
+++ b/src/hid_core/resources/vibration/n64_vibration_device.h
@@ -0,0 +1,29 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include <array>
+#include <mutex>
+
+#include "common/common_types.h"
+#include "core/hle/result.h"
+#include "hid_core/hid_types.h"
+#include "hid_core/resources/npad/npad_types.h"
+#include "hid_core/resources/vibration/vibration_base.h"
+
+namespace Service::HID {
+class NpadVibration;
+
+/// Handles Npad request from HID interfaces
+class NpadN64VibrationDevice final : public NpadVibrationBase {
+public:
+ explicit NpadN64VibrationDevice();
+
+ Result IncrementRefCounter() override;
+ Result DecrementRefCounter() override;
+
+ Result SendValueInBool(bool is_vibrating);
+ Result SendVibrationNotificationPattern(u32 pattern);
+};
+} // namespace Service::HID
diff --git a/src/hid_core/resources/vibration/vibration_base.cpp b/src/hid_core/resources/vibration/vibration_base.cpp
new file mode 100644
index 000000000..350f349c2
--- /dev/null
+++ b/src/hid_core/resources/vibration/vibration_base.cpp
@@ -0,0 +1,30 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "hid_core/hid_result.h"
+#include "hid_core/resources/npad/npad_types.h"
+#include "hid_core/resources/npad/npad_vibration.h"
+#include "hid_core/resources/vibration/vibration_base.h"
+
+namespace Service::HID {
+
+NpadVibrationBase::NpadVibrationBase() {}
+
+Result NpadVibrationBase::IncrementRefCounter() {
+ ref_counter++;
+ return ResultSuccess;
+}
+
+Result NpadVibrationBase::DecrementRefCounter() {
+ if (ref_counter > 0) {
+ ref_counter--;
+ }
+
+ return ResultSuccess;
+}
+
+bool NpadVibrationBase::IsVibrationMounted() const {
+ return is_mounted;
+}
+
+} // namespace Service::HID
diff --git a/src/hid_core/resources/vibration/vibration_base.h b/src/hid_core/resources/vibration/vibration_base.h
new file mode 100644
index 000000000..c6c5fc4d9
--- /dev/null
+++ b/src/hid_core/resources/vibration/vibration_base.h
@@ -0,0 +1,28 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include "common/common_types.h"
+#include "core/hle/result.h"
+
+namespace Service::HID {
+class NpadVibration;
+
+/// Handles Npad request from HID interfaces
+class NpadVibrationBase {
+public:
+ explicit NpadVibrationBase();
+
+ virtual Result IncrementRefCounter();
+ virtual Result DecrementRefCounter();
+
+ bool IsVibrationMounted() const;
+
+protected:
+ u64 xcd_handle{};
+ s32 ref_counter{};
+ bool is_mounted{};
+ NpadVibration* vibration_handler{nullptr};
+};
+} // namespace Service::HID
diff --git a/src/hid_core/resources/vibration/vibration_device.cpp b/src/hid_core/resources/vibration/vibration_device.cpp
new file mode 100644
index 000000000..888c3a7ed
--- /dev/null
+++ b/src/hid_core/resources/vibration/vibration_device.cpp
@@ -0,0 +1,84 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "hid_core/hid_result.h"
+#include "hid_core/resources/npad/npad_types.h"
+#include "hid_core/resources/npad/npad_vibration.h"
+#include "hid_core/resources/vibration/vibration_device.h"
+
+namespace Service::HID {
+
+NpadVibrationDevice::NpadVibrationDevice() {}
+
+Result NpadVibrationDevice::IncrementRefCounter() {
+ ref_counter++;
+ return ResultSuccess;
+}
+
+Result NpadVibrationDevice::DecrementRefCounter() {
+ if (ref_counter > 0) {
+ ref_counter--;
+ }
+
+ return ResultSuccess;
+}
+
+Result NpadVibrationDevice::SendVibrationValue(const Core::HID::VibrationValue& value) {
+ if (ref_counter == 0) {
+ return ResultVibrationNotInitialized;
+ }
+ if (!is_mounted) {
+ return ResultSuccess;
+ }
+
+ f32 volume = 1.0f;
+ const auto result = vibration_handler->GetVibrationVolume(volume);
+ if (result.IsError()) {
+ return result;
+ }
+ if (volume <= 0.0f) {
+ // TODO: SendVibrationValue
+ return ResultSuccess;
+ }
+
+ Core::HID::VibrationValue vibration_value = value;
+ vibration_value.high_amplitude *= volume;
+ vibration_value.low_amplitude *= volume;
+
+ // TODO: SendVibrationValue
+ return ResultSuccess;
+}
+
+Result NpadVibrationDevice::SendVibrationNotificationPattern([[maybe_unused]] u32 pattern) {
+ if (!is_mounted) {
+ return ResultSuccess;
+ }
+
+ f32 volume = 1.0f;
+ const auto result = vibration_handler->GetVibrationVolume(volume);
+ if (result.IsError()) {
+ return result;
+ }
+ if (volume <= 0.0) {
+ pattern = 0;
+ }
+
+ // return xcd_handle->SendVibrationNotificationPattern(pattern);
+ return ResultSuccess;
+}
+
+Result NpadVibrationDevice::GetActualVibrationValue(Core::HID::VibrationValue& out_value) {
+ if (ref_counter < 1) {
+ return ResultVibrationNotInitialized;
+ }
+
+ out_value = Core::HID::DEFAULT_VIBRATION_VALUE;
+ if (!is_mounted) {
+ return ResultSuccess;
+ }
+
+ // TODO: SendVibrationValue
+ return ResultSuccess;
+}
+
+} // namespace Service::HID
diff --git a/src/hid_core/resources/vibration/vibration_device.h b/src/hid_core/resources/vibration/vibration_device.h
new file mode 100644
index 000000000..3574ad60b
--- /dev/null
+++ b/src/hid_core/resources/vibration/vibration_device.h
@@ -0,0 +1,35 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include <array>
+#include <mutex>
+
+#include "common/common_types.h"
+#include "core/hle/result.h"
+#include "hid_core/hid_types.h"
+#include "hid_core/resources/npad/npad_types.h"
+#include "hid_core/resources/vibration/vibration_base.h"
+
+namespace Service::HID {
+class NpadVibration;
+
+/// Handles Npad request from HID interfaces
+class NpadVibrationDevice final : public NpadVibrationBase {
+public:
+ explicit NpadVibrationDevice();
+
+ Result IncrementRefCounter();
+ Result DecrementRefCounter();
+
+ Result SendVibrationValue(const Core::HID::VibrationValue& value);
+ Result SendVibrationNotificationPattern(u32 pattern);
+
+ Result GetActualVibrationValue(Core::HID::VibrationValue& out_value);
+
+private:
+ u32 device_index{};
+};
+
+} // namespace Service::HID
diff --git a/src/yuzu/applets/qt_controller.cpp b/src/yuzu/applets/qt_controller.cpp
index 8b340ee6c..48ce860ad 100644
--- a/src/yuzu/applets/qt_controller.cpp
+++ b/src/yuzu/applets/qt_controller.cpp
@@ -41,7 +41,7 @@ void UpdateController(Core::HID::EmulatedController* controller,
bool IsControllerCompatible(Core::HID::NpadStyleIndex controller_type,
Core::Frontend::ControllerParameters parameters) {
switch (controller_type) {
- case Core::HID::NpadStyleIndex::ProController:
+ case Core::HID::NpadStyleIndex::Fullkey:
return parameters.allow_pro_controller;
case Core::HID::NpadStyleIndex::JoyconDual:
return parameters.allow_dual_joycons;
@@ -462,7 +462,7 @@ void QtControllerSelectorDialog::SetEmulatedControllers(std::size_t player_index
};
if (npad_style_set.fullkey == 1) {
- add_item(Core::HID::NpadStyleIndex::ProController, tr("Pro Controller"));
+ add_item(Core::HID::NpadStyleIndex::Fullkey, tr("Pro Controller"));
}
if (npad_style_set.joycon_dual == 1) {
@@ -519,7 +519,7 @@ Core::HID::NpadStyleIndex QtControllerSelectorDialog::GetControllerTypeFromIndex
[index](const auto& pair) { return pair.first == index; });
if (it == pairs.end()) {
- return Core::HID::NpadStyleIndex::ProController;
+ return Core::HID::NpadStyleIndex::Fullkey;
}
return it->second;
@@ -549,7 +549,7 @@ void QtControllerSelectorDialog::UpdateControllerIcon(std::size_t player_index)
const QString stylesheet = [this, player_index] {
switch (GetControllerTypeFromIndex(emulated_controllers[player_index]->currentIndex(),
player_index)) {
- case Core::HID::NpadStyleIndex::ProController:
+ case Core::HID::NpadStyleIndex::Fullkey:
case Core::HID::NpadStyleIndex::GameCube:
return QStringLiteral("image: url(:/controller/applet_pro_controller%0); ");
case Core::HID::NpadStyleIndex::JoyconDual:
diff --git a/src/yuzu/applets/qt_software_keyboard.cpp b/src/yuzu/applets/qt_software_keyboard.cpp
index bbe17c35e..ac81ace9e 100644
--- a/src/yuzu/applets/qt_software_keyboard.cpp
+++ b/src/yuzu/applets/qt_software_keyboard.cpp
@@ -832,7 +832,7 @@ void QtSoftwareKeyboardDialog::SetControllerImage() {
}();
switch (controller_type) {
- case Core::HID::NpadStyleIndex::ProController:
+ case Core::HID::NpadStyleIndex::Fullkey:
case Core::HID::NpadStyleIndex::GameCube:
ui->icon_controller->setStyleSheet(
QStringLiteral("image: url(:/overlay/controller_pro%1.png);").arg(theme));
diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp
index f3552191a..5dac9f1e7 100644
--- a/src/yuzu/configuration/configure_input_player.cpp
+++ b/src/yuzu/configuration/configure_input_player.cpp
@@ -1094,7 +1094,7 @@ void ConfigureInputPlayer::SetConnectableControllers() {
};
if (npad_style_set.fullkey == 1) {
- add_item(Core::HID::NpadStyleIndex::ProController, tr("Pro Controller"));
+ add_item(Core::HID::NpadStyleIndex::Fullkey, tr("Pro Controller"));
}
if (npad_style_set.joycon_dual == 1) {
@@ -1149,7 +1149,7 @@ Core::HID::NpadStyleIndex ConfigureInputPlayer::GetControllerTypeFromIndex(int i
[index](const auto& pair) { return pair.first == index; });
if (it == index_controller_type_pairs.end()) {
- return Core::HID::NpadStyleIndex::ProController;
+ return Core::HID::NpadStyleIndex::Fullkey;
}
return it->second;
@@ -1178,7 +1178,7 @@ void ConfigureInputPlayer::UpdateInputDevices() {
void ConfigureInputPlayer::UpdateControllerAvailableButtons() {
auto layout = GetControllerTypeFromIndex(ui->comboControllerType->currentIndex());
if (debug) {
- layout = Core::HID::NpadStyleIndex::ProController;
+ layout = Core::HID::NpadStyleIndex::Fullkey;
}
// List of all the widgets that will be hidden by any of the following layouts that need
@@ -1206,7 +1206,7 @@ void ConfigureInputPlayer::UpdateControllerAvailableButtons() {
std::vector<QWidget*> layout_hidden;
switch (layout) {
- case Core::HID::NpadStyleIndex::ProController:
+ case Core::HID::NpadStyleIndex::Fullkey:
case Core::HID::NpadStyleIndex::Handheld:
layout_hidden = {
ui->buttonShoulderButtonsSLSRLeft,
@@ -1254,7 +1254,7 @@ void ConfigureInputPlayer::UpdateControllerAvailableButtons() {
void ConfigureInputPlayer::UpdateControllerEnabledButtons() {
auto layout = GetControllerTypeFromIndex(ui->comboControllerType->currentIndex());
if (debug) {
- layout = Core::HID::NpadStyleIndex::ProController;
+ layout = Core::HID::NpadStyleIndex::Fullkey;
}
// List of all the widgets that will be disabled by any of the following layouts that need
@@ -1271,7 +1271,7 @@ void ConfigureInputPlayer::UpdateControllerEnabledButtons() {
std::vector<QWidget*> layout_disable;
switch (layout) {
- case Core::HID::NpadStyleIndex::ProController:
+ case Core::HID::NpadStyleIndex::Fullkey:
case Core::HID::NpadStyleIndex::JoyconDual:
case Core::HID::NpadStyleIndex::Handheld:
case Core::HID::NpadStyleIndex::JoyconLeft:
@@ -1304,7 +1304,7 @@ void ConfigureInputPlayer::UpdateMotionButtons() {
// Show/hide the "Motion 1/2" groupboxes depending on the currently selected controller.
switch (GetControllerTypeFromIndex(ui->comboControllerType->currentIndex())) {
- case Core::HID::NpadStyleIndex::ProController:
+ case Core::HID::NpadStyleIndex::Fullkey:
case Core::HID::NpadStyleIndex::JoyconLeft:
case Core::HID::NpadStyleIndex::Handheld:
// Show "Motion 1" and hide "Motion 2".
@@ -1333,11 +1333,11 @@ void ConfigureInputPlayer::UpdateMotionButtons() {
void ConfigureInputPlayer::UpdateControllerButtonNames() {
auto layout = GetControllerTypeFromIndex(ui->comboControllerType->currentIndex());
if (debug) {
- layout = Core::HID::NpadStyleIndex::ProController;
+ layout = Core::HID::NpadStyleIndex::Fullkey;
}
switch (layout) {
- case Core::HID::NpadStyleIndex::ProController:
+ case Core::HID::NpadStyleIndex::Fullkey:
case Core::HID::NpadStyleIndex::JoyconDual:
case Core::HID::NpadStyleIndex::Handheld:
case Core::HID::NpadStyleIndex::JoyconLeft:
diff --git a/src/yuzu/configuration/configure_input_player_widget.cpp b/src/yuzu/configuration/configure_input_player_widget.cpp
index 19fdca7d3..8f91f5e92 100644
--- a/src/yuzu/configuration/configure_input_player_widget.cpp
+++ b/src/yuzu/configuration/configure_input_player_widget.cpp
@@ -244,7 +244,7 @@ void PlayerControlPreview::paintEvent(QPaintEvent* event) {
case Core::HID::NpadStyleIndex::GameCube:
DrawGCController(p, center);
break;
- case Core::HID::NpadStyleIndex::ProController:
+ case Core::HID::NpadStyleIndex::Fullkey:
default:
DrawProController(p, center);
break;
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index 29f47bf48..33756febf 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -3988,7 +3988,7 @@ void GMainWindow::OnToggleDockedMode() {
tr("Handheld controller can't be used on docked mode. Pro "
"controller will be selected."));
handheld->Disconnect();
- player_1->SetNpadStyleIndex(Core::HID::NpadStyleIndex::ProController);
+ player_1->SetNpadStyleIndex(Core::HID::NpadStyleIndex::Fullkey);
player_1->Connect();
controller_dialog->refreshConfiguration();
}
diff --git a/src/yuzu/util/controller_navigation.cpp b/src/yuzu/util/controller_navigation.cpp
index 2690b075d..0dbfca243 100644
--- a/src/yuzu/util/controller_navigation.cpp
+++ b/src/yuzu/util/controller_navigation.cpp
@@ -66,7 +66,7 @@ void ControllerNavigation::ControllerUpdateButton() {
}
switch (controller_type) {
- case Core::HID::NpadStyleIndex::ProController:
+ case Core::HID::NpadStyleIndex::Fullkey:
case Core::HID::NpadStyleIndex::JoyconDual:
case Core::HID::NpadStyleIndex::Handheld:
case Core::HID::NpadStyleIndex::GameCube:
@@ -116,7 +116,7 @@ void ControllerNavigation::ControllerUpdateStick() {
}
switch (controller_type) {
- case Core::HID::NpadStyleIndex::ProController:
+ case Core::HID::NpadStyleIndex::Fullkey:
case Core::HID::NpadStyleIndex::JoyconDual:
case Core::HID::NpadStyleIndex::Handheld:
case Core::HID::NpadStyleIndex::GameCube: