Compare commits

..

123 Commits

Author SHA1 Message Date
f44933930d Android 260 2024-02-22 00:57:08 +00:00
e96826419b Merge yuzu-emu#13096 2024-02-22 00:57:08 +00:00
3f232161be Merge yuzu-emu#13075 2024-02-22 00:57:08 +00:00
98eb3df15c Merge yuzu-emu#13000 2024-02-22 00:57:08 +00:00
74ba9dde58 Merge yuzu-emu#12982 2024-02-22 00:57:07 +00:00
f3f63aa1e7 Merge yuzu-emu#12749 2024-02-22 00:57:07 +00:00
1bea1ee418 Merge yuzu-emu#12461 2024-02-22 00:57:07 +00:00
8bbc209950 Merge pull request #13105 from t895/connection-fix
android: Misc controller fixes
2024-02-21 10:43:46 -05:00
9e1a67b950 fs: add missing mutex header for member (#13106) 2024-02-21 16:43:05 +01:00
de5422b1fd android: Connect controllers with supported styles
If you tried to connect a controller that was previously configured with an unsupported style for your game, when you try to connect that controller, it will immediately disconnect. This ensures that the controller that is being connected will be changed to the first supported style index before being connected.
2024-02-21 08:37:55 -05:00
45f450fca5 android: Add additional check for hasMapping
Controls can have no mapping if they are either "[empty]" or and empty string. This was causing an issue if you reset mapping on all controllers and then tried to play a game. The check to determine whether auto mapping was required would fail and leave you will no mapped controllers. This feels a bit like user error but it smooths things out if you forget so I see it as necessary.
2024-02-21 08:17:30 -05:00
9a3fd76b25 android: Enable all controller styles on emulation shutdown 2024-02-21 08:13:54 -05:00
60fc6df407 Merge pull request #13099 from t895/default-fix
android: Fix extra stick setting default values
2024-02-21 07:02:58 -05:00
de2d496e71 android: Fix extra stick setting default values
The default value was accidentally hardcoded for all extra stick settings
2024-02-20 22:13:59 -05:00
7b5bdd076d Merge pull request #13095 from liamwhite/ns-oops
ns: fix alignment of uid type
2024-02-20 21:19:35 -05:00
e0c17a2113 Merge pull request #10529 from liamwhite/critical-spacing
caches: make critical reclamation less eager and possible in more cases
2024-02-20 23:19:27 -03:00
b107435a3f ns: fix alignment of uid type 2024-02-20 18:43:44 -05:00
4e1fcd4a63 Merge pull request #13091 from t895/device-renaming
android: Expose device name setting
2024-02-20 18:30:54 -05:00
ea4a96b45e Merge pull request #13079 from liamwhite/vi3
vi: misc fixes
2024-02-20 18:30:47 -05:00
6a90db8c19 android: Expose device name setting 2024-02-20 08:16:38 -05:00
0e5972b0b5 android: Add StringInputSetting settings item 2024-02-20 08:06:56 -05:00
5f7608a7c6 vi: ignore shared buffer destruction failure on termination 2024-02-20 00:02:56 -05:00
668ff0db3a vi: remove superfluous locking in shared buffer manager 2024-02-19 23:59:35 -05:00
9f159dd62c nvnflinger/vi: don't recreate buffer queue on open/close 2024-02-19 23:59:35 -05:00
d1eaeeed8c Import keys from filesystem. (#13056)
* Import keys, re-initialize KeyManager, re-scan vfs, re-populate game list.

* <.< spelling.

* Update based on feedback on #13047 and this PR

* Based on feedback: Don't delete existing files. There's legitimate reasons that someone may want to keep their retail keys and title key handling is resilient to mismatches.

* Update src/yuzu/main.cpp

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

* Remove translation of literal filename/filter format.

* clang-format.

---------

Co-authored-by: Tobias <thm.frey@gmail.com>
2024-02-19 19:18:13 -05:00
10e27a2902 Merge pull request #13086 from t895/clear-button-fix
android: Fix broken clear button check
2024-02-19 19:18:05 -05:00
f567a41f53 android: Have input overlay follow player 1 style index (#13085) 2024-02-19 22:47:21 +01:00
704c62ca01 android: Fix broken clear button check 2024-02-19 15:54:52 -05:00
8d5473e67c Merge pull request #13031 from german77/btm-interfcae
service: btm: Migrate service to new IPC
2024-02-19 14:49:42 -05:00
3b1b98c645 android: Fix overlay visibility reset (#13083) 2024-02-19 19:44:42 +01:00
daf350f5d3 android: Show done button when configuring input overlay (#13082) 2024-02-19 19:26:18 +01:00
c9ef2e26ca Merge pull request #13080 from FearlessTobi/scope-exit
scope_exit: Make constexpr
2024-02-19 10:50:45 -05:00
310c1f50be scope_exit: Make constexpr
Allows the use of the macro in constexpr-contexts.
Also avoids some potential problems when nesting braces inside it.
2024-02-19 16:00:46 +01:00
665fce871f core/CMakeLists: Sort alphabetically 2024-02-19 15:51:02 +01:00
58c7e846cb Merge pull request #13006 from liamwhite/a-hat-in-vram
buffer_cache: use mapped range with large vertex buffer size
2024-02-18 23:37:49 -06:00
8b0fb98a11 Merge pull request #13026 from liamwhite/scale-this-mf
shader_recompiler: fix non-const offset for arrayed image types
2024-02-18 23:37:25 -06:00
8615509c40 Merge pull request #13035 from liamwhite/vi2
vi: manage resources independently of nvnflinger and refactor
2024-02-18 23:36:53 -06:00
d0af52f28e Merge pull request #13048 from liamwhite/new-shell
ns: rewrite for new IPC
2024-02-18 23:36:29 -06:00
ef89b79d7e Merge pull request #13070 from liamwhite/offset
am: account for offset in transfer memory storage
2024-02-18 19:03:56 -05:00
3e41f9a673 Merge pull request #13030 from german77/audio-controller
service: audio: Rewrite IAudioController to new IPC
2024-02-18 19:03:49 -05:00
d45a12826c ns: address review comments 2024-02-18 19:02:00 -05:00
911ee8fd1f am: account for offset in transfer memory storage 2024-02-18 14:56:48 -05: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
940a71422e nvnflinger: check for layers before compose 2024-02-18 11:25:52 -05: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
4cdf18095d ns: rewrite IQueryService 2024-02-18 10:35:39 -05:00
2d43831d1f ns: rewrite IServiceGetterInterface 2024-02-18 10:35:39 -05:00
2e96921f9c ns: rewrite IApplicationManagerInterface 2024-02-18 10:35:39 -05:00
cf0de18982 ns: move IDevelopInterface 2024-02-18 10:35:37 -05:00
ae83ee28a3 ns: rewrite ISystemUpdateInterface 2024-02-18 10:32:21 -05:00
306ed4984b ns: move ISystemUpdateControl 2024-02-18 10:32:21 -05:00
626f2e65b1 ns: rewrite IVulnerabilityManagerInterface 2024-02-18 10:32:21 -05:00
2eded86b4b ns: rewrite IReadOnlyApplicationControlDataInterface 2024-02-18 10:32:21 -05:00
786fc512e2 ns: rewrite IReadOnlyApplicationRecordInterface 2024-02-18 10:32:21 -05:00
c31ac45332 ns: add IDynamicRightsInterface 2024-02-18 10:32:21 -05:00
db172ba249 ns: rewrite IDownloadTaskInterface 2024-02-18 10:32:21 -05:00
bb59940b03 ns: rewrite IDocumentInterface 2024-02-18 10:32:21 -05:00
04887953ff ns: rewrite IContentManagementInterface 2024-02-18 10:32:21 -05:00
8ea72cc99d ns: move IFactoryResetInterface 2024-02-18 10:32:21 -05:00
44d2e90217 ns: move IECommerceInterface 2024-02-18 10:32:21 -05:00
12926eb5db ns: move IApplicationVersionInterface 2024-02-18 10:32:21 -05:00
ae114d2fa1 ns: move IAccountProxyInterface 2024-02-18 10:32:21 -05:00
270d07be2f ns: rewrite IPlatformServiceManager 2024-02-18 10:32:21 -05:00
947cdbe4b1 ns: rename results header 2024-02-18 10:32:21 -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
a07f0883b9 service: vi: Implement ListDisplayMode 2024-02-17 18:08:41 -05:00
812f23d05c vi: manage resources independently of nvnflinger and refactor 2024-02-17 18:08:38 -05:00
dcce9837d2 vi: move shared buffer management from nvnflinger 2024-02-17 18:01:41 -05:00
ee8eccc5fa nvnflinger: convert to process 2024-02-17 18:01:41 -05:00
7b79cddacd am: unify display layer management 2024-02-17 18:00:28 -05: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
4cbafc1ef6 service: audio: Rewrite IAudioController to new IPC 2024-02-17 15:05:13 -06: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
110969e207 service: btm: Implement function needed by QLaunch 2024-02-17 12:39:36 -06: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
9c0724b270 service: btm: Migrate service to new IPC 2024-02-16 12:15:06 -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
462ea921e3 shader_recompiler: fix non-const offset for arrayed image types 2024-02-15 18:49:23 -05:00
cb29aa0473 Revert "shader_recompiler: use only ConstOffset for OpImageFetch"
This reverts commit f296a9ce9a.
2024-02-15 18:38:56 -05:00
af42482565 kernel: add and enable system suspend type 2024-02-14 17:03:50 -05:00
3067bfd126 buffer_cache: use mapped range with large vertex buffer size 2024-02-13 08:27:33 -05:00
368bf2211f texture_cache: tweak iteration tracking change 2024-02-11 13:41:13 -05:00
de8a623932 texture_cache: avoid overestimation of ASTC texture sizes 2024-02-11 13:41:13 -05:00
865a0186b6 caches: make critical reclamation less eager and possible in more cases 2024-02-11 13:41:13 -05:00
301 changed files with 7267 additions and 5219 deletions

View File

@ -1,13 +1,11 @@
| Pull Request | Commit | Title | Author | Merged? | | Pull Request | Commit | Title | Author | Merged? |
|----|----|----|----|----| |----|----|----|----|----|
| [10529](https://github.com/yuzu-emu/yuzu//pull/10529) | [`368bf2211`](https://github.com/yuzu-emu/yuzu//pull/10529/files) | caches: make critical reclamation less eager and possible in more cases | [liamwhite](https://github.com/liamwhite/) | Yes | | [12461](https://github.com/yuzu-emu/yuzu//pull/12461) | [`2831f5dc6`](https://github.com/yuzu-emu/yuzu//pull/12461/files) | Rework Nvdec and VIC to fix out-of-order videos, and speed up decoding. | [Kelebek1](https://github.com/Kelebek1/) | Yes |
| [12461](https://github.com/yuzu-emu/yuzu//pull/12461) | [`acc26667b`](https://github.com/yuzu-emu/yuzu//pull/12461/files) | Rework Nvdec and VIC to fix out-of-order videos, and speed up decoding. | [Kelebek1](https://github.com/Kelebek1/) | Yes |
| [12749](https://github.com/yuzu-emu/yuzu//pull/12749) | [`aad4b0d6f`](https://github.com/yuzu-emu/yuzu//pull/12749/files) | general: workarounds for SMMU syncing issues | [liamwhite](https://github.com/liamwhite/) | Yes | | [12749](https://github.com/yuzu-emu/yuzu//pull/12749) | [`aad4b0d6f`](https://github.com/yuzu-emu/yuzu//pull/12749/files) | general: workarounds for SMMU syncing issues | [liamwhite](https://github.com/liamwhite/) | Yes |
| [12982](https://github.com/yuzu-emu/yuzu//pull/12982) | [`ef5027712`](https://github.com/yuzu-emu/yuzu//pull/12982/files) | fs: Add FileSystemAccessor and use cmif serialization | [FearlessTobi](https://github.com/FearlessTobi/) | Yes |
| [13000](https://github.com/yuzu-emu/yuzu//pull/13000) | [`461eaca7e`](https://github.com/yuzu-emu/yuzu//pull/13000/files) | device_memory_manager: skip unregistered interfaces on invalidate | [liamwhite](https://github.com/liamwhite/) | Yes | | [13000](https://github.com/yuzu-emu/yuzu//pull/13000) | [`461eaca7e`](https://github.com/yuzu-emu/yuzu//pull/13000/files) | device_memory_manager: skip unregistered interfaces on invalidate | [liamwhite](https://github.com/liamwhite/) | Yes |
| [13006](https://github.com/yuzu-emu/yuzu//pull/13006) | [`3067bfd12`](https://github.com/yuzu-emu/yuzu//pull/13006/files) | buffer_cache: use mapped range with large vertex buffer size | [liamwhite](https://github.com/liamwhite/) | Yes | | [13075](https://github.com/yuzu-emu/yuzu//pull/13075) | [`f46dc3168`](https://github.com/yuzu-emu/yuzu//pull/13075/files) | shader_recompiler: throw on missing geometry streams in geometry shaders | [liamwhite](https://github.com/liamwhite/) | Yes |
| [13017](https://github.com/yuzu-emu/yuzu//pull/13017) | [`af4248256`](https://github.com/yuzu-emu/yuzu//pull/13017/files) | kernel: add and enable system suspend type | [liamwhite](https://github.com/liamwhite/) | Yes | | [13096](https://github.com/yuzu-emu/yuzu//pull/13096) | [`0a8759057`](https://github.com/yuzu-emu/yuzu//pull/13096/files) | texture_cache: use two-pass collection for costly load resources | [liamwhite](https://github.com/liamwhite/) | Yes |
| [13026](https://github.com/yuzu-emu/yuzu//pull/13026) | [`462ea921e`](https://github.com/yuzu-emu/yuzu//pull/13026/files) | shader_recompiler: fix non-const offset for arrayed image types | [liamwhite](https://github.com/liamwhite/) | Yes |
| [13034](https://github.com/yuzu-emu/yuzu//pull/13034) | [`50ecad547`](https://github.com/yuzu-emu/yuzu//pull/13034/files) | android: Input mapping | [t895](https://github.com/t895/) | Yes |
End of merge log. You can find the original README.md below the break. End of merge log. You can find the original README.md below the break.

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

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

View File

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

View File

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

View File

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

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,25 +107,21 @@ class InputProfileDialogFragment : DialogFragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
viewLifecycleOwner.lifecycleScope.launch { settingsViewModel.shouldShowDeleteProfileDialog.collect(viewLifecycleOwner) {
repeatOnLifecycle(Lifecycle.State.CREATED) { if (it.isNotEmpty()) {
settingsViewModel.shouldShowDeleteProfileDialog.collect { MessageDialogFragment.newInstance(
if (it.isNotEmpty()) { activity = requireActivity(),
MessageDialogFragment.newInstance( titleId = R.string.delete_input_profile,
activity = requireActivity(), descriptionId = R.string.delete_input_profile_description,
titleId = R.string.delete_input_profile, positiveAction = {
descriptionId = R.string.delete_input_profile_description, setting.deleteProfile(it)
positiveAction = { settingsViewModel.setReloadListAndNotifyDataset(true)
setting.deleteProfile(it) },
settingsViewModel.setReloadListAndNotifyDataset(true) negativeAction = {},
}, negativeButtonTitleId = android.R.string.cancel
negativeAction = {}, ).show(parentFragmentManager, MessageDialogFragment.TAG)
negativeButtonTitleId = android.R.string.cancel settingsViewModel.setShouldShowDeleteProfileDialog("")
).show(parentFragmentManager, MessageDialogFragment.TAG) dismiss()
settingsViewModel.setShouldShowDeleteProfileDialog("")
dismiss()
}
}
} }
} }
} }

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

View File

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

View File

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

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,65 +95,37 @@ class SettingsFragment : Fragment() {
settingsViewModel.setShouldNavigateBack(true) settingsViewModel.setShouldNavigateBack(true)
} }
viewLifecycleOwner.lifecycleScope.apply { settingsViewModel.shouldReloadSettingsList.collect(
launch { viewLifecycleOwner,
repeatOnLifecycle(Lifecycle.State.CREATED) { resetState = { settingsViewModel.setShouldReloadSettingsList(false) }
settingsViewModel.shouldReloadSettingsList.collectLatest { ) { if (it) presenter.loadSettingsList() }
if (it) { settingsViewModel.adapterItemChanged.collect(
settingsViewModel.setShouldReloadSettingsList(false) viewLifecycleOwner,
presenter.loadSettingsList() resetState = { settingsViewModel.setAdapterItemChanged(-1) }
} ) { if (it != -1) settingsAdapter?.notifyItemChanged(it) }
} settingsViewModel.datasetChanged.collect(
} viewLifecycleOwner,
} resetState = { settingsViewModel.setDatasetChanged(false) }
launch { ) { if (it) settingsAdapter?.notifyDataSetChanged() }
repeatOnLifecycle(Lifecycle.State.STARTED) { settingsViewModel.reloadListAndNotifyDataset.collect(
settingsViewModel.adapterItemChanged.collect { viewLifecycleOwner,
if (it != -1) { resetState = { settingsViewModel.setReloadListAndNotifyDataset(false) }
settingsAdapter?.notifyItemChanged(it) ) { if (it) presenter.loadSettingsList(true) }
settingsViewModel.setAdapterItemChanged(-1) settingsViewModel.shouldShowResetInputDialog.collect(
} viewLifecycleOwner,
} resetState = { settingsViewModel.setShouldShowResetInputDialog(false) }
} ) {
} if (it) {
launch { MessageDialogFragment.newInstance(
repeatOnLifecycle(Lifecycle.State.STARTED) { activity = requireActivity(),
settingsViewModel.datasetChanged.collect { titleId = R.string.reset_mapping,
if (it) { descriptionId = R.string.reset_mapping_description,
settingsAdapter?.notifyDataSetChanged() positiveAction = {
settingsViewModel.setDatasetChanged(false) NativeInput.resetControllerMappings(getPlayerIndex())
} settingsViewModel.setReloadListAndNotifyDataset(true)
} },
} negativeAction = {}
} ).show(parentFragmentManager, MessageDialogFragment.TAG)
launch {
repeatOnLifecycle(Lifecycle.State.CREATED) {
settingsViewModel.reloadListAndNotifyDataset.collectLatest {
if (it) {
settingsViewModel.setReloadListAndNotifyDataset(false)
presenter.loadSettingsList(true)
}
}
}
}
launch {
repeatOnLifecycle(Lifecycle.State.CREATED) {
settingsViewModel.shouldShowResetInputDialog.collectLatest {
if (it) {
MessageDialogFragment.newInstance(
activity = requireActivity(),
titleId = R.string.reset_mapping,
descriptionId = R.string.reset_mapping_description,
positiveAction = {
NativeInput.resetControllerMappings(getPlayerIndex())
settingsViewModel.setReloadListAndNotifyDataset(true)
},
negativeAction = {}
).show(parentFragmentManager, MessageDialogFragment.TAG)
settingsViewModel.setShouldShowResetInputDialog(false)
}
}
}
} }
} }

View File

@ -23,6 +23,7 @@ import org.yuzu.yuzu_emu.features.settings.model.LongSetting
import org.yuzu.yuzu_emu.features.settings.model.Settings import org.yuzu.yuzu_emu.features.settings.model.Settings
import org.yuzu.yuzu_emu.features.settings.model.Settings.MenuTag import org.yuzu.yuzu_emu.features.settings.model.Settings.MenuTag
import org.yuzu.yuzu_emu.features.settings.model.ShortSetting import org.yuzu.yuzu_emu.features.settings.model.ShortSetting
import org.yuzu.yuzu_emu.features.settings.model.StringSetting
import org.yuzu.yuzu_emu.features.settings.model.view.* import org.yuzu.yuzu_emu.features.settings.model.view.*
import org.yuzu.yuzu_emu.utils.InputHandler import org.yuzu.yuzu_emu.utils.InputHandler
import org.yuzu.yuzu_emu.utils.NativeConfig import org.yuzu.yuzu_emu.utils.NativeConfig
@ -153,6 +154,7 @@ class SettingsFragmentPresenter(
private fun addSystemSettings(sl: ArrayList<SettingsItem>) { private fun addSystemSettings(sl: ArrayList<SettingsItem>) {
sl.apply { sl.apply {
add(StringSetting.DEVICE_NAME.key)
add(BooleanSetting.RENDERER_USE_SPEED_LIMIT.key) add(BooleanSetting.RENDERER_USE_SPEED_LIMIT.key)
add(ShortSetting.RENDERER_SPEED_LIMIT.key) add(ShortSetting.RENDERER_SPEED_LIMIT.key)
add(BooleanSetting.USE_DOCKED_MODE.key) add(BooleanSetting.USE_DOCKED_MODE.key)
@ -778,7 +780,7 @@ class SettingsFragmentPresenter(
playerIndex: Int, playerIndex: Int,
paramName: String, paramName: String,
stick: NativeAnalog, stick: NativeAnalog,
defaultValue: Int defaultValue: Float
): AbstractIntSetting = ): AbstractIntSetting =
object : AbstractIntSetting { object : AbstractIntSetting {
val params get() = NativeInput.getStickParam(playerIndex, stick) val params get() = NativeInput.getStickParam(playerIndex, stick)
@ -786,7 +788,7 @@ class SettingsFragmentPresenter(
override val key = "" override val key = ""
override fun getInt(needsGlobal: Boolean): Int = override fun getInt(needsGlobal: Boolean): Int =
(params.get(paramName, 0.15f) * 100).toInt() (params.get(paramName, defaultValue) * 100).toInt()
override fun setInt(value: Int) { override fun setInt(value: Int) {
val tempParams = params val tempParams = params
@ -794,12 +796,12 @@ class SettingsFragmentPresenter(
NativeInput.setStickParam(playerIndex, stick, tempParams) NativeInput.setStickParam(playerIndex, stick, tempParams)
} }
override val defaultValue = defaultValue override val defaultValue = (defaultValue * 100).toInt()
override fun getValueAsString(needsGlobal: Boolean): String = override fun getValueAsString(needsGlobal: Boolean): String =
getInt(needsGlobal).toString() getInt(needsGlobal).toString()
override fun reset() = setInt(defaultValue) override fun reset() = setInt(this.defaultValue)
} }
private fun getExtraStickSettings( private fun getExtraStickSettings(
@ -809,11 +811,11 @@ class SettingsFragmentPresenter(
val stickIsController = val stickIsController =
NativeInput.isController(NativeInput.getStickParam(playerIndex, nativeAnalog)) NativeInput.isController(NativeInput.getStickParam(playerIndex, nativeAnalog))
val modifierRangeSetting = val modifierRangeSetting =
getStickIntSettingFromParam(playerIndex, "modifier_scale", nativeAnalog, 50) getStickIntSettingFromParam(playerIndex, "modifier_scale", nativeAnalog, 0.5f)
val stickRangeSetting = val stickRangeSetting =
getStickIntSettingFromParam(playerIndex, "range", nativeAnalog, 95) getStickIntSettingFromParam(playerIndex, "range", nativeAnalog, 0.95f)
val stickDeadzoneSetting = val stickDeadzoneSetting =
getStickIntSettingFromParam(playerIndex, "deadzone", nativeAnalog, 15) getStickIntSettingFromParam(playerIndex, "deadzone", nativeAnalog, 0.15f)
val out = mutableListOf<SettingsItem>().apply { val out = mutableListOf<SettingsItem>().apply {
if (stickIsController) { if (stickIsController) {

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,14 +81,10 @@ class SettingsSearchFragment : Fragment() {
search() search()
binding.settingsList.smoothScrollToPosition(0) binding.settingsList.smoothScrollToPosition(0)
} }
viewLifecycleOwner.lifecycleScope.launch { settingsViewModel.shouldReloadSettingsList.collect(viewLifecycleOwner) {
repeatOnLifecycle(Lifecycle.State.CREATED) { if (it) {
settingsViewModel.shouldReloadSettingsList.collect { settingsViewModel.setShouldReloadSettingsList(false)
if (it) { search()
settingsViewModel.setShouldReloadSettingsList(false)
search()
}
}
} }
} }
@ -106,10 +100,9 @@ class SettingsSearchFragment : Fragment() {
private fun search() { private fun search() {
val searchTerm = binding.searchText.text.toString().lowercase() val searchTerm = binding.searchText.text.toString().lowercase()
binding.clearButton.visibility = binding.clearButton.setVisible(visible = searchTerm.isNotEmpty(), gone = false)
if (searchTerm.isEmpty()) View.INVISIBLE else View.VISIBLE
if (searchTerm.isEmpty()) { if (searchTerm.isEmpty()) {
binding.noResultsView.visibility = View.VISIBLE binding.noResultsView.setVisible(visible = false, gone = false)
settingsAdapter?.submitList(emptyList()) settingsAdapter?.submitList(emptyList())
return return
} }
@ -136,8 +129,7 @@ class SettingsSearchFragment : Fragment() {
optionalSetting optionalSetting
} }
settingsAdapter?.submitList(sortedList) settingsAdapter?.submitList(sortedList)
binding.noResultsView.visibility = binding.noResultsView.setVisible(visible = sortedList.isEmpty(), gone = false)
if (sortedList.isEmpty()) View.VISIBLE else View.INVISIBLE
} }
private fun focusSearch() { private fun focusSearch() {

View File

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

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

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

View File

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

View File

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

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

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

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,57 +73,41 @@ class AddonsFragment : Fragment() {
adapter = AddonAdapter(addonViewModel) adapter = AddonAdapter(addonViewModel)
} }
viewLifecycleOwner.lifecycleScope.apply { addonViewModel.addonList.collect(viewLifecycleOwner) {
launch { (binding.listAddons.adapter as AddonAdapter).submitList(it)
repeatOnLifecycle(Lifecycle.State.STARTED) { }
addonViewModel.addonList.collect { addonViewModel.showModInstallPicker.collect(
(binding.listAddons.adapter as AddonAdapter).submitList(it) viewLifecycleOwner,
} resetState = { addonViewModel.showModInstallPicker(false) }
} ) { if (it) installAddon.launch(Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).data) }
addonViewModel.showModNoticeDialog.collect(
viewLifecycleOwner,
resetState = { addonViewModel.showModNoticeDialog(false) }
) {
if (it) {
MessageDialogFragment.newInstance(
requireActivity(),
titleId = R.string.addon_notice,
descriptionId = R.string.addon_notice_description,
dismissible = false,
positiveAction = { addonViewModel.showModInstallPicker(true) },
negativeAction = {},
negativeButtonTitleId = R.string.close
).show(parentFragmentManager, MessageDialogFragment.TAG)
} }
launch { }
repeatOnLifecycle(Lifecycle.State.STARTED) { addonViewModel.addonToDelete.collect(
addonViewModel.showModInstallPicker.collect { viewLifecycleOwner,
if (it) { resetState = { addonViewModel.setAddonToDelete(null) }
installAddon.launch(Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).data) ) {
addonViewModel.showModInstallPicker(false) if (it != null) {
} MessageDialogFragment.newInstance(
} requireActivity(),
} titleId = R.string.confirm_uninstall,
} descriptionId = R.string.confirm_uninstall_description,
launch { positiveAction = { addonViewModel.onDeleteAddon(it) },
repeatOnLifecycle(Lifecycle.State.STARTED) { negativeAction = {}
addonViewModel.showModNoticeDialog.collect { ).show(parentFragmentManager, MessageDialogFragment.TAG)
if (it) {
MessageDialogFragment.newInstance(
requireActivity(),
titleId = R.string.addon_notice,
descriptionId = R.string.addon_notice_description,
dismissible = false,
positiveAction = { addonViewModel.showModInstallPicker(true) },
negativeAction = {},
negativeButtonTitleId = R.string.close
).show(parentFragmentManager, MessageDialogFragment.TAG)
addonViewModel.showModNoticeDialog(false)
}
}
}
}
launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
addonViewModel.addonToDelete.collect {
if (it != null) {
MessageDialogFragment.newInstance(
requireActivity(),
titleId = R.string.confirm_uninstall,
descriptionId = R.string.confirm_uninstall_description,
positiveAction = { addonViewModel.onDeleteAddon(it) },
negativeAction = {}
).show(parentFragmentManager, MessageDialogFragment.TAG)
addonViewModel.setAddonToDelete(null)
}
}
}
} }
} }

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,129 +335,86 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
binding.loadingTitle.isSelected = true binding.loadingTitle.isSelected = true
binding.loadingText.isSelected = true binding.loadingText.isSelected = true
viewLifecycleOwner.lifecycleScope.apply { WindowInfoTracker.getOrCreate(requireContext())
launch { .windowLayoutInfo(requireActivity()).collect(viewLifecycleOwner) {
repeatOnLifecycle(Lifecycle.State.STARTED) { updateFoldableLayout(requireActivity() as EmulationActivity, it)
WindowInfoTracker.getOrCreate(requireContext())
.windowLayoutInfo(requireActivity())
.collect {
updateFoldableLayout(requireActivity() as EmulationActivity, it)
}
}
} }
launch { emulationViewModel.shaderProgress.collect(viewLifecycleOwner) {
repeatOnLifecycle(Lifecycle.State.CREATED) { if (it > 0 && it != emulationViewModel.totalShaders.value) {
emulationViewModel.shaderProgress.collectLatest { binding.loadingProgressIndicator.isIndeterminate = false
if (it > 0 && it != emulationViewModel.totalShaders.value) {
binding.loadingProgressIndicator.isIndeterminate = false
if (it < binding.loadingProgressIndicator.max) { if (it < binding.loadingProgressIndicator.max) {
binding.loadingProgressIndicator.progress = it binding.loadingProgressIndicator.progress = it
} }
} }
if (it == emulationViewModel.totalShaders.value) { if (it == emulationViewModel.totalShaders.value) {
binding.loadingText.setText(R.string.loading) binding.loadingText.setText(R.string.loading)
binding.loadingProgressIndicator.isIndeterminate = true binding.loadingProgressIndicator.isIndeterminate = true
}
}
}
} }
launch { }
repeatOnLifecycle(Lifecycle.State.CREATED) { emulationViewModel.totalShaders.collect(viewLifecycleOwner) {
emulationViewModel.totalShaders.collectLatest { binding.loadingProgressIndicator.max = it
binding.loadingProgressIndicator.max = it }
} emulationViewModel.shaderMessage.collect(viewLifecycleOwner) {
} if (it.isNotEmpty()) {
binding.loadingText.text = it
} }
launch { }
repeatOnLifecycle(Lifecycle.State.CREATED) {
emulationViewModel.shaderMessage.collectLatest {
if (it.isNotEmpty()) {
binding.loadingText.text = it
}
}
}
}
launch {
repeatOnLifecycle(Lifecycle.State.RESUMED) {
driverViewModel.isInteractionAllowed.collect {
if (it) {
startEmulation()
}
}
}
}
launch {
repeatOnLifecycle(Lifecycle.State.CREATED) {
emulationViewModel.emulationStarted.collectLatest {
if (it) {
binding.drawerLayout.setDrawerLockMode(IntSetting.LOCK_DRAWER.getInt())
ViewUtils.showView(binding.surfaceInputOverlay)
ViewUtils.hideView(binding.loadingIndicator)
emulationState.updateSurface() emulationViewModel.emulationStarted.collect(viewLifecycleOwner) {
if (it) {
binding.drawerLayout.setDrawerLockMode(IntSetting.LOCK_DRAWER.getInt())
ViewUtils.showView(binding.surfaceInputOverlay)
ViewUtils.hideView(binding.loadingIndicator)
// Setup overlays emulationState.updateSurface()
updateShowFpsOverlay()
updateThermalOverlay() // Setup overlays
} updateShowFpsOverlay()
} updateThermalOverlay()
}
} }
launch { }
repeatOnLifecycle(Lifecycle.State.CREATED) { emulationViewModel.isEmulationStopping.collect(viewLifecycleOwner) {
emulationViewModel.isEmulationStopping.collectLatest { if (it) {
if (it) { binding.loadingText.setText(R.string.shutting_down)
binding.loadingText.setText(R.string.shutting_down) ViewUtils.showView(binding.loadingIndicator)
ViewUtils.showView(binding.loadingIndicator) ViewUtils.hideView(binding.inputContainer)
ViewUtils.hideView(binding.inputContainer) ViewUtils.hideView(binding.showFpsText)
ViewUtils.hideView(binding.showFpsText)
}
}
}
} }
launch { }
repeatOnLifecycle(Lifecycle.State.CREATED) { emulationViewModel.drawerOpen.collect(viewLifecycleOwner) {
emulationViewModel.drawerOpen.collect { if (it) {
if (it) { binding.drawerLayout.open()
binding.drawerLayout.open() binding.inGameMenu.requestFocus()
binding.inGameMenu.requestFocus() } else {
} else { binding.drawerLayout.close()
binding.drawerLayout.close()
}
}
}
} }
launch { }
repeatOnLifecycle(Lifecycle.State.CREATED) { emulationViewModel.programChanged.collect(viewLifecycleOwner) {
emulationViewModel.programChanged.collect { if (it != 0) {
if (it != 0) { emulationViewModel.setEmulationStarted(false)
emulationViewModel.setEmulationStarted(false) binding.drawerLayout.close()
binding.drawerLayout.close() binding.drawerLayout
binding.drawerLayout .setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED)
.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED) ViewUtils.hideView(binding.surfaceInputOverlay)
ViewUtils.hideView(binding.surfaceInputOverlay) ViewUtils.showView(binding.loadingIndicator)
ViewUtils.showView(binding.loadingIndicator)
}
}
}
} }
launch { }
repeatOnLifecycle(Lifecycle.State.CREATED) { emulationViewModel.emulationStopped.collect(viewLifecycleOwner) {
emulationViewModel.emulationStopped.collect { if (it && emulationViewModel.programChanged.value != -1) {
if (it && emulationViewModel.programChanged.value != -1) { if (perfStatsUpdater != null) {
if (perfStatsUpdater != null) { perfStatsUpdateHandler.removeCallbacks(perfStatsUpdater!!)
perfStatsUpdateHandler.removeCallbacks(perfStatsUpdater!!)
}
emulationState.changeProgram(emulationViewModel.programChanged.value)
emulationViewModel.setProgramChanged(-1)
emulationViewModel.setEmulationStopped(false)
}
}
} }
emulationState.changeProgram(emulationViewModel.programChanged.value)
emulationViewModel.setProgramChanged(-1)
emulationViewModel.setEmulationStopped(false)
} }
} }
driverViewModel.isInteractionAllowed.collect(viewLifecycleOwner) {
if (it) startEmulation()
}
} }
private fun startEmulation(programIndex: Int = 0) { private fun startEmulation(programIndex: Int = 0) {
@ -500,14 +442,12 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
binding.drawerLayout.close() binding.drawerLayout.close()
} }
if (showInputOverlay) { if (showInputOverlay) {
binding.surfaceInputOverlay.visibility = View.INVISIBLE binding.surfaceInputOverlay.setVisible(visible = false, gone = false)
} }
} else { } else {
if (showInputOverlay && emulationViewModel.emulationStarted.value) { binding.surfaceInputOverlay.setVisible(
binding.surfaceInputOverlay.visibility = View.VISIBLE showInputOverlay && emulationViewModel.emulationStarted.value
} else { )
binding.surfaceInputOverlay.visibility = View.INVISIBLE
}
if (!isInFoldableLayout) { if (!isInFoldableLayout) {
if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) { if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) {
binding.surfaceInputOverlay.layout = OverlayLayout.Portrait binding.surfaceInputOverlay.layout = OverlayLayout.Portrait
@ -544,7 +484,9 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
} }
private fun updateShowFpsOverlay() { private fun updateShowFpsOverlay() {
if (BooleanSetting.SHOW_PERFORMANCE_OVERLAY.getBoolean()) { val showOverlay = BooleanSetting.SHOW_PERFORMANCE_OVERLAY.getBoolean()
binding.showFpsText.setVisible(showOverlay)
if (showOverlay) {
val SYSTEM_FPS = 0 val SYSTEM_FPS = 0
val FPS = 1 val FPS = 1
val FRAMETIME = 2 val FRAMETIME = 2
@ -564,17 +506,17 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
} }
} }
perfStatsUpdateHandler.post(perfStatsUpdater!!) perfStatsUpdateHandler.post(perfStatsUpdater!!)
binding.showFpsText.visibility = View.VISIBLE
} else { } else {
if (perfStatsUpdater != null) { if (perfStatsUpdater != null) {
perfStatsUpdateHandler.removeCallbacks(perfStatsUpdater!!) perfStatsUpdateHandler.removeCallbacks(perfStatsUpdater!!)
} }
binding.showFpsText.visibility = View.GONE
} }
} }
private fun updateThermalOverlay() { private fun updateThermalOverlay() {
if (BooleanSetting.SHOW_THERMAL_OVERLAY.getBoolean()) { val showOverlay = BooleanSetting.SHOW_THERMAL_OVERLAY.getBoolean()
binding.showThermalsText.setVisible(showOverlay)
if (showOverlay) {
thermalStatsUpdater = { thermalStatsUpdater = {
if (emulationViewModel.emulationStarted.value && if (emulationViewModel.emulationStarted.value &&
!emulationViewModel.isEmulationStopping.value !emulationViewModel.isEmulationStopping.value
@ -596,12 +538,10 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
} }
} }
thermalStatsUpdateHandler.post(thermalStatsUpdater!!) thermalStatsUpdateHandler.post(thermalStatsUpdater!!)
binding.showThermalsText.visibility = View.VISIBLE
} else { } else {
if (thermalStatsUpdater != null) { if (thermalStatsUpdater != null) {
thermalStatsUpdateHandler.removeCallbacks(thermalStatsUpdater!!) thermalStatsUpdateHandler.removeCallbacks(thermalStatsUpdater!!)
} }
binding.showThermalsText.visibility = View.GONE
} }
} }
@ -870,12 +810,12 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
} }
} }
} }
binding.doneControlConfig.visibility = View.VISIBLE binding.doneControlConfig.setVisible(true)
binding.surfaceInputOverlay.setIsInEditMode(true) binding.surfaceInputOverlay.setIsInEditMode(true)
} }
private fun stopConfiguringControls() { private fun stopConfiguringControls() {
binding.doneControlConfig.visibility = View.GONE binding.doneControlConfig.setVisible(false)
binding.surfaceInputOverlay.setIsInEditMode(false) binding.surfaceInputOverlay.setIsInEditMode(false)
// Unlock the orientation if it was locked for editing // Unlock the orientation if it was locked for editing
if (IntSetting.RENDERER_SCREEN_LAYOUT.getInt() == EmulationOrientation.Unspecified.int) { if (IntSetting.RENDERER_SCREEN_LAYOUT.getInt() == EmulationOrientation.Unspecified.int) {

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

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

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,72 +62,50 @@ class ProgressDialogFragment : DialogFragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
binding.message.isSelected = true binding.message.isSelected = true
viewLifecycleOwner.lifecycleScope.apply { taskViewModel.isComplete.collect(viewLifecycleOwner) {
launch { if (it) {
repeatOnLifecycle(Lifecycle.State.CREATED) { dismiss()
taskViewModel.isComplete.collect { when (val result = taskViewModel.result.value) {
if (it) { is String -> Toast.makeText(
dismiss() requireContext(),
when (val result = taskViewModel.result.value) { result,
is String -> Toast.makeText( Toast.LENGTH_LONG
requireContext(), ).show()
result,
Toast.LENGTH_LONG
).show()
is MessageDialogFragment -> result.show( is MessageDialogFragment -> result.show(
requireActivity().supportFragmentManager, requireActivity().supportFragmentManager,
MessageDialogFragment.TAG MessageDialogFragment.TAG
) )
else -> { else -> {
// Do nothing // Do nothing
}
}
taskViewModel.clear()
}
} }
} }
taskViewModel.clear()
} }
launch { }
repeatOnLifecycle(Lifecycle.State.CREATED) { taskViewModel.cancelled.collect(viewLifecycleOwner) {
taskViewModel.cancelled.collect { if (it) {
if (it) { dialog?.setTitle(R.string.cancelling)
dialog?.setTitle(R.string.cancelling) }
} }
} taskViewModel.progress.collect(viewLifecycleOwner) {
} if (it != 0.0) {
} binding.progressBar.apply {
launch { isIndeterminate = false
repeatOnLifecycle(Lifecycle.State.CREATED) { progress = (
taskViewModel.progress.collect { (it / taskViewModel.maxProgress.value) *
if (it != 0.0) { PROGRESS_BAR_RESOLUTION
binding.progressBar.apply { ).toInt()
isIndeterminate = false min = 0
progress = ( max = PROGRESS_BAR_RESOLUTION
(it / taskViewModel.maxProgress.value) *
PROGRESS_BAR_RESOLUTION
).toInt()
min = 0
max = PROGRESS_BAR_RESOLUTION
}
}
}
}
}
launch {
repeatOnLifecycle(Lifecycle.State.CREATED) {
taskViewModel.message.collect {
if (it.isEmpty()) {
binding.message.visibility = View.GONE
} else {
binding.message.visibility = View.VISIBLE
binding.message.text = it
}
}
} }
} }
} }
taskViewModel.message.collect(viewLifecycleOwner) {
binding.message.setVisible(it.isNotEmpty())
binding.message.text = it
}
} }
// By default, the ProgressDialog will immediately dismiss itself upon a button being pressed. // By default, the ProgressDialog will immediately dismiss itself upon a button being pressed.

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

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

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

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

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,42 +136,23 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
// Prevents navigation from being drawn for a short time on recreation if set to hidden // Prevents navigation from being drawn for a short time on recreation if set to hidden
if (!homeViewModel.navigationVisible.value.first) { if (!homeViewModel.navigationVisible.value.first) {
binding.navigationView.visibility = View.INVISIBLE binding.navigationView.setVisible(visible = false, gone = false)
binding.statusBarShade.visibility = View.INVISIBLE binding.statusBarShade.setVisible(visible = false, gone = false)
} }
lifecycleScope.apply { homeViewModel.navigationVisible.collect(this) { showNavigation(it.first, it.second) }
launch { homeViewModel.statusBarShadeVisible.collect(this) { showStatusBarShade(it) }
repeatOnLifecycle(Lifecycle.State.CREATED) { homeViewModel.contentToInstall.collect(
homeViewModel.navigationVisible.collect { showNavigation(it.first, it.second) } this,
} resetState = { homeViewModel.setContentToInstall(null) }
} ) {
launch { if (it != null) {
repeatOnLifecycle(Lifecycle.State.CREATED) { installContent(it)
homeViewModel.statusBarShadeVisible.collect { showStatusBarShade(it) }
}
}
launch {
repeatOnLifecycle(Lifecycle.State.CREATED) {
homeViewModel.contentToInstall.collect {
if (it != null) {
installContent(it)
homeViewModel.setContentToInstall(null)
}
}
}
}
launch {
repeatOnLifecycle(Lifecycle.State.CREATED) {
homeViewModel.checkKeys.collect {
if (it) {
checkKeys()
homeViewModel.setCheckKeys(false)
}
}
}
} }
} }
homeViewModel.checkKeys.collect(this, resetState = { homeViewModel.setCheckKeys(false) }) {
if (it) checkKeys()
}
setInsets() setInsets()
} }
@ -214,18 +192,14 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
private fun showNavigation(visible: Boolean, animated: Boolean) { private fun showNavigation(visible: Boolean, animated: Boolean) {
if (!animated) { if (!animated) {
if (visible) { binding.navigationView.setVisible(visible)
binding.navigationView.visibility = View.VISIBLE
} else {
binding.navigationView.visibility = View.INVISIBLE
}
return return
} }
val smallLayout = resources.getBoolean(R.bool.small_layout) val smallLayout = resources.getBoolean(R.bool.small_layout)
binding.navigationView.animate().apply { binding.navigationView.animate().apply {
if (visible) { if (visible) {
binding.navigationView.visibility = View.VISIBLE binding.navigationView.setVisible(true)
duration = 300 duration = 300
interpolator = PathInterpolator(0.05f, 0.7f, 0.1f, 1f) interpolator = PathInterpolator(0.05f, 0.7f, 0.1f, 1f)
@ -264,7 +238,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
} }
}.withEndAction { }.withEndAction {
if (!visible) { if (!visible) {
binding.navigationView.visibility = View.INVISIBLE binding.navigationView.setVisible(visible = false, gone = false)
} }
}.start() }.start()
} }
@ -272,7 +246,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
private fun showStatusBarShade(visible: Boolean) { private fun showStatusBarShade(visible: Boolean) {
binding.statusBarShade.animate().apply { binding.statusBarShade.animate().apply {
if (visible) { if (visible) {
binding.statusBarShade.visibility = View.VISIBLE binding.statusBarShade.setVisible(true)
binding.statusBarShade.translationY = binding.statusBarShade.height.toFloat() * -2 binding.statusBarShade.translationY = binding.statusBarShade.height.toFloat() * -2
duration = 300 duration = 300
translationY(0f) translationY(0f)
@ -284,7 +258,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
} }
}.withEndAction { }.withEndAction {
if (!visible) { if (!visible) {
binding.statusBarShade.visibility = View.INVISIBLE binding.statusBarShade.setVisible(visible = false, gone = false)
} }
}.start() }.start()
} }
@ -524,7 +498,8 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
this@MainActivity, this@MainActivity,
titleId = R.string.content_install_notice, titleId = R.string.content_install_notice,
descriptionId = R.string.content_install_notice_description, descriptionId = R.string.content_install_notice_description,
positiveAction = { homeViewModel.setContentToInstall(documents) } positiveAction = { homeViewModel.setContentToInstall(documents) },
negativeAction = {}
) )
} }
}.show(supportFragmentManager, ProgressDialogFragment.TAG) }.show(supportFragmentManager, ProgressDialogFragment.TAG)

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

@ -292,6 +292,9 @@ void EmulationSession::ShutdownEmulation() {
// Unload user input. // Unload user input.
m_system.HIDCore().UnloadInputDevices(); m_system.HIDCore().UnloadInputDevices();
// Enable all controllers
m_system.HIDCore().SetSupportedStyleTag({Core::HID::NpadStyleSet::All});
// Shutdown the main emulated process // Shutdown the main emulated process
if (m_load_result == Core::SystemResultStatus::Success) { if (m_load_result == Core::SystemResultStatus::Success) {
m_system.DetachDebugger(); m_system.DetachDebugger();
@ -404,7 +407,9 @@ static Core::SystemResultStatus RunEmulation(const std::string& filepath,
const size_t program_index, const size_t program_index,
const bool frontend_initiated) { const bool frontend_initiated) {
MicroProfileOnThreadCreate("EmuThread"); MicroProfileOnThreadCreate("EmuThread");
SCOPE_EXIT({ MicroProfileShutdown(); }); SCOPE_EXIT {
MicroProfileShutdown();
};
LOG_INFO(Frontend, "starting"); LOG_INFO(Frontend, "starting");
@ -413,7 +418,9 @@ static Core::SystemResultStatus RunEmulation(const std::string& filepath,
return Core::SystemResultStatus::ErrorLoader; return Core::SystemResultStatus::ErrorLoader;
} }
SCOPE_EXIT({ EmulationSession::GetInstance().ShutdownEmulation(); }); SCOPE_EXIT {
EmulationSession::GetInstance().ShutdownEmulation();
};
jconst result = EmulationSession::GetInstance().InitializeEmulation(filepath, program_index, jconst result = EmulationSession::GetInstance().InitializeEmulation(filepath, program_index,
frontend_initiated); frontend_initiated);

View File

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

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

@ -209,6 +209,7 @@
<string name="value_with_units">%1$s%2$s</string> <string name="value_with_units">%1$s%2$s</string>
<!-- System settings strings --> <!-- System settings strings -->
<string name="device_name">Device name</string>
<string name="use_docked_mode">Docked Mode</string> <string name="use_docked_mode">Docked Mode</string>
<string name="use_docked_mode_description">Increases resolution, decreasing performance. Handheld Mode is used when disabled, lowering resolution and increasing performance.</string> <string name="use_docked_mode_description">Increases resolution, decreasing performance. Handheld Mode is used when disabled, lowering resolution and increasing performance.</string>
<string name="emulated_region">Emulated region</string> <string name="emulated_region">Emulated region</string>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -2,8 +2,8 @@
# SPDX-License-Identifier: GPL-2.0-or-later # SPDX-License-Identifier: GPL-2.0-or-later
add_library(core STATIC add_library(core STATIC
arm/arm_interface.h
arm/arm_interface.cpp arm/arm_interface.cpp
arm/arm_interface.h
arm/debug.cpp arm/debug.cpp
arm/debug.h arm/debug.h
arm/exclusive_monitor.cpp arm/exclusive_monitor.cpp
@ -37,10 +37,10 @@ add_library(core STATIC
debugger/gdbstub.h debugger/gdbstub.h
debugger/gdbstub_arch.cpp debugger/gdbstub_arch.cpp
debugger/gdbstub_arch.h debugger/gdbstub_arch.h
device_memory_manager.h
device_memory_manager.inc
device_memory.cpp device_memory.cpp
device_memory.h device_memory.h
device_memory_manager.h
device_memory_manager.inc
file_sys/bis_factory.cpp file_sys/bis_factory.cpp
file_sys/bis_factory.h file_sys/bis_factory.h
file_sys/card_image.cpp file_sys/card_image.cpp
@ -59,8 +59,12 @@ add_library(core STATIC
file_sys/fs_path.h file_sys/fs_path.h
file_sys/fs_path_utility.h file_sys/fs_path_utility.h
file_sys/fs_string_util.h file_sys/fs_string_util.h
file_sys/fsa/fs_i_directory.h
file_sys/fsa/fs_i_file.h
file_sys/fsa/fs_i_filesystem.h
file_sys/fsmitm_romfsbuild.cpp file_sys/fsmitm_romfsbuild.cpp
file_sys/fsmitm_romfsbuild.h file_sys/fsmitm_romfsbuild.h
file_sys/fssrv/fssrv_sf_path.h
file_sys/fssystem/fs_i_storage.h file_sys/fssystem/fs_i_storage.h
file_sys/fssystem/fs_types.h file_sys/fssystem/fs_types.h
file_sys/fssystem/fssystem_aes_ctr_counter_extended_storage.cpp file_sys/fssystem/fssystem_aes_ctr_counter_extended_storage.cpp
@ -390,6 +394,20 @@ add_library(core STATIC
hle/service/acc/errors.h hle/service/acc/errors.h
hle/service/acc/profile_manager.cpp hle/service/acc/profile_manager.cpp
hle/service/acc/profile_manager.h hle/service/acc/profile_manager.h
hle/service/am/am.cpp
hle/service/am/am.h
hle/service/am/am_results.h
hle/service/am/am_types.h
hle/service/am/applet.cpp
hle/service/am/applet.h
hle/service/am/applet_data_broker.cpp
hle/service/am/applet_data_broker.h
hle/service/am/applet_manager.cpp
hle/service/am/applet_manager.h
hle/service/am/applet_message_queue.cpp
hle/service/am/applet_message_queue.h
hle/service/am/display_layer_manager.cpp
hle/service/am/display_layer_manager.h
hle/service/am/frontend/applet_cabinet.cpp hle/service/am/frontend/applet_cabinet.cpp
hle/service/am/frontend/applet_cabinet.h hle/service/am/frontend/applet_cabinet.h
hle/service/am/frontend/applet_controller.cpp hle/service/am/frontend/applet_controller.cpp
@ -411,24 +429,10 @@ add_library(core STATIC
hle/service/am/frontend/applet_web_browser_types.h hle/service/am/frontend/applet_web_browser_types.h
hle/service/am/frontend/applets.cpp hle/service/am/frontend/applets.cpp
hle/service/am/frontend/applets.h hle/service/am/frontend/applets.h
hle/service/am/am.cpp
hle/service/am/am.h
hle/service/am/am_results.h
hle/service/am/am_types.h
hle/service/am/applet.cpp
hle/service/am/applet.h
hle/service/am/applet_manager.cpp
hle/service/am/applet_data_broker.cpp
hle/service/am/applet_data_broker.h
hle/service/am/applet_manager.h
hle/service/am/applet_message_queue.cpp
hle/service/am/applet_message_queue.h
hle/service/am/hid_registration.cpp hle/service/am/hid_registration.cpp
hle/service/am/hid_registration.h hle/service/am/hid_registration.h
hle/service/am/library_applet_storage.cpp hle/service/am/library_applet_storage.cpp
hle/service/am/library_applet_storage.h hle/service/am/library_applet_storage.h
hle/service/am/managed_layer_holder.cpp
hle/service/am/managed_layer_holder.h
hle/service/am/process.cpp hle/service/am/process.cpp
hle/service/am/process.h hle/service/am/process.h
hle/service/am/service/all_system_applet_proxies_service.cpp hle/service/am/service/all_system_applet_proxies_service.cpp
@ -441,10 +445,10 @@ add_library(core STATIC
hle/service/am/service/application_creator.h hle/service/am/service/application_creator.h
hle/service/am/service/application_functions.cpp hle/service/am/service/application_functions.cpp
hle/service/am/service/application_functions.h hle/service/am/service/application_functions.h
hle/service/am/service/application_proxy_service.cpp
hle/service/am/service/application_proxy_service.h
hle/service/am/service/application_proxy.cpp hle/service/am/service/application_proxy.cpp
hle/service/am/service/application_proxy.h hle/service/am/service/application_proxy.h
hle/service/am/service/application_proxy_service.cpp
hle/service/am/service/application_proxy_service.h
hle/service/am/service/audio_controller.cpp hle/service/am/service/audio_controller.cpp
hle/service/am/service/audio_controller.h hle/service/am/service/audio_controller.h
hle/service/am/service/common_state_getter.cpp hle/service/am/service/common_state_getter.cpp
@ -473,16 +477,14 @@ add_library(core STATIC
hle/service/am/service/process_winding_controller.h hle/service/am/service/process_winding_controller.h
hle/service/am/service/self_controller.cpp hle/service/am/service/self_controller.cpp
hle/service/am/service/self_controller.h hle/service/am/service/self_controller.h
hle/service/am/service/storage_accessor.cpp
hle/service/am/service/storage_accessor.h
hle/service/am/service/storage.cpp hle/service/am/service/storage.cpp
hle/service/am/service/storage.h hle/service/am/service/storage.h
hle/service/am/service/storage_accessor.cpp
hle/service/am/service/storage_accessor.h
hle/service/am/service/system_applet_proxy.cpp hle/service/am/service/system_applet_proxy.cpp
hle/service/am/service/system_applet_proxy.h hle/service/am/service/system_applet_proxy.h
hle/service/am/service/window_controller.cpp hle/service/am/service/window_controller.cpp
hle/service/am/service/window_controller.h hle/service/am/service/window_controller.h
hle/service/am/system_buffer_manager.cpp
hle/service/am/system_buffer_manager.h
hle/service/aoc/aoc_u.cpp hle/service/aoc/aoc_u.cpp
hle/service/aoc/aoc_u.h hle/service/aoc/aoc_u.h
hle/service/apm/apm.cpp hle/service/apm/apm.cpp
@ -491,12 +493,12 @@ add_library(core STATIC
hle/service/apm/apm_controller.h hle/service/apm/apm_controller.h
hle/service/apm/apm_interface.cpp hle/service/apm/apm_interface.cpp
hle/service/apm/apm_interface.h hle/service/apm/apm_interface.h
hle/service/audio/audctl.cpp
hle/service/audio/audctl.h
hle/service/audio/audin_u.cpp hle/service/audio/audin_u.cpp
hle/service/audio/audin_u.h hle/service/audio/audin_u.h
hle/service/audio/audio.cpp hle/service/audio/audio.cpp
hle/service/audio/audio.h hle/service/audio/audio.h
hle/service/audio/audio_controller.cpp
hle/service/audio/audio_controller.h
hle/service/audio/audout_u.cpp hle/service/audio/audout_u.cpp
hle/service/audio/audout_u.h hle/service/audio/audout_u.h
hle/service/audio/audrec_a.cpp hle/service/audio/audrec_a.cpp
@ -510,18 +512,6 @@ add_library(core STATIC
hle/service/audio/hwopus.h hle/service/audio/hwopus.h
hle/service/bcat/backend/backend.cpp hle/service/bcat/backend/backend.cpp
hle/service/bcat/backend/backend.h hle/service/bcat/backend/backend.h
hle/service/bcat/news/newly_arrived_event_holder.cpp
hle/service/bcat/news/newly_arrived_event_holder.h
hle/service/bcat/news/news_data_service.cpp
hle/service/bcat/news/news_data_service.h
hle/service/bcat/news/news_database_service.cpp
hle/service/bcat/news/news_database_service.h
hle/service/bcat/news/news_service.cpp
hle/service/bcat/news/news_service.h
hle/service/bcat/news/overwrite_event_holder.cpp
hle/service/bcat/news/overwrite_event_holder.h
hle/service/bcat/news/service_creator.cpp
hle/service/bcat/news/service_creator.h
hle/service/bcat/bcat.cpp hle/service/bcat/bcat.cpp
hle/service/bcat/bcat.h hle/service/bcat/bcat.h
hle/service/bcat/bcat_result.h hle/service/bcat/bcat_result.h
@ -537,6 +527,18 @@ add_library(core STATIC
hle/service/bcat/delivery_cache_progress_service.h hle/service/bcat/delivery_cache_progress_service.h
hle/service/bcat/delivery_cache_storage_service.cpp hle/service/bcat/delivery_cache_storage_service.cpp
hle/service/bcat/delivery_cache_storage_service.h hle/service/bcat/delivery_cache_storage_service.h
hle/service/bcat/news/newly_arrived_event_holder.cpp
hle/service/bcat/news/newly_arrived_event_holder.h
hle/service/bcat/news/news_data_service.cpp
hle/service/bcat/news/news_data_service.h
hle/service/bcat/news/news_database_service.cpp
hle/service/bcat/news/news_database_service.h
hle/service/bcat/news/news_service.cpp
hle/service/bcat/news/news_service.h
hle/service/bcat/news/overwrite_event_holder.cpp
hle/service/bcat/news/overwrite_event_holder.h
hle/service/bcat/news/service_creator.cpp
hle/service/bcat/news/service_creator.h
hle/service/bcat/service_creator.cpp hle/service/bcat/service_creator.cpp
hle/service/bcat/service_creator.h hle/service/bcat/service_creator.h
hle/service/bpc/bpc.cpp hle/service/bpc/bpc.cpp
@ -545,6 +547,16 @@ add_library(core STATIC
hle/service/btdrv/btdrv.h hle/service/btdrv/btdrv.h
hle/service/btm/btm.cpp hle/service/btm/btm.cpp
hle/service/btm/btm.h hle/service/btm/btm.h
hle/service/btm/btm_debug.cpp
hle/service/btm/btm_debug.h
hle/service/btm/btm_system.cpp
hle/service/btm/btm_system.h
hle/service/btm/btm_system_core.cpp
hle/service/btm/btm_system_core.h
hle/service/btm/btm_user.cpp
hle/service/btm/btm_user.h
hle/service/btm/btm_user_core.cpp
hle/service/btm/btm_user_core.h
hle/service/caps/caps.cpp hle/service/caps/caps.cpp
hle/service/caps/caps.h hle/service/caps/caps.h
hle/service/caps/caps_a.cpp hle/service/caps/caps_a.cpp
@ -600,8 +612,6 @@ add_library(core STATIC
hle/service/filesystem/romfs_controller.h hle/service/filesystem/romfs_controller.h
hle/service/filesystem/save_data_controller.cpp hle/service/filesystem/save_data_controller.cpp
hle/service/filesystem/save_data_controller.h hle/service/filesystem/save_data_controller.h
hle/service/fgm/fgm.cpp
hle/service/fgm/fgm.h
hle/service/friend/friend.cpp hle/service/friend/friend.cpp
hle/service/friend/friend.h hle/service/friend/friend.h
hle/service/friend/friend_interface.cpp hle/service/friend/friend_interface.cpp
@ -739,15 +749,48 @@ add_library(core STATIC
hle/service/nim/nim.h hle/service/nim/nim.h
hle/service/npns/npns.cpp hle/service/npns/npns.cpp
hle/service/npns/npns.h hle/service/npns/npns.h
hle/service/ns/errors.h hle/service/ns/account_proxy_interface.cpp
hle/service/ns/iplatform_service_manager.cpp hle/service/ns/account_proxy_interface.h
hle/service/ns/iplatform_service_manager.h hle/service/ns/application_manager_interface.cpp
hle/service/ns/application_manager_interface.h
hle/service/ns/application_version_interface.cpp
hle/service/ns/application_version_interface.h
hle/service/ns/content_management_interface.cpp
hle/service/ns/content_management_interface.h
hle/service/ns/develop_interface.cpp
hle/service/ns/develop_interface.h
hle/service/ns/document_interface.cpp
hle/service/ns/document_interface.h
hle/service/ns/download_task_interface.cpp
hle/service/ns/download_task_interface.h
hle/service/ns/dynamic_rights_interface.cpp
hle/service/ns/dynamic_rights_interface.h
hle/service/ns/ecommerce_interface.cpp
hle/service/ns/ecommerce_interface.h
hle/service/ns/factory_reset_interface.cpp
hle/service/ns/factory_reset_interface.h
hle/service/ns/language.cpp hle/service/ns/language.cpp
hle/service/ns/language.h hle/service/ns/language.h
hle/service/ns/ns.cpp hle/service/ns/ns.cpp
hle/service/ns/ns.h hle/service/ns/ns.h
hle/service/ns/pdm_qry.cpp hle/service/ns/ns_results.h
hle/service/ns/pdm_qry.h hle/service/ns/ns_types.h
hle/service/ns/platform_service_manager.cpp
hle/service/ns/platform_service_manager.h
hle/service/ns/query_service.cpp
hle/service/ns/query_service.h
hle/service/ns/read_only_application_control_data_interface.cpp
hle/service/ns/read_only_application_control_data_interface.h
hle/service/ns/read_only_application_record_interface.cpp
hle/service/ns/read_only_application_record_interface.h
hle/service/ns/service_getter_interface.cpp
hle/service/ns/service_getter_interface.h
hle/service/ns/system_update_control.cpp
hle/service/ns/system_update_control.h
hle/service/ns/system_update_interface.cpp
hle/service/ns/system_update_interface.h
hle/service/ns/vulnerability_manager_interface.cpp
hle/service/ns/vulnerability_manager_interface.h
hle/service/nvdrv/core/container.cpp hle/service/nvdrv/core/container.cpp
hle/service/nvdrv/core/container.h hle/service/nvdrv/core/container.h
hle/service/nvdrv/core/heap_mapper.cpp hle/service/nvdrv/core/heap_mapper.cpp
@ -800,14 +843,14 @@ add_library(core STATIC
hle/service/nvnflinger/consumer_base.cpp hle/service/nvnflinger/consumer_base.cpp
hle/service/nvnflinger/consumer_base.h hle/service/nvnflinger/consumer_base.h
hle/service/nvnflinger/consumer_listener.h hle/service/nvnflinger/consumer_listener.h
hle/service/nvnflinger/fb_share_buffer_manager.cpp
hle/service/nvnflinger/fb_share_buffer_manager.h
hle/service/nvnflinger/graphic_buffer_producer.cpp hle/service/nvnflinger/graphic_buffer_producer.cpp
hle/service/nvnflinger/graphic_buffer_producer.h hle/service/nvnflinger/graphic_buffer_producer.h
hle/service/nvnflinger/hos_binder_driver_server.cpp
hle/service/nvnflinger/hos_binder_driver_server.h
hle/service/nvnflinger/hardware_composer.cpp hle/service/nvnflinger/hardware_composer.cpp
hle/service/nvnflinger/hardware_composer.h hle/service/nvnflinger/hardware_composer.h
hle/service/nvnflinger/hos_binder_driver.cpp
hle/service/nvnflinger/hos_binder_driver.h
hle/service/nvnflinger/hos_binder_driver_server.cpp
hle/service/nvnflinger/hos_binder_driver_server.h
hle/service/nvnflinger/hwc_layer.h hle/service/nvnflinger/hwc_layer.h
hle/service/nvnflinger/nvnflinger.cpp hle/service/nvnflinger/nvnflinger.cpp
hle/service/nvnflinger/nvnflinger.h hle/service/nvnflinger/nvnflinger.h
@ -815,6 +858,8 @@ add_library(core STATIC
hle/service/nvnflinger/pixel_format.h hle/service/nvnflinger/pixel_format.h
hle/service/nvnflinger/producer_listener.h hle/service/nvnflinger/producer_listener.h
hle/service/nvnflinger/status.h hle/service/nvnflinger/status.h
hle/service/nvnflinger/surface_flinger.cpp
hle/service/nvnflinger/surface_flinger.h
hle/service/nvnflinger/ui/fence.h hle/service/nvnflinger/ui/fence.h
hle/service/nvnflinger/ui/graphic_buffer.cpp hle/service/nvnflinger/ui/graphic_buffer.cpp
hle/service/nvnflinger/ui/graphic_buffer.h hle/service/nvnflinger/ui/graphic_buffer.h
@ -831,11 +876,11 @@ add_library(core STATIC
hle/service/omm/power_state_interface.h hle/service/omm/power_state_interface.h
hle/service/os/event.cpp hle/service/os/event.cpp
hle/service/os/event.h hle/service/os/event.h
hle/service/os/multi_wait.cpp
hle/service/os/multi_wait.h
hle/service/os/multi_wait_holder.cpp hle/service/os/multi_wait_holder.cpp
hle/service/os/multi_wait_holder.h hle/service/os/multi_wait_holder.h
hle/service/os/multi_wait_utils.h hle/service/os/multi_wait_utils.h
hle/service/os/multi_wait.cpp
hle/service/os/multi_wait.h
hle/service/os/mutex.cpp hle/service/os/mutex.cpp
hle/service/os/mutex.h hle/service/os/mutex.h
hle/service/pcie/pcie.cpp hle/service/pcie/pcie.cpp
@ -873,15 +918,17 @@ add_library(core STATIC
hle/service/psc/time/common.cpp hle/service/psc/time/common.cpp
hle/service/psc/time/common.h hle/service/psc/time/common.h
hle/service/psc/time/errors.h hle/service/psc/time/errors.h
hle/service/psc/time/shared_memory.cpp
hle/service/psc/time/shared_memory.h
hle/service/psc/time/static.cpp
hle/service/psc/time/static.h
hle/service/psc/time/manager.h hle/service/psc/time/manager.h
hle/service/psc/time/power_state_request_manager.cpp
hle/service/psc/time/power_state_request_manager.h
hle/service/psc/time/power_state_service.cpp hle/service/psc/time/power_state_service.cpp
hle/service/psc/time/power_state_service.h hle/service/psc/time/power_state_service.h
hle/service/psc/time/service_manager.cpp hle/service/psc/time/service_manager.cpp
hle/service/psc/time/service_manager.h hle/service/psc/time/service_manager.h
hle/service/psc/time/shared_memory.cpp
hle/service/psc/time/shared_memory.h
hle/service/psc/time/static.cpp
hle/service/psc/time/static.h
hle/service/psc/time/steady_clock.cpp hle/service/psc/time/steady_clock.cpp
hle/service/psc/time/steady_clock.h hle/service/psc/time/steady_clock.h
hle/service/psc/time/system_clock.cpp hle/service/psc/time/system_clock.cpp
@ -890,8 +937,6 @@ add_library(core STATIC
hle/service/psc/time/time_zone.h hle/service/psc/time/time_zone.h
hle/service/psc/time/time_zone_service.cpp hle/service/psc/time/time_zone_service.cpp
hle/service/psc/time/time_zone_service.h hle/service/psc/time/time_zone_service.h
hle/service/psc/time/power_state_request_manager.cpp
hle/service/psc/time/power_state_request_manager.h
hle/service/ptm/psm.cpp hle/service/ptm/psm.cpp
hle/service/ptm/psm.h hle/service/ptm/psm.h
hle/service/ptm/ptm.cpp hle/service/ptm/ptm.cpp
@ -908,19 +953,21 @@ add_library(core STATIC
hle/service/server_manager.h hle/service/server_manager.h
hle/service/service.cpp hle/service/service.cpp
hle/service/service.h hle/service/service.h
hle/service/set/setting_formats/appln_settings.cpp hle/service/services.cpp
hle/service/set/setting_formats/appln_settings.h hle/service/services.h
hle/service/set/setting_formats/device_settings.cpp
hle/service/set/setting_formats/device_settings.h
hle/service/set/setting_formats/system_settings.cpp
hle/service/set/setting_formats/system_settings.h
hle/service/set/setting_formats/private_settings.cpp
hle/service/set/setting_formats/private_settings.h
hle/service/set/factory_settings_server.cpp hle/service/set/factory_settings_server.cpp
hle/service/set/factory_settings_server.h hle/service/set/factory_settings_server.h
hle/service/set/firmware_debug_settings_server.cpp hle/service/set/firmware_debug_settings_server.cpp
hle/service/set/firmware_debug_settings_server.h hle/service/set/firmware_debug_settings_server.h
hle/service/set/key_code_map.h hle/service/set/key_code_map.h
hle/service/set/setting_formats/appln_settings.cpp
hle/service/set/setting_formats/appln_settings.h
hle/service/set/setting_formats/device_settings.cpp
hle/service/set/setting_formats/device_settings.h
hle/service/set/setting_formats/private_settings.cpp
hle/service/set/setting_formats/private_settings.h
hle/service/set/setting_formats/system_settings.cpp
hle/service/set/setting_formats/system_settings.h
hle/service/set/settings.cpp hle/service/set/settings.cpp
hle/service/set/settings.h hle/service/set/settings.h
hle/service/set/settings_server.cpp hle/service/set/settings_server.cpp
@ -955,30 +1002,36 @@ add_library(core STATIC
hle/service/ssl/ssl_backend.h hle/service/ssl/ssl_backend.h
hle/service/usb/usb.cpp hle/service/usb/usb.cpp
hle/service/usb/usb.h hle/service/usb/usb.h
hle/service/vi/display/vi_display.cpp
hle/service/vi/display/vi_display.h
hle/service/vi/layer/vi_layer.cpp
hle/service/vi/layer/vi_layer.h
hle/service/vi/application_display_service.cpp hle/service/vi/application_display_service.cpp
hle/service/vi/application_display_service.h hle/service/vi/application_display_service.h
hle/service/vi/application_root_service.cpp hle/service/vi/application_root_service.cpp
hle/service/vi/application_root_service.h hle/service/vi/application_root_service.h
hle/service/vi/hos_binder_driver.cpp hle/service/vi/conductor.cpp
hle/service/vi/hos_binder_driver.h hle/service/vi/conductor.h
hle/service/vi/container.cpp
hle/service/vi/container.h
hle/service/vi/display.h
hle/service/vi/display_list.h
hle/service/vi/layer.h
hle/service/vi/layer_list.h
hle/service/vi/manager_display_service.cpp hle/service/vi/manager_display_service.cpp
hle/service/vi/manager_display_service.h hle/service/vi/manager_display_service.h
hle/service/vi/manager_root_service.cpp hle/service/vi/manager_root_service.cpp
hle/service/vi/manager_root_service.h hle/service/vi/manager_root_service.h
hle/service/vi/service_creator.cpp hle/service/vi/service_creator.cpp
hle/service/vi/service_creator.h hle/service/vi/service_creator.h
hle/service/vi/shared_buffer_manager.cpp
hle/service/vi/shared_buffer_manager.h
hle/service/vi/system_display_service.cpp hle/service/vi/system_display_service.cpp
hle/service/vi/system_display_service.h hle/service/vi/system_display_service.h
hle/service/vi/system_root_service.cpp hle/service/vi/system_root_service.cpp
hle/service/vi/system_root_service.h hle/service/vi/system_root_service.h
hle/service/vi/vi_results.h
hle/service/vi/vi_types.h
hle/service/vi/vi.cpp hle/service/vi/vi.cpp
hle/service/vi/vi.h hle/service/vi/vi.h
hle/service/vi/vi_results.h
hle/service/vi/vi_types.h
hle/service/vi/vsync_manager.cpp
hle/service/vi/vsync_manager.h
internal_network/network.cpp internal_network/network.cpp
internal_network/network.h internal_network/network.h
internal_network/network_interface.cpp internal_network/network_interface.cpp

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

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

View File

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

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

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

View File

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

View File

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

View File

@ -381,7 +381,7 @@ public:
// Check that it's possible for us to remove a child // Check that it's possible for us to remove a child
auto* p = m_write_buffer.Get(); auto* p = m_write_buffer.Get();
s32 len = std::strlen(p); s32 len = static_cast<s32>(std::strlen(p));
R_UNLESS(len != 1 || (p[0] != '/' && p[0] != '.'), ResultNotImplemented); R_UNLESS(len != 1 || (p[0] != '/' && p[0] != '.'), ResultNotImplemented);
// Handle a trailing separator // Handle a trailing separator

View File

@ -426,9 +426,10 @@ public:
R_SUCCEED(); R_SUCCEED();
} }
static Result Normalize(char* dst, size_t* out_len, const char* path, size_t max_out_size, static constexpr Result Normalize(char* dst, size_t* out_len, const char* path,
bool is_windows_path, bool is_drive_relative_path, size_t max_out_size, bool is_windows_path,
bool allow_all_characters = false) { bool is_drive_relative_path,
bool allow_all_characters = false) {
// Use StringTraits names for remainder of scope // Use StringTraits names for remainder of scope
using namespace StringTraits; using namespace StringTraits;
@ -447,7 +448,7 @@ public:
char* replacement_path = nullptr; char* replacement_path = nullptr;
size_t replacement_path_size = 0; size_t replacement_path_size = 0;
SCOPE_EXIT({ SCOPE_EXIT {
if (replacement_path != nullptr) { if (replacement_path != nullptr) {
if (std::is_constant_evaluated()) { if (std::is_constant_evaluated()) {
delete[] replacement_path; delete[] replacement_path;
@ -455,7 +456,7 @@ public:
Deallocate(replacement_path, replacement_path_size); Deallocate(replacement_path, replacement_path_size);
} }
} }
}); };
// Perform path replacement, if necessary // Perform path replacement, if necessary
if (IsParentDirectoryPathReplacementNeeded(cur_path)) { if (IsParentDirectoryPathReplacementNeeded(cur_path)) {
@ -1102,8 +1103,8 @@ public:
R_SUCCEED(); R_SUCCEED();
} }
static Result Normalize(char* dst, size_t dst_size, const char* path, size_t path_len, static constexpr Result Normalize(char* dst, size_t dst_size, const char* path, size_t path_len,
const PathFlags& flags) { const PathFlags& flags) {
// Use StringTraits names for remainder of scope // Use StringTraits names for remainder of scope
using namespace StringTraits; using namespace StringTraits;
@ -1199,7 +1200,7 @@ public:
const size_t replaced_src_len = path_len - (src - path); const size_t replaced_src_len = path_len - (src - path);
char* replaced_src = nullptr; char* replaced_src = nullptr;
SCOPE_EXIT({ SCOPE_EXIT {
if (replaced_src != nullptr) { if (replaced_src != nullptr) {
if (std::is_constant_evaluated()) { if (std::is_constant_evaluated()) {
delete[] replaced_src; delete[] replaced_src;
@ -1207,7 +1208,7 @@ public:
Deallocate(replaced_src, replaced_src_len); Deallocate(replaced_src, replaced_src_len);
} }
} }
}); };
if (std::is_constant_evaluated()) { if (std::is_constant_evaluated()) {
replaced_src = new char[replaced_src_len]; replaced_src = new char[replaced_src_len];

View File

@ -19,6 +19,11 @@ constexpr int Strlen(const T* str) {
return length; return length;
} }
template <typename T>
constexpr int Strnlen(const T* str, std::size_t count) {
return Strnlen(str, static_cast<int>(count));
}
template <typename T> template <typename T>
constexpr int Strnlen(const T* str, int count) { constexpr int Strnlen(const T* str, int count) {
ASSERT(str != nullptr); ASSERT(str != nullptr);
@ -32,6 +37,11 @@ constexpr int Strnlen(const T* str, int count) {
return length; return length;
} }
template <typename T>
constexpr int Strncmp(const T* lhs, const T* rhs, std::size_t count) {
return Strncmp(lhs, rhs, static_cast<int>(count));
}
template <typename T> template <typename T>
constexpr int Strncmp(const T* lhs, const T* rhs, int count) { constexpr int Strncmp(const T* lhs, const T* rhs, int count) {
ASSERT(lhs != nullptr); ASSERT(lhs != nullptr);
@ -51,6 +61,11 @@ constexpr int Strncmp(const T* lhs, const T* rhs, int count) {
return l - r; return l - r;
} }
template <typename T>
static constexpr int Strlcpy(T* dst, const T* src, std::size_t count) {
return Strlcpy<T>(dst, src, static_cast<int>(count));
}
template <typename T> template <typename T>
static constexpr int Strlcpy(T* dst, const T* src, int count) { static constexpr int Strlcpy(T* dst, const T* src, int count) {
ASSERT(dst != nullptr); ASSERT(dst != nullptr);

View File

@ -0,0 +1,91 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "common/common_types.h"
#include "core/file_sys/errors.h"
#include "core/file_sys/fs_directory.h"
#include "core/file_sys/fs_file.h"
#include "core/file_sys/fs_filesystem.h"
#include "core/file_sys/savedata_factory.h"
#include "core/file_sys/vfs/vfs.h"
#include "core/hle/result.h"
namespace FileSys::Fsa {
class IDirectory {
public:
explicit IDirectory(VirtualDir backend_, OpenDirectoryMode mode)
: backend(std::move(backend_)) {
// TODO(DarkLordZach): Verify that this is the correct behavior.
// Build entry index now to save time later.
if (True(mode & OpenDirectoryMode::Directory)) {
BuildEntryIndex(backend->GetSubdirectories(), DirectoryEntryType::Directory);
}
if (True(mode & OpenDirectoryMode::File)) {
BuildEntryIndex(backend->GetFiles(), DirectoryEntryType::File);
}
}
virtual ~IDirectory() {}
Result Read(s64* out_count, DirectoryEntry* out_entries, s64 max_entries) {
R_UNLESS(out_count != nullptr, ResultNullptrArgument);
if (max_entries == 0) {
*out_count = 0;
R_SUCCEED();
}
R_UNLESS(out_entries != nullptr, ResultNullptrArgument);
R_UNLESS(max_entries > 0, ResultInvalidArgument);
R_RETURN(this->DoRead(out_count, out_entries, max_entries));
}
Result GetEntryCount(s64* out) {
R_UNLESS(out != nullptr, ResultNullptrArgument);
R_RETURN(this->DoGetEntryCount(out));
}
private:
Result DoRead(s64* out_count, DirectoryEntry* out_entries, s64 max_entries) {
const u64 actual_entries =
std::min(static_cast<u64>(max_entries), entries.size() - next_entry_index);
const auto* begin = reinterpret_cast<u8*>(entries.data() + next_entry_index);
const auto* end = reinterpret_cast<u8*>(entries.data() + next_entry_index + actual_entries);
const auto range_size = static_cast<std::size_t>(std::distance(begin, end));
next_entry_index += actual_entries;
*out_count = actual_entries;
std::memcpy(out_entries, begin, range_size);
R_SUCCEED();
}
Result DoGetEntryCount(s64* out) {
*out = entries.size() - next_entry_index;
R_SUCCEED();
}
// TODO: Remove this when VFS is gone
template <typename T>
void BuildEntryIndex(const std::vector<T>& new_data, DirectoryEntryType type) {
entries.reserve(entries.size() + new_data.size());
for (const auto& new_entry : new_data) {
auto name = new_entry->GetName();
if (type == DirectoryEntryType::File && name == GetSaveDataSizeFileName()) {
continue;
}
entries.emplace_back(name, static_cast<s8>(type),
type == DirectoryEntryType::Directory ? 0 : new_entry->GetSize());
}
}
VirtualDir backend;
std::vector<DirectoryEntry> entries;
u64 next_entry_index = 0;
};
} // namespace FileSys::Fsa

View File

@ -0,0 +1,167 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "common/overflow.h"
#include "core/file_sys/errors.h"
#include "core/file_sys/fs_file.h"
#include "core/file_sys/fs_filesystem.h"
#include "core/file_sys/fs_operate_range.h"
#include "core/file_sys/vfs/vfs.h"
#include "core/file_sys/vfs/vfs_types.h"
#include "core/hle/result.h"
namespace FileSys::Fsa {
class IFile {
public:
explicit IFile(VirtualFile backend_) : backend(std::move(backend_)) {}
virtual ~IFile() {}
Result Read(size_t* out, s64 offset, void* buffer, size_t size, const ReadOption& option) {
// Check that we have an output pointer
R_UNLESS(out != nullptr, ResultNullptrArgument);
// If we have nothing to read, just succeed
if (size == 0) {
*out = 0;
R_SUCCEED();
}
// Check that the read is valid
R_UNLESS(buffer != nullptr, ResultNullptrArgument);
R_UNLESS(offset >= 0, ResultOutOfRange);
R_UNLESS(Common::CanAddWithoutOverflow<s64>(offset, size), ResultOutOfRange);
// Do the read
R_RETURN(this->DoRead(out, offset, buffer, size, option));
}
Result Read(size_t* out, s64 offset, void* buffer, size_t size) {
R_RETURN(this->Read(out, offset, buffer, size, ReadOption::None));
}
Result GetSize(s64* out) {
R_UNLESS(out != nullptr, ResultNullptrArgument);
R_RETURN(this->DoGetSize(out));
}
Result Flush() {
R_RETURN(this->DoFlush());
}
Result Write(s64 offset, const void* buffer, size_t size, const WriteOption& option) {
// Handle the zero-size case
if (size == 0) {
if (option.HasFlushFlag()) {
R_TRY(this->Flush());
}
R_SUCCEED();
}
// Check the write is valid
R_UNLESS(buffer != nullptr, ResultNullptrArgument);
R_UNLESS(offset >= 0, ResultOutOfRange);
R_UNLESS(Common::CanAddWithoutOverflow<s64>(offset, size), ResultOutOfRange);
R_RETURN(this->DoWrite(offset, buffer, size, option));
}
Result SetSize(s64 size) {
R_UNLESS(size >= 0, ResultOutOfRange);
R_RETURN(this->DoSetSize(size));
}
Result OperateRange(void* dst, size_t dst_size, OperationId op_id, s64 offset, s64 size,
const void* src, size_t src_size) {
R_RETURN(this->DoOperateRange(dst, dst_size, op_id, offset, size, src, src_size));
}
Result OperateRange(OperationId op_id, s64 offset, s64 size) {
R_RETURN(this->DoOperateRange(nullptr, 0, op_id, offset, size, nullptr, 0));
}
protected:
Result DryRead(size_t* out, s64 offset, size_t size, const ReadOption& option,
OpenMode open_mode) {
// Check that we can read
R_UNLESS(static_cast<u32>(open_mode & OpenMode::Read) != 0, ResultReadNotPermitted);
// Get the file size, and validate our offset
s64 file_size = 0;
R_TRY(this->DoGetSize(std::addressof(file_size)));
R_UNLESS(offset <= file_size, ResultOutOfRange);
*out = static_cast<size_t>(std::min(file_size - offset, static_cast<s64>(size)));
R_SUCCEED();
}
Result DrySetSize(s64 size, OpenMode open_mode) {
// Check that we can write
R_UNLESS(static_cast<u32>(open_mode & OpenMode::Write) != 0, ResultWriteNotPermitted);
R_SUCCEED();
}
Result DryWrite(bool* out_append, s64 offset, size_t size, const WriteOption& option,
OpenMode open_mode) {
// Check that we can write
R_UNLESS(static_cast<u32>(open_mode & OpenMode::Write) != 0, ResultWriteNotPermitted);
// Get the file size
s64 file_size = 0;
R_TRY(this->DoGetSize(&file_size));
// Determine if we need to append
*out_append = false;
if (file_size < offset + static_cast<s64>(size)) {
R_UNLESS(static_cast<u32>(open_mode & OpenMode::AllowAppend) != 0,
ResultFileExtensionWithoutOpenModeAllowAppend);
*out_append = true;
}
R_SUCCEED();
}
private:
Result DoRead(size_t* out, s64 offset, void* buffer, size_t size, const ReadOption& option) {
const auto read_size = backend->Read(static_cast<u8*>(buffer), size, offset);
*out = read_size;
R_SUCCEED();
}
Result DoGetSize(s64* out) {
*out = backend->GetSize();
R_SUCCEED();
}
Result DoFlush() {
// Exists for SDK compatibiltity -- No need to flush file.
R_SUCCEED();
}
Result DoWrite(s64 offset, const void* buffer, size_t size, const WriteOption& option) {
const std::size_t written = backend->Write(static_cast<const u8*>(buffer), size, offset);
ASSERT_MSG(written == size,
"Could not write all bytes to file (requested={:016X}, actual={:016X}).", size,
written);
R_SUCCEED();
}
Result DoSetSize(s64 size) {
backend->Resize(size);
R_SUCCEED();
}
Result DoOperateRange(void* dst, size_t dst_size, OperationId op_id, s64 offset, s64 size,
const void* src, size_t src_size) {
R_THROW(ResultNotImplemented);
}
VirtualFile backend;
};
} // namespace FileSys::Fsa

View File

@ -0,0 +1,206 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "core/file_sys/errors.h"
#include "core/file_sys/fs_filesystem.h"
#include "core/file_sys/fs_path.h"
#include "core/file_sys/vfs/vfs_types.h"
#include "core/hle/result.h"
#include "core/hle/service/filesystem/filesystem.h"
namespace FileSys::Fsa {
class IFile;
class IDirectory;
enum class QueryId : u32 {
SetConcatenationFileAttribute = 0,
UpdateMac = 1,
IsSignedSystemPartitionOnSdCardValid = 2,
QueryUnpreparedFileInformation = 3,
};
class IFileSystem {
public:
explicit IFileSystem(VirtualDir backend_) : backend{std::move(backend_)} {}
virtual ~IFileSystem() {}
Result CreateFile(const Path& path, s64 size, CreateOption option) {
R_UNLESS(size >= 0, ResultOutOfRange);
R_RETURN(this->DoCreateFile(path, size, static_cast<int>(option)));
}
Result CreateFile(const Path& path, s64 size) {
R_RETURN(this->CreateFile(path, size, CreateOption::None));
}
Result DeleteFile(const Path& path) {
R_RETURN(this->DoDeleteFile(path));
}
Result CreateDirectory(const Path& path) {
R_RETURN(this->DoCreateDirectory(path));
}
Result DeleteDirectory(const Path& path) {
R_RETURN(this->DoDeleteDirectory(path));
}
Result DeleteDirectoryRecursively(const Path& path) {
R_RETURN(this->DoDeleteDirectoryRecursively(path));
}
Result RenameFile(const Path& old_path, const Path& new_path) {
R_RETURN(this->DoRenameFile(old_path, new_path));
}
Result RenameDirectory(const Path& old_path, const Path& new_path) {
R_RETURN(this->DoRenameDirectory(old_path, new_path));
}
Result GetEntryType(DirectoryEntryType* out, const Path& path) {
R_RETURN(this->DoGetEntryType(out, path));
}
Result OpenFile(VirtualFile* out_file, const Path& path, OpenMode mode) {
R_UNLESS(out_file != nullptr, ResultNullptrArgument);
R_UNLESS(static_cast<u32>(mode & OpenMode::ReadWrite) != 0, ResultInvalidOpenMode);
R_UNLESS(static_cast<u32>(mode & ~OpenMode::All) == 0, ResultInvalidOpenMode);
R_RETURN(this->DoOpenFile(out_file, path, mode));
}
Result OpenDirectory(VirtualDir* out_dir, const Path& path, OpenDirectoryMode mode) {
R_UNLESS(out_dir != nullptr, ResultNullptrArgument);
R_UNLESS(static_cast<u64>(mode & OpenDirectoryMode::All) != 0, ResultInvalidOpenMode);
R_UNLESS(static_cast<u64>(
mode & ~(OpenDirectoryMode::All | OpenDirectoryMode::NotRequireFileSize)) == 0,
ResultInvalidOpenMode);
R_RETURN(this->DoOpenDirectory(out_dir, path, mode));
}
Result Commit() {
R_RETURN(this->DoCommit());
}
Result GetFreeSpaceSize(s64* out, const Path& path) {
R_UNLESS(out != nullptr, ResultNullptrArgument);
R_RETURN(this->DoGetFreeSpaceSize(out, path));
}
Result GetTotalSpaceSize(s64* out, const Path& path) {
R_UNLESS(out != nullptr, ResultNullptrArgument);
R_RETURN(this->DoGetTotalSpaceSize(out, path));
}
Result CleanDirectoryRecursively(const Path& path) {
R_RETURN(this->DoCleanDirectoryRecursively(path));
}
Result GetFileTimeStampRaw(FileTimeStampRaw* out, const Path& path) {
R_UNLESS(out != nullptr, ResultNullptrArgument);
R_RETURN(this->DoGetFileTimeStampRaw(out, path));
}
Result QueryEntry(char* dst, size_t dst_size, const char* src, size_t src_size, QueryId query,
const Path& path) {
R_RETURN(this->DoQueryEntry(dst, dst_size, src, src_size, query, path));
}
// These aren't accessible as commands
Result CommitProvisionally(s64 counter) {
R_RETURN(this->DoCommitProvisionally(counter));
}
Result Rollback() {
R_RETURN(this->DoRollback());
}
Result Flush() {
R_RETURN(this->DoFlush());
}
private:
Result DoCreateFile(const Path& path, s64 size, int flags) {
R_RETURN(backend.CreateFile(path.GetString(), size));
}
Result DoDeleteFile(const Path& path) {
R_RETURN(backend.DeleteFile(path.GetString()));
}
Result DoCreateDirectory(const Path& path) {
R_RETURN(backend.CreateDirectory(path.GetString()));
}
Result DoDeleteDirectory(const Path& path) {
R_RETURN(backend.DeleteDirectory(path.GetString()));
}
Result DoDeleteDirectoryRecursively(const Path& path) {
R_RETURN(backend.DeleteDirectoryRecursively(path.GetString()));
}
Result DoRenameFile(const Path& old_path, const Path& new_path) {
R_RETURN(backend.RenameFile(old_path.GetString(), new_path.GetString()));
}
Result DoRenameDirectory(const Path& old_path, const Path& new_path) {
R_RETURN(backend.RenameDirectory(old_path.GetString(), new_path.GetString()));
}
Result DoGetEntryType(DirectoryEntryType* out, const Path& path) {
R_RETURN(backend.GetEntryType(out, path.GetString()));
}
Result DoOpenFile(VirtualFile* out_file, const Path& path, OpenMode mode) {
R_RETURN(backend.OpenFile(out_file, path.GetString(), mode));
}
Result DoOpenDirectory(VirtualDir* out_directory, const Path& path, OpenDirectoryMode mode) {
R_RETURN(backend.OpenDirectory(out_directory, path.GetString()));
}
Result DoCommit() {
R_THROW(ResultNotImplemented);
}
Result DoGetFreeSpaceSize(s64* out, const Path& path) {
R_THROW(ResultNotImplemented);
}
Result DoGetTotalSpaceSize(s64* out, const Path& path) {
R_THROW(ResultNotImplemented);
}
Result DoCleanDirectoryRecursively(const Path& path) {
R_RETURN(backend.CleanDirectoryRecursively(path.GetString()));
}
Result DoGetFileTimeStampRaw(FileTimeStampRaw* out, const Path& path) {
R_RETURN(backend.GetFileTimeStampRaw(out, path.GetString()));
}
Result DoQueryEntry(char* dst, size_t dst_size, const char* src, size_t src_size, QueryId query,
const Path& path) {
R_THROW(ResultNotImplemented);
}
// These aren't accessible as commands
Result DoCommitProvisionally(s64 counter) {
R_THROW(ResultNotImplemented);
}
Result DoRollback() {
R_THROW(ResultNotImplemented);
}
Result DoFlush() {
R_THROW(ResultNotImplemented);
}
Service::FileSystem::VfsDirectoryServiceWrapper backend;
};
} // namespace FileSys::Fsa

View File

@ -0,0 +1,36 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "core/file_sys/fs_directory.h"
namespace FileSys::Sf {
struct Path {
char str[EntryNameLengthMax + 1];
static constexpr Path Encode(const char* p) {
Path path = {};
for (size_t i = 0; i < sizeof(path) - 1; i++) {
path.str[i] = p[i];
if (p[i] == '\x00') {
break;
}
}
return path;
}
static constexpr size_t GetPathLength(const Path& path) {
size_t len = 0;
for (size_t i = 0; i < sizeof(path) - 1 && path.str[i] != '\x00'; i++) {
len++;
}
return len;
}
};
static_assert(std::is_trivially_copyable_v<Path>, "Path must be trivially copyable.");
using FspPath = Path;
} // namespace FileSys::Sf

View File

@ -3,6 +3,7 @@
#pragma once #pragma once
#include <mutex>
#include <optional> #include <optional>
#include "core/crypto/aes_util.h" #include "core/crypto/aes_util.h"

View File

@ -36,7 +36,9 @@ Result HierarchicalSha256Storage::Initialize(VirtualFile* base_storages, s32 lay
// Get the base storage size. // Get the base storage size.
m_base_storage_size = base_storages[2]->GetSize(); m_base_storage_size = base_storages[2]->GetSize();
{ {
auto size_guard = SCOPE_GUARD({ m_base_storage_size = 0; }); auto size_guard = SCOPE_GUARD {
m_base_storage_size = 0;
};
R_UNLESS(m_base_storage_size <= static_cast<s64>(HashSize) R_UNLESS(m_base_storage_size <= static_cast<s64>(HashSize)
<< m_log_size_ratio << m_log_size_ratio, << m_log_size_ratio << m_log_size_ratio,
ResultHierarchicalSha256BaseStorageTooLarge); ResultHierarchicalSha256BaseStorageTooLarge);

View File

@ -98,7 +98,9 @@ Loader::ResultStatus ProgramMetadata::Load(VirtualFile file) {
Loader::ResultStatus ProgramMetadata::Reload(VirtualFile file) { Loader::ResultStatus ProgramMetadata::Reload(VirtualFile file) {
const u64 original_program_id = aci_header.title_id; const u64 original_program_id = aci_header.title_id;
SCOPE_EXIT({ aci_header.title_id = original_program_id; }); SCOPE_EXIT {
aci_header.title_id = original_program_id;
};
return this->Load(file); return this->Load(file);
} }

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

@ -24,7 +24,9 @@ Result KClientSession::SendSyncRequest(uintptr_t address, size_t size) {
// Create a session request. // Create a session request.
KSessionRequest* request = KSessionRequest::Create(m_kernel); KSessionRequest* request = KSessionRequest::Create(m_kernel);
R_UNLESS(request != nullptr, ResultOutOfResource); R_UNLESS(request != nullptr, ResultOutOfResource);
SCOPE_EXIT({ request->Close(); }); SCOPE_EXIT {
request->Close();
};
// Initialize the request. // Initialize the request.
request->Initialize(nullptr, address, size); request->Initialize(nullptr, address, size);
@ -37,7 +39,9 @@ Result KClientSession::SendAsyncRequest(KEvent* event, uintptr_t address, size_t
// Create a session request. // Create a session request.
KSessionRequest* request = KSessionRequest::Create(m_kernel); KSessionRequest* request = KSessionRequest::Create(m_kernel);
R_UNLESS(request != nullptr, ResultOutOfResource); R_UNLESS(request != nullptr, ResultOutOfResource);
SCOPE_EXIT({ request->Close(); }); SCOPE_EXIT {
request->Close();
};
// Initialize the request. // Initialize the request.
request->Initialize(event, address, size); request->Initialize(event, address, size);

View File

@ -1305,11 +1305,11 @@ Result KPageTableBase::UnmapCodeMemory(KProcessAddress dst_address, KProcessAddr
// Ensure that we maintain the instruction cache. // Ensure that we maintain the instruction cache.
bool reprotected_pages = false; bool reprotected_pages = false;
SCOPE_EXIT({ SCOPE_EXIT {
if (reprotected_pages && any_code_pages) { if (reprotected_pages && any_code_pages) {
InvalidateInstructionCache(m_kernel, this, dst_address, size); InvalidateInstructionCache(m_kernel, this, dst_address, size);
} }
}); };
// Unmap. // Unmap.
{ {
@ -1397,7 +1397,9 @@ Result KPageTableBase::MapInsecureMemory(KProcessAddress address, size_t size) {
// Close the opened pages when we're done with them. // Close the opened pages when we're done with them.
// If the mapping succeeds, each page will gain an extra reference, otherwise they will be freed // If the mapping succeeds, each page will gain an extra reference, otherwise they will be freed
// automatically. // automatically.
SCOPE_EXIT({ pg.Close(); }); SCOPE_EXIT {
pg.Close();
};
// Clear all the newly allocated pages. // Clear all the newly allocated pages.
for (const auto& it : pg) { for (const auto& it : pg) {
@ -1603,7 +1605,9 @@ Result KPageTableBase::AllocateAndMapPagesImpl(PageLinkedList* page_list, KProce
m_kernel.MemoryManager().AllocateAndOpen(std::addressof(pg), num_pages, m_allocate_option)); m_kernel.MemoryManager().AllocateAndOpen(std::addressof(pg), num_pages, m_allocate_option));
// Ensure that the page group is closed when we're done working with it. // Ensure that the page group is closed when we're done working with it.
SCOPE_EXIT({ pg.Close(); }); SCOPE_EXIT {
pg.Close();
};
// Clear all pages. // Clear all pages.
for (const auto& it : pg) { for (const auto& it : pg) {
@ -2191,7 +2195,9 @@ Result KPageTableBase::SetHeapSize(KProcessAddress* out, size_t size) {
// Close the opened pages when we're done with them. // Close the opened pages when we're done with them.
// If the mapping succeeds, each page will gain an extra reference, otherwise they will be freed // If the mapping succeeds, each page will gain an extra reference, otherwise they will be freed
// automatically. // automatically.
SCOPE_EXIT({ pg.Close(); }); SCOPE_EXIT {
pg.Close();
};
// Clear all the newly allocated pages. // Clear all the newly allocated pages.
for (const auto& it : pg) { for (const auto& it : pg) {
@ -2592,7 +2598,9 @@ Result KPageTableBase::UnmapIoRegion(KProcessAddress dst_address, KPhysicalAddre
// Temporarily unlock ourselves, so that other operations can occur while we flush the // Temporarily unlock ourselves, so that other operations can occur while we flush the
// region. // region.
m_general_lock.Unlock(); m_general_lock.Unlock();
SCOPE_EXIT({ m_general_lock.Lock(); }); SCOPE_EXIT {
m_general_lock.Lock();
};
// Flush the region. // Flush the region.
R_ASSERT(FlushDataCache(dst_address, size)); R_ASSERT(FlushDataCache(dst_address, size));
@ -3311,10 +3319,10 @@ Result KPageTableBase::ReadIoMemoryImpl(KProcessAddress dst_addr, KPhysicalAddre
// Ensure we unmap the io memory when we're done with it. // Ensure we unmap the io memory when we're done with it.
const KPageProperties unmap_properties = const KPageProperties unmap_properties =
KPageProperties{KMemoryPermission::None, false, false, DisableMergeAttribute::None}; KPageProperties{KMemoryPermission::None, false, false, DisableMergeAttribute::None};
SCOPE_EXIT({ SCOPE_EXIT {
R_ASSERT(this->Operate(updater.GetPageList(), io_addr, map_size / PageSize, 0, false, R_ASSERT(this->Operate(updater.GetPageList(), io_addr, map_size / PageSize, 0, false,
unmap_properties, OperationType::Unmap, true)); unmap_properties, OperationType::Unmap, true));
}); };
// Read the memory. // Read the memory.
const KProcessAddress read_addr = io_addr + (GetInteger(phys_addr) & (PageSize - 1)); const KProcessAddress read_addr = io_addr + (GetInteger(phys_addr) & (PageSize - 1));
@ -3347,10 +3355,10 @@ Result KPageTableBase::WriteIoMemoryImpl(KPhysicalAddress phys_addr, KProcessAdd
// Ensure we unmap the io memory when we're done with it. // Ensure we unmap the io memory when we're done with it.
const KPageProperties unmap_properties = const KPageProperties unmap_properties =
KPageProperties{KMemoryPermission::None, false, false, DisableMergeAttribute::None}; KPageProperties{KMemoryPermission::None, false, false, DisableMergeAttribute::None};
SCOPE_EXIT({ SCOPE_EXIT {
R_ASSERT(this->Operate(updater.GetPageList(), io_addr, map_size / PageSize, 0, false, R_ASSERT(this->Operate(updater.GetPageList(), io_addr, map_size / PageSize, 0, false,
unmap_properties, OperationType::Unmap, true)); unmap_properties, OperationType::Unmap, true));
}); };
// Write the memory. // Write the memory.
const KProcessAddress write_addr = io_addr + (GetInteger(phys_addr) & (PageSize - 1)); const KProcessAddress write_addr = io_addr + (GetInteger(phys_addr) & (PageSize - 1));
@ -4491,14 +4499,14 @@ Result KPageTableBase::SetupForIpcServer(KProcessAddress* out_addr, size_t size,
// If the partial pages are mapped, an extra reference will have been opened. Otherwise, they'll // If the partial pages are mapped, an extra reference will have been opened. Otherwise, they'll
// free on scope exit. // free on scope exit.
SCOPE_EXIT({ SCOPE_EXIT {
if (start_partial_page != 0) { if (start_partial_page != 0) {
m_kernel.MemoryManager().Close(start_partial_page, 1); m_kernel.MemoryManager().Close(start_partial_page, 1);
} }
if (end_partial_page != 0) { if (end_partial_page != 0) {
m_kernel.MemoryManager().Close(end_partial_page, 1); m_kernel.MemoryManager().Close(end_partial_page, 1);
} }
}); };
ON_RESULT_FAILURE { ON_RESULT_FAILURE {
if (cur_mapped_addr != dst_addr) { if (cur_mapped_addr != dst_addr) {
@ -5166,10 +5174,10 @@ Result KPageTableBase::MapPhysicalMemory(KProcessAddress address, size_t size) {
GetCurrentProcess(m_kernel).GetId(), m_heap_fill_value)); GetCurrentProcess(m_kernel).GetId(), m_heap_fill_value));
// If we fail in the next bit (or retry), we need to cleanup the pages. // If we fail in the next bit (or retry), we need to cleanup the pages.
auto pg_guard = SCOPE_GUARD({ auto pg_guard = SCOPE_GUARD {
pg.OpenFirst(); pg.OpenFirst();
pg.Close(); pg.Close();
}); };
// Map the memory. // Map the memory.
{ {
@ -5694,7 +5702,9 @@ Result KPageTableBase::Operate(PageLinkedList* page_list, KProcessAddress virt_a
// Ensure that any pages we track are closed on exit. // Ensure that any pages we track are closed on exit.
KPageGroup pages_to_close(m_kernel, this->GetBlockInfoManager()); KPageGroup pages_to_close(m_kernel, this->GetBlockInfoManager());
SCOPE_EXIT({ pages_to_close.CloseAndReset(); }); SCOPE_EXIT {
pages_to_close.CloseAndReset();
};
// Make a page group representing the region to unmap. // Make a page group representing the region to unmap.
this->MakePageGroup(pages_to_close, virt_addr, num_pages); this->MakePageGroup(pages_to_close, virt_addr, num_pages);

View File

@ -77,7 +77,9 @@ Result TerminateChildren(KernelCore& kernel, KProcess* process,
} }
// Terminate and close the thread. // Terminate and close the thread.
SCOPE_EXIT({ cur_child->Close(); }); SCOPE_EXIT {
cur_child->Close();
};
if (const Result terminate_result = cur_child->Terminate(); if (const Result terminate_result = cur_child->Terminate();
ResultTerminationRequested == terminate_result) { ResultTerminationRequested == terminate_result) {
@ -466,11 +468,11 @@ void KProcess::DoWorkerTaskImpl() {
Result KProcess::StartTermination() { Result KProcess::StartTermination() {
// Finalize the handle table when we're done, if the process isn't immortal. // Finalize the handle table when we're done, if the process isn't immortal.
SCOPE_EXIT({ SCOPE_EXIT {
if (!m_is_immortal) { if (!m_is_immortal) {
this->FinalizeHandleTable(); this->FinalizeHandleTable();
} }
}); };
// Terminate child threads other than the current one. // Terminate child threads other than the current one.
R_RETURN(TerminateChildren(m_kernel, this, GetCurrentThreadPointer(m_kernel))); R_RETURN(TerminateChildren(m_kernel, this, GetCurrentThreadPointer(m_kernel)));
@ -964,7 +966,9 @@ Result KProcess::Run(s32 priority, size_t stack_size) {
// Create a new thread for the process. // Create a new thread for the process.
KThread* main_thread = KThread::Create(m_kernel); KThread* main_thread = KThread::Create(m_kernel);
R_UNLESS(main_thread != nullptr, ResultOutOfResource); R_UNLESS(main_thread != nullptr, ResultOutOfResource);
SCOPE_EXIT({ main_thread->Close(); }); SCOPE_EXIT {
main_thread->Close();
};
// Initialize the thread. // Initialize the thread.
R_TRY(KThread::InitializeUserThread(m_kernel.System(), main_thread, this->GetEntryPoint(), 0, R_TRY(KThread::InitializeUserThread(m_kernel.System(), main_thread, this->GetEntryPoint(), 0,
@ -1155,7 +1159,9 @@ Result KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std:
Kernel::CreateResourceLimitForProcess(m_kernel.System(), physical_memory_size); Kernel::CreateResourceLimitForProcess(m_kernel.System(), physical_memory_size);
// Ensure we maintain a clean state on exit. // Ensure we maintain a clean state on exit.
SCOPE_EXIT({ res_limit->Close(); }); SCOPE_EXIT {
res_limit->Close();
};
// Declare flags and code address. // Declare flags and code address.
Svc::CreateProcessFlag flag{}; Svc::CreateProcessFlag flag{};

View File

@ -651,11 +651,11 @@ Result ReceiveMessage(KernelCore& kernel, bool& recv_list_broken, uint64_t dst_m
// Process any special data. // Process any special data.
if (src_header.GetHasSpecialHeader()) { if (src_header.GetHasSpecialHeader()) {
// After we process, make sure we track whether the receive list is broken. // After we process, make sure we track whether the receive list is broken.
SCOPE_EXIT({ SCOPE_EXIT {
if (offset > dst_recv_list_idx) { if (offset > dst_recv_list_idx) {
recv_list_broken = true; recv_list_broken = true;
} }
}); };
// Process special data. // Process special data.
R_TRY(ProcessMessageSpecialData<false>(offset, dst_process, src_process, src_thread, R_TRY(ProcessMessageSpecialData<false>(offset, dst_process, src_process, src_thread,
@ -665,11 +665,11 @@ Result ReceiveMessage(KernelCore& kernel, bool& recv_list_broken, uint64_t dst_m
// Process any pointer buffers. // Process any pointer buffers.
for (auto i = 0; i < src_header.GetPointerCount(); ++i) { for (auto i = 0; i < src_header.GetPointerCount(); ++i) {
// After we process, make sure we track whether the receive list is broken. // After we process, make sure we track whether the receive list is broken.
SCOPE_EXIT({ SCOPE_EXIT {
if (offset > dst_recv_list_idx) { if (offset > dst_recv_list_idx) {
recv_list_broken = true; recv_list_broken = true;
} }
}); };
R_TRY(ProcessReceiveMessagePointerDescriptors( R_TRY(ProcessReceiveMessagePointerDescriptors(
offset, pointer_key, dst_page_table, src_page_table, dst_msg, src_msg, dst_recv_list, offset, pointer_key, dst_page_table, src_page_table, dst_msg, src_msg, dst_recv_list,
@ -680,11 +680,11 @@ Result ReceiveMessage(KernelCore& kernel, bool& recv_list_broken, uint64_t dst_m
// Process any map alias buffers. // Process any map alias buffers.
for (auto i = 0; i < src_header.GetMapAliasCount(); ++i) { for (auto i = 0; i < src_header.GetMapAliasCount(); ++i) {
// After we process, make sure we track whether the receive list is broken. // After we process, make sure we track whether the receive list is broken.
SCOPE_EXIT({ SCOPE_EXIT {
if (offset > dst_recv_list_idx) { if (offset > dst_recv_list_idx) {
recv_list_broken = true; recv_list_broken = true;
} }
}); };
// We process in order send, recv, exch. Buffers after send (recv/exch) are ReadWrite. // We process in order send, recv, exch. Buffers after send (recv/exch) are ReadWrite.
const KMemoryPermission perm = (i >= src_header.GetSendCount()) const KMemoryPermission perm = (i >= src_header.GetSendCount())
@ -702,11 +702,11 @@ Result ReceiveMessage(KernelCore& kernel, bool& recv_list_broken, uint64_t dst_m
// Process any raw data. // Process any raw data.
if (const auto raw_count = src_header.GetRawCount(); raw_count != 0) { if (const auto raw_count = src_header.GetRawCount(); raw_count != 0) {
// After we process, make sure we track whether the receive list is broken. // After we process, make sure we track whether the receive list is broken.
SCOPE_EXIT({ SCOPE_EXIT {
if (offset + raw_count > dst_recv_list_idx) { if (offset + raw_count > dst_recv_list_idx) {
recv_list_broken = true; recv_list_broken = true;
} }
}); };
// Get the offset and size. // Get the offset and size.
const size_t offset_words = offset * sizeof(u32); const size_t offset_words = offset * sizeof(u32);
@ -1124,7 +1124,9 @@ Result KServerSession::ReceiveRequest(uintptr_t server_message, uintptr_t server
client_thread->Open(); client_thread->Open();
} }
SCOPE_EXIT({ client_thread->Close(); }); SCOPE_EXIT {
client_thread->Close();
};
// Set the request as our current. // Set the request as our current.
m_current_request = request; m_current_request = request;
@ -1174,7 +1176,9 @@ Result KServerSession::ReceiveRequest(uintptr_t server_message, uintptr_t server
// Reply to the client. // Reply to the client.
{ {
// After we reply, close our reference to the request. // After we reply, close our reference to the request.
SCOPE_EXIT({ request->Close(); }); SCOPE_EXIT {
request->Close();
};
// Get the event to check whether the request is async. // Get the event to check whether the request is async.
if (KEvent* event = request->GetEvent(); event != nullptr) { if (KEvent* event = request->GetEvent(); event != nullptr) {
@ -1236,7 +1240,9 @@ Result KServerSession::SendReply(uintptr_t server_message, uintptr_t server_buff
} }
// Close reference to the request once we're done processing it. // Close reference to the request once we're done processing it.
SCOPE_EXIT({ request->Close(); }); SCOPE_EXIT {
request->Close();
};
// Extract relevant information from the request. // Extract relevant information from the request.
const uint64_t client_message = request->GetAddress(); const uint64_t client_message = request->GetAddress();
@ -1394,7 +1400,9 @@ void KServerSession::CleanupRequests() {
} }
// Close a reference to the request once it's cleaned up. // Close a reference to the request once it's cleaned up.
SCOPE_EXIT({ request->Close(); }); SCOPE_EXIT {
request->Close();
};
// Extract relevant information from the request. // Extract relevant information from the request.
const uint64_t client_message = request->GetAddress(); const uint64_t client_message = request->GetAddress();
@ -1491,7 +1499,9 @@ void KServerSession::OnClientClosed() {
ASSERT(thread != nullptr); ASSERT(thread != nullptr);
// Ensure that we close the request when done. // Ensure that we close the request when done.
SCOPE_EXIT({ request->Close(); }); SCOPE_EXIT {
request->Close();
};
// If we're terminating, close a reference to the thread and event. // If we're terminating, close a reference to the thread and event.
if (terminate) { if (terminate) {

View File

@ -21,7 +21,9 @@ Result KThreadLocalPage::Initialize(KernelCore& kernel, KProcess* process) {
// Allocate a new page. // Allocate a new page.
KPageBuffer* page_buf = KPageBuffer::Allocate(kernel); KPageBuffer* page_buf = KPageBuffer::Allocate(kernel);
R_UNLESS(page_buf != nullptr, ResultOutOfMemory); R_UNLESS(page_buf != nullptr, ResultOutOfMemory);
auto page_buf_guard = SCOPE_GUARD({ KPageBuffer::Free(kernel, page_buf); }); auto page_buf_guard = SCOPE_GUARD {
KPageBuffer::Free(kernel, page_buf);
};
// Map the address in. // Map the address in.
const auto phys_addr = kernel.System().DeviceMemory().GetPhysicalAddr(page_buf); const auto phys_addr = kernel.System().DeviceMemory().GetPhysicalAddr(page_buf);

View File

@ -24,7 +24,9 @@ Result KTransferMemory::Initialize(KProcessAddress addr, std::size_t size,
// Construct the page group, guarding to make sure our state is valid on exit. // Construct the page group, guarding to make sure our state is valid on exit.
m_page_group.emplace(m_kernel, page_table.GetBlockInfoManager()); m_page_group.emplace(m_kernel, page_table.GetBlockInfoManager());
auto pg_guard = SCOPE_GUARD({ m_page_group.reset(); }); auto pg_guard = SCOPE_GUARD {
m_page_group.reset();
};
// Lock the memory. // Lock the memory.
R_TRY(page_table.LockForTransferMemory(std::addressof(*m_page_group), addr, size, R_TRY(page_table.LockForTransferMemory(std::addressof(*m_page_group), addr, size,

View File

@ -109,7 +109,9 @@ struct KernelCore::Impl {
void Shutdown() { void Shutdown() {
is_shutting_down.store(true, std::memory_order_relaxed); is_shutting_down.store(true, std::memory_order_relaxed);
SCOPE_EXIT({ is_shutting_down.store(false, std::memory_order_relaxed); }); SCOPE_EXIT {
is_shutting_down.store(false, std::memory_order_relaxed);
};
CloseServices(); CloseServices();
@ -1080,7 +1082,9 @@ std::jthread KernelCore::RunOnHostCoreProcess(std::string&& process_name,
process->Initialize(Svc::CreateProcessParameter{}, GetSystemResourceLimit(), false))); process->Initialize(Svc::CreateProcessParameter{}, GetSystemResourceLimit(), false)));
// Ensure that we don't hold onto any extra references. // Ensure that we don't hold onto any extra references.
SCOPE_EXIT({ process->Close(); }); SCOPE_EXIT {
process->Close();
};
// Register the new process. // Register the new process.
KProcess::Register(*this, process); KProcess::Register(*this, process);
@ -1108,7 +1112,9 @@ void KernelCore::RunOnGuestCoreProcess(std::string&& process_name, std::function
process->Initialize(Svc::CreateProcessParameter{}, GetSystemResourceLimit(), false))); process->Initialize(Svc::CreateProcessParameter{}, GetSystemResourceLimit(), false)));
// Ensure that we don't hold onto any extra references. // Ensure that we don't hold onto any extra references.
SCOPE_EXIT({ process->Close(); }); SCOPE_EXIT {
process->Close();
};
// Register the new process. // Register the new process.
KProcess::Register(*this, process); KProcess::Register(*this, process);

View File

@ -45,7 +45,9 @@ Result CreateCodeMemory(Core::System& system, Handle* out, u64 address, uint64_t
KCodeMemory* code_mem = KCodeMemory::Create(kernel); KCodeMemory* code_mem = KCodeMemory::Create(kernel);
R_UNLESS(code_mem != nullptr, ResultOutOfResource); R_UNLESS(code_mem != nullptr, ResultOutOfResource);
SCOPE_EXIT({ code_mem->Close(); }); SCOPE_EXIT {
code_mem->Close();
};
// Verify that the region is in range. // Verify that the region is in range.
R_UNLESS(GetCurrentProcess(system.Kernel()).GetPageTable().Contains(address, size), R_UNLESS(GetCurrentProcess(system.Kernel()).GetPageTable().Contains(address, size),

View File

@ -28,7 +28,9 @@ Result CreateDeviceAddressSpace(Core::System& system, Handle* out, uint64_t das_
// Create the device address space. // Create the device address space.
KDeviceAddressSpace* das = KDeviceAddressSpace::Create(system.Kernel()); KDeviceAddressSpace* das = KDeviceAddressSpace::Create(system.Kernel());
R_UNLESS(das != nullptr, ResultOutOfResource); R_UNLESS(das != nullptr, ResultOutOfResource);
SCOPE_EXIT({ das->Close(); }); SCOPE_EXIT {
das->Close();
};
// Initialize the device address space. // Initialize the device address space.
R_TRY(das->Initialize(das_address, das_size)); R_TRY(das->Initialize(das_address, das_size));

View File

@ -72,10 +72,10 @@ Result CreateEvent(Core::System& system, Handle* out_write, Handle* out_read) {
event_reservation.Commit(); event_reservation.Commit();
// Ensure that we clean up the event (and its only references are handle table) on function end. // Ensure that we clean up the event (and its only references are handle table) on function end.
SCOPE_EXIT({ SCOPE_EXIT {
event->GetReadableEvent().Close(); event->GetReadableEvent().Close();
event->Close(); event->Close();
}); };
// Register the event. // Register the event.
KEvent::Register(kernel, event); KEvent::Register(kernel, event);

View File

@ -129,11 +129,11 @@ Result ReplyAndReceiveImpl(KernelCore& kernel, int32_t* out_index, uintptr_t mes
} }
// Ensure handles are closed when we're done. // Ensure handles are closed when we're done.
SCOPE_EXIT({ SCOPE_EXIT {
for (auto i = 0; i < num_handles; ++i) { for (auto i = 0; i < num_handles; ++i) {
objs[i]->Close(); objs[i]->Close();
} }
}); };
R_RETURN(ReplyAndReceiveImpl(kernel, out_index, message, buffer_size, message_paddr, objs, R_RETURN(ReplyAndReceiveImpl(kernel, out_index, message, buffer_size, message_paddr, objs,
num_handles, reply_target, timeout_ns)); num_handles, reply_target, timeout_ns));
@ -208,10 +208,10 @@ Result SendAsyncRequestWithUserBuffer(Core::System& system, Handle* out_event_ha
event_reservation.Commit(); event_reservation.Commit();
// At end of scope, kill the standing references to the sub events. // At end of scope, kill the standing references to the sub events.
SCOPE_EXIT({ SCOPE_EXIT {
event->GetReadableEvent().Close(); event->GetReadableEvent().Close();
event->Close(); event->Close();
}); };
// Register the event. // Register the event.
KEvent::Register(system.Kernel(), event); KEvent::Register(system.Kernel(), event);

View File

@ -68,10 +68,10 @@ Result CreatePort(Core::System& system, Handle* out_server, Handle* out_client,
port->Initialize(max_sessions, is_light, name); port->Initialize(max_sessions, is_light, name);
// Ensure that we clean up the port (and its only references are handle table) on function end. // Ensure that we clean up the port (and its only references are handle table) on function end.
SCOPE_EXIT({ SCOPE_EXIT {
port->GetServerPort().Close(); port->GetServerPort().Close();
port->GetClientPort().Close(); port->GetClientPort().Close();
}); };
// Register the port. // Register the port.
KPort::Register(kernel, port); KPort::Register(kernel, port);
@ -150,10 +150,10 @@ Result ManageNamedPort(Core::System& system, Handle* out_server_handle, uint64_t
KPort::Register(system.Kernel(), port); KPort::Register(system.Kernel(), port);
// Ensure that our only reference to the port is in the handle table when we're done. // Ensure that our only reference to the port is in the handle table when we're done.
SCOPE_EXIT({ SCOPE_EXIT {
port->GetClientPort().Close(); port->GetClientPort().Close();
port->GetServerPort().Close(); port->GetServerPort().Close();
}); };
// Register the handle in the table. // Register the handle in the table.
R_TRY(handle_table.Add(out_server_handle, std::addressof(port->GetServerPort()))); R_TRY(handle_table.Add(out_server_handle, std::addressof(port->GetServerPort())));

View File

@ -18,7 +18,9 @@ Result CreateResourceLimit(Core::System& system, Handle* out_handle) {
R_UNLESS(resource_limit != nullptr, ResultOutOfResource); R_UNLESS(resource_limit != nullptr, ResultOutOfResource);
// Ensure we don't leak a reference to the limit. // Ensure we don't leak a reference to the limit.
SCOPE_EXIT({ resource_limit->Close(); }); SCOPE_EXIT {
resource_limit->Close();
};
// Initialize the resource limit. // Initialize the resource limit.
resource_limit->Initialize(); resource_limit->Initialize();

View File

@ -69,10 +69,10 @@ Result CreateSession(Core::System& system, Handle* out_server, Handle* out_clien
// Ensure that we clean up the session (and its only references are handle table) on function // Ensure that we clean up the session (and its only references are handle table) on function
// end. // end.
SCOPE_EXIT({ SCOPE_EXIT {
session->GetClientSession().Close(); session->GetClientSession().Close();
session->GetServerSession().Close(); session->GetServerSession().Close();
}); };
// Register the session. // Register the session.
T::Register(system.Kernel(), session); T::Register(system.Kernel(), session);

View File

@ -78,11 +78,11 @@ Result WaitSynchronization(Core::System& system, int32_t* out_index, u64 user_ha
} }
// Ensure handles are closed when we're done. // Ensure handles are closed when we're done.
SCOPE_EXIT({ SCOPE_EXIT {
for (auto i = 0; i < num_handles; ++i) { for (auto i = 0; i < num_handles; ++i) {
objs[i]->Close(); objs[i]->Close();
} }
}); };
// Convert the timeout from nanoseconds to ticks. // Convert the timeout from nanoseconds to ticks.
s64 timeout; s64 timeout;

View File

@ -51,7 +51,9 @@ Result CreateThread(Core::System& system, Handle* out_handle, u64 entry_point, u
// Create the thread. // Create the thread.
KThread* thread = KThread::Create(kernel); KThread* thread = KThread::Create(kernel);
R_UNLESS(thread != nullptr, ResultOutOfResource) R_UNLESS(thread != nullptr, ResultOutOfResource)
SCOPE_EXIT({ thread->Close(); }); SCOPE_EXIT {
thread->Close();
};
// Initialize the thread. // Initialize the thread.
{ {

View File

@ -52,7 +52,9 @@ Result CreateTransferMemory(Core::System& system, Handle* out, u64 address, u64
R_UNLESS(trmem != nullptr, ResultOutOfResource); R_UNLESS(trmem != nullptr, ResultOutOfResource);
// Ensure the only reference is in the handle table when we're done. // Ensure the only reference is in the handle table when we're done.
SCOPE_EXIT({ trmem->Close(); }); SCOPE_EXIT {
trmem->Close();
};
// Ensure that the region is in range. // Ensure that the region is in range.
R_UNLESS(process.GetPageTable().Contains(address, size), ResultInvalidCurrentMemory); R_UNLESS(process.GetPageTable().Contains(address, size), ResultInvalidCurrentMemory);

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