From 51ad2d10de80393371a0e1b92e107c90231d0f72 Mon Sep 17 00:00:00 2001 From: t895 Date: Mon, 8 Jan 2024 00:16:28 -0500 Subject: android: Create generic adapter and viewholder Eliminates repeated code associated with every async differ list --- .../yuzu/yuzu_emu/adapters/AbstractDiffAdapter.kt | 33 ++++++++++++++++++++++ .../yuzu/yuzu_emu/viewholder/AbstractViewHolder.kt | 18 ++++++++++++ 2 files changed, 51 insertions(+) create mode 100644 src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/AbstractDiffAdapter.kt create mode 100644 src/android/app/src/main/java/org/yuzu/yuzu_emu/viewholder/AbstractViewHolder.kt (limited to 'src/android') diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/AbstractDiffAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/AbstractDiffAdapter.kt new file mode 100644 index 000000000..f006f9e3d --- /dev/null +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/AbstractDiffAdapter.kt @@ -0,0 +1,33 @@ +// SPDX-FileCopyrightText: 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.yuzu.yuzu_emu.adapters + +import android.annotation.SuppressLint +import androidx.recyclerview.widget.AsyncDifferConfig +import androidx.recyclerview.widget.DiffUtil +import androidx.recyclerview.widget.ListAdapter +import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder +import androidx.recyclerview.widget.RecyclerView + +/** + * Generic adapter that implements an [AsyncDifferConfig] and covers some of the basic boilerplate + * code used in every [RecyclerView]. + * Type assigned to [Model] must inherit from [Object] in order to be compared properly. + */ +abstract class AbstractDiffAdapter> : + ListAdapter(AsyncDifferConfig.Builder(DiffCallback()).build()) { + override fun onBindViewHolder(holder: Holder, position: Int) = + holder.bind(currentList[position]) + + private class DiffCallback : DiffUtil.ItemCallback() { + override fun areItemsTheSame(oldItem: Model & Any, newItem: Model & Any): Boolean { + return oldItem === newItem + } + + @SuppressLint("DiffUtilEquals") + override fun areContentsTheSame(oldItem: Model & Any, newItem: Model & Any): Boolean { + return oldItem == newItem + } + } +} diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/viewholder/AbstractViewHolder.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/viewholder/AbstractViewHolder.kt new file mode 100644 index 000000000..7101ad434 --- /dev/null +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/viewholder/AbstractViewHolder.kt @@ -0,0 +1,18 @@ +// SPDX-FileCopyrightText: 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.yuzu.yuzu_emu.viewholder + +import androidx.recyclerview.widget.RecyclerView +import androidx.viewbinding.ViewBinding +import org.yuzu.yuzu_emu.adapters.AbstractDiffAdapter +import org.yuzu.yuzu_emu.adapters.AbstractListAdapter + +/** + * [RecyclerView.ViewHolder] meant to work together with a [AbstractDiffAdapter] or a + * [AbstractListAdapter] so we can run [bind] on each list item without needing a manual hookup. + */ +abstract class AbstractViewHolder(binding: ViewBinding) : + RecyclerView.ViewHolder(binding.root) { + abstract fun bind(model: Model) +} -- cgit v1.2.3 From 78c323c4eb1beb502f1c2b5367ecb8654181c1e0 Mon Sep 17 00:00:00 2001 From: t895 Date: Mon, 8 Jan 2024 00:17:01 -0500 Subject: android: Refactor async diff adapters to use AbstractDiffAdapter --- .../org/yuzu/yuzu_emu/adapters/AddonAdapter.kt | 36 +--- .../org/yuzu/yuzu_emu/adapters/FolderAdapter.kt | 36 +--- .../java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt | 192 +++++++++------------ 3 files changed, 92 insertions(+), 172 deletions(-) (limited to 'src/android') diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/AddonAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/AddonAdapter.kt index 15c7ca3c9..94c151325 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/AddonAdapter.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/AddonAdapter.kt @@ -5,48 +5,28 @@ package org.yuzu.yuzu_emu.adapters import android.view.LayoutInflater import android.view.ViewGroup -import androidx.recyclerview.widget.AsyncDifferConfig -import androidx.recyclerview.widget.DiffUtil -import androidx.recyclerview.widget.ListAdapter -import androidx.recyclerview.widget.RecyclerView import org.yuzu.yuzu_emu.databinding.ListItemAddonBinding import org.yuzu.yuzu_emu.model.Addon +import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder -class AddonAdapter : ListAdapter( - AsyncDifferConfig.Builder(DiffCallback()).build() -) { +class AddonAdapter : AbstractDiffAdapter() { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): AddonViewHolder { ListItemAddonBinding.inflate(LayoutInflater.from(parent.context), parent, false) .also { return AddonViewHolder(it) } } - override fun getItemCount(): Int = currentList.size - - override fun onBindViewHolder(holder: AddonViewHolder, position: Int) = - holder.bind(currentList[position]) - inner class AddonViewHolder(val binding: ListItemAddonBinding) : - RecyclerView.ViewHolder(binding.root) { - fun bind(addon: Addon) { + AbstractViewHolder(binding) { + override fun bind(model: Addon) { binding.root.setOnClickListener { binding.addonSwitch.isChecked = !binding.addonSwitch.isChecked } - binding.title.text = addon.title - binding.version.text = addon.version + binding.title.text = model.title + binding.version.text = model.version binding.addonSwitch.setOnCheckedChangeListener { _, checked -> - addon.enabled = checked + model.enabled = checked } - binding.addonSwitch.isChecked = addon.enabled - } - } - - private class DiffCallback : DiffUtil.ItemCallback() { - override fun areItemsTheSame(oldItem: Addon, newItem: Addon): Boolean { - return oldItem == newItem - } - - override fun areContentsTheSame(oldItem: Addon, newItem: Addon): Boolean { - return oldItem == newItem + binding.addonSwitch.isChecked = model.enabled } } } diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/FolderAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/FolderAdapter.kt index ab657a7b9..3d8f0bda8 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/FolderAdapter.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/FolderAdapter.kt @@ -8,19 +8,14 @@ import android.text.TextUtils import android.view.LayoutInflater import android.view.ViewGroup import androidx.fragment.app.FragmentActivity -import androidx.recyclerview.widget.AsyncDifferConfig -import androidx.recyclerview.widget.DiffUtil -import androidx.recyclerview.widget.ListAdapter -import androidx.recyclerview.widget.RecyclerView import org.yuzu.yuzu_emu.databinding.CardFolderBinding import org.yuzu.yuzu_emu.fragments.GameFolderPropertiesDialogFragment import org.yuzu.yuzu_emu.model.GameDir import org.yuzu.yuzu_emu.model.GamesViewModel +import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder class FolderAdapter(val activity: FragmentActivity, val gamesViewModel: GamesViewModel) : - ListAdapter( - AsyncDifferConfig.Builder(DiffCallback()).build() - ) { + AbstractDiffAdapter() { override fun onCreateViewHolder( parent: ViewGroup, viewType: Int @@ -29,18 +24,11 @@ class FolderAdapter(val activity: FragmentActivity, val gamesViewModel: GamesVie .also { return FolderViewHolder(it) } } - override fun onBindViewHolder(holder: FolderAdapter.FolderViewHolder, position: Int) = - holder.bind(currentList[position]) - inner class FolderViewHolder(val binding: CardFolderBinding) : - RecyclerView.ViewHolder(binding.root) { - private lateinit var gameDir: GameDir - - fun bind(gameDir: GameDir) { - this.gameDir = gameDir - + AbstractViewHolder(binding) { + override fun bind(model: GameDir) { binding.apply { - path.text = Uri.parse(gameDir.uriString).path + path.text = Uri.parse(model.uriString).path path.postDelayed( { path.isSelected = true @@ -50,7 +38,7 @@ class FolderAdapter(val activity: FragmentActivity, val gamesViewModel: GamesVie ) buttonEdit.setOnClickListener { - GameFolderPropertiesDialogFragment.newInstance(this@FolderViewHolder.gameDir) + GameFolderPropertiesDialogFragment.newInstance(model) .show( activity.supportFragmentManager, GameFolderPropertiesDialogFragment.TAG @@ -58,19 +46,9 @@ class FolderAdapter(val activity: FragmentActivity, val gamesViewModel: GamesVie } buttonDelete.setOnClickListener { - gamesViewModel.removeFolder(this@FolderViewHolder.gameDir) + gamesViewModel.removeFolder(model) } } } } - - private class DiffCallback : DiffUtil.ItemCallback() { - override fun areItemsTheSame(oldItem: GameDir, newItem: GameDir): Boolean { - return oldItem == newItem - } - - override fun areContentsTheSame(oldItem: GameDir, newItem: GameDir): Boolean { - return oldItem == newItem - } - } } diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt index a578f0de8..e26c2e0ab 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt @@ -9,7 +9,6 @@ import android.graphics.drawable.LayerDrawable import android.net.Uri import android.text.TextUtils import android.view.LayoutInflater -import android.view.View import android.view.ViewGroup import android.widget.ImageView import android.widget.Toast @@ -25,10 +24,6 @@ import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.lifecycleScope import androidx.navigation.findNavController import androidx.preference.PreferenceManager -import androidx.recyclerview.widget.AsyncDifferConfig -import androidx.recyclerview.widget.DiffUtil -import androidx.recyclerview.widget.ListAdapter -import androidx.recyclerview.widget.RecyclerView import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext @@ -36,122 +31,26 @@ import org.yuzu.yuzu_emu.HomeNavigationDirections import org.yuzu.yuzu_emu.R import org.yuzu.yuzu_emu.YuzuApplication import org.yuzu.yuzu_emu.activities.EmulationActivity -import org.yuzu.yuzu_emu.adapters.GameAdapter.GameViewHolder import org.yuzu.yuzu_emu.databinding.CardGameBinding import org.yuzu.yuzu_emu.model.Game import org.yuzu.yuzu_emu.model.GamesViewModel import org.yuzu.yuzu_emu.utils.GameIconUtils +import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder class GameAdapter(private val activity: AppCompatActivity) : - ListAdapter(AsyncDifferConfig.Builder(DiffCallback()).build()), - View.OnClickListener, - View.OnLongClickListener { + AbstractDiffAdapter() { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): GameViewHolder { - // Create a new view. - val binding = CardGameBinding.inflate(LayoutInflater.from(parent.context), parent, false) - binding.cardGame.setOnClickListener(this) - binding.cardGame.setOnLongClickListener(this) - - // Use that view to create a ViewHolder. - return GameViewHolder(binding) - } - - override fun onBindViewHolder(holder: GameViewHolder, position: Int) = - holder.bind(currentList[position]) - - override fun getItemCount(): Int = currentList.size - - /** - * Launches the game that was clicked on. - * - * @param view The card representing the game the user wants to play. - */ - override fun onClick(view: View) { - val holder = view.tag as GameViewHolder - - val gameExists = DocumentFile.fromSingleUri( - YuzuApplication.appContext, - Uri.parse(holder.game.path) - )?.exists() == true - if (!gameExists) { - Toast.makeText( - YuzuApplication.appContext, - R.string.loader_error_file_not_found, - Toast.LENGTH_LONG - ).show() - - ViewModelProvider(activity)[GamesViewModel::class.java].reloadGames(true) - return - } - - val preferences = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext) - preferences.edit() - .putLong( - holder.game.keyLastPlayedTime, - System.currentTimeMillis() - ) - .apply() - - val openIntent = Intent(YuzuApplication.appContext, EmulationActivity::class.java).apply { - action = Intent.ACTION_VIEW - data = Uri.parse(holder.game.path) - } - - activity.lifecycleScope.launch { - withContext(Dispatchers.IO) { - val layerDrawable = ResourcesCompat.getDrawable( - YuzuApplication.appContext.resources, - R.drawable.shortcut, - null - ) as LayerDrawable - layerDrawable.setDrawableByLayerId( - R.id.shortcut_foreground, - GameIconUtils.getGameIcon(activity, holder.game) - .toDrawable(YuzuApplication.appContext.resources) - ) - val inset = YuzuApplication.appContext.resources - .getDimensionPixelSize(R.dimen.icon_inset) - layerDrawable.setLayerInset(1, inset, inset, inset, inset) - val shortcut = - ShortcutInfoCompat.Builder(YuzuApplication.appContext, holder.game.path) - .setShortLabel(holder.game.title) - .setIcon( - IconCompat.createWithAdaptiveBitmap( - layerDrawable.toBitmap(config = Bitmap.Config.ARGB_8888) - ) - ) - .setIntent(openIntent) - .build() - ShortcutManagerCompat.pushDynamicShortcut(YuzuApplication.appContext, shortcut) - } - } - - val action = HomeNavigationDirections.actionGlobalEmulationActivity(holder.game, true) - view.findNavController().navigate(action) - } - - override fun onLongClick(view: View): Boolean { - val holder = view.tag as GameViewHolder - val action = HomeNavigationDirections.actionGlobalPerGamePropertiesFragment(holder.game) - view.findNavController().navigate(action) - return true + CardGameBinding.inflate(LayoutInflater.from(parent.context), parent, false) + .also { return GameViewHolder(it) } } inner class GameViewHolder(val binding: CardGameBinding) : - RecyclerView.ViewHolder(binding.root) { - lateinit var game: Game - - init { - binding.cardGame.tag = this - } - - fun bind(game: Game) { - this.game = game - + AbstractViewHolder(binding) { + override fun bind(model: Game) { binding.imageGameScreen.scaleType = ImageView.ScaleType.CENTER_CROP - GameIconUtils.loadGameIcon(game, binding.imageGameScreen) + GameIconUtils.loadGameIcon(model, binding.imageGameScreen) - binding.textGameTitle.text = game.title.replace("[\\t\\n\\r]+".toRegex(), " ") + binding.textGameTitle.text = model.title.replace("[\\t\\n\\r]+".toRegex(), " ") binding.textGameTitle.postDelayed( { @@ -160,16 +59,79 @@ class GameAdapter(private val activity: AppCompatActivity) : }, 3000 ) + + binding.cardGame.setOnClickListener { onClick(model) } + binding.cardGame.setOnLongClickListener { onLongClick(model) } } - } - private class DiffCallback : DiffUtil.ItemCallback() { - override fun areItemsTheSame(oldItem: Game, newItem: Game): Boolean { - return oldItem == newItem + fun onClick(game: Game) { + val gameExists = DocumentFile.fromSingleUri( + YuzuApplication.appContext, + Uri.parse(game.path) + )?.exists() == true + if (!gameExists) { + Toast.makeText( + YuzuApplication.appContext, + R.string.loader_error_file_not_found, + Toast.LENGTH_LONG + ).show() + + ViewModelProvider(activity)[GamesViewModel::class.java].reloadGames(true) + return + } + + val preferences = + PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext) + preferences.edit() + .putLong( + game.keyLastPlayedTime, + System.currentTimeMillis() + ) + .apply() + + val openIntent = + Intent(YuzuApplication.appContext, EmulationActivity::class.java).apply { + action = Intent.ACTION_VIEW + data = Uri.parse(game.path) + } + + activity.lifecycleScope.launch { + withContext(Dispatchers.IO) { + val layerDrawable = ResourcesCompat.getDrawable( + YuzuApplication.appContext.resources, + R.drawable.shortcut, + null + ) as LayerDrawable + layerDrawable.setDrawableByLayerId( + R.id.shortcut_foreground, + GameIconUtils.getGameIcon(activity, game) + .toDrawable(YuzuApplication.appContext.resources) + ) + val inset = YuzuApplication.appContext.resources + .getDimensionPixelSize(R.dimen.icon_inset) + layerDrawable.setLayerInset(1, inset, inset, inset, inset) + val shortcut = + ShortcutInfoCompat.Builder(YuzuApplication.appContext, game.path) + .setShortLabel(game.title) + .setIcon( + IconCompat.createWithAdaptiveBitmap( + layerDrawable.toBitmap(config = Bitmap.Config.ARGB_8888) + ) + ) + .setIntent(openIntent) + .build() + ShortcutManagerCompat.pushDynamicShortcut(YuzuApplication.appContext, shortcut) + } + } + + val action = HomeNavigationDirections.actionGlobalEmulationActivity(game, true) + binding.root.findNavController().navigate(action) } - override fun areContentsTheSame(oldItem: Game, newItem: Game): Boolean { - return oldItem == newItem + fun onLongClick(game: Game): Boolean { + val action = HomeNavigationDirections.actionGlobalPerGamePropertiesFragment(game) + binding.root.findNavController().navigate(action) + return true } } } -- cgit v1.2.3 From ad0066a6b651ab47b7b7e916a9f716ef77fe2e04 Mon Sep 17 00:00:00 2001 From: t895 Date: Wed, 10 Jan 2024 12:50:32 -0500 Subject: android: Create generic list adapter for basic lists Simplifies basic setup for lists --- .../yuzu/yuzu_emu/adapters/AbstractListAdapter.kt | 98 ++++++++++++++++++++++ 1 file changed, 98 insertions(+) create mode 100644 src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/AbstractListAdapter.kt (limited to 'src/android') diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/AbstractListAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/AbstractListAdapter.kt new file mode 100644 index 000000000..3dfee3d0c --- /dev/null +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/AbstractListAdapter.kt @@ -0,0 +1,98 @@ +// SPDX-FileCopyrightText: 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.yuzu.yuzu_emu.adapters + +import android.annotation.SuppressLint +import androidx.recyclerview.widget.RecyclerView +import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder + +/** + * Generic list class meant to take care of basic lists + * @param currentList The list to show initially + */ +abstract class AbstractListAdapter>( + open var currentList: List +) : RecyclerView.Adapter() { + override fun onBindViewHolder(holder: Holder, position: Int) = + holder.bind(currentList[position]) + + override fun getItemCount(): Int = currentList.size + + /** + * Adds an item to [currentList] and notifies the underlying adapter of the change. If no parameter + * is passed in for position, [item] is added to the end of the list. Invokes [callback] last. + * @param item The item to add to the list + * @param position Index where [item] will be added + * @param callback Lambda that's called at the end of the list changes and has the added list + * position passed in as a parameter + */ + open fun addItem(item: Model, position: Int = -1, callback: ((position: Int) -> Unit)? = null) { + val newList = currentList.toMutableList() + val positionToUpdate: Int + if (position == -1) { + newList.add(item) + currentList = newList + positionToUpdate = currentList.size - 1 + } else { + newList.add(position, item) + currentList = newList + positionToUpdate = position + } + onItemAdded(positionToUpdate, callback) + } + + protected fun onItemAdded(position: Int, callback: ((Int) -> Unit)? = null) { + notifyItemInserted(position) + callback?.invoke(position) + } + + /** + * Replaces the [item] at [position] in the [currentList] and notifies the underlying adapter + * of the change. Invokes [callback] last. + * @param item New list item + * @param position Index where [item] will replace the existing list item + * @param callback Lambda that's called at the end of the list changes and has the changed list + * position passed in as a parameter + */ + fun changeItem(item: Model, position: Int, callback: ((position: Int) -> Unit)? = null) { + val newList = currentList.toMutableList() + newList[position] = item + currentList = newList + onItemChanged(position, callback) + } + + protected fun onItemChanged(position: Int, callback: ((Int) -> Unit)? = null) { + notifyItemChanged(position) + callback?.invoke(position) + } + + /** + * Removes the list item at [position] in [currentList] and notifies the underlying adapter + * of the change. Invokes [callback] last. + * @param position Index where the list item will be removed + * @param callback Lambda that's called at the end of the list changes and has the removed list + * position passed in as a parameter + */ + fun removeItem(position: Int, callback: ((position: Int) -> Unit)? = null) { + val newList = currentList.toMutableList() + newList.removeAt(position) + currentList = newList + onItemRemoved(position, callback) + } + + protected fun onItemRemoved(position: Int, callback: ((Int) -> Unit)? = null) { + notifyItemRemoved(position) + callback?.invoke(position) + } + + /** + * Replaces [currentList] with [newList] and notifies the underlying adapter of the change. + * @param newList The new list to replace [currentList] + */ + @SuppressLint("NotifyDataSetChanged") + open fun replaceList(newList: List) { + currentList = newList + notifyDataSetChanged() + } +} -- cgit v1.2.3 From 9130366a580aa1129a596bf6ee3ab4432d219b6d Mon Sep 17 00:00:00 2001 From: t895 Date: Wed, 10 Jan 2024 12:51:14 -0500 Subject: android: Refactor recycler view adapters to use AbstractListAdapter --- .../org/yuzu/yuzu_emu/adapters/AppletAdapter.kt | 88 +++++++++------------- .../adapters/CabinetLauncherDialogAdapter.kt | 57 ++++++-------- .../yuzu_emu/adapters/GamePropertiesAdapter.kt | 28 +++---- .../yuzu/yuzu_emu/adapters/HomeSettingAdapter.kt | 72 +++++++----------- .../yuzu/yuzu_emu/adapters/InstallableAdapter.kt | 36 ++++----- .../org/yuzu/yuzu_emu/adapters/LicenseAdapter.kt | 50 +++++------- .../org/yuzu/yuzu_emu/adapters/SetupAdapter.kt | 45 ++++------- 7 files changed, 140 insertions(+), 236 deletions(-) (limited to 'src/android') diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/AppletAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/AppletAdapter.kt index 4a05c5be9..41d7f72b8 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/AppletAdapter.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/AppletAdapter.kt @@ -4,13 +4,11 @@ package org.yuzu.yuzu_emu.adapters import android.view.LayoutInflater -import android.view.View import android.view.ViewGroup import android.widget.Toast import androidx.core.content.res.ResourcesCompat import androidx.fragment.app.FragmentActivity import androidx.navigation.findNavController -import androidx.recyclerview.widget.RecyclerView import org.yuzu.yuzu_emu.HomeNavigationDirections import org.yuzu.yuzu_emu.NativeLibrary import org.yuzu.yuzu_emu.R @@ -19,72 +17,58 @@ import org.yuzu.yuzu_emu.databinding.CardSimpleOutlinedBinding import org.yuzu.yuzu_emu.model.Applet import org.yuzu.yuzu_emu.model.AppletInfo import org.yuzu.yuzu_emu.model.Game +import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder -class AppletAdapter(val activity: FragmentActivity, var applets: List) : - RecyclerView.Adapter(), - View.OnClickListener { - +class AppletAdapter(val activity: FragmentActivity, applets: List) : + AbstractListAdapter(applets) { override fun onCreateViewHolder( parent: ViewGroup, viewType: Int ): AppletAdapter.AppletViewHolder { CardSimpleOutlinedBinding.inflate(LayoutInflater.from(parent.context), parent, false) - .apply { root.setOnClickListener(this@AppletAdapter) } .also { return AppletViewHolder(it) } } - override fun onBindViewHolder(holder: AppletViewHolder, position: Int) = - holder.bind(applets[position]) - - override fun getItemCount(): Int = applets.size - - override fun onClick(view: View) { - val applet = (view.tag as AppletViewHolder).applet - val appletPath = NativeLibrary.getAppletLaunchPath(applet.appletInfo.entryId) - if (appletPath.isEmpty()) { - Toast.makeText( - YuzuApplication.appContext, - R.string.applets_error_applet, - Toast.LENGTH_SHORT - ).show() - return - } - - if (applet.appletInfo == AppletInfo.Cabinet) { - view.findNavController() - .navigate(R.id.action_appletLauncherFragment_to_cabinetLauncherDialogFragment) - return - } - - NativeLibrary.setCurrentAppletId(applet.appletInfo.appletId) - val appletGame = Game( - title = YuzuApplication.appContext.getString(applet.titleId), - path = appletPath - ) - val action = HomeNavigationDirections.actionGlobalEmulationActivity(appletGame) - view.findNavController().navigate(action) - } - inner class AppletViewHolder(val binding: CardSimpleOutlinedBinding) : - RecyclerView.ViewHolder(binding.root) { - lateinit var applet: Applet - - init { - itemView.tag = this - } - - fun bind(applet: Applet) { - this.applet = applet - - binding.title.setText(applet.titleId) - binding.description.setText(applet.descriptionId) + AbstractViewHolder(binding) { + override fun bind(model: Applet) { + binding.title.setText(model.titleId) + binding.description.setText(model.descriptionId) binding.icon.setImageDrawable( ResourcesCompat.getDrawable( binding.icon.context.resources, - applet.iconId, + model.iconId, binding.icon.context.theme ) ) + + binding.root.setOnClickListener { onClick(model) } + } + + fun onClick(applet: Applet) { + val appletPath = NativeLibrary.getAppletLaunchPath(applet.appletInfo.entryId) + if (appletPath.isEmpty()) { + Toast.makeText( + binding.root.context, + R.string.applets_error_applet, + Toast.LENGTH_SHORT + ).show() + return + } + + if (applet.appletInfo == AppletInfo.Cabinet) { + binding.root.findNavController() + .navigate(R.id.action_appletLauncherFragment_to_cabinetLauncherDialogFragment) + return + } + + NativeLibrary.setCurrentAppletId(applet.appletInfo.appletId) + val appletGame = Game( + title = YuzuApplication.appContext.getString(applet.titleId), + path = appletPath + ) + val action = HomeNavigationDirections.actionGlobalEmulationActivity(appletGame) + binding.root.findNavController().navigate(action) } } } diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/CabinetLauncherDialogAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/CabinetLauncherDialogAdapter.kt index e7b7c0f2f..a56137148 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/CabinetLauncherDialogAdapter.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/CabinetLauncherDialogAdapter.kt @@ -4,12 +4,10 @@ package org.yuzu.yuzu_emu.adapters import android.view.LayoutInflater -import android.view.View import android.view.ViewGroup import androidx.core.content.res.ResourcesCompat import androidx.fragment.app.Fragment import androidx.navigation.fragment.findNavController -import androidx.recyclerview.widget.RecyclerView import org.yuzu.yuzu_emu.HomeNavigationDirections import org.yuzu.yuzu_emu.NativeLibrary import org.yuzu.yuzu_emu.R @@ -19,54 +17,43 @@ import org.yuzu.yuzu_emu.model.CabinetMode import org.yuzu.yuzu_emu.adapters.CabinetLauncherDialogAdapter.CabinetModeViewHolder import org.yuzu.yuzu_emu.model.AppletInfo import org.yuzu.yuzu_emu.model.Game +import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder class CabinetLauncherDialogAdapter(val fragment: Fragment) : - RecyclerView.Adapter(), - View.OnClickListener { - private val cabinetModes = CabinetMode.values().copyOfRange(1, CabinetMode.values().size) + AbstractListAdapter( + CabinetMode.values().copyOfRange(1, CabinetMode.entries.size).toList() + ) { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CabinetModeViewHolder { DialogListItemBinding.inflate(LayoutInflater.from(parent.context), parent, false) - .apply { root.setOnClickListener(this@CabinetLauncherDialogAdapter) } .also { return CabinetModeViewHolder(it) } } - override fun getItemCount(): Int = cabinetModes.size - - override fun onBindViewHolder(holder: CabinetModeViewHolder, position: Int) = - holder.bind(cabinetModes[position]) - - override fun onClick(view: View) { - val mode = (view.tag as CabinetModeViewHolder).cabinetMode - val appletPath = NativeLibrary.getAppletLaunchPath(AppletInfo.Cabinet.entryId) - NativeLibrary.setCurrentAppletId(AppletInfo.Cabinet.appletId) - NativeLibrary.setCabinetMode(mode.id) - val appletGame = Game( - title = YuzuApplication.appContext.getString(R.string.cabinet_applet), - path = appletPath - ) - val action = HomeNavigationDirections.actionGlobalEmulationActivity(appletGame) - fragment.findNavController().navigate(action) - } - inner class CabinetModeViewHolder(val binding: DialogListItemBinding) : - RecyclerView.ViewHolder(binding.root) { - lateinit var cabinetMode: CabinetMode - - init { - itemView.tag = this - } - - fun bind(cabinetMode: CabinetMode) { - this.cabinetMode = cabinetMode + AbstractViewHolder(binding) { + override fun bind(model: CabinetMode) { binding.icon.setImageDrawable( ResourcesCompat.getDrawable( binding.icon.context.resources, - cabinetMode.iconId, + model.iconId, binding.icon.context.theme ) ) - binding.title.setText(cabinetMode.titleId) + binding.title.setText(model.titleId) + + binding.root.setOnClickListener { onClick(model) } + } + + private fun onClick(mode: CabinetMode) { + val appletPath = NativeLibrary.getAppletLaunchPath(AppletInfo.Cabinet.entryId) + NativeLibrary.setCurrentAppletId(AppletInfo.Cabinet.appletId) + NativeLibrary.setCabinetMode(mode.id) + val appletGame = Game( + title = YuzuApplication.appContext.getString(R.string.cabinet_applet), + path = appletPath + ) + val action = HomeNavigationDirections.actionGlobalEmulationActivity(appletGame) + fragment.findNavController().navigate(action) } } } diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GamePropertiesAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GamePropertiesAdapter.kt index 95841d786..0046d5314 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GamePropertiesAdapter.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GamePropertiesAdapter.kt @@ -12,23 +12,22 @@ import androidx.lifecycle.Lifecycle import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.lifecycleScope import androidx.lifecycle.repeatOnLifecycle -import androidx.recyclerview.widget.RecyclerView import kotlinx.coroutines.launch import org.yuzu.yuzu_emu.databinding.CardInstallableIconBinding import org.yuzu.yuzu_emu.databinding.CardSimpleOutlinedBinding import org.yuzu.yuzu_emu.model.GameProperty import org.yuzu.yuzu_emu.model.InstallableProperty import org.yuzu.yuzu_emu.model.SubmenuProperty +import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder class GamePropertiesAdapter( private val viewLifecycle: LifecycleOwner, private var properties: List -) : - RecyclerView.Adapter() { +) : AbstractListAdapter>(properties) { override fun onCreateViewHolder( parent: ViewGroup, viewType: Int - ): GamePropertyViewHolder { + ): AbstractViewHolder { val inflater = LayoutInflater.from(parent.context) return when (viewType) { PropertyType.Submenu.ordinal -> { @@ -51,11 +50,6 @@ class GamePropertiesAdapter( } } - override fun getItemCount(): Int = properties.size - - override fun onBindViewHolder(holder: GamePropertyViewHolder, position: Int) = - holder.bind(properties[position]) - override fun getItemViewType(position: Int): Int { return when (properties[position]) { is SubmenuProperty -> PropertyType.Submenu.ordinal @@ -63,14 +57,10 @@ class GamePropertiesAdapter( } } - sealed class GamePropertyViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { - abstract fun bind(property: GameProperty) - } - inner class SubmenuPropertyViewHolder(val binding: CardSimpleOutlinedBinding) : - GamePropertyViewHolder(binding.root) { - override fun bind(property: GameProperty) { - val submenuProperty = property as SubmenuProperty + AbstractViewHolder(binding) { + override fun bind(model: GameProperty) { + val submenuProperty = model as SubmenuProperty binding.root.setOnClickListener { submenuProperty.action.invoke() @@ -108,9 +98,9 @@ class GamePropertiesAdapter( } inner class InstallablePropertyViewHolder(val binding: CardInstallableIconBinding) : - GamePropertyViewHolder(binding.root) { - override fun bind(property: GameProperty) { - val installableProperty = property as InstallableProperty + AbstractViewHolder(binding) { + override fun bind(model: GameProperty) { + val installableProperty = model as InstallableProperty binding.title.setText(installableProperty.titleId) binding.description.setText(installableProperty.descriptionId) diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/HomeSettingAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/HomeSettingAdapter.kt index 58ce343f4..b512845d5 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/HomeSettingAdapter.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/HomeSettingAdapter.kt @@ -14,69 +14,37 @@ import androidx.lifecycle.Lifecycle import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.lifecycleScope import androidx.lifecycle.repeatOnLifecycle -import androidx.recyclerview.widget.RecyclerView import kotlinx.coroutines.launch import org.yuzu.yuzu_emu.R import org.yuzu.yuzu_emu.databinding.CardHomeOptionBinding import org.yuzu.yuzu_emu.fragments.MessageDialogFragment import org.yuzu.yuzu_emu.model.HomeSetting +import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder class HomeSettingAdapter( private val activity: AppCompatActivity, private val viewLifecycle: LifecycleOwner, - var options: List -) : - RecyclerView.Adapter(), - View.OnClickListener { + options: List +) : AbstractListAdapter(options) { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): HomeOptionViewHolder { - val binding = - CardHomeOptionBinding.inflate(LayoutInflater.from(parent.context), parent, false) - binding.root.setOnClickListener(this) - return HomeOptionViewHolder(binding) - } - - override fun getItemCount(): Int { - return options.size - } - - override fun onBindViewHolder(holder: HomeOptionViewHolder, position: Int) { - holder.bind(options[position]) - } - - override fun onClick(view: View) { - val holder = view.tag as HomeOptionViewHolder - if (holder.option.isEnabled.invoke()) { - holder.option.onClick.invoke() - } else { - MessageDialogFragment.newInstance( - activity, - titleId = holder.option.disabledTitleId, - descriptionId = holder.option.disabledMessageId - ).show(activity.supportFragmentManager, MessageDialogFragment.TAG) - } + CardHomeOptionBinding.inflate(LayoutInflater.from(parent.context), parent, false) + .also { return HomeOptionViewHolder(it) } } inner class HomeOptionViewHolder(val binding: CardHomeOptionBinding) : - RecyclerView.ViewHolder(binding.root) { - lateinit var option: HomeSetting - - init { - itemView.tag = this - } - - fun bind(option: HomeSetting) { - this.option = option - binding.optionTitle.text = activity.resources.getString(option.titleId) - binding.optionDescription.text = activity.resources.getString(option.descriptionId) + AbstractViewHolder(binding) { + override fun bind(model: HomeSetting) { + binding.optionTitle.text = activity.resources.getString(model.titleId) + binding.optionDescription.text = activity.resources.getString(model.descriptionId) binding.optionIcon.setImageDrawable( ResourcesCompat.getDrawable( activity.resources, - option.iconId, + model.iconId, activity.theme ) ) - when (option.titleId) { + when (model.titleId) { R.string.get_early_access -> binding.optionLayout.background = ContextCompat.getDrawable( @@ -85,7 +53,7 @@ class HomeSettingAdapter( ) } - if (!option.isEnabled.invoke()) { + if (!model.isEnabled.invoke()) { binding.optionTitle.alpha = 0.5f binding.optionDescription.alpha = 0.5f binding.optionIcon.alpha = 0.5f @@ -93,7 +61,7 @@ class HomeSettingAdapter( viewLifecycle.lifecycleScope.launch { viewLifecycle.repeatOnLifecycle(Lifecycle.State.CREATED) { - option.details.collect { updateOptionDetails(it) } + model.details.collect { updateOptionDetails(it) } } } binding.optionDetail.postDelayed( @@ -103,6 +71,20 @@ class HomeSettingAdapter( }, 3000 ) + + binding.root.setOnClickListener { onClick(model) } + } + + private fun onClick(model: HomeSetting) { + if (model.isEnabled.invoke()) { + model.onClick.invoke() + } else { + MessageDialogFragment.newInstance( + activity, + titleId = model.disabledTitleId, + descriptionId = model.disabledMessageId + ).show(activity.supportFragmentManager, MessageDialogFragment.TAG) + } } private fun updateOptionDetails(detailString: String) { diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/InstallableAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/InstallableAdapter.kt index e960fbaab..4218c4e52 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/InstallableAdapter.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/InstallableAdapter.kt @@ -6,43 +6,33 @@ package org.yuzu.yuzu_emu.adapters import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import androidx.recyclerview.widget.RecyclerView import org.yuzu.yuzu_emu.databinding.CardInstallableBinding import org.yuzu.yuzu_emu.model.Installable +import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder -class InstallableAdapter(private val installables: List) : - RecyclerView.Adapter() { +class InstallableAdapter(installables: List) : + AbstractListAdapter(installables) { override fun onCreateViewHolder( parent: ViewGroup, viewType: Int ): InstallableAdapter.InstallableViewHolder { - val binding = - CardInstallableBinding.inflate(LayoutInflater.from(parent.context), parent, false) - return InstallableViewHolder(binding) + CardInstallableBinding.inflate(LayoutInflater.from(parent.context), parent, false) + .also { return InstallableViewHolder(it) } } - override fun getItemCount(): Int = installables.size - - override fun onBindViewHolder(holder: InstallableAdapter.InstallableViewHolder, position: Int) = - holder.bind(installables[position]) - inner class InstallableViewHolder(val binding: CardInstallableBinding) : - RecyclerView.ViewHolder(binding.root) { - lateinit var installable: Installable - - fun bind(installable: Installable) { - this.installable = installable - - binding.title.setText(installable.titleId) - binding.description.setText(installable.descriptionId) + AbstractViewHolder(binding) { + override fun bind(model: Installable) { + binding.title.setText(model.titleId) + binding.description.setText(model.descriptionId) - if (installable.install != null) { + if (model.install != null) { binding.buttonInstall.visibility = View.VISIBLE - binding.buttonInstall.setOnClickListener { installable.install.invoke() } + binding.buttonInstall.setOnClickListener { model.install.invoke() } } - if (installable.export != null) { + if (model.export != null) { binding.buttonExport.visibility = View.VISIBLE - binding.buttonExport.setOnClickListener { installable.export.invoke() } + binding.buttonExport.setOnClickListener { model.export.invoke() } } } } diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/LicenseAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/LicenseAdapter.kt index bc6ff1364..38bb1f96f 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/LicenseAdapter.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/LicenseAdapter.kt @@ -7,49 +7,33 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.appcompat.app.AppCompatActivity -import androidx.recyclerview.widget.RecyclerView -import androidx.recyclerview.widget.RecyclerView.ViewHolder -import org.yuzu.yuzu_emu.YuzuApplication import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding import org.yuzu.yuzu_emu.fragments.LicenseBottomSheetDialogFragment import org.yuzu.yuzu_emu.model.License +import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder -class LicenseAdapter(private val activity: AppCompatActivity, var licenses: List) : - RecyclerView.Adapter(), - View.OnClickListener { +class LicenseAdapter(private val activity: AppCompatActivity, licenses: List) : + AbstractListAdapter(licenses) { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): LicenseViewHolder { - val binding = - ListItemSettingBinding.inflate(LayoutInflater.from(parent.context), parent, false) - binding.root.setOnClickListener(this) - return LicenseViewHolder(binding) + ListItemSettingBinding.inflate(LayoutInflater.from(parent.context), parent, false) + .also { return LicenseViewHolder(it) } } - override fun getItemCount(): Int = licenses.size + inner class LicenseViewHolder(val binding: ListItemSettingBinding) : + AbstractViewHolder(binding) { + override fun bind(model: License) { + binding.apply { + textSettingName.text = root.context.getString(model.titleId) + textSettingDescription.text = root.context.getString(model.descriptionId) + textSettingValue.visibility = View.GONE - override fun onBindViewHolder(holder: LicenseViewHolder, position: Int) { - holder.bind(licenses[position]) - } - - override fun onClick(view: View) { - val license = (view.tag as LicenseViewHolder).license - LicenseBottomSheetDialogFragment.newInstance(license) - .show(activity.supportFragmentManager, LicenseBottomSheetDialogFragment.TAG) - } - - inner class LicenseViewHolder(val binding: ListItemSettingBinding) : ViewHolder(binding.root) { - lateinit var license: License - - init { - itemView.tag = this + root.setOnClickListener { onClick(model) } + } } - fun bind(license: License) { - this.license = license - - val context = YuzuApplication.appContext - binding.textSettingName.text = context.getString(license.titleId) - binding.textSettingDescription.text = context.getString(license.descriptionId) - binding.textSettingValue.visibility = View.GONE + private fun onClick(license: License) { + LicenseBottomSheetDialogFragment.newInstance(license) + .show(activity.supportFragmentManager, LicenseBottomSheetDialogFragment.TAG) } } } diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/SetupAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/SetupAdapter.kt index 6b46d359e..02118e1a8 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/SetupAdapter.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/SetupAdapter.kt @@ -10,7 +10,6 @@ import android.view.ViewGroup import androidx.appcompat.app.AppCompatActivity import androidx.core.content.res.ResourcesCompat import androidx.lifecycle.ViewModelProvider -import androidx.recyclerview.widget.RecyclerView import com.google.android.material.button.MaterialButton import org.yuzu.yuzu_emu.databinding.PageSetupBinding import org.yuzu.yuzu_emu.model.HomeViewModel @@ -18,31 +17,19 @@ import org.yuzu.yuzu_emu.model.SetupCallback import org.yuzu.yuzu_emu.model.SetupPage import org.yuzu.yuzu_emu.model.StepState import org.yuzu.yuzu_emu.utils.ViewUtils +import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder -class SetupAdapter(val activity: AppCompatActivity, val pages: List) : - RecyclerView.Adapter() { +class SetupAdapter(val activity: AppCompatActivity, pages: List) : + AbstractListAdapter(pages) { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SetupPageViewHolder { - val binding = PageSetupBinding.inflate(LayoutInflater.from(parent.context), parent, false) - return SetupPageViewHolder(binding) + PageSetupBinding.inflate(LayoutInflater.from(parent.context), parent, false) + .also { return SetupPageViewHolder(it) } } - override fun getItemCount(): Int = pages.size - - override fun onBindViewHolder(holder: SetupPageViewHolder, position: Int) = - holder.bind(pages[position]) - inner class SetupPageViewHolder(val binding: PageSetupBinding) : - RecyclerView.ViewHolder(binding.root), SetupCallback { - lateinit var page: SetupPage - - init { - itemView.tag = this - } - - fun bind(page: SetupPage) { - this.page = page - - if (page.stepCompleted.invoke() == StepState.COMPLETE) { + AbstractViewHolder(binding), SetupCallback { + override fun bind(model: SetupPage) { + if (model.stepCompleted.invoke() == StepState.COMPLETE) { binding.buttonAction.visibility = View.INVISIBLE binding.textConfirmation.visibility = View.VISIBLE } @@ -50,31 +37,31 @@ class SetupAdapter(val activity: AppCompatActivity, val pages: List) binding.icon.setImageDrawable( ResourcesCompat.getDrawable( activity.resources, - page.iconId, + model.iconId, activity.theme ) ) - binding.textTitle.text = activity.resources.getString(page.titleId) + binding.textTitle.text = activity.resources.getString(model.titleId) binding.textDescription.text = - Html.fromHtml(activity.resources.getString(page.descriptionId), 0) + Html.fromHtml(activity.resources.getString(model.descriptionId), 0) binding.buttonAction.apply { - text = activity.resources.getString(page.buttonTextId) - if (page.buttonIconId != 0) { + text = activity.resources.getString(model.buttonTextId) + if (model.buttonIconId != 0) { icon = ResourcesCompat.getDrawable( activity.resources, - page.buttonIconId, + model.buttonIconId, activity.theme ) } iconGravity = - if (page.leftAlignedIcon) { + if (model.leftAlignedIcon) { MaterialButton.ICON_GRAVITY_START } else { MaterialButton.ICON_GRAVITY_END } setOnClickListener { - page.buttonAction.invoke(this@SetupPageViewHolder) + model.buttonAction.invoke(this@SetupPageViewHolder) } } } -- cgit v1.2.3 From b17db2b4627acc1f9d0e9862ccfa802cf959f1e1 Mon Sep 17 00:00:00 2001 From: t895 Date: Wed, 10 Jan 2024 13:04:06 -0500 Subject: android: Create generic single selection list adapter --- .../adapters/AbstractSingleSelectionList.kt | 105 +++++++++++++++++++++ .../java/org/yuzu/yuzu_emu/model/SelectableItem.kt | 9 ++ 2 files changed, 114 insertions(+) create mode 100644 src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/AbstractSingleSelectionList.kt create mode 100644 src/android/app/src/main/java/org/yuzu/yuzu_emu/model/SelectableItem.kt (limited to 'src/android') diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/AbstractSingleSelectionList.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/AbstractSingleSelectionList.kt new file mode 100644 index 000000000..52163f9d7 --- /dev/null +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/AbstractSingleSelectionList.kt @@ -0,0 +1,105 @@ +// SPDX-FileCopyrightText: 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.yuzu.yuzu_emu.adapters + +import org.yuzu.yuzu_emu.model.SelectableItem +import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder + +/** + * Generic list class meant to take care of single selection UI updates + * @param currentList The list to show initially + * @param defaultSelection The default selection to use if no list items are selected by + * [SelectableItem.selected] or if the currently selected item is removed from the list + */ +abstract class AbstractSingleSelectionList< + Model : SelectableItem, + Holder : AbstractViewHolder + >( + final override var currentList: List, + private val defaultSelection: DefaultSelection = DefaultSelection.Start +) : AbstractListAdapter(currentList) { + var selectedItem = getDefaultSelection() + + init { + findSelectedItem() + } + + /** + * Changes the selection state of the [SelectableItem] that was selected and the previously selected + * item and notifies the underlying adapter of the change for those items. Invokes [callback] last. + * Does nothing if [position] is the same as the currently selected item. + * @param position Index of the item that was selected + * @param callback Lambda that's called at the end of the list changes and has the selected list + * position passed in as a parameter + */ + fun selectItem(position: Int, callback: ((position: Int) -> Unit)? = null) { + if (position == selectedItem) { + return + } + + val previouslySelectedItem = selectedItem + selectedItem = position + if (currentList.indices.contains(selectedItem)) { + currentList[selectedItem].onSelectionStateChanged(true) + } + if (currentList.indices.contains(previouslySelectedItem)) { + currentList[previouslySelectedItem].onSelectionStateChanged(false) + } + onItemChanged(previouslySelectedItem) + onItemChanged(selectedItem) + callback?.invoke(position) + } + + /** + * Removes a given item from the list and notifies the underlying adapter of the change. If the + * currently selected item was the item that was removed, the item at the position provided + * by [defaultSelection] will be made the new selection. Invokes [callback] last. + * @param position Index of the item that was removed + * @param callback Lambda that's called at the end of the list changes and has the removed and + * selected list positions passed in as parameters + */ + fun removeSelectableItem( + position: Int, + callback: ((removedPosition: Int, selectedPosition: Int) -> Unit)? + ) { + removeItem(position) + if (position == selectedItem) { + selectedItem = getDefaultSelection() + currentList[selectedItem].onSelectionStateChanged(true) + onItemChanged(selectedItem) + } else if (position < selectedItem) { + selectedItem-- + } + callback?.invoke(position, selectedItem) + } + + override fun addItem(item: Model, position: Int, callback: ((Int) -> Unit)?) { + super.addItem(item, position, callback) + if (position <= selectedItem && position != -1) { + selectedItem++ + } + } + + override fun replaceList(newList: List) { + super.replaceList(newList) + findSelectedItem() + } + + private fun findSelectedItem() { + for (i in currentList.indices) { + if (currentList[i].selected) { + selectedItem = i + break + } + } + } + + private fun getDefaultSelection(): Int = + when (defaultSelection) { + DefaultSelection.Start -> currentList.indices.first + DefaultSelection.End -> currentList.indices.last + } + + enum class DefaultSelection { Start, End } +} diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/SelectableItem.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/SelectableItem.kt new file mode 100644 index 000000000..11c22d323 --- /dev/null +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/SelectableItem.kt @@ -0,0 +1,9 @@ +// SPDX-FileCopyrightText: 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.yuzu.yuzu_emu.model + +interface SelectableItem { + var selected: Boolean + fun onSelectionStateChanged(selected: Boolean) +} -- cgit v1.2.3 From 93239f191a17179599ccadea29b9f4adb5a4dc60 Mon Sep 17 00:00:00 2001 From: t895 Date: Wed, 10 Jan 2024 13:05:50 -0500 Subject: android: Refactor DriverAdapter to use AbstractSingleSelectionList --- .../org/yuzu/yuzu_emu/adapters/DriverAdapter.kt | 96 ++++++---------------- 1 file changed, 23 insertions(+), 73 deletions(-) (limited to 'src/android') diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/DriverAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/DriverAdapter.kt index d290a656c..ca353cea7 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/DriverAdapter.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/DriverAdapter.kt @@ -7,65 +7,34 @@ 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.Driver import org.yuzu.yuzu_emu.model.DriverViewModel -import org.yuzu.yuzu_emu.utils.GpuDriverHelper -import org.yuzu.yuzu_emu.utils.GpuDriverMetadata +import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder class DriverAdapter(private val driverViewModel: DriverViewModel) : - ListAdapter, DriverAdapter.DriverViewHolder>( - AsyncDifferConfig.Builder(DiffCallback()).build() + AbstractSingleSelectionList( + driverViewModel.driverList.value ) { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): DriverViewHolder { - val binding = - CardDriverOptionBinding.inflate(LayoutInflater.from(parent.context), parent, false) - return DriverViewHolder(binding) - } - - override fun getItemCount(): Int = currentList.size - - override fun onBindViewHolder(holder: DriverViewHolder, position: Int) = - holder.bind(currentList[position]) - - private fun onSelectDriver(position: Int) { - driverViewModel.setSelectedDriverIndex(position) - notifyItemChanged(driverViewModel.previouslySelectedDriver) - notifyItemChanged(driverViewModel.selectedDriver) - } - - private fun onDeleteDriver(driverData: Pair, position: Int) { - if (driverViewModel.selectedDriver > position) { - driverViewModel.setSelectedDriverIndex(driverViewModel.selectedDriver - 1) - } - if (GpuDriverHelper.customDriverSettingData == driverData.second) { - driverViewModel.setSelectedDriverIndex(0) - } - driverViewModel.driversToDelete.add(driverData.first) - driverViewModel.removeDriver(driverData) - notifyItemRemoved(position) - notifyItemChanged(driverViewModel.selectedDriver) + CardDriverOptionBinding.inflate(LayoutInflater.from(parent.context), parent, false) + .also { return DriverViewHolder(it) } } inner class DriverViewHolder(val binding: CardDriverOptionBinding) : - RecyclerView.ViewHolder(binding.root) { - private lateinit var driverData: Pair - - fun bind(driverData: Pair) { - this.driverData = driverData - val driver = driverData.second - + AbstractViewHolder(binding) { + override fun bind(model: Driver) { binding.apply { - radioButton.isChecked = driverViewModel.selectedDriver == bindingAdapterPosition + radioButton.isChecked = model.selected root.setOnClickListener { - onSelectDriver(bindingAdapterPosition) + selectItem(bindingAdapterPosition) { driverViewModel.onDriverSelected(it) } } buttonDelete.setOnClickListener { - onDeleteDriver(driverData, bindingAdapterPosition) + removeSelectableItem( + bindingAdapterPosition + ) { removedPosition: Int, selectedPosition: Int -> + driverViewModel.onDriverRemoved(removedPosition, selectedPosition) + } } // Delay marquee by 3s @@ -80,38 +49,19 @@ class DriverAdapter(private val driverViewModel: DriverViewModel) : }, 3000 ) - if (driver.name == null) { - title.setText(R.string.system_gpu_driver) - description.text = "" - version.text = "" - version.visibility = View.GONE - description.visibility = View.GONE - buttonDelete.visibility = View.GONE - } else { - title.text = driver.name - version.text = driver.version - description.text = driver.description + title.text = model.title + version.text = model.version + description.text = model.description + if (model.description.isNotEmpty()) { version.visibility = View.VISIBLE description.visibility = View.VISIBLE buttonDelete.visibility = View.VISIBLE + } else { + version.visibility = View.GONE + description.visibility = View.GONE + buttonDelete.visibility = View.GONE } } } } - - private class DiffCallback : DiffUtil.ItemCallback>() { - override fun areItemsTheSame( - oldItem: Pair, - newItem: Pair - ): Boolean { - return oldItem.first == newItem.first - } - - override fun areContentsTheSame( - oldItem: Pair, - newItem: Pair - ): Boolean { - return oldItem.second == newItem.second - } - } } -- cgit v1.2.3 From 6bfc3c530ce0c8ba3ac0a62609d1266aa8d67d35 Mon Sep 17 00:00:00 2001 From: t895 Date: Wed, 10 Jan 2024 13:09:06 -0500 Subject: android: Rework driver fragment Applies settings upon selection and uses a new Driver model to represent the information in-view. Also switches from an async diff list to a plain one. --- .../yuzu_emu/fragments/DriverManagerFragment.kt | 39 +++---- .../main/java/org/yuzu/yuzu_emu/model/Driver.kt | 27 +++++ .../org/yuzu/yuzu_emu/model/DriverViewModel.kt | 125 +++++++-------------- .../org/yuzu/yuzu_emu/utils/GpuDriverHelper.kt | 3 - 4 files changed, 84 insertions(+), 110 deletions(-) create mode 100644 src/android/app/src/main/java/org/yuzu/yuzu_emu/model/Driver.kt (limited to 'src/android') diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/DriverManagerFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/DriverManagerFragment.kt index cc71254dc..82c966954 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/DriverManagerFragment.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/DriverManagerFragment.kt @@ -13,16 +13,16 @@ 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.navigation.fragment.navArgs import androidx.recyclerview.widget.GridLayoutManager import com.google.android.material.transition.MaterialSharedAxis -import kotlinx.coroutines.flow.collectLatest -import kotlinx.coroutines.launch +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext import org.yuzu.yuzu_emu.R import org.yuzu.yuzu_emu.adapters.DriverAdapter import org.yuzu.yuzu_emu.databinding.FragmentDriverManagerBinding +import org.yuzu.yuzu_emu.model.Driver.Companion.toDriver import org.yuzu.yuzu_emu.model.DriverViewModel import org.yuzu.yuzu_emu.model.HomeViewModel import org.yuzu.yuzu_emu.utils.FileUtil @@ -85,25 +85,6 @@ class DriverManagerFragment : Fragment() { adapter = DriverAdapter(driverViewModel) } - viewLifecycleOwner.lifecycleScope.apply { - launch { - driverViewModel.driverList.collectLatest { - (binding.listDrivers.adapter as DriverAdapter).submitList(it) - } - } - launch { - driverViewModel.newDriverInstalled.collect { - if (_binding != null && it) { - (binding.listDrivers.adapter as DriverAdapter).apply { - notifyItemChanged(driverViewModel.previouslySelectedDriver) - notifyItemChanged(driverViewModel.selectedDriver) - driverViewModel.setNewDriverInstalled(false) - } - } - } - } - } - setInsets() } @@ -177,12 +158,20 @@ class DriverManagerFragment : Fragment() { val driverData = GpuDriverHelper.getMetadataFromZip(driverFile) val driverInList = - driverViewModel.driverList.value.firstOrNull { it.second == driverData } + driverViewModel.driverData.firstOrNull { it.second == driverData } if (driverInList != null) { return@newInstance getString(R.string.driver_already_installed) } else { - driverViewModel.addDriver(Pair(driverPath, driverData)) - driverViewModel.setNewDriverInstalled(true) + driverViewModel.onDriverAdded(Pair(driverPath, driverData)) + withContext(Dispatchers.Main) { + if (_binding != null) { + val adapter = binding.listDrivers.adapter as DriverAdapter + adapter.addItem(driverData.toDriver()) + adapter.selectItem(adapter.currentList.indices.last) + binding.listDrivers + .smoothScrollToPosition(adapter.currentList.indices.last) + } + } } return@newInstance Any() }.show(childFragmentManager, IndeterminateProgressDialogFragment.TAG) diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/Driver.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/Driver.kt new file mode 100644 index 000000000..de342212a --- /dev/null +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/Driver.kt @@ -0,0 +1,27 @@ +// SPDX-FileCopyrightText: 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.yuzu.yuzu_emu.model + +import org.yuzu.yuzu_emu.utils.GpuDriverMetadata + +data class Driver( + override var selected: Boolean, + val title: String, + val version: String = "", + val description: String = "" +) : SelectableItem { + override fun onSelectionStateChanged(selected: Boolean) { + this.selected = selected + } + + companion object { + fun GpuDriverMetadata.toDriver(selected: Boolean = false): Driver = + Driver( + selected, + this.name ?: "", + this.version ?: "", + this.description ?: "" + ) + } +} diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/DriverViewModel.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/DriverViewModel.kt index 76accf8f3..a1fee48cc 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/DriverViewModel.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/DriverViewModel.kt @@ -17,11 +17,10 @@ import org.yuzu.yuzu_emu.R import org.yuzu.yuzu_emu.YuzuApplication import org.yuzu.yuzu_emu.features.settings.model.StringSetting import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile -import org.yuzu.yuzu_emu.utils.FileUtil +import org.yuzu.yuzu_emu.model.Driver.Companion.toDriver import org.yuzu.yuzu_emu.utils.GpuDriverHelper import org.yuzu.yuzu_emu.utils.GpuDriverMetadata import org.yuzu.yuzu_emu.utils.NativeConfig -import java.io.BufferedOutputStream import java.io.File class DriverViewModel : ViewModel() { @@ -38,97 +37,74 @@ class DriverViewModel : ViewModel() { !loading && ready && !deleting }.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), initialValue = false) - private val _driverList = MutableStateFlow(GpuDriverHelper.getDrivers()) - val driverList: StateFlow>> get() = _driverList + var driverData = GpuDriverHelper.getDrivers() - var previouslySelectedDriver = 0 - var selectedDriver = -1 + private val _driverList = MutableStateFlow(emptyList()) + val driverList: StateFlow> get() = _driverList // Used for showing which driver is currently installed within the driver manager card private val _selectedDriverTitle = MutableStateFlow("") val selectedDriverTitle: StateFlow get() = _selectedDriverTitle - private val _newDriverInstalled = MutableStateFlow(false) - val newDriverInstalled: StateFlow get() = _newDriverInstalled - - val driversToDelete = mutableListOf() + private val driversToDelete = mutableListOf() init { - val currentDriverMetadata = GpuDriverHelper.installedCustomDriverData - findSelectedDriver(currentDriverMetadata) - - // If a user had installed a driver before the manager was implemented, this zips - // the installed driver to UserData/gpu_drivers/CustomDriver.zip so that it can - // be indexed and exported as expected. - if (selectedDriver == -1) { - val driverToSave = - File(GpuDriverHelper.driverStoragePath, "CustomDriver.zip") - driverToSave.createNewFile() - FileUtil.zipFromInternalStorage( - File(GpuDriverHelper.driverInstallationPath!!), - GpuDriverHelper.driverInstallationPath!!, - BufferedOutputStream(driverToSave.outputStream()) - ) - _driverList.value.add(Pair(driverToSave.path, currentDriverMetadata)) - setSelectedDriverIndex(_driverList.value.size - 1) - } - - // If a user had installed a driver before the config was reworked to be multiplatform, - // we have save the path of the previously selected driver to the new setting. - if (StringSetting.DRIVER_PATH.getString(true).isEmpty() && selectedDriver > 0 && - StringSetting.DRIVER_PATH.global - ) { - StringSetting.DRIVER_PATH.setString(_driverList.value[selectedDriver].first) - NativeConfig.saveGlobalConfig() - } else { - findSelectedDriver(GpuDriverHelper.customDriverSettingData) - } + updateDriverList() updateDriverNameForGame(null) } - fun setSelectedDriverIndex(value: Int) { - if (selectedDriver != -1) { - previouslySelectedDriver = selectedDriver - } - selectedDriver = value + fun reloadDriverData() { + _areDriversLoading.value = true + driverData = GpuDriverHelper.getDrivers() + updateDriverList() + _areDriversLoading.value = false } - fun setNewDriverInstalled(value: Boolean) { - _newDriverInstalled.value = value - } - - fun addDriver(driverData: Pair) { - val driverIndex = _driverList.value.indexOfFirst { it == driverData } - if (driverIndex == -1) { - _driverList.value.add(driverData) - setSelectedDriverIndex(_driverList.value.size - 1) - _selectedDriverTitle.value = driverData.second.name - ?: YuzuApplication.appContext.getString(R.string.system_gpu_driver) - } else { - setSelectedDriverIndex(driverIndex) + private fun updateDriverList() { + val selectedDriver = GpuDriverHelper.customDriverSettingData + val newDriverList = mutableListOf( + Driver( + selectedDriver == GpuDriverMetadata(), + YuzuApplication.appContext.getString(R.string.system_gpu_driver) + ) + ) + driverData.forEach { + newDriverList.add(it.second.toDriver(it.second == selectedDriver)) } - } - - fun removeDriver(driverData: Pair) { - _driverList.value.remove(driverData) + _driverList.value = newDriverList } fun onOpenDriverManager(game: Game?) { if (game != null) { SettingsFile.loadCustomConfig(game) } + updateDriverList() + } - val driverPath = StringSetting.DRIVER_PATH.getString() - if (driverPath.isEmpty()) { - setSelectedDriverIndex(0) + fun onDriverSelected(position: Int) { + if (position == 0) { + StringSetting.DRIVER_PATH.setString("") } else { - findSelectedDriver(GpuDriverHelper.getMetadataFromZip(File(driverPath))) + StringSetting.DRIVER_PATH.setString(driverData[position - 1].first) } } + fun onDriverRemoved(removedPosition: Int, selectedPosition: Int) { + driversToDelete.add(driverData[removedPosition - 1].first) + driverData.removeAt(removedPosition - 1) + onDriverSelected(selectedPosition) + } + + fun onDriverAdded(driver: Pair) { + if (driversToDelete.contains(driver.first)) { + driversToDelete.remove(driver.first) + } + driverData.add(driver) + onDriverSelected(driverData.size) + } + fun onCloseDriverManager(game: Game?) { _isDeletingDrivers.value = true - StringSetting.DRIVER_PATH.setString(driverList.value[selectedDriver].first) updateDriverNameForGame(game) if (game == null) { NativeConfig.saveGlobalConfig() @@ -181,20 +157,6 @@ class DriverViewModel : ViewModel() { } } - private fun findSelectedDriver(currentDriverMetadata: GpuDriverMetadata) { - if (driverList.value.size == 1) { - setSelectedDriverIndex(0) - return - } - - driverList.value.forEachIndexed { i: Int, driver: Pair -> - if (driver.second == currentDriverMetadata) { - setSelectedDriverIndex(i) - return - } - } - } - fun updateDriverNameForGame(game: Game?) { if (!GpuDriverHelper.supportsCustomDriverLoading()) { return @@ -217,7 +179,6 @@ class DriverViewModel : ViewModel() { private fun setDriverReady() { _isDriverReady.value = true - _selectedDriverTitle.value = GpuDriverHelper.customDriverSettingData.name - ?: YuzuApplication.appContext.getString(R.string.system_gpu_driver) + updateName() } } diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GpuDriverHelper.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GpuDriverHelper.kt index 685272288..a8f9dcc34 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GpuDriverHelper.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GpuDriverHelper.kt @@ -62,9 +62,6 @@ object GpuDriverHelper { ?.sortedByDescending { it: Pair -> it.second.name } ?.distinct() ?.toMutableList() ?: mutableListOf() - - // TODO: Get system driver information - drivers.add(0, Pair("", GpuDriverMetadata())) return drivers } -- cgit v1.2.3 From 9e974d4c7e44b1067a5c71c56b172eb15c75e43f Mon Sep 17 00:00:00 2001 From: t895 Date: Wed, 10 Jan 2024 13:09:21 -0500 Subject: android: Reload driver data on importing user data --- .../app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src/android') diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt index 622ae996e..644289e25 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt @@ -41,6 +41,7 @@ import org.yuzu.yuzu_emu.fragments.AddGameFolderDialogFragment import org.yuzu.yuzu_emu.fragments.IndeterminateProgressDialogFragment import org.yuzu.yuzu_emu.fragments.MessageDialogFragment import org.yuzu.yuzu_emu.model.AddonViewModel +import org.yuzu.yuzu_emu.model.DriverViewModel import org.yuzu.yuzu_emu.model.GamesViewModel import org.yuzu.yuzu_emu.model.HomeViewModel import org.yuzu.yuzu_emu.model.TaskState @@ -58,6 +59,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider { private val gamesViewModel: GamesViewModel by viewModels() private val taskViewModel: TaskViewModel by viewModels() private val addonViewModel: AddonViewModel by viewModels() + private val driverViewModel: DriverViewModel by viewModels() override var themeId: Int = 0 @@ -689,6 +691,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider { NativeLibrary.initializeSystem(true) NativeConfig.initializeGlobalConfig() gamesViewModel.reloadGames(false) + driverViewModel.reloadDriverData() return@newInstance getString(R.string.user_data_import_success) }.show(supportFragmentManager, IndeterminateProgressDialogFragment.TAG) -- cgit v1.2.3 From dac8c4ce4d41d12b812caa59bc7f040220caf652 Mon Sep 17 00:00:00 2001 From: t895 Date: Wed, 10 Jan 2024 15:34:14 -0500 Subject: android: Add button to use global driver value --- .../org/yuzu/yuzu_emu/adapters/DriverAdapter.kt | 7 +++- .../yuzu_emu/fragments/DriverManagerFragment.kt | 39 ++++++++++++++++++++++ .../org/yuzu/yuzu_emu/model/DriverViewModel.kt | 10 +++++- .../app/src/main/res/menu/menu_driver_manager.xml | 11 ++++++ 4 files changed, 65 insertions(+), 2 deletions(-) create mode 100644 src/android/app/src/main/res/menu/menu_driver_manager.xml (limited to 'src/android') diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/DriverAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/DriverAdapter.kt index ca353cea7..d6f17cf29 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/DriverAdapter.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/DriverAdapter.kt @@ -8,6 +8,7 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import org.yuzu.yuzu_emu.databinding.CardDriverOptionBinding +import org.yuzu.yuzu_emu.features.settings.model.StringSetting import org.yuzu.yuzu_emu.model.Driver import org.yuzu.yuzu_emu.model.DriverViewModel import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder @@ -27,13 +28,17 @@ class DriverAdapter(private val driverViewModel: DriverViewModel) : binding.apply { radioButton.isChecked = model.selected root.setOnClickListener { - selectItem(bindingAdapterPosition) { driverViewModel.onDriverSelected(it) } + selectItem(bindingAdapterPosition) { + driverViewModel.onDriverSelected(it) + driverViewModel.showClearButton(!StringSetting.DRIVER_PATH.global) + } } buttonDelete.setOnClickListener { removeSelectableItem( bindingAdapterPosition ) { removedPosition: Int, selectedPosition: Int -> driverViewModel.onDriverRemoved(removedPosition, selectedPosition) + driverViewModel.showClearButton(!StringSetting.DRIVER_PATH.global) } } diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/DriverManagerFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/DriverManagerFragment.kt index 82c966954..c01fdff25 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/DriverManagerFragment.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/DriverManagerFragment.kt @@ -3,6 +3,7 @@ package org.yuzu.yuzu_emu.fragments +import android.annotation.SuppressLint import android.os.Bundle import android.view.LayoutInflater import android.view.View @@ -13,20 +14,26 @@ import androidx.core.view.WindowInsetsCompat import androidx.core.view.updatePadding import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.lifecycleScope +import androidx.lifecycle.repeatOnLifecycle import androidx.navigation.findNavController import androidx.navigation.fragment.navArgs import androidx.recyclerview.widget.GridLayoutManager import com.google.android.material.transition.MaterialSharedAxis import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import org.yuzu.yuzu_emu.R import org.yuzu.yuzu_emu.adapters.DriverAdapter import org.yuzu.yuzu_emu.databinding.FragmentDriverManagerBinding +import org.yuzu.yuzu_emu.features.settings.model.StringSetting import org.yuzu.yuzu_emu.model.Driver.Companion.toDriver import org.yuzu.yuzu_emu.model.DriverViewModel import org.yuzu.yuzu_emu.model.HomeViewModel import org.yuzu.yuzu_emu.utils.FileUtil import org.yuzu.yuzu_emu.utils.GpuDriverHelper +import org.yuzu.yuzu_emu.utils.NativeConfig import java.io.File import java.io.IOException @@ -55,12 +62,43 @@ class DriverManagerFragment : Fragment() { return binding.root } + // This is using the correct scope, lint is just acting up + @SuppressLint("UnsafeRepeatOnLifecycleDetector") override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) homeViewModel.setNavigationVisibility(visible = false, animated = true) homeViewModel.setStatusBarShadeVisibility(visible = false) driverViewModel.onOpenDriverManager(args.game) + if (NativeConfig.isPerGameConfigLoaded()) { + binding.toolbarDrivers.inflateMenu(R.menu.menu_driver_manager) + driverViewModel.showClearButton(!StringSetting.DRIVER_PATH.global) + binding.toolbarDrivers.setOnMenuItemClickListener { + when (it.itemId) { + R.id.menu_driver_clear -> { + StringSetting.DRIVER_PATH.global = true + driverViewModel.updateDriverList() + (binding.listDrivers.adapter as DriverAdapter) + .replaceList(driverViewModel.driverList.value) + driverViewModel.showClearButton(false) + true + } + + else -> false + } + } + + viewLifecycleOwner.lifecycleScope.apply { + launch { + repeatOnLifecycle(Lifecycle.State.STARTED) { + driverViewModel.showClearButton.collect { + binding.toolbarDrivers.menu + .findItem(R.id.menu_driver_clear).isVisible = it + } + } + } + } + } if (!driverViewModel.isInteractionAllowed.value) { DriversLoadingDialogFragment().show( @@ -168,6 +206,7 @@ class DriverManagerFragment : Fragment() { val adapter = binding.listDrivers.adapter as DriverAdapter adapter.addItem(driverData.toDriver()) adapter.selectItem(adapter.currentList.indices.last) + driverViewModel.showClearButton(!StringSetting.DRIVER_PATH.global) binding.listDrivers .smoothScrollToPosition(adapter.currentList.indices.last) } diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/DriverViewModel.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/DriverViewModel.kt index a1fee48cc..15ae3a42b 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/DriverViewModel.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/DriverViewModel.kt @@ -9,6 +9,7 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch @@ -46,6 +47,9 @@ class DriverViewModel : ViewModel() { private val _selectedDriverTitle = MutableStateFlow("") val selectedDriverTitle: StateFlow get() = _selectedDriverTitle + private val _showClearButton = MutableStateFlow(false) + val showClearButton = _showClearButton.asStateFlow() + private val driversToDelete = mutableListOf() init { @@ -60,7 +64,7 @@ class DriverViewModel : ViewModel() { _areDriversLoading.value = false } - private fun updateDriverList() { + fun updateDriverList() { val selectedDriver = GpuDriverHelper.customDriverSettingData val newDriverList = mutableListOf( Driver( @@ -81,6 +85,10 @@ class DriverViewModel : ViewModel() { updateDriverList() } + fun showClearButton(value: Boolean) { + _showClearButton.value = value + } + fun onDriverSelected(position: Int) { if (position == 0) { StringSetting.DRIVER_PATH.setString("") diff --git a/src/android/app/src/main/res/menu/menu_driver_manager.xml b/src/android/app/src/main/res/menu/menu_driver_manager.xml new file mode 100644 index 000000000..dee5d57b6 --- /dev/null +++ b/src/android/app/src/main/res/menu/menu_driver_manager.xml @@ -0,0 +1,11 @@ + + + + + + -- cgit v1.2.3 From d3ba6b334bdec89834f511dff70217fc9adb943c Mon Sep 17 00:00:00 2001 From: t895 Date: Wed, 10 Jan 2024 15:36:22 -0500 Subject: android: Fix added driver path While this didn't break anything, the extra separator was unnecessary --- .../src/main/java/org/yuzu/yuzu_emu/fragments/DriverManagerFragment.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/android') diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/DriverManagerFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/DriverManagerFragment.kt index c01fdff25..9dabb9c41 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/DriverManagerFragment.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/DriverManagerFragment.kt @@ -179,7 +179,7 @@ class DriverManagerFragment : Fragment() { false ) { val driverPath = - "${GpuDriverHelper.driverStoragePath}/${FileUtil.getFilename(result)}" + "${GpuDriverHelper.driverStoragePath}${FileUtil.getFilename(result)}" val driverFile = File(driverPath) // Ignore file exceptions when a user selects an invalid zip -- cgit v1.2.3 From aae9eea53208fc0924c90ebb1272fcfaa3f23e0c Mon Sep 17 00:00:00 2001 From: Liam Date: Mon, 8 Jan 2024 00:49:00 -0500 Subject: fsp-srv: use program registry for SetCurrentProcess --- src/android/app/src/main/jni/native.cpp | 6 +- src/core/CMakeLists.txt | 4 + src/core/core.cpp | 1 + src/core/file_sys/savedata_factory.cpp | 17 +- src/core/file_sys/savedata_factory.h | 10 +- src/core/hle/service/am/am.cpp | 7 +- src/core/hle/service/filesystem/filesystem.cpp | 227 +++++---------------- src/core/hle/service/filesystem/filesystem.h | 59 +++--- src/core/hle/service/filesystem/fsp_srv.cpp | 55 ++--- src/core/hle/service/filesystem/fsp_srv.h | 6 + .../hle/service/filesystem/romfs_controller.cpp | 37 ++++ src/core/hle/service/filesystem/romfs_controller.h | 31 +++ .../service/filesystem/save_data_controller.cpp | 99 +++++++++ .../hle/service/filesystem/save_data_controller.h | 35 ++++ src/core/loader/deconstructed_rom_directory.cpp | 14 -- src/core/loader/nca.cpp | 6 +- src/core/loader/nro.cpp | 6 +- src/core/loader/nsp.cpp | 3 +- src/core/loader/xci.cpp | 3 +- src/yuzu/main.cpp | 8 +- 20 files changed, 362 insertions(+), 272 deletions(-) create mode 100644 src/core/hle/service/filesystem/romfs_controller.cpp create mode 100644 src/core/hle/service/filesystem/romfs_controller.h create mode 100644 src/core/hle/service/filesystem/save_data_controller.cpp create mode 100644 src/core/hle/service/filesystem/save_data_controller.h (limited to 'src/android') diff --git a/src/android/app/src/main/jni/native.cpp b/src/android/app/src/main/jni/native.cpp index 136c8dee6..8017eb58d 100644 --- a/src/android/app/src/main/jni/native.cpp +++ b/src/android/app/src/main/jni/native.cpp @@ -770,8 +770,8 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_initializeEmptyUserDirectory(JNIEnv* ASSERT(user_id); const auto user_save_data_path = FileSys::SaveDataFactory::GetFullPath( - EmulationSession::GetInstance().System(), vfs_nand_dir, FileSys::SaveDataSpaceId::NandUser, - FileSys::SaveDataType::SaveData, 1, user_id->AsU128(), 0); + {}, vfs_nand_dir, FileSys::SaveDataSpaceId::NandUser, FileSys::SaveDataType::SaveData, 1, + user_id->AsU128(), 0); const auto full_path = Common::FS::ConcatPathSafe(nand_dir, user_save_data_path); if (!Common::FS::CreateParentDirs(full_path)) { @@ -878,7 +878,7 @@ jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getSavePath(JNIEnv* env, jobject j FileSys::Mode::Read); const auto user_save_data_path = FileSys::SaveDataFactory::GetFullPath( - system, vfsNandDir, FileSys::SaveDataSpaceId::NandUser, FileSys::SaveDataType::SaveData, + {}, vfsNandDir, FileSys::SaveDataSpaceId::NandUser, FileSys::SaveDataType::SaveData, program_id, user_id->AsU128(), 0); return ToJString(env, user_save_data_path); } diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 753f55ebe..293d9647b 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -490,6 +490,10 @@ add_library(core STATIC hle/service/filesystem/fsp_pr.h hle/service/filesystem/fsp_srv.cpp hle/service/filesystem/fsp_srv.h + hle/service/filesystem/romfs_controller.cpp + hle/service/filesystem/romfs_controller.h + hle/service/filesystem/save_data_controller.cpp + hle/service/filesystem/save_data_controller.h hle/service/fgm/fgm.cpp hle/service/fgm/fgm.h hle/service/friend/friend.cpp diff --git a/src/core/core.cpp b/src/core/core.cpp index c063f7719..461eea9c8 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -413,6 +413,7 @@ struct System::Impl { kernel.ShutdownCores(); services.reset(); service_manager.reset(); + fs_controller.Reset(); cheat_engine.reset(); telemetry_session.reset(); time_manager.Shutdown(); diff --git a/src/core/file_sys/savedata_factory.cpp b/src/core/file_sys/savedata_factory.cpp index 12b3bd797..23196cd5f 100644 --- a/src/core/file_sys/savedata_factory.cpp +++ b/src/core/file_sys/savedata_factory.cpp @@ -97,8 +97,9 @@ std::string SaveDataAttribute::DebugInfo() const { static_cast(rank), index); } -SaveDataFactory::SaveDataFactory(Core::System& system_, VirtualDir save_directory_) - : dir{std::move(save_directory_)}, system{system_} { +SaveDataFactory::SaveDataFactory(Core::System& system_, ProgramId program_id_, + VirtualDir save_directory_) + : system{system_}, program_id{program_id_}, dir{std::move(save_directory_)} { // Delete all temporary storages // On hardware, it is expected that temporary storage be empty at first use. dir->DeleteSubdirectoryRecursive("temp"); @@ -110,7 +111,7 @@ VirtualDir SaveDataFactory::Create(SaveDataSpaceId space, const SaveDataAttribut PrintSaveDataAttributeWarnings(meta); const auto save_directory = - GetFullPath(system, dir, space, meta.type, meta.title_id, meta.user_id, meta.save_id); + GetFullPath(program_id, dir, space, meta.type, meta.title_id, meta.user_id, meta.save_id); return dir->CreateDirectoryRelative(save_directory); } @@ -118,7 +119,7 @@ VirtualDir SaveDataFactory::Create(SaveDataSpaceId space, const SaveDataAttribut VirtualDir SaveDataFactory::Open(SaveDataSpaceId space, const SaveDataAttribute& meta) const { const auto save_directory = - GetFullPath(system, dir, space, meta.type, meta.title_id, meta.user_id, meta.save_id); + GetFullPath(program_id, dir, space, meta.type, meta.title_id, meta.user_id, meta.save_id); auto out = dir->GetDirectoryRelative(save_directory); @@ -147,14 +148,14 @@ std::string SaveDataFactory::GetSaveDataSpaceIdPath(SaveDataSpaceId space) { } } -std::string SaveDataFactory::GetFullPath(Core::System& system, VirtualDir dir, +std::string SaveDataFactory::GetFullPath(ProgramId program_id, VirtualDir dir, SaveDataSpaceId space, SaveDataType type, u64 title_id, u128 user_id, u64 save_id) { // According to switchbrew, if a save is of type SaveData and the title id field is 0, it should // be interpreted as the title id of the current process. if (type == SaveDataType::SaveData || type == SaveDataType::DeviceSaveData) { if (title_id == 0) { - title_id = system.GetApplicationProcessProgramID(); + title_id = program_id; } } @@ -201,7 +202,7 @@ std::string SaveDataFactory::GetUserGameSaveDataRoot(u128 user_id, bool future) SaveDataSize SaveDataFactory::ReadSaveDataSize(SaveDataType type, u64 title_id, u128 user_id) const { const auto path = - GetFullPath(system, dir, SaveDataSpaceId::NandUser, type, title_id, user_id, 0); + GetFullPath(program_id, dir, SaveDataSpaceId::NandUser, type, title_id, user_id, 0); const auto relative_dir = GetOrCreateDirectoryRelative(dir, path); const auto size_file = relative_dir->GetFile(GetSaveDataSizeFileName()); @@ -220,7 +221,7 @@ SaveDataSize SaveDataFactory::ReadSaveDataSize(SaveDataType type, u64 title_id, void SaveDataFactory::WriteSaveDataSize(SaveDataType type, u64 title_id, u128 user_id, SaveDataSize new_value) const { const auto path = - GetFullPath(system, dir, SaveDataSpaceId::NandUser, type, title_id, user_id, 0); + GetFullPath(program_id, dir, SaveDataSpaceId::NandUser, type, title_id, user_id, 0); const auto relative_dir = GetOrCreateDirectoryRelative(dir, path); const auto size_file = relative_dir->CreateFile(GetSaveDataSizeFileName()); diff --git a/src/core/file_sys/savedata_factory.h b/src/core/file_sys/savedata_factory.h index fd4887e99..30d96928e 100644 --- a/src/core/file_sys/savedata_factory.h +++ b/src/core/file_sys/savedata_factory.h @@ -87,10 +87,13 @@ constexpr const char* GetSaveDataSizeFileName() { return ".yuzu_save_size"; } +using ProgramId = u64; + /// File system interface to the SaveData archive class SaveDataFactory { public: - explicit SaveDataFactory(Core::System& system_, VirtualDir save_directory_); + explicit SaveDataFactory(Core::System& system_, ProgramId program_id_, + VirtualDir save_directory_); ~SaveDataFactory(); VirtualDir Create(SaveDataSpaceId space, const SaveDataAttribute& meta) const; @@ -99,7 +102,7 @@ public: VirtualDir GetSaveDataSpaceDirectory(SaveDataSpaceId space) const; static std::string GetSaveDataSpaceIdPath(SaveDataSpaceId space); - static std::string GetFullPath(Core::System& system, VirtualDir dir, SaveDataSpaceId space, + static std::string GetFullPath(ProgramId program_id, VirtualDir dir, SaveDataSpaceId space, SaveDataType type, u64 title_id, u128 user_id, u64 save_id); static std::string GetUserGameSaveDataRoot(u128 user_id, bool future); @@ -110,8 +113,9 @@ public: void SetAutoCreate(bool state); private: - VirtualDir dir; Core::System& system; + ProgramId program_id; + VirtualDir dir; bool auto_create{true}; }; diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index 9e05bdafa..a768bdc54 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp @@ -36,6 +36,7 @@ #include "core/hle/service/caps/caps_su.h" #include "core/hle/service/caps/caps_types.h" #include "core/hle/service/filesystem/filesystem.h" +#include "core/hle/service/filesystem/save_data_controller.h" #include "core/hle/service/ipc_helpers.h" #include "core/hle/service/ns/ns.h" #include "core/hle/service/nvnflinger/fb_share_buffer_manager.h" @@ -2178,7 +2179,7 @@ void IApplicationFunctions::EnsureSaveData(HLERequestContext& ctx) { attribute.type = FileSys::SaveDataType::SaveData; FileSys::VirtualDir save_data{}; - const auto res = system.GetFileSystemController().CreateSaveData( + const auto res = system.GetFileSystemController().OpenSaveDataController()->CreateSaveData( &save_data, FileSys::SaveDataSpaceId::NandUser, attribute); IPC::ResponseBuilder rb{ctx, 4}; @@ -2353,7 +2354,7 @@ void IApplicationFunctions::ExtendSaveData(HLERequestContext& ctx) { "new_journal={:016X}", static_cast(type), user_id[1], user_id[0], new_normal_size, new_journal_size); - system.GetFileSystemController().WriteSaveDataSize( + system.GetFileSystemController().OpenSaveDataController()->WriteSaveDataSize( type, system.GetApplicationProcessProgramID(), user_id, {new_normal_size, new_journal_size}); @@ -2378,7 +2379,7 @@ void IApplicationFunctions::GetSaveDataSize(HLERequestContext& ctx) { LOG_DEBUG(Service_AM, "called with type={:02X}, user_id={:016X}{:016X}", type, user_id[1], user_id[0]); - const auto size = system.GetFileSystemController().ReadSaveDataSize( + const auto size = system.GetFileSystemController().OpenSaveDataController()->ReadSaveDataSize( type, system.GetApplicationProcessProgramID(), user_id); IPC::ResponseBuilder rb{ctx, 6}; diff --git a/src/core/hle/service/filesystem/filesystem.cpp b/src/core/hle/service/filesystem/filesystem.cpp index 780f8c74d..ca6d8d607 100644 --- a/src/core/hle/service/filesystem/filesystem.cpp +++ b/src/core/hle/service/filesystem/filesystem.cpp @@ -24,15 +24,13 @@ #include "core/hle/service/filesystem/fsp_ldr.h" #include "core/hle/service/filesystem/fsp_pr.h" #include "core/hle/service/filesystem/fsp_srv.h" +#include "core/hle/service/filesystem/romfs_controller.h" +#include "core/hle/service/filesystem/save_data_controller.h" #include "core/hle/service/server_manager.h" #include "core/loader/loader.h" namespace Service::FileSystem { -// A default size for normal/journal save data size if application control metadata cannot be found. -// This should be large enough to satisfy even the most extreme requirements (~4.2GB) -constexpr u64 SUFFICIENT_SAVE_DATA_SIZE = 0xF0000000; - static FileSys::VirtualDir GetDirectoryRelativeWrapped(FileSys::VirtualDir base, std::string_view dir_name_) { std::string dir_name(Common::FS::SanitizePath(dir_name_)); @@ -297,145 +295,65 @@ FileSystemController::FileSystemController(Core::System& system_) : system{syste FileSystemController::~FileSystemController() = default; -Result FileSystemController::RegisterRomFS(std::unique_ptr&& factory) { - romfs_factory = std::move(factory); - LOG_DEBUG(Service_FS, "Registered RomFS"); - return ResultSuccess; -} - -Result FileSystemController::RegisterSaveData(std::unique_ptr&& factory) { - ASSERT_MSG(save_data_factory == nullptr, "Tried to register a second save data"); - save_data_factory = std::move(factory); - LOG_DEBUG(Service_FS, "Registered save data"); - return ResultSuccess; -} +Result FileSystemController::RegisterProcess( + ProcessId process_id, ProgramId program_id, + std::shared_ptr&& romfs_factory) { + std::scoped_lock lk{registration_lock}; -Result FileSystemController::RegisterSDMC(std::unique_ptr&& factory) { - ASSERT_MSG(sdmc_factory == nullptr, "Tried to register a second SDMC"); - sdmc_factory = std::move(factory); - LOG_DEBUG(Service_FS, "Registered SDMC"); - return ResultSuccess; -} + registrations.emplace(process_id, Registration{ + .program_id = program_id, + .romfs_factory = std::move(romfs_factory), + .save_data_factory = CreateSaveDataFactory(program_id), + }); -Result FileSystemController::RegisterBIS(std::unique_ptr&& factory) { - ASSERT_MSG(bis_factory == nullptr, "Tried to register a second BIS"); - bis_factory = std::move(factory); - LOG_DEBUG(Service_FS, "Registered BIS"); + LOG_DEBUG(Service_FS, "Registered for process {}", process_id); return ResultSuccess; } -void FileSystemController::SetPackedUpdate(FileSys::VirtualFile update_raw) { - LOG_TRACE(Service_FS, "Setting packed update for romfs"); - - if (romfs_factory == nullptr) - return; - - romfs_factory->SetPackedUpdate(std::move(update_raw)); -} - -FileSys::VirtualFile FileSystemController::OpenRomFSCurrentProcess() const { - LOG_TRACE(Service_FS, "Opening RomFS for current process"); - - if (romfs_factory == nullptr) { - return nullptr; - } - - return romfs_factory->OpenCurrentProcess(system.GetApplicationProcessProgramID()); -} - -FileSys::VirtualFile FileSystemController::OpenPatchedRomFS(u64 title_id, - FileSys::ContentRecordType type) const { - LOG_TRACE(Service_FS, "Opening patched RomFS for title_id={:016X}", title_id); - - if (romfs_factory == nullptr) { - return nullptr; - } - - return romfs_factory->OpenPatchedRomFS(title_id, type); -} - -FileSys::VirtualFile FileSystemController::OpenPatchedRomFSWithProgramIndex( - u64 title_id, u8 program_index, FileSys::ContentRecordType type) const { - LOG_TRACE(Service_FS, "Opening patched RomFS for title_id={:016X}, program_index={}", title_id, - program_index); - - if (romfs_factory == nullptr) { - return nullptr; - } - - return romfs_factory->OpenPatchedRomFSWithProgramIndex(title_id, program_index, type); -} - -FileSys::VirtualFile FileSystemController::OpenRomFS(u64 title_id, FileSys::StorageId storage_id, - FileSys::ContentRecordType type) const { - LOG_TRACE(Service_FS, "Opening RomFS for title_id={:016X}, storage_id={:02X}, type={:02X}", - title_id, storage_id, type); - - if (romfs_factory == nullptr) { - return nullptr; - } - - return romfs_factory->Open(title_id, storage_id, type); -} - -std::shared_ptr FileSystemController::OpenBaseNca( - u64 title_id, FileSys::StorageId storage_id, FileSys::ContentRecordType type) const { - return romfs_factory->GetEntry(title_id, storage_id, type); -} - -Result FileSystemController::CreateSaveData(FileSys::VirtualDir* out_save_data, - FileSys::SaveDataSpaceId space, - const FileSys::SaveDataAttribute& save_struct) const { - LOG_TRACE(Service_FS, "Creating Save Data for space_id={:01X}, save_struct={}", space, - save_struct.DebugInfo()); +Result FileSystemController::OpenProcess( + ProgramId* out_program_id, std::shared_ptr* out_save_data_controller, + std::shared_ptr* out_romfs_controller, ProcessId process_id) { + std::scoped_lock lk{registration_lock}; - if (save_data_factory == nullptr) { + const auto it = registrations.find(process_id); + if (it == registrations.end()) { return FileSys::ERROR_ENTITY_NOT_FOUND; } - auto save_data = save_data_factory->Create(space, save_struct); - if (save_data == nullptr) { - return FileSys::ERROR_ENTITY_NOT_FOUND; - } - - *out_save_data = save_data; + *out_program_id = it->second.program_id; + *out_save_data_controller = + std::make_shared(system, it->second.save_data_factory); + *out_romfs_controller = + std::make_shared(it->second.romfs_factory, it->second.program_id); return ResultSuccess; } -Result FileSystemController::OpenSaveData(FileSys::VirtualDir* out_save_data, - FileSys::SaveDataSpaceId space, - const FileSys::SaveDataAttribute& attribute) const { - LOG_TRACE(Service_FS, "Opening Save Data for space_id={:01X}, save_struct={}", space, - attribute.DebugInfo()); - - if (save_data_factory == nullptr) { - return FileSys::ERROR_ENTITY_NOT_FOUND; - } +void FileSystemController::SetPackedUpdate(ProcessId process_id, FileSys::VirtualFile update_raw) { + LOG_TRACE(Service_FS, "Setting packed update for romfs"); - auto save_data = save_data_factory->Open(space, attribute); - if (save_data == nullptr) { - return FileSys::ERROR_ENTITY_NOT_FOUND; + std::scoped_lock lk{registration_lock}; + const auto it = registrations.find(process_id); + if (it == registrations.end()) { + return; } - *out_save_data = save_data; - return ResultSuccess; + it->second.romfs_factory->SetPackedUpdate(std::move(update_raw)); } -Result FileSystemController::OpenSaveDataSpace(FileSys::VirtualDir* out_save_data_space, - FileSys::SaveDataSpaceId space) const { - LOG_TRACE(Service_FS, "Opening Save Data Space for space_id={:01X}", space); - - if (save_data_factory == nullptr) { - return FileSys::ERROR_ENTITY_NOT_FOUND; - } +std::shared_ptr FileSystemController::OpenSaveDataController() { + return std::make_shared(system, CreateSaveDataFactory(ProgramId{})); +} - auto save_data_space = save_data_factory->GetSaveDataSpaceDirectory(space); - if (save_data_space == nullptr) { - return FileSys::ERROR_ENTITY_NOT_FOUND; - } +std::shared_ptr FileSystemController::CreateSaveDataFactory( + ProgramId program_id) { + using YuzuPath = Common::FS::YuzuPath; + const auto rw_mode = FileSys::Mode::ReadWrite; - *out_save_data_space = save_data_space; - return ResultSuccess; + auto vfs = system.GetFilesystem(); + const auto nand_directory = + vfs->OpenDirectory(Common::FS::GetYuzuPathString(YuzuPath::NANDDir), rw_mode); + return std::make_shared(system, program_id, + std::move(nand_directory)); } Result FileSystemController::OpenSDMC(FileSys::VirtualDir* out_sdmc) const { @@ -540,48 +458,6 @@ u64 FileSystemController::GetTotalSpaceSize(FileSys::StorageId id) const { return 0; } -FileSys::SaveDataSize FileSystemController::ReadSaveDataSize(FileSys::SaveDataType type, - u64 title_id, u128 user_id) const { - if (save_data_factory == nullptr) { - return {0, 0}; - } - - const auto value = save_data_factory->ReadSaveDataSize(type, title_id, user_id); - - if (value.normal == 0 && value.journal == 0) { - FileSys::SaveDataSize new_size{SUFFICIENT_SAVE_DATA_SIZE, SUFFICIENT_SAVE_DATA_SIZE}; - - FileSys::NACP nacp; - const auto res = system.GetAppLoader().ReadControlData(nacp); - - if (res != Loader::ResultStatus::Success) { - const FileSys::PatchManager pm{system.GetApplicationProcessProgramID(), - system.GetFileSystemController(), - system.GetContentProvider()}; - const auto metadata = pm.GetControlMetadata(); - const auto& nacp_unique = metadata.first; - - if (nacp_unique != nullptr) { - new_size = {nacp_unique->GetDefaultNormalSaveSize(), - nacp_unique->GetDefaultJournalSaveSize()}; - } - } else { - new_size = {nacp.GetDefaultNormalSaveSize(), nacp.GetDefaultJournalSaveSize()}; - } - - WriteSaveDataSize(type, title_id, user_id, new_size); - return new_size; - } - - return value; -} - -void FileSystemController::WriteSaveDataSize(FileSys::SaveDataType type, u64 title_id, u128 user_id, - FileSys::SaveDataSize new_value) const { - if (save_data_factory != nullptr) - save_data_factory->WriteSaveDataSize(type, title_id, user_id, new_value); -} - void FileSystemController::SetGameCard(FileSys::VirtualFile file) { gamecard = std::make_unique(file); const auto dir = gamecard->ConcatenatedPseudoDirectory(); @@ -801,14 +677,9 @@ FileSys::VirtualDir FileSystemController::GetBCATDirectory(u64 title_id) const { return bis_factory->GetBCATDirectory(title_id); } -void FileSystemController::SetAutoSaveDataCreation(bool enable) { - save_data_factory->SetAutoCreate(enable); -} - void FileSystemController::CreateFactories(FileSys::VfsFilesystem& vfs, bool overwrite) { if (overwrite) { bis_factory = nullptr; - save_data_factory = nullptr; sdmc_factory = nullptr; } @@ -836,11 +707,6 @@ void FileSystemController::CreateFactories(FileSys::VfsFilesystem& vfs, bool ove bis_factory->GetUserNANDContents()); } - if (save_data_factory == nullptr) { - save_data_factory = - std::make_unique(system, std::move(nand_directory)); - } - if (sdmc_factory == nullptr) { sdmc_factory = std::make_unique(std::move(sd_directory), std::move(sd_load_directory)); @@ -849,12 +715,19 @@ void FileSystemController::CreateFactories(FileSys::VfsFilesystem& vfs, bool ove } } +void FileSystemController::Reset() { + std::scoped_lock lk{registration_lock}; + registrations.clear(); +} + void LoopProcess(Core::System& system) { auto server_manager = std::make_unique(system); + const auto FileSystemProxyFactory = [&] { return std::make_shared(system); }; + server_manager->RegisterNamedService("fsp-ldr", std::make_shared(system)); server_manager->RegisterNamedService("fsp:pr", std::make_shared(system)); - server_manager->RegisterNamedService("fsp-srv", std::make_shared(system)); + server_manager->RegisterNamedService("fsp-srv", std::move(FileSystemProxyFactory)); ServerManager::RunServer(std::move(server_manager)); } diff --git a/src/core/hle/service/filesystem/filesystem.h b/src/core/hle/service/filesystem/filesystem.h index 276d264e1..48f37d289 100644 --- a/src/core/hle/service/filesystem/filesystem.h +++ b/src/core/hle/service/filesystem/filesystem.h @@ -43,6 +43,9 @@ class ServiceManager; namespace FileSystem { +class RomFsController; +class SaveDataController; + enum class ContentStorageId : u32 { System, User, @@ -61,32 +64,24 @@ enum class OpenDirectoryMode : u64 { }; DECLARE_ENUM_FLAG_OPERATORS(OpenDirectoryMode); +using ProcessId = u64; +using ProgramId = u64; + class FileSystemController { public: explicit FileSystemController(Core::System& system_); ~FileSystemController(); - Result RegisterRomFS(std::unique_ptr&& factory); - Result RegisterSaveData(std::unique_ptr&& factory); - Result RegisterSDMC(std::unique_ptr&& factory); - Result RegisterBIS(std::unique_ptr&& factory); - - void SetPackedUpdate(FileSys::VirtualFile update_raw); - FileSys::VirtualFile OpenRomFSCurrentProcess() const; - FileSys::VirtualFile OpenPatchedRomFS(u64 title_id, FileSys::ContentRecordType type) const; - FileSys::VirtualFile OpenPatchedRomFSWithProgramIndex(u64 title_id, u8 program_index, - FileSys::ContentRecordType type) const; - FileSys::VirtualFile OpenRomFS(u64 title_id, FileSys::StorageId storage_id, - FileSys::ContentRecordType type) const; - std::shared_ptr OpenBaseNca(u64 title_id, FileSys::StorageId storage_id, - FileSys::ContentRecordType type) const; - - Result CreateSaveData(FileSys::VirtualDir* out_save_data, FileSys::SaveDataSpaceId space, - const FileSys::SaveDataAttribute& save_struct) const; - Result OpenSaveData(FileSys::VirtualDir* out_save_data, FileSys::SaveDataSpaceId space, - const FileSys::SaveDataAttribute& save_struct) const; - Result OpenSaveDataSpace(FileSys::VirtualDir* out_save_data_space, - FileSys::SaveDataSpaceId space) const; + Result RegisterProcess(ProcessId process_id, ProgramId program_id, + std::shared_ptr&& factory); + Result OpenProcess(ProgramId* out_program_id, + std::shared_ptr* out_save_data_controller, + std::shared_ptr* out_romfs_controller, + ProcessId process_id); + void SetPackedUpdate(ProcessId process_id, FileSys::VirtualFile update_raw); + + std::shared_ptr OpenSaveDataController(); + Result OpenSDMC(FileSys::VirtualDir* out_sdmc) const; Result OpenBISPartition(FileSys::VirtualDir* out_bis_partition, FileSys::BisPartitionId id) const; @@ -96,11 +91,6 @@ public: u64 GetFreeSpaceSize(FileSys::StorageId id) const; u64 GetTotalSpaceSize(FileSys::StorageId id) const; - FileSys::SaveDataSize ReadSaveDataSize(FileSys::SaveDataType type, u64 title_id, - u128 user_id) const; - void WriteSaveDataSize(FileSys::SaveDataType type, u64 title_id, u128 user_id, - FileSys::SaveDataSize new_value) const; - void SetGameCard(FileSys::VirtualFile file); FileSys::XCI* GetGameCard() const; @@ -133,15 +123,24 @@ public: FileSys::VirtualDir GetBCATDirectory(u64 title_id) const; - void SetAutoSaveDataCreation(bool enable); - // Creates the SaveData, SDMC, and BIS Factories. Should be called once and before any function // above is called. void CreateFactories(FileSys::VfsFilesystem& vfs, bool overwrite = true); + void Reset(); + private: - std::unique_ptr romfs_factory; - std::unique_ptr save_data_factory; + std::shared_ptr CreateSaveDataFactory(ProgramId program_id); + + struct Registration { + ProgramId program_id; + std::shared_ptr romfs_factory; + std::shared_ptr save_data_factory; + }; + + std::mutex registration_lock; + std::map registrations; + std::unique_ptr sdmc_factory; std::unique_ptr bis_factory; diff --git a/src/core/hle/service/filesystem/fsp_srv.cpp b/src/core/hle/service/filesystem/fsp_srv.cpp index 82ecc1b90..a2397bec4 100644 --- a/src/core/hle/service/filesystem/fsp_srv.cpp +++ b/src/core/hle/service/filesystem/fsp_srv.cpp @@ -27,6 +27,8 @@ #include "core/hle/result.h" #include "core/hle/service/filesystem/filesystem.h" #include "core/hle/service/filesystem/fsp_srv.h" +#include "core/hle/service/filesystem/romfs_controller.h" +#include "core/hle/service/filesystem/save_data_controller.h" #include "core/hle/service/hle_ipc.h" #include "core/hle/service/ipc_helpers.h" #include "core/reporter.h" @@ -577,9 +579,11 @@ private: class ISaveDataInfoReader final : public ServiceFramework { public: - explicit ISaveDataInfoReader(Core::System& system_, FileSys::SaveDataSpaceId space, - FileSystemController& fsc_) - : ServiceFramework{system_, "ISaveDataInfoReader"}, fsc{fsc_} { + explicit ISaveDataInfoReader(Core::System& system_, + std::shared_ptr save_data_controller_, + FileSys::SaveDataSpaceId space) + : ServiceFramework{system_, "ISaveDataInfoReader"}, save_data_controller{ + save_data_controller_} { static const FunctionInfo functions[] = { {0, &ISaveDataInfoReader::ReadSaveDataInfo, "ReadSaveDataInfo"}, }; @@ -626,7 +630,7 @@ private: void FindAllSaves(FileSys::SaveDataSpaceId space) { FileSys::VirtualDir save_root{}; - const auto result = fsc.OpenSaveDataSpace(&save_root, space); + const auto result = save_data_controller->OpenSaveDataSpace(&save_root, space); if (result != ResultSuccess || save_root == nullptr) { LOG_ERROR(Service_FS, "The save root for the space_id={:02X} was invalid!", space); @@ -723,7 +727,8 @@ private: }; static_assert(sizeof(SaveDataInfo) == 0x60, "SaveDataInfo has incorrect size."); - FileSystemController& fsc; + ProcessId process_id = 0; + std::shared_ptr save_data_controller; std::vector info; u64 next_entry_index = 0; }; @@ -863,21 +868,20 @@ FSP_SRV::FSP_SRV(Core::System& system_) if (Settings::values.enable_fs_access_log) { access_log_mode = AccessLogMode::SdCard; } - - // This should be true on creation - fsc.SetAutoSaveDataCreation(true); } FSP_SRV::~FSP_SRV() = default; void FSP_SRV::SetCurrentProcess(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - current_process_id = rp.Pop(); + current_process_id = ctx.GetPID(); LOG_DEBUG(Service_FS, "called. current_process_id=0x{:016X}", current_process_id); + const auto res = + fsc.OpenProcess(&program_id, &save_data_controller, &romfs_controller, current_process_id); + IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + rb.Push(res); } void FSP_SRV::OpenFileSystemWithPatch(HLERequestContext& ctx) { @@ -916,7 +920,8 @@ void FSP_SRV::CreateSaveDataFileSystem(HLERequestContext& ctx) { uid[1], uid[0]); FileSys::VirtualDir save_data_dir{}; - fsc.CreateSaveData(&save_data_dir, FileSys::SaveDataSpaceId::NandUser, save_struct); + save_data_controller->CreateSaveData(&save_data_dir, FileSys::SaveDataSpaceId::NandUser, + save_struct); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); @@ -931,7 +936,8 @@ void FSP_SRV::CreateSaveDataFileSystemBySystemSaveDataId(HLERequestContext& ctx) LOG_DEBUG(Service_FS, "called save_struct = {}", save_struct.DebugInfo()); FileSys::VirtualDir save_data_dir{}; - fsc.CreateSaveData(&save_data_dir, FileSys::SaveDataSpaceId::NandSystem, save_struct); + save_data_controller->CreateSaveData(&save_data_dir, FileSys::SaveDataSpaceId::NandSystem, + save_struct); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); @@ -950,7 +956,8 @@ void FSP_SRV::OpenSaveDataFileSystem(HLERequestContext& ctx) { LOG_INFO(Service_FS, "called."); FileSys::VirtualDir dir{}; - auto result = fsc.OpenSaveData(&dir, parameters.space_id, parameters.attribute); + auto result = + save_data_controller->OpenSaveData(&dir, parameters.space_id, parameters.attribute); if (result != ResultSuccess) { IPC::ResponseBuilder rb{ctx, 2, 0, 0}; rb.Push(FileSys::ERROR_ENTITY_NOT_FOUND); @@ -1001,7 +1008,7 @@ void FSP_SRV::OpenSaveDataInfoReaderBySaveDataSpaceId(HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(ResultSuccess); rb.PushIpcInterface( - std::make_shared(system, space, fsc)); + std::make_shared(system, save_data_controller, space)); } void FSP_SRV::OpenSaveDataInfoReaderOnlyCacheStorage(HLERequestContext& ctx) { @@ -1009,8 +1016,8 @@ void FSP_SRV::OpenSaveDataInfoReaderOnlyCacheStorage(HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(ResultSuccess); - rb.PushIpcInterface(system, FileSys::SaveDataSpaceId::TemporaryStorage, - fsc); + rb.PushIpcInterface(system, save_data_controller, + FileSys::SaveDataSpaceId::TemporaryStorage); } void FSP_SRV::WriteSaveDataFileSystemExtraDataBySaveDataAttribute(HLERequestContext& ctx) { @@ -1050,7 +1057,7 @@ void FSP_SRV::OpenDataStorageByCurrentProcess(HLERequestContext& ctx) { LOG_DEBUG(Service_FS, "called"); if (!romfs) { - auto current_romfs = fsc.OpenRomFSCurrentProcess(); + auto current_romfs = romfs_controller->OpenRomFSCurrentProcess(); if (!current_romfs) { // TODO (bunnei): Find the right error code to use here LOG_CRITICAL(Service_FS, "no file system interface available!"); @@ -1078,7 +1085,7 @@ void FSP_SRV::OpenDataStorageByDataId(HLERequestContext& ctx) { LOG_DEBUG(Service_FS, "called with storage_id={:02X}, unknown={:08X}, title_id={:016X}", storage_id, unknown, title_id); - auto data = fsc.OpenRomFS(title_id, storage_id, FileSys::ContentRecordType::Data); + auto data = romfs_controller->OpenRomFS(title_id, storage_id, FileSys::ContentRecordType::Data); if (!data) { const auto archive = FileSys::SystemArchive::SynthesizeSystemArchive(title_id); @@ -1101,7 +1108,8 @@ void FSP_SRV::OpenDataStorageByDataId(HLERequestContext& ctx) { const FileSys::PatchManager pm{title_id, fsc, content_provider}; - auto base = fsc.OpenBaseNca(title_id, storage_id, FileSys::ContentRecordType::Data); + auto base = + romfs_controller->OpenBaseNca(title_id, storage_id, FileSys::ContentRecordType::Data); auto storage = std::make_shared( system, pm.PatchRomFS(base.get(), std::move(data), FileSys::ContentRecordType::Data)); @@ -1129,9 +1137,8 @@ void FSP_SRV::OpenDataStorageWithProgramIndex(HLERequestContext& ctx) { LOG_DEBUG(Service_FS, "called, program_index={}", program_index); - auto patched_romfs = - fsc.OpenPatchedRomFSWithProgramIndex(system.GetApplicationProcessProgramID(), program_index, - FileSys::ContentRecordType::Program); + auto patched_romfs = romfs_controller->OpenPatchedRomFSWithProgramIndex( + program_id, program_index, FileSys::ContentRecordType::Program); if (!patched_romfs) { // TODO: Find the right error code to use here @@ -1152,7 +1159,7 @@ void FSP_SRV::OpenDataStorageWithProgramIndex(HLERequestContext& ctx) { void FSP_SRV::DisableAutoSaveDataCreation(HLERequestContext& ctx) { LOG_DEBUG(Service_FS, "called"); - fsc.SetAutoSaveDataCreation(false); + save_data_controller->SetAutoCreate(false); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); diff --git a/src/core/hle/service/filesystem/fsp_srv.h b/src/core/hle/service/filesystem/fsp_srv.h index 280bc9867..26980af99 100644 --- a/src/core/hle/service/filesystem/fsp_srv.h +++ b/src/core/hle/service/filesystem/fsp_srv.h @@ -17,6 +17,9 @@ class FileSystemBackend; namespace Service::FileSystem { +class RomFsController; +class SaveDataController; + enum class AccessLogVersion : u32 { V7_0_0 = 2, @@ -67,6 +70,9 @@ private: u64 current_process_id = 0; u32 access_log_program_index = 0; AccessLogMode access_log_mode = AccessLogMode::None; + u64 program_id = 0; + std::shared_ptr save_data_controller; + std::shared_ptr romfs_controller; }; } // namespace Service::FileSystem diff --git a/src/core/hle/service/filesystem/romfs_controller.cpp b/src/core/hle/service/filesystem/romfs_controller.cpp new file mode 100644 index 000000000..19c9cec72 --- /dev/null +++ b/src/core/hle/service/filesystem/romfs_controller.cpp @@ -0,0 +1,37 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/service/filesystem/romfs_controller.h" + +namespace Service::FileSystem { + +RomFsController::RomFsController(std::shared_ptr factory_, u64 program_id_) + : factory{std::move(factory_)}, program_id{program_id_} {} +RomFsController::~RomFsController() = default; + +FileSys::VirtualFile RomFsController::OpenRomFSCurrentProcess() { + return factory->OpenCurrentProcess(program_id); +} + +FileSys::VirtualFile RomFsController::OpenPatchedRomFS(u64 title_id, + FileSys::ContentRecordType type) { + return factory->OpenPatchedRomFS(title_id, type); +} + +FileSys::VirtualFile RomFsController::OpenPatchedRomFSWithProgramIndex( + u64 title_id, u8 program_index, FileSys::ContentRecordType type) { + return factory->OpenPatchedRomFSWithProgramIndex(title_id, program_index, type); +} + +FileSys::VirtualFile RomFsController::OpenRomFS(u64 title_id, FileSys::StorageId storage_id, + FileSys::ContentRecordType type) { + return factory->Open(title_id, storage_id, type); +} + +std::shared_ptr RomFsController::OpenBaseNca(u64 title_id, + FileSys::StorageId storage_id, + FileSys::ContentRecordType type) { + return factory->GetEntry(title_id, storage_id, type); +} + +} // namespace Service::FileSystem diff --git a/src/core/hle/service/filesystem/romfs_controller.h b/src/core/hle/service/filesystem/romfs_controller.h new file mode 100644 index 000000000..9a478f71d --- /dev/null +++ b/src/core/hle/service/filesystem/romfs_controller.h @@ -0,0 +1,31 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/file_sys/nca_metadata.h" +#include "core/file_sys/romfs_factory.h" +#include "core/file_sys/vfs_types.h" + +namespace Service::FileSystem { + +class RomFsController { +public: + explicit RomFsController(std::shared_ptr factory_, u64 program_id_); + ~RomFsController(); + + FileSys::VirtualFile OpenRomFSCurrentProcess(); + FileSys::VirtualFile OpenPatchedRomFS(u64 title_id, FileSys::ContentRecordType type); + FileSys::VirtualFile OpenPatchedRomFSWithProgramIndex(u64 title_id, u8 program_index, + FileSys::ContentRecordType type); + FileSys::VirtualFile OpenRomFS(u64 title_id, FileSys::StorageId storage_id, + FileSys::ContentRecordType type); + std::shared_ptr OpenBaseNca(u64 title_id, FileSys::StorageId storage_id, + FileSys::ContentRecordType type); + +private: + const std::shared_ptr factory; + const u64 program_id; +}; + +} // namespace Service::FileSystem diff --git a/src/core/hle/service/filesystem/save_data_controller.cpp b/src/core/hle/service/filesystem/save_data_controller.cpp new file mode 100644 index 000000000..d19b3ea1e --- /dev/null +++ b/src/core/hle/service/filesystem/save_data_controller.cpp @@ -0,0 +1,99 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/core.h" +#include "core/file_sys/control_metadata.h" +#include "core/file_sys/errors.h" +#include "core/file_sys/patch_manager.h" +#include "core/hle/service/filesystem/save_data_controller.h" +#include "core/loader/loader.h" + +namespace Service::FileSystem { + +namespace { + +// A default size for normal/journal save data size if application control metadata cannot be found. +// This should be large enough to satisfy even the most extreme requirements (~4.2GB) +constexpr u64 SufficientSaveDataSize = 0xF0000000; + +FileSys::SaveDataSize GetDefaultSaveDataSize(Core::System& system, u64 program_id) { + const FileSys::PatchManager pm{program_id, system.GetFileSystemController(), + system.GetContentProvider()}; + const auto metadata = pm.GetControlMetadata(); + const auto& nacp = metadata.first; + + if (nacp != nullptr) { + return {nacp->GetDefaultNormalSaveSize(), nacp->GetDefaultJournalSaveSize()}; + } + + return {SufficientSaveDataSize, SufficientSaveDataSize}; +} + +} // namespace + +SaveDataController::SaveDataController(Core::System& system_, + std::shared_ptr factory_) + : system{system_}, factory{std::move(factory_)} {} +SaveDataController::~SaveDataController() = default; + +Result SaveDataController::CreateSaveData(FileSys::VirtualDir* out_save_data, + FileSys::SaveDataSpaceId space, + const FileSys::SaveDataAttribute& attribute) { + LOG_TRACE(Service_FS, "Creating Save Data for space_id={:01X}, save_struct={}", space, + attribute.DebugInfo()); + + auto save_data = factory->Create(space, attribute); + if (save_data == nullptr) { + return FileSys::ERROR_ENTITY_NOT_FOUND; + } + + *out_save_data = save_data; + return ResultSuccess; +} + +Result SaveDataController::OpenSaveData(FileSys::VirtualDir* out_save_data, + FileSys::SaveDataSpaceId space, + const FileSys::SaveDataAttribute& attribute) { + auto save_data = factory->Open(space, attribute); + if (save_data == nullptr) { + return FileSys::ERROR_ENTITY_NOT_FOUND; + } + + *out_save_data = save_data; + return ResultSuccess; +} + +Result SaveDataController::OpenSaveDataSpace(FileSys::VirtualDir* out_save_data_space, + FileSys::SaveDataSpaceId space) { + auto save_data_space = factory->GetSaveDataSpaceDirectory(space); + if (save_data_space == nullptr) { + return FileSys::ERROR_ENTITY_NOT_FOUND; + } + + *out_save_data_space = save_data_space; + return ResultSuccess; +} + +FileSys::SaveDataSize SaveDataController::ReadSaveDataSize(FileSys::SaveDataType type, u64 title_id, + u128 user_id) { + const auto value = factory->ReadSaveDataSize(type, title_id, user_id); + + if (value.normal == 0 && value.journal == 0) { + const auto size = GetDefaultSaveDataSize(system, title_id); + factory->WriteSaveDataSize(type, title_id, user_id, size); + return size; + } + + return value; +} + +void SaveDataController::WriteSaveDataSize(FileSys::SaveDataType type, u64 title_id, u128 user_id, + FileSys::SaveDataSize new_value) { + factory->WriteSaveDataSize(type, title_id, user_id, new_value); +} + +void SaveDataController::SetAutoCreate(bool state) { + factory->SetAutoCreate(state); +} + +} // namespace Service::FileSystem diff --git a/src/core/hle/service/filesystem/save_data_controller.h b/src/core/hle/service/filesystem/save_data_controller.h new file mode 100644 index 000000000..863188e4c --- /dev/null +++ b/src/core/hle/service/filesystem/save_data_controller.h @@ -0,0 +1,35 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/file_sys/nca_metadata.h" +#include "core/file_sys/savedata_factory.h" +#include "core/file_sys/vfs_types.h" + +namespace Service::FileSystem { + +class SaveDataController { +public: + explicit SaveDataController(Core::System& system, + std::shared_ptr factory_); + ~SaveDataController(); + + Result CreateSaveData(FileSys::VirtualDir* out_save_data, FileSys::SaveDataSpaceId space, + const FileSys::SaveDataAttribute& attribute); + Result OpenSaveData(FileSys::VirtualDir* out_save_data, FileSys::SaveDataSpaceId space, + const FileSys::SaveDataAttribute& attribute); + Result OpenSaveDataSpace(FileSys::VirtualDir* out_save_data_space, + FileSys::SaveDataSpaceId space); + + FileSys::SaveDataSize ReadSaveDataSize(FileSys::SaveDataType type, u64 title_id, u128 user_id); + void WriteSaveDataSize(FileSys::SaveDataType type, u64 title_id, u128 user_id, + FileSys::SaveDataSize new_value); + void SetAutoCreate(bool state); + +private: + Core::System& system; + const std::shared_ptr factory; +}; + +} // namespace Service::FileSystem diff --git a/src/core/loader/deconstructed_rom_directory.cpp b/src/core/loader/deconstructed_rom_directory.cpp index c9f8707b7..1e599e78b 100644 --- a/src/core/loader/deconstructed_rom_directory.cpp +++ b/src/core/loader/deconstructed_rom_directory.cpp @@ -216,20 +216,6 @@ AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirect LOG_DEBUG(Loader, "loaded module {} @ {:#X}", module, load_addr); } - // Find the RomFS by searching for a ".romfs" file in this directory - const auto& files = dir->GetFiles(); - const auto romfs_iter = - std::find_if(files.begin(), files.end(), [](const FileSys::VirtualFile& f) { - return f->GetName().find(".romfs") != std::string::npos; - }); - - // Register the RomFS if a ".romfs" file was found - if (romfs_iter != files.end() && *romfs_iter != nullptr) { - romfs = *romfs_iter; - system.GetFileSystemController().RegisterRomFS(std::make_unique( - *this, system.GetContentProvider(), system.GetFileSystemController())); - } - is_loaded = true; return {ResultStatus::Success, LoadParameters{metadata.GetMainThreadPriority(), metadata.GetMainThreadStackSize()}}; diff --git a/src/core/loader/nca.cpp b/src/core/loader/nca.cpp index 814407535..2a32b1276 100644 --- a/src/core/loader/nca.cpp +++ b/src/core/loader/nca.cpp @@ -74,8 +74,10 @@ AppLoader_NCA::LoadResult AppLoader_NCA::Load(Kernel::KProcess& process, Core::S return load_result; } - system.GetFileSystemController().RegisterRomFS(std::make_unique( - *this, system.GetContentProvider(), system.GetFileSystemController())); + system.GetFileSystemController().RegisterProcess( + process.GetProcessId(), nca->GetTitleId(), + std::make_shared(*this, system.GetContentProvider(), + system.GetFileSystemController())); is_loaded = true; return load_result; diff --git a/src/core/loader/nro.cpp b/src/core/loader/nro.cpp index e74697cda..83371fcbd 100644 --- a/src/core/loader/nro.cpp +++ b/src/core/loader/nro.cpp @@ -276,8 +276,10 @@ AppLoader_NRO::LoadResult AppLoader_NRO::Load(Kernel::KProcess& process, Core::S } if (romfs != nullptr) { - system.GetFileSystemController().RegisterRomFS(std::make_unique( - *this, system.GetContentProvider(), system.GetFileSystemController())); + system.GetFileSystemController().RegisterProcess( + process.GetProcessId(), {}, + std::make_unique(*this, system.GetContentProvider(), + system.GetFileSystemController())); } is_loaded = true; diff --git a/src/core/loader/nsp.cpp b/src/core/loader/nsp.cpp index f4ab75b77..28116ff3a 100644 --- a/src/core/loader/nsp.cpp +++ b/src/core/loader/nsp.cpp @@ -111,7 +111,8 @@ AppLoader_NSP::LoadResult AppLoader_NSP::Load(Kernel::KProcess& process, Core::S FileSys::VirtualFile update_raw; if (ReadUpdateRaw(update_raw) == ResultStatus::Success && update_raw != nullptr) { - system.GetFileSystemController().SetPackedUpdate(std::move(update_raw)); + system.GetFileSystemController().SetPackedUpdate(process.GetProcessId(), + std::move(update_raw)); } is_loaded = true; diff --git a/src/core/loader/xci.cpp b/src/core/loader/xci.cpp index 12d72c380..e9abb199a 100644 --- a/src/core/loader/xci.cpp +++ b/src/core/loader/xci.cpp @@ -78,7 +78,8 @@ AppLoader_XCI::LoadResult AppLoader_XCI::Load(Kernel::KProcess& process, Core::S FileSys::VirtualFile update_raw; if (ReadUpdateRaw(update_raw) == ResultStatus::Success && update_raw != nullptr) { - system.GetFileSystemController().SetPackedUpdate(std::move(update_raw)); + system.GetFileSystemController().SetPackedUpdate(process.GetProcessId(), + std::move(update_raw)); } is_loaded = true; diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 4f4c75f5c..29f47bf48 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -2292,14 +2292,14 @@ void GMainWindow::OnGameListOpenFolder(u64 program_id, GameListOpenTarget target ASSERT(user_id); const auto user_save_data_path = FileSys::SaveDataFactory::GetFullPath( - *system, vfs_nand_dir, FileSys::SaveDataSpaceId::NandUser, + {}, vfs_nand_dir, FileSys::SaveDataSpaceId::NandUser, FileSys::SaveDataType::SaveData, program_id, user_id->AsU128(), 0); path = Common::FS::ConcatPathSafe(nand_dir, user_save_data_path); } else { // Device save data const auto device_save_data_path = FileSys::SaveDataFactory::GetFullPath( - *system, vfs_nand_dir, FileSys::SaveDataSpaceId::NandUser, + {}, vfs_nand_dir, FileSys::SaveDataSpaceId::NandUser, FileSys::SaveDataType::SaveData, program_id, {}, 0); path = Common::FS::ConcatPathSafe(nand_dir, device_save_data_path); @@ -2662,8 +2662,8 @@ void GMainWindow::RemoveCacheStorage(u64 program_id) { vfs->OpenDirectory(Common::FS::PathToUTF8String(nand_dir), FileSys::Mode::Read); const auto cache_storage_path = FileSys::SaveDataFactory::GetFullPath( - *system, vfs_nand_dir, FileSys::SaveDataSpaceId::NandUser, - FileSys::SaveDataType::CacheStorage, 0 /* program_id */, {}, 0); + {}, vfs_nand_dir, FileSys::SaveDataSpaceId::NandUser, FileSys::SaveDataType::CacheStorage, + 0 /* program_id */, {}, 0); const auto path = Common::FS::ConcatPathSafe(nand_dir, cache_storage_path); -- cgit v1.2.3 From b5dac5f525e8d5884506ebd98a530e237b518480 Mon Sep 17 00:00:00 2001 From: german77 Date: Sun, 7 Jan 2024 09:05:12 -0600 Subject: service: hid: Create abstracted pad structure --- src/android/app/src/main/jni/native.cpp | 4 +- src/core/frontend/applets/controller.cpp | 2 +- src/core/hle/service/hid/hid_server.cpp | 2 +- src/hid_core/CMakeLists.txt | 34 +++ src/hid_core/frontend/emulated_controller.cpp | 22 +- src/hid_core/hid_types.h | 3 +- src/hid_core/hid_util.h | 2 +- .../abstracted_pad/abstract_battery_handler.cpp | 197 +++++++++++++ .../abstracted_pad/abstract_battery_handler.h | 49 ++++ .../abstracted_pad/abstract_button_handler.cpp | 199 +++++++++++++ .../abstracted_pad/abstract_button_handler.h | 75 +++++ .../abstracted_pad/abstract_ir_sensor_handler.cpp | 126 ++++++++ .../abstracted_pad/abstract_ir_sensor_handler.h | 56 ++++ .../abstracted_pad/abstract_led_handler.cpp | 123 ++++++++ .../abstracted_pad/abstract_led_handler.h | 43 +++ .../abstracted_pad/abstract_mcu_handler.cpp | 108 +++++++ .../abstracted_pad/abstract_mcu_handler.h | 52 ++++ .../abstracted_pad/abstract_nfc_handler.cpp | 140 +++++++++ .../abstracted_pad/abstract_nfc_handler.h | 57 ++++ .../resources/abstracted_pad/abstract_pad.cpp | 294 +++++++++++++++++++ .../resources/abstracted_pad/abstract_pad.h | 123 ++++++++ .../abstracted_pad/abstract_pad_holder.cpp | 99 +++++++ .../resources/abstracted_pad/abstract_pad_holder.h | 47 +++ .../abstracted_pad/abstract_palma_handler.cpp | 47 +++ .../abstracted_pad/abstract_palma_handler.h | 37 +++ .../abstracted_pad/abstract_properties_handler.cpp | 322 +++++++++++++++++++++ .../abstracted_pad/abstract_properties_handler.h | 86 ++++++ .../abstracted_pad/abstract_sixaxis_handler.cpp | 154 ++++++++++ .../abstracted_pad/abstract_sixaxis_handler.h | 61 ++++ .../abstracted_pad/abstract_vibration_handler.cpp | 73 +++++ .../abstracted_pad/abstract_vibration_handler.h | 51 ++++ src/hid_core/resources/npad/npad.cpp | 8 +- src/hid_core/resources/npad/npad_data.cpp | 2 +- src/hid_core/resources/npad/npad_types.h | 99 +++++++ src/hid_core/resources/npad/npad_vibration.cpp | 80 +++++ src/hid_core/resources/npad/npad_vibration.h | 34 +++ src/hid_core/resources/six_axis/six_axis.cpp | 6 +- .../resources/vibration/gc_vibration_device.cpp | 106 +++++++ .../resources/vibration/gc_vibration_device.h | 31 ++ .../resources/vibration/n64_vibration_device.cpp | 80 +++++ .../resources/vibration/n64_vibration_device.h | 29 ++ .../resources/vibration/vibration_base.cpp | 30 ++ src/hid_core/resources/vibration/vibration_base.h | 28 ++ .../resources/vibration/vibration_device.cpp | 84 ++++++ .../resources/vibration/vibration_device.h | 35 +++ src/yuzu/applets/qt_controller.cpp | 8 +- src/yuzu/applets/qt_software_keyboard.cpp | 2 +- src/yuzu/configuration/configure_input_player.cpp | 18 +- .../configure_input_player_widget.cpp | 2 +- src/yuzu/main.cpp | 2 +- src/yuzu/util/controller_navigation.cpp | 4 +- 51 files changed, 3333 insertions(+), 43 deletions(-) create mode 100644 src/hid_core/resources/abstracted_pad/abstract_battery_handler.cpp create mode 100644 src/hid_core/resources/abstracted_pad/abstract_battery_handler.h create mode 100644 src/hid_core/resources/abstracted_pad/abstract_button_handler.cpp create mode 100644 src/hid_core/resources/abstracted_pad/abstract_button_handler.h create mode 100644 src/hid_core/resources/abstracted_pad/abstract_ir_sensor_handler.cpp create mode 100644 src/hid_core/resources/abstracted_pad/abstract_ir_sensor_handler.h create mode 100644 src/hid_core/resources/abstracted_pad/abstract_led_handler.cpp create mode 100644 src/hid_core/resources/abstracted_pad/abstract_led_handler.h create mode 100644 src/hid_core/resources/abstracted_pad/abstract_mcu_handler.cpp create mode 100644 src/hid_core/resources/abstracted_pad/abstract_mcu_handler.h create mode 100644 src/hid_core/resources/abstracted_pad/abstract_nfc_handler.cpp create mode 100644 src/hid_core/resources/abstracted_pad/abstract_nfc_handler.h create mode 100644 src/hid_core/resources/abstracted_pad/abstract_pad.cpp create mode 100644 src/hid_core/resources/abstracted_pad/abstract_pad.h create mode 100644 src/hid_core/resources/abstracted_pad/abstract_pad_holder.cpp create mode 100644 src/hid_core/resources/abstracted_pad/abstract_pad_holder.h create mode 100644 src/hid_core/resources/abstracted_pad/abstract_palma_handler.cpp create mode 100644 src/hid_core/resources/abstracted_pad/abstract_palma_handler.h create mode 100644 src/hid_core/resources/abstracted_pad/abstract_properties_handler.cpp create mode 100644 src/hid_core/resources/abstracted_pad/abstract_properties_handler.h create mode 100644 src/hid_core/resources/abstracted_pad/abstract_sixaxis_handler.cpp create mode 100644 src/hid_core/resources/abstracted_pad/abstract_sixaxis_handler.h create mode 100644 src/hid_core/resources/abstracted_pad/abstract_vibration_handler.cpp create mode 100644 src/hid_core/resources/abstracted_pad/abstract_vibration_handler.h create mode 100644 src/hid_core/resources/npad/npad_vibration.cpp create mode 100644 src/hid_core/resources/npad/npad_vibration.h create mode 100644 src/hid_core/resources/vibration/gc_vibration_device.cpp create mode 100644 src/hid_core/resources/vibration/gc_vibration_device.h create mode 100644 src/hid_core/resources/vibration/n64_vibration_device.cpp create mode 100644 src/hid_core/resources/vibration/n64_vibration_device.h create mode 100644 src/hid_core/resources/vibration/vibration_base.cpp create mode 100644 src/hid_core/resources/vibration/vibration_base.h create mode 100644 src/hid_core/resources/vibration/vibration_device.cpp create mode 100644 src/hid_core/resources/vibration/vibration_device.h (limited to 'src/android') diff --git a/src/android/app/src/main/jni/native.cpp b/src/android/app/src/main/jni/native.cpp index 136c8dee6..e436622e0 100644 --- a/src/android/app/src/main/jni/native.cpp +++ b/src/android/app/src/main/jni/native.cpp @@ -410,8 +410,8 @@ void EmulationSession::OnGamepadConnectEvent([[maybe_unused]] int index) { jauto handheld = m_system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Handheld); if (controller->GetNpadStyleIndex() == Core::HID::NpadStyleIndex::Handheld) { - handheld->SetNpadStyleIndex(Core::HID::NpadStyleIndex::ProController); - controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::ProController); + handheld->SetNpadStyleIndex(Core::HID::NpadStyleIndex::Fullkey); + controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::Fullkey); handheld->Disconnect(); } } diff --git a/src/core/frontend/applets/controller.cpp b/src/core/frontend/applets/controller.cpp index 34fe23b6a..e04d884ba 100644 --- a/src/core/frontend/applets/controller.cpp +++ b/src/core/frontend/applets/controller.cpp @@ -47,7 +47,7 @@ void DefaultControllerApplet::ReconfigureControllers(ReconfigureCallback callbac // Connect controllers based on the following priority list from highest to lowest priority: // Pro Controller -> Dual Joycons -> Left Joycon/Right Joycon -> Handheld if (parameters.allow_pro_controller) { - controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::ProController); + controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::Fullkey); controller->Connect(true); } else if (parameters.allow_dual_joycons) { controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::JoyconDual); diff --git a/src/core/hle/service/hid/hid_server.cpp b/src/core/hle/service/hid/hid_server.cpp index 74898888a..1951da33b 100644 --- a/src/core/hle/service/hid/hid_server.cpp +++ b/src/core/hle/service/hid/hid_server.cpp @@ -1498,7 +1498,7 @@ void IHidServer::GetVibrationDeviceInfo(HLERequestContext& ctx) { bool check_device_index = false; switch (vibration_device_handle.npad_type) { - case Core::HID::NpadStyleIndex::ProController: + case Core::HID::NpadStyleIndex::Fullkey: case Core::HID::NpadStyleIndex::Handheld: case Core::HID::NpadStyleIndex::JoyconDual: case Core::HID::NpadStyleIndex::JoyconLeft: diff --git a/src/hid_core/CMakeLists.txt b/src/hid_core/CMakeLists.txt index cce4e6857..aa85502b5 100644 --- a/src/hid_core/CMakeLists.txt +++ b/src/hid_core/CMakeLists.txt @@ -36,6 +36,30 @@ add_library(hid_core STATIC irsensor/processor_base.h irsensor/tera_plugin_processor.cpp irsensor/tera_plugin_processor.h + resources/abstracted_pad/abstract_battery_handler.cpp + resources/abstracted_pad/abstract_battery_handler.h + resources/abstracted_pad/abstract_button_handler.cpp + resources/abstracted_pad/abstract_button_handler.h + resources/abstracted_pad/abstract_ir_sensor_handler.cpp + resources/abstracted_pad/abstract_ir_sensor_handler.h + resources/abstracted_pad/abstract_led_handler.cpp + resources/abstracted_pad/abstract_led_handler.h + resources/abstracted_pad/abstract_mcu_handler.cpp + resources/abstracted_pad/abstract_mcu_handler.h + resources/abstracted_pad/abstract_nfc_handler.cpp + resources/abstracted_pad/abstract_nfc_handler.h + resources/abstracted_pad/abstract_pad.cpp + resources/abstracted_pad/abstract_pad.h + resources/abstracted_pad/abstract_pad_holder.cpp + resources/abstracted_pad/abstract_pad_holder.h + resources/abstracted_pad/abstract_palma_handler.cpp + resources/abstracted_pad/abstract_palma_handler.h + resources/abstracted_pad/abstract_properties_handler.cpp + resources/abstracted_pad/abstract_properties_handler.h + resources/abstracted_pad/abstract_sixaxis_handler.cpp + resources/abstracted_pad/abstract_sixaxis_handler.h + resources/abstracted_pad/abstract_vibration_handler.cpp + resources/abstracted_pad/abstract_vibration_handler.h resources/debug_pad/debug_pad.cpp resources/debug_pad/debug_pad.h resources/debug_pad/debug_pad_types.h @@ -56,6 +80,8 @@ add_library(hid_core STATIC resources/npad/npad_resource.cpp resources/npad/npad_resource.h resources/npad/npad_types.h + resources/npad/npad_vibration.cpp + resources/npad/npad_vibration.h resources/palma/palma.cpp resources/palma/palma.h resources/six_axis/console_six_axis.cpp @@ -78,6 +104,14 @@ add_library(hid_core STATIC resources/touch_screen/touch_types.h resources/unique_pad/unique_pad.cpp resources/unique_pad/unique_pad.h + resources/vibration/gc_vibration_device.h + resources/vibration/gc_vibration_device.cpp + resources/vibration/n64_vibration_device.h + resources/vibration/n64_vibration_device.cpp + resources/vibration/vibration_base.h + resources/vibration/vibration_base.cpp + resources/vibration/vibration_device.h + resources/vibration/vibration_device.cpp resources/applet_resource.cpp resources/applet_resource.h resources/controller_base.cpp diff --git a/src/hid_core/frontend/emulated_controller.cpp b/src/hid_core/frontend/emulated_controller.cpp index 3d2d1e9f9..a6a96935d 100644 --- a/src/hid_core/frontend/emulated_controller.cpp +++ b/src/hid_core/frontend/emulated_controller.cpp @@ -27,7 +27,7 @@ EmulatedController::~EmulatedController() = default; NpadStyleIndex EmulatedController::MapSettingsTypeToNPad(Settings::ControllerType type) { switch (type) { case Settings::ControllerType::ProController: - return NpadStyleIndex::ProController; + return NpadStyleIndex::Fullkey; case Settings::ControllerType::DualJoyconDetached: return NpadStyleIndex::JoyconDual; case Settings::ControllerType::LeftJoycon: @@ -49,13 +49,13 @@ NpadStyleIndex EmulatedController::MapSettingsTypeToNPad(Settings::ControllerTyp case Settings::ControllerType::SegaGenesis: return NpadStyleIndex::SegaGenesis; default: - return NpadStyleIndex::ProController; + return NpadStyleIndex::Fullkey; } } Settings::ControllerType EmulatedController::MapNPadToSettingsType(NpadStyleIndex type) { switch (type) { - case NpadStyleIndex::ProController: + case NpadStyleIndex::Fullkey: return Settings::ControllerType::ProController; case NpadStyleIndex::JoyconDual: return Settings::ControllerType::DualJoyconDetached; @@ -106,7 +106,7 @@ void EmulatedController::ReloadFromSettings() { SetNpadStyleIndex(MapSettingsTypeToNPad(player.controller_type)); original_npad_type = npad_type; } else { - SetNpadStyleIndex(NpadStyleIndex::ProController); + SetNpadStyleIndex(NpadStyleIndex::Fullkey); original_npad_type = npad_type; } @@ -1073,7 +1073,7 @@ void EmulatedController::SetColors(const Common::Input::CallbackStatus& callback .body = GetNpadColor(controller.color_values[index].body), .button = GetNpadColor(controller.color_values[index].buttons), }; - if (npad_type == NpadStyleIndex::ProController) { + if (npad_type == NpadStyleIndex::Fullkey) { controller.colors_state.left = { .body = GetNpadColor(controller.color_values[index].left_grip), .button = GetNpadColor(controller.color_values[index].buttons), @@ -1356,7 +1356,7 @@ bool EmulatedController::HasNfc() const { switch (npad_type) { case NpadStyleIndex::JoyconRight: case NpadStyleIndex::JoyconDual: - case NpadStyleIndex::ProController: + case NpadStyleIndex::Fullkey: case NpadStyleIndex::Handheld: break; default: @@ -1548,7 +1548,7 @@ void EmulatedController::SetSupportedNpadStyleTag(NpadStyleTag supported_styles) // Fallback Fullkey controllers to Pro controllers if (IsControllerFullkey() && supported_style_tag.fullkey) { LOG_WARNING(Service_HID, "Reconnecting controller type {} as Pro controller", npad_type); - SetNpadStyleIndex(NpadStyleIndex::ProController); + SetNpadStyleIndex(NpadStyleIndex::Fullkey); Connect(); return; } @@ -1556,13 +1556,13 @@ void EmulatedController::SetSupportedNpadStyleTag(NpadStyleTag supported_styles) // Fallback Dual joycon controllers to Pro controllers if (npad_type == NpadStyleIndex::JoyconDual && supported_style_tag.fullkey) { LOG_WARNING(Service_HID, "Reconnecting controller type {} as Pro controller", npad_type); - SetNpadStyleIndex(NpadStyleIndex::ProController); + SetNpadStyleIndex(NpadStyleIndex::Fullkey); Connect(); return; } // Fallback Pro controllers to Dual joycon - if (npad_type == NpadStyleIndex::ProController && supported_style_tag.joycon_dual) { + if (npad_type == NpadStyleIndex::Fullkey && supported_style_tag.joycon_dual) { LOG_WARNING(Service_HID, "Reconnecting controller type {} as Dual Joycons", npad_type); SetNpadStyleIndex(NpadStyleIndex::JoyconDual); Connect(); @@ -1577,7 +1577,7 @@ bool EmulatedController::IsControllerFullkey(bool use_temporary_value) const { std::scoped_lock lock{mutex}; const auto type = is_configuring && use_temporary_value ? tmp_npad_type : npad_type; switch (type) { - case NpadStyleIndex::ProController: + case NpadStyleIndex::Fullkey: case NpadStyleIndex::GameCube: case NpadStyleIndex::NES: case NpadStyleIndex::SNES: @@ -1593,7 +1593,7 @@ bool EmulatedController::IsControllerSupported(bool use_temporary_value) const { std::scoped_lock lock{mutex}; const auto type = is_configuring && use_temporary_value ? tmp_npad_type : npad_type; switch (type) { - case NpadStyleIndex::ProController: + case NpadStyleIndex::Fullkey: return supported_style_tag.fullkey.As(); case NpadStyleIndex::Handheld: return supported_style_tag.handheld.As(); diff --git a/src/hid_core/hid_types.h b/src/hid_core/hid_types.h index a81ed6af0..2c3f02f34 100644 --- a/src/hid_core/hid_types.h +++ b/src/hid_core/hid_types.h @@ -220,6 +220,7 @@ enum class NpadIdType : u32 { }; enum class NpadInterfaceType : u8 { + None = 0, Bluetooth = 1, Rail = 2, Usb = 3, @@ -229,7 +230,7 @@ enum class NpadInterfaceType : u8 { // This is nn::hid::NpadStyleIndex enum class NpadStyleIndex : u8 { None = 0, - ProController = 3, + Fullkey = 3, Handheld = 4, HandheldNES = 4, JoyconDual = 5, diff --git a/src/hid_core/hid_util.h b/src/hid_core/hid_util.h index 94ff2d23a..397a87472 100644 --- a/src/hid_core/hid_util.h +++ b/src/hid_core/hid_util.h @@ -42,7 +42,7 @@ constexpr Result IsSixaxisHandleValid(const Core::HID::SixAxisSensorHandle& hand constexpr Result IsVibrationHandleValid(const Core::HID::VibrationDeviceHandle& handle) { switch (handle.npad_type) { - case Core::HID::NpadStyleIndex::ProController: + case Core::HID::NpadStyleIndex::Fullkey: case Core::HID::NpadStyleIndex::Handheld: case Core::HID::NpadStyleIndex::JoyconDual: case Core::HID::NpadStyleIndex::JoyconLeft: diff --git a/src/hid_core/resources/abstracted_pad/abstract_battery_handler.cpp b/src/hid_core/resources/abstracted_pad/abstract_battery_handler.cpp new file mode 100644 index 000000000..62fbbb0a7 --- /dev/null +++ b/src/hid_core/resources/abstracted_pad/abstract_battery_handler.cpp @@ -0,0 +1,197 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "core/core_timing.h" +#include "hid_core/hid_result.h" +#include "hid_core/hid_util.h" +#include "hid_core/resources/abstracted_pad/abstract_battery_handler.h" +#include "hid_core/resources/abstracted_pad/abstract_pad_holder.h" +#include "hid_core/resources/abstracted_pad/abstract_properties_handler.h" +#include "hid_core/resources/applet_resource.h" +#include "hid_core/resources/npad/npad_types.h" +#include "hid_core/resources/shared_memory_format.h" + +namespace Service::HID { + +NpadAbstractBatteryHandler::NpadAbstractBatteryHandler() {} + +NpadAbstractBatteryHandler::~NpadAbstractBatteryHandler() = default; + +void NpadAbstractBatteryHandler::SetAbstractPadHolder(NpadAbstractedPadHolder* holder) { + abstract_pad_holder = holder; +} + +void NpadAbstractBatteryHandler::SetAppletResource(AppletResourceHolder* applet_resource) { + applet_resource_holder = applet_resource; +} + +void NpadAbstractBatteryHandler::SetPropertiesHandler(NpadAbstractPropertiesHandler* handler) { + properties_handler = handler; +} + +Result NpadAbstractBatteryHandler::IncrementRefCounter() { + if (ref_counter == std::numeric_limits::max() - 1) { + return ResultNpadHandlerOverflow; + } + ref_counter++; + return ResultSuccess; +} + +Result NpadAbstractBatteryHandler::DecrementRefCounter() { + if (ref_counter == 0) { + return ResultNpadHandlerNotInitialized; + } + ref_counter--; + return ResultSuccess; +} + +Result NpadAbstractBatteryHandler::UpdateBatteryState(u64 aruid) { + const auto npad_index = NpadIdTypeToIndex(properties_handler->GetNpadId()); + AruidData* aruid_data = applet_resource_holder->applet_resource->GetAruidData(aruid); + if (aruid_data == nullptr) { + return ResultSuccess; + } + + auto& npad_internal_state = + aruid_data->shared_memory_format->npad.npad_entry[npad_index].internal_state; + auto& system_properties = npad_internal_state.system_properties; + + system_properties.is_charging_joy_dual.Assign(dual_battery.is_charging); + system_properties.is_powered_joy_dual.Assign(dual_battery.is_powered); + system_properties.is_charging_joy_left.Assign(left_battery.is_charging); + system_properties.is_powered_joy_left.Assign(left_battery.is_powered); + system_properties.is_charging_joy_right.Assign(right_battery.is_charging); + system_properties.is_powered_joy_right.Assign(right_battery.is_powered); + + npad_internal_state.battery_level_dual = dual_battery.battery_level; + npad_internal_state.battery_level_left = left_battery.battery_level; + npad_internal_state.battery_level_right = right_battery.battery_level; + + return ResultSuccess; +} + +void NpadAbstractBatteryHandler::UpdateBatteryState() { + if (ref_counter == 0) { + return; + } + has_new_battery_data = GetNewBatteryState(); +} + +bool NpadAbstractBatteryHandler::GetNewBatteryState() { + bool has_changed = false; + Core::HID::NpadPowerInfo new_dual_battery_state{}; + Core::HID::NpadPowerInfo new_left_battery_state{}; + Core::HID::NpadPowerInfo new_right_battery_state{}; + std::array abstract_pads{}; + const std::size_t count = abstract_pad_holder->GetAbstractedPads(abstract_pads); + + for (std::size_t i = 0; i < count; i++) { + auto* abstract_pad = abstract_pads[i]; + if (!abstract_pad->internal_flags.is_connected) { + continue; + } + const auto power_info = abstract_pad->power_info; + if (power_info.battery_level > Core::HID::NpadBatteryLevel::Full) { + // Abort + continue; + } + + const auto style = abstract_pad->assignment_style; + + if (style.is_external_assigned || style.is_handheld_assigned) { + new_dual_battery_state = power_info; + } + if (style.is_external_left_assigned || style.is_handheld_left_assigned) { + new_left_battery_state = power_info; + } + if (style.is_external_right_assigned || style.is_handheld_right_assigned) { + new_right_battery_state = power_info; + } + + if (abstract_pad->internal_flags.is_battery_low_ovln_required) { + if (abstract_pad->interface_type == Core::HID::NpadInterfaceType::Rail) { + // TODO + } + abstract_pad->internal_flags.is_battery_low_ovln_required.Assign(false); + } + } + + if (dual_battery.battery_level != new_dual_battery_state.battery_level || + dual_battery.is_charging != new_dual_battery_state.is_charging || + dual_battery.is_powered != new_dual_battery_state.is_powered) { + has_changed = true; + dual_battery = new_dual_battery_state; + } + + if (left_battery.battery_level != new_left_battery_state.battery_level || + left_battery.is_charging != new_left_battery_state.is_charging || + left_battery.is_powered != new_left_battery_state.is_powered) { + has_changed = true; + left_battery = new_left_battery_state; + } + + if (right_battery.battery_level != new_right_battery_state.battery_level || + right_battery.is_charging != new_right_battery_state.is_charging || + right_battery.is_powered != new_right_battery_state.is_powered) { + has_changed = true; + right_battery = new_right_battery_state; + } + + return has_changed; +} + +void NpadAbstractBatteryHandler::UpdateCoreBatteryState() { + if (ref_counter == 0) { + return; + } + if (!has_new_battery_data) { + return; + } + + UpdateBatteryState(0); +} + +void NpadAbstractBatteryHandler::InitializeBatteryState(u64 aruid) { + UpdateBatteryState(aruid); +} + +bool NpadAbstractBatteryHandler::HasBattery() const { + std::array abstract_pads{}; + const std::size_t count = abstract_pad_holder->GetAbstractedPads(abstract_pads); + + for (std::size_t i = 0; i < count; i++) { + const auto* abstract_pad = abstract_pads[i]; + if (!abstract_pad->internal_flags.is_connected) { + continue; + } + return abstract_pad->disabled_feature_set.has_fullkey_battery || + abstract_pad->disabled_feature_set.has_left_right_joy_battery; + } + + return false; +} + +void NpadAbstractBatteryHandler::HasLeftRightBattery(bool& has_left, bool& has_right) const { + std::array abstract_pads{}; + const std::size_t count = abstract_pad_holder->GetAbstractedPads(abstract_pads); + + has_left = false; + has_right = false; + + for (std::size_t i = 0; i < count; i++) { + const auto* abstract_pad = abstract_pads[i]; + if (!abstract_pad->internal_flags.is_connected) { + continue; + } + if (!abstract_pad->disabled_feature_set.has_fullkey_battery && + !abstract_pad->disabled_feature_set.has_left_right_joy_battery) { + continue; + } + has_left = abstract_pad->assignment_style.is_external_left_assigned || + abstract_pad->assignment_style.is_handheld_left_assigned; + has_right = abstract_pad->assignment_style.is_external_right_assigned || + abstract_pad->assignment_style.is_handheld_right_assigned; + } +} + +} // namespace Service::HID diff --git a/src/hid_core/resources/abstracted_pad/abstract_battery_handler.h b/src/hid_core/resources/abstracted_pad/abstract_battery_handler.h new file mode 100644 index 000000000..85ac5eb72 --- /dev/null +++ b/src/hid_core/resources/abstracted_pad/abstract_battery_handler.h @@ -0,0 +1,49 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include "common/common_types.h" +#include "core/hle/result.h" +#include "hid_core/hid_types.h" + +namespace Service::HID { +struct AppletResourceHolder; +class NpadAbstractedPadHolder; +class NpadAbstractPropertiesHandler; + +/// Handles Npad request from HID interfaces +class NpadAbstractBatteryHandler final { +public: + explicit NpadAbstractBatteryHandler(); + ~NpadAbstractBatteryHandler(); + + void SetAbstractPadHolder(NpadAbstractedPadHolder* holder); + void SetAppletResource(AppletResourceHolder* applet_resource); + void SetPropertiesHandler(NpadAbstractPropertiesHandler* handler); + + Result IncrementRefCounter(); + Result DecrementRefCounter(); + + Result UpdateBatteryState(u64 aruid); + void UpdateBatteryState(); + bool GetNewBatteryState(); + void UpdateCoreBatteryState(); + void InitializeBatteryState(u64 aruid); + + bool HasBattery() const; + void HasLeftRightBattery(bool& has_left, bool& has_right) const; + +private: + AppletResourceHolder* applet_resource_holder{nullptr}; + NpadAbstractedPadHolder* abstract_pad_holder{nullptr}; + NpadAbstractPropertiesHandler* properties_handler{nullptr}; + + s32 ref_counter{}; + Core::HID::NpadPowerInfo dual_battery{}; + Core::HID::NpadPowerInfo left_battery{}; + Core::HID::NpadPowerInfo right_battery{}; + bool has_new_battery_data{}; +}; + +} // namespace Service::HID diff --git a/src/hid_core/resources/abstracted_pad/abstract_button_handler.cpp b/src/hid_core/resources/abstracted_pad/abstract_button_handler.cpp new file mode 100644 index 000000000..587169433 --- /dev/null +++ b/src/hid_core/resources/abstracted_pad/abstract_button_handler.cpp @@ -0,0 +1,199 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "hid_core/hid_result.h" +#include "hid_core/hid_util.h" +#include "hid_core/resources/abstracted_pad/abstract_button_handler.h" +#include "hid_core/resources/abstracted_pad/abstract_pad_holder.h" +#include "hid_core/resources/abstracted_pad/abstract_properties_handler.h" +#include "hid_core/resources/applet_resource.h" +#include "hid_core/resources/npad/npad_resource.h" +#include "hid_core/resources/npad/npad_types.h" +#include "hid_core/resources/shared_memory_format.h" + +namespace Service::HID { + +NpadAbstractButtonHandler::NpadAbstractButtonHandler() {} + +NpadAbstractButtonHandler::~NpadAbstractButtonHandler() = default; + +void NpadAbstractButtonHandler::SetAbstractPadHolder(NpadAbstractedPadHolder* holder) { + abstract_pad_holder = holder; +} + +void NpadAbstractButtonHandler::SetAppletResource(AppletResourceHolder* applet_resource) { + applet_resource_holder = applet_resource; +} + +void NpadAbstractButtonHandler::SetPropertiesHandler(NpadAbstractPropertiesHandler* handler) { + properties_handler = handler; +} + +Result NpadAbstractButtonHandler::IncrementRefCounter() { + if (ref_counter == std::numeric_limits::max() - 1) { + return ResultNpadHandlerOverflow; + } + ref_counter++; + return ResultSuccess; +} + +Result NpadAbstractButtonHandler::DecrementRefCounter() { + if (ref_counter == 0) { + return ResultNpadHandlerNotInitialized; + } + ref_counter--; + return ResultSuccess; +} + +Result NpadAbstractButtonHandler::UpdateAllButtonWithHomeProtection(u64 aruid) { + const Core::HID::NpadIdType npad_id = properties_handler->GetNpadId(); + auto* data = applet_resource_holder->applet_resource->GetAruidData(aruid); + + if (data == nullptr) { + return ResultSuccess; + } + + auto& npad_entry = data->shared_memory_format->npad.npad_entry[NpadIdTypeToIndex(npad_id)]; + UpdateButtonLifo(npad_entry, aruid); + + bool is_home_button_protection_enabled{}; + const auto result = applet_resource_holder->shared_npad_resource->GetHomeProtectionEnabled( + is_home_button_protection_enabled, aruid, npad_id); + + if (result.IsError()) { + return ResultSuccess; + } + + npad_entry.internal_state.button_properties.is_home_button_protection_enabled.Assign( + is_home_button_protection_enabled); + + return ResultSuccess; +} + +void NpadAbstractButtonHandler::UpdateAllButtonLifo() { + Core::HID::NpadIdType npad_id = properties_handler->GetNpadId(); + for (std::size_t i = 0; i < AruidIndexMax; i++) { + auto* data = applet_resource_holder->applet_resource->GetAruidDataByIndex(i); + auto& npad_entry = data->shared_memory_format->npad.npad_entry[NpadIdTypeToIndex(npad_id)]; + UpdateButtonLifo(npad_entry, data->aruid); + } +} + +void NpadAbstractButtonHandler::UpdateCoreBatteryState() { + Core::HID::NpadIdType npad_id = properties_handler->GetNpadId(); + for (std::size_t i = 0; i < AruidIndexMax; i++) { + auto* data = applet_resource_holder->applet_resource->GetAruidDataByIndex(i); + auto& npad_entry = data->shared_memory_format->npad.npad_entry[NpadIdTypeToIndex(npad_id)]; + UpdateButtonLifo(npad_entry, data->aruid); + } +} + +void NpadAbstractButtonHandler::UpdateButtonState(u64 aruid) { + Core::HID::NpadIdType npad_id = properties_handler->GetNpadId(); + auto* data = applet_resource_holder->applet_resource->GetAruidData(aruid); + if (data == nullptr) { + return; + } + auto& npad_entry = data->shared_memory_format->npad.npad_entry[NpadIdTypeToIndex(npad_id)]; + UpdateButtonLifo(npad_entry, aruid); +} + +Result NpadAbstractButtonHandler::SetHomeProtection(bool is_enabled, u64 aruid) { + const Core::HID::NpadIdType npad_id = properties_handler->GetNpadId(); + auto result = applet_resource_holder->shared_npad_resource->SetHomeProtectionEnabled( + aruid, npad_id, is_enabled); + if (result.IsError()) { + return result; + } + + auto* data = applet_resource_holder->applet_resource->GetAruidData(aruid); + if (data == nullptr) { + return ResultSuccess; + } + + bool is_home_protection_enabled{}; + result = applet_resource_holder->shared_npad_resource->GetHomeProtectionEnabled( + is_home_protection_enabled, aruid, npad_id); + if (result.IsError()) { + return ResultSuccess; + } + + auto& npad_entry = data->shared_memory_format->npad.npad_entry[NpadIdTypeToIndex(npad_id)]; + npad_entry.internal_state.button_properties.is_home_button_protection_enabled.Assign( + is_home_protection_enabled); + return ResultSuccess; +} + +bool NpadAbstractButtonHandler::IsButtonPressedOnConsoleMode() { + return is_button_pressed_on_console_mode; +} + +void NpadAbstractButtonHandler::EnableCenterClamp() { + std::array abstract_pads{}; + const std::size_t count = abstract_pad_holder->GetAbstractedPads(abstract_pads); + + for (std::size_t i = 0; i < count; i++) { + auto* abstract_pad = abstract_pads[i]; + if (!abstract_pad->internal_flags.is_connected) { + continue; + } + abstract_pad->internal_flags.use_center_clamp.Assign(true); + } +} + +void NpadAbstractButtonHandler::UpdateButtonLifo(NpadSharedMemoryEntry& shared_memory, u64 aruid) { + auto* npad_resource = applet_resource_holder->shared_npad_resource; + Core::HID::NpadStyleTag style_tag = {properties_handler->GetStyleSet(aruid)}; + style_tag.system_ext.Assign(npad_resource->GetActiveData()->GetNpadSystemExtState()); + + UpdateNpadFullkeyLifo(style_tag, 0, aruid, shared_memory); + UpdateHandheldLifo(style_tag, 1, aruid, shared_memory); + UpdateJoyconDualLifo(style_tag, 2, aruid, shared_memory); + UpdateJoyconLeftLifo(style_tag, 3, aruid, shared_memory); + UpdateJoyconRightLifo(style_tag, 4, aruid, shared_memory); + UpdatePalmaLifo(style_tag, 5, aruid, shared_memory); + UpdateSystemExtLifo(style_tag, 6, aruid, shared_memory); +} + +void NpadAbstractButtonHandler::UpdateNpadFullkeyLifo(Core::HID::NpadStyleTag style_tag, + int style_index, u64 aruid, + NpadSharedMemoryEntry& shared_memory) { + // TODO +} + +void NpadAbstractButtonHandler::UpdateHandheldLifo(Core::HID::NpadStyleTag style_tag, + int style_index, u64 aruid, + NpadSharedMemoryEntry& shared_memory) { + // TODO +} + +void NpadAbstractButtonHandler::UpdateJoyconDualLifo(Core::HID::NpadStyleTag style_tag, + int style_index, u64 aruid, + NpadSharedMemoryEntry& shared_memory) { + // TODO +} + +void NpadAbstractButtonHandler::UpdateJoyconLeftLifo(Core::HID::NpadStyleTag style_tag, + int style_index, u64 aruid, + NpadSharedMemoryEntry& shared_memory) { + // TODO +} + +void NpadAbstractButtonHandler::UpdateJoyconRightLifo(Core::HID::NpadStyleTag style_tag, + int style_index, u64 aruid, + NpadSharedMemoryEntry& shared_memory) { + // TODO +} + +void NpadAbstractButtonHandler::UpdateSystemExtLifo(Core::HID::NpadStyleTag style_tag, + int style_index, u64 aruid, + NpadSharedMemoryEntry& shared_memory) { + // TODO +} + +void NpadAbstractButtonHandler::UpdatePalmaLifo(Core::HID::NpadStyleTag style_tag, int style_index, + u64 aruid, NpadSharedMemoryEntry& shared_memory) { + // TODO +} + +} // namespace Service::HID diff --git a/src/hid_core/resources/abstracted_pad/abstract_button_handler.h b/src/hid_core/resources/abstracted_pad/abstract_button_handler.h new file mode 100644 index 000000000..01eafe96d --- /dev/null +++ b/src/hid_core/resources/abstracted_pad/abstract_button_handler.h @@ -0,0 +1,75 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include "common/common_types.h" +#include "core/hle/result.h" +#include "hid_core/hid_types.h" + +namespace Service::HID { +struct NpadSharedMemoryEntry; + +struct AppletResourceHolder; +class NpadAbstractedPadHolder; +class NpadAbstractPropertiesHandler; + +/// Handles Npad request from HID interfaces +class NpadAbstractButtonHandler final { +public: + explicit NpadAbstractButtonHandler(); + ~NpadAbstractButtonHandler(); + + void SetAbstractPadHolder(NpadAbstractedPadHolder* holder); + void SetAppletResource(AppletResourceHolder* applet_resource); + void SetPropertiesHandler(NpadAbstractPropertiesHandler* handler); + + Result IncrementRefCounter(); + Result DecrementRefCounter(); + + Result UpdateAllButtonWithHomeProtection(u64 aruid); + + void UpdateAllButtonLifo(); + void UpdateCoreBatteryState(); + void UpdateButtonState(u64 aruid); + + Result SetHomeProtection(bool is_enabled, u64 aruid); + bool IsButtonPressedOnConsoleMode(); + void EnableCenterClamp(); + + void UpdateButtonLifo(NpadSharedMemoryEntry& shared_memory, u64 aruid); + + void UpdateNpadFullkeyLifo(Core::HID::NpadStyleTag style_tag, int index, u64 aruid, + NpadSharedMemoryEntry& shared_memory); + void UpdateHandheldLifo(Core::HID::NpadStyleTag style_tag, int index, u64 aruid, + NpadSharedMemoryEntry& shared_memory); + void UpdateJoyconDualLifo(Core::HID::NpadStyleTag style_tag, int index, u64 aruid, + NpadSharedMemoryEntry& shared_memory); + void UpdateJoyconLeftLifo(Core::HID::NpadStyleTag style_tag, int index, u64 aruid, + NpadSharedMemoryEntry& shared_memory); + void UpdateJoyconRightLifo(Core::HID::NpadStyleTag style_tag, int index, u64 aruid, + NpadSharedMemoryEntry& shared_memory); + void UpdateSystemExtLifo(Core::HID::NpadStyleTag style_tag, int index, u64 aruid, + NpadSharedMemoryEntry& shared_memory); + void UpdatePalmaLifo(Core::HID::NpadStyleTag style_tag, int index, u64 aruid, + NpadSharedMemoryEntry& shared_memory); + +private: + struct GcTrigger { + float left; + float right; + }; + + AppletResourceHolder* applet_resource_holder{nullptr}; + NpadAbstractedPadHolder* abstract_pad_holder{nullptr}; + NpadAbstractPropertiesHandler* properties_handler{nullptr}; + + s32 ref_counter{}; + + bool is_button_pressed_on_console_mode{}; + + u64 gc_sampling_number{}; + GcTrigger gc_trigger_state{}; +}; + +} // namespace Service::HID diff --git a/src/hid_core/resources/abstracted_pad/abstract_ir_sensor_handler.cpp b/src/hid_core/resources/abstracted_pad/abstract_ir_sensor_handler.cpp new file mode 100644 index 000000000..d4e4181bf --- /dev/null +++ b/src/hid_core/resources/abstracted_pad/abstract_ir_sensor_handler.cpp @@ -0,0 +1,126 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "core/hle/kernel/k_event.h" +#include "core/hle/kernel/k_readable_event.h" +#include "hid_core/hid_result.h" +#include "hid_core/resources/abstracted_pad/abstract_ir_sensor_handler.h" +#include "hid_core/resources/abstracted_pad/abstract_pad_holder.h" +#include "hid_core/resources/abstracted_pad/abstract_properties_handler.h" +#include "hid_core/resources/npad/npad_types.h" + +namespace Service::HID { + +NpadAbstractIrSensorHandler::NpadAbstractIrSensorHandler() {} + +NpadAbstractIrSensorHandler::~NpadAbstractIrSensorHandler() = default; + +void NpadAbstractIrSensorHandler::SetAbstractPadHolder(NpadAbstractedPadHolder* holder) { + abstract_pad_holder = holder; +} + +void NpadAbstractIrSensorHandler::SetPropertiesHandler(NpadAbstractPropertiesHandler* handler) { + properties_handler = handler; +} + +Result NpadAbstractIrSensorHandler::IncrementRefCounter() { + if (ref_counter == std::numeric_limits::max() - 1) { + return ResultNpadHandlerOverflow; + } + ref_counter++; + return ResultSuccess; +} + +Result NpadAbstractIrSensorHandler::DecrementRefCounter() { + if (ref_counter == 0) { + return ResultNpadHandlerNotInitialized; + } + ref_counter--; + return ResultSuccess; +} + +void NpadAbstractIrSensorHandler::UpdateIrSensorState() { + const auto previous_state = sensor_state; + std::array abstract_pads{}; + const std::size_t count = abstract_pad_holder->GetAbstractedPads(abstract_pads); + + if (count == 0) { + sensor_state = NpadIrSensorState::Disabled; + if (sensor_state == previous_state) { + return; + } + ir_sensor_event->Signal(); + return; + } + + bool is_found{}; + for (std::size_t i = 0; i < count; i++) { + auto* abstract_pad = abstract_pads[i]; + if (!abstract_pad->internal_flags.is_connected) { + continue; + } + if (!abstract_pad->disabled_feature_set.has_bluetooth_address) { + continue; + } + is_found = true; + xcd_handle = abstract_pad->xcd_handle; + } + + if (is_found) { + if (sensor_state == NpadIrSensorState::Active) { + return; + } + sensor_state = NpadIrSensorState::Available; + if (sensor_state == previous_state) { + return; + } + ir_sensor_event->Signal(); + return; + } + + sensor_state = NpadIrSensorState::Unavailable; + if (sensor_state == previous_state) { + return; + } + + ir_sensor_event->Signal(); + return; +} + +Result NpadAbstractIrSensorHandler::ActivateIrSensor(bool is_enabled) { + if (sensor_state == NpadIrSensorState::Unavailable) { + return ResultIrSensorIsNotReady; + } + if (is_enabled && sensor_state == NpadIrSensorState::Available) { + sensor_state = NpadIrSensorState::Active; + } else { + if (is_enabled) { + return ResultSuccess; + } + if (sensor_state != NpadIrSensorState::Active) { + return ResultSuccess; + } + sensor_state = NpadIrSensorState::Available; + } + ir_sensor_event->Signal(); + return ResultSuccess; +} + +Result NpadAbstractIrSensorHandler::GetIrSensorEventHandle(Kernel::KReadableEvent** out_event) { + *out_event = &ir_sensor_event->GetReadableEvent(); + return ResultSuccess; +} + +Result NpadAbstractIrSensorHandler::GetXcdHandleForNpadWithIrSensor(u64& handle) const { + if (sensor_state < NpadIrSensorState::Available) { + return ResultIrSensorIsNotReady; + } + handle = xcd_handle; + return ResultSuccess; +} + +NpadIrSensorState NpadAbstractIrSensorHandler::GetSensorState() const { + return sensor_state; +} + +} // namespace Service::HID diff --git a/src/hid_core/resources/abstracted_pad/abstract_ir_sensor_handler.h b/src/hid_core/resources/abstracted_pad/abstract_ir_sensor_handler.h new file mode 100644 index 000000000..fe8e005af --- /dev/null +++ b/src/hid_core/resources/abstracted_pad/abstract_ir_sensor_handler.h @@ -0,0 +1,56 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include "common/common_types.h" +#include "core/hle/result.h" +#include "hid_core/hid_types.h" + +namespace Kernel { +class KEvent; +class KReadableEvent; +} // namespace Kernel + +enum class NpadIrSensorState : u32 { + Disabled, + Unavailable, + Available, + Active, +}; + +namespace Service::HID { +class NpadAbstractedPadHolder; +class NpadAbstractPropertiesHandler; + +/// Handles Npad request from HID interfaces +class NpadAbstractIrSensorHandler final { +public: + explicit NpadAbstractIrSensorHandler(); + ~NpadAbstractIrSensorHandler(); + + void SetAbstractPadHolder(NpadAbstractedPadHolder* holder); + void SetPropertiesHandler(NpadAbstractPropertiesHandler* handler); + + Result IncrementRefCounter(); + Result DecrementRefCounter(); + + void UpdateIrSensorState(); + Result ActivateIrSensor(bool param_2); + + Result GetIrSensorEventHandle(Kernel::KReadableEvent** out_event); + + Result GetXcdHandleForNpadWithIrSensor(u64& handle) const; + + NpadIrSensorState GetSensorState() const; + +private: + NpadAbstractedPadHolder* abstract_pad_holder{nullptr}; + NpadAbstractPropertiesHandler* properties_handler{nullptr}; + + s32 ref_counter{}; + Kernel::KEvent* ir_sensor_event{nullptr}; + u64 xcd_handle{}; + NpadIrSensorState sensor_state{}; +}; +} // namespace Service::HID diff --git a/src/hid_core/resources/abstracted_pad/abstract_led_handler.cpp b/src/hid_core/resources/abstracted_pad/abstract_led_handler.cpp new file mode 100644 index 000000000..0b2bfe88d --- /dev/null +++ b/src/hid_core/resources/abstracted_pad/abstract_led_handler.cpp @@ -0,0 +1,123 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "core/core_timing.h" +#include "hid_core/hid_result.h" +#include "hid_core/hid_util.h" +#include "hid_core/resources/abstracted_pad/abstract_led_handler.h" +#include "hid_core/resources/abstracted_pad/abstract_pad_holder.h" +#include "hid_core/resources/abstracted_pad/abstract_properties_handler.h" +#include "hid_core/resources/applet_resource.h" +#include "hid_core/resources/npad/npad_types.h" + +namespace Service::HID { + +NpadAbstractLedHandler::NpadAbstractLedHandler() {} + +NpadAbstractLedHandler::~NpadAbstractLedHandler() = default; + +void NpadAbstractLedHandler::SetAbstractPadHolder(NpadAbstractedPadHolder* holder) { + abstract_pad_holder = holder; +} + +void NpadAbstractLedHandler::SetAppletResource(AppletResourceHolder* applet_resource) { + applet_resource_holder = applet_resource; +} + +void NpadAbstractLedHandler::SetPropertiesHandler(NpadAbstractPropertiesHandler* handler) { + properties_handler = handler; +} + +Result NpadAbstractLedHandler::IncrementRefCounter() { + if (ref_counter == std::numeric_limits::max() - 1) { + return ResultNpadHandlerOverflow; + } + ref_counter++; + return ResultSuccess; +} + +Result NpadAbstractLedHandler::DecrementRefCounter() { + if (ref_counter == 0) { + return ResultNpadHandlerNotInitialized; + } + ref_counter--; + return ResultSuccess; +} + +void NpadAbstractLedHandler::SetNpadLedHandlerLedPattern() { + const auto npad_id = properties_handler->GetNpadId(); + + switch (npad_id) { + case Core::HID::NpadIdType::Player1: + left_pattern = Core::HID::LedPattern{1, 0, 0, 0}; + break; + case Core::HID::NpadIdType::Player2: + left_pattern = Core::HID::LedPattern{1, 1, 0, 0}; + break; + case Core::HID::NpadIdType::Player3: + left_pattern = Core::HID::LedPattern{1, 1, 1, 0}; + break; + case Core::HID::NpadIdType::Player4: + left_pattern = Core::HID::LedPattern{1, 1, 1, 1}; + break; + case Core::HID::NpadIdType::Player5: + left_pattern = Core::HID::LedPattern{1, 0, 0, 1}; + break; + case Core::HID::NpadIdType::Player6: + left_pattern = Core::HID::LedPattern{1, 0, 1, 0}; + break; + case Core::HID::NpadIdType::Player7: + left_pattern = Core::HID::LedPattern{1, 0, 1, 1}; + break; + case Core::HID::NpadIdType::Player8: + left_pattern = Core::HID::LedPattern{0, 1, 1, 0}; + break; + case Core::HID::NpadIdType::Other: + case Core::HID::NpadIdType::Handheld: + left_pattern = Core::HID::LedPattern{0, 0, 0, 0}; + break; + default: + ASSERT_MSG(false, "Invalid npad id type"); + break; + } + + switch (npad_id) { + case Core::HID::NpadIdType::Player1: + right_pattern = Core::HID::LedPattern{0, 0, 0, 1}; + break; + case Core::HID::NpadIdType::Player2: + right_pattern = Core::HID::LedPattern{0, 1, 1, 1}; + break; + case Core::HID::NpadIdType::Player3: + right_pattern = Core::HID::LedPattern{0, 1, 1, 1}; + break; + case Core::HID::NpadIdType::Player4: + right_pattern = Core::HID::LedPattern{1, 1, 1, 1}; + break; + case Core::HID::NpadIdType::Player5: + right_pattern = Core::HID::LedPattern{1, 0, 0, 1}; + break; + case Core::HID::NpadIdType::Player6: + right_pattern = Core::HID::LedPattern{0, 1, 0, 1}; + break; + case Core::HID::NpadIdType::Player7: + right_pattern = Core::HID::LedPattern{1, 1, 0, 1}; + break; + case Core::HID::NpadIdType::Player8: + right_pattern = Core::HID::LedPattern{0, 1, 1, 0}; + break; + case Core::HID::NpadIdType::Other: + case Core::HID::NpadIdType::Handheld: + right_pattern = Core::HID::LedPattern{0, 0, 0, 0}; + break; + default: + ASSERT_MSG(false, "Invalid npad id type"); + break; + } +} + +void NpadAbstractLedHandler::SetLedBlinkingDevice(Core::HID::LedPattern pattern) { + led_blinking = pattern; +} + +} // namespace Service::HID diff --git a/src/hid_core/resources/abstracted_pad/abstract_led_handler.h b/src/hid_core/resources/abstracted_pad/abstract_led_handler.h new file mode 100644 index 000000000..09528129b --- /dev/null +++ b/src/hid_core/resources/abstracted_pad/abstract_led_handler.h @@ -0,0 +1,43 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include "common/common_types.h" +#include "core/hle/result.h" +#include "hid_core/hid_types.h" + +namespace Service::HID { +struct AppletResourceHolder; +class NpadAbstractedPadHolder; +class NpadAbstractPropertiesHandler; + +/// Handles Npad request from HID interfaces +class NpadAbstractLedHandler final { +public: + explicit NpadAbstractLedHandler(); + ~NpadAbstractLedHandler(); + + void SetAbstractPadHolder(NpadAbstractedPadHolder* holder); + void SetAppletResource(AppletResourceHolder* applet_resource); + void SetPropertiesHandler(NpadAbstractPropertiesHandler* handler); + + Result IncrementRefCounter(); + Result DecrementRefCounter(); + + void SetNpadLedHandlerLedPattern(); + + void SetLedBlinkingDevice(Core::HID::LedPattern pattern); + +private: + AppletResourceHolder* applet_resource_holder{nullptr}; + NpadAbstractedPadHolder* abstract_pad_holder{nullptr}; + NpadAbstractPropertiesHandler* properties_handler{nullptr}; + + s32 ref_counter{}; + Core::HID::LedPattern led_blinking{0, 0, 0, 0}; + Core::HID::LedPattern left_pattern{0, 0, 0, 0}; + Core::HID::LedPattern right_pattern{0, 0, 0, 0}; + u64 led_interval{}; +}; +} // namespace Service::HID diff --git a/src/hid_core/resources/abstracted_pad/abstract_mcu_handler.cpp b/src/hid_core/resources/abstracted_pad/abstract_mcu_handler.cpp new file mode 100644 index 000000000..6f35bd95c --- /dev/null +++ b/src/hid_core/resources/abstracted_pad/abstract_mcu_handler.cpp @@ -0,0 +1,108 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "hid_core/hid_result.h" +#include "hid_core/resources/abstracted_pad/abstract_mcu_handler.h" +#include "hid_core/resources/abstracted_pad/abstract_pad_holder.h" +#include "hid_core/resources/abstracted_pad/abstract_properties_handler.h" +#include "hid_core/resources/npad/npad_types.h" + +namespace Service::HID { + +NpadAbstractMcuHandler::NpadAbstractMcuHandler() {} + +NpadAbstractMcuHandler::~NpadAbstractMcuHandler() = default; + +void NpadAbstractMcuHandler::SetAbstractPadHolder(NpadAbstractedPadHolder* holder) { + abstract_pad_holder = holder; +} + +void NpadAbstractMcuHandler::SetPropertiesHandler(NpadAbstractPropertiesHandler* handler) { + properties_handler = handler; +} + +Result NpadAbstractMcuHandler::IncrementRefCounter() { + if (ref_counter == std::numeric_limits::max() - 1) { + return ResultNpadHandlerOverflow; + } + ref_counter++; + return ResultSuccess; +} + +Result NpadAbstractMcuHandler::DecrementRefCounter() { + if (ref_counter == 0) { + return ResultNpadHandlerNotInitialized; + } + ref_counter--; + return ResultSuccess; +} + +void NpadAbstractMcuHandler::UpdateMcuState() { + std::array abstract_pads{}; + const std::size_t count = properties_handler->GetAbstractedPads(abstract_pads); + + if (count == 0) { + mcu_holder = {}; + return; + } + + for (std::size_t i = 0; i < count; i++) { + auto* abstract_pad = abstract_pads[i]; + if (!abstract_pad->internal_flags.is_connected) { + continue; + } + if (!abstract_pad->disabled_feature_set.has_left_joy_rail_bus) { + if (!abstract_pad->disabled_feature_set.has_left_joy_six_axis_sensor && + !abstract_pad->disabled_feature_set.has_right_joy_six_axis_sensor) { + continue; + } + if (mcu_holder[1].state != NpadMcuState::Active) { + mcu_holder[1].state = NpadMcuState::Available; + } + mcu_holder[1].abstracted_pad = abstract_pad; + continue; + } + if (mcu_holder[0].state != NpadMcuState::Active) { + mcu_holder[0].state = NpadMcuState::Available; + } + mcu_holder[0].abstracted_pad = abstract_pad; + } +} + +Result NpadAbstractMcuHandler::GetAbstractedPad(IAbstractedPad** data, u32 mcu_index) { + if (mcu_holder[mcu_index].state == NpadMcuState::None || + mcu_holder[mcu_index].abstracted_pad == nullptr) { + return ResultMcuIsNotReady; + } + *data = mcu_holder[mcu_index].abstracted_pad; + return ResultSuccess; +} + +NpadMcuState NpadAbstractMcuHandler::GetMcuState(u32 mcu_index) { + return mcu_holder[mcu_index].state; +} + +Result NpadAbstractMcuHandler::SetMcuState(bool is_enabled, u32 mcu_index) { + NpadMcuState& state = mcu_holder[mcu_index].state; + + if (state == NpadMcuState::None) { + return ResultMcuIsNotReady; + } + + if ((is_enabled) && (state == NpadMcuState::Available)) { + state = NpadMcuState::Active; + return ResultSuccess; + } + + if (is_enabled) { + return ResultSuccess; + } + if (state != NpadMcuState::Active) { + return ResultSuccess; + } + + state = NpadMcuState::Available; + return ResultSuccess; +} + +} // namespace Service::HID diff --git a/src/hid_core/resources/abstracted_pad/abstract_mcu_handler.h b/src/hid_core/resources/abstracted_pad/abstract_mcu_handler.h new file mode 100644 index 000000000..9902dd03a --- /dev/null +++ b/src/hid_core/resources/abstracted_pad/abstract_mcu_handler.h @@ -0,0 +1,52 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include "common/common_types.h" +#include "core/hle/result.h" +#include "hid_core/hid_types.h" + +namespace Service::HID { +struct IAbstractedPad; +class NpadAbstractedPadHolder; +class NpadAbstractPropertiesHandler; + +enum class NpadMcuState : u32 { + None, + Available, + Active, +}; + +struct NpadMcuHolder { + NpadMcuState state; + INSERT_PADDING_BYTES(0x4); + IAbstractedPad* abstracted_pad; +}; +static_assert(sizeof(NpadMcuHolder) == 0x10, "NpadMcuHolder is an invalid size"); + +/// Handles Npad request from HID interfaces +class NpadAbstractMcuHandler final { +public: + explicit NpadAbstractMcuHandler(); + ~NpadAbstractMcuHandler(); + + void SetAbstractPadHolder(NpadAbstractedPadHolder* holder); + void SetPropertiesHandler(NpadAbstractPropertiesHandler* handler); + + Result IncrementRefCounter(); + Result DecrementRefCounter(); + + void UpdateMcuState(); + Result GetAbstractedPad(IAbstractedPad** data, u32 mcu_index); + NpadMcuState GetMcuState(u32 mcu_index); + Result SetMcuState(bool is_enabled, u32 mcu_index); + +private: + NpadAbstractedPadHolder* abstract_pad_holder{nullptr}; + NpadAbstractPropertiesHandler* properties_handler{nullptr}; + + s32 ref_counter{}; + std::array mcu_holder{}; +}; +} // namespace Service::HID diff --git a/src/hid_core/resources/abstracted_pad/abstract_nfc_handler.cpp b/src/hid_core/resources/abstracted_pad/abstract_nfc_handler.cpp new file mode 100644 index 000000000..bd9b79333 --- /dev/null +++ b/src/hid_core/resources/abstracted_pad/abstract_nfc_handler.cpp @@ -0,0 +1,140 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "core/hle/kernel/k_event.h" +#include "core/hle/kernel/k_readable_event.h" +#include "hid_core/hid_result.h" +#include "hid_core/resources/abstracted_pad/abstract_nfc_handler.h" +#include "hid_core/resources/abstracted_pad/abstract_pad_holder.h" +#include "hid_core/resources/abstracted_pad/abstract_properties_handler.h" +#include "hid_core/resources/npad/npad_types.h" + +namespace Service::HID { + +NpadAbstractNfcHandler::NpadAbstractNfcHandler() {} + +NpadAbstractNfcHandler::~NpadAbstractNfcHandler() = default; + +void NpadAbstractNfcHandler::SetAbstractPadHolder(NpadAbstractedPadHolder* holder) { + abstract_pad_holder = holder; +} + +void NpadAbstractNfcHandler::SetPropertiesHandler(NpadAbstractPropertiesHandler* handler) { + properties_handler = handler; +} + +Result NpadAbstractNfcHandler::IncrementRefCounter() { + if (ref_counter == std::numeric_limits::max() - 1) { + return ResultNpadHandlerOverflow; + } + ref_counter++; + return ResultSuccess; +} + +Result NpadAbstractNfcHandler::DecrementRefCounter() { + if (ref_counter == 0) { + return ResultNpadHandlerNotInitialized; + } + ref_counter--; + return ResultSuccess; +} + +void NpadAbstractNfcHandler::UpdateNfcState() { + std::array abstract_pads{}; + const std::size_t count = properties_handler->GetAbstractedPads(abstract_pads); + + if (count == 0) { + if (sensor_state == NpadNfcState::Active) { + nfc_activate_event->Signal(); + } + if (sensor_state == NpadNfcState::Unavailable) { + return; + } + sensor_state = NpadNfcState::Unavailable; + input_event->Signal(); + return; + } + + bool is_found{}; + for (std::size_t i = 0; i < count; i++) { + auto* abstract_pad = abstract_pads[i]; + if (!abstract_pad->internal_flags.is_connected) { + continue; + } + if (!abstract_pad->disabled_feature_set.has_nfc) { + continue; + } + is_found = true; + xcd_handle = 0; + } + + if (is_found) { + if (sensor_state == NpadNfcState::Active) { + return; + } + if (sensor_state == NpadNfcState::Available) { + return; + } + sensor_state = NpadNfcState::Available; + input_event->Signal(); + return; + } + + if (sensor_state == NpadNfcState::Active) { + nfc_activate_event->Signal(); + } + if (sensor_state == NpadNfcState::Unavailable) { + return; + } + sensor_state = NpadNfcState::Unavailable; + input_event->Signal(); + return; +} + +bool NpadAbstractNfcHandler::HasNfcSensor() { + return sensor_state != NpadNfcState::Unavailable; +} + +bool NpadAbstractNfcHandler::IsNfcActivated() { + return sensor_state == NpadNfcState::Active; +} + +Result NpadAbstractNfcHandler::GetAcquireNfcActivateEventHandle( + Kernel::KReadableEvent** out_event) { + *out_event = &nfc_activate_event->GetReadableEvent(); + return ResultSuccess; +} + +void NpadAbstractNfcHandler::SetInputEvent(Kernel::KEvent* event) { + input_event = event; +} + +Result NpadAbstractNfcHandler::ActivateNfc(bool is_enabled) { + if (sensor_state == NpadNfcState::Active) { + return ResultNfcIsNotReady; + } + + NpadNfcState new_state = NpadNfcState::Available; + if (is_enabled) { + new_state = NpadNfcState::Active; + } + if (sensor_state != new_state) { + sensor_state = new_state; + nfc_activate_event->Signal(); + } + return ResultSuccess; +} + +Result NpadAbstractNfcHandler::GetXcdHandleWithNfc(u64& out_xcd_handle) const { + if (sensor_state == NpadNfcState::Unavailable) { + return ResultNfcIsNotReady; + } + if (xcd_handle == 0) { + return ResultNfcXcdHandleIsNotInitialized; + } + + out_xcd_handle = xcd_handle; + return ResultSuccess; +} + +} // namespace Service::HID diff --git a/src/hid_core/resources/abstracted_pad/abstract_nfc_handler.h b/src/hid_core/resources/abstracted_pad/abstract_nfc_handler.h new file mode 100644 index 000000000..0702722a6 --- /dev/null +++ b/src/hid_core/resources/abstracted_pad/abstract_nfc_handler.h @@ -0,0 +1,57 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include "common/common_types.h" +#include "core/hle/result.h" +#include "hid_core/hid_types.h" + +namespace Kernel { +class KReadableEvent; +} + +enum class NpadNfcState : u32 { + Unavailable, + Available, + Active, +}; + +namespace Service::HID { +class NpadAbstractedPadHolder; +class NpadAbstractPropertiesHandler; + +/// Handles Npad request from HID interfaces +class NpadAbstractNfcHandler final { +public: + explicit NpadAbstractNfcHandler(); + ~NpadAbstractNfcHandler(); + + void SetAbstractPadHolder(NpadAbstractedPadHolder* holder); + void SetPropertiesHandler(NpadAbstractPropertiesHandler* handler); + + Result IncrementRefCounter(); + Result DecrementRefCounter(); + + void UpdateNfcState(); + bool HasNfcSensor(); + bool IsNfcActivated(); + + Result GetAcquireNfcActivateEventHandle(Kernel::KReadableEvent** out_event); + void SetInputEvent(Kernel::KEvent* event); + + Result ActivateNfc(bool is_enabled); + + Result GetXcdHandleWithNfc(u64& out_xcd_handle) const; + +private: + NpadAbstractedPadHolder* abstract_pad_holder{nullptr}; + NpadAbstractPropertiesHandler* properties_handler{nullptr}; + + s32 ref_counter{}; + Kernel::KEvent* nfc_activate_event{nullptr}; + Kernel::KEvent* input_event{nullptr}; + u64 xcd_handle{}; + NpadNfcState sensor_state{NpadNfcState::Unavailable}; +}; +} // namespace Service::HID diff --git a/src/hid_core/resources/abstracted_pad/abstract_pad.cpp b/src/hid_core/resources/abstracted_pad/abstract_pad.cpp new file mode 100644 index 000000000..2c7691d7c --- /dev/null +++ b/src/hid_core/resources/abstracted_pad/abstract_pad.cpp @@ -0,0 +1,294 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "hid_core/hid_result.h" +#include "hid_core/resources/abstracted_pad/abstract_pad.h" +#include "hid_core/resources/applet_resource.h" +#include "hid_core/resources/npad/npad_types.h" + +namespace Service::HID { + +AbstractPad::AbstractPad() {} + +AbstractPad::~AbstractPad() = default; + +void AbstractPad::SetExternals(AppletResourceHolder* applet_resource, + CaptureButtonResource* capture_button_resource, + HomeButtonResource* home_button_resource, + SixAxisResource* sixaxis_resource, PalmaResource* palma_resource, + VibrationHandler* vibration) { + applet_resource_holder = applet_resource; + + properties_handler.SetAppletResource(applet_resource_holder); + properties_handler.SetAbstractPadHolder(&abstract_pad_holder); + + led_handler.SetAppletResource(applet_resource_holder); + led_handler.SetAbstractPadHolder(&abstract_pad_holder); + led_handler.SetPropertiesHandler(&properties_handler); + + ir_sensor_handler.SetAbstractPadHolder(&abstract_pad_holder); + ir_sensor_handler.SetPropertiesHandler(&properties_handler); + + nfc_handler.SetAbstractPadHolder(&abstract_pad_holder); + nfc_handler.SetPropertiesHandler(&properties_handler); + + mcu_handler.SetAbstractPadHolder(&abstract_pad_holder); + mcu_handler.SetPropertiesHandler(&properties_handler); + + std::array vibration_devices{&vibration_left, &vibration_right}; + vibration_handler.SetAppletResource(applet_resource_holder); + vibration_handler.SetAbstractPadHolder(&abstract_pad_holder); + vibration_handler.SetPropertiesHandler(&properties_handler); + vibration_handler.SetN64Vibration(&vibration_n64); + vibration_handler.SetVibration(vibration_devices); + vibration_handler.SetGcVibration(&vibration_gc); + + sixaxis_handler.SetAppletResource(applet_resource_holder); + sixaxis_handler.SetAbstractPadHolder(&abstract_pad_holder); + sixaxis_handler.SetPropertiesHandler(&properties_handler); + sixaxis_handler.SetSixaxisResource(sixaxis_resource); + + button_handler.SetAppletResource(applet_resource_holder); + button_handler.SetAbstractPadHolder(&abstract_pad_holder); + button_handler.SetPropertiesHandler(&properties_handler); + + battery_handler.SetAppletResource(applet_resource_holder); + battery_handler.SetAbstractPadHolder(&abstract_pad_holder); + battery_handler.SetPropertiesHandler(&properties_handler); + + palma_handler.SetAbstractPadHolder(&abstract_pad_holder); + palma_handler.SetPropertiesHandler(&properties_handler); + palma_handler.SetPalmaResource(palma_resource); +} + +void AbstractPad::SetNpadId(Core::HID::NpadIdType npad_id) { + properties_handler.SetNpadId(npad_id); +} + +Result AbstractPad::Activate() { + if (ref_counter == std::numeric_limits::max() - 1) { + return ResultNpadHandlerOverflow; + } + + if (ref_counter != 0) { + ref_counter++; + return ResultSuccess; + } + + std::size_t stage = 0; + Result result = ResultSuccess; + + if (result.IsSuccess()) { + stage++; + result = properties_handler.IncrementRefCounter(); + } + if (result.IsSuccess()) { + stage++; + result = led_handler.IncrementRefCounter(); + } + if (result.IsSuccess()) { + stage++; + result = ir_sensor_handler.IncrementRefCounter(); + } + if (result.IsSuccess()) { + stage++; + result = mcu_handler.IncrementRefCounter(); + } + if (result.IsSuccess()) { + stage++; + result = nfc_handler.IncrementRefCounter(); + } + if (result.IsSuccess()) { + stage++; + result = vibration_handler.IncrementRefCounter(); + } + if (result.IsSuccess()) { + stage++; + result = sixaxis_handler.IncrementRefCounter(); + } + if (result.IsSuccess()) { + stage++; + result = button_handler.IncrementRefCounter(); + } + if (result.IsSuccess()) { + stage++; + result = battery_handler.IncrementRefCounter(); + } + if (result.IsSuccess()) { + stage++; + result = palma_handler.IncrementRefCounter(); + } + + if (result.IsSuccess()) { + ref_counter++; + return result; + } + + if (stage > 9) { + battery_handler.DecrementRefCounter(); + } + if (stage > 8) { + button_handler.DecrementRefCounter(); + } + if (stage > 7) { + sixaxis_handler.DecrementRefCounter(); + } + if (stage > 6) { + vibration_handler.DecrementRefCounter(); + } + if (stage > 5) { + nfc_handler.DecrementRefCounter(); + } + if (stage > 4) { + mcu_handler.DecrementRefCounter(); + } + if (stage > 3) { + ir_sensor_handler.DecrementRefCounter(); + } + if (stage > 2) { + led_handler.DecrementRefCounter(); + } + if (stage > 1) { + properties_handler.DecrementRefCounter(); + } + return result; +} + +Result AbstractPad::Deactivate() { + if (ref_counter == 0) { + return ResultNpadResourceNotInitialized; + } + + ref_counter--; + battery_handler.DecrementRefCounter(); + button_handler.DecrementRefCounter(); + sixaxis_handler.DecrementRefCounter(); + vibration_handler.DecrementRefCounter(); + nfc_handler.DecrementRefCounter(); + ir_sensor_handler.DecrementRefCounter(); + mcu_handler.DecrementRefCounter(); + led_handler.DecrementRefCounter(); + properties_handler.DecrementRefCounter(); + palma_handler.DecrementRefCounter(); + + return ResultSuccess; +} + +Result AbstractPad::ActivateNpad(u64 aruid) { + Result result = ResultSuccess; + if (result.IsSuccess()) { + result = properties_handler.ActivateNpadUnknown0x88(aruid); + } + if (result.IsSuccess()) { + result = sixaxis_handler.UpdateSixAxisState2(aruid); + } + if (result.IsSuccess()) { + result = battery_handler.UpdateBatteryState(aruid); + } + return result; +} + +NpadAbstractedPadHolder* AbstractPad::GetAbstractedPadHolder() { + return &abstract_pad_holder; +} + +NpadAbstractPropertiesHandler* AbstractPad::GetAbstractPropertiesHandler() { + return &properties_handler; +} + +NpadAbstractLedHandler* AbstractPad::GetAbstractLedHandler() { + return &led_handler; +} + +NpadAbstractIrSensorHandler* AbstractPad::GetAbstractIrSensorHandler() { + return &ir_sensor_handler; +} + +NpadAbstractMcuHandler* AbstractPad::GetAbstractMcuHandler() { + return &mcu_handler; +} + +NpadAbstractNfcHandler* AbstractPad::GetAbstractNfcHandler() { + return &nfc_handler; +} + +NpadAbstractVibrationHandler* AbstractPad::GetAbstractVibrationHandler() { + return &vibration_handler; +} + +NpadAbstractSixAxisHandler* AbstractPad::GetAbstractSixAxisHandler() { + return &sixaxis_handler; +} + +NpadAbstractButtonHandler* AbstractPad::GetAbstractButtonHandler() { + return &button_handler; +} + +NpadAbstractBatteryHandler* AbstractPad::GetAbstractBatteryHandler() { + return &battery_handler; +} + +NpadN64VibrationDevice* AbstractPad::GetN64VibrationDevice() { + return &vibration_n64; +} + +NpadVibrationDevice* AbstractPad::GetVibrationDevice(Core::HID::DeviceIndex device_index) { + if (device_index == Core::HID::DeviceIndex::Right) { + return &vibration_right; + } + return &vibration_left; +} + +void AbstractPad::GetLeftRightVibrationDevice(std::vector list) { + list.emplace_back(&vibration_left); + list.emplace_back(&vibration_right); +} + +NpadGcVibrationDevice* AbstractPad::GetGCVibrationDevice() { + return &vibration_gc; +} + +Core::HID::NpadIdType AbstractPad::GetLastActiveNpad() { + return properties_handler.GetNpadId(); +} + +void AbstractPad::UpdateInterfaceType() { + if (interface_type != properties_handler.GetInterfaceType()) { + Update(); + } + battery_handler.UpdateBatteryState(); +} + +void AbstractPad::Update() { + properties_handler.UpdateDeviceType(); + led_handler.SetNpadLedHandlerLedPattern(); + vibration_handler.UpdateVibrationState(); + sixaxis_handler.UpdateSixAxisState(); + nfc_handler.UpdateNfcState(); + ir_sensor_handler.UpdateIrSensorState(); + mcu_handler.UpdateMcuState(); + palma_handler.UpdatePalmaState(); + battery_handler.UpdateBatteryState(); + button_handler.EnableCenterClamp(); + + interface_type = properties_handler.GetInterfaceType(); + + std::scoped_lock lock{*applet_resource_holder->shared_mutex}; + properties_handler.UpdateAllDeviceProperties(); + battery_handler.UpdateCoreBatteryState(); + button_handler.UpdateCoreBatteryState(); +} + +void AbstractPad::UpdatePadState() { + button_handler.UpdateAllButtonLifo(); + sixaxis_handler.UpdateSixAxisState(); + battery_handler.UpdateCoreBatteryState(); +} + +void AbstractPad::EnableAppletToGetInput(u64 aruid) { + button_handler.UpdateButtonState(aruid); + sixaxis_handler.UpdateSixAxisState(aruid); + battery_handler.UpdateBatteryState(aruid); +} + +} // namespace Service::HID diff --git a/src/hid_core/resources/abstracted_pad/abstract_pad.h b/src/hid_core/resources/abstracted_pad/abstract_pad.h new file mode 100644 index 000000000..cbdf84af7 --- /dev/null +++ b/src/hid_core/resources/abstracted_pad/abstract_pad.h @@ -0,0 +1,123 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include +#include + +#include "common/common_types.h" +#include "core/hle/result.h" +#include "hid_core/hid_types.h" +#include "hid_core/resources/applet_resource.h" +#include "hid_core/resources/npad/npad_types.h" + +#include "hid_core/resources/abstracted_pad/abstract_battery_handler.h" +#include "hid_core/resources/abstracted_pad/abstract_button_handler.h" +#include "hid_core/resources/abstracted_pad/abstract_ir_sensor_handler.h" +#include "hid_core/resources/abstracted_pad/abstract_led_handler.h" +#include "hid_core/resources/abstracted_pad/abstract_mcu_handler.h" +#include "hid_core/resources/abstracted_pad/abstract_nfc_handler.h" +#include "hid_core/resources/abstracted_pad/abstract_pad_holder.h" +#include "hid_core/resources/abstracted_pad/abstract_palma_handler.h" +#include "hid_core/resources/abstracted_pad/abstract_properties_handler.h" +#include "hid_core/resources/abstracted_pad/abstract_sixaxis_handler.h" +#include "hid_core/resources/abstracted_pad/abstract_vibration_handler.h" +#include "hid_core/resources/vibration/gc_vibration_device.h" +#include "hid_core/resources/vibration/n64_vibration_device.h" +#include "hid_core/resources/vibration/vibration_device.h" + +namespace Service::HID { +class AppletResource; +class SixAxisResource; +class PalmaResource; +class NPadResource; +class AbstractPad; +class NpadLastActiveHandler; +class NpadIrNfcHandler; +class UniquePads; +class NpadPalmaHandler; +class FirmwareResource; +class NpadVibration; +class NpadHighestBattery; +class NpadGcVibration; + +class CaptureButtonResource; +class HomeButtonResource; +class VibrationHandler; + +struct HandheldConfig; + +/// Handles Npad request from HID interfaces +class AbstractPad final { +public: + explicit AbstractPad(); + ~AbstractPad(); + + void SetExternals(AppletResourceHolder* applet_resource, + CaptureButtonResource* capture_button_resource, + HomeButtonResource* home_button_resource, SixAxisResource* sixaxis_resource, + PalmaResource* palma_resource, VibrationHandler* vibration); + void SetNpadId(Core::HID::NpadIdType npad_id); + + Result Activate(); + Result Deactivate(); + + Result ActivateNpad(u64 aruid); + + NpadAbstractedPadHolder* GetAbstractedPadHolder(); + NpadAbstractPropertiesHandler* GetAbstractPropertiesHandler(); + NpadAbstractLedHandler* GetAbstractLedHandler(); + NpadAbstractIrSensorHandler* GetAbstractIrSensorHandler(); + NpadAbstractMcuHandler* GetAbstractMcuHandler(); + NpadAbstractNfcHandler* GetAbstractNfcHandler(); + NpadAbstractVibrationHandler* GetAbstractVibrationHandler(); + NpadAbstractSixAxisHandler* GetAbstractSixAxisHandler(); + NpadAbstractButtonHandler* GetAbstractButtonHandler(); + NpadAbstractBatteryHandler* GetAbstractBatteryHandler(); + + NpadN64VibrationDevice* GetN64VibrationDevice(); + NpadVibrationDevice* GetVibrationDevice(Core::HID::DeviceIndex device_index); + void GetLeftRightVibrationDevice(std::vector list); + NpadGcVibrationDevice* GetGCVibrationDevice(); + + Core::HID::NpadIdType GetLastActiveNpad(); + void UpdateInterfaceType(); + void Update(); + + void UpdatePadState(); + void EnableAppletToGetInput(u64 aruid); + +private: + AppletResourceHolder* applet_resource_holder{nullptr}; + NpadAbstractedPadHolder abstract_pad_holder{}; + NpadAbstractPropertiesHandler properties_handler{}; + NpadAbstractLedHandler led_handler{}; + NpadAbstractIrSensorHandler ir_sensor_handler{}; + NpadAbstractNfcHandler nfc_handler{}; + NpadAbstractMcuHandler mcu_handler{}; + NpadAbstractVibrationHandler vibration_handler{}; + NpadAbstractSixAxisHandler sixaxis_handler{}; + NpadAbstractButtonHandler button_handler{}; + NpadAbstractBatteryHandler battery_handler{}; + NpadAbstractPalmaHandler palma_handler{}; + + NpadN64VibrationDevice vibration_n64{}; + NpadVibrationDevice vibration_left{}; + NpadVibrationDevice vibration_right{}; + NpadGcVibrationDevice vibration_gc{}; + + // SixAxisConfigHolder fullkey_config; + // SixAxisConfigHolder handheld_config; + // SixAxisConfigHolder dual_left_config; + // SixAxisConfigHolder dual_right_config; + // SixAxisConfigHolder left_config; + // SixAxisConfigHolder right_config; + + s32 ref_counter{}; + Core::HID::NpadInterfaceType interface_type{Core::HID::NpadInterfaceType::None}; +}; + +using FullAbstractPad = std::array; + +} // namespace Service::HID diff --git a/src/hid_core/resources/abstracted_pad/abstract_pad_holder.cpp b/src/hid_core/resources/abstracted_pad/abstract_pad_holder.cpp new file mode 100644 index 000000000..8334dc34f --- /dev/null +++ b/src/hid_core/resources/abstracted_pad/abstract_pad_holder.cpp @@ -0,0 +1,99 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "hid_core/hid_result.h" +#include "hid_core/resources/abstracted_pad/abstract_pad_holder.h" +#include "hid_core/resources/npad/npad_types.h" + +namespace Service::HID { + +Result NpadAbstractedPadHolder::RegisterAbstractPad(IAbstractedPad* abstracted_pad) { + if (list_size >= assignment_list.size()) { + return ResultNpadIsNotProController; + } + + for (std::size_t i = 0; i < list_size; i++) { + if (assignment_list[i].device_type == abstracted_pad->device_type) { + return ResultNpadIsNotProController; + } + } + + assignment_list[list_size] = { + .abstracted_pad = abstracted_pad, + .device_type = abstracted_pad->device_type, + .interface_type = abstracted_pad->interface_type, + .controller_id = abstracted_pad->controller_id, + }; + + list_size++; + return ResultSuccess; +} + +void NpadAbstractedPadHolder::RemoveAbstractPadByControllerId(u64 controller_id) { + if (list_size == 0) { + return; + } + if (controller_id == 0) { + return; + } + for (std::size_t i = 0; i < list_size; i++) { + if (assignment_list[i].controller_id != controller_id) { + continue; + } + for (std::size_t e = i + 1; e < list_size; e++) { + assignment_list[e - 1] = assignment_list[e]; + } + list_size--; + return; + } +} + +void NpadAbstractedPadHolder::DetachAbstractedPad() { + while (list_size > 0) { + for (std::size_t i = 1; i < list_size; i++) { + assignment_list[i - 1] = assignment_list[i]; + } + list_size--; + } +} + +u64 NpadAbstractedPadHolder::RemoveAbstractPadByAssignmentStyle( + Service::HID::AssignmentStyle assignment_style) { + for (std::size_t i = 0; i < list_size; i++) { + if ((assignment_style.raw & assignment_list[i].abstracted_pad->assignment_style.raw) == 0) { + continue; + } + for (std::size_t e = i + 1; e < list_size; e++) { + assignment_list[e - 1] = assignment_list[e]; + } + list_size--; + return list_size; + } + return list_size; +} + +u32 NpadAbstractedPadHolder::GetAbstractedPads(std::span list) const { + u32 num_elements = std::min(static_cast(list.size()), list_size); + for (std::size_t i = 0; i < num_elements; i++) { + list[i] = assignment_list[i].abstracted_pad; + } + return num_elements; +} + +void NpadAbstractedPadHolder::SetAssignmentMode(const NpadJoyAssignmentMode& mode) { + assignment_mode = mode; +} + +NpadJoyAssignmentMode NpadAbstractedPadHolder::GetAssignmentMode() const { + return assignment_mode; +} + +std::size_t NpadAbstractedPadHolder::GetStyleIndexList( + std::span list) const { + for (std::size_t i = 0; i < list_size; i++) { + list[i] = assignment_list[i].device_type; + } + return list_size; +} + +} // namespace Service::HID diff --git a/src/hid_core/resources/abstracted_pad/abstract_pad_holder.h b/src/hid_core/resources/abstracted_pad/abstract_pad_holder.h new file mode 100644 index 000000000..fb7f472e8 --- /dev/null +++ b/src/hid_core/resources/abstracted_pad/abstract_pad_holder.h @@ -0,0 +1,47 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include +#include +#include + +#include "common/common_types.h" +#include "core/hle/result.h" +#include "hid_core/hid_types.h" +#include "hid_core/resources/npad/npad_types.h" + +namespace Service::HID { +struct IAbstractedPad; + +struct AbstractAssignmentHolder { + IAbstractedPad* abstracted_pad; + Core::HID::NpadStyleIndex device_type; + Core::HID::NpadInterfaceType interface_type; + INSERT_PADDING_BYTES(0x6); + u64 controller_id; +}; +static_assert(sizeof(AbstractAssignmentHolder) == 0x18, + "AbstractAssignmentHolder is an invalid size"); + +/// This is nn::hid::server::NpadAbstractedPadHolder +class NpadAbstractedPadHolder final { +public: + Result RegisterAbstractPad(IAbstractedPad* abstracted_pad); + void RemoveAbstractPadByControllerId(u64 controller_id); + void DetachAbstractedPad(); + u64 RemoveAbstractPadByAssignmentStyle(Service::HID::AssignmentStyle assignment_style); + u32 GetAbstractedPads(std::span list) const; + + void SetAssignmentMode(const NpadJoyAssignmentMode& mode); + NpadJoyAssignmentMode GetAssignmentMode() const; + + std::size_t GetStyleIndexList(std::span list) const; + +private: + std::array assignment_list{}; + u32 list_size{}; + NpadJoyAssignmentMode assignment_mode{NpadJoyAssignmentMode::Dual}; +}; +} // namespace Service::HID diff --git a/src/hid_core/resources/abstracted_pad/abstract_palma_handler.cpp b/src/hid_core/resources/abstracted_pad/abstract_palma_handler.cpp new file mode 100644 index 000000000..04d276d61 --- /dev/null +++ b/src/hid_core/resources/abstracted_pad/abstract_palma_handler.cpp @@ -0,0 +1,47 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "hid_core/hid_result.h" +#include "hid_core/resources/abstracted_pad/abstract_palma_handler.h" +#include "hid_core/resources/abstracted_pad/abstract_properties_handler.h" + +namespace Service::HID { + +NpadAbstractPalmaHandler::NpadAbstractPalmaHandler() {} + +NpadAbstractPalmaHandler::~NpadAbstractPalmaHandler() = default; + +void NpadAbstractPalmaHandler::SetAbstractPadHolder(NpadAbstractedPadHolder* holder) { + abstract_pad_holder = holder; +} + +void NpadAbstractPalmaHandler::SetPropertiesHandler(NpadAbstractPropertiesHandler* handler) { + properties_handler = handler; + return; +} + +void NpadAbstractPalmaHandler::SetPalmaResource(PalmaResource* resource) { + palma_resource = resource; +} + +Result NpadAbstractPalmaHandler::IncrementRefCounter() { + if (ref_counter == std::numeric_limits::max() - 1) { + return ResultNpadHandlerOverflow; + } + ref_counter++; + return ResultSuccess; +} + +Result NpadAbstractPalmaHandler::DecrementRefCounter() { + if (ref_counter == 0) { + return ResultNpadHandlerNotInitialized; + } + ref_counter--; + return ResultSuccess; +} + +void NpadAbstractPalmaHandler::UpdatePalmaState() { + // TODO +} + +} // namespace Service::HID diff --git a/src/hid_core/resources/abstracted_pad/abstract_palma_handler.h b/src/hid_core/resources/abstracted_pad/abstract_palma_handler.h new file mode 100644 index 000000000..fbd2e67e5 --- /dev/null +++ b/src/hid_core/resources/abstracted_pad/abstract_palma_handler.h @@ -0,0 +1,37 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include "common/common_types.h" +#include "core/hle/result.h" +#include "hid_core/hid_types.h" + +namespace Service::HID { +class NpadAbstractedPadHolder; +class NpadAbstractPropertiesHandler; +class PalmaResource; + +class NpadAbstractPalmaHandler final { +public: + explicit NpadAbstractPalmaHandler(); + ~NpadAbstractPalmaHandler(); + + void SetAbstractPadHolder(NpadAbstractedPadHolder* holder); + void SetPropertiesHandler(NpadAbstractPropertiesHandler* handler); + void SetPalmaResource(PalmaResource* resource); + + Result IncrementRefCounter(); + Result DecrementRefCounter(); + + void UpdatePalmaState(); + +private: + NpadAbstractedPadHolder* abstract_pad_holder{nullptr}; + NpadAbstractPropertiesHandler* properties_handler{nullptr}; + PalmaResource* palma_resource{nullptr}; + + s32 ref_counter{}; +}; + +} // namespace Service::HID diff --git a/src/hid_core/resources/abstracted_pad/abstract_properties_handler.cpp b/src/hid_core/resources/abstracted_pad/abstract_properties_handler.cpp new file mode 100644 index 000000000..4897a2784 --- /dev/null +++ b/src/hid_core/resources/abstracted_pad/abstract_properties_handler.cpp @@ -0,0 +1,322 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "hid_core/hid_util.h" +#include "hid_core/resources/abstracted_pad/abstract_pad_holder.h" +#include "hid_core/resources/abstracted_pad/abstract_properties_handler.h" +#include "hid_core/resources/applet_resource.h" +#include "hid_core/resources/npad/npad_resource.h" +#include "hid_core/resources/npad/npad_types.h" +#include "hid_core/resources/shared_memory_format.h" + +namespace Service::HID { + +NpadAbstractPropertiesHandler::NpadAbstractPropertiesHandler() {} + +NpadAbstractPropertiesHandler::~NpadAbstractPropertiesHandler() = default; + +void NpadAbstractPropertiesHandler::SetAbstractPadHolder(NpadAbstractedPadHolder* holder) { + abstract_pad_holder = holder; + return; +} + +void NpadAbstractPropertiesHandler::SetAppletResource(AppletResourceHolder* applet_resource) { + applet_resource_holder = applet_resource; + return; +} + +void NpadAbstractPropertiesHandler::SetNpadId(Core::HID::NpadIdType npad_id) { + if (!IsNpadIdValid(npad_id)) { + ASSERT_MSG(false, "Invalid npad id"); + } + + npad_id_type = npad_id; +} + +Core::HID::NpadIdType NpadAbstractPropertiesHandler::GetNpadId() const { + return npad_id_type; +} + +Result NpadAbstractPropertiesHandler::IncrementRefCounter() { + if (ref_counter == std::numeric_limits::max() - 1) { + return ResultNpadHandlerOverflow; + } + + if (ref_counter != 0) { + ref_counter++; + return ResultSuccess; + } + + const auto npad_index = NpadIdTypeToIndex(npad_id_type); + for (std::size_t aruid_index = 0; aruid_index < AruidIndexMax; aruid_index++) { + auto* data = applet_resource_holder->applet_resource->GetAruidData(aruid_index); + auto& internal_state = + data->shared_memory_format->npad.npad_entry[npad_index].internal_state; + if (!data->flag.is_assigned) { + continue; + } + internal_state.fullkey_lifo.buffer_count = 0; + internal_state.handheld_lifo.buffer_count = 0; + internal_state.joy_dual_lifo.buffer_count = 0; + internal_state.joy_left_lifo.buffer_count = 0; + internal_state.joy_right_lifo.buffer_count = 0; + internal_state.palma_lifo.buffer_count = 0; + internal_state.system_ext_lifo.buffer_count = 0; + internal_state.gc_trigger_lifo.buffer_count = 0; + internal_state.sixaxis_fullkey_lifo.lifo.buffer_count = 0; + internal_state.sixaxis_handheld_lifo.lifo.buffer_count = 0; + internal_state.sixaxis_dual_left_lifo.lifo.buffer_count = 0; + internal_state.sixaxis_dual_right_lifo.lifo.buffer_count = 0; + internal_state.sixaxis_left_lifo.lifo.buffer_count = 0; + internal_state.sixaxis_right_lifo.lifo.buffer_count = 0; + + internal_state.style_tag = {Core::HID::NpadStyleSet::None}; + internal_state.assignment_mode = NpadJoyAssignmentMode::Dual; + internal_state.joycon_color = {}; + internal_state.fullkey_color = {}; + + internal_state.system_properties.raw = 0; + internal_state.button_properties.raw = 0; + internal_state.device_type.raw = 0; + + internal_state.battery_level_dual = Core::HID::NpadBatteryLevel::Empty; + internal_state.battery_level_left = Core::HID::NpadBatteryLevel::Empty; + internal_state.battery_level_right = Core::HID::NpadBatteryLevel::Empty; + + internal_state.applet_footer_type = AppletFooterUiType::None; + internal_state.applet_footer_attributes = {}; + internal_state.lark_type_l_and_main = {}; + internal_state.lark_type_r = {}; + + internal_state.sixaxis_fullkey_properties.is_newly_assigned.Assign(true); + internal_state.sixaxis_handheld_properties.is_newly_assigned.Assign(true); + internal_state.sixaxis_dual_left_properties.is_newly_assigned.Assign(true); + internal_state.sixaxis_dual_right_properties.is_newly_assigned.Assign(true); + internal_state.sixaxis_left_properties.is_newly_assigned.Assign(true); + internal_state.sixaxis_right_properties.is_newly_assigned.Assign(true); + } + + ref_counter++; + return ResultSuccess; +} + +Result NpadAbstractPropertiesHandler::DecrementRefCounter() { + if (ref_counter == 0) { + return ResultNpadHandlerNotInitialized; + } + ref_counter--; + return ResultSuccess; +} + +Result NpadAbstractPropertiesHandler::ActivateNpadUnknown0x88(u64 aruid) { + const auto npad_index = NpadIdTypeToIndex(npad_id_type); + for (std::size_t aruid_index = 0; aruid_index < AruidIndexMax; aruid_index++) { + auto* data = applet_resource_holder->applet_resource->GetAruidData(aruid_index); + if (!data->flag.is_assigned || data->aruid != aruid) { + continue; + } + UpdateDeviceProperties(aruid, data->shared_memory_format->npad.npad_entry[npad_index]); + return ResultSuccess; + } + return ResultSuccess; +} + +void NpadAbstractPropertiesHandler::UpdateDeviceType() { + // TODO +} + +void NpadAbstractPropertiesHandler::UpdateDeviceColor() { + // TODO +} + +void NpadAbstractPropertiesHandler::UpdateFooterAttributes() { + // TODO +} + +void NpadAbstractPropertiesHandler::UpdateAllDeviceProperties() { + const auto npad_index = NpadIdTypeToIndex(npad_id_type); + for (std::size_t aruid_index = 0; aruid_index < AruidIndexMax; aruid_index++) { + auto* data = applet_resource_holder->applet_resource->GetAruidData(aruid_index); + if (!data->flag.is_assigned) { + continue; + } + auto& npad_entry = data->shared_memory_format->npad.npad_entry[npad_index]; + UpdateDeviceProperties(data->aruid, npad_entry); + } +} + +Core::HID::NpadInterfaceType NpadAbstractPropertiesHandler::GetFullkeyInterfaceType() { + std::array abstract_pads{}; + const std::size_t count = abstract_pad_holder->GetAbstractedPads(abstract_pads); + + for (std::size_t i = 0; i < count; i++) { + auto* abstract_pad = abstract_pads[i]; + if (!abstract_pad->internal_flags.is_connected) { + continue; + } + if (abstract_pad->device_type != Core::HID::NpadStyleIndex::Fullkey) { + continue; + } + if (abstract_pad->interface_type >= Core::HID::NpadInterfaceType::Embedded) { + // Abort + continue; + } + return abstract_pad->interface_type; + } + + return Core::HID::NpadInterfaceType::None; +} + +Core::HID::NpadInterfaceType NpadAbstractPropertiesHandler::GetInterfaceType() { + std::array abstract_pads{}; + const std::size_t count = abstract_pad_holder->GetAbstractedPads(abstract_pads); + + for (std::size_t i = 0; i < count; i++) { + auto* abstract_pad = abstract_pads[i]; + if (!abstract_pad->internal_flags.is_connected) { + continue; + } + if (!abstract_pad->disabled_feature_set.has_identification_code) { + continue; + } + if (abstract_pad->interface_type >= Core::HID::NpadInterfaceType::Embedded) { + // Abort + continue; + } + return abstract_pad->interface_type; + } + return Core::HID::NpadInterfaceType::None; +} + +Core::HID::NpadStyleSet NpadAbstractPropertiesHandler::GetStyleSet(u64 aruid) { + // TODO + return Core::HID::NpadStyleSet::None; +} + +std::size_t NpadAbstractPropertiesHandler::GetAbstractedPadsWithStyleTag( + std::span list, Core::HID::NpadStyleTag style) { + std::array abstract_pads{}; + const std::size_t count = abstract_pad_holder->GetAbstractedPads(abstract_pads); + + if (count == 0) { + return count; + } + + bool is_supported_style_set{}; + const auto result = applet_resource_holder->shared_npad_resource->IsSupportedNpadStyleSet( + is_supported_style_set, applet_resource_holder->applet_resource->GetActiveAruid()); + + if (!is_supported_style_set || result.IsError()) { + for (std::size_t i = 0; i < count; i++) { + // TODO + } + return count; + } + + std::size_t filtered_count{}; + for (std::size_t i = 0; i < count; i++) { + auto* abstract_pad = abstract_pads[i]; + const bool is_enabled = true; + if (is_enabled) { + list[filtered_count] = abstract_pad; + filtered_count++; + } + } + + return filtered_count; +} + +std::size_t NpadAbstractPropertiesHandler::GetAbstractedPads(std::span list) { + Core::HID::NpadStyleTag style{ + GetStyleSet(applet_resource_holder->applet_resource->GetActiveAruid())}; + return GetAbstractedPadsWithStyleTag(list, style); +} + +AppletFooterUiType NpadAbstractPropertiesHandler::GetAppletFooterUiType() { + return applet_ui_type.footer; +} + +AppletDetailedUiType NpadAbstractPropertiesHandler::GetAppletDetailedUiType() { + return applet_ui_type; +} + +void NpadAbstractPropertiesHandler::UpdateDeviceProperties(u64 aruid, + NpadSharedMemoryEntry& internal_state) { + // TODO +} + +Core::HID::NpadInterfaceType NpadAbstractPropertiesHandler::GetNpadInterfaceType() { + std::array abstract_pads{}; + const std::size_t count = abstract_pad_holder->GetAbstractedPads(abstract_pads); + + for (std::size_t i = 0; i < count; i++) { + auto* abstract_pad = abstract_pads[i]; + if (!abstract_pad->internal_flags.is_connected) { + continue; + } + if (abstract_pad->interface_type >= Core::HID::NpadInterfaceType::Embedded) { + // Abort + continue; + } + return abstract_pad->interface_type; + } + + return Core::HID::NpadInterfaceType::None; +} + +Result NpadAbstractPropertiesHandler::GetNpadFullKeyGripColor( + Core::HID::NpadColor& main_color, Core::HID::NpadColor& sub_color) const { + std::array abstract_pads{}; + const std::size_t count = abstract_pad_holder->GetAbstractedPads(abstract_pads); + + if (applet_ui_type.footer != AppletFooterUiType::SwitchProController) { + return ResultNpadIsNotProController; + } + + for (std::size_t i = 0; i < count; i++) { + auto* abstract_pad = abstract_pads[i]; + if (!abstract_pad->internal_flags.is_connected) { + continue; + } + return ResultSuccess; + } + + return ResultNpadIsNotProController; +} + +void NpadAbstractPropertiesHandler::GetNpadLeftRightInterfaceType( + Core::HID::NpadInterfaceType& out_left_interface, + Core::HID::NpadInterfaceType& out_right_interface) const { + out_left_interface = Core::HID::NpadInterfaceType::None; + out_right_interface = Core::HID::NpadInterfaceType::None; + + std::array abstract_pads{}; + const std::size_t count = abstract_pad_holder->GetAbstractedPads(abstract_pads); + + for (std::size_t i = 0; i < count; i++) { + auto* abstract_pad = abstract_pads[i]; + if (!abstract_pad->internal_flags.is_connected) { + continue; + } + if (abstract_pad->assignment_style.is_external_left_assigned && + abstract_pad->assignment_style.is_handheld_left_assigned) { + if (abstract_pad->interface_type > Core::HID::NpadInterfaceType::Embedded) { + // Abort + continue; + } + out_left_interface = abstract_pad->interface_type; + continue; + } + if (abstract_pad->assignment_style.is_external_right_assigned && + abstract_pad->assignment_style.is_handheld_right_assigned) { + if (abstract_pad->interface_type > Core::HID::NpadInterfaceType::Embedded) { + // Abort + continue; + } + out_right_interface = abstract_pad->interface_type; + continue; + } + } +} + +} // namespace Service::HID diff --git a/src/hid_core/resources/abstracted_pad/abstract_properties_handler.h b/src/hid_core/resources/abstracted_pad/abstract_properties_handler.h new file mode 100644 index 000000000..fa6827899 --- /dev/null +++ b/src/hid_core/resources/abstracted_pad/abstract_properties_handler.h @@ -0,0 +1,86 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include + +#include "common/common_types.h" +#include "core/hle/result.h" +#include "hid_core/hid_types.h" +#include "hid_core/resources/npad/npad_types.h" + +namespace Service::HID { +struct NpadSharedMemoryEntry; + +struct AppletResourceHolder; +class NpadAbstractedPadHolder; + +struct ColorProperties { + ColorAttribute attribute; + Core::HID::NpadControllerColor color; + INSERT_PADDING_BYTES(0x4); +}; + +/// Handles Npad request from HID interfaces +class NpadAbstractPropertiesHandler final { +public: + explicit NpadAbstractPropertiesHandler(); + ~NpadAbstractPropertiesHandler(); + + void SetAbstractPadHolder(NpadAbstractedPadHolder* holder); + void SetAppletResource(AppletResourceHolder* applet_resource); + void SetNpadId(Core::HID::NpadIdType npad_id); + + Core::HID::NpadIdType GetNpadId() const; + + Result IncrementRefCounter(); + Result DecrementRefCounter(); + + Result ActivateNpadUnknown0x88(u64 aruid); + + void UpdateDeviceType(); + void UpdateDeviceColor(); + void UpdateFooterAttributes(); + void UpdateAllDeviceProperties(); + + Core::HID::NpadInterfaceType GetFullkeyInterfaceType(); + Core::HID::NpadInterfaceType GetInterfaceType(); + + Core::HID::NpadStyleSet GetStyleSet(u64 aruid); + std::size_t GetAbstractedPadsWithStyleTag(std::span list, + Core::HID::NpadStyleTag style); + std::size_t GetAbstractedPads(std::span list); + + AppletFooterUiType GetAppletFooterUiType(); + + AppletDetailedUiType GetAppletDetailedUiType(); + + void UpdateDeviceProperties(u64 aruid, NpadSharedMemoryEntry& internal_state); + + Core::HID::NpadInterfaceType GetNpadInterfaceType(); + + Result GetNpadFullKeyGripColor(Core::HID::NpadColor& main_color, + Core::HID::NpadColor& sub_color) const; + + void GetNpadLeftRightInterfaceType(Core::HID::NpadInterfaceType& param_2, + Core::HID::NpadInterfaceType& param_3) const; + +private: + AppletResourceHolder* applet_resource_holder{nullptr}; + NpadAbstractedPadHolder* abstract_pad_holder{nullptr}; + Core::HID::NpadIdType npad_id_type{Core::HID::NpadIdType::Invalid}; + s32 ref_counter{}; + Core::HID::DeviceIndex device_type{}; + AppletDetailedUiType applet_ui_type{}; + AppletFooterUiAttributes applet_ui_attributes{}; + bool is_vertical{}; + bool is_horizontal{}; + bool use_plus{}; + bool use_minus{}; + bool has_directional_buttons{}; + ColorProperties fullkey_color{}; + ColorProperties left_color{}; + ColorProperties right_color{}; +}; +} // namespace Service::HID diff --git a/src/hid_core/resources/abstracted_pad/abstract_sixaxis_handler.cpp b/src/hid_core/resources/abstracted_pad/abstract_sixaxis_handler.cpp new file mode 100644 index 000000000..6d759298e --- /dev/null +++ b/src/hid_core/resources/abstracted_pad/abstract_sixaxis_handler.cpp @@ -0,0 +1,154 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "hid_core/hid_result.h" +#include "hid_core/hid_util.h" +#include "hid_core/resources/abstracted_pad/abstract_pad_holder.h" +#include "hid_core/resources/abstracted_pad/abstract_properties_handler.h" +#include "hid_core/resources/abstracted_pad/abstract_sixaxis_handler.h" +#include "hid_core/resources/applet_resource.h" +#include "hid_core/resources/npad/npad_types.h" +#include "hid_core/resources/shared_memory_format.h" + +namespace Service::HID { + +NpadAbstractSixAxisHandler::NpadAbstractSixAxisHandler() {} + +NpadAbstractSixAxisHandler::~NpadAbstractSixAxisHandler() = default; + +void NpadAbstractSixAxisHandler::SetAbstractPadHolder(NpadAbstractedPadHolder* holder) { + abstract_pad_holder = holder; +} + +void NpadAbstractSixAxisHandler::SetAppletResource(AppletResourceHolder* applet_resource) { + applet_resource_holder = applet_resource; +} + +void NpadAbstractSixAxisHandler::SetPropertiesHandler(NpadAbstractPropertiesHandler* handler) { + properties_handler = handler; +} + +void NpadAbstractSixAxisHandler::SetSixaxisResource(SixAxisResource* resource) { + six_axis_resource = resource; +} + +Result NpadAbstractSixAxisHandler::IncrementRefCounter() { + if (ref_counter == std::numeric_limits::max() - 1) { + return ResultNpadHandlerOverflow; + } + ref_counter++; + return ResultSuccess; +} + +Result NpadAbstractSixAxisHandler::DecrementRefCounter() { + if (ref_counter == 0) { + return ResultNpadHandlerNotInitialized; + } + ref_counter--; + return ResultSuccess; +} + +u64 NpadAbstractSixAxisHandler::IsFirmwareUpdateAvailable() { + // TODO + return false; +} + +Result NpadAbstractSixAxisHandler::UpdateSixAxisState() { + Core::HID::NpadIdType npad_id = properties_handler->GetNpadId(); + for (std::size_t i = 0; i < AruidIndexMax; i++) { + auto* data = applet_resource_holder->applet_resource->GetAruidDataByIndex(i); + if (data->flag.is_assigned) { + continue; + } + auto& npad_entry = data->shared_memory_format->npad.npad_entry[NpadIdTypeToIndex(npad_id)]; + UpdateSixaxisInternalState(npad_entry, data->aruid, + data->flag.enable_six_axis_sensor.As()); + } + return ResultSuccess; +} + +Result NpadAbstractSixAxisHandler::UpdateSixAxisState(u64 aruid) { + Core::HID::NpadIdType npad_id = properties_handler->GetNpadId(); + auto* data = applet_resource_holder->applet_resource->GetAruidData(aruid); + if (data == nullptr) { + return ResultSuccess; + } + auto& npad_entry = data->shared_memory_format->npad.npad_entry[NpadIdTypeToIndex(npad_id)]; + UpdateSixaxisInternalState(npad_entry, data->aruid, + data->flag.enable_six_axis_sensor.As()); + return ResultSuccess; +} + +Result NpadAbstractSixAxisHandler::UpdateSixAxisState2(u64 aruid) { + const auto npad_index = NpadIdTypeToIndex(properties_handler->GetNpadId()); + AruidData* aruid_data = applet_resource_holder->applet_resource->GetAruidData(aruid); + if (aruid_data == nullptr) { + return ResultSuccess; + } + auto& npad_internal_state = aruid_data->shared_memory_format->npad.npad_entry[npad_index]; + UpdateSixaxisInternalState(npad_internal_state, aruid, + aruid_data->flag.enable_six_axis_sensor.As()); + return ResultSuccess; +} + +void NpadAbstractSixAxisHandler::UpdateSixaxisInternalState(NpadSharedMemoryEntry& npad_entry, + u64 aruid, bool is_sensor_enabled) { + const Core::HID::NpadStyleTag style_tag{properties_handler->GetStyleSet(aruid)}; + + if (!style_tag.palma) { + UpdateSixaxisFullkeyLifo(style_tag, npad_entry.internal_state.sixaxis_fullkey_lifo, + is_sensor_enabled); + } else { + UpdateSixAxisPalmaLifo(style_tag, npad_entry.internal_state.sixaxis_fullkey_lifo, + is_sensor_enabled); + } + UpdateSixaxisHandheldLifo(style_tag, npad_entry.internal_state.sixaxis_handheld_lifo, + is_sensor_enabled); + UpdateSixaxisDualLifo(style_tag, npad_entry.internal_state.sixaxis_dual_left_lifo, + is_sensor_enabled); + UpdateSixaxisDualLifo(style_tag, npad_entry.internal_state.sixaxis_dual_right_lifo, + is_sensor_enabled); + UpdateSixaxisLeftLifo(style_tag, npad_entry.internal_state.sixaxis_left_lifo, + is_sensor_enabled); + UpdateSixaxisRightLifo(style_tag, npad_entry.internal_state.sixaxis_right_lifo, + is_sensor_enabled); + // TODO: Set sixaxis properties +} + +void NpadAbstractSixAxisHandler::UpdateSixaxisFullkeyLifo(Core::HID::NpadStyleTag style_tag, + NpadSixAxisSensorLifo& sensor_lifo, + bool is_sensor_enabled) { + // TODO +} + +void NpadAbstractSixAxisHandler::UpdateSixAxisPalmaLifo(Core::HID::NpadStyleTag style_tag, + NpadSixAxisSensorLifo& sensor_lifo, + bool is_sensor_enabled) { + // TODO +} + +void NpadAbstractSixAxisHandler::UpdateSixaxisHandheldLifo(Core::HID::NpadStyleTag style_tag, + NpadSixAxisSensorLifo& sensor_lifo, + bool is_sensor_enabled) { + // TODO +} + +void NpadAbstractSixAxisHandler::UpdateSixaxisDualLifo(Core::HID::NpadStyleTag style_tag, + NpadSixAxisSensorLifo& sensor_lifo, + bool is_sensor_enabled) { + // TODO +} + +void NpadAbstractSixAxisHandler::UpdateSixaxisLeftLifo(Core::HID::NpadStyleTag style_tag, + NpadSixAxisSensorLifo& sensor_lifo, + bool is_sensor_enabled) { + // TODO +} + +void NpadAbstractSixAxisHandler::UpdateSixaxisRightLifo(Core::HID::NpadStyleTag style_tag, + NpadSixAxisSensorLifo& sensor_lifo, + bool is_sensor_enabled) { + // TODO +} + +} // namespace Service::HID diff --git a/src/hid_core/resources/abstracted_pad/abstract_sixaxis_handler.h b/src/hid_core/resources/abstracted_pad/abstract_sixaxis_handler.h new file mode 100644 index 000000000..9c20459e9 --- /dev/null +++ b/src/hid_core/resources/abstracted_pad/abstract_sixaxis_handler.h @@ -0,0 +1,61 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include "common/common_types.h" +#include "core/hle/result.h" +#include "hid_core/hid_types.h" + +namespace Service::HID { +class SixAxisResource; +struct AppletResourceHolder; +class NpadAbstractedPadHolder; +class NpadAbstractPropertiesHandler; +struct NpadSixAxisSensorLifo; + +/// Handles Npad request from HID interfaces +class NpadAbstractSixAxisHandler final { +public: + explicit NpadAbstractSixAxisHandler(); + ~NpadAbstractSixAxisHandler(); + + void SetAbstractPadHolder(NpadAbstractedPadHolder* holder); + void SetAppletResource(AppletResourceHolder* applet_resource); + void SetPropertiesHandler(NpadAbstractPropertiesHandler* handler); + void SetSixaxisResource(SixAxisResource* resource); + + Result IncrementRefCounter(); + Result DecrementRefCounter(); + + u64 IsFirmwareUpdateAvailable(); + + Result UpdateSixAxisState(); + Result UpdateSixAxisState(u64 aruid); + Result UpdateSixAxisState2(u64 aruid); + +private: + void UpdateSixaxisInternalState(NpadSharedMemoryEntry& npad_entry, u64 aruid, + bool is_sensor_enabled); + void UpdateSixaxisFullkeyLifo(Core::HID::NpadStyleTag style_tag, + NpadSixAxisSensorLifo& sensor_lifo, bool is_sensor_enabled); + void UpdateSixAxisPalmaLifo(Core::HID::NpadStyleTag style_tag, + NpadSixAxisSensorLifo& sensor_lifo, bool is_sensor_enabled); + void UpdateSixaxisHandheldLifo(Core::HID::NpadStyleTag style_tag, + NpadSixAxisSensorLifo& sensor_lifo, bool is_sensor_enabled); + void UpdateSixaxisDualLifo(Core::HID::NpadStyleTag style_tag, + NpadSixAxisSensorLifo& sensor_lifo, bool is_sensor_enabled); + void UpdateSixaxisLeftLifo(Core::HID::NpadStyleTag style_tag, + NpadSixAxisSensorLifo& sensor_lifo, bool is_sensor_enabled); + void UpdateSixaxisRightLifo(Core::HID::NpadStyleTag style_tag, + NpadSixAxisSensorLifo& sensor_lifo, bool is_sensor_enabled); + + AppletResourceHolder* applet_resource_holder{nullptr}; + NpadAbstractedPadHolder* abstract_pad_holder{nullptr}; + NpadAbstractPropertiesHandler* properties_handler{nullptr}; + SixAxisResource* six_axis_resource{nullptr}; + + s32 ref_counter{}; +}; + +} // namespace Service::HID diff --git a/src/hid_core/resources/abstracted_pad/abstract_vibration_handler.cpp b/src/hid_core/resources/abstracted_pad/abstract_vibration_handler.cpp new file mode 100644 index 000000000..a00d6c9de --- /dev/null +++ b/src/hid_core/resources/abstracted_pad/abstract_vibration_handler.cpp @@ -0,0 +1,73 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "hid_core/hid_result.h" +#include "hid_core/hid_util.h" +#include "hid_core/resources/abstracted_pad/abstract_pad_holder.h" +#include "hid_core/resources/abstracted_pad/abstract_properties_handler.h" +#include "hid_core/resources/abstracted_pad/abstract_vibration_handler.h" +#include "hid_core/resources/applet_resource.h" +#include "hid_core/resources/npad/npad_vibration.h" +#include "hid_core/resources/vibration/gc_vibration_device.h" +#include "hid_core/resources/vibration/n64_vibration_device.h" +#include "hid_core/resources/vibration/vibration_device.h" + +namespace Service::HID { + +NpadAbstractVibrationHandler::NpadAbstractVibrationHandler() {} + +NpadAbstractVibrationHandler::~NpadAbstractVibrationHandler() = default; + +void NpadAbstractVibrationHandler::SetAbstractPadHolder(NpadAbstractedPadHolder* holder) { + abstract_pad_holder = holder; +} + +void NpadAbstractVibrationHandler::SetAppletResource(AppletResourceHolder* applet_resource) { + applet_resource_holder = applet_resource; +} + +void NpadAbstractVibrationHandler::SetPropertiesHandler(NpadAbstractPropertiesHandler* handler) { + properties_handler = handler; +} + +void NpadAbstractVibrationHandler::SetN64Vibration(NpadN64VibrationDevice* n64_device) { + n64_vibration_device = n64_device; +} + +void NpadAbstractVibrationHandler::SetVibration(std::span device) { + for (std::size_t i = 0; i < device.size() && i < vibration_device.size(); i++) { + vibration_device[i] = device[i]; + } +} + +void NpadAbstractVibrationHandler::SetGcVibration(NpadGcVibrationDevice* gc_device) { + gc_vibration_device = gc_device; +} + +Result NpadAbstractVibrationHandler::IncrementRefCounter() { + if (ref_counter == std::numeric_limits::max() - 1) { + return ResultNpadHandlerOverflow; + } + ref_counter++; + return ResultSuccess; +} + +Result NpadAbstractVibrationHandler::DecrementRefCounter() { + if (ref_counter == 0) { + return ResultNpadHandlerNotInitialized; + } + ref_counter--; + return ResultSuccess; +} + +void NpadAbstractVibrationHandler::UpdateVibrationState() { + const bool is_handheld_hid_enabled = + applet_resource_holder->handheld_config->is_handheld_hid_enabled; + const bool is_force_handheld_style_vibration = + applet_resource_holder->handheld_config->is_force_handheld_style_vibration; + + if (!is_handheld_hid_enabled && is_force_handheld_style_vibration) { + // TODO + } +} +} // namespace Service::HID diff --git a/src/hid_core/resources/abstracted_pad/abstract_vibration_handler.h b/src/hid_core/resources/abstracted_pad/abstract_vibration_handler.h new file mode 100644 index 000000000..aeb07ce86 --- /dev/null +++ b/src/hid_core/resources/abstracted_pad/abstract_vibration_handler.h @@ -0,0 +1,51 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include + +#include "common/common_types.h" +#include "core/hle/result.h" +#include "hid_core/hid_types.h" + +namespace Service::HID { +struct AppletResourceHolder; +class NpadAbstractedPadHolder; +class NpadAbstractPropertiesHandler; +class NpadGcVibrationDevice; +class NpadVibrationDevice; +class NpadN64VibrationDevice; +class NpadVibration; + +/// Keeps track of battery levels and updates npad battery shared memory values +class NpadAbstractVibrationHandler final { +public: + explicit NpadAbstractVibrationHandler(); + ~NpadAbstractVibrationHandler(); + + void SetAbstractPadHolder(NpadAbstractedPadHolder* holder); + void SetAppletResource(AppletResourceHolder* applet_resource); + void SetPropertiesHandler(NpadAbstractPropertiesHandler* handler); + + void SetN64Vibration(NpadN64VibrationDevice* n64_device); + void SetVibration(std::span device); + void SetGcVibration(NpadGcVibrationDevice* gc_device); + + Result IncrementRefCounter(); + Result DecrementRefCounter(); + + void UpdateVibrationState(); + +private: + AppletResourceHolder* applet_resource_holder{nullptr}; + NpadAbstractedPadHolder* abstract_pad_holder{nullptr}; + NpadAbstractPropertiesHandler* properties_handler{nullptr}; + + NpadN64VibrationDevice* n64_vibration_device{nullptr}; + std::array vibration_device{}; + NpadGcVibrationDevice* gc_vibration_device{nullptr}; + NpadVibration* vibration_handler{nullptr}; + s32 ref_counter{}; +}; +} // namespace Service::HID diff --git a/src/hid_core/resources/npad/npad.cpp b/src/hid_core/resources/npad/npad.cpp index 1f8a0f8ab..97537a2e2 100644 --- a/src/hid_core/resources/npad/npad.cpp +++ b/src/hid_core/resources/npad/npad.cpp @@ -193,7 +193,7 @@ void NPad::InitNewlyAddedController(u64 aruid, Core::HID::NpadIdType npad_id) { case Core::HID::NpadStyleIndex::None: ASSERT(false); break; - case Core::HID::NpadStyleIndex::ProController: + case Core::HID::NpadStyleIndex::Fullkey: shared_memory->fullkey_color.attribute = ColorAttribute::Ok; shared_memory->fullkey_color.fullkey = body_colors.fullkey; shared_memory->battery_level_dual = battery_level.dual.battery_level; @@ -491,7 +491,7 @@ void NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing) { case Core::HID::NpadStyleIndex::None: ASSERT(false); break; - case Core::HID::NpadStyleIndex::ProController: + case Core::HID::NpadStyleIndex::Fullkey: case Core::HID::NpadStyleIndex::NES: case Core::HID::NpadStyleIndex::SNES: case Core::HID::NpadStyleIndex::N64: @@ -1292,7 +1292,7 @@ Core::HID::SixAxisSensorProperties& NPad::GetSixaxisProperties( u64 aruid, const Core::HID::SixAxisSensorHandle& sixaxis_handle) { auto& controller = GetControllerFromHandle(aruid, sixaxis_handle); switch (sixaxis_handle.npad_type) { - case Core::HID::NpadStyleIndex::ProController: + case Core::HID::NpadStyleIndex::Fullkey: case Core::HID::NpadStyleIndex::Pokeball: return controller.shared_memory->sixaxis_fullkey_properties; case Core::HID::NpadStyleIndex::Handheld: @@ -1315,7 +1315,7 @@ const Core::HID::SixAxisSensorProperties& NPad::GetSixaxisProperties( u64 aruid, const Core::HID::SixAxisSensorHandle& sixaxis_handle) const { const auto& controller = GetControllerFromHandle(aruid, sixaxis_handle); switch (sixaxis_handle.npad_type) { - case Core::HID::NpadStyleIndex::ProController: + case Core::HID::NpadStyleIndex::Fullkey: case Core::HID::NpadStyleIndex::Pokeball: return controller.shared_memory->sixaxis_fullkey_properties; case Core::HID::NpadStyleIndex::Handheld: diff --git a/src/hid_core/resources/npad/npad_data.cpp b/src/hid_core/resources/npad/npad_data.cpp index c7e9760cb..29ad5cb08 100644 --- a/src/hid_core/resources/npad/npad_data.cpp +++ b/src/hid_core/resources/npad/npad_data.cpp @@ -151,7 +151,7 @@ Core::HID::NpadStyleSet NPadData::GetSupportedNpadStyleSet() const { bool NPadData::IsNpadStyleIndexSupported(Core::HID::NpadStyleIndex style_index) const { Core::HID::NpadStyleTag style = {supported_npad_style_set}; switch (style_index) { - case Core::HID::NpadStyleIndex::ProController: + case Core::HID::NpadStyleIndex::Fullkey: return style.fullkey.As(); case Core::HID::NpadStyleIndex::Handheld: return style.handheld.As(); diff --git a/src/hid_core/resources/npad/npad_types.h b/src/hid_core/resources/npad/npad_types.h index a02f9cf16..074dd40cf 100644 --- a/src/hid_core/resources/npad/npad_types.h +++ b/src/hid_core/resources/npad/npad_types.h @@ -252,4 +252,103 @@ enum class NpadLagerType : u32 { U, }; +// nn::hidtypes::FeatureType +struct FeatureType { + union { + u64 raw{}; + BitField<0, 1, u64> has_left_analog_stick; + BitField<1, 1, u64> has_right_analog_stick; + BitField<2, 1, u64> has_left_joy_six_axis_sensor; + BitField<3, 1, u64> has_right_joy_six_axis_sensor; + BitField<4, 1, u64> has_fullkey_joy_six_axis_sensor; + BitField<5, 1, u64> has_left_lra_vibration_device; + BitField<6, 1, u64> has_right_lra_vibration_device; + BitField<7, 1, u64> has_gc_vibration_device; + BitField<8, 1, u64> has_erm_vibration_device; + BitField<9, 1, u64> has_left_joy_rail_bus; + BitField<10, 1, u64> has_right_joy_rail_bus; + BitField<11, 1, u64> has_internal_bus; + BitField<12, 1, u64> is_palma; + BitField<13, 1, u64> has_nfc; + BitField<14, 1, u64> has_ir_sensor; + BitField<15, 1, u64> is_analog_stick_calibration_supported; + BitField<16, 1, u64> is_six_axis_Sensor_user_calibration_supported; + BitField<17, 1, u64> has_left_right_joy_battery; + BitField<18, 1, u64> has_fullkey_battery; + BitField<19, 1, u64> is_disconnect_controller_if_battery_none; + BitField<20, 1, u64> has_controller_color; + BitField<21, 1, u64> has_grip_color; + BitField<22, 1, u64> has_identification_code; + BitField<23, 1, u64> has_bluetooth_address; + BitField<24, 1, u64> has_mcu; + BitField<25, 1, u64> has_notification_led; + BitField<26, 1, u64> has_directional_buttons; + BitField<27, 1, u64> has_indicator_led; + BitField<28, 1, u64> is_button_config_embedded_supported; + BitField<29, 1, u64> is_button_config_full_supported; + BitField<30, 1, u64> is_button_config_left_supported; + BitField<31, 1, u64> is_button_config_right_supported; + BitField<32, 1, u64> is_usb_hid_device; + BitField<33, 1, u64> is_kuina_device; + BitField<34, 1, u64> is_direct_usb_to_bt_switching_device; + BitField<35, 1, u64> is_normalize_analog_stick_with_inner_cross; + }; +}; +static_assert(sizeof(FeatureType) == 8, "FeatureType is an invalid size"); + +// This is nn::hid::AssignmentStyle +struct AssignmentStyle { + union { + u32 raw{}; + BitField<0, 1, u32> is_external_assigned; + BitField<1, 1, u32> is_external_left_assigned; + BitField<2, 1, u32> is_external_right_assigned; + BitField<3, 1, u32> is_handheld_assigned; + BitField<4, 1, u32> is_handheld_left_assigned; + BitField<5, 1, u32> is_handheld_right_assigned; + }; +}; +static_assert(sizeof(AssignmentStyle) == 4, "AssignmentStyle is an invalid size"); + +// This is nn::hid::server::IAbstractedPad::InternalFlags +struct InternalFlags { + union { + u32 raw{}; + BitField<0, 1, u32> is_bound; + BitField<1, 1, u32> is_connected; + BitField<2, 1, u32> is_battery_low_ovln_required; + BitField<3, 1, u32> is_battery_low_ovln_delay_required; + BitField<4, 1, u32> is_sample_recieved; + BitField<5, 1, u32> is_virtual_input; + BitField<6, 1, u32> is_wired; + BitField<8, 1, u32> use_center_clamp; + BitField<9, 1, u32> has_virtual_six_axis_sensor_acceleration; + BitField<10, 1, u32> has_virtual_six_axis_sensor_angle; + BitField<11, 1, u32> is_debug_pad; + }; +}; +static_assert(sizeof(InternalFlags) == 4, "InternalFlags is an invalid size"); + +/// This is nn::hid::server::IAbstractedPad +struct IAbstractedPad { + InternalFlags internal_flags; + u64 controller_id; + u32 controller_number; + u64 low_battery_display_delay_time; + u64 low_battery_display_delay_interval; + FeatureType feature_set; + FeatureType disabled_feature_set; + AssignmentStyle assignment_style; + Core::HID::NpadStyleIndex device_type; + Core::HID::NpadInterfaceType interface_type; + Core::HID::NpadPowerInfo power_info; + u32 pad_state; + u32 button_mask; + u32 system_button_mask; + u8 indicator; + std::vector virtual_six_axis_sensor_acceleration; + std::vector virtual_six_axis_sensor_angle; + u64 xcd_handle; + u64 color; +}; } // namespace Service::HID diff --git a/src/hid_core/resources/npad/npad_vibration.cpp b/src/hid_core/resources/npad/npad_vibration.cpp new file mode 100644 index 000000000..3bdd55dec --- /dev/null +++ b/src/hid_core/resources/npad/npad_vibration.cpp @@ -0,0 +1,80 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "hid_core/hid_result.h" +#include "hid_core/resources/npad/npad_vibration.h" + +namespace Service::HID { + +NpadVibration::NpadVibration() {} + +NpadVibration::~NpadVibration() = default; + +Result NpadVibration::Activate() { + std::scoped_lock lock{mutex}; + + const f32 master_volume = 1.0f; // nn::settings::system::GetVibrationMasterVolume(); + // if (master_volume < 0.0f || master_volume > 1.0f) { + // return ResultVibrationStrenghtOutOfRange; + // } + + volume = master_volume; + return ResultSuccess; +} + +Result NpadVibration::Deactivate() { + return ResultSuccess; +} + +Result NpadVibration::SetVibrationMasterVolume(f32 master_volume) { + std::scoped_lock lock{mutex}; + + if (master_volume < 0.0f && master_volume > 1.0f) { + return ResultVibrationStrenghtOutOfRange; + } + + volume = master_volume; + // nn::settings::system::SetVibrationMasterVolume(master_volume); + + return ResultSuccess; +} + +Result NpadVibration::GetVibrationVolume(f32& out_volume) const { + std::scoped_lock lock{mutex}; + out_volume = volume; + return ResultSuccess; +} + +Result NpadVibration::GetVibrationMasterVolume(f32& out_volume) const { + std::scoped_lock lock{mutex}; + + const f32 master_volume = 1.0f; // nn::settings::system::GetVibrationMasterVolume(); + // if (master_volume < 0.0f || master_volume > 1.0f) { + // return ResultVibrationStrenghtOutOfRange; + // } + + out_volume = master_volume; + return ResultSuccess; +} + +Result NpadVibration::BeginPermitVibrationSession(u64 aruid) { + std::scoped_lock lock{mutex}; + session_aruid = aruid; + volume = 1.0; + return ResultSuccess; +} + +Result NpadVibration::EndPermitVibrationSession() { + std::scoped_lock lock{mutex}; + + const f32 master_volume = 1.0f; // nn::settings::system::GetVibrationMasterVolume(); + // if (master_volume < 0.0f || master_volume > 1.0f) { + // return ResultVibrationStrenghtOutOfRange; + // } + + volume = master_volume; + session_aruid = 0; + return ResultSuccess; +} + +} // namespace Service::HID diff --git a/src/hid_core/resources/npad/npad_vibration.h b/src/hid_core/resources/npad/npad_vibration.h new file mode 100644 index 000000000..0748aeffc --- /dev/null +++ b/src/hid_core/resources/npad/npad_vibration.h @@ -0,0 +1,34 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include + +#include "common/common_types.h" +#include "core/hle/result.h" + +namespace Service::HID { + +class NpadVibration final { +public: + explicit NpadVibration(); + ~NpadVibration(); + + Result Activate(); + Result Deactivate(); + + Result SetVibrationMasterVolume(f32 master_volume); + Result GetVibrationVolume(f32& out_volume) const; + Result GetVibrationMasterVolume(f32& out_volume) const; + + Result BeginPermitVibrationSession(u64 aruid); + Result EndPermitVibrationSession(); + +private: + f32 volume{}; + u64 session_aruid{}; + mutable std::mutex mutex; +}; + +} // namespace Service::HID diff --git a/src/hid_core/resources/six_axis/six_axis.cpp b/src/hid_core/resources/six_axis/six_axis.cpp index 8a9677c50..da12d2d5a 100644 --- a/src/hid_core/resources/six_axis/six_axis.cpp +++ b/src/hid_core/resources/six_axis/six_axis.cpp @@ -114,7 +114,7 @@ void SixAxis::OnUpdate(const Core::Timing::CoreTiming& core_timing) { case Core::HID::NpadStyleIndex::None: ASSERT(false); break; - case Core::HID::NpadStyleIndex::ProController: + case Core::HID::NpadStyleIndex::Fullkey: set_motion_state(sixaxis_fullkey_state, motion_state[0]); break; case Core::HID::NpadStyleIndex::Handheld: @@ -345,7 +345,7 @@ SixAxis::SixaxisParameters& SixAxis::GetSixaxisState( const Core::HID::SixAxisSensorHandle& sixaxis_handle) { auto& controller = GetControllerFromHandle(sixaxis_handle); switch (sixaxis_handle.npad_type) { - case Core::HID::NpadStyleIndex::ProController: + case Core::HID::NpadStyleIndex::Fullkey: case Core::HID::NpadStyleIndex::Pokeball: return controller.sixaxis_fullkey; case Core::HID::NpadStyleIndex::Handheld: @@ -368,7 +368,7 @@ const SixAxis::SixaxisParameters& SixAxis::GetSixaxisState( const Core::HID::SixAxisSensorHandle& sixaxis_handle) const { const auto& controller = GetControllerFromHandle(sixaxis_handle); switch (sixaxis_handle.npad_type) { - case Core::HID::NpadStyleIndex::ProController: + case Core::HID::NpadStyleIndex::Fullkey: case Core::HID::NpadStyleIndex::Pokeball: return controller.sixaxis_fullkey; case Core::HID::NpadStyleIndex::Handheld: diff --git a/src/hid_core/resources/vibration/gc_vibration_device.cpp b/src/hid_core/resources/vibration/gc_vibration_device.cpp new file mode 100644 index 000000000..f01f81b9a --- /dev/null +++ b/src/hid_core/resources/vibration/gc_vibration_device.cpp @@ -0,0 +1,106 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "hid_core/hid_result.h" +#include "hid_core/resources/npad/npad_types.h" +#include "hid_core/resources/npad/npad_vibration.h" +#include "hid_core/resources/vibration/gc_vibration_device.h" + +namespace Service::HID { + +NpadGcVibrationDevice::NpadGcVibrationDevice() {} + +Result NpadGcVibrationDevice::IncrementRefCounter() { + if (ref_counter == 0 && is_mounted) { + f32 volume = 1.0f; + const auto result = vibration_handler->GetVibrationVolume(volume); + if (result.IsSuccess()) { + // TODO: SendVibrationGcErmCommand + } + } + ref_counter++; + return ResultSuccess; +} + +Result NpadGcVibrationDevice::DecrementRefCounter() { + if (ref_counter == 1 && !is_mounted) { + f32 volume = 1.0f; + const auto result = vibration_handler->GetVibrationVolume(volume); + if (result.IsSuccess()) { + // TODO: SendVibrationGcErmCommand + } + } + + if (ref_counter > 0) { + ref_counter--; + } + + return ResultSuccess; +} + +Result NpadGcVibrationDevice::SendVibrationGcErmCommand(Core::HID::VibrationGcErmCommand command) { + if (!is_mounted) { + return ResultSuccess; + } + f32 volume = 1.0f; + const auto result = vibration_handler->GetVibrationVolume(volume); + if (result.IsError()) { + return result; + } + if (volume == 0.0) { + command = Core::HID::VibrationGcErmCommand::Stop; + } else { + if (command > Core::HID::VibrationGcErmCommand::StopHard) { + // Abort + return ResultSuccess; + } + } + // TODO: SendVibrationGcErmCommand + return ResultSuccess; +} + +Result NpadGcVibrationDevice::GetActualVibrationGcErmCommand( + Core::HID::VibrationGcErmCommand& out_command) { + if (!is_mounted) { + out_command = Core::HID::VibrationGcErmCommand::Stop; + return ResultSuccess; + } + + f32 volume = 1.0f; + const auto result = vibration_handler->GetVibrationVolume(volume); + if (result.IsError()) { + return result; + } + if (volume == 0.0f) { + out_command = Core::HID::VibrationGcErmCommand::Stop; + return ResultSuccess; + } + + // TODO: GetActualVibrationGcErmCommand + return ResultSuccess; +} + +Result NpadGcVibrationDevice::SendVibrationNotificationPattern( + Core::HID::VibrationGcErmCommand command) { + if (!is_mounted) { + return ResultSuccess; + } + + f32 volume = 1.0f; + const auto result = vibration_handler->GetVibrationVolume(volume); + if (result.IsError()) { + return result; + } + if (volume <= 0.0f) { + command = Core::HID::VibrationGcErmCommand::Stop; + } + if (command > Core::HID::VibrationGcErmCommand::StopHard) { + // Abort + return ResultSuccess; + } + + // TODO: SendVibrationNotificationPattern + return ResultSuccess; +} + +} // namespace Service::HID diff --git a/src/hid_core/resources/vibration/gc_vibration_device.h b/src/hid_core/resources/vibration/gc_vibration_device.h new file mode 100644 index 000000000..87abca57d --- /dev/null +++ b/src/hid_core/resources/vibration/gc_vibration_device.h @@ -0,0 +1,31 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include +#include + +#include "common/common_types.h" +#include "core/hle/result.h" +#include "hid_core/hid_types.h" +#include "hid_core/resources/npad/npad_types.h" +#include "hid_core/resources/vibration/vibration_base.h" + +namespace Service::HID { +class NpadVibration; + +/// Handles Npad request from HID interfaces +class NpadGcVibrationDevice final : public NpadVibrationBase { +public: + explicit NpadGcVibrationDevice(); + + Result IncrementRefCounter() override; + Result DecrementRefCounter() override; + + Result SendVibrationGcErmCommand(Core::HID::VibrationGcErmCommand command); + + Result GetActualVibrationGcErmCommand(Core::HID::VibrationGcErmCommand& out_command); + Result SendVibrationNotificationPattern(Core::HID::VibrationGcErmCommand command); +}; +} // namespace Service::HID diff --git a/src/hid_core/resources/vibration/n64_vibration_device.cpp b/src/hid_core/resources/vibration/n64_vibration_device.cpp new file mode 100644 index 000000000..639f87abf --- /dev/null +++ b/src/hid_core/resources/vibration/n64_vibration_device.cpp @@ -0,0 +1,80 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "hid_core/hid_result.h" +#include "hid_core/resources/npad/npad_types.h" +#include "hid_core/resources/npad/npad_vibration.h" +#include "hid_core/resources/vibration/n64_vibration_device.h" + +namespace Service::HID { + +NpadN64VibrationDevice::NpadN64VibrationDevice() {} + +Result NpadN64VibrationDevice::IncrementRefCounter() { + if (ref_counter == 0 && is_mounted) { + f32 volume = 1.0f; + const auto result = vibration_handler->GetVibrationVolume(volume); + if (result.IsSuccess()) { + // TODO: SendVibrationInBool + } + } + + ref_counter++; + return ResultSuccess; +} + +Result NpadN64VibrationDevice::DecrementRefCounter() { + if (ref_counter == 1) { + if (!is_mounted) { + ref_counter = 0; + if (is_mounted != false) { + // TODO: SendVibrationInBool + } + return ResultSuccess; + } + f32 volume = 1.0f; + const auto result = vibration_handler->GetVibrationVolume(volume); + if (result.IsSuccess()) { + // TODO + } + } + + if (ref_counter > 0) { + ref_counter--; + } + + return ResultSuccess; +} + +Result NpadN64VibrationDevice::SendValueInBool(bool is_vibrating) { + if (ref_counter < 1) { + return ResultVibrationNotInitialized; + } + if (is_mounted) { + f32 volume = 1.0f; + const auto result = vibration_handler->GetVibrationVolume(volume); + if (result.IsError()) { + return result; + } + // TODO: SendVibrationInBool + } + return ResultSuccess; +} + +Result NpadN64VibrationDevice::SendVibrationNotificationPattern([[maybe_unused]] u32 pattern) { + if (!is_mounted) { + return ResultSuccess; + } + f32 volume = 1.0f; + const auto result = vibration_handler->GetVibrationVolume(volume); + if (result.IsError()) { + return result; + } + if (volume <= 0.0) { + pattern = 0; + } + // TODO: SendVibrationNotificationPattern + return ResultSuccess; +} + +} // namespace Service::HID diff --git a/src/hid_core/resources/vibration/n64_vibration_device.h b/src/hid_core/resources/vibration/n64_vibration_device.h new file mode 100644 index 000000000..54e6efc1a --- /dev/null +++ b/src/hid_core/resources/vibration/n64_vibration_device.h @@ -0,0 +1,29 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include +#include + +#include "common/common_types.h" +#include "core/hle/result.h" +#include "hid_core/hid_types.h" +#include "hid_core/resources/npad/npad_types.h" +#include "hid_core/resources/vibration/vibration_base.h" + +namespace Service::HID { +class NpadVibration; + +/// Handles Npad request from HID interfaces +class NpadN64VibrationDevice final : public NpadVibrationBase { +public: + explicit NpadN64VibrationDevice(); + + Result IncrementRefCounter() override; + Result DecrementRefCounter() override; + + Result SendValueInBool(bool is_vibrating); + Result SendVibrationNotificationPattern(u32 pattern); +}; +} // namespace Service::HID diff --git a/src/hid_core/resources/vibration/vibration_base.cpp b/src/hid_core/resources/vibration/vibration_base.cpp new file mode 100644 index 000000000..350f349c2 --- /dev/null +++ b/src/hid_core/resources/vibration/vibration_base.cpp @@ -0,0 +1,30 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "hid_core/hid_result.h" +#include "hid_core/resources/npad/npad_types.h" +#include "hid_core/resources/npad/npad_vibration.h" +#include "hid_core/resources/vibration/vibration_base.h" + +namespace Service::HID { + +NpadVibrationBase::NpadVibrationBase() {} + +Result NpadVibrationBase::IncrementRefCounter() { + ref_counter++; + return ResultSuccess; +} + +Result NpadVibrationBase::DecrementRefCounter() { + if (ref_counter > 0) { + ref_counter--; + } + + return ResultSuccess; +} + +bool NpadVibrationBase::IsVibrationMounted() const { + return is_mounted; +} + +} // namespace Service::HID diff --git a/src/hid_core/resources/vibration/vibration_base.h b/src/hid_core/resources/vibration/vibration_base.h new file mode 100644 index 000000000..c6c5fc4d9 --- /dev/null +++ b/src/hid_core/resources/vibration/vibration_base.h @@ -0,0 +1,28 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include "common/common_types.h" +#include "core/hle/result.h" + +namespace Service::HID { +class NpadVibration; + +/// Handles Npad request from HID interfaces +class NpadVibrationBase { +public: + explicit NpadVibrationBase(); + + virtual Result IncrementRefCounter(); + virtual Result DecrementRefCounter(); + + bool IsVibrationMounted() const; + +protected: + u64 xcd_handle{}; + s32 ref_counter{}; + bool is_mounted{}; + NpadVibration* vibration_handler{nullptr}; +}; +} // namespace Service::HID diff --git a/src/hid_core/resources/vibration/vibration_device.cpp b/src/hid_core/resources/vibration/vibration_device.cpp new file mode 100644 index 000000000..888c3a7ed --- /dev/null +++ b/src/hid_core/resources/vibration/vibration_device.cpp @@ -0,0 +1,84 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "hid_core/hid_result.h" +#include "hid_core/resources/npad/npad_types.h" +#include "hid_core/resources/npad/npad_vibration.h" +#include "hid_core/resources/vibration/vibration_device.h" + +namespace Service::HID { + +NpadVibrationDevice::NpadVibrationDevice() {} + +Result NpadVibrationDevice::IncrementRefCounter() { + ref_counter++; + return ResultSuccess; +} + +Result NpadVibrationDevice::DecrementRefCounter() { + if (ref_counter > 0) { + ref_counter--; + } + + return ResultSuccess; +} + +Result NpadVibrationDevice::SendVibrationValue(const Core::HID::VibrationValue& value) { + if (ref_counter == 0) { + return ResultVibrationNotInitialized; + } + if (!is_mounted) { + return ResultSuccess; + } + + f32 volume = 1.0f; + const auto result = vibration_handler->GetVibrationVolume(volume); + if (result.IsError()) { + return result; + } + if (volume <= 0.0f) { + // TODO: SendVibrationValue + return ResultSuccess; + } + + Core::HID::VibrationValue vibration_value = value; + vibration_value.high_amplitude *= volume; + vibration_value.low_amplitude *= volume; + + // TODO: SendVibrationValue + return ResultSuccess; +} + +Result NpadVibrationDevice::SendVibrationNotificationPattern([[maybe_unused]] u32 pattern) { + if (!is_mounted) { + return ResultSuccess; + } + + f32 volume = 1.0f; + const auto result = vibration_handler->GetVibrationVolume(volume); + if (result.IsError()) { + return result; + } + if (volume <= 0.0) { + pattern = 0; + } + + // return xcd_handle->SendVibrationNotificationPattern(pattern); + return ResultSuccess; +} + +Result NpadVibrationDevice::GetActualVibrationValue(Core::HID::VibrationValue& out_value) { + if (ref_counter < 1) { + return ResultVibrationNotInitialized; + } + + out_value = Core::HID::DEFAULT_VIBRATION_VALUE; + if (!is_mounted) { + return ResultSuccess; + } + + // TODO: SendVibrationValue + return ResultSuccess; +} + +} // namespace Service::HID diff --git a/src/hid_core/resources/vibration/vibration_device.h b/src/hid_core/resources/vibration/vibration_device.h new file mode 100644 index 000000000..3574ad60b --- /dev/null +++ b/src/hid_core/resources/vibration/vibration_device.h @@ -0,0 +1,35 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include +#include + +#include "common/common_types.h" +#include "core/hle/result.h" +#include "hid_core/hid_types.h" +#include "hid_core/resources/npad/npad_types.h" +#include "hid_core/resources/vibration/vibration_base.h" + +namespace Service::HID { +class NpadVibration; + +/// Handles Npad request from HID interfaces +class NpadVibrationDevice final : public NpadVibrationBase { +public: + explicit NpadVibrationDevice(); + + Result IncrementRefCounter(); + Result DecrementRefCounter(); + + Result SendVibrationValue(const Core::HID::VibrationValue& value); + Result SendVibrationNotificationPattern(u32 pattern); + + Result GetActualVibrationValue(Core::HID::VibrationValue& out_value); + +private: + u32 device_index{}; +}; + +} // namespace Service::HID diff --git a/src/yuzu/applets/qt_controller.cpp b/src/yuzu/applets/qt_controller.cpp index 8b340ee6c..48ce860ad 100644 --- a/src/yuzu/applets/qt_controller.cpp +++ b/src/yuzu/applets/qt_controller.cpp @@ -41,7 +41,7 @@ void UpdateController(Core::HID::EmulatedController* controller, bool IsControllerCompatible(Core::HID::NpadStyleIndex controller_type, Core::Frontend::ControllerParameters parameters) { switch (controller_type) { - case Core::HID::NpadStyleIndex::ProController: + case Core::HID::NpadStyleIndex::Fullkey: return parameters.allow_pro_controller; case Core::HID::NpadStyleIndex::JoyconDual: return parameters.allow_dual_joycons; @@ -462,7 +462,7 @@ void QtControllerSelectorDialog::SetEmulatedControllers(std::size_t player_index }; if (npad_style_set.fullkey == 1) { - add_item(Core::HID::NpadStyleIndex::ProController, tr("Pro Controller")); + add_item(Core::HID::NpadStyleIndex::Fullkey, tr("Pro Controller")); } if (npad_style_set.joycon_dual == 1) { @@ -519,7 +519,7 @@ Core::HID::NpadStyleIndex QtControllerSelectorDialog::GetControllerTypeFromIndex [index](const auto& pair) { return pair.first == index; }); if (it == pairs.end()) { - return Core::HID::NpadStyleIndex::ProController; + return Core::HID::NpadStyleIndex::Fullkey; } return it->second; @@ -549,7 +549,7 @@ void QtControllerSelectorDialog::UpdateControllerIcon(std::size_t player_index) const QString stylesheet = [this, player_index] { switch (GetControllerTypeFromIndex(emulated_controllers[player_index]->currentIndex(), player_index)) { - case Core::HID::NpadStyleIndex::ProController: + case Core::HID::NpadStyleIndex::Fullkey: case Core::HID::NpadStyleIndex::GameCube: return QStringLiteral("image: url(:/controller/applet_pro_controller%0); "); case Core::HID::NpadStyleIndex::JoyconDual: diff --git a/src/yuzu/applets/qt_software_keyboard.cpp b/src/yuzu/applets/qt_software_keyboard.cpp index bbe17c35e..ac81ace9e 100644 --- a/src/yuzu/applets/qt_software_keyboard.cpp +++ b/src/yuzu/applets/qt_software_keyboard.cpp @@ -832,7 +832,7 @@ void QtSoftwareKeyboardDialog::SetControllerImage() { }(); switch (controller_type) { - case Core::HID::NpadStyleIndex::ProController: + case Core::HID::NpadStyleIndex::Fullkey: case Core::HID::NpadStyleIndex::GameCube: ui->icon_controller->setStyleSheet( QStringLiteral("image: url(:/overlay/controller_pro%1.png);").arg(theme)); diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp index f3552191a..5dac9f1e7 100644 --- a/src/yuzu/configuration/configure_input_player.cpp +++ b/src/yuzu/configuration/configure_input_player.cpp @@ -1094,7 +1094,7 @@ void ConfigureInputPlayer::SetConnectableControllers() { }; if (npad_style_set.fullkey == 1) { - add_item(Core::HID::NpadStyleIndex::ProController, tr("Pro Controller")); + add_item(Core::HID::NpadStyleIndex::Fullkey, tr("Pro Controller")); } if (npad_style_set.joycon_dual == 1) { @@ -1149,7 +1149,7 @@ Core::HID::NpadStyleIndex ConfigureInputPlayer::GetControllerTypeFromIndex(int i [index](const auto& pair) { return pair.first == index; }); if (it == index_controller_type_pairs.end()) { - return Core::HID::NpadStyleIndex::ProController; + return Core::HID::NpadStyleIndex::Fullkey; } return it->second; @@ -1178,7 +1178,7 @@ void ConfigureInputPlayer::UpdateInputDevices() { void ConfigureInputPlayer::UpdateControllerAvailableButtons() { auto layout = GetControllerTypeFromIndex(ui->comboControllerType->currentIndex()); if (debug) { - layout = Core::HID::NpadStyleIndex::ProController; + layout = Core::HID::NpadStyleIndex::Fullkey; } // List of all the widgets that will be hidden by any of the following layouts that need @@ -1206,7 +1206,7 @@ void ConfigureInputPlayer::UpdateControllerAvailableButtons() { std::vector layout_hidden; switch (layout) { - case Core::HID::NpadStyleIndex::ProController: + case Core::HID::NpadStyleIndex::Fullkey: case Core::HID::NpadStyleIndex::Handheld: layout_hidden = { ui->buttonShoulderButtonsSLSRLeft, @@ -1254,7 +1254,7 @@ void ConfigureInputPlayer::UpdateControllerAvailableButtons() { void ConfigureInputPlayer::UpdateControllerEnabledButtons() { auto layout = GetControllerTypeFromIndex(ui->comboControllerType->currentIndex()); if (debug) { - layout = Core::HID::NpadStyleIndex::ProController; + layout = Core::HID::NpadStyleIndex::Fullkey; } // List of all the widgets that will be disabled by any of the following layouts that need @@ -1271,7 +1271,7 @@ void ConfigureInputPlayer::UpdateControllerEnabledButtons() { std::vector layout_disable; switch (layout) { - case Core::HID::NpadStyleIndex::ProController: + case Core::HID::NpadStyleIndex::Fullkey: case Core::HID::NpadStyleIndex::JoyconDual: case Core::HID::NpadStyleIndex::Handheld: case Core::HID::NpadStyleIndex::JoyconLeft: @@ -1304,7 +1304,7 @@ void ConfigureInputPlayer::UpdateMotionButtons() { // Show/hide the "Motion 1/2" groupboxes depending on the currently selected controller. switch (GetControllerTypeFromIndex(ui->comboControllerType->currentIndex())) { - case Core::HID::NpadStyleIndex::ProController: + case Core::HID::NpadStyleIndex::Fullkey: case Core::HID::NpadStyleIndex::JoyconLeft: case Core::HID::NpadStyleIndex::Handheld: // Show "Motion 1" and hide "Motion 2". @@ -1333,11 +1333,11 @@ void ConfigureInputPlayer::UpdateMotionButtons() { void ConfigureInputPlayer::UpdateControllerButtonNames() { auto layout = GetControllerTypeFromIndex(ui->comboControllerType->currentIndex()); if (debug) { - layout = Core::HID::NpadStyleIndex::ProController; + layout = Core::HID::NpadStyleIndex::Fullkey; } switch (layout) { - case Core::HID::NpadStyleIndex::ProController: + case Core::HID::NpadStyleIndex::Fullkey: case Core::HID::NpadStyleIndex::JoyconDual: case Core::HID::NpadStyleIndex::Handheld: case Core::HID::NpadStyleIndex::JoyconLeft: diff --git a/src/yuzu/configuration/configure_input_player_widget.cpp b/src/yuzu/configuration/configure_input_player_widget.cpp index 19fdca7d3..8f91f5e92 100644 --- a/src/yuzu/configuration/configure_input_player_widget.cpp +++ b/src/yuzu/configuration/configure_input_player_widget.cpp @@ -244,7 +244,7 @@ void PlayerControlPreview::paintEvent(QPaintEvent* event) { case Core::HID::NpadStyleIndex::GameCube: DrawGCController(p, center); break; - case Core::HID::NpadStyleIndex::ProController: + case Core::HID::NpadStyleIndex::Fullkey: default: DrawProController(p, center); break; diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 4f4c75f5c..fd5342537 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -3988,7 +3988,7 @@ void GMainWindow::OnToggleDockedMode() { tr("Handheld controller can't be used on docked mode. Pro " "controller will be selected.")); handheld->Disconnect(); - player_1->SetNpadStyleIndex(Core::HID::NpadStyleIndex::ProController); + player_1->SetNpadStyleIndex(Core::HID::NpadStyleIndex::Fullkey); player_1->Connect(); controller_dialog->refreshConfiguration(); } diff --git a/src/yuzu/util/controller_navigation.cpp b/src/yuzu/util/controller_navigation.cpp index 2690b075d..0dbfca243 100644 --- a/src/yuzu/util/controller_navigation.cpp +++ b/src/yuzu/util/controller_navigation.cpp @@ -66,7 +66,7 @@ void ControllerNavigation::ControllerUpdateButton() { } switch (controller_type) { - case Core::HID::NpadStyleIndex::ProController: + case Core::HID::NpadStyleIndex::Fullkey: case Core::HID::NpadStyleIndex::JoyconDual: case Core::HID::NpadStyleIndex::Handheld: case Core::HID::NpadStyleIndex::GameCube: @@ -116,7 +116,7 @@ void ControllerNavigation::ControllerUpdateStick() { } switch (controller_type) { - case Core::HID::NpadStyleIndex::ProController: + case Core::HID::NpadStyleIndex::Fullkey: case Core::HID::NpadStyleIndex::JoyconDual: case Core::HID::NpadStyleIndex::Handheld: case Core::HID::NpadStyleIndex::GameCube: -- cgit v1.2.3 From cdeaca73c460fa4a85d2f7d493828711f90e8747 Mon Sep 17 00:00:00 2001 From: t895 Date: Thu, 11 Jan 2024 21:53:11 -0500 Subject: android: Move ktlintCheck to yuzu-verify --- .ci/scripts/format/script.sh | 3 +++ .github/workflows/verify.yml | 8 +++++--- src/android/app/build.gradle.kts | 9 ++++++++- 3 files changed, 16 insertions(+), 4 deletions(-) (limited to 'src/android') diff --git a/.ci/scripts/format/script.sh b/.ci/scripts/format/script.sh index c22398de0..f9c63dbfa 100755 --- a/.ci/scripts/format/script.sh +++ b/.ci/scripts/format/script.sh @@ -32,3 +32,6 @@ if [ ! -z "$DIFF" ]; then echo "$DIFF" exit 1 fi + +cd src/android +./gradlew ktlintCheck diff --git a/.github/workflows/verify.yml b/.github/workflows/verify.yml index c073f3f3f..62eb69aeb 100644 --- a/.github/workflows/verify.yml +++ b/.github/workflows/verify.yml @@ -13,13 +13,15 @@ jobs: format: name: 'verify format' runs-on: ubuntu-latest - container: - image: yuzuemu/build-environments:linux-clang-format - options: -u 1001 steps: - uses: actions/checkout@v3 with: submodules: false + - name: set up JDK 17 + uses: actions/setup-java@v3 + with: + java-version: '17' + distribution: 'temurin' - name: 'Verify Formatting' run: bash -ex ./.ci/scripts/format/script.sh build: diff --git a/src/android/app/build.gradle.kts b/src/android/app/build.gradle.kts index 53aafa08c..d62254dd3 100644 --- a/src/android/app/build.gradle.kts +++ b/src/android/app/build.gradle.kts @@ -188,8 +188,15 @@ tasks.create("ktlintReset") { delete(File(buildDir.path + File.separator + "intermediates/ktLint")) } +val showFormatHelp = { + logger.lifecycle( + "If this check fails, please try running \"gradlew ktlintFormat\" for automatic " + + "codestyle fixes" + ) +} +tasks.getByPath("ktlintKotlinScriptCheck").doFirst { showFormatHelp.invoke() } +tasks.getByPath("ktlintMainSourceSetCheck").doFirst { showFormatHelp.invoke() } tasks.getByPath("loadKtlintReporters").dependsOn("ktlintReset") -tasks.getByPath("preBuild").dependsOn("ktlintCheck") ktlint { version.set("0.47.1") -- cgit v1.2.3 From 15d8a405296a0c1902cab1320d0894feb3129a2d Mon Sep 17 00:00:00 2001 From: t895 Date: Sat, 13 Jan 2024 18:06:33 -0500 Subject: android: Clean up git commands in build.gradle --- src/android/app/build.gradle.kts | 74 ++++++++++------------------------------ 1 file changed, 18 insertions(+), 56 deletions(-) (limited to 'src/android') diff --git a/src/android/app/build.gradle.kts b/src/android/app/build.gradle.kts index 53aafa08c..7318338fe 100644 --- a/src/android/app/build.gradle.kts +++ b/src/android/app/build.gradle.kts @@ -228,71 +228,33 @@ dependencies { implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.0") } -fun getGitVersion(): String { - var versionName = "0.0" - - try { - versionName = ProcessBuilder("git", "describe", "--always", "--long") +fun runGitCommand(command: List): String { + return try { + ProcessBuilder(command) .directory(project.rootDir) .redirectOutput(ProcessBuilder.Redirect.PIPE) .redirectError(ProcessBuilder.Redirect.PIPE) .start().inputStream.bufferedReader().use { it.readText() } .trim() - .replace(Regex("(-0)?-[^-]+$"), "") } catch (e: Exception) { - logger.error("Cannot find git, defaulting to dummy version number") - } - - if (System.getenv("GITHUB_ACTIONS") != null) { - val gitTag = System.getenv("GIT_TAG_NAME") - versionName = gitTag ?: versionName + logger.error("Cannot find git") + "" } - - return versionName } -fun getGitHash(): String { - try { - val processBuilder = ProcessBuilder("git", "rev-parse", "--short", "HEAD") - processBuilder.directory(project.rootDir) - val process = processBuilder.start() - val inputStream = process.inputStream - val errorStream = process.errorStream - process.waitFor() - - return if (process.exitValue() == 0) { - inputStream.bufferedReader() - .use { it.readText().trim() } // return the value of gitHash - } else { - val errorMessage = errorStream.bufferedReader().use { it.readText().trim() } - logger.error("Error running git command: $errorMessage") - "dummy-hash" // return a dummy hash value in case of an error - } - } catch (e: Exception) { - logger.error("$e: Cannot find git, defaulting to dummy build hash") - return "dummy-hash" // return a dummy hash value in case of an error +fun getGitVersion(): String { + val versionName = if (System.getenv("GITHUB_ACTIONS") != null) { + val gitTag = System.getenv("GIT_TAG_NAME") ?: "" + gitTag + } else { + runGitCommand(listOf("git", "describe", "--always", "--long")) + .replace(Regex("(-0)?-[^-]+$"), "") } + return versionName.ifEmpty { "0.0" } } -fun getBranch(): String { - try { - val processBuilder = ProcessBuilder("git", "rev-parse", "--abbrev-ref", "HEAD") - processBuilder.directory(project.rootDir) - val process = processBuilder.start() - val inputStream = process.inputStream - val errorStream = process.errorStream - process.waitFor() - - return if (process.exitValue() == 0) { - inputStream.bufferedReader() - .use { it.readText().trim() } // return the value of gitHash - } else { - val errorMessage = errorStream.bufferedReader().use { it.readText().trim() } - logger.error("Error running git command: $errorMessage") - "dummy-hash" // return a dummy hash value in case of an error - } - } catch (e: Exception) { - logger.error("$e: Cannot find git, defaulting to dummy build hash") - return "dummy-hash" // return a dummy hash value in case of an error - } -} +fun getGitHash(): String = + runGitCommand(listOf("git", "rev-parse", "--short", "HEAD")).ifEmpty { "dummy-hash" } + +fun getBranch(): String = + runGitCommand(listOf("git", "rev-parse", "--abbrev-ref", "HEAD")).ifEmpty { "dummy-hash" } -- cgit v1.2.3 From 7b3941e5d47915590f477b7900fcbd1759168295 Mon Sep 17 00:00:00 2001 From: t895 Date: Sat, 13 Jan 2024 18:12:19 -0500 Subject: android: Show version name instead of git hash in the about fragment --- .../app/src/main/java/org/yuzu/yuzu_emu/fragments/AboutFragment.kt | 4 ++-- src/android/app/src/main/res/layout-w600dp/fragment_about.xml | 4 ++-- src/android/app/src/main/res/layout/fragment_about.xml | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) (limited to 'src/android') diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AboutFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AboutFragment.kt index a1620fbb7..5b5f800c1 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AboutFragment.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AboutFragment.kt @@ -76,8 +76,8 @@ class AboutFragment : Fragment() { binding.root.findNavController().navigate(R.id.action_aboutFragment_to_licensesFragment) } - binding.textBuildHash.text = BuildConfig.GIT_HASH - binding.buttonBuildHash.setOnClickListener { + binding.textVersionName.text = BuildConfig.VERSION_NAME + binding.textVersionName.setOnClickListener { val clipBoard = requireContext().getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager val clip = ClipData.newPlainText(getString(R.string.build), BuildConfig.GIT_HASH) diff --git a/src/android/app/src/main/res/layout-w600dp/fragment_about.xml b/src/android/app/src/main/res/layout-w600dp/fragment_about.xml index a26ffbc73..655e49219 100644 --- a/src/android/app/src/main/res/layout-w600dp/fragment_about.xml +++ b/src/android/app/src/main/res/layout-w600dp/fragment_about.xml @@ -147,7 +147,7 @@ android:layout_marginHorizontal="20dp" /> Date: Mon, 15 Jan 2024 12:41:49 -0500 Subject: android: Fix overlay toggle ordering --- src/android/app/src/main/res/values/arrays.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/android') diff --git a/src/android/app/src/main/res/values/arrays.xml b/src/android/app/src/main/res/values/arrays.xml index 0363ff3b6..78e855bde 100644 --- a/src/android/app/src/main/res/values/arrays.xml +++ b/src/android/app/src/main/res/values/arrays.xml @@ -228,10 +228,10 @@ R ZL ZR - @string/gamepad_left_stick - @string/gamepad_right_stick L3 R3 + @string/gamepad_left_stick + @string/gamepad_right_stick @string/gamepad_d_pad -- cgit v1.2.3