9 Commits

Author SHA1 Message Date
Narr the Reg
7114b9669a yuzu: refactor shortcut code 2023-10-13 16:51:00 -06:00
DanielSvoboda
56e5d99684 Improvement in Directory Path Detection for Shortcuts (#11749)
* Improvement in Directory Path Detection for Shortcuts

This pull request updates how the directory path for shortcuts is determined. The main changes are:

1. Replaced the use of environment variables to determine the path of the desktop and applications menu with `QStandardPaths::writableLocation`. This change addresses an issue where the desktop path was not correctly identified when its location was customized, as shown in the attached screenshot.

2. Added conversion from `QString` to `std::string` using `toUtf8()`, which correctly handles non-ASCII characters in directory paths. This change ensures that directory paths containing Portuguese words like "Área de trabalho" are supported.

3. Replaced directory checking using `Common::FS::IsDir()` with `QDir::exists()`.

These changes should improve cross-platform compatibility and code robustness. Because it couldn't locate my desktop, which wasn't on the C drive, but on the F, and even though localization wouldn't work because it was setting it to find the 'Desktop' folder and in the computer's language it says 'Área de trabalho', that will fix for other languages too.

* Update main.cpp

* formatting

* Update src/yuzu/main.cpp

Co-authored-by: Tobias <thm.frey@gmail.com>

* Update src/yuzu/main.cpp

Co-authored-by: Tobias <thm.frey@gmail.com>

* Update main.cpp

* Update main.cpp

* Update main.cpp

desktopPath > desktop_Path
applicationsPath > applications_Path

* Update main.cpp

* formatting

* Update main.cpp

This code will attempt to use QStandardPaths to find the applications directory. If that fails, it will resort to using the ~/.local/share/applications directory, which is a common location for application shortcuts in Linux.

* Update main.cpp

* formatting

---------

Co-authored-by: Tobias <thm.frey@gmail.com>
2023-10-13 09:57:49 -06:00
liamwhite
1a4874e178 Merge pull request #11769 from liamwhite/qt-ownership-issue
qt: ensure worker cancellation is complete before clearing
2023-10-13 09:29:13 -04:00
liamwhite
c00b63b9e1 Merge pull request #11766 from liamwhite/open-sesame
k_page_table: add missing page group open when locking memory
2023-10-13 09:29:05 -04:00
liamwhite
c8602e1b1f Merge pull request #11649 from t895/driver-manager
android: Driver manager
2023-10-13 09:28:53 -04:00
Liam
faa6c35e78 qt: ensure worker cancellation is complete before clearing 2023-10-12 21:07:49 -04:00
Charles Lombardo
a5fb9de6fa android: Add GPU driver management fragment
Implements a GPU driver manager that saves all drivers to the user data directory and asynchronously installs drivers when they're needed.
2023-10-12 17:17:21 -04:00
Liam
c4ec76edba k_page_table: add missing page group open when locking memory 2023-10-12 15:00:26 -04:00
Charles Lombardo
26f9d1f122 android: Use application context for all FileUtil functions 2023-10-01 15:56:02 -04:00
187 changed files with 18872 additions and 11961 deletions

View File

@@ -15,13 +15,9 @@ import androidx.annotation.Keep
import androidx.fragment.app.DialogFragment
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import java.lang.ref.WeakReference
import org.yuzu.yuzu_emu.YuzuApplication.Companion.appContext
import org.yuzu.yuzu_emu.activities.EmulationActivity
import org.yuzu.yuzu_emu.utils.DocumentsTree.Companion.isNativePath
import org.yuzu.yuzu_emu.utils.FileUtil.exists
import org.yuzu.yuzu_emu.utils.FileUtil.getFileSize
import org.yuzu.yuzu_emu.utils.FileUtil.isDirectory
import org.yuzu.yuzu_emu.utils.FileUtil.openContentUri
import org.yuzu.yuzu_emu.utils.FileUtil
import org.yuzu.yuzu_emu.utils.Log
import org.yuzu.yuzu_emu.utils.SerializableHelper.serializable
@@ -75,7 +71,7 @@ object NativeLibrary {
return if (isNativePath(path!!)) {
YuzuApplication.documentsTree!!.openContentUri(path, openmode)
} else {
openContentUri(appContext, path, openmode)
FileUtil.openContentUri(path, openmode)
}
}
@@ -85,7 +81,7 @@ object NativeLibrary {
return if (isNativePath(path!!)) {
YuzuApplication.documentsTree!!.getFileSize(path)
} else {
getFileSize(appContext, path)
FileUtil.getFileSize(path)
}
}
@@ -95,7 +91,7 @@ object NativeLibrary {
return if (isNativePath(path!!)) {
YuzuApplication.documentsTree!!.exists(path)
} else {
exists(appContext, path)
FileUtil.exists(path)
}
}
@@ -105,7 +101,7 @@ object NativeLibrary {
return if (isNativePath(path!!)) {
YuzuApplication.documentsTree!!.isDirectory(path)
} else {
isDirectory(appContext, path)
FileUtil.isDirectory(path)
}
}

View File

@@ -47,7 +47,7 @@ class YuzuApplication : Application() {
application = this
documentsTree = DocumentsTree()
DirectoryInitialization.start()
GpuDriverHelper.initializeDriverParameters(applicationContext)
GpuDriverHelper.initializeDriverParameters()
NativeLibrary.logDeviceInfo()
createNotificationChannels()

View File

@@ -0,0 +1,117 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
package org.yuzu.yuzu_emu.adapters
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.model.DriverViewModel
import org.yuzu.yuzu_emu.utils.GpuDriverHelper
import org.yuzu.yuzu_emu.utils.GpuDriverMetadata
class DriverAdapter(private val driverViewModel: DriverViewModel) :
ListAdapter<Pair<String, GpuDriverMetadata>, DriverAdapter.DriverViewHolder>(
AsyncDifferConfig.Builder(DiffCallback()).build()
) {
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.customDriverData == driverData.second) {
driverViewModel.setSelectedDriverIndex(0)
}
driverViewModel.driversToDelete.add(driverData.first)
driverViewModel.removeDriver(driverData)
notifyItemRemoved(position)
notifyItemChanged(driverViewModel.selectedDriver)
}
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
binding.apply {
radioButton.isChecked = driverViewModel.selectedDriver == bindingAdapterPosition
root.setOnClickListener {
onSelectDriver(bindingAdapterPosition)
}
buttonDelete.setOnClickListener {
onDeleteDriver(driverData, bindingAdapterPosition)
}
// Delay marquee by 3s
title.postDelayed(
{
title.isSelected = true
title.ellipsize = TextUtils.TruncateAt.MARQUEE
version.isSelected = true
version.ellipsize = TextUtils.TruncateAt.MARQUEE
description.isSelected = true
description.ellipsize = TextUtils.TruncateAt.MARQUEE
},
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
version.visibility = View.VISIBLE
description.visibility = View.VISIBLE
buttonDelete.visibility = View.VISIBLE
}
}
}
}
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
}
}
}

View File

@@ -0,0 +1,185 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
package org.yuzu.yuzu_emu.fragments
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.activity.result.contract.ActivityResultContracts
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.updatePadding
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.lifecycle.lifecycleScope
import androidx.navigation.findNavController
import androidx.recyclerview.widget.GridLayoutManager
import com.google.android.material.transition.MaterialSharedAxis
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch
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.model.DriverViewModel
import org.yuzu.yuzu_emu.model.HomeViewModel
import org.yuzu.yuzu_emu.utils.FileUtil
import org.yuzu.yuzu_emu.utils.GpuDriverHelper
import java.io.IOException
class DriverManagerFragment : Fragment() {
private var _binding: FragmentDriverManagerBinding? = null
private val binding get() = _binding!!
private val homeViewModel: HomeViewModel by activityViewModels()
private val driverViewModel: DriverViewModel by activityViewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enterTransition = MaterialSharedAxis(MaterialSharedAxis.X, true)
returnTransition = MaterialSharedAxis(MaterialSharedAxis.X, false)
reenterTransition = MaterialSharedAxis(MaterialSharedAxis.X, false)
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = FragmentDriverManagerBinding.inflate(inflater)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
homeViewModel.setNavigationVisibility(visible = false, animated = true)
homeViewModel.setStatusBarShadeVisibility(visible = false)
if (!driverViewModel.isInteractionAllowed) {
DriversLoadingDialogFragment().show(
childFragmentManager,
DriversLoadingDialogFragment.TAG
)
}
binding.toolbarDrivers.setNavigationOnClickListener {
binding.root.findNavController().popBackStack()
}
binding.buttonInstall.setOnClickListener {
getDriver.launch(arrayOf("application/zip"))
}
binding.listDrivers.apply {
layoutManager = GridLayoutManager(
requireContext(),
resources.getInteger(R.integer.grid_columns)
)
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()
}
// Start installing requested driver
override fun onStop() {
super.onStop()
driverViewModel.onCloseDriverManager()
}
private fun setInsets() =
ViewCompat.setOnApplyWindowInsetsListener(
binding.root
) { _: View, windowInsets: WindowInsetsCompat ->
val barInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
val cutoutInsets = windowInsets.getInsets(WindowInsetsCompat.Type.displayCutout())
val leftInsets = barInsets.left + cutoutInsets.left
val rightInsets = barInsets.right + cutoutInsets.right
val mlpAppBar = binding.toolbarDrivers.layoutParams as ViewGroup.MarginLayoutParams
mlpAppBar.leftMargin = leftInsets
mlpAppBar.rightMargin = rightInsets
binding.toolbarDrivers.layoutParams = mlpAppBar
val mlplistDrivers = binding.listDrivers.layoutParams as ViewGroup.MarginLayoutParams
mlplistDrivers.leftMargin = leftInsets
mlplistDrivers.rightMargin = rightInsets
binding.listDrivers.layoutParams = mlplistDrivers
val fabSpacing = resources.getDimensionPixelSize(R.dimen.spacing_fab)
val mlpFab =
binding.buttonInstall.layoutParams as ViewGroup.MarginLayoutParams
mlpFab.leftMargin = leftInsets + fabSpacing
mlpFab.rightMargin = rightInsets + fabSpacing
mlpFab.bottomMargin = barInsets.bottom + fabSpacing
binding.buttonInstall.layoutParams = mlpFab
binding.listDrivers.updatePadding(
bottom = barInsets.bottom +
resources.getDimensionPixelSize(R.dimen.spacing_bottom_list_fab)
)
windowInsets
}
private val getDriver =
registerForActivityResult(ActivityResultContracts.OpenDocument()) { result ->
if (result == null) {
return@registerForActivityResult
}
IndeterminateProgressDialogFragment.newInstance(
requireActivity(),
R.string.installing_driver,
false
) {
// Ignore file exceptions when a user selects an invalid zip
try {
GpuDriverHelper.copyDriverToInternalStorage(result)
} catch (_: IOException) {
return@newInstance getString(R.string.select_gpu_driver_error)
}
val driverData = GpuDriverHelper.customDriverData
if (driverData.name == null) {
return@newInstance getString(R.string.select_gpu_driver_error)
}
val driverInList =
driverViewModel.driverList.value.firstOrNull { it.second == driverData }
if (driverInList != null) {
return@newInstance getString(R.string.driver_already_installed)
} else {
driverViewModel.addDriver(
Pair(
"${GpuDriverHelper.driverStoragePath}/${FileUtil.getFilename(result)}",
driverData
)
)
driverViewModel.setNewDriverInstalled(true)
}
return@newInstance Any()
}.show(childFragmentManager, IndeterminateProgressDialogFragment.TAG)
}
}

View File

@@ -0,0 +1,75 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
package org.yuzu.yuzu_emu.fragments
import android.app.Dialog
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.DialogFragment
import androidx.fragment.app.activityViewModels
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import kotlinx.coroutines.launch
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.databinding.DialogProgressBarBinding
import org.yuzu.yuzu_emu.model.DriverViewModel
class DriversLoadingDialogFragment : DialogFragment() {
private val driverViewModel: DriverViewModel by activityViewModels()
private lateinit var binding: DialogProgressBarBinding
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
binding = DialogProgressBarBinding.inflate(layoutInflater)
binding.progressBar.isIndeterminate = true
isCancelable = false
return MaterialAlertDialogBuilder(requireContext())
.setTitle(R.string.loading)
.setView(binding.root)
.create()
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View = binding.root
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewLifecycleOwner.lifecycleScope.apply {
launch {
repeatOnLifecycle(Lifecycle.State.RESUMED) {
driverViewModel.areDriversLoading.collect { checkForDismiss() }
}
}
launch {
repeatOnLifecycle(Lifecycle.State.RESUMED) {
driverViewModel.isDriverReady.collect { checkForDismiss() }
}
}
launch {
repeatOnLifecycle(Lifecycle.State.RESUMED) {
driverViewModel.isDeletingDrivers.collect { checkForDismiss() }
}
}
}
}
private fun checkForDismiss() {
if (driverViewModel.isInteractionAllowed) {
dismiss()
}
}
companion object {
const val TAG = "DriversLoadingDialogFragment"
}
}

View File

@@ -39,6 +39,7 @@ import androidx.window.layout.WindowLayoutInfo
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.slider.Slider
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch
import org.yuzu.yuzu_emu.HomeNavigationDirections
@@ -50,6 +51,7 @@ import org.yuzu.yuzu_emu.databinding.DialogOverlayAdjustBinding
import org.yuzu.yuzu_emu.databinding.FragmentEmulationBinding
import org.yuzu.yuzu_emu.features.settings.model.IntSetting
import org.yuzu.yuzu_emu.features.settings.model.Settings
import org.yuzu.yuzu_emu.model.DriverViewModel
import org.yuzu.yuzu_emu.model.Game
import org.yuzu.yuzu_emu.model.EmulationViewModel
import org.yuzu.yuzu_emu.overlay.InputOverlay
@@ -70,6 +72,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
private lateinit var game: Game
private val emulationViewModel: EmulationViewModel by activityViewModels()
private val driverViewModel: DriverViewModel by activityViewModels()
private var isInFoldableLayout = false
@@ -299,6 +302,21 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
}
}
}
launch {
repeatOnLifecycle(Lifecycle.State.RESUMED) {
driverViewModel.isDriverReady.collect {
if (it && !emulationState.isRunning) {
if (!DirectoryInitialization.areDirectoriesReady) {
DirectoryInitialization.start()
}
updateScreenLayout()
emulationState.run(emulationActivity!!.isActivityRecreated)
}
}
}
}
}
}
@@ -332,17 +350,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
}
}
override fun onResume() {
super.onResume()
if (!DirectoryInitialization.areDirectoriesReady) {
DirectoryInitialization.start()
}
updateScreenLayout()
emulationState.run(emulationActivity!!.isActivityRecreated)
}
override fun onPause() {
if (emulationState.isRunning && emulationActivity?.isInPictureInPictureMode != true) {
emulationState.pause()

View File

@@ -5,7 +5,6 @@ package org.yuzu.yuzu_emu.fragments
import android.Manifest
import android.content.ActivityNotFoundException
import android.content.DialogInterface
import android.content.Intent
import android.content.pm.PackageManager
import android.os.Bundle
@@ -28,7 +27,6 @@ import androidx.fragment.app.activityViewModels
import androidx.navigation.findNavController
import androidx.navigation.fragment.findNavController
import androidx.recyclerview.widget.LinearLayoutManager
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.transition.MaterialSharedAxis
import org.yuzu.yuzu_emu.BuildConfig
import org.yuzu.yuzu_emu.HomeNavigationDirections
@@ -37,6 +35,7 @@ import org.yuzu.yuzu_emu.adapters.HomeSettingAdapter
import org.yuzu.yuzu_emu.databinding.FragmentHomeSettingsBinding
import org.yuzu.yuzu_emu.features.DocumentProvider
import org.yuzu.yuzu_emu.features.settings.model.Settings
import org.yuzu.yuzu_emu.model.DriverViewModel
import org.yuzu.yuzu_emu.model.HomeSetting
import org.yuzu.yuzu_emu.model.HomeViewModel
import org.yuzu.yuzu_emu.ui.main.MainActivity
@@ -50,6 +49,7 @@ class HomeSettingsFragment : Fragment() {
private lateinit var mainActivity: MainActivity
private val homeViewModel: HomeViewModel by activityViewModels()
private val driverViewModel: DriverViewModel by activityViewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@@ -107,13 +107,17 @@ class HomeSettingsFragment : Fragment() {
)
add(
HomeSetting(
R.string.install_gpu_driver,
R.string.gpu_driver_manager,
R.string.install_gpu_driver_description,
R.drawable.ic_exit,
{ driverInstaller() },
R.drawable.ic_build,
{
binding.root.findNavController()
.navigate(R.id.action_homeSettingsFragment_to_driverManagerFragment)
},
{ GpuDriverHelper.supportsCustomDriverLoading() },
R.string.custom_driver_not_supported,
R.string.custom_driver_not_supported_description
R.string.custom_driver_not_supported_description,
driverViewModel.selectedDriverMetadata
)
)
add(
@@ -292,31 +296,6 @@ class HomeSettingsFragment : Fragment() {
}
}
private fun driverInstaller() {
// Get the driver name for the dialog message.
var driverName = GpuDriverHelper.customDriverName
if (driverName == null) {
driverName = getString(R.string.system_gpu_driver)
}
MaterialAlertDialogBuilder(requireContext())
.setTitle(getString(R.string.select_gpu_driver_title))
.setMessage(driverName)
.setNegativeButton(android.R.string.cancel, null)
.setNeutralButton(R.string.select_gpu_driver_default) { _: DialogInterface?, _: Int ->
GpuDriverHelper.installDefaultDriver(requireContext())
Toast.makeText(
requireContext(),
R.string.select_gpu_driver_use_default,
Toast.LENGTH_SHORT
).show()
}
.setPositiveButton(R.string.select_gpu_driver_install) { _: DialogInterface?, _: Int ->
mainActivity.getDriver.launch(arrayOf("application/zip"))
}
.show()
}
private fun shareLog() {
val file = DocumentFile.fromSingleUri(
mainActivity,

View File

@@ -10,8 +10,8 @@ import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.DialogFragment
import androidx.fragment.app.FragmentActivity
import androidx.fragment.app.activityViewModels
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.ViewModelProvider
@@ -78,6 +78,10 @@ class IndeterminateProgressDialogFragment : DialogFragment() {
requireActivity().supportFragmentManager,
MessageDialogFragment.TAG
)
else -> {
// Do nothing
}
}
taskViewModel.clear()
}
@@ -115,7 +119,7 @@ class IndeterminateProgressDialogFragment : DialogFragment() {
private const val CANCELLABLE = "Cancellable"
fun newInstance(
activity: AppCompatActivity,
activity: FragmentActivity,
titleId: Int,
cancellable: Boolean = false,
task: () -> Any

View File

@@ -0,0 +1,158 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
package org.yuzu.yuzu_emu.model
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.YuzuApplication
import org.yuzu.yuzu_emu.utils.FileUtil
import org.yuzu.yuzu_emu.utils.GpuDriverHelper
import org.yuzu.yuzu_emu.utils.GpuDriverMetadata
import java.io.BufferedOutputStream
import java.io.File
class DriverViewModel : ViewModel() {
private val _areDriversLoading = MutableStateFlow(false)
val areDriversLoading: StateFlow<Boolean> get() = _areDriversLoading
private val _isDriverReady = MutableStateFlow(true)
val isDriverReady: StateFlow<Boolean> get() = _isDriverReady
private val _isDeletingDrivers = MutableStateFlow(false)
val isDeletingDrivers: StateFlow<Boolean> get() = _isDeletingDrivers
private val _driverList = MutableStateFlow(mutableListOf<Pair<String, GpuDriverMetadata>>())
val driverList: StateFlow<MutableList<Pair<String, GpuDriverMetadata>>> get() = _driverList
var previouslySelectedDriver = 0
var selectedDriver = -1
private val _selectedDriverMetadata =
MutableStateFlow(
GpuDriverHelper.customDriverData.name
?: YuzuApplication.appContext.getString(R.string.system_gpu_driver)
)
val selectedDriverMetadata: StateFlow<String> get() = _selectedDriverMetadata
private val _newDriverInstalled = MutableStateFlow(false)
val newDriverInstalled: StateFlow<Boolean> get() = _newDriverInstalled
val driversToDelete = mutableListOf<String>()
val isInteractionAllowed
get() = !areDriversLoading.value && isDriverReady.value && !isDeletingDrivers.value
init {
_areDriversLoading.value = true
viewModelScope.launch {
withContext(Dispatchers.IO) {
val drivers = GpuDriverHelper.getDrivers()
val currentDriverMetadata = GpuDriverHelper.customDriverData
for (i in drivers.indices) {
if (drivers[i].second == currentDriverMetadata) {
setSelectedDriverIndex(i)
break
}
}
// 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())
)
drivers.add(Pair(driverToSave.path, currentDriverMetadata))
setSelectedDriverIndex(drivers.size - 1)
}
_driverList.value = drivers
_areDriversLoading.value = false
}
}
}
fun setSelectedDriverIndex(value: Int) {
if (selectedDriver != -1) {
previouslySelectedDriver = selectedDriver
}
selectedDriver = value
}
fun setNewDriverInstalled(value: Boolean) {
_newDriverInstalled.value = value
}
fun addDriver(driverData: Pair<String, GpuDriverMetadata>) {
val driverIndex = _driverList.value.indexOfFirst { it == driverData }
if (driverIndex == -1) {
setSelectedDriverIndex(_driverList.value.size)
_driverList.value.add(driverData)
_selectedDriverMetadata.value = driverData.second.name
?: YuzuApplication.appContext.getString(R.string.system_gpu_driver)
} else {
setSelectedDriverIndex(driverIndex)
}
}
fun removeDriver(driverData: Pair<String, GpuDriverMetadata>) {
_driverList.value.remove(driverData)
}
fun onCloseDriverManager() {
_isDeletingDrivers.value = true
viewModelScope.launch {
withContext(Dispatchers.IO) {
driversToDelete.forEach {
val driver = File(it)
if (driver.exists()) {
driver.delete()
}
}
driversToDelete.clear()
_isDeletingDrivers.value = false
}
}
if (GpuDriverHelper.customDriverData == driverList.value[selectedDriver].second) {
return
}
_isDriverReady.value = false
viewModelScope.launch {
withContext(Dispatchers.IO) {
if (selectedDriver == 0) {
GpuDriverHelper.installDefaultDriver()
setDriverReady()
return@withContext
}
val driverToInstall = File(driverList.value[selectedDriver].first)
if (driverToInstall.exists()) {
GpuDriverHelper.installCustomDriver(driverToInstall)
} else {
GpuDriverHelper.installDefaultDriver()
}
setDriverReady()
}
}
}
private fun setDriverReady() {
_isDriverReady.value = true
_selectedDriverMetadata.value = GpuDriverHelper.customDriverData.name
?: YuzuApplication.appContext.getString(R.string.system_gpu_driver)
}
}

View File

@@ -29,12 +29,10 @@ import androidx.navigation.fragment.NavHostFragment
import androidx.navigation.ui.setupWithNavController
import androidx.preference.PreferenceManager
import com.google.android.material.color.MaterialColors
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.navigation.NavigationBarView
import kotlinx.coroutines.CoroutineScope
import java.io.File
import java.io.FilenameFilter
import java.io.IOException
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
@@ -43,7 +41,6 @@ import org.yuzu.yuzu_emu.NativeLibrary
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.activities.EmulationActivity
import org.yuzu.yuzu_emu.databinding.ActivityMainBinding
import org.yuzu.yuzu_emu.databinding.DialogProgressBarBinding
import org.yuzu.yuzu_emu.features.DocumentProvider
import org.yuzu.yuzu_emu.features.settings.model.Settings
import org.yuzu.yuzu_emu.fragments.IndeterminateProgressDialogFragment
@@ -343,11 +340,10 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
val dstPath = DirectoryInitialization.userDirectory + "/keys/"
if (FileUtil.copyUriToInternalStorage(
applicationContext,
result,
dstPath,
"prod.keys"
)
) != null
) {
if (NativeLibrary.reloadKeys()) {
Toast.makeText(
@@ -446,11 +442,10 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
val dstPath = DirectoryInitialization.userDirectory + "/keys/"
if (FileUtil.copyUriToInternalStorage(
applicationContext,
result,
dstPath,
"key_retail.bin"
)
) != null
) {
if (NativeLibrary.reloadKeys()) {
Toast.makeText(
@@ -469,59 +464,6 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
}
}
val getDriver =
registerForActivityResult(ActivityResultContracts.OpenDocument()) { result ->
if (result == null) {
return@registerForActivityResult
}
val takeFlags =
Intent.FLAG_GRANT_WRITE_URI_PERMISSION or Intent.FLAG_GRANT_READ_URI_PERMISSION
contentResolver.takePersistableUriPermission(
result,
takeFlags
)
val progressBinding = DialogProgressBarBinding.inflate(layoutInflater)
progressBinding.progressBar.isIndeterminate = true
val installationDialog = MaterialAlertDialogBuilder(this)
.setTitle(R.string.installing_driver)
.setView(progressBinding.root)
.show()
lifecycleScope.launch {
withContext(Dispatchers.IO) {
// Ignore file exceptions when a user selects an invalid zip
try {
GpuDriverHelper.installCustomDriver(applicationContext, result)
} catch (_: IOException) {
}
withContext(Dispatchers.Main) {
installationDialog.dismiss()
val driverName = GpuDriverHelper.customDriverName
if (driverName != null) {
Toast.makeText(
applicationContext,
getString(
R.string.select_gpu_driver_install_success,
driverName
),
Toast.LENGTH_SHORT
).show()
} else {
Toast.makeText(
applicationContext,
R.string.select_gpu_driver_error,
Toast.LENGTH_LONG
).show()
}
}
}
}
}
val installGameUpdate = registerForActivityResult(
ActivityResultContracts.OpenMultipleDocuments()
) { documents: List<Uri> ->

View File

@@ -7,7 +7,6 @@ import android.net.Uri
import androidx.documentfile.provider.DocumentFile
import java.io.File
import java.util.*
import org.yuzu.yuzu_emu.YuzuApplication
import org.yuzu.yuzu_emu.model.MinimalDocumentFile
class DocumentsTree {
@@ -22,7 +21,7 @@ class DocumentsTree {
fun openContentUri(filepath: String, openMode: String?): Int {
val node = resolvePath(filepath) ?: return -1
return FileUtil.openContentUri(YuzuApplication.appContext, node.uri.toString(), openMode)
return FileUtil.openContentUri(node.uri.toString(), openMode)
}
fun getFileSize(filepath: String): Long {
@@ -30,7 +29,7 @@ class DocumentsTree {
return if (node == null || node.isDirectory) {
0
} else {
FileUtil.getFileSize(YuzuApplication.appContext, node.uri.toString())
FileUtil.getFileSize(node.uri.toString())
}
}
@@ -67,7 +66,7 @@ class DocumentsTree {
* @param parent parent node of this level
*/
private fun structTree(parent: DocumentsNode) {
val documents = FileUtil.listFiles(YuzuApplication.appContext, parent.uri!!)
val documents = FileUtil.listFiles(parent.uri!!)
for (document in documents) {
val node = DocumentsNode(document)
node.parent = parent

View File

@@ -3,7 +3,6 @@
package org.yuzu.yuzu_emu.utils
import android.content.Context
import android.database.Cursor
import android.net.Uri
import android.provider.DocumentsContract
@@ -11,7 +10,6 @@ import androidx.documentfile.provider.DocumentFile
import kotlinx.coroutines.flow.StateFlow
import java.io.BufferedInputStream
import java.io.File
import java.io.FileOutputStream
import java.io.IOException
import java.io.InputStream
import java.net.URLDecoder
@@ -21,6 +19,8 @@ import org.yuzu.yuzu_emu.YuzuApplication
import org.yuzu.yuzu_emu.model.MinimalDocumentFile
import org.yuzu.yuzu_emu.model.TaskState
import java.io.BufferedOutputStream
import java.lang.NullPointerException
import java.nio.charset.StandardCharsets
import java.util.zip.ZipOutputStream
object FileUtil {
@@ -29,6 +29,8 @@ object FileUtil {
const val APPLICATION_OCTET_STREAM = "application/octet-stream"
const val TEXT_PLAIN = "text/plain"
private val context get() = YuzuApplication.appContext
/**
* Create a file from directory with filename.
* @param context Application context
@@ -36,11 +38,11 @@ object FileUtil {
* @param filename file display name.
* @return boolean
*/
fun createFile(context: Context?, directory: String?, filename: String): DocumentFile? {
fun createFile(directory: String?, filename: String): DocumentFile? {
var decodedFilename = filename
try {
val directoryUri = Uri.parse(directory)
val parent = DocumentFile.fromTreeUri(context!!, directoryUri) ?: return null
val parent = DocumentFile.fromTreeUri(context, directoryUri) ?: return null
decodedFilename = URLDecoder.decode(decodedFilename, DECODE_METHOD)
var mimeType = APPLICATION_OCTET_STREAM
if (decodedFilename.endsWith(".txt")) {
@@ -56,16 +58,15 @@ object FileUtil {
/**
* Create a directory from directory with filename.
* @param context Application context
* @param directory parent path for directory.
* @param directoryName directory display name.
* @return boolean
*/
fun createDir(context: Context?, directory: String?, directoryName: String?): DocumentFile? {
fun createDir(directory: String?, directoryName: String?): DocumentFile? {
var decodedDirectoryName = directoryName
try {
val directoryUri = Uri.parse(directory)
val parent = DocumentFile.fromTreeUri(context!!, directoryUri) ?: return null
val parent = DocumentFile.fromTreeUri(context, directoryUri) ?: return null
decodedDirectoryName = URLDecoder.decode(decodedDirectoryName, DECODE_METHOD)
val isExist = parent.findFile(decodedDirectoryName)
return isExist ?: parent.createDirectory(decodedDirectoryName)
@@ -77,13 +78,12 @@ object FileUtil {
/**
* Open content uri and return file descriptor to JNI.
* @param context Application context
* @param path Native content uri path
* @param openMode will be one of "r", "r", "rw", "wa", "rwa"
* @return file descriptor
*/
@JvmStatic
fun openContentUri(context: Context, path: String, openMode: String?): Int {
fun openContentUri(path: String, openMode: String?): Int {
try {
val uri = Uri.parse(path)
val parcelFileDescriptor = context.contentResolver.openFileDescriptor(uri, openMode!!)
@@ -103,11 +103,10 @@ object FileUtil {
/**
* Reference: https://stackoverflow.com/questions/42186820/documentfile-is-very-slow
* This function will be faster than DoucmentFile.listFiles
* @param context Application context
* @param uri Directory uri.
* @return CheapDocument lists.
*/
fun listFiles(context: Context, uri: Uri): Array<MinimalDocumentFile> {
fun listFiles(uri: Uri): Array<MinimalDocumentFile> {
val resolver = context.contentResolver
val columns = arrayOf(
DocumentsContract.Document.COLUMN_DOCUMENT_ID,
@@ -145,7 +144,7 @@ object FileUtil {
* @param path Native content uri path
* @return bool
*/
fun exists(context: Context, path: String?): Boolean {
fun exists(path: String?): Boolean {
var c: Cursor? = null
try {
val mUri = Uri.parse(path)
@@ -165,7 +164,7 @@ object FileUtil {
* @param path content uri path
* @return bool
*/
fun isDirectory(context: Context, path: String): Boolean {
fun isDirectory(path: String): Boolean {
val resolver = context.contentResolver
val columns = arrayOf(
DocumentsContract.Document.COLUMN_MIME_TYPE
@@ -210,10 +209,10 @@ object FileUtil {
return filename
}
fun getFilesName(context: Context, path: String): Array<String> {
fun getFilesName(path: String): Array<String> {
val uri = Uri.parse(path)
val files: MutableList<String> = ArrayList()
for (file in listFiles(context, uri)) {
for (file in listFiles(uri)) {
files.add(file.filename)
}
return files.toTypedArray()
@@ -225,7 +224,7 @@ object FileUtil {
* @return long file size
*/
@JvmStatic
fun getFileSize(context: Context, path: String): Long {
fun getFileSize(path: String): Long {
val resolver = context.contentResolver
val columns = arrayOf(
DocumentsContract.Document.COLUMN_SIZE
@@ -245,44 +244,38 @@ object FileUtil {
return size
}
/**
* Creates an input stream with a given [Uri] and copies its data to the given path. This will
* overwrite any pre-existing files.
*
* @param sourceUri The [Uri] to copy data from
* @param destinationParentPath Destination directory
* @param destinationFilename Optionally renames the file once copied
*/
fun copyUriToInternalStorage(
context: Context,
sourceUri: Uri?,
sourceUri: Uri,
destinationParentPath: String,
destinationFilename: String
): Boolean {
var input: InputStream? = null
var output: FileOutputStream? = null
destinationFilename: String = ""
): File? =
try {
input = context.contentResolver.openInputStream(sourceUri!!)
output = FileOutputStream("$destinationParentPath/$destinationFilename")
val buffer = ByteArray(1024)
var len: Int
while (input!!.read(buffer).also { len = it } != -1) {
output.write(buffer, 0, len)
val fileName =
if (destinationFilename == "") getFilename(sourceUri) else "/$destinationFilename"
val inputStream = context.contentResolver.openInputStream(sourceUri)!!
val destinationFile = File("$destinationParentPath$fileName")
if (destinationFile.exists()) {
destinationFile.delete()
}
output.flush()
return true
} catch (e: Exception) {
Log.error("[FileUtil]: Cannot copy file, error: " + e.message)
} finally {
if (input != null) {
try {
input.close()
} catch (e: IOException) {
Log.error("[FileUtil]: Cannot close input file, error: " + e.message)
}
}
if (output != null) {
try {
output.close()
} catch (e: IOException) {
Log.error("[FileUtil]: Cannot close output file, error: " + e.message)
}
destinationFile.outputStream().use { fos ->
inputStream.use { it.copyTo(fos) }
}
destinationFile
} catch (e: IOException) {
null
} catch (e: NullPointerException) {
null
}
return false
}
/**
* Extracts the given zip file into the given directory.
@@ -368,4 +361,12 @@ object FileUtil {
return fileName.substring(fileName.lastIndexOf(".") + 1)
.lowercase()
}
@Throws(IOException::class)
fun getStringFromFile(file: File): String =
String(file.readBytes(), StandardCharsets.UTF_8)
@Throws(IOException::class)
fun getStringFromInputStream(stream: InputStream): String =
String(stream.readBytes(), StandardCharsets.UTF_8)
}

View File

@@ -30,7 +30,7 @@ object GameHelper {
// Ensure keys are loaded so that ROM metadata can be decrypted.
NativeLibrary.reloadKeys()
addGamesRecursive(games, FileUtil.listFiles(context, gamesUri), 3)
addGamesRecursive(games, FileUtil.listFiles(gamesUri), 3)
// Cache list of games found on disk
val serializedGames = mutableSetOf<String>()
@@ -58,7 +58,7 @@ object GameHelper {
if (it.isDirectory) {
addGamesRecursive(
games,
FileUtil.listFiles(YuzuApplication.appContext, it.uri),
FileUtil.listFiles(it.uri),
depth - 1
)
} else {

View File

@@ -3,64 +3,33 @@
package org.yuzu.yuzu_emu.utils
import android.content.Context
import android.net.Uri
import android.os.Build
import java.io.BufferedInputStream
import java.io.File
import java.io.FileInputStream
import java.io.FileOutputStream
import java.io.IOException
import java.util.zip.ZipInputStream
import org.yuzu.yuzu_emu.NativeLibrary
import org.yuzu.yuzu_emu.utils.FileUtil.copyUriToInternalStorage
import org.yuzu.yuzu_emu.YuzuApplication
import java.util.zip.ZipException
import java.util.zip.ZipFile
object GpuDriverHelper {
private const val META_JSON_FILENAME = "meta.json"
private const val DRIVER_INTERNAL_FILENAME = "gpu_driver.zip"
private var fileRedirectionPath: String? = null
private var driverInstallationPath: String? = null
var driverInstallationPath: String? = null
private var hookLibPath: String? = null
@Throws(IOException::class)
private fun unzip(zipFilePath: String, destDir: String) {
val dir = File(destDir)
val driverStoragePath get() = DirectoryInitialization.userDirectory!! + "/gpu_drivers/"
// Create output directory if it doesn't exist
if (!dir.exists()) dir.mkdirs()
// Unpack the files.
val inputStream = FileInputStream(zipFilePath)
val zis = ZipInputStream(BufferedInputStream(inputStream))
val buffer = ByteArray(1024)
var ze = zis.nextEntry
while (ze != null) {
val newFile = File(destDir, ze.name)
val canonicalPath = newFile.canonicalPath
if (!canonicalPath.startsWith(destDir + ze.name)) {
throw SecurityException("Zip file attempted path traversal! " + ze.name)
}
newFile.parentFile!!.mkdirs()
val fos = FileOutputStream(newFile)
var len: Int
while (zis.read(buffer).also { len = it } > 0) {
fos.write(buffer, 0, len)
}
fos.close()
zis.closeEntry()
ze = zis.nextEntry
}
zis.closeEntry()
}
fun initializeDriverParameters(context: Context) {
fun initializeDriverParameters() {
try {
// Initialize the file redirection directory.
fileRedirectionPath =
context.getExternalFilesDir(null)!!.canonicalPath + "/gpu/vk_file_redirect/"
fileRedirectionPath = YuzuApplication.appContext
.getExternalFilesDir(null)!!.canonicalPath + "/gpu/vk_file_redirect/"
// Initialize the driver installation directory.
driverInstallationPath = context.filesDir.canonicalPath + "/gpu_driver/"
driverInstallationPath = YuzuApplication.appContext
.filesDir.canonicalPath + "/gpu_driver/"
} catch (e: IOException) {
throw RuntimeException(e)
}
@@ -69,68 +38,169 @@ object GpuDriverHelper {
initializeDirectories()
// Initialize hook libraries directory.
hookLibPath = context.applicationInfo.nativeLibraryDir + "/"
hookLibPath = YuzuApplication.appContext.applicationInfo.nativeLibraryDir + "/"
// Initialize GPU driver.
NativeLibrary.initializeGpuDriver(
hookLibPath,
driverInstallationPath,
customDriverLibraryName,
customDriverData.libraryName,
fileRedirectionPath
)
}
fun installDefaultDriver(context: Context) {
// Removing the installed driver will result in the backend using the default system driver.
val driverInstallationDir = File(driverInstallationPath!!)
deleteRecursive(driverInstallationDir)
initializeDriverParameters(context)
fun getDrivers(): MutableList<Pair<String, GpuDriverMetadata>> {
val driverZips = File(driverStoragePath).listFiles()
val drivers: MutableList<Pair<String, GpuDriverMetadata>> =
driverZips
?.mapNotNull {
val metadata = getMetadataFromZip(it)
metadata.name?.let { _ -> Pair(it.path, metadata) }
}
?.sortedByDescending { it: Pair<String, GpuDriverMetadata> -> it.second.name }
?.distinct()
?.toMutableList() ?: mutableListOf()
// TODO: Get system driver information
drivers.add(0, Pair("", GpuDriverMetadata()))
return drivers
}
fun installCustomDriver(context: Context, driverPathUri: Uri?) {
fun installDefaultDriver() {
// Removing the installed driver will result in the backend using the default system driver.
File(driverInstallationPath!!).deleteRecursively()
initializeDriverParameters()
}
fun copyDriverToInternalStorage(driverUri: Uri): Boolean {
// Ensure we have directories.
initializeDirectories()
// Copy the zip file URI to user data
val copiedFile =
FileUtil.copyUriToInternalStorage(driverUri, driverStoragePath) ?: return false
// Validate driver
val metadata = getMetadataFromZip(copiedFile)
if (metadata.name == null) {
copiedFile.delete()
return false
}
if (metadata.minApi > Build.VERSION.SDK_INT) {
copiedFile.delete()
return false
}
return true
}
/**
* Copies driver zip into user data directory so that it can be exported along with
* other user data and also unzipped into the installation directory
*/
fun installCustomDriver(driverUri: Uri): Boolean {
// Revert to system default in the event the specified driver is bad.
installDefaultDriver(context)
installDefaultDriver()
// Ensure we have directories.
initializeDirectories()
// Copy the zip file URI into our private storage.
copyUriToInternalStorage(
context,
driverPathUri,
driverInstallationPath!!,
DRIVER_INTERNAL_FILENAME
)
// Copy the zip file URI to user data
val copiedFile =
FileUtil.copyUriToInternalStorage(driverUri, driverStoragePath) ?: return false
// Validate driver
val metadata = getMetadataFromZip(copiedFile)
if (metadata.name == null) {
copiedFile.delete()
return false
}
if (metadata.minApi > Build.VERSION.SDK_INT) {
copiedFile.delete()
return false
}
// Unzip the driver.
try {
unzip(driverInstallationPath + DRIVER_INTERNAL_FILENAME, driverInstallationPath!!)
FileUtil.unzipToInternalStorage(
BufferedInputStream(copiedFile.inputStream()),
File(driverInstallationPath!!)
)
} catch (e: SecurityException) {
return
return false
}
// Initialize the driver parameters.
initializeDriverParameters(context)
initializeDriverParameters()
return true
}
/**
* Unzips driver into installation directory
*/
fun installCustomDriver(driver: File): Boolean {
// Revert to system default in the event the specified driver is bad.
installDefaultDriver()
// Ensure we have directories.
initializeDirectories()
// Validate driver
val metadata = getMetadataFromZip(driver)
if (metadata.name == null) {
driver.delete()
return false
}
// Unzip the driver to the private installation directory
try {
FileUtil.unzipToInternalStorage(
BufferedInputStream(driver.inputStream()),
File(driverInstallationPath!!)
)
} catch (e: SecurityException) {
return false
}
// Initialize the driver parameters.
initializeDriverParameters()
return true
}
/**
* Takes in a zip file and reads the meta.json file for presentation to the UI
*
* @param driver Zip containing driver and meta.json file
* @return A non-null [GpuDriverMetadata] instance that may have null members
*/
fun getMetadataFromZip(driver: File): GpuDriverMetadata {
try {
ZipFile(driver).use { zf ->
val entries = zf.entries()
while (entries.hasMoreElements()) {
val entry = entries.nextElement()
if (!entry.isDirectory && entry.name.lowercase().contains(".json")) {
zf.getInputStream(entry).use {
return GpuDriverMetadata(it, entry.size)
}
}
}
}
} catch (_: ZipException) {
}
return GpuDriverMetadata()
}
external fun supportsCustomDriverLoading(): Boolean
// Parse the custom driver metadata to retrieve the name.
val customDriverName: String?
get() {
val metadata = GpuDriverMetadata(driverInstallationPath + META_JSON_FILENAME)
return metadata.name
}
val customDriverData: GpuDriverMetadata
get() = GpuDriverMetadata(File(driverInstallationPath + META_JSON_FILENAME))
// Parse the custom driver metadata to retrieve the library name.
private val customDriverLibraryName: String?
get() {
// Parse the custom driver metadata to retrieve the library name.
val metadata = GpuDriverMetadata(driverInstallationPath + META_JSON_FILENAME)
return metadata.libraryName
}
private fun initializeDirectories() {
fun initializeDirectories() {
// Ensure the file redirection directory exists.
val fileRedirectionDir = File(fileRedirectionPath!!)
if (!fileRedirectionDir.exists()) {
@@ -141,14 +211,10 @@ object GpuDriverHelper {
if (!driverInstallationDir.exists()) {
driverInstallationDir.mkdirs()
}
}
private fun deleteRecursive(fileOrDirectory: File) {
if (fileOrDirectory.isDirectory) {
for (child in fileOrDirectory.listFiles()!!) {
deleteRecursive(child)
}
// Ensure the driver storage directory exists
val driverStorageDirectory = File(driverStoragePath)
if (!driverStorageDirectory.exists()) {
driverStorageDirectory.mkdirs()
}
fileOrDirectory.delete()
}
}

View File

@@ -4,29 +4,29 @@
package org.yuzu.yuzu_emu.utils
import java.io.IOException
import java.nio.charset.StandardCharsets
import java.nio.file.Files
import java.nio.file.Paths
import org.json.JSONException
import org.json.JSONObject
import java.io.File
import java.io.InputStream
class GpuDriverMetadata(metadataFilePath: String) {
var name: String? = null
var description: String? = null
var author: String? = null
var vendor: String? = null
var driverVersion: String? = null
var minApi = 0
var libraryName: String? = null
class GpuDriverMetadata {
/**
* Tries to get driver metadata information from a meta.json [File]
*
* @param metadataFile meta.json file provided with a GPU driver
*/
constructor(metadataFile: File) {
if (metadataFile.length() > MAX_META_SIZE_BYTES) {
return
}
init {
try {
val json = JSONObject(getStringFromFile(metadataFilePath))
val json = JSONObject(FileUtil.getStringFromFile(metadataFile))
name = json.getString("name")
description = json.getString("description")
author = json.getString("author")
vendor = json.getString("vendor")
driverVersion = json.getString("driverVersion")
version = json.getString("driverVersion")
minApi = json.getInt("minApi")
libraryName = json.getString("libraryName")
} catch (e: JSONException) {
@@ -36,12 +36,84 @@ class GpuDriverMetadata(metadataFilePath: String) {
}
}
companion object {
@Throws(IOException::class)
private fun getStringFromFile(filePath: String): String {
val path = Paths.get(filePath)
val bytes = Files.readAllBytes(path)
return String(bytes, StandardCharsets.UTF_8)
/**
* Tries to get driver metadata information from an input stream that's intended to be
* from a zip file
*
* @param metadataStream ZipEntry input stream
* @param size Size of the file in bytes
*/
constructor(metadataStream: InputStream, size: Long) {
if (size > MAX_META_SIZE_BYTES) {
return
}
try {
val json = JSONObject(FileUtil.getStringFromInputStream(metadataStream))
name = json.getString("name")
description = json.getString("description")
author = json.getString("author")
vendor = json.getString("vendor")
version = json.getString("driverVersion")
minApi = json.getInt("minApi")
libraryName = json.getString("libraryName")
} catch (e: JSONException) {
// JSON is malformed, ignore and treat as unsupported metadata.
} catch (e: IOException) {
// File is inaccessible, ignore and treat as unsupported metadata.
}
}
/**
* Creates an empty metadata instance
*/
constructor()
override fun equals(other: Any?): Boolean {
if (other !is GpuDriverMetadata) {
return false
}
return other.name == name &&
other.description == description &&
other.author == author &&
other.vendor == vendor &&
other.version == version &&
other.minApi == minApi &&
other.libraryName == libraryName
}
override fun hashCode(): Int {
var result = name?.hashCode() ?: 0
result = 31 * result + (description?.hashCode() ?: 0)
result = 31 * result + (author?.hashCode() ?: 0)
result = 31 * result + (vendor?.hashCode() ?: 0)
result = 31 * result + (version?.hashCode() ?: 0)
result = 31 * result + minApi
result = 31 * result + (libraryName?.hashCode() ?: 0)
return result
}
override fun toString(): String =
"""
Name - $name
Description - $description
Author - $author
Vendor - $vendor
Version - $version
Min API - $minApi
Library Name - $libraryName
""".trimMargin().trimIndent()
var name: String? = null
var description: String? = null
var author: String? = null
var vendor: String? = null
var version: String? = null
var minApi = 0
var libraryName: String? = null
companion object {
private const val MAX_META_SIZE_BYTES = 500000
}
}

View File

@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="?attr/colorControlNormal"
android:pathData="M22.7,19l-9.1,-9.1c0.9,-2.3 0.4,-5 -1.5,-6.9 -2,-2 -5,-2.4 -7.4,-1.3L9,6 6,9 1.6,4.7C0.4,7.1 0.9,10.1 2.9,12.1c1.9,1.9 4.6,2.4 6.9,1.5l9.1,9.1c0.4,0.4 1,0.4 1.4,0l2.3,-2.3c0.5,-0.4 0.5,-1.1 0.1,-1.4z" />
</vector>

View File

@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="?attr/colorControlNormal"
android:pathData="M6,19c0,1.1 0.9,2 2,2h8c1.1,0 2,-0.9 2,-2V7H6v12zM19,4h-3.5l-1,-1h-5l-1,1H5v2h14V4z" />
</vector>

View File

@@ -0,0 +1,89 @@
<?xml version="1.0" encoding="utf-8"?>
<com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
style="?attr/materialCardViewOutlinedStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="16dp"
android:layout_marginVertical="12dp"
android:background="?attr/selectableItemBackground"
android:clickable="true"
android:focusable="true">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_gravity="center"
android:padding="16dp">
<RadioButton
android:id="@+id/radio_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:clickable="false"
android:checked="false" />
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="vertical"
android:layout_gravity="center_vertical">
<com.google.android.material.textview.MaterialTextView
android:id="@+id/title"
style="@style/TextAppearance.Material3.TitleMedium"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ellipsize="none"
android:marqueeRepeatLimit="marquee_forever"
android:requiresFadingEdge="horizontal"
android:singleLine="true"
android:textAlignment="viewStart"
tools:text="@string/select_gpu_driver_default" />
<com.google.android.material.textview.MaterialTextView
android:id="@+id/version"
style="@style/TextAppearance.Material3.BodyMedium"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="6dp"
android:ellipsize="none"
android:marqueeRepeatLimit="marquee_forever"
android:requiresFadingEdge="horizontal"
android:singleLine="true"
android:textAlignment="viewStart"
tools:text="@string/install_gpu_driver_description" />
<com.google.android.material.textview.MaterialTextView
android:id="@+id/description"
style="@style/TextAppearance.Material3.BodyMedium"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="6dp"
android:ellipsize="none"
android:marqueeRepeatLimit="marquee_forever"
android:requiresFadingEdge="horizontal"
android:singleLine="true"
android:textAlignment="viewStart"
tools:text="@string/install_gpu_driver_description" />
</LinearLayout>
<Button
android:id="@+id/button_delete"
style="@style/Widget.Material3.Button.IconButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:contentDescription="@string/delete"
android:tooltipText="@string/delete"
app:icon="@drawable/ic_delete"
app:iconTint="?attr/colorControlNormal" />
</LinearLayout>
</com.google.android.material.card.MaterialCardView>

View File

@@ -0,0 +1,48 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/coordinator_licenses"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?attr/colorSurface">
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/appbar_drivers"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fitsSystemWindows="true"
app:liftOnScrollTargetViewId="@id/list_drivers">
<com.google.android.material.appbar.MaterialToolbar
android:id="@+id/toolbar_drivers"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:navigationIcon="@drawable/ic_back"
app:title="@string/gpu_driver_manager" />
</com.google.android.material.appbar.AppBarLayout>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/list_drivers"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false"
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
<com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
android:id="@+id/button_install"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:text="@string/install"
app:icon="@drawable/ic_add"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -22,6 +22,9 @@
<action
android:id="@+id/action_homeSettingsFragment_to_installableFragment"
app:destination="@id/installableFragment" />
<action
android:id="@+id/action_homeSettingsFragment_to_driverManagerFragment"
app:destination="@id/driverManagerFragment" />
</fragment>
<fragment
@@ -95,5 +98,9 @@
android:id="@+id/installableFragment"
android:name="org.yuzu.yuzu_emu.fragments.InstallableFragment"
android:label="InstallableFragment" />
<fragment
android:id="@+id/driverManagerFragment"
android:name="org.yuzu.yuzu_emu.fragments.DriverManagerFragment"
android:label="DriverManagerFragment" />
</navigation>

View File

@@ -168,9 +168,7 @@
<string name="select_gpu_driver_title">Möchtest du deinen aktuellen GPU-Treiber ersetzen?</string>
<string name="select_gpu_driver_install">Installieren</string>
<string name="select_gpu_driver_default">Standard</string>
<string name="select_gpu_driver_install_success">%s wurde installiert</string>
<string name="select_gpu_driver_use_default">Standard GPU-Treiber wird verwendet</string>
<string name="select_gpu_driver_error">Ungültiger Treiber ausgewählt, Standard-Treiber wird verwendet!</string>
<string name="system_gpu_driver">System GPU-Treiber</string>
<string name="installing_driver">Treiber wird installiert...</string>

View File

@@ -171,9 +171,7 @@
<string name="select_gpu_driver_title">¿Quiere reemplazar el driver de GPU actual?</string>
<string name="select_gpu_driver_install">Instalar</string>
<string name="select_gpu_driver_default">Predeterminado</string>
<string name="select_gpu_driver_install_success">Instalado %s</string>
<string name="select_gpu_driver_use_default">Usando el driver de GPU por defecto </string>
<string name="select_gpu_driver_error">¡Driver no válido, utilizando el predeterminado del sistema!</string>
<string name="system_gpu_driver">Driver GPU del sistema</string>
<string name="installing_driver">Instalando driver...</string>

View File

@@ -171,9 +171,7 @@
<string name="select_gpu_driver_title">Souhaitez vous remplacer votre pilote actuel ?</string>
<string name="select_gpu_driver_install">Installer</string>
<string name="select_gpu_driver_default">Défaut</string>
<string name="select_gpu_driver_install_success">%s Installé</string>
<string name="select_gpu_driver_use_default">Utilisation du pilote de GPU par défaut</string>
<string name="select_gpu_driver_error">Pilote non valide sélectionné, utilisation du paramètre par défaut du système !</string>
<string name="system_gpu_driver">Pilote du GPU du système</string>
<string name="installing_driver">Installation du pilote...</string>

View File

@@ -171,9 +171,7 @@
<string name="select_gpu_driver_title">Vuoi sostituire il driver della tua GPU attuale?</string>
<string name="select_gpu_driver_install">Installa</string>
<string name="select_gpu_driver_default">Predefinito</string>
<string name="select_gpu_driver_install_success">Installato%s</string>
<string name="select_gpu_driver_use_default">Utilizza il driver predefinito della GPU.</string>
<string name="select_gpu_driver_error">Il driver selezionato è invalido, è in utilizzo quello predefinito di sistema!</string>
<string name="system_gpu_driver">Driver GPU del sistema</string>
<string name="installing_driver">Installando i driver...</string>

View File

@@ -170,9 +170,7 @@
<string name="select_gpu_driver_title">現在のGPUドライバーを置き換えますか</string>
<string name="select_gpu_driver_install">インストール</string>
<string name="select_gpu_driver_default">デフォルト</string>
<string name="select_gpu_driver_install_success">%s をインストールしました</string>
<string name="select_gpu_driver_use_default">デフォルトのGPUドライバーを使用します</string>
<string name="select_gpu_driver_error">選択されたドライバが無効なため、システムのデフォルトを使用します!</string>
<string name="system_gpu_driver">システムのGPUドライバ</string>
<string name="installing_driver">インストール中…</string>

View File

@@ -171,9 +171,7 @@
<string name="select_gpu_driver_title">현재 사용 중인 GPU 드라이버를 교체하겠습니까?</string>
<string name="select_gpu_driver_install">설치</string>
<string name="select_gpu_driver_default">기본값</string>
<string name="select_gpu_driver_install_success">설치된 %s</string>
<string name="select_gpu_driver_use_default">기본 GPU 드라이버 사용</string>
<string name="select_gpu_driver_error">시스템 기본값을 사용하여 잘못된 드라이버를 선택했습니다!</string>
<string name="system_gpu_driver">시스템 GPU 드라이버</string>
<string name="installing_driver">드라이버 설치 중...</string>

View File

@@ -171,9 +171,7 @@
<string name="select_gpu_driver_title">Ønsker du å bytte ut din nåværende GPU-driver?</string>
<string name="select_gpu_driver_install">Installer</string>
<string name="select_gpu_driver_default">Standard</string>
<string name="select_gpu_driver_install_success">Installert %s</string>
<string name="select_gpu_driver_use_default">Bruk av standard GPU-driver</string>
<string name="select_gpu_driver_error">Ugyldig driver valgt, bruker systemstandard!</string>
<string name="system_gpu_driver">Systemets GPU-driver</string>
<string name="installing_driver">Installerer driver...</string>

View File

@@ -171,9 +171,7 @@
<string name="select_gpu_driver_title">Chcesz zastąpić obecny sterownik układu graficznego?</string>
<string name="select_gpu_driver_install">Zainstaluj</string>
<string name="select_gpu_driver_default">Domyślne</string>
<string name="select_gpu_driver_install_success">Zainstalowano %s</string>
<string name="select_gpu_driver_use_default">Aktywny domyślny sterownik GPU</string>
<string name="select_gpu_driver_error">Wybrano błędny sterownik, powrót do domyślnego. </string>
<string name="system_gpu_driver">Systemowy sterownik GPU</string>
<string name="installing_driver">Instalowanie sterownika...</string>

View File

@@ -171,9 +171,7 @@
<string name="select_gpu_driver_title">Queres substituir o driver do GPU atual? </string>
<string name="select_gpu_driver_install">Instalar</string>
<string name="select_gpu_driver_default">Padrão</string>
<string name="select_gpu_driver_install_success">Instalado%s</string>
<string name="select_gpu_driver_use_default">Usar o driver padrão do GPU</string>
<string name="select_gpu_driver_error">Driver selecionado inválido, a usar o padrão do sistema!</string>
<string name="system_gpu_driver">Driver do GPU padrão</string>
<string name="installing_driver">A instalar o Driver...</string>

View File

@@ -171,9 +171,7 @@
<string name="select_gpu_driver_title">Queres substituir o driver do GPU atual? </string>
<string name="select_gpu_driver_install">Instalar</string>
<string name="select_gpu_driver_default">Padrão</string>
<string name="select_gpu_driver_install_success">Instalado%s</string>
<string name="select_gpu_driver_use_default">Usar o driver padrão do GPU</string>
<string name="select_gpu_driver_error">Driver selecionado inválido, a usar o padrão do sistema!</string>
<string name="system_gpu_driver">Driver do GPU padrão</string>
<string name="installing_driver">A instalar o Driver...</string>

View File

@@ -171,9 +171,7 @@
<string name="select_gpu_driver_title">Хотите заменить текущий драйвер ГП?</string>
<string name="select_gpu_driver_install">Установить</string>
<string name="select_gpu_driver_default">По умолчанию</string>
<string name="select_gpu_driver_install_success">Установлено %s</string>
<string name="select_gpu_driver_use_default">Используется стандартный драйвер ГП </string>
<string name="select_gpu_driver_error">Выбран неверный драйвер, используется стандартный системный!</string>
<string name="system_gpu_driver">Системный драйвер ГП</string>
<string name="installing_driver">Установка драйвера...</string>

View File

@@ -171,9 +171,7 @@
<string name="select_gpu_driver_title">Хочете замінити поточний драйвер ГП?</string>
<string name="select_gpu_driver_install">Встановити</string>
<string name="select_gpu_driver_default">За замовчуванням</string>
<string name="select_gpu_driver_install_success">Встановлено %s</string>
<string name="select_gpu_driver_use_default">Використовується стандартний драйвер ГП</string>
<string name="select_gpu_driver_error">Обрано неправильний драйвер, використовується стандартний системний!</string>
<string name="system_gpu_driver">Системний драйвер ГП</string>
<string name="installing_driver">Встановлення драйвера...</string>

View File

@@ -171,9 +171,7 @@
<string name="select_gpu_driver_title">要取代您当前的 GPU 驱动程序吗?</string>
<string name="select_gpu_driver_install">安装</string>
<string name="select_gpu_driver_default">系统默认</string>
<string name="select_gpu_driver_install_success">已安装 %s</string>
<string name="select_gpu_driver_use_default">使用默认 GPU 驱动程序</string>
<string name="select_gpu_driver_error">选择的驱动程序无效,将使用系统默认的驱动程序!</string>
<string name="system_gpu_driver">系统 GPU 驱动程序</string>
<string name="installing_driver">正在安装驱动程序…</string>

View File

@@ -171,9 +171,7 @@
<string name="select_gpu_driver_title">要取代您目前的 GPU 驅動程式嗎?</string>
<string name="select_gpu_driver_install">安裝</string>
<string name="select_gpu_driver_default">預設</string>
<string name="select_gpu_driver_install_success">已安裝 %s</string>
<string name="select_gpu_driver_use_default">使用預設 GPU 驅動程式</string>
<string name="select_gpu_driver_error">選取的驅動程式無效,將使用系統預設驅動程式!</string>
<string name="system_gpu_driver">系統 GPU 驅動程式</string>
<string name="installing_driver">正在安裝驅動程式…</string>

View File

@@ -13,6 +13,8 @@
<dimen name="menu_width">256dp</dimen>
<dimen name="card_width">165dp</dimen>
<dimen name="icon_inset">24dp</dimen>
<dimen name="spacing_bottom_list_fab">72dp</dimen>
<dimen name="spacing_fab">24dp</dimen>
<dimen name="dialog_margin">20dp</dimen>
<dimen name="elevated_app_bar">3dp</dimen>

View File

@@ -72,6 +72,7 @@
<string name="invalid_keys_error">Invalid encryption keys</string>
<string name="dumping_keys_quickstart_link">https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys</string>
<string name="install_keys_failure_description">The selected file is incorrect or corrupt. Please redump your keys.</string>
<string name="gpu_driver_manager">GPU Driver Manager</string>
<string name="install_gpu_driver">Install GPU driver</string>
<string name="install_gpu_driver_description">Install alternative drivers for potentially better performance or accuracy</string>
<string name="advanced_settings">Advanced settings</string>
@@ -234,15 +235,17 @@
<string name="export_failed">Export failed</string>
<string name="import_failed">Import failed</string>
<string name="cancelling">Cancelling</string>
<string name="install">Install</string>
<string name="delete">Delete</string>
<!-- GPU driver installation -->
<string name="select_gpu_driver">Select GPU driver</string>
<string name="select_gpu_driver_title">Would you like to replace your current GPU driver?</string>
<string name="select_gpu_driver_install">Install</string>
<string name="select_gpu_driver_default">Default</string>
<string name="select_gpu_driver_install_success">Installed %s</string>
<string name="select_gpu_driver_use_default">Using default GPU driver</string>
<string name="select_gpu_driver_error">Invalid driver selected, using system default!</string>
<string name="select_gpu_driver_error">Invalid driver selected</string>
<string name="driver_already_installed">Driver already installed</string>
<string name="system_gpu_driver">System GPU driver</string>
<string name="installing_driver">Installing driver…</string>

View File

@@ -181,10 +181,20 @@ add_library(core STATIC
frontend/framebuffer_layout.cpp
frontend/framebuffer_layout.h
frontend/graphics_context.h
hid/emulated_console.cpp
hid/emulated_console.h
hid/emulated_controller.cpp
hid/emulated_controller.h
hid/emulated_devices.cpp
hid/emulated_devices.h
hid/hid_core.cpp
hid/hid_core.h
hid/hid_types.h
hid/input_converter.cpp
hid/input_converter.h
hid/input_interpreter.cpp
hid/input_interpreter.h
hid/irs_types.h
hid/motion_input.cpp
hid/motion_input.h
hle/api_version.h
@@ -511,53 +521,59 @@ add_library(core STATIC
hle/service/grc/grc.h
hle/service/hid/hid.cpp
hle/service/hid/hid.h
hle/service/hid/hid_debug_server.cpp
hle/service/hid/hid_debug_server.h
hle/service/hid/hid_firmware_settings.cpp
hle/service/hid/hid_firmware_settings.h
hle/service/hid/hid_result.h
hle/service/hid/hid_server.cpp
hle/service/hid/hid_server.h
hle/service/hid/hid_system_server.cpp
hle/service/hid/hid_system_server.h
hle/service/hid/hid_types.h
hle/service/hid/hid_util.h
hle/service/hid/hidbus.cpp
hle/service/hid/hidbus.h
hle/service/hid/irs.cpp
hle/service/hid/irs.h
hle/service/hid/resource_manager.cpp
hle/service/hid/resource_manager.h
hle/service/hid/irs_ring_lifo.h
hle/service/hid/ring_lifo.h
hle/service/hid/xcd.cpp
hle/service/hid/xcd.h
hle/service/hid/resource_manager/npad_resource/npad.cpp
hle/service/hid/resource_manager/npad_resource/npad.h
hle/service/hid/resource_manager/npad_resource/npad_controller_state.cpp
hle/service/hid/resource_manager/npad_resource/npad_controller_state.h
hle/service/hid/resource_manager/npad_resource/npad_state.cpp
hle/service/hid/resource_manager/npad_resource/npad_state.h
hle/service/hid/resource_manager/npad_resource/npad_shared_types.h
hle/service/hid/resource_manager/base_resource.cpp
hle/service/hid/resource_manager/base_resource.h
hle/service/hid/resource_manager/debug_pad.cpp
hle/service/hid/resource_manager/debug_pad.h
hle/service/hid/resource_manager/gesture.cpp
hle/service/hid/resource_manager/gesture.h
hle/service/hid/resource_manager/keyboard.cpp
hle/service/hid/resource_manager/keyboard.h
hle/service/hid/resource_manager/mouse.cpp
hle/service/hid/resource_manager/mouse.h
hle/service/hid/resource_manager/palma.cpp
hle/service/hid/resource_manager/palma.h
hle/service/hid/resource_manager/ring_lifo.h
hle/service/hid/resource_manager/sixaxis.cpp
hle/service/hid/resource_manager/sixaxis.h
hle/service/hid/resource_manager/touch_screen.cpp
hle/service/hid/resource_manager/touch_screen.h
hle/service/hid/hidbus/hidbus_result.h
hle/service/hid/hidbus/hidbus_types.h
hle/service/hid/irs/irs_result.h
hle/service/hid/irs/irs_types.h
hle/service/hid/errors.h
hle/service/hid/controllers/console_sixaxis.cpp
hle/service/hid/controllers/console_sixaxis.h
hle/service/hid/controllers/controller_base.cpp
hle/service/hid/controllers/controller_base.h
hle/service/hid/controllers/debug_pad.cpp
hle/service/hid/controllers/debug_pad.h
hle/service/hid/controllers/gesture.cpp
hle/service/hid/controllers/gesture.h
hle/service/hid/controllers/keyboard.cpp
hle/service/hid/controllers/keyboard.h
hle/service/hid/controllers/mouse.cpp
hle/service/hid/controllers/mouse.h
hle/service/hid/controllers/npad.cpp
hle/service/hid/controllers/npad.h
hle/service/hid/controllers/palma.cpp
hle/service/hid/controllers/palma.h
hle/service/hid/controllers/stubbed.cpp
hle/service/hid/controllers/stubbed.h
hle/service/hid/controllers/touchscreen.cpp
hle/service/hid/controllers/touchscreen.h
hle/service/hid/controllers/xpad.cpp
hle/service/hid/controllers/xpad.h
hle/service/hid/hidbus/hidbus_base.cpp
hle/service/hid/hidbus/hidbus_base.h
hle/service/hid/hidbus/ringcon.cpp
hle/service/hid/hidbus/ringcon.h
hle/service/hid/hidbus/starlink.cpp
hle/service/hid/hidbus/starlink.h
hle/service/hid/hidbus/stubbed.cpp
hle/service/hid/hidbus/stubbed.h
hle/service/hid/irsensor/clustering_processor.cpp
hle/service/hid/irsensor/clustering_processor.h
hle/service/hid/irsensor/image_transfer_processor.cpp
hle/service/hid/irsensor/image_transfer_processor.h
hle/service/hid/irsensor/ir_led_processor.cpp
hle/service/hid/irsensor/ir_led_processor.h
hle/service/hid/irsensor/moment_processor.cpp
hle/service/hid/irsensor/moment_processor.h
hle/service/hid/irsensor/pointing_processor.cpp
hle/service/hid/irsensor/pointing_processor.h
hle/service/hid/irsensor/processor_base.cpp
hle/service/hid/irsensor/processor_base.h
hle/service/hid/irsensor/tera_plugin_processor.cpp
hle/service/hid/irsensor/tera_plugin_processor.h
hle/service/lbl/lbl.cpp
hle/service/lbl/lbl.h
hle/service/ldn/lan_discovery.cpp

View File

@@ -6,7 +6,9 @@
#include "common/settings.h"
#include "common/settings_enums.h"
#include "core/frontend/applets/controller.h"
#include "core/hid/emulated_controller.h"
#include "core/hid/hid_core.h"
#include "core/hid/hid_types.h"
namespace Core::Frontend {
@@ -22,54 +24,54 @@ void DefaultControllerApplet::ReconfigureControllers(ReconfigureCallback callbac
const ControllerParameters& parameters) const {
LOG_INFO(Service_HID, "called, deducing the best configuration based on the given parameters!");
//const std::size_t min_supported_players =
// parameters.enable_single_mode ? 1 : parameters.min_players;
const std::size_t min_supported_players =
parameters.enable_single_mode ? 1 : parameters.min_players;
//// Disconnect Handheld first.
//auto* handheld = hid_core.GetEmulatedController(Core::HID::NpadIdType::Handheld);
//handheld->Disconnect();
// Disconnect Handheld first.
auto* handheld = hid_core.GetEmulatedController(Core::HID::NpadIdType::Handheld);
handheld->Disconnect();
//// Deduce the best configuration based on the input parameters.
//for (std::size_t index = 0; index < hid_core.available_controllers - 2; ++index) {
// auto* controller = hid_core.GetEmulatedControllerByIndex(index);
// Deduce the best configuration based on the input parameters.
for (std::size_t index = 0; index < hid_core.available_controllers - 2; ++index) {
auto* controller = hid_core.GetEmulatedControllerByIndex(index);
// // First, disconnect all controllers regardless of the value of keep_controllers_connected.
// // This makes it easy to connect the desired controllers.
// controller->Disconnect();
// First, disconnect all controllers regardless of the value of keep_controllers_connected.
// This makes it easy to connect the desired controllers.
controller->Disconnect();
// // Only connect the minimum number of required players.
// if (index >= min_supported_players) {
// continue;
// }
// Only connect the minimum number of required players.
if (index >= min_supported_players) {
continue;
}
// // 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->Connect(true);
// } else if (parameters.allow_dual_joycons) {
// controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::JoyconDual);
// controller->Connect(true);
// } else if (parameters.allow_left_joycon && parameters.allow_right_joycon) {
// // Assign left joycons to even player indices and right joycons to odd player indices.
// // We do this since Captain Toad Treasure Tracker expects a left joycon for Player 1 and
// // a right Joycon for Player 2 in 2 Player Assist mode.
// if (index % 2 == 0) {
// controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::JoyconLeft);
// controller->Connect(true);
// } else {
// controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::JoyconRight);
// controller->Connect(true);
// }
// } else if (index == 0 && parameters.enable_single_mode && parameters.allow_handheld &&
// !Settings::values.use_docked_mode.GetValue()) {
// // We should *never* reach here under any normal circumstances.
// controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::Handheld);
// controller->Connect(true);
// } else {
// ASSERT_MSG(false, "Unable to add a new controller based on the given parameters!");
// }
//}
// 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->Connect(true);
} else if (parameters.allow_dual_joycons) {
controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::JoyconDual);
controller->Connect(true);
} else if (parameters.allow_left_joycon && parameters.allow_right_joycon) {
// Assign left joycons to even player indices and right joycons to odd player indices.
// We do this since Captain Toad Treasure Tracker expects a left joycon for Player 1 and
// a right Joycon for Player 2 in 2 Player Assist mode.
if (index % 2 == 0) {
controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::JoyconLeft);
controller->Connect(true);
} else {
controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::JoyconRight);
controller->Connect(true);
}
} else if (index == 0 && parameters.enable_single_mode && parameters.allow_handheld &&
!Settings::IsDockedMode()) {
// We should *never* reach here under any normal circumstances.
controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::Handheld);
controller->Connect(true);
} else {
ASSERT_MSG(false, "Unable to add a new controller based on the given parameters!");
}
}
callback(true);
}

View File

@@ -0,0 +1,324 @@
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/settings.h"
#include "core/hid/emulated_console.h"
#include "core/hid/input_converter.h"
namespace Core::HID {
EmulatedConsole::EmulatedConsole() = default;
EmulatedConsole::~EmulatedConsole() = default;
void EmulatedConsole::ReloadFromSettings() {
// Using first motion device from player 1. No need to assign any unique config at the moment
const auto& player = Settings::values.players.GetValue()[0];
motion_params[0] = Common::ParamPackage(player.motions[0]);
ReloadInput();
}
void EmulatedConsole::SetTouchParams() {
std::size_t index = 0;
// We can't use mouse as touch if native mouse is enabled
if (!Settings::values.mouse_enabled) {
touch_params[index++] =
Common::ParamPackage{"engine:mouse,axis_x:0,axis_y:1,button:0,port:2"};
}
touch_params[index++] =
Common::ParamPackage{"engine:cemuhookudp,axis_x:17,axis_y:18,button:65536"};
touch_params[index++] =
Common::ParamPackage{"engine:cemuhookudp,axis_x:19,axis_y:20,button:131072"};
for (int i = 0; i < static_cast<int>(MaxActiveTouchInputs); i++) {
Common::ParamPackage touchscreen_param{};
touchscreen_param.Set("engine", "touch");
touchscreen_param.Set("axis_x", i * 2);
touchscreen_param.Set("axis_y", (i * 2) + 1);
touchscreen_param.Set("button", i);
touch_params[index++] = std::move(touchscreen_param);
}
if (Settings::values.touch_from_button_maps.empty()) {
LOG_WARNING(Input, "touch_from_button_maps is unset by frontend config");
return;
}
const auto button_index =
static_cast<u64>(Settings::values.touch_from_button_map_index.GetValue());
const auto& touch_buttons = Settings::values.touch_from_button_maps[button_index].buttons;
// Map the rest of the fingers from touch from button configuration
for (const auto& config_entry : touch_buttons) {
if (index >= MaxTouchDevices) {
continue;
}
Common::ParamPackage params{config_entry};
Common::ParamPackage touch_button_params;
const int x = params.Get("x", 0);
const int y = params.Get("y", 0);
params.Erase("x");
params.Erase("y");
touch_button_params.Set("engine", "touch_from_button");
touch_button_params.Set("button", params.Serialize());
touch_button_params.Set("x", x);
touch_button_params.Set("y", y);
touch_params[index] = std::move(touch_button_params);
index++;
}
}
void EmulatedConsole::ReloadInput() {
// If you load any device here add the equivalent to the UnloadInput() function
SetTouchParams();
motion_params[1] = Common::ParamPackage{"engine:virtual_gamepad,port:8,motion:0"};
for (std::size_t index = 0; index < motion_devices.size(); ++index) {
motion_devices[index] = Common::Input::CreateInputDevice(motion_params[index]);
if (!motion_devices[index]) {
continue;
}
motion_devices[index]->SetCallback({
.on_change =
[this](const Common::Input::CallbackStatus& callback) { SetMotion(callback); },
});
}
// Restore motion state
auto& emulated_motion = console.motion_values.emulated;
auto& motion = console.motion_state;
emulated_motion.ResetRotations();
emulated_motion.ResetQuaternion();
motion.accel = emulated_motion.GetAcceleration();
motion.gyro = emulated_motion.GetGyroscope();
motion.rotation = emulated_motion.GetRotations();
motion.orientation = emulated_motion.GetOrientation();
motion.is_at_rest = !emulated_motion.IsMoving(motion_sensitivity);
// Unique index for identifying touch device source
std::size_t index = 0;
for (auto& touch_device : touch_devices) {
touch_device = Common::Input::CreateInputDevice(touch_params[index]);
if (!touch_device) {
continue;
}
touch_device->SetCallback({
.on_change =
[this, index](const Common::Input::CallbackStatus& callback) {
SetTouch(callback, index);
},
});
index++;
}
}
void EmulatedConsole::UnloadInput() {
for (auto& motion : motion_devices) {
motion.reset();
}
for (auto& touch : touch_devices) {
touch.reset();
}
}
void EmulatedConsole::EnableConfiguration() {
is_configuring = true;
SaveCurrentConfig();
}
void EmulatedConsole::DisableConfiguration() {
is_configuring = false;
}
bool EmulatedConsole::IsConfiguring() const {
return is_configuring;
}
void EmulatedConsole::SaveCurrentConfig() {
if (!is_configuring) {
return;
}
}
void EmulatedConsole::RestoreConfig() {
if (!is_configuring) {
return;
}
ReloadFromSettings();
}
Common::ParamPackage EmulatedConsole::GetMotionParam() const {
return motion_params[0];
}
void EmulatedConsole::SetMotionParam(Common::ParamPackage param) {
motion_params[0] = std::move(param);
ReloadInput();
}
void EmulatedConsole::SetMotion(const Common::Input::CallbackStatus& callback) {
std::unique_lock lock{mutex};
auto& raw_status = console.motion_values.raw_status;
auto& emulated = console.motion_values.emulated;
raw_status = TransformToMotion(callback);
emulated.SetAcceleration(Common::Vec3f{
raw_status.accel.x.value,
raw_status.accel.y.value,
raw_status.accel.z.value,
});
emulated.SetGyroscope(Common::Vec3f{
raw_status.gyro.x.value,
raw_status.gyro.y.value,
raw_status.gyro.z.value,
});
emulated.UpdateRotation(raw_status.delta_timestamp);
emulated.UpdateOrientation(raw_status.delta_timestamp);
if (is_configuring) {
lock.unlock();
TriggerOnChange(ConsoleTriggerType::Motion);
return;
}
auto& motion = console.motion_state;
motion.accel = emulated.GetAcceleration();
motion.gyro = emulated.GetGyroscope();
motion.rotation = emulated.GetRotations();
motion.orientation = emulated.GetOrientation();
motion.quaternion = emulated.GetQuaternion();
motion.gyro_bias = emulated.GetGyroBias();
motion.is_at_rest = !emulated.IsMoving(motion_sensitivity);
// Find what is this value
motion.verticalization_error = 0.0f;
lock.unlock();
TriggerOnChange(ConsoleTriggerType::Motion);
}
void EmulatedConsole::SetTouch(const Common::Input::CallbackStatus& callback, std::size_t index) {
if (index >= MaxTouchDevices) {
return;
}
std::unique_lock lock{mutex};
const auto touch_input = TransformToTouch(callback);
auto touch_index = GetIndexFromFingerId(index);
bool is_new_input = false;
if (!touch_index.has_value() && touch_input.pressed.value) {
touch_index = GetNextFreeIndex();
is_new_input = true;
}
// No free entries or invalid state. Ignore input
if (!touch_index.has_value()) {
return;
}
auto& touch_value = console.touch_values[touch_index.value()];
if (is_new_input) {
touch_value.pressed.value = true;
touch_value.id = static_cast<int>(index);
}
touch_value.x = touch_input.x;
touch_value.y = touch_input.y;
if (!touch_input.pressed.value) {
touch_value.pressed.value = false;
}
if (is_configuring) {
lock.unlock();
TriggerOnChange(ConsoleTriggerType::Touch);
return;
}
// Touch outside allowed range. Ignore input
if (touch_index.value() >= MaxActiveTouchInputs) {
return;
}
console.touch_state[touch_index.value()] = {
.position = {touch_value.x.value, touch_value.y.value},
.id = static_cast<u32>(touch_index.value()),
.pressed = touch_input.pressed.value,
};
lock.unlock();
TriggerOnChange(ConsoleTriggerType::Touch);
}
ConsoleMotionValues EmulatedConsole::GetMotionValues() const {
std::scoped_lock lock{mutex};
return console.motion_values;
}
TouchValues EmulatedConsole::GetTouchValues() const {
std::scoped_lock lock{mutex};
return console.touch_values;
}
ConsoleMotion EmulatedConsole::GetMotion() const {
std::scoped_lock lock{mutex};
return console.motion_state;
}
TouchFingerState EmulatedConsole::GetTouch() const {
std::scoped_lock lock{mutex};
return console.touch_state;
}
std::optional<std::size_t> EmulatedConsole::GetIndexFromFingerId(std::size_t finger_id) const {
for (std::size_t index = 0; index < MaxTouchDevices; ++index) {
const auto& finger = console.touch_values[index];
if (!finger.pressed.value) {
continue;
}
if (finger.id == static_cast<int>(finger_id)) {
return index;
}
}
return std::nullopt;
}
std::optional<std::size_t> EmulatedConsole::GetNextFreeIndex() const {
for (std::size_t index = 0; index < MaxTouchDevices; ++index) {
if (!console.touch_values[index].pressed.value) {
return index;
}
}
return std::nullopt;
}
void EmulatedConsole::TriggerOnChange(ConsoleTriggerType type) {
std::scoped_lock lock{callback_mutex};
for (const auto& poller_pair : callback_list) {
const ConsoleUpdateCallback& poller = poller_pair.second;
if (poller.on_change) {
poller.on_change(type);
}
}
}
int EmulatedConsole::SetCallback(ConsoleUpdateCallback update_callback) {
std::scoped_lock lock{callback_mutex};
callback_list.insert_or_assign(last_callback_key, std::move(update_callback));
return last_callback_key++;
}
void EmulatedConsole::DeleteCallback(int key) {
std::scoped_lock lock{callback_mutex};
const auto& iterator = callback_list.find(key);
if (iterator == callback_list.end()) {
LOG_ERROR(Input, "Tried to delete non-existent callback {}", key);
return;
}
callback_list.erase(iterator);
}
} // namespace Core::HID

View File

@@ -0,0 +1,200 @@
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <array>
#include <functional>
#include <memory>
#include <mutex>
#include <optional>
#include <unordered_map>
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/input.h"
#include "common/param_package.h"
#include "common/point.h"
#include "common/quaternion.h"
#include "common/vector_math.h"
#include "core/hid/hid_types.h"
#include "core/hid/motion_input.h"
namespace Core::HID {
static constexpr std::size_t MaxTouchDevices = 32;
static constexpr std::size_t MaxActiveTouchInputs = 16;
struct ConsoleMotionInfo {
Common::Input::MotionStatus raw_status{};
MotionInput emulated{};
};
using ConsoleMotionDevices = std::array<std::unique_ptr<Common::Input::InputDevice>, 2>;
using TouchDevices = std::array<std::unique_ptr<Common::Input::InputDevice>, MaxTouchDevices>;
using ConsoleMotionParams = std::array<Common::ParamPackage, 2>;
using TouchParams = std::array<Common::ParamPackage, MaxTouchDevices>;
using ConsoleMotionValues = ConsoleMotionInfo;
using TouchValues = std::array<Common::Input::TouchStatus, MaxTouchDevices>;
struct TouchFinger {
u64 last_touch{};
Common::Point<float> position{};
u32 id{};
TouchAttribute attribute{};
bool pressed{};
};
// Contains all motion related data that is used on the services
struct ConsoleMotion {
Common::Vec3f accel{};
Common::Vec3f gyro{};
Common::Vec3f rotation{};
std::array<Common::Vec3f, 3> orientation{};
Common::Quaternion<f32> quaternion{};
Common::Vec3f gyro_bias{};
f32 verticalization_error{};
bool is_at_rest{};
};
using TouchFingerState = std::array<TouchFinger, MaxActiveTouchInputs>;
struct ConsoleStatus {
// Data from input_common
ConsoleMotionValues motion_values{};
TouchValues touch_values{};
// Data for HID services
ConsoleMotion motion_state{};
TouchFingerState touch_state{};
};
enum class ConsoleTriggerType {
Motion,
Touch,
All,
};
struct ConsoleUpdateCallback {
std::function<void(ConsoleTriggerType)> on_change;
};
class EmulatedConsole {
public:
/**
* Contains all input data within the emulated switch console tablet such as touch and motion
*/
explicit EmulatedConsole();
~EmulatedConsole();
YUZU_NON_COPYABLE(EmulatedConsole);
YUZU_NON_MOVEABLE(EmulatedConsole);
/// Removes all callbacks created from input devices
void UnloadInput();
/**
* Sets the emulated console into configuring mode
* This prevents the modification of the HID state of the emulated console by input commands
*/
void EnableConfiguration();
/// Returns the emulated console into normal mode, allowing the modification of the HID state
void DisableConfiguration();
/// Returns true if the emulated console is in configuring mode
bool IsConfiguring() const;
/// Reload all input devices
void ReloadInput();
/// Overrides current mapped devices with the stored configuration and reloads all input devices
void ReloadFromSettings();
/// Saves the current mapped configuration
void SaveCurrentConfig();
/// Reverts any mapped changes made that weren't saved
void RestoreConfig();
// Returns the current mapped motion device
Common::ParamPackage GetMotionParam() const;
/**
* Updates the current mapped motion device
* @param param ParamPackage with controller data to be mapped
*/
void SetMotionParam(Common::ParamPackage param);
/// Returns the latest status of motion input from the console with parameters
ConsoleMotionValues GetMotionValues() const;
/// Returns the latest status of touch input from the console with parameters
TouchValues GetTouchValues() const;
/// Returns the latest status of motion input from the console
ConsoleMotion GetMotion() const;
/// Returns the latest status of touch input from the console
TouchFingerState GetTouch() const;
/**
* Adds a callback to the list of events
* @param update_callback A ConsoleUpdateCallback that will be triggered
* @return an unique key corresponding to the callback index in the list
*/
int SetCallback(ConsoleUpdateCallback update_callback);
/**
* Removes a callback from the list stopping any future events to this object
* @param key Key corresponding to the callback index in the list
*/
void DeleteCallback(int key);
private:
/// Creates and stores the touch params
void SetTouchParams();
/**
* Updates the motion status of the console
* @param callback A CallbackStatus containing gyro and accelerometer data
*/
void SetMotion(const Common::Input::CallbackStatus& callback);
/**
* Updates the touch status of the console
* @param callback A CallbackStatus containing the touch position
* @param index Finger ID to be updated
*/
void SetTouch(const Common::Input::CallbackStatus& callback, std::size_t index);
std::optional<std::size_t> GetIndexFromFingerId(std::size_t finger_id) const;
std::optional<std::size_t> GetNextFreeIndex() const;
/**
* Triggers a callback that something has changed on the console status
* @param type Input type of the event to trigger
*/
void TriggerOnChange(ConsoleTriggerType type);
bool is_configuring{false};
f32 motion_sensitivity{0.01f};
ConsoleMotionParams motion_params;
TouchParams touch_params;
ConsoleMotionDevices motion_devices;
TouchDevices touch_devices;
mutable std::mutex mutex;
mutable std::mutex callback_mutex;
std::unordered_map<int, ConsoleUpdateCallback> callback_list;
int last_callback_key = 0;
// Stores the current status of all console input
ConsoleStatus console;
};
} // namespace Core::HID

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,615 @@
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <array>
#include <functional>
#include <memory>
#include <mutex>
#include <unordered_map>
#include <vector>
#include "common/common_types.h"
#include "common/input.h"
#include "common/param_package.h"
#include "common/settings.h"
#include "common/vector_math.h"
#include "core/hid/hid_types.h"
#include "core/hid/irs_types.h"
#include "core/hid/motion_input.h"
namespace Core::HID {
const std::size_t max_emulated_controllers = 2;
const std::size_t output_devices_size = 4;
struct ControllerMotionInfo {
Common::Input::MotionStatus raw_status{};
MotionInput emulated{};
};
using ButtonDevices =
std::array<std::unique_ptr<Common::Input::InputDevice>, Settings::NativeButton::NumButtons>;
using StickDevices =
std::array<std::unique_ptr<Common::Input::InputDevice>, Settings::NativeAnalog::NumAnalogs>;
using ControllerMotionDevices =
std::array<std::unique_ptr<Common::Input::InputDevice>, Settings::NativeMotion::NumMotions>;
using TriggerDevices =
std::array<std::unique_ptr<Common::Input::InputDevice>, Settings::NativeTrigger::NumTriggers>;
using ColorDevices =
std::array<std::unique_ptr<Common::Input::InputDevice>, max_emulated_controllers>;
using BatteryDevices =
std::array<std::unique_ptr<Common::Input::InputDevice>, max_emulated_controllers>;
using CameraDevices =
std::array<std::unique_ptr<Common::Input::InputDevice>, max_emulated_controllers>;
using RingAnalogDevices =
std::array<std::unique_ptr<Common::Input::InputDevice>, max_emulated_controllers>;
using NfcDevices =
std::array<std::unique_ptr<Common::Input::InputDevice>, max_emulated_controllers>;
using OutputDevices = std::array<std::unique_ptr<Common::Input::OutputDevice>, output_devices_size>;
using ButtonParams = std::array<Common::ParamPackage, Settings::NativeButton::NumButtons>;
using StickParams = std::array<Common::ParamPackage, Settings::NativeAnalog::NumAnalogs>;
using ControllerMotionParams = std::array<Common::ParamPackage, Settings::NativeMotion::NumMotions>;
using TriggerParams = std::array<Common::ParamPackage, Settings::NativeTrigger::NumTriggers>;
using ColorParams = std::array<Common::ParamPackage, max_emulated_controllers>;
using BatteryParams = std::array<Common::ParamPackage, max_emulated_controllers>;
using CameraParams = std::array<Common::ParamPackage, max_emulated_controllers>;
using RingAnalogParams = std::array<Common::ParamPackage, max_emulated_controllers>;
using NfcParams = std::array<Common::ParamPackage, max_emulated_controllers>;
using OutputParams = std::array<Common::ParamPackage, output_devices_size>;
using ButtonValues = std::array<Common::Input::ButtonStatus, Settings::NativeButton::NumButtons>;
using SticksValues = std::array<Common::Input::StickStatus, Settings::NativeAnalog::NumAnalogs>;
using TriggerValues =
std::array<Common::Input::TriggerStatus, Settings::NativeTrigger::NumTriggers>;
using ControllerMotionValues = std::array<ControllerMotionInfo, Settings::NativeMotion::NumMotions>;
using ColorValues = std::array<Common::Input::BodyColorStatus, max_emulated_controllers>;
using BatteryValues = std::array<Common::Input::BatteryStatus, max_emulated_controllers>;
using CameraValues = Common::Input::CameraStatus;
using RingAnalogValue = Common::Input::AnalogStatus;
using NfcValues = Common::Input::NfcStatus;
using VibrationValues = std::array<Common::Input::VibrationStatus, max_emulated_controllers>;
struct AnalogSticks {
AnalogStickState left{};
AnalogStickState right{};
};
struct ControllerColors {
NpadControllerColor fullkey{};
NpadControllerColor left{};
NpadControllerColor right{};
};
struct BatteryLevelState {
NpadPowerInfo dual{};
NpadPowerInfo left{};
NpadPowerInfo right{};
};
struct CameraState {
Core::IrSensor::ImageTransferProcessorFormat format{};
std::vector<u8> data{};
std::size_t sample{};
};
struct RingSensorForce {
f32 force;
};
using NfcState = Common::Input::NfcStatus;
struct ControllerMotion {
Common::Vec3f accel{};
Common::Vec3f gyro{};
Common::Vec3f rotation{};
Common::Vec3f euler{};
std::array<Common::Vec3f, 3> orientation{};
bool is_at_rest{};
};
enum EmulatedDeviceIndex : u8 {
LeftIndex,
RightIndex,
DualIndex,
AllDevices,
};
using MotionState = std::array<ControllerMotion, 2>;
struct ControllerStatus {
// Data from input_common
ButtonValues button_values{};
SticksValues stick_values{};
ControllerMotionValues motion_values{};
TriggerValues trigger_values{};
ColorValues color_values{};
BatteryValues battery_values{};
VibrationValues vibration_values{};
CameraValues camera_values{};
RingAnalogValue ring_analog_value{};
NfcValues nfc_values{};
// Data for HID services
HomeButtonState home_button_state{};
CaptureButtonState capture_button_state{};
NpadButtonState npad_button_state{};
DebugPadButton debug_pad_button_state{};
AnalogSticks analog_stick_state{};
MotionState motion_state{};
NpadGcTriggerState gc_trigger_state{};
ControllerColors colors_state{};
BatteryLevelState battery_state{};
CameraState camera_state{};
RingSensorForce ring_analog_state{};
NfcState nfc_state{};
Common::Input::PollingMode left_polling_mode{};
Common::Input::PollingMode right_polling_mode{};
};
enum class ControllerTriggerType {
Button,
Stick,
Trigger,
Motion,
Color,
Battery,
Vibration,
IrSensor,
RingController,
Nfc,
Connected,
Disconnected,
Type,
All,
};
struct ControllerUpdateCallback {
std::function<void(ControllerTriggerType)> on_change;
bool is_npad_service;
};
class EmulatedController {
public:
/**
* Contains all input data (buttons, joysticks, vibration, and motion) within this controller.
* @param npad_id_type npad id type for this specific controller
*/
explicit EmulatedController(NpadIdType npad_id_type_);
~EmulatedController();
YUZU_NON_COPYABLE(EmulatedController);
YUZU_NON_MOVEABLE(EmulatedController);
/// Converts the controller type from settings to npad type
static NpadStyleIndex MapSettingsTypeToNPad(Settings::ControllerType type);
/// Converts npad type to the equivalent of controller type from settings
static Settings::ControllerType MapNPadToSettingsType(NpadStyleIndex type);
/// Gets the NpadIdType for this controller
NpadIdType GetNpadIdType() const;
/// Sets the NpadStyleIndex for this controller
void SetNpadStyleIndex(NpadStyleIndex npad_type_);
/**
* Gets the NpadStyleIndex for this controller
* @param get_temporary_value If true tmp_npad_type will be returned
* @return NpadStyleIndex set on the controller
*/
NpadStyleIndex GetNpadStyleIndex(bool get_temporary_value = false) const;
/**
* Sets the supported controller types. Disconnects the controller if current type is not
* supported
* @param supported_styles bitflag with supported types
*/
void SetSupportedNpadStyleTag(NpadStyleTag supported_styles);
/**
* Sets the connected status to true
* @param use_temporary_value If true tmp_npad_type will be used
*/
void Connect(bool use_temporary_value = false);
/// Sets the connected status to false
void Disconnect();
/**
* Is the emulated connected
* @param get_temporary_value If true tmp_is_connected will be returned
* @return true if the controller has the connected status
*/
bool IsConnected(bool get_temporary_value = false) const;
/// Removes all callbacks created from input devices
void UnloadInput();
/**
* Sets the emulated controller into configuring mode
* This prevents the modification of the HID state of the emulated controller by input commands
*/
void EnableConfiguration();
/// Returns the emulated controller into normal mode, allowing the modification of the HID state
void DisableConfiguration();
/// Enables Home and Screenshot buttons
void EnableSystemButtons();
/// Disables Home and Screenshot buttons
void DisableSystemButtons();
/// Sets Home and Screenshot buttons to false
void ResetSystemButtons();
/// Returns true if the emulated controller is in configuring mode
bool IsConfiguring() const;
/// Reload all input devices
void ReloadInput();
/// Overrides current mapped devices with the stored configuration and reloads all input devices
void ReloadFromSettings();
/// Saves the current mapped configuration
void SaveCurrentConfig();
/// Reverts any mapped changes made that weren't saved
void RestoreConfig();
/// Returns a vector of mapped devices from the mapped button and stick parameters
std::vector<Common::ParamPackage> GetMappedDevices() const;
// Returns the current mapped button device
Common::ParamPackage GetButtonParam(std::size_t index) const;
// Returns the current mapped stick device
Common::ParamPackage GetStickParam(std::size_t index) const;
// Returns the current mapped motion device
Common::ParamPackage GetMotionParam(std::size_t index) const;
/**
* Updates the current mapped button device
* @param param ParamPackage with controller data to be mapped
*/
void SetButtonParam(std::size_t index, Common::ParamPackage param);
/**
* Updates the current mapped stick device
* @param param ParamPackage with controller data to be mapped
*/
void SetStickParam(std::size_t index, Common::ParamPackage param);
/**
* Updates the current mapped motion device
* @param param ParamPackage with controller data to be mapped
*/
void SetMotionParam(std::size_t index, Common::ParamPackage param);
/// Auto calibrates the current motion devices
void StartMotionCalibration();
/// Returns the latest button status from the controller with parameters
ButtonValues GetButtonsValues() const;
/// Returns the latest analog stick status from the controller with parameters
SticksValues GetSticksValues() const;
/// Returns the latest trigger status from the controller with parameters
TriggerValues GetTriggersValues() const;
/// Returns the latest motion status from the controller with parameters
ControllerMotionValues GetMotionValues() const;
/// Returns the latest color status from the controller with parameters
ColorValues GetColorsValues() const;
/// Returns the latest battery status from the controller with parameters
BatteryValues GetBatteryValues() const;
/// Returns the latest camera status from the controller with parameters
CameraValues GetCameraValues() const;
/// Returns the latest status of analog input from the ring sensor with parameters
RingAnalogValue GetRingSensorValues() const;
/// Returns the latest status of button input for the hid::HomeButton service
HomeButtonState GetHomeButtons() const;
/// Returns the latest status of button input for the hid::CaptureButton service
CaptureButtonState GetCaptureButtons() const;
/// Returns the latest status of button input for the hid::Npad service
NpadButtonState GetNpadButtons() const;
/// Returns the latest status of button input for the debug pad service
DebugPadButton GetDebugPadButtons() const;
/// Returns the latest status of stick input from the mouse
AnalogSticks GetSticks() const;
/// Returns the latest status of trigger input from the mouse
NpadGcTriggerState GetTriggers() const;
/// Returns the latest status of motion input from the mouse
MotionState GetMotions() const;
/// Returns the latest color value from the controller
ControllerColors GetColors() const;
/// Returns the latest battery status from the controller
BatteryLevelState GetBattery() const;
/// Returns the latest camera status from the controller
const CameraState& GetCamera() const;
/// Returns the latest ringcon force sensor value
RingSensorForce GetRingSensorForce() const;
/// Returns the latest ntag status from the controller
const NfcState& GetNfc() const;
/**
* Sends a specific vibration to the output device
* @return true if vibration had no errors
*/
bool SetVibration(std::size_t device_index, VibrationValue vibration);
/**
* Sends a small vibration to the output device
* @return true if SetVibration was successful
*/
bool IsVibrationEnabled(std::size_t device_index);
/**
* Sets the desired data to be polled from a controller
* @param device_index index of the controller to set the polling mode
* @param polling_mode type of input desired buttons, gyro, nfc, ir, etc.
* @return driver result from this command
*/
Common::Input::DriverResult SetPollingMode(EmulatedDeviceIndex device_index,
Common::Input::PollingMode polling_mode);
/**
* Get the current polling mode from a controller
* @param device_index index of the controller to set the polling mode
* @return current polling mode
*/
Common::Input::PollingMode GetPollingMode(EmulatedDeviceIndex device_index) const;
/**
* Sets the desired camera format to be polled from a controller
* @param camera_format size of each frame
* @return true if SetCameraFormat was successful
*/
bool SetCameraFormat(Core::IrSensor::ImageTransferProcessorFormat camera_format);
// Returns the current mapped ring device
Common::ParamPackage GetRingParam() const;
/**
* Updates the current mapped ring device
* @param param ParamPackage with ring sensor data to be mapped
*/
void SetRingParam(Common::ParamPackage param);
/// Returns true if the device has nfc support
bool HasNfc() const;
/// Sets the joycon in nfc mode and increments the handle count
bool AddNfcHandle();
/// Decrements the handle count if zero sets the joycon in active mode
bool RemoveNfcHandle();
/// Start searching for nfc tags
bool StartNfcPolling();
/// Stop searching for nfc tags
bool StopNfcPolling();
/// Returns true if the nfc tag was readable
bool ReadAmiiboData(std::vector<u8>& data);
/// Returns true if the nfc tag was written
bool WriteNfc(const std::vector<u8>& data);
/// Returns true if the nfc tag was readable
bool ReadMifareData(const Common::Input::MifareRequest& request,
Common::Input::MifareRequest& out_data);
/// Returns true if the nfc tag was written
bool WriteMifareData(const Common::Input::MifareRequest& request);
/// Returns the led pattern corresponding to this emulated controller
LedPattern GetLedPattern() const;
/// Asks the output device to change the player led pattern
void SetLedPattern();
/// Changes sensitivity of the motion sensor
void SetGyroscopeZeroDriftMode(GyroscopeZeroDriftMode mode);
/**
* Adds a callback to the list of events
* @param update_callback A ConsoleUpdateCallback that will be triggered
* @return an unique key corresponding to the callback index in the list
*/
int SetCallback(ControllerUpdateCallback update_callback);
/**
* Removes a callback from the list stopping any future events to this object
* @param key Key corresponding to the callback index in the list
*/
void DeleteCallback(int key);
/// Swaps the state of the turbo buttons and updates motion input
void StatusUpdate();
private:
/// creates input devices from params
void LoadDevices();
/// Set the params for TAS devices
void LoadTASParams();
/// Set the params for virtual pad devices
void LoadVirtualGamepadParams();
/**
* @param use_temporary_value If true tmp_npad_type will be used
* @return true if the controller style is fullkey
*/
bool IsControllerFullkey(bool use_temporary_value = false) const;
/**
* Checks the current controller type against the supported_style_tag
* @param use_temporary_value If true tmp_npad_type will be used
* @return true if the controller is supported
*/
bool IsControllerSupported(bool use_temporary_value = false) const;
/**
* Updates the button status of the controller
* @param callback A CallbackStatus containing the button status
* @param index Button ID of the to be updated
*/
void SetButton(const Common::Input::CallbackStatus& callback, std::size_t index,
Common::UUID uuid);
/**
* Updates the analog stick status of the controller
* @param callback A CallbackStatus containing the analog stick status
* @param index stick ID of the to be updated
*/
void SetStick(const Common::Input::CallbackStatus& callback, std::size_t index,
Common::UUID uuid);
/**
* Updates the trigger status of the controller
* @param callback A CallbackStatus containing the trigger status
* @param index trigger ID of the to be updated
*/
void SetTrigger(const Common::Input::CallbackStatus& callback, std::size_t index,
Common::UUID uuid);
/**
* Updates the motion status of the controller
* @param callback A CallbackStatus containing gyro and accelerometer data
* @param index motion ID of the to be updated
*/
void SetMotion(const Common::Input::CallbackStatus& callback, std::size_t index);
/**
* Updates the color status of the controller
* @param callback A CallbackStatus containing the color status
* @param index color ID of the to be updated
*/
void SetColors(const Common::Input::CallbackStatus& callback, std::size_t index);
/**
* Updates the battery status of the controller
* @param callback A CallbackStatus containing the battery status
* @param index battery ID of the to be updated
*/
void SetBattery(const Common::Input::CallbackStatus& callback, std::size_t index);
/**
* Updates the camera status of the controller
* @param callback A CallbackStatus containing the camera status
*/
void SetCamera(const Common::Input::CallbackStatus& callback);
/**
* Updates the ring analog sensor status of the ring controller
* @param callback A CallbackStatus containing the force status
*/
void SetRingAnalog(const Common::Input::CallbackStatus& callback);
/**
* Updates the nfc status of the controller
* @param callback A CallbackStatus containing the nfc status
*/
void SetNfc(const Common::Input::CallbackStatus& callback);
/**
* Converts a color format from bgra to rgba
* @param color in bgra format
* @return NpadColor in rgba format
*/
NpadColor GetNpadColor(u32 color);
/**
* Triggers a callback that something has changed on the controller status
* @param type Input type of the event to trigger
* @param is_service_update indicates if this event should only be sent to HID services
*/
void TriggerOnChange(ControllerTriggerType type, bool is_service_update);
NpadButton GetTurboButtonMask() const;
const NpadIdType npad_id_type;
NpadStyleIndex npad_type{NpadStyleIndex::None};
NpadStyleIndex original_npad_type{NpadStyleIndex::None};
NpadStyleTag supported_style_tag{NpadStyleSet::All};
bool is_connected{false};
bool is_configuring{false};
bool system_buttons_enabled{true};
f32 motion_sensitivity{Core::HID::MotionInput::IsAtRestStandard};
u32 turbo_button_state{0};
std::size_t nfc_handles{0};
// Temporary values to avoid doing changes while the controller is in configuring mode
NpadStyleIndex tmp_npad_type{NpadStyleIndex::None};
bool tmp_is_connected{false};
ButtonParams button_params;
StickParams stick_params;
ControllerMotionParams motion_params;
TriggerParams trigger_params;
BatteryParams battery_params;
ColorParams color_params;
CameraParams camera_params;
RingAnalogParams ring_params;
NfcParams nfc_params;
OutputParams output_params;
ButtonDevices button_devices;
StickDevices stick_devices;
ControllerMotionDevices motion_devices;
TriggerDevices trigger_devices;
BatteryDevices battery_devices;
ColorDevices color_devices;
CameraDevices camera_devices;
RingAnalogDevices ring_analog_devices;
NfcDevices nfc_devices;
OutputDevices output_devices;
// TAS related variables
ButtonParams tas_button_params;
StickParams tas_stick_params;
ButtonDevices tas_button_devices;
StickDevices tas_stick_devices;
// Virtual gamepad related variables
ButtonParams virtual_button_params;
StickParams virtual_stick_params;
ControllerMotionParams virtual_motion_params;
ButtonDevices virtual_button_devices;
StickDevices virtual_stick_devices;
ControllerMotionDevices virtual_motion_devices;
mutable std::mutex mutex;
mutable std::mutex callback_mutex;
mutable std::mutex npad_mutex;
mutable std::mutex connect_mutex;
std::unordered_map<int, ControllerUpdateCallback> callback_list;
int last_callback_key = 0;
// Stores the current status of all controller input
ControllerStatus controller;
};
} // namespace Core::HID

View File

@@ -0,0 +1,483 @@
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm>
#include <fmt/format.h>
#include "core/hid/emulated_devices.h"
#include "core/hid/input_converter.h"
namespace Core::HID {
EmulatedDevices::EmulatedDevices() = default;
EmulatedDevices::~EmulatedDevices() = default;
void EmulatedDevices::ReloadFromSettings() {
ReloadInput();
}
void EmulatedDevices::ReloadInput() {
// If you load any device here add the equivalent to the UnloadInput() function
// Native Mouse is mapped on port 1, pad 0
const Common::ParamPackage mouse_params{"engine:mouse,port:1,pad:0"};
// Keyboard keys is mapped on port 1, pad 0 for normal keys, pad 1 for moddifier keys
const Common::ParamPackage keyboard_params{"engine:keyboard,port:1"};
std::size_t key_index = 0;
for (auto& mouse_device : mouse_button_devices) {
Common::ParamPackage mouse_button_params = mouse_params;
mouse_button_params.Set("button", static_cast<int>(key_index));
mouse_device = Common::Input::CreateInputDevice(mouse_button_params);
key_index++;
}
Common::ParamPackage mouse_position_params = mouse_params;
mouse_position_params.Set("axis_x", 0);
mouse_position_params.Set("axis_y", 1);
mouse_position_params.Set("deadzone", 0.0f);
mouse_position_params.Set("range", 1.0f);
mouse_position_params.Set("threshold", 0.0f);
mouse_stick_device = Common::Input::CreateInputDevice(mouse_position_params);
// First two axis are reserved for mouse position
key_index = 2;
for (auto& mouse_device : mouse_wheel_devices) {
Common::ParamPackage mouse_wheel_params = mouse_params;
mouse_wheel_params.Set("axis", static_cast<int>(key_index));
mouse_device = Common::Input::CreateInputDevice(mouse_wheel_params);
key_index++;
}
key_index = 0;
for (auto& keyboard_device : keyboard_devices) {
Common::ParamPackage keyboard_key_params = keyboard_params;
keyboard_key_params.Set("button", static_cast<int>(key_index));
keyboard_key_params.Set("pad", 0);
keyboard_device = Common::Input::CreateInputDevice(keyboard_key_params);
key_index++;
}
key_index = 0;
for (auto& keyboard_device : keyboard_modifier_devices) {
Common::ParamPackage keyboard_moddifier_params = keyboard_params;
keyboard_moddifier_params.Set("button", static_cast<int>(key_index));
keyboard_moddifier_params.Set("pad", 1);
keyboard_device = Common::Input::CreateInputDevice(keyboard_moddifier_params);
key_index++;
}
for (std::size_t index = 0; index < mouse_button_devices.size(); ++index) {
if (!mouse_button_devices[index]) {
continue;
}
mouse_button_devices[index]->SetCallback({
.on_change =
[this, index](const Common::Input::CallbackStatus& callback) {
SetMouseButton(callback, index);
},
});
}
for (std::size_t index = 0; index < mouse_wheel_devices.size(); ++index) {
if (!mouse_wheel_devices[index]) {
continue;
}
mouse_wheel_devices[index]->SetCallback({
.on_change =
[this, index](const Common::Input::CallbackStatus& callback) {
SetMouseWheel(callback, index);
},
});
}
if (mouse_stick_device) {
mouse_stick_device->SetCallback({
.on_change =
[this](const Common::Input::CallbackStatus& callback) {
SetMousePosition(callback);
},
});
}
for (std::size_t index = 0; index < keyboard_devices.size(); ++index) {
if (!keyboard_devices[index]) {
continue;
}
keyboard_devices[index]->SetCallback({
.on_change =
[this, index](const Common::Input::CallbackStatus& callback) {
SetKeyboardButton(callback, index);
},
});
}
for (std::size_t index = 0; index < keyboard_modifier_devices.size(); ++index) {
if (!keyboard_modifier_devices[index]) {
continue;
}
keyboard_modifier_devices[index]->SetCallback({
.on_change =
[this, index](const Common::Input::CallbackStatus& callback) {
SetKeyboardModifier(callback, index);
},
});
}
}
void EmulatedDevices::UnloadInput() {
for (auto& button : mouse_button_devices) {
button.reset();
}
for (auto& analog : mouse_wheel_devices) {
analog.reset();
}
mouse_stick_device.reset();
for (auto& button : keyboard_devices) {
button.reset();
}
for (auto& button : keyboard_modifier_devices) {
button.reset();
}
}
void EmulatedDevices::EnableConfiguration() {
is_configuring = true;
SaveCurrentConfig();
}
void EmulatedDevices::DisableConfiguration() {
is_configuring = false;
}
bool EmulatedDevices::IsConfiguring() const {
return is_configuring;
}
void EmulatedDevices::SaveCurrentConfig() {
if (!is_configuring) {
return;
}
}
void EmulatedDevices::RestoreConfig() {
if (!is_configuring) {
return;
}
ReloadFromSettings();
}
void EmulatedDevices::SetKeyboardButton(const Common::Input::CallbackStatus& callback,
std::size_t index) {
if (index >= device_status.keyboard_values.size()) {
return;
}
std::unique_lock lock{mutex};
bool value_changed = false;
const auto new_status = TransformToButton(callback);
auto& current_status = device_status.keyboard_values[index];
current_status.toggle = new_status.toggle;
// Update button status with current status
if (!current_status.toggle) {
current_status.locked = false;
if (current_status.value != new_status.value) {
current_status.value = new_status.value;
value_changed = true;
}
} else {
// Toggle button and lock status
if (new_status.value && !current_status.locked) {
current_status.locked = true;
current_status.value = !current_status.value;
value_changed = true;
}
// Unlock button, ready for next press
if (!new_status.value && current_status.locked) {
current_status.locked = false;
}
}
if (!value_changed) {
return;
}
if (is_configuring) {
lock.unlock();
TriggerOnChange(DeviceTriggerType::Keyboard);
return;
}
// Index should be converted from NativeKeyboard to KeyboardKeyIndex
UpdateKey(index, current_status.value);
lock.unlock();
TriggerOnChange(DeviceTriggerType::Keyboard);
}
void EmulatedDevices::UpdateKey(std::size_t key_index, bool status) {
constexpr std::size_t KEYS_PER_BYTE = 8;
auto& entry = device_status.keyboard_state.key[key_index / KEYS_PER_BYTE];
const u8 mask = static_cast<u8>(1 << (key_index % KEYS_PER_BYTE));
if (status) {
entry = entry | mask;
} else {
entry = static_cast<u8>(entry & ~mask);
}
}
void EmulatedDevices::SetKeyboardModifier(const Common::Input::CallbackStatus& callback,
std::size_t index) {
if (index >= device_status.keyboard_moddifier_values.size()) {
return;
}
std::unique_lock lock{mutex};
bool value_changed = false;
const auto new_status = TransformToButton(callback);
auto& current_status = device_status.keyboard_moddifier_values[index];
current_status.toggle = new_status.toggle;
// Update button status with current
if (!current_status.toggle) {
current_status.locked = false;
if (current_status.value != new_status.value) {
current_status.value = new_status.value;
value_changed = true;
}
} else {
// Toggle button and lock status
if (new_status.value && !current_status.locked) {
current_status.locked = true;
current_status.value = !current_status.value;
value_changed = true;
}
// Unlock button ready for next press
if (!new_status.value && current_status.locked) {
current_status.locked = false;
}
}
if (!value_changed) {
return;
}
if (is_configuring) {
lock.unlock();
TriggerOnChange(DeviceTriggerType::KeyboardModdifier);
return;
}
switch (index) {
case Settings::NativeKeyboard::LeftControl:
case Settings::NativeKeyboard::RightControl:
device_status.keyboard_moddifier_state.control.Assign(current_status.value);
break;
case Settings::NativeKeyboard::LeftShift:
case Settings::NativeKeyboard::RightShift:
device_status.keyboard_moddifier_state.shift.Assign(current_status.value);
break;
case Settings::NativeKeyboard::LeftAlt:
device_status.keyboard_moddifier_state.left_alt.Assign(current_status.value);
break;
case Settings::NativeKeyboard::RightAlt:
device_status.keyboard_moddifier_state.right_alt.Assign(current_status.value);
break;
case Settings::NativeKeyboard::CapsLock:
device_status.keyboard_moddifier_state.caps_lock.Assign(current_status.value);
break;
case Settings::NativeKeyboard::ScrollLock:
device_status.keyboard_moddifier_state.scroll_lock.Assign(current_status.value);
break;
case Settings::NativeKeyboard::NumLock:
device_status.keyboard_moddifier_state.num_lock.Assign(current_status.value);
break;
}
lock.unlock();
TriggerOnChange(DeviceTriggerType::KeyboardModdifier);
}
void EmulatedDevices::SetMouseButton(const Common::Input::CallbackStatus& callback,
std::size_t index) {
if (index >= device_status.mouse_button_values.size()) {
return;
}
std::unique_lock lock{mutex};
bool value_changed = false;
const auto new_status = TransformToButton(callback);
auto& current_status = device_status.mouse_button_values[index];
current_status.toggle = new_status.toggle;
// Update button status with current
if (!current_status.toggle) {
current_status.locked = false;
if (current_status.value != new_status.value) {
current_status.value = new_status.value;
value_changed = true;
}
} else {
// Toggle button and lock status
if (new_status.value && !current_status.locked) {
current_status.locked = true;
current_status.value = !current_status.value;
value_changed = true;
}
// Unlock button ready for next press
if (!new_status.value && current_status.locked) {
current_status.locked = false;
}
}
if (!value_changed) {
return;
}
if (is_configuring) {
lock.unlock();
TriggerOnChange(DeviceTriggerType::Mouse);
return;
}
switch (index) {
case Settings::NativeMouseButton::Left:
device_status.mouse_button_state.left.Assign(current_status.value);
break;
case Settings::NativeMouseButton::Right:
device_status.mouse_button_state.right.Assign(current_status.value);
break;
case Settings::NativeMouseButton::Middle:
device_status.mouse_button_state.middle.Assign(current_status.value);
break;
case Settings::NativeMouseButton::Forward:
device_status.mouse_button_state.forward.Assign(current_status.value);
break;
case Settings::NativeMouseButton::Back:
device_status.mouse_button_state.back.Assign(current_status.value);
break;
}
lock.unlock();
TriggerOnChange(DeviceTriggerType::Mouse);
}
void EmulatedDevices::SetMouseWheel(const Common::Input::CallbackStatus& callback,
std::size_t index) {
if (index >= device_status.mouse_wheel_values.size()) {
return;
}
std::unique_lock lock{mutex};
const auto analog_value = TransformToAnalog(callback);
device_status.mouse_wheel_values[index] = analog_value;
if (is_configuring) {
device_status.mouse_wheel_state = {};
lock.unlock();
TriggerOnChange(DeviceTriggerType::Mouse);
return;
}
switch (index) {
case Settings::NativeMouseWheel::X:
device_status.mouse_wheel_state.x = static_cast<s32>(analog_value.value);
break;
case Settings::NativeMouseWheel::Y:
device_status.mouse_wheel_state.y = static_cast<s32>(analog_value.value);
break;
}
lock.unlock();
TriggerOnChange(DeviceTriggerType::Mouse);
}
void EmulatedDevices::SetMousePosition(const Common::Input::CallbackStatus& callback) {
std::unique_lock lock{mutex};
const auto touch_value = TransformToTouch(callback);
device_status.mouse_stick_value = touch_value;
if (is_configuring) {
device_status.mouse_position_state = {};
lock.unlock();
TriggerOnChange(DeviceTriggerType::Mouse);
return;
}
device_status.mouse_position_state.x = touch_value.x.value;
device_status.mouse_position_state.y = touch_value.y.value;
lock.unlock();
TriggerOnChange(DeviceTriggerType::Mouse);
}
KeyboardValues EmulatedDevices::GetKeyboardValues() const {
std::scoped_lock lock{mutex};
return device_status.keyboard_values;
}
KeyboardModifierValues EmulatedDevices::GetKeyboardModdifierValues() const {
std::scoped_lock lock{mutex};
return device_status.keyboard_moddifier_values;
}
MouseButtonValues EmulatedDevices::GetMouseButtonsValues() const {
std::scoped_lock lock{mutex};
return device_status.mouse_button_values;
}
KeyboardKey EmulatedDevices::GetKeyboard() const {
std::scoped_lock lock{mutex};
return device_status.keyboard_state;
}
KeyboardModifier EmulatedDevices::GetKeyboardModifier() const {
std::scoped_lock lock{mutex};
return device_status.keyboard_moddifier_state;
}
MouseButton EmulatedDevices::GetMouseButtons() const {
std::scoped_lock lock{mutex};
return device_status.mouse_button_state;
}
MousePosition EmulatedDevices::GetMousePosition() const {
std::scoped_lock lock{mutex};
return device_status.mouse_position_state;
}
AnalogStickState EmulatedDevices::GetMouseWheel() const {
std::scoped_lock lock{mutex};
return device_status.mouse_wheel_state;
}
void EmulatedDevices::TriggerOnChange(DeviceTriggerType type) {
std::scoped_lock lock{callback_mutex};
for (const auto& poller_pair : callback_list) {
const InterfaceUpdateCallback& poller = poller_pair.second;
if (poller.on_change) {
poller.on_change(type);
}
}
}
int EmulatedDevices::SetCallback(InterfaceUpdateCallback update_callback) {
std::scoped_lock lock{callback_mutex};
callback_list.insert_or_assign(last_callback_key, std::move(update_callback));
return last_callback_key++;
}
void EmulatedDevices::DeleteCallback(int key) {
std::scoped_lock lock{callback_mutex};
const auto& iterator = callback_list.find(key);
if (iterator == callback_list.end()) {
LOG_ERROR(Input, "Tried to delete non-existent callback {}", key);
return;
}
callback_list.erase(iterator);
}
} // namespace Core::HID

View File

@@ -0,0 +1,212 @@
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <array>
#include <functional>
#include <memory>
#include <mutex>
#include <unordered_map>
#include <vector>
#include "common/common_types.h"
#include "common/input.h"
#include "common/param_package.h"
#include "common/settings.h"
#include "core/hid/hid_types.h"
namespace Core::HID {
using KeyboardDevices = std::array<std::unique_ptr<Common::Input::InputDevice>,
Settings::NativeKeyboard::NumKeyboardKeys>;
using KeyboardModifierDevices = std::array<std::unique_ptr<Common::Input::InputDevice>,
Settings::NativeKeyboard::NumKeyboardMods>;
using MouseButtonDevices = std::array<std::unique_ptr<Common::Input::InputDevice>,
Settings::NativeMouseButton::NumMouseButtons>;
using MouseWheelDevices = std::array<std::unique_ptr<Common::Input::InputDevice>,
Settings::NativeMouseWheel::NumMouseWheels>;
using MouseStickDevice = std::unique_ptr<Common::Input::InputDevice>;
using MouseButtonParams =
std::array<Common::ParamPackage, Settings::NativeMouseButton::NumMouseButtons>;
using KeyboardValues =
std::array<Common::Input::ButtonStatus, Settings::NativeKeyboard::NumKeyboardKeys>;
using KeyboardModifierValues =
std::array<Common::Input::ButtonStatus, Settings::NativeKeyboard::NumKeyboardMods>;
using MouseButtonValues =
std::array<Common::Input::ButtonStatus, Settings::NativeMouseButton::NumMouseButtons>;
using MouseWheelValues =
std::array<Common::Input::AnalogStatus, Settings::NativeMouseWheel::NumMouseWheels>;
using MouseStickValue = Common::Input::TouchStatus;
struct MousePosition {
f32 x;
f32 y;
};
struct DeviceStatus {
// Data from input_common
KeyboardValues keyboard_values{};
KeyboardModifierValues keyboard_moddifier_values{};
MouseButtonValues mouse_button_values{};
MouseWheelValues mouse_wheel_values{};
MouseStickValue mouse_stick_value{};
// Data for HID services
KeyboardKey keyboard_state{};
KeyboardModifier keyboard_moddifier_state{};
MouseButton mouse_button_state{};
MousePosition mouse_position_state{};
AnalogStickState mouse_wheel_state{};
};
enum class DeviceTriggerType {
Keyboard,
KeyboardModdifier,
Mouse,
RingController,
};
struct InterfaceUpdateCallback {
std::function<void(DeviceTriggerType)> on_change;
};
class EmulatedDevices {
public:
/**
* Contains all input data related to external devices that aren't necessarily a controller
* This includes devices such as the keyboard or mouse
*/
explicit EmulatedDevices();
~EmulatedDevices();
YUZU_NON_COPYABLE(EmulatedDevices);
YUZU_NON_MOVEABLE(EmulatedDevices);
/// Removes all callbacks created from input devices
void UnloadInput();
/**
* Sets the emulated devices into configuring mode
* This prevents the modification of the HID state of the emulated devices by input commands
*/
void EnableConfiguration();
/// Returns the emulated devices into normal mode, allowing the modification of the HID state
void DisableConfiguration();
/// Returns true if the emulated device is in configuring mode
bool IsConfiguring() const;
/// Reload all input devices
void ReloadInput();
/// Overrides current mapped devices with the stored configuration and reloads all input devices
void ReloadFromSettings();
/// Saves the current mapped configuration
void SaveCurrentConfig();
/// Reverts any mapped changes made that weren't saved
void RestoreConfig();
/// Returns the latest status of button input from the keyboard with parameters
KeyboardValues GetKeyboardValues() const;
/// Returns the latest status of button input from the keyboard modifiers with parameters
KeyboardModifierValues GetKeyboardModdifierValues() const;
/// Returns the latest status of button input from the mouse with parameters
MouseButtonValues GetMouseButtonsValues() const;
/// Returns the latest status of button input from the keyboard
KeyboardKey GetKeyboard() const;
/// Returns the latest status of button input from the keyboard modifiers
KeyboardModifier GetKeyboardModifier() const;
/// Returns the latest status of button input from the mouse
MouseButton GetMouseButtons() const;
/// Returns the latest mouse coordinates
MousePosition GetMousePosition() const;
/// Returns the latest mouse wheel change
AnalogStickState GetMouseWheel() const;
/**
* Adds a callback to the list of events
* @param update_callback InterfaceUpdateCallback that will be triggered
* @return an unique key corresponding to the callback index in the list
*/
int SetCallback(InterfaceUpdateCallback update_callback);
/**
* Removes a callback from the list stopping any future events to this object
* @param key Key corresponding to the callback index in the list
*/
void DeleteCallback(int key);
private:
/// Helps assigning a value to keyboard_state
void UpdateKey(std::size_t key_index, bool status);
/**
* Updates the touch status of the keyboard device
* @param callback A CallbackStatus containing the key status
* @param index key ID to be updated
*/
void SetKeyboardButton(const Common::Input::CallbackStatus& callback, std::size_t index);
/**
* Updates the keyboard status of the keyboard device
* @param callback A CallbackStatus containing the modifier key status
* @param index modifier key ID to be updated
*/
void SetKeyboardModifier(const Common::Input::CallbackStatus& callback, std::size_t index);
/**
* Updates the mouse button status of the mouse device
* @param callback A CallbackStatus containing the button status
* @param index Button ID to be updated
*/
void SetMouseButton(const Common::Input::CallbackStatus& callback, std::size_t index);
/**
* Updates the mouse wheel status of the mouse device
* @param callback A CallbackStatus containing the wheel status
* @param index wheel ID to be updated
*/
void SetMouseWheel(const Common::Input::CallbackStatus& callback, std::size_t index);
/**
* Updates the mouse position status of the mouse device
* @param callback A CallbackStatus containing the position status
*/
void SetMousePosition(const Common::Input::CallbackStatus& callback);
/**
* Triggers a callback that something has changed on the device status
* @param type Input type of the event to trigger
*/
void TriggerOnChange(DeviceTriggerType type);
bool is_configuring{false};
KeyboardDevices keyboard_devices;
KeyboardModifierDevices keyboard_modifier_devices;
MouseButtonDevices mouse_button_devices;
MouseWheelDevices mouse_wheel_devices;
MouseStickDevice mouse_stick_device;
mutable std::mutex mutex;
mutable std::mutex callback_mutex;
std::unordered_map<int, InterfaceUpdateCallback> callback_list;
int last_callback_key = 0;
// Stores the current status of all external device input
DeviceStatus device_status;
};
} // namespace Core::HID

View File

@@ -2,12 +2,220 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/assert.h"
#include "core/hid/emulated_console.h"
#include "core/hid/emulated_controller.h"
#include "core/hid/emulated_devices.h"
#include "core/hid/hid_core.h"
namespace Core::HID {
HIDCore::HIDCore() {}
HIDCore::HIDCore()
: player_1{std::make_unique<EmulatedController>(NpadIdType::Player1)},
player_2{std::make_unique<EmulatedController>(NpadIdType::Player2)},
player_3{std::make_unique<EmulatedController>(NpadIdType::Player3)},
player_4{std::make_unique<EmulatedController>(NpadIdType::Player4)},
player_5{std::make_unique<EmulatedController>(NpadIdType::Player5)},
player_6{std::make_unique<EmulatedController>(NpadIdType::Player6)},
player_7{std::make_unique<EmulatedController>(NpadIdType::Player7)},
player_8{std::make_unique<EmulatedController>(NpadIdType::Player8)},
other{std::make_unique<EmulatedController>(NpadIdType::Other)},
handheld{std::make_unique<EmulatedController>(NpadIdType::Handheld)},
console{std::make_unique<EmulatedConsole>()}, devices{std::make_unique<EmulatedDevices>()} {}
HIDCore::~HIDCore() = default;
EmulatedController* HIDCore::GetEmulatedController(NpadIdType npad_id_type) {
switch (npad_id_type) {
case NpadIdType::Player1:
return player_1.get();
case NpadIdType::Player2:
return player_2.get();
case NpadIdType::Player3:
return player_3.get();
case NpadIdType::Player4:
return player_4.get();
case NpadIdType::Player5:
return player_5.get();
case NpadIdType::Player6:
return player_6.get();
case NpadIdType::Player7:
return player_7.get();
case NpadIdType::Player8:
return player_8.get();
case NpadIdType::Other:
return other.get();
case NpadIdType::Handheld:
return handheld.get();
case NpadIdType::Invalid:
default:
ASSERT_MSG(false, "Invalid NpadIdType={}", npad_id_type);
return nullptr;
}
}
const EmulatedController* HIDCore::GetEmulatedController(NpadIdType npad_id_type) const {
switch (npad_id_type) {
case NpadIdType::Player1:
return player_1.get();
case NpadIdType::Player2:
return player_2.get();
case NpadIdType::Player3:
return player_3.get();
case NpadIdType::Player4:
return player_4.get();
case NpadIdType::Player5:
return player_5.get();
case NpadIdType::Player6:
return player_6.get();
case NpadIdType::Player7:
return player_7.get();
case NpadIdType::Player8:
return player_8.get();
case NpadIdType::Other:
return other.get();
case NpadIdType::Handheld:
return handheld.get();
case NpadIdType::Invalid:
default:
ASSERT_MSG(false, "Invalid NpadIdType={}", npad_id_type);
return nullptr;
}
}
EmulatedConsole* HIDCore::GetEmulatedConsole() {
return console.get();
}
const EmulatedConsole* HIDCore::GetEmulatedConsole() const {
return console.get();
}
EmulatedDevices* HIDCore::GetEmulatedDevices() {
return devices.get();
}
const EmulatedDevices* HIDCore::GetEmulatedDevices() const {
return devices.get();
}
EmulatedController* HIDCore::GetEmulatedControllerByIndex(std::size_t index) {
return GetEmulatedController(IndexToNpadIdType(index));
}
const EmulatedController* HIDCore::GetEmulatedControllerByIndex(std::size_t index) const {
return GetEmulatedController(IndexToNpadIdType(index));
}
void HIDCore::SetSupportedStyleTag(NpadStyleTag style_tag) {
supported_style_tag.raw = style_tag.raw;
player_1->SetSupportedNpadStyleTag(supported_style_tag);
player_2->SetSupportedNpadStyleTag(supported_style_tag);
player_3->SetSupportedNpadStyleTag(supported_style_tag);
player_4->SetSupportedNpadStyleTag(supported_style_tag);
player_5->SetSupportedNpadStyleTag(supported_style_tag);
player_6->SetSupportedNpadStyleTag(supported_style_tag);
player_7->SetSupportedNpadStyleTag(supported_style_tag);
player_8->SetSupportedNpadStyleTag(supported_style_tag);
other->SetSupportedNpadStyleTag(supported_style_tag);
handheld->SetSupportedNpadStyleTag(supported_style_tag);
}
NpadStyleTag HIDCore::GetSupportedStyleTag() const {
return supported_style_tag;
}
s8 HIDCore::GetPlayerCount() const {
s8 active_players = 0;
for (std::size_t player_index = 0; player_index < available_controllers - 2; ++player_index) {
const auto* const controller = GetEmulatedControllerByIndex(player_index);
if (controller->IsConnected()) {
active_players++;
}
}
return active_players;
}
NpadIdType HIDCore::GetFirstNpadId() const {
for (std::size_t player_index = 0; player_index < available_controllers; ++player_index) {
const auto* const controller = GetEmulatedControllerByIndex(player_index);
if (controller->IsConnected()) {
return controller->GetNpadIdType();
}
}
return NpadIdType::Player1;
}
NpadIdType HIDCore::GetFirstDisconnectedNpadId() const {
for (std::size_t player_index = 0; player_index < available_controllers; ++player_index) {
const auto* const controller = GetEmulatedControllerByIndex(player_index);
if (!controller->IsConnected()) {
return controller->GetNpadIdType();
}
}
return NpadIdType::Player1;
}
void HIDCore::SetLastActiveController(NpadIdType npad_id) {
last_active_controller = npad_id;
}
NpadIdType HIDCore::GetLastActiveController() const {
return last_active_controller;
}
void HIDCore::EnableAllControllerConfiguration() {
player_1->EnableConfiguration();
player_2->EnableConfiguration();
player_3->EnableConfiguration();
player_4->EnableConfiguration();
player_5->EnableConfiguration();
player_6->EnableConfiguration();
player_7->EnableConfiguration();
player_8->EnableConfiguration();
other->EnableConfiguration();
handheld->EnableConfiguration();
}
void HIDCore::DisableAllControllerConfiguration() {
player_1->DisableConfiguration();
player_2->DisableConfiguration();
player_3->DisableConfiguration();
player_4->DisableConfiguration();
player_5->DisableConfiguration();
player_6->DisableConfiguration();
player_7->DisableConfiguration();
player_8->DisableConfiguration();
other->DisableConfiguration();
handheld->DisableConfiguration();
}
void HIDCore::ReloadInputDevices() {
player_1->ReloadFromSettings();
player_2->ReloadFromSettings();
player_3->ReloadFromSettings();
player_4->ReloadFromSettings();
player_5->ReloadFromSettings();
player_6->ReloadFromSettings();
player_7->ReloadFromSettings();
player_8->ReloadFromSettings();
other->ReloadFromSettings();
handheld->ReloadFromSettings();
console->ReloadFromSettings();
devices->ReloadFromSettings();
}
void HIDCore::UnloadInputDevices() {
player_1->UnloadInput();
player_2->UnloadInput();
player_3->UnloadInput();
player_4->UnloadInput();
player_5->UnloadInput();
player_6->UnloadInput();
player_7->UnloadInput();
player_8->UnloadInput();
other->UnloadInput();
handheld->UnloadInput();
console->UnloadInput();
devices->UnloadInput();
}
} // namespace Core::HID

View File

@@ -6,6 +6,13 @@
#include <memory>
#include "common/common_funcs.h"
#include "core/hid/hid_types.h"
namespace Core::HID {
class EmulatedConsole;
class EmulatedController;
class EmulatedDevices;
} // namespace Core::HID
namespace Core::HID {
@@ -16,6 +23,67 @@ public:
YUZU_NON_COPYABLE(HIDCore);
YUZU_NON_MOVEABLE(HIDCore);
EmulatedController* GetEmulatedController(NpadIdType npad_id_type);
const EmulatedController* GetEmulatedController(NpadIdType npad_id_type) const;
EmulatedController* GetEmulatedControllerByIndex(std::size_t index);
const EmulatedController* GetEmulatedControllerByIndex(std::size_t index) const;
EmulatedConsole* GetEmulatedConsole();
const EmulatedConsole* GetEmulatedConsole() const;
EmulatedDevices* GetEmulatedDevices();
const EmulatedDevices* GetEmulatedDevices() const;
void SetSupportedStyleTag(NpadStyleTag style_tag);
NpadStyleTag GetSupportedStyleTag() const;
/// Counts the connected players from P1-P8
s8 GetPlayerCount() const;
/// Returns the first connected npad id
NpadIdType GetFirstNpadId() const;
/// Returns the first disconnected npad id
NpadIdType GetFirstDisconnectedNpadId() const;
/// Sets the npad id of the last active controller
void SetLastActiveController(NpadIdType npad_id);
/// Returns the npad id of the last controller that pushed a button
NpadIdType GetLastActiveController() const;
/// Sets all emulated controllers into configuring mode.
void EnableAllControllerConfiguration();
/// Sets all emulated controllers into normal mode.
void DisableAllControllerConfiguration();
/// Reloads all input devices from settings
void ReloadInputDevices();
/// Removes all callbacks from input common
void UnloadInputDevices();
/// Number of emulated controllers
static constexpr std::size_t available_controllers{10};
private:
std::unique_ptr<EmulatedController> player_1;
std::unique_ptr<EmulatedController> player_2;
std::unique_ptr<EmulatedController> player_3;
std::unique_ptr<EmulatedController> player_4;
std::unique_ptr<EmulatedController> player_5;
std::unique_ptr<EmulatedController> player_6;
std::unique_ptr<EmulatedController> player_7;
std::unique_ptr<EmulatedController> player_8;
std::unique_ptr<EmulatedController> other;
std::unique_ptr<EmulatedController> handheld;
std::unique_ptr<EmulatedConsole> console;
std::unique_ptr<EmulatedDevices> devices;
NpadStyleTag supported_style_tag{NpadStyleSet::All};
NpadIdType last_active_controller{NpadIdType::Handheld};
};
} // namespace Core::HID

View File

@@ -1,33 +1,21 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <array>
#include "common/bit_field.h"
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/point.h"
#include "common/uuid.h"
#include "core/hle/result.h"
namespace Service::HID {
namespace Core::HID {
enum class DeviceIndex : u8 {
Left = 0,
Right = 1,
None = 2,
MaxDeviceIndex = 3,
MaxDeviceInfo = None,
};
// This is nn::hid::NpadCommunicationMode
enum class NpadCommunicationMode : u64 {
Mode_5ms = 0,
Mode_10ms = 1,
Mode_15ms = 2,
Default = 3,
};
// This is nn::hid::NpadButton
@@ -230,18 +218,10 @@ enum class NpadIdType : u32 {
Invalid = 0xFFFFFFFF,
};
enum class NpadRevision : u32 {
Revision0 = 0,
Revision1 = 1,
Revision2 = 2,
Revision3 = 3,
};
static_assert(sizeof(NpadRevision) == 4, "NpadRevision is an invalid size");
// This is nn::hid::NpadStyleIndex
enum class NpadStyleIndex : u8 {
None = 0,
FullKey = 3,
ProController = 3,
Handheld = 4,
HandheldNES = 4,
JoyconDual = 5,
@@ -278,7 +258,6 @@ enum class NpadStyleSet : u32 {
All = 0xFFFFFFFFU,
};
DECLARE_ENUM_FLAG_OPERATORS(NpadStyleSet);
static_assert(sizeof(NpadStyleSet) == 4, "NpadStyleSet is an invalid size");
// This is nn::hid::VibrationDevicePosition
@@ -303,12 +282,6 @@ enum class VibrationGcErmCommand : u64 {
StopHard = 2,
};
// This is nn::hid::debug::AccelerometerPlayMode
enum class AccelerometerPlayMode : u32 {
Loose = 0,
Tight = 1,
};
// This is nn::hid::GyroscopeZeroDriftMode
enum class GyroscopeZeroDriftMode : u32 {
Loose = 0,
@@ -316,24 +289,17 @@ enum class GyroscopeZeroDriftMode : u32 {
Tight = 2,
};
// This is nn::hid::NpadJoyDeviceType
enum class NpadJoyDeviceType : s64 {
Left = 0,
Right = 1,
// This is nn::settings::system::TouchScreenMode
enum class TouchScreenMode : u32 {
Stylus = 0,
Standard = 1,
};
// This is nn::hid::NpadJoyAssignmentMode
enum class NpadJoyAssignmentMode : u32 {
Dual = 0,
Single = 1,
};
// This is nn::hid::NpadHandheldActivationMode
enum class NpadHandheldActivationMode : u64 {
Dual = 0,
Single = 1,
None = 2,
MaxActivationMode = 3,
// This is nn::hid::TouchScreenModeForNx
enum class TouchScreenModeForNx : u8 {
UseSystemSetting,
Finger,
Heat2,
};
// This is nn::hid::NpadStyleTag
@@ -356,153 +322,9 @@ struct NpadStyleTag {
BitField<29, 1, u32> system_ext;
BitField<30, 1, u32> system;
};
NpadStyleTag() {
raw = NpadStyleSet::None;
}
NpadStyleTag(NpadStyleSet style_set) {
raw = style_set;
}
};
static_assert(sizeof(NpadStyleTag) == 4, "NpadStyleTag is an invalid size");
// This is nn::hid::NpadJoyHoldType
enum class NpadJoyHoldType : u64 {
Vertical = 0,
Horizontal = 1,
};
// This is nn::hid::system::AppletFooterUiAttributesSet
struct AppletFooterUiAttributes {
INSERT_PADDING_BYTES(0x4);
};
// This is nn::hid::system::AppletFooterUiType
enum class AppletFooterUiType : u8 {
None = 0,
HandheldNone = 1,
HandheldJoyConLeftOnly = 1,
HandheldJoyConRightOnly = 3,
HandheldJoyConLeftJoyConRight = 4,
JoyDual = 5,
JoyDualLeftOnly = 6,
JoyDualRightOnly = 7,
JoyLeftHorizontal = 8,
JoyLeftVertical = 9,
JoyRightHorizontal = 10,
JoyRightVertical = 11,
SwitchProController = 12,
CompatibleProController = 13,
CompatibleJoyCon = 14,
LarkHvc1 = 15,
LarkHvc2 = 16,
LarkNesLeft = 17,
LarkNesRight = 18,
Lucia = 19,
Verification = 20,
Lagon = 21,
};
// This is nn::hid::detail::ColorAttribute
enum class ColorAttribute : u32 {
Ok = 0,
ReadError = 1,
NoController = 2,
};
static_assert(sizeof(ColorAttribute) == 4, "ColorAttribute is an invalid size");
// This is nn::hid::NpadLarkType
enum class NpadLarkType : u32 {
Invalid,
H1,
H2,
NL,
NR,
};
// This is nn::hid::NpadLuciaType
enum class NpadLuciaType : u32 {
Invalid,
J,
E,
U,
};
// This is nn::hid::NpadLagonType
enum class NpadLagonType : u32 {
Invalid,
};
// This is nn::hid::NpadLagerType
enum class NpadLagerType : u32 {
Invalid,
J,
E,
U,
};
// This is nn::hid::DebugPadAttribute
enum class DebugPadAttribute : u32 {
IsConnected = 1 << 0,
};
// This is nn::hid::NpadSystemProperties
struct NPadSystemProperties {
union {
s64 raw{};
BitField<0, 1, s64> is_charging_joy_dual;
BitField<1, 1, s64> is_charging_joy_left;
BitField<2, 1, s64> is_charging_joy_right;
BitField<3, 1, s64> is_powered_joy_dual;
BitField<4, 1, s64> is_powered_joy_left;
BitField<5, 1, s64> is_powered_joy_right;
BitField<9, 1, s64> is_system_unsupported_button;
BitField<10, 1, s64> is_system_ext_unsupported_button;
BitField<11, 1, s64> is_vertical;
BitField<12, 1, s64> is_horizontal;
BitField<13, 1, s64> use_plus;
BitField<14, 1, s64> use_minus;
BitField<15, 1, s64> use_directional_buttons;
};
};
static_assert(sizeof(NPadSystemProperties) == 0x8, "NPadSystemProperties is an invalid size");
// This is nn::hid::NpadSystemButtonProperties
struct NpadSystemButtonProperties {
union {
s32 raw{};
BitField<0, 1, s32> is_home_button_protection_enabled;
};
};
static_assert(sizeof(NpadSystemButtonProperties) == 0x4, "NPadButtonProperties is an invalid size");
// This is nn::hid::system::DeviceType
struct DeviceType {
union {
u32 raw{};
BitField<0, 1, s32> fullkey;
BitField<1, 1, s32> debug_pad;
BitField<2, 1, s32> handheld_left;
BitField<3, 1, s32> handheld_right;
BitField<4, 1, s32> joycon_left;
BitField<5, 1, s32> joycon_right;
BitField<6, 1, s32> palma;
BitField<7, 1, s32> lark_hvc_left;
BitField<8, 1, s32> lark_hvc_right;
BitField<9, 1, s32> lark_nes_left;
BitField<10, 1, s32> lark_nes_right;
BitField<11, 1, s32> handheld_lark_hvc_left;
BitField<12, 1, s32> handheld_lark_hvc_right;
BitField<13, 1, s32> handheld_lark_nes_left;
BitField<14, 1, s32> handheld_lark_nes_right;
BitField<15, 1, s32> lucia;
BitField<16, 1, s32> lagon;
BitField<17, 1, s32> lager;
BitField<31, 1, s32> system;
};
};
// This is nn::hid::TouchAttribute
struct TouchAttribute {
union {
@@ -513,20 +335,6 @@ struct TouchAttribute {
};
static_assert(sizeof(TouchAttribute) == 0x4, "TouchAttribute is an invalid size");
// This is nn::hid::NpadAttribute
struct NpadAttribute {
union {
u32 raw{};
BitField<0, 1, u32> is_connected;
BitField<1, 1, u32> is_wired;
BitField<2, 1, u32> is_left_connected;
BitField<3, 1, u32> is_left_wired;
BitField<4, 1, u32> is_right_connected;
BitField<5, 1, u32> is_right_wired;
};
};
static_assert(sizeof(NpadAttribute) == 4, "NpadAttribute is an invalid size");
// This is nn::hid::TouchState
struct TouchState {
u64 delta_time{};
@@ -539,6 +347,14 @@ struct TouchState {
};
static_assert(sizeof(TouchState) == 0x28, "Touchstate is an invalid size");
// This is nn::hid::TouchScreenConfigurationForNx
struct TouchScreenConfigurationForNx {
TouchScreenModeForNx mode{TouchScreenModeForNx::UseSystemSetting};
INSERT_PADDING_BYTES(0xF);
};
static_assert(sizeof(TouchScreenConfigurationForNx) == 0x10,
"TouchScreenConfigurationForNx is an invalid size");
struct NpadColor {
u8 r{};
u8 g{};
@@ -693,16 +509,6 @@ struct DebugPadButton {
};
static_assert(sizeof(DebugPadButton) == 0x4, "DebugPadButton is an invalid size");
// This is nn::hid::debug::DebugPadAutoPilotState
struct DebugPadAutoPilotState {
DebugPadAttribute attribute;
DebugPadButton buttons;
AnalogStickState stick_l;
AnalogStickState stick_r;
};
static_assert(sizeof(DebugPadAutoPilotState) == 0x18, "DebugPadAutoPilotState is an invalid size");
// This is nn::hid::ConsoleSixAxisSensorHandle
struct ConsoleSixAxisSensorHandle {
u8 unknown_1{};
@@ -723,36 +529,12 @@ static_assert(sizeof(SixAxisSensorHandle) == 4, "SixAxisSensorHandle is an inval
// These parameters seem related to how much gyro/accelerometer is used
struct SixAxisSensorFusionParameters {
f32 parameter1{};
f32 parameter2{};
f32 parameter1{0.03f}; // Range 0.0 to 1.0, default 0.03
f32 parameter2{0.4f}; // Default 0.4
};
static_assert(sizeof(SixAxisSensorFusionParameters) == 8,
"SixAxisSensorFusionParameters is an invalid size");
// These parameters seem related to how much gyro/accelerometer is used
struct SixAxisSensorShiftAccelerometerCalibration {
f32 calibration1{};
f32 calibration2{};
};
static_assert(sizeof(SixAxisSensorShiftAccelerometerCalibration) == 8,
"SixAxisSensorShiftAccelerometerCalibration is an invalid size");
// These parameters seem related to how much gyro/accelerometer is used
struct SixAxisSensorShiftGyroscopeCalibration {
f32 calibration1{};
f32 calibration2{};
};
static_assert(sizeof(SixAxisSensorShiftGyroscopeCalibration) == 8,
"SixAxisSensorShiftGyroscopeCalibration is an invalid size");
// These parameters seem related to how much gyro/accelerometer is used
struct SixAxisAccelerometerParameters {
f32 parameter1{};
f32 parameter2{};
};
static_assert(sizeof(SixAxisAccelerometerParameters) == 8,
"SixAxisSensorFusionParameters is an invalid size");
// This is nn::hid::server::SixAxisSensorProperties
struct SixAxisSensorProperties {
union {
@@ -798,7 +580,7 @@ static_assert(sizeof(SixAxisSensorIcInformation) == 0xC8,
// This is nn::hid::VibrationDeviceHandle
struct VibrationDeviceHandle {
NpadStyleIndex npad_style_index{NpadStyleIndex::None};
NpadStyleIndex npad_type{NpadStyleIndex::None};
u8 npad_id{};
DeviceIndex device_index{DeviceIndex::None};
INSERT_PADDING_BYTES_NOINIT(1);
@@ -863,16 +645,6 @@ struct KeyboardKey {
};
static_assert(sizeof(KeyboardKey) == 0x20, "KeyboardKey is an invalid size");
// This is nn::hid::debug::KeyboardAutoPilotState
struct KeyboardAutoPilotState {
KeyboardModifier modifier{};
INSERT_PADDING_BYTES(0x4);
KeyboardKey keys{};
};
static_assert(sizeof(KeyboardAutoPilotState) == 0x28, "KeyboardAutoPilotState is an invalid size");
// This is nn::hid::MouseButton
struct MouseButton {
union {
@@ -911,101 +683,65 @@ struct MouseState {
};
static_assert(sizeof(MouseState) == 0x28, "MouseState is an invalid size");
// This is nn::hid::debug::MouseAutoPilotState
struct MouseAutoPilotState {
s32 x{};
s32 y{};
s32 delta_x{};
s32 delta_y{};
s32 delta_wheel{};
MouseButton button{};
MouseAttribute attribute{};
struct UniquePadId {
u64 id;
};
static_assert(sizeof(MouseAutoPilotState) == 0x1c, "MouseAutoPilotState is an invalid size");
static_assert(sizeof(UniquePadId) == 0x8, "UniquePadId is an invalid size");
// This is nn::hid::PalmaOperationType
enum class PalmaOperationType : u32 {
PlayActivity,
SetFrModeType,
ReadStep,
EnableStep,
ResetStep,
ReadApplicationSection,
WriteApplicationSection,
ReadUniqueCode,
SetUniqueCodeInvalid,
WriteActivityEntry,
WriteRgbLedPatternEntry,
WriteWaveEntry,
ReadDataBaseIdentificationVersion,
WriteDataBaseIdentificationVersion,
SuspendFeature,
ReadPlayLog,
ResetPlayLog,
};
/// Converts a NpadIdType to an array index.
constexpr size_t NpadIdTypeToIndex(NpadIdType npad_id_type) {
switch (npad_id_type) {
case NpadIdType::Player1:
return 0;
case NpadIdType::Player2:
return 1;
case NpadIdType::Player3:
return 2;
case NpadIdType::Player4:
return 3;
case NpadIdType::Player5:
return 4;
case NpadIdType::Player6:
return 5;
case NpadIdType::Player7:
return 6;
case NpadIdType::Player8:
return 7;
case NpadIdType::Handheld:
return 8;
case NpadIdType::Other:
return 9;
default:
return 0;
}
}
// This is nn::hid::PalmaWaveSet
enum class PalmaWaveSet : u64 {
Small,
Medium,
Large,
};
/// Converts an array index to a NpadIdType
constexpr NpadIdType IndexToNpadIdType(size_t index) {
switch (index) {
case 0:
return NpadIdType::Player1;
case 1:
return NpadIdType::Player2;
case 2:
return NpadIdType::Player3;
case 3:
return NpadIdType::Player4;
case 4:
return NpadIdType::Player5;
case 5:
return NpadIdType::Player6;
case 6:
return NpadIdType::Player7;
case 7:
return NpadIdType::Player8;
case 8:
return NpadIdType::Handheld;
case 9:
return NpadIdType::Other;
default:
return NpadIdType::Invalid;
}
}
// This is nn::hid::PalmaFrModeType
enum class PalmaFrModeType : u64 {
Off,
B01,
B02,
B03,
Downloaded,
};
// This is nn::hid::PalmaFeature
enum class PalmaFeature : u64 {
FrMode,
RumbleFeedback,
Step,
MuteSwitch,
};
using PalmaOperationData = std::array<u8, 0x140>;
// This is nn::hid::PalmaOperationInfo
struct PalmaOperationInfo {
PalmaOperationType operation;
Result result;
PalmaOperationData data;
};
static_assert(sizeof(PalmaOperationInfo) == 0x148, "PalmaOperationInfo is an invalid size");
// This is nn::hid::PalmaActivityEntry
struct PalmaActivityEntry {
u32 rgb_led_pattern_index;
INSERT_PADDING_BYTES(2);
PalmaWaveSet wave_set;
u32 wave_index;
INSERT_PADDING_BYTES(12);
};
static_assert(sizeof(PalmaActivityEntry) == 0x20, "PalmaActivityEntry is an invalid size");
struct PalmaConnectionHandle {
NpadIdType npad_id;
INSERT_PADDING_BYTES(4);
};
static_assert(sizeof(PalmaConnectionHandle) == 0x8, "PalmaConnectionHandle has incorrect size.");
// This is nn::hid::TouchScreenModeForNx
enum class TouchScreenModeForNx : u8 {
UseSystemSetting,
Finger,
Heat2,
};
// This is nn::hid::TouchScreenConfigurationForNx
struct TouchScreenConfigurationForNx {
TouchScreenModeForNx mode{TouchScreenModeForNx::UseSystemSetting};
INSERT_PADDING_BYTES(0xF);
};
static_assert(sizeof(TouchScreenConfigurationForNx) == 0x10,
"TouchScreenConfigurationForNx has incorrect size.");
} // namespace Service::HID
} // namespace Core::HID

View File

@@ -0,0 +1,60 @@
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/core.h"
#include "core/hid/hid_types.h"
#include "core/hid/input_interpreter.h"
#include "core/hle/service/hid/controllers/npad.h"
#include "core/hle/service/hid/hid.h"
#include "core/hle/service/sm/sm.h"
InputInterpreter::InputInterpreter(Core::System& system)
: npad{system.ServiceManager()
.GetService<Service::HID::Hid>("hid")
->GetAppletResource()
->GetController<Service::HID::Controller_NPad>(Service::HID::HidController::NPad)} {
ResetButtonStates();
}
InputInterpreter::~InputInterpreter() = default;
void InputInterpreter::PollInput() {
const auto button_state = npad.GetAndResetPressState();
previous_index = current_index;
current_index = (current_index + 1) % button_states.size();
button_states[current_index] = button_state;
}
void InputInterpreter::ResetButtonStates() {
previous_index = 0;
current_index = 0;
button_states[0] = Core::HID::NpadButton::All;
for (std::size_t i = 1; i < button_states.size(); ++i) {
button_states[i] = Core::HID::NpadButton::None;
}
}
bool InputInterpreter::IsButtonPressed(Core::HID::NpadButton button) const {
return True(button_states[current_index] & button);
}
bool InputInterpreter::IsButtonPressedOnce(Core::HID::NpadButton button) const {
const bool current_press = True(button_states[current_index] & button);
const bool previous_press = True(button_states[previous_index] & button);
return current_press && !previous_press;
}
bool InputInterpreter::IsButtonHeld(Core::HID::NpadButton button) const {
Core::HID::NpadButton held_buttons{button_states[0]};
for (std::size_t i = 1; i < button_states.size(); ++i) {
held_buttons &= button_states[i];
}
return True(held_buttons & button);
}

View File

@@ -0,0 +1,111 @@
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <array>
#include "common/common_types.h"
namespace Core {
class System;
}
namespace Core::HID {
enum class NpadButton : u64;
}
namespace Service::HID {
class Controller_NPad;
}
/**
* The InputInterpreter class interfaces with HID to retrieve button press states.
* Input is intended to be polled every 50ms so that a button is considered to be
* held down after 400ms has elapsed since the initial button press and subsequent
* repeated presses occur every 50ms.
*/
class InputInterpreter {
public:
explicit InputInterpreter(Core::System& system);
virtual ~InputInterpreter();
/// Gets a button state from HID and inserts it into the array of button states.
void PollInput();
/// Resets all the button states to their defaults.
void ResetButtonStates();
/**
* Checks whether the button is pressed.
*
* @param button The button to check.
*
* @returns True when the button is pressed.
*/
[[nodiscard]] bool IsButtonPressed(Core::HID::NpadButton button) const;
/**
* Checks whether any of the buttons in the parameter list is pressed.
*
* @tparam HIDButton The buttons to check.
*
* @returns True when at least one of the buttons is pressed.
*/
template <Core::HID::NpadButton... T>
[[nodiscard]] bool IsAnyButtonPressed() {
return (IsButtonPressed(T) || ...);
}
/**
* The specified button is considered to be pressed once
* if it is currently pressed and not pressed previously.
*
* @param button The button to check.
*
* @returns True when the button is pressed once.
*/
[[nodiscard]] bool IsButtonPressedOnce(Core::HID::NpadButton button) const;
/**
* Checks whether any of the buttons in the parameter list is pressed once.
*
* @tparam T The buttons to check.
*
* @returns True when at least one of the buttons is pressed once.
*/
template <Core::HID::NpadButton... T>
[[nodiscard]] bool IsAnyButtonPressedOnce() const {
return (IsButtonPressedOnce(T) || ...);
}
/**
* The specified button is considered to be held down if it is pressed in all 9 button states.
*
* @param button The button to check.
*
* @returns True when the button is held down.
*/
[[nodiscard]] bool IsButtonHeld(Core::HID::NpadButton button) const;
/**
* Checks whether any of the buttons in the parameter list is held down.
*
* @tparam T The buttons to check.
*
* @returns True when at least one of the buttons is held down.
*/
template <Core::HID::NpadButton... T>
[[nodiscard]] bool IsAnyButtonHeld() const {
return (IsButtonHeld(T) || ...);
}
private:
Service::HID::Controller_NPad& npad;
/// Stores 9 consecutive button states polled from HID.
std::array<Core::HID::NpadButton, 9> button_states{};
std::size_t previous_index{};
std::size_t current_index{};
};

View File

@@ -5,9 +5,9 @@
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "core/hle/service/hid/hid_server/hid_types.h"
#include "core/hid/hid_types.h"
namespace Service::IRS {
namespace Core::IrSensor {
// This is nn::irsensor::CameraAmbientNoiseLevel
enum class CameraAmbientNoiseLevel : u32 {
@@ -170,7 +170,7 @@ static_assert(sizeof(PackedCameraConfig) == 0x10, "PackedCameraConfig is an inva
// This is nn::irsensor::IrCameraHandle
struct IrCameraHandle {
u8 npad_id{};
Service::HID::NpadStyleIndex npad_type{Service::HID::NpadStyleIndex::None};
Core::HID::NpadStyleIndex npad_type{Core::HID::NpadStyleIndex::None};
INSERT_PADDING_BYTES(2);
};
static_assert(sizeof(IrCameraHandle) == 4, "IrCameraHandle is an invalid size");
@@ -281,9 +281,10 @@ static_assert(sizeof(ProcessorState) == 0xE20, "ProcessorState is an invalid siz
// This is nn::irsensor::detail::DeviceFormat
struct DeviceFormat {
IrCameraStatus camera_status{IrCameraStatus::Unconnected};
IrCameraInternalStatus camera_internal_status{IrCameraInternalStatus::Ready};
IrSensorMode mode{IrSensorMode::None};
Core::IrSensor::IrCameraStatus camera_status{Core::IrSensor::IrCameraStatus::Unconnected};
Core::IrSensor::IrCameraInternalStatus camera_internal_status{
Core::IrSensor::IrCameraInternalStatus::Ready};
Core::IrSensor::IrSensorMode mode{Core::IrSensor::IrSensorMode::None};
ProcessorState state{};
};
static_assert(sizeof(DeviceFormat) == 0xE30, "DeviceFormat is an invalid size");
@@ -291,10 +292,10 @@ static_assert(sizeof(DeviceFormat) == 0xE30, "DeviceFormat is an invalid size");
// This is nn::irsensor::ImageTransferProcessorState
struct ImageTransferProcessorState {
u64 sampling_number;
CameraAmbientNoiseLevel ambient_noise_level;
Core::IrSensor::CameraAmbientNoiseLevel ambient_noise_level;
INSERT_PADDING_BYTES(4);
};
static_assert(sizeof(ImageTransferProcessorState) == 0x10,
"ImageTransferProcessorState is an invalid size");
} // namespace Service::IRS
} // namespace Core::IrSensor

View File

@@ -3405,6 +3405,11 @@ Result KPageTable::LockMemoryAndOpen(KPageGroup* out_pg, KPhysicalAddress* out_K
new_attr, KMemoryBlockDisableMergeAttribute::Locked,
KMemoryBlockDisableMergeAttribute::None);
// If we have an output page group, open.
if (out_pg) {
out_pg->Open();
}
R_SUCCEED();
}

View File

@@ -12,7 +12,6 @@
#include "core/hle/service/am/applets/applet_cabinet.h"
#include "core/hle/service/mii/mii_manager.h"
#include "core/hle/service/nfc/common/device.h"
#include "core/hle/service/hid/hid_types.h"
namespace Service::AM::Applets {
@@ -74,7 +73,7 @@ void Cabinet::Execute() {
// TODO: listen on all controllers
if (nfp_device == nullptr) {
nfp_device = std::make_shared<Service::NFC::NfcDevice>(
Service::HID::NpadIdType::Player1, system, service_context, availability_change_event);
system.HIDCore().GetFirstNpadId(), system, service_context, availability_change_event);
nfp_device->Initialize();
nfp_device->StartDetection(Service::NFC::NfcProtocol::All);
}

View File

@@ -9,11 +9,13 @@
#include "common/string_util.h"
#include "core/core.h"
#include "core/frontend/applets/controller.h"
#include "core/hid/emulated_controller.h"
#include "core/hid/hid_core.h"
#include "core/hle/service/hid/hid_types.h"
#include "core/hid/hid_types.h"
#include "core/hle/result.h"
#include "core/hle/service/am/am.h"
#include "core/hle/service/am/applets/applet_controller.h"
#include "core/hle/service/hid/controllers/npad.h"
namespace Service::AM::Applets {
@@ -24,7 +26,7 @@ namespace Service::AM::Applets {
static Core::Frontend::ControllerParameters ConvertToFrontendParameters(
ControllerSupportArgPrivate private_arg, ControllerSupportArgHeader header, bool enable_text,
std::vector<IdentificationColor> identification_colors, std::vector<ExplainText> text) {
Service::HID::NpadStyleTag npad_style_set;
Core::HID::NpadStyleTag npad_style_set;
npad_style_set.raw = private_arg.style_set;
return {
@@ -245,9 +247,9 @@ void Controller::ConfigurationComplete(bool is_success) {
// If enable_single_mode is enabled, player_count is 1 regardless of any other parameters.
// Otherwise, only count connected players from P1-P8.
result_info.player_count = is_single_mode ? 1 : 0;
result_info.player_count = is_single_mode ? 1 : system.HIDCore().GetPlayerCount();
result_info.selected_id = static_cast<u32>(Service::HID::NpadIdType::Player1);
result_info.selected_id = static_cast<u32>(system.HIDCore().GetFirstNpadId());
result_info.result =
is_success ? ControllerSupportResult::Success : ControllerSupportResult::Cancel;

View File

@@ -15,7 +15,7 @@ namespace Core {
class System;
}
namespace Service::HID {
namespace Core::HID {
enum class NpadStyleSet : u32;
}
@@ -60,7 +60,7 @@ struct ControllerSupportArgPrivate {
bool flag_1{};
ControllerSupportMode mode{};
ControllerSupportCaller caller{};
Service::HID::NpadStyleSet style_set{};
Core::HID::NpadStyleSet style_set{};
u32 joy_hold_type{};
};
static_assert(sizeof(ControllerSupportArgPrivate) == 0x14,

View File

@@ -67,34 +67,6 @@ enum class AppletId : u32 {
LoginShare = 0x18,
WebAuth = 0x19,
MyPage = 0x1A,
Gift = 0x1B,
UserMigration = 0x1C,
PreomiaSys = 0x1D,
Story = 0x1E,
PreomiaUsr = 0x1F,
PreomiaUsrDummy = 0x20,
Sample = 0x21,
PromoteQualification = 0x22,
Unknown35 = 0x35,
Unknown36 = 0x36,
Unknown37 = 0x37,
Unknown38 = 0x38,
DevlopmentTool = 0x3E8,
CombinationLA = 0x3F1,
AeSystemApplet = 0x3F2,
AeOverlayApplet = 0x3F3,
AeStarter = 0x3F4,
AeLibraryAppletAlone = 0x3F5,
AeLibraryApplet1 = 0x3F6,
AeLibraryApplet2 = 0x3F7,
AeLibraryApplet3 = 0x3F8,
AeLibraryApplet4 = 0x3F9,
AppletISA = 0x3FA,
AppletIOA = 0x3FB,
AppletISTA = 0x3FC,
AppletILA1 = 0x3FD,
AppletILA2 = 0x3FE,
};
enum class LibraryAppletMode : u32 {

View File

@@ -0,0 +1,74 @@
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/core.h"
#include "core/core_timing.h"
#include "core/hid/emulated_console.h"
#include "core/hid/hid_core.h"
#include "core/hle/service/hid/controllers/console_sixaxis.h"
#include "core/memory.h"
namespace Service::HID {
constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3C200;
Controller_ConsoleSixAxis::Controller_ConsoleSixAxis(Core::System& system_, u8* raw_shared_memory_)
: ControllerBase{system_.HIDCore()}, system{system_} {
console = hid_core.GetEmulatedConsole();
static_assert(SHARED_MEMORY_OFFSET + sizeof(ConsoleSharedMemory) < shared_memory_size,
"ConsoleSharedMemory is bigger than the shared memory");
shared_memory = std::construct_at(
reinterpret_cast<ConsoleSharedMemory*>(raw_shared_memory_ + SHARED_MEMORY_OFFSET));
}
Controller_ConsoleSixAxis::~Controller_ConsoleSixAxis() = default;
void Controller_ConsoleSixAxis::OnInit() {}
void Controller_ConsoleSixAxis::OnRelease() {}
void Controller_ConsoleSixAxis::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
if (!IsControllerActivated() || transfer_memory == 0) {
seven_sixaxis_lifo.buffer_count = 0;
seven_sixaxis_lifo.buffer_tail = 0;
return;
}
const auto& last_entry = seven_sixaxis_lifo.ReadCurrentEntry().state;
next_seven_sixaxis_state.sampling_number = last_entry.sampling_number + 1;
const auto motion_status = console->GetMotion();
last_global_timestamp = core_timing.GetGlobalTimeNs().count();
// This value increments every time the switch goes to sleep
next_seven_sixaxis_state.unknown = 1;
next_seven_sixaxis_state.timestamp = last_global_timestamp - last_saved_timestamp;
next_seven_sixaxis_state.accel = motion_status.accel;
next_seven_sixaxis_state.gyro = motion_status.gyro;
next_seven_sixaxis_state.quaternion = {
{
motion_status.quaternion.xyz.y,
motion_status.quaternion.xyz.x,
-motion_status.quaternion.w,
},
-motion_status.quaternion.xyz.z,
};
shared_memory->sampling_number++;
shared_memory->is_seven_six_axis_sensor_at_rest = motion_status.is_at_rest;
shared_memory->verticalization_error = motion_status.verticalization_error;
shared_memory->gyro_bias = motion_status.gyro_bias;
// Update seven six axis transfer memory
seven_sixaxis_lifo.WriteNextEntry(next_seven_sixaxis_state);
system.ApplicationMemory().WriteBlock(transfer_memory, &seven_sixaxis_lifo,
sizeof(seven_sixaxis_lifo));
}
void Controller_ConsoleSixAxis::SetTransferMemoryAddress(Common::ProcessAddress t_mem) {
transfer_memory = t_mem;
}
void Controller_ConsoleSixAxis::ResetTimestamp() {
last_saved_timestamp = last_global_timestamp;
}
} // namespace Service::HID

View File

@@ -0,0 +1,78 @@
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <array>
#include "common/quaternion.h"
#include "common/typed_address.h"
#include "core/hle/service/hid/controllers/controller_base.h"
#include "core/hle/service/hid/ring_lifo.h"
namespace Core {
class System;
} // namespace Core
namespace Core::HID {
class EmulatedConsole;
} // namespace Core::HID
namespace Service::HID {
class Controller_ConsoleSixAxis final : public ControllerBase {
public:
explicit Controller_ConsoleSixAxis(Core::System& system_, u8* raw_shared_memory_);
~Controller_ConsoleSixAxis() override;
// Called when the controller is initialized
void OnInit() override;
// When the controller is released
void OnRelease() override;
// When the controller is requesting an update for the shared memory
void OnUpdate(const Core::Timing::CoreTiming& core_timing) override;
// Called on InitializeSevenSixAxisSensor
void SetTransferMemoryAddress(Common::ProcessAddress t_mem);
// Called on ResetSevenSixAxisSensorTimestamp
void ResetTimestamp();
private:
struct SevenSixAxisState {
INSERT_PADDING_WORDS(2); // unused
u64 timestamp{};
u64 sampling_number{};
u64 unknown{};
Common::Vec3f accel{};
Common::Vec3f gyro{};
Common::Quaternion<f32> quaternion{};
};
static_assert(sizeof(SevenSixAxisState) == 0x48, "SevenSixAxisState is an invalid size");
// This is nn::hid::detail::ConsoleSixAxisSensorSharedMemoryFormat
struct ConsoleSharedMemory {
u64 sampling_number{};
bool is_seven_six_axis_sensor_at_rest{};
INSERT_PADDING_BYTES(3); // padding
f32 verticalization_error{};
Common::Vec3f gyro_bias{};
INSERT_PADDING_BYTES(4); // padding
};
static_assert(sizeof(ConsoleSharedMemory) == 0x20, "ConsoleSharedMemory is an invalid size");
Lifo<SevenSixAxisState, 0x21> seven_sixaxis_lifo{};
static_assert(sizeof(seven_sixaxis_lifo) == 0xA70, "SevenSixAxisState is an invalid size");
SevenSixAxisState next_seven_sixaxis_state{};
Common::ProcessAddress transfer_memory{};
ConsoleSharedMemory* shared_memory = nullptr;
Core::HID::EmulatedConsole* console = nullptr;
u64 last_saved_timestamp{};
u64 last_global_timestamp{};
Core::System& system;
};
} // namespace Service::HID

View File

@@ -0,0 +1,29 @@
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/service/hid/controllers/controller_base.h"
namespace Service::HID {
ControllerBase::ControllerBase(Core::HID::HIDCore& hid_core_) : hid_core(hid_core_) {}
ControllerBase::~ControllerBase() = default;
void ControllerBase::ActivateController() {
if (is_activated) {
return;
}
is_activated = true;
OnInit();
}
void ControllerBase::DeactivateController() {
if (is_activated) {
OnRelease();
}
is_activated = false;
}
bool ControllerBase::IsControllerActivated() const {
return is_activated;
}
} // namespace Service::HID

View File

@@ -0,0 +1,48 @@
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "common/common_types.h"
namespace Core::Timing {
class CoreTiming;
}
namespace Core::HID {
class HIDCore;
}
namespace Service::HID {
class ControllerBase {
public:
explicit ControllerBase(Core::HID::HIDCore& hid_core_);
virtual ~ControllerBase();
// Called when the controller is initialized
virtual void OnInit() = 0;
// When the controller is released
virtual void OnRelease() = 0;
// When the controller is requesting an update for the shared memory
virtual void OnUpdate(const Core::Timing::CoreTiming& core_timing) = 0;
// When the controller is requesting a motion update for the shared memory
virtual void OnMotionUpdate(const Core::Timing::CoreTiming& core_timing) {}
void ActivateController();
void DeactivateController();
bool IsControllerActivated() const;
static const std::size_t hid_entry_count = 17;
static const std::size_t shared_memory_size = 0x40000;
protected:
bool is_activated{false};
Core::HID::HIDCore& hid_core;
};
} // namespace Service::HID

View File

@@ -0,0 +1,55 @@
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <cstring>
#include "common/common_types.h"
#include "common/settings.h"
#include "core/core_timing.h"
#include "core/hid/emulated_controller.h"
#include "core/hid/hid_core.h"
#include "core/hid/hid_types.h"
#include "core/hle/service/hid/controllers/debug_pad.h"
namespace Service::HID {
constexpr std::size_t SHARED_MEMORY_OFFSET = 0x00000;
Controller_DebugPad::Controller_DebugPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_)
: ControllerBase{hid_core_} {
static_assert(SHARED_MEMORY_OFFSET + sizeof(DebugPadSharedMemory) < shared_memory_size,
"DebugPadSharedMemory is bigger than the shared memory");
shared_memory = std::construct_at(
reinterpret_cast<DebugPadSharedMemory*>(raw_shared_memory_ + SHARED_MEMORY_OFFSET));
controller = hid_core.GetEmulatedController(Core::HID::NpadIdType::Other);
}
Controller_DebugPad::~Controller_DebugPad() = default;
void Controller_DebugPad::OnInit() {}
void Controller_DebugPad::OnRelease() {}
void Controller_DebugPad::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
if (!IsControllerActivated()) {
shared_memory->debug_pad_lifo.buffer_count = 0;
shared_memory->debug_pad_lifo.buffer_tail = 0;
return;
}
const auto& last_entry = shared_memory->debug_pad_lifo.ReadCurrentEntry().state;
next_state.sampling_number = last_entry.sampling_number + 1;
if (Settings::values.debug_pad_enabled) {
next_state.attribute.connected.Assign(1);
const auto& button_state = controller->GetDebugPadButtons();
const auto& stick_state = controller->GetSticks();
next_state.pad_state = button_state;
next_state.l_stick = stick_state.left;
next_state.r_stick = stick_state.right;
}
shared_memory->debug_pad_lifo.WriteNextEntry(next_state);
}
} // namespace Service::HID

View File

@@ -0,0 +1,64 @@
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "common/bit_field.h"
#include "common/common_types.h"
#include "core/hle/service/hid/controllers/controller_base.h"
#include "core/hle/service/hid/ring_lifo.h"
namespace Core::HID {
class EmulatedController;
struct DebugPadButton;
struct AnalogStickState;
} // namespace Core::HID
namespace Service::HID {
class Controller_DebugPad final : public ControllerBase {
public:
explicit Controller_DebugPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_);
~Controller_DebugPad() override;
// Called when the controller is initialized
void OnInit() override;
// When the controller is released
void OnRelease() override;
// When the controller is requesting an update for the shared memory
void OnUpdate(const Core::Timing::CoreTiming& core_timing) override;
private:
// This is nn::hid::DebugPadAttribute
struct DebugPadAttribute {
union {
u32 raw{};
BitField<0, 1, u32> connected;
};
};
static_assert(sizeof(DebugPadAttribute) == 0x4, "DebugPadAttribute is an invalid size");
// This is nn::hid::DebugPadState
struct DebugPadState {
s64 sampling_number{};
DebugPadAttribute attribute{};
Core::HID::DebugPadButton pad_state{};
Core::HID::AnalogStickState r_stick{};
Core::HID::AnalogStickState l_stick{};
};
static_assert(sizeof(DebugPadState) == 0x20, "DebugPadState is an invalid state");
struct DebugPadSharedMemory {
// This is nn::hid::detail::DebugPadLifo
Lifo<DebugPadState, hid_entry_count> debug_pad_lifo{};
static_assert(sizeof(debug_pad_lifo) == 0x2C8, "debug_pad_lifo is an invalid size");
INSERT_PADDING_WORDS(0x4E);
};
static_assert(sizeof(DebugPadSharedMemory) == 0x400, "DebugPadSharedMemory is an invalid size");
DebugPadState next_state{};
DebugPadSharedMemory* shared_memory = nullptr;
Core::HID::EmulatedController* controller = nullptr;
};
} // namespace Service::HID

View File

@@ -0,0 +1,359 @@
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/logging/log.h"
#include "common/math_util.h"
#include "common/settings.h"
#include "core/core_timing.h"
#include "core/frontend/emu_window.h"
#include "core/hid/hid_core.h"
#include "core/hle/service/hid/controllers/gesture.h"
namespace Service::HID {
constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3BA00;
// HW is around 700, value is set to 400 to make it easier to trigger with mouse
constexpr f32 swipe_threshold = 400.0f; // Threshold in pixels/s
constexpr f32 angle_threshold = 0.015f; // Threshold in radians
constexpr f32 pinch_threshold = 0.5f; // Threshold in pixels
constexpr f32 press_delay = 0.5f; // Time in seconds
constexpr f32 double_tap_delay = 0.35f; // Time in seconds
constexpr f32 Square(s32 num) {
return static_cast<f32>(num * num);
}
Controller_Gesture::Controller_Gesture(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_)
: ControllerBase(hid_core_) {
static_assert(SHARED_MEMORY_OFFSET + sizeof(GestureSharedMemory) < shared_memory_size,
"GestureSharedMemory is bigger than the shared memory");
shared_memory = std::construct_at(
reinterpret_cast<GestureSharedMemory*>(raw_shared_memory_ + SHARED_MEMORY_OFFSET));
console = hid_core.GetEmulatedConsole();
}
Controller_Gesture::~Controller_Gesture() = default;
void Controller_Gesture::OnInit() {
shared_memory->gesture_lifo.buffer_count = 0;
shared_memory->gesture_lifo.buffer_tail = 0;
force_update = true;
}
void Controller_Gesture::OnRelease() {}
void Controller_Gesture::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
if (!IsControllerActivated()) {
shared_memory->gesture_lifo.buffer_count = 0;
shared_memory->gesture_lifo.buffer_tail = 0;
return;
}
ReadTouchInput();
GestureProperties gesture = GetGestureProperties();
f32 time_difference =
static_cast<f32>(shared_memory->gesture_lifo.timestamp - last_update_timestamp) /
(1000 * 1000 * 1000);
// Only update if necessary
if (!ShouldUpdateGesture(gesture, time_difference)) {
return;
}
last_update_timestamp = shared_memory->gesture_lifo.timestamp;
UpdateGestureSharedMemory(gesture, time_difference);
}
void Controller_Gesture::ReadTouchInput() {
if (!Settings::values.touchscreen.enabled) {
fingers = {};
return;
}
const auto touch_status = console->GetTouch();
for (std::size_t id = 0; id < fingers.size(); ++id) {
fingers[id] = touch_status[id];
}
}
bool Controller_Gesture::ShouldUpdateGesture(const GestureProperties& gesture,
f32 time_difference) {
const auto& last_entry = GetLastGestureEntry();
if (force_update) {
force_update = false;
return true;
}
// Update if coordinates change
for (size_t id = 0; id < MAX_POINTS; id++) {
if (gesture.points[id] != last_gesture.points[id]) {
return true;
}
}
// Update on press and hold event after 0.5 seconds
if (last_entry.type == GestureType::Touch && last_entry.point_count == 1 &&
time_difference > press_delay) {
return enable_press_and_tap;
}
return false;
}
void Controller_Gesture::UpdateGestureSharedMemory(GestureProperties& gesture,
f32 time_difference) {
GestureType type = GestureType::Idle;
GestureAttribute attributes{};
const auto& last_entry = shared_memory->gesture_lifo.ReadCurrentEntry().state;
// Reset next state to default
next_state.sampling_number = last_entry.sampling_number + 1;
next_state.delta = {};
next_state.vel_x = 0;
next_state.vel_y = 0;
next_state.direction = GestureDirection::None;
next_state.rotation_angle = 0;
next_state.scale = 0;
if (gesture.active_points > 0) {
if (last_gesture.active_points == 0) {
NewGesture(gesture, type, attributes);
} else {
UpdateExistingGesture(gesture, type, time_difference);
}
} else {
EndGesture(gesture, last_gesture, type, attributes, time_difference);
}
// Apply attributes
next_state.detection_count = gesture.detection_count;
next_state.type = type;
next_state.attributes = attributes;
next_state.pos = gesture.mid_point;
next_state.point_count = static_cast<s32>(gesture.active_points);
next_state.points = gesture.points;
last_gesture = gesture;
shared_memory->gesture_lifo.WriteNextEntry(next_state);
}
void Controller_Gesture::NewGesture(GestureProperties& gesture, GestureType& type,
GestureAttribute& attributes) {
const auto& last_entry = GetLastGestureEntry();
gesture.detection_count++;
type = GestureType::Touch;
// New touch after cancel is not considered new
if (last_entry.type != GestureType::Cancel) {
attributes.is_new_touch.Assign(1);
enable_press_and_tap = true;
}
}
void Controller_Gesture::UpdateExistingGesture(GestureProperties& gesture, GestureType& type,
f32 time_difference) {
const auto& last_entry = GetLastGestureEntry();
// Promote to pan type if touch moved
for (size_t id = 0; id < MAX_POINTS; id++) {
if (gesture.points[id] != last_gesture.points[id]) {
type = GestureType::Pan;
break;
}
}
// Number of fingers changed cancel the last event and clear data
if (gesture.active_points != last_gesture.active_points) {
type = GestureType::Cancel;
enable_press_and_tap = false;
gesture.active_points = 0;
gesture.mid_point = {};
gesture.points.fill({});
return;
}
// Calculate extra parameters of panning
if (type == GestureType::Pan) {
UpdatePanEvent(gesture, last_gesture, type, time_difference);
return;
}
// Promote to press type
if (last_entry.type == GestureType::Touch) {
type = GestureType::Press;
}
}
void Controller_Gesture::EndGesture(GestureProperties& gesture,
GestureProperties& last_gesture_props, GestureType& type,
GestureAttribute& attributes, f32 time_difference) {
const auto& last_entry = GetLastGestureEntry();
if (last_gesture_props.active_points != 0) {
switch (last_entry.type) {
case GestureType::Touch:
if (enable_press_and_tap) {
SetTapEvent(gesture, last_gesture_props, type, attributes);
return;
}
type = GestureType::Cancel;
force_update = true;
break;
case GestureType::Press:
case GestureType::Tap:
case GestureType::Swipe:
case GestureType::Pinch:
case GestureType::Rotate:
type = GestureType::Complete;
force_update = true;
break;
case GestureType::Pan:
EndPanEvent(gesture, last_gesture_props, type, time_difference);
break;
default:
break;
}
return;
}
if (last_entry.type == GestureType::Complete || last_entry.type == GestureType::Cancel) {
gesture.detection_count++;
}
}
void Controller_Gesture::SetTapEvent(GestureProperties& gesture,
GestureProperties& last_gesture_props, GestureType& type,
GestureAttribute& attributes) {
type = GestureType::Tap;
gesture = last_gesture_props;
force_update = true;
f32 tap_time_difference =
static_cast<f32>(last_update_timestamp - last_tap_timestamp) / (1000 * 1000 * 1000);
last_tap_timestamp = last_update_timestamp;
if (tap_time_difference < double_tap_delay) {
attributes.is_double_tap.Assign(1);
}
}
void Controller_Gesture::UpdatePanEvent(GestureProperties& gesture,
GestureProperties& last_gesture_props, GestureType& type,
f32 time_difference) {
const auto& last_entry = GetLastGestureEntry();
next_state.delta = gesture.mid_point - last_entry.pos;
next_state.vel_x = static_cast<f32>(next_state.delta.x) / time_difference;
next_state.vel_y = static_cast<f32>(next_state.delta.y) / time_difference;
last_pan_time_difference = time_difference;
// Promote to pinch type
if (std::abs(gesture.average_distance - last_gesture_props.average_distance) >
pinch_threshold) {
type = GestureType::Pinch;
next_state.scale = gesture.average_distance / last_gesture_props.average_distance;
}
const f32 angle_between_two_lines = std::atan((gesture.angle - last_gesture_props.angle) /
(1 + (gesture.angle * last_gesture_props.angle)));
// Promote to rotate type
if (std::abs(angle_between_two_lines) > angle_threshold) {
type = GestureType::Rotate;
next_state.scale = 0;
next_state.rotation_angle = angle_between_two_lines * 180.0f / Common::PI;
}
}
void Controller_Gesture::EndPanEvent(GestureProperties& gesture,
GestureProperties& last_gesture_props, GestureType& type,
f32 time_difference) {
const auto& last_entry = GetLastGestureEntry();
next_state.vel_x =
static_cast<f32>(last_entry.delta.x) / (last_pan_time_difference + time_difference);
next_state.vel_y =
static_cast<f32>(last_entry.delta.y) / (last_pan_time_difference + time_difference);
const f32 curr_vel =
std::sqrt((next_state.vel_x * next_state.vel_x) + (next_state.vel_y * next_state.vel_y));
// Set swipe event with parameters
if (curr_vel > swipe_threshold) {
SetSwipeEvent(gesture, last_gesture_props, type);
return;
}
// End panning without swipe
type = GestureType::Complete;
next_state.vel_x = 0;
next_state.vel_y = 0;
force_update = true;
}
void Controller_Gesture::SetSwipeEvent(GestureProperties& gesture,
GestureProperties& last_gesture_props, GestureType& type) {
const auto& last_entry = GetLastGestureEntry();
type = GestureType::Swipe;
gesture = last_gesture_props;
force_update = true;
next_state.delta = last_entry.delta;
if (std::abs(next_state.delta.x) > std::abs(next_state.delta.y)) {
if (next_state.delta.x > 0) {
next_state.direction = GestureDirection::Right;
return;
}
next_state.direction = GestureDirection::Left;
return;
}
if (next_state.delta.y > 0) {
next_state.direction = GestureDirection::Down;
return;
}
next_state.direction = GestureDirection::Up;
}
const Controller_Gesture::GestureState& Controller_Gesture::GetLastGestureEntry() const {
return shared_memory->gesture_lifo.ReadCurrentEntry().state;
}
Controller_Gesture::GestureProperties Controller_Gesture::GetGestureProperties() {
GestureProperties gesture;
std::array<Core::HID::TouchFinger, MAX_POINTS> active_fingers;
const auto end_iter = std::copy_if(fingers.begin(), fingers.end(), active_fingers.begin(),
[](const auto& finger) { return finger.pressed; });
gesture.active_points =
static_cast<std::size_t>(std::distance(active_fingers.begin(), end_iter));
for (size_t id = 0; id < gesture.active_points; ++id) {
const auto& [active_x, active_y] = active_fingers[id].position;
gesture.points[id] = {
.x = static_cast<s32>(active_x * Layout::ScreenUndocked::Width),
.y = static_cast<s32>(active_y * Layout::ScreenUndocked::Height),
};
// Hack: There is no touch in docked but games still allow it
if (Settings::IsDockedMode()) {
gesture.points[id] = {
.x = static_cast<s32>(active_x * Layout::ScreenDocked::Width),
.y = static_cast<s32>(active_y * Layout::ScreenDocked::Height),
};
}
gesture.mid_point.x += static_cast<s32>(gesture.points[id].x / gesture.active_points);
gesture.mid_point.y += static_cast<s32>(gesture.points[id].y / gesture.active_points);
}
for (size_t id = 0; id < gesture.active_points; ++id) {
const f32 distance = std::sqrt(Square(gesture.mid_point.x - gesture.points[id].x) +
Square(gesture.mid_point.y - gesture.points[id].y));
gesture.average_distance += distance / static_cast<f32>(gesture.active_points);
}
gesture.angle = std::atan2(static_cast<f32>(gesture.mid_point.y - gesture.points[0].y),
static_cast<f32>(gesture.mid_point.x - gesture.points[0].x));
gesture.detection_count = last_gesture.detection_count;
return gesture;
}
} // namespace Service::HID

View File

@@ -0,0 +1,156 @@
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <array>
#include "common/bit_field.h"
#include "common/common_types.h"
#include "common/point.h"
#include "core/hid/emulated_console.h"
#include "core/hle/service/hid/controllers/controller_base.h"
#include "core/hle/service/hid/ring_lifo.h"
namespace Service::HID {
class Controller_Gesture final : public ControllerBase {
public:
explicit Controller_Gesture(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_);
~Controller_Gesture() override;
// Called when the controller is initialized
void OnInit() override;
// When the controller is released
void OnRelease() override;
// When the controller is requesting an update for the shared memory
void OnUpdate(const Core::Timing::CoreTiming& core_timing) override;
private:
static constexpr size_t MAX_FINGERS = 16;
static constexpr size_t MAX_POINTS = 4;
// This is nn::hid::GestureType
enum class GestureType : u32 {
Idle, // Nothing touching the screen
Complete, // Set at the end of a touch event
Cancel, // Set when the number of fingers change
Touch, // A finger just touched the screen
Press, // Set if last type is touch and the finger hasn't moved
Tap, // Fast press then release
Pan, // All points moving together across the screen
Swipe, // Fast press movement and release of a single point
Pinch, // All points moving away/closer to the midpoint
Rotate, // All points rotating from the midpoint
};
// This is nn::hid::GestureDirection
enum class GestureDirection : u32 {
None,
Left,
Up,
Right,
Down,
};
// This is nn::hid::GestureAttribute
struct GestureAttribute {
union {
u32 raw{};
BitField<4, 1, u32> is_new_touch;
BitField<8, 1, u32> is_double_tap;
};
};
static_assert(sizeof(GestureAttribute) == 4, "GestureAttribute is an invalid size");
// This is nn::hid::GestureState
struct GestureState {
s64 sampling_number{};
s64 detection_count{};
GestureType type{GestureType::Idle};
GestureDirection direction{GestureDirection::None};
Common::Point<s32> pos{};
Common::Point<s32> delta{};
f32 vel_x{};
f32 vel_y{};
GestureAttribute attributes{};
f32 scale{};
f32 rotation_angle{};
s32 point_count{};
std::array<Common::Point<s32>, 4> points{};
};
static_assert(sizeof(GestureState) == 0x60, "GestureState is an invalid size");
struct GestureProperties {
std::array<Common::Point<s32>, MAX_POINTS> points{};
std::size_t active_points{};
Common::Point<s32> mid_point{};
s64 detection_count{};
u64 delta_time{};
f32 average_distance{};
f32 angle{};
};
struct GestureSharedMemory {
// This is nn::hid::detail::GestureLifo
Lifo<GestureState, hid_entry_count> gesture_lifo{};
static_assert(sizeof(gesture_lifo) == 0x708, "gesture_lifo is an invalid size");
INSERT_PADDING_WORDS(0x3E);
};
static_assert(sizeof(GestureSharedMemory) == 0x800, "GestureSharedMemory is an invalid size");
// Reads input from all available input engines
void ReadTouchInput();
// Returns true if gesture state needs to be updated
bool ShouldUpdateGesture(const GestureProperties& gesture, f32 time_difference);
// Updates the shared memory to the next state
void UpdateGestureSharedMemory(GestureProperties& gesture, f32 time_difference);
// Initializes new gesture
void NewGesture(GestureProperties& gesture, GestureType& type, GestureAttribute& attributes);
// Updates existing gesture state
void UpdateExistingGesture(GestureProperties& gesture, GestureType& type, f32 time_difference);
// Terminates exiting gesture
void EndGesture(GestureProperties& gesture, GestureProperties& last_gesture_props,
GestureType& type, GestureAttribute& attributes, f32 time_difference);
// Set current event to a tap event
void SetTapEvent(GestureProperties& gesture, GestureProperties& last_gesture_props,
GestureType& type, GestureAttribute& attributes);
// Calculates and set the extra parameters related to a pan event
void UpdatePanEvent(GestureProperties& gesture, GestureProperties& last_gesture_props,
GestureType& type, f32 time_difference);
// Terminates the pan event
void EndPanEvent(GestureProperties& gesture, GestureProperties& last_gesture_props,
GestureType& type, f32 time_difference);
// Set current event to a swipe event
void SetSwipeEvent(GestureProperties& gesture, GestureProperties& last_gesture_props,
GestureType& type);
// Retrieves the last gesture entry, as indicated by shared memory indices.
[[nodiscard]] const GestureState& GetLastGestureEntry() const;
// Returns the average distance, angle and middle point of the active fingers
GestureProperties GetGestureProperties();
GestureState next_state{};
GestureSharedMemory* shared_memory = nullptr;
Core::HID::EmulatedConsole* console = nullptr;
std::array<Core::HID::TouchFinger, MAX_POINTS> fingers{};
GestureProperties last_gesture{};
s64 last_update_timestamp{};
s64 last_tap_timestamp{};
f32 last_pan_time_difference{};
bool force_update{false};
bool enable_press_and_tap{false};
};
} // namespace Service::HID

View File

@@ -0,0 +1,52 @@
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <cstring>
#include "common/common_types.h"
#include "common/settings.h"
#include "core/core_timing.h"
#include "core/hid/emulated_devices.h"
#include "core/hid/hid_core.h"
#include "core/hle/service/hid/controllers/keyboard.h"
namespace Service::HID {
constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3800;
Controller_Keyboard::Controller_Keyboard(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_)
: ControllerBase{hid_core_} {
static_assert(SHARED_MEMORY_OFFSET + sizeof(KeyboardSharedMemory) < shared_memory_size,
"KeyboardSharedMemory is bigger than the shared memory");
shared_memory = std::construct_at(
reinterpret_cast<KeyboardSharedMemory*>(raw_shared_memory_ + SHARED_MEMORY_OFFSET));
emulated_devices = hid_core.GetEmulatedDevices();
}
Controller_Keyboard::~Controller_Keyboard() = default;
void Controller_Keyboard::OnInit() {}
void Controller_Keyboard::OnRelease() {}
void Controller_Keyboard::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
if (!IsControllerActivated()) {
shared_memory->keyboard_lifo.buffer_count = 0;
shared_memory->keyboard_lifo.buffer_tail = 0;
return;
}
const auto& last_entry = shared_memory->keyboard_lifo.ReadCurrentEntry().state;
next_state.sampling_number = last_entry.sampling_number + 1;
if (Settings::values.keyboard_enabled) {
const auto& keyboard_state = emulated_devices->GetKeyboard();
const auto& keyboard_modifier_state = emulated_devices->GetKeyboardModifier();
next_state.key = keyboard_state;
next_state.modifier = keyboard_modifier_state;
next_state.attribute.is_connected.Assign(1);
}
shared_memory->keyboard_lifo.WriteNextEntry(next_state);
}
} // namespace Service::HID

View File

@@ -0,0 +1,53 @@
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "common/common_types.h"
#include "core/hle/service/hid/controllers/controller_base.h"
#include "core/hle/service/hid/ring_lifo.h"
namespace Core::HID {
class EmulatedDevices;
struct KeyboardModifier;
struct KeyboardKey;
} // namespace Core::HID
namespace Service::HID {
class Controller_Keyboard final : public ControllerBase {
public:
explicit Controller_Keyboard(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_);
~Controller_Keyboard() override;
// Called when the controller is initialized
void OnInit() override;
// When the controller is released
void OnRelease() override;
// When the controller is requesting an update for the shared memory
void OnUpdate(const Core::Timing::CoreTiming& core_timing) override;
private:
// This is nn::hid::detail::KeyboardState
struct KeyboardState {
s64 sampling_number{};
Core::HID::KeyboardModifier modifier{};
Core::HID::KeyboardAttribute attribute{};
Core::HID::KeyboardKey key{};
};
static_assert(sizeof(KeyboardState) == 0x30, "KeyboardState is an invalid size");
struct KeyboardSharedMemory {
// This is nn::hid::detail::KeyboardLifo
Lifo<KeyboardState, hid_entry_count> keyboard_lifo{};
static_assert(sizeof(keyboard_lifo) == 0x3D8, "keyboard_lifo is an invalid size");
INSERT_PADDING_WORDS(0xA);
};
static_assert(sizeof(KeyboardSharedMemory) == 0x400, "KeyboardSharedMemory is an invalid size");
KeyboardState next_state{};
KeyboardSharedMemory* shared_memory = nullptr;
Core::HID::EmulatedDevices* emulated_devices = nullptr;
};
} // namespace Service::HID

View File

@@ -0,0 +1,60 @@
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <cstring>
#include "common/common_types.h"
#include "core/core_timing.h"
#include "core/frontend/emu_window.h"
#include "core/hid/emulated_devices.h"
#include "core/hid/hid_core.h"
#include "core/hle/service/hid/controllers/mouse.h"
namespace Service::HID {
constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3400;
Controller_Mouse::Controller_Mouse(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_)
: ControllerBase{hid_core_} {
static_assert(SHARED_MEMORY_OFFSET + sizeof(MouseSharedMemory) < shared_memory_size,
"MouseSharedMemory is bigger than the shared memory");
shared_memory = std::construct_at(
reinterpret_cast<MouseSharedMemory*>(raw_shared_memory_ + SHARED_MEMORY_OFFSET));
emulated_devices = hid_core.GetEmulatedDevices();
}
Controller_Mouse::~Controller_Mouse() = default;
void Controller_Mouse::OnInit() {}
void Controller_Mouse::OnRelease() {}
void Controller_Mouse::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
if (!IsControllerActivated()) {
shared_memory->mouse_lifo.buffer_count = 0;
shared_memory->mouse_lifo.buffer_tail = 0;
return;
}
next_state = {};
const auto& last_entry = shared_memory->mouse_lifo.ReadCurrentEntry().state;
next_state.sampling_number = last_entry.sampling_number + 1;
if (Settings::values.mouse_enabled) {
const auto& mouse_button_state = emulated_devices->GetMouseButtons();
const auto& mouse_position_state = emulated_devices->GetMousePosition();
const auto& mouse_wheel_state = emulated_devices->GetMouseWheel();
next_state.attribute.is_connected.Assign(1);
next_state.x = static_cast<s32>(mouse_position_state.x * Layout::ScreenUndocked::Width);
next_state.y = static_cast<s32>(mouse_position_state.y * Layout::ScreenUndocked::Height);
next_state.delta_x = next_state.x - last_entry.x;
next_state.delta_y = next_state.y - last_entry.y;
next_state.delta_wheel_x = mouse_wheel_state.x - last_mouse_wheel_state.x;
next_state.delta_wheel_y = mouse_wheel_state.y - last_mouse_wheel_state.y;
last_mouse_wheel_state = mouse_wheel_state;
next_state.button = mouse_button_state;
}
shared_memory->mouse_lifo.WriteNextEntry(next_state);
}
} // namespace Service::HID

View File

@@ -0,0 +1,45 @@
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "common/common_types.h"
#include "core/hle/service/hid/controllers/controller_base.h"
#include "core/hle/service/hid/ring_lifo.h"
namespace Core::HID {
class EmulatedDevices;
struct MouseState;
struct AnalogStickState;
} // namespace Core::HID
namespace Service::HID {
class Controller_Mouse final : public ControllerBase {
public:
explicit Controller_Mouse(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_);
~Controller_Mouse() override;
// Called when the controller is initialized
void OnInit() override;
// When the controller is released
void OnRelease() override;
// When the controller is requesting an update for the shared memory
void OnUpdate(const Core::Timing::CoreTiming& core_timing) override;
private:
struct MouseSharedMemory {
// This is nn::hid::detail::MouseLifo
Lifo<Core::HID::MouseState, hid_entry_count> mouse_lifo{};
static_assert(sizeof(mouse_lifo) == 0x350, "mouse_lifo is an invalid size");
INSERT_PADDING_WORDS(0x2C);
};
static_assert(sizeof(MouseSharedMemory) == 0x400, "MouseSharedMemory is an invalid size");
Core::HID::MouseState next_state{};
Core::HID::AnalogStickState last_mouse_wheel_state{};
MouseSharedMemory* shared_memory = nullptr;
Core::HID::EmulatedDevices* emulated_devices = nullptr;
};
} // namespace Service::HID

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,555 @@
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <array>
#include <atomic>
#include <mutex>
#include <span>
#include "common/bit_field.h"
#include "common/common_types.h"
#include "common/vector_math.h"
#include "core/hid/hid_types.h"
#include "core/hle/service/hid/controllers/controller_base.h"
#include "core/hle/service/hid/ring_lifo.h"
namespace Core::HID {
class EmulatedController;
enum class ControllerTriggerType;
} // namespace Core::HID
namespace Kernel {
class KEvent;
class KReadableEvent;
} // namespace Kernel
namespace Service::KernelHelpers {
class ServiceContext;
} // namespace Service::KernelHelpers
union Result;
namespace Service::HID {
class Controller_NPad final : public ControllerBase {
public:
explicit Controller_NPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_,
KernelHelpers::ServiceContext& service_context_);
~Controller_NPad() override;
// Called when the controller is initialized
void OnInit() override;
// When the controller is released
void OnRelease() override;
// When the controller is requesting an update for the shared memory
void OnUpdate(const Core::Timing::CoreTiming& core_timing) override;
// When the controller is requesting a motion update for the shared memory
void OnMotionUpdate(const Core::Timing::CoreTiming& core_timing) override;
// This is nn::hid::NpadJoyHoldType
enum class NpadJoyHoldType : u64 {
Vertical = 0,
Horizontal = 1,
};
// This is nn::hid::NpadJoyAssignmentMode
enum class NpadJoyAssignmentMode : u32 {
Dual = 0,
Single = 1,
};
// This is nn::hid::NpadJoyDeviceType
enum class NpadJoyDeviceType : s64 {
Left = 0,
Right = 1,
};
// This is nn::hid::NpadHandheldActivationMode
enum class NpadHandheldActivationMode : u64 {
Dual = 0,
Single = 1,
None = 2,
MaxActivationMode = 3,
};
// This is nn::hid::NpadCommunicationMode
enum class NpadCommunicationMode : u64 {
Mode_5ms = 0,
Mode_10ms = 1,
Mode_15ms = 2,
Default = 3,
};
void SetSupportedStyleSet(Core::HID::NpadStyleTag style_set);
Core::HID::NpadStyleTag GetSupportedStyleSet() const;
Result SetSupportedNpadIdTypes(std::span<const u8> data);
void GetSupportedNpadIdTypes(u32* data, std::size_t max_length);
std::size_t GetSupportedNpadIdTypesSize() const;
void SetHoldType(NpadJoyHoldType joy_hold_type);
NpadJoyHoldType GetHoldType() const;
void SetNpadHandheldActivationMode(NpadHandheldActivationMode activation_mode);
NpadHandheldActivationMode GetNpadHandheldActivationMode() const;
void SetNpadCommunicationMode(NpadCommunicationMode communication_mode_);
NpadCommunicationMode GetNpadCommunicationMode() const;
bool SetNpadMode(Core::HID::NpadIdType& new_npad_id, Core::HID::NpadIdType npad_id,
NpadJoyDeviceType npad_device_type, NpadJoyAssignmentMode assignment_mode);
bool VibrateControllerAtIndex(Core::HID::NpadIdType npad_id, std::size_t device_index,
const Core::HID::VibrationValue& vibration_value);
void VibrateController(const Core::HID::VibrationDeviceHandle& vibration_device_handle,
const Core::HID::VibrationValue& vibration_value);
void VibrateControllers(
std::span<const Core::HID::VibrationDeviceHandle> vibration_device_handles,
std::span<const Core::HID::VibrationValue> vibration_values);
Core::HID::VibrationValue GetLastVibration(
const Core::HID::VibrationDeviceHandle& vibration_device_handle) const;
void InitializeVibrationDevice(const Core::HID::VibrationDeviceHandle& vibration_device_handle);
void InitializeVibrationDeviceAtIndex(Core::HID::NpadIdType npad_id, std::size_t device_index);
void SetPermitVibrationSession(bool permit_vibration_session);
bool IsVibrationDeviceMounted(
const Core::HID::VibrationDeviceHandle& vibration_device_handle) const;
Kernel::KReadableEvent& GetStyleSetChangedEvent(Core::HID::NpadIdType npad_id);
void SignalStyleSetChangedEvent(Core::HID::NpadIdType npad_id) const;
// Adds a new controller at an index.
void AddNewControllerAt(Core::HID::NpadStyleIndex controller, Core::HID::NpadIdType npad_id);
// Adds a new controller at an index with connection status.
void UpdateControllerAt(Core::HID::NpadStyleIndex controller, Core::HID::NpadIdType npad_id,
bool connected);
Result DisconnectNpad(Core::HID::NpadIdType npad_id);
Result SetGyroscopeZeroDriftMode(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
Core::HID::GyroscopeZeroDriftMode drift_mode);
Result GetGyroscopeZeroDriftMode(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
Core::HID::GyroscopeZeroDriftMode& drift_mode) const;
Result IsSixAxisSensorAtRest(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
bool& is_at_rest) const;
Result IsFirmwareUpdateAvailableForSixAxisSensor(
const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool& is_firmware_available) const;
Result EnableSixAxisSensorUnalteredPassthrough(
const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool is_enabled);
Result IsSixAxisSensorUnalteredPassthroughEnabled(
const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool& is_enabled) const;
Result LoadSixAxisSensorCalibrationParameter(
const Core::HID::SixAxisSensorHandle& sixaxis_handle,
Core::HID::SixAxisSensorCalibrationParameter& calibration) const;
Result GetSixAxisSensorIcInformation(
const Core::HID::SixAxisSensorHandle& sixaxis_handle,
Core::HID::SixAxisSensorIcInformation& ic_information) const;
Result ResetIsSixAxisSensorDeviceNewlyAssigned(
const Core::HID::SixAxisSensorHandle& sixaxis_handle);
Result SetSixAxisEnabled(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
bool sixaxis_status);
Result IsSixAxisSensorFusionEnabled(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
bool& is_fusion_enabled) const;
Result SetSixAxisFusionEnabled(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
bool is_fusion_enabled);
Result SetSixAxisFusionParameters(
const Core::HID::SixAxisSensorHandle& sixaxis_handle,
Core::HID::SixAxisSensorFusionParameters sixaxis_fusion_parameters);
Result GetSixAxisFusionParameters(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
Core::HID::SixAxisSensorFusionParameters& parameters) const;
Result GetLedPattern(Core::HID::NpadIdType npad_id, Core::HID::LedPattern& pattern) const;
Result IsUnintendedHomeButtonInputProtectionEnabled(Core::HID::NpadIdType npad_id,
bool& is_enabled) const;
Result SetUnintendedHomeButtonInputProtectionEnabled(bool is_protection_enabled,
Core::HID::NpadIdType npad_id);
void SetAnalogStickUseCenterClamp(bool use_center_clamp);
void ClearAllConnectedControllers();
void DisconnectAllConnectedControllers();
void ConnectAllDisconnectedControllers();
void ClearAllControllers();
Result MergeSingleJoyAsDualJoy(Core::HID::NpadIdType npad_id_1,
Core::HID::NpadIdType npad_id_2);
void StartLRAssignmentMode();
void StopLRAssignmentMode();
Result SwapNpadAssignment(Core::HID::NpadIdType npad_id_1, Core::HID::NpadIdType npad_id_2);
// Logical OR for all buttons presses on all controllers
// Specifically for cheat engine and other features.
Core::HID::NpadButton GetAndResetPressState();
void ApplyNpadSystemCommonPolicy();
static bool IsNpadIdValid(Core::HID::NpadIdType npad_id);
static Result IsDeviceHandleValid(const Core::HID::VibrationDeviceHandle& device_handle);
static Result VerifyValidSixAxisSensorHandle(
const Core::HID::SixAxisSensorHandle& device_handle);
private:
static constexpr std::size_t NPAD_COUNT = 10;
// This is nn::hid::detail::ColorAttribute
enum class ColorAttribute : u32 {
Ok = 0,
ReadError = 1,
NoController = 2,
};
static_assert(sizeof(ColorAttribute) == 4, "ColorAttribute is an invalid size");
// This is nn::hid::detail::NpadFullKeyColorState
struct NpadFullKeyColorState {
ColorAttribute attribute{ColorAttribute::NoController};
Core::HID::NpadControllerColor fullkey{};
};
static_assert(sizeof(NpadFullKeyColorState) == 0xC, "NpadFullKeyColorState is an invalid size");
// This is nn::hid::detail::NpadJoyColorState
struct NpadJoyColorState {
ColorAttribute attribute{ColorAttribute::NoController};
Core::HID::NpadControllerColor left{};
Core::HID::NpadControllerColor right{};
};
static_assert(sizeof(NpadJoyColorState) == 0x14, "NpadJoyColorState is an invalid size");
// This is nn::hid::NpadAttribute
struct NpadAttribute {
union {
u32 raw{};
BitField<0, 1, u32> is_connected;
BitField<1, 1, u32> is_wired;
BitField<2, 1, u32> is_left_connected;
BitField<3, 1, u32> is_left_wired;
BitField<4, 1, u32> is_right_connected;
BitField<5, 1, u32> is_right_wired;
};
};
static_assert(sizeof(NpadAttribute) == 4, "NpadAttribute is an invalid size");
// This is nn::hid::NpadFullKeyState
// This is nn::hid::NpadHandheldState
// This is nn::hid::NpadJoyDualState
// This is nn::hid::NpadJoyLeftState
// This is nn::hid::NpadJoyRightState
// This is nn::hid::NpadPalmaState
// This is nn::hid::NpadSystemExtState
struct NPadGenericState {
s64_le sampling_number{};
Core::HID::NpadButtonState npad_buttons{};
Core::HID::AnalogStickState l_stick{};
Core::HID::AnalogStickState r_stick{};
NpadAttribute connection_status{};
INSERT_PADDING_BYTES(4); // Reserved
};
static_assert(sizeof(NPadGenericState) == 0x28, "NPadGenericState is an invalid size");
// This is nn::hid::SixAxisSensorAttribute
struct SixAxisSensorAttribute {
union {
u32 raw{};
BitField<0, 1, u32> is_connected;
BitField<1, 1, u32> is_interpolated;
};
};
static_assert(sizeof(SixAxisSensorAttribute) == 4, "SixAxisSensorAttribute is an invalid size");
// This is nn::hid::SixAxisSensorState
struct SixAxisSensorState {
s64 delta_time{};
s64 sampling_number{};
Common::Vec3f accel{};
Common::Vec3f gyro{};
Common::Vec3f rotation{};
std::array<Common::Vec3f, 3> orientation{};
SixAxisSensorAttribute attribute{};
INSERT_PADDING_BYTES(4); // Reserved
};
static_assert(sizeof(SixAxisSensorState) == 0x60, "SixAxisSensorState is an invalid size");
// This is nn::hid::server::NpadGcTriggerState
struct NpadGcTriggerState {
s64 sampling_number{};
s32 l_analog{};
s32 r_analog{};
};
static_assert(sizeof(NpadGcTriggerState) == 0x10, "NpadGcTriggerState is an invalid size");
// This is nn::hid::NpadSystemProperties
struct NPadSystemProperties {
union {
s64 raw{};
BitField<0, 1, s64> is_charging_joy_dual;
BitField<1, 1, s64> is_charging_joy_left;
BitField<2, 1, s64> is_charging_joy_right;
BitField<3, 1, s64> is_powered_joy_dual;
BitField<4, 1, s64> is_powered_joy_left;
BitField<5, 1, s64> is_powered_joy_right;
BitField<9, 1, s64> is_system_unsupported_button;
BitField<10, 1, s64> is_system_ext_unsupported_button;
BitField<11, 1, s64> is_vertical;
BitField<12, 1, s64> is_horizontal;
BitField<13, 1, s64> use_plus;
BitField<14, 1, s64> use_minus;
BitField<15, 1, s64> use_directional_buttons;
};
};
static_assert(sizeof(NPadSystemProperties) == 0x8, "NPadSystemProperties is an invalid size");
// This is nn::hid::NpadSystemButtonProperties
struct NpadSystemButtonProperties {
union {
s32 raw{};
BitField<0, 1, s32> is_home_button_protection_enabled;
};
};
static_assert(sizeof(NpadSystemButtonProperties) == 0x4,
"NPadButtonProperties is an invalid size");
// This is nn::hid::system::DeviceType
struct DeviceType {
union {
u32 raw{};
BitField<0, 1, s32> fullkey;
BitField<1, 1, s32> debug_pad;
BitField<2, 1, s32> handheld_left;
BitField<3, 1, s32> handheld_right;
BitField<4, 1, s32> joycon_left;
BitField<5, 1, s32> joycon_right;
BitField<6, 1, s32> palma;
BitField<7, 1, s32> lark_hvc_left;
BitField<8, 1, s32> lark_hvc_right;
BitField<9, 1, s32> lark_nes_left;
BitField<10, 1, s32> lark_nes_right;
BitField<11, 1, s32> handheld_lark_hvc_left;
BitField<12, 1, s32> handheld_lark_hvc_right;
BitField<13, 1, s32> handheld_lark_nes_left;
BitField<14, 1, s32> handheld_lark_nes_right;
BitField<15, 1, s32> lucia;
BitField<16, 1, s32> lagon;
BitField<17, 1, s32> lager;
BitField<31, 1, s32> system;
};
};
// This is nn::hid::detail::NfcXcdDeviceHandleStateImpl
struct NfcXcdDeviceHandleStateImpl {
u64 handle{};
bool is_available{};
bool is_activated{};
INSERT_PADDING_BYTES(0x6); // Reserved
u64 sampling_number{};
};
static_assert(sizeof(NfcXcdDeviceHandleStateImpl) == 0x18,
"NfcXcdDeviceHandleStateImpl is an invalid size");
// This is nn::hid::system::AppletFooterUiAttributesSet
struct AppletFooterUiAttributes {
INSERT_PADDING_BYTES(0x4);
};
// This is nn::hid::system::AppletFooterUiType
enum class AppletFooterUiType : u8 {
None = 0,
HandheldNone = 1,
HandheldJoyConLeftOnly = 2,
HandheldJoyConRightOnly = 3,
HandheldJoyConLeftJoyConRight = 4,
JoyDual = 5,
JoyDualLeftOnly = 6,
JoyDualRightOnly = 7,
JoyLeftHorizontal = 8,
JoyLeftVertical = 9,
JoyRightHorizontal = 10,
JoyRightVertical = 11,
SwitchProController = 12,
CompatibleProController = 13,
CompatibleJoyCon = 14,
LarkHvc1 = 15,
LarkHvc2 = 16,
LarkNesLeft = 17,
LarkNesRight = 18,
Lucia = 19,
Verification = 20,
Lagon = 21,
};
// This is nn::hid::NpadLarkType
enum class NpadLarkType : u32 {
Invalid,
H1,
H2,
NL,
NR,
};
// This is nn::hid::NpadLuciaType
enum class NpadLuciaType : u32 {
Invalid,
J,
E,
U,
};
// This is nn::hid::NpadLagonType
enum class NpadLagonType : u32 {
Invalid,
};
// This is nn::hid::NpadLagerType
enum class NpadLagerType : u32 {
Invalid,
J,
E,
U,
};
// This is nn::hid::detail::NpadInternalState
struct NpadInternalState {
Core::HID::NpadStyleTag style_tag{Core::HID::NpadStyleSet::None};
NpadJoyAssignmentMode assignment_mode{NpadJoyAssignmentMode::Dual};
NpadFullKeyColorState fullkey_color{};
NpadJoyColorState joycon_color{};
Lifo<NPadGenericState, hid_entry_count> fullkey_lifo{};
Lifo<NPadGenericState, hid_entry_count> handheld_lifo{};
Lifo<NPadGenericState, hid_entry_count> joy_dual_lifo{};
Lifo<NPadGenericState, hid_entry_count> joy_left_lifo{};
Lifo<NPadGenericState, hid_entry_count> joy_right_lifo{};
Lifo<NPadGenericState, hid_entry_count> palma_lifo{};
Lifo<NPadGenericState, hid_entry_count> system_ext_lifo{};
Lifo<SixAxisSensorState, hid_entry_count> sixaxis_fullkey_lifo{};
Lifo<SixAxisSensorState, hid_entry_count> sixaxis_handheld_lifo{};
Lifo<SixAxisSensorState, hid_entry_count> sixaxis_dual_left_lifo{};
Lifo<SixAxisSensorState, hid_entry_count> sixaxis_dual_right_lifo{};
Lifo<SixAxisSensorState, hid_entry_count> sixaxis_left_lifo{};
Lifo<SixAxisSensorState, hid_entry_count> sixaxis_right_lifo{};
DeviceType device_type{};
INSERT_PADDING_BYTES(0x4); // Reserved
NPadSystemProperties system_properties{};
NpadSystemButtonProperties button_properties{};
Core::HID::NpadBatteryLevel battery_level_dual{};
Core::HID::NpadBatteryLevel battery_level_left{};
Core::HID::NpadBatteryLevel battery_level_right{};
AppletFooterUiAttributes applet_footer_attributes{};
AppletFooterUiType applet_footer_type{AppletFooterUiType::None};
INSERT_PADDING_BYTES(0x5B); // Reserved
INSERT_PADDING_BYTES(0x20); // Unknown
Lifo<NpadGcTriggerState, hid_entry_count> gc_trigger_lifo{};
NpadLarkType lark_type_l_and_main{};
NpadLarkType lark_type_r{};
NpadLuciaType lucia_type{};
NpadLagonType lagon_type{};
NpadLagerType lager_type{};
Core::HID::SixAxisSensorProperties sixaxis_fullkey_properties;
Core::HID::SixAxisSensorProperties sixaxis_handheld_properties;
Core::HID::SixAxisSensorProperties sixaxis_dual_left_properties;
Core::HID::SixAxisSensorProperties sixaxis_dual_right_properties;
Core::HID::SixAxisSensorProperties sixaxis_left_properties;
Core::HID::SixAxisSensorProperties sixaxis_right_properties;
INSERT_PADDING_BYTES(0xc06); // Unknown
};
static_assert(sizeof(NpadInternalState) == 0x5000, "NpadInternalState is an invalid size");
struct VibrationData {
bool device_mounted{};
Core::HID::VibrationValue latest_vibration_value{};
std::chrono::steady_clock::time_point last_vibration_timepoint{};
};
struct SixaxisParameters {
bool is_fusion_enabled{true};
bool unaltered_passtrough{false};
Core::HID::SixAxisSensorFusionParameters fusion{};
Core::HID::SixAxisSensorCalibrationParameter calibration{};
Core::HID::SixAxisSensorIcInformation ic_information{};
Core::HID::GyroscopeZeroDriftMode gyroscope_zero_drift_mode{
Core::HID::GyroscopeZeroDriftMode::Standard};
};
struct NpadControllerData {
Kernel::KEvent* styleset_changed_event{};
NpadInternalState* shared_memory = nullptr;
Core::HID::EmulatedController* device = nullptr;
std::array<VibrationData, 2> vibration{};
bool unintended_home_button_input_protection{};
bool is_connected{};
// Dual joycons can have only one side connected
bool is_dual_left_connected{true};
bool is_dual_right_connected{true};
// Motion parameters
bool sixaxis_at_rest{true};
bool sixaxis_sensor_enabled{true};
SixaxisParameters sixaxis_fullkey{};
SixaxisParameters sixaxis_handheld{};
SixaxisParameters sixaxis_dual_left{};
SixaxisParameters sixaxis_dual_right{};
SixaxisParameters sixaxis_left{};
SixaxisParameters sixaxis_right{};
SixaxisParameters sixaxis_unknown{};
// Current pad state
NPadGenericState npad_pad_state{};
NPadGenericState npad_libnx_state{};
NpadGcTriggerState npad_trigger_state{};
SixAxisSensorState sixaxis_fullkey_state{};
SixAxisSensorState sixaxis_handheld_state{};
SixAxisSensorState sixaxis_dual_left_state{};
SixAxisSensorState sixaxis_dual_right_state{};
SixAxisSensorState sixaxis_left_lifo_state{};
SixAxisSensorState sixaxis_right_lifo_state{};
int callback_key{};
};
void ControllerUpdate(Core::HID::ControllerTriggerType type, std::size_t controller_idx);
void InitNewlyAddedController(Core::HID::NpadIdType npad_id);
bool IsControllerSupported(Core::HID::NpadStyleIndex controller) const;
void RequestPadStateUpdate(Core::HID::NpadIdType npad_id);
void WriteEmptyEntry(NpadInternalState* npad);
NpadControllerData& GetControllerFromHandle(
const Core::HID::SixAxisSensorHandle& device_handle);
const NpadControllerData& GetControllerFromHandle(
const Core::HID::SixAxisSensorHandle& device_handle) const;
NpadControllerData& GetControllerFromHandle(
const Core::HID::VibrationDeviceHandle& device_handle);
const NpadControllerData& GetControllerFromHandle(
const Core::HID::VibrationDeviceHandle& device_handle) const;
NpadControllerData& GetControllerFromNpadIdType(Core::HID::NpadIdType npad_id);
const NpadControllerData& GetControllerFromNpadIdType(Core::HID::NpadIdType npad_id) const;
Core::HID::SixAxisSensorProperties& GetSixaxisProperties(
const Core::HID::SixAxisSensorHandle& device_handle);
const Core::HID::SixAxisSensorProperties& GetSixaxisProperties(
const Core::HID::SixAxisSensorHandle& device_handle) const;
SixaxisParameters& GetSixaxisState(const Core::HID::SixAxisSensorHandle& device_handle);
const SixaxisParameters& GetSixaxisState(
const Core::HID::SixAxisSensorHandle& device_handle) const;
std::atomic<u64> press_state{};
std::array<NpadControllerData, NPAD_COUNT> controller_data{};
KernelHelpers::ServiceContext& service_context;
std::mutex mutex;
std::vector<Core::HID::NpadIdType> supported_npad_id_types{};
NpadJoyHoldType hold_type{NpadJoyHoldType::Vertical};
NpadHandheldActivationMode handheld_activation_mode{NpadHandheldActivationMode::Dual};
NpadCommunicationMode communication_mode{NpadCommunicationMode::Default};
bool permit_vibration_session_enabled{false};
bool analog_stick_use_center_clamp{false};
bool is_in_lr_assignment_mode{false};
bool is_controller_initialized{false};
};
} // namespace Service::HID

View File

@@ -0,0 +1,229 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/core_timing.h"
#include "core/hid/emulated_controller.h"
#include "core/hid/hid_core.h"
#include "core/hid/hid_types.h"
#include "core/hle/kernel/k_event.h"
#include "core/hle/kernel/k_readable_event.h"
#include "core/hle/service/hid/controllers/palma.h"
#include "core/hle/service/kernel_helpers.h"
namespace Service::HID {
Controller_Palma::Controller_Palma(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_,
KernelHelpers::ServiceContext& service_context_)
: ControllerBase{hid_core_}, service_context{service_context_} {
controller = hid_core.GetEmulatedController(Core::HID::NpadIdType::Other);
operation_complete_event = service_context.CreateEvent("hid:PalmaOperationCompleteEvent");
}
Controller_Palma::~Controller_Palma() = default;
void Controller_Palma::OnInit() {}
void Controller_Palma::OnRelease() {}
void Controller_Palma::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
if (!IsControllerActivated()) {
return;
}
}
Result Controller_Palma::GetPalmaConnectionHandle(Core::HID::NpadIdType npad_id,
PalmaConnectionHandle& handle) {
active_handle.npad_id = npad_id;
handle = active_handle;
return ResultSuccess;
}
Result Controller_Palma::InitializePalma(const PalmaConnectionHandle& handle) {
if (handle.npad_id != active_handle.npad_id) {
return InvalidPalmaHandle;
}
ActivateController();
return ResultSuccess;
}
Kernel::KReadableEvent& Controller_Palma::AcquirePalmaOperationCompleteEvent(
const PalmaConnectionHandle& handle) const {
if (handle.npad_id != active_handle.npad_id) {
LOG_ERROR(Service_HID, "Invalid npad id {}", handle.npad_id);
}
return operation_complete_event->GetReadableEvent();
}
Result Controller_Palma::GetPalmaOperationInfo(const PalmaConnectionHandle& handle,
PalmaOperationType& operation_type,
PalmaOperationData& data) const {
if (handle.npad_id != active_handle.npad_id) {
return InvalidPalmaHandle;
}
operation_type = operation.operation;
data = operation.data;
return ResultSuccess;
}
Result Controller_Palma::PlayPalmaActivity(const PalmaConnectionHandle& handle,
u64 palma_activity) {
if (handle.npad_id != active_handle.npad_id) {
return InvalidPalmaHandle;
}
operation.operation = PalmaOperationType::PlayActivity;
operation.result = PalmaResultSuccess;
operation.data = {};
operation_complete_event->Signal();
return ResultSuccess;
}
Result Controller_Palma::SetPalmaFrModeType(const PalmaConnectionHandle& handle,
PalmaFrModeType fr_mode_) {
if (handle.npad_id != active_handle.npad_id) {
return InvalidPalmaHandle;
}
fr_mode = fr_mode_;
return ResultSuccess;
}
Result Controller_Palma::ReadPalmaStep(const PalmaConnectionHandle& handle) {
if (handle.npad_id != active_handle.npad_id) {
return InvalidPalmaHandle;
}
operation.operation = PalmaOperationType::ReadStep;
operation.result = PalmaResultSuccess;
operation.data = {};
operation_complete_event->Signal();
return ResultSuccess;
}
Result Controller_Palma::EnablePalmaStep(const PalmaConnectionHandle& handle, bool is_enabled) {
if (handle.npad_id != active_handle.npad_id) {
return InvalidPalmaHandle;
}
return ResultSuccess;
}
Result Controller_Palma::ResetPalmaStep(const PalmaConnectionHandle& handle) {
if (handle.npad_id != active_handle.npad_id) {
return InvalidPalmaHandle;
}
return ResultSuccess;
}
void Controller_Palma::ReadPalmaApplicationSection() {}
void Controller_Palma::WritePalmaApplicationSection() {}
Result Controller_Palma::ReadPalmaUniqueCode(const PalmaConnectionHandle& handle) {
if (handle.npad_id != active_handle.npad_id) {
return InvalidPalmaHandle;
}
operation.operation = PalmaOperationType::ReadUniqueCode;
operation.result = PalmaResultSuccess;
operation.data = {};
operation_complete_event->Signal();
return ResultSuccess;
}
Result Controller_Palma::SetPalmaUniqueCodeInvalid(const PalmaConnectionHandle& handle) {
if (handle.npad_id != active_handle.npad_id) {
return InvalidPalmaHandle;
}
operation.operation = PalmaOperationType::SetUniqueCodeInvalid;
operation.result = PalmaResultSuccess;
operation.data = {};
operation_complete_event->Signal();
return ResultSuccess;
}
void Controller_Palma::WritePalmaActivityEntry() {}
Result Controller_Palma::WritePalmaRgbLedPatternEntry(const PalmaConnectionHandle& handle,
u64 unknown) {
if (handle.npad_id != active_handle.npad_id) {
return InvalidPalmaHandle;
}
operation.operation = PalmaOperationType::WriteRgbLedPatternEntry;
operation.result = PalmaResultSuccess;
operation.data = {};
operation_complete_event->Signal();
return ResultSuccess;
}
Result Controller_Palma::WritePalmaWaveEntry(const PalmaConnectionHandle& handle, PalmaWaveSet wave,
Common::ProcessAddress t_mem, u64 size) {
if (handle.npad_id != active_handle.npad_id) {
return InvalidPalmaHandle;
}
operation.operation = PalmaOperationType::WriteWaveEntry;
operation.result = PalmaResultSuccess;
operation.data = {};
operation_complete_event->Signal();
return ResultSuccess;
}
Result Controller_Palma::SetPalmaDataBaseIdentificationVersion(const PalmaConnectionHandle& handle,
s32 database_id_version_) {
if (handle.npad_id != active_handle.npad_id) {
return InvalidPalmaHandle;
}
database_id_version = database_id_version_;
operation.operation = PalmaOperationType::ReadDataBaseIdentificationVersion;
operation.result = PalmaResultSuccess;
operation.data[0] = {};
operation_complete_event->Signal();
return ResultSuccess;
}
Result Controller_Palma::GetPalmaDataBaseIdentificationVersion(
const PalmaConnectionHandle& handle) {
if (handle.npad_id != active_handle.npad_id) {
return InvalidPalmaHandle;
}
operation.operation = PalmaOperationType::ReadDataBaseIdentificationVersion;
operation.result = PalmaResultSuccess;
operation.data = {};
operation.data[0] = static_cast<u8>(database_id_version);
operation_complete_event->Signal();
return ResultSuccess;
}
void Controller_Palma::SuspendPalmaFeature() {}
Result Controller_Palma::GetPalmaOperationResult(const PalmaConnectionHandle& handle) const {
if (handle.npad_id != active_handle.npad_id) {
return InvalidPalmaHandle;
}
return operation.result;
}
void Controller_Palma::ReadPalmaPlayLog() {}
void Controller_Palma::ResetPalmaPlayLog() {}
void Controller_Palma::SetIsPalmaAllConnectable(bool is_all_connectable) {
// If true controllers are able to be paired
is_connectable = is_all_connectable;
}
void Controller_Palma::SetIsPalmaPairedConnectable() {}
Result Controller_Palma::PairPalma(const PalmaConnectionHandle& handle) {
if (handle.npad_id != active_handle.npad_id) {
return InvalidPalmaHandle;
}
// TODO: Do something
return ResultSuccess;
}
void Controller_Palma::SetPalmaBoostMode(bool boost_mode) {}
void Controller_Palma::CancelWritePalmaWaveEntry() {}
void Controller_Palma::EnablePalmaBoostMode() {}
void Controller_Palma::GetPalmaBluetoothAddress() {}
void Controller_Palma::SetDisallowedPalmaConnection() {}
} // namespace Service::HID

View File

@@ -0,0 +1,163 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <array>
#include "common/common_funcs.h"
#include "common/typed_address.h"
#include "core/hle/service/hid/controllers/controller_base.h"
#include "core/hle/service/hid/errors.h"
namespace Kernel {
class KEvent;
class KReadableEvent;
} // namespace Kernel
namespace Service::KernelHelpers {
class ServiceContext;
}
namespace Core::HID {
class EmulatedController;
} // namespace Core::HID
namespace Service::HID {
class Controller_Palma final : public ControllerBase {
public:
using PalmaOperationData = std::array<u8, 0x140>;
// This is nn::hid::PalmaOperationType
enum class PalmaOperationType {
PlayActivity,
SetFrModeType,
ReadStep,
EnableStep,
ResetStep,
ReadApplicationSection,
WriteApplicationSection,
ReadUniqueCode,
SetUniqueCodeInvalid,
WriteActivityEntry,
WriteRgbLedPatternEntry,
WriteWaveEntry,
ReadDataBaseIdentificationVersion,
WriteDataBaseIdentificationVersion,
SuspendFeature,
ReadPlayLog,
ResetPlayLog,
};
// This is nn::hid::PalmaWaveSet
enum class PalmaWaveSet : u64 {
Small,
Medium,
Large,
};
// This is nn::hid::PalmaFrModeType
enum class PalmaFrModeType : u64 {
Off,
B01,
B02,
B03,
Downloaded,
};
// This is nn::hid::PalmaFeature
enum class PalmaFeature : u64 {
FrMode,
RumbleFeedback,
Step,
MuteSwitch,
};
// This is nn::hid::PalmaOperationInfo
struct PalmaOperationInfo {
PalmaOperationType operation{};
Result result{PalmaResultSuccess};
PalmaOperationData data{};
};
static_assert(sizeof(PalmaOperationInfo) == 0x148, "PalmaOperationInfo is an invalid size");
// This is nn::hid::PalmaActivityEntry
struct PalmaActivityEntry {
u32 rgb_led_pattern_index;
INSERT_PADDING_BYTES(2);
PalmaWaveSet wave_set;
u32 wave_index;
INSERT_PADDING_BYTES(12);
};
static_assert(sizeof(PalmaActivityEntry) == 0x20, "PalmaActivityEntry is an invalid size");
struct PalmaConnectionHandle {
Core::HID::NpadIdType npad_id;
INSERT_PADDING_BYTES(4); // Unknown
};
static_assert(sizeof(PalmaConnectionHandle) == 0x8,
"PalmaConnectionHandle has incorrect size.");
explicit Controller_Palma(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_,
KernelHelpers::ServiceContext& service_context_);
~Controller_Palma() override;
// Called when the controller is initialized
void OnInit() override;
// When the controller is released
void OnRelease() override;
// When the controller is requesting an update for the shared memory
void OnUpdate(const Core::Timing::CoreTiming& core_timing) override;
Result GetPalmaConnectionHandle(Core::HID::NpadIdType npad_id, PalmaConnectionHandle& handle);
Result InitializePalma(const PalmaConnectionHandle& handle);
Kernel::KReadableEvent& AcquirePalmaOperationCompleteEvent(
const PalmaConnectionHandle& handle) const;
Result GetPalmaOperationInfo(const PalmaConnectionHandle& handle,
PalmaOperationType& operation_type,
PalmaOperationData& data) const;
Result PlayPalmaActivity(const PalmaConnectionHandle& handle, u64 palma_activity);
Result SetPalmaFrModeType(const PalmaConnectionHandle& handle, PalmaFrModeType fr_mode_);
Result ReadPalmaStep(const PalmaConnectionHandle& handle);
Result EnablePalmaStep(const PalmaConnectionHandle& handle, bool is_enabled);
Result ResetPalmaStep(const PalmaConnectionHandle& handle);
Result ReadPalmaUniqueCode(const PalmaConnectionHandle& handle);
Result SetPalmaUniqueCodeInvalid(const PalmaConnectionHandle& handle);
Result WritePalmaRgbLedPatternEntry(const PalmaConnectionHandle& handle, u64 unknown);
Result WritePalmaWaveEntry(const PalmaConnectionHandle& handle, PalmaWaveSet wave,
Common::ProcessAddress t_mem, u64 size);
Result SetPalmaDataBaseIdentificationVersion(const PalmaConnectionHandle& handle,
s32 database_id_version_);
Result GetPalmaDataBaseIdentificationVersion(const PalmaConnectionHandle& handle);
Result GetPalmaOperationResult(const PalmaConnectionHandle& handle) const;
void SetIsPalmaAllConnectable(bool is_all_connectable);
Result PairPalma(const PalmaConnectionHandle& handle);
void SetPalmaBoostMode(bool boost_mode);
private:
void ReadPalmaApplicationSection();
void WritePalmaApplicationSection();
void WritePalmaActivityEntry();
void SuspendPalmaFeature();
void ReadPalmaPlayLog();
void ResetPalmaPlayLog();
void SetIsPalmaPairedConnectable();
void CancelWritePalmaWaveEntry();
void EnablePalmaBoostMode();
void GetPalmaBluetoothAddress();
void SetDisallowedPalmaConnection();
bool is_connectable{};
s32 database_id_version{};
PalmaOperationInfo operation{};
PalmaFrModeType fr_mode{};
PalmaConnectionHandle active_handle{};
Core::HID::EmulatedController* controller;
Kernel::KEvent* operation_complete_event;
KernelHelpers::ServiceContext& service_context;
};
} // namespace Service::HID

View File

@@ -0,0 +1,42 @@
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <cstring>
#include "common/common_types.h"
#include "core/core_timing.h"
#include "core/hid/hid_core.h"
#include "core/hle/service/hid/controllers/stubbed.h"
namespace Service::HID {
Controller_Stubbed::Controller_Stubbed(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_)
: ControllerBase{hid_core_} {
raw_shared_memory = raw_shared_memory_;
}
Controller_Stubbed::~Controller_Stubbed() = default;
void Controller_Stubbed::OnInit() {}
void Controller_Stubbed::OnRelease() {}
void Controller_Stubbed::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
if (!smart_update) {
return;
}
CommonHeader header{};
header.timestamp = core_timing.GetGlobalTimeNs().count();
header.total_entry_count = 17;
header.entry_count = 0;
header.last_entry_index = 0;
std::memcpy(raw_shared_memory + common_offset, &header, sizeof(CommonHeader));
}
void Controller_Stubbed::SetCommonHeaderOffset(std::size_t off) {
common_offset = off;
smart_update = true;
}
} // namespace Service::HID

View File

@@ -0,0 +1,39 @@
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "common/common_types.h"
#include "core/hle/service/hid/controllers/controller_base.h"
namespace Service::HID {
class Controller_Stubbed final : public ControllerBase {
public:
explicit Controller_Stubbed(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_);
~Controller_Stubbed() override;
// Called when the controller is initialized
void OnInit() override;
// When the controller is released
void OnRelease() override;
// When the controller is requesting an update for the shared memory
void OnUpdate(const Core::Timing::CoreTiming& core_timing) override;
void SetCommonHeaderOffset(std::size_t off);
private:
struct CommonHeader {
s64 timestamp{};
s64 total_entry_count{};
s64 last_entry_index{};
s64 entry_count{};
};
static_assert(sizeof(CommonHeader) == 0x20, "CommonHeader is an invalid size");
u8* raw_shared_memory = nullptr;
bool smart_update{};
std::size_t common_offset{};
};
} // namespace Service::HID

View File

@@ -0,0 +1,124 @@
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm>
#include <cstring>
#include "common/common_types.h"
#include "common/settings.h"
#include "core/core.h"
#include "core/core_timing.h"
#include "core/frontend/emu_window.h"
#include "core/hid/emulated_console.h"
#include "core/hid/hid_core.h"
#include "core/hle/service/hid/controllers/touchscreen.h"
namespace Service::HID {
constexpr std::size_t SHARED_MEMORY_OFFSET = 0x400;
Controller_Touchscreen::Controller_Touchscreen(Core::HID::HIDCore& hid_core_,
u8* raw_shared_memory_)
: ControllerBase{hid_core_} {
static_assert(SHARED_MEMORY_OFFSET + sizeof(TouchSharedMemory) < shared_memory_size,
"TouchSharedMemory is bigger than the shared memory");
shared_memory = std::construct_at(
reinterpret_cast<TouchSharedMemory*>(raw_shared_memory_ + SHARED_MEMORY_OFFSET));
console = hid_core.GetEmulatedConsole();
}
Controller_Touchscreen::~Controller_Touchscreen() = default;
void Controller_Touchscreen::OnInit() {}
void Controller_Touchscreen::OnRelease() {}
void Controller_Touchscreen::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
shared_memory->touch_screen_lifo.timestamp = core_timing.GetGlobalTimeNs().count();
if (!IsControllerActivated()) {
shared_memory->touch_screen_lifo.buffer_count = 0;
shared_memory->touch_screen_lifo.buffer_tail = 0;
return;
}
const auto touch_status = console->GetTouch();
for (std::size_t id = 0; id < MAX_FINGERS; id++) {
const auto& current_touch = touch_status[id];
auto& finger = fingers[id];
finger.id = current_touch.id;
if (finger.attribute.start_touch) {
finger.attribute.raw = 0;
continue;
}
if (finger.attribute.end_touch) {
finger.attribute.raw = 0;
finger.pressed = false;
continue;
}
if (!finger.pressed && current_touch.pressed) {
// Ignore all touch fingers if disabled
if (!Settings::values.touchscreen.enabled) {
continue;
}
finger.attribute.start_touch.Assign(1);
finger.pressed = true;
finger.position = current_touch.position;
continue;
}
if (finger.pressed && !current_touch.pressed) {
finger.attribute.raw = 0;
finger.attribute.end_touch.Assign(1);
continue;
}
// Only update position if touch is not on a special frame
finger.position = current_touch.position;
}
std::array<Core::HID::TouchFinger, MAX_FINGERS> active_fingers;
const auto end_iter = std::copy_if(fingers.begin(), fingers.end(), active_fingers.begin(),
[](const auto& finger) { return finger.pressed; });
const auto active_fingers_count =
static_cast<std::size_t>(std::distance(active_fingers.begin(), end_iter));
const u64 timestamp = static_cast<u64>(core_timing.GetGlobalTimeNs().count());
const auto& last_entry = shared_memory->touch_screen_lifo.ReadCurrentEntry().state;
next_state.sampling_number = last_entry.sampling_number + 1;
next_state.entry_count = static_cast<s32>(active_fingers_count);
for (std::size_t id = 0; id < MAX_FINGERS; ++id) {
auto& touch_entry = next_state.states[id];
if (id < active_fingers_count) {
const auto& [active_x, active_y] = active_fingers[id].position;
touch_entry.position = {
.x = static_cast<u16>(active_x * Layout::ScreenUndocked::Width),
.y = static_cast<u16>(active_y * Layout::ScreenUndocked::Height),
};
touch_entry.diameter_x = Settings::values.touchscreen.diameter_x;
touch_entry.diameter_y = Settings::values.touchscreen.diameter_y;
touch_entry.rotation_angle = Settings::values.touchscreen.rotation_angle;
touch_entry.delta_time = timestamp - active_fingers[id].last_touch;
fingers[active_fingers[id].id].last_touch = timestamp;
touch_entry.finger = active_fingers[id].id;
touch_entry.attribute.raw = active_fingers[id].attribute.raw;
} else {
// Clear touch entry
touch_entry.attribute.raw = 0;
touch_entry.position = {};
touch_entry.diameter_x = 0;
touch_entry.diameter_y = 0;
touch_entry.rotation_angle = 0;
touch_entry.delta_time = 0;
touch_entry.finger = 0;
}
}
shared_memory->touch_screen_lifo.WriteNextEntry(next_state);
}
} // namespace Service::HID

View File

@@ -0,0 +1,57 @@
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "core/hid/hid_types.h"
#include "core/hle/service/hid/controllers/controller_base.h"
#include "core/hle/service/hid/ring_lifo.h"
namespace Core::HID {
class EmulatedConsole;
} // namespace Core::HID
namespace Service::HID {
class Controller_Touchscreen final : public ControllerBase {
public:
explicit Controller_Touchscreen(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_);
~Controller_Touchscreen() override;
// Called when the controller is initialized
void OnInit() override;
// When the controller is released
void OnRelease() override;
// When the controller is requesting an update for the shared memory
void OnUpdate(const Core::Timing::CoreTiming& core_timing) override;
private:
static constexpr std::size_t MAX_FINGERS = 16;
// This is nn::hid::TouchScreenState
struct TouchScreenState {
s64 sampling_number{};
s32 entry_count{};
INSERT_PADDING_BYTES(4); // Reserved
std::array<Core::HID::TouchState, MAX_FINGERS> states{};
};
static_assert(sizeof(TouchScreenState) == 0x290, "TouchScreenState is an invalid size");
struct TouchSharedMemory {
// This is nn::hid::detail::TouchScreenLifo
Lifo<TouchScreenState, hid_entry_count> touch_screen_lifo{};
static_assert(sizeof(touch_screen_lifo) == 0x2C38, "touch_screen_lifo is an invalid size");
INSERT_PADDING_WORDS(0xF2);
};
static_assert(sizeof(TouchSharedMemory) == 0x3000, "TouchSharedMemory is an invalid size");
TouchScreenState next_state{};
TouchSharedMemory* shared_memory = nullptr;
Core::HID::EmulatedConsole* console = nullptr;
std::array<Core::HID::TouchFinger, MAX_FINGERS> fingers{};
};
} // namespace Service::HID

View File

@@ -0,0 +1,40 @@
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <cstring>
#include "common/common_types.h"
#include "core/core_timing.h"
#include "core/hid/hid_core.h"
#include "core/hle/service/hid/controllers/xpad.h"
namespace Service::HID {
constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3C00;
Controller_XPad::Controller_XPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_)
: ControllerBase{hid_core_} {
static_assert(SHARED_MEMORY_OFFSET + sizeof(XpadSharedMemory) < shared_memory_size,
"XpadSharedMemory is bigger than the shared memory");
shared_memory = std::construct_at(
reinterpret_cast<XpadSharedMemory*>(raw_shared_memory_ + SHARED_MEMORY_OFFSET));
}
Controller_XPad::~Controller_XPad() = default;
void Controller_XPad::OnInit() {}
void Controller_XPad::OnRelease() {}
void Controller_XPad::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
if (!IsControllerActivated()) {
shared_memory->basic_xpad_lifo.buffer_count = 0;
shared_memory->basic_xpad_lifo.buffer_tail = 0;
return;
}
const auto& last_entry = shared_memory->basic_xpad_lifo.ReadCurrentEntry().state;
next_state.sampling_number = last_entry.sampling_number + 1;
// TODO(ogniK): Update xpad states
shared_memory->basic_xpad_lifo.WriteNextEntry(next_state);
}
} // namespace Service::HID

View File

@@ -0,0 +1,112 @@
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "common/bit_field.h"
#include "common/common_types.h"
#include "core/hid/hid_types.h"
#include "core/hle/service/hid/controllers/controller_base.h"
#include "core/hle/service/hid/ring_lifo.h"
namespace Service::HID {
class Controller_XPad final : public ControllerBase {
public:
explicit Controller_XPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_);
~Controller_XPad() override;
// Called when the controller is initialized
void OnInit() override;
// When the controller is released
void OnRelease() override;
// When the controller is requesting an update for the shared memory
void OnUpdate(const Core::Timing::CoreTiming& core_timing) override;
private:
// This is nn::hid::BasicXpadAttributeSet
struct BasicXpadAttributeSet {
union {
u32 raw{};
BitField<0, 1, u32> is_connected;
BitField<1, 1, u32> is_wired;
BitField<2, 1, u32> is_left_connected;
BitField<3, 1, u32> is_left_wired;
BitField<4, 1, u32> is_right_connected;
BitField<5, 1, u32> is_right_wired;
};
};
static_assert(sizeof(BasicXpadAttributeSet) == 4, "BasicXpadAttributeSet is an invalid size");
// This is nn::hid::BasicXpadButtonSet
struct BasicXpadButtonSet {
union {
u32 raw{};
// Button states
BitField<0, 1, u32> a;
BitField<1, 1, u32> b;
BitField<2, 1, u32> x;
BitField<3, 1, u32> y;
BitField<4, 1, u32> l_stick;
BitField<5, 1, u32> r_stick;
BitField<6, 1, u32> l;
BitField<7, 1, u32> r;
BitField<8, 1, u32> zl;
BitField<9, 1, u32> zr;
BitField<10, 1, u32> plus;
BitField<11, 1, u32> minus;
// D-Pad
BitField<12, 1, u32> d_left;
BitField<13, 1, u32> d_up;
BitField<14, 1, u32> d_right;
BitField<15, 1, u32> d_down;
// Left JoyStick
BitField<16, 1, u32> l_stick_left;
BitField<17, 1, u32> l_stick_up;
BitField<18, 1, u32> l_stick_right;
BitField<19, 1, u32> l_stick_down;
// Right JoyStick
BitField<20, 1, u32> r_stick_left;
BitField<21, 1, u32> r_stick_up;
BitField<22, 1, u32> r_stick_right;
BitField<23, 1, u32> r_stick_down;
// Not always active?
BitField<24, 1, u32> left_sl;
BitField<25, 1, u32> left_sr;
BitField<26, 1, u32> right_sl;
BitField<27, 1, u32> right_sr;
BitField<28, 1, u32> palma;
BitField<30, 1, u32> handheld_left_b;
};
};
static_assert(sizeof(BasicXpadButtonSet) == 4, "BasicXpadButtonSet is an invalid size");
// This is nn::hid::detail::BasicXpadState
struct BasicXpadState {
s64 sampling_number{};
BasicXpadAttributeSet attributes{};
BasicXpadButtonSet pad_states{};
Core::HID::AnalogStickState l_stick{};
Core::HID::AnalogStickState r_stick{};
};
static_assert(sizeof(BasicXpadState) == 0x20, "BasicXpadState is an invalid size");
struct XpadSharedMemory {
// This is nn::hid::detail::BasicXpadLifo
Lifo<BasicXpadState, hid_entry_count> basic_xpad_lifo{};
static_assert(sizeof(basic_xpad_lifo) == 0x2C8, "basic_xpad_lifo is an invalid size");
INSERT_PADDING_WORDS(0x4E);
};
static_assert(sizeof(XpadSharedMemory) == 0x400, "XpadSharedMemory is an invalid size");
BasicXpadState next_state{};
XpadSharedMemory* shared_memory = nullptr;
};
} // namespace Service::HID

View File

@@ -0,0 +1,31 @@
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "core/hle/result.h"
namespace Service::HID {
constexpr Result PalmaResultSuccess{ErrorModule::HID, 0};
constexpr Result NpadInvalidHandle{ErrorModule::HID, 100};
constexpr Result NpadDeviceIndexOutOfRange{ErrorModule::HID, 107};
constexpr Result VibrationInvalidStyleIndex{ErrorModule::HID, 122};
constexpr Result VibrationInvalidNpadId{ErrorModule::HID, 123};
constexpr Result VibrationDeviceIndexOutOfRange{ErrorModule::HID, 124};
constexpr Result InvalidSixAxisFusionRange{ErrorModule::HID, 423};
constexpr Result NpadIsDualJoycon{ErrorModule::HID, 601};
constexpr Result NpadIsSameType{ErrorModule::HID, 602};
constexpr Result InvalidNpadId{ErrorModule::HID, 709};
constexpr Result NpadNotConnected{ErrorModule::HID, 710};
constexpr Result InvalidArraySize{ErrorModule::HID, 715};
constexpr Result InvalidPalmaHandle{ErrorModule::HID, 3302};
} // namespace Service::HID
namespace Service::IRS {
constexpr Result InvalidProcessorState{ErrorModule::Irsensor, 78};
constexpr Result InvalidIrCameraHandle{ErrorModule::Irsensor, 204};
} // namespace Service::IRS

File diff suppressed because it is too large Load Diff

View File

@@ -3,14 +3,220 @@
#pragma once
#include <chrono>
#include "core/hle/service/hid/controllers/controller_base.h"
#include "core/hle/service/kernel_helpers.h"
#include "core/hle/service/service.h"
namespace Core {
class System;
namespace Core::Timing {
struct EventType;
}
namespace Service::SM {
class ServiceManager;
}
namespace Service::HID {
enum class HidController : std::size_t {
DebugPad,
Touchscreen,
Mouse,
Keyboard,
XPad,
HomeButton,
SleepButton,
CaptureButton,
InputDetector,
UniquePad,
NPad,
Gesture,
ConsoleSixAxisSensor,
DebugMouse,
Palma,
MaxControllers,
};
class IAppletResource final : public ServiceFramework<IAppletResource> {
public:
explicit IAppletResource(Core::System& system_,
KernelHelpers::ServiceContext& service_context_);
~IAppletResource() override;
void ActivateController(HidController controller);
void DeactivateController(HidController controller);
template <typename T>
T& GetController(HidController controller) {
return static_cast<T&>(*controllers[static_cast<size_t>(controller)]);
}
template <typename T>
const T& GetController(HidController controller) const {
return static_cast<T&>(*controllers[static_cast<size_t>(controller)]);
}
private:
template <typename T>
void MakeController(HidController controller, u8* shared_memory) {
if constexpr (std::is_constructible_v<T, Core::System&, u8*>) {
controllers[static_cast<std::size_t>(controller)] =
std::make_unique<T>(system, shared_memory);
} else {
controllers[static_cast<std::size_t>(controller)] =
std::make_unique<T>(system.HIDCore(), shared_memory);
}
}
template <typename T>
void MakeControllerWithServiceContext(HidController controller, u8* shared_memory) {
controllers[static_cast<std::size_t>(controller)] =
std::make_unique<T>(system.HIDCore(), shared_memory, service_context);
}
void GetSharedMemoryHandle(HLERequestContext& ctx);
void UpdateControllers(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
void UpdateNpad(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
void UpdateMouseKeyboard(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
void UpdateMotion(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
KernelHelpers::ServiceContext& service_context;
std::shared_ptr<Core::Timing::EventType> npad_update_event;
std::shared_ptr<Core::Timing::EventType> default_update_event;
std::shared_ptr<Core::Timing::EventType> mouse_keyboard_update_event;
std::shared_ptr<Core::Timing::EventType> motion_update_event;
std::array<std::unique_ptr<ControllerBase>, static_cast<size_t>(HidController::MaxControllers)>
controllers{};
};
class Hid final : public ServiceFramework<Hid> {
public:
explicit Hid(Core::System& system_, std::shared_ptr<IAppletResource> applet_resource_);
~Hid() override;
std::shared_ptr<IAppletResource> GetAppletResource();
private:
void CreateAppletResource(HLERequestContext& ctx);
void ActivateDebugPad(HLERequestContext& ctx);
void ActivateTouchScreen(HLERequestContext& ctx);
void ActivateMouse(HLERequestContext& ctx);
void ActivateKeyboard(HLERequestContext& ctx);
void SendKeyboardLockKeyEvent(HLERequestContext& ctx);
void ActivateXpad(HLERequestContext& ctx);
void GetXpadIDs(HLERequestContext& ctx);
void ActivateSixAxisSensor(HLERequestContext& ctx);
void DeactivateSixAxisSensor(HLERequestContext& ctx);
void StartSixAxisSensor(HLERequestContext& ctx);
void StopSixAxisSensor(HLERequestContext& ctx);
void IsSixAxisSensorFusionEnabled(HLERequestContext& ctx);
void EnableSixAxisSensorFusion(HLERequestContext& ctx);
void SetSixAxisSensorFusionParameters(HLERequestContext& ctx);
void GetSixAxisSensorFusionParameters(HLERequestContext& ctx);
void ResetSixAxisSensorFusionParameters(HLERequestContext& ctx);
void SetGyroscopeZeroDriftMode(HLERequestContext& ctx);
void GetGyroscopeZeroDriftMode(HLERequestContext& ctx);
void ResetGyroscopeZeroDriftMode(HLERequestContext& ctx);
void IsSixAxisSensorAtRest(HLERequestContext& ctx);
void IsFirmwareUpdateAvailableForSixAxisSensor(HLERequestContext& ctx);
void EnableSixAxisSensorUnalteredPassthrough(HLERequestContext& ctx);
void IsSixAxisSensorUnalteredPassthroughEnabled(HLERequestContext& ctx);
void LoadSixAxisSensorCalibrationParameter(HLERequestContext& ctx);
void GetSixAxisSensorIcInformation(HLERequestContext& ctx);
void ResetIsSixAxisSensorDeviceNewlyAssigned(HLERequestContext& ctx);
void ActivateGesture(HLERequestContext& ctx);
void SetSupportedNpadStyleSet(HLERequestContext& ctx);
void GetSupportedNpadStyleSet(HLERequestContext& ctx);
void SetSupportedNpadIdType(HLERequestContext& ctx);
void ActivateNpad(HLERequestContext& ctx);
void DeactivateNpad(HLERequestContext& ctx);
void AcquireNpadStyleSetUpdateEventHandle(HLERequestContext& ctx);
void DisconnectNpad(HLERequestContext& ctx);
void GetPlayerLedPattern(HLERequestContext& ctx);
void ActivateNpadWithRevision(HLERequestContext& ctx);
void SetNpadJoyHoldType(HLERequestContext& ctx);
void GetNpadJoyHoldType(HLERequestContext& ctx);
void SetNpadJoyAssignmentModeSingleByDefault(HLERequestContext& ctx);
void SetNpadJoyAssignmentModeSingle(HLERequestContext& ctx);
void SetNpadJoyAssignmentModeDual(HLERequestContext& ctx);
void MergeSingleJoyAsDualJoy(HLERequestContext& ctx);
void StartLrAssignmentMode(HLERequestContext& ctx);
void StopLrAssignmentMode(HLERequestContext& ctx);
void SetNpadHandheldActivationMode(HLERequestContext& ctx);
void GetNpadHandheldActivationMode(HLERequestContext& ctx);
void SwapNpadAssignment(HLERequestContext& ctx);
void IsUnintendedHomeButtonInputProtectionEnabled(HLERequestContext& ctx);
void EnableUnintendedHomeButtonInputProtection(HLERequestContext& ctx);
void SetNpadJoyAssignmentModeSingleWithDestination(HLERequestContext& ctx);
void SetNpadAnalogStickUseCenterClamp(HLERequestContext& ctx);
void SetNpadCaptureButtonAssignment(HLERequestContext& ctx);
void ClearNpadCaptureButtonAssignment(HLERequestContext& ctx);
void GetVibrationDeviceInfo(HLERequestContext& ctx);
void SendVibrationValue(HLERequestContext& ctx);
void GetActualVibrationValue(HLERequestContext& ctx);
void CreateActiveVibrationDeviceList(HLERequestContext& ctx);
void PermitVibration(HLERequestContext& ctx);
void IsVibrationPermitted(HLERequestContext& ctx);
void SendVibrationValues(HLERequestContext& ctx);
void SendVibrationGcErmCommand(HLERequestContext& ctx);
void GetActualVibrationGcErmCommand(HLERequestContext& ctx);
void BeginPermitVibrationSession(HLERequestContext& ctx);
void EndPermitVibrationSession(HLERequestContext& ctx);
void IsVibrationDeviceMounted(HLERequestContext& ctx);
void ActivateConsoleSixAxisSensor(HLERequestContext& ctx);
void StartConsoleSixAxisSensor(HLERequestContext& ctx);
void StopConsoleSixAxisSensor(HLERequestContext& ctx);
void ActivateSevenSixAxisSensor(HLERequestContext& ctx);
void StartSevenSixAxisSensor(HLERequestContext& ctx);
void StopSevenSixAxisSensor(HLERequestContext& ctx);
void InitializeSevenSixAxisSensor(HLERequestContext& ctx);
void FinalizeSevenSixAxisSensor(HLERequestContext& ctx);
void ResetSevenSixAxisSensorTimestamp(HLERequestContext& ctx);
void IsUsbFullKeyControllerEnabled(HLERequestContext& ctx);
void GetPalmaConnectionHandle(HLERequestContext& ctx);
void InitializePalma(HLERequestContext& ctx);
void AcquirePalmaOperationCompleteEvent(HLERequestContext& ctx);
void GetPalmaOperationInfo(HLERequestContext& ctx);
void PlayPalmaActivity(HLERequestContext& ctx);
void SetPalmaFrModeType(HLERequestContext& ctx);
void ReadPalmaStep(HLERequestContext& ctx);
void EnablePalmaStep(HLERequestContext& ctx);
void ResetPalmaStep(HLERequestContext& ctx);
void ReadPalmaApplicationSection(HLERequestContext& ctx);
void WritePalmaApplicationSection(HLERequestContext& ctx);
void ReadPalmaUniqueCode(HLERequestContext& ctx);
void SetPalmaUniqueCodeInvalid(HLERequestContext& ctx);
void WritePalmaActivityEntry(HLERequestContext& ctx);
void WritePalmaRgbLedPatternEntry(HLERequestContext& ctx);
void WritePalmaWaveEntry(HLERequestContext& ctx);
void SetPalmaDataBaseIdentificationVersion(HLERequestContext& ctx);
void GetPalmaDataBaseIdentificationVersion(HLERequestContext& ctx);
void SuspendPalmaFeature(HLERequestContext& ctx);
void GetPalmaOperationResult(HLERequestContext& ctx);
void ReadPalmaPlayLog(HLERequestContext& ctx);
void ResetPalmaPlayLog(HLERequestContext& ctx);
void SetIsPalmaAllConnectable(HLERequestContext& ctx);
void SetIsPalmaPairedConnectable(HLERequestContext& ctx);
void PairPalma(HLERequestContext& ctx);
void SetPalmaBoostMode(HLERequestContext& ctx);
void CancelWritePalmaWaveEntry(HLERequestContext& ctx);
void EnablePalmaBoostMode(HLERequestContext& ctx);
void GetPalmaBluetoothAddress(HLERequestContext& ctx);
void SetDisallowedPalmaConnection(HLERequestContext& ctx);
void SetNpadCommunicationMode(HLERequestContext& ctx);
void GetNpadCommunicationMode(HLERequestContext& ctx);
void SetTouchScreenConfiguration(HLERequestContext& ctx);
void IsFirmwareUpdateNeededForNotification(HLERequestContext& ctx);
std::shared_ptr<IAppletResource> applet_resource;
KernelHelpers::ServiceContext service_context;
};
void LoopProcess(Core::System& system);
} // namespace Service::HID

View File

@@ -1,650 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include "core/hle/service/hid//hid_result.h"
#include "core/hle/service/hid//hid_types.h"
#include "core/hle/service/hid/hid_debug_server.h"
#include "core/hle/service/hid/resource_manager.h"
#include "core/hle/service/hid/resource_manager/debug_pad.h"
#include "core/hle/service/hid/resource_manager/gesture.h"
#include "core/hle/service/hid/resource_manager/keyboard.h"
#include "core/hle/service/hid/resource_manager/mouse.h"
#include "core/hle/service/hid/resource_manager/npad_resource/npad.h"
#include "core/hle/service/hid/resource_manager/palma.h"
#include "core/hle/service/hid/resource_manager/sixaxis.h"
#include "core/hle/service/hid/resource_manager/touch_screen.h"
#include "core/hle/service/hid/hid_firmware_settings.h"
#include "core/hle/service/ipc_helpers.h"
namespace Service::HID {
IHidDebugServer::IHidDebugServer(Core::System& system_, std::shared_ptr<ResourceManager> resource,
std::shared_ptr<HidFirmwareSettings> settings)
: ServiceFramework{system_, "hid:dbg"}, resource_manager{resource},
firmware_settings{settings} {
// clang-format off
static const FunctionInfo functions[] = {
{0, &IHidDebugServer::DeactivateDebugPad, "DeactivateDebugPad"},
{1, &IHidDebugServer::SetDebugPadAutoPilotState, "SetDebugPadAutoPilotState"},
{2, &IHidDebugServer::UnsetDebugPadAutoPilotState, "UnsetDebugPadAutoPilotState"},
{10, &IHidDebugServer::DeactivateTouchScreen, "DeactivateTouchScreen"},
{11, &IHidDebugServer::SetTouchScreenAutoPilotState, "SetTouchScreenAutoPilotState"},
{12, &IHidDebugServer::UnsetTouchScreenAutoPilotState, "UnsetTouchScreenAutoPilotState"},
{13, &IHidDebugServer::GetTouchScreenConfiguration, "GetTouchScreenConfiguration"},
{14, &IHidDebugServer::ProcessTouchScreenAutoTune, "ProcessTouchScreenAutoTune"},
{15, &IHidDebugServer::ForceStopTouchScreenManagement, "ForceStopTouchScreenManagement"},
{16, &IHidDebugServer::ForceRestartTouchScreenManagement, "ForceRestartTouchScreenManagement"},
{17, &IHidDebugServer::IsTouchScreenManaged, "IsTouchScreenManaged"},
{20, &IHidDebugServer::DeactivateMouse, "DeactivateMouse"},
{21, &IHidDebugServer::SetMouseAutoPilotState, "SetMouseAutoPilotState"},
{22, &IHidDebugServer::UnsetMouseAutoPilotState, "UnsetMouseAutoPilotState"},
{25, &IHidDebugServer::SetDebugMouseAutoPilotState, "SetDebugMouseAutoPilotState"},
{26, &IHidDebugServer::UnsetDebugMouseAutoPilotState, "UnsetDebugMouseAutoPilotState"},
{30, &IHidDebugServer::DeactivateKeyboard, "DeactivateKeyboard"},
{31, &IHidDebugServer::SetKeyboardAutoPilotState, "SetKeyboardAutoPilotState"},
{32, &IHidDebugServer::UnsetKeyboardAutoPilotState, "UnsetKeyboardAutoPilotState"},
{50, &IHidDebugServer::DeactivateXpad, "DeactivateXpad"},
{60, &IHidDebugServer::ClearNpadSystemCommonPolicy, "ClearNpadSystemCommonPolicy"},
{61, &IHidDebugServer::DeactivateNpad, "DeactivateNpad"},
{62, &IHidDebugServer::ForceDisconnectNpad, "ForceDisconnectNpad"},
{91, &IHidDebugServer::DeactivateGesture, "DeactivateGesture"},
{110, &IHidDebugServer::DeactivateHomeButton, "DeactivateHomeButton"},
{111, &IHidDebugServer::SetHomeButtonAutoPilotState, "SetHomeButtonAutoPilotState"},
{112, &IHidDebugServer::UnsetHomeButtonAutoPilotState, "UnsetHomeButtonAutoPilotState"},
{120, &IHidDebugServer::DeactivateSleepButton, "DeactivateSleepButton"},
{121, &IHidDebugServer::SetSleepButtonAutoPilotState, "SetSleepButtonAutoPilotState"},
{122, &IHidDebugServer::UnsetSleepButtonAutoPilotState, "UnsetSleepButtonAutoPilotState"},
{123, &IHidDebugServer::DeactivateInputDetector, "DeactivateInputDetector"},
{130, &IHidDebugServer::DeactivateCaptureButton, "DeactivateCaptureButton"},
{131, &IHidDebugServer::SetCaptureButtonAutoPilotState, "SetCaptureButtonAutoPilotState"},
{132, &IHidDebugServer::UnsetCaptureButtonAutoPilotState, "UnsetCaptureButtonAutoPilotState"},
{133, &IHidDebugServer::SetShiftAccelerometerCalibrationValue, "SetShiftAccelerometerCalibrationValue"},
{134, &IHidDebugServer::GetShiftAccelerometerCalibrationValue, "GetShiftAccelerometerCalibrationValue"},
{135, &IHidDebugServer::SetShiftGyroscopeCalibrationValue, "SetShiftGyroscopeCalibrationValue"},
{136, &IHidDebugServer::GetShiftGyroscopeCalibrationValue, "GetShiftGyroscopeCalibrationValue"},
{140, nullptr, "SetSixAxisSensorMode"},
{140, nullptr, "DeactivateConsoleSixAxisSensor"},
{141, nullptr, "GetConsoleSixAxisSensorSamplingFrequency"},
{142, nullptr, "DeactivateSevenSixAxisSensor"},
{143, nullptr, "GetConsoleSixAxisSensorCountStates"},
{144, &IHidDebugServer::GetAccelerometerFsr, "GetAccelerometerFsr"},
{145, &IHidDebugServer::SetAccelerometerFsr, "SetAccelerometerFsr"},
{146, &IHidDebugServer::GetAccelerometerOdr, "GetAccelerometerOdr"},
{147, &IHidDebugServer::SetAccelerometerOdr, "SetAccelerometerOdr"},
{148, &IHidDebugServer::GetGyroscopeFsr, "GetGyroscopeFsr"},
{149, &IHidDebugServer::SetGyroscopeFsr, "SetGyroscopeFsr"},
{150, &IHidDebugServer::GetGyroscopeOdr, "GetGyroscopeOdr"},
{151, &IHidDebugServer::SetGyroscopeOdr, "SetGyroscopeOdr"},
{152, nullptr, "GetWhoAmI"},
{201, nullptr, "ActivateFirmwareUpdate"},
{202, nullptr, "DeactivateFirmwareUpdate"},
{203, nullptr, "StartFirmwareUpdate"},
{204, nullptr, "GetFirmwareUpdateStage"},
{205, nullptr, "GetFirmwareVersion"},
{206, nullptr, "GetDestinationFirmwareVersion"},
{207, nullptr, "DiscardFirmwareInfoCacheForRevert"},
{208, nullptr, "StartFirmwareUpdateForRevert"},
{209, nullptr, "GetAvailableFirmwareVersionForRevert"},
{210, nullptr, "IsFirmwareUpdatingDevice"},
{211, nullptr, "StartFirmwareUpdateIndividual"},
{215, nullptr, "SetUsbFirmwareForceUpdateEnabled"},
{216, nullptr, "SetAllKuinaDevicesToFirmwareUpdateMode"},
{221, nullptr, "UpdateControllerColor"},
{222, nullptr, "ConnectUsbPadsAsync"},
{223, nullptr, "DisconnectUsbPadsAsync"},
{224, nullptr, "UpdateDesignInfo"},
{225, nullptr, "GetUniquePadDriverState"},
{226, nullptr, "GetSixAxisSensorDriverStates"},
{227, nullptr, "GetRxPacketHistory"},
{228, nullptr, "AcquireOperationEventHandle"},
{229, nullptr, "ReadSerialFlash"},
{230, nullptr, "WriteSerialFlash"},
{231, nullptr, "GetOperationResult"},
{232, nullptr, "EnableShipmentMode"},
{233, nullptr, "ClearPairingInfo"},
{234, nullptr, "GetUniquePadDeviceTypeSetInternal"},
{235, nullptr, "EnableAnalogStickPower"},
{236, nullptr, "RequestKuinaUartClockCal"},
{237, nullptr, "GetKuinaUartClockCal"},
{238, nullptr, "SetKuinaUartClockTrim"},
{239, nullptr, "KuinaLoopbackTest"},
{240, nullptr, "RequestBatteryVoltage"},
{241, nullptr, "GetBatteryVoltage"},
{242, nullptr, "GetUniquePadPowerInfo"},
{243, nullptr, "RebootUniquePad"},
{244, nullptr, "RequestKuinaFirmwareVersion"},
{245, nullptr, "GetKuinaFirmwareVersion"},
{246, nullptr, "GetVidPid"},
{247, nullptr, "GetAnalogStickCalibrationValue"},
{248, nullptr, "GetUniquePadIdsFull"},
{249, nullptr, "ConnectUniquePad"},
{250, nullptr, "IsVirtual"},
{251, nullptr, "GetAnalogStickModuleParam"},
{301, nullptr, "GetAbstractedPadHandles"},
{302, nullptr, "GetAbstractedPadState"},
{303, nullptr, "GetAbstractedPadsState"},
{321, nullptr, "SetAutoPilotVirtualPadState"},
{322, nullptr, "UnsetAutoPilotVirtualPadState"},
{323, nullptr, "UnsetAllAutoPilotVirtualPadState"},
{324, nullptr, "AttachHdlsWorkBuffer"},
{325, nullptr, "ReleaseHdlsWorkBuffer"},
{326, nullptr, "DumpHdlsNpadAssignmentState"},
{327, nullptr, "DumpHdlsStates"},
{328, nullptr, "ApplyHdlsNpadAssignmentState"},
{329, nullptr, "ApplyHdlsStateList"},
{330, nullptr, "AttachHdlsVirtualDevice"},
{331, nullptr, "DetachHdlsVirtualDevice"},
{332, nullptr, "SetHdlsState"},
{350, nullptr, "AddRegisteredDevice"},
{400, nullptr, "DisableExternalMcuOnNxDevice"},
{401, nullptr, "DisableRailDeviceFiltering"},
{402, nullptr, "EnableWiredPairing"},
{403, nullptr, "EnableShipmentModeAutoClear"},
{404, nullptr, "SetRailEnabled"},
{500, nullptr, "SetFactoryInt"},
{501, nullptr, "IsFactoryBootEnabled"},
{550, nullptr, "SetAnalogStickModelDataTemporarily"},
{551, nullptr, "GetAnalogStickModelData"},
{552, nullptr, "ResetAnalogStickModelData"},
{600, nullptr, "ConvertPadState"},
{650, nullptr, "AddButtonPlayData"},
{651, nullptr, "StartButtonPlayData"},
{652, nullptr, "StopButtonPlayData"},
{2000, nullptr, "DeactivateDigitizer"},
{2001, nullptr, "SetDigitizerAutoPilotState"},
{2002, nullptr, "UnsetDigitizerAutoPilotState"},
{3000, &IHidDebugServer::ReloadFirmwareDebugSettings, "ReloadFirmwareDebugSettings"},
};
// clang-format on
RegisterHandlers(functions);
}
IHidDebugServer::~IHidDebugServer() = default;
void IHidDebugServer::DeactivateDebugPad(HLERequestContext& ctx) {
LOG_DEBUG(Service_HID, "called");
Result result = ResultSuccess;
if (!firmware_settings->IsDeviceManaged()) {
result = GetResourceManager()->GetDebugPad()->Deactivate();
}
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
}
void IHidDebugServer::SetDebugPadAutoPilotState(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto auto_pilot_state{rp.Pop<DebugPadAutoPilotState>()};
LOG_DEBUG(Service_HID, "called");
const Result result =
GetResourceManager()->GetDebugPad()->SetAutoPilotState(auto_pilot_state);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
}
void IHidDebugServer::UnsetDebugPadAutoPilotState(HLERequestContext& ctx) {
LOG_DEBUG(Service_HID, "called");
const Result result =
GetResourceManager()->GetDebugPad()->UnsetAutoPilotState();
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
}
void IHidDebugServer::DeactivateTouchScreen(HLERequestContext& ctx) {
LOG_DEBUG(Service_HID, "called");
Result result = ResultSuccess;
if (!firmware_settings->IsDeviceManaged()) {
result = GetResourceManager()->GetTouchScreen()->Deactivate();
}
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
}
void IHidDebugServer::SetTouchScreenAutoPilotState(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto touch_state_elements{ctx.GetReadBufferNumElements<TouchState>()};
const auto touch_state_buffer{ctx.ReadBuffer()};
LOG_DEBUG(Service_HID, "called, touch_state_elements={}", touch_state_elements);
std::vector<TouchState> touch_states(touch_state_elements);
memcpy(touch_states.data(), touch_state_buffer.data(), touch_state_buffer.size());
const Result result = GetResourceManager()->GetTouchScreen()->SetAutoPilotState(touch_states);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
}
void IHidDebugServer::UnsetTouchScreenAutoPilotState(HLERequestContext& ctx) {
LOG_DEBUG(Service_HID, "called");
const Result result = GetResourceManager()->GetTouchScreen()->UnsetAutoPilotState();
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
}
void IHidDebugServer::GetTouchScreenConfiguration(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto applet_resource_user_id{rp.Pop<u64>()};
LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}",
applet_resource_user_id);
TouchScreenConfigurationForNx touch_screen_configuration{};
const Result result =
GetResourceManager()->GetTouchScreen()->GetConfiguration(applet_resource_user_id);
switch (touch_screen_configuration.mode) {
case TouchScreenModeForNx::UseSystemSetting:
case TouchScreenModeForNx::Finger:
case TouchScreenModeForNx::Heat2:
break;
default:
touch_screen_configuration.mode = TouchScreenModeForNx::UseSystemSetting;
break;
}
IPC::ResponseBuilder rb{ctx, 6};
rb.Push(ResultSuccess);
rb.PushRaw(touch_screen_configuration);
}
void IHidDebugServer::ProcessTouchScreenAutoTune(HLERequestContext& ctx) {
LOG_DEBUG(Service_HID, "called");
const Result result = GetResourceManager()->GetTouchScreen()->ProcessAutoTune();
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
}
void IHidDebugServer::ForceStopTouchScreenManagement(HLERequestContext& ctx){
LOG_DEBUG(Service_HID, "called");
Result result = ResultSuccess;
auto touch_screen = GetResourceManager()->GetTouchScreen();
if (firmware_settings->IsDeviceManaged()) {
if (firmware_settings->IsTouchI2cManaged()) {
result=touch_screen->Something();
if (result.IsSuccess()) {
result = touch_screen->Somethingx2();
}
if (result.IsSuccess()) {
result = touch_screen->DeactivateTouchScreen();
}
if (result.IsSuccess()) {
result = touch_screen->DeactivateGesture();
}
}
}
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
}
void IHidDebugServer::ForceRestartTouchScreenManagement(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
u32 basic_gesture_id;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
};
static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
const auto parameters{rp.PopRaw<Parameters>()};
LOG_DEBUG(Service_HID, "called, basic_gesture_id={}, applet_resource_user_id={}",
parameters.basic_gesture_id, parameters.applet_resource_user_id);
Result result = ResultSuccess;
auto touch_screen = GetResourceManager()->GetTouchScreen();
if (firmware_settings->IsDeviceManaged() && firmware_settings->IsTouchI2cManaged()) {
result = touch_screen->ActivateGestureUnmanaged();
if (result.IsSuccess()) {
result = touch_screen->ActivateGesture(basic_gesture_id, applet_resource_user_id);
}
if (result.IsSuccess()) {
result = touch_screen->ActivateTouchScreenUnmanaged();
}
if (result.IsSuccess()) {
result = touch_screen->ActivateTouchScreen();
}
}
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
}
void IHidDebugServer::IsTouchScreenManaged(HLERequestContext& ctx){
LOG_DEBUG(Service_HID, "called");
bool is_managed{};
bool is_bool{};
bool is_baal{};
Result result = GetResourceManager()->GetTouchScreen()->SomeFunction(is_bool);
if (result.IsSuccess()) {
GetResourceManager()->GetTouchScreen()->SomeFunction2(is_baal);
}
if (result.IsSuccess()) {
is_managed = is_bool | is_baal;
}
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
}
void IHidDebugServer::DeactivateMouse(HLERequestContext& ctx) {
LOG_DEBUG(Service_HID, "called");
Result result = ResultSuccess;
if (!firmware_settings->IsDeviceManaged()) {
result = GetResourceManager()->GetMouse()->Deactivate();
}
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
}
void IHidDebugServer::SetMouseAutoPilotState(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto auto_pilot_state{rp.Pop<MouseAutoPilotState>()};
LOG_DEBUG(Service_HID, "called");
const Result result = GetResourceManager()->GetMouse()->SetAutoPilotState(auto_pilot_state);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
}
void IHidDebugServer::UnsetMouseAutoPilotState(HLERequestContext& ctx) {
LOG_DEBUG(Service_HID, "called");
const Result result = GetResourceManager()->GetMouse()->UnsetAutoPilotState();
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
}
void IHidDebugServer::SetDebugMouseAutoPilotState(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto auto_pilot_state{rp.Pop<MouseAutoPilotState>()};
LOG_DEBUG(Service_HID, "called");
const Result result = GetResourceManager()->GetDebugMouse()->SetAutoPilotState(auto_pilot_state);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
}
void IHidDebugServer::UnsetDebugMouseAutoPilotState(HLERequestContext& ctx) {
LOG_DEBUG(Service_HID, "called");
const Result result = GetResourceManager()->GetDebugMouse()->UnsetAutoPilotState();
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
}
void IHidDebugServer::DeactivateKeyboard(HLERequestContext& ctx) {
LOG_DEBUG(Service_HID, "called");
Result result = ResultSuccess;
if (!firmware_settings->IsDeviceManaged()) {
result = GetResourceManager()->GetKeyboard()->Deactivate();
}
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
}
void IHidDebugServer::SetKeyboardAutoPilotState(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto auto_pilot_state{rp.Pop<KeyboardAutoPilotState>()};
LOG_DEBUG(Service_HID, "called");
const Result result =
GetResourceManager()->GetKeyboard()->SetAutoPilotState(auto_pilot_state);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
}
void IHidDebugServer::UnsetKeyboardAutoPilotState(HLERequestContext& ctx) {
LOG_DEBUG(Service_HID, "called");
const Result result = GetResourceManager()->GetKeyboard()->UnsetAutoPilotState();
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
}
void IHidDebugServer::DeactivateXpad(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto joy_xpad_id{rp.Pop<u32>()};
LOG_DEBUG(Service_HID, "called, joy_xpad_id={}", joy_xpad_id);
// This function has been stubbed since 10.0.0+
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void IHidDebugServer::ClearNpadSystemCommonPolicy(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto applet_resource_user_id{rp.Pop<u64>()};
LOG_INFO(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
const auto npad = GetResourceManager()->GetNpad();
const auto result = npad->ClearNpadSystemCommonPolicy(applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
}
void IHidDebugServer::DeactivateNpad(HLERequestContext& ctx) {}
void IHidDebugServer::ForceDisconnectNpad(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto npad_id{rp.PopEnum<NpadIdType>()};
LOG_INFO(Service_HID, "called, npad_id={}", npad_id);
GetResourceManager()->GetNpad()->ForceDisconnectNpad(NpadIdType::Player1);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void IHidDebugServer::DeactivateGesture(HLERequestContext& ctx) {}
void IHidDebugServer::DeactivateHomeButton(HLERequestContext& ctx) {}
void IHidDebugServer::SetHomeButtonAutoPilotState(HLERequestContext& ctx) {}
void IHidDebugServer::UnsetHomeButtonAutoPilotState(HLERequestContext& ctx) {}
void IHidDebugServer::DeactivateSleepButton(HLERequestContext& ctx) {}
void IHidDebugServer::SetSleepButtonAutoPilotState(HLERequestContext& ctx) {}
void IHidDebugServer::UnsetSleepButtonAutoPilotState(HLERequestContext& ctx) {}
void IHidDebugServer::DeactivateInputDetector(HLERequestContext& ctx) {}
void IHidDebugServer::DeactivateCaptureButton(HLERequestContext& ctx) {}
void IHidDebugServer::SetCaptureButtonAutoPilotState(HLERequestContext& ctx) {}
void IHidDebugServer::UnsetCaptureButtonAutoPilotState(HLERequestContext& ctx) {}
void IHidDebugServer::SetShiftAccelerometerCalibrationValue(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
SixAxisSensorHandle sixaxis_handle;
SixAxisSensorShiftAccelerometerCalibration accelerometer_calibration;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
};
static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size.");
const auto parameters{rp.PopRaw<Parameters>()};
LOG_DEBUG(Service_HID,
"called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
Result result = ResultSuccess;
std::array<u64, ARUID_MAX> aruid_list{};
std::shared_ptr<SixAxisSensorState> state = nullptr;
const auto sixaxis = GetResourceManager()->GetSixAxis();
sixaxis->GetAruidList(aruid_list);
for (const auto& aruid : aruid_list) {
result = sixaxis->GetSensorState(state, aruid, parameters.sixaxis_handle);
if (result.IsError()) {
break;
}
state->SetShiftAccelerometerCalibrationValue(parameters.accelerometer_calibration);
}
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
}
void IHidDebugServer::GetShiftAccelerometerCalibrationValue(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
SixAxisSensorHandle sixaxis_handle;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
};
static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
const auto parameters{rp.PopRaw<Parameters>()};
LOG_DEBUG(Service_HID,
"called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
SixAxisSensorShiftAccelerometerCalibration accelerometer_calibration;
std::shared_ptr<SixAxisSensorState> state = nullptr;
const auto sixaxis = GetResourceManager()->GetSixAxis();
const auto result = sixaxis->GetSensorState(state, parameters.applet_resource_user_id,
parameters.sixaxis_handle);
if (result.IsSuccess()) {
accelerometer_calibration = state->GetShiftAccelerometerCalibrationValue();
}
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(result);
rb.PushRaw(accelerometer_calibration);
}
void IHidDebugServer::SetShiftGyroscopeCalibrationValue(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
SixAxisSensorHandle sixaxis_handle;
SixAxisSensorShiftGyroscopeCalibration gyroscope_calibration;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
};
static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size.");
const auto parameters{rp.PopRaw<Parameters>()};
LOG_DEBUG(Service_HID,
"called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
Result result = ResultSuccess;
std::array<u64, ARUID_MAX> aruid_list{};
std::shared_ptr<SixAxisSensorState> state = nullptr;
const auto sixaxis = GetResourceManager()->GetSixAxis();
sixaxis->GetAruidList(aruid_list);
for (const auto& aruid : aruid_list) {
result = sixaxis->GetSensorState(state, aruid, parameters.sixaxis_handle);
if (result.IsError()) {
break;
}
state->SetShiftGyroscopeCalibrationValue(parameters.gyroscope_calibration);
}
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
}
void IHidDebugServer::GetShiftGyroscopeCalibrationValue(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
SixAxisSensorHandle sixaxis_handle;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
};
static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
const auto parameters{rp.PopRaw<Parameters>()};
LOG_DEBUG(Service_HID,
"called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
SixAxisSensorShiftGyroscopeCalibration gyroscope_calibration;
std::shared_ptr<SixAxisSensorState> state = nullptr;
const auto sixaxis = GetResourceManager()->GetSixAxis();
const auto result = sixaxis->GetSensorState(state, parameters.applet_resource_user_id,
parameters.sixaxis_handle);
if (result.IsSuccess()) {
gyroscope_calibration = state->GetShiftGyroscopeCalibrationValue();
}
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(result);
rb.PushRaw(gyroscope_calibration);
}
void IHidDebugServer::GetAccelerometerFsr(HLERequestContext& ctx) {}
void IHidDebugServer::SetAccelerometerFsr(HLERequestContext& ctx) {}
void IHidDebugServer::GetAccelerometerOdr(HLERequestContext& ctx) {}
void IHidDebugServer::SetAccelerometerOdr(HLERequestContext& ctx) {}
void IHidDebugServer::GetGyroscopeFsr(HLERequestContext& ctx) {}
void IHidDebugServer::SetGyroscopeFsr(HLERequestContext& ctx) {}
void IHidDebugServer::GetGyroscopeOdr(HLERequestContext& ctx) {}
void IHidDebugServer::SetGyroscopeOdr(HLERequestContext& ctx) {}
void IHidDebugServer::ReloadFirmwareDebugSettings(HLERequestContext& ctx) {
firmware_settings->Reload();
LOG_INFO(Service_HID, "called");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
std::shared_ptr<ResourceManager> IHidDebugServer::GetResourceManager() {
resource_manager->VerifiyInitalization();
return resource_manager;
}
} // namespace Service::HID

View File

@@ -1,78 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include "core/hle/service/service.h"
namespace Core {
class System;
}
namespace Service::HID {
class ResourceManager;
class HidFirmwareSettings;
class IHidDebugServer final : public ServiceFramework<IHidDebugServer> {
public:
explicit IHidDebugServer(Core::System& system_, std::shared_ptr<ResourceManager> resource,
std::shared_ptr<HidFirmwareSettings> settings);
~IHidDebugServer() override;
private:
// Service calls
void DeactivateDebugPad(HLERequestContext& ctx);
void SetDebugPadAutoPilotState(HLERequestContext& ctx);
void UnsetDebugPadAutoPilotState(HLERequestContext& ctx);
void DeactivateTouchScreen(HLERequestContext& ctx);
void SetTouchScreenAutoPilotState(HLERequestContext& ctx);
void UnsetTouchScreenAutoPilotState(HLERequestContext& ctx);
void GetTouchScreenConfiguration(HLERequestContext& ctx);
void ProcessTouchScreenAutoTune(HLERequestContext& ctx);
void ForceStopTouchScreenManagement(HLERequestContext& ctx);
void ForceRestartTouchScreenManagement(HLERequestContext& ctx);
void IsTouchScreenManaged(HLERequestContext& ctx);
void DeactivateMouse(HLERequestContext& ctx);
void SetMouseAutoPilotState(HLERequestContext& ctx);
void UnsetMouseAutoPilotState(HLERequestContext& ctx);
void SetDebugMouseAutoPilotState(HLERequestContext& ctx);
void UnsetDebugMouseAutoPilotState(HLERequestContext& ctx);
void DeactivateKeyboard(HLERequestContext& ctx);
void SetKeyboardAutoPilotState(HLERequestContext& ctx);
void UnsetKeyboardAutoPilotState(HLERequestContext& ctx);
void DeactivateXpad(HLERequestContext& ctx);
void ClearNpadSystemCommonPolicy(HLERequestContext& ctx);
void DeactivateNpad(HLERequestContext& ctx);
void ForceDisconnectNpad(HLERequestContext& ctx);
void DeactivateGesture(HLERequestContext& ctx);
void DeactivateHomeButton(HLERequestContext& ctx);
void SetHomeButtonAutoPilotState(HLERequestContext& ctx);
void UnsetHomeButtonAutoPilotState(HLERequestContext& ctx);
void DeactivateSleepButton(HLERequestContext& ctx);
void SetSleepButtonAutoPilotState(HLERequestContext& ctx);
void UnsetSleepButtonAutoPilotState(HLERequestContext& ctx);
void DeactivateInputDetector(HLERequestContext& ctx);
void DeactivateCaptureButton(HLERequestContext& ctx);
void SetCaptureButtonAutoPilotState(HLERequestContext& ctx);
void UnsetCaptureButtonAutoPilotState(HLERequestContext& ctx);
void SetShiftAccelerometerCalibrationValue(HLERequestContext& ctx);
void GetShiftAccelerometerCalibrationValue(HLERequestContext& ctx);
void SetShiftGyroscopeCalibrationValue(HLERequestContext& ctx);
void GetShiftGyroscopeCalibrationValue(HLERequestContext& ctx);
void GetAccelerometerFsr(HLERequestContext& ctx);
void SetAccelerometerFsr(HLERequestContext& ctx);
void GetAccelerometerOdr(HLERequestContext& ctx);
void SetAccelerometerOdr(HLERequestContext& ctx);
void GetGyroscopeFsr(HLERequestContext& ctx);
void SetGyroscopeFsr(HLERequestContext& ctx);
void GetGyroscopeOdr(HLERequestContext& ctx);
void SetGyroscopeOdr(HLERequestContext& ctx);
void ReloadFirmwareDebugSettings(HLERequestContext& ctx);
std::shared_ptr<ResourceManager> GetResourceManager();
std::shared_ptr<ResourceManager> resource_manager = nullptr;
std::shared_ptr<HidFirmwareSettings> firmware_settings = nullptr;
};
} // namespace Service::HID

View File

@@ -1,148 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/service/hid/hid_firmware_settings.h"
namespace Service::HID {
HidFirmwareSettings::HidFirmwareSettings() {
LoadSettings(true);
}
void HidFirmwareSettings::Reload() {
LoadSettings(true);
}
void HidFirmwareSettings::LoadSettings(bool should_initialize) {
// TODO: uncomment this code when we can talk between services
if (is_debug_settings_initalized && !should_initialize) {
return;
}
is_debugpad_enabled = true;
// nn::settings::fwdbg::GetSettingsItemValue(&is_debugpad_enabled, 1, "hid_debug",
// "enables_debugpad")
is_device_managed = true;
// nn::settings::fwdbg::GetSettingsItemValue(&is_device_managed, 1, "hid_debug",
// "manages_devices");
is_touch_i2c_managed = is_device_managed;
if (is_device_managed) {
// nn::settings::fwdbg::GetSettingsItemValue(&setting_hid_debug_manages_touch_ic_i2c, 1,
// "hid_debug", "manages_touch_ic_i2c");
}
is_future_devices_emulated = false;
// nn::settings::fwdbg::GetSettingsItemValue(&setting_hid_debug_emulate_future_device, 1,
// "hid_debug", "emulate_future_device");
is_mcu_hardware_error_emulated = false;
// nn::settings::fwdbg::GetSettingsItemValue(&setting_hid_debug_emulate_mcu_hardware_error, 1,
// "hid_debug", "emulate_mcu_hardware_error");
is_rail_enabled = true;
// nn::settings::fwdbg::GetSettingsItemValue(&setting_hid_debug_enables_rail, 1, "hid_debug",
// "enables_rail");
is_firmware_update_failure_emulated = false;
// nn::settings::fwdbg::GetSettingsItemValue(setting_hid_debug_emulate_firmware_update_failure,
// 1, "hid_debug", "emulate_firmware_update_failure");
is_firmware_update_failure = {};
if (is_firmware_update_failure_emulated) {
const std::size_t size = 0; // GetSettingsItemValueSize("hid_debug",
// "firmware_update_failure"); if (size != 0) {
[[maybe_unused]] const std::size_t setting_size =
std::min(size, is_firmware_update_failure.size());
// nn::settings::fwdbg::GetSettingsItemValue(&is_firmware_update_failure, setting_size,
// "hid_debug", "firmware_update_failure");
//}
}
is_ble_disabled = false;
// nn::settings::fwdbg::GetSettingsItemValue(&setting_hid_debug_ble_disabled, 1, "hid_debug",
// "ble_disabled");
is_dscale_disabled = false;
// nn::settings::fwdbg::GetSettingsItemValue(&setting_hid_debug_dscale_disabled, 1, "hid_debug",
// "dscale_disabled");
is_handheld_forced = true;
// nn::settings::fwdbg::GetSettingsItemValue(&force_handheld, 1, "hid_debug", "force_handheld");
features_per_id_disabled = {};
// nn::settings::fwdbg::GetSettingsItemValue(&disabled_features_per_id,
// is_features_per_id_disabled.size(), "hid_debug",
// "disabled_features_per_id");
is_touch_firmware_auto_update_disabled = false;
// nn::settings::fwdbg::GetSettingsItemValue(
// &setting_hid_debug_touch_firmware_auto_update_disabled, 1, "hid_debug",
// "touch_firmware_auto_update_disabled");
is_debug_settings_initalized = true;
}
bool HidFirmwareSettings::IsDebugPadEnabled() {
LoadSettings(false);
return is_debugpad_enabled;
}
bool HidFirmwareSettings::IsDeviceManaged() {
LoadSettings(false);
return is_device_managed;
}
bool HidFirmwareSettings::IsEmulateFutureDevice() {
LoadSettings(false);
return is_future_devices_emulated;
}
bool HidFirmwareSettings::IsTouchI2cManaged() {
LoadSettings(false);
return is_touch_i2c_managed;
}
bool HidFirmwareSettings::IsHandheldForced() {
LoadSettings(false);
return is_handheld_forced;
}
bool HidFirmwareSettings::IsRailEnabled() {
LoadSettings(false);
return is_rail_enabled;
}
bool HidFirmwareSettings::IsHardwareErrorEmulated() {
LoadSettings(false);
return is_mcu_hardware_error_emulated;
}
bool HidFirmwareSettings::IsBleDisabled() {
LoadSettings(false);
return is_ble_disabled;
}
bool HidFirmwareSettings::IsDscaleDisabled() {
LoadSettings(false);
return is_dscale_disabled;
}
bool HidFirmwareSettings::IsTouchAutoUpdateDisabled() {
LoadSettings(false);
return is_touch_firmware_auto_update_disabled;
}
HidFirmwareSettings::FirmwareSetting HidFirmwareSettings::GetFirmwareUpdateFailure() {
LoadSettings(false);
return is_firmware_update_failure;
}
HidFirmwareSettings::FeaturesPerId HidFirmwareSettings::FeaturesDisabledPerId() {
LoadSettings(false);
return features_per_id_disabled;
}
} // namespace Service::HID

View File

@@ -1,54 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "common/common_types.h"
namespace Service::HID {
/// Loads firmware config from nn::settings::fwdbg
class HidFirmwareSettings {
public:
using FirmwareSetting = std::array<u8, 4>;
using FeaturesPerId = std::array<bool, 0xA8>;
HidFirmwareSettings();
void Reload();
void LoadSettings(bool should_initialize);
bool IsDebugPadEnabled();
bool IsDeviceManaged();
bool IsEmulateFutureDevice();
bool IsTouchI2cManaged();
bool IsHandheldForced();
bool IsRailEnabled();
bool IsHardwareErrorEmulated();
bool IsBleDisabled();
bool IsDscaleDisabled();
bool IsTouchAutoUpdateDisabled();
FirmwareSetting GetFirmwareUpdateFailure();
FeaturesPerId FeaturesDisabledPerId();
private:
bool is_debug_settings_initalized{};
// Debug settings
bool is_debugpad_enabled{};
bool is_device_managed{};
bool is_touch_i2c_managed{};
bool is_future_devices_emulated{};
bool is_mcu_hardware_error_emulated{};
bool is_rail_enabled{};
bool is_firmware_update_failure_emulated{};
bool is_ble_disabled{};
bool is_dscale_disabled{};
bool is_handheld_forced{};
bool is_touch_firmware_auto_update_disabled{};
FirmwareSetting is_firmware_update_failure{};
FeaturesPerId features_per_id_disabled{};
};
} // namespace Service::HID

View File

@@ -1,41 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include "core/hle/result.h"
namespace Service::HID {
constexpr Result ResultPalmaResultSuccess{ErrorModule::HID, 0};
constexpr Result ResultNpadInvalidHandle{ErrorModule::HID, 100};
constexpr Result ResultNpadDeviceIndexOutOfRange{ErrorModule::HID, 107};
constexpr Result ResultVibrationInvalidStyleIndex{ErrorModule::HID, 122};
constexpr Result ResultVibrationInvalidNpadId{ErrorModule::HID, 123};
constexpr Result ResultVibrationVolumeOutOfRange{ErrorModule::HID, 126};
constexpr Result ResultVibrationDeviceIndexOutOfRange{ErrorModule::HID, 124};
constexpr Result ResultVibrationArraySizeMismatch{ErrorModule::HID, 131};
constexpr Result ResultInvalidSixAxisFusionRange{ErrorModule::HID, 423};
constexpr Result ResultNpadIsDualJoycon{ErrorModule::HID, 601};
constexpr Result ResultNpadIsSameType{ErrorModule::HID, 602};
constexpr Result ResultInvalidNpadId{ErrorModule::HID, 709};
constexpr Result ResultNpadNotConnected{ErrorModule::HID, 710};
constexpr Result ResultInvalidArraySize{ErrorModule::HID, 715};
constexpr Result ResultUndefinedStyleSet{ErrorModule::HID, 716};
constexpr Result ResultInvalidStyleSet{ErrorModule::HID, 717};
constexpr Result ResultUnknown108{ErrorModule::HID, 108};
constexpr Result ResultInvalidPalmaNpadId{ErrorModule::HID, 3301};
constexpr Result ResultInvalidPalmaHandle{ErrorModule::HID, 3302};
constexpr Result ResultOperationFailed{ErrorModule::HID, 3304};
} // namespace Service::HID
namespace Service::XCD {
constexpr Result ResultUnknown1{ErrorModule::XCD, 1};
constexpr Result ResultUnknown365{ErrorModule::XCD, 365};
} // namespace Service::XCD

File diff suppressed because it is too large Load Diff

View File

@@ -1,198 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include "core/hle/service/service.h"
namespace Core {
class System;
}
namespace Service::HID {
struct VibrationDeviceHandle;
struct VibrationValue;
class ResourceManager;
class HidFirmwareSettings;
class IAppletResource final : public ServiceFramework<IAppletResource> {
public:
explicit IAppletResource(Core::System& system_);
~IAppletResource() override;
private:
void GetSharedMemoryHandle(HLERequestContext& ctx);
};
class IActiveVibrationDeviceList final : public ServiceFramework<IActiveVibrationDeviceList> {
public:
explicit IActiveVibrationDeviceList(Core::System& system_,
std::shared_ptr<ResourceManager> resource);
~IActiveVibrationDeviceList() override;
private:
void InitializeVibrationDevice(HLERequestContext& ctx);
std::shared_ptr<ResourceManager> resource_manager = nullptr;
};
class IHidServer final : public ServiceFramework<IHidServer> {
public:
explicit IHidServer(Core::System& system_, std::shared_ptr<ResourceManager> resource,
std::shared_ptr<HidFirmwareSettings> settings);
~IHidServer() override;
private:
void CreateAppletResource(HLERequestContext& ctx);
void ActivateDebugPad(HLERequestContext& ctx);
void ActivateTouchScreen(HLERequestContext& ctx);
void ActivateMouse(HLERequestContext& ctx);
void ActivateDebugMouse(HLERequestContext& ctx);
void ActivateKeyboard(HLERequestContext& ctx);
void SendKeyboardLockKeyEvent(HLERequestContext& ctx);
void AcquireXpadIdEventHandle(HLERequestContext& ctx);
void ReleaseXpadIdEventHandle(HLERequestContext& ctx);
void ActivateXpad(HLERequestContext& ctx);
void GetXpadIds(HLERequestContext& ctx);
void ActivateJoyXpad(HLERequestContext& ctx);
void GetJoyXpadLifoHandle(HLERequestContext& ctx);
void GetJoyXpadIds(HLERequestContext& ctx);
void ActivateSixAxisSensor(HLERequestContext& ctx);
void DeactivateSixAxisSensor(HLERequestContext& ctx);
void GetSixAxisSensorLifoHandle(HLERequestContext& ctx);
void ActivateJoySixAxisSensor(HLERequestContext& ctx);
void DeactivateJoySixAxisSensor(HLERequestContext& ctx);
void GetJoySixAxisSensorLifoHandle(HLERequestContext& ctx);
void StartSixAxisSensor(HLERequestContext& ctx);
void StopSixAxisSensor(HLERequestContext& ctx);
void IsSixAxisSensorFusionEnabled(HLERequestContext& ctx);
void EnableSixAxisSensorFusion(HLERequestContext& ctx);
void SetSixAxisSensorFusionParameters(HLERequestContext& ctx);
void GetSixAxisSensorFusionParameters(HLERequestContext& ctx);
void ResetSixAxisSensorFusionParameters(HLERequestContext& ctx);
void SetAccelerometerParameters(HLERequestContext& ctx);
void GetAccelerometerParameters(HLERequestContext& ctx);
void ResetAccelerometerParameters(HLERequestContext& ctx);
void SetAccelerometerPlayMode(HLERequestContext& ctx);
void GetAccelerometerPlayMode(HLERequestContext& ctx);
void ResetAccelerometerPlayMode(HLERequestContext& ctx);
void SetGyroscopeZeroDriftMode(HLERequestContext& ctx);
void GetGyroscopeZeroDriftMode(HLERequestContext& ctx);
void ResetGyroscopeZeroDriftMode(HLERequestContext& ctx);
void IsSixAxisSensorAtRest(HLERequestContext& ctx);
void IsFirmwareUpdateAvailableForSixAxisSensor(HLERequestContext& ctx);
void EnableSixAxisSensorUnalteredPassthrough(HLERequestContext& ctx);
void IsSixAxisSensorUnalteredPassthroughEnabled(HLERequestContext& ctx);
void StoreSixAxisSensorCalibrationParameter(HLERequestContext& ctx);
void LoadSixAxisSensorCalibrationParameter(HLERequestContext& ctx);
void GetSixAxisSensorIcInformation(HLERequestContext& ctx);
void ResetIsSixAxisSensorDeviceNewlyAssigned(HLERequestContext& ctx);
void ActivateGesture(HLERequestContext& ctx);
void SetSupportedNpadStyleSet(HLERequestContext& ctx);
void GetSupportedNpadStyleSet(HLERequestContext& ctx);
void SetSupportedNpadIdType(HLERequestContext& ctx);
void ActivateNpad(HLERequestContext& ctx);
void DeactivateNpad(HLERequestContext& ctx);
void AcquireNpadStyleSetUpdateEventHandle(HLERequestContext& ctx);
void DisconnectNpad(HLERequestContext& ctx);
void GetPlayerLedPattern(HLERequestContext& ctx);
void ActivateNpadWithRevision(HLERequestContext& ctx);
void SetNpadJoyHoldType(HLERequestContext& ctx);
void GetNpadJoyHoldType(HLERequestContext& ctx);
void SetNpadJoyAssignmentModeSingleByDefault(HLERequestContext& ctx);
void SetNpadJoyAssignmentModeSingle(HLERequestContext& ctx);
void SetNpadJoyAssignmentModeDual(HLERequestContext& ctx);
void MergeSingleJoyAsDualJoy(HLERequestContext& ctx);
void StartLrAssignmentMode(HLERequestContext& ctx);
void StopLrAssignmentMode(HLERequestContext& ctx);
void SetNpadHandheldActivationMode(HLERequestContext& ctx);
void GetNpadHandheldActivationMode(HLERequestContext& ctx);
void SwapNpadAssignment(HLERequestContext& ctx);
void IsUnintendedHomeButtonInputProtectionEnabled(HLERequestContext& ctx);
void EnableUnintendedHomeButtonInputProtection(HLERequestContext& ctx);
void SetNpadJoyAssignmentModeSingleWithDestination(HLERequestContext& ctx);
void SetNpadAnalogStickUseCenterClamp(HLERequestContext& ctx);
void SetNpadCaptureButtonAssignment(HLERequestContext& ctx);
void ClearNpadCaptureButtonAssignment(HLERequestContext& ctx);
void GetVibrationDeviceInfo(HLERequestContext& ctx);
void SendVibrationValue(HLERequestContext& ctx);
void GetActualVibrationValue(HLERequestContext& ctx);
void CreateActiveVibrationDeviceList(HLERequestContext& ctx);
void PermitVibration(HLERequestContext& ctx);
void IsVibrationPermitted(HLERequestContext& ctx);
void SendVibrationValues(HLERequestContext& ctx);
void SendVibrationGcErmCommand(HLERequestContext& ctx);
void GetActualVibrationGcErmCommand(HLERequestContext& ctx);
void BeginPermitVibrationSession(HLERequestContext& ctx);
void EndPermitVibrationSession(HLERequestContext& ctx);
void IsVibrationDeviceMounted(HLERequestContext& ctx);
void SendVibrationValueInBool(HLERequestContext& ctx);
void ActivateConsoleSixAxisSensor(HLERequestContext& ctx);
void StartConsoleSixAxisSensor(HLERequestContext& ctx);
void StopConsoleSixAxisSensor(HLERequestContext& ctx);
void ActivateSevenSixAxisSensor(HLERequestContext& ctx);
void StartSevenSixAxisSensor(HLERequestContext& ctx);
void StopSevenSixAxisSensor(HLERequestContext& ctx);
void InitializeSevenSixAxisSensor(HLERequestContext& ctx);
void FinalizeSevenSixAxisSensor(HLERequestContext& ctx);
void SetSevenSixAxisSensorFusionStrength(HLERequestContext& ctx);
void GetSevenSixAxisSensorFusionStrength(HLERequestContext& ctx);
void ResetSevenSixAxisSensorTimestamp(HLERequestContext& ctx);
void IsUsbFullKeyControllerEnabled(HLERequestContext& ctx);
void EnableUsbFullKeyController(HLERequestContext& ctx);
void IsUsbFullKeyControllerConnected(HLERequestContext& ctx);
void HasBattery(HLERequestContext& ctx);
void HasLeftRightBattery(HLERequestContext& ctx);
void GetNpadInterfaceType(HLERequestContext& ctx);
void GetNpadLeftRightInterfaceType(HLERequestContext& ctx);
void GetNpadOfHighestBatteryLevel(HLERequestContext& ctx);
void GetNpadOfHighestBatteryLevelForJoyRight(HLERequestContext& ctx);
void GetPalmaConnectionHandle(HLERequestContext& ctx);
void InitializePalma(HLERequestContext& ctx);
void AcquirePalmaOperationCompleteEvent(HLERequestContext& ctx);
void GetPalmaOperationInfo(HLERequestContext& ctx);
void PlayPalmaActivity(HLERequestContext& ctx);
void SetPalmaFrModeType(HLERequestContext& ctx);
void ReadPalmaStep(HLERequestContext& ctx);
void EnablePalmaStep(HLERequestContext& ctx);
void ResetPalmaStep(HLERequestContext& ctx);
void ReadPalmaApplicationSection(HLERequestContext& ctx);
void WritePalmaApplicationSection(HLERequestContext& ctx);
void ReadPalmaUniqueCode(HLERequestContext& ctx);
void SetPalmaUniqueCodeInvalid(HLERequestContext& ctx);
void WritePalmaActivityEntry(HLERequestContext& ctx);
void WritePalmaRgbLedPatternEntry(HLERequestContext& ctx);
void WritePalmaWaveEntry(HLERequestContext& ctx);
void SetPalmaDataBaseIdentificationVersion(HLERequestContext& ctx);
void GetPalmaDataBaseIdentificationVersion(HLERequestContext& ctx);
void SuspendPalmaFeature(HLERequestContext& ctx);
void GetPalmaOperationResult(HLERequestContext& ctx);
void ReadPalmaPlayLog(HLERequestContext& ctx);
void ResetPalmaPlayLog(HLERequestContext& ctx);
void SetIsPalmaAllConnectable(HLERequestContext& ctx);
void SetIsPalmaPairedConnectable(HLERequestContext& ctx);
void PairPalma(HLERequestContext& ctx);
void SetPalmaBoostMode(HLERequestContext& ctx);
void CancelWritePalmaWaveEntry(HLERequestContext& ctx);
void EnablePalmaBoostMode(HLERequestContext& ctx);
void GetPalmaBluetoothAddress(HLERequestContext& ctx);
void SetDisallowedPalmaConnection(HLERequestContext& ctx);
void SetNpadCommunicationMode(HLERequestContext& ctx);
void GetNpadCommunicationMode(HLERequestContext& ctx);
void SetTouchScreenConfiguration(HLERequestContext& ctx);
void IsFirmwareUpdateNeededForNotification(HLERequestContext& ctx);
void ActivateDigitizer(HLERequestContext& ctx);
private:
Result SendVibrationValueImpl(const u64 aruid, const VibrationDeviceHandle& handle,
const VibrationValue& value);
std::shared_ptr<ResourceManager> GetResourceManager();
std::shared_ptr<ResourceManager> resource_manager = nullptr;
std::shared_ptr<IAppletResource> applet_resource = nullptr;
std::shared_ptr<HidFirmwareSettings> firmware_settings = nullptr;
};
} // namespace Service::HID

View File

@@ -1,403 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include "core/hle/service/hid//hid_result.h"
#include "core/hle/service/hid//hid_types.h"
#include "core/hle/service/hid/hid_system_server.h"
#include "core/hle/service/hid/resource_manager.h"
#include "core/hle/service/hid/resource_manager/npad_resource/npad.h"
#include "core/hle/service/hid/resource_manager/touch_screen.h"
#include "core/hle/service/ipc_helpers.h"
namespace Service::HID {
IHidSystemServer::IHidSystemServer(Core::System& system_, std::shared_ptr<ResourceManager> resource)
: ServiceFramework{system_, "hid:sys"}, resource_manager{resource} {
// clang-format off
static const FunctionInfo functions[] = {
{31, nullptr, "SendKeyboardLockKeyEvent"},
{101, nullptr, "AcquireHomeButtonEventHandle"},
{111, nullptr, "ActivateHomeButton"},
{121, nullptr, "AcquireSleepButtonEventHandle"},
{131, nullptr, "ActivateSleepButton"},
{141, nullptr, "AcquireCaptureButtonEventHandle"},
{151, nullptr, "ActivateCaptureButton"},
{161, nullptr, "GetPlatformConfig"},
{210, nullptr, "AcquireNfcDeviceUpdateEventHandle"},
{211, nullptr, "GetNpadsWithNfc"},
{212, nullptr, "AcquireNfcActivateEventHandle"},
{213, nullptr, "ActivateNfc"},
{214, nullptr, "GetXcdHandleForNpadWithNfc"},
{215, nullptr, "IsNfcActivated"},
{230, nullptr, "AcquireIrSensorEventHandle"},
{231, nullptr, "ActivateIrSensor"},
{232, nullptr, "GetIrSensorState"},
{233, nullptr, "GetXcdHandleForNpadWithIrSensor"},
{301, nullptr, "ActivateNpadSystem"},
{303, &IHidSystemServer::ApplyNpadSystemCommonPolicy, "ApplyNpadSystemCommonPolicy"},
{304, &IHidSystemServer::EnableAssigningSingleOnSlSrPress, "EnableAssigningSingleOnSlSrPress"},
{305, &IHidSystemServer::DisableAssigningSingleOnSlSrPress, "DisableAssigningSingleOnSlSrPress"},
{306, &IHidSystemServer::GetLastActiveNpad, "GetLastActiveNpad"},
{307, nullptr, "GetNpadSystemExtStyle"},
{308, &IHidSystemServer::ApplyNpadSystemCommonPolicyFull, "ApplyNpadSystemCommonPolicyFull"},
{309, nullptr, "GetNpadFullKeyGripColor"},
{310, &IHidSystemServer::GetMaskedSupportedNpadStyleSet, "GetMaskedSupportedNpadStyleSet"},
{311, nullptr, "SetNpadPlayerLedBlinkingDevice"},
{312, &IHidSystemServer::SetSupportedNpadStyleSetAll, "SetSupportedNpadStyleSetAll"},
{313, &IHidSystemServer::GetNpadCaptureButtonAssignment, "GetNpadCaptureButtonAssignment"},
{314, nullptr, "GetAppletFooterUiType"},
{315, nullptr, "GetAppletDetailedUiType"},
{316, nullptr, "GetNpadInterfaceType"},
{317, nullptr, "GetNpadLeftRightInterfaceType"},
{318, nullptr, "HasBattery"},
{319, nullptr, "HasLeftRightBattery"},
{321, nullptr/*&IHidSystemServer::GetUniquePadsFromNpad*/, "GetUniquePadsFromNpad"},
{322, nullptr, "GetIrSensorState"},
{323, nullptr, "GetXcdHandleForNpadWithIrSensor"},
{324, nullptr, "GetUniquePadButtonSet"},
{325, nullptr, "GetUniquePadColor"},
{326, nullptr, "GetUniquePadAppletDetailedUiType"},
{327, nullptr, "GetAbstractedPadIdDataFromNpad"},
{328, nullptr, "AttachAbstractedPadToNpad"},
{329, nullptr, "DetachAbstractedPadAll"},
{330, nullptr, "CheckAbstractedPadConnection"},
{500, nullptr, "SetAppletResourceUserId"},
{501, nullptr, "RegisterAppletResourceUserId"},
{502, nullptr, "UnregisterAppletResourceUserId"},
{503, nullptr, "EnableAppletToGetInput"},
{504, nullptr, "SetAruidValidForVibration"},
{505, nullptr, "EnableAppletToGetSixAxisSensor"},
{506, nullptr, "EnableAppletToGetPadInput"},
{507, nullptr, "EnableAppletToGetTouchScreen"},
{510, &IHidSystemServer::SetVibrationMasterVolume, "SetVibrationMasterVolume"},
{511, &IHidSystemServer::GetVibrationMasterVolume, "GetVibrationMasterVolume"},
{512, nullptr, "BeginPermitVibrationSession"},
{513, nullptr, "EndPermitVibrationSession"},
{514, nullptr, "Unknown514"},
{520, nullptr, "EnableHandheldHids"},
{521, nullptr, "DisableHandheldHids"},
{522, nullptr, "SetJoyConRailEnabled"},
{523, nullptr, "IsJoyConRailEnabled"},
{524, nullptr, "IsHandheldHidsEnabled"},
{525, nullptr, "IsJoyConAttachedOnAllRail"},
{540, nullptr, "AcquirePlayReportControllerUsageUpdateEvent"},
{541, nullptr, "GetPlayReportControllerUsages"},
{542, nullptr, "AcquirePlayReportRegisteredDeviceUpdateEvent"},
{543, nullptr, "GetRegisteredDevicesOld"},
{544, nullptr, "AcquireConnectionTriggerTimeoutEvent"},
{545, nullptr, "SendConnectionTrigger"},
{546, nullptr, "AcquireDeviceRegisteredEventForControllerSupport"},
{547, nullptr, "GetAllowedBluetoothLinksCount"},
{548, nullptr, "GetRegisteredDevices"},
{549, nullptr, "GetConnectableRegisteredDevices"},
{700, nullptr, "ActivateUniquePad"},
{702, nullptr, "AcquireUniquePadConnectionEventHandle"},
{703, nullptr, "GetUniquePadIds"},
{751, nullptr/*&IHidSystemServer::AcquireJoyDetachOnBluetoothOffEventHandle*/, "AcquireJoyDetachOnBluetoothOffEventHandle"},
{800, nullptr, "ListSixAxisSensorHandles"},
{801, nullptr, "IsSixAxisSensorUserCalibrationSupported"},
{802, nullptr, "ResetSixAxisSensorCalibrationValues"},
{803, nullptr, "StartSixAxisSensorUserCalibration"},
{804, nullptr, "CancelSixAxisSensorUserCalibration"},
{805, nullptr, "GetUniquePadBluetoothAddress"},
{806, nullptr, "DisconnectUniquePad"},
{807, nullptr, "GetUniquePadType"},
{808, nullptr, "GetUniquePadInterface"},
{809, nullptr, "GetUniquePadSerialNumber"},
{810, nullptr, "GetUniquePadControllerNumber"},
{811, nullptr, "GetSixAxisSensorUserCalibrationStage"},
{812, nullptr, "GetConsoleUniqueSixAxisSensorHandle"},
{821, nullptr, "StartAnalogStickManualCalibration"},
{822, nullptr, "RetryCurrentAnalogStickManualCalibrationStage"},
{823, nullptr, "CancelAnalogStickManualCalibration"},
{824, nullptr, "ResetAnalogStickManualCalibration"},
{825, nullptr, "GetAnalogStickState"},
{826, nullptr, "GetAnalogStickManualCalibrationStage"},
{827, nullptr, "IsAnalogStickButtonPressed"},
{828, nullptr, "IsAnalogStickInReleasePosition"},
{829, nullptr, "IsAnalogStickInCircumference"},
{830, nullptr, "SetNotificationLedPattern"},
{831, nullptr, "SetNotificationLedPatternWithTimeout"},
{832, nullptr, "PrepareHidsForNotificationWake"},
{850, nullptr/*&IHidSystemServer::IsUsbFullKeyControllerEnabled*/, "IsUsbFullKeyControllerEnabled"},
{851, nullptr, "EnableUsbFullKeyController"},
{852, nullptr, "IsUsbConnected"},
{870, nullptr, "IsHandheldButtonPressedOnConsoleMode"},
{900, nullptr, "ActivateInputDetector"},
{901, nullptr, "NotifyInputDetector"},
{1000, nullptr, "InitializeFirmwareUpdate"},
{1001, nullptr, "GetFirmwareVersion"},
{1002, nullptr, "GetAvailableFirmwareVersion"},
{1003, nullptr, "IsFirmwareUpdateAvailable"},
{1004, nullptr, "CheckFirmwareUpdateRequired"},
{1005, nullptr, "StartFirmwareUpdate"},
{1006, nullptr, "AbortFirmwareUpdate"},
{1007, nullptr, "GetFirmwareUpdateState"},
{1008, nullptr, "ActivateAudioControl"},
{1009, nullptr, "AcquireAudioControlEventHandle"},
{1010, nullptr, "GetAudioControlStates"},
{1011, nullptr, "DeactivateAudioControl"},
{1050, nullptr, "IsSixAxisSensorAccurateUserCalibrationSupported"},
{1051, nullptr, "StartSixAxisSensorAccurateUserCalibration"},
{1052, nullptr, "CancelSixAxisSensorAccurateUserCalibration"},
{1053, nullptr, "GetSixAxisSensorAccurateUserCalibrationState"},
{1100, nullptr, "GetHidbusSystemServiceObject"},
{1120, nullptr, "SetFirmwareHotfixUpdateSkipEnabled"},
{1130, nullptr, "InitializeUsbFirmwareUpdate"},
{1131, nullptr, "FinalizeUsbFirmwareUpdate"},
{1132, nullptr, "CheckUsbFirmwareUpdateRequired"},
{1133, nullptr, "StartUsbFirmwareUpdate"},
{1134, nullptr, "GetUsbFirmwareUpdateState"},
{1150, nullptr, "SetTouchScreenMagnification"},
{1151, nullptr, "GetTouchScreenFirmwareVersion"},
{1152, &IHidSystemServer::SetTouchScreenDefaultConfiguration, "SetTouchScreenDefaultConfiguration"},
{1153, &IHidSystemServer::GetTouchScreenDefaultConfiguration, "GetTouchScreenDefaultConfiguration"},
{1154, nullptr, "IsFirmwareAvailableForNotification"},
{1155, nullptr, "SetForceHandheldStyleVibration"},
{1156, nullptr, "SendConnectionTriggerWithoutTimeoutEvent"},
{1157, nullptr, "CancelConnectionTrigger"},
{1200, nullptr, "IsButtonConfigSupported"},
{1201, nullptr, "IsButtonConfigEmbeddedSupported"},
{1202, nullptr, "DeleteButtonConfig"},
{1203, nullptr, "DeleteButtonConfigEmbedded"},
{1204, nullptr, "SetButtonConfigEnabled"},
{1205, nullptr, "SetButtonConfigEmbeddedEnabled"},
{1206, nullptr, "IsButtonConfigEnabled"},
{1207, nullptr, "IsButtonConfigEmbeddedEnabled"},
{1208, nullptr, "SetButtonConfigEmbedded"},
{1209, nullptr, "SetButtonConfigFull"},
{1210, nullptr, "SetButtonConfigLeft"},
{1211, nullptr, "SetButtonConfigRight"},
{1212, nullptr, "GetButtonConfigEmbedded"},
{1213, nullptr, "GetButtonConfigFull"},
{1214, nullptr, "GetButtonConfigLeft"},
{1215, nullptr, "GetButtonConfigRight"},
{1250, nullptr, "IsCustomButtonConfigSupported"},
{1251, nullptr, "IsDefaultButtonConfigEmbedded"},
{1252, nullptr, "IsDefaultButtonConfigFull"},
{1253, nullptr, "IsDefaultButtonConfigLeft"},
{1254, nullptr, "IsDefaultButtonConfigRight"},
{1255, nullptr, "IsButtonConfigStorageEmbeddedEmpty"},
{1256, nullptr, "IsButtonConfigStorageFullEmpty"},
{1257, nullptr, "IsButtonConfigStorageLeftEmpty"},
{1258, nullptr, "IsButtonConfigStorageRightEmpty"},
{1259, nullptr, "GetButtonConfigStorageEmbeddedDeprecated"},
{1260, nullptr, "GetButtonConfigStorageFullDeprecated"},
{1261, nullptr, "GetButtonConfigStorageLeftDeprecated"},
{1262, nullptr, "GetButtonConfigStorageRightDeprecated"},
{1263, nullptr, "SetButtonConfigStorageEmbeddedDeprecated"},
{1264, nullptr, "SetButtonConfigStorageFullDeprecated"},
{1265, nullptr, "SetButtonConfigStorageLeftDeprecated"},
{1266, nullptr, "SetButtonConfigStorageRightDeprecated"},
{1267, nullptr, "DeleteButtonConfigStorageEmbedded"},
{1268, nullptr, "DeleteButtonConfigStorageFull"},
{1269, nullptr, "DeleteButtonConfigStorageLeft"},
{1270, nullptr, "DeleteButtonConfigStorageRight"},
{1271, nullptr, "IsUsingCustomButtonConfig"},
{1272, nullptr, "IsAnyCustomButtonConfigEnabled"},
{1273, nullptr, "SetAllCustomButtonConfigEnabled"},
{1274, nullptr, "SetDefaultButtonConfig"},
{1275, nullptr, "SetAllDefaultButtonConfig"},
{1276, nullptr, "SetHidButtonConfigEmbedded"},
{1277, nullptr, "SetHidButtonConfigFull"},
{1278, nullptr, "SetHidButtonConfigLeft"},
{1279, nullptr, "SetHidButtonConfigRight"},
{1280, nullptr, "GetHidButtonConfigEmbedded"},
{1281, nullptr, "GetHidButtonConfigFull"},
{1282, nullptr, "GetHidButtonConfigLeft"},
{1283, nullptr, "GetHidButtonConfigRight"},
{1284, nullptr, "GetButtonConfigStorageEmbedded"},
{1285, nullptr, "GetButtonConfigStorageFull"},
{1286, nullptr, "GetButtonConfigStorageLeft"},
{1287, nullptr, "GetButtonConfigStorageRight"},
{1288, nullptr, "SetButtonConfigStorageEmbedded"},
{1289, nullptr, "SetButtonConfigStorageFull"},
{1290, nullptr, "DeleteButtonConfigStorageRight"},
{1291, nullptr, "DeleteButtonConfigStorageRight"},
};
// clang-format on
RegisterHandlers(functions);
}
IHidSystemServer::~IHidSystemServer() = default;
void IHidSystemServer::ApplyNpadSystemCommonPolicy(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto applet_resource_user_id{rp.Pop<u64>()};
LOG_INFO(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
GetResourceManager()->GetNpad()->ApplyNpadSystemCommonPolicy(applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void IHidSystemServer::EnableAssigningSingleOnSlSrPress(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto applet_resource_user_id{rp.Pop<u64>()};
LOG_INFO(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
GetResourceManager()->GetNpad()->AssigningSingleOnSlSrPress(applet_resource_user_id, true);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void IHidSystemServer::DisableAssigningSingleOnSlSrPress(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto applet_resource_user_id{rp.Pop<u64>()};
LOG_INFO(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
GetResourceManager()->GetNpad()->AssigningSingleOnSlSrPress(applet_resource_user_id, false);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void IHidSystemServer::GetLastActiveNpad(HLERequestContext& ctx) {
NpadIdType npad_id{};
const Result result = GetResourceManager()->GetNpad()->GetLastActiveNpad(npad_id);
LOG_DEBUG(Service_HID, "called, npad_id={}", npad_id);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(result);
// rb.PushEnum(ResultSuccess);
}
void IHidSystemServer::ApplyNpadSystemCommonPolicyFull(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto applet_resource_user_id{rp.Pop<u64>()};
LOG_INFO(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
GetResourceManager()->GetNpad()->ApplyNpadSystemCommonPolicyFull(applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void IHidSystemServer::GetMaskedSupportedNpadStyleSet(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto applet_resource_user_id{rp.Pop<u64>()};
LOG_INFO(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
NpadStyleSet supported_styleset{};
const auto& npad = GetResourceManager()->GetNpad();
const Result result =
npad->GetMaskedSupportedNpadStyleSet(applet_resource_user_id, supported_styleset);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(result);
rb.PushEnum(supported_styleset);
}
void IHidSystemServer::SetSupportedNpadStyleSetAll(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto applet_resource_user_id{rp.Pop<u64>()};
LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
const auto& npad = GetResourceManager()->GetNpad();
const auto result = npad->SetSupportedNpadStyleSet(applet_resource_user_id, NpadStyleSet::All);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
}
void IHidSystemServer::GetNpadCaptureButtonAssignment(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto applet_resource_user_id{rp.Pop<u64>()};
LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
const auto size = ctx.GetWriteBufferNumElements<NpadButton>();
std::vector<NpadButton> button_assignment(size);
const auto& npad = GetResourceManager()->GetNpad();
const auto reply_size =
npad->GetNpadCaptureButtonAssignment(applet_resource_user_id, button_assignment);
ctx.WriteBuffer(button_assignment);
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(ResultSuccess);
rb.Push(reply_size);
}
void IHidSystemServer::SetVibrationMasterVolume(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto volume{rp.Pop<float>()};
LOG_DEBUG(Service_HID, "called, volume={}", volume);
const Result result = GetResourceManager()->GetNpad()->SetVibrationMasterVolume(volume);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
}
void IHidSystemServer::GetVibrationMasterVolume(HLERequestContext& ctx) {
float volume{};
const Result result = GetResourceManager()->GetNpad()->GetVibrationMasterVolume(volume);
LOG_DEBUG(Service_HID, "called, volume={}", volume);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(result);
rb.Push(volume);
}
void IHidSystemServer::SetTouchScreenDefaultConfiguration(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
auto touch_screen_configuration{rp.PopRaw<TouchScreenConfigurationForNx>()};
LOG_WARNING(Service_HID, "(STUBBED) called, mode={}", touch_screen_configuration.mode);
switch (touch_screen_configuration.mode) {
case TouchScreenModeForNx::UseSystemSetting:
case TouchScreenModeForNx::Finger:
case TouchScreenModeForNx::Heat2:
break;
default:
touch_screen_configuration.mode = TouchScreenModeForNx::UseSystemSetting;
} /*
const Result result = GetResourceManager()->GetTouchScreen()->SetTouchScreenConfiguration(
touch_screen_configuration);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);*/
}
void IHidSystemServer::GetTouchScreenDefaultConfiguration(HLERequestContext& ctx) {
LOG_WARNING(Service_HID, "(STUBBED) called");
// TouchScreenConfigurationForNx touch_screen_configuration{};
// const Result result = GetResourceManager()->GetTouchScreen()->GetDefaultConfiguration(
// touch_screen_configuration.mode);
// switch (touch_screen_configuration.mode) {
// case TouchScreenModeForNx::UseSystemSetting:
// case TouchScreenModeForNx::Finger:
// case TouchScreenModeForNx::Heat2:
// break;
// default:
// touch_screen_configuration.mode = TouchScreenModeForNx::UseSystemSetting;
// }
// IPC::ResponseBuilder rb{ctx, 6};
// rb.Push(ResultSuccess);
// rb.PushRaw(touch_screen_configuration);
}
std::shared_ptr<ResourceManager> IHidSystemServer::GetResourceManager() {
resource_manager->VerifiyInitalization();
return resource_manager;
}
} // namespace Service::HID

View File

@@ -1,39 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include "core/hle/service/service.h"
namespace Core {
class System;
}
namespace Service::HID {
class ResourceManager;
class IHidSystemServer final : public ServiceFramework<IHidSystemServer> {
public:
explicit IHidSystemServer(Core::System& system_, std::shared_ptr<ResourceManager> resource);
~IHidSystemServer() override;
private:
void ApplyNpadSystemCommonPolicy(HLERequestContext& ctx);
void EnableAssigningSingleOnSlSrPress(HLERequestContext& ctx);
void DisableAssigningSingleOnSlSrPress(HLERequestContext& ctx);
void GetLastActiveNpad(HLERequestContext& ctx);
void ApplyNpadSystemCommonPolicyFull(HLERequestContext& ctx);
void GetMaskedSupportedNpadStyleSet(HLERequestContext& ctx);
void SetSupportedNpadStyleSetAll(HLERequestContext& ctx);
void GetNpadCaptureButtonAssignment(HLERequestContext& ctx);
void SetVibrationMasterVolume(HLERequestContext& ctx);
void GetVibrationMasterVolume(HLERequestContext& ctx);
void SetTouchScreenDefaultConfiguration(HLERequestContext& ctx);
void GetTouchScreenDefaultConfiguration(HLERequestContext& ctx);
std::shared_ptr<ResourceManager> GetResourceManager();
std::shared_ptr<ResourceManager> resource_manager = nullptr;
};
} // namespace Service::HID

View File

@@ -1,132 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include "core/hle/service/hid/hid_result.h"
#include "core/hle/service/hid/hid_types.h"
namespace Service::HID {
constexpr bool IsNpadIdValid(const NpadIdType npad_id) {
switch (npad_id) {
case NpadIdType::Player1:
case NpadIdType::Player2:
case NpadIdType::Player3:
case NpadIdType::Player4:
case NpadIdType::Player5:
case NpadIdType::Player6:
case NpadIdType::Player7:
case NpadIdType::Player8:
case NpadIdType::Other:
case NpadIdType::Handheld:
return true;
default:
return false;
}
}
constexpr Result IsVibrationHandleValid(const VibrationDeviceHandle& handle) {
switch (handle.npad_style_index) {
case NpadStyleIndex::FullKey:
case NpadStyleIndex::Handheld:
case NpadStyleIndex::JoyconDual:
case NpadStyleIndex::JoyconLeft:
case NpadStyleIndex::JoyconRight:
case NpadStyleIndex::GameCube:
case NpadStyleIndex::N64:
case NpadStyleIndex::SystemExt:
case NpadStyleIndex::System:
// These support vibration
break;
default:
return ResultVibrationInvalidStyleIndex;
}
if (IsNpadIdValid(static_cast<NpadIdType>(handle.npad_id))) {
return ResultVibrationInvalidNpadId;
}
if (handle.device_index >= DeviceIndex::MaxDeviceInfo) {
return ResultVibrationDeviceIndexOutOfRange;
}
return ResultSuccess;
}
/// Converts a NpadIdType to an array index.
constexpr size_t NpadIdTypeToIndex(NpadIdType npad_id_type) {
switch (npad_id_type) {
case NpadIdType::Player1:
return 0;
case NpadIdType::Player2:
return 1;
case NpadIdType::Player3:
return 2;
case NpadIdType::Player4:
return 3;
case NpadIdType::Player5:
return 4;
case NpadIdType::Player6:
return 5;
case NpadIdType::Player7:
return 6;
case NpadIdType::Player8:
return 7;
case NpadIdType::Handheld:
return 8;
case NpadIdType::Other:
return 9;
default:
return 8;
}
}
/// Converts an array index to a NpadIdType
constexpr NpadIdType IndexToNpadIdType(size_t index) {
switch (index) {
case 0:
return NpadIdType::Player1;
case 1:
return NpadIdType::Player2;
case 2:
return NpadIdType::Player3;
case 3:
return NpadIdType::Player4;
case 4:
return NpadIdType::Player5;
case 5:
return NpadIdType::Player6;
case 6:
return NpadIdType::Player7;
case 7:
return NpadIdType::Player8;
case 8:
return NpadIdType::Handheld;
case 9:
return NpadIdType::Other;
default:
return NpadIdType::Invalid;
}
}
constexpr NpadStyleSet GetStylesetByIndex(std::size_t index) {
switch (index) {
case 0:
return NpadStyleSet::Fullkey;
case 1:
return NpadStyleSet::Handheld;
case 2:
return NpadStyleSet::JoyDual;
case 3:
return NpadStyleSet::JoyLeft;
case 4:
return NpadStyleSet::JoyRight;
case 5:
return NpadStyleSet::Palma;
default:
return NpadStyleSet::None;
}
}
} // namespace Service::HID

View File

@@ -1,35 +1,523 @@
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/logging/log.h"
#include "common/settings.h"
#include "core/core.h"
#include "core/core_timing.h"
#include "core/hid/hid_types.h"
#include "core/hle/kernel/k_event.h"
#include "core/hle/kernel/k_readable_event.h"
#include "core/hle/kernel/k_shared_memory.h"
#include "core/hle/kernel/k_transfer_memory.h"
#include "core/hle/service/hid/hidbus.h"
#include "core/hle/service/hid/hidbus/ringcon.h"
#include "core/hle/service/hid/hidbus/starlink.h"
#include "core/hle/service/hid/hidbus/stubbed.h"
#include "core/hle/service/ipc_helpers.h"
#include "core/hle/service/service.h"
#include "core/memory.h"
namespace Service::HID {
// (15ms, 66Hz)
constexpr auto hidbus_update_ns = std::chrono::nanoseconds{15 * 1000 * 1000};
IHidbusServer::IHidbusServer(Core::System& system_) : ServiceFramework{system_, "hidbus"} {
HidBus::HidBus(Core::System& system_)
: ServiceFramework{system_, "hidbus"}, service_context{system_, service_name} {
// clang-format off
static const FunctionInfo functions[] = {
{1, nullptr, "GetBusHandle"},
{2, nullptr, "IsExternalDeviceConnected"},
{3, nullptr, "Initialize"},
{4, nullptr, "Finalize"},
{5, nullptr, "EnableExternalDevice"},
{6, nullptr, "GetExternalDeviceId"},
{7, nullptr, "SendCommandAsync"},
{8, nullptr, "GetSendCommandAsynceResult"},
{9, nullptr, "SetEventForSendCommandAsycResult"},
{10, nullptr, "GetSharedMemoryHandle"},
{11, nullptr, "EnableJoyPollingReceiveMode"},
{12, nullptr, "DisableJoyPollingReceiveMode"},
{1, &HidBus::GetBusHandle, "GetBusHandle"},
{2, &HidBus::IsExternalDeviceConnected, "IsExternalDeviceConnected"},
{3, &HidBus::Initialize, "Initialize"},
{4, &HidBus::Finalize, "Finalize"},
{5, &HidBus::EnableExternalDevice, "EnableExternalDevice"},
{6, &HidBus::GetExternalDeviceId, "GetExternalDeviceId"},
{7, &HidBus::SendCommandAsync, "SendCommandAsync"},
{8, &HidBus::GetSendCommandAsynceResult, "GetSendCommandAsynceResult"},
{9, &HidBus::SetEventForSendCommandAsycResult, "SetEventForSendCommandAsycResult"},
{10, &HidBus::GetSharedMemoryHandle, "GetSharedMemoryHandle"},
{11, &HidBus::EnableJoyPollingReceiveMode, "EnableJoyPollingReceiveMode"},
{12, &HidBus::DisableJoyPollingReceiveMode, "DisableJoyPollingReceiveMode"},
{13, nullptr, "GetPollingData"},
{14, nullptr, "SetStatusManagerType"},
{14, &HidBus::SetStatusManagerType, "SetStatusManagerType"},
};
// clang-format on
RegisterHandlers(functions);
// Register update callbacks
hidbus_update_event = Core::Timing::CreateEvent(
"Hidbus::UpdateCallback",
[this](std::uintptr_t user_data, s64 time,
std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
const auto guard = LockService();
UpdateHidbus(user_data, ns_late);
return std::nullopt;
});
system_.CoreTiming().ScheduleLoopingEvent(hidbus_update_ns, hidbus_update_ns,
hidbus_update_event);
}
IHidbusServer::~IHidbusServer() = default;
HidBus::~HidBus() {
system.CoreTiming().UnscheduleEvent(hidbus_update_event, 0);
}
} // namespace Service::HID
void HidBus::UpdateHidbus(std::uintptr_t user_data, 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) {
continue;
}
auto& device = devices[i].device;
device->OnUpdate();
auto& cur_entry = hidbus_status.entries[devices[i].handle.internal_index];
cur_entry.is_polling_mode = device->IsPollingMode();
cur_entry.polling_mode = device->GetPollingMode();
cur_entry.is_enabled = device->IsEnabled();
u8* shared_memory = system.Kernel().GetHidBusSharedMem().GetPointer();
std::memcpy(shared_memory + (i * sizeof(HidbusStatusManagerEntry)), &hidbus_status,
sizeof(HidbusStatusManagerEntry));
}
}
}
std::optional<std::size_t> HidBus::GetDeviceIndexFromHandle(BusHandle handle) const {
for (std::size_t i = 0; i < devices.size(); ++i) {
const auto& device_handle = devices[i].handle;
if (handle.abstracted_pad_id == device_handle.abstracted_pad_id &&
handle.internal_index == device_handle.internal_index &&
handle.player_number == device_handle.player_number &&
handle.bus_type_id == device_handle.bus_type_id &&
handle.is_valid == device_handle.is_valid) {
return i;
}
}
return std::nullopt;
}
void HidBus::GetBusHandle(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
Core::HID::NpadIdType npad_id;
INSERT_PADDING_WORDS_NOINIT(1);
BusType bus_type;
u64 applet_resource_user_id;
};
static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size.");
const auto parameters{rp.PopRaw<Parameters>()};
LOG_INFO(Service_HID, "called, npad_id={}, bus_type={}, applet_resource_user_id={}",
parameters.npad_id, parameters.bus_type, parameters.applet_resource_user_id);
bool is_handle_found = 0;
std::size_t handle_index = 0;
for (std::size_t i = 0; i < devices.size(); i++) {
const auto& handle = devices[i].handle;
if (!handle.is_valid) {
continue;
}
if (static_cast<Core::HID::NpadIdType>(handle.player_number) == parameters.npad_id &&
handle.bus_type_id == static_cast<u8>(parameters.bus_type)) {
is_handle_found = true;
handle_index = i;
break;
}
}
// Handle not found. Create a new one
if (!is_handle_found) {
for (std::size_t i = 0; i < devices.size(); i++) {
if (devices[i].handle.is_valid) {
continue;
}
devices[i].handle = {
.abstracted_pad_id = static_cast<u8>(i),
.internal_index = static_cast<u8>(i),
.player_number = static_cast<u8>(parameters.npad_id),
.bus_type_id = static_cast<u8>(parameters.bus_type),
.is_valid = true,
};
handle_index = i;
break;
}
}
struct OutData {
bool is_valid;
INSERT_PADDING_BYTES(7);
BusHandle handle;
};
static_assert(sizeof(OutData) == 0x10, "OutData has incorrect size.");
const OutData out_data{
.is_valid = true,
.handle = devices[handle_index].handle,
};
IPC::ResponseBuilder rb{ctx, 6};
rb.Push(ResultSuccess);
rb.PushRaw(out_data);
}
void HidBus::IsExternalDeviceConnected(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto bus_handle_{rp.PopRaw<BusHandle>()};
LOG_INFO(Service_HID,
"Called, abstracted_pad_id={}, bus_type={}, internal_index={}, "
"player_number={}, is_valid={}",
bus_handle_.abstracted_pad_id, bus_handle_.bus_type_id, bus_handle_.internal_index,
bus_handle_.player_number, bus_handle_.is_valid);
const auto device_index = GetDeviceIndexFromHandle(bus_handle_);
if (device_index) {
const auto& device = devices[device_index.value()].device;
const bool is_attached = device->IsDeviceActivated();
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.Push(is_attached);
return;
}
LOG_ERROR(Service_HID, "Invalid handle");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultUnknown);
return;
}
void HidBus::Initialize(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto bus_handle_{rp.PopRaw<BusHandle>()};
const auto applet_resource_user_id{rp.Pop<u64>()};
LOG_INFO(Service_HID,
"called, abstracted_pad_id={} bus_type={} internal_index={} "
"player_number={} is_valid={}, applet_resource_user_id={}",
bus_handle_.abstracted_pad_id, bus_handle_.bus_type_id, bus_handle_.internal_index,
bus_handle_.player_number, bus_handle_.is_valid, applet_resource_user_id);
is_hidbus_enabled = true;
const auto device_index = GetDeviceIndexFromHandle(bus_handle_);
if (device_index) {
const auto entry_index = devices[device_index.value()].handle.internal_index;
auto& cur_entry = hidbus_status.entries[entry_index];
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()].device->ActivateDevice();
cur_entry.is_in_focus = true;
cur_entry.is_connected = true;
cur_entry.is_connected_result = ResultSuccess;
cur_entry.is_enabled = false;
cur_entry.is_polling_mode = false;
} else {
MakeDevice<HidbusStubbed>(bus_handle_);
devices[device_index.value()].is_device_initializated = true;
cur_entry.is_in_focus = true;
cur_entry.is_connected = false;
cur_entry.is_connected_result = ResultSuccess;
cur_entry.is_enabled = false;
cur_entry.is_polling_mode = false;
}
std::memcpy(system.Kernel().GetHidBusSharedMem().GetPointer(), &hidbus_status,
sizeof(hidbus_status));
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
return;
}
LOG_ERROR(Service_HID, "Invalid handle");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultUnknown);
return;
}
void HidBus::Finalize(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto bus_handle_{rp.PopRaw<BusHandle>()};
const auto applet_resource_user_id{rp.Pop<u64>()};
LOG_INFO(Service_HID,
"called, abstracted_pad_id={}, bus_type={}, internal_index={}, "
"player_number={}, is_valid={}, applet_resource_user_id={}",
bus_handle_.abstracted_pad_id, bus_handle_.bus_type_id, bus_handle_.internal_index,
bus_handle_.player_number, bus_handle_.is_valid, applet_resource_user_id);
const auto device_index = GetDeviceIndexFromHandle(bus_handle_);
if (device_index) {
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;
device->DeactivateDevice();
cur_entry.is_in_focus = true;
cur_entry.is_connected = false;
cur_entry.is_connected_result = ResultSuccess;
cur_entry.is_enabled = false;
cur_entry.is_polling_mode = false;
std::memcpy(system.Kernel().GetHidBusSharedMem().GetPointer(), &hidbus_status,
sizeof(hidbus_status));
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
return;
}
LOG_ERROR(Service_HID, "Invalid handle");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultUnknown);
return;
}
void HidBus::EnableExternalDevice(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
bool enable;
INSERT_PADDING_BYTES_NOINIT(7);
BusHandle bus_handle;
u64 inval;
u64 applet_resource_user_id;
};
static_assert(sizeof(Parameters) == 0x20, "Parameters has incorrect size.");
const auto parameters{rp.PopRaw<Parameters>()};
LOG_DEBUG(Service_HID,
"called, enable={}, abstracted_pad_id={}, bus_type={}, internal_index={}, "
"player_number={}, is_valid={}, inval={}, applet_resource_user_id{}",
parameters.enable, parameters.bus_handle.abstracted_pad_id,
parameters.bus_handle.bus_type_id, parameters.bus_handle.internal_index,
parameters.bus_handle.player_number, parameters.bus_handle.is_valid, parameters.inval,
parameters.applet_resource_user_id);
const auto device_index = GetDeviceIndexFromHandle(parameters.bus_handle);
if (device_index) {
auto& device = devices[device_index.value()].device;
device->Enable(parameters.enable);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
return;
}
LOG_ERROR(Service_HID, "Invalid handle");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultUnknown);
return;
}
void HidBus::GetExternalDeviceId(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto bus_handle_{rp.PopRaw<BusHandle>()};
LOG_DEBUG(Service_HID,
"called, abstracted_pad_id={}, bus_type={}, internal_index={}, player_number={}, "
"is_valid={}",
bus_handle_.abstracted_pad_id, bus_handle_.bus_type_id, bus_handle_.internal_index,
bus_handle_.player_number, bus_handle_.is_valid);
const auto device_index = GetDeviceIndexFromHandle(bus_handle_);
if (device_index) {
const auto& device = devices[device_index.value()].device;
u32 device_id = device->GetDeviceId();
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.Push<u32>(device_id);
return;
}
LOG_ERROR(Service_HID, "Invalid handle");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultUnknown);
return;
}
void HidBus::SendCommandAsync(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto data = ctx.ReadBuffer();
const auto bus_handle_{rp.PopRaw<BusHandle>()};
LOG_DEBUG(Service_HID,
"called, data_size={}, abstracted_pad_id={}, bus_type={}, internal_index={}, "
"player_number={}, is_valid={}",
data.size(), bus_handle_.abstracted_pad_id, bus_handle_.bus_type_id,
bus_handle_.internal_index, bus_handle_.player_number, bus_handle_.is_valid);
const auto device_index = GetDeviceIndexFromHandle(bus_handle_);
if (device_index) {
auto& device = devices[device_index.value()].device;
device->SetCommand(data);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
return;
}
LOG_ERROR(Service_HID, "Invalid handle");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultUnknown);
return;
};
void HidBus::GetSendCommandAsynceResult(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto bus_handle_{rp.PopRaw<BusHandle>()};
LOG_DEBUG(Service_HID,
"called, abstracted_pad_id={}, bus_type={}, internal_index={}, player_number={}, "
"is_valid={}",
bus_handle_.abstracted_pad_id, bus_handle_.bus_type_id, bus_handle_.internal_index,
bus_handle_.player_number, bus_handle_.is_valid);
const auto device_index = GetDeviceIndexFromHandle(bus_handle_);
if (device_index) {
const auto& device = devices[device_index.value()].device;
const std::vector<u8> data = device->GetReply();
const u64 data_size = ctx.WriteBuffer(data);
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(ResultSuccess);
rb.Push<u64>(data_size);
return;
}
LOG_ERROR(Service_HID, "Invalid handle");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultUnknown);
return;
};
void HidBus::SetEventForSendCommandAsycResult(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto bus_handle_{rp.PopRaw<BusHandle>()};
LOG_INFO(Service_HID,
"called, abstracted_pad_id={}, bus_type={}, internal_index={}, player_number={}, "
"is_valid={}",
bus_handle_.abstracted_pad_id, bus_handle_.bus_type_id, bus_handle_.internal_index,
bus_handle_.player_number, bus_handle_.is_valid);
const auto device_index = GetDeviceIndexFromHandle(bus_handle_);
if (device_index) {
const auto& device = devices[device_index.value()].device;
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(ResultSuccess);
rb.PushCopyObjects(device->GetSendCommandAsycEvent());
return;
}
LOG_ERROR(Service_HID, "Invalid handle");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultUnknown);
return;
};
void HidBus::GetSharedMemoryHandle(HLERequestContext& ctx) {
LOG_DEBUG(Service_HID, "called");
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(ResultSuccess);
rb.PushCopyObjects(&system.Kernel().GetHidBusSharedMem());
}
void HidBus::EnableJoyPollingReceiveMode(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto t_mem_size{rp.Pop<u32>()};
const auto t_mem_handle{ctx.GetCopyHandle(0)};
const auto polling_mode_{rp.PopEnum<JoyPollingMode>()};
const auto bus_handle_{rp.PopRaw<BusHandle>()};
ASSERT_MSG(t_mem_size == 0x1000, "t_mem_size is not 0x1000 bytes");
auto t_mem = system.ApplicationProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(
t_mem_handle);
if (t_mem.IsNull()) {
LOG_ERROR(Service_HID, "t_mem is a nullptr for handle=0x{:08X}", t_mem_handle);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultUnknown);
return;
}
ASSERT_MSG(t_mem->GetSize() == 0x1000, "t_mem has incorrect size");
LOG_INFO(Service_HID,
"called, t_mem_handle=0x{:08X}, polling_mode={}, abstracted_pad_id={}, bus_type={}, "
"internal_index={}, player_number={}, is_valid={}",
t_mem_handle, polling_mode_, bus_handle_.abstracted_pad_id, bus_handle_.bus_type_id,
bus_handle_.internal_index, bus_handle_.player_number, bus_handle_.is_valid);
const auto device_index = GetDeviceIndexFromHandle(bus_handle_);
if (device_index) {
auto& device = devices[device_index.value()].device;
device->SetPollingMode(polling_mode_);
device->SetTransferMemoryAddress(t_mem->GetSourceAddress());
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
return;
}
LOG_ERROR(Service_HID, "Invalid handle");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultUnknown);
return;
}
void HidBus::DisableJoyPollingReceiveMode(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto bus_handle_{rp.PopRaw<BusHandle>()};
LOG_INFO(Service_HID,
"called, abstracted_pad_id={}, bus_type={}, internal_index={}, player_number={}, "
"is_valid={}",
bus_handle_.abstracted_pad_id, bus_handle_.bus_type_id, bus_handle_.internal_index,
bus_handle_.player_number, bus_handle_.is_valid);
const auto device_index = GetDeviceIndexFromHandle(bus_handle_);
if (device_index) {
auto& device = devices[device_index.value()].device;
device->DisablePollingMode();
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
return;
}
LOG_ERROR(Service_HID, "Invalid handle");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultUnknown);
return;
}
void HidBus::SetStatusManagerType(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto manager_type{rp.PopEnum<StatusManagerType>()};
LOG_WARNING(Service_HID, "(STUBBED) called, manager_type={}", manager_type);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
};
} // namespace Service::HID

View File

@@ -3,18 +3,127 @@
#pragma once
#include <functional>
#include "core/hle/service/hid/hidbus/hidbus_base.h"
#include "core/hle/service/kernel_helpers.h"
#include "core/hle/service/service.h"
namespace Core::Timing {
struct EventType;
} // namespace Core::Timing
namespace Core {
class System;
}
} // namespace Core
namespace Service::HID {
class IHidbusServer final : public ServiceFramework<IHidbusServer> {
class HidBus final : public ServiceFramework<HidBus> {
public:
explicit IHidbusServer(Core::System& system_);
~IHidbusServer() override;
explicit HidBus(Core::System& system_);
~HidBus() override;
private:
static const std::size_t max_number_of_handles = 0x13;
enum class HidBusDeviceId : std::size_t {
RingController = 0x20,
FamicomRight = 0x21,
Starlink = 0x28,
};
// This is nn::hidbus::detail::StatusManagerType
enum class StatusManagerType : u32 {
None,
Type16,
Type32,
};
// This is nn::hidbus::BusType
enum class BusType : u32 {
LeftJoyRail,
RightJoyRail,
InternalBus, // Lark microphone
MaxBusType,
};
// This is nn::hidbus::BusHandle
struct BusHandle {
u32 abstracted_pad_id;
u8 internal_index;
u8 player_number;
u8 bus_type_id;
bool is_valid;
};
static_assert(sizeof(BusHandle) == 0x8, "BusHandle is an invalid size");
// This is nn::hidbus::JoyPollingReceivedData
struct JoyPollingReceivedData {
std::array<u8, 0x30> data;
u64 out_size;
u64 sampling_number;
};
static_assert(sizeof(JoyPollingReceivedData) == 0x40,
"JoyPollingReceivedData is an invalid size");
struct HidbusStatusManagerEntry {
u8 is_connected{};
INSERT_PADDING_BYTES(0x3);
Result is_connected_result{0};
u8 is_enabled{};
u8 is_in_focus{};
u8 is_polling_mode{};
u8 reserved{};
JoyPollingMode polling_mode{};
INSERT_PADDING_BYTES(0x70); // Unknown
};
static_assert(sizeof(HidbusStatusManagerEntry) == 0x80,
"HidbusStatusManagerEntry is an invalid size");
struct HidbusStatusManager {
std::array<HidbusStatusManagerEntry, max_number_of_handles> entries{};
INSERT_PADDING_BYTES(0x680); // Unused
};
static_assert(sizeof(HidbusStatusManager) <= 0x1000, "HidbusStatusManager is an invalid size");
struct HidbusDevice {
bool is_device_initializated{};
BusHandle handle{};
std::unique_ptr<HidbusBase> device{nullptr};
};
void GetBusHandle(HLERequestContext& ctx);
void IsExternalDeviceConnected(HLERequestContext& ctx);
void Initialize(HLERequestContext& ctx);
void Finalize(HLERequestContext& ctx);
void EnableExternalDevice(HLERequestContext& ctx);
void GetExternalDeviceId(HLERequestContext& ctx);
void SendCommandAsync(HLERequestContext& ctx);
void GetSendCommandAsynceResult(HLERequestContext& ctx);
void SetEventForSendCommandAsycResult(HLERequestContext& ctx);
void GetSharedMemoryHandle(HLERequestContext& ctx);
void EnableJoyPollingReceiveMode(HLERequestContext& ctx);
void DisableJoyPollingReceiveMode(HLERequestContext& ctx);
void SetStatusManagerType(HLERequestContext& ctx);
void UpdateHidbus(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
std::optional<std::size_t> GetDeviceIndexFromHandle(BusHandle handle) const;
template <typename T>
void MakeDevice(BusHandle handle) {
const auto device_index = GetDeviceIndexFromHandle(handle);
if (device_index) {
devices[device_index.value()].device = std::make_unique<T>(system, service_context);
}
}
bool is_hidbus_enabled{false};
HidbusStatusManager hidbus_status{};
std::array<HidbusDevice, max_number_of_handles> devices{};
std::shared_ptr<Core::Timing::EventType> hidbus_update_event;
KernelHelpers::ServiceContext service_context;
};
} // namespace Service::HID
} // namespace Service::HID

View File

@@ -0,0 +1,70 @@
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hid/hid_core.h"
#include "core/hle/kernel/k_event.h"
#include "core/hle/kernel/k_readable_event.h"
#include "core/hle/service/hid/hidbus/hidbus_base.h"
#include "core/hle/service/kernel_helpers.h"
namespace Service::HID {
HidbusBase::HidbusBase(Core::System& system_, KernelHelpers::ServiceContext& service_context_)
: system(system_), service_context(service_context_) {
send_command_async_event = service_context.CreateEvent("hidbus:SendCommandAsyncEvent");
}
HidbusBase::~HidbusBase() = default;
void HidbusBase::ActivateDevice() {
if (is_activated) {
return;
}
is_activated = true;
OnInit();
}
void HidbusBase::DeactivateDevice() {
if (is_activated) {
OnRelease();
}
is_activated = false;
}
bool HidbusBase::IsDeviceActivated() const {
return is_activated;
}
void HidbusBase::Enable(bool enable) {
device_enabled = enable;
}
bool HidbusBase::IsEnabled() const {
return device_enabled;
}
bool HidbusBase::IsPollingMode() const {
return polling_mode_enabled;
}
JoyPollingMode HidbusBase::GetPollingMode() const {
return polling_mode;
}
void HidbusBase::SetPollingMode(JoyPollingMode mode) {
polling_mode = mode;
polling_mode_enabled = true;
}
void HidbusBase::DisablePollingMode() {
polling_mode_enabled = false;
}
void HidbusBase::SetTransferMemoryAddress(Common::ProcessAddress t_mem) {
transfer_memory = t_mem;
}
Kernel::KReadableEvent& HidbusBase::GetSendCommandAsycEvent() const {
return send_command_async_event->GetReadableEvent();
}
} // namespace Service::HID

View File

@@ -0,0 +1,183 @@
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <array>
#include <span>
#include "common/typed_address.h"
#include "core/hle/result.h"
namespace Core {
class System;
}
namespace Kernel {
class KEvent;
class KReadableEvent;
} // namespace Kernel
namespace Service::KernelHelpers {
class ServiceContext;
}
namespace Service::HID {
// This is nn::hidbus::JoyPollingMode
enum class JoyPollingMode : u32 {
SixAxisSensorDisable,
SixAxisSensorEnable,
ButtonOnly,
};
struct DataAccessorHeader {
Result result{ResultUnknown};
INSERT_PADDING_WORDS(0x1);
std::array<u8, 0x18> unused{};
u64 latest_entry{};
u64 total_entries{};
};
static_assert(sizeof(DataAccessorHeader) == 0x30, "DataAccessorHeader is an invalid size");
struct JoyDisableSixAxisPollingData {
std::array<u8, 0x26> data;
u8 out_size;
INSERT_PADDING_BYTES(0x1);
u64 sampling_number;
};
static_assert(sizeof(JoyDisableSixAxisPollingData) == 0x30,
"JoyDisableSixAxisPollingData is an invalid size");
struct JoyEnableSixAxisPollingData {
std::array<u8, 0x8> data;
u8 out_size;
INSERT_PADDING_BYTES(0x7);
u64 sampling_number;
};
static_assert(sizeof(JoyEnableSixAxisPollingData) == 0x18,
"JoyEnableSixAxisPollingData is an invalid size");
struct JoyButtonOnlyPollingData {
std::array<u8, 0x2c> data;
u8 out_size;
INSERT_PADDING_BYTES(0x3);
u64 sampling_number;
};
static_assert(sizeof(JoyButtonOnlyPollingData) == 0x38,
"JoyButtonOnlyPollingData is an invalid size");
struct JoyDisableSixAxisPollingEntry {
u64 sampling_number;
JoyDisableSixAxisPollingData polling_data;
};
static_assert(sizeof(JoyDisableSixAxisPollingEntry) == 0x38,
"JoyDisableSixAxisPollingEntry is an invalid size");
struct JoyEnableSixAxisPollingEntry {
u64 sampling_number;
JoyEnableSixAxisPollingData polling_data;
};
static_assert(sizeof(JoyEnableSixAxisPollingEntry) == 0x20,
"JoyEnableSixAxisPollingEntry is an invalid size");
struct JoyButtonOnlyPollingEntry {
u64 sampling_number;
JoyButtonOnlyPollingData polling_data;
};
static_assert(sizeof(JoyButtonOnlyPollingEntry) == 0x40,
"JoyButtonOnlyPollingEntry is an invalid size");
struct JoyDisableSixAxisDataAccessor {
DataAccessorHeader header{};
std::array<JoyDisableSixAxisPollingEntry, 0xb> entries{};
};
static_assert(sizeof(JoyDisableSixAxisDataAccessor) == 0x298,
"JoyDisableSixAxisDataAccessor is an invalid size");
struct JoyEnableSixAxisDataAccessor {
DataAccessorHeader header{};
std::array<JoyEnableSixAxisPollingEntry, 0xb> entries{};
};
static_assert(sizeof(JoyEnableSixAxisDataAccessor) == 0x190,
"JoyEnableSixAxisDataAccessor is an invalid size");
struct ButtonOnlyPollingDataAccessor {
DataAccessorHeader header;
std::array<JoyButtonOnlyPollingEntry, 0xb> entries;
};
static_assert(sizeof(ButtonOnlyPollingDataAccessor) == 0x2F0,
"ButtonOnlyPollingDataAccessor is an invalid size");
class HidbusBase {
public:
explicit HidbusBase(Core::System& system_, KernelHelpers::ServiceContext& service_context_);
virtual ~HidbusBase();
void ActivateDevice();
void DeactivateDevice();
bool IsDeviceActivated() const;
// Enables/disables the device
void Enable(bool enable);
// returns true if device is enabled
bool IsEnabled() const;
// returns true if polling mode is enabled
bool IsPollingMode() const;
// returns polling mode
JoyPollingMode GetPollingMode() const;
// Sets and enables JoyPollingMode
void SetPollingMode(JoyPollingMode mode);
// Disables JoyPollingMode
void DisablePollingMode();
// Called on EnableJoyPollingReceiveMode
void SetTransferMemoryAddress(Common::ProcessAddress t_mem);
Kernel::KReadableEvent& GetSendCommandAsycEvent() const;
virtual void OnInit() {}
virtual void OnRelease() {}
// Updates device transfer memory
virtual void OnUpdate() {}
// Returns the device ID of the joycon
virtual u8 GetDeviceId() const {
return {};
}
// Assigns a command from data
virtual bool SetCommand(std::span<const u8> data) {
return {};
}
// Returns a reply from a command
virtual std::vector<u8> GetReply() const {
return {};
}
protected:
bool is_activated{};
bool device_enabled{};
bool polling_mode_enabled{};
JoyPollingMode polling_mode = {};
// TODO(German77): All data accessors need to be replaced with a ring lifo object
JoyDisableSixAxisDataAccessor disable_sixaxis_data{};
JoyEnableSixAxisDataAccessor enable_sixaxis_data{};
ButtonOnlyPollingDataAccessor button_only_data{};
Common::ProcessAddress transfer_memory{};
Core::System& system;
Kernel::KEvent* send_command_async_event;
KernelHelpers::ServiceContext& service_context;
};
} // namespace Service::HID

View File

@@ -1,20 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include "core/hle/result.h"
namespace Service::HIDBUS {
constexpr Result ResultNotEnabled{ErrorModule::HIDBUS, 1};
constexpr Result ResultControllerUpdateFailed{ErrorModule::HIDBUS, 3};
constexpr Result ResultInvalidBusHandle{ErrorModule::HIDBUS, 4};
constexpr Result ResultNoDeviceConnected{ErrorModule::HIDBUS, 5};
constexpr Result ResultUknown6{ErrorModule::HIDBUS, 6};
constexpr Result ResultPollingRecieveDataNotAvailable{ErrorModule::HIDBUS, 7};
constexpr Result ResultDeviceEnabledNotSet{ErrorModule::HIDBUS, 8};
constexpr Result ResultBusHandleAlreadyInitialized{ErrorModule::HIDBUS, 10};
constexpr Result ResultInvalidBusType{ErrorModule::HIDBUS, 11};
} // namespace Service::HIDBUS

View File

@@ -1,11 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include <array>
#include "common/common_funcs.h"
#include "common/common_types.h"
namespace Service::HIDBUS {} // namespace Service::HIDBUS

View File

@@ -0,0 +1,292 @@
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/core.h"
#include "core/hid/emulated_controller.h"
#include "core/hid/hid_core.h"
#include "core/hle/kernel/k_event.h"
#include "core/hle/kernel/k_readable_event.h"
#include "core/hle/service/hid/hidbus/ringcon.h"
#include "core/memory.h"
namespace Service::HID {
RingController::RingController(Core::System& system_,
KernelHelpers::ServiceContext& service_context_)
: HidbusBase(system_, service_context_) {
input = system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1);
}
RingController::~RingController() = default;
void RingController::OnInit() {
input->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
Common::Input::PollingMode::Ring);
return;
}
void RingController::OnRelease() {
input->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
Common::Input::PollingMode::Active);
return;
};
void RingController::OnUpdate() {
if (!is_activated) {
return;
}
if (!device_enabled) {
return;
}
if (!polling_mode_enabled || transfer_memory == 0) {
return;
}
// TODO: Increment multitasking counters from motion and sensor data
switch (polling_mode) {
case JoyPollingMode::SixAxisSensorEnable: {
enable_sixaxis_data.header.total_entries = 10;
enable_sixaxis_data.header.result = ResultSuccess;
const auto& last_entry =
enable_sixaxis_data.entries[enable_sixaxis_data.header.latest_entry];
enable_sixaxis_data.header.latest_entry =
(enable_sixaxis_data.header.latest_entry + 1) % 10;
auto& curr_entry = enable_sixaxis_data.entries[enable_sixaxis_data.header.latest_entry];
curr_entry.sampling_number = last_entry.sampling_number + 1;
curr_entry.polling_data.sampling_number = curr_entry.sampling_number;
const RingConData ringcon_value = GetSensorValue();
curr_entry.polling_data.out_size = sizeof(ringcon_value);
std::memcpy(curr_entry.polling_data.data.data(), &ringcon_value, sizeof(ringcon_value));
system.ApplicationMemory().WriteBlock(transfer_memory, &enable_sixaxis_data,
sizeof(enable_sixaxis_data));
break;
}
default:
LOG_ERROR(Service_HID, "Polling mode not supported {}", polling_mode);
break;
}
}
RingController::RingConData RingController::GetSensorValue() const {
RingConData ringcon_sensor_value{
.status = DataValid::Valid,
.data = 0,
};
const f32 force_value = input->GetRingSensorForce().force * range;
ringcon_sensor_value.data = static_cast<s16>(force_value) + idle_value;
return ringcon_sensor_value;
}
u8 RingController::GetDeviceId() const {
return device_id;
}
std::vector<u8> RingController::GetReply() const {
const RingConCommands current_command = command;
switch (current_command) {
case RingConCommands::GetFirmwareVersion:
return GetFirmwareVersionReply();
case RingConCommands::ReadId:
return GetReadIdReply();
case RingConCommands::c20105:
return GetC020105Reply();
case RingConCommands::ReadUnkCal:
return GetReadUnkCalReply();
case RingConCommands::ReadFactoryCal:
return GetReadFactoryCalReply();
case RingConCommands::ReadUserCal:
return GetReadUserCalReply();
case RingConCommands::ReadRepCount:
return GetReadRepCountReply();
case RingConCommands::ReadTotalPushCount:
return GetReadTotalPushCountReply();
case RingConCommands::ResetRepCount:
return GetResetRepCountReply();
case RingConCommands::SaveCalData:
return GetSaveDataReply();
default:
return GetErrorReply();
}
}
bool RingController::SetCommand(std::span<const u8> data) {
if (data.size() < 4) {
LOG_ERROR(Service_HID, "Command size not supported {}", data.size());
command = RingConCommands::Error;
return false;
}
std::memcpy(&command, data.data(), sizeof(RingConCommands));
switch (command) {
case RingConCommands::GetFirmwareVersion:
case RingConCommands::ReadId:
case RingConCommands::c20105:
case RingConCommands::ReadUnkCal:
case RingConCommands::ReadFactoryCal:
case RingConCommands::ReadUserCal:
case RingConCommands::ReadRepCount:
case RingConCommands::ReadTotalPushCount:
ASSERT_MSG(data.size() == 0x4, "data.size is not 0x4 bytes");
send_command_async_event->Signal();
return true;
case RingConCommands::ResetRepCount:
ASSERT_MSG(data.size() == 0x4, "data.size is not 0x4 bytes");
total_rep_count = 0;
send_command_async_event->Signal();
return true;
case RingConCommands::SaveCalData: {
ASSERT_MSG(data.size() == 0x14, "data.size is not 0x14 bytes");
SaveCalData save_info{};
std::memcpy(&save_info, data.data(), sizeof(SaveCalData));
user_calibration = save_info.calibration;
send_command_async_event->Signal();
return true;
}
default:
LOG_ERROR(Service_HID, "Command not implemented {}", command);
command = RingConCommands::Error;
// Signal a reply to avoid softlocking the game
send_command_async_event->Signal();
return false;
}
}
std::vector<u8> RingController::GetFirmwareVersionReply() const {
const FirmwareVersionReply reply{
.status = DataValid::Valid,
.firmware = version,
};
return GetDataVector(reply);
}
std::vector<u8> RingController::GetReadIdReply() const {
// The values are hardcoded from a real joycon
const ReadIdReply reply{
.status = DataValid::Valid,
.id_l_x0 = 8,
.id_l_x0_2 = 41,
.id_l_x4 = 22294,
.id_h_x0 = 19777,
.id_h_x0_2 = 13621,
.id_h_x4 = 8245,
};
return GetDataVector(reply);
}
std::vector<u8> RingController::GetC020105Reply() const {
const Cmd020105Reply reply{
.status = DataValid::Valid,
.data = 1,
};
return GetDataVector(reply);
}
std::vector<u8> RingController::GetReadUnkCalReply() const {
const ReadUnkCalReply reply{
.status = DataValid::Valid,
.data = 0,
};
return GetDataVector(reply);
}
std::vector<u8> RingController::GetReadFactoryCalReply() const {
const ReadFactoryCalReply reply{
.status = DataValid::Valid,
.calibration = factory_calibration,
};
return GetDataVector(reply);
}
std::vector<u8> RingController::GetReadUserCalReply() const {
const ReadUserCalReply reply{
.status = DataValid::Valid,
.calibration = user_calibration,
};
return GetDataVector(reply);
}
std::vector<u8> RingController::GetReadRepCountReply() const {
const GetThreeByteReply reply{
.status = DataValid::Valid,
.data = {total_rep_count, 0, 0},
.crc = GetCrcValue({total_rep_count, 0, 0, 0}),
};
return GetDataVector(reply);
}
std::vector<u8> RingController::GetReadTotalPushCountReply() const {
const GetThreeByteReply reply{
.status = DataValid::Valid,
.data = {total_push_count, 0, 0},
.crc = GetCrcValue({total_push_count, 0, 0, 0}),
};
return GetDataVector(reply);
}
std::vector<u8> RingController::GetResetRepCountReply() const {
return GetReadRepCountReply();
}
std::vector<u8> RingController::GetSaveDataReply() const {
const StatusReply reply{
.status = DataValid::Valid,
};
return GetDataVector(reply);
}
std::vector<u8> RingController::GetErrorReply() const {
const ErrorReply reply{
.status = DataValid::BadCRC,
};
return GetDataVector(reply);
}
u8 RingController::GetCrcValue(const std::vector<u8>& data) const {
u8 crc = 0;
for (std::size_t index = 0; index < data.size(); index++) {
for (u8 i = 0x80; i > 0; i >>= 1) {
bool bit = (crc & 0x80) != 0;
if ((data[index] & i) != 0) {
bit = !bit;
}
crc <<= 1;
if (bit) {
crc ^= 0x8d;
}
}
}
return crc;
}
template <typename T>
std::vector<u8> RingController::GetDataVector(const T& reply) const {
static_assert(std::is_trivially_copyable_v<T>);
std::vector<u8> data;
data.resize(sizeof(reply));
std::memcpy(data.data(), &reply, sizeof(reply));
return data;
}
} // namespace Service::HID

View File

@@ -0,0 +1,253 @@
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <array>
#include <span>
#include "common/common_types.h"
#include "core/hle/service/hid/hidbus/hidbus_base.h"
namespace Core::HID {
class EmulatedController;
} // namespace Core::HID
namespace Service::HID {
class RingController final : public HidbusBase {
public:
explicit RingController(Core::System& system_, KernelHelpers::ServiceContext& service_context_);
~RingController() override;
void OnInit() override;
void OnRelease() override;
// Updates ringcon transfer memory
void OnUpdate() override;
// Returns the device ID of the joycon
u8 GetDeviceId() const override;
// Assigns a command from data
bool SetCommand(std::span<const u8> data) override;
// Returns a reply from a command
std::vector<u8> GetReply() const override;
private:
// These values are obtained from a real ring controller
static constexpr s16 idle_value = 2280;
static constexpr s16 idle_deadzone = 120;
static constexpr s16 range = 2500;
// Most missing command names are leftovers from other firmware versions
enum class RingConCommands : u32 {
GetFirmwareVersion = 0x00020000,
ReadId = 0x00020100,
JoyPolling = 0x00020101,
Unknown1 = 0x00020104,
c20105 = 0x00020105,
Unknown2 = 0x00020204,
Unknown3 = 0x00020304,
Unknown4 = 0x00020404,
ReadUnkCal = 0x00020504,
ReadFactoryCal = 0x00020A04,
Unknown5 = 0x00021104,
Unknown6 = 0x00021204,
Unknown7 = 0x00021304,
ReadUserCal = 0x00021A04,
ReadRepCount = 0x00023104,
ReadTotalPushCount = 0x00023204,
ResetRepCount = 0x04013104,
Unknown8 = 0x04011104,
Unknown9 = 0x04011204,
Unknown10 = 0x04011304,
SaveCalData = 0x10011A04,
Error = 0xFFFFFFFF,
};
enum class DataValid : u32 {
Valid,
BadCRC,
Cal,
};
struct FirmwareVersion {
u8 sub;
u8 main;
};
static_assert(sizeof(FirmwareVersion) == 0x2, "FirmwareVersion is an invalid size");
struct FactoryCalibration {
s32_le os_max;
s32_le hk_max;
s32_le zero_min;
s32_le zero_max;
};
static_assert(sizeof(FactoryCalibration) == 0x10, "FactoryCalibration is an invalid size");
struct CalibrationValue {
s16 value;
u16 crc;
};
static_assert(sizeof(CalibrationValue) == 0x4, "CalibrationValue is an invalid size");
struct UserCalibration {
CalibrationValue os_max;
CalibrationValue hk_max;
CalibrationValue zero;
};
static_assert(sizeof(UserCalibration) == 0xC, "UserCalibration is an invalid size");
struct SaveCalData {
RingConCommands command;
UserCalibration calibration;
INSERT_PADDING_BYTES_NOINIT(4);
};
static_assert(sizeof(SaveCalData) == 0x14, "SaveCalData is an invalid size");
static_assert(std::is_trivially_copyable_v<SaveCalData>,
"SaveCalData must be trivially copyable");
struct FirmwareVersionReply {
DataValid status;
FirmwareVersion firmware;
INSERT_PADDING_BYTES(0x2);
};
static_assert(sizeof(FirmwareVersionReply) == 0x8, "FirmwareVersionReply is an invalid size");
struct Cmd020105Reply {
DataValid status;
u8 data;
INSERT_PADDING_BYTES(0x3);
};
static_assert(sizeof(Cmd020105Reply) == 0x8, "Cmd020105Reply is an invalid size");
struct StatusReply {
DataValid status;
};
static_assert(sizeof(StatusReply) == 0x4, "StatusReply is an invalid size");
struct GetThreeByteReply {
DataValid status;
std::array<u8, 3> data;
u8 crc;
};
static_assert(sizeof(GetThreeByteReply) == 0x8, "GetThreeByteReply is an invalid size");
struct ReadUnkCalReply {
DataValid status;
u16 data;
INSERT_PADDING_BYTES(0x2);
};
static_assert(sizeof(ReadUnkCalReply) == 0x8, "ReadUnkCalReply is an invalid size");
struct ReadFactoryCalReply {
DataValid status;
FactoryCalibration calibration;
};
static_assert(sizeof(ReadFactoryCalReply) == 0x14, "ReadFactoryCalReply is an invalid size");
struct ReadUserCalReply {
DataValid status;
UserCalibration calibration;
INSERT_PADDING_BYTES(0x4);
};
static_assert(sizeof(ReadUserCalReply) == 0x14, "ReadUserCalReply is an invalid size");
struct ReadIdReply {
DataValid status;
u16 id_l_x0;
u16 id_l_x0_2;
u16 id_l_x4;
u16 id_h_x0;
u16 id_h_x0_2;
u16 id_h_x4;
};
static_assert(sizeof(ReadIdReply) == 0x10, "ReadIdReply is an invalid size");
struct ErrorReply {
DataValid status;
INSERT_PADDING_BYTES(0x3);
};
static_assert(sizeof(ErrorReply) == 0x8, "ErrorReply is an invalid size");
struct RingConData {
DataValid status;
s16_le data;
INSERT_PADDING_BYTES(0x2);
};
static_assert(sizeof(RingConData) == 0x8, "RingConData is an invalid size");
// Returns RingConData struct with pressure sensor values
RingConData GetSensorValue() const;
// Returns 8 byte reply with firmware version
std::vector<u8> GetFirmwareVersionReply() const;
// Returns 16 byte reply with ID values
std::vector<u8> GetReadIdReply() const;
// (STUBBED) Returns 8 byte reply
std::vector<u8> GetC020105Reply() const;
// (STUBBED) Returns 8 byte empty reply
std::vector<u8> GetReadUnkCalReply() const;
// Returns 20 byte reply with factory calibration values
std::vector<u8> GetReadFactoryCalReply() const;
// Returns 20 byte reply with user calibration values
std::vector<u8> GetReadUserCalReply() const;
// Returns 8 byte reply
std::vector<u8> GetReadRepCountReply() const;
// Returns 8 byte reply
std::vector<u8> GetReadTotalPushCountReply() const;
// Returns 8 byte reply
std::vector<u8> GetResetRepCountReply() const;
// Returns 4 byte save data reply
std::vector<u8> GetSaveDataReply() const;
// Returns 8 byte error reply
std::vector<u8> GetErrorReply() const;
// Returns 8 bit redundancy check from provided data
u8 GetCrcValue(const std::vector<u8>& data) const;
// Converts structs to an u8 vector equivalent
template <typename T>
std::vector<u8> GetDataVector(const T& reply) const;
RingConCommands command{RingConCommands::Error};
// These counters are used in multitasking mode while the switch is sleeping
// Total steps taken
u8 total_rep_count = 0;
// Total times the ring was pushed
u8 total_push_count = 0;
const u8 device_id = 0x20;
const FirmwareVersion version = {
.sub = 0x0,
.main = 0x2c,
};
const FactoryCalibration factory_calibration = {
.os_max = idle_value + range + idle_deadzone,
.hk_max = idle_value - range - idle_deadzone,
.zero_min = idle_value - idle_deadzone,
.zero_max = idle_value + idle_deadzone,
};
UserCalibration user_calibration = {
.os_max = {.value = range, .crc = 228},
.hk_max = {.value = -range, .crc = 239},
.zero = {.value = idle_value, .crc = 225},
};
Core::HID::EmulatedController* input;
};
} // namespace Service::HID

View File

@@ -0,0 +1,50 @@
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hid/emulated_controller.h"
#include "core/hid/hid_core.h"
#include "core/hle/service/hid/hidbus/starlink.h"
namespace Service::HID {
constexpr u8 DEVICE_ID = 0x28;
Starlink::Starlink(Core::System& system_, KernelHelpers::ServiceContext& service_context_)
: HidbusBase(system_, service_context_) {}
Starlink::~Starlink() = default;
void Starlink::OnInit() {
return;
}
void Starlink::OnRelease() {
return;
};
void Starlink::OnUpdate() {
if (!is_activated) {
return;
}
if (!device_enabled) {
return;
}
if (!polling_mode_enabled || transfer_memory == 0) {
return;
}
LOG_ERROR(Service_HID, "Polling mode not supported {}", polling_mode);
}
u8 Starlink::GetDeviceId() const {
return DEVICE_ID;
}
std::vector<u8> Starlink::GetReply() const {
return {};
}
bool Starlink::SetCommand(std::span<const u8> data) {
LOG_ERROR(Service_HID, "Command not implemented");
return false;
}
} // namespace Service::HID

View File

@@ -0,0 +1,37 @@
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "common/common_types.h"
#include "core/hle/service/hid/hidbus/hidbus_base.h"
namespace Core::HID {
class EmulatedController;
} // namespace Core::HID
namespace Service::HID {
class Starlink final : public HidbusBase {
public:
explicit Starlink(Core::System& system_, KernelHelpers::ServiceContext& service_context_);
~Starlink() override;
void OnInit() override;
void OnRelease() override;
// Updates ringcon transfer memory
void OnUpdate() override;
// Returns the device ID of the joycon
u8 GetDeviceId() const override;
// Assigns a command from data
bool SetCommand(std::span<const u8> data) override;
// Returns a reply from a command
std::vector<u8> GetReply() const override;
};
} // namespace Service::HID

Some files were not shown because too many files have changed in this diff Show More