Compare commits

..

58 Commits

Author SHA1 Message Date
e1ea5ba66b Android 255 2024-02-18 20:17:36 +00:00
9b29d2c340 Merge yuzu-emu#13070 2024-02-18 20:17:35 +00:00
f688e8cc5d Merge yuzu-emu#13048 2024-02-18 20:17:35 +00:00
f5322ea1ee Merge yuzu-emu#13035 2024-02-18 20:17:35 +00:00
5c92adbcf6 Merge yuzu-emu#13030 2024-02-18 20:17:35 +00:00
b66fd41ba1 Merge yuzu-emu#13026 2024-02-18 20:17:35 +00:00
2938aa58ed Merge yuzu-emu#13006 2024-02-18 20:17:35 +00:00
78379761cc Merge yuzu-emu#13000 2024-02-18 20:17:35 +00:00
8614751812 Merge yuzu-emu#12749 2024-02-18 20:17:35 +00:00
a224414f0f Merge yuzu-emu#12461 2024-02-18 20:17:35 +00:00
baf65362ab Merge yuzu-emu#10529 2024-02-18 20:17:35 +00:00
5361027ef0 Merge pull request #13068 from german77/no_errors
core: hid: Remove driver errors from log
2024-02-18 13:30:54 -05:00
56721517ea core: hid: Remove driver errors from log 2024-02-18 10:54:56 -06:00
da225d4aa1 Merge pull request #13067 from t895/xbox-automap-invert
android: Flip AB/XY for xbox controllers during auto-mapping
2024-02-18 10:48:54 -05:00
8d74c107f5 android: Flip AB/XY for xbox controllers during auto-mapping 2024-02-18 10:40:33 -05:00
1fc86b1e3a Merge pull request #13032 from german77/qlauncher
service: Implement functions needed by Qlaunch
2024-02-18 10:37:52 -05:00
5583957616 Merge pull request #13064 from t895/auto-map-fail
android: Only do first startup automapping if nothing has been mapped
2024-02-18 10:27:49 -05:00
6d731e1aa1 Merge pull request #13049 from Leystryku/master
Fix Just Dance 2023 not booting
2024-02-18 10:25:18 -05:00
839ded7d59 Merge pull request #13065 from t895/cancel-button-fail
android: Show cancel button for the content install notice
2024-02-18 10:25:04 -05:00
f57281ebc1 Merge pull request #13066 from t895/touch-fix
android: Map touches to touchscreen
2024-02-18 10:24:58 -05:00
0a3bc6c0cf android: Map touches to touchscreen
I neglected to map touches to the touchscreen when refactoring in the input mapping PR. This fixes that regression.
2024-02-18 10:00:37 -05:00
55a7815064 android: Show cancel button for the content install notice 2024-02-18 09:23:46 -05:00
a1c4f53c8c android: Only do first startup automapping if nothing has been mapped 2024-02-18 09:18:54 -05:00
8bbb44a74e service: Change unique_ptr to make_unique in GetCacheStorageMax 2024-02-18 07:03:50 +01:00
bc5ae04ea0 file_sys: Formatting changes and use unique_ptr in GetCacheStorageMax 2024-02-18 06:17:35 +01:00
4f387b0b74 file_sys: Fix nacp field cache_storage_max_index datatype 2024-02-18 06:00:42 +01:00
bdf8aca750 Merge pull request #13047 from anpilley/import-firmware
Import firmware from folder of loose NCA files
2024-02-17 23:18:00 -05:00
acfc4d6dfb Merge pull request #13054 from t895/lifecycle-utils
android: Create lifecycle utility to simplify common StateFlow operations
2024-02-17 23:17:49 -05:00
35a3c7226a android: Create lifecycle utility to simplify common StateFlow operations 2024-02-17 23:09:09 -05:00
d93fdc8a6c service: Add proper GetCacheStorageMax implementation to IApplicationFunctions 2024-02-18 05:02:35 +01:00
5d3c7433b8 Merge pull request #13050 from t895/marquee-helper
android: Use extension functions for view visibility and text marquee
2024-02-17 22:48:36 -05:00
0010d42f82 android: Use extension functions for view visibility and text marquee 2024-02-17 22:45:33 -05:00
316089c39f Merge pull request #13052 from t895/serializable-stuff
android: Move CoreErrorDialogFragment to its own file
2024-02-17 22:22:46 -05:00
5024df1925 Merge pull request #13051 from german77/cheatmiss
dmnt: cheats: Fix valid address range
2024-02-17 22:22:14 -05:00
e7146309de Merge pull request #13034 from t895/map-all-the-inputs
android: Input mapping
2024-02-17 22:22:06 -05:00
c327d2a62c android: Move CoreErrorDialogFragment to its own file 2024-02-17 21:58:25 -05:00
cb2e312f13 Add check for corrupted firmware files after install. 2024-02-18 12:31:14 +11:00
366bb52ec8 dmnt: cheats: Fix valid address range 2024-02-17 19:10:17 -06:00
82949085c0 fsp: Add FlushAccessLogOnSdCard stub 2024-02-18 00:52:22 +01:00
90c43aa2e7 service: Add GetCacheStorageMax stub to IApplicationFunctions 2024-02-18 00:49:41 +01:00
53f8383354 Merge pull request #13017 from liamwhite/suspension
kernel: add and enable system suspend type
2024-02-17 17:00:07 -06:00
36108ce2be Merge pull request #13040 from Kelebek1/timezone_shutdown
Close reference to TimeZoneBinary on game close
2024-02-17 17:53:53 -05:00
e31c926bf0 >.> spelling 2024-02-18 07:58:41 +11:00
59ede32f8e cleanup by clang-format. 2024-02-18 07:41:24 +11:00
9eba64adce Improve behavior when one or more firmware files can't be deleted. 2024-02-18 07:38:47 +11:00
50ecad547e android: Input mapping 2024-02-17 12:32:33 -05:00
34fb14ec9a Close reference to TimeZoneBinary on game close 2024-02-17 16:00:14 +00:00
e2e0916100 Merge branch 'yuzu-emu:master' into import-firmware 2024-02-17 23:36:43 +11:00
501e3ae05a Implement In-app firmware installation. 2024-02-17 23:33:55 +11:00
ac33847b30 hid_core: Prevent crash if we try to iterate through empty color devices list 2024-02-16 21:11:47 -05:00
18494b0ad6 hid_core: Use dedicated "port" for android's input overlay 2024-02-16 21:09:42 -05:00
dc2c302a84 config: Reset per-game profile name on load if empty 2024-02-16 21:07:03 -05:00
a251f77556 android: Allow SettingsItems to use String or StringRes 2024-02-16 21:04:26 -05:00
ec02a1cfe5 service: erpt: Implement SubmitContext 2024-02-16 12:22:09 -06:00
39b958ab86 service: caps: Implement GetAlbumFIleList 2024-02-16 12:15:37 -06:00
dbcc447f43 service: am: Fix GetMainAppletAvailableUsers for user creation 2024-02-16 12:13:10 -06:00
2954c01b47 service: am: Add QLaunch launcher 2024-02-16 12:13:10 -06:00
af42482565 kernel: add and enable system suspend type 2024-02-14 17:03:50 -05:00
205 changed files with 5200 additions and 4228 deletions

View File

@ -5,9 +5,11 @@
| [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 |
| [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 | | [13006](https://github.com/yuzu-emu/yuzu//pull/13006) | [`3067bfd12`](https://github.com/yuzu-emu/yuzu//pull/13006/files) | buffer_cache: use mapped range with large vertex buffer size | [liamwhite](https://github.com/liamwhite/) | Yes |
| [13017](https://github.com/yuzu-emu/yuzu//pull/13017) | [`af4248256`](https://github.com/yuzu-emu/yuzu//pull/13017/files) | kernel: add and enable system suspend type | [liamwhite](https://github.com/liamwhite/) | Yes |
| [13026](https://github.com/yuzu-emu/yuzu//pull/13026) | [`462ea921e`](https://github.com/yuzu-emu/yuzu//pull/13026/files) | shader_recompiler: fix non-const offset for arrayed image types | [liamwhite](https://github.com/liamwhite/) | Yes | | [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 | | [13030](https://github.com/yuzu-emu/yuzu//pull/13030) | [`4cbafc1ef`](https://github.com/yuzu-emu/yuzu//pull/13030/files) | service: audio: Rewrite IAudioController to new IPC | [german77](https://github.com/german77/) | Yes |
| [13035](https://github.com/yuzu-emu/yuzu//pull/13035) | [`940a71422`](https://github.com/yuzu-emu/yuzu//pull/13035/files) | vi: manage resources independently of nvnflinger and refactor | [liamwhite](https://github.com/liamwhite/) | Yes |
| [13048](https://github.com/yuzu-emu/yuzu//pull/13048) | [`4cdf18095`](https://github.com/yuzu-emu/yuzu//pull/13048/files) | ns: rewrite for new IPC | [liamwhite](https://github.com/liamwhite/) | Yes |
| [13070](https://github.com/yuzu-emu/yuzu//pull/13070) | [`911ee8fd1`](https://github.com/yuzu-emu/yuzu//pull/13070/files) | am: account for offset in transfer memory storage | [liamwhite](https://github.com/liamwhite/) | 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.

View File

@ -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() }

View File

@ -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) {

View File

@ -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
}
} }
} }
} }

View File

@ -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)

View File

@ -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) }
} }

View File

@ -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() }
}
} }
} }

View File

@ -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)
} }
} }
} }

View File

@ -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() }
}
} }
} }
} }

View File

@ -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) }
} }

View File

@ -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(

View File

@ -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,9 +107,7 @@ 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) {
settingsViewModel.shouldShowDeleteProfileDialog.collect {
if (it.isNotEmpty()) { if (it.isNotEmpty()) {
MessageDialogFragment.newInstance( MessageDialogFragment.newInstance(
activity = requireActivity(), activity = requireActivity(),
@ -130,8 +125,6 @@ class InputProfileDialogFragment : DialogFragment() {
} }
} }
} }
}
}
companion object { companion object {
const val TAG = "InputProfileDialogFragment" const val TAG = "InputProfileDialogFragment"

View File

@ -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,41 +65,25 @@ class SettingsActivity : AppCompatActivity() {
) )
} }
lifecycleScope.apply { settingsViewModel.shouldRecreate.collect(
launch { this,
repeatOnLifecycle(Lifecycle.State.CREATED) { resetState = { settingsViewModel.setShouldRecreate(false) }
settingsViewModel.shouldRecreate.collectLatest { ) { if (it) recreate() }
settingsViewModel.shouldNavigateBack.collect(
this,
resetState = { settingsViewModel.setShouldNavigateBack(false) }
) { if (it) navigateBack() }
settingsViewModel.shouldShowResetSettingsDialog.collect(
this,
resetState = { settingsViewModel.setShouldShowResetSettingsDialog(false) }
) {
if (it) { if (it) {
settingsViewModel.setShouldRecreate(false)
recreate()
}
}
}
}
launch {
repeatOnLifecycle(Lifecycle.State.CREATED) {
settingsViewModel.shouldNavigateBack.collectLatest {
if (it) {
settingsViewModel.setShouldNavigateBack(false)
navigateBack()
}
}
}
}
launch {
repeatOnLifecycle(Lifecycle.State.CREATED) {
settingsViewModel.shouldShowResetSettingsDialog.collectLatest {
if (it) {
settingsViewModel.setShouldShowResetSettingsDialog(false)
ResetSettingsDialogFragment().show( ResetSettingsDialogFragment().show(
supportFragmentManager, supportFragmentManager,
ResetSettingsDialogFragment.TAG ResetSettingsDialogFragment.TAG
) )
} }
} }
}
}
}
onBackPressedDispatcher.addCallback( onBackPressedDispatcher.addCallback(
this, this,

View File

@ -11,12 +11,8 @@ 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.DialogSliderBinding import org.yuzu.yuzu_emu.databinding.DialogSliderBinding
import org.yuzu.yuzu_emu.features.input.NativeInput import org.yuzu.yuzu_emu.features.input.NativeInput
@ -29,6 +25,7 @@ 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.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
@ -169,21 +166,15 @@ 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) {
settingsViewModel.sliderTextValue.collect {
sliderBinding.textValue.text = it sliderBinding.textValue.text = it
} }
} settingsViewModel.sliderProgress.collect(viewLifecycleOwner) {
repeatOnLifecycle(Lifecycle.State.CREATED) {
settingsViewModel.sliderProgress.collect {
sliderBinding.slider.value = it.toFloat() sliderBinding.slider.value = it.toFloat()
} }
} }
} }
} }
}
}
override fun onClick(dialog: DialogInterface, which: Int) { override fun onClick(dialog: DialogInterface, which: Int) {
when (settingsViewModel.clickedItem) { when (settingsViewModel.clickedItem) {

View File

@ -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,50 +95,26 @@ 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) }
} ) {
}
launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
settingsViewModel.datasetChanged.collect {
if (it) {
settingsAdapter?.notifyDataSetChanged()
settingsViewModel.setDatasetChanged(false)
}
}
}
}
launch {
repeatOnLifecycle(Lifecycle.State.CREATED) {
settingsViewModel.reloadListAndNotifyDataset.collectLatest {
if (it) {
settingsViewModel.setReloadListAndNotifyDataset(false)
presenter.loadSettingsList(true)
}
}
}
}
launch {
repeatOnLifecycle(Lifecycle.State.CREATED) {
settingsViewModel.shouldShowResetInputDialog.collectLatest {
if (it) { if (it) {
MessageDialogFragment.newInstance( MessageDialogFragment.newInstance(
activity = requireActivity(), activity = requireActivity(),
@ -155,10 +126,6 @@ class SettingsFragment : Fragment() {
}, },
negativeAction = {} negativeAction = {}
).show(parentFragmentManager, MessageDialogFragment.TAG) ).show(parentFragmentManager, MessageDialogFragment.TAG)
settingsViewModel.setShouldShowResetInputDialog(false)
}
}
}
} }
} }

View File

@ -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,16 +81,12 @@ class SettingsSearchFragment : Fragment() {
search() search()
binding.settingsList.smoothScrollToPosition(0) binding.settingsList.smoothScrollToPosition(0)
} }
viewLifecycleOwner.lifecycleScope.launch { settingsViewModel.shouldReloadSettingsList.collect(viewLifecycleOwner) {
repeatOnLifecycle(Lifecycle.State.CREATED) {
settingsViewModel.shouldReloadSettingsList.collect {
if (it) { if (it) {
settingsViewModel.setShouldReloadSettingsList(false) settingsViewModel.setShouldReloadSettingsList(false)
search() search()
} }
} }
}
}
search() 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() {

View File

@ -14,6 +14,7 @@ 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.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 +23,18 @@ 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(
!NativeConfig.isPerGameConfigLoaded() !setting.setting.global || NativeConfig.isPerGameConfigLoaded()
) { )
View.GONE
} else {
View.VISIBLE
}
binding.buttonClear.setOnClickListener { binding.buttonClear.setOnClickListener {
adapter.onClearClick(setting, bindingAdapterPosition) adapter.onClearClick(setting, bindingAdapterPosition)
} }

View File

@ -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) =

View File

@ -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
}
} }
} }

View File

@ -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)
} }

View File

@ -11,6 +11,7 @@ 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.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 +20,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 +45,12 @@ 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(
!NativeConfig.isPerGameConfigLoaded() !setting.setting.global || NativeConfig.isPerGameConfigLoaded()
) { )
View.GONE
} else {
View.VISIBLE
}
binding.buttonClear.setOnClickListener { binding.buttonClear.setOnClickListener {
adapter.onClearClick(setting, bindingAdapterPosition) adapter.onClearClick(setting, bindingAdapterPosition)
} }

View File

@ -10,6 +10,7 @@ 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.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 +19,18 @@ 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(
!NativeConfig.isPerGameConfigLoaded() !setting.setting.global || NativeConfig.isPerGameConfigLoaded()
) { )
View.GONE
} else {
View.VISIBLE
}
binding.buttonClear.setOnClickListener { binding.buttonClear.setOnClickListener {
adapter.onClearClick(setting, bindingAdapterPosition) adapter.onClearClick(setting, bindingAdapterPosition)
} }

View File

@ -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) {

View File

@ -10,6 +10,7 @@ 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.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 +20,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 +29,9 @@ 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(
!NativeConfig.isPerGameConfigLoaded() !setting.setting.global || NativeConfig.isPerGameConfigLoaded()
) { )
View.GONE
} else {
View.VISIBLE
}
binding.buttonClear.setOnClickListener { binding.buttonClear.setOnClickListener {
adapter.onClearClick(setting, bindingAdapterPosition) adapter.onClearClick(setting, bindingAdapterPosition)
} }

View File

@ -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,27 +73,17 @@ class AddonsFragment : Fragment() {
adapter = AddonAdapter(addonViewModel) adapter = AddonAdapter(addonViewModel)
} }
viewLifecycleOwner.lifecycleScope.apply { addonViewModel.addonList.collect(viewLifecycleOwner) {
launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
addonViewModel.addonList.collect {
(binding.listAddons.adapter as AddonAdapter).submitList(it) (binding.listAddons.adapter as AddonAdapter).submitList(it)
} }
} addonViewModel.showModInstallPicker.collect(
} viewLifecycleOwner,
launch { resetState = { addonViewModel.showModInstallPicker(false) }
repeatOnLifecycle(Lifecycle.State.STARTED) { ) { if (it) installAddon.launch(Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).data) }
addonViewModel.showModInstallPicker.collect { addonViewModel.showModNoticeDialog.collect(
if (it) { viewLifecycleOwner,
installAddon.launch(Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).data) resetState = { addonViewModel.showModNoticeDialog(false) }
addonViewModel.showModInstallPicker(false) ) {
}
}
}
}
launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
addonViewModel.showModNoticeDialog.collect {
if (it) { if (it) {
MessageDialogFragment.newInstance( MessageDialogFragment.newInstance(
requireActivity(), requireActivity(),
@ -109,14 +94,12 @@ class AddonsFragment : Fragment() {
negativeAction = {}, negativeAction = {},
negativeButtonTitleId = R.string.close negativeButtonTitleId = R.string.close
).show(parentFragmentManager, MessageDialogFragment.TAG) ).show(parentFragmentManager, MessageDialogFragment.TAG)
addonViewModel.showModNoticeDialog(false)
} }
} }
} addonViewModel.addonToDelete.collect(
} viewLifecycleOwner,
launch { resetState = { addonViewModel.setAddonToDelete(null) }
repeatOnLifecycle(Lifecycle.State.STARTED) { ) {
addonViewModel.addonToDelete.collect {
if (it != null) { if (it != null) {
MessageDialogFragment.newInstance( MessageDialogFragment.newInstance(
requireActivity(), requireActivity(),
@ -125,10 +108,6 @@ class AddonsFragment : Fragment() {
positiveAction = { addonViewModel.onDeleteAddon(it) }, positiveAction = { addonViewModel.onDeleteAddon(it) },
negativeAction = {} negativeAction = {}
).show(parentFragmentManager, MessageDialogFragment.TAG) ).show(parentFragmentManager, MessageDialogFragment.TAG)
addonViewModel.setAddonToDelete(null)
}
}
}
} }
} }

View File

@ -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
}
}
}

View File

@ -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
}
}
}
} }
} }

View File

@ -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 {

View File

@ -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,19 +335,11 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
binding.loadingTitle.isSelected = true binding.loadingTitle.isSelected = true
binding.loadingText.isSelected = true binding.loadingText.isSelected = true
viewLifecycleOwner.lifecycleScope.apply {
launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
WindowInfoTracker.getOrCreate(requireContext()) WindowInfoTracker.getOrCreate(requireContext())
.windowLayoutInfo(requireActivity()) .windowLayoutInfo(requireActivity()).collect(viewLifecycleOwner) {
.collect {
updateFoldableLayout(requireActivity() as EmulationActivity, it) updateFoldableLayout(requireActivity() as EmulationActivity, it)
} }
} emulationViewModel.shaderProgress.collect(viewLifecycleOwner) {
}
launch {
repeatOnLifecycle(Lifecycle.State.CREATED) {
emulationViewModel.shaderProgress.collectLatest {
if (it > 0 && it != emulationViewModel.totalShaders.value) { if (it > 0 && it != emulationViewModel.totalShaders.value) {
binding.loadingProgressIndicator.isIndeterminate = false binding.loadingProgressIndicator.isIndeterminate = false
@ -376,36 +353,16 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
binding.loadingProgressIndicator.isIndeterminate = true binding.loadingProgressIndicator.isIndeterminate = true
} }
} }
} emulationViewModel.totalShaders.collect(viewLifecycleOwner) {
}
launch {
repeatOnLifecycle(Lifecycle.State.CREATED) {
emulationViewModel.totalShaders.collectLatest {
binding.loadingProgressIndicator.max = it binding.loadingProgressIndicator.max = it
} }
} emulationViewModel.shaderMessage.collect(viewLifecycleOwner) {
}
launch {
repeatOnLifecycle(Lifecycle.State.CREATED) {
emulationViewModel.shaderMessage.collectLatest {
if (it.isNotEmpty()) { if (it.isNotEmpty()) {
binding.loadingText.text = it binding.loadingText.text = it
} }
} }
}
} emulationViewModel.emulationStarted.collect(viewLifecycleOwner) {
launch {
repeatOnLifecycle(Lifecycle.State.RESUMED) {
driverViewModel.isInteractionAllowed.collect {
if (it) {
startEmulation()
}
}
}
}
launch {
repeatOnLifecycle(Lifecycle.State.CREATED) {
emulationViewModel.emulationStarted.collectLatest {
if (it) { if (it) {
binding.drawerLayout.setDrawerLockMode(IntSetting.LOCK_DRAWER.getInt()) binding.drawerLayout.setDrawerLockMode(IntSetting.LOCK_DRAWER.getInt())
ViewUtils.showView(binding.surfaceInputOverlay) ViewUtils.showView(binding.surfaceInputOverlay)
@ -418,11 +375,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
updateThermalOverlay() updateThermalOverlay()
} }
} }
} emulationViewModel.isEmulationStopping.collect(viewLifecycleOwner) {
}
launch {
repeatOnLifecycle(Lifecycle.State.CREATED) {
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)
@ -430,11 +383,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
ViewUtils.hideView(binding.showFpsText) ViewUtils.hideView(binding.showFpsText)
} }
} }
} emulationViewModel.drawerOpen.collect(viewLifecycleOwner) {
}
launch {
repeatOnLifecycle(Lifecycle.State.CREATED) {
emulationViewModel.drawerOpen.collect {
if (it) { if (it) {
binding.drawerLayout.open() binding.drawerLayout.open()
binding.inGameMenu.requestFocus() binding.inGameMenu.requestFocus()
@ -442,11 +391,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
binding.drawerLayout.close() binding.drawerLayout.close()
} }
} }
} emulationViewModel.programChanged.collect(viewLifecycleOwner) {
}
launch {
repeatOnLifecycle(Lifecycle.State.CREATED) {
emulationViewModel.programChanged.collect {
if (it != 0) { if (it != 0) {
emulationViewModel.setEmulationStarted(false) emulationViewModel.setEmulationStarted(false)
binding.drawerLayout.close() binding.drawerLayout.close()
@ -456,11 +401,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
ViewUtils.showView(binding.loadingIndicator) ViewUtils.showView(binding.loadingIndicator)
} }
} }
} emulationViewModel.emulationStopped.collect(viewLifecycleOwner) {
}
launch {
repeatOnLifecycle(Lifecycle.State.CREATED) {
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!!)
@ -470,8 +411,9 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
emulationViewModel.setEmulationStopped(false) emulationViewModel.setEmulationStopped(false)
} }
} }
}
} driverViewModel.isInteractionAllowed.collect(viewLifecycleOwner) {
if (it) startEmulation()
} }
} }
@ -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(false)
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) {

View File

@ -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,13 +68,9 @@ class GameFoldersFragment : Fragment() {
adapter = FolderAdapter(requireActivity(), gamesViewModel) adapter = FolderAdapter(requireActivity(), gamesViewModel)
} }
viewLifecycleOwner.lifecycleScope.launch { gamesViewModel.folders.collect(viewLifecycleOwner) {
repeatOnLifecycle(Lifecycle.State.CREATED) {
gamesViewModel.folders.collect {
(binding.listFolders.adapter as FolderAdapter).submitList(it) (binding.listFolders.adapter as FolderAdapter).submitList(it)
} }
}
}
val mainActivity = requireActivity() as MainActivity val mainActivity = requireActivity() as MainActivity
binding.buttonAdd.setOnClickListener { binding.buttonAdd.setOnClickListener {

View File

@ -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)

View File

@ -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()
} }

View File

@ -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,16 +73,12 @@ class InstallableFragment : Fragment() {
binding.root.findNavController().popBackStack() binding.root.findNavController().popBackStack()
} }
viewLifecycleOwner.lifecycleScope.launch { homeViewModel.openImportSaves.collect(viewLifecycleOwner) {
repeatOnLifecycle(Lifecycle.State.CREATED) {
homeViewModel.openImportSaves.collect {
if (it) { if (it) {
importSaves.launch(arrayOf("application/zip")) importSaves.launch(arrayOf("application/zip"))
homeViewModel.setOpenImportSaves(false) homeViewModel.setOpenImportSaves(false)
} }
} }
}
}
val installables = listOf( val installables = listOf(
Installable( Installable(

View File

@ -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,10 +62,7 @@ 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 {
repeatOnLifecycle(Lifecycle.State.CREATED) {
taskViewModel.isComplete.collect {
if (it) { if (it) {
dismiss() dismiss()
when (val result = taskViewModel.result.value) { when (val result = taskViewModel.result.value) {
@ -89,20 +84,12 @@ class ProgressDialogFragment : DialogFragment() {
taskViewModel.clear() taskViewModel.clear()
} }
} }
} taskViewModel.cancelled.collect(viewLifecycleOwner) {
}
launch {
repeatOnLifecycle(Lifecycle.State.CREATED) {
taskViewModel.cancelled.collect {
if (it) { if (it) {
dialog?.setTitle(R.string.cancelling) dialog?.setTitle(R.string.cancelling)
} }
} }
} taskViewModel.progress.collect(viewLifecycleOwner) {
}
launch {
repeatOnLifecycle(Lifecycle.State.CREATED) {
taskViewModel.progress.collect {
if (it != 0.0) { if (it != 0.0) {
binding.progressBar.apply { binding.progressBar.apply {
isIndeterminate = false isIndeterminate = false
@ -115,22 +102,11 @@ class ProgressDialogFragment : DialogFragment() {
} }
} }
} }
} taskViewModel.message.collect(viewLifecycleOwner) {
} binding.message.setVisible(it.isNotEmpty())
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 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.
// Setting the OnClickListener again after the dialog is shown overrides this behavior. // Setting the OnClickListener again after the dialog is shown overrides this behavior.

View File

@ -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)
}
}
}
}
launch {
repeatOnLifecycle(Lifecycle.State.CREATED) {
gamesViewModel.games.collectLatest { filterAndSearch() }
}
}
launch {
repeatOnLifecycle(Lifecycle.State.CREATED) {
gamesViewModel.searchedGames.collect {
(binding.gridGamesSearch.adapter as GameAdapter).submitList(it) (binding.gridGamesSearch.adapter as GameAdapter).submitList(it)
if (it.isEmpty()) { binding.noResultsView.setVisible(it.isNotEmpty())
binding.noResultsView.visibility = View.VISIBLE
} else {
binding.noResultsView.visibility = View.GONE
}
}
}
}
} }
binding.clearButton.setOnClickListener { binding.searchText.setText("") } binding.clearButton.setOnClickListener { binding.searchText.setText("") }

View File

@ -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)
} }

View File

@ -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 {
repeatOnLifecycle(Lifecycle.State.RESUMED) {
gamesViewModel.isReloading.collect {
binding.swipeRefresh.isRefreshing = it binding.swipeRefresh.isRefreshing = it
if (gamesViewModel.games.value.isEmpty() && !it) { binding.noticeText.setVisible(
binding.noticeText.visibility = View.VISIBLE visible = gamesViewModel.games.value.isEmpty() && !it,
} else { gone = false
binding.noticeText.visibility = View.INVISIBLE )
} }
} gamesViewModel.games.collect(viewLifecycleOwner) {
}
}
launch {
repeatOnLifecycle(Lifecycle.State.RESUMED) {
gamesViewModel.games.collectLatest {
(binding.gridGames.adapter as GameAdapter).submitList(it) (binding.gridGames.adapter as GameAdapter).submitList(it)
} }
} gamesViewModel.shouldSwapData.collect(
} viewLifecycleOwner,
launch { resetState = { gamesViewModel.setShouldSwapData(false) }
repeatOnLifecycle(Lifecycle.State.RESUMED) { ) {
gamesViewModel.shouldSwapData.collect {
if (it) { if (it) {
(binding.gridGames.adapter as GameAdapter).submitList( (binding.gridGames.adapter as GameAdapter).submitList(gamesViewModel.games.value)
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()
} }

View File

@ -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,41 +136,22 @@ 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 {
repeatOnLifecycle(Lifecycle.State.CREATED) {
homeViewModel.statusBarShadeVisible.collect { showStatusBarShade(it) }
}
}
launch {
repeatOnLifecycle(Lifecycle.State.CREATED) {
homeViewModel.contentToInstall.collect {
if (it != null) { if (it != null) {
installContent(it) 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)

View File

@ -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()
}
}
}
}
}

View File

@ -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)
}
} }

View File

@ -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>(

View File

@ -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)};
} }

View File

@ -190,8 +190,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 +198,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);
} }
} }

View File

@ -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" />

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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" />

View File

@ -423,12 +423,12 @@ add_library(core STATIC
hle/service/am/applet_manager.h hle/service/am/applet_manager.h
hle/service/am/applet_message_queue.cpp hle/service/am/applet_message_queue.cpp
hle/service/am/applet_message_queue.h hle/service/am/applet_message_queue.h
hle/service/am/display_layer_manager.cpp
hle/service/am/display_layer_manager.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
@ -481,8 +481,6 @@ add_library(core STATIC
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 +489,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
@ -739,15 +737,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_results.h
hle/service/ns/ns_types.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/platform_service_manager.cpp
hle/service/ns/pdm_qry.h 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,12 +831,12 @@ 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.cpp
hle/service/nvnflinger/hos_binder_driver_server.h hle/service/nvnflinger/hos_binder_driver_server.h
hle/service/nvnflinger/hos_binder_driver.cpp
hle/service/nvnflinger/hos_binder_driver.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/hwc_layer.h hle/service/nvnflinger/hwc_layer.h
@ -815,6 +846,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
@ -908,6 +941,8 @@ 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/services.cpp
hle/service/services.h
hle/service/set/setting_formats/appln_settings.cpp hle/service/set/setting_formats/appln_settings.cpp
hle/service/set/setting_formats/appln_settings.h hle/service/set/setting_formats/appln_settings.h
hle/service/set/setting_formats/device_settings.cpp hle/service/set/setting_formats/device_settings.cpp
@ -955,22 +990,26 @@ 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_list.h
hle/service/vi/display.h
hle/service/vi/layer_list.h
hle/service/vi/layer.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
@ -979,6 +1018,8 @@ add_library(core STATIC
hle/service/vi/vi_types.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/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

View File

@ -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{};

View File

@ -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.");

View 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 {

View File

@ -329,9 +329,8 @@ bool ProfileManager::GetProfileBaseAndData(const ProfileInfo& user, ProfileBase&
/// Returns if the system is allowing user registrations or not /// Returns if the system is allowing user registrations or not
bool ProfileManager::CanSystemRegisterUser() const { bool ProfileManager::CanSystemRegisterUser() const {
return false; // TODO(ogniK): Games shouldn't have // TODO: Both games and applets can register users. Determine when this condition is not meet.
// access to user registration, when we return true;
// emulate qlaunch. Update this to dynamically change.
} }
bool ProfileManager::RemoveUser(UUID uuid) { bool ProfileManager::RemoveUser(UUID uuid) {

View File

@ -8,13 +8,13 @@
namespace Service::AM { namespace Service::AM {
void LoopProcess(Nvnflinger::Nvnflinger& nvnflinger, Core::System& system) { void LoopProcess(Core::System& system) {
auto server_manager = std::make_unique<ServerManager>(system); auto server_manager = std::make_unique<ServerManager>(system);
server_manager->RegisterNamedService( server_manager->RegisterNamedService("appletAE",
"appletAE", std::make_shared<IAllSystemAppletProxiesService>(system, nvnflinger)); std::make_shared<IAllSystemAppletProxiesService>(system));
server_manager->RegisterNamedService( server_manager->RegisterNamedService("appletOE",
"appletOE", std::make_shared<IApplicationProxyService>(system, nvnflinger)); std::make_shared<IApplicationProxyService>(system));
ServerManager::RunServer(std::move(server_manager)); ServerManager::RunServer(std::move(server_manager));
} }

View File

@ -7,12 +7,8 @@ namespace Core {
class System; class System;
} }
namespace Service::Nvnflinger {
class Nvnflinger;
}
namespace Service::AM { namespace Service::AM {
void LoopProcess(Nvnflinger::Nvnflinger& nvnflinger, Core::System& system); void LoopProcess(Core::System& system);
} // namespace Service::AM } // namespace Service::AM

View File

@ -14,10 +14,9 @@
#include "core/hle/service/am/am_types.h" #include "core/hle/service/am/am_types.h"
#include "core/hle/service/am/applet_message_queue.h" #include "core/hle/service/am/applet_message_queue.h"
#include "core/hle/service/am/display_layer_manager.h"
#include "core/hle/service/am/hid_registration.h" #include "core/hle/service/am/hid_registration.h"
#include "core/hle/service/am/managed_layer_holder.h"
#include "core/hle/service/am/process.h" #include "core/hle/service/am/process.h"
#include "core/hle/service/am/system_buffer_manager.h"
namespace Service::AM { namespace Service::AM {
@ -54,8 +53,7 @@ struct Applet {
HidRegistration hid_registration; HidRegistration hid_registration;
// vi state // vi state
SystemBufferManager system_buffer_manager{}; DisplayLayerManager display_layer_manager{};
ManagedLayerHolder managed_layer_holder{};
// Applet common functions // Applet common functions
Result terminate_result{}; Result terminate_result{};

View File

@ -35,6 +35,21 @@ AppletStorageChannel& InitializeFakeCallerApplet(Core::System& system,
return applet->caller_applet_broker->GetInData(); return applet->caller_applet_broker->GetInData();
} }
void PushInShowQlaunch(Core::System& system, AppletStorageChannel& channel) {
const CommonArguments arguments{
.arguments_version = CommonArgumentVersion::Version3,
.size = CommonArgumentSize::Version3,
.library_version = 0,
.theme_color = ThemeColor::BasicBlack,
.play_startup_sound = true,
.system_tick = system.CoreTiming().GetClockTicks(),
};
std::vector<u8> argument_data(sizeof(arguments));
std::memcpy(argument_data.data(), &arguments, sizeof(arguments));
channel.Push(std::make_shared<IStorage>(system, std::move(argument_data)));
}
void PushInShowAlbum(Core::System& system, AppletStorageChannel& channel) { void PushInShowAlbum(Core::System& system, AppletStorageChannel& channel) {
const CommonArguments arguments{ const CommonArguments arguments{
.arguments_version = CommonArgumentVersion::Version3, .arguments_version = CommonArgumentVersion::Version3,
@ -284,6 +299,9 @@ void AppletManager::CreateAndInsertByFrontendAppletParameters(
// Starting from frontend, some applets require input data. // Starting from frontend, some applets require input data.
switch (applet->applet_id) { switch (applet->applet_id) {
case AppletId::QLaunch:
PushInShowQlaunch(m_system, InitializeFakeCallerApplet(m_system, applet));
break;
case AppletId::Cabinet: case AppletId::Cabinet:
PushInShowCabinetData(m_system, InitializeFakeCallerApplet(m_system, applet)); PushInShowCabinetData(m_system, InitializeFakeCallerApplet(m_system, applet));
break; break;

View File

@ -0,0 +1,151 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/core.h"
#include "core/hle/service/am/display_layer_manager.h"
#include "core/hle/service/sm/sm.h"
#include "core/hle/service/vi/application_display_service.h"
#include "core/hle/service/vi/container.h"
#include "core/hle/service/vi/manager_display_service.h"
#include "core/hle/service/vi/manager_root_service.h"
#include "core/hle/service/vi/shared_buffer_manager.h"
#include "core/hle/service/vi/vi_results.h"
#include "core/hle/service/vi/vi_types.h"
namespace Service::AM {
DisplayLayerManager::DisplayLayerManager() = default;
DisplayLayerManager::~DisplayLayerManager() {
this->Finalize();
}
void DisplayLayerManager::Initialize(Core::System& system, Kernel::KProcess* process,
AppletId applet_id, LibraryAppletMode mode) {
R_ASSERT(system.ServiceManager()
.GetService<VI::IManagerRootService>("vi:m", true)
->GetDisplayService(&m_display_service, VI::Policy::Compositor));
R_ASSERT(m_display_service->GetManagerDisplayService(&m_manager_display_service));
m_process = process;
m_system_shared_buffer_id = 0;
m_system_shared_layer_id = 0;
m_applet_id = applet_id;
m_buffer_sharing_enabled = false;
m_blending_enabled = mode == LibraryAppletMode::PartialForeground ||
mode == LibraryAppletMode::PartialForegroundIndirectDisplay;
}
void DisplayLayerManager::Finalize() {
if (!m_manager_display_service) {
return;
}
// Clean up managed layers.
for (const auto& layer : m_managed_display_layers) {
m_manager_display_service->DestroyManagedLayer(layer);
}
for (const auto& layer : m_managed_display_recording_layers) {
m_manager_display_service->DestroyManagedLayer(layer);
}
// Clean up shared layers.
if (m_buffer_sharing_enabled) {
m_manager_display_service->DestroySharedLayerSession(m_process);
}
m_manager_display_service = nullptr;
m_display_service = nullptr;
}
Result DisplayLayerManager::CreateManagedDisplayLayer(u64* out_layer_id) {
R_UNLESS(m_manager_display_service != nullptr, VI::ResultOperationFailed);
// TODO(Subv): Find out how AM determines the display to use, for now just
// create the layer in the Default display.
u64 display_id;
R_TRY(m_display_service->OpenDisplay(&display_id, VI::DisplayName{"Default"}));
R_TRY(m_manager_display_service->CreateManagedLayer(
out_layer_id, 0, display_id, Service::AppletResourceUserId{m_process->GetProcessId()}));
m_manager_display_service->SetLayerVisibility(m_visible, *out_layer_id);
m_managed_display_layers.emplace(*out_layer_id);
R_SUCCEED();
}
Result DisplayLayerManager::CreateManagedDisplaySeparableLayer(u64* out_layer_id,
u64* out_recording_layer_id) {
R_UNLESS(m_manager_display_service != nullptr, VI::ResultOperationFailed);
// TODO(Subv): Find out how AM determines the display to use, for now just
// create the layer in the Default display.
// This calls nn::vi::CreateRecordingLayer() which creates another layer.
// Currently we do not support more than 1 layer per display, output 1 layer id for now.
// Outputting 1 layer id instead of the expected 2 has not been observed to cause any adverse
// side effects.
*out_recording_layer_id = 0;
R_RETURN(this->CreateManagedDisplayLayer(out_layer_id));
}
Result DisplayLayerManager::IsSystemBufferSharingEnabled() {
// Succeed if already enabled.
R_SUCCEED_IF(m_buffer_sharing_enabled);
// Ensure we can access shared layers.
R_UNLESS(m_manager_display_service != nullptr, VI::ResultOperationFailed);
R_UNLESS(m_applet_id != AppletId::Application, VI::ResultPermissionDenied);
// Create the shared layer.
u64 display_id;
R_TRY(m_display_service->OpenDisplay(&display_id, VI::DisplayName{"Default"}));
R_TRY(m_manager_display_service->CreateSharedLayerSession(m_process, &m_system_shared_buffer_id,
&m_system_shared_layer_id, display_id,
m_blending_enabled));
// We succeeded, so set up remaining state.
m_buffer_sharing_enabled = true;
m_manager_display_service->SetLayerVisibility(m_visible, m_system_shared_layer_id);
R_SUCCEED();
}
Result DisplayLayerManager::GetSystemSharedLayerHandle(u64* out_system_shared_buffer_id,
u64* out_system_shared_layer_id) {
R_TRY(this->IsSystemBufferSharingEnabled());
*out_system_shared_buffer_id = m_system_shared_buffer_id;
*out_system_shared_layer_id = m_system_shared_layer_id;
R_SUCCEED();
}
void DisplayLayerManager::SetWindowVisibility(bool visible) {
if (m_visible == visible) {
return;
}
m_visible = visible;
if (m_manager_display_service) {
if (m_system_shared_layer_id) {
m_manager_display_service->SetLayerVisibility(m_visible, m_system_shared_layer_id);
}
for (const auto layer_id : m_managed_display_layers) {
m_manager_display_service->SetLayerVisibility(m_visible, layer_id);
}
}
}
bool DisplayLayerManager::GetWindowVisibility() const {
return m_visible;
}
Result DisplayLayerManager::WriteAppletCaptureBuffer(bool* out_was_written,
s32* out_fbshare_layer_index) {
R_UNLESS(m_buffer_sharing_enabled, VI::ResultPermissionDenied);
R_RETURN(m_display_service->GetContainer()->GetSharedBufferManager()->WriteAppletCaptureBuffer(
out_was_written, out_fbshare_layer_index));
}
} // namespace Service::AM

View File

@ -0,0 +1,62 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <set>
#include "common/common_types.h"
#include "core/hle/result.h"
#include "core/hle/service/am/am_types.h"
namespace Core {
class System;
}
namespace Kernel {
class KProcess;
}
namespace Service::VI {
class IApplicationDisplayService;
class IManagerDisplayService;
} // namespace Service::VI
namespace Service::AM {
class DisplayLayerManager {
public:
explicit DisplayLayerManager();
~DisplayLayerManager();
void Initialize(Core::System& system, Kernel::KProcess* process, AppletId applet_id,
LibraryAppletMode mode);
void Finalize();
Result CreateManagedDisplayLayer(u64* out_layer_id);
Result CreateManagedDisplaySeparableLayer(u64* out_layer_id, u64* out_recording_layer_id);
Result IsSystemBufferSharingEnabled();
Result GetSystemSharedLayerHandle(u64* out_system_shared_buffer_id,
u64* out_system_shared_layer_id);
void SetWindowVisibility(bool visible);
bool GetWindowVisibility() const;
Result WriteAppletCaptureBuffer(bool* out_was_written, s32* out_fbshare_layer_index);
private:
Kernel::KProcess* m_process{};
std::shared_ptr<VI::IApplicationDisplayService> m_display_service{};
std::shared_ptr<VI::IManagerDisplayService> m_manager_display_service{};
std::set<u64> m_managed_display_layers{};
std::set<u64> m_managed_display_recording_layers{};
u64 m_system_shared_buffer_id{};
u64 m_system_shared_layer_id{};
AppletId m_applet_id{};
bool m_buffer_sharing_enabled{};
bool m_blending_enabled{};
bool m_visible{true};
};
} // namespace Service::AM

View File

@ -22,7 +22,7 @@
#include "core/hle/service/am/frontend/applet_web_browser.h" #include "core/hle/service/am/frontend/applet_web_browser.h"
#include "core/hle/service/am/service/storage.h" #include "core/hle/service/am/service/storage.h"
#include "core/hle/service/filesystem/filesystem.h" #include "core/hle/service/filesystem/filesystem.h"
#include "core/hle/service/ns/iplatform_service_manager.h" #include "core/hle/service/ns/platform_service_manager.h"
#include "core/loader/loader.h" #include "core/loader/loader.h"
namespace Service::AM::Frontend { namespace Service::AM::Frontend {

View File

@ -70,7 +70,7 @@ public:
Result Read(s64 offset, void* buffer, size_t size) override { Result Read(s64 offset, void* buffer, size_t size) override {
R_TRY(ValidateOffset(offset, size, m_size)); R_TRY(ValidateOffset(offset, size, m_size));
m_memory.ReadBlock(m_trmem->GetSourceAddress(), buffer, size); m_memory.ReadBlock(m_trmem->GetSourceAddress() + offset, buffer, size);
R_SUCCEED(); R_SUCCEED();
} }
@ -79,7 +79,7 @@ public:
R_UNLESS(m_is_writable, ResultUnknown); R_UNLESS(m_is_writable, ResultUnknown);
R_TRY(ValidateOffset(offset, size, m_size)); R_TRY(ValidateOffset(offset, size, m_size));
m_memory.WriteBlock(m_trmem->GetSourceAddress(), buffer, size); m_memory.WriteBlock(m_trmem->GetSourceAddress() + offset, buffer, size);
R_SUCCEED(); R_SUCCEED();
} }

View File

@ -1,59 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/service/am/managed_layer_holder.h"
#include "core/hle/service/nvnflinger/nvnflinger.h"
namespace Service::AM {
ManagedLayerHolder::ManagedLayerHolder() = default;
ManagedLayerHolder::~ManagedLayerHolder() {
if (!m_nvnflinger) {
return;
}
for (const auto& layer : m_managed_display_layers) {
m_nvnflinger->DestroyLayer(layer);
}
for (const auto& layer : m_managed_display_recording_layers) {
m_nvnflinger->DestroyLayer(layer);
}
m_nvnflinger = nullptr;
}
void ManagedLayerHolder::Initialize(Nvnflinger::Nvnflinger* nvnflinger) {
m_nvnflinger = nvnflinger;
}
void ManagedLayerHolder::CreateManagedDisplayLayer(u64* out_layer) {
// TODO(Subv): Find out how AM determines the display to use, for now just
// create the layer in the Default display.
const auto display_id = m_nvnflinger->OpenDisplay("Default");
const auto layer_id = m_nvnflinger->CreateLayer(*display_id);
m_managed_display_layers.emplace(*layer_id);
*out_layer = *layer_id;
}
void ManagedLayerHolder::CreateManagedDisplaySeparableLayer(u64* out_layer,
u64* out_recording_layer) {
// TODO(Subv): Find out how AM determines the display to use, for now just
// create the layer in the Default display.
// This calls nn::vi::CreateRecordingLayer() which creates another layer.
// Currently we do not support more than 1 layer per display, output 1 layer id for now.
// Outputting 1 layer id instead of the expected 2 has not been observed to cause any adverse
// side effects.
// TODO: Support multiple layers
const auto display_id = m_nvnflinger->OpenDisplay("Default");
const auto layer_id = m_nvnflinger->CreateLayer(*display_id);
m_managed_display_layers.emplace(*layer_id);
*out_layer = *layer_id;
*out_recording_layer = 0;
}
} // namespace Service::AM

View File

@ -1,32 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <set>
#include "common/common_funcs.h"
#include "common/common_types.h"
namespace Service::Nvnflinger {
class Nvnflinger;
}
namespace Service::AM {
class ManagedLayerHolder {
public:
ManagedLayerHolder();
~ManagedLayerHolder();
void Initialize(Nvnflinger::Nvnflinger* nvnflinger);
void CreateManagedDisplayLayer(u64* out_layer);
void CreateManagedDisplaySeparableLayer(u64* out_layer, u64* out_recording_layer);
private:
Nvnflinger::Nvnflinger* m_nvnflinger{};
std::set<u64> m_managed_display_layers{};
std::set<u64> m_managed_display_recording_layers{};
};
} // namespace Service::AM

View File

@ -10,9 +10,8 @@
namespace Service::AM { namespace Service::AM {
IAllSystemAppletProxiesService::IAllSystemAppletProxiesService(Core::System& system_, IAllSystemAppletProxiesService::IAllSystemAppletProxiesService(Core::System& system_)
Nvnflinger::Nvnflinger& nvnflinger) : ServiceFramework{system_, "appletAE"} {
: ServiceFramework{system_, "appletAE"}, m_nvnflinger{nvnflinger} {
// clang-format off // clang-format off
static const FunctionInfo functions[] = { static const FunctionInfo functions[] = {
{100, D<&IAllSystemAppletProxiesService::OpenSystemAppletProxy>, "OpenSystemAppletProxy"}, {100, D<&IAllSystemAppletProxiesService::OpenSystemAppletProxy>, "OpenSystemAppletProxy"},
@ -37,8 +36,8 @@ Result IAllSystemAppletProxiesService::OpenSystemAppletProxy(
LOG_DEBUG(Service_AM, "called"); LOG_DEBUG(Service_AM, "called");
if (const auto applet = this->GetAppletFromProcessId(pid); applet) { if (const auto applet = this->GetAppletFromProcessId(pid); applet) {
*out_system_applet_proxy = std::make_shared<ISystemAppletProxy>( *out_system_applet_proxy =
system, applet, process_handle.Get(), m_nvnflinger); std::make_shared<ISystemAppletProxy>(system, applet, process_handle.Get());
R_SUCCEED(); R_SUCCEED();
} else { } else {
UNIMPLEMENTED(); UNIMPLEMENTED();
@ -53,8 +52,8 @@ Result IAllSystemAppletProxiesService::OpenLibraryAppletProxy(
LOG_DEBUG(Service_AM, "called"); LOG_DEBUG(Service_AM, "called");
if (const auto applet = this->GetAppletFromProcessId(pid); applet) { if (const auto applet = this->GetAppletFromProcessId(pid); applet) {
*out_library_applet_proxy = std::make_shared<ILibraryAppletProxy>( *out_library_applet_proxy =
system, applet, process_handle.Get(), m_nvnflinger); std::make_shared<ILibraryAppletProxy>(system, applet, process_handle.Get());
R_SUCCEED(); R_SUCCEED();
} else { } else {
UNIMPLEMENTED(); UNIMPLEMENTED();

View File

@ -8,10 +8,6 @@
namespace Service { namespace Service {
namespace Nvnflinger {
class Nvnflinger;
}
namespace AM { namespace AM {
struct Applet; struct Applet;
@ -22,8 +18,7 @@ class ISystemAppletProxy;
class IAllSystemAppletProxiesService final class IAllSystemAppletProxiesService final
: public ServiceFramework<IAllSystemAppletProxiesService> { : public ServiceFramework<IAllSystemAppletProxiesService> {
public: public:
explicit IAllSystemAppletProxiesService(Core::System& system_, explicit IAllSystemAppletProxiesService(Core::System& system_);
Nvnflinger::Nvnflinger& nvnflinger);
~IAllSystemAppletProxiesService() override; ~IAllSystemAppletProxiesService() override;
private: private:
@ -40,7 +35,6 @@ private:
private: private:
std::shared_ptr<Applet> GetAppletFromProcessId(ProcessId pid); std::shared_ptr<Applet> GetAppletFromProcessId(ProcessId pid);
Nvnflinger::Nvnflinger& m_nvnflinger;
}; };
} // namespace AM } // namespace AM

View File

@ -15,7 +15,9 @@
#include "core/hle/service/cmif_serialization.h" #include "core/hle/service/cmif_serialization.h"
#include "core/hle/service/filesystem/filesystem.h" #include "core/hle/service/filesystem/filesystem.h"
#include "core/hle/service/filesystem/save_data_controller.h" #include "core/hle/service/filesystem/save_data_controller.h"
#include "core/hle/service/ns/ns.h" #include "core/hle/service/glue/glue_manager.h"
#include "core/hle/service/ns/application_manager_interface.h"
#include "core/hle/service/ns/service_getter_interface.h"
#include "core/hle/service/sm/sm.h" #include "core/hle/service/sm/sm.h"
namespace Service::AM { namespace Service::AM {
@ -40,7 +42,7 @@ IApplicationFunctions::IApplicationFunctions(Core::System& system_, std::shared_
{26, D<&IApplicationFunctions::GetSaveDataSize>, "GetSaveDataSize"}, {26, D<&IApplicationFunctions::GetSaveDataSize>, "GetSaveDataSize"},
{27, D<&IApplicationFunctions::CreateCacheStorage>, "CreateCacheStorage"}, {27, D<&IApplicationFunctions::CreateCacheStorage>, "CreateCacheStorage"},
{28, D<&IApplicationFunctions::GetSaveDataSizeMax>, "GetSaveDataSizeMax"}, {28, D<&IApplicationFunctions::GetSaveDataSizeMax>, "GetSaveDataSizeMax"},
{29, nullptr, "GetCacheStorageMax"}, {29, D<&IApplicationFunctions::GetCacheStorageMax>, "GetCacheStorageMax"},
{30, D<&IApplicationFunctions::BeginBlockingHomeButtonShortAndLongPressed>, "BeginBlockingHomeButtonShortAndLongPressed"}, {30, D<&IApplicationFunctions::BeginBlockingHomeButtonShortAndLongPressed>, "BeginBlockingHomeButtonShortAndLongPressed"},
{31, D<&IApplicationFunctions::EndBlockingHomeButtonShortAndLongPressed>, "EndBlockingHomeButtonShortAndLongPressed"}, {31, D<&IApplicationFunctions::EndBlockingHomeButtonShortAndLongPressed>, "EndBlockingHomeButtonShortAndLongPressed"},
{32, D<&IApplicationFunctions::BeginBlockingHomeButton>, "BeginBlockingHomeButton"}, {32, D<&IApplicationFunctions::BeginBlockingHomeButton>, "BeginBlockingHomeButton"},
@ -162,11 +164,13 @@ Result IApplicationFunctions::GetDesiredLanguage(Out<u64> out_language_code) {
// Call IApplicationManagerInterface implementation. // Call IApplicationManagerInterface implementation.
auto& service_manager = system.ServiceManager(); auto& service_manager = system.ServiceManager();
auto ns_am2 = service_manager.GetService<NS::NS>("ns:am2"); auto ns_am2 = service_manager.GetService<NS::IServiceGetterInterface>("ns:am2");
auto app_man = ns_am2->GetApplicationManagerInterface();
std::shared_ptr<NS::IApplicationManagerInterface> app_man;
R_TRY(ns_am2->GetApplicationManagerInterface(&app_man));
// Get desired application language // Get desired application language
u8 desired_language{}; NS::ApplicationLanguage desired_language{};
R_TRY(app_man->GetApplicationDesiredLanguage(&desired_language, supported_languages)); R_TRY(app_man->GetApplicationDesiredLanguage(&desired_language, supported_languages));
// Convert to settings language code. // Convert to settings language code.
@ -267,6 +271,22 @@ Result IApplicationFunctions::GetSaveDataSizeMax(Out<u64> out_max_normal_size,
R_SUCCEED(); R_SUCCEED();
} }
Result IApplicationFunctions::GetCacheStorageMax(Out<u32> out_cache_storage_index_max,
Out<u64> out_max_journal_size) {
LOG_DEBUG(Service_AM, "called");
std::vector<u8> nacp;
R_TRY(system.GetARPManager().GetControlProperty(&nacp, m_applet->program_id));
auto raw_nacp = std::make_unique<FileSys::RawNACP>();
std::memcpy(raw_nacp.get(), nacp.data(), std::min(sizeof(*raw_nacp), nacp.size()));
*out_cache_storage_index_max = static_cast<u32>(raw_nacp->cache_storage_max_index);
*out_max_journal_size = static_cast<u64>(raw_nacp->cache_storage_data_and_journal_max_size);
R_SUCCEED();
}
Result IApplicationFunctions::BeginBlockingHomeButtonShortAndLongPressed(s64 unused) { Result IApplicationFunctions::BeginBlockingHomeButtonShortAndLongPressed(s64 unused) {
LOG_WARNING(Service_AM, "(STUBBED) called"); LOG_WARNING(Service_AM, "(STUBBED) called");

View File

@ -40,6 +40,7 @@ private:
Result CreateCacheStorage(Out<u32> out_target_media, Out<u64> out_required_size, u16 index, Result CreateCacheStorage(Out<u32> out_target_media, Out<u64> out_required_size, u16 index,
u64 normal_size, u64 journal_size); u64 normal_size, u64 journal_size);
Result GetSaveDataSizeMax(Out<u64> out_max_normal_size, Out<u64> out_max_journal_size); Result GetSaveDataSizeMax(Out<u64> out_max_normal_size, Out<u64> out_max_journal_size);
Result GetCacheStorageMax(Out<u32> out_cache_storage_index_max, Out<u64> out_max_journal_size);
Result BeginBlockingHomeButtonShortAndLongPressed(s64 unused); Result BeginBlockingHomeButtonShortAndLongPressed(s64 unused);
Result EndBlockingHomeButtonShortAndLongPressed(); Result EndBlockingHomeButtonShortAndLongPressed();
Result BeginBlockingHomeButton(s64 timeout_ns); Result BeginBlockingHomeButton(s64 timeout_ns);

View File

@ -17,9 +17,9 @@
namespace Service::AM { namespace Service::AM {
IApplicationProxy::IApplicationProxy(Core::System& system_, std::shared_ptr<Applet> applet, IApplicationProxy::IApplicationProxy(Core::System& system_, std::shared_ptr<Applet> applet,
Kernel::KProcess* process, Nvnflinger::Nvnflinger& nvnflinger) Kernel::KProcess* process)
: ServiceFramework{system_, "IApplicationProxy"}, : ServiceFramework{system_, "IApplicationProxy"}, m_process{process}, m_applet{
m_nvnflinger{nvnflinger}, m_process{process}, m_applet{std::move(applet)} { std::move(applet)} {
// clang-format off // clang-format off
static const FunctionInfo functions[] = { static const FunctionInfo functions[] = {
{0, D<&IApplicationProxy::GetCommonStateGetter>, "GetCommonStateGetter"}, {0, D<&IApplicationProxy::GetCommonStateGetter>, "GetCommonStateGetter"},
@ -77,8 +77,7 @@ Result IApplicationProxy::GetWindowController(
Result IApplicationProxy::GetSelfController( Result IApplicationProxy::GetSelfController(
Out<SharedPointer<ISelfController>> out_self_controller) { Out<SharedPointer<ISelfController>> out_self_controller) {
LOG_DEBUG(Service_AM, "called"); LOG_DEBUG(Service_AM, "called");
*out_self_controller = *out_self_controller = std::make_shared<ISelfController>(system, m_applet, m_process);
std::make_shared<ISelfController>(system, m_applet, m_process, m_nvnflinger);
R_SUCCEED(); R_SUCCEED();
} }

View File

@ -22,7 +22,7 @@ class IWindowController;
class IApplicationProxy final : public ServiceFramework<IApplicationProxy> { class IApplicationProxy final : public ServiceFramework<IApplicationProxy> {
public: public:
explicit IApplicationProxy(Core::System& system_, std::shared_ptr<Applet> applet, explicit IApplicationProxy(Core::System& system_, std::shared_ptr<Applet> applet,
Kernel::KProcess* process, Nvnflinger::Nvnflinger& nvnflinger); Kernel::KProcess* process);
~IApplicationProxy(); ~IApplicationProxy();
private: private:
@ -40,7 +40,6 @@ private:
Out<SharedPointer<IApplicationFunctions>> out_application_functions); Out<SharedPointer<IApplicationFunctions>> out_application_functions);
private: private:
Nvnflinger::Nvnflinger& m_nvnflinger;
Kernel::KProcess* const m_process; Kernel::KProcess* const m_process;
const std::shared_ptr<Applet> m_applet; const std::shared_ptr<Applet> m_applet;
}; };

View File

@ -10,9 +10,8 @@
namespace Service::AM { namespace Service::AM {
IApplicationProxyService::IApplicationProxyService(Core::System& system_, IApplicationProxyService::IApplicationProxyService(Core::System& system_)
Nvnflinger::Nvnflinger& nvnflinger) : ServiceFramework{system_, "appletOE"} {
: ServiceFramework{system_, "appletOE"}, m_nvnflinger{nvnflinger} {
static const FunctionInfo functions[] = { static const FunctionInfo functions[] = {
{0, D<&IApplicationProxyService::OpenApplicationProxy>, "OpenApplicationProxy"}, {0, D<&IApplicationProxyService::OpenApplicationProxy>, "OpenApplicationProxy"},
}; };
@ -28,7 +27,7 @@ Result IApplicationProxyService::OpenApplicationProxy(
if (const auto applet = this->GetAppletFromProcessId(pid)) { if (const auto applet = this->GetAppletFromProcessId(pid)) {
*out_application_proxy = *out_application_proxy =
std::make_shared<IApplicationProxy>(system, applet, process_handle.Get(), m_nvnflinger); std::make_shared<IApplicationProxy>(system, applet, process_handle.Get());
R_SUCCEED(); R_SUCCEED();
} else { } else {
UNIMPLEMENTED(); UNIMPLEMENTED();

View File

@ -8,10 +8,6 @@
namespace Service { namespace Service {
namespace Nvnflinger {
class Nvnflinger;
}
namespace AM { namespace AM {
struct Applet; struct Applet;
@ -19,7 +15,7 @@ class IApplicationProxy;
class IApplicationProxyService final : public ServiceFramework<IApplicationProxyService> { class IApplicationProxyService final : public ServiceFramework<IApplicationProxyService> {
public: public:
explicit IApplicationProxyService(Core::System& system_, Nvnflinger::Nvnflinger& nvnflinger); explicit IApplicationProxyService(Core::System& system_);
~IApplicationProxyService() override; ~IApplicationProxyService() override;
private: private:
@ -28,7 +24,6 @@ private:
private: private:
std::shared_ptr<Applet> GetAppletFromProcessId(ProcessId pid); std::shared_ptr<Applet> GetAppletFromProcessId(ProcessId pid);
Nvnflinger::Nvnflinger& m_nvnflinger;
}; };
} // namespace AM } // namespace AM

View File

@ -69,7 +69,7 @@ Result IDisplayController::ClearCaptureBuffer(bool unknown0, s32 fbshare_layer_i
Result IDisplayController::AcquireLastForegroundCaptureSharedBuffer( Result IDisplayController::AcquireLastForegroundCaptureSharedBuffer(
Out<bool> out_was_written, Out<s32> out_fbshare_layer_index) { Out<bool> out_was_written, Out<s32> out_fbshare_layer_index) {
LOG_WARNING(Service_AM, "(STUBBED) called"); LOG_WARNING(Service_AM, "(STUBBED) called");
R_RETURN(applet->system_buffer_manager.WriteAppletCaptureBuffer(out_was_written, R_RETURN(applet->display_layer_manager.WriteAppletCaptureBuffer(out_was_written,
out_fbshare_layer_index)); out_fbshare_layer_index));
} }
@ -81,7 +81,7 @@ Result IDisplayController::ReleaseLastForegroundCaptureSharedBuffer() {
Result IDisplayController::AcquireCallerAppletCaptureSharedBuffer( Result IDisplayController::AcquireCallerAppletCaptureSharedBuffer(
Out<bool> out_was_written, Out<s32> out_fbshare_layer_index) { Out<bool> out_was_written, Out<s32> out_fbshare_layer_index) {
LOG_WARNING(Service_AM, "(STUBBED) called"); LOG_WARNING(Service_AM, "(STUBBED) called");
R_RETURN(applet->system_buffer_manager.WriteAppletCaptureBuffer(out_was_written, R_RETURN(applet->display_layer_manager.WriteAppletCaptureBuffer(out_was_written,
out_fbshare_layer_index)); out_fbshare_layer_index));
} }
@ -93,7 +93,7 @@ Result IDisplayController::ReleaseCallerAppletCaptureSharedBuffer() {
Result IDisplayController::AcquireLastApplicationCaptureSharedBuffer( Result IDisplayController::AcquireLastApplicationCaptureSharedBuffer(
Out<bool> out_was_written, Out<s32> out_fbshare_layer_index) { Out<bool> out_was_written, Out<s32> out_fbshare_layer_index) {
LOG_WARNING(Service_AM, "(STUBBED) called"); LOG_WARNING(Service_AM, "(STUBBED) called");
R_RETURN(applet->system_buffer_manager.WriteAppletCaptureBuffer(out_was_written, R_RETURN(applet->display_layer_manager.WriteAppletCaptureBuffer(out_was_written,
out_fbshare_layer_index)); out_fbshare_layer_index));
} }

View File

@ -135,7 +135,7 @@ std::shared_ptr<ILibraryAppletAccessor> CreateGuestApplet(Core::System& system,
case LibraryAppletMode::AllForegroundInitiallyHidden: case LibraryAppletMode::AllForegroundInitiallyHidden:
applet->hid_registration.EnableAppletToGetInput(false); applet->hid_registration.EnableAppletToGetInput(false);
applet->focus_state = FocusState::NotInFocus; applet->focus_state = FocusState::NotInFocus;
applet->system_buffer_manager.SetWindowVisibility(false); applet->display_layer_manager.SetWindowVisibility(false);
applet->message_queue.PushMessage(AppletMessage::ChangeIntoBackground); applet->message_queue.PushMessage(AppletMessage::ChangeIntoBackground);
break; break;
} }

View File

@ -19,10 +19,9 @@
namespace Service::AM { namespace Service::AM {
ILibraryAppletProxy::ILibraryAppletProxy(Core::System& system_, std::shared_ptr<Applet> applet, ILibraryAppletProxy::ILibraryAppletProxy(Core::System& system_, std::shared_ptr<Applet> applet,
Kernel::KProcess* process, Kernel::KProcess* process)
Nvnflinger::Nvnflinger& nvnflinger) : ServiceFramework{system_, "ILibraryAppletProxy"}, m_process{process}, m_applet{
: ServiceFramework{system_, "ILibraryAppletProxy"}, std::move(applet)} {
m_nvnflinger{nvnflinger}, m_process{process}, m_applet{std::move(applet)} {
// clang-format off // clang-format off
static const FunctionInfo functions[] = { static const FunctionInfo functions[] = {
{0, D<&ILibraryAppletProxy::GetCommonStateGetter>, "GetCommonStateGetter"}, {0, D<&ILibraryAppletProxy::GetCommonStateGetter>, "GetCommonStateGetter"},
@ -83,8 +82,7 @@ Result ILibraryAppletProxy::GetWindowController(
Result ILibraryAppletProxy::GetSelfController( Result ILibraryAppletProxy::GetSelfController(
Out<SharedPointer<ISelfController>> out_self_controller) { Out<SharedPointer<ISelfController>> out_self_controller) {
LOG_DEBUG(Service_AM, "called"); LOG_DEBUG(Service_AM, "called");
*out_self_controller = *out_self_controller = std::make_shared<ISelfController>(system, m_applet, m_process);
std::make_shared<ISelfController>(system, m_applet, m_process, m_nvnflinger);
R_SUCCEED(); R_SUCCEED();
} }

View File

@ -25,7 +25,7 @@ class IWindowController;
class ILibraryAppletProxy final : public ServiceFramework<ILibraryAppletProxy> { class ILibraryAppletProxy final : public ServiceFramework<ILibraryAppletProxy> {
public: public:
explicit ILibraryAppletProxy(Core::System& system_, std::shared_ptr<Applet> applet, explicit ILibraryAppletProxy(Core::System& system_, std::shared_ptr<Applet> applet,
Kernel::KProcess* process, Nvnflinger::Nvnflinger& nvnflinger); Kernel::KProcess* process);
~ILibraryAppletProxy(); ~ILibraryAppletProxy();
private: private:
@ -47,7 +47,6 @@ private:
Result GetGlobalStateController( Result GetGlobalStateController(
Out<SharedPointer<IGlobalStateController>> out_global_state_controller); Out<SharedPointer<IGlobalStateController>> out_global_state_controller);
Nvnflinger::Nvnflinger& m_nvnflinger;
Kernel::KProcess* const m_process; Kernel::KProcess* const m_process;
const std::shared_ptr<Applet> m_applet; const std::shared_ptr<Applet> m_applet;
}; };

View File

@ -14,7 +14,8 @@
#include "core/hle/service/cmif_serialization.h" #include "core/hle/service/cmif_serialization.h"
#include "core/hle/service/filesystem/filesystem.h" #include "core/hle/service/filesystem/filesystem.h"
#include "core/hle/service/glue/glue_manager.h" #include "core/hle/service/glue/glue_manager.h"
#include "core/hle/service/ns/ns.h" #include "core/hle/service/ns/application_manager_interface.h"
#include "core/hle/service/ns/service_getter_interface.h"
#include "core/hle/service/sm/sm.h" #include "core/hle/service/sm/sm.h"
namespace Service::AM { namespace Service::AM {
@ -256,11 +257,13 @@ Result ILibraryAppletSelfAccessor::GetMainAppletApplicationDesiredLanguage(
// Call IApplicationManagerInterface implementation. // Call IApplicationManagerInterface implementation.
auto& service_manager = system.ServiceManager(); auto& service_manager = system.ServiceManager();
auto ns_am2 = service_manager.GetService<NS::NS>("ns:am2"); auto ns_am2 = service_manager.GetService<NS::IServiceGetterInterface>("ns:am2");
auto app_man = ns_am2->GetApplicationManagerInterface();
std::shared_ptr<NS::IApplicationManagerInterface> app_man;
R_TRY(ns_am2->GetApplicationManagerInterface(&app_man));
// Get desired application language // Get desired application language
u8 desired_language{}; NS::ApplicationLanguage desired_language{};
R_TRY(app_man->GetApplicationDesiredLanguage(&desired_language, supported_languages)); R_TRY(app_man->GetApplicationDesiredLanguage(&desired_language, supported_languages));
// Convert to settings language code. // Convert to settings language code.
@ -284,17 +287,17 @@ Result ILibraryAppletSelfAccessor::GetCurrentApplicationId(Out<u64> out_applicat
} }
Result ILibraryAppletSelfAccessor::GetMainAppletAvailableUsers( Result ILibraryAppletSelfAccessor::GetMainAppletAvailableUsers(
Out<bool> out_no_users_available, Out<s32> out_users_count, Out<bool> out_can_select_any_user, Out<s32> out_users_count,
OutArray<Common::UUID, BufferAttr_HipcMapAlias> out_users) { OutArray<Common::UUID, BufferAttr_HipcMapAlias> out_users) {
const Service::Account::ProfileManager manager{}; const Service::Account::ProfileManager manager{};
*out_no_users_available = true; *out_can_select_any_user = false;
*out_users_count = -1; *out_users_count = -1;
LOG_INFO(Service_AM, "called"); LOG_INFO(Service_AM, "called");
if (manager.GetUserCount() > 0) { if (manager.GetUserCount() > 0) {
*out_no_users_available = false; *out_can_select_any_user = true;
*out_users_count = static_cast<s32>(manager.GetUserCount()); *out_users_count = static_cast<s32>(manager.GetUserCount());
const auto users = manager.GetAllUsers(); const auto users = manager.GetAllUsers();

View File

@ -71,7 +71,7 @@ private:
ErrorCode error_code, InLargeData<ErrorContext, BufferAttr_HipcMapAlias> error_context); ErrorCode error_code, InLargeData<ErrorContext, BufferAttr_HipcMapAlias> error_context);
Result GetMainAppletApplicationDesiredLanguage(Out<u64> out_desired_language); Result GetMainAppletApplicationDesiredLanguage(Out<u64> out_desired_language);
Result GetCurrentApplicationId(Out<u64> out_application_id); Result GetCurrentApplicationId(Out<u64> out_application_id);
Result GetMainAppletAvailableUsers(Out<bool> out_no_users_available, Out<s32> out_users_count, Result GetMainAppletAvailableUsers(Out<bool> out_can_select_any_user, Out<s32> out_users_count,
OutArray<Common::UUID, BufferAttr_HipcMapAlias> out_users); OutArray<Common::UUID, BufferAttr_HipcMapAlias> out_users);
Result ShouldSetGpuTimeSliceManually(Out<bool> out_should_set_gpu_time_slice_manually); Result ShouldSetGpuTimeSliceManually(Out<bool> out_should_set_gpu_time_slice_manually);
Result Cmd160(Out<u64> out_unknown0); Result Cmd160(Out<u64> out_unknown0);

View File

@ -15,9 +15,9 @@
namespace Service::AM { namespace Service::AM {
ISelfController::ISelfController(Core::System& system_, std::shared_ptr<Applet> applet, ISelfController::ISelfController(Core::System& system_, std::shared_ptr<Applet> applet,
Kernel::KProcess* process, Nvnflinger::Nvnflinger& nvnflinger) Kernel::KProcess* process)
: ServiceFramework{system_, "ISelfController"}, : ServiceFramework{system_, "ISelfController"}, m_process{process}, m_applet{
m_nvnflinger{nvnflinger}, m_process{process}, m_applet{std::move(applet)} { std::move(applet)} {
// clang-format off // clang-format off
static const FunctionInfo functions[] = { static const FunctionInfo functions[] = {
{0, D<&ISelfController::Exit>, "Exit"}, {0, D<&ISelfController::Exit>, "Exit"},
@ -72,9 +72,16 @@ ISelfController::ISelfController(Core::System& system_, std::shared_ptr<Applet>
// clang-format on // clang-format on
RegisterHandlers(functions); RegisterHandlers(functions);
std::scoped_lock lk{m_applet->lock};
m_applet->display_layer_manager.Initialize(system, m_process, m_applet->applet_id,
m_applet->library_applet_mode);
} }
ISelfController::~ISelfController() = default; ISelfController::~ISelfController() {
std::scoped_lock lk{m_applet->lock};
m_applet->display_layer_manager.Finalize();
}
Result ISelfController::Exit() { Result ISelfController::Exit() {
LOG_DEBUG(Service_AM, "called"); LOG_DEBUG(Service_AM, "called");
@ -212,48 +219,42 @@ Result ISelfController::SetAlbumImageOrientation(
Result ISelfController::IsSystemBufferSharingEnabled() { Result ISelfController::IsSystemBufferSharingEnabled() {
LOG_INFO(Service_AM, "called"); LOG_INFO(Service_AM, "called");
R_SUCCEED_IF(m_applet->system_buffer_manager.Initialize(
&m_nvnflinger, m_process, m_applet->applet_id, m_applet->library_applet_mode)); std::scoped_lock lk{m_applet->lock};
R_THROW(VI::ResultOperationFailed); R_RETURN(m_applet->display_layer_manager.IsSystemBufferSharingEnabled());
} }
Result ISelfController::GetSystemSharedBufferHandle(Out<u64> out_buffer_id) { Result ISelfController::GetSystemSharedBufferHandle(Out<u64> out_buffer_id) {
LOG_WARNING(Service_AM, "(STUBBED) called"); LOG_INFO(Service_AM, "called");
R_TRY(this->IsSystemBufferSharingEnabled());
u64 layer_id; u64 layer_id;
m_applet->system_buffer_manager.GetSystemSharedLayerHandle(out_buffer_id, &layer_id);
R_SUCCEED(); std::scoped_lock lk{m_applet->lock};
R_RETURN(m_applet->display_layer_manager.GetSystemSharedLayerHandle(out_buffer_id, &layer_id));
} }
Result ISelfController::GetSystemSharedLayerHandle(Out<u64> out_buffer_id, Out<u64> out_layer_id) { Result ISelfController::GetSystemSharedLayerHandle(Out<u64> out_buffer_id, Out<u64> out_layer_id) {
LOG_INFO(Service_AM, "(STUBBED) called"); LOG_INFO(Service_AM, "called");
R_TRY(this->IsSystemBufferSharingEnabled()); std::scoped_lock lk{m_applet->lock};
R_RETURN(
m_applet->system_buffer_manager.GetSystemSharedLayerHandle(out_buffer_id, out_layer_id); m_applet->display_layer_manager.GetSystemSharedLayerHandle(out_buffer_id, out_layer_id));
R_SUCCEED();
} }
Result ISelfController::CreateManagedDisplayLayer(Out<u64> out_layer_id) { Result ISelfController::CreateManagedDisplayLayer(Out<u64> out_layer_id) {
LOG_INFO(Service_AM, "called"); LOG_INFO(Service_AM, "called");
m_applet->managed_layer_holder.Initialize(&m_nvnflinger); std::scoped_lock lk{m_applet->lock};
m_applet->managed_layer_holder.CreateManagedDisplayLayer(out_layer_id); R_RETURN(m_applet->display_layer_manager.CreateManagedDisplayLayer(out_layer_id));
R_SUCCEED();
} }
Result ISelfController::CreateManagedDisplaySeparableLayer(Out<u64> out_layer_id, Result ISelfController::CreateManagedDisplaySeparableLayer(Out<u64> out_layer_id,
Out<u64> out_recording_layer_id) { Out<u64> out_recording_layer_id) {
LOG_WARNING(Service_AM, "(STUBBED) called"); LOG_WARNING(Service_AM, "(STUBBED) called");
m_applet->managed_layer_holder.Initialize(&m_nvnflinger); std::scoped_lock lk{m_applet->lock};
m_applet->managed_layer_holder.CreateManagedDisplaySeparableLayer(out_layer_id, R_RETURN(m_applet->display_layer_manager.CreateManagedDisplaySeparableLayer(
out_recording_layer_id); out_layer_id, out_recording_layer_id));
R_SUCCEED();
} }
Result ISelfController::SetHandlesRequestToDisplay(bool enable) { Result ISelfController::SetHandlesRequestToDisplay(bool enable) {

View File

@ -23,7 +23,7 @@ struct Applet;
class ISelfController final : public ServiceFramework<ISelfController> { class ISelfController final : public ServiceFramework<ISelfController> {
public: public:
explicit ISelfController(Core::System& system_, std::shared_ptr<Applet> applet, explicit ISelfController(Core::System& system_, std::shared_ptr<Applet> applet,
Kernel::KProcess* process, Nvnflinger::Nvnflinger& nvnflinger); Kernel::KProcess* process);
~ISelfController() override; ~ISelfController() override;
private: private:
@ -64,7 +64,6 @@ private:
Result SaveCurrentScreenshot(Capture::AlbumReportOption album_report_option); Result SaveCurrentScreenshot(Capture::AlbumReportOption album_report_option);
Result SetRecordVolumeMuted(bool muted); Result SetRecordVolumeMuted(bool muted);
Nvnflinger::Nvnflinger& m_nvnflinger;
Kernel::KProcess* const m_process; Kernel::KProcess* const m_process;
const std::shared_ptr<Applet> m_applet; const std::shared_ptr<Applet> m_applet;
}; };

View File

@ -19,10 +19,9 @@
namespace Service::AM { namespace Service::AM {
ISystemAppletProxy::ISystemAppletProxy(Core::System& system_, std::shared_ptr<Applet> applet, ISystemAppletProxy::ISystemAppletProxy(Core::System& system_, std::shared_ptr<Applet> applet,
Kernel::KProcess* process, Kernel::KProcess* process)
Nvnflinger::Nvnflinger& nvnflinger) : ServiceFramework{system_, "ISystemAppletProxy"}, m_process{process}, m_applet{
: ServiceFramework{system_, "ISystemAppletProxy"}, std::move(applet)} {
m_nvnflinger{nvnflinger}, m_process{process}, m_applet{std::move(applet)} {
// clang-format off // clang-format off
static const FunctionInfo functions[] = { static const FunctionInfo functions[] = {
{0, D<&ISystemAppletProxy::GetCommonStateGetter>, "GetCommonStateGetter"}, {0, D<&ISystemAppletProxy::GetCommonStateGetter>, "GetCommonStateGetter"},
@ -83,8 +82,7 @@ Result ISystemAppletProxy::GetWindowController(
Result ISystemAppletProxy::GetSelfController( Result ISystemAppletProxy::GetSelfController(
Out<SharedPointer<ISelfController>> out_self_controller) { Out<SharedPointer<ISelfController>> out_self_controller) {
LOG_DEBUG(Service_AM, "called"); LOG_DEBUG(Service_AM, "called");
*out_self_controller = *out_self_controller = std::make_shared<ISelfController>(system, m_applet, m_process);
std::make_shared<ISelfController>(system, m_applet, m_process, m_nvnflinger);
R_SUCCEED(); R_SUCCEED();
} }

View File

@ -25,7 +25,7 @@ class IWindowController;
class ISystemAppletProxy final : public ServiceFramework<ISystemAppletProxy> { class ISystemAppletProxy final : public ServiceFramework<ISystemAppletProxy> {
public: public:
explicit ISystemAppletProxy(Core::System& system, std::shared_ptr<Applet> applet, explicit ISystemAppletProxy(Core::System& system, std::shared_ptr<Applet> applet,
Kernel::KProcess* process, Nvnflinger::Nvnflinger& nvnflinger); Kernel::KProcess* process);
~ISystemAppletProxy(); ~ISystemAppletProxy();
private: private:
@ -46,7 +46,6 @@ private:
Result GetGlobalStateController( Result GetGlobalStateController(
Out<SharedPointer<IGlobalStateController>> out_global_state_controller); Out<SharedPointer<IGlobalStateController>> out_global_state_controller);
Nvnflinger::Nvnflinger& m_nvnflinger;
Kernel::KProcess* const m_process; Kernel::KProcess* const m_process;
const std::shared_ptr<Applet> m_applet; const std::shared_ptr<Applet> m_applet;
}; };

View File

@ -63,7 +63,7 @@ Result IWindowController::RejectToChangeIntoBackground() {
} }
Result IWindowController::SetAppletWindowVisibility(bool visible) { Result IWindowController::SetAppletWindowVisibility(bool visible) {
m_applet->system_buffer_manager.SetWindowVisibility(visible); m_applet->display_layer_manager.SetWindowVisibility(visible);
m_applet->hid_registration.EnableAppletToGetInput(visible); m_applet->hid_registration.EnableAppletToGetInput(visible);
if (visible) { if (visible) {

View File

@ -1,80 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/service/am/system_buffer_manager.h"
#include "core/hle/service/nvnflinger/fb_share_buffer_manager.h"
#include "core/hle/service/nvnflinger/nvnflinger.h"
#include "core/hle/service/vi/vi_results.h"
namespace Service::AM {
SystemBufferManager::SystemBufferManager() = default;
SystemBufferManager::~SystemBufferManager() {
if (!m_nvnflinger) {
return;
}
// Clean up shared layers.
if (m_buffer_sharing_enabled) {
m_nvnflinger->GetSystemBufferManager().Finalize(m_process);
}
}
bool SystemBufferManager::Initialize(Nvnflinger::Nvnflinger* nvnflinger, Kernel::KProcess* process,
AppletId applet_id, LibraryAppletMode mode) {
if (m_nvnflinger) {
return m_buffer_sharing_enabled;
}
m_process = process;
m_nvnflinger = nvnflinger;
m_buffer_sharing_enabled = false;
m_system_shared_buffer_id = 0;
m_system_shared_layer_id = 0;
if (applet_id <= AppletId::Application) {
return false;
}
Nvnflinger::LayerBlending blending = Nvnflinger::LayerBlending::None;
if (mode == LibraryAppletMode::PartialForeground ||
mode == LibraryAppletMode::PartialForegroundIndirectDisplay) {
blending = Nvnflinger::LayerBlending::Coverage;
}
const auto display_id = m_nvnflinger->OpenDisplay("Default").value();
const auto res = m_nvnflinger->GetSystemBufferManager().Initialize(
m_process, &m_system_shared_buffer_id, &m_system_shared_layer_id, display_id, blending);
if (res.IsSuccess()) {
m_buffer_sharing_enabled = true;
m_nvnflinger->SetLayerVisibility(m_system_shared_layer_id, m_visible);
}
return m_buffer_sharing_enabled;
}
void SystemBufferManager::SetWindowVisibility(bool visible) {
if (m_visible == visible) {
return;
}
m_visible = visible;
if (m_nvnflinger) {
m_nvnflinger->SetLayerVisibility(m_system_shared_layer_id, m_visible);
}
}
Result SystemBufferManager::WriteAppletCaptureBuffer(bool* out_was_written,
s32* out_fbshare_layer_index) {
if (!m_buffer_sharing_enabled) {
return VI::ResultPermissionDenied;
}
return m_nvnflinger->GetSystemBufferManager().WriteAppletCaptureBuffer(out_was_written,
out_fbshare_layer_index);
}
} // namespace Service::AM

View File

@ -1,52 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <set>
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "core/hle/service/am/am_types.h"
namespace Kernel {
class KProcess;
}
namespace Service::Nvnflinger {
class Nvnflinger;
}
union Result;
namespace Service::AM {
class SystemBufferManager {
public:
SystemBufferManager();
~SystemBufferManager();
bool Initialize(Nvnflinger::Nvnflinger* flinger, Kernel::KProcess* process, AppletId applet_id,
LibraryAppletMode mode);
void GetSystemSharedLayerHandle(u64* out_system_shared_buffer_id,
u64* out_system_shared_layer_id) {
*out_system_shared_buffer_id = m_system_shared_buffer_id;
*out_system_shared_layer_id = m_system_shared_layer_id;
}
void SetWindowVisibility(bool visible);
Result WriteAppletCaptureBuffer(bool* out_was_written, s32* out_fbshare_layer_index);
private:
Kernel::KProcess* m_process{};
Nvnflinger::Nvnflinger* m_nvnflinger{};
bool m_buffer_sharing_enabled{};
bool m_visible{true};
u64 m_system_shared_buffer_id{};
u64 m_system_shared_layer_id{};
};
} // namespace Service::AM

View File

@ -1,201 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/logging/log.h"
#include "core/hle/service/audio/audctl.h"
#include "core/hle/service/ipc_helpers.h"
#include "core/hle/service/set/system_settings_server.h"
#include "core/hle/service/sm/sm.h"
namespace Service::Audio {
AudCtl::AudCtl(Core::System& system_) : ServiceFramework{system_, "audctl"} {
// clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "GetTargetVolume"},
{1, nullptr, "SetTargetVolume"},
{2, &AudCtl::GetTargetVolumeMin, "GetTargetVolumeMin"},
{3, &AudCtl::GetTargetVolumeMax, "GetTargetVolumeMax"},
{4, nullptr, "IsTargetMute"},
{5, nullptr, "SetTargetMute"},
{6, nullptr, "IsTargetConnected"},
{7, nullptr, "SetDefaultTarget"},
{8, nullptr, "GetDefaultTarget"},
{9, &AudCtl::GetAudioOutputMode, "GetAudioOutputMode"},
{10, &AudCtl::SetAudioOutputMode, "SetAudioOutputMode"},
{11, nullptr, "SetForceMutePolicy"},
{12, &AudCtl::GetForceMutePolicy, "GetForceMutePolicy"},
{13, &AudCtl::GetOutputModeSetting, "GetOutputModeSetting"},
{14, &AudCtl::SetOutputModeSetting, "SetOutputModeSetting"},
{15, nullptr, "SetOutputTarget"},
{16, nullptr, "SetInputTargetForceEnabled"},
{17, &AudCtl::SetHeadphoneOutputLevelMode, "SetHeadphoneOutputLevelMode"},
{18, &AudCtl::GetHeadphoneOutputLevelMode, "GetHeadphoneOutputLevelMode"},
{19, nullptr, "AcquireAudioVolumeUpdateEventForPlayReport"},
{20, nullptr, "AcquireAudioOutputDeviceUpdateEventForPlayReport"},
{21, nullptr, "GetAudioOutputTargetForPlayReport"},
{22, nullptr, "NotifyHeadphoneVolumeWarningDisplayedEvent"},
{23, nullptr, "SetSystemOutputMasterVolume"},
{24, nullptr, "GetSystemOutputMasterVolume"},
{25, nullptr, "GetAudioVolumeDataForPlayReport"},
{26, nullptr, "UpdateHeadphoneSettings"},
{27, nullptr, "SetVolumeMappingTableForDev"},
{28, nullptr, "GetAudioOutputChannelCountForPlayReport"},
{29, nullptr, "BindAudioOutputChannelCountUpdateEventForPlayReport"},
{30, &AudCtl::SetSpeakerAutoMuteEnabled, "SetSpeakerAutoMuteEnabled"},
{31, &AudCtl::IsSpeakerAutoMuteEnabled, "IsSpeakerAutoMuteEnabled"},
{32, nullptr, "GetActiveOutputTarget"},
{33, nullptr, "GetTargetDeviceInfo"},
{34, nullptr, "AcquireTargetNotification"},
{35, nullptr, "SetHearingProtectionSafeguardTimerRemainingTimeForDebug"},
{36, nullptr, "GetHearingProtectionSafeguardTimerRemainingTimeForDebug"},
{37, nullptr, "SetHearingProtectionSafeguardEnabled"},
{38, nullptr, "IsHearingProtectionSafeguardEnabled"},
{39, nullptr, "IsHearingProtectionSafeguardMonitoringOutputForDebug"},
{40, nullptr, "GetSystemInformationForDebug"},
{41, nullptr, "SetVolumeButtonLongPressTime"},
{42, nullptr, "SetNativeVolumeForDebug"},
{10000, nullptr, "NotifyAudioOutputTargetForPlayReport"},
{10001, nullptr, "NotifyAudioOutputChannelCountForPlayReport"},
{10002, nullptr, "NotifyUnsupportedUsbOutputDeviceAttachedForPlayReport"},
{10100, nullptr, "GetAudioVolumeDataForPlayReport"},
{10101, nullptr, "BindAudioVolumeUpdateEventForPlayReport"},
{10102, nullptr, "BindAudioOutputTargetUpdateEventForPlayReport"},
{10103, nullptr, "GetAudioOutputTargetForPlayReport"},
{10104, nullptr, "GetAudioOutputChannelCountForPlayReport"},
{10105, nullptr, "BindAudioOutputChannelCountUpdateEventForPlayReport"},
{10106, nullptr, "GetDefaultAudioOutputTargetForPlayReport"},
{50000, nullptr, "SetAnalogInputBoostGainForPrototyping"},
};
// clang-format on
RegisterHandlers(functions);
m_set_sys =
system.ServiceManager().GetService<Service::Set::ISystemSettingsServer>("set:sys", true);
}
AudCtl::~AudCtl() = default;
void AudCtl::GetTargetVolumeMin(HLERequestContext& ctx) {
LOG_DEBUG(Audio, "called.");
// This service function is currently hardcoded on the
// actual console to this value (as of 8.0.0).
constexpr s32 target_min_volume = 0;
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.Push(target_min_volume);
}
void AudCtl::GetTargetVolumeMax(HLERequestContext& ctx) {
LOG_DEBUG(Audio, "called.");
// This service function is currently hardcoded on the
// actual console to this value (as of 8.0.0).
constexpr s32 target_max_volume = 15;
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.Push(target_max_volume);
}
void AudCtl::GetAudioOutputMode(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto target{rp.PopEnum<Set::AudioOutputModeTarget>()};
Set::AudioOutputMode output_mode{};
const auto result = m_set_sys->GetAudioOutputMode(&output_mode, target);
LOG_INFO(Service_SET, "called, target={}, output_mode={}", target, output_mode);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(result);
rb.PushEnum(output_mode);
}
void AudCtl::SetAudioOutputMode(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto target{rp.PopEnum<Set::AudioOutputModeTarget>()};
const auto output_mode{rp.PopEnum<Set::AudioOutputMode>()};
const auto result = m_set_sys->SetAudioOutputMode(target, output_mode);
LOG_INFO(Service_SET, "called, target={}, output_mode={}", target, output_mode);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
}
void AudCtl::GetForceMutePolicy(HLERequestContext& ctx) {
LOG_WARNING(Audio, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.PushEnum(ForceMutePolicy::Disable);
}
void AudCtl::GetOutputModeSetting(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto target{rp.PopEnum<Set::AudioOutputModeTarget>()};
LOG_WARNING(Audio, "(STUBBED) called, target={}", target);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.PushEnum(Set::AudioOutputMode::ch_7_1);
}
void AudCtl::SetOutputModeSetting(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto target{rp.PopEnum<Set::AudioOutputModeTarget>()};
const auto output_mode{rp.PopEnum<Set::AudioOutputMode>()};
LOG_INFO(Service_SET, "called, target={}, output_mode={}", target, output_mode);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void AudCtl::SetHeadphoneOutputLevelMode(HLERequestContext& ctx) {
LOG_WARNING(Audio, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void AudCtl::GetHeadphoneOutputLevelMode(HLERequestContext& ctx) {
LOG_WARNING(Audio, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.PushEnum(HeadphoneOutputLevelMode::Normal);
}
void AudCtl::SetSpeakerAutoMuteEnabled(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto is_speaker_auto_mute_enabled{rp.Pop<bool>()};
LOG_WARNING(Audio, "(STUBBED) called, is_speaker_auto_mute_enabled={}",
is_speaker_auto_mute_enabled);
const auto result = m_set_sys->SetSpeakerAutoMuteFlag(is_speaker_auto_mute_enabled);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
}
void AudCtl::IsSpeakerAutoMuteEnabled(HLERequestContext& ctx) {
bool is_speaker_auto_mute_enabled{};
const auto result = m_set_sys->GetSpeakerAutoMuteFlag(&is_speaker_auto_mute_enabled);
LOG_WARNING(Audio, "(STUBBED) called, is_speaker_auto_mute_enabled={}",
is_speaker_auto_mute_enabled);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(result);
rb.Push<u8>(is_speaker_auto_mute_enabled);
}
} // namespace Service::Audio

View File

@ -1,50 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "core/hle/service/service.h"
namespace Core {
class System;
}
namespace Service::Set {
class ISystemSettingsServer;
}
namespace Service::Audio {
class AudCtl final : public ServiceFramework<AudCtl> {
public:
explicit AudCtl(Core::System& system_);
~AudCtl() override;
private:
enum class ForceMutePolicy {
Disable,
SpeakerMuteOnHeadphoneUnplugged,
};
enum class HeadphoneOutputLevelMode {
Normal,
HighPower,
};
void GetTargetVolumeMin(HLERequestContext& ctx);
void GetTargetVolumeMax(HLERequestContext& ctx);
void GetAudioOutputMode(HLERequestContext& ctx);
void SetAudioOutputMode(HLERequestContext& ctx);
void GetForceMutePolicy(HLERequestContext& ctx);
void GetOutputModeSetting(HLERequestContext& ctx);
void SetOutputModeSetting(HLERequestContext& ctx);
void SetHeadphoneOutputLevelMode(HLERequestContext& ctx);
void GetHeadphoneOutputLevelMode(HLERequestContext& ctx);
void SetSpeakerAutoMuteEnabled(HLERequestContext& ctx);
void IsSpeakerAutoMuteEnabled(HLERequestContext& ctx);
void AcquireTargetNotification(HLERequestContext& ctx);
std::shared_ptr<Service::Set::ISystemSettingsServer> m_set_sys;
};
} // namespace Service::Audio

View File

@ -2,9 +2,9 @@
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include "core/core.h" #include "core/core.h"
#include "core/hle/service/audio/audctl.h"
#include "core/hle/service/audio/audin_u.h" #include "core/hle/service/audio/audin_u.h"
#include "core/hle/service/audio/audio.h" #include "core/hle/service/audio/audio.h"
#include "core/hle/service/audio/audio_controller.h"
#include "core/hle/service/audio/audout_u.h" #include "core/hle/service/audio/audout_u.h"
#include "core/hle/service/audio/audrec_a.h" #include "core/hle/service/audio/audrec_a.h"
#include "core/hle/service/audio/audrec_u.h" #include "core/hle/service/audio/audrec_u.h"
@ -18,7 +18,7 @@ namespace Service::Audio {
void LoopProcess(Core::System& system) { void LoopProcess(Core::System& system) {
auto server_manager = std::make_unique<ServerManager>(system); auto server_manager = std::make_unique<ServerManager>(system);
server_manager->RegisterNamedService("audctl", std::make_shared<AudCtl>(system)); server_manager->RegisterNamedService("audctl", std::make_shared<IAudioController>(system));
server_manager->RegisterNamedService("audout:u", std::make_shared<AudOutU>(system)); server_manager->RegisterNamedService("audout:u", std::make_shared<AudOutU>(system));
server_manager->RegisterNamedService("audin:u", std::make_shared<AudInU>(system)); server_manager->RegisterNamedService("audin:u", std::make_shared<AudInU>(system));
server_manager->RegisterNamedService("audrec:a", std::make_shared<AudRecA>(system)); server_manager->RegisterNamedService("audrec:a", std::make_shared<AudRecA>(system));

View File

@ -0,0 +1,174 @@
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/logging/log.h"
#include "core/hle/service/audio/audio_controller.h"
#include "core/hle/service/cmif_serialization.h"
#include "core/hle/service/ipc_helpers.h"
#include "core/hle/service/set/system_settings_server.h"
#include "core/hle/service/sm/sm.h"
namespace Service::Audio {
IAudioController::IAudioController(Core::System& system_)
: ServiceFramework{system_, "audctl"}, service_context{system, "audctl"} {
// clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "GetTargetVolume"},
{1, nullptr, "SetTargetVolume"},
{2, C<&IAudioController::GetTargetVolumeMin>, "GetTargetVolumeMin"},
{3, C<&IAudioController::GetTargetVolumeMax>, "GetTargetVolumeMax"},
{4, nullptr, "IsTargetMute"},
{5, nullptr, "SetTargetMute"},
{6, nullptr, "IsTargetConnected"},
{7, nullptr, "SetDefaultTarget"},
{8, nullptr, "GetDefaultTarget"},
{9, C<&IAudioController::GetAudioOutputMode>, "GetAudioOutputMode"},
{10, C<&IAudioController::SetAudioOutputMode>, "SetAudioOutputMode"},
{11, nullptr, "SetForceMutePolicy"},
{12, C<&IAudioController::GetForceMutePolicy>, "GetForceMutePolicy"},
{13, C<&IAudioController::GetOutputModeSetting>, "GetOutputModeSetting"},
{14, C<&IAudioController::SetOutputModeSetting>, "SetOutputModeSetting"},
{15, nullptr, "SetOutputTarget"},
{16, nullptr, "SetInputTargetForceEnabled"},
{17, C<&IAudioController::SetHeadphoneOutputLevelMode>, "SetHeadphoneOutputLevelMode"},
{18, C<&IAudioController::GetHeadphoneOutputLevelMode>, "GetHeadphoneOutputLevelMode"},
{19, nullptr, "AcquireAudioVolumeUpdateEventForPlayReport"},
{20, nullptr, "AcquireAudioOutputDeviceUpdateEventForPlayReport"},
{21, nullptr, "GetAudioOutputTargetForPlayReport"},
{22, nullptr, "NotifyHeadphoneVolumeWarningDisplayedEvent"},
{23, nullptr, "SetSystemOutputMasterVolume"},
{24, nullptr, "GetSystemOutputMasterVolume"},
{25, nullptr, "GetAudioVolumeDataForPlayReport"},
{26, nullptr, "UpdateHeadphoneSettings"},
{27, nullptr, "SetVolumeMappingTableForDev"},
{28, nullptr, "GetAudioOutputChannelCountForPlayReport"},
{29, nullptr, "BindAudioOutputChannelCountUpdateEventForPlayReport"},
{30, C<&IAudioController::SetSpeakerAutoMuteEnabled>, "SetSpeakerAutoMuteEnabled"},
{31, C<&IAudioController::IsSpeakerAutoMuteEnabled>, "IsSpeakerAutoMuteEnabled"},
{32, nullptr, "GetActiveOutputTarget"},
{33, nullptr, "GetTargetDeviceInfo"},
{34, C<&IAudioController::AcquireTargetNotification>, "AcquireTargetNotification"},
{35, nullptr, "SetHearingProtectionSafeguardTimerRemainingTimeForDebug"},
{36, nullptr, "GetHearingProtectionSafeguardTimerRemainingTimeForDebug"},
{37, nullptr, "SetHearingProtectionSafeguardEnabled"},
{38, nullptr, "IsHearingProtectionSafeguardEnabled"},
{39, nullptr, "IsHearingProtectionSafeguardMonitoringOutputForDebug"},
{40, nullptr, "GetSystemInformationForDebug"},
{41, nullptr, "SetVolumeButtonLongPressTime"},
{42, nullptr, "SetNativeVolumeForDebug"},
{10000, nullptr, "NotifyAudioOutputTargetForPlayReport"},
{10001, nullptr, "NotifyAudioOutputChannelCountForPlayReport"},
{10002, nullptr, "NotifyUnsupportedUsbOutputDeviceAttachedForPlayReport"},
{10100, nullptr, "GetAudioVolumeDataForPlayReport"},
{10101, nullptr, "BindAudioVolumeUpdateEventForPlayReport"},
{10102, nullptr, "BindAudioOutputTargetUpdateEventForPlayReport"},
{10103, nullptr, "GetAudioOutputTargetForPlayReport"},
{10104, nullptr, "GetAudioOutputChannelCountForPlayReport"},
{10105, nullptr, "BindAudioOutputChannelCountUpdateEventForPlayReport"},
{10106, nullptr, "GetDefaultAudioOutputTargetForPlayReport"},
{50000, nullptr, "SetAnalogInputBoostGainForPrototyping"},
};
// clang-format on
RegisterHandlers(functions);
m_set_sys =
system.ServiceManager().GetService<Service::Set::ISystemSettingsServer>("set:sys", true);
notification_event = service_context.CreateEvent("IAudioController:NotificationEvent");
}
IAudioController::~IAudioController() {
service_context.CloseEvent(notification_event);
};
Result IAudioController::GetTargetVolumeMin(Out<s32> out_target_min_volume) {
LOG_DEBUG(Audio, "called.");
// This service function is currently hardcoded on the
// actual console to this value (as of 8.0.0).
*out_target_min_volume = 0;
R_SUCCEED();
}
Result IAudioController::GetTargetVolumeMax(Out<s32> out_target_max_volume) {
LOG_DEBUG(Audio, "called.");
// This service function is currently hardcoded on the
// actual console to this value (as of 8.0.0).
*out_target_max_volume = 15;
R_SUCCEED();
}
Result IAudioController::GetAudioOutputMode(Out<Set::AudioOutputMode> out_output_mode,
Set::AudioOutputModeTarget target) {
const auto result = m_set_sys->GetAudioOutputMode(out_output_mode, target);
LOG_INFO(Service_SET, "called, target={}, output_mode={}", target, *out_output_mode);
R_RETURN(result);
}
Result IAudioController::SetAudioOutputMode(Set::AudioOutputModeTarget target,
Set::AudioOutputMode output_mode) {
LOG_INFO(Service_SET, "called, target={}, output_mode={}", target, output_mode);
R_RETURN(m_set_sys->SetAudioOutputMode(target, output_mode));
}
Result IAudioController::GetForceMutePolicy(Out<ForceMutePolicy> out_mute_policy) {
LOG_WARNING(Audio, "(STUBBED) called");
// Removed on FW 13.2.1+
*out_mute_policy = ForceMutePolicy::Disable;
R_SUCCEED();
}
Result IAudioController::GetOutputModeSetting(Out<Set::AudioOutputMode> out_output_mode,
Set::AudioOutputModeTarget target) {
LOG_WARNING(Audio, "(STUBBED) called, target={}", target);
*out_output_mode = Set::AudioOutputMode::ch_7_1;
R_SUCCEED();
}
Result IAudioController::SetOutputModeSetting(Set::AudioOutputModeTarget target,
Set::AudioOutputMode output_mode) {
LOG_INFO(Service_SET, "called, target={}, output_mode={}", target, output_mode);
R_SUCCEED();
}
Result IAudioController::SetHeadphoneOutputLevelMode(HeadphoneOutputLevelMode output_level_mode) {
LOG_WARNING(Audio, "(STUBBED) called");
R_SUCCEED();
}
Result IAudioController::GetHeadphoneOutputLevelMode(
Out<HeadphoneOutputLevelMode> out_output_level_mode) {
LOG_INFO(Audio, "called");
*out_output_level_mode = HeadphoneOutputLevelMode::Normal;
R_SUCCEED();
}
Result IAudioController::SetSpeakerAutoMuteEnabled(bool is_speaker_auto_mute_enabled) {
LOG_INFO(Audio, "called, is_speaker_auto_mute_enabled={}", is_speaker_auto_mute_enabled);
R_RETURN(m_set_sys->SetSpeakerAutoMuteFlag(is_speaker_auto_mute_enabled));
}
Result IAudioController::IsSpeakerAutoMuteEnabled(Out<bool> out_is_speaker_auto_mute_enabled) {
const auto result = m_set_sys->GetSpeakerAutoMuteFlag(out_is_speaker_auto_mute_enabled);
LOG_INFO(Audio, "called, is_speaker_auto_mute_enabled={}", *out_is_speaker_auto_mute_enabled);
R_RETURN(result);
}
Result IAudioController::AcquireTargetNotification(
OutCopyHandle<Kernel::KReadableEvent> out_notification_event) {
LOG_WARNING(Service_AM, "(STUBBED) called");
*out_notification_event = &notification_event->GetReadableEvent();
R_SUCCEED();
}
} // namespace Service::Audio

View File

@ -0,0 +1,58 @@
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "core/hle/service/cmif_types.h"
#include "core/hle/service/service.h"
#include "core/hle/service/set/settings_types.h"
namespace Core {
class System;
}
namespace Service::Set {
class ISystemSettingsServer;
}
namespace Service::Audio {
class IAudioController final : public ServiceFramework<IAudioController> {
public:
explicit IAudioController(Core::System& system_);
~IAudioController() override;
private:
enum class ForceMutePolicy {
Disable,
SpeakerMuteOnHeadphoneUnplugged,
};
enum class HeadphoneOutputLevelMode {
Normal,
HighPower,
};
Result GetTargetVolumeMin(Out<s32> out_target_min_volume);
Result GetTargetVolumeMax(Out<s32> out_target_max_volume);
Result GetAudioOutputMode(Out<Set::AudioOutputMode> out_output_mode,
Set::AudioOutputModeTarget target);
Result SetAudioOutputMode(Set::AudioOutputModeTarget target, Set::AudioOutputMode output_mode);
Result GetForceMutePolicy(Out<ForceMutePolicy> out_mute_policy);
Result GetOutputModeSetting(Out<Set::AudioOutputMode> out_output_mode,
Set::AudioOutputModeTarget target);
Result SetOutputModeSetting(Set::AudioOutputModeTarget target,
Set::AudioOutputMode output_mode);
Result SetHeadphoneOutputLevelMode(HeadphoneOutputLevelMode output_level_mode);
Result GetHeadphoneOutputLevelMode(Out<HeadphoneOutputLevelMode> out_output_level_mode);
Result SetSpeakerAutoMuteEnabled(bool is_speaker_auto_mute_enabled);
Result IsSpeakerAutoMuteEnabled(Out<bool> out_is_speaker_auto_mute_enabled);
Result AcquireTargetNotification(OutCopyHandle<Kernel::KReadableEvent> out_notification_event);
KernelHelpers::ServiceContext service_context;
Kernel::KEvent* notification_event;
std::shared_ptr<Service::Set::ISystemSettingsServer> m_set_sys;
};
} // namespace Service::Audio

View File

@ -16,7 +16,7 @@ IAlbumAccessorService::IAlbumAccessorService(Core::System& system_,
// clang-format off // clang-format off
static const FunctionInfo functions[] = { static const FunctionInfo functions[] = {
{0, nullptr, "GetAlbumFileCount"}, {0, nullptr, "GetAlbumFileCount"},
{1, nullptr, "GetAlbumFileList"}, {1, C<&IAlbumAccessorService::GetAlbumFileList>, "GetAlbumFileList"},
{2, nullptr, "LoadAlbumFile"}, {2, nullptr, "LoadAlbumFile"},
{3, C<&IAlbumAccessorService::DeleteAlbumFile>, "DeleteAlbumFile"}, {3, C<&IAlbumAccessorService::DeleteAlbumFile>, "DeleteAlbumFile"},
{4, nullptr, "StorageCopyAlbumFile"}, {4, nullptr, "StorageCopyAlbumFile"},
@ -62,6 +62,15 @@ IAlbumAccessorService::IAlbumAccessorService(Core::System& system_,
IAlbumAccessorService::~IAlbumAccessorService() = default; IAlbumAccessorService::~IAlbumAccessorService() = default;
Result IAlbumAccessorService::GetAlbumFileList(
Out<u64> out_count, AlbumStorage storage,
OutArray<AlbumEntry, BufferAttr_HipcMapAlias> out_entries) {
LOG_INFO(Service_Capture, "called, storage={}", storage);
const Result result = manager->GetAlbumFileList(out_entries, *out_count, storage, 0);
R_RETURN(TranslateResult(result));
}
Result IAlbumAccessorService::DeleteAlbumFile(AlbumFileId file_id) { Result IAlbumAccessorService::DeleteAlbumFile(AlbumFileId file_id) {
LOG_INFO(Service_Capture, "called, application_id=0x{:0x}, storage={}, type={}", LOG_INFO(Service_Capture, "called, application_id=0x{:0x}, storage={}, type={}",
file_id.application_id, file_id.storage, file_id.type); file_id.application_id, file_id.storage, file_id.type);

View File

@ -21,6 +21,9 @@ public:
~IAlbumAccessorService() override; ~IAlbumAccessorService() override;
private: private:
Result GetAlbumFileList(Out<u64> out_count, AlbumStorage storage,
OutArray<AlbumEntry, BufferAttr_HipcMapAlias> out_entries);
Result DeleteAlbumFile(AlbumFileId file_id); Result DeleteAlbumFile(AlbumFileId file_id);
Result IsAlbumMounted(Out<bool> out_is_mounted, AlbumStorage storage); Result IsAlbumMounted(Out<bool> out_is_mounted, AlbumStorage storage);

View File

@ -3,6 +3,8 @@
#include <memory> #include <memory>
#include "common/logging/log.h"
#include "core/hle/service/cmif_serialization.h"
#include "core/hle/service/erpt/erpt.h" #include "core/hle/service/erpt/erpt.h"
#include "core/hle/service/server_manager.h" #include "core/hle/service/server_manager.h"
#include "core/hle/service/service.h" #include "core/hle/service/service.h"
@ -15,7 +17,7 @@ public:
explicit ErrorReportContext(Core::System& system_) : ServiceFramework{system_, "erpt:c"} { explicit ErrorReportContext(Core::System& system_) : ServiceFramework{system_, "erpt:c"} {
// clang-format off // clang-format off
static const FunctionInfo functions[] = { static const FunctionInfo functions[] = {
{0, nullptr, "SubmitContext"}, {0, C<&ErrorReportContext::SubmitContext>, "SubmitContext"},
{1, nullptr, "CreateReportV0"}, {1, nullptr, "CreateReportV0"},
{2, nullptr, "SetInitialLaunchSettingsCompletionTime"}, {2, nullptr, "SetInitialLaunchSettingsCompletionTime"},
{3, nullptr, "ClearInitialLaunchSettingsCompletionTime"}, {3, nullptr, "ClearInitialLaunchSettingsCompletionTime"},
@ -36,6 +38,14 @@ public:
RegisterHandlers(functions); RegisterHandlers(functions);
} }
private:
Result SubmitContext(InBuffer<BufferAttr_HipcMapAlias> buffer_a,
InBuffer<BufferAttr_HipcMapAlias> buffer_b) {
LOG_WARNING(Service_SET, "(STUBBED) called, buffer_a_size={}, buffer_b_size={}",
buffer_a.size(), buffer_b.size());
R_SUCCEED();
}
}; };
class ErrorReportSession final : public ServiceFramework<ErrorReportSession> { class ErrorReportSession final : public ServiceFramework<ErrorReportSession> {

View File

@ -336,7 +336,7 @@ FSP_SRV::FSP_SRV(Core::System& system_)
{1012, nullptr, "GetFsStackUsage"}, {1012, nullptr, "GetFsStackUsage"},
{1013, nullptr, "UnsetSaveDataRootPath"}, {1013, nullptr, "UnsetSaveDataRootPath"},
{1014, nullptr, "OutputMultiProgramTagAccessLog"}, {1014, nullptr, "OutputMultiProgramTagAccessLog"},
{1016, nullptr, "FlushAccessLogOnSdCard"}, {1016, &FSP_SRV::FlushAccessLogOnSdCard, "FlushAccessLogOnSdCard"},
{1017, nullptr, "OutputApplicationInfoAccessLog"}, {1017, nullptr, "OutputApplicationInfoAccessLog"},
{1018, nullptr, "SetDebugOption"}, {1018, nullptr, "SetDebugOption"},
{1019, nullptr, "UnsetDebugOption"}, {1019, nullptr, "UnsetDebugOption"},
@ -706,6 +706,13 @@ void FSP_SRV::GetProgramIndexForAccessLog(HLERequestContext& ctx) {
rb.Push(access_log_program_index); rb.Push(access_log_program_index);
} }
void FSP_SRV::FlushAccessLogOnSdCard(HLERequestContext& ctx) {
LOG_DEBUG(Service_FS, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void FSP_SRV::GetCacheStorageSize(HLERequestContext& ctx) { void FSP_SRV::GetCacheStorageSize(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx}; IPC::RequestParser rp{ctx};
const auto index{rp.Pop<s32>()}; const auto index{rp.Pop<s32>()};

View File

@ -58,6 +58,7 @@ private:
void SetGlobalAccessLogMode(HLERequestContext& ctx); void SetGlobalAccessLogMode(HLERequestContext& ctx);
void GetGlobalAccessLogMode(HLERequestContext& ctx); void GetGlobalAccessLogMode(HLERequestContext& ctx);
void OutputAccessLogToSdCard(HLERequestContext& ctx); void OutputAccessLogToSdCard(HLERequestContext& ctx);
void FlushAccessLogOnSdCard(HLERequestContext& ctx);
void GetProgramIndexForAccessLog(HLERequestContext& ctx); void GetProgramIndexForAccessLog(HLERequestContext& ctx);
void OpenMultiCommitManager(HLERequestContext& ctx); void OpenMultiCommitManager(HLERequestContext& ctx);
void GetCacheStorageSize(HLERequestContext& ctx); void GetCacheStorageSize(HLERequestContext& ctx);

View File

@ -186,6 +186,10 @@ TimeManager::TimeManager(Core::System& system)
} }
} }
TimeManager::~TimeManager() {
ResetTimeZoneBinary();
}
Result TimeManager::SetupStandardSteadyClockCore() { Result TimeManager::SetupStandardSteadyClockCore() {
Common::UUID external_clock_source_id{}; Common::UUID external_clock_source_id{};
auto res = m_set_sys->GetExternalSteadyClockSourceId(&external_clock_source_id); auto res = m_set_sys->GetExternalSteadyClockSourceId(&external_clock_source_id);

View File

@ -26,6 +26,7 @@ namespace Service::Glue::Time {
class TimeManager { class TimeManager {
public: public:
explicit TimeManager(Core::System& system); explicit TimeManager(Core::System& system);
~TimeManager();
std::shared_ptr<Service::Set::ISystemSettingsServer> m_set_sys; std::shared_ptr<Service::Set::ISystemSettingsServer> m_set_sys;

View File

@ -0,0 +1,21 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/service/ns/account_proxy_interface.h"
namespace Service::NS {
IAccountProxyInterface::IAccountProxyInterface(Core::System& system_)
: ServiceFramework{system_, "IAccountProxyInterface"} {
// clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "CreateUserAccount"},
};
// clang-format on
RegisterHandlers(functions);
}
IAccountProxyInterface::~IAccountProxyInterface() = default;
} // namespace Service::NS

View File

@ -0,0 +1,16 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "core/hle/service/service.h"
namespace Service::NS {
class IAccountProxyInterface final : public ServiceFramework<IAccountProxyInterface> {
public:
explicit IAccountProxyInterface(Core::System& system_);
~IAccountProxyInterface() override;
};
} // namespace Service::NS

View File

@ -0,0 +1,518 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/file_sys/nca_metadata.h"
#include "core/file_sys/registered_cache.h"
#include "core/hle/service/cmif_serialization.h"
#include "core/hle/service/filesystem/filesystem.h"
#include "core/hle/service/ns/application_manager_interface.h"
#include "core/hle/service/ns/content_management_interface.h"
#include "core/hle/service/ns/read_only_application_control_data_interface.h"
namespace Service::NS {
IApplicationManagerInterface::IApplicationManagerInterface(Core::System& system_)
: ServiceFramework{system_, "IApplicationManagerInterface"},
service_context{system, "IApplicationManagerInterface"},
record_update_system_event{service_context}, sd_card_mount_status_event{service_context},
gamecard_update_detection_event{service_context},
gamecard_mount_status_event{service_context}, gamecard_mount_failure_event{service_context} {
// clang-format off
static const FunctionInfo functions[] = {
{0, D<&IApplicationManagerInterface::ListApplicationRecord>, "ListApplicationRecord"},
{1, nullptr, "GenerateApplicationRecordCount"},
{2, D<&IApplicationManagerInterface::GetApplicationRecordUpdateSystemEvent>, "GetApplicationRecordUpdateSystemEvent"},
{3, nullptr, "GetApplicationViewDeprecated"},
{4, nullptr, "DeleteApplicationEntity"},
{5, nullptr, "DeleteApplicationCompletely"},
{6, nullptr, "IsAnyApplicationEntityRedundant"},
{7, nullptr, "DeleteRedundantApplicationEntity"},
{8, nullptr, "IsApplicationEntityMovable"},
{9, nullptr, "MoveApplicationEntity"},
{11, nullptr, "CalculateApplicationOccupiedSize"},
{16, nullptr, "PushApplicationRecord"},
{17, nullptr, "ListApplicationRecordContentMeta"},
{19, nullptr, "LaunchApplicationOld"},
{21, nullptr, "GetApplicationContentPath"},
{22, nullptr, "TerminateApplication"},
{23, nullptr, "ResolveApplicationContentPath"},
{26, nullptr, "BeginInstallApplication"},
{27, nullptr, "DeleteApplicationRecord"},
{30, nullptr, "RequestApplicationUpdateInfo"},
{31, nullptr, "Unknown31"},
{32, nullptr, "CancelApplicationDownload"},
{33, nullptr, "ResumeApplicationDownload"},
{35, nullptr, "UpdateVersionList"},
{36, nullptr, "PushLaunchVersion"},
{37, nullptr, "ListRequiredVersion"},
{38, D<&IApplicationManagerInterface::CheckApplicationLaunchVersion>, "CheckApplicationLaunchVersion"},
{39, nullptr, "CheckApplicationLaunchRights"},
{40, nullptr, "GetApplicationLogoData"},
{41, nullptr, "CalculateApplicationDownloadRequiredSize"},
{42, nullptr, "CleanupSdCard"},
{43, D<&IApplicationManagerInterface::CheckSdCardMountStatus>, "CheckSdCardMountStatus"},
{44, D<&IApplicationManagerInterface::GetSdCardMountStatusChangedEvent>, "GetSdCardMountStatusChangedEvent"},
{45, nullptr, "GetGameCardAttachmentEvent"},
{46, nullptr, "GetGameCardAttachmentInfo"},
{47, nullptr, "GetTotalSpaceSize"},
{48, D<&IApplicationManagerInterface::GetFreeSpaceSize>, "GetFreeSpaceSize"},
{49, nullptr, "GetSdCardRemovedEvent"},
{52, D<&IApplicationManagerInterface::GetGameCardUpdateDetectionEvent>, "GetGameCardUpdateDetectionEvent"},
{53, nullptr, "DisableApplicationAutoDelete"},
{54, nullptr, "EnableApplicationAutoDelete"},
{55, D<&IApplicationManagerInterface::GetApplicationDesiredLanguage>, "GetApplicationDesiredLanguage"},
{56, nullptr, "SetApplicationTerminateResult"},
{57, nullptr, "ClearApplicationTerminateResult"},
{58, nullptr, "GetLastSdCardMountUnexpectedResult"},
{59, D<&IApplicationManagerInterface::ConvertApplicationLanguageToLanguageCode>, "ConvertApplicationLanguageToLanguageCode"},
{60, nullptr, "ConvertLanguageCodeToApplicationLanguage"},
{61, nullptr, "GetBackgroundDownloadStressTaskInfo"},
{62, nullptr, "GetGameCardStopper"},
{63, nullptr, "IsSystemProgramInstalled"},
{64, nullptr, "StartApplyDeltaTask"},
{65, nullptr, "GetRequestServerStopper"},
{66, nullptr, "GetBackgroundApplyDeltaStressTaskInfo"},
{67, nullptr, "CancelApplicationApplyDelta"},
{68, nullptr, "ResumeApplicationApplyDelta"},
{69, nullptr, "CalculateApplicationApplyDeltaRequiredSize"},
{70, D<&IApplicationManagerInterface::ResumeAll>, "ResumeAll"},
{71, D<&IApplicationManagerInterface::GetStorageSize>, "GetStorageSize"},
{80, nullptr, "RequestDownloadApplication"},
{81, nullptr, "RequestDownloadAddOnContent"},
{82, nullptr, "DownloadApplication"},
{83, nullptr, "CheckApplicationResumeRights"},
{84, nullptr, "GetDynamicCommitEvent"},
{85, nullptr, "RequestUpdateApplication2"},
{86, nullptr, "EnableApplicationCrashReport"},
{87, nullptr, "IsApplicationCrashReportEnabled"},
{90, nullptr, "BoostSystemMemoryResourceLimit"},
{91, nullptr, "DeprecatedLaunchApplication"},
{92, nullptr, "GetRunningApplicationProgramId"},
{93, nullptr, "GetMainApplicationProgramIndex"},
{94, nullptr, "LaunchApplication"},
{95, nullptr, "GetApplicationLaunchInfo"},
{96, nullptr, "AcquireApplicationLaunchInfo"},
{97, nullptr, "GetMainApplicationProgramIndexByApplicationLaunchInfo"},
{98, nullptr, "EnableApplicationAllThreadDumpOnCrash"},
{99, nullptr, "LaunchDevMenu"},
{100, nullptr, "ResetToFactorySettings"},
{101, nullptr, "ResetToFactorySettingsWithoutUserSaveData"},
{102, nullptr, "ResetToFactorySettingsForRefurbishment"},
{103, nullptr, "ResetToFactorySettingsWithPlatformRegion"},
{104, nullptr, "ResetToFactorySettingsWithPlatformRegionAuthentication"},
{105, nullptr, "RequestResetToFactorySettingsSecurely"},
{106, nullptr, "RequestResetToFactorySettingsWithPlatformRegionAuthenticationSecurely"},
{200, nullptr, "CalculateUserSaveDataStatistics"},
{201, nullptr, "DeleteUserSaveDataAll"},
{210, nullptr, "DeleteUserSystemSaveData"},
{211, nullptr, "DeleteSaveData"},
{220, nullptr, "UnregisterNetworkServiceAccount"},
{221, nullptr, "UnregisterNetworkServiceAccountWithUserSaveDataDeletion"},
{300, nullptr, "GetApplicationShellEvent"},
{301, nullptr, "PopApplicationShellEventInfo"},
{302, nullptr, "LaunchLibraryApplet"},
{303, nullptr, "TerminateLibraryApplet"},
{304, nullptr, "LaunchSystemApplet"},
{305, nullptr, "TerminateSystemApplet"},
{306, nullptr, "LaunchOverlayApplet"},
{307, nullptr, "TerminateOverlayApplet"},
{400, D<&IApplicationManagerInterface::GetApplicationControlData>, "GetApplicationControlData"},
{401, nullptr, "InvalidateAllApplicationControlCache"},
{402, nullptr, "RequestDownloadApplicationControlData"},
{403, nullptr, "GetMaxApplicationControlCacheCount"},
{404, nullptr, "InvalidateApplicationControlCache"},
{405, nullptr, "ListApplicationControlCacheEntryInfo"},
{406, nullptr, "GetApplicationControlProperty"},
{407, nullptr, "ListApplicationTitle"},
{408, nullptr, "ListApplicationIcon"},
{502, nullptr, "RequestCheckGameCardRegistration"},
{503, nullptr, "RequestGameCardRegistrationGoldPoint"},
{504, nullptr, "RequestRegisterGameCard"},
{505, D<&IApplicationManagerInterface::GetGameCardMountFailureEvent>, "GetGameCardMountFailureEvent"},
{506, nullptr, "IsGameCardInserted"},
{507, nullptr, "EnsureGameCardAccess"},
{508, nullptr, "GetLastGameCardMountFailureResult"},
{509, nullptr, "ListApplicationIdOnGameCard"},
{510, nullptr, "GetGameCardPlatformRegion"},
{600, nullptr, "CountApplicationContentMeta"},
{601, nullptr, "ListApplicationContentMetaStatus"},
{602, nullptr, "ListAvailableAddOnContent"},
{603, nullptr, "GetOwnedApplicationContentMetaStatus"},
{604, nullptr, "RegisterContentsExternalKey"},
{605, nullptr, "ListApplicationContentMetaStatusWithRightsCheck"},
{606, nullptr, "GetContentMetaStorage"},
{607, nullptr, "ListAvailableAddOnContent"},
{609, nullptr, "ListAvailabilityAssuredAddOnContent"},
{610, nullptr, "GetInstalledContentMetaStorage"},
{611, nullptr, "PrepareAddOnContent"},
{700, nullptr, "PushDownloadTaskList"},
{701, nullptr, "ClearTaskStatusList"},
{702, nullptr, "RequestDownloadTaskList"},
{703, nullptr, "RequestEnsureDownloadTask"},
{704, nullptr, "ListDownloadTaskStatus"},
{705, nullptr, "RequestDownloadTaskListData"},
{800, nullptr, "RequestVersionList"},
{801, nullptr, "ListVersionList"},
{802, nullptr, "RequestVersionListData"},
{900, nullptr, "GetApplicationRecord"},
{901, nullptr, "GetApplicationRecordProperty"},
{902, nullptr, "EnableApplicationAutoUpdate"},
{903, nullptr, "DisableApplicationAutoUpdate"},
{904, nullptr, "TouchApplication"},
{905, nullptr, "RequestApplicationUpdate"},
{906, D<&IApplicationManagerInterface::IsApplicationUpdateRequested>, "IsApplicationUpdateRequested"},
{907, nullptr, "WithdrawApplicationUpdateRequest"},
{908, nullptr, "ListApplicationRecordInstalledContentMeta"},
{909, nullptr, "WithdrawCleanupAddOnContentsWithNoRightsRecommendation"},
{910, nullptr, "HasApplicationRecord"},
{911, nullptr, "SetPreInstalledApplication"},
{912, nullptr, "ClearPreInstalledApplicationFlag"},
{913, nullptr, "ListAllApplicationRecord"},
{914, nullptr, "HideApplicationRecord"},
{915, nullptr, "ShowApplicationRecord"},
{916, nullptr, "IsApplicationAutoDeleteDisabled"},
{1000, nullptr, "RequestVerifyApplicationDeprecated"},
{1001, nullptr, "CorruptApplicationForDebug"},
{1002, nullptr, "RequestVerifyAddOnContentsRights"},
{1003, nullptr, "RequestVerifyApplication"},
{1004, nullptr, "CorruptContentForDebug"},
{1200, nullptr, "NeedsUpdateVulnerability"},
{1300, D<&IApplicationManagerInterface::IsAnyApplicationEntityInstalled>, "IsAnyApplicationEntityInstalled"},
{1301, nullptr, "DeleteApplicationContentEntities"},
{1302, nullptr, "CleanupUnrecordedApplicationEntity"},
{1303, nullptr, "CleanupAddOnContentsWithNoRights"},
{1304, nullptr, "DeleteApplicationContentEntity"},
{1305, nullptr, "TryDeleteRunningApplicationEntity"},
{1306, nullptr, "TryDeleteRunningApplicationCompletely"},
{1307, nullptr, "TryDeleteRunningApplicationContentEntities"},
{1308, nullptr, "DeleteApplicationCompletelyForDebug"},
{1309, nullptr, "CleanupUnavailableAddOnContents"},
{1310, nullptr, "RequestMoveApplicationEntity"},
{1311, nullptr, "EstimateSizeToMove"},
{1312, nullptr, "HasMovableEntity"},
{1313, nullptr, "CleanupOrphanContents"},
{1314, nullptr, "CheckPreconditionSatisfiedToMove"},
{1400, nullptr, "PrepareShutdown"},
{1500, nullptr, "FormatSdCard"},
{1501, nullptr, "NeedsSystemUpdateToFormatSdCard"},
{1502, nullptr, "GetLastSdCardFormatUnexpectedResult"},
{1504, nullptr, "InsertSdCard"},
{1505, nullptr, "RemoveSdCard"},
{1506, nullptr, "GetSdCardStartupStatus"},
{1600, nullptr, "GetSystemSeedForPseudoDeviceId"},
{1601, nullptr, "ResetSystemSeedForPseudoDeviceId"},
{1700, nullptr, "ListApplicationDownloadingContentMeta"},
{1701, D<&IApplicationManagerInterface::GetApplicationView>, "GetApplicationView"},
{1702, nullptr, "GetApplicationDownloadTaskStatus"},
{1703, nullptr, "GetApplicationViewDownloadErrorContext"},
{1704, D<&IApplicationManagerInterface::GetApplicationViewWithPromotionInfo>, "GetApplicationViewWithPromotionInfo"},
{1705, nullptr, "IsPatchAutoDeletableApplication"},
{1800, nullptr, "IsNotificationSetupCompleted"},
{1801, nullptr, "GetLastNotificationInfoCount"},
{1802, nullptr, "ListLastNotificationInfo"},
{1803, nullptr, "ListNotificationTask"},
{1900, nullptr, "IsActiveAccount"},
{1901, nullptr, "RequestDownloadApplicationPrepurchasedRights"},
{1902, nullptr, "GetApplicationTicketInfo"},
{1903, nullptr, "RequestDownloadApplicationPrepurchasedRightsForAccount"},
{2000, nullptr, "GetSystemDeliveryInfo"},
{2001, nullptr, "SelectLatestSystemDeliveryInfo"},
{2002, nullptr, "VerifyDeliveryProtocolVersion"},
{2003, nullptr, "GetApplicationDeliveryInfo"},
{2004, nullptr, "HasAllContentsToDeliver"},
{2005, nullptr, "CompareApplicationDeliveryInfo"},
{2006, nullptr, "CanDeliverApplication"},
{2007, nullptr, "ListContentMetaKeyToDeliverApplication"},
{2008, nullptr, "NeedsSystemUpdateToDeliverApplication"},
{2009, nullptr, "EstimateRequiredSize"},
{2010, nullptr, "RequestReceiveApplication"},
{2011, nullptr, "CommitReceiveApplication"},
{2012, nullptr, "GetReceiveApplicationProgress"},
{2013, nullptr, "RequestSendApplication"},
{2014, nullptr, "GetSendApplicationProgress"},
{2015, nullptr, "CompareSystemDeliveryInfo"},
{2016, nullptr, "ListNotCommittedContentMeta"},
{2017, nullptr, "CreateDownloadTask"},
{2018, nullptr, "GetApplicationDeliveryInfoHash"},
{2050, D<&IApplicationManagerInterface::GetApplicationRightsOnClient>, "GetApplicationRightsOnClient"},
{2051, nullptr, "InvalidateRightsIdCache"},
{2100, D<&IApplicationManagerInterface::GetApplicationTerminateResult>, "GetApplicationTerminateResult"},
{2101, nullptr, "GetRawApplicationTerminateResult"},
{2150, nullptr, "CreateRightsEnvironment"},
{2151, nullptr, "DestroyRightsEnvironment"},
{2152, nullptr, "ActivateRightsEnvironment"},
{2153, nullptr, "DeactivateRightsEnvironment"},
{2154, nullptr, "ForceActivateRightsContextForExit"},
{2155, nullptr, "UpdateRightsEnvironmentStatus"},
{2156, nullptr, "CreateRightsEnvironmentForMicroApplication"},
{2160, nullptr, "AddTargetApplicationToRightsEnvironment"},
{2161, nullptr, "SetUsersToRightsEnvironment"},
{2170, nullptr, "GetRightsEnvironmentStatus"},
{2171, nullptr, "GetRightsEnvironmentStatusChangedEvent"},
{2180, nullptr, "RequestExtendRightsInRightsEnvironment"},
{2181, nullptr, "GetResultOfExtendRightsInRightsEnvironment"},
{2182, nullptr, "SetActiveRightsContextUsingStateToRightsEnvironment"},
{2190, nullptr, "GetRightsEnvironmentHandleForApplication"},
{2199, nullptr, "GetRightsEnvironmentCountForDebug"},
{2200, nullptr, "GetGameCardApplicationCopyIdentifier"},
{2201, nullptr, "GetInstalledApplicationCopyIdentifier"},
{2250, nullptr, "RequestReportActiveELicence"},
{2300, nullptr, "ListEventLog"},
{2350, nullptr, "PerformAutoUpdateByApplicationId"},
{2351, nullptr, "RequestNoDownloadRightsErrorResolution"},
{2352, nullptr, "RequestResolveNoDownloadRightsError"},
{2353, nullptr, "GetApplicationDownloadTaskInfo"},
{2354, nullptr, "PrioritizeApplicationBackgroundTask"},
{2355, nullptr, "PreferStorageEfficientUpdate"},
{2356, nullptr, "RequestStorageEfficientUpdatePreferable"},
{2357, nullptr, "EnableMultiCoreDownload"},
{2358, nullptr, "DisableMultiCoreDownload"},
{2359, nullptr, "IsMultiCoreDownloadEnabled"},
{2400, nullptr, "GetPromotionInfo"},
{2401, nullptr, "CountPromotionInfo"},
{2402, nullptr, "ListPromotionInfo"},
{2403, nullptr, "ImportPromotionJsonForDebug"},
{2404, nullptr, "ClearPromotionInfoForDebug"},
{2500, nullptr, "ConfirmAvailableTime"},
{2510, nullptr, "CreateApplicationResource"},
{2511, nullptr, "GetApplicationResource"},
{2513, nullptr, "LaunchMicroApplication"},
{2514, nullptr, "ClearTaskOfAsyncTaskManager"},
{2515, nullptr, "CleanupAllPlaceHolderAndFragmentsIfNoTask"},
{2516, nullptr, "EnsureApplicationCertificate"},
{2517, nullptr, "CreateApplicationInstance"},
{2518, nullptr, "UpdateQualificationForDebug"},
{2519, nullptr, "IsQualificationTransitionSupported"},
{2520, nullptr, "IsQualificationTransitionSupportedByProcessId"},
{2521, nullptr, "GetRightsUserChangedEvent"},
{2522, nullptr, "IsRomRedirectionAvailable"},
{2800, nullptr, "GetApplicationIdOfPreomia"},
{3000, nullptr, "RegisterDeviceLockKey"},
{3001, nullptr, "UnregisterDeviceLockKey"},
{3002, nullptr, "VerifyDeviceLockKey"},
{3003, nullptr, "HideApplicationIcon"},
{3004, nullptr, "ShowApplicationIcon"},
{3005, nullptr, "HideApplicationTitle"},
{3006, nullptr, "ShowApplicationTitle"},
{3007, nullptr, "EnableGameCard"},
{3008, nullptr, "DisableGameCard"},
{3009, nullptr, "EnableLocalContentShare"},
{3010, nullptr, "DisableLocalContentShare"},
{3011, nullptr, "IsApplicationIconHidden"},
{3012, nullptr, "IsApplicationTitleHidden"},
{3013, nullptr, "IsGameCardEnabled"},
{3014, nullptr, "IsLocalContentShareEnabled"},
{3050, nullptr, "ListAssignELicenseTaskResult"},
{9999, nullptr, "GetApplicationCertificate"},
};
// clang-format on
RegisterHandlers(functions);
}
IApplicationManagerInterface::~IApplicationManagerInterface() = default;
Result IApplicationManagerInterface::GetApplicationControlData(
OutBuffer<BufferAttr_HipcMapAlias> out_buffer, Out<u32> out_actual_size,
ApplicationControlSource application_control_source, u64 application_id) {
LOG_DEBUG(Service_NS, "called");
R_RETURN(IReadOnlyApplicationControlDataInterface(system).GetApplicationControlData(
out_buffer, out_actual_size, application_control_source, application_id));
}
Result IApplicationManagerInterface::GetApplicationDesiredLanguage(
Out<ApplicationLanguage> out_desired_language, u32 supported_languages) {
LOG_DEBUG(Service_NS, "called");
R_RETURN(IReadOnlyApplicationControlDataInterface(system).GetApplicationDesiredLanguage(
out_desired_language, supported_languages));
}
Result IApplicationManagerInterface::ConvertApplicationLanguageToLanguageCode(
Out<u64> out_language_code, ApplicationLanguage application_language) {
LOG_DEBUG(Service_NS, "called");
R_RETURN(
IReadOnlyApplicationControlDataInterface(system).ConvertApplicationLanguageToLanguageCode(
out_language_code, application_language));
}
Result IApplicationManagerInterface::ListApplicationRecord(
OutArray<ApplicationRecord, BufferAttr_HipcMapAlias> out_records, Out<s32> out_count,
s32 offset) {
const auto limit = out_records.size();
LOG_WARNING(Service_NS, "(STUBBED) called");
const auto& cache = system.GetContentProviderUnion();
const auto installed_games = cache.ListEntriesFilterOrigin(
std::nullopt, FileSys::TitleType::Application, FileSys::ContentRecordType::Program);
size_t i = 0;
u8 ii = 24;
for (const auto& [slot, game] : installed_games) {
if (i >= limit) {
break;
}
if (game.title_id == 0 || game.title_id < 0x0100000000001FFFull) {
continue;
}
if (offset > 0) {
offset--;
continue;
}
ApplicationRecord record{};
record.application_id = game.title_id;
record.type = ApplicationRecordType::Installed;
record.unknown = 0; // 2 = needs update
record.unknown2 = ii++;
out_records[i++] = record;
}
*out_count = static_cast<s32>(i);
R_SUCCEED();
}
Result IApplicationManagerInterface::GetApplicationRecordUpdateSystemEvent(
OutCopyHandle<Kernel::KReadableEvent> out_event) {
LOG_WARNING(Service_NS, "(STUBBED) called");
record_update_system_event.Signal();
*out_event = record_update_system_event.GetHandle();
R_SUCCEED();
}
Result IApplicationManagerInterface::GetGameCardMountFailureEvent(
OutCopyHandle<Kernel::KReadableEvent> out_event) {
LOG_WARNING(Service_NS, "(STUBBED) called");
*out_event = gamecard_mount_failure_event.GetHandle();
R_SUCCEED();
}
Result IApplicationManagerInterface::IsAnyApplicationEntityInstalled(
Out<bool> out_is_any_application_entity_installed) {
LOG_WARNING(Service_NS, "(STUBBED) called");
*out_is_any_application_entity_installed = true;
R_SUCCEED();
}
Result IApplicationManagerInterface::GetApplicationView(
OutArray<ApplicationView, BufferAttr_HipcMapAlias> out_application_views,
InArray<u64, BufferAttr_HipcMapAlias> application_ids) {
const auto size = std::min(out_application_views.size(), application_ids.size());
LOG_WARNING(Service_NS, "(STUBBED) called, size={}", application_ids.size());
for (size_t i = 0; i < size; i++) {
ApplicationView view{};
view.application_id = application_ids[i];
view.unk = 0x70000;
view.flags = 0x401f17;
out_application_views[i] = view;
}
R_SUCCEED();
}
Result IApplicationManagerInterface::GetApplicationViewWithPromotionInfo(
OutArray<ApplicationViewWithPromotionInfo, BufferAttr_HipcMapAlias> out_application_views,
InArray<u64, BufferAttr_HipcMapAlias> application_ids) {
const auto size = std::min(out_application_views.size(), application_ids.size());
LOG_WARNING(Service_NS, "(STUBBED) called, size={}", application_ids.size());
for (size_t i = 0; i < size; i++) {
ApplicationViewWithPromotionInfo view{};
view.view.application_id = application_ids[i];
view.view.unk = 0x70000;
view.view.flags = 0x401f17;
view.promotion = {};
out_application_views[i] = view;
}
R_SUCCEED();
}
Result IApplicationManagerInterface::GetApplicationRightsOnClient(
OutArray<ApplicationRightsOnClient, BufferAttr_HipcMapAlias> out_rights, Out<u32> out_count,
Common::UUID account_id, u32 flags, u64 application_id) {
LOG_WARNING(Service_NS, "(STUBBED) called, flags={}, application_id={:016X}, account_id={}",
flags, application_id, account_id.FormattedString());
if (!out_rights.empty()) {
out_rights[0] = {
.application_id = application_id,
.uid = account_id,
.flags = 0,
.flags2 = 0,
};
*out_count = 1;
} else {
*out_count = 0;
}
R_SUCCEED();
}
Result IApplicationManagerInterface::CheckSdCardMountStatus() {
LOG_DEBUG(Service_NS, "called");
R_RETURN(IContentManagementInterface(system).CheckSdCardMountStatus());
}
Result IApplicationManagerInterface::GetSdCardMountStatusChangedEvent(
OutCopyHandle<Kernel::KReadableEvent> out_event) {
LOG_WARNING(Service_NS, "(STUBBED) called");
*out_event = sd_card_mount_status_event.GetHandle();
R_SUCCEED();
}
Result IApplicationManagerInterface::GetFreeSpaceSize(Out<s64> out_free_space_size,
FileSys::StorageId storage_id) {
LOG_DEBUG(Service_NS, "called");
R_RETURN(IContentManagementInterface(system).GetFreeSpaceSize(out_free_space_size, storage_id));
}
Result IApplicationManagerInterface::GetGameCardUpdateDetectionEvent(
OutCopyHandle<Kernel::KReadableEvent> out_event) {
LOG_WARNING(Service_NS, "(STUBBED) called");
*out_event = gamecard_update_detection_event.GetHandle();
R_SUCCEED();
}
Result IApplicationManagerInterface::ResumeAll() {
LOG_WARNING(Service_NS, "(STUBBED) called");
R_SUCCEED();
}
Result IApplicationManagerInterface::GetStorageSize(Out<s64> out_total_space_size,
Out<s64> out_free_space_size,
FileSys::StorageId storage_id) {
LOG_INFO(Service_NS, "called, storage_id={}", storage_id);
*out_total_space_size = system.GetFileSystemController().GetTotalSpaceSize(storage_id);
*out_free_space_size = system.GetFileSystemController().GetFreeSpaceSize(storage_id);
R_SUCCEED();
}
Result IApplicationManagerInterface::IsApplicationUpdateRequested(Out<bool> out_update_required,
Out<u32> out_update_version,
u64 application_id) {
LOG_WARNING(Service_NS, "(STUBBED) called. application_id={:016X}", application_id);
*out_update_required = false;
*out_update_version = 0;
R_SUCCEED();
}
Result IApplicationManagerInterface::CheckApplicationLaunchVersion(u64 application_id) {
LOG_WARNING(Service_NS, "(STUBBED) called. application_id={:016X}", application_id);
R_SUCCEED();
}
Result IApplicationManagerInterface::GetApplicationTerminateResult(Out<Result> out_result,
u64 application_id) {
LOG_WARNING(Service_NS, "(STUBBED) called. application_id={:016X}", application_id);
*out_result = ResultSuccess;
R_SUCCEED();
}
} // namespace Service::NS

View File

@ -0,0 +1,62 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "core/hle/service/cmif_types.h"
#include "core/hle/service/ns/language.h"
#include "core/hle/service/ns/ns_types.h"
#include "core/hle/service/os/event.h"
#include "core/hle/service/service.h"
namespace Service::NS {
class IApplicationManagerInterface final : public ServiceFramework<IApplicationManagerInterface> {
public:
explicit IApplicationManagerInterface(Core::System& system_);
~IApplicationManagerInterface() override;
Result GetApplicationControlData(OutBuffer<BufferAttr_HipcMapAlias> out_buffer,
Out<u32> out_actual_size,
ApplicationControlSource application_control_source,
u64 application_id);
Result GetApplicationDesiredLanguage(Out<ApplicationLanguage> out_desired_language,
u32 supported_languages);
Result ConvertApplicationLanguageToLanguageCode(Out<u64> out_language_code,
ApplicationLanguage application_language);
Result ListApplicationRecord(OutArray<ApplicationRecord, BufferAttr_HipcMapAlias> out_records,
Out<s32> out_count, s32 offset);
Result GetApplicationRecordUpdateSystemEvent(OutCopyHandle<Kernel::KReadableEvent> out_event);
Result GetGameCardMountFailureEvent(OutCopyHandle<Kernel::KReadableEvent> out_event);
Result IsAnyApplicationEntityInstalled(Out<bool> out_is_any_application_entity_installed);
Result GetApplicationView(
OutArray<ApplicationView, BufferAttr_HipcMapAlias> out_application_views,
InArray<u64, BufferAttr_HipcMapAlias> application_ids);
Result GetApplicationViewWithPromotionInfo(
OutArray<ApplicationViewWithPromotionInfo, BufferAttr_HipcMapAlias> out_application_views,
InArray<u64, BufferAttr_HipcMapAlias> application_ids);
Result GetApplicationRightsOnClient(
OutArray<ApplicationRightsOnClient, BufferAttr_HipcMapAlias> out_rights, Out<u32> out_count,
Common::UUID account_id, u32 flags, u64 application_id);
Result CheckSdCardMountStatus();
Result GetSdCardMountStatusChangedEvent(OutCopyHandle<Kernel::KReadableEvent> out_event);
Result GetFreeSpaceSize(Out<s64> out_free_space_size, FileSys::StorageId storage_id);
Result GetGameCardUpdateDetectionEvent(OutCopyHandle<Kernel::KReadableEvent> out_event);
Result ResumeAll();
Result GetStorageSize(Out<s64> out_total_space_size, Out<s64> out_free_space_size,
FileSys::StorageId storage_id);
Result IsApplicationUpdateRequested(Out<bool> out_update_required, Out<u32> out_update_version,
u64 application_id);
Result CheckApplicationLaunchVersion(u64 application_id);
Result GetApplicationTerminateResult(Out<Result> out_result, u64 application_id);
private:
KernelHelpers::ServiceContext service_context;
Event record_update_system_event;
Event sd_card_mount_status_event;
Event gamecard_update_detection_event;
Event gamecard_mount_status_event;
Event gamecard_mount_failure_event;
};
} // namespace Service::NS

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