summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/android/app/src/main/AndroidManifest.xml1
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt224
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/YuzuApplication.kt2
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt45
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/DriverAdapter.kt26
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/FolderAdapter.kt10
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt11
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GamePropertiesAdapter.kt39
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/HomeSettingAdapter.kt25
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/InstallableAdapter.kt14
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/LicenseAdapter.kt4
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/SetupAdapter.kt6
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/input/NativeInput.kt416
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/input/YuzuInputDevice.kt93
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/input/YuzuVibrator.kt76
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/input/model/AnalogDirection.kt11
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/input/model/ButtonName.kt19
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/input/model/InputType.kt13
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/input/model/NativeAnalog.kt14
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/input/model/NativeButton.kt38
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/input/model/NativeTrigger.kt10
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/input/model/NpadStyleIndex.kt30
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/input/model/PlayerInput.kt83
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/Settings.kt15
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/AnalogInputSetting.kt31
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/ButtonInputSetting.kt29
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/DateTimeSetting.kt9
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/HeaderSetting.kt7
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/InputProfileSetting.kt32
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/InputSetting.kt134
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/IntSingleChoiceSetting.kt38
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/ModifierInputSetting.kt31
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/RunnableSetting.kt11
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt233
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SingleChoiceSetting.kt14
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SliderSetting.kt15
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/StringSingleChoiceSetting.kt11
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SubmenuSetting.kt10
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SwitchSetting.kt9
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/InputDialogFragment.kt300
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/InputProfileAdapter.kt68
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/InputProfileDialogFragment.kt148
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/NewInputProfileDialogFragment.kt79
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivity.kt58
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsAdapter.kt247
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsDialogFragment.kt (renamed from src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SettingsDialogFragment.kt)97
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragment.kt85
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt794
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsSearchFragment.kt (renamed from src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SettingsSearchFragment.kt)32
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsViewModel.kt (renamed from src/android/app/src/main/java/org/yuzu/yuzu_emu/model/SettingsViewModel.kt)43
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/DateTimeViewHolder.kt24
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/HeaderViewHolder.kt2
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/InputProfileViewHolder.kt34
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/InputViewHolder.kt60
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/RunnableViewHolder.kt26
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SingleChoiceViewHolder.kt81
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SliderViewHolder.kt23
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SubmenuViewHolder.kt29
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SwitchSettingViewHolder.kt24
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AddonsFragment.kt91
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/CoreErrorDialogFragment.kt47
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/DriverManagerFragment.kt18
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/DriversLoadingDialogFragment.kt13
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt229
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GameFoldersFragment.kt12
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GameInfoFragment.kt3
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GamePropertiesFragment.kt46
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt14
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/InstallableFragment.kt16
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/ProgressDialogFragment.kt102
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SearchFragment.kt52
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SetupFragment.kt46
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlay.kt101
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlayDrawableButton.kt7
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlayDrawableDpad.kt25
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlayDrawableJoystick.kt15
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/GamesFragment.kt71
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt67
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/InputHandler.kt456
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/LifecycleUtils.kt38
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/NativeConfig.kt15
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/NfcReader.kt6
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ParamPackage.kt141
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ViewUtils.kt33
-rw-r--r--src/android/app/src/main/jni/CMakeLists.txt1
-rw-r--r--src/android/app/src/main/jni/android_config.cpp141
-rw-r--r--src/android/app/src/main/jni/android_config.h7
-rw-r--r--src/android/app/src/main/jni/emu_window/emu_window.cpp43
-rw-r--r--src/android/app/src/main/jni/emu_window/emu_window.h15
-rw-r--r--src/android/app/src/main/jni/native.cpp175
-rw-r--r--src/android/app/src/main/jni/native.h5
-rw-r--r--src/android/app/src/main/jni/native_config.cpp117
-rw-r--r--src/android/app/src/main/jni/native_input.cpp629
-rw-r--r--src/android/app/src/main/res/drawable/button_anim.xml142
-rw-r--r--src/android/app/src/main/res/drawable/ic_controller_disconnected.xml9
-rw-r--r--src/android/app/src/main/res/drawable/ic_more_vert.xml9
-rw-r--r--src/android/app/src/main/res/drawable/ic_new_label.xml9
-rw-r--r--src/android/app/src/main/res/drawable/ic_overlay.xml21
-rw-r--r--src/android/app/src/main/res/drawable/ic_share.xml9
-rw-r--r--src/android/app/src/main/res/drawable/stick_one_direction_anim.xml118
-rw-r--r--src/android/app/src/main/res/drawable/stick_two_direction_anim.xml173
-rw-r--r--src/android/app/src/main/res/layout-ldrtl/list_item_setting_input.xml63
-rw-r--r--src/android/app/src/main/res/layout/card_driver_option.xml9
-rw-r--r--src/android/app/src/main/res/layout/card_folder.xml3
-rw-r--r--src/android/app/src/main/res/layout/card_game.xml3
-rw-r--r--src/android/app/src/main/res/layout/card_simple_outlined.xml3
-rw-r--r--src/android/app/src/main/res/layout/dialog_input_profiles.xml6
-rw-r--r--src/android/app/src/main/res/layout/dialog_mapping.xml26
-rw-r--r--src/android/app/src/main/res/layout/fragment_game_properties.xml3
-rw-r--r--src/android/app/src/main/res/layout/list_item_input_profile.xml74
-rw-r--r--src/android/app/src/main/res/layout/list_item_setting_input.xml63
-rw-r--r--src/android/app/src/main/res/menu/menu_in_game.xml7
-rw-r--r--src/android/app/src/main/res/menu/menu_input_options.xml34
-rw-r--r--src/android/app/src/main/res/navigation/settings_navigation.xml2
-rw-r--r--src/android/app/src/main/res/values-w600dp/dimens.xml2
-rw-r--r--src/android/app/src/main/res/values/dimens.xml2
-rw-r--r--src/android/app/src/main/res/values/strings.xml93
-rw-r--r--src/audio_core/sink/cubeb_sink.cpp4
-rw-r--r--src/audio_core/sink/sink_stream.cpp4
-rw-r--r--src/common/android/id_cache.cpp163
-rw-r--r--src/common/android/id_cache.h24
-rw-r--r--src/common/demangle.cpp4
-rw-r--r--src/common/host_memory.cpp4
-rw-r--r--src/common/page_table.cpp4
-rw-r--r--src/common/scope_exit.h66
-rw-r--r--src/common/settings_input.h4
-rw-r--r--src/core/CMakeLists.txt181
-rw-r--r--src/core/core.cpp21
-rw-r--r--src/core/cpu_manager.cpp4
-rw-r--r--src/core/device_memory_manager.inc4
-rw-r--r--src/core/file_sys/control_metadata.h4
-rw-r--r--src/core/file_sys/fs_directory.h4
-rw-r--r--src/core/file_sys/fs_path_utility.h12
-rw-r--r--src/core/file_sys/fssystem/fssystem_hierarchical_sha256_storage.cpp4
-rw-r--r--src/core/file_sys/program_metadata.cpp4
-rw-r--r--src/core/file_sys/system_archive/shared_font.cpp2
-rw-r--r--src/core/hle/kernel/k_client_session.cpp8
-rw-r--r--src/core/hle/kernel/k_page_table_base.cpp40
-rw-r--r--src/core/hle/kernel/k_process.cpp16
-rw-r--r--src/core/hle/kernel/k_server_session.cpp36
-rw-r--r--src/core/hle/kernel/k_thread.h4
-rw-r--r--src/core/hle/kernel/k_thread_local_page.cpp4
-rw-r--r--src/core/hle/kernel/k_transfer_memory.cpp4
-rw-r--r--src/core/hle/kernel/kernel.cpp69
-rw-r--r--src/core/hle/kernel/kernel.h4
-rw-r--r--src/core/hle/kernel/svc/svc_code_memory.cpp4
-rw-r--r--src/core/hle/kernel/svc/svc_device_address_space.cpp4
-rw-r--r--src/core/hle/kernel/svc/svc_event.cpp4
-rw-r--r--src/core/hle/kernel/svc/svc_ipc.cpp8
-rw-r--r--src/core/hle/kernel/svc/svc_port.cpp8
-rw-r--r--src/core/hle/kernel/svc/svc_resource_limit.cpp4
-rw-r--r--src/core/hle/kernel/svc/svc_session.cpp4
-rw-r--r--src/core/hle/kernel/svc/svc_synchronization.cpp4
-rw-r--r--src/core/hle/kernel/svc/svc_thread.cpp4
-rw-r--r--src/core/hle/kernel/svc/svc_transfer_memory.cpp4
-rw-r--r--src/core/hle/service/acc/profile_manager.cpp5
-rw-r--r--src/core/hle/service/am/am.cpp10
-rw-r--r--src/core/hle/service/am/am.h6
-rw-r--r--src/core/hle/service/am/applet.h6
-rw-r--r--src/core/hle/service/am/applet_data_broker.cpp4
-rw-r--r--src/core/hle/service/am/applet_manager.cpp18
-rw-r--r--src/core/hle/service/am/display_layer_manager.cpp151
-rw-r--r--src/core/hle/service/am/display_layer_manager.h62
-rw-r--r--src/core/hle/service/am/frontend/applet_web_browser.cpp2
-rw-r--r--src/core/hle/service/am/library_applet_storage.cpp4
-rw-r--r--src/core/hle/service/am/managed_layer_holder.cpp59
-rw-r--r--src/core/hle/service/am/managed_layer_holder.h32
-rw-r--r--src/core/hle/service/am/process.cpp4
-rw-r--r--src/core/hle/service/am/service/all_system_applet_proxies_service.cpp13
-rw-r--r--src/core/hle/service/am/service/all_system_applet_proxies_service.h8
-rw-r--r--src/core/hle/service/am/service/application_functions.cpp30
-rw-r--r--src/core/hle/service/am/service/application_functions.h1
-rw-r--r--src/core/hle/service/am/service/application_proxy.cpp9
-rw-r--r--src/core/hle/service/am/service/application_proxy.h3
-rw-r--r--src/core/hle/service/am/service/application_proxy_service.cpp7
-rw-r--r--src/core/hle/service/am/service/application_proxy_service.h7
-rw-r--r--src/core/hle/service/am/service/display_controller.cpp6
-rw-r--r--src/core/hle/service/am/service/library_applet_creator.cpp2
-rw-r--r--src/core/hle/service/am/service/library_applet_proxy.cpp10
-rw-r--r--src/core/hle/service/am/service/library_applet_proxy.h3
-rw-r--r--src/core/hle/service/am/service/library_applet_self_accessor.cpp17
-rw-r--r--src/core/hle/service/am/service/library_applet_self_accessor.h2
-rw-r--r--src/core/hle/service/am/service/self_controller.cpp53
-rw-r--r--src/core/hle/service/am/service/self_controller.h3
-rw-r--r--src/core/hle/service/am/service/system_applet_proxy.cpp10
-rw-r--r--src/core/hle/service/am/service/system_applet_proxy.h3
-rw-r--r--src/core/hle/service/am/service/window_controller.cpp2
-rw-r--r--src/core/hle/service/am/system_buffer_manager.cpp80
-rw-r--r--src/core/hle/service/am/system_buffer_manager.h52
-rw-r--r--src/core/hle/service/audio/audctl.cpp201
-rw-r--r--src/core/hle/service/audio/audctl.h50
-rw-r--r--src/core/hle/service/audio/audio.cpp4
-rw-r--r--src/core/hle/service/audio/audio_controller.cpp174
-rw-r--r--src/core/hle/service/audio/audio_controller.h58
-rw-r--r--src/core/hle/service/caps/caps_a.cpp11
-rw-r--r--src/core/hle/service/caps/caps_a.h3
-rw-r--r--src/core/hle/service/erpt/erpt.cpp12
-rw-r--r--src/core/hle/service/filesystem/fsp/fsp_srv.cpp9
-rw-r--r--src/core/hle/service/filesystem/fsp/fsp_srv.h1
-rw-r--r--src/core/hle/service/glue/time/manager.cpp4
-rw-r--r--src/core/hle/service/glue/time/manager.h1
-rw-r--r--src/core/hle/service/glue/time/static.cpp41
-rw-r--r--src/core/hle/service/glue/time/time_zone.cpp36
-rw-r--r--src/core/hle/service/ns/account_proxy_interface.cpp21
-rw-r--r--src/core/hle/service/ns/account_proxy_interface.h16
-rw-r--r--src/core/hle/service/ns/application_manager_interface.cpp519
-rw-r--r--src/core/hle/service/ns/application_manager_interface.h62
-rw-r--r--src/core/hle/service/ns/application_version_interface.cpp33
-rw-r--r--src/core/hle/service/ns/application_version_interface.h16
-rw-r--r--src/core/hle/service/ns/content_management_interface.cpp72
-rw-r--r--src/core/hle/service/ns/content_management_interface.h25
-rw-r--r--src/core/hle/service/ns/develop_interface.cpp38
-rw-r--r--src/core/hle/service/ns/develop_interface.h16
-rw-r--r--src/core/hle/service/ns/document_interface.cpp38
-rw-r--r--src/core/hle/service/ns/document_interface.h22
-rw-r--r--src/core/hle/service/ns/download_task_interface.cpp39
-rw-r--r--src/core/hle/service/ns/download_task_interface.h20
-rw-r--r--src/core/hle/service/ns/dynamic_rights_interface.cpp62
-rw-r--r--src/core/hle/service/ns/dynamic_rights_interface.h22
-rw-r--r--src/core/hle/service/ns/ecommerce_interface.cpp27
-rw-r--r--src/core/hle/service/ns/ecommerce_interface.h16
-rw-r--r--src/core/hle/service/ns/factory_reset_interface.cpp27
-rw-r--r--src/core/hle/service/ns/factory_reset_interface.h16
-rw-r--r--src/core/hle/service/ns/ns.cpp903
-rw-r--r--src/core/hle/service/ns/ns.h133
-rw-r--r--src/core/hle/service/ns/ns_results.h (renamed from src/core/hle/service/ns/errors.h)0
-rw-r--r--src/core/hle/service/ns/ns_types.h111
-rw-r--r--src/core/hle/service/ns/pdm_qry.cpp67
-rw-r--r--src/core/hle/service/ns/platform_service_manager.cpp (renamed from src/core/hle/service/ns/iplatform_service_manager.cpp)130
-rw-r--r--src/core/hle/service/ns/platform_service_manager.h (renamed from src/core/hle/service/ns/iplatform_service_manager.h)33
-rw-r--r--src/core/hle/service/ns/query_service.cpp57
-rw-r--r--src/core/hle/service/ns/query_service.h (renamed from src/core/hle/service/ns/pdm_qry.h)12
-rw-r--r--src/core/hle/service/ns/read_only_application_control_data_interface.cpp122
-rw-r--r--src/core/hle/service/ns/read_only_application_control_data_interface.h30
-rw-r--r--src/core/hle/service/ns/read_only_application_record_interface.cpp38
-rw-r--r--src/core/hle/service/ns/read_only_application_record_interface.h22
-rw-r--r--src/core/hle/service/ns/service_getter_interface.cpp120
-rw-r--r--src/core/hle/service/ns/service_getter_interface.h47
-rw-r--r--src/core/hle/service/ns/system_update_control.cpp44
-rw-r--r--src/core/hle/service/ns/system_update_control.h16
-rw-r--r--src/core/hle/service/ns/system_update_interface.cpp61
-rw-r--r--src/core/hle/service/ns/system_update_interface.h38
-rw-r--r--src/core/hle/service/ns/vulnerability_manager_interface.cpp31
-rw-r--r--src/core/hle/service/ns/vulnerability_manager_interface.h21
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp4
-rw-r--r--src/core/hle/service/nvdrv/nvdrv.cpp3
-rw-r--r--src/core/hle/service/nvdrv/nvdrv.h9
-rw-r--r--src/core/hle/service/nvdrv/nvdrv_interface.cpp10
-rw-r--r--src/core/hle/service/nvdrv/nvdrv_interface.h4
-rw-r--r--src/core/hle/service/nvnflinger/binder.h23
-rw-r--r--src/core/hle/service/nvnflinger/buffer_item_consumer.cpp2
-rw-r--r--src/core/hle/service/nvnflinger/buffer_item_consumer.h2
-rw-r--r--src/core/hle/service/nvnflinger/buffer_queue_consumer.cpp76
-rw-r--r--src/core/hle/service/nvnflinger/buffer_queue_consumer.h10
-rw-r--r--src/core/hle/service/nvnflinger/buffer_queue_producer.cpp32
-rw-r--r--src/core/hle/service/nvnflinger/buffer_queue_producer.h8
-rw-r--r--src/core/hle/service/nvnflinger/consumer_base.cpp2
-rw-r--r--src/core/hle/service/nvnflinger/consumer_base.h4
-rw-r--r--src/core/hle/service/nvnflinger/display.h55
-rw-r--r--src/core/hle/service/nvnflinger/hardware_composer.cpp65
-rw-r--r--src/core/hle/service/nvnflinger/hardware_composer.h20
-rw-r--r--src/core/hle/service/nvnflinger/hos_binder_driver.cpp (renamed from src/core/hle/service/vi/hos_binder_driver.cpp)31
-rw-r--r--src/core/hle/service/nvnflinger/hos_binder_driver.h (renamed from src/core/hle/service/vi/hos_binder_driver.h)30
-rw-r--r--src/core/hle/service/nvnflinger/hos_binder_driver_server.cpp22
-rw-r--r--src/core/hle/service/nvnflinger/hos_binder_driver_server.h16
-rw-r--r--src/core/hle/service/nvnflinger/nvnflinger.cpp333
-rw-r--r--src/core/hle/service/nvnflinger/nvnflinger.h166
-rw-r--r--src/core/hle/service/nvnflinger/surface_flinger.cpp124
-rw-r--r--src/core/hle/service/nvnflinger/surface_flinger.h65
-rw-r--r--src/core/hle/service/psc/time/static.cpp33
-rw-r--r--src/core/hle/service/psc/time/steady_clock.cpp25
-rw-r--r--src/core/hle/service/psc/time/system_clock.cpp8
-rw-r--r--src/core/hle/service/psc/time/time_zone_service.cpp32
-rw-r--r--src/core/hle/service/server_manager.cpp8
-rw-r--r--src/core/hle/service/service.cpp136
-rw-r--r--src/core/hle/service/service.h21
-rw-r--r--src/core/hle/service/services.cpp136
-rw-r--r--src/core/hle/service/services.h22
-rw-r--r--src/core/hle/service/vi/application_display_service.cpp123
-rw-r--r--src/core/hle/service/vi/application_display_service.h34
-rw-r--r--src/core/hle/service/vi/application_root_service.cpp13
-rw-r--r--src/core/hle/service/vi/application_root_service.h12
-rw-r--r--src/core/hle/service/vi/conductor.cpp114
-rw-r--r--src/core/hle/service/vi/conductor.h57
-rw-r--r--src/core/hle/service/vi/container.cpp228
-rw-r--r--src/core/hle/service/vi/container.h92
-rw-r--r--src/core/hle/service/vi/display.h44
-rw-r--r--src/core/hle/service/vi/display/vi_display.cpp143
-rw-r--r--src/core/hle/service/vi/display/vi_display.h143
-rw-r--r--src/core/hle/service/vi/display_list.h83
-rw-r--r--src/core/hle/service/vi/layer.h79
-rw-r--r--src/core/hle/service/vi/layer/vi_layer.cpp18
-rw-r--r--src/core/hle/service/vi/layer/vi_layer.h118
-rw-r--r--src/core/hle/service/vi/layer_list.h69
-rw-r--r--src/core/hle/service/vi/manager_display_service.cpp46
-rw-r--r--src/core/hle/service/vi/manager_display_service.h21
-rw-r--r--src/core/hle/service/vi/manager_root_service.cpp14
-rw-r--r--src/core/hle/service/vi/manager_root_service.h14
-rw-r--r--src/core/hle/service/vi/service_creator.cpp5
-rw-r--r--src/core/hle/service/vi/service_creator.h9
-rw-r--r--src/core/hle/service/vi/shared_buffer_manager.cpp (renamed from src/core/hle/service/nvnflinger/fb_share_buffer_manager.cpp)136
-rw-r--r--src/core/hle/service/vi/shared_buffer_manager.h (renamed from src/core/hle/service/nvnflinger/fb_share_buffer_manager.h)46
-rw-r--r--src/core/hle/service/vi/system_display_service.cpp66
-rw-r--r--src/core/hle/service/vi/system_display_service.h20
-rw-r--r--src/core/hle/service/vi/system_root_service.cpp11
-rw-r--r--src/core/hle/service/vi/system_root_service.h12
-rw-r--r--src/core/hle/service/vi/vi.cpp21
-rw-r--r--src/core/hle/service/vi/vi.h10
-rw-r--r--src/core/hle/service/vi/vi_types.h10
-rw-r--r--src/core/hle/service/vi/vsync_manager.cpp26
-rw-r--r--src/core/hle/service/vi/vsync_manager.h29
-rw-r--r--src/core/loader/nca.cpp4
-rw-r--r--src/core/memory.cpp8
-rw-r--r--src/core/memory/cheat_engine.cpp4
-rw-r--r--src/core/memory/dmnt_cheat_vm.cpp4
-rw-r--r--src/frontend_common/config.cpp1
-rw-r--r--src/frontend_common/content_manager.h5
-rw-r--r--src/hid_core/frontend/emulated_controller.cpp66
-rw-r--r--src/hid_core/frontend/emulated_controller.h3
-rw-r--r--src/input_common/CMakeLists.txt10
-rw-r--r--src/input_common/drivers/android.cpp324
-rw-r--r--src/input_common/drivers/android.h124
-rw-r--r--src/input_common/helpers/joycon_driver.cpp4
-rw-r--r--src/input_common/main.cpp28
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_image.cpp47
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_instructions.h4
-rw-r--r--src/video_core/buffer_cache/buffer_cache.h2
-rw-r--r--src/video_core/engines/maxwell_3d.cpp4
-rw-r--r--src/video_core/fence_manager.h4
-rw-r--r--src/video_core/gpu_thread.cpp4
-rw-r--r--src/video_core/host1x/ffmpeg/ffmpeg.cpp4
-rw-r--r--src/video_core/macro/macro_hle.cpp8
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp8
-rw-r--r--src/video_core/renderer_vulkan/present/layer.cpp4
-rw-r--r--src/video_core/renderer_vulkan/renderer_vulkan.cpp4
-rw-r--r--src/video_core/renderer_vulkan/vk_rasterizer.cpp8
-rw-r--r--src/video_core/vulkan_common/nsight_aftermath_tracker.cpp4
-rw-r--r--src/yuzu/configuration/qt_config.cpp1
-rw-r--r--src/yuzu/main.cpp171
-rw-r--r--src/yuzu/main.h1
-rw-r--r--src/yuzu/main.ui18
-rw-r--r--src/yuzu_cmd/sdl_config.cpp1
-rw-r--r--src/yuzu_cmd/yuzu.cpp4
343 files changed, 11631 insertions, 5613 deletions
diff --git a/src/android/app/src/main/AndroidManifest.xml b/src/android/app/src/main/AndroidManifest.xml
index 7890b30ca..b037fc055 100644
--- a/src/android/app/src/main/AndroidManifest.xml
+++ b/src/android/app/src/main/AndroidManifest.xml
@@ -14,6 +14,7 @@ SPDX-License-Identifier: GPL-3.0-or-later
14 <uses-permission android:name="android.permission.INTERNET" /> 14 <uses-permission android:name="android.permission.INTERNET" />
15 <uses-permission android:name="android.permission.NFC" /> 15 <uses-permission android:name="android.permission.NFC" />
16 <uses-permission android:name="android.permission.POST_NOTIFICATIONS" /> 16 <uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
17 <uses-permission android:name="android.permission.VIBRATE" />
17 18
18 <application 19 <application
19 android:name="org.yuzu.yuzu_emu.YuzuApplication" 20 android:name="org.yuzu.yuzu_emu.YuzuApplication"
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt
index 6ebb46af7..02a20dacf 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt
@@ -3,24 +3,21 @@
3 3
4package org.yuzu.yuzu_emu 4package org.yuzu.yuzu_emu
5 5
6import android.app.Dialog
7import android.content.DialogInterface 6import android.content.DialogInterface
8import android.net.Uri 7import android.net.Uri
9import android.os.Bundle
10import android.text.Html 8import android.text.Html
11import android.text.method.LinkMovementMethod 9import android.text.method.LinkMovementMethod
12import android.view.Surface 10import android.view.Surface
13import android.view.View 11import android.view.View
14import android.widget.TextView 12import android.widget.TextView
15import androidx.annotation.Keep 13import androidx.annotation.Keep
16import androidx.fragment.app.DialogFragment
17import com.google.android.material.dialog.MaterialAlertDialogBuilder 14import com.google.android.material.dialog.MaterialAlertDialogBuilder
18import java.lang.ref.WeakReference 15import java.lang.ref.WeakReference
19import org.yuzu.yuzu_emu.activities.EmulationActivity 16import org.yuzu.yuzu_emu.activities.EmulationActivity
17import org.yuzu.yuzu_emu.fragments.CoreErrorDialogFragment
20import org.yuzu.yuzu_emu.utils.DocumentsTree 18import org.yuzu.yuzu_emu.utils.DocumentsTree
21import org.yuzu.yuzu_emu.utils.FileUtil 19import org.yuzu.yuzu_emu.utils.FileUtil
22import org.yuzu.yuzu_emu.utils.Log 20import org.yuzu.yuzu_emu.utils.Log
23import org.yuzu.yuzu_emu.utils.SerializableHelper.serializable
24import org.yuzu.yuzu_emu.model.InstallResult 21import org.yuzu.yuzu_emu.model.InstallResult
25import org.yuzu.yuzu_emu.model.Patch 22import org.yuzu.yuzu_emu.model.Patch
26import org.yuzu.yuzu_emu.model.GameVerificationResult 23import org.yuzu.yuzu_emu.model.GameVerificationResult
@@ -30,34 +27,6 @@ import org.yuzu.yuzu_emu.model.GameVerificationResult
30 * with the native side of the Yuzu code. 27 * with the native side of the Yuzu code.
31 */ 28 */
32object NativeLibrary { 29object NativeLibrary {
33 /**
34 * Default controller id for each device
35 */
36 const val Player1Device = 0
37 const val Player2Device = 1
38 const val Player3Device = 2
39 const val Player4Device = 3
40 const val Player5Device = 4
41 const val Player6Device = 5
42 const val Player7Device = 6
43 const val Player8Device = 7
44 const val ConsoleDevice = 8
45
46 /**
47 * Controller type for each device
48 */
49 const val ProController = 3
50 const val Handheld = 4
51 const val JoyconDual = 5
52 const val JoyconLeft = 6
53 const val JoyconRight = 7
54 const val GameCube = 8
55 const val Pokeball = 9
56 const val NES = 10
57 const val SNES = 11
58 const val N64 = 12
59 const val SegaGenesis = 13
60
61 @JvmField 30 @JvmField
62 var sEmulationActivity = WeakReference<EmulationActivity?>(null) 31 var sEmulationActivity = WeakReference<EmulationActivity?>(null)
63 32
@@ -127,112 +96,6 @@ object NativeLibrary {
127 FileUtil.getFilename(Uri.parse(path)) 96 FileUtil.getFilename(Uri.parse(path))
128 } 97 }
129 98
130 /**
131 * Returns true if pro controller isn't available and handheld is
132 */
133 external fun isHandheldOnly(): Boolean
134
135 /**
136 * Changes controller type for a specific device.
137 *
138 * @param Device The input descriptor of the gamepad.
139 * @param Type The NpadStyleIndex of the gamepad.
140 */
141 external fun setDeviceType(Device: Int, Type: Int): Boolean
142
143 /**
144 * Handles event when a gamepad is connected.
145 *
146 * @param Device The input descriptor of the gamepad.
147 */
148 external fun onGamePadConnectEvent(Device: Int): Boolean
149
150 /**
151 * Handles event when a gamepad is disconnected.
152 *
153 * @param Device The input descriptor of the gamepad.
154 */
155 external fun onGamePadDisconnectEvent(Device: Int): Boolean
156
157 /**
158 * Handles button press events for a gamepad.
159 *
160 * @param Device The input descriptor of the gamepad.
161 * @param Button Key code identifying which button was pressed.
162 * @param Action Mask identifying which action is happening (button pressed down, or button released).
163 * @return If we handled the button press.
164 */
165 external fun onGamePadButtonEvent(Device: Int, Button: Int, Action: Int): Boolean
166
167 /**
168 * Handles joystick movement events.
169 *
170 * @param Device The device ID of the gamepad.
171 * @param Axis The axis ID
172 * @param x_axis The value of the x-axis represented by the given ID.
173 * @param y_axis The value of the y-axis represented by the given ID.
174 */
175 external fun onGamePadJoystickEvent(
176 Device: Int,
177 Axis: Int,
178 x_axis: Float,
179 y_axis: Float
180 ): Boolean
181
182 /**
183 * Handles motion events.
184 *
185 * @param delta_timestamp The finger id corresponding to this event
186 * @param gyro_x,gyro_y,gyro_z The value of the accelerometer sensor.
187 * @param accel_x,accel_y,accel_z The value of the y-axis
188 */
189 external fun onGamePadMotionEvent(
190 Device: Int,
191 delta_timestamp: Long,
192 gyro_x: Float,
193 gyro_y: Float,
194 gyro_z: Float,
195 accel_x: Float,
196 accel_y: Float,
197 accel_z: Float
198 ): Boolean
199
200 /**
201 * Signals and load a nfc tag
202 *
203 * @param data Byte array containing all the data from a nfc tag
204 */
205 external fun onReadNfcTag(data: ByteArray?): Boolean
206
207 /**
208 * Removes current loaded nfc tag
209 */
210 external fun onRemoveNfcTag(): Boolean
211
212 /**
213 * Handles touch press events.
214 *
215 * @param finger_id The finger id corresponding to this event
216 * @param x_axis The value of the x-axis.
217 * @param y_axis The value of the y-axis.
218 */
219 external fun onTouchPressed(finger_id: Int, x_axis: Float, y_axis: Float)
220
221 /**
222 * Handles touch movement.
223 *
224 * @param x_axis The value of the instantaneous x-axis.
225 * @param y_axis The value of the instantaneous y-axis.
226 */
227 external fun onTouchMoved(finger_id: Int, x_axis: Float, y_axis: Float)
228
229 /**
230 * Handles touch release events.
231 *
232 * @param finger_id The finger id corresponding to this event
233 */
234 external fun onTouchReleased(finger_id: Int)
235
236 external fun setAppDirectory(directory: String) 99 external fun setAppDirectory(directory: String)
237 100
238 /** 101 /**
@@ -318,46 +181,13 @@ object NativeLibrary {
318 ErrorUnknown 181 ErrorUnknown
319 } 182 }
320 183
321 private var coreErrorAlertResult = false 184 var coreErrorAlertResult = false
322 private val coreErrorAlertLock = Object() 185 val coreErrorAlertLock = Object()
323
324 class CoreErrorDialogFragment : DialogFragment() {
325 override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
326 val title = requireArguments().serializable<String>("title")
327 val message = requireArguments().serializable<String>("message")
328
329 return MaterialAlertDialogBuilder(requireActivity())
330 .setTitle(title)
331 .setMessage(message)
332 .setPositiveButton(R.string.continue_button, null)
333 .setNegativeButton(R.string.abort_button) { _: DialogInterface?, _: Int ->
334 coreErrorAlertResult = false
335 synchronized(coreErrorAlertLock) { coreErrorAlertLock.notify() }
336 }
337 .create()
338 }
339
340 override fun onDismiss(dialog: DialogInterface) {
341 coreErrorAlertResult = true
342 synchronized(coreErrorAlertLock) { coreErrorAlertLock.notify() }
343 }
344
345 companion object {
346 fun newInstance(title: String?, message: String?): CoreErrorDialogFragment {
347 val frag = CoreErrorDialogFragment()
348 val args = Bundle()
349 args.putString("title", title)
350 args.putString("message", message)
351 frag.arguments = args
352 return frag
353 }
354 }
355 }
356 186
357 private fun onCoreErrorImpl(title: String, message: String) { 187 private fun onCoreErrorImpl(title: String, message: String) {
358 val emulationActivity = sEmulationActivity.get() 188 val emulationActivity = sEmulationActivity.get()
359 if (emulationActivity == null) { 189 if (emulationActivity == null) {
360 error("[NativeLibrary] EmulationActivity not present") 190 Log.error("[NativeLibrary] EmulationActivity not present")
361 return 191 return
362 } 192 }
363 193
@@ -373,7 +203,7 @@ object NativeLibrary {
373 fun onCoreError(error: CoreError?, details: String): Boolean { 203 fun onCoreError(error: CoreError?, details: String): Boolean {
374 val emulationActivity = sEmulationActivity.get() 204 val emulationActivity = sEmulationActivity.get()
375 if (emulationActivity == null) { 205 if (emulationActivity == null) {
376 error("[NativeLibrary] EmulationActivity not present") 206 Log.error("[NativeLibrary] EmulationActivity not present")
377 return false 207 return false
378 } 208 }
379 209
@@ -404,7 +234,7 @@ object NativeLibrary {
404 } 234 }
405 235
406 // Show the AlertDialog on the main thread. 236 // Show the AlertDialog on the main thread.
407 emulationActivity.runOnUiThread(Runnable { onCoreErrorImpl(title, message) }) 237 emulationActivity.runOnUiThread { onCoreErrorImpl(title, message) }
408 238
409 // Wait for the lock to notify that it is complete. 239 // Wait for the lock to notify that it is complete.
410 synchronized(coreErrorAlertLock) { coreErrorAlertLock.wait() } 240 synchronized(coreErrorAlertLock) { coreErrorAlertLock.wait() }
@@ -629,46 +459,4 @@ object NativeLibrary {
629 * Checks if all necessary keys are present for decryption 459 * Checks if all necessary keys are present for decryption
630 */ 460 */
631 external fun areKeysPresent(): Boolean 461 external fun areKeysPresent(): Boolean
632
633 /**
634 * Button type for use in onTouchEvent
635 */
636 object ButtonType {
637 const val BUTTON_A = 0
638 const val BUTTON_B = 1
639 const val BUTTON_X = 2
640 const val BUTTON_Y = 3
641 const val STICK_L = 4
642 const val STICK_R = 5
643 const val TRIGGER_L = 6
644 const val TRIGGER_R = 7
645 const val TRIGGER_ZL = 8
646 const val TRIGGER_ZR = 9
647 const val BUTTON_PLUS = 10
648 const val BUTTON_MINUS = 11
649 const val DPAD_LEFT = 12
650 const val DPAD_UP = 13
651 const val DPAD_RIGHT = 14
652 const val DPAD_DOWN = 15
653 const val BUTTON_SL = 16
654 const val BUTTON_SR = 17
655 const val BUTTON_HOME = 18
656 const val BUTTON_CAPTURE = 19
657 }
658
659 /**
660 * Stick type for use in onTouchEvent
661 */
662 object StickType {
663 const val STICK_L = 0
664 const val STICK_R = 1
665 }
666
667 /**
668 * Button states
669 */
670 object ButtonState {
671 const val RELEASED = 0
672 const val PRESSED = 1
673 }
674} 462}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/YuzuApplication.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/YuzuApplication.kt
index 76778c10a..72943f33e 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/YuzuApplication.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/YuzuApplication.kt
@@ -7,6 +7,7 @@ import android.app.Application
7import android.app.NotificationChannel 7import android.app.NotificationChannel
8import android.app.NotificationManager 8import android.app.NotificationManager
9import android.content.Context 9import android.content.Context
10import org.yuzu.yuzu_emu.features.input.NativeInput
10import java.io.File 11import java.io.File
11import org.yuzu.yuzu_emu.utils.DirectoryInitialization 12import org.yuzu.yuzu_emu.utils.DirectoryInitialization
12import org.yuzu.yuzu_emu.utils.DocumentsTree 13import org.yuzu.yuzu_emu.utils.DocumentsTree
@@ -37,6 +38,7 @@ class YuzuApplication : Application() {
37 documentsTree = DocumentsTree() 38 documentsTree = DocumentsTree()
38 DirectoryInitialization.start() 39 DirectoryInitialization.start()
39 GpuDriverHelper.initializeDriverParameters() 40 GpuDriverHelper.initializeDriverParameters()
41 NativeInput.reloadInputDevices()
40 NativeLibrary.logDeviceInfo() 42 NativeLibrary.logDeviceInfo()
41 Log.logDeviceInfo() 43 Log.logDeviceInfo()
42 44
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt
index 7a8d03610..c962558a7 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt
@@ -39,6 +39,7 @@ import org.yuzu.yuzu_emu.NativeLibrary
39import org.yuzu.yuzu_emu.R 39import org.yuzu.yuzu_emu.R
40import org.yuzu.yuzu_emu.YuzuApplication 40import org.yuzu.yuzu_emu.YuzuApplication
41import org.yuzu.yuzu_emu.databinding.ActivityEmulationBinding 41import org.yuzu.yuzu_emu.databinding.ActivityEmulationBinding
42import org.yuzu.yuzu_emu.features.input.NativeInput
42import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting 43import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting
43import org.yuzu.yuzu_emu.features.settings.model.IntSetting 44import org.yuzu.yuzu_emu.features.settings.model.IntSetting
44import org.yuzu.yuzu_emu.features.settings.model.Settings 45import org.yuzu.yuzu_emu.features.settings.model.Settings
@@ -47,7 +48,9 @@ import org.yuzu.yuzu_emu.model.Game
47import org.yuzu.yuzu_emu.utils.InputHandler 48import org.yuzu.yuzu_emu.utils.InputHandler
48import org.yuzu.yuzu_emu.utils.Log 49import org.yuzu.yuzu_emu.utils.Log
49import org.yuzu.yuzu_emu.utils.MemoryUtil 50import org.yuzu.yuzu_emu.utils.MemoryUtil
51import org.yuzu.yuzu_emu.utils.NativeConfig
50import org.yuzu.yuzu_emu.utils.NfcReader 52import org.yuzu.yuzu_emu.utils.NfcReader
53import org.yuzu.yuzu_emu.utils.ParamPackage
51import org.yuzu.yuzu_emu.utils.ThemeHelper 54import org.yuzu.yuzu_emu.utils.ThemeHelper
52import java.text.NumberFormat 55import java.text.NumberFormat
53import kotlin.math.roundToInt 56import kotlin.math.roundToInt
@@ -63,8 +66,6 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
63 private var motionTimestamp: Long = 0 66 private var motionTimestamp: Long = 0
64 private var flipMotionOrientation: Boolean = false 67 private var flipMotionOrientation: Boolean = false
65 68
66 private var controllerIds = InputHandler.getGameControllerIds()
67
68 private val actionPause = "ACTION_EMULATOR_PAUSE" 69 private val actionPause = "ACTION_EMULATOR_PAUSE"
69 private val actionPlay = "ACTION_EMULATOR_PLAY" 70 private val actionPlay = "ACTION_EMULATOR_PLAY"
70 private val actionMute = "ACTION_EMULATOR_MUTE" 71 private val actionMute = "ACTION_EMULATOR_MUTE"
@@ -78,6 +79,33 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
78 79
79 super.onCreate(savedInstanceState) 80 super.onCreate(savedInstanceState)
80 81
82 InputHandler.updateControllerData()
83 val players = NativeConfig.getInputSettings(true)
84 var hasConfiguredControllers = false
85 players.forEach {
86 if (it.hasMapping()) {
87 hasConfiguredControllers = true
88 }
89 }
90 if (!hasConfiguredControllers && InputHandler.androidControllers.isNotEmpty()) {
91 var params: ParamPackage? = null
92 for (controller in InputHandler.registeredControllers) {
93 if (controller.get("port", -1) == 0) {
94 params = controller
95 break
96 }
97 }
98
99 if (params != null) {
100 NativeInput.updateMappingsWithDefault(
101 0,
102 params,
103 params.get("display", getString(R.string.unknown))
104 )
105 NativeConfig.saveGlobalConfig()
106 }
107 }
108
81 binding = ActivityEmulationBinding.inflate(layoutInflater) 109 binding = ActivityEmulationBinding.inflate(layoutInflater)
82 setContentView(binding.root) 110 setContentView(binding.root)
83 111
@@ -95,8 +123,6 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
95 nfcReader = NfcReader(this) 123 nfcReader = NfcReader(this)
96 nfcReader.initialize() 124 nfcReader.initialize()
97 125
98 InputHandler.initialize()
99
100 val preferences = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext) 126 val preferences = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
101 if (!preferences.getBoolean(Settings.PREF_MEMORY_WARNING_SHOWN, false)) { 127 if (!preferences.getBoolean(Settings.PREF_MEMORY_WARNING_SHOWN, false)) {
102 if (MemoryUtil.isLessThan(MemoryUtil.REQUIRED_MEMORY, MemoryUtil.totalMemory)) { 128 if (MemoryUtil.isLessThan(MemoryUtil.REQUIRED_MEMORY, MemoryUtil.totalMemory)) {
@@ -147,7 +173,7 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
147 super.onResume() 173 super.onResume()
148 nfcReader.startScanning() 174 nfcReader.startScanning()
149 startMotionSensorListener() 175 startMotionSensorListener()
150 InputHandler.updateControllerIds() 176 InputHandler.updateControllerData()
151 177
152 buildPictureInPictureParams() 178 buildPictureInPictureParams()
153 } 179 }
@@ -172,6 +198,7 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
172 super.onNewIntent(intent) 198 super.onNewIntent(intent)
173 setIntent(intent) 199 setIntent(intent)
174 nfcReader.onNewIntent(intent) 200 nfcReader.onNewIntent(intent)
201 InputHandler.updateControllerData()
175 } 202 }
176 203
177 override fun dispatchKeyEvent(event: KeyEvent): Boolean { 204 override fun dispatchKeyEvent(event: KeyEvent): Boolean {
@@ -244,8 +271,8 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
244 } 271 }
245 val deltaTimestamp = (event.timestamp - motionTimestamp) / 1000 272 val deltaTimestamp = (event.timestamp - motionTimestamp) / 1000
246 motionTimestamp = event.timestamp 273 motionTimestamp = event.timestamp
247 NativeLibrary.onGamePadMotionEvent( 274 NativeInput.onDeviceMotionEvent(
248 NativeLibrary.Player1Device, 275 NativeInput.Player1Device,
249 deltaTimestamp, 276 deltaTimestamp,
250 gyro[0], 277 gyro[0],
251 gyro[1], 278 gyro[1],
@@ -254,8 +281,8 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
254 accel[1], 281 accel[1],
255 accel[2] 282 accel[2]
256 ) 283 )
257 NativeLibrary.onGamePadMotionEvent( 284 NativeInput.onDeviceMotionEvent(
258 NativeLibrary.ConsoleDevice, 285 NativeInput.ConsoleDevice,
259 deltaTimestamp, 286 deltaTimestamp,
260 gyro[0], 287 gyro[0],
261 gyro[1], 288 gyro[1],
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/DriverAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/DriverAdapter.kt
index f218c76ef..50663ad91 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/DriverAdapter.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/DriverAdapter.kt
@@ -3,15 +3,15 @@
3 3
4package org.yuzu.yuzu_emu.adapters 4package org.yuzu.yuzu_emu.adapters
5 5
6import android.text.TextUtils
7import android.view.LayoutInflater 6import android.view.LayoutInflater
8import android.view.View
9import android.view.ViewGroup 7import android.view.ViewGroup
10import org.yuzu.yuzu_emu.R 8import org.yuzu.yuzu_emu.R
11import org.yuzu.yuzu_emu.databinding.CardDriverOptionBinding 9import org.yuzu.yuzu_emu.databinding.CardDriverOptionBinding
12import org.yuzu.yuzu_emu.features.settings.model.StringSetting 10import org.yuzu.yuzu_emu.features.settings.model.StringSetting
13import org.yuzu.yuzu_emu.model.Driver 11import org.yuzu.yuzu_emu.model.Driver
14import org.yuzu.yuzu_emu.model.DriverViewModel 12import org.yuzu.yuzu_emu.model.DriverViewModel
13import org.yuzu.yuzu_emu.utils.ViewUtils.marquee
14import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
15import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder 15import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder
16 16
17class DriverAdapter(private val driverViewModel: DriverViewModel) : 17class DriverAdapter(private val driverViewModel: DriverViewModel) :
@@ -44,25 +44,15 @@ class DriverAdapter(private val driverViewModel: DriverViewModel) :
44 } 44 }
45 45
46 // Delay marquee by 3s 46 // Delay marquee by 3s
47 title.postDelayed( 47 title.marquee()
48 { 48 version.marquee()
49 title.isSelected = true 49 description.marquee()
50 title.ellipsize = TextUtils.TruncateAt.MARQUEE
51 version.isSelected = true
52 version.ellipsize = TextUtils.TruncateAt.MARQUEE
53 description.isSelected = true
54 description.ellipsize = TextUtils.TruncateAt.MARQUEE
55 },
56 3000
57 )
58 title.text = model.title 50 title.text = model.title
59 version.text = model.version 51 version.text = model.version
60 description.text = model.description 52 description.text = model.description
61 if (model.title != binding.root.context.getString(R.string.system_gpu_driver)) { 53 buttonDelete.setVisible(
62 buttonDelete.visibility = View.VISIBLE 54 model.title != binding.root.context.getString(R.string.system_gpu_driver)
63 } else { 55 )
64 buttonDelete.visibility = View.GONE
65 }
66 } 56 }
67 } 57 }
68 } 58 }
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/FolderAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/FolderAdapter.kt
index 3d8f0bda8..5cbd15d2a 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/FolderAdapter.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/FolderAdapter.kt
@@ -4,7 +4,6 @@
4package org.yuzu.yuzu_emu.adapters 4package org.yuzu.yuzu_emu.adapters
5 5
6import android.net.Uri 6import android.net.Uri
7import android.text.TextUtils
8import android.view.LayoutInflater 7import android.view.LayoutInflater
9import android.view.ViewGroup 8import android.view.ViewGroup
10import androidx.fragment.app.FragmentActivity 9import androidx.fragment.app.FragmentActivity
@@ -12,6 +11,7 @@ import org.yuzu.yuzu_emu.databinding.CardFolderBinding
12import org.yuzu.yuzu_emu.fragments.GameFolderPropertiesDialogFragment 11import org.yuzu.yuzu_emu.fragments.GameFolderPropertiesDialogFragment
13import org.yuzu.yuzu_emu.model.GameDir 12import org.yuzu.yuzu_emu.model.GameDir
14import org.yuzu.yuzu_emu.model.GamesViewModel 13import org.yuzu.yuzu_emu.model.GamesViewModel
14import org.yuzu.yuzu_emu.utils.ViewUtils.marquee
15import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder 15import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder
16 16
17class FolderAdapter(val activity: FragmentActivity, val gamesViewModel: GamesViewModel) : 17class FolderAdapter(val activity: FragmentActivity, val gamesViewModel: GamesViewModel) :
@@ -29,13 +29,7 @@ class FolderAdapter(val activity: FragmentActivity, val gamesViewModel: GamesVie
29 override fun bind(model: GameDir) { 29 override fun bind(model: GameDir) {
30 binding.apply { 30 binding.apply {
31 path.text = Uri.parse(model.uriString).path 31 path.text = Uri.parse(model.uriString).path
32 path.postDelayed( 32 path.marquee()
33 {
34 path.isSelected = true
35 path.ellipsize = TextUtils.TruncateAt.MARQUEE
36 },
37 3000
38 )
39 33
40 buttonEdit.setOnClickListener { 34 buttonEdit.setOnClickListener {
41 GameFolderPropertiesDialogFragment.newInstance(model) 35 GameFolderPropertiesDialogFragment.newInstance(model)
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt
index 85c8249e6..b1f247ac3 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt
@@ -4,7 +4,6 @@
4package org.yuzu.yuzu_emu.adapters 4package org.yuzu.yuzu_emu.adapters
5 5
6import android.net.Uri 6import android.net.Uri
7import android.text.TextUtils
8import android.view.LayoutInflater 7import android.view.LayoutInflater
9import android.view.ViewGroup 8import android.view.ViewGroup
10import android.widget.ImageView 9import android.widget.ImageView
@@ -27,6 +26,7 @@ import org.yuzu.yuzu_emu.databinding.CardGameBinding
27import org.yuzu.yuzu_emu.model.Game 26import org.yuzu.yuzu_emu.model.Game
28import org.yuzu.yuzu_emu.model.GamesViewModel 27import org.yuzu.yuzu_emu.model.GamesViewModel
29import org.yuzu.yuzu_emu.utils.GameIconUtils 28import org.yuzu.yuzu_emu.utils.GameIconUtils
29import org.yuzu.yuzu_emu.utils.ViewUtils.marquee
30import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder 30import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder
31 31
32class GameAdapter(private val activity: AppCompatActivity) : 32class GameAdapter(private val activity: AppCompatActivity) :
@@ -44,14 +44,7 @@ class GameAdapter(private val activity: AppCompatActivity) :
44 44
45 binding.textGameTitle.text = model.title.replace("[\\t\\n\\r]+".toRegex(), " ") 45 binding.textGameTitle.text = model.title.replace("[\\t\\n\\r]+".toRegex(), " ")
46 46
47 binding.textGameTitle.postDelayed( 47 binding.textGameTitle.marquee()
48 {
49 binding.textGameTitle.ellipsize = TextUtils.TruncateAt.MARQUEE
50 binding.textGameTitle.isSelected = true
51 },
52 3000
53 )
54
55 binding.cardGame.setOnClickListener { onClick(model) } 48 binding.cardGame.setOnClickListener { onClick(model) }
56 binding.cardGame.setOnLongClickListener { onLongClick(model) } 49 binding.cardGame.setOnLongClickListener { onLongClick(model) }
57 } 50 }
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GamePropertiesAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GamePropertiesAdapter.kt
index 0046d5314..7366e2c77 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GamePropertiesAdapter.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GamePropertiesAdapter.kt
@@ -3,21 +3,18 @@
3 3
4package org.yuzu.yuzu_emu.adapters 4package org.yuzu.yuzu_emu.adapters
5 5
6import android.text.TextUtils
7import android.view.LayoutInflater 6import android.view.LayoutInflater
8import android.view.View
9import android.view.ViewGroup 7import android.view.ViewGroup
10import androidx.core.content.res.ResourcesCompat 8import androidx.core.content.res.ResourcesCompat
11import androidx.lifecycle.Lifecycle
12import androidx.lifecycle.LifecycleOwner 9import androidx.lifecycle.LifecycleOwner
13import androidx.lifecycle.lifecycleScope
14import androidx.lifecycle.repeatOnLifecycle
15import kotlinx.coroutines.launch
16import org.yuzu.yuzu_emu.databinding.CardInstallableIconBinding 10import org.yuzu.yuzu_emu.databinding.CardInstallableIconBinding
17import org.yuzu.yuzu_emu.databinding.CardSimpleOutlinedBinding 11import org.yuzu.yuzu_emu.databinding.CardSimpleOutlinedBinding
18import org.yuzu.yuzu_emu.model.GameProperty 12import org.yuzu.yuzu_emu.model.GameProperty
19import org.yuzu.yuzu_emu.model.InstallableProperty 13import org.yuzu.yuzu_emu.model.InstallableProperty
20import org.yuzu.yuzu_emu.model.SubmenuProperty 14import org.yuzu.yuzu_emu.model.SubmenuProperty
15import org.yuzu.yuzu_emu.utils.ViewUtils.marquee
16import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
17import org.yuzu.yuzu_emu.utils.collect
21import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder 18import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder
22 19
23class GamePropertiesAdapter( 20class GamePropertiesAdapter(
@@ -76,23 +73,15 @@ class GamePropertiesAdapter(
76 ) 73 )
77 ) 74 )
78 75
79 binding.details.postDelayed({ 76 binding.details.marquee()
80 binding.details.isSelected = true
81 binding.details.ellipsize = TextUtils.TruncateAt.MARQUEE
82 }, 3000)
83
84 if (submenuProperty.details != null) { 77 if (submenuProperty.details != null) {
85 binding.details.visibility = View.VISIBLE 78 binding.details.setVisible(true)
86 binding.details.text = submenuProperty.details.invoke() 79 binding.details.text = submenuProperty.details.invoke()
87 } else if (submenuProperty.detailsFlow != null) { 80 } else if (submenuProperty.detailsFlow != null) {
88 binding.details.visibility = View.VISIBLE 81 binding.details.setVisible(true)
89 viewLifecycle.lifecycleScope.launch { 82 submenuProperty.detailsFlow.collect(viewLifecycle) { binding.details.text = it }
90 viewLifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
91 submenuProperty.detailsFlow.collect { binding.details.text = it }
92 }
93 }
94 } else { 83 } else {
95 binding.details.visibility = View.GONE 84 binding.details.setVisible(false)
96 } 85 }
97 } 86 }
98 } 87 }
@@ -112,14 +101,10 @@ class GamePropertiesAdapter(
112 ) 101 )
113 ) 102 )
114 103
115 if (installableProperty.install != null) { 104 binding.buttonInstall.setVisible(installableProperty.install != null)
116 binding.buttonInstall.visibility = View.VISIBLE 105 binding.buttonInstall.setOnClickListener { installableProperty.install?.invoke() }
117 binding.buttonInstall.setOnClickListener { installableProperty.install.invoke() } 106 binding.buttonExport.setVisible(installableProperty.export != null)
118 } 107 binding.buttonExport.setOnClickListener { installableProperty.export?.invoke() }
119 if (installableProperty.export != null) {
120 binding.buttonExport.visibility = View.VISIBLE
121 binding.buttonExport.setOnClickListener { installableProperty.export.invoke() }
122 }
123 } 108 }
124 } 109 }
125 110
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/HomeSettingAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/HomeSettingAdapter.kt
index b512845d5..0bd196673 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/HomeSettingAdapter.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/HomeSettingAdapter.kt
@@ -3,22 +3,19 @@
3 3
4package org.yuzu.yuzu_emu.adapters 4package org.yuzu.yuzu_emu.adapters
5 5
6import android.text.TextUtils
7import android.view.LayoutInflater 6import android.view.LayoutInflater
8import android.view.View
9import android.view.ViewGroup 7import android.view.ViewGroup
10import androidx.appcompat.app.AppCompatActivity 8import androidx.appcompat.app.AppCompatActivity
11import androidx.core.content.ContextCompat 9import androidx.core.content.ContextCompat
12import androidx.core.content.res.ResourcesCompat 10import androidx.core.content.res.ResourcesCompat
13import androidx.lifecycle.Lifecycle
14import androidx.lifecycle.LifecycleOwner 11import androidx.lifecycle.LifecycleOwner
15import androidx.lifecycle.lifecycleScope
16import androidx.lifecycle.repeatOnLifecycle
17import kotlinx.coroutines.launch
18import org.yuzu.yuzu_emu.R 12import org.yuzu.yuzu_emu.R
19import org.yuzu.yuzu_emu.databinding.CardHomeOptionBinding 13import org.yuzu.yuzu_emu.databinding.CardHomeOptionBinding
20import org.yuzu.yuzu_emu.fragments.MessageDialogFragment 14import org.yuzu.yuzu_emu.fragments.MessageDialogFragment
21import org.yuzu.yuzu_emu.model.HomeSetting 15import org.yuzu.yuzu_emu.model.HomeSetting
16import org.yuzu.yuzu_emu.utils.ViewUtils.marquee
17import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
18import org.yuzu.yuzu_emu.utils.collect
22import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder 19import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder
23 20
24class HomeSettingAdapter( 21class HomeSettingAdapter(
@@ -59,18 +56,8 @@ class HomeSettingAdapter(
59 binding.optionIcon.alpha = 0.5f 56 binding.optionIcon.alpha = 0.5f
60 } 57 }
61 58
62 viewLifecycle.lifecycleScope.launch { 59 model.details.collect(viewLifecycle) { updateOptionDetails(it) }
63 viewLifecycle.repeatOnLifecycle(Lifecycle.State.CREATED) { 60 binding.optionDetail.marquee()
64 model.details.collect { updateOptionDetails(it) }
65 }
66 }
67 binding.optionDetail.postDelayed(
68 {
69 binding.optionDetail.ellipsize = TextUtils.TruncateAt.MARQUEE
70 binding.optionDetail.isSelected = true
71 },
72 3000
73 )
74 61
75 binding.root.setOnClickListener { onClick(model) } 62 binding.root.setOnClickListener { onClick(model) }
76 } 63 }
@@ -90,7 +77,7 @@ class HomeSettingAdapter(
90 private fun updateOptionDetails(detailString: String) { 77 private fun updateOptionDetails(detailString: String) {
91 if (detailString.isNotEmpty()) { 78 if (detailString.isNotEmpty()) {
92 binding.optionDetail.text = detailString 79 binding.optionDetail.text = detailString
93 binding.optionDetail.visibility = View.VISIBLE 80 binding.optionDetail.setVisible(true)
94 } 81 }
95 } 82 }
96 } 83 }
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/InstallableAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/InstallableAdapter.kt
index 4218c4e52..1ba75fa2f 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/InstallableAdapter.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/InstallableAdapter.kt
@@ -4,10 +4,10 @@
4package org.yuzu.yuzu_emu.adapters 4package org.yuzu.yuzu_emu.adapters
5 5
6import android.view.LayoutInflater 6import android.view.LayoutInflater
7import android.view.View
8import android.view.ViewGroup 7import android.view.ViewGroup
9import org.yuzu.yuzu_emu.databinding.CardInstallableBinding 8import org.yuzu.yuzu_emu.databinding.CardInstallableBinding
10import org.yuzu.yuzu_emu.model.Installable 9import org.yuzu.yuzu_emu.model.Installable
10import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
11import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder 11import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder
12 12
13class InstallableAdapter(installables: List<Installable>) : 13class InstallableAdapter(installables: List<Installable>) :
@@ -26,14 +26,10 @@ class InstallableAdapter(installables: List<Installable>) :
26 binding.title.setText(model.titleId) 26 binding.title.setText(model.titleId)
27 binding.description.setText(model.descriptionId) 27 binding.description.setText(model.descriptionId)
28 28
29 if (model.install != null) { 29 binding.buttonInstall.setVisible(model.install != null)
30 binding.buttonInstall.visibility = View.VISIBLE 30 binding.buttonInstall.setOnClickListener { model.install?.invoke() }
31 binding.buttonInstall.setOnClickListener { model.install.invoke() } 31 binding.buttonExport.setVisible(model.export != null)
32 } 32 binding.buttonExport.setOnClickListener { model.export?.invoke() }
33 if (model.export != null) {
34 binding.buttonExport.visibility = View.VISIBLE
35 binding.buttonExport.setOnClickListener { model.export.invoke() }
36 }
37 } 33 }
38 } 34 }
39} 35}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/LicenseAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/LicenseAdapter.kt
index 38bb1f96f..1379968f9 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/LicenseAdapter.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/LicenseAdapter.kt
@@ -4,12 +4,12 @@
4package org.yuzu.yuzu_emu.adapters 4package org.yuzu.yuzu_emu.adapters
5 5
6import android.view.LayoutInflater 6import android.view.LayoutInflater
7import android.view.View
8import android.view.ViewGroup 7import android.view.ViewGroup
9import androidx.appcompat.app.AppCompatActivity 8import androidx.appcompat.app.AppCompatActivity
10import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding 9import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding
11import org.yuzu.yuzu_emu.fragments.LicenseBottomSheetDialogFragment 10import org.yuzu.yuzu_emu.fragments.LicenseBottomSheetDialogFragment
12import org.yuzu.yuzu_emu.model.License 11import org.yuzu.yuzu_emu.model.License
12import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
13import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder 13import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder
14 14
15class LicenseAdapter(private val activity: AppCompatActivity, licenses: List<License>) : 15class LicenseAdapter(private val activity: AppCompatActivity, licenses: List<License>) :
@@ -25,7 +25,7 @@ class LicenseAdapter(private val activity: AppCompatActivity, licenses: List<Lic
25 binding.apply { 25 binding.apply {
26 textSettingName.text = root.context.getString(model.titleId) 26 textSettingName.text = root.context.getString(model.titleId)
27 textSettingDescription.text = root.context.getString(model.descriptionId) 27 textSettingDescription.text = root.context.getString(model.descriptionId)
28 textSettingValue.visibility = View.GONE 28 textSettingValue.setVisible(false)
29 29
30 root.setOnClickListener { onClick(model) } 30 root.setOnClickListener { onClick(model) }
31 } 31 }
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/SetupAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/SetupAdapter.kt
index 02118e1a8..a5f610b31 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/SetupAdapter.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/SetupAdapter.kt
@@ -5,7 +5,6 @@ package org.yuzu.yuzu_emu.adapters
5 5
6import android.text.Html 6import android.text.Html
7import android.view.LayoutInflater 7import android.view.LayoutInflater
8import android.view.View
9import android.view.ViewGroup 8import android.view.ViewGroup
10import androidx.appcompat.app.AppCompatActivity 9import androidx.appcompat.app.AppCompatActivity
11import androidx.core.content.res.ResourcesCompat 10import androidx.core.content.res.ResourcesCompat
@@ -17,6 +16,7 @@ import org.yuzu.yuzu_emu.model.SetupCallback
17import org.yuzu.yuzu_emu.model.SetupPage 16import org.yuzu.yuzu_emu.model.SetupPage
18import org.yuzu.yuzu_emu.model.StepState 17import org.yuzu.yuzu_emu.model.StepState
19import org.yuzu.yuzu_emu.utils.ViewUtils 18import org.yuzu.yuzu_emu.utils.ViewUtils
19import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
20import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder 20import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder
21 21
22class SetupAdapter(val activity: AppCompatActivity, pages: List<SetupPage>) : 22class SetupAdapter(val activity: AppCompatActivity, pages: List<SetupPage>) :
@@ -30,8 +30,8 @@ class SetupAdapter(val activity: AppCompatActivity, pages: List<SetupPage>) :
30 AbstractViewHolder<SetupPage>(binding), SetupCallback { 30 AbstractViewHolder<SetupPage>(binding), SetupCallback {
31 override fun bind(model: SetupPage) { 31 override fun bind(model: SetupPage) {
32 if (model.stepCompleted.invoke() == StepState.COMPLETE) { 32 if (model.stepCompleted.invoke() == StepState.COMPLETE) {
33 binding.buttonAction.visibility = View.INVISIBLE 33 binding.buttonAction.setVisible(visible = false, gone = false)
34 binding.textConfirmation.visibility = View.VISIBLE 34 binding.textConfirmation.setVisible(true)
35 } 35 }
36 36
37 binding.icon.setImageDrawable( 37 binding.icon.setImageDrawable(
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/input/NativeInput.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/input/NativeInput.kt
new file mode 100644
index 000000000..15d776311
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/input/NativeInput.kt
@@ -0,0 +1,416 @@
1// SPDX-FileCopyrightText: 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4package org.yuzu.yuzu_emu.features.input
5
6import org.yuzu.yuzu_emu.features.input.model.NativeButton
7import org.yuzu.yuzu_emu.features.input.model.NativeAnalog
8import org.yuzu.yuzu_emu.features.input.model.InputType
9import org.yuzu.yuzu_emu.features.input.model.ButtonName
10import org.yuzu.yuzu_emu.features.input.model.NpadStyleIndex
11import org.yuzu.yuzu_emu.utils.NativeConfig
12import org.yuzu.yuzu_emu.utils.ParamPackage
13import android.view.InputDevice
14
15object NativeInput {
16 /**
17 * Default controller id for each device
18 */
19 const val Player1Device = 0
20 const val Player2Device = 1
21 const val Player3Device = 2
22 const val Player4Device = 3
23 const val Player5Device = 4
24 const val Player6Device = 5
25 const val Player7Device = 6
26 const val Player8Device = 7
27 const val ConsoleDevice = 8
28
29 /**
30 * Button states
31 */
32 object ButtonState {
33 const val RELEASED = 0
34 const val PRESSED = 1
35 }
36
37 /**
38 * Returns true if pro controller isn't available and handheld is.
39 * Intended to check where the input overlay should direct its inputs.
40 */
41 external fun isHandheldOnly(): Boolean
42
43 /**
44 * Handles button press events for a gamepad.
45 * @param guid 32 character hexadecimal string consisting of the controller's PID+VID.
46 * @param port Port determined by controller connection order.
47 * @param buttonId The Android Keycode corresponding to this event.
48 * @param action Mask identifying which action is happening (button pressed down, or button released).
49 */
50 external fun onGamePadButtonEvent(
51 guid: String,
52 port: Int,
53 buttonId: Int,
54 action: Int
55 )
56
57 /**
58 * Handles axis movement events.
59 * @param guid 32 character hexadecimal string consisting of the controller's PID+VID.
60 * @param port Port determined by controller connection order.
61 * @param axis The axis ID.
62 * @param value Value along the given axis.
63 */
64 external fun onGamePadAxisEvent(guid: String, port: Int, axis: Int, value: Float)
65
66 /**
67 * Handles motion events.
68 * @param guid 32 character hexadecimal string consisting of the controller's PID+VID.
69 * @param port Port determined by controller connection order.
70 * @param deltaTimestamp The finger id corresponding to this event.
71 * @param xGyro The value of the x-axis for the gyroscope.
72 * @param yGyro The value of the y-axis for the gyroscope.
73 * @param zGyro The value of the z-axis for the gyroscope.
74 * @param xAccel The value of the x-axis for the accelerometer.
75 * @param yAccel The value of the y-axis for the accelerometer.
76 * @param zAccel The value of the z-axis for the accelerometer.
77 */
78 external fun onGamePadMotionEvent(
79 guid: String,
80 port: Int,
81 deltaTimestamp: Long,
82 xGyro: Float,
83 yGyro: Float,
84 zGyro: Float,
85 xAccel: Float,
86 yAccel: Float,
87 zAccel: Float
88 )
89
90 /**
91 * Signals and load a nfc tag
92 * @param data Byte array containing all the data from a nfc tag.
93 */
94 external fun onReadNfcTag(data: ByteArray?)
95
96 /**
97 * Removes current loaded nfc tag.
98 */
99 external fun onRemoveNfcTag()
100
101 /**
102 * Handles touch press events.
103 * @param fingerId The finger id corresponding to this event.
104 * @param xAxis The value of the x-axis on the touchscreen.
105 * @param yAxis The value of the y-axis on the touchscreen.
106 */
107 external fun onTouchPressed(fingerId: Int, xAxis: Float, yAxis: Float)
108
109 /**
110 * Handles touch movement.
111 * @param fingerId The finger id corresponding to this event.
112 * @param xAxis The value of the x-axis on the touchscreen.
113 * @param yAxis The value of the y-axis on the touchscreen.
114 */
115 external fun onTouchMoved(fingerId: Int, xAxis: Float, yAxis: Float)
116
117 /**
118 * Handles touch release events.
119 * @param fingerId The finger id corresponding to this event
120 */
121 external fun onTouchReleased(fingerId: Int)
122
123 /**
124 * Sends a button input to the global virtual controllers.
125 * @param port Port determined by controller connection order.
126 * @param button The [NativeButton] corresponding to this event.
127 * @param action Mask identifying which action is happening (button pressed down, or button released).
128 */
129 fun onOverlayButtonEvent(port: Int, button: NativeButton, action: Int) =
130 onOverlayButtonEventImpl(port, button.int, action)
131
132 private external fun onOverlayButtonEventImpl(port: Int, buttonId: Int, action: Int)
133
134 /**
135 * Sends a joystick input to the global virtual controllers.
136 * @param port Port determined by controller connection order.
137 * @param stick The [NativeAnalog] corresponding to this event.
138 * @param xAxis Value along the X axis.
139 * @param yAxis Value along the Y axis.
140 */
141 fun onOverlayJoystickEvent(port: Int, stick: NativeAnalog, xAxis: Float, yAxis: Float) =
142 onOverlayJoystickEventImpl(port, stick.int, xAxis, yAxis)
143
144 private external fun onOverlayJoystickEventImpl(
145 port: Int,
146 stickId: Int,
147 xAxis: Float,
148 yAxis: Float
149 )
150
151 /**
152 * Handles motion events for the global virtual controllers.
153 * @param port Port determined by controller connection order
154 * @param deltaTimestamp The finger id corresponding to this event.
155 * @param xGyro The value of the x-axis for the gyroscope.
156 * @param yGyro The value of the y-axis for the gyroscope.
157 * @param zGyro The value of the z-axis for the gyroscope.
158 * @param xAccel The value of the x-axis for the accelerometer.
159 * @param yAccel The value of the y-axis for the accelerometer.
160 * @param zAccel The value of the z-axis for the accelerometer.
161 */
162 external fun onDeviceMotionEvent(
163 port: Int,
164 deltaTimestamp: Long,
165 xGyro: Float,
166 yGyro: Float,
167 zGyro: Float,
168 xAccel: Float,
169 yAccel: Float,
170 zAccel: Float
171 )
172
173 /**
174 * Reloads all input devices from the currently loaded Settings::values.players into HID Core
175 */
176 external fun reloadInputDevices()
177
178 /**
179 * Registers a controller to be used with mapping
180 * @param device An [InputDevice] or the input overlay wrapped with [YuzuInputDevice]
181 */
182 external fun registerController(device: YuzuInputDevice)
183
184 /**
185 * Gets the names of input devices that have been registered with the input subsystem via [registerController]
186 */
187 external fun getInputDevices(): Array<String>
188
189 /**
190 * Reads all input profiles from disk. Must be called before creating a profile picker.
191 */
192 external fun loadInputProfiles()
193
194 /**
195 * Gets the names of each available input profile.
196 */
197 external fun getInputProfileNames(): Array<String>
198
199 /**
200 * Checks if the user-provided name for an input profile is valid.
201 * @param name User-provided name for an input profile.
202 * @return Whether [name] is valid or not.
203 */
204 external fun isProfileNameValid(name: String): Boolean
205
206 /**
207 * Creates a new input profile.
208 * @param name The new profile's name.
209 * @param playerIndex Index of the player that's currently being edited. Used to write the profile
210 * name to this player's config.
211 * @return Whether creating the profile was successful or not.
212 */
213 external fun createProfile(name: String, playerIndex: Int): Boolean
214
215 /**
216 * Deletes an input profile.
217 * @param name Name of the profile to delete.
218 * @param playerIndex Index of the player that's currently being edited. Used to remove the profile
219 * name from this player's config if they have it loaded.
220 * @return Whether deleting this profile was successful or not.
221 */
222 external fun deleteProfile(name: String, playerIndex: Int): Boolean
223
224 /**
225 * Loads an input profile.
226 * @param name Name of the input profile to load.
227 * @param playerIndex Index of the player that will have this profile loaded.
228 * @return Whether loading this profile was successful or not.
229 */
230 external fun loadProfile(name: String, playerIndex: Int): Boolean
231
232 /**
233 * Saves an input profile.
234 * @param name Name of the profile to save.
235 * @param playerIndex Index of the player that's currently being edited. Used to write the profile
236 * name to this player's config.
237 * @return Whether saving the profile was successful or not.
238 */
239 external fun saveProfile(name: String, playerIndex: Int): Boolean
240
241 /**
242 * Intended to be used immediately before a call to [NativeConfig.saveControlPlayerValues]
243 * Must be used while per-game config is loaded.
244 */
245 external fun loadPerGameConfiguration(
246 playerIndex: Int,
247 selectedIndex: Int,
248 selectedProfileName: String
249 )
250
251 /**
252 * Tells the input subsystem to start listening for inputs to map.
253 * @param type Type of input to map as shown by the int property in each [InputType].
254 */
255 external fun beginMapping(type: Int)
256
257 /**
258 * Gets an input's [ParamPackage] as a serialized string. Used for input verification before mapping.
259 * Must be run after [beginMapping] and before [stopMapping].
260 */
261 external fun getNextInput(): String
262
263 /**
264 * Tells the input subsystem to stop listening for inputs to map.
265 */
266 external fun stopMapping()
267
268 /**
269 * Updates a controller's mappings with auto-mapping params.
270 * @param playerIndex Index of the player to auto-map.
271 * @param deviceParams [ParamPackage] representing the device to auto-map as received
272 * from [getInputDevices].
273 * @param displayName Name of the device to auto-map as received from the "display" param in [deviceParams].
274 * Intended to be a way to provide a default name for a controller if the "display" param is empty.
275 */
276 fun updateMappingsWithDefault(
277 playerIndex: Int,
278 deviceParams: ParamPackage,
279 displayName: String
280 ) = updateMappingsWithDefaultImpl(playerIndex, deviceParams.serialize(), displayName)
281
282 private external fun updateMappingsWithDefaultImpl(
283 playerIndex: Int,
284 deviceParams: String,
285 displayName: String
286 )
287
288 /**
289 * Gets the params for a specific button.
290 * @param playerIndex Index of the player to get params from.
291 * @param button The [NativeButton] to get params for.
292 * @return A [ParamPackage] representing a player's specific button.
293 */
294 fun getButtonParam(playerIndex: Int, button: NativeButton): ParamPackage =
295 ParamPackage(getButtonParamImpl(playerIndex, button.int))
296
297 private external fun getButtonParamImpl(playerIndex: Int, buttonId: Int): String
298
299 /**
300 * Sets the params for a specific button.
301 * @param playerIndex Index of the player to set params for.
302 * @param button The [NativeButton] to set params for.
303 * @param param A [ParamPackage] to set.
304 */
305 fun setButtonParam(playerIndex: Int, button: NativeButton, param: ParamPackage) =
306 setButtonParamImpl(playerIndex, button.int, param.serialize())
307
308 private external fun setButtonParamImpl(playerIndex: Int, buttonId: Int, param: String)
309
310 /**
311 * Gets the params for a specific stick.
312 * @param playerIndex Index of the player to get params from.
313 * @param stick The [NativeAnalog] to get params for.
314 * @return A [ParamPackage] representing a player's specific stick.
315 */
316 fun getStickParam(playerIndex: Int, stick: NativeAnalog): ParamPackage =
317 ParamPackage(getStickParamImpl(playerIndex, stick.int))
318
319 private external fun getStickParamImpl(playerIndex: Int, stickId: Int): String
320
321 /**
322 * Sets the params for a specific stick.
323 * @param playerIndex Index of the player to set params for.
324 * @param stick The [NativeAnalog] to set params for.
325 * @param param A [ParamPackage] to set.
326 */
327 fun setStickParam(playerIndex: Int, stick: NativeAnalog, param: ParamPackage) =
328 setStickParamImpl(playerIndex, stick.int, param.serialize())
329
330 private external fun setStickParamImpl(playerIndex: Int, stickId: Int, param: String)
331
332 /**
333 * Gets the int representation of a [ButtonName]. Tells you what to show as the mapped input for
334 * a button/analog/other.
335 * @param param A [ParamPackage] that represents a specific button's params.
336 * @return The [ButtonName] for [param].
337 */
338 fun getButtonName(param: ParamPackage): ButtonName =
339 ButtonName.from(getButtonNameImpl(param.serialize()))
340
341 private external fun getButtonNameImpl(param: String): Int
342
343 /**
344 * Gets each supported [NpadStyleIndex] for a given player.
345 * @param playerIndex Index of the player to get supported indexes for.
346 * @return List of each supported [NpadStyleIndex].
347 */
348 fun getSupportedStyleTags(playerIndex: Int): List<NpadStyleIndex> =
349 getSupportedStyleTagsImpl(playerIndex).map { NpadStyleIndex.from(it) }
350
351 private external fun getSupportedStyleTagsImpl(playerIndex: Int): IntArray
352
353 /**
354 * Gets the [NpadStyleIndex] for a given player.
355 * @param playerIndex Index of the player to get an [NpadStyleIndex] from.
356 * @return The [NpadStyleIndex] for a given player.
357 */
358 fun getStyleIndex(playerIndex: Int): NpadStyleIndex =
359 NpadStyleIndex.from(getStyleIndexImpl(playerIndex))
360
361 private external fun getStyleIndexImpl(playerIndex: Int): Int
362
363 /**
364 * Sets the [NpadStyleIndex] for a given player.
365 * @param playerIndex Index of the player to change.
366 * @param style The new style to set.
367 */
368 fun setStyleIndex(playerIndex: Int, style: NpadStyleIndex) =
369 setStyleIndexImpl(playerIndex, style.int)
370
371 private external fun setStyleIndexImpl(playerIndex: Int, styleIndex: Int)
372
373 /**
374 * Checks if a device is a controller.
375 * @param params [ParamPackage] for an input device retrieved from [getInputDevices]
376 * @return Whether the device is a controller or not.
377 */
378 fun isController(params: ParamPackage): Boolean = isControllerImpl(params.serialize())
379
380 private external fun isControllerImpl(params: String): Boolean
381
382 /**
383 * Checks if a controller is connected
384 * @param playerIndex Index of the player to check.
385 * @return Whether the player is connected or not.
386 */
387 external fun getIsConnected(playerIndex: Int): Boolean
388
389 /**
390 * Connects/disconnects a controller and ensures that connection order stays in-tact.
391 * @param playerIndex Index of the player to connect/disconnect.
392 * @param connected Whether to connect or disconnect this controller.
393 */
394 fun connectControllers(playerIndex: Int, connected: Boolean = true) {
395 val connectedControllers = mutableListOf<Boolean>().apply {
396 if (connected) {
397 for (i in 0 until 8) {
398 add(i <= playerIndex)
399 }
400 } else {
401 for (i in 0 until 8) {
402 add(i < playerIndex)
403 }
404 }
405 }
406 connectControllersImpl(connectedControllers.toBooleanArray())
407 }
408
409 private external fun connectControllersImpl(connected: BooleanArray)
410
411 /**
412 * Resets all of the button and analog mappings for a player.
413 * @param playerIndex Index of the player that will have its mappings reset.
414 */
415 external fun resetControllerMappings(playerIndex: Int)
416}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/input/YuzuInputDevice.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/input/YuzuInputDevice.kt
new file mode 100644
index 000000000..15cc38c7f
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/input/YuzuInputDevice.kt
@@ -0,0 +1,93 @@
1// SPDX-FileCopyrightText: 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4package org.yuzu.yuzu_emu.features.input
5
6import android.view.InputDevice
7import androidx.annotation.Keep
8import org.yuzu.yuzu_emu.YuzuApplication
9import org.yuzu.yuzu_emu.R
10import org.yuzu.yuzu_emu.utils.InputHandler.getGUID
11
12@Keep
13interface YuzuInputDevice {
14 fun getName(): String
15
16 fun getGUID(): String
17
18 fun getPort(): Int
19
20 fun getSupportsVibration(): Boolean
21
22 fun vibrate(intensity: Float)
23
24 fun getAxes(): Array<Int> = arrayOf()
25 fun hasKeys(keys: IntArray): BooleanArray = BooleanArray(0)
26}
27
28class YuzuPhysicalDevice(
29 private val device: InputDevice,
30 private val port: Int,
31 useSystemVibrator: Boolean
32) : YuzuInputDevice {
33 private val vibrator = if (useSystemVibrator) {
34 YuzuVibrator.getSystemVibrator()
35 } else {
36 YuzuVibrator.getControllerVibrator(device)
37 }
38
39 override fun getName(): String {
40 return device.name
41 }
42
43 override fun getGUID(): String {
44 return device.getGUID()
45 }
46
47 override fun getPort(): Int {
48 return port
49 }
50
51 override fun getSupportsVibration(): Boolean {
52 return vibrator.supportsVibration()
53 }
54
55 override fun vibrate(intensity: Float) {
56 vibrator.vibrate(intensity)
57 }
58
59 override fun getAxes(): Array<Int> = device.motionRanges.map { it.axis }.toTypedArray()
60 override fun hasKeys(keys: IntArray): BooleanArray = device.hasKeys(*keys)
61}
62
63class YuzuInputOverlayDevice(
64 private val vibration: Boolean,
65 private val port: Int
66) : YuzuInputDevice {
67 private val vibrator = YuzuVibrator.getSystemVibrator()
68
69 override fun getName(): String {
70 return YuzuApplication.appContext.getString(R.string.input_overlay)
71 }
72
73 override fun getGUID(): String {
74 return "00000000000000000000000000000000"
75 }
76
77 override fun getPort(): Int {
78 return port
79 }
80
81 override fun getSupportsVibration(): Boolean {
82 if (vibration) {
83 return vibrator.supportsVibration()
84 }
85 return false
86 }
87
88 override fun vibrate(intensity: Float) {
89 if (vibration) {
90 vibrator.vibrate(intensity)
91 }
92 }
93}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/input/YuzuVibrator.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/input/YuzuVibrator.kt
new file mode 100644
index 000000000..aac49ecae
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/input/YuzuVibrator.kt
@@ -0,0 +1,76 @@
1// SPDX-FileCopyrightText: 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4package org.yuzu.yuzu_emu.features.input
5
6import android.content.Context
7import android.os.Build
8import android.os.CombinedVibration
9import android.os.VibrationEffect
10import android.os.Vibrator
11import android.os.VibratorManager
12import android.view.InputDevice
13import androidx.annotation.Keep
14import androidx.annotation.RequiresApi
15import org.yuzu.yuzu_emu.YuzuApplication
16
17@Keep
18@Suppress("DEPRECATION")
19interface YuzuVibrator {
20 fun supportsVibration(): Boolean
21
22 fun vibrate(intensity: Float)
23
24 companion object {
25 fun getControllerVibrator(device: InputDevice): YuzuVibrator =
26 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
27 YuzuVibratorManager(device.vibratorManager)
28 } else {
29 YuzuVibratorManagerCompat(device.vibrator)
30 }
31
32 fun getSystemVibrator(): YuzuVibrator =
33 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
34 val vibratorManager = YuzuApplication.appContext
35 .getSystemService(Context.VIBRATOR_MANAGER_SERVICE) as VibratorManager
36 YuzuVibratorManager(vibratorManager)
37 } else {
38 val vibrator = YuzuApplication.appContext
39 .getSystemService(Context.VIBRATOR_SERVICE) as Vibrator
40 YuzuVibratorManagerCompat(vibrator)
41 }
42
43 fun getVibrationEffect(intensity: Float): VibrationEffect? {
44 if (intensity > 0f) {
45 return VibrationEffect.createOneShot(
46 50,
47 (255.0 * intensity).toInt().coerceIn(1, 255)
48 )
49 }
50 return null
51 }
52 }
53}
54
55@RequiresApi(Build.VERSION_CODES.S)
56class YuzuVibratorManager(private val vibratorManager: VibratorManager) : YuzuVibrator {
57 override fun supportsVibration(): Boolean {
58 return vibratorManager.vibratorIds.isNotEmpty()
59 }
60
61 override fun vibrate(intensity: Float) {
62 val vibration = YuzuVibrator.getVibrationEffect(intensity) ?: return
63 vibratorManager.vibrate(CombinedVibration.createParallel(vibration))
64 }
65}
66
67class YuzuVibratorManagerCompat(private val vibrator: Vibrator) : YuzuVibrator {
68 override fun supportsVibration(): Boolean {
69 return vibrator.hasVibrator()
70 }
71
72 override fun vibrate(intensity: Float) {
73 val vibration = YuzuVibrator.getVibrationEffect(intensity) ?: return
74 vibrator.vibrate(vibration)
75 }
76}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/input/model/AnalogDirection.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/input/model/AnalogDirection.kt
new file mode 100644
index 000000000..0a5fab2ae
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/input/model/AnalogDirection.kt
@@ -0,0 +1,11 @@
1// SPDX-FileCopyrightText: 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4package org.yuzu.yuzu_emu.features.input.model
5
6enum class AnalogDirection(val int: Int, val param: String) {
7 Up(0, "up"),
8 Down(1, "down"),
9 Left(2, "left"),
10 Right(3, "right")
11}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/input/model/ButtonName.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/input/model/ButtonName.kt
new file mode 100644
index 000000000..b8846ecad
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/input/model/ButtonName.kt
@@ -0,0 +1,19 @@
1// SPDX-FileCopyrightText: 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4package org.yuzu.yuzu_emu.features.input.model
5
6// Loosely matches the enum in common/input.h
7enum class ButtonName(val int: Int) {
8 Invalid(1),
9
10 // This will display the engine name instead of the button name
11 Engine(2),
12
13 // This will display the button by value instead of the button name
14 Value(3);
15
16 companion object {
17 fun from(int: Int): ButtonName = entries.firstOrNull { it.int == int } ?: Invalid
18 }
19}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/input/model/InputType.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/input/model/InputType.kt
new file mode 100644
index 000000000..f725231cb
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/input/model/InputType.kt
@@ -0,0 +1,13 @@
1// SPDX-FileCopyrightText: 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4package org.yuzu.yuzu_emu.features.input.model
5
6// Must match the corresponding enum in input_common/main.h
7enum class InputType(val int: Int) {
8 None(0),
9 Button(1),
10 Stick(2),
11 Motion(3),
12 Touch(4)
13}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/input/model/NativeAnalog.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/input/model/NativeAnalog.kt
new file mode 100644
index 000000000..c3b7a785d
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/input/model/NativeAnalog.kt
@@ -0,0 +1,14 @@
1// SPDX-FileCopyrightText: 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4package org.yuzu.yuzu_emu.features.input.model
5
6// Must match enum in src/common/settings_input.h
7enum class NativeAnalog(val int: Int) {
8 LStick(0),
9 RStick(1);
10
11 companion object {
12 fun from(int: Int): NativeAnalog = entries.firstOrNull { it.int == int } ?: LStick
13 }
14}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/input/model/NativeButton.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/input/model/NativeButton.kt
new file mode 100644
index 000000000..c5ccd7115
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/input/model/NativeButton.kt
@@ -0,0 +1,38 @@
1// SPDX-FileCopyrightText: 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4package org.yuzu.yuzu_emu.features.input.model
5
6// Must match enum in src/common/settings_input.h
7enum class NativeButton(val int: Int) {
8 A(0),
9 B(1),
10 X(2),
11 Y(3),
12 LStick(4),
13 RStick(5),
14 L(6),
15 R(7),
16 ZL(8),
17 ZR(9),
18 Plus(10),
19 Minus(11),
20
21 DLeft(12),
22 DUp(13),
23 DRight(14),
24 DDown(15),
25
26 SLLeft(16),
27 SRLeft(17),
28
29 Home(18),
30 Capture(19),
31
32 SLRight(20),
33 SRRight(21);
34
35 companion object {
36 fun from(int: Int): NativeButton = entries.firstOrNull { it.int == int } ?: A
37 }
38}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/input/model/NativeTrigger.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/input/model/NativeTrigger.kt
new file mode 100644
index 000000000..625f352b4
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/input/model/NativeTrigger.kt
@@ -0,0 +1,10 @@
1// SPDX-FileCopyrightText: 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4package org.yuzu.yuzu_emu.features.input.model
5
6// Must match enum in src/common/settings_input.h
7enum class NativeTrigger(val int: Int) {
8 LTrigger(0),
9 RTrigger(1)
10}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/input/model/NpadStyleIndex.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/input/model/NpadStyleIndex.kt
new file mode 100644
index 000000000..e2a3d7aff
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/input/model/NpadStyleIndex.kt
@@ -0,0 +1,30 @@
1// SPDX-FileCopyrightText: 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4package org.yuzu.yuzu_emu.features.input.model
5
6import androidx.annotation.StringRes
7import org.yuzu.yuzu_emu.R
8
9// Must match enum in src/core/hid/hid_types.h
10enum class NpadStyleIndex(val int: Int, @StringRes val nameId: Int = 0) {
11 None(0),
12 Fullkey(3, R.string.pro_controller),
13 Handheld(4, R.string.handheld),
14 HandheldNES(4),
15 JoyconDual(5, R.string.dual_joycons),
16 JoyconLeft(6, R.string.left_joycon),
17 JoyconRight(7, R.string.right_joycon),
18 GameCube(8, R.string.gamecube_controller),
19 Pokeball(9),
20 NES(10),
21 SNES(12),
22 N64(13),
23 SegaGenesis(14),
24 SystemExt(32),
25 System(33);
26
27 companion object {
28 fun from(int: Int): NpadStyleIndex = entries.firstOrNull { it.int == int } ?: None
29 }
30}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/input/model/PlayerInput.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/input/model/PlayerInput.kt
new file mode 100644
index 000000000..d35de80c4
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/input/model/PlayerInput.kt
@@ -0,0 +1,83 @@
1// SPDX-FileCopyrightText: 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4package org.yuzu.yuzu_emu.features.input.model
5
6import androidx.annotation.Keep
7
8@Keep
9data class PlayerInput(
10 var connected: Boolean,
11 var buttons: Array<String>,
12 var analogs: Array<String>,
13 var motions: Array<String>,
14
15 var vibrationEnabled: Boolean,
16 var vibrationStrength: Int,
17
18 var bodyColorLeft: Long,
19 var bodyColorRight: Long,
20 var buttonColorLeft: Long,
21 var buttonColorRight: Long,
22 var profileName: String,
23
24 var useSystemVibrator: Boolean
25) {
26 // It's recommended to use the generated equals() and hashCode() methods
27 // when using arrays in a data class
28 override fun equals(other: Any?): Boolean {
29 if (this === other) return true
30 if (javaClass != other?.javaClass) return false
31
32 other as PlayerInput
33
34 if (connected != other.connected) return false
35 if (!buttons.contentEquals(other.buttons)) return false
36 if (!analogs.contentEquals(other.analogs)) return false
37 if (!motions.contentEquals(other.motions)) return false
38 if (vibrationEnabled != other.vibrationEnabled) return false
39 if (vibrationStrength != other.vibrationStrength) return false
40 if (bodyColorLeft != other.bodyColorLeft) return false
41 if (bodyColorRight != other.bodyColorRight) return false
42 if (buttonColorLeft != other.buttonColorLeft) return false
43 if (buttonColorRight != other.buttonColorRight) return false
44 if (profileName != other.profileName) return false
45 return useSystemVibrator == other.useSystemVibrator
46 }
47
48 override fun hashCode(): Int {
49 var result = connected.hashCode()
50 result = 31 * result + buttons.contentHashCode()
51 result = 31 * result + analogs.contentHashCode()
52 result = 31 * result + motions.contentHashCode()
53 result = 31 * result + vibrationEnabled.hashCode()
54 result = 31 * result + vibrationStrength
55 result = 31 * result + bodyColorLeft.hashCode()
56 result = 31 * result + bodyColorRight.hashCode()
57 result = 31 * result + buttonColorLeft.hashCode()
58 result = 31 * result + buttonColorRight.hashCode()
59 result = 31 * result + profileName.hashCode()
60 result = 31 * result + useSystemVibrator.hashCode()
61 return result
62 }
63
64 fun hasMapping(): Boolean {
65 var hasMapping = false
66 buttons.forEach {
67 if (it != "[empty]") {
68 hasMapping = true
69 }
70 }
71 analogs.forEach {
72 if (it != "[empty]") {
73 hasMapping = true
74 }
75 }
76 motions.forEach {
77 if (it != "[empty]") {
78 hasMapping = true
79 }
80 }
81 return hasMapping
82 }
83}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/Settings.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/Settings.kt
index 862c6c483..4f6b93bd2 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/Settings.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/Settings.kt
@@ -4,17 +4,30 @@
4package org.yuzu.yuzu_emu.features.settings.model 4package org.yuzu.yuzu_emu.features.settings.model
5 5
6import org.yuzu.yuzu_emu.R 6import org.yuzu.yuzu_emu.R
7import org.yuzu.yuzu_emu.YuzuApplication
7 8
8object Settings { 9object Settings {
9 enum class MenuTag(val titleId: Int) { 10 enum class MenuTag(val titleId: Int = 0) {
10 SECTION_ROOT(R.string.advanced_settings), 11 SECTION_ROOT(R.string.advanced_settings),
11 SECTION_SYSTEM(R.string.preferences_system), 12 SECTION_SYSTEM(R.string.preferences_system),
12 SECTION_RENDERER(R.string.preferences_graphics), 13 SECTION_RENDERER(R.string.preferences_graphics),
13 SECTION_AUDIO(R.string.preferences_audio), 14 SECTION_AUDIO(R.string.preferences_audio),
15 SECTION_INPUT(R.string.preferences_controls),
16 SECTION_INPUT_PLAYER_ONE,
17 SECTION_INPUT_PLAYER_TWO,
18 SECTION_INPUT_PLAYER_THREE,
19 SECTION_INPUT_PLAYER_FOUR,
20 SECTION_INPUT_PLAYER_FIVE,
21 SECTION_INPUT_PLAYER_SIX,
22 SECTION_INPUT_PLAYER_SEVEN,
23 SECTION_INPUT_PLAYER_EIGHT,
14 SECTION_THEME(R.string.preferences_theme), 24 SECTION_THEME(R.string.preferences_theme),
15 SECTION_DEBUG(R.string.preferences_debug); 25 SECTION_DEBUG(R.string.preferences_debug);
16 } 26 }
17 27
28 fun getPlayerString(player: Int): String =
29 YuzuApplication.appContext.getString(R.string.preferences_player, player)
30
18 const val PREF_FIRST_APP_LAUNCH = "FirstApplicationLaunch" 31 const val PREF_FIRST_APP_LAUNCH = "FirstApplicationLaunch"
19 const val PREF_MEMORY_WARNING_SHOWN = "MemoryWarningShown" 32 const val PREF_MEMORY_WARNING_SHOWN = "MemoryWarningShown"
20 33
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/AnalogInputSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/AnalogInputSetting.kt
new file mode 100644
index 000000000..a2996725e
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/AnalogInputSetting.kt
@@ -0,0 +1,31 @@
1// SPDX-FileCopyrightText: 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4package org.yuzu.yuzu_emu.features.settings.model.view
5
6import androidx.annotation.StringRes
7import org.yuzu.yuzu_emu.features.input.NativeInput
8import org.yuzu.yuzu_emu.features.input.model.AnalogDirection
9import org.yuzu.yuzu_emu.features.input.model.InputType
10import org.yuzu.yuzu_emu.features.input.model.NativeAnalog
11import org.yuzu.yuzu_emu.utils.ParamPackage
12
13class AnalogInputSetting(
14 override val playerIndex: Int,
15 val nativeAnalog: NativeAnalog,
16 val analogDirection: AnalogDirection,
17 @StringRes titleId: Int = 0,
18 titleString: String = ""
19) : InputSetting(titleId, titleString) {
20 override val type = TYPE_INPUT
21 override val inputType = InputType.Stick
22
23 override fun getSelectedValue(): String {
24 val params = NativeInput.getStickParam(playerIndex, nativeAnalog)
25 val analog = analogToText(params, analogDirection.param)
26 return getDisplayString(params, analog)
27 }
28
29 override fun setSelectedValue(param: ParamPackage) =
30 NativeInput.setStickParam(playerIndex, nativeAnalog, param)
31}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/ButtonInputSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/ButtonInputSetting.kt
new file mode 100644
index 000000000..786d09a7a
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/ButtonInputSetting.kt
@@ -0,0 +1,29 @@
1// SPDX-FileCopyrightText: 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4package org.yuzu.yuzu_emu.features.settings.model.view
5
6import androidx.annotation.StringRes
7import org.yuzu.yuzu_emu.utils.ParamPackage
8import org.yuzu.yuzu_emu.features.input.NativeInput
9import org.yuzu.yuzu_emu.features.input.model.InputType
10import org.yuzu.yuzu_emu.features.input.model.NativeButton
11
12class ButtonInputSetting(
13 override val playerIndex: Int,
14 val nativeButton: NativeButton,
15 @StringRes titleId: Int = 0,
16 titleString: String = ""
17) : InputSetting(titleId, titleString) {
18 override val type = TYPE_INPUT
19 override val inputType = InputType.Button
20
21 override fun getSelectedValue(): String {
22 val params = NativeInput.getButtonParam(playerIndex, nativeButton)
23 val button = buttonToText(params)
24 return getDisplayString(params, button)
25 }
26
27 override fun setSelectedValue(param: ParamPackage) =
28 NativeInput.setButtonParam(playerIndex, nativeButton, param)
29}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/DateTimeSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/DateTimeSetting.kt
index 1d81f5f2b..58febff1d 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/DateTimeSetting.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/DateTimeSetting.kt
@@ -3,13 +3,16 @@
3 3
4package org.yuzu.yuzu_emu.features.settings.model.view 4package org.yuzu.yuzu_emu.features.settings.model.view
5 5
6import androidx.annotation.StringRes
6import org.yuzu.yuzu_emu.features.settings.model.AbstractLongSetting 7import org.yuzu.yuzu_emu.features.settings.model.AbstractLongSetting
7 8
8class DateTimeSetting( 9class DateTimeSetting(
9 private val longSetting: AbstractLongSetting, 10 private val longSetting: AbstractLongSetting,
10 titleId: Int, 11 @StringRes titleId: Int = 0,
11 descriptionId: Int 12 titleString: String = "",
12) : SettingsItem(longSetting, titleId, descriptionId) { 13 @StringRes descriptionId: Int = 0,
14 descriptionString: String = ""
15) : SettingsItem(longSetting, titleId, titleString, descriptionId, descriptionString) {
13 override val type = TYPE_DATETIME_SETTING 16 override val type = TYPE_DATETIME_SETTING
14 17
15 fun getValue(needsGlobal: Boolean = false): Long = longSetting.getLong(needsGlobal) 18 fun getValue(needsGlobal: Boolean = false): Long = longSetting.getLong(needsGlobal)
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/HeaderSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/HeaderSetting.kt
index d31ce1c31..8a6a51d5c 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/HeaderSetting.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/HeaderSetting.kt
@@ -3,8 +3,11 @@
3 3
4package org.yuzu.yuzu_emu.features.settings.model.view 4package org.yuzu.yuzu_emu.features.settings.model.view
5 5
6import androidx.annotation.StringRes
7
6class HeaderSetting( 8class HeaderSetting(
7 titleId: Int 9 @StringRes titleId: Int = 0,
8) : SettingsItem(emptySetting, titleId, 0) { 10 titleString: String = ""
11) : SettingsItem(emptySetting, titleId, titleString, 0, "") {
9 override val type = TYPE_HEADER 12 override val type = TYPE_HEADER
10} 13}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/InputProfileSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/InputProfileSetting.kt
new file mode 100644
index 000000000..c46de08c5
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/InputProfileSetting.kt
@@ -0,0 +1,32 @@
1// SPDX-FileCopyrightText: 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4package org.yuzu.yuzu_emu.features.settings.model.view
5
6import org.yuzu.yuzu_emu.R
7import org.yuzu.yuzu_emu.features.input.NativeInput
8import org.yuzu.yuzu_emu.utils.NativeConfig
9
10class InputProfileSetting(private val playerIndex: Int) :
11 SettingsItem(emptySetting, R.string.profile, "", 0, "") {
12 override val type = TYPE_INPUT_PROFILE
13
14 fun getCurrentProfile(): String =
15 NativeConfig.getInputSettings(true)[playerIndex].profileName
16
17 fun getProfileNames(): Array<String> = NativeInput.getInputProfileNames()
18
19 fun isProfileNameValid(name: String): Boolean = NativeInput.isProfileNameValid(name)
20
21 fun createProfile(name: String): Boolean = NativeInput.createProfile(name, playerIndex)
22
23 fun deleteProfile(name: String): Boolean = NativeInput.deleteProfile(name, playerIndex)
24
25 fun loadProfile(name: String): Boolean {
26 val result = NativeInput.loadProfile(name, playerIndex)
27 NativeInput.reloadInputDevices()
28 return result
29 }
30
31 fun saveProfile(name: String): Boolean = NativeInput.saveProfile(name, playerIndex)
32}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/InputSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/InputSetting.kt
new file mode 100644
index 000000000..2d118bff3
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/InputSetting.kt
@@ -0,0 +1,134 @@
1// SPDX-FileCopyrightText: 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4package org.yuzu.yuzu_emu.features.settings.model.view
5
6import androidx.annotation.StringRes
7import org.yuzu.yuzu_emu.R
8import org.yuzu.yuzu_emu.YuzuApplication
9import org.yuzu.yuzu_emu.features.input.NativeInput
10import org.yuzu.yuzu_emu.features.input.model.ButtonName
11import org.yuzu.yuzu_emu.features.input.model.InputType
12import org.yuzu.yuzu_emu.utils.ParamPackage
13
14sealed class InputSetting(
15 @StringRes titleId: Int,
16 titleString: String
17) : SettingsItem(emptySetting, titleId, titleString, 0, "") {
18 override val type = TYPE_INPUT
19 abstract val inputType: InputType
20 abstract val playerIndex: Int
21
22 protected val context get() = YuzuApplication.appContext
23
24 abstract fun getSelectedValue(): String
25
26 abstract fun setSelectedValue(param: ParamPackage)
27
28 protected fun getDisplayString(params: ParamPackage, control: String): String {
29 val deviceName = params.get("display", "")
30 deviceName.ifEmpty {
31 return context.getString(R.string.not_set)
32 }
33 return "$deviceName: $control"
34 }
35
36 private fun getDirectionName(direction: String): String =
37 when (direction) {
38 "up" -> context.getString(R.string.up)
39 "down" -> context.getString(R.string.down)
40 "left" -> context.getString(R.string.left)
41 "right" -> context.getString(R.string.right)
42 else -> direction
43 }
44
45 protected fun buttonToText(param: ParamPackage): String {
46 if (!param.has("engine")) {
47 return context.getString(R.string.not_set)
48 }
49
50 val toggle = if (param.get("toggle", false)) "~" else ""
51 val inverted = if (param.get("inverted", false)) "!" else ""
52 val invert = if (param.get("invert", "+") == "-") "-" else ""
53 val turbo = if (param.get("turbo", false)) "$" else ""
54 val commonButtonName = NativeInput.getButtonName(param)
55
56 if (commonButtonName == ButtonName.Invalid) {
57 return context.getString(R.string.invalid)
58 }
59
60 if (commonButtonName == ButtonName.Engine) {
61 return param.get("engine", "")
62 }
63
64 if (commonButtonName == ButtonName.Value) {
65 if (param.has("hat")) {
66 val hat = getDirectionName(param.get("direction", ""))
67 return context.getString(R.string.qualified_hat, turbo, toggle, inverted, hat)
68 }
69 if (param.has("axis")) {
70 val axis = param.get("axis", "")
71 return context.getString(
72 R.string.qualified_button_stick_axis,
73 toggle,
74 inverted,
75 invert,
76 axis
77 )
78 }
79 if (param.has("button")) {
80 val button = param.get("button", "")
81 return context.getString(R.string.qualified_button, turbo, toggle, inverted, button)
82 }
83 }
84
85 return context.getString(R.string.unknown)
86 }
87
88 protected fun analogToText(param: ParamPackage, direction: String): String {
89 if (!param.has("engine")) {
90 return context.getString(R.string.not_set)
91 }
92
93 if (param.get("engine", "") == "analog_from_button") {
94 return buttonToText(ParamPackage(param.get(direction, "")))
95 }
96
97 if (!param.has("axis_x") || !param.has("axis_y")) {
98 return context.getString(R.string.unknown)
99 }
100
101 val xAxis = param.get("axis_x", "")
102 val yAxis = param.get("axis_y", "")
103 val xInvert = param.get("invert_x", "+") == "-"
104 val yInvert = param.get("invert_y", "+") == "-"
105
106 if (direction == "modifier") {
107 return context.getString(R.string.unused)
108 }
109
110 when (direction) {
111 "up" -> {
112 val yInvertString = if (yInvert) "+" else "-"
113 return context.getString(R.string.qualified_axis, yAxis, yInvertString)
114 }
115
116 "down" -> {
117 val yInvertString = if (yInvert) "-" else "+"
118 return context.getString(R.string.qualified_axis, yAxis, yInvertString)
119 }
120
121 "left" -> {
122 val xInvertString = if (xInvert) "+" else "-"
123 return context.getString(R.string.qualified_axis, xAxis, xInvertString)
124 }
125
126 "right" -> {
127 val xInvertString = if (xInvert) "-" else "+"
128 return context.getString(R.string.qualified_axis, xAxis, xInvertString)
129 }
130 }
131
132 return context.getString(R.string.unknown)
133 }
134}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/IntSingleChoiceSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/IntSingleChoiceSetting.kt
new file mode 100644
index 000000000..e024c793a
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/IntSingleChoiceSetting.kt
@@ -0,0 +1,38 @@
1// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4package org.yuzu.yuzu_emu.features.settings.model.view
5
6import androidx.annotation.StringRes
7import org.yuzu.yuzu_emu.features.settings.model.AbstractIntSetting
8
9class IntSingleChoiceSetting(
10 private val intSetting: AbstractIntSetting,
11 @StringRes titleId: Int = 0,
12 titleString: String = "",
13 @StringRes descriptionId: Int = 0,
14 descriptionString: String = "",
15 val choices: Array<String>,
16 val values: Array<Int>
17) : SettingsItem(intSetting, titleId, titleString, descriptionId, descriptionString) {
18 override val type = TYPE_INT_SINGLE_CHOICE
19
20 fun getValueAt(index: Int): Int =
21 if (values.indices.contains(index)) values[index] else -1
22
23 fun getChoiceAt(index: Int): String =
24 if (choices.indices.contains(index)) choices[index] else ""
25
26 fun getSelectedValue(needsGlobal: Boolean = false) = intSetting.getInt(needsGlobal)
27 fun setSelectedValue(value: Int) = intSetting.setInt(value)
28
29 val selectedValueIndex: Int
30 get() {
31 for (i in values.indices) {
32 if (values[i] == getSelectedValue()) {
33 return i
34 }
35 }
36 return -1
37 }
38}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/ModifierInputSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/ModifierInputSetting.kt
new file mode 100644
index 000000000..a1db3cc87
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/ModifierInputSetting.kt
@@ -0,0 +1,31 @@
1// SPDX-FileCopyrightText: 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4package org.yuzu.yuzu_emu.features.settings.model.view
5
6import androidx.annotation.StringRes
7import org.yuzu.yuzu_emu.features.input.NativeInput
8import org.yuzu.yuzu_emu.features.input.model.InputType
9import org.yuzu.yuzu_emu.features.input.model.NativeAnalog
10import org.yuzu.yuzu_emu.utils.ParamPackage
11
12class ModifierInputSetting(
13 override val playerIndex: Int,
14 val nativeAnalog: NativeAnalog,
15 @StringRes titleId: Int = 0,
16 titleString: String = ""
17) : InputSetting(titleId, titleString) {
18 override val inputType = InputType.Button
19
20 override fun getSelectedValue(): String {
21 val analogParam = NativeInput.getStickParam(playerIndex, nativeAnalog)
22 val modifierParam = ParamPackage(analogParam.get("modifier", ""))
23 return buttonToText(modifierParam)
24 }
25
26 override fun setSelectedValue(param: ParamPackage) {
27 val newParam = NativeInput.getStickParam(playerIndex, nativeAnalog)
28 newParam.set("modifier", param.serialize())
29 NativeInput.setStickParam(playerIndex, nativeAnalog, newParam)
30 }
31}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/RunnableSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/RunnableSetting.kt
index 425160024..06f607424 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/RunnableSetting.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/RunnableSetting.kt
@@ -4,13 +4,16 @@
4package org.yuzu.yuzu_emu.features.settings.model.view 4package org.yuzu.yuzu_emu.features.settings.model.view
5 5
6import androidx.annotation.DrawableRes 6import androidx.annotation.DrawableRes
7import androidx.annotation.StringRes
7 8
8class RunnableSetting( 9class RunnableSetting(
9 titleId: Int, 10 @StringRes titleId: Int = 0,
10 descriptionId: Int, 11 titleString: String = "",
11 val isRuntimeRunnable: Boolean, 12 @StringRes descriptionId: Int = 0,
13 descriptionString: String = "",
14 val isRunnable: Boolean,
12 @DrawableRes val iconId: Int = 0, 15 @DrawableRes val iconId: Int = 0,
13 val runnable: () -> Unit 16 val runnable: () -> Unit
14) : SettingsItem(emptySetting, titleId, descriptionId) { 17) : SettingsItem(emptySetting, titleId, titleString, descriptionId, descriptionString) {
15 override val type = TYPE_RUNNABLE 18 override val type = TYPE_RUNNABLE
16} 19}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt
index 21ca97bc1..8f724835e 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt
@@ -3,8 +3,12 @@
3 3
4package org.yuzu.yuzu_emu.features.settings.model.view 4package org.yuzu.yuzu_emu.features.settings.model.view
5 5
6import androidx.annotation.StringRes
6import org.yuzu.yuzu_emu.NativeLibrary 7import org.yuzu.yuzu_emu.NativeLibrary
7import org.yuzu.yuzu_emu.R 8import org.yuzu.yuzu_emu.R
9import org.yuzu.yuzu_emu.YuzuApplication
10import org.yuzu.yuzu_emu.features.input.NativeInput
11import org.yuzu.yuzu_emu.features.input.model.NpadStyleIndex
8import org.yuzu.yuzu_emu.features.settings.model.AbstractBooleanSetting 12import org.yuzu.yuzu_emu.features.settings.model.AbstractBooleanSetting
9import org.yuzu.yuzu_emu.features.settings.model.AbstractSetting 13import org.yuzu.yuzu_emu.features.settings.model.AbstractSetting
10import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting 14import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting
@@ -23,13 +27,34 @@ import org.yuzu.yuzu_emu.utils.NativeConfig
23 */ 27 */
24abstract class SettingsItem( 28abstract class SettingsItem(
25 val setting: AbstractSetting, 29 val setting: AbstractSetting,
26 val nameId: Int, 30 @StringRes val titleId: Int,
27 val descriptionId: Int 31 val titleString: String,
32 @StringRes val descriptionId: Int,
33 val descriptionString: String
28) { 34) {
29 abstract val type: Int 35 abstract val type: Int
30 36
37 val title: String by lazy {
38 if (titleId != 0) {
39 return@lazy YuzuApplication.appContext.getString(titleId)
40 }
41 return@lazy titleString
42 }
43
44 val description: String by lazy {
45 if (descriptionId != 0) {
46 return@lazy YuzuApplication.appContext.getString(descriptionId)
47 }
48 return@lazy descriptionString
49 }
50
31 val isEditable: Boolean 51 val isEditable: Boolean
32 get() { 52 get() {
53 // Can't change docked mode toggle when using handheld mode
54 if (setting.key == BooleanSetting.USE_DOCKED_MODE.key) {
55 return NativeInput.getStyleIndex(0) != NpadStyleIndex.Handheld
56 }
57
33 // Can't edit settings that aren't saveable in per-game config even if they are switchable 58 // Can't edit settings that aren't saveable in per-game config even if they are switchable
34 if (NativeConfig.isPerGameConfigLoaded() && !setting.isSaveable) { 59 if (NativeConfig.isPerGameConfigLoaded() && !setting.isSaveable) {
35 return false 60 return false
@@ -59,6 +84,9 @@ abstract class SettingsItem(
59 const val TYPE_STRING_SINGLE_CHOICE = 5 84 const val TYPE_STRING_SINGLE_CHOICE = 5
60 const val TYPE_DATETIME_SETTING = 6 85 const val TYPE_DATETIME_SETTING = 6
61 const val TYPE_RUNNABLE = 7 86 const val TYPE_RUNNABLE = 7
87 const val TYPE_INPUT = 8
88 const val TYPE_INT_SINGLE_CHOICE = 9
89 const val TYPE_INPUT_PROFILE = 10
62 90
63 const val FASTMEM_COMBINED = "fastmem_combined" 91 const val FASTMEM_COMBINED = "fastmem_combined"
64 92
@@ -80,237 +108,242 @@ abstract class SettingsItem(
80 put( 108 put(
81 SwitchSetting( 109 SwitchSetting(
82 BooleanSetting.RENDERER_USE_SPEED_LIMIT, 110 BooleanSetting.RENDERER_USE_SPEED_LIMIT,
83 R.string.frame_limit_enable, 111 titleId = R.string.frame_limit_enable,
84 R.string.frame_limit_enable_description 112 descriptionId = R.string.frame_limit_enable_description
85 ) 113 )
86 ) 114 )
87 put( 115 put(
88 SliderSetting( 116 SliderSetting(
89 ShortSetting.RENDERER_SPEED_LIMIT, 117 ShortSetting.RENDERER_SPEED_LIMIT,
90 R.string.frame_limit_slider, 118 titleId = R.string.frame_limit_slider,
91 R.string.frame_limit_slider_description, 119 descriptionId = R.string.frame_limit_slider_description,
92 1, 120 min = 1,
93 400, 121 max = 400,
94 "%" 122 units = "%"
95 ) 123 )
96 ) 124 )
97 put( 125 put(
98 SingleChoiceSetting( 126 SingleChoiceSetting(
99 IntSetting.CPU_BACKEND, 127 IntSetting.CPU_BACKEND,
100 R.string.cpu_backend, 128 titleId = R.string.cpu_backend,
101 0, 129 choicesId = R.array.cpuBackendArm64Names,
102 R.array.cpuBackendArm64Names, 130 valuesId = R.array.cpuBackendArm64Values
103 R.array.cpuBackendArm64Values
104 ) 131 )
105 ) 132 )
106 put( 133 put(
107 SingleChoiceSetting( 134 SingleChoiceSetting(
108 IntSetting.CPU_ACCURACY, 135 IntSetting.CPU_ACCURACY,
109 R.string.cpu_accuracy, 136 titleId = R.string.cpu_accuracy,
110 0, 137 choicesId = R.array.cpuAccuracyNames,
111 R.array.cpuAccuracyNames, 138 valuesId = R.array.cpuAccuracyValues
112 R.array.cpuAccuracyValues
113 ) 139 )
114 ) 140 )
115 put( 141 put(
116 SwitchSetting( 142 SwitchSetting(
117 BooleanSetting.PICTURE_IN_PICTURE, 143 BooleanSetting.PICTURE_IN_PICTURE,
118 R.string.picture_in_picture, 144 titleId = R.string.picture_in_picture,
119 R.string.picture_in_picture_description 145 descriptionId = R.string.picture_in_picture_description
120 ) 146 )
121 ) 147 )
148
149 val dockedModeSetting = object : AbstractBooleanSetting {
150 override val key = BooleanSetting.USE_DOCKED_MODE.key
151
152 override fun getBoolean(needsGlobal: Boolean): Boolean {
153 if (NativeInput.getStyleIndex(0) == NpadStyleIndex.Handheld) {
154 return false
155 }
156 return BooleanSetting.USE_DOCKED_MODE.getBoolean(needsGlobal)
157 }
158
159 override fun setBoolean(value: Boolean) =
160 BooleanSetting.USE_DOCKED_MODE.setBoolean(value)
161
162 override val defaultValue = BooleanSetting.USE_DOCKED_MODE.defaultValue
163
164 override fun getValueAsString(needsGlobal: Boolean): String =
165 BooleanSetting.USE_DOCKED_MODE.getValueAsString(needsGlobal)
166
167 override fun reset() = BooleanSetting.USE_DOCKED_MODE.reset()
168 }
122 put( 169 put(
123 SwitchSetting( 170 SwitchSetting(
124 BooleanSetting.USE_DOCKED_MODE, 171 dockedModeSetting,
125 R.string.use_docked_mode, 172 titleId = R.string.use_docked_mode,
126 R.string.use_docked_mode_description 173 descriptionId = R.string.use_docked_mode_description
127 ) 174 )
128 ) 175 )
176
129 put( 177 put(
130 SingleChoiceSetting( 178 SingleChoiceSetting(
131 IntSetting.REGION_INDEX, 179 IntSetting.REGION_INDEX,
132 R.string.emulated_region, 180 titleId = R.string.emulated_region,
133 0, 181 choicesId = R.array.regionNames,
134 R.array.regionNames, 182 valuesId = R.array.regionValues
135 R.array.regionValues
136 ) 183 )
137 ) 184 )
138 put( 185 put(
139 SingleChoiceSetting( 186 SingleChoiceSetting(
140 IntSetting.LANGUAGE_INDEX, 187 IntSetting.LANGUAGE_INDEX,
141 R.string.emulated_language, 188 titleId = R.string.emulated_language,
142 0, 189 choicesId = R.array.languageNames,
143 R.array.languageNames, 190 valuesId = R.array.languageValues
144 R.array.languageValues
145 ) 191 )
146 ) 192 )
147 put( 193 put(
148 SwitchSetting( 194 SwitchSetting(
149 BooleanSetting.USE_CUSTOM_RTC, 195 BooleanSetting.USE_CUSTOM_RTC,
150 R.string.use_custom_rtc, 196 titleId = R.string.use_custom_rtc,
151 R.string.use_custom_rtc_description 197 descriptionId = R.string.use_custom_rtc_description
152 ) 198 )
153 ) 199 )
154 put(DateTimeSetting(LongSetting.CUSTOM_RTC, R.string.set_custom_rtc, 0)) 200 put(DateTimeSetting(LongSetting.CUSTOM_RTC, titleId = R.string.set_custom_rtc))
155 put( 201 put(
156 SingleChoiceSetting( 202 SingleChoiceSetting(
157 IntSetting.RENDERER_ACCURACY, 203 IntSetting.RENDERER_ACCURACY,
158 R.string.renderer_accuracy, 204 titleId = R.string.renderer_accuracy,
159 0, 205 choicesId = R.array.rendererAccuracyNames,
160 R.array.rendererAccuracyNames, 206 valuesId = R.array.rendererAccuracyValues
161 R.array.rendererAccuracyValues
162 ) 207 )
163 ) 208 )
164 put( 209 put(
165 SingleChoiceSetting( 210 SingleChoiceSetting(
166 IntSetting.RENDERER_RESOLUTION, 211 IntSetting.RENDERER_RESOLUTION,
167 R.string.renderer_resolution, 212 titleId = R.string.renderer_resolution,
168 0, 213 choicesId = R.array.rendererResolutionNames,
169 R.array.rendererResolutionNames, 214 valuesId = R.array.rendererResolutionValues
170 R.array.rendererResolutionValues
171 ) 215 )
172 ) 216 )
173 put( 217 put(
174 SingleChoiceSetting( 218 SingleChoiceSetting(
175 IntSetting.RENDERER_VSYNC, 219 IntSetting.RENDERER_VSYNC,
176 R.string.renderer_vsync, 220 titleId = R.string.renderer_vsync,
177 0, 221 choicesId = R.array.rendererVSyncNames,
178 R.array.rendererVSyncNames, 222 valuesId = R.array.rendererVSyncValues
179 R.array.rendererVSyncValues
180 ) 223 )
181 ) 224 )
182 put( 225 put(
183 SingleChoiceSetting( 226 SingleChoiceSetting(
184 IntSetting.RENDERER_SCALING_FILTER, 227 IntSetting.RENDERER_SCALING_FILTER,
185 R.string.renderer_scaling_filter, 228 titleId = R.string.renderer_scaling_filter,
186 0, 229 choicesId = R.array.rendererScalingFilterNames,
187 R.array.rendererScalingFilterNames, 230 valuesId = R.array.rendererScalingFilterValues
188 R.array.rendererScalingFilterValues
189 ) 231 )
190 ) 232 )
191 put( 233 put(
192 SliderSetting( 234 SliderSetting(
193 IntSetting.FSR_SHARPENING_SLIDER, 235 IntSetting.FSR_SHARPENING_SLIDER,
194 R.string.fsr_sharpness, 236 titleId = R.string.fsr_sharpness,
195 R.string.fsr_sharpness_description, 237 descriptionId = R.string.fsr_sharpness_description,
196 0, 238 units = "%"
197 100,
198 "%"
199 ) 239 )
200 ) 240 )
201 put( 241 put(
202 SingleChoiceSetting( 242 SingleChoiceSetting(
203 IntSetting.RENDERER_ANTI_ALIASING, 243 IntSetting.RENDERER_ANTI_ALIASING,
204 R.string.renderer_anti_aliasing, 244 titleId = R.string.renderer_anti_aliasing,
205 0, 245 choicesId = R.array.rendererAntiAliasingNames,
206 R.array.rendererAntiAliasingNames, 246 valuesId = R.array.rendererAntiAliasingValues
207 R.array.rendererAntiAliasingValues
208 ) 247 )
209 ) 248 )
210 put( 249 put(
211 SingleChoiceSetting( 250 SingleChoiceSetting(
212 IntSetting.RENDERER_SCREEN_LAYOUT, 251 IntSetting.RENDERER_SCREEN_LAYOUT,
213 R.string.renderer_screen_layout, 252 titleId = R.string.renderer_screen_layout,
214 0, 253 choicesId = R.array.rendererScreenLayoutNames,
215 R.array.rendererScreenLayoutNames, 254 valuesId = R.array.rendererScreenLayoutValues
216 R.array.rendererScreenLayoutValues
217 ) 255 )
218 ) 256 )
219 put( 257 put(
220 SingleChoiceSetting( 258 SingleChoiceSetting(
221 IntSetting.RENDERER_ASPECT_RATIO, 259 IntSetting.RENDERER_ASPECT_RATIO,
222 R.string.renderer_aspect_ratio, 260 titleId = R.string.renderer_aspect_ratio,
223 0, 261 choicesId = R.array.rendererAspectRatioNames,
224 R.array.rendererAspectRatioNames, 262 valuesId = R.array.rendererAspectRatioValues
225 R.array.rendererAspectRatioValues
226 ) 263 )
227 ) 264 )
228 put( 265 put(
229 SingleChoiceSetting( 266 SingleChoiceSetting(
230 IntSetting.VERTICAL_ALIGNMENT, 267 IntSetting.VERTICAL_ALIGNMENT,
231 R.string.vertical_alignment, 268 titleId = R.string.vertical_alignment,
232 0, 269 descriptionId = 0,
233 R.array.verticalAlignmentEntries, 270 choicesId = R.array.verticalAlignmentEntries,
234 R.array.verticalAlignmentValues 271 valuesId = R.array.verticalAlignmentValues
235 ) 272 )
236 ) 273 )
237 put( 274 put(
238 SwitchSetting( 275 SwitchSetting(
239 BooleanSetting.RENDERER_USE_DISK_SHADER_CACHE, 276 BooleanSetting.RENDERER_USE_DISK_SHADER_CACHE,
240 R.string.use_disk_shader_cache, 277 titleId = R.string.use_disk_shader_cache,
241 R.string.use_disk_shader_cache_description 278 descriptionId = R.string.use_disk_shader_cache_description
242 ) 279 )
243 ) 280 )
244 put( 281 put(
245 SwitchSetting( 282 SwitchSetting(
246 BooleanSetting.RENDERER_FORCE_MAX_CLOCK, 283 BooleanSetting.RENDERER_FORCE_MAX_CLOCK,
247 R.string.renderer_force_max_clock, 284 titleId = R.string.renderer_force_max_clock,
248 R.string.renderer_force_max_clock_description 285 descriptionId = R.string.renderer_force_max_clock_description
249 ) 286 )
250 ) 287 )
251 put( 288 put(
252 SwitchSetting( 289 SwitchSetting(
253 BooleanSetting.RENDERER_ASYNCHRONOUS_SHADERS, 290 BooleanSetting.RENDERER_ASYNCHRONOUS_SHADERS,
254 R.string.renderer_asynchronous_shaders, 291 titleId = R.string.renderer_asynchronous_shaders,
255 R.string.renderer_asynchronous_shaders_description 292 descriptionId = R.string.renderer_asynchronous_shaders_description
256 ) 293 )
257 ) 294 )
258 put( 295 put(
259 SwitchSetting( 296 SwitchSetting(
260 BooleanSetting.RENDERER_REACTIVE_FLUSHING, 297 BooleanSetting.RENDERER_REACTIVE_FLUSHING,
261 R.string.renderer_reactive_flushing, 298 titleId = R.string.renderer_reactive_flushing,
262 R.string.renderer_reactive_flushing_description 299 descriptionId = R.string.renderer_reactive_flushing_description
263 ) 300 )
264 ) 301 )
265 put( 302 put(
266 SingleChoiceSetting( 303 SingleChoiceSetting(
267 IntSetting.MAX_ANISOTROPY, 304 IntSetting.MAX_ANISOTROPY,
268 R.string.anisotropic_filtering, 305 titleId = R.string.anisotropic_filtering,
269 R.string.anisotropic_filtering_description, 306 descriptionId = R.string.anisotropic_filtering_description,
270 R.array.anisoEntries, 307 choicesId = R.array.anisoEntries,
271 R.array.anisoValues 308 valuesId = R.array.anisoValues
272 ) 309 )
273 ) 310 )
274 put( 311 put(
275 SingleChoiceSetting( 312 SingleChoiceSetting(
276 IntSetting.AUDIO_OUTPUT_ENGINE, 313 IntSetting.AUDIO_OUTPUT_ENGINE,
277 R.string.audio_output_engine, 314 titleId = R.string.audio_output_engine,
278 0, 315 choicesId = R.array.outputEngineEntries,
279 R.array.outputEngineEntries, 316 valuesId = R.array.outputEngineValues
280 R.array.outputEngineValues
281 ) 317 )
282 ) 318 )
283 put( 319 put(
284 SliderSetting( 320 SliderSetting(
285 ByteSetting.AUDIO_VOLUME, 321 ByteSetting.AUDIO_VOLUME,
286 R.string.audio_volume, 322 titleId = R.string.audio_volume,
287 R.string.audio_volume_description, 323 descriptionId = R.string.audio_volume_description,
288 0, 324 units = "%"
289 100,
290 "%"
291 ) 325 )
292 ) 326 )
293 put( 327 put(
294 SingleChoiceSetting( 328 SingleChoiceSetting(
295 IntSetting.RENDERER_BACKEND, 329 IntSetting.RENDERER_BACKEND,
296 R.string.renderer_api, 330 titleId = R.string.renderer_api,
297 0, 331 choicesId = R.array.rendererApiNames,
298 R.array.rendererApiNames, 332 valuesId = R.array.rendererApiValues
299 R.array.rendererApiValues
300 ) 333 )
301 ) 334 )
302 put( 335 put(
303 SwitchSetting( 336 SwitchSetting(
304 BooleanSetting.RENDERER_DEBUG, 337 BooleanSetting.RENDERER_DEBUG,
305 R.string.renderer_debug, 338 titleId = R.string.renderer_debug,
306 R.string.renderer_debug_description 339 descriptionId = R.string.renderer_debug_description
307 ) 340 )
308 ) 341 )
309 put( 342 put(
310 SwitchSetting( 343 SwitchSetting(
311 BooleanSetting.CPU_DEBUG_MODE, 344 BooleanSetting.CPU_DEBUG_MODE,
312 R.string.cpu_debug_mode, 345 titleId = R.string.cpu_debug_mode,
313 R.string.cpu_debug_mode_description 346 descriptionId = R.string.cpu_debug_mode_description
314 ) 347 )
315 ) 348 )
316 349
@@ -346,7 +379,7 @@ abstract class SettingsItem(
346 379
347 override fun reset() = setBoolean(defaultValue) 380 override fun reset() = setBoolean(defaultValue)
348 } 381 }
349 put(SwitchSetting(fastmem, R.string.fastmem, 0)) 382 put(SwitchSetting(fastmem, R.string.fastmem))
350 } 383 }
351 } 384 }
352} 385}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SingleChoiceSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SingleChoiceSetting.kt
index 97a5a9e59..ea5e099ed 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SingleChoiceSetting.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SingleChoiceSetting.kt
@@ -3,16 +3,20 @@
3 3
4package org.yuzu.yuzu_emu.features.settings.model.view 4package org.yuzu.yuzu_emu.features.settings.model.view
5 5
6import androidx.annotation.ArrayRes
7import androidx.annotation.StringRes
6import org.yuzu.yuzu_emu.features.settings.model.AbstractIntSetting 8import org.yuzu.yuzu_emu.features.settings.model.AbstractIntSetting
7import org.yuzu.yuzu_emu.features.settings.model.AbstractSetting 9import org.yuzu.yuzu_emu.features.settings.model.AbstractSetting
8 10
9class SingleChoiceSetting( 11class SingleChoiceSetting(
10 setting: AbstractSetting, 12 setting: AbstractSetting,
11 titleId: Int, 13 @StringRes titleId: Int = 0,
12 descriptionId: Int, 14 titleString: String = "",
13 val choicesId: Int, 15 @StringRes descriptionId: Int = 0,
14 val valuesId: Int 16 descriptionString: String = "",
15) : SettingsItem(setting, titleId, descriptionId) { 17 @ArrayRes val choicesId: Int,
18 @ArrayRes val valuesId: Int
19) : SettingsItem(setting, titleId, titleString, descriptionId, descriptionString) {
16 override val type = TYPE_SINGLE_CHOICE 20 override val type = TYPE_SINGLE_CHOICE
17 21
18 fun getSelectedValue(needsGlobal: Boolean = false) = 22 fun getSelectedValue(needsGlobal: Boolean = false) =
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SliderSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SliderSetting.kt
index b9b709bf7..6a5cdf48b 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SliderSetting.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SliderSetting.kt
@@ -3,6 +3,7 @@
3 3
4package org.yuzu.yuzu_emu.features.settings.model.view 4package org.yuzu.yuzu_emu.features.settings.model.view
5 5
6import androidx.annotation.StringRes
6import org.yuzu.yuzu_emu.features.settings.model.AbstractByteSetting 7import org.yuzu.yuzu_emu.features.settings.model.AbstractByteSetting
7import org.yuzu.yuzu_emu.features.settings.model.AbstractFloatSetting 8import org.yuzu.yuzu_emu.features.settings.model.AbstractFloatSetting
8import org.yuzu.yuzu_emu.features.settings.model.AbstractIntSetting 9import org.yuzu.yuzu_emu.features.settings.model.AbstractIntSetting
@@ -12,12 +13,14 @@ import kotlin.math.roundToInt
12 13
13class SliderSetting( 14class SliderSetting(
14 setting: AbstractSetting, 15 setting: AbstractSetting,
15 titleId: Int, 16 @StringRes titleId: Int = 0,
16 descriptionId: Int, 17 titleString: String = "",
17 val min: Int, 18 @StringRes descriptionId: Int = 0,
18 val max: Int, 19 descriptionString: String = "",
19 val units: String 20 val min: Int = 0,
20) : SettingsItem(setting, titleId, descriptionId) { 21 val max: Int = 100,
22 val units: String = ""
23) : SettingsItem(setting, titleId, titleString, descriptionId, descriptionString) {
21 override val type = TYPE_SLIDER 24 override val type = TYPE_SLIDER
22 25
23 fun getSelectedValue(needsGlobal: Boolean = false) = 26 fun getSelectedValue(needsGlobal: Boolean = false) =
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/StringSingleChoiceSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/StringSingleChoiceSetting.kt
index ba7920f50..5260ff4dc 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/StringSingleChoiceSetting.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/StringSingleChoiceSetting.kt
@@ -3,15 +3,18 @@
3 3
4package org.yuzu.yuzu_emu.features.settings.model.view 4package org.yuzu.yuzu_emu.features.settings.model.view
5 5
6import androidx.annotation.StringRes
6import org.yuzu.yuzu_emu.features.settings.model.AbstractStringSetting 7import org.yuzu.yuzu_emu.features.settings.model.AbstractStringSetting
7 8
8class StringSingleChoiceSetting( 9class StringSingleChoiceSetting(
9 private val stringSetting: AbstractStringSetting, 10 private val stringSetting: AbstractStringSetting,
10 titleId: Int, 11 @StringRes titleId: Int = 0,
11 descriptionId: Int, 12 titleString: String = "",
13 @StringRes descriptionId: Int = 0,
14 descriptionString: String = "",
12 val choices: Array<String>, 15 val choices: Array<String>,
13 val values: Array<String> 16 val values: Array<String>
14) : SettingsItem(stringSetting, titleId, descriptionId) { 17) : SettingsItem(stringSetting, titleId, titleString, descriptionId, descriptionString) {
15 override val type = TYPE_STRING_SINGLE_CHOICE 18 override val type = TYPE_STRING_SINGLE_CHOICE
16 19
17 fun getValueAt(index: Int): String = 20 fun getValueAt(index: Int): String =
@@ -20,7 +23,7 @@ class StringSingleChoiceSetting(
20 fun getSelectedValue(needsGlobal: Boolean = false) = stringSetting.getString(needsGlobal) 23 fun getSelectedValue(needsGlobal: Boolean = false) = stringSetting.getString(needsGlobal)
21 fun setSelectedValue(value: String) = stringSetting.setString(value) 24 fun setSelectedValue(value: String) = stringSetting.setString(value)
22 25
23 val selectValueIndex: Int 26 val selectedValueIndex: Int
24 get() { 27 get() {
25 for (i in values.indices) { 28 for (i in values.indices) {
26 if (values[i] == getSelectedValue()) { 29 if (values[i] == getSelectedValue()) {
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SubmenuSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SubmenuSetting.kt
index 94953b18a..c722393dd 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SubmenuSetting.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SubmenuSetting.kt
@@ -8,10 +8,12 @@ import androidx.annotation.StringRes
8import org.yuzu.yuzu_emu.features.settings.model.Settings 8import org.yuzu.yuzu_emu.features.settings.model.Settings
9 9
10class SubmenuSetting( 10class SubmenuSetting(
11 @StringRes titleId: Int, 11 @StringRes titleId: Int = 0,
12 @StringRes descriptionId: Int, 12 titleString: String = "",
13 @DrawableRes val iconId: Int, 13 @StringRes descriptionId: Int = 0,
14 descriptionString: String = "",
15 @DrawableRes val iconId: Int = 0,
14 val menuKey: Settings.MenuTag 16 val menuKey: Settings.MenuTag
15) : SettingsItem(emptySetting, titleId, descriptionId) { 17) : SettingsItem(emptySetting, titleId, titleString, descriptionId, descriptionString) {
16 override val type = TYPE_SUBMENU 18 override val type = TYPE_SUBMENU
17} 19}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SwitchSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SwitchSetting.kt
index 44d47dd69..4984bf52e 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SwitchSetting.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SwitchSetting.kt
@@ -3,15 +3,18 @@
3 3
4package org.yuzu.yuzu_emu.features.settings.model.view 4package org.yuzu.yuzu_emu.features.settings.model.view
5 5
6import androidx.annotation.StringRes
6import org.yuzu.yuzu_emu.features.settings.model.AbstractBooleanSetting 7import org.yuzu.yuzu_emu.features.settings.model.AbstractBooleanSetting
7import org.yuzu.yuzu_emu.features.settings.model.AbstractIntSetting 8import org.yuzu.yuzu_emu.features.settings.model.AbstractIntSetting
8import org.yuzu.yuzu_emu.features.settings.model.AbstractSetting 9import org.yuzu.yuzu_emu.features.settings.model.AbstractSetting
9 10
10class SwitchSetting( 11class SwitchSetting(
11 setting: AbstractSetting, 12 setting: AbstractSetting,
12 titleId: Int, 13 @StringRes titleId: Int = 0,
13 descriptionId: Int 14 titleString: String = "",
14) : SettingsItem(setting, titleId, descriptionId) { 15 @StringRes descriptionId: Int = 0,
16 descriptionString: String = ""
17) : SettingsItem(setting, titleId, titleString, descriptionId, descriptionString) {
15 override val type = TYPE_SWITCH 18 override val type = TYPE_SWITCH
16 19
17 fun getIsChecked(needsGlobal: Boolean = false): Boolean { 20 fun getIsChecked(needsGlobal: Boolean = false): Boolean {
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/InputDialogFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/InputDialogFragment.kt
new file mode 100644
index 000000000..16a1d0504
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/InputDialogFragment.kt
@@ -0,0 +1,300 @@
1// SPDX-FileCopyrightText: 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4package org.yuzu.yuzu_emu.features.settings.ui
5
6import android.app.Dialog
7import android.graphics.drawable.Animatable2
8import android.graphics.drawable.AnimatedVectorDrawable
9import android.graphics.drawable.Drawable
10import android.os.Bundle
11import android.view.InputDevice
12import android.view.KeyEvent
13import android.view.LayoutInflater
14import android.view.MotionEvent
15import android.view.View
16import android.view.ViewGroup
17import androidx.fragment.app.DialogFragment
18import androidx.fragment.app.activityViewModels
19import com.google.android.material.dialog.MaterialAlertDialogBuilder
20import org.yuzu.yuzu_emu.R
21import org.yuzu.yuzu_emu.databinding.DialogMappingBinding
22import org.yuzu.yuzu_emu.features.input.NativeInput
23import org.yuzu.yuzu_emu.features.input.model.NativeAnalog
24import org.yuzu.yuzu_emu.features.input.model.NativeButton
25import org.yuzu.yuzu_emu.features.settings.model.view.AnalogInputSetting
26import org.yuzu.yuzu_emu.features.settings.model.view.ButtonInputSetting
27import org.yuzu.yuzu_emu.features.settings.model.view.InputSetting
28import org.yuzu.yuzu_emu.features.settings.model.view.ModifierInputSetting
29import org.yuzu.yuzu_emu.utils.InputHandler
30import org.yuzu.yuzu_emu.utils.ParamPackage
31
32class InputDialogFragment : DialogFragment() {
33 private var inputAccepted = false
34
35 private var position: Int = 0
36
37 private lateinit var inputSetting: InputSetting
38
39 private lateinit var binding: DialogMappingBinding
40
41 private val settingsViewModel: SettingsViewModel by activityViewModels()
42
43 override fun onCreate(savedInstanceState: Bundle?) {
44 super.onCreate(savedInstanceState)
45 if (settingsViewModel.clickedItem == null) dismiss()
46
47 position = requireArguments().getInt(POSITION)
48
49 InputHandler.updateControllerData()
50 }
51
52 override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
53 inputSetting = settingsViewModel.clickedItem as InputSetting
54 binding = DialogMappingBinding.inflate(layoutInflater)
55
56 val builder = MaterialAlertDialogBuilder(requireContext())
57 .setPositiveButton(android.R.string.cancel) { _, _ ->
58 NativeInput.stopMapping()
59 dismiss()
60 }
61 .setView(binding.root)
62
63 val playButtonMapAnimation = { twoDirections: Boolean ->
64 val stickAnimation: AnimatedVectorDrawable
65 val buttonAnimation: AnimatedVectorDrawable
66 binding.imageStickAnimation.apply {
67 val anim = if (twoDirections) {
68 R.drawable.stick_two_direction_anim
69 } else {
70 R.drawable.stick_one_direction_anim
71 }
72 setBackgroundResource(anim)
73 stickAnimation = background as AnimatedVectorDrawable
74 }
75 binding.imageButtonAnimation.apply {
76 setBackgroundResource(R.drawable.button_anim)
77 buttonAnimation = background as AnimatedVectorDrawable
78 }
79 stickAnimation.registerAnimationCallback(object : Animatable2.AnimationCallback() {
80 override fun onAnimationEnd(drawable: Drawable?) {
81 buttonAnimation.start()
82 }
83 })
84 buttonAnimation.registerAnimationCallback(object : Animatable2.AnimationCallback() {
85 override fun onAnimationEnd(drawable: Drawable?) {
86 stickAnimation.start()
87 }
88 })
89 stickAnimation.start()
90 }
91
92 when (val setting = inputSetting) {
93 is AnalogInputSetting -> {
94 when (setting.nativeAnalog) {
95 NativeAnalog.LStick -> builder.setTitle(
96 getString(R.string.map_control, getString(R.string.left_stick))
97 )
98
99 NativeAnalog.RStick -> builder.setTitle(
100 getString(R.string.map_control, getString(R.string.right_stick))
101 )
102 }
103
104 builder.setMessage(R.string.stick_map_description)
105
106 playButtonMapAnimation.invoke(true)
107 }
108
109 is ModifierInputSetting -> {
110 builder.setTitle(getString(R.string.map_control, setting.title))
111 .setMessage(R.string.button_map_description)
112 playButtonMapAnimation.invoke(false)
113 }
114
115 is ButtonInputSetting -> {
116 if (setting.nativeButton == NativeButton.DUp ||
117 setting.nativeButton == NativeButton.DDown ||
118 setting.nativeButton == NativeButton.DLeft ||
119 setting.nativeButton == NativeButton.DRight
120 ) {
121 builder.setTitle(getString(R.string.map_dpad_direction, setting.title))
122 } else {
123 builder.setTitle(getString(R.string.map_control, setting.title))
124 }
125 builder.setMessage(R.string.button_map_description)
126 playButtonMapAnimation.invoke(false)
127 }
128 }
129
130 return builder.create()
131 }
132
133 override fun onCreateView(
134 inflater: LayoutInflater,
135 container: ViewGroup?,
136 savedInstanceState: Bundle?
137 ): View {
138 return binding.root
139 }
140
141 override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
142 super.onViewCreated(view, savedInstanceState)
143 view.requestFocus()
144 view.setOnFocusChangeListener { v, hasFocus -> if (!hasFocus) v.requestFocus() }
145 dialog?.setOnKeyListener { _, _, keyEvent -> onKeyEvent(keyEvent) }
146 binding.root.setOnGenericMotionListener { _, motionEvent -> onMotionEvent(motionEvent) }
147 NativeInput.beginMapping(inputSetting.inputType.int)
148 }
149
150 private fun onKeyEvent(event: KeyEvent): Boolean {
151 if (event.source and InputDevice.SOURCE_JOYSTICK != InputDevice.SOURCE_JOYSTICK &&
152 event.source and InputDevice.SOURCE_GAMEPAD != InputDevice.SOURCE_GAMEPAD
153 ) {
154 return false
155 }
156
157 val action = when (event.action) {
158 KeyEvent.ACTION_DOWN -> NativeInput.ButtonState.PRESSED
159 KeyEvent.ACTION_UP -> NativeInput.ButtonState.RELEASED
160 else -> return false
161 }
162 val controllerData =
163 InputHandler.androidControllers[event.device.controllerNumber] ?: return false
164 NativeInput.onGamePadButtonEvent(
165 controllerData.getGUID(),
166 controllerData.getPort(),
167 event.keyCode,
168 action
169 )
170 onInputReceived(event.device)
171 return true
172 }
173
174 private fun onMotionEvent(event: MotionEvent): Boolean {
175 if (event.source and InputDevice.SOURCE_JOYSTICK != InputDevice.SOURCE_JOYSTICK &&
176 event.source and InputDevice.SOURCE_GAMEPAD != InputDevice.SOURCE_GAMEPAD
177 ) {
178 return false
179 }
180
181 // Temp workaround for DPads that give both axis and button input. The input system can't
182 // take in a specific axis direction for a binding so you lose half of the directions for a DPad.
183
184 val controllerData =
185 InputHandler.androidControllers[event.device.controllerNumber] ?: return false
186 event.device.motionRanges.forEach {
187 NativeInput.onGamePadAxisEvent(
188 controllerData.getGUID(),
189 controllerData.getPort(),
190 it.axis,
191 event.getAxisValue(it.axis)
192 )
193 onInputReceived(event.device)
194 }
195 return true
196 }
197
198 private fun onInputReceived(device: InputDevice) {
199 val params = ParamPackage(NativeInput.getNextInput())
200 if (params.has("engine") && isInputAcceptable(params) && !inputAccepted) {
201 inputAccepted = true
202 setResult(params, device)
203 }
204 }
205
206 private fun setResult(params: ParamPackage, device: InputDevice) {
207 NativeInput.stopMapping()
208 params.set("display", "${device.name} ${params.get("port", 0)}")
209 when (val item = settingsViewModel.clickedItem as InputSetting) {
210 is ModifierInputSetting,
211 is ButtonInputSetting -> {
212 // Invert DPad up and left bindings by default
213 val tempSetting = inputSetting as? ButtonInputSetting
214 if (tempSetting != null) {
215 if (tempSetting.nativeButton == NativeButton.DUp ||
216 tempSetting.nativeButton == NativeButton.DLeft &&
217 params.has("axis")
218 ) {
219 params.set("invert", "-")
220 }
221 }
222
223 item.setSelectedValue(params)
224 settingsViewModel.setAdapterItemChanged(position)
225 }
226
227 is AnalogInputSetting -> {
228 var analogParam = NativeInput.getStickParam(item.playerIndex, item.nativeAnalog)
229 analogParam = adjustAnalogParam(params, analogParam, item.analogDirection.param)
230
231 // Invert Y-Axis by default
232 analogParam.set("invert_y", "-")
233
234 item.setSelectedValue(analogParam)
235 settingsViewModel.setReloadListAndNotifyDataset(true)
236 }
237 }
238 dismiss()
239 }
240
241 private fun adjustAnalogParam(
242 inputParam: ParamPackage,
243 analogParam: ParamPackage,
244 buttonName: String
245 ): ParamPackage {
246 // The poller returned a complete axis, so set all the buttons
247 if (inputParam.has("axis_x") && inputParam.has("axis_y")) {
248 return inputParam
249 }
250
251 // Check if the current configuration has either no engine or an axis binding.
252 // Clears out the old binding and adds one with analog_from_button.
253 if (!analogParam.has("engine") || analogParam.has("axis_x") || analogParam.has("axis_y")) {
254 analogParam.clear()
255 analogParam.set("engine", "analog_from_button")
256 }
257 analogParam.set(buttonName, inputParam.serialize())
258 return analogParam
259 }
260
261 private fun isInputAcceptable(params: ParamPackage): Boolean {
262 if (InputHandler.registeredControllers.size == 1) {
263 return true
264 }
265
266 if (params.has("motion")) {
267 return true
268 }
269
270 val currentDevice = settingsViewModel.getCurrentDeviceParams(params)
271 if (currentDevice.get("engine", "any") == "any") {
272 return true
273 }
274
275 val guidMatch = params.get("guid", "") == currentDevice.get("guid", "") ||
276 params.get("guid", "") == currentDevice.get("guid2", "")
277 return params.get("engine", "") == currentDevice.get("engine", "") &&
278 guidMatch &&
279 params.get("port", 0) == currentDevice.get("port", 0)
280 }
281
282 companion object {
283 const val TAG = "InputDialogFragment"
284
285 const val POSITION = "Position"
286
287 fun newInstance(
288 inputMappingViewModel: SettingsViewModel,
289 setting: InputSetting,
290 position: Int
291 ): InputDialogFragment {
292 inputMappingViewModel.clickedItem = setting
293 val args = Bundle()
294 args.putInt(POSITION, position)
295 val fragment = InputDialogFragment()
296 fragment.arguments = args
297 return fragment
298 }
299 }
300}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/InputProfileAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/InputProfileAdapter.kt
new file mode 100644
index 000000000..5656e9d8d
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/InputProfileAdapter.kt
@@ -0,0 +1,68 @@
1// SPDX-FileCopyrightText: 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4package org.yuzu.yuzu_emu.features.settings.ui
5
6import android.view.LayoutInflater
7import android.view.View
8import android.view.ViewGroup
9import org.yuzu.yuzu_emu.YuzuApplication
10import org.yuzu.yuzu_emu.adapters.AbstractListAdapter
11import org.yuzu.yuzu_emu.databinding.ListItemInputProfileBinding
12import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder
13import org.yuzu.yuzu_emu.R
14
15class InputProfileAdapter(options: List<ProfileItem>) :
16 AbstractListAdapter<ProfileItem, AbstractViewHolder<ProfileItem>>(options) {
17 override fun onCreateViewHolder(
18 parent: ViewGroup,
19 viewType: Int
20 ): AbstractViewHolder<ProfileItem> {
21 ListItemInputProfileBinding.inflate(LayoutInflater.from(parent.context), parent, false)
22 .also { return InputProfileViewHolder(it) }
23 }
24
25 inner class InputProfileViewHolder(val binding: ListItemInputProfileBinding) :
26 AbstractViewHolder<ProfileItem>(binding) {
27 override fun bind(model: ProfileItem) {
28 when (model) {
29 is ExistingProfileItem -> {
30 binding.title.text = model.name
31 binding.buttonNew.visibility = View.GONE
32 binding.buttonDelete.visibility = View.VISIBLE
33 binding.buttonDelete.setOnClickListener { model.deleteProfile.invoke() }
34 binding.buttonSave.visibility = View.VISIBLE
35 binding.buttonSave.setOnClickListener { model.saveProfile.invoke() }
36 binding.buttonLoad.visibility = View.VISIBLE
37 binding.buttonLoad.setOnClickListener { model.loadProfile.invoke() }
38 }
39
40 is NewProfileItem -> {
41 binding.title.text = model.name
42 binding.buttonNew.visibility = View.VISIBLE
43 binding.buttonNew.setOnClickListener { model.createNewProfile.invoke() }
44 binding.buttonSave.visibility = View.GONE
45 binding.buttonDelete.visibility = View.GONE
46 binding.buttonLoad.visibility = View.GONE
47 }
48 }
49 }
50 }
51}
52
53sealed interface ProfileItem {
54 val name: String
55}
56
57data class NewProfileItem(
58 val createNewProfile: () -> Unit
59) : ProfileItem {
60 override val name: String = YuzuApplication.appContext.getString(R.string.create_new_profile)
61}
62
63data class ExistingProfileItem(
64 override val name: String,
65 val deleteProfile: () -> Unit,
66 val saveProfile: () -> Unit,
67 val loadProfile: () -> Unit
68) : ProfileItem
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/InputProfileDialogFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/InputProfileDialogFragment.kt
new file mode 100644
index 000000000..1bae593ae
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/InputProfileDialogFragment.kt
@@ -0,0 +1,148 @@
1// SPDX-FileCopyrightText: 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4package org.yuzu.yuzu_emu.features.settings.ui
5
6import android.app.Dialog
7import android.os.Bundle
8import android.view.LayoutInflater
9import android.view.View
10import android.view.ViewGroup
11import android.widget.Toast
12import androidx.fragment.app.DialogFragment
13import androidx.fragment.app.activityViewModels
14import androidx.recyclerview.widget.LinearLayoutManager
15import com.google.android.material.dialog.MaterialAlertDialogBuilder
16import org.yuzu.yuzu_emu.R
17import org.yuzu.yuzu_emu.databinding.DialogInputProfilesBinding
18import org.yuzu.yuzu_emu.features.settings.model.view.InputProfileSetting
19import org.yuzu.yuzu_emu.fragments.MessageDialogFragment
20import org.yuzu.yuzu_emu.utils.collect
21
22class InputProfileDialogFragment : DialogFragment() {
23 private var position = 0
24
25 private val settingsViewModel: SettingsViewModel by activityViewModels()
26
27 private lateinit var binding: DialogInputProfilesBinding
28
29 private lateinit var setting: InputProfileSetting
30
31 override fun onCreate(savedInstanceState: Bundle?) {
32 super.onCreate(savedInstanceState)
33 position = requireArguments().getInt(POSITION)
34 }
35
36 override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
37 binding = DialogInputProfilesBinding.inflate(layoutInflater)
38
39 setting = settingsViewModel.clickedItem as InputProfileSetting
40 val options = mutableListOf<ProfileItem>().apply {
41 add(
42 NewProfileItem(
43 createNewProfile = {
44 NewInputProfileDialogFragment.newInstance(
45 settingsViewModel,
46 setting,
47 position
48 ).show(parentFragmentManager, NewInputProfileDialogFragment.TAG)
49 dismiss()
50 }
51 )
52 )
53
54 val onActionDismiss = {
55 settingsViewModel.setReloadListAndNotifyDataset(true)
56 dismiss()
57 }
58 setting.getProfileNames().forEach {
59 add(
60 ExistingProfileItem(
61 it,
62 deleteProfile = {
63 settingsViewModel.setShouldShowDeleteProfileDialog(it)
64 },
65 saveProfile = {
66 if (!setting.saveProfile(it)) {
67 Toast.makeText(
68 requireContext(),
69 R.string.failed_to_save_profile,
70 Toast.LENGTH_SHORT
71 ).show()
72 }
73 onActionDismiss.invoke()
74 },
75 loadProfile = {
76 if (!setting.loadProfile(it)) {
77 Toast.makeText(
78 requireContext(),
79 R.string.failed_to_load_profile,
80 Toast.LENGTH_SHORT
81 ).show()
82 }
83 onActionDismiss.invoke()
84 }
85 )
86 )
87 }
88 }
89 binding.listProfiles.apply {
90 layoutManager = LinearLayoutManager(requireContext())
91 adapter = InputProfileAdapter(options)
92 }
93
94 return MaterialAlertDialogBuilder(requireContext())
95 .setView(binding.root)
96 .create()
97 }
98
99 override fun onCreateView(
100 inflater: LayoutInflater,
101 container: ViewGroup?,
102 savedInstanceState: Bundle?
103 ): View {
104 return binding.root
105 }
106
107 override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
108 super.onViewCreated(view, savedInstanceState)
109
110 settingsViewModel.shouldShowDeleteProfileDialog.collect(viewLifecycleOwner) {
111 if (it.isNotEmpty()) {
112 MessageDialogFragment.newInstance(
113 activity = requireActivity(),
114 titleId = R.string.delete_input_profile,
115 descriptionId = R.string.delete_input_profile_description,
116 positiveAction = {
117 setting.deleteProfile(it)
118 settingsViewModel.setReloadListAndNotifyDataset(true)
119 },
120 negativeAction = {},
121 negativeButtonTitleId = android.R.string.cancel
122 ).show(parentFragmentManager, MessageDialogFragment.TAG)
123 settingsViewModel.setShouldShowDeleteProfileDialog("")
124 dismiss()
125 }
126 }
127 }
128
129 companion object {
130 const val TAG = "InputProfileDialogFragment"
131
132 const val POSITION = "Position"
133
134 fun newInstance(
135 settingsViewModel: SettingsViewModel,
136 profileSetting: InputProfileSetting,
137 position: Int
138 ): InputProfileDialogFragment {
139 settingsViewModel.clickedItem = profileSetting
140
141 val args = Bundle()
142 args.putInt(POSITION, position)
143 val fragment = InputProfileDialogFragment()
144 fragment.arguments = args
145 return fragment
146 }
147 }
148}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/NewInputProfileDialogFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/NewInputProfileDialogFragment.kt
new file mode 100644
index 000000000..6e52bea80
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/NewInputProfileDialogFragment.kt
@@ -0,0 +1,79 @@
1// SPDX-FileCopyrightText: 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4package org.yuzu.yuzu_emu.features.settings.ui
5
6import android.app.Dialog
7import android.os.Bundle
8import android.widget.Toast
9import androidx.fragment.app.DialogFragment
10import androidx.fragment.app.activityViewModels
11import com.google.android.material.dialog.MaterialAlertDialogBuilder
12import org.yuzu.yuzu_emu.databinding.DialogEditTextBinding
13import org.yuzu.yuzu_emu.features.settings.model.view.InputProfileSetting
14import org.yuzu.yuzu_emu.R
15
16class NewInputProfileDialogFragment : DialogFragment() {
17 private var position = 0
18
19 private val settingsViewModel: SettingsViewModel by activityViewModels()
20
21 private lateinit var binding: DialogEditTextBinding
22
23 override fun onCreate(savedInstanceState: Bundle?) {
24 super.onCreate(savedInstanceState)
25 position = requireArguments().getInt(POSITION)
26 }
27
28 override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
29 binding = DialogEditTextBinding.inflate(layoutInflater)
30
31 val setting = settingsViewModel.clickedItem as InputProfileSetting
32 return MaterialAlertDialogBuilder(requireContext())
33 .setTitle(R.string.enter_profile_name)
34 .setPositiveButton(android.R.string.ok) { _, _ ->
35 val profileName = binding.editText.text.toString()
36 if (!setting.isProfileNameValid(profileName)) {
37 Toast.makeText(
38 requireContext(),
39 R.string.invalid_profile_name,
40 Toast.LENGTH_SHORT
41 ).show()
42 return@setPositiveButton
43 }
44
45 if (!setting.createProfile(profileName)) {
46 Toast.makeText(
47 requireContext(),
48 R.string.profile_name_already_exists,
49 Toast.LENGTH_SHORT
50 ).show()
51 } else {
52 settingsViewModel.setAdapterItemChanged(position)
53 }
54 }
55 .setNegativeButton(android.R.string.cancel, null)
56 .setView(binding.root)
57 .show()
58 }
59
60 companion object {
61 const val TAG = "NewInputProfileDialogFragment"
62
63 const val POSITION = "Position"
64
65 fun newInstance(
66 settingsViewModel: SettingsViewModel,
67 profileSetting: InputProfileSetting,
68 position: Int
69 ): NewInputProfileDialogFragment {
70 settingsViewModel.clickedItem = profileSetting
71
72 val args = Bundle()
73 args.putInt(POSITION, position)
74 val fragment = NewInputProfileDialogFragment()
75 fragment.arguments = args
76 return fragment
77 }
78 }
79}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivity.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivity.kt
index 6f072241a..455b3b5ff 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivity.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivity.kt
@@ -13,21 +13,16 @@ import androidx.appcompat.app.AppCompatActivity
13import androidx.core.view.ViewCompat 13import androidx.core.view.ViewCompat
14import androidx.core.view.WindowCompat 14import androidx.core.view.WindowCompat
15import androidx.core.view.WindowInsetsCompat 15import androidx.core.view.WindowInsetsCompat
16import androidx.lifecycle.Lifecycle
17import androidx.lifecycle.lifecycleScope
18import androidx.lifecycle.repeatOnLifecycle
19import androidx.navigation.fragment.NavHostFragment 16import androidx.navigation.fragment.NavHostFragment
20import androidx.navigation.navArgs 17import androidx.navigation.navArgs
21import com.google.android.material.color.MaterialColors 18import com.google.android.material.color.MaterialColors
22import kotlinx.coroutines.flow.collectLatest
23import kotlinx.coroutines.launch
24import org.yuzu.yuzu_emu.NativeLibrary 19import org.yuzu.yuzu_emu.NativeLibrary
25import java.io.IOException 20import java.io.IOException
26import org.yuzu.yuzu_emu.R 21import org.yuzu.yuzu_emu.R
27import org.yuzu.yuzu_emu.databinding.ActivitySettingsBinding 22import org.yuzu.yuzu_emu.databinding.ActivitySettingsBinding
23import org.yuzu.yuzu_emu.features.input.NativeInput
28import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile 24import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile
29import org.yuzu.yuzu_emu.fragments.ResetSettingsDialogFragment 25import org.yuzu.yuzu_emu.fragments.ResetSettingsDialogFragment
30import org.yuzu.yuzu_emu.model.SettingsViewModel
31import org.yuzu.yuzu_emu.utils.* 26import org.yuzu.yuzu_emu.utils.*
32 27
33class SettingsActivity : AppCompatActivity() { 28class SettingsActivity : AppCompatActivity() {
@@ -70,39 +65,23 @@ class SettingsActivity : AppCompatActivity() {
70 ) 65 )
71 } 66 }
72 67
73 lifecycleScope.apply { 68 settingsViewModel.shouldRecreate.collect(
74 launch { 69 this,
75 repeatOnLifecycle(Lifecycle.State.CREATED) { 70 resetState = { settingsViewModel.setShouldRecreate(false) }
76 settingsViewModel.shouldRecreate.collectLatest { 71 ) { if (it) recreate() }
77 if (it) { 72 settingsViewModel.shouldNavigateBack.collect(
78 settingsViewModel.setShouldRecreate(false) 73 this,
79 recreate() 74 resetState = { settingsViewModel.setShouldNavigateBack(false) }
80 } 75 ) { if (it) navigateBack() }
81 } 76 settingsViewModel.shouldShowResetSettingsDialog.collect(
82 } 77 this,
83 } 78 resetState = { settingsViewModel.setShouldShowResetSettingsDialog(false) }
84 launch { 79 ) {
85 repeatOnLifecycle(Lifecycle.State.CREATED) { 80 if (it) {
86 settingsViewModel.shouldNavigateBack.collectLatest { 81 ResetSettingsDialogFragment().show(
87 if (it) { 82 supportFragmentManager,
88 settingsViewModel.setShouldNavigateBack(false) 83 ResetSettingsDialogFragment.TAG
89 navigateBack() 84 )
90 }
91 }
92 }
93 }
94 launch {
95 repeatOnLifecycle(Lifecycle.State.CREATED) {
96 settingsViewModel.shouldShowResetSettingsDialog.collectLatest {
97 if (it) {
98 settingsViewModel.setShouldShowResetSettingsDialog(false)
99 ResetSettingsDialogFragment().show(
100 supportFragmentManager,
101 ResetSettingsDialogFragment.TAG
102 )
103 }
104 }
105 }
106 } 85 }
107 } 86 }
108 87
@@ -137,6 +116,7 @@ class SettingsActivity : AppCompatActivity() {
137 super.onStop() 116 super.onStop()
138 Log.info("[SettingsActivity] Settings activity stopping. Saving settings to INI...") 117 Log.info("[SettingsActivity] Settings activity stopping. Saving settings to INI...")
139 if (isFinishing) { 118 if (isFinishing) {
119 NativeInput.reloadInputDevices()
140 NativeLibrary.applySettings() 120 NativeLibrary.applySettings()
141 if (args.game == null) { 121 if (args.game == null) {
142 NativeConfig.saveGlobalConfig() 122 NativeConfig.saveGlobalConfig()
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsAdapter.kt
index be9b3031b..45c8faa10 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsAdapter.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsAdapter.kt
@@ -8,12 +8,11 @@ import android.icu.util.Calendar
8import android.icu.util.TimeZone 8import android.icu.util.TimeZone
9import android.text.format.DateFormat 9import android.text.format.DateFormat
10import android.view.LayoutInflater 10import android.view.LayoutInflater
11import android.view.View
11import android.view.ViewGroup 12import android.view.ViewGroup
13import android.widget.PopupMenu
12import androidx.fragment.app.Fragment 14import androidx.fragment.app.Fragment
13import androidx.lifecycle.Lifecycle
14import androidx.lifecycle.ViewModelProvider 15import androidx.lifecycle.ViewModelProvider
15import androidx.lifecycle.lifecycleScope
16import androidx.lifecycle.repeatOnLifecycle
17import androidx.navigation.findNavController 16import androidx.navigation.findNavController
18import androidx.recyclerview.widget.AsyncDifferConfig 17import androidx.recyclerview.widget.AsyncDifferConfig
19import androidx.recyclerview.widget.DiffUtil 18import androidx.recyclerview.widget.DiffUtil
@@ -21,16 +20,18 @@ import androidx.recyclerview.widget.ListAdapter
21import com.google.android.material.datepicker.MaterialDatePicker 20import com.google.android.material.datepicker.MaterialDatePicker
22import com.google.android.material.timepicker.MaterialTimePicker 21import com.google.android.material.timepicker.MaterialTimePicker
23import com.google.android.material.timepicker.TimeFormat 22import com.google.android.material.timepicker.TimeFormat
24import kotlinx.coroutines.launch
25import org.yuzu.yuzu_emu.R 23import org.yuzu.yuzu_emu.R
26import org.yuzu.yuzu_emu.SettingsNavigationDirections 24import org.yuzu.yuzu_emu.SettingsNavigationDirections
27import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding 25import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding
26import org.yuzu.yuzu_emu.databinding.ListItemSettingInputBinding
28import org.yuzu.yuzu_emu.databinding.ListItemSettingSwitchBinding 27import org.yuzu.yuzu_emu.databinding.ListItemSettingSwitchBinding
29import org.yuzu.yuzu_emu.databinding.ListItemSettingsHeaderBinding 28import org.yuzu.yuzu_emu.databinding.ListItemSettingsHeaderBinding
29import org.yuzu.yuzu_emu.features.input.NativeInput
30import org.yuzu.yuzu_emu.features.input.model.AnalogDirection
31import org.yuzu.yuzu_emu.features.settings.model.AbstractIntSetting
30import org.yuzu.yuzu_emu.features.settings.model.view.* 32import org.yuzu.yuzu_emu.features.settings.model.view.*
31import org.yuzu.yuzu_emu.features.settings.ui.viewholder.* 33import org.yuzu.yuzu_emu.features.settings.ui.viewholder.*
32import org.yuzu.yuzu_emu.fragments.SettingsDialogFragment 34import org.yuzu.yuzu_emu.utils.ParamPackage
33import org.yuzu.yuzu_emu.model.SettingsViewModel
34 35
35class SettingsAdapter( 36class SettingsAdapter(
36 private val fragment: Fragment, 37 private val fragment: Fragment,
@@ -41,19 +42,6 @@ class SettingsAdapter(
41 private val settingsViewModel: SettingsViewModel 42 private val settingsViewModel: SettingsViewModel
42 get() = ViewModelProvider(fragment.requireActivity())[SettingsViewModel::class.java] 43 get() = ViewModelProvider(fragment.requireActivity())[SettingsViewModel::class.java]
43 44
44 init {
45 fragment.viewLifecycleOwner.lifecycleScope.launch {
46 fragment.repeatOnLifecycle(Lifecycle.State.STARTED) {
47 settingsViewModel.adapterItemChanged.collect {
48 if (it != -1) {
49 notifyItemChanged(it)
50 settingsViewModel.setAdapterItemChanged(-1)
51 }
52 }
53 }
54 }
55 }
56
57 override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SettingViewHolder { 45 override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SettingViewHolder {
58 val inflater = LayoutInflater.from(parent.context) 46 val inflater = LayoutInflater.from(parent.context)
59 return when (viewType) { 47 return when (viewType) {
@@ -85,8 +73,19 @@ class SettingsAdapter(
85 RunnableViewHolder(ListItemSettingBinding.inflate(inflater), this) 73 RunnableViewHolder(ListItemSettingBinding.inflate(inflater), this)
86 } 74 }
87 75
76 SettingsItem.TYPE_INPUT -> {
77 InputViewHolder(ListItemSettingInputBinding.inflate(inflater), this)
78 }
79
80 SettingsItem.TYPE_INT_SINGLE_CHOICE -> {
81 SingleChoiceViewHolder(ListItemSettingBinding.inflate(inflater), this)
82 }
83
84 SettingsItem.TYPE_INPUT_PROFILE -> {
85 InputProfileViewHolder(ListItemSettingBinding.inflate(inflater), this)
86 }
87
88 else -> { 88 else -> {
89 // TODO: Create an error view since we can't return null now
90 HeaderViewHolder(ListItemSettingsHeaderBinding.inflate(inflater), this) 89 HeaderViewHolder(ListItemSettingsHeaderBinding.inflate(inflater), this)
91 } 90 }
92 } 91 }
@@ -126,6 +125,15 @@ class SettingsAdapter(
126 ).show(fragment.childFragmentManager, SettingsDialogFragment.TAG) 125 ).show(fragment.childFragmentManager, SettingsDialogFragment.TAG)
127 } 126 }
128 127
128 fun onIntSingleChoiceClick(item: IntSingleChoiceSetting, position: Int) {
129 SettingsDialogFragment.newInstance(
130 settingsViewModel,
131 item,
132 SettingsItem.TYPE_INT_SINGLE_CHOICE,
133 position
134 ).show(fragment.childFragmentManager, SettingsDialogFragment.TAG)
135 }
136
129 fun onDateTimeClick(item: DateTimeSetting, position: Int) { 137 fun onDateTimeClick(item: DateTimeSetting, position: Int) {
130 val storedTime = item.getValue() * 1000 138 val storedTime = item.getValue() * 1000
131 139
@@ -185,6 +193,205 @@ class SettingsAdapter(
185 fragment.view?.findNavController()?.navigate(action) 193 fragment.view?.findNavController()?.navigate(action)
186 } 194 }
187 195
196 fun onInputProfileClick(item: InputProfileSetting, position: Int) {
197 InputProfileDialogFragment.newInstance(
198 settingsViewModel,
199 item,
200 position
201 ).show(fragment.childFragmentManager, InputProfileDialogFragment.TAG)
202 }
203
204 fun onInputClick(item: InputSetting, position: Int) {
205 InputDialogFragment.newInstance(
206 settingsViewModel,
207 item,
208 position
209 ).show(fragment.childFragmentManager, InputDialogFragment.TAG)
210 }
211
212 fun onInputOptionsClick(anchor: View, item: InputSetting, position: Int) {
213 val popup = PopupMenu(context, anchor)
214 popup.menuInflater.inflate(R.menu.menu_input_options, popup.menu)
215
216 popup.menu.apply {
217 val invertAxis = findItem(R.id.invert_axis)
218 val invertButton = findItem(R.id.invert_button)
219 val toggleButton = findItem(R.id.toggle_button)
220 val turboButton = findItem(R.id.turbo_button)
221 val setThreshold = findItem(R.id.set_threshold)
222 val toggleAxis = findItem(R.id.toggle_axis)
223 when (item) {
224 is AnalogInputSetting -> {
225 val params = NativeInput.getStickParam(item.playerIndex, item.nativeAnalog)
226
227 invertAxis.isVisible = true
228 invertAxis.isCheckable = true
229 invertAxis.isChecked = when (item.analogDirection) {
230 AnalogDirection.Left, AnalogDirection.Right -> {
231 params.get("invert_x", "+") == "-"
232 }
233
234 AnalogDirection.Up, AnalogDirection.Down -> {
235 params.get("invert_y", "+") == "-"
236 }
237 }
238 invertAxis.setOnMenuItemClickListener {
239 if (item.analogDirection == AnalogDirection.Left ||
240 item.analogDirection == AnalogDirection.Right
241 ) {
242 val invertValue = params.get("invert_x", "+") == "-"
243 val invertString = if (invertValue) "+" else "-"
244 params.set("invert_x", invertString)
245 } else if (
246 item.analogDirection == AnalogDirection.Up ||
247 item.analogDirection == AnalogDirection.Down
248 ) {
249 val invertValue = params.get("invert_y", "+") == "-"
250 val invertString = if (invertValue) "+" else "-"
251 params.set("invert_y", invertString)
252 }
253 true
254 }
255
256 popup.setOnDismissListener {
257 NativeInput.setStickParam(item.playerIndex, item.nativeAnalog, params)
258 settingsViewModel.setDatasetChanged(true)
259 }
260 }
261
262 is ButtonInputSetting -> {
263 val params = NativeInput.getButtonParam(item.playerIndex, item.nativeButton)
264 if (params.has("code") || params.has("button") || params.has("hat")) {
265 val buttonInvert = params.get("inverted", false)
266 invertButton.isVisible = true
267 invertButton.isCheckable = true
268 invertButton.isChecked = buttonInvert
269 invertButton.setOnMenuItemClickListener {
270 params.set("inverted", !buttonInvert)
271 true
272 }
273
274 val toggle = params.get("toggle", false)
275 toggleButton.isVisible = true
276 toggleButton.isCheckable = true
277 toggleButton.isChecked = toggle
278 toggleButton.setOnMenuItemClickListener {
279 params.set("toggle", !toggle)
280 true
281 }
282
283 val turbo = params.get("turbo", false)
284 turboButton.isVisible = true
285 turboButton.isCheckable = true
286 turboButton.isChecked = turbo
287 turboButton.setOnMenuItemClickListener {
288 params.set("turbo", !turbo)
289 true
290 }
291 } else if (params.has("axis")) {
292 val axisInvert = params.get("invert", "+") == "-"
293 invertAxis.isVisible = true
294 invertAxis.isCheckable = true
295 invertAxis.isChecked = axisInvert
296 invertAxis.setOnMenuItemClickListener {
297 params.set("invert", if (!axisInvert) "-" else "+")
298 true
299 }
300
301 val buttonInvert = params.get("inverted", false)
302 invertButton.isVisible = true
303 invertButton.isCheckable = true
304 invertButton.isChecked = buttonInvert
305 invertButton.setOnMenuItemClickListener {
306 params.set("inverted", !buttonInvert)
307 true
308 }
309
310 setThreshold.isVisible = true
311 val thresholdSetting = object : AbstractIntSetting {
312 override val key = ""
313
314 override fun getInt(needsGlobal: Boolean): Int =
315 (params.get("threshold", 0.5f) * 100).toInt()
316
317 override fun setInt(value: Int) {
318 params.set("threshold", value.toFloat() / 100)
319 NativeInput.setButtonParam(
320 item.playerIndex,
321 item.nativeButton,
322 params
323 )
324 }
325
326 override val defaultValue = 50
327
328 override fun getValueAsString(needsGlobal: Boolean): String =
329 getInt(needsGlobal).toString()
330
331 override fun reset() = setInt(defaultValue)
332 }
333 setThreshold.setOnMenuItemClickListener {
334 onSliderClick(
335 SliderSetting(thresholdSetting, R.string.set_threshold),
336 position
337 )
338 true
339 }
340
341 val axisToggle = params.get("toggle", false)
342 toggleAxis.isVisible = true
343 toggleAxis.isCheckable = true
344 toggleAxis.isChecked = axisToggle
345 toggleAxis.setOnMenuItemClickListener {
346 params.set("toggle", !axisToggle)
347 true
348 }
349 }
350
351 popup.setOnDismissListener {
352 NativeInput.setButtonParam(item.playerIndex, item.nativeButton, params)
353 settingsViewModel.setAdapterItemChanged(position)
354 }
355 }
356
357 is ModifierInputSetting -> {
358 val stickParams = NativeInput.getStickParam(item.playerIndex, item.nativeAnalog)
359 val modifierParams = ParamPackage(stickParams.get("modifier", ""))
360
361 val invert = modifierParams.get("inverted", false)
362 invertButton.isVisible = true
363 invertButton.isCheckable = true
364 invertButton.isChecked = invert
365 invertButton.setOnMenuItemClickListener {
366 modifierParams.set("inverted", !invert)
367 stickParams.set("modifier", modifierParams.serialize())
368 true
369 }
370
371 val toggle = modifierParams.get("toggle", false)
372 toggleButton.isVisible = true
373 toggleButton.isCheckable = true
374 toggleButton.isChecked = toggle
375 toggleButton.setOnMenuItemClickListener {
376 modifierParams.set("toggle", !toggle)
377 stickParams.set("modifier", modifierParams.serialize())
378 true
379 }
380
381 popup.setOnDismissListener {
382 NativeInput.setStickParam(
383 item.playerIndex,
384 item.nativeAnalog,
385 stickParams
386 )
387 settingsViewModel.setAdapterItemChanged(position)
388 }
389 }
390 }
391 }
392 popup.show()
393 }
394
188 fun onLongClick(item: SettingsItem, position: Int): Boolean { 395 fun onLongClick(item: SettingsItem, position: Int): Boolean {
189 SettingsDialogFragment.newInstance( 396 SettingsDialogFragment.newInstance(
190 settingsViewModel, 397 settingsViewModel,
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SettingsDialogFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsDialogFragment.kt
index 60e029f34..a81ff6b1a 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SettingsDialogFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsDialogFragment.kt
@@ -1,7 +1,7 @@
1// SPDX-FileCopyrightText: 2023 yuzu Emulator Project 1// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4package org.yuzu.yuzu_emu.fragments 4package org.yuzu.yuzu_emu.features.settings.ui
5 5
6import android.app.Dialog 6import android.app.Dialog
7import android.content.DialogInterface 7import android.content.DialogInterface
@@ -11,19 +11,21 @@ import android.view.View
11import android.view.ViewGroup 11import android.view.ViewGroup
12import androidx.fragment.app.DialogFragment 12import androidx.fragment.app.DialogFragment
13import androidx.fragment.app.activityViewModels 13import androidx.fragment.app.activityViewModels
14import androidx.lifecycle.Lifecycle
15import androidx.lifecycle.lifecycleScope
16import androidx.lifecycle.repeatOnLifecycle
17import com.google.android.material.dialog.MaterialAlertDialogBuilder 14import com.google.android.material.dialog.MaterialAlertDialogBuilder
18import com.google.android.material.slider.Slider 15import com.google.android.material.slider.Slider
19import kotlinx.coroutines.launch
20import org.yuzu.yuzu_emu.R 16import org.yuzu.yuzu_emu.R
21import org.yuzu.yuzu_emu.databinding.DialogSliderBinding 17import org.yuzu.yuzu_emu.databinding.DialogSliderBinding
18import org.yuzu.yuzu_emu.features.input.NativeInput
19import org.yuzu.yuzu_emu.features.input.model.AnalogDirection
20import org.yuzu.yuzu_emu.features.settings.model.view.AnalogInputSetting
21import org.yuzu.yuzu_emu.features.settings.model.view.ButtonInputSetting
22import org.yuzu.yuzu_emu.features.settings.model.view.IntSingleChoiceSetting
22import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem 23import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem
23import org.yuzu.yuzu_emu.features.settings.model.view.SingleChoiceSetting 24import org.yuzu.yuzu_emu.features.settings.model.view.SingleChoiceSetting
24import org.yuzu.yuzu_emu.features.settings.model.view.SliderSetting 25import org.yuzu.yuzu_emu.features.settings.model.view.SliderSetting
25import org.yuzu.yuzu_emu.features.settings.model.view.StringSingleChoiceSetting 26import org.yuzu.yuzu_emu.features.settings.model.view.StringSingleChoiceSetting
26import org.yuzu.yuzu_emu.model.SettingsViewModel 27import org.yuzu.yuzu_emu.utils.ParamPackage
28import org.yuzu.yuzu_emu.utils.collect
27 29
28class SettingsDialogFragment : DialogFragment(), DialogInterface.OnClickListener { 30class SettingsDialogFragment : DialogFragment(), DialogInterface.OnClickListener {
29 private var type = 0 31 private var type = 0
@@ -50,8 +52,49 @@ class SettingsDialogFragment : DialogFragment(), DialogInterface.OnClickListener
50 MaterialAlertDialogBuilder(requireContext()) 52 MaterialAlertDialogBuilder(requireContext())
51 .setMessage(R.string.reset_setting_confirmation) 53 .setMessage(R.string.reset_setting_confirmation)
52 .setPositiveButton(android.R.string.ok) { _: DialogInterface, _: Int -> 54 .setPositiveButton(android.R.string.ok) { _: DialogInterface, _: Int ->
53 settingsViewModel.clickedItem!!.setting.reset() 55 when (val item = settingsViewModel.clickedItem) {
54 settingsViewModel.setAdapterItemChanged(position) 56 is AnalogInputSetting -> {
57 val stickParam = NativeInput.getStickParam(
58 item.playerIndex,
59 item.nativeAnalog
60 )
61 if (stickParam.get("engine", "") == "analog_from_button") {
62 when (item.analogDirection) {
63 AnalogDirection.Up -> stickParam.erase("up")
64 AnalogDirection.Down -> stickParam.erase("down")
65 AnalogDirection.Left -> stickParam.erase("left")
66 AnalogDirection.Right -> stickParam.erase("right")
67 }
68 NativeInput.setStickParam(
69 item.playerIndex,
70 item.nativeAnalog,
71 stickParam
72 )
73 settingsViewModel.setAdapterItemChanged(position)
74 } else {
75 NativeInput.setStickParam(
76 item.playerIndex,
77 item.nativeAnalog,
78 ParamPackage()
79 )
80 settingsViewModel.setDatasetChanged(true)
81 }
82 }
83
84 is ButtonInputSetting -> {
85 NativeInput.setButtonParam(
86 item.playerIndex,
87 item.nativeButton,
88 ParamPackage()
89 )
90 settingsViewModel.setAdapterItemChanged(position)
91 }
92
93 else -> {
94 settingsViewModel.clickedItem!!.setting.reset()
95 settingsViewModel.setAdapterItemChanged(position)
96 }
97 }
55 } 98 }
56 .setNegativeButton(android.R.string.cancel, null) 99 .setNegativeButton(android.R.string.cancel, null)
57 .create() 100 .create()
@@ -61,7 +104,7 @@ class SettingsDialogFragment : DialogFragment(), DialogInterface.OnClickListener
61 val item = settingsViewModel.clickedItem as SingleChoiceSetting 104 val item = settingsViewModel.clickedItem as SingleChoiceSetting
62 val value = getSelectionForSingleChoiceValue(item) 105 val value = getSelectionForSingleChoiceValue(item)
63 MaterialAlertDialogBuilder(requireContext()) 106 MaterialAlertDialogBuilder(requireContext())
64 .setTitle(item.nameId) 107 .setTitle(item.title)
65 .setSingleChoiceItems(item.choicesId, value, this) 108 .setSingleChoiceItems(item.choicesId, value, this)
66 .create() 109 .create()
67 } 110 }
@@ -81,7 +124,7 @@ class SettingsDialogFragment : DialogFragment(), DialogInterface.OnClickListener
81 } 124 }
82 125
83 MaterialAlertDialogBuilder(requireContext()) 126 MaterialAlertDialogBuilder(requireContext())
84 .setTitle(item.nameId) 127 .setTitle(item.title)
85 .setView(sliderBinding.root) 128 .setView(sliderBinding.root)
86 .setPositiveButton(android.R.string.ok, this) 129 .setPositiveButton(android.R.string.ok, this)
87 .setNegativeButton(android.R.string.cancel, defaultCancelListener) 130 .setNegativeButton(android.R.string.cancel, defaultCancelListener)
@@ -91,8 +134,16 @@ class SettingsDialogFragment : DialogFragment(), DialogInterface.OnClickListener
91 SettingsItem.TYPE_STRING_SINGLE_CHOICE -> { 134 SettingsItem.TYPE_STRING_SINGLE_CHOICE -> {
92 val item = settingsViewModel.clickedItem as StringSingleChoiceSetting 135 val item = settingsViewModel.clickedItem as StringSingleChoiceSetting
93 MaterialAlertDialogBuilder(requireContext()) 136 MaterialAlertDialogBuilder(requireContext())
94 .setTitle(item.nameId) 137 .setTitle(item.title)
95 .setSingleChoiceItems(item.choices, item.selectValueIndex, this) 138 .setSingleChoiceItems(item.choices, item.selectedValueIndex, this)
139 .create()
140 }
141
142 SettingsItem.TYPE_INT_SINGLE_CHOICE -> {
143 val item = settingsViewModel.clickedItem as IntSingleChoiceSetting
144 MaterialAlertDialogBuilder(requireContext())
145 .setTitle(item.title)
146 .setSingleChoiceItems(item.choices, item.selectedValueIndex, this)
96 .create() 147 .create()
97 } 148 }
98 149
@@ -115,17 +166,11 @@ class SettingsDialogFragment : DialogFragment(), DialogInterface.OnClickListener
115 super.onViewCreated(view, savedInstanceState) 166 super.onViewCreated(view, savedInstanceState)
116 when (type) { 167 when (type) {
117 SettingsItem.TYPE_SLIDER -> { 168 SettingsItem.TYPE_SLIDER -> {
118 viewLifecycleOwner.lifecycleScope.launch { 169 settingsViewModel.sliderTextValue.collect(viewLifecycleOwner) {
119 repeatOnLifecycle(Lifecycle.State.CREATED) { 170 sliderBinding.textValue.text = it
120 settingsViewModel.sliderTextValue.collect { 171 }
121 sliderBinding.textValue.text = it 172 settingsViewModel.sliderProgress.collect(viewLifecycleOwner) {
122 } 173 sliderBinding.slider.value = it.toFloat()
123 }
124 repeatOnLifecycle(Lifecycle.State.CREATED) {
125 settingsViewModel.sliderProgress.collect {
126 sliderBinding.slider.value = it.toFloat()
127 }
128 }
129 } 174 }
130 } 175 }
131 } 176 }
@@ -145,6 +190,12 @@ class SettingsDialogFragment : DialogFragment(), DialogInterface.OnClickListener
145 scSetting.setSelectedValue(value) 190 scSetting.setSelectedValue(value)
146 } 191 }
147 192
193 is IntSingleChoiceSetting -> {
194 val scSetting = settingsViewModel.clickedItem as IntSingleChoiceSetting
195 val value = scSetting.getValueAt(which)
196 scSetting.setSelectedValue(value)
197 }
198
148 is SliderSetting -> { 199 is SliderSetting -> {
149 val sliderSetting = settingsViewModel.clickedItem as SliderSetting 200 val sliderSetting = settingsViewModel.clickedItem as SliderSetting
150 sliderSetting.setSelectedValue(settingsViewModel.sliderProgress.value) 201 sliderSetting.setSelectedValue(settingsViewModel.sliderProgress.value)
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragment.kt
index 6f6e7be10..ec16f16c4 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragment.kt
@@ -13,20 +13,17 @@ import androidx.core.view.WindowInsetsCompat
13import androidx.core.view.updatePadding 13import androidx.core.view.updatePadding
14import androidx.fragment.app.Fragment 14import androidx.fragment.app.Fragment
15import androidx.fragment.app.activityViewModels 15import androidx.fragment.app.activityViewModels
16import androidx.lifecycle.Lifecycle
17import androidx.lifecycle.lifecycleScope
18import androidx.lifecycle.repeatOnLifecycle
19import androidx.navigation.findNavController 16import androidx.navigation.findNavController
20import androidx.navigation.fragment.navArgs 17import androidx.navigation.fragment.navArgs
21import androidx.recyclerview.widget.LinearLayoutManager 18import androidx.recyclerview.widget.LinearLayoutManager
22import com.google.android.material.transition.MaterialSharedAxis 19import com.google.android.material.transition.MaterialSharedAxis
23import kotlinx.coroutines.flow.collectLatest
24import kotlinx.coroutines.launch
25import org.yuzu.yuzu_emu.R 20import org.yuzu.yuzu_emu.R
26import org.yuzu.yuzu_emu.databinding.FragmentSettingsBinding 21import org.yuzu.yuzu_emu.databinding.FragmentSettingsBinding
22import org.yuzu.yuzu_emu.features.input.NativeInput
27import org.yuzu.yuzu_emu.features.settings.model.Settings 23import org.yuzu.yuzu_emu.features.settings.model.Settings
28import org.yuzu.yuzu_emu.model.SettingsViewModel 24import org.yuzu.yuzu_emu.fragments.MessageDialogFragment
29import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins 25import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
26import org.yuzu.yuzu_emu.utils.collect
30 27
31class SettingsFragment : Fragment() { 28class SettingsFragment : Fragment() {
32 private lateinit var presenter: SettingsFragmentPresenter 29 private lateinit var presenter: SettingsFragmentPresenter
@@ -45,6 +42,12 @@ class SettingsFragment : Fragment() {
45 returnTransition = MaterialSharedAxis(MaterialSharedAxis.X, false) 42 returnTransition = MaterialSharedAxis(MaterialSharedAxis.X, false)
46 reenterTransition = MaterialSharedAxis(MaterialSharedAxis.X, false) 43 reenterTransition = MaterialSharedAxis(MaterialSharedAxis.X, false)
47 exitTransition = MaterialSharedAxis(MaterialSharedAxis.X, true) 44 exitTransition = MaterialSharedAxis(MaterialSharedAxis.X, true)
45
46 val playerIndex = getPlayerIndex()
47 if (playerIndex != -1) {
48 NativeInput.loadInputProfiles()
49 NativeInput.reloadInputDevices()
50 }
48 } 51 }
49 52
50 override fun onCreateView( 53 override fun onCreateView(
@@ -56,9 +59,9 @@ class SettingsFragment : Fragment() {
56 return binding.root 59 return binding.root
57 } 60 }
58 61
59 // This is using the correct scope, lint is just acting up 62 @SuppressLint("NotifyDataSetChanged")
60 @SuppressLint("UnsafeRepeatOnLifecycleDetector")
61 override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 63 override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
64 super.onViewCreated(view, savedInstanceState)
62 settingsAdapter = SettingsAdapter(this, requireContext()) 65 settingsAdapter = SettingsAdapter(this, requireContext())
63 presenter = SettingsFragmentPresenter( 66 presenter = SettingsFragmentPresenter(
64 settingsViewModel, 67 settingsViewModel,
@@ -71,7 +74,17 @@ class SettingsFragment : Fragment() {
71 ) { 74 ) {
72 args.game!!.title 75 args.game!!.title
73 } else { 76 } else {
74 getString(args.menuTag.titleId) 77 when (args.menuTag) {
78 Settings.MenuTag.SECTION_INPUT_PLAYER_ONE -> Settings.getPlayerString(1)
79 Settings.MenuTag.SECTION_INPUT_PLAYER_TWO -> Settings.getPlayerString(2)
80 Settings.MenuTag.SECTION_INPUT_PLAYER_THREE -> Settings.getPlayerString(3)
81 Settings.MenuTag.SECTION_INPUT_PLAYER_FOUR -> Settings.getPlayerString(4)
82 Settings.MenuTag.SECTION_INPUT_PLAYER_FIVE -> Settings.getPlayerString(5)
83 Settings.MenuTag.SECTION_INPUT_PLAYER_SIX -> Settings.getPlayerString(6)
84 Settings.MenuTag.SECTION_INPUT_PLAYER_SEVEN -> Settings.getPlayerString(7)
85 Settings.MenuTag.SECTION_INPUT_PLAYER_EIGHT -> Settings.getPlayerString(8)
86 else -> getString(args.menuTag.titleId)
87 }
75 } 88 }
76 binding.listSettings.apply { 89 binding.listSettings.apply {
77 adapter = settingsAdapter 90 adapter = settingsAdapter
@@ -82,16 +95,37 @@ class SettingsFragment : Fragment() {
82 settingsViewModel.setShouldNavigateBack(true) 95 settingsViewModel.setShouldNavigateBack(true)
83 } 96 }
84 97
85 viewLifecycleOwner.lifecycleScope.apply { 98 settingsViewModel.shouldReloadSettingsList.collect(
86 launch { 99 viewLifecycleOwner,
87 repeatOnLifecycle(Lifecycle.State.CREATED) { 100 resetState = { settingsViewModel.setShouldReloadSettingsList(false) }
88 settingsViewModel.shouldReloadSettingsList.collectLatest { 101 ) { if (it) presenter.loadSettingsList() }
89 if (it) { 102 settingsViewModel.adapterItemChanged.collect(
90 settingsViewModel.setShouldReloadSettingsList(false) 103 viewLifecycleOwner,
91 presenter.loadSettingsList() 104 resetState = { settingsViewModel.setAdapterItemChanged(-1) }
92 } 105 ) { if (it != -1) settingsAdapter?.notifyItemChanged(it) }
93 } 106 settingsViewModel.datasetChanged.collect(
94 } 107 viewLifecycleOwner,
108 resetState = { settingsViewModel.setDatasetChanged(false) }
109 ) { if (it) settingsAdapter?.notifyDataSetChanged() }
110 settingsViewModel.reloadListAndNotifyDataset.collect(
111 viewLifecycleOwner,
112 resetState = { settingsViewModel.setReloadListAndNotifyDataset(false) }
113 ) { if (it) presenter.loadSettingsList(true) }
114 settingsViewModel.shouldShowResetInputDialog.collect(
115 viewLifecycleOwner,
116 resetState = { settingsViewModel.setShouldShowResetInputDialog(false) }
117 ) {
118 if (it) {
119 MessageDialogFragment.newInstance(
120 activity = requireActivity(),
121 titleId = R.string.reset_mapping,
122 descriptionId = R.string.reset_mapping_description,
123 positiveAction = {
124 NativeInput.resetControllerMappings(getPlayerIndex())
125 settingsViewModel.setReloadListAndNotifyDataset(true)
126 },
127 negativeAction = {}
128 ).show(parentFragmentManager, MessageDialogFragment.TAG)
95 } 129 }
96 } 130 }
97 131
@@ -115,6 +149,19 @@ class SettingsFragment : Fragment() {
115 setInsets() 149 setInsets()
116 } 150 }
117 151
152 private fun getPlayerIndex(): Int =
153 when (args.menuTag) {
154 Settings.MenuTag.SECTION_INPUT_PLAYER_ONE -> 0
155 Settings.MenuTag.SECTION_INPUT_PLAYER_TWO -> 1
156 Settings.MenuTag.SECTION_INPUT_PLAYER_THREE -> 2
157 Settings.MenuTag.SECTION_INPUT_PLAYER_FOUR -> 3
158 Settings.MenuTag.SECTION_INPUT_PLAYER_FIVE -> 4
159 Settings.MenuTag.SECTION_INPUT_PLAYER_SIX -> 5
160 Settings.MenuTag.SECTION_INPUT_PLAYER_SEVEN -> 6
161 Settings.MenuTag.SECTION_INPUT_PLAYER_EIGHT -> 7
162 else -> -1
163 }
164
118 private fun setInsets() { 165 private fun setInsets() {
119 ViewCompat.setOnApplyWindowInsetsListener( 166 ViewCompat.setOnApplyWindowInsetsListener(
120 binding.root 167 binding.root
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt
index db1a58147..e491c29a2 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt
@@ -3,11 +3,17 @@
3 3
4package org.yuzu.yuzu_emu.features.settings.ui 4package org.yuzu.yuzu_emu.features.settings.ui
5 5
6import android.annotation.SuppressLint
6import android.os.Build 7import android.os.Build
7import android.widget.Toast 8import android.widget.Toast
8import org.yuzu.yuzu_emu.NativeLibrary 9import org.yuzu.yuzu_emu.NativeLibrary
9import org.yuzu.yuzu_emu.R 10import org.yuzu.yuzu_emu.R
10import org.yuzu.yuzu_emu.YuzuApplication 11import org.yuzu.yuzu_emu.YuzuApplication
12import org.yuzu.yuzu_emu.features.input.NativeInput
13import org.yuzu.yuzu_emu.features.input.model.AnalogDirection
14import org.yuzu.yuzu_emu.features.input.model.NativeAnalog
15import org.yuzu.yuzu_emu.features.input.model.NativeButton
16import org.yuzu.yuzu_emu.features.input.model.NpadStyleIndex
11import org.yuzu.yuzu_emu.features.settings.model.AbstractBooleanSetting 17import org.yuzu.yuzu_emu.features.settings.model.AbstractBooleanSetting
12import org.yuzu.yuzu_emu.features.settings.model.AbstractIntSetting 18import org.yuzu.yuzu_emu.features.settings.model.AbstractIntSetting
13import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting 19import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting
@@ -15,18 +21,21 @@ import org.yuzu.yuzu_emu.features.settings.model.ByteSetting
15import org.yuzu.yuzu_emu.features.settings.model.IntSetting 21import org.yuzu.yuzu_emu.features.settings.model.IntSetting
16import org.yuzu.yuzu_emu.features.settings.model.LongSetting 22import org.yuzu.yuzu_emu.features.settings.model.LongSetting
17import org.yuzu.yuzu_emu.features.settings.model.Settings 23import org.yuzu.yuzu_emu.features.settings.model.Settings
24import org.yuzu.yuzu_emu.features.settings.model.Settings.MenuTag
18import org.yuzu.yuzu_emu.features.settings.model.ShortSetting 25import org.yuzu.yuzu_emu.features.settings.model.ShortSetting
19import org.yuzu.yuzu_emu.features.settings.model.view.* 26import org.yuzu.yuzu_emu.features.settings.model.view.*
20import org.yuzu.yuzu_emu.model.SettingsViewModel 27import org.yuzu.yuzu_emu.utils.InputHandler
21import org.yuzu.yuzu_emu.utils.NativeConfig 28import org.yuzu.yuzu_emu.utils.NativeConfig
22 29
23class SettingsFragmentPresenter( 30class SettingsFragmentPresenter(
24 private val settingsViewModel: SettingsViewModel, 31 private val settingsViewModel: SettingsViewModel,
25 private val adapter: SettingsAdapter, 32 private val adapter: SettingsAdapter,
26 private var menuTag: Settings.MenuTag 33 private var menuTag: MenuTag
27) { 34) {
28 private var settingsList = ArrayList<SettingsItem>() 35 private var settingsList = ArrayList<SettingsItem>()
29 36
37 private val context get() = YuzuApplication.appContext
38
30 // Extension for altering settings list based on each setting's properties 39 // Extension for altering settings list based on each setting's properties
31 fun ArrayList<SettingsItem>.add(key: String) { 40 fun ArrayList<SettingsItem>.add(key: String) {
32 val item = SettingsItem.settingsItems[key]!! 41 val item = SettingsItem.settingsItems[key]!!
@@ -53,73 +62,90 @@ class SettingsFragmentPresenter(
53 add(item) 62 add(item)
54 } 63 }
55 64
65 // Allows you to show/hide abstract settings based on the paired setting key
66 fun ArrayList<SettingsItem>.addAbstract(item: SettingsItem) {
67 val pairedSettingKey = item.setting.pairedSettingKey
68 if (pairedSettingKey.isNotEmpty()) {
69 val pairedSettingsItem =
70 this.firstOrNull { it.setting.key == pairedSettingKey } ?: return
71 val pairedSetting = pairedSettingsItem.setting as AbstractBooleanSetting
72 if (!pairedSetting.getBoolean(!NativeConfig.isPerGameConfigLoaded())) return
73 }
74 add(item)
75 }
76
56 fun onViewCreated() { 77 fun onViewCreated() {
57 loadSettingsList() 78 loadSettingsList()
58 } 79 }
59 80
60 fun loadSettingsList() { 81 @SuppressLint("NotifyDataSetChanged")
82 fun loadSettingsList(notifyDataSetChanged: Boolean = false) {
61 val sl = ArrayList<SettingsItem>() 83 val sl = ArrayList<SettingsItem>()
62 when (menuTag) { 84 when (menuTag) {
63 Settings.MenuTag.SECTION_ROOT -> addConfigSettings(sl) 85 MenuTag.SECTION_ROOT -> addConfigSettings(sl)
64 Settings.MenuTag.SECTION_SYSTEM -> addSystemSettings(sl) 86 MenuTag.SECTION_SYSTEM -> addSystemSettings(sl)
65 Settings.MenuTag.SECTION_RENDERER -> addGraphicsSettings(sl) 87 MenuTag.SECTION_RENDERER -> addGraphicsSettings(sl)
66 Settings.MenuTag.SECTION_AUDIO -> addAudioSettings(sl) 88 MenuTag.SECTION_AUDIO -> addAudioSettings(sl)
67 Settings.MenuTag.SECTION_THEME -> addThemeSettings(sl) 89 MenuTag.SECTION_INPUT -> addInputSettings(sl)
68 Settings.MenuTag.SECTION_DEBUG -> addDebugSettings(sl) 90 MenuTag.SECTION_INPUT_PLAYER_ONE -> addInputPlayer(sl, 0)
69 else -> { 91 MenuTag.SECTION_INPUT_PLAYER_TWO -> addInputPlayer(sl, 1)
70 val context = YuzuApplication.appContext 92 MenuTag.SECTION_INPUT_PLAYER_THREE -> addInputPlayer(sl, 2)
71 Toast.makeText( 93 MenuTag.SECTION_INPUT_PLAYER_FOUR -> addInputPlayer(sl, 3)
72 context, 94 MenuTag.SECTION_INPUT_PLAYER_FIVE -> addInputPlayer(sl, 4)
73 context.getString(R.string.unimplemented_menu), 95 MenuTag.SECTION_INPUT_PLAYER_SIX -> addInputPlayer(sl, 5)
74 Toast.LENGTH_SHORT 96 MenuTag.SECTION_INPUT_PLAYER_SEVEN -> addInputPlayer(sl, 6)
75 ).show() 97 MenuTag.SECTION_INPUT_PLAYER_EIGHT -> addInputPlayer(sl, 7)
76 return 98 MenuTag.SECTION_THEME -> addThemeSettings(sl)
77 } 99 MenuTag.SECTION_DEBUG -> addDebugSettings(sl)
78 } 100 }
79 settingsList = sl 101 settingsList = sl
80 adapter.submitList(settingsList) 102 adapter.submitList(settingsList) {
103 if (notifyDataSetChanged) {
104 adapter.notifyDataSetChanged()
105 }
106 }
81 } 107 }
82 108
83 private fun addConfigSettings(sl: ArrayList<SettingsItem>) { 109 private fun addConfigSettings(sl: ArrayList<SettingsItem>) {
84 sl.apply { 110 sl.apply {
85 add( 111 add(
86 SubmenuSetting( 112 SubmenuSetting(
87 R.string.preferences_system, 113 titleId = R.string.preferences_system,
88 R.string.preferences_system_description, 114 descriptionId = R.string.preferences_system_description,
89 R.drawable.ic_system_settings, 115 iconId = R.drawable.ic_system_settings,
90 Settings.MenuTag.SECTION_SYSTEM 116 menuKey = MenuTag.SECTION_SYSTEM
91 ) 117 )
92 ) 118 )
93 add( 119 add(
94 SubmenuSetting( 120 SubmenuSetting(
95 R.string.preferences_graphics, 121 titleId = R.string.preferences_graphics,
96 R.string.preferences_graphics_description, 122 descriptionId = R.string.preferences_graphics_description,
97 R.drawable.ic_graphics, 123 iconId = R.drawable.ic_graphics,
98 Settings.MenuTag.SECTION_RENDERER 124 menuKey = MenuTag.SECTION_RENDERER
99 ) 125 )
100 ) 126 )
101 add( 127 add(
102 SubmenuSetting( 128 SubmenuSetting(
103 R.string.preferences_audio, 129 titleId = R.string.preferences_audio,
104 R.string.preferences_audio_description, 130 descriptionId = R.string.preferences_audio_description,
105 R.drawable.ic_audio, 131 iconId = R.drawable.ic_audio,
106 Settings.MenuTag.SECTION_AUDIO 132 menuKey = MenuTag.SECTION_AUDIO
107 ) 133 )
108 ) 134 )
109 add( 135 add(
110 SubmenuSetting( 136 SubmenuSetting(
111 R.string.preferences_debug, 137 titleId = R.string.preferences_debug,
112 R.string.preferences_debug_description, 138 descriptionId = R.string.preferences_debug_description,
113 R.drawable.ic_code, 139 iconId = R.drawable.ic_code,
114 Settings.MenuTag.SECTION_DEBUG 140 menuKey = MenuTag.SECTION_DEBUG
115 ) 141 )
116 ) 142 )
117 add( 143 add(
118 RunnableSetting( 144 RunnableSetting(
119 R.string.reset_to_default, 145 titleId = R.string.reset_to_default,
120 R.string.reset_to_default_description, 146 descriptionId = R.string.reset_to_default_description,
121 false, 147 isRunnable = !NativeLibrary.isRunning(),
122 R.drawable.ic_restore 148 iconId = R.drawable.ic_restore
123 ) { settingsViewModel.setShouldShowResetSettingsDialog(true) } 149 ) { settingsViewModel.setShouldShowResetSettingsDialog(true) }
124 ) 150 )
125 } 151 }
@@ -164,6 +190,671 @@ class SettingsFragmentPresenter(
164 } 190 }
165 } 191 }
166 192
193 private fun addInputSettings(sl: ArrayList<SettingsItem>) {
194 settingsViewModel.currentDevice = 0
195
196 if (NativeConfig.isPerGameConfigLoaded()) {
197 NativeInput.loadInputProfiles()
198 val profiles = NativeInput.getInputProfileNames().toMutableList()
199 profiles.add(0, "")
200 val prettyProfiles = profiles.toTypedArray()
201 prettyProfiles[0] =
202 context.getString(R.string.use_global_input_configuration)
203 sl.apply {
204 for (i in 0 until 8) {
205 add(
206 IntSingleChoiceSetting(
207 getPerGameProfileSetting(profiles, i),
208 titleString = getPlayerProfileString(i + 1),
209 choices = prettyProfiles,
210 values = IntArray(profiles.size) { it }.toTypedArray()
211 )
212 )
213 }
214 }
215 return
216 }
217
218 val getConnectedIcon: (Int) -> Int = { playerIndex: Int ->
219 if (NativeInput.getIsConnected(playerIndex)) {
220 R.drawable.ic_controller
221 } else {
222 R.drawable.ic_controller_disconnected
223 }
224 }
225
226 val inputSettings = NativeConfig.getInputSettings(true)
227 sl.apply {
228 add(
229 SubmenuSetting(
230 titleString = Settings.getPlayerString(1),
231 descriptionString = inputSettings[0].profileName,
232 menuKey = MenuTag.SECTION_INPUT_PLAYER_ONE,
233 iconId = getConnectedIcon(0)
234 )
235 )
236 add(
237 SubmenuSetting(
238 titleString = Settings.getPlayerString(2),
239 descriptionString = inputSettings[1].profileName,
240 menuKey = MenuTag.SECTION_INPUT_PLAYER_TWO,
241 iconId = getConnectedIcon(1)
242 )
243 )
244 add(
245 SubmenuSetting(
246 titleString = Settings.getPlayerString(3),
247 descriptionString = inputSettings[2].profileName,
248 menuKey = MenuTag.SECTION_INPUT_PLAYER_THREE,
249 iconId = getConnectedIcon(2)
250 )
251 )
252 add(
253 SubmenuSetting(
254 titleString = Settings.getPlayerString(4),
255 descriptionString = inputSettings[3].profileName,
256 menuKey = MenuTag.SECTION_INPUT_PLAYER_FOUR,
257 iconId = getConnectedIcon(3)
258 )
259 )
260 add(
261 SubmenuSetting(
262 titleString = Settings.getPlayerString(5),
263 descriptionString = inputSettings[4].profileName,
264 menuKey = MenuTag.SECTION_INPUT_PLAYER_FIVE,
265 iconId = getConnectedIcon(4)
266 )
267 )
268 add(
269 SubmenuSetting(
270 titleString = Settings.getPlayerString(6),
271 descriptionString = inputSettings[5].profileName,
272 menuKey = MenuTag.SECTION_INPUT_PLAYER_SIX,
273 iconId = getConnectedIcon(5)
274 )
275 )
276 add(
277 SubmenuSetting(
278 titleString = Settings.getPlayerString(7),
279 descriptionString = inputSettings[6].profileName,
280 menuKey = MenuTag.SECTION_INPUT_PLAYER_SEVEN,
281 iconId = getConnectedIcon(6)
282 )
283 )
284 add(
285 SubmenuSetting(
286 titleString = Settings.getPlayerString(8),
287 descriptionString = inputSettings[7].profileName,
288 menuKey = MenuTag.SECTION_INPUT_PLAYER_EIGHT,
289 iconId = getConnectedIcon(7)
290 )
291 )
292 }
293 }
294
295 private fun getPlayerProfileString(player: Int): String =
296 context.getString(R.string.player_num_profile, player)
297
298 private fun getPerGameProfileSetting(
299 profiles: List<String>,
300 playerIndex: Int
301 ): AbstractIntSetting {
302 return object : AbstractIntSetting {
303 private val players
304 get() = NativeConfig.getInputSettings(false)
305
306 override val key = ""
307
308 override fun getInt(needsGlobal: Boolean): Int {
309 val currentProfile = players[playerIndex].profileName
310 profiles.forEachIndexed { i, profile ->
311 if (profile == currentProfile) {
312 return i
313 }
314 }
315 return 0
316 }
317
318 override fun setInt(value: Int) {
319 NativeInput.loadPerGameConfiguration(playerIndex, value, profiles[value])
320 NativeInput.connectControllers(playerIndex)
321 NativeConfig.saveControlPlayerValues()
322 }
323
324 override val defaultValue = 0
325
326 override fun getValueAsString(needsGlobal: Boolean): String = getInt().toString()
327
328 override fun reset() = setInt(defaultValue)
329
330 override var global = true
331
332 override val isRuntimeModifiable = true
333
334 override val isSaveable = true
335 }
336 }
337
338 private fun addInputPlayer(sl: ArrayList<SettingsItem>, playerIndex: Int) {
339 sl.apply {
340 val connectedSetting = object : AbstractBooleanSetting {
341 override val key = "connected"
342
343 override fun getBoolean(needsGlobal: Boolean): Boolean =
344 NativeInput.getIsConnected(playerIndex)
345
346 override fun setBoolean(value: Boolean) =
347 NativeInput.connectControllers(playerIndex, value)
348
349 override val defaultValue = playerIndex == 0
350
351 override fun getValueAsString(needsGlobal: Boolean): String =
352 getBoolean(needsGlobal).toString()
353
354 override fun reset() = setBoolean(defaultValue)
355 }
356 add(SwitchSetting(connectedSetting, R.string.connected))
357
358 val styleTags = NativeInput.getSupportedStyleTags(playerIndex)
359 val npadType = object : AbstractIntSetting {
360 override val key = "npad_type"
361 override fun getInt(needsGlobal: Boolean): Int {
362 val styleIndex = NativeInput.getStyleIndex(playerIndex)
363 return styleTags.indexOfFirst { it == styleIndex }
364 }
365
366 override fun setInt(value: Int) {
367 NativeInput.setStyleIndex(playerIndex, styleTags[value])
368 settingsViewModel.setReloadListAndNotifyDataset(true)
369 }
370
371 override val defaultValue = NpadStyleIndex.Fullkey.int
372 override fun getValueAsString(needsGlobal: Boolean): String = getInt().toString()
373 override fun reset() = setInt(defaultValue)
374 override val pairedSettingKey: String = "connected"
375 }
376 addAbstract(
377 IntSingleChoiceSetting(
378 npadType,
379 titleId = R.string.controller_type,
380 choices = styleTags.map { context.getString(it.nameId) }
381 .toTypedArray(),
382 values = IntArray(styleTags.size) { it }.toTypedArray()
383 )
384 )
385
386 InputHandler.updateControllerData()
387
388 val autoMappingSetting = object : AbstractIntSetting {
389 override val key = "auto_mapping_device"
390
391 override fun getInt(needsGlobal: Boolean): Int = -1
392
393 override fun setInt(value: Int) {
394 val registeredController = InputHandler.registeredControllers[value + 1]
395 val displayName = registeredController.get(
396 "display",
397 context.getString(R.string.unknown)
398 )
399 NativeInput.updateMappingsWithDefault(
400 playerIndex,
401 registeredController,
402 displayName
403 )
404 Toast.makeText(
405 context,
406 context.getString(R.string.attempted_auto_map, displayName),
407 Toast.LENGTH_SHORT
408 ).show()
409 settingsViewModel.setReloadListAndNotifyDataset(true)
410 }
411
412 override val defaultValue = -1
413
414 override fun getValueAsString(needsGlobal: Boolean) = getInt().toString()
415
416 override fun reset() = setInt(defaultValue)
417
418 override val isRuntimeModifiable: Boolean = true
419 }
420
421 val unknownString = context.getString(R.string.unknown)
422 val prettyAutoMappingControllerList = InputHandler.registeredControllers.mapNotNull {
423 val port = it.get("port", -1)
424 return@mapNotNull if (port == 100 || port == -1) {
425 null
426 } else {
427 it.get("display", unknownString)
428 }
429 }.toTypedArray()
430 add(
431 IntSingleChoiceSetting(
432 autoMappingSetting,
433 titleId = R.string.auto_map,
434 descriptionId = R.string.auto_map_description,
435 choices = prettyAutoMappingControllerList,
436 values = IntArray(prettyAutoMappingControllerList.size) { it }.toTypedArray()
437 )
438 )
439
440 val mappingFilterSetting = object : AbstractIntSetting {
441 override val key = "mapping_filter"
442
443 override fun getInt(needsGlobal: Boolean): Int = settingsViewModel.currentDevice
444
445 override fun setInt(value: Int) {
446 settingsViewModel.currentDevice = value
447 }
448
449 override val defaultValue = 0
450
451 override fun getValueAsString(needsGlobal: Boolean) = getInt().toString()
452
453 override fun reset() = setInt(defaultValue)
454
455 override val isRuntimeModifiable: Boolean = true
456 }
457
458 val prettyControllerList = InputHandler.registeredControllers.mapNotNull {
459 return@mapNotNull if (it.get("port", 0) == 100) {
460 null
461 } else {
462 it.get("display", unknownString)
463 }
464 }.toTypedArray()
465 add(
466 IntSingleChoiceSetting(
467 mappingFilterSetting,
468 titleId = R.string.input_mapping_filter,
469 descriptionId = R.string.input_mapping_filter_description,
470 choices = prettyControllerList,
471 values = IntArray(prettyControllerList.size) { it }.toTypedArray()
472 )
473 )
474
475 add(InputProfileSetting(playerIndex))
476 add(
477 RunnableSetting(titleId = R.string.reset_to_default, isRunnable = true) {
478 settingsViewModel.setShouldShowResetInputDialog(true)
479 }
480 )
481
482 val styleIndex = NativeInput.getStyleIndex(playerIndex)
483
484 // Buttons
485 when (styleIndex) {
486 NpadStyleIndex.Fullkey,
487 NpadStyleIndex.Handheld,
488 NpadStyleIndex.JoyconDual -> {
489 add(HeaderSetting(R.string.buttons))
490 add(ButtonInputSetting(playerIndex, NativeButton.A, R.string.button_a))
491 add(ButtonInputSetting(playerIndex, NativeButton.B, R.string.button_b))
492 add(ButtonInputSetting(playerIndex, NativeButton.X, R.string.button_x))
493 add(ButtonInputSetting(playerIndex, NativeButton.Y, R.string.button_y))
494 add(ButtonInputSetting(playerIndex, NativeButton.Plus, R.string.button_plus))
495 add(ButtonInputSetting(playerIndex, NativeButton.Minus, R.string.button_minus))
496 add(ButtonInputSetting(playerIndex, NativeButton.Home, R.string.button_home))
497 add(
498 ButtonInputSetting(
499 playerIndex,
500 NativeButton.Capture,
501 R.string.button_capture
502 )
503 )
504 }
505
506 NpadStyleIndex.JoyconLeft -> {
507 add(HeaderSetting(R.string.buttons))
508 add(ButtonInputSetting(playerIndex, NativeButton.Minus, R.string.button_minus))
509 add(
510 ButtonInputSetting(
511 playerIndex,
512 NativeButton.Capture,
513 R.string.button_capture
514 )
515 )
516 }
517
518 NpadStyleIndex.JoyconRight -> {
519 add(HeaderSetting(R.string.buttons))
520 add(ButtonInputSetting(playerIndex, NativeButton.A, R.string.button_a))
521 add(ButtonInputSetting(playerIndex, NativeButton.B, R.string.button_b))
522 add(ButtonInputSetting(playerIndex, NativeButton.X, R.string.button_x))
523 add(ButtonInputSetting(playerIndex, NativeButton.Y, R.string.button_y))
524 add(ButtonInputSetting(playerIndex, NativeButton.Plus, R.string.button_plus))
525 add(ButtonInputSetting(playerIndex, NativeButton.Home, R.string.button_home))
526 }
527
528 NpadStyleIndex.GameCube -> {
529 add(HeaderSetting(R.string.buttons))
530 add(ButtonInputSetting(playerIndex, NativeButton.A, R.string.button_a))
531 add(ButtonInputSetting(playerIndex, NativeButton.B, R.string.button_b))
532 add(ButtonInputSetting(playerIndex, NativeButton.X, R.string.button_x))
533 add(ButtonInputSetting(playerIndex, NativeButton.Y, R.string.button_y))
534 add(ButtonInputSetting(playerIndex, NativeButton.Plus, R.string.start_pause))
535 }
536
537 else -> {
538 // No-op
539 }
540 }
541
542 when (styleIndex) {
543 NpadStyleIndex.Fullkey,
544 NpadStyleIndex.Handheld,
545 NpadStyleIndex.JoyconDual,
546 NpadStyleIndex.JoyconLeft -> {
547 add(HeaderSetting(R.string.dpad))
548 add(ButtonInputSetting(playerIndex, NativeButton.DUp, R.string.up))
549 add(ButtonInputSetting(playerIndex, NativeButton.DDown, R.string.down))
550 add(ButtonInputSetting(playerIndex, NativeButton.DLeft, R.string.left))
551 add(ButtonInputSetting(playerIndex, NativeButton.DRight, R.string.right))
552 }
553
554 else -> {
555 // No-op
556 }
557 }
558
559 // Left stick
560 when (styleIndex) {
561 NpadStyleIndex.Fullkey,
562 NpadStyleIndex.Handheld,
563 NpadStyleIndex.JoyconDual,
564 NpadStyleIndex.JoyconLeft -> {
565 add(HeaderSetting(R.string.left_stick))
566 addAll(getStickDirections(playerIndex, NativeAnalog.LStick))
567 add(ButtonInputSetting(playerIndex, NativeButton.LStick, R.string.pressed))
568 addAll(getExtraStickSettings(playerIndex, NativeAnalog.LStick))
569 }
570
571 NpadStyleIndex.GameCube -> {
572 add(HeaderSetting(R.string.control_stick))
573 addAll(getStickDirections(playerIndex, NativeAnalog.LStick))
574 addAll(getExtraStickSettings(playerIndex, NativeAnalog.LStick))
575 }
576
577 else -> {
578 // No-op
579 }
580 }
581
582 // Right stick
583 when (styleIndex) {
584 NpadStyleIndex.Fullkey,
585 NpadStyleIndex.Handheld,
586 NpadStyleIndex.JoyconDual,
587 NpadStyleIndex.JoyconRight -> {
588 add(HeaderSetting(R.string.right_stick))
589 addAll(getStickDirections(playerIndex, NativeAnalog.RStick))
590 add(ButtonInputSetting(playerIndex, NativeButton.RStick, R.string.pressed))
591 addAll(getExtraStickSettings(playerIndex, NativeAnalog.RStick))
592 }
593
594 NpadStyleIndex.GameCube -> {
595 add(HeaderSetting(R.string.c_stick))
596 addAll(getStickDirections(playerIndex, NativeAnalog.RStick))
597 addAll(getExtraStickSettings(playerIndex, NativeAnalog.RStick))
598 }
599
600 else -> {
601 // No-op
602 }
603 }
604
605 // L/R, ZL/ZR, and SL/SR
606 when (styleIndex) {
607 NpadStyleIndex.Fullkey,
608 NpadStyleIndex.Handheld -> {
609 add(HeaderSetting(R.string.triggers))
610 add(ButtonInputSetting(playerIndex, NativeButton.L, R.string.button_l))
611 add(ButtonInputSetting(playerIndex, NativeButton.R, R.string.button_r))
612 add(ButtonInputSetting(playerIndex, NativeButton.ZL, R.string.button_zl))
613 add(ButtonInputSetting(playerIndex, NativeButton.ZR, R.string.button_zr))
614 }
615
616 NpadStyleIndex.JoyconDual -> {
617 add(HeaderSetting(R.string.triggers))
618 add(ButtonInputSetting(playerIndex, NativeButton.L, R.string.button_l))
619 add(ButtonInputSetting(playerIndex, NativeButton.R, R.string.button_r))
620 add(ButtonInputSetting(playerIndex, NativeButton.ZL, R.string.button_zl))
621 add(ButtonInputSetting(playerIndex, NativeButton.ZR, R.string.button_zr))
622 add(
623 ButtonInputSetting(
624 playerIndex,
625 NativeButton.SLLeft,
626 R.string.button_sl_left
627 )
628 )
629 add(
630 ButtonInputSetting(
631 playerIndex,
632 NativeButton.SRLeft,
633 R.string.button_sr_left
634 )
635 )
636 add(
637 ButtonInputSetting(
638 playerIndex,
639 NativeButton.SLRight,
640 R.string.button_sl_right
641 )
642 )
643 add(
644 ButtonInputSetting(
645 playerIndex,
646 NativeButton.SRRight,
647 R.string.button_sr_right
648 )
649 )
650 }
651
652 NpadStyleIndex.JoyconLeft -> {
653 add(HeaderSetting(R.string.triggers))
654 add(ButtonInputSetting(playerIndex, NativeButton.L, R.string.button_l))
655 add(ButtonInputSetting(playerIndex, NativeButton.ZL, R.string.button_zl))
656 add(
657 ButtonInputSetting(
658 playerIndex,
659 NativeButton.SLLeft,
660 R.string.button_sl_left
661 )
662 )
663 add(
664 ButtonInputSetting(
665 playerIndex,
666 NativeButton.SRLeft,
667 R.string.button_sr_left
668 )
669 )
670 }
671
672 NpadStyleIndex.JoyconRight -> {
673 add(HeaderSetting(R.string.triggers))
674 add(ButtonInputSetting(playerIndex, NativeButton.R, R.string.button_r))
675 add(ButtonInputSetting(playerIndex, NativeButton.ZR, R.string.button_zr))
676 add(
677 ButtonInputSetting(
678 playerIndex,
679 NativeButton.SLRight,
680 R.string.button_sl_right
681 )
682 )
683 add(
684 ButtonInputSetting(
685 playerIndex,
686 NativeButton.SRRight,
687 R.string.button_sr_right
688 )
689 )
690 }
691
692 NpadStyleIndex.GameCube -> {
693 add(HeaderSetting(R.string.triggers))
694 add(ButtonInputSetting(playerIndex, NativeButton.R, R.string.button_z))
695 add(ButtonInputSetting(playerIndex, NativeButton.ZL, R.string.button_l))
696 add(ButtonInputSetting(playerIndex, NativeButton.ZR, R.string.button_r))
697 }
698
699 else -> {
700 // No-op
701 }
702 }
703
704 add(HeaderSetting(R.string.vibration))
705 val vibrationEnabledSetting = object : AbstractBooleanSetting {
706 override val key = "vibration"
707
708 override fun getBoolean(needsGlobal: Boolean): Boolean =
709 NativeConfig.getInputSettings(true)[playerIndex].vibrationEnabled
710
711 override fun setBoolean(value: Boolean) {
712 val settings = NativeConfig.getInputSettings(true)
713 settings[playerIndex].vibrationEnabled = value
714 NativeConfig.setInputSettings(settings, true)
715 }
716
717 override val defaultValue = true
718
719 override fun getValueAsString(needsGlobal: Boolean): String =
720 getBoolean(needsGlobal).toString()
721
722 override fun reset() = setBoolean(defaultValue)
723 }
724 add(SwitchSetting(vibrationEnabledSetting, R.string.vibration))
725
726 val useSystemVibratorSetting = object : AbstractBooleanSetting {
727 override val key = ""
728
729 override fun getBoolean(needsGlobal: Boolean): Boolean =
730 NativeConfig.getInputSettings(true)[playerIndex].useSystemVibrator
731
732 override fun setBoolean(value: Boolean) {
733 val settings = NativeConfig.getInputSettings(true)
734 settings[playerIndex].useSystemVibrator = value
735 NativeConfig.setInputSettings(settings, true)
736 }
737
738 override val defaultValue = playerIndex == 0
739
740 override fun getValueAsString(needsGlobal: Boolean): String =
741 getBoolean(needsGlobal).toString()
742
743 override fun reset() = setBoolean(defaultValue)
744
745 override val pairedSettingKey: String = "vibration"
746 }
747 addAbstract(SwitchSetting(useSystemVibratorSetting, R.string.use_system_vibrator))
748
749 val vibrationStrengthSetting = object : AbstractIntSetting {
750 override val key = ""
751
752 override fun getInt(needsGlobal: Boolean): Int =
753 NativeConfig.getInputSettings(true)[playerIndex].vibrationStrength
754
755 override fun setInt(value: Int) {
756 val settings = NativeConfig.getInputSettings(true)
757 settings[playerIndex].vibrationStrength = value
758 NativeConfig.setInputSettings(settings, true)
759 }
760
761 override val defaultValue = 100
762
763 override fun getValueAsString(needsGlobal: Boolean): String =
764 getInt(needsGlobal).toString()
765
766 override fun reset() = setInt(defaultValue)
767
768 override val pairedSettingKey: String = "vibration"
769 }
770 addAbstract(
771 SliderSetting(vibrationStrengthSetting, R.string.vibration_strength, units = "%")
772 )
773 }
774 }
775
776 // Convenience function for creating AbstractIntSettings for modifier range/stick range/stick deadzones
777 private fun getStickIntSettingFromParam(
778 playerIndex: Int,
779 paramName: String,
780 stick: NativeAnalog,
781 defaultValue: Int
782 ): AbstractIntSetting =
783 object : AbstractIntSetting {
784 val params get() = NativeInput.getStickParam(playerIndex, stick)
785
786 override val key = ""
787
788 override fun getInt(needsGlobal: Boolean): Int =
789 (params.get(paramName, 0.15f) * 100).toInt()
790
791 override fun setInt(value: Int) {
792 val tempParams = params
793 tempParams.set(paramName, value.toFloat() / 100)
794 NativeInput.setStickParam(playerIndex, stick, tempParams)
795 }
796
797 override val defaultValue = defaultValue
798
799 override fun getValueAsString(needsGlobal: Boolean): String =
800 getInt(needsGlobal).toString()
801
802 override fun reset() = setInt(defaultValue)
803 }
804
805 private fun getExtraStickSettings(
806 playerIndex: Int,
807 nativeAnalog: NativeAnalog
808 ): List<SettingsItem> {
809 val stickIsController =
810 NativeInput.isController(NativeInput.getStickParam(playerIndex, nativeAnalog))
811 val modifierRangeSetting =
812 getStickIntSettingFromParam(playerIndex, "modifier_scale", nativeAnalog, 50)
813 val stickRangeSetting =
814 getStickIntSettingFromParam(playerIndex, "range", nativeAnalog, 95)
815 val stickDeadzoneSetting =
816 getStickIntSettingFromParam(playerIndex, "deadzone", nativeAnalog, 15)
817
818 val out = mutableListOf<SettingsItem>().apply {
819 if (stickIsController) {
820 add(SliderSetting(stickRangeSetting, titleId = R.string.range, min = 25, max = 150))
821 add(SliderSetting(stickDeadzoneSetting, R.string.deadzone))
822 } else {
823 add(ModifierInputSetting(playerIndex, NativeAnalog.LStick, R.string.modifier))
824 add(SliderSetting(modifierRangeSetting, R.string.modifier_range))
825 }
826 }
827 return out
828 }
829
830 private fun getStickDirections(player: Int, stick: NativeAnalog): List<AnalogInputSetting> =
831 listOf(
832 AnalogInputSetting(
833 player,
834 stick,
835 AnalogDirection.Up,
836 R.string.up
837 ),
838 AnalogInputSetting(
839 player,
840 stick,
841 AnalogDirection.Down,
842 R.string.down
843 ),
844 AnalogInputSetting(
845 player,
846 stick,
847 AnalogDirection.Left,
848 R.string.left
849 ),
850 AnalogInputSetting(
851 player,
852 stick,
853 AnalogDirection.Right,
854 R.string.right
855 )
856 )
857
167 private fun addThemeSettings(sl: ArrayList<SettingsItem>) { 858 private fun addThemeSettings(sl: ArrayList<SettingsItem>) {
168 sl.apply { 859 sl.apply {
169 val theme: AbstractIntSetting = object : AbstractIntSetting { 860 val theme: AbstractIntSetting = object : AbstractIntSetting {
@@ -186,20 +877,18 @@ class SettingsFragmentPresenter(
186 add( 877 add(
187 SingleChoiceSetting( 878 SingleChoiceSetting(
188 theme, 879 theme,
189 R.string.change_app_theme, 880 titleId = R.string.change_app_theme,
190 0, 881 choicesId = R.array.themeEntriesA12,
191 R.array.themeEntriesA12, 882 valuesId = R.array.themeValuesA12
192 R.array.themeValuesA12
193 ) 883 )
194 ) 884 )
195 } else { 885 } else {
196 add( 886 add(
197 SingleChoiceSetting( 887 SingleChoiceSetting(
198 theme, 888 theme,
199 R.string.change_app_theme, 889 titleId = R.string.change_app_theme,
200 0, 890 choicesId = R.array.themeEntries,
201 R.array.themeEntries, 891 valuesId = R.array.themeValues
202 R.array.themeValues
203 ) 892 )
204 ) 893 )
205 } 894 }
@@ -228,10 +917,9 @@ class SettingsFragmentPresenter(
228 add( 917 add(
229 SingleChoiceSetting( 918 SingleChoiceSetting(
230 themeMode, 919 themeMode,
231 R.string.change_theme_mode, 920 titleId = R.string.change_theme_mode,
232 0, 921 choicesId = R.array.themeModeEntries,
233 R.array.themeModeEntries, 922 valuesId = R.array.themeModeValues
234 R.array.themeModeValues
235 ) 923 )
236 ) 924 )
237 925
@@ -262,8 +950,8 @@ class SettingsFragmentPresenter(
262 add( 950 add(
263 SwitchSetting( 951 SwitchSetting(
264 blackBackgrounds, 952 blackBackgrounds,
265 R.string.use_black_backgrounds, 953 titleId = R.string.use_black_backgrounds,
266 R.string.use_black_backgrounds_description 954 descriptionId = R.string.use_black_backgrounds_description
267 ) 955 )
268 ) 956 )
269 } 957 }
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SettingsSearchFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsSearchFragment.kt
index a135b80b4..ed60cf34f 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SettingsSearchFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsSearchFragment.kt
@@ -1,7 +1,7 @@
1// SPDX-FileCopyrightText: 2023 yuzu Emulator Project 1// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4package org.yuzu.yuzu_emu.fragments 4package org.yuzu.yuzu_emu.features.settings.ui
5 5
6import android.content.Context 6import android.content.Context
7import android.os.Bundle 7import android.os.Bundle
@@ -15,21 +15,17 @@ import androidx.core.view.updatePadding
15import androidx.core.widget.doOnTextChanged 15import androidx.core.widget.doOnTextChanged
16import androidx.fragment.app.Fragment 16import androidx.fragment.app.Fragment
17import androidx.fragment.app.activityViewModels 17import androidx.fragment.app.activityViewModels
18import androidx.lifecycle.Lifecycle
19import androidx.lifecycle.lifecycleScope
20import androidx.lifecycle.repeatOnLifecycle
21import androidx.recyclerview.widget.LinearLayoutManager 18import androidx.recyclerview.widget.LinearLayoutManager
22import com.google.android.material.divider.MaterialDividerItemDecoration 19import com.google.android.material.divider.MaterialDividerItemDecoration
23import com.google.android.material.transition.MaterialSharedAxis 20import com.google.android.material.transition.MaterialSharedAxis
24import info.debatty.java.stringsimilarity.Cosine 21import info.debatty.java.stringsimilarity.Cosine
25import kotlinx.coroutines.launch
26import org.yuzu.yuzu_emu.R 22import org.yuzu.yuzu_emu.R
27import org.yuzu.yuzu_emu.databinding.FragmentSettingsSearchBinding 23import org.yuzu.yuzu_emu.databinding.FragmentSettingsSearchBinding
28import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem 24import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem
29import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter
30import org.yuzu.yuzu_emu.model.SettingsViewModel
31import org.yuzu.yuzu_emu.utils.NativeConfig 25import org.yuzu.yuzu_emu.utils.NativeConfig
26import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
32import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins 27import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
28import org.yuzu.yuzu_emu.utils.collect
33 29
34class SettingsSearchFragment : Fragment() { 30class SettingsSearchFragment : Fragment() {
35 private var _binding: FragmentSettingsSearchBinding? = null 31 private var _binding: FragmentSettingsSearchBinding? = null
@@ -85,14 +81,10 @@ class SettingsSearchFragment : Fragment() {
85 search() 81 search()
86 binding.settingsList.smoothScrollToPosition(0) 82 binding.settingsList.smoothScrollToPosition(0)
87 } 83 }
88 viewLifecycleOwner.lifecycleScope.launch { 84 settingsViewModel.shouldReloadSettingsList.collect(viewLifecycleOwner) {
89 repeatOnLifecycle(Lifecycle.State.CREATED) { 85 if (it) {
90 settingsViewModel.shouldReloadSettingsList.collect { 86 settingsViewModel.setShouldReloadSettingsList(false)
91 if (it) { 87 search()
92 settingsViewModel.setShouldReloadSettingsList(false)
93 search()
94 }
95 }
96 } 88 }
97 } 89 }
98 90
@@ -108,10 +100,9 @@ class SettingsSearchFragment : Fragment() {
108 100
109 private fun search() { 101 private fun search() {
110 val searchTerm = binding.searchText.text.toString().lowercase() 102 val searchTerm = binding.searchText.text.toString().lowercase()
111 binding.clearButton.visibility = 103 binding.clearButton.setVisible(visible = searchTerm.isNotEmpty(), gone = false)
112 if (searchTerm.isEmpty()) View.INVISIBLE else View.VISIBLE
113 if (searchTerm.isEmpty()) { 104 if (searchTerm.isEmpty()) {
114 binding.noResultsView.visibility = View.VISIBLE 105 binding.noResultsView.setVisible(visible = false, gone = false)
115 settingsAdapter?.submitList(emptyList()) 106 settingsAdapter?.submitList(emptyList())
116 return 107 return
117 } 108 }
@@ -119,7 +110,7 @@ class SettingsSearchFragment : Fragment() {
119 val baseList = SettingsItem.settingsItems 110 val baseList = SettingsItem.settingsItems
120 val similarityAlgorithm = if (searchTerm.length > 2) Cosine() else Cosine(1) 111 val similarityAlgorithm = if (searchTerm.length > 2) Cosine() else Cosine(1)
121 val sortedList: List<SettingsItem> = baseList.mapNotNull { item -> 112 val sortedList: List<SettingsItem> = baseList.mapNotNull { item ->
122 val title = getString(item.value.nameId).lowercase() 113 val title = item.value.title.lowercase()
123 val similarity = similarityAlgorithm.similarity(searchTerm, title) 114 val similarity = similarityAlgorithm.similarity(searchTerm, title)
124 if (similarity > 0.08) { 115 if (similarity > 0.08) {
125 Pair(similarity, item) 116 Pair(similarity, item)
@@ -138,8 +129,7 @@ class SettingsSearchFragment : Fragment() {
138 optionalSetting 129 optionalSetting
139 } 130 }
140 settingsAdapter?.submitList(sortedList) 131 settingsAdapter?.submitList(sortedList)
141 binding.noResultsView.visibility = 132 binding.noResultsView.setVisible(visible = sortedList.isEmpty(), gone = false)
142 if (sortedList.isEmpty()) View.VISIBLE else View.INVISIBLE
143 } 133 }
144 134
145 private fun focusSearch() { 135 private fun focusSearch() {
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/SettingsViewModel.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsViewModel.kt
index 5cb6a5d57..fbdca04e9 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/SettingsViewModel.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsViewModel.kt
@@ -1,20 +1,26 @@
1// SPDX-FileCopyrightText: 2023 yuzu Emulator Project 1// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4package org.yuzu.yuzu_emu.model 4package org.yuzu.yuzu_emu.features.settings.ui
5 5
6import androidx.lifecycle.ViewModel 6import androidx.lifecycle.ViewModel
7import kotlinx.coroutines.flow.MutableStateFlow 7import kotlinx.coroutines.flow.MutableStateFlow
8import kotlinx.coroutines.flow.StateFlow 8import kotlinx.coroutines.flow.StateFlow
9import kotlinx.coroutines.flow.asStateFlow
9import org.yuzu.yuzu_emu.R 10import org.yuzu.yuzu_emu.R
10import org.yuzu.yuzu_emu.YuzuApplication 11import org.yuzu.yuzu_emu.YuzuApplication
11import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem 12import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem
13import org.yuzu.yuzu_emu.model.Game
14import org.yuzu.yuzu_emu.utils.InputHandler
15import org.yuzu.yuzu_emu.utils.ParamPackage
12 16
13class SettingsViewModel : ViewModel() { 17class SettingsViewModel : ViewModel() {
14 var game: Game? = null 18 var game: Game? = null
15 19
16 var clickedItem: SettingsItem? = null 20 var clickedItem: SettingsItem? = null
17 21
22 var currentDevice = 0
23
18 val shouldRecreate: StateFlow<Boolean> get() = _shouldRecreate 24 val shouldRecreate: StateFlow<Boolean> get() = _shouldRecreate
19 private val _shouldRecreate = MutableStateFlow(false) 25 private val _shouldRecreate = MutableStateFlow(false)
20 26
@@ -36,6 +42,18 @@ class SettingsViewModel : ViewModel() {
36 val adapterItemChanged: StateFlow<Int> get() = _adapterItemChanged 42 val adapterItemChanged: StateFlow<Int> get() = _adapterItemChanged
37 private val _adapterItemChanged = MutableStateFlow(-1) 43 private val _adapterItemChanged = MutableStateFlow(-1)
38 44
45 private val _datasetChanged = MutableStateFlow(false)
46 val datasetChanged = _datasetChanged.asStateFlow()
47
48 private val _reloadListAndNotifyDataset = MutableStateFlow(false)
49 val reloadListAndNotifyDataset = _reloadListAndNotifyDataset.asStateFlow()
50
51 private val _shouldShowDeleteProfileDialog = MutableStateFlow("")
52 val shouldShowDeleteProfileDialog = _shouldShowDeleteProfileDialog.asStateFlow()
53
54 private val _shouldShowResetInputDialog = MutableStateFlow(false)
55 val shouldShowResetInputDialog = _shouldShowResetInputDialog.asStateFlow()
56
39 fun setShouldRecreate(value: Boolean) { 57 fun setShouldRecreate(value: Boolean) {
40 _shouldRecreate.value = value 58 _shouldRecreate.value = value
41 } 59 }
@@ -68,4 +86,27 @@ class SettingsViewModel : ViewModel() {
68 fun setAdapterItemChanged(value: Int) { 86 fun setAdapterItemChanged(value: Int) {
69 _adapterItemChanged.value = value 87 _adapterItemChanged.value = value
70 } 88 }
89
90 fun setDatasetChanged(value: Boolean) {
91 _datasetChanged.value = value
92 }
93
94 fun setReloadListAndNotifyDataset(value: Boolean) {
95 _reloadListAndNotifyDataset.value = value
96 }
97
98 fun setShouldShowDeleteProfileDialog(profile: String) {
99 _shouldShowDeleteProfileDialog.value = profile
100 }
101
102 fun setShouldShowResetInputDialog(value: Boolean) {
103 _shouldShowResetInputDialog.value = value
104 }
105
106 fun getCurrentDeviceParams(defaultParams: ParamPackage): ParamPackage =
107 try {
108 InputHandler.registeredControllers[currentDevice]
109 } catch (e: IndexOutOfBoundsException) {
110 defaultParams
111 }
71} 112}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/DateTimeViewHolder.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/DateTimeViewHolder.kt
index 5ad0899dd..367db7fd2 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/DateTimeViewHolder.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/DateTimeViewHolder.kt
@@ -14,6 +14,7 @@ import org.yuzu.yuzu_emu.features.settings.model.view.DateTimeSetting
14import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem 14import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem
15import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter 15import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter
16import org.yuzu.yuzu_emu.utils.NativeConfig 16import org.yuzu.yuzu_emu.utils.NativeConfig
17import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
17 18
18class DateTimeViewHolder(val binding: ListItemSettingBinding, adapter: SettingsAdapter) : 19class DateTimeViewHolder(val binding: ListItemSettingBinding, adapter: SettingsAdapter) :
19 SettingViewHolder(binding.root, adapter) { 20 SettingViewHolder(binding.root, adapter) {
@@ -21,28 +22,19 @@ class DateTimeViewHolder(val binding: ListItemSettingBinding, adapter: SettingsA
21 22
22 override fun bind(item: SettingsItem) { 23 override fun bind(item: SettingsItem) {
23 setting = item as DateTimeSetting 24 setting = item as DateTimeSetting
24 binding.textSettingName.setText(item.nameId) 25 binding.textSettingName.text = item.title
25 if (item.descriptionId != 0) { 26 binding.textSettingDescription.setVisible(item.description.isNotEmpty())
26 binding.textSettingDescription.setText(item.descriptionId) 27 binding.textSettingDescription.text = item.description
27 binding.textSettingDescription.visibility = View.VISIBLE 28 binding.textSettingValue.setVisible(true)
28 } else {
29 binding.textSettingDescription.visibility = View.GONE
30 }
31
32 binding.textSettingValue.visibility = View.VISIBLE
33 val epochTime = setting.getValue() 29 val epochTime = setting.getValue()
34 val instant = Instant.ofEpochMilli(epochTime * 1000) 30 val instant = Instant.ofEpochMilli(epochTime * 1000)
35 val zonedTime = ZonedDateTime.ofInstant(instant, ZoneId.of("UTC")) 31 val zonedTime = ZonedDateTime.ofInstant(instant, ZoneId.of("UTC"))
36 val dateFormatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM) 32 val dateFormatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM)
37 binding.textSettingValue.text = dateFormatter.format(zonedTime) 33 binding.textSettingValue.text = dateFormatter.format(zonedTime)
38 34
39 binding.buttonClear.visibility = if (setting.setting.global || 35 binding.buttonClear.setVisible(
40 !NativeConfig.isPerGameConfigLoaded() 36 !setting.setting.global || NativeConfig.isPerGameConfigLoaded()
41 ) { 37 )
42 View.GONE
43 } else {
44 View.VISIBLE
45 }
46 binding.buttonClear.setOnClickListener { 38 binding.buttonClear.setOnClickListener {
47 adapter.onClearClick(setting, bindingAdapterPosition) 39 adapter.onClearClick(setting, bindingAdapterPosition)
48 } 40 }
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/HeaderViewHolder.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/HeaderViewHolder.kt
index f5bcf705c..0815c36e2 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/HeaderViewHolder.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/HeaderViewHolder.kt
@@ -16,7 +16,7 @@ class HeaderViewHolder(val binding: ListItemSettingsHeaderBinding, adapter: Sett
16 } 16 }
17 17
18 override fun bind(item: SettingsItem) { 18 override fun bind(item: SettingsItem) {
19 binding.textHeaderName.setText(item.nameId) 19 binding.textHeaderName.text = item.title
20 } 20 }
21 21
22 override fun onClick(clicked: View) { 22 override fun onClick(clicked: View) {
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/InputProfileViewHolder.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/InputProfileViewHolder.kt
new file mode 100644
index 000000000..86af3a226
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/InputProfileViewHolder.kt
@@ -0,0 +1,34 @@
1// SPDX-FileCopyrightText: 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4package org.yuzu.yuzu_emu.features.settings.ui.viewholder
5
6import android.view.View
7import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding
8import org.yuzu.yuzu_emu.features.settings.model.view.InputProfileSetting
9import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem
10import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter
11import org.yuzu.yuzu_emu.R
12import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
13
14class InputProfileViewHolder(val binding: ListItemSettingBinding, adapter: SettingsAdapter) :
15 SettingViewHolder(binding.root, adapter) {
16 private lateinit var setting: InputProfileSetting
17
18 override fun bind(item: SettingsItem) {
19 setting = item as InputProfileSetting
20 binding.textSettingName.text = setting.title
21 binding.textSettingValue.text =
22 setting.getCurrentProfile().ifEmpty { binding.root.context.getString(R.string.not_set) }
23
24 binding.textSettingDescription.setVisible(false)
25 binding.buttonClear.setVisible(false)
26 binding.icon.setVisible(false)
27 binding.buttonClear.setVisible(false)
28 }
29
30 override fun onClick(clicked: View) =
31 adapter.onInputProfileClick(setting, bindingAdapterPosition)
32
33 override fun onLongClick(clicked: View): Boolean = false
34}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/InputViewHolder.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/InputViewHolder.kt
new file mode 100644
index 000000000..9d9047804
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/InputViewHolder.kt
@@ -0,0 +1,60 @@
1// SPDX-FileCopyrightText: 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4package org.yuzu.yuzu_emu.features.settings.ui.viewholder
5
6import android.view.View
7import org.yuzu.yuzu_emu.databinding.ListItemSettingInputBinding
8import org.yuzu.yuzu_emu.features.input.NativeInput
9import org.yuzu.yuzu_emu.features.settings.model.view.AnalogInputSetting
10import org.yuzu.yuzu_emu.features.settings.model.view.ButtonInputSetting
11import org.yuzu.yuzu_emu.features.settings.model.view.InputSetting
12import org.yuzu.yuzu_emu.features.settings.model.view.ModifierInputSetting
13import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem
14import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter
15import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
16
17class InputViewHolder(val binding: ListItemSettingInputBinding, adapter: SettingsAdapter) :
18 SettingViewHolder(binding.root, adapter) {
19 private lateinit var setting: InputSetting
20
21 override fun bind(item: SettingsItem) {
22 setting = item as InputSetting
23 binding.textSettingName.text = setting.title
24 binding.textSettingValue.text = setting.getSelectedValue()
25
26 when (item) {
27 is AnalogInputSetting -> {
28 val param = NativeInput.getStickParam(item.playerIndex, item.nativeAnalog)
29 binding.buttonOptions.setVisible(
30 param.get("engine", "") == "analog_from_button" ||
31 param.has("axis_x") || param.has("axis_y")
32 )
33 }
34
35 is ButtonInputSetting -> {
36 val param = NativeInput.getButtonParam(item.playerIndex, item.nativeButton)
37 binding.buttonOptions.setVisible(
38 param.has("code") || param.has("button") || param.has("hat") ||
39 param.has("axis")
40 )
41 }
42
43 is ModifierInputSetting -> {
44 val params = NativeInput.getStickParam(item.playerIndex, item.nativeAnalog)
45 binding.buttonOptions.setVisible(params.has("modifier"))
46 }
47 }
48
49 binding.buttonOptions.setOnClickListener(null)
50 binding.buttonOptions.setOnClickListener {
51 adapter.onInputOptionsClick(binding.buttonOptions, setting, bindingAdapterPosition)
52 }
53 }
54
55 override fun onClick(clicked: View) =
56 adapter.onInputClick(setting, bindingAdapterPosition)
57
58 override fun onLongClick(clicked: View): Boolean =
59 adapter.onLongClick(setting, bindingAdapterPosition)
60}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/RunnableViewHolder.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/RunnableViewHolder.kt
index 507184238..fc2ffb515 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/RunnableViewHolder.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/RunnableViewHolder.kt
@@ -5,11 +5,11 @@ package org.yuzu.yuzu_emu.features.settings.ui.viewholder
5 5
6import android.view.View 6import android.view.View
7import androidx.core.content.res.ResourcesCompat 7import androidx.core.content.res.ResourcesCompat
8import org.yuzu.yuzu_emu.NativeLibrary
9import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding 8import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding
10import org.yuzu.yuzu_emu.features.settings.model.view.RunnableSetting 9import org.yuzu.yuzu_emu.features.settings.model.view.RunnableSetting
11import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem 10import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem
12import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter 11import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter
12import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
13 13
14class RunnableViewHolder(val binding: ListItemSettingBinding, adapter: SettingsAdapter) : 14class RunnableViewHolder(val binding: ListItemSettingBinding, adapter: SettingsAdapter) :
15 SettingViewHolder(binding.root, adapter) { 15 SettingViewHolder(binding.root, adapter) {
@@ -17,34 +17,28 @@ class RunnableViewHolder(val binding: ListItemSettingBinding, adapter: SettingsA
17 17
18 override fun bind(item: SettingsItem) { 18 override fun bind(item: SettingsItem) {
19 setting = item as RunnableSetting 19 setting = item as RunnableSetting
20 if (item.iconId != 0) { 20 binding.icon.setVisible(setting.iconId != 0)
21 binding.icon.visibility = View.VISIBLE 21 if (setting.iconId != 0) {
22 binding.icon.setImageDrawable( 22 binding.icon.setImageDrawable(
23 ResourcesCompat.getDrawable( 23 ResourcesCompat.getDrawable(
24 binding.icon.resources, 24 binding.icon.resources,
25 item.iconId, 25 setting.iconId,
26 binding.icon.context.theme 26 binding.icon.context.theme
27 ) 27 )
28 ) 28 )
29 } else {
30 binding.icon.visibility = View.GONE
31 } 29 }
32 30
33 binding.textSettingName.setText(item.nameId) 31 binding.textSettingName.text = setting.title
34 if (item.descriptionId != 0) { 32 binding.textSettingDescription.setVisible(setting.description.isNotEmpty())
35 binding.textSettingDescription.setText(item.descriptionId) 33 binding.textSettingDescription.text = item.description
36 binding.textSettingDescription.visibility = View.VISIBLE 34 binding.textSettingValue.setVisible(false)
37 } else { 35 binding.buttonClear.setVisible(false)
38 binding.textSettingDescription.visibility = View.GONE
39 }
40 binding.textSettingValue.visibility = View.GONE
41 binding.buttonClear.visibility = View.GONE
42 36
43 setStyle(setting.isEditable, binding) 37 setStyle(setting.isEditable, binding)
44 } 38 }
45 39
46 override fun onClick(clicked: View) { 40 override fun onClick(clicked: View) {
47 if (!setting.isRuntimeRunnable && !NativeLibrary.isRunning()) { 41 if (setting.isRunnable) {
48 setting.runnable.invoke() 42 setting.runnable.invoke()
49 } 43 }
50 } 44 }
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SingleChoiceViewHolder.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SingleChoiceViewHolder.kt
index 02dab3785..e2fe0b072 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SingleChoiceViewHolder.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SingleChoiceViewHolder.kt
@@ -5,11 +5,13 @@ package org.yuzu.yuzu_emu.features.settings.ui.viewholder
5 5
6import android.view.View 6import android.view.View
7import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding 7import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding
8import org.yuzu.yuzu_emu.features.settings.model.view.IntSingleChoiceSetting
8import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem 9import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem
9import org.yuzu.yuzu_emu.features.settings.model.view.SingleChoiceSetting 10import org.yuzu.yuzu_emu.features.settings.model.view.SingleChoiceSetting
10import org.yuzu.yuzu_emu.features.settings.model.view.StringSingleChoiceSetting 11import org.yuzu.yuzu_emu.features.settings.model.view.StringSingleChoiceSetting
11import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter 12import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter
12import org.yuzu.yuzu_emu.utils.NativeConfig 13import org.yuzu.yuzu_emu.utils.NativeConfig
14import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
13 15
14class SingleChoiceViewHolder(val binding: ListItemSettingBinding, adapter: SettingsAdapter) : 16class SingleChoiceViewHolder(val binding: ListItemSettingBinding, adapter: SettingsAdapter) :
15 SettingViewHolder(binding.root, adapter) { 17 SettingViewHolder(binding.root, adapter) {
@@ -17,40 +19,38 @@ class SingleChoiceViewHolder(val binding: ListItemSettingBinding, adapter: Setti
17 19
18 override fun bind(item: SettingsItem) { 20 override fun bind(item: SettingsItem) {
19 setting = item 21 setting = item
20 binding.textSettingName.setText(item.nameId) 22 binding.textSettingName.text = setting.title
21 if (item.descriptionId != 0) { 23 binding.textSettingDescription.setVisible(item.description.isNotEmpty())
22 binding.textSettingDescription.setText(item.descriptionId) 24 binding.textSettingDescription.text = item.description
23 binding.textSettingDescription.visibility = View.VISIBLE
24 } else {
25 binding.textSettingDescription.visibility = View.GONE
26 }
27 25
28 binding.textSettingValue.visibility = View.VISIBLE 26 binding.textSettingValue.setVisible(true)
29 if (item is SingleChoiceSetting) { 27 when (item) {
30 val resMgr = binding.textSettingValue.context.resources 28 is SingleChoiceSetting -> {
31 val values = resMgr.getIntArray(item.valuesId) 29 val resMgr = binding.textSettingValue.context.resources
32 for (i in values.indices) { 30 val values = resMgr.getIntArray(item.valuesId)
33 if (values[i] == item.getSelectedValue()) { 31 for (i in values.indices) {
34 binding.textSettingValue.text = resMgr.getStringArray(item.choicesId)[i] 32 if (values[i] == item.getSelectedValue()) {
35 break 33 binding.textSettingValue.text = resMgr.getStringArray(item.choicesId)[i]
34 break
35 }
36 } 36 }
37 } 37 }
38 } else if (item is StringSingleChoiceSetting) { 38
39 for (i in item.values.indices) { 39 is StringSingleChoiceSetting -> {
40 if (item.values[i] == item.getSelectedValue()) { 40 binding.textSettingValue.text = item.getSelectedValue()
41 binding.textSettingValue.text = item.choices[i]
42 break
43 }
44 } 41 }
45 }
46 42
47 binding.buttonClear.visibility = if (setting.setting.global || 43 is IntSingleChoiceSetting -> {
48 !NativeConfig.isPerGameConfigLoaded() 44 binding.textSettingValue.text = item.getChoiceAt(item.getSelectedValue())
49 ) { 45 }
50 View.GONE 46 }
51 } else { 47 if (binding.textSettingValue.text.isEmpty()) {
52 View.VISIBLE 48 binding.textSettingValue.setVisible(false)
53 } 49 }
50
51 binding.buttonClear.setVisible(
52 !setting.setting.global || NativeConfig.isPerGameConfigLoaded()
53 )
54 binding.buttonClear.setOnClickListener { 54 binding.buttonClear.setOnClickListener {
55 adapter.onClearClick(setting, bindingAdapterPosition) 55 adapter.onClearClick(setting, bindingAdapterPosition)
56 } 56 }
@@ -63,16 +63,25 @@ class SingleChoiceViewHolder(val binding: ListItemSettingBinding, adapter: Setti
63 return 63 return
64 } 64 }
65 65
66 if (setting is SingleChoiceSetting) { 66 when (setting) {
67 adapter.onSingleChoiceClick( 67 is SingleChoiceSetting -> adapter.onSingleChoiceClick(
68 (setting as SingleChoiceSetting), 68 setting as SingleChoiceSetting,
69 bindingAdapterPosition
70 )
71 } else if (setting is StringSingleChoiceSetting) {
72 adapter.onStringSingleChoiceClick(
73 (setting as StringSingleChoiceSetting),
74 bindingAdapterPosition 69 bindingAdapterPosition
75 ) 70 )
71
72 is StringSingleChoiceSetting -> {
73 adapter.onStringSingleChoiceClick(
74 setting as StringSingleChoiceSetting,
75 bindingAdapterPosition
76 )
77 }
78
79 is IntSingleChoiceSetting -> {
80 adapter.onIntSingleChoiceClick(
81 setting as IntSingleChoiceSetting,
82 bindingAdapterPosition
83 )
84 }
76 } 85 }
77 } 86 }
78 87
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SliderViewHolder.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SliderViewHolder.kt
index 596c18012..a37b59b44 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SliderViewHolder.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SliderViewHolder.kt
@@ -10,6 +10,7 @@ import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem
10import org.yuzu.yuzu_emu.features.settings.model.view.SliderSetting 10import org.yuzu.yuzu_emu.features.settings.model.view.SliderSetting
11import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter 11import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter
12import org.yuzu.yuzu_emu.utils.NativeConfig 12import org.yuzu.yuzu_emu.utils.NativeConfig
13import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
13 14
14class SliderViewHolder(val binding: ListItemSettingBinding, adapter: SettingsAdapter) : 15class SliderViewHolder(val binding: ListItemSettingBinding, adapter: SettingsAdapter) :
15 SettingViewHolder(binding.root, adapter) { 16 SettingViewHolder(binding.root, adapter) {
@@ -17,27 +18,19 @@ class SliderViewHolder(val binding: ListItemSettingBinding, adapter: SettingsAda
17 18
18 override fun bind(item: SettingsItem) { 19 override fun bind(item: SettingsItem) {
19 setting = item as SliderSetting 20 setting = item as SliderSetting
20 binding.textSettingName.setText(item.nameId) 21 binding.textSettingName.text = setting.title
21 if (item.descriptionId != 0) { 22 binding.textSettingDescription.setVisible(item.description.isNotEmpty())
22 binding.textSettingDescription.setText(item.descriptionId) 23 binding.textSettingDescription.text = setting.description
23 binding.textSettingDescription.visibility = View.VISIBLE 24 binding.textSettingValue.setVisible(true)
24 } else {
25 binding.textSettingDescription.visibility = View.GONE
26 }
27 binding.textSettingValue.visibility = View.VISIBLE
28 binding.textSettingValue.text = String.format( 25 binding.textSettingValue.text = String.format(
29 binding.textSettingValue.context.getString(R.string.value_with_units), 26 binding.textSettingValue.context.getString(R.string.value_with_units),
30 setting.getSelectedValue(), 27 setting.getSelectedValue(),
31 setting.units 28 setting.units
32 ) 29 )
33 30
34 binding.buttonClear.visibility = if (setting.setting.global || 31 binding.buttonClear.setVisible(
35 !NativeConfig.isPerGameConfigLoaded() 32 !setting.setting.global || NativeConfig.isPerGameConfigLoaded()
36 ) { 33 )
37 View.GONE
38 } else {
39 View.VISIBLE
40 }
41 binding.buttonClear.setOnClickListener { 34 binding.buttonClear.setOnClickListener {
42 adapter.onClearClick(setting, bindingAdapterPosition) 35 adapter.onClearClick(setting, bindingAdapterPosition)
43 } 36 }
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SubmenuViewHolder.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SubmenuViewHolder.kt
index 20d35a17d..f7a9c08c3 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SubmenuViewHolder.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SubmenuViewHolder.kt
@@ -9,39 +9,34 @@ import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding
9import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem 9import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem
10import org.yuzu.yuzu_emu.features.settings.model.view.SubmenuSetting 10import org.yuzu.yuzu_emu.features.settings.model.view.SubmenuSetting
11import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter 11import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter
12import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
12 13
13class SubmenuViewHolder(val binding: ListItemSettingBinding, adapter: SettingsAdapter) : 14class SubmenuViewHolder(val binding: ListItemSettingBinding, adapter: SettingsAdapter) :
14 SettingViewHolder(binding.root, adapter) { 15 SettingViewHolder(binding.root, adapter) {
15 private lateinit var item: SubmenuSetting 16 private lateinit var setting: SubmenuSetting
16 17
17 override fun bind(item: SettingsItem) { 18 override fun bind(item: SettingsItem) {
18 this.item = item as SubmenuSetting 19 setting = item as SubmenuSetting
19 if (item.iconId != 0) { 20 binding.icon.setVisible(setting.iconId != 0)
20 binding.icon.visibility = View.VISIBLE 21 if (setting.iconId != 0) {
21 binding.icon.setImageDrawable( 22 binding.icon.setImageDrawable(
22 ResourcesCompat.getDrawable( 23 ResourcesCompat.getDrawable(
23 binding.icon.resources, 24 binding.icon.resources,
24 item.iconId, 25 setting.iconId,
25 binding.icon.context.theme 26 binding.icon.context.theme
26 ) 27 )
27 ) 28 )
28 } else {
29 binding.icon.visibility = View.GONE
30 } 29 }
31 30
32 binding.textSettingName.setText(item.nameId) 31 binding.textSettingName.text = setting.title
33 if (item.descriptionId != 0) { 32 binding.textSettingDescription.setVisible(setting.description.isNotEmpty())
34 binding.textSettingDescription.setText(item.descriptionId) 33 binding.textSettingDescription.text = setting.description
35 binding.textSettingDescription.visibility = View.VISIBLE 34 binding.textSettingValue.setVisible(false)
36 } else { 35 binding.buttonClear.setVisible(false)
37 binding.textSettingDescription.visibility = View.GONE
38 }
39 binding.textSettingValue.visibility = View.GONE
40 binding.buttonClear.visibility = View.GONE
41 } 36 }
42 37
43 override fun onClick(clicked: View) { 38 override fun onClick(clicked: View) {
44 adapter.onSubmenuClick(item) 39 adapter.onSubmenuClick(setting)
45 } 40 }
46 41
47 override fun onLongClick(clicked: View): Boolean { 42 override fun onLongClick(clicked: View): Boolean {
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SwitchSettingViewHolder.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SwitchSettingViewHolder.kt
index d26bf9374..53f7b301f 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SwitchSettingViewHolder.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SwitchSettingViewHolder.kt
@@ -10,6 +10,7 @@ import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem
10import org.yuzu.yuzu_emu.features.settings.model.view.SwitchSetting 10import org.yuzu.yuzu_emu.features.settings.model.view.SwitchSetting
11import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter 11import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter
12import org.yuzu.yuzu_emu.utils.NativeConfig 12import org.yuzu.yuzu_emu.utils.NativeConfig
13import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
13 14
14class SwitchSettingViewHolder(val binding: ListItemSettingSwitchBinding, adapter: SettingsAdapter) : 15class SwitchSettingViewHolder(val binding: ListItemSettingSwitchBinding, adapter: SettingsAdapter) :
15 SettingViewHolder(binding.root, adapter) { 16 SettingViewHolder(binding.root, adapter) {
@@ -18,28 +19,19 @@ class SwitchSettingViewHolder(val binding: ListItemSettingSwitchBinding, adapter
18 19
19 override fun bind(item: SettingsItem) { 20 override fun bind(item: SettingsItem) {
20 setting = item as SwitchSetting 21 setting = item as SwitchSetting
21 binding.textSettingName.setText(item.nameId) 22 binding.textSettingName.text = setting.title
22 if (item.descriptionId != 0) { 23 binding.textSettingDescription.setVisible(setting.description.isNotEmpty())
23 binding.textSettingDescription.setText(item.descriptionId) 24 binding.textSettingDescription.text = setting.description
24 binding.textSettingDescription.visibility = View.VISIBLE
25 } else {
26 binding.textSettingDescription.text = ""
27 binding.textSettingDescription.visibility = View.GONE
28 }
29 25
30 binding.switchWidget.setOnCheckedChangeListener(null) 26 binding.switchWidget.setOnCheckedChangeListener(null)
31 binding.switchWidget.isChecked = setting.getIsChecked(setting.needsRuntimeGlobal) 27 binding.switchWidget.isChecked = setting.getIsChecked(setting.needsRuntimeGlobal)
32 binding.switchWidget.setOnCheckedChangeListener { _: CompoundButton, _: Boolean -> 28 binding.switchWidget.setOnCheckedChangeListener { _: CompoundButton, _: Boolean ->
33 adapter.onBooleanClick(item, binding.switchWidget.isChecked, bindingAdapterPosition) 29 adapter.onBooleanClick(setting, binding.switchWidget.isChecked, bindingAdapterPosition)
34 } 30 }
35 31
36 binding.buttonClear.visibility = if (setting.setting.global || 32 binding.buttonClear.setVisible(
37 !NativeConfig.isPerGameConfigLoaded() 33 !setting.setting.global || NativeConfig.isPerGameConfigLoaded()
38 ) { 34 )
39 View.GONE
40 } else {
41 View.VISIBLE
42 }
43 binding.buttonClear.setOnClickListener { 35 binding.buttonClear.setOnClickListener {
44 adapter.onClearClick(setting, bindingAdapterPosition) 36 adapter.onClearClick(setting, bindingAdapterPosition)
45 } 37 }
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AddonsFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AddonsFragment.kt
index 872553ac4..110aa2960 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AddonsFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AddonsFragment.kt
@@ -3,7 +3,6 @@
3 3
4package org.yuzu.yuzu_emu.fragments 4package org.yuzu.yuzu_emu.fragments
5 5
6import android.annotation.SuppressLint
7import android.content.Intent 6import android.content.Intent
8import android.os.Bundle 7import android.os.Bundle
9import android.view.LayoutInflater 8import android.view.LayoutInflater
@@ -16,9 +15,6 @@ import androidx.core.view.updatePadding
16import androidx.documentfile.provider.DocumentFile 15import androidx.documentfile.provider.DocumentFile
17import androidx.fragment.app.Fragment 16import androidx.fragment.app.Fragment
18import androidx.fragment.app.activityViewModels 17import androidx.fragment.app.activityViewModels
19import androidx.lifecycle.Lifecycle
20import androidx.lifecycle.lifecycleScope
21import androidx.lifecycle.repeatOnLifecycle
22import androidx.navigation.findNavController 18import androidx.navigation.findNavController
23import androidx.navigation.fragment.navArgs 19import androidx.navigation.fragment.navArgs
24import androidx.recyclerview.widget.LinearLayoutManager 20import androidx.recyclerview.widget.LinearLayoutManager
@@ -32,6 +28,7 @@ import org.yuzu.yuzu_emu.model.HomeViewModel
32import org.yuzu.yuzu_emu.utils.AddonUtil 28import org.yuzu.yuzu_emu.utils.AddonUtil
33import org.yuzu.yuzu_emu.utils.FileUtil.copyFilesTo 29import org.yuzu.yuzu_emu.utils.FileUtil.copyFilesTo
34import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins 30import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
31import org.yuzu.yuzu_emu.utils.collect
35import java.io.File 32import java.io.File
36 33
37class AddonsFragment : Fragment() { 34class AddonsFragment : Fragment() {
@@ -60,8 +57,6 @@ class AddonsFragment : Fragment() {
60 return binding.root 57 return binding.root
61 } 58 }
62 59
63 // This is using the correct scope, lint is just acting up
64 @SuppressLint("UnsafeRepeatOnLifecycleDetector")
65 override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 60 override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
66 super.onViewCreated(view, savedInstanceState) 61 super.onViewCreated(view, savedInstanceState)
67 homeViewModel.setNavigationVisibility(visible = false, animated = false) 62 homeViewModel.setNavigationVisibility(visible = false, animated = false)
@@ -78,57 +73,41 @@ class AddonsFragment : Fragment() {
78 adapter = AddonAdapter(addonViewModel) 73 adapter = AddonAdapter(addonViewModel)
79 } 74 }
80 75
81 viewLifecycleOwner.lifecycleScope.apply { 76 addonViewModel.addonList.collect(viewLifecycleOwner) {
82 launch { 77 (binding.listAddons.adapter as AddonAdapter).submitList(it)
83 repeatOnLifecycle(Lifecycle.State.STARTED) { 78 }
84 addonViewModel.addonList.collect { 79 addonViewModel.showModInstallPicker.collect(
85 (binding.listAddons.adapter as AddonAdapter).submitList(it) 80 viewLifecycleOwner,
86 } 81 resetState = { addonViewModel.showModInstallPicker(false) }
87 } 82 ) { if (it) installAddon.launch(Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).data) }
88 } 83 addonViewModel.showModNoticeDialog.collect(
89 launch { 84 viewLifecycleOwner,
90 repeatOnLifecycle(Lifecycle.State.STARTED) { 85 resetState = { addonViewModel.showModNoticeDialog(false) }
91 addonViewModel.showModInstallPicker.collect { 86 ) {
92 if (it) { 87 if (it) {
93 installAddon.launch(Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).data) 88 MessageDialogFragment.newInstance(
94 addonViewModel.showModInstallPicker(false) 89 requireActivity(),
95 } 90 titleId = R.string.addon_notice,
96 } 91 descriptionId = R.string.addon_notice_description,
97 } 92 dismissible = false,
98 } 93 positiveAction = { addonViewModel.showModInstallPicker(true) },
99 launch { 94 negativeAction = {},
100 repeatOnLifecycle(Lifecycle.State.STARTED) { 95 negativeButtonTitleId = R.string.close
101 addonViewModel.showModNoticeDialog.collect { 96 ).show(parentFragmentManager, MessageDialogFragment.TAG)
102 if (it) {
103 MessageDialogFragment.newInstance(
104 requireActivity(),
105 titleId = R.string.addon_notice,
106 descriptionId = R.string.addon_notice_description,
107 dismissible = false,
108 positiveAction = { addonViewModel.showModInstallPicker(true) },
109 negativeAction = {},
110 negativeButtonTitleId = R.string.close
111 ).show(parentFragmentManager, MessageDialogFragment.TAG)
112 addonViewModel.showModNoticeDialog(false)
113 }
114 }
115 }
116 } 97 }
117 launch { 98 }
118 repeatOnLifecycle(Lifecycle.State.STARTED) { 99 addonViewModel.addonToDelete.collect(
119 addonViewModel.addonToDelete.collect { 100 viewLifecycleOwner,
120 if (it != null) { 101 resetState = { addonViewModel.setAddonToDelete(null) }
121 MessageDialogFragment.newInstance( 102 ) {
122 requireActivity(), 103 if (it != null) {
123 titleId = R.string.confirm_uninstall, 104 MessageDialogFragment.newInstance(
124 descriptionId = R.string.confirm_uninstall_description, 105 requireActivity(),
125 positiveAction = { addonViewModel.onDeleteAddon(it) }, 106 titleId = R.string.confirm_uninstall,
126 negativeAction = {} 107 descriptionId = R.string.confirm_uninstall_description,
127 ).show(parentFragmentManager, MessageDialogFragment.TAG) 108 positiveAction = { addonViewModel.onDeleteAddon(it) },
128 addonViewModel.setAddonToDelete(null) 109 negativeAction = {}
129 } 110 ).show(parentFragmentManager, MessageDialogFragment.TAG)
130 }
131 }
132 } 111 }
133 } 112 }
134 113
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/CoreErrorDialogFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/CoreErrorDialogFragment.kt
new file mode 100644
index 000000000..299f8da71
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/CoreErrorDialogFragment.kt
@@ -0,0 +1,47 @@
1// SPDX-FileCopyrightText: 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4package org.yuzu.yuzu_emu.fragments
5
6import android.app.Dialog
7import android.content.DialogInterface
8import android.os.Bundle
9import androidx.fragment.app.DialogFragment
10import com.google.android.material.dialog.MaterialAlertDialogBuilder
11import org.yuzu.yuzu_emu.NativeLibrary
12import org.yuzu.yuzu_emu.R
13
14class CoreErrorDialogFragment : DialogFragment() {
15 override fun onCreateDialog(savedInstanceState: Bundle?): Dialog =
16 MaterialAlertDialogBuilder(requireActivity())
17 .setTitle(requireArguments().getString(TITLE))
18 .setMessage(requireArguments().getString(MESSAGE))
19 .setPositiveButton(R.string.continue_button, null)
20 .setNegativeButton(R.string.abort_button) { _: DialogInterface?, _: Int ->
21 NativeLibrary.coreErrorAlertResult = false
22 synchronized(NativeLibrary.coreErrorAlertLock) {
23 NativeLibrary.coreErrorAlertLock.notify()
24 }
25 }
26 .create()
27
28 override fun onDismiss(dialog: DialogInterface) {
29 super.onDismiss(dialog)
30 NativeLibrary.coreErrorAlertResult = true
31 synchronized(NativeLibrary.coreErrorAlertLock) { NativeLibrary.coreErrorAlertLock.notify() }
32 }
33
34 companion object {
35 const val TITLE = "Title"
36 const val MESSAGE = "Message"
37
38 fun newInstance(title: String, message: String): CoreErrorDialogFragment {
39 val frag = CoreErrorDialogFragment()
40 val args = Bundle()
41 args.putString(TITLE, title)
42 args.putString(MESSAGE, message)
43 frag.arguments = args
44 return frag
45 }
46 }
47}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/DriverManagerFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/DriverManagerFragment.kt
index 41cff46c1..8b23a1021 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/DriverManagerFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/DriverManagerFragment.kt
@@ -3,7 +3,6 @@
3 3
4package org.yuzu.yuzu_emu.fragments 4package org.yuzu.yuzu_emu.fragments
5 5
6import android.annotation.SuppressLint
7import android.os.Bundle 6import android.os.Bundle
8import android.view.LayoutInflater 7import android.view.LayoutInflater
9import android.view.View 8import android.view.View
@@ -14,9 +13,6 @@ import androidx.core.view.WindowInsetsCompat
14import androidx.core.view.updatePadding 13import androidx.core.view.updatePadding
15import androidx.fragment.app.Fragment 14import androidx.fragment.app.Fragment
16import androidx.fragment.app.activityViewModels 15import androidx.fragment.app.activityViewModels
17import androidx.lifecycle.Lifecycle
18import androidx.lifecycle.lifecycleScope
19import androidx.lifecycle.repeatOnLifecycle
20import androidx.navigation.findNavController 16import androidx.navigation.findNavController
21import androidx.navigation.fragment.navArgs 17import androidx.navigation.fragment.navArgs
22import androidx.recyclerview.widget.GridLayoutManager 18import androidx.recyclerview.widget.GridLayoutManager
@@ -35,6 +31,7 @@ import org.yuzu.yuzu_emu.utils.FileUtil
35import org.yuzu.yuzu_emu.utils.GpuDriverHelper 31import org.yuzu.yuzu_emu.utils.GpuDriverHelper
36import org.yuzu.yuzu_emu.utils.NativeConfig 32import org.yuzu.yuzu_emu.utils.NativeConfig
37import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins 33import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
34import org.yuzu.yuzu_emu.utils.collect
38import java.io.File 35import java.io.File
39import java.io.IOException 36import java.io.IOException
40 37
@@ -63,8 +60,6 @@ class DriverManagerFragment : Fragment() {
63 return binding.root 60 return binding.root
64 } 61 }
65 62
66 // This is using the correct scope, lint is just acting up
67 @SuppressLint("UnsafeRepeatOnLifecycleDetector")
68 override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 63 override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
69 super.onViewCreated(view, savedInstanceState) 64 super.onViewCreated(view, savedInstanceState)
70 homeViewModel.setNavigationVisibility(visible = false, animated = true) 65 homeViewModel.setNavigationVisibility(visible = false, animated = true)
@@ -89,15 +84,8 @@ class DriverManagerFragment : Fragment() {
89 } 84 }
90 } 85 }
91 86
92 viewLifecycleOwner.lifecycleScope.apply { 87 driverViewModel.showClearButton.collect(viewLifecycleOwner) {
93 launch { 88 binding.toolbarDrivers.menu.findItem(R.id.menu_driver_use_global).isVisible = it
94 repeatOnLifecycle(Lifecycle.State.STARTED) {
95 driverViewModel.showClearButton.collect {
96 binding.toolbarDrivers.menu
97 .findItem(R.id.menu_driver_use_global).isVisible = it
98 }
99 }
100 }
101 } 89 }
102 } 90 }
103 91
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/DriversLoadingDialogFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/DriversLoadingDialogFragment.kt
index 6a47b29f0..bad56e434 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/DriversLoadingDialogFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/DriversLoadingDialogFragment.kt
@@ -10,14 +10,11 @@ import android.view.View
10import android.view.ViewGroup 10import android.view.ViewGroup
11import androidx.fragment.app.DialogFragment 11import androidx.fragment.app.DialogFragment
12import androidx.fragment.app.activityViewModels 12import androidx.fragment.app.activityViewModels
13import androidx.lifecycle.Lifecycle
14import androidx.lifecycle.lifecycleScope
15import androidx.lifecycle.repeatOnLifecycle
16import com.google.android.material.dialog.MaterialAlertDialogBuilder 13import com.google.android.material.dialog.MaterialAlertDialogBuilder
17import kotlinx.coroutines.launch
18import org.yuzu.yuzu_emu.R 14import org.yuzu.yuzu_emu.R
19import org.yuzu.yuzu_emu.databinding.DialogProgressBarBinding 15import org.yuzu.yuzu_emu.databinding.DialogProgressBarBinding
20import org.yuzu.yuzu_emu.model.DriverViewModel 16import org.yuzu.yuzu_emu.model.DriverViewModel
17import org.yuzu.yuzu_emu.utils.collect
21 18
22class DriversLoadingDialogFragment : DialogFragment() { 19class DriversLoadingDialogFragment : DialogFragment() {
23 private val driverViewModel: DriverViewModel by activityViewModels() 20 private val driverViewModel: DriverViewModel by activityViewModels()
@@ -44,13 +41,7 @@ class DriversLoadingDialogFragment : DialogFragment() {
44 41
45 override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 42 override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
46 super.onViewCreated(view, savedInstanceState) 43 super.onViewCreated(view, savedInstanceState)
47 viewLifecycleOwner.lifecycleScope.apply { 44 driverViewModel.isInteractionAllowed.collect(viewLifecycleOwner) { if (it) dismiss() }
48 launch {
49 repeatOnLifecycle(Lifecycle.State.RESUMED) {
50 driverViewModel.isInteractionAllowed.collect { if (it) dismiss() }
51 }
52 }
53 }
54 } 45 }
55 46
56 companion object { 47 companion object {
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt
index 6b25cc525..bcc880e17 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt
@@ -32,9 +32,6 @@ import androidx.drawerlayout.widget.DrawerLayout
32import androidx.drawerlayout.widget.DrawerLayout.DrawerListener 32import androidx.drawerlayout.widget.DrawerLayout.DrawerListener
33import androidx.fragment.app.Fragment 33import androidx.fragment.app.Fragment
34import androidx.fragment.app.activityViewModels 34import androidx.fragment.app.activityViewModels
35import androidx.lifecycle.Lifecycle
36import androidx.lifecycle.lifecycleScope
37import androidx.lifecycle.repeatOnLifecycle
38import androidx.navigation.findNavController 35import androidx.navigation.findNavController
39import androidx.navigation.fragment.navArgs 36import androidx.navigation.fragment.navArgs
40import androidx.window.layout.FoldingFeature 37import androidx.window.layout.FoldingFeature
@@ -42,9 +39,6 @@ import androidx.window.layout.WindowInfoTracker
42import androidx.window.layout.WindowLayoutInfo 39import androidx.window.layout.WindowLayoutInfo
43import com.google.android.material.dialog.MaterialAlertDialogBuilder 40import com.google.android.material.dialog.MaterialAlertDialogBuilder
44import com.google.android.material.slider.Slider 41import com.google.android.material.slider.Slider
45import kotlinx.coroutines.Dispatchers
46import kotlinx.coroutines.flow.collectLatest
47import kotlinx.coroutines.launch
48import org.yuzu.yuzu_emu.HomeNavigationDirections 42import org.yuzu.yuzu_emu.HomeNavigationDirections
49import org.yuzu.yuzu_emu.NativeLibrary 43import org.yuzu.yuzu_emu.NativeLibrary
50import org.yuzu.yuzu_emu.R 44import org.yuzu.yuzu_emu.R
@@ -63,6 +57,7 @@ import org.yuzu.yuzu_emu.model.EmulationViewModel
63import org.yuzu.yuzu_emu.overlay.model.OverlayControl 57import org.yuzu.yuzu_emu.overlay.model.OverlayControl
64import org.yuzu.yuzu_emu.overlay.model.OverlayLayout 58import org.yuzu.yuzu_emu.overlay.model.OverlayLayout
65import org.yuzu.yuzu_emu.utils.* 59import org.yuzu.yuzu_emu.utils.*
60import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
66import java.lang.NullPointerException 61import java.lang.NullPointerException
67 62
68class EmulationFragment : Fragment(), SurfaceHolder.Callback { 63class EmulationFragment : Fragment(), SurfaceHolder.Callback {
@@ -90,14 +85,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
90 if (context is EmulationActivity) { 85 if (context is EmulationActivity) {
91 emulationActivity = context 86 emulationActivity = context
92 NativeLibrary.setEmulationActivity(context) 87 NativeLibrary.setEmulationActivity(context)
93
94 lifecycleScope.launch(Dispatchers.Main) {
95 lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
96 WindowInfoTracker.getOrCreate(context)
97 .windowLayoutInfo(context)
98 .collect { updateFoldableLayout(context, it) }
99 }
100 }
101 } else { 88 } else {
102 throw IllegalStateException("EmulationFragment must have EmulationActivity parent") 89 throw IllegalStateException("EmulationFragment must have EmulationActivity parent")
103 } 90 }
@@ -168,8 +155,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
168 return binding.root 155 return binding.root
169 } 156 }
170 157
171 // This is using the correct scope, lint is just acting up
172 @SuppressLint("UnsafeRepeatOnLifecycleDetector")
173 override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 158 override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
174 super.onViewCreated(view, savedInstanceState) 159 super.onViewCreated(view, savedInstanceState)
175 if (requireActivity().isFinishing) { 160 if (requireActivity().isFinishing) {
@@ -277,6 +262,15 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
277 true 262 true
278 } 263 }
279 264
265 R.id.menu_controls -> {
266 val action = HomeNavigationDirections.actionGlobalSettingsActivity(
267 null,
268 Settings.MenuTag.SECTION_INPUT
269 )
270 binding.root.findNavController().navigate(action)
271 true
272 }
273
280 R.id.menu_overlay_controls -> { 274 R.id.menu_overlay_controls -> {
281 showOverlayOptions() 275 showOverlayOptions()
282 true 276 true
@@ -341,129 +335,86 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
341 binding.loadingTitle.isSelected = true 335 binding.loadingTitle.isSelected = true
342 binding.loadingText.isSelected = true 336 binding.loadingText.isSelected = true
343 337
344 viewLifecycleOwner.lifecycleScope.apply { 338 WindowInfoTracker.getOrCreate(requireContext())
345 launch { 339 .windowLayoutInfo(requireActivity()).collect(viewLifecycleOwner) {
346 repeatOnLifecycle(Lifecycle.State.STARTED) { 340 updateFoldableLayout(requireActivity() as EmulationActivity, it)
347 WindowInfoTracker.getOrCreate(requireContext())
348 .windowLayoutInfo(requireActivity())
349 .collect {
350 updateFoldableLayout(requireActivity() as EmulationActivity, it)
351 }
352 }
353 } 341 }
354 launch { 342 emulationViewModel.shaderProgress.collect(viewLifecycleOwner) {
355 repeatOnLifecycle(Lifecycle.State.CREATED) { 343 if (it > 0 && it != emulationViewModel.totalShaders.value) {
356 emulationViewModel.shaderProgress.collectLatest { 344 binding.loadingProgressIndicator.isIndeterminate = false
357 if (it > 0 && it != emulationViewModel.totalShaders.value) {
358 binding.loadingProgressIndicator.isIndeterminate = false
359
360 if (it < binding.loadingProgressIndicator.max) {
361 binding.loadingProgressIndicator.progress = it
362 }
363 }
364 345
365 if (it == emulationViewModel.totalShaders.value) { 346 if (it < binding.loadingProgressIndicator.max) {
366 binding.loadingText.setText(R.string.loading) 347 binding.loadingProgressIndicator.progress = it
367 binding.loadingProgressIndicator.isIndeterminate = true
368 }
369 }
370 } 348 }
371 } 349 }
372 launch { 350
373 repeatOnLifecycle(Lifecycle.State.CREATED) { 351 if (it == emulationViewModel.totalShaders.value) {
374 emulationViewModel.totalShaders.collectLatest { 352 binding.loadingText.setText(R.string.loading)
375 binding.loadingProgressIndicator.max = it 353 binding.loadingProgressIndicator.isIndeterminate = true
376 }
377 }
378 }
379 launch {
380 repeatOnLifecycle(Lifecycle.State.CREATED) {
381 emulationViewModel.shaderMessage.collectLatest {
382 if (it.isNotEmpty()) {
383 binding.loadingText.text = it
384 }
385 }
386 }
387 } 354 }
388 launch { 355 }
389 repeatOnLifecycle(Lifecycle.State.RESUMED) { 356 emulationViewModel.totalShaders.collect(viewLifecycleOwner) {
390 driverViewModel.isInteractionAllowed.collect { 357 binding.loadingProgressIndicator.max = it
391 if (it) { 358 }
392 startEmulation() 359 emulationViewModel.shaderMessage.collect(viewLifecycleOwner) {
393 } 360 if (it.isNotEmpty()) {
394 } 361 binding.loadingText.text = it
395 }
396 } 362 }
397 launch { 363 }
398 repeatOnLifecycle(Lifecycle.State.CREATED) { 364
399 emulationViewModel.emulationStarted.collectLatest { 365 emulationViewModel.emulationStarted.collect(viewLifecycleOwner) {
400 if (it) { 366 if (it) {
401 binding.drawerLayout.setDrawerLockMode(IntSetting.LOCK_DRAWER.getInt()) 367 binding.drawerLayout.setDrawerLockMode(IntSetting.LOCK_DRAWER.getInt())
402 ViewUtils.showView(binding.surfaceInputOverlay) 368 ViewUtils.showView(binding.surfaceInputOverlay)
403 ViewUtils.hideView(binding.loadingIndicator) 369 ViewUtils.hideView(binding.loadingIndicator)
404 370
405 emulationState.updateSurface() 371 emulationState.updateSurface()
406 372
407 // Setup overlays 373 // Setup overlays
408 updateShowFpsOverlay() 374 updateShowFpsOverlay()
409 updateThermalOverlay() 375 updateThermalOverlay()
410 }
411 }
412 }
413 } 376 }
414 launch { 377 }
415 repeatOnLifecycle(Lifecycle.State.CREATED) { 378 emulationViewModel.isEmulationStopping.collect(viewLifecycleOwner) {
416 emulationViewModel.isEmulationStopping.collectLatest { 379 if (it) {
417 if (it) { 380 binding.loadingText.setText(R.string.shutting_down)
418 binding.loadingText.setText(R.string.shutting_down) 381 ViewUtils.showView(binding.loadingIndicator)
419 ViewUtils.showView(binding.loadingIndicator) 382 ViewUtils.hideView(binding.inputContainer)
420 ViewUtils.hideView(binding.inputContainer) 383 ViewUtils.hideView(binding.showFpsText)
421 ViewUtils.hideView(binding.showFpsText)
422 }
423 }
424 }
425 } 384 }
426 launch { 385 }
427 repeatOnLifecycle(Lifecycle.State.CREATED) { 386 emulationViewModel.drawerOpen.collect(viewLifecycleOwner) {
428 emulationViewModel.drawerOpen.collect { 387 if (it) {
429 if (it) { 388 binding.drawerLayout.open()
430 binding.drawerLayout.open() 389 binding.inGameMenu.requestFocus()
431 binding.inGameMenu.requestFocus() 390 } else {
432 } else { 391 binding.drawerLayout.close()
433 binding.drawerLayout.close()
434 }
435 }
436 }
437 } 392 }
438 launch { 393 }
439 repeatOnLifecycle(Lifecycle.State.CREATED) { 394 emulationViewModel.programChanged.collect(viewLifecycleOwner) {
440 emulationViewModel.programChanged.collect { 395 if (it != 0) {
441 if (it != 0) { 396 emulationViewModel.setEmulationStarted(false)
442 emulationViewModel.setEmulationStarted(false) 397 binding.drawerLayout.close()
443 binding.drawerLayout.close() 398 binding.drawerLayout
444 binding.drawerLayout 399 .setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED)
445 .setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED) 400 ViewUtils.hideView(binding.surfaceInputOverlay)
446 ViewUtils.hideView(binding.surfaceInputOverlay) 401 ViewUtils.showView(binding.loadingIndicator)
447 ViewUtils.showView(binding.loadingIndicator)
448 }
449 }
450 }
451 } 402 }
452 launch { 403 }
453 repeatOnLifecycle(Lifecycle.State.CREATED) { 404 emulationViewModel.emulationStopped.collect(viewLifecycleOwner) {
454 emulationViewModel.emulationStopped.collect { 405 if (it && emulationViewModel.programChanged.value != -1) {
455 if (it && emulationViewModel.programChanged.value != -1) { 406 if (perfStatsUpdater != null) {
456 if (perfStatsUpdater != null) { 407 perfStatsUpdateHandler.removeCallbacks(perfStatsUpdater!!)
457 perfStatsUpdateHandler.removeCallbacks(perfStatsUpdater!!)
458 }
459 emulationState.changeProgram(emulationViewModel.programChanged.value)
460 emulationViewModel.setProgramChanged(-1)
461 emulationViewModel.setEmulationStopped(false)
462 }
463 }
464 } 408 }
409 emulationState.changeProgram(emulationViewModel.programChanged.value)
410 emulationViewModel.setProgramChanged(-1)
411 emulationViewModel.setEmulationStopped(false)
465 } 412 }
466 } 413 }
414
415 driverViewModel.isInteractionAllowed.collect(viewLifecycleOwner) {
416 if (it) startEmulation()
417 }
467 } 418 }
468 419
469 private fun startEmulation(programIndex: Int = 0) { 420 private fun startEmulation(programIndex: Int = 0) {
@@ -491,14 +442,12 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
491 binding.drawerLayout.close() 442 binding.drawerLayout.close()
492 } 443 }
493 if (showInputOverlay) { 444 if (showInputOverlay) {
494 binding.surfaceInputOverlay.visibility = View.INVISIBLE 445 binding.surfaceInputOverlay.setVisible(visible = false, gone = false)
495 } 446 }
496 } else { 447 } else {
497 if (showInputOverlay && emulationViewModel.emulationStarted.value) { 448 binding.surfaceInputOverlay.setVisible(
498 binding.surfaceInputOverlay.visibility = View.VISIBLE 449 showInputOverlay && emulationViewModel.emulationStarted.value
499 } else { 450 )
500 binding.surfaceInputOverlay.visibility = View.INVISIBLE
501 }
502 if (!isInFoldableLayout) { 451 if (!isInFoldableLayout) {
503 if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) { 452 if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) {
504 binding.surfaceInputOverlay.layout = OverlayLayout.Portrait 453 binding.surfaceInputOverlay.layout = OverlayLayout.Portrait
@@ -535,7 +484,9 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
535 } 484 }
536 485
537 private fun updateShowFpsOverlay() { 486 private fun updateShowFpsOverlay() {
538 if (BooleanSetting.SHOW_PERFORMANCE_OVERLAY.getBoolean()) { 487 val showOverlay = BooleanSetting.SHOW_PERFORMANCE_OVERLAY.getBoolean()
488 binding.showFpsText.setVisible(showOverlay)
489 if (showOverlay) {
539 val SYSTEM_FPS = 0 490 val SYSTEM_FPS = 0
540 val FPS = 1 491 val FPS = 1
541 val FRAMETIME = 2 492 val FRAMETIME = 2
@@ -555,17 +506,17 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
555 } 506 }
556 } 507 }
557 perfStatsUpdateHandler.post(perfStatsUpdater!!) 508 perfStatsUpdateHandler.post(perfStatsUpdater!!)
558 binding.showFpsText.visibility = View.VISIBLE
559 } else { 509 } else {
560 if (perfStatsUpdater != null) { 510 if (perfStatsUpdater != null) {
561 perfStatsUpdateHandler.removeCallbacks(perfStatsUpdater!!) 511 perfStatsUpdateHandler.removeCallbacks(perfStatsUpdater!!)
562 } 512 }
563 binding.showFpsText.visibility = View.GONE
564 } 513 }
565 } 514 }
566 515
567 private fun updateThermalOverlay() { 516 private fun updateThermalOverlay() {
568 if (BooleanSetting.SHOW_THERMAL_OVERLAY.getBoolean()) { 517 val showOverlay = BooleanSetting.SHOW_THERMAL_OVERLAY.getBoolean()
518 binding.showThermalsText.setVisible(showOverlay)
519 if (showOverlay) {
569 thermalStatsUpdater = { 520 thermalStatsUpdater = {
570 if (emulationViewModel.emulationStarted.value && 521 if (emulationViewModel.emulationStarted.value &&
571 !emulationViewModel.isEmulationStopping.value 522 !emulationViewModel.isEmulationStopping.value
@@ -587,12 +538,10 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
587 } 538 }
588 } 539 }
589 thermalStatsUpdateHandler.post(thermalStatsUpdater!!) 540 thermalStatsUpdateHandler.post(thermalStatsUpdater!!)
590 binding.showThermalsText.visibility = View.VISIBLE
591 } else { 541 } else {
592 if (thermalStatsUpdater != null) { 542 if (thermalStatsUpdater != null) {
593 thermalStatsUpdateHandler.removeCallbacks(thermalStatsUpdater!!) 543 thermalStatsUpdateHandler.removeCallbacks(thermalStatsUpdater!!)
594 } 544 }
595 binding.showThermalsText.visibility = View.GONE
596 } 545 }
597 } 546 }
598 547
@@ -861,12 +810,12 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
861 } 810 }
862 } 811 }
863 } 812 }
864 binding.doneControlConfig.visibility = View.VISIBLE 813 binding.doneControlConfig.setVisible(true)
865 binding.surfaceInputOverlay.setIsInEditMode(true) 814 binding.surfaceInputOverlay.setIsInEditMode(true)
866 } 815 }
867 816
868 private fun stopConfiguringControls() { 817 private fun stopConfiguringControls() {
869 binding.doneControlConfig.visibility = View.GONE 818 binding.doneControlConfig.setVisible(false)
870 binding.surfaceInputOverlay.setIsInEditMode(false) 819 binding.surfaceInputOverlay.setIsInEditMode(false)
871 // Unlock the orientation if it was locked for editing 820 // Unlock the orientation if it was locked for editing
872 if (IntSetting.RENDERER_SCREEN_LAYOUT.getInt() == EmulationOrientation.Unspecified.int) { 821 if (IntSetting.RENDERER_SCREEN_LAYOUT.getInt() == EmulationOrientation.Unspecified.int) {
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GameFoldersFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GameFoldersFragment.kt
index 5c558b1a5..3a6f7a38c 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GameFoldersFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GameFoldersFragment.kt
@@ -13,9 +13,6 @@ import androidx.core.view.WindowInsetsCompat
13import androidx.core.view.updatePadding 13import androidx.core.view.updatePadding
14import androidx.fragment.app.Fragment 14import androidx.fragment.app.Fragment
15import androidx.fragment.app.activityViewModels 15import androidx.fragment.app.activityViewModels
16import androidx.lifecycle.Lifecycle
17import androidx.lifecycle.lifecycleScope
18import androidx.lifecycle.repeatOnLifecycle
19import androidx.navigation.findNavController 16import androidx.navigation.findNavController
20import androidx.recyclerview.widget.GridLayoutManager 17import androidx.recyclerview.widget.GridLayoutManager
21import com.google.android.material.transition.MaterialSharedAxis 18import com.google.android.material.transition.MaterialSharedAxis
@@ -27,6 +24,7 @@ import org.yuzu.yuzu_emu.model.GamesViewModel
27import org.yuzu.yuzu_emu.model.HomeViewModel 24import org.yuzu.yuzu_emu.model.HomeViewModel
28import org.yuzu.yuzu_emu.ui.main.MainActivity 25import org.yuzu.yuzu_emu.ui.main.MainActivity
29import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins 26import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
27import org.yuzu.yuzu_emu.utils.collect
30 28
31class GameFoldersFragment : Fragment() { 29class GameFoldersFragment : Fragment() {
32 private var _binding: FragmentFoldersBinding? = null 30 private var _binding: FragmentFoldersBinding? = null
@@ -70,12 +68,8 @@ class GameFoldersFragment : Fragment() {
70 adapter = FolderAdapter(requireActivity(), gamesViewModel) 68 adapter = FolderAdapter(requireActivity(), gamesViewModel)
71 } 69 }
72 70
73 viewLifecycleOwner.lifecycleScope.launch { 71 gamesViewModel.folders.collect(viewLifecycleOwner) {
74 repeatOnLifecycle(Lifecycle.State.CREATED) { 72 (binding.listFolders.adapter as FolderAdapter).submitList(it)
75 gamesViewModel.folders.collect {
76 (binding.listFolders.adapter as FolderAdapter).submitList(it)
77 }
78 }
79 } 73 }
80 74
81 val mainActivity = requireActivity() as MainActivity 75 val mainActivity = requireActivity() as MainActivity
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GameInfoFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GameInfoFragment.kt
index dbd56e84f..97a8954bb 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GameInfoFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GameInfoFragment.kt
@@ -27,6 +27,7 @@ import org.yuzu.yuzu_emu.databinding.FragmentGameInfoBinding
27import org.yuzu.yuzu_emu.model.GameVerificationResult 27import org.yuzu.yuzu_emu.model.GameVerificationResult
28import org.yuzu.yuzu_emu.model.HomeViewModel 28import org.yuzu.yuzu_emu.model.HomeViewModel
29import org.yuzu.yuzu_emu.utils.GameMetadata 29import org.yuzu.yuzu_emu.utils.GameMetadata
30import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
30import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins 31import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
31 32
32class GameInfoFragment : Fragment() { 33class GameInfoFragment : Fragment() {
@@ -85,7 +86,7 @@ class GameInfoFragment : Fragment() {
85 copyToClipboard(getString(R.string.developer), args.game.developer) 86 copyToClipboard(getString(R.string.developer), args.game.developer)
86 } 87 }
87 } else { 88 } else {
88 developer.visibility = View.GONE 89 developer.setVisible(false)
89 } 90 }
90 91
91 version.setHint(R.string.version) 92 version.setHint(R.string.version)
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GamePropertiesFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GamePropertiesFragment.kt
index 3ea5e16ca..c06842c59 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GamePropertiesFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GamePropertiesFragment.kt
@@ -3,11 +3,9 @@
3 3
4package org.yuzu.yuzu_emu.fragments 4package org.yuzu.yuzu_emu.fragments
5 5
6import android.annotation.SuppressLint
7import android.content.pm.ShortcutInfo 6import android.content.pm.ShortcutInfo
8import android.content.pm.ShortcutManager 7import android.content.pm.ShortcutManager
9import android.os.Bundle 8import android.os.Bundle
10import android.text.TextUtils
11import android.view.LayoutInflater 9import android.view.LayoutInflater
12import android.view.View 10import android.view.View
13import android.view.ViewGroup 11import android.view.ViewGroup
@@ -18,9 +16,7 @@ import androidx.core.view.WindowInsetsCompat
18import androidx.core.view.updatePadding 16import androidx.core.view.updatePadding
19import androidx.fragment.app.Fragment 17import androidx.fragment.app.Fragment
20import androidx.fragment.app.activityViewModels 18import androidx.fragment.app.activityViewModels
21import androidx.lifecycle.Lifecycle
22import androidx.lifecycle.lifecycleScope 19import androidx.lifecycle.lifecycleScope
23import androidx.lifecycle.repeatOnLifecycle
24import androidx.navigation.findNavController 20import androidx.navigation.findNavController
25import androidx.navigation.fragment.navArgs 21import androidx.navigation.fragment.navArgs
26import androidx.recyclerview.widget.GridLayoutManager 22import androidx.recyclerview.widget.GridLayoutManager
@@ -46,7 +42,9 @@ import org.yuzu.yuzu_emu.utils.FileUtil
46import org.yuzu.yuzu_emu.utils.GameIconUtils 42import org.yuzu.yuzu_emu.utils.GameIconUtils
47import org.yuzu.yuzu_emu.utils.GpuDriverHelper 43import org.yuzu.yuzu_emu.utils.GpuDriverHelper
48import org.yuzu.yuzu_emu.utils.MemoryUtil 44import org.yuzu.yuzu_emu.utils.MemoryUtil
45import org.yuzu.yuzu_emu.utils.ViewUtils.marquee
49import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins 46import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
47import org.yuzu.yuzu_emu.utils.collect
50import java.io.BufferedOutputStream 48import java.io.BufferedOutputStream
51import java.io.File 49import java.io.File
52 50
@@ -76,8 +74,6 @@ class GamePropertiesFragment : Fragment() {
76 return binding.root 74 return binding.root
77 } 75 }
78 76
79 // This is using the correct scope, lint is just acting up
80 @SuppressLint("UnsafeRepeatOnLifecycleDetector")
81 override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 77 override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
82 super.onViewCreated(view, savedInstanceState) 78 super.onViewCreated(view, savedInstanceState)
83 homeViewModel.setNavigationVisibility(visible = false, animated = true) 79 homeViewModel.setNavigationVisibility(visible = false, animated = true)
@@ -107,13 +103,7 @@ class GamePropertiesFragment : Fragment() {
107 103
108 GameIconUtils.loadGameIcon(args.game, binding.imageGameScreen) 104 GameIconUtils.loadGameIcon(args.game, binding.imageGameScreen)
109 binding.title.text = args.game.title 105 binding.title.text = args.game.title
110 binding.title.postDelayed( 106 binding.title.marquee()
111 {
112 binding.title.ellipsize = TextUtils.TruncateAt.MARQUEE
113 binding.title.isSelected = true
114 },
115 3000
116 )
117 107
118 binding.buttonStart.setOnClickListener { 108 binding.buttonStart.setOnClickListener {
119 LaunchGameDialogFragment.newInstance(args.game) 109 LaunchGameDialogFragment.newInstance(args.game)
@@ -122,28 +112,14 @@ class GamePropertiesFragment : Fragment() {
122 112
123 reloadList() 113 reloadList()
124 114
125 viewLifecycleOwner.lifecycleScope.apply { 115 homeViewModel.openImportSaves.collect(
126 launch { 116 viewLifecycleOwner,
127 repeatOnLifecycle(Lifecycle.State.STARTED) { 117 resetState = { homeViewModel.setOpenImportSaves(false) }
128 homeViewModel.openImportSaves.collect { 118 ) { if (it) importSaves.launch(arrayOf("application/zip")) }
129 if (it) { 119 homeViewModel.reloadPropertiesList.collect(
130 importSaves.launch(arrayOf("application/zip")) 120 viewLifecycleOwner,
131 homeViewModel.setOpenImportSaves(false) 121 resetState = { homeViewModel.reloadPropertiesList(false) }
132 } 122 ) { if (it) reloadList() }
133 }
134 }
135 }
136 launch {
137 repeatOnLifecycle(Lifecycle.State.STARTED) {
138 homeViewModel.reloadPropertiesList.collect {
139 if (it) {
140 reloadList()
141 homeViewModel.reloadPropertiesList(false)
142 }
143 }
144 }
145 }
146 }
147 123
148 setInsets() 124 setInsets()
149 } 125 }
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt
index 87e130d3e..14a2504b6 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt
@@ -91,6 +91,20 @@ class HomeSettingsFragment : Fragment() {
91 ) 91 )
92 add( 92 add(
93 HomeSetting( 93 HomeSetting(
94 R.string.preferences_controls,
95 R.string.preferences_controls_description,
96 R.drawable.ic_controller,
97 {
98 val action = HomeNavigationDirections.actionGlobalSettingsActivity(
99 null,
100 Settings.MenuTag.SECTION_INPUT
101 )
102 binding.root.findNavController().navigate(action)
103 }
104 )
105 )
106 add(
107 HomeSetting(
94 R.string.gpu_driver_manager, 108 R.string.gpu_driver_manager,
95 R.string.install_gpu_driver_description, 109 R.string.install_gpu_driver_description,
96 R.drawable.ic_build, 110 R.drawable.ic_build,
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/InstallableFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/InstallableFragment.kt
index 63112dc6f..d218da1c8 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/InstallableFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/InstallableFragment.kt
@@ -14,9 +14,6 @@ import androidx.core.view.WindowInsetsCompat
14import androidx.core.view.updatePadding 14import androidx.core.view.updatePadding
15import androidx.fragment.app.Fragment 15import androidx.fragment.app.Fragment
16import androidx.fragment.app.activityViewModels 16import androidx.fragment.app.activityViewModels
17import androidx.lifecycle.Lifecycle
18import androidx.lifecycle.lifecycleScope
19import androidx.lifecycle.repeatOnLifecycle
20import androidx.navigation.findNavController 17import androidx.navigation.findNavController
21import androidx.recyclerview.widget.GridLayoutManager 18import androidx.recyclerview.widget.GridLayoutManager
22import com.google.android.material.transition.MaterialSharedAxis 19import com.google.android.material.transition.MaterialSharedAxis
@@ -35,6 +32,7 @@ import org.yuzu.yuzu_emu.ui.main.MainActivity
35import org.yuzu.yuzu_emu.utils.DirectoryInitialization 32import org.yuzu.yuzu_emu.utils.DirectoryInitialization
36import org.yuzu.yuzu_emu.utils.FileUtil 33import org.yuzu.yuzu_emu.utils.FileUtil
37import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins 34import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
35import org.yuzu.yuzu_emu.utils.collect
38import java.io.BufferedOutputStream 36import java.io.BufferedOutputStream
39import java.io.File 37import java.io.File
40import java.math.BigInteger 38import java.math.BigInteger
@@ -75,14 +73,10 @@ class InstallableFragment : Fragment() {
75 binding.root.findNavController().popBackStack() 73 binding.root.findNavController().popBackStack()
76 } 74 }
77 75
78 viewLifecycleOwner.lifecycleScope.launch { 76 homeViewModel.openImportSaves.collect(viewLifecycleOwner) {
79 repeatOnLifecycle(Lifecycle.State.CREATED) { 77 if (it) {
80 homeViewModel.openImportSaves.collect { 78 importSaves.launch(arrayOf("application/zip"))
81 if (it) { 79 homeViewModel.setOpenImportSaves(false)
82 importSaves.launch(arrayOf("application/zip"))
83 homeViewModel.setOpenImportSaves(false)
84 }
85 }
86 } 80 }
87 } 81 }
88 82
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/ProgressDialogFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/ProgressDialogFragment.kt
index d201cb80c..ee3bb0386 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/ProgressDialogFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/ProgressDialogFragment.kt
@@ -13,15 +13,13 @@ import androidx.appcompat.app.AlertDialog
13import androidx.fragment.app.DialogFragment 13import androidx.fragment.app.DialogFragment
14import androidx.fragment.app.FragmentActivity 14import androidx.fragment.app.FragmentActivity
15import androidx.fragment.app.activityViewModels 15import androidx.fragment.app.activityViewModels
16import androidx.lifecycle.Lifecycle
17import androidx.lifecycle.ViewModelProvider 16import androidx.lifecycle.ViewModelProvider
18import androidx.lifecycle.lifecycleScope
19import androidx.lifecycle.repeatOnLifecycle
20import com.google.android.material.dialog.MaterialAlertDialogBuilder 17import com.google.android.material.dialog.MaterialAlertDialogBuilder
21import kotlinx.coroutines.launch
22import org.yuzu.yuzu_emu.R 18import org.yuzu.yuzu_emu.R
23import org.yuzu.yuzu_emu.databinding.DialogProgressBarBinding 19import org.yuzu.yuzu_emu.databinding.DialogProgressBarBinding
24import org.yuzu.yuzu_emu.model.TaskViewModel 20import org.yuzu.yuzu_emu.model.TaskViewModel
21import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
22import org.yuzu.yuzu_emu.utils.collect
25 23
26class ProgressDialogFragment : DialogFragment() { 24class ProgressDialogFragment : DialogFragment() {
27 private val taskViewModel: TaskViewModel by activityViewModels() 25 private val taskViewModel: TaskViewModel by activityViewModels()
@@ -64,72 +62,50 @@ class ProgressDialogFragment : DialogFragment() {
64 override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 62 override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
65 super.onViewCreated(view, savedInstanceState) 63 super.onViewCreated(view, savedInstanceState)
66 binding.message.isSelected = true 64 binding.message.isSelected = true
67 viewLifecycleOwner.lifecycleScope.apply { 65 taskViewModel.isComplete.collect(viewLifecycleOwner) {
68 launch { 66 if (it) {
69 repeatOnLifecycle(Lifecycle.State.CREATED) { 67 dismiss()
70 taskViewModel.isComplete.collect { 68 when (val result = taskViewModel.result.value) {
71 if (it) { 69 is String -> Toast.makeText(
72 dismiss() 70 requireContext(),
73 when (val result = taskViewModel.result.value) { 71 result,
74 is String -> Toast.makeText( 72 Toast.LENGTH_LONG
75 requireContext(), 73 ).show()
76 result, 74
77 Toast.LENGTH_LONG 75 is MessageDialogFragment -> result.show(
78 ).show() 76 requireActivity().supportFragmentManager,
79 77 MessageDialogFragment.TAG
80 is MessageDialogFragment -> result.show( 78 )
81 requireActivity().supportFragmentManager, 79
82 MessageDialogFragment.TAG 80 else -> {
83 ) 81 // Do nothing
84
85 else -> {
86 // Do nothing
87 }
88 }
89 taskViewModel.clear()
90 }
91 } 82 }
92 } 83 }
84 taskViewModel.clear()
93 } 85 }
94 launch { 86 }
95 repeatOnLifecycle(Lifecycle.State.CREATED) { 87 taskViewModel.cancelled.collect(viewLifecycleOwner) {
96 taskViewModel.cancelled.collect { 88 if (it) {
97 if (it) { 89 dialog?.setTitle(R.string.cancelling)
98 dialog?.setTitle(R.string.cancelling)
99 }
100 }
101 }
102 }
103 launch {
104 repeatOnLifecycle(Lifecycle.State.CREATED) {
105 taskViewModel.progress.collect {
106 if (it != 0.0) {
107 binding.progressBar.apply {
108 isIndeterminate = false
109 progress = (
110 (it / taskViewModel.maxProgress.value) *
111 PROGRESS_BAR_RESOLUTION
112 ).toInt()
113 min = 0
114 max = PROGRESS_BAR_RESOLUTION
115 }
116 }
117 }
118 }
119 } 90 }
120 launch { 91 }
121 repeatOnLifecycle(Lifecycle.State.CREATED) { 92 taskViewModel.progress.collect(viewLifecycleOwner) {
122 taskViewModel.message.collect { 93 if (it != 0.0) {
123 if (it.isEmpty()) { 94 binding.progressBar.apply {
124 binding.message.visibility = View.GONE 95 isIndeterminate = false
125 } else { 96 progress = (
126 binding.message.visibility = View.VISIBLE 97 (it / taskViewModel.maxProgress.value) *
127 binding.message.text = it 98 PROGRESS_BAR_RESOLUTION
128 } 99 ).toInt()
129 } 100 min = 0
101 max = PROGRESS_BAR_RESOLUTION
130 } 102 }
131 } 103 }
132 } 104 }
105 taskViewModel.message.collect(viewLifecycleOwner) {
106 binding.message.setVisible(it.isNotEmpty())
107 binding.message.text = it
108 }
133 } 109 }
134 110
135 // By default, the ProgressDialog will immediately dismiss itself upon a button being pressed. 111 // By default, the ProgressDialog will immediately dismiss itself upon a button being pressed.
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SearchFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SearchFragment.kt
index 20b10b1a0..662ae9760 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SearchFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SearchFragment.kt
@@ -3,7 +3,6 @@
3 3
4package org.yuzu.yuzu_emu.fragments 4package org.yuzu.yuzu_emu.fragments
5 5
6import android.annotation.SuppressLint
7import android.content.Context 6import android.content.Context
8import android.content.SharedPreferences 7import android.content.SharedPreferences
9import android.os.Bundle 8import android.os.Bundle
@@ -18,14 +17,9 @@ import androidx.core.view.updatePadding
18import androidx.core.widget.doOnTextChanged 17import androidx.core.widget.doOnTextChanged
19import androidx.fragment.app.Fragment 18import androidx.fragment.app.Fragment
20import androidx.fragment.app.activityViewModels 19import androidx.fragment.app.activityViewModels
21import androidx.lifecycle.Lifecycle
22import androidx.lifecycle.lifecycleScope
23import androidx.lifecycle.repeatOnLifecycle
24import androidx.preference.PreferenceManager 20import androidx.preference.PreferenceManager
25import info.debatty.java.stringsimilarity.Jaccard 21import info.debatty.java.stringsimilarity.Jaccard
26import info.debatty.java.stringsimilarity.JaroWinkler 22import info.debatty.java.stringsimilarity.JaroWinkler
27import kotlinx.coroutines.flow.collectLatest
28import kotlinx.coroutines.launch
29import java.util.Locale 23import java.util.Locale
30import org.yuzu.yuzu_emu.R 24import org.yuzu.yuzu_emu.R
31import org.yuzu.yuzu_emu.YuzuApplication 25import org.yuzu.yuzu_emu.YuzuApplication
@@ -35,6 +29,8 @@ import org.yuzu.yuzu_emu.layout.AutofitGridLayoutManager
35import org.yuzu.yuzu_emu.model.Game 29import org.yuzu.yuzu_emu.model.Game
36import org.yuzu.yuzu_emu.model.GamesViewModel 30import org.yuzu.yuzu_emu.model.GamesViewModel
37import org.yuzu.yuzu_emu.model.HomeViewModel 31import org.yuzu.yuzu_emu.model.HomeViewModel
32import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
33import org.yuzu.yuzu_emu.utils.collect
38 34
39class SearchFragment : Fragment() { 35class SearchFragment : Fragment() {
40 private var _binding: FragmentSearchBinding? = null 36 private var _binding: FragmentSearchBinding? = null
@@ -58,8 +54,6 @@ class SearchFragment : Fragment() {
58 return binding.root 54 return binding.root
59 } 55 }
60 56
61 // This is using the correct scope, lint is just acting up
62 @SuppressLint("UnsafeRepeatOnLifecycleDetector")
63 override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 57 override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
64 super.onViewCreated(view, savedInstanceState) 58 super.onViewCreated(view, savedInstanceState)
65 homeViewModel.setNavigationVisibility(visible = true, animated = true) 59 homeViewModel.setNavigationVisibility(visible = true, animated = true)
@@ -81,42 +75,18 @@ class SearchFragment : Fragment() {
81 binding.chipGroup.setOnCheckedStateChangeListener { _, _ -> filterAndSearch() } 75 binding.chipGroup.setOnCheckedStateChangeListener { _, _ -> filterAndSearch() }
82 76
83 binding.searchText.doOnTextChanged { text: CharSequence?, _: Int, _: Int, _: Int -> 77 binding.searchText.doOnTextChanged { text: CharSequence?, _: Int, _: Int, _: Int ->
84 if (text.toString().isNotEmpty()) { 78 binding.clearButton.setVisible(text.toString().isNotEmpty())
85 binding.clearButton.visibility = View.VISIBLE
86 } else {
87 binding.clearButton.visibility = View.INVISIBLE
88 }
89 filterAndSearch() 79 filterAndSearch()
90 } 80 }
91 81
92 viewLifecycleOwner.lifecycleScope.apply { 82 gamesViewModel.searchFocused.collect(
93 launch { 83 viewLifecycleOwner,
94 repeatOnLifecycle(Lifecycle.State.CREATED) { 84 resetState = { gamesViewModel.setSearchFocused(false) }
95 gamesViewModel.searchFocused.collect { 85 ) { if (it) focusSearch() }
96 if (it) { 86 gamesViewModel.games.collect(viewLifecycleOwner) { filterAndSearch() }
97 focusSearch() 87 gamesViewModel.searchedGames.collect(viewLifecycleOwner) {
98 gamesViewModel.setSearchFocused(false) 88 (binding.gridGamesSearch.adapter as GameAdapter).submitList(it)
99 } 89 binding.noResultsView.setVisible(it.isNotEmpty())
100 }
101 }
102 }
103 launch {
104 repeatOnLifecycle(Lifecycle.State.CREATED) {
105 gamesViewModel.games.collectLatest { filterAndSearch() }
106 }
107 }
108 launch {
109 repeatOnLifecycle(Lifecycle.State.CREATED) {
110 gamesViewModel.searchedGames.collect {
111 (binding.gridGamesSearch.adapter as GameAdapter).submitList(it)
112 if (it.isEmpty()) {
113 binding.noResultsView.visibility = View.VISIBLE
114 } else {
115 binding.noResultsView.visibility = View.GONE
116 }
117 }
118 }
119 }
120 } 90 }
121 91
122 binding.clearButton.setOnClickListener { binding.searchText.setText("") } 92 binding.clearButton.setOnClickListener { binding.searchText.setText("") }
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SetupFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SetupFragment.kt
index ebf41a639..4f7548e98 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SetupFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SetupFragment.kt
@@ -4,7 +4,6 @@
4package org.yuzu.yuzu_emu.fragments 4package org.yuzu.yuzu_emu.fragments
5 5
6import android.Manifest 6import android.Manifest
7import android.annotation.SuppressLint
8import android.content.Intent 7import android.content.Intent
9import android.os.Build 8import android.os.Build
10import android.os.Bundle 9import android.os.Bundle
@@ -23,9 +22,6 @@ import androidx.core.view.isVisible
23import androidx.core.view.updatePadding 22import androidx.core.view.updatePadding
24import androidx.fragment.app.Fragment 23import androidx.fragment.app.Fragment
25import androidx.fragment.app.activityViewModels 24import androidx.fragment.app.activityViewModels
26import androidx.lifecycle.Lifecycle
27import androidx.lifecycle.lifecycleScope
28import androidx.lifecycle.repeatOnLifecycle
29import androidx.navigation.findNavController 25import androidx.navigation.findNavController
30import androidx.preference.PreferenceManager 26import androidx.preference.PreferenceManager
31import androidx.viewpager2.widget.ViewPager2.OnPageChangeCallback 27import androidx.viewpager2.widget.ViewPager2.OnPageChangeCallback
@@ -46,6 +42,8 @@ import org.yuzu.yuzu_emu.ui.main.MainActivity
46import org.yuzu.yuzu_emu.utils.DirectoryInitialization 42import org.yuzu.yuzu_emu.utils.DirectoryInitialization
47import org.yuzu.yuzu_emu.utils.NativeConfig 43import org.yuzu.yuzu_emu.utils.NativeConfig
48import org.yuzu.yuzu_emu.utils.ViewUtils 44import org.yuzu.yuzu_emu.utils.ViewUtils
45import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
46import org.yuzu.yuzu_emu.utils.collect
49 47
50class SetupFragment : Fragment() { 48class SetupFragment : Fragment() {
51 private var _binding: FragmentSetupBinding? = null 49 private var _binding: FragmentSetupBinding? = null
@@ -77,8 +75,6 @@ class SetupFragment : Fragment() {
77 return binding.root 75 return binding.root
78 } 76 }
79 77
80 // This is using the correct scope, lint is just acting up
81 @SuppressLint("UnsafeRepeatOnLifecycleDetector")
82 override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 78 override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
83 mainActivity = requireActivity() as MainActivity 79 mainActivity = requireActivity() as MainActivity
84 80
@@ -210,28 +206,14 @@ class SetupFragment : Fragment() {
210 ) 206 )
211 } 207 }
212 208
213 viewLifecycleOwner.lifecycleScope.apply { 209 homeViewModel.shouldPageForward.collect(
214 launch { 210 viewLifecycleOwner,
215 repeatOnLifecycle(Lifecycle.State.CREATED) { 211 resetState = { homeViewModel.setShouldPageForward(false) }
216 homeViewModel.shouldPageForward.collect { 212 ) { if (it) pageForward() }
217 if (it) { 213 homeViewModel.gamesDirSelected.collect(
218 pageForward() 214 viewLifecycleOwner,
219 homeViewModel.setShouldPageForward(false) 215 resetState = { homeViewModel.setGamesDirSelected(false) }
220 } 216 ) { if (it) gamesDirCallback.onStepCompleted() }
221 }
222 }
223 }
224 launch {
225 repeatOnLifecycle(Lifecycle.State.CREATED) {
226 homeViewModel.gamesDirSelected.collect {
227 if (it) {
228 gamesDirCallback.onStepCompleted()
229 homeViewModel.setGamesDirSelected(false)
230 }
231 }
232 }
233 }
234 }
235 217
236 binding.viewPager2.apply { 218 binding.viewPager2.apply {
237 adapter = SetupAdapter(requireActivity() as AppCompatActivity, pages) 219 adapter = SetupAdapter(requireActivity() as AppCompatActivity, pages)
@@ -292,12 +274,8 @@ class SetupFragment : Fragment() {
292 val backIsVisible = savedInstanceState.getBoolean(KEY_BACK_VISIBILITY) 274 val backIsVisible = savedInstanceState.getBoolean(KEY_BACK_VISIBILITY)
293 hasBeenWarned = savedInstanceState.getBooleanArray(KEY_HAS_BEEN_WARNED)!! 275 hasBeenWarned = savedInstanceState.getBooleanArray(KEY_HAS_BEEN_WARNED)!!
294 276
295 if (nextIsVisible) { 277 binding.buttonNext.setVisible(nextIsVisible)
296 binding.buttonNext.visibility = View.VISIBLE 278 binding.buttonBack.setVisible(backIsVisible)
297 }
298 if (backIsVisible) {
299 binding.buttonBack.visibility = View.VISIBLE
300 }
301 } else { 279 } else {
302 hasBeenWarned = BooleanArray(pages.size) 280 hasBeenWarned = BooleanArray(pages.size)
303 } 281 }
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlay.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlay.kt
index c87486c90..ed112a38d 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlay.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlay.kt
@@ -24,10 +24,10 @@ import androidx.core.content.ContextCompat
24import androidx.window.layout.WindowMetricsCalculator 24import androidx.window.layout.WindowMetricsCalculator
25import kotlin.math.max 25import kotlin.math.max
26import kotlin.math.min 26import kotlin.math.min
27import org.yuzu.yuzu_emu.NativeLibrary 27import org.yuzu.yuzu_emu.features.input.NativeInput
28import org.yuzu.yuzu_emu.NativeLibrary.ButtonType
29import org.yuzu.yuzu_emu.NativeLibrary.StickType
30import org.yuzu.yuzu_emu.R 28import org.yuzu.yuzu_emu.R
29import org.yuzu.yuzu_emu.features.input.model.NativeAnalog
30import org.yuzu.yuzu_emu.features.input.model.NativeButton
31import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting 31import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting
32import org.yuzu.yuzu_emu.features.settings.model.IntSetting 32import org.yuzu.yuzu_emu.features.settings.model.IntSetting
33import org.yuzu.yuzu_emu.overlay.model.OverlayControl 33import org.yuzu.yuzu_emu.overlay.model.OverlayControl
@@ -100,19 +100,19 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
100 100
101 var shouldUpdateView = false 101 var shouldUpdateView = false
102 val playerIndex = 102 val playerIndex =
103 if (NativeLibrary.isHandheldOnly()) { 103 if (NativeInput.isHandheldOnly()) {
104 NativeLibrary.ConsoleDevice 104 NativeInput.ConsoleDevice
105 } else { 105 } else {
106 NativeLibrary.Player1Device 106 NativeInput.Player1Device
107 } 107 }
108 108
109 for (button in overlayButtons) { 109 for (button in overlayButtons) {
110 if (!button.updateStatus(event)) { 110 if (!button.updateStatus(event)) {
111 continue 111 continue
112 } 112 }
113 NativeLibrary.onGamePadButtonEvent( 113 NativeInput.onOverlayButtonEvent(
114 playerIndex, 114 playerIndex,
115 button.buttonId, 115 button.button,
116 button.status 116 button.status
117 ) 117 )
118 playHaptics(event) 118 playHaptics(event)
@@ -123,24 +123,24 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
123 if (!dpad.updateStatus(event, BooleanSetting.DPAD_SLIDE.getBoolean())) { 123 if (!dpad.updateStatus(event, BooleanSetting.DPAD_SLIDE.getBoolean())) {
124 continue 124 continue
125 } 125 }
126 NativeLibrary.onGamePadButtonEvent( 126 NativeInput.onOverlayButtonEvent(
127 playerIndex, 127 playerIndex,
128 dpad.upId, 128 dpad.up,
129 dpad.upStatus 129 dpad.upStatus
130 ) 130 )
131 NativeLibrary.onGamePadButtonEvent( 131 NativeInput.onOverlayButtonEvent(
132 playerIndex, 132 playerIndex,
133 dpad.downId, 133 dpad.down,
134 dpad.downStatus 134 dpad.downStatus
135 ) 135 )
136 NativeLibrary.onGamePadButtonEvent( 136 NativeInput.onOverlayButtonEvent(
137 playerIndex, 137 playerIndex,
138 dpad.leftId, 138 dpad.left,
139 dpad.leftStatus 139 dpad.leftStatus
140 ) 140 )
141 NativeLibrary.onGamePadButtonEvent( 141 NativeInput.onOverlayButtonEvent(
142 playerIndex, 142 playerIndex,
143 dpad.rightId, 143 dpad.right,
144 dpad.rightStatus 144 dpad.rightStatus
145 ) 145 )
146 playHaptics(event) 146 playHaptics(event)
@@ -151,16 +151,15 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
151 if (!joystick.updateStatus(event)) { 151 if (!joystick.updateStatus(event)) {
152 continue 152 continue
153 } 153 }
154 val axisID = joystick.joystickId 154 NativeInput.onOverlayJoystickEvent(
155 NativeLibrary.onGamePadJoystickEvent(
156 playerIndex, 155 playerIndex,
157 axisID, 156 joystick.joystick,
158 joystick.xAxis, 157 joystick.xAxis,
159 joystick.realYAxis 158 joystick.realYAxis
160 ) 159 )
161 NativeLibrary.onGamePadButtonEvent( 160 NativeInput.onOverlayButtonEvent(
162 playerIndex, 161 playerIndex,
163 joystick.buttonId, 162 joystick.button,
164 joystick.buttonStatus 163 joystick.buttonStatus
165 ) 164 )
166 playHaptics(event) 165 playHaptics(event)
@@ -187,7 +186,7 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
187 motionEvent == MotionEvent.ACTION_UP || motionEvent == MotionEvent.ACTION_POINTER_UP 186 motionEvent == MotionEvent.ACTION_UP || motionEvent == MotionEvent.ACTION_POINTER_UP
188 187
189 if (isActionDown && !isTouchInputConsumed(pointerId)) { 188 if (isActionDown && !isTouchInputConsumed(pointerId)) {
190 NativeLibrary.onTouchPressed(pointerId, xPosition.toFloat(), yPosition.toFloat()) 189 NativeInput.onTouchPressed(pointerId, xPosition.toFloat(), yPosition.toFloat())
191 } 190 }
192 191
193 if (isActionMove) { 192 if (isActionMove) {
@@ -196,12 +195,12 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
196 if (isTouchInputConsumed(fingerId)) { 195 if (isTouchInputConsumed(fingerId)) {
197 continue 196 continue
198 } 197 }
199 NativeLibrary.onTouchMoved(fingerId, event.getX(i), event.getY(i)) 198 NativeInput.onTouchMoved(fingerId, event.getX(i), event.getY(i))
200 } 199 }
201 } 200 }
202 201
203 if (isActionUp && !isTouchInputConsumed(pointerId)) { 202 if (isActionUp && !isTouchInputConsumed(pointerId)) {
204 NativeLibrary.onTouchReleased(pointerId) 203 NativeInput.onTouchReleased(pointerId)
205 } 204 }
206 205
207 return true 206 return true
@@ -359,7 +358,7 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
359 windowSize, 358 windowSize,
360 R.drawable.facebutton_a, 359 R.drawable.facebutton_a,
361 R.drawable.facebutton_a_depressed, 360 R.drawable.facebutton_a_depressed,
362 ButtonType.BUTTON_A, 361 NativeButton.A,
363 data, 362 data,
364 position 363 position
365 ) 364 )
@@ -373,7 +372,7 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
373 windowSize, 372 windowSize,
374 R.drawable.facebutton_b, 373 R.drawable.facebutton_b,
375 R.drawable.facebutton_b_depressed, 374 R.drawable.facebutton_b_depressed,
376 ButtonType.BUTTON_B, 375 NativeButton.B,
377 data, 376 data,
378 position 377 position
379 ) 378 )
@@ -387,7 +386,7 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
387 windowSize, 386 windowSize,
388 R.drawable.facebutton_x, 387 R.drawable.facebutton_x,
389 R.drawable.facebutton_x_depressed, 388 R.drawable.facebutton_x_depressed,
390 ButtonType.BUTTON_X, 389 NativeButton.X,
391 data, 390 data,
392 position 391 position
393 ) 392 )
@@ -401,7 +400,7 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
401 windowSize, 400 windowSize,
402 R.drawable.facebutton_y, 401 R.drawable.facebutton_y,
403 R.drawable.facebutton_y_depressed, 402 R.drawable.facebutton_y_depressed,
404 ButtonType.BUTTON_Y, 403 NativeButton.Y,
405 data, 404 data,
406 position 405 position
407 ) 406 )
@@ -415,7 +414,7 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
415 windowSize, 414 windowSize,
416 R.drawable.facebutton_plus, 415 R.drawable.facebutton_plus,
417 R.drawable.facebutton_plus_depressed, 416 R.drawable.facebutton_plus_depressed,
418 ButtonType.BUTTON_PLUS, 417 NativeButton.Plus,
419 data, 418 data,
420 position 419 position
421 ) 420 )
@@ -429,7 +428,7 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
429 windowSize, 428 windowSize,
430 R.drawable.facebutton_minus, 429 R.drawable.facebutton_minus,
431 R.drawable.facebutton_minus_depressed, 430 R.drawable.facebutton_minus_depressed,
432 ButtonType.BUTTON_MINUS, 431 NativeButton.Minus,
433 data, 432 data,
434 position 433 position
435 ) 434 )
@@ -443,7 +442,7 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
443 windowSize, 442 windowSize,
444 R.drawable.facebutton_home, 443 R.drawable.facebutton_home,
445 R.drawable.facebutton_home_depressed, 444 R.drawable.facebutton_home_depressed,
446 ButtonType.BUTTON_HOME, 445 NativeButton.Home,
447 data, 446 data,
448 position 447 position
449 ) 448 )
@@ -457,7 +456,7 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
457 windowSize, 456 windowSize,
458 R.drawable.facebutton_screenshot, 457 R.drawable.facebutton_screenshot,
459 R.drawable.facebutton_screenshot_depressed, 458 R.drawable.facebutton_screenshot_depressed,
460 ButtonType.BUTTON_CAPTURE, 459 NativeButton.Capture,
461 data, 460 data,
462 position 461 position
463 ) 462 )
@@ -471,7 +470,7 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
471 windowSize, 470 windowSize,
472 R.drawable.l_shoulder, 471 R.drawable.l_shoulder,
473 R.drawable.l_shoulder_depressed, 472 R.drawable.l_shoulder_depressed,
474 ButtonType.TRIGGER_L, 473 NativeButton.L,
475 data, 474 data,
476 position 475 position
477 ) 476 )
@@ -485,7 +484,7 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
485 windowSize, 484 windowSize,
486 R.drawable.r_shoulder, 485 R.drawable.r_shoulder,
487 R.drawable.r_shoulder_depressed, 486 R.drawable.r_shoulder_depressed,
488 ButtonType.TRIGGER_R, 487 NativeButton.R,
489 data, 488 data,
490 position 489 position
491 ) 490 )
@@ -499,7 +498,7 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
499 windowSize, 498 windowSize,
500 R.drawable.zl_trigger, 499 R.drawable.zl_trigger,
501 R.drawable.zl_trigger_depressed, 500 R.drawable.zl_trigger_depressed,
502 ButtonType.TRIGGER_ZL, 501 NativeButton.ZL,
503 data, 502 data,
504 position 503 position
505 ) 504 )
@@ -513,7 +512,7 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
513 windowSize, 512 windowSize,
514 R.drawable.zr_trigger, 513 R.drawable.zr_trigger,
515 R.drawable.zr_trigger_depressed, 514 R.drawable.zr_trigger_depressed,
516 ButtonType.TRIGGER_ZR, 515 NativeButton.ZR,
517 data, 516 data,
518 position 517 position
519 ) 518 )
@@ -527,7 +526,7 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
527 windowSize, 526 windowSize,
528 R.drawable.button_l3, 527 R.drawable.button_l3,
529 R.drawable.button_l3_depressed, 528 R.drawable.button_l3_depressed,
530 ButtonType.STICK_L, 529 NativeButton.LStick,
531 data, 530 data,
532 position 531 position
533 ) 532 )
@@ -541,7 +540,7 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
541 windowSize, 540 windowSize,
542 R.drawable.button_r3, 541 R.drawable.button_r3,
543 R.drawable.button_r3_depressed, 542 R.drawable.button_r3_depressed,
544 ButtonType.STICK_R, 543 NativeButton.RStick,
545 data, 544 data,
546 position 545 position
547 ) 546 )
@@ -556,8 +555,8 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
556 R.drawable.joystick_range, 555 R.drawable.joystick_range,
557 R.drawable.joystick, 556 R.drawable.joystick,
558 R.drawable.joystick_depressed, 557 R.drawable.joystick_depressed,
559 StickType.STICK_L, 558 NativeAnalog.LStick,
560 ButtonType.STICK_L, 559 NativeButton.LStick,
561 data, 560 data,
562 position 561 position
563 ) 562 )
@@ -572,8 +571,8 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
572 R.drawable.joystick_range, 571 R.drawable.joystick_range,
573 R.drawable.joystick, 572 R.drawable.joystick,
574 R.drawable.joystick_depressed, 573 R.drawable.joystick_depressed,
575 StickType.STICK_R, 574 NativeAnalog.RStick,
576 ButtonType.STICK_R, 575 NativeButton.RStick,
577 data, 576 data,
578 position 577 position
579 ) 578 )
@@ -665,7 +664,7 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
665 664
666 val overlayControlData = NativeConfig.getOverlayControlData() 665 val overlayControlData = NativeConfig.getOverlayControlData()
667 overlayControlData.forEach { 666 overlayControlData.forEach {
668 it.enabled = OverlayControl.from(it.id)?.defaultVisibility == false 667 it.enabled = OverlayControl.from(it.id)?.defaultVisibility == true
669 } 668 }
670 NativeConfig.setOverlayControlData(overlayControlData) 669 NativeConfig.setOverlayControlData(overlayControlData)
671 670
@@ -835,7 +834,7 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
835 windowSize: Pair<Point, Point>, 834 windowSize: Pair<Point, Point>,
836 defaultResId: Int, 835 defaultResId: Int,
837 pressedResId: Int, 836 pressedResId: Int,
838 buttonId: Int, 837 button: NativeButton,
839 overlayControlData: OverlayControlData, 838 overlayControlData: OverlayControlData,
840 position: Pair<Double, Double> 839 position: Pair<Double, Double>
841 ): InputOverlayDrawableButton { 840 ): InputOverlayDrawableButton {
@@ -869,7 +868,7 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
869 res, 868 res,
870 defaultStateBitmap, 869 defaultStateBitmap,
871 pressedStateBitmap, 870 pressedStateBitmap,
872 buttonId, 871 button,
873 overlayControlData 872 overlayControlData
874 ) 873 )
875 874
@@ -940,11 +939,7 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
940 res, 939 res,
941 defaultStateBitmap, 940 defaultStateBitmap,
942 pressedOneDirectionStateBitmap, 941 pressedOneDirectionStateBitmap,
943 pressedTwoDirectionsStateBitmap, 942 pressedTwoDirectionsStateBitmap
944 ButtonType.DPAD_UP,
945 ButtonType.DPAD_DOWN,
946 ButtonType.DPAD_LEFT,
947 ButtonType.DPAD_RIGHT
948 ) 943 )
949 944
950 // Get the minimum and maximum coordinates of the screen where the button can be placed. 945 // Get the minimum and maximum coordinates of the screen where the button can be placed.
@@ -993,8 +988,8 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
993 resOuter: Int, 988 resOuter: Int,
994 defaultResInner: Int, 989 defaultResInner: Int,
995 pressedResInner: Int, 990 pressedResInner: Int,
996 joystick: Int, 991 joystick: NativeAnalog,
997 buttonId: Int, 992 button: NativeButton,
998 overlayControlData: OverlayControlData, 993 overlayControlData: OverlayControlData,
999 position: Pair<Double, Double> 994 position: Pair<Double, Double>
1000 ): InputOverlayDrawableJoystick { 995 ): InputOverlayDrawableJoystick {
@@ -1042,7 +1037,7 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
1042 outerRect, 1037 outerRect,
1043 innerRect, 1038 innerRect,
1044 joystick, 1039 joystick,
1045 buttonId, 1040 button,
1046 overlayControlData.id 1041 overlayControlData.id
1047 ) 1042 )
1048 1043
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlayDrawableButton.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlayDrawableButton.kt
index b14a4f96e..fee3d04ee 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlayDrawableButton.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlayDrawableButton.kt
@@ -9,7 +9,8 @@ import android.graphics.Canvas
9import android.graphics.Rect 9import android.graphics.Rect
10import android.graphics.drawable.BitmapDrawable 10import android.graphics.drawable.BitmapDrawable
11import android.view.MotionEvent 11import android.view.MotionEvent
12import org.yuzu.yuzu_emu.NativeLibrary.ButtonState 12import org.yuzu.yuzu_emu.features.input.NativeInput.ButtonState
13import org.yuzu.yuzu_emu.features.input.model.NativeButton
13import org.yuzu.yuzu_emu.overlay.model.OverlayControlData 14import org.yuzu.yuzu_emu.overlay.model.OverlayControlData
14 15
15/** 16/**
@@ -19,13 +20,13 @@ import org.yuzu.yuzu_emu.overlay.model.OverlayControlData
19 * @param res [Resources] instance. 20 * @param res [Resources] instance.
20 * @param defaultStateBitmap [Bitmap] to use with the default state Drawable. 21 * @param defaultStateBitmap [Bitmap] to use with the default state Drawable.
21 * @param pressedStateBitmap [Bitmap] to use with the pressed state Drawable. 22 * @param pressedStateBitmap [Bitmap] to use with the pressed state Drawable.
22 * @param buttonId Identifier for this type of button. 23 * @param button [NativeButton] for this type of button.
23 */ 24 */
24class InputOverlayDrawableButton( 25class InputOverlayDrawableButton(
25 res: Resources, 26 res: Resources,
26 defaultStateBitmap: Bitmap, 27 defaultStateBitmap: Bitmap,
27 pressedStateBitmap: Bitmap, 28 pressedStateBitmap: Bitmap,
28 val buttonId: Int, 29 val button: NativeButton,
29 val overlayControlData: OverlayControlData 30 val overlayControlData: OverlayControlData
30) { 31) {
31 // The ID value what motion event is tracking 32 // The ID value what motion event is tracking
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlayDrawableDpad.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlayDrawableDpad.kt
index 8aef6f5a5..0cb6ff244 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlayDrawableDpad.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlayDrawableDpad.kt
@@ -9,7 +9,8 @@ import android.graphics.Canvas
9import android.graphics.Rect 9import android.graphics.Rect
10import android.graphics.drawable.BitmapDrawable 10import android.graphics.drawable.BitmapDrawable
11import android.view.MotionEvent 11import android.view.MotionEvent
12import org.yuzu.yuzu_emu.NativeLibrary.ButtonState 12import org.yuzu.yuzu_emu.features.input.NativeInput.ButtonState
13import org.yuzu.yuzu_emu.features.input.model.NativeButton
13 14
14/** 15/**
15 * Custom [BitmapDrawable] that is capable 16 * Custom [BitmapDrawable] that is capable
@@ -19,20 +20,12 @@ import org.yuzu.yuzu_emu.NativeLibrary.ButtonState
19 * @param defaultStateBitmap [Bitmap] of the default state. 20 * @param defaultStateBitmap [Bitmap] of the default state.
20 * @param pressedOneDirectionStateBitmap [Bitmap] of the pressed state in one direction. 21 * @param pressedOneDirectionStateBitmap [Bitmap] of the pressed state in one direction.
21 * @param pressedTwoDirectionsStateBitmap [Bitmap] of the pressed state in two direction. 22 * @param pressedTwoDirectionsStateBitmap [Bitmap] of the pressed state in two direction.
22 * @param buttonUp Identifier for the up button.
23 * @param buttonDown Identifier for the down button.
24 * @param buttonLeft Identifier for the left button.
25 * @param buttonRight Identifier for the right button.
26 */ 23 */
27class InputOverlayDrawableDpad( 24class InputOverlayDrawableDpad(
28 res: Resources, 25 res: Resources,
29 defaultStateBitmap: Bitmap, 26 defaultStateBitmap: Bitmap,
30 pressedOneDirectionStateBitmap: Bitmap, 27 pressedOneDirectionStateBitmap: Bitmap,
31 pressedTwoDirectionsStateBitmap: Bitmap, 28 pressedTwoDirectionsStateBitmap: Bitmap
32 buttonUp: Int,
33 buttonDown: Int,
34 buttonLeft: Int,
35 buttonRight: Int
36) { 29) {
37 /** 30 /**
38 * Gets one of the InputOverlayDrawableDpad's button IDs. 31 * Gets one of the InputOverlayDrawableDpad's button IDs.
@@ -40,10 +33,10 @@ class InputOverlayDrawableDpad(
40 * @return the requested InputOverlayDrawableDpad's button ID. 33 * @return the requested InputOverlayDrawableDpad's button ID.
41 */ 34 */
42 // The ID identifying what type of button this Drawable represents. 35 // The ID identifying what type of button this Drawable represents.
43 val upId: Int 36 val up = NativeButton.DUp
44 val downId: Int 37 val down = NativeButton.DDown
45 val leftId: Int 38 val left = NativeButton.DLeft
46 val rightId: Int 39 val right = NativeButton.DRight
47 var trackId: Int 40 var trackId: Int
48 41
49 val width: Int 42 val width: Int
@@ -69,10 +62,6 @@ class InputOverlayDrawableDpad(
69 this.pressedTwoDirectionsStateBitmap = BitmapDrawable(res, pressedTwoDirectionsStateBitmap) 62 this.pressedTwoDirectionsStateBitmap = BitmapDrawable(res, pressedTwoDirectionsStateBitmap)
70 width = this.defaultStateBitmap.intrinsicWidth 63 width = this.defaultStateBitmap.intrinsicWidth
71 height = this.defaultStateBitmap.intrinsicHeight 64 height = this.defaultStateBitmap.intrinsicHeight
72 upId = buttonUp
73 downId = buttonDown
74 leftId = buttonLeft
75 rightId = buttonRight
76 trackId = -1 65 trackId = -1
77 } 66 }
78 67
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlayDrawableJoystick.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlayDrawableJoystick.kt
index 113bf7c24..4b07107fc 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlayDrawableJoystick.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlayDrawableJoystick.kt
@@ -13,7 +13,9 @@ import kotlin.math.atan2
13import kotlin.math.cos 13import kotlin.math.cos
14import kotlin.math.sin 14import kotlin.math.sin
15import kotlin.math.sqrt 15import kotlin.math.sqrt
16import org.yuzu.yuzu_emu.NativeLibrary 16import org.yuzu.yuzu_emu.features.input.NativeInput.ButtonState
17import org.yuzu.yuzu_emu.features.input.model.NativeAnalog
18import org.yuzu.yuzu_emu.features.input.model.NativeButton
17import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting 19import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting
18 20
19/** 21/**
@@ -26,8 +28,8 @@ import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting
26 * @param bitmapInnerPressed [Bitmap] which represents the pressed inner movable part of the joystick. 28 * @param bitmapInnerPressed [Bitmap] which represents the pressed inner movable part of the joystick.
27 * @param rectOuter [Rect] which represents the outer joystick bounds. 29 * @param rectOuter [Rect] which represents the outer joystick bounds.
28 * @param rectInner [Rect] which represents the inner joystick bounds. 30 * @param rectInner [Rect] which represents the inner joystick bounds.
29 * @param joystickId The ID value what type of joystick this Drawable represents. 31 * @param joystick The [NativeAnalog] this Drawable represents.
30 * @param buttonId The ID value what type of button this Drawable represents. 32 * @param button The [NativeButton] this Drawable represents.
31 */ 33 */
32class InputOverlayDrawableJoystick( 34class InputOverlayDrawableJoystick(
33 res: Resources, 35 res: Resources,
@@ -36,8 +38,8 @@ class InputOverlayDrawableJoystick(
36 bitmapInnerPressed: Bitmap, 38 bitmapInnerPressed: Bitmap,
37 rectOuter: Rect, 39 rectOuter: Rect,
38 rectInner: Rect, 40 rectInner: Rect,
39 val joystickId: Int, 41 val joystick: NativeAnalog,
40 val buttonId: Int, 42 val button: NativeButton,
41 val prefId: String 43 val prefId: String
42) { 44) {
43 // The ID value what motion event is tracking 45 // The ID value what motion event is tracking
@@ -69,8 +71,7 @@ class InputOverlayDrawableJoystick(
69 71
70 // TODO: Add button support 72 // TODO: Add button support
71 val buttonStatus: Int 73 val buttonStatus: Int
72 get() = 74 get() = ButtonState.RELEASED
73 NativeLibrary.ButtonState.RELEASED
74 var bounds: Rect 75 var bounds: Rect
75 get() = outerBitmap.bounds 76 get() = outerBitmap.bounds
76 set(bounds) { 77 set(bounds) {
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/GamesFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/GamesFragment.kt
index 23ca49b53..fadb20e39 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/GamesFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/GamesFragment.kt
@@ -3,7 +3,6 @@
3 3
4package org.yuzu.yuzu_emu.ui 4package org.yuzu.yuzu_emu.ui
5 5
6import android.annotation.SuppressLint
7import android.os.Bundle 6import android.os.Bundle
8import android.view.LayoutInflater 7import android.view.LayoutInflater
9import android.view.View 8import android.view.View
@@ -14,19 +13,16 @@ import androidx.core.view.WindowInsetsCompat
14import androidx.core.view.updatePadding 13import androidx.core.view.updatePadding
15import androidx.fragment.app.Fragment 14import androidx.fragment.app.Fragment
16import androidx.fragment.app.activityViewModels 15import androidx.fragment.app.activityViewModels
17import androidx.lifecycle.Lifecycle
18import androidx.lifecycle.lifecycleScope
19import androidx.lifecycle.repeatOnLifecycle
20import com.google.android.material.color.MaterialColors 16import com.google.android.material.color.MaterialColors
21import kotlinx.coroutines.flow.collectLatest
22import kotlinx.coroutines.launch
23import org.yuzu.yuzu_emu.R 17import org.yuzu.yuzu_emu.R
24import org.yuzu.yuzu_emu.adapters.GameAdapter 18import org.yuzu.yuzu_emu.adapters.GameAdapter
25import org.yuzu.yuzu_emu.databinding.FragmentGamesBinding 19import org.yuzu.yuzu_emu.databinding.FragmentGamesBinding
26import org.yuzu.yuzu_emu.layout.AutofitGridLayoutManager 20import org.yuzu.yuzu_emu.layout.AutofitGridLayoutManager
27import org.yuzu.yuzu_emu.model.GamesViewModel 21import org.yuzu.yuzu_emu.model.GamesViewModel
28import org.yuzu.yuzu_emu.model.HomeViewModel 22import org.yuzu.yuzu_emu.model.HomeViewModel
23import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
29import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins 24import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
25import org.yuzu.yuzu_emu.utils.collect
30 26
31class GamesFragment : Fragment() { 27class GamesFragment : Fragment() {
32 private var _binding: FragmentGamesBinding? = null 28 private var _binding: FragmentGamesBinding? = null
@@ -44,8 +40,6 @@ class GamesFragment : Fragment() {
44 return binding.root 40 return binding.root
45 } 41 }
46 42
47 // This is using the correct scope, lint is just acting up
48 @SuppressLint("UnsafeRepeatOnLifecycleDetector")
49 override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 43 override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
50 super.onViewCreated(view, savedInstanceState) 44 super.onViewCreated(view, savedInstanceState)
51 homeViewModel.setNavigationVisibility(visible = true, animated = true) 45 homeViewModel.setNavigationVisibility(visible = true, animated = true)
@@ -88,49 +82,28 @@ class GamesFragment : Fragment() {
88 } 82 }
89 } 83 }
90 84
91 viewLifecycleOwner.lifecycleScope.apply { 85 gamesViewModel.isReloading.collect(viewLifecycleOwner) {
92 launch { 86 binding.swipeRefresh.isRefreshing = it
93 repeatOnLifecycle(Lifecycle.State.RESUMED) { 87 binding.noticeText.setVisible(
94 gamesViewModel.isReloading.collect { 88 visible = gamesViewModel.games.value.isEmpty() && !it,
95 binding.swipeRefresh.isRefreshing = it 89 gone = false
96 if (gamesViewModel.games.value.isEmpty() && !it) { 90 )
97 binding.noticeText.visibility = View.VISIBLE 91 }
98 } else { 92 gamesViewModel.games.collect(viewLifecycleOwner) {
99 binding.noticeText.visibility = View.INVISIBLE 93 (binding.gridGames.adapter as GameAdapter).submitList(it)
100 } 94 }
101 } 95 gamesViewModel.shouldSwapData.collect(
102 } 96 viewLifecycleOwner,
103 } 97 resetState = { gamesViewModel.setShouldSwapData(false) }
104 launch { 98 ) {
105 repeatOnLifecycle(Lifecycle.State.RESUMED) { 99 if (it) {
106 gamesViewModel.games.collectLatest { 100 (binding.gridGames.adapter as GameAdapter).submitList(gamesViewModel.games.value)
107 (binding.gridGames.adapter as GameAdapter).submitList(it)
108 }
109 }
110 }
111 launch {
112 repeatOnLifecycle(Lifecycle.State.RESUMED) {
113 gamesViewModel.shouldSwapData.collect {
114 if (it) {
115 (binding.gridGames.adapter as GameAdapter).submitList(
116 gamesViewModel.games.value
117 )
118 gamesViewModel.setShouldSwapData(false)
119 }
120 }
121 }
122 }
123 launch {
124 repeatOnLifecycle(Lifecycle.State.RESUMED) {
125 gamesViewModel.shouldScrollToTop.collect {
126 if (it) {
127 scrollToTop()
128 gamesViewModel.setShouldScrollToTop(false)
129 }
130 }
131 }
132 } 101 }
133 } 102 }
103 gamesViewModel.shouldScrollToTop.collect(
104 viewLifecycleOwner,
105 resetState = { gamesViewModel.setShouldScrollToTop(false) }
106 ) { if (it) scrollToTop() }
134 107
135 setInsets() 108 setInsets()
136 } 109 }
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt
index 4df4ac4c6..757463a0b 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt
@@ -19,9 +19,6 @@ import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
19import androidx.core.view.ViewCompat 19import androidx.core.view.ViewCompat
20import androidx.core.view.WindowCompat 20import androidx.core.view.WindowCompat
21import androidx.core.view.WindowInsetsCompat 21import androidx.core.view.WindowInsetsCompat
22import androidx.lifecycle.Lifecycle
23import androidx.lifecycle.lifecycleScope
24import androidx.lifecycle.repeatOnLifecycle
25import androidx.navigation.NavController 22import androidx.navigation.NavController
26import androidx.navigation.fragment.NavHostFragment 23import androidx.navigation.fragment.NavHostFragment
27import androidx.navigation.ui.setupWithNavController 24import androidx.navigation.ui.setupWithNavController
@@ -30,7 +27,6 @@ import com.google.android.material.color.MaterialColors
30import com.google.android.material.navigation.NavigationBarView 27import com.google.android.material.navigation.NavigationBarView
31import java.io.File 28import java.io.File
32import java.io.FilenameFilter 29import java.io.FilenameFilter
33import kotlinx.coroutines.launch
34import org.yuzu.yuzu_emu.HomeNavigationDirections 30import org.yuzu.yuzu_emu.HomeNavigationDirections
35import org.yuzu.yuzu_emu.NativeLibrary 31import org.yuzu.yuzu_emu.NativeLibrary
36import org.yuzu.yuzu_emu.R 32import org.yuzu.yuzu_emu.R
@@ -47,6 +43,7 @@ import org.yuzu.yuzu_emu.model.InstallResult
47import org.yuzu.yuzu_emu.model.TaskState 43import org.yuzu.yuzu_emu.model.TaskState
48import org.yuzu.yuzu_emu.model.TaskViewModel 44import org.yuzu.yuzu_emu.model.TaskViewModel
49import org.yuzu.yuzu_emu.utils.* 45import org.yuzu.yuzu_emu.utils.*
46import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
50import java.io.BufferedInputStream 47import java.io.BufferedInputStream
51import java.io.BufferedOutputStream 48import java.io.BufferedOutputStream
52import java.util.zip.ZipEntry 49import java.util.zip.ZipEntry
@@ -139,42 +136,23 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
139 136
140 // Prevents navigation from being drawn for a short time on recreation if set to hidden 137 // Prevents navigation from being drawn for a short time on recreation if set to hidden
141 if (!homeViewModel.navigationVisible.value.first) { 138 if (!homeViewModel.navigationVisible.value.first) {
142 binding.navigationView.visibility = View.INVISIBLE 139 binding.navigationView.setVisible(visible = false, gone = false)
143 binding.statusBarShade.visibility = View.INVISIBLE 140 binding.statusBarShade.setVisible(visible = false, gone = false)
144 } 141 }
145 142
146 lifecycleScope.apply { 143 homeViewModel.navigationVisible.collect(this) { showNavigation(it.first, it.second) }
147 launch { 144 homeViewModel.statusBarShadeVisible.collect(this) { showStatusBarShade(it) }
148 repeatOnLifecycle(Lifecycle.State.CREATED) { 145 homeViewModel.contentToInstall.collect(
149 homeViewModel.navigationVisible.collect { showNavigation(it.first, it.second) } 146 this,
150 } 147 resetState = { homeViewModel.setContentToInstall(null) }
151 } 148 ) {
152 launch { 149 if (it != null) {
153 repeatOnLifecycle(Lifecycle.State.CREATED) { 150 installContent(it)
154 homeViewModel.statusBarShadeVisible.collect { showStatusBarShade(it) }
155 }
156 }
157 launch {
158 repeatOnLifecycle(Lifecycle.State.CREATED) {
159 homeViewModel.contentToInstall.collect {
160 if (it != null) {
161 installContent(it)
162 homeViewModel.setContentToInstall(null)
163 }
164 }
165 }
166 }
167 launch {
168 repeatOnLifecycle(Lifecycle.State.CREATED) {
169 homeViewModel.checkKeys.collect {
170 if (it) {
171 checkKeys()
172 homeViewModel.setCheckKeys(false)
173 }
174 }
175 }
176 } 151 }
177 } 152 }
153 homeViewModel.checkKeys.collect(this, resetState = { homeViewModel.setCheckKeys(false) }) {
154 if (it) checkKeys()
155 }
178 156
179 setInsets() 157 setInsets()
180 } 158 }
@@ -214,18 +192,14 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
214 192
215 private fun showNavigation(visible: Boolean, animated: Boolean) { 193 private fun showNavigation(visible: Boolean, animated: Boolean) {
216 if (!animated) { 194 if (!animated) {
217 if (visible) { 195 binding.navigationView.setVisible(visible)
218 binding.navigationView.visibility = View.VISIBLE
219 } else {
220 binding.navigationView.visibility = View.INVISIBLE
221 }
222 return 196 return
223 } 197 }
224 198
225 val smallLayout = resources.getBoolean(R.bool.small_layout) 199 val smallLayout = resources.getBoolean(R.bool.small_layout)
226 binding.navigationView.animate().apply { 200 binding.navigationView.animate().apply {
227 if (visible) { 201 if (visible) {
228 binding.navigationView.visibility = View.VISIBLE 202 binding.navigationView.setVisible(true)
229 duration = 300 203 duration = 300
230 interpolator = PathInterpolator(0.05f, 0.7f, 0.1f, 1f) 204 interpolator = PathInterpolator(0.05f, 0.7f, 0.1f, 1f)
231 205
@@ -264,7 +238,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
264 } 238 }
265 }.withEndAction { 239 }.withEndAction {
266 if (!visible) { 240 if (!visible) {
267 binding.navigationView.visibility = View.INVISIBLE 241 binding.navigationView.setVisible(visible = false, gone = false)
268 } 242 }
269 }.start() 243 }.start()
270 } 244 }
@@ -272,7 +246,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
272 private fun showStatusBarShade(visible: Boolean) { 246 private fun showStatusBarShade(visible: Boolean) {
273 binding.statusBarShade.animate().apply { 247 binding.statusBarShade.animate().apply {
274 if (visible) { 248 if (visible) {
275 binding.statusBarShade.visibility = View.VISIBLE 249 binding.statusBarShade.setVisible(true)
276 binding.statusBarShade.translationY = binding.statusBarShade.height.toFloat() * -2 250 binding.statusBarShade.translationY = binding.statusBarShade.height.toFloat() * -2
277 duration = 300 251 duration = 300
278 translationY(0f) 252 translationY(0f)
@@ -284,7 +258,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
284 } 258 }
285 }.withEndAction { 259 }.withEndAction {
286 if (!visible) { 260 if (!visible) {
287 binding.statusBarShade.visibility = View.INVISIBLE 261 binding.statusBarShade.setVisible(visible = false, gone = false)
288 } 262 }
289 }.start() 263 }.start()
290 } 264 }
@@ -524,7 +498,8 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
524 this@MainActivity, 498 this@MainActivity,
525 titleId = R.string.content_install_notice, 499 titleId = R.string.content_install_notice,
526 descriptionId = R.string.content_install_notice_description, 500 descriptionId = R.string.content_install_notice_description,
527 positiveAction = { homeViewModel.setContentToInstall(documents) } 501 positiveAction = { homeViewModel.setContentToInstall(documents) },
502 negativeAction = {}
528 ) 503 )
529 } 504 }
530 }.show(supportFragmentManager, ProgressDialogFragment.TAG) 505 }.show(supportFragmentManager, ProgressDialogFragment.TAG)
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/InputHandler.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/InputHandler.kt
index e63382e1d..2c7356e6a 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/InputHandler.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/InputHandler.kt
@@ -6,439 +6,89 @@ package org.yuzu.yuzu_emu.utils
6import android.view.InputDevice 6import android.view.InputDevice
7import android.view.KeyEvent 7import android.view.KeyEvent
8import android.view.MotionEvent 8import android.view.MotionEvent
9import kotlin.math.sqrt 9import org.yuzu.yuzu_emu.features.input.NativeInput
10import org.yuzu.yuzu_emu.NativeLibrary 10import org.yuzu.yuzu_emu.features.input.YuzuInputOverlayDevice
11import org.yuzu.yuzu_emu.features.input.YuzuPhysicalDevice
11 12
12object InputHandler { 13object InputHandler {
13 private var controllerIds = getGameControllerIds() 14 var androidControllers = mapOf<Int, YuzuPhysicalDevice>()
14 15 var registeredControllers = mutableListOf<ParamPackage>()
15 fun initialize() {
16 // Connect first controller
17 NativeLibrary.onGamePadConnectEvent(getPlayerNumber(NativeLibrary.Player1Device))
18 }
19
20 fun updateControllerIds() {
21 controllerIds = getGameControllerIds()
22 }
23 16
24 fun dispatchKeyEvent(event: KeyEvent): Boolean { 17 fun dispatchKeyEvent(event: KeyEvent): Boolean {
25 val button: Int = when (event.device.vendorId) {
26 0x045E -> getInputXboxButtonKey(event.keyCode)
27 0x054C -> getInputDS5ButtonKey(event.keyCode)
28 0x057E -> getInputJoyconButtonKey(event.keyCode)
29 0x1532 -> getInputRazerButtonKey(event.keyCode)
30 0x3537 -> getInputRedmagicButtonKey(event.keyCode)
31 0x358A -> getInputBackboneLabsButtonKey(event.keyCode)
32 else -> getInputGenericButtonKey(event.keyCode)
33 }
34
35 val action = when (event.action) { 18 val action = when (event.action) {
36 KeyEvent.ACTION_DOWN -> NativeLibrary.ButtonState.PRESSED 19 KeyEvent.ACTION_DOWN -> NativeInput.ButtonState.PRESSED
37 KeyEvent.ACTION_UP -> NativeLibrary.ButtonState.RELEASED 20 KeyEvent.ACTION_UP -> NativeInput.ButtonState.RELEASED
38 else -> return false 21 else -> return false
39 } 22 }
40 23
41 // Ignore invalid buttons 24 var controllerData = androidControllers[event.device.controllerNumber]
42 if (button < 0) { 25 if (controllerData == null) {
43 return false 26 updateControllerData()
27 controllerData = androidControllers[event.device.controllerNumber] ?: return false
44 } 28 }
45 29
46 return NativeLibrary.onGamePadButtonEvent( 30 NativeInput.onGamePadButtonEvent(
47 getPlayerNumber(event.device.controllerNumber, event.deviceId), 31 controllerData.getGUID(),
48 button, 32 controllerData.getPort(),
33 event.keyCode,
49 action 34 action
50 ) 35 )
36 return true
51 } 37 }
52 38
53 fun dispatchGenericMotionEvent(event: MotionEvent): Boolean { 39 fun dispatchGenericMotionEvent(event: MotionEvent): Boolean {
54 val device = event.device 40 val controllerData =
55 // Check every axis input available on the controller 41 androidControllers[event.device.controllerNumber] ?: return false
56 for (range in device.motionRanges) { 42 event.device.motionRanges.forEach {
57 val axis = range.axis 43 NativeInput.onGamePadAxisEvent(
58 when (device.vendorId) { 44 controllerData.getGUID(),
59 0x045E -> setGenericAxisInput(event, axis) 45 controllerData.getPort(),
60 0x054C -> setGenericAxisInput(event, axis) 46 it.axis,
61 0x057E -> setJoyconAxisInput(event, axis) 47 event.getAxisValue(it.axis)
62 0x1532 -> setRazerAxisInput(event, axis) 48 )
63 else -> setGenericAxisInput(event, axis)
64 }
65 } 49 }
66
67 return true 50 return true
68 } 51 }
69 52
70 private fun getPlayerNumber(index: Int, deviceId: Int = -1): Int { 53 fun getDevices(): Map<Int, YuzuPhysicalDevice> {
71 var deviceIndex = index 54 val gameControllerDeviceIds = mutableMapOf<Int, YuzuPhysicalDevice>()
72 if (deviceId != -1) {
73 deviceIndex = controllerIds[deviceId] ?: 0
74 }
75
76 // TODO: Joycons are handled as different controllers. Find a way to merge them.
77 return when (deviceIndex) {
78 2 -> NativeLibrary.Player2Device
79 3 -> NativeLibrary.Player3Device
80 4 -> NativeLibrary.Player4Device
81 5 -> NativeLibrary.Player5Device
82 6 -> NativeLibrary.Player6Device
83 7 -> NativeLibrary.Player7Device
84 8 -> NativeLibrary.Player8Device
85 else -> if (NativeLibrary.isHandheldOnly()) {
86 NativeLibrary.ConsoleDevice
87 } else {
88 NativeLibrary.Player1Device
89 }
90 }
91 }
92
93 private fun setStickState(playerNumber: Int, index: Int, xAxis: Float, yAxis: Float) {
94 // Calculate vector size
95 val r2 = xAxis * xAxis + yAxis * yAxis
96 var r = sqrt(r2.toDouble()).toFloat()
97
98 // Adjust range of joystick
99 val deadzone = 0.15f
100 var x = xAxis
101 var y = yAxis
102
103 if (r > deadzone) {
104 val deadzoneFactor = 1.0f / r * (r - deadzone) / (1.0f - deadzone)
105 x *= deadzoneFactor
106 y *= deadzoneFactor
107 r *= deadzoneFactor
108 } else {
109 x = 0.0f
110 y = 0.0f
111 }
112
113 // Normalize joystick
114 if (r > 1.0f) {
115 x /= r
116 y /= r
117 }
118
119 NativeLibrary.onGamePadJoystickEvent(
120 playerNumber,
121 index,
122 x,
123 -y
124 )
125 }
126
127 private fun getAxisToButton(axis: Float): Int {
128 return if (axis > 0.5f) {
129 NativeLibrary.ButtonState.PRESSED
130 } else {
131 NativeLibrary.ButtonState.RELEASED
132 }
133 }
134
135 private fun setAxisDpadState(playerNumber: Int, xAxis: Float, yAxis: Float) {
136 NativeLibrary.onGamePadButtonEvent(
137 playerNumber,
138 NativeLibrary.ButtonType.DPAD_UP,
139 getAxisToButton(-yAxis)
140 )
141 NativeLibrary.onGamePadButtonEvent(
142 playerNumber,
143 NativeLibrary.ButtonType.DPAD_DOWN,
144 getAxisToButton(yAxis)
145 )
146 NativeLibrary.onGamePadButtonEvent(
147 playerNumber,
148 NativeLibrary.ButtonType.DPAD_LEFT,
149 getAxisToButton(-xAxis)
150 )
151 NativeLibrary.onGamePadButtonEvent(
152 playerNumber,
153 NativeLibrary.ButtonType.DPAD_RIGHT,
154 getAxisToButton(xAxis)
155 )
156 }
157
158 private fun getInputDS5ButtonKey(key: Int): Int {
159 // The missing ds5 buttons are axis
160 return when (key) {
161 KeyEvent.KEYCODE_BUTTON_A -> NativeLibrary.ButtonType.BUTTON_B
162 KeyEvent.KEYCODE_BUTTON_B -> NativeLibrary.ButtonType.BUTTON_A
163 KeyEvent.KEYCODE_BUTTON_X -> NativeLibrary.ButtonType.BUTTON_Y
164 KeyEvent.KEYCODE_BUTTON_Y -> NativeLibrary.ButtonType.BUTTON_X
165 KeyEvent.KEYCODE_BUTTON_L1 -> NativeLibrary.ButtonType.TRIGGER_L
166 KeyEvent.KEYCODE_BUTTON_R1 -> NativeLibrary.ButtonType.TRIGGER_R
167 KeyEvent.KEYCODE_BUTTON_THUMBL -> NativeLibrary.ButtonType.STICK_L
168 KeyEvent.KEYCODE_BUTTON_THUMBR -> NativeLibrary.ButtonType.STICK_R
169 KeyEvent.KEYCODE_BUTTON_START -> NativeLibrary.ButtonType.BUTTON_PLUS
170 KeyEvent.KEYCODE_BUTTON_SELECT -> NativeLibrary.ButtonType.BUTTON_MINUS
171 else -> -1
172 }
173 }
174
175 private fun getInputJoyconButtonKey(key: Int): Int {
176 // Joycon support is half dead. A lot of buttons can't be mapped
177 return when (key) {
178 KeyEvent.KEYCODE_BUTTON_A -> NativeLibrary.ButtonType.BUTTON_B
179 KeyEvent.KEYCODE_BUTTON_B -> NativeLibrary.ButtonType.BUTTON_A
180 KeyEvent.KEYCODE_BUTTON_X -> NativeLibrary.ButtonType.BUTTON_X
181 KeyEvent.KEYCODE_BUTTON_Y -> NativeLibrary.ButtonType.BUTTON_Y
182 KeyEvent.KEYCODE_DPAD_UP -> NativeLibrary.ButtonType.DPAD_UP
183 KeyEvent.KEYCODE_DPAD_DOWN -> NativeLibrary.ButtonType.DPAD_DOWN
184 KeyEvent.KEYCODE_DPAD_LEFT -> NativeLibrary.ButtonType.DPAD_LEFT
185 KeyEvent.KEYCODE_DPAD_RIGHT -> NativeLibrary.ButtonType.DPAD_RIGHT
186 KeyEvent.KEYCODE_BUTTON_L1 -> NativeLibrary.ButtonType.TRIGGER_L
187 KeyEvent.KEYCODE_BUTTON_R1 -> NativeLibrary.ButtonType.TRIGGER_R
188 KeyEvent.KEYCODE_BUTTON_L2 -> NativeLibrary.ButtonType.TRIGGER_ZL
189 KeyEvent.KEYCODE_BUTTON_R2 -> NativeLibrary.ButtonType.TRIGGER_ZR
190 KeyEvent.KEYCODE_BUTTON_THUMBL -> NativeLibrary.ButtonType.STICK_L
191 KeyEvent.KEYCODE_BUTTON_THUMBR -> NativeLibrary.ButtonType.STICK_R
192 KeyEvent.KEYCODE_BUTTON_START -> NativeLibrary.ButtonType.BUTTON_PLUS
193 KeyEvent.KEYCODE_BUTTON_SELECT -> NativeLibrary.ButtonType.BUTTON_MINUS
194 else -> -1
195 }
196 }
197
198 private fun getInputXboxButtonKey(key: Int): Int {
199 // The missing xbox buttons are axis
200 return when (key) {
201 KeyEvent.KEYCODE_BUTTON_A -> NativeLibrary.ButtonType.BUTTON_A
202 KeyEvent.KEYCODE_BUTTON_B -> NativeLibrary.ButtonType.BUTTON_B
203 KeyEvent.KEYCODE_BUTTON_X -> NativeLibrary.ButtonType.BUTTON_X
204 KeyEvent.KEYCODE_BUTTON_Y -> NativeLibrary.ButtonType.BUTTON_Y
205 KeyEvent.KEYCODE_BUTTON_L1 -> NativeLibrary.ButtonType.TRIGGER_L
206 KeyEvent.KEYCODE_BUTTON_R1 -> NativeLibrary.ButtonType.TRIGGER_R
207 KeyEvent.KEYCODE_BUTTON_THUMBL -> NativeLibrary.ButtonType.STICK_L
208 KeyEvent.KEYCODE_BUTTON_THUMBR -> NativeLibrary.ButtonType.STICK_R
209 KeyEvent.KEYCODE_BUTTON_START -> NativeLibrary.ButtonType.BUTTON_PLUS
210 KeyEvent.KEYCODE_BUTTON_SELECT -> NativeLibrary.ButtonType.BUTTON_MINUS
211 else -> -1
212 }
213 }
214
215 private fun getInputRazerButtonKey(key: Int): Int {
216 // The missing xbox buttons are axis
217 return when (key) {
218 KeyEvent.KEYCODE_BUTTON_A -> NativeLibrary.ButtonType.BUTTON_B
219 KeyEvent.KEYCODE_BUTTON_B -> NativeLibrary.ButtonType.BUTTON_A
220 KeyEvent.KEYCODE_BUTTON_X -> NativeLibrary.ButtonType.BUTTON_Y
221 KeyEvent.KEYCODE_BUTTON_Y -> NativeLibrary.ButtonType.BUTTON_X
222 KeyEvent.KEYCODE_BUTTON_L1 -> NativeLibrary.ButtonType.TRIGGER_L
223 KeyEvent.KEYCODE_BUTTON_R1 -> NativeLibrary.ButtonType.TRIGGER_R
224 KeyEvent.KEYCODE_BUTTON_THUMBL -> NativeLibrary.ButtonType.STICK_L
225 KeyEvent.KEYCODE_BUTTON_THUMBR -> NativeLibrary.ButtonType.STICK_R
226 KeyEvent.KEYCODE_BUTTON_START -> NativeLibrary.ButtonType.BUTTON_PLUS
227 KeyEvent.KEYCODE_BUTTON_SELECT -> NativeLibrary.ButtonType.BUTTON_MINUS
228 else -> -1
229 }
230 }
231
232 private fun getInputRedmagicButtonKey(key: Int): Int {
233 return when (key) {
234 KeyEvent.KEYCODE_BUTTON_A -> NativeLibrary.ButtonType.BUTTON_B
235 KeyEvent.KEYCODE_BUTTON_B -> NativeLibrary.ButtonType.BUTTON_A
236 KeyEvent.KEYCODE_BUTTON_X -> NativeLibrary.ButtonType.BUTTON_Y
237 KeyEvent.KEYCODE_BUTTON_Y -> NativeLibrary.ButtonType.BUTTON_X
238 KeyEvent.KEYCODE_BUTTON_L1 -> NativeLibrary.ButtonType.TRIGGER_L
239 KeyEvent.KEYCODE_BUTTON_R1 -> NativeLibrary.ButtonType.TRIGGER_R
240 KeyEvent.KEYCODE_BUTTON_L2 -> NativeLibrary.ButtonType.TRIGGER_ZL
241 KeyEvent.KEYCODE_BUTTON_R2 -> NativeLibrary.ButtonType.TRIGGER_ZR
242 KeyEvent.KEYCODE_BUTTON_THUMBL -> NativeLibrary.ButtonType.STICK_L
243 KeyEvent.KEYCODE_BUTTON_THUMBR -> NativeLibrary.ButtonType.STICK_R
244 KeyEvent.KEYCODE_BUTTON_START -> NativeLibrary.ButtonType.BUTTON_PLUS
245 KeyEvent.KEYCODE_BUTTON_SELECT -> NativeLibrary.ButtonType.BUTTON_MINUS
246 else -> -1
247 }
248 }
249
250 private fun getInputBackboneLabsButtonKey(key: Int): Int {
251 return when (key) {
252 KeyEvent.KEYCODE_BUTTON_A -> NativeLibrary.ButtonType.BUTTON_B
253 KeyEvent.KEYCODE_BUTTON_B -> NativeLibrary.ButtonType.BUTTON_A
254 KeyEvent.KEYCODE_BUTTON_X -> NativeLibrary.ButtonType.BUTTON_Y
255 KeyEvent.KEYCODE_BUTTON_Y -> NativeLibrary.ButtonType.BUTTON_X
256 KeyEvent.KEYCODE_BUTTON_L1 -> NativeLibrary.ButtonType.TRIGGER_L
257 KeyEvent.KEYCODE_BUTTON_R1 -> NativeLibrary.ButtonType.TRIGGER_R
258 KeyEvent.KEYCODE_BUTTON_L2 -> NativeLibrary.ButtonType.TRIGGER_ZL
259 KeyEvent.KEYCODE_BUTTON_R2 -> NativeLibrary.ButtonType.TRIGGER_ZR
260 KeyEvent.KEYCODE_BUTTON_THUMBL -> NativeLibrary.ButtonType.STICK_L
261 KeyEvent.KEYCODE_BUTTON_THUMBR -> NativeLibrary.ButtonType.STICK_R
262 KeyEvent.KEYCODE_BUTTON_START -> NativeLibrary.ButtonType.BUTTON_PLUS
263 KeyEvent.KEYCODE_BUTTON_SELECT -> NativeLibrary.ButtonType.BUTTON_MINUS
264 else -> -1
265 }
266 }
267
268 private fun getInputGenericButtonKey(key: Int): Int {
269 return when (key) {
270 KeyEvent.KEYCODE_BUTTON_A -> NativeLibrary.ButtonType.BUTTON_A
271 KeyEvent.KEYCODE_BUTTON_B -> NativeLibrary.ButtonType.BUTTON_B
272 KeyEvent.KEYCODE_BUTTON_X -> NativeLibrary.ButtonType.BUTTON_X
273 KeyEvent.KEYCODE_BUTTON_Y -> NativeLibrary.ButtonType.BUTTON_Y
274 KeyEvent.KEYCODE_DPAD_UP -> NativeLibrary.ButtonType.DPAD_UP
275 KeyEvent.KEYCODE_DPAD_DOWN -> NativeLibrary.ButtonType.DPAD_DOWN
276 KeyEvent.KEYCODE_DPAD_LEFT -> NativeLibrary.ButtonType.DPAD_LEFT
277 KeyEvent.KEYCODE_DPAD_RIGHT -> NativeLibrary.ButtonType.DPAD_RIGHT
278 KeyEvent.KEYCODE_BUTTON_L1 -> NativeLibrary.ButtonType.TRIGGER_L
279 KeyEvent.KEYCODE_BUTTON_R1 -> NativeLibrary.ButtonType.TRIGGER_R
280 KeyEvent.KEYCODE_BUTTON_L2 -> NativeLibrary.ButtonType.TRIGGER_ZL
281 KeyEvent.KEYCODE_BUTTON_R2 -> NativeLibrary.ButtonType.TRIGGER_ZR
282 KeyEvent.KEYCODE_BUTTON_THUMBL -> NativeLibrary.ButtonType.STICK_L
283 KeyEvent.KEYCODE_BUTTON_THUMBR -> NativeLibrary.ButtonType.STICK_R
284 KeyEvent.KEYCODE_BUTTON_START -> NativeLibrary.ButtonType.BUTTON_PLUS
285 KeyEvent.KEYCODE_BUTTON_SELECT -> NativeLibrary.ButtonType.BUTTON_MINUS
286 else -> -1
287 }
288 }
289
290 private fun setGenericAxisInput(event: MotionEvent, axis: Int) {
291 val playerNumber = getPlayerNumber(event.device.controllerNumber, event.deviceId)
292
293 when (axis) {
294 MotionEvent.AXIS_X, MotionEvent.AXIS_Y ->
295 setStickState(
296 playerNumber,
297 NativeLibrary.StickType.STICK_L,
298 event.getAxisValue(MotionEvent.AXIS_X),
299 event.getAxisValue(MotionEvent.AXIS_Y)
300 )
301 MotionEvent.AXIS_RX, MotionEvent.AXIS_RY ->
302 setStickState(
303 playerNumber,
304 NativeLibrary.StickType.STICK_R,
305 event.getAxisValue(MotionEvent.AXIS_RX),
306 event.getAxisValue(MotionEvent.AXIS_RY)
307 )
308 MotionEvent.AXIS_Z, MotionEvent.AXIS_RZ ->
309 setStickState(
310 playerNumber,
311 NativeLibrary.StickType.STICK_R,
312 event.getAxisValue(MotionEvent.AXIS_Z),
313 event.getAxisValue(MotionEvent.AXIS_RZ)
314 )
315 MotionEvent.AXIS_LTRIGGER ->
316 NativeLibrary.onGamePadButtonEvent(
317 playerNumber,
318 NativeLibrary.ButtonType.TRIGGER_ZL,
319 getAxisToButton(event.getAxisValue(MotionEvent.AXIS_LTRIGGER))
320 )
321 MotionEvent.AXIS_BRAKE ->
322 NativeLibrary.onGamePadButtonEvent(
323 playerNumber,
324 NativeLibrary.ButtonType.TRIGGER_ZL,
325 getAxisToButton(event.getAxisValue(MotionEvent.AXIS_BRAKE))
326 )
327 MotionEvent.AXIS_RTRIGGER ->
328 NativeLibrary.onGamePadButtonEvent(
329 playerNumber,
330 NativeLibrary.ButtonType.TRIGGER_ZR,
331 getAxisToButton(event.getAxisValue(MotionEvent.AXIS_RTRIGGER))
332 )
333 MotionEvent.AXIS_GAS ->
334 NativeLibrary.onGamePadButtonEvent(
335 playerNumber,
336 NativeLibrary.ButtonType.TRIGGER_ZR,
337 getAxisToButton(event.getAxisValue(MotionEvent.AXIS_GAS))
338 )
339 MotionEvent.AXIS_HAT_X, MotionEvent.AXIS_HAT_Y ->
340 setAxisDpadState(
341 playerNumber,
342 event.getAxisValue(MotionEvent.AXIS_HAT_X),
343 event.getAxisValue(MotionEvent.AXIS_HAT_Y)
344 )
345 }
346 }
347
348 private fun setJoyconAxisInput(event: MotionEvent, axis: Int) {
349 // Joycon support is half dead. Right joystick doesn't work
350 val playerNumber = getPlayerNumber(event.device.controllerNumber, event.deviceId)
351
352 when (axis) {
353 MotionEvent.AXIS_X, MotionEvent.AXIS_Y ->
354 setStickState(
355 playerNumber,
356 NativeLibrary.StickType.STICK_L,
357 event.getAxisValue(MotionEvent.AXIS_X),
358 event.getAxisValue(MotionEvent.AXIS_Y)
359 )
360 MotionEvent.AXIS_Z, MotionEvent.AXIS_RZ ->
361 setStickState(
362 playerNumber,
363 NativeLibrary.StickType.STICK_R,
364 event.getAxisValue(MotionEvent.AXIS_Z),
365 event.getAxisValue(MotionEvent.AXIS_RZ)
366 )
367 MotionEvent.AXIS_RX, MotionEvent.AXIS_RY ->
368 setStickState(
369 playerNumber,
370 NativeLibrary.StickType.STICK_R,
371 event.getAxisValue(MotionEvent.AXIS_RX),
372 event.getAxisValue(MotionEvent.AXIS_RY)
373 )
374 }
375 }
376
377 private fun setRazerAxisInput(event: MotionEvent, axis: Int) {
378 val playerNumber = getPlayerNumber(event.device.controllerNumber, event.deviceId)
379
380 when (axis) {
381 MotionEvent.AXIS_X, MotionEvent.AXIS_Y ->
382 setStickState(
383 playerNumber,
384 NativeLibrary.StickType.STICK_L,
385 event.getAxisValue(MotionEvent.AXIS_X),
386 event.getAxisValue(MotionEvent.AXIS_Y)
387 )
388 MotionEvent.AXIS_Z, MotionEvent.AXIS_RZ ->
389 setStickState(
390 playerNumber,
391 NativeLibrary.StickType.STICK_R,
392 event.getAxisValue(MotionEvent.AXIS_Z),
393 event.getAxisValue(MotionEvent.AXIS_RZ)
394 )
395 MotionEvent.AXIS_BRAKE ->
396 NativeLibrary.onGamePadButtonEvent(
397 playerNumber,
398 NativeLibrary.ButtonType.TRIGGER_ZL,
399 getAxisToButton(event.getAxisValue(MotionEvent.AXIS_BRAKE))
400 )
401 MotionEvent.AXIS_GAS ->
402 NativeLibrary.onGamePadButtonEvent(
403 playerNumber,
404 NativeLibrary.ButtonType.TRIGGER_ZR,
405 getAxisToButton(event.getAxisValue(MotionEvent.AXIS_GAS))
406 )
407 MotionEvent.AXIS_HAT_X, MotionEvent.AXIS_HAT_Y ->
408 setAxisDpadState(
409 playerNumber,
410 event.getAxisValue(MotionEvent.AXIS_HAT_X),
411 event.getAxisValue(MotionEvent.AXIS_HAT_Y)
412 )
413 }
414 }
415
416 fun getGameControllerIds(): Map<Int, Int> {
417 val gameControllerDeviceIds = mutableMapOf<Int, Int>()
418 val deviceIds = InputDevice.getDeviceIds() 55 val deviceIds = InputDevice.getDeviceIds()
419 var controllerSlot = 1 56 var port = 0
57 val inputSettings = NativeConfig.getInputSettings(true)
420 deviceIds.forEach { deviceId -> 58 deviceIds.forEach { deviceId ->
421 InputDevice.getDevice(deviceId)?.apply { 59 InputDevice.getDevice(deviceId)?.apply {
422 // Don't over-assign controllers
423 if (controllerSlot >= 8) {
424 return gameControllerDeviceIds
425 }
426
427 // Verify that the device has gamepad buttons, control sticks, or both. 60 // Verify that the device has gamepad buttons, control sticks, or both.
428 if (sources and InputDevice.SOURCE_GAMEPAD == InputDevice.SOURCE_GAMEPAD || 61 if (sources and InputDevice.SOURCE_GAMEPAD == InputDevice.SOURCE_GAMEPAD ||
429 sources and InputDevice.SOURCE_JOYSTICK == InputDevice.SOURCE_JOYSTICK 62 sources and InputDevice.SOURCE_JOYSTICK == InputDevice.SOURCE_JOYSTICK
430 ) { 63 ) {
431 // This device is a game controller. Store its device ID. 64 if (!gameControllerDeviceIds.contains(controllerNumber)) {
432 if (deviceId and id and vendorId and productId != 0) { 65 gameControllerDeviceIds[controllerNumber] = YuzuPhysicalDevice(
433 // Additionally filter out devices that have no ID 66 this,
434 gameControllerDeviceIds 67 port,
435 .takeIf { !it.contains(deviceId) } 68 inputSettings[port].useSystemVibrator
436 ?.put(deviceId, controllerSlot) 69 )
437 controllerSlot++
438 } 70 }
71 port++
439 } 72 }
440 } 73 }
441 } 74 }
442 return gameControllerDeviceIds 75 return gameControllerDeviceIds
443 } 76 }
77
78 fun updateControllerData() {
79 androidControllers = getDevices()
80 androidControllers.forEach {
81 NativeInput.registerController(it.value)
82 }
83
84 // Register the input overlay on a dedicated port for all player 1 vibrations
85 NativeInput.registerController(YuzuInputOverlayDevice(androidControllers.isEmpty(), 100))
86 registeredControllers.clear()
87 NativeInput.getInputDevices().forEach {
88 registeredControllers.add(ParamPackage(it))
89 }
90 registeredControllers.sortBy { it.get("port", 0) }
91 }
92
93 fun InputDevice.getGUID(): String = String.format("%016x%016x", productId, vendorId)
444} 94}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/LifecycleUtils.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/LifecycleUtils.kt
new file mode 100644
index 000000000..d5c19c681
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/LifecycleUtils.kt
@@ -0,0 +1,38 @@
1// SPDX-FileCopyrightText: 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4package org.yuzu.yuzu_emu.utils
5
6import androidx.lifecycle.Lifecycle
7import androidx.lifecycle.LifecycleOwner
8import androidx.lifecycle.lifecycleScope
9import androidx.lifecycle.repeatOnLifecycle
10import kotlinx.coroutines.flow.Flow
11import kotlinx.coroutines.flow.MutableStateFlow
12import kotlinx.coroutines.launch
13
14/**
15 * Collects this [Flow] with a given [LifecycleOwner].
16 * @param scope [LifecycleOwner] that this [Flow] will be collected with.
17 * @param repeatState When to repeat collection on this [Flow].
18 * @param resetState Optional lambda to reset state of an underlying [MutableStateFlow] after
19 * [stateCollector] has been run.
20 * @param stateCollector Lambda that receives new state.
21 */
22inline fun <reified T> Flow<T>.collect(
23 scope: LifecycleOwner,
24 repeatState: Lifecycle.State = Lifecycle.State.CREATED,
25 crossinline resetState: () -> Unit = {},
26 crossinline stateCollector: (state: T) -> Unit
27) {
28 scope.apply {
29 lifecycleScope.launch {
30 repeatOnLifecycle(repeatState) {
31 this@collect.collect {
32 stateCollector(it)
33 resetState()
34 }
35 }
36 }
37 }
38}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/NativeConfig.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/NativeConfig.kt
index a4c14b3a7..7228f25d2 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/NativeConfig.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/NativeConfig.kt
@@ -6,6 +6,8 @@ package org.yuzu.yuzu_emu.utils
6import org.yuzu.yuzu_emu.model.GameDir 6import org.yuzu.yuzu_emu.model.GameDir
7import org.yuzu.yuzu_emu.overlay.model.OverlayControlData 7import org.yuzu.yuzu_emu.overlay.model.OverlayControlData
8 8
9import org.yuzu.yuzu_emu.features.input.model.PlayerInput
10
9object NativeConfig { 11object NativeConfig {
10 /** 12 /**
11 * Loads global config. 13 * Loads global config.
@@ -168,4 +170,17 @@ object NativeConfig {
168 */ 170 */
169 @Synchronized 171 @Synchronized
170 external fun setOverlayControlData(overlayControlData: Array<OverlayControlData>) 172 external fun setOverlayControlData(overlayControlData: Array<OverlayControlData>)
173
174 @Synchronized
175 external fun getInputSettings(global: Boolean): Array<PlayerInput>
176
177 @Synchronized
178 external fun setInputSettings(value: Array<PlayerInput>, global: Boolean)
179
180 /**
181 * Saves control values for a specific player
182 * Must be used when per game config is loaded
183 */
184 @Synchronized
185 external fun saveControlPlayerValues()
171} 186}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/NfcReader.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/NfcReader.kt
index 68ed66565..331b7ddca 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/NfcReader.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/NfcReader.kt
@@ -14,7 +14,7 @@ import android.os.Build
14import android.os.Handler 14import android.os.Handler
15import android.os.Looper 15import android.os.Looper
16import java.io.IOException 16import java.io.IOException
17import org.yuzu.yuzu_emu.NativeLibrary 17import org.yuzu.yuzu_emu.features.input.NativeInput
18 18
19class NfcReader(private val activity: Activity) { 19class NfcReader(private val activity: Activity) {
20 private var nfcAdapter: NfcAdapter? = null 20 private var nfcAdapter: NfcAdapter? = null
@@ -76,12 +76,12 @@ class NfcReader(private val activity: Activity) {
76 amiibo.connect() 76 amiibo.connect()
77 77
78 val tagData = ntag215ReadAll(amiibo) ?: return 78 val tagData = ntag215ReadAll(amiibo) ?: return
79 NativeLibrary.onReadNfcTag(tagData) 79 NativeInput.onReadNfcTag(tagData)
80 80
81 nfcAdapter?.ignore( 81 nfcAdapter?.ignore(
82 tag, 82 tag,
83 1000, 83 1000,
84 { NativeLibrary.onRemoveNfcTag() }, 84 { NativeInput.onRemoveNfcTag() },
85 Handler(Looper.getMainLooper()) 85 Handler(Looper.getMainLooper())
86 ) 86 )
87 } 87 }
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ParamPackage.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ParamPackage.kt
new file mode 100644
index 000000000..83fc7da3c
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ParamPackage.kt
@@ -0,0 +1,141 @@
1// SPDX-FileCopyrightText: 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4package org.yuzu.yuzu_emu.utils
5
6// Kotlin version of src/common/param_package.h
7class ParamPackage(serialized: String = "") {
8 private val KEY_VALUE_SEPARATOR = ":"
9 private val PARAM_SEPARATOR = ","
10
11 private val ESCAPE_CHARACTER = "$"
12 private val KEY_VALUE_SEPARATOR_ESCAPE = "$0"
13 private val PARAM_SEPARATOR_ESCAPE = "$1"
14 private val ESCAPE_CHARACTER_ESCAPE = "$2"
15
16 private val EMPTY_PLACEHOLDER = "[empty]"
17
18 val data = mutableMapOf<String, String>()
19
20 init {
21 val pairs = serialized.split(PARAM_SEPARATOR)
22 for (pair in pairs) {
23 val keyValue = pair.split(KEY_VALUE_SEPARATOR).toMutableList()
24 if (keyValue.size != 2) {
25 Log.error("[ParamPackage] Invalid key pair $keyValue")
26 continue
27 }
28
29 keyValue.forEachIndexed { i: Int, _: String ->
30 keyValue[i] = keyValue[i].replace(KEY_VALUE_SEPARATOR_ESCAPE, KEY_VALUE_SEPARATOR)
31 keyValue[i] = keyValue[i].replace(PARAM_SEPARATOR_ESCAPE, PARAM_SEPARATOR)
32 keyValue[i] = keyValue[i].replace(ESCAPE_CHARACTER_ESCAPE, ESCAPE_CHARACTER)
33 }
34
35 set(keyValue[0], keyValue[1])
36 }
37 }
38
39 constructor(params: List<Pair<String, String>>) : this() {
40 params.forEach {
41 data[it.first] = it.second
42 }
43 }
44
45 fun serialize(): String {
46 if (data.isEmpty()) {
47 return EMPTY_PLACEHOLDER
48 }
49
50 val result = StringBuilder()
51 data.forEach {
52 val keyValue = mutableListOf(it.key, it.value)
53 keyValue.forEachIndexed { i, _ ->
54 keyValue[i] = keyValue[i].replace(ESCAPE_CHARACTER, ESCAPE_CHARACTER_ESCAPE)
55 keyValue[i] = keyValue[i].replace(PARAM_SEPARATOR, PARAM_SEPARATOR_ESCAPE)
56 keyValue[i] = keyValue[i].replace(KEY_VALUE_SEPARATOR, KEY_VALUE_SEPARATOR_ESCAPE)
57 }
58 result.append("${keyValue[0]}$KEY_VALUE_SEPARATOR${keyValue[1]}$PARAM_SEPARATOR")
59 }
60 return result.removeSuffix(PARAM_SEPARATOR).toString()
61 }
62
63 fun get(key: String, defaultValue: String): String =
64 if (has(key)) {
65 data[key]!!
66 } else {
67 Log.debug("[ParamPackage] key $key not found")
68 defaultValue
69 }
70
71 fun get(key: String, defaultValue: Int): Int =
72 if (has(key)) {
73 try {
74 data[key]!!.toInt()
75 } catch (e: NumberFormatException) {
76 Log.debug("[ParamPackage] failed to convert ${data[key]!!} to int")
77 defaultValue
78 }
79 } else {
80 Log.debug("[ParamPackage] key $key not found")
81 defaultValue
82 }
83
84 private fun Int.toBoolean(): Boolean =
85 if (this == 1) {
86 true
87 } else if (this == 0) {
88 false
89 } else {
90 throw Exception("Tried to convert a value to a boolean that was not 0 or 1!")
91 }
92
93 fun get(key: String, defaultValue: Boolean): Boolean =
94 if (has(key)) {
95 try {
96 get(key, if (defaultValue) 1 else 0).toBoolean()
97 } catch (e: Exception) {
98 Log.debug("[ParamPackage] failed to convert ${data[key]!!} to boolean")
99 defaultValue
100 }
101 } else {
102 Log.debug("[ParamPackage] key $key not found")
103 defaultValue
104 }
105
106 fun get(key: String, defaultValue: Float): Float =
107 if (has(key)) {
108 try {
109 data[key]!!.toFloat()
110 } catch (e: NumberFormatException) {
111 Log.debug("[ParamPackage] failed to convert ${data[key]!!} to float")
112 defaultValue
113 }
114 } else {
115 Log.debug("[ParamPackage] key $key not found")
116 defaultValue
117 }
118
119 fun set(key: String, value: String) {
120 data[key] = value
121 }
122
123 fun set(key: String, value: Int) {
124 data[key] = value.toString()
125 }
126
127 fun Boolean.toInt(): Int = if (this) 1 else 0
128 fun set(key: String, value: Boolean) {
129 data[key] = value.toInt().toString()
130 }
131
132 fun set(key: String, value: Float) {
133 data[key] = value.toString()
134 }
135
136 fun has(key: String): Boolean = data.containsKey(key)
137
138 fun erase(key: String) = data.remove(key)
139
140 fun clear() = data.clear()
141}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ViewUtils.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ViewUtils.kt
index ffbfa9337..244091aec 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ViewUtils.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ViewUtils.kt
@@ -3,8 +3,10 @@
3 3
4package org.yuzu.yuzu_emu.utils 4package org.yuzu.yuzu_emu.utils
5 5
6import android.text.TextUtils
6import android.view.View 7import android.view.View
7import android.view.ViewGroup 8import android.view.ViewGroup
9import android.widget.TextView
8 10
9object ViewUtils { 11object ViewUtils {
10 fun showView(view: View, length: Long = 300) { 12 fun showView(view: View, length: Long = 300) {
@@ -57,4 +59,35 @@ object ViewUtils {
57 } 59 }
58 this.layoutParams = layoutParams 60 this.layoutParams = layoutParams
59 } 61 }
62
63 /**
64 * Shows or hides a view.
65 * @param visible Whether a view will be made View.VISIBLE or View.INVISIBLE/GONE.
66 * @param gone Optional parameter for hiding a view. Uses View.GONE if true and View.INVISIBLE otherwise.
67 */
68 fun View.setVisible(visible: Boolean, gone: Boolean = true) {
69 visibility = if (visible) {
70 View.VISIBLE
71 } else {
72 if (gone) {
73 View.GONE
74 } else {
75 View.INVISIBLE
76 }
77 }
78 }
79
80 /**
81 * Starts a marquee on some text.
82 * @param delay Optional parameter for changing the start delay. 3 seconds of delay by default.
83 */
84 fun TextView.marquee(delay: Long = 3000) {
85 ellipsize = null
86 marqueeRepeatLimit = -1
87 isSingleLine = true
88 postDelayed({
89 ellipsize = TextUtils.TruncateAt.MARQUEE
90 isSelected = true
91 }, delay)
92 }
60} 93}
diff --git a/src/android/app/src/main/jni/CMakeLists.txt b/src/android/app/src/main/jni/CMakeLists.txt
index 20b319c12..ec8ae5c57 100644
--- a/src/android/app/src/main/jni/CMakeLists.txt
+++ b/src/android/app/src/main/jni/CMakeLists.txt
@@ -12,6 +12,7 @@ add_library(yuzu-android SHARED
12 native_log.cpp 12 native_log.cpp
13 android_config.cpp 13 android_config.cpp
14 android_config.h 14 android_config.h
15 native_input.cpp
15) 16)
16 17
17set_property(TARGET yuzu-android PROPERTY IMPORTED_LOCATION ${FFmpeg_LIBRARY_DIR}) 18set_property(TARGET yuzu-android PROPERTY IMPORTED_LOCATION ${FFmpeg_LIBRARY_DIR})
diff --git a/src/android/app/src/main/jni/android_config.cpp b/src/android/app/src/main/jni/android_config.cpp
index e147560c3..a79a64afb 100644
--- a/src/android/app/src/main/jni/android_config.cpp
+++ b/src/android/app/src/main/jni/android_config.cpp
@@ -1,6 +1,8 @@
1// SPDX-FileCopyrightText: 2023 yuzu Emulator Project 1// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include <common/logging/log.h>
5#include <input_common/main.h>
4#include "android_config.h" 6#include "android_config.h"
5#include "android_settings.h" 7#include "android_settings.h"
6#include "common/settings_setting.h" 8#include "common/settings_setting.h"
@@ -32,6 +34,7 @@ void AndroidConfig::ReadAndroidValues() {
32 ReadOverlayValues(); 34 ReadOverlayValues();
33 } 35 }
34 ReadDriverValues(); 36 ReadDriverValues();
37 ReadAndroidControlValues();
35} 38}
36 39
37void AndroidConfig::ReadAndroidUIValues() { 40void AndroidConfig::ReadAndroidUIValues() {
@@ -107,6 +110,76 @@ void AndroidConfig::ReadOverlayValues() {
107 EndGroup(); 110 EndGroup();
108} 111}
109 112
113void AndroidConfig::ReadAndroidPlayerValues(std::size_t player_index) {
114 std::string player_prefix;
115 if (type != ConfigType::InputProfile) {
116 player_prefix.append("player_").append(ToString(player_index)).append("_");
117 }
118
119 auto& player = Settings::values.players.GetValue()[player_index];
120 if (IsCustomConfig()) {
121 const auto profile_name =
122 ReadStringSetting(std::string(player_prefix).append("profile_name"));
123 if (profile_name.empty()) {
124 // Use the global input config
125 player = Settings::values.players.GetValue(true)[player_index];
126 player.profile_name = "";
127 return;
128 }
129 }
130
131 // Android doesn't have default options for controllers. We have the input overlay for that.
132 for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
133 const std::string default_param;
134 auto& player_buttons = player.buttons[i];
135
136 player_buttons = ReadStringSetting(
137 std::string(player_prefix).append(Settings::NativeButton::mapping[i]), default_param);
138 if (player_buttons.empty()) {
139 player_buttons = default_param;
140 }
141 }
142 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
143 const std::string default_param;
144 auto& player_analogs = player.analogs[i];
145
146 player_analogs = ReadStringSetting(
147 std::string(player_prefix).append(Settings::NativeAnalog::mapping[i]), default_param);
148 if (player_analogs.empty()) {
149 player_analogs = default_param;
150 }
151 }
152 for (int i = 0; i < Settings::NativeMotion::NumMotions; ++i) {
153 const std::string default_param;
154 auto& player_motions = player.motions[i];
155
156 player_motions = ReadStringSetting(
157 std::string(player_prefix).append(Settings::NativeMotion::mapping[i]), default_param);
158 if (player_motions.empty()) {
159 player_motions = default_param;
160 }
161 }
162 player.use_system_vibrator = ReadBooleanSetting(
163 std::string(player_prefix).append("use_system_vibrator"), player_index == 0);
164}
165
166void AndroidConfig::ReadAndroidControlValues() {
167 BeginGroup(Settings::TranslateCategory(Settings::Category::Controls));
168
169 Settings::values.players.SetGlobal(!IsCustomConfig());
170 for (std::size_t p = 0; p < Settings::values.players.GetValue().size(); ++p) {
171 ReadAndroidPlayerValues(p);
172 }
173 if (IsCustomConfig()) {
174 EndGroup();
175 return;
176 }
177 // ReadDebugControlValues();
178 // ReadHidbusValues();
179
180 EndGroup();
181}
182
110void AndroidConfig::SaveAndroidValues() { 183void AndroidConfig::SaveAndroidValues() {
111 if (global) { 184 if (global) {
112 SaveAndroidUIValues(); 185 SaveAndroidUIValues();
@@ -114,6 +187,7 @@ void AndroidConfig::SaveAndroidValues() {
114 SaveOverlayValues(); 187 SaveOverlayValues();
115 } 188 }
116 SaveDriverValues(); 189 SaveDriverValues();
190 SaveAndroidControlValues();
117 191
118 WriteToIni(); 192 WriteToIni();
119} 193}
@@ -187,6 +261,52 @@ void AndroidConfig::SaveOverlayValues() {
187 EndGroup(); 261 EndGroup();
188} 262}
189 263
264void AndroidConfig::SaveAndroidPlayerValues(std::size_t player_index) {
265 std::string player_prefix;
266 if (type != ConfigType::InputProfile) {
267 player_prefix = std::string("player_").append(ToString(player_index)).append("_");
268 }
269
270 const auto& player = Settings::values.players.GetValue()[player_index];
271 if (IsCustomConfig() && player.profile_name.empty()) {
272 // No custom profile selected
273 return;
274 }
275
276 const std::string default_param;
277 for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
278 WriteStringSetting(std::string(player_prefix).append(Settings::NativeButton::mapping[i]),
279 player.buttons[i], std::make_optional(default_param));
280 }
281 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
282 WriteStringSetting(std::string(player_prefix).append(Settings::NativeAnalog::mapping[i]),
283 player.analogs[i], std::make_optional(default_param));
284 }
285 for (int i = 0; i < Settings::NativeMotion::NumMotions; ++i) {
286 WriteStringSetting(std::string(player_prefix).append(Settings::NativeMotion::mapping[i]),
287 player.motions[i], std::make_optional(default_param));
288 }
289 WriteBooleanSetting(std::string(player_prefix).append("use_system_vibrator"),
290 player.use_system_vibrator, std::make_optional(player_index == 0));
291}
292
293void AndroidConfig::SaveAndroidControlValues() {
294 BeginGroup(Settings::TranslateCategory(Settings::Category::Controls));
295
296 Settings::values.players.SetGlobal(!IsCustomConfig());
297 for (std::size_t p = 0; p < Settings::values.players.GetValue().size(); ++p) {
298 SaveAndroidPlayerValues(p);
299 }
300 if (IsCustomConfig()) {
301 EndGroup();
302 return;
303 }
304 // SaveDebugControlValues();
305 // SaveHidbusValues();
306
307 EndGroup();
308}
309
190std::vector<Settings::BasicSetting*>& AndroidConfig::FindRelevantList(Settings::Category category) { 310std::vector<Settings::BasicSetting*>& AndroidConfig::FindRelevantList(Settings::Category category) {
191 auto& map = Settings::values.linkage.by_category; 311 auto& map = Settings::values.linkage.by_category;
192 if (map.contains(category)) { 312 if (map.contains(category)) {
@@ -194,3 +314,24 @@ std::vector<Settings::BasicSetting*>& AndroidConfig::FindRelevantList(Settings::
194 } 314 }
195 return AndroidSettings::values.linkage.by_category[category]; 315 return AndroidSettings::values.linkage.by_category[category];
196} 316}
317
318void AndroidConfig::ReadAndroidControlPlayerValues(std::size_t player_index) {
319 BeginGroup(Settings::TranslateCategory(Settings::Category::Controls));
320
321 ReadPlayerValues(player_index);
322 ReadAndroidPlayerValues(player_index);
323
324 EndGroup();
325}
326
327void AndroidConfig::SaveAndroidControlPlayerValues(std::size_t player_index) {
328 BeginGroup(Settings::TranslateCategory(Settings::Category::Controls));
329
330 LOG_DEBUG(Config, "Saving players control configuration values");
331 SavePlayerValues(player_index);
332 SaveAndroidPlayerValues(player_index);
333
334 EndGroup();
335
336 WriteToIni();
337}
diff --git a/src/android/app/src/main/jni/android_config.h b/src/android/app/src/main/jni/android_config.h
index 693e1e3f0..28ef5d0a8 100644
--- a/src/android/app/src/main/jni/android_config.h
+++ b/src/android/app/src/main/jni/android_config.h
@@ -13,7 +13,12 @@ public:
13 void ReloadAllValues() override; 13 void ReloadAllValues() override;
14 void SaveAllValues() override; 14 void SaveAllValues() override;
15 15
16 void ReadAndroidControlPlayerValues(std::size_t player_index);
17 void SaveAndroidControlPlayerValues(std::size_t player_index);
18
16protected: 19protected:
20 void ReadAndroidPlayerValues(std::size_t player_index);
21 void ReadAndroidControlValues();
17 void ReadAndroidValues(); 22 void ReadAndroidValues();
18 void ReadAndroidUIValues(); 23 void ReadAndroidUIValues();
19 void ReadDriverValues(); 24 void ReadDriverValues();
@@ -27,6 +32,8 @@ protected:
27 void ReadUILayoutValues() override {} 32 void ReadUILayoutValues() override {}
28 void ReadMultiplayerValues() override {} 33 void ReadMultiplayerValues() override {}
29 34
35 void SaveAndroidPlayerValues(std::size_t player_index);
36 void SaveAndroidControlValues();
30 void SaveAndroidValues(); 37 void SaveAndroidValues();
31 void SaveAndroidUIValues(); 38 void SaveAndroidUIValues();
32 void SaveDriverValues(); 39 void SaveDriverValues();
diff --git a/src/android/app/src/main/jni/emu_window/emu_window.cpp b/src/android/app/src/main/jni/emu_window/emu_window.cpp
index c927cddda..06db55369 100644
--- a/src/android/app/src/main/jni/emu_window/emu_window.cpp
+++ b/src/android/app/src/main/jni/emu_window/emu_window.cpp
@@ -5,6 +5,7 @@
5 5
6#include "common/android/id_cache.h" 6#include "common/android/id_cache.h"
7#include "common/logging/log.h" 7#include "common/logging/log.h"
8#include "input_common/drivers/android.h"
8#include "input_common/drivers/touch_screen.h" 9#include "input_common/drivers/touch_screen.h"
9#include "input_common/drivers/virtual_amiibo.h" 10#include "input_common/drivers/virtual_amiibo.h"
10#include "input_common/drivers/virtual_gamepad.h" 11#include "input_common/drivers/virtual_gamepad.h"
@@ -24,39 +25,18 @@ void EmuWindow_Android::OnSurfaceChanged(ANativeWindow* surface) {
24 25
25void EmuWindow_Android::OnTouchPressed(int id, float x, float y) { 26void EmuWindow_Android::OnTouchPressed(int id, float x, float y) {
26 const auto [touch_x, touch_y] = MapToTouchScreen(x, y); 27 const auto [touch_x, touch_y] = MapToTouchScreen(x, y);
27 m_input_subsystem->GetTouchScreen()->TouchPressed(touch_x, touch_y, id); 28 EmulationSession::GetInstance().GetInputSubsystem().GetTouchScreen()->TouchPressed(touch_x,
29 touch_y, id);
28} 30}
29 31
30void EmuWindow_Android::OnTouchMoved(int id, float x, float y) { 32void EmuWindow_Android::OnTouchMoved(int id, float x, float y) {
31 const auto [touch_x, touch_y] = MapToTouchScreen(x, y); 33 const auto [touch_x, touch_y] = MapToTouchScreen(x, y);
32 m_input_subsystem->GetTouchScreen()->TouchMoved(touch_x, touch_y, id); 34 EmulationSession::GetInstance().GetInputSubsystem().GetTouchScreen()->TouchMoved(touch_x,
35 touch_y, id);
33} 36}
34 37
35void EmuWindow_Android::OnTouchReleased(int id) { 38void EmuWindow_Android::OnTouchReleased(int id) {
36 m_input_subsystem->GetTouchScreen()->TouchReleased(id); 39 EmulationSession::GetInstance().GetInputSubsystem().GetTouchScreen()->TouchReleased(id);
37}
38
39void EmuWindow_Android::OnGamepadButtonEvent(int player_index, int button_id, bool pressed) {
40 m_input_subsystem->GetVirtualGamepad()->SetButtonState(player_index, button_id, pressed);
41}
42
43void EmuWindow_Android::OnGamepadJoystickEvent(int player_index, int stick_id, float x, float y) {
44 m_input_subsystem->GetVirtualGamepad()->SetStickPosition(player_index, stick_id, x, y);
45}
46
47void EmuWindow_Android::OnGamepadMotionEvent(int player_index, u64 delta_timestamp, float gyro_x,
48 float gyro_y, float gyro_z, float accel_x,
49 float accel_y, float accel_z) {
50 m_input_subsystem->GetVirtualGamepad()->SetMotionState(
51 player_index, delta_timestamp, gyro_x, gyro_y, gyro_z, accel_x, accel_y, accel_z);
52}
53
54void EmuWindow_Android::OnReadNfcTag(std::span<u8> data) {
55 m_input_subsystem->GetVirtualAmiibo()->LoadAmiibo(data);
56}
57
58void EmuWindow_Android::OnRemoveNfcTag() {
59 m_input_subsystem->GetVirtualAmiibo()->CloseAmiibo();
60} 40}
61 41
62void EmuWindow_Android::OnFrameDisplayed() { 42void EmuWindow_Android::OnFrameDisplayed() {
@@ -67,10 +47,9 @@ void EmuWindow_Android::OnFrameDisplayed() {
67 } 47 }
68} 48}
69 49
70EmuWindow_Android::EmuWindow_Android(InputCommon::InputSubsystem* input_subsystem, 50EmuWindow_Android::EmuWindow_Android(ANativeWindow* surface,
71 ANativeWindow* surface,
72 std::shared_ptr<Common::DynamicLibrary> driver_library) 51 std::shared_ptr<Common::DynamicLibrary> driver_library)
73 : m_input_subsystem{input_subsystem}, m_driver_library{driver_library} { 52 : m_driver_library{driver_library} {
74 LOG_INFO(Frontend, "initializing"); 53 LOG_INFO(Frontend, "initializing");
75 54
76 if (!surface) { 55 if (!surface) {
@@ -80,10 +59,4 @@ EmuWindow_Android::EmuWindow_Android(InputCommon::InputSubsystem* input_subsyste
80 59
81 OnSurfaceChanged(surface); 60 OnSurfaceChanged(surface);
82 window_info.type = Core::Frontend::WindowSystemType::Android; 61 window_info.type = Core::Frontend::WindowSystemType::Android;
83
84 m_input_subsystem->Initialize();
85}
86
87EmuWindow_Android::~EmuWindow_Android() {
88 m_input_subsystem->Shutdown();
89} 62}
diff --git a/src/android/app/src/main/jni/emu_window/emu_window.h b/src/android/app/src/main/jni/emu_window/emu_window.h
index a34a0e479..d7b5fc6da 100644
--- a/src/android/app/src/main/jni/emu_window/emu_window.h
+++ b/src/android/app/src/main/jni/emu_window/emu_window.h
@@ -30,22 +30,17 @@ private:
30class EmuWindow_Android final : public Core::Frontend::EmuWindow { 30class EmuWindow_Android final : public Core::Frontend::EmuWindow {
31 31
32public: 32public:
33 EmuWindow_Android(InputCommon::InputSubsystem* input_subsystem, ANativeWindow* surface, 33 EmuWindow_Android(ANativeWindow* surface,
34 std::shared_ptr<Common::DynamicLibrary> driver_library); 34 std::shared_ptr<Common::DynamicLibrary> driver_library);
35 35
36 ~EmuWindow_Android(); 36 ~EmuWindow_Android() = default;
37 37
38 void OnSurfaceChanged(ANativeWindow* surface); 38 void OnSurfaceChanged(ANativeWindow* surface);
39 void OnFrameDisplayed() override;
40
39 void OnTouchPressed(int id, float x, float y); 41 void OnTouchPressed(int id, float x, float y);
40 void OnTouchMoved(int id, float x, float y); 42 void OnTouchMoved(int id, float x, float y);
41 void OnTouchReleased(int id); 43 void OnTouchReleased(int id);
42 void OnGamepadButtonEvent(int player_index, int button_id, bool pressed);
43 void OnGamepadJoystickEvent(int player_index, int stick_id, float x, float y);
44 void OnGamepadMotionEvent(int player_index, u64 delta_timestamp, float gyro_x, float gyro_y,
45 float gyro_z, float accel_x, float accel_y, float accel_z);
46 void OnReadNfcTag(std::span<u8> data);
47 void OnRemoveNfcTag();
48 void OnFrameDisplayed() override;
49 44
50 std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override { 45 std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override {
51 return {std::make_unique<GraphicsContext_Android>(m_driver_library)}; 46 return {std::make_unique<GraphicsContext_Android>(m_driver_library)};
@@ -55,8 +50,6 @@ public:
55 }; 50 };
56 51
57private: 52private:
58 InputCommon::InputSubsystem* m_input_subsystem{};
59
60 float m_window_width{}; 53 float m_window_width{};
61 float m_window_height{}; 54 float m_window_height{};
62 55
diff --git a/src/android/app/src/main/jni/native.cpp b/src/android/app/src/main/jni/native.cpp
index a4d8454e8..4ea82e217 100644
--- a/src/android/app/src/main/jni/native.cpp
+++ b/src/android/app/src/main/jni/native.cpp
@@ -88,6 +88,10 @@ FileSys::ManualContentProvider* EmulationSession::GetContentProvider() {
88 return m_manual_provider.get(); 88 return m_manual_provider.get();
89} 89}
90 90
91InputCommon::InputSubsystem& EmulationSession::GetInputSubsystem() {
92 return m_input_subsystem;
93}
94
91const EmuWindow_Android& EmulationSession::Window() const { 95const EmuWindow_Android& EmulationSession::Window() const {
92 return *m_window; 96 return *m_window;
93} 97}
@@ -198,6 +202,8 @@ void EmulationSession::InitializeSystem(bool reload) {
198 Common::Log::Initialize(); 202 Common::Log::Initialize();
199 Common::Log::SetColorConsoleBackendEnabled(true); 203 Common::Log::SetColorConsoleBackendEnabled(true);
200 Common::Log::Start(); 204 Common::Log::Start();
205
206 m_input_subsystem.Initialize();
201 } 207 }
202 208
203 // Initialize filesystem. 209 // Initialize filesystem.
@@ -222,8 +228,7 @@ Core::SystemResultStatus EmulationSession::InitializeEmulation(const std::string
222 std::scoped_lock lock(m_mutex); 228 std::scoped_lock lock(m_mutex);
223 229
224 // Create the render window. 230 // Create the render window.
225 m_window = 231 m_window = std::make_unique<EmuWindow_Android>(m_native_window, m_vulkan_library);
226 std::make_unique<EmuWindow_Android>(&m_input_subsystem, m_native_window, m_vulkan_library);
227 232
228 // Initialize system. 233 // Initialize system.
229 jauto android_keyboard = std::make_unique<Common::Android::SoftwareKeyboard::AndroidKeyboard>(); 234 jauto android_keyboard = std::make_unique<Common::Android::SoftwareKeyboard::AndroidKeyboard>();
@@ -355,60 +360,6 @@ void EmulationSession::RunEmulation() {
355 m_applet_id = static_cast<int>(Service::AM::AppletId::Application); 360 m_applet_id = static_cast<int>(Service::AM::AppletId::Application);
356} 361}
357 362
358bool EmulationSession::IsHandheldOnly() {
359 jconst npad_style_set = m_system.HIDCore().GetSupportedStyleTag();
360
361 if (npad_style_set.fullkey == 1) {
362 return false;
363 }
364
365 if (npad_style_set.handheld == 0) {
366 return false;
367 }
368
369 return !Settings::IsDockedMode();
370}
371
372void EmulationSession::SetDeviceType([[maybe_unused]] int index, int type) {
373 jauto controller = m_system.HIDCore().GetEmulatedControllerByIndex(index);
374 controller->SetNpadStyleIndex(static_cast<Core::HID::NpadStyleIndex>(type));
375}
376
377void EmulationSession::OnGamepadConnectEvent([[maybe_unused]] int index) {
378 jauto controller = m_system.HIDCore().GetEmulatedControllerByIndex(index);
379
380 // Ensure that player1 is configured correctly and handheld disconnected
381 if (controller->GetNpadIdType() == Core::HID::NpadIdType::Player1) {
382 jauto handheld = m_system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Handheld);
383
384 if (controller->GetNpadStyleIndex() == Core::HID::NpadStyleIndex::Handheld) {
385 handheld->SetNpadStyleIndex(Core::HID::NpadStyleIndex::Fullkey);
386 controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::Fullkey);
387 handheld->Disconnect();
388 }
389 }
390
391 // Ensure that handheld is configured correctly and player 1 disconnected
392 if (controller->GetNpadIdType() == Core::HID::NpadIdType::Handheld) {
393 jauto player1 = m_system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1);
394
395 if (controller->GetNpadStyleIndex() != Core::HID::NpadStyleIndex::Handheld) {
396 player1->SetNpadStyleIndex(Core::HID::NpadStyleIndex::Handheld);
397 controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::Handheld);
398 player1->Disconnect();
399 }
400 }
401
402 if (!controller->IsConnected()) {
403 controller->Connect();
404 }
405}
406
407void EmulationSession::OnGamepadDisconnectEvent([[maybe_unused]] int index) {
408 jauto controller = m_system.HIDCore().GetEmulatedControllerByIndex(index);
409 controller->Disconnect();
410}
411
412Common::Android::SoftwareKeyboard::AndroidKeyboard* EmulationSession::SoftwareKeyboard() { 363Common::Android::SoftwareKeyboard::AndroidKeyboard* EmulationSession::SoftwareKeyboard() {
413 return m_software_keyboard; 364 return m_software_keyboard;
414} 365}
@@ -453,7 +404,9 @@ static Core::SystemResultStatus RunEmulation(const std::string& filepath,
453 const size_t program_index, 404 const size_t program_index,
454 const bool frontend_initiated) { 405 const bool frontend_initiated) {
455 MicroProfileOnThreadCreate("EmuThread"); 406 MicroProfileOnThreadCreate("EmuThread");
456 SCOPE_EXIT({ MicroProfileShutdown(); }); 407 SCOPE_EXIT {
408 MicroProfileShutdown();
409 };
457 410
458 LOG_INFO(Frontend, "starting"); 411 LOG_INFO(Frontend, "starting");
459 412
@@ -462,7 +415,9 @@ static Core::SystemResultStatus RunEmulation(const std::string& filepath,
462 return Core::SystemResultStatus::ErrorLoader; 415 return Core::SystemResultStatus::ErrorLoader;
463 } 416 }
464 417
465 SCOPE_EXIT({ EmulationSession::GetInstance().ShutdownEmulation(); }); 418 SCOPE_EXIT {
419 EmulationSession::GetInstance().ShutdownEmulation();
420 };
466 421
467 jconst result = EmulationSession::GetInstance().InitializeEmulation(filepath, program_index, 422 jconst result = EmulationSession::GetInstance().InitializeEmulation(filepath, program_index,
468 frontend_initiated); 423 frontend_initiated);
@@ -574,14 +529,14 @@ jobjectArray Java_org_yuzu_yuzu_1emu_utils_GpuDriverHelper_getSystemDriverInfo(
574 nullptr, nullptr, file_redirect_dir_, nullptr); 529 nullptr, nullptr, file_redirect_dir_, nullptr);
575 auto driver_library = std::make_shared<Common::DynamicLibrary>(handle); 530 auto driver_library = std::make_shared<Common::DynamicLibrary>(handle);
576 InputCommon::InputSubsystem input_subsystem; 531 InputCommon::InputSubsystem input_subsystem;
577 auto m_window = std::make_unique<EmuWindow_Android>( 532 auto window =
578 &input_subsystem, ANativeWindow_fromSurface(env, j_surf), driver_library); 533 std::make_unique<EmuWindow_Android>(ANativeWindow_fromSurface(env, j_surf), driver_library);
579 534
580 Vulkan::vk::InstanceDispatch dld; 535 Vulkan::vk::InstanceDispatch dld;
581 Vulkan::vk::Instance vk_instance = Vulkan::CreateInstance( 536 Vulkan::vk::Instance vk_instance = Vulkan::CreateInstance(
582 *driver_library, dld, VK_API_VERSION_1_1, Core::Frontend::WindowSystemType::Android); 537 *driver_library, dld, VK_API_VERSION_1_1, Core::Frontend::WindowSystemType::Android);
583 538
584 auto surface = Vulkan::CreateSurface(vk_instance, m_window->GetWindowInfo()); 539 auto surface = Vulkan::CreateSurface(vk_instance, window->GetWindowInfo());
585 540
586 auto device = Vulkan::CreateDevice(vk_instance, dld, *surface); 541 auto device = Vulkan::CreateDevice(vk_instance, dld, *surface);
587 542
@@ -622,103 +577,6 @@ jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_isPaused(JNIEnv* env, jclass claz
622 return static_cast<jboolean>(EmulationSession::GetInstance().IsPaused()); 577 return static_cast<jboolean>(EmulationSession::GetInstance().IsPaused());
623} 578}
624 579
625jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_isHandheldOnly(JNIEnv* env, jclass clazz) {
626 return EmulationSession::GetInstance().IsHandheldOnly();
627}
628
629jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_setDeviceType(JNIEnv* env, jclass clazz,
630 jint j_device, jint j_type) {
631 if (EmulationSession::GetInstance().IsRunning()) {
632 EmulationSession::GetInstance().SetDeviceType(j_device, j_type);
633 }
634 return static_cast<jboolean>(true);
635}
636
637jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadConnectEvent(JNIEnv* env, jclass clazz,
638 jint j_device) {
639 if (EmulationSession::GetInstance().IsRunning()) {
640 EmulationSession::GetInstance().OnGamepadConnectEvent(j_device);
641 }
642 return static_cast<jboolean>(true);
643}
644
645jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadDisconnectEvent(JNIEnv* env, jclass clazz,
646 jint j_device) {
647 if (EmulationSession::GetInstance().IsRunning()) {
648 EmulationSession::GetInstance().OnGamepadDisconnectEvent(j_device);
649 }
650 return static_cast<jboolean>(true);
651}
652jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadButtonEvent(JNIEnv* env, jclass clazz,
653 jint j_device, jint j_button,
654 jint action) {
655 if (EmulationSession::GetInstance().IsRunning()) {
656 // Ensure gamepad is connected
657 EmulationSession::GetInstance().OnGamepadConnectEvent(j_device);
658 EmulationSession::GetInstance().Window().OnGamepadButtonEvent(j_device, j_button,
659 action != 0);
660 }
661 return static_cast<jboolean>(true);
662}
663
664jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadJoystickEvent(JNIEnv* env, jclass clazz,
665 jint j_device, jint stick_id,
666 jfloat x, jfloat y) {
667 if (EmulationSession::GetInstance().IsRunning()) {
668 EmulationSession::GetInstance().Window().OnGamepadJoystickEvent(j_device, stick_id, x, y);
669 }
670 return static_cast<jboolean>(true);
671}
672
673jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadMotionEvent(
674 JNIEnv* env, jclass clazz, jint j_device, jlong delta_timestamp, jfloat gyro_x, jfloat gyro_y,
675 jfloat gyro_z, jfloat accel_x, jfloat accel_y, jfloat accel_z) {
676 if (EmulationSession::GetInstance().IsRunning()) {
677 EmulationSession::GetInstance().Window().OnGamepadMotionEvent(
678 j_device, delta_timestamp, gyro_x, gyro_y, gyro_z, accel_x, accel_y, accel_z);
679 }
680 return static_cast<jboolean>(true);
681}
682
683jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onReadNfcTag(JNIEnv* env, jclass clazz,
684 jbyteArray j_data) {
685 jboolean isCopy{false};
686 std::span<u8> data(reinterpret_cast<u8*>(env->GetByteArrayElements(j_data, &isCopy)),
687 static_cast<size_t>(env->GetArrayLength(j_data)));
688
689 if (EmulationSession::GetInstance().IsRunning()) {
690 EmulationSession::GetInstance().Window().OnReadNfcTag(data);
691 }
692 return static_cast<jboolean>(true);
693}
694
695jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onRemoveNfcTag(JNIEnv* env, jclass clazz) {
696 if (EmulationSession::GetInstance().IsRunning()) {
697 EmulationSession::GetInstance().Window().OnRemoveNfcTag();
698 }
699 return static_cast<jboolean>(true);
700}
701
702void Java_org_yuzu_yuzu_1emu_NativeLibrary_onTouchPressed(JNIEnv* env, jclass clazz, jint id,
703 jfloat x, jfloat y) {
704 if (EmulationSession::GetInstance().IsRunning()) {
705 EmulationSession::GetInstance().Window().OnTouchPressed(id, x, y);
706 }
707}
708
709void Java_org_yuzu_yuzu_1emu_NativeLibrary_onTouchMoved(JNIEnv* env, jclass clazz, jint id,
710 jfloat x, jfloat y) {
711 if (EmulationSession::GetInstance().IsRunning()) {
712 EmulationSession::GetInstance().Window().OnTouchMoved(id, x, y);
713 }
714}
715
716void Java_org_yuzu_yuzu_1emu_NativeLibrary_onTouchReleased(JNIEnv* env, jclass clazz, jint id) {
717 if (EmulationSession::GetInstance().IsRunning()) {
718 EmulationSession::GetInstance().Window().OnTouchReleased(id);
719 }
720}
721
722void Java_org_yuzu_yuzu_1emu_NativeLibrary_initializeSystem(JNIEnv* env, jclass clazz, 580void Java_org_yuzu_yuzu_1emu_NativeLibrary_initializeSystem(JNIEnv* env, jclass clazz,
723 jboolean reload) { 581 jboolean reload) {
724 // Initialize the emulated system. 582 // Initialize the emulated system.
@@ -759,6 +617,7 @@ jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getGpuDriver(JNIEnv* env, jobject
759 617
760void Java_org_yuzu_yuzu_1emu_NativeLibrary_applySettings(JNIEnv* env, jobject jobj) { 618void Java_org_yuzu_yuzu_1emu_NativeLibrary_applySettings(JNIEnv* env, jobject jobj) {
761 EmulationSession::GetInstance().System().ApplySettings(); 619 EmulationSession::GetInstance().System().ApplySettings();
620 EmulationSession::GetInstance().System().HIDCore().ReloadInputDevices();
762} 621}
763 622
764void Java_org_yuzu_yuzu_1emu_NativeLibrary_logSettings(JNIEnv* env, jobject jobj) { 623void Java_org_yuzu_yuzu_1emu_NativeLibrary_logSettings(JNIEnv* env, jobject jobj) {
diff --git a/src/android/app/src/main/jni/native.h b/src/android/app/src/main/jni/native.h
index 47936e305..6a4551ada 100644
--- a/src/android/app/src/main/jni/native.h
+++ b/src/android/app/src/main/jni/native.h
@@ -23,6 +23,7 @@ public:
23 const Core::System& System() const; 23 const Core::System& System() const;
24 Core::System& System(); 24 Core::System& System();
25 FileSys::ManualContentProvider* GetContentProvider(); 25 FileSys::ManualContentProvider* GetContentProvider();
26 InputCommon::InputSubsystem& GetInputSubsystem();
26 27
27 const EmuWindow_Android& Window() const; 28 const EmuWindow_Android& Window() const;
28 EmuWindow_Android& Window(); 29 EmuWindow_Android& Window();
@@ -50,10 +51,6 @@ public:
50 const std::size_t program_index, 51 const std::size_t program_index,
51 const bool frontend_initiated); 52 const bool frontend_initiated);
52 53
53 bool IsHandheldOnly();
54 void SetDeviceType([[maybe_unused]] int index, int type);
55 void OnGamepadConnectEvent([[maybe_unused]] int index);
56 void OnGamepadDisconnectEvent([[maybe_unused]] int index);
57 Common::Android::SoftwareKeyboard::AndroidKeyboard* SoftwareKeyboard(); 54 Common::Android::SoftwareKeyboard::AndroidKeyboard* SoftwareKeyboard();
58 55
59 static void OnEmulationStarted(); 56 static void OnEmulationStarted();
diff --git a/src/android/app/src/main/jni/native_config.cpp b/src/android/app/src/main/jni/native_config.cpp
index 8ae10fbc7..0b26280c6 100644
--- a/src/android/app/src/main/jni/native_config.cpp
+++ b/src/android/app/src/main/jni/native_config.cpp
@@ -3,7 +3,6 @@
3 3
4#include <string> 4#include <string>
5 5
6#include <common/fs/fs_util.h>
7#include <jni.h> 6#include <jni.h>
8 7
9#include "android_config.h" 8#include "android_config.h"
@@ -425,4 +424,120 @@ void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_setOverlayControlData(
425 } 424 }
426} 425}
427 426
427jobjectArray Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getInputSettings(JNIEnv* env, jobject obj,
428 jboolean j_global) {
429 Settings::values.players.SetGlobal(static_cast<bool>(j_global));
430 auto& players = Settings::values.players.GetValue();
431 jobjectArray j_input_settings =
432 env->NewObjectArray(players.size(), Common::Android::GetPlayerInputClass(), nullptr);
433 for (size_t i = 0; i < players.size(); ++i) {
434 auto j_connected = static_cast<jboolean>(players[i].connected);
435
436 jobjectArray j_buttons = env->NewObjectArray(
437 players[i].buttons.size(), Common::Android::GetStringClass(), env->NewStringUTF(""));
438 for (size_t j = 0; j < players[i].buttons.size(); ++j) {
439 env->SetObjectArrayElement(j_buttons, j,
440 Common::Android::ToJString(env, players[i].buttons[j]));
441 }
442 jobjectArray j_analogs = env->NewObjectArray(
443 players[i].analogs.size(), Common::Android::GetStringClass(), env->NewStringUTF(""));
444 for (size_t j = 0; j < players[i].analogs.size(); ++j) {
445 env->SetObjectArrayElement(j_analogs, j,
446 Common::Android::ToJString(env, players[i].analogs[j]));
447 }
448 jobjectArray j_motions = env->NewObjectArray(
449 players[i].motions.size(), Common::Android::GetStringClass(), env->NewStringUTF(""));
450 for (size_t j = 0; j < players[i].motions.size(); ++j) {
451 env->SetObjectArrayElement(j_motions, j,
452 Common::Android::ToJString(env, players[i].motions[j]));
453 }
454
455 auto j_vibration_enabled = static_cast<jboolean>(players[i].vibration_enabled);
456 auto j_vibration_strength = static_cast<jint>(players[i].vibration_strength);
457
458 auto j_body_color_left = static_cast<jlong>(players[i].body_color_left);
459 auto j_body_color_right = static_cast<jlong>(players[i].body_color_right);
460 auto j_button_color_left = static_cast<jlong>(players[i].button_color_left);
461 auto j_button_color_right = static_cast<jlong>(players[i].button_color_right);
462
463 auto j_profile_name = Common::Android::ToJString(env, players[i].profile_name);
464
465 auto j_use_system_vibrator = players[i].use_system_vibrator;
466
467 jobject playerInput = env->NewObject(
468 Common::Android::GetPlayerInputClass(), Common::Android::GetPlayerInputConstructor(),
469 j_connected, j_buttons, j_analogs, j_motions, j_vibration_enabled, j_vibration_strength,
470 j_body_color_left, j_body_color_right, j_button_color_left, j_button_color_right,
471 j_profile_name, j_use_system_vibrator);
472 env->SetObjectArrayElement(j_input_settings, i, playerInput);
473 }
474 return j_input_settings;
475}
476
477void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_setInputSettings(JNIEnv* env, jobject obj,
478 jobjectArray j_value,
479 jboolean j_global) {
480 auto& players = Settings::values.players.GetValue(static_cast<bool>(j_global));
481 int playersSize = env->GetArrayLength(j_value);
482 for (int i = 0; i < playersSize; ++i) {
483 jobject jplayer = env->GetObjectArrayElement(j_value, i);
484
485 players[i].connected = static_cast<bool>(
486 env->GetBooleanField(jplayer, Common::Android::GetPlayerInputConnectedField()));
487
488 auto j_buttons_array = static_cast<jobjectArray>(
489 env->GetObjectField(jplayer, Common::Android::GetPlayerInputButtonsField()));
490 int buttons_size = env->GetArrayLength(j_buttons_array);
491 for (int j = 0; j < buttons_size; ++j) {
492 auto button = static_cast<jstring>(env->GetObjectArrayElement(j_buttons_array, j));
493 players[i].buttons[j] = Common::Android::GetJString(env, button);
494 }
495 auto j_analogs_array = static_cast<jobjectArray>(
496 env->GetObjectField(jplayer, Common::Android::GetPlayerInputAnalogsField()));
497 int analogs_size = env->GetArrayLength(j_analogs_array);
498 for (int j = 0; j < analogs_size; ++j) {
499 auto analog = static_cast<jstring>(env->GetObjectArrayElement(j_analogs_array, j));
500 players[i].analogs[j] = Common::Android::GetJString(env, analog);
501 }
502 auto j_motions_array = static_cast<jobjectArray>(
503 env->GetObjectField(jplayer, Common::Android::GetPlayerInputMotionsField()));
504 int motions_size = env->GetArrayLength(j_motions_array);
505 for (int j = 0; j < motions_size; ++j) {
506 auto motion = static_cast<jstring>(env->GetObjectArrayElement(j_motions_array, j));
507 players[i].motions[j] = Common::Android::GetJString(env, motion);
508 }
509
510 players[i].vibration_enabled = static_cast<bool>(
511 env->GetBooleanField(jplayer, Common::Android::GetPlayerInputVibrationEnabledField()));
512 players[i].vibration_strength = static_cast<int>(
513 env->GetIntField(jplayer, Common::Android::GetPlayerInputVibrationStrengthField()));
514
515 players[i].body_color_left = static_cast<u32>(
516 env->GetLongField(jplayer, Common::Android::GetPlayerInputBodyColorLeftField()));
517 players[i].body_color_right = static_cast<u32>(
518 env->GetLongField(jplayer, Common::Android::GetPlayerInputBodyColorRightField()));
519 players[i].button_color_left = static_cast<u32>(
520 env->GetLongField(jplayer, Common::Android::GetPlayerInputButtonColorLeftField()));
521 players[i].button_color_right = static_cast<u32>(
522 env->GetLongField(jplayer, Common::Android::GetPlayerInputButtonColorRightField()));
523
524 auto profileName = static_cast<jstring>(
525 env->GetObjectField(jplayer, Common::Android::GetPlayerInputProfileNameField()));
526 players[i].profile_name = Common::Android::GetJString(env, profileName);
527
528 players[i].use_system_vibrator =
529 env->GetBooleanField(jplayer, Common::Android::GetPlayerInputUseSystemVibratorField());
530 }
531}
532
533void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_saveControlPlayerValues(JNIEnv* env, jobject obj) {
534 Settings::values.players.SetGlobal(false);
535
536 // Clear all controls from the config in case the user reverted back to globals
537 per_game_config->ClearControlPlayerValues();
538 for (size_t index = 0; index < Settings::values.players.GetValue().size(); ++index) {
539 per_game_config->SaveAndroidControlPlayerValues(index);
540 }
541}
542
428} // extern "C" 543} // extern "C"
diff --git a/src/android/app/src/main/jni/native_input.cpp b/src/android/app/src/main/jni/native_input.cpp
new file mode 100644
index 000000000..37a65f2b8
--- /dev/null
+++ b/src/android/app/src/main/jni/native_input.cpp
@@ -0,0 +1,629 @@
1// SPDX-FileCopyrightText: 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <common/fs/fs.h>
5#include <common/fs/path_util.h>
6#include <common/settings.h>
7#include <hid_core/hid_types.h>
8#include <jni.h>
9
10#include "android_config.h"
11#include "common/android/android_common.h"
12#include "common/android/id_cache.h"
13#include "hid_core/frontend/emulated_controller.h"
14#include "hid_core/hid_core.h"
15#include "input_common/drivers/android.h"
16#include "input_common/drivers/touch_screen.h"
17#include "input_common/drivers/virtual_amiibo.h"
18#include "input_common/drivers/virtual_gamepad.h"
19#include "native.h"
20
21std::unordered_map<std::string, std::unique_ptr<AndroidConfig>> map_profiles;
22
23bool IsHandheldOnly() {
24 const auto npad_style_set =
25 EmulationSession::GetInstance().System().HIDCore().GetSupportedStyleTag();
26
27 if (npad_style_set.fullkey == 1) {
28 return false;
29 }
30
31 if (npad_style_set.handheld == 0) {
32 return false;
33 }
34
35 return !Settings::IsDockedMode();
36}
37
38std::filesystem::path GetNameWithoutExtension(std::filesystem::path filename) {
39 return filename.replace_extension();
40}
41
42bool IsProfileNameValid(std::string_view profile_name) {
43 return profile_name.find_first_of("<>:;\"/\\|,.!?*") == std::string::npos;
44}
45
46bool ProfileExistsInFilesystem(std::string_view profile_name) {
47 return Common::FS::Exists(Common::FS::GetYuzuPath(Common::FS::YuzuPath::ConfigDir) / "input" /
48 fmt::format("{}.ini", profile_name));
49}
50
51bool ProfileExistsInMap(const std::string& profile_name) {
52 return map_profiles.find(profile_name) != map_profiles.end();
53}
54
55bool SaveProfile(const std::string& profile_name, std::size_t player_index) {
56 if (!ProfileExistsInMap(profile_name)) {
57 return false;
58 }
59
60 Settings::values.players.GetValue()[player_index].profile_name = profile_name;
61 map_profiles[profile_name]->SaveAndroidControlPlayerValues(player_index);
62 return true;
63}
64
65bool LoadProfile(std::string& profile_name, std::size_t player_index) {
66 if (!ProfileExistsInMap(profile_name)) {
67 return false;
68 }
69
70 if (!ProfileExistsInFilesystem(profile_name)) {
71 map_profiles.erase(profile_name);
72 return false;
73 }
74
75 LOG_INFO(Config, "Loading input profile `{}`", profile_name);
76
77 Settings::values.players.GetValue()[player_index].profile_name = profile_name;
78 map_profiles[profile_name]->ReadAndroidControlPlayerValues(player_index);
79 return true;
80}
81
82void ApplyControllerConfig(size_t player_index,
83 const std::function<void(Core::HID::EmulatedController*)>& apply) {
84 auto& hid_core = EmulationSession::GetInstance().System().HIDCore();
85 if (player_index == 0) {
86 auto* handheld = hid_core.GetEmulatedController(Core::HID::NpadIdType::Handheld);
87 auto* player_one = hid_core.GetEmulatedController(Core::HID::NpadIdType::Player1);
88 handheld->EnableConfiguration();
89 player_one->EnableConfiguration();
90 apply(handheld);
91 apply(player_one);
92 handheld->DisableConfiguration();
93 player_one->DisableConfiguration();
94 handheld->SaveCurrentConfig();
95 player_one->SaveCurrentConfig();
96 } else {
97 auto* controller = hid_core.GetEmulatedControllerByIndex(player_index);
98 controller->EnableConfiguration();
99 apply(controller);
100 controller->DisableConfiguration();
101 controller->SaveCurrentConfig();
102 }
103}
104
105void ConnectController(size_t player_index, bool connected) {
106 auto& hid_core = EmulationSession::GetInstance().System().HIDCore();
107 if (player_index == 0) {
108 auto* handheld = hid_core.GetEmulatedController(Core::HID::NpadIdType::Handheld);
109 auto* player_one = hid_core.GetEmulatedController(Core::HID::NpadIdType::Player1);
110 handheld->EnableConfiguration();
111 player_one->EnableConfiguration();
112 if (player_one->GetNpadStyleIndex(true) == Core::HID::NpadStyleIndex::Handheld) {
113 if (connected) {
114 handheld->Connect();
115 } else {
116 handheld->Disconnect();
117 }
118 player_one->Disconnect();
119 } else {
120 if (connected) {
121 player_one->Connect();
122 } else {
123 player_one->Disconnect();
124 }
125 handheld->Disconnect();
126 }
127 handheld->DisableConfiguration();
128 player_one->DisableConfiguration();
129 handheld->SaveCurrentConfig();
130 player_one->SaveCurrentConfig();
131 } else {
132 auto* controller = hid_core.GetEmulatedControllerByIndex(player_index);
133 controller->EnableConfiguration();
134 if (connected) {
135 controller->Connect();
136 } else {
137 controller->Disconnect();
138 }
139 controller->DisableConfiguration();
140 controller->SaveCurrentConfig();
141 }
142}
143
144extern "C" {
145
146jboolean Java_org_yuzu_yuzu_1emu_features_input_NativeInput_isHandheldOnly(JNIEnv* env,
147 jobject j_obj) {
148 return IsHandheldOnly();
149}
150
151void Java_org_yuzu_yuzu_1emu_features_input_NativeInput_onGamePadButtonEvent(
152 JNIEnv* env, jobject j_obj, jstring j_guid, jint j_port, jint j_button_id, jint j_action) {
153 EmulationSession::GetInstance().GetInputSubsystem().GetAndroid()->SetButtonState(
154 Common::Android::GetJString(env, j_guid), j_port, j_button_id, j_action != 0);
155}
156
157void Java_org_yuzu_yuzu_1emu_features_input_NativeInput_onGamePadAxisEvent(
158 JNIEnv* env, jobject j_obj, jstring j_guid, jint j_port, jint j_stick_id, jfloat j_value) {
159 EmulationSession::GetInstance().GetInputSubsystem().GetAndroid()->SetAxisPosition(
160 Common::Android::GetJString(env, j_guid), j_port, j_stick_id, j_value);
161}
162
163void Java_org_yuzu_yuzu_1emu_features_input_NativeInput_onGamePadMotionEvent(
164 JNIEnv* env, jobject j_obj, jstring j_guid, jint j_port, jlong j_delta_timestamp,
165 jfloat j_x_gyro, jfloat j_y_gyro, jfloat j_z_gyro, jfloat j_x_accel, jfloat j_y_accel,
166 jfloat j_z_accel) {
167 EmulationSession::GetInstance().GetInputSubsystem().GetAndroid()->SetMotionState(
168 Common::Android::GetJString(env, j_guid), j_port, j_delta_timestamp, j_x_gyro, j_y_gyro,
169 j_z_gyro, j_x_accel, j_y_accel, j_z_accel);
170}
171
172void Java_org_yuzu_yuzu_1emu_features_input_NativeInput_onReadNfcTag(JNIEnv* env, jobject j_obj,
173 jbyteArray j_data) {
174 jboolean isCopy{false};
175 std::span<u8> data(reinterpret_cast<u8*>(env->GetByteArrayElements(j_data, &isCopy)),
176 static_cast<size_t>(env->GetArrayLength(j_data)));
177
178 if (EmulationSession::GetInstance().IsRunning()) {
179 EmulationSession::GetInstance().GetInputSubsystem().GetVirtualAmiibo()->LoadAmiibo(data);
180 }
181}
182
183void Java_org_yuzu_yuzu_1emu_features_input_NativeInput_onRemoveNfcTag(JNIEnv* env, jobject j_obj) {
184 if (EmulationSession::GetInstance().IsRunning()) {
185 EmulationSession::GetInstance().GetInputSubsystem().GetVirtualAmiibo()->CloseAmiibo();
186 }
187}
188
189void Java_org_yuzu_yuzu_1emu_features_input_NativeInput_onTouchPressed(JNIEnv* env, jobject j_obj,
190 jint j_id, jfloat j_x_axis,
191 jfloat j_y_axis) {
192 if (EmulationSession::GetInstance().IsRunning()) {
193 EmulationSession::GetInstance().Window().OnTouchPressed(j_id, j_x_axis, j_y_axis);
194 }
195}
196
197void Java_org_yuzu_yuzu_1emu_features_input_NativeInput_onTouchMoved(JNIEnv* env, jobject j_obj,
198 jint j_id, jfloat j_x_axis,
199 jfloat j_y_axis) {
200 if (EmulationSession::GetInstance().IsRunning()) {
201 EmulationSession::GetInstance().Window().OnTouchMoved(j_id, j_x_axis, j_y_axis);
202 }
203}
204
205void Java_org_yuzu_yuzu_1emu_features_input_NativeInput_onTouchReleased(JNIEnv* env, jobject j_obj,
206 jint j_id) {
207 if (EmulationSession::GetInstance().IsRunning()) {
208 EmulationSession::GetInstance().Window().OnTouchReleased(j_id);
209 }
210}
211
212void Java_org_yuzu_yuzu_1emu_features_input_NativeInput_onOverlayButtonEventImpl(
213 JNIEnv* env, jobject j_obj, jint j_port, jint j_button_id, jint j_action) {
214 if (EmulationSession::GetInstance().IsRunning()) {
215 EmulationSession::GetInstance().GetInputSubsystem().GetVirtualGamepad()->SetButtonState(
216 j_port, j_button_id, j_action == 1);
217 }
218}
219
220void Java_org_yuzu_yuzu_1emu_features_input_NativeInput_onOverlayJoystickEventImpl(
221 JNIEnv* env, jobject j_obj, jint j_port, jint j_stick_id, jfloat j_x_axis, jfloat j_y_axis) {
222 if (EmulationSession::GetInstance().IsRunning()) {
223 EmulationSession::GetInstance().GetInputSubsystem().GetVirtualGamepad()->SetStickPosition(
224 j_port, j_stick_id, j_x_axis, j_y_axis);
225 }
226}
227
228void Java_org_yuzu_yuzu_1emu_features_input_NativeInput_onDeviceMotionEvent(
229 JNIEnv* env, jobject j_obj, jint j_port, jlong j_delta_timestamp, jfloat j_x_gyro,
230 jfloat j_y_gyro, jfloat j_z_gyro, jfloat j_x_accel, jfloat j_y_accel, jfloat j_z_accel) {
231 if (EmulationSession::GetInstance().IsRunning()) {
232 EmulationSession::GetInstance().GetInputSubsystem().GetVirtualGamepad()->SetMotionState(
233 j_port, j_delta_timestamp, j_x_gyro, j_y_gyro, j_z_gyro, j_x_accel, j_y_accel,
234 j_z_accel);
235 }
236}
237
238void Java_org_yuzu_yuzu_1emu_features_input_NativeInput_reloadInputDevices(JNIEnv* env,
239 jobject j_obj) {
240 EmulationSession::GetInstance().System().HIDCore().ReloadInputDevices();
241}
242
243void Java_org_yuzu_yuzu_1emu_features_input_NativeInput_registerController(JNIEnv* env,
244 jobject j_obj,
245 jobject j_device) {
246 EmulationSession::GetInstance().GetInputSubsystem().GetAndroid()->RegisterController(j_device);
247}
248
249jobjectArray Java_org_yuzu_yuzu_1emu_features_input_NativeInput_getInputDevices(JNIEnv* env,
250 jobject j_obj) {
251 auto devices = EmulationSession::GetInstance().GetInputSubsystem().GetInputDevices();
252 jobjectArray jdevices = env->NewObjectArray(devices.size(), Common::Android::GetStringClass(),
253 Common::Android::ToJString(env, ""));
254 for (size_t i = 0; i < devices.size(); ++i) {
255 env->SetObjectArrayElement(jdevices, i,
256 Common::Android::ToJString(env, devices[i].Serialize()));
257 }
258 return jdevices;
259}
260
261void Java_org_yuzu_yuzu_1emu_features_input_NativeInput_loadInputProfiles(JNIEnv* env,
262 jobject j_obj) {
263 map_profiles.clear();
264 const auto input_profile_loc =
265 Common::FS::GetYuzuPath(Common::FS::YuzuPath::ConfigDir) / "input";
266
267 if (Common::FS::IsDir(input_profile_loc)) {
268 Common::FS::IterateDirEntries(
269 input_profile_loc,
270 [&](const std::filesystem::path& full_path) {
271 const auto filename = full_path.filename();
272 const auto name_without_ext =
273 Common::FS::PathToUTF8String(GetNameWithoutExtension(filename));
274
275 if (filename.extension() == ".ini" && IsProfileNameValid(name_without_ext)) {
276 map_profiles.insert_or_assign(
277 name_without_ext, std::make_unique<AndroidConfig>(
278 name_without_ext, Config::ConfigType::InputProfile));
279 }
280
281 return true;
282 },
283 Common::FS::DirEntryFilter::File);
284 }
285}
286
287jobjectArray Java_org_yuzu_yuzu_1emu_features_input_NativeInput_getInputProfileNames(
288 JNIEnv* env, jobject j_obj) {
289 std::vector<std::string> profile_names;
290 profile_names.reserve(map_profiles.size());
291
292 auto it = map_profiles.cbegin();
293 while (it != map_profiles.cend()) {
294 const auto& [profile_name, config] = *it;
295 if (!ProfileExistsInFilesystem(profile_name)) {
296 it = map_profiles.erase(it);
297 continue;
298 }
299
300 profile_names.push_back(profile_name);
301 ++it;
302 }
303
304 std::stable_sort(profile_names.begin(), profile_names.end());
305
306 jobjectArray j_profile_names =
307 env->NewObjectArray(profile_names.size(), Common::Android::GetStringClass(),
308 Common::Android::ToJString(env, ""));
309 for (size_t i = 0; i < profile_names.size(); ++i) {
310 env->SetObjectArrayElement(j_profile_names, i,
311 Common::Android::ToJString(env, profile_names[i]));
312 }
313
314 return j_profile_names;
315}
316
317jboolean Java_org_yuzu_yuzu_1emu_features_input_NativeInput_isProfileNameValid(JNIEnv* env,
318 jobject j_obj,
319 jstring j_name) {
320 return Common::Android::GetJString(env, j_name).find_first_of("<>:;\"/\\|,.!?*") ==
321 std::string::npos;
322}
323
324jboolean Java_org_yuzu_yuzu_1emu_features_input_NativeInput_createProfile(JNIEnv* env,
325 jobject j_obj,
326 jstring j_name,
327 jint j_player_index) {
328 auto profile_name = Common::Android::GetJString(env, j_name);
329 if (ProfileExistsInMap(profile_name)) {
330 return false;
331 }
332
333 map_profiles.insert_or_assign(
334 profile_name,
335 std::make_unique<AndroidConfig>(profile_name, Config::ConfigType::InputProfile));
336
337 return SaveProfile(profile_name, j_player_index);
338}
339
340jboolean Java_org_yuzu_yuzu_1emu_features_input_NativeInput_deleteProfile(JNIEnv* env,
341 jobject j_obj,
342 jstring j_name,
343 jint j_player_index) {
344 auto profile_name = Common::Android::GetJString(env, j_name);
345 if (!ProfileExistsInMap(profile_name)) {
346 return false;
347 }
348
349 if (!ProfileExistsInFilesystem(profile_name) ||
350 Common::FS::RemoveFile(map_profiles[profile_name]->GetConfigFilePath())) {
351 map_profiles.erase(profile_name);
352 }
353
354 Settings::values.players.GetValue()[j_player_index].profile_name = "";
355 return !ProfileExistsInMap(profile_name) && !ProfileExistsInFilesystem(profile_name);
356}
357
358jboolean Java_org_yuzu_yuzu_1emu_features_input_NativeInput_loadProfile(JNIEnv* env, jobject j_obj,
359 jstring j_name,
360 jint j_player_index) {
361 auto profile_name = Common::Android::GetJString(env, j_name);
362 return LoadProfile(profile_name, j_player_index);
363}
364
365jboolean Java_org_yuzu_yuzu_1emu_features_input_NativeInput_saveProfile(JNIEnv* env, jobject j_obj,
366 jstring j_name,
367 jint j_player_index) {
368 auto profile_name = Common::Android::GetJString(env, j_name);
369 return SaveProfile(profile_name, j_player_index);
370}
371
372void Java_org_yuzu_yuzu_1emu_features_input_NativeInput_loadPerGameConfiguration(
373 JNIEnv* env, jobject j_obj, jint j_player_index, jint j_selected_index,
374 jstring j_selected_profile_name) {
375 static constexpr size_t HANDHELD_INDEX = 8;
376
377 auto& hid_core = EmulationSession::GetInstance().System().HIDCore();
378 Settings::values.players.SetGlobal(false);
379
380 auto profile_name = Common::Android::GetJString(env, j_selected_profile_name);
381 auto* emulated_controller = hid_core.GetEmulatedControllerByIndex(j_player_index);
382
383 if (j_selected_index == 0) {
384 Settings::values.players.GetValue()[j_player_index].profile_name = "";
385 if (j_player_index == 0) {
386 Settings::values.players.GetValue()[HANDHELD_INDEX] = {};
387 }
388 Settings::values.players.SetGlobal(true);
389 emulated_controller->ReloadFromSettings();
390 return;
391 }
392 if (profile_name.empty()) {
393 return;
394 }
395 auto& player = Settings::values.players.GetValue()[j_player_index];
396 auto& global_player = Settings::values.players.GetValue(true)[j_player_index];
397 player.profile_name = profile_name;
398 global_player.profile_name = profile_name;
399 // Read from the profile into the custom player settings
400 LoadProfile(profile_name, j_player_index);
401 // Make sure the controller is connected
402 player.connected = true;
403
404 emulated_controller->ReloadFromSettings();
405
406 if (j_player_index > 0) {
407 return;
408 }
409 // Handle Handheld cases
410 auto& handheld_player = Settings::values.players.GetValue()[HANDHELD_INDEX];
411 auto* handheld_controller = hid_core.GetEmulatedController(Core::HID::NpadIdType::Handheld);
412 if (player.controller_type == Settings::ControllerType::Handheld) {
413 handheld_player = player;
414 } else {
415 handheld_player = {};
416 }
417 handheld_controller->ReloadFromSettings();
418}
419
420void Java_org_yuzu_yuzu_1emu_features_input_NativeInput_beginMapping(JNIEnv* env, jobject j_obj,
421 jint jtype) {
422 EmulationSession::GetInstance().GetInputSubsystem().BeginMapping(
423 static_cast<InputCommon::Polling::InputType>(jtype));
424}
425
426jstring Java_org_yuzu_yuzu_1emu_features_input_NativeInput_getNextInput(JNIEnv* env,
427 jobject j_obj) {
428 return Common::Android::ToJString(
429 env, EmulationSession::GetInstance().GetInputSubsystem().GetNextInput().Serialize());
430}
431
432void Java_org_yuzu_yuzu_1emu_features_input_NativeInput_stopMapping(JNIEnv* env, jobject j_obj) {
433 EmulationSession::GetInstance().GetInputSubsystem().StopMapping();
434}
435
436void Java_org_yuzu_yuzu_1emu_features_input_NativeInput_updateMappingsWithDefaultImpl(
437 JNIEnv* env, jobject j_obj, jint j_player_index, jstring j_device_params,
438 jstring j_display_name) {
439 auto& input_subsystem = EmulationSession::GetInstance().GetInputSubsystem();
440
441 // Clear all previous mappings
442 for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; ++button_id) {
443 ApplyControllerConfig(j_player_index, [&](Core::HID::EmulatedController* controller) {
444 controller->SetButtonParam(button_id, {});
445 });
446 }
447 for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; ++analog_id) {
448 ApplyControllerConfig(j_player_index, [&](Core::HID::EmulatedController* controller) {
449 controller->SetStickParam(analog_id, {});
450 });
451 }
452
453 // Apply new mappings
454 auto device = Common::ParamPackage(Common::Android::GetJString(env, j_device_params));
455 auto button_mappings = input_subsystem.GetButtonMappingForDevice(device);
456 auto analog_mappings = input_subsystem.GetAnalogMappingForDevice(device);
457 auto display_name = Common::Android::GetJString(env, j_display_name);
458 for (const auto& button_mapping : button_mappings) {
459 const std::size_t index = button_mapping.first;
460 auto named_mapping = button_mapping.second;
461 named_mapping.Set("display", display_name);
462 ApplyControllerConfig(j_player_index, [&](Core::HID::EmulatedController* controller) {
463 controller->SetButtonParam(index, named_mapping);
464 });
465 }
466 for (const auto& analog_mapping : analog_mappings) {
467 const std::size_t index = analog_mapping.first;
468 auto named_mapping = analog_mapping.second;
469 named_mapping.Set("display", display_name);
470 ApplyControllerConfig(j_player_index, [&](Core::HID::EmulatedController* controller) {
471 controller->SetStickParam(index, named_mapping);
472 });
473 }
474}
475
476jstring Java_org_yuzu_yuzu_1emu_features_input_NativeInput_getButtonParamImpl(JNIEnv* env,
477 jobject j_obj,
478 jint j_player_index,
479 jint j_button) {
480 return Common::Android::ToJString(env, EmulationSession::GetInstance()
481 .System()
482 .HIDCore()
483 .GetEmulatedControllerByIndex(j_player_index)
484 ->GetButtonParam(j_button)
485 .Serialize());
486}
487
488void Java_org_yuzu_yuzu_1emu_features_input_NativeInput_setButtonParamImpl(
489 JNIEnv* env, jobject j_obj, jint j_player_index, jint j_button_id, jstring j_param) {
490 ApplyControllerConfig(j_player_index, [&](Core::HID::EmulatedController* controller) {
491 controller->SetButtonParam(j_button_id,
492 Common::ParamPackage(Common::Android::GetJString(env, j_param)));
493 });
494}
495
496jstring Java_org_yuzu_yuzu_1emu_features_input_NativeInput_getStickParamImpl(JNIEnv* env,
497 jobject j_obj,
498 jint j_player_index,
499 jint j_stick) {
500 return Common::Android::ToJString(env, EmulationSession::GetInstance()
501 .System()
502 .HIDCore()
503 .GetEmulatedControllerByIndex(j_player_index)
504 ->GetStickParam(j_stick)
505 .Serialize());
506}
507
508void Java_org_yuzu_yuzu_1emu_features_input_NativeInput_setStickParamImpl(
509 JNIEnv* env, jobject j_obj, jint j_player_index, jint j_stick_id, jstring j_param) {
510 ApplyControllerConfig(j_player_index, [&](Core::HID::EmulatedController* controller) {
511 controller->SetStickParam(j_stick_id,
512 Common::ParamPackage(Common::Android::GetJString(env, j_param)));
513 });
514}
515
516jint Java_org_yuzu_yuzu_1emu_features_input_NativeInput_getButtonNameImpl(JNIEnv* env,
517 jobject j_obj,
518 jstring j_param) {
519 return static_cast<jint>(EmulationSession::GetInstance().GetInputSubsystem().GetButtonName(
520 Common::ParamPackage(Common::Android::GetJString(env, j_param))));
521}
522
523jintArray Java_org_yuzu_yuzu_1emu_features_input_NativeInput_getSupportedStyleTagsImpl(
524 JNIEnv* env, jobject j_obj, jint j_player_index) {
525 auto& hid_core = EmulationSession::GetInstance().System().HIDCore();
526 const auto npad_style_set = hid_core.GetSupportedStyleTag();
527 std::vector<s32> supported_indexes;
528 if (npad_style_set.fullkey == 1) {
529 supported_indexes.push_back(static_cast<u32>(Core::HID::NpadStyleIndex::Fullkey));
530 }
531
532 if (npad_style_set.joycon_dual == 1) {
533 supported_indexes.push_back(static_cast<u32>(Core::HID::NpadStyleIndex::JoyconDual));
534 }
535
536 if (npad_style_set.joycon_left == 1) {
537 supported_indexes.push_back(static_cast<u32>(Core::HID::NpadStyleIndex::JoyconLeft));
538 }
539
540 if (npad_style_set.joycon_right == 1) {
541 supported_indexes.push_back(static_cast<u32>(Core::HID::NpadStyleIndex::JoyconRight));
542 }
543
544 if (j_player_index == 0 && npad_style_set.handheld == 1) {
545 supported_indexes.push_back(static_cast<u32>(Core::HID::NpadStyleIndex::Handheld));
546 }
547
548 if (npad_style_set.gamecube == 1) {
549 supported_indexes.push_back(static_cast<u32>(Core::HID::NpadStyleIndex::GameCube));
550 }
551
552 jintArray j_supported_indexes = env->NewIntArray(supported_indexes.size());
553 env->SetIntArrayRegion(j_supported_indexes, 0, supported_indexes.size(),
554 supported_indexes.data());
555 return j_supported_indexes;
556}
557
558jint Java_org_yuzu_yuzu_1emu_features_input_NativeInput_getStyleIndexImpl(JNIEnv* env,
559 jobject j_obj,
560 jint j_player_index) {
561 return static_cast<s32>(EmulationSession::GetInstance()
562 .System()
563 .HIDCore()
564 .GetEmulatedControllerByIndex(j_player_index)
565 ->GetNpadStyleIndex(true));
566}
567
568void Java_org_yuzu_yuzu_1emu_features_input_NativeInput_setStyleIndexImpl(JNIEnv* env,
569 jobject j_obj,
570 jint j_player_index,
571 jint j_style_index) {
572 auto& hid_core = EmulationSession::GetInstance().System().HIDCore();
573 auto type = static_cast<Core::HID::NpadStyleIndex>(j_style_index);
574 ApplyControllerConfig(j_player_index, [&](Core::HID::EmulatedController* controller) {
575 controller->SetNpadStyleIndex(type);
576 });
577 if (j_player_index == 0) {
578 auto* handheld = hid_core.GetEmulatedController(Core::HID::NpadIdType::Handheld);
579 auto* player_one = hid_core.GetEmulatedController(Core::HID::NpadIdType::Player1);
580 ConnectController(j_player_index,
581 player_one->IsConnected(true) || handheld->IsConnected(true));
582 }
583}
584
585jboolean Java_org_yuzu_yuzu_1emu_features_input_NativeInput_isControllerImpl(JNIEnv* env,
586 jobject j_obj,
587 jstring jparams) {
588 return static_cast<jint>(EmulationSession::GetInstance().GetInputSubsystem().IsController(
589 Common::ParamPackage(Common::Android::GetJString(env, jparams))));
590}
591
592jboolean Java_org_yuzu_yuzu_1emu_features_input_NativeInput_getIsConnected(JNIEnv* env,
593 jobject j_obj,
594 jint j_player_index) {
595 auto& hid_core = EmulationSession::GetInstance().System().HIDCore();
596 auto* controller = hid_core.GetEmulatedControllerByIndex(static_cast<size_t>(j_player_index));
597 if (j_player_index == 0 &&
598 controller->GetNpadStyleIndex(true) == Core::HID::NpadStyleIndex::Handheld) {
599 return hid_core.GetEmulatedController(Core::HID::NpadIdType::Handheld)->IsConnected(true);
600 }
601 return controller->IsConnected(true);
602}
603
604void Java_org_yuzu_yuzu_1emu_features_input_NativeInput_connectControllersImpl(
605 JNIEnv* env, jobject j_obj, jbooleanArray j_connected) {
606 jboolean isCopy = false;
607 auto j_connected_array_size = env->GetArrayLength(j_connected);
608 jboolean* j_connected_array = env->GetBooleanArrayElements(j_connected, &isCopy);
609 for (int i = 0; i < j_connected_array_size; ++i) {
610 ConnectController(i, j_connected_array[i]);
611 }
612}
613
614void Java_org_yuzu_yuzu_1emu_features_input_NativeInput_resetControllerMappings(
615 JNIEnv* env, jobject j_obj, jint j_player_index) {
616 // Clear all previous mappings
617 for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; ++button_id) {
618 ApplyControllerConfig(j_player_index, [&](Core::HID::EmulatedController* controller) {
619 controller->SetButtonParam(button_id, {});
620 });
621 }
622 for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; ++analog_id) {
623 ApplyControllerConfig(j_player_index, [&](Core::HID::EmulatedController* controller) {
624 controller->SetStickParam(analog_id, {});
625 });
626 }
627}
628
629} // extern "C"
diff --git a/src/android/app/src/main/res/drawable/button_anim.xml b/src/android/app/src/main/res/drawable/button_anim.xml
new file mode 100644
index 000000000..ccdc5ca6a
--- /dev/null
+++ b/src/android/app/src/main/res/drawable/button_anim.xml
@@ -0,0 +1,142 @@
1<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
2 xmlns:aapt="http://schemas.android.com/aapt">
3 <aapt:attr name="android:drawable">
4 <vector
5 android:width="1000dp"
6 android:height="1000dp"
7 android:viewportWidth="1000"
8 android:viewportHeight="1000">
9 <group android:name="_R_G">
10 <group
11 android:name="_R_G_L_0_G"
12 android:pivotX="100"
13 android:pivotY="100"
14 android:scaleX="4.5"
15 android:scaleY="4.5"
16 android:translateX="400"
17 android:translateY="400">
18 <path
19 android:name="_R_G_L_0_G_D_0_P_0"
20 android:fillAlpha="1"
21 android:fillColor="?attr/colorSecondaryContainer"
22 android:fillType="nonZero"
23 android:pathData=" M198.56 100 C198.56,154.43 154.43,198.56 100,198.56 C45.57,198.56 1.44,154.43 1.44,100 C1.44,45.57 45.57,1.44 100,1.44 C154.43,1.44 198.56,45.57 198.56,100c " />
24 <path
25 android:name="_R_G_L_0_G_D_2_P_0"
26 android:fillAlpha="0.8"
27 android:fillColor="?attr/colorOnSecondaryContainer"
28 android:fillType="nonZero"
29 android:pathData=" M50.14 151.21 C50.53,150.18 89.6,49.87 90.1,48.63 C90.1,48.63 90.67,47.2 90.67,47.2 C90.67,47.2 101.67,47.2 101.67,47.2 C101.67,47.2 112.67,47.2 112.67,47.2 C112.67,47.2 133.47,99.12 133.47,99.12 C144.91,127.68 154.32,151.17 154.38,151.33 C154.47,151.56 152.2,151.6 143.14,151.55 C143.14,151.55 131.79,151.48 131.79,151.48 C131.79,151.48 127.22,139.57 127.22,139.57 C127.22,139.57 122.65,127.66 122.65,127.66 C122.65,127.66 101.68,127.73 101.68,127.73 C101.68,127.73 80.71,127.8 80.71,127.8 C80.71,127.8 76.38,139.71 76.38,139.71 C76.38,139.71 72.06,151.62 72.06,151.62 C72.06,151.62 61.02,151.62 61.02,151.62 C50.61,151.62 50,151.55 50.14,151.22 C50.14,151.22 50.14,151.21 50.14,151.21c M115.86 110.06 C115.8,109.91 112.55,101.13 108.62,90.56 C104.7,80 101.42,71.43 101.34,71.53 C101.22,71.66 92.84,94.61 87.25,110.06 C87.17,110.29 90.13,110.34 101.56,110.34 C113,110.34 115.95,110.28 115.86,110.06c " />
30 </group>
31 </group>
32 <group android:name="time_group" />
33 </vector>
34 </aapt:attr>
35 <target android:name="_R_G_L_0_G">
36 <aapt:attr name="android:animation">
37 <set android:ordering="together">
38 <objectAnimator
39 android:duration="100"
40 android:propertyName="scaleX"
41 android:startOffset="0"
42 android:valueFrom="4.5"
43 android:valueTo="3.75"
44 android:valueType="floatType">
45 <aapt:attr name="android:interpolator">
46 <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0" />
47 </aapt:attr>
48 </objectAnimator>
49 <objectAnimator
50 android:duration="100"
51 android:propertyName="scaleY"
52 android:startOffset="0"
53 android:valueFrom="4.5"
54 android:valueTo="3.75"
55 android:valueType="floatType">
56 <aapt:attr name="android:interpolator">
57 <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0" />
58 </aapt:attr>
59 </objectAnimator>
60 <objectAnimator
61 android:duration="234"
62 android:propertyName="scaleX"
63 android:startOffset="100"
64 android:valueFrom="3.75"
65 android:valueTo="3.75"
66 android:valueType="floatType">
67 <aapt:attr name="android:interpolator">
68 <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0" />
69 </aapt:attr>
70 </objectAnimator>
71 <objectAnimator
72 android:duration="234"
73 android:propertyName="scaleY"
74 android:startOffset="100"
75 android:valueFrom="3.75"
76 android:valueTo="3.75"
77 android:valueType="floatType">
78 <aapt:attr name="android:interpolator">
79 <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0" />
80 </aapt:attr>
81 </objectAnimator>
82 <objectAnimator
83 android:duration="167"
84 android:propertyName="scaleX"
85 android:startOffset="334"
86 android:valueFrom="3.75"
87 android:valueTo="4.75"
88 android:valueType="floatType">
89 <aapt:attr name="android:interpolator">
90 <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0" />
91 </aapt:attr>
92 </objectAnimator>
93 <objectAnimator
94 android:duration="167"
95 android:propertyName="scaleY"
96 android:startOffset="334"
97 android:valueFrom="3.75"
98 android:valueTo="4.75"
99 android:valueType="floatType">
100 <aapt:attr name="android:interpolator">
101 <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0" />
102 </aapt:attr>
103 </objectAnimator>
104 <objectAnimator
105 android:duration="67"
106 android:propertyName="scaleX"
107 android:startOffset="501"
108 android:valueFrom="4.75"
109 android:valueTo="4.5"
110 android:valueType="floatType">
111 <aapt:attr name="android:interpolator">
112 <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0" />
113 </aapt:attr>
114 </objectAnimator>
115 <objectAnimator
116 android:duration="67"
117 android:propertyName="scaleY"
118 android:startOffset="501"
119 android:valueFrom="4.75"
120 android:valueTo="4.5"
121 android:valueType="floatType">
122 <aapt:attr name="android:interpolator">
123 <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0" />
124 </aapt:attr>
125 </objectAnimator>
126 </set>
127 </aapt:attr>
128 </target>
129 <target android:name="time_group">
130 <aapt:attr name="android:animation">
131 <set android:ordering="together">
132 <objectAnimator
133 android:duration="1034"
134 android:propertyName="translateX"
135 android:startOffset="0"
136 android:valueFrom="0"
137 android:valueTo="1"
138 android:valueType="floatType" />
139 </set>
140 </aapt:attr>
141 </target>
142</animated-vector>
diff --git a/src/android/app/src/main/res/drawable/ic_controller_disconnected.xml b/src/android/app/src/main/res/drawable/ic_controller_disconnected.xml
new file mode 100644
index 000000000..8e3c66f74
--- /dev/null
+++ b/src/android/app/src/main/res/drawable/ic_controller_disconnected.xml
@@ -0,0 +1,9 @@
1<vector xmlns:android="http://schemas.android.com/apk/res/android"
2 android:width="24dp"
3 android:height="24dp"
4 android:viewportWidth="960"
5 android:viewportHeight="960">
6 <path
7 android:fillColor="?attr/colorControlNormal"
8 android:pathData="M700,480q-25,0 -42.5,-17.5T640,420q0,-25 17.5,-42.5T700,360q25,0 42.5,17.5T760,420q0,25 -17.5,42.5T700,480ZM366,480ZM280,600v-80h-80v-80h80v-80h80v80h80v80h-80v80h-80ZM160,720q-33,0 -56.5,-23.5T80,640v-320q0,-34 24,-57.5t58,-23.5h77l81,81L160,320v320h366L55,169l57,-57 736,736 -57,57 -185,-185L160,720ZM880,640q0,26 -14,46t-37,29l-29,-29v-366L434,320l-80,-80h446q33,0 56.5,23.5T880,320v320ZM617,503Z" />
9</vector>
diff --git a/src/android/app/src/main/res/drawable/ic_more_vert.xml b/src/android/app/src/main/res/drawable/ic_more_vert.xml
new file mode 100644
index 000000000..9f62ac595
--- /dev/null
+++ b/src/android/app/src/main/res/drawable/ic_more_vert.xml
@@ -0,0 +1,9 @@
1<vector xmlns:android="http://schemas.android.com/apk/res/android"
2 android:height="24dp"
3 android:viewportHeight="24"
4 android:viewportWidth="24"
5 android:width="24dp">
6 <path
7 android:fillColor="?attr/colorControlNormal"
8 android:pathData="M12,8c1.1,0 2,-0.9 2,-2s-0.9,-2 -2,-2 -2,0.9 -2,2 0.9,2 2,2zM12,10c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2zM12,16c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2z" />
9</vector>
diff --git a/src/android/app/src/main/res/drawable/ic_new_label.xml b/src/android/app/src/main/res/drawable/ic_new_label.xml
new file mode 100644
index 000000000..fac562c26
--- /dev/null
+++ b/src/android/app/src/main/res/drawable/ic_new_label.xml
@@ -0,0 +1,9 @@
1<vector xmlns:android="http://schemas.android.com/apk/res/android"
2 android:width="24dp"
3 android:height="24dp"
4 android:viewportWidth="24"
5 android:viewportHeight="24">
6 <path
7 android:fillColor="?attr/colorControlNormal"
8 android:pathData="M21,12l-4.37,6.16C16.26,18.68 15.65,19 15,19h-3l0,-6H9v-3H3V7c0,-1.1 0.9,-2 2,-2h10c0.65,0 1.26,0.31 1.63,0.84L21,12zM10,15H7v-3H5v3H2v2h3v3h2v-3h3V15z" />
9</vector>
diff --git a/src/android/app/src/main/res/drawable/ic_overlay.xml b/src/android/app/src/main/res/drawable/ic_overlay.xml
new file mode 100644
index 000000000..c7986c5a2
--- /dev/null
+++ b/src/android/app/src/main/res/drawable/ic_overlay.xml
@@ -0,0 +1,21 @@
1<vector xmlns:android="http://schemas.android.com/apk/res/android"
2 android:width="24dp"
3 android:height="24dp"
4 android:viewportWidth="24"
5 android:viewportHeight="24">
6 <path
7 android:fillColor="?attr/colorControlNormal"
8 android:pathData="M21,5H3C1.9,5 1,5.9 1,7v10c0,1.1 0.9,2 2,2h18c1.1,0 2,-0.9 2,-2V7C23,5.9 22.1,5 21,5zM18,17H6V7h12V17z" />
9 <path
10 android:fillColor="?attr/colorControlNormal"
11 android:pathData="M15,11.25h1.5v1.5h-1.5z" />
12 <path
13 android:fillColor="?attr/colorControlNormal"
14 android:pathData="M12.5,11.25h1.5v1.5h-1.5z" />
15 <path
16 android:fillColor="?attr/colorControlNormal"
17 android:pathData="M10,11.25h1.5v1.5h-1.5z" />
18 <path
19 android:fillColor="?attr/colorControlNormal"
20 android:pathData="M7.5,11.25h1.5v1.5h-1.5z" />
21</vector>
diff --git a/src/android/app/src/main/res/drawable/ic_share.xml b/src/android/app/src/main/res/drawable/ic_share.xml
new file mode 100644
index 000000000..3fc2f3c99
--- /dev/null
+++ b/src/android/app/src/main/res/drawable/ic_share.xml
@@ -0,0 +1,9 @@
1<vector xmlns:android="http://schemas.android.com/apk/res/android"
2 android:width="24dp"
3 android:height="24dp"
4 android:viewportWidth="24"
5 android:viewportHeight="24">
6 <path
7 android:fillColor="?attr/colorControlNormal"
8 android:pathData="M18,16.08c-0.76,0 -1.44,0.3 -1.96,0.77L8.91,12.7c0.05,-0.23 0.09,-0.46 0.09,-0.7s-0.04,-0.47 -0.09,-0.7l7.05,-4.11c0.54,0.5 1.25,0.81 2.04,0.81 1.66,0 3,-1.34 3,-3s-1.34,-3 -3,-3 -3,1.34 -3,3c0,0.24 0.04,0.47 0.09,0.7L8.04,9.81C7.5,9.31 6.79,9 6,9c-1.66,0 -3,1.34 -3,3s1.34,3 3,3c0.79,0 1.5,-0.31 2.04,-0.81l7.12,4.16c-0.05,0.21 -0.08,0.43 -0.08,0.65 0,1.61 1.31,2.92 2.92,2.92 1.61,0 2.92,-1.31 2.92,-2.92s-1.31,-2.92 -2.92,-2.92z" />
9</vector>
diff --git a/src/android/app/src/main/res/drawable/stick_one_direction_anim.xml b/src/android/app/src/main/res/drawable/stick_one_direction_anim.xml
new file mode 100644
index 000000000..a1da1316f
--- /dev/null
+++ b/src/android/app/src/main/res/drawable/stick_one_direction_anim.xml
@@ -0,0 +1,118 @@
1<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
2 xmlns:aapt="http://schemas.android.com/aapt">
3 <aapt:attr name="android:drawable">
4 <vector
5 android:width="1000dp"
6 android:height="1000dp"
7 android:viewportWidth="1000"
8 android:viewportHeight="1000">
9 <group android:name="_R_G">
10 <group
11 android:name="_R_G_L_1_G"
12 android:pivotX="100"
13 android:pivotY="100"
14 android:scaleX="5"
15 android:scaleY="5"
16 android:translateX="400"
17 android:translateY="400">
18 <path
19 android:name="_R_G_L_1_G_D_0_P_0"
20 android:pathData=" M100 199.39 C59.8,199.39 23.56,175.17 8.18,138.04 C-7.2,100.9 1.3,58.15 29.73,29.72 C58.15,1.3 100.9,-7.21 138.04,8.18 C175.18,23.56 199.39,59.8 199.39,100 C199.33,154.87 154.87,199.33 100,199.39c "
21 android:strokeWidth="1"
22 android:strokeAlpha="0.6"
23 android:strokeColor="?attr/colorOutline"
24 android:strokeLineCap="round"
25 android:strokeLineJoin="round" />
26 </group>
27 <group
28 android:name="_R_G_L_0_G_T_1"
29 android:scaleX="5"
30 android:scaleY="5"
31 android:translateX="500"
32 android:translateY="500">
33 <group
34 android:name="_R_G_L_0_G"
35 android:translateX="-100"
36 android:translateY="-100">
37 <path
38 android:name="_R_G_L_0_G_D_0_P_0"
39 android:fillAlpha="1"
40 android:fillColor="?attr/colorSecondaryContainer"
41 android:fillType="nonZero"
42 android:pathData=" M100.45 28.02 C140.63,28.02 173.2,60.59 173.2,100.77 C173.2,140.95 140.63,173.52 100.45,173.52 C60.27,173.52 27.7,140.95 27.7,100.77 C27.7,60.59 60.27,28.02 100.45,28.02c " />
43 <path
44 android:name="_R_G_L_0_G_D_2_P_0"
45 android:fillAlpha="0.8"
46 android:fillColor="?attr/colorOnSecondaryContainer"
47 android:fillType="nonZero"
48 android:pathData=" M100.45 50.26 C128.62,50.26 151.46,73.1 151.46,101.28 C151.46,129.45 128.62,152.29 100.45,152.29 C72.27,152.29 49.43,129.45 49.43,101.28 C49.43,73.1 72.27,50.26 100.45,50.26c " />
49 </group>
50 </group>
51 </group>
52 <group android:name="time_group" />
53 </vector>
54 </aapt:attr>
55 <target android:name="_R_G_L_0_G_T_1">
56 <aapt:attr name="android:animation">
57 <set android:ordering="together">
58 <objectAnimator
59 android:duration="267"
60 android:pathData="M 500,500C 500,500 364,500 364,500"
61 android:propertyName="translateXY"
62 android:propertyXName="translateX"
63 android:propertyYName="translateY"
64 android:startOffset="0">
65 <aapt:attr name="android:interpolator">
66 <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0" />
67 </aapt:attr>
68 </objectAnimator>
69 <objectAnimator
70 android:duration="234"
71 android:pathData="M 364,500C 364,500 364,500 364,500"
72 android:propertyName="translateXY"
73 android:propertyXName="translateX"
74 android:propertyYName="translateY"
75 android:startOffset="267">
76 <aapt:attr name="android:interpolator">
77 <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0.333 0.667,0.667 1.0,1.0" />
78 </aapt:attr>
79 </objectAnimator>
80 <objectAnimator
81 android:duration="133"
82 android:pathData="M 364,500C 364,500 525,500 525,500"
83 android:propertyName="translateXY"
84 android:propertyXName="translateX"
85 android:propertyYName="translateY"
86 android:startOffset="501">
87 <aapt:attr name="android:interpolator">
88 <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0" />
89 </aapt:attr>
90 </objectAnimator>
91 <objectAnimator
92 android:duration="100"
93 android:pathData="M 525,500C 525,500 500,500 500,500"
94 android:propertyName="translateXY"
95 android:propertyXName="translateX"
96 android:propertyYName="translateY"
97 android:startOffset="634">
98 <aapt:attr name="android:interpolator">
99 <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0" />
100 </aapt:attr>
101 </objectAnimator>
102 </set>
103 </aapt:attr>
104 </target>
105 <target android:name="time_group">
106 <aapt:attr name="android:animation">
107 <set android:ordering="together">
108 <objectAnimator
109 android:duration="968"
110 android:propertyName="translateX"
111 android:startOffset="0"
112 android:valueFrom="0"
113 android:valueTo="1"
114 android:valueType="floatType" />
115 </set>
116 </aapt:attr>
117 </target>
118</animated-vector>
diff --git a/src/android/app/src/main/res/drawable/stick_two_direction_anim.xml b/src/android/app/src/main/res/drawable/stick_two_direction_anim.xml
new file mode 100644
index 000000000..bc71adcbd
--- /dev/null
+++ b/src/android/app/src/main/res/drawable/stick_two_direction_anim.xml
@@ -0,0 +1,173 @@
1<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
2 xmlns:aapt="http://schemas.android.com/aapt">
3 <aapt:attr name="android:drawable">
4 <vector
5 android:width="1000dp"
6 android:height="1000dp"
7 android:viewportWidth="1000"
8 android:viewportHeight="1000">
9 <group android:name="_R_G">
10 <group
11 android:name="_R_G_L_1_G"
12 android:pivotX="100"
13 android:pivotY="100"
14 android:scaleX="5"
15 android:scaleY="5"
16 android:translateX="400"
17 android:translateY="400">
18 <path
19 android:name="_R_G_L_1_G_D_0_P_0"
20 android:pathData=" M100 199.39 C59.8,199.39 23.56,175.17 8.18,138.04 C-7.2,100.9 1.3,58.15 29.73,29.72 C58.15,1.3 100.9,-7.21 138.04,8.18 C175.18,23.56 199.39,59.8 199.39,100 C199.33,154.87 154.87,199.33 100,199.39c "
21 android:strokeWidth="1"
22 android:strokeAlpha="0.6"
23 android:strokeColor="?attr/colorOutline"
24 android:strokeLineCap="round"
25 android:strokeLineJoin="round" />
26 </group>
27 <group
28 android:name="_R_G_L_0_G_T_1"
29 android:scaleX="5"
30 android:scaleY="5"
31 android:translateX="500"
32 android:translateY="500">
33 <group
34 android:name="_R_G_L_0_G"
35 android:translateX="-100"
36 android:translateY="-100">
37 <path
38 android:name="_R_G_L_0_G_D_0_P_0"
39 android:fillAlpha="1"
40 android:fillColor="?attr/colorSecondaryContainer"
41 android:fillType="nonZero"
42 android:pathData=" M100.45 28.02 C140.63,28.02 173.2,60.59 173.2,100.77 C173.2,140.95 140.63,173.52 100.45,173.52 C60.27,173.52 27.7,140.95 27.7,100.77 C27.7,60.59 60.27,28.02 100.45,28.02c " />
43 <path
44 android:name="_R_G_L_0_G_D_2_P_0"
45 android:fillAlpha="0.8"
46 android:fillColor="?attr/colorOnSecondaryContainer"
47 android:fillType="nonZero"
48 android:pathData=" M100.45 50.26 C128.62,50.26 151.46,73.1 151.46,101.28 C151.46,129.45 128.62,152.29 100.45,152.29 C72.27,152.29 49.43,129.45 49.43,101.28 C49.43,73.1 72.27,50.26 100.45,50.26c " />
49 </group>
50 </group>
51 </group>
52 <group android:name="time_group" />
53 </vector>
54 </aapt:attr>
55 <target android:name="_R_G_L_0_G_T_1">
56 <aapt:attr name="android:animation">
57 <set android:ordering="together">
58 <objectAnimator
59 android:duration="267"
60 android:pathData="M 500,500C 500,500 364,500 364,500"
61 android:propertyName="translateXY"
62 android:propertyXName="translateX"
63 android:propertyYName="translateY"
64 android:startOffset="0">
65 <aapt:attr name="android:interpolator">
66 <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0" />
67 </aapt:attr>
68 </objectAnimator>
69 <objectAnimator
70 android:duration="234"
71 android:pathData="M 364,500C 364,500 364,500 364,500"
72 android:propertyName="translateXY"
73 android:propertyXName="translateX"
74 android:propertyYName="translateY"
75 android:startOffset="267">
76 <aapt:attr name="android:interpolator">
77 <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0.333 0.667,0.667 1.0,1.0" />
78 </aapt:attr>
79 </objectAnimator>
80 <objectAnimator
81 android:duration="133"
82 android:pathData="M 364,500C 364,500 525,500 525,500"
83 android:propertyName="translateXY"
84 android:propertyXName="translateX"
85 android:propertyYName="translateY"
86 android:startOffset="501">
87 <aapt:attr name="android:interpolator">
88 <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0" />
89 </aapt:attr>
90 </objectAnimator>
91 <objectAnimator
92 android:duration="100"
93 android:pathData="M 525,500C 525,500 500,500 500,500"
94 android:propertyName="translateXY"
95 android:propertyXName="translateX"
96 android:propertyYName="translateY"
97 android:startOffset="634">
98 <aapt:attr name="android:interpolator">
99 <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0" />
100 </aapt:attr>
101 </objectAnimator>
102 <objectAnimator
103 android:duration="400"
104 android:pathData="M 500,500C 500,500 500,500 500,500"
105 android:propertyName="translateXY"
106 android:propertyXName="translateX"
107 android:propertyYName="translateY"
108 android:startOffset="734">
109 <aapt:attr name="android:interpolator">
110 <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0.333 0.667,0.667 1.0,1.0" />
111 </aapt:attr>
112 </objectAnimator>
113 <objectAnimator
114 android:duration="267"
115 android:pathData="M 500,500C 500,500 500,364 500,364"
116 android:propertyName="translateXY"
117 android:propertyXName="translateX"
118 android:propertyYName="translateY"
119 android:startOffset="1134">
120 <aapt:attr name="android:interpolator">
121 <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0" />
122 </aapt:attr>
123 </objectAnimator>
124 <objectAnimator
125 android:duration="234"
126 android:pathData="M 500,364C 500,364 500,364 500,364"
127 android:propertyName="translateXY"
128 android:propertyXName="translateX"
129 android:propertyYName="translateY"
130 android:startOffset="1401">
131 <aapt:attr name="android:interpolator">
132 <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0.333 0.667,0.667 1.0,1.0" />
133 </aapt:attr>
134 </objectAnimator>
135 <objectAnimator
136 android:duration="133"
137 android:pathData="M 500,364C 500,364 500,535 500,535"
138 android:propertyName="translateXY"
139 android:propertyXName="translateX"
140 android:propertyYName="translateY"
141 android:startOffset="1635">
142 <aapt:attr name="android:interpolator">
143 <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0" />
144 </aapt:attr>
145 </objectAnimator>
146 <objectAnimator
147 android:duration="100"
148 android:pathData="M 500,535C 500,535 500,500 500,500"
149 android:propertyName="translateXY"
150 android:propertyXName="translateX"
151 android:propertyYName="translateY"
152 android:startOffset="1768">
153 <aapt:attr name="android:interpolator">
154 <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0" />
155 </aapt:attr>
156 </objectAnimator>
157 </set>
158 </aapt:attr>
159 </target>
160 <target android:name="time_group">
161 <aapt:attr name="android:animation">
162 <set android:ordering="together">
163 <objectAnimator
164 android:duration="2269"
165 android:propertyName="translateX"
166 android:startOffset="0"
167 android:valueFrom="0"
168 android:valueTo="1"
169 android:valueType="floatType" />
170 </set>
171 </aapt:attr>
172 </target>
173</animated-vector>
diff --git a/src/android/app/src/main/res/layout-ldrtl/list_item_setting_input.xml b/src/android/app/src/main/res/layout-ldrtl/list_item_setting_input.xml
new file mode 100644
index 000000000..583620dc6
--- /dev/null
+++ b/src/android/app/src/main/res/layout-ldrtl/list_item_setting_input.xml
@@ -0,0 +1,63 @@
1<?xml version="1.0" encoding="utf-8"?>
2<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
3 xmlns:app="http://schemas.android.com/apk/res-auto"
4 xmlns:tools="http://schemas.android.com/tools"
5 android:id="@+id/setting_body"
6 android:layout_width="match_parent"
7 android:layout_height="wrap_content"
8 android:background="?android:attr/selectableItemBackground"
9 android:clickable="true"
10 android:focusable="true"
11 android:gravity="center_vertical"
12 android:minHeight="72dp"
13 android:padding="16dp"
14 android:nextFocusLeft="@id/button_options">
15
16 <LinearLayout
17 android:layout_width="match_parent"
18 android:layout_height="wrap_content"
19 android:gravity="center_vertical"
20 android:orientation="horizontal">
21
22 <LinearLayout
23 android:layout_width="0dp"
24 android:layout_height="wrap_content"
25 android:orientation="vertical"
26 android:layout_weight="1">
27
28 <com.google.android.material.textview.MaterialTextView
29 android:id="@+id/text_setting_name"
30 style="@style/TextAppearance.Material3.HeadlineMedium"
31 android:layout_width="match_parent"
32 android:layout_height="wrap_content"
33 android:textAlignment="viewStart"
34 android:textSize="17sp"
35 app:lineHeight="22dp"
36 tools:text="Setting Name" />
37
38 <com.google.android.material.textview.MaterialTextView
39 android:id="@+id/text_setting_value"
40 style="@style/TextAppearance.Material3.LabelMedium"
41 android:layout_width="match_parent"
42 android:layout_height="wrap_content"
43 android:layout_marginTop="@dimen/spacing_small"
44 android:textAlignment="viewStart"
45 android:textStyle="bold"
46 android:textSize="13sp"
47 tools:text="1x" />
48
49 </LinearLayout>
50
51 <Button
52 android:id="@+id/button_options"
53 style="?attr/materialIconButtonStyle"
54 android:layout_width="wrap_content"
55 android:layout_height="wrap_content"
56 android:nextFocusRight="@id/setting_body"
57 app:icon="@drawable/ic_more_vert"
58 app:iconSize="24dp"
59 app:iconTint="?attr/colorOnSurface" />
60
61 </LinearLayout>
62
63</RelativeLayout>
diff --git a/src/android/app/src/main/res/layout/card_driver_option.xml b/src/android/app/src/main/res/layout/card_driver_option.xml
index bda524f0f..09e26990b 100644
--- a/src/android/app/src/main/res/layout/card_driver_option.xml
+++ b/src/android/app/src/main/res/layout/card_driver_option.xml
@@ -39,10 +39,7 @@
39 style="@style/TextAppearance.Material3.TitleMedium" 39 style="@style/TextAppearance.Material3.TitleMedium"
40 android:layout_width="match_parent" 40 android:layout_width="match_parent"
41 android:layout_height="wrap_content" 41 android:layout_height="wrap_content"
42 android:ellipsize="none"
43 android:marqueeRepeatLimit="marquee_forever"
44 android:requiresFadingEdge="horizontal" 42 android:requiresFadingEdge="horizontal"
45 android:singleLine="true"
46 android:textAlignment="viewStart" 43 android:textAlignment="viewStart"
47 tools:text="@string/select_gpu_driver_default" /> 44 tools:text="@string/select_gpu_driver_default" />
48 45
@@ -52,10 +49,7 @@
52 android:layout_width="match_parent" 49 android:layout_width="match_parent"
53 android:layout_height="wrap_content" 50 android:layout_height="wrap_content"
54 android:layout_marginTop="6dp" 51 android:layout_marginTop="6dp"
55 android:ellipsize="none"
56 android:marqueeRepeatLimit="marquee_forever"
57 android:requiresFadingEdge="horizontal" 52 android:requiresFadingEdge="horizontal"
58 android:singleLine="true"
59 android:textAlignment="viewStart" 53 android:textAlignment="viewStart"
60 tools:text="@string/install_gpu_driver_description" /> 54 tools:text="@string/install_gpu_driver_description" />
61 55
@@ -65,10 +59,7 @@
65 android:layout_width="match_parent" 59 android:layout_width="match_parent"
66 android:layout_height="wrap_content" 60 android:layout_height="wrap_content"
67 android:layout_marginTop="6dp" 61 android:layout_marginTop="6dp"
68 android:ellipsize="none"
69 android:marqueeRepeatLimit="marquee_forever"
70 android:requiresFadingEdge="horizontal" 62 android:requiresFadingEdge="horizontal"
71 android:singleLine="true"
72 android:textAlignment="viewStart" 63 android:textAlignment="viewStart"
73 tools:text="@string/install_gpu_driver_description" /> 64 tools:text="@string/install_gpu_driver_description" />
74 65
diff --git a/src/android/app/src/main/res/layout/card_folder.xml b/src/android/app/src/main/res/layout/card_folder.xml
index ed4a7ca8f..e3a5f1a86 100644
--- a/src/android/app/src/main/res/layout/card_folder.xml
+++ b/src/android/app/src/main/res/layout/card_folder.xml
@@ -21,10 +21,7 @@
21 android:layout_width="0dp" 21 android:layout_width="0dp"
22 android:layout_height="wrap_content" 22 android:layout_height="wrap_content"
23 android:layout_gravity="center_vertical|start" 23 android:layout_gravity="center_vertical|start"
24 android:ellipsize="none"
25 android:marqueeRepeatLimit="marquee_forever"
26 android:requiresFadingEdge="horizontal" 24 android:requiresFadingEdge="horizontal"
27 android:singleLine="true"
28 android:textAlignment="viewStart" 25 android:textAlignment="viewStart"
29 app:layout_constraintBottom_toBottomOf="parent" 26 app:layout_constraintBottom_toBottomOf="parent"
30 app:layout_constraintEnd_toStartOf="@+id/button_layout" 27 app:layout_constraintEnd_toStartOf="@+id/button_layout"
diff --git a/src/android/app/src/main/res/layout/card_game.xml b/src/android/app/src/main/res/layout/card_game.xml
index 6340171ec..411b50315 100644
--- a/src/android/app/src/main/res/layout/card_game.xml
+++ b/src/android/app/src/main/res/layout/card_game.xml
@@ -40,10 +40,7 @@
40 android:layout_width="0dp" 40 android:layout_width="0dp"
41 android:layout_height="wrap_content" 41 android:layout_height="wrap_content"
42 android:layout_marginTop="8dp" 42 android:layout_marginTop="8dp"
43 android:ellipsize="none"
44 android:marqueeRepeatLimit="marquee_forever"
45 android:requiresFadingEdge="horizontal" 43 android:requiresFadingEdge="horizontal"
46 android:singleLine="true"
47 android:textAlignment="center" 44 android:textAlignment="center"
48 android:textSize="14sp" 45 android:textSize="14sp"
49 app:layout_constraintEnd_toEndOf="@+id/image_game_screen" 46 app:layout_constraintEnd_toEndOf="@+id/image_game_screen"
diff --git a/src/android/app/src/main/res/layout/card_simple_outlined.xml b/src/android/app/src/main/res/layout/card_simple_outlined.xml
index b73930e7e..e29df6a2d 100644
--- a/src/android/app/src/main/res/layout/card_simple_outlined.xml
+++ b/src/android/app/src/main/res/layout/card_simple_outlined.xml
@@ -59,9 +59,6 @@
59 android:textAlignment="viewStart" 59 android:textAlignment="viewStart"
60 android:textSize="14sp" 60 android:textSize="14sp"
61 android:textStyle="bold" 61 android:textStyle="bold"
62 android:singleLine="true"
63 android:marqueeRepeatLimit="marquee_forever"
64 android:ellipsize="none"
65 android:requiresFadingEdge="horizontal" 62 android:requiresFadingEdge="horizontal"
66 android:layout_marginTop="6dp" 63 android:layout_marginTop="6dp"
67 android:visibility="gone" 64 android:visibility="gone"
diff --git a/src/android/app/src/main/res/layout/dialog_input_profiles.xml b/src/android/app/src/main/res/layout/dialog_input_profiles.xml
new file mode 100644
index 000000000..6ad76fe41
--- /dev/null
+++ b/src/android/app/src/main/res/layout/dialog_input_profiles.xml
@@ -0,0 +1,6 @@
1<?xml version="1.0" encoding="utf-8"?>
2<androidx.recyclerview.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android"
3 android:id="@+id/list_profiles"
4 android:layout_width="match_parent"
5 android:layout_height="wrap_content"
6 android:fadeScrollbars="false" />
diff --git a/src/android/app/src/main/res/layout/dialog_mapping.xml b/src/android/app/src/main/res/layout/dialog_mapping.xml
new file mode 100644
index 000000000..06190b8d2
--- /dev/null
+++ b/src/android/app/src/main/res/layout/dialog_mapping.xml
@@ -0,0 +1,26 @@
1<?xml version="1.0" encoding="utf-8"?>
2<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
3 android:layout_width="match_parent"
4 android:layout_height="wrap_content"
5 xmlns:tools="http://schemas.android.com/tools"
6 android:defaultFocusHighlightEnabled="false"
7 android:focusable="true"
8 android:focusableInTouchMode="true"
9 android:focusedByDefault="true"
10 android:orientation="horizontal"
11 android:gravity="center">
12
13 <ImageView
14 android:id="@+id/image_stick_animation"
15 android:layout_width="@dimen/mapping_anim_size"
16 android:layout_height="@dimen/mapping_anim_size"
17 tools:src="@drawable/stick_two_direction_anim" />
18
19 <ImageView
20 android:id="@+id/image_button_animation"
21 android:layout_width="@dimen/mapping_anim_size"
22 android:layout_height="@dimen/mapping_anim_size"
23 android:layout_marginStart="48dp"
24 tools:src="@drawable/button_anim" />
25
26</LinearLayout>
diff --git a/src/android/app/src/main/res/layout/fragment_game_properties.xml b/src/android/app/src/main/res/layout/fragment_game_properties.xml
index 436ebd79d..5e3f3cf28 100644
--- a/src/android/app/src/main/res/layout/fragment_game_properties.xml
+++ b/src/android/app/src/main/res/layout/fragment_game_properties.xml
@@ -76,10 +76,7 @@
76 android:layout_marginTop="12dp" 76 android:layout_marginTop="12dp"
77 android:layout_marginBottom="12dp" 77 android:layout_marginBottom="12dp"
78 android:layout_marginHorizontal="16dp" 78 android:layout_marginHorizontal="16dp"
79 android:ellipsize="none"
80 android:marqueeRepeatLimit="marquee_forever"
81 android:requiresFadingEdge="horizontal" 79 android:requiresFadingEdge="horizontal"
82 android:singleLine="true"
83 android:textAlignment="center" 80 android:textAlignment="center"
84 tools:text="deko_basic" /> 81 tools:text="deko_basic" />
85 82
diff --git a/src/android/app/src/main/res/layout/list_item_input_profile.xml b/src/android/app/src/main/res/layout/list_item_input_profile.xml
new file mode 100644
index 000000000..a08dccf0c
--- /dev/null
+++ b/src/android/app/src/main/res/layout/list_item_input_profile.xml
@@ -0,0 +1,74 @@
1<?xml version="1.0" encoding="utf-8"?>
2<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
3 xmlns:app="http://schemas.android.com/apk/res-auto"
4 xmlns:tools="http://schemas.android.com/tools"
5 android:layout_width="match_parent"
6 android:layout_height="wrap_content"
7 android:focusable="false"
8 android:paddingHorizontal="20dp"
9 android:paddingVertical="16dp">
10
11 <com.google.android.material.textview.MaterialTextView
12 android:id="@+id/title"
13 style="@style/TextAppearance.Material3.HeadlineMedium"
14 android:layout_width="0dp"
15 android:layout_height="0dp"
16 android:textAlignment="viewStart"
17 android:gravity="start|center_vertical"
18 android:textSize="17sp"
19 android:layout_marginEnd="16dp"
20 app:layout_constraintBottom_toBottomOf="@+id/button_layout"
21 app:layout_constraintEnd_toStartOf="@+id/button_layout"
22 app:layout_constraintStart_toStartOf="parent"
23 app:layout_constraintTop_toTopOf="parent"
24 app:lineHeight="28dp"
25 tools:text="My profile" />
26
27 <LinearLayout
28 android:id="@+id/button_layout"
29 android:layout_width="wrap_content"
30 android:layout_height="wrap_content"
31 android:gravity="center_vertical"
32 android:orientation="horizontal"
33 app:layout_constraintEnd_toEndOf="parent"
34 app:layout_constraintTop_toTopOf="parent">
35
36 <Button
37 android:id="@+id/button_new"
38 style="@style/Widget.Material3.Button.IconButton.Filled.Tonal"
39 android:layout_width="wrap_content"
40 android:layout_height="wrap_content"
41 android:contentDescription="@string/create_new_profile"
42 android:tooltipText="@string/create_new_profile"
43 app:icon="@drawable/ic_new_label" />
44
45 <Button
46 android:id="@+id/button_delete"
47 style="@style/Widget.Material3.Button.IconButton.Filled.Tonal"
48 android:layout_width="wrap_content"
49 android:layout_height="wrap_content"
50 android:contentDescription="@string/delete"
51 android:tooltipText="@string/delete"
52 app:icon="@drawable/ic_delete" />
53
54 <Button
55 android:id="@+id/button_save"
56 style="@style/Widget.Material3.Button.IconButton.Filled.Tonal"
57 android:layout_width="wrap_content"
58 android:layout_height="wrap_content"
59 android:contentDescription="@string/save"
60 android:tooltipText="@string/save"
61 app:icon="@drawable/ic_save" />
62
63 <Button
64 android:id="@+id/button_load"
65 style="@style/Widget.Material3.Button.IconButton.Filled.Tonal"
66 android:layout_width="wrap_content"
67 android:layout_height="wrap_content"
68 android:contentDescription="@string/load"
69 android:tooltipText="@string/load"
70 app:icon="@drawable/ic_import" />
71
72 </LinearLayout>
73
74</androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/src/android/app/src/main/res/layout/list_item_setting_input.xml b/src/android/app/src/main/res/layout/list_item_setting_input.xml
new file mode 100644
index 000000000..d67cbe245
--- /dev/null
+++ b/src/android/app/src/main/res/layout/list_item_setting_input.xml
@@ -0,0 +1,63 @@
1<?xml version="1.0" encoding="utf-8"?>
2<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
3 xmlns:app="http://schemas.android.com/apk/res-auto"
4 xmlns:tools="http://schemas.android.com/tools"
5 android:id="@+id/setting_body"
6 android:layout_width="match_parent"
7 android:layout_height="wrap_content"
8 android:background="?android:attr/selectableItemBackground"
9 android:clickable="true"
10 android:focusable="true"
11 android:gravity="center_vertical"
12 android:minHeight="72dp"
13 android:padding="16dp"
14 android:nextFocusRight="@id/button_options">
15
16 <LinearLayout
17 android:layout_width="match_parent"
18 android:layout_height="wrap_content"
19 android:gravity="center_vertical"
20 android:orientation="horizontal">
21
22 <LinearLayout
23 android:layout_width="0dp"
24 android:layout_height="wrap_content"
25 android:orientation="vertical"
26 android:layout_weight="1">
27
28 <com.google.android.material.textview.MaterialTextView
29 android:id="@+id/text_setting_name"
30 style="@style/TextAppearance.Material3.HeadlineMedium"
31 android:layout_width="match_parent"
32 android:layout_height="wrap_content"
33 android:textAlignment="viewStart"
34 android:textSize="17sp"
35 app:lineHeight="22dp"
36 tools:text="Setting Name" />
37
38 <com.google.android.material.textview.MaterialTextView
39 android:id="@+id/text_setting_value"
40 style="@style/TextAppearance.Material3.LabelMedium"
41 android:layout_width="match_parent"
42 android:layout_height="wrap_content"
43 android:layout_marginTop="@dimen/spacing_small"
44 android:textAlignment="viewStart"
45 android:textStyle="bold"
46 android:textSize="13sp"
47 tools:text="1x" />
48
49 </LinearLayout>
50
51 <Button
52 android:id="@+id/button_options"
53 style="?attr/materialIconButtonStyle"
54 android:layout_width="wrap_content"
55 android:layout_height="wrap_content"
56 android:nextFocusLeft="@id/setting_body"
57 app:icon="@drawable/ic_more_vert"
58 app:iconSize="24dp"
59 app:iconTint="?attr/colorOnSurface" />
60
61 </LinearLayout>
62
63</RelativeLayout>
diff --git a/src/android/app/src/main/res/menu/menu_in_game.xml b/src/android/app/src/main/res/menu/menu_in_game.xml
index eecb0563b..867197ebc 100644
--- a/src/android/app/src/main/res/menu/menu_in_game.xml
+++ b/src/android/app/src/main/res/menu/menu_in_game.xml
@@ -17,8 +17,13 @@
17 android:title="@string/per_game_settings" /> 17 android:title="@string/per_game_settings" />
18 18
19 <item 19 <item
20 android:id="@+id/menu_overlay_controls" 20 android:id="@+id/menu_controls"
21 android:icon="@drawable/ic_controller" 21 android:icon="@drawable/ic_controller"
22 android:title="@string/preferences_controls" />
23
24 <item
25 android:id="@+id/menu_overlay_controls"
26 android:icon="@drawable/ic_overlay"
22 android:title="@string/emulation_input_overlay" /> 27 android:title="@string/emulation_input_overlay" />
23 28
24 <item 29 <item
diff --git a/src/android/app/src/main/res/menu/menu_input_options.xml b/src/android/app/src/main/res/menu/menu_input_options.xml
new file mode 100644
index 000000000..81ea5043f
--- /dev/null
+++ b/src/android/app/src/main/res/menu/menu_input_options.xml
@@ -0,0 +1,34 @@
1<?xml version="1.0" encoding="utf-8"?>
2<menu xmlns:android="http://schemas.android.com/apk/res/android">
3
4 <item
5 android:id="@+id/invert_axis"
6 android:title="@string/invert_axis"
7 android:visible="false" />
8
9 <item
10 android:id="@+id/invert_button"
11 android:title="@string/invert_button"
12 android:visible="false" />
13
14 <item
15 android:id="@+id/toggle_button"
16 android:title="@string/toggle_button"
17 android:visible="false" />
18
19 <item
20 android:id="@+id/turbo_button"
21 android:title="@string/turbo_button"
22 android:visible="false" />
23
24 <item
25 android:id="@+id/set_threshold"
26 android:title="@string/set_threshold"
27 android:visible="false" />
28
29 <item
30 android:id="@+id/toggle_axis"
31 android:title="@string/toggle_axis"
32 android:visible="false" />
33
34</menu>
diff --git a/src/android/app/src/main/res/navigation/settings_navigation.xml b/src/android/app/src/main/res/navigation/settings_navigation.xml
index 1d87d36b3..e4c66e7d5 100644
--- a/src/android/app/src/main/res/navigation/settings_navigation.xml
+++ b/src/android/app/src/main/res/navigation/settings_navigation.xml
@@ -26,7 +26,7 @@
26 26
27 <fragment 27 <fragment
28 android:id="@+id/settingsSearchFragment" 28 android:id="@+id/settingsSearchFragment"
29 android:name="org.yuzu.yuzu_emu.fragments.SettingsSearchFragment" 29 android:name="org.yuzu.yuzu_emu.features.settings.ui.SettingsSearchFragment"
30 android:label="SettingsSearchFragment" /> 30 android:label="SettingsSearchFragment" />
31 31
32</navigation> 32</navigation>
diff --git a/src/android/app/src/main/res/values-w600dp/dimens.xml b/src/android/app/src/main/res/values-w600dp/dimens.xml
index 128319e27..0e2d40876 100644
--- a/src/android/app/src/main/res/values-w600dp/dimens.xml
+++ b/src/android/app/src/main/res/values-w600dp/dimens.xml
@@ -2,4 +2,6 @@
2<resources> 2<resources>
3 <dimen name="spacing_navigation">0dp</dimen> 3 <dimen name="spacing_navigation">0dp</dimen>
4 <dimen name="spacing_navigation_rail">80dp</dimen> 4 <dimen name="spacing_navigation_rail">80dp</dimen>
5
6 <dimen name="mapping_anim_size">100dp</dimen>
5</resources> 7</resources>
diff --git a/src/android/app/src/main/res/values/dimens.xml b/src/android/app/src/main/res/values/dimens.xml
index 992b5ae44..bf733637f 100644
--- a/src/android/app/src/main/res/values/dimens.xml
+++ b/src/android/app/src/main/res/values/dimens.xml
@@ -18,4 +18,6 @@
18 18
19 <dimen name="dialog_margin">20dp</dimen> 19 <dimen name="dialog_margin">20dp</dimen>
20 <dimen name="elevated_app_bar">3dp</dimen> 20 <dimen name="elevated_app_bar">3dp</dimen>
21
22 <dimen name="mapping_anim_size">75dp</dimen>
21</resources> 23</resources>
diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml
index 78a4c958a..6a631f664 100644
--- a/src/android/app/src/main/res/values/strings.xml
+++ b/src/android/app/src/main/res/values/strings.xml
@@ -255,6 +255,92 @@
255 <string name="audio_volume">Volume</string> 255 <string name="audio_volume">Volume</string>
256 <string name="audio_volume_description">Specifies the volume of audio output.</string> 256 <string name="audio_volume_description">Specifies the volume of audio output.</string>
257 257
258 <!-- Input strings -->
259 <string name="buttons">Buttons</string>
260 <string name="button_a">A</string>
261 <string name="button_b">B</string>
262 <string name="button_x">X</string>
263 <string name="button_y">Y</string>
264 <string name="button_plus">Plus</string>
265 <string name="button_minus">Minus</string>
266 <string name="button_home">Home</string>
267 <string name="button_capture">Capture</string>
268 <string name="start_pause">Start/Pause</string>
269 <string name="dpad">D-Pad</string>
270 <string name="up">Up</string>
271 <string name="down">Down</string>
272 <string name="left">Left</string>
273 <string name="right">Right</string>
274 <string name="left_stick">Left stick</string>
275 <string name="control_stick">Control stick</string>
276 <string name="right_stick">Right stick</string>
277 <string name="c_stick">C-Stick</string>
278 <string name="pressed">Pressed</string>
279 <string name="range">Range</string>
280 <string name="deadzone">Deadzone</string>
281 <string name="modifier">Modifier</string>
282 <string name="modifier_range">Modifier range</string>
283 <string name="triggers">Triggers</string>
284 <string name="button_l">L</string>
285 <string name="button_r">R</string>
286 <string name="button_zl">ZL</string>
287 <string name="button_zr">ZR</string>
288 <string name="button_sl_left">Left SL</string>
289 <string name="button_sr_left">Left SR</string>
290 <string name="button_sl_right">Right SL</string>
291 <string name="button_sr_right">Right SR</string>
292 <string name="button_z">Z</string>
293 <string name="invalid">Invalid</string>
294 <string name="not_set">Not set</string>
295 <string name="unknown">Unknown</string>
296 <string name="qualified_hat">%1$s%2$s%3$sHat %4$s</string>
297 <string name="qualified_button_stick_axis">%1$s%2$s%3$sAxis %4$s</string>
298 <string name="qualified_button">%1$s%2$s%3$sButton %4$s</string>
299 <string name="qualified_axis">Axis %1$s%2$s</string>
300 <string name="unused">Unused</string>
301 <string name="input_prompt">Move or press an input</string>
302 <string name="unsupported_input">Unsupported input type</string>
303 <string name="input_mapping_filter">Input mapping filter</string>
304 <string name="input_mapping_filter_description">Select a device to filter mapping inputs</string>
305 <string name="auto_map">Auto-map a controller</string>
306 <string name="auto_map_description">Select a device to attempt auto-mapping</string>
307 <string name="attempted_auto_map">Attempted auto-map with %1$s</string>
308 <string name="controller_type">Controller type</string>
309 <string name="pro_controller">Pro Controller</string>
310 <string name="handheld">Handheld</string>
311 <string name="dual_joycons">Dual Joycons</string>
312 <string name="left_joycon">Left Joycon</string>
313 <string name="right_joycon">Right Joycon</string>
314 <string name="gamecube_controller">GameCube Controller</string>
315 <string name="invert_axis">Invert axis</string>
316 <string name="invert_button">Invert button</string>
317 <string name="toggle_button">Toggle button</string>
318 <string name="turbo_button">Turbo button</string>
319 <string name="set_threshold">Set threshold</string>
320 <string name="toggle_axis">Toggle axis</string>
321 <string name="connected">Connected</string>
322 <string name="use_system_vibrator">Use system vibrator</string>
323 <string name="input_overlay">Input overlay</string>
324 <string name="vibration">Vibration</string>
325 <string name="vibration_strength">Vibration strength</string>
326 <string name="profile">Profile</string>
327 <string name="create_new_profile">Create new profile</string>
328 <string name="enter_profile_name">Enter profile name</string>
329 <string name="profile_name_already_exists">Profile name already exists</string>
330 <string name="invalid_profile_name">Invalid profile name</string>
331 <string name="use_global_input_configuration">Use global input configuration</string>
332 <string name="player_num_profile">Player %d profile</string>
333 <string name="delete_input_profile">Delete input profile</string>
334 <string name="delete_input_profile_description">Are you sure that you want to delete this profile? This is not recoverable.</string>
335 <string name="stick_map_description">Move a stick left and then up or press a button</string>
336 <string name="button_map_description">Press a button or move a trigger/stick</string>
337 <string name="map_dpad_direction">Map to D-Pad %1$s</string>
338 <string name="map_control">Map to %1$s</string>
339 <string name="failed_to_load_profile">Failed to load profile</string>
340 <string name="failed_to_save_profile">Failed to save profile</string>
341 <string name="reset_mapping">Reset mappings</string>
342 <string name="reset_mapping_description">Are you sure that you want to reset all mappings for this controller to default? This cannot be undone.</string>
343
258 <!-- Miscellaneous --> 344 <!-- Miscellaneous -->
259 <string name="slider_default">Default</string> 345 <string name="slider_default">Default</string>
260 <string name="ini_saved">Saved settings</string> 346 <string name="ini_saved">Saved settings</string>
@@ -292,6 +378,10 @@
292 <string name="more_options">More options</string> 378 <string name="more_options">More options</string>
293 <string name="use_global_setting">Use global setting</string> 379 <string name="use_global_setting">Use global setting</string>
294 <string name="operation_completed_successfully">The operation completed successfully</string> 380 <string name="operation_completed_successfully">The operation completed successfully</string>
381 <string name="retry">Retry</string>
382 <string name="confirm">Confirm</string>
383 <string name="load">Load</string>
384 <string name="save">Save</string>
295 385
296 <!-- GPU driver installation --> 386 <!-- GPU driver installation -->
297 <string name="select_gpu_driver">Select GPU driver</string> 387 <string name="select_gpu_driver">Select GPU driver</string>
@@ -313,6 +403,9 @@
313 <string name="preferences_graphics_description">Accuracy level, resolution, shader cache</string> 403 <string name="preferences_graphics_description">Accuracy level, resolution, shader cache</string>
314 <string name="preferences_audio">Audio</string> 404 <string name="preferences_audio">Audio</string>
315 <string name="preferences_audio_description">Output engine, volume</string> 405 <string name="preferences_audio_description">Output engine, volume</string>
406 <string name="preferences_controls">Controls</string>
407 <string name="preferences_controls_description">Map controller input</string>
408 <string name="preferences_player">Player %d</string>
316 <string name="preferences_theme">Theme and color</string> 409 <string name="preferences_theme">Theme and color</string>
317 <string name="preferences_debug">Debug</string> 410 <string name="preferences_debug">Debug</string>
318 <string name="preferences_debug_description">CPU/GPU debugging, graphics API, fastmem</string> 411 <string name="preferences_debug_description">CPU/GPU debugging, graphics API, fastmem</string>
diff --git a/src/audio_core/sink/cubeb_sink.cpp b/src/audio_core/sink/cubeb_sink.cpp
index d97ca2a40..49efae8e3 100644
--- a/src/audio_core/sink/cubeb_sink.cpp
+++ b/src/audio_core/sink/cubeb_sink.cpp
@@ -357,7 +357,9 @@ bool IsCubebSuitable() {
357 return false; 357 return false;
358 } 358 }
359 359
360 SCOPE_EXIT({ cubeb_destroy(ctx); }); 360 SCOPE_EXIT {
361 cubeb_destroy(ctx);
362 };
361 363
362#ifdef _WIN32 364#ifdef _WIN32
363 if (SUCCEEDED(com_init_result)) { 365 if (SUCCEEDED(com_init_result)) {
diff --git a/src/audio_core/sink/sink_stream.cpp b/src/audio_core/sink/sink_stream.cpp
index c047b0668..0a98eb31e 100644
--- a/src/audio_core/sink/sink_stream.cpp
+++ b/src/audio_core/sink/sink_stream.cpp
@@ -20,10 +20,10 @@
20namespace AudioCore::Sink { 20namespace AudioCore::Sink {
21 21
22void SinkStream::AppendBuffer(SinkBuffer& buffer, std::span<s16> samples) { 22void SinkStream::AppendBuffer(SinkBuffer& buffer, std::span<s16> samples) {
23 SCOPE_EXIT({ 23 SCOPE_EXIT {
24 queue.enqueue(buffer); 24 queue.enqueue(buffer);
25 ++queued_buffers; 25 ++queued_buffers;
26 }); 26 };
27 27
28 if (type == StreamType::In) { 28 if (type == StreamType::In) {
29 return; 29 return;
diff --git a/src/common/android/id_cache.cpp b/src/common/android/id_cache.cpp
index f39262db9..1145cbdf2 100644
--- a/src/common/android/id_cache.cpp
+++ b/src/common/android/id_cache.cpp
@@ -65,6 +65,30 @@ static jclass s_boolean_class;
65static jmethodID s_boolean_constructor; 65static jmethodID s_boolean_constructor;
66static jfieldID s_boolean_value_field; 66static jfieldID s_boolean_value_field;
67 67
68static jclass s_player_input_class;
69static jmethodID s_player_input_constructor;
70static jfieldID s_player_input_connected_field;
71static jfieldID s_player_input_buttons_field;
72static jfieldID s_player_input_analogs_field;
73static jfieldID s_player_input_motions_field;
74static jfieldID s_player_input_vibration_enabled_field;
75static jfieldID s_player_input_vibration_strength_field;
76static jfieldID s_player_input_body_color_left_field;
77static jfieldID s_player_input_body_color_right_field;
78static jfieldID s_player_input_button_color_left_field;
79static jfieldID s_player_input_button_color_right_field;
80static jfieldID s_player_input_profile_name_field;
81static jfieldID s_player_input_use_system_vibrator_field;
82
83static jclass s_yuzu_input_device_interface;
84static jmethodID s_yuzu_input_device_get_name;
85static jmethodID s_yuzu_input_device_get_guid;
86static jmethodID s_yuzu_input_device_get_port;
87static jmethodID s_yuzu_input_device_get_supports_vibration;
88static jmethodID s_yuzu_input_device_vibrate;
89static jmethodID s_yuzu_input_device_get_axes;
90static jmethodID s_yuzu_input_device_has_keys;
91
68static constexpr jint JNI_VERSION = JNI_VERSION_1_6; 92static constexpr jint JNI_VERSION = JNI_VERSION_1_6;
69 93
70namespace Common::Android { 94namespace Common::Android {
@@ -276,6 +300,94 @@ jfieldID GetBooleanValueField() {
276 return s_boolean_value_field; 300 return s_boolean_value_field;
277} 301}
278 302
303jclass GetPlayerInputClass() {
304 return s_player_input_class;
305}
306
307jmethodID GetPlayerInputConstructor() {
308 return s_player_input_constructor;
309}
310
311jfieldID GetPlayerInputConnectedField() {
312 return s_player_input_connected_field;
313}
314
315jfieldID GetPlayerInputButtonsField() {
316 return s_player_input_buttons_field;
317}
318
319jfieldID GetPlayerInputAnalogsField() {
320 return s_player_input_analogs_field;
321}
322
323jfieldID GetPlayerInputMotionsField() {
324 return s_player_input_motions_field;
325}
326
327jfieldID GetPlayerInputVibrationEnabledField() {
328 return s_player_input_vibration_enabled_field;
329}
330
331jfieldID GetPlayerInputVibrationStrengthField() {
332 return s_player_input_vibration_strength_field;
333}
334
335jfieldID GetPlayerInputBodyColorLeftField() {
336 return s_player_input_body_color_left_field;
337}
338
339jfieldID GetPlayerInputBodyColorRightField() {
340 return s_player_input_body_color_right_field;
341}
342
343jfieldID GetPlayerInputButtonColorLeftField() {
344 return s_player_input_button_color_left_field;
345}
346
347jfieldID GetPlayerInputButtonColorRightField() {
348 return s_player_input_button_color_right_field;
349}
350
351jfieldID GetPlayerInputProfileNameField() {
352 return s_player_input_profile_name_field;
353}
354
355jfieldID GetPlayerInputUseSystemVibratorField() {
356 return s_player_input_use_system_vibrator_field;
357}
358
359jclass GetYuzuInputDeviceInterface() {
360 return s_yuzu_input_device_interface;
361}
362
363jmethodID GetYuzuDeviceGetName() {
364 return s_yuzu_input_device_get_name;
365}
366
367jmethodID GetYuzuDeviceGetGUID() {
368 return s_yuzu_input_device_get_guid;
369}
370
371jmethodID GetYuzuDeviceGetPort() {
372 return s_yuzu_input_device_get_port;
373}
374
375jmethodID GetYuzuDeviceGetSupportsVibration() {
376 return s_yuzu_input_device_get_supports_vibration;
377}
378
379jmethodID GetYuzuDeviceVibrate() {
380 return s_yuzu_input_device_vibrate;
381}
382
383jmethodID GetYuzuDeviceGetAxes() {
384 return s_yuzu_input_device_get_axes;
385}
386
387jmethodID GetYuzuDeviceHasKeys() {
388 return s_yuzu_input_device_has_keys;
389}
390
279#ifdef __cplusplus 391#ifdef __cplusplus
280extern "C" { 392extern "C" {
281#endif 393#endif
@@ -387,6 +499,55 @@ jint JNI_OnLoad(JavaVM* vm, void* reserved) {
387 s_boolean_value_field = env->GetFieldID(boolean_class, "value", "Z"); 499 s_boolean_value_field = env->GetFieldID(boolean_class, "value", "Z");
388 env->DeleteLocalRef(boolean_class); 500 env->DeleteLocalRef(boolean_class);
389 501
502 const jclass player_input_class =
503 env->FindClass("org/yuzu/yuzu_emu/features/input/model/PlayerInput");
504 s_player_input_class = reinterpret_cast<jclass>(env->NewGlobalRef(player_input_class));
505 s_player_input_constructor = env->GetMethodID(
506 player_input_class, "<init>",
507 "(Z[Ljava/lang/String;[Ljava/lang/String;[Ljava/lang/String;ZIJJJJLjava/lang/String;Z)V");
508 s_player_input_connected_field = env->GetFieldID(player_input_class, "connected", "Z");
509 s_player_input_buttons_field =
510 env->GetFieldID(player_input_class, "buttons", "[Ljava/lang/String;");
511 s_player_input_analogs_field =
512 env->GetFieldID(player_input_class, "analogs", "[Ljava/lang/String;");
513 s_player_input_motions_field =
514 env->GetFieldID(player_input_class, "motions", "[Ljava/lang/String;");
515 s_player_input_vibration_enabled_field =
516 env->GetFieldID(player_input_class, "vibrationEnabled", "Z");
517 s_player_input_vibration_strength_field =
518 env->GetFieldID(player_input_class, "vibrationStrength", "I");
519 s_player_input_body_color_left_field =
520 env->GetFieldID(player_input_class, "bodyColorLeft", "J");
521 s_player_input_body_color_right_field =
522 env->GetFieldID(player_input_class, "bodyColorRight", "J");
523 s_player_input_button_color_left_field =
524 env->GetFieldID(player_input_class, "buttonColorLeft", "J");
525 s_player_input_button_color_right_field =
526 env->GetFieldID(player_input_class, "buttonColorRight", "J");
527 s_player_input_profile_name_field =
528 env->GetFieldID(player_input_class, "profileName", "Ljava/lang/String;");
529 s_player_input_use_system_vibrator_field =
530 env->GetFieldID(player_input_class, "useSystemVibrator", "Z");
531 env->DeleteLocalRef(player_input_class);
532
533 const jclass yuzu_input_device_interface =
534 env->FindClass("org/yuzu/yuzu_emu/features/input/YuzuInputDevice");
535 s_yuzu_input_device_interface =
536 reinterpret_cast<jclass>(env->NewGlobalRef(yuzu_input_device_interface));
537 s_yuzu_input_device_get_name =
538 env->GetMethodID(yuzu_input_device_interface, "getName", "()Ljava/lang/String;");
539 s_yuzu_input_device_get_guid =
540 env->GetMethodID(yuzu_input_device_interface, "getGUID", "()Ljava/lang/String;");
541 s_yuzu_input_device_get_port = env->GetMethodID(yuzu_input_device_interface, "getPort", "()I");
542 s_yuzu_input_device_get_supports_vibration =
543 env->GetMethodID(yuzu_input_device_interface, "getSupportsVibration", "()Z");
544 s_yuzu_input_device_vibrate = env->GetMethodID(yuzu_input_device_interface, "vibrate", "(F)V");
545 s_yuzu_input_device_get_axes =
546 env->GetMethodID(yuzu_input_device_interface, "getAxes", "()[Ljava/lang/Integer;");
547 s_yuzu_input_device_has_keys =
548 env->GetMethodID(yuzu_input_device_interface, "hasKeys", "([I)[Z");
549 env->DeleteLocalRef(yuzu_input_device_interface);
550
390 // Initialize Android Storage 551 // Initialize Android Storage
391 Common::FS::Android::RegisterCallbacks(env, s_native_library_class); 552 Common::FS::Android::RegisterCallbacks(env, s_native_library_class);
392 553
@@ -416,6 +577,8 @@ void JNI_OnUnload(JavaVM* vm, void* reserved) {
416 env->DeleteGlobalRef(s_double_class); 577 env->DeleteGlobalRef(s_double_class);
417 env->DeleteGlobalRef(s_integer_class); 578 env->DeleteGlobalRef(s_integer_class);
418 env->DeleteGlobalRef(s_boolean_class); 579 env->DeleteGlobalRef(s_boolean_class);
580 env->DeleteGlobalRef(s_player_input_class);
581 env->DeleteGlobalRef(s_yuzu_input_device_interface);
419 582
420 // UnInitialize applets 583 // UnInitialize applets
421 SoftwareKeyboard::CleanupJNI(env); 584 SoftwareKeyboard::CleanupJNI(env);
diff --git a/src/common/android/id_cache.h b/src/common/android/id_cache.h
index 47802f96c..cd2844dcc 100644
--- a/src/common/android/id_cache.h
+++ b/src/common/android/id_cache.h
@@ -85,4 +85,28 @@ jclass GetBooleanClass();
85jmethodID GetBooleanConstructor(); 85jmethodID GetBooleanConstructor();
86jfieldID GetBooleanValueField(); 86jfieldID GetBooleanValueField();
87 87
88jclass GetPlayerInputClass();
89jmethodID GetPlayerInputConstructor();
90jfieldID GetPlayerInputConnectedField();
91jfieldID GetPlayerInputButtonsField();
92jfieldID GetPlayerInputAnalogsField();
93jfieldID GetPlayerInputMotionsField();
94jfieldID GetPlayerInputVibrationEnabledField();
95jfieldID GetPlayerInputVibrationStrengthField();
96jfieldID GetPlayerInputBodyColorLeftField();
97jfieldID GetPlayerInputBodyColorRightField();
98jfieldID GetPlayerInputButtonColorLeftField();
99jfieldID GetPlayerInputButtonColorRightField();
100jfieldID GetPlayerInputProfileNameField();
101jfieldID GetPlayerInputUseSystemVibratorField();
102
103jclass GetYuzuInputDeviceInterface();
104jmethodID GetYuzuDeviceGetName();
105jmethodID GetYuzuDeviceGetGUID();
106jmethodID GetYuzuDeviceGetPort();
107jmethodID GetYuzuDeviceGetSupportsVibration();
108jmethodID GetYuzuDeviceVibrate();
109jmethodID GetYuzuDeviceGetAxes();
110jmethodID GetYuzuDeviceHasKeys();
111
88} // namespace Common::Android 112} // namespace Common::Android
diff --git a/src/common/demangle.cpp b/src/common/demangle.cpp
index 6e117cb41..b2c9d126a 100644
--- a/src/common/demangle.cpp
+++ b/src/common/demangle.cpp
@@ -20,7 +20,9 @@ std::string DemangleSymbol(const std::string& mangled) {
20 } 20 }
21 21
22 char* demangled = nullptr; 22 char* demangled = nullptr;
23 SCOPE_EXIT({ std::free(demangled); }); 23 SCOPE_EXIT {
24 std::free(demangled);
25 };
24 26
25 if (is_itanium(mangled)) { 27 if (is_itanium(mangled)) {
26 demangled = llvm::itaniumDemangle(mangled.c_str()); 28 demangled = llvm::itaniumDemangle(mangled.c_str());
diff --git a/src/common/host_memory.cpp b/src/common/host_memory.cpp
index 860c39e6a..e0b5a6a67 100644
--- a/src/common/host_memory.cpp
+++ b/src/common/host_memory.cpp
@@ -430,11 +430,11 @@ public:
430 explicit Impl(size_t backing_size_, size_t virtual_size_) 430 explicit Impl(size_t backing_size_, size_t virtual_size_)
431 : backing_size{backing_size_}, virtual_size{virtual_size_} { 431 : backing_size{backing_size_}, virtual_size{virtual_size_} {
432 bool good = false; 432 bool good = false;
433 SCOPE_EXIT({ 433 SCOPE_EXIT {
434 if (!good) { 434 if (!good) {
435 Release(); 435 Release();
436 } 436 }
437 }); 437 };
438 438
439 long page_size = sysconf(_SC_PAGESIZE); 439 long page_size = sysconf(_SC_PAGESIZE);
440 if (page_size != 0x1000) { 440 if (page_size != 0x1000) {
diff --git a/src/common/page_table.cpp b/src/common/page_table.cpp
index 85dc18c11..3205eb7da 100644
--- a/src/common/page_table.cpp
+++ b/src/common/page_table.cpp
@@ -24,10 +24,10 @@ bool PageTable::ContinueTraversal(TraversalEntry* out_entry, TraversalContext* c
24 out_entry->block_size = page_size; 24 out_entry->block_size = page_size;
25 25
26 // Regardless of whether the page was mapped, advance on exit. 26 // Regardless of whether the page was mapped, advance on exit.
27 SCOPE_EXIT({ 27 SCOPE_EXIT {
28 context->next_page += 1; 28 context->next_page += 1;
29 context->next_offset += page_size; 29 context->next_offset += page_size;
30 }); 30 };
31 31
32 // Validate that we can read the actual entry. 32 // Validate that we can read the actual entry.
33 const auto page = context->next_page; 33 const auto page = context->next_page;
diff --git a/src/common/scope_exit.h b/src/common/scope_exit.h
index e9c789c88..f3e88cde9 100644
--- a/src/common/scope_exit.h
+++ b/src/common/scope_exit.h
@@ -7,29 +7,61 @@
7#include "common/common_funcs.h" 7#include "common/common_funcs.h"
8 8
9namespace detail { 9namespace detail {
10template <typename Func> 10template <class F>
11struct ScopeExitHelper { 11class ScopeGuard {
12 explicit ScopeExitHelper(Func&& func_) : func(std::move(func_)) {} 12 YUZU_NON_COPYABLE(ScopeGuard);
13 ~ScopeExitHelper() { 13
14private:
15 F f;
16 bool active;
17
18public:
19 constexpr ScopeGuard(F f_) : f(std::move(f_)), active(true) {}
20 constexpr ~ScopeGuard() {
14 if (active) { 21 if (active) {
15 func(); 22 f();
16 } 23 }
17 } 24 }
18 25 constexpr void Cancel() {
19 void Cancel() {
20 active = false; 26 active = false;
21 } 27 }
22 28
23 Func func; 29 constexpr ScopeGuard(ScopeGuard&& rhs) : f(std::move(rhs.f)), active(rhs.active) {
24 bool active{true}; 30 rhs.Cancel();
31 }
32
33 ScopeGuard& operator=(ScopeGuard&& rhs) = delete;
25}; 34};
26 35
27template <typename Func> 36template <class F>
28ScopeExitHelper<Func> ScopeExit(Func&& func) { 37constexpr ScopeGuard<F> MakeScopeGuard(F f) {
29 return ScopeExitHelper<Func>(std::forward<Func>(func)); 38 return ScopeGuard<F>(std::move(f));
30} 39}
40
41enum class ScopeGuardOnExit {};
42
43template <typename F>
44constexpr ScopeGuard<F> operator+(ScopeGuardOnExit, F&& f) {
45 return ScopeGuard<F>(std::forward<F>(f));
46}
47
31} // namespace detail 48} // namespace detail
32 49
50#define CONCATENATE_IMPL(s1, s2) s1##s2
51#define CONCATENATE(s1, s2) CONCATENATE_IMPL(s1, s2)
52
53#ifdef __COUNTER__
54#define ANONYMOUS_VARIABLE(pref) CONCATENATE(pref, __COUNTER__)
55#else
56#define ANONYMOUS_VARIABLE(pref) CONCATENATE(pref, __LINE__)
57#endif
58
59/**
60 * This macro is similar to SCOPE_EXIT, except the object is caller managed. This is intended to be
61 * used when the caller might want to cancel the ScopeExit.
62 */
63#define SCOPE_GUARD detail::ScopeGuardOnExit() + [&]()
64
33/** 65/**
34 * This macro allows you to conveniently specify a block of code that will run on scope exit. Handy 66 * This macro allows you to conveniently specify a block of code that will run on scope exit. Handy
35 * for doing ad-hoc clean-up tasks in a function with multiple returns. 67 * for doing ad-hoc clean-up tasks in a function with multiple returns.
@@ -38,7 +70,7 @@ ScopeExitHelper<Func> ScopeExit(Func&& func) {
38 * \code 70 * \code
39 * const int saved_val = g_foo; 71 * const int saved_val = g_foo;
40 * g_foo = 55; 72 * g_foo = 55;
41 * SCOPE_EXIT({ g_foo = saved_val; }); 73 * SCOPE_EXIT{ g_foo = saved_val; };
42 * 74 *
43 * if (Bar()) { 75 * if (Bar()) {
44 * return 0; 76 * return 0;
@@ -47,10 +79,4 @@ ScopeExitHelper<Func> ScopeExit(Func&& func) {
47 * } 79 * }
48 * \endcode 80 * \endcode
49 */ 81 */
50#define SCOPE_EXIT(body) auto CONCAT2(scope_exit_helper_, __LINE__) = detail::ScopeExit([&]() body) 82#define SCOPE_EXIT auto ANONYMOUS_VARIABLE(SCOPE_EXIT_STATE_) = SCOPE_GUARD
51
52/**
53 * This macro is similar to SCOPE_EXIT, except the object is caller managed. This is intended to be
54 * used when the caller might want to cancel the ScopeExit.
55 */
56#define SCOPE_GUARD(body) detail::ScopeExit([&]() body)
diff --git a/src/common/settings_input.h b/src/common/settings_input.h
index 53a95ef8f..a99bb0892 100644
--- a/src/common/settings_input.h
+++ b/src/common/settings_input.h
@@ -395,6 +395,10 @@ struct PlayerInput {
395 u32 button_color_left; 395 u32 button_color_left;
396 u32 button_color_right; 396 u32 button_color_right;
397 std::string profile_name; 397 std::string profile_name;
398
399 // This is meant to tell the Android frontend whether to use a device's built-in vibration
400 // motor or a controller's vibrations.
401 bool use_system_vibrator;
398}; 402};
399 403
400struct TouchscreenInput { 404struct TouchscreenInput {
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 93c548942..f67a12f8f 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -2,8 +2,8 @@
2# SPDX-License-Identifier: GPL-2.0-or-later 2# SPDX-License-Identifier: GPL-2.0-or-later
3 3
4add_library(core STATIC 4add_library(core STATIC
5 arm/arm_interface.h
6 arm/arm_interface.cpp 5 arm/arm_interface.cpp
6 arm/arm_interface.h
7 arm/debug.cpp 7 arm/debug.cpp
8 arm/debug.h 8 arm/debug.h
9 arm/exclusive_monitor.cpp 9 arm/exclusive_monitor.cpp
@@ -37,10 +37,10 @@ add_library(core STATIC
37 debugger/gdbstub.h 37 debugger/gdbstub.h
38 debugger/gdbstub_arch.cpp 38 debugger/gdbstub_arch.cpp
39 debugger/gdbstub_arch.h 39 debugger/gdbstub_arch.h
40 device_memory_manager.h
41 device_memory_manager.inc
42 device_memory.cpp 40 device_memory.cpp
43 device_memory.h 41 device_memory.h
42 device_memory_manager.h
43 device_memory_manager.inc
44 file_sys/bis_factory.cpp 44 file_sys/bis_factory.cpp
45 file_sys/bis_factory.h 45 file_sys/bis_factory.h
46 file_sys/card_image.cpp 46 file_sys/card_image.cpp
@@ -390,6 +390,20 @@ add_library(core STATIC
390 hle/service/acc/errors.h 390 hle/service/acc/errors.h
391 hle/service/acc/profile_manager.cpp 391 hle/service/acc/profile_manager.cpp
392 hle/service/acc/profile_manager.h 392 hle/service/acc/profile_manager.h
393 hle/service/am/am.cpp
394 hle/service/am/am.h
395 hle/service/am/am_results.h
396 hle/service/am/am_types.h
397 hle/service/am/applet.cpp
398 hle/service/am/applet.h
399 hle/service/am/applet_data_broker.cpp
400 hle/service/am/applet_data_broker.h
401 hle/service/am/applet_manager.cpp
402 hle/service/am/applet_manager.h
403 hle/service/am/applet_message_queue.cpp
404 hle/service/am/applet_message_queue.h
405 hle/service/am/display_layer_manager.cpp
406 hle/service/am/display_layer_manager.h
393 hle/service/am/frontend/applet_cabinet.cpp 407 hle/service/am/frontend/applet_cabinet.cpp
394 hle/service/am/frontend/applet_cabinet.h 408 hle/service/am/frontend/applet_cabinet.h
395 hle/service/am/frontend/applet_controller.cpp 409 hle/service/am/frontend/applet_controller.cpp
@@ -411,24 +425,10 @@ add_library(core STATIC
411 hle/service/am/frontend/applet_web_browser_types.h 425 hle/service/am/frontend/applet_web_browser_types.h
412 hle/service/am/frontend/applets.cpp 426 hle/service/am/frontend/applets.cpp
413 hle/service/am/frontend/applets.h 427 hle/service/am/frontend/applets.h
414 hle/service/am/am.cpp
415 hle/service/am/am.h
416 hle/service/am/am_results.h
417 hle/service/am/am_types.h
418 hle/service/am/applet.cpp
419 hle/service/am/applet.h
420 hle/service/am/applet_manager.cpp
421 hle/service/am/applet_data_broker.cpp
422 hle/service/am/applet_data_broker.h
423 hle/service/am/applet_manager.h
424 hle/service/am/applet_message_queue.cpp
425 hle/service/am/applet_message_queue.h
426 hle/service/am/hid_registration.cpp 428 hle/service/am/hid_registration.cpp
427 hle/service/am/hid_registration.h 429 hle/service/am/hid_registration.h
428 hle/service/am/library_applet_storage.cpp 430 hle/service/am/library_applet_storage.cpp
429 hle/service/am/library_applet_storage.h 431 hle/service/am/library_applet_storage.h
430 hle/service/am/managed_layer_holder.cpp
431 hle/service/am/managed_layer_holder.h
432 hle/service/am/process.cpp 432 hle/service/am/process.cpp
433 hle/service/am/process.h 433 hle/service/am/process.h
434 hle/service/am/service/all_system_applet_proxies_service.cpp 434 hle/service/am/service/all_system_applet_proxies_service.cpp
@@ -441,10 +441,10 @@ add_library(core STATIC
441 hle/service/am/service/application_creator.h 441 hle/service/am/service/application_creator.h
442 hle/service/am/service/application_functions.cpp 442 hle/service/am/service/application_functions.cpp
443 hle/service/am/service/application_functions.h 443 hle/service/am/service/application_functions.h
444 hle/service/am/service/application_proxy_service.cpp
445 hle/service/am/service/application_proxy_service.h
446 hle/service/am/service/application_proxy.cpp 444 hle/service/am/service/application_proxy.cpp
447 hle/service/am/service/application_proxy.h 445 hle/service/am/service/application_proxy.h
446 hle/service/am/service/application_proxy_service.cpp
447 hle/service/am/service/application_proxy_service.h
448 hle/service/am/service/audio_controller.cpp 448 hle/service/am/service/audio_controller.cpp
449 hle/service/am/service/audio_controller.h 449 hle/service/am/service/audio_controller.h
450 hle/service/am/service/common_state_getter.cpp 450 hle/service/am/service/common_state_getter.cpp
@@ -473,16 +473,14 @@ add_library(core STATIC
473 hle/service/am/service/process_winding_controller.h 473 hle/service/am/service/process_winding_controller.h
474 hle/service/am/service/self_controller.cpp 474 hle/service/am/service/self_controller.cpp
475 hle/service/am/service/self_controller.h 475 hle/service/am/service/self_controller.h
476 hle/service/am/service/storage_accessor.cpp
477 hle/service/am/service/storage_accessor.h
478 hle/service/am/service/storage.cpp 476 hle/service/am/service/storage.cpp
479 hle/service/am/service/storage.h 477 hle/service/am/service/storage.h
478 hle/service/am/service/storage_accessor.cpp
479 hle/service/am/service/storage_accessor.h
480 hle/service/am/service/system_applet_proxy.cpp 480 hle/service/am/service/system_applet_proxy.cpp
481 hle/service/am/service/system_applet_proxy.h 481 hle/service/am/service/system_applet_proxy.h
482 hle/service/am/service/window_controller.cpp 482 hle/service/am/service/window_controller.cpp
483 hle/service/am/service/window_controller.h 483 hle/service/am/service/window_controller.h
484 hle/service/am/system_buffer_manager.cpp
485 hle/service/am/system_buffer_manager.h
486 hle/service/aoc/aoc_u.cpp 484 hle/service/aoc/aoc_u.cpp
487 hle/service/aoc/aoc_u.h 485 hle/service/aoc/aoc_u.h
488 hle/service/apm/apm.cpp 486 hle/service/apm/apm.cpp
@@ -491,12 +489,12 @@ add_library(core STATIC
491 hle/service/apm/apm_controller.h 489 hle/service/apm/apm_controller.h
492 hle/service/apm/apm_interface.cpp 490 hle/service/apm/apm_interface.cpp
493 hle/service/apm/apm_interface.h 491 hle/service/apm/apm_interface.h
494 hle/service/audio/audctl.cpp
495 hle/service/audio/audctl.h
496 hle/service/audio/audin_u.cpp 492 hle/service/audio/audin_u.cpp
497 hle/service/audio/audin_u.h 493 hle/service/audio/audin_u.h
498 hle/service/audio/audio.cpp 494 hle/service/audio/audio.cpp
499 hle/service/audio/audio.h 495 hle/service/audio/audio.h
496 hle/service/audio/audio_controller.cpp
497 hle/service/audio/audio_controller.h
500 hle/service/audio/audout_u.cpp 498 hle/service/audio/audout_u.cpp
501 hle/service/audio/audout_u.h 499 hle/service/audio/audout_u.h
502 hle/service/audio/audrec_a.cpp 500 hle/service/audio/audrec_a.cpp
@@ -510,18 +508,6 @@ add_library(core STATIC
510 hle/service/audio/hwopus.h 508 hle/service/audio/hwopus.h
511 hle/service/bcat/backend/backend.cpp 509 hle/service/bcat/backend/backend.cpp
512 hle/service/bcat/backend/backend.h 510 hle/service/bcat/backend/backend.h
513 hle/service/bcat/news/newly_arrived_event_holder.cpp
514 hle/service/bcat/news/newly_arrived_event_holder.h
515 hle/service/bcat/news/news_data_service.cpp
516 hle/service/bcat/news/news_data_service.h
517 hle/service/bcat/news/news_database_service.cpp
518 hle/service/bcat/news/news_database_service.h
519 hle/service/bcat/news/news_service.cpp
520 hle/service/bcat/news/news_service.h
521 hle/service/bcat/news/overwrite_event_holder.cpp
522 hle/service/bcat/news/overwrite_event_holder.h
523 hle/service/bcat/news/service_creator.cpp
524 hle/service/bcat/news/service_creator.h
525 hle/service/bcat/bcat.cpp 511 hle/service/bcat/bcat.cpp
526 hle/service/bcat/bcat.h 512 hle/service/bcat/bcat.h
527 hle/service/bcat/bcat_result.h 513 hle/service/bcat/bcat_result.h
@@ -537,6 +523,18 @@ add_library(core STATIC
537 hle/service/bcat/delivery_cache_progress_service.h 523 hle/service/bcat/delivery_cache_progress_service.h
538 hle/service/bcat/delivery_cache_storage_service.cpp 524 hle/service/bcat/delivery_cache_storage_service.cpp
539 hle/service/bcat/delivery_cache_storage_service.h 525 hle/service/bcat/delivery_cache_storage_service.h
526 hle/service/bcat/news/newly_arrived_event_holder.cpp
527 hle/service/bcat/news/newly_arrived_event_holder.h
528 hle/service/bcat/news/news_data_service.cpp
529 hle/service/bcat/news/news_data_service.h
530 hle/service/bcat/news/news_database_service.cpp
531 hle/service/bcat/news/news_database_service.h
532 hle/service/bcat/news/news_service.cpp
533 hle/service/bcat/news/news_service.h
534 hle/service/bcat/news/overwrite_event_holder.cpp
535 hle/service/bcat/news/overwrite_event_holder.h
536 hle/service/bcat/news/service_creator.cpp
537 hle/service/bcat/news/service_creator.h
540 hle/service/bcat/service_creator.cpp 538 hle/service/bcat/service_creator.cpp
541 hle/service/bcat/service_creator.h 539 hle/service/bcat/service_creator.h
542 hle/service/bpc/bpc.cpp 540 hle/service/bpc/bpc.cpp
@@ -610,8 +608,6 @@ add_library(core STATIC
610 hle/service/filesystem/romfs_controller.h 608 hle/service/filesystem/romfs_controller.h
611 hle/service/filesystem/save_data_controller.cpp 609 hle/service/filesystem/save_data_controller.cpp
612 hle/service/filesystem/save_data_controller.h 610 hle/service/filesystem/save_data_controller.h
613 hle/service/fgm/fgm.cpp
614 hle/service/fgm/fgm.h
615 hle/service/friend/friend.cpp 611 hle/service/friend/friend.cpp
616 hle/service/friend/friend.h 612 hle/service/friend/friend.h
617 hle/service/friend/friend_interface.cpp 613 hle/service/friend/friend_interface.cpp
@@ -749,15 +745,48 @@ add_library(core STATIC
749 hle/service/nim/nim.h 745 hle/service/nim/nim.h
750 hle/service/npns/npns.cpp 746 hle/service/npns/npns.cpp
751 hle/service/npns/npns.h 747 hle/service/npns/npns.h
752 hle/service/ns/errors.h 748 hle/service/ns/account_proxy_interface.cpp
753 hle/service/ns/iplatform_service_manager.cpp 749 hle/service/ns/account_proxy_interface.h
754 hle/service/ns/iplatform_service_manager.h 750 hle/service/ns/application_manager_interface.cpp
751 hle/service/ns/application_manager_interface.h
752 hle/service/ns/application_version_interface.cpp
753 hle/service/ns/application_version_interface.h
754 hle/service/ns/content_management_interface.cpp
755 hle/service/ns/content_management_interface.h
756 hle/service/ns/develop_interface.cpp
757 hle/service/ns/develop_interface.h
758 hle/service/ns/document_interface.cpp
759 hle/service/ns/document_interface.h
760 hle/service/ns/download_task_interface.cpp
761 hle/service/ns/download_task_interface.h
762 hle/service/ns/dynamic_rights_interface.cpp
763 hle/service/ns/dynamic_rights_interface.h
764 hle/service/ns/ecommerce_interface.cpp
765 hle/service/ns/ecommerce_interface.h
766 hle/service/ns/factory_reset_interface.cpp
767 hle/service/ns/factory_reset_interface.h
755 hle/service/ns/language.cpp 768 hle/service/ns/language.cpp
756 hle/service/ns/language.h 769 hle/service/ns/language.h
757 hle/service/ns/ns.cpp 770 hle/service/ns/ns.cpp
758 hle/service/ns/ns.h 771 hle/service/ns/ns.h
759 hle/service/ns/pdm_qry.cpp 772 hle/service/ns/ns_results.h
760 hle/service/ns/pdm_qry.h 773 hle/service/ns/ns_types.h
774 hle/service/ns/platform_service_manager.cpp
775 hle/service/ns/platform_service_manager.h
776 hle/service/ns/query_service.cpp
777 hle/service/ns/query_service.h
778 hle/service/ns/read_only_application_control_data_interface.cpp
779 hle/service/ns/read_only_application_control_data_interface.h
780 hle/service/ns/read_only_application_record_interface.cpp
781 hle/service/ns/read_only_application_record_interface.h
782 hle/service/ns/service_getter_interface.cpp
783 hle/service/ns/service_getter_interface.h
784 hle/service/ns/system_update_control.cpp
785 hle/service/ns/system_update_control.h
786 hle/service/ns/system_update_interface.cpp
787 hle/service/ns/system_update_interface.h
788 hle/service/ns/vulnerability_manager_interface.cpp
789 hle/service/ns/vulnerability_manager_interface.h
761 hle/service/nvdrv/core/container.cpp 790 hle/service/nvdrv/core/container.cpp
762 hle/service/nvdrv/core/container.h 791 hle/service/nvdrv/core/container.h
763 hle/service/nvdrv/core/heap_mapper.cpp 792 hle/service/nvdrv/core/heap_mapper.cpp
@@ -810,14 +839,14 @@ add_library(core STATIC
810 hle/service/nvnflinger/consumer_base.cpp 839 hle/service/nvnflinger/consumer_base.cpp
811 hle/service/nvnflinger/consumer_base.h 840 hle/service/nvnflinger/consumer_base.h
812 hle/service/nvnflinger/consumer_listener.h 841 hle/service/nvnflinger/consumer_listener.h
813 hle/service/nvnflinger/fb_share_buffer_manager.cpp
814 hle/service/nvnflinger/fb_share_buffer_manager.h
815 hle/service/nvnflinger/graphic_buffer_producer.cpp 842 hle/service/nvnflinger/graphic_buffer_producer.cpp
816 hle/service/nvnflinger/graphic_buffer_producer.h 843 hle/service/nvnflinger/graphic_buffer_producer.h
817 hle/service/nvnflinger/hos_binder_driver_server.cpp
818 hle/service/nvnflinger/hos_binder_driver_server.h
819 hle/service/nvnflinger/hardware_composer.cpp 844 hle/service/nvnflinger/hardware_composer.cpp
820 hle/service/nvnflinger/hardware_composer.h 845 hle/service/nvnflinger/hardware_composer.h
846 hle/service/nvnflinger/hos_binder_driver.cpp
847 hle/service/nvnflinger/hos_binder_driver.h
848 hle/service/nvnflinger/hos_binder_driver_server.cpp
849 hle/service/nvnflinger/hos_binder_driver_server.h
821 hle/service/nvnflinger/hwc_layer.h 850 hle/service/nvnflinger/hwc_layer.h
822 hle/service/nvnflinger/nvnflinger.cpp 851 hle/service/nvnflinger/nvnflinger.cpp
823 hle/service/nvnflinger/nvnflinger.h 852 hle/service/nvnflinger/nvnflinger.h
@@ -825,6 +854,8 @@ add_library(core STATIC
825 hle/service/nvnflinger/pixel_format.h 854 hle/service/nvnflinger/pixel_format.h
826 hle/service/nvnflinger/producer_listener.h 855 hle/service/nvnflinger/producer_listener.h
827 hle/service/nvnflinger/status.h 856 hle/service/nvnflinger/status.h
857 hle/service/nvnflinger/surface_flinger.cpp
858 hle/service/nvnflinger/surface_flinger.h
828 hle/service/nvnflinger/ui/fence.h 859 hle/service/nvnflinger/ui/fence.h
829 hle/service/nvnflinger/ui/graphic_buffer.cpp 860 hle/service/nvnflinger/ui/graphic_buffer.cpp
830 hle/service/nvnflinger/ui/graphic_buffer.h 861 hle/service/nvnflinger/ui/graphic_buffer.h
@@ -841,11 +872,11 @@ add_library(core STATIC
841 hle/service/omm/power_state_interface.h 872 hle/service/omm/power_state_interface.h
842 hle/service/os/event.cpp 873 hle/service/os/event.cpp
843 hle/service/os/event.h 874 hle/service/os/event.h
875 hle/service/os/multi_wait.cpp
876 hle/service/os/multi_wait.h
844 hle/service/os/multi_wait_holder.cpp 877 hle/service/os/multi_wait_holder.cpp
845 hle/service/os/multi_wait_holder.h 878 hle/service/os/multi_wait_holder.h
846 hle/service/os/multi_wait_utils.h 879 hle/service/os/multi_wait_utils.h
847 hle/service/os/multi_wait.cpp
848 hle/service/os/multi_wait.h
849 hle/service/os/mutex.cpp 880 hle/service/os/mutex.cpp
850 hle/service/os/mutex.h 881 hle/service/os/mutex.h
851 hle/service/pcie/pcie.cpp 882 hle/service/pcie/pcie.cpp
@@ -883,15 +914,17 @@ add_library(core STATIC
883 hle/service/psc/time/common.cpp 914 hle/service/psc/time/common.cpp
884 hle/service/psc/time/common.h 915 hle/service/psc/time/common.h
885 hle/service/psc/time/errors.h 916 hle/service/psc/time/errors.h
886 hle/service/psc/time/shared_memory.cpp
887 hle/service/psc/time/shared_memory.h
888 hle/service/psc/time/static.cpp
889 hle/service/psc/time/static.h
890 hle/service/psc/time/manager.h 917 hle/service/psc/time/manager.h
918 hle/service/psc/time/power_state_request_manager.cpp
919 hle/service/psc/time/power_state_request_manager.h
891 hle/service/psc/time/power_state_service.cpp 920 hle/service/psc/time/power_state_service.cpp
892 hle/service/psc/time/power_state_service.h 921 hle/service/psc/time/power_state_service.h
893 hle/service/psc/time/service_manager.cpp 922 hle/service/psc/time/service_manager.cpp
894 hle/service/psc/time/service_manager.h 923 hle/service/psc/time/service_manager.h
924 hle/service/psc/time/shared_memory.cpp
925 hle/service/psc/time/shared_memory.h
926 hle/service/psc/time/static.cpp
927 hle/service/psc/time/static.h
895 hle/service/psc/time/steady_clock.cpp 928 hle/service/psc/time/steady_clock.cpp
896 hle/service/psc/time/steady_clock.h 929 hle/service/psc/time/steady_clock.h
897 hle/service/psc/time/system_clock.cpp 930 hle/service/psc/time/system_clock.cpp
@@ -900,8 +933,6 @@ add_library(core STATIC
900 hle/service/psc/time/time_zone.h 933 hle/service/psc/time/time_zone.h
901 hle/service/psc/time/time_zone_service.cpp 934 hle/service/psc/time/time_zone_service.cpp
902 hle/service/psc/time/time_zone_service.h 935 hle/service/psc/time/time_zone_service.h
903 hle/service/psc/time/power_state_request_manager.cpp
904 hle/service/psc/time/power_state_request_manager.h
905 hle/service/ptm/psm.cpp 936 hle/service/ptm/psm.cpp
906 hle/service/ptm/psm.h 937 hle/service/ptm/psm.h
907 hle/service/ptm/ptm.cpp 938 hle/service/ptm/ptm.cpp
@@ -918,19 +949,21 @@ add_library(core STATIC
918 hle/service/server_manager.h 949 hle/service/server_manager.h
919 hle/service/service.cpp 950 hle/service/service.cpp
920 hle/service/service.h 951 hle/service/service.h
952 hle/service/services.cpp
953 hle/service/services.h
954 hle/service/set/factory_settings_server.cpp
955 hle/service/set/factory_settings_server.h
956 hle/service/set/firmware_debug_settings_server.cpp
957 hle/service/set/firmware_debug_settings_server.h
958 hle/service/set/key_code_map.h
921 hle/service/set/setting_formats/appln_settings.cpp 959 hle/service/set/setting_formats/appln_settings.cpp
922 hle/service/set/setting_formats/appln_settings.h 960 hle/service/set/setting_formats/appln_settings.h
923 hle/service/set/setting_formats/device_settings.cpp 961 hle/service/set/setting_formats/device_settings.cpp
924 hle/service/set/setting_formats/device_settings.h 962 hle/service/set/setting_formats/device_settings.h
925 hle/service/set/setting_formats/system_settings.cpp
926 hle/service/set/setting_formats/system_settings.h
927 hle/service/set/setting_formats/private_settings.cpp 963 hle/service/set/setting_formats/private_settings.cpp
928 hle/service/set/setting_formats/private_settings.h 964 hle/service/set/setting_formats/private_settings.h
929 hle/service/set/factory_settings_server.cpp 965 hle/service/set/setting_formats/system_settings.cpp
930 hle/service/set/factory_settings_server.h 966 hle/service/set/setting_formats/system_settings.h
931 hle/service/set/firmware_debug_settings_server.cpp
932 hle/service/set/firmware_debug_settings_server.h
933 hle/service/set/key_code_map.h
934 hle/service/set/settings.cpp 967 hle/service/set/settings.cpp
935 hle/service/set/settings.h 968 hle/service/set/settings.h
936 hle/service/set/settings_server.cpp 969 hle/service/set/settings_server.cpp
@@ -965,30 +998,36 @@ add_library(core STATIC
965 hle/service/ssl/ssl_backend.h 998 hle/service/ssl/ssl_backend.h
966 hle/service/usb/usb.cpp 999 hle/service/usb/usb.cpp
967 hle/service/usb/usb.h 1000 hle/service/usb/usb.h
968 hle/service/vi/display/vi_display.cpp
969 hle/service/vi/display/vi_display.h
970 hle/service/vi/layer/vi_layer.cpp
971 hle/service/vi/layer/vi_layer.h
972 hle/service/vi/application_display_service.cpp 1001 hle/service/vi/application_display_service.cpp
973 hle/service/vi/application_display_service.h 1002 hle/service/vi/application_display_service.h
974 hle/service/vi/application_root_service.cpp 1003 hle/service/vi/application_root_service.cpp
975 hle/service/vi/application_root_service.h 1004 hle/service/vi/application_root_service.h
976 hle/service/vi/hos_binder_driver.cpp 1005 hle/service/vi/conductor.cpp
977 hle/service/vi/hos_binder_driver.h 1006 hle/service/vi/conductor.h
1007 hle/service/vi/container.cpp
1008 hle/service/vi/container.h
1009 hle/service/vi/display.h
1010 hle/service/vi/display_list.h
1011 hle/service/vi/layer.h
1012 hle/service/vi/layer_list.h
978 hle/service/vi/manager_display_service.cpp 1013 hle/service/vi/manager_display_service.cpp
979 hle/service/vi/manager_display_service.h 1014 hle/service/vi/manager_display_service.h
980 hle/service/vi/manager_root_service.cpp 1015 hle/service/vi/manager_root_service.cpp
981 hle/service/vi/manager_root_service.h 1016 hle/service/vi/manager_root_service.h
982 hle/service/vi/service_creator.cpp 1017 hle/service/vi/service_creator.cpp
983 hle/service/vi/service_creator.h 1018 hle/service/vi/service_creator.h
1019 hle/service/vi/shared_buffer_manager.cpp
1020 hle/service/vi/shared_buffer_manager.h
984 hle/service/vi/system_display_service.cpp 1021 hle/service/vi/system_display_service.cpp
985 hle/service/vi/system_display_service.h 1022 hle/service/vi/system_display_service.h
986 hle/service/vi/system_root_service.cpp 1023 hle/service/vi/system_root_service.cpp
987 hle/service/vi/system_root_service.h 1024 hle/service/vi/system_root_service.h
988 hle/service/vi/vi_results.h
989 hle/service/vi/vi_types.h
990 hle/service/vi/vi.cpp 1025 hle/service/vi/vi.cpp
991 hle/service/vi/vi.h 1026 hle/service/vi/vi.h
1027 hle/service/vi/vi_results.h
1028 hle/service/vi/vi_types.h
1029 hle/service/vi/vsync_manager.cpp
1030 hle/service/vi/vsync_manager.h
992 internal_network/network.cpp 1031 internal_network/network.cpp
993 internal_network/network.h 1032 internal_network/network.h
994 internal_network/network_interface.cpp 1033 internal_network/network_interface.cpp
diff --git a/src/core/core.cpp b/src/core/core.cpp
index 435ef6793..9e8936728 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -47,6 +47,7 @@
47#include "core/hle/service/psc/time/system_clock.h" 47#include "core/hle/service/psc/time/system_clock.h"
48#include "core/hle/service/psc/time/time_zone_service.h" 48#include "core/hle/service/psc/time/time_zone_service.h"
49#include "core/hle/service/service.h" 49#include "core/hle/service/service.h"
50#include "core/hle/service/services.h"
50#include "core/hle/service/set/system_settings_server.h" 51#include "core/hle/service/set/system_settings_server.h"
51#include "core/hle/service/sm/sm.h" 52#include "core/hle/service/sm/sm.h"
52#include "core/internal_network/network.h" 53#include "core/internal_network/network.h"
@@ -242,7 +243,7 @@ struct System::Impl {
242 void Run() { 243 void Run() {
243 std::unique_lock<std::mutex> lk(suspend_guard); 244 std::unique_lock<std::mutex> lk(suspend_guard);
244 245
245 kernel.SuspendApplication(false); 246 kernel.SuspendEmulation(false);
246 core_timing.SyncPause(false); 247 core_timing.SyncPause(false);
247 is_paused.store(false, std::memory_order_relaxed); 248 is_paused.store(false, std::memory_order_relaxed);
248 } 249 }
@@ -251,7 +252,7 @@ struct System::Impl {
251 std::unique_lock<std::mutex> lk(suspend_guard); 252 std::unique_lock<std::mutex> lk(suspend_guard);
252 253
253 core_timing.SyncPause(true); 254 core_timing.SyncPause(true);
254 kernel.SuspendApplication(true); 255 kernel.SuspendEmulation(true);
255 is_paused.store(true, std::memory_order_relaxed); 256 is_paused.store(true, std::memory_order_relaxed);
256 } 257 }
257 258
@@ -261,7 +262,7 @@ struct System::Impl {
261 262
262 std::unique_lock<std::mutex> StallApplication() { 263 std::unique_lock<std::mutex> StallApplication() {
263 std::unique_lock<std::mutex> lk(suspend_guard); 264 std::unique_lock<std::mutex> lk(suspend_guard);
264 kernel.SuspendApplication(true); 265 kernel.SuspendEmulation(true);
265 core_timing.SyncPause(true); 266 core_timing.SyncPause(true);
266 return lk; 267 return lk;
267 } 268 }
@@ -269,7 +270,7 @@ struct System::Impl {
269 void UnstallApplication() { 270 void UnstallApplication() {
270 if (!IsPaused()) { 271 if (!IsPaused()) {
271 core_timing.SyncPause(false); 272 core_timing.SyncPause(false);
272 kernel.SuspendApplication(false); 273 kernel.SuspendEmulation(false);
273 } 274 }
274 } 275 }
275 276
@@ -310,7 +311,8 @@ struct System::Impl {
310 audio_core = std::make_unique<AudioCore::AudioCore>(system); 311 audio_core = std::make_unique<AudioCore::AudioCore>(system);
311 312
312 service_manager = std::make_shared<Service::SM::ServiceManager>(kernel); 313 service_manager = std::make_shared<Service::SM::ServiceManager>(kernel);
313 services = std::make_unique<Service::Services>(service_manager, system); 314 services =
315 std::make_unique<Service::Services>(service_manager, system, stop_event.get_token());
314 316
315 is_powered_on = true; 317 is_powered_on = true;
316 exit_locked = false; 318 exit_locked = false;
@@ -458,11 +460,10 @@ struct System::Impl {
458 gpu_core->NotifyShutdown(); 460 gpu_core->NotifyShutdown();
459 } 461 }
460 462
463 stop_event.request_stop();
464 core_timing.SyncPause(false);
461 Network::CancelPendingSocketOperations(); 465 Network::CancelPendingSocketOperations();
462 kernel.SuspendApplication(true); 466 kernel.SuspendEmulation(true);
463 if (services) {
464 services->KillNVNFlinger();
465 }
466 kernel.CloseServices(); 467 kernel.CloseServices();
467 kernel.ShutdownCores(); 468 kernel.ShutdownCores();
468 applet_manager.Reset(); 469 applet_manager.Reset();
@@ -480,6 +481,7 @@ struct System::Impl {
480 cpu_manager.Shutdown(); 481 cpu_manager.Shutdown();
481 debugger.reset(); 482 debugger.reset();
482 kernel.Shutdown(); 483 kernel.Shutdown();
484 stop_event = {};
483 Network::RestartSocketOperations(); 485 Network::RestartSocketOperations();
484 486
485 if (auto room_member = room_network.GetRoomMember().lock()) { 487 if (auto room_member = room_network.GetRoomMember().lock()) {
@@ -615,6 +617,7 @@ struct System::Impl {
615 617
616 ExecuteProgramCallback execute_program_callback; 618 ExecuteProgramCallback execute_program_callback;
617 ExitCallback exit_callback; 619 ExitCallback exit_callback;
620 std::stop_source stop_event;
618 621
619 std::array<u64, Core::Hardware::NUM_CPU_CORES> dynarmic_ticks{}; 622 std::array<u64, Core::Hardware::NUM_CPU_CORES> dynarmic_ticks{};
620 std::array<MicroProfileToken, Core::Hardware::NUM_CPU_CORES> microprofile_cpu{}; 623 std::array<MicroProfileToken, Core::Hardware::NUM_CPU_CORES> microprofile_cpu{};
diff --git a/src/core/cpu_manager.cpp b/src/core/cpu_manager.cpp
index 7a5c22f78..9b1c77387 100644
--- a/src/core/cpu_manager.cpp
+++ b/src/core/cpu_manager.cpp
@@ -199,10 +199,10 @@ void CpuManager::RunThread(std::stop_token token, std::size_t core) {
199 data.host_context = Common::Fiber::ThreadToFiber(); 199 data.host_context = Common::Fiber::ThreadToFiber();
200 200
201 // Cleanup 201 // Cleanup
202 SCOPE_EXIT({ 202 SCOPE_EXIT {
203 data.host_context->Exit(); 203 data.host_context->Exit();
204 MicroProfileOnThreadExit(); 204 MicroProfileOnThreadExit();
205 }); 205 };
206 206
207 // Running 207 // Running
208 if (!gpu_barrier->Sync(token)) { 208 if (!gpu_barrier->Sync(token)) {
diff --git a/src/core/device_memory_manager.inc b/src/core/device_memory_manager.inc
index 6dfee806c..37c1e69c3 100644
--- a/src/core/device_memory_manager.inc
+++ b/src/core/device_memory_manager.inc
@@ -391,12 +391,12 @@ void DeviceMemoryManager<Traits>::WalkBlock(DAddr addr, std::size_t size, auto o
391 std::min((next_pages << Memory::YUZU_PAGEBITS) - page_offset, remaining_size); 391 std::min((next_pages << Memory::YUZU_PAGEBITS) - page_offset, remaining_size);
392 const auto current_vaddr = 392 const auto current_vaddr =
393 static_cast<u64>((page_index << Memory::YUZU_PAGEBITS) + page_offset); 393 static_cast<u64>((page_index << Memory::YUZU_PAGEBITS) + page_offset);
394 SCOPE_EXIT({ 394 SCOPE_EXIT{
395 page_index += next_pages; 395 page_index += next_pages;
396 page_offset = 0; 396 page_offset = 0;
397 increment(copy_amount); 397 increment(copy_amount);
398 remaining_size -= copy_amount; 398 remaining_size -= copy_amount;
399 }); 399 };
400 400
401 auto phys_addr = compressed_physical_ptr[page_index]; 401 auto phys_addr = compressed_physical_ptr[page_index];
402 if (phys_addr == 0) { 402 if (phys_addr == 0) {
diff --git a/src/core/file_sys/control_metadata.h b/src/core/file_sys/control_metadata.h
index 555b9d8f7..667efbbab 100644
--- a/src/core/file_sys/control_metadata.h
+++ b/src/core/file_sys/control_metadata.h
@@ -64,8 +64,8 @@ struct RawNACP {
64 u64_le cache_storage_size; 64 u64_le cache_storage_size;
65 u64_le cache_storage_journal_size; 65 u64_le cache_storage_journal_size;
66 u64_le cache_storage_data_and_journal_max_size; 66 u64_le cache_storage_data_and_journal_max_size;
67 u64_le cache_storage_max_index; 67 u16_le cache_storage_max_index;
68 INSERT_PADDING_BYTES(0xE70); 68 INSERT_PADDING_BYTES(0xE76);
69}; 69};
70static_assert(sizeof(RawNACP) == 0x4000, "RawNACP has incorrect size."); 70static_assert(sizeof(RawNACP) == 0x4000, "RawNACP has incorrect size.");
71 71
diff --git a/src/core/file_sys/fs_directory.h b/src/core/file_sys/fs_directory.h
index 25c9cb18a..3f90abb8f 100644
--- a/src/core/file_sys/fs_directory.h
+++ b/src/core/file_sys/fs_directory.h
@@ -3,6 +3,10 @@
3 3
4#pragma once 4#pragma once
5 5
6#include <string_view>
7#include "common/common_funcs.h"
8#include "common/common_types.h"
9
6namespace FileSys { 10namespace FileSys {
7 11
8constexpr inline size_t EntryNameLengthMax = 0x300; 12constexpr inline size_t EntryNameLengthMax = 0x300;
diff --git a/src/core/file_sys/fs_path_utility.h b/src/core/file_sys/fs_path_utility.h
index e9011d065..5643141f9 100644
--- a/src/core/file_sys/fs_path_utility.h
+++ b/src/core/file_sys/fs_path_utility.h
@@ -447,7 +447,7 @@ public:
447 char* replacement_path = nullptr; 447 char* replacement_path = nullptr;
448 size_t replacement_path_size = 0; 448 size_t replacement_path_size = 0;
449 449
450 SCOPE_EXIT({ 450 SCOPE_EXIT {
451 if (replacement_path != nullptr) { 451 if (replacement_path != nullptr) {
452 if (std::is_constant_evaluated()) { 452 if (std::is_constant_evaluated()) {
453 delete[] replacement_path; 453 delete[] replacement_path;
@@ -455,7 +455,7 @@ public:
455 Deallocate(replacement_path, replacement_path_size); 455 Deallocate(replacement_path, replacement_path_size);
456 } 456 }
457 } 457 }
458 }); 458 };
459 459
460 // Perform path replacement, if necessary 460 // Perform path replacement, if necessary
461 if (IsParentDirectoryPathReplacementNeeded(cur_path)) { 461 if (IsParentDirectoryPathReplacementNeeded(cur_path)) {
@@ -1102,8 +1102,8 @@ public:
1102 R_SUCCEED(); 1102 R_SUCCEED();
1103 } 1103 }
1104 1104
1105 static Result Normalize(char* dst, size_t dst_size, const char* path, size_t path_len, 1105 static constexpr Result Normalize(char* dst, size_t dst_size, const char* path, size_t path_len,
1106 const PathFlags& flags) { 1106 const PathFlags& flags) {
1107 // Use StringTraits names for remainder of scope 1107 // Use StringTraits names for remainder of scope
1108 using namespace StringTraits; 1108 using namespace StringTraits;
1109 1109
@@ -1199,7 +1199,7 @@ public:
1199 const size_t replaced_src_len = path_len - (src - path); 1199 const size_t replaced_src_len = path_len - (src - path);
1200 1200
1201 char* replaced_src = nullptr; 1201 char* replaced_src = nullptr;
1202 SCOPE_EXIT({ 1202 SCOPE_EXIT {
1203 if (replaced_src != nullptr) { 1203 if (replaced_src != nullptr) {
1204 if (std::is_constant_evaluated()) { 1204 if (std::is_constant_evaluated()) {
1205 delete[] replaced_src; 1205 delete[] replaced_src;
@@ -1207,7 +1207,7 @@ public:
1207 Deallocate(replaced_src, replaced_src_len); 1207 Deallocate(replaced_src, replaced_src_len);
1208 } 1208 }
1209 } 1209 }
1210 }); 1210 };
1211 1211
1212 if (std::is_constant_evaluated()) { 1212 if (std::is_constant_evaluated()) {
1213 replaced_src = new char[replaced_src_len]; 1213 replaced_src = new char[replaced_src_len];
diff --git a/src/core/file_sys/fssystem/fssystem_hierarchical_sha256_storage.cpp b/src/core/file_sys/fssystem/fssystem_hierarchical_sha256_storage.cpp
index caea0b8f8..a68fd973c 100644
--- a/src/core/file_sys/fssystem/fssystem_hierarchical_sha256_storage.cpp
+++ b/src/core/file_sys/fssystem/fssystem_hierarchical_sha256_storage.cpp
@@ -36,7 +36,9 @@ Result HierarchicalSha256Storage::Initialize(VirtualFile* base_storages, s32 lay
36 // Get the base storage size. 36 // Get the base storage size.
37 m_base_storage_size = base_storages[2]->GetSize(); 37 m_base_storage_size = base_storages[2]->GetSize();
38 { 38 {
39 auto size_guard = SCOPE_GUARD({ m_base_storage_size = 0; }); 39 auto size_guard = SCOPE_GUARD {
40 m_base_storage_size = 0;
41 };
40 R_UNLESS(m_base_storage_size <= static_cast<s64>(HashSize) 42 R_UNLESS(m_base_storage_size <= static_cast<s64>(HashSize)
41 << m_log_size_ratio << m_log_size_ratio, 43 << m_log_size_ratio << m_log_size_ratio,
42 ResultHierarchicalSha256BaseStorageTooLarge); 44 ResultHierarchicalSha256BaseStorageTooLarge);
diff --git a/src/core/file_sys/program_metadata.cpp b/src/core/file_sys/program_metadata.cpp
index ae4e441c9..289969cc4 100644
--- a/src/core/file_sys/program_metadata.cpp
+++ b/src/core/file_sys/program_metadata.cpp
@@ -98,7 +98,9 @@ Loader::ResultStatus ProgramMetadata::Load(VirtualFile file) {
98 98
99Loader::ResultStatus ProgramMetadata::Reload(VirtualFile file) { 99Loader::ResultStatus ProgramMetadata::Reload(VirtualFile file) {
100 const u64 original_program_id = aci_header.title_id; 100 const u64 original_program_id = aci_header.title_id;
101 SCOPE_EXIT({ aci_header.title_id = original_program_id; }); 101 SCOPE_EXIT {
102 aci_header.title_id = original_program_id;
103 };
102 104
103 return this->Load(file); 105 return this->Load(file);
104} 106}
diff --git a/src/core/file_sys/system_archive/shared_font.cpp b/src/core/file_sys/system_archive/shared_font.cpp
index deb52069d..9ea16aa59 100644
--- a/src/core/file_sys/system_archive/shared_font.cpp
+++ b/src/core/file_sys/system_archive/shared_font.cpp
@@ -9,7 +9,7 @@
9#include "core/file_sys/system_archive/data/font_standard.h" 9#include "core/file_sys/system_archive/data/font_standard.h"
10#include "core/file_sys/system_archive/shared_font.h" 10#include "core/file_sys/system_archive/shared_font.h"
11#include "core/file_sys/vfs/vfs_vector.h" 11#include "core/file_sys/vfs/vfs_vector.h"
12#include "core/hle/service/ns/iplatform_service_manager.h" 12#include "core/hle/service/ns/platform_service_manager.h"
13 13
14namespace FileSys::SystemArchive { 14namespace FileSys::SystemArchive {
15 15
diff --git a/src/core/hle/kernel/k_client_session.cpp b/src/core/hle/kernel/k_client_session.cpp
index 472e8571c..3e01e3b67 100644
--- a/src/core/hle/kernel/k_client_session.cpp
+++ b/src/core/hle/kernel/k_client_session.cpp
@@ -24,7 +24,9 @@ Result KClientSession::SendSyncRequest(uintptr_t address, size_t size) {
24 // Create a session request. 24 // Create a session request.
25 KSessionRequest* request = KSessionRequest::Create(m_kernel); 25 KSessionRequest* request = KSessionRequest::Create(m_kernel);
26 R_UNLESS(request != nullptr, ResultOutOfResource); 26 R_UNLESS(request != nullptr, ResultOutOfResource);
27 SCOPE_EXIT({ request->Close(); }); 27 SCOPE_EXIT {
28 request->Close();
29 };
28 30
29 // Initialize the request. 31 // Initialize the request.
30 request->Initialize(nullptr, address, size); 32 request->Initialize(nullptr, address, size);
@@ -37,7 +39,9 @@ Result KClientSession::SendAsyncRequest(KEvent* event, uintptr_t address, size_t
37 // Create a session request. 39 // Create a session request.
38 KSessionRequest* request = KSessionRequest::Create(m_kernel); 40 KSessionRequest* request = KSessionRequest::Create(m_kernel);
39 R_UNLESS(request != nullptr, ResultOutOfResource); 41 R_UNLESS(request != nullptr, ResultOutOfResource);
40 SCOPE_EXIT({ request->Close(); }); 42 SCOPE_EXIT {
43 request->Close();
44 };
41 45
42 // Initialize the request. 46 // Initialize the request.
43 request->Initialize(event, address, size); 47 request->Initialize(event, address, size);
diff --git a/src/core/hle/kernel/k_page_table_base.cpp b/src/core/hle/kernel/k_page_table_base.cpp
index 1dd86fb3c..19cdf4f3a 100644
--- a/src/core/hle/kernel/k_page_table_base.cpp
+++ b/src/core/hle/kernel/k_page_table_base.cpp
@@ -1305,11 +1305,11 @@ Result KPageTableBase::UnmapCodeMemory(KProcessAddress dst_address, KProcessAddr
1305 1305
1306 // Ensure that we maintain the instruction cache. 1306 // Ensure that we maintain the instruction cache.
1307 bool reprotected_pages = false; 1307 bool reprotected_pages = false;
1308 SCOPE_EXIT({ 1308 SCOPE_EXIT {
1309 if (reprotected_pages && any_code_pages) { 1309 if (reprotected_pages && any_code_pages) {
1310 InvalidateInstructionCache(m_kernel, this, dst_address, size); 1310 InvalidateInstructionCache(m_kernel, this, dst_address, size);
1311 } 1311 }
1312 }); 1312 };
1313 1313
1314 // Unmap. 1314 // Unmap.
1315 { 1315 {
@@ -1397,7 +1397,9 @@ Result KPageTableBase::MapInsecureMemory(KProcessAddress address, size_t size) {
1397 // Close the opened pages when we're done with them. 1397 // Close the opened pages when we're done with them.
1398 // If the mapping succeeds, each page will gain an extra reference, otherwise they will be freed 1398 // If the mapping succeeds, each page will gain an extra reference, otherwise they will be freed
1399 // automatically. 1399 // automatically.
1400 SCOPE_EXIT({ pg.Close(); }); 1400 SCOPE_EXIT {
1401 pg.Close();
1402 };
1401 1403
1402 // Clear all the newly allocated pages. 1404 // Clear all the newly allocated pages.
1403 for (const auto& it : pg) { 1405 for (const auto& it : pg) {
@@ -1603,7 +1605,9 @@ Result KPageTableBase::AllocateAndMapPagesImpl(PageLinkedList* page_list, KProce
1603 m_kernel.MemoryManager().AllocateAndOpen(std::addressof(pg), num_pages, m_allocate_option)); 1605 m_kernel.MemoryManager().AllocateAndOpen(std::addressof(pg), num_pages, m_allocate_option));
1604 1606
1605 // Ensure that the page group is closed when we're done working with it. 1607 // Ensure that the page group is closed when we're done working with it.
1606 SCOPE_EXIT({ pg.Close(); }); 1608 SCOPE_EXIT {
1609 pg.Close();
1610 };
1607 1611
1608 // Clear all pages. 1612 // Clear all pages.
1609 for (const auto& it : pg) { 1613 for (const auto& it : pg) {
@@ -2191,7 +2195,9 @@ Result KPageTableBase::SetHeapSize(KProcessAddress* out, size_t size) {
2191 // Close the opened pages when we're done with them. 2195 // Close the opened pages when we're done with them.
2192 // If the mapping succeeds, each page will gain an extra reference, otherwise they will be freed 2196 // If the mapping succeeds, each page will gain an extra reference, otherwise they will be freed
2193 // automatically. 2197 // automatically.
2194 SCOPE_EXIT({ pg.Close(); }); 2198 SCOPE_EXIT {
2199 pg.Close();
2200 };
2195 2201
2196 // Clear all the newly allocated pages. 2202 // Clear all the newly allocated pages.
2197 for (const auto& it : pg) { 2203 for (const auto& it : pg) {
@@ -2592,7 +2598,9 @@ Result KPageTableBase::UnmapIoRegion(KProcessAddress dst_address, KPhysicalAddre
2592 // Temporarily unlock ourselves, so that other operations can occur while we flush the 2598 // Temporarily unlock ourselves, so that other operations can occur while we flush the
2593 // region. 2599 // region.
2594 m_general_lock.Unlock(); 2600 m_general_lock.Unlock();
2595 SCOPE_EXIT({ m_general_lock.Lock(); }); 2601 SCOPE_EXIT {
2602 m_general_lock.Lock();
2603 };
2596 2604
2597 // Flush the region. 2605 // Flush the region.
2598 R_ASSERT(FlushDataCache(dst_address, size)); 2606 R_ASSERT(FlushDataCache(dst_address, size));
@@ -3311,10 +3319,10 @@ Result KPageTableBase::ReadIoMemoryImpl(KProcessAddress dst_addr, KPhysicalAddre
3311 // Ensure we unmap the io memory when we're done with it. 3319 // Ensure we unmap the io memory when we're done with it.
3312 const KPageProperties unmap_properties = 3320 const KPageProperties unmap_properties =
3313 KPageProperties{KMemoryPermission::None, false, false, DisableMergeAttribute::None}; 3321 KPageProperties{KMemoryPermission::None, false, false, DisableMergeAttribute::None};
3314 SCOPE_EXIT({ 3322 SCOPE_EXIT {
3315 R_ASSERT(this->Operate(updater.GetPageList(), io_addr, map_size / PageSize, 0, false, 3323 R_ASSERT(this->Operate(updater.GetPageList(), io_addr, map_size / PageSize, 0, false,
3316 unmap_properties, OperationType::Unmap, true)); 3324 unmap_properties, OperationType::Unmap, true));
3317 }); 3325 };
3318 3326
3319 // Read the memory. 3327 // Read the memory.
3320 const KProcessAddress read_addr = io_addr + (GetInteger(phys_addr) & (PageSize - 1)); 3328 const KProcessAddress read_addr = io_addr + (GetInteger(phys_addr) & (PageSize - 1));
@@ -3347,10 +3355,10 @@ Result KPageTableBase::WriteIoMemoryImpl(KPhysicalAddress phys_addr, KProcessAdd
3347 // Ensure we unmap the io memory when we're done with it. 3355 // Ensure we unmap the io memory when we're done with it.
3348 const KPageProperties unmap_properties = 3356 const KPageProperties unmap_properties =
3349 KPageProperties{KMemoryPermission::None, false, false, DisableMergeAttribute::None}; 3357 KPageProperties{KMemoryPermission::None, false, false, DisableMergeAttribute::None};
3350 SCOPE_EXIT({ 3358 SCOPE_EXIT {
3351 R_ASSERT(this->Operate(updater.GetPageList(), io_addr, map_size / PageSize, 0, false, 3359 R_ASSERT(this->Operate(updater.GetPageList(), io_addr, map_size / PageSize, 0, false,
3352 unmap_properties, OperationType::Unmap, true)); 3360 unmap_properties, OperationType::Unmap, true));
3353 }); 3361 };
3354 3362
3355 // Write the memory. 3363 // Write the memory.
3356 const KProcessAddress write_addr = io_addr + (GetInteger(phys_addr) & (PageSize - 1)); 3364 const KProcessAddress write_addr = io_addr + (GetInteger(phys_addr) & (PageSize - 1));
@@ -4491,14 +4499,14 @@ Result KPageTableBase::SetupForIpcServer(KProcessAddress* out_addr, size_t size,
4491 4499
4492 // If the partial pages are mapped, an extra reference will have been opened. Otherwise, they'll 4500 // If the partial pages are mapped, an extra reference will have been opened. Otherwise, they'll
4493 // free on scope exit. 4501 // free on scope exit.
4494 SCOPE_EXIT({ 4502 SCOPE_EXIT {
4495 if (start_partial_page != 0) { 4503 if (start_partial_page != 0) {
4496 m_kernel.MemoryManager().Close(start_partial_page, 1); 4504 m_kernel.MemoryManager().Close(start_partial_page, 1);
4497 } 4505 }
4498 if (end_partial_page != 0) { 4506 if (end_partial_page != 0) {
4499 m_kernel.MemoryManager().Close(end_partial_page, 1); 4507 m_kernel.MemoryManager().Close(end_partial_page, 1);
4500 } 4508 }
4501 }); 4509 };
4502 4510
4503 ON_RESULT_FAILURE { 4511 ON_RESULT_FAILURE {
4504 if (cur_mapped_addr != dst_addr) { 4512 if (cur_mapped_addr != dst_addr) {
@@ -5166,10 +5174,10 @@ Result KPageTableBase::MapPhysicalMemory(KProcessAddress address, size_t size) {
5166 GetCurrentProcess(m_kernel).GetId(), m_heap_fill_value)); 5174 GetCurrentProcess(m_kernel).GetId(), m_heap_fill_value));
5167 5175
5168 // If we fail in the next bit (or retry), we need to cleanup the pages. 5176 // If we fail in the next bit (or retry), we need to cleanup the pages.
5169 auto pg_guard = SCOPE_GUARD({ 5177 auto pg_guard = SCOPE_GUARD {
5170 pg.OpenFirst(); 5178 pg.OpenFirst();
5171 pg.Close(); 5179 pg.Close();
5172 }); 5180 };
5173 5181
5174 // Map the memory. 5182 // Map the memory.
5175 { 5183 {
@@ -5694,7 +5702,9 @@ Result KPageTableBase::Operate(PageLinkedList* page_list, KProcessAddress virt_a
5694 5702
5695 // Ensure that any pages we track are closed on exit. 5703 // Ensure that any pages we track are closed on exit.
5696 KPageGroup pages_to_close(m_kernel, this->GetBlockInfoManager()); 5704 KPageGroup pages_to_close(m_kernel, this->GetBlockInfoManager());
5697 SCOPE_EXIT({ pages_to_close.CloseAndReset(); }); 5705 SCOPE_EXIT {
5706 pages_to_close.CloseAndReset();
5707 };
5698 5708
5699 // Make a page group representing the region to unmap. 5709 // Make a page group representing the region to unmap.
5700 this->MakePageGroup(pages_to_close, virt_addr, num_pages); 5710 this->MakePageGroup(pages_to_close, virt_addr, num_pages);
diff --git a/src/core/hle/kernel/k_process.cpp b/src/core/hle/kernel/k_process.cpp
index 1bcc42890..cb9a11a63 100644
--- a/src/core/hle/kernel/k_process.cpp
+++ b/src/core/hle/kernel/k_process.cpp
@@ -77,7 +77,9 @@ Result TerminateChildren(KernelCore& kernel, KProcess* process,
77 } 77 }
78 78
79 // Terminate and close the thread. 79 // Terminate and close the thread.
80 SCOPE_EXIT({ cur_child->Close(); }); 80 SCOPE_EXIT {
81 cur_child->Close();
82 };
81 83
82 if (const Result terminate_result = cur_child->Terminate(); 84 if (const Result terminate_result = cur_child->Terminate();
83 ResultTerminationRequested == terminate_result) { 85 ResultTerminationRequested == terminate_result) {
@@ -466,11 +468,11 @@ void KProcess::DoWorkerTaskImpl() {
466 468
467Result KProcess::StartTermination() { 469Result KProcess::StartTermination() {
468 // Finalize the handle table when we're done, if the process isn't immortal. 470 // Finalize the handle table when we're done, if the process isn't immortal.
469 SCOPE_EXIT({ 471 SCOPE_EXIT {
470 if (!m_is_immortal) { 472 if (!m_is_immortal) {
471 this->FinalizeHandleTable(); 473 this->FinalizeHandleTable();
472 } 474 }
473 }); 475 };
474 476
475 // Terminate child threads other than the current one. 477 // Terminate child threads other than the current one.
476 R_RETURN(TerminateChildren(m_kernel, this, GetCurrentThreadPointer(m_kernel))); 478 R_RETURN(TerminateChildren(m_kernel, this, GetCurrentThreadPointer(m_kernel)));
@@ -964,7 +966,9 @@ Result KProcess::Run(s32 priority, size_t stack_size) {
964 // Create a new thread for the process. 966 // Create a new thread for the process.
965 KThread* main_thread = KThread::Create(m_kernel); 967 KThread* main_thread = KThread::Create(m_kernel);
966 R_UNLESS(main_thread != nullptr, ResultOutOfResource); 968 R_UNLESS(main_thread != nullptr, ResultOutOfResource);
967 SCOPE_EXIT({ main_thread->Close(); }); 969 SCOPE_EXIT {
970 main_thread->Close();
971 };
968 972
969 // Initialize the thread. 973 // Initialize the thread.
970 R_TRY(KThread::InitializeUserThread(m_kernel.System(), main_thread, this->GetEntryPoint(), 0, 974 R_TRY(KThread::InitializeUserThread(m_kernel.System(), main_thread, this->GetEntryPoint(), 0,
@@ -1155,7 +1159,9 @@ Result KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std:
1155 Kernel::CreateResourceLimitForProcess(m_kernel.System(), physical_memory_size); 1159 Kernel::CreateResourceLimitForProcess(m_kernel.System(), physical_memory_size);
1156 1160
1157 // Ensure we maintain a clean state on exit. 1161 // Ensure we maintain a clean state on exit.
1158 SCOPE_EXIT({ res_limit->Close(); }); 1162 SCOPE_EXIT {
1163 res_limit->Close();
1164 };
1159 1165
1160 // Declare flags and code address. 1166 // Declare flags and code address.
1161 Svc::CreateProcessFlag flag{}; 1167 Svc::CreateProcessFlag flag{};
diff --git a/src/core/hle/kernel/k_server_session.cpp b/src/core/hle/kernel/k_server_session.cpp
index adaabdd6d..40c3323ef 100644
--- a/src/core/hle/kernel/k_server_session.cpp
+++ b/src/core/hle/kernel/k_server_session.cpp
@@ -651,11 +651,11 @@ Result ReceiveMessage(KernelCore& kernel, bool& recv_list_broken, uint64_t dst_m
651 // Process any special data. 651 // Process any special data.
652 if (src_header.GetHasSpecialHeader()) { 652 if (src_header.GetHasSpecialHeader()) {
653 // After we process, make sure we track whether the receive list is broken. 653 // After we process, make sure we track whether the receive list is broken.
654 SCOPE_EXIT({ 654 SCOPE_EXIT {
655 if (offset > dst_recv_list_idx) { 655 if (offset > dst_recv_list_idx) {
656 recv_list_broken = true; 656 recv_list_broken = true;
657 } 657 }
658 }); 658 };
659 659
660 // Process special data. 660 // Process special data.
661 R_TRY(ProcessMessageSpecialData<false>(offset, dst_process, src_process, src_thread, 661 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
665 // Process any pointer buffers. 665 // Process any pointer buffers.
666 for (auto i = 0; i < src_header.GetPointerCount(); ++i) { 666 for (auto i = 0; i < src_header.GetPointerCount(); ++i) {
667 // After we process, make sure we track whether the receive list is broken. 667 // After we process, make sure we track whether the receive list is broken.
668 SCOPE_EXIT({ 668 SCOPE_EXIT {
669 if (offset > dst_recv_list_idx) { 669 if (offset > dst_recv_list_idx) {
670 recv_list_broken = true; 670 recv_list_broken = true;
671 } 671 }
672 }); 672 };
673 673
674 R_TRY(ProcessReceiveMessagePointerDescriptors( 674 R_TRY(ProcessReceiveMessagePointerDescriptors(
675 offset, pointer_key, dst_page_table, src_page_table, dst_msg, src_msg, dst_recv_list, 675 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
680 // Process any map alias buffers. 680 // Process any map alias buffers.
681 for (auto i = 0; i < src_header.GetMapAliasCount(); ++i) { 681 for (auto i = 0; i < src_header.GetMapAliasCount(); ++i) {
682 // After we process, make sure we track whether the receive list is broken. 682 // After we process, make sure we track whether the receive list is broken.
683 SCOPE_EXIT({ 683 SCOPE_EXIT {
684 if (offset > dst_recv_list_idx) { 684 if (offset > dst_recv_list_idx) {
685 recv_list_broken = true; 685 recv_list_broken = true;
686 } 686 }
687 }); 687 };
688 688
689 // We process in order send, recv, exch. Buffers after send (recv/exch) are ReadWrite. 689 // We process in order send, recv, exch. Buffers after send (recv/exch) are ReadWrite.
690 const KMemoryPermission perm = (i >= src_header.GetSendCount()) 690 const KMemoryPermission perm = (i >= src_header.GetSendCount())
@@ -702,11 +702,11 @@ Result ReceiveMessage(KernelCore& kernel, bool& recv_list_broken, uint64_t dst_m
702 // Process any raw data. 702 // Process any raw data.
703 if (const auto raw_count = src_header.GetRawCount(); raw_count != 0) { 703 if (const auto raw_count = src_header.GetRawCount(); raw_count != 0) {
704 // After we process, make sure we track whether the receive list is broken. 704 // After we process, make sure we track whether the receive list is broken.
705 SCOPE_EXIT({ 705 SCOPE_EXIT {
706 if (offset + raw_count > dst_recv_list_idx) { 706 if (offset + raw_count > dst_recv_list_idx) {
707 recv_list_broken = true; 707 recv_list_broken = true;
708 } 708 }
709 }); 709 };
710 710
711 // Get the offset and size. 711 // Get the offset and size.
712 const size_t offset_words = offset * sizeof(u32); 712 const size_t offset_words = offset * sizeof(u32);
@@ -1124,7 +1124,9 @@ Result KServerSession::ReceiveRequest(uintptr_t server_message, uintptr_t server
1124 client_thread->Open(); 1124 client_thread->Open();
1125 } 1125 }
1126 1126
1127 SCOPE_EXIT({ client_thread->Close(); }); 1127 SCOPE_EXIT {
1128 client_thread->Close();
1129 };
1128 1130
1129 // Set the request as our current. 1131 // Set the request as our current.
1130 m_current_request = request; 1132 m_current_request = request;
@@ -1174,7 +1176,9 @@ Result KServerSession::ReceiveRequest(uintptr_t server_message, uintptr_t server
1174 // Reply to the client. 1176 // Reply to the client.
1175 { 1177 {
1176 // After we reply, close our reference to the request. 1178 // After we reply, close our reference to the request.
1177 SCOPE_EXIT({ request->Close(); }); 1179 SCOPE_EXIT {
1180 request->Close();
1181 };
1178 1182
1179 // Get the event to check whether the request is async. 1183 // Get the event to check whether the request is async.
1180 if (KEvent* event = request->GetEvent(); event != nullptr) { 1184 if (KEvent* event = request->GetEvent(); event != nullptr) {
@@ -1236,7 +1240,9 @@ Result KServerSession::SendReply(uintptr_t server_message, uintptr_t server_buff
1236 } 1240 }
1237 1241
1238 // Close reference to the request once we're done processing it. 1242 // Close reference to the request once we're done processing it.
1239 SCOPE_EXIT({ request->Close(); }); 1243 SCOPE_EXIT {
1244 request->Close();
1245 };
1240 1246
1241 // Extract relevant information from the request. 1247 // Extract relevant information from the request.
1242 const uint64_t client_message = request->GetAddress(); 1248 const uint64_t client_message = request->GetAddress();
@@ -1394,7 +1400,9 @@ void KServerSession::CleanupRequests() {
1394 } 1400 }
1395 1401
1396 // Close a reference to the request once it's cleaned up. 1402 // Close a reference to the request once it's cleaned up.
1397 SCOPE_EXIT({ request->Close(); }); 1403 SCOPE_EXIT {
1404 request->Close();
1405 };
1398 1406
1399 // Extract relevant information from the request. 1407 // Extract relevant information from the request.
1400 const uint64_t client_message = request->GetAddress(); 1408 const uint64_t client_message = request->GetAddress();
@@ -1491,7 +1499,9 @@ void KServerSession::OnClientClosed() {
1491 ASSERT(thread != nullptr); 1499 ASSERT(thread != nullptr);
1492 1500
1493 // Ensure that we close the request when done. 1501 // Ensure that we close the request when done.
1494 SCOPE_EXIT({ request->Close(); }); 1502 SCOPE_EXIT {
1503 request->Close();
1504 };
1495 1505
1496 // If we're terminating, close a reference to the thread and event. 1506 // If we're terminating, close a reference to the thread and event.
1497 if (terminate) { 1507 if (terminate) {
diff --git a/src/core/hle/kernel/k_thread.h b/src/core/hle/kernel/k_thread.h
index f13e232b2..e928cfebc 100644
--- a/src/core/hle/kernel/k_thread.h
+++ b/src/core/hle/kernel/k_thread.h
@@ -66,6 +66,7 @@ enum class SuspendType : u32 {
66 Debug = 2, 66 Debug = 2,
67 Backtrace = 3, 67 Backtrace = 3,
68 Init = 4, 68 Init = 4,
69 System = 5,
69 70
70 Count, 71 Count,
71}; 72};
@@ -84,8 +85,9 @@ enum class ThreadState : u16 {
84 DebugSuspended = (1 << (2 + SuspendShift)), 85 DebugSuspended = (1 << (2 + SuspendShift)),
85 BacktraceSuspended = (1 << (3 + SuspendShift)), 86 BacktraceSuspended = (1 << (3 + SuspendShift)),
86 InitSuspended = (1 << (4 + SuspendShift)), 87 InitSuspended = (1 << (4 + SuspendShift)),
88 SystemSuspended = (1 << (5 + SuspendShift)),
87 89
88 SuspendFlagMask = ((1 << 5) - 1) << SuspendShift, 90 SuspendFlagMask = ((1 << 6) - 1) << SuspendShift,
89}; 91};
90DECLARE_ENUM_FLAG_OPERATORS(ThreadState); 92DECLARE_ENUM_FLAG_OPERATORS(ThreadState);
91 93
diff --git a/src/core/hle/kernel/k_thread_local_page.cpp b/src/core/hle/kernel/k_thread_local_page.cpp
index a632d1634..1952c0083 100644
--- a/src/core/hle/kernel/k_thread_local_page.cpp
+++ b/src/core/hle/kernel/k_thread_local_page.cpp
@@ -21,7 +21,9 @@ Result KThreadLocalPage::Initialize(KernelCore& kernel, KProcess* process) {
21 // Allocate a new page. 21 // Allocate a new page.
22 KPageBuffer* page_buf = KPageBuffer::Allocate(kernel); 22 KPageBuffer* page_buf = KPageBuffer::Allocate(kernel);
23 R_UNLESS(page_buf != nullptr, ResultOutOfMemory); 23 R_UNLESS(page_buf != nullptr, ResultOutOfMemory);
24 auto page_buf_guard = SCOPE_GUARD({ KPageBuffer::Free(kernel, page_buf); }); 24 auto page_buf_guard = SCOPE_GUARD {
25 KPageBuffer::Free(kernel, page_buf);
26 };
25 27
26 // Map the address in. 28 // Map the address in.
27 const auto phys_addr = kernel.System().DeviceMemory().GetPhysicalAddr(page_buf); 29 const auto phys_addr = kernel.System().DeviceMemory().GetPhysicalAddr(page_buf);
diff --git a/src/core/hle/kernel/k_transfer_memory.cpp b/src/core/hle/kernel/k_transfer_memory.cpp
index cbb1b02bb..09295e8ad 100644
--- a/src/core/hle/kernel/k_transfer_memory.cpp
+++ b/src/core/hle/kernel/k_transfer_memory.cpp
@@ -24,7 +24,9 @@ Result KTransferMemory::Initialize(KProcessAddress addr, std::size_t size,
24 24
25 // Construct the page group, guarding to make sure our state is valid on exit. 25 // Construct the page group, guarding to make sure our state is valid on exit.
26 m_page_group.emplace(m_kernel, page_table.GetBlockInfoManager()); 26 m_page_group.emplace(m_kernel, page_table.GetBlockInfoManager());
27 auto pg_guard = SCOPE_GUARD({ m_page_group.reset(); }); 27 auto pg_guard = SCOPE_GUARD {
28 m_page_group.reset();
29 };
28 30
29 // Lock the memory. 31 // Lock the memory.
30 R_TRY(page_table.LockForTransferMemory(std::addressof(*m_page_group), addr, size, 32 R_TRY(page_table.LockForTransferMemory(std::addressof(*m_page_group), addr, size,
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index 34b25be66..9e5eaeec4 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -109,7 +109,9 @@ struct KernelCore::Impl {
109 109
110 void Shutdown() { 110 void Shutdown() {
111 is_shutting_down.store(true, std::memory_order_relaxed); 111 is_shutting_down.store(true, std::memory_order_relaxed);
112 SCOPE_EXIT({ is_shutting_down.store(false, std::memory_order_relaxed); }); 112 SCOPE_EXIT {
113 is_shutting_down.store(false, std::memory_order_relaxed);
114 };
113 115
114 CloseServices(); 116 CloseServices();
115 117
@@ -1080,7 +1082,9 @@ std::jthread KernelCore::RunOnHostCoreProcess(std::string&& process_name,
1080 process->Initialize(Svc::CreateProcessParameter{}, GetSystemResourceLimit(), false))); 1082 process->Initialize(Svc::CreateProcessParameter{}, GetSystemResourceLimit(), false)));
1081 1083
1082 // Ensure that we don't hold onto any extra references. 1084 // Ensure that we don't hold onto any extra references.
1083 SCOPE_EXIT({ process->Close(); }); 1085 SCOPE_EXIT {
1086 process->Close();
1087 };
1084 1088
1085 // Register the new process. 1089 // Register the new process.
1086 KProcess::Register(*this, process); 1090 KProcess::Register(*this, process);
@@ -1108,7 +1112,9 @@ void KernelCore::RunOnGuestCoreProcess(std::string&& process_name, std::function
1108 process->Initialize(Svc::CreateProcessParameter{}, GetSystemResourceLimit(), false))); 1112 process->Initialize(Svc::CreateProcessParameter{}, GetSystemResourceLimit(), false)));
1109 1113
1110 // Ensure that we don't hold onto any extra references. 1114 // Ensure that we don't hold onto any extra references.
1111 SCOPE_EXIT({ process->Close(); }); 1115 SCOPE_EXIT {
1116 process->Close();
1117 };
1112 1118
1113 // Register the new process. 1119 // Register the new process.
1114 KProcess::Register(*this, process); 1120 KProcess::Register(*this, process);
@@ -1204,39 +1210,48 @@ const Kernel::KSharedMemory& KernelCore::GetHidBusSharedMem() const {
1204 return *impl->hidbus_shared_mem; 1210 return *impl->hidbus_shared_mem;
1205} 1211}
1206 1212
1207void KernelCore::SuspendApplication(bool suspended) { 1213void KernelCore::SuspendEmulation(bool suspended) {
1208 const bool should_suspend{exception_exited || suspended}; 1214 const bool should_suspend{exception_exited || suspended};
1209 const auto activity = 1215 auto processes = GetProcessList();
1210 should_suspend ? Svc::ProcessActivity::Paused : Svc::ProcessActivity::Runnable;
1211 1216
1212 // Get the application process. 1217 for (auto& process : processes) {
1213 KScopedAutoObject<KProcess> process = ApplicationProcess(); 1218 KScopedLightLock ll{process->GetListLock()};
1214 if (process.IsNull()) { 1219
1215 return; 1220 for (auto& thread : process->GetThreadList()) {
1221 if (should_suspend) {
1222 thread.RequestSuspend(SuspendType::System);
1223 } else {
1224 thread.Resume(SuspendType::System);
1225 }
1226 }
1216 } 1227 }
1217 1228
1218 // Set the new activity. 1229 if (!should_suspend) {
1219 process->SetActivity(activity); 1230 return;
1231 }
1220 1232
1221 // Wait for process execution to stop. 1233 // Wait for process execution to stop.
1222 bool must_wait{should_suspend}; 1234 // KernelCore::SuspendEmulation must be called from locked context,
1223 1235 // or we could race another call, interfering with waiting.
1224 // KernelCore::SuspendApplication must be called from locked context, 1236 const auto TryWait = [&]() {
1225 // or we could race another call to SetActivity, interfering with waiting.
1226 while (must_wait) {
1227 KScopedSchedulerLock sl{*this}; 1237 KScopedSchedulerLock sl{*this};
1228 1238
1229 // Assume that all threads have finished running. 1239 for (auto& process : processes) {
1230 must_wait = false; 1240 for (auto i = 0; i < static_cast<s32>(Core::Hardware::NUM_CPU_CORES); ++i) {
1231 1241 if (Scheduler(i).GetSchedulerCurrentThread()->GetOwnerProcess() ==
1232 for (auto i = 0; i < static_cast<s32>(Core::Hardware::NUM_CPU_CORES); ++i) { 1242 process.GetPointerUnsafe()) {
1233 if (Scheduler(i).GetSchedulerCurrentThread()->GetOwnerProcess() == 1243 // A thread has not finished running yet.
1234 process.GetPointerUnsafe()) { 1244 // Continue waiting.
1235 // A thread has not finished running yet. 1245 return false;
1236 // Continue waiting. 1246 }
1237 must_wait = true;
1238 } 1247 }
1239 } 1248 }
1249
1250 return true;
1251 };
1252
1253 while (!TryWait()) {
1254 // ...
1240 } 1255 }
1241} 1256}
1242 1257
@@ -1260,7 +1275,7 @@ bool KernelCore::IsShuttingDown() const {
1260 1275
1261void KernelCore::ExceptionalExitApplication() { 1276void KernelCore::ExceptionalExitApplication() {
1262 exception_exited = true; 1277 exception_exited = true;
1263 SuspendApplication(true); 1278 SuspendEmulation(true);
1264} 1279}
1265 1280
1266void KernelCore::EnterSVCProfile() { 1281void KernelCore::EnterSVCProfile() {
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h
index 8ea5bed1c..57182c0c8 100644
--- a/src/core/hle/kernel/kernel.h
+++ b/src/core/hle/kernel/kernel.h
@@ -258,8 +258,8 @@ public:
258 /// Gets the shared memory object for HIDBus services. 258 /// Gets the shared memory object for HIDBus services.
259 const Kernel::KSharedMemory& GetHidBusSharedMem() const; 259 const Kernel::KSharedMemory& GetHidBusSharedMem() const;
260 260
261 /// Suspend/unsuspend application process. 261 /// Suspend/unsuspend emulated processes.
262 void SuspendApplication(bool suspend); 262 void SuspendEmulation(bool suspend);
263 263
264 /// Exceptional exit application process. 264 /// Exceptional exit application process.
265 void ExceptionalExitApplication(); 265 void ExceptionalExitApplication();
diff --git a/src/core/hle/kernel/svc/svc_code_memory.cpp b/src/core/hle/kernel/svc/svc_code_memory.cpp
index bae4cb0cd..7be2802f0 100644
--- a/src/core/hle/kernel/svc/svc_code_memory.cpp
+++ b/src/core/hle/kernel/svc/svc_code_memory.cpp
@@ -45,7 +45,9 @@ Result CreateCodeMemory(Core::System& system, Handle* out, u64 address, uint64_t
45 45
46 KCodeMemory* code_mem = KCodeMemory::Create(kernel); 46 KCodeMemory* code_mem = KCodeMemory::Create(kernel);
47 R_UNLESS(code_mem != nullptr, ResultOutOfResource); 47 R_UNLESS(code_mem != nullptr, ResultOutOfResource);
48 SCOPE_EXIT({ code_mem->Close(); }); 48 SCOPE_EXIT {
49 code_mem->Close();
50 };
49 51
50 // Verify that the region is in range. 52 // Verify that the region is in range.
51 R_UNLESS(GetCurrentProcess(system.Kernel()).GetPageTable().Contains(address, size), 53 R_UNLESS(GetCurrentProcess(system.Kernel()).GetPageTable().Contains(address, size),
diff --git a/src/core/hle/kernel/svc/svc_device_address_space.cpp b/src/core/hle/kernel/svc/svc_device_address_space.cpp
index 42add9473..ac828320f 100644
--- a/src/core/hle/kernel/svc/svc_device_address_space.cpp
+++ b/src/core/hle/kernel/svc/svc_device_address_space.cpp
@@ -28,7 +28,9 @@ Result CreateDeviceAddressSpace(Core::System& system, Handle* out, uint64_t das_
28 // Create the device address space. 28 // Create the device address space.
29 KDeviceAddressSpace* das = KDeviceAddressSpace::Create(system.Kernel()); 29 KDeviceAddressSpace* das = KDeviceAddressSpace::Create(system.Kernel());
30 R_UNLESS(das != nullptr, ResultOutOfResource); 30 R_UNLESS(das != nullptr, ResultOutOfResource);
31 SCOPE_EXIT({ das->Close(); }); 31 SCOPE_EXIT {
32 das->Close();
33 };
32 34
33 // Initialize the device address space. 35 // Initialize the device address space.
34 R_TRY(das->Initialize(das_address, das_size)); 36 R_TRY(das->Initialize(das_address, das_size));
diff --git a/src/core/hle/kernel/svc/svc_event.cpp b/src/core/hle/kernel/svc/svc_event.cpp
index 901202e6a..8e4beb396 100644
--- a/src/core/hle/kernel/svc/svc_event.cpp
+++ b/src/core/hle/kernel/svc/svc_event.cpp
@@ -72,10 +72,10 @@ Result CreateEvent(Core::System& system, Handle* out_write, Handle* out_read) {
72 event_reservation.Commit(); 72 event_reservation.Commit();
73 73
74 // Ensure that we clean up the event (and its only references are handle table) on function end. 74 // Ensure that we clean up the event (and its only references are handle table) on function end.
75 SCOPE_EXIT({ 75 SCOPE_EXIT {
76 event->GetReadableEvent().Close(); 76 event->GetReadableEvent().Close();
77 event->Close(); 77 event->Close();
78 }); 78 };
79 79
80 // Register the event. 80 // Register the event.
81 KEvent::Register(kernel, event); 81 KEvent::Register(kernel, event);
diff --git a/src/core/hle/kernel/svc/svc_ipc.cpp b/src/core/hle/kernel/svc/svc_ipc.cpp
index 85cc4f561..b619bd70a 100644
--- a/src/core/hle/kernel/svc/svc_ipc.cpp
+++ b/src/core/hle/kernel/svc/svc_ipc.cpp
@@ -129,11 +129,11 @@ Result ReplyAndReceiveImpl(KernelCore& kernel, int32_t* out_index, uintptr_t mes
129 } 129 }
130 130
131 // Ensure handles are closed when we're done. 131 // Ensure handles are closed when we're done.
132 SCOPE_EXIT({ 132 SCOPE_EXIT {
133 for (auto i = 0; i < num_handles; ++i) { 133 for (auto i = 0; i < num_handles; ++i) {
134 objs[i]->Close(); 134 objs[i]->Close();
135 } 135 }
136 }); 136 };
137 137
138 R_RETURN(ReplyAndReceiveImpl(kernel, out_index, message, buffer_size, message_paddr, objs, 138 R_RETURN(ReplyAndReceiveImpl(kernel, out_index, message, buffer_size, message_paddr, objs,
139 num_handles, reply_target, timeout_ns)); 139 num_handles, reply_target, timeout_ns));
@@ -208,10 +208,10 @@ Result SendAsyncRequestWithUserBuffer(Core::System& system, Handle* out_event_ha
208 event_reservation.Commit(); 208 event_reservation.Commit();
209 209
210 // At end of scope, kill the standing references to the sub events. 210 // At end of scope, kill the standing references to the sub events.
211 SCOPE_EXIT({ 211 SCOPE_EXIT {
212 event->GetReadableEvent().Close(); 212 event->GetReadableEvent().Close();
213 event->Close(); 213 event->Close();
214 }); 214 };
215 215
216 // Register the event. 216 // Register the event.
217 KEvent::Register(system.Kernel(), event); 217 KEvent::Register(system.Kernel(), event);
diff --git a/src/core/hle/kernel/svc/svc_port.cpp b/src/core/hle/kernel/svc/svc_port.cpp
index 737749f7d..9a22dadaf 100644
--- a/src/core/hle/kernel/svc/svc_port.cpp
+++ b/src/core/hle/kernel/svc/svc_port.cpp
@@ -68,10 +68,10 @@ Result CreatePort(Core::System& system, Handle* out_server, Handle* out_client,
68 port->Initialize(max_sessions, is_light, name); 68 port->Initialize(max_sessions, is_light, name);
69 69
70 // Ensure that we clean up the port (and its only references are handle table) on function end. 70 // Ensure that we clean up the port (and its only references are handle table) on function end.
71 SCOPE_EXIT({ 71 SCOPE_EXIT {
72 port->GetServerPort().Close(); 72 port->GetServerPort().Close();
73 port->GetClientPort().Close(); 73 port->GetClientPort().Close();
74 }); 74 };
75 75
76 // Register the port. 76 // Register the port.
77 KPort::Register(kernel, port); 77 KPort::Register(kernel, port);
@@ -150,10 +150,10 @@ Result ManageNamedPort(Core::System& system, Handle* out_server_handle, uint64_t
150 KPort::Register(system.Kernel(), port); 150 KPort::Register(system.Kernel(), port);
151 151
152 // Ensure that our only reference to the port is in the handle table when we're done. 152 // Ensure that our only reference to the port is in the handle table when we're done.
153 SCOPE_EXIT({ 153 SCOPE_EXIT {
154 port->GetClientPort().Close(); 154 port->GetClientPort().Close();
155 port->GetServerPort().Close(); 155 port->GetServerPort().Close();
156 }); 156 };
157 157
158 // Register the handle in the table. 158 // Register the handle in the table.
159 R_TRY(handle_table.Add(out_server_handle, std::addressof(port->GetServerPort()))); 159 R_TRY(handle_table.Add(out_server_handle, std::addressof(port->GetServerPort())));
diff --git a/src/core/hle/kernel/svc/svc_resource_limit.cpp b/src/core/hle/kernel/svc/svc_resource_limit.cpp
index c8e820b6a..6f3972482 100644
--- a/src/core/hle/kernel/svc/svc_resource_limit.cpp
+++ b/src/core/hle/kernel/svc/svc_resource_limit.cpp
@@ -18,7 +18,9 @@ Result CreateResourceLimit(Core::System& system, Handle* out_handle) {
18 R_UNLESS(resource_limit != nullptr, ResultOutOfResource); 18 R_UNLESS(resource_limit != nullptr, ResultOutOfResource);
19 19
20 // Ensure we don't leak a reference to the limit. 20 // Ensure we don't leak a reference to the limit.
21 SCOPE_EXIT({ resource_limit->Close(); }); 21 SCOPE_EXIT {
22 resource_limit->Close();
23 };
22 24
23 // Initialize the resource limit. 25 // Initialize the resource limit.
24 resource_limit->Initialize(); 26 resource_limit->Initialize();
diff --git a/src/core/hle/kernel/svc/svc_session.cpp b/src/core/hle/kernel/svc/svc_session.cpp
index 2f5905f32..b034d21d1 100644
--- a/src/core/hle/kernel/svc/svc_session.cpp
+++ b/src/core/hle/kernel/svc/svc_session.cpp
@@ -69,10 +69,10 @@ Result CreateSession(Core::System& system, Handle* out_server, Handle* out_clien
69 69
70 // Ensure that we clean up the session (and its only references are handle table) on function 70 // Ensure that we clean up the session (and its only references are handle table) on function
71 // end. 71 // end.
72 SCOPE_EXIT({ 72 SCOPE_EXIT {
73 session->GetClientSession().Close(); 73 session->GetClientSession().Close();
74 session->GetServerSession().Close(); 74 session->GetServerSession().Close();
75 }); 75 };
76 76
77 // Register the session. 77 // Register the session.
78 T::Register(system.Kernel(), session); 78 T::Register(system.Kernel(), session);
diff --git a/src/core/hle/kernel/svc/svc_synchronization.cpp b/src/core/hle/kernel/svc/svc_synchronization.cpp
index 6c79cfd8d..fb03908d7 100644
--- a/src/core/hle/kernel/svc/svc_synchronization.cpp
+++ b/src/core/hle/kernel/svc/svc_synchronization.cpp
@@ -78,11 +78,11 @@ Result WaitSynchronization(Core::System& system, int32_t* out_index, u64 user_ha
78 } 78 }
79 79
80 // Ensure handles are closed when we're done. 80 // Ensure handles are closed when we're done.
81 SCOPE_EXIT({ 81 SCOPE_EXIT {
82 for (auto i = 0; i < num_handles; ++i) { 82 for (auto i = 0; i < num_handles; ++i) {
83 objs[i]->Close(); 83 objs[i]->Close();
84 } 84 }
85 }); 85 };
86 86
87 // Convert the timeout from nanoseconds to ticks. 87 // Convert the timeout from nanoseconds to ticks.
88 s64 timeout; 88 s64 timeout;
diff --git a/src/core/hle/kernel/svc/svc_thread.cpp b/src/core/hle/kernel/svc/svc_thread.cpp
index 7681afa33..7517bb9d3 100644
--- a/src/core/hle/kernel/svc/svc_thread.cpp
+++ b/src/core/hle/kernel/svc/svc_thread.cpp
@@ -51,7 +51,9 @@ Result CreateThread(Core::System& system, Handle* out_handle, u64 entry_point, u
51 // Create the thread. 51 // Create the thread.
52 KThread* thread = KThread::Create(kernel); 52 KThread* thread = KThread::Create(kernel);
53 R_UNLESS(thread != nullptr, ResultOutOfResource) 53 R_UNLESS(thread != nullptr, ResultOutOfResource)
54 SCOPE_EXIT({ thread->Close(); }); 54 SCOPE_EXIT {
55 thread->Close();
56 };
55 57
56 // Initialize the thread. 58 // Initialize the thread.
57 { 59 {
diff --git a/src/core/hle/kernel/svc/svc_transfer_memory.cpp b/src/core/hle/kernel/svc/svc_transfer_memory.cpp
index 671bca23f..2ea0d4421 100644
--- a/src/core/hle/kernel/svc/svc_transfer_memory.cpp
+++ b/src/core/hle/kernel/svc/svc_transfer_memory.cpp
@@ -52,7 +52,9 @@ Result CreateTransferMemory(Core::System& system, Handle* out, u64 address, u64
52 R_UNLESS(trmem != nullptr, ResultOutOfResource); 52 R_UNLESS(trmem != nullptr, ResultOutOfResource);
53 53
54 // Ensure the only reference is in the handle table when we're done. 54 // Ensure the only reference is in the handle table when we're done.
55 SCOPE_EXIT({ trmem->Close(); }); 55 SCOPE_EXIT {
56 trmem->Close();
57 };
56 58
57 // Ensure that the region is in range. 59 // Ensure that the region is in range.
58 R_UNLESS(process.GetPageTable().Contains(address, size), ResultInvalidCurrentMemory); 60 R_UNLESS(process.GetPageTable().Contains(address, size), ResultInvalidCurrentMemory);
diff --git a/src/core/hle/service/acc/profile_manager.cpp b/src/core/hle/service/acc/profile_manager.cpp
index 29a10ad13..ee9795532 100644
--- a/src/core/hle/service/acc/profile_manager.cpp
+++ b/src/core/hle/service/acc/profile_manager.cpp
@@ -329,9 +329,8 @@ bool ProfileManager::GetProfileBaseAndData(const ProfileInfo& user, ProfileBase&
329 329
330/// Returns if the system is allowing user registrations or not 330/// Returns if the system is allowing user registrations or not
331bool ProfileManager::CanSystemRegisterUser() const { 331bool ProfileManager::CanSystemRegisterUser() const {
332 return false; // TODO(ogniK): Games shouldn't have 332 // TODO: Both games and applets can register users. Determine when this condition is not meet.
333 // access to user registration, when we 333 return true;
334 // emulate qlaunch. Update this to dynamically change.
335} 334}
336 335
337bool ProfileManager::RemoveUser(UUID uuid) { 336bool ProfileManager::RemoveUser(UUID uuid) {
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp
index 9dc710ba9..8c4e14f08 100644
--- a/src/core/hle/service/am/am.cpp
+++ b/src/core/hle/service/am/am.cpp
@@ -8,13 +8,13 @@
8 8
9namespace Service::AM { 9namespace Service::AM {
10 10
11void LoopProcess(Nvnflinger::Nvnflinger& nvnflinger, Core::System& system) { 11void LoopProcess(Core::System& system) {
12 auto server_manager = std::make_unique<ServerManager>(system); 12 auto server_manager = std::make_unique<ServerManager>(system);
13 13
14 server_manager->RegisterNamedService( 14 server_manager->RegisterNamedService("appletAE",
15 "appletAE", std::make_shared<IAllSystemAppletProxiesService>(system, nvnflinger)); 15 std::make_shared<IAllSystemAppletProxiesService>(system));
16 server_manager->RegisterNamedService( 16 server_manager->RegisterNamedService("appletOE",
17 "appletOE", std::make_shared<IApplicationProxyService>(system, nvnflinger)); 17 std::make_shared<IApplicationProxyService>(system));
18 ServerManager::RunServer(std::move(server_manager)); 18 ServerManager::RunServer(std::move(server_manager));
19} 19}
20 20
diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h
index 4a2d797bd..1afe253ae 100644
--- a/src/core/hle/service/am/am.h
+++ b/src/core/hle/service/am/am.h
@@ -7,12 +7,8 @@ namespace Core {
7class System; 7class System;
8} 8}
9 9
10namespace Service::Nvnflinger {
11class Nvnflinger;
12}
13
14namespace Service::AM { 10namespace Service::AM {
15 11
16void LoopProcess(Nvnflinger::Nvnflinger& nvnflinger, Core::System& system); 12void LoopProcess(Core::System& system);
17 13
18} // namespace Service::AM 14} // namespace Service::AM
diff --git a/src/core/hle/service/am/applet.h b/src/core/hle/service/am/applet.h
index 4f34d4811..ad602153e 100644
--- a/src/core/hle/service/am/applet.h
+++ b/src/core/hle/service/am/applet.h
@@ -14,10 +14,9 @@
14 14
15#include "core/hle/service/am/am_types.h" 15#include "core/hle/service/am/am_types.h"
16#include "core/hle/service/am/applet_message_queue.h" 16#include "core/hle/service/am/applet_message_queue.h"
17#include "core/hle/service/am/display_layer_manager.h"
17#include "core/hle/service/am/hid_registration.h" 18#include "core/hle/service/am/hid_registration.h"
18#include "core/hle/service/am/managed_layer_holder.h"
19#include "core/hle/service/am/process.h" 19#include "core/hle/service/am/process.h"
20#include "core/hle/service/am/system_buffer_manager.h"
21 20
22namespace Service::AM { 21namespace Service::AM {
23 22
@@ -54,8 +53,7 @@ struct Applet {
54 HidRegistration hid_registration; 53 HidRegistration hid_registration;
55 54
56 // vi state 55 // vi state
57 SystemBufferManager system_buffer_manager{}; 56 DisplayLayerManager display_layer_manager{};
58 ManagedLayerHolder managed_layer_holder{};
59 57
60 // Applet common functions 58 // Applet common functions
61 Result terminate_result{}; 59 Result terminate_result{};
diff --git a/src/core/hle/service/am/applet_data_broker.cpp b/src/core/hle/service/am/applet_data_broker.cpp
index 4d58c4db5..9057244a9 100644
--- a/src/core/hle/service/am/applet_data_broker.cpp
+++ b/src/core/hle/service/am/applet_data_broker.cpp
@@ -24,11 +24,11 @@ void AppletStorageChannel::Push(std::shared_ptr<IStorage> storage) {
24Result AppletStorageChannel::Pop(std::shared_ptr<IStorage>* out_storage) { 24Result AppletStorageChannel::Pop(std::shared_ptr<IStorage>* out_storage) {
25 std::scoped_lock lk{m_lock}; 25 std::scoped_lock lk{m_lock};
26 26
27 SCOPE_EXIT({ 27 SCOPE_EXIT {
28 if (m_data.empty()) { 28 if (m_data.empty()) {
29 m_event.Clear(); 29 m_event.Clear();
30 } 30 }
31 }); 31 };
32 32
33 R_UNLESS(!m_data.empty(), AM::ResultNoDataInChannel); 33 R_UNLESS(!m_data.empty(), AM::ResultNoDataInChannel);
34 34
diff --git a/src/core/hle/service/am/applet_manager.cpp b/src/core/hle/service/am/applet_manager.cpp
index 4c7266f89..2e109181d 100644
--- a/src/core/hle/service/am/applet_manager.cpp
+++ b/src/core/hle/service/am/applet_manager.cpp
@@ -35,6 +35,21 @@ AppletStorageChannel& InitializeFakeCallerApplet(Core::System& system,
35 return applet->caller_applet_broker->GetInData(); 35 return applet->caller_applet_broker->GetInData();
36} 36}
37 37
38void PushInShowQlaunch(Core::System& system, AppletStorageChannel& channel) {
39 const CommonArguments arguments{
40 .arguments_version = CommonArgumentVersion::Version3,
41 .size = CommonArgumentSize::Version3,
42 .library_version = 0,
43 .theme_color = ThemeColor::BasicBlack,
44 .play_startup_sound = true,
45 .system_tick = system.CoreTiming().GetClockTicks(),
46 };
47
48 std::vector<u8> argument_data(sizeof(arguments));
49 std::memcpy(argument_data.data(), &arguments, sizeof(arguments));
50 channel.Push(std::make_shared<IStorage>(system, std::move(argument_data)));
51}
52
38void PushInShowAlbum(Core::System& system, AppletStorageChannel& channel) { 53void PushInShowAlbum(Core::System& system, AppletStorageChannel& channel) {
39 const CommonArguments arguments{ 54 const CommonArguments arguments{
40 .arguments_version = CommonArgumentVersion::Version3, 55 .arguments_version = CommonArgumentVersion::Version3,
@@ -284,6 +299,9 @@ void AppletManager::CreateAndInsertByFrontendAppletParameters(
284 299
285 // Starting from frontend, some applets require input data. 300 // Starting from frontend, some applets require input data.
286 switch (applet->applet_id) { 301 switch (applet->applet_id) {
302 case AppletId::QLaunch:
303 PushInShowQlaunch(m_system, InitializeFakeCallerApplet(m_system, applet));
304 break;
287 case AppletId::Cabinet: 305 case AppletId::Cabinet:
288 PushInShowCabinetData(m_system, InitializeFakeCallerApplet(m_system, applet)); 306 PushInShowCabinetData(m_system, InitializeFakeCallerApplet(m_system, applet));
289 break; 307 break;
diff --git a/src/core/hle/service/am/display_layer_manager.cpp b/src/core/hle/service/am/display_layer_manager.cpp
new file mode 100644
index 000000000..85ff6fb88
--- /dev/null
+++ b/src/core/hle/service/am/display_layer_manager.cpp
@@ -0,0 +1,151 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/core.h"
5#include "core/hle/service/am/display_layer_manager.h"
6#include "core/hle/service/sm/sm.h"
7#include "core/hle/service/vi/application_display_service.h"
8#include "core/hle/service/vi/container.h"
9#include "core/hle/service/vi/manager_display_service.h"
10#include "core/hle/service/vi/manager_root_service.h"
11#include "core/hle/service/vi/shared_buffer_manager.h"
12#include "core/hle/service/vi/vi_results.h"
13#include "core/hle/service/vi/vi_types.h"
14
15namespace Service::AM {
16
17DisplayLayerManager::DisplayLayerManager() = default;
18DisplayLayerManager::~DisplayLayerManager() {
19 this->Finalize();
20}
21
22void DisplayLayerManager::Initialize(Core::System& system, Kernel::KProcess* process,
23 AppletId applet_id, LibraryAppletMode mode) {
24 R_ASSERT(system.ServiceManager()
25 .GetService<VI::IManagerRootService>("vi:m", true)
26 ->GetDisplayService(&m_display_service, VI::Policy::Compositor));
27 R_ASSERT(m_display_service->GetManagerDisplayService(&m_manager_display_service));
28
29 m_process = process;
30 m_system_shared_buffer_id = 0;
31 m_system_shared_layer_id = 0;
32 m_applet_id = applet_id;
33 m_buffer_sharing_enabled = false;
34 m_blending_enabled = mode == LibraryAppletMode::PartialForeground ||
35 mode == LibraryAppletMode::PartialForegroundIndirectDisplay;
36}
37
38void DisplayLayerManager::Finalize() {
39 if (!m_manager_display_service) {
40 return;
41 }
42
43 // Clean up managed layers.
44 for (const auto& layer : m_managed_display_layers) {
45 m_manager_display_service->DestroyManagedLayer(layer);
46 }
47
48 for (const auto& layer : m_managed_display_recording_layers) {
49 m_manager_display_service->DestroyManagedLayer(layer);
50 }
51
52 // Clean up shared layers.
53 if (m_buffer_sharing_enabled) {
54 m_manager_display_service->DestroySharedLayerSession(m_process);
55 }
56
57 m_manager_display_service = nullptr;
58 m_display_service = nullptr;
59}
60
61Result DisplayLayerManager::CreateManagedDisplayLayer(u64* out_layer_id) {
62 R_UNLESS(m_manager_display_service != nullptr, VI::ResultOperationFailed);
63
64 // TODO(Subv): Find out how AM determines the display to use, for now just
65 // create the layer in the Default display.
66 u64 display_id;
67 R_TRY(m_display_service->OpenDisplay(&display_id, VI::DisplayName{"Default"}));
68 R_TRY(m_manager_display_service->CreateManagedLayer(
69 out_layer_id, 0, display_id, Service::AppletResourceUserId{m_process->GetProcessId()}));
70
71 m_manager_display_service->SetLayerVisibility(m_visible, *out_layer_id);
72 m_managed_display_layers.emplace(*out_layer_id);
73
74 R_SUCCEED();
75}
76
77Result DisplayLayerManager::CreateManagedDisplaySeparableLayer(u64* out_layer_id,
78 u64* out_recording_layer_id) {
79 R_UNLESS(m_manager_display_service != nullptr, VI::ResultOperationFailed);
80
81 // TODO(Subv): Find out how AM determines the display to use, for now just
82 // create the layer in the Default display.
83 // This calls nn::vi::CreateRecordingLayer() which creates another layer.
84 // Currently we do not support more than 1 layer per display, output 1 layer id for now.
85 // Outputting 1 layer id instead of the expected 2 has not been observed to cause any adverse
86 // side effects.
87 *out_recording_layer_id = 0;
88 R_RETURN(this->CreateManagedDisplayLayer(out_layer_id));
89}
90
91Result DisplayLayerManager::IsSystemBufferSharingEnabled() {
92 // Succeed if already enabled.
93 R_SUCCEED_IF(m_buffer_sharing_enabled);
94
95 // Ensure we can access shared layers.
96 R_UNLESS(m_manager_display_service != nullptr, VI::ResultOperationFailed);
97 R_UNLESS(m_applet_id != AppletId::Application, VI::ResultPermissionDenied);
98
99 // Create the shared layer.
100 u64 display_id;
101 R_TRY(m_display_service->OpenDisplay(&display_id, VI::DisplayName{"Default"}));
102 R_TRY(m_manager_display_service->CreateSharedLayerSession(m_process, &m_system_shared_buffer_id,
103 &m_system_shared_layer_id, display_id,
104 m_blending_enabled));
105
106 // We succeeded, so set up remaining state.
107 m_buffer_sharing_enabled = true;
108 m_manager_display_service->SetLayerVisibility(m_visible, m_system_shared_layer_id);
109 R_SUCCEED();
110}
111
112Result DisplayLayerManager::GetSystemSharedLayerHandle(u64* out_system_shared_buffer_id,
113 u64* out_system_shared_layer_id) {
114 R_TRY(this->IsSystemBufferSharingEnabled());
115
116 *out_system_shared_buffer_id = m_system_shared_buffer_id;
117 *out_system_shared_layer_id = m_system_shared_layer_id;
118
119 R_SUCCEED();
120}
121
122void DisplayLayerManager::SetWindowVisibility(bool visible) {
123 if (m_visible == visible) {
124 return;
125 }
126
127 m_visible = visible;
128
129 if (m_manager_display_service) {
130 if (m_system_shared_layer_id) {
131 m_manager_display_service->SetLayerVisibility(m_visible, m_system_shared_layer_id);
132 }
133
134 for (const auto layer_id : m_managed_display_layers) {
135 m_manager_display_service->SetLayerVisibility(m_visible, layer_id);
136 }
137 }
138}
139
140bool DisplayLayerManager::GetWindowVisibility() const {
141 return m_visible;
142}
143
144Result DisplayLayerManager::WriteAppletCaptureBuffer(bool* out_was_written,
145 s32* out_fbshare_layer_index) {
146 R_UNLESS(m_buffer_sharing_enabled, VI::ResultPermissionDenied);
147 R_RETURN(m_display_service->GetContainer()->GetSharedBufferManager()->WriteAppletCaptureBuffer(
148 out_was_written, out_fbshare_layer_index));
149}
150
151} // namespace Service::AM
diff --git a/src/core/hle/service/am/display_layer_manager.h b/src/core/hle/service/am/display_layer_manager.h
new file mode 100644
index 000000000..a66509c04
--- /dev/null
+++ b/src/core/hle/service/am/display_layer_manager.h
@@ -0,0 +1,62 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <set>
7
8#include "common/common_types.h"
9#include "core/hle/result.h"
10#include "core/hle/service/am/am_types.h"
11
12namespace Core {
13class System;
14}
15
16namespace Kernel {
17class KProcess;
18}
19
20namespace Service::VI {
21class IApplicationDisplayService;
22class IManagerDisplayService;
23} // namespace Service::VI
24
25namespace Service::AM {
26
27class DisplayLayerManager {
28public:
29 explicit DisplayLayerManager();
30 ~DisplayLayerManager();
31
32 void Initialize(Core::System& system, Kernel::KProcess* process, AppletId applet_id,
33 LibraryAppletMode mode);
34 void Finalize();
35
36 Result CreateManagedDisplayLayer(u64* out_layer_id);
37 Result CreateManagedDisplaySeparableLayer(u64* out_layer_id, u64* out_recording_layer_id);
38
39 Result IsSystemBufferSharingEnabled();
40 Result GetSystemSharedLayerHandle(u64* out_system_shared_buffer_id,
41 u64* out_system_shared_layer_id);
42
43 void SetWindowVisibility(bool visible);
44 bool GetWindowVisibility() const;
45
46 Result WriteAppletCaptureBuffer(bool* out_was_written, s32* out_fbshare_layer_index);
47
48private:
49 Kernel::KProcess* m_process{};
50 std::shared_ptr<VI::IApplicationDisplayService> m_display_service{};
51 std::shared_ptr<VI::IManagerDisplayService> m_manager_display_service{};
52 std::set<u64> m_managed_display_layers{};
53 std::set<u64> m_managed_display_recording_layers{};
54 u64 m_system_shared_buffer_id{};
55 u64 m_system_shared_layer_id{};
56 AppletId m_applet_id{};
57 bool m_buffer_sharing_enabled{};
58 bool m_blending_enabled{};
59 bool m_visible{true};
60};
61
62} // namespace Service::AM
diff --git a/src/core/hle/service/am/frontend/applet_web_browser.cpp b/src/core/hle/service/am/frontend/applet_web_browser.cpp
index bb60260b4..835c20c4e 100644
--- a/src/core/hle/service/am/frontend/applet_web_browser.cpp
+++ b/src/core/hle/service/am/frontend/applet_web_browser.cpp
@@ -22,7 +22,7 @@
22#include "core/hle/service/am/frontend/applet_web_browser.h" 22#include "core/hle/service/am/frontend/applet_web_browser.h"
23#include "core/hle/service/am/service/storage.h" 23#include "core/hle/service/am/service/storage.h"
24#include "core/hle/service/filesystem/filesystem.h" 24#include "core/hle/service/filesystem/filesystem.h"
25#include "core/hle/service/ns/iplatform_service_manager.h" 25#include "core/hle/service/ns/platform_service_manager.h"
26#include "core/loader/loader.h" 26#include "core/loader/loader.h"
27 27
28namespace Service::AM::Frontend { 28namespace Service::AM::Frontend {
diff --git a/src/core/hle/service/am/library_applet_storage.cpp b/src/core/hle/service/am/library_applet_storage.cpp
index 46e6c0111..0412c215d 100644
--- a/src/core/hle/service/am/library_applet_storage.cpp
+++ b/src/core/hle/service/am/library_applet_storage.cpp
@@ -70,7 +70,7 @@ public:
70 Result Read(s64 offset, void* buffer, size_t size) override { 70 Result Read(s64 offset, void* buffer, size_t size) override {
71 R_TRY(ValidateOffset(offset, size, m_size)); 71 R_TRY(ValidateOffset(offset, size, m_size));
72 72
73 m_memory.ReadBlock(m_trmem->GetSourceAddress(), buffer, size); 73 m_memory.ReadBlock(m_trmem->GetSourceAddress() + offset, buffer, size);
74 74
75 R_SUCCEED(); 75 R_SUCCEED();
76 } 76 }
@@ -79,7 +79,7 @@ public:
79 R_UNLESS(m_is_writable, ResultUnknown); 79 R_UNLESS(m_is_writable, ResultUnknown);
80 R_TRY(ValidateOffset(offset, size, m_size)); 80 R_TRY(ValidateOffset(offset, size, m_size));
81 81
82 m_memory.WriteBlock(m_trmem->GetSourceAddress(), buffer, size); 82 m_memory.WriteBlock(m_trmem->GetSourceAddress() + offset, buffer, size);
83 83
84 R_SUCCEED(); 84 R_SUCCEED();
85 } 85 }
diff --git a/src/core/hle/service/am/managed_layer_holder.cpp b/src/core/hle/service/am/managed_layer_holder.cpp
deleted file mode 100644
index 61eb8641a..000000000
--- a/src/core/hle/service/am/managed_layer_holder.cpp
+++ /dev/null
@@ -1,59 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/hle/service/am/managed_layer_holder.h"
5#include "core/hle/service/nvnflinger/nvnflinger.h"
6
7namespace Service::AM {
8
9ManagedLayerHolder::ManagedLayerHolder() = default;
10ManagedLayerHolder::~ManagedLayerHolder() {
11 if (!m_nvnflinger) {
12 return;
13 }
14
15 for (const auto& layer : m_managed_display_layers) {
16 m_nvnflinger->DestroyLayer(layer);
17 }
18
19 for (const auto& layer : m_managed_display_recording_layers) {
20 m_nvnflinger->DestroyLayer(layer);
21 }
22
23 m_nvnflinger = nullptr;
24}
25
26void ManagedLayerHolder::Initialize(Nvnflinger::Nvnflinger* nvnflinger) {
27 m_nvnflinger = nvnflinger;
28}
29
30void ManagedLayerHolder::CreateManagedDisplayLayer(u64* out_layer) {
31 // TODO(Subv): Find out how AM determines the display to use, for now just
32 // create the layer in the Default display.
33 const auto display_id = m_nvnflinger->OpenDisplay("Default");
34 const auto layer_id = m_nvnflinger->CreateLayer(*display_id);
35
36 m_managed_display_layers.emplace(*layer_id);
37
38 *out_layer = *layer_id;
39}
40
41void ManagedLayerHolder::CreateManagedDisplaySeparableLayer(u64* out_layer,
42 u64* out_recording_layer) {
43 // TODO(Subv): Find out how AM determines the display to use, for now just
44 // create the layer in the Default display.
45 // This calls nn::vi::CreateRecordingLayer() which creates another layer.
46 // Currently we do not support more than 1 layer per display, output 1 layer id for now.
47 // Outputting 1 layer id instead of the expected 2 has not been observed to cause any adverse
48 // side effects.
49 // TODO: Support multiple layers
50 const auto display_id = m_nvnflinger->OpenDisplay("Default");
51 const auto layer_id = m_nvnflinger->CreateLayer(*display_id);
52
53 m_managed_display_layers.emplace(*layer_id);
54
55 *out_layer = *layer_id;
56 *out_recording_layer = 0;
57}
58
59} // namespace Service::AM
diff --git a/src/core/hle/service/am/managed_layer_holder.h b/src/core/hle/service/am/managed_layer_holder.h
deleted file mode 100644
index f7fe03f24..000000000
--- a/src/core/hle/service/am/managed_layer_holder.h
+++ /dev/null
@@ -1,32 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <set>
7
8#include "common/common_funcs.h"
9#include "common/common_types.h"
10
11namespace Service::Nvnflinger {
12class Nvnflinger;
13}
14
15namespace Service::AM {
16
17class ManagedLayerHolder {
18public:
19 ManagedLayerHolder();
20 ~ManagedLayerHolder();
21
22 void Initialize(Nvnflinger::Nvnflinger* nvnflinger);
23 void CreateManagedDisplayLayer(u64* out_layer);
24 void CreateManagedDisplaySeparableLayer(u64* out_layer, u64* out_recording_layer);
25
26private:
27 Nvnflinger::Nvnflinger* m_nvnflinger{};
28 std::set<u64> m_managed_display_layers{};
29 std::set<u64> m_managed_display_recording_layers{};
30};
31
32} // namespace Service::AM
diff --git a/src/core/hle/service/am/process.cpp b/src/core/hle/service/am/process.cpp
index 992c50713..388d2045c 100644
--- a/src/core/hle/service/am/process.cpp
+++ b/src/core/hle/service/am/process.cpp
@@ -68,7 +68,9 @@ bool Process::Initialize(u64 program_id, u8 minimum_key_generation, u8 maximum_k
68 Kernel::KProcess::Register(m_system.Kernel(), process); 68 Kernel::KProcess::Register(m_system.Kernel(), process);
69 69
70 // On exit, ensure we free the additional reference to the process. 70 // On exit, ensure we free the additional reference to the process.
71 SCOPE_EXIT({ process->Close(); }); 71 SCOPE_EXIT {
72 process->Close();
73 };
72 74
73 // Insert process modules into memory. 75 // Insert process modules into memory.
74 const auto [load_result, load_parameters] = app_loader->Load(*process, m_system); 76 const auto [load_result, load_parameters] = app_loader->Load(*process, m_system);
diff --git a/src/core/hle/service/am/service/all_system_applet_proxies_service.cpp b/src/core/hle/service/am/service/all_system_applet_proxies_service.cpp
index eebd90ba2..21747783a 100644
--- a/src/core/hle/service/am/service/all_system_applet_proxies_service.cpp
+++ b/src/core/hle/service/am/service/all_system_applet_proxies_service.cpp
@@ -10,9 +10,8 @@
10 10
11namespace Service::AM { 11namespace Service::AM {
12 12
13IAllSystemAppletProxiesService::IAllSystemAppletProxiesService(Core::System& system_, 13IAllSystemAppletProxiesService::IAllSystemAppletProxiesService(Core::System& system_)
14 Nvnflinger::Nvnflinger& nvnflinger) 14 : ServiceFramework{system_, "appletAE"} {
15 : ServiceFramework{system_, "appletAE"}, m_nvnflinger{nvnflinger} {
16 // clang-format off 15 // clang-format off
17 static const FunctionInfo functions[] = { 16 static const FunctionInfo functions[] = {
18 {100, D<&IAllSystemAppletProxiesService::OpenSystemAppletProxy>, "OpenSystemAppletProxy"}, 17 {100, D<&IAllSystemAppletProxiesService::OpenSystemAppletProxy>, "OpenSystemAppletProxy"},
@@ -37,8 +36,8 @@ Result IAllSystemAppletProxiesService::OpenSystemAppletProxy(
37 LOG_DEBUG(Service_AM, "called"); 36 LOG_DEBUG(Service_AM, "called");
38 37
39 if (const auto applet = this->GetAppletFromProcessId(pid); applet) { 38 if (const auto applet = this->GetAppletFromProcessId(pid); applet) {
40 *out_system_applet_proxy = std::make_shared<ISystemAppletProxy>( 39 *out_system_applet_proxy =
41 system, applet, process_handle.Get(), m_nvnflinger); 40 std::make_shared<ISystemAppletProxy>(system, applet, process_handle.Get());
42 R_SUCCEED(); 41 R_SUCCEED();
43 } else { 42 } else {
44 UNIMPLEMENTED(); 43 UNIMPLEMENTED();
@@ -53,8 +52,8 @@ Result IAllSystemAppletProxiesService::OpenLibraryAppletProxy(
53 LOG_DEBUG(Service_AM, "called"); 52 LOG_DEBUG(Service_AM, "called");
54 53
55 if (const auto applet = this->GetAppletFromProcessId(pid); applet) { 54 if (const auto applet = this->GetAppletFromProcessId(pid); applet) {
56 *out_library_applet_proxy = std::make_shared<ILibraryAppletProxy>( 55 *out_library_applet_proxy =
57 system, applet, process_handle.Get(), m_nvnflinger); 56 std::make_shared<ILibraryAppletProxy>(system, applet, process_handle.Get());
58 R_SUCCEED(); 57 R_SUCCEED();
59 } else { 58 } else {
60 UNIMPLEMENTED(); 59 UNIMPLEMENTED();
diff --git a/src/core/hle/service/am/service/all_system_applet_proxies_service.h b/src/core/hle/service/am/service/all_system_applet_proxies_service.h
index 38b1ca2ea..0e2dcb86d 100644
--- a/src/core/hle/service/am/service/all_system_applet_proxies_service.h
+++ b/src/core/hle/service/am/service/all_system_applet_proxies_service.h
@@ -8,10 +8,6 @@
8 8
9namespace Service { 9namespace Service {
10 10
11namespace Nvnflinger {
12class Nvnflinger;
13}
14
15namespace AM { 11namespace AM {
16 12
17struct Applet; 13struct Applet;
@@ -22,8 +18,7 @@ class ISystemAppletProxy;
22class IAllSystemAppletProxiesService final 18class IAllSystemAppletProxiesService final
23 : public ServiceFramework<IAllSystemAppletProxiesService> { 19 : public ServiceFramework<IAllSystemAppletProxiesService> {
24public: 20public:
25 explicit IAllSystemAppletProxiesService(Core::System& system_, 21 explicit IAllSystemAppletProxiesService(Core::System& system_);
26 Nvnflinger::Nvnflinger& nvnflinger);
27 ~IAllSystemAppletProxiesService() override; 22 ~IAllSystemAppletProxiesService() override;
28 23
29private: 24private:
@@ -40,7 +35,6 @@ private:
40 35
41private: 36private:
42 std::shared_ptr<Applet> GetAppletFromProcessId(ProcessId pid); 37 std::shared_ptr<Applet> GetAppletFromProcessId(ProcessId pid);
43 Nvnflinger::Nvnflinger& m_nvnflinger;
44}; 38};
45 39
46} // namespace AM 40} // namespace AM
diff --git a/src/core/hle/service/am/service/application_functions.cpp b/src/core/hle/service/am/service/application_functions.cpp
index b788fddd4..cb53b07e0 100644
--- a/src/core/hle/service/am/service/application_functions.cpp
+++ b/src/core/hle/service/am/service/application_functions.cpp
@@ -15,7 +15,9 @@
15#include "core/hle/service/cmif_serialization.h" 15#include "core/hle/service/cmif_serialization.h"
16#include "core/hle/service/filesystem/filesystem.h" 16#include "core/hle/service/filesystem/filesystem.h"
17#include "core/hle/service/filesystem/save_data_controller.h" 17#include "core/hle/service/filesystem/save_data_controller.h"
18#include "core/hle/service/ns/ns.h" 18#include "core/hle/service/glue/glue_manager.h"
19#include "core/hle/service/ns/application_manager_interface.h"
20#include "core/hle/service/ns/service_getter_interface.h"
19#include "core/hle/service/sm/sm.h" 21#include "core/hle/service/sm/sm.h"
20 22
21namespace Service::AM { 23namespace Service::AM {
@@ -40,7 +42,7 @@ IApplicationFunctions::IApplicationFunctions(Core::System& system_, std::shared_
40 {26, D<&IApplicationFunctions::GetSaveDataSize>, "GetSaveDataSize"}, 42 {26, D<&IApplicationFunctions::GetSaveDataSize>, "GetSaveDataSize"},
41 {27, D<&IApplicationFunctions::CreateCacheStorage>, "CreateCacheStorage"}, 43 {27, D<&IApplicationFunctions::CreateCacheStorage>, "CreateCacheStorage"},
42 {28, D<&IApplicationFunctions::GetSaveDataSizeMax>, "GetSaveDataSizeMax"}, 44 {28, D<&IApplicationFunctions::GetSaveDataSizeMax>, "GetSaveDataSizeMax"},
43 {29, nullptr, "GetCacheStorageMax"}, 45 {29, D<&IApplicationFunctions::GetCacheStorageMax>, "GetCacheStorageMax"},
44 {30, D<&IApplicationFunctions::BeginBlockingHomeButtonShortAndLongPressed>, "BeginBlockingHomeButtonShortAndLongPressed"}, 46 {30, D<&IApplicationFunctions::BeginBlockingHomeButtonShortAndLongPressed>, "BeginBlockingHomeButtonShortAndLongPressed"},
45 {31, D<&IApplicationFunctions::EndBlockingHomeButtonShortAndLongPressed>, "EndBlockingHomeButtonShortAndLongPressed"}, 47 {31, D<&IApplicationFunctions::EndBlockingHomeButtonShortAndLongPressed>, "EndBlockingHomeButtonShortAndLongPressed"},
46 {32, D<&IApplicationFunctions::BeginBlockingHomeButton>, "BeginBlockingHomeButton"}, 48 {32, D<&IApplicationFunctions::BeginBlockingHomeButton>, "BeginBlockingHomeButton"},
@@ -162,11 +164,13 @@ Result IApplicationFunctions::GetDesiredLanguage(Out<u64> out_language_code) {
162 164
163 // Call IApplicationManagerInterface implementation. 165 // Call IApplicationManagerInterface implementation.
164 auto& service_manager = system.ServiceManager(); 166 auto& service_manager = system.ServiceManager();
165 auto ns_am2 = service_manager.GetService<NS::NS>("ns:am2"); 167 auto ns_am2 = service_manager.GetService<NS::IServiceGetterInterface>("ns:am2");
166 auto app_man = ns_am2->GetApplicationManagerInterface(); 168
169 std::shared_ptr<NS::IApplicationManagerInterface> app_man;
170 R_TRY(ns_am2->GetApplicationManagerInterface(&app_man));
167 171
168 // Get desired application language 172 // Get desired application language
169 u8 desired_language{}; 173 NS::ApplicationLanguage desired_language{};
170 R_TRY(app_man->GetApplicationDesiredLanguage(&desired_language, supported_languages)); 174 R_TRY(app_man->GetApplicationDesiredLanguage(&desired_language, supported_languages));
171 175
172 // Convert to settings language code. 176 // Convert to settings language code.
@@ -267,6 +271,22 @@ Result IApplicationFunctions::GetSaveDataSizeMax(Out<u64> out_max_normal_size,
267 R_SUCCEED(); 271 R_SUCCEED();
268} 272}
269 273
274Result IApplicationFunctions::GetCacheStorageMax(Out<u32> out_cache_storage_index_max,
275 Out<u64> out_max_journal_size) {
276 LOG_DEBUG(Service_AM, "called");
277
278 std::vector<u8> nacp;
279 R_TRY(system.GetARPManager().GetControlProperty(&nacp, m_applet->program_id));
280
281 auto raw_nacp = std::make_unique<FileSys::RawNACP>();
282 std::memcpy(raw_nacp.get(), nacp.data(), std::min(sizeof(*raw_nacp), nacp.size()));
283
284 *out_cache_storage_index_max = static_cast<u32>(raw_nacp->cache_storage_max_index);
285 *out_max_journal_size = static_cast<u64>(raw_nacp->cache_storage_data_and_journal_max_size);
286
287 R_SUCCEED();
288}
289
270Result IApplicationFunctions::BeginBlockingHomeButtonShortAndLongPressed(s64 unused) { 290Result IApplicationFunctions::BeginBlockingHomeButtonShortAndLongPressed(s64 unused) {
271 LOG_WARNING(Service_AM, "(STUBBED) called"); 291 LOG_WARNING(Service_AM, "(STUBBED) called");
272 292
diff --git a/src/core/hle/service/am/service/application_functions.h b/src/core/hle/service/am/service/application_functions.h
index 3548202f8..10025a152 100644
--- a/src/core/hle/service/am/service/application_functions.h
+++ b/src/core/hle/service/am/service/application_functions.h
@@ -40,6 +40,7 @@ private:
40 Result CreateCacheStorage(Out<u32> out_target_media, Out<u64> out_required_size, u16 index, 40 Result CreateCacheStorage(Out<u32> out_target_media, Out<u64> out_required_size, u16 index,
41 u64 normal_size, u64 journal_size); 41 u64 normal_size, u64 journal_size);
42 Result GetSaveDataSizeMax(Out<u64> out_max_normal_size, Out<u64> out_max_journal_size); 42 Result GetSaveDataSizeMax(Out<u64> out_max_normal_size, Out<u64> out_max_journal_size);
43 Result GetCacheStorageMax(Out<u32> out_cache_storage_index_max, Out<u64> out_max_journal_size);
43 Result BeginBlockingHomeButtonShortAndLongPressed(s64 unused); 44 Result BeginBlockingHomeButtonShortAndLongPressed(s64 unused);
44 Result EndBlockingHomeButtonShortAndLongPressed(); 45 Result EndBlockingHomeButtonShortAndLongPressed();
45 Result BeginBlockingHomeButton(s64 timeout_ns); 46 Result BeginBlockingHomeButton(s64 timeout_ns);
diff --git a/src/core/hle/service/am/service/application_proxy.cpp b/src/core/hle/service/am/service/application_proxy.cpp
index 776f4552b..19d6a3b89 100644
--- a/src/core/hle/service/am/service/application_proxy.cpp
+++ b/src/core/hle/service/am/service/application_proxy.cpp
@@ -17,9 +17,9 @@
17namespace Service::AM { 17namespace Service::AM {
18 18
19IApplicationProxy::IApplicationProxy(Core::System& system_, std::shared_ptr<Applet> applet, 19IApplicationProxy::IApplicationProxy(Core::System& system_, std::shared_ptr<Applet> applet,
20 Kernel::KProcess* process, Nvnflinger::Nvnflinger& nvnflinger) 20 Kernel::KProcess* process)
21 : ServiceFramework{system_, "IApplicationProxy"}, 21 : ServiceFramework{system_, "IApplicationProxy"}, m_process{process}, m_applet{
22 m_nvnflinger{nvnflinger}, m_process{process}, m_applet{std::move(applet)} { 22 std::move(applet)} {
23 // clang-format off 23 // clang-format off
24 static const FunctionInfo functions[] = { 24 static const FunctionInfo functions[] = {
25 {0, D<&IApplicationProxy::GetCommonStateGetter>, "GetCommonStateGetter"}, 25 {0, D<&IApplicationProxy::GetCommonStateGetter>, "GetCommonStateGetter"},
@@ -77,8 +77,7 @@ Result IApplicationProxy::GetWindowController(
77Result IApplicationProxy::GetSelfController( 77Result IApplicationProxy::GetSelfController(
78 Out<SharedPointer<ISelfController>> out_self_controller) { 78 Out<SharedPointer<ISelfController>> out_self_controller) {
79 LOG_DEBUG(Service_AM, "called"); 79 LOG_DEBUG(Service_AM, "called");
80 *out_self_controller = 80 *out_self_controller = std::make_shared<ISelfController>(system, m_applet, m_process);
81 std::make_shared<ISelfController>(system, m_applet, m_process, m_nvnflinger);
82 R_SUCCEED(); 81 R_SUCCEED();
83} 82}
84 83
diff --git a/src/core/hle/service/am/service/application_proxy.h b/src/core/hle/service/am/service/application_proxy.h
index 1ebc593ba..6da350df7 100644
--- a/src/core/hle/service/am/service/application_proxy.h
+++ b/src/core/hle/service/am/service/application_proxy.h
@@ -22,7 +22,7 @@ class IWindowController;
22class IApplicationProxy final : public ServiceFramework<IApplicationProxy> { 22class IApplicationProxy final : public ServiceFramework<IApplicationProxy> {
23public: 23public:
24 explicit IApplicationProxy(Core::System& system_, std::shared_ptr<Applet> applet, 24 explicit IApplicationProxy(Core::System& system_, std::shared_ptr<Applet> applet,
25 Kernel::KProcess* process, Nvnflinger::Nvnflinger& nvnflinger); 25 Kernel::KProcess* process);
26 ~IApplicationProxy(); 26 ~IApplicationProxy();
27 27
28private: 28private:
@@ -40,7 +40,6 @@ private:
40 Out<SharedPointer<IApplicationFunctions>> out_application_functions); 40 Out<SharedPointer<IApplicationFunctions>> out_application_functions);
41 41
42private: 42private:
43 Nvnflinger::Nvnflinger& m_nvnflinger;
44 Kernel::KProcess* const m_process; 43 Kernel::KProcess* const m_process;
45 const std::shared_ptr<Applet> m_applet; 44 const std::shared_ptr<Applet> m_applet;
46}; 45};
diff --git a/src/core/hle/service/am/service/application_proxy_service.cpp b/src/core/hle/service/am/service/application_proxy_service.cpp
index 36d4478df..fd66e77b9 100644
--- a/src/core/hle/service/am/service/application_proxy_service.cpp
+++ b/src/core/hle/service/am/service/application_proxy_service.cpp
@@ -10,9 +10,8 @@
10 10
11namespace Service::AM { 11namespace Service::AM {
12 12
13IApplicationProxyService::IApplicationProxyService(Core::System& system_, 13IApplicationProxyService::IApplicationProxyService(Core::System& system_)
14 Nvnflinger::Nvnflinger& nvnflinger) 14 : ServiceFramework{system_, "appletOE"} {
15 : ServiceFramework{system_, "appletOE"}, m_nvnflinger{nvnflinger} {
16 static const FunctionInfo functions[] = { 15 static const FunctionInfo functions[] = {
17 {0, D<&IApplicationProxyService::OpenApplicationProxy>, "OpenApplicationProxy"}, 16 {0, D<&IApplicationProxyService::OpenApplicationProxy>, "OpenApplicationProxy"},
18 }; 17 };
@@ -28,7 +27,7 @@ Result IApplicationProxyService::OpenApplicationProxy(
28 27
29 if (const auto applet = this->GetAppletFromProcessId(pid)) { 28 if (const auto applet = this->GetAppletFromProcessId(pid)) {
30 *out_application_proxy = 29 *out_application_proxy =
31 std::make_shared<IApplicationProxy>(system, applet, process_handle.Get(), m_nvnflinger); 30 std::make_shared<IApplicationProxy>(system, applet, process_handle.Get());
32 R_SUCCEED(); 31 R_SUCCEED();
33 } else { 32 } else {
34 UNIMPLEMENTED(); 33 UNIMPLEMENTED();
diff --git a/src/core/hle/service/am/service/application_proxy_service.h b/src/core/hle/service/am/service/application_proxy_service.h
index 1c1d32d0b..8efafa31a 100644
--- a/src/core/hle/service/am/service/application_proxy_service.h
+++ b/src/core/hle/service/am/service/application_proxy_service.h
@@ -8,10 +8,6 @@
8 8
9namespace Service { 9namespace Service {
10 10
11namespace Nvnflinger {
12class Nvnflinger;
13}
14
15namespace AM { 11namespace AM {
16 12
17struct Applet; 13struct Applet;
@@ -19,7 +15,7 @@ class IApplicationProxy;
19 15
20class IApplicationProxyService final : public ServiceFramework<IApplicationProxyService> { 16class IApplicationProxyService final : public ServiceFramework<IApplicationProxyService> {
21public: 17public:
22 explicit IApplicationProxyService(Core::System& system_, Nvnflinger::Nvnflinger& nvnflinger); 18 explicit IApplicationProxyService(Core::System& system_);
23 ~IApplicationProxyService() override; 19 ~IApplicationProxyService() override;
24 20
25private: 21private:
@@ -28,7 +24,6 @@ private:
28 24
29private: 25private:
30 std::shared_ptr<Applet> GetAppletFromProcessId(ProcessId pid); 26 std::shared_ptr<Applet> GetAppletFromProcessId(ProcessId pid);
31 Nvnflinger::Nvnflinger& m_nvnflinger;
32}; 27};
33 28
34} // namespace AM 29} // namespace AM
diff --git a/src/core/hle/service/am/service/display_controller.cpp b/src/core/hle/service/am/service/display_controller.cpp
index 249c73dfb..ed71f9093 100644
--- a/src/core/hle/service/am/service/display_controller.cpp
+++ b/src/core/hle/service/am/service/display_controller.cpp
@@ -69,7 +69,7 @@ Result IDisplayController::ClearCaptureBuffer(bool unknown0, s32 fbshare_layer_i
69Result IDisplayController::AcquireLastForegroundCaptureSharedBuffer( 69Result IDisplayController::AcquireLastForegroundCaptureSharedBuffer(
70 Out<bool> out_was_written, Out<s32> out_fbshare_layer_index) { 70 Out<bool> out_was_written, Out<s32> out_fbshare_layer_index) {
71 LOG_WARNING(Service_AM, "(STUBBED) called"); 71 LOG_WARNING(Service_AM, "(STUBBED) called");
72 R_RETURN(applet->system_buffer_manager.WriteAppletCaptureBuffer(out_was_written, 72 R_RETURN(applet->display_layer_manager.WriteAppletCaptureBuffer(out_was_written,
73 out_fbshare_layer_index)); 73 out_fbshare_layer_index));
74} 74}
75 75
@@ -81,7 +81,7 @@ Result IDisplayController::ReleaseLastForegroundCaptureSharedBuffer() {
81Result IDisplayController::AcquireCallerAppletCaptureSharedBuffer( 81Result IDisplayController::AcquireCallerAppletCaptureSharedBuffer(
82 Out<bool> out_was_written, Out<s32> out_fbshare_layer_index) { 82 Out<bool> out_was_written, Out<s32> out_fbshare_layer_index) {
83 LOG_WARNING(Service_AM, "(STUBBED) called"); 83 LOG_WARNING(Service_AM, "(STUBBED) called");
84 R_RETURN(applet->system_buffer_manager.WriteAppletCaptureBuffer(out_was_written, 84 R_RETURN(applet->display_layer_manager.WriteAppletCaptureBuffer(out_was_written,
85 out_fbshare_layer_index)); 85 out_fbshare_layer_index));
86} 86}
87 87
@@ -93,7 +93,7 @@ Result IDisplayController::ReleaseCallerAppletCaptureSharedBuffer() {
93Result IDisplayController::AcquireLastApplicationCaptureSharedBuffer( 93Result IDisplayController::AcquireLastApplicationCaptureSharedBuffer(
94 Out<bool> out_was_written, Out<s32> out_fbshare_layer_index) { 94 Out<bool> out_was_written, Out<s32> out_fbshare_layer_index) {
95 LOG_WARNING(Service_AM, "(STUBBED) called"); 95 LOG_WARNING(Service_AM, "(STUBBED) called");
96 R_RETURN(applet->system_buffer_manager.WriteAppletCaptureBuffer(out_was_written, 96 R_RETURN(applet->display_layer_manager.WriteAppletCaptureBuffer(out_was_written,
97 out_fbshare_layer_index)); 97 out_fbshare_layer_index));
98} 98}
99 99
diff --git a/src/core/hle/service/am/service/library_applet_creator.cpp b/src/core/hle/service/am/service/library_applet_creator.cpp
index 166637d60..c97358d81 100644
--- a/src/core/hle/service/am/service/library_applet_creator.cpp
+++ b/src/core/hle/service/am/service/library_applet_creator.cpp
@@ -135,7 +135,7 @@ std::shared_ptr<ILibraryAppletAccessor> CreateGuestApplet(Core::System& system,
135 case LibraryAppletMode::AllForegroundInitiallyHidden: 135 case LibraryAppletMode::AllForegroundInitiallyHidden:
136 applet->hid_registration.EnableAppletToGetInput(false); 136 applet->hid_registration.EnableAppletToGetInput(false);
137 applet->focus_state = FocusState::NotInFocus; 137 applet->focus_state = FocusState::NotInFocus;
138 applet->system_buffer_manager.SetWindowVisibility(false); 138 applet->display_layer_manager.SetWindowVisibility(false);
139 applet->message_queue.PushMessage(AppletMessage::ChangeIntoBackground); 139 applet->message_queue.PushMessage(AppletMessage::ChangeIntoBackground);
140 break; 140 break;
141 } 141 }
diff --git a/src/core/hle/service/am/service/library_applet_proxy.cpp b/src/core/hle/service/am/service/library_applet_proxy.cpp
index bcb44a71c..58e709347 100644
--- a/src/core/hle/service/am/service/library_applet_proxy.cpp
+++ b/src/core/hle/service/am/service/library_applet_proxy.cpp
@@ -19,10 +19,9 @@
19namespace Service::AM { 19namespace Service::AM {
20 20
21ILibraryAppletProxy::ILibraryAppletProxy(Core::System& system_, std::shared_ptr<Applet> applet, 21ILibraryAppletProxy::ILibraryAppletProxy(Core::System& system_, std::shared_ptr<Applet> applet,
22 Kernel::KProcess* process, 22 Kernel::KProcess* process)
23 Nvnflinger::Nvnflinger& nvnflinger) 23 : ServiceFramework{system_, "ILibraryAppletProxy"}, m_process{process}, m_applet{
24 : ServiceFramework{system_, "ILibraryAppletProxy"}, 24 std::move(applet)} {
25 m_nvnflinger{nvnflinger}, m_process{process}, m_applet{std::move(applet)} {
26 // clang-format off 25 // clang-format off
27 static const FunctionInfo functions[] = { 26 static const FunctionInfo functions[] = {
28 {0, D<&ILibraryAppletProxy::GetCommonStateGetter>, "GetCommonStateGetter"}, 27 {0, D<&ILibraryAppletProxy::GetCommonStateGetter>, "GetCommonStateGetter"},
@@ -83,8 +82,7 @@ Result ILibraryAppletProxy::GetWindowController(
83Result ILibraryAppletProxy::GetSelfController( 82Result ILibraryAppletProxy::GetSelfController(
84 Out<SharedPointer<ISelfController>> out_self_controller) { 83 Out<SharedPointer<ISelfController>> out_self_controller) {
85 LOG_DEBUG(Service_AM, "called"); 84 LOG_DEBUG(Service_AM, "called");
86 *out_self_controller = 85 *out_self_controller = std::make_shared<ISelfController>(system, m_applet, m_process);
87 std::make_shared<ISelfController>(system, m_applet, m_process, m_nvnflinger);
88 R_SUCCEED(); 86 R_SUCCEED();
89} 87}
90 88
diff --git a/src/core/hle/service/am/service/library_applet_proxy.h b/src/core/hle/service/am/service/library_applet_proxy.h
index 23e64e295..7d0714b85 100644
--- a/src/core/hle/service/am/service/library_applet_proxy.h
+++ b/src/core/hle/service/am/service/library_applet_proxy.h
@@ -25,7 +25,7 @@ class IWindowController;
25class ILibraryAppletProxy final : public ServiceFramework<ILibraryAppletProxy> { 25class ILibraryAppletProxy final : public ServiceFramework<ILibraryAppletProxy> {
26public: 26public:
27 explicit ILibraryAppletProxy(Core::System& system_, std::shared_ptr<Applet> applet, 27 explicit ILibraryAppletProxy(Core::System& system_, std::shared_ptr<Applet> applet,
28 Kernel::KProcess* process, Nvnflinger::Nvnflinger& nvnflinger); 28 Kernel::KProcess* process);
29 ~ILibraryAppletProxy(); 29 ~ILibraryAppletProxy();
30 30
31private: 31private:
@@ -47,7 +47,6 @@ private:
47 Result GetGlobalStateController( 47 Result GetGlobalStateController(
48 Out<SharedPointer<IGlobalStateController>> out_global_state_controller); 48 Out<SharedPointer<IGlobalStateController>> out_global_state_controller);
49 49
50 Nvnflinger::Nvnflinger& m_nvnflinger;
51 Kernel::KProcess* const m_process; 50 Kernel::KProcess* const m_process;
52 const std::shared_ptr<Applet> m_applet; 51 const std::shared_ptr<Applet> m_applet;
53}; 52};
diff --git a/src/core/hle/service/am/service/library_applet_self_accessor.cpp b/src/core/hle/service/am/service/library_applet_self_accessor.cpp
index 7a3a86e88..330eb26f0 100644
--- a/src/core/hle/service/am/service/library_applet_self_accessor.cpp
+++ b/src/core/hle/service/am/service/library_applet_self_accessor.cpp
@@ -14,7 +14,8 @@
14#include "core/hle/service/cmif_serialization.h" 14#include "core/hle/service/cmif_serialization.h"
15#include "core/hle/service/filesystem/filesystem.h" 15#include "core/hle/service/filesystem/filesystem.h"
16#include "core/hle/service/glue/glue_manager.h" 16#include "core/hle/service/glue/glue_manager.h"
17#include "core/hle/service/ns/ns.h" 17#include "core/hle/service/ns/application_manager_interface.h"
18#include "core/hle/service/ns/service_getter_interface.h"
18#include "core/hle/service/sm/sm.h" 19#include "core/hle/service/sm/sm.h"
19 20
20namespace Service::AM { 21namespace Service::AM {
@@ -256,11 +257,13 @@ Result ILibraryAppletSelfAccessor::GetMainAppletApplicationDesiredLanguage(
256 257
257 // Call IApplicationManagerInterface implementation. 258 // Call IApplicationManagerInterface implementation.
258 auto& service_manager = system.ServiceManager(); 259 auto& service_manager = system.ServiceManager();
259 auto ns_am2 = service_manager.GetService<NS::NS>("ns:am2"); 260 auto ns_am2 = service_manager.GetService<NS::IServiceGetterInterface>("ns:am2");
260 auto app_man = ns_am2->GetApplicationManagerInterface(); 261
262 std::shared_ptr<NS::IApplicationManagerInterface> app_man;
263 R_TRY(ns_am2->GetApplicationManagerInterface(&app_man));
261 264
262 // Get desired application language 265 // Get desired application language
263 u8 desired_language{}; 266 NS::ApplicationLanguage desired_language{};
264 R_TRY(app_man->GetApplicationDesiredLanguage(&desired_language, supported_languages)); 267 R_TRY(app_man->GetApplicationDesiredLanguage(&desired_language, supported_languages));
265 268
266 // Convert to settings language code. 269 // Convert to settings language code.
@@ -284,17 +287,17 @@ Result ILibraryAppletSelfAccessor::GetCurrentApplicationId(Out<u64> out_applicat
284} 287}
285 288
286Result ILibraryAppletSelfAccessor::GetMainAppletAvailableUsers( 289Result ILibraryAppletSelfAccessor::GetMainAppletAvailableUsers(
287 Out<bool> out_no_users_available, Out<s32> out_users_count, 290 Out<bool> out_can_select_any_user, Out<s32> out_users_count,
288 OutArray<Common::UUID, BufferAttr_HipcMapAlias> out_users) { 291 OutArray<Common::UUID, BufferAttr_HipcMapAlias> out_users) {
289 const Service::Account::ProfileManager manager{}; 292 const Service::Account::ProfileManager manager{};
290 293
291 *out_no_users_available = true; 294 *out_can_select_any_user = false;
292 *out_users_count = -1; 295 *out_users_count = -1;
293 296
294 LOG_INFO(Service_AM, "called"); 297 LOG_INFO(Service_AM, "called");
295 298
296 if (manager.GetUserCount() > 0) { 299 if (manager.GetUserCount() > 0) {
297 *out_no_users_available = false; 300 *out_can_select_any_user = true;
298 *out_users_count = static_cast<s32>(manager.GetUserCount()); 301 *out_users_count = static_cast<s32>(manager.GetUserCount());
299 302
300 const auto users = manager.GetAllUsers(); 303 const auto users = manager.GetAllUsers();
diff --git a/src/core/hle/service/am/service/library_applet_self_accessor.h b/src/core/hle/service/am/service/library_applet_self_accessor.h
index a9743569f..3e60393c2 100644
--- a/src/core/hle/service/am/service/library_applet_self_accessor.h
+++ b/src/core/hle/service/am/service/library_applet_self_accessor.h
@@ -71,7 +71,7 @@ private:
71 ErrorCode error_code, InLargeData<ErrorContext, BufferAttr_HipcMapAlias> error_context); 71 ErrorCode error_code, InLargeData<ErrorContext, BufferAttr_HipcMapAlias> error_context);
72 Result GetMainAppletApplicationDesiredLanguage(Out<u64> out_desired_language); 72 Result GetMainAppletApplicationDesiredLanguage(Out<u64> out_desired_language);
73 Result GetCurrentApplicationId(Out<u64> out_application_id); 73 Result GetCurrentApplicationId(Out<u64> out_application_id);
74 Result GetMainAppletAvailableUsers(Out<bool> out_no_users_available, Out<s32> out_users_count, 74 Result GetMainAppletAvailableUsers(Out<bool> out_can_select_any_user, Out<s32> out_users_count,
75 OutArray<Common::UUID, BufferAttr_HipcMapAlias> out_users); 75 OutArray<Common::UUID, BufferAttr_HipcMapAlias> out_users);
76 Result ShouldSetGpuTimeSliceManually(Out<bool> out_should_set_gpu_time_slice_manually); 76 Result ShouldSetGpuTimeSliceManually(Out<bool> out_should_set_gpu_time_slice_manually);
77 Result Cmd160(Out<u64> out_unknown0); 77 Result Cmd160(Out<u64> out_unknown0);
diff --git a/src/core/hle/service/am/service/self_controller.cpp b/src/core/hle/service/am/service/self_controller.cpp
index 5c4c13de1..06314407c 100644
--- a/src/core/hle/service/am/service/self_controller.cpp
+++ b/src/core/hle/service/am/service/self_controller.cpp
@@ -15,9 +15,9 @@
15namespace Service::AM { 15namespace Service::AM {
16 16
17ISelfController::ISelfController(Core::System& system_, std::shared_ptr<Applet> applet, 17ISelfController::ISelfController(Core::System& system_, std::shared_ptr<Applet> applet,
18 Kernel::KProcess* process, Nvnflinger::Nvnflinger& nvnflinger) 18 Kernel::KProcess* process)
19 : ServiceFramework{system_, "ISelfController"}, 19 : ServiceFramework{system_, "ISelfController"}, m_process{process}, m_applet{
20 m_nvnflinger{nvnflinger}, m_process{process}, m_applet{std::move(applet)} { 20 std::move(applet)} {
21 // clang-format off 21 // clang-format off
22 static const FunctionInfo functions[] = { 22 static const FunctionInfo functions[] = {
23 {0, D<&ISelfController::Exit>, "Exit"}, 23 {0, D<&ISelfController::Exit>, "Exit"},
@@ -72,9 +72,16 @@ ISelfController::ISelfController(Core::System& system_, std::shared_ptr<Applet>
72 // clang-format on 72 // clang-format on
73 73
74 RegisterHandlers(functions); 74 RegisterHandlers(functions);
75
76 std::scoped_lock lk{m_applet->lock};
77 m_applet->display_layer_manager.Initialize(system, m_process, m_applet->applet_id,
78 m_applet->library_applet_mode);
75} 79}
76 80
77ISelfController::~ISelfController() = default; 81ISelfController::~ISelfController() {
82 std::scoped_lock lk{m_applet->lock};
83 m_applet->display_layer_manager.Finalize();
84}
78 85
79Result ISelfController::Exit() { 86Result ISelfController::Exit() {
80 LOG_DEBUG(Service_AM, "called"); 87 LOG_DEBUG(Service_AM, "called");
@@ -212,48 +219,42 @@ Result ISelfController::SetAlbumImageOrientation(
212 219
213Result ISelfController::IsSystemBufferSharingEnabled() { 220Result ISelfController::IsSystemBufferSharingEnabled() {
214 LOG_INFO(Service_AM, "called"); 221 LOG_INFO(Service_AM, "called");
215 R_SUCCEED_IF(m_applet->system_buffer_manager.Initialize( 222
216 &m_nvnflinger, m_process, m_applet->applet_id, m_applet->library_applet_mode)); 223 std::scoped_lock lk{m_applet->lock};
217 R_THROW(VI::ResultOperationFailed); 224 R_RETURN(m_applet->display_layer_manager.IsSystemBufferSharingEnabled());
218} 225}
219 226
220Result ISelfController::GetSystemSharedBufferHandle(Out<u64> out_buffer_id) { 227Result ISelfController::GetSystemSharedBufferHandle(Out<u64> out_buffer_id) {
221 LOG_WARNING(Service_AM, "(STUBBED) called"); 228 LOG_INFO(Service_AM, "called");
222
223 R_TRY(this->IsSystemBufferSharingEnabled());
224 229
225 u64 layer_id; 230 u64 layer_id;
226 m_applet->system_buffer_manager.GetSystemSharedLayerHandle(out_buffer_id, &layer_id); 231
227 R_SUCCEED(); 232 std::scoped_lock lk{m_applet->lock};
233 R_RETURN(m_applet->display_layer_manager.GetSystemSharedLayerHandle(out_buffer_id, &layer_id));
228} 234}
229 235
230Result ISelfController::GetSystemSharedLayerHandle(Out<u64> out_buffer_id, Out<u64> out_layer_id) { 236Result ISelfController::GetSystemSharedLayerHandle(Out<u64> out_buffer_id, Out<u64> out_layer_id) {
231 LOG_INFO(Service_AM, "(STUBBED) called"); 237 LOG_INFO(Service_AM, "called");
232
233 R_TRY(this->IsSystemBufferSharingEnabled());
234 238
235 m_applet->system_buffer_manager.GetSystemSharedLayerHandle(out_buffer_id, out_layer_id); 239 std::scoped_lock lk{m_applet->lock};
236 R_SUCCEED(); 240 R_RETURN(
241 m_applet->display_layer_manager.GetSystemSharedLayerHandle(out_buffer_id, out_layer_id));
237} 242}
238 243
239Result ISelfController::CreateManagedDisplayLayer(Out<u64> out_layer_id) { 244Result ISelfController::CreateManagedDisplayLayer(Out<u64> out_layer_id) {
240 LOG_INFO(Service_AM, "called"); 245 LOG_INFO(Service_AM, "called");
241 246
242 m_applet->managed_layer_holder.Initialize(&m_nvnflinger); 247 std::scoped_lock lk{m_applet->lock};
243 m_applet->managed_layer_holder.CreateManagedDisplayLayer(out_layer_id); 248 R_RETURN(m_applet->display_layer_manager.CreateManagedDisplayLayer(out_layer_id));
244
245 R_SUCCEED();
246} 249}
247 250
248Result ISelfController::CreateManagedDisplaySeparableLayer(Out<u64> out_layer_id, 251Result ISelfController::CreateManagedDisplaySeparableLayer(Out<u64> out_layer_id,
249 Out<u64> out_recording_layer_id) { 252 Out<u64> out_recording_layer_id) {
250 LOG_WARNING(Service_AM, "(STUBBED) called"); 253 LOG_WARNING(Service_AM, "(STUBBED) called");
251 254
252 m_applet->managed_layer_holder.Initialize(&m_nvnflinger); 255 std::scoped_lock lk{m_applet->lock};
253 m_applet->managed_layer_holder.CreateManagedDisplaySeparableLayer(out_layer_id, 256 R_RETURN(m_applet->display_layer_manager.CreateManagedDisplaySeparableLayer(
254 out_recording_layer_id); 257 out_layer_id, out_recording_layer_id));
255
256 R_SUCCEED();
257} 258}
258 259
259Result ISelfController::SetHandlesRequestToDisplay(bool enable) { 260Result ISelfController::SetHandlesRequestToDisplay(bool enable) {
diff --git a/src/core/hle/service/am/service/self_controller.h b/src/core/hle/service/am/service/self_controller.h
index 01fa381a3..eca083cfe 100644
--- a/src/core/hle/service/am/service/self_controller.h
+++ b/src/core/hle/service/am/service/self_controller.h
@@ -23,7 +23,7 @@ struct Applet;
23class ISelfController final : public ServiceFramework<ISelfController> { 23class ISelfController final : public ServiceFramework<ISelfController> {
24public: 24public:
25 explicit ISelfController(Core::System& system_, std::shared_ptr<Applet> applet, 25 explicit ISelfController(Core::System& system_, std::shared_ptr<Applet> applet,
26 Kernel::KProcess* process, Nvnflinger::Nvnflinger& nvnflinger); 26 Kernel::KProcess* process);
27 ~ISelfController() override; 27 ~ISelfController() override;
28 28
29private: 29private:
@@ -64,7 +64,6 @@ private:
64 Result SaveCurrentScreenshot(Capture::AlbumReportOption album_report_option); 64 Result SaveCurrentScreenshot(Capture::AlbumReportOption album_report_option);
65 Result SetRecordVolumeMuted(bool muted); 65 Result SetRecordVolumeMuted(bool muted);
66 66
67 Nvnflinger::Nvnflinger& m_nvnflinger;
68 Kernel::KProcess* const m_process; 67 Kernel::KProcess* const m_process;
69 const std::shared_ptr<Applet> m_applet; 68 const std::shared_ptr<Applet> m_applet;
70}; 69};
diff --git a/src/core/hle/service/am/service/system_applet_proxy.cpp b/src/core/hle/service/am/service/system_applet_proxy.cpp
index 5ec509d2e..d1871ef9b 100644
--- a/src/core/hle/service/am/service/system_applet_proxy.cpp
+++ b/src/core/hle/service/am/service/system_applet_proxy.cpp
@@ -19,10 +19,9 @@
19namespace Service::AM { 19namespace Service::AM {
20 20
21ISystemAppletProxy::ISystemAppletProxy(Core::System& system_, std::shared_ptr<Applet> applet, 21ISystemAppletProxy::ISystemAppletProxy(Core::System& system_, std::shared_ptr<Applet> applet,
22 Kernel::KProcess* process, 22 Kernel::KProcess* process)
23 Nvnflinger::Nvnflinger& nvnflinger) 23 : ServiceFramework{system_, "ISystemAppletProxy"}, m_process{process}, m_applet{
24 : ServiceFramework{system_, "ISystemAppletProxy"}, 24 std::move(applet)} {
25 m_nvnflinger{nvnflinger}, m_process{process}, m_applet{std::move(applet)} {
26 // clang-format off 25 // clang-format off
27 static const FunctionInfo functions[] = { 26 static const FunctionInfo functions[] = {
28 {0, D<&ISystemAppletProxy::GetCommonStateGetter>, "GetCommonStateGetter"}, 27 {0, D<&ISystemAppletProxy::GetCommonStateGetter>, "GetCommonStateGetter"},
@@ -83,8 +82,7 @@ Result ISystemAppletProxy::GetWindowController(
83Result ISystemAppletProxy::GetSelfController( 82Result ISystemAppletProxy::GetSelfController(
84 Out<SharedPointer<ISelfController>> out_self_controller) { 83 Out<SharedPointer<ISelfController>> out_self_controller) {
85 LOG_DEBUG(Service_AM, "called"); 84 LOG_DEBUG(Service_AM, "called");
86 *out_self_controller = 85 *out_self_controller = std::make_shared<ISelfController>(system, m_applet, m_process);
87 std::make_shared<ISelfController>(system, m_applet, m_process, m_nvnflinger);
88 R_SUCCEED(); 86 R_SUCCEED();
89} 87}
90 88
diff --git a/src/core/hle/service/am/service/system_applet_proxy.h b/src/core/hle/service/am/service/system_applet_proxy.h
index 3d5040315..67cd50e03 100644
--- a/src/core/hle/service/am/service/system_applet_proxy.h
+++ b/src/core/hle/service/am/service/system_applet_proxy.h
@@ -25,7 +25,7 @@ class IWindowController;
25class ISystemAppletProxy final : public ServiceFramework<ISystemAppletProxy> { 25class ISystemAppletProxy final : public ServiceFramework<ISystemAppletProxy> {
26public: 26public:
27 explicit ISystemAppletProxy(Core::System& system, std::shared_ptr<Applet> applet, 27 explicit ISystemAppletProxy(Core::System& system, std::shared_ptr<Applet> applet,
28 Kernel::KProcess* process, Nvnflinger::Nvnflinger& nvnflinger); 28 Kernel::KProcess* process);
29 ~ISystemAppletProxy(); 29 ~ISystemAppletProxy();
30 30
31private: 31private:
@@ -46,7 +46,6 @@ private:
46 Result GetGlobalStateController( 46 Result GetGlobalStateController(
47 Out<SharedPointer<IGlobalStateController>> out_global_state_controller); 47 Out<SharedPointer<IGlobalStateController>> out_global_state_controller);
48 48
49 Nvnflinger::Nvnflinger& m_nvnflinger;
50 Kernel::KProcess* const m_process; 49 Kernel::KProcess* const m_process;
51 const std::shared_ptr<Applet> m_applet; 50 const std::shared_ptr<Applet> m_applet;
52}; 51};
diff --git a/src/core/hle/service/am/service/window_controller.cpp b/src/core/hle/service/am/service/window_controller.cpp
index b874ecb91..99a4f50a2 100644
--- a/src/core/hle/service/am/service/window_controller.cpp
+++ b/src/core/hle/service/am/service/window_controller.cpp
@@ -63,7 +63,7 @@ Result IWindowController::RejectToChangeIntoBackground() {
63} 63}
64 64
65Result IWindowController::SetAppletWindowVisibility(bool visible) { 65Result IWindowController::SetAppletWindowVisibility(bool visible) {
66 m_applet->system_buffer_manager.SetWindowVisibility(visible); 66 m_applet->display_layer_manager.SetWindowVisibility(visible);
67 m_applet->hid_registration.EnableAppletToGetInput(visible); 67 m_applet->hid_registration.EnableAppletToGetInput(visible);
68 68
69 if (visible) { 69 if (visible) {
diff --git a/src/core/hle/service/am/system_buffer_manager.cpp b/src/core/hle/service/am/system_buffer_manager.cpp
deleted file mode 100644
index 48923fe41..000000000
--- a/src/core/hle/service/am/system_buffer_manager.cpp
+++ /dev/null
@@ -1,80 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/hle/service/am/system_buffer_manager.h"
5#include "core/hle/service/nvnflinger/fb_share_buffer_manager.h"
6#include "core/hle/service/nvnflinger/nvnflinger.h"
7#include "core/hle/service/vi/vi_results.h"
8
9namespace Service::AM {
10
11SystemBufferManager::SystemBufferManager() = default;
12
13SystemBufferManager::~SystemBufferManager() {
14 if (!m_nvnflinger) {
15 return;
16 }
17
18 // Clean up shared layers.
19 if (m_buffer_sharing_enabled) {
20 m_nvnflinger->GetSystemBufferManager().Finalize(m_process);
21 }
22}
23
24bool SystemBufferManager::Initialize(Nvnflinger::Nvnflinger* nvnflinger, Kernel::KProcess* process,
25 AppletId applet_id, LibraryAppletMode mode) {
26 if (m_nvnflinger) {
27 return m_buffer_sharing_enabled;
28 }
29
30 m_process = process;
31 m_nvnflinger = nvnflinger;
32 m_buffer_sharing_enabled = false;
33 m_system_shared_buffer_id = 0;
34 m_system_shared_layer_id = 0;
35
36 if (applet_id <= AppletId::Application) {
37 return false;
38 }
39
40 Nvnflinger::LayerBlending blending = Nvnflinger::LayerBlending::None;
41 if (mode == LibraryAppletMode::PartialForeground ||
42 mode == LibraryAppletMode::PartialForegroundIndirectDisplay) {
43 blending = Nvnflinger::LayerBlending::Coverage;
44 }
45
46 const auto display_id = m_nvnflinger->OpenDisplay("Default").value();
47 const auto res = m_nvnflinger->GetSystemBufferManager().Initialize(
48 m_process, &m_system_shared_buffer_id, &m_system_shared_layer_id, display_id, blending);
49
50 if (res.IsSuccess()) {
51 m_buffer_sharing_enabled = true;
52 m_nvnflinger->SetLayerVisibility(m_system_shared_layer_id, m_visible);
53 }
54
55 return m_buffer_sharing_enabled;
56}
57
58void SystemBufferManager::SetWindowVisibility(bool visible) {
59 if (m_visible == visible) {
60 return;
61 }
62
63 m_visible = visible;
64
65 if (m_nvnflinger) {
66 m_nvnflinger->SetLayerVisibility(m_system_shared_layer_id, m_visible);
67 }
68}
69
70Result SystemBufferManager::WriteAppletCaptureBuffer(bool* out_was_written,
71 s32* out_fbshare_layer_index) {
72 if (!m_buffer_sharing_enabled) {
73 return VI::ResultPermissionDenied;
74 }
75
76 return m_nvnflinger->GetSystemBufferManager().WriteAppletCaptureBuffer(out_was_written,
77 out_fbshare_layer_index);
78}
79
80} // namespace Service::AM
diff --git a/src/core/hle/service/am/system_buffer_manager.h b/src/core/hle/service/am/system_buffer_manager.h
deleted file mode 100644
index 0690f68b6..000000000
--- a/src/core/hle/service/am/system_buffer_manager.h
+++ /dev/null
@@ -1,52 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <set>
7
8#include "common/common_funcs.h"
9#include "common/common_types.h"
10
11#include "core/hle/service/am/am_types.h"
12
13namespace Kernel {
14class KProcess;
15}
16
17namespace Service::Nvnflinger {
18class Nvnflinger;
19}
20
21union Result;
22
23namespace Service::AM {
24
25class SystemBufferManager {
26public:
27 SystemBufferManager();
28 ~SystemBufferManager();
29
30 bool Initialize(Nvnflinger::Nvnflinger* flinger, Kernel::KProcess* process, AppletId applet_id,
31 LibraryAppletMode mode);
32
33 void GetSystemSharedLayerHandle(u64* out_system_shared_buffer_id,
34 u64* out_system_shared_layer_id) {
35 *out_system_shared_buffer_id = m_system_shared_buffer_id;
36 *out_system_shared_layer_id = m_system_shared_layer_id;
37 }
38
39 void SetWindowVisibility(bool visible);
40
41 Result WriteAppletCaptureBuffer(bool* out_was_written, s32* out_fbshare_layer_index);
42
43private:
44 Kernel::KProcess* m_process{};
45 Nvnflinger::Nvnflinger* m_nvnflinger{};
46 bool m_buffer_sharing_enabled{};
47 bool m_visible{true};
48 u64 m_system_shared_buffer_id{};
49 u64 m_system_shared_layer_id{};
50};
51
52} // namespace Service::AM
diff --git a/src/core/hle/service/audio/audctl.cpp b/src/core/hle/service/audio/audctl.cpp
deleted file mode 100644
index cf4bb4034..000000000
--- a/src/core/hle/service/audio/audctl.cpp
+++ /dev/null
@@ -1,201 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "common/logging/log.h"
5#include "core/hle/service/audio/audctl.h"
6#include "core/hle/service/ipc_helpers.h"
7#include "core/hle/service/set/system_settings_server.h"
8#include "core/hle/service/sm/sm.h"
9
10namespace Service::Audio {
11
12AudCtl::AudCtl(Core::System& system_) : ServiceFramework{system_, "audctl"} {
13 // clang-format off
14 static const FunctionInfo functions[] = {
15 {0, nullptr, "GetTargetVolume"},
16 {1, nullptr, "SetTargetVolume"},
17 {2, &AudCtl::GetTargetVolumeMin, "GetTargetVolumeMin"},
18 {3, &AudCtl::GetTargetVolumeMax, "GetTargetVolumeMax"},
19 {4, nullptr, "IsTargetMute"},
20 {5, nullptr, "SetTargetMute"},
21 {6, nullptr, "IsTargetConnected"},
22 {7, nullptr, "SetDefaultTarget"},
23 {8, nullptr, "GetDefaultTarget"},
24 {9, &AudCtl::GetAudioOutputMode, "GetAudioOutputMode"},
25 {10, &AudCtl::SetAudioOutputMode, "SetAudioOutputMode"},
26 {11, nullptr, "SetForceMutePolicy"},
27 {12, &AudCtl::GetForceMutePolicy, "GetForceMutePolicy"},
28 {13, &AudCtl::GetOutputModeSetting, "GetOutputModeSetting"},
29 {14, &AudCtl::SetOutputModeSetting, "SetOutputModeSetting"},
30 {15, nullptr, "SetOutputTarget"},
31 {16, nullptr, "SetInputTargetForceEnabled"},
32 {17, &AudCtl::SetHeadphoneOutputLevelMode, "SetHeadphoneOutputLevelMode"},
33 {18, &AudCtl::GetHeadphoneOutputLevelMode, "GetHeadphoneOutputLevelMode"},
34 {19, nullptr, "AcquireAudioVolumeUpdateEventForPlayReport"},
35 {20, nullptr, "AcquireAudioOutputDeviceUpdateEventForPlayReport"},
36 {21, nullptr, "GetAudioOutputTargetForPlayReport"},
37 {22, nullptr, "NotifyHeadphoneVolumeWarningDisplayedEvent"},
38 {23, nullptr, "SetSystemOutputMasterVolume"},
39 {24, nullptr, "GetSystemOutputMasterVolume"},
40 {25, nullptr, "GetAudioVolumeDataForPlayReport"},
41 {26, nullptr, "UpdateHeadphoneSettings"},
42 {27, nullptr, "SetVolumeMappingTableForDev"},
43 {28, nullptr, "GetAudioOutputChannelCountForPlayReport"},
44 {29, nullptr, "BindAudioOutputChannelCountUpdateEventForPlayReport"},
45 {30, &AudCtl::SetSpeakerAutoMuteEnabled, "SetSpeakerAutoMuteEnabled"},
46 {31, &AudCtl::IsSpeakerAutoMuteEnabled, "IsSpeakerAutoMuteEnabled"},
47 {32, nullptr, "GetActiveOutputTarget"},
48 {33, nullptr, "GetTargetDeviceInfo"},
49 {34, nullptr, "AcquireTargetNotification"},
50 {35, nullptr, "SetHearingProtectionSafeguardTimerRemainingTimeForDebug"},
51 {36, nullptr, "GetHearingProtectionSafeguardTimerRemainingTimeForDebug"},
52 {37, nullptr, "SetHearingProtectionSafeguardEnabled"},
53 {38, nullptr, "IsHearingProtectionSafeguardEnabled"},
54 {39, nullptr, "IsHearingProtectionSafeguardMonitoringOutputForDebug"},
55 {40, nullptr, "GetSystemInformationForDebug"},
56 {41, nullptr, "SetVolumeButtonLongPressTime"},
57 {42, nullptr, "SetNativeVolumeForDebug"},
58 {10000, nullptr, "NotifyAudioOutputTargetForPlayReport"},
59 {10001, nullptr, "NotifyAudioOutputChannelCountForPlayReport"},
60 {10002, nullptr, "NotifyUnsupportedUsbOutputDeviceAttachedForPlayReport"},
61 {10100, nullptr, "GetAudioVolumeDataForPlayReport"},
62 {10101, nullptr, "BindAudioVolumeUpdateEventForPlayReport"},
63 {10102, nullptr, "BindAudioOutputTargetUpdateEventForPlayReport"},
64 {10103, nullptr, "GetAudioOutputTargetForPlayReport"},
65 {10104, nullptr, "GetAudioOutputChannelCountForPlayReport"},
66 {10105, nullptr, "BindAudioOutputChannelCountUpdateEventForPlayReport"},
67 {10106, nullptr, "GetDefaultAudioOutputTargetForPlayReport"},
68 {50000, nullptr, "SetAnalogInputBoostGainForPrototyping"},
69 };
70 // clang-format on
71
72 RegisterHandlers(functions);
73
74 m_set_sys =
75 system.ServiceManager().GetService<Service::Set::ISystemSettingsServer>("set:sys", true);
76}
77
78AudCtl::~AudCtl() = default;
79
80void AudCtl::GetTargetVolumeMin(HLERequestContext& ctx) {
81 LOG_DEBUG(Audio, "called.");
82
83 // This service function is currently hardcoded on the
84 // actual console to this value (as of 8.0.0).
85 constexpr s32 target_min_volume = 0;
86
87 IPC::ResponseBuilder rb{ctx, 3};
88 rb.Push(ResultSuccess);
89 rb.Push(target_min_volume);
90}
91
92void AudCtl::GetTargetVolumeMax(HLERequestContext& ctx) {
93 LOG_DEBUG(Audio, "called.");
94
95 // This service function is currently hardcoded on the
96 // actual console to this value (as of 8.0.0).
97 constexpr s32 target_max_volume = 15;
98
99 IPC::ResponseBuilder rb{ctx, 3};
100 rb.Push(ResultSuccess);
101 rb.Push(target_max_volume);
102}
103
104void AudCtl::GetAudioOutputMode(HLERequestContext& ctx) {
105 IPC::RequestParser rp{ctx};
106 const auto target{rp.PopEnum<Set::AudioOutputModeTarget>()};
107
108 Set::AudioOutputMode output_mode{};
109 const auto result = m_set_sys->GetAudioOutputMode(&output_mode, target);
110
111 LOG_INFO(Service_SET, "called, target={}, output_mode={}", target, output_mode);
112
113 IPC::ResponseBuilder rb{ctx, 3};
114 rb.Push(result);
115 rb.PushEnum(output_mode);
116}
117
118void AudCtl::SetAudioOutputMode(HLERequestContext& ctx) {
119 IPC::RequestParser rp{ctx};
120 const auto target{rp.PopEnum<Set::AudioOutputModeTarget>()};
121 const auto output_mode{rp.PopEnum<Set::AudioOutputMode>()};
122
123 const auto result = m_set_sys->SetAudioOutputMode(target, output_mode);
124
125 LOG_INFO(Service_SET, "called, target={}, output_mode={}", target, output_mode);
126
127 IPC::ResponseBuilder rb{ctx, 2};
128 rb.Push(result);
129}
130
131void AudCtl::GetForceMutePolicy(HLERequestContext& ctx) {
132 LOG_WARNING(Audio, "(STUBBED) called");
133
134 IPC::ResponseBuilder rb{ctx, 3};
135 rb.Push(ResultSuccess);
136 rb.PushEnum(ForceMutePolicy::Disable);
137}
138
139void AudCtl::GetOutputModeSetting(HLERequestContext& ctx) {
140 IPC::RequestParser rp{ctx};
141 const auto target{rp.PopEnum<Set::AudioOutputModeTarget>()};
142
143 LOG_WARNING(Audio, "(STUBBED) called, target={}", target);
144
145 IPC::ResponseBuilder rb{ctx, 3};
146 rb.Push(ResultSuccess);
147 rb.PushEnum(Set::AudioOutputMode::ch_7_1);
148}
149
150void AudCtl::SetOutputModeSetting(HLERequestContext& ctx) {
151 IPC::RequestParser rp{ctx};
152 const auto target{rp.PopEnum<Set::AudioOutputModeTarget>()};
153 const auto output_mode{rp.PopEnum<Set::AudioOutputMode>()};
154
155 LOG_INFO(Service_SET, "called, target={}, output_mode={}", target, output_mode);
156
157 IPC::ResponseBuilder rb{ctx, 2};
158 rb.Push(ResultSuccess);
159}
160
161void AudCtl::SetHeadphoneOutputLevelMode(HLERequestContext& ctx) {
162 LOG_WARNING(Audio, "(STUBBED) called");
163
164 IPC::ResponseBuilder rb{ctx, 2};
165 rb.Push(ResultSuccess);
166}
167
168void AudCtl::GetHeadphoneOutputLevelMode(HLERequestContext& ctx) {
169 LOG_WARNING(Audio, "(STUBBED) called");
170
171 IPC::ResponseBuilder rb{ctx, 3};
172 rb.Push(ResultSuccess);
173 rb.PushEnum(HeadphoneOutputLevelMode::Normal);
174}
175
176void AudCtl::SetSpeakerAutoMuteEnabled(HLERequestContext& ctx) {
177 IPC::RequestParser rp{ctx};
178 const auto is_speaker_auto_mute_enabled{rp.Pop<bool>()};
179
180 LOG_WARNING(Audio, "(STUBBED) called, is_speaker_auto_mute_enabled={}",
181 is_speaker_auto_mute_enabled);
182
183 const auto result = m_set_sys->SetSpeakerAutoMuteFlag(is_speaker_auto_mute_enabled);
184
185 IPC::ResponseBuilder rb{ctx, 2};
186 rb.Push(result);
187}
188
189void AudCtl::IsSpeakerAutoMuteEnabled(HLERequestContext& ctx) {
190 bool is_speaker_auto_mute_enabled{};
191 const auto result = m_set_sys->GetSpeakerAutoMuteFlag(&is_speaker_auto_mute_enabled);
192
193 LOG_WARNING(Audio, "(STUBBED) called, is_speaker_auto_mute_enabled={}",
194 is_speaker_auto_mute_enabled);
195
196 IPC::ResponseBuilder rb{ctx, 3};
197 rb.Push(result);
198 rb.Push<u8>(is_speaker_auto_mute_enabled);
199}
200
201} // namespace Service::Audio
diff --git a/src/core/hle/service/audio/audctl.h b/src/core/hle/service/audio/audctl.h
deleted file mode 100644
index 4c90ead70..000000000
--- a/src/core/hle/service/audio/audctl.h
+++ /dev/null
@@ -1,50 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "core/hle/service/service.h"
7
8namespace Core {
9class System;
10}
11
12namespace Service::Set {
13class ISystemSettingsServer;
14}
15
16namespace Service::Audio {
17
18class AudCtl final : public ServiceFramework<AudCtl> {
19public:
20 explicit AudCtl(Core::System& system_);
21 ~AudCtl() override;
22
23private:
24 enum class ForceMutePolicy {
25 Disable,
26 SpeakerMuteOnHeadphoneUnplugged,
27 };
28
29 enum class HeadphoneOutputLevelMode {
30 Normal,
31 HighPower,
32 };
33
34 void GetTargetVolumeMin(HLERequestContext& ctx);
35 void GetTargetVolumeMax(HLERequestContext& ctx);
36 void GetAudioOutputMode(HLERequestContext& ctx);
37 void SetAudioOutputMode(HLERequestContext& ctx);
38 void GetForceMutePolicy(HLERequestContext& ctx);
39 void GetOutputModeSetting(HLERequestContext& ctx);
40 void SetOutputModeSetting(HLERequestContext& ctx);
41 void SetHeadphoneOutputLevelMode(HLERequestContext& ctx);
42 void GetHeadphoneOutputLevelMode(HLERequestContext& ctx);
43 void SetSpeakerAutoMuteEnabled(HLERequestContext& ctx);
44 void IsSpeakerAutoMuteEnabled(HLERequestContext& ctx);
45 void AcquireTargetNotification(HLERequestContext& ctx);
46
47 std::shared_ptr<Service::Set::ISystemSettingsServer> m_set_sys;
48};
49
50} // namespace Service::Audio
diff --git a/src/core/hle/service/audio/audio.cpp b/src/core/hle/service/audio/audio.cpp
index dccd16309..44af030eb 100644
--- a/src/core/hle/service/audio/audio.cpp
+++ b/src/core/hle/service/audio/audio.cpp
@@ -2,9 +2,9 @@
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include "core/core.h" 4#include "core/core.h"
5#include "core/hle/service/audio/audctl.h"
6#include "core/hle/service/audio/audin_u.h" 5#include "core/hle/service/audio/audin_u.h"
7#include "core/hle/service/audio/audio.h" 6#include "core/hle/service/audio/audio.h"
7#include "core/hle/service/audio/audio_controller.h"
8#include "core/hle/service/audio/audout_u.h" 8#include "core/hle/service/audio/audout_u.h"
9#include "core/hle/service/audio/audrec_a.h" 9#include "core/hle/service/audio/audrec_a.h"
10#include "core/hle/service/audio/audrec_u.h" 10#include "core/hle/service/audio/audrec_u.h"
@@ -18,7 +18,7 @@ namespace Service::Audio {
18void LoopProcess(Core::System& system) { 18void LoopProcess(Core::System& system) {
19 auto server_manager = std::make_unique<ServerManager>(system); 19 auto server_manager = std::make_unique<ServerManager>(system);
20 20
21 server_manager->RegisterNamedService("audctl", std::make_shared<AudCtl>(system)); 21 server_manager->RegisterNamedService("audctl", std::make_shared<IAudioController>(system));
22 server_manager->RegisterNamedService("audout:u", std::make_shared<AudOutU>(system)); 22 server_manager->RegisterNamedService("audout:u", std::make_shared<AudOutU>(system));
23 server_manager->RegisterNamedService("audin:u", std::make_shared<AudInU>(system)); 23 server_manager->RegisterNamedService("audin:u", std::make_shared<AudInU>(system));
24 server_manager->RegisterNamedService("audrec:a", std::make_shared<AudRecA>(system)); 24 server_manager->RegisterNamedService("audrec:a", std::make_shared<AudRecA>(system));
diff --git a/src/core/hle/service/audio/audio_controller.cpp b/src/core/hle/service/audio/audio_controller.cpp
new file mode 100644
index 000000000..a6da66d0f
--- /dev/null
+++ b/src/core/hle/service/audio/audio_controller.cpp
@@ -0,0 +1,174 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "common/logging/log.h"
5#include "core/hle/service/audio/audio_controller.h"
6#include "core/hle/service/cmif_serialization.h"
7#include "core/hle/service/ipc_helpers.h"
8#include "core/hle/service/set/system_settings_server.h"
9#include "core/hle/service/sm/sm.h"
10
11namespace Service::Audio {
12
13IAudioController::IAudioController(Core::System& system_)
14 : ServiceFramework{system_, "audctl"}, service_context{system, "audctl"} {
15 // clang-format off
16 static const FunctionInfo functions[] = {
17 {0, nullptr, "GetTargetVolume"},
18 {1, nullptr, "SetTargetVolume"},
19 {2, C<&IAudioController::GetTargetVolumeMin>, "GetTargetVolumeMin"},
20 {3, C<&IAudioController::GetTargetVolumeMax>, "GetTargetVolumeMax"},
21 {4, nullptr, "IsTargetMute"},
22 {5, nullptr, "SetTargetMute"},
23 {6, nullptr, "IsTargetConnected"},
24 {7, nullptr, "SetDefaultTarget"},
25 {8, nullptr, "GetDefaultTarget"},
26 {9, C<&IAudioController::GetAudioOutputMode>, "GetAudioOutputMode"},
27 {10, C<&IAudioController::SetAudioOutputMode>, "SetAudioOutputMode"},
28 {11, nullptr, "SetForceMutePolicy"},
29 {12, C<&IAudioController::GetForceMutePolicy>, "GetForceMutePolicy"},
30 {13, C<&IAudioController::GetOutputModeSetting>, "GetOutputModeSetting"},
31 {14, C<&IAudioController::SetOutputModeSetting>, "SetOutputModeSetting"},
32 {15, nullptr, "SetOutputTarget"},
33 {16, nullptr, "SetInputTargetForceEnabled"},
34 {17, C<&IAudioController::SetHeadphoneOutputLevelMode>, "SetHeadphoneOutputLevelMode"},
35 {18, C<&IAudioController::GetHeadphoneOutputLevelMode>, "GetHeadphoneOutputLevelMode"},
36 {19, nullptr, "AcquireAudioVolumeUpdateEventForPlayReport"},
37 {20, nullptr, "AcquireAudioOutputDeviceUpdateEventForPlayReport"},
38 {21, nullptr, "GetAudioOutputTargetForPlayReport"},
39 {22, nullptr, "NotifyHeadphoneVolumeWarningDisplayedEvent"},
40 {23, nullptr, "SetSystemOutputMasterVolume"},
41 {24, nullptr, "GetSystemOutputMasterVolume"},
42 {25, nullptr, "GetAudioVolumeDataForPlayReport"},
43 {26, nullptr, "UpdateHeadphoneSettings"},
44 {27, nullptr, "SetVolumeMappingTableForDev"},
45 {28, nullptr, "GetAudioOutputChannelCountForPlayReport"},
46 {29, nullptr, "BindAudioOutputChannelCountUpdateEventForPlayReport"},
47 {30, C<&IAudioController::SetSpeakerAutoMuteEnabled>, "SetSpeakerAutoMuteEnabled"},
48 {31, C<&IAudioController::IsSpeakerAutoMuteEnabled>, "IsSpeakerAutoMuteEnabled"},
49 {32, nullptr, "GetActiveOutputTarget"},
50 {33, nullptr, "GetTargetDeviceInfo"},
51 {34, C<&IAudioController::AcquireTargetNotification>, "AcquireTargetNotification"},
52 {35, nullptr, "SetHearingProtectionSafeguardTimerRemainingTimeForDebug"},
53 {36, nullptr, "GetHearingProtectionSafeguardTimerRemainingTimeForDebug"},
54 {37, nullptr, "SetHearingProtectionSafeguardEnabled"},
55 {38, nullptr, "IsHearingProtectionSafeguardEnabled"},
56 {39, nullptr, "IsHearingProtectionSafeguardMonitoringOutputForDebug"},
57 {40, nullptr, "GetSystemInformationForDebug"},
58 {41, nullptr, "SetVolumeButtonLongPressTime"},
59 {42, nullptr, "SetNativeVolumeForDebug"},
60 {10000, nullptr, "NotifyAudioOutputTargetForPlayReport"},
61 {10001, nullptr, "NotifyAudioOutputChannelCountForPlayReport"},
62 {10002, nullptr, "NotifyUnsupportedUsbOutputDeviceAttachedForPlayReport"},
63 {10100, nullptr, "GetAudioVolumeDataForPlayReport"},
64 {10101, nullptr, "BindAudioVolumeUpdateEventForPlayReport"},
65 {10102, nullptr, "BindAudioOutputTargetUpdateEventForPlayReport"},
66 {10103, nullptr, "GetAudioOutputTargetForPlayReport"},
67 {10104, nullptr, "GetAudioOutputChannelCountForPlayReport"},
68 {10105, nullptr, "BindAudioOutputChannelCountUpdateEventForPlayReport"},
69 {10106, nullptr, "GetDefaultAudioOutputTargetForPlayReport"},
70 {50000, nullptr, "SetAnalogInputBoostGainForPrototyping"},
71 };
72 // clang-format on
73
74 RegisterHandlers(functions);
75
76 m_set_sys =
77 system.ServiceManager().GetService<Service::Set::ISystemSettingsServer>("set:sys", true);
78 notification_event = service_context.CreateEvent("IAudioController:NotificationEvent");
79}
80
81IAudioController::~IAudioController() {
82 service_context.CloseEvent(notification_event);
83};
84
85Result IAudioController::GetTargetVolumeMin(Out<s32> out_target_min_volume) {
86 LOG_DEBUG(Audio, "called.");
87
88 // This service function is currently hardcoded on the
89 // actual console to this value (as of 8.0.0).
90 *out_target_min_volume = 0;
91 R_SUCCEED();
92}
93
94Result IAudioController::GetTargetVolumeMax(Out<s32> out_target_max_volume) {
95 LOG_DEBUG(Audio, "called.");
96
97 // This service function is currently hardcoded on the
98 // actual console to this value (as of 8.0.0).
99 *out_target_max_volume = 15;
100 R_SUCCEED();
101}
102
103Result IAudioController::GetAudioOutputMode(Out<Set::AudioOutputMode> out_output_mode,
104 Set::AudioOutputModeTarget target) {
105 const auto result = m_set_sys->GetAudioOutputMode(out_output_mode, target);
106
107 LOG_INFO(Service_SET, "called, target={}, output_mode={}", target, *out_output_mode);
108 R_RETURN(result);
109}
110
111Result IAudioController::SetAudioOutputMode(Set::AudioOutputModeTarget target,
112 Set::AudioOutputMode output_mode) {
113 LOG_INFO(Service_SET, "called, target={}, output_mode={}", target, output_mode);
114
115 R_RETURN(m_set_sys->SetAudioOutputMode(target, output_mode));
116}
117
118Result IAudioController::GetForceMutePolicy(Out<ForceMutePolicy> out_mute_policy) {
119 LOG_WARNING(Audio, "(STUBBED) called");
120
121 // Removed on FW 13.2.1+
122 *out_mute_policy = ForceMutePolicy::Disable;
123 R_SUCCEED();
124}
125
126Result IAudioController::GetOutputModeSetting(Out<Set::AudioOutputMode> out_output_mode,
127 Set::AudioOutputModeTarget target) {
128 LOG_WARNING(Audio, "(STUBBED) called, target={}", target);
129
130 *out_output_mode = Set::AudioOutputMode::ch_7_1;
131 R_SUCCEED();
132}
133
134Result IAudioController::SetOutputModeSetting(Set::AudioOutputModeTarget target,
135 Set::AudioOutputMode output_mode) {
136 LOG_INFO(Service_SET, "called, target={}, output_mode={}", target, output_mode);
137 R_SUCCEED();
138}
139
140Result IAudioController::SetHeadphoneOutputLevelMode(HeadphoneOutputLevelMode output_level_mode) {
141 LOG_WARNING(Audio, "(STUBBED) called");
142 R_SUCCEED();
143}
144
145Result IAudioController::GetHeadphoneOutputLevelMode(
146 Out<HeadphoneOutputLevelMode> out_output_level_mode) {
147 LOG_INFO(Audio, "called");
148
149 *out_output_level_mode = HeadphoneOutputLevelMode::Normal;
150 R_SUCCEED();
151}
152
153Result IAudioController::SetSpeakerAutoMuteEnabled(bool is_speaker_auto_mute_enabled) {
154 LOG_INFO(Audio, "called, is_speaker_auto_mute_enabled={}", is_speaker_auto_mute_enabled);
155
156 R_RETURN(m_set_sys->SetSpeakerAutoMuteFlag(is_speaker_auto_mute_enabled));
157}
158
159Result IAudioController::IsSpeakerAutoMuteEnabled(Out<bool> out_is_speaker_auto_mute_enabled) {
160 const auto result = m_set_sys->GetSpeakerAutoMuteFlag(out_is_speaker_auto_mute_enabled);
161
162 LOG_INFO(Audio, "called, is_speaker_auto_mute_enabled={}", *out_is_speaker_auto_mute_enabled);
163 R_RETURN(result);
164}
165
166Result IAudioController::AcquireTargetNotification(
167 OutCopyHandle<Kernel::KReadableEvent> out_notification_event) {
168 LOG_WARNING(Service_AM, "(STUBBED) called");
169
170 *out_notification_event = &notification_event->GetReadableEvent();
171 R_SUCCEED();
172}
173
174} // namespace Service::Audio
diff --git a/src/core/hle/service/audio/audio_controller.h b/src/core/hle/service/audio/audio_controller.h
new file mode 100644
index 000000000..9e8514373
--- /dev/null
+++ b/src/core/hle/service/audio/audio_controller.h
@@ -0,0 +1,58 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "core/hle/service/cmif_types.h"
7#include "core/hle/service/service.h"
8#include "core/hle/service/set/settings_types.h"
9
10namespace Core {
11class System;
12}
13
14namespace Service::Set {
15class ISystemSettingsServer;
16}
17
18namespace Service::Audio {
19
20class IAudioController final : public ServiceFramework<IAudioController> {
21public:
22 explicit IAudioController(Core::System& system_);
23 ~IAudioController() override;
24
25private:
26 enum class ForceMutePolicy {
27 Disable,
28 SpeakerMuteOnHeadphoneUnplugged,
29 };
30
31 enum class HeadphoneOutputLevelMode {
32 Normal,
33 HighPower,
34 };
35
36 Result GetTargetVolumeMin(Out<s32> out_target_min_volume);
37 Result GetTargetVolumeMax(Out<s32> out_target_max_volume);
38 Result GetAudioOutputMode(Out<Set::AudioOutputMode> out_output_mode,
39 Set::AudioOutputModeTarget target);
40 Result SetAudioOutputMode(Set::AudioOutputModeTarget target, Set::AudioOutputMode output_mode);
41 Result GetForceMutePolicy(Out<ForceMutePolicy> out_mute_policy);
42 Result GetOutputModeSetting(Out<Set::AudioOutputMode> out_output_mode,
43 Set::AudioOutputModeTarget target);
44 Result SetOutputModeSetting(Set::AudioOutputModeTarget target,
45 Set::AudioOutputMode output_mode);
46 Result SetHeadphoneOutputLevelMode(HeadphoneOutputLevelMode output_level_mode);
47 Result GetHeadphoneOutputLevelMode(Out<HeadphoneOutputLevelMode> out_output_level_mode);
48 Result SetSpeakerAutoMuteEnabled(bool is_speaker_auto_mute_enabled);
49 Result IsSpeakerAutoMuteEnabled(Out<bool> out_is_speaker_auto_mute_enabled);
50 Result AcquireTargetNotification(OutCopyHandle<Kernel::KReadableEvent> out_notification_event);
51
52 KernelHelpers::ServiceContext service_context;
53
54 Kernel::KEvent* notification_event;
55 std::shared_ptr<Service::Set::ISystemSettingsServer> m_set_sys;
56};
57
58} // namespace Service::Audio
diff --git a/src/core/hle/service/caps/caps_a.cpp b/src/core/hle/service/caps/caps_a.cpp
index 47ff072c5..52228b830 100644
--- a/src/core/hle/service/caps/caps_a.cpp
+++ b/src/core/hle/service/caps/caps_a.cpp
@@ -16,7 +16,7 @@ IAlbumAccessorService::IAlbumAccessorService(Core::System& system_,
16 // clang-format off 16 // clang-format off
17 static const FunctionInfo functions[] = { 17 static const FunctionInfo functions[] = {
18 {0, nullptr, "GetAlbumFileCount"}, 18 {0, nullptr, "GetAlbumFileCount"},
19 {1, nullptr, "GetAlbumFileList"}, 19 {1, C<&IAlbumAccessorService::GetAlbumFileList>, "GetAlbumFileList"},
20 {2, nullptr, "LoadAlbumFile"}, 20 {2, nullptr, "LoadAlbumFile"},
21 {3, C<&IAlbumAccessorService::DeleteAlbumFile>, "DeleteAlbumFile"}, 21 {3, C<&IAlbumAccessorService::DeleteAlbumFile>, "DeleteAlbumFile"},
22 {4, nullptr, "StorageCopyAlbumFile"}, 22 {4, nullptr, "StorageCopyAlbumFile"},
@@ -62,6 +62,15 @@ IAlbumAccessorService::IAlbumAccessorService(Core::System& system_,
62 62
63IAlbumAccessorService::~IAlbumAccessorService() = default; 63IAlbumAccessorService::~IAlbumAccessorService() = default;
64 64
65Result IAlbumAccessorService::GetAlbumFileList(
66 Out<u64> out_count, AlbumStorage storage,
67 OutArray<AlbumEntry, BufferAttr_HipcMapAlias> out_entries) {
68 LOG_INFO(Service_Capture, "called, storage={}", storage);
69
70 const Result result = manager->GetAlbumFileList(out_entries, *out_count, storage, 0);
71 R_RETURN(TranslateResult(result));
72}
73
65Result IAlbumAccessorService::DeleteAlbumFile(AlbumFileId file_id) { 74Result IAlbumAccessorService::DeleteAlbumFile(AlbumFileId file_id) {
66 LOG_INFO(Service_Capture, "called, application_id=0x{:0x}, storage={}, type={}", 75 LOG_INFO(Service_Capture, "called, application_id=0x{:0x}, storage={}, type={}",
67 file_id.application_id, file_id.storage, file_id.type); 76 file_id.application_id, file_id.storage, file_id.type);
diff --git a/src/core/hle/service/caps/caps_a.h b/src/core/hle/service/caps/caps_a.h
index 2cb9b4547..c7a5208e3 100644
--- a/src/core/hle/service/caps/caps_a.h
+++ b/src/core/hle/service/caps/caps_a.h
@@ -21,6 +21,9 @@ public:
21 ~IAlbumAccessorService() override; 21 ~IAlbumAccessorService() override;
22 22
23private: 23private:
24 Result GetAlbumFileList(Out<u64> out_count, AlbumStorage storage,
25 OutArray<AlbumEntry, BufferAttr_HipcMapAlias> out_entries);
26
24 Result DeleteAlbumFile(AlbumFileId file_id); 27 Result DeleteAlbumFile(AlbumFileId file_id);
25 28
26 Result IsAlbumMounted(Out<bool> out_is_mounted, AlbumStorage storage); 29 Result IsAlbumMounted(Out<bool> out_is_mounted, AlbumStorage storage);
diff --git a/src/core/hle/service/erpt/erpt.cpp b/src/core/hle/service/erpt/erpt.cpp
index 3ea862fad..39ae3a723 100644
--- a/src/core/hle/service/erpt/erpt.cpp
+++ b/src/core/hle/service/erpt/erpt.cpp
@@ -3,6 +3,8 @@
3 3
4#include <memory> 4#include <memory>
5 5
6#include "common/logging/log.h"
7#include "core/hle/service/cmif_serialization.h"
6#include "core/hle/service/erpt/erpt.h" 8#include "core/hle/service/erpt/erpt.h"
7#include "core/hle/service/server_manager.h" 9#include "core/hle/service/server_manager.h"
8#include "core/hle/service/service.h" 10#include "core/hle/service/service.h"
@@ -15,7 +17,7 @@ public:
15 explicit ErrorReportContext(Core::System& system_) : ServiceFramework{system_, "erpt:c"} { 17 explicit ErrorReportContext(Core::System& system_) : ServiceFramework{system_, "erpt:c"} {
16 // clang-format off 18 // clang-format off
17 static const FunctionInfo functions[] = { 19 static const FunctionInfo functions[] = {
18 {0, nullptr, "SubmitContext"}, 20 {0, C<&ErrorReportContext::SubmitContext>, "SubmitContext"},
19 {1, nullptr, "CreateReportV0"}, 21 {1, nullptr, "CreateReportV0"},
20 {2, nullptr, "SetInitialLaunchSettingsCompletionTime"}, 22 {2, nullptr, "SetInitialLaunchSettingsCompletionTime"},
21 {3, nullptr, "ClearInitialLaunchSettingsCompletionTime"}, 23 {3, nullptr, "ClearInitialLaunchSettingsCompletionTime"},
@@ -36,6 +38,14 @@ public:
36 38
37 RegisterHandlers(functions); 39 RegisterHandlers(functions);
38 } 40 }
41
42private:
43 Result SubmitContext(InBuffer<BufferAttr_HipcMapAlias> buffer_a,
44 InBuffer<BufferAttr_HipcMapAlias> buffer_b) {
45 LOG_WARNING(Service_SET, "(STUBBED) called, buffer_a_size={}, buffer_b_size={}",
46 buffer_a.size(), buffer_b.size());
47 R_SUCCEED();
48 }
39}; 49};
40 50
41class ErrorReportSession final : public ServiceFramework<ErrorReportSession> { 51class ErrorReportSession final : public ServiceFramework<ErrorReportSession> {
diff --git a/src/core/hle/service/filesystem/fsp/fsp_srv.cpp b/src/core/hle/service/filesystem/fsp/fsp_srv.cpp
index 63c2d3a58..2d49f30c8 100644
--- a/src/core/hle/service/filesystem/fsp/fsp_srv.cpp
+++ b/src/core/hle/service/filesystem/fsp/fsp_srv.cpp
@@ -336,7 +336,7 @@ FSP_SRV::FSP_SRV(Core::System& system_)
336 {1012, nullptr, "GetFsStackUsage"}, 336 {1012, nullptr, "GetFsStackUsage"},
337 {1013, nullptr, "UnsetSaveDataRootPath"}, 337 {1013, nullptr, "UnsetSaveDataRootPath"},
338 {1014, nullptr, "OutputMultiProgramTagAccessLog"}, 338 {1014, nullptr, "OutputMultiProgramTagAccessLog"},
339 {1016, nullptr, "FlushAccessLogOnSdCard"}, 339 {1016, &FSP_SRV::FlushAccessLogOnSdCard, "FlushAccessLogOnSdCard"},
340 {1017, nullptr, "OutputApplicationInfoAccessLog"}, 340 {1017, nullptr, "OutputApplicationInfoAccessLog"},
341 {1018, nullptr, "SetDebugOption"}, 341 {1018, nullptr, "SetDebugOption"},
342 {1019, nullptr, "UnsetDebugOption"}, 342 {1019, nullptr, "UnsetDebugOption"},
@@ -706,6 +706,13 @@ void FSP_SRV::GetProgramIndexForAccessLog(HLERequestContext& ctx) {
706 rb.Push(access_log_program_index); 706 rb.Push(access_log_program_index);
707} 707}
708 708
709void FSP_SRV::FlushAccessLogOnSdCard(HLERequestContext& ctx) {
710 LOG_DEBUG(Service_FS, "(STUBBED) called");
711
712 IPC::ResponseBuilder rb{ctx, 2};
713 rb.Push(ResultSuccess);
714}
715
709void FSP_SRV::GetCacheStorageSize(HLERequestContext& ctx) { 716void FSP_SRV::GetCacheStorageSize(HLERequestContext& ctx) {
710 IPC::RequestParser rp{ctx}; 717 IPC::RequestParser rp{ctx};
711 const auto index{rp.Pop<s32>()}; 718 const auto index{rp.Pop<s32>()};
diff --git a/src/core/hle/service/filesystem/fsp/fsp_srv.h b/src/core/hle/service/filesystem/fsp/fsp_srv.h
index 26980af99..59406e6f9 100644
--- a/src/core/hle/service/filesystem/fsp/fsp_srv.h
+++ b/src/core/hle/service/filesystem/fsp/fsp_srv.h
@@ -58,6 +58,7 @@ private:
58 void SetGlobalAccessLogMode(HLERequestContext& ctx); 58 void SetGlobalAccessLogMode(HLERequestContext& ctx);
59 void GetGlobalAccessLogMode(HLERequestContext& ctx); 59 void GetGlobalAccessLogMode(HLERequestContext& ctx);
60 void OutputAccessLogToSdCard(HLERequestContext& ctx); 60 void OutputAccessLogToSdCard(HLERequestContext& ctx);
61 void FlushAccessLogOnSdCard(HLERequestContext& ctx);
61 void GetProgramIndexForAccessLog(HLERequestContext& ctx); 62 void GetProgramIndexForAccessLog(HLERequestContext& ctx);
62 void OpenMultiCommitManager(HLERequestContext& ctx); 63 void OpenMultiCommitManager(HLERequestContext& ctx);
63 void GetCacheStorageSize(HLERequestContext& ctx); 64 void GetCacheStorageSize(HLERequestContext& ctx);
diff --git a/src/core/hle/service/glue/time/manager.cpp b/src/core/hle/service/glue/time/manager.cpp
index cad755fa7..059ac3fc9 100644
--- a/src/core/hle/service/glue/time/manager.cpp
+++ b/src/core/hle/service/glue/time/manager.cpp
@@ -186,6 +186,10 @@ TimeManager::TimeManager(Core::System& system)
186 } 186 }
187} 187}
188 188
189TimeManager::~TimeManager() {
190 ResetTimeZoneBinary();
191}
192
189Result TimeManager::SetupStandardSteadyClockCore() { 193Result TimeManager::SetupStandardSteadyClockCore() {
190 Common::UUID external_clock_source_id{}; 194 Common::UUID external_clock_source_id{};
191 auto res = m_set_sys->GetExternalSteadyClockSourceId(&external_clock_source_id); 195 auto res = m_set_sys->GetExternalSteadyClockSourceId(&external_clock_source_id);
diff --git a/src/core/hle/service/glue/time/manager.h b/src/core/hle/service/glue/time/manager.h
index 1de93f8f9..bb4b65049 100644
--- a/src/core/hle/service/glue/time/manager.h
+++ b/src/core/hle/service/glue/time/manager.h
@@ -26,6 +26,7 @@ namespace Service::Glue::Time {
26class TimeManager { 26class TimeManager {
27public: 27public:
28 explicit TimeManager(Core::System& system); 28 explicit TimeManager(Core::System& system);
29 ~TimeManager();
29 30
30 std::shared_ptr<Service::Set::ISystemSettingsServer> m_set_sys; 31 std::shared_ptr<Service::Set::ISystemSettingsServer> m_set_sys;
31 32
diff --git a/src/core/hle/service/glue/time/static.cpp b/src/core/hle/service/glue/time/static.cpp
index ec9b0efb1..b801faef2 100644
--- a/src/core/hle/service/glue/time/static.cpp
+++ b/src/core/hle/service/glue/time/static.cpp
@@ -142,16 +142,18 @@ Result StaticService::SetStandardSteadyClockInternalOffset(s64 offset_ns) {
142} 142}
143 143
144Result StaticService::GetStandardSteadyClockRtcValue(Out<s64> out_rtc_value) { 144Result StaticService::GetStandardSteadyClockRtcValue(Out<s64> out_rtc_value) {
145 SCOPE_EXIT({ LOG_DEBUG(Service_Time, "called. out_rtc_value={}", *out_rtc_value); }); 145 SCOPE_EXIT {
146 LOG_DEBUG(Service_Time, "called. out_rtc_value={}", *out_rtc_value);
147 };
146 148
147 R_RETURN(m_standard_steady_clock_resource.GetRtcTimeInSeconds(*out_rtc_value)); 149 R_RETURN(m_standard_steady_clock_resource.GetRtcTimeInSeconds(*out_rtc_value));
148} 150}
149 151
150Result StaticService::IsStandardUserSystemClockAutomaticCorrectionEnabled( 152Result StaticService::IsStandardUserSystemClockAutomaticCorrectionEnabled(
151 Out<bool> out_automatic_correction) { 153 Out<bool> out_automatic_correction) {
152 SCOPE_EXIT({ 154 SCOPE_EXIT {
153 LOG_DEBUG(Service_Time, "called. out_automatic_correction={}", *out_automatic_correction); 155 LOG_DEBUG(Service_Time, "called. out_automatic_correction={}", *out_automatic_correction);
154 }); 156 };
155 157
156 R_RETURN(m_wrapped_service->IsStandardUserSystemClockAutomaticCorrectionEnabled( 158 R_RETURN(m_wrapped_service->IsStandardUserSystemClockAutomaticCorrectionEnabled(
157 out_automatic_correction)); 159 out_automatic_correction));
@@ -166,21 +168,27 @@ Result StaticService::SetStandardUserSystemClockAutomaticCorrectionEnabled(
166} 168}
167 169
168Result StaticService::GetStandardUserSystemClockInitialYear(Out<s32> out_year) { 170Result StaticService::GetStandardUserSystemClockInitialYear(Out<s32> out_year) {
169 SCOPE_EXIT({ LOG_DEBUG(Service_Time, "called. out_year={}", *out_year); }); 171 SCOPE_EXIT {
172 LOG_DEBUG(Service_Time, "called. out_year={}", *out_year);
173 };
170 174
171 R_RETURN(m_set_sys->GetSettingsItemValueImpl<s32>(*out_year, "time", 175 R_RETURN(m_set_sys->GetSettingsItemValueImpl<s32>(*out_year, "time",
172 "standard_user_clock_initial_year")); 176 "standard_user_clock_initial_year"));
173} 177}
174 178
175Result StaticService::IsStandardNetworkSystemClockAccuracySufficient(Out<bool> out_is_sufficient) { 179Result StaticService::IsStandardNetworkSystemClockAccuracySufficient(Out<bool> out_is_sufficient) {
176 SCOPE_EXIT({ LOG_DEBUG(Service_Time, "called. out_is_sufficient={}", *out_is_sufficient); }); 180 SCOPE_EXIT {
181 LOG_DEBUG(Service_Time, "called. out_is_sufficient={}", *out_is_sufficient);
182 };
177 183
178 R_RETURN(m_wrapped_service->IsStandardNetworkSystemClockAccuracySufficient(out_is_sufficient)); 184 R_RETURN(m_wrapped_service->IsStandardNetworkSystemClockAccuracySufficient(out_is_sufficient));
179} 185}
180 186
181Result StaticService::GetStandardUserSystemClockAutomaticCorrectionUpdatedTime( 187Result StaticService::GetStandardUserSystemClockAutomaticCorrectionUpdatedTime(
182 Out<Service::PSC::Time::SteadyClockTimePoint> out_time_point) { 188 Out<Service::PSC::Time::SteadyClockTimePoint> out_time_point) {
183 SCOPE_EXIT({ LOG_DEBUG(Service_Time, "called. out_time_point={}", *out_time_point); }); 189 SCOPE_EXIT {
190 LOG_DEBUG(Service_Time, "called. out_time_point={}", *out_time_point);
191 };
184 192
185 R_RETURN(m_wrapped_service->GetStandardUserSystemClockAutomaticCorrectionUpdatedTime( 193 R_RETURN(m_wrapped_service->GetStandardUserSystemClockAutomaticCorrectionUpdatedTime(
186 out_time_point)); 194 out_time_point));
@@ -188,15 +196,18 @@ Result StaticService::GetStandardUserSystemClockAutomaticCorrectionUpdatedTime(
188 196
189Result StaticService::CalculateMonotonicSystemClockBaseTimePoint( 197Result StaticService::CalculateMonotonicSystemClockBaseTimePoint(
190 Out<s64> out_time, const Service::PSC::Time::SystemClockContext& context) { 198 Out<s64> out_time, const Service::PSC::Time::SystemClockContext& context) {
191 SCOPE_EXIT({ LOG_DEBUG(Service_Time, "called. context={} out_time={}", context, *out_time); }); 199 SCOPE_EXIT {
200 LOG_DEBUG(Service_Time, "called. context={} out_time={}", context, *out_time);
201 };
192 202
193 R_RETURN(m_wrapped_service->CalculateMonotonicSystemClockBaseTimePoint(out_time, context)); 203 R_RETURN(m_wrapped_service->CalculateMonotonicSystemClockBaseTimePoint(out_time, context));
194} 204}
195 205
196Result StaticService::GetClockSnapshot(OutClockSnapshot out_snapshot, 206Result StaticService::GetClockSnapshot(OutClockSnapshot out_snapshot,
197 Service::PSC::Time::TimeType type) { 207 Service::PSC::Time::TimeType type) {
198 SCOPE_EXIT( 208 SCOPE_EXIT {
199 { LOG_DEBUG(Service_Time, "called. type={} out_snapshot={}", type, *out_snapshot); }); 209 LOG_DEBUG(Service_Time, "called. type={} out_snapshot={}", type, *out_snapshot);
210 };
200 211
201 R_RETURN(m_wrapped_service->GetClockSnapshot(out_snapshot, type)); 212 R_RETURN(m_wrapped_service->GetClockSnapshot(out_snapshot, type));
202} 213}
@@ -205,11 +216,11 @@ Result StaticService::GetClockSnapshotFromSystemClockContext(
205 Service::PSC::Time::TimeType type, OutClockSnapshot out_snapshot, 216 Service::PSC::Time::TimeType type, OutClockSnapshot out_snapshot,
206 const Service::PSC::Time::SystemClockContext& user_context, 217 const Service::PSC::Time::SystemClockContext& user_context,
207 const Service::PSC::Time::SystemClockContext& network_context) { 218 const Service::PSC::Time::SystemClockContext& network_context) {
208 SCOPE_EXIT({ 219 SCOPE_EXIT {
209 LOG_DEBUG(Service_Time, 220 LOG_DEBUG(Service_Time,
210 "called. type={} out_snapshot={} user_context={} network_context={}", type, 221 "called. type={} out_snapshot={} user_context={} network_context={}", type,
211 *out_snapshot, user_context, network_context); 222 *out_snapshot, user_context, network_context);
212 }); 223 };
213 224
214 R_RETURN(m_wrapped_service->GetClockSnapshotFromSystemClockContext( 225 R_RETURN(m_wrapped_service->GetClockSnapshotFromSystemClockContext(
215 type, out_snapshot, user_context, network_context)); 226 type, out_snapshot, user_context, network_context));
@@ -218,14 +229,18 @@ Result StaticService::GetClockSnapshotFromSystemClockContext(
218Result StaticService::CalculateStandardUserSystemClockDifferenceByUser(Out<s64> out_time, 229Result StaticService::CalculateStandardUserSystemClockDifferenceByUser(Out<s64> out_time,
219 InClockSnapshot a, 230 InClockSnapshot a,
220 InClockSnapshot b) { 231 InClockSnapshot b) {
221 SCOPE_EXIT({ LOG_DEBUG(Service_Time, "called. a={} b={} out_time={}", *a, *b, *out_time); }); 232 SCOPE_EXIT {
233 LOG_DEBUG(Service_Time, "called. a={} b={} out_time={}", *a, *b, *out_time);
234 };
222 235
223 R_RETURN(m_wrapped_service->CalculateStandardUserSystemClockDifferenceByUser(out_time, a, b)); 236 R_RETURN(m_wrapped_service->CalculateStandardUserSystemClockDifferenceByUser(out_time, a, b));
224} 237}
225 238
226Result StaticService::CalculateSpanBetween(Out<s64> out_time, InClockSnapshot a, 239Result StaticService::CalculateSpanBetween(Out<s64> out_time, InClockSnapshot a,
227 InClockSnapshot b) { 240 InClockSnapshot b) {
228 SCOPE_EXIT({ LOG_DEBUG(Service_Time, "called. a={} b={} out_time={}", *a, *b, *out_time); }); 241 SCOPE_EXIT {
242 LOG_DEBUG(Service_Time, "called. a={} b={} out_time={}", *a, *b, *out_time);
243 };
229 244
230 R_RETURN(m_wrapped_service->CalculateSpanBetween(out_time, a, b)); 245 R_RETURN(m_wrapped_service->CalculateSpanBetween(out_time, a, b));
231} 246}
diff --git a/src/core/hle/service/glue/time/time_zone.cpp b/src/core/hle/service/glue/time/time_zone.cpp
index 36f163419..f4d0c87d5 100644
--- a/src/core/hle/service/glue/time/time_zone.cpp
+++ b/src/core/hle/service/glue/time/time_zone.cpp
@@ -57,7 +57,9 @@ TimeZoneService::~TimeZoneService() = default;
57 57
58Result TimeZoneService::GetDeviceLocationName( 58Result TimeZoneService::GetDeviceLocationName(
59 Out<Service::PSC::Time::LocationName> out_location_name) { 59 Out<Service::PSC::Time::LocationName> out_location_name) {
60 SCOPE_EXIT({ LOG_DEBUG(Service_Time, "called. out_location_name={}", *out_location_name); }); 60 SCOPE_EXIT {
61 LOG_DEBUG(Service_Time, "called. out_location_name={}", *out_location_name);
62 };
61 63
62 R_RETURN(m_wrapped_service->GetDeviceLocationName(out_location_name)); 64 R_RETURN(m_wrapped_service->GetDeviceLocationName(out_location_name));
63} 65}
@@ -94,7 +96,9 @@ Result TimeZoneService::SetDeviceLocationName(
94} 96}
95 97
96Result TimeZoneService::GetTotalLocationNameCount(Out<u32> out_count) { 98Result TimeZoneService::GetTotalLocationNameCount(Out<u32> out_count) {
97 SCOPE_EXIT({ LOG_DEBUG(Service_Time, "called. out_count={}", *out_count); }); 99 SCOPE_EXIT {
100 LOG_DEBUG(Service_Time, "called. out_count={}", *out_count);
101 };
98 102
99 R_RETURN(m_wrapped_service->GetTotalLocationNameCount(out_count)); 103 R_RETURN(m_wrapped_service->GetTotalLocationNameCount(out_count));
100} 104}
@@ -102,10 +106,10 @@ Result TimeZoneService::GetTotalLocationNameCount(Out<u32> out_count) {
102Result TimeZoneService::LoadLocationNameList( 106Result TimeZoneService::LoadLocationNameList(
103 Out<u32> out_count, 107 Out<u32> out_count,
104 OutArray<Service::PSC::Time::LocationName, BufferAttr_HipcMapAlias> out_names, u32 index) { 108 OutArray<Service::PSC::Time::LocationName, BufferAttr_HipcMapAlias> out_names, u32 index) {
105 SCOPE_EXIT({ 109 SCOPE_EXIT {
106 LOG_DEBUG(Service_Time, "called. index={} out_count={} out_names[0]={} out_names[1]={}", 110 LOG_DEBUG(Service_Time, "called. index={} out_count={} out_names[0]={} out_names[1]={}",
107 index, *out_count, out_names[0], out_names[1]); 111 index, *out_count, out_names[0], out_names[1]);
108 }); 112 };
109 113
110 std::scoped_lock l{m_mutex}; 114 std::scoped_lock l{m_mutex};
111 R_RETURN(GetTimeZoneLocationList(*out_count, out_names, out_names.size(), index)); 115 R_RETURN(GetTimeZoneLocationList(*out_count, out_names, out_names.size(), index));
@@ -124,7 +128,9 @@ Result TimeZoneService::LoadTimeZoneRule(OutRule out_rule,
124 128
125Result TimeZoneService::GetTimeZoneRuleVersion( 129Result TimeZoneService::GetTimeZoneRuleVersion(
126 Out<Service::PSC::Time::RuleVersion> out_rule_version) { 130 Out<Service::PSC::Time::RuleVersion> out_rule_version) {
127 SCOPE_EXIT({ LOG_DEBUG(Service_Time, "called. out_rule_version={}", *out_rule_version); }); 131 SCOPE_EXIT {
132 LOG_DEBUG(Service_Time, "called. out_rule_version={}", *out_rule_version);
133 };
128 134
129 R_RETURN(m_wrapped_service->GetTimeZoneRuleVersion(out_rule_version)); 135 R_RETURN(m_wrapped_service->GetTimeZoneRuleVersion(out_rule_version));
130} 136}
@@ -132,10 +138,10 @@ Result TimeZoneService::GetTimeZoneRuleVersion(
132Result TimeZoneService::GetDeviceLocationNameAndUpdatedTime( 138Result TimeZoneService::GetDeviceLocationNameAndUpdatedTime(
133 Out<Service::PSC::Time::LocationName> location_name, 139 Out<Service::PSC::Time::LocationName> location_name,
134 Out<Service::PSC::Time::SteadyClockTimePoint> out_time_point) { 140 Out<Service::PSC::Time::SteadyClockTimePoint> out_time_point) {
135 SCOPE_EXIT({ 141 SCOPE_EXIT {
136 LOG_DEBUG(Service_Time, "called. location_name={} out_time_point={}", *location_name, 142 LOG_DEBUG(Service_Time, "called. location_name={} out_time_point={}", *location_name,
137 *out_time_point); 143 *out_time_point);
138 }); 144 };
139 145
140 R_RETURN(m_wrapped_service->GetDeviceLocationNameAndUpdatedTime(location_name, out_time_point)); 146 R_RETURN(m_wrapped_service->GetDeviceLocationNameAndUpdatedTime(location_name, out_time_point));
141} 147}
@@ -178,10 +184,10 @@ Result TimeZoneService::GetDeviceLocationNameOperationEventReadableHandle(
178Result TimeZoneService::ToCalendarTime( 184Result TimeZoneService::ToCalendarTime(
179 Out<Service::PSC::Time::CalendarTime> out_calendar_time, 185 Out<Service::PSC::Time::CalendarTime> out_calendar_time,
180 Out<Service::PSC::Time::CalendarAdditionalInfo> out_additional_info, s64 time, InRule rule) { 186 Out<Service::PSC::Time::CalendarAdditionalInfo> out_additional_info, s64 time, InRule rule) {
181 SCOPE_EXIT({ 187 SCOPE_EXIT {
182 LOG_DEBUG(Service_Time, "called. time={} out_calendar_time={} out_additional_info={}", time, 188 LOG_DEBUG(Service_Time, "called. time={} out_calendar_time={} out_additional_info={}", time,
183 *out_calendar_time, *out_additional_info); 189 *out_calendar_time, *out_additional_info);
184 }); 190 };
185 191
186 R_RETURN(m_wrapped_service->ToCalendarTime(out_calendar_time, out_additional_info, time, rule)); 192 R_RETURN(m_wrapped_service->ToCalendarTime(out_calendar_time, out_additional_info, time, rule));
187} 193}
@@ -189,10 +195,10 @@ Result TimeZoneService::ToCalendarTime(
189Result TimeZoneService::ToCalendarTimeWithMyRule( 195Result TimeZoneService::ToCalendarTimeWithMyRule(
190 Out<Service::PSC::Time::CalendarTime> out_calendar_time, 196 Out<Service::PSC::Time::CalendarTime> out_calendar_time,
191 Out<Service::PSC::Time::CalendarAdditionalInfo> out_additional_info, s64 time) { 197 Out<Service::PSC::Time::CalendarAdditionalInfo> out_additional_info, s64 time) {
192 SCOPE_EXIT({ 198 SCOPE_EXIT {
193 LOG_DEBUG(Service_Time, "called. time={} out_calendar_time={} out_additional_info={}", time, 199 LOG_DEBUG(Service_Time, "called. time={} out_calendar_time={} out_additional_info={}", time,
194 *out_calendar_time, *out_additional_info); 200 *out_calendar_time, *out_additional_info);
195 }); 201 };
196 202
197 R_RETURN( 203 R_RETURN(
198 m_wrapped_service->ToCalendarTimeWithMyRule(out_calendar_time, out_additional_info, time)); 204 m_wrapped_service->ToCalendarTimeWithMyRule(out_calendar_time, out_additional_info, time));
@@ -202,11 +208,11 @@ Result TimeZoneService::ToPosixTime(Out<u32> out_count,
202 OutArray<s64, BufferAttr_HipcPointer> out_times, 208 OutArray<s64, BufferAttr_HipcPointer> out_times,
203 const Service::PSC::Time::CalendarTime& calendar_time, 209 const Service::PSC::Time::CalendarTime& calendar_time,
204 InRule rule) { 210 InRule rule) {
205 SCOPE_EXIT({ 211 SCOPE_EXIT {
206 LOG_DEBUG(Service_Time, 212 LOG_DEBUG(Service_Time,
207 "called. calendar_time={} out_count={} out_times[0]={} out_times[1]={}", 213 "called. calendar_time={} out_count={} out_times[0]={} out_times[1]={}",
208 calendar_time, *out_count, out_times[0], out_times[1]); 214 calendar_time, *out_count, out_times[0], out_times[1]);
209 }); 215 };
210 216
211 R_RETURN(m_wrapped_service->ToPosixTime(out_count, out_times, calendar_time, rule)); 217 R_RETURN(m_wrapped_service->ToPosixTime(out_count, out_times, calendar_time, rule));
212} 218}
@@ -214,11 +220,11 @@ Result TimeZoneService::ToPosixTime(Out<u32> out_count,
214Result TimeZoneService::ToPosixTimeWithMyRule( 220Result TimeZoneService::ToPosixTimeWithMyRule(
215 Out<u32> out_count, OutArray<s64, BufferAttr_HipcPointer> out_times, 221 Out<u32> out_count, OutArray<s64, BufferAttr_HipcPointer> out_times,
216 const Service::PSC::Time::CalendarTime& calendar_time) { 222 const Service::PSC::Time::CalendarTime& calendar_time) {
217 SCOPE_EXIT({ 223 SCOPE_EXIT {
218 LOG_DEBUG(Service_Time, 224 LOG_DEBUG(Service_Time,
219 "called. calendar_time={} out_count={} out_times[0]={} out_times[1]={}", 225 "called. calendar_time={} out_count={} out_times[0]={} out_times[1]={}",
220 calendar_time, *out_count, out_times[0], out_times[1]); 226 calendar_time, *out_count, out_times[0], out_times[1]);
221 }); 227 };
222 228
223 R_RETURN(m_wrapped_service->ToPosixTimeWithMyRule(out_count, out_times, calendar_time)); 229 R_RETURN(m_wrapped_service->ToPosixTimeWithMyRule(out_count, out_times, calendar_time));
224} 230}
diff --git a/src/core/hle/service/ns/account_proxy_interface.cpp b/src/core/hle/service/ns/account_proxy_interface.cpp
new file mode 100644
index 000000000..e5041af66
--- /dev/null
+++ b/src/core/hle/service/ns/account_proxy_interface.cpp
@@ -0,0 +1,21 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/hle/service/ns/account_proxy_interface.h"
5
6namespace Service::NS {
7
8IAccountProxyInterface::IAccountProxyInterface(Core::System& system_)
9 : ServiceFramework{system_, "IAccountProxyInterface"} {
10 // clang-format off
11 static const FunctionInfo functions[] = {
12 {0, nullptr, "CreateUserAccount"},
13 };
14 // clang-format on
15
16 RegisterHandlers(functions);
17}
18
19IAccountProxyInterface::~IAccountProxyInterface() = default;
20
21} // namespace Service::NS
diff --git a/src/core/hle/service/ns/account_proxy_interface.h b/src/core/hle/service/ns/account_proxy_interface.h
new file mode 100644
index 000000000..e944d2a75
--- /dev/null
+++ b/src/core/hle/service/ns/account_proxy_interface.h
@@ -0,0 +1,16 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "core/hle/service/service.h"
7
8namespace Service::NS {
9
10class IAccountProxyInterface final : public ServiceFramework<IAccountProxyInterface> {
11public:
12 explicit IAccountProxyInterface(Core::System& system_);
13 ~IAccountProxyInterface() override;
14};
15
16} // namespace Service::NS
diff --git a/src/core/hle/service/ns/application_manager_interface.cpp b/src/core/hle/service/ns/application_manager_interface.cpp
new file mode 100644
index 000000000..2e3a44c0d
--- /dev/null
+++ b/src/core/hle/service/ns/application_manager_interface.cpp
@@ -0,0 +1,519 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/file_sys/nca_metadata.h"
5#include "core/file_sys/registered_cache.h"
6#include "core/hle/service/cmif_serialization.h"
7#include "core/hle/service/filesystem/filesystem.h"
8#include "core/hle/service/ns/application_manager_interface.h"
9#include "core/hle/service/ns/content_management_interface.h"
10#include "core/hle/service/ns/read_only_application_control_data_interface.h"
11
12namespace Service::NS {
13
14IApplicationManagerInterface::IApplicationManagerInterface(Core::System& system_)
15 : ServiceFramework{system_, "IApplicationManagerInterface"},
16 service_context{system, "IApplicationManagerInterface"},
17 record_update_system_event{service_context}, sd_card_mount_status_event{service_context},
18 gamecard_update_detection_event{service_context},
19 gamecard_mount_status_event{service_context}, gamecard_mount_failure_event{service_context} {
20 // clang-format off
21 static const FunctionInfo functions[] = {
22 {0, D<&IApplicationManagerInterface::ListApplicationRecord>, "ListApplicationRecord"},
23 {1, nullptr, "GenerateApplicationRecordCount"},
24 {2, D<&IApplicationManagerInterface::GetApplicationRecordUpdateSystemEvent>, "GetApplicationRecordUpdateSystemEvent"},
25 {3, nullptr, "GetApplicationViewDeprecated"},
26 {4, nullptr, "DeleteApplicationEntity"},
27 {5, nullptr, "DeleteApplicationCompletely"},
28 {6, nullptr, "IsAnyApplicationEntityRedundant"},
29 {7, nullptr, "DeleteRedundantApplicationEntity"},
30 {8, nullptr, "IsApplicationEntityMovable"},
31 {9, nullptr, "MoveApplicationEntity"},
32 {11, nullptr, "CalculateApplicationOccupiedSize"},
33 {16, nullptr, "PushApplicationRecord"},
34 {17, nullptr, "ListApplicationRecordContentMeta"},
35 {19, nullptr, "LaunchApplicationOld"},
36 {21, nullptr, "GetApplicationContentPath"},
37 {22, nullptr, "TerminateApplication"},
38 {23, nullptr, "ResolveApplicationContentPath"},
39 {26, nullptr, "BeginInstallApplication"},
40 {27, nullptr, "DeleteApplicationRecord"},
41 {30, nullptr, "RequestApplicationUpdateInfo"},
42 {31, nullptr, "Unknown31"},
43 {32, nullptr, "CancelApplicationDownload"},
44 {33, nullptr, "ResumeApplicationDownload"},
45 {35, nullptr, "UpdateVersionList"},
46 {36, nullptr, "PushLaunchVersion"},
47 {37, nullptr, "ListRequiredVersion"},
48 {38, D<&IApplicationManagerInterface::CheckApplicationLaunchVersion>, "CheckApplicationLaunchVersion"},
49 {39, nullptr, "CheckApplicationLaunchRights"},
50 {40, nullptr, "GetApplicationLogoData"},
51 {41, nullptr, "CalculateApplicationDownloadRequiredSize"},
52 {42, nullptr, "CleanupSdCard"},
53 {43, D<&IApplicationManagerInterface::CheckSdCardMountStatus>, "CheckSdCardMountStatus"},
54 {44, D<&IApplicationManagerInterface::GetSdCardMountStatusChangedEvent>, "GetSdCardMountStatusChangedEvent"},
55 {45, nullptr, "GetGameCardAttachmentEvent"},
56 {46, nullptr, "GetGameCardAttachmentInfo"},
57 {47, nullptr, "GetTotalSpaceSize"},
58 {48, D<&IApplicationManagerInterface::GetFreeSpaceSize>, "GetFreeSpaceSize"},
59 {49, nullptr, "GetSdCardRemovedEvent"},
60 {52, D<&IApplicationManagerInterface::GetGameCardUpdateDetectionEvent>, "GetGameCardUpdateDetectionEvent"},
61 {53, nullptr, "DisableApplicationAutoDelete"},
62 {54, nullptr, "EnableApplicationAutoDelete"},
63 {55, D<&IApplicationManagerInterface::GetApplicationDesiredLanguage>, "GetApplicationDesiredLanguage"},
64 {56, nullptr, "SetApplicationTerminateResult"},
65 {57, nullptr, "ClearApplicationTerminateResult"},
66 {58, nullptr, "GetLastSdCardMountUnexpectedResult"},
67 {59, D<&IApplicationManagerInterface::ConvertApplicationLanguageToLanguageCode>, "ConvertApplicationLanguageToLanguageCode"},
68 {60, nullptr, "ConvertLanguageCodeToApplicationLanguage"},
69 {61, nullptr, "GetBackgroundDownloadStressTaskInfo"},
70 {62, nullptr, "GetGameCardStopper"},
71 {63, nullptr, "IsSystemProgramInstalled"},
72 {64, nullptr, "StartApplyDeltaTask"},
73 {65, nullptr, "GetRequestServerStopper"},
74 {66, nullptr, "GetBackgroundApplyDeltaStressTaskInfo"},
75 {67, nullptr, "CancelApplicationApplyDelta"},
76 {68, nullptr, "ResumeApplicationApplyDelta"},
77 {69, nullptr, "CalculateApplicationApplyDeltaRequiredSize"},
78 {70, D<&IApplicationManagerInterface::ResumeAll>, "ResumeAll"},
79 {71, D<&IApplicationManagerInterface::GetStorageSize>, "GetStorageSize"},
80 {80, nullptr, "RequestDownloadApplication"},
81 {81, nullptr, "RequestDownloadAddOnContent"},
82 {82, nullptr, "DownloadApplication"},
83 {83, nullptr, "CheckApplicationResumeRights"},
84 {84, nullptr, "GetDynamicCommitEvent"},
85 {85, nullptr, "RequestUpdateApplication2"},
86 {86, nullptr, "EnableApplicationCrashReport"},
87 {87, nullptr, "IsApplicationCrashReportEnabled"},
88 {90, nullptr, "BoostSystemMemoryResourceLimit"},
89 {91, nullptr, "DeprecatedLaunchApplication"},
90 {92, nullptr, "GetRunningApplicationProgramId"},
91 {93, nullptr, "GetMainApplicationProgramIndex"},
92 {94, nullptr, "LaunchApplication"},
93 {95, nullptr, "GetApplicationLaunchInfo"},
94 {96, nullptr, "AcquireApplicationLaunchInfo"},
95 {97, nullptr, "GetMainApplicationProgramIndexByApplicationLaunchInfo"},
96 {98, nullptr, "EnableApplicationAllThreadDumpOnCrash"},
97 {99, nullptr, "LaunchDevMenu"},
98 {100, nullptr, "ResetToFactorySettings"},
99 {101, nullptr, "ResetToFactorySettingsWithoutUserSaveData"},
100 {102, nullptr, "ResetToFactorySettingsForRefurbishment"},
101 {103, nullptr, "ResetToFactorySettingsWithPlatformRegion"},
102 {104, nullptr, "ResetToFactorySettingsWithPlatformRegionAuthentication"},
103 {105, nullptr, "RequestResetToFactorySettingsSecurely"},
104 {106, nullptr, "RequestResetToFactorySettingsWithPlatformRegionAuthenticationSecurely"},
105 {200, nullptr, "CalculateUserSaveDataStatistics"},
106 {201, nullptr, "DeleteUserSaveDataAll"},
107 {210, nullptr, "DeleteUserSystemSaveData"},
108 {211, nullptr, "DeleteSaveData"},
109 {220, nullptr, "UnregisterNetworkServiceAccount"},
110 {221, nullptr, "UnregisterNetworkServiceAccountWithUserSaveDataDeletion"},
111 {300, nullptr, "GetApplicationShellEvent"},
112 {301, nullptr, "PopApplicationShellEventInfo"},
113 {302, nullptr, "LaunchLibraryApplet"},
114 {303, nullptr, "TerminateLibraryApplet"},
115 {304, nullptr, "LaunchSystemApplet"},
116 {305, nullptr, "TerminateSystemApplet"},
117 {306, nullptr, "LaunchOverlayApplet"},
118 {307, nullptr, "TerminateOverlayApplet"},
119 {400, D<&IApplicationManagerInterface::GetApplicationControlData>, "GetApplicationControlData"},
120 {401, nullptr, "InvalidateAllApplicationControlCache"},
121 {402, nullptr, "RequestDownloadApplicationControlData"},
122 {403, nullptr, "GetMaxApplicationControlCacheCount"},
123 {404, nullptr, "InvalidateApplicationControlCache"},
124 {405, nullptr, "ListApplicationControlCacheEntryInfo"},
125 {406, nullptr, "GetApplicationControlProperty"},
126 {407, nullptr, "ListApplicationTitle"},
127 {408, nullptr, "ListApplicationIcon"},
128 {502, nullptr, "RequestCheckGameCardRegistration"},
129 {503, nullptr, "RequestGameCardRegistrationGoldPoint"},
130 {504, nullptr, "RequestRegisterGameCard"},
131 {505, D<&IApplicationManagerInterface::GetGameCardMountFailureEvent>, "GetGameCardMountFailureEvent"},
132 {506, nullptr, "IsGameCardInserted"},
133 {507, nullptr, "EnsureGameCardAccess"},
134 {508, nullptr, "GetLastGameCardMountFailureResult"},
135 {509, nullptr, "ListApplicationIdOnGameCard"},
136 {510, nullptr, "GetGameCardPlatformRegion"},
137 {600, nullptr, "CountApplicationContentMeta"},
138 {601, nullptr, "ListApplicationContentMetaStatus"},
139 {602, nullptr, "ListAvailableAddOnContent"},
140 {603, nullptr, "GetOwnedApplicationContentMetaStatus"},
141 {604, nullptr, "RegisterContentsExternalKey"},
142 {605, nullptr, "ListApplicationContentMetaStatusWithRightsCheck"},
143 {606, nullptr, "GetContentMetaStorage"},
144 {607, nullptr, "ListAvailableAddOnContent"},
145 {609, nullptr, "ListAvailabilityAssuredAddOnContent"},
146 {610, nullptr, "GetInstalledContentMetaStorage"},
147 {611, nullptr, "PrepareAddOnContent"},
148 {700, nullptr, "PushDownloadTaskList"},
149 {701, nullptr, "ClearTaskStatusList"},
150 {702, nullptr, "RequestDownloadTaskList"},
151 {703, nullptr, "RequestEnsureDownloadTask"},
152 {704, nullptr, "ListDownloadTaskStatus"},
153 {705, nullptr, "RequestDownloadTaskListData"},
154 {800, nullptr, "RequestVersionList"},
155 {801, nullptr, "ListVersionList"},
156 {802, nullptr, "RequestVersionListData"},
157 {900, nullptr, "GetApplicationRecord"},
158 {901, nullptr, "GetApplicationRecordProperty"},
159 {902, nullptr, "EnableApplicationAutoUpdate"},
160 {903, nullptr, "DisableApplicationAutoUpdate"},
161 {904, nullptr, "TouchApplication"},
162 {905, nullptr, "RequestApplicationUpdate"},
163 {906, D<&IApplicationManagerInterface::IsApplicationUpdateRequested>, "IsApplicationUpdateRequested"},
164 {907, nullptr, "WithdrawApplicationUpdateRequest"},
165 {908, nullptr, "ListApplicationRecordInstalledContentMeta"},
166 {909, nullptr, "WithdrawCleanupAddOnContentsWithNoRightsRecommendation"},
167 {910, nullptr, "HasApplicationRecord"},
168 {911, nullptr, "SetPreInstalledApplication"},
169 {912, nullptr, "ClearPreInstalledApplicationFlag"},
170 {913, nullptr, "ListAllApplicationRecord"},
171 {914, nullptr, "HideApplicationRecord"},
172 {915, nullptr, "ShowApplicationRecord"},
173 {916, nullptr, "IsApplicationAutoDeleteDisabled"},
174 {1000, nullptr, "RequestVerifyApplicationDeprecated"},
175 {1001, nullptr, "CorruptApplicationForDebug"},
176 {1002, nullptr, "RequestVerifyAddOnContentsRights"},
177 {1003, nullptr, "RequestVerifyApplication"},
178 {1004, nullptr, "CorruptContentForDebug"},
179 {1200, nullptr, "NeedsUpdateVulnerability"},
180 {1300, D<&IApplicationManagerInterface::IsAnyApplicationEntityInstalled>, "IsAnyApplicationEntityInstalled"},
181 {1301, nullptr, "DeleteApplicationContentEntities"},
182 {1302, nullptr, "CleanupUnrecordedApplicationEntity"},
183 {1303, nullptr, "CleanupAddOnContentsWithNoRights"},
184 {1304, nullptr, "DeleteApplicationContentEntity"},
185 {1305, nullptr, "TryDeleteRunningApplicationEntity"},
186 {1306, nullptr, "TryDeleteRunningApplicationCompletely"},
187 {1307, nullptr, "TryDeleteRunningApplicationContentEntities"},
188 {1308, nullptr, "DeleteApplicationCompletelyForDebug"},
189 {1309, nullptr, "CleanupUnavailableAddOnContents"},
190 {1310, nullptr, "RequestMoveApplicationEntity"},
191 {1311, nullptr, "EstimateSizeToMove"},
192 {1312, nullptr, "HasMovableEntity"},
193 {1313, nullptr, "CleanupOrphanContents"},
194 {1314, nullptr, "CheckPreconditionSatisfiedToMove"},
195 {1400, nullptr, "PrepareShutdown"},
196 {1500, nullptr, "FormatSdCard"},
197 {1501, nullptr, "NeedsSystemUpdateToFormatSdCard"},
198 {1502, nullptr, "GetLastSdCardFormatUnexpectedResult"},
199 {1504, nullptr, "InsertSdCard"},
200 {1505, nullptr, "RemoveSdCard"},
201 {1506, nullptr, "GetSdCardStartupStatus"},
202 {1600, nullptr, "GetSystemSeedForPseudoDeviceId"},
203 {1601, nullptr, "ResetSystemSeedForPseudoDeviceId"},
204 {1700, nullptr, "ListApplicationDownloadingContentMeta"},
205 {1701, D<&IApplicationManagerInterface::GetApplicationView>, "GetApplicationView"},
206 {1702, nullptr, "GetApplicationDownloadTaskStatus"},
207 {1703, nullptr, "GetApplicationViewDownloadErrorContext"},
208 {1704, D<&IApplicationManagerInterface::GetApplicationViewWithPromotionInfo>, "GetApplicationViewWithPromotionInfo"},
209 {1705, nullptr, "IsPatchAutoDeletableApplication"},
210 {1800, nullptr, "IsNotificationSetupCompleted"},
211 {1801, nullptr, "GetLastNotificationInfoCount"},
212 {1802, nullptr, "ListLastNotificationInfo"},
213 {1803, nullptr, "ListNotificationTask"},
214 {1900, nullptr, "IsActiveAccount"},
215 {1901, nullptr, "RequestDownloadApplicationPrepurchasedRights"},
216 {1902, nullptr, "GetApplicationTicketInfo"},
217 {1903, nullptr, "RequestDownloadApplicationPrepurchasedRightsForAccount"},
218 {2000, nullptr, "GetSystemDeliveryInfo"},
219 {2001, nullptr, "SelectLatestSystemDeliveryInfo"},
220 {2002, nullptr, "VerifyDeliveryProtocolVersion"},
221 {2003, nullptr, "GetApplicationDeliveryInfo"},
222 {2004, nullptr, "HasAllContentsToDeliver"},
223 {2005, nullptr, "CompareApplicationDeliveryInfo"},
224 {2006, nullptr, "CanDeliverApplication"},
225 {2007, nullptr, "ListContentMetaKeyToDeliverApplication"},
226 {2008, nullptr, "NeedsSystemUpdateToDeliverApplication"},
227 {2009, nullptr, "EstimateRequiredSize"},
228 {2010, nullptr, "RequestReceiveApplication"},
229 {2011, nullptr, "CommitReceiveApplication"},
230 {2012, nullptr, "GetReceiveApplicationProgress"},
231 {2013, nullptr, "RequestSendApplication"},
232 {2014, nullptr, "GetSendApplicationProgress"},
233 {2015, nullptr, "CompareSystemDeliveryInfo"},
234 {2016, nullptr, "ListNotCommittedContentMeta"},
235 {2017, nullptr, "CreateDownloadTask"},
236 {2018, nullptr, "GetApplicationDeliveryInfoHash"},
237 {2050, D<&IApplicationManagerInterface::GetApplicationRightsOnClient>, "GetApplicationRightsOnClient"},
238 {2051, nullptr, "InvalidateRightsIdCache"},
239 {2100, D<&IApplicationManagerInterface::GetApplicationTerminateResult>, "GetApplicationTerminateResult"},
240 {2101, nullptr, "GetRawApplicationTerminateResult"},
241 {2150, nullptr, "CreateRightsEnvironment"},
242 {2151, nullptr, "DestroyRightsEnvironment"},
243 {2152, nullptr, "ActivateRightsEnvironment"},
244 {2153, nullptr, "DeactivateRightsEnvironment"},
245 {2154, nullptr, "ForceActivateRightsContextForExit"},
246 {2155, nullptr, "UpdateRightsEnvironmentStatus"},
247 {2156, nullptr, "CreateRightsEnvironmentForMicroApplication"},
248 {2160, nullptr, "AddTargetApplicationToRightsEnvironment"},
249 {2161, nullptr, "SetUsersToRightsEnvironment"},
250 {2170, nullptr, "GetRightsEnvironmentStatus"},
251 {2171, nullptr, "GetRightsEnvironmentStatusChangedEvent"},
252 {2180, nullptr, "RequestExtendRightsInRightsEnvironment"},
253 {2181, nullptr, "GetResultOfExtendRightsInRightsEnvironment"},
254 {2182, nullptr, "SetActiveRightsContextUsingStateToRightsEnvironment"},
255 {2190, nullptr, "GetRightsEnvironmentHandleForApplication"},
256 {2199, nullptr, "GetRightsEnvironmentCountForDebug"},
257 {2200, nullptr, "GetGameCardApplicationCopyIdentifier"},
258 {2201, nullptr, "GetInstalledApplicationCopyIdentifier"},
259 {2250, nullptr, "RequestReportActiveELicence"},
260 {2300, nullptr, "ListEventLog"},
261 {2350, nullptr, "PerformAutoUpdateByApplicationId"},
262 {2351, nullptr, "RequestNoDownloadRightsErrorResolution"},
263 {2352, nullptr, "RequestResolveNoDownloadRightsError"},
264 {2353, nullptr, "GetApplicationDownloadTaskInfo"},
265 {2354, nullptr, "PrioritizeApplicationBackgroundTask"},
266 {2355, nullptr, "PreferStorageEfficientUpdate"},
267 {2356, nullptr, "RequestStorageEfficientUpdatePreferable"},
268 {2357, nullptr, "EnableMultiCoreDownload"},
269 {2358, nullptr, "DisableMultiCoreDownload"},
270 {2359, nullptr, "IsMultiCoreDownloadEnabled"},
271 {2400, nullptr, "GetPromotionInfo"},
272 {2401, nullptr, "CountPromotionInfo"},
273 {2402, nullptr, "ListPromotionInfo"},
274 {2403, nullptr, "ImportPromotionJsonForDebug"},
275 {2404, nullptr, "ClearPromotionInfoForDebug"},
276 {2500, nullptr, "ConfirmAvailableTime"},
277 {2510, nullptr, "CreateApplicationResource"},
278 {2511, nullptr, "GetApplicationResource"},
279 {2513, nullptr, "LaunchMicroApplication"},
280 {2514, nullptr, "ClearTaskOfAsyncTaskManager"},
281 {2515, nullptr, "CleanupAllPlaceHolderAndFragmentsIfNoTask"},
282 {2516, nullptr, "EnsureApplicationCertificate"},
283 {2517, nullptr, "CreateApplicationInstance"},
284 {2518, nullptr, "UpdateQualificationForDebug"},
285 {2519, nullptr, "IsQualificationTransitionSupported"},
286 {2520, nullptr, "IsQualificationTransitionSupportedByProcessId"},
287 {2521, nullptr, "GetRightsUserChangedEvent"},
288 {2522, nullptr, "IsRomRedirectionAvailable"},
289 {2800, nullptr, "GetApplicationIdOfPreomia"},
290 {3000, nullptr, "RegisterDeviceLockKey"},
291 {3001, nullptr, "UnregisterDeviceLockKey"},
292 {3002, nullptr, "VerifyDeviceLockKey"},
293 {3003, nullptr, "HideApplicationIcon"},
294 {3004, nullptr, "ShowApplicationIcon"},
295 {3005, nullptr, "HideApplicationTitle"},
296 {3006, nullptr, "ShowApplicationTitle"},
297 {3007, nullptr, "EnableGameCard"},
298 {3008, nullptr, "DisableGameCard"},
299 {3009, nullptr, "EnableLocalContentShare"},
300 {3010, nullptr, "DisableLocalContentShare"},
301 {3011, nullptr, "IsApplicationIconHidden"},
302 {3012, nullptr, "IsApplicationTitleHidden"},
303 {3013, nullptr, "IsGameCardEnabled"},
304 {3014, nullptr, "IsLocalContentShareEnabled"},
305 {3050, nullptr, "ListAssignELicenseTaskResult"},
306 {9999, nullptr, "GetApplicationCertificate"},
307 };
308 // clang-format on
309
310 RegisterHandlers(functions);
311}
312
313IApplicationManagerInterface::~IApplicationManagerInterface() = default;
314
315Result IApplicationManagerInterface::GetApplicationControlData(
316 OutBuffer<BufferAttr_HipcMapAlias> out_buffer, Out<u32> out_actual_size,
317 ApplicationControlSource application_control_source, u64 application_id) {
318 LOG_DEBUG(Service_NS, "called");
319 R_RETURN(IReadOnlyApplicationControlDataInterface(system).GetApplicationControlData(
320 out_buffer, out_actual_size, application_control_source, application_id));
321}
322
323Result IApplicationManagerInterface::GetApplicationDesiredLanguage(
324 Out<ApplicationLanguage> out_desired_language, u32 supported_languages) {
325 LOG_DEBUG(Service_NS, "called");
326 R_RETURN(IReadOnlyApplicationControlDataInterface(system).GetApplicationDesiredLanguage(
327 out_desired_language, supported_languages));
328}
329
330Result IApplicationManagerInterface::ConvertApplicationLanguageToLanguageCode(
331 Out<u64> out_language_code, ApplicationLanguage application_language) {
332 LOG_DEBUG(Service_NS, "called");
333 R_RETURN(
334 IReadOnlyApplicationControlDataInterface(system).ConvertApplicationLanguageToLanguageCode(
335 out_language_code, application_language));
336}
337
338Result IApplicationManagerInterface::ListApplicationRecord(
339 OutArray<ApplicationRecord, BufferAttr_HipcMapAlias> out_records, Out<s32> out_count,
340 s32 offset) {
341 const auto limit = out_records.size();
342
343 LOG_WARNING(Service_NS, "(STUBBED) called");
344 const auto& cache = system.GetContentProviderUnion();
345 const auto installed_games = cache.ListEntriesFilterOrigin(
346 std::nullopt, FileSys::TitleType::Application, FileSys::ContentRecordType::Program);
347
348 size_t i = 0;
349 u8 ii = 24;
350
351 for (const auto& [slot, game] : installed_games) {
352 if (i >= limit) {
353 break;
354 }
355 if (game.title_id == 0 || game.title_id < 0x0100000000001FFFull) {
356 continue;
357 }
358 if (offset > 0) {
359 offset--;
360 continue;
361 }
362
363 ApplicationRecord record{};
364 record.application_id = game.title_id;
365 record.type = ApplicationRecordType::Installed;
366 record.unknown = 0; // 2 = needs update
367 record.unknown2 = ii++;
368
369 out_records[i++] = record;
370 }
371
372 *out_count = static_cast<s32>(i);
373 R_SUCCEED();
374}
375
376Result IApplicationManagerInterface::GetApplicationRecordUpdateSystemEvent(
377 OutCopyHandle<Kernel::KReadableEvent> out_event) {
378 LOG_WARNING(Service_NS, "(STUBBED) called");
379
380 record_update_system_event.Signal();
381 *out_event = record_update_system_event.GetHandle();
382
383 R_SUCCEED();
384}
385
386Result IApplicationManagerInterface::GetGameCardMountFailureEvent(
387 OutCopyHandle<Kernel::KReadableEvent> out_event) {
388 LOG_WARNING(Service_NS, "(STUBBED) called");
389 *out_event = gamecard_mount_failure_event.GetHandle();
390 R_SUCCEED();
391}
392
393Result IApplicationManagerInterface::IsAnyApplicationEntityInstalled(
394 Out<bool> out_is_any_application_entity_installed) {
395 LOG_WARNING(Service_NS, "(STUBBED) called");
396 *out_is_any_application_entity_installed = true;
397 R_SUCCEED();
398}
399
400Result IApplicationManagerInterface::GetApplicationView(
401 OutArray<ApplicationView, BufferAttr_HipcMapAlias> out_application_views,
402 InArray<u64, BufferAttr_HipcMapAlias> application_ids) {
403 const auto size = std::min(out_application_views.size(), application_ids.size());
404 LOG_WARNING(Service_NS, "(STUBBED) called, size={}", application_ids.size());
405
406 for (size_t i = 0; i < size; i++) {
407 ApplicationView view{};
408 view.application_id = application_ids[i];
409 view.unk = 0x70000;
410 view.flags = 0x401f17;
411
412 out_application_views[i] = view;
413 }
414
415 R_SUCCEED();
416}
417
418Result IApplicationManagerInterface::GetApplicationViewWithPromotionInfo(
419 OutArray<ApplicationViewWithPromotionInfo, BufferAttr_HipcMapAlias> out_application_views,
420 InArray<u64, BufferAttr_HipcMapAlias> application_ids) {
421 const auto size = std::min(out_application_views.size(), application_ids.size());
422 LOG_WARNING(Service_NS, "(STUBBED) called, size={}", application_ids.size());
423
424 for (size_t i = 0; i < size; i++) {
425 ApplicationViewWithPromotionInfo view{};
426 view.view.application_id = application_ids[i];
427 view.view.unk = 0x70000;
428 view.view.flags = 0x401f17;
429 view.promotion = {};
430
431 out_application_views[i] = view;
432 }
433
434 R_SUCCEED();
435}
436
437Result IApplicationManagerInterface::GetApplicationRightsOnClient(
438 OutArray<ApplicationRightsOnClient, BufferAttr_HipcMapAlias> out_rights, Out<u32> out_count,
439 Common::UUID account_id, u32 flags, u64 application_id) {
440 LOG_WARNING(Service_NS, "(STUBBED) called, flags={}, application_id={:016X}, account_id={}",
441 flags, application_id, account_id.FormattedString());
442
443 if (!out_rights.empty()) {
444 ApplicationRightsOnClient rights{};
445 rights.application_id = application_id;
446 rights.uid = account_id;
447 rights.flags = 0;
448 rights.flags2 = 0;
449
450 out_rights[0] = rights;
451 *out_count = 1;
452 } else {
453 *out_count = 0;
454 }
455
456 R_SUCCEED();
457}
458
459Result IApplicationManagerInterface::CheckSdCardMountStatus() {
460 LOG_DEBUG(Service_NS, "called");
461 R_RETURN(IContentManagementInterface(system).CheckSdCardMountStatus());
462}
463
464Result IApplicationManagerInterface::GetSdCardMountStatusChangedEvent(
465 OutCopyHandle<Kernel::KReadableEvent> out_event) {
466 LOG_WARNING(Service_NS, "(STUBBED) called");
467 *out_event = sd_card_mount_status_event.GetHandle();
468 R_SUCCEED();
469}
470
471Result IApplicationManagerInterface::GetFreeSpaceSize(Out<s64> out_free_space_size,
472 FileSys::StorageId storage_id) {
473 LOG_DEBUG(Service_NS, "called");
474 R_RETURN(IContentManagementInterface(system).GetFreeSpaceSize(out_free_space_size, storage_id));
475}
476
477Result IApplicationManagerInterface::GetGameCardUpdateDetectionEvent(
478 OutCopyHandle<Kernel::KReadableEvent> out_event) {
479 LOG_WARNING(Service_NS, "(STUBBED) called");
480 *out_event = gamecard_update_detection_event.GetHandle();
481 R_SUCCEED();
482}
483
484Result IApplicationManagerInterface::ResumeAll() {
485 LOG_WARNING(Service_NS, "(STUBBED) called");
486 R_SUCCEED();
487}
488
489Result IApplicationManagerInterface::GetStorageSize(Out<s64> out_total_space_size,
490 Out<s64> out_free_space_size,
491 FileSys::StorageId storage_id) {
492 LOG_INFO(Service_NS, "called, storage_id={}", storage_id);
493 *out_total_space_size = system.GetFileSystemController().GetTotalSpaceSize(storage_id);
494 *out_free_space_size = system.GetFileSystemController().GetFreeSpaceSize(storage_id);
495 R_SUCCEED();
496}
497
498Result IApplicationManagerInterface::IsApplicationUpdateRequested(Out<bool> out_update_required,
499 Out<u32> out_update_version,
500 u64 application_id) {
501 LOG_WARNING(Service_NS, "(STUBBED) called. application_id={:016X}", application_id);
502 *out_update_required = false;
503 *out_update_version = 0;
504 R_SUCCEED();
505}
506
507Result IApplicationManagerInterface::CheckApplicationLaunchVersion(u64 application_id) {
508 LOG_WARNING(Service_NS, "(STUBBED) called. application_id={:016X}", application_id);
509 R_SUCCEED();
510}
511
512Result IApplicationManagerInterface::GetApplicationTerminateResult(Out<Result> out_result,
513 u64 application_id) {
514 LOG_WARNING(Service_NS, "(STUBBED) called. application_id={:016X}", application_id);
515 *out_result = ResultSuccess;
516 R_SUCCEED();
517}
518
519} // namespace Service::NS
diff --git a/src/core/hle/service/ns/application_manager_interface.h b/src/core/hle/service/ns/application_manager_interface.h
new file mode 100644
index 000000000..350ec37ce
--- /dev/null
+++ b/src/core/hle/service/ns/application_manager_interface.h
@@ -0,0 +1,62 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "core/hle/service/cmif_types.h"
7#include "core/hle/service/ns/language.h"
8#include "core/hle/service/ns/ns_types.h"
9#include "core/hle/service/os/event.h"
10#include "core/hle/service/service.h"
11
12namespace Service::NS {
13
14class IApplicationManagerInterface final : public ServiceFramework<IApplicationManagerInterface> {
15public:
16 explicit IApplicationManagerInterface(Core::System& system_);
17 ~IApplicationManagerInterface() override;
18
19 Result GetApplicationControlData(OutBuffer<BufferAttr_HipcMapAlias> out_buffer,
20 Out<u32> out_actual_size,
21 ApplicationControlSource application_control_source,
22 u64 application_id);
23 Result GetApplicationDesiredLanguage(Out<ApplicationLanguage> out_desired_language,
24 u32 supported_languages);
25 Result ConvertApplicationLanguageToLanguageCode(Out<u64> out_language_code,
26 ApplicationLanguage application_language);
27 Result ListApplicationRecord(OutArray<ApplicationRecord, BufferAttr_HipcMapAlias> out_records,
28 Out<s32> out_count, s32 offset);
29 Result GetApplicationRecordUpdateSystemEvent(OutCopyHandle<Kernel::KReadableEvent> out_event);
30 Result GetGameCardMountFailureEvent(OutCopyHandle<Kernel::KReadableEvent> out_event);
31 Result IsAnyApplicationEntityInstalled(Out<bool> out_is_any_application_entity_installed);
32 Result GetApplicationView(
33 OutArray<ApplicationView, BufferAttr_HipcMapAlias> out_application_views,
34 InArray<u64, BufferAttr_HipcMapAlias> application_ids);
35 Result GetApplicationViewWithPromotionInfo(
36 OutArray<ApplicationViewWithPromotionInfo, BufferAttr_HipcMapAlias> out_application_views,
37 InArray<u64, BufferAttr_HipcMapAlias> application_ids);
38 Result GetApplicationRightsOnClient(
39 OutArray<ApplicationRightsOnClient, BufferAttr_HipcMapAlias> out_rights, Out<u32> out_count,
40 Common::UUID account_id, u32 flags, u64 application_id);
41 Result CheckSdCardMountStatus();
42 Result GetSdCardMountStatusChangedEvent(OutCopyHandle<Kernel::KReadableEvent> out_event);
43 Result GetFreeSpaceSize(Out<s64> out_free_space_size, FileSys::StorageId storage_id);
44 Result GetGameCardUpdateDetectionEvent(OutCopyHandle<Kernel::KReadableEvent> out_event);
45 Result ResumeAll();
46 Result GetStorageSize(Out<s64> out_total_space_size, Out<s64> out_free_space_size,
47 FileSys::StorageId storage_id);
48 Result IsApplicationUpdateRequested(Out<bool> out_update_required, Out<u32> out_update_version,
49 u64 application_id);
50 Result CheckApplicationLaunchVersion(u64 application_id);
51 Result GetApplicationTerminateResult(Out<Result> out_result, u64 application_id);
52
53private:
54 KernelHelpers::ServiceContext service_context;
55 Event record_update_system_event;
56 Event sd_card_mount_status_event;
57 Event gamecard_update_detection_event;
58 Event gamecard_mount_status_event;
59 Event gamecard_mount_failure_event;
60};
61
62} // namespace Service::NS
diff --git a/src/core/hle/service/ns/application_version_interface.cpp b/src/core/hle/service/ns/application_version_interface.cpp
new file mode 100644
index 000000000..b89e127db
--- /dev/null
+++ b/src/core/hle/service/ns/application_version_interface.cpp
@@ -0,0 +1,33 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/hle/service/ns/application_version_interface.h"
5
6namespace Service::NS {
7
8IApplicationVersionInterface::IApplicationVersionInterface(Core::System& system_)
9 : ServiceFramework{system_, "IApplicationVersionInterface"} {
10 // clang-format off
11 static const FunctionInfo functions[] = {
12 {0, nullptr, "GetLaunchRequiredVersion"},
13 {1, nullptr, "UpgradeLaunchRequiredVersion"},
14 {35, nullptr, "UpdateVersionList"},
15 {36, nullptr, "PushLaunchVersion"},
16 {37, nullptr, "ListRequiredVersion"},
17 {800, nullptr, "RequestVersionList"},
18 {801, nullptr, "ListVersionList"},
19 {802, nullptr, "RequestVersionListData"},
20 {900, nullptr, "ImportAutoUpdatePolicyJsonForDebug"},
21 {901, nullptr, "ListDefaultAutoUpdatePolicy"},
22 {902, nullptr, "ListAutoUpdatePolicyForSpecificApplication"},
23 {1000, nullptr, "PerformAutoUpdate"},
24 {1001, nullptr, "ListAutoUpdateSchedule"},
25 };
26 // clang-format on
27
28 RegisterHandlers(functions);
29}
30
31IApplicationVersionInterface::~IApplicationVersionInterface() = default;
32
33} // namespace Service::NS
diff --git a/src/core/hle/service/ns/application_version_interface.h b/src/core/hle/service/ns/application_version_interface.h
new file mode 100644
index 000000000..b288cff1b
--- /dev/null
+++ b/src/core/hle/service/ns/application_version_interface.h
@@ -0,0 +1,16 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "core/hle/service/service.h"
7
8namespace Service::NS {
9
10class IApplicationVersionInterface final : public ServiceFramework<IApplicationVersionInterface> {
11public:
12 explicit IApplicationVersionInterface(Core::System& system_);
13 ~IApplicationVersionInterface() override;
14};
15
16} // namespace Service::NS
diff --git a/src/core/hle/service/ns/content_management_interface.cpp b/src/core/hle/service/ns/content_management_interface.cpp
new file mode 100644
index 000000000..69bb3f6e4
--- /dev/null
+++ b/src/core/hle/service/ns/content_management_interface.cpp
@@ -0,0 +1,72 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "common/common_funcs.h"
5#include "core/core.h"
6#include "core/hle/service/cmif_serialization.h"
7#include "core/hle/service/filesystem/filesystem.h"
8#include "core/hle/service/ns/content_management_interface.h"
9#include "core/hle/service/ns/ns_types.h"
10
11namespace Service::NS {
12
13IContentManagementInterface::IContentManagementInterface(Core::System& system_)
14 : ServiceFramework{system_, "IContentManagementInterface"} {
15 // clang-format off
16 static const FunctionInfo functions[] = {
17 {11, D<&IContentManagementInterface::CalculateApplicationOccupiedSize>, "CalculateApplicationOccupiedSize"},
18 {43, D<&IContentManagementInterface::CheckSdCardMountStatus>, "CheckSdCardMountStatus"},
19 {47, D<&IContentManagementInterface::GetTotalSpaceSize>, "GetTotalSpaceSize"},
20 {48, D<&IContentManagementInterface::GetFreeSpaceSize>, "GetFreeSpaceSize"},
21 {600, nullptr, "CountApplicationContentMeta"},
22 {601, nullptr, "ListApplicationContentMetaStatus"},
23 {605, nullptr, "ListApplicationContentMetaStatusWithRightsCheck"},
24 {607, nullptr, "IsAnyApplicationRunning"},
25 };
26 // clang-format on
27
28 RegisterHandlers(functions);
29}
30
31IContentManagementInterface::~IContentManagementInterface() = default;
32
33Result IContentManagementInterface::CalculateApplicationOccupiedSize(
34 Out<ApplicationOccupiedSize> out_size, u64 application_id) {
35 LOG_WARNING(Service_NS, "(STUBBED) called, application_id={:016X}", application_id);
36
37 using namespace Common::Literals;
38
39 constexpr ApplicationOccupiedSizeEntity stub_entity{
40 .storage_id = FileSys::StorageId::SdCard,
41 .app_size = 8_GiB,
42 .patch_size = 2_GiB,
43 .aoc_size = 12_MiB,
44 };
45
46 for (auto& entity : out_size->entities) {
47 entity = stub_entity;
48 }
49
50 R_SUCCEED();
51}
52
53Result IContentManagementInterface::CheckSdCardMountStatus() {
54 LOG_WARNING(Service_NS, "(STUBBED) called");
55 R_SUCCEED();
56}
57
58Result IContentManagementInterface::GetTotalSpaceSize(Out<s64> out_total_space_size,
59 FileSys::StorageId storage_id) {
60 LOG_INFO(Service_NS, "(STUBBED) called, storage_id={}", storage_id);
61 *out_total_space_size = system.GetFileSystemController().GetTotalSpaceSize(storage_id);
62 R_SUCCEED();
63}
64
65Result IContentManagementInterface::GetFreeSpaceSize(Out<s64> out_free_space_size,
66 FileSys::StorageId storage_id) {
67 LOG_INFO(Service_NS, "(STUBBED) called, storage_id={}", storage_id);
68 *out_free_space_size = system.GetFileSystemController().GetFreeSpaceSize(storage_id);
69 R_SUCCEED();
70}
71
72} // namespace Service::NS
diff --git a/src/core/hle/service/ns/content_management_interface.h b/src/core/hle/service/ns/content_management_interface.h
new file mode 100644
index 000000000..2894628e5
--- /dev/null
+++ b/src/core/hle/service/ns/content_management_interface.h
@@ -0,0 +1,25 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "core/hle/service/cmif_types.h"
7#include "core/hle/service/ns/ns_types.h"
8#include "core/hle/service/service.h"
9
10namespace Service::NS {
11
12class IContentManagementInterface final : public ServiceFramework<IContentManagementInterface> {
13public:
14 explicit IContentManagementInterface(Core::System& system_);
15 ~IContentManagementInterface() override;
16
17public:
18 Result CalculateApplicationOccupiedSize(Out<ApplicationOccupiedSize> out_size,
19 u64 application_id);
20 Result CheckSdCardMountStatus();
21 Result GetTotalSpaceSize(Out<s64> out_total_space_size, FileSys::StorageId storage_id);
22 Result GetFreeSpaceSize(Out<s64> out_free_space_size, FileSys::StorageId storage_id);
23};
24
25} // namespace Service::NS
diff --git a/src/core/hle/service/ns/develop_interface.cpp b/src/core/hle/service/ns/develop_interface.cpp
new file mode 100644
index 000000000..880bdbebb
--- /dev/null
+++ b/src/core/hle/service/ns/develop_interface.cpp
@@ -0,0 +1,38 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/hle/service/ns/develop_interface.h"
5
6namespace Service::NS {
7
8IDevelopInterface::IDevelopInterface(Core::System& system_) : ServiceFramework{system_, "ns:dev"} {
9 // clang-format off
10 static const FunctionInfo functions[] = {
11 {0, nullptr, "LaunchProgram"},
12 {1, nullptr, "TerminateProcess"},
13 {2, nullptr, "TerminateProgram"},
14 {4, nullptr, "GetShellEvent"},
15 {5, nullptr, "GetShellEventInfo"},
16 {6, nullptr, "TerminateApplication"},
17 {7, nullptr, "PrepareLaunchProgramFromHost"},
18 {8, nullptr, "LaunchApplicationFromHost"},
19 {9, nullptr, "LaunchApplicationWithStorageIdForDevelop"},
20 {10, nullptr, "IsSystemMemoryResourceLimitBoosted"},
21 {11, nullptr, "GetRunningApplicationProcessIdForDevelop"},
22 {12, nullptr, "SetCurrentApplicationRightsEnvironmentCanBeActiveForDevelop"},
23 {13, nullptr, "CreateApplicationResourceForDevelop"},
24 {14, nullptr, "IsPreomiaForDevelop"},
25 {15, nullptr, "GetApplicationProgramIdFromHost"},
26 {16, nullptr, "RefreshCachedDebugValues"},
27 {17, nullptr, "PrepareLaunchApplicationFromHost"},
28 {18, nullptr, "GetLaunchEvent"},
29 {19, nullptr, "GetLaunchResult"},
30 };
31 // clang-format on
32
33 RegisterHandlers(functions);
34}
35
36IDevelopInterface::~IDevelopInterface() = default;
37
38} // namespace Service::NS
diff --git a/src/core/hle/service/ns/develop_interface.h b/src/core/hle/service/ns/develop_interface.h
new file mode 100644
index 000000000..a9f81ccd6
--- /dev/null
+++ b/src/core/hle/service/ns/develop_interface.h
@@ -0,0 +1,16 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "core/hle/service/service.h"
7
8namespace Service::NS {
9
10class IDevelopInterface final : public ServiceFramework<IDevelopInterface> {
11public:
12 explicit IDevelopInterface(Core::System& system_);
13 ~IDevelopInterface() override;
14};
15
16} // namespace Service::NS
diff --git a/src/core/hle/service/ns/document_interface.cpp b/src/core/hle/service/ns/document_interface.cpp
new file mode 100644
index 000000000..51a1e46c0
--- /dev/null
+++ b/src/core/hle/service/ns/document_interface.cpp
@@ -0,0 +1,38 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/core.h"
5#include "core/hle/service/cmif_serialization.h"
6#include "core/hle/service/ns/document_interface.h"
7
8namespace Service::NS {
9
10IDocumentInterface::IDocumentInterface(Core::System& system_)
11 : ServiceFramework{system_, "IDocumentInterface"} {
12 // clang-format off
13 static const FunctionInfo functions[] = {
14 {21, nullptr, "GetApplicationContentPath"},
15 {23, D<&IDocumentInterface::ResolveApplicationContentPath>, "ResolveApplicationContentPath"},
16 {92, D<&IDocumentInterface::GetRunningApplicationProgramId>, "GetRunningApplicationProgramId"},
17 };
18 // clang-format on
19
20 RegisterHandlers(functions);
21}
22
23IDocumentInterface::~IDocumentInterface() = default;
24
25Result IDocumentInterface::ResolveApplicationContentPath(ContentPath content_path) {
26 LOG_WARNING(Service_NS, "(STUBBED) called, file_system_proxy_type={}, program_id={:016X}",
27 content_path.file_system_proxy_type, content_path.program_id);
28 R_SUCCEED();
29}
30
31Result IDocumentInterface::GetRunningApplicationProgramId(Out<u64> out_program_id,
32 u64 caller_program_id) {
33 LOG_WARNING(Service_NS, "(STUBBED) called, caller_program_id={:016X}", caller_program_id);
34 *out_program_id = system.GetApplicationProcessProgramID();
35 R_SUCCEED();
36}
37
38} // namespace Service::NS
diff --git a/src/core/hle/service/ns/document_interface.h b/src/core/hle/service/ns/document_interface.h
new file mode 100644
index 000000000..cd461652c
--- /dev/null
+++ b/src/core/hle/service/ns/document_interface.h
@@ -0,0 +1,22 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "core/hle/service/cmif_types.h"
7#include "core/hle/service/ns/ns_types.h"
8#include "core/hle/service/service.h"
9
10namespace Service::NS {
11
12class IDocumentInterface final : public ServiceFramework<IDocumentInterface> {
13public:
14 explicit IDocumentInterface(Core::System& system_);
15 ~IDocumentInterface() override;
16
17private:
18 Result ResolveApplicationContentPath(ContentPath content_path);
19 Result GetRunningApplicationProgramId(Out<u64> out_program_id, u64 caller_program_id);
20};
21
22} // namespace Service::NS
diff --git a/src/core/hle/service/ns/download_task_interface.cpp b/src/core/hle/service/ns/download_task_interface.cpp
new file mode 100644
index 000000000..62dc7f187
--- /dev/null
+++ b/src/core/hle/service/ns/download_task_interface.cpp
@@ -0,0 +1,39 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/hle/service/cmif_serialization.h"
5#include "core/hle/service/ns/download_task_interface.h"
6
7namespace Service::NS {
8
9IDownloadTaskInterface::IDownloadTaskInterface(Core::System& system_)
10 : ServiceFramework{system_, "IDownloadTaskInterface"} {
11 // clang-format off
12 static const FunctionInfo functions[] = {
13 {701, nullptr, "ClearTaskStatusList"},
14 {702, nullptr, "RequestDownloadTaskList"},
15 {703, nullptr, "RequestEnsureDownloadTask"},
16 {704, nullptr, "ListDownloadTaskStatus"},
17 {705, nullptr, "RequestDownloadTaskListData"},
18 {706, nullptr, "TryCommitCurrentApplicationDownloadTask"},
19 {707, D<&IDownloadTaskInterface::EnableAutoCommit>, "EnableAutoCommit"},
20 {708, D<&IDownloadTaskInterface::DisableAutoCommit>, "DisableAutoCommit"},
21 {709, nullptr, "TriggerDynamicCommitEvent"},
22 };
23 // clang-format on
24
25 RegisterHandlers(functions);
26}
27
28IDownloadTaskInterface::~IDownloadTaskInterface() = default;
29
30Result IDownloadTaskInterface::EnableAutoCommit() {
31 LOG_WARNING(Service_NS, "(STUBBED) called");
32 R_SUCCEED();
33}
34Result IDownloadTaskInterface::DisableAutoCommit() {
35 LOG_WARNING(Service_NS, "(STUBBED) called");
36 R_SUCCEED();
37}
38
39} // namespace Service::NS
diff --git a/src/core/hle/service/ns/download_task_interface.h b/src/core/hle/service/ns/download_task_interface.h
new file mode 100644
index 000000000..b1cb69cb8
--- /dev/null
+++ b/src/core/hle/service/ns/download_task_interface.h
@@ -0,0 +1,20 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "core/hle/service/service.h"
7
8namespace Service::NS {
9
10class IDownloadTaskInterface final : public ServiceFramework<IDownloadTaskInterface> {
11public:
12 explicit IDownloadTaskInterface(Core::System& system_);
13 ~IDownloadTaskInterface() override;
14
15private:
16 Result EnableAutoCommit();
17 Result DisableAutoCommit();
18};
19
20} // namespace Service::NS
diff --git a/src/core/hle/service/ns/dynamic_rights_interface.cpp b/src/core/hle/service/ns/dynamic_rights_interface.cpp
new file mode 100644
index 000000000..ce81e203f
--- /dev/null
+++ b/src/core/hle/service/ns/dynamic_rights_interface.cpp
@@ -0,0 +1,62 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/hle/service/cmif_serialization.h"
5#include "core/hle/service/ns/dynamic_rights_interface.h"
6
7namespace Service::NS {
8
9IDynamicRightsInterface::IDynamicRightsInterface(Core::System& system_)
10 : ServiceFramework{system_, "DynamicRightsInterface"} {
11 // clang-format off
12 static const FunctionInfo functions[] = {
13 {0, nullptr, "RequestApplicationRightsOnServer"},
14 {1, nullptr, "RequestAssignRights"},
15 {4, nullptr, "DeprecatedRequestAssignRightsToResume"},
16 {5, D<&IDynamicRightsInterface::VerifyActivatedRightsOwners>, "VerifyActivatedRightsOwners"},
17 {6, nullptr, "DeprecatedGetApplicationRightsStatus"},
18 {7, nullptr, "RequestPrefetchForDynamicRights"},
19 {8, nullptr, "GetDynamicRightsState"},
20 {9, nullptr, "RequestApplicationRightsOnServerToResume"},
21 {10, nullptr, "RequestAssignRightsToResume"},
22 {11, nullptr, "GetActivatedRightsUsers"},
23 {12, nullptr, "GetApplicationRightsStatus"},
24 {13, D<&IDynamicRightsInterface::GetRunningApplicationStatus>, "GetRunningApplicationStatus"},
25 {14, nullptr, "SelectApplicationLicense"},
26 {15, nullptr, "RequestContentsAuthorizationToken"},
27 {16, nullptr, "QualifyUser"},
28 {17, nullptr, "QualifyUserWithProcessId"},
29 {18, D<&IDynamicRightsInterface::NotifyApplicationRightsCheckStart>, "NotifyApplicationRightsCheckStart"},
30 {19, nullptr, "UpdateUserList"},
31 {20, nullptr, "IsRightsLostUser"},
32 {21, nullptr, "SetRequiredAddOnContentsOnContentsAvailabilityTransition"},
33 {22, nullptr, "GetLimitedApplicationLicense"},
34 {23, nullptr, "GetLimitedApplicationLicenseUpgradableEvent"},
35 {24, nullptr, "NotifyLimitedApplicationLicenseUpgradableEventForDebug"},
36 {25, nullptr, "RequestProceedDynamicRightsState"},
37 };
38 // clang-format on
39
40 RegisterHandlers(functions);
41}
42
43IDynamicRightsInterface::~IDynamicRightsInterface() = default;
44
45Result IDynamicRightsInterface::NotifyApplicationRightsCheckStart() {
46 LOG_WARNING(Service_NS, "(STUBBED) called");
47 R_SUCCEED();
48}
49
50Result IDynamicRightsInterface::GetRunningApplicationStatus(Out<u32> out_status,
51 u64 rights_handle) {
52 LOG_WARNING(Service_NS, "(STUBBED) called, rights_handle={:#x}", rights_handle);
53 *out_status = 0;
54 R_SUCCEED();
55}
56
57Result IDynamicRightsInterface::VerifyActivatedRightsOwners(u64 rights_handle) {
58 LOG_WARNING(Service_NS, "(STUBBED) called, rights_handle={:#x}", rights_handle);
59 R_SUCCEED();
60}
61
62} // namespace Service::NS
diff --git a/src/core/hle/service/ns/dynamic_rights_interface.h b/src/core/hle/service/ns/dynamic_rights_interface.h
new file mode 100644
index 000000000..877e009b0
--- /dev/null
+++ b/src/core/hle/service/ns/dynamic_rights_interface.h
@@ -0,0 +1,22 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "core/hle/service/cmif_types.h"
7#include "core/hle/service/service.h"
8
9namespace Service::NS {
10
11class IDynamicRightsInterface final : public ServiceFramework<IDynamicRightsInterface> {
12public:
13 explicit IDynamicRightsInterface(Core::System& system_);
14 ~IDynamicRightsInterface() override;
15
16private:
17 Result NotifyApplicationRightsCheckStart();
18 Result GetRunningApplicationStatus(Out<u32> out_status, u64 rights_handle);
19 Result VerifyActivatedRightsOwners(u64 rights_handle);
20};
21
22} // namespace Service::NS
diff --git a/src/core/hle/service/ns/ecommerce_interface.cpp b/src/core/hle/service/ns/ecommerce_interface.cpp
new file mode 100644
index 000000000..76fc425f0
--- /dev/null
+++ b/src/core/hle/service/ns/ecommerce_interface.cpp
@@ -0,0 +1,27 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/hle/service/ns/ecommerce_interface.h"
5
6namespace Service::NS {
7
8IECommerceInterface::IECommerceInterface(Core::System& system_)
9 : ServiceFramework{system_, "IECommerceInterface"} {
10 // clang-format off
11 static const FunctionInfo functions[] = {
12 {0, nullptr, "RequestLinkDevice"},
13 {1, nullptr, "RequestCleanupAllPreInstalledApplications"},
14 {2, nullptr, "RequestCleanupPreInstalledApplication"},
15 {3, nullptr, "RequestSyncRights"},
16 {4, nullptr, "RequestUnlinkDevice"},
17 {5, nullptr, "RequestRevokeAllELicense"},
18 {6, nullptr, "RequestSyncRightsBasedOnAssignedELicenses"},
19 };
20 // clang-format on
21
22 RegisterHandlers(functions);
23}
24
25IECommerceInterface::~IECommerceInterface() = default;
26
27} // namespace Service::NS
diff --git a/src/core/hle/service/ns/ecommerce_interface.h b/src/core/hle/service/ns/ecommerce_interface.h
new file mode 100644
index 000000000..4352101f4
--- /dev/null
+++ b/src/core/hle/service/ns/ecommerce_interface.h
@@ -0,0 +1,16 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "core/hle/service/service.h"
7
8namespace Service::NS {
9
10class IECommerceInterface final : public ServiceFramework<IECommerceInterface> {
11public:
12 explicit IECommerceInterface(Core::System& system_);
13 ~IECommerceInterface() override;
14};
15
16} // namespace Service::NS
diff --git a/src/core/hle/service/ns/factory_reset_interface.cpp b/src/core/hle/service/ns/factory_reset_interface.cpp
new file mode 100644
index 000000000..fd5cf7e1f
--- /dev/null
+++ b/src/core/hle/service/ns/factory_reset_interface.cpp
@@ -0,0 +1,27 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/hle/service/ns/factory_reset_interface.h"
5
6namespace Service::NS {
7
8IFactoryResetInterface::IFactoryResetInterface(Core::System& system_)
9 : ServiceFramework{system_, "IFactoryResetInterface"} {
10 // clang-format off
11 static const FunctionInfo functions[] = {
12 {100, nullptr, "ResetToFactorySettings"},
13 {101, nullptr, "ResetToFactorySettingsWithoutUserSaveData"},
14 {102, nullptr, "ResetToFactorySettingsForRefurbishment"},
15 {103, nullptr, "ResetToFactorySettingsWithPlatformRegion"},
16 {104, nullptr, "ResetToFactorySettingsWithPlatformRegionAuthentication"},
17 {105, nullptr, "RequestResetToFactorySettingsSecurely"},
18 {106, nullptr, "RequestResetToFactorySettingsWithPlatformRegionAuthenticationSecurely"},
19 };
20 // clang-format on
21
22 RegisterHandlers(functions);
23}
24
25IFactoryResetInterface::~IFactoryResetInterface() = default;
26
27} // namespace Service::NS
diff --git a/src/core/hle/service/ns/factory_reset_interface.h b/src/core/hle/service/ns/factory_reset_interface.h
new file mode 100644
index 000000000..50d125123
--- /dev/null
+++ b/src/core/hle/service/ns/factory_reset_interface.h
@@ -0,0 +1,16 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "core/hle/service/service.h"
7
8namespace Service::NS {
9
10class IFactoryResetInterface final : public ServiceFramework<IFactoryResetInterface> {
11public:
12 explicit IFactoryResetInterface(Core::System& system_);
13 ~IFactoryResetInterface() override;
14};
15
16} // namespace Service::NS
diff --git a/src/core/hle/service/ns/ns.cpp b/src/core/hle/service/ns/ns.cpp
index 19c3ff01b..8402e83cb 100644
--- a/src/core/hle/service/ns/ns.cpp
+++ b/src/core/hle/service/ns/ns.cpp
@@ -1,893 +1,38 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include "common/logging/log.h" 4#include "core/hle/service/ns/develop_interface.h"
5#include "common/settings.h"
6#include "core/arm/debug.h"
7#include "core/core.h"
8#include "core/file_sys/control_metadata.h"
9#include "core/file_sys/patch_manager.h"
10#include "core/file_sys/vfs/vfs.h"
11#include "core/hle/service/filesystem/filesystem.h"
12#include "core/hle/service/glue/glue_manager.h"
13#include "core/hle/service/ipc_helpers.h"
14#include "core/hle/service/ns/errors.h"
15#include "core/hle/service/ns/iplatform_service_manager.h"
16#include "core/hle/service/ns/language.h"
17#include "core/hle/service/ns/ns.h" 5#include "core/hle/service/ns/ns.h"
18#include "core/hle/service/ns/pdm_qry.h" 6#include "core/hle/service/ns/platform_service_manager.h"
7#include "core/hle/service/ns/query_service.h"
8#include "core/hle/service/ns/service_getter_interface.h"
9#include "core/hle/service/ns/system_update_interface.h"
10#include "core/hle/service/ns/vulnerability_manager_interface.h"
19#include "core/hle/service/server_manager.h" 11#include "core/hle/service/server_manager.h"
20#include "core/hle/service/set/settings_server.h"
21 12
22namespace Service::NS { 13namespace Service::NS {
23 14
24IAccountProxyInterface::IAccountProxyInterface(Core::System& system_)
25 : ServiceFramework{system_, "IAccountProxyInterface"} {
26 // clang-format off
27 static const FunctionInfo functions[] = {
28 {0, nullptr, "CreateUserAccount"},
29 };
30 // clang-format on
31
32 RegisterHandlers(functions);
33}
34
35IAccountProxyInterface::~IAccountProxyInterface() = default;
36
37IApplicationManagerInterface::IApplicationManagerInterface(Core::System& system_)
38 : ServiceFramework{system_, "IApplicationManagerInterface"} {
39 // clang-format off
40 static const FunctionInfo functions[] = {
41 {0, nullptr, "ListApplicationRecord"},
42 {1, nullptr, "GenerateApplicationRecordCount"},
43 {2, nullptr, "GetApplicationRecordUpdateSystemEvent"},
44 {3, nullptr, "GetApplicationViewDeprecated"},
45 {4, nullptr, "DeleteApplicationEntity"},
46 {5, nullptr, "DeleteApplicationCompletely"},
47 {6, nullptr, "IsAnyApplicationEntityRedundant"},
48 {7, nullptr, "DeleteRedundantApplicationEntity"},
49 {8, nullptr, "IsApplicationEntityMovable"},
50 {9, nullptr, "MoveApplicationEntity"},
51 {11, nullptr, "CalculateApplicationOccupiedSize"},
52 {16, nullptr, "PushApplicationRecord"},
53 {17, nullptr, "ListApplicationRecordContentMeta"},
54 {19, nullptr, "LaunchApplicationOld"},
55 {21, nullptr, "GetApplicationContentPath"},
56 {22, nullptr, "TerminateApplication"},
57 {23, nullptr, "ResolveApplicationContentPath"},
58 {26, nullptr, "BeginInstallApplication"},
59 {27, nullptr, "DeleteApplicationRecord"},
60 {30, nullptr, "RequestApplicationUpdateInfo"},
61 {31, nullptr, "Unknown31"},
62 {32, nullptr, "CancelApplicationDownload"},
63 {33, nullptr, "ResumeApplicationDownload"},
64 {35, nullptr, "UpdateVersionList"},
65 {36, nullptr, "PushLaunchVersion"},
66 {37, nullptr, "ListRequiredVersion"},
67 {38, nullptr, "CheckApplicationLaunchVersion"},
68 {39, nullptr, "CheckApplicationLaunchRights"},
69 {40, nullptr, "GetApplicationLogoData"},
70 {41, nullptr, "CalculateApplicationDownloadRequiredSize"},
71 {42, nullptr, "CleanupSdCard"},
72 {43, nullptr, "CheckSdCardMountStatus"},
73 {44, nullptr, "GetSdCardMountStatusChangedEvent"},
74 {45, nullptr, "GetGameCardAttachmentEvent"},
75 {46, nullptr, "GetGameCardAttachmentInfo"},
76 {47, nullptr, "GetTotalSpaceSize"},
77 {48, nullptr, "GetFreeSpaceSize"},
78 {49, nullptr, "GetSdCardRemovedEvent"},
79 {52, nullptr, "GetGameCardUpdateDetectionEvent"},
80 {53, nullptr, "DisableApplicationAutoDelete"},
81 {54, nullptr, "EnableApplicationAutoDelete"},
82 {55, &IApplicationManagerInterface::GetApplicationDesiredLanguage, "GetApplicationDesiredLanguage"},
83 {56, nullptr, "SetApplicationTerminateResult"},
84 {57, nullptr, "ClearApplicationTerminateResult"},
85 {58, nullptr, "GetLastSdCardMountUnexpectedResult"},
86 {59, &IApplicationManagerInterface::ConvertApplicationLanguageToLanguageCode, "ConvertApplicationLanguageToLanguageCode"},
87 {60, nullptr, "ConvertLanguageCodeToApplicationLanguage"},
88 {61, nullptr, "GetBackgroundDownloadStressTaskInfo"},
89 {62, nullptr, "GetGameCardStopper"},
90 {63, nullptr, "IsSystemProgramInstalled"},
91 {64, nullptr, "StartApplyDeltaTask"},
92 {65, nullptr, "GetRequestServerStopper"},
93 {66, nullptr, "GetBackgroundApplyDeltaStressTaskInfo"},
94 {67, nullptr, "CancelApplicationApplyDelta"},
95 {68, nullptr, "ResumeApplicationApplyDelta"},
96 {69, nullptr, "CalculateApplicationApplyDeltaRequiredSize"},
97 {70, nullptr, "ResumeAll"},
98 {71, nullptr, "GetStorageSize"},
99 {80, nullptr, "RequestDownloadApplication"},
100 {81, nullptr, "RequestDownloadAddOnContent"},
101 {82, nullptr, "DownloadApplication"},
102 {83, nullptr, "CheckApplicationResumeRights"},
103 {84, nullptr, "GetDynamicCommitEvent"},
104 {85, nullptr, "RequestUpdateApplication2"},
105 {86, nullptr, "EnableApplicationCrashReport"},
106 {87, nullptr, "IsApplicationCrashReportEnabled"},
107 {90, nullptr, "BoostSystemMemoryResourceLimit"},
108 {91, nullptr, "DeprecatedLaunchApplication"},
109 {92, nullptr, "GetRunningApplicationProgramId"},
110 {93, nullptr, "GetMainApplicationProgramIndex"},
111 {94, nullptr, "LaunchApplication"},
112 {95, nullptr, "GetApplicationLaunchInfo"},
113 {96, nullptr, "AcquireApplicationLaunchInfo"},
114 {97, nullptr, "GetMainApplicationProgramIndexByApplicationLaunchInfo"},
115 {98, nullptr, "EnableApplicationAllThreadDumpOnCrash"},
116 {99, nullptr, "LaunchDevMenu"},
117 {100, nullptr, "ResetToFactorySettings"},
118 {101, nullptr, "ResetToFactorySettingsWithoutUserSaveData"},
119 {102, nullptr, "ResetToFactorySettingsForRefurbishment"},
120 {103, nullptr, "ResetToFactorySettingsWithPlatformRegion"},
121 {104, nullptr, "ResetToFactorySettingsWithPlatformRegionAuthentication"},
122 {105, nullptr, "RequestResetToFactorySettingsSecurely"},
123 {106, nullptr, "RequestResetToFactorySettingsWithPlatformRegionAuthenticationSecurely"},
124 {200, nullptr, "CalculateUserSaveDataStatistics"},
125 {201, nullptr, "DeleteUserSaveDataAll"},
126 {210, nullptr, "DeleteUserSystemSaveData"},
127 {211, nullptr, "DeleteSaveData"},
128 {220, nullptr, "UnregisterNetworkServiceAccount"},
129 {221, nullptr, "UnregisterNetworkServiceAccountWithUserSaveDataDeletion"},
130 {300, nullptr, "GetApplicationShellEvent"},
131 {301, nullptr, "PopApplicationShellEventInfo"},
132 {302, nullptr, "LaunchLibraryApplet"},
133 {303, nullptr, "TerminateLibraryApplet"},
134 {304, nullptr, "LaunchSystemApplet"},
135 {305, nullptr, "TerminateSystemApplet"},
136 {306, nullptr, "LaunchOverlayApplet"},
137 {307, nullptr, "TerminateOverlayApplet"},
138 {400, &IApplicationManagerInterface::GetApplicationControlData, "GetApplicationControlData"},
139 {401, nullptr, "InvalidateAllApplicationControlCache"},
140 {402, nullptr, "RequestDownloadApplicationControlData"},
141 {403, nullptr, "GetMaxApplicationControlCacheCount"},
142 {404, nullptr, "InvalidateApplicationControlCache"},
143 {405, nullptr, "ListApplicationControlCacheEntryInfo"},
144 {406, nullptr, "GetApplicationControlProperty"},
145 {407, nullptr, "ListApplicationTitle"},
146 {408, nullptr, "ListApplicationIcon"},
147 {502, nullptr, "RequestCheckGameCardRegistration"},
148 {503, nullptr, "RequestGameCardRegistrationGoldPoint"},
149 {504, nullptr, "RequestRegisterGameCard"},
150 {505, nullptr, "GetGameCardMountFailureEvent"},
151 {506, nullptr, "IsGameCardInserted"},
152 {507, nullptr, "EnsureGameCardAccess"},
153 {508, nullptr, "GetLastGameCardMountFailureResult"},
154 {509, nullptr, "ListApplicationIdOnGameCard"},
155 {510, nullptr, "GetGameCardPlatformRegion"},
156 {600, nullptr, "CountApplicationContentMeta"},
157 {601, nullptr, "ListApplicationContentMetaStatus"},
158 {602, nullptr, "ListAvailableAddOnContent"},
159 {603, nullptr, "GetOwnedApplicationContentMetaStatus"},
160 {604, nullptr, "RegisterContentsExternalKey"},
161 {605, nullptr, "ListApplicationContentMetaStatusWithRightsCheck"},
162 {606, nullptr, "GetContentMetaStorage"},
163 {607, nullptr, "ListAvailableAddOnContent"},
164 {609, nullptr, "ListAvailabilityAssuredAddOnContent"},
165 {610, nullptr, "GetInstalledContentMetaStorage"},
166 {611, nullptr, "PrepareAddOnContent"},
167 {700, nullptr, "PushDownloadTaskList"},
168 {701, nullptr, "ClearTaskStatusList"},
169 {702, nullptr, "RequestDownloadTaskList"},
170 {703, nullptr, "RequestEnsureDownloadTask"},
171 {704, nullptr, "ListDownloadTaskStatus"},
172 {705, nullptr, "RequestDownloadTaskListData"},
173 {800, nullptr, "RequestVersionList"},
174 {801, nullptr, "ListVersionList"},
175 {802, nullptr, "RequestVersionListData"},
176 {900, nullptr, "GetApplicationRecord"},
177 {901, nullptr, "GetApplicationRecordProperty"},
178 {902, nullptr, "EnableApplicationAutoUpdate"},
179 {903, nullptr, "DisableApplicationAutoUpdate"},
180 {904, nullptr, "TouchApplication"},
181 {905, nullptr, "RequestApplicationUpdate"},
182 {906, nullptr, "IsApplicationUpdateRequested"},
183 {907, nullptr, "WithdrawApplicationUpdateRequest"},
184 {908, nullptr, "ListApplicationRecordInstalledContentMeta"},
185 {909, nullptr, "WithdrawCleanupAddOnContentsWithNoRightsRecommendation"},
186 {910, nullptr, "HasApplicationRecord"},
187 {911, nullptr, "SetPreInstalledApplication"},
188 {912, nullptr, "ClearPreInstalledApplicationFlag"},
189 {913, nullptr, "ListAllApplicationRecord"},
190 {914, nullptr, "HideApplicationRecord"},
191 {915, nullptr, "ShowApplicationRecord"},
192 {916, nullptr, "IsApplicationAutoDeleteDisabled"},
193 {1000, nullptr, "RequestVerifyApplicationDeprecated"},
194 {1001, nullptr, "CorruptApplicationForDebug"},
195 {1002, nullptr, "RequestVerifyAddOnContentsRights"},
196 {1003, nullptr, "RequestVerifyApplication"},
197 {1004, nullptr, "CorruptContentForDebug"},
198 {1200, nullptr, "NeedsUpdateVulnerability"},
199 {1300, nullptr, "IsAnyApplicationEntityInstalled"},
200 {1301, nullptr, "DeleteApplicationContentEntities"},
201 {1302, nullptr, "CleanupUnrecordedApplicationEntity"},
202 {1303, nullptr, "CleanupAddOnContentsWithNoRights"},
203 {1304, nullptr, "DeleteApplicationContentEntity"},
204 {1305, nullptr, "TryDeleteRunningApplicationEntity"},
205 {1306, nullptr, "TryDeleteRunningApplicationCompletely"},
206 {1307, nullptr, "TryDeleteRunningApplicationContentEntities"},
207 {1308, nullptr, "DeleteApplicationCompletelyForDebug"},
208 {1309, nullptr, "CleanupUnavailableAddOnContents"},
209 {1310, nullptr, "RequestMoveApplicationEntity"},
210 {1311, nullptr, "EstimateSizeToMove"},
211 {1312, nullptr, "HasMovableEntity"},
212 {1313, nullptr, "CleanupOrphanContents"},
213 {1314, nullptr, "CheckPreconditionSatisfiedToMove"},
214 {1400, nullptr, "PrepareShutdown"},
215 {1500, nullptr, "FormatSdCard"},
216 {1501, nullptr, "NeedsSystemUpdateToFormatSdCard"},
217 {1502, nullptr, "GetLastSdCardFormatUnexpectedResult"},
218 {1504, nullptr, "InsertSdCard"},
219 {1505, nullptr, "RemoveSdCard"},
220 {1506, nullptr, "GetSdCardStartupStatus"},
221 {1600, nullptr, "GetSystemSeedForPseudoDeviceId"},
222 {1601, nullptr, "ResetSystemSeedForPseudoDeviceId"},
223 {1700, nullptr, "ListApplicationDownloadingContentMeta"},
224 {1701, nullptr, "GetApplicationView"},
225 {1702, nullptr, "GetApplicationDownloadTaskStatus"},
226 {1703, nullptr, "GetApplicationViewDownloadErrorContext"},
227 {1704, nullptr, "GetApplicationViewWithPromotionInfo"},
228 {1705, nullptr, "IsPatchAutoDeletableApplication"},
229 {1800, nullptr, "IsNotificationSetupCompleted"},
230 {1801, nullptr, "GetLastNotificationInfoCount"},
231 {1802, nullptr, "ListLastNotificationInfo"},
232 {1803, nullptr, "ListNotificationTask"},
233 {1900, nullptr, "IsActiveAccount"},
234 {1901, nullptr, "RequestDownloadApplicationPrepurchasedRights"},
235 {1902, nullptr, "GetApplicationTicketInfo"},
236 {1903, nullptr, "RequestDownloadApplicationPrepurchasedRightsForAccount"},
237 {2000, nullptr, "GetSystemDeliveryInfo"},
238 {2001, nullptr, "SelectLatestSystemDeliveryInfo"},
239 {2002, nullptr, "VerifyDeliveryProtocolVersion"},
240 {2003, nullptr, "GetApplicationDeliveryInfo"},
241 {2004, nullptr, "HasAllContentsToDeliver"},
242 {2005, nullptr, "CompareApplicationDeliveryInfo"},
243 {2006, nullptr, "CanDeliverApplication"},
244 {2007, nullptr, "ListContentMetaKeyToDeliverApplication"},
245 {2008, nullptr, "NeedsSystemUpdateToDeliverApplication"},
246 {2009, nullptr, "EstimateRequiredSize"},
247 {2010, nullptr, "RequestReceiveApplication"},
248 {2011, nullptr, "CommitReceiveApplication"},
249 {2012, nullptr, "GetReceiveApplicationProgress"},
250 {2013, nullptr, "RequestSendApplication"},
251 {2014, nullptr, "GetSendApplicationProgress"},
252 {2015, nullptr, "CompareSystemDeliveryInfo"},
253 {2016, nullptr, "ListNotCommittedContentMeta"},
254 {2017, nullptr, "CreateDownloadTask"},
255 {2018, nullptr, "GetApplicationDeliveryInfoHash"},
256 {2050, nullptr, "GetApplicationRightsOnClient"},
257 {2051, nullptr, "InvalidateRightsIdCache"},
258 {2100, nullptr, "GetApplicationTerminateResult"},
259 {2101, nullptr, "GetRawApplicationTerminateResult"},
260 {2150, nullptr, "CreateRightsEnvironment"},
261 {2151, nullptr, "DestroyRightsEnvironment"},
262 {2152, nullptr, "ActivateRightsEnvironment"},
263 {2153, nullptr, "DeactivateRightsEnvironment"},
264 {2154, nullptr, "ForceActivateRightsContextForExit"},
265 {2155, nullptr, "UpdateRightsEnvironmentStatus"},
266 {2156, nullptr, "CreateRightsEnvironmentForMicroApplication"},
267 {2160, nullptr, "AddTargetApplicationToRightsEnvironment"},
268 {2161, nullptr, "SetUsersToRightsEnvironment"},
269 {2170, nullptr, "GetRightsEnvironmentStatus"},
270 {2171, nullptr, "GetRightsEnvironmentStatusChangedEvent"},
271 {2180, nullptr, "RequestExtendRightsInRightsEnvironment"},
272 {2181, nullptr, "GetResultOfExtendRightsInRightsEnvironment"},
273 {2182, nullptr, "SetActiveRightsContextUsingStateToRightsEnvironment"},
274 {2190, nullptr, "GetRightsEnvironmentHandleForApplication"},
275 {2199, nullptr, "GetRightsEnvironmentCountForDebug"},
276 {2200, nullptr, "GetGameCardApplicationCopyIdentifier"},
277 {2201, nullptr, "GetInstalledApplicationCopyIdentifier"},
278 {2250, nullptr, "RequestReportActiveELicence"},
279 {2300, nullptr, "ListEventLog"},
280 {2350, nullptr, "PerformAutoUpdateByApplicationId"},
281 {2351, nullptr, "RequestNoDownloadRightsErrorResolution"},
282 {2352, nullptr, "RequestResolveNoDownloadRightsError"},
283 {2353, nullptr, "GetApplicationDownloadTaskInfo"},
284 {2354, nullptr, "PrioritizeApplicationBackgroundTask"},
285 {2355, nullptr, "PreferStorageEfficientUpdate"},
286 {2356, nullptr, "RequestStorageEfficientUpdatePreferable"},
287 {2357, nullptr, "EnableMultiCoreDownload"},
288 {2358, nullptr, "DisableMultiCoreDownload"},
289 {2359, nullptr, "IsMultiCoreDownloadEnabled"},
290 {2400, nullptr, "GetPromotionInfo"},
291 {2401, nullptr, "CountPromotionInfo"},
292 {2402, nullptr, "ListPromotionInfo"},
293 {2403, nullptr, "ImportPromotionJsonForDebug"},
294 {2404, nullptr, "ClearPromotionInfoForDebug"},
295 {2500, nullptr, "ConfirmAvailableTime"},
296 {2510, nullptr, "CreateApplicationResource"},
297 {2511, nullptr, "GetApplicationResource"},
298 {2513, nullptr, "LaunchMicroApplication"},
299 {2514, nullptr, "ClearTaskOfAsyncTaskManager"},
300 {2515, nullptr, "CleanupAllPlaceHolderAndFragmentsIfNoTask"},
301 {2516, nullptr, "EnsureApplicationCertificate"},
302 {2517, nullptr, "CreateApplicationInstance"},
303 {2518, nullptr, "UpdateQualificationForDebug"},
304 {2519, nullptr, "IsQualificationTransitionSupported"},
305 {2520, nullptr, "IsQualificationTransitionSupportedByProcessId"},
306 {2521, nullptr, "GetRightsUserChangedEvent"},
307 {2522, nullptr, "IsRomRedirectionAvailable"},
308 {2800, nullptr, "GetApplicationIdOfPreomia"},
309 {3000, nullptr, "RegisterDeviceLockKey"},
310 {3001, nullptr, "UnregisterDeviceLockKey"},
311 {3002, nullptr, "VerifyDeviceLockKey"},
312 {3003, nullptr, "HideApplicationIcon"},
313 {3004, nullptr, "ShowApplicationIcon"},
314 {3005, nullptr, "HideApplicationTitle"},
315 {3006, nullptr, "ShowApplicationTitle"},
316 {3007, nullptr, "EnableGameCard"},
317 {3008, nullptr, "DisableGameCard"},
318 {3009, nullptr, "EnableLocalContentShare"},
319 {3010, nullptr, "DisableLocalContentShare"},
320 {3011, nullptr, "IsApplicationIconHidden"},
321 {3012, nullptr, "IsApplicationTitleHidden"},
322 {3013, nullptr, "IsGameCardEnabled"},
323 {3014, nullptr, "IsLocalContentShareEnabled"},
324 {3050, nullptr, "ListAssignELicenseTaskResult"},
325 {9999, nullptr, "GetApplicationCertificate"},
326 };
327 // clang-format on
328
329 RegisterHandlers(functions);
330}
331
332IApplicationManagerInterface::~IApplicationManagerInterface() = default;
333
334void IApplicationManagerInterface::GetApplicationControlData(HLERequestContext& ctx) {
335 IPC::RequestParser rp{ctx};
336 const auto flag = rp.PopRaw<u64>();
337 LOG_DEBUG(Service_NS, "called with flag={:016X}", flag);
338
339 const auto title_id = rp.PopRaw<u64>();
340
341 const auto size = ctx.GetWriteBufferSize();
342
343 const FileSys::PatchManager pm{title_id, system.GetFileSystemController(),
344 system.GetContentProvider()};
345 const auto control = pm.GetControlMetadata();
346
347 std::vector<u8> out;
348
349 if (control.first != nullptr) {
350 if (size < 0x4000) {
351 LOG_ERROR(Service_NS,
352 "output buffer is too small! (actual={:016X}, expected_min=0x4000)", size);
353 IPC::ResponseBuilder rb{ctx, 2};
354 // TODO(DarkLordZach): Find a better error code for this.
355 rb.Push(ResultUnknown);
356 return;
357 }
358
359 out.resize(0x4000);
360 const auto bytes = control.first->GetRawBytes();
361 std::memcpy(out.data(), bytes.data(), bytes.size());
362 } else {
363 LOG_WARNING(Service_NS, "missing NACP data for title_id={:016X}, defaulting to zeros.",
364 title_id);
365 out.resize(std::min<u64>(0x4000, size));
366 }
367
368 if (control.second != nullptr) {
369 if (size < 0x4000 + control.second->GetSize()) {
370 LOG_ERROR(Service_NS,
371 "output buffer is too small! (actual={:016X}, expected_min={:016X})", size,
372 0x4000 + control.second->GetSize());
373 IPC::ResponseBuilder rb{ctx, 2};
374 // TODO(DarkLordZach): Find a better error code for this.
375 rb.Push(ResultUnknown);
376 return;
377 }
378
379 out.resize(0x4000 + control.second->GetSize());
380 control.second->Read(out.data() + 0x4000, control.second->GetSize());
381 } else {
382 LOG_WARNING(Service_NS, "missing icon data for title_id={:016X}, defaulting to zeros.",
383 title_id);
384 }
385
386 ctx.WriteBuffer(out);
387
388 IPC::ResponseBuilder rb{ctx, 3};
389 rb.Push(ResultSuccess);
390 rb.Push<u32>(static_cast<u32>(out.size()));
391}
392
393void IApplicationManagerInterface::GetApplicationDesiredLanguage(HLERequestContext& ctx) {
394 IPC::RequestParser rp{ctx};
395 const auto supported_languages = rp.Pop<u32>();
396
397 u8 desired_language{};
398 const auto res = GetApplicationDesiredLanguage(&desired_language, supported_languages);
399 if (res == ResultSuccess) {
400 IPC::ResponseBuilder rb{ctx, 3};
401 rb.Push(ResultSuccess);
402 rb.Push<u32>(desired_language);
403 } else {
404 IPC::ResponseBuilder rb{ctx, 2};
405 rb.Push(res);
406 }
407}
408
409Result IApplicationManagerInterface::GetApplicationDesiredLanguage(u8* out_desired_language,
410 const u32 supported_languages) {
411 LOG_DEBUG(Service_NS, "called with supported_languages={:08X}", supported_languages);
412
413 // Get language code from settings
414 const auto language_code =
415 Set::GetLanguageCodeFromIndex(static_cast<s32>(Settings::values.language_index.GetValue()));
416
417 // Convert to application language, get priority list
418 const auto application_language = ConvertToApplicationLanguage(language_code);
419 if (application_language == std::nullopt) {
420 LOG_ERROR(Service_NS, "Could not convert application language! language_code={}",
421 language_code);
422 return Service::NS::ResultApplicationLanguageNotFound;
423 }
424 const auto priority_list = GetApplicationLanguagePriorityList(*application_language);
425 if (!priority_list) {
426 LOG_ERROR(Service_NS,
427 "Could not find application language priorities! application_language={}",
428 *application_language);
429 return Service::NS::ResultApplicationLanguageNotFound;
430 }
431
432 // Try to find a valid language.
433 for (const auto lang : *priority_list) {
434 const auto supported_flag = GetSupportedLanguageFlag(lang);
435 if (supported_languages == 0 || (supported_languages & supported_flag) == supported_flag) {
436 *out_desired_language = static_cast<u8>(lang);
437 return ResultSuccess;
438 }
439 }
440
441 LOG_ERROR(Service_NS, "Could not find a valid language! supported_languages={:08X}",
442 supported_languages);
443 return Service::NS::ResultApplicationLanguageNotFound;
444}
445
446void IApplicationManagerInterface::ConvertApplicationLanguageToLanguageCode(
447 HLERequestContext& ctx) {
448 IPC::RequestParser rp{ctx};
449 const auto application_language = rp.Pop<u8>();
450
451 u64 language_code{};
452 const auto res = ConvertApplicationLanguageToLanguageCode(&language_code, application_language);
453 if (res == ResultSuccess) {
454 IPC::ResponseBuilder rb{ctx, 4};
455 rb.Push(ResultSuccess);
456 rb.Push(language_code);
457 } else {
458 IPC::ResponseBuilder rb{ctx, 2};
459 rb.Push(res);
460 }
461}
462
463Result IApplicationManagerInterface::ConvertApplicationLanguageToLanguageCode(
464 u64* out_language_code, u8 application_language) {
465 const auto language_code =
466 ConvertToLanguageCode(static_cast<ApplicationLanguage>(application_language));
467 if (language_code == std::nullopt) {
468 LOG_ERROR(Service_NS, "Language not found! application_language={}", application_language);
469 return Service::NS::ResultApplicationLanguageNotFound;
470 }
471
472 *out_language_code = static_cast<u64>(*language_code);
473 return ResultSuccess;
474}
475
476IApplicationVersionInterface::IApplicationVersionInterface(Core::System& system_)
477 : ServiceFramework{system_, "IApplicationVersionInterface"} {
478 // clang-format off
479 static const FunctionInfo functions[] = {
480 {0, nullptr, "GetLaunchRequiredVersion"},
481 {1, nullptr, "UpgradeLaunchRequiredVersion"},
482 {35, nullptr, "UpdateVersionList"},
483 {36, nullptr, "PushLaunchVersion"},
484 {37, nullptr, "ListRequiredVersion"},
485 {800, nullptr, "RequestVersionList"},
486 {801, nullptr, "ListVersionList"},
487 {802, nullptr, "RequestVersionListData"},
488 {900, nullptr, "ImportAutoUpdatePolicyJsonForDebug"},
489 {901, nullptr, "ListDefaultAutoUpdatePolicy"},
490 {902, nullptr, "ListAutoUpdatePolicyForSpecificApplication"},
491 {1000, nullptr, "PerformAutoUpdate"},
492 {1001, nullptr, "ListAutoUpdateSchedule"},
493 };
494 // clang-format on
495
496 RegisterHandlers(functions);
497}
498
499IApplicationVersionInterface::~IApplicationVersionInterface() = default;
500
501IContentManagementInterface::IContentManagementInterface(Core::System& system_)
502 : ServiceFramework{system_, "IContentManagementInterface"} {
503 // clang-format off
504 static const FunctionInfo functions[] = {
505 {11, nullptr, "CalculateApplicationOccupiedSize"},
506 {43, nullptr, "CheckSdCardMountStatus"},
507 {47, &IContentManagementInterface::GetTotalSpaceSize, "GetTotalSpaceSize"},
508 {48, &IContentManagementInterface::GetFreeSpaceSize, "GetFreeSpaceSize"},
509 {600, nullptr, "CountApplicationContentMeta"},
510 {601, nullptr, "ListApplicationContentMetaStatus"},
511 {605, nullptr, "ListApplicationContentMetaStatusWithRightsCheck"},
512 {607, nullptr, "IsAnyApplicationRunning"},
513 };
514 // clang-format on
515
516 RegisterHandlers(functions);
517}
518
519IContentManagementInterface::~IContentManagementInterface() = default;
520
521void IContentManagementInterface::GetTotalSpaceSize(HLERequestContext& ctx) {
522 IPC::RequestParser rp{ctx};
523 const auto storage{rp.PopEnum<FileSys::StorageId>()};
524
525 LOG_INFO(Service_Capture, "called, storage={}", storage);
526
527 IPC::ResponseBuilder rb{ctx, 4};
528 rb.Push(ResultSuccess);
529 rb.Push<u64>(system.GetFileSystemController().GetTotalSpaceSize(storage));
530}
531
532void IContentManagementInterface::GetFreeSpaceSize(HLERequestContext& ctx) {
533 IPC::RequestParser rp{ctx};
534 const auto storage{rp.PopEnum<FileSys::StorageId>()};
535
536 LOG_INFO(Service_Capture, "called, storage={}", storage);
537
538 IPC::ResponseBuilder rb{ctx, 4};
539 rb.Push(ResultSuccess);
540 rb.Push<u64>(system.GetFileSystemController().GetFreeSpaceSize(storage));
541}
542
543IDocumentInterface::IDocumentInterface(Core::System& system_)
544 : ServiceFramework{system_, "IDocumentInterface"} {
545 // clang-format off
546 static const FunctionInfo functions[] = {
547 {21, nullptr, "GetApplicationContentPath"},
548 {23, &IDocumentInterface::ResolveApplicationContentPath, "ResolveApplicationContentPath"},
549 {92, &IDocumentInterface::GetRunningApplicationProgramId, "GetRunningApplicationProgramId"},
550 };
551 // clang-format on
552
553 RegisterHandlers(functions);
554}
555
556IDocumentInterface::~IDocumentInterface() = default;
557
558void IDocumentInterface::ResolveApplicationContentPath(HLERequestContext& ctx) {
559 struct ContentPath {
560 u8 file_system_proxy_type;
561 u64 program_id;
562 };
563 static_assert(sizeof(ContentPath) == 0x10, "ContentPath has wrong size");
564
565 IPC::RequestParser rp{ctx};
566 auto content_path = rp.PopRaw<ContentPath>();
567 LOG_WARNING(Service_NS, "(STUBBED) called, file_system_proxy_type={}, program_id={:016X}",
568 content_path.file_system_proxy_type, content_path.program_id);
569
570 IPC::ResponseBuilder rb{ctx, 2};
571 rb.Push(ResultSuccess);
572}
573
574void IDocumentInterface::GetRunningApplicationProgramId(HLERequestContext& ctx) {
575 IPC::RequestParser rp{ctx};
576 const auto caller_program_id = rp.PopRaw<u64>();
577 LOG_WARNING(Service_NS, "(STUBBED) called, caller_program_id={:016X}", caller_program_id);
578
579 IPC::ResponseBuilder rb{ctx, 4};
580 rb.Push(ResultSuccess);
581 rb.Push<u64>(system.GetApplicationProcessProgramID());
582}
583
584IDownloadTaskInterface::IDownloadTaskInterface(Core::System& system_)
585 : ServiceFramework{system_, "IDownloadTaskInterface"} {
586 // clang-format off
587 static const FunctionInfo functions[] = {
588 {701, nullptr, "ClearTaskStatusList"},
589 {702, nullptr, "RequestDownloadTaskList"},
590 {703, nullptr, "RequestEnsureDownloadTask"},
591 {704, nullptr, "ListDownloadTaskStatus"},
592 {705, nullptr, "RequestDownloadTaskListData"},
593 {706, nullptr, "TryCommitCurrentApplicationDownloadTask"},
594 {707, nullptr, "EnableAutoCommit"},
595 {708, nullptr, "DisableAutoCommit"},
596 {709, nullptr, "TriggerDynamicCommitEvent"},
597 };
598 // clang-format on
599
600 RegisterHandlers(functions);
601}
602
603IDownloadTaskInterface::~IDownloadTaskInterface() = default;
604
605IECommerceInterface::IECommerceInterface(Core::System& system_)
606 : ServiceFramework{system_, "IECommerceInterface"} {
607 // clang-format off
608 static const FunctionInfo functions[] = {
609 {0, nullptr, "RequestLinkDevice"},
610 {1, nullptr, "RequestCleanupAllPreInstalledApplications"},
611 {2, nullptr, "RequestCleanupPreInstalledApplication"},
612 {3, nullptr, "RequestSyncRights"},
613 {4, nullptr, "RequestUnlinkDevice"},
614 {5, nullptr, "RequestRevokeAllELicense"},
615 {6, nullptr, "RequestSyncRightsBasedOnAssignedELicenses"},
616 };
617 // clang-format on
618
619 RegisterHandlers(functions);
620}
621
622IECommerceInterface::~IECommerceInterface() = default;
623
624IFactoryResetInterface::IFactoryResetInterface(Core::System& system_)
625 : ServiceFramework{system_, "IFactoryResetInterface"} {
626 // clang-format off
627 static const FunctionInfo functions[] = {
628 {100, nullptr, "ResetToFactorySettings"},
629 {101, nullptr, "ResetToFactorySettingsWithoutUserSaveData"},
630 {102, nullptr, "ResetToFactorySettingsForRefurbishment"},
631 {103, nullptr, "ResetToFactorySettingsWithPlatformRegion"},
632 {104, nullptr, "ResetToFactorySettingsWithPlatformRegionAuthentication"},
633 {105, nullptr, "RequestResetToFactorySettingsSecurely"},
634 {106, nullptr, "RequestResetToFactorySettingsWithPlatformRegionAuthenticationSecurely"},
635 };
636 // clang-format on
637
638 RegisterHandlers(functions);
639}
640
641IFactoryResetInterface::~IFactoryResetInterface() = default;
642
643IReadOnlyApplicationRecordInterface::IReadOnlyApplicationRecordInterface(Core::System& system_)
644 : ServiceFramework{system_, "IReadOnlyApplicationRecordInterface"} {
645 static const FunctionInfo functions[] = {
646 {0, &IReadOnlyApplicationRecordInterface::HasApplicationRecord, "HasApplicationRecord"},
647 {1, nullptr, "NotifyApplicationFailure"},
648 {2, &IReadOnlyApplicationRecordInterface::IsDataCorruptedResult, "IsDataCorruptedResult"},
649 };
650 // clang-format on
651
652 RegisterHandlers(functions);
653}
654
655IReadOnlyApplicationRecordInterface::~IReadOnlyApplicationRecordInterface() = default;
656
657void IReadOnlyApplicationRecordInterface::HasApplicationRecord(HLERequestContext& ctx) {
658 IPC::RequestParser rp{ctx};
659 const u64 program_id = rp.PopRaw<u64>();
660 LOG_WARNING(Service_NS, "(STUBBED) called, program_id={:X}", program_id);
661
662 IPC::ResponseBuilder rb{ctx, 3};
663 rb.Push(ResultSuccess);
664 rb.Push<u8>(1);
665}
666
667void IReadOnlyApplicationRecordInterface::IsDataCorruptedResult(HLERequestContext& ctx) {
668 IPC::RequestParser rp{ctx};
669 const auto result = rp.PopRaw<Result>();
670 LOG_WARNING(Service_NS, "(STUBBED) called, result={:#x}", result.GetInnerValue());
671
672 IPC::ResponseBuilder rb{ctx, 3};
673 rb.Push(ResultSuccess);
674 rb.Push<u8>(0);
675}
676
677IReadOnlyApplicationControlDataInterface::IReadOnlyApplicationControlDataInterface(
678 Core::System& system_)
679 : ServiceFramework{system_, "IReadOnlyApplicationControlDataInterface"} {
680 // clang-format off
681 static const FunctionInfo functions[] = {
682 {0, &IReadOnlyApplicationControlDataInterface::GetApplicationControlData, "GetApplicationControlData"},
683 {1, nullptr, "GetApplicationDesiredLanguage"},
684 {2, nullptr, "ConvertApplicationLanguageToLanguageCode"},
685 {3, nullptr, "ConvertLanguageCodeToApplicationLanguage"},
686 {4, nullptr, "SelectApplicationDesiredLanguage"},
687 };
688 // clang-format on
689
690 RegisterHandlers(functions);
691}
692
693IReadOnlyApplicationControlDataInterface::~IReadOnlyApplicationControlDataInterface() = default;
694
695void IReadOnlyApplicationControlDataInterface::GetApplicationControlData(HLERequestContext& ctx) {
696 enum class ApplicationControlSource : u8 {
697 CacheOnly,
698 Storage,
699 StorageOnly,
700 };
701
702 struct RequestParameters {
703 ApplicationControlSource source;
704 u64 application_id;
705 };
706 static_assert(sizeof(RequestParameters) == 0x10, "RequestParameters has incorrect size.");
707
708 IPC::RequestParser rp{ctx};
709 std::vector<u8> nacp_data{};
710 const auto parameters{rp.PopRaw<RequestParameters>()};
711 const auto result =
712 system.GetARPManager().GetControlProperty(&nacp_data, parameters.application_id);
713
714 if (result == ResultSuccess) {
715 ctx.WriteBuffer(nacp_data.data(), nacp_data.size());
716 }
717
718 IPC::ResponseBuilder rb{ctx, 2};
719 rb.Push(result);
720}
721
722NS::NS(const char* name, Core::System& system_) : ServiceFramework{system_, name} {
723 // clang-format off
724 static const FunctionInfo functions[] = {
725 {7988, nullptr, "GetDynamicRightsInterface"},
726 {7989, &NS::PushInterface<IReadOnlyApplicationControlDataInterface>, "GetReadOnlyApplicationControlDataInterface"},
727 {7991, &NS::PushInterface<IReadOnlyApplicationRecordInterface>, "GetReadOnlyApplicationRecordInterface"},
728 {7992, &NS::PushInterface<IECommerceInterface>, "GetECommerceInterface"},
729 {7993, &NS::PushInterface<IApplicationVersionInterface>, "GetApplicationVersionInterface"},
730 {7994, &NS::PushInterface<IFactoryResetInterface>, "GetFactoryResetInterface"},
731 {7995, &NS::PushInterface<IAccountProxyInterface>, "GetAccountProxyInterface"},
732 {7996, &NS::PushIApplicationManagerInterface, "GetApplicationManagerInterface"},
733 {7997, &NS::PushInterface<IDownloadTaskInterface>, "GetDownloadTaskInterface"},
734 {7998, &NS::PushInterface<IContentManagementInterface>, "GetContentManagementInterface"},
735 {7999, &NS::PushInterface<IDocumentInterface>, "GetDocumentInterface"},
736 };
737 // clang-format on
738
739 RegisterHandlers(functions);
740}
741
742NS::~NS() = default;
743
744std::shared_ptr<IApplicationManagerInterface> NS::GetApplicationManagerInterface() const {
745 return GetInterface<IApplicationManagerInterface>(system);
746}
747
748class NS_DEV final : public ServiceFramework<NS_DEV> {
749public:
750 explicit NS_DEV(Core::System& system_) : ServiceFramework{system_, "ns:dev"} {
751 // clang-format off
752 static const FunctionInfo functions[] = {
753 {0, nullptr, "LaunchProgram"},
754 {1, nullptr, "TerminateProcess"},
755 {2, nullptr, "TerminateProgram"},
756 {4, nullptr, "GetShellEvent"},
757 {5, nullptr, "GetShellEventInfo"},
758 {6, nullptr, "TerminateApplication"},
759 {7, nullptr, "PrepareLaunchProgramFromHost"},
760 {8, nullptr, "LaunchApplicationFromHost"},
761 {9, nullptr, "LaunchApplicationWithStorageIdForDevelop"},
762 {10, nullptr, "IsSystemMemoryResourceLimitBoosted"},
763 {11, nullptr, "GetRunningApplicationProcessIdForDevelop"},
764 {12, nullptr, "SetCurrentApplicationRightsEnvironmentCanBeActiveForDevelop"},
765 {13, nullptr, "CreateApplicationResourceForDevelop"},
766 {14, nullptr, "IsPreomiaForDevelop"},
767 {15, nullptr, "GetApplicationProgramIdFromHost"},
768 {16, nullptr, "RefreshCachedDebugValues"},
769 {17, nullptr, "PrepareLaunchApplicationFromHost"},
770 {18, nullptr, "GetLaunchEvent"},
771 {19, nullptr, "GetLaunchResult"},
772 };
773 // clang-format on
774
775 RegisterHandlers(functions);
776 }
777};
778
779class ISystemUpdateControl final : public ServiceFramework<ISystemUpdateControl> {
780public:
781 explicit ISystemUpdateControl(Core::System& system_)
782 : ServiceFramework{system_, "ISystemUpdateControl"} {
783 // clang-format off
784 static const FunctionInfo functions[] = {
785 {0, nullptr, "HasDownloaded"},
786 {1, nullptr, "RequestCheckLatestUpdate"},
787 {2, nullptr, "RequestDownloadLatestUpdate"},
788 {3, nullptr, "GetDownloadProgress"},
789 {4, nullptr, "ApplyDownloadedUpdate"},
790 {5, nullptr, "RequestPrepareCardUpdate"},
791 {6, nullptr, "GetPrepareCardUpdateProgress"},
792 {7, nullptr, "HasPreparedCardUpdate"},
793 {8, nullptr, "ApplyCardUpdate"},
794 {9, nullptr, "GetDownloadedEulaDataSize"},
795 {10, nullptr, "GetDownloadedEulaData"},
796 {11, nullptr, "SetupCardUpdate"},
797 {12, nullptr, "GetPreparedCardUpdateEulaDataSize"},
798 {13, nullptr, "GetPreparedCardUpdateEulaData"},
799 {14, nullptr, "SetupCardUpdateViaSystemUpdater"},
800 {15, nullptr, "HasReceived"},
801 {16, nullptr, "RequestReceiveSystemUpdate"},
802 {17, nullptr, "GetReceiveProgress"},
803 {18, nullptr, "ApplyReceivedUpdate"},
804 {19, nullptr, "GetReceivedEulaDataSize"},
805 {20, nullptr, "GetReceivedEulaData"},
806 {21, nullptr, "SetupToReceiveSystemUpdate"},
807 {22, nullptr, "RequestCheckLatestUpdateIncludesRebootlessUpdate"},
808 };
809 // clang-format on
810
811 RegisterHandlers(functions);
812 }
813};
814
815class NS_SU final : public ServiceFramework<NS_SU> {
816public:
817 explicit NS_SU(Core::System& system_) : ServiceFramework{system_, "ns:su"} {
818 // clang-format off
819 static const FunctionInfo functions[] = {
820 {0, nullptr, "GetBackgroundNetworkUpdateState"},
821 {1, &NS_SU::OpenSystemUpdateControl, "OpenSystemUpdateControl"},
822 {2, nullptr, "NotifyExFatDriverRequired"},
823 {3, nullptr, "ClearExFatDriverStatusForDebug"},
824 {4, nullptr, "RequestBackgroundNetworkUpdate"},
825 {5, nullptr, "NotifyBackgroundNetworkUpdate"},
826 {6, nullptr, "NotifyExFatDriverDownloadedForDebug"},
827 {9, nullptr, "GetSystemUpdateNotificationEventForContentDelivery"},
828 {10, nullptr, "NotifySystemUpdateForContentDelivery"},
829 {11, nullptr, "PrepareShutdown"},
830 {12, nullptr, "Unknown12"},
831 {13, nullptr, "Unknown13"},
832 {14, nullptr, "Unknown14"},
833 {15, nullptr, "Unknown15"},
834 {16, nullptr, "DestroySystemUpdateTask"},
835 {17, nullptr, "RequestSendSystemUpdate"},
836 {18, nullptr, "GetSendSystemUpdateProgress"},
837 };
838 // clang-format on
839
840 RegisterHandlers(functions);
841 }
842
843private:
844 void OpenSystemUpdateControl(HLERequestContext& ctx) {
845 LOG_DEBUG(Service_NS, "called");
846
847 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
848 rb.Push(ResultSuccess);
849 rb.PushIpcInterface<ISystemUpdateControl>(system);
850 }
851};
852
853class NS_VM final : public ServiceFramework<NS_VM> {
854public:
855 explicit NS_VM(Core::System& system_) : ServiceFramework{system_, "ns:vm"} {
856 // clang-format off
857 static const FunctionInfo functions[] = {
858 {1200, &NS_VM::NeedsUpdateVulnerability, "NeedsUpdateVulnerability"},
859 {1201, nullptr, "UpdateSafeSystemVersionForDebug"},
860 {1202, nullptr, "GetSafeSystemVersion"},
861 };
862 // clang-format on
863
864 RegisterHandlers(functions);
865 }
866
867private:
868 void NeedsUpdateVulnerability(HLERequestContext& ctx) {
869 LOG_WARNING(Service_NS, "(STUBBED) called");
870
871 IPC::ResponseBuilder rb{ctx, 3};
872 rb.Push(ResultSuccess);
873 rb.Push(false);
874 }
875};
876
877void LoopProcess(Core::System& system) { 15void LoopProcess(Core::System& system) {
878 auto server_manager = std::make_unique<ServerManager>(system); 16 auto server_manager = std::make_unique<ServerManager>(system);
879 17
880 server_manager->RegisterNamedService("ns:am2", std::make_shared<NS>("ns:am2", system)); 18 server_manager->RegisterNamedService(
881 server_manager->RegisterNamedService("ns:ec", std::make_shared<NS>("ns:ec", system)); 19 "ns:am2", std::make_shared<IServiceGetterInterface>(system, "ns:am2"));
882 server_manager->RegisterNamedService("ns:rid", std::make_shared<NS>("ns:rid", system)); 20 server_manager->RegisterNamedService(
883 server_manager->RegisterNamedService("ns:rt", std::make_shared<NS>("ns:rt", system)); 21 "ns:ec", std::make_shared<IServiceGetterInterface>(system, "ns:ec"));
884 server_manager->RegisterNamedService("ns:web", std::make_shared<NS>("ns:web", system)); 22 server_manager->RegisterNamedService(
885 server_manager->RegisterNamedService("ns:ro", std::make_shared<NS>("ns:ro", system)); 23 "ns:rid", std::make_shared<IServiceGetterInterface>(system, "ns:rid"));
886 24 server_manager->RegisterNamedService(
887 server_manager->RegisterNamedService("ns:dev", std::make_shared<NS_DEV>(system)); 25 "ns:rt", std::make_shared<IServiceGetterInterface>(system, "ns:rt"));
888 server_manager->RegisterNamedService("ns:su", std::make_shared<NS_SU>(system)); 26 server_manager->RegisterNamedService(
889 server_manager->RegisterNamedService("ns:vm", std::make_shared<NS_VM>(system)); 27 "ns:web", std::make_shared<IServiceGetterInterface>(system, "ns:web"));
890 server_manager->RegisterNamedService("pdm:qry", std::make_shared<PDM_QRY>(system)); 28 server_manager->RegisterNamedService(
29 "ns:ro", std::make_shared<IServiceGetterInterface>(system, "ns:ro"));
30
31 server_manager->RegisterNamedService("ns:dev", std::make_shared<IDevelopInterface>(system));
32 server_manager->RegisterNamedService("ns:su", std::make_shared<ISystemUpdateInterface>(system));
33 server_manager->RegisterNamedService("ns:vm",
34 std::make_shared<IVulnerabilityManagerInterface>(system));
35 server_manager->RegisterNamedService("pdm:qry", std::make_shared<IQueryService>(system));
891 36
892 server_manager->RegisterNamedService("pl:s", 37 server_manager->RegisterNamedService("pl:s",
893 std::make_shared<IPlatformServiceManager>(system, "pl:s")); 38 std::make_shared<IPlatformServiceManager>(system, "pl:s"));
diff --git a/src/core/hle/service/ns/ns.h b/src/core/hle/service/ns/ns.h
index 9ee306ef9..f79b4ae3d 100644
--- a/src/core/hle/service/ns/ns.h
+++ b/src/core/hle/service/ns/ns.h
@@ -3,141 +3,12 @@
3 3
4#pragma once 4#pragma once
5 5
6#include "core/hle/service/service.h"
7
8namespace Core { 6namespace Core {
9class System; 7class System;
10} 8}
11 9
12namespace Service { 10namespace Service::NS {
13
14namespace FileSystem {
15class FileSystemController;
16} // namespace FileSystem
17
18namespace NS {
19
20class IAccountProxyInterface final : public ServiceFramework<IAccountProxyInterface> {
21public:
22 explicit IAccountProxyInterface(Core::System& system_);
23 ~IAccountProxyInterface() override;
24};
25
26class IApplicationManagerInterface final : public ServiceFramework<IApplicationManagerInterface> {
27public:
28 explicit IApplicationManagerInterface(Core::System& system_);
29 ~IApplicationManagerInterface() override;
30
31 Result GetApplicationDesiredLanguage(u8* out_desired_language, u32 supported_languages);
32 Result ConvertApplicationLanguageToLanguageCode(u64* out_language_code,
33 u8 application_language);
34
35private:
36 void GetApplicationControlData(HLERequestContext& ctx);
37 void GetApplicationDesiredLanguage(HLERequestContext& ctx);
38 void ConvertApplicationLanguageToLanguageCode(HLERequestContext& ctx);
39};
40
41class IApplicationVersionInterface final : public ServiceFramework<IApplicationVersionInterface> {
42public:
43 explicit IApplicationVersionInterface(Core::System& system_);
44 ~IApplicationVersionInterface() override;
45};
46
47class IContentManagementInterface final : public ServiceFramework<IContentManagementInterface> {
48public:
49 explicit IContentManagementInterface(Core::System& system_);
50 ~IContentManagementInterface() override;
51
52private:
53 void GetTotalSpaceSize(HLERequestContext& ctx);
54 void GetFreeSpaceSize(HLERequestContext& ctx);
55};
56
57class IDocumentInterface final : public ServiceFramework<IDocumentInterface> {
58public:
59 explicit IDocumentInterface(Core::System& system_);
60 ~IDocumentInterface() override;
61
62private:
63 void ResolveApplicationContentPath(HLERequestContext& ctx);
64 void GetRunningApplicationProgramId(HLERequestContext& ctx);
65};
66
67class IDownloadTaskInterface final : public ServiceFramework<IDownloadTaskInterface> {
68public:
69 explicit IDownloadTaskInterface(Core::System& system_);
70 ~IDownloadTaskInterface() override;
71};
72
73class IECommerceInterface final : public ServiceFramework<IECommerceInterface> {
74public:
75 explicit IECommerceInterface(Core::System& system_);
76 ~IECommerceInterface() override;
77};
78
79class IFactoryResetInterface final : public ServiceFramework<IFactoryResetInterface> {
80public:
81 explicit IFactoryResetInterface(Core::System& system_);
82 ~IFactoryResetInterface() override;
83};
84
85class IReadOnlyApplicationRecordInterface final
86 : public ServiceFramework<IReadOnlyApplicationRecordInterface> {
87public:
88 explicit IReadOnlyApplicationRecordInterface(Core::System& system_);
89 ~IReadOnlyApplicationRecordInterface() override;
90
91private:
92 void HasApplicationRecord(HLERequestContext& ctx);
93 void IsDataCorruptedResult(HLERequestContext& ctx);
94};
95
96class IReadOnlyApplicationControlDataInterface final
97 : public ServiceFramework<IReadOnlyApplicationControlDataInterface> {
98public:
99 explicit IReadOnlyApplicationControlDataInterface(Core::System& system_);
100 ~IReadOnlyApplicationControlDataInterface() override;
101
102private:
103 void GetApplicationControlData(HLERequestContext& ctx);
104};
105
106class NS final : public ServiceFramework<NS> {
107public:
108 explicit NS(const char* name, Core::System& system_);
109 ~NS() override;
110
111 std::shared_ptr<IApplicationManagerInterface> GetApplicationManagerInterface() const;
112
113private:
114 template <typename T, typename... Args>
115 void PushInterface(HLERequestContext& ctx) {
116 LOG_DEBUG(Service_NS, "called");
117
118 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
119 rb.Push(ResultSuccess);
120 rb.PushIpcInterface<T>(system);
121 }
122
123 void PushIApplicationManagerInterface(HLERequestContext& ctx) {
124 LOG_DEBUG(Service_NS, "called");
125
126 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
127 rb.Push(ResultSuccess);
128 rb.PushIpcInterface<IApplicationManagerInterface>(system);
129 }
130
131 template <typename T, typename... Args>
132 std::shared_ptr<T> GetInterface(Args&&... args) const {
133 static_assert(std::is_base_of_v<SessionRequestHandler, T>,
134 "Not a base of ServiceFrameworkBase");
135
136 return std::make_shared<T>(std::forward<Args>(args)...);
137 }
138};
139 11
140void LoopProcess(Core::System& system); 12void LoopProcess(Core::System& system);
141 13
142} // namespace NS 14} // namespace Service::NS
143} // namespace Service
diff --git a/src/core/hle/service/ns/errors.h b/src/core/hle/service/ns/ns_results.h
index 16d2ea6f7..16d2ea6f7 100644
--- a/src/core/hle/service/ns/errors.h
+++ b/src/core/hle/service/ns/ns_results.h
diff --git a/src/core/hle/service/ns/ns_types.h b/src/core/hle/service/ns/ns_types.h
new file mode 100644
index 000000000..38421b0f4
--- /dev/null
+++ b/src/core/hle/service/ns/ns_types.h
@@ -0,0 +1,111 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "common/common_funcs.h"
7#include "common/uuid.h"
8#include "core/file_sys/romfs_factory.h"
9
10namespace Service::NS {
11
12enum class ApplicationRecordType : u8 {
13 Installing = 2,
14 Installed = 3,
15 GameCardNotInserted = 5,
16 Archived = 11,
17 GameCard = 16,
18};
19
20enum class ApplicationControlSource : u8 {
21 CacheOnly = 0,
22 Storage = 1,
23 StorageOnly = 2,
24};
25
26enum class BackgroundNetworkUpdateState : u8 {
27 None,
28 InProgress,
29 Ready,
30};
31
32struct ApplicationRecord {
33 u64 application_id;
34 ApplicationRecordType type;
35 u8 unknown;
36 INSERT_PADDING_BYTES_NOINIT(0x6);
37 u8 unknown2;
38 INSERT_PADDING_BYTES_NOINIT(0x7);
39};
40static_assert(sizeof(ApplicationRecord) == 0x18, "ApplicationRecord has incorrect size.");
41
42/// ApplicationView
43struct ApplicationView {
44 u64 application_id; ///< ApplicationId.
45 u32 unk; ///< Unknown.
46 u32 flags; ///< Flags.
47 std::array<u8, 0x10> unk_x10; ///< Unknown.
48 u32 unk_x20; ///< Unknown.
49 u16 unk_x24; ///< Unknown.
50 std::array<u8, 0x2> unk_x26; ///< Unknown.
51 std::array<u8, 0x8> unk_x28; ///< Unknown.
52 std::array<u8, 0x10> unk_x30; ///< Unknown.
53 u32 unk_x40; ///< Unknown.
54 u8 unk_x44; ///< Unknown.
55 std::array<u8, 0xb> unk_x45; ///< Unknown.
56};
57static_assert(sizeof(ApplicationView) == 0x50, "ApplicationView has incorrect size.");
58
59struct ApplicationRightsOnClient {
60 u64 application_id;
61 Common::UUID uid;
62 u8 flags;
63 u8 flags2;
64 INSERT_PADDING_BYTES_NOINIT(0x6);
65};
66static_assert(sizeof(ApplicationRightsOnClient) == 0x20,
67 "ApplicationRightsOnClient has incorrect size.");
68
69/// NsPromotionInfo
70struct PromotionInfo {
71 u64 start_timestamp; ///< POSIX timestamp for the promotion start.
72 u64 end_timestamp; ///< POSIX timestamp for the promotion end.
73 s64 remaining_time; ///< Remaining time until the promotion ends, in nanoseconds
74 ///< ({end_timestamp - current_time} converted to nanoseconds).
75 INSERT_PADDING_BYTES_NOINIT(0x4);
76 u8 flags; ///< Flags. Bit0: whether the PromotionInfo is valid (including bit1). Bit1 clear:
77 ///< remaining_time is set.
78 INSERT_PADDING_BYTES_NOINIT(0x3);
79};
80static_assert(sizeof(PromotionInfo) == 0x20, "PromotionInfo has incorrect size.");
81
82/// NsApplicationViewWithPromotionInfo
83struct ApplicationViewWithPromotionInfo {
84 ApplicationView view; ///< \ref NsApplicationView
85 PromotionInfo promotion; ///< \ref NsPromotionInfo
86};
87static_assert(sizeof(ApplicationViewWithPromotionInfo) == 0x70,
88 "ApplicationViewWithPromotionInfo has incorrect size.");
89
90struct ApplicationOccupiedSizeEntity {
91 FileSys::StorageId storage_id;
92 u64 app_size;
93 u64 patch_size;
94 u64 aoc_size;
95};
96static_assert(sizeof(ApplicationOccupiedSizeEntity) == 0x20,
97 "ApplicationOccupiedSizeEntity has incorrect size.");
98
99struct ApplicationOccupiedSize {
100 std::array<ApplicationOccupiedSizeEntity, 4> entities;
101};
102static_assert(sizeof(ApplicationOccupiedSize) == 0x80,
103 "ApplicationOccupiedSize has incorrect size.");
104
105struct ContentPath {
106 u8 file_system_proxy_type;
107 u64 program_id;
108};
109static_assert(sizeof(ContentPath) == 0x10, "ContentPath has incorrect size.");
110
111} // namespace Service::NS
diff --git a/src/core/hle/service/ns/pdm_qry.cpp b/src/core/hle/service/ns/pdm_qry.cpp
deleted file mode 100644
index ce0ee30e0..000000000
--- a/src/core/hle/service/ns/pdm_qry.cpp
+++ /dev/null
@@ -1,67 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <memory>
5
6#include "common/logging/log.h"
7#include "common/uuid.h"
8#include "core/hle/service/ipc_helpers.h"
9#include "core/hle/service/ns/pdm_qry.h"
10#include "core/hle/service/service.h"
11
12namespace Service::NS {
13
14PDM_QRY::PDM_QRY(Core::System& system_) : ServiceFramework{system_, "pdm:qry"} {
15 // clang-format off
16 static const FunctionInfo functions[] = {
17 {0, nullptr, "QueryAppletEvent"},
18 {1, nullptr, "QueryPlayStatistics"},
19 {2, nullptr, "QueryPlayStatisticsByUserAccountId"},
20 {3, nullptr, "QueryPlayStatisticsByNetworkServiceAccountId"},
21 {4, nullptr, "QueryPlayStatisticsByApplicationId"},
22 {5, &PDM_QRY::QueryPlayStatisticsByApplicationIdAndUserAccountId, "QueryPlayStatisticsByApplicationIdAndUserAccountId"},
23 {6, nullptr, "QueryPlayStatisticsByApplicationIdAndNetworkServiceAccountId"},
24 {7, nullptr, "QueryLastPlayTimeV0"},
25 {8, nullptr, "QueryPlayEvent"},
26 {9, nullptr, "GetAvailablePlayEventRange"},
27 {10, nullptr, "QueryAccountEvent"},
28 {11, nullptr, "QueryAccountPlayEvent"},
29 {12, nullptr, "GetAvailableAccountPlayEventRange"},
30 {13, nullptr, "QueryApplicationPlayStatisticsForSystemV0"},
31 {14, nullptr, "QueryRecentlyPlayedApplication"},
32 {15, nullptr, "GetRecentlyPlayedApplicationUpdateEvent"},
33 {16, nullptr, "QueryApplicationPlayStatisticsByUserAccountIdForSystemV0"},
34 {17, nullptr, "QueryLastPlayTime"},
35 {18, nullptr, "QueryApplicationPlayStatisticsForSystem"},
36 {19, nullptr, "QueryApplicationPlayStatisticsByUserAccountIdForSystem"},
37 };
38 // clang-format on
39
40 RegisterHandlers(functions);
41}
42
43PDM_QRY::~PDM_QRY() = default;
44
45void PDM_QRY::QueryPlayStatisticsByApplicationIdAndUserAccountId(HLERequestContext& ctx) {
46 IPC::RequestParser rp{ctx};
47 const auto unknown = rp.Pop<bool>();
48 rp.Pop<u8>(); // Padding
49 const auto application_id = rp.Pop<u64>();
50 const auto user_account_uid = rp.PopRaw<Common::UUID>();
51
52 // TODO(German77): Read statistics of the game
53 PlayStatistics statistics{
54 .application_id = application_id,
55 .total_launches = 1,
56 };
57
58 LOG_WARNING(Service_NS,
59 "(STUBBED) called. unknown={}. application_id=0x{:016X}, user_account_uid=0x{}",
60 unknown, application_id, user_account_uid.RawString());
61
62 IPC::ResponseBuilder rb{ctx, 12};
63 rb.Push(ResultSuccess);
64 rb.PushRaw(statistics);
65}
66
67} // namespace Service::NS
diff --git a/src/core/hle/service/ns/iplatform_service_manager.cpp b/src/core/hle/service/ns/platform_service_manager.cpp
index 46268be95..23cf05005 100644
--- a/src/core/hle/service/ns/iplatform_service_manager.cpp
+++ b/src/core/hle/service/ns/platform_service_manager.cpp
@@ -18,9 +18,9 @@
18#include "core/hle/kernel/k_shared_memory.h" 18#include "core/hle/kernel/k_shared_memory.h"
19#include "core/hle/kernel/kernel.h" 19#include "core/hle/kernel/kernel.h"
20#include "core/hle/kernel/physical_memory.h" 20#include "core/hle/kernel/physical_memory.h"
21#include "core/hle/service/cmif_serialization.h"
21#include "core/hle/service/filesystem/filesystem.h" 22#include "core/hle/service/filesystem/filesystem.h"
22#include "core/hle/service/ipc_helpers.h" 23#include "core/hle/service/ns/platform_service_manager.h"
23#include "core/hle/service/ns/iplatform_service_manager.h"
24 24
25namespace Service::NS { 25namespace Service::NS {
26 26
@@ -37,11 +37,6 @@ constexpr u32 EXPECTED_MAGIC{0x36f81a1e}; // What we expect the encrypted bfttf
37constexpr u64 SHARED_FONT_MEM_SIZE{0x1100000}; 37constexpr u64 SHARED_FONT_MEM_SIZE{0x1100000};
38constexpr FontRegion EMPTY_REGION{0, 0}; 38constexpr FontRegion EMPTY_REGION{0, 0};
39 39
40enum class LoadState : u32 {
41 Loading = 0,
42 Done = 1,
43};
44
45static void DecryptSharedFont(const std::vector<u32>& input, Kernel::PhysicalMemory& output, 40static void DecryptSharedFont(const std::vector<u32>& input, Kernel::PhysicalMemory& output,
46 std::size_t& offset) { 41 std::size_t& offset) {
47 ASSERT_MSG(offset + (input.size() * sizeof(u32)) < SHARED_FONT_MEM_SIZE, 42 ASSERT_MSG(offset + (input.size() * sizeof(u32)) < SHARED_FONT_MEM_SIZE,
@@ -138,13 +133,13 @@ IPlatformServiceManager::IPlatformServiceManager(Core::System& system_, const ch
138 : ServiceFramework{system_, service_name_}, impl{std::make_unique<Impl>()} { 133 : ServiceFramework{system_, service_name_}, impl{std::make_unique<Impl>()} {
139 // clang-format off 134 // clang-format off
140 static const FunctionInfo functions[] = { 135 static const FunctionInfo functions[] = {
141 {0, &IPlatformServiceManager::RequestLoad, "RequestLoad"}, 136 {0, D<&IPlatformServiceManager::RequestLoad>, "RequestLoad"},
142 {1, &IPlatformServiceManager::GetLoadState, "GetLoadState"}, 137 {1, D<&IPlatformServiceManager::GetLoadState>, "GetLoadState"},
143 {2, &IPlatformServiceManager::GetSize, "GetSize"}, 138 {2, D<&IPlatformServiceManager::GetSize>, "GetSize"},
144 {3, &IPlatformServiceManager::GetSharedMemoryAddressOffset, "GetSharedMemoryAddressOffset"}, 139 {3, D<&IPlatformServiceManager::GetSharedMemoryAddressOffset>, "GetSharedMemoryAddressOffset"},
145 {4, &IPlatformServiceManager::GetSharedMemoryNativeHandle, "GetSharedMemoryNativeHandle"}, 140 {4, D<&IPlatformServiceManager::GetSharedMemoryNativeHandle>, "GetSharedMemoryNativeHandle"},
146 {5, &IPlatformServiceManager::GetSharedFontInOrderOfPriority, "GetSharedFontInOrderOfPriority"}, 141 {5, D<&IPlatformServiceManager::GetSharedFontInOrderOfPriority>, "GetSharedFontInOrderOfPriority"},
147 {6, &IPlatformServiceManager::GetSharedFontInOrderOfPriority, "GetSharedFontInOrderOfPriorityForSystem"}, 142 {6, D<&IPlatformServiceManager::GetSharedFontInOrderOfPriority>, "GetSharedFontInOrderOfPriorityForSystem"},
148 {100, nullptr, "RequestApplicationFunctionAuthorization"}, 143 {100, nullptr, "RequestApplicationFunctionAuthorization"},
149 {101, nullptr, "RequestApplicationFunctionAuthorizationByProcessId"}, 144 {101, nullptr, "RequestApplicationFunctionAuthorizationByProcessId"},
150 {102, nullptr, "RequestApplicationFunctionAuthorizationByApplicationId"}, 145 {102, nullptr, "RequestApplicationFunctionAuthorizationByApplicationId"},
@@ -208,47 +203,33 @@ IPlatformServiceManager::IPlatformServiceManager(Core::System& system_, const ch
208 203
209IPlatformServiceManager::~IPlatformServiceManager() = default; 204IPlatformServiceManager::~IPlatformServiceManager() = default;
210 205
211void IPlatformServiceManager::RequestLoad(HLERequestContext& ctx) { 206Result IPlatformServiceManager::RequestLoad(SharedFontType type) {
212 IPC::RequestParser rp{ctx};
213 const u32 shared_font_type{rp.Pop<u32>()};
214 // Games don't call this so all fonts should be loaded 207 // Games don't call this so all fonts should be loaded
215 LOG_DEBUG(Service_NS, "called, shared_font_type={}", shared_font_type); 208 LOG_DEBUG(Service_NS, "called, shared_font_type={}", type);
216 209 R_SUCCEED();
217 IPC::ResponseBuilder rb{ctx, 2};
218 rb.Push(ResultSuccess);
219} 210}
220 211
221void IPlatformServiceManager::GetLoadState(HLERequestContext& ctx) { 212Result IPlatformServiceManager::GetLoadState(Out<LoadState> out_load_state, SharedFontType type) {
222 IPC::RequestParser rp{ctx}; 213 LOG_DEBUG(Service_NS, "called, shared_font_type={}", type);
223 const u32 font_id{rp.Pop<u32>()}; 214 *out_load_state = LoadState::Loaded;
224 LOG_DEBUG(Service_NS, "called, font_id={}", font_id); 215 R_SUCCEED();
225
226 IPC::ResponseBuilder rb{ctx, 3};
227 rb.Push(ResultSuccess);
228 rb.Push<u32>(static_cast<u32>(LoadState::Done));
229} 216}
230 217
231void IPlatformServiceManager::GetSize(HLERequestContext& ctx) { 218Result IPlatformServiceManager::GetSize(Out<u32> out_size, SharedFontType type) {
232 IPC::RequestParser rp{ctx}; 219 LOG_DEBUG(Service_NS, "called, shared_font_type={}", type);
233 const u32 font_id{rp.Pop<u32>()}; 220 *out_size = impl->GetSharedFontRegion(static_cast<size_t>(type)).size;
234 LOG_DEBUG(Service_NS, "called, font_id={}", font_id); 221 R_SUCCEED();
235
236 IPC::ResponseBuilder rb{ctx, 3};
237 rb.Push(ResultSuccess);
238 rb.Push<u32>(impl->GetSharedFontRegion(font_id).size);
239} 222}
240 223
241void IPlatformServiceManager::GetSharedMemoryAddressOffset(HLERequestContext& ctx) { 224Result IPlatformServiceManager::GetSharedMemoryAddressOffset(Out<u32> out_shared_memory_offset,
242 IPC::RequestParser rp{ctx}; 225 SharedFontType type) {
243 const u32 font_id{rp.Pop<u32>()}; 226 LOG_DEBUG(Service_NS, "called, shared_font_type={}", type);
244 LOG_DEBUG(Service_NS, "called, font_id={}", font_id); 227 *out_shared_memory_offset = impl->GetSharedFontRegion(static_cast<size_t>(type)).offset;
245 228 R_SUCCEED();
246 IPC::ResponseBuilder rb{ctx, 3};
247 rb.Push(ResultSuccess);
248 rb.Push<u32>(impl->GetSharedFontRegion(font_id).offset);
249} 229}
250 230
251void IPlatformServiceManager::GetSharedMemoryNativeHandle(HLERequestContext& ctx) { 231Result IPlatformServiceManager::GetSharedMemoryNativeHandle(
232 OutCopyHandle<Kernel::KSharedMemory> out_shared_memory_native_handle) {
252 // Map backing memory for the font data 233 // Map backing memory for the font data
253 LOG_DEBUG(Service_NS, "called"); 234 LOG_DEBUG(Service_NS, "called");
254 235
@@ -256,50 +237,37 @@ void IPlatformServiceManager::GetSharedMemoryNativeHandle(HLERequestContext& ctx
256 std::memcpy(kernel.GetFontSharedMem().GetPointer(), impl->shared_font->data(), 237 std::memcpy(kernel.GetFontSharedMem().GetPointer(), impl->shared_font->data(),
257 impl->shared_font->size()); 238 impl->shared_font->size());
258 239
259 IPC::ResponseBuilder rb{ctx, 2, 1}; 240 // FIXME: this shouldn't belong to the kernel
260 rb.Push(ResultSuccess); 241 *out_shared_memory_native_handle = &kernel.GetFontSharedMem();
261 rb.PushCopyObjects(&kernel.GetFontSharedMem()); 242 R_SUCCEED();
262} 243}
263 244
264void IPlatformServiceManager::GetSharedFontInOrderOfPriority(HLERequestContext& ctx) { 245Result IPlatformServiceManager::GetSharedFontInOrderOfPriority(
246 OutArray<u32, BufferAttr_HipcMapAlias> out_font_codes,
247 OutArray<u32, BufferAttr_HipcMapAlias> out_font_offsets,
248 OutArray<u32, BufferAttr_HipcMapAlias> out_font_sizes, Out<bool> out_fonts_are_loaded,
249 Out<u32> out_font_count, Set::LanguageCode language_code) {
250 LOG_DEBUG(Service_NS, "called, language_code={:#x}", language_code);
251
265 // The maximum number of elements that can be returned is 6. Regardless of the available fonts 252 // The maximum number of elements that can be returned is 6. Regardless of the available fonts
266 // or buffer size. 253 // or buffer size.
267 constexpr std::size_t MaxElementCount = 6; 254 constexpr size_t MaxElementCount = 6;
268 IPC::RequestParser rp{ctx};
269 const u64 language_code{rp.Pop<u64>()}; // TODO(ogniK): Find out what this is used for
270 const std::size_t font_codes_count =
271 std::min(MaxElementCount, ctx.GetWriteBufferNumElements<u32>(0));
272 const std::size_t font_offsets_count =
273 std::min(MaxElementCount, ctx.GetWriteBufferNumElements<u32>(1));
274 const std::size_t font_sizes_count =
275 std::min(MaxElementCount, ctx.GetWriteBufferNumElements<u32>(2));
276 LOG_DEBUG(Service_NS, "called, language_code={:X}", language_code);
277
278 IPC::ResponseBuilder rb{ctx, 4};
279 std::vector<u32> font_codes;
280 std::vector<u32> font_offsets;
281 std::vector<u32> font_sizes;
282 255
283 // TODO(ogniK): Have actual priority order 256 // TODO(ogniK): Have actual priority order
284 for (std::size_t i = 0; i < impl->shared_font_regions.size(); i++) { 257 const auto max_size = std::min({MaxElementCount, out_font_codes.size(), out_font_offsets.size(),
285 font_codes.push_back(static_cast<u32>(i)); 258 out_font_sizes.size(), impl->shared_font_regions.size()});
286 auto region = impl->GetSharedFontRegion(i);
287 font_offsets.push_back(region.offset);
288 font_sizes.push_back(region.size);
289 }
290 259
291 // Resize buffers if game requests smaller size output 260 for (size_t i = 0; i < max_size; i++) {
292 font_codes.resize(std::min(font_codes.size(), font_codes_count)); 261 auto region = impl->GetSharedFontRegion(i);
293 font_offsets.resize(std::min(font_offsets.size(), font_offsets_count));
294 font_sizes.resize(std::min(font_sizes.size(), font_sizes_count));
295 262
296 ctx.WriteBuffer(font_codes, 0); 263 out_font_codes[i] = static_cast<u32>(i);
297 ctx.WriteBuffer(font_offsets, 1); 264 out_font_offsets[i] = region.offset;
298 ctx.WriteBuffer(font_sizes, 2); 265 out_font_sizes[i] = region.size;
266 }
299 267
300 rb.Push(ResultSuccess); 268 *out_fonts_are_loaded = true;
301 rb.Push<u8>(static_cast<u8>(LoadState::Done)); // Fonts Loaded 269 *out_font_count = static_cast<u32>(max_size);
302 rb.Push<u32>(static_cast<u32>(font_codes.size())); 270 R_SUCCEED();
303} 271}
304 272
305} // namespace Service::NS 273} // namespace Service::NS
diff --git a/src/core/hle/service/ns/iplatform_service_manager.h b/src/core/hle/service/ns/platform_service_manager.h
index 03071e02b..b82c385a6 100644
--- a/src/core/hle/service/ns/iplatform_service_manager.h
+++ b/src/core/hle/service/ns/platform_service_manager.h
@@ -5,7 +5,9 @@
5 5
6#include <memory> 6#include <memory>
7#include <vector> 7#include <vector>
8#include "core/hle/service/cmif_types.h"
8#include "core/hle/service/service.h" 9#include "core/hle/service/service.h"
10#include "core/hle/service/set/settings_types.h"
9 11
10namespace Service { 12namespace Service {
11 13
@@ -23,6 +25,20 @@ enum class FontArchives : u64 {
23 ChineseSimple = 0x0100000000000814, 25 ChineseSimple = 0x0100000000000814,
24}; 26};
25 27
28enum class SharedFontType : u32 {
29 JapanUSEuropeStandard = 0,
30 ChineseSimplified = 1,
31 ExtendedChineseSimplified = 2,
32 ChineseTraditional = 3,
33 KoreanHangul = 4,
34 NintendoExtended = 5,
35};
36
37enum class LoadState : u32 {
38 Loading = 0,
39 Loaded = 1,
40};
41
26constexpr std::array<std::pair<FontArchives, const char*>, 7> SHARED_FONTS{ 42constexpr std::array<std::pair<FontArchives, const char*>, 7> SHARED_FONTS{
27 std::make_pair(FontArchives::Standard, "nintendo_udsg-r_std_003.bfttf"), 43 std::make_pair(FontArchives::Standard, "nintendo_udsg-r_std_003.bfttf"),
28 std::make_pair(FontArchives::ChineseSimple, "nintendo_udsg-r_org_zh-cn_003.bfttf"), 44 std::make_pair(FontArchives::ChineseSimple, "nintendo_udsg-r_org_zh-cn_003.bfttf"),
@@ -42,12 +58,17 @@ public:
42 ~IPlatformServiceManager() override; 58 ~IPlatformServiceManager() override;
43 59
44private: 60private:
45 void RequestLoad(HLERequestContext& ctx); 61 Result RequestLoad(SharedFontType type);
46 void GetLoadState(HLERequestContext& ctx); 62 Result GetLoadState(Out<LoadState> out_load_state, SharedFontType type);
47 void GetSize(HLERequestContext& ctx); 63 Result GetSize(Out<u32> out_size, SharedFontType type);
48 void GetSharedMemoryAddressOffset(HLERequestContext& ctx); 64 Result GetSharedMemoryAddressOffset(Out<u32> out_shared_memory_offset, SharedFontType type);
49 void GetSharedMemoryNativeHandle(HLERequestContext& ctx); 65 Result GetSharedMemoryNativeHandle(
50 void GetSharedFontInOrderOfPriority(HLERequestContext& ctx); 66 OutCopyHandle<Kernel::KSharedMemory> out_shared_memory_native_handle);
67 Result GetSharedFontInOrderOfPriority(OutArray<u32, BufferAttr_HipcMapAlias> out_font_codes,
68 OutArray<u32, BufferAttr_HipcMapAlias> out_font_offsets,
69 OutArray<u32, BufferAttr_HipcMapAlias> out_font_sizes,
70 Out<bool> out_fonts_are_loaded, Out<u32> out_font_count,
71 Set::LanguageCode language_code);
51 72
52 struct Impl; 73 struct Impl;
53 std::unique_ptr<Impl> impl; 74 std::unique_ptr<Impl> impl;
diff --git a/src/core/hle/service/ns/query_service.cpp b/src/core/hle/service/ns/query_service.cpp
new file mode 100644
index 000000000..946b7fa23
--- /dev/null
+++ b/src/core/hle/service/ns/query_service.cpp
@@ -0,0 +1,57 @@
1// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "common/logging/log.h"
5#include "common/uuid.h"
6#include "core/hle/service/cmif_serialization.h"
7#include "core/hle/service/ns/query_service.h"
8#include "core/hle/service/service.h"
9
10namespace Service::NS {
11
12IQueryService::IQueryService(Core::System& system_) : ServiceFramework{system_, "pdm:qry"} {
13 // clang-format off
14 static const FunctionInfo functions[] = {
15 {0, nullptr, "QueryAppletEvent"},
16 {1, nullptr, "QueryPlayStatistics"},
17 {2, nullptr, "QueryPlayStatisticsByUserAccountId"},
18 {3, nullptr, "QueryPlayStatisticsByNetworkServiceAccountId"},
19 {4, nullptr, "QueryPlayStatisticsByApplicationId"},
20 {5, D<&IQueryService::QueryPlayStatisticsByApplicationIdAndUserAccountId>, "QueryPlayStatisticsByApplicationIdAndUserAccountId"},
21 {6, nullptr, "QueryPlayStatisticsByApplicationIdAndNetworkServiceAccountId"},
22 {7, nullptr, "QueryLastPlayTimeV0"},
23 {8, nullptr, "QueryPlayEvent"},
24 {9, nullptr, "GetAvailablePlayEventRange"},
25 {10, nullptr, "QueryAccountEvent"},
26 {11, nullptr, "QueryAccountPlayEvent"},
27 {12, nullptr, "GetAvailableAccountPlayEventRange"},
28 {13, nullptr, "QueryApplicationPlayStatisticsForSystemV0"},
29 {14, nullptr, "QueryRecentlyPlayedApplication"},
30 {15, nullptr, "GetRecentlyPlayedApplicationUpdateEvent"},
31 {16, nullptr, "QueryApplicationPlayStatisticsByUserAccountIdForSystemV0"},
32 {17, nullptr, "QueryLastPlayTime"},
33 {18, nullptr, "QueryApplicationPlayStatisticsForSystem"},
34 {19, nullptr, "QueryApplicationPlayStatisticsByUserAccountIdForSystem"},
35 };
36 // clang-format on
37
38 RegisterHandlers(functions);
39}
40
41IQueryService::~IQueryService() = default;
42
43Result IQueryService::QueryPlayStatisticsByApplicationIdAndUserAccountId(
44 Out<PlayStatistics> out_play_statistics, bool unknown, Common::UUID account_id,
45 u64 application_id) {
46 // TODO(German77): Read statistics of the game
47 *out_play_statistics = {
48 .application_id = application_id,
49 .total_launches = 1,
50 };
51
52 LOG_WARNING(Service_NS, "(STUBBED) called. unknown={}. application_id={:016X}, account_id={}",
53 unknown, application_id, account_id.FormattedString());
54 R_SUCCEED();
55}
56
57} // namespace Service::NS
diff --git a/src/core/hle/service/ns/pdm_qry.h b/src/core/hle/service/ns/query_service.h
index c98e01660..6cdbfa277 100644
--- a/src/core/hle/service/ns/pdm_qry.h
+++ b/src/core/hle/service/ns/query_service.h
@@ -3,6 +3,8 @@
3 3
4#pragma once 4#pragma once
5 5
6#include "common/uuid.h"
7#include "core/hle/service/cmif_types.h"
6#include "core/hle/service/service.h" 8#include "core/hle/service/service.h"
7 9
8namespace Service::NS { 10namespace Service::NS {
@@ -20,13 +22,15 @@ struct PlayStatistics {
20}; 22};
21static_assert(sizeof(PlayStatistics) == 0x28, "PlayStatistics is an invalid size"); 23static_assert(sizeof(PlayStatistics) == 0x28, "PlayStatistics is an invalid size");
22 24
23class PDM_QRY final : public ServiceFramework<PDM_QRY> { 25class IQueryService final : public ServiceFramework<IQueryService> {
24public: 26public:
25 explicit PDM_QRY(Core::System& system_); 27 explicit IQueryService(Core::System& system_);
26 ~PDM_QRY() override; 28 ~IQueryService() override;
27 29
28private: 30private:
29 void QueryPlayStatisticsByApplicationIdAndUserAccountId(HLERequestContext& ctx); 31 Result QueryPlayStatisticsByApplicationIdAndUserAccountId(
32 Out<PlayStatistics> out_play_statistics, bool unknown, Common::UUID account_id,
33 u64 application_id);
30}; 34};
31 35
32} // namespace Service::NS 36} // namespace Service::NS
diff --git a/src/core/hle/service/ns/read_only_application_control_data_interface.cpp b/src/core/hle/service/ns/read_only_application_control_data_interface.cpp
new file mode 100644
index 000000000..9b2ca94a4
--- /dev/null
+++ b/src/core/hle/service/ns/read_only_application_control_data_interface.cpp
@@ -0,0 +1,122 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "common/settings.h"
5#include "core/file_sys/control_metadata.h"
6#include "core/file_sys/patch_manager.h"
7#include "core/file_sys/vfs/vfs.h"
8#include "core/hle/service/cmif_serialization.h"
9#include "core/hle/service/ns/language.h"
10#include "core/hle/service/ns/ns_results.h"
11#include "core/hle/service/ns/read_only_application_control_data_interface.h"
12#include "core/hle/service/set/settings_server.h"
13
14namespace Service::NS {
15
16IReadOnlyApplicationControlDataInterface::IReadOnlyApplicationControlDataInterface(
17 Core::System& system_)
18 : ServiceFramework{system_, "IReadOnlyApplicationControlDataInterface"} {
19 // clang-format off
20 static const FunctionInfo functions[] = {
21 {0, D<&IReadOnlyApplicationControlDataInterface::GetApplicationControlData>, "GetApplicationControlData"},
22 {1, D<&IReadOnlyApplicationControlDataInterface::GetApplicationDesiredLanguage>, "GetApplicationDesiredLanguage"},
23 {2, D<&IReadOnlyApplicationControlDataInterface::ConvertApplicationLanguageToLanguageCode>, "ConvertApplicationLanguageToLanguageCode"},
24 {3, nullptr, "ConvertLanguageCodeToApplicationLanguage"},
25 {4, nullptr, "SelectApplicationDesiredLanguage"},
26 };
27 // clang-format on
28
29 RegisterHandlers(functions);
30}
31
32IReadOnlyApplicationControlDataInterface::~IReadOnlyApplicationControlDataInterface() = default;
33
34Result IReadOnlyApplicationControlDataInterface::GetApplicationControlData(
35 OutBuffer<BufferAttr_HipcMapAlias> out_buffer, Out<u32> out_actual_size,
36 ApplicationControlSource application_control_source, u64 application_id) {
37 LOG_INFO(Service_NS, "called with control_source={}, application_id={:016X}",
38 application_control_source, application_id);
39
40 const FileSys::PatchManager pm{application_id, system.GetFileSystemController(),
41 system.GetContentProvider()};
42 const auto control = pm.GetControlMetadata();
43 const auto size = out_buffer.size();
44
45 const auto icon_size = control.second ? control.second->GetSize() : 0;
46 const auto total_size = sizeof(FileSys::RawNACP) + icon_size;
47
48 if (size < total_size) {
49 LOG_ERROR(Service_NS, "output buffer is too small! (actual={:016X}, expected_min=0x4000)",
50 size);
51 R_THROW(ResultUnknown);
52 }
53
54 if (control.first != nullptr) {
55 const auto bytes = control.first->GetRawBytes();
56 std::memcpy(out_buffer.data(), bytes.data(), bytes.size());
57 } else {
58 LOG_WARNING(Service_NS, "missing NACP data for application_id={:016X}, defaulting to zero",
59 application_id);
60 std::memset(out_buffer.data(), 0, sizeof(FileSys::RawNACP));
61 }
62
63 if (control.second != nullptr) {
64 control.second->Read(out_buffer.data() + sizeof(FileSys::RawNACP), icon_size);
65 } else {
66 LOG_WARNING(Service_NS, "missing icon data for application_id={:016X}", application_id);
67 }
68
69 *out_actual_size = static_cast<u32>(total_size);
70 R_SUCCEED();
71}
72
73Result IReadOnlyApplicationControlDataInterface::GetApplicationDesiredLanguage(
74 Out<ApplicationLanguage> out_desired_language, u32 supported_languages) {
75 LOG_INFO(Service_NS, "called with supported_languages={:08X}", supported_languages);
76
77 // Get language code from settings
78 const auto language_code =
79 Set::GetLanguageCodeFromIndex(static_cast<s32>(Settings::values.language_index.GetValue()));
80
81 // Convert to application language, get priority list
82 const auto application_language = ConvertToApplicationLanguage(language_code);
83 if (application_language == std::nullopt) {
84 LOG_ERROR(Service_NS, "Could not convert application language! language_code={}",
85 language_code);
86 R_THROW(Service::NS::ResultApplicationLanguageNotFound);
87 }
88 const auto priority_list = GetApplicationLanguagePriorityList(*application_language);
89 if (!priority_list) {
90 LOG_ERROR(Service_NS,
91 "Could not find application language priorities! application_language={}",
92 *application_language);
93 R_THROW(Service::NS::ResultApplicationLanguageNotFound);
94 }
95
96 // Try to find a valid language.
97 for (const auto lang : *priority_list) {
98 const auto supported_flag = GetSupportedLanguageFlag(lang);
99 if (supported_languages == 0 || (supported_languages & supported_flag) == supported_flag) {
100 *out_desired_language = lang;
101 R_SUCCEED();
102 }
103 }
104
105 LOG_ERROR(Service_NS, "Could not find a valid language! supported_languages={:08X}",
106 supported_languages);
107 R_THROW(Service::NS::ResultApplicationLanguageNotFound);
108}
109
110Result IReadOnlyApplicationControlDataInterface::ConvertApplicationLanguageToLanguageCode(
111 Out<u64> out_language_code, ApplicationLanguage application_language) {
112 const auto language_code = ConvertToLanguageCode(application_language);
113 if (language_code == std::nullopt) {
114 LOG_ERROR(Service_NS, "Language not found! application_language={}", application_language);
115 R_THROW(Service::NS::ResultApplicationLanguageNotFound);
116 }
117
118 *out_language_code = static_cast<u64>(*language_code);
119 R_SUCCEED();
120}
121
122} // namespace Service::NS
diff --git a/src/core/hle/service/ns/read_only_application_control_data_interface.h b/src/core/hle/service/ns/read_only_application_control_data_interface.h
new file mode 100644
index 000000000..ac099435a
--- /dev/null
+++ b/src/core/hle/service/ns/read_only_application_control_data_interface.h
@@ -0,0 +1,30 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "core/hle/service/cmif_types.h"
7#include "core/hle/service/ns/language.h"
8#include "core/hle/service/ns/ns_types.h"
9#include "core/hle/service/service.h"
10
11namespace Service::NS {
12
13class IReadOnlyApplicationControlDataInterface final
14 : public ServiceFramework<IReadOnlyApplicationControlDataInterface> {
15public:
16 explicit IReadOnlyApplicationControlDataInterface(Core::System& system_);
17 ~IReadOnlyApplicationControlDataInterface() override;
18
19public:
20 Result GetApplicationControlData(OutBuffer<BufferAttr_HipcMapAlias> out_buffer,
21 Out<u32> out_actual_size,
22 ApplicationControlSource application_control_source,
23 u64 application_id);
24 Result GetApplicationDesiredLanguage(Out<ApplicationLanguage> out_desired_language,
25 u32 supported_languages);
26 Result ConvertApplicationLanguageToLanguageCode(Out<u64> out_language_code,
27 ApplicationLanguage application_language);
28};
29
30} // namespace Service::NS
diff --git a/src/core/hle/service/ns/read_only_application_record_interface.cpp b/src/core/hle/service/ns/read_only_application_record_interface.cpp
new file mode 100644
index 000000000..816a1e1dc
--- /dev/null
+++ b/src/core/hle/service/ns/read_only_application_record_interface.cpp
@@ -0,0 +1,38 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/hle/service/cmif_serialization.h"
5#include "core/hle/service/ns/read_only_application_record_interface.h"
6
7namespace Service::NS {
8
9IReadOnlyApplicationRecordInterface::IReadOnlyApplicationRecordInterface(Core::System& system_)
10 : ServiceFramework{system_, "IReadOnlyApplicationRecordInterface"} {
11 static const FunctionInfo functions[] = {
12 {0, D<&IReadOnlyApplicationRecordInterface::HasApplicationRecord>, "HasApplicationRecord"},
13 {1, nullptr, "NotifyApplicationFailure"},
14 {2, D<&IReadOnlyApplicationRecordInterface::IsDataCorruptedResult>,
15 "IsDataCorruptedResult"},
16 };
17 // clang-format on
18
19 RegisterHandlers(functions);
20}
21
22IReadOnlyApplicationRecordInterface::~IReadOnlyApplicationRecordInterface() = default;
23
24Result IReadOnlyApplicationRecordInterface::HasApplicationRecord(
25 Out<bool> out_has_application_record, u64 program_id) {
26 LOG_WARNING(Service_NS, "(STUBBED) called, program_id={:016X}", program_id);
27 *out_has_application_record = true;
28 R_SUCCEED();
29}
30
31Result IReadOnlyApplicationRecordInterface::IsDataCorruptedResult(
32 Out<bool> out_is_data_corrupted_result, Result result) {
33 LOG_WARNING(Service_NS, "(STUBBED) called, result={:#x}", result.GetInnerValue());
34 *out_is_data_corrupted_result = false;
35 R_SUCCEED();
36}
37
38} // namespace Service::NS
diff --git a/src/core/hle/service/ns/read_only_application_record_interface.h b/src/core/hle/service/ns/read_only_application_record_interface.h
new file mode 100644
index 000000000..d06e8f5e6
--- /dev/null
+++ b/src/core/hle/service/ns/read_only_application_record_interface.h
@@ -0,0 +1,22 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "core/hle/service/cmif_types.h"
7#include "core/hle/service/service.h"
8
9namespace Service::NS {
10
11class IReadOnlyApplicationRecordInterface final
12 : public ServiceFramework<IReadOnlyApplicationRecordInterface> {
13public:
14 explicit IReadOnlyApplicationRecordInterface(Core::System& system_);
15 ~IReadOnlyApplicationRecordInterface() override;
16
17private:
18 Result HasApplicationRecord(Out<bool> out_has_application_record, u64 program_id);
19 Result IsDataCorruptedResult(Out<bool> out_is_data_corrupted_result, Result result);
20};
21
22} // namespace Service::NS
diff --git a/src/core/hle/service/ns/service_getter_interface.cpp b/src/core/hle/service/ns/service_getter_interface.cpp
new file mode 100644
index 000000000..1a3dd7166
--- /dev/null
+++ b/src/core/hle/service/ns/service_getter_interface.cpp
@@ -0,0 +1,120 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/hle/service/cmif_serialization.h"
5#include "core/hle/service/ns/account_proxy_interface.h"
6#include "core/hle/service/ns/application_manager_interface.h"
7#include "core/hle/service/ns/application_version_interface.h"
8#include "core/hle/service/ns/content_management_interface.h"
9#include "core/hle/service/ns/document_interface.h"
10#include "core/hle/service/ns/download_task_interface.h"
11#include "core/hle/service/ns/dynamic_rights_interface.h"
12#include "core/hle/service/ns/ecommerce_interface.h"
13#include "core/hle/service/ns/factory_reset_interface.h"
14#include "core/hle/service/ns/read_only_application_control_data_interface.h"
15#include "core/hle/service/ns/read_only_application_record_interface.h"
16#include "core/hle/service/ns/service_getter_interface.h"
17
18namespace Service::NS {
19
20IServiceGetterInterface::IServiceGetterInterface(Core::System& system_, const char* name)
21 : ServiceFramework{system_, name} {
22 // clang-format off
23 static const FunctionInfo functions[] = {
24 {7988, D<&IServiceGetterInterface::GetDynamicRightsInterface>, "GetDynamicRightsInterface"},
25 {7989, D<&IServiceGetterInterface::GetReadOnlyApplicationControlDataInterface>, "GetReadOnlyApplicationControlDataInterface"},
26 {7991, D<&IServiceGetterInterface::GetReadOnlyApplicationRecordInterface>, "GetReadOnlyApplicationRecordInterface"},
27 {7992, D<&IServiceGetterInterface::GetECommerceInterface>, "GetECommerceInterface"},
28 {7993, D<&IServiceGetterInterface::GetApplicationVersionInterface>, "GetApplicationVersionInterface"},
29 {7994, D<&IServiceGetterInterface::GetFactoryResetInterface>, "GetFactoryResetInterface"},
30 {7995, D<&IServiceGetterInterface::GetAccountProxyInterface>, "GetAccountProxyInterface"},
31 {7996, D<&IServiceGetterInterface::GetApplicationManagerInterface>, "GetApplicationManagerInterface"},
32 {7997, D<&IServiceGetterInterface::GetDownloadTaskInterface>, "GetDownloadTaskInterface"},
33 {7998, D<&IServiceGetterInterface::GetContentManagementInterface>, "GetContentManagementInterface"},
34 {7999, D<&IServiceGetterInterface::GetDocumentInterface>, "GetDocumentInterface"},
35 };
36 // clang-format on
37
38 RegisterHandlers(functions);
39}
40
41IServiceGetterInterface::~IServiceGetterInterface() = default;
42
43Result IServiceGetterInterface::GetDynamicRightsInterface(
44 Out<SharedPointer<IDynamicRightsInterface>> out_interface) {
45 LOG_DEBUG(Service_NS, "called");
46 *out_interface = std::make_shared<IDynamicRightsInterface>(system);
47 R_SUCCEED();
48}
49
50Result IServiceGetterInterface::GetReadOnlyApplicationControlDataInterface(
51 Out<SharedPointer<IReadOnlyApplicationControlDataInterface>> out_interface) {
52 LOG_DEBUG(Service_NS, "called");
53 *out_interface = std::make_shared<IReadOnlyApplicationControlDataInterface>(system);
54 R_SUCCEED();
55}
56
57Result IServiceGetterInterface::GetReadOnlyApplicationRecordInterface(
58 Out<SharedPointer<IReadOnlyApplicationRecordInterface>> out_interface) {
59 LOG_DEBUG(Service_NS, "called");
60 *out_interface = std::make_shared<IReadOnlyApplicationRecordInterface>(system);
61 R_SUCCEED();
62}
63
64Result IServiceGetterInterface::GetECommerceInterface(
65 Out<SharedPointer<IECommerceInterface>> out_interface) {
66 LOG_DEBUG(Service_NS, "called");
67 *out_interface = std::make_shared<IECommerceInterface>(system);
68 R_SUCCEED();
69}
70
71Result IServiceGetterInterface::GetApplicationVersionInterface(
72 Out<SharedPointer<IApplicationVersionInterface>> out_interface) {
73 LOG_DEBUG(Service_NS, "called");
74 *out_interface = std::make_shared<IApplicationVersionInterface>(system);
75 R_SUCCEED();
76}
77
78Result IServiceGetterInterface::GetFactoryResetInterface(
79 Out<SharedPointer<IFactoryResetInterface>> out_interface) {
80 LOG_DEBUG(Service_NS, "called");
81 *out_interface = std::make_shared<IFactoryResetInterface>(system);
82 R_SUCCEED();
83}
84
85Result IServiceGetterInterface::GetAccountProxyInterface(
86 Out<SharedPointer<IAccountProxyInterface>> out_interface) {
87 LOG_DEBUG(Service_NS, "called");
88 *out_interface = std::make_shared<IAccountProxyInterface>(system);
89 R_SUCCEED();
90}
91
92Result IServiceGetterInterface::GetApplicationManagerInterface(
93 Out<SharedPointer<IApplicationManagerInterface>> out_interface) {
94 LOG_DEBUG(Service_NS, "called");
95 *out_interface = std::make_shared<IApplicationManagerInterface>(system);
96 R_SUCCEED();
97}
98
99Result IServiceGetterInterface::GetDownloadTaskInterface(
100 Out<SharedPointer<IDownloadTaskInterface>> out_interface) {
101 LOG_DEBUG(Service_NS, "called");
102 *out_interface = std::make_shared<IDownloadTaskInterface>(system);
103 R_SUCCEED();
104}
105
106Result IServiceGetterInterface::GetContentManagementInterface(
107 Out<SharedPointer<IContentManagementInterface>> out_interface) {
108 LOG_DEBUG(Service_NS, "called");
109 *out_interface = std::make_shared<IContentManagementInterface>(system);
110 R_SUCCEED();
111}
112
113Result IServiceGetterInterface::GetDocumentInterface(
114 Out<SharedPointer<IDocumentInterface>> out_interface) {
115 LOG_DEBUG(Service_NS, "called");
116 *out_interface = std::make_shared<IDocumentInterface>(system);
117 R_SUCCEED();
118}
119
120} // namespace Service::NS
diff --git a/src/core/hle/service/ns/service_getter_interface.h b/src/core/hle/service/ns/service_getter_interface.h
new file mode 100644
index 000000000..bbc18d444
--- /dev/null
+++ b/src/core/hle/service/ns/service_getter_interface.h
@@ -0,0 +1,47 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "core/hle/service/cmif_types.h"
7#include "core/hle/service/service.h"
8
9namespace Service::NS {
10
11class IDynamicRightsInterface;
12class IReadOnlyApplicationControlDataInterface;
13class IReadOnlyApplicationRecordInterface;
14class IECommerceInterface;
15class IApplicationVersionInterface;
16class IFactoryResetInterface;
17class IAccountProxyInterface;
18class IApplicationManagerInterface;
19class IDownloadTaskInterface;
20class IContentManagementInterface;
21class IDocumentInterface;
22
23class IServiceGetterInterface : public ServiceFramework<IServiceGetterInterface> {
24public:
25 explicit IServiceGetterInterface(Core::System& system_, const char* name);
26 ~IServiceGetterInterface() override;
27
28public:
29 Result GetDynamicRightsInterface(Out<SharedPointer<IDynamicRightsInterface>> out_interface);
30 Result GetReadOnlyApplicationControlDataInterface(
31 Out<SharedPointer<IReadOnlyApplicationControlDataInterface>> out_interface);
32 Result GetReadOnlyApplicationRecordInterface(
33 Out<SharedPointer<IReadOnlyApplicationRecordInterface>> out_interface);
34 Result GetECommerceInterface(Out<SharedPointer<IECommerceInterface>> out_interface);
35 Result GetApplicationVersionInterface(
36 Out<SharedPointer<IApplicationVersionInterface>> out_interface);
37 Result GetFactoryResetInterface(Out<SharedPointer<IFactoryResetInterface>> out_interface);
38 Result GetAccountProxyInterface(Out<SharedPointer<IAccountProxyInterface>> out_interface);
39 Result GetApplicationManagerInterface(
40 Out<SharedPointer<IApplicationManagerInterface>> out_interface);
41 Result GetDownloadTaskInterface(Out<SharedPointer<IDownloadTaskInterface>> out_interface);
42 Result GetContentManagementInterface(
43 Out<SharedPointer<IContentManagementInterface>> out_interface);
44 Result GetDocumentInterface(Out<SharedPointer<IDocumentInterface>> out_interface);
45};
46
47} // namespace Service::NS
diff --git a/src/core/hle/service/ns/system_update_control.cpp b/src/core/hle/service/ns/system_update_control.cpp
new file mode 100644
index 000000000..f5f5cfd90
--- /dev/null
+++ b/src/core/hle/service/ns/system_update_control.cpp
@@ -0,0 +1,44 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/hle/service/cmif_serialization.h"
5#include "core/hle/service/ns/system_update_control.h"
6
7namespace Service::NS {
8
9ISystemUpdateControl::ISystemUpdateControl(Core::System& system_)
10 : ServiceFramework{system_, "ISystemUpdateControl"} {
11 // clang-format off
12 static const FunctionInfo functions[] = {
13 {0, nullptr, "HasDownloaded"},
14 {1, nullptr, "RequestCheckLatestUpdate"},
15 {2, nullptr, "RequestDownloadLatestUpdate"},
16 {3, nullptr, "GetDownloadProgress"},
17 {4, nullptr, "ApplyDownloadedUpdate"},
18 {5, nullptr, "RequestPrepareCardUpdate"},
19 {6, nullptr, "GetPrepareCardUpdateProgress"},
20 {7, nullptr, "HasPreparedCardUpdate"},
21 {8, nullptr, "ApplyCardUpdate"},
22 {9, nullptr, "GetDownloadedEulaDataSize"},
23 {10, nullptr, "GetDownloadedEulaData"},
24 {11, nullptr, "SetupCardUpdate"},
25 {12, nullptr, "GetPreparedCardUpdateEulaDataSize"},
26 {13, nullptr, "GetPreparedCardUpdateEulaData"},
27 {14, nullptr, "SetupCardUpdateViaSystemUpdater"},
28 {15, nullptr, "HasReceived"},
29 {16, nullptr, "RequestReceiveSystemUpdate"},
30 {17, nullptr, "GetReceiveProgress"},
31 {18, nullptr, "ApplyReceivedUpdate"},
32 {19, nullptr, "GetReceivedEulaDataSize"},
33 {20, nullptr, "GetReceivedEulaData"},
34 {21, nullptr, "SetupToReceiveSystemUpdate"},
35 {22, nullptr, "RequestCheckLatestUpdateIncludesRebootlessUpdate"},
36 };
37 // clang-format on
38
39 RegisterHandlers(functions);
40}
41
42ISystemUpdateControl::~ISystemUpdateControl() = default;
43
44} // namespace Service::NS
diff --git a/src/core/hle/service/ns/system_update_control.h b/src/core/hle/service/ns/system_update_control.h
new file mode 100644
index 000000000..a30a09000
--- /dev/null
+++ b/src/core/hle/service/ns/system_update_control.h
@@ -0,0 +1,16 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "core/hle/service/service.h"
7
8namespace Service::NS {
9
10class ISystemUpdateControl final : public ServiceFramework<ISystemUpdateControl> {
11public:
12 explicit ISystemUpdateControl(Core::System& system_);
13 ~ISystemUpdateControl() override;
14};
15
16} // namespace Service::NS
diff --git a/src/core/hle/service/ns/system_update_interface.cpp b/src/core/hle/service/ns/system_update_interface.cpp
new file mode 100644
index 000000000..7e22ca3db
--- /dev/null
+++ b/src/core/hle/service/ns/system_update_interface.cpp
@@ -0,0 +1,61 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/hle/service/cmif_serialization.h"
5#include "core/hle/service/ns/system_update_control.h"
6#include "core/hle/service/ns/system_update_interface.h"
7
8namespace Service::NS {
9
10ISystemUpdateInterface::ISystemUpdateInterface(Core::System& system_)
11 : ServiceFramework{system_, "ns:su"}, service_context{system_, "ns:su"},
12 update_notification_event{service_context} {
13 // clang-format off
14 static const FunctionInfo functions[] = {
15 {0, D<&ISystemUpdateInterface::GetBackgroundNetworkUpdateState>, "GetBackgroundNetworkUpdateState"},
16 {1, D<&ISystemUpdateInterface::OpenSystemUpdateControl>, "OpenSystemUpdateControl"},
17 {2, nullptr, "NotifyExFatDriverRequired"},
18 {3, nullptr, "ClearExFatDriverStatusForDebug"},
19 {4, nullptr, "RequestBackgroundNetworkUpdate"},
20 {5, nullptr, "NotifyBackgroundNetworkUpdate"},
21 {6, nullptr, "NotifyExFatDriverDownloadedForDebug"},
22 {9, D<&ISystemUpdateInterface::GetSystemUpdateNotificationEventForContentDelivery>, "GetSystemUpdateNotificationEventForContentDelivery"},
23 {10, nullptr, "NotifySystemUpdateForContentDelivery"},
24 {11, nullptr, "PrepareShutdown"},
25 {12, nullptr, "Unknown12"},
26 {13, nullptr, "Unknown13"},
27 {14, nullptr, "Unknown14"},
28 {15, nullptr, "Unknown15"},
29 {16, nullptr, "DestroySystemUpdateTask"},
30 {17, nullptr, "RequestSendSystemUpdate"},
31 {18, nullptr, "GetSendSystemUpdateProgress"},
32 };
33 // clang-format on
34
35 RegisterHandlers(functions);
36}
37
38ISystemUpdateInterface::~ISystemUpdateInterface() = default;
39
40Result ISystemUpdateInterface::GetBackgroundNetworkUpdateState(
41 Out<BackgroundNetworkUpdateState> out_background_network_update_state) {
42 LOG_WARNING(Service_AM, "(STUBBED) called");
43 *out_background_network_update_state = BackgroundNetworkUpdateState::None;
44 R_SUCCEED();
45}
46
47Result ISystemUpdateInterface::OpenSystemUpdateControl(
48 Out<SharedPointer<ISystemUpdateControl>> out_system_update_control) {
49 LOG_WARNING(Service_NS, "(STUBBED) called");
50 *out_system_update_control = std::make_shared<ISystemUpdateControl>(system);
51 R_SUCCEED();
52}
53
54Result ISystemUpdateInterface::GetSystemUpdateNotificationEventForContentDelivery(
55 OutCopyHandle<Kernel::KReadableEvent> out_event) {
56 LOG_WARNING(Service_NS, "(STUBBED) called");
57 *out_event = update_notification_event.GetHandle();
58 R_SUCCEED();
59}
60
61} // namespace Service::NS
diff --git a/src/core/hle/service/ns/system_update_interface.h b/src/core/hle/service/ns/system_update_interface.h
new file mode 100644
index 000000000..36a2880ec
--- /dev/null
+++ b/src/core/hle/service/ns/system_update_interface.h
@@ -0,0 +1,38 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "core/hle/service/cmif_types.h"
7#include "core/hle/service/kernel_helpers.h"
8#include "core/hle/service/ns/ns_types.h"
9#include "core/hle/service/os/event.h"
10#include "core/hle/service/service.h"
11
12namespace Kernel {
13class KReadableEvent;
14}
15
16namespace Service::NS {
17
18class ISystemUpdateControl;
19
20class ISystemUpdateInterface final : public ServiceFramework<ISystemUpdateInterface> {
21public:
22 explicit ISystemUpdateInterface(Core::System& system_);
23 ~ISystemUpdateInterface() override;
24
25private:
26 Result GetBackgroundNetworkUpdateState(
27 Out<BackgroundNetworkUpdateState> out_background_network_update_state);
28 Result OpenSystemUpdateControl(
29 Out<SharedPointer<ISystemUpdateControl>> out_system_update_control);
30 Result GetSystemUpdateNotificationEventForContentDelivery(
31 OutCopyHandle<Kernel::KReadableEvent> out_event);
32
33private:
34 KernelHelpers::ServiceContext service_context;
35 Event update_notification_event;
36};
37
38} // namespace Service::NS
diff --git a/src/core/hle/service/ns/vulnerability_manager_interface.cpp b/src/core/hle/service/ns/vulnerability_manager_interface.cpp
new file mode 100644
index 000000000..69c21fb89
--- /dev/null
+++ b/src/core/hle/service/ns/vulnerability_manager_interface.cpp
@@ -0,0 +1,31 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/hle/service/cmif_serialization.h"
5#include "core/hle/service/ns/vulnerability_manager_interface.h"
6
7namespace Service::NS {
8
9IVulnerabilityManagerInterface::IVulnerabilityManagerInterface(Core::System& system_)
10 : ServiceFramework{system_, "ns:vm"} {
11 // clang-format off
12 static const FunctionInfo functions[] = {
13 {1200, D<&IVulnerabilityManagerInterface::NeedsUpdateVulnerability>, "NeedsUpdateVulnerability"},
14 {1201, nullptr, "UpdateSafeSystemVersionForDebug"},
15 {1202, nullptr, "GetSafeSystemVersion"},
16 };
17 // clang-format on
18
19 RegisterHandlers(functions);
20}
21
22IVulnerabilityManagerInterface::~IVulnerabilityManagerInterface() = default;
23
24Result IVulnerabilityManagerInterface::NeedsUpdateVulnerability(
25 Out<bool> out_needs_update_vulnerability) {
26 LOG_WARNING(Service_NS, "(STUBBED) called");
27 *out_needs_update_vulnerability = false;
28 R_SUCCEED();
29}
30
31} // namespace Service::NS
diff --git a/src/core/hle/service/ns/vulnerability_manager_interface.h b/src/core/hle/service/ns/vulnerability_manager_interface.h
new file mode 100644
index 000000000..c689cf7ec
--- /dev/null
+++ b/src/core/hle/service/ns/vulnerability_manager_interface.h
@@ -0,0 +1,21 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "core/hle/service/cmif_types.h"
7#include "core/hle/service/service.h"
8
9namespace Service::NS {
10
11class IVulnerabilityManagerInterface final
12 : public ServiceFramework<IVulnerabilityManagerInterface> {
13public:
14 explicit IVulnerabilityManagerInterface(Core::System& system_);
15 ~IVulnerabilityManagerInterface() override;
16
17private:
18 Result NeedsUpdateVulnerability(Out<bool> out_needs_update_vulnerability);
19};
20
21} // namespace Service::NS
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
index 250d01de3..0265d55f2 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
@@ -92,11 +92,11 @@ NvResult nvhost_ctrl::IocCtrlEventWait(IocCtrlEventWaitParams& params, bool is_a
92 92
93 bool must_unmark_fail = !is_allocation; 93 bool must_unmark_fail = !is_allocation;
94 const u32 event_id = params.value.raw; 94 const u32 event_id = params.value.raw;
95 SCOPE_EXIT({ 95 SCOPE_EXIT {
96 if (must_unmark_fail) { 96 if (must_unmark_fail) {
97 events[event_id].fails = 0; 97 events[event_id].fails = 0;
98 } 98 }
99 }); 99 };
100 100
101 const u32 fence_id = static_cast<u32>(params.fence.id); 101 const u32 fence_id = static_cast<u32>(params.fence.id);
102 102
diff --git a/src/core/hle/service/nvdrv/nvdrv.cpp b/src/core/hle/service/nvdrv/nvdrv.cpp
index cb256e5b4..03eb507b9 100644
--- a/src/core/hle/service/nvdrv/nvdrv.cpp
+++ b/src/core/hle/service/nvdrv/nvdrv.cpp
@@ -42,7 +42,7 @@ void EventInterface::FreeEvent(Kernel::KEvent* event) {
42 module.service_context.CloseEvent(event); 42 module.service_context.CloseEvent(event);
43} 43}
44 44
45void LoopProcess(Nvnflinger::Nvnflinger& nvnflinger, Core::System& system) { 45void LoopProcess(Core::System& system) {
46 auto server_manager = std::make_unique<ServerManager>(system); 46 auto server_manager = std::make_unique<ServerManager>(system);
47 auto module = std::make_shared<Module>(system); 47 auto module = std::make_shared<Module>(system);
48 const auto NvdrvInterfaceFactoryForApplication = [&, module] { 48 const auto NvdrvInterfaceFactoryForApplication = [&, module] {
@@ -62,7 +62,6 @@ void LoopProcess(Nvnflinger::Nvnflinger& nvnflinger, Core::System& system) {
62 server_manager->RegisterNamedService("nvdrv:s", NvdrvInterfaceFactoryForSysmodules); 62 server_manager->RegisterNamedService("nvdrv:s", NvdrvInterfaceFactoryForSysmodules);
63 server_manager->RegisterNamedService("nvdrv:t", NvdrvInterfaceFactoryForTesting); 63 server_manager->RegisterNamedService("nvdrv:t", NvdrvInterfaceFactoryForTesting);
64 server_manager->RegisterNamedService("nvmemp", std::make_shared<NVMEMP>(system)); 64 server_manager->RegisterNamedService("nvmemp", std::make_shared<NVMEMP>(system));
65 nvnflinger.SetNVDrvInstance(module);
66 ServerManager::RunServer(std::move(server_manager)); 65 ServerManager::RunServer(std::move(server_manager));
67} 66}
68 67
diff --git a/src/core/hle/service/nvdrv/nvdrv.h b/src/core/hle/service/nvdrv/nvdrv.h
index c594f0e5e..b76f81e59 100644
--- a/src/core/hle/service/nvdrv/nvdrv.h
+++ b/src/core/hle/service/nvdrv/nvdrv.h
@@ -10,13 +10,11 @@
10#include <span> 10#include <span>
11#include <string> 11#include <string>
12#include <unordered_map> 12#include <unordered_map>
13#include <vector>
14 13
15#include "common/common_types.h" 14#include "common/common_types.h"
16#include "core/hle/service/kernel_helpers.h" 15#include "core/hle/service/kernel_helpers.h"
17#include "core/hle/service/nvdrv/core/container.h" 16#include "core/hle/service/nvdrv/core/container.h"
18#include "core/hle/service/nvdrv/nvdata.h" 17#include "core/hle/service/nvdrv/nvdata.h"
19#include "core/hle/service/nvnflinger/ui/fence.h"
20#include "core/hle/service/service.h" 18#include "core/hle/service/service.h"
21 19
22namespace Core { 20namespace Core {
@@ -27,10 +25,6 @@ namespace Kernel {
27class KEvent; 25class KEvent;
28} 26}
29 27
30namespace Service::Nvnflinger {
31class Nvnflinger;
32}
33
34namespace Service::Nvidia { 28namespace Service::Nvidia {
35 29
36namespace NvCore { 30namespace NvCore {
@@ -99,7 +93,6 @@ public:
99 93
100private: 94private:
101 friend class EventInterface; 95 friend class EventInterface;
102 friend class Service::Nvnflinger::Nvnflinger;
103 96
104 /// Manages syncpoints on the host 97 /// Manages syncpoints on the host
105 NvCore::Container container; 98 NvCore::Container container;
@@ -118,6 +111,6 @@ private:
118 std::unordered_map<std::string, std::function<FilesContainerType::iterator(DeviceFD)>> builders; 111 std::unordered_map<std::string, std::function<FilesContainerType::iterator(DeviceFD)>> builders;
119}; 112};
120 113
121void LoopProcess(Nvnflinger::Nvnflinger& nvnflinger, Core::System& system); 114void LoopProcess(Core::System& system);
122 115
123} // namespace Service::Nvidia 116} // namespace Service::Nvidia
diff --git a/src/core/hle/service/nvdrv/nvdrv_interface.cpp b/src/core/hle/service/nvdrv/nvdrv_interface.cpp
index ffe72f281..258970fd5 100644
--- a/src/core/hle/service/nvdrv/nvdrv_interface.cpp
+++ b/src/core/hle/service/nvdrv/nvdrv_interface.cpp
@@ -154,10 +154,10 @@ void NVDRV::Close(HLERequestContext& ctx) {
154void NVDRV::Initialize(HLERequestContext& ctx) { 154void NVDRV::Initialize(HLERequestContext& ctx) {
155 LOG_WARNING(Service_NVDRV, "(STUBBED) called"); 155 LOG_WARNING(Service_NVDRV, "(STUBBED) called");
156 IPC::ResponseBuilder rb{ctx, 3}; 156 IPC::ResponseBuilder rb{ctx, 3};
157 SCOPE_EXIT({ 157 SCOPE_EXIT {
158 rb.Push(ResultSuccess); 158 rb.Push(ResultSuccess);
159 rb.PushEnum(NvResult::Success); 159 rb.PushEnum(NvResult::Success);
160 }); 160 };
161 161
162 if (is_initialized) { 162 if (is_initialized) {
163 // No need to initialize again 163 // No need to initialize again
@@ -263,8 +263,10 @@ NVDRV::NVDRV(Core::System& system_, std::shared_ptr<Module> nvdrv_, const char*
263} 263}
264 264
265NVDRV::~NVDRV() { 265NVDRV::~NVDRV() {
266 auto& container = nvdrv->GetContainer(); 266 if (is_initialized) {
267 container.CloseSession(session_id); 267 auto& container = nvdrv->GetContainer();
268 container.CloseSession(session_id);
269 }
268} 270}
269 271
270} // namespace Service::Nvidia 272} // namespace Service::Nvidia
diff --git a/src/core/hle/service/nvdrv/nvdrv_interface.h b/src/core/hle/service/nvdrv/nvdrv_interface.h
index f2195ae1e..c72f92597 100644
--- a/src/core/hle/service/nvdrv/nvdrv_interface.h
+++ b/src/core/hle/service/nvdrv/nvdrv_interface.h
@@ -16,6 +16,10 @@ public:
16 explicit NVDRV(Core::System& system_, std::shared_ptr<Module> nvdrv_, const char* name); 16 explicit NVDRV(Core::System& system_, std::shared_ptr<Module> nvdrv_, const char* name);
17 ~NVDRV() override; 17 ~NVDRV() override;
18 18
19 std::shared_ptr<Module> GetModule() const {
20 return nvdrv;
21 }
22
19private: 23private:
20 void Open(HLERequestContext& ctx); 24 void Open(HLERequestContext& ctx);
21 void Ioctl1(HLERequestContext& ctx); 25 void Ioctl1(HLERequestContext& ctx);
diff --git a/src/core/hle/service/nvnflinger/binder.h b/src/core/hle/service/nvnflinger/binder.h
index 179938192..124accb94 100644
--- a/src/core/hle/service/nvnflinger/binder.h
+++ b/src/core/hle/service/nvnflinger/binder.h
@@ -20,29 +20,12 @@ class HLERequestContext;
20 20
21namespace Service::android { 21namespace Service::android {
22 22
23enum class TransactionId {
24 RequestBuffer = 1,
25 SetBufferCount = 2,
26 DequeueBuffer = 3,
27 DetachBuffer = 4,
28 DetachNextBuffer = 5,
29 AttachBuffer = 6,
30 QueueBuffer = 7,
31 CancelBuffer = 8,
32 Query = 9,
33 Connect = 10,
34 Disconnect = 11,
35 AllocateBuffers = 13,
36 SetPreallocatedBuffer = 14,
37 GetBufferHistory = 17,
38};
39
40class IBinder { 23class IBinder {
41public: 24public:
42 virtual ~IBinder() = default; 25 virtual ~IBinder() = default;
43 virtual void Transact(android::TransactionId code, u32 flags, std::span<const u8> parcel_data, 26 virtual void Transact(u32 code, std::span<const u8> parcel_data, std::span<u8> parcel_reply,
44 std::span<u8> parcel_reply) = 0; 27 u32 flags) = 0;
45 virtual Kernel::KReadableEvent& GetNativeHandle() = 0; 28 virtual Kernel::KReadableEvent* GetNativeHandle(u32 type_id) = 0;
46}; 29};
47 30
48} // namespace Service::android 31} // namespace Service::android
diff --git a/src/core/hle/service/nvnflinger/buffer_item_consumer.cpp b/src/core/hle/service/nvnflinger/buffer_item_consumer.cpp
index cf151ea3a..123507123 100644
--- a/src/core/hle/service/nvnflinger/buffer_item_consumer.cpp
+++ b/src/core/hle/service/nvnflinger/buffer_item_consumer.cpp
@@ -12,7 +12,7 @@
12 12
13namespace Service::android { 13namespace Service::android {
14 14
15BufferItemConsumer::BufferItemConsumer(std::unique_ptr<BufferQueueConsumer> consumer_) 15BufferItemConsumer::BufferItemConsumer(std::shared_ptr<BufferQueueConsumer> consumer_)
16 : ConsumerBase{std::move(consumer_)} {} 16 : ConsumerBase{std::move(consumer_)} {}
17 17
18Status BufferItemConsumer::AcquireBuffer(BufferItem* item, std::chrono::nanoseconds present_when, 18Status BufferItemConsumer::AcquireBuffer(BufferItem* item, std::chrono::nanoseconds present_when,
diff --git a/src/core/hle/service/nvnflinger/buffer_item_consumer.h b/src/core/hle/service/nvnflinger/buffer_item_consumer.h
index e0c6b3604..9f95c9280 100644
--- a/src/core/hle/service/nvnflinger/buffer_item_consumer.h
+++ b/src/core/hle/service/nvnflinger/buffer_item_consumer.h
@@ -19,7 +19,7 @@ class BufferItem;
19 19
20class BufferItemConsumer final : public ConsumerBase { 20class BufferItemConsumer final : public ConsumerBase {
21public: 21public:
22 explicit BufferItemConsumer(std::unique_ptr<BufferQueueConsumer> consumer); 22 explicit BufferItemConsumer(std::shared_ptr<BufferQueueConsumer> consumer);
23 Status AcquireBuffer(BufferItem* item, std::chrono::nanoseconds present_when, 23 Status AcquireBuffer(BufferItem* item, std::chrono::nanoseconds present_when,
24 bool wait_for_fence = true); 24 bool wait_for_fence = true);
25 Status ReleaseBuffer(const BufferItem& item, const Fence& release_fence); 25 Status ReleaseBuffer(const BufferItem& item, const Fence& release_fence);
diff --git a/src/core/hle/service/nvnflinger/buffer_queue_consumer.cpp b/src/core/hle/service/nvnflinger/buffer_queue_consumer.cpp
index bbe8e06d4..3bc23aa97 100644
--- a/src/core/hle/service/nvnflinger/buffer_queue_consumer.cpp
+++ b/src/core/hle/service/nvnflinger/buffer_queue_consumer.cpp
@@ -4,12 +4,13 @@
4// Parts of this implementation were based on: 4// Parts of this implementation were based on:
5// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/libs/gui/BufferQueueConsumer.cpp 5// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/libs/gui/BufferQueueConsumer.cpp
6 6
7#include "common/assert.h"
7#include "common/logging/log.h" 8#include "common/logging/log.h"
8#include "core/hle/service/nvnflinger/buffer_item.h" 9#include "core/hle/service/nvnflinger/buffer_item.h"
9#include "core/hle/service/nvnflinger/buffer_queue_consumer.h" 10#include "core/hle/service/nvnflinger/buffer_queue_consumer.h"
10#include "core/hle/service/nvnflinger/buffer_queue_core.h" 11#include "core/hle/service/nvnflinger/buffer_queue_core.h"
12#include "core/hle/service/nvnflinger/parcel.h"
11#include "core/hle/service/nvnflinger/producer_listener.h" 13#include "core/hle/service/nvnflinger/producer_listener.h"
12#include "core/hle/service/nvnflinger/ui/graphic_buffer.h"
13 14
14namespace Service::android { 15namespace Service::android {
15 16
@@ -254,4 +255,77 @@ Status BufferQueueConsumer::GetReleasedBuffers(u64* out_slot_mask) {
254 return Status::NoError; 255 return Status::NoError;
255} 256}
256 257
258void BufferQueueConsumer::Transact(u32 code, std::span<const u8> parcel_data,
259 std::span<u8> parcel_reply, u32 flags) {
260 // Values used by BnGraphicBufferConsumer onTransact
261 enum class TransactionId {
262 AcquireBuffer = 1,
263 DetachBuffer = 2,
264 AttachBuffer = 3,
265 ReleaseBuffer = 4,
266 ConsumerConnect = 5,
267 ConsumerDisconnect = 6,
268 GetReleasedBuffers = 7,
269 SetDefaultBufferSize = 8,
270 SetDefaultMaxBufferCount = 9,
271 DisableAsyncBuffer = 10,
272 SetMaxAcquiredBufferCount = 11,
273 SetConsumerName = 12,
274 SetDefaultBufferFormat = 13,
275 SetConsumerUsageBits = 14,
276 SetTransformHint = 15,
277 GetSidebandStream = 16,
278 Unknown18 = 18,
279 Unknown20 = 20,
280 };
281
282 Status status{Status::NoError};
283 InputParcel parcel_in{parcel_data};
284 OutputParcel parcel_out{};
285
286 switch (static_cast<TransactionId>(code)) {
287 case TransactionId::AcquireBuffer: {
288 BufferItem item;
289 const s64 present_when = parcel_in.Read<s64>();
290
291 status = AcquireBuffer(&item, std::chrono::nanoseconds{present_when});
292
293 // TODO: can't write this directly, needs a flattener for the sp<GraphicBuffer>
294 // parcel_out.WriteFlattened(item);
295 UNREACHABLE();
296 }
297 case TransactionId::ReleaseBuffer: {
298 const s32 slot = parcel_in.Read<s32>();
299 const u64 frame_number = parcel_in.Read<u64>();
300 const auto release_fence = parcel_in.ReadFlattened<Fence>();
301
302 status = ReleaseBuffer(slot, frame_number, release_fence);
303
304 break;
305 }
306 case TransactionId::GetReleasedBuffers: {
307 u64 slot_mask = 0;
308
309 status = GetReleasedBuffers(&slot_mask);
310
311 parcel_out.Write(slot_mask);
312 break;
313 }
314 default:
315 ASSERT_MSG(false, "called, code={} flags={}", code, flags);
316 break;
317 }
318
319 parcel_out.Write(status);
320
321 const auto serialized = parcel_out.Serialize();
322 std::memcpy(parcel_reply.data(), serialized.data(),
323 std::min(parcel_reply.size(), serialized.size()));
324}
325
326Kernel::KReadableEvent* BufferQueueConsumer::GetNativeHandle(u32 type_id) {
327 ASSERT_MSG(false, "called, type_id={}", type_id);
328 return nullptr;
329}
330
257} // namespace Service::android 331} // namespace Service::android
diff --git a/src/core/hle/service/nvnflinger/buffer_queue_consumer.h b/src/core/hle/service/nvnflinger/buffer_queue_consumer.h
index 0a61e8dbd..a9226f1c3 100644
--- a/src/core/hle/service/nvnflinger/buffer_queue_consumer.h
+++ b/src/core/hle/service/nvnflinger/buffer_queue_consumer.h
@@ -10,6 +10,7 @@
10#include <memory> 10#include <memory>
11 11
12#include "common/common_types.h" 12#include "common/common_types.h"
13#include "core/hle/service/nvnflinger/binder.h"
13#include "core/hle/service/nvnflinger/buffer_queue_defs.h" 14#include "core/hle/service/nvnflinger/buffer_queue_defs.h"
14#include "core/hle/service/nvnflinger/status.h" 15#include "core/hle/service/nvnflinger/status.h"
15 16
@@ -19,10 +20,10 @@ class BufferItem;
19class BufferQueueCore; 20class BufferQueueCore;
20class IConsumerListener; 21class IConsumerListener;
21 22
22class BufferQueueConsumer final { 23class BufferQueueConsumer final : public IBinder {
23public: 24public:
24 explicit BufferQueueConsumer(std::shared_ptr<BufferQueueCore> core_); 25 explicit BufferQueueConsumer(std::shared_ptr<BufferQueueCore> core_);
25 ~BufferQueueConsumer(); 26 ~BufferQueueConsumer() override;
26 27
27 Status AcquireBuffer(BufferItem* out_buffer, std::chrono::nanoseconds expected_present); 28 Status AcquireBuffer(BufferItem* out_buffer, std::chrono::nanoseconds expected_present);
28 Status ReleaseBuffer(s32 slot, u64 frame_number, const Fence& release_fence); 29 Status ReleaseBuffer(s32 slot, u64 frame_number, const Fence& release_fence);
@@ -30,6 +31,11 @@ public:
30 Status Disconnect(); 31 Status Disconnect();
31 Status GetReleasedBuffers(u64* out_slot_mask); 32 Status GetReleasedBuffers(u64* out_slot_mask);
32 33
34 void Transact(u32 code, std::span<const u8> parcel_data, std::span<u8> parcel_reply,
35 u32 flags) override;
36
37 Kernel::KReadableEvent* GetNativeHandle(u32 type_id) override;
38
33private: 39private:
34 std::shared_ptr<BufferQueueCore> core; 40 std::shared_ptr<BufferQueueCore> core;
35 BufferQueueDefs::SlotsType& slots; 41 BufferQueueDefs::SlotsType& slots;
diff --git a/src/core/hle/service/nvnflinger/buffer_queue_producer.cpp b/src/core/hle/service/nvnflinger/buffer_queue_producer.cpp
index ec83beb9b..9e5091eeb 100644
--- a/src/core/hle/service/nvnflinger/buffer_queue_producer.cpp
+++ b/src/core/hle/service/nvnflinger/buffer_queue_producer.cpp
@@ -6,12 +6,9 @@
6 6
7#include "common/assert.h" 7#include "common/assert.h"
8#include "common/logging/log.h" 8#include "common/logging/log.h"
9#include "common/settings.h"
10#include "core/core.h"
11#include "core/hle/kernel/k_event.h" 9#include "core/hle/kernel/k_event.h"
12#include "core/hle/kernel/k_readable_event.h" 10#include "core/hle/kernel/k_readable_event.h"
13#include "core/hle/kernel/kernel.h" 11#include "core/hle/kernel/kernel.h"
14#include "core/hle/service/hle_ipc.h"
15#include "core/hle/service/kernel_helpers.h" 12#include "core/hle/service/kernel_helpers.h"
16#include "core/hle/service/nvnflinger/buffer_queue_core.h" 13#include "core/hle/service/nvnflinger/buffer_queue_core.h"
17#include "core/hle/service/nvnflinger/buffer_queue_producer.h" 14#include "core/hle/service/nvnflinger/buffer_queue_producer.h"
@@ -19,7 +16,6 @@
19#include "core/hle/service/nvnflinger/parcel.h" 16#include "core/hle/service/nvnflinger/parcel.h"
20#include "core/hle/service/nvnflinger/ui/graphic_buffer.h" 17#include "core/hle/service/nvnflinger/ui/graphic_buffer.h"
21#include "core/hle/service/nvnflinger/window.h" 18#include "core/hle/service/nvnflinger/window.h"
22#include "core/hle/service/vi/vi.h"
23 19
24namespace Service::android { 20namespace Service::android {
25 21
@@ -807,13 +803,31 @@ Status BufferQueueProducer::SetPreallocatedBuffer(s32 slot,
807 return Status::NoError; 803 return Status::NoError;
808} 804}
809 805
810void BufferQueueProducer::Transact(TransactionId code, u32 flags, std::span<const u8> parcel_data, 806void BufferQueueProducer::Transact(u32 code, std::span<const u8> parcel_data,
811 std::span<u8> parcel_reply) { 807 std::span<u8> parcel_reply, u32 flags) {
808 // Values used by BnGraphicBufferProducer onTransact
809 enum class TransactionId {
810 RequestBuffer = 1,
811 SetBufferCount = 2,
812 DequeueBuffer = 3,
813 DetachBuffer = 4,
814 DetachNextBuffer = 5,
815 AttachBuffer = 6,
816 QueueBuffer = 7,
817 CancelBuffer = 8,
818 Query = 9,
819 Connect = 10,
820 Disconnect = 11,
821 AllocateBuffers = 13,
822 SetPreallocatedBuffer = 14,
823 GetBufferHistory = 17,
824 };
825
812 Status status{Status::NoError}; 826 Status status{Status::NoError};
813 InputParcel parcel_in{parcel_data}; 827 InputParcel parcel_in{parcel_data};
814 OutputParcel parcel_out{}; 828 OutputParcel parcel_out{};
815 829
816 switch (code) { 830 switch (static_cast<TransactionId>(code)) {
817 case TransactionId::Connect: { 831 case TransactionId::Connect: {
818 const auto enable_listener = parcel_in.Read<bool>(); 832 const auto enable_listener = parcel_in.Read<bool>();
819 const auto api = parcel_in.Read<NativeWindowApi>(); 833 const auto api = parcel_in.Read<NativeWindowApi>();
@@ -923,8 +937,8 @@ void BufferQueueProducer::Transact(TransactionId code, u32 flags, std::span<cons
923 std::min(parcel_reply.size(), serialized.size())); 937 std::min(parcel_reply.size(), serialized.size()));
924} 938}
925 939
926Kernel::KReadableEvent& BufferQueueProducer::GetNativeHandle() { 940Kernel::KReadableEvent* BufferQueueProducer::GetNativeHandle(u32 type_id) {
927 return buffer_wait_event->GetReadableEvent(); 941 return &buffer_wait_event->GetReadableEvent();
928} 942}
929 943
930} // namespace Service::android 944} // namespace Service::android
diff --git a/src/core/hle/service/nvnflinger/buffer_queue_producer.h b/src/core/hle/service/nvnflinger/buffer_queue_producer.h
index 4682b0f84..048523514 100644
--- a/src/core/hle/service/nvnflinger/buffer_queue_producer.h
+++ b/src/core/hle/service/nvnflinger/buffer_queue_producer.h
@@ -45,12 +45,12 @@ public:
45 explicit BufferQueueProducer(Service::KernelHelpers::ServiceContext& service_context_, 45 explicit BufferQueueProducer(Service::KernelHelpers::ServiceContext& service_context_,
46 std::shared_ptr<BufferQueueCore> buffer_queue_core_, 46 std::shared_ptr<BufferQueueCore> buffer_queue_core_,
47 Service::Nvidia::NvCore::NvMap& nvmap_); 47 Service::Nvidia::NvCore::NvMap& nvmap_);
48 ~BufferQueueProducer(); 48 ~BufferQueueProducer() override;
49 49
50 void Transact(android::TransactionId code, u32 flags, std::span<const u8> parcel_data, 50 void Transact(u32 code, std::span<const u8> parcel_data, std::span<u8> parcel_reply,
51 std::span<u8> parcel_reply) override; 51 u32 flags) override;
52 52
53 Kernel::KReadableEvent& GetNativeHandle() override; 53 Kernel::KReadableEvent* GetNativeHandle(u32 type_id) override;
54 54
55public: 55public:
56 Status RequestBuffer(s32 slot, std::shared_ptr<GraphicBuffer>* buf); 56 Status RequestBuffer(s32 slot, std::shared_ptr<GraphicBuffer>* buf);
diff --git a/src/core/hle/service/nvnflinger/consumer_base.cpp b/src/core/hle/service/nvnflinger/consumer_base.cpp
index 1059e72bf..e360ebfd8 100644
--- a/src/core/hle/service/nvnflinger/consumer_base.cpp
+++ b/src/core/hle/service/nvnflinger/consumer_base.cpp
@@ -14,7 +14,7 @@
14 14
15namespace Service::android { 15namespace Service::android {
16 16
17ConsumerBase::ConsumerBase(std::unique_ptr<BufferQueueConsumer> consumer_) 17ConsumerBase::ConsumerBase(std::shared_ptr<BufferQueueConsumer> consumer_)
18 : consumer{std::move(consumer_)} {} 18 : consumer{std::move(consumer_)} {}
19 19
20ConsumerBase::~ConsumerBase() { 20ConsumerBase::~ConsumerBase() {
diff --git a/src/core/hle/service/nvnflinger/consumer_base.h b/src/core/hle/service/nvnflinger/consumer_base.h
index ea3e9e97a..b29c16f86 100644
--- a/src/core/hle/service/nvnflinger/consumer_base.h
+++ b/src/core/hle/service/nvnflinger/consumer_base.h
@@ -27,7 +27,7 @@ public:
27 void Abandon(); 27 void Abandon();
28 28
29protected: 29protected:
30 explicit ConsumerBase(std::unique_ptr<BufferQueueConsumer> consumer_); 30 explicit ConsumerBase(std::shared_ptr<BufferQueueConsumer> consumer_);
31 ~ConsumerBase() override; 31 ~ConsumerBase() override;
32 32
33 void OnFrameAvailable(const BufferItem& item) override; 33 void OnFrameAvailable(const BufferItem& item) override;
@@ -54,7 +54,7 @@ protected:
54 54
55 bool is_abandoned{}; 55 bool is_abandoned{};
56 56
57 std::unique_ptr<BufferQueueConsumer> consumer; 57 std::shared_ptr<BufferQueueConsumer> consumer;
58 58
59 mutable std::mutex mutex; 59 mutable std::mutex mutex;
60}; 60};
diff --git a/src/core/hle/service/nvnflinger/display.h b/src/core/hle/service/nvnflinger/display.h
new file mode 100644
index 000000000..f27cbf144
--- /dev/null
+++ b/src/core/hle/service/nvnflinger/display.h
@@ -0,0 +1,55 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <list>
7
8#include "core/hle/service/nvnflinger/buffer_item_consumer.h"
9#include "core/hle/service/nvnflinger/hwc_layer.h"
10
11namespace Service::Nvnflinger {
12
13struct Layer {
14 explicit Layer(std::shared_ptr<android::BufferItemConsumer> buffer_item_consumer_,
15 s32 consumer_id_)
16 : buffer_item_consumer(std::move(buffer_item_consumer_)), consumer_id(consumer_id_),
17 blending(LayerBlending::None), visible(true) {}
18 ~Layer() {
19 buffer_item_consumer->Abandon();
20 }
21
22 std::shared_ptr<android::BufferItemConsumer> buffer_item_consumer;
23 s32 consumer_id;
24 LayerBlending blending;
25 bool visible;
26};
27
28struct LayerStack {
29 std::list<Layer> layers;
30};
31
32struct Display {
33 explicit Display(u64 id_) {
34 id = id_;
35 }
36
37 Layer* FindLayer(s32 consumer_id) {
38 for (auto& layer : stack.layers) {
39 if (layer.consumer_id == consumer_id) {
40 return &layer;
41 }
42 }
43
44 return nullptr;
45 }
46
47 bool HasLayers() {
48 return !stack.layers.empty();
49 }
50
51 u64 id;
52 LayerStack stack;
53};
54
55} // namespace Service::Nvnflinger
diff --git a/src/core/hle/service/nvnflinger/hardware_composer.cpp b/src/core/hle/service/nvnflinger/hardware_composer.cpp
index be7eb97a3..02215a786 100644
--- a/src/core/hle/service/nvnflinger/hardware_composer.cpp
+++ b/src/core/hle/service/nvnflinger/hardware_composer.cpp
@@ -10,8 +10,6 @@
10#include "core/hle/service/nvnflinger/hardware_composer.h" 10#include "core/hle/service/nvnflinger/hardware_composer.h"
11#include "core/hle/service/nvnflinger/hwc_layer.h" 11#include "core/hle/service/nvnflinger/hwc_layer.h"
12#include "core/hle/service/nvnflinger/ui/graphic_buffer.h" 12#include "core/hle/service/nvnflinger/ui/graphic_buffer.h"
13#include "core/hle/service/vi/display/vi_display.h"
14#include "core/hle/service/vi/layer/vi_layer.h"
15 13
16namespace Service::Nvnflinger { 14namespace Service::Nvnflinger {
17 15
@@ -44,7 +42,7 @@ s32 NormalizeSwapInterval(f32* out_speed_scale, s32 swap_interval) {
44HardwareComposer::HardwareComposer() = default; 42HardwareComposer::HardwareComposer() = default;
45HardwareComposer::~HardwareComposer() = default; 43HardwareComposer::~HardwareComposer() = default;
46 44
47u32 HardwareComposer::ComposeLocked(f32* out_speed_scale, VI::Display& display, 45u32 HardwareComposer::ComposeLocked(f32* out_speed_scale, Display& display,
48 Nvidia::Devices::nvdisp_disp0& nvdisp) { 46 Nvidia::Devices::nvdisp_disp0& nvdisp) {
49 boost::container::small_vector<HwcLayer, 2> composition_stack; 47 boost::container::small_vector<HwcLayer, 2> composition_stack;
50 48
@@ -56,12 +54,11 @@ u32 HardwareComposer::ComposeLocked(f32* out_speed_scale, VI::Display& display,
56 bool has_acquired_buffer{}; 54 bool has_acquired_buffer{};
57 55
58 // Acquire all necessary framebuffers. 56 // Acquire all necessary framebuffers.
59 for (size_t i = 0; i < display.GetNumLayers(); i++) { 57 for (auto& layer : display.stack.layers) {
60 auto& layer = display.GetLayer(i); 58 auto consumer_id = layer.consumer_id;
61 auto layer_id = layer.GetLayerId();
62 59
63 // Try to fetch the framebuffer (either new or stale). 60 // Try to fetch the framebuffer (either new or stale).
64 const auto result = this->CacheFramebufferLocked(layer, layer_id); 61 const auto result = this->CacheFramebufferLocked(layer, consumer_id);
65 62
66 // If we failed, skip this layer. 63 // If we failed, skip this layer.
67 if (result == CacheStatus::NoBufferAvailable) { 64 if (result == CacheStatus::NoBufferAvailable) {
@@ -73,24 +70,26 @@ u32 HardwareComposer::ComposeLocked(f32* out_speed_scale, VI::Display& display,
73 has_acquired_buffer = true; 70 has_acquired_buffer = true;
74 } 71 }
75 72
76 const auto& buffer = m_framebuffers[layer_id]; 73 const auto& buffer = m_framebuffers[consumer_id];
77 const auto& item = buffer.item; 74 const auto& item = buffer.item;
78 const auto& igbp_buffer = *item.graphic_buffer; 75 const auto& igbp_buffer = *item.graphic_buffer;
79 76
80 // TODO: get proper Z-index from layer 77 // TODO: get proper Z-index from layer
81 composition_stack.emplace_back(HwcLayer{ 78 if (layer.visible) {
82 .buffer_handle = igbp_buffer.BufferId(), 79 composition_stack.emplace_back(HwcLayer{
83 .offset = igbp_buffer.Offset(), 80 .buffer_handle = igbp_buffer.BufferId(),
84 .format = igbp_buffer.ExternalFormat(), 81 .offset = igbp_buffer.Offset(),
85 .width = igbp_buffer.Width(), 82 .format = igbp_buffer.ExternalFormat(),
86 .height = igbp_buffer.Height(), 83 .width = igbp_buffer.Width(),
87 .stride = igbp_buffer.Stride(), 84 .height = igbp_buffer.Height(),
88 .z_index = 0, 85 .stride = igbp_buffer.Stride(),
89 .blending = layer.GetBlending(), 86 .z_index = 0,
90 .transform = static_cast<android::BufferTransformFlags>(item.transform), 87 .blending = layer.blending,
91 .crop_rect = item.crop, 88 .transform = static_cast<android::BufferTransformFlags>(item.transform),
92 .acquire_fence = item.fence, 89 .crop_rect = item.crop,
93 }); 90 .acquire_fence = item.fence,
91 });
92 }
94 93
95 // We need to compose again either before this frame is supposed to 94 // We need to compose again either before this frame is supposed to
96 // be released, or exactly on the vsync period it should be released. 95 // be released, or exactly on the vsync period it should be released.
@@ -138,7 +137,7 @@ u32 HardwareComposer::ComposeLocked(f32* out_speed_scale, VI::Display& display,
138 if (auto* layer = display.FindLayer(layer_id); layer != nullptr) { 137 if (auto* layer = display.FindLayer(layer_id); layer != nullptr) {
139 // TODO: support release fence 138 // TODO: support release fence
140 // This is needed to prevent screen tearing 139 // This is needed to prevent screen tearing
141 layer->GetConsumer().ReleaseBuffer(framebuffer.item, android::Fence::NoFence()); 140 layer->buffer_item_consumer->ReleaseBuffer(framebuffer.item, android::Fence::NoFence());
142 framebuffer.is_acquired = false; 141 framebuffer.is_acquired = false;
143 } 142 }
144 } 143 }
@@ -146,26 +145,26 @@ u32 HardwareComposer::ComposeLocked(f32* out_speed_scale, VI::Display& display,
146 return frame_advance; 145 return frame_advance;
147} 146}
148 147
149void HardwareComposer::RemoveLayerLocked(VI::Display& display, LayerId layer_id) { 148void HardwareComposer::RemoveLayerLocked(Display& display, ConsumerId consumer_id) {
150 // Check if we are tracking a slot with this layer_id. 149 // Check if we are tracking a slot with this consumer_id.
151 const auto it = m_framebuffers.find(layer_id); 150 const auto it = m_framebuffers.find(consumer_id);
152 if (it == m_framebuffers.end()) { 151 if (it == m_framebuffers.end()) {
153 return; 152 return;
154 } 153 }
155 154
156 // Try to release the buffer item. 155 // Try to release the buffer item.
157 auto* const layer = display.FindLayer(layer_id); 156 auto* const layer = display.FindLayer(consumer_id);
158 if (layer && it->second.is_acquired) { 157 if (layer && it->second.is_acquired) {
159 layer->GetConsumer().ReleaseBuffer(it->second.item, android::Fence::NoFence()); 158 layer->buffer_item_consumer->ReleaseBuffer(it->second.item, android::Fence::NoFence());
160 } 159 }
161 160
162 // Erase the slot. 161 // Erase the slot.
163 m_framebuffers.erase(it); 162 m_framebuffers.erase(it);
164} 163}
165 164
166bool HardwareComposer::TryAcquireFramebufferLocked(VI::Layer& layer, Framebuffer& framebuffer) { 165bool HardwareComposer::TryAcquireFramebufferLocked(Layer& layer, Framebuffer& framebuffer) {
167 // Attempt the update. 166 // Attempt the update.
168 const auto status = layer.GetConsumer().AcquireBuffer(&framebuffer.item, {}, false); 167 const auto status = layer.buffer_item_consumer->AcquireBuffer(&framebuffer.item, {}, false);
169 if (status != android::Status::NoError) { 168 if (status != android::Status::NoError) {
170 return false; 169 return false;
171 } 170 }
@@ -178,10 +177,10 @@ bool HardwareComposer::TryAcquireFramebufferLocked(VI::Layer& layer, Framebuffer
178 return true; 177 return true;
179} 178}
180 179
181HardwareComposer::CacheStatus HardwareComposer::CacheFramebufferLocked(VI::Layer& layer, 180HardwareComposer::CacheStatus HardwareComposer::CacheFramebufferLocked(Layer& layer,
182 LayerId layer_id) { 181 ConsumerId consumer_id) {
183 // Check if this framebuffer is already present. 182 // Check if this framebuffer is already present.
184 const auto it = m_framebuffers.find(layer_id); 183 const auto it = m_framebuffers.find(consumer_id);
185 if (it != m_framebuffers.end()) { 184 if (it != m_framebuffers.end()) {
186 // If it's currently still acquired, we are done. 185 // If it's currently still acquired, we are done.
187 if (it->second.is_acquired) { 186 if (it->second.is_acquired) {
@@ -203,7 +202,7 @@ HardwareComposer::CacheStatus HardwareComposer::CacheFramebufferLocked(VI::Layer
203 202
204 if (this->TryAcquireFramebufferLocked(layer, framebuffer)) { 203 if (this->TryAcquireFramebufferLocked(layer, framebuffer)) {
205 // Move the buffer item into a new slot. 204 // Move the buffer item into a new slot.
206 m_framebuffers.emplace(layer_id, std::move(framebuffer)); 205 m_framebuffers.emplace(consumer_id, std::move(framebuffer));
207 206
208 // We succeeded. 207 // We succeeded.
209 return CacheStatus::BufferAcquired; 208 return CacheStatus::BufferAcquired;
diff --git a/src/core/hle/service/nvnflinger/hardware_composer.h b/src/core/hle/service/nvnflinger/hardware_composer.h
index 28392c512..c5b830468 100644
--- a/src/core/hle/service/nvnflinger/hardware_composer.h
+++ b/src/core/hle/service/nvnflinger/hardware_composer.h
@@ -3,35 +3,29 @@
3 3
4#pragma once 4#pragma once
5 5
6#include <memory>
7#include <boost/container/flat_map.hpp> 6#include <boost/container/flat_map.hpp>
8 7
9#include "core/hle/service/nvnflinger/buffer_item.h" 8#include "core/hle/service/nvnflinger/buffer_item.h"
9#include "core/hle/service/nvnflinger/display.h"
10 10
11namespace Service::Nvidia::Devices { 11namespace Service::Nvidia::Devices {
12class nvdisp_disp0; 12class nvdisp_disp0;
13} 13}
14 14
15namespace Service::VI {
16class Display;
17class Layer;
18} // namespace Service::VI
19
20namespace Service::Nvnflinger { 15namespace Service::Nvnflinger {
21 16
22using LayerId = u64; 17using ConsumerId = s32;
23 18
24class HardwareComposer { 19class HardwareComposer {
25public: 20public:
26 explicit HardwareComposer(); 21 explicit HardwareComposer();
27 ~HardwareComposer(); 22 ~HardwareComposer();
28 23
29 u32 ComposeLocked(f32* out_speed_scale, VI::Display& display, 24 u32 ComposeLocked(f32* out_speed_scale, Display& display,
30 Nvidia::Devices::nvdisp_disp0& nvdisp); 25 Nvidia::Devices::nvdisp_disp0& nvdisp);
31 void RemoveLayerLocked(VI::Display& display, LayerId layer_id); 26 void RemoveLayerLocked(Display& display, ConsumerId consumer_id);
32 27
33private: 28private:
34 // TODO: do we want to track frame number in vi instead?
35 u64 m_frame_number{0}; 29 u64 m_frame_number{0};
36 30
37private: 31private:
@@ -49,11 +43,11 @@ private:
49 CachedBufferReused, 43 CachedBufferReused,
50 }; 44 };
51 45
52 boost::container::flat_map<LayerId, Framebuffer> m_framebuffers{}; 46 boost::container::flat_map<ConsumerId, Framebuffer> m_framebuffers{};
53 47
54private: 48private:
55 bool TryAcquireFramebufferLocked(VI::Layer& layer, Framebuffer& framebuffer); 49 bool TryAcquireFramebufferLocked(Layer& layer, Framebuffer& framebuffer);
56 CacheStatus CacheFramebufferLocked(VI::Layer& layer, LayerId layer_id); 50 CacheStatus CacheFramebufferLocked(Layer& layer, ConsumerId consumer_id);
57}; 51};
58 52
59} // namespace Service::Nvnflinger 53} // namespace Service::Nvnflinger
diff --git a/src/core/hle/service/vi/hos_binder_driver.cpp b/src/core/hle/service/nvnflinger/hos_binder_driver.cpp
index ba0317245..8629a2e89 100644
--- a/src/core/hle/service/vi/hos_binder_driver.cpp
+++ b/src/core/hle/service/nvnflinger/hos_binder_driver.cpp
@@ -3,13 +3,16 @@
3 3
4#include "core/hle/service/cmif_serialization.h" 4#include "core/hle/service/cmif_serialization.h"
5#include "core/hle/service/nvnflinger/binder.h" 5#include "core/hle/service/nvnflinger/binder.h"
6#include "core/hle/service/nvnflinger/hos_binder_driver.h"
6#include "core/hle/service/nvnflinger/hos_binder_driver_server.h" 7#include "core/hle/service/nvnflinger/hos_binder_driver_server.h"
7#include "core/hle/service/vi/hos_binder_driver.h"
8 8
9namespace Service::VI { 9namespace Service::Nvnflinger {
10 10
11IHOSBinderDriver::IHOSBinderDriver(Core::System& system_, Nvnflinger::HosBinderDriverServer& server) 11IHOSBinderDriver::IHOSBinderDriver(Core::System& system_,
12 : ServiceFramework{system_, "IHOSBinderDriver"}, m_server(server) { 12 std::shared_ptr<HosBinderDriverServer> server,
13 std::shared_ptr<SurfaceFlinger> surface_flinger)
14 : ServiceFramework{system_, "IHOSBinderDriver"}, m_server(server),
15 m_surface_flinger(surface_flinger) {
13 static const FunctionInfo functions[] = { 16 static const FunctionInfo functions[] = {
14 {0, C<&IHOSBinderDriver::TransactParcel>, "TransactParcel"}, 17 {0, C<&IHOSBinderDriver::TransactParcel>, "TransactParcel"},
15 {1, C<&IHOSBinderDriver::AdjustRefcount>, "AdjustRefcount"}, 18 {1, C<&IHOSBinderDriver::AdjustRefcount>, "AdjustRefcount"},
@@ -21,13 +24,18 @@ IHOSBinderDriver::IHOSBinderDriver(Core::System& system_, Nvnflinger::HosBinderD
21 24
22IHOSBinderDriver::~IHOSBinderDriver() = default; 25IHOSBinderDriver::~IHOSBinderDriver() = default;
23 26
24Result IHOSBinderDriver::TransactParcel(s32 binder_id, android::TransactionId transaction_id, 27Result IHOSBinderDriver::TransactParcel(s32 binder_id, u32 transaction_id,
25 InBuffer<BufferAttr_HipcMapAlias> parcel_data, 28 InBuffer<BufferAttr_HipcMapAlias> parcel_data,
26 OutBuffer<BufferAttr_HipcMapAlias> parcel_reply, 29 OutBuffer<BufferAttr_HipcMapAlias> parcel_reply,
27 u32 flags) { 30 u32 flags) {
28 LOG_DEBUG(Service_VI, "called. id={} transaction={}, flags={}", binder_id, transaction_id, 31 LOG_DEBUG(Service_VI, "called. id={} transaction={}, flags={}", binder_id, transaction_id,
29 flags); 32 flags);
30 m_server.TryGetProducer(binder_id)->Transact(transaction_id, flags, parcel_data, parcel_reply); 33
34 const auto binder = m_server->TryGetBinder(binder_id);
35 R_SUCCEED_IF(binder == nullptr);
36
37 binder->Transact(transaction_id, parcel_data, parcel_reply, flags);
38
31 R_SUCCEED(); 39 R_SUCCEED();
32} 40}
33 41
@@ -39,15 +47,20 @@ Result IHOSBinderDriver::AdjustRefcount(s32 binder_id, s32 addval, s32 type) {
39Result IHOSBinderDriver::GetNativeHandle(s32 binder_id, u32 type_id, 47Result IHOSBinderDriver::GetNativeHandle(s32 binder_id, u32 type_id,
40 OutCopyHandle<Kernel::KReadableEvent> out_handle) { 48 OutCopyHandle<Kernel::KReadableEvent> out_handle) {
41 LOG_WARNING(Service_VI, "(STUBBED) called id={}, type_id={}", binder_id, type_id); 49 LOG_WARNING(Service_VI, "(STUBBED) called id={}, type_id={}", binder_id, type_id);
42 *out_handle = &m_server.TryGetProducer(binder_id)->GetNativeHandle(); 50
51 const auto binder = m_server->TryGetBinder(binder_id);
52 R_UNLESS(binder != nullptr, ResultUnknown);
53
54 *out_handle = binder->GetNativeHandle(type_id);
55
43 R_SUCCEED(); 56 R_SUCCEED();
44} 57}
45 58
46Result IHOSBinderDriver::TransactParcelAuto(s32 binder_id, android::TransactionId transaction_id, 59Result IHOSBinderDriver::TransactParcelAuto(s32 binder_id, u32 transaction_id,
47 InBuffer<BufferAttr_HipcAutoSelect> parcel_data, 60 InBuffer<BufferAttr_HipcAutoSelect> parcel_data,
48 OutBuffer<BufferAttr_HipcAutoSelect> parcel_reply, 61 OutBuffer<BufferAttr_HipcAutoSelect> parcel_reply,
49 u32 flags) { 62 u32 flags) {
50 R_RETURN(this->TransactParcel(binder_id, transaction_id, parcel_data, parcel_reply, flags)); 63 R_RETURN(this->TransactParcel(binder_id, transaction_id, parcel_data, parcel_reply, flags));
51} 64}
52 65
53} // namespace Service::VI 66} // namespace Service::Nvnflinger
diff --git a/src/core/hle/service/vi/hos_binder_driver.h b/src/core/hle/service/nvnflinger/hos_binder_driver.h
index ed6e8cdbe..b7fb07bd2 100644
--- a/src/core/hle/service/vi/hos_binder_driver.h
+++ b/src/core/hle/service/nvnflinger/hos_binder_driver.h
@@ -2,29 +2,45 @@
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include "core/hle/service/cmif_types.h" 4#include "core/hle/service/cmif_types.h"
5#include "core/hle/service/nvnflinger/binder.h"
6#include "core/hle/service/service.h" 5#include "core/hle/service/service.h"
7 6
8namespace Service::VI { 7namespace Kernel {
8class KReadableEvent;
9}
10
11namespace Service::Nvnflinger {
12
13class HosBinderDriverServer;
14class SurfaceFlinger;
9 15
10class IHOSBinderDriver final : public ServiceFramework<IHOSBinderDriver> { 16class IHOSBinderDriver final : public ServiceFramework<IHOSBinderDriver> {
11public: 17public:
12 explicit IHOSBinderDriver(Core::System& system_, Nvnflinger::HosBinderDriverServer& server); 18 explicit IHOSBinderDriver(Core::System& system_, std::shared_ptr<HosBinderDriverServer> server,
19 std::shared_ptr<SurfaceFlinger> surface_flinger);
13 ~IHOSBinderDriver() override; 20 ~IHOSBinderDriver() override;
14 21
22 std::shared_ptr<SurfaceFlinger> GetSurfaceFlinger() {
23 return m_surface_flinger;
24 }
25
26 std::shared_ptr<HosBinderDriverServer> GetServer() {
27 return m_server;
28 }
29
15private: 30private:
16 Result TransactParcel(s32 binder_id, android::TransactionId transaction_id, 31 Result TransactParcel(s32 binder_id, u32 transaction_id,
17 InBuffer<BufferAttr_HipcMapAlias> parcel_data, 32 InBuffer<BufferAttr_HipcMapAlias> parcel_data,
18 OutBuffer<BufferAttr_HipcMapAlias> parcel_reply, u32 flags); 33 OutBuffer<BufferAttr_HipcMapAlias> parcel_reply, u32 flags);
19 Result AdjustRefcount(s32 binder_id, s32 addval, s32 type); 34 Result AdjustRefcount(s32 binder_id, s32 addval, s32 type);
20 Result GetNativeHandle(s32 binder_id, u32 type_id, 35 Result GetNativeHandle(s32 binder_id, u32 type_id,
21 OutCopyHandle<Kernel::KReadableEvent> out_handle); 36 OutCopyHandle<Kernel::KReadableEvent> out_handle);
22 Result TransactParcelAuto(s32 binder_id, android::TransactionId transaction_id, 37 Result TransactParcelAuto(s32 binder_id, u32 transaction_id,
23 InBuffer<BufferAttr_HipcAutoSelect> parcel_data, 38 InBuffer<BufferAttr_HipcAutoSelect> parcel_data,
24 OutBuffer<BufferAttr_HipcAutoSelect> parcel_reply, u32 flags); 39 OutBuffer<BufferAttr_HipcAutoSelect> parcel_reply, u32 flags);
25 40
26private: 41private:
27 Nvnflinger::HosBinderDriverServer& m_server; 42 const std::shared_ptr<HosBinderDriverServer> m_server;
43 const std::shared_ptr<SurfaceFlinger> m_surface_flinger;
28}; 44};
29 45
30} // namespace Service::VI 46} // namespace Service::Nvnflinger
diff --git a/src/core/hle/service/nvnflinger/hos_binder_driver_server.cpp b/src/core/hle/service/nvnflinger/hos_binder_driver_server.cpp
index b86a79ec9..29addda44 100644
--- a/src/core/hle/service/nvnflinger/hos_binder_driver_server.cpp
+++ b/src/core/hle/service/nvnflinger/hos_binder_driver_server.cpp
@@ -8,26 +8,30 @@
8 8
9namespace Service::Nvnflinger { 9namespace Service::Nvnflinger {
10 10
11HosBinderDriverServer::HosBinderDriverServer(Core::System& system_) 11HosBinderDriverServer::HosBinderDriverServer() = default;
12 : service_context(system_, "HosBinderDriverServer") {} 12HosBinderDriverServer::~HosBinderDriverServer() = default;
13 13
14HosBinderDriverServer::~HosBinderDriverServer() {} 14s32 HosBinderDriverServer::RegisterBinder(std::shared_ptr<android::IBinder>&& binder) {
15
16u64 HosBinderDriverServer::RegisterProducer(std::unique_ptr<android::IBinder>&& binder) {
17 std::scoped_lock lk{lock}; 15 std::scoped_lock lk{lock};
18 16
19 last_id++; 17 last_id++;
20 18
21 producers[last_id] = std::move(binder); 19 binders[last_id] = std::move(binder);
22 20
23 return last_id; 21 return last_id;
24} 22}
25 23
26android::IBinder* HosBinderDriverServer::TryGetProducer(u64 id) { 24void HosBinderDriverServer::UnregisterBinder(s32 binder_id) {
25 std::scoped_lock lk{lock};
26
27 binders.erase(binder_id);
28}
29
30std::shared_ptr<android::IBinder> HosBinderDriverServer::TryGetBinder(s32 id) const {
27 std::scoped_lock lk{lock}; 31 std::scoped_lock lk{lock};
28 32
29 if (auto search = producers.find(id); search != producers.end()) { 33 if (auto search = binders.find(id); search != binders.end()) {
30 return search->second.get(); 34 return search->second;
31 } 35 }
32 36
33 return {}; 37 return {};
diff --git a/src/core/hle/service/nvnflinger/hos_binder_driver_server.h b/src/core/hle/service/nvnflinger/hos_binder_driver_server.h
index 58bb9469a..d72b50833 100644
--- a/src/core/hle/service/nvnflinger/hos_binder_driver_server.h
+++ b/src/core/hle/service/nvnflinger/hos_binder_driver_server.h
@@ -8,7 +8,6 @@
8#include <unordered_map> 8#include <unordered_map>
9 9
10#include "common/common_types.h" 10#include "common/common_types.h"
11#include "core/hle/service/kernel_helpers.h"
12#include "core/hle/service/nvnflinger/binder.h" 11#include "core/hle/service/nvnflinger/binder.h"
13 12
14namespace Core { 13namespace Core {
@@ -19,19 +18,18 @@ namespace Service::Nvnflinger {
19 18
20class HosBinderDriverServer final { 19class HosBinderDriverServer final {
21public: 20public:
22 explicit HosBinderDriverServer(Core::System& system_); 21 explicit HosBinderDriverServer();
23 ~HosBinderDriverServer(); 22 ~HosBinderDriverServer();
24 23
25 u64 RegisterProducer(std::unique_ptr<android::IBinder>&& binder); 24 s32 RegisterBinder(std::shared_ptr<android::IBinder>&& binder);
25 void UnregisterBinder(s32 binder_id);
26 26
27 android::IBinder* TryGetProducer(u64 id); 27 std::shared_ptr<android::IBinder> TryGetBinder(s32 id) const;
28 28
29private: 29private:
30 KernelHelpers::ServiceContext service_context; 30 std::unordered_map<s32, std::shared_ptr<android::IBinder>> binders;
31 31 mutable std::mutex lock;
32 std::unordered_map<u64, std::unique_ptr<android::IBinder>> producers; 32 s32 last_id{};
33 std::mutex lock;
34 u64 last_id{};
35}; 33};
36 34
37} // namespace Service::Nvnflinger 35} // namespace Service::Nvnflinger
diff --git a/src/core/hle/service/nvnflinger/nvnflinger.cpp b/src/core/hle/service/nvnflinger/nvnflinger.cpp
index 687ccc9f9..9e3b68b8a 100644
--- a/src/core/hle/service/nvnflinger/nvnflinger.cpp
+++ b/src/core/hle/service/nvnflinger/nvnflinger.cpp
@@ -1,335 +1,24 @@
1// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later 2// SPDX-License-Identifier: GPL-3.0-or-later
3 3
4#include <algorithm>
5#include <optional>
6
7#include "common/assert.h"
8#include "common/logging/log.h"
9#include "common/microprofile.h"
10#include "common/scope_exit.h"
11#include "common/settings.h"
12#include "common/thread.h"
13#include "core/core.h" 4#include "core/core.h"
14#include "core/core_timing.h" 5#include "core/hle/service/nvnflinger/hos_binder_driver.h"
15#include "core/hle/kernel/k_readable_event.h"
16#include "core/hle/service/nvdrv/devices/nvdisp_disp0.h"
17#include "core/hle/service/nvdrv/nvdrv.h"
18#include "core/hle/service/nvnflinger/buffer_item_consumer.h"
19#include "core/hle/service/nvnflinger/buffer_queue_core.h"
20#include "core/hle/service/nvnflinger/fb_share_buffer_manager.h"
21#include "core/hle/service/nvnflinger/hardware_composer.h"
22#include "core/hle/service/nvnflinger/hos_binder_driver_server.h" 6#include "core/hle/service/nvnflinger/hos_binder_driver_server.h"
23#include "core/hle/service/nvnflinger/nvnflinger.h" 7#include "core/hle/service/nvnflinger/nvnflinger.h"
24#include "core/hle/service/nvnflinger/ui/graphic_buffer.h" 8#include "core/hle/service/nvnflinger/surface_flinger.h"
25#include "core/hle/service/vi/display/vi_display.h" 9#include "core/hle/service/server_manager.h"
26#include "core/hle/service/vi/layer/vi_layer.h" 10#include "core/hle/service/sm/sm.h"
27#include "core/hle/service/vi/vi_results.h"
28#include "video_core/gpu.h"
29#include "video_core/host1x/host1x.h"
30#include "video_core/host1x/syncpoint_manager.h"
31 11
32namespace Service::Nvnflinger { 12namespace Service::Nvnflinger {
33 13
34constexpr auto frame_ns = std::chrono::nanoseconds{1000000000 / 60}; 14void LoopProcess(Core::System& system) {
35 15 const auto binder_server = std::make_shared<HosBinderDriverServer>();
36void Nvnflinger::SplitVSync(std::stop_token stop_token) { 16 const auto surface_flinger = std::make_shared<SurfaceFlinger>(system, *binder_server);
37 system.RegisterHostThread();
38 std::string name = "VSyncThread";
39 MicroProfileOnThreadCreate(name.c_str());
40
41 // Cleanup
42 SCOPE_EXIT({ MicroProfileOnThreadExit(); });
43
44 Common::SetCurrentThreadName(name.c_str());
45 Common::SetCurrentThreadPriority(Common::ThreadPriority::High);
46
47 while (!stop_token.stop_requested()) {
48 vsync_signal.Wait();
49
50 const auto lock_guard = Lock();
51
52 if (!is_abandoned) {
53 Compose();
54 }
55 }
56}
57
58Nvnflinger::Nvnflinger(Core::System& system_, HosBinderDriverServer& hos_binder_driver_server_)
59 : system(system_), service_context(system_, "nvnflinger"),
60 hos_binder_driver_server(hos_binder_driver_server_) {
61 displays.emplace_back(0, "Default", hos_binder_driver_server, service_context, system);
62 displays.emplace_back(1, "External", hos_binder_driver_server, service_context, system);
63 displays.emplace_back(2, "Edid", hos_binder_driver_server, service_context, system);
64 displays.emplace_back(3, "Internal", hos_binder_driver_server, service_context, system);
65 displays.emplace_back(4, "Null", hos_binder_driver_server, service_context, system);
66 guard = std::make_shared<std::mutex>();
67
68 // Schedule the screen composition events
69 multi_composition_event = Core::Timing::CreateEvent(
70 "ScreenComposition",
71 [this](s64 time,
72 std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
73 vsync_signal.Set();
74 return std::chrono::nanoseconds(GetNextTicks());
75 });
76
77 single_composition_event = Core::Timing::CreateEvent(
78 "ScreenComposition",
79 [this](s64 time,
80 std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
81 const auto lock_guard = Lock();
82 Compose();
83
84 return std::chrono::nanoseconds(GetNextTicks());
85 });
86
87 if (system.IsMulticore()) {
88 system.CoreTiming().ScheduleLoopingEvent(frame_ns, frame_ns, multi_composition_event);
89 vsync_thread = std::jthread([this](std::stop_token token) { SplitVSync(token); });
90 } else {
91 system.CoreTiming().ScheduleLoopingEvent(frame_ns, frame_ns, single_composition_event);
92 }
93}
94
95Nvnflinger::~Nvnflinger() {
96 if (system.IsMulticore()) {
97 system.CoreTiming().UnscheduleEvent(multi_composition_event);
98 vsync_thread.request_stop();
99 vsync_signal.Set();
100 } else {
101 system.CoreTiming().UnscheduleEvent(single_composition_event);
102 }
103
104 ShutdownLayers();
105
106 if (nvdrv) {
107 nvdrv->Close(disp_fd);
108 }
109}
110
111void Nvnflinger::ShutdownLayers() {
112 // Abandon consumers.
113 {
114 const auto lock_guard = Lock();
115 for (auto& display : displays) {
116 display.Abandon();
117 }
118
119 is_abandoned = true;
120 }
121
122 // Join the vsync thread, if it exists.
123 vsync_thread = {};
124}
125
126void Nvnflinger::SetNVDrvInstance(std::shared_ptr<Nvidia::Module> instance) {
127 nvdrv = std::move(instance);
128 disp_fd = nvdrv->Open("/dev/nvdisp_disp0", {});
129}
130
131std::optional<u64> Nvnflinger::OpenDisplay(std::string_view name) {
132 const auto lock_guard = Lock();
133
134 LOG_DEBUG(Service_Nvnflinger, "Opening \"{}\" display", name);
135
136 const auto itr =
137 std::find_if(displays.begin(), displays.end(),
138 [&](const VI::Display& display) { return display.GetName() == name; });
139
140 if (itr == displays.end()) {
141 return std::nullopt;
142 }
143
144 return itr->GetID();
145}
146
147bool Nvnflinger::CloseDisplay(u64 display_id) {
148 const auto lock_guard = Lock();
149 auto* const display = FindDisplay(display_id);
150
151 if (display == nullptr) {
152 return false;
153 }
154
155 display->Reset();
156
157 return true;
158}
159
160std::optional<u64> Nvnflinger::CreateLayer(u64 display_id, LayerBlending blending) {
161 const auto lock_guard = Lock();
162 auto* const display = FindDisplay(display_id);
163
164 if (display == nullptr) {
165 return std::nullopt;
166 }
167
168 const u64 layer_id = next_layer_id++;
169 CreateLayerAtId(*display, layer_id, blending);
170 return layer_id;
171}
172
173void Nvnflinger::CreateLayerAtId(VI::Display& display, u64 layer_id, LayerBlending blending) {
174 const auto buffer_id = next_buffer_queue_id++;
175 display.CreateLayer(layer_id, buffer_id, nvdrv->container);
176 display.FindLayer(layer_id)->SetBlending(blending);
177}
178
179bool Nvnflinger::OpenLayer(u64 layer_id) {
180 const auto lock_guard = Lock();
181
182 for (auto& display : displays) {
183 if (auto* layer = display.FindLayer(layer_id); layer) {
184 return layer->Open();
185 }
186 }
187
188 return false;
189}
190
191bool Nvnflinger::CloseLayer(u64 layer_id) {
192 const auto lock_guard = Lock();
193
194 for (auto& display : displays) {
195 if (auto* layer = display.FindLayer(layer_id); layer) {
196 return layer->Close();
197 }
198 }
199
200 return false;
201}
202
203void Nvnflinger::SetLayerVisibility(u64 layer_id, bool visible) {
204 const auto lock_guard = Lock();
205
206 for (auto& display : displays) {
207 if (auto* layer = display.FindLayer(layer_id); layer) {
208 layer->SetVisibility(visible);
209 }
210 }
211}
212
213void Nvnflinger::DestroyLayer(u64 layer_id) {
214 const auto lock_guard = Lock();
215
216 for (auto& display : displays) {
217 display.DestroyLayer(layer_id);
218 }
219}
220
221std::optional<u32> Nvnflinger::FindBufferQueueId(u64 display_id, u64 layer_id) {
222 const auto lock_guard = Lock();
223 const auto* const layer = FindLayer(display_id, layer_id);
224
225 if (layer == nullptr) {
226 return std::nullopt;
227 }
228
229 return layer->GetBinderId();
230}
231
232Result Nvnflinger::FindVsyncEvent(Kernel::KReadableEvent** out_vsync_event, u64 display_id) {
233 const auto lock_guard = Lock();
234 auto* const display = FindDisplay(display_id);
235
236 if (display == nullptr) {
237 return VI::ResultNotFound;
238 }
239
240 *out_vsync_event = display->GetVSyncEvent();
241 return ResultSuccess;
242}
243
244VI::Display* Nvnflinger::FindDisplay(u64 display_id) {
245 const auto itr =
246 std::find_if(displays.begin(), displays.end(),
247 [&](const VI::Display& display) { return display.GetID() == display_id; });
248
249 if (itr == displays.end()) {
250 return nullptr;
251 }
252
253 return &*itr;
254}
255
256const VI::Display* Nvnflinger::FindDisplay(u64 display_id) const {
257 const auto itr =
258 std::find_if(displays.begin(), displays.end(),
259 [&](const VI::Display& display) { return display.GetID() == display_id; });
260
261 if (itr == displays.end()) {
262 return nullptr;
263 }
264
265 return &*itr;
266}
267
268VI::Layer* Nvnflinger::FindLayer(u64 display_id, u64 layer_id) {
269 auto* const display = FindDisplay(display_id);
270
271 if (display == nullptr) {
272 return nullptr;
273 }
274
275 return display->FindLayer(layer_id);
276}
277
278void Nvnflinger::Compose() {
279 for (auto& display : displays) {
280 // Trigger vsync for this display at the end of drawing
281 SCOPE_EXIT({ display.SignalVSyncEvent(); });
282
283 // Don't do anything for displays without layers.
284 if (!display.HasLayers()) {
285 continue;
286 }
287
288 if (!system.IsPoweredOn()) {
289 return; // We are likely shutting down
290 }
291
292 auto nvdisp = nvdrv->GetDevice<Nvidia::Devices::nvdisp_disp0>(disp_fd);
293 ASSERT(nvdisp);
294
295 swap_interval = display.GetComposer().ComposeLocked(&compose_speed_scale, display, *nvdisp);
296 }
297}
298
299s64 Nvnflinger::GetNextTicks() const {
300 const auto& settings = Settings::values;
301 auto speed_scale = 1.f;
302 if (settings.use_multi_core.GetValue()) {
303 if (settings.use_speed_limit.GetValue()) {
304 // Scales the speed based on speed_limit setting on MC. SC is handled by
305 // SpeedLimiter::DoSpeedLimiting.
306 speed_scale = 100.f / settings.speed_limit.GetValue();
307 } else {
308 // Run at unlocked framerate.
309 speed_scale = 0.01f;
310 }
311 }
312
313 // Adjust by speed limit determined during composition.
314 speed_scale /= compose_speed_scale;
315
316 if (system.GetNVDECActive() && settings.use_video_framerate.GetValue()) {
317 // Run at intended presentation rate during video playback.
318 speed_scale = 1.f;
319 }
320
321 const f32 effective_fps = 60.f / static_cast<f32>(swap_interval);
322 return static_cast<s64>(speed_scale * (1000000000.f / effective_fps));
323}
324
325FbShareBufferManager& Nvnflinger::GetSystemBufferManager() {
326 const auto lock_guard = Lock();
327
328 if (!system_buffer_manager) {
329 system_buffer_manager = std::make_unique<FbShareBufferManager>(system, *this, nvdrv);
330 }
331 17
332 return *system_buffer_manager; 18 auto server_manager = std::make_unique<ServerManager>(system);
19 server_manager->RegisterNamedService(
20 "dispdrv", std::make_shared<IHOSBinderDriver>(system, binder_server, surface_flinger));
21 ServerManager::RunServer(std::move(server_manager));
333} 22}
334 23
335} // namespace Service::Nvnflinger 24} // namespace Service::Nvnflinger
diff --git a/src/core/hle/service/nvnflinger/nvnflinger.h b/src/core/hle/service/nvnflinger/nvnflinger.h
index 4cf4f069d..5c41f3013 100644
--- a/src/core/hle/service/nvnflinger/nvnflinger.h
+++ b/src/core/hle/service/nvnflinger/nvnflinger.h
@@ -3,170 +3,12 @@
3 3
4#pragma once 4#pragma once
5 5
6#include <list> 6namespace Core {
7#include <memory> 7class System;
8#include <mutex> 8}
9#include <optional>
10#include <thread>
11#include <vector>
12
13#include "common/common_types.h"
14#include "common/polyfill_thread.h"
15#include "common/thread.h"
16#include "core/hle/result.h"
17#include "core/hle/service/kernel_helpers.h"
18#include "core/hle/service/nvnflinger/hwc_layer.h"
19
20namespace Common {
21class Event;
22} // namespace Common
23
24namespace Core::Timing {
25class CoreTiming;
26struct EventType;
27} // namespace Core::Timing
28
29namespace Kernel {
30class KReadableEvent;
31} // namespace Kernel
32
33namespace Service::Nvidia {
34class Module;
35} // namespace Service::Nvidia
36
37namespace Service::VI {
38class Display;
39class Layer;
40} // namespace Service::VI
41
42namespace Service::android {
43class BufferQueueCore;
44class BufferQueueProducer;
45} // namespace Service::android
46 9
47namespace Service::Nvnflinger { 10namespace Service::Nvnflinger {
48 11
49class FbShareBufferManager; 12void LoopProcess(Core::System& system);
50class HardwareComposer;
51class HosBinderDriverServer;
52
53class Nvnflinger final {
54public:
55 explicit Nvnflinger(Core::System& system_, HosBinderDriverServer& hos_binder_driver_server_);
56 ~Nvnflinger();
57
58 void ShutdownLayers();
59
60 /// Sets the NVDrv module instance to use to send buffers to the GPU.
61 void SetNVDrvInstance(std::shared_ptr<Nvidia::Module> instance);
62
63 /// Opens the specified display and returns the ID.
64 ///
65 /// If an invalid display name is provided, then an empty optional is returned.
66 [[nodiscard]] std::optional<u64> OpenDisplay(std::string_view name);
67
68 /// Closes the specified display by its ID.
69 ///
70 /// Returns false if an invalid display ID is provided.
71 [[nodiscard]] bool CloseDisplay(u64 display_id);
72
73 /// Creates a layer on the specified display and returns the layer ID.
74 ///
75 /// If an invalid display ID is specified, then an empty optional is returned.
76 [[nodiscard]] std::optional<u64> CreateLayer(u64 display_id,
77 LayerBlending blending = LayerBlending::None);
78
79 /// Opens a layer on all displays for the given layer ID.
80 bool OpenLayer(u64 layer_id);
81
82 /// Closes a layer on all displays for the given layer ID.
83 bool CloseLayer(u64 layer_id);
84
85 /// Makes a layer visible on all displays for the given layer ID.
86 void SetLayerVisibility(u64 layer_id, bool visible);
87
88 /// Destroys the given layer ID.
89 void DestroyLayer(u64 layer_id);
90
91 /// Finds the buffer queue ID of the specified layer in the specified display.
92 ///
93 /// If an invalid display ID or layer ID is provided, then an empty optional is returned.
94 [[nodiscard]] std::optional<u32> FindBufferQueueId(u64 display_id, u64 layer_id);
95
96 /// Gets the vsync event for the specified display.
97 ///
98 /// If an invalid display ID is provided, then VI::ResultNotFound is returned.
99 /// If the vsync event has already been retrieved, then VI::ResultPermissionDenied is returned.
100 [[nodiscard]] Result FindVsyncEvent(Kernel::KReadableEvent** out_vsync_event, u64 display_id);
101
102 /// Performs a composition request to the emulated nvidia GPU and triggers the vsync events when
103 /// finished.
104 void Compose();
105
106 [[nodiscard]] s64 GetNextTicks() const;
107
108 FbShareBufferManager& GetSystemBufferManager();
109
110private:
111 struct Layer {
112 std::unique_ptr<android::BufferQueueCore> core;
113 std::unique_ptr<android::BufferQueueProducer> producer;
114 };
115
116 friend class FbShareBufferManager;
117
118private:
119 [[nodiscard]] std::unique_lock<std::mutex> Lock() const {
120 return std::unique_lock{*guard};
121 }
122
123 /// Finds the display identified by the specified ID.
124 [[nodiscard]] VI::Display* FindDisplay(u64 display_id);
125
126 /// Finds the display identified by the specified ID.
127 [[nodiscard]] const VI::Display* FindDisplay(u64 display_id) const;
128
129 /// Finds the layer identified by the specified ID in the desired display.
130 [[nodiscard]] VI::Layer* FindLayer(u64 display_id, u64 layer_id);
131
132 /// Creates a layer with the specified layer ID in the desired display.
133 void CreateLayerAtId(VI::Display& display, u64 layer_id, LayerBlending blending);
134
135 void SplitVSync(std::stop_token stop_token);
136
137 std::shared_ptr<Nvidia::Module> nvdrv;
138 s32 disp_fd;
139
140 std::list<VI::Display> displays;
141
142 /// Id to use for the next layer that is created, this counter is shared among all displays.
143 u64 next_layer_id = 1;
144 /// Id to use for the next buffer queue that is created, this counter is shared among all
145 /// layers.
146 u32 next_buffer_queue_id = 1;
147
148 s32 swap_interval = 1;
149 f32 compose_speed_scale = 1.0f;
150
151 bool is_abandoned = false;
152
153 /// Event that handles screen composition.
154 std::shared_ptr<Core::Timing::EventType> multi_composition_event;
155 std::shared_ptr<Core::Timing::EventType> single_composition_event;
156
157 std::unique_ptr<FbShareBufferManager> system_buffer_manager;
158
159 std::shared_ptr<std::mutex> guard;
160
161 Core::System& system;
162
163 Common::Event vsync_signal;
164
165 std::jthread vsync_thread;
166
167 KernelHelpers::ServiceContext service_context;
168
169 HosBinderDriverServer& hos_binder_driver_server;
170};
171 13
172} // namespace Service::Nvnflinger 14} // namespace Service::Nvnflinger
diff --git a/src/core/hle/service/nvnflinger/surface_flinger.cpp b/src/core/hle/service/nvnflinger/surface_flinger.cpp
new file mode 100644
index 000000000..41a705717
--- /dev/null
+++ b/src/core/hle/service/nvnflinger/surface_flinger.cpp
@@ -0,0 +1,124 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/core.h"
5#include "core/hle/service/nvdrv/devices/nvdisp_disp0.h"
6#include "core/hle/service/nvdrv/nvdrv_interface.h"
7#include "core/hle/service/nvnflinger/display.h"
8#include "core/hle/service/nvnflinger/hos_binder_driver_server.h"
9#include "core/hle/service/nvnflinger/surface_flinger.h"
10#include "core/hle/service/sm/sm.h"
11
12#include "core/hle/service/nvnflinger/buffer_queue_consumer.h"
13#include "core/hle/service/nvnflinger/buffer_queue_core.h"
14#include "core/hle/service/nvnflinger/buffer_queue_producer.h"
15
16namespace Service::Nvnflinger {
17
18SurfaceFlinger::SurfaceFlinger(Core::System& system, HosBinderDriverServer& server)
19 : m_system(system), m_server(server), m_context(m_system, "SurfaceFlinger") {
20 nvdrv = m_system.ServiceManager().GetService<Nvidia::NVDRV>("nvdrv:s", true)->GetModule();
21 disp_fd = nvdrv->Open("/dev/nvdisp_disp0", {});
22}
23
24SurfaceFlinger::~SurfaceFlinger() {
25 nvdrv->Close(disp_fd);
26}
27
28void SurfaceFlinger::AddDisplay(u64 display_id) {
29 m_displays.emplace_back(display_id);
30}
31
32void SurfaceFlinger::RemoveDisplay(u64 display_id) {
33 std::erase_if(m_displays, [&](auto& display) { return display.id == display_id; });
34}
35
36bool SurfaceFlinger::ComposeDisplay(s32* out_swap_interval, f32* out_compose_speed_scale,
37 u64 display_id) {
38 auto* const display = this->FindDisplay(display_id);
39 if (!display || !display->HasLayers()) {
40 return false;
41 }
42
43 *out_swap_interval =
44 m_composer.ComposeLocked(out_compose_speed_scale, *display,
45 *nvdrv->GetDevice<Nvidia::Devices::nvdisp_disp0>(disp_fd));
46 return true;
47}
48
49void SurfaceFlinger::AddLayerToDisplayStack(u64 display_id, s32 consumer_binder_id) {
50 auto* const display = this->FindDisplay(display_id);
51 auto binder = std::static_pointer_cast<android::BufferQueueConsumer>(
52 m_server.TryGetBinder(consumer_binder_id));
53
54 if (!display || !binder) {
55 return;
56 }
57
58 auto buffer_item_consumer = std::make_shared<android::BufferItemConsumer>(std::move(binder));
59 buffer_item_consumer->Connect(false);
60
61 display->stack.layers.emplace_back(std::move(buffer_item_consumer), consumer_binder_id);
62}
63
64void SurfaceFlinger::RemoveLayerFromDisplayStack(u64 display_id, s32 consumer_binder_id) {
65 auto* const display = this->FindDisplay(display_id);
66 if (!display) {
67 return;
68 }
69
70 m_composer.RemoveLayerLocked(*display, consumer_binder_id);
71 std::erase_if(display->stack.layers,
72 [&](auto& layer) { return layer.consumer_id == consumer_binder_id; });
73}
74
75void SurfaceFlinger::SetLayerVisibility(s32 consumer_binder_id, bool visible) {
76 if (auto* layer = this->FindLayer(consumer_binder_id); layer != nullptr) {
77 layer->visible = visible;
78 return;
79 }
80}
81
82void SurfaceFlinger::SetLayerBlending(s32 consumer_binder_id, LayerBlending blending) {
83 if (auto* layer = this->FindLayer(consumer_binder_id); layer != nullptr) {
84 layer->blending = blending;
85 return;
86 }
87}
88
89Display* SurfaceFlinger::FindDisplay(u64 display_id) {
90 for (auto& display : m_displays) {
91 if (display.id == display_id) {
92 return &display;
93 }
94 }
95
96 return nullptr;
97}
98
99Layer* SurfaceFlinger::FindLayer(s32 consumer_binder_id) {
100 for (auto& display : m_displays) {
101 if (auto* layer = display.FindLayer(consumer_binder_id); layer != nullptr) {
102 return layer;
103 }
104 }
105
106 return nullptr;
107}
108
109void SurfaceFlinger::CreateBufferQueue(s32* out_consumer_binder_id, s32* out_producer_binder_id) {
110 auto& nvmap = nvdrv->GetContainer().GetNvMapFile();
111 auto core = std::make_shared<android::BufferQueueCore>();
112 auto producer = std::make_shared<android::BufferQueueProducer>(m_context, core, nvmap);
113 auto consumer = std::make_shared<android::BufferQueueConsumer>(core);
114
115 *out_consumer_binder_id = m_server.RegisterBinder(std::move(consumer));
116 *out_producer_binder_id = m_server.RegisterBinder(std::move(producer));
117}
118
119void SurfaceFlinger::DestroyBufferQueue(s32 consumer_binder_id, s32 producer_binder_id) {
120 m_server.UnregisterBinder(producer_binder_id);
121 m_server.UnregisterBinder(consumer_binder_id);
122}
123
124} // namespace Service::Nvnflinger
diff --git a/src/core/hle/service/nvnflinger/surface_flinger.h b/src/core/hle/service/nvnflinger/surface_flinger.h
new file mode 100644
index 000000000..d8c53fbda
--- /dev/null
+++ b/src/core/hle/service/nvnflinger/surface_flinger.h
@@ -0,0 +1,65 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <vector>
7
8#include "common/common_types.h"
9#include "core/hle/service/kernel_helpers.h"
10#include "core/hle/service/nvnflinger/hardware_composer.h"
11
12namespace Core {
13class System;
14}
15
16namespace Service::Nvidia {
17class Module;
18}
19
20// TODO: ISurfaceComposer
21// TODO: ISurfaceComposerClient
22
23namespace Service::Nvnflinger {
24
25struct Display;
26class HosBinderDriverServer;
27enum class LayerBlending : u32;
28struct Layer;
29
30class SurfaceFlinger {
31public:
32 explicit SurfaceFlinger(Core::System& system, HosBinderDriverServer& server);
33 ~SurfaceFlinger();
34
35 void AddDisplay(u64 display_id);
36 void RemoveDisplay(u64 display_id);
37 bool ComposeDisplay(s32* out_swap_interval, f32* out_compose_speed_scale, u64 display_id);
38
39 void AddLayerToDisplayStack(u64 display_id, s32 consumer_binder_id);
40 void RemoveLayerFromDisplayStack(u64 display_id, s32 consumer_binder_id);
41
42 void SetLayerVisibility(s32 consumer_binder_id, bool visible);
43 void SetLayerBlending(s32 consumer_binder_id, LayerBlending blending);
44
45private:
46 Display* FindDisplay(u64 display_id);
47 Layer* FindLayer(s32 consumer_binder_id);
48
49public:
50 // TODO: these don't belong here
51 void CreateBufferQueue(s32* out_consumer_binder_id, s32* out_producer_binder_id);
52 void DestroyBufferQueue(s32 consumer_binder_id, s32 producer_binder_id);
53
54private:
55 Core::System& m_system;
56 HosBinderDriverServer& m_server;
57 KernelHelpers::ServiceContext m_context;
58
59 std::vector<Display> m_displays;
60 std::shared_ptr<Nvidia::Module> nvdrv;
61 s32 disp_fd;
62 HardwareComposer m_composer;
63};
64
65} // namespace Service::Nvnflinger
diff --git a/src/core/hle/service/psc/time/static.cpp b/src/core/hle/service/psc/time/static.cpp
index 24b85cc61..9a0adb295 100644
--- a/src/core/hle/service/psc/time/static.cpp
+++ b/src/core/hle/service/psc/time/static.cpp
@@ -144,7 +144,9 @@ Result StaticService::GetStandardSteadyClockRtcValue(Out<s64> out_rtc_value) {
144 144
145Result StaticService::IsStandardUserSystemClockAutomaticCorrectionEnabled( 145Result StaticService::IsStandardUserSystemClockAutomaticCorrectionEnabled(
146 Out<bool> out_is_enabled) { 146 Out<bool> out_is_enabled) {
147 SCOPE_EXIT({ LOG_DEBUG(Service_Time, "called. out_is_enabled={}", *out_is_enabled); }); 147 SCOPE_EXIT {
148 LOG_DEBUG(Service_Time, "called. out_is_enabled={}", *out_is_enabled);
149 };
148 150
149 R_UNLESS(m_user_system_clock.IsInitialized(), ResultClockUninitialized); 151 R_UNLESS(m_user_system_clock.IsInitialized(), ResultClockUninitialized);
150 152
@@ -180,7 +182,9 @@ Result StaticService::GetStandardUserSystemClockInitialYear(Out<s32> out_year) {
180} 182}
181 183
182Result StaticService::IsStandardNetworkSystemClockAccuracySufficient(Out<bool> out_is_sufficient) { 184Result StaticService::IsStandardNetworkSystemClockAccuracySufficient(Out<bool> out_is_sufficient) {
183 SCOPE_EXIT({ LOG_DEBUG(Service_Time, "called. out_is_sufficient={}", *out_is_sufficient); }); 185 SCOPE_EXIT {
186 LOG_DEBUG(Service_Time, "called. out_is_sufficient={}", *out_is_sufficient);
187 };
184 188
185 *out_is_sufficient = m_network_system_clock.IsAccuracySufficient(); 189 *out_is_sufficient = m_network_system_clock.IsAccuracySufficient();
186 190
@@ -189,7 +193,9 @@ Result StaticService::IsStandardNetworkSystemClockAccuracySufficient(Out<bool> o
189 193
190Result StaticService::GetStandardUserSystemClockAutomaticCorrectionUpdatedTime( 194Result StaticService::GetStandardUserSystemClockAutomaticCorrectionUpdatedTime(
191 Out<SteadyClockTimePoint> out_time_point) { 195 Out<SteadyClockTimePoint> out_time_point) {
192 SCOPE_EXIT({ LOG_DEBUG(Service_Time, "called. out_time_point={}", *out_time_point); }); 196 SCOPE_EXIT {
197 LOG_DEBUG(Service_Time, "called. out_time_point={}", *out_time_point);
198 };
193 199
194 R_UNLESS(m_user_system_clock.IsInitialized(), ResultClockUninitialized); 200 R_UNLESS(m_user_system_clock.IsInitialized(), ResultClockUninitialized);
195 201
@@ -200,7 +206,9 @@ Result StaticService::GetStandardUserSystemClockAutomaticCorrectionUpdatedTime(
200 206
201Result StaticService::CalculateMonotonicSystemClockBaseTimePoint( 207Result StaticService::CalculateMonotonicSystemClockBaseTimePoint(
202 Out<s64> out_time, const SystemClockContext& context) { 208 Out<s64> out_time, const SystemClockContext& context) {
203 SCOPE_EXIT({ LOG_DEBUG(Service_Time, "called. context={} out_time={}", context, *out_time); }); 209 SCOPE_EXIT {
210 LOG_DEBUG(Service_Time, "called. context={} out_time={}", context, *out_time);
211 };
204 212
205 R_UNLESS(m_time->m_standard_steady_clock.IsInitialized(), ResultClockUninitialized); 213 R_UNLESS(m_time->m_standard_steady_clock.IsInitialized(), ResultClockUninitialized);
206 214
@@ -219,8 +227,9 @@ Result StaticService::CalculateMonotonicSystemClockBaseTimePoint(
219} 227}
220 228
221Result StaticService::GetClockSnapshot(OutClockSnapshot out_snapshot, TimeType type) { 229Result StaticService::GetClockSnapshot(OutClockSnapshot out_snapshot, TimeType type) {
222 SCOPE_EXIT( 230 SCOPE_EXIT {
223 { LOG_DEBUG(Service_Time, "called. type={} out_snapshot={}", type, *out_snapshot); }); 231 LOG_DEBUG(Service_Time, "called. type={} out_snapshot={}", type, *out_snapshot);
232 };
224 233
225 SystemClockContext user_context{}; 234 SystemClockContext user_context{};
226 R_TRY(m_user_system_clock.GetContext(user_context)); 235 R_TRY(m_user_system_clock.GetContext(user_context));
@@ -234,11 +243,11 @@ Result StaticService::GetClockSnapshot(OutClockSnapshot out_snapshot, TimeType t
234Result StaticService::GetClockSnapshotFromSystemClockContext( 243Result StaticService::GetClockSnapshotFromSystemClockContext(
235 TimeType type, OutClockSnapshot out_snapshot, const SystemClockContext& user_context, 244 TimeType type, OutClockSnapshot out_snapshot, const SystemClockContext& user_context,
236 const SystemClockContext& network_context) { 245 const SystemClockContext& network_context) {
237 SCOPE_EXIT({ 246 SCOPE_EXIT {
238 LOG_DEBUG(Service_Time, 247 LOG_DEBUG(Service_Time,
239 "called. type={} user_context={} network_context={} out_snapshot={}", type, 248 "called. type={} user_context={} network_context={} out_snapshot={}", type,
240 user_context, network_context, *out_snapshot); 249 user_context, network_context, *out_snapshot);
241 }); 250 };
242 251
243 R_RETURN(GetClockSnapshotImpl(out_snapshot, user_context, network_context, type)); 252 R_RETURN(GetClockSnapshotImpl(out_snapshot, user_context, network_context, type));
244} 253}
@@ -246,9 +255,9 @@ Result StaticService::GetClockSnapshotFromSystemClockContext(
246Result StaticService::CalculateStandardUserSystemClockDifferenceByUser(Out<s64> out_difference, 255Result StaticService::CalculateStandardUserSystemClockDifferenceByUser(Out<s64> out_difference,
247 InClockSnapshot a, 256 InClockSnapshot a,
248 InClockSnapshot b) { 257 InClockSnapshot b) {
249 SCOPE_EXIT({ 258 SCOPE_EXIT {
250 LOG_DEBUG(Service_Time, "called. a={} b={} out_difference={}", *a, *b, *out_difference); 259 LOG_DEBUG(Service_Time, "called. a={} b={} out_difference={}", *a, *b, *out_difference);
251 }); 260 };
252 261
253 auto diff_s = 262 auto diff_s =
254 std::chrono::seconds(b->user_context.offset) - std::chrono::seconds(a->user_context.offset); 263 std::chrono::seconds(b->user_context.offset) - std::chrono::seconds(a->user_context.offset);
@@ -276,7 +285,9 @@ Result StaticService::CalculateStandardUserSystemClockDifferenceByUser(Out<s64>
276 285
277Result StaticService::CalculateSpanBetween(Out<s64> out_time, InClockSnapshot a, 286Result StaticService::CalculateSpanBetween(Out<s64> out_time, InClockSnapshot a,
278 InClockSnapshot b) { 287 InClockSnapshot b) {
279 SCOPE_EXIT({ LOG_DEBUG(Service_Time, "called. a={} b={} out_time={}", *a, *b, *out_time); }); 288 SCOPE_EXIT {
289 LOG_DEBUG(Service_Time, "called. a={} b={} out_time={}", *a, *b, *out_time);
290 };
280 291
281 s64 time_s{}; 292 s64 time_s{};
282 auto res = 293 auto res =
diff --git a/src/core/hle/service/psc/time/steady_clock.cpp b/src/core/hle/service/psc/time/steady_clock.cpp
index 948610a2b..78dcf532c 100644
--- a/src/core/hle/service/psc/time/steady_clock.cpp
+++ b/src/core/hle/service/psc/time/steady_clock.cpp
@@ -29,7 +29,9 @@ SteadyClock::SteadyClock(Core::System& system_, std::shared_ptr<TimeManager> man
29} 29}
30 30
31Result SteadyClock::GetCurrentTimePoint(Out<SteadyClockTimePoint> out_time_point) { 31Result SteadyClock::GetCurrentTimePoint(Out<SteadyClockTimePoint> out_time_point) {
32 SCOPE_EXIT({ LOG_DEBUG(Service_Time, "called. out_time_point={}", *out_time_point); }); 32 SCOPE_EXIT {
33 LOG_DEBUG(Service_Time, "called. out_time_point={}", *out_time_point);
34 };
33 35
34 R_UNLESS(m_can_write_uninitialized_clock || m_clock_core.IsInitialized(), 36 R_UNLESS(m_can_write_uninitialized_clock || m_clock_core.IsInitialized(),
35 ResultClockUninitialized); 37 ResultClockUninitialized);
@@ -38,7 +40,9 @@ Result SteadyClock::GetCurrentTimePoint(Out<SteadyClockTimePoint> out_time_point
38} 40}
39 41
40Result SteadyClock::GetTestOffset(Out<s64> out_test_offset) { 42Result SteadyClock::GetTestOffset(Out<s64> out_test_offset) {
41 SCOPE_EXIT({ LOG_DEBUG(Service_Time, "called. out_test_offset={}", *out_test_offset); }); 43 SCOPE_EXIT {
44 LOG_DEBUG(Service_Time, "called. out_test_offset={}", *out_test_offset);
45 };
42 46
43 R_UNLESS(m_can_write_uninitialized_clock || m_clock_core.IsInitialized(), 47 R_UNLESS(m_can_write_uninitialized_clock || m_clock_core.IsInitialized(),
44 ResultClockUninitialized); 48 ResultClockUninitialized);
@@ -59,7 +63,9 @@ Result SteadyClock::SetTestOffset(s64 test_offset) {
59} 63}
60 64
61Result SteadyClock::GetRtcValue(Out<s64> out_rtc_value) { 65Result SteadyClock::GetRtcValue(Out<s64> out_rtc_value) {
62 SCOPE_EXIT({ LOG_DEBUG(Service_Time, "called. out_rtc_value={}", *out_rtc_value); }); 66 SCOPE_EXIT {
67 LOG_DEBUG(Service_Time, "called. out_rtc_value={}", *out_rtc_value);
68 };
63 69
64 R_UNLESS(m_can_write_uninitialized_clock || m_clock_core.IsInitialized(), 70 R_UNLESS(m_can_write_uninitialized_clock || m_clock_core.IsInitialized(),
65 ResultClockUninitialized); 71 ResultClockUninitialized);
@@ -68,7 +74,9 @@ Result SteadyClock::GetRtcValue(Out<s64> out_rtc_value) {
68} 74}
69 75
70Result SteadyClock::IsRtcResetDetected(Out<bool> out_is_detected) { 76Result SteadyClock::IsRtcResetDetected(Out<bool> out_is_detected) {
71 SCOPE_EXIT({ LOG_DEBUG(Service_Time, "called. out_is_detected={}", *out_is_detected); }); 77 SCOPE_EXIT {
78 LOG_DEBUG(Service_Time, "called. out_is_detected={}", *out_is_detected);
79 };
72 80
73 R_UNLESS(m_can_write_uninitialized_clock || m_clock_core.IsInitialized(), 81 R_UNLESS(m_can_write_uninitialized_clock || m_clock_core.IsInitialized(),
74 ResultClockUninitialized); 82 ResultClockUninitialized);
@@ -78,7 +86,9 @@ Result SteadyClock::IsRtcResetDetected(Out<bool> out_is_detected) {
78} 86}
79 87
80Result SteadyClock::GetSetupResultValue(Out<Result> out_result) { 88Result SteadyClock::GetSetupResultValue(Out<Result> out_result) {
81 SCOPE_EXIT({ LOG_DEBUG(Service_Time, "called. out_result=0x{:X}", out_result->raw); }); 89 SCOPE_EXIT {
90 LOG_DEBUG(Service_Time, "called. out_result=0x{:X}", out_result->raw);
91 };
82 92
83 R_UNLESS(m_can_write_uninitialized_clock || m_clock_core.IsInitialized(), 93 R_UNLESS(m_can_write_uninitialized_clock || m_clock_core.IsInitialized(),
84 ResultClockUninitialized); 94 ResultClockUninitialized);
@@ -88,8 +98,9 @@ Result SteadyClock::GetSetupResultValue(Out<Result> out_result) {
88} 98}
89 99
90Result SteadyClock::GetInternalOffset(Out<s64> out_internal_offset) { 100Result SteadyClock::GetInternalOffset(Out<s64> out_internal_offset) {
91 SCOPE_EXIT( 101 SCOPE_EXIT {
92 { LOG_DEBUG(Service_Time, "called. out_internal_offset={}", *out_internal_offset); }); 102 LOG_DEBUG(Service_Time, "called. out_internal_offset={}", *out_internal_offset);
103 };
93 104
94 R_UNLESS(m_can_write_uninitialized_clock || m_clock_core.IsInitialized(), 105 R_UNLESS(m_can_write_uninitialized_clock || m_clock_core.IsInitialized(),
95 ResultClockUninitialized); 106 ResultClockUninitialized);
diff --git a/src/core/hle/service/psc/time/system_clock.cpp b/src/core/hle/service/psc/time/system_clock.cpp
index b4e9264d8..9f841d8e0 100644
--- a/src/core/hle/service/psc/time/system_clock.cpp
+++ b/src/core/hle/service/psc/time/system_clock.cpp
@@ -26,7 +26,9 @@ SystemClock::SystemClock(Core::System& system_, SystemClockCore& clock_core, boo
26} 26}
27 27
28Result SystemClock::GetCurrentTime(Out<s64> out_time) { 28Result SystemClock::GetCurrentTime(Out<s64> out_time) {
29 SCOPE_EXIT({ LOG_DEBUG(Service_Time, "called. out_time={}", *out_time); }); 29 SCOPE_EXIT {
30 LOG_DEBUG(Service_Time, "called. out_time={}", *out_time);
31 };
30 32
31 R_UNLESS(m_can_write_uninitialized_clock || m_clock_core.IsInitialized(), 33 R_UNLESS(m_can_write_uninitialized_clock || m_clock_core.IsInitialized(),
32 ResultClockUninitialized); 34 ResultClockUninitialized);
@@ -45,7 +47,9 @@ Result SystemClock::SetCurrentTime(s64 time) {
45} 47}
46 48
47Result SystemClock::GetSystemClockContext(Out<SystemClockContext> out_context) { 49Result SystemClock::GetSystemClockContext(Out<SystemClockContext> out_context) {
48 SCOPE_EXIT({ LOG_DEBUG(Service_Time, "called. out_context={}", *out_context); }); 50 SCOPE_EXIT {
51 LOG_DEBUG(Service_Time, "called. out_context={}", *out_context);
52 };
49 53
50 R_UNLESS(m_can_write_uninitialized_clock || m_clock_core.IsInitialized(), 54 R_UNLESS(m_can_write_uninitialized_clock || m_clock_core.IsInitialized(),
51 ResultClockUninitialized); 55 ResultClockUninitialized);
diff --git a/src/core/hle/service/psc/time/time_zone_service.cpp b/src/core/hle/service/psc/time/time_zone_service.cpp
index 2f80030a4..9e0674f27 100644
--- a/src/core/hle/service/psc/time/time_zone_service.cpp
+++ b/src/core/hle/service/psc/time/time_zone_service.cpp
@@ -37,7 +37,9 @@ TimeZoneService::TimeZoneService(Core::System& system_, StandardSteadyClockCore&
37} 37}
38 38
39Result TimeZoneService::GetDeviceLocationName(Out<LocationName> out_location_name) { 39Result TimeZoneService::GetDeviceLocationName(Out<LocationName> out_location_name) {
40 SCOPE_EXIT({ LOG_DEBUG(Service_Time, "called. out_location_name={}", *out_location_name); }); 40 SCOPE_EXIT {
41 LOG_DEBUG(Service_Time, "called. out_location_name={}", *out_location_name);
42 };
41 43
42 R_RETURN(m_time_zone.GetLocationName(*out_location_name)); 44 R_RETURN(m_time_zone.GetLocationName(*out_location_name));
43} 45}
@@ -50,7 +52,9 @@ Result TimeZoneService::SetDeviceLocationName(const LocationName& location_name)
50} 52}
51 53
52Result TimeZoneService::GetTotalLocationNameCount(Out<u32> out_count) { 54Result TimeZoneService::GetTotalLocationNameCount(Out<u32> out_count) {
53 SCOPE_EXIT({ LOG_DEBUG(Service_Time, "called. out_count={}", *out_count); }); 55 SCOPE_EXIT {
56 LOG_DEBUG(Service_Time, "called. out_count={}", *out_count);
57 };
54 58
55 R_RETURN(m_time_zone.GetTotalLocationCount(*out_count)); 59 R_RETURN(m_time_zone.GetTotalLocationCount(*out_count));
56} 60}
@@ -69,17 +73,19 @@ Result TimeZoneService::LoadTimeZoneRule(OutRule out_rule, const LocationName& l
69} 73}
70 74
71Result TimeZoneService::GetTimeZoneRuleVersion(Out<RuleVersion> out_rule_version) { 75Result TimeZoneService::GetTimeZoneRuleVersion(Out<RuleVersion> out_rule_version) {
72 SCOPE_EXIT({ LOG_DEBUG(Service_Time, "called. out_rule_version={}", *out_rule_version); }); 76 SCOPE_EXIT {
77 LOG_DEBUG(Service_Time, "called. out_rule_version={}", *out_rule_version);
78 };
73 79
74 R_RETURN(m_time_zone.GetRuleVersion(*out_rule_version)); 80 R_RETURN(m_time_zone.GetRuleVersion(*out_rule_version));
75} 81}
76 82
77Result TimeZoneService::GetDeviceLocationNameAndUpdatedTime( 83Result TimeZoneService::GetDeviceLocationNameAndUpdatedTime(
78 Out<LocationName> out_location_name, Out<SteadyClockTimePoint> out_time_point) { 84 Out<LocationName> out_location_name, Out<SteadyClockTimePoint> out_time_point) {
79 SCOPE_EXIT({ 85 SCOPE_EXIT {
80 LOG_DEBUG(Service_Time, "called. out_location_name={} out_time_point={}", 86 LOG_DEBUG(Service_Time, "called. out_location_name={} out_time_point={}",
81 *out_location_name, *out_time_point); 87 *out_location_name, *out_time_point);
82 }); 88 };
83 89
84 R_TRY(m_time_zone.GetLocationName(*out_location_name)); 90 R_TRY(m_time_zone.GetLocationName(*out_location_name));
85 R_RETURN(m_time_zone.GetTimePoint(*out_time_point)); 91 R_RETURN(m_time_zone.GetTimePoint(*out_time_point));
@@ -116,10 +122,10 @@ Result TimeZoneService::GetDeviceLocationNameOperationEventReadableHandle(
116Result TimeZoneService::ToCalendarTime(Out<CalendarTime> out_calendar_time, 122Result TimeZoneService::ToCalendarTime(Out<CalendarTime> out_calendar_time,
117 Out<CalendarAdditionalInfo> out_additional_info, s64 time, 123 Out<CalendarAdditionalInfo> out_additional_info, s64 time,
118 InRule rule) { 124 InRule rule) {
119 SCOPE_EXIT({ 125 SCOPE_EXIT {
120 LOG_DEBUG(Service_Time, "called. time={} out_calendar_time={} out_additional_info={}", time, 126 LOG_DEBUG(Service_Time, "called. time={} out_calendar_time={} out_additional_info={}", time,
121 *out_calendar_time, *out_additional_info); 127 *out_calendar_time, *out_additional_info);
122 }); 128 };
123 129
124 R_RETURN( 130 R_RETURN(
125 m_time_zone.ToCalendarTime(*out_calendar_time, *out_additional_info, time, *rule.Get())); 131 m_time_zone.ToCalendarTime(*out_calendar_time, *out_additional_info, time, *rule.Get()));
@@ -128,10 +134,10 @@ Result TimeZoneService::ToCalendarTime(Out<CalendarTime> out_calendar_time,
128Result TimeZoneService::ToCalendarTimeWithMyRule(Out<CalendarTime> out_calendar_time, 134Result TimeZoneService::ToCalendarTimeWithMyRule(Out<CalendarTime> out_calendar_time,
129 Out<CalendarAdditionalInfo> out_additional_info, 135 Out<CalendarAdditionalInfo> out_additional_info,
130 s64 time) { 136 s64 time) {
131 SCOPE_EXIT({ 137 SCOPE_EXIT {
132 LOG_DEBUG(Service_Time, "called. time={} out_calendar_time={} out_additional_info={}", time, 138 LOG_DEBUG(Service_Time, "called. time={} out_calendar_time={} out_additional_info={}", time,
133 *out_calendar_time, *out_additional_info); 139 *out_calendar_time, *out_additional_info);
134 }); 140 };
135 141
136 R_RETURN(m_time_zone.ToCalendarTimeWithMyRule(*out_calendar_time, *out_additional_info, time)); 142 R_RETURN(m_time_zone.ToCalendarTimeWithMyRule(*out_calendar_time, *out_additional_info, time));
137} 143}
@@ -139,11 +145,11 @@ Result TimeZoneService::ToCalendarTimeWithMyRule(Out<CalendarTime> out_calendar_
139Result TimeZoneService::ToPosixTime(Out<u32> out_count, 145Result TimeZoneService::ToPosixTime(Out<u32> out_count,
140 OutArray<s64, BufferAttr_HipcPointer> out_times, 146 OutArray<s64, BufferAttr_HipcPointer> out_times,
141 const CalendarTime& calendar_time, InRule rule) { 147 const CalendarTime& calendar_time, InRule rule) {
142 SCOPE_EXIT({ 148 SCOPE_EXIT {
143 LOG_DEBUG(Service_Time, 149 LOG_DEBUG(Service_Time,
144 "called. calendar_time={} out_count={} out_times[0]={} out_times[1]={} ", 150 "called. calendar_time={} out_count={} out_times[0]={} out_times[1]={} ",
145 calendar_time, *out_count, out_times[0], out_times[1]); 151 calendar_time, *out_count, out_times[0], out_times[1]);
146 }); 152 };
147 153
148 R_RETURN( 154 R_RETURN(
149 m_time_zone.ToPosixTime(*out_count, out_times, out_times.size(), calendar_time, *rule)); 155 m_time_zone.ToPosixTime(*out_count, out_times, out_times.size(), calendar_time, *rule));
@@ -152,11 +158,11 @@ Result TimeZoneService::ToPosixTime(Out<u32> out_count,
152Result TimeZoneService::ToPosixTimeWithMyRule(Out<u32> out_count, 158Result TimeZoneService::ToPosixTimeWithMyRule(Out<u32> out_count,
153 OutArray<s64, BufferAttr_HipcPointer> out_times, 159 OutArray<s64, BufferAttr_HipcPointer> out_times,
154 const CalendarTime& calendar_time) { 160 const CalendarTime& calendar_time) {
155 SCOPE_EXIT({ 161 SCOPE_EXIT {
156 LOG_DEBUG(Service_Time, 162 LOG_DEBUG(Service_Time,
157 "called. calendar_time={} out_count={} out_times[0]={} out_times[1]={} ", 163 "called. calendar_time={} out_count={} out_times[0]={} out_times[1]={} ",
158 calendar_time, *out_count, out_times[0], out_times[1]); 164 calendar_time, *out_count, out_times[0], out_times[1]);
159 }); 165 };
160 166
161 R_RETURN( 167 R_RETURN(
162 m_time_zone.ToPosixTimeWithMyRule(*out_count, out_times, out_times.size(), calendar_time)); 168 m_time_zone.ToPosixTimeWithMyRule(*out_count, out_times, out_times.size(), calendar_time));
diff --git a/src/core/hle/service/server_manager.cpp b/src/core/hle/service/server_manager.cpp
index 8c7f94c8c..0b41bbcb9 100644
--- a/src/core/hle/service/server_manager.cpp
+++ b/src/core/hle/service/server_manager.cpp
@@ -177,10 +177,10 @@ Result ServerManager::ManageNamedPort(const std::string& service_name,
177 Kernel::KPort::Register(m_system.Kernel(), port); 177 Kernel::KPort::Register(m_system.Kernel(), port);
178 178
179 // Ensure that our reference to the port is closed if we fail to register it. 179 // Ensure that our reference to the port is closed if we fail to register it.
180 SCOPE_EXIT({ 180 SCOPE_EXIT {
181 port->GetClientPort().Close(); 181 port->GetClientPort().Close();
182 port->GetServerPort().Close(); 182 port->GetServerPort().Close();
183 }); 183 };
184 184
185 // Register the object name with the kernel. 185 // Register the object name with the kernel.
186 R_TRY(Kernel::KObjectName::NewFromName(m_system.Kernel(), std::addressof(port->GetClientPort()), 186 R_TRY(Kernel::KObjectName::NewFromName(m_system.Kernel(), std::addressof(port->GetClientPort()),
@@ -237,7 +237,9 @@ void ServerManager::StartAdditionalHostThreads(const char* name, size_t num_thre
237} 237}
238 238
239Result ServerManager::LoopProcess() { 239Result ServerManager::LoopProcess() {
240 SCOPE_EXIT({ m_stopped.Set(); }); 240 SCOPE_EXIT {
241 m_stopped.Set();
242 };
241 243
242 R_RETURN(this->LoopProcessImpl()); 244 R_RETURN(this->LoopProcessImpl());
243} 245}
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp
index fbdf217ba..ce5e3b5b4 100644
--- a/src/core/hle/service/service.cpp
+++ b/src/core/hle/service/service.cpp
@@ -7,68 +7,10 @@
7#include "common/settings.h" 7#include "common/settings.h"
8#include "core/core.h" 8#include "core/core.h"
9#include "core/hle/ipc.h" 9#include "core/hle/ipc.h"
10#include "core/hle/kernel/k_process.h"
11#include "core/hle/kernel/k_server_port.h"
12#include "core/hle/kernel/kernel.h" 10#include "core/hle/kernel/kernel.h"
13#include "core/hle/service/acc/acc.h"
14#include "core/hle/service/am/am.h"
15#include "core/hle/service/aoc/aoc_u.h"
16#include "core/hle/service/apm/apm.h"
17#include "core/hle/service/audio/audio.h"
18#include "core/hle/service/bcat/bcat.h"
19#include "core/hle/service/bpc/bpc.h"
20#include "core/hle/service/btdrv/btdrv.h"
21#include "core/hle/service/btm/btm.h"
22#include "core/hle/service/caps/caps.h"
23#include "core/hle/service/erpt/erpt.h"
24#include "core/hle/service/es/es.h"
25#include "core/hle/service/eupld/eupld.h"
26#include "core/hle/service/fatal/fatal.h"
27#include "core/hle/service/fgm/fgm.h"
28#include "core/hle/service/filesystem/filesystem.h"
29#include "core/hle/service/friend/friend.h"
30#include "core/hle/service/glue/glue.h"
31#include "core/hle/service/grc/grc.h"
32#include "core/hle/service/hid/hid.h"
33#include "core/hle/service/ipc_helpers.h" 11#include "core/hle/service/ipc_helpers.h"
34#include "core/hle/service/jit/jit.h"
35#include "core/hle/service/lbl/lbl.h"
36#include "core/hle/service/ldn/ldn.h"
37#include "core/hle/service/ldr/ldr.h"
38#include "core/hle/service/lm/lm.h"
39#include "core/hle/service/mig/mig.h"
40#include "core/hle/service/mii/mii.h"
41#include "core/hle/service/mm/mm_u.h"
42#include "core/hle/service/mnpp/mnpp_app.h"
43#include "core/hle/service/ncm/ncm.h"
44#include "core/hle/service/nfc/nfc.h"
45#include "core/hle/service/nfp/nfp.h"
46#include "core/hle/service/ngc/ngc.h"
47#include "core/hle/service/nifm/nifm.h"
48#include "core/hle/service/nim/nim.h"
49#include "core/hle/service/npns/npns.h"
50#include "core/hle/service/ns/ns.h"
51#include "core/hle/service/nvdrv/nvdrv.h"
52#include "core/hle/service/nvnflinger/hos_binder_driver_server.h"
53#include "core/hle/service/nvnflinger/nvnflinger.h"
54#include "core/hle/service/olsc/olsc.h"
55#include "core/hle/service/omm/omm.h"
56#include "core/hle/service/pcie/pcie.h"
57#include "core/hle/service/pctl/pctl_module.h"
58#include "core/hle/service/pcv/pcv.h"
59#include "core/hle/service/pm/pm.h"
60#include "core/hle/service/prepo/prepo.h"
61#include "core/hle/service/psc/psc.h"
62#include "core/hle/service/ptm/ptm.h"
63#include "core/hle/service/ro/ro.h"
64#include "core/hle/service/service.h" 12#include "core/hle/service/service.h"
65#include "core/hle/service/set/settings.h"
66#include "core/hle/service/sm/sm.h" 13#include "core/hle/service/sm/sm.h"
67#include "core/hle/service/sockets/sockets.h"
68#include "core/hle/service/spl/spl_module.h"
69#include "core/hle/service/ssl/ssl.h"
70#include "core/hle/service/usb/usb.h"
71#include "core/hle/service/vi/vi.h"
72#include "core/reporter.h" 14#include "core/reporter.h"
73 15
74namespace Service { 16namespace Service {
@@ -209,82 +151,4 @@ Result ServiceFrameworkBase::HandleSyncRequest(Kernel::KServerSession& session,
209 return result; 151 return result;
210} 152}
211 153
212/// Initialize Services
213Services::Services(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system)
214 : hos_binder_driver_server{std::make_unique<Nvnflinger::HosBinderDriverServer>(system)},
215 nv_flinger{std::make_unique<Nvnflinger::Nvnflinger>(system, *hos_binder_driver_server)} {
216
217 auto& kernel = system.Kernel();
218
219 // Nvnflinger needs to be accessed by several services like Vi and AppletOE so we instantiate it
220 // here and pass it into the respective InstallInterfaces functions.
221 system.GetFileSystemController().CreateFactories(*system.GetFilesystem(), false);
222
223 // clang-format off
224 kernel.RunOnHostCoreProcess("audio", [&] { Audio::LoopProcess(system); }).detach();
225 kernel.RunOnHostCoreProcess("FS", [&] { FileSystem::LoopProcess(system); }).detach();
226 kernel.RunOnHostCoreProcess("jit", [&] { JIT::LoopProcess(system); }).detach();
227 kernel.RunOnHostCoreProcess("ldn", [&] { LDN::LoopProcess(system); }).detach();
228 kernel.RunOnHostCoreProcess("Loader", [&] { LDR::LoopProcess(system); }).detach();
229 kernel.RunOnHostCoreProcess("nvservices", [&] { Nvidia::LoopProcess(*nv_flinger, system); }).detach();
230 kernel.RunOnHostCoreProcess("bsdsocket", [&] { Sockets::LoopProcess(system); }).detach();
231 kernel.RunOnHostCoreProcess("vi", [&] { VI::LoopProcess(system, *nv_flinger, *hos_binder_driver_server); }).detach();
232
233 kernel.RunOnGuestCoreProcess("sm", [&] { SM::LoopProcess(system); });
234 kernel.RunOnGuestCoreProcess("account", [&] { Account::LoopProcess(system); });
235 kernel.RunOnGuestCoreProcess("am", [&] { AM::LoopProcess(*nv_flinger, system); });
236 kernel.RunOnGuestCoreProcess("aoc", [&] { AOC::LoopProcess(system); });
237 kernel.RunOnGuestCoreProcess("apm", [&] { APM::LoopProcess(system); });
238 kernel.RunOnGuestCoreProcess("bcat", [&] { BCAT::LoopProcess(system); });
239 kernel.RunOnGuestCoreProcess("bpc", [&] { BPC::LoopProcess(system); });
240 kernel.RunOnGuestCoreProcess("btdrv", [&] { BtDrv::LoopProcess(system); });
241 kernel.RunOnGuestCoreProcess("btm", [&] { BTM::LoopProcess(system); });
242 kernel.RunOnGuestCoreProcess("capsrv", [&] { Capture::LoopProcess(system); });
243 kernel.RunOnGuestCoreProcess("erpt", [&] { ERPT::LoopProcess(system); });
244 kernel.RunOnGuestCoreProcess("es", [&] { ES::LoopProcess(system); });
245 kernel.RunOnGuestCoreProcess("eupld", [&] { EUPLD::LoopProcess(system); });
246 kernel.RunOnGuestCoreProcess("fatal", [&] { Fatal::LoopProcess(system); });
247 kernel.RunOnGuestCoreProcess("fgm", [&] { FGM::LoopProcess(system); });
248 kernel.RunOnGuestCoreProcess("friends", [&] { Friend::LoopProcess(system); });
249 // glue depends on settings and psc, so they must come first
250 kernel.RunOnGuestCoreProcess("settings", [&] { Set::LoopProcess(system); });
251 kernel.RunOnGuestCoreProcess("psc", [&] { PSC::LoopProcess(system); });
252 kernel.RunOnGuestCoreProcess("glue", [&] { Glue::LoopProcess(system); });
253 kernel.RunOnGuestCoreProcess("grc", [&] { GRC::LoopProcess(system); });
254 kernel.RunOnGuestCoreProcess("hid", [&] { HID::LoopProcess(system); });
255 kernel.RunOnGuestCoreProcess("lbl", [&] { LBL::LoopProcess(system); });
256 kernel.RunOnGuestCoreProcess("LogManager.Prod", [&] { LM::LoopProcess(system); });
257 kernel.RunOnGuestCoreProcess("mig", [&] { Migration::LoopProcess(system); });
258 kernel.RunOnGuestCoreProcess("mii", [&] { Mii::LoopProcess(system); });
259 kernel.RunOnGuestCoreProcess("mm", [&] { MM::LoopProcess(system); });
260 kernel.RunOnGuestCoreProcess("mnpp", [&] { MNPP::LoopProcess(system); });
261 kernel.RunOnGuestCoreProcess("NCM", [&] { NCM::LoopProcess(system); });
262 kernel.RunOnGuestCoreProcess("nfc", [&] { NFC::LoopProcess(system); });
263 kernel.RunOnGuestCoreProcess("nfp", [&] { NFP::LoopProcess(system); });
264 kernel.RunOnGuestCoreProcess("ngc", [&] { NGC::LoopProcess(system); });
265 kernel.RunOnGuestCoreProcess("nifm", [&] { NIFM::LoopProcess(system); });
266 kernel.RunOnGuestCoreProcess("nim", [&] { NIM::LoopProcess(system); });
267 kernel.RunOnGuestCoreProcess("npns", [&] { NPNS::LoopProcess(system); });
268 kernel.RunOnGuestCoreProcess("ns", [&] { NS::LoopProcess(system); });
269 kernel.RunOnGuestCoreProcess("olsc", [&] { OLSC::LoopProcess(system); });
270 kernel.RunOnGuestCoreProcess("omm", [&] { OMM::LoopProcess(system); });
271 kernel.RunOnGuestCoreProcess("pcie", [&] { PCIe::LoopProcess(system); });
272 kernel.RunOnGuestCoreProcess("pctl", [&] { PCTL::LoopProcess(system); });
273 kernel.RunOnGuestCoreProcess("pcv", [&] { PCV::LoopProcess(system); });
274 kernel.RunOnGuestCoreProcess("prepo", [&] { PlayReport::LoopProcess(system); });
275 kernel.RunOnGuestCoreProcess("ProcessManager", [&] { PM::LoopProcess(system); });
276 kernel.RunOnGuestCoreProcess("ptm", [&] { PTM::LoopProcess(system); });
277 kernel.RunOnGuestCoreProcess("ro", [&] { RO::LoopProcess(system); });
278 kernel.RunOnGuestCoreProcess("spl", [&] { SPL::LoopProcess(system); });
279 kernel.RunOnGuestCoreProcess("ssl", [&] { SSL::LoopProcess(system); });
280 kernel.RunOnGuestCoreProcess("usb", [&] { USB::LoopProcess(system); });
281 // clang-format on
282}
283
284Services::~Services() = default;
285
286void Services::KillNVNFlinger() {
287 nv_flinger->ShutdownLayers();
288}
289
290} // namespace Service 154} // namespace Service
diff --git a/src/core/hle/service/service.h b/src/core/hle/service/service.h
index 22d1343d5..36aae1c79 100644
--- a/src/core/hle/service/service.h
+++ b/src/core/hle/service/service.h
@@ -28,11 +28,6 @@ namespace FileSystem {
28class FileSystemController; 28class FileSystemController;
29} 29}
30 30
31namespace Nvnflinger {
32class HosBinderDriverServer;
33class Nvnflinger;
34} // namespace Nvnflinger
35
36namespace SM { 31namespace SM {
37class ServiceManager; 32class ServiceManager;
38} 33}
@@ -236,20 +231,4 @@ private:
236 } 231 }
237}; 232};
238 233
239/**
240 * The purpose of this class is to own any objects that need to be shared across the other service
241 * implementations. Will be torn down when the global system instance is shutdown.
242 */
243class Services final {
244public:
245 explicit Services(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system);
246 ~Services();
247
248 void KillNVNFlinger();
249
250private:
251 std::unique_ptr<Nvnflinger::HosBinderDriverServer> hos_binder_driver_server;
252 std::unique_ptr<Nvnflinger::Nvnflinger> nv_flinger;
253};
254
255} // namespace Service 234} // namespace Service
diff --git a/src/core/hle/service/services.cpp b/src/core/hle/service/services.cpp
new file mode 100644
index 000000000..d6c6eff50
--- /dev/null
+++ b/src/core/hle/service/services.cpp
@@ -0,0 +1,136 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/hle/service/services.h"
5
6#include "core/hle/service/acc/acc.h"
7#include "core/hle/service/am/am.h"
8#include "core/hle/service/aoc/aoc_u.h"
9#include "core/hle/service/apm/apm.h"
10#include "core/hle/service/audio/audio.h"
11#include "core/hle/service/bcat/bcat.h"
12#include "core/hle/service/bpc/bpc.h"
13#include "core/hle/service/btdrv/btdrv.h"
14#include "core/hle/service/btm/btm.h"
15#include "core/hle/service/caps/caps.h"
16#include "core/hle/service/erpt/erpt.h"
17#include "core/hle/service/es/es.h"
18#include "core/hle/service/eupld/eupld.h"
19#include "core/hle/service/fatal/fatal.h"
20#include "core/hle/service/fgm/fgm.h"
21#include "core/hle/service/filesystem/filesystem.h"
22#include "core/hle/service/friend/friend.h"
23#include "core/hle/service/glue/glue.h"
24#include "core/hle/service/grc/grc.h"
25#include "core/hle/service/hid/hid.h"
26#include "core/hle/service/ipc_helpers.h"
27#include "core/hle/service/jit/jit.h"
28#include "core/hle/service/lbl/lbl.h"
29#include "core/hle/service/ldn/ldn.h"
30#include "core/hle/service/ldr/ldr.h"
31#include "core/hle/service/lm/lm.h"
32#include "core/hle/service/mig/mig.h"
33#include "core/hle/service/mii/mii.h"
34#include "core/hle/service/mm/mm_u.h"
35#include "core/hle/service/mnpp/mnpp_app.h"
36#include "core/hle/service/ncm/ncm.h"
37#include "core/hle/service/nfc/nfc.h"
38#include "core/hle/service/nfp/nfp.h"
39#include "core/hle/service/ngc/ngc.h"
40#include "core/hle/service/nifm/nifm.h"
41#include "core/hle/service/nim/nim.h"
42#include "core/hle/service/npns/npns.h"
43#include "core/hle/service/ns/ns.h"
44#include "core/hle/service/nvdrv/nvdrv.h"
45#include "core/hle/service/nvnflinger/nvnflinger.h"
46#include "core/hle/service/olsc/olsc.h"
47#include "core/hle/service/omm/omm.h"
48#include "core/hle/service/pcie/pcie.h"
49#include "core/hle/service/pctl/pctl_module.h"
50#include "core/hle/service/pcv/pcv.h"
51#include "core/hle/service/pm/pm.h"
52#include "core/hle/service/prepo/prepo.h"
53#include "core/hle/service/psc/psc.h"
54#include "core/hle/service/ptm/ptm.h"
55#include "core/hle/service/ro/ro.h"
56#include "core/hle/service/service.h"
57#include "core/hle/service/set/settings.h"
58#include "core/hle/service/sm/sm.h"
59#include "core/hle/service/sockets/sockets.h"
60#include "core/hle/service/spl/spl_module.h"
61#include "core/hle/service/ssl/ssl.h"
62#include "core/hle/service/usb/usb.h"
63#include "core/hle/service/vi/vi.h"
64
65namespace Service {
66
67Services::Services(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system,
68 std::stop_token token) {
69 auto& kernel = system.Kernel();
70
71 system.GetFileSystemController().CreateFactories(*system.GetFilesystem(), false);
72
73 // clang-format off
74 kernel.RunOnHostCoreProcess("audio", [&] { Audio::LoopProcess(system); }).detach();
75 kernel.RunOnHostCoreProcess("FS", [&] { FileSystem::LoopProcess(system); }).detach();
76 kernel.RunOnHostCoreProcess("jit", [&] { JIT::LoopProcess(system); }).detach();
77 kernel.RunOnHostCoreProcess("ldn", [&] { LDN::LoopProcess(system); }).detach();
78 kernel.RunOnHostCoreProcess("Loader", [&] { LDR::LoopProcess(system); }).detach();
79 kernel.RunOnHostCoreProcess("nvservices", [&] { Nvidia::LoopProcess(system); }).detach();
80 kernel.RunOnHostCoreProcess("bsdsocket", [&] { Sockets::LoopProcess(system); }).detach();
81 kernel.RunOnHostCoreProcess("vi", [&, token] { VI::LoopProcess(system, token); }).detach();
82
83 kernel.RunOnGuestCoreProcess("sm", [&] { SM::LoopProcess(system); });
84 kernel.RunOnGuestCoreProcess("account", [&] { Account::LoopProcess(system); });
85 kernel.RunOnGuestCoreProcess("am", [&] { AM::LoopProcess(system); });
86 kernel.RunOnGuestCoreProcess("aoc", [&] { AOC::LoopProcess(system); });
87 kernel.RunOnGuestCoreProcess("apm", [&] { APM::LoopProcess(system); });
88 kernel.RunOnGuestCoreProcess("bcat", [&] { BCAT::LoopProcess(system); });
89 kernel.RunOnGuestCoreProcess("bpc", [&] { BPC::LoopProcess(system); });
90 kernel.RunOnGuestCoreProcess("btdrv", [&] { BtDrv::LoopProcess(system); });
91 kernel.RunOnGuestCoreProcess("btm", [&] { BTM::LoopProcess(system); });
92 kernel.RunOnGuestCoreProcess("capsrv", [&] { Capture::LoopProcess(system); });
93 kernel.RunOnGuestCoreProcess("erpt", [&] { ERPT::LoopProcess(system); });
94 kernel.RunOnGuestCoreProcess("es", [&] { ES::LoopProcess(system); });
95 kernel.RunOnGuestCoreProcess("eupld", [&] { EUPLD::LoopProcess(system); });
96 kernel.RunOnGuestCoreProcess("fatal", [&] { Fatal::LoopProcess(system); });
97 kernel.RunOnGuestCoreProcess("fgm", [&] { FGM::LoopProcess(system); });
98 kernel.RunOnGuestCoreProcess("friends", [&] { Friend::LoopProcess(system); });
99 kernel.RunOnGuestCoreProcess("settings", [&] { Set::LoopProcess(system); });
100 kernel.RunOnGuestCoreProcess("psc", [&] { PSC::LoopProcess(system); });
101 kernel.RunOnGuestCoreProcess("glue", [&] { Glue::LoopProcess(system); });
102 kernel.RunOnGuestCoreProcess("grc", [&] { GRC::LoopProcess(system); });
103 kernel.RunOnGuestCoreProcess("hid", [&] { HID::LoopProcess(system); });
104 kernel.RunOnGuestCoreProcess("lbl", [&] { LBL::LoopProcess(system); });
105 kernel.RunOnGuestCoreProcess("LogManager.Prod", [&] { LM::LoopProcess(system); });
106 kernel.RunOnGuestCoreProcess("mig", [&] { Migration::LoopProcess(system); });
107 kernel.RunOnGuestCoreProcess("mii", [&] { Mii::LoopProcess(system); });
108 kernel.RunOnGuestCoreProcess("mm", [&] { MM::LoopProcess(system); });
109 kernel.RunOnGuestCoreProcess("mnpp", [&] { MNPP::LoopProcess(system); });
110 kernel.RunOnGuestCoreProcess("nvnflinger", [&] { Nvnflinger::LoopProcess(system); });
111 kernel.RunOnGuestCoreProcess("NCM", [&] { NCM::LoopProcess(system); });
112 kernel.RunOnGuestCoreProcess("nfc", [&] { NFC::LoopProcess(system); });
113 kernel.RunOnGuestCoreProcess("nfp", [&] { NFP::LoopProcess(system); });
114 kernel.RunOnGuestCoreProcess("ngc", [&] { NGC::LoopProcess(system); });
115 kernel.RunOnGuestCoreProcess("nifm", [&] { NIFM::LoopProcess(system); });
116 kernel.RunOnGuestCoreProcess("nim", [&] { NIM::LoopProcess(system); });
117 kernel.RunOnGuestCoreProcess("npns", [&] { NPNS::LoopProcess(system); });
118 kernel.RunOnGuestCoreProcess("ns", [&] { NS::LoopProcess(system); });
119 kernel.RunOnGuestCoreProcess("olsc", [&] { OLSC::LoopProcess(system); });
120 kernel.RunOnGuestCoreProcess("omm", [&] { OMM::LoopProcess(system); });
121 kernel.RunOnGuestCoreProcess("pcie", [&] { PCIe::LoopProcess(system); });
122 kernel.RunOnGuestCoreProcess("pctl", [&] { PCTL::LoopProcess(system); });
123 kernel.RunOnGuestCoreProcess("pcv", [&] { PCV::LoopProcess(system); });
124 kernel.RunOnGuestCoreProcess("prepo", [&] { PlayReport::LoopProcess(system); });
125 kernel.RunOnGuestCoreProcess("ProcessManager", [&] { PM::LoopProcess(system); });
126 kernel.RunOnGuestCoreProcess("ptm", [&] { PTM::LoopProcess(system); });
127 kernel.RunOnGuestCoreProcess("ro", [&] { RO::LoopProcess(system); });
128 kernel.RunOnGuestCoreProcess("spl", [&] { SPL::LoopProcess(system); });
129 kernel.RunOnGuestCoreProcess("ssl", [&] { SSL::LoopProcess(system); });
130 kernel.RunOnGuestCoreProcess("usb", [&] { USB::LoopProcess(system); });
131 // clang-format on
132}
133
134Services::~Services() = default;
135
136} // namespace Service
diff --git a/src/core/hle/service/services.h b/src/core/hle/service/services.h
new file mode 100644
index 000000000..a99fa1e53
--- /dev/null
+++ b/src/core/hle/service/services.h
@@ -0,0 +1,22 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "common/polyfill_thread.h"
7#include "core/hle/service/sm/sm.h"
8
9namespace Service {
10
11/**
12 * The purpose of this class is to own any objects that need to be shared across the other service
13 * implementations. Will be torn down when the global system instance is shutdown.
14 */
15class Services final {
16public:
17 explicit Services(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system,
18 std::stop_token token);
19 ~Services();
20};
21
22} // namespace Service
diff --git a/src/core/hle/service/vi/application_display_service.cpp b/src/core/hle/service/vi/application_display_service.cpp
index 78229e30f..6b0bcb536 100644
--- a/src/core/hle/service/vi/application_display_service.cpp
+++ b/src/core/hle/service/vi/application_display_service.cpp
@@ -2,22 +2,21 @@
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include "core/hle/service/cmif_serialization.h" 4#include "core/hle/service/cmif_serialization.h"
5#include "core/hle/service/nvnflinger/nvnflinger.h" 5#include "core/hle/service/nvnflinger/hos_binder_driver.h"
6#include "core/hle/service/nvnflinger/parcel.h" 6#include "core/hle/service/nvnflinger/parcel.h"
7#include "core/hle/service/os/event.h"
7#include "core/hle/service/vi/application_display_service.h" 8#include "core/hle/service/vi/application_display_service.h"
8#include "core/hle/service/vi/hos_binder_driver.h" 9#include "core/hle/service/vi/container.h"
9#include "core/hle/service/vi/manager_display_service.h" 10#include "core/hle/service/vi/manager_display_service.h"
10#include "core/hle/service/vi/system_display_service.h" 11#include "core/hle/service/vi/system_display_service.h"
11#include "core/hle/service/vi/vi_results.h" 12#include "core/hle/service/vi/vi_results.h"
12 13
13namespace Service::VI { 14namespace Service::VI {
14 15
15IApplicationDisplayService::IApplicationDisplayService( 16IApplicationDisplayService::IApplicationDisplayService(Core::System& system_,
16 Core::System& system_, Nvnflinger::Nvnflinger& nvnflinger, 17 std::shared_ptr<Container> container)
17 Nvnflinger::HosBinderDriverServer& hos_binder_driver_server) 18 : ServiceFramework{system_, "IApplicationDisplayService"},
18 : ServiceFramework{system_, "IApplicationDisplayService"}, m_nvnflinger{nvnflinger}, 19 m_container{std::move(container)}, m_context{system, "IApplicationDisplayService"} {
19 m_hos_binder_driver_server{hos_binder_driver_server} {
20
21 // clang-format off 20 // clang-format off
22 static const FunctionInfo functions[] = { 21 static const FunctionInfo functions[] = {
23 {100, C<&IApplicationDisplayService::GetRelayService>, "GetRelayService"}, 22 {100, C<&IApplicationDisplayService::GetRelayService>, "GetRelayService"},
@@ -48,38 +47,41 @@ IApplicationDisplayService::IApplicationDisplayService(
48} 47}
49 48
50IApplicationDisplayService::~IApplicationDisplayService() { 49IApplicationDisplayService::~IApplicationDisplayService() {
50 for (auto& [display_id, event] : m_display_vsync_events) {
51 m_container->UnlinkVsyncEvent(display_id, &event);
52 }
53 for (const auto layer_id : m_open_layer_ids) {
54 m_container->CloseLayer(layer_id);
55 }
51 for (const auto layer_id : m_stray_layer_ids) { 56 for (const auto layer_id : m_stray_layer_ids) {
52 m_nvnflinger.DestroyLayer(layer_id); 57 m_container->DestroyStrayLayer(layer_id);
53 } 58 }
54} 59}
55 60
56Result IApplicationDisplayService::GetRelayService( 61Result IApplicationDisplayService::GetRelayService(
57 Out<SharedPointer<IHOSBinderDriver>> out_relay_service) { 62 Out<SharedPointer<Nvnflinger::IHOSBinderDriver>> out_relay_service) {
58 LOG_WARNING(Service_VI, "(STUBBED) called"); 63 LOG_WARNING(Service_VI, "(STUBBED) called");
59 *out_relay_service = std::make_shared<IHOSBinderDriver>(system, m_hos_binder_driver_server); 64 R_RETURN(m_container->GetBinderDriver(out_relay_service));
60 R_SUCCEED();
61} 65}
62 66
63Result IApplicationDisplayService::GetSystemDisplayService( 67Result IApplicationDisplayService::GetSystemDisplayService(
64 Out<SharedPointer<ISystemDisplayService>> out_system_display_service) { 68 Out<SharedPointer<ISystemDisplayService>> out_system_display_service) {
65 LOG_WARNING(Service_VI, "(STUBBED) called"); 69 LOG_WARNING(Service_VI, "(STUBBED) called");
66 *out_system_display_service = std::make_shared<ISystemDisplayService>(system, m_nvnflinger); 70 *out_system_display_service = std::make_shared<ISystemDisplayService>(system, m_container);
67 R_SUCCEED(); 71 R_SUCCEED();
68} 72}
69 73
70Result IApplicationDisplayService::GetManagerDisplayService( 74Result IApplicationDisplayService::GetManagerDisplayService(
71 Out<SharedPointer<IManagerDisplayService>> out_manager_display_service) { 75 Out<SharedPointer<IManagerDisplayService>> out_manager_display_service) {
72 LOG_WARNING(Service_VI, "(STUBBED) called"); 76 LOG_WARNING(Service_VI, "(STUBBED) called");
73 *out_manager_display_service = std::make_shared<IManagerDisplayService>(system, m_nvnflinger); 77 *out_manager_display_service = std::make_shared<IManagerDisplayService>(system, m_container);
74 R_SUCCEED(); 78 R_SUCCEED();
75} 79}
76 80
77Result IApplicationDisplayService::GetIndirectDisplayTransactionService( 81Result IApplicationDisplayService::GetIndirectDisplayTransactionService(
78 Out<SharedPointer<IHOSBinderDriver>> out_indirect_display_transaction_service) { 82 Out<SharedPointer<Nvnflinger::IHOSBinderDriver>> out_indirect_display_transaction_service) {
79 LOG_WARNING(Service_VI, "(STUBBED) called"); 83 LOG_WARNING(Service_VI, "(STUBBED) called");
80 *out_indirect_display_transaction_service = 84 R_RETURN(m_container->GetBinderDriver(out_indirect_display_transaction_service));
81 std::make_shared<IHOSBinderDriver>(system, m_hos_binder_driver_server);
82 R_SUCCEED();
83} 85}
84 86
85Result IApplicationDisplayService::OpenDisplay(Out<u64> out_display_id, DisplayName display_name) { 87Result IApplicationDisplayService::OpenDisplay(Out<u64> out_display_id, DisplayName display_name) {
@@ -89,14 +91,7 @@ Result IApplicationDisplayService::OpenDisplay(Out<u64> out_display_id, DisplayN
89 ASSERT_MSG(strcmp(display_name.data(), "Default") == 0, 91 ASSERT_MSG(strcmp(display_name.data(), "Default") == 0,
90 "Non-default displays aren't supported yet"); 92 "Non-default displays aren't supported yet");
91 93
92 const auto display_id = m_nvnflinger.OpenDisplay(display_name.data()); 94 R_RETURN(m_container->OpenDisplay(out_display_id, display_name));
93 if (!display_id) {
94 LOG_ERROR(Service_VI, "Display not found! display_name={}", display_name.data());
95 R_THROW(VI::ResultNotFound);
96 }
97
98 *out_display_id = *display_id;
99 R_SUCCEED();
100} 95}
101 96
102Result IApplicationDisplayService::OpenDefaultDisplay(Out<u64> out_display_id) { 97Result IApplicationDisplayService::OpenDefaultDisplay(Out<u64> out_display_id) {
@@ -106,8 +101,7 @@ Result IApplicationDisplayService::OpenDefaultDisplay(Out<u64> out_display_id) {
106 101
107Result IApplicationDisplayService::CloseDisplay(u64 display_id) { 102Result IApplicationDisplayService::CloseDisplay(u64 display_id) {
108 LOG_DEBUG(Service_VI, "called"); 103 LOG_DEBUG(Service_VI, "called");
109 R_SUCCEED_IF(m_nvnflinger.CloseDisplay(display_id)); 104 R_RETURN(m_container->CloseDisplay(display_id));
110 R_THROW(ResultUnknown);
111} 105}
112 106
113Result IApplicationDisplayService::SetDisplayEnabled(u32 state, u64 display_id) { 107Result IApplicationDisplayService::SetDisplayEnabled(u32 state, u64 display_id) {
@@ -168,25 +162,19 @@ Result IApplicationDisplayService::OpenLayer(Out<u64> out_size,
168 162
169 LOG_DEBUG(Service_VI, "called. layer_id={}, aruid={:#x}", layer_id, aruid.pid); 163 LOG_DEBUG(Service_VI, "called. layer_id={}, aruid={:#x}", layer_id, aruid.pid);
170 164
171 const auto display_id = m_nvnflinger.OpenDisplay(display_name.data()); 165 u64 display_id;
172 if (!display_id) { 166 R_TRY(m_container->OpenDisplay(&display_id, display_name));
173 LOG_ERROR(Service_VI, "Layer not found! layer_id={}", layer_id);
174 R_THROW(VI::ResultNotFound);
175 }
176 167
177 const auto buffer_queue_id = m_nvnflinger.FindBufferQueueId(*display_id, layer_id); 168 s32 producer_binder_id;
178 if (!buffer_queue_id) { 169 R_TRY(m_container->OpenLayer(&producer_binder_id, layer_id, aruid.pid));
179 LOG_ERROR(Service_VI, "Buffer queue id not found! display_id={}", *display_id);
180 R_THROW(VI::ResultNotFound);
181 }
182 170
183 if (!m_nvnflinger.OpenLayer(layer_id)) { 171 {
184 LOG_WARNING(Service_VI, "Tried to open layer which was already open"); 172 std::scoped_lock lk{m_lock};
185 R_THROW(VI::ResultOperationFailed); 173 m_open_layer_ids.insert(layer_id);
186 } 174 }
187 175
188 android::OutputParcel parcel; 176 android::OutputParcel parcel;
189 parcel.WriteInterface(NativeWindow{*buffer_queue_id}); 177 parcel.WriteInterface(NativeWindow{producer_binder_id});
190 178
191 const auto buffer = parcel.Serialize(); 179 const auto buffer = parcel.Serialize();
192 std::memcpy(out_native_window.data(), buffer.data(), 180 std::memcpy(out_native_window.data(), buffer.data(),
@@ -199,12 +187,13 @@ Result IApplicationDisplayService::OpenLayer(Out<u64> out_size,
199Result IApplicationDisplayService::CloseLayer(u64 layer_id) { 187Result IApplicationDisplayService::CloseLayer(u64 layer_id) {
200 LOG_DEBUG(Service_VI, "called. layer_id={}", layer_id); 188 LOG_DEBUG(Service_VI, "called. layer_id={}", layer_id);
201 189
202 if (!m_nvnflinger.CloseLayer(layer_id)) { 190 {
203 LOG_WARNING(Service_VI, "Tried to close layer which was not open"); 191 std::scoped_lock lk{m_lock};
204 R_THROW(VI::ResultOperationFailed); 192 R_UNLESS(m_open_layer_ids.contains(layer_id), VI::ResultNotFound);
193 m_open_layer_ids.erase(layer_id);
205 } 194 }
206 195
207 R_SUCCEED(); 196 R_RETURN(m_container->CloseLayer(layer_id));
208} 197}
209 198
210Result IApplicationDisplayService::CreateStrayLayer( 199Result IApplicationDisplayService::CreateStrayLayer(
@@ -212,27 +201,19 @@ Result IApplicationDisplayService::CreateStrayLayer(
212 u32 flags, u64 display_id) { 201 u32 flags, u64 display_id) {
213 LOG_DEBUG(Service_VI, "called. flags={}, display_id={}", flags, display_id); 202 LOG_DEBUG(Service_VI, "called. flags={}, display_id={}", flags, display_id);
214 203
215 const auto layer_id = m_nvnflinger.CreateLayer(display_id); 204 s32 producer_binder_id;
216 if (!layer_id) { 205 R_TRY(m_container->CreateStrayLayer(&producer_binder_id, out_layer_id, display_id));
217 LOG_ERROR(Service_VI, "Layer not found! display_id={}", display_id);
218 R_THROW(VI::ResultNotFound);
219 }
220 206
221 m_stray_layer_ids.push_back(*layer_id); 207 std::scoped_lock lk{m_lock};
222 const auto buffer_queue_id = m_nvnflinger.FindBufferQueueId(display_id, *layer_id); 208 m_stray_layer_ids.insert(*out_layer_id);
223 if (!buffer_queue_id) {
224 LOG_ERROR(Service_VI, "Buffer queue id not found! display_id={}", display_id);
225 R_THROW(VI::ResultNotFound);
226 }
227 209
228 android::OutputParcel parcel; 210 android::OutputParcel parcel;
229 parcel.WriteInterface(NativeWindow{*buffer_queue_id}); 211 parcel.WriteInterface(NativeWindow{producer_binder_id});
230 212
231 const auto buffer = parcel.Serialize(); 213 const auto buffer = parcel.Serialize();
232 std::memcpy(out_native_window.data(), buffer.data(), 214 std::memcpy(out_native_window.data(), buffer.data(),
233 std::min(out_native_window.size(), buffer.size())); 215 std::min(out_native_window.size(), buffer.size()));
234 216
235 *out_layer_id = *layer_id;
236 *out_size = buffer.size(); 217 *out_size = buffer.size();
237 218
238 R_SUCCEED(); 219 R_SUCCEED();
@@ -240,25 +221,27 @@ Result IApplicationDisplayService::CreateStrayLayer(
240 221
241Result IApplicationDisplayService::DestroyStrayLayer(u64 layer_id) { 222Result IApplicationDisplayService::DestroyStrayLayer(u64 layer_id) {
242 LOG_WARNING(Service_VI, "(STUBBED) called. layer_id={}", layer_id); 223 LOG_WARNING(Service_VI, "(STUBBED) called. layer_id={}", layer_id);
243 m_nvnflinger.DestroyLayer(layer_id); 224
244 R_SUCCEED(); 225 {
226 std::scoped_lock lk{m_lock};
227 R_UNLESS(m_stray_layer_ids.contains(layer_id), VI::ResultNotFound);
228 m_stray_layer_ids.erase(layer_id);
229 }
230
231 R_RETURN(m_container->DestroyStrayLayer(layer_id));
245} 232}
246 233
247Result IApplicationDisplayService::GetDisplayVsyncEvent( 234Result IApplicationDisplayService::GetDisplayVsyncEvent(
248 OutCopyHandle<Kernel::KReadableEvent> out_vsync_event, u64 display_id) { 235 OutCopyHandle<Kernel::KReadableEvent> out_vsync_event, u64 display_id) {
249 LOG_DEBUG(Service_VI, "called. display_id={}", display_id); 236 LOG_DEBUG(Service_VI, "called. display_id={}", display_id);
250 237
251 const auto result = m_nvnflinger.FindVsyncEvent(out_vsync_event, display_id); 238 std::scoped_lock lk{m_lock};
252 if (result != ResultSuccess) {
253 if (result == ResultNotFound) {
254 LOG_ERROR(Service_VI, "Vsync event was not found for display_id={}", display_id);
255 }
256 239
257 R_THROW(result); 240 auto [it, created] = m_display_vsync_events.emplace(display_id, m_context);
258 } 241 R_UNLESS(created, VI::ResultPermissionDenied);
259 242
260 R_UNLESS(!m_vsync_event_fetched, VI::ResultPermissionDenied); 243 m_container->LinkVsyncEvent(display_id, &it->second);
261 m_vsync_event_fetched = true; 244 *out_vsync_event = it->second.GetHandle();
262 245
263 R_SUCCEED(); 246 R_SUCCEED();
264} 247}
diff --git a/src/core/hle/service/vi/application_display_service.h b/src/core/hle/service/vi/application_display_service.h
index 5dff4bb31..1bdeb8f84 100644
--- a/src/core/hle/service/vi/application_display_service.h
+++ b/src/core/hle/service/vi/application_display_service.h
@@ -1,7 +1,12 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include <map>
5#include <set>
6
4#include "core/hle/service/cmif_types.h" 7#include "core/hle/service/cmif_types.h"
8#include "core/hle/service/kernel_helpers.h"
9#include "core/hle/service/os/event.h"
5#include "core/hle/service/service.h" 10#include "core/hle/service/service.h"
6#include "core/hle/service/vi/vi_types.h" 11#include "core/hle/service/vi/vi_types.h"
7 12
@@ -9,26 +14,33 @@ namespace Kernel {
9class KReadableEvent; 14class KReadableEvent;
10} 15}
11 16
17namespace Service::Nvnflinger {
18class IHOSBinderDriver;
19}
20
12namespace Service::VI { 21namespace Service::VI {
13 22
14class IHOSBinderDriver; 23class Container;
15class IManagerDisplayService; 24class IManagerDisplayService;
16class ISystemDisplayService; 25class ISystemDisplayService;
17 26
18class IApplicationDisplayService final : public ServiceFramework<IApplicationDisplayService> { 27class IApplicationDisplayService final : public ServiceFramework<IApplicationDisplayService> {
19public: 28public:
20 IApplicationDisplayService(Core::System& system_, Nvnflinger::Nvnflinger& nvnflinger, 29 IApplicationDisplayService(Core::System& system_, std::shared_ptr<Container> container);
21 Nvnflinger::HosBinderDriverServer& hos_binder_driver_server);
22 ~IApplicationDisplayService() override; 30 ~IApplicationDisplayService() override;
23 31
24private: 32 std::shared_ptr<Container> GetContainer() const {
25 Result GetRelayService(Out<SharedPointer<IHOSBinderDriver>> out_relay_service); 33 return m_container;
34 }
35
36public:
37 Result GetRelayService(Out<SharedPointer<Nvnflinger::IHOSBinderDriver>> out_relay_service);
26 Result GetSystemDisplayService( 38 Result GetSystemDisplayService(
27 Out<SharedPointer<ISystemDisplayService>> out_system_display_service); 39 Out<SharedPointer<ISystemDisplayService>> out_system_display_service);
28 Result GetManagerDisplayService( 40 Result GetManagerDisplayService(
29 Out<SharedPointer<IManagerDisplayService>> out_manager_display_service); 41 Out<SharedPointer<IManagerDisplayService>> out_manager_display_service);
30 Result GetIndirectDisplayTransactionService( 42 Result GetIndirectDisplayTransactionService(
31 Out<SharedPointer<IHOSBinderDriver>> out_indirect_display_transaction_service); 43 Out<SharedPointer<Nvnflinger::IHOSBinderDriver>> out_indirect_display_transaction_service);
32 Result OpenDisplay(Out<u64> out_display_id, DisplayName display_name); 44 Result OpenDisplay(Out<u64> out_display_id, DisplayName display_name);
33 Result OpenDefaultDisplay(Out<u64> out_display_id); 45 Result OpenDefaultDisplay(Out<u64> out_display_id);
34 Result CloseDisplay(u64 display_id); 46 Result CloseDisplay(u64 display_id);
@@ -56,9 +68,13 @@ private:
56 s64 width, s64 height); 68 s64 width, s64 height);
57 69
58private: 70private:
59 Nvnflinger::Nvnflinger& m_nvnflinger; 71 const std::shared_ptr<Container> m_container;
60 Nvnflinger::HosBinderDriverServer& m_hos_binder_driver_server; 72
61 std::vector<u64> m_stray_layer_ids; 73 KernelHelpers::ServiceContext m_context;
74 std::mutex m_lock{};
75 std::set<u64> m_open_layer_ids{};
76 std::set<u64> m_stray_layer_ids{};
77 std::map<u64, Event> m_display_vsync_events{};
62 bool m_vsync_event_fetched{false}; 78 bool m_vsync_event_fetched{false};
63}; 79};
64 80
diff --git a/src/core/hle/service/vi/application_root_service.cpp b/src/core/hle/service/vi/application_root_service.cpp
index 7af7f062c..7f35a048d 100644
--- a/src/core/hle/service/vi/application_root_service.cpp
+++ b/src/core/hle/service/vi/application_root_service.cpp
@@ -4,17 +4,16 @@
4#include "core/hle/service/cmif_serialization.h" 4#include "core/hle/service/cmif_serialization.h"
5#include "core/hle/service/vi/application_display_service.h" 5#include "core/hle/service/vi/application_display_service.h"
6#include "core/hle/service/vi/application_root_service.h" 6#include "core/hle/service/vi/application_root_service.h"
7#include "core/hle/service/vi/container.h"
7#include "core/hle/service/vi/service_creator.h" 8#include "core/hle/service/vi/service_creator.h"
8#include "core/hle/service/vi/vi.h" 9#include "core/hle/service/vi/vi.h"
9#include "core/hle/service/vi/vi_types.h" 10#include "core/hle/service/vi/vi_types.h"
10 11
11namespace Service::VI { 12namespace Service::VI {
12 13
13IApplicationRootService::IApplicationRootService( 14IApplicationRootService::IApplicationRootService(Core::System& system_,
14 Core::System& system_, Nvnflinger::Nvnflinger& nvnflinger, 15 std::shared_ptr<Container> container)
15 Nvnflinger::HosBinderDriverServer& hos_binder_driver_server) 16 : ServiceFramework{system_, "vi:u"}, m_container{std::move(container)} {
16 : ServiceFramework{system_, "vi:u"}, m_nvnflinger{nvnflinger}, m_hos_binder_driver_server{
17 hos_binder_driver_server} {
18 static const FunctionInfo functions[] = { 17 static const FunctionInfo functions[] = {
19 {0, C<&IApplicationRootService::GetDisplayService>, "GetDisplayService"}, 18 {0, C<&IApplicationRootService::GetDisplayService>, "GetDisplayService"},
20 {1, nullptr, "GetDisplayServiceWithProxyNameExchange"}, 19 {1, nullptr, "GetDisplayServiceWithProxyNameExchange"},
@@ -27,8 +26,8 @@ IApplicationRootService::~IApplicationRootService() = default;
27Result IApplicationRootService::GetDisplayService( 26Result IApplicationRootService::GetDisplayService(
28 Out<SharedPointer<IApplicationDisplayService>> out_application_display_service, Policy policy) { 27 Out<SharedPointer<IApplicationDisplayService>> out_application_display_service, Policy policy) {
29 LOG_DEBUG(Service_VI, "called"); 28 LOG_DEBUG(Service_VI, "called");
30 R_RETURN(GetApplicationDisplayService(out_application_display_service, system, m_nvnflinger, 29 R_RETURN(GetApplicationDisplayService(out_application_display_service, system, m_container,
31 m_hos_binder_driver_server, Permission::User, policy)); 30 Permission::User, policy));
32} 31}
33 32
34} // namespace Service::VI 33} // namespace Service::VI
diff --git a/src/core/hle/service/vi/application_root_service.h b/src/core/hle/service/vi/application_root_service.h
index 9dbf28cb4..15aa4483d 100644
--- a/src/core/hle/service/vi/application_root_service.h
+++ b/src/core/hle/service/vi/application_root_service.h
@@ -10,20 +10,15 @@ namespace Core {
10class System; 10class System;
11} 11}
12 12
13namespace Service::Nvnflinger {
14class HosBinderDriverServer;
15class Nvnflinger;
16} // namespace Service::Nvnflinger
17
18namespace Service::VI { 13namespace Service::VI {
19 14
15class Container;
20class IApplicationDisplayService; 16class IApplicationDisplayService;
21enum class Policy : u32; 17enum class Policy : u32;
22 18
23class IApplicationRootService final : public ServiceFramework<IApplicationRootService> { 19class IApplicationRootService final : public ServiceFramework<IApplicationRootService> {
24public: 20public:
25 explicit IApplicationRootService(Core::System& system_, Nvnflinger::Nvnflinger& nvnflinger, 21 explicit IApplicationRootService(Core::System& system_, std::shared_ptr<Container> container);
26 Nvnflinger::HosBinderDriverServer& hos_binder_driver_server);
27 ~IApplicationRootService() override; 22 ~IApplicationRootService() override;
28 23
29private: 24private:
@@ -32,8 +27,7 @@ private:
32 Policy policy); 27 Policy policy);
33 28
34private: 29private:
35 Nvnflinger::Nvnflinger& m_nvnflinger; 30 const std::shared_ptr<Container> m_container;
36 Nvnflinger::HosBinderDriverServer& m_hos_binder_driver_server;
37}; 31};
38 32
39} // namespace Service::VI 33} // namespace Service::VI
diff --git a/src/core/hle/service/vi/conductor.cpp b/src/core/hle/service/vi/conductor.cpp
new file mode 100644
index 000000000..c8ce4fca0
--- /dev/null
+++ b/src/core/hle/service/vi/conductor.cpp
@@ -0,0 +1,114 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "common/settings.h"
5#include "core/core.h"
6#include "core/core_timing.h"
7#include "core/hle/service/vi/conductor.h"
8#include "core/hle/service/vi/container.h"
9#include "core/hle/service/vi/display_list.h"
10#include "core/hle/service/vi/vsync_manager.h"
11
12constexpr auto FrameNs = std::chrono::nanoseconds{1000000000 / 60};
13
14namespace Service::VI {
15
16Conductor::Conductor(Core::System& system, Container& container, DisplayList& displays)
17 : m_system(system), m_container(container) {
18 displays.ForEachDisplay([&](Display& display) {
19 m_vsync_managers.insert({display.GetId(), VsyncManager{}});
20 });
21
22 if (system.IsMulticore()) {
23 m_event = Core::Timing::CreateEvent(
24 "ScreenComposition",
25 [this](s64 time,
26 std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
27 m_signal.Set();
28 return std::chrono::nanoseconds(this->GetNextTicks());
29 });
30
31 system.CoreTiming().ScheduleLoopingEvent(FrameNs, FrameNs, m_event);
32 m_thread = std::jthread([this](std::stop_token token) { this->VsyncThread(token); });
33 } else {
34 m_event = Core::Timing::CreateEvent(
35 "ScreenComposition",
36 [this](s64 time,
37 std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
38 this->ProcessVsync();
39 return std::chrono::nanoseconds(this->GetNextTicks());
40 });
41
42 system.CoreTiming().ScheduleLoopingEvent(FrameNs, FrameNs, m_event);
43 }
44}
45
46Conductor::~Conductor() {
47 m_system.CoreTiming().UnscheduleEvent(m_event);
48
49 if (m_system.IsMulticore()) {
50 m_thread.request_stop();
51 m_signal.Set();
52 }
53}
54
55void Conductor::LinkVsyncEvent(u64 display_id, Event* event) {
56 if (auto it = m_vsync_managers.find(display_id); it != m_vsync_managers.end()) {
57 it->second.LinkVsyncEvent(event);
58 }
59}
60
61void Conductor::UnlinkVsyncEvent(u64 display_id, Event* event) {
62 if (auto it = m_vsync_managers.find(display_id); it != m_vsync_managers.end()) {
63 it->second.UnlinkVsyncEvent(event);
64 }
65}
66
67void Conductor::ProcessVsync() {
68 for (auto& [display_id, manager] : m_vsync_managers) {
69 m_container.ComposeOnDisplay(&m_swap_interval, &m_compose_speed_scale, display_id);
70 manager.SignalVsync();
71 }
72}
73
74void Conductor::VsyncThread(std::stop_token token) {
75 Common::SetCurrentThreadName("VSyncThread");
76
77 while (!token.stop_requested()) {
78 m_signal.Wait();
79
80 if (m_system.IsShuttingDown()) {
81 return;
82 }
83
84 this->ProcessVsync();
85 }
86}
87
88s64 Conductor::GetNextTicks() const {
89 const auto& settings = Settings::values;
90 auto speed_scale = 1.f;
91 if (settings.use_multi_core.GetValue()) {
92 if (settings.use_speed_limit.GetValue()) {
93 // Scales the speed based on speed_limit setting on MC. SC is handled by
94 // SpeedLimiter::DoSpeedLimiting.
95 speed_scale = 100.f / settings.speed_limit.GetValue();
96 } else {
97 // Run at unlocked framerate.
98 speed_scale = 0.01f;
99 }
100 }
101
102 // Adjust by speed limit determined during composition.
103 speed_scale /= m_compose_speed_scale;
104
105 if (m_system.GetNVDECActive() && settings.use_video_framerate.GetValue()) {
106 // Run at intended presentation rate during video playback.
107 speed_scale = 1.f;
108 }
109
110 const f32 effective_fps = 60.f / static_cast<f32>(m_swap_interval);
111 return static_cast<s64>(speed_scale * (1000000000.f / effective_fps));
112}
113
114} // namespace Service::VI
diff --git a/src/core/hle/service/vi/conductor.h b/src/core/hle/service/vi/conductor.h
new file mode 100644
index 000000000..52e3595d2
--- /dev/null
+++ b/src/core/hle/service/vi/conductor.h
@@ -0,0 +1,57 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <memory>
7#include <unordered_map>
8
9#include "common/common_types.h"
10#include "common/polyfill_thread.h"
11#include "common/thread.h"
12
13namespace Core {
14class System;
15}
16
17namespace Core::Timing {
18struct EventType;
19}
20
21namespace Service {
22class Event;
23}
24
25namespace Service::VI {
26
27class Container;
28class DisplayList;
29class VsyncManager;
30
31class Conductor {
32public:
33 explicit Conductor(Core::System& system, Container& container, DisplayList& displays);
34 ~Conductor();
35
36 void LinkVsyncEvent(u64 display_id, Event* event);
37 void UnlinkVsyncEvent(u64 display_id, Event* event);
38
39private:
40 void ProcessVsync();
41 void VsyncThread(std::stop_token token);
42 s64 GetNextTicks() const;
43
44private:
45 Core::System& m_system;
46 Container& m_container;
47 std::unordered_map<u64, VsyncManager> m_vsync_managers;
48 std::shared_ptr<Core::Timing::EventType> m_event;
49 Common::Event m_signal;
50 std::jthread m_thread;
51
52private:
53 s32 m_swap_interval = 1;
54 f32 m_compose_speed_scale = 1.0f;
55};
56
57} // namespace Service::VI
diff --git a/src/core/hle/service/vi/container.cpp b/src/core/hle/service/vi/container.cpp
new file mode 100644
index 000000000..310a207f1
--- /dev/null
+++ b/src/core/hle/service/vi/container.cpp
@@ -0,0 +1,228 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/core.h"
5#include "core/hle/service/nvdrv/nvdrv_interface.h"
6#include "core/hle/service/nvnflinger/buffer_queue_producer.h"
7#include "core/hle/service/nvnflinger/hos_binder_driver.h"
8#include "core/hle/service/nvnflinger/hos_binder_driver_server.h"
9#include "core/hle/service/nvnflinger/surface_flinger.h"
10#include "core/hle/service/sm/sm.h"
11#include "core/hle/service/vi/container.h"
12#include "core/hle/service/vi/vi_results.h"
13
14namespace Service::VI {
15
16Container::Container(Core::System& system) {
17 m_displays.CreateDisplay(DisplayName{"Default"});
18 m_displays.CreateDisplay(DisplayName{"External"});
19 m_displays.CreateDisplay(DisplayName{"Edid"});
20 m_displays.CreateDisplay(DisplayName{"Internal"});
21 m_displays.CreateDisplay(DisplayName{"Null"});
22
23 m_binder_driver =
24 system.ServiceManager().GetService<Nvnflinger::IHOSBinderDriver>("dispdrv", true);
25 m_surface_flinger = m_binder_driver->GetSurfaceFlinger();
26
27 const auto nvdrv =
28 system.ServiceManager().GetService<Nvidia::NVDRV>("nvdrv:s", true)->GetModule();
29 m_shared_buffer_manager.emplace(system, *this, nvdrv);
30
31 m_displays.ForEachDisplay(
32 [&](auto& display) { m_surface_flinger->AddDisplay(display.GetId()); });
33
34 m_conductor.emplace(system, *this, m_displays);
35}
36
37Container::~Container() {
38 this->OnTerminate();
39}
40
41void Container::OnTerminate() {
42 std::scoped_lock lk{m_lock};
43
44 m_is_shut_down = true;
45
46 m_layers.ForEachLayer([&](auto& layer) {
47 if (layer.IsOpen()) {
48 this->DestroyBufferQueueLocked(&layer);
49 }
50 });
51
52 m_displays.ForEachDisplay(
53 [&](auto& display) { m_surface_flinger->RemoveDisplay(display.GetId()); });
54}
55
56SharedBufferManager* Container::GetSharedBufferManager() {
57 return std::addressof(*m_shared_buffer_manager);
58}
59
60Result Container::GetBinderDriver(
61 std::shared_ptr<Nvnflinger::IHOSBinderDriver>* out_binder_driver) {
62 *out_binder_driver = m_binder_driver;
63 R_SUCCEED();
64}
65
66Result Container::GetLayerProducerHandle(
67 std::shared_ptr<android::BufferQueueProducer>* out_producer, u64 layer_id) {
68 std::scoped_lock lk{m_lock};
69
70 auto* const layer = m_layers.GetLayerById(layer_id);
71 R_UNLESS(layer != nullptr, VI::ResultNotFound);
72
73 const auto binder = m_binder_driver->GetServer()->TryGetBinder(layer->GetProducerBinderId());
74 R_UNLESS(binder != nullptr, VI::ResultNotFound);
75
76 *out_producer = std::static_pointer_cast<android::BufferQueueProducer>(binder);
77 R_SUCCEED();
78}
79
80Result Container::OpenDisplay(u64* out_display_id, const DisplayName& display_name) {
81 auto* const display = m_displays.GetDisplayByName(display_name);
82 R_UNLESS(display != nullptr, VI::ResultNotFound);
83
84 *out_display_id = display->GetId();
85 R_SUCCEED();
86}
87
88Result Container::CloseDisplay(u64 display_id) {
89 R_SUCCEED();
90}
91
92Result Container::CreateManagedLayer(u64* out_layer_id, u64 display_id, u64 owner_aruid) {
93 std::scoped_lock lk{m_lock};
94 R_RETURN(this->CreateLayerLocked(out_layer_id, display_id, owner_aruid));
95}
96
97Result Container::DestroyManagedLayer(u64 layer_id) {
98 std::scoped_lock lk{m_lock};
99
100 // Try to close, if open, but don't fail if not.
101 this->CloseLayerLocked(layer_id);
102
103 R_RETURN(this->DestroyLayerLocked(layer_id));
104}
105
106Result Container::OpenLayer(s32* out_producer_binder_id, u64 layer_id, u64 aruid) {
107 std::scoped_lock lk{m_lock};
108 R_RETURN(this->OpenLayerLocked(out_producer_binder_id, layer_id, aruid));
109}
110
111Result Container::CloseLayer(u64 layer_id) {
112 std::scoped_lock lk{m_lock};
113 R_RETURN(this->CloseLayerLocked(layer_id));
114}
115
116Result Container::SetLayerVisibility(u64 layer_id, bool visible) {
117 std::scoped_lock lk{m_lock};
118
119 auto* const layer = m_layers.GetLayerById(layer_id);
120 R_UNLESS(layer != nullptr, VI::ResultNotFound);
121
122 m_surface_flinger->SetLayerVisibility(layer->GetConsumerBinderId(), visible);
123 R_SUCCEED();
124}
125
126Result Container::SetLayerBlending(u64 layer_id, bool enabled) {
127 std::scoped_lock lk{m_lock};
128
129 auto* const layer = m_layers.GetLayerById(layer_id);
130 R_UNLESS(layer != nullptr, VI::ResultNotFound);
131
132 m_surface_flinger->SetLayerBlending(layer->GetConsumerBinderId(),
133 enabled ? Nvnflinger::LayerBlending::Coverage
134 : Nvnflinger::LayerBlending::None);
135 R_SUCCEED();
136}
137
138void Container::LinkVsyncEvent(u64 display_id, Event* event) {
139 std::scoped_lock lk{m_lock};
140 m_conductor->LinkVsyncEvent(display_id, event);
141}
142
143void Container::UnlinkVsyncEvent(u64 display_id, Event* event) {
144 std::scoped_lock lk{m_lock};
145 m_conductor->UnlinkVsyncEvent(display_id, event);
146}
147
148Result Container::CreateStrayLayer(s32* out_producer_binder_id, u64* out_layer_id, u64 display_id) {
149 std::scoped_lock lk{m_lock};
150 R_TRY(this->CreateLayerLocked(out_layer_id, display_id, {}));
151 R_RETURN(this->OpenLayerLocked(out_producer_binder_id, *out_layer_id, {}));
152}
153
154Result Container::DestroyStrayLayer(u64 layer_id) {
155 std::scoped_lock lk{m_lock};
156 R_TRY(this->CloseLayerLocked(layer_id));
157 R_RETURN(this->DestroyLayerLocked(layer_id));
158}
159
160Result Container::CreateLayerLocked(u64* out_layer_id, u64 display_id, u64 owner_aruid) {
161 auto* const display = m_displays.GetDisplayById(display_id);
162 R_UNLESS(display != nullptr, VI::ResultNotFound);
163
164 auto* const layer = m_layers.CreateLayer(owner_aruid, display);
165 R_UNLESS(layer != nullptr, VI::ResultNotFound);
166
167 *out_layer_id = layer->GetId();
168 R_SUCCEED();
169}
170
171Result Container::DestroyLayerLocked(u64 layer_id) {
172 R_SUCCEED_IF(m_layers.DestroyLayer(layer_id));
173 R_THROW(VI::ResultNotFound);
174}
175
176Result Container::OpenLayerLocked(s32* out_producer_binder_id, u64 layer_id, u64 aruid) {
177 R_UNLESS(!m_is_shut_down, VI::ResultOperationFailed);
178
179 auto* const layer = m_layers.GetLayerById(layer_id);
180 R_UNLESS(layer != nullptr, VI::ResultNotFound);
181 R_UNLESS(!layer->IsOpen(), VI::ResultOperationFailed);
182 R_UNLESS(layer->GetOwnerAruid() == aruid, VI::ResultPermissionDenied);
183
184 this->CreateBufferQueueLocked(layer);
185 *out_producer_binder_id = layer->GetProducerBinderId();
186
187 R_SUCCEED();
188}
189
190Result Container::CloseLayerLocked(u64 layer_id) {
191 auto* const layer = m_layers.GetLayerById(layer_id);
192 R_UNLESS(layer != nullptr, VI::ResultNotFound);
193 R_UNLESS(layer->IsOpen(), VI::ResultOperationFailed);
194
195 this->DestroyBufferQueueLocked(layer);
196
197 R_SUCCEED();
198}
199
200void Container::CreateBufferQueueLocked(Layer* layer) {
201 s32 consumer_binder_id, producer_binder_id;
202 m_surface_flinger->CreateBufferQueue(&consumer_binder_id, &producer_binder_id);
203 layer->Open(consumer_binder_id, producer_binder_id);
204
205 if (auto* display = layer->GetDisplay(); display != nullptr) {
206 m_surface_flinger->AddLayerToDisplayStack(display->GetId(), consumer_binder_id);
207 }
208}
209
210void Container::DestroyBufferQueueLocked(Layer* layer) {
211 if (auto* display = layer->GetDisplay(); display != nullptr) {
212 m_surface_flinger->RemoveLayerFromDisplayStack(display->GetId(),
213 layer->GetConsumerBinderId());
214 }
215
216 layer->Close();
217 m_surface_flinger->DestroyBufferQueue(layer->GetConsumerBinderId(),
218 layer->GetProducerBinderId());
219}
220
221bool Container::ComposeOnDisplay(s32* out_swap_interval, f32* out_compose_speed_scale,
222 u64 display_id) {
223 std::scoped_lock lk{m_lock};
224 return m_surface_flinger->ComposeDisplay(out_swap_interval, out_compose_speed_scale,
225 display_id);
226}
227
228} // namespace Service::VI
diff --git a/src/core/hle/service/vi/container.h b/src/core/hle/service/vi/container.h
new file mode 100644
index 000000000..cd0d2ca86
--- /dev/null
+++ b/src/core/hle/service/vi/container.h
@@ -0,0 +1,92 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <memory>
7#include <mutex>
8#include <optional>
9
10#include "core/hle/service/vi/conductor.h"
11#include "core/hle/service/vi/display_list.h"
12#include "core/hle/service/vi/layer_list.h"
13#include "core/hle/service/vi/shared_buffer_manager.h"
14
15union Result;
16
17namespace Service::android {
18class BufferQueueProducer;
19}
20
21namespace Service::Nvnflinger {
22class IHOSBinderDriver;
23class SurfaceFlinger;
24} // namespace Service::Nvnflinger
25
26namespace Service {
27class Event;
28}
29
30namespace Service::VI {
31
32class SharedBufferManager;
33
34class Container {
35public:
36 explicit Container(Core::System& system);
37 ~Container();
38
39 void OnTerminate();
40
41 SharedBufferManager* GetSharedBufferManager();
42
43 Result GetBinderDriver(std::shared_ptr<Nvnflinger::IHOSBinderDriver>* out_binder_driver);
44 Result GetLayerProducerHandle(std::shared_ptr<android::BufferQueueProducer>* out_producer,
45 u64 layer_id);
46
47 Result OpenDisplay(u64* out_display_id, const DisplayName& display_name);
48 Result CloseDisplay(u64 display_id);
49
50 // Managed layers are created by the interaction between am and ommdisp
51 // on behalf of an applet. Their lifetime ends with the lifetime of the
52 // applet's ISelfController.
53 Result CreateManagedLayer(u64* out_layer_id, u64 display_id, u64 owner_aruid);
54 Result DestroyManagedLayer(u64 layer_id);
55 Result OpenLayer(s32* out_producer_binder_id, u64 layer_id, u64 aruid);
56 Result CloseLayer(u64 layer_id);
57
58 // Stray layers are created by non-applet sysmodules. Their lifetime ends
59 // with the lifetime of the IApplicationDisplayService which created them.
60 Result CreateStrayLayer(s32* out_producer_binder_id, u64* out_layer_id, u64 display_id);
61 Result DestroyStrayLayer(u64 layer_id);
62
63 Result SetLayerVisibility(u64 layer_id, bool visible);
64 Result SetLayerBlending(u64 layer_id, bool enabled);
65
66 void LinkVsyncEvent(u64 display_id, Event* event);
67 void UnlinkVsyncEvent(u64 display_id, Event* event);
68
69private:
70 Result CreateLayerLocked(u64* out_layer_id, u64 display_id, u64 owner_aruid);
71 Result DestroyLayerLocked(u64 layer_id);
72 Result OpenLayerLocked(s32* out_producer_binder_id, u64 layer_id, u64 aruid);
73 Result CloseLayerLocked(u64 layer_id);
74
75 void CreateBufferQueueLocked(Layer* layer);
76 void DestroyBufferQueueLocked(Layer* layer);
77
78public:
79 bool ComposeOnDisplay(s32* out_swap_interval, f32* out_compose_speed_scale, u64 display_id);
80
81private:
82 std::mutex m_lock{};
83 DisplayList m_displays{};
84 LayerList m_layers{};
85 std::shared_ptr<Nvnflinger::IHOSBinderDriver> m_binder_driver{};
86 std::shared_ptr<Nvnflinger::SurfaceFlinger> m_surface_flinger{};
87 std::optional<SharedBufferManager> m_shared_buffer_manager{};
88 std::optional<Conductor> m_conductor{};
89 bool m_is_shut_down{};
90};
91
92} // namespace Service::VI
diff --git a/src/core/hle/service/vi/display.h b/src/core/hle/service/vi/display.h
new file mode 100644
index 000000000..fceda75e3
--- /dev/null
+++ b/src/core/hle/service/vi/display.h
@@ -0,0 +1,44 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "core/hle/service/vi/vi_types.h"
7
8namespace Service::VI {
9
10class Display {
11public:
12 constexpr Display() = default;
13
14 void Initialize(u64 id, const DisplayName& display_name) {
15 m_id = id;
16 m_display_name = display_name;
17 m_is_initialized = true;
18 }
19
20 void Finalize() {
21 m_id = {};
22 m_display_name = {};
23 m_is_initialized = {};
24 }
25
26 u64 GetId() const {
27 return m_id;
28 }
29
30 const DisplayName& GetDisplayName() const {
31 return m_display_name;
32 }
33
34 bool IsInitialized() const {
35 return m_is_initialized;
36 }
37
38private:
39 u64 m_id{};
40 DisplayName m_display_name{};
41 bool m_is_initialized{};
42};
43
44} // namespace Service::VI
diff --git a/src/core/hle/service/vi/display/vi_display.cpp b/src/core/hle/service/vi/display/vi_display.cpp
deleted file mode 100644
index 7f2af9acc..000000000
--- a/src/core/hle/service/vi/display/vi_display.cpp
+++ /dev/null
@@ -1,143 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <algorithm>
5#include <utility>
6
7#include <fmt/format.h>
8
9#include "common/assert.h"
10#include "core/core.h"
11#include "core/hle/kernel/k_event.h"
12#include "core/hle/kernel/k_readable_event.h"
13#include "core/hle/service/kernel_helpers.h"
14#include "core/hle/service/nvdrv/core/container.h"
15#include "core/hle/service/nvnflinger/buffer_item_consumer.h"
16#include "core/hle/service/nvnflinger/buffer_queue_consumer.h"
17#include "core/hle/service/nvnflinger/buffer_queue_core.h"
18#include "core/hle/service/nvnflinger/buffer_queue_producer.h"
19#include "core/hle/service/nvnflinger/hardware_composer.h"
20#include "core/hle/service/nvnflinger/hos_binder_driver_server.h"
21#include "core/hle/service/vi/display/vi_display.h"
22#include "core/hle/service/vi/layer/vi_layer.h"
23#include "core/hle/service/vi/vi_results.h"
24
25namespace Service::VI {
26
27struct BufferQueue {
28 std::shared_ptr<android::BufferQueueCore> core;
29 std::unique_ptr<android::BufferQueueProducer> producer;
30 std::unique_ptr<android::BufferQueueConsumer> consumer;
31};
32
33static BufferQueue CreateBufferQueue(KernelHelpers::ServiceContext& service_context,
34 Service::Nvidia::NvCore::NvMap& nvmap) {
35 auto buffer_queue_core = std::make_shared<android::BufferQueueCore>();
36 return {
37 buffer_queue_core,
38 std::make_unique<android::BufferQueueProducer>(service_context, buffer_queue_core, nvmap),
39 std::make_unique<android::BufferQueueConsumer>(buffer_queue_core)};
40}
41
42Display::Display(u64 id, std::string name_,
43 Nvnflinger::HosBinderDriverServer& hos_binder_driver_server_,
44 KernelHelpers::ServiceContext& service_context_, Core::System& system_)
45 : display_id{id}, name{std::move(name_)}, hos_binder_driver_server{hos_binder_driver_server_},
46 service_context{service_context_} {
47 hardware_composer = std::make_unique<Nvnflinger::HardwareComposer>();
48 vsync_event = service_context.CreateEvent(fmt::format("Display VSync Event {}", id));
49}
50
51Display::~Display() {
52 service_context.CloseEvent(vsync_event);
53}
54
55Layer& Display::GetLayer(std::size_t index) {
56 size_t i = 0;
57 for (auto& layer : layers) {
58 if (!layer->IsOpen() || !layer->IsVisible()) {
59 continue;
60 }
61
62 if (i == index) {
63 return *layer;
64 }
65
66 i++;
67 }
68
69 UNREACHABLE();
70}
71
72size_t Display::GetNumLayers() const {
73 return std::ranges::count_if(layers, [](auto& l) { return l->IsOpen() && l->IsVisible(); });
74}
75
76Kernel::KReadableEvent* Display::GetVSyncEvent() {
77 return &vsync_event->GetReadableEvent();
78}
79
80void Display::SignalVSyncEvent() {
81 vsync_event->Signal();
82}
83
84void Display::CreateLayer(u64 layer_id, u32 binder_id,
85 Service::Nvidia::NvCore::Container& nv_core) {
86 auto [core, producer, consumer] = CreateBufferQueue(service_context, nv_core.GetNvMapFile());
87
88 auto buffer_item_consumer = std::make_shared<android::BufferItemConsumer>(std::move(consumer));
89 buffer_item_consumer->Connect(false);
90
91 layers.emplace_back(std::make_unique<Layer>(layer_id, binder_id, *core, *producer,
92 std::move(buffer_item_consumer)));
93
94 if (is_abandoned) {
95 this->FindLayer(layer_id)->GetConsumer().Abandon();
96 }
97
98 hos_binder_driver_server.RegisterProducer(std::move(producer));
99}
100
101void Display::DestroyLayer(u64 layer_id) {
102 if (auto* layer = this->FindLayer(layer_id); layer != nullptr) {
103 layer->GetConsumer().Abandon();
104 }
105
106 std::erase_if(layers,
107 [layer_id](const auto& layer) { return layer->GetLayerId() == layer_id; });
108}
109
110void Display::Abandon() {
111 for (auto& layer : layers) {
112 layer->GetConsumer().Abandon();
113 }
114 is_abandoned = true;
115}
116
117Layer* Display::FindLayer(u64 layer_id) {
118 const auto itr =
119 std::find_if(layers.begin(), layers.end(), [layer_id](const std::unique_ptr<Layer>& layer) {
120 return layer->GetLayerId() == layer_id;
121 });
122
123 if (itr == layers.end()) {
124 return nullptr;
125 }
126
127 return itr->get();
128}
129
130const Layer* Display::FindLayer(u64 layer_id) const {
131 const auto itr =
132 std::find_if(layers.begin(), layers.end(), [layer_id](const std::unique_ptr<Layer>& layer) {
133 return layer->GetLayerId() == layer_id;
134 });
135
136 if (itr == layers.end()) {
137 return nullptr;
138 }
139
140 return itr->get();
141}
142
143} // namespace Service::VI
diff --git a/src/core/hle/service/vi/display/vi_display.h b/src/core/hle/service/vi/display/vi_display.h
deleted file mode 100644
index 220292cff..000000000
--- a/src/core/hle/service/vi/display/vi_display.h
+++ /dev/null
@@ -1,143 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <memory>
7#include <string>
8#include <vector>
9
10#include "common/common_funcs.h"
11#include "common/common_types.h"
12#include "core/hle/result.h"
13
14namespace Core {
15class System;
16}
17
18namespace Kernel {
19class KEvent;
20class KReadableEvent;
21} // namespace Kernel
22
23namespace Service::android {
24class BufferQueueProducer;
25}
26
27namespace Service::KernelHelpers {
28class ServiceContext;
29}
30
31namespace Service::Nvnflinger {
32class HardwareComposer;
33class HosBinderDriverServer;
34} // namespace Service::Nvnflinger
35
36namespace Service::Nvidia::NvCore {
37class Container;
38class NvMap;
39} // namespace Service::Nvidia::NvCore
40
41namespace Service::VI {
42
43class Layer;
44
45/// Represents a single display type
46class Display {
47public:
48 YUZU_NON_COPYABLE(Display);
49 YUZU_NON_MOVEABLE(Display);
50
51 /// Constructs a display with a given unique ID and name.
52 ///
53 /// @param id The unique ID for this display.
54 /// @param hos_binder_driver_server_ Nvnflinger HOSBinderDriver server instance.
55 /// @param service_context_ The ServiceContext for the owning service.
56 /// @param name_ The name for this display.
57 /// @param system_ The global system instance.
58 ///
59 Display(u64 id, std::string name_, Nvnflinger::HosBinderDriverServer& hos_binder_driver_server_,
60 KernelHelpers::ServiceContext& service_context_, Core::System& system_);
61 ~Display();
62
63 /// Gets the unique ID assigned to this display.
64 u64 GetID() const {
65 return display_id;
66 }
67
68 /// Gets the name of this display
69 const std::string& GetName() const {
70 return name;
71 }
72
73 /// Whether or not this display has any layers added to it.
74 bool HasLayers() const {
75 return GetNumLayers() > 0;
76 }
77
78 /// Gets a layer for this display based off an index.
79 Layer& GetLayer(std::size_t index);
80
81 std::size_t GetNumLayers() const;
82
83 /// Gets the internal vsync event.
84 Kernel::KReadableEvent* GetVSyncEvent();
85
86 /// Signals the internal vsync event.
87 void SignalVSyncEvent();
88
89 /// Creates and adds a layer to this display with the given ID.
90 ///
91 /// @param layer_id The ID to assign to the created layer.
92 /// @param binder_id The ID assigned to the buffer queue.
93 ///
94 void CreateLayer(u64 layer_id, u32 binder_id, Service::Nvidia::NvCore::Container& core);
95
96 /// Removes a layer from this display with the given ID.
97 ///
98 /// @param layer_id The ID assigned to the layer to destroy.
99 ///
100 void DestroyLayer(u64 layer_id);
101
102 /// Resets the display for a new connection.
103 void Reset() {
104 layers.clear();
105 }
106
107 void Abandon();
108
109 /// Attempts to find a layer with the given ID.
110 ///
111 /// @param layer_id The layer ID.
112 ///
113 /// @returns If found, the Layer instance with the given ID.
114 /// If not found, then nullptr is returned.
115 ///
116 Layer* FindLayer(u64 layer_id);
117
118 /// Attempts to find a layer with the given ID.
119 ///
120 /// @param layer_id The layer ID.
121 ///
122 /// @returns If found, the Layer instance with the given ID.
123 /// If not found, then nullptr is returned.
124 ///
125 const Layer* FindLayer(u64 layer_id) const;
126
127 Nvnflinger::HardwareComposer& GetComposer() const {
128 return *hardware_composer;
129 }
130
131private:
132 u64 display_id;
133 std::string name;
134 Nvnflinger::HosBinderDriverServer& hos_binder_driver_server;
135 KernelHelpers::ServiceContext& service_context;
136
137 std::vector<std::unique_ptr<Layer>> layers;
138 std::unique_ptr<Nvnflinger::HardwareComposer> hardware_composer;
139 Kernel::KEvent* vsync_event{};
140 bool is_abandoned{};
141};
142
143} // namespace Service::VI
diff --git a/src/core/hle/service/vi/display_list.h b/src/core/hle/service/vi/display_list.h
new file mode 100644
index 000000000..f710ac472
--- /dev/null
+++ b/src/core/hle/service/vi/display_list.h
@@ -0,0 +1,83 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <cstring>
7
8#include "core/hle/service/vi/display.h"
9
10namespace Service::VI {
11
12class DisplayList {
13public:
14 constexpr DisplayList() = default;
15
16 bool CreateDisplay(const DisplayName& name) {
17 Display* const display = this->GetFreeDisplay();
18 if (!display) {
19 return false;
20 }
21
22 display->Initialize(m_next_id++, name);
23 return true;
24 }
25
26 bool DestroyDisplay(u64 display_id) {
27 Display* display = this->GetDisplayById(display_id);
28 if (!display) {
29 return false;
30 }
31
32 display->Finalize();
33 return true;
34 }
35
36 Display* GetDisplayByName(const DisplayName& name) {
37 for (auto& display : m_displays) {
38 if (display.IsInitialized() &&
39 std::strncmp(name.data(), display.GetDisplayName().data(), sizeof(DisplayName)) ==
40 0) {
41 return &display;
42 }
43 }
44
45 return nullptr;
46 }
47
48 Display* GetDisplayById(u64 display_id) {
49 for (auto& display : m_displays) {
50 if (display.IsInitialized() && display.GetId() == display_id) {
51 return &display;
52 }
53 }
54
55 return nullptr;
56 }
57
58 template <typename F>
59 void ForEachDisplay(F&& cb) {
60 for (auto& display : m_displays) {
61 if (display.IsInitialized()) {
62 cb(display);
63 }
64 }
65 }
66
67private:
68 Display* GetFreeDisplay() {
69 for (auto& display : m_displays) {
70 if (!display.IsInitialized()) {
71 return &display;
72 }
73 }
74
75 return nullptr;
76 }
77
78private:
79 std::array<Display, 8> m_displays{};
80 u64 m_next_id{};
81};
82
83} // namespace Service::VI
diff --git a/src/core/hle/service/vi/layer.h b/src/core/hle/service/vi/layer.h
new file mode 100644
index 000000000..b85c8df61
--- /dev/null
+++ b/src/core/hle/service/vi/layer.h
@@ -0,0 +1,79 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "common/common_types.h"
7
8namespace Service::VI {
9
10class Display;
11
12class Layer {
13public:
14 constexpr Layer() = default;
15
16 void Initialize(u64 id, u64 owner_aruid, Display* display) {
17 m_id = id;
18 m_owner_aruid = owner_aruid;
19 m_display = display;
20 m_is_initialized = true;
21 }
22
23 void Finalize() {
24 m_id = {};
25 m_display = {};
26 m_is_initialized = {};
27 }
28
29 void Open(s32 consumer_binder_id, s32 producer_binder_id) {
30 m_consumer_binder_id = consumer_binder_id;
31 m_producer_binder_id = producer_binder_id;
32 m_is_open = true;
33 }
34
35 void Close() {
36 m_producer_binder_id = {};
37 m_consumer_binder_id = {};
38 m_is_open = {};
39 }
40
41 u64 GetId() const {
42 return m_id;
43 }
44
45 u64 GetOwnerAruid() const {
46 return m_owner_aruid;
47 }
48
49 Display* GetDisplay() const {
50 return m_display;
51 }
52
53 s32 GetConsumerBinderId() const {
54 return m_consumer_binder_id;
55 }
56
57 s32 GetProducerBinderId() const {
58 return m_producer_binder_id;
59 }
60
61 bool IsInitialized() const {
62 return m_is_initialized;
63 }
64
65 bool IsOpen() const {
66 return m_is_open;
67 }
68
69private:
70 u64 m_id{};
71 u64 m_owner_aruid{};
72 Display* m_display{};
73 s32 m_consumer_binder_id{};
74 s32 m_producer_binder_id{};
75 bool m_is_initialized{};
76 bool m_is_open{};
77};
78
79} // namespace Service::VI
diff --git a/src/core/hle/service/vi/layer/vi_layer.cpp b/src/core/hle/service/vi/layer/vi_layer.cpp
deleted file mode 100644
index eca35d82a..000000000
--- a/src/core/hle/service/vi/layer/vi_layer.cpp
+++ /dev/null
@@ -1,18 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/hle/service/nvnflinger/hwc_layer.h"
5#include "core/hle/service/vi/layer/vi_layer.h"
6
7namespace Service::VI {
8
9Layer::Layer(u64 layer_id_, u32 binder_id_, android::BufferQueueCore& core_,
10 android::BufferQueueProducer& binder_,
11 std::shared_ptr<android::BufferItemConsumer>&& consumer_)
12 : layer_id{layer_id_}, binder_id{binder_id_}, core{core_}, binder{binder_}, consumer{std::move(
13 consumer_)},
14 blending{Nvnflinger::LayerBlending::None}, open{false}, visible{true} {}
15
16Layer::~Layer() = default;
17
18} // namespace Service::VI
diff --git a/src/core/hle/service/vi/layer/vi_layer.h b/src/core/hle/service/vi/layer/vi_layer.h
deleted file mode 100644
index 14e229903..000000000
--- a/src/core/hle/service/vi/layer/vi_layer.h
+++ /dev/null
@@ -1,118 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <memory>
7#include <utility>
8
9#include "common/common_types.h"
10
11namespace Service::android {
12class BufferItemConsumer;
13class BufferQueueCore;
14class BufferQueueProducer;
15} // namespace Service::android
16
17namespace Service::Nvnflinger {
18enum class LayerBlending : u32;
19}
20
21namespace Service::VI {
22
23/// Represents a single display layer.
24class Layer {
25public:
26 /// Constructs a layer with a given ID and buffer queue.
27 ///
28 /// @param layer_id_ The ID to assign to this layer.
29 /// @param binder_id_ The binder ID to assign to this layer.
30 /// @param binder_ The buffer producer queue for this layer to use.
31 ///
32 Layer(u64 layer_id_, u32 binder_id_, android::BufferQueueCore& core_,
33 android::BufferQueueProducer& binder_,
34 std::shared_ptr<android::BufferItemConsumer>&& consumer_);
35 ~Layer();
36
37 Layer(const Layer&) = delete;
38 Layer& operator=(const Layer&) = delete;
39
40 Layer(Layer&&) = default;
41 Layer& operator=(Layer&&) = delete;
42
43 /// Gets the ID for this layer.
44 u64 GetLayerId() const {
45 return layer_id;
46 }
47
48 /// Gets the binder ID for this layer.
49 u32 GetBinderId() const {
50 return binder_id;
51 }
52
53 /// Gets a reference to the buffer queue this layer is using.
54 android::BufferQueueProducer& GetBufferQueue() {
55 return binder;
56 }
57
58 /// Gets a const reference to the buffer queue this layer is using.
59 const android::BufferQueueProducer& GetBufferQueue() const {
60 return binder;
61 }
62
63 android::BufferItemConsumer& GetConsumer() {
64 return *consumer;
65 }
66
67 const android::BufferItemConsumer& GetConsumer() const {
68 return *consumer;
69 }
70
71 android::BufferQueueCore& Core() {
72 return core;
73 }
74
75 const android::BufferQueueCore& Core() const {
76 return core;
77 }
78
79 bool IsVisible() const {
80 return visible;
81 }
82
83 void SetVisibility(bool v) {
84 visible = v;
85 }
86
87 bool IsOpen() const {
88 return open;
89 }
90
91 bool Close() {
92 return std::exchange(open, false);
93 }
94
95 bool Open() {
96 return !std::exchange(open, true);
97 }
98
99 Nvnflinger::LayerBlending GetBlending() {
100 return blending;
101 }
102
103 void SetBlending(Nvnflinger::LayerBlending b) {
104 blending = b;
105 }
106
107private:
108 const u64 layer_id;
109 const u32 binder_id;
110 android::BufferQueueCore& core;
111 android::BufferQueueProducer& binder;
112 std::shared_ptr<android::BufferItemConsumer> consumer;
113 Service::Nvnflinger::LayerBlending blending;
114 bool open;
115 bool visible;
116};
117
118} // namespace Service::VI
diff --git a/src/core/hle/service/vi/layer_list.h b/src/core/hle/service/vi/layer_list.h
new file mode 100644
index 000000000..1738ede9a
--- /dev/null
+++ b/src/core/hle/service/vi/layer_list.h
@@ -0,0 +1,69 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "core/hle/service/vi/layer.h"
7
8namespace Service::VI {
9
10class LayerList {
11public:
12 constexpr LayerList() = default;
13
14 Layer* CreateLayer(u64 owner_aruid, Display* display) {
15 Layer* const layer = GetFreeLayer();
16 if (!layer) {
17 return nullptr;
18 }
19
20 layer->Initialize(++m_next_id, owner_aruid, display);
21 return layer;
22 }
23
24 bool DestroyLayer(u64 layer_id) {
25 Layer* const layer = GetLayerById(layer_id);
26 if (!layer) {
27 return false;
28 }
29
30 layer->Finalize();
31 return true;
32 }
33
34 Layer* GetLayerById(u64 layer_id) {
35 for (auto& layer : m_layers) {
36 if (layer.IsInitialized() && layer.GetId() == layer_id) {
37 return &layer;
38 }
39 }
40
41 return nullptr;
42 }
43
44 template <typename F>
45 void ForEachLayer(F&& cb) {
46 for (auto& layer : m_layers) {
47 if (layer.IsInitialized()) {
48 cb(layer);
49 }
50 }
51 }
52
53private:
54 Layer* GetFreeLayer() {
55 for (auto& layer : m_layers) {
56 if (!layer.IsInitialized()) {
57 return &layer;
58 }
59 }
60
61 return nullptr;
62 }
63
64private:
65 std::array<Layer, 8> m_layers{};
66 u64 m_next_id{};
67};
68
69} // namespace Service::VI
diff --git a/src/core/hle/service/vi/manager_display_service.cpp b/src/core/hle/service/vi/manager_display_service.cpp
index 17f2f3b8f..9f856282e 100644
--- a/src/core/hle/service/vi/manager_display_service.cpp
+++ b/src/core/hle/service/vi/manager_display_service.cpp
@@ -2,22 +2,21 @@
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include "core/hle/service/cmif_serialization.h" 4#include "core/hle/service/cmif_serialization.h"
5#include "core/hle/service/nvnflinger/nvnflinger.h" 5#include "core/hle/service/vi/container.h"
6#include "core/hle/service/vi/manager_display_service.h" 6#include "core/hle/service/vi/manager_display_service.h"
7#include "core/hle/service/vi/vi_results.h"
8 7
9namespace Service::VI { 8namespace Service::VI {
10 9
11IManagerDisplayService::IManagerDisplayService(Core::System& system_, 10IManagerDisplayService::IManagerDisplayService(Core::System& system_,
12 Nvnflinger::Nvnflinger& nvnflinger) 11 std::shared_ptr<Container> container)
13 : ServiceFramework{system_, "IManagerDisplayService"}, m_nvnflinger{nvnflinger} { 12 : ServiceFramework{system_, "IManagerDisplayService"}, m_container{std::move(container)} {
14 // clang-format off 13 // clang-format off
15 static const FunctionInfo functions[] = { 14 static const FunctionInfo functions[] = {
16 {200, nullptr, "AllocateProcessHeapBlock"}, 15 {200, nullptr, "AllocateProcessHeapBlock"},
17 {201, nullptr, "FreeProcessHeapBlock"}, 16 {201, nullptr, "FreeProcessHeapBlock"},
18 {1102, nullptr, "GetDisplayResolution"}, 17 {1102, nullptr, "GetDisplayResolution"},
19 {2010, C<&IManagerDisplayService::CreateManagedLayer>, "CreateManagedLayer"}, 18 {2010, C<&IManagerDisplayService::CreateManagedLayer>, "CreateManagedLayer"},
20 {2011, nullptr, "DestroyManagedLayer"}, 19 {2011, C<&IManagerDisplayService::DestroyManagedLayer>, "DestroyManagedLayer"},
21 {2012, nullptr, "CreateStrayLayer"}, 20 {2012, nullptr, "CreateStrayLayer"},
22 {2050, nullptr, "CreateIndirectLayer"}, 21 {2050, nullptr, "CreateIndirectLayer"},
23 {2051, nullptr, "DestroyIndirectLayer"}, 22 {2051, nullptr, "DestroyIndirectLayer"},
@@ -102,19 +101,30 @@ IManagerDisplayService::IManagerDisplayService(Core::System& system_,
102 101
103IManagerDisplayService::~IManagerDisplayService() = default; 102IManagerDisplayService::~IManagerDisplayService() = default;
104 103
105Result IManagerDisplayService::CreateManagedLayer(Out<u64> out_layer_id, u32 unknown, 104Result IManagerDisplayService::CreateSharedLayerSession(Kernel::KProcess* owner_process,
106 u64 display_id, AppletResourceUserId aruid) { 105 u64* out_buffer_id, u64* out_layer_handle,
107 LOG_WARNING(Service_VI, "(STUBBED) called. unknown={}, display={}, aruid={}", unknown, 106 u64 display_id, bool enable_blending) {
108 display_id, aruid.pid); 107 R_RETURN(m_container->GetSharedBufferManager()->CreateSession(
108 owner_process, out_buffer_id, out_layer_handle, display_id, enable_blending));
109}
109 110
110 const auto layer_id = m_nvnflinger.CreateLayer(display_id); 111void IManagerDisplayService::DestroySharedLayerSession(Kernel::KProcess* owner_process) {
111 if (!layer_id) { 112 m_container->GetSharedBufferManager()->DestroySession(owner_process);
112 LOG_ERROR(Service_VI, "Layer not found! display={}", display_id); 113}
113 R_THROW(VI::ResultNotFound);
114 }
115 114
116 *out_layer_id = *layer_id; 115Result IManagerDisplayService::SetLayerBlending(bool enabled, u64 layer_id) {
117 R_SUCCEED(); 116 R_RETURN(m_container->SetLayerBlending(layer_id, enabled));
117}
118
119Result IManagerDisplayService::CreateManagedLayer(Out<u64> out_layer_id, u32 flags, u64 display_id,
120 AppletResourceUserId aruid) {
121 LOG_DEBUG(Service_VI, "called. flags={}, display={}, aruid={}", flags, display_id, aruid.pid);
122 R_RETURN(m_container->CreateManagedLayer(out_layer_id, display_id, aruid.pid));
123}
124
125Result IManagerDisplayService::DestroyManagedLayer(u64 layer_id) {
126 LOG_DEBUG(Service_VI, "called. layer_id={}", layer_id);
127 R_RETURN(m_container->DestroyManagedLayer(layer_id));
118} 128}
119 129
120Result IManagerDisplayService::AddToLayerStack(u32 stack_id, u64 layer_id) { 130Result IManagerDisplayService::AddToLayerStack(u32 stack_id, u64 layer_id) {
@@ -123,8 +133,8 @@ Result IManagerDisplayService::AddToLayerStack(u32 stack_id, u64 layer_id) {
123} 133}
124 134
125Result IManagerDisplayService::SetLayerVisibility(bool visible, u64 layer_id) { 135Result IManagerDisplayService::SetLayerVisibility(bool visible, u64 layer_id) {
126 LOG_WARNING(Service_VI, "(STUBBED) called, layer_id={}, visible={}", layer_id, visible); 136 LOG_DEBUG(Service_VI, "called, layer_id={}, visible={}", layer_id, visible);
127 R_SUCCEED(); 137 R_RETURN(m_container->SetLayerVisibility(layer_id, visible));
128} 138}
129 139
130} // namespace Service::VI 140} // namespace Service::VI
diff --git a/src/core/hle/service/vi/manager_display_service.h b/src/core/hle/service/vi/manager_display_service.h
index 60e646ee0..b1bdf7f41 100644
--- a/src/core/hle/service/vi/manager_display_service.h
+++ b/src/core/hle/service/vi/manager_display_service.h
@@ -4,21 +4,34 @@
4#include "core/hle/service/cmif_types.h" 4#include "core/hle/service/cmif_types.h"
5#include "core/hle/service/service.h" 5#include "core/hle/service/service.h"
6 6
7namespace Kernel {
8class KProcess;
9}
10
7namespace Service::VI { 11namespace Service::VI {
8 12
13class Container;
14
9class IManagerDisplayService final : public ServiceFramework<IManagerDisplayService> { 15class IManagerDisplayService final : public ServiceFramework<IManagerDisplayService> {
10public: 16public:
11 explicit IManagerDisplayService(Core::System& system_, Nvnflinger::Nvnflinger& nvnflinger); 17 explicit IManagerDisplayService(Core::System& system_, std::shared_ptr<Container> container);
12 ~IManagerDisplayService() override; 18 ~IManagerDisplayService() override;
13 19
14private: 20 Result CreateSharedLayerSession(Kernel::KProcess* owner_process, u64* out_buffer_id,
15 Result CreateManagedLayer(Out<u64> out_layer_id, u32 unknown, u64 display_id, 21 u64* out_layer_handle, u64 display_id, bool enable_blending);
22 void DestroySharedLayerSession(Kernel::KProcess* owner_process);
23
24 Result SetLayerBlending(bool enabled, u64 layer_id);
25
26public:
27 Result CreateManagedLayer(Out<u64> out_layer_id, u32 flags, u64 display_id,
16 AppletResourceUserId aruid); 28 AppletResourceUserId aruid);
29 Result DestroyManagedLayer(u64 layer_id);
17 Result AddToLayerStack(u32 stack_id, u64 layer_id); 30 Result AddToLayerStack(u32 stack_id, u64 layer_id);
18 Result SetLayerVisibility(bool visible, u64 layer_id); 31 Result SetLayerVisibility(bool visible, u64 layer_id);
19 32
20private: 33private:
21 Nvnflinger::Nvnflinger& m_nvnflinger; 34 const std::shared_ptr<Container> m_container;
22}; 35};
23 36
24} // namespace Service::VI 37} // namespace Service::VI
diff --git a/src/core/hle/service/vi/manager_root_service.cpp b/src/core/hle/service/vi/manager_root_service.cpp
index a7eee4f04..0f16a15b4 100644
--- a/src/core/hle/service/vi/manager_root_service.cpp
+++ b/src/core/hle/service/vi/manager_root_service.cpp
@@ -2,7 +2,9 @@
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include "core/hle/service/cmif_serialization.h" 4#include "core/hle/service/cmif_serialization.h"
5#include "core/hle/service/nvnflinger/hos_binder_driver.h"
5#include "core/hle/service/vi/application_display_service.h" 6#include "core/hle/service/vi/application_display_service.h"
7#include "core/hle/service/vi/container.h"
6#include "core/hle/service/vi/manager_root_service.h" 8#include "core/hle/service/vi/manager_root_service.h"
7#include "core/hle/service/vi/service_creator.h" 9#include "core/hle/service/vi/service_creator.h"
8#include "core/hle/service/vi/vi.h" 10#include "core/hle/service/vi/vi.h"
@@ -10,11 +12,9 @@
10 12
11namespace Service::VI { 13namespace Service::VI {
12 14
13IManagerRootService::IManagerRootService( 15IManagerRootService::IManagerRootService(Core::System& system_,
14 Core::System& system_, Nvnflinger::Nvnflinger& nvnflinger, 16 std::shared_ptr<Container> container)
15 Nvnflinger::HosBinderDriverServer& hos_binder_driver_server) 17 : ServiceFramework{system_, "vi:m"}, m_container{std::move(container)} {
16 : ServiceFramework{system_, "vi:m"}, m_nvnflinger{nvnflinger}, m_hos_binder_driver_server{
17 hos_binder_driver_server} {
18 static const FunctionInfo functions[] = { 18 static const FunctionInfo functions[] = {
19 {2, C<&IManagerRootService::GetDisplayService>, "GetDisplayService"}, 19 {2, C<&IManagerRootService::GetDisplayService>, "GetDisplayService"},
20 {3, nullptr, "GetDisplayServiceWithProxyNameExchange"}, 20 {3, nullptr, "GetDisplayServiceWithProxyNameExchange"},
@@ -31,8 +31,8 @@ IManagerRootService::~IManagerRootService() = default;
31Result IManagerRootService::GetDisplayService( 31Result IManagerRootService::GetDisplayService(
32 Out<SharedPointer<IApplicationDisplayService>> out_application_display_service, Policy policy) { 32 Out<SharedPointer<IApplicationDisplayService>> out_application_display_service, Policy policy) {
33 LOG_DEBUG(Service_VI, "called"); 33 LOG_DEBUG(Service_VI, "called");
34 R_RETURN(GetApplicationDisplayService(out_application_display_service, system, m_nvnflinger, 34 R_RETURN(GetApplicationDisplayService(out_application_display_service, system, m_container,
35 m_hos_binder_driver_server, Permission::Manager, policy)); 35 Permission::Manager, policy));
36} 36}
37 37
38} // namespace Service::VI 38} // namespace Service::VI
diff --git a/src/core/hle/service/vi/manager_root_service.h b/src/core/hle/service/vi/manager_root_service.h
index e6cb77aeb..77cd32869 100644
--- a/src/core/hle/service/vi/manager_root_service.h
+++ b/src/core/hle/service/vi/manager_root_service.h
@@ -10,29 +10,23 @@ namespace Core {
10class System; 10class System;
11} 11}
12 12
13namespace Service::Nvnflinger {
14class HosBinderDriverServer;
15class Nvnflinger;
16} // namespace Service::Nvnflinger
17
18namespace Service::VI { 13namespace Service::VI {
19 14
15class Container;
20class IApplicationDisplayService; 16class IApplicationDisplayService;
21enum class Policy : u32; 17enum class Policy : u32;
22 18
23class IManagerRootService final : public ServiceFramework<IManagerRootService> { 19class IManagerRootService final : public ServiceFramework<IManagerRootService> {
24public: 20public:
25 explicit IManagerRootService(Core::System& system_, Nvnflinger::Nvnflinger& nvnflinger, 21 explicit IManagerRootService(Core::System& system_, std::shared_ptr<Container> container);
26 Nvnflinger::HosBinderDriverServer& hos_binder_driver_server);
27 ~IManagerRootService() override; 22 ~IManagerRootService() override;
28 23
29private:
30 Result GetDisplayService( 24 Result GetDisplayService(
31 Out<SharedPointer<IApplicationDisplayService>> out_application_display_service, 25 Out<SharedPointer<IApplicationDisplayService>> out_application_display_service,
32 Policy policy); 26 Policy policy);
33 27
34 Nvnflinger::Nvnflinger& m_nvnflinger; 28private:
35 Nvnflinger::HosBinderDriverServer& m_hos_binder_driver_server; 29 const std::shared_ptr<Container> m_container;
36}; 30};
37 31
38} // namespace Service::VI 32} // namespace Service::VI
diff --git a/src/core/hle/service/vi/service_creator.cpp b/src/core/hle/service/vi/service_creator.cpp
index 1de9d61a4..2b8e5f957 100644
--- a/src/core/hle/service/vi/service_creator.cpp
+++ b/src/core/hle/service/vi/service_creator.cpp
@@ -22,8 +22,7 @@ static bool IsValidServiceAccess(Permission permission, Policy policy) {
22 22
23Result GetApplicationDisplayService( 23Result GetApplicationDisplayService(
24 std::shared_ptr<IApplicationDisplayService>* out_application_display_service, 24 std::shared_ptr<IApplicationDisplayService>* out_application_display_service,
25 Core::System& system, Nvnflinger::Nvnflinger& nvnflinger, 25 Core::System& system, std::shared_ptr<Container> container, Permission permission,
26 Nvnflinger::HosBinderDriverServer& hos_binder_driver_server, Permission permission,
27 Policy policy) { 26 Policy policy) {
28 27
29 if (!IsValidServiceAccess(permission, policy)) { 28 if (!IsValidServiceAccess(permission, policy)) {
@@ -32,7 +31,7 @@ Result GetApplicationDisplayService(
32 } 31 }
33 32
34 *out_application_display_service = 33 *out_application_display_service =
35 std::make_shared<IApplicationDisplayService>(system, nvnflinger, hos_binder_driver_server); 34 std::make_shared<IApplicationDisplayService>(system, std::move(container));
36 R_SUCCEED(); 35 R_SUCCEED();
37} 36}
38 37
diff --git a/src/core/hle/service/vi/service_creator.h b/src/core/hle/service/vi/service_creator.h
index 8963bcd26..c6ba1797d 100644
--- a/src/core/hle/service/vi/service_creator.h
+++ b/src/core/hle/service/vi/service_creator.h
@@ -11,23 +11,18 @@ namespace Core {
11class System; 11class System;
12} 12}
13 13
14namespace Service::Nvnflinger {
15class HosBinderDriverServer;
16class Nvnflinger;
17} // namespace Service::Nvnflinger
18
19union Result; 14union Result;
20 15
21namespace Service::VI { 16namespace Service::VI {
22 17
18class Container;
23class IApplicationDisplayService; 19class IApplicationDisplayService;
24enum class Permission; 20enum class Permission;
25enum class Policy : u32; 21enum class Policy : u32;
26 22
27Result GetApplicationDisplayService( 23Result GetApplicationDisplayService(
28 std::shared_ptr<IApplicationDisplayService>* out_application_display_service, 24 std::shared_ptr<IApplicationDisplayService>* out_application_display_service,
29 Core::System& system, Nvnflinger::Nvnflinger& nvnflinger, 25 Core::System& system, std::shared_ptr<Container> container, Permission permission,
30 Nvnflinger::HosBinderDriverServer& hos_binder_driver_server, Permission permission,
31 Policy policy); 26 Policy policy);
32 27
33} // namespace Service::VI 28} // namespace Service::VI
diff --git a/src/core/hle/service/nvnflinger/fb_share_buffer_manager.cpp b/src/core/hle/service/vi/shared_buffer_manager.cpp
index 90f7248a0..869b18961 100644
--- a/src/core/hle/service/nvnflinger/fb_share_buffer_manager.cpp
+++ b/src/core/hle/service/vi/shared_buffer_manager.cpp
@@ -9,15 +9,15 @@
9#include "core/hle/service/nvdrv/devices/nvmap.h" 9#include "core/hle/service/nvdrv/devices/nvmap.h"
10#include "core/hle/service/nvdrv/nvdrv.h" 10#include "core/hle/service/nvdrv/nvdrv.h"
11#include "core/hle/service/nvnflinger/buffer_queue_producer.h" 11#include "core/hle/service/nvnflinger/buffer_queue_producer.h"
12#include "core/hle/service/nvnflinger/fb_share_buffer_manager.h"
13#include "core/hle/service/nvnflinger/pixel_format.h" 12#include "core/hle/service/nvnflinger/pixel_format.h"
14#include "core/hle/service/nvnflinger/ui/graphic_buffer.h" 13#include "core/hle/service/nvnflinger/ui/graphic_buffer.h"
15#include "core/hle/service/vi/layer/vi_layer.h" 14#include "core/hle/service/vi/container.h"
15#include "core/hle/service/vi/shared_buffer_manager.h"
16#include "core/hle/service/vi/vi_results.h" 16#include "core/hle/service/vi/vi_results.h"
17#include "video_core/gpu.h" 17#include "video_core/gpu.h"
18#include "video_core/host1x/host1x.h" 18#include "video_core/host1x/host1x.h"
19 19
20namespace Service::Nvnflinger { 20namespace Service::VI {
21 21
22namespace { 22namespace {
23 23
@@ -26,7 +26,6 @@ Result AllocateSharedBufferMemory(std::unique_ptr<Kernel::KPageGroup>* out_page_
26 using Core::Memory::YUZU_PAGESIZE; 26 using Core::Memory::YUZU_PAGESIZE;
27 27
28 // Allocate memory for the system shared buffer. 28 // Allocate memory for the system shared buffer.
29 // FIXME: This memory belongs to vi's .data section.
30 auto& kernel = system.Kernel(); 29 auto& kernel = system.Kernel();
31 30
32 // Hold a temporary page group reference while we try to map it. 31 // Hold a temporary page group reference while we try to map it.
@@ -204,15 +203,15 @@ void MakeGraphicBuffer(android::BufferQueueProducer& producer, u32 slot, u32 han
204 203
205} // namespace 204} // namespace
206 205
207FbShareBufferManager::FbShareBufferManager(Core::System& system, Nvnflinger& flinger, 206SharedBufferManager::SharedBufferManager(Core::System& system, Container& container,
208 std::shared_ptr<Nvidia::Module> nvdrv) 207 std::shared_ptr<Nvidia::Module> nvdrv)
209 : m_system(system), m_flinger(flinger), m_nvdrv(std::move(nvdrv)) {} 208 : m_system(system), m_container(container), m_nvdrv(std::move(nvdrv)) {}
210 209
211FbShareBufferManager::~FbShareBufferManager() = default; 210SharedBufferManager::~SharedBufferManager() = default;
212 211
213Result FbShareBufferManager::Initialize(Kernel::KProcess* owner_process, u64* out_buffer_id, 212Result SharedBufferManager::CreateSession(Kernel::KProcess* owner_process, u64* out_buffer_id,
214 u64* out_layer_handle, u64 display_id, 213 u64* out_layer_handle, u64 display_id,
215 LayerBlending blending) { 214 bool enable_blending) {
216 std::scoped_lock lk{m_guard}; 215 std::scoped_lock lk{m_guard};
217 216
218 // Ensure we haven't already created. 217 // Ensure we haven't already created.
@@ -237,7 +236,7 @@ Result FbShareBufferManager::Initialize(Kernel::KProcess* owner_process, u64* ou
237 owner_process, m_system)); 236 owner_process, m_system));
238 237
239 // Create new session. 238 // Create new session.
240 auto [it, was_emplaced] = m_sessions.emplace(aruid, FbShareSession{}); 239 auto [it, was_emplaced] = m_sessions.emplace(aruid, SharedBufferSession{});
241 auto& session = it->second; 240 auto& session = it->second;
242 241
243 auto& container = m_nvdrv->GetContainer(); 242 auto& container = m_nvdrv->GetContainer();
@@ -249,17 +248,18 @@ Result FbShareBufferManager::Initialize(Kernel::KProcess* owner_process, u64* ou
249 session.nvmap_fd, map_address, SharedBufferSize)); 248 session.nvmap_fd, map_address, SharedBufferSize));
250 249
251 // Create and open a layer for the display. 250 // Create and open a layer for the display.
252 session.layer_id = m_flinger.CreateLayer(m_display_id, blending).value(); 251 s32 producer_binder_id;
253 m_flinger.OpenLayer(session.layer_id); 252 R_TRY(m_container.CreateStrayLayer(std::addressof(producer_binder_id),
253 std::addressof(session.layer_id), display_id));
254 254
255 // Get the layer. 255 // Configure blending.
256 VI::Layer* layer = m_flinger.FindLayer(m_display_id, session.layer_id); 256 R_ASSERT(m_container.SetLayerBlending(session.layer_id, enable_blending));
257 ASSERT(layer != nullptr);
258 257
259 // Get the producer and set preallocated buffers. 258 // Get the producer and set preallocated buffers.
260 auto& producer = layer->GetBufferQueue(); 259 std::shared_ptr<android::BufferQueueProducer> producer;
261 MakeGraphicBuffer(producer, 0, session.buffer_nvmap_handle); 260 R_TRY(m_container.GetLayerProducerHandle(std::addressof(producer), session.layer_id));
262 MakeGraphicBuffer(producer, 1, session.buffer_nvmap_handle); 261 MakeGraphicBuffer(*producer, 0, session.buffer_nvmap_handle);
262 MakeGraphicBuffer(*producer, 1, session.buffer_nvmap_handle);
263 263
264 // Assign outputs. 264 // Assign outputs.
265 *out_buffer_id = m_buffer_id; 265 *out_buffer_id = m_buffer_id;
@@ -269,7 +269,7 @@ Result FbShareBufferManager::Initialize(Kernel::KProcess* owner_process, u64* ou
269 R_SUCCEED(); 269 R_SUCCEED();
270} 270}
271 271
272void FbShareBufferManager::Finalize(Kernel::KProcess* owner_process) { 272void SharedBufferManager::DestroySession(Kernel::KProcess* owner_process) {
273 std::scoped_lock lk{m_guard}; 273 std::scoped_lock lk{m_guard};
274 274
275 if (m_buffer_id == 0) { 275 if (m_buffer_id == 0) {
@@ -285,7 +285,7 @@ void FbShareBufferManager::Finalize(Kernel::KProcess* owner_process) {
285 auto& session = it->second; 285 auto& session = it->second;
286 286
287 // Destroy the layer. 287 // Destroy the layer.
288 m_flinger.DestroyLayer(session.layer_id); 288 R_ASSERT(m_container.DestroyStrayLayer(session.layer_id));
289 289
290 // Close nvmap handle. 290 // Close nvmap handle.
291 FreeHandle(session.buffer_nvmap_handle, *m_nvdrv, session.nvmap_fd); 291 FreeHandle(session.buffer_nvmap_handle, *m_nvdrv, session.nvmap_fd);
@@ -301,11 +301,11 @@ void FbShareBufferManager::Finalize(Kernel::KProcess* owner_process) {
301 m_sessions.erase(it); 301 m_sessions.erase(it);
302} 302}
303 303
304Result FbShareBufferManager::GetSharedBufferMemoryHandleId(u64* out_buffer_size, 304Result SharedBufferManager::GetSharedBufferMemoryHandleId(u64* out_buffer_size,
305 s32* out_nvmap_handle, 305 s32* out_nvmap_handle,
306 SharedMemoryPoolLayout* out_pool_layout, 306 SharedMemoryPoolLayout* out_pool_layout,
307 u64 buffer_id, 307 u64 buffer_id,
308 u64 applet_resource_user_id) { 308 u64 applet_resource_user_id) {
309 std::scoped_lock lk{m_guard}; 309 std::scoped_lock lk{m_guard};
310 310
311 R_UNLESS(m_buffer_id > 0, VI::ResultNotFound); 311 R_UNLESS(m_buffer_id > 0, VI::ResultNotFound);
@@ -319,36 +319,20 @@ Result FbShareBufferManager::GetSharedBufferMemoryHandleId(u64* out_buffer_size,
319 R_SUCCEED(); 319 R_SUCCEED();
320} 320}
321 321
322Result FbShareBufferManager::GetLayerFromId(VI::Layer** out_layer, u64 layer_id) { 322Result SharedBufferManager::AcquireSharedFrameBuffer(android::Fence* out_fence,
323 // Ensure the layer id is valid. 323 std::array<s32, 4>& out_slot_indexes,
324 R_UNLESS(layer_id > 0, VI::ResultNotFound); 324 s64* out_target_slot, u64 layer_id) {
325
326 // Get the layer.
327 VI::Layer* layer = m_flinger.FindLayer(m_display_id, layer_id);
328 R_UNLESS(layer != nullptr, VI::ResultNotFound);
329
330 // We succeeded.
331 *out_layer = layer;
332 R_SUCCEED();
333}
334
335Result FbShareBufferManager::AcquireSharedFrameBuffer(android::Fence* out_fence,
336 std::array<s32, 4>& out_slot_indexes,
337 s64* out_target_slot, u64 layer_id) {
338 std::scoped_lock lk{m_guard}; 325 std::scoped_lock lk{m_guard};
339 326
340 // Get the layer.
341 VI::Layer* layer;
342 R_TRY(this->GetLayerFromId(std::addressof(layer), layer_id));
343
344 // Get the producer. 327 // Get the producer.
345 auto& producer = layer->GetBufferQueue(); 328 std::shared_ptr<android::BufferQueueProducer> producer;
329 R_TRY(m_container.GetLayerProducerHandle(std::addressof(producer), layer_id));
346 330
347 // Get the next buffer from the producer. 331 // Get the next buffer from the producer.
348 s32 slot; 332 s32 slot;
349 R_UNLESS(producer.DequeueBuffer(std::addressof(slot), out_fence, SharedBufferAsync != 0, 333 R_UNLESS(producer->DequeueBuffer(std::addressof(slot), out_fence, SharedBufferAsync != 0,
350 SharedBufferWidth, SharedBufferHeight, 334 SharedBufferWidth, SharedBufferHeight,
351 SharedBufferBlockLinearFormat, 0) == android::Status::NoError, 335 SharedBufferBlockLinearFormat, 0) == android::Status::NoError,
352 VI::ResultOperationFailed); 336 VI::ResultOperationFailed);
353 337
354 // Assign remaining outputs. 338 // Assign remaining outputs.
@@ -359,27 +343,24 @@ Result FbShareBufferManager::AcquireSharedFrameBuffer(android::Fence* out_fence,
359 R_SUCCEED(); 343 R_SUCCEED();
360} 344}
361 345
362Result FbShareBufferManager::PresentSharedFrameBuffer(android::Fence fence, 346Result SharedBufferManager::PresentSharedFrameBuffer(android::Fence fence,
363 Common::Rectangle<s32> crop_region, 347 Common::Rectangle<s32> crop_region,
364 u32 transform, s32 swap_interval, 348 u32 transform, s32 swap_interval, u64 layer_id,
365 u64 layer_id, s64 slot) { 349 s64 slot) {
366 std::scoped_lock lk{m_guard}; 350 std::scoped_lock lk{m_guard};
367 351
368 // Get the layer.
369 VI::Layer* layer;
370 R_TRY(this->GetLayerFromId(std::addressof(layer), layer_id));
371
372 // Get the producer. 352 // Get the producer.
373 auto& producer = layer->GetBufferQueue(); 353 std::shared_ptr<android::BufferQueueProducer> producer;
354 R_TRY(m_container.GetLayerProducerHandle(std::addressof(producer), layer_id));
374 355
375 // Request to queue the buffer. 356 // Request to queue the buffer.
376 std::shared_ptr<android::GraphicBuffer> buffer; 357 std::shared_ptr<android::GraphicBuffer> buffer;
377 R_UNLESS(producer.RequestBuffer(static_cast<s32>(slot), std::addressof(buffer)) == 358 R_UNLESS(producer->RequestBuffer(static_cast<s32>(slot), std::addressof(buffer)) ==
378 android::Status::NoError, 359 android::Status::NoError,
379 VI::ResultOperationFailed); 360 VI::ResultOperationFailed);
380 361
381 ON_RESULT_FAILURE { 362 ON_RESULT_FAILURE {
382 producer.CancelBuffer(static_cast<s32>(slot), fence); 363 producer->CancelBuffer(static_cast<s32>(slot), fence);
383 }; 364 };
384 365
385 // Queue the buffer to the producer. 366 // Queue the buffer to the producer.
@@ -389,7 +370,7 @@ Result FbShareBufferManager::PresentSharedFrameBuffer(android::Fence fence,
389 input.fence = fence; 370 input.fence = fence;
390 input.transform = static_cast<android::NativeWindowTransform>(transform); 371 input.transform = static_cast<android::NativeWindowTransform>(transform);
391 input.swap_interval = swap_interval; 372 input.swap_interval = swap_interval;
392 R_UNLESS(producer.QueueBuffer(static_cast<s32>(slot), input, std::addressof(output)) == 373 R_UNLESS(producer->QueueBuffer(static_cast<s32>(slot), input, std::addressof(output)) ==
393 android::Status::NoError, 374 android::Status::NoError,
394 VI::ResultOperationFailed); 375 VI::ResultOperationFailed);
395 376
@@ -397,25 +378,36 @@ Result FbShareBufferManager::PresentSharedFrameBuffer(android::Fence fence,
397 R_SUCCEED(); 378 R_SUCCEED();
398} 379}
399 380
400Result FbShareBufferManager::GetSharedFrameBufferAcquirableEvent(Kernel::KReadableEvent** out_event, 381Result SharedBufferManager::CancelSharedFrameBuffer(u64 layer_id, s64 slot) {
401 u64 layer_id) {
402 std::scoped_lock lk{m_guard}; 382 std::scoped_lock lk{m_guard};
403 383
404 // Get the layer. 384 // Get the producer.
405 VI::Layer* layer; 385 std::shared_ptr<android::BufferQueueProducer> producer;
406 R_TRY(this->GetLayerFromId(std::addressof(layer), layer_id)); 386 R_TRY(m_container.GetLayerProducerHandle(std::addressof(producer), layer_id));
387
388 // Cancel.
389 producer->CancelBuffer(static_cast<s32>(slot), android::Fence::NoFence());
390
391 // We succeeded.
392 R_SUCCEED();
393}
394
395Result SharedBufferManager::GetSharedFrameBufferAcquirableEvent(Kernel::KReadableEvent** out_event,
396 u64 layer_id) {
397 std::scoped_lock lk{m_guard};
407 398
408 // Get the producer. 399 // Get the producer.
409 auto& producer = layer->GetBufferQueue(); 400 std::shared_ptr<android::BufferQueueProducer> producer;
401 R_TRY(m_container.GetLayerProducerHandle(std::addressof(producer), layer_id));
410 402
411 // Set the event. 403 // Set the event.
412 *out_event = std::addressof(producer.GetNativeHandle()); 404 *out_event = producer->GetNativeHandle({});
413 405
414 // We succeeded. 406 // We succeeded.
415 R_SUCCEED(); 407 R_SUCCEED();
416} 408}
417 409
418Result FbShareBufferManager::WriteAppletCaptureBuffer(bool* out_was_written, s32* out_layer_index) { 410Result SharedBufferManager::WriteAppletCaptureBuffer(bool* out_was_written, s32* out_layer_index) {
419 std::vector<u8> capture_buffer(m_system.GPU().GetAppletCaptureBuffer()); 411 std::vector<u8> capture_buffer(m_system.GPU().GetAppletCaptureBuffer());
420 Common::ScratchBuffer<u32> scratch; 412 Common::ScratchBuffer<u32> scratch;
421 413
@@ -444,4 +436,4 @@ Result FbShareBufferManager::WriteAppletCaptureBuffer(bool* out_was_written, s32
444 R_SUCCEED(); 436 R_SUCCEED();
445} 437}
446 438
447} // namespace Service::Nvnflinger 439} // namespace Service::VI
diff --git a/src/core/hle/service/nvnflinger/fb_share_buffer_manager.h b/src/core/hle/service/vi/shared_buffer_manager.h
index b79a7d23a..7c9bb7199 100644
--- a/src/core/hle/service/nvnflinger/fb_share_buffer_manager.h
+++ b/src/core/hle/service/vi/shared_buffer_manager.h
@@ -8,15 +8,27 @@
8#include "common/math_util.h" 8#include "common/math_util.h"
9#include "core/hle/service/nvdrv/core/container.h" 9#include "core/hle/service/nvdrv/core/container.h"
10#include "core/hle/service/nvdrv/nvdata.h" 10#include "core/hle/service/nvdrv/nvdata.h"
11#include "core/hle/service/nvnflinger/hwc_layer.h"
12#include "core/hle/service/nvnflinger/nvnflinger.h" 11#include "core/hle/service/nvnflinger/nvnflinger.h"
13#include "core/hle/service/nvnflinger/ui/fence.h" 12#include "core/hle/service/nvnflinger/ui/fence.h"
14 13
15namespace Kernel { 14namespace Kernel {
16class KPageGroup; 15class KPageGroup;
16class KReadableEvent;
17} // namespace Kernel
18
19namespace Service::android {
20class BufferQueueProducer;
21}
22
23namespace Service::Nvidia {
24class Module;
17} 25}
18 26
19namespace Service::Nvnflinger { 27union Result;
28
29namespace Service::VI {
30
31class Container;
20 32
21struct SharedMemorySlot { 33struct SharedMemorySlot {
22 u64 buffer_offset; 34 u64 buffer_offset;
@@ -32,17 +44,17 @@ struct SharedMemoryPoolLayout {
32}; 44};
33static_assert(sizeof(SharedMemoryPoolLayout) == 0x188, "SharedMemoryPoolLayout has wrong size"); 45static_assert(sizeof(SharedMemoryPoolLayout) == 0x188, "SharedMemoryPoolLayout has wrong size");
34 46
35struct FbShareSession; 47struct SharedBufferSession;
36 48
37class FbShareBufferManager final { 49class SharedBufferManager final {
38public: 50public:
39 explicit FbShareBufferManager(Core::System& system, Nvnflinger& flinger, 51 explicit SharedBufferManager(Core::System& system, Container& container,
40 std::shared_ptr<Nvidia::Module> nvdrv); 52 std::shared_ptr<Nvidia::Module> nvdrv);
41 ~FbShareBufferManager(); 53 ~SharedBufferManager();
42 54
43 Result Initialize(Kernel::KProcess* owner_process, u64* out_buffer_id, u64* out_layer_handle, 55 Result CreateSession(Kernel::KProcess* owner_process, u64* out_buffer_id, u64* out_layer_handle,
44 u64 display_id, LayerBlending blending); 56 u64 display_id, bool enable_blending);
45 void Finalize(Kernel::KProcess* owner_process); 57 void DestroySession(Kernel::KProcess* owner_process);
46 58
47 Result GetSharedBufferMemoryHandleId(u64* out_buffer_size, s32* out_nvmap_handle, 59 Result GetSharedBufferMemoryHandleId(u64* out_buffer_size, s32* out_nvmap_handle,
48 SharedMemoryPoolLayout* out_pool_layout, u64 buffer_id, 60 SharedMemoryPoolLayout* out_pool_layout, u64 buffer_id,
@@ -51,32 +63,30 @@ public:
51 s64* out_target_slot, u64 layer_id); 63 s64* out_target_slot, u64 layer_id);
52 Result PresentSharedFrameBuffer(android::Fence fence, Common::Rectangle<s32> crop_region, 64 Result PresentSharedFrameBuffer(android::Fence fence, Common::Rectangle<s32> crop_region,
53 u32 transform, s32 swap_interval, u64 layer_id, s64 slot); 65 u32 transform, s32 swap_interval, u64 layer_id, s64 slot);
66 Result CancelSharedFrameBuffer(u64 layer_id, s64 slot);
54 Result GetSharedFrameBufferAcquirableEvent(Kernel::KReadableEvent** out_event, u64 layer_id); 67 Result GetSharedFrameBufferAcquirableEvent(Kernel::KReadableEvent** out_event, u64 layer_id);
55 68
56 Result WriteAppletCaptureBuffer(bool* out_was_written, s32* out_layer_index); 69 Result WriteAppletCaptureBuffer(bool* out_was_written, s32* out_layer_index);
57 70
58private: 71private:
59 Result GetLayerFromId(VI::Layer** out_layer, u64 layer_id);
60
61private:
62 u64 m_next_buffer_id = 1; 72 u64 m_next_buffer_id = 1;
63 u64 m_display_id = 0; 73 u64 m_display_id = 0;
64 u64 m_buffer_id = 0; 74 u64 m_buffer_id = 0;
65 SharedMemoryPoolLayout m_pool_layout = {}; 75 SharedMemoryPoolLayout m_pool_layout = {};
66 std::map<u64, FbShareSession> m_sessions; 76 std::map<u64, SharedBufferSession> m_sessions;
67 std::unique_ptr<Kernel::KPageGroup> m_buffer_page_group; 77 std::unique_ptr<Kernel::KPageGroup> m_buffer_page_group;
68 78
69 std::mutex m_guard; 79 std::mutex m_guard;
70 Core::System& m_system; 80 Core::System& m_system;
71 Nvnflinger& m_flinger; 81 Container& m_container;
72 std::shared_ptr<Nvidia::Module> m_nvdrv; 82 const std::shared_ptr<Nvidia::Module> m_nvdrv;
73}; 83};
74 84
75struct FbShareSession { 85struct SharedBufferSession {
76 Nvidia::DeviceFD nvmap_fd = {}; 86 Nvidia::DeviceFD nvmap_fd = {};
77 Nvidia::NvCore::SessionId session_id = {}; 87 Nvidia::NvCore::SessionId session_id = {};
78 u64 layer_id = {}; 88 u64 layer_id = {};
79 u32 buffer_nvmap_handle = 0; 89 u32 buffer_nvmap_handle = 0;
80}; 90};
81 91
82} // namespace Service::Nvnflinger 92} // namespace Service::VI
diff --git a/src/core/hle/service/vi/system_display_service.cpp b/src/core/hle/service/vi/system_display_service.cpp
index 1e1cfc817..c3c50b07b 100644
--- a/src/core/hle/service/vi/system_display_service.cpp
+++ b/src/core/hle/service/vi/system_display_service.cpp
@@ -3,15 +3,15 @@
3 3
4#include "common/settings.h" 4#include "common/settings.h"
5#include "core/hle/service/cmif_serialization.h" 5#include "core/hle/service/cmif_serialization.h"
6#include "core/hle/service/nvnflinger/fb_share_buffer_manager.h" 6#include "core/hle/service/vi/container.h"
7#include "core/hle/service/vi/system_display_service.h" 7#include "core/hle/service/vi/system_display_service.h"
8#include "core/hle/service/vi/vi_types.h" 8#include "core/hle/service/vi/vi_types.h"
9 9
10namespace Service::VI { 10namespace Service::VI {
11 11
12ISystemDisplayService::ISystemDisplayService(Core::System& system_, 12ISystemDisplayService::ISystemDisplayService(Core::System& system_,
13 Nvnflinger::Nvnflinger& nvnflinger) 13 std::shared_ptr<Container> container)
14 : ServiceFramework{system_, "ISystemDisplayService"}, m_nvnflinger{nvnflinger} { 14 : ServiceFramework{system_, "ISystemDisplayService"}, m_container{std::move(container)} {
15 // clang-format off 15 // clang-format off
16 static const FunctionInfo functions[] = { 16 static const FunctionInfo functions[] = {
17 {1200, nullptr, "GetZOrderCountMin"}, 17 {1200, nullptr, "GetZOrderCountMin"},
@@ -29,7 +29,7 @@ ISystemDisplayService::ISystemDisplayService(Core::System& system_,
29 {2400, nullptr, "OpenIndirectLayer"}, 29 {2400, nullptr, "OpenIndirectLayer"},
30 {2401, nullptr, "CloseIndirectLayer"}, 30 {2401, nullptr, "CloseIndirectLayer"},
31 {2402, nullptr, "FlipIndirectLayer"}, 31 {2402, nullptr, "FlipIndirectLayer"},
32 {3000, nullptr, "ListDisplayModes"}, 32 {3000, C<&ISystemDisplayService::ListDisplayModes>, "ListDisplayModes"},
33 {3001, nullptr, "ListDisplayRgbRanges"}, 33 {3001, nullptr, "ListDisplayRgbRanges"},
34 {3002, nullptr, "ListDisplayContentTypes"}, 34 {3002, nullptr, "ListDisplayContentTypes"},
35 {3200, C<&ISystemDisplayService::GetDisplayMode>, "GetDisplayMode"}, 35 {3200, C<&ISystemDisplayService::GetDisplayMode>, "GetDisplayMode"},
@@ -59,7 +59,7 @@ ISystemDisplayService::ISystemDisplayService(Core::System& system_,
59 {8255, C<&ISystemDisplayService::PresentSharedFrameBuffer>, "PresentSharedFrameBuffer"}, 59 {8255, C<&ISystemDisplayService::PresentSharedFrameBuffer>, "PresentSharedFrameBuffer"},
60 {8256, C<&ISystemDisplayService::GetSharedFrameBufferAcquirableEvent>, "GetSharedFrameBufferAcquirableEvent"}, 60 {8256, C<&ISystemDisplayService::GetSharedFrameBufferAcquirableEvent>, "GetSharedFrameBufferAcquirableEvent"},
61 {8257, nullptr, "FillSharedFrameBufferColor"}, 61 {8257, nullptr, "FillSharedFrameBufferColor"},
62 {8258, nullptr, "CancelSharedFrameBuffer"}, 62 {8258, C<&ISystemDisplayService::CancelSharedFrameBuffer>, "CancelSharedFrameBuffer"},
63 {9000, nullptr, "GetDp2hdmiController"}, 63 {9000, nullptr, "GetDp2hdmiController"},
64 }; 64 };
65 // clang-format on 65 // clang-format on
@@ -80,31 +80,50 @@ Result ISystemDisplayService::SetLayerVisibility(bool visible, u64 layer_id) {
80 R_SUCCEED(); 80 R_SUCCEED();
81} 81}
82 82
83Result ISystemDisplayService::GetDisplayMode(Out<u32> out_width, Out<u32> out_height, 83Result ISystemDisplayService::ListDisplayModes(
84 Out<f32> out_refresh_rate, Out<u32> out_unknown) { 84 Out<u64> out_count, u64 display_id,
85 LOG_WARNING(Service_VI, "(STUBBED) called"); 85 OutArray<DisplayMode, BufferAttr_HipcMapAlias> out_display_modes) {
86 LOG_WARNING(Service_VI, "(STUBBED) called, display_id={}", display_id);
87
88 if (!out_display_modes.empty()) {
89 out_display_modes[0] = {
90 .width = 1920,
91 .height = 1080,
92 .refresh_rate = 60.f,
93 .unknown = {},
94 };
95 *out_count = 1;
96 } else {
97 *out_count = 0;
98 }
99
100 R_SUCCEED();
101}
102
103Result ISystemDisplayService::GetDisplayMode(Out<DisplayMode> out_display_mode, u64 display_id) {
104 LOG_WARNING(Service_VI, "(STUBBED) called, display_id={}", display_id);
86 105
87 if (Settings::IsDockedMode()) { 106 if (Settings::IsDockedMode()) {
88 *out_width = static_cast<u32>(DisplayResolution::DockedWidth); 107 out_display_mode->width = static_cast<u32>(DisplayResolution::DockedWidth);
89 *out_height = static_cast<u32>(DisplayResolution::DockedHeight); 108 out_display_mode->height = static_cast<u32>(DisplayResolution::DockedHeight);
90 } else { 109 } else {
91 *out_width = static_cast<u32>(DisplayResolution::UndockedWidth); 110 out_display_mode->width = static_cast<u32>(DisplayResolution::UndockedWidth);
92 *out_height = static_cast<u32>(DisplayResolution::UndockedHeight); 111 out_display_mode->height = static_cast<u32>(DisplayResolution::UndockedHeight);
93 } 112 }
94 113
95 *out_refresh_rate = 60.f; // This wouldn't seem to be correct for 30 fps games. 114 out_display_mode->refresh_rate = 60.f; // This wouldn't seem to be correct for 30 fps games.
96 *out_unknown = 0; 115 out_display_mode->unknown = 0;
97 116
98 R_SUCCEED(); 117 R_SUCCEED();
99} 118}
100 119
101Result ISystemDisplayService::GetSharedBufferMemoryHandleId( 120Result ISystemDisplayService::GetSharedBufferMemoryHandleId(
102 Out<s32> out_nvmap_handle, Out<u64> out_size, 121 Out<s32> out_nvmap_handle, Out<u64> out_size,
103 OutLargeData<Nvnflinger::SharedMemoryPoolLayout, BufferAttr_HipcMapAlias> out_pool_layout, 122 OutLargeData<SharedMemoryPoolLayout, BufferAttr_HipcMapAlias> out_pool_layout, u64 buffer_id,
104 u64 buffer_id, ClientAppletResourceUserId aruid) { 123 ClientAppletResourceUserId aruid) {
105 LOG_INFO(Service_VI, "called. buffer_id={}, aruid={:#x}", buffer_id, aruid.pid); 124 LOG_INFO(Service_VI, "called. buffer_id={}, aruid={:#x}", buffer_id, aruid.pid);
106 125
107 R_RETURN(m_nvnflinger.GetSystemBufferManager().GetSharedBufferMemoryHandleId( 126 R_RETURN(m_container->GetSharedBufferManager()->GetSharedBufferMemoryHandleId(
108 out_size, out_nvmap_handle, out_pool_layout, buffer_id, aruid.pid)); 127 out_size, out_nvmap_handle, out_pool_layout, buffer_id, aruid.pid));
109} 128}
110 129
@@ -122,7 +141,7 @@ Result ISystemDisplayService::AcquireSharedFrameBuffer(Out<android::Fence> out_f
122 Out<std::array<s32, 4>> out_slots, 141 Out<std::array<s32, 4>> out_slots,
123 Out<s64> out_target_slot, u64 layer_id) { 142 Out<s64> out_target_slot, u64 layer_id) {
124 LOG_DEBUG(Service_VI, "called"); 143 LOG_DEBUG(Service_VI, "called");
125 R_RETURN(m_nvnflinger.GetSystemBufferManager().AcquireSharedFrameBuffer( 144 R_RETURN(m_container->GetSharedBufferManager()->AcquireSharedFrameBuffer(
126 out_fence, *out_slots, out_target_slot, layer_id)); 145 out_fence, *out_slots, out_target_slot, layer_id));
127} 146}
128 147
@@ -131,15 +150,20 @@ Result ISystemDisplayService::PresentSharedFrameBuffer(android::Fence fence,
131 u32 window_transform, s32 swap_interval, 150 u32 window_transform, s32 swap_interval,
132 u64 layer_id, s64 surface_id) { 151 u64 layer_id, s64 surface_id) {
133 LOG_DEBUG(Service_VI, "called"); 152 LOG_DEBUG(Service_VI, "called");
134 R_RETURN(m_nvnflinger.GetSystemBufferManager().PresentSharedFrameBuffer( 153 R_RETURN(m_container->GetSharedBufferManager()->PresentSharedFrameBuffer(
135 fence, crop_region, window_transform, swap_interval, layer_id, surface_id)); 154 fence, crop_region, window_transform, swap_interval, layer_id, surface_id));
136} 155}
137 156
138Result ISystemDisplayService::GetSharedFrameBufferAcquirableEvent( 157Result ISystemDisplayService::GetSharedFrameBufferAcquirableEvent(
139 OutCopyHandle<Kernel::KReadableEvent> out_event, u64 layer_id) { 158 OutCopyHandle<Kernel::KReadableEvent> out_event, u64 layer_id) {
140 LOG_DEBUG(Service_VI, "called"); 159 LOG_DEBUG(Service_VI, "called");
141 R_RETURN(m_nvnflinger.GetSystemBufferManager().GetSharedFrameBufferAcquirableEvent(out_event, 160 R_RETURN(m_container->GetSharedBufferManager()->GetSharedFrameBufferAcquirableEvent(out_event,
142 layer_id)); 161 layer_id));
162}
163
164Result ISystemDisplayService::CancelSharedFrameBuffer(u64 layer_id, s64 slot) {
165 LOG_DEBUG(Service_VI, "called");
166 R_RETURN(m_container->GetSharedBufferManager()->CancelSharedFrameBuffer(layer_id, slot));
143} 167}
144 168
145} // namespace Service::VI 169} // namespace Service::VI
diff --git a/src/core/hle/service/vi/system_display_service.h b/src/core/hle/service/vi/system_display_service.h
index cfcb196fd..7228d826e 100644
--- a/src/core/hle/service/vi/system_display_service.h
+++ b/src/core/hle/service/vi/system_display_service.h
@@ -5,27 +5,28 @@
5#include "core/hle/service/cmif_types.h" 5#include "core/hle/service/cmif_types.h"
6#include "core/hle/service/nvnflinger/ui/fence.h" 6#include "core/hle/service/nvnflinger/ui/fence.h"
7#include "core/hle/service/service.h" 7#include "core/hle/service/service.h"
8 8#include "core/hle/service/vi/shared_buffer_manager.h"
9namespace Service::Nvnflinger {
10struct SharedMemoryPoolLayout;
11}
12 9
13namespace Service::VI { 10namespace Service::VI {
11struct DisplayMode;
12
13class Container;
14 14
15class ISystemDisplayService final : public ServiceFramework<ISystemDisplayService> { 15class ISystemDisplayService final : public ServiceFramework<ISystemDisplayService> {
16public: 16public:
17 explicit ISystemDisplayService(Core::System& system_, Nvnflinger::Nvnflinger& nvnflinger); 17 explicit ISystemDisplayService(Core::System& system_, std::shared_ptr<Container> container);
18 ~ISystemDisplayService() override; 18 ~ISystemDisplayService() override;
19 19
20private: 20private:
21 Result SetLayerZ(u32 z_value, u64 layer_id); 21 Result SetLayerZ(u32 z_value, u64 layer_id);
22 Result SetLayerVisibility(bool visible, u64 layer_id); 22 Result SetLayerVisibility(bool visible, u64 layer_id);
23 Result GetDisplayMode(Out<u32> out_width, Out<u32> out_height, Out<f32> out_refresh_rate, 23 Result ListDisplayModes(Out<u64> out_count, u64 display_id,
24 Out<u32> out_unknown); 24 OutArray<DisplayMode, BufferAttr_HipcMapAlias> out_display_modes);
25 Result GetDisplayMode(Out<DisplayMode> out_display_mode, u64 display_id);
25 26
26 Result GetSharedBufferMemoryHandleId( 27 Result GetSharedBufferMemoryHandleId(
27 Out<s32> out_nvmap_handle, Out<u64> out_size, 28 Out<s32> out_nvmap_handle, Out<u64> out_size,
28 OutLargeData<Nvnflinger::SharedMemoryPoolLayout, BufferAttr_HipcMapAlias> out_pool_layout, 29 OutLargeData<SharedMemoryPoolLayout, BufferAttr_HipcMapAlias> out_pool_layout,
29 u64 buffer_id, ClientAppletResourceUserId aruid); 30 u64 buffer_id, ClientAppletResourceUserId aruid);
30 Result OpenSharedLayer(u64 layer_id); 31 Result OpenSharedLayer(u64 layer_id);
31 Result ConnectSharedLayer(u64 layer_id); 32 Result ConnectSharedLayer(u64 layer_id);
@@ -37,9 +38,10 @@ private:
37 Result PresentSharedFrameBuffer(android::Fence fence, Common::Rectangle<s32> crop_region, 38 Result PresentSharedFrameBuffer(android::Fence fence, Common::Rectangle<s32> crop_region,
38 u32 window_transform, s32 swap_interval, u64 layer_id, 39 u32 window_transform, s32 swap_interval, u64 layer_id,
39 s64 surface_id); 40 s64 surface_id);
41 Result CancelSharedFrameBuffer(u64 layer_id, s64 slot);
40 42
41private: 43private:
42 Nvnflinger::Nvnflinger& m_nvnflinger; 44 const std::shared_ptr<Container> m_container;
43}; 45};
44 46
45} // namespace Service::VI 47} // namespace Service::VI
diff --git a/src/core/hle/service/vi/system_root_service.cpp b/src/core/hle/service/vi/system_root_service.cpp
index 8789b4cfb..3489727d8 100644
--- a/src/core/hle/service/vi/system_root_service.cpp
+++ b/src/core/hle/service/vi/system_root_service.cpp
@@ -3,6 +3,7 @@
3 3
4#include "core/hle/service/cmif_serialization.h" 4#include "core/hle/service/cmif_serialization.h"
5#include "core/hle/service/vi/application_display_service.h" 5#include "core/hle/service/vi/application_display_service.h"
6#include "core/hle/service/vi/container.h"
6#include "core/hle/service/vi/service_creator.h" 7#include "core/hle/service/vi/service_creator.h"
7#include "core/hle/service/vi/system_root_service.h" 8#include "core/hle/service/vi/system_root_service.h"
8#include "core/hle/service/vi/vi.h" 9#include "core/hle/service/vi/vi.h"
@@ -10,10 +11,8 @@
10 11
11namespace Service::VI { 12namespace Service::VI {
12 13
13ISystemRootService::ISystemRootService(Core::System& system_, Nvnflinger::Nvnflinger& nvnflinger, 14ISystemRootService::ISystemRootService(Core::System& system_, std::shared_ptr<Container> container)
14 Nvnflinger::HosBinderDriverServer& hos_binder_driver_server) 15 : ServiceFramework{system_, "vi:s"}, m_container{std::move(container)} {
15 : ServiceFramework{system_, "vi:s"}, m_nvnflinger{nvnflinger}, m_hos_binder_driver_server{
16 hos_binder_driver_server} {
17 static const FunctionInfo functions[] = { 16 static const FunctionInfo functions[] = {
18 {1, C<&ISystemRootService::GetDisplayService>, "GetDisplayService"}, 17 {1, C<&ISystemRootService::GetDisplayService>, "GetDisplayService"},
19 {3, nullptr, "GetDisplayServiceWithProxyNameExchange"}, 18 {3, nullptr, "GetDisplayServiceWithProxyNameExchange"},
@@ -26,8 +25,8 @@ ISystemRootService::~ISystemRootService() = default;
26Result ISystemRootService::GetDisplayService( 25Result ISystemRootService::GetDisplayService(
27 Out<SharedPointer<IApplicationDisplayService>> out_application_display_service, Policy policy) { 26 Out<SharedPointer<IApplicationDisplayService>> out_application_display_service, Policy policy) {
28 LOG_DEBUG(Service_VI, "called"); 27 LOG_DEBUG(Service_VI, "called");
29 R_RETURN(GetApplicationDisplayService(out_application_display_service, system, m_nvnflinger, 28 R_RETURN(GetApplicationDisplayService(out_application_display_service, system, m_container,
30 m_hos_binder_driver_server, Permission::System, policy)); 29 Permission::System, policy));
31} 30}
32 31
33} // namespace Service::VI 32} // namespace Service::VI
diff --git a/src/core/hle/service/vi/system_root_service.h b/src/core/hle/service/vi/system_root_service.h
index 2c547faa5..9d5aa53d3 100644
--- a/src/core/hle/service/vi/system_root_service.h
+++ b/src/core/hle/service/vi/system_root_service.h
@@ -10,20 +10,15 @@ namespace Core {
10class System; 10class System;
11} 11}
12 12
13namespace Service::Nvnflinger {
14class HosBinderDriverServer;
15class Nvnflinger;
16} // namespace Service::Nvnflinger
17
18namespace Service::VI { 13namespace Service::VI {
19 14
15class Container;
20class IApplicationDisplayService; 16class IApplicationDisplayService;
21enum class Policy : u32; 17enum class Policy : u32;
22 18
23class ISystemRootService final : public ServiceFramework<ISystemRootService> { 19class ISystemRootService final : public ServiceFramework<ISystemRootService> {
24public: 20public:
25 explicit ISystemRootService(Core::System& system_, Nvnflinger::Nvnflinger& nvnflinger, 21 explicit ISystemRootService(Core::System& system_, std::shared_ptr<Container> container);
26 Nvnflinger::HosBinderDriverServer& hos_binder_driver_server);
27 ~ISystemRootService() override; 22 ~ISystemRootService() override;
28 23
29private: 24private:
@@ -31,8 +26,7 @@ private:
31 Out<SharedPointer<IApplicationDisplayService>> out_application_display_service, 26 Out<SharedPointer<IApplicationDisplayService>> out_application_display_service,
32 Policy policy); 27 Policy policy);
33 28
34 Nvnflinger::Nvnflinger& m_nvnflinger; 29 const std::shared_ptr<Container> m_container;
35 Nvnflinger::HosBinderDriverServer& m_hos_binder_driver_server;
36}; 30};
37 31
38} // namespace Service::VI 32} // namespace Service::VI
diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp
index 304e589b7..b388efaf6 100644
--- a/src/core/hle/service/vi/vi.cpp
+++ b/src/core/hle/service/vi/vi.cpp
@@ -1,25 +1,30 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include "core/core.h"
4#include "core/hle/service/server_manager.h" 5#include "core/hle/service/server_manager.h"
5#include "core/hle/service/vi/application_display_service.h"
6#include "core/hle/service/vi/application_root_service.h" 6#include "core/hle/service/vi/application_root_service.h"
7#include "core/hle/service/vi/container.h"
7#include "core/hle/service/vi/manager_root_service.h" 8#include "core/hle/service/vi/manager_root_service.h"
8#include "core/hle/service/vi/system_root_service.h" 9#include "core/hle/service/vi/system_root_service.h"
9#include "core/hle/service/vi/vi.h" 10#include "core/hle/service/vi/vi.h"
10 11
11namespace Service::VI { 12namespace Service::VI {
12 13
13void LoopProcess(Core::System& system, Nvnflinger::Nvnflinger& nvnflinger, 14void LoopProcess(Core::System& system, std::stop_token token) {
14 Nvnflinger::HosBinderDriverServer& hos_binder_driver_server) { 15 const auto container = std::make_shared<Container>(system);
16
15 auto server_manager = std::make_unique<ServerManager>(system); 17 auto server_manager = std::make_unique<ServerManager>(system);
16 18
17 server_manager->RegisterNamedService("vi:m", std::make_shared<IManagerRootService>( 19 server_manager->RegisterNamedService("vi:m",
18 system, nvnflinger, hos_binder_driver_server)); 20 std::make_shared<IManagerRootService>(system, container));
21 server_manager->RegisterNamedService("vi:s",
22 std::make_shared<ISystemRootService>(system, container));
19 server_manager->RegisterNamedService( 23 server_manager->RegisterNamedService(
20 "vi:s", std::make_shared<ISystemRootService>(system, nvnflinger, hos_binder_driver_server)); 24 "vi:u", std::make_shared<IApplicationRootService>(system, container));
21 server_manager->RegisterNamedService("vi:u", std::make_shared<IApplicationRootService>( 25
22 system, nvnflinger, hos_binder_driver_server)); 26 std::stop_callback cb(token, [=] { container->OnTerminate(); });
27
23 ServerManager::RunServer(std::move(server_manager)); 28 ServerManager::RunServer(std::move(server_manager));
24} 29}
25 30
diff --git a/src/core/hle/service/vi/vi.h b/src/core/hle/service/vi/vi.h
index 8e681370d..7c1f350d8 100644
--- a/src/core/hle/service/vi/vi.h
+++ b/src/core/hle/service/vi/vi.h
@@ -3,18 +3,14 @@
3 3
4#pragma once 4#pragma once
5 5
6#include "common/polyfill_thread.h"
7
6namespace Core { 8namespace Core {
7class System; 9class System;
8} 10}
9 11
10namespace Service::Nvnflinger {
11class HosBinderDriverServer;
12class Nvnflinger;
13} // namespace Service::Nvnflinger
14
15namespace Service::VI { 12namespace Service::VI {
16 13
17void LoopProcess(Core::System& system, Nvnflinger::Nvnflinger& nvnflinger, 14void LoopProcess(Core::System& system, std::stop_token token);
18 Nvnflinger::HosBinderDriverServer& hos_binder_driver_server);
19 15
20} // namespace Service::VI 16} // namespace Service::VI
diff --git a/src/core/hle/service/vi/vi_types.h b/src/core/hle/service/vi/vi_types.h
index 91e4b380c..95ff66358 100644
--- a/src/core/hle/service/vi/vi_types.h
+++ b/src/core/hle/service/vi/vi_types.h
@@ -66,9 +66,17 @@ struct DisplayInfo {
66}; 66};
67static_assert(sizeof(DisplayInfo) == 0x60, "DisplayInfo has wrong size"); 67static_assert(sizeof(DisplayInfo) == 0x60, "DisplayInfo has wrong size");
68 68
69struct DisplayMode {
70 u32 width;
71 u32 height;
72 f32 refresh_rate;
73 u32 unknown;
74};
75static_assert(sizeof(DisplayMode) == 0x10, "DisplayMode has wrong size");
76
69class NativeWindow final { 77class NativeWindow final {
70public: 78public:
71 constexpr explicit NativeWindow(u32 id_) : id{id_} {} 79 constexpr explicit NativeWindow(s32 id_) : id{static_cast<u64>(id_)} {}
72 constexpr explicit NativeWindow(const NativeWindow& other) = default; 80 constexpr explicit NativeWindow(const NativeWindow& other) = default;
73 81
74private: 82private:
diff --git a/src/core/hle/service/vi/vsync_manager.cpp b/src/core/hle/service/vi/vsync_manager.cpp
new file mode 100644
index 000000000..bdc4dfa96
--- /dev/null
+++ b/src/core/hle/service/vi/vsync_manager.cpp
@@ -0,0 +1,26 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/hle/service/os/event.h"
5#include "core/hle/service/vi/vsync_manager.h"
6
7namespace Service::VI {
8
9VsyncManager::VsyncManager() = default;
10VsyncManager::~VsyncManager() = default;
11
12void VsyncManager::SignalVsync() {
13 for (auto* event : m_vsync_events) {
14 event->Signal();
15 }
16}
17
18void VsyncManager::LinkVsyncEvent(Event* event) {
19 m_vsync_events.insert(event);
20}
21
22void VsyncManager::UnlinkVsyncEvent(Event* event) {
23 m_vsync_events.erase(event);
24}
25
26} // namespace Service::VI
diff --git a/src/core/hle/service/vi/vsync_manager.h b/src/core/hle/service/vi/vsync_manager.h
new file mode 100644
index 000000000..5d45bb5ee
--- /dev/null
+++ b/src/core/hle/service/vi/vsync_manager.h
@@ -0,0 +1,29 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <set>
7
8namespace Service {
9class Event;
10}
11
12namespace Service::VI {
13
14class DisplayList;
15
16class VsyncManager {
17public:
18 explicit VsyncManager();
19 ~VsyncManager();
20
21 void SignalVsync();
22 void LinkVsyncEvent(Event* event);
23 void UnlinkVsyncEvent(Event* event);
24
25private:
26 std::set<Event*> m_vsync_events;
27};
28
29} // namespace Service::VI
diff --git a/src/core/loader/nca.cpp b/src/core/loader/nca.cpp
index 2a32b1276..de27ec49e 100644
--- a/src/core/loader/nca.cpp
+++ b/src/core/loader/nca.cpp
@@ -118,7 +118,9 @@ ResultStatus AppLoader_NCA::VerifyIntegrity(std::function<bool(size_t, size_t)>
118 mbedtls_sha256_starts_ret(&ctx, 0); 118 mbedtls_sha256_starts_ret(&ctx, 0);
119 119
120 // Ensure we maintain a clean state on exit. 120 // Ensure we maintain a clean state on exit.
121 SCOPE_EXIT({ mbedtls_sha256_free(&ctx); }); 121 SCOPE_EXIT {
122 mbedtls_sha256_free(&ctx);
123 };
122 124
123 // Declare counters. 125 // Declare counters.
124 const size_t total_size = file->GetSize(); 126 const size_t total_size = file->GetSize();
diff --git a/src/core/memory.cpp b/src/core/memory.cpp
index e10a4601e..8775369a4 100644
--- a/src/core/memory.cpp
+++ b/src/core/memory.cpp
@@ -831,11 +831,11 @@ struct Memory::Impl {
831 if (core == sys_core) [[unlikely]] { 831 if (core == sys_core) [[unlikely]] {
832 sys_core_guard.lock(); 832 sys_core_guard.lock();
833 } 833 }
834 SCOPE_EXIT({ 834 SCOPE_EXIT {
835 if (core == sys_core) [[unlikely]] { 835 if (core == sys_core) [[unlikely]] {
836 sys_core_guard.unlock(); 836 sys_core_guard.unlock();
837 } 837 }
838 }); 838 };
839 gpu_device_memory->ApplyOpOnPointer(p, scratch_buffers[core], [&](DAddr address) { 839 gpu_device_memory->ApplyOpOnPointer(p, scratch_buffers[core], [&](DAddr address) {
840 auto& current_area = rasterizer_write_areas[core]; 840 auto& current_area = rasterizer_write_areas[core];
841 PAddr subaddress = address >> YUZU_PAGEBITS; 841 PAddr subaddress = address >> YUZU_PAGEBITS;
@@ -866,11 +866,11 @@ struct Memory::Impl {
866 if (core == sys_core) [[unlikely]] { 866 if (core == sys_core) [[unlikely]] {
867 sys_core_guard.lock(); 867 sys_core_guard.lock();
868 } 868 }
869 SCOPE_EXIT({ 869 SCOPE_EXIT {
870 if (core == sys_core) [[unlikely]] { 870 if (core == sys_core) [[unlikely]] {
871 sys_core_guard.unlock(); 871 sys_core_guard.unlock();
872 } 872 }
873 }); 873 };
874 auto& gpu = system.GPU(); 874 auto& gpu = system.GPU();
875 gpu_device_memory->ApplyOpOnPointer( 875 gpu_device_memory->ApplyOpOnPointer(
876 p, scratch_buffers[core], [&](DAddr address) { gpu.InvalidateRegion(address, size); }); 876 p, scratch_buffers[core], [&](DAddr address) { gpu.InvalidateRegion(address, size); });
diff --git a/src/core/memory/cheat_engine.cpp b/src/core/memory/cheat_engine.cpp
index b84b57d92..d8921e565 100644
--- a/src/core/memory/cheat_engine.cpp
+++ b/src/core/memory/cheat_engine.cpp
@@ -117,9 +117,9 @@ bool StandardVmCallbacks::IsAddressInRange(VAddr in) const {
117 (in < metadata.heap_extents.base || 117 (in < metadata.heap_extents.base ||
118 in >= metadata.heap_extents.base + metadata.heap_extents.size) && 118 in >= metadata.heap_extents.base + metadata.heap_extents.size) &&
119 (in < metadata.alias_extents.base || 119 (in < metadata.alias_extents.base ||
120 in >= metadata.heap_extents.base + metadata.alias_extents.size) && 120 in >= metadata.alias_extents.base + metadata.alias_extents.size) &&
121 (in < metadata.aslr_extents.base || 121 (in < metadata.aslr_extents.base ||
122 in >= metadata.heap_extents.base + metadata.aslr_extents.size)) { 122 in >= metadata.aslr_extents.base + metadata.aslr_extents.size)) {
123 LOG_DEBUG(CheatEngine, 123 LOG_DEBUG(CheatEngine,
124 "Cheat attempting to access memory at invalid address={:016X}, if this " 124 "Cheat attempting to access memory at invalid address={:016X}, if this "
125 "persists, " 125 "persists, "
diff --git a/src/core/memory/dmnt_cheat_vm.cpp b/src/core/memory/dmnt_cheat_vm.cpp
index f7097d01d..caceeec4f 100644
--- a/src/core/memory/dmnt_cheat_vm.cpp
+++ b/src/core/memory/dmnt_cheat_vm.cpp
@@ -224,12 +224,12 @@ bool DmntCheatVm::DecodeNextOpcode(CheatVmOpcode& out) {
224 // If we've ever seen a decode failure, return false. 224 // If we've ever seen a decode failure, return false.
225 bool valid = decode_success; 225 bool valid = decode_success;
226 CheatVmOpcode opcode = {}; 226 CheatVmOpcode opcode = {};
227 SCOPE_EXIT({ 227 SCOPE_EXIT {
228 decode_success &= valid; 228 decode_success &= valid;
229 if (valid) { 229 if (valid) {
230 out = opcode; 230 out = opcode;
231 } 231 }
232 }); 232 };
233 233
234 // Helper function for getting instruction dwords. 234 // Helper function for getting instruction dwords.
235 const auto GetNextDword = [&] { 235 const auto GetNextDword = [&] {
diff --git a/src/frontend_common/config.cpp b/src/frontend_common/config.cpp
index 2bebfeef9..95f8c8c36 100644
--- a/src/frontend_common/config.cpp
+++ b/src/frontend_common/config.cpp
@@ -138,6 +138,7 @@ void Config::ReadPlayerValues(const std::size_t player_index) {
138 if (profile_name.empty()) { 138 if (profile_name.empty()) {
139 // Use the global input config 139 // Use the global input config
140 player = Settings::values.players.GetValue(true)[player_index]; 140 player = Settings::values.players.GetValue(true)[player_index];
141 player.profile_name = "";
141 return; 142 return;
142 } 143 }
143 player.profile_name = profile_name; 144 player.profile_name = profile_name;
diff --git a/src/frontend_common/content_manager.h b/src/frontend_common/content_manager.h
index f3efe3465..c4e97a47b 100644
--- a/src/frontend_common/content_manager.h
+++ b/src/frontend_common/content_manager.h
@@ -251,11 +251,12 @@ inline InstallResult InstallNCA(FileSys::VfsFilesystem& vfs, const std::string&
251 * \param callback Callback to report the progress of the installation. The first size_t 251 * \param callback Callback to report the progress of the installation. The first size_t
252 * parameter is the total size of the installed contents and the second is the current progress. If 252 * parameter is the total size of the installed contents and the second is the current progress. If
253 * you return true to the callback, it will cancel the installation as soon as possible. 253 * you return true to the callback, it will cancel the installation as soon as possible.
254 * \param firmware_only Set to true to only scan system nand NCAs (firmware), post firmware install.
254 * \return A list of entries that failed to install. Returns an empty vector if successful. 255 * \return A list of entries that failed to install. Returns an empty vector if successful.
255 */ 256 */
256inline std::vector<std::string> VerifyInstalledContents( 257inline std::vector<std::string> VerifyInstalledContents(
257 Core::System& system, FileSys::ManualContentProvider& provider, 258 Core::System& system, FileSys::ManualContentProvider& provider,
258 const std::function<bool(size_t, size_t)>& callback) { 259 const std::function<bool(size_t, size_t)>& callback, bool firmware_only = false) {
259 // Get content registries. 260 // Get content registries.
260 auto bis_contents = system.GetFileSystemController().GetSystemNANDContents(); 261 auto bis_contents = system.GetFileSystemController().GetSystemNANDContents();
261 auto user_contents = system.GetFileSystemController().GetUserNANDContents(); 262 auto user_contents = system.GetFileSystemController().GetUserNANDContents();
@@ -264,7 +265,7 @@ inline std::vector<std::string> VerifyInstalledContents(
264 if (bis_contents) { 265 if (bis_contents) {
265 content_providers.push_back(bis_contents); 266 content_providers.push_back(bis_contents);
266 } 267 }
267 if (user_contents) { 268 if (user_contents && !firmware_only) {
268 content_providers.push_back(user_contents); 269 content_providers.push_back(user_contents);
269 } 270 }
270 271
diff --git a/src/hid_core/frontend/emulated_controller.cpp b/src/hid_core/frontend/emulated_controller.cpp
index 819460eb5..5cd26819c 100644
--- a/src/hid_core/frontend/emulated_controller.cpp
+++ b/src/hid_core/frontend/emulated_controller.cpp
@@ -174,18 +174,25 @@ void EmulatedController::LoadDevices() {
174 // Only map virtual devices to the first controller 174 // Only map virtual devices to the first controller
175 if (npad_id_type == NpadIdType::Player1 || npad_id_type == NpadIdType::Handheld) { 175 if (npad_id_type == NpadIdType::Player1 || npad_id_type == NpadIdType::Handheld) {
176 camera_params[1] = Common::ParamPackage{"engine:camera,camera:1"}; 176 camera_params[1] = Common::ParamPackage{"engine:camera,camera:1"};
177 ring_params[1] = Common::ParamPackage{"engine:joycon,axis_x:100,axis_y:101"};
178 nfc_params[0] = Common::ParamPackage{"engine:virtual_amiibo,nfc:1"}; 177 nfc_params[0] = Common::ParamPackage{"engine:virtual_amiibo,nfc:1"};
178#ifdef HAVE_LIBUSB
179 ring_params[1] = Common::ParamPackage{"engine:joycon,axis_x:100,axis_y:101"};
180#endif
181#ifdef ANDROID
182 android_params = Common::ParamPackage{"engine:android,port:100"};
183#endif
179 } 184 }
180 185
181 output_params[LeftIndex] = left_joycon; 186 output_params[LeftIndex] = left_joycon;
182 output_params[RightIndex] = right_joycon; 187 output_params[RightIndex] = right_joycon;
183 output_params[2] = camera_params[1]; 188 output_params[2] = camera_params[1];
184 output_params[3] = nfc_params[0]; 189 output_params[3] = nfc_params[0];
190 output_params[4] = android_params;
185 output_params[LeftIndex].Set("output", true); 191 output_params[LeftIndex].Set("output", true);
186 output_params[RightIndex].Set("output", true); 192 output_params[RightIndex].Set("output", true);
187 output_params[2].Set("output", true); 193 output_params[2].Set("output", true);
188 output_params[3].Set("output", true); 194 output_params[3].Set("output", true);
195 output_params[4].Set("output", true);
189 196
190 LoadTASParams(); 197 LoadTASParams();
191 LoadVirtualGamepadParams(); 198 LoadVirtualGamepadParams();
@@ -578,6 +585,9 @@ void EmulatedController::DisableConfiguration() {
578 585
579 // Get Joycon colors before turning on the controller 586 // Get Joycon colors before turning on the controller
580 for (const auto& color_device : color_devices) { 587 for (const auto& color_device : color_devices) {
588 if (color_device == nullptr) {
589 continue;
590 }
581 color_device->ForceUpdate(); 591 color_device->ForceUpdate();
582 } 592 }
583 593
@@ -923,8 +933,9 @@ void EmulatedController::SetStick(const Common::Input::CallbackStatus& callback,
923 if (index >= controller.stick_values.size()) { 933 if (index >= controller.stick_values.size()) {
924 return; 934 return;
925 } 935 }
926 auto trigger_guard = 936 auto trigger_guard = SCOPE_GUARD {
927 SCOPE_GUARD({ TriggerOnChange(ControllerTriggerType::Stick, !is_configuring); }); 937 TriggerOnChange(ControllerTriggerType::Stick, !is_configuring);
938 };
928 std::scoped_lock lock{mutex}; 939 std::scoped_lock lock{mutex};
929 const auto stick_value = TransformToStick(callback); 940 const auto stick_value = TransformToStick(callback);
930 941
@@ -979,8 +990,9 @@ void EmulatedController::SetTrigger(const Common::Input::CallbackStatus& callbac
979 if (index >= controller.trigger_values.size()) { 990 if (index >= controller.trigger_values.size()) {
980 return; 991 return;
981 } 992 }
982 auto trigger_guard = 993 auto trigger_guard = SCOPE_GUARD {
983 SCOPE_GUARD({ TriggerOnChange(ControllerTriggerType::Trigger, !is_configuring); }); 994 TriggerOnChange(ControllerTriggerType::Trigger, !is_configuring);
995 };
984 std::scoped_lock lock{mutex}; 996 std::scoped_lock lock{mutex};
985 const auto trigger_value = TransformToTrigger(callback); 997 const auto trigger_value = TransformToTrigger(callback);
986 998
@@ -1026,7 +1038,9 @@ void EmulatedController::SetMotion(const Common::Input::CallbackStatus& callback
1026 if (index >= controller.motion_values.size()) { 1038 if (index >= controller.motion_values.size()) {
1027 return; 1039 return;
1028 } 1040 }
1029 SCOPE_EXIT({ TriggerOnChange(ControllerTriggerType::Motion, !is_configuring); }); 1041 SCOPE_EXIT {
1042 TriggerOnChange(ControllerTriggerType::Motion, !is_configuring);
1043 };
1030 std::scoped_lock lock{mutex}; 1044 std::scoped_lock lock{mutex};
1031 auto& raw_status = controller.motion_values[index].raw_status; 1045 auto& raw_status = controller.motion_values[index].raw_status;
1032 auto& emulated = controller.motion_values[index].emulated; 1046 auto& emulated = controller.motion_values[index].emulated;
@@ -1060,8 +1074,9 @@ void EmulatedController::SetColors(const Common::Input::CallbackStatus& callback
1060 if (index >= controller.color_values.size()) { 1074 if (index >= controller.color_values.size()) {
1061 return; 1075 return;
1062 } 1076 }
1063 auto trigger_guard = 1077 auto trigger_guard = SCOPE_GUARD {
1064 SCOPE_GUARD({ TriggerOnChange(ControllerTriggerType::Color, !is_configuring); }); 1078 TriggerOnChange(ControllerTriggerType::Color, !is_configuring);
1079 };
1065 std::scoped_lock lock{mutex}; 1080 std::scoped_lock lock{mutex};
1066 controller.color_values[index] = TransformToColor(callback); 1081 controller.color_values[index] = TransformToColor(callback);
1067 1082
@@ -1110,7 +1125,9 @@ void EmulatedController::SetBattery(const Common::Input::CallbackStatus& callbac
1110 if (index >= controller.battery_values.size()) { 1125 if (index >= controller.battery_values.size()) {
1111 return; 1126 return;
1112 } 1127 }
1113 SCOPE_EXIT({ TriggerOnChange(ControllerTriggerType::Battery, !is_configuring); }); 1128 SCOPE_EXIT {
1129 TriggerOnChange(ControllerTriggerType::Battery, !is_configuring);
1130 };
1114 std::scoped_lock lock{mutex}; 1131 std::scoped_lock lock{mutex};
1115 controller.battery_values[index] = TransformToBattery(callback); 1132 controller.battery_values[index] = TransformToBattery(callback);
1116 1133
@@ -1173,7 +1190,9 @@ void EmulatedController::SetBattery(const Common::Input::CallbackStatus& callbac
1173} 1190}
1174 1191
1175void EmulatedController::SetCamera(const Common::Input::CallbackStatus& callback) { 1192void EmulatedController::SetCamera(const Common::Input::CallbackStatus& callback) {
1176 SCOPE_EXIT({ TriggerOnChange(ControllerTriggerType::IrSensor, !is_configuring); }); 1193 SCOPE_EXIT {
1194 TriggerOnChange(ControllerTriggerType::IrSensor, !is_configuring);
1195 };
1177 std::scoped_lock lock{mutex}; 1196 std::scoped_lock lock{mutex};
1178 controller.camera_values = TransformToCamera(callback); 1197 controller.camera_values = TransformToCamera(callback);
1179 1198
@@ -1188,7 +1207,9 @@ void EmulatedController::SetCamera(const Common::Input::CallbackStatus& callback
1188} 1207}
1189 1208
1190void EmulatedController::SetRingAnalog(const Common::Input::CallbackStatus& callback) { 1209void EmulatedController::SetRingAnalog(const Common::Input::CallbackStatus& callback) {
1191 SCOPE_EXIT({ TriggerOnChange(ControllerTriggerType::RingController, !is_configuring); }); 1210 SCOPE_EXIT {
1211 TriggerOnChange(ControllerTriggerType::RingController, !is_configuring);
1212 };
1192 std::scoped_lock lock{mutex}; 1213 std::scoped_lock lock{mutex};
1193 const auto force_value = TransformToStick(callback); 1214 const auto force_value = TransformToStick(callback);
1194 1215
@@ -1202,7 +1223,9 @@ void EmulatedController::SetRingAnalog(const Common::Input::CallbackStatus& call
1202} 1223}
1203 1224
1204void EmulatedController::SetNfc(const Common::Input::CallbackStatus& callback) { 1225void EmulatedController::SetNfc(const Common::Input::CallbackStatus& callback) {
1205 SCOPE_EXIT({ TriggerOnChange(ControllerTriggerType::Nfc, !is_configuring); }); 1226 SCOPE_EXIT {
1227 TriggerOnChange(ControllerTriggerType::Nfc, !is_configuring);
1228 };
1206 std::scoped_lock lock{mutex}; 1229 std::scoped_lock lock{mutex};
1207 controller.nfc_values = TransformToNfc(callback); 1230 controller.nfc_values = TransformToNfc(callback);
1208 1231
@@ -1277,6 +1300,10 @@ bool EmulatedController::SetVibration(DeviceIndex device_index, const VibrationV
1277 .high_frequency = vibration.high_frequency, 1300 .high_frequency = vibration.high_frequency,
1278 .type = type, 1301 .type = type,
1279 }; 1302 };
1303
1304 // Send vibrations to Android's input overlay
1305 output_devices[4]->SetVibration(status);
1306
1280 return output_devices[index]->SetVibration(status) == Common::Input::DriverResult::Success; 1307 return output_devices[index]->SetVibration(status) == Common::Input::DriverResult::Success;
1281} 1308}
1282 1309
@@ -1671,8 +1698,9 @@ void EmulatedController::Connect(bool use_temporary_value) {
1671 return; 1698 return;
1672 } 1699 }
1673 1700
1674 auto trigger_guard = 1701 auto trigger_guard = SCOPE_GUARD {
1675 SCOPE_GUARD({ TriggerOnChange(ControllerTriggerType::Connected, !is_configuring); }); 1702 TriggerOnChange(ControllerTriggerType::Connected, !is_configuring);
1703 };
1676 std::scoped_lock lock{connect_mutex, mutex}; 1704 std::scoped_lock lock{connect_mutex, mutex};
1677 if (is_configuring) { 1705 if (is_configuring) {
1678 tmp_is_connected = true; 1706 tmp_is_connected = true;
@@ -1687,8 +1715,9 @@ void EmulatedController::Connect(bool use_temporary_value) {
1687} 1715}
1688 1716
1689void EmulatedController::Disconnect() { 1717void EmulatedController::Disconnect() {
1690 auto trigger_guard = 1718 auto trigger_guard = SCOPE_GUARD {
1691 SCOPE_GUARD({ TriggerOnChange(ControllerTriggerType::Disconnected, !is_configuring); }); 1719 TriggerOnChange(ControllerTriggerType::Disconnected, !is_configuring);
1720 };
1692 std::scoped_lock lock{connect_mutex, mutex}; 1721 std::scoped_lock lock{connect_mutex, mutex};
1693 if (is_configuring) { 1722 if (is_configuring) {
1694 tmp_is_connected = false; 1723 tmp_is_connected = false;
@@ -1724,8 +1753,9 @@ NpadStyleIndex EmulatedController::GetNpadStyleIndex(bool get_temporary_value) c
1724} 1753}
1725 1754
1726void EmulatedController::SetNpadStyleIndex(NpadStyleIndex npad_type_) { 1755void EmulatedController::SetNpadStyleIndex(NpadStyleIndex npad_type_) {
1727 auto trigger_guard = 1756 auto trigger_guard = SCOPE_GUARD {
1728 SCOPE_GUARD({ TriggerOnChange(ControllerTriggerType::Type, !is_configuring); }); 1757 TriggerOnChange(ControllerTriggerType::Type, !is_configuring);
1758 };
1729 std::scoped_lock lock{mutex, npad_mutex}; 1759 std::scoped_lock lock{mutex, npad_mutex};
1730 1760
1731 if (is_configuring) { 1761 if (is_configuring) {
diff --git a/src/hid_core/frontend/emulated_controller.h b/src/hid_core/frontend/emulated_controller.h
index 701b38300..ab3c6fcd3 100644
--- a/src/hid_core/frontend/emulated_controller.h
+++ b/src/hid_core/frontend/emulated_controller.h
@@ -21,7 +21,7 @@
21 21
22namespace Core::HID { 22namespace Core::HID {
23const std::size_t max_emulated_controllers = 2; 23const std::size_t max_emulated_controllers = 2;
24const std::size_t output_devices_size = 4; 24const std::size_t output_devices_size = 5;
25struct ControllerMotionInfo { 25struct ControllerMotionInfo {
26 Common::Input::MotionStatus raw_status{}; 26 Common::Input::MotionStatus raw_status{};
27 MotionInput emulated{}; 27 MotionInput emulated{};
@@ -597,6 +597,7 @@ private:
597 CameraParams camera_params; 597 CameraParams camera_params;
598 RingAnalogParams ring_params; 598 RingAnalogParams ring_params;
599 NfcParams nfc_params; 599 NfcParams nfc_params;
600 Common::ParamPackage android_params;
600 OutputParams output_params; 601 OutputParams output_params;
601 602
602 ButtonDevices button_devices; 603 ButtonDevices button_devices;
diff --git a/src/input_common/CMakeLists.txt b/src/input_common/CMakeLists.txt
index d0a71a15b..d455323e0 100644
--- a/src/input_common/CMakeLists.txt
+++ b/src/input_common/CMakeLists.txt
@@ -2,8 +2,6 @@
2# SPDX-License-Identifier: GPL-2.0-or-later 2# SPDX-License-Identifier: GPL-2.0-or-later
3 3
4add_library(input_common STATIC 4add_library(input_common STATIC
5 drivers/android.cpp
6 drivers/android.h
7 drivers/camera.cpp 5 drivers/camera.cpp
8 drivers/camera.h 6 drivers/camera.h
9 drivers/keyboard.cpp 7 drivers/keyboard.cpp
@@ -94,3 +92,11 @@ target_link_libraries(input_common PUBLIC hid_core PRIVATE common Boost::headers
94if (YUZU_USE_PRECOMPILED_HEADERS) 92if (YUZU_USE_PRECOMPILED_HEADERS)
95 target_precompile_headers(input_common PRIVATE precompiled_headers.h) 93 target_precompile_headers(input_common PRIVATE precompiled_headers.h)
96endif() 94endif()
95
96if (ANDROID)
97 target_sources(input_common PRIVATE
98 drivers/android.cpp
99 drivers/android.h
100 )
101 target_link_libraries(input_common PRIVATE android)
102endif()
diff --git a/src/input_common/drivers/android.cpp b/src/input_common/drivers/android.cpp
index b6a03fdc0..e859cc538 100644
--- a/src/input_common/drivers/android.cpp
+++ b/src/input_common/drivers/android.cpp
@@ -1,30 +1,47 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later 2// SPDX-License-Identifier: GPL-3.0-or-later
3 3
4#include <set>
5#include <common/settings_input.h>
6#include <jni.h>
7#include "common/android/android_common.h"
8#include "common/android/id_cache.h"
4#include "input_common/drivers/android.h" 9#include "input_common/drivers/android.h"
5 10
6namespace InputCommon { 11namespace InputCommon {
7 12
8Android::Android(std::string input_engine_) : InputEngine(std::move(input_engine_)) {} 13Android::Android(std::string input_engine_) : InputEngine(std::move(input_engine_)) {}
9 14
10void Android::RegisterController(std::size_t controller_number) { 15void Android::RegisterController(jobject j_input_device) {
11 PreSetController(GetIdentifier(controller_number)); 16 auto env = Common::Android::GetEnvForThread();
17 const std::string guid = Common::Android::GetJString(
18 env, static_cast<jstring>(
19 env->CallObjectMethod(j_input_device, Common::Android::GetYuzuDeviceGetGUID())));
20 const s32 port = env->CallIntMethod(j_input_device, Common::Android::GetYuzuDeviceGetPort());
21 const auto identifier = GetIdentifier(guid, static_cast<size_t>(port));
22 PreSetController(identifier);
23
24 if (input_devices.find(identifier) != input_devices.end()) {
25 env->DeleteGlobalRef(input_devices[identifier]);
26 }
27 auto new_device = env->NewGlobalRef(j_input_device);
28 input_devices[identifier] = new_device;
12} 29}
13 30
14void Android::SetButtonState(std::size_t controller_number, int button_id, bool value) { 31void Android::SetButtonState(std::string guid, size_t port, int button_id, bool value) {
15 const auto identifier = GetIdentifier(controller_number); 32 const auto identifier = GetIdentifier(guid, port);
16 SetButton(identifier, button_id, value); 33 SetButton(identifier, button_id, value);
17} 34}
18 35
19void Android::SetAxisState(std::size_t controller_number, int axis_id, float value) { 36void Android::SetAxisPosition(std::string guid, size_t port, int axis_id, float value) {
20 const auto identifier = GetIdentifier(controller_number); 37 const auto identifier = GetIdentifier(guid, port);
21 SetAxis(identifier, axis_id, value); 38 SetAxis(identifier, axis_id, value);
22} 39}
23 40
24void Android::SetMotionState(std::size_t controller_number, u64 delta_timestamp, float gyro_x, 41void Android::SetMotionState(std::string guid, size_t port, u64 delta_timestamp, float gyro_x,
25 float gyro_y, float gyro_z, float accel_x, float accel_y, 42 float gyro_y, float gyro_z, float accel_x, float accel_y,
26 float accel_z) { 43 float accel_z) {
27 const auto identifier = GetIdentifier(controller_number); 44 const auto identifier = GetIdentifier(guid, port);
28 const BasicMotion motion_data{ 45 const BasicMotion motion_data{
29 .gyro_x = gyro_x, 46 .gyro_x = gyro_x,
30 .gyro_y = gyro_y, 47 .gyro_y = gyro_y,
@@ -37,10 +54,295 @@ void Android::SetMotionState(std::size_t controller_number, u64 delta_timestamp,
37 SetMotion(identifier, 0, motion_data); 54 SetMotion(identifier, 0, motion_data);
38} 55}
39 56
40PadIdentifier Android::GetIdentifier(std::size_t controller_number) const { 57Common::Input::DriverResult Android::SetVibration(
58 [[maybe_unused]] const PadIdentifier& identifier,
59 [[maybe_unused]] const Common::Input::VibrationStatus& vibration) {
60 auto device = input_devices.find(identifier);
61 if (device != input_devices.end()) {
62 Common::Android::RunJNIOnFiber<void>([&](JNIEnv* env) {
63 float average_intensity =
64 static_cast<float>((vibration.high_amplitude + vibration.low_amplitude) / 2.0);
65 env->CallVoidMethod(device->second, Common::Android::GetYuzuDeviceVibrate(),
66 average_intensity);
67 });
68 return Common::Input::DriverResult::Success;
69 }
70 return Common::Input::DriverResult::NotSupported;
71}
72
73bool Android::IsVibrationEnabled([[maybe_unused]] const PadIdentifier& identifier) {
74 auto device = input_devices.find(identifier);
75 if (device != input_devices.end()) {
76 return Common::Android::RunJNIOnFiber<bool>([&](JNIEnv* env) {
77 return static_cast<bool>(env->CallBooleanMethod(
78 device->second, Common::Android::GetYuzuDeviceGetSupportsVibration()));
79 });
80 }
81 return false;
82}
83
84std::vector<Common::ParamPackage> Android::GetInputDevices() const {
85 std::vector<Common::ParamPackage> devices;
86 auto env = Common::Android::GetEnvForThread();
87 for (const auto& [key, value] : input_devices) {
88 auto name_object = static_cast<jstring>(
89 env->CallObjectMethod(value, Common::Android::GetYuzuDeviceGetName()));
90 const std::string name =
91 fmt::format("{} {}", Common::Android::GetJString(env, name_object), key.port);
92 devices.emplace_back(Common::ParamPackage{
93 {"engine", GetEngineName()},
94 {"display", std::move(name)},
95 {"guid", key.guid.RawString()},
96 {"port", std::to_string(key.port)},
97 });
98 }
99 return devices;
100}
101
102std::set<s32> Android::GetDeviceAxes(JNIEnv* env, jobject& j_device) const {
103 auto j_axes = static_cast<jobjectArray>(
104 env->CallObjectMethod(j_device, Common::Android::GetYuzuDeviceGetAxes()));
105 std::set<s32> axes;
106 for (int i = 0; i < env->GetArrayLength(j_axes); ++i) {
107 jobject axis = env->GetObjectArrayElement(j_axes, i);
108 axes.insert(env->GetIntField(axis, Common::Android::GetIntegerValueField()));
109 }
110 return axes;
111}
112
113Common::ParamPackage Android::BuildParamPackageForAnalog(PadIdentifier identifier, int axis_x,
114 int axis_y) const {
115 Common::ParamPackage params;
116 params.Set("engine", GetEngineName());
117 params.Set("port", static_cast<int>(identifier.port));
118 params.Set("guid", identifier.guid.RawString());
119 params.Set("axis_x", axis_x);
120 params.Set("axis_y", axis_y);
121 params.Set("offset_x", 0);
122 params.Set("offset_y", 0);
123 params.Set("invert_x", "+");
124
125 // Invert Y-Axis by default
126 params.Set("invert_y", "-");
127 return params;
128}
129
130Common::ParamPackage Android::BuildAnalogParamPackageForButton(PadIdentifier identifier, s32 axis,
131 bool invert) const {
132 Common::ParamPackage params{};
133 params.Set("engine", GetEngineName());
134 params.Set("port", static_cast<int>(identifier.port));
135 params.Set("guid", identifier.guid.RawString());
136 params.Set("axis", axis);
137 params.Set("threshold", "0.5");
138 params.Set("invert", invert ? "-" : "+");
139 return params;
140}
141
142Common::ParamPackage Android::BuildButtonParamPackageForButton(PadIdentifier identifier,
143 s32 button) const {
144 Common::ParamPackage params{};
145 params.Set("engine", GetEngineName());
146 params.Set("port", static_cast<int>(identifier.port));
147 params.Set("guid", identifier.guid.RawString());
148 params.Set("button", button);
149 return params;
150}
151
152bool Android::MatchVID(Common::UUID device, const std::vector<std::string>& vids) const {
153 for (size_t i = 0; i < vids.size(); ++i) {
154 auto fucker = device.RawString();
155 if (fucker.find(vids[i]) != std::string::npos) {
156 return true;
157 }
158 }
159 return false;
160}
161
162AnalogMapping Android::GetAnalogMappingForDevice(const Common::ParamPackage& params) {
163 if (!params.Has("guid") || !params.Has("port")) {
164 return {};
165 }
166
167 auto identifier =
168 GetIdentifier(params.Get("guid", ""), static_cast<size_t>(params.Get("port", 0)));
169 auto& j_device = input_devices[identifier];
170 if (j_device == nullptr) {
171 return {};
172 }
173
174 auto env = Common::Android::GetEnvForThread();
175 std::set<s32> axes = GetDeviceAxes(env, j_device);
176 if (axes.size() == 0) {
177 return {};
178 }
179
180 AnalogMapping mapping = {};
181 if (axes.find(AXIS_X) != axes.end() && axes.find(AXIS_Y) != axes.end()) {
182 mapping.insert_or_assign(Settings::NativeAnalog::LStick,
183 BuildParamPackageForAnalog(identifier, AXIS_X, AXIS_Y));
184 }
185
186 if (axes.find(AXIS_RX) != axes.end() && axes.find(AXIS_RY) != axes.end()) {
187 mapping.insert_or_assign(Settings::NativeAnalog::RStick,
188 BuildParamPackageForAnalog(identifier, AXIS_RX, AXIS_RY));
189 } else if (axes.find(AXIS_Z) != axes.end() && axes.find(AXIS_RZ) != axes.end()) {
190 mapping.insert_or_assign(Settings::NativeAnalog::RStick,
191 BuildParamPackageForAnalog(identifier, AXIS_Z, AXIS_RZ));
192 }
193 return mapping;
194}
195
196ButtonMapping Android::GetButtonMappingForDevice(const Common::ParamPackage& params) {
197 if (!params.Has("guid") || !params.Has("port")) {
198 return {};
199 }
200
201 auto identifier =
202 GetIdentifier(params.Get("guid", ""), static_cast<size_t>(params.Get("port", 0)));
203 auto& j_device = input_devices[identifier];
204 if (j_device == nullptr) {
205 return {};
206 }
207
208 auto env = Common::Android::GetEnvForThread();
209 jintArray j_keys = env->NewIntArray(static_cast<int>(keycode_ids.size()));
210 env->SetIntArrayRegion(j_keys, 0, static_cast<int>(keycode_ids.size()), keycode_ids.data());
211 auto j_has_keys_object = static_cast<jbooleanArray>(
212 env->CallObjectMethod(j_device, Common::Android::GetYuzuDeviceHasKeys(), j_keys));
213 jboolean isCopy = false;
214 jboolean* j_has_keys = env->GetBooleanArrayElements(j_has_keys_object, &isCopy);
215
216 std::set<s32> available_keys;
217 for (size_t i = 0; i < keycode_ids.size(); ++i) {
218 if (j_has_keys[i]) {
219 available_keys.insert(keycode_ids[i]);
220 }
221 }
222
223 // Some devices use axes instead of buttons for certain controls so we need all the axes here
224 std::set<s32> axes = GetDeviceAxes(env, j_device);
225
226 ButtonMapping mapping = {};
227 if (axes.find(AXIS_HAT_X) != axes.end() && axes.find(AXIS_HAT_Y) != axes.end()) {
228 mapping.insert_or_assign(Settings::NativeButton::DUp,
229 BuildAnalogParamPackageForButton(identifier, AXIS_HAT_Y, true));
230 mapping.insert_or_assign(Settings::NativeButton::DDown,
231 BuildAnalogParamPackageForButton(identifier, AXIS_HAT_Y, false));
232 mapping.insert_or_assign(Settings::NativeButton::DLeft,
233 BuildAnalogParamPackageForButton(identifier, AXIS_HAT_X, true));
234 mapping.insert_or_assign(Settings::NativeButton::DRight,
235 BuildAnalogParamPackageForButton(identifier, AXIS_HAT_X, false));
236 } else if (available_keys.find(KEYCODE_DPAD_UP) != available_keys.end() &&
237 available_keys.find(KEYCODE_DPAD_DOWN) != available_keys.end() &&
238 available_keys.find(KEYCODE_DPAD_LEFT) != available_keys.end() &&
239 available_keys.find(KEYCODE_DPAD_RIGHT) != available_keys.end()) {
240 mapping.insert_or_assign(Settings::NativeButton::DUp,
241 BuildButtonParamPackageForButton(identifier, KEYCODE_DPAD_UP));
242 mapping.insert_or_assign(Settings::NativeButton::DDown,
243 BuildButtonParamPackageForButton(identifier, KEYCODE_DPAD_DOWN));
244 mapping.insert_or_assign(Settings::NativeButton::DLeft,
245 BuildButtonParamPackageForButton(identifier, KEYCODE_DPAD_LEFT));
246 mapping.insert_or_assign(Settings::NativeButton::DRight,
247 BuildButtonParamPackageForButton(identifier, KEYCODE_DPAD_RIGHT));
248 }
249
250 if (axes.find(AXIS_LTRIGGER) != axes.end()) {
251 mapping.insert_or_assign(Settings::NativeButton::ZL, BuildAnalogParamPackageForButton(
252 identifier, AXIS_LTRIGGER, false));
253 } else if (available_keys.find(KEYCODE_BUTTON_L2) != available_keys.end()) {
254 mapping.insert_or_assign(Settings::NativeButton::ZL,
255 BuildButtonParamPackageForButton(identifier, KEYCODE_BUTTON_L2));
256 }
257
258 if (axes.find(AXIS_RTRIGGER) != axes.end()) {
259 mapping.insert_or_assign(Settings::NativeButton::ZR, BuildAnalogParamPackageForButton(
260 identifier, AXIS_RTRIGGER, false));
261 } else if (available_keys.find(KEYCODE_BUTTON_R2) != available_keys.end()) {
262 mapping.insert_or_assign(Settings::NativeButton::ZR,
263 BuildButtonParamPackageForButton(identifier, KEYCODE_BUTTON_R2));
264 }
265
266 if (available_keys.find(KEYCODE_BUTTON_A) != available_keys.end()) {
267 if (MatchVID(identifier.guid, flipped_ab_vids)) {
268 mapping.insert_or_assign(Settings::NativeButton::B, BuildButtonParamPackageForButton(
269 identifier, KEYCODE_BUTTON_A));
270 } else {
271 mapping.insert_or_assign(Settings::NativeButton::A, BuildButtonParamPackageForButton(
272 identifier, KEYCODE_BUTTON_A));
273 }
274 }
275 if (available_keys.find(KEYCODE_BUTTON_B) != available_keys.end()) {
276 if (MatchVID(identifier.guid, flipped_ab_vids)) {
277 mapping.insert_or_assign(Settings::NativeButton::A, BuildButtonParamPackageForButton(
278 identifier, KEYCODE_BUTTON_B));
279 } else {
280 mapping.insert_or_assign(Settings::NativeButton::B, BuildButtonParamPackageForButton(
281 identifier, KEYCODE_BUTTON_B));
282 }
283 }
284 if (available_keys.find(KEYCODE_BUTTON_X) != available_keys.end()) {
285 if (MatchVID(identifier.guid, flipped_xy_vids)) {
286 mapping.insert_or_assign(Settings::NativeButton::Y, BuildButtonParamPackageForButton(
287 identifier, KEYCODE_BUTTON_X));
288 } else {
289 mapping.insert_or_assign(Settings::NativeButton::X, BuildButtonParamPackageForButton(
290 identifier, KEYCODE_BUTTON_X));
291 }
292 }
293 if (available_keys.find(KEYCODE_BUTTON_Y) != available_keys.end()) {
294 if (MatchVID(identifier.guid, flipped_xy_vids)) {
295 mapping.insert_or_assign(Settings::NativeButton::X, BuildButtonParamPackageForButton(
296 identifier, KEYCODE_BUTTON_Y));
297 } else {
298 mapping.insert_or_assign(Settings::NativeButton::Y, BuildButtonParamPackageForButton(
299 identifier, KEYCODE_BUTTON_Y));
300 }
301 }
302
303 if (available_keys.find(KEYCODE_BUTTON_L1) != available_keys.end()) {
304 mapping.insert_or_assign(Settings::NativeButton::L,
305 BuildButtonParamPackageForButton(identifier, KEYCODE_BUTTON_L1));
306 }
307 if (available_keys.find(KEYCODE_BUTTON_R1) != available_keys.end()) {
308 mapping.insert_or_assign(Settings::NativeButton::R,
309 BuildButtonParamPackageForButton(identifier, KEYCODE_BUTTON_R1));
310 }
311
312 if (available_keys.find(KEYCODE_BUTTON_THUMBL) != available_keys.end()) {
313 mapping.insert_or_assign(
314 Settings::NativeButton::LStick,
315 BuildButtonParamPackageForButton(identifier, KEYCODE_BUTTON_THUMBL));
316 }
317 if (available_keys.find(KEYCODE_BUTTON_THUMBR) != available_keys.end()) {
318 mapping.insert_or_assign(
319 Settings::NativeButton::RStick,
320 BuildButtonParamPackageForButton(identifier, KEYCODE_BUTTON_THUMBR));
321 }
322
323 if (available_keys.find(KEYCODE_BUTTON_START) != available_keys.end()) {
324 mapping.insert_or_assign(
325 Settings::NativeButton::Plus,
326 BuildButtonParamPackageForButton(identifier, KEYCODE_BUTTON_START));
327 }
328 if (available_keys.find(KEYCODE_BUTTON_SELECT) != available_keys.end()) {
329 mapping.insert_or_assign(
330 Settings::NativeButton::Minus,
331 BuildButtonParamPackageForButton(identifier, KEYCODE_BUTTON_SELECT));
332 }
333
334 return mapping;
335}
336
337Common::Input::ButtonNames Android::GetUIName(
338 [[maybe_unused]] const Common::ParamPackage& params) const {
339 return Common::Input::ButtonNames::Value;
340}
341
342PadIdentifier Android::GetIdentifier(const std::string& guid, size_t port) const {
41 return { 343 return {
42 .guid = Common::UUID{}, 344 .guid = Common::UUID{guid},
43 .port = controller_number, 345 .port = port,
44 .pad = 0, 346 .pad = 0,
45 }; 347 };
46} 348}
diff --git a/src/input_common/drivers/android.h b/src/input_common/drivers/android.h
index 3f01817f6..8a386c1b1 100644
--- a/src/input_common/drivers/android.h
+++ b/src/input_common/drivers/android.h
@@ -3,6 +3,8 @@
3 3
4#pragma once 4#pragma once
5 5
6#include <set>
7#include <jni.h>
6#include "input_common/input_engine.h" 8#include "input_common/input_engine.h"
7 9
8namespace InputCommon { 10namespace InputCommon {
@@ -15,40 +17,122 @@ public:
15 explicit Android(std::string input_engine_); 17 explicit Android(std::string input_engine_);
16 18
17 /** 19 /**
18 * Registers controller number to accept new inputs 20 * Registers controller number to accept new inputs.
19 * @param controller_number the controller number that will take this action 21 * @param j_input_device YuzuInputDevice object from the Android frontend to register.
20 */ 22 */
21 void RegisterController(std::size_t controller_number); 23 void RegisterController(jobject j_input_device);
22 24
23 /** 25 /**
24 * Sets the status of all buttons bound with the key to pressed 26 * Sets the status of a button on a specific controller.
25 * @param controller_number the controller number that will take this action 27 * @param guid 32 character hexadecimal string consisting of the controller's PID+VID.
26 * @param button_id the id of the button 28 * @param port Port determined by controller connection order.
27 * @param value indicates if the button is pressed or not 29 * @param button_id The Android Keycode corresponding to this event.
30 * @param value Whether the button is pressed or not.
28 */ 31 */
29 void SetButtonState(std::size_t controller_number, int button_id, bool value); 32 void SetButtonState(std::string guid, size_t port, int button_id, bool value);
30 33
31 /** 34 /**
32 * Sets the status of a analog input to a specific player index 35 * Sets the status of an axis on a specific controller.
33 * @param controller_number the controller number that will take this action 36 * @param guid 32 character hexadecimal string consisting of the controller's PID+VID.
34 * @param axis_id the id of the axis to move 37 * @param port Port determined by controller connection order.
35 * @param value the analog position of the axis 38 * @param axis_id The Android axis ID corresponding to this event.
39 * @param value Value along the given axis.
36 */ 40 */
37 void SetAxisState(std::size_t controller_number, int axis_id, float value); 41 void SetAxisPosition(std::string guid, size_t port, int axis_id, float value);
38 42
39 /** 43 /**
40 * Sets the status of the motion sensor to a specific player index 44 * Sets the status of the motion sensor on a specific controller
41 * @param controller_number the controller number that will take this action 45 * @param guid 32 character hexadecimal string consisting of the controller's PID+VID.
42 * @param delta_timestamp time passed since last reading 46 * @param port Port determined by controller connection order.
43 * @param gyro_x,gyro_y,gyro_z the gyro sensor readings 47 * @param delta_timestamp Time passed since the last read.
44 * @param accel_x,accel_y,accel_z the accelerometer reading 48 * @param gyro_x,gyro_y,gyro_z Gyro sensor readings.
49 * @param accel_x,accel_y,accel_z Accelerometer sensor readings.
45 */ 50 */
46 void SetMotionState(std::size_t controller_number, u64 delta_timestamp, float gyro_x, 51 void SetMotionState(std::string guid, size_t port, u64 delta_timestamp, float gyro_x,
47 float gyro_y, float gyro_z, float accel_x, float accel_y, float accel_z); 52 float gyro_y, float gyro_z, float accel_x, float accel_y, float accel_z);
48 53
54 Common::Input::DriverResult SetVibration(
55 const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) override;
56
57 bool IsVibrationEnabled(const PadIdentifier& identifier) override;
58
59 std::vector<Common::ParamPackage> GetInputDevices() const override;
60
61 /**
62 * Gets the axes reported by the YuzuInputDevice.
63 * @param env JNI environment pointer.
64 * @param j_device YuzuInputDevice from the Android frontend.
65 * @return Set of the axes reported by the underlying Android InputDevice
66 */
67 std::set<s32> GetDeviceAxes(JNIEnv* env, jobject& j_device) const;
68
69 Common::ParamPackage BuildParamPackageForAnalog(PadIdentifier identifier, int axis_x,
70 int axis_y) const;
71
72 Common::ParamPackage BuildAnalogParamPackageForButton(PadIdentifier identifier, s32 axis,
73 bool invert) const;
74
75 Common::ParamPackage BuildButtonParamPackageForButton(PadIdentifier identifier,
76 s32 button) const;
77
78 bool MatchVID(Common::UUID device, const std::vector<std::string>& vids) const;
79
80 AnalogMapping GetAnalogMappingForDevice(const Common::ParamPackage& params) override;
81
82 ButtonMapping GetButtonMappingForDevice(const Common::ParamPackage& params) override;
83
84 Common::Input::ButtonNames GetUIName(const Common::ParamPackage& params) const override;
85
49private: 86private:
87 std::unordered_map<PadIdentifier, jobject> input_devices;
88
50 /// Returns the correct identifier corresponding to the player index 89 /// Returns the correct identifier corresponding to the player index
51 PadIdentifier GetIdentifier(std::size_t controller_number) const; 90 PadIdentifier GetIdentifier(const std::string& guid, size_t port) const;
91
92 static constexpr s32 AXIS_X = 0;
93 static constexpr s32 AXIS_Y = 1;
94 static constexpr s32 AXIS_Z = 11;
95 static constexpr s32 AXIS_RX = 12;
96 static constexpr s32 AXIS_RY = 13;
97 static constexpr s32 AXIS_RZ = 14;
98 static constexpr s32 AXIS_HAT_X = 15;
99 static constexpr s32 AXIS_HAT_Y = 16;
100 static constexpr s32 AXIS_LTRIGGER = 17;
101 static constexpr s32 AXIS_RTRIGGER = 18;
102
103 static constexpr s32 KEYCODE_DPAD_UP = 19;
104 static constexpr s32 KEYCODE_DPAD_DOWN = 20;
105 static constexpr s32 KEYCODE_DPAD_LEFT = 21;
106 static constexpr s32 KEYCODE_DPAD_RIGHT = 22;
107 static constexpr s32 KEYCODE_BUTTON_A = 96;
108 static constexpr s32 KEYCODE_BUTTON_B = 97;
109 static constexpr s32 KEYCODE_BUTTON_X = 99;
110 static constexpr s32 KEYCODE_BUTTON_Y = 100;
111 static constexpr s32 KEYCODE_BUTTON_L1 = 102;
112 static constexpr s32 KEYCODE_BUTTON_R1 = 103;
113 static constexpr s32 KEYCODE_BUTTON_L2 = 104;
114 static constexpr s32 KEYCODE_BUTTON_R2 = 105;
115 static constexpr s32 KEYCODE_BUTTON_THUMBL = 106;
116 static constexpr s32 KEYCODE_BUTTON_THUMBR = 107;
117 static constexpr s32 KEYCODE_BUTTON_START = 108;
118 static constexpr s32 KEYCODE_BUTTON_SELECT = 109;
119 const std::vector<s32> keycode_ids{
120 KEYCODE_DPAD_UP, KEYCODE_DPAD_DOWN, KEYCODE_DPAD_LEFT, KEYCODE_DPAD_RIGHT,
121 KEYCODE_BUTTON_A, KEYCODE_BUTTON_B, KEYCODE_BUTTON_X, KEYCODE_BUTTON_Y,
122 KEYCODE_BUTTON_L1, KEYCODE_BUTTON_R1, KEYCODE_BUTTON_L2, KEYCODE_BUTTON_R2,
123 KEYCODE_BUTTON_THUMBL, KEYCODE_BUTTON_THUMBR, KEYCODE_BUTTON_START, KEYCODE_BUTTON_SELECT,
124 };
125
126 const std::string sony_vid{"054c"};
127 const std::string nintendo_vid{"057e"};
128 const std::string razer_vid{"1532"};
129 const std::string redmagic_vid{"3537"};
130 const std::string backbone_labs_vid{"358a"};
131 const std::string xbox_vid{"045e"};
132 const std::vector<std::string> flipped_ab_vids{sony_vid, nintendo_vid, razer_vid,
133 redmagic_vid, backbone_labs_vid, xbox_vid};
134 const std::vector<std::string> flipped_xy_vids{sony_vid, razer_vid, redmagic_vid,
135 backbone_labs_vid, xbox_vid};
52}; 136};
53 137
54} // namespace InputCommon 138} // namespace InputCommon
diff --git a/src/input_common/helpers/joycon_driver.cpp b/src/input_common/helpers/joycon_driver.cpp
index c9f903213..0dd1c958a 100644
--- a/src/input_common/helpers/joycon_driver.cpp
+++ b/src/input_common/helpers/joycon_driver.cpp
@@ -268,7 +268,9 @@ void JoyconDriver::OnNewData(std::span<u8> buffer) {
268} 268}
269 269
270Common::Input::DriverResult JoyconDriver::SetPollingMode() { 270Common::Input::DriverResult JoyconDriver::SetPollingMode() {
271 SCOPE_EXIT({ disable_input_thread = false; }); 271 SCOPE_EXIT {
272 disable_input_thread = false;
273 };
272 disable_input_thread = true; 274 disable_input_thread = true;
273 275
274 rumble_protocol->EnableRumble(vibration_enabled && supported_features.vibration); 276 rumble_protocol->EnableRumble(vibration_enabled && supported_features.vibration);
diff --git a/src/input_common/main.cpp b/src/input_common/main.cpp
index f8749ebbf..62a7ae40f 100644
--- a/src/input_common/main.cpp
+++ b/src/input_common/main.cpp
@@ -4,7 +4,6 @@
4#include <memory> 4#include <memory>
5#include "common/input.h" 5#include "common/input.h"
6#include "common/param_package.h" 6#include "common/param_package.h"
7#include "input_common/drivers/android.h"
8#include "input_common/drivers/camera.h" 7#include "input_common/drivers/camera.h"
9#include "input_common/drivers/keyboard.h" 8#include "input_common/drivers/keyboard.h"
10#include "input_common/drivers/mouse.h" 9#include "input_common/drivers/mouse.h"
@@ -28,6 +27,10 @@
28#include "input_common/drivers/sdl_driver.h" 27#include "input_common/drivers/sdl_driver.h"
29#endif 28#endif
30 29
30#ifdef ANDROID
31#include "input_common/drivers/android.h"
32#endif
33
31namespace InputCommon { 34namespace InputCommon {
32 35
33/// Dummy engine to get periodic updates 36/// Dummy engine to get periodic updates
@@ -79,7 +82,9 @@ struct InputSubsystem::Impl {
79 RegisterEngine("cemuhookudp", udp_client); 82 RegisterEngine("cemuhookudp", udp_client);
80 RegisterEngine("tas", tas_input); 83 RegisterEngine("tas", tas_input);
81 RegisterEngine("camera", camera); 84 RegisterEngine("camera", camera);
85#ifdef ANDROID
82 RegisterEngine("android", android); 86 RegisterEngine("android", android);
87#endif
83 RegisterEngine("virtual_amiibo", virtual_amiibo); 88 RegisterEngine("virtual_amiibo", virtual_amiibo);
84 RegisterEngine("virtual_gamepad", virtual_gamepad); 89 RegisterEngine("virtual_gamepad", virtual_gamepad);
85#ifdef HAVE_SDL2 90#ifdef HAVE_SDL2
@@ -111,7 +116,9 @@ struct InputSubsystem::Impl {
111 UnregisterEngine(udp_client); 116 UnregisterEngine(udp_client);
112 UnregisterEngine(tas_input); 117 UnregisterEngine(tas_input);
113 UnregisterEngine(camera); 118 UnregisterEngine(camera);
119#ifdef ANDROID
114 UnregisterEngine(android); 120 UnregisterEngine(android);
121#endif
115 UnregisterEngine(virtual_amiibo); 122 UnregisterEngine(virtual_amiibo);
116 UnregisterEngine(virtual_gamepad); 123 UnregisterEngine(virtual_gamepad);
117#ifdef HAVE_SDL2 124#ifdef HAVE_SDL2
@@ -128,12 +135,16 @@ struct InputSubsystem::Impl {
128 Common::ParamPackage{{"display", "Any"}, {"engine", "any"}}, 135 Common::ParamPackage{{"display", "Any"}, {"engine", "any"}},
129 }; 136 };
130 137
138#ifndef ANDROID
131 auto keyboard_devices = keyboard->GetInputDevices(); 139 auto keyboard_devices = keyboard->GetInputDevices();
132 devices.insert(devices.end(), keyboard_devices.begin(), keyboard_devices.end()); 140 devices.insert(devices.end(), keyboard_devices.begin(), keyboard_devices.end());
133 auto mouse_devices = mouse->GetInputDevices(); 141 auto mouse_devices = mouse->GetInputDevices();
134 devices.insert(devices.end(), mouse_devices.begin(), mouse_devices.end()); 142 devices.insert(devices.end(), mouse_devices.begin(), mouse_devices.end());
143#endif
144#ifdef ANDROID
135 auto android_devices = android->GetInputDevices(); 145 auto android_devices = android->GetInputDevices();
136 devices.insert(devices.end(), android_devices.begin(), android_devices.end()); 146 devices.insert(devices.end(), android_devices.begin(), android_devices.end());
147#endif
137#ifdef HAVE_LIBUSB 148#ifdef HAVE_LIBUSB
138 auto gcadapter_devices = gcadapter->GetInputDevices(); 149 auto gcadapter_devices = gcadapter->GetInputDevices();
139 devices.insert(devices.end(), gcadapter_devices.begin(), gcadapter_devices.end()); 150 devices.insert(devices.end(), gcadapter_devices.begin(), gcadapter_devices.end());
@@ -162,9 +173,11 @@ struct InputSubsystem::Impl {
162 if (engine == mouse->GetEngineName()) { 173 if (engine == mouse->GetEngineName()) {
163 return mouse; 174 return mouse;
164 } 175 }
176#ifdef ANDROID
165 if (engine == android->GetEngineName()) { 177 if (engine == android->GetEngineName()) {
166 return android; 178 return android;
167 } 179 }
180#endif
168#ifdef HAVE_LIBUSB 181#ifdef HAVE_LIBUSB
169 if (engine == gcadapter->GetEngineName()) { 182 if (engine == gcadapter->GetEngineName()) {
170 return gcadapter; 183 return gcadapter;
@@ -245,9 +258,11 @@ struct InputSubsystem::Impl {
245 if (engine == mouse->GetEngineName()) { 258 if (engine == mouse->GetEngineName()) {
246 return true; 259 return true;
247 } 260 }
261#ifdef ANDROID
248 if (engine == android->GetEngineName()) { 262 if (engine == android->GetEngineName()) {
249 return true; 263 return true;
250 } 264 }
265#endif
251#ifdef HAVE_LIBUSB 266#ifdef HAVE_LIBUSB
252 if (engine == gcadapter->GetEngineName()) { 267 if (engine == gcadapter->GetEngineName()) {
253 return true; 268 return true;
@@ -276,7 +291,9 @@ struct InputSubsystem::Impl {
276 void BeginConfiguration() { 291 void BeginConfiguration() {
277 keyboard->BeginConfiguration(); 292 keyboard->BeginConfiguration();
278 mouse->BeginConfiguration(); 293 mouse->BeginConfiguration();
294#ifdef ANDROID
279 android->BeginConfiguration(); 295 android->BeginConfiguration();
296#endif
280#ifdef HAVE_LIBUSB 297#ifdef HAVE_LIBUSB
281 gcadapter->BeginConfiguration(); 298 gcadapter->BeginConfiguration();
282#endif 299#endif
@@ -290,7 +307,9 @@ struct InputSubsystem::Impl {
290 void EndConfiguration() { 307 void EndConfiguration() {
291 keyboard->EndConfiguration(); 308 keyboard->EndConfiguration();
292 mouse->EndConfiguration(); 309 mouse->EndConfiguration();
310#ifdef ANDROID
293 android->EndConfiguration(); 311 android->EndConfiguration();
312#endif
294#ifdef HAVE_LIBUSB 313#ifdef HAVE_LIBUSB
295 gcadapter->EndConfiguration(); 314 gcadapter->EndConfiguration();
296#endif 315#endif
@@ -321,7 +340,6 @@ struct InputSubsystem::Impl {
321 std::shared_ptr<TasInput::Tas> tas_input; 340 std::shared_ptr<TasInput::Tas> tas_input;
322 std::shared_ptr<CemuhookUDP::UDPClient> udp_client; 341 std::shared_ptr<CemuhookUDP::UDPClient> udp_client;
323 std::shared_ptr<Camera> camera; 342 std::shared_ptr<Camera> camera;
324 std::shared_ptr<Android> android;
325 std::shared_ptr<VirtualAmiibo> virtual_amiibo; 343 std::shared_ptr<VirtualAmiibo> virtual_amiibo;
326 std::shared_ptr<VirtualGamepad> virtual_gamepad; 344 std::shared_ptr<VirtualGamepad> virtual_gamepad;
327 345
@@ -333,6 +351,10 @@ struct InputSubsystem::Impl {
333 std::shared_ptr<SDLDriver> sdl; 351 std::shared_ptr<SDLDriver> sdl;
334 std::shared_ptr<Joycons> joycon; 352 std::shared_ptr<Joycons> joycon;
335#endif 353#endif
354
355#ifdef ANDROID
356 std::shared_ptr<Android> android;
357#endif
336}; 358};
337 359
338InputSubsystem::InputSubsystem() : impl{std::make_unique<Impl>()} {} 360InputSubsystem::InputSubsystem() : impl{std::make_unique<Impl>()} {}
@@ -387,6 +409,7 @@ const Camera* InputSubsystem::GetCamera() const {
387 return impl->camera.get(); 409 return impl->camera.get();
388} 410}
389 411
412#ifdef ANDROID
390Android* InputSubsystem::GetAndroid() { 413Android* InputSubsystem::GetAndroid() {
391 return impl->android.get(); 414 return impl->android.get();
392} 415}
@@ -394,6 +417,7 @@ Android* InputSubsystem::GetAndroid() {
394const Android* InputSubsystem::GetAndroid() const { 417const Android* InputSubsystem::GetAndroid() const {
395 return impl->android.get(); 418 return impl->android.get();
396} 419}
420#endif
397 421
398VirtualAmiibo* InputSubsystem::GetVirtualAmiibo() { 422VirtualAmiibo* InputSubsystem::GetVirtualAmiibo() {
399 return impl->virtual_amiibo.get(); 423 return impl->virtual_amiibo.get();
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp
index 44281e407..945cdb42b 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp
@@ -60,11 +60,10 @@ public:
60 Add(spv::ImageOperandsMask::ConstOffsets, offsets); 60 Add(spv::ImageOperandsMask::ConstOffsets, offsets);
61 } 61 }
62 62
63 explicit ImageOperands(EmitContext& ctx, const IR::Value& offset, Id lod, Id ms) { 63 explicit ImageOperands(Id lod, Id ms) {
64 if (Sirit::ValidId(lod)) { 64 if (Sirit::ValidId(lod)) {
65 Add(spv::ImageOperandsMask::Lod, lod); 65 Add(spv::ImageOperandsMask::Lod, lod);
66 } 66 }
67 AddOffset(ctx, offset, ImageFetchOffsetAllowed);
68 if (Sirit::ValidId(ms)) { 67 if (Sirit::ValidId(ms)) {
69 Add(spv::ImageOperandsMask::Sample, ms); 68 Add(spv::ImageOperandsMask::Sample, ms);
70 } 69 }
@@ -312,6 +311,43 @@ Id ImageGatherSubpixelOffset(EmitContext& ctx, const IR::TextureInstInfo& info,
312 return coords; 311 return coords;
313 } 312 }
314} 313}
314
315void AddOffsetToCoordinates(EmitContext& ctx, const IR::TextureInstInfo& info, Id& coords,
316 Id offset) {
317 if (!Sirit::ValidId(offset)) {
318 return;
319 }
320
321 Id result_type{};
322 switch (info.type) {
323 case TextureType::Buffer:
324 case TextureType::Color1D: {
325 result_type = ctx.U32[1];
326 break;
327 }
328 case TextureType::ColorArray1D:
329 offset = ctx.OpCompositeConstruct(ctx.U32[2], offset, ctx.u32_zero_value);
330 [[fallthrough]];
331 case TextureType::Color2D:
332 case TextureType::Color2DRect: {
333 result_type = ctx.U32[2];
334 break;
335 }
336 case TextureType::ColorArray2D:
337 offset = ctx.OpCompositeConstruct(ctx.U32[3], ctx.OpCompositeExtract(ctx.U32[1], coords, 0),
338 ctx.OpCompositeExtract(ctx.U32[1], coords, 1),
339 ctx.u32_zero_value);
340 [[fallthrough]];
341 case TextureType::Color3D: {
342 result_type = ctx.U32[3];
343 break;
344 }
345 case TextureType::ColorCube:
346 case TextureType::ColorArrayCube:
347 return;
348 }
349 coords = ctx.OpIAdd(result_type, coords, offset);
350}
315} // Anonymous namespace 351} // Anonymous namespace
316 352
317Id EmitBindlessImageSampleImplicitLod(EmitContext&) { 353Id EmitBindlessImageSampleImplicitLod(EmitContext&) {
@@ -494,9 +530,10 @@ Id EmitImageGatherDref(EmitContext& ctx, IR::Inst* inst, const IR::Value& index,
494 operands.Span()); 530 operands.Span());
495} 531}
496 532
497Id EmitImageFetch(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, 533Id EmitImageFetch(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, Id offset,
498 const IR::Value& offset, Id lod, Id ms) { 534 Id lod, Id ms) {
499 const auto info{inst->Flags<IR::TextureInstInfo>()}; 535 const auto info{inst->Flags<IR::TextureInstInfo>()};
536 AddOffsetToCoordinates(ctx, info, coords, offset);
500 if (info.type == TextureType::Buffer) { 537 if (info.type == TextureType::Buffer) {
501 lod = Id{}; 538 lod = Id{};
502 } 539 }
@@ -504,7 +541,7 @@ Id EmitImageFetch(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id c
504 // This image is multisampled, lod must be implicit 541 // This image is multisampled, lod must be implicit
505 lod = Id{}; 542 lod = Id{};
506 } 543 }
507 const ImageOperands operands(ctx, offset, lod, ms); 544 const ImageOperands operands(lod, ms);
508 return Emit(&EmitContext::OpImageSparseFetch, &EmitContext::OpImageFetch, ctx, inst, ctx.F32[4], 545 return Emit(&EmitContext::OpImageSparseFetch, &EmitContext::OpImageFetch, ctx, inst, ctx.F32[4],
509 TextureImage(ctx, info, index), coords, operands.MaskOptional(), operands.Span()); 546 TextureImage(ctx, info, index), coords, operands.MaskOptional(), operands.Span());
510} 547}
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h b/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h
index 08fcabd58..5c01b1012 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h
@@ -537,8 +537,8 @@ Id EmitImageGather(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id
537 const IR::Value& offset, const IR::Value& offset2); 537 const IR::Value& offset, const IR::Value& offset2);
538Id EmitImageGatherDref(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, 538Id EmitImageGatherDref(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
539 const IR::Value& offset, const IR::Value& offset2, Id dref); 539 const IR::Value& offset, const IR::Value& offset2, Id dref);
540Id EmitImageFetch(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, 540Id EmitImageFetch(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, Id offset,
541 const IR::Value& offset, Id lod, Id ms); 541 Id lod, Id ms);
542Id EmitImageQueryDimensions(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id lod, 542Id EmitImageQueryDimensions(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id lod,
543 const IR::Value& skip_mips); 543 const IR::Value& skip_mips);
544Id EmitImageQueryLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords); 544Id EmitImageQueryLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords);
diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h
index 6d3d933c5..296c90e85 100644
--- a/src/video_core/buffer_cache/buffer_cache.h
+++ b/src/video_core/buffer_cache/buffer_cache.h
@@ -1130,7 +1130,7 @@ void BufferCache<P>::UpdateVertexBuffer(u32 index) {
1130 channel_state->vertex_buffers[index] = NULL_BINDING; 1130 channel_state->vertex_buffers[index] = NULL_BINDING;
1131 return; 1131 return;
1132 } 1132 }
1133 if (!gpu_memory->IsWithinGPUAddressRange(gpu_addr_end)) { 1133 if (!gpu_memory->IsWithinGPUAddressRange(gpu_addr_end) || size >= 64_MiB) {
1134 size = static_cast<u32>(gpu_memory->MaxContinuousRange(gpu_addr_begin, size)); 1134 size = static_cast<u32>(gpu_memory->MaxContinuousRange(gpu_addr_begin, size));
1135 } 1135 }
1136 const BufferId buffer_id = FindBuffer(*device_addr, size); 1136 const BufferId buffer_id = FindBuffer(*device_addr, size);
diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp
index a94e1f043..0d47b032c 100644
--- a/src/video_core/engines/maxwell_3d.cpp
+++ b/src/video_core/engines/maxwell_3d.cpp
@@ -291,7 +291,9 @@ u32 Maxwell3D::ProcessShadowRam(u32 method, u32 argument) {
291} 291}
292 292
293void Maxwell3D::ConsumeSinkImpl() { 293void Maxwell3D::ConsumeSinkImpl() {
294 SCOPE_EXIT({ method_sink.clear(); }); 294 SCOPE_EXIT {
295 method_sink.clear();
296 };
295 const auto control = shadow_state.shadow_ram_control; 297 const auto control = shadow_state.shadow_ram_control;
296 if (control == Regs::ShadowRamControl::Track || 298 if (control == Regs::ShadowRamControl::Track ||
297 control == Regs::ShadowRamControl::TrackWithFilter) { 299 control == Regs::ShadowRamControl::TrackWithFilter) {
diff --git a/src/video_core/fence_manager.h b/src/video_core/fence_manager.h
index c3eda6893..2135f1f2d 100644
--- a/src/video_core/fence_manager.h
+++ b/src/video_core/fence_manager.h
@@ -197,7 +197,9 @@ private:
197 MicroProfileOnThreadCreate(name.c_str()); 197 MicroProfileOnThreadCreate(name.c_str());
198 198
199 // Cleanup 199 // Cleanup
200 SCOPE_EXIT({ MicroProfileOnThreadExit(); }); 200 SCOPE_EXIT {
201 MicroProfileOnThreadExit();
202 };
201 203
202 Common::SetCurrentThreadName(name.c_str()); 204 Common::SetCurrentThreadName(name.c_str());
203 Common::SetCurrentThreadPriority(Common::ThreadPriority::High); 205 Common::SetCurrentThreadPriority(Common::ThreadPriority::High);
diff --git a/src/video_core/gpu_thread.cpp b/src/video_core/gpu_thread.cpp
index 58d8110b8..477e11457 100644
--- a/src/video_core/gpu_thread.cpp
+++ b/src/video_core/gpu_thread.cpp
@@ -22,7 +22,9 @@ static void RunThread(std::stop_token stop_token, Core::System& system,
22 Tegra::Control::Scheduler& scheduler, SynchState& state) { 22 Tegra::Control::Scheduler& scheduler, SynchState& state) {
23 std::string name = "GPU"; 23 std::string name = "GPU";
24 MicroProfileOnThreadCreate(name.c_str()); 24 MicroProfileOnThreadCreate(name.c_str());
25 SCOPE_EXIT({ MicroProfileOnThreadExit(); }); 25 SCOPE_EXIT {
26 MicroProfileOnThreadExit();
27 };
26 28
27 Common::SetCurrentThreadName(name.c_str()); 29 Common::SetCurrentThreadName(name.c_str());
28 Common::SetCurrentThreadPriority(Common::ThreadPriority::Critical); 30 Common::SetCurrentThreadPriority(Common::ThreadPriority::Critical);
diff --git a/src/video_core/host1x/ffmpeg/ffmpeg.cpp b/src/video_core/host1x/ffmpeg/ffmpeg.cpp
index 96686da59..1003cd38d 100644
--- a/src/video_core/host1x/ffmpeg/ffmpeg.cpp
+++ b/src/video_core/host1x/ffmpeg/ffmpeg.cpp
@@ -273,10 +273,10 @@ DeinterlaceFilter::DeinterlaceFilter(const Frame& frame) {
273 const AVFilter* buffer_sink = avfilter_get_by_name("buffersink"); 273 const AVFilter* buffer_sink = avfilter_get_by_name("buffersink");
274 AVFilterInOut* inputs = avfilter_inout_alloc(); 274 AVFilterInOut* inputs = avfilter_inout_alloc();
275 AVFilterInOut* outputs = avfilter_inout_alloc(); 275 AVFilterInOut* outputs = avfilter_inout_alloc();
276 SCOPE_EXIT({ 276 SCOPE_EXIT {
277 avfilter_inout_free(&inputs); 277 avfilter_inout_free(&inputs);
278 avfilter_inout_free(&outputs); 278 avfilter_inout_free(&outputs);
279 }); 279 };
280 280
281 // Don't know how to get the accurate time_base but it doesn't matter for yadif filter 281 // Don't know how to get the accurate time_base but it doesn't matter for yadif filter
282 // so just use 1/1 to make buffer filter happy 282 // so just use 1/1 to make buffer filter happy
diff --git a/src/video_core/macro/macro_hle.cpp b/src/video_core/macro/macro_hle.cpp
index 46e853e04..fb529f88b 100644
--- a/src/video_core/macro/macro_hle.cpp
+++ b/src/video_core/macro/macro_hle.cpp
@@ -92,12 +92,12 @@ public:
92 92
93private: 93private:
94 void Fallback(const std::vector<u32>& parameters) { 94 void Fallback(const std::vector<u32>& parameters) {
95 SCOPE_EXIT({ 95 SCOPE_EXIT {
96 if (extended) { 96 if (extended) {
97 maxwell3d.engine_state = Maxwell3D::EngineHint::None; 97 maxwell3d.engine_state = Maxwell3D::EngineHint::None;
98 maxwell3d.replace_table.clear(); 98 maxwell3d.replace_table.clear();
99 } 99 }
100 }); 100 };
101 maxwell3d.RefreshParameters(); 101 maxwell3d.RefreshParameters();
102 const u32 instance_count = (maxwell3d.GetRegisterValue(0xD1B) & parameters[2]); 102 const u32 instance_count = (maxwell3d.GetRegisterValue(0xD1B) & parameters[2]);
103 103
@@ -281,12 +281,12 @@ public:
281 281
282private: 282private:
283 void Fallback(const std::vector<u32>& parameters) { 283 void Fallback(const std::vector<u32>& parameters) {
284 SCOPE_EXIT({ 284 SCOPE_EXIT {
285 // Clean everything. 285 // Clean everything.
286 maxwell3d.regs.vertex_id_base = 0x0; 286 maxwell3d.regs.vertex_id_base = 0x0;
287 maxwell3d.engine_state = Maxwell3D::EngineHint::None; 287 maxwell3d.engine_state = Maxwell3D::EngineHint::None;
288 maxwell3d.replace_table.clear(); 288 maxwell3d.replace_table.clear();
289 }); 289 };
290 maxwell3d.RefreshParameters(); 290 maxwell3d.RefreshParameters();
291 const u32 start_indirect = parameters[0]; 291 const u32 start_indirect = parameters[0];
292 const u32 end_indirect = parameters[1]; 292 const u32 end_indirect = parameters[1];
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index b42fb110c..16af8e6bd 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -230,7 +230,9 @@ template <typename Func>
230void RasterizerOpenGL::PrepareDraw(bool is_indexed, Func&& draw_func) { 230void RasterizerOpenGL::PrepareDraw(bool is_indexed, Func&& draw_func) {
231 MICROPROFILE_SCOPE(OpenGL_Drawing); 231 MICROPROFILE_SCOPE(OpenGL_Drawing);
232 232
233 SCOPE_EXIT({ gpu.TickWork(); }); 233 SCOPE_EXIT {
234 gpu.TickWork();
235 };
234 gpu_memory->FlushCaching(); 236 gpu_memory->FlushCaching();
235 237
236 GraphicsPipeline* const pipeline{shader_cache.CurrentGraphicsPipeline()}; 238 GraphicsPipeline* const pipeline{shader_cache.CurrentGraphicsPipeline()};
@@ -355,7 +357,9 @@ void RasterizerOpenGL::DrawIndirect() {
355void RasterizerOpenGL::DrawTexture() { 357void RasterizerOpenGL::DrawTexture() {
356 MICROPROFILE_SCOPE(OpenGL_Drawing); 358 MICROPROFILE_SCOPE(OpenGL_Drawing);
357 359
358 SCOPE_EXIT({ gpu.TickWork(); }); 360 SCOPE_EXIT {
361 gpu.TickWork();
362 };
359 363
360 texture_cache.SynchronizeGraphicsDescriptors(); 364 texture_cache.SynchronizeGraphicsDescriptors();
361 texture_cache.UpdateRenderTargets(false); 365 texture_cache.UpdateRenderTargets(false);
diff --git a/src/video_core/renderer_vulkan/present/layer.cpp b/src/video_core/renderer_vulkan/present/layer.cpp
index 3847a9a13..4e41afe5b 100644
--- a/src/video_core/renderer_vulkan/present/layer.cpp
+++ b/src/video_core/renderer_vulkan/present/layer.cpp
@@ -82,7 +82,9 @@ void Layer::ConfigureDraw(PresentPushConstants* out_push_constants,
82 // Finish any pending renderpass 82 // Finish any pending renderpass
83 scheduler.RequestOutsideRenderPassOperationContext(); 83 scheduler.RequestOutsideRenderPassOperationContext();
84 scheduler.Wait(resource_ticks[image_index]); 84 scheduler.Wait(resource_ticks[image_index]);
85 SCOPE_EXIT({ resource_ticks[image_index] = scheduler.CurrentTick(); }); 85 SCOPE_EXIT {
86 resource_ticks[image_index] = scheduler.CurrentTick();
87 };
86 88
87 if (!use_accelerated) { 89 if (!use_accelerated) {
88 UpdateRawImage(framebuffer, image_index); 90 UpdateRawImage(framebuffer, image_index);
diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.cpp b/src/video_core/renderer_vulkan/renderer_vulkan.cpp
index d50417116..c553f5b3d 100644
--- a/src/video_core/renderer_vulkan/renderer_vulkan.cpp
+++ b/src/video_core/renderer_vulkan/renderer_vulkan.cpp
@@ -144,7 +144,9 @@ void RendererVulkan::Composite(std::span<const Tegra::FramebufferConfig> framebu
144 return; 144 return;
145 } 145 }
146 146
147 SCOPE_EXIT({ render_window.OnFrameDisplayed(); }); 147 SCOPE_EXIT {
148 render_window.OnFrameDisplayed();
149 };
148 150
149 RenderAppletCaptureLayer(framebuffers); 151 RenderAppletCaptureLayer(framebuffers);
150 152
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
index aa0a027bb..74f9f099e 100644
--- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp
+++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
@@ -196,7 +196,9 @@ template <typename Func>
196void RasterizerVulkan::PrepareDraw(bool is_indexed, Func&& draw_func) { 196void RasterizerVulkan::PrepareDraw(bool is_indexed, Func&& draw_func) {
197 MICROPROFILE_SCOPE(Vulkan_Drawing); 197 MICROPROFILE_SCOPE(Vulkan_Drawing);
198 198
199 SCOPE_EXIT({ gpu.TickWork(); }); 199 SCOPE_EXIT {
200 gpu.TickWork();
201 };
200 FlushWork(); 202 FlushWork();
201 gpu_memory->FlushCaching(); 203 gpu_memory->FlushCaching();
202 204
@@ -288,7 +290,9 @@ void RasterizerVulkan::DrawIndirect() {
288void RasterizerVulkan::DrawTexture() { 290void RasterizerVulkan::DrawTexture() {
289 MICROPROFILE_SCOPE(Vulkan_Drawing); 291 MICROPROFILE_SCOPE(Vulkan_Drawing);
290 292
291 SCOPE_EXIT({ gpu.TickWork(); }); 293 SCOPE_EXIT {
294 gpu.TickWork();
295 };
292 FlushWork(); 296 FlushWork();
293 297
294 query_cache.NotifySegment(true); 298 query_cache.NotifySegment(true);
diff --git a/src/video_core/vulkan_common/nsight_aftermath_tracker.cpp b/src/video_core/vulkan_common/nsight_aftermath_tracker.cpp
index 5fa0d9620..f41c3e506 100644
--- a/src/video_core/vulkan_common/nsight_aftermath_tracker.cpp
+++ b/src/video_core/vulkan_common/nsight_aftermath_tracker.cpp
@@ -116,7 +116,9 @@ void NsightAftermathTracker::OnGpuCrashDumpCallback(const void* gpu_crash_dump,
116 LOG_ERROR(Render_Vulkan, "Failed to create decoder"); 116 LOG_ERROR(Render_Vulkan, "Failed to create decoder");
117 return; 117 return;
118 } 118 }
119 SCOPE_EXIT({ GFSDK_Aftermath_GpuCrashDump_DestroyDecoder(decoder); }); 119 SCOPE_EXIT {
120 GFSDK_Aftermath_GpuCrashDump_DestroyDecoder(decoder);
121 };
120 122
121 u32 json_size = 0; 123 u32 json_size = 0;
122 if (!GFSDK_Aftermath_SUCCEED(GFSDK_Aftermath_GpuCrashDump_GenerateJSON( 124 if (!GFSDK_Aftermath_SUCCEED(GFSDK_Aftermath_GpuCrashDump_GenerateJSON(
diff --git a/src/yuzu/configuration/qt_config.cpp b/src/yuzu/configuration/qt_config.cpp
index 1051031f2..37951b9c8 100644
--- a/src/yuzu/configuration/qt_config.cpp
+++ b/src/yuzu/configuration/qt_config.cpp
@@ -90,6 +90,7 @@ void QtConfig::ReadQtPlayerValues(const std::size_t player_index) {
90 if (profile_name.empty()) { 90 if (profile_name.empty()) {
91 // Use the global input config 91 // Use the global input config
92 player = Settings::values.players.GetValue(true)[player_index]; 92 player = Settings::values.players.GetValue(true)[player_index];
93 player.profile_name = "";
93 return; 94 return;
94 } 95 }
95 } 96 }
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index dfa50006a..236642fb9 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -646,10 +646,10 @@ void GMainWindow::AmiiboSettingsShowDialog(const Core::Frontend::CabinetParamete
646 std::shared_ptr<Service::NFC::NfcDevice> nfp_device) { 646 std::shared_ptr<Service::NFC::NfcDevice> nfp_device) {
647 cabinet_applet = 647 cabinet_applet =
648 new QtAmiiboSettingsDialog(this, parameters, input_subsystem.get(), nfp_device); 648 new QtAmiiboSettingsDialog(this, parameters, input_subsystem.get(), nfp_device);
649 SCOPE_EXIT({ 649 SCOPE_EXIT {
650 cabinet_applet->deleteLater(); 650 cabinet_applet->deleteLater();
651 cabinet_applet = nullptr; 651 cabinet_applet = nullptr;
652 }); 652 };
653 653
654 cabinet_applet->setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowStaysOnTopHint | 654 cabinet_applet->setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowStaysOnTopHint |
655 Qt::WindowTitleHint | Qt::WindowSystemMenuHint); 655 Qt::WindowTitleHint | Qt::WindowSystemMenuHint);
@@ -673,10 +673,10 @@ void GMainWindow::ControllerSelectorReconfigureControllers(
673 const Core::Frontend::ControllerParameters& parameters) { 673 const Core::Frontend::ControllerParameters& parameters) {
674 controller_applet = 674 controller_applet =
675 new QtControllerSelectorDialog(this, parameters, input_subsystem.get(), *system); 675 new QtControllerSelectorDialog(this, parameters, input_subsystem.get(), *system);
676 SCOPE_EXIT({ 676 SCOPE_EXIT {
677 controller_applet->deleteLater(); 677 controller_applet->deleteLater();
678 controller_applet = nullptr; 678 controller_applet = nullptr;
679 }); 679 };
680 680
681 controller_applet->setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | 681 controller_applet->setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint |
682 Qt::WindowStaysOnTopHint | Qt::WindowTitleHint | 682 Qt::WindowStaysOnTopHint | Qt::WindowTitleHint |
@@ -703,10 +703,10 @@ void GMainWindow::ControllerSelectorRequestExit() {
703void GMainWindow::ProfileSelectorSelectProfile( 703void GMainWindow::ProfileSelectorSelectProfile(
704 const Core::Frontend::ProfileSelectParameters& parameters) { 704 const Core::Frontend::ProfileSelectParameters& parameters) {
705 profile_select_applet = new QtProfileSelectionDialog(*system, this, parameters); 705 profile_select_applet = new QtProfileSelectionDialog(*system, this, parameters);
706 SCOPE_EXIT({ 706 SCOPE_EXIT {
707 profile_select_applet->deleteLater(); 707 profile_select_applet->deleteLater();
708 profile_select_applet = nullptr; 708 profile_select_applet = nullptr;
709 }); 709 };
710 710
711 profile_select_applet->setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | 711 profile_select_applet->setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint |
712 Qt::WindowStaysOnTopHint | Qt::WindowTitleHint | 712 Qt::WindowStaysOnTopHint | Qt::WindowTitleHint |
@@ -1603,6 +1603,7 @@ void GMainWindow::ConnectMenuEvents() {
1603 // Help 1603 // Help
1604 connect_menu(ui->action_Open_yuzu_Folder, &GMainWindow::OnOpenYuzuFolder); 1604 connect_menu(ui->action_Open_yuzu_Folder, &GMainWindow::OnOpenYuzuFolder);
1605 connect_menu(ui->action_Verify_installed_contents, &GMainWindow::OnVerifyInstalledContents); 1605 connect_menu(ui->action_Verify_installed_contents, &GMainWindow::OnVerifyInstalledContents);
1606 connect_menu(ui->action_Install_Firmware, &GMainWindow::OnInstallFirmware);
1606 connect_menu(ui->action_About, &GMainWindow::OnAbout); 1607 connect_menu(ui->action_About, &GMainWindow::OnAbout);
1607} 1608}
1608 1609
@@ -1631,6 +1632,8 @@ void GMainWindow::UpdateMenuState() {
1631 action->setEnabled(emulation_running); 1632 action->setEnabled(emulation_running);
1632 } 1633 }
1633 1634
1635 ui->action_Install_Firmware->setEnabled(!emulation_running);
1636
1634 for (QAction* action : applet_actions) { 1637 for (QAction* action : applet_actions) {
1635 action->setEnabled(is_firmware_available && !emulation_running); 1638 action->setEnabled(is_firmware_available && !emulation_running);
1636 } 1639 }
@@ -2882,17 +2885,19 @@ bool GMainWindow::CreateShortcutLink(const std::filesystem::path& shortcut_path,
2882 LOG_ERROR(Frontend, "CoInitialize failed"); 2885 LOG_ERROR(Frontend, "CoInitialize failed");
2883 return false; 2886 return false;
2884 } 2887 }
2885 SCOPE_EXIT({ CoUninitialize(); }); 2888 SCOPE_EXIT {
2889 CoUninitialize();
2890 };
2886 IShellLinkW* ps1 = nullptr; 2891 IShellLinkW* ps1 = nullptr;
2887 IPersistFile* persist_file = nullptr; 2892 IPersistFile* persist_file = nullptr;
2888 SCOPE_EXIT({ 2893 SCOPE_EXIT {
2889 if (persist_file != nullptr) { 2894 if (persist_file != nullptr) {
2890 persist_file->Release(); 2895 persist_file->Release();
2891 } 2896 }
2892 if (ps1 != nullptr) { 2897 if (ps1 != nullptr) {
2893 ps1->Release(); 2898 ps1->Release();
2894 } 2899 }
2895 }); 2900 };
2896 HRESULT hres = CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER, IID_IShellLinkW, 2901 HRESULT hres = CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER, IID_IShellLinkW,
2897 reinterpret_cast<void**>(&ps1)); 2902 reinterpret_cast<void**>(&ps1));
2898 if (FAILED(hres)) { 2903 if (FAILED(hres)) {
@@ -3517,10 +3522,10 @@ void GMainWindow::OnSaveConfig() {
3517void GMainWindow::ErrorDisplayDisplayError(QString error_code, QString error_text) { 3522void GMainWindow::ErrorDisplayDisplayError(QString error_code, QString error_text) {
3518 error_applet = new OverlayDialog(render_window, *system, error_code, error_text, QString{}, 3523 error_applet = new OverlayDialog(render_window, *system, error_code, error_text, QString{},
3519 tr("OK"), Qt::AlignLeft | Qt::AlignVCenter); 3524 tr("OK"), Qt::AlignLeft | Qt::AlignVCenter);
3520 SCOPE_EXIT({ 3525 SCOPE_EXIT {
3521 error_applet->deleteLater(); 3526 error_applet->deleteLater();
3522 error_applet = nullptr; 3527 error_applet = nullptr;
3523 }); 3528 };
3524 error_applet->exec(); 3529 error_applet->exec();
3525 3530
3526 emit ErrorDisplayFinished(); 3531 emit ErrorDisplayFinished();
@@ -4150,6 +4155,146 @@ void GMainWindow::OnVerifyInstalledContents() {
4150 } 4155 }
4151} 4156}
4152 4157
4158void GMainWindow::OnInstallFirmware() {
4159 // Don't do this while emulation is running, that'd probably be a bad idea.
4160 if (emu_thread != nullptr && emu_thread->IsRunning()) {
4161 return;
4162 }
4163
4164 // Check for installed keys, error out, suggest restart?
4165 if (!ContentManager::AreKeysPresent()) {
4166 QMessageBox::information(
4167 this, tr("Keys not installed"),
4168 tr("Install decryption keys and restart yuzu before attempting to install firmware."));
4169 return;
4170 }
4171
4172 QString firmware_source_location =
4173 QFileDialog::getExistingDirectory(this, tr("Select Dumped Firmware Source Location"),
4174 QString::fromStdString(""), QFileDialog::ShowDirsOnly);
4175 if (firmware_source_location.isEmpty()) {
4176 return;
4177 }
4178
4179 QProgressDialog progress(tr("Installing Firmware..."), tr("Cancel"), 0, 100, this);
4180 progress.setWindowModality(Qt::WindowModal);
4181 progress.setMinimumDuration(100);
4182 progress.setAutoClose(false);
4183 progress.setAutoReset(false);
4184 progress.show();
4185
4186 // Declare progress callback.
4187 auto QtProgressCallback = [&](size_t total_size, size_t processed_size) {
4188 progress.setValue(static_cast<int>((processed_size * 100) / total_size));
4189 return progress.wasCanceled();
4190 };
4191
4192 LOG_INFO(Frontend, "Installing firmware from {}", firmware_source_location.toStdString());
4193
4194 // Check for a reasonable number of .nca files (don't hardcode them, just see if there's some in
4195 // there.)
4196 std::filesystem::path firmware_source_path = firmware_source_location.toStdString();
4197 if (!Common::FS::IsDir(firmware_source_path)) {
4198 progress.close();
4199 return;
4200 }
4201
4202 std::vector<std::filesystem::path> out;
4203 const Common::FS::DirEntryCallable callback =
4204 [&out](const std::filesystem::directory_entry& entry) {
4205 if (entry.path().has_extension() && entry.path().extension() == ".nca")
4206 out.emplace_back(entry.path());
4207
4208 return true;
4209 };
4210
4211 QtProgressCallback(100, 10);
4212
4213 Common::FS::IterateDirEntries(firmware_source_path, callback, Common::FS::DirEntryFilter::File);
4214 if (out.size() <= 0) {
4215 progress.close();
4216 QMessageBox::warning(this, tr("Firmware install failed"),
4217 tr("Unable to locate potential firmware NCA files"));
4218 return;
4219 }
4220
4221 // Locate and erase the content of nand/system/Content/registered/*.nca, if any.
4222 auto sysnand_content_vdir = system->GetFileSystemController().GetSystemNANDContentDirectory();
4223 if (!sysnand_content_vdir->CleanSubdirectoryRecursive("registered")) {
4224 progress.close();
4225 QMessageBox::critical(this, tr("Firmware install failed"),
4226 tr("Failed to delete one or more firmware file."));
4227 return;
4228 }
4229
4230 LOG_INFO(Frontend,
4231 "Cleaned nand/system/Content/registered folder in preparation for new firmware.");
4232
4233 QtProgressCallback(100, 20);
4234
4235 auto firmware_vdir = sysnand_content_vdir->GetDirectoryRelative("registered");
4236
4237 bool success = true;
4238 bool cancelled = false;
4239 int i = 0;
4240 for (const auto& firmware_src_path : out) {
4241 i++;
4242 auto firmware_src_vfile =
4243 vfs->OpenFile(firmware_src_path.generic_string(), FileSys::OpenMode::Read);
4244 auto firmware_dst_vfile =
4245 firmware_vdir->CreateFileRelative(firmware_src_path.filename().string());
4246
4247 if (!VfsRawCopy(firmware_src_vfile, firmware_dst_vfile)) {
4248 LOG_ERROR(Frontend, "Failed to copy firmware file {} to {} in registered folder!",
4249 firmware_src_path.generic_string(), firmware_src_path.filename().string());
4250 success = false;
4251 }
4252
4253 if (QtProgressCallback(100, 20 + (int)(((float)(i) / (float)out.size()) * 70.0))) {
4254 success = false;
4255 cancelled = true;
4256 break;
4257 }
4258 }
4259
4260 if (!success && !cancelled) {
4261 progress.close();
4262 QMessageBox::critical(this, tr("Firmware install failed"),
4263 tr("One or more firmware files failed to copy into NAND."));
4264 return;
4265 } else if (cancelled) {
4266 progress.close();
4267 QMessageBox::warning(this, tr("Firmware install failed"),
4268 tr("Firmware installation cancelled, firmware may be in bad state, "
4269 "restart yuzu or re-install firmware."));
4270 return;
4271 }
4272
4273 // Re-scan VFS for the newly placed firmware files.
4274 system->GetFileSystemController().CreateFactories(*vfs);
4275
4276 auto VerifyFirmwareCallback = [&](size_t total_size, size_t processed_size) {
4277 progress.setValue(90 + static_cast<int>((processed_size * 10) / total_size));
4278 return progress.wasCanceled();
4279 };
4280
4281 auto result =
4282 ContentManager::VerifyInstalledContents(*system, *provider, VerifyFirmwareCallback, true);
4283
4284 if (result.size() > 0) {
4285 const auto failed_names =
4286 QString::fromStdString(fmt::format("{}", fmt::join(result, "\n")));
4287 progress.close();
4288 QMessageBox::critical(
4289 this, tr("Firmware integrity verification failed!"),
4290 tr("Verification failed for the following files:\n\n%1").arg(failed_names));
4291 return;
4292 }
4293
4294 progress.close();
4295 OnCheckFirmwareDecryption();
4296}
4297
4153void GMainWindow::OnAbout() { 4298void GMainWindow::OnAbout() {
4154 AboutDialog aboutDialog(this); 4299 AboutDialog aboutDialog(this);
4155 aboutDialog.exec(); 4300 aboutDialog.exec();
@@ -5049,7 +5194,9 @@ int main(int argc, char* argv[]) {
5049 5194
5050 Common::DetachedTasks detached_tasks; 5195 Common::DetachedTasks detached_tasks;
5051 MicroProfileOnThreadCreate("Frontend"); 5196 MicroProfileOnThreadCreate("Frontend");
5052 SCOPE_EXIT({ MicroProfileShutdown(); }); 5197 SCOPE_EXIT {
5198 MicroProfileShutdown();
5199 };
5053 5200
5054 Common::ConfigureNvidiaEnvironmentFlags(); 5201 Common::ConfigureNvidiaEnvironmentFlags();
5055 5202
diff --git a/src/yuzu/main.h b/src/yuzu/main.h
index aba61e388..1f0e35c67 100644
--- a/src/yuzu/main.h
+++ b/src/yuzu/main.h
@@ -380,6 +380,7 @@ private slots:
380 void OnLoadAmiibo(); 380 void OnLoadAmiibo();
381 void OnOpenYuzuFolder(); 381 void OnOpenYuzuFolder();
382 void OnVerifyInstalledContents(); 382 void OnVerifyInstalledContents();
383 void OnInstallFirmware();
383 void OnAbout(); 384 void OnAbout();
384 void OnToggleFilterBar(); 385 void OnToggleFilterBar();
385 void OnToggleStatusBar(); 386 void OnToggleStatusBar();
diff --git a/src/yuzu/main.ui b/src/yuzu/main.ui
index 6a6b0821f..6ff444a22 100644
--- a/src/yuzu/main.ui
+++ b/src/yuzu/main.ui
@@ -25,7 +25,16 @@
25 </property> 25 </property>
26 <widget class="QWidget" name="centralwidget"> 26 <widget class="QWidget" name="centralwidget">
27 <layout class="QHBoxLayout" name="horizontalLayout"> 27 <layout class="QHBoxLayout" name="horizontalLayout">
28 <property name="margin" stdset="0"> 28 <property name="leftMargin">
29 <number>0</number>
30 </property>
31 <property name="topMargin">
32 <number>0</number>
33 </property>
34 <property name="rightMargin">
35 <number>0</number>
36 </property>
37 <property name="bottomMargin">
29 <number>0</number> 38 <number>0</number>
30 </property> 39 </property>
31 </layout> 40 </layout>
@@ -156,8 +165,8 @@
156 <addaction name="separator"/> 165 <addaction name="separator"/>
157 <addaction name="action_Configure_Tas"/> 166 <addaction name="action_Configure_Tas"/>
158 </widget> 167 </widget>
159 <addaction name="action_Rederive"/>
160 <addaction name="action_Verify_installed_contents"/> 168 <addaction name="action_Verify_installed_contents"/>
169 <addaction name="action_Install_Firmware"/>
161 <addaction name="separator"/> 170 <addaction name="separator"/>
162 <addaction name="menu_cabinet_applet"/> 171 <addaction name="menu_cabinet_applet"/>
163 <addaction name="action_Load_Album"/> 172 <addaction name="action_Load_Album"/>
@@ -455,6 +464,11 @@
455 <string>Open &amp;Controller Menu</string> 464 <string>Open &amp;Controller Menu</string>
456 </property> 465 </property>
457 </action> 466 </action>
467 <action name="action_Install_Firmware">
468 <property name="text">
469 <string>Install Firmware</string>
470 </property>
471 </action>
458 </widget> 472 </widget>
459 <resources> 473 <resources>
460 <include location="yuzu.qrc"/> 474 <include location="yuzu.qrc"/>
diff --git a/src/yuzu_cmd/sdl_config.cpp b/src/yuzu_cmd/sdl_config.cpp
index 995114510..6e0f254b6 100644
--- a/src/yuzu_cmd/sdl_config.cpp
+++ b/src/yuzu_cmd/sdl_config.cpp
@@ -103,6 +103,7 @@ void SdlConfig::ReadSdlPlayerValues(const std::size_t player_index) {
103 if (profile_name.empty()) { 103 if (profile_name.empty()) {
104 // Use the global input config 104 // Use the global input config
105 player = Settings::values.players.GetValue(true)[player_index]; 105 player = Settings::values.players.GetValue(true)[player_index];
106 player.profile_name = "";
106 return; 107 return;
107 } 108 }
108 } 109 }
diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp
index 3b321dad1..8a8cdbc44 100644
--- a/src/yuzu_cmd/yuzu.cpp
+++ b/src/yuzu_cmd/yuzu.cpp
@@ -327,7 +327,9 @@ int main(int argc, char** argv) {
327#endif 327#endif
328 328
329 MicroProfileOnThreadCreate("EmuThread"); 329 MicroProfileOnThreadCreate("EmuThread");
330 SCOPE_EXIT({ MicroProfileShutdown(); }); 330 SCOPE_EXIT {
331 MicroProfileShutdown();
332 };
331 333
332 Common::ConfigureNvidiaEnvironmentFlags(); 334 Common::ConfigureNvidiaEnvironmentFlags();
333 335