Compare commits
123 Commits
android-25
...
android-26
Author | SHA1 | Date | |
---|---|---|---|
f44933930d | |||
e96826419b | |||
3f232161be | |||
98eb3df15c | |||
74ba9dde58 | |||
f3f63aa1e7 | |||
1bea1ee418 | |||
8bbc209950 | |||
9e1a67b950 | |||
de5422b1fd | |||
45f450fca5 | |||
9a3fd76b25 | |||
60fc6df407 | |||
de2d496e71 | |||
7b5bdd076d | |||
e0c17a2113 | |||
b107435a3f | |||
4e1fcd4a63 | |||
ea4a96b45e | |||
6a90db8c19 | |||
0e5972b0b5 | |||
5f7608a7c6 | |||
668ff0db3a | |||
9f159dd62c | |||
d1eaeeed8c | |||
10e27a2902 | |||
f567a41f53 | |||
704c62ca01 | |||
8d5473e67c | |||
3b1b98c645 | |||
daf350f5d3 | |||
c9ef2e26ca | |||
310c1f50be | |||
665fce871f | |||
58c7e846cb | |||
8b0fb98a11 | |||
8615509c40 | |||
d0af52f28e | |||
ef89b79d7e | |||
3e41f9a673 | |||
d45a12826c | |||
911ee8fd1f | |||
5361027ef0 | |||
56721517ea | |||
940a71422e | |||
da225d4aa1 | |||
8d74c107f5 | |||
1fc86b1e3a | |||
4cdf18095d | |||
2d43831d1f | |||
2e96921f9c | |||
cf0de18982 | |||
ae83ee28a3 | |||
306ed4984b | |||
626f2e65b1 | |||
2eded86b4b | |||
786fc512e2 | |||
c31ac45332 | |||
db172ba249 | |||
bb59940b03 | |||
04887953ff | |||
8ea72cc99d | |||
44d2e90217 | |||
12926eb5db | |||
ae114d2fa1 | |||
270d07be2f | |||
947cdbe4b1 | |||
5583957616 | |||
6d731e1aa1 | |||
839ded7d59 | |||
f57281ebc1 | |||
0a3bc6c0cf | |||
55a7815064 | |||
a1c4f53c8c | |||
8bbb44a74e | |||
bc5ae04ea0 | |||
4f387b0b74 | |||
bdf8aca750 | |||
acfc4d6dfb | |||
35a3c7226a | |||
d93fdc8a6c | |||
5d3c7433b8 | |||
0010d42f82 | |||
316089c39f | |||
5024df1925 | |||
e7146309de | |||
c327d2a62c | |||
cb2e312f13 | |||
366bb52ec8 | |||
82949085c0 | |||
90c43aa2e7 | |||
a07f0883b9 | |||
812f23d05c | |||
dcce9837d2 | |||
ee8eccc5fa | |||
7b79cddacd | |||
53f8383354 | |||
36108ce2be | |||
4cbafc1ef6 | |||
e31c926bf0 | |||
59ede32f8e | |||
9eba64adce | |||
110969e207 | |||
50ecad547e | |||
34fb14ec9a | |||
e2e0916100 | |||
501e3ae05a | |||
ac33847b30 | |||
18494b0ad6 | |||
dc2c302a84 | |||
a251f77556 | |||
ec02a1cfe5 | |||
39b958ab86 | |||
9c0724b270 | |||
dbcc447f43 | |||
2954c01b47 | |||
462ea921e3 | |||
cb29aa0473 | |||
af42482565 | |||
3067bfd126 | |||
368bf2211f | |||
de8a623932 | |||
865a0186b6 |
10
README.md
10
README.md
@ -1,13 +1,11 @@
|
|||||||
| Pull Request | Commit | Title | Author | Merged? |
|
| Pull Request | Commit | Title | Author | Merged? |
|
||||||
|----|----|----|----|----|
|
|----|----|----|----|----|
|
||||||
| [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) | [`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 |
|
||||||
| [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 |
|
| [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 |
|
||||||
|
| [12982](https://github.com/yuzu-emu/yuzu//pull/12982) | [`ef5027712`](https://github.com/yuzu-emu/yuzu//pull/12982/files) | fs: Add FileSystemAccessor and use cmif serialization | [FearlessTobi](https://github.com/FearlessTobi/) | 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 |
|
| [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 |
|
| [13075](https://github.com/yuzu-emu/yuzu//pull/13075) | [`f46dc3168`](https://github.com/yuzu-emu/yuzu//pull/13075/files) | shader_recompiler: throw on missing geometry streams in geometry shaders | [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 |
|
| [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 |
|
||||||
| [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) | [`50ecad547`](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.
|
End of merge log. You can find the original README.md below the break.
|
||||||
|
@ -3,24 +3,21 @@
|
|||||||
|
|
||||||
package org.yuzu.yuzu_emu
|
package org.yuzu.yuzu_emu
|
||||||
|
|
||||||
import android.app.Dialog
|
|
||||||
import android.content.DialogInterface
|
import android.content.DialogInterface
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Bundle
|
|
||||||
import android.text.Html
|
import android.text.Html
|
||||||
import android.text.method.LinkMovementMethod
|
import android.text.method.LinkMovementMethod
|
||||||
import android.view.Surface
|
import android.view.Surface
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
import androidx.annotation.Keep
|
import androidx.annotation.Keep
|
||||||
import androidx.fragment.app.DialogFragment
|
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
import java.lang.ref.WeakReference
|
import java.lang.ref.WeakReference
|
||||||
import org.yuzu.yuzu_emu.activities.EmulationActivity
|
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.DocumentsTree
|
||||||
import org.yuzu.yuzu_emu.utils.FileUtil
|
import org.yuzu.yuzu_emu.utils.FileUtil
|
||||||
import org.yuzu.yuzu_emu.utils.Log
|
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.InstallResult
|
||||||
import org.yuzu.yuzu_emu.model.Patch
|
import org.yuzu.yuzu_emu.model.Patch
|
||||||
import org.yuzu.yuzu_emu.model.GameVerificationResult
|
import org.yuzu.yuzu_emu.model.GameVerificationResult
|
||||||
@ -184,46 +181,13 @@ object NativeLibrary {
|
|||||||
ErrorUnknown
|
ErrorUnknown
|
||||||
}
|
}
|
||||||
|
|
||||||
private var coreErrorAlertResult = false
|
var coreErrorAlertResult = false
|
||||||
private val coreErrorAlertLock = Object()
|
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) {
|
private fun onCoreErrorImpl(title: String, message: String) {
|
||||||
val emulationActivity = sEmulationActivity.get()
|
val emulationActivity = sEmulationActivity.get()
|
||||||
if (emulationActivity == null) {
|
if (emulationActivity == null) {
|
||||||
error("[NativeLibrary] EmulationActivity not present")
|
Log.error("[NativeLibrary] EmulationActivity not present")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -239,7 +203,7 @@ object NativeLibrary {
|
|||||||
fun onCoreError(error: CoreError?, details: String): Boolean {
|
fun onCoreError(error: CoreError?, details: String): Boolean {
|
||||||
val emulationActivity = sEmulationActivity.get()
|
val emulationActivity = sEmulationActivity.get()
|
||||||
if (emulationActivity == null) {
|
if (emulationActivity == null) {
|
||||||
error("[NativeLibrary] EmulationActivity not present")
|
Log.error("[NativeLibrary] EmulationActivity not present")
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -270,7 +234,7 @@ object NativeLibrary {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Show the AlertDialog on the main thread.
|
// Show the AlertDialog on the main thread.
|
||||||
emulationActivity.runOnUiThread(Runnable { onCoreErrorImpl(title, message) })
|
emulationActivity.runOnUiThread { onCoreErrorImpl(title, message) }
|
||||||
|
|
||||||
// Wait for the lock to notify that it is complete.
|
// Wait for the lock to notify that it is complete.
|
||||||
synchronized(coreErrorAlertLock) { coreErrorAlertLock.wait() }
|
synchronized(coreErrorAlertLock) { coreErrorAlertLock.wait() }
|
||||||
|
@ -80,8 +80,14 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
|
|||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
InputHandler.updateControllerData()
|
InputHandler.updateControllerData()
|
||||||
val playerOne = NativeConfig.getInputSettings(true)[0]
|
val players = NativeConfig.getInputSettings(true)
|
||||||
if (!playerOne.hasMapping() && InputHandler.androidControllers.isNotEmpty()) {
|
var hasConfiguredControllers = false
|
||||||
|
players.forEach {
|
||||||
|
if (it.hasMapping()) {
|
||||||
|
hasConfiguredControllers = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!hasConfiguredControllers && InputHandler.androidControllers.isNotEmpty()) {
|
||||||
var params: ParamPackage? = null
|
var params: ParamPackage? = null
|
||||||
for (controller in InputHandler.registeredControllers) {
|
for (controller in InputHandler.registeredControllers) {
|
||||||
if (controller.get("port", -1) == 0) {
|
if (controller.get("port", -1) == 0) {
|
||||||
|
@ -3,15 +3,15 @@
|
|||||||
|
|
||||||
package org.yuzu.yuzu_emu.adapters
|
package org.yuzu.yuzu_emu.adapters
|
||||||
|
|
||||||
import android.text.TextUtils
|
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import org.yuzu.yuzu_emu.R
|
import org.yuzu.yuzu_emu.R
|
||||||
import org.yuzu.yuzu_emu.databinding.CardDriverOptionBinding
|
import org.yuzu.yuzu_emu.databinding.CardDriverOptionBinding
|
||||||
import org.yuzu.yuzu_emu.features.settings.model.StringSetting
|
import org.yuzu.yuzu_emu.features.settings.model.StringSetting
|
||||||
import org.yuzu.yuzu_emu.model.Driver
|
import org.yuzu.yuzu_emu.model.Driver
|
||||||
import org.yuzu.yuzu_emu.model.DriverViewModel
|
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
|
import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder
|
||||||
|
|
||||||
class DriverAdapter(private val driverViewModel: DriverViewModel) :
|
class DriverAdapter(private val driverViewModel: DriverViewModel) :
|
||||||
@ -44,25 +44,15 @@ class DriverAdapter(private val driverViewModel: DriverViewModel) :
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Delay marquee by 3s
|
// Delay marquee by 3s
|
||||||
title.postDelayed(
|
title.marquee()
|
||||||
{
|
version.marquee()
|
||||||
title.isSelected = true
|
description.marquee()
|
||||||
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
|
title.text = model.title
|
||||||
version.text = model.version
|
version.text = model.version
|
||||||
description.text = model.description
|
description.text = model.description
|
||||||
if (model.title != binding.root.context.getString(R.string.system_gpu_driver)) {
|
buttonDelete.setVisible(
|
||||||
buttonDelete.visibility = View.VISIBLE
|
model.title != binding.root.context.getString(R.string.system_gpu_driver)
|
||||||
} else {
|
)
|
||||||
buttonDelete.visibility = View.GONE
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,6 @@
|
|||||||
package org.yuzu.yuzu_emu.adapters
|
package org.yuzu.yuzu_emu.adapters
|
||||||
|
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.text.TextUtils
|
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import androidx.fragment.app.FragmentActivity
|
import androidx.fragment.app.FragmentActivity
|
||||||
@ -12,6 +11,7 @@ import org.yuzu.yuzu_emu.databinding.CardFolderBinding
|
|||||||
import org.yuzu.yuzu_emu.fragments.GameFolderPropertiesDialogFragment
|
import org.yuzu.yuzu_emu.fragments.GameFolderPropertiesDialogFragment
|
||||||
import org.yuzu.yuzu_emu.model.GameDir
|
import org.yuzu.yuzu_emu.model.GameDir
|
||||||
import org.yuzu.yuzu_emu.model.GamesViewModel
|
import org.yuzu.yuzu_emu.model.GamesViewModel
|
||||||
|
import org.yuzu.yuzu_emu.utils.ViewUtils.marquee
|
||||||
import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder
|
import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder
|
||||||
|
|
||||||
class FolderAdapter(val activity: FragmentActivity, val gamesViewModel: GamesViewModel) :
|
class FolderAdapter(val activity: FragmentActivity, val gamesViewModel: GamesViewModel) :
|
||||||
@ -29,13 +29,7 @@ class FolderAdapter(val activity: FragmentActivity, val gamesViewModel: GamesVie
|
|||||||
override fun bind(model: GameDir) {
|
override fun bind(model: GameDir) {
|
||||||
binding.apply {
|
binding.apply {
|
||||||
path.text = Uri.parse(model.uriString).path
|
path.text = Uri.parse(model.uriString).path
|
||||||
path.postDelayed(
|
path.marquee()
|
||||||
{
|
|
||||||
path.isSelected = true
|
|
||||||
path.ellipsize = TextUtils.TruncateAt.MARQUEE
|
|
||||||
},
|
|
||||||
3000
|
|
||||||
)
|
|
||||||
|
|
||||||
buttonEdit.setOnClickListener {
|
buttonEdit.setOnClickListener {
|
||||||
GameFolderPropertiesDialogFragment.newInstance(model)
|
GameFolderPropertiesDialogFragment.newInstance(model)
|
||||||
|
@ -4,7 +4,6 @@
|
|||||||
package org.yuzu.yuzu_emu.adapters
|
package org.yuzu.yuzu_emu.adapters
|
||||||
|
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.text.TextUtils
|
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.widget.ImageView
|
import android.widget.ImageView
|
||||||
@ -27,6 +26,7 @@ import org.yuzu.yuzu_emu.databinding.CardGameBinding
|
|||||||
import org.yuzu.yuzu_emu.model.Game
|
import org.yuzu.yuzu_emu.model.Game
|
||||||
import org.yuzu.yuzu_emu.model.GamesViewModel
|
import org.yuzu.yuzu_emu.model.GamesViewModel
|
||||||
import org.yuzu.yuzu_emu.utils.GameIconUtils
|
import org.yuzu.yuzu_emu.utils.GameIconUtils
|
||||||
|
import org.yuzu.yuzu_emu.utils.ViewUtils.marquee
|
||||||
import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder
|
import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder
|
||||||
|
|
||||||
class GameAdapter(private val activity: AppCompatActivity) :
|
class GameAdapter(private val activity: AppCompatActivity) :
|
||||||
@ -44,14 +44,7 @@ class GameAdapter(private val activity: AppCompatActivity) :
|
|||||||
|
|
||||||
binding.textGameTitle.text = model.title.replace("[\\t\\n\\r]+".toRegex(), " ")
|
binding.textGameTitle.text = model.title.replace("[\\t\\n\\r]+".toRegex(), " ")
|
||||||
|
|
||||||
binding.textGameTitle.postDelayed(
|
binding.textGameTitle.marquee()
|
||||||
{
|
|
||||||
binding.textGameTitle.ellipsize = TextUtils.TruncateAt.MARQUEE
|
|
||||||
binding.textGameTitle.isSelected = true
|
|
||||||
},
|
|
||||||
3000
|
|
||||||
)
|
|
||||||
|
|
||||||
binding.cardGame.setOnClickListener { onClick(model) }
|
binding.cardGame.setOnClickListener { onClick(model) }
|
||||||
binding.cardGame.setOnLongClickListener { onLongClick(model) }
|
binding.cardGame.setOnLongClickListener { onLongClick(model) }
|
||||||
}
|
}
|
||||||
|
@ -3,21 +3,18 @@
|
|||||||
|
|
||||||
package org.yuzu.yuzu_emu.adapters
|
package org.yuzu.yuzu_emu.adapters
|
||||||
|
|
||||||
import android.text.TextUtils
|
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import androidx.core.content.res.ResourcesCompat
|
import androidx.core.content.res.ResourcesCompat
|
||||||
import androidx.lifecycle.Lifecycle
|
|
||||||
import androidx.lifecycle.LifecycleOwner
|
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.CardInstallableIconBinding
|
||||||
import org.yuzu.yuzu_emu.databinding.CardSimpleOutlinedBinding
|
import org.yuzu.yuzu_emu.databinding.CardSimpleOutlinedBinding
|
||||||
import org.yuzu.yuzu_emu.model.GameProperty
|
import org.yuzu.yuzu_emu.model.GameProperty
|
||||||
import org.yuzu.yuzu_emu.model.InstallableProperty
|
import org.yuzu.yuzu_emu.model.InstallableProperty
|
||||||
import org.yuzu.yuzu_emu.model.SubmenuProperty
|
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
|
import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder
|
||||||
|
|
||||||
class GamePropertiesAdapter(
|
class GamePropertiesAdapter(
|
||||||
@ -76,23 +73,15 @@ class GamePropertiesAdapter(
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
binding.details.postDelayed({
|
binding.details.marquee()
|
||||||
binding.details.isSelected = true
|
|
||||||
binding.details.ellipsize = TextUtils.TruncateAt.MARQUEE
|
|
||||||
}, 3000)
|
|
||||||
|
|
||||||
if (submenuProperty.details != null) {
|
if (submenuProperty.details != null) {
|
||||||
binding.details.visibility = View.VISIBLE
|
binding.details.setVisible(true)
|
||||||
binding.details.text = submenuProperty.details.invoke()
|
binding.details.text = submenuProperty.details.invoke()
|
||||||
} else if (submenuProperty.detailsFlow != null) {
|
} else if (submenuProperty.detailsFlow != null) {
|
||||||
binding.details.visibility = View.VISIBLE
|
binding.details.setVisible(true)
|
||||||
viewLifecycle.lifecycleScope.launch {
|
submenuProperty.detailsFlow.collect(viewLifecycle) { binding.details.text = it }
|
||||||
viewLifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
|
|
||||||
submenuProperty.detailsFlow.collect { binding.details.text = it }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
binding.details.visibility = View.GONE
|
binding.details.setVisible(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -112,14 +101,10 @@ class GamePropertiesAdapter(
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
if (installableProperty.install != null) {
|
binding.buttonInstall.setVisible(installableProperty.install != null)
|
||||||
binding.buttonInstall.visibility = View.VISIBLE
|
binding.buttonInstall.setOnClickListener { installableProperty.install?.invoke() }
|
||||||
binding.buttonInstall.setOnClickListener { installableProperty.install.invoke() }
|
binding.buttonExport.setVisible(installableProperty.export != null)
|
||||||
}
|
binding.buttonExport.setOnClickListener { installableProperty.export?.invoke() }
|
||||||
if (installableProperty.export != null) {
|
|
||||||
binding.buttonExport.visibility = View.VISIBLE
|
|
||||||
binding.buttonExport.setOnClickListener { installableProperty.export.invoke() }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,22 +3,19 @@
|
|||||||
|
|
||||||
package org.yuzu.yuzu_emu.adapters
|
package org.yuzu.yuzu_emu.adapters
|
||||||
|
|
||||||
import android.text.TextUtils
|
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.core.content.res.ResourcesCompat
|
import androidx.core.content.res.ResourcesCompat
|
||||||
import androidx.lifecycle.Lifecycle
|
|
||||||
import androidx.lifecycle.LifecycleOwner
|
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.R
|
||||||
import org.yuzu.yuzu_emu.databinding.CardHomeOptionBinding
|
import org.yuzu.yuzu_emu.databinding.CardHomeOptionBinding
|
||||||
import org.yuzu.yuzu_emu.fragments.MessageDialogFragment
|
import org.yuzu.yuzu_emu.fragments.MessageDialogFragment
|
||||||
import org.yuzu.yuzu_emu.model.HomeSetting
|
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
|
import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder
|
||||||
|
|
||||||
class HomeSettingAdapter(
|
class HomeSettingAdapter(
|
||||||
@ -59,18 +56,8 @@ class HomeSettingAdapter(
|
|||||||
binding.optionIcon.alpha = 0.5f
|
binding.optionIcon.alpha = 0.5f
|
||||||
}
|
}
|
||||||
|
|
||||||
viewLifecycle.lifecycleScope.launch {
|
model.details.collect(viewLifecycle) { updateOptionDetails(it) }
|
||||||
viewLifecycle.repeatOnLifecycle(Lifecycle.State.CREATED) {
|
binding.optionDetail.marquee()
|
||||||
model.details.collect { updateOptionDetails(it) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
binding.optionDetail.postDelayed(
|
|
||||||
{
|
|
||||||
binding.optionDetail.ellipsize = TextUtils.TruncateAt.MARQUEE
|
|
||||||
binding.optionDetail.isSelected = true
|
|
||||||
},
|
|
||||||
3000
|
|
||||||
)
|
|
||||||
|
|
||||||
binding.root.setOnClickListener { onClick(model) }
|
binding.root.setOnClickListener { onClick(model) }
|
||||||
}
|
}
|
||||||
@ -90,7 +77,7 @@ class HomeSettingAdapter(
|
|||||||
private fun updateOptionDetails(detailString: String) {
|
private fun updateOptionDetails(detailString: String) {
|
||||||
if (detailString.isNotEmpty()) {
|
if (detailString.isNotEmpty()) {
|
||||||
binding.optionDetail.text = detailString
|
binding.optionDetail.text = detailString
|
||||||
binding.optionDetail.visibility = View.VISIBLE
|
binding.optionDetail.setVisible(true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,10 +4,10 @@
|
|||||||
package org.yuzu.yuzu_emu.adapters
|
package org.yuzu.yuzu_emu.adapters
|
||||||
|
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import org.yuzu.yuzu_emu.databinding.CardInstallableBinding
|
import org.yuzu.yuzu_emu.databinding.CardInstallableBinding
|
||||||
import org.yuzu.yuzu_emu.model.Installable
|
import org.yuzu.yuzu_emu.model.Installable
|
||||||
|
import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
|
||||||
import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder
|
import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder
|
||||||
|
|
||||||
class InstallableAdapter(installables: List<Installable>) :
|
class InstallableAdapter(installables: List<Installable>) :
|
||||||
@ -26,14 +26,10 @@ class InstallableAdapter(installables: List<Installable>) :
|
|||||||
binding.title.setText(model.titleId)
|
binding.title.setText(model.titleId)
|
||||||
binding.description.setText(model.descriptionId)
|
binding.description.setText(model.descriptionId)
|
||||||
|
|
||||||
if (model.install != null) {
|
binding.buttonInstall.setVisible(model.install != null)
|
||||||
binding.buttonInstall.visibility = View.VISIBLE
|
binding.buttonInstall.setOnClickListener { model.install?.invoke() }
|
||||||
binding.buttonInstall.setOnClickListener { model.install.invoke() }
|
binding.buttonExport.setVisible(model.export != null)
|
||||||
}
|
binding.buttonExport.setOnClickListener { model.export?.invoke() }
|
||||||
if (model.export != null) {
|
|
||||||
binding.buttonExport.visibility = View.VISIBLE
|
|
||||||
binding.buttonExport.setOnClickListener { model.export.invoke() }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,12 +4,12 @@
|
|||||||
package org.yuzu.yuzu_emu.adapters
|
package org.yuzu.yuzu_emu.adapters
|
||||||
|
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding
|
import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding
|
||||||
import org.yuzu.yuzu_emu.fragments.LicenseBottomSheetDialogFragment
|
import org.yuzu.yuzu_emu.fragments.LicenseBottomSheetDialogFragment
|
||||||
import org.yuzu.yuzu_emu.model.License
|
import org.yuzu.yuzu_emu.model.License
|
||||||
|
import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
|
||||||
import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder
|
import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder
|
||||||
|
|
||||||
class LicenseAdapter(private val activity: AppCompatActivity, licenses: List<License>) :
|
class LicenseAdapter(private val activity: AppCompatActivity, licenses: List<License>) :
|
||||||
@ -25,7 +25,7 @@ class LicenseAdapter(private val activity: AppCompatActivity, licenses: List<Lic
|
|||||||
binding.apply {
|
binding.apply {
|
||||||
textSettingName.text = root.context.getString(model.titleId)
|
textSettingName.text = root.context.getString(model.titleId)
|
||||||
textSettingDescription.text = root.context.getString(model.descriptionId)
|
textSettingDescription.text = root.context.getString(model.descriptionId)
|
||||||
textSettingValue.visibility = View.GONE
|
textSettingValue.setVisible(false)
|
||||||
|
|
||||||
root.setOnClickListener { onClick(model) }
|
root.setOnClickListener { onClick(model) }
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,6 @@ package org.yuzu.yuzu_emu.adapters
|
|||||||
|
|
||||||
import android.text.Html
|
import android.text.Html
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.core.content.res.ResourcesCompat
|
import androidx.core.content.res.ResourcesCompat
|
||||||
@ -17,6 +16,7 @@ import org.yuzu.yuzu_emu.model.SetupCallback
|
|||||||
import org.yuzu.yuzu_emu.model.SetupPage
|
import org.yuzu.yuzu_emu.model.SetupPage
|
||||||
import org.yuzu.yuzu_emu.model.StepState
|
import org.yuzu.yuzu_emu.model.StepState
|
||||||
import org.yuzu.yuzu_emu.utils.ViewUtils
|
import org.yuzu.yuzu_emu.utils.ViewUtils
|
||||||
|
import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
|
||||||
import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder
|
import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder
|
||||||
|
|
||||||
class SetupAdapter(val activity: AppCompatActivity, pages: List<SetupPage>) :
|
class SetupAdapter(val activity: AppCompatActivity, pages: List<SetupPage>) :
|
||||||
@ -30,8 +30,8 @@ class SetupAdapter(val activity: AppCompatActivity, pages: List<SetupPage>) :
|
|||||||
AbstractViewHolder<SetupPage>(binding), SetupCallback {
|
AbstractViewHolder<SetupPage>(binding), SetupCallback {
|
||||||
override fun bind(model: SetupPage) {
|
override fun bind(model: SetupPage) {
|
||||||
if (model.stepCompleted.invoke() == StepState.COMPLETE) {
|
if (model.stepCompleted.invoke() == StepState.COMPLETE) {
|
||||||
binding.buttonAction.visibility = View.INVISIBLE
|
binding.buttonAction.setVisible(visible = false, gone = false)
|
||||||
binding.textConfirmation.visibility = View.VISIBLE
|
binding.textConfirmation.setVisible(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
binding.icon.setImageDrawable(
|
binding.icon.setImageDrawable(
|
||||||
|
@ -64,17 +64,17 @@ data class PlayerInput(
|
|||||||
fun hasMapping(): Boolean {
|
fun hasMapping(): Boolean {
|
||||||
var hasMapping = false
|
var hasMapping = false
|
||||||
buttons.forEach {
|
buttons.forEach {
|
||||||
if (it != "[empty]") {
|
if (it != "[empty]" && it.isNotEmpty()) {
|
||||||
hasMapping = true
|
hasMapping = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
analogs.forEach {
|
analogs.forEach {
|
||||||
if (it != "[empty]") {
|
if (it != "[empty]" && it.isNotEmpty()) {
|
||||||
hasMapping = true
|
hasMapping = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
motions.forEach {
|
motions.forEach {
|
||||||
if (it != "[empty]") {
|
if (it != "[empty]" && it.isNotEmpty()) {
|
||||||
hasMapping = true
|
hasMapping = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,8 @@ package org.yuzu.yuzu_emu.features.settings.model
|
|||||||
import org.yuzu.yuzu_emu.utils.NativeConfig
|
import org.yuzu.yuzu_emu.utils.NativeConfig
|
||||||
|
|
||||||
enum class StringSetting(override val key: String) : AbstractStringSetting {
|
enum class StringSetting(override val key: String) : AbstractStringSetting {
|
||||||
DRIVER_PATH("driver_path");
|
DRIVER_PATH("driver_path"),
|
||||||
|
DEVICE_NAME("device_name");
|
||||||
|
|
||||||
override fun getString(needsGlobal: Boolean): String = NativeConfig.getString(key, needsGlobal)
|
override fun getString(needsGlobal: Boolean): String = NativeConfig.getString(key, needsGlobal)
|
||||||
|
|
||||||
|
@ -16,6 +16,7 @@ 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.IntSetting
|
||||||
import org.yuzu.yuzu_emu.features.settings.model.LongSetting
|
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.ShortSetting
|
||||||
|
import org.yuzu.yuzu_emu.features.settings.model.StringSetting
|
||||||
import org.yuzu.yuzu_emu.utils.NativeConfig
|
import org.yuzu.yuzu_emu.utils.NativeConfig
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -75,6 +76,9 @@ abstract class SettingsItem(
|
|||||||
get() = NativeLibrary.isRunning() && !setting.global &&
|
get() = NativeLibrary.isRunning() && !setting.global &&
|
||||||
!NativeConfig.isPerGameConfigLoaded()
|
!NativeConfig.isPerGameConfigLoaded()
|
||||||
|
|
||||||
|
val clearable: Boolean
|
||||||
|
get() = !setting.global && NativeConfig.isPerGameConfigLoaded()
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val TYPE_HEADER = 0
|
const val TYPE_HEADER = 0
|
||||||
const val TYPE_SWITCH = 1
|
const val TYPE_SWITCH = 1
|
||||||
@ -87,6 +91,7 @@ abstract class SettingsItem(
|
|||||||
const val TYPE_INPUT = 8
|
const val TYPE_INPUT = 8
|
||||||
const val TYPE_INT_SINGLE_CHOICE = 9
|
const val TYPE_INT_SINGLE_CHOICE = 9
|
||||||
const val TYPE_INPUT_PROFILE = 10
|
const val TYPE_INPUT_PROFILE = 10
|
||||||
|
const val TYPE_STRING_INPUT = 11
|
||||||
|
|
||||||
const val FASTMEM_COMBINED = "fastmem_combined"
|
const val FASTMEM_COMBINED = "fastmem_combined"
|
||||||
|
|
||||||
@ -105,6 +110,7 @@ abstract class SettingsItem(
|
|||||||
|
|
||||||
// List of all general
|
// List of all general
|
||||||
val settingsItems = HashMap<String, SettingsItem>().apply {
|
val settingsItems = HashMap<String, SettingsItem>().apply {
|
||||||
|
put(StringInputSetting(StringSetting.DEVICE_NAME, titleId = R.string.device_name))
|
||||||
put(
|
put(
|
||||||
SwitchSetting(
|
SwitchSetting(
|
||||||
BooleanSetting.RENDERER_USE_SPEED_LIMIT,
|
BooleanSetting.RENDERER_USE_SPEED_LIMIT,
|
||||||
|
@ -0,0 +1,22 @@
|
|||||||
|
// 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)
|
||||||
|
}
|
@ -11,16 +11,13 @@ import android.view.ViewGroup
|
|||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.fragment.app.DialogFragment
|
import androidx.fragment.app.DialogFragment
|
||||||
import androidx.fragment.app.activityViewModels
|
import androidx.fragment.app.activityViewModels
|
||||||
import androidx.lifecycle.Lifecycle
|
|
||||||
import androidx.lifecycle.lifecycleScope
|
|
||||||
import androidx.lifecycle.repeatOnLifecycle
|
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import org.yuzu.yuzu_emu.R
|
import org.yuzu.yuzu_emu.R
|
||||||
import org.yuzu.yuzu_emu.databinding.DialogInputProfilesBinding
|
import org.yuzu.yuzu_emu.databinding.DialogInputProfilesBinding
|
||||||
import org.yuzu.yuzu_emu.features.settings.model.view.InputProfileSetting
|
import org.yuzu.yuzu_emu.features.settings.model.view.InputProfileSetting
|
||||||
import org.yuzu.yuzu_emu.fragments.MessageDialogFragment
|
import org.yuzu.yuzu_emu.fragments.MessageDialogFragment
|
||||||
|
import org.yuzu.yuzu_emu.utils.collect
|
||||||
|
|
||||||
class InputProfileDialogFragment : DialogFragment() {
|
class InputProfileDialogFragment : DialogFragment() {
|
||||||
private var position = 0
|
private var position = 0
|
||||||
@ -110,25 +107,21 @@ class InputProfileDialogFragment : DialogFragment() {
|
|||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
|
||||||
viewLifecycleOwner.lifecycleScope.launch {
|
settingsViewModel.shouldShowDeleteProfileDialog.collect(viewLifecycleOwner) {
|
||||||
repeatOnLifecycle(Lifecycle.State.CREATED) {
|
if (it.isNotEmpty()) {
|
||||||
settingsViewModel.shouldShowDeleteProfileDialog.collect {
|
MessageDialogFragment.newInstance(
|
||||||
if (it.isNotEmpty()) {
|
activity = requireActivity(),
|
||||||
MessageDialogFragment.newInstance(
|
titleId = R.string.delete_input_profile,
|
||||||
activity = requireActivity(),
|
descriptionId = R.string.delete_input_profile_description,
|
||||||
titleId = R.string.delete_input_profile,
|
positiveAction = {
|
||||||
descriptionId = R.string.delete_input_profile_description,
|
setting.deleteProfile(it)
|
||||||
positiveAction = {
|
settingsViewModel.setReloadListAndNotifyDataset(true)
|
||||||
setting.deleteProfile(it)
|
},
|
||||||
settingsViewModel.setReloadListAndNotifyDataset(true)
|
negativeAction = {},
|
||||||
},
|
negativeButtonTitleId = android.R.string.cancel
|
||||||
negativeAction = {},
|
).show(parentFragmentManager, MessageDialogFragment.TAG)
|
||||||
negativeButtonTitleId = android.R.string.cancel
|
settingsViewModel.setShouldShowDeleteProfileDialog("")
|
||||||
).show(parentFragmentManager, MessageDialogFragment.TAG)
|
dismiss()
|
||||||
settingsViewModel.setShouldShowDeleteProfileDialog("")
|
|
||||||
dismiss()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,14 +13,9 @@ import androidx.appcompat.app.AppCompatActivity
|
|||||||
import androidx.core.view.ViewCompat
|
import androidx.core.view.ViewCompat
|
||||||
import androidx.core.view.WindowCompat
|
import androidx.core.view.WindowCompat
|
||||||
import androidx.core.view.WindowInsetsCompat
|
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.fragment.NavHostFragment
|
||||||
import androidx.navigation.navArgs
|
import androidx.navigation.navArgs
|
||||||
import com.google.android.material.color.MaterialColors
|
import com.google.android.material.color.MaterialColors
|
||||||
import kotlinx.coroutines.flow.collectLatest
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import org.yuzu.yuzu_emu.NativeLibrary
|
import org.yuzu.yuzu_emu.NativeLibrary
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import org.yuzu.yuzu_emu.R
|
import org.yuzu.yuzu_emu.R
|
||||||
@ -70,39 +65,23 @@ class SettingsActivity : AppCompatActivity() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
lifecycleScope.apply {
|
settingsViewModel.shouldRecreate.collect(
|
||||||
launch {
|
this,
|
||||||
repeatOnLifecycle(Lifecycle.State.CREATED) {
|
resetState = { settingsViewModel.setShouldRecreate(false) }
|
||||||
settingsViewModel.shouldRecreate.collectLatest {
|
) { if (it) recreate() }
|
||||||
if (it) {
|
settingsViewModel.shouldNavigateBack.collect(
|
||||||
settingsViewModel.setShouldRecreate(false)
|
this,
|
||||||
recreate()
|
resetState = { settingsViewModel.setShouldNavigateBack(false) }
|
||||||
}
|
) { if (it) navigateBack() }
|
||||||
}
|
settingsViewModel.shouldShowResetSettingsDialog.collect(
|
||||||
}
|
this,
|
||||||
}
|
resetState = { settingsViewModel.setShouldShowResetSettingsDialog(false) }
|
||||||
launch {
|
) {
|
||||||
repeatOnLifecycle(Lifecycle.State.CREATED) {
|
if (it) {
|
||||||
settingsViewModel.shouldNavigateBack.collectLatest {
|
ResetSettingsDialogFragment().show(
|
||||||
if (it) {
|
supportFragmentManager,
|
||||||
settingsViewModel.setShouldNavigateBack(false)
|
ResetSettingsDialogFragment.TAG
|
||||||
navigateBack()
|
)
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
launch {
|
|
||||||
repeatOnLifecycle(Lifecycle.State.CREATED) {
|
|
||||||
settingsViewModel.shouldShowResetSettingsDialog.collectLatest {
|
|
||||||
if (it) {
|
|
||||||
settingsViewModel.setShouldShowResetSettingsDialog(false)
|
|
||||||
ResetSettingsDialogFragment().show(
|
|
||||||
supportFragmentManager,
|
|
||||||
ResetSettingsDialogFragment.TAG
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -85,6 +85,10 @@ class SettingsAdapter(
|
|||||||
InputProfileViewHolder(ListItemSettingBinding.inflate(inflater), this)
|
InputProfileViewHolder(ListItemSettingBinding.inflate(inflater), this)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SettingsItem.TYPE_STRING_INPUT -> {
|
||||||
|
StringInputViewHolder(ListItemSettingBinding.inflate(inflater), this)
|
||||||
|
}
|
||||||
|
|
||||||
else -> {
|
else -> {
|
||||||
HeaderViewHolder(ListItemSettingsHeaderBinding.inflate(inflater), this)
|
HeaderViewHolder(ListItemSettingsHeaderBinding.inflate(inflater), this)
|
||||||
}
|
}
|
||||||
@ -392,6 +396,15 @@ class SettingsAdapter(
|
|||||||
popup.show()
|
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 {
|
fun onLongClick(item: SettingsItem, position: Int): Boolean {
|
||||||
SettingsDialogFragment.newInstance(
|
SettingsDialogFragment.newInstance(
|
||||||
settingsViewModel,
|
settingsViewModel,
|
||||||
|
@ -11,13 +11,10 @@ import android.view.View
|
|||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import androidx.fragment.app.DialogFragment
|
import androidx.fragment.app.DialogFragment
|
||||||
import androidx.fragment.app.activityViewModels
|
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.dialog.MaterialAlertDialogBuilder
|
||||||
import com.google.android.material.slider.Slider
|
import com.google.android.material.slider.Slider
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import org.yuzu.yuzu_emu.R
|
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.databinding.DialogSliderBinding
|
||||||
import org.yuzu.yuzu_emu.features.input.NativeInput
|
import org.yuzu.yuzu_emu.features.input.NativeInput
|
||||||
import org.yuzu.yuzu_emu.features.input.model.AnalogDirection
|
import org.yuzu.yuzu_emu.features.input.model.AnalogDirection
|
||||||
@ -27,8 +24,10 @@ 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.SettingsItem
|
||||||
import org.yuzu.yuzu_emu.features.settings.model.view.SingleChoiceSetting
|
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.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.features.settings.model.view.StringSingleChoiceSetting
|
||||||
import org.yuzu.yuzu_emu.utils.ParamPackage
|
import org.yuzu.yuzu_emu.utils.ParamPackage
|
||||||
|
import org.yuzu.yuzu_emu.utils.collect
|
||||||
|
|
||||||
class SettingsDialogFragment : DialogFragment(), DialogInterface.OnClickListener {
|
class SettingsDialogFragment : DialogFragment(), DialogInterface.OnClickListener {
|
||||||
private var type = 0
|
private var type = 0
|
||||||
@ -40,6 +39,7 @@ class SettingsDialogFragment : DialogFragment(), DialogInterface.OnClickListener
|
|||||||
private val settingsViewModel: SettingsViewModel by activityViewModels()
|
private val settingsViewModel: SettingsViewModel by activityViewModels()
|
||||||
|
|
||||||
private lateinit var sliderBinding: DialogSliderBinding
|
private lateinit var sliderBinding: DialogSliderBinding
|
||||||
|
private lateinit var stringInputBinding: DialogEditTextBinding
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
@ -134,6 +134,18 @@ class SettingsDialogFragment : DialogFragment(), DialogInterface.OnClickListener
|
|||||||
.create()
|
.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 -> {
|
SettingsItem.TYPE_STRING_SINGLE_CHOICE -> {
|
||||||
val item = settingsViewModel.clickedItem as StringSingleChoiceSetting
|
val item = settingsViewModel.clickedItem as StringSingleChoiceSetting
|
||||||
MaterialAlertDialogBuilder(requireContext())
|
MaterialAlertDialogBuilder(requireContext())
|
||||||
@ -161,6 +173,7 @@ class SettingsDialogFragment : DialogFragment(), DialogInterface.OnClickListener
|
|||||||
): View? {
|
): View? {
|
||||||
return when (type) {
|
return when (type) {
|
||||||
SettingsItem.TYPE_SLIDER -> sliderBinding.root
|
SettingsItem.TYPE_SLIDER -> sliderBinding.root
|
||||||
|
SettingsItem.TYPE_STRING_INPUT -> stringInputBinding.root
|
||||||
else -> super.onCreateView(inflater, container, savedInstanceState)
|
else -> super.onCreateView(inflater, container, savedInstanceState)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -169,17 +182,11 @@ class SettingsDialogFragment : DialogFragment(), DialogInterface.OnClickListener
|
|||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
when (type) {
|
when (type) {
|
||||||
SettingsItem.TYPE_SLIDER -> {
|
SettingsItem.TYPE_SLIDER -> {
|
||||||
viewLifecycleOwner.lifecycleScope.launch {
|
settingsViewModel.sliderTextValue.collect(viewLifecycleOwner) {
|
||||||
repeatOnLifecycle(Lifecycle.State.CREATED) {
|
sliderBinding.textValue.text = it
|
||||||
settingsViewModel.sliderTextValue.collect {
|
}
|
||||||
sliderBinding.textValue.text = it
|
settingsViewModel.sliderProgress.collect(viewLifecycleOwner) {
|
||||||
}
|
sliderBinding.slider.value = it.toFloat()
|
||||||
}
|
|
||||||
repeatOnLifecycle(Lifecycle.State.CREATED) {
|
|
||||||
settingsViewModel.sliderProgress.collect {
|
|
||||||
sliderBinding.slider.value = it.toFloat()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -209,6 +216,13 @@ class SettingsDialogFragment : DialogFragment(), DialogInterface.OnClickListener
|
|||||||
val sliderSetting = settingsViewModel.clickedItem as SliderSetting
|
val sliderSetting = settingsViewModel.clickedItem as SliderSetting
|
||||||
sliderSetting.setSelectedValue(settingsViewModel.sliderProgress.value)
|
sliderSetting.setSelectedValue(settingsViewModel.sliderProgress.value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
is StringInputSetting -> {
|
||||||
|
val stringInputSetting = settingsViewModel.clickedItem as StringInputSetting
|
||||||
|
stringInputSetting.setSelectedValue(
|
||||||
|
(stringInputBinding.editText.text ?: "").toString()
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
closeDialog()
|
closeDialog()
|
||||||
}
|
}
|
||||||
|
@ -13,21 +13,17 @@ import androidx.core.view.WindowInsetsCompat
|
|||||||
import androidx.core.view.updatePadding
|
import androidx.core.view.updatePadding
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.fragment.app.activityViewModels
|
import androidx.fragment.app.activityViewModels
|
||||||
import androidx.lifecycle.Lifecycle
|
|
||||||
import androidx.lifecycle.lifecycleScope
|
|
||||||
import androidx.lifecycle.repeatOnLifecycle
|
|
||||||
import androidx.navigation.findNavController
|
import androidx.navigation.findNavController
|
||||||
import androidx.navigation.fragment.navArgs
|
import androidx.navigation.fragment.navArgs
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import com.google.android.material.transition.MaterialSharedAxis
|
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.R
|
||||||
import org.yuzu.yuzu_emu.databinding.FragmentSettingsBinding
|
import org.yuzu.yuzu_emu.databinding.FragmentSettingsBinding
|
||||||
import org.yuzu.yuzu_emu.features.input.NativeInput
|
import org.yuzu.yuzu_emu.features.input.NativeInput
|
||||||
import org.yuzu.yuzu_emu.features.settings.model.Settings
|
import org.yuzu.yuzu_emu.features.settings.model.Settings
|
||||||
import org.yuzu.yuzu_emu.fragments.MessageDialogFragment
|
import org.yuzu.yuzu_emu.fragments.MessageDialogFragment
|
||||||
import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
|
import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
|
||||||
|
import org.yuzu.yuzu_emu.utils.collect
|
||||||
|
|
||||||
class SettingsFragment : Fragment() {
|
class SettingsFragment : Fragment() {
|
||||||
private lateinit var presenter: SettingsFragmentPresenter
|
private lateinit var presenter: SettingsFragmentPresenter
|
||||||
@ -63,8 +59,7 @@ class SettingsFragment : Fragment() {
|
|||||||
return binding.root
|
return binding.root
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is using the correct scope, lint is just acting up
|
@SuppressLint("NotifyDataSetChanged")
|
||||||
@SuppressLint("UnsafeRepeatOnLifecycleDetector", "NotifyDataSetChanged")
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
settingsAdapter = SettingsAdapter(this, requireContext())
|
settingsAdapter = SettingsAdapter(this, requireContext())
|
||||||
@ -100,65 +95,37 @@ class SettingsFragment : Fragment() {
|
|||||||
settingsViewModel.setShouldNavigateBack(true)
|
settingsViewModel.setShouldNavigateBack(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
viewLifecycleOwner.lifecycleScope.apply {
|
settingsViewModel.shouldReloadSettingsList.collect(
|
||||||
launch {
|
viewLifecycleOwner,
|
||||||
repeatOnLifecycle(Lifecycle.State.CREATED) {
|
resetState = { settingsViewModel.setShouldReloadSettingsList(false) }
|
||||||
settingsViewModel.shouldReloadSettingsList.collectLatest {
|
) { if (it) presenter.loadSettingsList() }
|
||||||
if (it) {
|
settingsViewModel.adapterItemChanged.collect(
|
||||||
settingsViewModel.setShouldReloadSettingsList(false)
|
viewLifecycleOwner,
|
||||||
presenter.loadSettingsList()
|
resetState = { settingsViewModel.setAdapterItemChanged(-1) }
|
||||||
}
|
) { if (it != -1) settingsAdapter?.notifyItemChanged(it) }
|
||||||
}
|
settingsViewModel.datasetChanged.collect(
|
||||||
}
|
viewLifecycleOwner,
|
||||||
}
|
resetState = { settingsViewModel.setDatasetChanged(false) }
|
||||||
launch {
|
) { if (it) settingsAdapter?.notifyDataSetChanged() }
|
||||||
repeatOnLifecycle(Lifecycle.State.STARTED) {
|
settingsViewModel.reloadListAndNotifyDataset.collect(
|
||||||
settingsViewModel.adapterItemChanged.collect {
|
viewLifecycleOwner,
|
||||||
if (it != -1) {
|
resetState = { settingsViewModel.setReloadListAndNotifyDataset(false) }
|
||||||
settingsAdapter?.notifyItemChanged(it)
|
) { if (it) presenter.loadSettingsList(true) }
|
||||||
settingsViewModel.setAdapterItemChanged(-1)
|
settingsViewModel.shouldShowResetInputDialog.collect(
|
||||||
}
|
viewLifecycleOwner,
|
||||||
}
|
resetState = { settingsViewModel.setShouldShowResetInputDialog(false) }
|
||||||
}
|
) {
|
||||||
}
|
if (it) {
|
||||||
launch {
|
MessageDialogFragment.newInstance(
|
||||||
repeatOnLifecycle(Lifecycle.State.STARTED) {
|
activity = requireActivity(),
|
||||||
settingsViewModel.datasetChanged.collect {
|
titleId = R.string.reset_mapping,
|
||||||
if (it) {
|
descriptionId = R.string.reset_mapping_description,
|
||||||
settingsAdapter?.notifyDataSetChanged()
|
positiveAction = {
|
||||||
settingsViewModel.setDatasetChanged(false)
|
NativeInput.resetControllerMappings(getPlayerIndex())
|
||||||
}
|
settingsViewModel.setReloadListAndNotifyDataset(true)
|
||||||
}
|
},
|
||||||
}
|
negativeAction = {}
|
||||||
}
|
).show(parentFragmentManager, MessageDialogFragment.TAG)
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,6 +23,7 @@ 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
|
||||||
import org.yuzu.yuzu_emu.features.settings.model.Settings.MenuTag
|
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.ShortSetting
|
||||||
|
import org.yuzu.yuzu_emu.features.settings.model.StringSetting
|
||||||
import org.yuzu.yuzu_emu.features.settings.model.view.*
|
import org.yuzu.yuzu_emu.features.settings.model.view.*
|
||||||
import org.yuzu.yuzu_emu.utils.InputHandler
|
import org.yuzu.yuzu_emu.utils.InputHandler
|
||||||
import org.yuzu.yuzu_emu.utils.NativeConfig
|
import org.yuzu.yuzu_emu.utils.NativeConfig
|
||||||
@ -153,6 +154,7 @@ class SettingsFragmentPresenter(
|
|||||||
|
|
||||||
private fun addSystemSettings(sl: ArrayList<SettingsItem>) {
|
private fun addSystemSettings(sl: ArrayList<SettingsItem>) {
|
||||||
sl.apply {
|
sl.apply {
|
||||||
|
add(StringSetting.DEVICE_NAME.key)
|
||||||
add(BooleanSetting.RENDERER_USE_SPEED_LIMIT.key)
|
add(BooleanSetting.RENDERER_USE_SPEED_LIMIT.key)
|
||||||
add(ShortSetting.RENDERER_SPEED_LIMIT.key)
|
add(ShortSetting.RENDERER_SPEED_LIMIT.key)
|
||||||
add(BooleanSetting.USE_DOCKED_MODE.key)
|
add(BooleanSetting.USE_DOCKED_MODE.key)
|
||||||
@ -778,7 +780,7 @@ class SettingsFragmentPresenter(
|
|||||||
playerIndex: Int,
|
playerIndex: Int,
|
||||||
paramName: String,
|
paramName: String,
|
||||||
stick: NativeAnalog,
|
stick: NativeAnalog,
|
||||||
defaultValue: Int
|
defaultValue: Float
|
||||||
): AbstractIntSetting =
|
): AbstractIntSetting =
|
||||||
object : AbstractIntSetting {
|
object : AbstractIntSetting {
|
||||||
val params get() = NativeInput.getStickParam(playerIndex, stick)
|
val params get() = NativeInput.getStickParam(playerIndex, stick)
|
||||||
@ -786,7 +788,7 @@ class SettingsFragmentPresenter(
|
|||||||
override val key = ""
|
override val key = ""
|
||||||
|
|
||||||
override fun getInt(needsGlobal: Boolean): Int =
|
override fun getInt(needsGlobal: Boolean): Int =
|
||||||
(params.get(paramName, 0.15f) * 100).toInt()
|
(params.get(paramName, defaultValue) * 100).toInt()
|
||||||
|
|
||||||
override fun setInt(value: Int) {
|
override fun setInt(value: Int) {
|
||||||
val tempParams = params
|
val tempParams = params
|
||||||
@ -794,12 +796,12 @@ class SettingsFragmentPresenter(
|
|||||||
NativeInput.setStickParam(playerIndex, stick, tempParams)
|
NativeInput.setStickParam(playerIndex, stick, tempParams)
|
||||||
}
|
}
|
||||||
|
|
||||||
override val defaultValue = defaultValue
|
override val defaultValue = (defaultValue * 100).toInt()
|
||||||
|
|
||||||
override fun getValueAsString(needsGlobal: Boolean): String =
|
override fun getValueAsString(needsGlobal: Boolean): String =
|
||||||
getInt(needsGlobal).toString()
|
getInt(needsGlobal).toString()
|
||||||
|
|
||||||
override fun reset() = setInt(defaultValue)
|
override fun reset() = setInt(this.defaultValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getExtraStickSettings(
|
private fun getExtraStickSettings(
|
||||||
@ -809,11 +811,11 @@ class SettingsFragmentPresenter(
|
|||||||
val stickIsController =
|
val stickIsController =
|
||||||
NativeInput.isController(NativeInput.getStickParam(playerIndex, nativeAnalog))
|
NativeInput.isController(NativeInput.getStickParam(playerIndex, nativeAnalog))
|
||||||
val modifierRangeSetting =
|
val modifierRangeSetting =
|
||||||
getStickIntSettingFromParam(playerIndex, "modifier_scale", nativeAnalog, 50)
|
getStickIntSettingFromParam(playerIndex, "modifier_scale", nativeAnalog, 0.5f)
|
||||||
val stickRangeSetting =
|
val stickRangeSetting =
|
||||||
getStickIntSettingFromParam(playerIndex, "range", nativeAnalog, 95)
|
getStickIntSettingFromParam(playerIndex, "range", nativeAnalog, 0.95f)
|
||||||
val stickDeadzoneSetting =
|
val stickDeadzoneSetting =
|
||||||
getStickIntSettingFromParam(playerIndex, "deadzone", nativeAnalog, 15)
|
getStickIntSettingFromParam(playerIndex, "deadzone", nativeAnalog, 0.15f)
|
||||||
|
|
||||||
val out = mutableListOf<SettingsItem>().apply {
|
val out = mutableListOf<SettingsItem>().apply {
|
||||||
if (stickIsController) {
|
if (stickIsController) {
|
||||||
|
@ -15,19 +15,17 @@ import androidx.core.view.updatePadding
|
|||||||
import androidx.core.widget.doOnTextChanged
|
import androidx.core.widget.doOnTextChanged
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.fragment.app.activityViewModels
|
import androidx.fragment.app.activityViewModels
|
||||||
import androidx.lifecycle.Lifecycle
|
|
||||||
import androidx.lifecycle.lifecycleScope
|
|
||||||
import androidx.lifecycle.repeatOnLifecycle
|
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import com.google.android.material.divider.MaterialDividerItemDecoration
|
import com.google.android.material.divider.MaterialDividerItemDecoration
|
||||||
import com.google.android.material.transition.MaterialSharedAxis
|
import com.google.android.material.transition.MaterialSharedAxis
|
||||||
import info.debatty.java.stringsimilarity.Cosine
|
import info.debatty.java.stringsimilarity.Cosine
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import org.yuzu.yuzu_emu.R
|
import org.yuzu.yuzu_emu.R
|
||||||
import org.yuzu.yuzu_emu.databinding.FragmentSettingsSearchBinding
|
import org.yuzu.yuzu_emu.databinding.FragmentSettingsSearchBinding
|
||||||
import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem
|
import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem
|
||||||
import org.yuzu.yuzu_emu.utils.NativeConfig
|
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.ViewUtils.updateMargins
|
||||||
|
import org.yuzu.yuzu_emu.utils.collect
|
||||||
|
|
||||||
class SettingsSearchFragment : Fragment() {
|
class SettingsSearchFragment : Fragment() {
|
||||||
private var _binding: FragmentSettingsSearchBinding? = null
|
private var _binding: FragmentSettingsSearchBinding? = null
|
||||||
@ -83,14 +81,10 @@ class SettingsSearchFragment : Fragment() {
|
|||||||
search()
|
search()
|
||||||
binding.settingsList.smoothScrollToPosition(0)
|
binding.settingsList.smoothScrollToPosition(0)
|
||||||
}
|
}
|
||||||
viewLifecycleOwner.lifecycleScope.launch {
|
settingsViewModel.shouldReloadSettingsList.collect(viewLifecycleOwner) {
|
||||||
repeatOnLifecycle(Lifecycle.State.CREATED) {
|
if (it) {
|
||||||
settingsViewModel.shouldReloadSettingsList.collect {
|
settingsViewModel.setShouldReloadSettingsList(false)
|
||||||
if (it) {
|
search()
|
||||||
settingsViewModel.setShouldReloadSettingsList(false)
|
|
||||||
search()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -106,10 +100,9 @@ class SettingsSearchFragment : Fragment() {
|
|||||||
|
|
||||||
private fun search() {
|
private fun search() {
|
||||||
val searchTerm = binding.searchText.text.toString().lowercase()
|
val searchTerm = binding.searchText.text.toString().lowercase()
|
||||||
binding.clearButton.visibility =
|
binding.clearButton.setVisible(visible = searchTerm.isNotEmpty(), gone = false)
|
||||||
if (searchTerm.isEmpty()) View.INVISIBLE else View.VISIBLE
|
|
||||||
if (searchTerm.isEmpty()) {
|
if (searchTerm.isEmpty()) {
|
||||||
binding.noResultsView.visibility = View.VISIBLE
|
binding.noResultsView.setVisible(visible = false, gone = false)
|
||||||
settingsAdapter?.submitList(emptyList())
|
settingsAdapter?.submitList(emptyList())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -136,8 +129,7 @@ class SettingsSearchFragment : Fragment() {
|
|||||||
optionalSetting
|
optionalSetting
|
||||||
}
|
}
|
||||||
settingsAdapter?.submitList(sortedList)
|
settingsAdapter?.submitList(sortedList)
|
||||||
binding.noResultsView.visibility =
|
binding.noResultsView.setVisible(visible = sortedList.isEmpty(), gone = false)
|
||||||
if (sortedList.isEmpty()) View.VISIBLE else View.INVISIBLE
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun focusSearch() {
|
private fun focusSearch() {
|
||||||
|
@ -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.DateTimeSetting
|
||||||
import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem
|
import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem
|
||||||
import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter
|
import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter
|
||||||
import org.yuzu.yuzu_emu.utils.NativeConfig
|
import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
|
||||||
|
|
||||||
class DateTimeViewHolder(val binding: ListItemSettingBinding, adapter: SettingsAdapter) :
|
class DateTimeViewHolder(val binding: ListItemSettingBinding, adapter: SettingsAdapter) :
|
||||||
SettingViewHolder(binding.root, adapter) {
|
SettingViewHolder(binding.root, adapter) {
|
||||||
@ -22,27 +22,16 @@ class DateTimeViewHolder(val binding: ListItemSettingBinding, adapter: SettingsA
|
|||||||
override fun bind(item: SettingsItem) {
|
override fun bind(item: SettingsItem) {
|
||||||
setting = item as DateTimeSetting
|
setting = item as DateTimeSetting
|
||||||
binding.textSettingName.text = item.title
|
binding.textSettingName.text = item.title
|
||||||
if (setting.description.isNotEmpty()) {
|
binding.textSettingDescription.setVisible(item.description.isNotEmpty())
|
||||||
binding.textSettingDescription.text = item.description
|
binding.textSettingDescription.text = item.description
|
||||||
binding.textSettingDescription.visibility = View.VISIBLE
|
binding.textSettingValue.setVisible(true)
|
||||||
} else {
|
|
||||||
binding.textSettingDescription.visibility = View.GONE
|
|
||||||
}
|
|
||||||
|
|
||||||
binding.textSettingValue.visibility = View.VISIBLE
|
|
||||||
val epochTime = setting.getValue()
|
val epochTime = setting.getValue()
|
||||||
val instant = Instant.ofEpochMilli(epochTime * 1000)
|
val instant = Instant.ofEpochMilli(epochTime * 1000)
|
||||||
val zonedTime = ZonedDateTime.ofInstant(instant, ZoneId.of("UTC"))
|
val zonedTime = ZonedDateTime.ofInstant(instant, ZoneId.of("UTC"))
|
||||||
val dateFormatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM)
|
val dateFormatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM)
|
||||||
binding.textSettingValue.text = dateFormatter.format(zonedTime)
|
binding.textSettingValue.text = dateFormatter.format(zonedTime)
|
||||||
|
|
||||||
binding.buttonClear.visibility = if (setting.setting.global ||
|
binding.buttonClear.setVisible(setting.clearable)
|
||||||
!NativeConfig.isPerGameConfigLoaded()
|
|
||||||
) {
|
|
||||||
View.GONE
|
|
||||||
} else {
|
|
||||||
View.VISIBLE
|
|
||||||
}
|
|
||||||
binding.buttonClear.setOnClickListener {
|
binding.buttonClear.setOnClickListener {
|
||||||
adapter.onClearClick(setting, bindingAdapterPosition)
|
adapter.onClearClick(setting, bindingAdapterPosition)
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,7 @@ 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.model.view.SettingsItem
|
||||||
import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter
|
import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter
|
||||||
import org.yuzu.yuzu_emu.R
|
import org.yuzu.yuzu_emu.R
|
||||||
|
import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
|
||||||
|
|
||||||
class InputProfileViewHolder(val binding: ListItemSettingBinding, adapter: SettingsAdapter) :
|
class InputProfileViewHolder(val binding: ListItemSettingBinding, adapter: SettingsAdapter) :
|
||||||
SettingViewHolder(binding.root, adapter) {
|
SettingViewHolder(binding.root, adapter) {
|
||||||
@ -20,10 +21,10 @@ class InputProfileViewHolder(val binding: ListItemSettingBinding, adapter: Setti
|
|||||||
binding.textSettingValue.text =
|
binding.textSettingValue.text =
|
||||||
setting.getCurrentProfile().ifEmpty { binding.root.context.getString(R.string.not_set) }
|
setting.getCurrentProfile().ifEmpty { binding.root.context.getString(R.string.not_set) }
|
||||||
|
|
||||||
binding.textSettingDescription.visibility = View.GONE
|
binding.textSettingDescription.setVisible(false)
|
||||||
binding.buttonClear.visibility = View.GONE
|
binding.buttonClear.setVisible(false)
|
||||||
binding.icon.visibility = View.GONE
|
binding.icon.setVisible(false)
|
||||||
binding.buttonClear.visibility = View.GONE
|
binding.buttonClear.setVisible(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onClick(clicked: View) =
|
override fun onClick(clicked: View) =
|
||||||
|
@ -12,6 +12,7 @@ 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.ModifierInputSetting
|
||||||
import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem
|
import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem
|
||||||
import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter
|
import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter
|
||||||
|
import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
|
||||||
|
|
||||||
class InputViewHolder(val binding: ListItemSettingInputBinding, adapter: SettingsAdapter) :
|
class InputViewHolder(val binding: ListItemSettingInputBinding, adapter: SettingsAdapter) :
|
||||||
SettingViewHolder(binding.root, adapter) {
|
SettingViewHolder(binding.root, adapter) {
|
||||||
@ -22,38 +23,26 @@ class InputViewHolder(val binding: ListItemSettingInputBinding, adapter: Setting
|
|||||||
binding.textSettingName.text = setting.title
|
binding.textSettingName.text = setting.title
|
||||||
binding.textSettingValue.text = setting.getSelectedValue()
|
binding.textSettingValue.text = setting.getSelectedValue()
|
||||||
|
|
||||||
binding.buttonOptions.visibility = when (item) {
|
when (item) {
|
||||||
is AnalogInputSetting -> {
|
is AnalogInputSetting -> {
|
||||||
val param = NativeInput.getStickParam(item.playerIndex, item.nativeAnalog)
|
val param = NativeInput.getStickParam(item.playerIndex, item.nativeAnalog)
|
||||||
if (
|
binding.buttonOptions.setVisible(
|
||||||
param.get("engine", "") == "analog_from_button" ||
|
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 -> {
|
is ButtonInputSetting -> {
|
||||||
val param = NativeInput.getButtonParam(item.playerIndex, item.nativeButton)
|
val param = NativeInput.getButtonParam(item.playerIndex, item.nativeButton)
|
||||||
if (
|
binding.buttonOptions.setVisible(
|
||||||
param.has("code") || param.has("button") || param.has("hat") ||
|
param.has("code") || param.has("button") || param.has("hat") ||
|
||||||
param.has("axis")
|
param.has("axis")
|
||||||
) {
|
)
|
||||||
View.VISIBLE
|
|
||||||
} else {
|
|
||||||
View.GONE
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
is ModifierInputSetting -> {
|
is ModifierInputSetting -> {
|
||||||
val params = NativeInput.getStickParam(item.playerIndex, item.nativeAnalog)
|
val params = NativeInput.getStickParam(item.playerIndex, item.nativeAnalog)
|
||||||
if (params.has("modifier")) {
|
binding.buttonOptions.setVisible(params.has("modifier"))
|
||||||
View.VISIBLE
|
|
||||||
} else {
|
|
||||||
View.GONE
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,6 +9,7 @@ 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.RunnableSetting
|
||||||
import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem
|
import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem
|
||||||
import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter
|
import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter
|
||||||
|
import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
|
||||||
|
|
||||||
class RunnableViewHolder(val binding: ListItemSettingBinding, adapter: SettingsAdapter) :
|
class RunnableViewHolder(val binding: ListItemSettingBinding, adapter: SettingsAdapter) :
|
||||||
SettingViewHolder(binding.root, adapter) {
|
SettingViewHolder(binding.root, adapter) {
|
||||||
@ -16,8 +17,8 @@ class RunnableViewHolder(val binding: ListItemSettingBinding, adapter: SettingsA
|
|||||||
|
|
||||||
override fun bind(item: SettingsItem) {
|
override fun bind(item: SettingsItem) {
|
||||||
setting = item as RunnableSetting
|
setting = item as RunnableSetting
|
||||||
|
binding.icon.setVisible(setting.iconId != 0)
|
||||||
if (setting.iconId != 0) {
|
if (setting.iconId != 0) {
|
||||||
binding.icon.visibility = View.VISIBLE
|
|
||||||
binding.icon.setImageDrawable(
|
binding.icon.setImageDrawable(
|
||||||
ResourcesCompat.getDrawable(
|
ResourcesCompat.getDrawable(
|
||||||
binding.icon.resources,
|
binding.icon.resources,
|
||||||
@ -25,19 +26,13 @@ class RunnableViewHolder(val binding: ListItemSettingBinding, adapter: SettingsA
|
|||||||
binding.icon.context.theme
|
binding.icon.context.theme
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
} else {
|
|
||||||
binding.icon.visibility = View.GONE
|
|
||||||
}
|
}
|
||||||
|
|
||||||
binding.textSettingName.text = setting.title
|
binding.textSettingName.text = setting.title
|
||||||
if (setting.description.isNotEmpty()) {
|
binding.textSettingDescription.setVisible(setting.description.isNotEmpty())
|
||||||
binding.textSettingDescription.setText(item.descriptionId)
|
binding.textSettingDescription.text = item.description
|
||||||
binding.textSettingDescription.visibility = View.VISIBLE
|
binding.textSettingValue.setVisible(false)
|
||||||
} else {
|
binding.buttonClear.setVisible(false)
|
||||||
binding.textSettingDescription.visibility = View.GONE
|
|
||||||
}
|
|
||||||
binding.textSettingValue.visibility = View.GONE
|
|
||||||
binding.buttonClear.visibility = View.GONE
|
|
||||||
|
|
||||||
setStyle(setting.isEditable, binding)
|
setStyle(setting.isEditable, binding)
|
||||||
}
|
}
|
||||||
|
@ -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.SingleChoiceSetting
|
||||||
import org.yuzu.yuzu_emu.features.settings.model.view.StringSingleChoiceSetting
|
import org.yuzu.yuzu_emu.features.settings.model.view.StringSingleChoiceSetting
|
||||||
import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter
|
import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter
|
||||||
import org.yuzu.yuzu_emu.utils.NativeConfig
|
import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
|
||||||
|
|
||||||
class SingleChoiceViewHolder(val binding: ListItemSettingBinding, adapter: SettingsAdapter) :
|
class SingleChoiceViewHolder(val binding: ListItemSettingBinding, adapter: SettingsAdapter) :
|
||||||
SettingViewHolder(binding.root, adapter) {
|
SettingViewHolder(binding.root, adapter) {
|
||||||
@ -19,14 +19,10 @@ class SingleChoiceViewHolder(val binding: ListItemSettingBinding, adapter: Setti
|
|||||||
override fun bind(item: SettingsItem) {
|
override fun bind(item: SettingsItem) {
|
||||||
setting = item
|
setting = item
|
||||||
binding.textSettingName.text = setting.title
|
binding.textSettingName.text = setting.title
|
||||||
if (item.description.isNotEmpty()) {
|
binding.textSettingDescription.setVisible(item.description.isNotEmpty())
|
||||||
binding.textSettingDescription.text = item.description
|
binding.textSettingDescription.text = item.description
|
||||||
binding.textSettingDescription.visibility = View.VISIBLE
|
|
||||||
} else {
|
|
||||||
binding.textSettingDescription.visibility = View.GONE
|
|
||||||
}
|
|
||||||
|
|
||||||
binding.textSettingValue.visibility = View.VISIBLE
|
binding.textSettingValue.setVisible(true)
|
||||||
when (item) {
|
when (item) {
|
||||||
is SingleChoiceSetting -> {
|
is SingleChoiceSetting -> {
|
||||||
val resMgr = binding.textSettingValue.context.resources
|
val resMgr = binding.textSettingValue.context.resources
|
||||||
@ -48,16 +44,10 @@ class SingleChoiceViewHolder(val binding: ListItemSettingBinding, adapter: Setti
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (binding.textSettingValue.text.isEmpty()) {
|
if (binding.textSettingValue.text.isEmpty()) {
|
||||||
binding.textSettingValue.visibility = View.GONE
|
binding.textSettingValue.setVisible(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
binding.buttonClear.visibility = if (setting.setting.global ||
|
binding.buttonClear.setVisible(setting.clearable)
|
||||||
!NativeConfig.isPerGameConfigLoaded()
|
|
||||||
) {
|
|
||||||
View.GONE
|
|
||||||
} else {
|
|
||||||
View.VISIBLE
|
|
||||||
}
|
|
||||||
binding.buttonClear.setOnClickListener {
|
binding.buttonClear.setOnClickListener {
|
||||||
adapter.onClearClick(setting, bindingAdapterPosition)
|
adapter.onClearClick(setting, bindingAdapterPosition)
|
||||||
}
|
}
|
||||||
|
@ -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.SettingsItem
|
||||||
import org.yuzu.yuzu_emu.features.settings.model.view.SliderSetting
|
import org.yuzu.yuzu_emu.features.settings.model.view.SliderSetting
|
||||||
import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter
|
import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter
|
||||||
import org.yuzu.yuzu_emu.utils.NativeConfig
|
import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
|
||||||
|
|
||||||
class SliderViewHolder(val binding: ListItemSettingBinding, adapter: SettingsAdapter) :
|
class SliderViewHolder(val binding: ListItemSettingBinding, adapter: SettingsAdapter) :
|
||||||
SettingViewHolder(binding.root, adapter) {
|
SettingViewHolder(binding.root, adapter) {
|
||||||
@ -18,26 +18,16 @@ class SliderViewHolder(val binding: ListItemSettingBinding, adapter: SettingsAda
|
|||||||
override fun bind(item: SettingsItem) {
|
override fun bind(item: SettingsItem) {
|
||||||
setting = item as SliderSetting
|
setting = item as SliderSetting
|
||||||
binding.textSettingName.text = setting.title
|
binding.textSettingName.text = setting.title
|
||||||
if (item.description.isNotEmpty()) {
|
binding.textSettingDescription.setVisible(item.description.isNotEmpty())
|
||||||
binding.textSettingDescription.text = setting.description
|
binding.textSettingDescription.text = setting.description
|
||||||
binding.textSettingDescription.visibility = View.VISIBLE
|
binding.textSettingValue.setVisible(true)
|
||||||
} else {
|
|
||||||
binding.textSettingDescription.visibility = View.GONE
|
|
||||||
}
|
|
||||||
binding.textSettingValue.visibility = View.VISIBLE
|
|
||||||
binding.textSettingValue.text = String.format(
|
binding.textSettingValue.text = String.format(
|
||||||
binding.textSettingValue.context.getString(R.string.value_with_units),
|
binding.textSettingValue.context.getString(R.string.value_with_units),
|
||||||
setting.getSelectedValue(),
|
setting.getSelectedValue(),
|
||||||
setting.units
|
setting.units
|
||||||
)
|
)
|
||||||
|
|
||||||
binding.buttonClear.visibility = if (setting.setting.global ||
|
binding.buttonClear.setVisible(setting.clearable)
|
||||||
!NativeConfig.isPerGameConfigLoaded()
|
|
||||||
) {
|
|
||||||
View.GONE
|
|
||||||
} else {
|
|
||||||
View.VISIBLE
|
|
||||||
}
|
|
||||||
binding.buttonClear.setOnClickListener {
|
binding.buttonClear.setOnClickListener {
|
||||||
adapter.onClearClick(setting, bindingAdapterPosition)
|
adapter.onClearClick(setting, bindingAdapterPosition)
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,45 @@
|
|||||||
|
// 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
|
||||||
|
}
|
||||||
|
}
|
@ -9,6 +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.SettingsItem
|
||||||
import org.yuzu.yuzu_emu.features.settings.model.view.SubmenuSetting
|
import org.yuzu.yuzu_emu.features.settings.model.view.SubmenuSetting
|
||||||
import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter
|
import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter
|
||||||
|
import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
|
||||||
|
|
||||||
class SubmenuViewHolder(val binding: ListItemSettingBinding, adapter: SettingsAdapter) :
|
class SubmenuViewHolder(val binding: ListItemSettingBinding, adapter: SettingsAdapter) :
|
||||||
SettingViewHolder(binding.root, adapter) {
|
SettingViewHolder(binding.root, adapter) {
|
||||||
@ -16,8 +17,8 @@ class SubmenuViewHolder(val binding: ListItemSettingBinding, adapter: SettingsAd
|
|||||||
|
|
||||||
override fun bind(item: SettingsItem) {
|
override fun bind(item: SettingsItem) {
|
||||||
setting = item as SubmenuSetting
|
setting = item as SubmenuSetting
|
||||||
|
binding.icon.setVisible(setting.iconId != 0)
|
||||||
if (setting.iconId != 0) {
|
if (setting.iconId != 0) {
|
||||||
binding.icon.visibility = View.VISIBLE
|
|
||||||
binding.icon.setImageDrawable(
|
binding.icon.setImageDrawable(
|
||||||
ResourcesCompat.getDrawable(
|
ResourcesCompat.getDrawable(
|
||||||
binding.icon.resources,
|
binding.icon.resources,
|
||||||
@ -25,19 +26,13 @@ class SubmenuViewHolder(val binding: ListItemSettingBinding, adapter: SettingsAd
|
|||||||
binding.icon.context.theme
|
binding.icon.context.theme
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
} else {
|
|
||||||
binding.icon.visibility = View.GONE
|
|
||||||
}
|
}
|
||||||
|
|
||||||
binding.textSettingName.text = setting.title
|
binding.textSettingName.text = setting.title
|
||||||
if (setting.description.isNotEmpty()) {
|
binding.textSettingDescription.setVisible(setting.description.isNotEmpty())
|
||||||
binding.textSettingDescription.text = setting.description
|
binding.textSettingDescription.text = setting.description
|
||||||
binding.textSettingDescription.visibility = View.VISIBLE
|
binding.textSettingValue.setVisible(false)
|
||||||
} else {
|
binding.buttonClear.setVisible(false)
|
||||||
binding.textSettingDescription.visibility = View.GONE
|
|
||||||
}
|
|
||||||
binding.textSettingValue.visibility = View.GONE
|
|
||||||
binding.buttonClear.visibility = View.GONE
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onClick(clicked: View) {
|
override fun onClick(clicked: View) {
|
||||||
|
@ -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.SettingsItem
|
||||||
import org.yuzu.yuzu_emu.features.settings.model.view.SwitchSetting
|
import org.yuzu.yuzu_emu.features.settings.model.view.SwitchSetting
|
||||||
import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter
|
import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter
|
||||||
import org.yuzu.yuzu_emu.utils.NativeConfig
|
import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
|
||||||
|
|
||||||
class SwitchSettingViewHolder(val binding: ListItemSettingSwitchBinding, adapter: SettingsAdapter) :
|
class SwitchSettingViewHolder(val binding: ListItemSettingSwitchBinding, adapter: SettingsAdapter) :
|
||||||
SettingViewHolder(binding.root, adapter) {
|
SettingViewHolder(binding.root, adapter) {
|
||||||
@ -19,12 +19,8 @@ class SwitchSettingViewHolder(val binding: ListItemSettingSwitchBinding, adapter
|
|||||||
override fun bind(item: SettingsItem) {
|
override fun bind(item: SettingsItem) {
|
||||||
setting = item as SwitchSetting
|
setting = item as SwitchSetting
|
||||||
binding.textSettingName.text = setting.title
|
binding.textSettingName.text = setting.title
|
||||||
if (setting.description.isNotEmpty()) {
|
binding.textSettingDescription.setVisible(setting.description.isNotEmpty())
|
||||||
binding.textSettingDescription.text = setting.description
|
binding.textSettingDescription.text = setting.description
|
||||||
binding.textSettingDescription.visibility = View.VISIBLE
|
|
||||||
} else {
|
|
||||||
binding.textSettingDescription.visibility = View.GONE
|
|
||||||
}
|
|
||||||
|
|
||||||
binding.switchWidget.setOnCheckedChangeListener(null)
|
binding.switchWidget.setOnCheckedChangeListener(null)
|
||||||
binding.switchWidget.isChecked = setting.getIsChecked(setting.needsRuntimeGlobal)
|
binding.switchWidget.isChecked = setting.getIsChecked(setting.needsRuntimeGlobal)
|
||||||
@ -32,13 +28,7 @@ class SwitchSettingViewHolder(val binding: ListItemSettingSwitchBinding, adapter
|
|||||||
adapter.onBooleanClick(setting, binding.switchWidget.isChecked, bindingAdapterPosition)
|
adapter.onBooleanClick(setting, binding.switchWidget.isChecked, bindingAdapterPosition)
|
||||||
}
|
}
|
||||||
|
|
||||||
binding.buttonClear.visibility = if (setting.setting.global ||
|
binding.buttonClear.setVisible(setting.clearable)
|
||||||
!NativeConfig.isPerGameConfigLoaded()
|
|
||||||
) {
|
|
||||||
View.GONE
|
|
||||||
} else {
|
|
||||||
View.VISIBLE
|
|
||||||
}
|
|
||||||
binding.buttonClear.setOnClickListener {
|
binding.buttonClear.setOnClickListener {
|
||||||
adapter.onClearClick(setting, bindingAdapterPosition)
|
adapter.onClearClick(setting, bindingAdapterPosition)
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,6 @@
|
|||||||
|
|
||||||
package org.yuzu.yuzu_emu.fragments
|
package org.yuzu.yuzu_emu.fragments
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
@ -16,9 +15,6 @@ import androidx.core.view.updatePadding
|
|||||||
import androidx.documentfile.provider.DocumentFile
|
import androidx.documentfile.provider.DocumentFile
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.fragment.app.activityViewModels
|
import androidx.fragment.app.activityViewModels
|
||||||
import androidx.lifecycle.Lifecycle
|
|
||||||
import androidx.lifecycle.lifecycleScope
|
|
||||||
import androidx.lifecycle.repeatOnLifecycle
|
|
||||||
import androidx.navigation.findNavController
|
import androidx.navigation.findNavController
|
||||||
import androidx.navigation.fragment.navArgs
|
import androidx.navigation.fragment.navArgs
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
@ -32,6 +28,7 @@ import org.yuzu.yuzu_emu.model.HomeViewModel
|
|||||||
import org.yuzu.yuzu_emu.utils.AddonUtil
|
import org.yuzu.yuzu_emu.utils.AddonUtil
|
||||||
import org.yuzu.yuzu_emu.utils.FileUtil.copyFilesTo
|
import org.yuzu.yuzu_emu.utils.FileUtil.copyFilesTo
|
||||||
import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
|
import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
|
||||||
|
import org.yuzu.yuzu_emu.utils.collect
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
class AddonsFragment : Fragment() {
|
class AddonsFragment : Fragment() {
|
||||||
@ -60,8 +57,6 @@ class AddonsFragment : Fragment() {
|
|||||||
return binding.root
|
return binding.root
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is using the correct scope, lint is just acting up
|
|
||||||
@SuppressLint("UnsafeRepeatOnLifecycleDetector")
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
homeViewModel.setNavigationVisibility(visible = false, animated = false)
|
homeViewModel.setNavigationVisibility(visible = false, animated = false)
|
||||||
@ -78,57 +73,41 @@ class AddonsFragment : Fragment() {
|
|||||||
adapter = AddonAdapter(addonViewModel)
|
adapter = AddonAdapter(addonViewModel)
|
||||||
}
|
}
|
||||||
|
|
||||||
viewLifecycleOwner.lifecycleScope.apply {
|
addonViewModel.addonList.collect(viewLifecycleOwner) {
|
||||||
launch {
|
(binding.listAddons.adapter as AddonAdapter).submitList(it)
|
||||||
repeatOnLifecycle(Lifecycle.State.STARTED) {
|
}
|
||||||
addonViewModel.addonList.collect {
|
addonViewModel.showModInstallPicker.collect(
|
||||||
(binding.listAddons.adapter as AddonAdapter).submitList(it)
|
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)
|
||||||
}
|
}
|
||||||
launch {
|
}
|
||||||
repeatOnLifecycle(Lifecycle.State.STARTED) {
|
addonViewModel.addonToDelete.collect(
|
||||||
addonViewModel.showModInstallPicker.collect {
|
viewLifecycleOwner,
|
||||||
if (it) {
|
resetState = { addonViewModel.setAddonToDelete(null) }
|
||||||
installAddon.launch(Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).data)
|
) {
|
||||||
addonViewModel.showModInstallPicker(false)
|
if (it != null) {
|
||||||
}
|
MessageDialogFragment.newInstance(
|
||||||
}
|
requireActivity(),
|
||||||
}
|
titleId = R.string.confirm_uninstall,
|
||||||
}
|
descriptionId = R.string.confirm_uninstall_description,
|
||||||
launch {
|
positiveAction = { addonViewModel.onDeleteAddon(it) },
|
||||||
repeatOnLifecycle(Lifecycle.State.STARTED) {
|
negativeAction = {}
|
||||||
addonViewModel.showModNoticeDialog.collect {
|
).show(parentFragmentManager, MessageDialogFragment.TAG)
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,47 @@
|
|||||||
|
// 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -3,7 +3,6 @@
|
|||||||
|
|
||||||
package org.yuzu.yuzu_emu.fragments
|
package org.yuzu.yuzu_emu.fragments
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
@ -14,9 +13,6 @@ import androidx.core.view.WindowInsetsCompat
|
|||||||
import androidx.core.view.updatePadding
|
import androidx.core.view.updatePadding
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.fragment.app.activityViewModels
|
import androidx.fragment.app.activityViewModels
|
||||||
import androidx.lifecycle.Lifecycle
|
|
||||||
import androidx.lifecycle.lifecycleScope
|
|
||||||
import androidx.lifecycle.repeatOnLifecycle
|
|
||||||
import androidx.navigation.findNavController
|
import androidx.navigation.findNavController
|
||||||
import androidx.navigation.fragment.navArgs
|
import androidx.navigation.fragment.navArgs
|
||||||
import androidx.recyclerview.widget.GridLayoutManager
|
import androidx.recyclerview.widget.GridLayoutManager
|
||||||
@ -35,6 +31,7 @@ import org.yuzu.yuzu_emu.utils.FileUtil
|
|||||||
import org.yuzu.yuzu_emu.utils.GpuDriverHelper
|
import org.yuzu.yuzu_emu.utils.GpuDriverHelper
|
||||||
import org.yuzu.yuzu_emu.utils.NativeConfig
|
import org.yuzu.yuzu_emu.utils.NativeConfig
|
||||||
import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
|
import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
|
||||||
|
import org.yuzu.yuzu_emu.utils.collect
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
|
|
||||||
@ -63,8 +60,6 @@ class DriverManagerFragment : Fragment() {
|
|||||||
return binding.root
|
return binding.root
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is using the correct scope, lint is just acting up
|
|
||||||
@SuppressLint("UnsafeRepeatOnLifecycleDetector")
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
homeViewModel.setNavigationVisibility(visible = false, animated = true)
|
homeViewModel.setNavigationVisibility(visible = false, animated = true)
|
||||||
@ -89,15 +84,8 @@ class DriverManagerFragment : Fragment() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
viewLifecycleOwner.lifecycleScope.apply {
|
driverViewModel.showClearButton.collect(viewLifecycleOwner) {
|
||||||
launch {
|
binding.toolbarDrivers.menu.findItem(R.id.menu_driver_use_global).isVisible = it
|
||||||
repeatOnLifecycle(Lifecycle.State.STARTED) {
|
|
||||||
driverViewModel.showClearButton.collect {
|
|
||||||
binding.toolbarDrivers.menu
|
|
||||||
.findItem(R.id.menu_driver_use_global).isVisible = it
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,14 +10,11 @@ import android.view.View
|
|||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import androidx.fragment.app.DialogFragment
|
import androidx.fragment.app.DialogFragment
|
||||||
import androidx.fragment.app.activityViewModels
|
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.dialog.MaterialAlertDialogBuilder
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import org.yuzu.yuzu_emu.R
|
import org.yuzu.yuzu_emu.R
|
||||||
import org.yuzu.yuzu_emu.databinding.DialogProgressBarBinding
|
import org.yuzu.yuzu_emu.databinding.DialogProgressBarBinding
|
||||||
import org.yuzu.yuzu_emu.model.DriverViewModel
|
import org.yuzu.yuzu_emu.model.DriverViewModel
|
||||||
|
import org.yuzu.yuzu_emu.utils.collect
|
||||||
|
|
||||||
class DriversLoadingDialogFragment : DialogFragment() {
|
class DriversLoadingDialogFragment : DialogFragment() {
|
||||||
private val driverViewModel: DriverViewModel by activityViewModels()
|
private val driverViewModel: DriverViewModel by activityViewModels()
|
||||||
@ -44,13 +41,7 @@ class DriversLoadingDialogFragment : DialogFragment() {
|
|||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
viewLifecycleOwner.lifecycleScope.apply {
|
driverViewModel.isInteractionAllowed.collect(viewLifecycleOwner) { if (it) dismiss() }
|
||||||
launch {
|
|
||||||
repeatOnLifecycle(Lifecycle.State.RESUMED) {
|
|
||||||
driverViewModel.isInteractionAllowed.collect { if (it) dismiss() }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
@ -32,9 +32,6 @@ import androidx.drawerlayout.widget.DrawerLayout
|
|||||||
import androidx.drawerlayout.widget.DrawerLayout.DrawerListener
|
import androidx.drawerlayout.widget.DrawerLayout.DrawerListener
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.fragment.app.activityViewModels
|
import androidx.fragment.app.activityViewModels
|
||||||
import androidx.lifecycle.Lifecycle
|
|
||||||
import androidx.lifecycle.lifecycleScope
|
|
||||||
import androidx.lifecycle.repeatOnLifecycle
|
|
||||||
import androidx.navigation.findNavController
|
import androidx.navigation.findNavController
|
||||||
import androidx.navigation.fragment.navArgs
|
import androidx.navigation.fragment.navArgs
|
||||||
import androidx.window.layout.FoldingFeature
|
import androidx.window.layout.FoldingFeature
|
||||||
@ -42,9 +39,6 @@ import androidx.window.layout.WindowInfoTracker
|
|||||||
import androidx.window.layout.WindowLayoutInfo
|
import androidx.window.layout.WindowLayoutInfo
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
import com.google.android.material.slider.Slider
|
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.HomeNavigationDirections
|
||||||
import org.yuzu.yuzu_emu.NativeLibrary
|
import org.yuzu.yuzu_emu.NativeLibrary
|
||||||
import org.yuzu.yuzu_emu.R
|
import org.yuzu.yuzu_emu.R
|
||||||
@ -63,6 +57,7 @@ import org.yuzu.yuzu_emu.model.EmulationViewModel
|
|||||||
import org.yuzu.yuzu_emu.overlay.model.OverlayControl
|
import org.yuzu.yuzu_emu.overlay.model.OverlayControl
|
||||||
import org.yuzu.yuzu_emu.overlay.model.OverlayLayout
|
import org.yuzu.yuzu_emu.overlay.model.OverlayLayout
|
||||||
import org.yuzu.yuzu_emu.utils.*
|
import org.yuzu.yuzu_emu.utils.*
|
||||||
|
import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
|
||||||
import java.lang.NullPointerException
|
import java.lang.NullPointerException
|
||||||
|
|
||||||
class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
||||||
@ -90,14 +85,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
|||||||
if (context is EmulationActivity) {
|
if (context is EmulationActivity) {
|
||||||
emulationActivity = context
|
emulationActivity = context
|
||||||
NativeLibrary.setEmulationActivity(context)
|
NativeLibrary.setEmulationActivity(context)
|
||||||
|
|
||||||
lifecycleScope.launch(Dispatchers.Main) {
|
|
||||||
lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
|
|
||||||
WindowInfoTracker.getOrCreate(context)
|
|
||||||
.windowLayoutInfo(context)
|
|
||||||
.collect { updateFoldableLayout(context, it) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
throw IllegalStateException("EmulationFragment must have EmulationActivity parent")
|
throw IllegalStateException("EmulationFragment must have EmulationActivity parent")
|
||||||
}
|
}
|
||||||
@ -168,8 +155,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
|||||||
return binding.root
|
return binding.root
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is using the correct scope, lint is just acting up
|
|
||||||
@SuppressLint("UnsafeRepeatOnLifecycleDetector")
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
if (requireActivity().isFinishing) {
|
if (requireActivity().isFinishing) {
|
||||||
@ -350,129 +335,86 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
|||||||
binding.loadingTitle.isSelected = true
|
binding.loadingTitle.isSelected = true
|
||||||
binding.loadingText.isSelected = true
|
binding.loadingText.isSelected = true
|
||||||
|
|
||||||
viewLifecycleOwner.lifecycleScope.apply {
|
WindowInfoTracker.getOrCreate(requireContext())
|
||||||
launch {
|
.windowLayoutInfo(requireActivity()).collect(viewLifecycleOwner) {
|
||||||
repeatOnLifecycle(Lifecycle.State.STARTED) {
|
updateFoldableLayout(requireActivity() as EmulationActivity, it)
|
||||||
WindowInfoTracker.getOrCreate(requireContext())
|
|
||||||
.windowLayoutInfo(requireActivity())
|
|
||||||
.collect {
|
|
||||||
updateFoldableLayout(requireActivity() as EmulationActivity, it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
launch {
|
emulationViewModel.shaderProgress.collect(viewLifecycleOwner) {
|
||||||
repeatOnLifecycle(Lifecycle.State.CREATED) {
|
if (it > 0 && it != emulationViewModel.totalShaders.value) {
|
||||||
emulationViewModel.shaderProgress.collectLatest {
|
binding.loadingProgressIndicator.isIndeterminate = false
|
||||||
if (it > 0 && it != emulationViewModel.totalShaders.value) {
|
|
||||||
binding.loadingProgressIndicator.isIndeterminate = false
|
|
||||||
|
|
||||||
if (it < binding.loadingProgressIndicator.max) {
|
if (it < binding.loadingProgressIndicator.max) {
|
||||||
binding.loadingProgressIndicator.progress = it
|
binding.loadingProgressIndicator.progress = it
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (it == emulationViewModel.totalShaders.value) {
|
if (it == emulationViewModel.totalShaders.value) {
|
||||||
binding.loadingText.setText(R.string.loading)
|
binding.loadingText.setText(R.string.loading)
|
||||||
binding.loadingProgressIndicator.isIndeterminate = true
|
binding.loadingProgressIndicator.isIndeterminate = true
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
launch {
|
}
|
||||||
repeatOnLifecycle(Lifecycle.State.CREATED) {
|
emulationViewModel.totalShaders.collect(viewLifecycleOwner) {
|
||||||
emulationViewModel.totalShaders.collectLatest {
|
binding.loadingProgressIndicator.max = it
|
||||||
binding.loadingProgressIndicator.max = it
|
}
|
||||||
}
|
emulationViewModel.shaderMessage.collect(viewLifecycleOwner) {
|
||||||
}
|
if (it.isNotEmpty()) {
|
||||||
|
binding.loadingText.text = 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)
|
|
||||||
|
|
||||||
emulationState.updateSurface()
|
emulationViewModel.emulationStarted.collect(viewLifecycleOwner) {
|
||||||
|
if (it) {
|
||||||
|
binding.drawerLayout.setDrawerLockMode(IntSetting.LOCK_DRAWER.getInt())
|
||||||
|
ViewUtils.showView(binding.surfaceInputOverlay)
|
||||||
|
ViewUtils.hideView(binding.loadingIndicator)
|
||||||
|
|
||||||
// Setup overlays
|
emulationState.updateSurface()
|
||||||
updateShowFpsOverlay()
|
|
||||||
updateThermalOverlay()
|
// Setup overlays
|
||||||
}
|
updateShowFpsOverlay()
|
||||||
}
|
updateThermalOverlay()
|
||||||
}
|
|
||||||
}
|
}
|
||||||
launch {
|
}
|
||||||
repeatOnLifecycle(Lifecycle.State.CREATED) {
|
emulationViewModel.isEmulationStopping.collect(viewLifecycleOwner) {
|
||||||
emulationViewModel.isEmulationStopping.collectLatest {
|
if (it) {
|
||||||
if (it) {
|
binding.loadingText.setText(R.string.shutting_down)
|
||||||
binding.loadingText.setText(R.string.shutting_down)
|
ViewUtils.showView(binding.loadingIndicator)
|
||||||
ViewUtils.showView(binding.loadingIndicator)
|
ViewUtils.hideView(binding.inputContainer)
|
||||||
ViewUtils.hideView(binding.inputContainer)
|
ViewUtils.hideView(binding.showFpsText)
|
||||||
ViewUtils.hideView(binding.showFpsText)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
launch {
|
}
|
||||||
repeatOnLifecycle(Lifecycle.State.CREATED) {
|
emulationViewModel.drawerOpen.collect(viewLifecycleOwner) {
|
||||||
emulationViewModel.drawerOpen.collect {
|
if (it) {
|
||||||
if (it) {
|
binding.drawerLayout.open()
|
||||||
binding.drawerLayout.open()
|
binding.inGameMenu.requestFocus()
|
||||||
binding.inGameMenu.requestFocus()
|
} else {
|
||||||
} else {
|
binding.drawerLayout.close()
|
||||||
binding.drawerLayout.close()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
launch {
|
}
|
||||||
repeatOnLifecycle(Lifecycle.State.CREATED) {
|
emulationViewModel.programChanged.collect(viewLifecycleOwner) {
|
||||||
emulationViewModel.programChanged.collect {
|
if (it != 0) {
|
||||||
if (it != 0) {
|
emulationViewModel.setEmulationStarted(false)
|
||||||
emulationViewModel.setEmulationStarted(false)
|
binding.drawerLayout.close()
|
||||||
binding.drawerLayout.close()
|
binding.drawerLayout
|
||||||
binding.drawerLayout
|
.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED)
|
||||||
.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED)
|
ViewUtils.hideView(binding.surfaceInputOverlay)
|
||||||
ViewUtils.hideView(binding.surfaceInputOverlay)
|
ViewUtils.showView(binding.loadingIndicator)
|
||||||
ViewUtils.showView(binding.loadingIndicator)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
launch {
|
}
|
||||||
repeatOnLifecycle(Lifecycle.State.CREATED) {
|
emulationViewModel.emulationStopped.collect(viewLifecycleOwner) {
|
||||||
emulationViewModel.emulationStopped.collect {
|
if (it && emulationViewModel.programChanged.value != -1) {
|
||||||
if (it && emulationViewModel.programChanged.value != -1) {
|
if (perfStatsUpdater != null) {
|
||||||
if (perfStatsUpdater != null) {
|
perfStatsUpdateHandler.removeCallbacks(perfStatsUpdater!!)
|
||||||
perfStatsUpdateHandler.removeCallbacks(perfStatsUpdater!!)
|
|
||||||
}
|
|
||||||
emulationState.changeProgram(emulationViewModel.programChanged.value)
|
|
||||||
emulationViewModel.setProgramChanged(-1)
|
|
||||||
emulationViewModel.setEmulationStopped(false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
emulationState.changeProgram(emulationViewModel.programChanged.value)
|
||||||
|
emulationViewModel.setProgramChanged(-1)
|
||||||
|
emulationViewModel.setEmulationStopped(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
driverViewModel.isInteractionAllowed.collect(viewLifecycleOwner) {
|
||||||
|
if (it) startEmulation()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun startEmulation(programIndex: Int = 0) {
|
private fun startEmulation(programIndex: Int = 0) {
|
||||||
@ -500,14 +442,12 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
|||||||
binding.drawerLayout.close()
|
binding.drawerLayout.close()
|
||||||
}
|
}
|
||||||
if (showInputOverlay) {
|
if (showInputOverlay) {
|
||||||
binding.surfaceInputOverlay.visibility = View.INVISIBLE
|
binding.surfaceInputOverlay.setVisible(visible = false, gone = false)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (showInputOverlay && emulationViewModel.emulationStarted.value) {
|
binding.surfaceInputOverlay.setVisible(
|
||||||
binding.surfaceInputOverlay.visibility = View.VISIBLE
|
showInputOverlay && emulationViewModel.emulationStarted.value
|
||||||
} else {
|
)
|
||||||
binding.surfaceInputOverlay.visibility = View.INVISIBLE
|
|
||||||
}
|
|
||||||
if (!isInFoldableLayout) {
|
if (!isInFoldableLayout) {
|
||||||
if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) {
|
if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) {
|
||||||
binding.surfaceInputOverlay.layout = OverlayLayout.Portrait
|
binding.surfaceInputOverlay.layout = OverlayLayout.Portrait
|
||||||
@ -544,7 +484,9 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun updateShowFpsOverlay() {
|
private fun updateShowFpsOverlay() {
|
||||||
if (BooleanSetting.SHOW_PERFORMANCE_OVERLAY.getBoolean()) {
|
val showOverlay = BooleanSetting.SHOW_PERFORMANCE_OVERLAY.getBoolean()
|
||||||
|
binding.showFpsText.setVisible(showOverlay)
|
||||||
|
if (showOverlay) {
|
||||||
val SYSTEM_FPS = 0
|
val SYSTEM_FPS = 0
|
||||||
val FPS = 1
|
val FPS = 1
|
||||||
val FRAMETIME = 2
|
val FRAMETIME = 2
|
||||||
@ -564,17 +506,17 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
perfStatsUpdateHandler.post(perfStatsUpdater!!)
|
perfStatsUpdateHandler.post(perfStatsUpdater!!)
|
||||||
binding.showFpsText.visibility = View.VISIBLE
|
|
||||||
} else {
|
} else {
|
||||||
if (perfStatsUpdater != null) {
|
if (perfStatsUpdater != null) {
|
||||||
perfStatsUpdateHandler.removeCallbacks(perfStatsUpdater!!)
|
perfStatsUpdateHandler.removeCallbacks(perfStatsUpdater!!)
|
||||||
}
|
}
|
||||||
binding.showFpsText.visibility = View.GONE
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateThermalOverlay() {
|
private fun updateThermalOverlay() {
|
||||||
if (BooleanSetting.SHOW_THERMAL_OVERLAY.getBoolean()) {
|
val showOverlay = BooleanSetting.SHOW_THERMAL_OVERLAY.getBoolean()
|
||||||
|
binding.showThermalsText.setVisible(showOverlay)
|
||||||
|
if (showOverlay) {
|
||||||
thermalStatsUpdater = {
|
thermalStatsUpdater = {
|
||||||
if (emulationViewModel.emulationStarted.value &&
|
if (emulationViewModel.emulationStarted.value &&
|
||||||
!emulationViewModel.isEmulationStopping.value
|
!emulationViewModel.isEmulationStopping.value
|
||||||
@ -596,12 +538,10 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
thermalStatsUpdateHandler.post(thermalStatsUpdater!!)
|
thermalStatsUpdateHandler.post(thermalStatsUpdater!!)
|
||||||
binding.showThermalsText.visibility = View.VISIBLE
|
|
||||||
} else {
|
} else {
|
||||||
if (thermalStatsUpdater != null) {
|
if (thermalStatsUpdater != null) {
|
||||||
thermalStatsUpdateHandler.removeCallbacks(thermalStatsUpdater!!)
|
thermalStatsUpdateHandler.removeCallbacks(thermalStatsUpdater!!)
|
||||||
}
|
}
|
||||||
binding.showThermalsText.visibility = View.GONE
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -870,12 +810,12 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
binding.doneControlConfig.visibility = View.VISIBLE
|
binding.doneControlConfig.setVisible(true)
|
||||||
binding.surfaceInputOverlay.setIsInEditMode(true)
|
binding.surfaceInputOverlay.setIsInEditMode(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun stopConfiguringControls() {
|
private fun stopConfiguringControls() {
|
||||||
binding.doneControlConfig.visibility = View.GONE
|
binding.doneControlConfig.setVisible(false)
|
||||||
binding.surfaceInputOverlay.setIsInEditMode(false)
|
binding.surfaceInputOverlay.setIsInEditMode(false)
|
||||||
// Unlock the orientation if it was locked for editing
|
// Unlock the orientation if it was locked for editing
|
||||||
if (IntSetting.RENDERER_SCREEN_LAYOUT.getInt() == EmulationOrientation.Unspecified.int) {
|
if (IntSetting.RENDERER_SCREEN_LAYOUT.getInt() == EmulationOrientation.Unspecified.int) {
|
||||||
|
@ -13,9 +13,6 @@ import androidx.core.view.WindowInsetsCompat
|
|||||||
import androidx.core.view.updatePadding
|
import androidx.core.view.updatePadding
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.fragment.app.activityViewModels
|
import androidx.fragment.app.activityViewModels
|
||||||
import androidx.lifecycle.Lifecycle
|
|
||||||
import androidx.lifecycle.lifecycleScope
|
|
||||||
import androidx.lifecycle.repeatOnLifecycle
|
|
||||||
import androidx.navigation.findNavController
|
import androidx.navigation.findNavController
|
||||||
import androidx.recyclerview.widget.GridLayoutManager
|
import androidx.recyclerview.widget.GridLayoutManager
|
||||||
import com.google.android.material.transition.MaterialSharedAxis
|
import com.google.android.material.transition.MaterialSharedAxis
|
||||||
@ -27,6 +24,7 @@ import org.yuzu.yuzu_emu.model.GamesViewModel
|
|||||||
import org.yuzu.yuzu_emu.model.HomeViewModel
|
import org.yuzu.yuzu_emu.model.HomeViewModel
|
||||||
import org.yuzu.yuzu_emu.ui.main.MainActivity
|
import org.yuzu.yuzu_emu.ui.main.MainActivity
|
||||||
import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
|
import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
|
||||||
|
import org.yuzu.yuzu_emu.utils.collect
|
||||||
|
|
||||||
class GameFoldersFragment : Fragment() {
|
class GameFoldersFragment : Fragment() {
|
||||||
private var _binding: FragmentFoldersBinding? = null
|
private var _binding: FragmentFoldersBinding? = null
|
||||||
@ -70,12 +68,8 @@ class GameFoldersFragment : Fragment() {
|
|||||||
adapter = FolderAdapter(requireActivity(), gamesViewModel)
|
adapter = FolderAdapter(requireActivity(), gamesViewModel)
|
||||||
}
|
}
|
||||||
|
|
||||||
viewLifecycleOwner.lifecycleScope.launch {
|
gamesViewModel.folders.collect(viewLifecycleOwner) {
|
||||||
repeatOnLifecycle(Lifecycle.State.CREATED) {
|
(binding.listFolders.adapter as FolderAdapter).submitList(it)
|
||||||
gamesViewModel.folders.collect {
|
|
||||||
(binding.listFolders.adapter as FolderAdapter).submitList(it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val mainActivity = requireActivity() as MainActivity
|
val mainActivity = requireActivity() as MainActivity
|
||||||
|
@ -27,6 +27,7 @@ import org.yuzu.yuzu_emu.databinding.FragmentGameInfoBinding
|
|||||||
import org.yuzu.yuzu_emu.model.GameVerificationResult
|
import org.yuzu.yuzu_emu.model.GameVerificationResult
|
||||||
import org.yuzu.yuzu_emu.model.HomeViewModel
|
import org.yuzu.yuzu_emu.model.HomeViewModel
|
||||||
import org.yuzu.yuzu_emu.utils.GameMetadata
|
import org.yuzu.yuzu_emu.utils.GameMetadata
|
||||||
|
import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
|
||||||
import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
|
import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
|
||||||
|
|
||||||
class GameInfoFragment : Fragment() {
|
class GameInfoFragment : Fragment() {
|
||||||
@ -85,7 +86,7 @@ class GameInfoFragment : Fragment() {
|
|||||||
copyToClipboard(getString(R.string.developer), args.game.developer)
|
copyToClipboard(getString(R.string.developer), args.game.developer)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
developer.visibility = View.GONE
|
developer.setVisible(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
version.setHint(R.string.version)
|
version.setHint(R.string.version)
|
||||||
|
@ -3,11 +3,9 @@
|
|||||||
|
|
||||||
package org.yuzu.yuzu_emu.fragments
|
package org.yuzu.yuzu_emu.fragments
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
|
||||||
import android.content.pm.ShortcutInfo
|
import android.content.pm.ShortcutInfo
|
||||||
import android.content.pm.ShortcutManager
|
import android.content.pm.ShortcutManager
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.text.TextUtils
|
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
@ -18,9 +16,7 @@ import androidx.core.view.WindowInsetsCompat
|
|||||||
import androidx.core.view.updatePadding
|
import androidx.core.view.updatePadding
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.fragment.app.activityViewModels
|
import androidx.fragment.app.activityViewModels
|
||||||
import androidx.lifecycle.Lifecycle
|
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.lifecycle.repeatOnLifecycle
|
|
||||||
import androidx.navigation.findNavController
|
import androidx.navigation.findNavController
|
||||||
import androidx.navigation.fragment.navArgs
|
import androidx.navigation.fragment.navArgs
|
||||||
import androidx.recyclerview.widget.GridLayoutManager
|
import androidx.recyclerview.widget.GridLayoutManager
|
||||||
@ -46,7 +42,9 @@ import org.yuzu.yuzu_emu.utils.FileUtil
|
|||||||
import org.yuzu.yuzu_emu.utils.GameIconUtils
|
import org.yuzu.yuzu_emu.utils.GameIconUtils
|
||||||
import org.yuzu.yuzu_emu.utils.GpuDriverHelper
|
import org.yuzu.yuzu_emu.utils.GpuDriverHelper
|
||||||
import org.yuzu.yuzu_emu.utils.MemoryUtil
|
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.ViewUtils.updateMargins
|
||||||
|
import org.yuzu.yuzu_emu.utils.collect
|
||||||
import java.io.BufferedOutputStream
|
import java.io.BufferedOutputStream
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
@ -76,8 +74,6 @@ class GamePropertiesFragment : Fragment() {
|
|||||||
return binding.root
|
return binding.root
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is using the correct scope, lint is just acting up
|
|
||||||
@SuppressLint("UnsafeRepeatOnLifecycleDetector")
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
homeViewModel.setNavigationVisibility(visible = false, animated = true)
|
homeViewModel.setNavigationVisibility(visible = false, animated = true)
|
||||||
@ -107,13 +103,7 @@ class GamePropertiesFragment : Fragment() {
|
|||||||
|
|
||||||
GameIconUtils.loadGameIcon(args.game, binding.imageGameScreen)
|
GameIconUtils.loadGameIcon(args.game, binding.imageGameScreen)
|
||||||
binding.title.text = args.game.title
|
binding.title.text = args.game.title
|
||||||
binding.title.postDelayed(
|
binding.title.marquee()
|
||||||
{
|
|
||||||
binding.title.ellipsize = TextUtils.TruncateAt.MARQUEE
|
|
||||||
binding.title.isSelected = true
|
|
||||||
},
|
|
||||||
3000
|
|
||||||
)
|
|
||||||
|
|
||||||
binding.buttonStart.setOnClickListener {
|
binding.buttonStart.setOnClickListener {
|
||||||
LaunchGameDialogFragment.newInstance(args.game)
|
LaunchGameDialogFragment.newInstance(args.game)
|
||||||
@ -122,28 +112,14 @@ class GamePropertiesFragment : Fragment() {
|
|||||||
|
|
||||||
reloadList()
|
reloadList()
|
||||||
|
|
||||||
viewLifecycleOwner.lifecycleScope.apply {
|
homeViewModel.openImportSaves.collect(
|
||||||
launch {
|
viewLifecycleOwner,
|
||||||
repeatOnLifecycle(Lifecycle.State.STARTED) {
|
resetState = { homeViewModel.setOpenImportSaves(false) }
|
||||||
homeViewModel.openImportSaves.collect {
|
) { if (it) importSaves.launch(arrayOf("application/zip")) }
|
||||||
if (it) {
|
homeViewModel.reloadPropertiesList.collect(
|
||||||
importSaves.launch(arrayOf("application/zip"))
|
viewLifecycleOwner,
|
||||||
homeViewModel.setOpenImportSaves(false)
|
resetState = { homeViewModel.reloadPropertiesList(false) }
|
||||||
}
|
) { if (it) reloadList() }
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
launch {
|
|
||||||
repeatOnLifecycle(Lifecycle.State.STARTED) {
|
|
||||||
homeViewModel.reloadPropertiesList.collect {
|
|
||||||
if (it) {
|
|
||||||
reloadList()
|
|
||||||
homeViewModel.reloadPropertiesList(false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
setInsets()
|
setInsets()
|
||||||
}
|
}
|
||||||
|
@ -14,9 +14,6 @@ import androidx.core.view.WindowInsetsCompat
|
|||||||
import androidx.core.view.updatePadding
|
import androidx.core.view.updatePadding
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.fragment.app.activityViewModels
|
import androidx.fragment.app.activityViewModels
|
||||||
import androidx.lifecycle.Lifecycle
|
|
||||||
import androidx.lifecycle.lifecycleScope
|
|
||||||
import androidx.lifecycle.repeatOnLifecycle
|
|
||||||
import androidx.navigation.findNavController
|
import androidx.navigation.findNavController
|
||||||
import androidx.recyclerview.widget.GridLayoutManager
|
import androidx.recyclerview.widget.GridLayoutManager
|
||||||
import com.google.android.material.transition.MaterialSharedAxis
|
import com.google.android.material.transition.MaterialSharedAxis
|
||||||
@ -35,6 +32,7 @@ import org.yuzu.yuzu_emu.ui.main.MainActivity
|
|||||||
import org.yuzu.yuzu_emu.utils.DirectoryInitialization
|
import org.yuzu.yuzu_emu.utils.DirectoryInitialization
|
||||||
import org.yuzu.yuzu_emu.utils.FileUtil
|
import org.yuzu.yuzu_emu.utils.FileUtil
|
||||||
import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
|
import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
|
||||||
|
import org.yuzu.yuzu_emu.utils.collect
|
||||||
import java.io.BufferedOutputStream
|
import java.io.BufferedOutputStream
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.math.BigInteger
|
import java.math.BigInteger
|
||||||
@ -75,14 +73,10 @@ class InstallableFragment : Fragment() {
|
|||||||
binding.root.findNavController().popBackStack()
|
binding.root.findNavController().popBackStack()
|
||||||
}
|
}
|
||||||
|
|
||||||
viewLifecycleOwner.lifecycleScope.launch {
|
homeViewModel.openImportSaves.collect(viewLifecycleOwner) {
|
||||||
repeatOnLifecycle(Lifecycle.State.CREATED) {
|
if (it) {
|
||||||
homeViewModel.openImportSaves.collect {
|
importSaves.launch(arrayOf("application/zip"))
|
||||||
if (it) {
|
homeViewModel.setOpenImportSaves(false)
|
||||||
importSaves.launch(arrayOf("application/zip"))
|
|
||||||
homeViewModel.setOpenImportSaves(false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,15 +13,13 @@ import androidx.appcompat.app.AlertDialog
|
|||||||
import androidx.fragment.app.DialogFragment
|
import androidx.fragment.app.DialogFragment
|
||||||
import androidx.fragment.app.FragmentActivity
|
import androidx.fragment.app.FragmentActivity
|
||||||
import androidx.fragment.app.activityViewModels
|
import androidx.fragment.app.activityViewModels
|
||||||
import androidx.lifecycle.Lifecycle
|
|
||||||
import androidx.lifecycle.ViewModelProvider
|
import androidx.lifecycle.ViewModelProvider
|
||||||
import androidx.lifecycle.lifecycleScope
|
|
||||||
import androidx.lifecycle.repeatOnLifecycle
|
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import org.yuzu.yuzu_emu.R
|
import org.yuzu.yuzu_emu.R
|
||||||
import org.yuzu.yuzu_emu.databinding.DialogProgressBarBinding
|
import org.yuzu.yuzu_emu.databinding.DialogProgressBarBinding
|
||||||
import org.yuzu.yuzu_emu.model.TaskViewModel
|
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() {
|
class ProgressDialogFragment : DialogFragment() {
|
||||||
private val taskViewModel: TaskViewModel by activityViewModels()
|
private val taskViewModel: TaskViewModel by activityViewModels()
|
||||||
@ -64,72 +62,50 @@ class ProgressDialogFragment : DialogFragment() {
|
|||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
binding.message.isSelected = true
|
binding.message.isSelected = true
|
||||||
viewLifecycleOwner.lifecycleScope.apply {
|
taskViewModel.isComplete.collect(viewLifecycleOwner) {
|
||||||
launch {
|
if (it) {
|
||||||
repeatOnLifecycle(Lifecycle.State.CREATED) {
|
dismiss()
|
||||||
taskViewModel.isComplete.collect {
|
when (val result = taskViewModel.result.value) {
|
||||||
if (it) {
|
is String -> Toast.makeText(
|
||||||
dismiss()
|
requireContext(),
|
||||||
when (val result = taskViewModel.result.value) {
|
result,
|
||||||
is String -> Toast.makeText(
|
Toast.LENGTH_LONG
|
||||||
requireContext(),
|
).show()
|
||||||
result,
|
|
||||||
Toast.LENGTH_LONG
|
|
||||||
).show()
|
|
||||||
|
|
||||||
is MessageDialogFragment -> result.show(
|
is MessageDialogFragment -> result.show(
|
||||||
requireActivity().supportFragmentManager,
|
requireActivity().supportFragmentManager,
|
||||||
MessageDialogFragment.TAG
|
MessageDialogFragment.TAG
|
||||||
)
|
)
|
||||||
|
|
||||||
else -> {
|
else -> {
|
||||||
// Do nothing
|
// Do nothing
|
||||||
}
|
|
||||||
}
|
|
||||||
taskViewModel.clear()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
taskViewModel.clear()
|
||||||
}
|
}
|
||||||
launch {
|
}
|
||||||
repeatOnLifecycle(Lifecycle.State.CREATED) {
|
taskViewModel.cancelled.collect(viewLifecycleOwner) {
|
||||||
taskViewModel.cancelled.collect {
|
if (it) {
|
||||||
if (it) {
|
dialog?.setTitle(R.string.cancelling)
|
||||||
dialog?.setTitle(R.string.cancelling)
|
}
|
||||||
}
|
}
|
||||||
}
|
taskViewModel.progress.collect(viewLifecycleOwner) {
|
||||||
}
|
if (it != 0.0) {
|
||||||
}
|
binding.progressBar.apply {
|
||||||
launch {
|
isIndeterminate = false
|
||||||
repeatOnLifecycle(Lifecycle.State.CREATED) {
|
progress = (
|
||||||
taskViewModel.progress.collect {
|
(it / taskViewModel.maxProgress.value) *
|
||||||
if (it != 0.0) {
|
PROGRESS_BAR_RESOLUTION
|
||||||
binding.progressBar.apply {
|
).toInt()
|
||||||
isIndeterminate = false
|
min = 0
|
||||||
progress = (
|
max = PROGRESS_BAR_RESOLUTION
|
||||||
(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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// By default, the ProgressDialog will immediately dismiss itself upon a button being pressed.
|
// By default, the ProgressDialog will immediately dismiss itself upon a button being pressed.
|
||||||
|
@ -3,7 +3,6 @@
|
|||||||
|
|
||||||
package org.yuzu.yuzu_emu.fragments
|
package org.yuzu.yuzu_emu.fragments
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.SharedPreferences
|
import android.content.SharedPreferences
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
@ -18,14 +17,9 @@ import androidx.core.view.updatePadding
|
|||||||
import androidx.core.widget.doOnTextChanged
|
import androidx.core.widget.doOnTextChanged
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.fragment.app.activityViewModels
|
import androidx.fragment.app.activityViewModels
|
||||||
import androidx.lifecycle.Lifecycle
|
|
||||||
import androidx.lifecycle.lifecycleScope
|
|
||||||
import androidx.lifecycle.repeatOnLifecycle
|
|
||||||
import androidx.preference.PreferenceManager
|
import androidx.preference.PreferenceManager
|
||||||
import info.debatty.java.stringsimilarity.Jaccard
|
import info.debatty.java.stringsimilarity.Jaccard
|
||||||
import info.debatty.java.stringsimilarity.JaroWinkler
|
import info.debatty.java.stringsimilarity.JaroWinkler
|
||||||
import kotlinx.coroutines.flow.collectLatest
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
import org.yuzu.yuzu_emu.R
|
import org.yuzu.yuzu_emu.R
|
||||||
import org.yuzu.yuzu_emu.YuzuApplication
|
import org.yuzu.yuzu_emu.YuzuApplication
|
||||||
@ -35,6 +29,8 @@ import org.yuzu.yuzu_emu.layout.AutofitGridLayoutManager
|
|||||||
import org.yuzu.yuzu_emu.model.Game
|
import org.yuzu.yuzu_emu.model.Game
|
||||||
import org.yuzu.yuzu_emu.model.GamesViewModel
|
import org.yuzu.yuzu_emu.model.GamesViewModel
|
||||||
import org.yuzu.yuzu_emu.model.HomeViewModel
|
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() {
|
class SearchFragment : Fragment() {
|
||||||
private var _binding: FragmentSearchBinding? = null
|
private var _binding: FragmentSearchBinding? = null
|
||||||
@ -58,8 +54,6 @@ class SearchFragment : Fragment() {
|
|||||||
return binding.root
|
return binding.root
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is using the correct scope, lint is just acting up
|
|
||||||
@SuppressLint("UnsafeRepeatOnLifecycleDetector")
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
homeViewModel.setNavigationVisibility(visible = true, animated = true)
|
homeViewModel.setNavigationVisibility(visible = true, animated = true)
|
||||||
@ -81,42 +75,18 @@ class SearchFragment : Fragment() {
|
|||||||
binding.chipGroup.setOnCheckedStateChangeListener { _, _ -> filterAndSearch() }
|
binding.chipGroup.setOnCheckedStateChangeListener { _, _ -> filterAndSearch() }
|
||||||
|
|
||||||
binding.searchText.doOnTextChanged { text: CharSequence?, _: Int, _: Int, _: Int ->
|
binding.searchText.doOnTextChanged { text: CharSequence?, _: Int, _: Int, _: Int ->
|
||||||
if (text.toString().isNotEmpty()) {
|
binding.clearButton.setVisible(text.toString().isNotEmpty())
|
||||||
binding.clearButton.visibility = View.VISIBLE
|
|
||||||
} else {
|
|
||||||
binding.clearButton.visibility = View.INVISIBLE
|
|
||||||
}
|
|
||||||
filterAndSearch()
|
filterAndSearch()
|
||||||
}
|
}
|
||||||
|
|
||||||
viewLifecycleOwner.lifecycleScope.apply {
|
gamesViewModel.searchFocused.collect(
|
||||||
launch {
|
viewLifecycleOwner,
|
||||||
repeatOnLifecycle(Lifecycle.State.CREATED) {
|
resetState = { gamesViewModel.setSearchFocused(false) }
|
||||||
gamesViewModel.searchFocused.collect {
|
) { if (it) focusSearch() }
|
||||||
if (it) {
|
gamesViewModel.games.collect(viewLifecycleOwner) { filterAndSearch() }
|
||||||
focusSearch()
|
gamesViewModel.searchedGames.collect(viewLifecycleOwner) {
|
||||||
gamesViewModel.setSearchFocused(false)
|
(binding.gridGamesSearch.adapter as GameAdapter).submitList(it)
|
||||||
}
|
binding.noResultsView.setVisible(it.isNotEmpty())
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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("") }
|
binding.clearButton.setOnClickListener { binding.searchText.setText("") }
|
||||||
|
@ -4,7 +4,6 @@
|
|||||||
package org.yuzu.yuzu_emu.fragments
|
package org.yuzu.yuzu_emu.fragments
|
||||||
|
|
||||||
import android.Manifest
|
import android.Manifest
|
||||||
import android.annotation.SuppressLint
|
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
@ -23,9 +22,6 @@ import androidx.core.view.isVisible
|
|||||||
import androidx.core.view.updatePadding
|
import androidx.core.view.updatePadding
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.fragment.app.activityViewModels
|
import androidx.fragment.app.activityViewModels
|
||||||
import androidx.lifecycle.Lifecycle
|
|
||||||
import androidx.lifecycle.lifecycleScope
|
|
||||||
import androidx.lifecycle.repeatOnLifecycle
|
|
||||||
import androidx.navigation.findNavController
|
import androidx.navigation.findNavController
|
||||||
import androidx.preference.PreferenceManager
|
import androidx.preference.PreferenceManager
|
||||||
import androidx.viewpager2.widget.ViewPager2.OnPageChangeCallback
|
import androidx.viewpager2.widget.ViewPager2.OnPageChangeCallback
|
||||||
@ -46,6 +42,8 @@ import org.yuzu.yuzu_emu.ui.main.MainActivity
|
|||||||
import org.yuzu.yuzu_emu.utils.DirectoryInitialization
|
import org.yuzu.yuzu_emu.utils.DirectoryInitialization
|
||||||
import org.yuzu.yuzu_emu.utils.NativeConfig
|
import org.yuzu.yuzu_emu.utils.NativeConfig
|
||||||
import org.yuzu.yuzu_emu.utils.ViewUtils
|
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() {
|
class SetupFragment : Fragment() {
|
||||||
private var _binding: FragmentSetupBinding? = null
|
private var _binding: FragmentSetupBinding? = null
|
||||||
@ -77,8 +75,6 @@ class SetupFragment : Fragment() {
|
|||||||
return binding.root
|
return binding.root
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is using the correct scope, lint is just acting up
|
|
||||||
@SuppressLint("UnsafeRepeatOnLifecycleDetector")
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
mainActivity = requireActivity() as MainActivity
|
mainActivity = requireActivity() as MainActivity
|
||||||
|
|
||||||
@ -210,28 +206,14 @@ class SetupFragment : Fragment() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
viewLifecycleOwner.lifecycleScope.apply {
|
homeViewModel.shouldPageForward.collect(
|
||||||
launch {
|
viewLifecycleOwner,
|
||||||
repeatOnLifecycle(Lifecycle.State.CREATED) {
|
resetState = { homeViewModel.setShouldPageForward(false) }
|
||||||
homeViewModel.shouldPageForward.collect {
|
) { if (it) pageForward() }
|
||||||
if (it) {
|
homeViewModel.gamesDirSelected.collect(
|
||||||
pageForward()
|
viewLifecycleOwner,
|
||||||
homeViewModel.setShouldPageForward(false)
|
resetState = { homeViewModel.setGamesDirSelected(false) }
|
||||||
}
|
) { if (it) gamesDirCallback.onStepCompleted() }
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
launch {
|
|
||||||
repeatOnLifecycle(Lifecycle.State.CREATED) {
|
|
||||||
homeViewModel.gamesDirSelected.collect {
|
|
||||||
if (it) {
|
|
||||||
gamesDirCallback.onStepCompleted()
|
|
||||||
homeViewModel.setGamesDirSelected(false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
binding.viewPager2.apply {
|
binding.viewPager2.apply {
|
||||||
adapter = SetupAdapter(requireActivity() as AppCompatActivity, pages)
|
adapter = SetupAdapter(requireActivity() as AppCompatActivity, pages)
|
||||||
@ -292,12 +274,8 @@ class SetupFragment : Fragment() {
|
|||||||
val backIsVisible = savedInstanceState.getBoolean(KEY_BACK_VISIBILITY)
|
val backIsVisible = savedInstanceState.getBoolean(KEY_BACK_VISIBILITY)
|
||||||
hasBeenWarned = savedInstanceState.getBooleanArray(KEY_HAS_BEEN_WARNED)!!
|
hasBeenWarned = savedInstanceState.getBooleanArray(KEY_HAS_BEEN_WARNED)!!
|
||||||
|
|
||||||
if (nextIsVisible) {
|
binding.buttonNext.setVisible(nextIsVisible)
|
||||||
binding.buttonNext.visibility = View.VISIBLE
|
binding.buttonBack.setVisible(backIsVisible)
|
||||||
}
|
|
||||||
if (backIsVisible) {
|
|
||||||
binding.buttonBack.visibility = View.VISIBLE
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
hasBeenWarned = BooleanArray(pages.size)
|
hasBeenWarned = BooleanArray(pages.size)
|
||||||
}
|
}
|
||||||
|
@ -28,6 +28,7 @@ import org.yuzu.yuzu_emu.features.input.NativeInput
|
|||||||
import org.yuzu.yuzu_emu.R
|
import org.yuzu.yuzu_emu.R
|
||||||
import org.yuzu.yuzu_emu.features.input.model.NativeAnalog
|
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.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.BooleanSetting
|
||||||
import org.yuzu.yuzu_emu.features.settings.model.IntSetting
|
import org.yuzu.yuzu_emu.features.settings.model.IntSetting
|
||||||
import org.yuzu.yuzu_emu.overlay.model.OverlayControl
|
import org.yuzu.yuzu_emu.overlay.model.OverlayControl
|
||||||
@ -99,12 +100,10 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
|
|||||||
}
|
}
|
||||||
|
|
||||||
var shouldUpdateView = false
|
var shouldUpdateView = false
|
||||||
val playerIndex =
|
val playerIndex = when (NativeInput.getStyleIndex(0)) {
|
||||||
if (NativeInput.isHandheldOnly()) {
|
NpadStyleIndex.Handheld -> 8
|
||||||
NativeInput.ConsoleDevice
|
else -> 0
|
||||||
} else {
|
}
|
||||||
NativeInput.Player1Device
|
|
||||||
}
|
|
||||||
|
|
||||||
for (button in overlayButtons) {
|
for (button in overlayButtons) {
|
||||||
if (!button.updateStatus(event)) {
|
if (!button.updateStatus(event)) {
|
||||||
@ -664,7 +663,7 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
|
|||||||
|
|
||||||
val overlayControlData = NativeConfig.getOverlayControlData()
|
val overlayControlData = NativeConfig.getOverlayControlData()
|
||||||
overlayControlData.forEach {
|
overlayControlData.forEach {
|
||||||
it.enabled = OverlayControl.from(it.id)?.defaultVisibility == false
|
it.enabled = OverlayControl.from(it.id)?.defaultVisibility == true
|
||||||
}
|
}
|
||||||
NativeConfig.setOverlayControlData(overlayControlData)
|
NativeConfig.setOverlayControlData(overlayControlData)
|
||||||
|
|
||||||
|
@ -3,7 +3,6 @@
|
|||||||
|
|
||||||
package org.yuzu.yuzu_emu.ui
|
package org.yuzu.yuzu_emu.ui
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
@ -14,19 +13,16 @@ import androidx.core.view.WindowInsetsCompat
|
|||||||
import androidx.core.view.updatePadding
|
import androidx.core.view.updatePadding
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.fragment.app.activityViewModels
|
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 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.R
|
||||||
import org.yuzu.yuzu_emu.adapters.GameAdapter
|
import org.yuzu.yuzu_emu.adapters.GameAdapter
|
||||||
import org.yuzu.yuzu_emu.databinding.FragmentGamesBinding
|
import org.yuzu.yuzu_emu.databinding.FragmentGamesBinding
|
||||||
import org.yuzu.yuzu_emu.layout.AutofitGridLayoutManager
|
import org.yuzu.yuzu_emu.layout.AutofitGridLayoutManager
|
||||||
import org.yuzu.yuzu_emu.model.GamesViewModel
|
import org.yuzu.yuzu_emu.model.GamesViewModel
|
||||||
import org.yuzu.yuzu_emu.model.HomeViewModel
|
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.ViewUtils.updateMargins
|
||||||
|
import org.yuzu.yuzu_emu.utils.collect
|
||||||
|
|
||||||
class GamesFragment : Fragment() {
|
class GamesFragment : Fragment() {
|
||||||
private var _binding: FragmentGamesBinding? = null
|
private var _binding: FragmentGamesBinding? = null
|
||||||
@ -44,8 +40,6 @@ class GamesFragment : Fragment() {
|
|||||||
return binding.root
|
return binding.root
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is using the correct scope, lint is just acting up
|
|
||||||
@SuppressLint("UnsafeRepeatOnLifecycleDetector")
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
homeViewModel.setNavigationVisibility(visible = true, animated = true)
|
homeViewModel.setNavigationVisibility(visible = true, animated = true)
|
||||||
@ -88,49 +82,28 @@ class GamesFragment : Fragment() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
viewLifecycleOwner.lifecycleScope.apply {
|
gamesViewModel.isReloading.collect(viewLifecycleOwner) {
|
||||||
launch {
|
binding.swipeRefresh.isRefreshing = it
|
||||||
repeatOnLifecycle(Lifecycle.State.RESUMED) {
|
binding.noticeText.setVisible(
|
||||||
gamesViewModel.isReloading.collect {
|
visible = gamesViewModel.games.value.isEmpty() && !it,
|
||||||
binding.swipeRefresh.isRefreshing = it
|
gone = false
|
||||||
if (gamesViewModel.games.value.isEmpty() && !it) {
|
)
|
||||||
binding.noticeText.visibility = View.VISIBLE
|
}
|
||||||
} else {
|
gamesViewModel.games.collect(viewLifecycleOwner) {
|
||||||
binding.noticeText.visibility = View.INVISIBLE
|
(binding.gridGames.adapter as GameAdapter).submitList(it)
|
||||||
}
|
}
|
||||||
}
|
gamesViewModel.shouldSwapData.collect(
|
||||||
}
|
viewLifecycleOwner,
|
||||||
}
|
resetState = { gamesViewModel.setShouldSwapData(false) }
|
||||||
launch {
|
) {
|
||||||
repeatOnLifecycle(Lifecycle.State.RESUMED) {
|
if (it) {
|
||||||
gamesViewModel.games.collectLatest {
|
(binding.gridGames.adapter as GameAdapter).submitList(gamesViewModel.games.value)
|
||||||
(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()
|
setInsets()
|
||||||
}
|
}
|
||||||
|
@ -19,9 +19,6 @@ import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
|
|||||||
import androidx.core.view.ViewCompat
|
import androidx.core.view.ViewCompat
|
||||||
import androidx.core.view.WindowCompat
|
import androidx.core.view.WindowCompat
|
||||||
import androidx.core.view.WindowInsetsCompat
|
import androidx.core.view.WindowInsetsCompat
|
||||||
import androidx.lifecycle.Lifecycle
|
|
||||||
import androidx.lifecycle.lifecycleScope
|
|
||||||
import androidx.lifecycle.repeatOnLifecycle
|
|
||||||
import androidx.navigation.NavController
|
import androidx.navigation.NavController
|
||||||
import androidx.navigation.fragment.NavHostFragment
|
import androidx.navigation.fragment.NavHostFragment
|
||||||
import androidx.navigation.ui.setupWithNavController
|
import androidx.navigation.ui.setupWithNavController
|
||||||
@ -30,7 +27,6 @@ import com.google.android.material.color.MaterialColors
|
|||||||
import com.google.android.material.navigation.NavigationBarView
|
import com.google.android.material.navigation.NavigationBarView
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.FilenameFilter
|
import java.io.FilenameFilter
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import org.yuzu.yuzu_emu.HomeNavigationDirections
|
import org.yuzu.yuzu_emu.HomeNavigationDirections
|
||||||
import org.yuzu.yuzu_emu.NativeLibrary
|
import org.yuzu.yuzu_emu.NativeLibrary
|
||||||
import org.yuzu.yuzu_emu.R
|
import org.yuzu.yuzu_emu.R
|
||||||
@ -47,6 +43,7 @@ import org.yuzu.yuzu_emu.model.InstallResult
|
|||||||
import org.yuzu.yuzu_emu.model.TaskState
|
import org.yuzu.yuzu_emu.model.TaskState
|
||||||
import org.yuzu.yuzu_emu.model.TaskViewModel
|
import org.yuzu.yuzu_emu.model.TaskViewModel
|
||||||
import org.yuzu.yuzu_emu.utils.*
|
import org.yuzu.yuzu_emu.utils.*
|
||||||
|
import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
|
||||||
import java.io.BufferedInputStream
|
import java.io.BufferedInputStream
|
||||||
import java.io.BufferedOutputStream
|
import java.io.BufferedOutputStream
|
||||||
import java.util.zip.ZipEntry
|
import java.util.zip.ZipEntry
|
||||||
@ -139,42 +136,23 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
|
|||||||
|
|
||||||
// Prevents navigation from being drawn for a short time on recreation if set to hidden
|
// Prevents navigation from being drawn for a short time on recreation if set to hidden
|
||||||
if (!homeViewModel.navigationVisible.value.first) {
|
if (!homeViewModel.navigationVisible.value.first) {
|
||||||
binding.navigationView.visibility = View.INVISIBLE
|
binding.navigationView.setVisible(visible = false, gone = false)
|
||||||
binding.statusBarShade.visibility = View.INVISIBLE
|
binding.statusBarShade.setVisible(visible = false, gone = false)
|
||||||
}
|
}
|
||||||
|
|
||||||
lifecycleScope.apply {
|
homeViewModel.navigationVisible.collect(this) { showNavigation(it.first, it.second) }
|
||||||
launch {
|
homeViewModel.statusBarShadeVisible.collect(this) { showStatusBarShade(it) }
|
||||||
repeatOnLifecycle(Lifecycle.State.CREATED) {
|
homeViewModel.contentToInstall.collect(
|
||||||
homeViewModel.navigationVisible.collect { showNavigation(it.first, it.second) }
|
this,
|
||||||
}
|
resetState = { homeViewModel.setContentToInstall(null) }
|
||||||
}
|
) {
|
||||||
launch {
|
if (it != null) {
|
||||||
repeatOnLifecycle(Lifecycle.State.CREATED) {
|
installContent(it)
|
||||||
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()
|
setInsets()
|
||||||
}
|
}
|
||||||
@ -214,18 +192,14 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
|
|||||||
|
|
||||||
private fun showNavigation(visible: Boolean, animated: Boolean) {
|
private fun showNavigation(visible: Boolean, animated: Boolean) {
|
||||||
if (!animated) {
|
if (!animated) {
|
||||||
if (visible) {
|
binding.navigationView.setVisible(visible)
|
||||||
binding.navigationView.visibility = View.VISIBLE
|
|
||||||
} else {
|
|
||||||
binding.navigationView.visibility = View.INVISIBLE
|
|
||||||
}
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
val smallLayout = resources.getBoolean(R.bool.small_layout)
|
val smallLayout = resources.getBoolean(R.bool.small_layout)
|
||||||
binding.navigationView.animate().apply {
|
binding.navigationView.animate().apply {
|
||||||
if (visible) {
|
if (visible) {
|
||||||
binding.navigationView.visibility = View.VISIBLE
|
binding.navigationView.setVisible(true)
|
||||||
duration = 300
|
duration = 300
|
||||||
interpolator = PathInterpolator(0.05f, 0.7f, 0.1f, 1f)
|
interpolator = PathInterpolator(0.05f, 0.7f, 0.1f, 1f)
|
||||||
|
|
||||||
@ -264,7 +238,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
|
|||||||
}
|
}
|
||||||
}.withEndAction {
|
}.withEndAction {
|
||||||
if (!visible) {
|
if (!visible) {
|
||||||
binding.navigationView.visibility = View.INVISIBLE
|
binding.navigationView.setVisible(visible = false, gone = false)
|
||||||
}
|
}
|
||||||
}.start()
|
}.start()
|
||||||
}
|
}
|
||||||
@ -272,7 +246,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
|
|||||||
private fun showStatusBarShade(visible: Boolean) {
|
private fun showStatusBarShade(visible: Boolean) {
|
||||||
binding.statusBarShade.animate().apply {
|
binding.statusBarShade.animate().apply {
|
||||||
if (visible) {
|
if (visible) {
|
||||||
binding.statusBarShade.visibility = View.VISIBLE
|
binding.statusBarShade.setVisible(true)
|
||||||
binding.statusBarShade.translationY = binding.statusBarShade.height.toFloat() * -2
|
binding.statusBarShade.translationY = binding.statusBarShade.height.toFloat() * -2
|
||||||
duration = 300
|
duration = 300
|
||||||
translationY(0f)
|
translationY(0f)
|
||||||
@ -284,7 +258,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
|
|||||||
}
|
}
|
||||||
}.withEndAction {
|
}.withEndAction {
|
||||||
if (!visible) {
|
if (!visible) {
|
||||||
binding.statusBarShade.visibility = View.INVISIBLE
|
binding.statusBarShade.setVisible(visible = false, gone = false)
|
||||||
}
|
}
|
||||||
}.start()
|
}.start()
|
||||||
}
|
}
|
||||||
@ -524,7 +498,8 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
|
|||||||
this@MainActivity,
|
this@MainActivity,
|
||||||
titleId = R.string.content_install_notice,
|
titleId = R.string.content_install_notice,
|
||||||
descriptionId = R.string.content_install_notice_description,
|
descriptionId = R.string.content_install_notice_description,
|
||||||
positiveAction = { homeViewModel.setContentToInstall(documents) }
|
positiveAction = { homeViewModel.setContentToInstall(documents) },
|
||||||
|
negativeAction = {}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}.show(supportFragmentManager, ProgressDialogFragment.TAG)
|
}.show(supportFragmentManager, ProgressDialogFragment.TAG)
|
||||||
|
@ -0,0 +1,38 @@
|
|||||||
|
// 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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -3,8 +3,10 @@
|
|||||||
|
|
||||||
package org.yuzu.yuzu_emu.utils
|
package org.yuzu.yuzu_emu.utils
|
||||||
|
|
||||||
|
import android.text.TextUtils
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
|
import android.widget.TextView
|
||||||
|
|
||||||
object ViewUtils {
|
object ViewUtils {
|
||||||
fun showView(view: View, length: Long = 300) {
|
fun showView(view: View, length: Long = 300) {
|
||||||
@ -57,4 +59,35 @@ object ViewUtils {
|
|||||||
}
|
}
|
||||||
this.layoutParams = layoutParams
|
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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,6 +23,22 @@ void EmuWindow_Android::OnSurfaceChanged(ANativeWindow* surface) {
|
|||||||
window_info.render_surface = reinterpret_cast<void*>(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() {
|
void EmuWindow_Android::OnFrameDisplayed() {
|
||||||
if (!m_first_frame) {
|
if (!m_first_frame) {
|
||||||
Common::Android::RunJNIOnFiber<void>(
|
Common::Android::RunJNIOnFiber<void>(
|
||||||
|
@ -38,6 +38,10 @@ public:
|
|||||||
void OnSurfaceChanged(ANativeWindow* surface);
|
void OnSurfaceChanged(ANativeWindow* surface);
|
||||||
void OnFrameDisplayed() override;
|
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 {
|
std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override {
|
||||||
return {std::make_unique<GraphicsContext_Android>(m_driver_library)};
|
return {std::make_unique<GraphicsContext_Android>(m_driver_library)};
|
||||||
}
|
}
|
||||||
|
@ -292,6 +292,9 @@ void EmulationSession::ShutdownEmulation() {
|
|||||||
// Unload user input.
|
// Unload user input.
|
||||||
m_system.HIDCore().UnloadInputDevices();
|
m_system.HIDCore().UnloadInputDevices();
|
||||||
|
|
||||||
|
// Enable all controllers
|
||||||
|
m_system.HIDCore().SetSupportedStyleTag({Core::HID::NpadStyleSet::All});
|
||||||
|
|
||||||
// Shutdown the main emulated process
|
// Shutdown the main emulated process
|
||||||
if (m_load_result == Core::SystemResultStatus::Success) {
|
if (m_load_result == Core::SystemResultStatus::Success) {
|
||||||
m_system.DetachDebugger();
|
m_system.DetachDebugger();
|
||||||
@ -404,7 +407,9 @@ static Core::SystemResultStatus RunEmulation(const std::string& filepath,
|
|||||||
const size_t program_index,
|
const size_t program_index,
|
||||||
const bool frontend_initiated) {
|
const bool frontend_initiated) {
|
||||||
MicroProfileOnThreadCreate("EmuThread");
|
MicroProfileOnThreadCreate("EmuThread");
|
||||||
SCOPE_EXIT({ MicroProfileShutdown(); });
|
SCOPE_EXIT {
|
||||||
|
MicroProfileShutdown();
|
||||||
|
};
|
||||||
|
|
||||||
LOG_INFO(Frontend, "starting");
|
LOG_INFO(Frontend, "starting");
|
||||||
|
|
||||||
@ -413,7 +418,9 @@ static Core::SystemResultStatus RunEmulation(const std::string& filepath,
|
|||||||
return Core::SystemResultStatus::ErrorLoader;
|
return Core::SystemResultStatus::ErrorLoader;
|
||||||
}
|
}
|
||||||
|
|
||||||
SCOPE_EXIT({ EmulationSession::GetInstance().ShutdownEmulation(); });
|
SCOPE_EXIT {
|
||||||
|
EmulationSession::GetInstance().ShutdownEmulation();
|
||||||
|
};
|
||||||
|
|
||||||
jconst result = EmulationSession::GetInstance().InitializeEmulation(filepath, program_index,
|
jconst result = EmulationSession::GetInstance().InitializeEmulation(filepath, program_index,
|
||||||
frontend_initiated);
|
frontend_initiated);
|
||||||
|
@ -102,8 +102,50 @@ 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) {
|
void ConnectController(size_t player_index, bool connected) {
|
||||||
auto& hid_core = EmulationSession::GetInstance().System().HIDCore();
|
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) {
|
if (player_index == 0) {
|
||||||
auto* handheld = hid_core.GetEmulatedController(Core::HID::NpadIdType::Handheld);
|
auto* handheld = hid_core.GetEmulatedController(Core::HID::NpadIdType::Handheld);
|
||||||
auto* player_one = hid_core.GetEmulatedController(Core::HID::NpadIdType::Player1);
|
auto* player_one = hid_core.GetEmulatedController(Core::HID::NpadIdType::Player1);
|
||||||
@ -190,8 +232,7 @@ void Java_org_yuzu_yuzu_1emu_features_input_NativeInput_onTouchPressed(JNIEnv* e
|
|||||||
jint j_id, jfloat j_x_axis,
|
jint j_id, jfloat j_x_axis,
|
||||||
jfloat j_y_axis) {
|
jfloat j_y_axis) {
|
||||||
if (EmulationSession::GetInstance().IsRunning()) {
|
if (EmulationSession::GetInstance().IsRunning()) {
|
||||||
EmulationSession::GetInstance().GetInputSubsystem().GetTouchScreen()->TouchPressed(
|
EmulationSession::GetInstance().Window().OnTouchPressed(j_id, j_x_axis, j_y_axis);
|
||||||
j_id, j_x_axis, j_y_axis);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -199,15 +240,14 @@ void Java_org_yuzu_yuzu_1emu_features_input_NativeInput_onTouchMoved(JNIEnv* env
|
|||||||
jint j_id, jfloat j_x_axis,
|
jint j_id, jfloat j_x_axis,
|
||||||
jfloat j_y_axis) {
|
jfloat j_y_axis) {
|
||||||
if (EmulationSession::GetInstance().IsRunning()) {
|
if (EmulationSession::GetInstance().IsRunning()) {
|
||||||
EmulationSession::GetInstance().GetInputSubsystem().GetTouchScreen()->TouchMoved(
|
EmulationSession::GetInstance().Window().OnTouchMoved(j_id, j_x_axis, j_y_axis);
|
||||||
j_id, j_x_axis, j_y_axis);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Java_org_yuzu_yuzu_1emu_features_input_NativeInput_onTouchReleased(JNIEnv* env, jobject j_obj,
|
void Java_org_yuzu_yuzu_1emu_features_input_NativeInput_onTouchReleased(JNIEnv* env, jobject j_obj,
|
||||||
jint j_id) {
|
jint j_id) {
|
||||||
if (EmulationSession::GetInstance().IsRunning()) {
|
if (EmulationSession::GetInstance().IsRunning()) {
|
||||||
EmulationSession::GetInstance().GetInputSubsystem().GetTouchScreen()->TouchReleased(j_id);
|
EmulationSession::GetInstance().Window().OnTouchReleased(j_id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -524,36 +564,10 @@ jint Java_org_yuzu_yuzu_1emu_features_input_NativeInput_getButtonNameImpl(JNIEnv
|
|||||||
|
|
||||||
jintArray Java_org_yuzu_yuzu_1emu_features_input_NativeInput_getSupportedStyleTagsImpl(
|
jintArray Java_org_yuzu_yuzu_1emu_features_input_NativeInput_getSupportedStyleTagsImpl(
|
||||||
JNIEnv* env, jobject j_obj, jint j_player_index) {
|
JNIEnv* env, jobject j_obj, jint j_player_index) {
|
||||||
auto& hid_core = EmulationSession::GetInstance().System().HIDCore();
|
auto supported_styles = GetSupportedStyles(j_player_index);
|
||||||
const auto npad_style_set = hid_core.GetSupportedStyleTag();
|
jintArray j_supported_indexes = env->NewIntArray(supported_styles.size());
|
||||||
std::vector<s32> supported_indexes;
|
env->SetIntArrayRegion(j_supported_indexes, 0, supported_styles.size(),
|
||||||
if (npad_style_set.fullkey == 1) {
|
supported_styles.data());
|
||||||
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;
|
return j_supported_indexes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -39,10 +39,7 @@
|
|||||||
style="@style/TextAppearance.Material3.TitleMedium"
|
style="@style/TextAppearance.Material3.TitleMedium"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:ellipsize="none"
|
|
||||||
android:marqueeRepeatLimit="marquee_forever"
|
|
||||||
android:requiresFadingEdge="horizontal"
|
android:requiresFadingEdge="horizontal"
|
||||||
android:singleLine="true"
|
|
||||||
android:textAlignment="viewStart"
|
android:textAlignment="viewStart"
|
||||||
tools:text="@string/select_gpu_driver_default" />
|
tools:text="@string/select_gpu_driver_default" />
|
||||||
|
|
||||||
@ -52,10 +49,7 @@
|
|||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="6dp"
|
android:layout_marginTop="6dp"
|
||||||
android:ellipsize="none"
|
|
||||||
android:marqueeRepeatLimit="marquee_forever"
|
|
||||||
android:requiresFadingEdge="horizontal"
|
android:requiresFadingEdge="horizontal"
|
||||||
android:singleLine="true"
|
|
||||||
android:textAlignment="viewStart"
|
android:textAlignment="viewStart"
|
||||||
tools:text="@string/install_gpu_driver_description" />
|
tools:text="@string/install_gpu_driver_description" />
|
||||||
|
|
||||||
@ -65,10 +59,7 @@
|
|||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="6dp"
|
android:layout_marginTop="6dp"
|
||||||
android:ellipsize="none"
|
|
||||||
android:marqueeRepeatLimit="marquee_forever"
|
|
||||||
android:requiresFadingEdge="horizontal"
|
android:requiresFadingEdge="horizontal"
|
||||||
android:singleLine="true"
|
|
||||||
android:textAlignment="viewStart"
|
android:textAlignment="viewStart"
|
||||||
tools:text="@string/install_gpu_driver_description" />
|
tools:text="@string/install_gpu_driver_description" />
|
||||||
|
|
||||||
|
@ -21,10 +21,7 @@
|
|||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_gravity="center_vertical|start"
|
android:layout_gravity="center_vertical|start"
|
||||||
android:ellipsize="none"
|
|
||||||
android:marqueeRepeatLimit="marquee_forever"
|
|
||||||
android:requiresFadingEdge="horizontal"
|
android:requiresFadingEdge="horizontal"
|
||||||
android:singleLine="true"
|
|
||||||
android:textAlignment="viewStart"
|
android:textAlignment="viewStart"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintEnd_toStartOf="@+id/button_layout"
|
app:layout_constraintEnd_toStartOf="@+id/button_layout"
|
||||||
|
@ -40,10 +40,7 @@
|
|||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="8dp"
|
android:layout_marginTop="8dp"
|
||||||
android:ellipsize="none"
|
|
||||||
android:marqueeRepeatLimit="marquee_forever"
|
|
||||||
android:requiresFadingEdge="horizontal"
|
android:requiresFadingEdge="horizontal"
|
||||||
android:singleLine="true"
|
|
||||||
android:textAlignment="center"
|
android:textAlignment="center"
|
||||||
android:textSize="14sp"
|
android:textSize="14sp"
|
||||||
app:layout_constraintEnd_toEndOf="@+id/image_game_screen"
|
app:layout_constraintEnd_toEndOf="@+id/image_game_screen"
|
||||||
|
@ -59,9 +59,6 @@
|
|||||||
android:textAlignment="viewStart"
|
android:textAlignment="viewStart"
|
||||||
android:textSize="14sp"
|
android:textSize="14sp"
|
||||||
android:textStyle="bold"
|
android:textStyle="bold"
|
||||||
android:singleLine="true"
|
|
||||||
android:marqueeRepeatLimit="marquee_forever"
|
|
||||||
android:ellipsize="none"
|
|
||||||
android:requiresFadingEdge="horizontal"
|
android:requiresFadingEdge="horizontal"
|
||||||
android:layout_marginTop="6dp"
|
android:layout_marginTop="6dp"
|
||||||
android:visibility="gone"
|
android:visibility="gone"
|
||||||
|
@ -76,10 +76,7 @@
|
|||||||
android:layout_marginTop="12dp"
|
android:layout_marginTop="12dp"
|
||||||
android:layout_marginBottom="12dp"
|
android:layout_marginBottom="12dp"
|
||||||
android:layout_marginHorizontal="16dp"
|
android:layout_marginHorizontal="16dp"
|
||||||
android:ellipsize="none"
|
|
||||||
android:marqueeRepeatLimit="marquee_forever"
|
|
||||||
android:requiresFadingEdge="horizontal"
|
android:requiresFadingEdge="horizontal"
|
||||||
android:singleLine="true"
|
|
||||||
android:textAlignment="center"
|
android:textAlignment="center"
|
||||||
tools:text="deko_basic" />
|
tools:text="deko_basic" />
|
||||||
|
|
||||||
|
@ -209,6 +209,7 @@
|
|||||||
<string name="value_with_units">%1$s%2$s</string>
|
<string name="value_with_units">%1$s%2$s</string>
|
||||||
|
|
||||||
<!-- System settings strings -->
|
<!-- System settings strings -->
|
||||||
|
<string name="device_name">Device name</string>
|
||||||
<string name="use_docked_mode">Docked Mode</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="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>
|
<string name="emulated_region">Emulated region</string>
|
||||||
|
@ -357,7 +357,9 @@ bool IsCubebSuitable() {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
SCOPE_EXIT({ cubeb_destroy(ctx); });
|
SCOPE_EXIT {
|
||||||
|
cubeb_destroy(ctx);
|
||||||
|
};
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
if (SUCCEEDED(com_init_result)) {
|
if (SUCCEEDED(com_init_result)) {
|
||||||
|
@ -20,10 +20,10 @@
|
|||||||
namespace AudioCore::Sink {
|
namespace AudioCore::Sink {
|
||||||
|
|
||||||
void SinkStream::AppendBuffer(SinkBuffer& buffer, std::span<s16> samples) {
|
void SinkStream::AppendBuffer(SinkBuffer& buffer, std::span<s16> samples) {
|
||||||
SCOPE_EXIT({
|
SCOPE_EXIT {
|
||||||
queue.enqueue(buffer);
|
queue.enqueue(buffer);
|
||||||
++queued_buffers;
|
++queued_buffers;
|
||||||
});
|
};
|
||||||
|
|
||||||
if (type == StreamType::In) {
|
if (type == StreamType::In) {
|
||||||
return;
|
return;
|
||||||
|
@ -20,7 +20,9 @@ std::string DemangleSymbol(const std::string& mangled) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
char* demangled = nullptr;
|
char* demangled = nullptr;
|
||||||
SCOPE_EXIT({ std::free(demangled); });
|
SCOPE_EXIT {
|
||||||
|
std::free(demangled);
|
||||||
|
};
|
||||||
|
|
||||||
if (is_itanium(mangled)) {
|
if (is_itanium(mangled)) {
|
||||||
demangled = llvm::itaniumDemangle(mangled.c_str());
|
demangled = llvm::itaniumDemangle(mangled.c_str());
|
||||||
|
@ -430,11 +430,11 @@ public:
|
|||||||
explicit Impl(size_t backing_size_, size_t virtual_size_)
|
explicit Impl(size_t backing_size_, size_t virtual_size_)
|
||||||
: backing_size{backing_size_}, virtual_size{virtual_size_} {
|
: backing_size{backing_size_}, virtual_size{virtual_size_} {
|
||||||
bool good = false;
|
bool good = false;
|
||||||
SCOPE_EXIT({
|
SCOPE_EXIT {
|
||||||
if (!good) {
|
if (!good) {
|
||||||
Release();
|
Release();
|
||||||
}
|
}
|
||||||
});
|
};
|
||||||
|
|
||||||
long page_size = sysconf(_SC_PAGESIZE);
|
long page_size = sysconf(_SC_PAGESIZE);
|
||||||
if (page_size != 0x1000) {
|
if (page_size != 0x1000) {
|
||||||
|
@ -24,10 +24,10 @@ bool PageTable::ContinueTraversal(TraversalEntry* out_entry, TraversalContext* c
|
|||||||
out_entry->block_size = page_size;
|
out_entry->block_size = page_size;
|
||||||
|
|
||||||
// Regardless of whether the page was mapped, advance on exit.
|
// Regardless of whether the page was mapped, advance on exit.
|
||||||
SCOPE_EXIT({
|
SCOPE_EXIT {
|
||||||
context->next_page += 1;
|
context->next_page += 1;
|
||||||
context->next_offset += page_size;
|
context->next_offset += page_size;
|
||||||
});
|
};
|
||||||
|
|
||||||
// Validate that we can read the actual entry.
|
// Validate that we can read the actual entry.
|
||||||
const auto page = context->next_page;
|
const auto page = context->next_page;
|
||||||
|
@ -7,29 +7,61 @@
|
|||||||
#include "common/common_funcs.h"
|
#include "common/common_funcs.h"
|
||||||
|
|
||||||
namespace detail {
|
namespace detail {
|
||||||
template <typename Func>
|
template <class F>
|
||||||
struct ScopeExitHelper {
|
class ScopeGuard {
|
||||||
explicit ScopeExitHelper(Func&& func_) : func(std::move(func_)) {}
|
YUZU_NON_COPYABLE(ScopeGuard);
|
||||||
~ScopeExitHelper() {
|
|
||||||
|
private:
|
||||||
|
F f;
|
||||||
|
bool active;
|
||||||
|
|
||||||
|
public:
|
||||||
|
constexpr ScopeGuard(F f_) : f(std::move(f_)), active(true) {}
|
||||||
|
constexpr ~ScopeGuard() {
|
||||||
if (active) {
|
if (active) {
|
||||||
func();
|
f();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
constexpr void Cancel() {
|
||||||
void Cancel() {
|
|
||||||
active = false;
|
active = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Func func;
|
constexpr ScopeGuard(ScopeGuard&& rhs) : f(std::move(rhs.f)), active(rhs.active) {
|
||||||
bool active{true};
|
rhs.Cancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
ScopeGuard& operator=(ScopeGuard&& rhs) = delete;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename Func>
|
template <class F>
|
||||||
ScopeExitHelper<Func> ScopeExit(Func&& func) {
|
constexpr ScopeGuard<F> MakeScopeGuard(F f) {
|
||||||
return ScopeExitHelper<Func>(std::forward<Func>(func));
|
return ScopeGuard<F>(std::move(f));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum class ScopeGuardOnExit {};
|
||||||
|
|
||||||
|
template <typename F>
|
||||||
|
constexpr ScopeGuard<F> operator+(ScopeGuardOnExit, F&& f) {
|
||||||
|
return ScopeGuard<F>(std::forward<F>(f));
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace detail
|
} // 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
|
* 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.
|
* for doing ad-hoc clean-up tasks in a function with multiple returns.
|
||||||
@ -38,7 +70,7 @@ ScopeExitHelper<Func> ScopeExit(Func&& func) {
|
|||||||
* \code
|
* \code
|
||||||
* const int saved_val = g_foo;
|
* const int saved_val = g_foo;
|
||||||
* g_foo = 55;
|
* g_foo = 55;
|
||||||
* SCOPE_EXIT({ g_foo = saved_val; });
|
* SCOPE_EXIT{ g_foo = saved_val; };
|
||||||
*
|
*
|
||||||
* if (Bar()) {
|
* if (Bar()) {
|
||||||
* return 0;
|
* return 0;
|
||||||
@ -47,10 +79,4 @@ ScopeExitHelper<Func> ScopeExit(Func&& func) {
|
|||||||
* }
|
* }
|
||||||
* \endcode
|
* \endcode
|
||||||
*/
|
*/
|
||||||
#define SCOPE_EXIT(body) auto CONCAT2(scope_exit_helper_, __LINE__) = detail::ScopeExit([&]() body)
|
#define SCOPE_EXIT auto ANONYMOUS_VARIABLE(SCOPE_EXIT_STATE_) = SCOPE_GUARD
|
||||||
|
|
||||||
/**
|
|
||||||
* 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)
|
|
||||||
|
@ -2,8 +2,8 @@
|
|||||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
add_library(core STATIC
|
add_library(core STATIC
|
||||||
arm/arm_interface.h
|
|
||||||
arm/arm_interface.cpp
|
arm/arm_interface.cpp
|
||||||
|
arm/arm_interface.h
|
||||||
arm/debug.cpp
|
arm/debug.cpp
|
||||||
arm/debug.h
|
arm/debug.h
|
||||||
arm/exclusive_monitor.cpp
|
arm/exclusive_monitor.cpp
|
||||||
@ -37,10 +37,10 @@ add_library(core STATIC
|
|||||||
debugger/gdbstub.h
|
debugger/gdbstub.h
|
||||||
debugger/gdbstub_arch.cpp
|
debugger/gdbstub_arch.cpp
|
||||||
debugger/gdbstub_arch.h
|
debugger/gdbstub_arch.h
|
||||||
device_memory_manager.h
|
|
||||||
device_memory_manager.inc
|
|
||||||
device_memory.cpp
|
device_memory.cpp
|
||||||
device_memory.h
|
device_memory.h
|
||||||
|
device_memory_manager.h
|
||||||
|
device_memory_manager.inc
|
||||||
file_sys/bis_factory.cpp
|
file_sys/bis_factory.cpp
|
||||||
file_sys/bis_factory.h
|
file_sys/bis_factory.h
|
||||||
file_sys/card_image.cpp
|
file_sys/card_image.cpp
|
||||||
@ -59,8 +59,12 @@ add_library(core STATIC
|
|||||||
file_sys/fs_path.h
|
file_sys/fs_path.h
|
||||||
file_sys/fs_path_utility.h
|
file_sys/fs_path_utility.h
|
||||||
file_sys/fs_string_util.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.cpp
|
||||||
file_sys/fsmitm_romfsbuild.h
|
file_sys/fsmitm_romfsbuild.h
|
||||||
|
file_sys/fssrv/fssrv_sf_path.h
|
||||||
file_sys/fssystem/fs_i_storage.h
|
file_sys/fssystem/fs_i_storage.h
|
||||||
file_sys/fssystem/fs_types.h
|
file_sys/fssystem/fs_types.h
|
||||||
file_sys/fssystem/fssystem_aes_ctr_counter_extended_storage.cpp
|
file_sys/fssystem/fssystem_aes_ctr_counter_extended_storage.cpp
|
||||||
@ -390,6 +394,20 @@ add_library(core STATIC
|
|||||||
hle/service/acc/errors.h
|
hle/service/acc/errors.h
|
||||||
hle/service/acc/profile_manager.cpp
|
hle/service/acc/profile_manager.cpp
|
||||||
hle/service/acc/profile_manager.h
|
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.cpp
|
||||||
hle/service/am/frontend/applet_cabinet.h
|
hle/service/am/frontend/applet_cabinet.h
|
||||||
hle/service/am/frontend/applet_controller.cpp
|
hle/service/am/frontend/applet_controller.cpp
|
||||||
@ -411,24 +429,10 @@ add_library(core STATIC
|
|||||||
hle/service/am/frontend/applet_web_browser_types.h
|
hle/service/am/frontend/applet_web_browser_types.h
|
||||||
hle/service/am/frontend/applets.cpp
|
hle/service/am/frontend/applets.cpp
|
||||||
hle/service/am/frontend/applets.h
|
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.cpp
|
||||||
hle/service/am/hid_registration.h
|
hle/service/am/hid_registration.h
|
||||||
hle/service/am/library_applet_storage.cpp
|
hle/service/am/library_applet_storage.cpp
|
||||||
hle/service/am/library_applet_storage.h
|
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.cpp
|
||||||
hle/service/am/process.h
|
hle/service/am/process.h
|
||||||
hle/service/am/service/all_system_applet_proxies_service.cpp
|
hle/service/am/service/all_system_applet_proxies_service.cpp
|
||||||
@ -441,10 +445,10 @@ add_library(core STATIC
|
|||||||
hle/service/am/service/application_creator.h
|
hle/service/am/service/application_creator.h
|
||||||
hle/service/am/service/application_functions.cpp
|
hle/service/am/service/application_functions.cpp
|
||||||
hle/service/am/service/application_functions.h
|
hle/service/am/service/application_functions.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.cpp
|
||||||
hle/service/am/service/application_proxy.h
|
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/audio_controller.cpp
|
hle/service/am/service/audio_controller.cpp
|
||||||
hle/service/am/service/audio_controller.h
|
hle/service/am/service/audio_controller.h
|
||||||
hle/service/am/service/common_state_getter.cpp
|
hle/service/am/service/common_state_getter.cpp
|
||||||
@ -473,16 +477,14 @@ add_library(core STATIC
|
|||||||
hle/service/am/service/process_winding_controller.h
|
hle/service/am/service/process_winding_controller.h
|
||||||
hle/service/am/service/self_controller.cpp
|
hle/service/am/service/self_controller.cpp
|
||||||
hle/service/am/service/self_controller.h
|
hle/service/am/service/self_controller.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.cpp
|
||||||
hle/service/am/service/storage.h
|
hle/service/am/service/storage.h
|
||||||
|
hle/service/am/service/storage_accessor.cpp
|
||||||
|
hle/service/am/service/storage_accessor.h
|
||||||
hle/service/am/service/system_applet_proxy.cpp
|
hle/service/am/service/system_applet_proxy.cpp
|
||||||
hle/service/am/service/system_applet_proxy.h
|
hle/service/am/service/system_applet_proxy.h
|
||||||
hle/service/am/service/window_controller.cpp
|
hle/service/am/service/window_controller.cpp
|
||||||
hle/service/am/service/window_controller.h
|
hle/service/am/service/window_controller.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.cpp
|
||||||
hle/service/aoc/aoc_u.h
|
hle/service/aoc/aoc_u.h
|
||||||
hle/service/apm/apm.cpp
|
hle/service/apm/apm.cpp
|
||||||
@ -491,12 +493,12 @@ add_library(core STATIC
|
|||||||
hle/service/apm/apm_controller.h
|
hle/service/apm/apm_controller.h
|
||||||
hle/service/apm/apm_interface.cpp
|
hle/service/apm/apm_interface.cpp
|
||||||
hle/service/apm/apm_interface.h
|
hle/service/apm/apm_interface.h
|
||||||
hle/service/audio/audctl.cpp
|
|
||||||
hle/service/audio/audctl.h
|
|
||||||
hle/service/audio/audin_u.cpp
|
hle/service/audio/audin_u.cpp
|
||||||
hle/service/audio/audin_u.h
|
hle/service/audio/audin_u.h
|
||||||
hle/service/audio/audio.cpp
|
hle/service/audio/audio.cpp
|
||||||
hle/service/audio/audio.h
|
hle/service/audio/audio.h
|
||||||
|
hle/service/audio/audio_controller.cpp
|
||||||
|
hle/service/audio/audio_controller.h
|
||||||
hle/service/audio/audout_u.cpp
|
hle/service/audio/audout_u.cpp
|
||||||
hle/service/audio/audout_u.h
|
hle/service/audio/audout_u.h
|
||||||
hle/service/audio/audrec_a.cpp
|
hle/service/audio/audrec_a.cpp
|
||||||
@ -510,18 +512,6 @@ add_library(core STATIC
|
|||||||
hle/service/audio/hwopus.h
|
hle/service/audio/hwopus.h
|
||||||
hle/service/bcat/backend/backend.cpp
|
hle/service/bcat/backend/backend.cpp
|
||||||
hle/service/bcat/backend/backend.h
|
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.cpp
|
||||||
hle/service/bcat/bcat.h
|
hle/service/bcat/bcat.h
|
||||||
hle/service/bcat/bcat_result.h
|
hle/service/bcat/bcat_result.h
|
||||||
@ -537,6 +527,18 @@ add_library(core STATIC
|
|||||||
hle/service/bcat/delivery_cache_progress_service.h
|
hle/service/bcat/delivery_cache_progress_service.h
|
||||||
hle/service/bcat/delivery_cache_storage_service.cpp
|
hle/service/bcat/delivery_cache_storage_service.cpp
|
||||||
hle/service/bcat/delivery_cache_storage_service.h
|
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.cpp
|
||||||
hle/service/bcat/service_creator.h
|
hle/service/bcat/service_creator.h
|
||||||
hle/service/bpc/bpc.cpp
|
hle/service/bpc/bpc.cpp
|
||||||
@ -545,6 +547,16 @@ add_library(core STATIC
|
|||||||
hle/service/btdrv/btdrv.h
|
hle/service/btdrv/btdrv.h
|
||||||
hle/service/btm/btm.cpp
|
hle/service/btm/btm.cpp
|
||||||
hle/service/btm/btm.h
|
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.cpp
|
||||||
hle/service/caps/caps.h
|
hle/service/caps/caps.h
|
||||||
hle/service/caps/caps_a.cpp
|
hle/service/caps/caps_a.cpp
|
||||||
@ -600,8 +612,6 @@ add_library(core STATIC
|
|||||||
hle/service/filesystem/romfs_controller.h
|
hle/service/filesystem/romfs_controller.h
|
||||||
hle/service/filesystem/save_data_controller.cpp
|
hle/service/filesystem/save_data_controller.cpp
|
||||||
hle/service/filesystem/save_data_controller.h
|
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.cpp
|
||||||
hle/service/friend/friend.h
|
hle/service/friend/friend.h
|
||||||
hle/service/friend/friend_interface.cpp
|
hle/service/friend/friend_interface.cpp
|
||||||
@ -739,15 +749,48 @@ add_library(core STATIC
|
|||||||
hle/service/nim/nim.h
|
hle/service/nim/nim.h
|
||||||
hle/service/npns/npns.cpp
|
hle/service/npns/npns.cpp
|
||||||
hle/service/npns/npns.h
|
hle/service/npns/npns.h
|
||||||
hle/service/ns/errors.h
|
hle/service/ns/account_proxy_interface.cpp
|
||||||
hle/service/ns/iplatform_service_manager.cpp
|
hle/service/ns/account_proxy_interface.h
|
||||||
hle/service/ns/iplatform_service_manager.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/language.cpp
|
hle/service/ns/language.cpp
|
||||||
hle/service/ns/language.h
|
hle/service/ns/language.h
|
||||||
hle/service/ns/ns.cpp
|
hle/service/ns/ns.cpp
|
||||||
hle/service/ns/ns.h
|
hle/service/ns/ns.h
|
||||||
hle/service/ns/pdm_qry.cpp
|
hle/service/ns/ns_results.h
|
||||||
hle/service/ns/pdm_qry.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/nvdrv/core/container.cpp
|
hle/service/nvdrv/core/container.cpp
|
||||||
hle/service/nvdrv/core/container.h
|
hle/service/nvdrv/core/container.h
|
||||||
hle/service/nvdrv/core/heap_mapper.cpp
|
hle/service/nvdrv/core/heap_mapper.cpp
|
||||||
@ -800,14 +843,14 @@ add_library(core STATIC
|
|||||||
hle/service/nvnflinger/consumer_base.cpp
|
hle/service/nvnflinger/consumer_base.cpp
|
||||||
hle/service/nvnflinger/consumer_base.h
|
hle/service/nvnflinger/consumer_base.h
|
||||||
hle/service/nvnflinger/consumer_listener.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.cpp
|
||||||
hle/service/nvnflinger/graphic_buffer_producer.h
|
hle/service/nvnflinger/graphic_buffer_producer.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.cpp
|
||||||
hle/service/nvnflinger/hardware_composer.h
|
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/hwc_layer.h
|
hle/service/nvnflinger/hwc_layer.h
|
||||||
hle/service/nvnflinger/nvnflinger.cpp
|
hle/service/nvnflinger/nvnflinger.cpp
|
||||||
hle/service/nvnflinger/nvnflinger.h
|
hle/service/nvnflinger/nvnflinger.h
|
||||||
@ -815,6 +858,8 @@ add_library(core STATIC
|
|||||||
hle/service/nvnflinger/pixel_format.h
|
hle/service/nvnflinger/pixel_format.h
|
||||||
hle/service/nvnflinger/producer_listener.h
|
hle/service/nvnflinger/producer_listener.h
|
||||||
hle/service/nvnflinger/status.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/fence.h
|
||||||
hle/service/nvnflinger/ui/graphic_buffer.cpp
|
hle/service/nvnflinger/ui/graphic_buffer.cpp
|
||||||
hle/service/nvnflinger/ui/graphic_buffer.h
|
hle/service/nvnflinger/ui/graphic_buffer.h
|
||||||
@ -831,11 +876,11 @@ add_library(core STATIC
|
|||||||
hle/service/omm/power_state_interface.h
|
hle/service/omm/power_state_interface.h
|
||||||
hle/service/os/event.cpp
|
hle/service/os/event.cpp
|
||||||
hle/service/os/event.h
|
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.cpp
|
||||||
hle/service/os/multi_wait_holder.h
|
hle/service/os/multi_wait_holder.h
|
||||||
hle/service/os/multi_wait_utils.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.cpp
|
||||||
hle/service/os/mutex.h
|
hle/service/os/mutex.h
|
||||||
hle/service/pcie/pcie.cpp
|
hle/service/pcie/pcie.cpp
|
||||||
@ -873,15 +918,17 @@ add_library(core STATIC
|
|||||||
hle/service/psc/time/common.cpp
|
hle/service/psc/time/common.cpp
|
||||||
hle/service/psc/time/common.h
|
hle/service/psc/time/common.h
|
||||||
hle/service/psc/time/errors.h
|
hle/service/psc/time/errors.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/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.cpp
|
||||||
hle/service/psc/time/power_state_service.h
|
hle/service/psc/time/power_state_service.h
|
||||||
hle/service/psc/time/service_manager.cpp
|
hle/service/psc/time/service_manager.cpp
|
||||||
hle/service/psc/time/service_manager.h
|
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/steady_clock.cpp
|
hle/service/psc/time/steady_clock.cpp
|
||||||
hle/service/psc/time/steady_clock.h
|
hle/service/psc/time/steady_clock.h
|
||||||
hle/service/psc/time/system_clock.cpp
|
hle/service/psc/time/system_clock.cpp
|
||||||
@ -890,8 +937,6 @@ add_library(core STATIC
|
|||||||
hle/service/psc/time/time_zone.h
|
hle/service/psc/time/time_zone.h
|
||||||
hle/service/psc/time/time_zone_service.cpp
|
hle/service/psc/time/time_zone_service.cpp
|
||||||
hle/service/psc/time/time_zone_service.h
|
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.cpp
|
||||||
hle/service/ptm/psm.h
|
hle/service/ptm/psm.h
|
||||||
hle/service/ptm/ptm.cpp
|
hle/service/ptm/ptm.cpp
|
||||||
@ -908,19 +953,21 @@ add_library(core STATIC
|
|||||||
hle/service/server_manager.h
|
hle/service/server_manager.h
|
||||||
hle/service/service.cpp
|
hle/service/service.cpp
|
||||||
hle/service/service.h
|
hle/service/service.h
|
||||||
hle/service/set/setting_formats/appln_settings.cpp
|
hle/service/services.cpp
|
||||||
hle/service/set/setting_formats/appln_settings.h
|
hle/service/services.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.cpp
|
||||||
hle/service/set/factory_settings_server.h
|
hle/service/set/factory_settings_server.h
|
||||||
hle/service/set/firmware_debug_settings_server.cpp
|
hle/service/set/firmware_debug_settings_server.cpp
|
||||||
hle/service/set/firmware_debug_settings_server.h
|
hle/service/set/firmware_debug_settings_server.h
|
||||||
hle/service/set/key_code_map.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.cpp
|
||||||
hle/service/set/settings.h
|
hle/service/set/settings.h
|
||||||
hle/service/set/settings_server.cpp
|
hle/service/set/settings_server.cpp
|
||||||
@ -955,30 +1002,36 @@ add_library(core STATIC
|
|||||||
hle/service/ssl/ssl_backend.h
|
hle/service/ssl/ssl_backend.h
|
||||||
hle/service/usb/usb.cpp
|
hle/service/usb/usb.cpp
|
||||||
hle/service/usb/usb.h
|
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.cpp
|
||||||
hle/service/vi/application_display_service.h
|
hle/service/vi/application_display_service.h
|
||||||
hle/service/vi/application_root_service.cpp
|
hle/service/vi/application_root_service.cpp
|
||||||
hle/service/vi/application_root_service.h
|
hle/service/vi/application_root_service.h
|
||||||
hle/service/vi/hos_binder_driver.cpp
|
hle/service/vi/conductor.cpp
|
||||||
hle/service/vi/hos_binder_driver.h
|
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/manager_display_service.cpp
|
hle/service/vi/manager_display_service.cpp
|
||||||
hle/service/vi/manager_display_service.h
|
hle/service/vi/manager_display_service.h
|
||||||
hle/service/vi/manager_root_service.cpp
|
hle/service/vi/manager_root_service.cpp
|
||||||
hle/service/vi/manager_root_service.h
|
hle/service/vi/manager_root_service.h
|
||||||
hle/service/vi/service_creator.cpp
|
hle/service/vi/service_creator.cpp
|
||||||
hle/service/vi/service_creator.h
|
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.cpp
|
||||||
hle/service/vi/system_display_service.h
|
hle/service/vi/system_display_service.h
|
||||||
hle/service/vi/system_root_service.cpp
|
hle/service/vi/system_root_service.cpp
|
||||||
hle/service/vi/system_root_service.h
|
hle/service/vi/system_root_service.h
|
||||||
hle/service/vi/vi_results.h
|
|
||||||
hle/service/vi/vi_types.h
|
|
||||||
hle/service/vi/vi.cpp
|
hle/service/vi/vi.cpp
|
||||||
hle/service/vi/vi.h
|
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
|
||||||
internal_network/network.cpp
|
internal_network/network.cpp
|
||||||
internal_network/network.h
|
internal_network/network.h
|
||||||
internal_network/network_interface.cpp
|
internal_network/network_interface.cpp
|
||||||
|
@ -47,6 +47,7 @@
|
|||||||
#include "core/hle/service/psc/time/system_clock.h"
|
#include "core/hle/service/psc/time/system_clock.h"
|
||||||
#include "core/hle/service/psc/time/time_zone_service.h"
|
#include "core/hle/service/psc/time/time_zone_service.h"
|
||||||
#include "core/hle/service/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/set/system_settings_server.h"
|
||||||
#include "core/hle/service/sm/sm.h"
|
#include "core/hle/service/sm/sm.h"
|
||||||
#include "core/internal_network/network.h"
|
#include "core/internal_network/network.h"
|
||||||
@ -310,7 +311,8 @@ struct System::Impl {
|
|||||||
audio_core = std::make_unique<AudioCore::AudioCore>(system);
|
audio_core = std::make_unique<AudioCore::AudioCore>(system);
|
||||||
|
|
||||||
service_manager = std::make_shared<Service::SM::ServiceManager>(kernel);
|
service_manager = std::make_shared<Service::SM::ServiceManager>(kernel);
|
||||||
services = std::make_unique<Service::Services>(service_manager, system);
|
services =
|
||||||
|
std::make_unique<Service::Services>(service_manager, system, stop_event.get_token());
|
||||||
|
|
||||||
is_powered_on = true;
|
is_powered_on = true;
|
||||||
exit_locked = false;
|
exit_locked = false;
|
||||||
@ -458,11 +460,10 @@ struct System::Impl {
|
|||||||
gpu_core->NotifyShutdown();
|
gpu_core->NotifyShutdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
stop_event.request_stop();
|
||||||
|
core_timing.SyncPause(false);
|
||||||
Network::CancelPendingSocketOperations();
|
Network::CancelPendingSocketOperations();
|
||||||
kernel.SuspendEmulation(true);
|
kernel.SuspendEmulation(true);
|
||||||
if (services) {
|
|
||||||
services->KillNVNFlinger();
|
|
||||||
}
|
|
||||||
kernel.CloseServices();
|
kernel.CloseServices();
|
||||||
kernel.ShutdownCores();
|
kernel.ShutdownCores();
|
||||||
applet_manager.Reset();
|
applet_manager.Reset();
|
||||||
@ -480,6 +481,7 @@ struct System::Impl {
|
|||||||
cpu_manager.Shutdown();
|
cpu_manager.Shutdown();
|
||||||
debugger.reset();
|
debugger.reset();
|
||||||
kernel.Shutdown();
|
kernel.Shutdown();
|
||||||
|
stop_event = {};
|
||||||
Network::RestartSocketOperations();
|
Network::RestartSocketOperations();
|
||||||
|
|
||||||
if (auto room_member = room_network.GetRoomMember().lock()) {
|
if (auto room_member = room_network.GetRoomMember().lock()) {
|
||||||
@ -615,6 +617,7 @@ struct System::Impl {
|
|||||||
|
|
||||||
ExecuteProgramCallback execute_program_callback;
|
ExecuteProgramCallback execute_program_callback;
|
||||||
ExitCallback exit_callback;
|
ExitCallback exit_callback;
|
||||||
|
std::stop_source stop_event;
|
||||||
|
|
||||||
std::array<u64, Core::Hardware::NUM_CPU_CORES> dynarmic_ticks{};
|
std::array<u64, Core::Hardware::NUM_CPU_CORES> dynarmic_ticks{};
|
||||||
std::array<MicroProfileToken, Core::Hardware::NUM_CPU_CORES> microprofile_cpu{};
|
std::array<MicroProfileToken, Core::Hardware::NUM_CPU_CORES> microprofile_cpu{};
|
||||||
|
@ -199,10 +199,10 @@ void CpuManager::RunThread(std::stop_token token, std::size_t core) {
|
|||||||
data.host_context = Common::Fiber::ThreadToFiber();
|
data.host_context = Common::Fiber::ThreadToFiber();
|
||||||
|
|
||||||
// Cleanup
|
// Cleanup
|
||||||
SCOPE_EXIT({
|
SCOPE_EXIT {
|
||||||
data.host_context->Exit();
|
data.host_context->Exit();
|
||||||
MicroProfileOnThreadExit();
|
MicroProfileOnThreadExit();
|
||||||
});
|
};
|
||||||
|
|
||||||
// Running
|
// Running
|
||||||
if (!gpu_barrier->Sync(token)) {
|
if (!gpu_barrier->Sync(token)) {
|
||||||
|
@ -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);
|
std::min((next_pages << Memory::YUZU_PAGEBITS) - page_offset, remaining_size);
|
||||||
const auto current_vaddr =
|
const auto current_vaddr =
|
||||||
static_cast<u64>((page_index << Memory::YUZU_PAGEBITS) + page_offset);
|
static_cast<u64>((page_index << Memory::YUZU_PAGEBITS) + page_offset);
|
||||||
SCOPE_EXIT({
|
SCOPE_EXIT{
|
||||||
page_index += next_pages;
|
page_index += next_pages;
|
||||||
page_offset = 0;
|
page_offset = 0;
|
||||||
increment(copy_amount);
|
increment(copy_amount);
|
||||||
remaining_size -= copy_amount;
|
remaining_size -= copy_amount;
|
||||||
});
|
};
|
||||||
|
|
||||||
auto phys_addr = compressed_physical_ptr[page_index];
|
auto phys_addr = compressed_physical_ptr[page_index];
|
||||||
if (phys_addr == 0) {
|
if (phys_addr == 0) {
|
||||||
|
@ -64,8 +64,8 @@ struct RawNACP {
|
|||||||
u64_le cache_storage_size;
|
u64_le cache_storage_size;
|
||||||
u64_le cache_storage_journal_size;
|
u64_le cache_storage_journal_size;
|
||||||
u64_le cache_storage_data_and_journal_max_size;
|
u64_le cache_storage_data_and_journal_max_size;
|
||||||
u64_le cache_storage_max_index;
|
u16_le cache_storage_max_index;
|
||||||
INSERT_PADDING_BYTES(0xE70);
|
INSERT_PADDING_BYTES(0xE76);
|
||||||
};
|
};
|
||||||
static_assert(sizeof(RawNACP) == 0x4000, "RawNACP has incorrect size.");
|
static_assert(sizeof(RawNACP) == 0x4000, "RawNACP has incorrect size.");
|
||||||
|
|
||||||
|
@ -3,6 +3,10 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <string_view>
|
||||||
|
#include "common/common_funcs.h"
|
||||||
|
#include "common/common_types.h"
|
||||||
|
|
||||||
namespace FileSys {
|
namespace FileSys {
|
||||||
|
|
||||||
constexpr inline size_t EntryNameLengthMax = 0x300;
|
constexpr inline size_t EntryNameLengthMax = 0x300;
|
||||||
|
@ -23,6 +23,8 @@ enum class OpenDirectoryMode : u64 {
|
|||||||
File = (1 << 1),
|
File = (1 << 1),
|
||||||
|
|
||||||
All = (Directory | File),
|
All = (Directory | File),
|
||||||
|
|
||||||
|
NotRequireFileSize = (1ULL << 31),
|
||||||
};
|
};
|
||||||
DECLARE_ENUM_FLAG_OPERATORS(OpenDirectoryMode)
|
DECLARE_ENUM_FLAG_OPERATORS(OpenDirectoryMode)
|
||||||
|
|
||||||
@ -36,4 +38,29 @@ enum class CreateOption : u8 {
|
|||||||
BigFile = (1 << 0),
|
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
|
} // namespace FileSys
|
||||||
|
@ -10,7 +10,7 @@ namespace FileSys {
|
|||||||
|
|
||||||
constexpr size_t RequiredAlignment = alignof(u64);
|
constexpr size_t RequiredAlignment = alignof(u64);
|
||||||
|
|
||||||
void* AllocateUnsafe(size_t size) {
|
inline void* AllocateUnsafe(size_t size) {
|
||||||
// Allocate
|
// Allocate
|
||||||
void* const ptr = ::operator new(size, std::align_val_t{RequiredAlignment});
|
void* const ptr = ::operator new(size, std::align_val_t{RequiredAlignment});
|
||||||
|
|
||||||
@ -21,16 +21,16 @@ void* AllocateUnsafe(size_t size) {
|
|||||||
return ptr;
|
return ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DeallocateUnsafe(void* ptr, size_t size) {
|
inline void DeallocateUnsafe(void* ptr, size_t size) {
|
||||||
// Deallocate the pointer
|
// Deallocate the pointer
|
||||||
::operator delete(ptr, std::align_val_t{RequiredAlignment});
|
::operator delete(ptr, std::align_val_t{RequiredAlignment});
|
||||||
}
|
}
|
||||||
|
|
||||||
void* Allocate(size_t size) {
|
inline void* Allocate(size_t size) {
|
||||||
return AllocateUnsafe(size);
|
return AllocateUnsafe(size);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Deallocate(void* ptr, size_t size) {
|
inline void Deallocate(void* ptr, size_t size) {
|
||||||
// If the pointer is non-null, deallocate it
|
// If the pointer is non-null, deallocate it
|
||||||
if (ptr != nullptr) {
|
if (ptr != nullptr) {
|
||||||
DeallocateUnsafe(ptr, size);
|
DeallocateUnsafe(ptr, size);
|
||||||
|
@ -381,7 +381,7 @@ public:
|
|||||||
|
|
||||||
// Check that it's possible for us to remove a child
|
// Check that it's possible for us to remove a child
|
||||||
auto* p = m_write_buffer.Get();
|
auto* p = m_write_buffer.Get();
|
||||||
s32 len = std::strlen(p);
|
s32 len = static_cast<s32>(std::strlen(p));
|
||||||
R_UNLESS(len != 1 || (p[0] != '/' && p[0] != '.'), ResultNotImplemented);
|
R_UNLESS(len != 1 || (p[0] != '/' && p[0] != '.'), ResultNotImplemented);
|
||||||
|
|
||||||
// Handle a trailing separator
|
// Handle a trailing separator
|
||||||
|
@ -426,9 +426,10 @@ public:
|
|||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|
||||||
static Result Normalize(char* dst, size_t* out_len, const char* path, size_t max_out_size,
|
static constexpr Result Normalize(char* dst, size_t* out_len, const char* path,
|
||||||
bool is_windows_path, bool is_drive_relative_path,
|
size_t max_out_size, bool is_windows_path,
|
||||||
bool allow_all_characters = false) {
|
bool is_drive_relative_path,
|
||||||
|
bool allow_all_characters = false) {
|
||||||
// Use StringTraits names for remainder of scope
|
// Use StringTraits names for remainder of scope
|
||||||
using namespace StringTraits;
|
using namespace StringTraits;
|
||||||
|
|
||||||
@ -447,7 +448,7 @@ public:
|
|||||||
char* replacement_path = nullptr;
|
char* replacement_path = nullptr;
|
||||||
size_t replacement_path_size = 0;
|
size_t replacement_path_size = 0;
|
||||||
|
|
||||||
SCOPE_EXIT({
|
SCOPE_EXIT {
|
||||||
if (replacement_path != nullptr) {
|
if (replacement_path != nullptr) {
|
||||||
if (std::is_constant_evaluated()) {
|
if (std::is_constant_evaluated()) {
|
||||||
delete[] replacement_path;
|
delete[] replacement_path;
|
||||||
@ -455,7 +456,7 @@ public:
|
|||||||
Deallocate(replacement_path, replacement_path_size);
|
Deallocate(replacement_path, replacement_path_size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
};
|
||||||
|
|
||||||
// Perform path replacement, if necessary
|
// Perform path replacement, if necessary
|
||||||
if (IsParentDirectoryPathReplacementNeeded(cur_path)) {
|
if (IsParentDirectoryPathReplacementNeeded(cur_path)) {
|
||||||
@ -1102,8 +1103,8 @@ public:
|
|||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|
||||||
static Result Normalize(char* dst, size_t dst_size, const char* path, size_t path_len,
|
static constexpr Result Normalize(char* dst, size_t dst_size, const char* path, size_t path_len,
|
||||||
const PathFlags& flags) {
|
const PathFlags& flags) {
|
||||||
// Use StringTraits names for remainder of scope
|
// Use StringTraits names for remainder of scope
|
||||||
using namespace StringTraits;
|
using namespace StringTraits;
|
||||||
|
|
||||||
@ -1199,7 +1200,7 @@ public:
|
|||||||
const size_t replaced_src_len = path_len - (src - path);
|
const size_t replaced_src_len = path_len - (src - path);
|
||||||
|
|
||||||
char* replaced_src = nullptr;
|
char* replaced_src = nullptr;
|
||||||
SCOPE_EXIT({
|
SCOPE_EXIT {
|
||||||
if (replaced_src != nullptr) {
|
if (replaced_src != nullptr) {
|
||||||
if (std::is_constant_evaluated()) {
|
if (std::is_constant_evaluated()) {
|
||||||
delete[] replaced_src;
|
delete[] replaced_src;
|
||||||
@ -1207,7 +1208,7 @@ public:
|
|||||||
Deallocate(replaced_src, replaced_src_len);
|
Deallocate(replaced_src, replaced_src_len);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
};
|
||||||
|
|
||||||
if (std::is_constant_evaluated()) {
|
if (std::is_constant_evaluated()) {
|
||||||
replaced_src = new char[replaced_src_len];
|
replaced_src = new char[replaced_src_len];
|
||||||
|
@ -19,6 +19,11 @@ constexpr int Strlen(const T* str) {
|
|||||||
return length;
|
return length;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
constexpr int Strnlen(const T* str, std::size_t count) {
|
||||||
|
return Strnlen(str, static_cast<int>(count));
|
||||||
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
constexpr int Strnlen(const T* str, int count) {
|
constexpr int Strnlen(const T* str, int count) {
|
||||||
ASSERT(str != nullptr);
|
ASSERT(str != nullptr);
|
||||||
@ -32,6 +37,11 @@ constexpr int Strnlen(const T* str, int count) {
|
|||||||
return length;
|
return length;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
constexpr int Strncmp(const T* lhs, const T* rhs, std::size_t count) {
|
||||||
|
return Strncmp(lhs, rhs, static_cast<int>(count));
|
||||||
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
constexpr int Strncmp(const T* lhs, const T* rhs, int count) {
|
constexpr int Strncmp(const T* lhs, const T* rhs, int count) {
|
||||||
ASSERT(lhs != nullptr);
|
ASSERT(lhs != nullptr);
|
||||||
@ -51,6 +61,11 @@ constexpr int Strncmp(const T* lhs, const T* rhs, int count) {
|
|||||||
return l - r;
|
return l - r;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
static constexpr int Strlcpy(T* dst, const T* src, std::size_t count) {
|
||||||
|
return Strlcpy<T>(dst, src, static_cast<int>(count));
|
||||||
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
static constexpr int Strlcpy(T* dst, const T* src, int count) {
|
static constexpr int Strlcpy(T* dst, const T* src, int count) {
|
||||||
ASSERT(dst != nullptr);
|
ASSERT(dst != nullptr);
|
||||||
|
91
src/core/file_sys/fsa/fs_i_directory.h
Normal file
91
src/core/file_sys/fsa/fs_i_directory.h
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "common/common_types.h"
|
||||||
|
#include "core/file_sys/errors.h"
|
||||||
|
#include "core/file_sys/fs_directory.h"
|
||||||
|
#include "core/file_sys/fs_file.h"
|
||||||
|
#include "core/file_sys/fs_filesystem.h"
|
||||||
|
#include "core/file_sys/savedata_factory.h"
|
||||||
|
#include "core/file_sys/vfs/vfs.h"
|
||||||
|
#include "core/hle/result.h"
|
||||||
|
|
||||||
|
namespace FileSys::Fsa {
|
||||||
|
|
||||||
|
class IDirectory {
|
||||||
|
public:
|
||||||
|
explicit IDirectory(VirtualDir backend_, OpenDirectoryMode mode)
|
||||||
|
: backend(std::move(backend_)) {
|
||||||
|
// TODO(DarkLordZach): Verify that this is the correct behavior.
|
||||||
|
// Build entry index now to save time later.
|
||||||
|
if (True(mode & OpenDirectoryMode::Directory)) {
|
||||||
|
BuildEntryIndex(backend->GetSubdirectories(), DirectoryEntryType::Directory);
|
||||||
|
}
|
||||||
|
if (True(mode & OpenDirectoryMode::File)) {
|
||||||
|
BuildEntryIndex(backend->GetFiles(), DirectoryEntryType::File);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
virtual ~IDirectory() {}
|
||||||
|
|
||||||
|
Result Read(s64* out_count, DirectoryEntry* out_entries, s64 max_entries) {
|
||||||
|
R_UNLESS(out_count != nullptr, ResultNullptrArgument);
|
||||||
|
if (max_entries == 0) {
|
||||||
|
*out_count = 0;
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
|
R_UNLESS(out_entries != nullptr, ResultNullptrArgument);
|
||||||
|
R_UNLESS(max_entries > 0, ResultInvalidArgument);
|
||||||
|
R_RETURN(this->DoRead(out_count, out_entries, max_entries));
|
||||||
|
}
|
||||||
|
|
||||||
|
Result GetEntryCount(s64* out) {
|
||||||
|
R_UNLESS(out != nullptr, ResultNullptrArgument);
|
||||||
|
R_RETURN(this->DoGetEntryCount(out));
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
Result DoRead(s64* out_count, DirectoryEntry* out_entries, s64 max_entries) {
|
||||||
|
const u64 actual_entries =
|
||||||
|
std::min(static_cast<u64>(max_entries), entries.size() - next_entry_index);
|
||||||
|
const auto* begin = reinterpret_cast<u8*>(entries.data() + next_entry_index);
|
||||||
|
const auto* end = reinterpret_cast<u8*>(entries.data() + next_entry_index + actual_entries);
|
||||||
|
const auto range_size = static_cast<std::size_t>(std::distance(begin, end));
|
||||||
|
|
||||||
|
next_entry_index += actual_entries;
|
||||||
|
*out_count = actual_entries;
|
||||||
|
|
||||||
|
std::memcpy(out_entries, begin, range_size);
|
||||||
|
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result DoGetEntryCount(s64* out) {
|
||||||
|
*out = entries.size() - next_entry_index;
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Remove this when VFS is gone
|
||||||
|
template <typename T>
|
||||||
|
void BuildEntryIndex(const std::vector<T>& new_data, DirectoryEntryType type) {
|
||||||
|
entries.reserve(entries.size() + new_data.size());
|
||||||
|
|
||||||
|
for (const auto& new_entry : new_data) {
|
||||||
|
auto name = new_entry->GetName();
|
||||||
|
|
||||||
|
if (type == DirectoryEntryType::File && name == GetSaveDataSizeFileName()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
entries.emplace_back(name, static_cast<s8>(type),
|
||||||
|
type == DirectoryEntryType::Directory ? 0 : new_entry->GetSize());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
VirtualDir backend;
|
||||||
|
std::vector<DirectoryEntry> entries;
|
||||||
|
u64 next_entry_index = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace FileSys::Fsa
|
167
src/core/file_sys/fsa/fs_i_file.h
Normal file
167
src/core/file_sys/fsa/fs_i_file.h
Normal file
@ -0,0 +1,167 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "common/overflow.h"
|
||||||
|
#include "core/file_sys/errors.h"
|
||||||
|
#include "core/file_sys/fs_file.h"
|
||||||
|
#include "core/file_sys/fs_filesystem.h"
|
||||||
|
#include "core/file_sys/fs_operate_range.h"
|
||||||
|
#include "core/file_sys/vfs/vfs.h"
|
||||||
|
#include "core/file_sys/vfs/vfs_types.h"
|
||||||
|
#include "core/hle/result.h"
|
||||||
|
|
||||||
|
namespace FileSys::Fsa {
|
||||||
|
|
||||||
|
class IFile {
|
||||||
|
public:
|
||||||
|
explicit IFile(VirtualFile backend_) : backend(std::move(backend_)) {}
|
||||||
|
virtual ~IFile() {}
|
||||||
|
|
||||||
|
Result Read(size_t* out, s64 offset, void* buffer, size_t size, const ReadOption& option) {
|
||||||
|
// Check that we have an output pointer
|
||||||
|
R_UNLESS(out != nullptr, ResultNullptrArgument);
|
||||||
|
|
||||||
|
// If we have nothing to read, just succeed
|
||||||
|
if (size == 0) {
|
||||||
|
*out = 0;
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that the read is valid
|
||||||
|
R_UNLESS(buffer != nullptr, ResultNullptrArgument);
|
||||||
|
R_UNLESS(offset >= 0, ResultOutOfRange);
|
||||||
|
R_UNLESS(Common::CanAddWithoutOverflow<s64>(offset, size), ResultOutOfRange);
|
||||||
|
|
||||||
|
// Do the read
|
||||||
|
R_RETURN(this->DoRead(out, offset, buffer, size, option));
|
||||||
|
}
|
||||||
|
|
||||||
|
Result Read(size_t* out, s64 offset, void* buffer, size_t size) {
|
||||||
|
R_RETURN(this->Read(out, offset, buffer, size, ReadOption::None));
|
||||||
|
}
|
||||||
|
|
||||||
|
Result GetSize(s64* out) {
|
||||||
|
R_UNLESS(out != nullptr, ResultNullptrArgument);
|
||||||
|
R_RETURN(this->DoGetSize(out));
|
||||||
|
}
|
||||||
|
|
||||||
|
Result Flush() {
|
||||||
|
R_RETURN(this->DoFlush());
|
||||||
|
}
|
||||||
|
|
||||||
|
Result Write(s64 offset, const void* buffer, size_t size, const WriteOption& option) {
|
||||||
|
// Handle the zero-size case
|
||||||
|
if (size == 0) {
|
||||||
|
if (option.HasFlushFlag()) {
|
||||||
|
R_TRY(this->Flush());
|
||||||
|
}
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check the write is valid
|
||||||
|
R_UNLESS(buffer != nullptr, ResultNullptrArgument);
|
||||||
|
R_UNLESS(offset >= 0, ResultOutOfRange);
|
||||||
|
R_UNLESS(Common::CanAddWithoutOverflow<s64>(offset, size), ResultOutOfRange);
|
||||||
|
|
||||||
|
R_RETURN(this->DoWrite(offset, buffer, size, option));
|
||||||
|
}
|
||||||
|
|
||||||
|
Result SetSize(s64 size) {
|
||||||
|
R_UNLESS(size >= 0, ResultOutOfRange);
|
||||||
|
R_RETURN(this->DoSetSize(size));
|
||||||
|
}
|
||||||
|
|
||||||
|
Result OperateRange(void* dst, size_t dst_size, OperationId op_id, s64 offset, s64 size,
|
||||||
|
const void* src, size_t src_size) {
|
||||||
|
R_RETURN(this->DoOperateRange(dst, dst_size, op_id, offset, size, src, src_size));
|
||||||
|
}
|
||||||
|
|
||||||
|
Result OperateRange(OperationId op_id, s64 offset, s64 size) {
|
||||||
|
R_RETURN(this->DoOperateRange(nullptr, 0, op_id, offset, size, nullptr, 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
Result DryRead(size_t* out, s64 offset, size_t size, const ReadOption& option,
|
||||||
|
OpenMode open_mode) {
|
||||||
|
// Check that we can read
|
||||||
|
R_UNLESS(static_cast<u32>(open_mode & OpenMode::Read) != 0, ResultReadNotPermitted);
|
||||||
|
|
||||||
|
// Get the file size, and validate our offset
|
||||||
|
s64 file_size = 0;
|
||||||
|
R_TRY(this->DoGetSize(std::addressof(file_size)));
|
||||||
|
R_UNLESS(offset <= file_size, ResultOutOfRange);
|
||||||
|
|
||||||
|
*out = static_cast<size_t>(std::min(file_size - offset, static_cast<s64>(size)));
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result DrySetSize(s64 size, OpenMode open_mode) {
|
||||||
|
// Check that we can write
|
||||||
|
R_UNLESS(static_cast<u32>(open_mode & OpenMode::Write) != 0, ResultWriteNotPermitted);
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result DryWrite(bool* out_append, s64 offset, size_t size, const WriteOption& option,
|
||||||
|
OpenMode open_mode) {
|
||||||
|
// Check that we can write
|
||||||
|
R_UNLESS(static_cast<u32>(open_mode & OpenMode::Write) != 0, ResultWriteNotPermitted);
|
||||||
|
|
||||||
|
// Get the file size
|
||||||
|
s64 file_size = 0;
|
||||||
|
R_TRY(this->DoGetSize(&file_size));
|
||||||
|
|
||||||
|
// Determine if we need to append
|
||||||
|
*out_append = false;
|
||||||
|
if (file_size < offset + static_cast<s64>(size)) {
|
||||||
|
R_UNLESS(static_cast<u32>(open_mode & OpenMode::AllowAppend) != 0,
|
||||||
|
ResultFileExtensionWithoutOpenModeAllowAppend);
|
||||||
|
*out_append = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
Result DoRead(size_t* out, s64 offset, void* buffer, size_t size, const ReadOption& option) {
|
||||||
|
const auto read_size = backend->Read(static_cast<u8*>(buffer), size, offset);
|
||||||
|
*out = read_size;
|
||||||
|
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result DoGetSize(s64* out) {
|
||||||
|
*out = backend->GetSize();
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result DoFlush() {
|
||||||
|
// Exists for SDK compatibiltity -- No need to flush file.
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result DoWrite(s64 offset, const void* buffer, size_t size, const WriteOption& option) {
|
||||||
|
const std::size_t written = backend->Write(static_cast<const u8*>(buffer), size, offset);
|
||||||
|
|
||||||
|
ASSERT_MSG(written == size,
|
||||||
|
"Could not write all bytes to file (requested={:016X}, actual={:016X}).", size,
|
||||||
|
written);
|
||||||
|
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result DoSetSize(s64 size) {
|
||||||
|
backend->Resize(size);
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result DoOperateRange(void* dst, size_t dst_size, OperationId op_id, s64 offset, s64 size,
|
||||||
|
const void* src, size_t src_size) {
|
||||||
|
R_THROW(ResultNotImplemented);
|
||||||
|
}
|
||||||
|
|
||||||
|
VirtualFile backend;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace FileSys::Fsa
|
206
src/core/file_sys/fsa/fs_i_filesystem.h
Normal file
206
src/core/file_sys/fsa/fs_i_filesystem.h
Normal file
@ -0,0 +1,206 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "core/file_sys/errors.h"
|
||||||
|
#include "core/file_sys/fs_filesystem.h"
|
||||||
|
#include "core/file_sys/fs_path.h"
|
||||||
|
#include "core/file_sys/vfs/vfs_types.h"
|
||||||
|
#include "core/hle/result.h"
|
||||||
|
#include "core/hle/service/filesystem/filesystem.h"
|
||||||
|
|
||||||
|
namespace FileSys::Fsa {
|
||||||
|
|
||||||
|
class IFile;
|
||||||
|
class IDirectory;
|
||||||
|
|
||||||
|
enum class QueryId : u32 {
|
||||||
|
SetConcatenationFileAttribute = 0,
|
||||||
|
UpdateMac = 1,
|
||||||
|
IsSignedSystemPartitionOnSdCardValid = 2,
|
||||||
|
QueryUnpreparedFileInformation = 3,
|
||||||
|
};
|
||||||
|
|
||||||
|
class IFileSystem {
|
||||||
|
public:
|
||||||
|
explicit IFileSystem(VirtualDir backend_) : backend{std::move(backend_)} {}
|
||||||
|
virtual ~IFileSystem() {}
|
||||||
|
|
||||||
|
Result CreateFile(const Path& path, s64 size, CreateOption option) {
|
||||||
|
R_UNLESS(size >= 0, ResultOutOfRange);
|
||||||
|
R_RETURN(this->DoCreateFile(path, size, static_cast<int>(option)));
|
||||||
|
}
|
||||||
|
|
||||||
|
Result CreateFile(const Path& path, s64 size) {
|
||||||
|
R_RETURN(this->CreateFile(path, size, CreateOption::None));
|
||||||
|
}
|
||||||
|
|
||||||
|
Result DeleteFile(const Path& path) {
|
||||||
|
R_RETURN(this->DoDeleteFile(path));
|
||||||
|
}
|
||||||
|
|
||||||
|
Result CreateDirectory(const Path& path) {
|
||||||
|
R_RETURN(this->DoCreateDirectory(path));
|
||||||
|
}
|
||||||
|
|
||||||
|
Result DeleteDirectory(const Path& path) {
|
||||||
|
R_RETURN(this->DoDeleteDirectory(path));
|
||||||
|
}
|
||||||
|
|
||||||
|
Result DeleteDirectoryRecursively(const Path& path) {
|
||||||
|
R_RETURN(this->DoDeleteDirectoryRecursively(path));
|
||||||
|
}
|
||||||
|
|
||||||
|
Result RenameFile(const Path& old_path, const Path& new_path) {
|
||||||
|
R_RETURN(this->DoRenameFile(old_path, new_path));
|
||||||
|
}
|
||||||
|
|
||||||
|
Result RenameDirectory(const Path& old_path, const Path& new_path) {
|
||||||
|
R_RETURN(this->DoRenameDirectory(old_path, new_path));
|
||||||
|
}
|
||||||
|
|
||||||
|
Result GetEntryType(DirectoryEntryType* out, const Path& path) {
|
||||||
|
R_RETURN(this->DoGetEntryType(out, path));
|
||||||
|
}
|
||||||
|
|
||||||
|
Result OpenFile(VirtualFile* out_file, const Path& path, OpenMode mode) {
|
||||||
|
R_UNLESS(out_file != nullptr, ResultNullptrArgument);
|
||||||
|
R_UNLESS(static_cast<u32>(mode & OpenMode::ReadWrite) != 0, ResultInvalidOpenMode);
|
||||||
|
R_UNLESS(static_cast<u32>(mode & ~OpenMode::All) == 0, ResultInvalidOpenMode);
|
||||||
|
R_RETURN(this->DoOpenFile(out_file, path, mode));
|
||||||
|
}
|
||||||
|
|
||||||
|
Result OpenDirectory(VirtualDir* out_dir, const Path& path, OpenDirectoryMode mode) {
|
||||||
|
R_UNLESS(out_dir != nullptr, ResultNullptrArgument);
|
||||||
|
R_UNLESS(static_cast<u64>(mode & OpenDirectoryMode::All) != 0, ResultInvalidOpenMode);
|
||||||
|
R_UNLESS(static_cast<u64>(
|
||||||
|
mode & ~(OpenDirectoryMode::All | OpenDirectoryMode::NotRequireFileSize)) == 0,
|
||||||
|
ResultInvalidOpenMode);
|
||||||
|
R_RETURN(this->DoOpenDirectory(out_dir, path, mode));
|
||||||
|
}
|
||||||
|
|
||||||
|
Result Commit() {
|
||||||
|
R_RETURN(this->DoCommit());
|
||||||
|
}
|
||||||
|
|
||||||
|
Result GetFreeSpaceSize(s64* out, const Path& path) {
|
||||||
|
R_UNLESS(out != nullptr, ResultNullptrArgument);
|
||||||
|
R_RETURN(this->DoGetFreeSpaceSize(out, path));
|
||||||
|
}
|
||||||
|
|
||||||
|
Result GetTotalSpaceSize(s64* out, const Path& path) {
|
||||||
|
R_UNLESS(out != nullptr, ResultNullptrArgument);
|
||||||
|
R_RETURN(this->DoGetTotalSpaceSize(out, path));
|
||||||
|
}
|
||||||
|
|
||||||
|
Result CleanDirectoryRecursively(const Path& path) {
|
||||||
|
R_RETURN(this->DoCleanDirectoryRecursively(path));
|
||||||
|
}
|
||||||
|
|
||||||
|
Result GetFileTimeStampRaw(FileTimeStampRaw* out, const Path& path) {
|
||||||
|
R_UNLESS(out != nullptr, ResultNullptrArgument);
|
||||||
|
R_RETURN(this->DoGetFileTimeStampRaw(out, path));
|
||||||
|
}
|
||||||
|
|
||||||
|
Result QueryEntry(char* dst, size_t dst_size, const char* src, size_t src_size, QueryId query,
|
||||||
|
const Path& path) {
|
||||||
|
R_RETURN(this->DoQueryEntry(dst, dst_size, src, src_size, query, path));
|
||||||
|
}
|
||||||
|
|
||||||
|
// These aren't accessible as commands
|
||||||
|
Result CommitProvisionally(s64 counter) {
|
||||||
|
R_RETURN(this->DoCommitProvisionally(counter));
|
||||||
|
}
|
||||||
|
|
||||||
|
Result Rollback() {
|
||||||
|
R_RETURN(this->DoRollback());
|
||||||
|
}
|
||||||
|
|
||||||
|
Result Flush() {
|
||||||
|
R_RETURN(this->DoFlush());
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
Result DoCreateFile(const Path& path, s64 size, int flags) {
|
||||||
|
R_RETURN(backend.CreateFile(path.GetString(), size));
|
||||||
|
}
|
||||||
|
|
||||||
|
Result DoDeleteFile(const Path& path) {
|
||||||
|
R_RETURN(backend.DeleteFile(path.GetString()));
|
||||||
|
}
|
||||||
|
|
||||||
|
Result DoCreateDirectory(const Path& path) {
|
||||||
|
R_RETURN(backend.CreateDirectory(path.GetString()));
|
||||||
|
}
|
||||||
|
|
||||||
|
Result DoDeleteDirectory(const Path& path) {
|
||||||
|
R_RETURN(backend.DeleteDirectory(path.GetString()));
|
||||||
|
}
|
||||||
|
|
||||||
|
Result DoDeleteDirectoryRecursively(const Path& path) {
|
||||||
|
R_RETURN(backend.DeleteDirectoryRecursively(path.GetString()));
|
||||||
|
}
|
||||||
|
|
||||||
|
Result DoRenameFile(const Path& old_path, const Path& new_path) {
|
||||||
|
R_RETURN(backend.RenameFile(old_path.GetString(), new_path.GetString()));
|
||||||
|
}
|
||||||
|
|
||||||
|
Result DoRenameDirectory(const Path& old_path, const Path& new_path) {
|
||||||
|
R_RETURN(backend.RenameDirectory(old_path.GetString(), new_path.GetString()));
|
||||||
|
}
|
||||||
|
|
||||||
|
Result DoGetEntryType(DirectoryEntryType* out, const Path& path) {
|
||||||
|
R_RETURN(backend.GetEntryType(out, path.GetString()));
|
||||||
|
}
|
||||||
|
|
||||||
|
Result DoOpenFile(VirtualFile* out_file, const Path& path, OpenMode mode) {
|
||||||
|
R_RETURN(backend.OpenFile(out_file, path.GetString(), mode));
|
||||||
|
}
|
||||||
|
|
||||||
|
Result DoOpenDirectory(VirtualDir* out_directory, const Path& path, OpenDirectoryMode mode) {
|
||||||
|
R_RETURN(backend.OpenDirectory(out_directory, path.GetString()));
|
||||||
|
}
|
||||||
|
|
||||||
|
Result DoCommit() {
|
||||||
|
R_THROW(ResultNotImplemented);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result DoGetFreeSpaceSize(s64* out, const Path& path) {
|
||||||
|
R_THROW(ResultNotImplemented);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result DoGetTotalSpaceSize(s64* out, const Path& path) {
|
||||||
|
R_THROW(ResultNotImplemented);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result DoCleanDirectoryRecursively(const Path& path) {
|
||||||
|
R_RETURN(backend.CleanDirectoryRecursively(path.GetString()));
|
||||||
|
}
|
||||||
|
|
||||||
|
Result DoGetFileTimeStampRaw(FileTimeStampRaw* out, const Path& path) {
|
||||||
|
R_RETURN(backend.GetFileTimeStampRaw(out, path.GetString()));
|
||||||
|
}
|
||||||
|
|
||||||
|
Result DoQueryEntry(char* dst, size_t dst_size, const char* src, size_t src_size, QueryId query,
|
||||||
|
const Path& path) {
|
||||||
|
R_THROW(ResultNotImplemented);
|
||||||
|
}
|
||||||
|
|
||||||
|
// These aren't accessible as commands
|
||||||
|
Result DoCommitProvisionally(s64 counter) {
|
||||||
|
R_THROW(ResultNotImplemented);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result DoRollback() {
|
||||||
|
R_THROW(ResultNotImplemented);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result DoFlush() {
|
||||||
|
R_THROW(ResultNotImplemented);
|
||||||
|
}
|
||||||
|
|
||||||
|
Service::FileSystem::VfsDirectoryServiceWrapper backend;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace FileSys::Fsa
|
36
src/core/file_sys/fssrv/fssrv_sf_path.h
Normal file
36
src/core/file_sys/fssrv/fssrv_sf_path.h
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "core/file_sys/fs_directory.h"
|
||||||
|
|
||||||
|
namespace FileSys::Sf {
|
||||||
|
|
||||||
|
struct Path {
|
||||||
|
char str[EntryNameLengthMax + 1];
|
||||||
|
|
||||||
|
static constexpr Path Encode(const char* p) {
|
||||||
|
Path path = {};
|
||||||
|
for (size_t i = 0; i < sizeof(path) - 1; i++) {
|
||||||
|
path.str[i] = p[i];
|
||||||
|
if (p[i] == '\x00') {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr size_t GetPathLength(const Path& path) {
|
||||||
|
size_t len = 0;
|
||||||
|
for (size_t i = 0; i < sizeof(path) - 1 && path.str[i] != '\x00'; i++) {
|
||||||
|
len++;
|
||||||
|
}
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
static_assert(std::is_trivially_copyable_v<Path>, "Path must be trivially copyable.");
|
||||||
|
|
||||||
|
using FspPath = Path;
|
||||||
|
|
||||||
|
} // namespace FileSys::Sf
|
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <mutex>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
|
||||||
#include "core/crypto/aes_util.h"
|
#include "core/crypto/aes_util.h"
|
||||||
|
@ -36,7 +36,9 @@ Result HierarchicalSha256Storage::Initialize(VirtualFile* base_storages, s32 lay
|
|||||||
// Get the base storage size.
|
// Get the base storage size.
|
||||||
m_base_storage_size = base_storages[2]->GetSize();
|
m_base_storage_size = base_storages[2]->GetSize();
|
||||||
{
|
{
|
||||||
auto size_guard = SCOPE_GUARD({ m_base_storage_size = 0; });
|
auto size_guard = SCOPE_GUARD {
|
||||||
|
m_base_storage_size = 0;
|
||||||
|
};
|
||||||
R_UNLESS(m_base_storage_size <= static_cast<s64>(HashSize)
|
R_UNLESS(m_base_storage_size <= static_cast<s64>(HashSize)
|
||||||
<< m_log_size_ratio << m_log_size_ratio,
|
<< m_log_size_ratio << m_log_size_ratio,
|
||||||
ResultHierarchicalSha256BaseStorageTooLarge);
|
ResultHierarchicalSha256BaseStorageTooLarge);
|
||||||
|
@ -98,7 +98,9 @@ Loader::ResultStatus ProgramMetadata::Load(VirtualFile file) {
|
|||||||
|
|
||||||
Loader::ResultStatus ProgramMetadata::Reload(VirtualFile file) {
|
Loader::ResultStatus ProgramMetadata::Reload(VirtualFile file) {
|
||||||
const u64 original_program_id = aci_header.title_id;
|
const u64 original_program_id = aci_header.title_id;
|
||||||
SCOPE_EXIT({ aci_header.title_id = original_program_id; });
|
SCOPE_EXIT {
|
||||||
|
aci_header.title_id = original_program_id;
|
||||||
|
};
|
||||||
|
|
||||||
return this->Load(file);
|
return this->Load(file);
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
#include "core/file_sys/system_archive/data/font_standard.h"
|
#include "core/file_sys/system_archive/data/font_standard.h"
|
||||||
#include "core/file_sys/system_archive/shared_font.h"
|
#include "core/file_sys/system_archive/shared_font.h"
|
||||||
#include "core/file_sys/vfs/vfs_vector.h"
|
#include "core/file_sys/vfs/vfs_vector.h"
|
||||||
#include "core/hle/service/ns/iplatform_service_manager.h"
|
#include "core/hle/service/ns/platform_service_manager.h"
|
||||||
|
|
||||||
namespace FileSys::SystemArchive {
|
namespace FileSys::SystemArchive {
|
||||||
|
|
||||||
|
@ -24,7 +24,9 @@ Result KClientSession::SendSyncRequest(uintptr_t address, size_t size) {
|
|||||||
// Create a session request.
|
// Create a session request.
|
||||||
KSessionRequest* request = KSessionRequest::Create(m_kernel);
|
KSessionRequest* request = KSessionRequest::Create(m_kernel);
|
||||||
R_UNLESS(request != nullptr, ResultOutOfResource);
|
R_UNLESS(request != nullptr, ResultOutOfResource);
|
||||||
SCOPE_EXIT({ request->Close(); });
|
SCOPE_EXIT {
|
||||||
|
request->Close();
|
||||||
|
};
|
||||||
|
|
||||||
// Initialize the request.
|
// Initialize the request.
|
||||||
request->Initialize(nullptr, address, size);
|
request->Initialize(nullptr, address, size);
|
||||||
@ -37,7 +39,9 @@ Result KClientSession::SendAsyncRequest(KEvent* event, uintptr_t address, size_t
|
|||||||
// Create a session request.
|
// Create a session request.
|
||||||
KSessionRequest* request = KSessionRequest::Create(m_kernel);
|
KSessionRequest* request = KSessionRequest::Create(m_kernel);
|
||||||
R_UNLESS(request != nullptr, ResultOutOfResource);
|
R_UNLESS(request != nullptr, ResultOutOfResource);
|
||||||
SCOPE_EXIT({ request->Close(); });
|
SCOPE_EXIT {
|
||||||
|
request->Close();
|
||||||
|
};
|
||||||
|
|
||||||
// Initialize the request.
|
// Initialize the request.
|
||||||
request->Initialize(event, address, size);
|
request->Initialize(event, address, size);
|
||||||
|
@ -1305,11 +1305,11 @@ Result KPageTableBase::UnmapCodeMemory(KProcessAddress dst_address, KProcessAddr
|
|||||||
|
|
||||||
// Ensure that we maintain the instruction cache.
|
// Ensure that we maintain the instruction cache.
|
||||||
bool reprotected_pages = false;
|
bool reprotected_pages = false;
|
||||||
SCOPE_EXIT({
|
SCOPE_EXIT {
|
||||||
if (reprotected_pages && any_code_pages) {
|
if (reprotected_pages && any_code_pages) {
|
||||||
InvalidateInstructionCache(m_kernel, this, dst_address, size);
|
InvalidateInstructionCache(m_kernel, this, dst_address, size);
|
||||||
}
|
}
|
||||||
});
|
};
|
||||||
|
|
||||||
// Unmap.
|
// Unmap.
|
||||||
{
|
{
|
||||||
@ -1397,7 +1397,9 @@ Result KPageTableBase::MapInsecureMemory(KProcessAddress address, size_t size) {
|
|||||||
// Close the opened pages when we're done with them.
|
// Close the opened pages when we're done with them.
|
||||||
// If the mapping succeeds, each page will gain an extra reference, otherwise they will be freed
|
// If the mapping succeeds, each page will gain an extra reference, otherwise they will be freed
|
||||||
// automatically.
|
// automatically.
|
||||||
SCOPE_EXIT({ pg.Close(); });
|
SCOPE_EXIT {
|
||||||
|
pg.Close();
|
||||||
|
};
|
||||||
|
|
||||||
// Clear all the newly allocated pages.
|
// Clear all the newly allocated pages.
|
||||||
for (const auto& it : pg) {
|
for (const auto& it : pg) {
|
||||||
@ -1603,7 +1605,9 @@ Result KPageTableBase::AllocateAndMapPagesImpl(PageLinkedList* page_list, KProce
|
|||||||
m_kernel.MemoryManager().AllocateAndOpen(std::addressof(pg), num_pages, m_allocate_option));
|
m_kernel.MemoryManager().AllocateAndOpen(std::addressof(pg), num_pages, m_allocate_option));
|
||||||
|
|
||||||
// Ensure that the page group is closed when we're done working with it.
|
// Ensure that the page group is closed when we're done working with it.
|
||||||
SCOPE_EXIT({ pg.Close(); });
|
SCOPE_EXIT {
|
||||||
|
pg.Close();
|
||||||
|
};
|
||||||
|
|
||||||
// Clear all pages.
|
// Clear all pages.
|
||||||
for (const auto& it : pg) {
|
for (const auto& it : pg) {
|
||||||
@ -2191,7 +2195,9 @@ Result KPageTableBase::SetHeapSize(KProcessAddress* out, size_t size) {
|
|||||||
// Close the opened pages when we're done with them.
|
// Close the opened pages when we're done with them.
|
||||||
// If the mapping succeeds, each page will gain an extra reference, otherwise they will be freed
|
// If the mapping succeeds, each page will gain an extra reference, otherwise they will be freed
|
||||||
// automatically.
|
// automatically.
|
||||||
SCOPE_EXIT({ pg.Close(); });
|
SCOPE_EXIT {
|
||||||
|
pg.Close();
|
||||||
|
};
|
||||||
|
|
||||||
// Clear all the newly allocated pages.
|
// Clear all the newly allocated pages.
|
||||||
for (const auto& it : pg) {
|
for (const auto& it : pg) {
|
||||||
@ -2592,7 +2598,9 @@ Result KPageTableBase::UnmapIoRegion(KProcessAddress dst_address, KPhysicalAddre
|
|||||||
// Temporarily unlock ourselves, so that other operations can occur while we flush the
|
// Temporarily unlock ourselves, so that other operations can occur while we flush the
|
||||||
// region.
|
// region.
|
||||||
m_general_lock.Unlock();
|
m_general_lock.Unlock();
|
||||||
SCOPE_EXIT({ m_general_lock.Lock(); });
|
SCOPE_EXIT {
|
||||||
|
m_general_lock.Lock();
|
||||||
|
};
|
||||||
|
|
||||||
// Flush the region.
|
// Flush the region.
|
||||||
R_ASSERT(FlushDataCache(dst_address, size));
|
R_ASSERT(FlushDataCache(dst_address, size));
|
||||||
@ -3311,10 +3319,10 @@ Result KPageTableBase::ReadIoMemoryImpl(KProcessAddress dst_addr, KPhysicalAddre
|
|||||||
// Ensure we unmap the io memory when we're done with it.
|
// Ensure we unmap the io memory when we're done with it.
|
||||||
const KPageProperties unmap_properties =
|
const KPageProperties unmap_properties =
|
||||||
KPageProperties{KMemoryPermission::None, false, false, DisableMergeAttribute::None};
|
KPageProperties{KMemoryPermission::None, false, false, DisableMergeAttribute::None};
|
||||||
SCOPE_EXIT({
|
SCOPE_EXIT {
|
||||||
R_ASSERT(this->Operate(updater.GetPageList(), io_addr, map_size / PageSize, 0, false,
|
R_ASSERT(this->Operate(updater.GetPageList(), io_addr, map_size / PageSize, 0, false,
|
||||||
unmap_properties, OperationType::Unmap, true));
|
unmap_properties, OperationType::Unmap, true));
|
||||||
});
|
};
|
||||||
|
|
||||||
// Read the memory.
|
// Read the memory.
|
||||||
const KProcessAddress read_addr = io_addr + (GetInteger(phys_addr) & (PageSize - 1));
|
const KProcessAddress read_addr = io_addr + (GetInteger(phys_addr) & (PageSize - 1));
|
||||||
@ -3347,10 +3355,10 @@ Result KPageTableBase::WriteIoMemoryImpl(KPhysicalAddress phys_addr, KProcessAdd
|
|||||||
// Ensure we unmap the io memory when we're done with it.
|
// Ensure we unmap the io memory when we're done with it.
|
||||||
const KPageProperties unmap_properties =
|
const KPageProperties unmap_properties =
|
||||||
KPageProperties{KMemoryPermission::None, false, false, DisableMergeAttribute::None};
|
KPageProperties{KMemoryPermission::None, false, false, DisableMergeAttribute::None};
|
||||||
SCOPE_EXIT({
|
SCOPE_EXIT {
|
||||||
R_ASSERT(this->Operate(updater.GetPageList(), io_addr, map_size / PageSize, 0, false,
|
R_ASSERT(this->Operate(updater.GetPageList(), io_addr, map_size / PageSize, 0, false,
|
||||||
unmap_properties, OperationType::Unmap, true));
|
unmap_properties, OperationType::Unmap, true));
|
||||||
});
|
};
|
||||||
|
|
||||||
// Write the memory.
|
// Write the memory.
|
||||||
const KProcessAddress write_addr = io_addr + (GetInteger(phys_addr) & (PageSize - 1));
|
const KProcessAddress write_addr = io_addr + (GetInteger(phys_addr) & (PageSize - 1));
|
||||||
@ -4491,14 +4499,14 @@ Result KPageTableBase::SetupForIpcServer(KProcessAddress* out_addr, size_t size,
|
|||||||
|
|
||||||
// If the partial pages are mapped, an extra reference will have been opened. Otherwise, they'll
|
// If the partial pages are mapped, an extra reference will have been opened. Otherwise, they'll
|
||||||
// free on scope exit.
|
// free on scope exit.
|
||||||
SCOPE_EXIT({
|
SCOPE_EXIT {
|
||||||
if (start_partial_page != 0) {
|
if (start_partial_page != 0) {
|
||||||
m_kernel.MemoryManager().Close(start_partial_page, 1);
|
m_kernel.MemoryManager().Close(start_partial_page, 1);
|
||||||
}
|
}
|
||||||
if (end_partial_page != 0) {
|
if (end_partial_page != 0) {
|
||||||
m_kernel.MemoryManager().Close(end_partial_page, 1);
|
m_kernel.MemoryManager().Close(end_partial_page, 1);
|
||||||
}
|
}
|
||||||
});
|
};
|
||||||
|
|
||||||
ON_RESULT_FAILURE {
|
ON_RESULT_FAILURE {
|
||||||
if (cur_mapped_addr != dst_addr) {
|
if (cur_mapped_addr != dst_addr) {
|
||||||
@ -5166,10 +5174,10 @@ Result KPageTableBase::MapPhysicalMemory(KProcessAddress address, size_t size) {
|
|||||||
GetCurrentProcess(m_kernel).GetId(), m_heap_fill_value));
|
GetCurrentProcess(m_kernel).GetId(), m_heap_fill_value));
|
||||||
|
|
||||||
// If we fail in the next bit (or retry), we need to cleanup the pages.
|
// If we fail in the next bit (or retry), we need to cleanup the pages.
|
||||||
auto pg_guard = SCOPE_GUARD({
|
auto pg_guard = SCOPE_GUARD {
|
||||||
pg.OpenFirst();
|
pg.OpenFirst();
|
||||||
pg.Close();
|
pg.Close();
|
||||||
});
|
};
|
||||||
|
|
||||||
// Map the memory.
|
// Map the memory.
|
||||||
{
|
{
|
||||||
@ -5694,7 +5702,9 @@ Result KPageTableBase::Operate(PageLinkedList* page_list, KProcessAddress virt_a
|
|||||||
|
|
||||||
// Ensure that any pages we track are closed on exit.
|
// Ensure that any pages we track are closed on exit.
|
||||||
KPageGroup pages_to_close(m_kernel, this->GetBlockInfoManager());
|
KPageGroup pages_to_close(m_kernel, this->GetBlockInfoManager());
|
||||||
SCOPE_EXIT({ pages_to_close.CloseAndReset(); });
|
SCOPE_EXIT {
|
||||||
|
pages_to_close.CloseAndReset();
|
||||||
|
};
|
||||||
|
|
||||||
// Make a page group representing the region to unmap.
|
// Make a page group representing the region to unmap.
|
||||||
this->MakePageGroup(pages_to_close, virt_addr, num_pages);
|
this->MakePageGroup(pages_to_close, virt_addr, num_pages);
|
||||||
|
@ -77,7 +77,9 @@ Result TerminateChildren(KernelCore& kernel, KProcess* process,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Terminate and close the thread.
|
// Terminate and close the thread.
|
||||||
SCOPE_EXIT({ cur_child->Close(); });
|
SCOPE_EXIT {
|
||||||
|
cur_child->Close();
|
||||||
|
};
|
||||||
|
|
||||||
if (const Result terminate_result = cur_child->Terminate();
|
if (const Result terminate_result = cur_child->Terminate();
|
||||||
ResultTerminationRequested == terminate_result) {
|
ResultTerminationRequested == terminate_result) {
|
||||||
@ -466,11 +468,11 @@ void KProcess::DoWorkerTaskImpl() {
|
|||||||
|
|
||||||
Result KProcess::StartTermination() {
|
Result KProcess::StartTermination() {
|
||||||
// Finalize the handle table when we're done, if the process isn't immortal.
|
// Finalize the handle table when we're done, if the process isn't immortal.
|
||||||
SCOPE_EXIT({
|
SCOPE_EXIT {
|
||||||
if (!m_is_immortal) {
|
if (!m_is_immortal) {
|
||||||
this->FinalizeHandleTable();
|
this->FinalizeHandleTable();
|
||||||
}
|
}
|
||||||
});
|
};
|
||||||
|
|
||||||
// Terminate child threads other than the current one.
|
// Terminate child threads other than the current one.
|
||||||
R_RETURN(TerminateChildren(m_kernel, this, GetCurrentThreadPointer(m_kernel)));
|
R_RETURN(TerminateChildren(m_kernel, this, GetCurrentThreadPointer(m_kernel)));
|
||||||
@ -964,7 +966,9 @@ Result KProcess::Run(s32 priority, size_t stack_size) {
|
|||||||
// Create a new thread for the process.
|
// Create a new thread for the process.
|
||||||
KThread* main_thread = KThread::Create(m_kernel);
|
KThread* main_thread = KThread::Create(m_kernel);
|
||||||
R_UNLESS(main_thread != nullptr, ResultOutOfResource);
|
R_UNLESS(main_thread != nullptr, ResultOutOfResource);
|
||||||
SCOPE_EXIT({ main_thread->Close(); });
|
SCOPE_EXIT {
|
||||||
|
main_thread->Close();
|
||||||
|
};
|
||||||
|
|
||||||
// Initialize the thread.
|
// Initialize the thread.
|
||||||
R_TRY(KThread::InitializeUserThread(m_kernel.System(), main_thread, this->GetEntryPoint(), 0,
|
R_TRY(KThread::InitializeUserThread(m_kernel.System(), main_thread, this->GetEntryPoint(), 0,
|
||||||
@ -1155,7 +1159,9 @@ Result KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std:
|
|||||||
Kernel::CreateResourceLimitForProcess(m_kernel.System(), physical_memory_size);
|
Kernel::CreateResourceLimitForProcess(m_kernel.System(), physical_memory_size);
|
||||||
|
|
||||||
// Ensure we maintain a clean state on exit.
|
// Ensure we maintain a clean state on exit.
|
||||||
SCOPE_EXIT({ res_limit->Close(); });
|
SCOPE_EXIT {
|
||||||
|
res_limit->Close();
|
||||||
|
};
|
||||||
|
|
||||||
// Declare flags and code address.
|
// Declare flags and code address.
|
||||||
Svc::CreateProcessFlag flag{};
|
Svc::CreateProcessFlag flag{};
|
||||||
|
@ -651,11 +651,11 @@ Result ReceiveMessage(KernelCore& kernel, bool& recv_list_broken, uint64_t dst_m
|
|||||||
// Process any special data.
|
// Process any special data.
|
||||||
if (src_header.GetHasSpecialHeader()) {
|
if (src_header.GetHasSpecialHeader()) {
|
||||||
// After we process, make sure we track whether the receive list is broken.
|
// After we process, make sure we track whether the receive list is broken.
|
||||||
SCOPE_EXIT({
|
SCOPE_EXIT {
|
||||||
if (offset > dst_recv_list_idx) {
|
if (offset > dst_recv_list_idx) {
|
||||||
recv_list_broken = true;
|
recv_list_broken = true;
|
||||||
}
|
}
|
||||||
});
|
};
|
||||||
|
|
||||||
// Process special data.
|
// Process special data.
|
||||||
R_TRY(ProcessMessageSpecialData<false>(offset, dst_process, src_process, src_thread,
|
R_TRY(ProcessMessageSpecialData<false>(offset, dst_process, src_process, src_thread,
|
||||||
@ -665,11 +665,11 @@ Result ReceiveMessage(KernelCore& kernel, bool& recv_list_broken, uint64_t dst_m
|
|||||||
// Process any pointer buffers.
|
// Process any pointer buffers.
|
||||||
for (auto i = 0; i < src_header.GetPointerCount(); ++i) {
|
for (auto i = 0; i < src_header.GetPointerCount(); ++i) {
|
||||||
// After we process, make sure we track whether the receive list is broken.
|
// After we process, make sure we track whether the receive list is broken.
|
||||||
SCOPE_EXIT({
|
SCOPE_EXIT {
|
||||||
if (offset > dst_recv_list_idx) {
|
if (offset > dst_recv_list_idx) {
|
||||||
recv_list_broken = true;
|
recv_list_broken = true;
|
||||||
}
|
}
|
||||||
});
|
};
|
||||||
|
|
||||||
R_TRY(ProcessReceiveMessagePointerDescriptors(
|
R_TRY(ProcessReceiveMessagePointerDescriptors(
|
||||||
offset, pointer_key, dst_page_table, src_page_table, dst_msg, src_msg, dst_recv_list,
|
offset, pointer_key, dst_page_table, src_page_table, dst_msg, src_msg, dst_recv_list,
|
||||||
@ -680,11 +680,11 @@ Result ReceiveMessage(KernelCore& kernel, bool& recv_list_broken, uint64_t dst_m
|
|||||||
// Process any map alias buffers.
|
// Process any map alias buffers.
|
||||||
for (auto i = 0; i < src_header.GetMapAliasCount(); ++i) {
|
for (auto i = 0; i < src_header.GetMapAliasCount(); ++i) {
|
||||||
// After we process, make sure we track whether the receive list is broken.
|
// After we process, make sure we track whether the receive list is broken.
|
||||||
SCOPE_EXIT({
|
SCOPE_EXIT {
|
||||||
if (offset > dst_recv_list_idx) {
|
if (offset > dst_recv_list_idx) {
|
||||||
recv_list_broken = true;
|
recv_list_broken = true;
|
||||||
}
|
}
|
||||||
});
|
};
|
||||||
|
|
||||||
// We process in order send, recv, exch. Buffers after send (recv/exch) are ReadWrite.
|
// We process in order send, recv, exch. Buffers after send (recv/exch) are ReadWrite.
|
||||||
const KMemoryPermission perm = (i >= src_header.GetSendCount())
|
const KMemoryPermission perm = (i >= src_header.GetSendCount())
|
||||||
@ -702,11 +702,11 @@ Result ReceiveMessage(KernelCore& kernel, bool& recv_list_broken, uint64_t dst_m
|
|||||||
// Process any raw data.
|
// Process any raw data.
|
||||||
if (const auto raw_count = src_header.GetRawCount(); raw_count != 0) {
|
if (const auto raw_count = src_header.GetRawCount(); raw_count != 0) {
|
||||||
// After we process, make sure we track whether the receive list is broken.
|
// After we process, make sure we track whether the receive list is broken.
|
||||||
SCOPE_EXIT({
|
SCOPE_EXIT {
|
||||||
if (offset + raw_count > dst_recv_list_idx) {
|
if (offset + raw_count > dst_recv_list_idx) {
|
||||||
recv_list_broken = true;
|
recv_list_broken = true;
|
||||||
}
|
}
|
||||||
});
|
};
|
||||||
|
|
||||||
// Get the offset and size.
|
// Get the offset and size.
|
||||||
const size_t offset_words = offset * sizeof(u32);
|
const size_t offset_words = offset * sizeof(u32);
|
||||||
@ -1124,7 +1124,9 @@ Result KServerSession::ReceiveRequest(uintptr_t server_message, uintptr_t server
|
|||||||
client_thread->Open();
|
client_thread->Open();
|
||||||
}
|
}
|
||||||
|
|
||||||
SCOPE_EXIT({ client_thread->Close(); });
|
SCOPE_EXIT {
|
||||||
|
client_thread->Close();
|
||||||
|
};
|
||||||
|
|
||||||
// Set the request as our current.
|
// Set the request as our current.
|
||||||
m_current_request = request;
|
m_current_request = request;
|
||||||
@ -1174,7 +1176,9 @@ Result KServerSession::ReceiveRequest(uintptr_t server_message, uintptr_t server
|
|||||||
// Reply to the client.
|
// Reply to the client.
|
||||||
{
|
{
|
||||||
// After we reply, close our reference to the request.
|
// After we reply, close our reference to the request.
|
||||||
SCOPE_EXIT({ request->Close(); });
|
SCOPE_EXIT {
|
||||||
|
request->Close();
|
||||||
|
};
|
||||||
|
|
||||||
// Get the event to check whether the request is async.
|
// Get the event to check whether the request is async.
|
||||||
if (KEvent* event = request->GetEvent(); event != nullptr) {
|
if (KEvent* event = request->GetEvent(); event != nullptr) {
|
||||||
@ -1236,7 +1240,9 @@ Result KServerSession::SendReply(uintptr_t server_message, uintptr_t server_buff
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Close reference to the request once we're done processing it.
|
// Close reference to the request once we're done processing it.
|
||||||
SCOPE_EXIT({ request->Close(); });
|
SCOPE_EXIT {
|
||||||
|
request->Close();
|
||||||
|
};
|
||||||
|
|
||||||
// Extract relevant information from the request.
|
// Extract relevant information from the request.
|
||||||
const uint64_t client_message = request->GetAddress();
|
const uint64_t client_message = request->GetAddress();
|
||||||
@ -1394,7 +1400,9 @@ void KServerSession::CleanupRequests() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Close a reference to the request once it's cleaned up.
|
// Close a reference to the request once it's cleaned up.
|
||||||
SCOPE_EXIT({ request->Close(); });
|
SCOPE_EXIT {
|
||||||
|
request->Close();
|
||||||
|
};
|
||||||
|
|
||||||
// Extract relevant information from the request.
|
// Extract relevant information from the request.
|
||||||
const uint64_t client_message = request->GetAddress();
|
const uint64_t client_message = request->GetAddress();
|
||||||
@ -1491,7 +1499,9 @@ void KServerSession::OnClientClosed() {
|
|||||||
ASSERT(thread != nullptr);
|
ASSERT(thread != nullptr);
|
||||||
|
|
||||||
// Ensure that we close the request when done.
|
// Ensure that we close the request when done.
|
||||||
SCOPE_EXIT({ request->Close(); });
|
SCOPE_EXIT {
|
||||||
|
request->Close();
|
||||||
|
};
|
||||||
|
|
||||||
// If we're terminating, close a reference to the thread and event.
|
// If we're terminating, close a reference to the thread and event.
|
||||||
if (terminate) {
|
if (terminate) {
|
||||||
|
@ -21,7 +21,9 @@ Result KThreadLocalPage::Initialize(KernelCore& kernel, KProcess* process) {
|
|||||||
// Allocate a new page.
|
// Allocate a new page.
|
||||||
KPageBuffer* page_buf = KPageBuffer::Allocate(kernel);
|
KPageBuffer* page_buf = KPageBuffer::Allocate(kernel);
|
||||||
R_UNLESS(page_buf != nullptr, ResultOutOfMemory);
|
R_UNLESS(page_buf != nullptr, ResultOutOfMemory);
|
||||||
auto page_buf_guard = SCOPE_GUARD({ KPageBuffer::Free(kernel, page_buf); });
|
auto page_buf_guard = SCOPE_GUARD {
|
||||||
|
KPageBuffer::Free(kernel, page_buf);
|
||||||
|
};
|
||||||
|
|
||||||
// Map the address in.
|
// Map the address in.
|
||||||
const auto phys_addr = kernel.System().DeviceMemory().GetPhysicalAddr(page_buf);
|
const auto phys_addr = kernel.System().DeviceMemory().GetPhysicalAddr(page_buf);
|
||||||
|
@ -24,7 +24,9 @@ Result KTransferMemory::Initialize(KProcessAddress addr, std::size_t size,
|
|||||||
|
|
||||||
// Construct the page group, guarding to make sure our state is valid on exit.
|
// Construct the page group, guarding to make sure our state is valid on exit.
|
||||||
m_page_group.emplace(m_kernel, page_table.GetBlockInfoManager());
|
m_page_group.emplace(m_kernel, page_table.GetBlockInfoManager());
|
||||||
auto pg_guard = SCOPE_GUARD({ m_page_group.reset(); });
|
auto pg_guard = SCOPE_GUARD {
|
||||||
|
m_page_group.reset();
|
||||||
|
};
|
||||||
|
|
||||||
// Lock the memory.
|
// Lock the memory.
|
||||||
R_TRY(page_table.LockForTransferMemory(std::addressof(*m_page_group), addr, size,
|
R_TRY(page_table.LockForTransferMemory(std::addressof(*m_page_group), addr, size,
|
||||||
|
@ -109,7 +109,9 @@ struct KernelCore::Impl {
|
|||||||
|
|
||||||
void Shutdown() {
|
void Shutdown() {
|
||||||
is_shutting_down.store(true, std::memory_order_relaxed);
|
is_shutting_down.store(true, std::memory_order_relaxed);
|
||||||
SCOPE_EXIT({ is_shutting_down.store(false, std::memory_order_relaxed); });
|
SCOPE_EXIT {
|
||||||
|
is_shutting_down.store(false, std::memory_order_relaxed);
|
||||||
|
};
|
||||||
|
|
||||||
CloseServices();
|
CloseServices();
|
||||||
|
|
||||||
@ -1080,7 +1082,9 @@ std::jthread KernelCore::RunOnHostCoreProcess(std::string&& process_name,
|
|||||||
process->Initialize(Svc::CreateProcessParameter{}, GetSystemResourceLimit(), false)));
|
process->Initialize(Svc::CreateProcessParameter{}, GetSystemResourceLimit(), false)));
|
||||||
|
|
||||||
// Ensure that we don't hold onto any extra references.
|
// Ensure that we don't hold onto any extra references.
|
||||||
SCOPE_EXIT({ process->Close(); });
|
SCOPE_EXIT {
|
||||||
|
process->Close();
|
||||||
|
};
|
||||||
|
|
||||||
// Register the new process.
|
// Register the new process.
|
||||||
KProcess::Register(*this, process);
|
KProcess::Register(*this, process);
|
||||||
@ -1108,7 +1112,9 @@ void KernelCore::RunOnGuestCoreProcess(std::string&& process_name, std::function
|
|||||||
process->Initialize(Svc::CreateProcessParameter{}, GetSystemResourceLimit(), false)));
|
process->Initialize(Svc::CreateProcessParameter{}, GetSystemResourceLimit(), false)));
|
||||||
|
|
||||||
// Ensure that we don't hold onto any extra references.
|
// Ensure that we don't hold onto any extra references.
|
||||||
SCOPE_EXIT({ process->Close(); });
|
SCOPE_EXIT {
|
||||||
|
process->Close();
|
||||||
|
};
|
||||||
|
|
||||||
// Register the new process.
|
// Register the new process.
|
||||||
KProcess::Register(*this, process);
|
KProcess::Register(*this, process);
|
||||||
|
@ -45,7 +45,9 @@ Result CreateCodeMemory(Core::System& system, Handle* out, u64 address, uint64_t
|
|||||||
|
|
||||||
KCodeMemory* code_mem = KCodeMemory::Create(kernel);
|
KCodeMemory* code_mem = KCodeMemory::Create(kernel);
|
||||||
R_UNLESS(code_mem != nullptr, ResultOutOfResource);
|
R_UNLESS(code_mem != nullptr, ResultOutOfResource);
|
||||||
SCOPE_EXIT({ code_mem->Close(); });
|
SCOPE_EXIT {
|
||||||
|
code_mem->Close();
|
||||||
|
};
|
||||||
|
|
||||||
// Verify that the region is in range.
|
// Verify that the region is in range.
|
||||||
R_UNLESS(GetCurrentProcess(system.Kernel()).GetPageTable().Contains(address, size),
|
R_UNLESS(GetCurrentProcess(system.Kernel()).GetPageTable().Contains(address, size),
|
||||||
|
@ -28,7 +28,9 @@ Result CreateDeviceAddressSpace(Core::System& system, Handle* out, uint64_t das_
|
|||||||
// Create the device address space.
|
// Create the device address space.
|
||||||
KDeviceAddressSpace* das = KDeviceAddressSpace::Create(system.Kernel());
|
KDeviceAddressSpace* das = KDeviceAddressSpace::Create(system.Kernel());
|
||||||
R_UNLESS(das != nullptr, ResultOutOfResource);
|
R_UNLESS(das != nullptr, ResultOutOfResource);
|
||||||
SCOPE_EXIT({ das->Close(); });
|
SCOPE_EXIT {
|
||||||
|
das->Close();
|
||||||
|
};
|
||||||
|
|
||||||
// Initialize the device address space.
|
// Initialize the device address space.
|
||||||
R_TRY(das->Initialize(das_address, das_size));
|
R_TRY(das->Initialize(das_address, das_size));
|
||||||
|
@ -72,10 +72,10 @@ Result CreateEvent(Core::System& system, Handle* out_write, Handle* out_read) {
|
|||||||
event_reservation.Commit();
|
event_reservation.Commit();
|
||||||
|
|
||||||
// Ensure that we clean up the event (and its only references are handle table) on function end.
|
// Ensure that we clean up the event (and its only references are handle table) on function end.
|
||||||
SCOPE_EXIT({
|
SCOPE_EXIT {
|
||||||
event->GetReadableEvent().Close();
|
event->GetReadableEvent().Close();
|
||||||
event->Close();
|
event->Close();
|
||||||
});
|
};
|
||||||
|
|
||||||
// Register the event.
|
// Register the event.
|
||||||
KEvent::Register(kernel, event);
|
KEvent::Register(kernel, event);
|
||||||
|
@ -129,11 +129,11 @@ Result ReplyAndReceiveImpl(KernelCore& kernel, int32_t* out_index, uintptr_t mes
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Ensure handles are closed when we're done.
|
// Ensure handles are closed when we're done.
|
||||||
SCOPE_EXIT({
|
SCOPE_EXIT {
|
||||||
for (auto i = 0; i < num_handles; ++i) {
|
for (auto i = 0; i < num_handles; ++i) {
|
||||||
objs[i]->Close();
|
objs[i]->Close();
|
||||||
}
|
}
|
||||||
});
|
};
|
||||||
|
|
||||||
R_RETURN(ReplyAndReceiveImpl(kernel, out_index, message, buffer_size, message_paddr, objs,
|
R_RETURN(ReplyAndReceiveImpl(kernel, out_index, message, buffer_size, message_paddr, objs,
|
||||||
num_handles, reply_target, timeout_ns));
|
num_handles, reply_target, timeout_ns));
|
||||||
@ -208,10 +208,10 @@ Result SendAsyncRequestWithUserBuffer(Core::System& system, Handle* out_event_ha
|
|||||||
event_reservation.Commit();
|
event_reservation.Commit();
|
||||||
|
|
||||||
// At end of scope, kill the standing references to the sub events.
|
// At end of scope, kill the standing references to the sub events.
|
||||||
SCOPE_EXIT({
|
SCOPE_EXIT {
|
||||||
event->GetReadableEvent().Close();
|
event->GetReadableEvent().Close();
|
||||||
event->Close();
|
event->Close();
|
||||||
});
|
};
|
||||||
|
|
||||||
// Register the event.
|
// Register the event.
|
||||||
KEvent::Register(system.Kernel(), event);
|
KEvent::Register(system.Kernel(), event);
|
||||||
|
@ -68,10 +68,10 @@ Result CreatePort(Core::System& system, Handle* out_server, Handle* out_client,
|
|||||||
port->Initialize(max_sessions, is_light, name);
|
port->Initialize(max_sessions, is_light, name);
|
||||||
|
|
||||||
// Ensure that we clean up the port (and its only references are handle table) on function end.
|
// Ensure that we clean up the port (and its only references are handle table) on function end.
|
||||||
SCOPE_EXIT({
|
SCOPE_EXIT {
|
||||||
port->GetServerPort().Close();
|
port->GetServerPort().Close();
|
||||||
port->GetClientPort().Close();
|
port->GetClientPort().Close();
|
||||||
});
|
};
|
||||||
|
|
||||||
// Register the port.
|
// Register the port.
|
||||||
KPort::Register(kernel, port);
|
KPort::Register(kernel, port);
|
||||||
@ -150,10 +150,10 @@ Result ManageNamedPort(Core::System& system, Handle* out_server_handle, uint64_t
|
|||||||
KPort::Register(system.Kernel(), port);
|
KPort::Register(system.Kernel(), port);
|
||||||
|
|
||||||
// Ensure that our only reference to the port is in the handle table when we're done.
|
// Ensure that our only reference to the port is in the handle table when we're done.
|
||||||
SCOPE_EXIT({
|
SCOPE_EXIT {
|
||||||
port->GetClientPort().Close();
|
port->GetClientPort().Close();
|
||||||
port->GetServerPort().Close();
|
port->GetServerPort().Close();
|
||||||
});
|
};
|
||||||
|
|
||||||
// Register the handle in the table.
|
// Register the handle in the table.
|
||||||
R_TRY(handle_table.Add(out_server_handle, std::addressof(port->GetServerPort())));
|
R_TRY(handle_table.Add(out_server_handle, std::addressof(port->GetServerPort())));
|
||||||
|
@ -18,7 +18,9 @@ Result CreateResourceLimit(Core::System& system, Handle* out_handle) {
|
|||||||
R_UNLESS(resource_limit != nullptr, ResultOutOfResource);
|
R_UNLESS(resource_limit != nullptr, ResultOutOfResource);
|
||||||
|
|
||||||
// Ensure we don't leak a reference to the limit.
|
// Ensure we don't leak a reference to the limit.
|
||||||
SCOPE_EXIT({ resource_limit->Close(); });
|
SCOPE_EXIT {
|
||||||
|
resource_limit->Close();
|
||||||
|
};
|
||||||
|
|
||||||
// Initialize the resource limit.
|
// Initialize the resource limit.
|
||||||
resource_limit->Initialize();
|
resource_limit->Initialize();
|
||||||
|
@ -69,10 +69,10 @@ Result CreateSession(Core::System& system, Handle* out_server, Handle* out_clien
|
|||||||
|
|
||||||
// Ensure that we clean up the session (and its only references are handle table) on function
|
// Ensure that we clean up the session (and its only references are handle table) on function
|
||||||
// end.
|
// end.
|
||||||
SCOPE_EXIT({
|
SCOPE_EXIT {
|
||||||
session->GetClientSession().Close();
|
session->GetClientSession().Close();
|
||||||
session->GetServerSession().Close();
|
session->GetServerSession().Close();
|
||||||
});
|
};
|
||||||
|
|
||||||
// Register the session.
|
// Register the session.
|
||||||
T::Register(system.Kernel(), session);
|
T::Register(system.Kernel(), session);
|
||||||
|
@ -78,11 +78,11 @@ Result WaitSynchronization(Core::System& system, int32_t* out_index, u64 user_ha
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Ensure handles are closed when we're done.
|
// Ensure handles are closed when we're done.
|
||||||
SCOPE_EXIT({
|
SCOPE_EXIT {
|
||||||
for (auto i = 0; i < num_handles; ++i) {
|
for (auto i = 0; i < num_handles; ++i) {
|
||||||
objs[i]->Close();
|
objs[i]->Close();
|
||||||
}
|
}
|
||||||
});
|
};
|
||||||
|
|
||||||
// Convert the timeout from nanoseconds to ticks.
|
// Convert the timeout from nanoseconds to ticks.
|
||||||
s64 timeout;
|
s64 timeout;
|
||||||
|
@ -51,7 +51,9 @@ Result CreateThread(Core::System& system, Handle* out_handle, u64 entry_point, u
|
|||||||
// Create the thread.
|
// Create the thread.
|
||||||
KThread* thread = KThread::Create(kernel);
|
KThread* thread = KThread::Create(kernel);
|
||||||
R_UNLESS(thread != nullptr, ResultOutOfResource)
|
R_UNLESS(thread != nullptr, ResultOutOfResource)
|
||||||
SCOPE_EXIT({ thread->Close(); });
|
SCOPE_EXIT {
|
||||||
|
thread->Close();
|
||||||
|
};
|
||||||
|
|
||||||
// Initialize the thread.
|
// Initialize the thread.
|
||||||
{
|
{
|
||||||
|
@ -52,7 +52,9 @@ Result CreateTransferMemory(Core::System& system, Handle* out, u64 address, u64
|
|||||||
R_UNLESS(trmem != nullptr, ResultOutOfResource);
|
R_UNLESS(trmem != nullptr, ResultOutOfResource);
|
||||||
|
|
||||||
// Ensure the only reference is in the handle table when we're done.
|
// Ensure the only reference is in the handle table when we're done.
|
||||||
SCOPE_EXIT({ trmem->Close(); });
|
SCOPE_EXIT {
|
||||||
|
trmem->Close();
|
||||||
|
};
|
||||||
|
|
||||||
// Ensure that the region is in range.
|
// Ensure that the region is in range.
|
||||||
R_UNLESS(process.GetPageTable().Contains(address, size), ResultInvalidCurrentMemory);
|
R_UNLESS(process.GetPageTable().Contains(address, size), ResultInvalidCurrentMemory);
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user