Compare commits

..

9 Commits

Author SHA1 Message Date
f58de25587 Android 252 2024-02-17 03:50:28 +00:00
434a5c8685 Merge yuzu-emu#13034 2024-02-17 03:50:28 +00:00
28919e2687 Merge yuzu-emu#13026 2024-02-17 03:50:28 +00:00
8dea5adc59 Merge yuzu-emu#13017 2024-02-17 03:50:28 +00:00
c52637925f Merge yuzu-emu#13006 2024-02-17 03:50:28 +00:00
87585c765d Merge yuzu-emu#13000 2024-02-17 03:50:28 +00:00
638ac4b834 Merge yuzu-emu#12749 2024-02-17 03:50:28 +00:00
b80685993c Merge yuzu-emu#12461 2024-02-17 03:50:28 +00:00
4ebf892607 Merge yuzu-emu#10529 2024-02-17 03:50:28 +00:00
418 changed files with 9280 additions and 11945 deletions

View File

@ -1,14 +1,13 @@
| Pull Request | Commit | Title | Author | Merged? |
|----|----|----|----|----|
| [12461](https://github.com/yuzu-emu/yuzu//pull/12461) | [`2831f5dc6`](https://github.com/yuzu-emu/yuzu//pull/12461/files) | Rework Nvdec and VIC to fix out-of-order videos, and speed up decoding. | [Kelebek1](https://github.com/Kelebek1/) | Yes |
| [10529](https://github.com/yuzu-emu/yuzu//pull/10529) | [`368bf2211`](https://github.com/yuzu-emu/yuzu//pull/10529/files) | caches: make critical reclamation less eager and possible in more cases | [liamwhite](https://github.com/liamwhite/) | Yes |
| [12461](https://github.com/yuzu-emu/yuzu//pull/12461) | [`acc26667b`](https://github.com/yuzu-emu/yuzu//pull/12461/files) | Rework Nvdec and VIC to fix out-of-order videos, and speed up decoding. | [Kelebek1](https://github.com/Kelebek1/) | Yes |
| [12749](https://github.com/yuzu-emu/yuzu//pull/12749) | [`aad4b0d6f`](https://github.com/yuzu-emu/yuzu//pull/12749/files) | general: workarounds for SMMU syncing issues | [liamwhite](https://github.com/liamwhite/) | Yes |
| [13073](https://github.com/yuzu-emu/yuzu//pull/13073) | [`b5a17b501`](https://github.com/yuzu-emu/yuzu//pull/13073/files) | fsp: Migrate remaining interfaces to cmif serialization | [FearlessTobi](https://github.com/FearlessTobi/) | Yes |
| [13081](https://github.com/yuzu-emu/yuzu//pull/13081) | [`197c4d325`](https://github.com/yuzu-emu/yuzu//pull/13081/files) | aoc: Migrate to use cmif serialization | [FearlessTobi](https://github.com/FearlessTobi/) | Yes |
| [13096](https://github.com/yuzu-emu/yuzu//pull/13096) | [`0a8759057`](https://github.com/yuzu-emu/yuzu//pull/13096/files) | texture_cache: use two-pass collection for costly load resources | [liamwhite](https://github.com/liamwhite/) | Yes |
| [13100](https://github.com/yuzu-emu/yuzu//pull/13100) | [`c04567fad`](https://github.com/yuzu-emu/yuzu//pull/13100/files) | audio: move to new ipc | [liamwhite](https://github.com/liamwhite/) | Yes |
| [13115](https://github.com/yuzu-emu/yuzu//pull/13115) | [`89c2fd3d2`](https://github.com/yuzu-emu/yuzu//pull/13115/files) | olsc, pctl: move to new ipc | [liamwhite](https://github.com/liamwhite/) | Yes |
| [13116](https://github.com/yuzu-emu/yuzu//pull/13116) | [`54ed837ad`](https://github.com/yuzu-emu/yuzu//pull/13116/files) | android: Flip AB/XY for 8Bitdo controllers | [t895](https://github.com/t895/) | Yes |
| [13117](https://github.com/yuzu-emu/yuzu//pull/13117) | [`e85466c1a`](https://github.com/yuzu-emu/yuzu//pull/13117/files) | psc: stub overlay notification channel | [liamwhite](https://github.com/liamwhite/) | Yes |
| [13000](https://github.com/yuzu-emu/yuzu//pull/13000) | [`461eaca7e`](https://github.com/yuzu-emu/yuzu//pull/13000/files) | device_memory_manager: skip unregistered interfaces on invalidate | [liamwhite](https://github.com/liamwhite/) | Yes |
| [13006](https://github.com/yuzu-emu/yuzu//pull/13006) | [`3067bfd12`](https://github.com/yuzu-emu/yuzu//pull/13006/files) | buffer_cache: use mapped range with large vertex buffer size | [liamwhite](https://github.com/liamwhite/) | Yes |
| [13017](https://github.com/yuzu-emu/yuzu//pull/13017) | [`af4248256`](https://github.com/yuzu-emu/yuzu//pull/13017/files) | kernel: add and enable system suspend type | [liamwhite](https://github.com/liamwhite/) | Yes |
| [13026](https://github.com/yuzu-emu/yuzu//pull/13026) | [`462ea921e`](https://github.com/yuzu-emu/yuzu//pull/13026/files) | shader_recompiler: fix non-const offset for arrayed image types | [liamwhite](https://github.com/liamwhite/) | Yes |
| [13034](https://github.com/yuzu-emu/yuzu//pull/13034) | [`c4500677a`](https://github.com/yuzu-emu/yuzu//pull/13034/files) | android: Input mapping | [t895](https://github.com/t895/) | Yes |
End of merge log. You can find the original README.md below the break.

View File

@ -121,7 +121,6 @@ else()
-Wno-attributes
-Wno-invalid-offsetof
-Wno-unused-parameter
-Wno-missing-field-initializers
)
if (CMAKE_CXX_COMPILER_ID MATCHES Clang) # Clang or AppleClang

View File

@ -3,21 +3,24 @@
package org.yuzu.yuzu_emu
import android.app.Dialog
import android.content.DialogInterface
import android.net.Uri
import android.os.Bundle
import android.text.Html
import android.text.method.LinkMovementMethod
import android.view.Surface
import android.view.View
import android.widget.TextView
import androidx.annotation.Keep
import androidx.fragment.app.DialogFragment
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import java.lang.ref.WeakReference
import org.yuzu.yuzu_emu.activities.EmulationActivity
import org.yuzu.yuzu_emu.fragments.CoreErrorDialogFragment
import org.yuzu.yuzu_emu.utils.DocumentsTree
import org.yuzu.yuzu_emu.utils.FileUtil
import org.yuzu.yuzu_emu.utils.Log
import org.yuzu.yuzu_emu.utils.SerializableHelper.serializable
import org.yuzu.yuzu_emu.model.InstallResult
import org.yuzu.yuzu_emu.model.Patch
import org.yuzu.yuzu_emu.model.GameVerificationResult
@ -181,13 +184,46 @@ object NativeLibrary {
ErrorUnknown
}
var coreErrorAlertResult = false
val coreErrorAlertLock = Object()
private var coreErrorAlertResult = false
private val coreErrorAlertLock = Object()
class CoreErrorDialogFragment : DialogFragment() {
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
val title = requireArguments().serializable<String>("title")
val message = requireArguments().serializable<String>("message")
return MaterialAlertDialogBuilder(requireActivity())
.setTitle(title)
.setMessage(message)
.setPositiveButton(R.string.continue_button, null)
.setNegativeButton(R.string.abort_button) { _: DialogInterface?, _: Int ->
coreErrorAlertResult = false
synchronized(coreErrorAlertLock) { coreErrorAlertLock.notify() }
}
.create()
}
override fun onDismiss(dialog: DialogInterface) {
coreErrorAlertResult = true
synchronized(coreErrorAlertLock) { coreErrorAlertLock.notify() }
}
companion object {
fun newInstance(title: String?, message: String?): CoreErrorDialogFragment {
val frag = CoreErrorDialogFragment()
val args = Bundle()
args.putString("title", title)
args.putString("message", message)
frag.arguments = args
return frag
}
}
}
private fun onCoreErrorImpl(title: String, message: String) {
val emulationActivity = sEmulationActivity.get()
if (emulationActivity == null) {
Log.error("[NativeLibrary] EmulationActivity not present")
error("[NativeLibrary] EmulationActivity not present")
return
}
@ -203,7 +239,7 @@ object NativeLibrary {
fun onCoreError(error: CoreError?, details: String): Boolean {
val emulationActivity = sEmulationActivity.get()
if (emulationActivity == null) {
Log.error("[NativeLibrary] EmulationActivity not present")
error("[NativeLibrary] EmulationActivity not present")
return false
}
@ -234,7 +270,7 @@ object NativeLibrary {
}
// Show the AlertDialog on the main thread.
emulationActivity.runOnUiThread { onCoreErrorImpl(title, message) }
emulationActivity.runOnUiThread(Runnable { onCoreErrorImpl(title, message) })
// Wait for the lock to notify that it is complete.
synchronized(coreErrorAlertLock) { coreErrorAlertLock.wait() }

View File

@ -80,14 +80,8 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
super.onCreate(savedInstanceState)
InputHandler.updateControllerData()
val players = NativeConfig.getInputSettings(true)
var hasConfiguredControllers = false
players.forEach {
if (it.hasMapping()) {
hasConfiguredControllers = true
}
}
if (!hasConfiguredControllers && InputHandler.androidControllers.isNotEmpty()) {
val playerOne = NativeConfig.getInputSettings(true)[0]
if (!playerOne.hasMapping() && InputHandler.androidControllers.isNotEmpty()) {
var params: ParamPackage? = null
for (controller in InputHandler.registeredControllers) {
if (controller.get("port", -1) == 0) {
@ -173,7 +167,6 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
super.onResume()
nfcReader.startScanning()
startMotionSensorListener()
InputHandler.updateControllerData()
buildPictureInPictureParams()
}
@ -198,7 +191,6 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
super.onNewIntent(intent)
setIntent(intent)
nfcReader.onNewIntent(intent)
InputHandler.updateControllerData()
}
override fun dispatchKeyEvent(event: KeyEvent): Boolean {

View File

@ -3,15 +3,15 @@
package org.yuzu.yuzu_emu.adapters
import android.text.TextUtils
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.databinding.CardDriverOptionBinding
import org.yuzu.yuzu_emu.features.settings.model.StringSetting
import org.yuzu.yuzu_emu.model.Driver
import org.yuzu.yuzu_emu.model.DriverViewModel
import org.yuzu.yuzu_emu.utils.ViewUtils.marquee
import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder
class DriverAdapter(private val driverViewModel: DriverViewModel) :
@ -44,15 +44,25 @@ class DriverAdapter(private val driverViewModel: DriverViewModel) :
}
// Delay marquee by 3s
title.marquee()
version.marquee()
description.marquee()
title.postDelayed(
{
title.isSelected = true
title.ellipsize = TextUtils.TruncateAt.MARQUEE
version.isSelected = true
version.ellipsize = TextUtils.TruncateAt.MARQUEE
description.isSelected = true
description.ellipsize = TextUtils.TruncateAt.MARQUEE
},
3000
)
title.text = model.title
version.text = model.version
description.text = model.description
buttonDelete.setVisible(
model.title != binding.root.context.getString(R.string.system_gpu_driver)
)
if (model.title != binding.root.context.getString(R.string.system_gpu_driver)) {
buttonDelete.visibility = View.VISIBLE
} else {
buttonDelete.visibility = View.GONE
}
}
}
}

View File

@ -4,6 +4,7 @@
package org.yuzu.yuzu_emu.adapters
import android.net.Uri
import android.text.TextUtils
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.fragment.app.FragmentActivity
@ -11,7 +12,6 @@ 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.utils.ViewUtils.marquee
import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder
class FolderAdapter(val activity: FragmentActivity, val gamesViewModel: GamesViewModel) :
@ -29,7 +29,13 @@ class FolderAdapter(val activity: FragmentActivity, val gamesViewModel: GamesVie
override fun bind(model: GameDir) {
binding.apply {
path.text = Uri.parse(model.uriString).path
path.marquee()
path.postDelayed(
{
path.isSelected = true
path.ellipsize = TextUtils.TruncateAt.MARQUEE
},
3000
)
buttonEdit.setOnClickListener {
GameFolderPropertiesDialogFragment.newInstance(model)

View File

@ -4,6 +4,7 @@
package org.yuzu.yuzu_emu.adapters
import android.net.Uri
import android.text.TextUtils
import android.view.LayoutInflater
import android.view.ViewGroup
import android.widget.ImageView
@ -26,7 +27,6 @@ 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.utils.ViewUtils.marquee
import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder
class GameAdapter(private val activity: AppCompatActivity) :
@ -44,7 +44,14 @@ class GameAdapter(private val activity: AppCompatActivity) :
binding.textGameTitle.text = model.title.replace("[\\t\\n\\r]+".toRegex(), " ")
binding.textGameTitle.marquee()
binding.textGameTitle.postDelayed(
{
binding.textGameTitle.ellipsize = TextUtils.TruncateAt.MARQUEE
binding.textGameTitle.isSelected = true
},
3000
)
binding.cardGame.setOnClickListener { onClick(model) }
binding.cardGame.setOnLongClickListener { onLongClick(model) }
}

View File

@ -3,18 +3,21 @@
package org.yuzu.yuzu_emu.adapters
import android.text.TextUtils
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.core.content.res.ResourcesCompat
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
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.utils.ViewUtils.marquee
import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
import org.yuzu.yuzu_emu.utils.collect
import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder
class GamePropertiesAdapter(
@ -73,15 +76,23 @@ class GamePropertiesAdapter(
)
)
binding.details.marquee()
binding.details.postDelayed({
binding.details.isSelected = true
binding.details.ellipsize = TextUtils.TruncateAt.MARQUEE
}, 3000)
if (submenuProperty.details != null) {
binding.details.setVisible(true)
binding.details.visibility = View.VISIBLE
binding.details.text = submenuProperty.details.invoke()
} else if (submenuProperty.detailsFlow != null) {
binding.details.setVisible(true)
submenuProperty.detailsFlow.collect(viewLifecycle) { binding.details.text = it }
binding.details.visibility = View.VISIBLE
viewLifecycle.lifecycleScope.launch {
viewLifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
submenuProperty.detailsFlow.collect { binding.details.text = it }
}
}
} else {
binding.details.setVisible(false)
binding.details.visibility = View.GONE
}
}
}
@ -101,10 +112,14 @@ class GamePropertiesAdapter(
)
)
binding.buttonInstall.setVisible(installableProperty.install != null)
binding.buttonInstall.setOnClickListener { installableProperty.install?.invoke() }
binding.buttonExport.setVisible(installableProperty.export != null)
binding.buttonExport.setOnClickListener { installableProperty.export?.invoke() }
if (installableProperty.install != null) {
binding.buttonInstall.visibility = View.VISIBLE
binding.buttonInstall.setOnClickListener { installableProperty.install.invoke() }
}
if (installableProperty.export != null) {
binding.buttonExport.visibility = View.VISIBLE
binding.buttonExport.setOnClickListener { installableProperty.export.invoke() }
}
}
}

View File

@ -3,19 +3,22 @@
package org.yuzu.yuzu_emu.adapters
import android.text.TextUtils
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat
import androidx.core.content.res.ResourcesCompat
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
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.utils.ViewUtils.marquee
import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
import org.yuzu.yuzu_emu.utils.collect
import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder
class HomeSettingAdapter(
@ -56,8 +59,18 @@ class HomeSettingAdapter(
binding.optionIcon.alpha = 0.5f
}
model.details.collect(viewLifecycle) { updateOptionDetails(it) }
binding.optionDetail.marquee()
viewLifecycle.lifecycleScope.launch {
viewLifecycle.repeatOnLifecycle(Lifecycle.State.CREATED) {
model.details.collect { updateOptionDetails(it) }
}
}
binding.optionDetail.postDelayed(
{
binding.optionDetail.ellipsize = TextUtils.TruncateAt.MARQUEE
binding.optionDetail.isSelected = true
},
3000
)
binding.root.setOnClickListener { onClick(model) }
}
@ -77,7 +90,7 @@ class HomeSettingAdapter(
private fun updateOptionDetails(detailString: String) {
if (detailString.isNotEmpty()) {
binding.optionDetail.text = detailString
binding.optionDetail.setVisible(true)
binding.optionDetail.visibility = View.VISIBLE
}
}
}

View File

@ -4,10 +4,10 @@
package org.yuzu.yuzu_emu.adapters
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import org.yuzu.yuzu_emu.databinding.CardInstallableBinding
import org.yuzu.yuzu_emu.model.Installable
import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder
class InstallableAdapter(installables: List<Installable>) :
@ -26,10 +26,14 @@ class InstallableAdapter(installables: List<Installable>) :
binding.title.setText(model.titleId)
binding.description.setText(model.descriptionId)
binding.buttonInstall.setVisible(model.install != null)
binding.buttonInstall.setOnClickListener { model.install?.invoke() }
binding.buttonExport.setVisible(model.export != null)
binding.buttonExport.setOnClickListener { model.export?.invoke() }
if (model.install != null) {
binding.buttonInstall.visibility = View.VISIBLE
binding.buttonInstall.setOnClickListener { model.install.invoke() }
}
if (model.export != null) {
binding.buttonExport.visibility = View.VISIBLE
binding.buttonExport.setOnClickListener { model.export.invoke() }
}
}
}
}

View File

@ -4,12 +4,12 @@
package org.yuzu.yuzu_emu.adapters
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.app.AppCompatActivity
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.utils.ViewUtils.setVisible
import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder
class LicenseAdapter(private val activity: AppCompatActivity, licenses: List<License>) :
@ -25,7 +25,7 @@ class LicenseAdapter(private val activity: AppCompatActivity, licenses: List<Lic
binding.apply {
textSettingName.text = root.context.getString(model.titleId)
textSettingDescription.text = root.context.getString(model.descriptionId)
textSettingValue.setVisible(false)
textSettingValue.visibility = View.GONE
root.setOnClickListener { onClick(model) }
}

View File

@ -5,6 +5,7 @@ package org.yuzu.yuzu_emu.adapters
import android.text.Html
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.res.ResourcesCompat
@ -16,7 +17,6 @@ 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.utils.ViewUtils.setVisible
import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder
class SetupAdapter(val activity: AppCompatActivity, pages: List<SetupPage>) :
@ -30,8 +30,8 @@ class SetupAdapter(val activity: AppCompatActivity, pages: List<SetupPage>) :
AbstractViewHolder<SetupPage>(binding), SetupCallback {
override fun bind(model: SetupPage) {
if (model.stepCompleted.invoke() == StepState.COMPLETE) {
binding.buttonAction.setVisible(visible = false, gone = false)
binding.textConfirmation.setVisible(true)
binding.buttonAction.visibility = View.INVISIBLE
binding.textConfirmation.visibility = View.VISIBLE
}
binding.icon.setImageDrawable(

View File

@ -57,7 +57,7 @@ object NativeInput {
/**
* Handles axis movement events.
* @param guid 32 character hexadecimal string consisting of the controller's PID+VID.
* @param port Port determined by controller connection order.
* @param port Port determined by controller connection order
* @param axis The axis ID.
* @param value Value along the given axis.
*/
@ -265,6 +265,11 @@ object NativeInput {
*/
external fun stopMapping()
/**
* Removes the loaded input profiles from memory.
*/
external fun unloadInputProfiles()
/**
* Updates a controller's mappings with auto-mapping params.
* @param playerIndex Index of the player to auto-map.

View File

@ -64,17 +64,17 @@ data class PlayerInput(
fun hasMapping(): Boolean {
var hasMapping = false
buttons.forEach {
if (it != "[empty]" && it.isNotEmpty()) {
if (it != "[empty]") {
hasMapping = true
}
}
analogs.forEach {
if (it != "[empty]" && it.isNotEmpty()) {
if (it != "[empty]") {
hasMapping = true
}
}
motions.forEach {
if (it != "[empty]" && it.isNotEmpty()) {
if (it != "[empty]") {
hasMapping = true
}
}

View File

@ -6,8 +6,7 @@ package org.yuzu.yuzu_emu.features.settings.model
import org.yuzu.yuzu_emu.utils.NativeConfig
enum class StringSetting(override val key: String) : AbstractStringSetting {
DRIVER_PATH("driver_path"),
DEVICE_NAME("device_name");
DRIVER_PATH("driver_path");
override fun getString(needsGlobal: Boolean): String = NativeConfig.getString(key, needsGlobal)

View File

@ -16,7 +16,6 @@ import org.yuzu.yuzu_emu.features.settings.model.ByteSetting
import org.yuzu.yuzu_emu.features.settings.model.IntSetting
import org.yuzu.yuzu_emu.features.settings.model.LongSetting
import org.yuzu.yuzu_emu.features.settings.model.ShortSetting
import org.yuzu.yuzu_emu.features.settings.model.StringSetting
import org.yuzu.yuzu_emu.utils.NativeConfig
/**
@ -76,9 +75,6 @@ abstract class SettingsItem(
get() = NativeLibrary.isRunning() && !setting.global &&
!NativeConfig.isPerGameConfigLoaded()
val clearable: Boolean
get() = !setting.global && NativeConfig.isPerGameConfigLoaded()
companion object {
const val TYPE_HEADER = 0
const val TYPE_SWITCH = 1
@ -91,7 +87,6 @@ abstract class SettingsItem(
const val TYPE_INPUT = 8
const val TYPE_INT_SINGLE_CHOICE = 9
const val TYPE_INPUT_PROFILE = 10
const val TYPE_STRING_INPUT = 11
const val FASTMEM_COMBINED = "fastmem_combined"
@ -110,7 +105,6 @@ abstract class SettingsItem(
// List of all general
val settingsItems = HashMap<String, SettingsItem>().apply {
put(StringInputSetting(StringSetting.DEVICE_NAME, titleId = R.string.device_name))
put(
SwitchSetting(
BooleanSetting.RENDERER_USE_SPEED_LIMIT,

View File

@ -1,22 +0,0 @@
// SPDX-FileCopyrightText: 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
package org.yuzu.yuzu_emu.features.settings.model.view
import androidx.annotation.StringRes
import org.yuzu.yuzu_emu.features.settings.model.AbstractStringSetting
class StringInputSetting(
setting: AbstractStringSetting,
@StringRes titleId: Int = 0,
titleString: String = "",
@StringRes descriptionId: Int = 0,
descriptionString: String = ""
) : SettingsItem(setting, titleId, titleString, descriptionId, descriptionString) {
override val type = TYPE_STRING_INPUT
fun getSelectedValue(needsGlobal: Boolean = false) = setting.getValueAsString(needsGlobal)
fun setSelectedValue(selection: String) =
(setting as AbstractStringSetting).setString(selection)
}

View File

@ -11,13 +11,16 @@ import android.view.ViewGroup
import android.widget.Toast
import androidx.fragment.app.DialogFragment
import androidx.fragment.app.activityViewModels
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import androidx.recyclerview.widget.LinearLayoutManager
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import kotlinx.coroutines.launch
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.databinding.DialogInputProfilesBinding
import org.yuzu.yuzu_emu.features.settings.model.view.InputProfileSetting
import org.yuzu.yuzu_emu.fragments.MessageDialogFragment
import org.yuzu.yuzu_emu.utils.collect
class InputProfileDialogFragment : DialogFragment() {
private var position = 0
@ -107,21 +110,25 @@ class InputProfileDialogFragment : DialogFragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
settingsViewModel.shouldShowDeleteProfileDialog.collect(viewLifecycleOwner) {
if (it.isNotEmpty()) {
MessageDialogFragment.newInstance(
activity = requireActivity(),
titleId = R.string.delete_input_profile,
descriptionId = R.string.delete_input_profile_description,
positiveAction = {
setting.deleteProfile(it)
settingsViewModel.setReloadListAndNotifyDataset(true)
},
negativeAction = {},
negativeButtonTitleId = android.R.string.cancel
).show(parentFragmentManager, MessageDialogFragment.TAG)
settingsViewModel.setShouldShowDeleteProfileDialog("")
dismiss()
viewLifecycleOwner.lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.CREATED) {
settingsViewModel.shouldShowDeleteProfileDialog.collect {
if (it.isNotEmpty()) {
MessageDialogFragment.newInstance(
activity = requireActivity(),
titleId = R.string.delete_input_profile,
descriptionId = R.string.delete_input_profile_description,
positiveAction = {
setting.deleteProfile(it)
settingsViewModel.setReloadListAndNotifyDataset(true)
},
negativeAction = {},
negativeButtonTitleId = android.R.string.cancel
).show(parentFragmentManager, MessageDialogFragment.TAG)
settingsViewModel.setShouldShowDeleteProfileDialog("")
dismiss()
}
}
}
}
}

View File

@ -13,9 +13,14 @@ import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.ViewCompat
import androidx.core.view.WindowCompat
import androidx.core.view.WindowInsetsCompat
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import androidx.navigation.fragment.NavHostFragment
import androidx.navigation.navArgs
import com.google.android.material.color.MaterialColors
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch
import org.yuzu.yuzu_emu.NativeLibrary
import java.io.IOException
import org.yuzu.yuzu_emu.R
@ -65,23 +70,39 @@ class SettingsActivity : AppCompatActivity() {
)
}
settingsViewModel.shouldRecreate.collect(
this,
resetState = { settingsViewModel.setShouldRecreate(false) }
) { if (it) recreate() }
settingsViewModel.shouldNavigateBack.collect(
this,
resetState = { settingsViewModel.setShouldNavigateBack(false) }
) { if (it) navigateBack() }
settingsViewModel.shouldShowResetSettingsDialog.collect(
this,
resetState = { settingsViewModel.setShouldShowResetSettingsDialog(false) }
) {
if (it) {
ResetSettingsDialogFragment().show(
supportFragmentManager,
ResetSettingsDialogFragment.TAG
)
lifecycleScope.apply {
launch {
repeatOnLifecycle(Lifecycle.State.CREATED) {
settingsViewModel.shouldRecreate.collectLatest {
if (it) {
settingsViewModel.setShouldRecreate(false)
recreate()
}
}
}
}
launch {
repeatOnLifecycle(Lifecycle.State.CREATED) {
settingsViewModel.shouldNavigateBack.collectLatest {
if (it) {
settingsViewModel.setShouldNavigateBack(false)
navigateBack()
}
}
}
}
launch {
repeatOnLifecycle(Lifecycle.State.CREATED) {
settingsViewModel.shouldShowResetSettingsDialog.collectLatest {
if (it) {
settingsViewModel.setShouldShowResetSettingsDialog(false)
ResetSettingsDialogFragment().show(
supportFragmentManager,
ResetSettingsDialogFragment.TAG
)
}
}
}
}
}
@ -116,6 +137,7 @@ class SettingsActivity : AppCompatActivity() {
super.onStop()
Log.info("[SettingsActivity] Settings activity stopping. Saving settings to INI...")
if (isFinishing) {
NativeInput.unloadInputProfiles()
NativeInput.reloadInputDevices()
NativeLibrary.applySettings()
if (args.game == null) {

View File

@ -85,10 +85,6 @@ class SettingsAdapter(
InputProfileViewHolder(ListItemSettingBinding.inflate(inflater), this)
}
SettingsItem.TYPE_STRING_INPUT -> {
StringInputViewHolder(ListItemSettingBinding.inflate(inflater), this)
}
else -> {
HeaderViewHolder(ListItemSettingsHeaderBinding.inflate(inflater), this)
}
@ -396,15 +392,6 @@ class SettingsAdapter(
popup.show()
}
fun onStringInputClick(item: StringInputSetting, position: Int) {
SettingsDialogFragment.newInstance(
settingsViewModel,
item,
SettingsItem.TYPE_STRING_INPUT,
position
).show(fragment.childFragmentManager, SettingsDialogFragment.TAG)
}
fun onLongClick(item: SettingsItem, position: Int): Boolean {
SettingsDialogFragment.newInstance(
settingsViewModel,

View File

@ -11,10 +11,13 @@ import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.DialogFragment
import androidx.fragment.app.activityViewModels
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.slider.Slider
import kotlinx.coroutines.launch
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.databinding.DialogEditTextBinding
import org.yuzu.yuzu_emu.databinding.DialogSliderBinding
import org.yuzu.yuzu_emu.features.input.NativeInput
import org.yuzu.yuzu_emu.features.input.model.AnalogDirection
@ -24,10 +27,8 @@ import org.yuzu.yuzu_emu.features.settings.model.view.IntSingleChoiceSetting
import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem
import org.yuzu.yuzu_emu.features.settings.model.view.SingleChoiceSetting
import org.yuzu.yuzu_emu.features.settings.model.view.SliderSetting
import org.yuzu.yuzu_emu.features.settings.model.view.StringInputSetting
import org.yuzu.yuzu_emu.features.settings.model.view.StringSingleChoiceSetting
import org.yuzu.yuzu_emu.utils.ParamPackage
import org.yuzu.yuzu_emu.utils.collect
class SettingsDialogFragment : DialogFragment(), DialogInterface.OnClickListener {
private var type = 0
@ -39,7 +40,6 @@ class SettingsDialogFragment : DialogFragment(), DialogInterface.OnClickListener
private val settingsViewModel: SettingsViewModel by activityViewModels()
private lateinit var sliderBinding: DialogSliderBinding
private lateinit var stringInputBinding: DialogEditTextBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@ -134,18 +134,6 @@ class SettingsDialogFragment : DialogFragment(), DialogInterface.OnClickListener
.create()
}
SettingsItem.TYPE_STRING_INPUT -> {
stringInputBinding = DialogEditTextBinding.inflate(layoutInflater)
val item = settingsViewModel.clickedItem as StringInputSetting
stringInputBinding.editText.setText(item.getSelectedValue())
MaterialAlertDialogBuilder(requireContext())
.setTitle(item.title)
.setView(stringInputBinding.root)
.setPositiveButton(android.R.string.ok, this)
.setNegativeButton(android.R.string.cancel, defaultCancelListener)
.create()
}
SettingsItem.TYPE_STRING_SINGLE_CHOICE -> {
val item = settingsViewModel.clickedItem as StringSingleChoiceSetting
MaterialAlertDialogBuilder(requireContext())
@ -173,7 +161,6 @@ class SettingsDialogFragment : DialogFragment(), DialogInterface.OnClickListener
): View? {
return when (type) {
SettingsItem.TYPE_SLIDER -> sliderBinding.root
SettingsItem.TYPE_STRING_INPUT -> stringInputBinding.root
else -> super.onCreateView(inflater, container, savedInstanceState)
}
}
@ -182,11 +169,17 @@ class SettingsDialogFragment : DialogFragment(), DialogInterface.OnClickListener
super.onViewCreated(view, savedInstanceState)
when (type) {
SettingsItem.TYPE_SLIDER -> {
settingsViewModel.sliderTextValue.collect(viewLifecycleOwner) {
sliderBinding.textValue.text = it
}
settingsViewModel.sliderProgress.collect(viewLifecycleOwner) {
sliderBinding.slider.value = it.toFloat()
viewLifecycleOwner.lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.CREATED) {
settingsViewModel.sliderTextValue.collect {
sliderBinding.textValue.text = it
}
}
repeatOnLifecycle(Lifecycle.State.CREATED) {
settingsViewModel.sliderProgress.collect {
sliderBinding.slider.value = it.toFloat()
}
}
}
}
}
@ -216,13 +209,6 @@ class SettingsDialogFragment : DialogFragment(), DialogInterface.OnClickListener
val sliderSetting = settingsViewModel.clickedItem as SliderSetting
sliderSetting.setSelectedValue(settingsViewModel.sliderProgress.value)
}
is StringInputSetting -> {
val stringInputSetting = settingsViewModel.clickedItem as StringInputSetting
stringInputSetting.setSelectedValue(
(stringInputBinding.editText.text ?: "").toString()
)
}
}
closeDialog()
}

View File

@ -13,17 +13,21 @@ 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.LinearLayoutManager
import com.google.android.material.transition.MaterialSharedAxis
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.databinding.FragmentSettingsBinding
import org.yuzu.yuzu_emu.features.input.NativeInput
import org.yuzu.yuzu_emu.features.settings.model.Settings
import org.yuzu.yuzu_emu.fragments.MessageDialogFragment
import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
import org.yuzu.yuzu_emu.utils.collect
class SettingsFragment : Fragment() {
private lateinit var presenter: SettingsFragmentPresenter
@ -59,7 +63,8 @@ class SettingsFragment : Fragment() {
return binding.root
}
@SuppressLint("NotifyDataSetChanged")
// This is using the correct scope, lint is just acting up
@SuppressLint("UnsafeRepeatOnLifecycleDetector", "NotifyDataSetChanged")
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
settingsAdapter = SettingsAdapter(this, requireContext())
@ -95,37 +100,65 @@ class SettingsFragment : Fragment() {
settingsViewModel.setShouldNavigateBack(true)
}
settingsViewModel.shouldReloadSettingsList.collect(
viewLifecycleOwner,
resetState = { settingsViewModel.setShouldReloadSettingsList(false) }
) { if (it) presenter.loadSettingsList() }
settingsViewModel.adapterItemChanged.collect(
viewLifecycleOwner,
resetState = { settingsViewModel.setAdapterItemChanged(-1) }
) { if (it != -1) settingsAdapter?.notifyItemChanged(it) }
settingsViewModel.datasetChanged.collect(
viewLifecycleOwner,
resetState = { settingsViewModel.setDatasetChanged(false) }
) { if (it) settingsAdapter?.notifyDataSetChanged() }
settingsViewModel.reloadListAndNotifyDataset.collect(
viewLifecycleOwner,
resetState = { settingsViewModel.setReloadListAndNotifyDataset(false) }
) { if (it) presenter.loadSettingsList(true) }
settingsViewModel.shouldShowResetInputDialog.collect(
viewLifecycleOwner,
resetState = { settingsViewModel.setShouldShowResetInputDialog(false) }
) {
if (it) {
MessageDialogFragment.newInstance(
activity = requireActivity(),
titleId = R.string.reset_mapping,
descriptionId = R.string.reset_mapping_description,
positiveAction = {
NativeInput.resetControllerMappings(getPlayerIndex())
settingsViewModel.setReloadListAndNotifyDataset(true)
},
negativeAction = {}
).show(parentFragmentManager, MessageDialogFragment.TAG)
viewLifecycleOwner.lifecycleScope.apply {
launch {
repeatOnLifecycle(Lifecycle.State.CREATED) {
settingsViewModel.shouldReloadSettingsList.collectLatest {
if (it) {
settingsViewModel.setShouldReloadSettingsList(false)
presenter.loadSettingsList()
}
}
}
}
launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
settingsViewModel.adapterItemChanged.collect {
if (it != -1) {
settingsAdapter?.notifyItemChanged(it)
settingsViewModel.setAdapterItemChanged(-1)
}
}
}
}
launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
settingsViewModel.datasetChanged.collect {
if (it) {
settingsAdapter?.notifyDataSetChanged()
settingsViewModel.setDatasetChanged(false)
}
}
}
}
launch {
repeatOnLifecycle(Lifecycle.State.CREATED) {
settingsViewModel.reloadListAndNotifyDataset.collectLatest {
if (it) {
settingsViewModel.setReloadListAndNotifyDataset(false)
presenter.loadSettingsList(true)
}
}
}
}
launch {
repeatOnLifecycle(Lifecycle.State.CREATED) {
settingsViewModel.shouldShowResetInputDialog.collectLatest {
if (it) {
MessageDialogFragment.newInstance(
activity = requireActivity(),
titleId = R.string.reset_mapping,
descriptionId = R.string.reset_mapping_description,
positiveAction = {
NativeInput.resetControllerMappings(getPlayerIndex())
settingsViewModel.setReloadListAndNotifyDataset(true)
},
negativeAction = {}
).show(parentFragmentManager, MessageDialogFragment.TAG)
settingsViewModel.setShouldShowResetInputDialog(false)
}
}
}
}
}

View File

@ -5,7 +5,6 @@ package org.yuzu.yuzu_emu.features.settings.ui
import android.annotation.SuppressLint
import android.os.Build
import android.widget.Toast
import org.yuzu.yuzu_emu.NativeLibrary
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.YuzuApplication
@ -23,7 +22,6 @@ import org.yuzu.yuzu_emu.features.settings.model.LongSetting
import org.yuzu.yuzu_emu.features.settings.model.Settings
import org.yuzu.yuzu_emu.features.settings.model.Settings.MenuTag
import org.yuzu.yuzu_emu.features.settings.model.ShortSetting
import org.yuzu.yuzu_emu.features.settings.model.StringSetting
import org.yuzu.yuzu_emu.features.settings.model.view.*
import org.yuzu.yuzu_emu.utils.InputHandler
import org.yuzu.yuzu_emu.utils.NativeConfig
@ -154,7 +152,6 @@ class SettingsFragmentPresenter(
private fun addSystemSettings(sl: ArrayList<SettingsItem>) {
sl.apply {
add(StringSetting.DEVICE_NAME.key)
add(BooleanSetting.RENDERER_USE_SPEED_LIMIT.key)
add(ShortSetting.RENDERER_SPEED_LIMIT.key)
add(BooleanSetting.USE_DOCKED_MODE.key)
@ -385,67 +382,22 @@ class SettingsFragmentPresenter(
)
)
InputHandler.updateControllerData()
val autoMappingSetting = object : AbstractIntSetting {
override val key = "auto_mapping_device"
override fun getInt(needsGlobal: Boolean): Int = -1
override fun setInt(value: Int) {
val registeredController = InputHandler.registeredControllers[value + 1]
val displayName = registeredController.get(
"display",
context.getString(R.string.unknown)
)
NativeInput.updateMappingsWithDefault(
playerIndex,
registeredController,
displayName
)
Toast.makeText(
context,
context.getString(R.string.attempted_auto_map, displayName),
Toast.LENGTH_SHORT
).show()
settingsViewModel.setReloadListAndNotifyDataset(true)
}
override val defaultValue = -1
override fun getValueAsString(needsGlobal: Boolean) = getInt().toString()
override fun reset() = setInt(defaultValue)
override val isRuntimeModifiable: Boolean = true
}
val unknownString = context.getString(R.string.unknown)
val prettyAutoMappingControllerList = InputHandler.registeredControllers.mapNotNull {
val port = it.get("port", -1)
return@mapNotNull if (port == 100 || port == -1) {
null
} else {
it.get("display", unknownString)
}
}.toTypedArray()
add(
IntSingleChoiceSetting(
autoMappingSetting,
titleId = R.string.auto_map,
descriptionId = R.string.auto_map_description,
choices = prettyAutoMappingControllerList,
values = IntArray(prettyAutoMappingControllerList.size) { it }.toTypedArray()
)
)
val mappingFilterSetting = object : AbstractIntSetting {
override val key = "mapping_filter"
val devicesSetting = object : AbstractIntSetting {
override val key = "input_device"
override fun getInt(needsGlobal: Boolean): Int = settingsViewModel.currentDevice
override fun setInt(value: Int) {
if (value != 0) {
val registeredController = InputHandler.registeredControllers[value]
NativeInput.updateMappingsWithDefault(
playerIndex,
registeredController,
registeredController.get("display", context.getString(R.string.unknown))
)
}
settingsViewModel.currentDevice = value
settingsViewModel.setReloadListAndNotifyDataset(true)
}
override val defaultValue = 0
@ -457,6 +409,8 @@ class SettingsFragmentPresenter(
override val isRuntimeModifiable: Boolean = true
}
InputHandler.updateControllerData()
val unknownString = context.getString(R.string.unknown)
val prettyControllerList = InputHandler.registeredControllers.mapNotNull {
return@mapNotNull if (it.get("port", 0) == 100) {
null
@ -466,9 +420,9 @@ class SettingsFragmentPresenter(
}.toTypedArray()
add(
IntSingleChoiceSetting(
mappingFilterSetting,
titleId = R.string.input_mapping_filter,
descriptionId = R.string.input_mapping_filter_description,
devicesSetting,
titleId = R.string.input_device,
descriptionId = R.string.input_device_description,
choices = prettyControllerList,
values = IntArray(prettyControllerList.size) { it }.toTypedArray()
)
@ -780,7 +734,7 @@ class SettingsFragmentPresenter(
playerIndex: Int,
paramName: String,
stick: NativeAnalog,
defaultValue: Float
defaultValue: Int
): AbstractIntSetting =
object : AbstractIntSetting {
val params get() = NativeInput.getStickParam(playerIndex, stick)
@ -788,7 +742,7 @@ class SettingsFragmentPresenter(
override val key = ""
override fun getInt(needsGlobal: Boolean): Int =
(params.get(paramName, defaultValue) * 100).toInt()
(params.get(paramName, 0.15f) * 100).toInt()
override fun setInt(value: Int) {
val tempParams = params
@ -796,12 +750,12 @@ class SettingsFragmentPresenter(
NativeInput.setStickParam(playerIndex, stick, tempParams)
}
override val defaultValue = (defaultValue * 100).toInt()
override val defaultValue = defaultValue
override fun getValueAsString(needsGlobal: Boolean): String =
getInt(needsGlobal).toString()
override fun reset() = setInt(this.defaultValue)
override fun reset() = setInt(defaultValue)
}
private fun getExtraStickSettings(
@ -811,11 +765,11 @@ class SettingsFragmentPresenter(
val stickIsController =
NativeInput.isController(NativeInput.getStickParam(playerIndex, nativeAnalog))
val modifierRangeSetting =
getStickIntSettingFromParam(playerIndex, "modifier_scale", nativeAnalog, 0.5f)
getStickIntSettingFromParam(playerIndex, "modifier_scale", nativeAnalog, 50)
val stickRangeSetting =
getStickIntSettingFromParam(playerIndex, "range", nativeAnalog, 0.95f)
getStickIntSettingFromParam(playerIndex, "range", nativeAnalog, 95)
val stickDeadzoneSetting =
getStickIntSettingFromParam(playerIndex, "deadzone", nativeAnalog, 0.15f)
getStickIntSettingFromParam(playerIndex, "deadzone", nativeAnalog, 15)
val out = mutableListOf<SettingsItem>().apply {
if (stickIsController) {

View File

@ -15,17 +15,19 @@ import androidx.core.view.updatePadding
import androidx.core.widget.doOnTextChanged
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import androidx.recyclerview.widget.LinearLayoutManager
import com.google.android.material.divider.MaterialDividerItemDecoration
import com.google.android.material.transition.MaterialSharedAxis
import info.debatty.java.stringsimilarity.Cosine
import kotlinx.coroutines.launch
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.databinding.FragmentSettingsSearchBinding
import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem
import org.yuzu.yuzu_emu.utils.NativeConfig
import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
import org.yuzu.yuzu_emu.utils.collect
class SettingsSearchFragment : Fragment() {
private var _binding: FragmentSettingsSearchBinding? = null
@ -81,10 +83,14 @@ class SettingsSearchFragment : Fragment() {
search()
binding.settingsList.smoothScrollToPosition(0)
}
settingsViewModel.shouldReloadSettingsList.collect(viewLifecycleOwner) {
if (it) {
settingsViewModel.setShouldReloadSettingsList(false)
search()
viewLifecycleOwner.lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.CREATED) {
settingsViewModel.shouldReloadSettingsList.collect {
if (it) {
settingsViewModel.setShouldReloadSettingsList(false)
search()
}
}
}
}
@ -100,9 +106,10 @@ class SettingsSearchFragment : Fragment() {
private fun search() {
val searchTerm = binding.searchText.text.toString().lowercase()
binding.clearButton.setVisible(visible = searchTerm.isNotEmpty(), gone = false)
binding.clearButton.visibility =
if (searchTerm.isEmpty()) View.INVISIBLE else View.VISIBLE
if (searchTerm.isEmpty()) {
binding.noResultsView.setVisible(visible = false, gone = false)
binding.noResultsView.visibility = View.VISIBLE
settingsAdapter?.submitList(emptyList())
return
}
@ -129,7 +136,8 @@ class SettingsSearchFragment : Fragment() {
optionalSetting
}
settingsAdapter?.submitList(sortedList)
binding.noResultsView.setVisible(visible = sortedList.isEmpty(), gone = false)
binding.noResultsView.visibility =
if (sortedList.isEmpty()) View.VISIBLE else View.INVISIBLE
}
private fun focusSearch() {

View File

@ -13,7 +13,7 @@ import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding
import org.yuzu.yuzu_emu.features.settings.model.view.DateTimeSetting
import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem
import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter
import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
import org.yuzu.yuzu_emu.utils.NativeConfig
class DateTimeViewHolder(val binding: ListItemSettingBinding, adapter: SettingsAdapter) :
SettingViewHolder(binding.root, adapter) {
@ -22,16 +22,27 @@ class DateTimeViewHolder(val binding: ListItemSettingBinding, adapter: SettingsA
override fun bind(item: SettingsItem) {
setting = item as DateTimeSetting
binding.textSettingName.text = item.title
binding.textSettingDescription.setVisible(item.description.isNotEmpty())
binding.textSettingDescription.text = item.description
binding.textSettingValue.setVisible(true)
if (setting.description.isNotEmpty()) {
binding.textSettingDescription.text = item.description
binding.textSettingDescription.visibility = View.VISIBLE
} else {
binding.textSettingDescription.visibility = View.GONE
}
binding.textSettingValue.visibility = View.VISIBLE
val epochTime = setting.getValue()
val instant = Instant.ofEpochMilli(epochTime * 1000)
val zonedTime = ZonedDateTime.ofInstant(instant, ZoneId.of("UTC"))
val dateFormatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM)
binding.textSettingValue.text = dateFormatter.format(zonedTime)
binding.buttonClear.setVisible(setting.clearable)
binding.buttonClear.visibility = if (setting.setting.global ||
!NativeConfig.isPerGameConfigLoaded()
) {
View.GONE
} else {
View.VISIBLE
}
binding.buttonClear.setOnClickListener {
adapter.onClearClick(setting, bindingAdapterPosition)
}

View File

@ -9,7 +9,6 @@ import org.yuzu.yuzu_emu.features.settings.model.view.InputProfileSetting
import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem
import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
class InputProfileViewHolder(val binding: ListItemSettingBinding, adapter: SettingsAdapter) :
SettingViewHolder(binding.root, adapter) {
@ -21,10 +20,10 @@ class InputProfileViewHolder(val binding: ListItemSettingBinding, adapter: Setti
binding.textSettingValue.text =
setting.getCurrentProfile().ifEmpty { binding.root.context.getString(R.string.not_set) }
binding.textSettingDescription.setVisible(false)
binding.buttonClear.setVisible(false)
binding.icon.setVisible(false)
binding.buttonClear.setVisible(false)
binding.textSettingDescription.visibility = View.GONE
binding.buttonClear.visibility = View.GONE
binding.icon.visibility = View.GONE
binding.buttonClear.visibility = View.GONE
}
override fun onClick(clicked: View) =

View File

@ -12,7 +12,6 @@ import org.yuzu.yuzu_emu.features.settings.model.view.InputSetting
import org.yuzu.yuzu_emu.features.settings.model.view.ModifierInputSetting
import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem
import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter
import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
class InputViewHolder(val binding: ListItemSettingInputBinding, adapter: SettingsAdapter) :
SettingViewHolder(binding.root, adapter) {
@ -23,26 +22,38 @@ class InputViewHolder(val binding: ListItemSettingInputBinding, adapter: Setting
binding.textSettingName.text = setting.title
binding.textSettingValue.text = setting.getSelectedValue()
when (item) {
binding.buttonOptions.visibility = when (item) {
is AnalogInputSetting -> {
val param = NativeInput.getStickParam(item.playerIndex, item.nativeAnalog)
binding.buttonOptions.setVisible(
if (
param.get("engine", "") == "analog_from_button" ||
param.has("axis_x") || param.has("axis_y")
)
param.has("axis_x") || param.has("axis_y")
) {
View.VISIBLE
} else {
View.GONE
}
}
is ButtonInputSetting -> {
val param = NativeInput.getButtonParam(item.playerIndex, item.nativeButton)
binding.buttonOptions.setVisible(
if (
param.has("code") || param.has("button") || param.has("hat") ||
param.has("axis")
)
param.has("axis")
) {
View.VISIBLE
} else {
View.GONE
}
}
is ModifierInputSetting -> {
val params = NativeInput.getStickParam(item.playerIndex, item.nativeAnalog)
binding.buttonOptions.setVisible(params.has("modifier"))
if (params.has("modifier")) {
View.VISIBLE
} else {
View.GONE
}
}
}

View File

@ -9,7 +9,6 @@ import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding
import org.yuzu.yuzu_emu.features.settings.model.view.RunnableSetting
import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem
import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter
import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
class RunnableViewHolder(val binding: ListItemSettingBinding, adapter: SettingsAdapter) :
SettingViewHolder(binding.root, adapter) {
@ -17,8 +16,8 @@ class RunnableViewHolder(val binding: ListItemSettingBinding, adapter: SettingsA
override fun bind(item: SettingsItem) {
setting = item as RunnableSetting
binding.icon.setVisible(setting.iconId != 0)
if (setting.iconId != 0) {
binding.icon.visibility = View.VISIBLE
binding.icon.setImageDrawable(
ResourcesCompat.getDrawable(
binding.icon.resources,
@ -26,13 +25,19 @@ class RunnableViewHolder(val binding: ListItemSettingBinding, adapter: SettingsA
binding.icon.context.theme
)
)
} else {
binding.icon.visibility = View.GONE
}
binding.textSettingName.text = setting.title
binding.textSettingDescription.setVisible(setting.description.isNotEmpty())
binding.textSettingDescription.text = item.description
binding.textSettingValue.setVisible(false)
binding.buttonClear.setVisible(false)
if (setting.description.isNotEmpty()) {
binding.textSettingDescription.setText(item.descriptionId)
binding.textSettingDescription.visibility = View.VISIBLE
} else {
binding.textSettingDescription.visibility = View.GONE
}
binding.textSettingValue.visibility = View.GONE
binding.buttonClear.visibility = View.GONE
setStyle(setting.isEditable, binding)
}

View File

@ -10,7 +10,7 @@ import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem
import org.yuzu.yuzu_emu.features.settings.model.view.SingleChoiceSetting
import org.yuzu.yuzu_emu.features.settings.model.view.StringSingleChoiceSetting
import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter
import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
import org.yuzu.yuzu_emu.utils.NativeConfig
class SingleChoiceViewHolder(val binding: ListItemSettingBinding, adapter: SettingsAdapter) :
SettingViewHolder(binding.root, adapter) {
@ -19,10 +19,14 @@ class SingleChoiceViewHolder(val binding: ListItemSettingBinding, adapter: Setti
override fun bind(item: SettingsItem) {
setting = item
binding.textSettingName.text = setting.title
binding.textSettingDescription.setVisible(item.description.isNotEmpty())
binding.textSettingDescription.text = item.description
if (item.description.isNotEmpty()) {
binding.textSettingDescription.text = item.description
binding.textSettingDescription.visibility = View.VISIBLE
} else {
binding.textSettingDescription.visibility = View.GONE
}
binding.textSettingValue.setVisible(true)
binding.textSettingValue.visibility = View.VISIBLE
when (item) {
is SingleChoiceSetting -> {
val resMgr = binding.textSettingValue.context.resources
@ -43,11 +47,14 @@ class SingleChoiceViewHolder(val binding: ListItemSettingBinding, adapter: Setti
binding.textSettingValue.text = item.getChoiceAt(item.getSelectedValue())
}
}
if (binding.textSettingValue.text.isEmpty()) {
binding.textSettingValue.setVisible(false)
}
binding.buttonClear.setVisible(setting.clearable)
binding.buttonClear.visibility = if (setting.setting.global ||
!NativeConfig.isPerGameConfigLoaded()
) {
View.GONE
} else {
View.VISIBLE
}
binding.buttonClear.setOnClickListener {
adapter.onClearClick(setting, bindingAdapterPosition)
}

View File

@ -9,7 +9,7 @@ import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding
import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem
import org.yuzu.yuzu_emu.features.settings.model.view.SliderSetting
import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter
import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
import org.yuzu.yuzu_emu.utils.NativeConfig
class SliderViewHolder(val binding: ListItemSettingBinding, adapter: SettingsAdapter) :
SettingViewHolder(binding.root, adapter) {
@ -18,16 +18,26 @@ class SliderViewHolder(val binding: ListItemSettingBinding, adapter: SettingsAda
override fun bind(item: SettingsItem) {
setting = item as SliderSetting
binding.textSettingName.text = setting.title
binding.textSettingDescription.setVisible(item.description.isNotEmpty())
binding.textSettingDescription.text = setting.description
binding.textSettingValue.setVisible(true)
if (item.description.isNotEmpty()) {
binding.textSettingDescription.text = setting.description
binding.textSettingDescription.visibility = View.VISIBLE
} else {
binding.textSettingDescription.visibility = View.GONE
}
binding.textSettingValue.visibility = View.VISIBLE
binding.textSettingValue.text = String.format(
binding.textSettingValue.context.getString(R.string.value_with_units),
setting.getSelectedValue(),
setting.units
)
binding.buttonClear.setVisible(setting.clearable)
binding.buttonClear.visibility = if (setting.setting.global ||
!NativeConfig.isPerGameConfigLoaded()
) {
View.GONE
} else {
View.VISIBLE
}
binding.buttonClear.setOnClickListener {
adapter.onClearClick(setting, bindingAdapterPosition)
}

View File

@ -1,45 +0,0 @@
// SPDX-FileCopyrightText: 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
package org.yuzu.yuzu_emu.features.settings.ui.viewholder
import android.view.View
import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding
import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem
import org.yuzu.yuzu_emu.features.settings.model.view.StringInputSetting
import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter
import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
class StringInputViewHolder(val binding: ListItemSettingBinding, adapter: SettingsAdapter) :
SettingViewHolder(binding.root, adapter) {
private lateinit var setting: StringInputSetting
override fun bind(item: SettingsItem) {
setting = item as StringInputSetting
binding.textSettingName.text = setting.title
binding.textSettingDescription.setVisible(setting.description.isNotEmpty())
binding.textSettingDescription.text = setting.description
binding.textSettingValue.setVisible(true)
binding.textSettingValue.text = setting.getSelectedValue()
binding.buttonClear.setVisible(setting.clearable)
binding.buttonClear.setOnClickListener {
adapter.onClearClick(setting, bindingAdapterPosition)
}
setStyle(setting.isEditable, binding)
}
override fun onClick(clicked: View) {
if (setting.isEditable) {
adapter.onStringInputClick(setting, bindingAdapterPosition)
}
}
override fun onLongClick(clicked: View): Boolean {
if (setting.isEditable) {
return adapter.onLongClick(setting, bindingAdapterPosition)
}
return false
}
}

View File

@ -9,7 +9,6 @@ import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding
import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem
import org.yuzu.yuzu_emu.features.settings.model.view.SubmenuSetting
import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter
import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
class SubmenuViewHolder(val binding: ListItemSettingBinding, adapter: SettingsAdapter) :
SettingViewHolder(binding.root, adapter) {
@ -17,8 +16,8 @@ class SubmenuViewHolder(val binding: ListItemSettingBinding, adapter: SettingsAd
override fun bind(item: SettingsItem) {
setting = item as SubmenuSetting
binding.icon.setVisible(setting.iconId != 0)
if (setting.iconId != 0) {
binding.icon.visibility = View.VISIBLE
binding.icon.setImageDrawable(
ResourcesCompat.getDrawable(
binding.icon.resources,
@ -26,13 +25,19 @@ class SubmenuViewHolder(val binding: ListItemSettingBinding, adapter: SettingsAd
binding.icon.context.theme
)
)
} else {
binding.icon.visibility = View.GONE
}
binding.textSettingName.text = setting.title
binding.textSettingDescription.setVisible(setting.description.isNotEmpty())
binding.textSettingDescription.text = setting.description
binding.textSettingValue.setVisible(false)
binding.buttonClear.setVisible(false)
if (setting.description.isNotEmpty()) {
binding.textSettingDescription.text = setting.description
binding.textSettingDescription.visibility = View.VISIBLE
} else {
binding.textSettingDescription.visibility = View.GONE
}
binding.textSettingValue.visibility = View.GONE
binding.buttonClear.visibility = View.GONE
}
override fun onClick(clicked: View) {

View File

@ -9,7 +9,7 @@ import org.yuzu.yuzu_emu.databinding.ListItemSettingSwitchBinding
import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem
import org.yuzu.yuzu_emu.features.settings.model.view.SwitchSetting
import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter
import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
import org.yuzu.yuzu_emu.utils.NativeConfig
class SwitchSettingViewHolder(val binding: ListItemSettingSwitchBinding, adapter: SettingsAdapter) :
SettingViewHolder(binding.root, adapter) {
@ -19,8 +19,12 @@ class SwitchSettingViewHolder(val binding: ListItemSettingSwitchBinding, adapter
override fun bind(item: SettingsItem) {
setting = item as SwitchSetting
binding.textSettingName.text = setting.title
binding.textSettingDescription.setVisible(setting.description.isNotEmpty())
binding.textSettingDescription.text = setting.description
if (setting.description.isNotEmpty()) {
binding.textSettingDescription.text = setting.description
binding.textSettingDescription.visibility = View.VISIBLE
} else {
binding.textSettingDescription.visibility = View.GONE
}
binding.switchWidget.setOnCheckedChangeListener(null)
binding.switchWidget.isChecked = setting.getIsChecked(setting.needsRuntimeGlobal)
@ -28,7 +32,13 @@ class SwitchSettingViewHolder(val binding: ListItemSettingSwitchBinding, adapter
adapter.onBooleanClick(setting, binding.switchWidget.isChecked, bindingAdapterPosition)
}
binding.buttonClear.setVisible(setting.clearable)
binding.buttonClear.visibility = if (setting.setting.global ||
!NativeConfig.isPerGameConfigLoaded()
) {
View.GONE
} else {
View.VISIBLE
}
binding.buttonClear.setOnClickListener {
adapter.onClearClick(setting, bindingAdapterPosition)
}

View File

@ -3,6 +3,7 @@
package org.yuzu.yuzu_emu.fragments
import android.annotation.SuppressLint
import android.content.Intent
import android.os.Bundle
import android.view.LayoutInflater
@ -15,6 +16,9 @@ import androidx.core.view.updatePadding
import androidx.documentfile.provider.DocumentFile
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.LinearLayoutManager
@ -28,7 +32,6 @@ import org.yuzu.yuzu_emu.model.HomeViewModel
import org.yuzu.yuzu_emu.utils.AddonUtil
import org.yuzu.yuzu_emu.utils.FileUtil.copyFilesTo
import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
import org.yuzu.yuzu_emu.utils.collect
import java.io.File
class AddonsFragment : Fragment() {
@ -57,6 +60,8 @@ class AddonsFragment : 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 = false)
@ -73,41 +78,57 @@ class AddonsFragment : Fragment() {
adapter = AddonAdapter(addonViewModel)
}
addonViewModel.addonList.collect(viewLifecycleOwner) {
(binding.listAddons.adapter as AddonAdapter).submitList(it)
}
addonViewModel.showModInstallPicker.collect(
viewLifecycleOwner,
resetState = { addonViewModel.showModInstallPicker(false) }
) { if (it) installAddon.launch(Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).data) }
addonViewModel.showModNoticeDialog.collect(
viewLifecycleOwner,
resetState = { addonViewModel.showModNoticeDialog(false) }
) {
if (it) {
MessageDialogFragment.newInstance(
requireActivity(),
titleId = R.string.addon_notice,
descriptionId = R.string.addon_notice_description,
dismissible = false,
positiveAction = { addonViewModel.showModInstallPicker(true) },
negativeAction = {},
negativeButtonTitleId = R.string.close
).show(parentFragmentManager, MessageDialogFragment.TAG)
viewLifecycleOwner.lifecycleScope.apply {
launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
addonViewModel.addonList.collect {
(binding.listAddons.adapter as AddonAdapter).submitList(it)
}
}
}
}
addonViewModel.addonToDelete.collect(
viewLifecycleOwner,
resetState = { addonViewModel.setAddonToDelete(null) }
) {
if (it != null) {
MessageDialogFragment.newInstance(
requireActivity(),
titleId = R.string.confirm_uninstall,
descriptionId = R.string.confirm_uninstall_description,
positiveAction = { addonViewModel.onDeleteAddon(it) },
negativeAction = {}
).show(parentFragmentManager, MessageDialogFragment.TAG)
launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
addonViewModel.showModInstallPicker.collect {
if (it) {
installAddon.launch(Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).data)
addonViewModel.showModInstallPicker(false)
}
}
}
}
launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
addonViewModel.showModNoticeDialog.collect {
if (it) {
MessageDialogFragment.newInstance(
requireActivity(),
titleId = R.string.addon_notice,
descriptionId = R.string.addon_notice_description,
dismissible = false,
positiveAction = { addonViewModel.showModInstallPicker(true) },
negativeAction = {},
negativeButtonTitleId = R.string.close
).show(parentFragmentManager, MessageDialogFragment.TAG)
addonViewModel.showModNoticeDialog(false)
}
}
}
}
launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
addonViewModel.addonToDelete.collect {
if (it != null) {
MessageDialogFragment.newInstance(
requireActivity(),
titleId = R.string.confirm_uninstall,
descriptionId = R.string.confirm_uninstall_description,
positiveAction = { addonViewModel.onDeleteAddon(it) },
negativeAction = {}
).show(parentFragmentManager, MessageDialogFragment.TAG)
addonViewModel.setAddonToDelete(null)
}
}
}
}
}

View File

@ -1,47 +0,0 @@
// SPDX-FileCopyrightText: 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
package org.yuzu.yuzu_emu.fragments
import android.app.Dialog
import android.content.DialogInterface
import android.os.Bundle
import androidx.fragment.app.DialogFragment
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import org.yuzu.yuzu_emu.NativeLibrary
import org.yuzu.yuzu_emu.R
class CoreErrorDialogFragment : DialogFragment() {
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog =
MaterialAlertDialogBuilder(requireActivity())
.setTitle(requireArguments().getString(TITLE))
.setMessage(requireArguments().getString(MESSAGE))
.setPositiveButton(R.string.continue_button, null)
.setNegativeButton(R.string.abort_button) { _: DialogInterface?, _: Int ->
NativeLibrary.coreErrorAlertResult = false
synchronized(NativeLibrary.coreErrorAlertLock) {
NativeLibrary.coreErrorAlertLock.notify()
}
}
.create()
override fun onDismiss(dialog: DialogInterface) {
super.onDismiss(dialog)
NativeLibrary.coreErrorAlertResult = true
synchronized(NativeLibrary.coreErrorAlertLock) { NativeLibrary.coreErrorAlertLock.notify() }
}
companion object {
const val TITLE = "Title"
const val MESSAGE = "Message"
fun newInstance(title: String, message: String): CoreErrorDialogFragment {
val frag = CoreErrorDialogFragment()
val args = Bundle()
args.putString(TITLE, title)
args.putString(MESSAGE, message)
frag.arguments = args
return frag
}
}
}

View File

@ -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,6 +14,9 @@ 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
@ -31,7 +35,6 @@ import org.yuzu.yuzu_emu.utils.FileUtil
import org.yuzu.yuzu_emu.utils.GpuDriverHelper
import org.yuzu.yuzu_emu.utils.NativeConfig
import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
import org.yuzu.yuzu_emu.utils.collect
import java.io.File
import java.io.IOException
@ -60,6 +63,8 @@ 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)
@ -84,8 +89,15 @@ class DriverManagerFragment : Fragment() {
}
}
driverViewModel.showClearButton.collect(viewLifecycleOwner) {
binding.toolbarDrivers.menu.findItem(R.id.menu_driver_use_global).isVisible = it
viewLifecycleOwner.lifecycleScope.apply {
launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
driverViewModel.showClearButton.collect {
binding.toolbarDrivers.menu
.findItem(R.id.menu_driver_use_global).isVisible = it
}
}
}
}
}

View File

@ -10,11 +10,14 @@ import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.DialogFragment
import androidx.fragment.app.activityViewModels
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import kotlinx.coroutines.launch
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.databinding.DialogProgressBarBinding
import org.yuzu.yuzu_emu.model.DriverViewModel
import org.yuzu.yuzu_emu.utils.collect
class DriversLoadingDialogFragment : DialogFragment() {
private val driverViewModel: DriverViewModel by activityViewModels()
@ -41,7 +44,13 @@ class DriversLoadingDialogFragment : DialogFragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
driverViewModel.isInteractionAllowed.collect(viewLifecycleOwner) { if (it) dismiss() }
viewLifecycleOwner.lifecycleScope.apply {
launch {
repeatOnLifecycle(Lifecycle.State.RESUMED) {
driverViewModel.isInteractionAllowed.collect { if (it) dismiss() }
}
}
}
}
companion object {

View File

@ -32,6 +32,9 @@ import androidx.drawerlayout.widget.DrawerLayout
import androidx.drawerlayout.widget.DrawerLayout.DrawerListener
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.window.layout.FoldingFeature
@ -39,6 +42,9 @@ import androidx.window.layout.WindowInfoTracker
import androidx.window.layout.WindowLayoutInfo
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.slider.Slider
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch
import org.yuzu.yuzu_emu.HomeNavigationDirections
import org.yuzu.yuzu_emu.NativeLibrary
import org.yuzu.yuzu_emu.R
@ -57,7 +63,6 @@ import org.yuzu.yuzu_emu.model.EmulationViewModel
import org.yuzu.yuzu_emu.overlay.model.OverlayControl
import org.yuzu.yuzu_emu.overlay.model.OverlayLayout
import org.yuzu.yuzu_emu.utils.*
import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
import java.lang.NullPointerException
class EmulationFragment : Fragment(), SurfaceHolder.Callback {
@ -85,6 +90,14 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
if (context is EmulationActivity) {
emulationActivity = context
NativeLibrary.setEmulationActivity(context)
lifecycleScope.launch(Dispatchers.Main) {
lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
WindowInfoTracker.getOrCreate(context)
.windowLayoutInfo(context)
.collect { updateFoldableLayout(context, it) }
}
}
} else {
throw IllegalStateException("EmulationFragment must have EmulationActivity parent")
}
@ -155,6 +168,8 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
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)
if (requireActivity().isFinishing) {
@ -335,85 +350,128 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
binding.loadingTitle.isSelected = true
binding.loadingText.isSelected = true
WindowInfoTracker.getOrCreate(requireContext())
.windowLayoutInfo(requireActivity()).collect(viewLifecycleOwner) {
updateFoldableLayout(requireActivity() as EmulationActivity, it)
}
emulationViewModel.shaderProgress.collect(viewLifecycleOwner) {
if (it > 0 && it != emulationViewModel.totalShaders.value) {
binding.loadingProgressIndicator.isIndeterminate = false
if (it < binding.loadingProgressIndicator.max) {
binding.loadingProgressIndicator.progress = it
viewLifecycleOwner.lifecycleScope.apply {
launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
WindowInfoTracker.getOrCreate(requireContext())
.windowLayoutInfo(requireActivity())
.collect {
updateFoldableLayout(requireActivity() as EmulationActivity, it)
}
}
}
launch {
repeatOnLifecycle(Lifecycle.State.CREATED) {
emulationViewModel.shaderProgress.collectLatest {
if (it > 0 && it != emulationViewModel.totalShaders.value) {
binding.loadingProgressIndicator.isIndeterminate = false
if (it == emulationViewModel.totalShaders.value) {
binding.loadingText.setText(R.string.loading)
binding.loadingProgressIndicator.isIndeterminate = true
}
}
emulationViewModel.totalShaders.collect(viewLifecycleOwner) {
binding.loadingProgressIndicator.max = it
}
emulationViewModel.shaderMessage.collect(viewLifecycleOwner) {
if (it.isNotEmpty()) {
binding.loadingText.text = it
}
}
if (it < binding.loadingProgressIndicator.max) {
binding.loadingProgressIndicator.progress = it
}
}
emulationViewModel.emulationStarted.collect(viewLifecycleOwner) {
if (it) {
binding.drawerLayout.setDrawerLockMode(IntSetting.LOCK_DRAWER.getInt())
ViewUtils.showView(binding.surfaceInputOverlay)
ViewUtils.hideView(binding.loadingIndicator)
emulationState.updateSurface()
// Setup overlays
updateShowFpsOverlay()
updateThermalOverlay()
}
}
emulationViewModel.isEmulationStopping.collect(viewLifecycleOwner) {
if (it) {
binding.loadingText.setText(R.string.shutting_down)
ViewUtils.showView(binding.loadingIndicator)
ViewUtils.hideView(binding.inputContainer)
ViewUtils.hideView(binding.showFpsText)
}
}
emulationViewModel.drawerOpen.collect(viewLifecycleOwner) {
if (it) {
binding.drawerLayout.open()
binding.inGameMenu.requestFocus()
} else {
binding.drawerLayout.close()
}
}
emulationViewModel.programChanged.collect(viewLifecycleOwner) {
if (it != 0) {
emulationViewModel.setEmulationStarted(false)
binding.drawerLayout.close()
binding.drawerLayout
.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED)
ViewUtils.hideView(binding.surfaceInputOverlay)
ViewUtils.showView(binding.loadingIndicator)
}
}
emulationViewModel.emulationStopped.collect(viewLifecycleOwner) {
if (it && emulationViewModel.programChanged.value != -1) {
if (perfStatsUpdater != null) {
perfStatsUpdateHandler.removeCallbacks(perfStatsUpdater!!)
if (it == emulationViewModel.totalShaders.value) {
binding.loadingText.setText(R.string.loading)
binding.loadingProgressIndicator.isIndeterminate = true
}
}
}
emulationState.changeProgram(emulationViewModel.programChanged.value)
emulationViewModel.setProgramChanged(-1)
emulationViewModel.setEmulationStopped(false)
}
}
launch {
repeatOnLifecycle(Lifecycle.State.CREATED) {
emulationViewModel.totalShaders.collectLatest {
binding.loadingProgressIndicator.max = it
}
}
}
launch {
repeatOnLifecycle(Lifecycle.State.CREATED) {
emulationViewModel.shaderMessage.collectLatest {
if (it.isNotEmpty()) {
binding.loadingText.text = it
}
}
}
}
launch {
repeatOnLifecycle(Lifecycle.State.RESUMED) {
driverViewModel.isInteractionAllowed.collect {
if (it) {
startEmulation()
}
}
}
}
launch {
repeatOnLifecycle(Lifecycle.State.CREATED) {
emulationViewModel.emulationStarted.collectLatest {
if (it) {
binding.drawerLayout.setDrawerLockMode(IntSetting.LOCK_DRAWER.getInt())
ViewUtils.showView(binding.surfaceInputOverlay)
ViewUtils.hideView(binding.loadingIndicator)
driverViewModel.isInteractionAllowed.collect(viewLifecycleOwner) {
if (it) startEmulation()
emulationState.updateSurface()
// Setup overlays
updateShowFpsOverlay()
updateThermalOverlay()
}
}
}
}
launch {
repeatOnLifecycle(Lifecycle.State.CREATED) {
emulationViewModel.isEmulationStopping.collectLatest {
if (it) {
binding.loadingText.setText(R.string.shutting_down)
ViewUtils.showView(binding.loadingIndicator)
ViewUtils.hideView(binding.inputContainer)
ViewUtils.hideView(binding.showFpsText)
}
}
}
}
launch {
repeatOnLifecycle(Lifecycle.State.CREATED) {
emulationViewModel.drawerOpen.collect {
if (it) {
binding.drawerLayout.open()
binding.inGameMenu.requestFocus()
} else {
binding.drawerLayout.close()
}
}
}
}
launch {
repeatOnLifecycle(Lifecycle.State.CREATED) {
emulationViewModel.programChanged.collect {
if (it != 0) {
emulationViewModel.setEmulationStarted(false)
binding.drawerLayout.close()
binding.drawerLayout
.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED)
ViewUtils.hideView(binding.surfaceInputOverlay)
ViewUtils.showView(binding.loadingIndicator)
}
}
}
}
launch {
repeatOnLifecycle(Lifecycle.State.CREATED) {
emulationViewModel.emulationStopped.collect {
if (it && emulationViewModel.programChanged.value != -1) {
if (perfStatsUpdater != null) {
perfStatsUpdateHandler.removeCallbacks(perfStatsUpdater!!)
}
emulationState.changeProgram(emulationViewModel.programChanged.value)
emulationViewModel.setProgramChanged(-1)
emulationViewModel.setEmulationStopped(false)
}
}
}
}
}
}
@ -442,12 +500,14 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
binding.drawerLayout.close()
}
if (showInputOverlay) {
binding.surfaceInputOverlay.setVisible(visible = false, gone = false)
binding.surfaceInputOverlay.visibility = View.INVISIBLE
}
} else {
binding.surfaceInputOverlay.setVisible(
showInputOverlay && emulationViewModel.emulationStarted.value
)
if (showInputOverlay && emulationViewModel.emulationStarted.value) {
binding.surfaceInputOverlay.visibility = View.VISIBLE
} else {
binding.surfaceInputOverlay.visibility = View.INVISIBLE
}
if (!isInFoldableLayout) {
if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) {
binding.surfaceInputOverlay.layout = OverlayLayout.Portrait
@ -484,9 +544,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
}
private fun updateShowFpsOverlay() {
val showOverlay = BooleanSetting.SHOW_PERFORMANCE_OVERLAY.getBoolean()
binding.showFpsText.setVisible(showOverlay)
if (showOverlay) {
if (BooleanSetting.SHOW_PERFORMANCE_OVERLAY.getBoolean()) {
val SYSTEM_FPS = 0
val FPS = 1
val FRAMETIME = 2
@ -506,17 +564,17 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
}
}
perfStatsUpdateHandler.post(perfStatsUpdater!!)
binding.showFpsText.visibility = View.VISIBLE
} else {
if (perfStatsUpdater != null) {
perfStatsUpdateHandler.removeCallbacks(perfStatsUpdater!!)
}
binding.showFpsText.visibility = View.GONE
}
}
private fun updateThermalOverlay() {
val showOverlay = BooleanSetting.SHOW_THERMAL_OVERLAY.getBoolean()
binding.showThermalsText.setVisible(showOverlay)
if (showOverlay) {
if (BooleanSetting.SHOW_THERMAL_OVERLAY.getBoolean()) {
thermalStatsUpdater = {
if (emulationViewModel.emulationStarted.value &&
!emulationViewModel.isEmulationStopping.value
@ -538,10 +596,12 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
}
}
thermalStatsUpdateHandler.post(thermalStatsUpdater!!)
binding.showThermalsText.visibility = View.VISIBLE
} else {
if (thermalStatsUpdater != null) {
thermalStatsUpdateHandler.removeCallbacks(thermalStatsUpdater!!)
}
binding.showThermalsText.visibility = View.GONE
}
}
@ -810,12 +870,12 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
}
}
}
binding.doneControlConfig.setVisible(true)
binding.doneControlConfig.visibility = View.VISIBLE
binding.surfaceInputOverlay.setIsInEditMode(true)
}
private fun stopConfiguringControls() {
binding.doneControlConfig.setVisible(false)
binding.doneControlConfig.visibility = View.GONE
binding.surfaceInputOverlay.setIsInEditMode(false)
// Unlock the orientation if it was locked for editing
if (IntSetting.RENDERER_SCREEN_LAYOUT.getInt() == EmulationOrientation.Unspecified.int) {

View File

@ -13,6 +13,9 @@ 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.recyclerview.widget.GridLayoutManager
import com.google.android.material.transition.MaterialSharedAxis
@ -24,7 +27,6 @@ import org.yuzu.yuzu_emu.model.GamesViewModel
import org.yuzu.yuzu_emu.model.HomeViewModel
import org.yuzu.yuzu_emu.ui.main.MainActivity
import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
import org.yuzu.yuzu_emu.utils.collect
class GameFoldersFragment : Fragment() {
private var _binding: FragmentFoldersBinding? = null
@ -68,8 +70,12 @@ class GameFoldersFragment : Fragment() {
adapter = FolderAdapter(requireActivity(), gamesViewModel)
}
gamesViewModel.folders.collect(viewLifecycleOwner) {
(binding.listFolders.adapter as FolderAdapter).submitList(it)
viewLifecycleOwner.lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.CREATED) {
gamesViewModel.folders.collect {
(binding.listFolders.adapter as FolderAdapter).submitList(it)
}
}
}
val mainActivity = requireActivity() as MainActivity

View File

@ -27,7 +27,6 @@ import org.yuzu.yuzu_emu.databinding.FragmentGameInfoBinding
import org.yuzu.yuzu_emu.model.GameVerificationResult
import org.yuzu.yuzu_emu.model.HomeViewModel
import org.yuzu.yuzu_emu.utils.GameMetadata
import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
class GameInfoFragment : Fragment() {
@ -86,7 +85,7 @@ class GameInfoFragment : Fragment() {
copyToClipboard(getString(R.string.developer), args.game.developer)
}
} else {
developer.setVisible(false)
developer.visibility = View.GONE
}
version.setHint(R.string.version)

View File

@ -3,9 +3,11 @@
package org.yuzu.yuzu_emu.fragments
import android.annotation.SuppressLint
import android.content.pm.ShortcutInfo
import android.content.pm.ShortcutManager
import android.os.Bundle
import android.text.TextUtils
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
@ -16,7 +18,9 @@ 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
@ -42,9 +46,7 @@ import org.yuzu.yuzu_emu.utils.FileUtil
import org.yuzu.yuzu_emu.utils.GameIconUtils
import org.yuzu.yuzu_emu.utils.GpuDriverHelper
import org.yuzu.yuzu_emu.utils.MemoryUtil
import org.yuzu.yuzu_emu.utils.ViewUtils.marquee
import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
import org.yuzu.yuzu_emu.utils.collect
import java.io.BufferedOutputStream
import java.io.File
@ -74,6 +76,8 @@ class GamePropertiesFragment : 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)
@ -103,7 +107,13 @@ class GamePropertiesFragment : Fragment() {
GameIconUtils.loadGameIcon(args.game, binding.imageGameScreen)
binding.title.text = args.game.title
binding.title.marquee()
binding.title.postDelayed(
{
binding.title.ellipsize = TextUtils.TruncateAt.MARQUEE
binding.title.isSelected = true
},
3000
)
binding.buttonStart.setOnClickListener {
LaunchGameDialogFragment.newInstance(args.game)
@ -112,14 +122,28 @@ class GamePropertiesFragment : Fragment() {
reloadList()
homeViewModel.openImportSaves.collect(
viewLifecycleOwner,
resetState = { homeViewModel.setOpenImportSaves(false) }
) { if (it) importSaves.launch(arrayOf("application/zip")) }
homeViewModel.reloadPropertiesList.collect(
viewLifecycleOwner,
resetState = { homeViewModel.reloadPropertiesList(false) }
) { if (it) reloadList() }
viewLifecycleOwner.lifecycleScope.apply {
launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
homeViewModel.openImportSaves.collect {
if (it) {
importSaves.launch(arrayOf("application/zip"))
homeViewModel.setOpenImportSaves(false)
}
}
}
}
launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
homeViewModel.reloadPropertiesList.collect {
if (it) {
reloadList()
homeViewModel.reloadPropertiesList(false)
}
}
}
}
}
setInsets()
}

View File

@ -14,6 +14,9 @@ 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.recyclerview.widget.GridLayoutManager
import com.google.android.material.transition.MaterialSharedAxis
@ -32,7 +35,6 @@ import org.yuzu.yuzu_emu.ui.main.MainActivity
import org.yuzu.yuzu_emu.utils.DirectoryInitialization
import org.yuzu.yuzu_emu.utils.FileUtil
import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
import org.yuzu.yuzu_emu.utils.collect
import java.io.BufferedOutputStream
import java.io.File
import java.math.BigInteger
@ -73,10 +75,14 @@ class InstallableFragment : Fragment() {
binding.root.findNavController().popBackStack()
}
homeViewModel.openImportSaves.collect(viewLifecycleOwner) {
if (it) {
importSaves.launch(arrayOf("application/zip"))
homeViewModel.setOpenImportSaves(false)
viewLifecycleOwner.lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.CREATED) {
homeViewModel.openImportSaves.collect {
if (it) {
importSaves.launch(arrayOf("application/zip"))
homeViewModel.setOpenImportSaves(false)
}
}
}
}

View File

@ -13,13 +13,15 @@ import androidx.appcompat.app.AlertDialog
import androidx.fragment.app.DialogFragment
import androidx.fragment.app.FragmentActivity
import androidx.fragment.app.activityViewModels
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import kotlinx.coroutines.launch
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.databinding.DialogProgressBarBinding
import org.yuzu.yuzu_emu.model.TaskViewModel
import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
import org.yuzu.yuzu_emu.utils.collect
class ProgressDialogFragment : DialogFragment() {
private val taskViewModel: TaskViewModel by activityViewModels()
@ -62,49 +64,71 @@ class ProgressDialogFragment : DialogFragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.message.isSelected = true
taskViewModel.isComplete.collect(viewLifecycleOwner) {
if (it) {
dismiss()
when (val result = taskViewModel.result.value) {
is String -> Toast.makeText(
requireContext(),
result,
Toast.LENGTH_LONG
).show()
viewLifecycleOwner.lifecycleScope.apply {
launch {
repeatOnLifecycle(Lifecycle.State.CREATED) {
taskViewModel.isComplete.collect {
if (it) {
dismiss()
when (val result = taskViewModel.result.value) {
is String -> Toast.makeText(
requireContext(),
result,
Toast.LENGTH_LONG
).show()
is MessageDialogFragment -> result.show(
requireActivity().supportFragmentManager,
MessageDialogFragment.TAG
)
is MessageDialogFragment -> result.show(
requireActivity().supportFragmentManager,
MessageDialogFragment.TAG
)
else -> {
// Do nothing
else -> {
// Do nothing
}
}
taskViewModel.clear()
}
}
}
taskViewModel.clear()
}
}
taskViewModel.cancelled.collect(viewLifecycleOwner) {
if (it) {
dialog?.setTitle(R.string.cancelling)
}
}
taskViewModel.progress.collect(viewLifecycleOwner) {
if (it != 0.0) {
binding.progressBar.apply {
isIndeterminate = false
progress = (
(it / taskViewModel.maxProgress.value) *
PROGRESS_BAR_RESOLUTION
).toInt()
min = 0
max = PROGRESS_BAR_RESOLUTION
launch {
repeatOnLifecycle(Lifecycle.State.CREATED) {
taskViewModel.cancelled.collect {
if (it) {
dialog?.setTitle(R.string.cancelling)
}
}
}
}
launch {
repeatOnLifecycle(Lifecycle.State.CREATED) {
taskViewModel.progress.collect {
if (it != 0.0) {
binding.progressBar.apply {
isIndeterminate = false
progress = (
(it / taskViewModel.maxProgress.value) *
PROGRESS_BAR_RESOLUTION
).toInt()
min = 0
max = PROGRESS_BAR_RESOLUTION
}
}
}
}
}
launch {
repeatOnLifecycle(Lifecycle.State.CREATED) {
taskViewModel.message.collect {
if (it.isEmpty()) {
binding.message.visibility = View.GONE
} else {
binding.message.visibility = View.VISIBLE
binding.message.text = it
}
}
}
}
}
taskViewModel.message.collect(viewLifecycleOwner) {
binding.message.setVisible(it.isNotEmpty())
binding.message.text = it
}
}

View File

@ -3,6 +3,7 @@
package org.yuzu.yuzu_emu.fragments
import android.annotation.SuppressLint
import android.content.Context
import android.content.SharedPreferences
import android.os.Bundle
@ -17,9 +18,14 @@ import androidx.core.view.updatePadding
import androidx.core.widget.doOnTextChanged
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import androidx.preference.PreferenceManager
import info.debatty.java.stringsimilarity.Jaccard
import info.debatty.java.stringsimilarity.JaroWinkler
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch
import java.util.Locale
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.YuzuApplication
@ -29,8 +35,6 @@ import org.yuzu.yuzu_emu.layout.AutofitGridLayoutManager
import org.yuzu.yuzu_emu.model.Game
import org.yuzu.yuzu_emu.model.GamesViewModel
import org.yuzu.yuzu_emu.model.HomeViewModel
import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
import org.yuzu.yuzu_emu.utils.collect
class SearchFragment : Fragment() {
private var _binding: FragmentSearchBinding? = null
@ -54,6 +58,8 @@ class SearchFragment : 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 = true, animated = true)
@ -75,18 +81,42 @@ class SearchFragment : Fragment() {
binding.chipGroup.setOnCheckedStateChangeListener { _, _ -> filterAndSearch() }
binding.searchText.doOnTextChanged { text: CharSequence?, _: Int, _: Int, _: Int ->
binding.clearButton.setVisible(text.toString().isNotEmpty())
if (text.toString().isNotEmpty()) {
binding.clearButton.visibility = View.VISIBLE
} else {
binding.clearButton.visibility = View.INVISIBLE
}
filterAndSearch()
}
gamesViewModel.searchFocused.collect(
viewLifecycleOwner,
resetState = { gamesViewModel.setSearchFocused(false) }
) { if (it) focusSearch() }
gamesViewModel.games.collect(viewLifecycleOwner) { filterAndSearch() }
gamesViewModel.searchedGames.collect(viewLifecycleOwner) {
(binding.gridGamesSearch.adapter as GameAdapter).submitList(it)
binding.noResultsView.setVisible(it.isNotEmpty())
viewLifecycleOwner.lifecycleScope.apply {
launch {
repeatOnLifecycle(Lifecycle.State.CREATED) {
gamesViewModel.searchFocused.collect {
if (it) {
focusSearch()
gamesViewModel.setSearchFocused(false)
}
}
}
}
launch {
repeatOnLifecycle(Lifecycle.State.CREATED) {
gamesViewModel.games.collectLatest { filterAndSearch() }
}
}
launch {
repeatOnLifecycle(Lifecycle.State.CREATED) {
gamesViewModel.searchedGames.collect {
(binding.gridGamesSearch.adapter as GameAdapter).submitList(it)
if (it.isEmpty()) {
binding.noResultsView.visibility = View.VISIBLE
} else {
binding.noResultsView.visibility = View.GONE
}
}
}
}
}
binding.clearButton.setOnClickListener { binding.searchText.setText("") }

View File

@ -4,6 +4,7 @@
package org.yuzu.yuzu_emu.fragments
import android.Manifest
import android.annotation.SuppressLint
import android.content.Intent
import android.os.Build
import android.os.Bundle
@ -22,6 +23,9 @@ import androidx.core.view.isVisible
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.preference.PreferenceManager
import androidx.viewpager2.widget.ViewPager2.OnPageChangeCallback
@ -42,8 +46,6 @@ import org.yuzu.yuzu_emu.ui.main.MainActivity
import org.yuzu.yuzu_emu.utils.DirectoryInitialization
import org.yuzu.yuzu_emu.utils.NativeConfig
import org.yuzu.yuzu_emu.utils.ViewUtils
import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
import org.yuzu.yuzu_emu.utils.collect
class SetupFragment : Fragment() {
private var _binding: FragmentSetupBinding? = null
@ -75,6 +77,8 @@ class SetupFragment : Fragment() {
return binding.root
}
// This is using the correct scope, lint is just acting up
@SuppressLint("UnsafeRepeatOnLifecycleDetector")
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
mainActivity = requireActivity() as MainActivity
@ -206,14 +210,28 @@ class SetupFragment : Fragment() {
)
}
homeViewModel.shouldPageForward.collect(
viewLifecycleOwner,
resetState = { homeViewModel.setShouldPageForward(false) }
) { if (it) pageForward() }
homeViewModel.gamesDirSelected.collect(
viewLifecycleOwner,
resetState = { homeViewModel.setGamesDirSelected(false) }
) { if (it) gamesDirCallback.onStepCompleted() }
viewLifecycleOwner.lifecycleScope.apply {
launch {
repeatOnLifecycle(Lifecycle.State.CREATED) {
homeViewModel.shouldPageForward.collect {
if (it) {
pageForward()
homeViewModel.setShouldPageForward(false)
}
}
}
}
launch {
repeatOnLifecycle(Lifecycle.State.CREATED) {
homeViewModel.gamesDirSelected.collect {
if (it) {
gamesDirCallback.onStepCompleted()
homeViewModel.setGamesDirSelected(false)
}
}
}
}
}
binding.viewPager2.apply {
adapter = SetupAdapter(requireActivity() as AppCompatActivity, pages)
@ -274,8 +292,12 @@ class SetupFragment : Fragment() {
val backIsVisible = savedInstanceState.getBoolean(KEY_BACK_VISIBILITY)
hasBeenWarned = savedInstanceState.getBooleanArray(KEY_HAS_BEEN_WARNED)!!
binding.buttonNext.setVisible(nextIsVisible)
binding.buttonBack.setVisible(backIsVisible)
if (nextIsVisible) {
binding.buttonNext.visibility = View.VISIBLE
}
if (backIsVisible) {
binding.buttonBack.visibility = View.VISIBLE
}
} else {
hasBeenWarned = BooleanArray(pages.size)
}

View File

@ -28,7 +28,6 @@ import org.yuzu.yuzu_emu.features.input.NativeInput
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.features.input.model.NativeAnalog
import org.yuzu.yuzu_emu.features.input.model.NativeButton
import org.yuzu.yuzu_emu.features.input.model.NpadStyleIndex
import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting
import org.yuzu.yuzu_emu.features.settings.model.IntSetting
import org.yuzu.yuzu_emu.overlay.model.OverlayControl
@ -100,10 +99,12 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
}
var shouldUpdateView = false
val playerIndex = when (NativeInput.getStyleIndex(0)) {
NpadStyleIndex.Handheld -> 8
else -> 0
}
val playerIndex =
if (NativeInput.isHandheldOnly()) {
NativeInput.ConsoleDevice
} else {
NativeInput.Player1Device
}
for (button in overlayButtons) {
if (!button.updateStatus(event)) {
@ -663,7 +664,7 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
val overlayControlData = NativeConfig.getOverlayControlData()
overlayControlData.forEach {
it.enabled = OverlayControl.from(it.id)?.defaultVisibility == true
it.enabled = OverlayControl.from(it.id)?.defaultVisibility == false
}
NativeConfig.setOverlayControlData(overlayControlData)

View File

@ -3,6 +3,7 @@
package org.yuzu.yuzu_emu.ui
import android.annotation.SuppressLint
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
@ -13,16 +14,19 @@ 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 com.google.android.material.color.MaterialColors
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.adapters.GameAdapter
import org.yuzu.yuzu_emu.databinding.FragmentGamesBinding
import org.yuzu.yuzu_emu.layout.AutofitGridLayoutManager
import org.yuzu.yuzu_emu.model.GamesViewModel
import org.yuzu.yuzu_emu.model.HomeViewModel
import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
import org.yuzu.yuzu_emu.utils.collect
class GamesFragment : Fragment() {
private var _binding: FragmentGamesBinding? = null
@ -40,6 +44,8 @@ class GamesFragment : 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 = true, animated = true)
@ -82,28 +88,49 @@ class GamesFragment : Fragment() {
}
}
gamesViewModel.isReloading.collect(viewLifecycleOwner) {
binding.swipeRefresh.isRefreshing = it
binding.noticeText.setVisible(
visible = gamesViewModel.games.value.isEmpty() && !it,
gone = false
)
}
gamesViewModel.games.collect(viewLifecycleOwner) {
(binding.gridGames.adapter as GameAdapter).submitList(it)
}
gamesViewModel.shouldSwapData.collect(
viewLifecycleOwner,
resetState = { gamesViewModel.setShouldSwapData(false) }
) {
if (it) {
(binding.gridGames.adapter as GameAdapter).submitList(gamesViewModel.games.value)
viewLifecycleOwner.lifecycleScope.apply {
launch {
repeatOnLifecycle(Lifecycle.State.RESUMED) {
gamesViewModel.isReloading.collect {
binding.swipeRefresh.isRefreshing = it
if (gamesViewModel.games.value.isEmpty() && !it) {
binding.noticeText.visibility = View.VISIBLE
} else {
binding.noticeText.visibility = View.INVISIBLE
}
}
}
}
launch {
repeatOnLifecycle(Lifecycle.State.RESUMED) {
gamesViewModel.games.collectLatest {
(binding.gridGames.adapter as GameAdapter).submitList(it)
}
}
}
launch {
repeatOnLifecycle(Lifecycle.State.RESUMED) {
gamesViewModel.shouldSwapData.collect {
if (it) {
(binding.gridGames.adapter as GameAdapter).submitList(
gamesViewModel.games.value
)
gamesViewModel.setShouldSwapData(false)
}
}
}
}
launch {
repeatOnLifecycle(Lifecycle.State.RESUMED) {
gamesViewModel.shouldScrollToTop.collect {
if (it) {
scrollToTop()
gamesViewModel.setShouldScrollToTop(false)
}
}
}
}
}
gamesViewModel.shouldScrollToTop.collect(
viewLifecycleOwner,
resetState = { gamesViewModel.setShouldScrollToTop(false) }
) { if (it) scrollToTop() }
setInsets()
}

View File

@ -19,6 +19,9 @@ import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
import androidx.core.view.ViewCompat
import androidx.core.view.WindowCompat
import androidx.core.view.WindowInsetsCompat
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import androidx.navigation.NavController
import androidx.navigation.fragment.NavHostFragment
import androidx.navigation.ui.setupWithNavController
@ -27,6 +30,7 @@ import com.google.android.material.color.MaterialColors
import com.google.android.material.navigation.NavigationBarView
import java.io.File
import java.io.FilenameFilter
import kotlinx.coroutines.launch
import org.yuzu.yuzu_emu.HomeNavigationDirections
import org.yuzu.yuzu_emu.NativeLibrary
import org.yuzu.yuzu_emu.R
@ -43,7 +47,6 @@ import org.yuzu.yuzu_emu.model.InstallResult
import org.yuzu.yuzu_emu.model.TaskState
import org.yuzu.yuzu_emu.model.TaskViewModel
import org.yuzu.yuzu_emu.utils.*
import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
import java.io.BufferedInputStream
import java.io.BufferedOutputStream
import java.util.zip.ZipEntry
@ -136,22 +139,41 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
// Prevents navigation from being drawn for a short time on recreation if set to hidden
if (!homeViewModel.navigationVisible.value.first) {
binding.navigationView.setVisible(visible = false, gone = false)
binding.statusBarShade.setVisible(visible = false, gone = false)
binding.navigationView.visibility = View.INVISIBLE
binding.statusBarShade.visibility = View.INVISIBLE
}
homeViewModel.navigationVisible.collect(this) { showNavigation(it.first, it.second) }
homeViewModel.statusBarShadeVisible.collect(this) { showStatusBarShade(it) }
homeViewModel.contentToInstall.collect(
this,
resetState = { homeViewModel.setContentToInstall(null) }
) {
if (it != null) {
installContent(it)
lifecycleScope.apply {
launch {
repeatOnLifecycle(Lifecycle.State.CREATED) {
homeViewModel.navigationVisible.collect { showNavigation(it.first, it.second) }
}
}
launch {
repeatOnLifecycle(Lifecycle.State.CREATED) {
homeViewModel.statusBarShadeVisible.collect { showStatusBarShade(it) }
}
}
launch {
repeatOnLifecycle(Lifecycle.State.CREATED) {
homeViewModel.contentToInstall.collect {
if (it != null) {
installContent(it)
homeViewModel.setContentToInstall(null)
}
}
}
}
launch {
repeatOnLifecycle(Lifecycle.State.CREATED) {
homeViewModel.checkKeys.collect {
if (it) {
checkKeys()
homeViewModel.setCheckKeys(false)
}
}
}
}
}
homeViewModel.checkKeys.collect(this, resetState = { homeViewModel.setCheckKeys(false) }) {
if (it) checkKeys()
}
setInsets()
@ -192,14 +214,18 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
private fun showNavigation(visible: Boolean, animated: Boolean) {
if (!animated) {
binding.navigationView.setVisible(visible)
if (visible) {
binding.navigationView.visibility = View.VISIBLE
} else {
binding.navigationView.visibility = View.INVISIBLE
}
return
}
val smallLayout = resources.getBoolean(R.bool.small_layout)
binding.navigationView.animate().apply {
if (visible) {
binding.navigationView.setVisible(true)
binding.navigationView.visibility = View.VISIBLE
duration = 300
interpolator = PathInterpolator(0.05f, 0.7f, 0.1f, 1f)
@ -238,7 +264,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
}
}.withEndAction {
if (!visible) {
binding.navigationView.setVisible(visible = false, gone = false)
binding.navigationView.visibility = View.INVISIBLE
}
}.start()
}
@ -246,7 +272,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
private fun showStatusBarShade(visible: Boolean) {
binding.statusBarShade.animate().apply {
if (visible) {
binding.statusBarShade.setVisible(true)
binding.statusBarShade.visibility = View.VISIBLE
binding.statusBarShade.translationY = binding.statusBarShade.height.toFloat() * -2
duration = 300
translationY(0f)
@ -258,7 +284,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
}
}.withEndAction {
if (!visible) {
binding.statusBarShade.setVisible(visible = false, gone = false)
binding.statusBarShade.visibility = View.INVISIBLE
}
}.start()
}
@ -498,8 +524,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
this@MainActivity,
titleId = R.string.content_install_notice,
descriptionId = R.string.content_install_notice_description,
positiveAction = { homeViewModel.setContentToInstall(documents) },
negativeAction = {}
positiveAction = { homeViewModel.setContentToInstall(documents) }
)
}
}.show(supportFragmentManager, ProgressDialogFragment.TAG)

View File

@ -21,12 +21,8 @@ object InputHandler {
else -> return false
}
var controllerData = androidControllers[event.device.controllerNumber]
if (controllerData == null) {
updateControllerData()
controllerData = androidControllers[event.device.controllerNumber] ?: return false
}
val controllerData =
androidControllers[event.device.controllerNumber] ?: return false
NativeInput.onGamePadButtonEvent(
controllerData.getGUID(),
controllerData.getPort(),

View File

@ -1,38 +0,0 @@
// SPDX-FileCopyrightText: 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
package org.yuzu.yuzu_emu.utils
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.launch
/**
* Collects this [Flow] with a given [LifecycleOwner].
* @param scope [LifecycleOwner] that this [Flow] will be collected with.
* @param repeatState When to repeat collection on this [Flow].
* @param resetState Optional lambda to reset state of an underlying [MutableStateFlow] after
* [stateCollector] has been run.
* @param stateCollector Lambda that receives new state.
*/
inline fun <reified T> Flow<T>.collect(
scope: LifecycleOwner,
repeatState: Lifecycle.State = Lifecycle.State.CREATED,
crossinline resetState: () -> Unit = {},
crossinline stateCollector: (state: T) -> Unit
) {
scope.apply {
lifecycleScope.launch {
repeatOnLifecycle(repeatState) {
this@collect.collect {
stateCollector(it)
resetState()
}
}
}
}
}

View File

@ -3,10 +3,8 @@
package org.yuzu.yuzu_emu.utils
import android.text.TextUtils
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
object ViewUtils {
fun showView(view: View, length: Long = 300) {
@ -59,35 +57,4 @@ object ViewUtils {
}
this.layoutParams = layoutParams
}
/**
* Shows or hides a view.
* @param visible Whether a view will be made View.VISIBLE or View.INVISIBLE/GONE.
* @param gone Optional parameter for hiding a view. Uses View.GONE if true and View.INVISIBLE otherwise.
*/
fun View.setVisible(visible: Boolean, gone: Boolean = true) {
visibility = if (visible) {
View.VISIBLE
} else {
if (gone) {
View.GONE
} else {
View.INVISIBLE
}
}
}
/**
* Starts a marquee on some text.
* @param delay Optional parameter for changing the start delay. 3 seconds of delay by default.
*/
fun TextView.marquee(delay: Long = 3000) {
ellipsize = null
marqueeRepeatLimit = -1
isSingleLine = true
postDelayed({
ellipsize = TextUtils.TruncateAt.MARQUEE
isSelected = true
}, delay)
}
}

View File

@ -23,22 +23,6 @@ void EmuWindow_Android::OnSurfaceChanged(ANativeWindow* surface) {
window_info.render_surface = reinterpret_cast<void*>(surface);
}
void EmuWindow_Android::OnTouchPressed(int id, float x, float y) {
const auto [touch_x, touch_y] = MapToTouchScreen(x, y);
EmulationSession::GetInstance().GetInputSubsystem().GetTouchScreen()->TouchPressed(touch_x,
touch_y, id);
}
void EmuWindow_Android::OnTouchMoved(int id, float x, float y) {
const auto [touch_x, touch_y] = MapToTouchScreen(x, y);
EmulationSession::GetInstance().GetInputSubsystem().GetTouchScreen()->TouchMoved(touch_x,
touch_y, id);
}
void EmuWindow_Android::OnTouchReleased(int id) {
EmulationSession::GetInstance().GetInputSubsystem().GetTouchScreen()->TouchReleased(id);
}
void EmuWindow_Android::OnFrameDisplayed() {
if (!m_first_frame) {
Common::Android::RunJNIOnFiber<void>(

View File

@ -38,10 +38,6 @@ public:
void OnSurfaceChanged(ANativeWindow* surface);
void OnFrameDisplayed() override;
void OnTouchPressed(int id, float x, float y);
void OnTouchMoved(int id, float x, float y);
void OnTouchReleased(int id);
std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override {
return {std::make_unique<GraphicsContext_Android>(m_driver_library)};
}

View File

@ -292,9 +292,6 @@ void EmulationSession::ShutdownEmulation() {
// Unload user input.
m_system.HIDCore().UnloadInputDevices();
// Enable all controllers
m_system.HIDCore().SetSupportedStyleTag({Core::HID::NpadStyleSet::All});
// Shutdown the main emulated process
if (m_load_result == Core::SystemResultStatus::Success) {
m_system.DetachDebugger();
@ -407,9 +404,7 @@ static Core::SystemResultStatus RunEmulation(const std::string& filepath,
const size_t program_index,
const bool frontend_initiated) {
MicroProfileOnThreadCreate("EmuThread");
SCOPE_EXIT {
MicroProfileShutdown();
};
SCOPE_EXIT({ MicroProfileShutdown(); });
LOG_INFO(Frontend, "starting");
@ -418,9 +413,7 @@ static Core::SystemResultStatus RunEmulation(const std::string& filepath,
return Core::SystemResultStatus::ErrorLoader;
}
SCOPE_EXIT {
EmulationSession::GetInstance().ShutdownEmulation();
};
SCOPE_EXIT({ EmulationSession::GetInstance().ShutdownEmulation(); });
jconst result = EmulationSession::GetInstance().InitializeEmulation(filepath, program_index,
frontend_initiated);
@ -668,7 +661,7 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_initializeEmptyUserDirectory(JNIEnv*
ASSERT(user_id);
const auto user_save_data_path = FileSys::SaveDataFactory::GetFullPath(
{}, vfs_nand_dir, FileSys::SaveDataSpaceId::User, FileSys::SaveDataType::Account, 1,
{}, 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);
@ -836,8 +829,8 @@ jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getSavePath(JNIEnv* env, jobject j
FileSys::OpenMode::Read);
const auto user_save_data_path = FileSys::SaveDataFactory::GetFullPath(
{}, vfsNandDir, FileSys::SaveDataSpaceId::User, FileSys::SaveDataType::Account, program_id,
user_id->AsU128(), 0);
{}, vfsNandDir, FileSys::SaveDataSpaceId::NandUser, FileSys::SaveDataType::SaveData,
program_id, user_id->AsU128(), 0);
return Common::Android::ToJString(env, user_save_data_path);
}

View File

@ -102,50 +102,8 @@ void ApplyControllerConfig(size_t player_index,
}
}
std::vector<s32> GetSupportedStyles(int player_index) {
auto& hid_core = EmulationSession::GetInstance().System().HIDCore();
const auto npad_style_set = hid_core.GetSupportedStyleTag();
std::vector<s32> supported_indexes;
if (npad_style_set.fullkey == 1) {
supported_indexes.push_back(static_cast<s32>(Core::HID::NpadStyleIndex::Fullkey));
}
if (npad_style_set.joycon_dual == 1) {
supported_indexes.push_back(static_cast<s32>(Core::HID::NpadStyleIndex::JoyconDual));
}
if (npad_style_set.joycon_left == 1) {
supported_indexes.push_back(static_cast<s32>(Core::HID::NpadStyleIndex::JoyconLeft));
}
if (npad_style_set.joycon_right == 1) {
supported_indexes.push_back(static_cast<s32>(Core::HID::NpadStyleIndex::JoyconRight));
}
if (player_index == 0 && npad_style_set.handheld == 1) {
supported_indexes.push_back(static_cast<s32>(Core::HID::NpadStyleIndex::Handheld));
}
if (npad_style_set.gamecube == 1) {
supported_indexes.push_back(static_cast<s32>(Core::HID::NpadStyleIndex::GameCube));
}
return supported_indexes;
}
void ConnectController(size_t player_index, bool connected) {
auto& hid_core = EmulationSession::GetInstance().System().HIDCore();
ApplyControllerConfig(player_index, [&](Core::HID::EmulatedController* controller) {
auto supported_styles = GetSupportedStyles(player_index);
auto controller_style = controller->GetNpadStyleIndex(true);
auto style = std::find(supported_styles.begin(), supported_styles.end(),
static_cast<int>(controller_style));
if (style == supported_styles.end() && !supported_styles.empty()) {
controller->SetNpadStyleIndex(
static_cast<Core::HID::NpadStyleIndex>(supported_styles[0]));
}
});
if (player_index == 0) {
auto* handheld = hid_core.GetEmulatedController(Core::HID::NpadIdType::Handheld);
auto* player_one = hid_core.GetEmulatedController(Core::HID::NpadIdType::Player1);
@ -232,7 +190,8 @@ void Java_org_yuzu_yuzu_1emu_features_input_NativeInput_onTouchPressed(JNIEnv* e
jint j_id, jfloat j_x_axis,
jfloat j_y_axis) {
if (EmulationSession::GetInstance().IsRunning()) {
EmulationSession::GetInstance().Window().OnTouchPressed(j_id, j_x_axis, j_y_axis);
EmulationSession::GetInstance().GetInputSubsystem().GetTouchScreen()->TouchPressed(
j_id, j_x_axis, j_y_axis);
}
}
@ -240,14 +199,15 @@ void Java_org_yuzu_yuzu_1emu_features_input_NativeInput_onTouchMoved(JNIEnv* env
jint j_id, jfloat j_x_axis,
jfloat j_y_axis) {
if (EmulationSession::GetInstance().IsRunning()) {
EmulationSession::GetInstance().Window().OnTouchMoved(j_id, j_x_axis, j_y_axis);
EmulationSession::GetInstance().GetInputSubsystem().GetTouchScreen()->TouchMoved(
j_id, j_x_axis, j_y_axis);
}
}
void Java_org_yuzu_yuzu_1emu_features_input_NativeInput_onTouchReleased(JNIEnv* env, jobject j_obj,
jint j_id) {
if (EmulationSession::GetInstance().IsRunning()) {
EmulationSession::GetInstance().Window().OnTouchReleased(j_id);
EmulationSession::GetInstance().GetInputSubsystem().GetTouchScreen()->TouchReleased(j_id);
}
}
@ -302,7 +262,6 @@ jobjectArray Java_org_yuzu_yuzu_1emu_features_input_NativeInput_getInputDevices(
void Java_org_yuzu_yuzu_1emu_features_input_NativeInput_loadInputProfiles(JNIEnv* env,
jobject j_obj) {
map_profiles.clear();
const auto input_profile_loc =
Common::FS::GetYuzuPath(Common::FS::YuzuPath::ConfigDir) / "input";
@ -475,6 +434,11 @@ void Java_org_yuzu_yuzu_1emu_features_input_NativeInput_stopMapping(JNIEnv* env,
EmulationSession::GetInstance().GetInputSubsystem().StopMapping();
}
void Java_org_yuzu_yuzu_1emu_features_input_NativeInput_unloadInputProfiles(JNIEnv* env,
jobject j_obj) {
map_profiles.clear();
}
void Java_org_yuzu_yuzu_1emu_features_input_NativeInput_updateMappingsWithDefaultImpl(
JNIEnv* env, jobject j_obj, jint j_player_index, jstring j_device_params,
jstring j_display_name) {
@ -564,10 +528,36 @@ jint Java_org_yuzu_yuzu_1emu_features_input_NativeInput_getButtonNameImpl(JNIEnv
jintArray Java_org_yuzu_yuzu_1emu_features_input_NativeInput_getSupportedStyleTagsImpl(
JNIEnv* env, jobject j_obj, jint j_player_index) {
auto supported_styles = GetSupportedStyles(j_player_index);
jintArray j_supported_indexes = env->NewIntArray(supported_styles.size());
env->SetIntArrayRegion(j_supported_indexes, 0, supported_styles.size(),
supported_styles.data());
auto& hid_core = EmulationSession::GetInstance().System().HIDCore();
const auto npad_style_set = hid_core.GetSupportedStyleTag();
std::vector<s32> supported_indexes;
if (npad_style_set.fullkey == 1) {
supported_indexes.push_back(static_cast<u32>(Core::HID::NpadStyleIndex::Fullkey));
}
if (npad_style_set.joycon_dual == 1) {
supported_indexes.push_back(static_cast<u32>(Core::HID::NpadStyleIndex::JoyconDual));
}
if (npad_style_set.joycon_left == 1) {
supported_indexes.push_back(static_cast<u32>(Core::HID::NpadStyleIndex::JoyconLeft));
}
if (npad_style_set.joycon_right == 1) {
supported_indexes.push_back(static_cast<u32>(Core::HID::NpadStyleIndex::JoyconRight));
}
if (j_player_index == 0 && npad_style_set.handheld == 1) {
supported_indexes.push_back(static_cast<u32>(Core::HID::NpadStyleIndex::Handheld));
}
if (npad_style_set.gamecube == 1) {
supported_indexes.push_back(static_cast<u32>(Core::HID::NpadStyleIndex::GameCube));
}
jintArray j_supported_indexes = env->NewIntArray(supported_indexes.size());
env->SetIntArrayRegion(j_supported_indexes, 0, supported_indexes.size(),
supported_indexes.data());
return j_supported_indexes;
}

View File

@ -39,7 +39,10 @@
style="@style/TextAppearance.Material3.TitleMedium"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ellipsize="none"
android:marqueeRepeatLimit="marquee_forever"
android:requiresFadingEdge="horizontal"
android:singleLine="true"
android:textAlignment="viewStart"
tools:text="@string/select_gpu_driver_default" />
@ -49,7 +52,10 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="6dp"
android:ellipsize="none"
android:marqueeRepeatLimit="marquee_forever"
android:requiresFadingEdge="horizontal"
android:singleLine="true"
android:textAlignment="viewStart"
tools:text="@string/install_gpu_driver_description" />
@ -59,7 +65,10 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="6dp"
android:ellipsize="none"
android:marqueeRepeatLimit="marquee_forever"
android:requiresFadingEdge="horizontal"
android:singleLine="true"
android:textAlignment="viewStart"
tools:text="@string/install_gpu_driver_description" />

View File

@ -21,7 +21,10 @@
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical|start"
android:ellipsize="none"
android:marqueeRepeatLimit="marquee_forever"
android:requiresFadingEdge="horizontal"
android:singleLine="true"
android:textAlignment="viewStart"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/button_layout"

View File

@ -40,7 +40,10 @@
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:ellipsize="none"
android:marqueeRepeatLimit="marquee_forever"
android:requiresFadingEdge="horizontal"
android:singleLine="true"
android:textAlignment="center"
android:textSize="14sp"
app:layout_constraintEnd_toEndOf="@+id/image_game_screen"

View File

@ -59,6 +59,9 @@
android:textAlignment="viewStart"
android:textSize="14sp"
android:textStyle="bold"
android:singleLine="true"
android:marqueeRepeatLimit="marquee_forever"
android:ellipsize="none"
android:requiresFadingEdge="horizontal"
android:layout_marginTop="6dp"
android:visibility="gone"

View File

@ -76,7 +76,10 @@
android:layout_marginTop="12dp"
android:layout_marginBottom="12dp"
android:layout_marginHorizontal="16dp"
android:ellipsize="none"
android:marqueeRepeatLimit="marquee_forever"
android:requiresFadingEdge="horizontal"
android:singleLine="true"
android:textAlignment="center"
tools:text="deko_basic" />

View File

@ -209,7 +209,6 @@
<string name="value_with_units">%1$s%2$s</string>
<!-- System settings strings -->
<string name="device_name">Device name</string>
<string name="use_docked_mode">Docked Mode</string>
<string name="use_docked_mode_description">Increases resolution, decreasing performance. Handheld Mode is used when disabled, lowering resolution and increasing performance.</string>
<string name="emulated_region">Emulated region</string>
@ -301,11 +300,8 @@
<string name="unused">Unused</string>
<string name="input_prompt">Move or press an input</string>
<string name="unsupported_input">Unsupported input type</string>
<string name="input_mapping_filter">Input mapping filter</string>
<string name="input_mapping_filter_description">Select a device to filter mapping inputs</string>
<string name="auto_map">Auto-map a controller</string>
<string name="auto_map_description">Select a device to attempt auto-mapping</string>
<string name="attempted_auto_map">Attempted auto-map with %1$s</string>
<string name="input_device">Input device</string>
<string name="input_device_description">Select an input device for auto-mapping. This will also act as a filter while mapping inputs.\nThis is temporary and not meant to lock this player to a specific controller.</string>
<string name="controller_type">Controller type</string>
<string name="pro_controller">Pro Controller</string>
<string name="handheld">Handheld</string>

View File

@ -73,15 +73,16 @@ void Manager::BufferReleaseAndRegister() {
}
}
u32 Manager::GetDeviceNames(std::span<Renderer::AudioDevice::AudioDeviceName> names,
u32 Manager::GetDeviceNames(std::vector<Renderer::AudioDevice::AudioDeviceName>& names,
[[maybe_unused]] const u32 max_count,
[[maybe_unused]] const bool filter) {
std::scoped_lock l{mutex};
LinkToManager();
auto input_devices{Sink::GetDeviceListForSink(Settings::values.sink_id.GetValue(), true)};
if (!input_devices.empty() && !names.empty()) {
names[0] = Renderer::AudioDevice::AudioDeviceName("Uac");
if (input_devices.size() > 1) {
names.emplace_back("Uac");
return 1;
}
return 0;

View File

@ -60,11 +60,13 @@ public:
* Get a list of audio in device names.
*
* @param names - Output container to write names to.
* @param max_count - Maximum number of device names to write. Unused
* @param filter - Should the list be filtered? Unused.
*
* @return Number of names written.
*/
u32 GetDeviceNames(std::span<Renderer::AudioDevice::AudioDeviceName> names, bool filter);
u32 GetDeviceNames(std::vector<Renderer::AudioDevice::AudioDeviceName>& names, u32 max_count,
bool filter);
/// Core system
Core::System& system;

View File

@ -146,11 +146,11 @@ public:
break;
}
tags[released++] = tag;
if (released >= tags.size()) {
break;
}
tags[released++] = tag;
}
return released;

View File

@ -28,8 +28,8 @@ OpusDecoder::~OpusDecoder() {
}
}
Result OpusDecoder::Initialize(const OpusParametersEx& params,
Kernel::KTransferMemory* transfer_memory, u64 transfer_memory_size) {
Result OpusDecoder::Initialize(OpusParametersEx& params, Kernel::KTransferMemory* transfer_memory,
u64 transfer_memory_size) {
auto frame_size{params.use_large_frame_size ? 5760 : 1920};
shared_buffer_size = transfer_memory_size;
shared_buffer = std::make_unique<u8[]>(shared_buffer_size);
@ -59,7 +59,7 @@ Result OpusDecoder::Initialize(const OpusParametersEx& params,
R_SUCCEED();
}
Result OpusDecoder::Initialize(const OpusMultiStreamParametersEx& params,
Result OpusDecoder::Initialize(OpusMultiStreamParametersEx& params,
Kernel::KTransferMemory* transfer_memory, u64 transfer_memory_size) {
auto frame_size{params.use_large_frame_size ? 5760 : 1920};
shared_buffer_size = transfer_memory_size;

View File

@ -22,10 +22,10 @@ public:
explicit OpusDecoder(Core::System& system, HardwareOpus& hardware_opus_);
~OpusDecoder();
Result Initialize(const OpusParametersEx& params, Kernel::KTransferMemory* transfer_memory,
Result Initialize(OpusParametersEx& params, Kernel::KTransferMemory* transfer_memory,
u64 transfer_memory_size);
Result Initialize(OpusMultiStreamParametersEx& params, Kernel::KTransferMemory* transfer_memory,
u64 transfer_memory_size);
Result Initialize(const OpusMultiStreamParametersEx& params,
Kernel::KTransferMemory* transfer_memory, u64 transfer_memory_size);
Result DecodeInterleaved(u32* out_data_size, u64* out_time_taken, u32* out_sample_count,
std::span<const u8> input_data, std::span<u8> output_data, bool reset);
Result SetContext([[maybe_unused]] std::span<const u8> context);

View File

@ -38,7 +38,7 @@ OpusDecoderManager::OpusDecoderManager(Core::System& system_)
}
}
Result OpusDecoderManager::GetWorkBufferSize(const OpusParameters& params, u32& out_size) {
Result OpusDecoderManager::GetWorkBufferSize(OpusParameters& params, u64& out_size) {
OpusParametersEx ex{
.sample_rate = params.sample_rate,
.channel_count = params.channel_count,
@ -47,11 +47,11 @@ Result OpusDecoderManager::GetWorkBufferSize(const OpusParameters& params, u32&
R_RETURN(GetWorkBufferSizeExEx(ex, out_size));
}
Result OpusDecoderManager::GetWorkBufferSizeEx(const OpusParametersEx& params, u32& out_size) {
Result OpusDecoderManager::GetWorkBufferSizeEx(OpusParametersEx& params, u64& out_size) {
R_RETURN(GetWorkBufferSizeExEx(params, out_size));
}
Result OpusDecoderManager::GetWorkBufferSizeExEx(const OpusParametersEx& params, u32& out_size) {
Result OpusDecoderManager::GetWorkBufferSizeExEx(OpusParametersEx& params, u64& out_size) {
R_UNLESS(IsValidChannelCount(params.channel_count), ResultInvalidOpusChannelCount);
R_UNLESS(IsValidSampleRate(params.sample_rate), ResultInvalidOpusSampleRate);
@ -63,8 +63,8 @@ Result OpusDecoderManager::GetWorkBufferSizeExEx(const OpusParametersEx& params,
R_SUCCEED();
}
Result OpusDecoderManager::GetWorkBufferSizeForMultiStream(const OpusMultiStreamParameters& params,
u32& out_size) {
Result OpusDecoderManager::GetWorkBufferSizeForMultiStream(OpusMultiStreamParameters& params,
u64& out_size) {
OpusMultiStreamParametersEx ex{
.sample_rate = params.sample_rate,
.channel_count = params.channel_count,
@ -76,13 +76,13 @@ Result OpusDecoderManager::GetWorkBufferSizeForMultiStream(const OpusMultiStream
R_RETURN(GetWorkBufferSizeForMultiStreamExEx(ex, out_size));
}
Result OpusDecoderManager::GetWorkBufferSizeForMultiStreamEx(
const OpusMultiStreamParametersEx& params, u32& out_size) {
Result OpusDecoderManager::GetWorkBufferSizeForMultiStreamEx(OpusMultiStreamParametersEx& params,
u64& out_size) {
R_RETURN(GetWorkBufferSizeForMultiStreamExEx(params, out_size));
}
Result OpusDecoderManager::GetWorkBufferSizeForMultiStreamExEx(
const OpusMultiStreamParametersEx& params, u32& out_size) {
Result OpusDecoderManager::GetWorkBufferSizeForMultiStreamExEx(OpusMultiStreamParametersEx& params,
u64& out_size) {
R_UNLESS(IsValidMultiStreamChannelCount(params.channel_count), ResultInvalidOpusChannelCount);
R_UNLESS(IsValidSampleRate(params.sample_rate), ResultInvalidOpusSampleRate);
R_UNLESS(IsValidStreamCount(params.channel_count, params.total_stream_count,

View File

@ -22,19 +22,17 @@ public:
return hardware_opus;
}
Result GetWorkBufferSize(const OpusParameters& params, u32& out_size);
Result GetWorkBufferSizeEx(const OpusParametersEx& params, u32& out_size);
Result GetWorkBufferSizeExEx(const OpusParametersEx& params, u32& out_size);
Result GetWorkBufferSizeForMultiStream(const OpusMultiStreamParameters& params, u32& out_size);
Result GetWorkBufferSizeForMultiStreamEx(const OpusMultiStreamParametersEx& params,
u32& out_size);
Result GetWorkBufferSizeForMultiStreamExEx(const OpusMultiStreamParametersEx& params,
u32& out_size);
Result GetWorkBufferSize(OpusParameters& params, u64& out_size);
Result GetWorkBufferSizeEx(OpusParametersEx& params, u64& out_size);
Result GetWorkBufferSizeExEx(OpusParametersEx& params, u64& out_size);
Result GetWorkBufferSizeForMultiStream(OpusMultiStreamParameters& params, u64& out_size);
Result GetWorkBufferSizeForMultiStreamEx(OpusMultiStreamParametersEx& params, u64& out_size);
Result GetWorkBufferSizeForMultiStreamExEx(OpusMultiStreamParametersEx& params, u64& out_size);
private:
Core::System& system;
HardwareOpus hardware_opus;
std::array<u32, MaxChannels> required_workbuffer_sizes{};
std::array<u64, MaxChannels> required_workbuffer_sizes{};
};
} // namespace AudioCore::OpusDecoder

View File

@ -42,7 +42,7 @@ HardwareOpus::HardwareOpus(Core::System& system_)
opus_decoder.SetSharedMemory(shared_memory);
}
u32 HardwareOpus::GetWorkBufferSize(u32 channel) {
u64 HardwareOpus::GetWorkBufferSize(u32 channel) {
if (!opus_decoder.IsRunning()) {
return 0;
}
@ -55,10 +55,10 @@ u32 HardwareOpus::GetWorkBufferSize(u32 channel) {
ADSP::OpusDecoder::Message::GetWorkBufferSizeOK, msg);
return 0;
}
return static_cast<u32>(shared_memory.dsp_return_data[0]);
return shared_memory.dsp_return_data[0];
}
u32 HardwareOpus::GetWorkBufferSizeForMultiStream(u32 total_stream_count, u32 stereo_stream_count) {
u64 HardwareOpus::GetWorkBufferSizeForMultiStream(u32 total_stream_count, u32 stereo_stream_count) {
std::scoped_lock l{mutex};
shared_memory.host_send_data[0] = total_stream_count;
shared_memory.host_send_data[1] = stereo_stream_count;
@ -70,7 +70,7 @@ u32 HardwareOpus::GetWorkBufferSizeForMultiStream(u32 total_stream_count, u32 st
ADSP::OpusDecoder::Message::GetWorkBufferSizeForMultiStreamOK, msg);
return 0;
}
return static_cast<u32>(shared_memory.dsp_return_data[0]);
return shared_memory.dsp_return_data[0];
}
Result HardwareOpus::InitializeDecodeObject(u32 sample_rate, u32 channel_count, void* buffer,
@ -94,9 +94,8 @@ Result HardwareOpus::InitializeDecodeObject(u32 sample_rate, u32 channel_count,
Result HardwareOpus::InitializeMultiStreamDecodeObject(u32 sample_rate, u32 channel_count,
u32 total_stream_count,
u32 stereo_stream_count,
const void* mappings, void* buffer,
u64 buffer_size) {
u32 stereo_stream_count, void* mappings,
void* buffer, u64 buffer_size) {
std::scoped_lock l{mutex};
shared_memory.host_send_data[0] = (u64)buffer;
shared_memory.host_send_data[1] = buffer_size;

View File

@ -16,14 +16,14 @@ class HardwareOpus {
public:
HardwareOpus(Core::System& system);
u32 GetWorkBufferSize(u32 channel);
u32 GetWorkBufferSizeForMultiStream(u32 total_stream_count, u32 stereo_stream_count);
u64 GetWorkBufferSize(u32 channel);
u64 GetWorkBufferSizeForMultiStream(u32 total_stream_count, u32 stereo_stream_count);
Result InitializeDecodeObject(u32 sample_rate, u32 channel_count, void* buffer,
u64 buffer_size);
Result InitializeMultiStreamDecodeObject(u32 sample_rate, u32 channel_count,
u32 totaL_stream_count, u32 stereo_stream_count,
const void* mappings, void* buffer, u64 buffer_size);
void* mappings, void* buffer, u64 buffer_size);
Result ShutdownDecodeObject(void* buffer, u64 buffer_size);
Result ShutdownMultiStreamDecodeObject(void* buffer, u64 buffer_size);
Result DecodeInterleaved(u32& out_sample_count, void* output_data, u64 output_data_size,

View File

@ -20,7 +20,7 @@ struct OpusParametersEx {
/* 0x00 */ u32 sample_rate;
/* 0x04 */ u32 channel_count;
/* 0x08 */ bool use_large_frame_size;
/* 0x09 */ INSERT_PADDING_BYTES_NOINIT(7);
/* 0x09 */ INSERT_PADDING_BYTES(7);
}; // size = 0x10
static_assert(sizeof(OpusParametersEx) == 0x10, "OpusParametersEx has the wrong size!");
@ -40,7 +40,7 @@ struct OpusMultiStreamParametersEx {
/* 0x08 */ u32 total_stream_count;
/* 0x0C */ u32 stereo_stream_count;
/* 0x10 */ bool use_large_frame_size;
/* 0x11 */ INSERT_PADDING_BYTES_NOINIT(7);
/* 0x11 */ INSERT_PADDING_BYTES(7);
/* 0x18 */ std::array<u8, OpusStreamCountMax + 1> mappings;
}; // size = 0x118
static_assert(sizeof(OpusMultiStreamParametersEx) == 0x118,

View File

@ -36,7 +36,8 @@ AudioDevice::AudioDevice(Core::System& system, const u64 applet_resource_user_id
: output_sink{system.AudioCore().GetOutputSink()},
applet_resource_user_id{applet_resource_user_id_}, user_revision{revision} {}
u32 AudioDevice::ListAudioDeviceName(std::span<AudioDeviceName> out_buffer) const {
u32 AudioDevice::ListAudioDeviceName(std::vector<AudioDeviceName>& out_buffer,
const size_t max_count) const {
std::span<const AudioDeviceName> names{};
if (CheckFeatureSupported(SupportTags::AudioUsbDeviceOutput, user_revision)) {
@ -45,18 +46,19 @@ u32 AudioDevice::ListAudioDeviceName(std::span<AudioDeviceName> out_buffer) cons
names = device_names;
}
const u32 out_count{static_cast<u32>(std::min(out_buffer.size(), names.size()))};
const u32 out_count{static_cast<u32>(std::min(max_count, names.size()))};
for (u32 i = 0; i < out_count; i++) {
out_buffer[i] = names[i];
out_buffer.push_back(names[i]);
}
return out_count;
}
u32 AudioDevice::ListAudioOutputDeviceName(std::span<AudioDeviceName> out_buffer) const {
const u32 out_count{static_cast<u32>(std::min(out_buffer.size(), output_device_names.size()))};
u32 AudioDevice::ListAudioOutputDeviceName(std::vector<AudioDeviceName>& out_buffer,
const size_t max_count) const {
const u32 out_count{static_cast<u32>(std::min(max_count, output_device_names.size()))};
for (u32 i = 0; i < out_count; i++) {
out_buffer[i] = output_device_names[i];
out_buffer.push_back(output_device_names[i]);
}
return out_count;
}

View File

@ -36,18 +36,20 @@ public:
* Get a list of the available output devices.
*
* @param out_buffer - Output buffer to write the available device names.
* @param max_count - Maximum number of devices to write (count of out_buffer).
* @return Number of device names written.
*/
u32 ListAudioDeviceName(std::span<AudioDeviceName> out_buffer) const;
u32 ListAudioDeviceName(std::vector<AudioDeviceName>& out_buffer, size_t max_count) const;
/**
* Get a list of the available output devices.
* Different to above somehow...
*
* @param out_buffer - Output buffer to write the available device names.
* @param max_count - Maximum number of devices to write (count of out_buffer).
* @return Number of device names written.
*/
u32 ListAudioOutputDeviceName(std::span<AudioDeviceName> out_buffer) const;
u32 ListAudioOutputDeviceName(std::vector<AudioDeviceName>& out_buffer, size_t max_count) const;
/**
* Set the volume of all streams in the backend sink.

View File

@ -17,8 +17,9 @@ Renderer::Renderer(Core::System& system_, Manager& manager_, Kernel::KEvent* ren
Result Renderer::Initialize(const AudioRendererParameterInternal& params,
Kernel::KTransferMemory* transfer_memory,
const u64 transfer_memory_size, Kernel::KProcess* process_handle,
const u64 applet_resource_user_id, const s32 session_id) {
const u64 transfer_memory_size, const u32 process_handle,
Kernel::KProcess& process, const u64 applet_resource_user_id,
const s32 session_id) {
if (params.execution_mode == ExecutionMode::Auto) {
if (!manager.AddSystem(system)) {
LOG_ERROR(Service_Audio,
@ -29,7 +30,7 @@ Result Renderer::Initialize(const AudioRendererParameterInternal& params,
}
initialized = true;
system.Initialize(params, transfer_memory, transfer_memory_size, process_handle,
system.Initialize(params, transfer_memory, transfer_memory_size, process_handle, process,
applet_resource_user_id, session_id);
return ResultSuccess;

View File

@ -38,14 +38,14 @@ public:
* @param params - Input parameters to initialize the system with.
* @param transfer_memory - Game-supplied memory for all workbuffers. Unused.
* @param transfer_memory_size - Size of the transfer memory. Unused.
* @param process_handle - Process handle, also used for memory.
* @param process_handle - Process handle, also used for memory. Unused.
* @param applet_resource_user_id - Applet id for this renderer. Unused.
* @param session_id - Session id of this renderer.
* @return Result code.
*/
Result Initialize(const AudioRendererParameterInternal& params,
Kernel::KTransferMemory* transfer_memory, u64 transfer_memory_size,
Kernel::KProcess* process_handle, u64 applet_resource_user_id,
u32 process_handle, Kernel::KProcess& process, u64 applet_resource_user_id,
s32 session_id);
/**

View File

@ -18,7 +18,7 @@
namespace AudioCore::Renderer {
InfoUpdater::InfoUpdater(std::span<const u8> input_, std::span<u8> output_,
Kernel::KProcess* process_handle_, BehaviorInfo& behaviour_)
const u32 process_handle_, BehaviorInfo& behaviour_)
: input{input_.data() + sizeof(UpdateDataHeader)},
input_origin{input_}, output{output_.data() + sizeof(UpdateDataHeader)},
output_origin{output_}, in_header{reinterpret_cast<const UpdateDataHeader*>(

View File

@ -8,10 +8,6 @@
#include "common/common_types.h"
#include "core/hle/service/audio/errors.h"
namespace Kernel {
class KProcess;
}
namespace AudioCore::Renderer {
class BehaviorInfo;
class VoiceContext;
@ -43,8 +39,8 @@ class InfoUpdater {
static_assert(sizeof(UpdateDataHeader) == 0x40, "UpdateDataHeader has the wrong size!");
public:
explicit InfoUpdater(std::span<const u8> input, std::span<u8> output,
Kernel::KProcess* process_handle, BehaviorInfo& behaviour);
explicit InfoUpdater(std::span<const u8> input, std::span<u8> output, u32 process_handle,
BehaviorInfo& behaviour);
/**
* Update the voice channel resources.
@ -201,7 +197,7 @@ private:
/// Expected output size, see CheckConsumedSize
u64 expected_output_size;
/// Unused
Kernel::KProcess* process_handle;
u32 process_handle;
/// Behaviour
BehaviorInfo& behaviour;
};

View File

@ -8,11 +8,11 @@
namespace AudioCore::Renderer {
PoolMapper::PoolMapper(Kernel::KProcess* process_handle_, bool force_map_)
PoolMapper::PoolMapper(u32 process_handle_, bool force_map_)
: process_handle{process_handle_}, force_map{force_map_} {}
PoolMapper::PoolMapper(Kernel::KProcess* process_handle_, std::span<MemoryPoolInfo> pool_infos_,
u32 pool_count_, bool force_map_)
PoolMapper::PoolMapper(u32 process_handle_, std::span<MemoryPoolInfo> pool_infos_, u32 pool_count_,
bool force_map_)
: process_handle{process_handle_}, pool_infos{pool_infos_.data()},
pool_count{pool_count_}, force_map{force_map_} {}
@ -106,17 +106,15 @@ bool PoolMapper::IsForceMapEnabled() const {
return force_map;
}
Kernel::KProcess* PoolMapper::GetProcessHandle(const MemoryPoolInfo* pool) const {
u32 PoolMapper::GetProcessHandle(const MemoryPoolInfo* pool) const {
switch (pool->GetLocation()) {
case MemoryPoolInfo::Location::CPU:
return process_handle;
case MemoryPoolInfo::Location::DSP:
// return Kernel::Svc::CurrentProcess;
return nullptr;
return Kernel::Svc::CurrentProcess;
}
LOG_WARNING(Service_Audio, "Invalid MemoryPoolInfo location!");
// return Kernel::Svc::CurrentProcess;
return nullptr;
return Kernel::Svc::CurrentProcess;
}
bool PoolMapper::Map([[maybe_unused]] const u32 handle, [[maybe_unused]] const CpuAddr cpu_addr,
@ -149,14 +147,14 @@ bool PoolMapper::Unmap([[maybe_unused]] const u32 handle, [[maybe_unused]] const
}
bool PoolMapper::Unmap(MemoryPoolInfo& pool) const {
[[maybe_unused]] Kernel::KProcess* handle{};
[[maybe_unused]] u32 handle{0};
switch (pool.GetLocation()) {
case MemoryPoolInfo::Location::CPU:
handle = process_handle;
break;
case MemoryPoolInfo::Location::DSP:
// handle = Kernel::Svc::CurrentProcess;
handle = Kernel::Svc::CurrentProcess;
break;
}
// nn::audio::dsp::UnmapUserPointer(handle, pool->cpu_address, pool->size);

View File

@ -10,10 +10,6 @@
#include "common/common_types.h"
#include "core/hle/service/audio/errors.h"
namespace Kernel {
class KProcess;
}
namespace AudioCore::Renderer {
class AddressInfo;
@ -22,9 +18,9 @@ class AddressInfo;
*/
class PoolMapper {
public:
explicit PoolMapper(Kernel::KProcess* process_handle, bool force_map);
explicit PoolMapper(Kernel::KProcess* process_handle, std::span<MemoryPoolInfo> pool_infos,
u32 pool_count, bool force_map);
explicit PoolMapper(u32 process_handle, bool force_map);
explicit PoolMapper(u32 process_handle, std::span<MemoryPoolInfo> pool_infos, u32 pool_count,
bool force_map);
/**
* Clear the usage state for all given pools.
@ -102,7 +98,7 @@ public:
* @return CurrentProcessHandle if location == DSP,
* the PoolMapper's process_handle if location == CPU
*/
Kernel::KProcess* GetProcessHandle(const MemoryPoolInfo* pool) const;
u32 GetProcessHandle(const MemoryPoolInfo* pool) const;
/**
* Map the given region with the given handle. This is a no-op.
@ -171,7 +167,7 @@ public:
private:
/// Process handle for this mapper, used when location == CPU
Kernel::KProcess* process_handle{};
u32 process_handle;
/// List of memory pools assigned to this mapper
MemoryPoolInfo* pool_infos{};
/// The number of pools

View File

@ -102,8 +102,8 @@ System::System(Core::System& core_, Kernel::KEvent* adsp_rendered_event_)
Result System::Initialize(const AudioRendererParameterInternal& params,
Kernel::KTransferMemory* transfer_memory, u64 transfer_memory_size,
Kernel::KProcess* process_handle_, u64 applet_resource_user_id_,
s32 session_id_) {
u32 process_handle_, Kernel::KProcess& process_,
u64 applet_resource_user_id_, s32 session_id_) {
if (!CheckValidRevision(params.revision)) {
return Service::Audio::ResultInvalidRevision;
}
@ -119,6 +119,7 @@ Result System::Initialize(const AudioRendererParameterInternal& params,
behavior.SetUserLibRevision(params.revision);
process_handle = process_handle_;
process = &process_;
applet_resource_user_id = applet_resource_user_id_;
session_id = session_id_;
@ -131,8 +132,7 @@ Result System::Initialize(const AudioRendererParameterInternal& params,
render_device = params.rendering_device;
execution_mode = params.execution_mode;
process_handle->GetMemory().ZeroBlock(transfer_memory->GetSourceAddress(),
transfer_memory_size);
process->GetMemory().ZeroBlock(transfer_memory->GetSourceAddress(), transfer_memory_size);
// Note: We're not actually using the transfer memory because it's a pain to code for.
// Allocate the memory normally instead and hope the game doesn't try to read anything back
@ -616,7 +616,7 @@ void System::SendCommandToDsp() {
static_cast<u64>((time_limit_percent / 100) * 2'880'000.0 *
(static_cast<f32>(render_time_limit_percent) / 100.0f))};
audio_renderer.SetCommandBuffer(session_id, translated_addr, command_size, time_limit,
applet_resource_user_id, process_handle,
applet_resource_user_id, process,
reset_command_buffers);
reset_command_buffers = false;
command_buffer_size = command_size;

View File

@ -74,14 +74,14 @@ public:
* @param params - Input parameters to initialize the system with.
* @param transfer_memory - Game-supplied memory for all workbuffers. Unused.
* @param transfer_memory_size - Size of the transfer memory. Unused.
* @param process_handle - Process handle, also used for memory.
* @param process_handle - Process handle, also used for memory. Unused.
* @param applet_resource_user_id - Applet id for this renderer. Unused.
* @param session_id - Session id of this renderer.
* @return Result code.
*/
Result Initialize(const AudioRendererParameterInternal& params,
Kernel::KTransferMemory* transfer_memory, u64 transfer_memory_size,
Kernel::KProcess* process_handle, u64 applet_resource_user_id,
u32 process_handle, Kernel::KProcess& process, u64 applet_resource_user_id,
s32 session_id);
/**
@ -278,7 +278,9 @@ private:
/// Does what locks do
std::mutex lock{};
/// Process this audio render is operating within, used for memory reads/writes.
Kernel::KProcess* process_handle{};
Kernel::KProcess* process{};
/// Handle for the process for this system, unused
u32 process_handle{};
/// Applet resource id for this system, unused
u64 applet_resource_user_id{};
/// Controls performance input and output

View File

@ -357,9 +357,7 @@ bool IsCubebSuitable() {
return false;
}
SCOPE_EXIT {
cubeb_destroy(ctx);
};
SCOPE_EXIT({ cubeb_destroy(ctx); });
#ifdef _WIN32
if (SUCCEEDED(com_init_result)) {

View File

@ -20,10 +20,10 @@
namespace AudioCore::Sink {
void SinkStream::AppendBuffer(SinkBuffer& buffer, std::span<s16> samples) {
SCOPE_EXIT {
SCOPE_EXIT({
queue.enqueue(buffer);
++queued_buffers;
};
});
if (type == StreamType::In) {
return;

View File

@ -20,9 +20,7 @@ std::string DemangleSymbol(const std::string& mangled) {
}
char* demangled = nullptr;
SCOPE_EXIT {
std::free(demangled);
};
SCOPE_EXIT({ std::free(demangled); });
if (is_itanium(mangled)) {
demangled = llvm::itaniumDemangle(mangled.c_str());

View File

@ -430,11 +430,11 @@ public:
explicit Impl(size_t backing_size_, size_t virtual_size_)
: backing_size{backing_size_}, virtual_size{virtual_size_} {
bool good = false;
SCOPE_EXIT {
SCOPE_EXIT({
if (!good) {
Release();
}
};
});
long page_size = sysconf(_SC_PAGESIZE);
if (page_size != 0x1000) {

View File

@ -24,10 +24,10 @@ bool PageTable::ContinueTraversal(TraversalEntry* out_entry, TraversalContext* c
out_entry->block_size = page_size;
// Regardless of whether the page was mapped, advance on exit.
SCOPE_EXIT {
SCOPE_EXIT({
context->next_page += 1;
context->next_offset += page_size;
};
});
// Validate that we can read the actual entry.
const auto page = context->next_page;

View File

@ -7,61 +7,29 @@
#include "common/common_funcs.h"
namespace detail {
template <class F>
class ScopeGuard {
YUZU_NON_COPYABLE(ScopeGuard);
private:
F f;
bool active;
public:
constexpr ScopeGuard(F f_) : f(std::move(f_)), active(true) {}
constexpr ~ScopeGuard() {
template <typename Func>
struct ScopeExitHelper {
explicit ScopeExitHelper(Func&& func_) : func(std::move(func_)) {}
~ScopeExitHelper() {
if (active) {
f();
func();
}
}
constexpr void Cancel() {
void Cancel() {
active = false;
}
constexpr ScopeGuard(ScopeGuard&& rhs) : f(std::move(rhs.f)), active(rhs.active) {
rhs.Cancel();
}
ScopeGuard& operator=(ScopeGuard&& rhs) = delete;
Func func;
bool active{true};
};
template <class F>
constexpr ScopeGuard<F> MakeScopeGuard(F f) {
return ScopeGuard<F>(std::move(f));
template <typename Func>
ScopeExitHelper<Func> ScopeExit(Func&& func) {
return ScopeExitHelper<Func>(std::forward<Func>(func));
}
enum class ScopeGuardOnExit {};
template <typename F>
constexpr ScopeGuard<F> operator+(ScopeGuardOnExit, F&& f) {
return ScopeGuard<F>(std::forward<F>(f));
}
} // namespace detail
#define CONCATENATE_IMPL(s1, s2) s1##s2
#define CONCATENATE(s1, s2) CONCATENATE_IMPL(s1, s2)
#ifdef __COUNTER__
#define ANONYMOUS_VARIABLE(pref) CONCATENATE(pref, __COUNTER__)
#else
#define ANONYMOUS_VARIABLE(pref) CONCATENATE(pref, __LINE__)
#endif
/**
* This macro is similar to SCOPE_EXIT, except the object is caller managed. This is intended to be
* used when the caller might want to cancel the ScopeExit.
*/
#define SCOPE_GUARD detail::ScopeGuardOnExit() + [&]()
/**
* This macro allows you to conveniently specify a block of code that will run on scope exit. Handy
* for doing ad-hoc clean-up tasks in a function with multiple returns.
@ -70,7 +38,7 @@ constexpr ScopeGuard<F> operator+(ScopeGuardOnExit, F&& f) {
* \code
* const int saved_val = g_foo;
* g_foo = 55;
* SCOPE_EXIT{ g_foo = saved_val; };
* SCOPE_EXIT({ g_foo = saved_val; });
*
* if (Bar()) {
* return 0;
@ -79,4 +47,10 @@ constexpr ScopeGuard<F> operator+(ScopeGuardOnExit, F&& f) {
* }
* \endcode
*/
#define SCOPE_EXIT auto ANONYMOUS_VARIABLE(SCOPE_EXIT_STATE_) = SCOPE_GUARD
#define SCOPE_EXIT(body) auto CONCAT2(scope_exit_helper_, __LINE__) = detail::ScopeExit([&]() body)
/**
* This macro is similar to SCOPE_EXIT, except the object is caller managed. This is intended to be
* used when the caller might want to cancel the ScopeExit.
*/
#define SCOPE_GUARD(body) detail::ScopeExit([&]() body)

View File

@ -384,12 +384,6 @@ struct Values {
AstcRecompression::Bc3,
"astc_recompression",
Category::RendererAdvanced};
SwitchableSetting<VramUsageMode, true> vram_usage_mode{linkage,
VramUsageMode::Conservative,
VramUsageMode::Conservative,
VramUsageMode::Aggressive,
"vram_usage_mode",
Category::RendererAdvanced};
SwitchableSetting<bool> async_presentation{linkage,
#ifdef ANDROID
true,

View File

@ -122,8 +122,6 @@ ENUM(AstcRecompression, Uncompressed, Bc1, Bc3);
ENUM(VSyncMode, Immediate, Mailbox, Fifo, FifoRelaxed);
ENUM(VramUsageMode, Conservative, Aggressive);
ENUM(RendererBackend, OpenGL, Vulkan, Null);
ENUM(ShaderBackend, Glsl, Glasm, SpirV);

View File

@ -396,9 +396,9 @@ struct PlayerInput {
u32 button_color_right;
std::string profile_name;
// This is meant to tell the Android frontend whether to use a device's built-in vibration
// motor or a controller's vibrations.
#ifdef ANDROID
bool use_system_vibrator;
#endif
};
struct TouchscreenInput {

View File

@ -38,10 +38,6 @@ std::string StringFromBuffer(std::span<const u8> data) {
return std::string(data.begin(), std::find(data.begin(), data.end(), '\0'));
}
std::string StringFromBuffer(std::span<const char> data) {
return std::string(data.begin(), std::find(data.begin(), data.end(), '\0'));
}
// Turns " hej " into "hej". Also handles tabs.
std::string StripSpaces(const std::string& str) {
const std::size_t s = str.find_first_not_of(" \t\r\n");

View File

@ -19,7 +19,6 @@ namespace Common {
[[nodiscard]] std::string ToUpper(std::string str);
[[nodiscard]] std::string StringFromBuffer(std::span<const u8> data);
[[nodiscard]] std::string StringFromBuffer(std::span<const char> data);
[[nodiscard]] std::string StripSpaces(const std::string& s);
[[nodiscard]] std::string StripQuotes(const std::string& s);

View File

@ -2,8 +2,8 @@
# SPDX-License-Identifier: GPL-2.0-or-later
add_library(core STATIC
arm/arm_interface.cpp
arm/arm_interface.h
arm/arm_interface.cpp
arm/debug.cpp
arm/debug.h
arm/exclusive_monitor.cpp
@ -37,10 +37,10 @@ add_library(core STATIC
debugger/gdbstub.h
debugger/gdbstub_arch.cpp
debugger/gdbstub_arch.h
device_memory.cpp
device_memory.h
device_memory_manager.h
device_memory_manager.inc
device_memory.cpp
device_memory.h
file_sys/bis_factory.cpp
file_sys/bis_factory.h
file_sys/card_image.cpp
@ -58,14 +58,9 @@ add_library(core STATIC
file_sys/fs_operate_range.h
file_sys/fs_path.h
file_sys/fs_path_utility.h
file_sys/fs_save_data_types.h
file_sys/fs_string_util.h
file_sys/fsa/fs_i_directory.h
file_sys/fsa/fs_i_file.h
file_sys/fsa/fs_i_filesystem.h
file_sys/fsmitm_romfsbuild.cpp
file_sys/fsmitm_romfsbuild.h
file_sys/fssrv/fssrv_sf_path.h
file_sys/fssystem/fs_i_storage.h
file_sys/fssystem/fs_types.h
file_sys/fssystem/fssystem_aes_ctr_counter_extended_storage.cpp
@ -395,20 +390,6 @@ add_library(core STATIC
hle/service/acc/errors.h
hle/service/acc/profile_manager.cpp
hle/service/acc/profile_manager.h
hle/service/am/am.cpp
hle/service/am/am.h
hle/service/am/am_results.h
hle/service/am/am_types.h
hle/service/am/applet.cpp
hle/service/am/applet.h
hle/service/am/applet_data_broker.cpp
hle/service/am/applet_data_broker.h
hle/service/am/applet_manager.cpp
hle/service/am/applet_manager.h
hle/service/am/applet_message_queue.cpp
hle/service/am/applet_message_queue.h
hle/service/am/display_layer_manager.cpp
hle/service/am/display_layer_manager.h
hle/service/am/frontend/applet_cabinet.cpp
hle/service/am/frontend/applet_cabinet.h
hle/service/am/frontend/applet_controller.cpp
@ -430,10 +411,24 @@ add_library(core STATIC
hle/service/am/frontend/applet_web_browser_types.h
hle/service/am/frontend/applets.cpp
hle/service/am/frontend/applets.h
hle/service/am/am.cpp
hle/service/am/am.h
hle/service/am/am_results.h
hle/service/am/am_types.h
hle/service/am/applet.cpp
hle/service/am/applet.h
hle/service/am/applet_manager.cpp
hle/service/am/applet_data_broker.cpp
hle/service/am/applet_data_broker.h
hle/service/am/applet_manager.h
hle/service/am/applet_message_queue.cpp
hle/service/am/applet_message_queue.h
hle/service/am/hid_registration.cpp
hle/service/am/hid_registration.h
hle/service/am/library_applet_storage.cpp
hle/service/am/library_applet_storage.h
hle/service/am/managed_layer_holder.cpp
hle/service/am/managed_layer_holder.h
hle/service/am/process.cpp
hle/service/am/process.h
hle/service/am/service/all_system_applet_proxies_service.cpp
@ -446,10 +441,10 @@ add_library(core STATIC
hle/service/am/service/application_creator.h
hle/service/am/service/application_functions.cpp
hle/service/am/service/application_functions.h
hle/service/am/service/application_proxy.cpp
hle/service/am/service/application_proxy.h
hle/service/am/service/application_proxy_service.cpp
hle/service/am/service/application_proxy_service.h
hle/service/am/service/application_proxy.cpp
hle/service/am/service/application_proxy.h
hle/service/am/service/audio_controller.cpp
hle/service/am/service/audio_controller.h
hle/service/am/service/common_state_getter.cpp
@ -478,53 +473,55 @@ add_library(core STATIC
hle/service/am/service/process_winding_controller.h
hle/service/am/service/self_controller.cpp
hle/service/am/service/self_controller.h
hle/service/am/service/storage.cpp
hle/service/am/service/storage.h
hle/service/am/service/storage_accessor.cpp
hle/service/am/service/storage_accessor.h
hle/service/am/service/storage.cpp
hle/service/am/service/storage.h
hle/service/am/service/system_applet_proxy.cpp
hle/service/am/service/system_applet_proxy.h
hle/service/am/service/window_controller.cpp
hle/service/am/service/window_controller.h
hle/service/aoc/addon_content_manager.cpp
hle/service/aoc/addon_content_manager.h
hle/service/aoc/purchase_event_manager.cpp
hle/service/aoc/purchase_event_manager.h
hle/service/am/system_buffer_manager.cpp
hle/service/am/system_buffer_manager.h
hle/service/aoc/aoc_u.cpp
hle/service/aoc/aoc_u.h
hle/service/apm/apm.cpp
hle/service/apm/apm.h
hle/service/apm/apm_controller.cpp
hle/service/apm/apm_controller.h
hle/service/apm/apm_interface.cpp
hle/service/apm/apm_interface.h
hle/service/audio/audio_controller.cpp
hle/service/audio/audio_controller.h
hle/service/audio/audio_device.cpp
hle/service/audio/audio_device.h
hle/service/audio/audio_in_manager.cpp
hle/service/audio/audio_in_manager.h
hle/service/audio/audio_in.cpp
hle/service/audio/audio_in.h
hle/service/audio/audio_out_manager.cpp
hle/service/audio/audio_out_manager.h
hle/service/audio/audio_out.cpp
hle/service/audio/audio_out.h
hle/service/audio/audio_renderer_manager.cpp
hle/service/audio/audio_renderer_manager.h
hle/service/audio/audio_renderer.cpp
hle/service/audio/audio_renderer.h
hle/service/audio/audctl.cpp
hle/service/audio/audctl.h
hle/service/audio/audin_u.cpp
hle/service/audio/audin_u.h
hle/service/audio/audio.cpp
hle/service/audio/audio.h
hle/service/audio/audout_u.cpp
hle/service/audio/audout_u.h
hle/service/audio/audrec_a.cpp
hle/service/audio/audrec_a.h
hle/service/audio/audrec_u.cpp
hle/service/audio/audrec_u.h
hle/service/audio/audren_u.cpp
hle/service/audio/audren_u.h
hle/service/audio/errors.h
hle/service/audio/final_output_recorder_manager_for_applet.cpp
hle/service/audio/final_output_recorder_manager_for_applet.h
hle/service/audio/final_output_recorder_manager.cpp
hle/service/audio/final_output_recorder_manager.h
hle/service/audio/hardware_opus_decoder_manager.cpp
hle/service/audio/hardware_opus_decoder_manager.h
hle/service/audio/hardware_opus_decoder.cpp
hle/service/audio/hardware_opus_decoder.h
hle/service/audio/hwopus.cpp
hle/service/audio/hwopus.h
hle/service/bcat/backend/backend.cpp
hle/service/bcat/backend/backend.h
hle/service/bcat/news/newly_arrived_event_holder.cpp
hle/service/bcat/news/newly_arrived_event_holder.h
hle/service/bcat/news/news_data_service.cpp
hle/service/bcat/news/news_data_service.h
hle/service/bcat/news/news_database_service.cpp
hle/service/bcat/news/news_database_service.h
hle/service/bcat/news/news_service.cpp
hle/service/bcat/news/news_service.h
hle/service/bcat/news/overwrite_event_holder.cpp
hle/service/bcat/news/overwrite_event_holder.h
hle/service/bcat/news/service_creator.cpp
hle/service/bcat/news/service_creator.h
hle/service/bcat/bcat.cpp
hle/service/bcat/bcat.h
hle/service/bcat/bcat_result.h
@ -540,18 +537,6 @@ add_library(core STATIC
hle/service/bcat/delivery_cache_progress_service.h
hle/service/bcat/delivery_cache_storage_service.cpp
hle/service/bcat/delivery_cache_storage_service.h
hle/service/bcat/news/newly_arrived_event_holder.cpp
hle/service/bcat/news/newly_arrived_event_holder.h
hle/service/bcat/news/news_data_service.cpp
hle/service/bcat/news/news_data_service.h
hle/service/bcat/news/news_database_service.cpp
hle/service/bcat/news/news_database_service.h
hle/service/bcat/news/news_service.cpp
hle/service/bcat/news/news_service.h
hle/service/bcat/news/overwrite_event_holder.cpp
hle/service/bcat/news/overwrite_event_holder.h
hle/service/bcat/news/service_creator.cpp
hle/service/bcat/news/service_creator.h
hle/service/bcat/service_creator.cpp
hle/service/bcat/service_creator.h
hle/service/bpc/bpc.cpp
@ -560,16 +545,6 @@ add_library(core STATIC
hle/service/btdrv/btdrv.h
hle/service/btm/btm.cpp
hle/service/btm/btm.h
hle/service/btm/btm_debug.cpp
hle/service/btm/btm_debug.h
hle/service/btm/btm_system.cpp
hle/service/btm/btm_system.h
hle/service/btm/btm_system_core.cpp
hle/service/btm/btm_system_core.h
hle/service/btm/btm_user.cpp
hle/service/btm/btm_user.h
hle/service/btm/btm_user_core.cpp
hle/service/btm/btm_user_core.h
hle/service/caps/caps.cpp
hle/service/caps/caps.h
hle/service/caps/caps_a.cpp
@ -612,10 +587,6 @@ add_library(core STATIC
hle/service/filesystem/fsp/fs_i_file.h
hle/service/filesystem/fsp/fs_i_filesystem.cpp
hle/service/filesystem/fsp/fs_i_filesystem.h
hle/service/filesystem/fsp/fs_i_multi_commit_manager.cpp
hle/service/filesystem/fsp/fs_i_multi_commit_manager.h
hle/service/filesystem/fsp/fs_i_save_data_info_reader.cpp
hle/service/filesystem/fsp/fs_i_save_data_info_reader.h
hle/service/filesystem/fsp/fs_i_storage.cpp
hle/service/filesystem/fsp/fs_i_storage.h
hle/service/filesystem/fsp/fsp_ldr.cpp
@ -624,11 +595,13 @@ add_library(core STATIC
hle/service/filesystem/fsp/fsp_pr.h
hle/service/filesystem/fsp/fsp_srv.cpp
hle/service/filesystem/fsp/fsp_srv.h
hle/service/filesystem/fsp/fsp_types.h
hle/service/filesystem/fsp/fsp_util.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
hle/service/friend/friend.h
hle/service/friend/friend_interface.cpp
@ -766,48 +739,15 @@ add_library(core STATIC
hle/service/nim/nim.h
hle/service/npns/npns.cpp
hle/service/npns/npns.h
hle/service/ns/account_proxy_interface.cpp
hle/service/ns/account_proxy_interface.h
hle/service/ns/application_manager_interface.cpp
hle/service/ns/application_manager_interface.h
hle/service/ns/application_version_interface.cpp
hle/service/ns/application_version_interface.h
hle/service/ns/content_management_interface.cpp
hle/service/ns/content_management_interface.h
hle/service/ns/develop_interface.cpp
hle/service/ns/develop_interface.h
hle/service/ns/document_interface.cpp
hle/service/ns/document_interface.h
hle/service/ns/download_task_interface.cpp
hle/service/ns/download_task_interface.h
hle/service/ns/dynamic_rights_interface.cpp
hle/service/ns/dynamic_rights_interface.h
hle/service/ns/ecommerce_interface.cpp
hle/service/ns/ecommerce_interface.h
hle/service/ns/factory_reset_interface.cpp
hle/service/ns/factory_reset_interface.h
hle/service/ns/errors.h
hle/service/ns/iplatform_service_manager.cpp
hle/service/ns/iplatform_service_manager.h
hle/service/ns/language.cpp
hle/service/ns/language.h
hle/service/ns/ns.cpp
hle/service/ns/ns.h
hle/service/ns/ns_results.h
hle/service/ns/ns_types.h
hle/service/ns/platform_service_manager.cpp
hle/service/ns/platform_service_manager.h
hle/service/ns/query_service.cpp
hle/service/ns/query_service.h
hle/service/ns/read_only_application_control_data_interface.cpp
hle/service/ns/read_only_application_control_data_interface.h
hle/service/ns/read_only_application_record_interface.cpp
hle/service/ns/read_only_application_record_interface.h
hle/service/ns/service_getter_interface.cpp
hle/service/ns/service_getter_interface.h
hle/service/ns/system_update_control.cpp
hle/service/ns/system_update_control.h
hle/service/ns/system_update_interface.cpp
hle/service/ns/system_update_interface.h
hle/service/ns/vulnerability_manager_interface.cpp
hle/service/ns/vulnerability_manager_interface.h
hle/service/ns/pdm_qry.cpp
hle/service/ns/pdm_qry.h
hle/service/nvdrv/core/container.cpp
hle/service/nvdrv/core/container.h
hle/service/nvdrv/core/heap_mapper.cpp
@ -860,14 +800,14 @@ add_library(core STATIC
hle/service/nvnflinger/consumer_base.cpp
hle/service/nvnflinger/consumer_base.h
hle/service/nvnflinger/consumer_listener.h
hle/service/nvnflinger/fb_share_buffer_manager.cpp
hle/service/nvnflinger/fb_share_buffer_manager.h
hle/service/nvnflinger/graphic_buffer_producer.cpp
hle/service/nvnflinger/graphic_buffer_producer.h
hle/service/nvnflinger/hardware_composer.cpp
hle/service/nvnflinger/hardware_composer.h
hle/service/nvnflinger/hos_binder_driver.cpp
hle/service/nvnflinger/hos_binder_driver.h
hle/service/nvnflinger/hos_binder_driver_server.cpp
hle/service/nvnflinger/hos_binder_driver_server.h
hle/service/nvnflinger/hardware_composer.cpp
hle/service/nvnflinger/hardware_composer.h
hle/service/nvnflinger/hwc_layer.h
hle/service/nvnflinger/nvnflinger.cpp
hle/service/nvnflinger/nvnflinger.h
@ -875,26 +815,12 @@ add_library(core STATIC
hle/service/nvnflinger/pixel_format.h
hle/service/nvnflinger/producer_listener.h
hle/service/nvnflinger/status.h
hle/service/nvnflinger/surface_flinger.cpp
hle/service/nvnflinger/surface_flinger.h
hle/service/nvnflinger/ui/fence.h
hle/service/nvnflinger/ui/graphic_buffer.cpp
hle/service/nvnflinger/ui/graphic_buffer.h
hle/service/nvnflinger/window.h
hle/service/olsc/daemon_controller.cpp
hle/service/olsc/daemon_controller.h
hle/service/olsc/native_handle_holder.cpp
hle/service/olsc/native_handle_holder.h
hle/service/olsc/olsc_service_for_application.cpp
hle/service/olsc/olsc_service_for_application.h
hle/service/olsc/olsc_service_for_system_service.cpp
hle/service/olsc/olsc_service_for_system_service.h
hle/service/olsc/olsc.cpp
hle/service/olsc/olsc.h
hle/service/olsc/remote_storage_controller.cpp
hle/service/olsc/remote_storage_controller.h
hle/service/olsc/transfer_task_list_controller.cpp
hle/service/olsc/transfer_task_list_controller.h
hle/service/omm/omm.cpp
hle/service/omm/omm.h
hle/service/omm/operation_mode_manager.cpp
@ -905,44 +831,25 @@ add_library(core STATIC
hle/service/omm/power_state_interface.h
hle/service/os/event.cpp
hle/service/os/event.h
hle/service/os/multi_wait.cpp
hle/service/os/multi_wait.h
hle/service/os/multi_wait_holder.cpp
hle/service/os/multi_wait_holder.h
hle/service/os/multi_wait_utils.h
hle/service/os/multi_wait.cpp
hle/service/os/multi_wait.h
hle/service/os/mutex.cpp
hle/service/os/mutex.h
hle/service/pcie/pcie.cpp
hle/service/pcie/pcie.h
hle/service/pctl/parental_control_service_factory.cpp
hle/service/pctl/parental_control_service_factory.h
hle/service/pctl/parental_control_service.cpp
hle/service/pctl/parental_control_service.h
hle/service/pctl/pctl.cpp
hle/service/pctl/pctl.h
hle/service/pctl/pctl_results.h
hle/service/pctl/pctl_types.h
hle/service/pctl/pctl_module.cpp
hle/service/pctl/pctl_module.h
hle/service/pcv/pcv.cpp
hle/service/pcv/pcv.h
hle/service/pm/pm.cpp
hle/service/pm/pm.h
hle/service/prepo/prepo.cpp
hle/service/prepo/prepo.h
hle/service/psc/ovln/ovln_types.h
hle/service/psc/ovln/receiver_service.cpp
hle/service/psc/ovln/receiver_service.h
hle/service/psc/ovln/receiver.cpp
hle/service/psc/ovln/receiver.h
hle/service/psc/ovln/sender_service.cpp
hle/service/psc/ovln/sender_service.h
hle/service/psc/ovln/sender.cpp
hle/service/psc/ovln/sender.h
hle/service/psc/pm_control.cpp
hle/service/psc/pm_control.h
hle/service/psc/pm_module.cpp
hle/service/psc/pm_module.h
hle/service/psc/pm_service.cpp
hle/service/psc/pm_service.h
hle/service/psc/psc.cpp
hle/service/psc/psc.h
hle/service/psc/time/alarms.cpp
@ -966,17 +873,15 @@ add_library(core STATIC
hle/service/psc/time/common.cpp
hle/service/psc/time/common.h
hle/service/psc/time/errors.h
hle/service/psc/time/manager.h
hle/service/psc/time/power_state_request_manager.cpp
hle/service/psc/time/power_state_request_manager.h
hle/service/psc/time/power_state_service.cpp
hle/service/psc/time/power_state_service.h
hle/service/psc/time/service_manager.cpp
hle/service/psc/time/service_manager.h
hle/service/psc/time/shared_memory.cpp
hle/service/psc/time/shared_memory.h
hle/service/psc/time/static.cpp
hle/service/psc/time/static.h
hle/service/psc/time/manager.h
hle/service/psc/time/power_state_service.cpp
hle/service/psc/time/power_state_service.h
hle/service/psc/time/service_manager.cpp
hle/service/psc/time/service_manager.h
hle/service/psc/time/steady_clock.cpp
hle/service/psc/time/steady_clock.h
hle/service/psc/time/system_clock.cpp
@ -985,6 +890,8 @@ add_library(core STATIC
hle/service/psc/time/time_zone.h
hle/service/psc/time/time_zone_service.cpp
hle/service/psc/time/time_zone_service.h
hle/service/psc/time/power_state_request_manager.cpp
hle/service/psc/time/power_state_request_manager.h
hle/service/ptm/psm.cpp
hle/service/ptm/psm.h
hle/service/ptm/ptm.cpp
@ -1001,21 +908,19 @@ add_library(core STATIC
hle/service/server_manager.h
hle/service/service.cpp
hle/service/service.h
hle/service/services.cpp
hle/service/services.h
hle/service/set/setting_formats/appln_settings.cpp
hle/service/set/setting_formats/appln_settings.h
hle/service/set/setting_formats/device_settings.cpp
hle/service/set/setting_formats/device_settings.h
hle/service/set/setting_formats/system_settings.cpp
hle/service/set/setting_formats/system_settings.h
hle/service/set/setting_formats/private_settings.cpp
hle/service/set/setting_formats/private_settings.h
hle/service/set/factory_settings_server.cpp
hle/service/set/factory_settings_server.h
hle/service/set/firmware_debug_settings_server.cpp
hle/service/set/firmware_debug_settings_server.h
hle/service/set/key_code_map.h
hle/service/set/setting_formats/appln_settings.cpp
hle/service/set/setting_formats/appln_settings.h
hle/service/set/setting_formats/device_settings.cpp
hle/service/set/setting_formats/device_settings.h
hle/service/set/setting_formats/private_settings.cpp
hle/service/set/setting_formats/private_settings.h
hle/service/set/setting_formats/system_settings.cpp
hle/service/set/setting_formats/system_settings.h
hle/service/set/settings.cpp
hle/service/set/settings.h
hle/service/set/settings_server.cpp
@ -1050,36 +955,30 @@ add_library(core STATIC
hle/service/ssl/ssl_backend.h
hle/service/usb/usb.cpp
hle/service/usb/usb.h
hle/service/vi/display/vi_display.cpp
hle/service/vi/display/vi_display.h
hle/service/vi/layer/vi_layer.cpp
hle/service/vi/layer/vi_layer.h
hle/service/vi/application_display_service.cpp
hle/service/vi/application_display_service.h
hle/service/vi/application_root_service.cpp
hle/service/vi/application_root_service.h
hle/service/vi/conductor.cpp
hle/service/vi/conductor.h
hle/service/vi/container.cpp
hle/service/vi/container.h
hle/service/vi/display.h
hle/service/vi/display_list.h
hle/service/vi/layer.h
hle/service/vi/layer_list.h
hle/service/vi/hos_binder_driver.cpp
hle/service/vi/hos_binder_driver.h
hle/service/vi/manager_display_service.cpp
hle/service/vi/manager_display_service.h
hle/service/vi/manager_root_service.cpp
hle/service/vi/manager_root_service.h
hle/service/vi/service_creator.cpp
hle/service/vi/service_creator.h
hle/service/vi/shared_buffer_manager.cpp
hle/service/vi/shared_buffer_manager.h
hle/service/vi/system_display_service.cpp
hle/service/vi/system_display_service.h
hle/service/vi/system_root_service.cpp
hle/service/vi/system_root_service.h
hle/service/vi/vi.cpp
hle/service/vi/vi.h
hle/service/vi/vi_results.h
hle/service/vi/vi_types.h
hle/service/vi/vsync_manager.cpp
hle/service/vi/vsync_manager.h
hle/service/vi/vi.cpp
hle/service/vi/vi.h
internal_network/network.cpp
internal_network/network.h
internal_network/network_interface.cpp

View File

@ -47,7 +47,6 @@
#include "core/hle/service/psc/time/system_clock.h"
#include "core/hle/service/psc/time/time_zone_service.h"
#include "core/hle/service/service.h"
#include "core/hle/service/services.h"
#include "core/hle/service/set/system_settings_server.h"
#include "core/hle/service/sm/sm.h"
#include "core/internal_network/network.h"
@ -311,8 +310,7 @@ struct System::Impl {
audio_core = std::make_unique<AudioCore::AudioCore>(system);
service_manager = std::make_shared<Service::SM::ServiceManager>(kernel);
services =
std::make_unique<Service::Services>(service_manager, system, stop_event.get_token());
services = std::make_unique<Service::Services>(service_manager, system);
is_powered_on = true;
exit_locked = false;
@ -460,10 +458,11 @@ struct System::Impl {
gpu_core->NotifyShutdown();
}
stop_event.request_stop();
core_timing.SyncPause(false);
Network::CancelPendingSocketOperations();
kernel.SuspendEmulation(true);
if (services) {
services->KillNVNFlinger();
}
kernel.CloseServices();
kernel.ShutdownCores();
applet_manager.Reset();
@ -481,7 +480,6 @@ struct System::Impl {
cpu_manager.Shutdown();
debugger.reset();
kernel.Shutdown();
stop_event = {};
Network::RestartSocketOperations();
if (auto room_member = room_network.GetRoomMember().lock()) {
@ -617,7 +615,6 @@ struct System::Impl {
ExecuteProgramCallback execute_program_callback;
ExitCallback exit_callback;
std::stop_source stop_event;
std::array<u64, Core::Hardware::NUM_CPU_CORES> dynarmic_ticks{};
std::array<MicroProfileToken, Core::Hardware::NUM_CPU_CORES> microprofile_cpu{};

View File

@ -199,10 +199,10 @@ void CpuManager::RunThread(std::stop_token token, std::size_t core) {
data.host_context = Common::Fiber::ThreadToFiber();
// Cleanup
SCOPE_EXIT {
SCOPE_EXIT({
data.host_context->Exit();
MicroProfileOnThreadExit();
};
});
// Running
if (!gpu_barrier->Sync(token)) {

View File

@ -391,12 +391,12 @@ void DeviceMemoryManager<Traits>::WalkBlock(DAddr addr, std::size_t size, auto o
std::min((next_pages << Memory::YUZU_PAGEBITS) - page_offset, remaining_size);
const auto current_vaddr =
static_cast<u64>((page_index << Memory::YUZU_PAGEBITS) + page_offset);
SCOPE_EXIT{
SCOPE_EXIT({
page_index += next_pages;
page_offset = 0;
increment(copy_amount);
remaining_size -= copy_amount;
};
});
auto phys_addr = compressed_physical_ptr[page_index];
if (phys_addr == 0) {

View File

@ -64,8 +64,8 @@ struct RawNACP {
u64_le cache_storage_size;
u64_le cache_storage_journal_size;
u64_le cache_storage_data_and_journal_max_size;
u16_le cache_storage_max_index;
INSERT_PADDING_BYTES(0xE76);
u64_le cache_storage_max_index;
INSERT_PADDING_BYTES(0xE70);
};
static_assert(sizeof(RawNACP) == 0x4000, "RawNACP has incorrect size.");

View File

@ -3,10 +3,6 @@
#pragma once
#include <string_view>
#include "common/common_funcs.h"
#include "common/common_types.h"
namespace FileSys {
constexpr inline size_t EntryNameLengthMax = 0x300;

View File

@ -23,8 +23,6 @@ enum class OpenDirectoryMode : u64 {
File = (1 << 1),
All = (Directory | File),
NotRequireFileSize = (1ULL << 31),
};
DECLARE_ENUM_FLAG_OPERATORS(OpenDirectoryMode)
@ -38,29 +36,4 @@ enum class CreateOption : u8 {
BigFile = (1 << 0),
};
struct FileSystemAttribute {
u8 dir_entry_name_length_max_defined;
u8 file_entry_name_length_max_defined;
u8 dir_path_name_length_max_defined;
u8 file_path_name_length_max_defined;
INSERT_PADDING_BYTES_NOINIT(0x5);
u8 utf16_dir_entry_name_length_max_defined;
u8 utf16_file_entry_name_length_max_defined;
u8 utf16_dir_path_name_length_max_defined;
u8 utf16_file_path_name_length_max_defined;
INSERT_PADDING_BYTES_NOINIT(0x18);
s32 dir_entry_name_length_max;
s32 file_entry_name_length_max;
s32 dir_path_name_length_max;
s32 file_path_name_length_max;
INSERT_PADDING_WORDS_NOINIT(0x5);
s32 utf16_dir_entry_name_length_max;
s32 utf16_file_entry_name_length_max;
s32 utf16_dir_path_name_length_max;
s32 utf16_file_path_name_length_max;
INSERT_PADDING_WORDS_NOINIT(0x18);
INSERT_PADDING_WORDS_NOINIT(0x1);
};
static_assert(sizeof(FileSystemAttribute) == 0xC0, "FileSystemAttribute has incorrect size");
} // namespace FileSys

View File

@ -10,7 +10,7 @@ namespace FileSys {
constexpr size_t RequiredAlignment = alignof(u64);
inline void* AllocateUnsafe(size_t size) {
void* AllocateUnsafe(size_t size) {
// Allocate
void* const ptr = ::operator new(size, std::align_val_t{RequiredAlignment});
@ -21,16 +21,16 @@ inline void* AllocateUnsafe(size_t size) {
return ptr;
}
inline void DeallocateUnsafe(void* ptr, size_t size) {
void DeallocateUnsafe(void* ptr, size_t size) {
// Deallocate the pointer
::operator delete(ptr, std::align_val_t{RequiredAlignment});
}
inline void* Allocate(size_t size) {
void* Allocate(size_t size) {
return AllocateUnsafe(size);
}
inline void Deallocate(void* ptr, size_t size) {
void Deallocate(void* ptr, size_t size) {
// If the pointer is non-null, deallocate it
if (ptr != nullptr) {
DeallocateUnsafe(ptr, size);

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