summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/CMakeLists.txt2
-rw-r--r--src/audio_core/delay_line.cpp4
-rw-r--r--src/audio_core/delay_line.h4
-rw-r--r--src/common/CMakeLists.txt1
-rw-r--r--src/common/bit_util.h7
-rw-r--r--src/common/host_memory.cpp4
-rw-r--r--src/common/input.h366
-rw-r--r--src/common/logging/filter.cpp1
-rw-r--r--src/common/logging/types.h1
-rw-r--r--src/common/settings.cpp1
-rw-r--r--src/common/settings.h15
-rw-r--r--src/common/settings_input.h70
-rw-r--r--src/common/x64/cpu_detect.cpp12
-rw-r--r--src/common/x64/native_clock.cpp36
-rw-r--r--src/core/CMakeLists.txt27
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_64.cpp20
-rw-r--r--src/core/core.cpp20
-rw-r--r--src/core/core.h13
-rw-r--r--src/core/cpu_manager.cpp23
-rw-r--r--src/core/frontend/applets/controller.cpp45
-rw-r--r--src/core/frontend/applets/controller.h8
-rw-r--r--src/core/frontend/emu_window.cpp98
-rw-r--r--src/core/frontend/emu_window.h30
-rw-r--r--src/core/frontend/framebuffer_layout.cpp7
-rw-r--r--src/core/frontend/framebuffer_layout.h11
-rw-r--r--src/core/frontend/input.h217
-rw-r--r--src/core/hid/emulated_console.cpp232
-rw-r--r--src/core/hid/emulated_console.h190
-rw-r--r--src/core/hid/emulated_controller.cpp1139
-rw-r--r--src/core/hid/emulated_controller.h411
-rw-r--r--src/core/hid/emulated_devices.cpp459
-rw-r--r--src/core/hid/emulated_devices.h210
-rw-r--r--src/core/hid/hid_core.cpp214
-rw-r--r--src/core/hid/hid_core.h82
-rw-r--r--src/core/hid/hid_types.h635
-rw-r--r--src/core/hid/input_converter.cpp383
-rw-r--r--src/core/hid/input_converter.h96
-rw-r--r--src/core/hid/input_interpreter.cpp (renamed from src/core/frontend/input_interpreter.cpp)27
-rw-r--r--src/core/hid/input_interpreter.h (renamed from src/core/frontend/input_interpreter.h)54
-rw-r--r--src/core/hid/motion_input.cpp (renamed from src/input_common/motion_input.cpp)57
-rw-r--r--src/core/hid/motion_input.h (renamed from src/input_common/motion_input.h)27
-rw-r--r--src/core/hle/kernel/init/init_slab_setup.cpp2
-rw-r--r--src/core/hle/kernel/k_address_arbiter.cpp92
-rw-r--r--src/core/hle/kernel/k_auto_object.h4
-rw-r--r--src/core/hle/kernel/k_class_token.cpp5
-rw-r--r--src/core/hle/kernel/k_code_memory.cpp146
-rw-r--r--src/core/hle/kernel/k_code_memory.h66
-rw-r--r--src/core/hle/kernel/k_condition_variable.cpp245
-rw-r--r--src/core/hle/kernel/k_condition_variable.h2
-rw-r--r--src/core/hle/kernel/k_handle_table.cpp6
-rw-r--r--src/core/hle/kernel/k_handle_table.h2
-rw-r--r--src/core/hle/kernel/k_light_condition_variable.cpp80
-rw-r--r--src/core/hle/kernel/k_light_condition_variable.h58
-rw-r--r--src/core/hle/kernel/k_light_lock.cpp72
-rw-r--r--src/core/hle/kernel/k_light_lock.h2
-rw-r--r--src/core/hle/kernel/k_memory_block.h20
-rw-r--r--src/core/hle/kernel/k_page_linked_list.h4
-rw-r--r--src/core/hle/kernel/k_page_table.cpp123
-rw-r--r--src/core/hle/kernel/k_page_table.h10
-rw-r--r--src/core/hle/kernel/k_process.cpp30
-rw-r--r--src/core/hle/kernel/k_process.h1
-rw-r--r--src/core/hle/kernel/k_scheduler.cpp101
-rw-r--r--src/core/hle/kernel/k_scheduler.h2
-rw-r--r--src/core/hle/kernel/k_scheduler_lock.h10
-rw-r--r--src/core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h1
-rw-r--r--src/core/hle/kernel/k_server_session.cpp3
-rw-r--r--src/core/hle/kernel/k_synchronization_object.cpp151
-rw-r--r--src/core/hle/kernel/k_synchronization_object.h32
-rw-r--r--src/core/hle/kernel/k_thread.cpp246
-rw-r--r--src/core/hle/kernel/k_thread.h72
-rw-r--r--src/core/hle/kernel/k_thread_queue.cpp49
-rw-r--r--src/core/hle/kernel/k_thread_queue.h74
-rw-r--r--src/core/hle/kernel/kernel.cpp103
-rw-r--r--src/core/hle/kernel/kernel.h9
-rw-r--r--src/core/hle/kernel/service_thread.cpp33
-rw-r--r--src/core/hle/kernel/svc.cpp309
-rw-r--r--src/core/hle/kernel/svc_wrap.h27
-rw-r--r--src/core/hle/kernel/time_manager.cpp6
-rw-r--r--src/core/hle/service/am/am.cpp14
-rw-r--r--src/core/hle/service/am/am.h1
-rw-r--r--src/core/hle/service/am/applets/applet_controller.cpp19
-rw-r--r--src/core/hle/service/am/applets/applet_controller.h6
-rw-r--r--src/core/hle/service/am/applets/applets.cpp2
-rw-r--r--src/core/hle/service/audio/hwopus.cpp4
-rw-r--r--src/core/hle/service/caps/caps.h2
-rw-r--r--src/core/hle/service/friend/friend.cpp23
-rw-r--r--src/core/hle/service/glue/glue.cpp4
-rw-r--r--src/core/hle/service/glue/notif.cpp44
-rw-r--r--src/core/hle/service/glue/notif.h25
-rw-r--r--src/core/hle/service/hid/controllers/console_sixaxis.cpp65
-rw-r--r--src/core/hle/service/hid/controllers/console_sixaxis.h44
-rw-r--r--src/core/hle/service/hid/controllers/controller_base.cpp4
-rw-r--r--src/core/hle/service/hid/controllers/controller_base.h21
-rw-r--r--src/core/hle/service/hid/controllers/debug_pad.cpp72
-rw-r--r--src/core/hle/service/hid/controllers/debug_pad.h82
-rw-r--r--src/core/hle/service/hid/controllers/gesture.cpp257
-rw-r--r--src/core/hle/service/hid/controllers/gesture.h109
-rw-r--r--src/core/hle/service/hid/controllers/keyboard.cpp59
-rw-r--r--src/core/hle/service/hid/controllers/keyboard.h59
-rw-r--r--src/core/hle/service/hid/controllers/mouse.cpp64
-rw-r--r--src/core/hle/service/hid/controllers/mouse.h63
-rw-r--r--src/core/hle/service/hid/controllers/npad.cpp1760
-rw-r--r--src/core/hle/service/hid/controllers/npad.h661
-rw-r--r--src/core/hle/service/hid/controllers/stubbed.cpp6
-rw-r--r--src/core/hle/service/hid/controllers/stubbed.h13
-rw-r--r--src/core/hle/service/hid/controllers/touchscreen.cpp139
-rw-r--r--src/core/hle/service/hid/controllers/touchscreen.h86
-rw-r--r--src/core/hle/service/hid/controllers/xpad.cpp32
-rw-r--r--src/core/hle/service/hid/controllers/xpad.h61
-rw-r--r--src/core/hle/service/hid/hid.cpp463
-rw-r--r--src/core/hle/service/hid/hid.h35
-rw-r--r--src/core/hle/service/hid/ring_lifo.h54
-rw-r--r--src/core/hle/service/ldr/ldr.cpp8
-rw-r--r--src/core/hle/service/ns/ns.cpp23
-rw-r--r--src/core/hle/service/ns/ns.h7
-rw-r--r--src/core/hle/service/ns/pdm_qry.cpp69
-rw-r--r--src/core/hle/service/ns/pdm_qry.h33
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp13
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_nvdec.h3
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp5
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h3
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_vic.cpp10
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_vic.h3
-rw-r--r--src/core/hle/service/nvdrv/nvdata.h4
-rw-r--r--src/core/hle/service/pm/pm.cpp47
-rw-r--r--src/core/loader/deconstructed_rom_directory.cpp5
-rw-r--r--src/core/memory/cheat_engine.cpp3
-rw-r--r--src/core/perf_stats.h4
-rw-r--r--src/input_common/CMakeLists.txt60
-rwxr-xr-xsrc/input_common/analog_from_button.cpp238
-rw-r--r--src/input_common/drivers/gc_adapter.cpp (renamed from src/input_common/gcadapter/gc_adapter.cpp)489
-rw-r--r--src/input_common/drivers/gc_adapter.h135
-rw-r--r--src/input_common/drivers/keyboard.cpp112
-rw-r--r--src/input_common/drivers/keyboard.h56
-rw-r--r--src/input_common/drivers/mouse.cpp185
-rw-r--r--src/input_common/drivers/mouse.h81
-rw-r--r--src/input_common/drivers/sdl_driver.cpp926
-rw-r--r--src/input_common/drivers/sdl_driver.h (renamed from src/input_common/sdl/sdl_impl.h)65
-rw-r--r--src/input_common/drivers/tas_input.cpp320
-rw-r--r--src/input_common/drivers/tas_input.h201
-rw-r--r--src/input_common/drivers/touch_screen.cpp53
-rw-r--r--src/input_common/drivers/touch_screen.h44
-rw-r--r--src/input_common/drivers/udp_client.cpp591
-rw-r--r--src/input_common/drivers/udp_client.h (renamed from src/input_common/udp/client.h)128
-rw-r--r--src/input_common/gcadapter/gc_adapter.h168
-rw-r--r--src/input_common/gcadapter/gc_poller.cpp356
-rw-r--r--src/input_common/gcadapter/gc_poller.h78
-rw-r--r--src/input_common/helpers/stick_from_buttons.cpp317
-rw-r--r--[-rwxr-xr-x]src/input_common/helpers/stick_from_buttons.h (renamed from src/input_common/analog_from_button.h)7
-rw-r--r--src/input_common/helpers/touch_from_buttons.cpp84
-rw-r--r--src/input_common/helpers/touch_from_buttons.h (renamed from src/input_common/touch_from_button.h)7
-rw-r--r--src/input_common/helpers/udp_protocol.cpp (renamed from src/input_common/udp/protocol.cpp)2
-rw-r--r--src/input_common/helpers/udp_protocol.h (renamed from src/input_common/udp/protocol.h)75
-rw-r--r--src/input_common/input_engine.cpp362
-rw-r--r--src/input_common/input_engine.h229
-rw-r--r--src/input_common/input_mapping.cpp207
-rw-r--r--src/input_common/input_mapping.h83
-rw-r--r--src/input_common/input_poller.cpp970
-rw-r--r--src/input_common/input_poller.h217
-rw-r--r--src/input_common/keyboard.cpp121
-rw-r--r--src/input_common/keyboard.h47
-rw-r--r--src/input_common/main.cpp468
-rw-r--r--src/input_common/main.h152
-rw-r--r--src/input_common/motion_from_button.cpp34
-rw-r--r--src/input_common/motion_from_button.h25
-rw-r--r--src/input_common/mouse/mouse_input.cpp223
-rw-r--r--src/input_common/mouse/mouse_input.h116
-rw-r--r--src/input_common/mouse/mouse_poller.cpp299
-rw-r--r--src/input_common/mouse/mouse_poller.h109
-rw-r--r--src/input_common/sdl/sdl.cpp19
-rw-r--r--src/input_common/sdl/sdl.h51
-rw-r--r--src/input_common/sdl/sdl_impl.cpp1658
-rw-r--r--src/input_common/tas/tas_input.cpp455
-rw-r--r--src/input_common/tas/tas_input.h237
-rw-r--r--src/input_common/tas/tas_poller.cpp101
-rw-r--r--src/input_common/tas/tas_poller.h43
-rw-r--r--src/input_common/touch_from_button.cpp53
-rw-r--r--src/input_common/udp/client.cpp526
-rw-r--r--src/input_common/udp/udp.cpp110
-rw-r--r--src/input_common/udp/udp.h57
-rw-r--r--src/shader_recompiler/CMakeLists.txt12
-rw-r--r--src/shader_recompiler/backend/glasm/emit_glasm.cpp2
-rw-r--r--src/shader_recompiler/backend/glasm/emit_glasm_barriers.cpp22
-rw-r--r--src/shader_recompiler/backend/glasm/emit_glasm_bitwise_conversion.cpp2
-rw-r--r--src/shader_recompiler/backend/glasm/emit_glasm_composite.cpp2
-rw-r--r--src/shader_recompiler/backend/glasm/emit_glasm_context_get_set.cpp31
-rw-r--r--src/shader_recompiler/backend/glasm/emit_glasm_control_flow.cpp18
-rw-r--r--src/shader_recompiler/backend/glasm/emit_glasm_convert.cpp2
-rw-r--r--src/shader_recompiler/backend/glasm/emit_glasm_floating_point.cpp2
-rw-r--r--src/shader_recompiler/backend/glasm/emit_glasm_image.cpp2
-rw-r--r--src/shader_recompiler/backend/glasm/emit_glasm_integer.cpp2
-rw-r--r--src/shader_recompiler/backend/glasm/emit_glasm_logical.cpp26
-rw-r--r--src/shader_recompiler/backend/glasm/emit_glasm_memory.cpp2
-rw-r--r--src/shader_recompiler/backend/glasm/emit_glasm_not_implemented.cpp171
-rw-r--r--src/shader_recompiler/backend/glasm/emit_glasm_select.cpp2
-rw-r--r--src/shader_recompiler/backend/glasm/emit_glasm_shared_memory.cpp2
-rw-r--r--src/shader_recompiler/backend/glasm/emit_glasm_special.cpp95
-rw-r--r--src/shader_recompiler/backend/glasm/emit_glasm_undefined.cpp30
-rw-r--r--src/shader_recompiler/backend/glasm/emit_glasm_warp.cpp2
-rw-r--r--src/shader_recompiler/backend/glasm/glasm_emit_context.cpp (renamed from src/shader_recompiler/backend/glasm/emit_context.cpp)2
-rw-r--r--src/shader_recompiler/backend/glasm/glasm_emit_context.h (renamed from src/shader_recompiler/backend/glasm/emit_context.h)0
-rw-r--r--src/shader_recompiler/backend/glasm/reg_alloc.cpp2
-rw-r--r--src/shader_recompiler/backend/glsl/emit_glsl.cpp2
-rw-r--r--src/shader_recompiler/backend/glsl/emit_glsl_atomic.cpp2
-rw-r--r--src/shader_recompiler/backend/glsl/emit_glsl_barriers.cpp2
-rw-r--r--src/shader_recompiler/backend/glsl/emit_glsl_bitwise_conversion.cpp2
-rw-r--r--src/shader_recompiler/backend/glsl/emit_glsl_composite.cpp2
-rw-r--r--src/shader_recompiler/backend/glsl/emit_glsl_context_get_set.cpp2
-rw-r--r--src/shader_recompiler/backend/glsl/emit_glsl_control_flow.cpp2
-rw-r--r--src/shader_recompiler/backend/glsl/emit_glsl_convert.cpp2
-rw-r--r--src/shader_recompiler/backend/glsl/emit_glsl_floating_point.cpp2
-rw-r--r--src/shader_recompiler/backend/glsl/emit_glsl_image.cpp2
-rw-r--r--src/shader_recompiler/backend/glsl/emit_glsl_integer.cpp2
-rw-r--r--src/shader_recompiler/backend/glsl/emit_glsl_logical.cpp2
-rw-r--r--src/shader_recompiler/backend/glsl/emit_glsl_memory.cpp2
-rw-r--r--src/shader_recompiler/backend/glsl/emit_glsl_not_implemented.cpp2
-rw-r--r--src/shader_recompiler/backend/glsl/emit_glsl_select.cpp2
-rw-r--r--src/shader_recompiler/backend/glsl/emit_glsl_shared_memory.cpp2
-rw-r--r--src/shader_recompiler/backend/glsl/emit_glsl_special.cpp2
-rw-r--r--src/shader_recompiler/backend/glsl/emit_glsl_undefined.cpp2
-rw-r--r--src/shader_recompiler/backend/glsl/emit_glsl_warp.cpp2
-rw-r--r--src/shader_recompiler/backend/glsl/glsl_emit_context.cpp (renamed from src/shader_recompiler/backend/glsl/emit_context.cpp)2
-rw-r--r--src/shader_recompiler/backend/glsl/glsl_emit_context.h (renamed from src/shader_recompiler/backend/glsl/emit_context.h)0
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv.cpp1
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv.h4
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_atomic.cpp1
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_barriers.cpp1
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_bitwise_conversion.cpp1
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_composite.cpp1
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp1
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_control_flow.cpp1
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_convert.cpp1
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_floating_point.cpp1
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_image.cpp1
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_image_atomic.cpp1
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_integer.cpp1
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_logical.cpp1
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_memory.cpp1
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_select.cpp1
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_shared_memory.cpp1
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_special.cpp1
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_undefined.cpp1
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_warp.cpp1
-rw-r--r--src/shader_recompiler/backend/spirv/spirv_emit_context.cpp (renamed from src/shader_recompiler/backend/spirv/emit_context.cpp)2
-rw-r--r--src/shader_recompiler/backend/spirv/spirv_emit_context.h (renamed from src/shader_recompiler/backend/spirv/emit_context.h)0
-rw-r--r--src/shader_recompiler/environment.h4
-rw-r--r--src/video_core/CMakeLists.txt1
-rw-r--r--src/video_core/command_classes/codecs/codec.cpp86
-rw-r--r--src/video_core/gpu.cpp44
-rw-r--r--src/video_core/gpu.h5
-rw-r--r--src/video_core/host_shaders/CMakeLists.txt2
-rw-r--r--src/video_core/host_shaders/convert_abgr8_to_d24s8.frag18
-rw-r--r--src/video_core/host_shaders/convert_d24s8_to_abgr8.frag23
-rw-r--r--src/video_core/renderer_opengl/gl_texture_cache.cpp63
-rw-r--r--src/video_core/renderer_opengl/gl_texture_cache.h12
-rw-r--r--src/video_core/renderer_opengl/maxwell_to_gl.h1
-rw-r--r--src/video_core/renderer_vulkan/blit_image.cpp155
-rw-r--r--src/video_core/renderer_vulkan/blit_image.h25
-rw-r--r--src/video_core/renderer_vulkan/maxwell_to_vk.cpp9
-rw-r--r--src/video_core/renderer_vulkan/vk_texture_cache.cpp202
-rw-r--r--src/video_core/renderer_vulkan/vk_texture_cache.h14
-rw-r--r--src/video_core/shader_notify.cpp2
-rw-r--r--src/video_core/shader_notify.h2
-rw-r--r--src/video_core/surface.cpp7
-rw-r--r--src/video_core/surface.h14
-rw-r--r--src/video_core/texture_cache/format_lookup_table.cpp2
-rw-r--r--src/video_core/texture_cache/formatter.h2
-rw-r--r--src/video_core/texture_cache/texture_cache.h105
-rw-r--r--src/video_core/texture_cache/texture_cache_base.h5
-rw-r--r--src/video_core/texture_cache/types.h1
-rw-r--r--src/video_core/texture_cache/util.cpp9
-rw-r--r--src/video_core/video_core.cpp6
-rw-r--r--src/video_core/video_core.h2
-rw-r--r--src/video_core/vulkan_common/vulkan_device.cpp11
-rw-r--r--src/yuzu/CMakeLists.txt5
-rw-r--r--src/yuzu/applets/qt_controller.cpp155
-rw-r--r--src/yuzu/applets/qt_controller.h20
-rw-r--r--src/yuzu/applets/qt_profile_select.cpp17
-rw-r--r--src/yuzu/applets/qt_profile_select.h8
-rw-r--r--src/yuzu/applets/qt_software_keyboard.cpp135
-rw-r--r--src/yuzu/applets/qt_software_keyboard.h12
-rw-r--r--src/yuzu/applets/qt_web_browser.cpp70
-rw-r--r--src/yuzu/applets/qt_web_browser.h12
-rw-r--r--src/yuzu/bootmanager.cpp370
-rw-r--r--src/yuzu/bootmanager.h22
-rw-r--r--src/yuzu/configuration/config.cpp224
-rw-r--r--src/yuzu/configuration/configure_debug.cpp2
-rw-r--r--src/yuzu/configuration/configure_debug.ui7
-rw-r--r--src/yuzu/configuration/configure_debug_controller.cpp9
-rw-r--r--src/yuzu/configuration/configure_debug_controller.h7
-rw-r--r--src/yuzu/configuration/configure_general.cpp20
-rw-r--r--src/yuzu/configuration/configure_general.ui99
-rw-r--r--src/yuzu/configuration/configure_graphics.ui2
-rw-r--r--src/yuzu/configuration/configure_input.cpp66
-rw-r--r--src/yuzu/configuration/configure_input_advanced.cpp6
-rw-r--r--src/yuzu/configuration/configure_input_advanced.ui217
-rw-r--r--src/yuzu/configuration/configure_input_player.cpp1230
-rw-r--r--src/yuzu/configuration/configure_input_player.h57
-rw-r--r--src/yuzu/configuration/configure_input_player.ui50
-rw-r--r--src/yuzu/configuration/configure_input_player_widget.cpp715
-rw-r--r--src/yuzu/configuration/configure_input_player_widget.h173
-rw-r--r--src/yuzu/configuration/configure_input_profile_dialog.cpp4
-rw-r--r--src/yuzu/configuration/configure_motion_touch.cpp12
-rw-r--r--src/yuzu/configuration/configure_motion_touch.ui70
-rw-r--r--src/yuzu/configuration/configure_mouse_advanced.cpp276
-rw-r--r--src/yuzu/configuration/configure_mouse_advanced.h78
-rw-r--r--src/yuzu/configuration/configure_mouse_advanced.ui335
-rw-r--r--src/yuzu/configuration/configure_tas.cpp2
-rw-r--r--src/yuzu/configuration/configure_tas.ui9
-rw-r--r--src/yuzu/configuration/configure_touch_from_button.cpp27
-rw-r--r--src/yuzu/configuration/configure_touch_from_button.h5
-rw-r--r--src/yuzu/configuration/configure_vibration.cpp74
-rw-r--r--src/yuzu/configuration/configure_vibration.h3
-rw-r--r--src/yuzu/debugger/controller.cpp75
-rw-r--r--src/yuzu/debugger/controller.h38
-rw-r--r--src/yuzu/debugger/profiler.cpp2
-rw-r--r--src/yuzu/game_list.cpp39
-rw-r--r--src/yuzu/game_list.h5
-rw-r--r--src/yuzu/loading_screen.cpp4
-rw-r--r--src/yuzu/loading_screen.h4
-rw-r--r--src/yuzu/main.cpp593
-rw-r--r--src/yuzu/main.h11
-rw-r--r--src/yuzu/main.ui99
-rw-r--r--src/yuzu/uisettings.cpp4
-rw-r--r--src/yuzu/uisettings.h3
-rw-r--r--src/yuzu/util/controller_navigation.cpp177
-rw-r--r--src/yuzu/util/controller_navigation.h51
-rw-r--r--src/yuzu/util/overlay_dialog.cpp27
-rw-r--r--src/yuzu/util/overlay_dialog.h10
-rw-r--r--src/yuzu_cmd/config.cpp178
-rw-r--r--src/yuzu_cmd/default_ini.h17
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2.cpp72
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2.h11
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp1
334 files changed, 18256 insertions, 12898 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 63dd9febf..19d16147d 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -24,6 +24,7 @@ if (MSVC)
24 # /W3 - Level 3 warnings 24 # /W3 - Level 3 warnings
25 # /MP - Multi-threaded compilation 25 # /MP - Multi-threaded compilation
26 # /Zi - Output debugging information 26 # /Zi - Output debugging information
27 # /Zm - Specifies the precompiled header memory allocation limit
27 # /Zo - Enhanced debug info for optimized builds 28 # /Zo - Enhanced debug info for optimized builds
28 # /permissive- - Enables stricter C++ standards conformance checks 29 # /permissive- - Enables stricter C++ standards conformance checks
29 # /EHsc - C++-only exception handling semantics 30 # /EHsc - C++-only exception handling semantics
@@ -36,6 +37,7 @@ if (MSVC)
36 add_compile_options( 37 add_compile_options(
37 /MP 38 /MP
38 /Zi 39 /Zi
40 /Zm200
39 /Zo 41 /Zo
40 /permissive- 42 /permissive-
41 /EHsc 43 /EHsc
diff --git a/src/audio_core/delay_line.cpp b/src/audio_core/delay_line.cpp
index f4e4dd8d2..2793ed8db 100644
--- a/src/audio_core/delay_line.cpp
+++ b/src/audio_core/delay_line.cpp
@@ -1,3 +1,7 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
1#include <cstring> 5#include <cstring>
2#include "audio_core/delay_line.h" 6#include "audio_core/delay_line.h"
3 7
diff --git a/src/audio_core/delay_line.h b/src/audio_core/delay_line.h
index cafddd432..84f11bc52 100644
--- a/src/audio_core/delay_line.h
+++ b/src/audio_core/delay_line.h
@@ -1,3 +1,7 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
1#pragma once 5#pragma once
2 6
3#include "common/common_types.h" 7#include "common/common_types.h"
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt
index 23d43a394..919da4a53 100644
--- a/src/common/CMakeLists.txt
+++ b/src/common/CMakeLists.txt
@@ -73,6 +73,7 @@ add_library(common STATIC
73 hex_util.h 73 hex_util.h
74 host_memory.cpp 74 host_memory.cpp
75 host_memory.h 75 host_memory.h
76 input.h
76 intrusive_red_black_tree.h 77 intrusive_red_black_tree.h
77 literals.h 78 literals.h
78 logging/backend.cpp 79 logging/backend.cpp
diff --git a/src/common/bit_util.h b/src/common/bit_util.h
index 64520ca4e..eef8c1c5a 100644
--- a/src/common/bit_util.h
+++ b/src/common/bit_util.h
@@ -7,6 +7,7 @@
7#include <bit> 7#include <bit>
8#include <climits> 8#include <climits>
9#include <cstddef> 9#include <cstddef>
10#include <type_traits>
10 11
11#include "common/common_types.h" 12#include "common/common_types.h"
12 13
@@ -44,4 +45,10 @@ template <typename T>
44 return static_cast<u32>(log2_f + static_cast<u64>((value ^ (1ULL << log2_f)) != 0ULL)); 45 return static_cast<u32>(log2_f + static_cast<u64>((value ^ (1ULL << log2_f)) != 0ULL));
45} 46}
46 47
48template <typename T>
49requires std::is_integral_v<T>
50[[nodiscard]] T NextPow2(T value) {
51 return static_cast<T>(1ULL << ((8U * sizeof(T)) - std::countl_zero(value - 1U)));
52}
53
47} // namespace Common 54} // namespace Common
diff --git a/src/common/host_memory.cpp b/src/common/host_memory.cpp
index b44a44949..28949fe5e 100644
--- a/src/common/host_memory.cpp
+++ b/src/common/host_memory.cpp
@@ -1,3 +1,7 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
1#ifdef _WIN32 5#ifdef _WIN32
2 6
3#include <iterator> 7#include <iterator>
diff --git a/src/common/input.h b/src/common/input.h
new file mode 100644
index 000000000..f775a4c01
--- /dev/null
+++ b/src/common/input.h
@@ -0,0 +1,366 @@
1// Copyright 2017 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <functional>
8#include <memory>
9#include <string>
10#include <unordered_map>
11#include <utility>
12#include "common/logging/log.h"
13#include "common/param_package.h"
14#include "common/uuid.h"
15
16namespace Common::Input {
17
18// Type of data that is expected to recieve or send
19enum class InputType {
20 None,
21 Battery,
22 Button,
23 Stick,
24 Analog,
25 Trigger,
26 Motion,
27 Touch,
28 Color,
29 Vibration,
30 Nfc,
31 Ir,
32};
33
34// Internal battery charge level
35enum class BatteryLevel : u32 {
36 None,
37 Empty,
38 Critical,
39 Low,
40 Medium,
41 Full,
42 Charging,
43};
44
45enum class PollingMode {
46 // Constant polling of buttons, analogs and motion data
47 Active,
48 // Only update on button change, digital analogs
49 Pasive,
50 // Enable near field communication polling
51 NFC,
52 // Enable infrared camera polling
53 IR,
54};
55
56// Vibration reply from the controller
57enum class VibrationError {
58 None,
59 NotSupported,
60 Disabled,
61 Unknown,
62};
63
64// Polling mode reply from the controller
65enum class PollingError {
66 None,
67 NotSupported,
68 Unknown,
69};
70
71// Hint for amplification curve to be used
72enum class VibrationAmplificationType {
73 Linear,
74 Exponential,
75};
76
77// Analog properties for calibration
78struct AnalogProperties {
79 // Anything below this value will be detected as zero
80 float deadzone{};
81 // Anyting above this values will be detected as one
82 float range{1.0f};
83 // Minimum value to be detected as active
84 float threshold{0.5f};
85 // Drift correction applied to the raw data
86 float offset{};
87 // Invert direction of the sensor data
88 bool inverted{};
89};
90
91// Single analog sensor data
92struct AnalogStatus {
93 float value{};
94 float raw_value{};
95 AnalogProperties properties{};
96};
97
98// Button data
99struct ButtonStatus {
100 Common::UUID uuid{};
101 bool value{};
102 bool inverted{};
103 bool toggle{};
104 bool locked{};
105};
106
107// Internal battery data
108using BatteryStatus = BatteryLevel;
109
110// Analog and digital joystick data
111struct StickStatus {
112 Common::UUID uuid{};
113 AnalogStatus x{};
114 AnalogStatus y{};
115 bool left{};
116 bool right{};
117 bool up{};
118 bool down{};
119};
120
121// Analog and digital trigger data
122struct TriggerStatus {
123 Common::UUID uuid{};
124 AnalogStatus analog{};
125 ButtonStatus pressed{};
126};
127
128// 3D vector representing motion input
129struct MotionSensor {
130 AnalogStatus x{};
131 AnalogStatus y{};
132 AnalogStatus z{};
133};
134
135// Motion data used to calculate controller orientation
136struct MotionStatus {
137 // Gyroscope vector measurement in radians/s.
138 MotionSensor gyro{};
139 // Acceleration vector measurement in G force
140 MotionSensor accel{};
141 // Time since last measurement in microseconds
142 u64 delta_timestamp{};
143 // Request to update after reading the value
144 bool force_update{};
145};
146
147// Data of a single point on a touch screen
148struct TouchStatus {
149 ButtonStatus pressed{};
150 AnalogStatus x{};
151 AnalogStatus y{};
152 int id{};
153};
154
155// Physical controller color in RGB format
156struct BodyColorStatus {
157 u32 body{};
158 u32 buttons{};
159};
160
161// HD rumble data
162struct VibrationStatus {
163 f32 low_amplitude{};
164 f32 low_frequency{};
165 f32 high_amplitude{};
166 f32 high_frequency{};
167 VibrationAmplificationType type;
168};
169
170// Physical controller LED pattern
171struct LedStatus {
172 bool led_1{};
173 bool led_2{};
174 bool led_3{};
175 bool led_4{};
176};
177
178// List of buttons to be passed to Qt that can be translated
179enum class ButtonNames {
180 Undefined,
181 Invalid,
182 // This will display the engine name instead of the button name
183 Engine,
184 // This will display the button by value instead of the button name
185 Value,
186 ButtonLeft,
187 ButtonRight,
188 ButtonDown,
189 ButtonUp,
190 TriggerZ,
191 TriggerR,
192 TriggerL,
193 ButtonA,
194 ButtonB,
195 ButtonX,
196 ButtonY,
197 ButtonStart,
198
199 // DS4 button names
200 L1,
201 L2,
202 L3,
203 R1,
204 R2,
205 R3,
206 Circle,
207 Cross,
208 Square,
209 Triangle,
210 Share,
211 Options,
212};
213
214// Callback data consisting of an input type and the equivalent data status
215struct CallbackStatus {
216 InputType type{InputType::None};
217 ButtonStatus button_status{};
218 StickStatus stick_status{};
219 AnalogStatus analog_status{};
220 TriggerStatus trigger_status{};
221 MotionStatus motion_status{};
222 TouchStatus touch_status{};
223 BodyColorStatus color_status{};
224 BatteryStatus battery_status{};
225 VibrationStatus vibration_status{};
226};
227
228// Triggered once every input change
229struct InputCallback {
230 std::function<void(const CallbackStatus&)> on_change;
231};
232
233/// An abstract class template for an input device (a button, an analog input, etc.).
234class InputDevice {
235public:
236 virtual ~InputDevice() = default;
237
238 // Request input device to update if necessary
239 virtual void SoftUpdate() {}
240
241 // Force input device to update data regardless of the current state
242 virtual void ForceUpdate() {}
243
244 // Sets the function to be triggered when input changes
245 void SetCallback(InputCallback callback_) {
246 callback = std::move(callback_);
247 }
248
249 // Triggers the function set in the callback
250 void TriggerOnChange(const CallbackStatus& status) {
251 if (callback.on_change) {
252 callback.on_change(status);
253 }
254 }
255
256private:
257 InputCallback callback;
258};
259
260/// An abstract class template for an output device (rumble, LED pattern, polling mode).
261class OutputDevice {
262public:
263 virtual ~OutputDevice() = default;
264
265 virtual void SetLED([[maybe_unused]] const LedStatus& led_status) {}
266
267 virtual VibrationError SetVibration([[maybe_unused]] const VibrationStatus& vibration_status) {
268 return VibrationError::NotSupported;
269 }
270
271 virtual PollingError SetPollingMode([[maybe_unused]] PollingMode polling_mode) {
272 return PollingError::NotSupported;
273 }
274};
275
276/// An abstract class template for a factory that can create input devices.
277template <typename InputDeviceType>
278class Factory {
279public:
280 virtual ~Factory() = default;
281 virtual std::unique_ptr<InputDeviceType> Create(const Common::ParamPackage&) = 0;
282};
283
284namespace Impl {
285
286template <typename InputDeviceType>
287using FactoryListType = std::unordered_map<std::string, std::shared_ptr<Factory<InputDeviceType>>>;
288
289template <typename InputDeviceType>
290struct FactoryList {
291 static FactoryListType<InputDeviceType> list;
292};
293
294template <typename InputDeviceType>
295FactoryListType<InputDeviceType> FactoryList<InputDeviceType>::list;
296
297} // namespace Impl
298
299/**
300 * Registers an input device factory.
301 * @tparam InputDeviceType the type of input devices the factory can create
302 * @param name the name of the factory. Will be used to match the "engine" parameter when creating
303 * a device
304 * @param factory the factory object to register
305 */
306template <typename InputDeviceType>
307void RegisterFactory(const std::string& name, std::shared_ptr<Factory<InputDeviceType>> factory) {
308 auto pair = std::make_pair(name, std::move(factory));
309 if (!Impl::FactoryList<InputDeviceType>::list.insert(std::move(pair)).second) {
310 LOG_ERROR(Input, "Factory '{}' already registered", name);
311 }
312}
313
314/**
315 * Unregisters an input device factory.
316 * @tparam InputDeviceType the type of input devices the factory can create
317 * @param name the name of the factory to unregister
318 */
319template <typename InputDeviceType>
320void UnregisterFactory(const std::string& name) {
321 if (Impl::FactoryList<InputDeviceType>::list.erase(name) == 0) {
322 LOG_ERROR(Input, "Factory '{}' not registered", name);
323 }
324}
325
326/**
327 * Create an input device from given paramters.
328 * @tparam InputDeviceType the type of input devices to create
329 * @param params a serialized ParamPackage string that contains all parameters for creating the
330 * device
331 */
332template <typename InputDeviceType>
333std::unique_ptr<InputDeviceType> CreateDeviceFromString(const std::string& params) {
334 const Common::ParamPackage package(params);
335 const std::string engine = package.Get("engine", "null");
336 const auto& factory_list = Impl::FactoryList<InputDeviceType>::list;
337 const auto pair = factory_list.find(engine);
338 if (pair == factory_list.end()) {
339 if (engine != "null") {
340 LOG_ERROR(Input, "Unknown engine name: {}", engine);
341 }
342 return std::make_unique<InputDeviceType>();
343 }
344 return pair->second->Create(package);
345}
346
347/**
348 * Create an input device from given paramters.
349 * @tparam InputDeviceType the type of input devices to create
350 * @param A ParamPackage that contains all parameters for creating the device
351 */
352template <typename InputDeviceType>
353std::unique_ptr<InputDeviceType> CreateDevice(const Common::ParamPackage package) {
354 const std::string engine = package.Get("engine", "null");
355 const auto& factory_list = Impl::FactoryList<InputDeviceType>::list;
356 const auto pair = factory_list.find(engine);
357 if (pair == factory_list.end()) {
358 if (engine != "null") {
359 LOG_ERROR(Input, "Unknown engine name: {}", engine);
360 }
361 return std::make_unique<InputDeviceType>();
362 }
363 return pair->second->Create(package);
364}
365
366} // namespace Common::Input
diff --git a/src/common/logging/filter.cpp b/src/common/logging/filter.cpp
index 42744c994..b898a652c 100644
--- a/src/common/logging/filter.cpp
+++ b/src/common/logging/filter.cpp
@@ -114,6 +114,7 @@ bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) {
114 SUB(Service, NGCT) \ 114 SUB(Service, NGCT) \
115 SUB(Service, NIFM) \ 115 SUB(Service, NIFM) \
116 SUB(Service, NIM) \ 116 SUB(Service, NIM) \
117 SUB(Service, NOTIF) \
117 SUB(Service, NPNS) \ 118 SUB(Service, NPNS) \
118 SUB(Service, NS) \ 119 SUB(Service, NS) \
119 SUB(Service, NVDRV) \ 120 SUB(Service, NVDRV) \
diff --git a/src/common/logging/types.h b/src/common/logging/types.h
index 2d21fc483..9ed0c7ad6 100644
--- a/src/common/logging/types.h
+++ b/src/common/logging/types.h
@@ -82,6 +82,7 @@ enum class Class : u8 {
82 Service_NGCT, ///< The NGCT (No Good Content for Terra) service 82 Service_NGCT, ///< The NGCT (No Good Content for Terra) service
83 Service_NIFM, ///< The NIFM (Network interface) service 83 Service_NIFM, ///< The NIFM (Network interface) service
84 Service_NIM, ///< The NIM service 84 Service_NIM, ///< The NIM service
85 Service_NOTIF, ///< The NOTIF (Notification) service
85 Service_NPNS, ///< The NPNS service 86 Service_NPNS, ///< The NPNS service
86 Service_NS, ///< The NS services 87 Service_NS, ///< The NS services
87 Service_NVDRV, ///< The NVDRV (Nvidia driver) service 88 Service_NVDRV, ///< The NVDRV (Nvidia driver) service
diff --git a/src/common/settings.cpp b/src/common/settings.cpp
index 3bcaa072f..6964a8273 100644
--- a/src/common/settings.cpp
+++ b/src/common/settings.cpp
@@ -183,6 +183,7 @@ void RestoreGlobalState(bool is_powered_on) {
183 values.max_anisotropy.SetGlobal(true); 183 values.max_anisotropy.SetGlobal(true);
184 values.use_speed_limit.SetGlobal(true); 184 values.use_speed_limit.SetGlobal(true);
185 values.speed_limit.SetGlobal(true); 185 values.speed_limit.SetGlobal(true);
186 values.fps_cap.SetGlobal(true);
186 values.use_disk_shader_cache.SetGlobal(true); 187 values.use_disk_shader_cache.SetGlobal(true);
187 values.gpu_accuracy.SetGlobal(true); 188 values.gpu_accuracy.SetGlobal(true);
188 values.use_asynchronous_gpu_emulation.SetGlobal(true); 189 values.use_asynchronous_gpu_emulation.SetGlobal(true);
diff --git a/src/common/settings.h b/src/common/settings.h
index 42f8b4a7d..313f1fa7f 100644
--- a/src/common/settings.h
+++ b/src/common/settings.h
@@ -6,7 +6,6 @@
6 6
7#include <algorithm> 7#include <algorithm>
8#include <array> 8#include <array>
9#include <atomic>
10#include <map> 9#include <map>
11#include <optional> 10#include <optional>
12#include <string> 11#include <string>
@@ -525,7 +524,7 @@ struct Values {
525 Setting<NvdecEmulation> nvdec_emulation{NvdecEmulation::GPU, "nvdec_emulation"}; 524 Setting<NvdecEmulation> nvdec_emulation{NvdecEmulation::GPU, "nvdec_emulation"};
526 Setting<bool> accelerate_astc{true, "accelerate_astc"}; 525 Setting<bool> accelerate_astc{true, "accelerate_astc"};
527 Setting<bool> use_vsync{true, "use_vsync"}; 526 Setting<bool> use_vsync{true, "use_vsync"};
528 BasicRangedSetting<u16> fps_cap{1000, 1, 1000, "fps_cap"}; 527 RangedSetting<u16> fps_cap{1000, 1, 1000, "fps_cap"};
529 BasicSetting<bool> disable_fps_limit{false, "disable_fps_limit"}; 528 BasicSetting<bool> disable_fps_limit{false, "disable_fps_limit"};
530 RangedSetting<ShaderBackend> shader_backend{ShaderBackend::GLASM, ShaderBackend::GLSL, 529 RangedSetting<ShaderBackend> shader_backend{ShaderBackend::GLASM, ShaderBackend::GLSL,
531 ShaderBackend::SPIRV, "shader_backend"}; 530 ShaderBackend::SPIRV, "shader_backend"};
@@ -560,25 +559,19 @@ struct Values {
560 Setting<bool> enable_accurate_vibrations{false, "enable_accurate_vibrations"}; 559 Setting<bool> enable_accurate_vibrations{false, "enable_accurate_vibrations"};
561 560
562 Setting<bool> motion_enabled{true, "motion_enabled"}; 561 Setting<bool> motion_enabled{true, "motion_enabled"};
563 BasicSetting<std::string> motion_device{"engine:motion_emu,update_period:100,sensitivity:0.01",
564 "motion_device"};
565 BasicSetting<std::string> udp_input_servers{"127.0.0.1:26760", "udp_input_servers"}; 562 BasicSetting<std::string> udp_input_servers{"127.0.0.1:26760", "udp_input_servers"};
563 BasicSetting<bool> enable_udp_controller{false, "enable_udp_controller"};
566 564
567 BasicSetting<bool> pause_tas_on_load{true, "pause_tas_on_load"}; 565 BasicSetting<bool> pause_tas_on_load{true, "pause_tas_on_load"};
568 BasicSetting<bool> tas_enable{false, "tas_enable"}; 566 BasicSetting<bool> tas_enable{false, "tas_enable"};
569 BasicSetting<bool> tas_loop{false, "tas_loop"}; 567 BasicSetting<bool> tas_loop{false, "tas_loop"};
570 BasicSetting<bool> tas_swap_controllers{true, "tas_swap_controllers"};
571 568
572 BasicSetting<bool> mouse_panning{false, "mouse_panning"}; 569 BasicSetting<bool> mouse_panning{false, "mouse_panning"};
573 BasicRangedSetting<u8> mouse_panning_sensitivity{10, 1, 100, "mouse_panning_sensitivity"}; 570 BasicRangedSetting<u8> mouse_panning_sensitivity{10, 1, 100, "mouse_panning_sensitivity"};
574 BasicSetting<bool> mouse_enabled{false, "mouse_enabled"}; 571 BasicSetting<bool> mouse_enabled{false, "mouse_enabled"};
575 std::string mouse_device;
576 MouseButtonsRaw mouse_buttons;
577 572
578 BasicSetting<bool> emulate_analog_keyboard{false, "emulate_analog_keyboard"}; 573 BasicSetting<bool> emulate_analog_keyboard{false, "emulate_analog_keyboard"};
579 BasicSetting<bool> keyboard_enabled{false, "keyboard_enabled"}; 574 BasicSetting<bool> keyboard_enabled{false, "keyboard_enabled"};
580 KeyboardKeysRaw keyboard_keys;
581 KeyboardModsRaw keyboard_mods;
582 575
583 BasicSetting<bool> debug_pad_enabled{false, "debug_pad_enabled"}; 576 BasicSetting<bool> debug_pad_enabled{false, "debug_pad_enabled"};
584 ButtonsRaw debug_pad_buttons; 577 ButtonsRaw debug_pad_buttons;
@@ -586,14 +579,11 @@ struct Values {
586 579
587 TouchscreenInput touchscreen; 580 TouchscreenInput touchscreen;
588 581
589 BasicSetting<bool> use_touch_from_button{false, "use_touch_from_button"};
590 BasicSetting<std::string> touch_device{"min_x:100,min_y:50,max_x:1800,max_y:850", 582 BasicSetting<std::string> touch_device{"min_x:100,min_y:50,max_x:1800,max_y:850",
591 "touch_device"}; 583 "touch_device"};
592 BasicSetting<int> touch_from_button_map_index{0, "touch_from_button_map"}; 584 BasicSetting<int> touch_from_button_map_index{0, "touch_from_button_map"};
593 std::vector<TouchFromButtonMap> touch_from_button_maps; 585 std::vector<TouchFromButtonMap> touch_from_button_maps;
594 586
595 std::atomic_bool is_device_reload_pending{true};
596
597 // Data Storage 587 // Data Storage
598 BasicSetting<bool> use_virtual_sd{true, "use_virtual_sd"}; 588 BasicSetting<bool> use_virtual_sd{true, "use_virtual_sd"};
599 BasicSetting<bool> gamecard_inserted{false, "gamecard_inserted"}; 589 BasicSetting<bool> gamecard_inserted{false, "gamecard_inserted"};
@@ -614,6 +604,7 @@ struct Values {
614 BasicSetting<bool> extended_logging{false, "extended_logging"}; 604 BasicSetting<bool> extended_logging{false, "extended_logging"};
615 BasicSetting<bool> use_debug_asserts{false, "use_debug_asserts"}; 605 BasicSetting<bool> use_debug_asserts{false, "use_debug_asserts"};
616 BasicSetting<bool> use_auto_stub{false, "use_auto_stub"}; 606 BasicSetting<bool> use_auto_stub{false, "use_auto_stub"};
607 BasicSetting<bool> enable_all_controllers{false, "enable_all_controllers"};
617 608
618 // Miscellaneous 609 // Miscellaneous
619 BasicSetting<std::string> log_filter{"*:Info", "log_filter"}; 610 BasicSetting<std::string> log_filter{"*:Info", "log_filter"};
diff --git a/src/common/settings_input.h b/src/common/settings_input.h
index 609600582..4ff37e186 100644
--- a/src/common/settings_input.h
+++ b/src/common/settings_input.h
@@ -62,11 +62,22 @@ enum Values : int {
62 62
63constexpr int STICK_HID_BEGIN = LStick; 63constexpr int STICK_HID_BEGIN = LStick;
64constexpr int STICK_HID_END = NumAnalogs; 64constexpr int STICK_HID_END = NumAnalogs;
65constexpr int NUM_STICKS_HID = NumAnalogs;
66 65
67extern const std::array<const char*, NumAnalogs> mapping; 66extern const std::array<const char*, NumAnalogs> mapping;
68} // namespace NativeAnalog 67} // namespace NativeAnalog
69 68
69namespace NativeTrigger {
70enum Values : int {
71 LTrigger,
72 RTrigger,
73
74 NumTriggers,
75};
76
77constexpr int TRIGGER_HID_BEGIN = LTrigger;
78constexpr int TRIGGER_HID_END = NumTriggers;
79} // namespace NativeTrigger
80
70namespace NativeVibration { 81namespace NativeVibration {
71enum Values : int { 82enum Values : int {
72 LeftVibrationDevice, 83 LeftVibrationDevice,
@@ -115,10 +126,20 @@ constexpr int NUM_MOUSE_HID = NumMouseButtons;
115extern const std::array<const char*, NumMouseButtons> mapping; 126extern const std::array<const char*, NumMouseButtons> mapping;
116} // namespace NativeMouseButton 127} // namespace NativeMouseButton
117 128
129namespace NativeMouseWheel {
130enum Values {
131 X,
132 Y,
133
134 NumMouseWheels,
135};
136
137extern const std::array<const char*, NumMouseWheels> mapping;
138} // namespace NativeMouseWheel
139
118namespace NativeKeyboard { 140namespace NativeKeyboard {
119enum Keys { 141enum Keys {
120 None, 142 None,
121 Error,
122 143
123 A = 4, 144 A = 4,
124 B, 145 B,
@@ -156,22 +177,22 @@ enum Keys {
156 N8, 177 N8,
157 N9, 178 N9,
158 N0, 179 N0,
159 Enter, 180 Return,
160 Escape, 181 Escape,
161 Backspace, 182 Backspace,
162 Tab, 183 Tab,
163 Space, 184 Space,
164 Minus, 185 Minus,
165 Equal, 186 Plus,
166 LeftBrace, 187 OpenBracket,
167 RightBrace, 188 CloseBracket,
168 Backslash, 189 Pipe,
169 Tilde, 190 Tilde,
170 Semicolon, 191 Semicolon,
171 Apostrophe, 192 Quote,
172 Grave, 193 Backquote,
173 Comma, 194 Comma,
174 Dot, 195 Period,
175 Slash, 196 Slash,
176 CapsLockKey, 197 CapsLockKey,
177 198
@@ -188,7 +209,7 @@ enum Keys {
188 F11, 209 F11,
189 F12, 210 F12,
190 211
191 SystemRequest, 212 PrintScreen,
192 ScrollLockKey, 213 ScrollLockKey,
193 Pause, 214 Pause,
194 Insert, 215 Insert,
@@ -257,8 +278,18 @@ enum Keys {
257 ScrollLockActive, 278 ScrollLockActive,
258 KPComma, 279 KPComma,
259 280
260 KPLeftParenthesis, 281 Ro = 0x87,
261 KPRightParenthesis, 282 KatakanaHiragana,
283 Yen,
284 Henkan,
285 Muhenkan,
286 NumPadCommaPc98,
287
288 HangulEnglish = 0x90,
289 Hanja,
290 KatakanaKey,
291 HiraganaKey,
292 ZenkakuHankaku,
262 293
263 LeftControlKey = 0xE0, 294 LeftControlKey = 0xE0,
264 LeftShiftKey, 295 LeftShiftKey,
@@ -307,6 +338,8 @@ enum Modifiers {
307 CapsLock, 338 CapsLock,
308 ScrollLock, 339 ScrollLock,
309 NumLock, 340 NumLock,
341 Katakana,
342 Hiragana,
310 343
311 NumKeyboardMods, 344 NumKeyboardMods,
312}; 345};
@@ -324,11 +357,6 @@ constexpr int NUM_KEYBOARD_MODS_HID = NumKeyboardMods;
324using AnalogsRaw = std::array<std::string, NativeAnalog::NumAnalogs>; 357using AnalogsRaw = std::array<std::string, NativeAnalog::NumAnalogs>;
325using ButtonsRaw = std::array<std::string, NativeButton::NumButtons>; 358using ButtonsRaw = std::array<std::string, NativeButton::NumButtons>;
326using MotionsRaw = std::array<std::string, NativeMotion::NumMotions>; 359using MotionsRaw = std::array<std::string, NativeMotion::NumMotions>;
327using VibrationsRaw = std::array<std::string, NativeVibration::NumVibrations>;
328
329using MouseButtonsRaw = std::array<std::string, NativeMouseButton::NumMouseButtons>;
330using KeyboardKeysRaw = std::array<std::string, NativeKeyboard::NumKeyboardKeys>;
331using KeyboardModsRaw = std::array<std::string, NativeKeyboard::NumKeyboardMods>;
332 360
333constexpr u32 JOYCON_BODY_NEON_RED = 0xFF3C28; 361constexpr u32 JOYCON_BODY_NEON_RED = 0xFF3C28;
334constexpr u32 JOYCON_BUTTONS_NEON_RED = 0x1E0A0A; 362constexpr u32 JOYCON_BUTTONS_NEON_RED = 0x1E0A0A;
@@ -342,6 +370,11 @@ enum class ControllerType {
342 RightJoycon, 370 RightJoycon,
343 Handheld, 371 Handheld,
344 GameCube, 372 GameCube,
373 Pokeball,
374 NES,
375 SNES,
376 N64,
377 SegaGenesis,
345}; 378};
346 379
347struct PlayerInput { 380struct PlayerInput {
@@ -349,7 +382,6 @@ struct PlayerInput {
349 ControllerType controller_type; 382 ControllerType controller_type;
350 ButtonsRaw buttons; 383 ButtonsRaw buttons;
351 AnalogsRaw analogs; 384 AnalogsRaw analogs;
352 VibrationsRaw vibrations;
353 MotionsRaw motions; 385 MotionsRaw motions;
354 386
355 bool vibration_enabled; 387 bool vibration_enabled;
diff --git a/src/common/x64/cpu_detect.cpp b/src/common/x64/cpu_detect.cpp
index fccd2eee5..fbeacc7e2 100644
--- a/src/common/x64/cpu_detect.cpp
+++ b/src/common/x64/cpu_detect.cpp
@@ -71,9 +71,6 @@ static CPUCaps Detect() {
71 else 71 else
72 caps.manufacturer = Manufacturer::Unknown; 72 caps.manufacturer = Manufacturer::Unknown;
73 73
74 u32 family = {};
75 u32 model = {};
76
77 __cpuid(cpu_id, 0x80000000); 74 __cpuid(cpu_id, 0x80000000);
78 75
79 u32 max_ex_fn = cpu_id[0]; 76 u32 max_ex_fn = cpu_id[0];
@@ -84,15 +81,6 @@ static CPUCaps Detect() {
84 // Detect family and other miscellaneous features 81 // Detect family and other miscellaneous features
85 if (max_std_fn >= 1) { 82 if (max_std_fn >= 1) {
86 __cpuid(cpu_id, 0x00000001); 83 __cpuid(cpu_id, 0x00000001);
87 family = (cpu_id[0] >> 8) & 0xf;
88 model = (cpu_id[0] >> 4) & 0xf;
89 if (family == 0xf) {
90 family += (cpu_id[0] >> 20) & 0xff;
91 }
92 if (family >= 6) {
93 model += ((cpu_id[0] >> 16) & 0xf) << 4;
94 }
95
96 if ((cpu_id[3] >> 25) & 1) 84 if ((cpu_id[3] >> 25) & 1)
97 caps.sse = true; 85 caps.sse = true;
98 if ((cpu_id[3] >> 26) & 1) 86 if ((cpu_id[3] >> 26) & 1)
diff --git a/src/common/x64/native_clock.cpp b/src/common/x64/native_clock.cpp
index 87de40624..82ee2c8a1 100644
--- a/src/common/x64/native_clock.cpp
+++ b/src/common/x64/native_clock.cpp
@@ -15,26 +15,26 @@
15namespace Common { 15namespace Common {
16 16
17u64 EstimateRDTSCFrequency() { 17u64 EstimateRDTSCFrequency() {
18 const auto milli_10 = std::chrono::milliseconds{10}; 18 // Discard the first result measuring the rdtsc.
19 // get current time
20 _mm_mfence(); 19 _mm_mfence();
21 const u64 tscStart = __rdtsc(); 20 __rdtsc();
22 const auto startTime = std::chrono::high_resolution_clock::now(); 21 std::this_thread::sleep_for(std::chrono::milliseconds{1});
23 // wait roughly 3 seconds 22 _mm_mfence();
24 while (true) { 23 __rdtsc();
25 auto milli = std::chrono::duration_cast<std::chrono::milliseconds>( 24
26 std::chrono::high_resolution_clock::now() - startTime); 25 // Get the current time.
27 if (milli.count() >= 3000) 26 const auto start_time = std::chrono::steady_clock::now();
28 break; 27 _mm_mfence();
29 std::this_thread::sleep_for(milli_10); 28 const u64 tsc_start = __rdtsc();
30 } 29 // Wait for 200 milliseconds.
31 const auto endTime = std::chrono::high_resolution_clock::now(); 30 std::this_thread::sleep_for(std::chrono::milliseconds{200});
31 const auto end_time = std::chrono::steady_clock::now();
32 _mm_mfence(); 32 _mm_mfence();
33 const u64 tscEnd = __rdtsc(); 33 const u64 tsc_end = __rdtsc();
34 // calculate difference 34 // Calculate differences.
35 const u64 timer_diff = 35 const u64 timer_diff = static_cast<u64>(
36 std::chrono::duration_cast<std::chrono::nanoseconds>(endTime - startTime).count(); 36 std::chrono::duration_cast<std::chrono::nanoseconds>(end_time - start_time).count());
37 const u64 tsc_diff = tscEnd - tscStart; 37 const u64 tsc_diff = tsc_end - tsc_start;
38 const u64 tsc_freq = MultiplyAndDivide64(tsc_diff, 1000000000ULL, timer_diff); 38 const u64 tsc_freq = MultiplyAndDivide64(tsc_diff, 1000000000ULL, timer_diff);
39 return tsc_freq; 39 return tsc_freq;
40} 40}
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 9f0fbba2d..49bed614a 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -132,11 +132,23 @@ add_library(core STATIC
132 frontend/emu_window.h 132 frontend/emu_window.h
133 frontend/framebuffer_layout.cpp 133 frontend/framebuffer_layout.cpp
134 frontend/framebuffer_layout.h 134 frontend/framebuffer_layout.h
135 frontend/input_interpreter.cpp
136 frontend/input_interpreter.h
137 frontend/input.h
138 hardware_interrupt_manager.cpp 135 hardware_interrupt_manager.cpp
139 hardware_interrupt_manager.h 136 hardware_interrupt_manager.h
137 hid/emulated_console.cpp
138 hid/emulated_console.h
139 hid/emulated_controller.cpp
140 hid/emulated_controller.h
141 hid/emulated_devices.cpp
142 hid/emulated_devices.h
143 hid/hid_core.cpp
144 hid/hid_core.h
145 hid/hid_types.h
146 hid/input_converter.cpp
147 hid/input_converter.h
148 hid/input_interpreter.cpp
149 hid/input_interpreter.h
150 hid/motion_input.cpp
151 hid/motion_input.h
140 hle/api_version.h 152 hle/api_version.h
141 hle/ipc.h 153 hle/ipc.h
142 hle/ipc_helpers.h 154 hle/ipc_helpers.h
@@ -167,12 +179,15 @@ add_library(core STATIC
167 hle/kernel/k_client_port.h 179 hle/kernel/k_client_port.h
168 hle/kernel/k_client_session.cpp 180 hle/kernel/k_client_session.cpp
169 hle/kernel/k_client_session.h 181 hle/kernel/k_client_session.h
182 hle/kernel/k_code_memory.cpp
183 hle/kernel/k_code_memory.h
170 hle/kernel/k_condition_variable.cpp 184 hle/kernel/k_condition_variable.cpp
171 hle/kernel/k_condition_variable.h 185 hle/kernel/k_condition_variable.h
172 hle/kernel/k_event.cpp 186 hle/kernel/k_event.cpp
173 hle/kernel/k_event.h 187 hle/kernel/k_event.h
174 hle/kernel/k_handle_table.cpp 188 hle/kernel/k_handle_table.cpp
175 hle/kernel/k_handle_table.h 189 hle/kernel/k_handle_table.h
190 hle/kernel/k_light_condition_variable.cpp
176 hle/kernel/k_light_condition_variable.h 191 hle/kernel/k_light_condition_variable.h
177 hle/kernel/k_light_lock.cpp 192 hle/kernel/k_light_lock.cpp
178 hle/kernel/k_light_lock.h 193 hle/kernel/k_light_lock.h
@@ -225,6 +240,7 @@ add_library(core STATIC
225 hle/kernel/k_system_control.h 240 hle/kernel/k_system_control.h
226 hle/kernel/k_thread.cpp 241 hle/kernel/k_thread.cpp
227 hle/kernel/k_thread.h 242 hle/kernel/k_thread.h
243 hle/kernel/k_thread_queue.cpp
228 hle/kernel/k_thread_queue.h 244 hle/kernel/k_thread_queue.h
229 hle/kernel/k_trace.h 245 hle/kernel/k_trace.h
230 hle/kernel/k_transfer_memory.cpp 246 hle/kernel/k_transfer_memory.cpp
@@ -396,12 +412,15 @@ add_library(core STATIC
396 hle/service/glue/glue.h 412 hle/service/glue/glue.h
397 hle/service/glue/glue_manager.cpp 413 hle/service/glue/glue_manager.cpp
398 hle/service/glue/glue_manager.h 414 hle/service/glue/glue_manager.h
415 hle/service/glue/notif.cpp
416 hle/service/glue/notif.h
399 hle/service/grc/grc.cpp 417 hle/service/grc/grc.cpp
400 hle/service/grc/grc.h 418 hle/service/grc/grc.h
401 hle/service/hid/hid.cpp 419 hle/service/hid/hid.cpp
402 hle/service/hid/hid.h 420 hle/service/hid/hid.h
403 hle/service/hid/irs.cpp 421 hle/service/hid/irs.cpp
404 hle/service/hid/irs.h 422 hle/service/hid/irs.h
423 hle/service/hid/ring_lifo.h
405 hle/service/hid/xcd.cpp 424 hle/service/hid/xcd.cpp
406 hle/service/hid/xcd.h 425 hle/service/hid/xcd.h
407 hle/service/hid/errors.h 426 hle/service/hid/errors.h
@@ -466,6 +485,8 @@ add_library(core STATIC
466 hle/service/ns/language.h 485 hle/service/ns/language.h
467 hle/service/ns/ns.cpp 486 hle/service/ns/ns.cpp
468 hle/service/ns/ns.h 487 hle/service/ns/ns.h
488 hle/service/ns/pdm_qry.cpp
489 hle/service/ns/pdm_qry.h
469 hle/service/ns/pl_u.cpp 490 hle/service/ns/pl_u.cpp
470 hle/service/ns/pl_u.h 491 hle/service/ns/pl_u.h
471 hle/service/nvdrv/devices/nvdevice.h 492 hle/service/nvdrv/devices/nvdevice.h
diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.cpp b/src/core/arm/dynarmic/arm_dynarmic_64.cpp
index 4e73cc03a..56836bd05 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_64.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic_64.cpp
@@ -86,6 +86,26 @@ public:
86 num_instructions, MemoryReadCode(pc)); 86 num_instructions, MemoryReadCode(pc));
87 } 87 }
88 88
89 void InstructionCacheOperationRaised(Dynarmic::A64::InstructionCacheOperation op,
90 VAddr value) override {
91 switch (op) {
92 case Dynarmic::A64::InstructionCacheOperation::InvalidateByVAToPoU: {
93 static constexpr u64 ICACHE_LINE_SIZE = 64;
94
95 const u64 cache_line_start = value & ~(ICACHE_LINE_SIZE - 1);
96 parent.InvalidateCacheRange(cache_line_start, ICACHE_LINE_SIZE);
97 break;
98 }
99 case Dynarmic::A64::InstructionCacheOperation::InvalidateAllToPoU:
100 parent.ClearInstructionCache();
101 break;
102 case Dynarmic::A64::InstructionCacheOperation::InvalidateAllToPoUInnerSharable:
103 default:
104 LOG_DEBUG(Core_ARM, "Unprocesseed instruction cache operation: {}", op);
105 break;
106 }
107 }
108
89 void ExceptionRaised(u64 pc, Dynarmic::A64::Exception exception) override { 109 void ExceptionRaised(u64 pc, Dynarmic::A64::Exception exception) override {
90 switch (exception) { 110 switch (exception) {
91 case Dynarmic::A64::Exception::WaitForInterrupt: 111 case Dynarmic::A64::Exception::WaitForInterrupt:
diff --git a/src/core/core.cpp b/src/core/core.cpp
index 07448fd29..aa96f709b 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -27,6 +27,7 @@
27#include "core/file_sys/vfs_concat.h" 27#include "core/file_sys/vfs_concat.h"
28#include "core/file_sys/vfs_real.h" 28#include "core/file_sys/vfs_real.h"
29#include "core/hardware_interrupt_manager.h" 29#include "core/hardware_interrupt_manager.h"
30#include "core/hid/hid_core.h"
30#include "core/hle/kernel/k_process.h" 31#include "core/hle/kernel/k_process.h"
31#include "core/hle/kernel/k_scheduler.h" 32#include "core/hle/kernel/k_scheduler.h"
32#include "core/hle/kernel/kernel.h" 33#include "core/hle/kernel/kernel.h"
@@ -126,7 +127,7 @@ FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs,
126 127
127struct System::Impl { 128struct System::Impl {
128 explicit Impl(System& system) 129 explicit Impl(System& system)
129 : kernel{system}, fs_controller{system}, memory{system}, 130 : kernel{system}, fs_controller{system}, memory{system}, hid_core{},
130 cpu_manager{system}, reporter{system}, applet_manager{system}, time_manager{system} {} 131 cpu_manager{system}, reporter{system}, applet_manager{system}, time_manager{system} {}
131 132
132 SystemResultStatus Run() { 133 SystemResultStatus Run() {
@@ -391,6 +392,7 @@ struct System::Impl {
391 std::unique_ptr<Hardware::InterruptManager> interrupt_manager; 392 std::unique_ptr<Hardware::InterruptManager> interrupt_manager;
392 std::unique_ptr<Core::DeviceMemory> device_memory; 393 std::unique_ptr<Core::DeviceMemory> device_memory;
393 Core::Memory::Memory memory; 394 Core::Memory::Memory memory;
395 Core::HID::HIDCore hid_core;
394 CpuManager cpu_manager; 396 CpuManager cpu_manager;
395 std::atomic_bool is_powered_on{}; 397 std::atomic_bool is_powered_on{};
396 bool exit_lock = false; 398 bool exit_lock = false;
@@ -519,12 +521,6 @@ const ARM_Interface& System::CurrentArmInterface() const {
519 return impl->kernel.CurrentPhysicalCore().ArmInterface(); 521 return impl->kernel.CurrentPhysicalCore().ArmInterface();
520} 522}
521 523
522std::size_t System::CurrentCoreIndex() const {
523 std::size_t core = impl->kernel.GetCurrentHostThreadID();
524 ASSERT(core < Core::Hardware::NUM_CPU_CORES);
525 return core;
526}
527
528Kernel::PhysicalCore& System::CurrentPhysicalCore() { 524Kernel::PhysicalCore& System::CurrentPhysicalCore() {
529 return impl->kernel.CurrentPhysicalCore(); 525 return impl->kernel.CurrentPhysicalCore();
530} 526}
@@ -615,6 +611,14 @@ const Kernel::KernelCore& System::Kernel() const {
615 return impl->kernel; 611 return impl->kernel;
616} 612}
617 613
614HID::HIDCore& System::HIDCore() {
615 return impl->hid_core;
616}
617
618const HID::HIDCore& System::HIDCore() const {
619 return impl->hid_core;
620}
621
618Timing::CoreTiming& System::CoreTiming() { 622Timing::CoreTiming& System::CoreTiming() {
619 return impl->core_timing; 623 return impl->core_timing;
620} 624}
@@ -825,8 +829,6 @@ void System::ApplySettings() {
825 if (IsPoweredOn()) { 829 if (IsPoweredOn()) {
826 Renderer().RefreshBaseSettings(); 830 Renderer().RefreshBaseSettings();
827 } 831 }
828
829 Service::HID::ReloadInputDevices();
830} 832}
831 833
832} // namespace Core 834} // namespace Core
diff --git a/src/core/core.h b/src/core/core.h
index 01bc0a2c7..52ff90359 100644
--- a/src/core/core.h
+++ b/src/core/core.h
@@ -89,6 +89,10 @@ namespace Core::Hardware {
89class InterruptManager; 89class InterruptManager;
90} 90}
91 91
92namespace Core::HID {
93class HIDCore;
94}
95
92namespace Core { 96namespace Core {
93 97
94class ARM_Interface; 98class ARM_Interface;
@@ -204,9 +208,6 @@ public:
204 /// Gets an ARM interface to the CPU core that is currently running 208 /// Gets an ARM interface to the CPU core that is currently running
205 [[nodiscard]] const ARM_Interface& CurrentArmInterface() const; 209 [[nodiscard]] const ARM_Interface& CurrentArmInterface() const;
206 210
207 /// Gets the index of the currently running CPU core
208 [[nodiscard]] std::size_t CurrentCoreIndex() const;
209
210 /// Gets the physical core for the CPU core that is currently running 211 /// Gets the physical core for the CPU core that is currently running
211 [[nodiscard]] Kernel::PhysicalCore& CurrentPhysicalCore(); 212 [[nodiscard]] Kernel::PhysicalCore& CurrentPhysicalCore();
212 213
@@ -285,6 +286,12 @@ public:
285 /// Provides a constant reference to the kernel instance. 286 /// Provides a constant reference to the kernel instance.
286 [[nodiscard]] const Kernel::KernelCore& Kernel() const; 287 [[nodiscard]] const Kernel::KernelCore& Kernel() const;
287 288
289 /// Gets a mutable reference to the HID interface.
290 [[nodiscard]] HID::HIDCore& HIDCore();
291
292 /// Gets an immutable reference to the HID interface.
293 [[nodiscard]] const HID::HIDCore& HIDCore() const;
294
288 /// Provides a reference to the internal PerfStats instance. 295 /// Provides a reference to the internal PerfStats instance.
289 [[nodiscard]] Core::PerfStats& GetPerfStats(); 296 [[nodiscard]] Core::PerfStats& GetPerfStats();
290 297
diff --git a/src/core/cpu_manager.cpp b/src/core/cpu_manager.cpp
index 5d43c6e5d..cbcc54891 100644
--- a/src/core/cpu_manager.cpp
+++ b/src/core/cpu_manager.cpp
@@ -117,17 +117,18 @@ void CpuManager::MultiCoreRunGuestLoop() {
117 physical_core = &kernel.CurrentPhysicalCore(); 117 physical_core = &kernel.CurrentPhysicalCore();
118 } 118 }
119 system.ExitDynarmicProfile(); 119 system.ExitDynarmicProfile();
120 physical_core->ArmInterface().ClearExclusiveState(); 120 {
121 kernel.CurrentScheduler()->RescheduleCurrentCore(); 121 Kernel::KScopedDisableDispatch dd(kernel);
122 physical_core->ArmInterface().ClearExclusiveState();
123 }
122 } 124 }
123} 125}
124 126
125void CpuManager::MultiCoreRunIdleThread() { 127void CpuManager::MultiCoreRunIdleThread() {
126 auto& kernel = system.Kernel(); 128 auto& kernel = system.Kernel();
127 while (true) { 129 while (true) {
128 auto& physical_core = kernel.CurrentPhysicalCore(); 130 Kernel::KScopedDisableDispatch dd(kernel);
129 physical_core.Idle(); 131 kernel.CurrentPhysicalCore().Idle();
130 kernel.CurrentScheduler()->RescheduleCurrentCore();
131 } 132 }
132} 133}
133 134
@@ -135,12 +136,12 @@ void CpuManager::MultiCoreRunSuspendThread() {
135 auto& kernel = system.Kernel(); 136 auto& kernel = system.Kernel();
136 kernel.CurrentScheduler()->OnThreadStart(); 137 kernel.CurrentScheduler()->OnThreadStart();
137 while (true) { 138 while (true) {
138 auto core = kernel.GetCurrentHostThreadID(); 139 auto core = kernel.CurrentPhysicalCoreIndex();
139 auto& scheduler = *kernel.CurrentScheduler(); 140 auto& scheduler = *kernel.CurrentScheduler();
140 Kernel::KThread* current_thread = scheduler.GetCurrentThread(); 141 Kernel::KThread* current_thread = scheduler.GetCurrentThread();
141 Common::Fiber::YieldTo(current_thread->GetHostContext(), *core_data[core].host_context); 142 Common::Fiber::YieldTo(current_thread->GetHostContext(), *core_data[core].host_context);
142 ASSERT(scheduler.ContextSwitchPending()); 143 ASSERT(scheduler.ContextSwitchPending());
143 ASSERT(core == kernel.GetCurrentHostThreadID()); 144 ASSERT(core == kernel.CurrentPhysicalCoreIndex());
144 scheduler.RescheduleCurrentCore(); 145 scheduler.RescheduleCurrentCore();
145 } 146 }
146} 147}
@@ -346,13 +347,9 @@ void CpuManager::RunThread(std::stop_token stop_token, std::size_t core) {
346 sc_sync_first_use = false; 347 sc_sync_first_use = false;
347 } 348 }
348 349
349 // Abort if emulation was killed before the session really starts 350 // Emulation was stopped
350 if (!system.IsPoweredOn()) {
351 return;
352 }
353
354 if (stop_token.stop_requested()) { 351 if (stop_token.stop_requested()) {
355 break; 352 return;
356 } 353 }
357 354
358 auto current_thread = system.Kernel().CurrentScheduler()->GetCurrentThread(); 355 auto current_thread = system.Kernel().CurrentScheduler()->GetCurrentThread();
diff --git a/src/core/frontend/applets/controller.cpp b/src/core/frontend/applets/controller.cpp
index 03bbedf8b..6dbd38ffa 100644
--- a/src/core/frontend/applets/controller.cpp
+++ b/src/core/frontend/applets/controller.cpp
@@ -5,16 +5,15 @@
5#include "common/assert.h" 5#include "common/assert.h"
6#include "common/logging/log.h" 6#include "common/logging/log.h"
7#include "core/frontend/applets/controller.h" 7#include "core/frontend/applets/controller.h"
8#include "core/hle/service/hid/controllers/npad.h" 8#include "core/hid/emulated_controller.h"
9#include "core/hle/service/hid/hid.h" 9#include "core/hid/hid_core.h"
10#include "core/hle/service/sm/sm.h" 10#include "core/hid/hid_types.h"
11 11
12namespace Core::Frontend { 12namespace Core::Frontend {
13 13
14ControllerApplet::~ControllerApplet() = default; 14ControllerApplet::~ControllerApplet() = default;
15 15
16DefaultControllerApplet::DefaultControllerApplet(Service::SM::ServiceManager& service_manager_) 16DefaultControllerApplet::DefaultControllerApplet(HID::HIDCore& hid_core_) : hid_core{hid_core_} {}
17 : service_manager{service_manager_} {}
18 17
19DefaultControllerApplet::~DefaultControllerApplet() = default; 18DefaultControllerApplet::~DefaultControllerApplet() = default;
20 19
@@ -22,24 +21,20 @@ void DefaultControllerApplet::ReconfigureControllers(std::function<void()> callb
22 const ControllerParameters& parameters) const { 21 const ControllerParameters& parameters) const {
23 LOG_INFO(Service_HID, "called, deducing the best configuration based on the given parameters!"); 22 LOG_INFO(Service_HID, "called, deducing the best configuration based on the given parameters!");
24 23
25 auto& npad =
26 service_manager.GetService<Service::HID::Hid>("hid")
27 ->GetAppletResource()
28 ->GetController<Service::HID::Controller_NPad>(Service::HID::HidController::NPad);
29
30 auto& players = Settings::values.players.GetValue();
31
32 const std::size_t min_supported_players = 24 const std::size_t min_supported_players =
33 parameters.enable_single_mode ? 1 : parameters.min_players; 25 parameters.enable_single_mode ? 1 : parameters.min_players;
34 26
35 // Disconnect Handheld first. 27 // Disconnect Handheld first.
36 npad.DisconnectNpadAtIndex(8); 28 auto* handheld = hid_core.GetEmulatedController(Core::HID::NpadIdType::Handheld);
29 handheld->Disconnect();
37 30
38 // Deduce the best configuration based on the input parameters. 31 // Deduce the best configuration based on the input parameters.
39 for (std::size_t index = 0; index < players.size() - 2; ++index) { 32 for (std::size_t index = 0; index < hid_core.available_controllers - 2; ++index) {
33 auto* controller = hid_core.GetEmulatedControllerByIndex(index);
34
40 // First, disconnect all controllers regardless of the value of keep_controllers_connected. 35 // First, disconnect all controllers regardless of the value of keep_controllers_connected.
41 // This makes it easy to connect the desired controllers. 36 // This makes it easy to connect the desired controllers.
42 npad.DisconnectNpadAtIndex(index); 37 controller->Disconnect();
43 38
44 // Only connect the minimum number of required players. 39 // Only connect the minimum number of required players.
45 if (index >= min_supported_players) { 40 if (index >= min_supported_players) {
@@ -49,27 +44,27 @@ void DefaultControllerApplet::ReconfigureControllers(std::function<void()> callb
49 // Connect controllers based on the following priority list from highest to lowest priority: 44 // Connect controllers based on the following priority list from highest to lowest priority:
50 // Pro Controller -> Dual Joycons -> Left Joycon/Right Joycon -> Handheld 45 // Pro Controller -> Dual Joycons -> Left Joycon/Right Joycon -> Handheld
51 if (parameters.allow_pro_controller) { 46 if (parameters.allow_pro_controller) {
52 npad.AddNewControllerAt( 47 controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::ProController);
53 npad.MapSettingsTypeToNPad(Settings::ControllerType::ProController), index); 48 controller->Connect();
54 } else if (parameters.allow_dual_joycons) { 49 } else if (parameters.allow_dual_joycons) {
55 npad.AddNewControllerAt( 50 controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::JoyconDual);
56 npad.MapSettingsTypeToNPad(Settings::ControllerType::DualJoyconDetached), index); 51 controller->Connect();
57 } else if (parameters.allow_left_joycon && parameters.allow_right_joycon) { 52 } else if (parameters.allow_left_joycon && parameters.allow_right_joycon) {
58 // Assign left joycons to even player indices and right joycons to odd player indices. 53 // Assign left joycons to even player indices and right joycons to odd player indices.
59 // We do this since Captain Toad Treasure Tracker expects a left joycon for Player 1 and 54 // We do this since Captain Toad Treasure Tracker expects a left joycon for Player 1 and
60 // a right Joycon for Player 2 in 2 Player Assist mode. 55 // a right Joycon for Player 2 in 2 Player Assist mode.
61 if (index % 2 == 0) { 56 if (index % 2 == 0) {
62 npad.AddNewControllerAt( 57 controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::JoyconLeft);
63 npad.MapSettingsTypeToNPad(Settings::ControllerType::LeftJoycon), index); 58 controller->Connect();
64 } else { 59 } else {
65 npad.AddNewControllerAt( 60 controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::JoyconRight);
66 npad.MapSettingsTypeToNPad(Settings::ControllerType::RightJoycon), index); 61 controller->Connect();
67 } 62 }
68 } else if (index == 0 && parameters.enable_single_mode && parameters.allow_handheld && 63 } else if (index == 0 && parameters.enable_single_mode && parameters.allow_handheld &&
69 !Settings::values.use_docked_mode.GetValue()) { 64 !Settings::values.use_docked_mode.GetValue()) {
70 // We should *never* reach here under any normal circumstances. 65 // We should *never* reach here under any normal circumstances.
71 npad.AddNewControllerAt(npad.MapSettingsTypeToNPad(Settings::ControllerType::Handheld), 66 controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::Handheld);
72 index); 67 controller->Connect();
73 } else { 68 } else {
74 UNREACHABLE_MSG("Unable to add a new controller based on the given parameters!"); 69 UNREACHABLE_MSG("Unable to add a new controller based on the given parameters!");
75 } 70 }
diff --git a/src/core/frontend/applets/controller.h b/src/core/frontend/applets/controller.h
index b0626a0f9..014bc8901 100644
--- a/src/core/frontend/applets/controller.h
+++ b/src/core/frontend/applets/controller.h
@@ -8,8 +8,8 @@
8 8
9#include "common/common_types.h" 9#include "common/common_types.h"
10 10
11namespace Service::SM { 11namespace Core::HID {
12class ServiceManager; 12class HIDCore;
13} 13}
14 14
15namespace Core::Frontend { 15namespace Core::Frontend {
@@ -44,14 +44,14 @@ public:
44 44
45class DefaultControllerApplet final : public ControllerApplet { 45class DefaultControllerApplet final : public ControllerApplet {
46public: 46public:
47 explicit DefaultControllerApplet(Service::SM::ServiceManager& service_manager_); 47 explicit DefaultControllerApplet(HID::HIDCore& hid_core_);
48 ~DefaultControllerApplet() override; 48 ~DefaultControllerApplet() override;
49 49
50 void ReconfigureControllers(std::function<void()> callback, 50 void ReconfigureControllers(std::function<void()> callback,
51 const ControllerParameters& parameters) const override; 51 const ControllerParameters& parameters) const override;
52 52
53private: 53private:
54 Service::SM::ServiceManager& service_manager; 54 HID::HIDCore& hid_core;
55}; 55};
56 56
57} // namespace Core::Frontend 57} // namespace Core::Frontend
diff --git a/src/core/frontend/emu_window.cpp b/src/core/frontend/emu_window.cpp
index e1f7e5886..57c6ffc43 100644
--- a/src/core/frontend/emu_window.cpp
+++ b/src/core/frontend/emu_window.cpp
@@ -3,87 +3,23 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <mutex> 5#include <mutex>
6#include "common/settings.h"
7#include "core/frontend/emu_window.h" 6#include "core/frontend/emu_window.h"
8#include "core/frontend/input.h"
9 7
10namespace Core::Frontend { 8namespace Core::Frontend {
11 9
12GraphicsContext::~GraphicsContext() = default; 10GraphicsContext::~GraphicsContext() = default;
13 11
14class EmuWindow::TouchState : public Input::Factory<Input::TouchDevice>,
15 public std::enable_shared_from_this<TouchState> {
16public:
17 std::unique_ptr<Input::TouchDevice> Create(const Common::ParamPackage&) override {
18 return std::make_unique<Device>(shared_from_this());
19 }
20
21 std::mutex mutex;
22
23 Input::TouchStatus status;
24
25private:
26 class Device : public Input::TouchDevice {
27 public:
28 explicit Device(std::weak_ptr<TouchState>&& touch_state_) : touch_state(touch_state_) {}
29 Input::TouchStatus GetStatus() const override {
30 if (auto state = touch_state.lock()) {
31 std::lock_guard guard{state->mutex};
32 return state->status;
33 }
34 return {};
35 }
36
37 private:
38 std::weak_ptr<TouchState> touch_state;
39 };
40};
41
42EmuWindow::EmuWindow() { 12EmuWindow::EmuWindow() {
43 // TODO: Find a better place to set this. 13 // TODO: Find a better place to set this.
44 config.min_client_area_size = 14 config.min_client_area_size =
45 std::make_pair(Layout::MinimumSize::Width, Layout::MinimumSize::Height); 15 std::make_pair(Layout::MinimumSize::Width, Layout::MinimumSize::Height);
46 active_config = config; 16 active_config = config;
47 touch_state = std::make_shared<TouchState>();
48 Input::RegisterFactory<Input::TouchDevice>("emu_window", touch_state);
49}
50
51EmuWindow::~EmuWindow() {
52 Input::UnregisterFactory<Input::TouchDevice>("emu_window");
53}
54
55/**
56 * Check if the given x/y coordinates are within the touchpad specified by the framebuffer layout
57 * @param layout FramebufferLayout object describing the framebuffer size and screen positions
58 * @param framebuffer_x Framebuffer x-coordinate to check
59 * @param framebuffer_y Framebuffer y-coordinate to check
60 * @return True if the coordinates are within the touchpad, otherwise false
61 */
62static bool IsWithinTouchscreen(const Layout::FramebufferLayout& layout, u32 framebuffer_x,
63 u32 framebuffer_y) {
64 return (framebuffer_y >= layout.screen.top && framebuffer_y < layout.screen.bottom &&
65 framebuffer_x >= layout.screen.left && framebuffer_x < layout.screen.right);
66}
67
68std::pair<u32, u32> EmuWindow::ClipToTouchScreen(u32 new_x, u32 new_y) const {
69 new_x = std::max(new_x, framebuffer_layout.screen.left);
70 new_x = std::min(new_x, framebuffer_layout.screen.right - 1);
71
72 new_y = std::max(new_y, framebuffer_layout.screen.top);
73 new_y = std::min(new_y, framebuffer_layout.screen.bottom - 1);
74
75 return std::make_pair(new_x, new_y);
76} 17}
77 18
78void EmuWindow::TouchPressed(u32 framebuffer_x, u32 framebuffer_y, size_t id) { 19EmuWindow::~EmuWindow() {}
79 if (!IsWithinTouchscreen(framebuffer_layout, framebuffer_x, framebuffer_y)) {
80 return;
81 }
82 if (id >= touch_state->status.size()) {
83 return;
84 }
85 20
86 std::lock_guard guard{touch_state->mutex}; 21std::pair<f32, f32> EmuWindow::MapToTouchScreen(u32 framebuffer_x, u32 framebuffer_y) const {
22 std::tie(framebuffer_x, framebuffer_y) = ClipToTouchScreen(framebuffer_x, framebuffer_y);
87 const float x = 23 const float x =
88 static_cast<float>(framebuffer_x - framebuffer_layout.screen.left) / 24 static_cast<float>(framebuffer_x - framebuffer_layout.screen.left) /
89 static_cast<float>(framebuffer_layout.screen.right - framebuffer_layout.screen.left); 25 static_cast<float>(framebuffer_layout.screen.right - framebuffer_layout.screen.left);
@@ -91,31 +27,17 @@ void EmuWindow::TouchPressed(u32 framebuffer_x, u32 framebuffer_y, size_t id) {
91 static_cast<float>(framebuffer_y - framebuffer_layout.screen.top) / 27 static_cast<float>(framebuffer_y - framebuffer_layout.screen.top) /
92 static_cast<float>(framebuffer_layout.screen.bottom - framebuffer_layout.screen.top); 28 static_cast<float>(framebuffer_layout.screen.bottom - framebuffer_layout.screen.top);
93 29
94 touch_state->status[id] = std::make_tuple(x, y, true); 30 return std::make_pair(x, y);
95}
96
97void EmuWindow::TouchReleased(size_t id) {
98 if (id >= touch_state->status.size()) {
99 return;
100 }
101 std::lock_guard guard{touch_state->mutex};
102 touch_state->status[id] = std::make_tuple(0.0f, 0.0f, false);
103} 31}
104 32
105void EmuWindow::TouchMoved(u32 framebuffer_x, u32 framebuffer_y, size_t id) { 33std::pair<u32, u32> EmuWindow::ClipToTouchScreen(u32 new_x, u32 new_y) const {
106 if (id >= touch_state->status.size()) { 34 new_x = std::max(new_x, framebuffer_layout.screen.left);
107 return; 35 new_x = std::min(new_x, framebuffer_layout.screen.right - 1);
108 }
109
110 if (!std::get<2>(touch_state->status[id])) {
111 return;
112 }
113 36
114 if (!IsWithinTouchscreen(framebuffer_layout, framebuffer_x, framebuffer_y)) { 37 new_y = std::max(new_y, framebuffer_layout.screen.top);
115 std::tie(framebuffer_x, framebuffer_y) = ClipToTouchScreen(framebuffer_x, framebuffer_y); 38 new_y = std::min(new_y, framebuffer_layout.screen.bottom - 1);
116 }
117 39
118 TouchPressed(framebuffer_x, framebuffer_y, id); 40 return std::make_pair(new_x, new_y);
119} 41}
120 42
121void EmuWindow::UpdateCurrentFramebufferLayout(u32 width, u32 height) { 43void EmuWindow::UpdateCurrentFramebufferLayout(u32 width, u32 height) {
diff --git a/src/core/frontend/emu_window.h b/src/core/frontend/emu_window.h
index 8a86a1d27..e413a520a 100644
--- a/src/core/frontend/emu_window.h
+++ b/src/core/frontend/emu_window.h
@@ -113,28 +113,6 @@ public:
113 virtual bool IsShown() const = 0; 113 virtual bool IsShown() const = 0;
114 114
115 /** 115 /**
116 * Signal that a touch pressed event has occurred (e.g. mouse click pressed)
117 * @param framebuffer_x Framebuffer x-coordinate that was pressed
118 * @param framebuffer_y Framebuffer y-coordinate that was pressed
119 * @param id Touch event ID
120 */
121 void TouchPressed(u32 framebuffer_x, u32 framebuffer_y, size_t id);
122
123 /**
124 * Signal that a touch released event has occurred (e.g. mouse click released)
125 * @param id Touch event ID
126 */
127 void TouchReleased(size_t id);
128
129 /**
130 * Signal that a touch movement event has occurred (e.g. mouse was moved over the emu window)
131 * @param framebuffer_x Framebuffer x-coordinate
132 * @param framebuffer_y Framebuffer y-coordinate
133 * @param id Touch event ID
134 */
135 void TouchMoved(u32 framebuffer_x, u32 framebuffer_y, size_t id);
136
137 /**
138 * Returns currently active configuration. 116 * Returns currently active configuration.
139 * @note Accesses to the returned object need not be consistent because it may be modified in 117 * @note Accesses to the returned object need not be consistent because it may be modified in
140 * another thread 118 * another thread
@@ -212,6 +190,11 @@ protected:
212 client_area_height = size.second; 190 client_area_height = size.second;
213 } 191 }
214 192
193 /**
194 * Converts a screen postion into the equivalent touchscreen position.
195 */
196 std::pair<f32, f32> MapToTouchScreen(u32 framebuffer_x, u32 framebuffer_y) const;
197
215 WindowSystemInfo window_info; 198 WindowSystemInfo window_info;
216 199
217private: 200private:
@@ -237,9 +220,6 @@ private:
237 WindowConfig config; ///< Internal configuration (changes pending for being applied in 220 WindowConfig config; ///< Internal configuration (changes pending for being applied in
238 /// ProcessConfigurationChanges) 221 /// ProcessConfigurationChanges)
239 WindowConfig active_config; ///< Internal active configuration 222 WindowConfig active_config; ///< Internal active configuration
240
241 class TouchState;
242 std::shared_ptr<TouchState> touch_state;
243}; 223};
244 224
245} // namespace Core::Frontend 225} // namespace Core::Frontend
diff --git a/src/core/frontend/framebuffer_layout.cpp b/src/core/frontend/framebuffer_layout.cpp
index 4b58b672a..26a5b12aa 100644
--- a/src/core/frontend/framebuffer_layout.cpp
+++ b/src/core/frontend/framebuffer_layout.cpp
@@ -25,7 +25,12 @@ FramebufferLayout DefaultFrameLayout(u32 width, u32 height) {
25 ASSERT(height > 0); 25 ASSERT(height > 0);
26 // The drawing code needs at least somewhat valid values for both screens 26 // The drawing code needs at least somewhat valid values for both screens
27 // so just calculate them both even if the other isn't showing. 27 // so just calculate them both even if the other isn't showing.
28 FramebufferLayout res{width, height, false, {}}; 28 FramebufferLayout res{
29 .width = width,
30 .height = height,
31 .screen = {},
32 .is_srgb = false,
33 };
29 34
30 const float window_aspect_ratio = static_cast<float>(height) / static_cast<float>(width); 35 const float window_aspect_ratio = static_cast<float>(height) / static_cast<float>(width);
31 const float emulation_aspect_ratio = EmulationAspectRatio( 36 const float emulation_aspect_ratio = EmulationAspectRatio(
diff --git a/src/core/frontend/framebuffer_layout.h b/src/core/frontend/framebuffer_layout.h
index 2e36c0163..8e341e4e2 100644
--- a/src/core/frontend/framebuffer_layout.h
+++ b/src/core/frontend/framebuffer_layout.h
@@ -35,17 +35,8 @@ enum class AspectRatio {
35struct FramebufferLayout { 35struct FramebufferLayout {
36 u32 width{ScreenUndocked::Width}; 36 u32 width{ScreenUndocked::Width};
37 u32 height{ScreenUndocked::Height}; 37 u32 height{ScreenUndocked::Height};
38 bool is_srgb{};
39
40 Common::Rectangle<u32> screen; 38 Common::Rectangle<u32> screen;
41 39 bool is_srgb{};
42 /**
43 * Returns the ration of pixel size of the screen, compared to the native size of the undocked
44 * Switch screen.
45 */
46 float GetScalingRatio() const {
47 return static_cast<float>(screen.GetWidth()) / ScreenUndocked::Width;
48 }
49}; 40};
50 41
51/** 42/**
diff --git a/src/core/frontend/input.h b/src/core/frontend/input.h
deleted file mode 100644
index f1747c5b2..000000000
--- a/src/core/frontend/input.h
+++ /dev/null
@@ -1,217 +0,0 @@
1// Copyright 2017 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <functional>
8#include <memory>
9#include <string>
10#include <tuple>
11#include <unordered_map>
12#include <utility>
13#include "common/logging/log.h"
14#include "common/param_package.h"
15#include "common/quaternion.h"
16#include "common/vector_math.h"
17
18namespace Input {
19
20enum class AnalogDirection : u8 {
21 RIGHT,
22 LEFT,
23 UP,
24 DOWN,
25};
26struct AnalogProperties {
27 float deadzone;
28 float range;
29 float threshold;
30};
31template <typename StatusType>
32struct InputCallback {
33 std::function<void(StatusType)> on_change;
34};
35
36/// An abstract class template for an input device (a button, an analog input, etc.).
37template <typename StatusType>
38class InputDevice {
39public:
40 virtual ~InputDevice() = default;
41 virtual StatusType GetStatus() const {
42 return {};
43 }
44 virtual StatusType GetRawStatus() const {
45 return GetStatus();
46 }
47 virtual AnalogProperties GetAnalogProperties() const {
48 return {};
49 }
50 virtual bool GetAnalogDirectionStatus([[maybe_unused]] AnalogDirection direction) const {
51 return {};
52 }
53 virtual bool SetRumblePlay([[maybe_unused]] f32 amp_low, [[maybe_unused]] f32 freq_low,
54 [[maybe_unused]] f32 amp_high,
55 [[maybe_unused]] f32 freq_high) const {
56 return {};
57 }
58 void SetCallback(InputCallback<StatusType> callback_) {
59 callback = std::move(callback_);
60 }
61 void TriggerOnChange() {
62 if (callback.on_change) {
63 callback.on_change(GetStatus());
64 }
65 }
66
67private:
68 InputCallback<StatusType> callback;
69};
70
71/// An abstract class template for a factory that can create input devices.
72template <typename InputDeviceType>
73class Factory {
74public:
75 virtual ~Factory() = default;
76 virtual std::unique_ptr<InputDeviceType> Create(const Common::ParamPackage&) = 0;
77};
78
79namespace Impl {
80
81template <typename InputDeviceType>
82using FactoryListType = std::unordered_map<std::string, std::shared_ptr<Factory<InputDeviceType>>>;
83
84template <typename InputDeviceType>
85struct FactoryList {
86 static FactoryListType<InputDeviceType> list;
87};
88
89template <typename InputDeviceType>
90FactoryListType<InputDeviceType> FactoryList<InputDeviceType>::list;
91
92} // namespace Impl
93
94/**
95 * Registers an input device factory.
96 * @tparam InputDeviceType the type of input devices the factory can create
97 * @param name the name of the factory. Will be used to match the "engine" parameter when creating
98 * a device
99 * @param factory the factory object to register
100 */
101template <typename InputDeviceType>
102void RegisterFactory(const std::string& name, std::shared_ptr<Factory<InputDeviceType>> factory) {
103 auto pair = std::make_pair(name, std::move(factory));
104 if (!Impl::FactoryList<InputDeviceType>::list.insert(std::move(pair)).second) {
105 LOG_ERROR(Input, "Factory '{}' already registered", name);
106 }
107}
108
109/**
110 * Unregisters an input device factory.
111 * @tparam InputDeviceType the type of input devices the factory can create
112 * @param name the name of the factory to unregister
113 */
114template <typename InputDeviceType>
115void UnregisterFactory(const std::string& name) {
116 if (Impl::FactoryList<InputDeviceType>::list.erase(name) == 0) {
117 LOG_ERROR(Input, "Factory '{}' not registered", name);
118 }
119}
120
121/**
122 * Create an input device from given paramters.
123 * @tparam InputDeviceType the type of input devices to create
124 * @param params a serialized ParamPackage string contains all parameters for creating the device
125 */
126template <typename InputDeviceType>
127std::unique_ptr<InputDeviceType> CreateDevice(const std::string& params) {
128 const Common::ParamPackage package(params);
129 const std::string engine = package.Get("engine", "null");
130 const auto& factory_list = Impl::FactoryList<InputDeviceType>::list;
131 const auto pair = factory_list.find(engine);
132 if (pair == factory_list.end()) {
133 if (engine != "null") {
134 LOG_ERROR(Input, "Unknown engine name: {}", engine);
135 }
136 return std::make_unique<InputDeviceType>();
137 }
138 return pair->second->Create(package);
139}
140
141/**
142 * A button device is an input device that returns bool as status.
143 * true for pressed; false for released.
144 */
145using ButtonDevice = InputDevice<bool>;
146
147/**
148 * An analog device is an input device that returns a tuple of x and y coordinates as status. The
149 * coordinates are within the unit circle. x+ is defined as right direction, and y+ is defined as up
150 * direction
151 */
152using AnalogDevice = InputDevice<std::tuple<float, float>>;
153
154/**
155 * A vibration device is an input device that returns an unsigned byte as status.
156 * It represents whether the vibration device supports vibration or not.
157 * If the status returns 1, it supports vibration. Otherwise, it does not support vibration.
158 */
159using VibrationDevice = InputDevice<u8>;
160
161/**
162 * A motion status is an object that returns a tuple of accelerometer state vector,
163 * gyroscope state vector, rotation state vector, orientation state matrix and quaterion state
164 * vector.
165 *
166 * For both 3D vectors:
167 * x+ is the same direction as RIGHT on D-pad.
168 * y+ is normal to the touch screen, pointing outward.
169 * z+ is the same direction as UP on D-pad.
170 *
171 * For accelerometer state vector
172 * Units: g (gravitational acceleration)
173 *
174 * For gyroscope state vector:
175 * Orientation is determined by right-hand rule.
176 * Units: deg/sec
177 *
178 * For rotation state vector
179 * Units: rotations
180 *
181 * For orientation state matrix
182 * x vector
183 * y vector
184 * z vector
185 *
186 * For quaternion state vector
187 * xyz vector
188 * w float
189 */
190using MotionStatus = std::tuple<Common::Vec3<float>, Common::Vec3<float>, Common::Vec3<float>,
191 std::array<Common::Vec3f, 3>, Common::Quaternion<f32>>;
192
193/**
194 * A motion device is an input device that returns a motion status object
195 */
196using MotionDevice = InputDevice<MotionStatus>;
197
198/**
199 * A touch status is an object that returns an array of 16 tuple elements of two floats and a bool.
200 * The floats are x and y coordinates in the range 0.0 - 1.0, and the bool indicates whether it is
201 * pressed.
202 */
203using TouchStatus = std::array<std::tuple<float, float, bool>, 16>;
204
205/**
206 * A touch device is an input device that returns a touch status object
207 */
208using TouchDevice = InputDevice<TouchStatus>;
209
210/**
211 * A mouse device is an input device that returns a tuple of two floats and four ints.
212 * The first two floats are X and Y device coordinates of the mouse (from 0-1).
213 * The s32s are the mouse wheel.
214 */
215using MouseDevice = InputDevice<std::tuple<float, float, s32, s32>>;
216
217} // namespace Input
diff --git a/src/core/hid/emulated_console.cpp b/src/core/hid/emulated_console.cpp
new file mode 100644
index 000000000..685ec080c
--- /dev/null
+++ b/src/core/hid/emulated_console.cpp
@@ -0,0 +1,232 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included
4
5#include "common/settings.h"
6#include "core/hid/emulated_console.h"
7#include "core/hid/input_converter.h"
8
9namespace Core::HID {
10EmulatedConsole::EmulatedConsole() = default;
11
12EmulatedConsole::~EmulatedConsole() = default;
13
14void EmulatedConsole::ReloadFromSettings() {
15 // Using first motion device from player 1. No need to assign any unique config at the moment
16 const auto& player = Settings::values.players.GetValue()[0];
17 motion_params = Common::ParamPackage(player.motions[0]);
18
19 ReloadInput();
20}
21
22void EmulatedConsole::SetTouchParams() {
23 // TODO(german77): Support any number of fingers
24 std::size_t index = 0;
25
26 // Hardcode mouse, touchscreen and cemuhook parameters
27 if (!Settings::values.mouse_enabled) {
28 // We can't use mouse as touch if native mouse is enabled
29 touch_params[index++] = Common::ParamPackage{"engine:mouse,axis_x:10,axis_y:11,button:0"};
30 }
31 touch_params[index++] = Common::ParamPackage{"engine:touch,axis_x:0,axis_y:1,button:0"};
32 touch_params[index++] = Common::ParamPackage{"engine:touch,axis_x:2,axis_y:3,button:1"};
33 touch_params[index++] =
34 Common::ParamPackage{"engine:cemuhookudp,axis_x:17,axis_y:18,button:65536"};
35 touch_params[index++] =
36 Common::ParamPackage{"engine:cemuhookudp,axis_x:19,axis_y:20,button:131072"};
37
38 const auto button_index =
39 static_cast<u64>(Settings::values.touch_from_button_map_index.GetValue());
40 const auto& touch_buttons = Settings::values.touch_from_button_maps[button_index].buttons;
41
42 // Map the rest of the fingers from touch from button configuration
43 for (const auto& config_entry : touch_buttons) {
44 if (index >= touch_params.size()) {
45 continue;
46 }
47 Common::ParamPackage params{config_entry};
48 Common::ParamPackage touch_button_params;
49 const int x = params.Get("x", 0);
50 const int y = params.Get("y", 0);
51 params.Erase("x");
52 params.Erase("y");
53 touch_button_params.Set("engine", "touch_from_button");
54 touch_button_params.Set("button", params.Serialize());
55 touch_button_params.Set("x", x);
56 touch_button_params.Set("y", y);
57 touch_button_params.Set("touch_id", static_cast<int>(index));
58 touch_params[index] = touch_button_params;
59 index++;
60 }
61}
62
63void EmulatedConsole::ReloadInput() {
64 // If you load any device here add the equivalent to the UnloadInput() function
65 SetTouchParams();
66
67 motion_devices = Common::Input::CreateDevice<Common::Input::InputDevice>(motion_params);
68 if (motion_devices) {
69 motion_devices->SetCallback({
70 .on_change =
71 [this](const Common::Input::CallbackStatus& callback) { SetMotion(callback); },
72 });
73 }
74
75 // Unique index for identifying touch device source
76 std::size_t index = 0;
77 for (auto& touch_device : touch_devices) {
78 touch_device = Common::Input::CreateDevice<Common::Input::InputDevice>(touch_params[index]);
79 if (!touch_device) {
80 continue;
81 }
82 touch_device->SetCallback({
83 .on_change =
84 [this, index](const Common::Input::CallbackStatus& callback) {
85 SetTouch(callback, index);
86 },
87 });
88 index++;
89 }
90}
91
92void EmulatedConsole::UnloadInput() {
93 motion_devices.reset();
94 for (auto& touch : touch_devices) {
95 touch.reset();
96 }
97}
98
99void EmulatedConsole::EnableConfiguration() {
100 is_configuring = true;
101 SaveCurrentConfig();
102}
103
104void EmulatedConsole::DisableConfiguration() {
105 is_configuring = false;
106}
107
108bool EmulatedConsole::IsConfiguring() const {
109 return is_configuring;
110}
111
112void EmulatedConsole::SaveCurrentConfig() {
113 if (!is_configuring) {
114 return;
115 }
116}
117
118void EmulatedConsole::RestoreConfig() {
119 if (!is_configuring) {
120 return;
121 }
122 ReloadFromSettings();
123}
124
125Common::ParamPackage EmulatedConsole::GetMotionParam() const {
126 return motion_params;
127}
128
129void EmulatedConsole::SetMotionParam(Common::ParamPackage param) {
130 motion_params = param;
131 ReloadInput();
132}
133
134void EmulatedConsole::SetMotion(const Common::Input::CallbackStatus& callback) {
135 std::lock_guard lock{mutex};
136 auto& raw_status = console.motion_values.raw_status;
137 auto& emulated = console.motion_values.emulated;
138
139 raw_status = TransformToMotion(callback);
140 emulated.SetAcceleration(Common::Vec3f{
141 raw_status.accel.x.value,
142 raw_status.accel.y.value,
143 raw_status.accel.z.value,
144 });
145 emulated.SetGyroscope(Common::Vec3f{
146 raw_status.gyro.x.value,
147 raw_status.gyro.y.value,
148 raw_status.gyro.z.value,
149 });
150 emulated.UpdateRotation(raw_status.delta_timestamp);
151 emulated.UpdateOrientation(raw_status.delta_timestamp);
152
153 if (is_configuring) {
154 TriggerOnChange(ConsoleTriggerType::Motion);
155 return;
156 }
157
158 auto& motion = console.motion_state;
159 motion.accel = emulated.GetAcceleration();
160 motion.gyro = emulated.GetGyroscope();
161 motion.rotation = emulated.GetGyroscope();
162 motion.orientation = emulated.GetOrientation();
163 motion.quaternion = emulated.GetQuaternion();
164 motion.is_at_rest = !emulated.IsMoving(motion_sensitivity);
165
166 TriggerOnChange(ConsoleTriggerType::Motion);
167}
168
169void EmulatedConsole::SetTouch(const Common::Input::CallbackStatus& callback, std::size_t index) {
170 if (index >= console.touch_values.size()) {
171 return;
172 }
173 std::lock_guard lock{mutex};
174
175 console.touch_values[index] = TransformToTouch(callback);
176
177 if (is_configuring) {
178 TriggerOnChange(ConsoleTriggerType::Touch);
179 return;
180 }
181
182 // TODO(german77): Remap touch id in sequential order
183 console.touch_state[index] = {
184 .position = {console.touch_values[index].x.value, console.touch_values[index].y.value},
185 .id = static_cast<u32>(console.touch_values[index].id),
186 .pressed = console.touch_values[index].pressed.value,
187 };
188
189 TriggerOnChange(ConsoleTriggerType::Touch);
190}
191
192ConsoleMotionValues EmulatedConsole::GetMotionValues() const {
193 return console.motion_values;
194}
195
196TouchValues EmulatedConsole::GetTouchValues() const {
197 return console.touch_values;
198}
199
200ConsoleMotion EmulatedConsole::GetMotion() const {
201 return console.motion_state;
202}
203
204TouchFingerState EmulatedConsole::GetTouch() const {
205 return console.touch_state;
206}
207
208void EmulatedConsole::TriggerOnChange(ConsoleTriggerType type) {
209 for (const auto& poller_pair : callback_list) {
210 const ConsoleUpdateCallback& poller = poller_pair.second;
211 if (poller.on_change) {
212 poller.on_change(type);
213 }
214 }
215}
216
217int EmulatedConsole::SetCallback(ConsoleUpdateCallback update_callback) {
218 std::lock_guard lock{mutex};
219 callback_list.insert_or_assign(last_callback_key, update_callback);
220 return last_callback_key++;
221}
222
223void EmulatedConsole::DeleteCallback(int key) {
224 std::lock_guard lock{mutex};
225 const auto& iterator = callback_list.find(key);
226 if (iterator == callback_list.end()) {
227 LOG_ERROR(Input, "Tried to delete non-existent callback {}", key);
228 return;
229 }
230 callback_list.erase(iterator);
231}
232} // namespace Core::HID
diff --git a/src/core/hid/emulated_console.h b/src/core/hid/emulated_console.h
new file mode 100644
index 000000000..3afd284d5
--- /dev/null
+++ b/src/core/hid/emulated_console.h
@@ -0,0 +1,190 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <array>
8#include <functional>
9#include <memory>
10#include <mutex>
11#include <unordered_map>
12
13#include "common/common_types.h"
14#include "common/input.h"
15#include "common/param_package.h"
16#include "common/point.h"
17#include "common/quaternion.h"
18#include "common/vector_math.h"
19#include "core/hid/hid_types.h"
20#include "core/hid/motion_input.h"
21
22namespace Core::HID {
23
24struct ConsoleMotionInfo {
25 Common::Input::MotionStatus raw_status{};
26 MotionInput emulated{};
27};
28
29using ConsoleMotionDevices = std::unique_ptr<Common::Input::InputDevice>;
30using TouchDevices = std::array<std::unique_ptr<Common::Input::InputDevice>, 16>;
31
32using ConsoleMotionParams = Common::ParamPackage;
33using TouchParams = std::array<Common::ParamPackage, 16>;
34
35using ConsoleMotionValues = ConsoleMotionInfo;
36using TouchValues = std::array<Common::Input::TouchStatus, 16>;
37
38struct TouchFinger {
39 u64 last_touch{};
40 Common::Point<float> position{};
41 u32 id{};
42 TouchAttribute attribute{};
43 bool pressed{};
44};
45
46// Contains all motion related data that is used on the services
47struct ConsoleMotion {
48 Common::Vec3f accel{};
49 Common::Vec3f gyro{};
50 Common::Vec3f rotation{};
51 std::array<Common::Vec3f, 3> orientation{};
52 Common::Quaternion<f32> quaternion{};
53 bool is_at_rest{};
54};
55
56using TouchFingerState = std::array<TouchFinger, 16>;
57
58struct ConsoleStatus {
59 // Data from input_common
60 ConsoleMotionValues motion_values{};
61 TouchValues touch_values{};
62
63 // Data for HID services
64 ConsoleMotion motion_state{};
65 TouchFingerState touch_state{};
66};
67
68enum class ConsoleTriggerType {
69 Motion,
70 Touch,
71 All,
72};
73
74struct ConsoleUpdateCallback {
75 std::function<void(ConsoleTriggerType)> on_change;
76};
77
78class EmulatedConsole {
79public:
80 /**
81 * Contains all input data within the emulated switch console tablet such as touch and motion
82 */
83 explicit EmulatedConsole();
84 ~EmulatedConsole();
85
86 YUZU_NON_COPYABLE(EmulatedConsole);
87 YUZU_NON_MOVEABLE(EmulatedConsole);
88
89 /// Removes all callbacks created from input devices
90 void UnloadInput();
91
92 /**
93 * Sets the emulated console into configuring mode
94 * This prevents the modification of the HID state of the emulated console by input commands
95 */
96 void EnableConfiguration();
97
98 /// Returns the emulated console into normal mode, allowing the modification of the HID state
99 void DisableConfiguration();
100
101 /// Returns true if the emulated console is in configuring mode
102 bool IsConfiguring() const;
103
104 /// Reload all input devices
105 void ReloadInput();
106
107 /// Overrides current mapped devices with the stored configuration and reloads all input devices
108 void ReloadFromSettings();
109
110 /// Saves the current mapped configuration
111 void SaveCurrentConfig();
112
113 /// Reverts any mapped changes made that weren't saved
114 void RestoreConfig();
115
116 // Returns the current mapped motion device
117 Common::ParamPackage GetMotionParam() const;
118
119 /**
120 * Updates the current mapped motion device
121 * @param param ParamPackage with controller data to be mapped
122 */
123 void SetMotionParam(Common::ParamPackage param);
124
125 /// Returns the latest status of motion input from the console with parameters
126 ConsoleMotionValues GetMotionValues() const;
127
128 /// Returns the latest status of touch input from the console with parameters
129 TouchValues GetTouchValues() const;
130
131 /// Returns the latest status of motion input from the console
132 ConsoleMotion GetMotion() const;
133
134 /// Returns the latest status of touch input from the console
135 TouchFingerState GetTouch() const;
136
137 /**
138 * Adds a callback to the list of events
139 * @param update_callback A ConsoleUpdateCallback that will be triggered
140 * @return an unique key corresponding to the callback index in the list
141 */
142 int SetCallback(ConsoleUpdateCallback update_callback);
143
144 /**
145 * Removes a callback from the list stopping any future events to this object
146 * @param key Key corresponding to the callback index in the list
147 */
148 void DeleteCallback(int key);
149
150private:
151 /// Creates and stores the touch params
152 void SetTouchParams();
153
154 /**
155 * Updates the motion status of the console
156 * @param callback A CallbackStatus containing gyro and accelerometer data
157 */
158 void SetMotion(const Common::Input::CallbackStatus& callback);
159
160 /**
161 * Updates the touch status of the console
162 * @param callback A CallbackStatus containing the touch position
163 * @param index Finger ID to be updated
164 */
165 void SetTouch(const Common::Input::CallbackStatus& callback, std::size_t index);
166
167 /**
168 * Triggers a callback that something has changed on the console status
169 * @param type Input type of the event to trigger
170 */
171 void TriggerOnChange(ConsoleTriggerType type);
172
173 bool is_configuring{false};
174 f32 motion_sensitivity{0.01f};
175
176 ConsoleMotionParams motion_params;
177 TouchParams touch_params;
178
179 ConsoleMotionDevices motion_devices;
180 TouchDevices touch_devices;
181
182 mutable std::mutex mutex;
183 std::unordered_map<int, ConsoleUpdateCallback> callback_list;
184 int last_callback_key = 0;
185
186 // Stores the current status of all console input
187 ConsoleStatus console;
188};
189
190} // namespace Core::HID
diff --git a/src/core/hid/emulated_controller.cpp b/src/core/hid/emulated_controller.cpp
new file mode 100644
index 000000000..93372445b
--- /dev/null
+++ b/src/core/hid/emulated_controller.cpp
@@ -0,0 +1,1139 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included
4
5#include "core/hid/emulated_controller.h"
6#include "core/hid/input_converter.h"
7
8namespace Core::HID {
9constexpr s32 HID_JOYSTICK_MAX = 0x7fff;
10constexpr s32 HID_TRIGGER_MAX = 0x7fff;
11
12EmulatedController::EmulatedController(NpadIdType npad_id_type_) : npad_id_type(npad_id_type_) {}
13
14EmulatedController::~EmulatedController() = default;
15
16NpadStyleIndex EmulatedController::MapSettingsTypeToNPad(Settings::ControllerType type) {
17 switch (type) {
18 case Settings::ControllerType::ProController:
19 return NpadStyleIndex::ProController;
20 case Settings::ControllerType::DualJoyconDetached:
21 return NpadStyleIndex::JoyconDual;
22 case Settings::ControllerType::LeftJoycon:
23 return NpadStyleIndex::JoyconLeft;
24 case Settings::ControllerType::RightJoycon:
25 return NpadStyleIndex::JoyconRight;
26 case Settings::ControllerType::Handheld:
27 return NpadStyleIndex::Handheld;
28 case Settings::ControllerType::GameCube:
29 return NpadStyleIndex::GameCube;
30 case Settings::ControllerType::Pokeball:
31 return NpadStyleIndex::Pokeball;
32 case Settings::ControllerType::NES:
33 return NpadStyleIndex::NES;
34 case Settings::ControllerType::SNES:
35 return NpadStyleIndex::SNES;
36 case Settings::ControllerType::N64:
37 return NpadStyleIndex::N64;
38 case Settings::ControllerType::SegaGenesis:
39 return NpadStyleIndex::SegaGenesis;
40 default:
41 return NpadStyleIndex::ProController;
42 }
43}
44
45Settings::ControllerType EmulatedController::MapNPadToSettingsType(NpadStyleIndex type) {
46 switch (type) {
47 case NpadStyleIndex::ProController:
48 return Settings::ControllerType::ProController;
49 case NpadStyleIndex::JoyconDual:
50 return Settings::ControllerType::DualJoyconDetached;
51 case NpadStyleIndex::JoyconLeft:
52 return Settings::ControllerType::LeftJoycon;
53 case NpadStyleIndex::JoyconRight:
54 return Settings::ControllerType::RightJoycon;
55 case NpadStyleIndex::Handheld:
56 return Settings::ControllerType::Handheld;
57 case NpadStyleIndex::GameCube:
58 return Settings::ControllerType::GameCube;
59 case NpadStyleIndex::Pokeball:
60 return Settings::ControllerType::Pokeball;
61 case NpadStyleIndex::NES:
62 return Settings::ControllerType::NES;
63 case NpadStyleIndex::SNES:
64 return Settings::ControllerType::SNES;
65 case NpadStyleIndex::N64:
66 return Settings::ControllerType::N64;
67 case NpadStyleIndex::SegaGenesis:
68 return Settings::ControllerType::SegaGenesis;
69 default:
70 return Settings::ControllerType::ProController;
71 }
72}
73
74void EmulatedController::ReloadFromSettings() {
75 const auto player_index = NpadIdTypeToIndex(npad_id_type);
76 const auto& player = Settings::values.players.GetValue()[player_index];
77
78 for (std::size_t index = 0; index < player.buttons.size(); ++index) {
79 button_params[index] = Common::ParamPackage(player.buttons[index]);
80 }
81 for (std::size_t index = 0; index < player.analogs.size(); ++index) {
82 stick_params[index] = Common::ParamPackage(player.analogs[index]);
83 }
84 for (std::size_t index = 0; index < player.motions.size(); ++index) {
85 motion_params[index] = Common::ParamPackage(player.motions[index]);
86 }
87
88 controller.colors_state.left = {
89 .body = player.body_color_left,
90 .button = player.button_color_left,
91 };
92
93 controller.colors_state.right = {
94 .body = player.body_color_right,
95 .button = player.button_color_right,
96 };
97
98 controller.colors_state.fullkey = controller.colors_state.left;
99
100 // Other or debug controller should always be a pro controller
101 if (npad_id_type != NpadIdType::Other) {
102 SetNpadStyleIndex(MapSettingsTypeToNPad(player.controller_type));
103 } else {
104 SetNpadStyleIndex(NpadStyleIndex::ProController);
105 }
106
107 if (player.connected) {
108 Connect();
109 } else {
110 Disconnect();
111 }
112
113 ReloadInput();
114}
115
116void EmulatedController::LoadDevices() {
117 // TODO(german77): Use more buttons to detect the correct device
118 const auto left_joycon = button_params[Settings::NativeButton::DRight];
119 const auto right_joycon = button_params[Settings::NativeButton::A];
120
121 // Triggers for GC controllers
122 trigger_params[LeftIndex] = button_params[Settings::NativeButton::ZL];
123 trigger_params[RightIndex] = button_params[Settings::NativeButton::ZR];
124
125 battery_params[LeftIndex] = left_joycon;
126 battery_params[RightIndex] = right_joycon;
127 battery_params[LeftIndex].Set("battery", true);
128 battery_params[RightIndex].Set("battery", true);
129
130 output_params[LeftIndex] = left_joycon;
131 output_params[RightIndex] = right_joycon;
132 output_params[LeftIndex].Set("output", true);
133 output_params[RightIndex].Set("output", true);
134
135 LoadTASParams();
136
137 std::transform(button_params.begin() + Settings::NativeButton::BUTTON_HID_BEGIN,
138 button_params.begin() + Settings::NativeButton::BUTTON_NS_END,
139 button_devices.begin(), Common::Input::CreateDevice<Common::Input::InputDevice>);
140 std::transform(stick_params.begin() + Settings::NativeAnalog::STICK_HID_BEGIN,
141 stick_params.begin() + Settings::NativeAnalog::STICK_HID_END,
142 stick_devices.begin(), Common::Input::CreateDevice<Common::Input::InputDevice>);
143 std::transform(motion_params.begin() + Settings::NativeMotion::MOTION_HID_BEGIN,
144 motion_params.begin() + Settings::NativeMotion::MOTION_HID_END,
145 motion_devices.begin(), Common::Input::CreateDevice<Common::Input::InputDevice>);
146 std::transform(trigger_params.begin(), trigger_params.end(), trigger_devices.begin(),
147 Common::Input::CreateDevice<Common::Input::InputDevice>);
148 std::transform(battery_params.begin(), battery_params.begin(), battery_devices.end(),
149 Common::Input::CreateDevice<Common::Input::InputDevice>);
150 std::transform(output_params.begin(), output_params.end(), output_devices.begin(),
151 Common::Input::CreateDevice<Common::Input::OutputDevice>);
152
153 // Initialize TAS devices
154 std::transform(tas_button_params.begin(), tas_button_params.end(), tas_button_devices.begin(),
155 Common::Input::CreateDevice<Common::Input::InputDevice>);
156 std::transform(tas_stick_params.begin(), tas_stick_params.end(), tas_stick_devices.begin(),
157 Common::Input::CreateDevice<Common::Input::InputDevice>);
158}
159
160void EmulatedController::LoadTASParams() {
161 const auto player_index = NpadIdTypeToIndex(npad_id_type);
162 Common::ParamPackage common_params{};
163 common_params.Set("engine", "tas");
164 common_params.Set("port", static_cast<int>(player_index));
165 for (auto& param : tas_button_params) {
166 param = common_params;
167 }
168 for (auto& param : tas_stick_params) {
169 param = common_params;
170 }
171
172 // TODO(german77): Replace this with an input profile or something better
173 tas_button_params[Settings::NativeButton::A].Set("button", 0);
174 tas_button_params[Settings::NativeButton::B].Set("button", 1);
175 tas_button_params[Settings::NativeButton::X].Set("button", 2);
176 tas_button_params[Settings::NativeButton::Y].Set("button", 3);
177 tas_button_params[Settings::NativeButton::LStick].Set("button", 4);
178 tas_button_params[Settings::NativeButton::RStick].Set("button", 5);
179 tas_button_params[Settings::NativeButton::L].Set("button", 6);
180 tas_button_params[Settings::NativeButton::R].Set("button", 7);
181 tas_button_params[Settings::NativeButton::ZL].Set("button", 8);
182 tas_button_params[Settings::NativeButton::ZR].Set("button", 9);
183 tas_button_params[Settings::NativeButton::Plus].Set("button", 10);
184 tas_button_params[Settings::NativeButton::Minus].Set("button", 11);
185 tas_button_params[Settings::NativeButton::DLeft].Set("button", 12);
186 tas_button_params[Settings::NativeButton::DUp].Set("button", 13);
187 tas_button_params[Settings::NativeButton::DRight].Set("button", 14);
188 tas_button_params[Settings::NativeButton::DDown].Set("button", 15);
189 tas_button_params[Settings::NativeButton::SL].Set("button", 16);
190 tas_button_params[Settings::NativeButton::SR].Set("button", 17);
191 tas_button_params[Settings::NativeButton::Home].Set("button", 18);
192 tas_button_params[Settings::NativeButton::Screenshot].Set("button", 19);
193
194 tas_stick_params[Settings::NativeAnalog::LStick].Set("axis_x", 0);
195 tas_stick_params[Settings::NativeAnalog::LStick].Set("axis_y", 1);
196 tas_stick_params[Settings::NativeAnalog::RStick].Set("axis_x", 2);
197 tas_stick_params[Settings::NativeAnalog::RStick].Set("axis_y", 3);
198}
199
200void EmulatedController::ReloadInput() {
201 // If you load any device here add the equivalent to the UnloadInput() function
202 LoadDevices();
203 for (std::size_t index = 0; index < button_devices.size(); ++index) {
204 if (!button_devices[index]) {
205 continue;
206 }
207 const auto uuid = Common::UUID{button_params[index].Get("guid", "")};
208 button_devices[index]->SetCallback({
209 .on_change =
210 [this, index, uuid](const Common::Input::CallbackStatus& callback) {
211 SetButton(callback, index, uuid);
212 },
213 });
214 button_devices[index]->ForceUpdate();
215 }
216
217 for (std::size_t index = 0; index < stick_devices.size(); ++index) {
218 if (!stick_devices[index]) {
219 continue;
220 }
221 const auto uuid = Common::UUID{stick_params[index].Get("guid", "")};
222 stick_devices[index]->SetCallback({
223 .on_change =
224 [this, index, uuid](const Common::Input::CallbackStatus& callback) {
225 SetStick(callback, index, uuid);
226 },
227 });
228 stick_devices[index]->ForceUpdate();
229 }
230
231 for (std::size_t index = 0; index < trigger_devices.size(); ++index) {
232 if (!trigger_devices[index]) {
233 continue;
234 }
235 const auto uuid = Common::UUID{trigger_params[index].Get("guid", "")};
236 trigger_devices[index]->SetCallback({
237 .on_change =
238 [this, index, uuid](const Common::Input::CallbackStatus& callback) {
239 SetTrigger(callback, index, uuid);
240 },
241 });
242 trigger_devices[index]->ForceUpdate();
243 }
244
245 for (std::size_t index = 0; index < battery_devices.size(); ++index) {
246 if (!battery_devices[index]) {
247 continue;
248 }
249 battery_devices[index]->SetCallback({
250 .on_change =
251 [this, index](const Common::Input::CallbackStatus& callback) {
252 SetBattery(callback, index);
253 },
254 });
255 battery_devices[index]->ForceUpdate();
256 }
257
258 for (std::size_t index = 0; index < motion_devices.size(); ++index) {
259 if (!motion_devices[index]) {
260 continue;
261 }
262 motion_devices[index]->SetCallback({
263 .on_change =
264 [this, index](const Common::Input::CallbackStatus& callback) {
265 SetMotion(callback, index);
266 },
267 });
268 motion_devices[index]->ForceUpdate();
269 }
270
271 // Use a common UUID for TAS
272 const auto tas_uuid = Common::UUID{0x0, 0x7A5};
273
274 // Register TAS devices. No need to force update
275 for (std::size_t index = 0; index < tas_button_devices.size(); ++index) {
276 if (!tas_button_devices[index]) {
277 continue;
278 }
279 tas_button_devices[index]->SetCallback({
280 .on_change =
281 [this, index, tas_uuid](const Common::Input::CallbackStatus& callback) {
282 SetButton(callback, index, tas_uuid);
283 },
284 });
285 }
286
287 for (std::size_t index = 0; index < tas_stick_devices.size(); ++index) {
288 if (!tas_stick_devices[index]) {
289 continue;
290 }
291 tas_stick_devices[index]->SetCallback({
292 .on_change =
293 [this, index, tas_uuid](const Common::Input::CallbackStatus& callback) {
294 SetStick(callback, index, tas_uuid);
295 },
296 });
297 }
298}
299
300void EmulatedController::UnloadInput() {
301 for (auto& button : button_devices) {
302 button.reset();
303 }
304 for (auto& stick : stick_devices) {
305 stick.reset();
306 }
307 for (auto& motion : motion_devices) {
308 motion.reset();
309 }
310 for (auto& trigger : trigger_devices) {
311 trigger.reset();
312 }
313 for (auto& battery : battery_devices) {
314 battery.reset();
315 }
316 for (auto& output : output_devices) {
317 output.reset();
318 }
319 for (auto& button : tas_button_devices) {
320 button.reset();
321 }
322 for (auto& stick : tas_stick_devices) {
323 stick.reset();
324 }
325}
326
327void EmulatedController::EnableConfiguration() {
328 is_configuring = true;
329 tmp_is_connected = is_connected;
330 tmp_npad_type = npad_type;
331}
332
333void EmulatedController::DisableConfiguration() {
334 is_configuring = false;
335
336 // Apply temporary npad type to the real controller
337 if (tmp_npad_type != npad_type) {
338 if (is_connected) {
339 Disconnect();
340 }
341 SetNpadStyleIndex(tmp_npad_type);
342 }
343
344 // Apply temporary connected status to the real controller
345 if (tmp_is_connected != is_connected) {
346 if (tmp_is_connected) {
347 Connect();
348 return;
349 }
350 Disconnect();
351 }
352}
353
354bool EmulatedController::IsConfiguring() const {
355 return is_configuring;
356}
357
358void EmulatedController::SaveCurrentConfig() {
359 const auto player_index = NpadIdTypeToIndex(npad_id_type);
360 auto& player = Settings::values.players.GetValue()[player_index];
361 player.connected = is_connected;
362 player.controller_type = MapNPadToSettingsType(npad_type);
363 for (std::size_t index = 0; index < player.buttons.size(); ++index) {
364 player.buttons[index] = button_params[index].Serialize();
365 }
366 for (std::size_t index = 0; index < player.analogs.size(); ++index) {
367 player.analogs[index] = stick_params[index].Serialize();
368 }
369 for (std::size_t index = 0; index < player.motions.size(); ++index) {
370 player.motions[index] = motion_params[index].Serialize();
371 }
372}
373
374void EmulatedController::RestoreConfig() {
375 if (!is_configuring) {
376 return;
377 }
378 ReloadFromSettings();
379}
380
381std::vector<Common::ParamPackage> EmulatedController::GetMappedDevices(
382 EmulatedDeviceIndex device_index) const {
383 std::vector<Common::ParamPackage> devices;
384 for (const auto& param : button_params) {
385 if (!param.Has("engine")) {
386 continue;
387 }
388 const auto devices_it = std::find_if(
389 devices.begin(), devices.end(), [param](const Common::ParamPackage param_) {
390 return param.Get("engine", "") == param_.Get("engine", "") &&
391 param.Get("guid", "") == param_.Get("guid", "") &&
392 param.Get("port", 0) == param_.Get("port", 0);
393 });
394 if (devices_it != devices.end()) {
395 continue;
396 }
397 Common::ParamPackage device{};
398 device.Set("engine", param.Get("engine", ""));
399 device.Set("guid", param.Get("guid", ""));
400 device.Set("port", param.Get("port", 0));
401 devices.push_back(device);
402 }
403
404 for (const auto& param : stick_params) {
405 if (!param.Has("engine")) {
406 continue;
407 }
408 if (param.Get("engine", "") == "analog_from_button") {
409 continue;
410 }
411 const auto devices_it = std::find_if(
412 devices.begin(), devices.end(), [param](const Common::ParamPackage param_) {
413 return param.Get("engine", "") == param_.Get("engine", "") &&
414 param.Get("guid", "") == param_.Get("guid", "") &&
415 param.Get("port", 0) == param_.Get("port", 0);
416 });
417 if (devices_it != devices.end()) {
418 continue;
419 }
420 Common::ParamPackage device{};
421 device.Set("engine", param.Get("engine", ""));
422 device.Set("guid", param.Get("guid", ""));
423 device.Set("port", param.Get("port", 0));
424 devices.push_back(device);
425 }
426 return devices;
427}
428
429Common::ParamPackage EmulatedController::GetButtonParam(std::size_t index) const {
430 if (index >= button_params.size()) {
431 return {};
432 }
433 return button_params[index];
434}
435
436Common::ParamPackage EmulatedController::GetStickParam(std::size_t index) const {
437 if (index >= stick_params.size()) {
438 return {};
439 }
440 return stick_params[index];
441}
442
443Common::ParamPackage EmulatedController::GetMotionParam(std::size_t index) const {
444 if (index >= motion_params.size()) {
445 return {};
446 }
447 return motion_params[index];
448}
449
450void EmulatedController::SetButtonParam(std::size_t index, Common::ParamPackage param) {
451 if (index >= button_params.size()) {
452 return;
453 }
454 button_params[index] = std::move(param);
455 ReloadInput();
456}
457
458void EmulatedController::SetStickParam(std::size_t index, Common::ParamPackage param) {
459 if (index >= stick_params.size()) {
460 return;
461 }
462 stick_params[index] = std::move(param);
463 ReloadInput();
464}
465
466void EmulatedController::SetMotionParam(std::size_t index, Common::ParamPackage param) {
467 if (index >= motion_params.size()) {
468 return;
469 }
470 motion_params[index] = std::move(param);
471 ReloadInput();
472}
473
474void EmulatedController::SetButton(const Common::Input::CallbackStatus& callback, std::size_t index,
475 Common::UUID uuid) {
476 if (index >= controller.button_values.size()) {
477 return;
478 }
479 {
480 std::lock_guard lock{mutex};
481 bool value_changed = false;
482 const auto new_status = TransformToButton(callback);
483 auto& current_status = controller.button_values[index];
484
485 // Only read button values that have the same uuid or are pressed once
486 if (current_status.uuid != uuid) {
487 if (!new_status.value) {
488 return;
489 }
490 }
491
492 current_status.toggle = new_status.toggle;
493 current_status.uuid = uuid;
494
495 // Update button status with current
496 if (!current_status.toggle) {
497 current_status.locked = false;
498 if (current_status.value != new_status.value) {
499 current_status.value = new_status.value;
500 value_changed = true;
501 }
502 } else {
503 // Toggle button and lock status
504 if (new_status.value && !current_status.locked) {
505 current_status.locked = true;
506 current_status.value = !current_status.value;
507 value_changed = true;
508 }
509
510 // Unlock button ready for next press
511 if (!new_status.value && current_status.locked) {
512 current_status.locked = false;
513 }
514 }
515
516 if (!value_changed) {
517 return;
518 }
519
520 if (is_configuring) {
521 controller.npad_button_state.raw = NpadButton::None;
522 controller.debug_pad_button_state.raw = 0;
523 TriggerOnChange(ControllerTriggerType::Button, false);
524 return;
525 }
526
527 switch (index) {
528 case Settings::NativeButton::A:
529 controller.npad_button_state.a.Assign(current_status.value);
530 controller.debug_pad_button_state.a.Assign(current_status.value);
531 break;
532 case Settings::NativeButton::B:
533 controller.npad_button_state.b.Assign(current_status.value);
534 controller.debug_pad_button_state.b.Assign(current_status.value);
535 break;
536 case Settings::NativeButton::X:
537 controller.npad_button_state.x.Assign(current_status.value);
538 controller.debug_pad_button_state.x.Assign(current_status.value);
539 break;
540 case Settings::NativeButton::Y:
541 controller.npad_button_state.y.Assign(current_status.value);
542 controller.debug_pad_button_state.y.Assign(current_status.value);
543 break;
544 case Settings::NativeButton::LStick:
545 controller.npad_button_state.stick_l.Assign(current_status.value);
546 break;
547 case Settings::NativeButton::RStick:
548 controller.npad_button_state.stick_r.Assign(current_status.value);
549 break;
550 case Settings::NativeButton::L:
551 controller.npad_button_state.l.Assign(current_status.value);
552 controller.debug_pad_button_state.l.Assign(current_status.value);
553 break;
554 case Settings::NativeButton::R:
555 controller.npad_button_state.r.Assign(current_status.value);
556 controller.debug_pad_button_state.r.Assign(current_status.value);
557 break;
558 case Settings::NativeButton::ZL:
559 controller.npad_button_state.zl.Assign(current_status.value);
560 controller.debug_pad_button_state.zl.Assign(current_status.value);
561 break;
562 case Settings::NativeButton::ZR:
563 controller.npad_button_state.zr.Assign(current_status.value);
564 controller.debug_pad_button_state.zr.Assign(current_status.value);
565 break;
566 case Settings::NativeButton::Plus:
567 controller.npad_button_state.plus.Assign(current_status.value);
568 controller.debug_pad_button_state.plus.Assign(current_status.value);
569 break;
570 case Settings::NativeButton::Minus:
571 controller.npad_button_state.minus.Assign(current_status.value);
572 controller.debug_pad_button_state.minus.Assign(current_status.value);
573 break;
574 case Settings::NativeButton::DLeft:
575 controller.npad_button_state.left.Assign(current_status.value);
576 controller.debug_pad_button_state.d_left.Assign(current_status.value);
577 break;
578 case Settings::NativeButton::DUp:
579 controller.npad_button_state.up.Assign(current_status.value);
580 controller.debug_pad_button_state.d_up.Assign(current_status.value);
581 break;
582 case Settings::NativeButton::DRight:
583 controller.npad_button_state.right.Assign(current_status.value);
584 controller.debug_pad_button_state.d_right.Assign(current_status.value);
585 break;
586 case Settings::NativeButton::DDown:
587 controller.npad_button_state.down.Assign(current_status.value);
588 controller.debug_pad_button_state.d_down.Assign(current_status.value);
589 break;
590 case Settings::NativeButton::SL:
591 controller.npad_button_state.left_sl.Assign(current_status.value);
592 controller.npad_button_state.right_sl.Assign(current_status.value);
593 break;
594 case Settings::NativeButton::SR:
595 controller.npad_button_state.left_sr.Assign(current_status.value);
596 controller.npad_button_state.right_sr.Assign(current_status.value);
597 break;
598 case Settings::NativeButton::Home:
599 case Settings::NativeButton::Screenshot:
600 break;
601 }
602 }
603 if (!is_connected) {
604 if (npad_id_type == NpadIdType::Player1 && npad_type != NpadStyleIndex::Handheld) {
605 Connect();
606 }
607 if (npad_id_type == NpadIdType::Handheld && npad_type == NpadStyleIndex::Handheld) {
608 Connect();
609 }
610 }
611 TriggerOnChange(ControllerTriggerType::Button, true);
612}
613
614void EmulatedController::SetStick(const Common::Input::CallbackStatus& callback, std::size_t index,
615 Common::UUID uuid) {
616 if (index >= controller.stick_values.size()) {
617 return;
618 }
619 std::lock_guard lock{mutex};
620 const auto stick_value = TransformToStick(callback);
621
622 // Only read stick values that have the same uuid or are over the threshold to avoid flapping
623 if (controller.stick_values[index].uuid != uuid) {
624 if (!stick_value.down && !stick_value.up && !stick_value.left && !stick_value.right) {
625 return;
626 }
627 }
628
629 controller.stick_values[index] = stick_value;
630 controller.stick_values[index].uuid = uuid;
631
632 if (is_configuring) {
633 controller.analog_stick_state.left = {};
634 controller.analog_stick_state.right = {};
635 TriggerOnChange(ControllerTriggerType::Stick, false);
636 return;
637 }
638
639 const AnalogStickState stick{
640 .x = static_cast<s32>(controller.stick_values[index].x.value * HID_JOYSTICK_MAX),
641 .y = static_cast<s32>(controller.stick_values[index].y.value * HID_JOYSTICK_MAX),
642 };
643
644 switch (index) {
645 case Settings::NativeAnalog::LStick:
646 controller.analog_stick_state.left = stick;
647 controller.npad_button_state.stick_l_left.Assign(controller.stick_values[index].left);
648 controller.npad_button_state.stick_l_up.Assign(controller.stick_values[index].up);
649 controller.npad_button_state.stick_l_right.Assign(controller.stick_values[index].right);
650 controller.npad_button_state.stick_l_down.Assign(controller.stick_values[index].down);
651 break;
652 case Settings::NativeAnalog::RStick:
653 controller.analog_stick_state.right = stick;
654 controller.npad_button_state.stick_r_left.Assign(controller.stick_values[index].left);
655 controller.npad_button_state.stick_r_up.Assign(controller.stick_values[index].up);
656 controller.npad_button_state.stick_r_right.Assign(controller.stick_values[index].right);
657 controller.npad_button_state.stick_r_down.Assign(controller.stick_values[index].down);
658 break;
659 }
660
661 TriggerOnChange(ControllerTriggerType::Stick, true);
662}
663
664void EmulatedController::SetTrigger(const Common::Input::CallbackStatus& callback,
665 std::size_t index, Common::UUID uuid) {
666 if (index >= controller.trigger_values.size()) {
667 return;
668 }
669 std::lock_guard lock{mutex};
670 const auto trigger_value = TransformToTrigger(callback);
671
672 // Only read trigger values that have the same uuid or are pressed once
673 if (controller.trigger_values[index].uuid != uuid) {
674 if (!trigger_value.pressed.value) {
675 return;
676 }
677 }
678
679 controller.trigger_values[index] = trigger_value;
680 controller.trigger_values[index].uuid = uuid;
681
682 if (is_configuring) {
683 controller.gc_trigger_state.left = 0;
684 controller.gc_trigger_state.right = 0;
685 TriggerOnChange(ControllerTriggerType::Trigger, false);
686 return;
687 }
688
689 const auto& trigger = controller.trigger_values[index];
690
691 switch (index) {
692 case Settings::NativeTrigger::LTrigger:
693 controller.gc_trigger_state.left = static_cast<s32>(trigger.analog.value * HID_TRIGGER_MAX);
694 controller.npad_button_state.zl.Assign(trigger.pressed.value);
695 break;
696 case Settings::NativeTrigger::RTrigger:
697 controller.gc_trigger_state.right =
698 static_cast<s32>(trigger.analog.value * HID_TRIGGER_MAX);
699 controller.npad_button_state.zr.Assign(trigger.pressed.value);
700 break;
701 }
702
703 TriggerOnChange(ControllerTriggerType::Trigger, true);
704}
705
706void EmulatedController::SetMotion(const Common::Input::CallbackStatus& callback,
707 std::size_t index) {
708 if (index >= controller.motion_values.size()) {
709 return;
710 }
711 std::lock_guard lock{mutex};
712 auto& raw_status = controller.motion_values[index].raw_status;
713 auto& emulated = controller.motion_values[index].emulated;
714
715 raw_status = TransformToMotion(callback);
716 emulated.SetAcceleration(Common::Vec3f{
717 raw_status.accel.x.value,
718 raw_status.accel.y.value,
719 raw_status.accel.z.value,
720 });
721 emulated.SetGyroscope(Common::Vec3f{
722 raw_status.gyro.x.value,
723 raw_status.gyro.y.value,
724 raw_status.gyro.z.value,
725 });
726 emulated.UpdateRotation(raw_status.delta_timestamp);
727 emulated.UpdateOrientation(raw_status.delta_timestamp);
728 force_update_motion = raw_status.force_update;
729
730 if (is_configuring) {
731 TriggerOnChange(ControllerTriggerType::Motion, false);
732 return;
733 }
734
735 auto& motion = controller.motion_state[index];
736 motion.accel = emulated.GetAcceleration();
737 motion.gyro = emulated.GetGyroscope();
738 motion.rotation = emulated.GetRotations();
739 motion.orientation = emulated.GetOrientation();
740 motion.is_at_rest = !emulated.IsMoving(motion_sensitivity);
741
742 TriggerOnChange(ControllerTriggerType::Motion, true);
743}
744
745void EmulatedController::SetBattery(const Common::Input::CallbackStatus& callback,
746 std::size_t index) {
747 if (index >= controller.battery_values.size()) {
748 return;
749 }
750 std::lock_guard lock{mutex};
751 controller.battery_values[index] = TransformToBattery(callback);
752
753 if (is_configuring) {
754 TriggerOnChange(ControllerTriggerType::Battery, false);
755 return;
756 }
757
758 bool is_charging = false;
759 bool is_powered = false;
760 NpadBatteryLevel battery_level = 0;
761 switch (controller.battery_values[index]) {
762 case Common::Input::BatteryLevel::Charging:
763 is_charging = true;
764 is_powered = true;
765 battery_level = 6;
766 break;
767 case Common::Input::BatteryLevel::Medium:
768 battery_level = 6;
769 break;
770 case Common::Input::BatteryLevel::Low:
771 battery_level = 4;
772 break;
773 case Common::Input::BatteryLevel::Critical:
774 battery_level = 2;
775 break;
776 case Common::Input::BatteryLevel::Empty:
777 battery_level = 0;
778 break;
779 case Common::Input::BatteryLevel::None:
780 case Common::Input::BatteryLevel::Full:
781 default:
782 is_powered = true;
783 battery_level = 8;
784 break;
785 }
786
787 switch (index) {
788 case LeftIndex:
789 controller.battery_state.left = {
790 .is_powered = is_powered,
791 .is_charging = is_charging,
792 .battery_level = battery_level,
793 };
794 break;
795 case RightIndex:
796 controller.battery_state.right = {
797 .is_powered = is_powered,
798 .is_charging = is_charging,
799 .battery_level = battery_level,
800 };
801 break;
802 case DualIndex:
803 controller.battery_state.dual = {
804 .is_powered = is_powered,
805 .is_charging = is_charging,
806 .battery_level = battery_level,
807 };
808 break;
809 }
810 TriggerOnChange(ControllerTriggerType::Battery, true);
811}
812
813bool EmulatedController::SetVibration(std::size_t device_index, VibrationValue vibration) {
814 if (device_index >= output_devices.size()) {
815 return false;
816 }
817 if (!output_devices[device_index]) {
818 return false;
819 }
820 const auto player_index = NpadIdTypeToIndex(npad_id_type);
821 const auto& player = Settings::values.players.GetValue()[player_index];
822 const f32 strength = static_cast<f32>(player.vibration_strength) / 100.0f;
823
824 if (!player.vibration_enabled) {
825 return false;
826 }
827
828 // Exponential amplification is too strong at low amplitudes. Switch to a linear
829 // amplification if strength is set below 0.7f
830 const Common::Input::VibrationAmplificationType type =
831 strength > 0.7f ? Common::Input::VibrationAmplificationType::Exponential
832 : Common::Input::VibrationAmplificationType::Linear;
833
834 const Common::Input::VibrationStatus status = {
835 .low_amplitude = std::min(vibration.low_amplitude * strength, 1.0f),
836 .low_frequency = vibration.low_frequency,
837 .high_amplitude = std::min(vibration.high_amplitude * strength, 1.0f),
838 .high_frequency = vibration.high_frequency,
839 .type = type,
840 };
841 return output_devices[device_index]->SetVibration(status) ==
842 Common::Input::VibrationError::None;
843}
844
845bool EmulatedController::TestVibration(std::size_t device_index) {
846 if (device_index >= output_devices.size()) {
847 return false;
848 }
849 if (!output_devices[device_index]) {
850 return false;
851 }
852
853 // Send a slight vibration to test for rumble support
854 constexpr Common::Input::VibrationStatus status = {
855 .low_amplitude = 0.001f,
856 .low_frequency = 160.0f,
857 .high_amplitude = 0.001f,
858 .high_frequency = 320.0f,
859 .type = Common::Input::VibrationAmplificationType::Linear,
860 };
861 return output_devices[device_index]->SetVibration(status) ==
862 Common::Input::VibrationError::None;
863}
864
865void EmulatedController::SetLedPattern() {
866 for (auto& device : output_devices) {
867 if (!device) {
868 continue;
869 }
870
871 const LedPattern pattern = GetLedPattern();
872 const Common::Input::LedStatus status = {
873 .led_1 = pattern.position1 != 0,
874 .led_2 = pattern.position2 != 0,
875 .led_3 = pattern.position3 != 0,
876 .led_4 = pattern.position4 != 0,
877 };
878 device->SetLED(status);
879 }
880}
881
882void EmulatedController::SetSupportedNpadStyleTag(NpadStyleTag supported_styles) {
883 supported_style_tag = supported_styles;
884 if (!is_connected) {
885 return;
886 }
887 if (!IsControllerSupported()) {
888 LOG_ERROR(Service_HID, "Controller type {} is not supported. Disconnecting controller",
889 npad_type);
890 Disconnect();
891 }
892}
893
894bool EmulatedController::IsControllerSupported() const {
895 switch (npad_type) {
896 case NpadStyleIndex::ProController:
897 return supported_style_tag.fullkey;
898 case NpadStyleIndex::Handheld:
899 return supported_style_tag.handheld;
900 case NpadStyleIndex::JoyconDual:
901 return supported_style_tag.joycon_dual;
902 case NpadStyleIndex::JoyconLeft:
903 return supported_style_tag.joycon_left;
904 case NpadStyleIndex::JoyconRight:
905 return supported_style_tag.joycon_right;
906 case NpadStyleIndex::GameCube:
907 return supported_style_tag.gamecube;
908 case NpadStyleIndex::Pokeball:
909 return supported_style_tag.palma;
910 case NpadStyleIndex::NES:
911 return supported_style_tag.lark;
912 case NpadStyleIndex::SNES:
913 return supported_style_tag.lucia;
914 case NpadStyleIndex::N64:
915 return supported_style_tag.lagoon;
916 case NpadStyleIndex::SegaGenesis:
917 return supported_style_tag.lager;
918 default:
919 return false;
920 }
921}
922
923void EmulatedController::Connect() {
924 if (!IsControllerSupported()) {
925 LOG_ERROR(Service_HID, "Controller type {} is not supported", npad_type);
926 return;
927 }
928 {
929 std::lock_guard lock{mutex};
930 if (is_configuring) {
931 tmp_is_connected = true;
932 TriggerOnChange(ControllerTriggerType::Connected, false);
933 return;
934 }
935
936 if (is_connected) {
937 return;
938 }
939 is_connected = true;
940 }
941 TriggerOnChange(ControllerTriggerType::Connected, true);
942}
943
944void EmulatedController::Disconnect() {
945 {
946 std::lock_guard lock{mutex};
947 if (is_configuring) {
948 tmp_is_connected = false;
949 TriggerOnChange(ControllerTriggerType::Disconnected, false);
950 return;
951 }
952
953 if (!is_connected) {
954 return;
955 }
956 is_connected = false;
957 }
958 TriggerOnChange(ControllerTriggerType::Disconnected, true);
959}
960
961bool EmulatedController::IsConnected(bool get_temporary_value) const {
962 if (get_temporary_value && is_configuring) {
963 return tmp_is_connected;
964 }
965 return is_connected;
966}
967
968bool EmulatedController::IsVibrationEnabled() const {
969 const auto player_index = NpadIdTypeToIndex(npad_id_type);
970 const auto& player = Settings::values.players.GetValue()[player_index];
971 return player.vibration_enabled;
972}
973
974NpadIdType EmulatedController::GetNpadIdType() const {
975 return npad_id_type;
976}
977
978NpadStyleIndex EmulatedController::GetNpadStyleIndex(bool get_temporary_value) const {
979 if (get_temporary_value && is_configuring) {
980 return tmp_npad_type;
981 }
982 return npad_type;
983}
984
985void EmulatedController::SetNpadStyleIndex(NpadStyleIndex npad_type_) {
986 {
987 std::lock_guard lock{mutex};
988
989 if (is_configuring) {
990 if (tmp_npad_type == npad_type_) {
991 return;
992 }
993 tmp_npad_type = npad_type_;
994 TriggerOnChange(ControllerTriggerType::Type, false);
995 return;
996 }
997
998 if (npad_type == npad_type_) {
999 return;
1000 }
1001 if (is_connected) {
1002 LOG_WARNING(Service_HID, "Controller {} type changed while it's connected",
1003 NpadIdTypeToIndex(npad_id_type));
1004 }
1005 npad_type = npad_type_;
1006 }
1007 TriggerOnChange(ControllerTriggerType::Type, true);
1008}
1009
1010LedPattern EmulatedController::GetLedPattern() const {
1011 switch (npad_id_type) {
1012 case NpadIdType::Player1:
1013 return LedPattern{1, 0, 0, 0};
1014 case NpadIdType::Player2:
1015 return LedPattern{1, 1, 0, 0};
1016 case NpadIdType::Player3:
1017 return LedPattern{1, 1, 1, 0};
1018 case NpadIdType::Player4:
1019 return LedPattern{1, 1, 1, 1};
1020 case NpadIdType::Player5:
1021 return LedPattern{1, 0, 0, 1};
1022 case NpadIdType::Player6:
1023 return LedPattern{1, 0, 1, 0};
1024 case NpadIdType::Player7:
1025 return LedPattern{1, 0, 1, 1};
1026 case NpadIdType::Player8:
1027 return LedPattern{0, 1, 1, 0};
1028 default:
1029 return LedPattern{0, 0, 0, 0};
1030 }
1031}
1032
1033ButtonValues EmulatedController::GetButtonsValues() const {
1034 return controller.button_values;
1035}
1036
1037SticksValues EmulatedController::GetSticksValues() const {
1038 return controller.stick_values;
1039}
1040
1041TriggerValues EmulatedController::GetTriggersValues() const {
1042 return controller.trigger_values;
1043}
1044
1045ControllerMotionValues EmulatedController::GetMotionValues() const {
1046 return controller.motion_values;
1047}
1048
1049ColorValues EmulatedController::GetColorsValues() const {
1050 return controller.color_values;
1051}
1052
1053BatteryValues EmulatedController::GetBatteryValues() const {
1054 return controller.battery_values;
1055}
1056
1057NpadButtonState EmulatedController::GetNpadButtons() const {
1058 if (is_configuring) {
1059 return {};
1060 }
1061 return controller.npad_button_state;
1062}
1063
1064DebugPadButton EmulatedController::GetDebugPadButtons() const {
1065 if (is_configuring) {
1066 return {};
1067 }
1068 return controller.debug_pad_button_state;
1069}
1070
1071AnalogSticks EmulatedController::GetSticks() const {
1072 if (is_configuring) {
1073 return {};
1074 }
1075 // Some drivers like stick from buttons need constant refreshing
1076 for (auto& device : stick_devices) {
1077 if (!device) {
1078 continue;
1079 }
1080 device->SoftUpdate();
1081 }
1082 return controller.analog_stick_state;
1083}
1084
1085NpadGcTriggerState EmulatedController::GetTriggers() const {
1086 if (is_configuring) {
1087 return {};
1088 }
1089 return controller.gc_trigger_state;
1090}
1091
1092MotionState EmulatedController::GetMotions() const {
1093 if (force_update_motion) {
1094 for (auto& device : motion_devices) {
1095 if (!device) {
1096 continue;
1097 }
1098 device->ForceUpdate();
1099 }
1100 }
1101 return controller.motion_state;
1102}
1103
1104ControllerColors EmulatedController::GetColors() const {
1105 return controller.colors_state;
1106}
1107
1108BatteryLevelState EmulatedController::GetBattery() const {
1109 return controller.battery_state;
1110}
1111
1112void EmulatedController::TriggerOnChange(ControllerTriggerType type, bool is_npad_service_update) {
1113 for (const auto& poller_pair : callback_list) {
1114 const ControllerUpdateCallback& poller = poller_pair.second;
1115 if (!is_npad_service_update && poller.is_npad_service) {
1116 continue;
1117 }
1118 if (poller.on_change) {
1119 poller.on_change(type);
1120 }
1121 }
1122}
1123
1124int EmulatedController::SetCallback(ControllerUpdateCallback update_callback) {
1125 std::lock_guard lock{mutex};
1126 callback_list.insert_or_assign(last_callback_key, std::move(update_callback));
1127 return last_callback_key++;
1128}
1129
1130void EmulatedController::DeleteCallback(int key) {
1131 std::lock_guard lock{mutex};
1132 const auto& iterator = callback_list.find(key);
1133 if (iterator == callback_list.end()) {
1134 LOG_ERROR(Input, "Tried to delete non-existent callback {}", key);
1135 return;
1136 }
1137 callback_list.erase(iterator);
1138}
1139} // namespace Core::HID
diff --git a/src/core/hid/emulated_controller.h b/src/core/hid/emulated_controller.h
new file mode 100644
index 000000000..e42aafebc
--- /dev/null
+++ b/src/core/hid/emulated_controller.h
@@ -0,0 +1,411 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <array>
8#include <functional>
9#include <memory>
10#include <mutex>
11#include <unordered_map>
12
13#include "common/common_types.h"
14#include "common/input.h"
15#include "common/param_package.h"
16#include "common/point.h"
17#include "common/quaternion.h"
18#include "common/settings.h"
19#include "common/vector_math.h"
20#include "core/hid/hid_types.h"
21#include "core/hid/motion_input.h"
22
23namespace Core::HID {
24const std::size_t max_emulated_controllers = 2;
25struct ControllerMotionInfo {
26 Common::Input::MotionStatus raw_status{};
27 MotionInput emulated{};
28};
29
30using ButtonDevices =
31 std::array<std::unique_ptr<Common::Input::InputDevice>, Settings::NativeButton::NumButtons>;
32using StickDevices =
33 std::array<std::unique_ptr<Common::Input::InputDevice>, Settings::NativeAnalog::NumAnalogs>;
34using ControllerMotionDevices =
35 std::array<std::unique_ptr<Common::Input::InputDevice>, Settings::NativeMotion::NumMotions>;
36using TriggerDevices =
37 std::array<std::unique_ptr<Common::Input::InputDevice>, Settings::NativeTrigger::NumTriggers>;
38using BatteryDevices =
39 std::array<std::unique_ptr<Common::Input::InputDevice>, max_emulated_controllers>;
40using OutputDevices =
41 std::array<std::unique_ptr<Common::Input::OutputDevice>, max_emulated_controllers>;
42
43using ButtonParams = std::array<Common::ParamPackage, Settings::NativeButton::NumButtons>;
44using StickParams = std::array<Common::ParamPackage, Settings::NativeAnalog::NumAnalogs>;
45using ControllerMotionParams = std::array<Common::ParamPackage, Settings::NativeMotion::NumMotions>;
46using TriggerParams = std::array<Common::ParamPackage, Settings::NativeTrigger::NumTriggers>;
47using BatteryParams = std::array<Common::ParamPackage, max_emulated_controllers>;
48using OutputParams = std::array<Common::ParamPackage, max_emulated_controllers>;
49
50using ButtonValues = std::array<Common::Input::ButtonStatus, Settings::NativeButton::NumButtons>;
51using SticksValues = std::array<Common::Input::StickStatus, Settings::NativeAnalog::NumAnalogs>;
52using TriggerValues =
53 std::array<Common::Input::TriggerStatus, Settings::NativeTrigger::NumTriggers>;
54using ControllerMotionValues = std::array<ControllerMotionInfo, Settings::NativeMotion::NumMotions>;
55using ColorValues = std::array<Common::Input::BodyColorStatus, max_emulated_controllers>;
56using BatteryValues = std::array<Common::Input::BatteryStatus, max_emulated_controllers>;
57using VibrationValues = std::array<Common::Input::VibrationStatus, max_emulated_controllers>;
58
59struct AnalogSticks {
60 AnalogStickState left{};
61 AnalogStickState right{};
62};
63
64struct ControllerColors {
65 NpadControllerColor fullkey{};
66 NpadControllerColor left{};
67 NpadControllerColor right{};
68};
69
70struct BatteryLevelState {
71 NpadPowerInfo dual{};
72 NpadPowerInfo left{};
73 NpadPowerInfo right{};
74};
75
76struct ControllerMotion {
77 Common::Vec3f accel{};
78 Common::Vec3f gyro{};
79 Common::Vec3f rotation{};
80 std::array<Common::Vec3f, 3> orientation{};
81 bool is_at_rest{};
82};
83
84enum EmulatedDeviceIndex : u8 {
85 LeftIndex,
86 RightIndex,
87 DualIndex,
88 AllDevices,
89};
90
91using MotionState = std::array<ControllerMotion, 2>;
92
93struct ControllerStatus {
94 // Data from input_common
95 ButtonValues button_values{};
96 SticksValues stick_values{};
97 ControllerMotionValues motion_values{};
98 TriggerValues trigger_values{};
99 ColorValues color_values{};
100 BatteryValues battery_values{};
101 VibrationValues vibration_values{};
102
103 // Data for HID serices
104 NpadButtonState npad_button_state{};
105 DebugPadButton debug_pad_button_state{};
106 AnalogSticks analog_stick_state{};
107 MotionState motion_state{};
108 NpadGcTriggerState gc_trigger_state{};
109 ControllerColors colors_state{};
110 BatteryLevelState battery_state{};
111};
112
113enum class ControllerTriggerType {
114 Button,
115 Stick,
116 Trigger,
117 Motion,
118 Color,
119 Battery,
120 Vibration,
121 Connected,
122 Disconnected,
123 Type,
124 All,
125};
126
127struct ControllerUpdateCallback {
128 std::function<void(ControllerTriggerType)> on_change;
129 bool is_npad_service;
130};
131
132class EmulatedController {
133public:
134 /**
135 * Contains all input data (buttons, joysticks, vibration, and motion) within this controller.
136 * @param npad_id_type npad id type for this specific controller
137 */
138 explicit EmulatedController(NpadIdType npad_id_type_);
139 ~EmulatedController();
140
141 YUZU_NON_COPYABLE(EmulatedController);
142 YUZU_NON_MOVEABLE(EmulatedController);
143
144 /// Converts the controller type from settings to npad type
145 static NpadStyleIndex MapSettingsTypeToNPad(Settings::ControllerType type);
146
147 /// Converts npad type to the equivalent of controller type from settings
148 static Settings::ControllerType MapNPadToSettingsType(NpadStyleIndex type);
149
150 /// Gets the NpadIdType for this controller
151 NpadIdType GetNpadIdType() const;
152
153 /// Sets the NpadStyleIndex for this controller
154 void SetNpadStyleIndex(NpadStyleIndex npad_type_);
155
156 /**
157 * Gets the NpadStyleIndex for this controller
158 * @param get_temporary_value If true tmp_npad_type will be returned
159 * @return NpadStyleIndex set on the controller
160 */
161 NpadStyleIndex GetNpadStyleIndex(bool get_temporary_value = false) const;
162
163 /**
164 * Sets the supported controller types. Disconnects the controller if current type is not
165 * supported
166 * @param supported_styles bitflag with supported types
167 */
168 void SetSupportedNpadStyleTag(NpadStyleTag supported_styles);
169
170 /// Sets the connected status to true
171 void Connect();
172
173 /// Sets the connected status to false
174 void Disconnect();
175
176 /**
177 * Is the emulated connected
178 * @param get_temporary_value If true tmp_is_connected will be returned
179 * @return true if the controller has the connected status
180 */
181 bool IsConnected(bool get_temporary_value = false) const;
182
183 /// Returns true if vibration is enabled
184 bool IsVibrationEnabled() const;
185
186 /// Removes all callbacks created from input devices
187 void UnloadInput();
188
189 /**
190 * Sets the emulated controller into configuring mode
191 * This prevents the modification of the HID state of the emulated controller by input commands
192 */
193 void EnableConfiguration();
194
195 /// Returns the emulated controller into normal mode, allowing the modification of the HID state
196 void DisableConfiguration();
197
198 /// Returns true if the emulated controller is in configuring mode
199 bool IsConfiguring() const;
200
201 /// Reload all input devices
202 void ReloadInput();
203
204 /// Overrides current mapped devices with the stored configuration and reloads all input devices
205 void ReloadFromSettings();
206
207 /// Saves the current mapped configuration
208 void SaveCurrentConfig();
209
210 /// Reverts any mapped changes made that weren't saved
211 void RestoreConfig();
212
213 /// Returns a vector of mapped devices from the mapped button and stick parameters
214 std::vector<Common::ParamPackage> GetMappedDevices(EmulatedDeviceIndex device_index) const;
215
216 // Returns the current mapped button device
217 Common::ParamPackage GetButtonParam(std::size_t index) const;
218
219 // Returns the current mapped stick device
220 Common::ParamPackage GetStickParam(std::size_t index) const;
221
222 // Returns the current mapped motion device
223 Common::ParamPackage GetMotionParam(std::size_t index) const;
224
225 /**
226 * Updates the current mapped button device
227 * @param param ParamPackage with controller data to be mapped
228 */
229 void SetButtonParam(std::size_t index, Common::ParamPackage param);
230
231 /**
232 * Updates the current mapped stick device
233 * @param param ParamPackage with controller data to be mapped
234 */
235 void SetStickParam(std::size_t index, Common::ParamPackage param);
236
237 /**
238 * Updates the current mapped motion device
239 * @param param ParamPackage with controller data to be mapped
240 */
241 void SetMotionParam(std::size_t index, Common::ParamPackage param);
242
243 /// Returns the latest button status from the controller with parameters
244 ButtonValues GetButtonsValues() const;
245
246 /// Returns the latest analog stick status from the controller with parameters
247 SticksValues GetSticksValues() const;
248
249 /// Returns the latest trigger status from the controller with parameters
250 TriggerValues GetTriggersValues() const;
251
252 /// Returns the latest motion status from the controller with parameters
253 ControllerMotionValues GetMotionValues() const;
254
255 /// Returns the latest color status from the controller with parameters
256 ColorValues GetColorsValues() const;
257
258 /// Returns the latest battery status from the controller with parameters
259 BatteryValues GetBatteryValues() const;
260
261 /// Returns the latest status of button input for the npad service
262 NpadButtonState GetNpadButtons() const;
263
264 /// Returns the latest status of button input for the debug pad service
265 DebugPadButton GetDebugPadButtons() const;
266
267 /// Returns the latest status of stick input from the mouse
268 AnalogSticks GetSticks() const;
269
270 /// Returns the latest status of trigger input from the mouse
271 NpadGcTriggerState GetTriggers() const;
272
273 /// Returns the latest status of motion input from the mouse
274 MotionState GetMotions() const;
275
276 /// Returns the latest color value from the controller
277 ControllerColors GetColors() const;
278
279 /// Returns the latest battery status from the controller
280 BatteryLevelState GetBattery() const;
281
282 /**
283 * Sends a specific vibration to the output device
284 * @return returns true if vibration had no errors
285 */
286 bool SetVibration(std::size_t device_index, VibrationValue vibration);
287
288 /**
289 * Sends a small vibration to the output device
290 * @return returns true if SetVibration was successfull
291 */
292 bool TestVibration(std::size_t device_index);
293
294 /// Returns the led pattern corresponding to this emulated controller
295 LedPattern GetLedPattern() const;
296
297 /// Asks the output device to change the player led pattern
298 void SetLedPattern();
299
300 /**
301 * Adds a callback to the list of events
302 * @param update_callback A ConsoleUpdateCallback that will be triggered
303 * @return an unique key corresponding to the callback index in the list
304 */
305 int SetCallback(ControllerUpdateCallback update_callback);
306
307 /**
308 * Removes a callback from the list stopping any future events to this object
309 * @param key Key corresponding to the callback index in the list
310 */
311 void DeleteCallback(int key);
312
313private:
314 /// creates input devices from params
315 void LoadDevices();
316
317 /// Set the params for TAS devices
318 void LoadTASParams();
319
320 /**
321 * Checks the current controller type against the supported_style_tag
322 * @return true if the controller is supported
323 */
324 bool IsControllerSupported() const;
325
326 /**
327 * Updates the button status of the controller
328 * @param callback A CallbackStatus containing the button status
329 * @param index Button ID of the to be updated
330 */
331 void SetButton(const Common::Input::CallbackStatus& callback, std::size_t index,
332 Common::UUID uuid);
333
334 /**
335 * Updates the analog stick status of the controller
336 * @param callback A CallbackStatus containing the analog stick status
337 * @param index stick ID of the to be updated
338 */
339 void SetStick(const Common::Input::CallbackStatus& callback, std::size_t index,
340 Common::UUID uuid);
341
342 /**
343 * Updates the trigger status of the controller
344 * @param callback A CallbackStatus containing the trigger status
345 * @param index trigger ID of the to be updated
346 */
347 void SetTrigger(const Common::Input::CallbackStatus& callback, std::size_t index,
348 Common::UUID uuid);
349
350 /**
351 * Updates the motion status of the controller
352 * @param callback A CallbackStatus containing gyro and accelerometer data
353 * @param index motion ID of the to be updated
354 */
355 void SetMotion(const Common::Input::CallbackStatus& callback, std::size_t index);
356
357 /**
358 * Updates the battery status of the controller
359 * @param callback A CallbackStatus containing the battery status
360 * @param index Button ID of the to be updated
361 */
362 void SetBattery(const Common::Input::CallbackStatus& callback, std::size_t index);
363
364 /**
365 * Triggers a callback that something has changed on the controller status
366 * @param type Input type of the event to trigger
367 * @param is_service_update indicates if this event should only be sent to HID services
368 */
369 void TriggerOnChange(ControllerTriggerType type, bool is_service_update);
370
371 NpadIdType npad_id_type;
372 NpadStyleIndex npad_type{NpadStyleIndex::None};
373 NpadStyleTag supported_style_tag{NpadStyleSet::All};
374 bool is_connected{false};
375 bool is_configuring{false};
376 f32 motion_sensitivity{0.01f};
377 bool force_update_motion{false};
378
379 // Temporary values to avoid doing changes while the controller is in configuring mode
380 NpadStyleIndex tmp_npad_type{NpadStyleIndex::None};
381 bool tmp_is_connected{false};
382
383 ButtonParams button_params;
384 StickParams stick_params;
385 ControllerMotionParams motion_params;
386 TriggerParams trigger_params;
387 BatteryParams battery_params;
388 OutputParams output_params;
389
390 ButtonDevices button_devices;
391 StickDevices stick_devices;
392 ControllerMotionDevices motion_devices;
393 TriggerDevices trigger_devices;
394 BatteryDevices battery_devices;
395 OutputDevices output_devices;
396
397 // TAS related variables
398 ButtonParams tas_button_params;
399 StickParams tas_stick_params;
400 ButtonDevices tas_button_devices;
401 StickDevices tas_stick_devices;
402
403 mutable std::mutex mutex;
404 std::unordered_map<int, ControllerUpdateCallback> callback_list;
405 int last_callback_key = 0;
406
407 // Stores the current status of all controller input
408 ControllerStatus controller;
409};
410
411} // namespace Core::HID
diff --git a/src/core/hid/emulated_devices.cpp b/src/core/hid/emulated_devices.cpp
new file mode 100644
index 000000000..708480f2d
--- /dev/null
+++ b/src/core/hid/emulated_devices.cpp
@@ -0,0 +1,459 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included
4
5#include <algorithm>
6#include <fmt/format.h>
7
8#include "core/hid/emulated_devices.h"
9#include "core/hid/input_converter.h"
10
11namespace Core::HID {
12
13EmulatedDevices::EmulatedDevices() = default;
14
15EmulatedDevices::~EmulatedDevices() = default;
16
17void EmulatedDevices::ReloadFromSettings() {
18 ReloadInput();
19}
20
21void EmulatedDevices::ReloadInput() {
22 // If you load any device here add the equivalent to the UnloadInput() function
23 std::size_t key_index = 0;
24 for (auto& mouse_device : mouse_button_devices) {
25 Common::ParamPackage mouse_params;
26 mouse_params.Set("engine", "mouse");
27 mouse_params.Set("button", static_cast<int>(key_index));
28 mouse_device = Common::Input::CreateDevice<Common::Input::InputDevice>(mouse_params);
29 key_index++;
30 }
31
32 mouse_stick_device = Common::Input::CreateDeviceFromString<Common::Input::InputDevice>(
33 "engine:mouse,axis_x:0,axis_y:1");
34
35 // First two axis are reserved for mouse position
36 key_index = 2;
37 for (auto& mouse_device : mouse_analog_devices) {
38 Common::ParamPackage mouse_params;
39 mouse_params.Set("engine", "mouse");
40 mouse_params.Set("axis", static_cast<int>(key_index));
41 mouse_device = Common::Input::CreateDevice<Common::Input::InputDevice>(mouse_params);
42 key_index++;
43 }
44
45 key_index = 0;
46 for (auto& keyboard_device : keyboard_devices) {
47 // Keyboard keys are only mapped on port 1, pad 0
48 Common::ParamPackage keyboard_params;
49 keyboard_params.Set("engine", "keyboard");
50 keyboard_params.Set("button", static_cast<int>(key_index));
51 keyboard_params.Set("port", 1);
52 keyboard_params.Set("pad", 0);
53 keyboard_device = Common::Input::CreateDevice<Common::Input::InputDevice>(keyboard_params);
54 key_index++;
55 }
56
57 key_index = 0;
58 for (auto& keyboard_device : keyboard_modifier_devices) {
59 // Keyboard moddifiers are only mapped on port 1, pad 1
60 Common::ParamPackage keyboard_params;
61 keyboard_params.Set("engine", "keyboard");
62 keyboard_params.Set("button", static_cast<int>(key_index));
63 keyboard_params.Set("port", 1);
64 keyboard_params.Set("pad", 1);
65 keyboard_device = Common::Input::CreateDevice<Common::Input::InputDevice>(keyboard_params);
66 key_index++;
67 }
68
69 for (std::size_t index = 0; index < mouse_button_devices.size(); ++index) {
70 if (!mouse_button_devices[index]) {
71 continue;
72 }
73 mouse_button_devices[index]->SetCallback({
74 .on_change =
75 [this, index](const Common::Input::CallbackStatus& callback) {
76 SetMouseButton(callback, index);
77 },
78 });
79 }
80
81 for (std::size_t index = 0; index < mouse_analog_devices.size(); ++index) {
82 if (!mouse_analog_devices[index]) {
83 continue;
84 }
85 mouse_analog_devices[index]->SetCallback({
86 .on_change =
87 [this, index](const Common::Input::CallbackStatus& callback) {
88 SetMouseAnalog(callback, index);
89 },
90 });
91 }
92
93 if (mouse_stick_device) {
94 mouse_stick_device->SetCallback({
95 .on_change =
96 [this](const Common::Input::CallbackStatus& callback) { SetMouseStick(callback); },
97 });
98 }
99
100 for (std::size_t index = 0; index < keyboard_devices.size(); ++index) {
101 if (!keyboard_devices[index]) {
102 continue;
103 }
104 keyboard_devices[index]->SetCallback({
105 .on_change =
106 [this, index](const Common::Input::CallbackStatus& callback) {
107 SetKeyboardButton(callback, index);
108 },
109 });
110 }
111
112 for (std::size_t index = 0; index < keyboard_modifier_devices.size(); ++index) {
113 if (!keyboard_modifier_devices[index]) {
114 continue;
115 }
116 keyboard_modifier_devices[index]->SetCallback({
117 .on_change =
118 [this, index](const Common::Input::CallbackStatus& callback) {
119 SetKeyboardModifier(callback, index);
120 },
121 });
122 }
123}
124
125void EmulatedDevices::UnloadInput() {
126 for (auto& button : mouse_button_devices) {
127 button.reset();
128 }
129 for (auto& analog : mouse_analog_devices) {
130 analog.reset();
131 }
132 mouse_stick_device.reset();
133 for (auto& button : keyboard_devices) {
134 button.reset();
135 }
136 for (auto& button : keyboard_modifier_devices) {
137 button.reset();
138 }
139}
140
141void EmulatedDevices::EnableConfiguration() {
142 is_configuring = true;
143 SaveCurrentConfig();
144}
145
146void EmulatedDevices::DisableConfiguration() {
147 is_configuring = false;
148}
149
150bool EmulatedDevices::IsConfiguring() const {
151 return is_configuring;
152}
153
154void EmulatedDevices::SaveCurrentConfig() {
155 if (!is_configuring) {
156 return;
157 }
158}
159
160void EmulatedDevices::RestoreConfig() {
161 if (!is_configuring) {
162 return;
163 }
164 ReloadFromSettings();
165}
166
167void EmulatedDevices::SetKeyboardButton(const Common::Input::CallbackStatus& callback,
168 std::size_t index) {
169 if (index >= device_status.keyboard_values.size()) {
170 return;
171 }
172 std::lock_guard lock{mutex};
173 bool value_changed = false;
174 const auto new_status = TransformToButton(callback);
175 auto& current_status = device_status.keyboard_values[index];
176 current_status.toggle = new_status.toggle;
177
178 // Update button status with current status
179 if (!current_status.toggle) {
180 current_status.locked = false;
181 if (current_status.value != new_status.value) {
182 current_status.value = new_status.value;
183 value_changed = true;
184 }
185 } else {
186 // Toggle button and lock status
187 if (new_status.value && !current_status.locked) {
188 current_status.locked = true;
189 current_status.value = !current_status.value;
190 value_changed = true;
191 }
192
193 // Unlock button, ready for next press
194 if (!new_status.value && current_status.locked) {
195 current_status.locked = false;
196 }
197 }
198
199 if (!value_changed) {
200 return;
201 }
202
203 if (is_configuring) {
204 TriggerOnChange(DeviceTriggerType::Keyboard);
205 return;
206 }
207
208 // Index should be converted from NativeKeyboard to KeyboardKeyIndex
209 UpdateKey(index, current_status.value);
210
211 TriggerOnChange(DeviceTriggerType::Keyboard);
212}
213
214void EmulatedDevices::UpdateKey(std::size_t key_index, bool status) {
215 constexpr std::size_t KEYS_PER_BYTE = 8;
216 auto& entry = device_status.keyboard_state.key[key_index / KEYS_PER_BYTE];
217 const u8 mask = static_cast<u8>(1 << (key_index % KEYS_PER_BYTE));
218 if (status) {
219 entry = entry | mask;
220 } else {
221 entry = static_cast<u8>(entry & ~mask);
222 }
223}
224
225void EmulatedDevices::SetKeyboardModifier(const Common::Input::CallbackStatus& callback,
226 std::size_t index) {
227 if (index >= device_status.keyboard_moddifier_values.size()) {
228 return;
229 }
230 std::lock_guard lock{mutex};
231 bool value_changed = false;
232 const auto new_status = TransformToButton(callback);
233 auto& current_status = device_status.keyboard_moddifier_values[index];
234 current_status.toggle = new_status.toggle;
235
236 // Update button status with current
237 if (!current_status.toggle) {
238 current_status.locked = false;
239 if (current_status.value != new_status.value) {
240 current_status.value = new_status.value;
241 value_changed = true;
242 }
243 } else {
244 // Toggle button and lock status
245 if (new_status.value && !current_status.locked) {
246 current_status.locked = true;
247 current_status.value = !current_status.value;
248 value_changed = true;
249 }
250
251 // Unlock button ready for next press
252 if (!new_status.value && current_status.locked) {
253 current_status.locked = false;
254 }
255 }
256
257 if (!value_changed) {
258 return;
259 }
260
261 if (is_configuring) {
262 TriggerOnChange(DeviceTriggerType::KeyboardModdifier);
263 return;
264 }
265
266 switch (index) {
267 case Settings::NativeKeyboard::LeftControl:
268 case Settings::NativeKeyboard::RightControl:
269 device_status.keyboard_moddifier_state.control.Assign(current_status.value);
270 break;
271 case Settings::NativeKeyboard::LeftShift:
272 case Settings::NativeKeyboard::RightShift:
273 device_status.keyboard_moddifier_state.shift.Assign(current_status.value);
274 break;
275 case Settings::NativeKeyboard::LeftAlt:
276 device_status.keyboard_moddifier_state.left_alt.Assign(current_status.value);
277 break;
278 case Settings::NativeKeyboard::RightAlt:
279 device_status.keyboard_moddifier_state.right_alt.Assign(current_status.value);
280 break;
281 case Settings::NativeKeyboard::CapsLock:
282 device_status.keyboard_moddifier_state.caps_lock.Assign(current_status.value);
283 break;
284 case Settings::NativeKeyboard::ScrollLock:
285 device_status.keyboard_moddifier_state.scroll_lock.Assign(current_status.value);
286 break;
287 case Settings::NativeKeyboard::NumLock:
288 device_status.keyboard_moddifier_state.num_lock.Assign(current_status.value);
289 break;
290 }
291
292 TriggerOnChange(DeviceTriggerType::KeyboardModdifier);
293}
294
295void EmulatedDevices::SetMouseButton(const Common::Input::CallbackStatus& callback,
296 std::size_t index) {
297 if (index >= device_status.mouse_button_values.size()) {
298 return;
299 }
300 std::lock_guard lock{mutex};
301 bool value_changed = false;
302 const auto new_status = TransformToButton(callback);
303 auto& current_status = device_status.mouse_button_values[index];
304 current_status.toggle = new_status.toggle;
305
306 // Update button status with current
307 if (!current_status.toggle) {
308 current_status.locked = false;
309 if (current_status.value != new_status.value) {
310 current_status.value = new_status.value;
311 value_changed = true;
312 }
313 } else {
314 // Toggle button and lock status
315 if (new_status.value && !current_status.locked) {
316 current_status.locked = true;
317 current_status.value = !current_status.value;
318 value_changed = true;
319 }
320
321 // Unlock button ready for next press
322 if (!new_status.value && current_status.locked) {
323 current_status.locked = false;
324 }
325 }
326
327 if (!value_changed) {
328 return;
329 }
330
331 if (is_configuring) {
332 TriggerOnChange(DeviceTriggerType::Mouse);
333 return;
334 }
335
336 switch (index) {
337 case Settings::NativeMouseButton::Left:
338 device_status.mouse_button_state.left.Assign(current_status.value);
339 break;
340 case Settings::NativeMouseButton::Right:
341 device_status.mouse_button_state.right.Assign(current_status.value);
342 break;
343 case Settings::NativeMouseButton::Middle:
344 device_status.mouse_button_state.middle.Assign(current_status.value);
345 break;
346 case Settings::NativeMouseButton::Forward:
347 device_status.mouse_button_state.forward.Assign(current_status.value);
348 break;
349 case Settings::NativeMouseButton::Back:
350 device_status.mouse_button_state.back.Assign(current_status.value);
351 break;
352 }
353
354 TriggerOnChange(DeviceTriggerType::Mouse);
355}
356
357void EmulatedDevices::SetMouseAnalog(const Common::Input::CallbackStatus& callback,
358 std::size_t index) {
359 if (index >= device_status.mouse_analog_values.size()) {
360 return;
361 }
362 std::lock_guard lock{mutex};
363 const auto analog_value = TransformToAnalog(callback);
364
365 device_status.mouse_analog_values[index] = analog_value;
366
367 if (is_configuring) {
368 device_status.mouse_position_state = {};
369 TriggerOnChange(DeviceTriggerType::Mouse);
370 return;
371 }
372
373 switch (index) {
374 case Settings::NativeMouseWheel::X:
375 device_status.mouse_wheel_state.x = static_cast<s32>(analog_value.value);
376 break;
377 case Settings::NativeMouseWheel::Y:
378 device_status.mouse_wheel_state.y = static_cast<s32>(analog_value.value);
379 break;
380 }
381
382 TriggerOnChange(DeviceTriggerType::Mouse);
383}
384
385void EmulatedDevices::SetMouseStick(const Common::Input::CallbackStatus& callback) {
386 std::lock_guard lock{mutex};
387 const auto touch_value = TransformToTouch(callback);
388
389 device_status.mouse_stick_value = touch_value;
390
391 if (is_configuring) {
392 device_status.mouse_position_state = {};
393 TriggerOnChange(DeviceTriggerType::Mouse);
394 return;
395 }
396
397 device_status.mouse_position_state.x = touch_value.x.value;
398 device_status.mouse_position_state.y = touch_value.y.value;
399
400 TriggerOnChange(DeviceTriggerType::Mouse);
401}
402
403KeyboardValues EmulatedDevices::GetKeyboardValues() const {
404 return device_status.keyboard_values;
405}
406
407KeyboardModifierValues EmulatedDevices::GetKeyboardModdifierValues() const {
408 return device_status.keyboard_moddifier_values;
409}
410
411MouseButtonValues EmulatedDevices::GetMouseButtonsValues() const {
412 return device_status.mouse_button_values;
413}
414
415KeyboardKey EmulatedDevices::GetKeyboard() const {
416 return device_status.keyboard_state;
417}
418
419KeyboardModifier EmulatedDevices::GetKeyboardModifier() const {
420 return device_status.keyboard_moddifier_state;
421}
422
423MouseButton EmulatedDevices::GetMouseButtons() const {
424 return device_status.mouse_button_state;
425}
426
427MousePosition EmulatedDevices::GetMousePosition() const {
428 return device_status.mouse_position_state;
429}
430
431AnalogStickState EmulatedDevices::GetMouseWheel() const {
432 return device_status.mouse_wheel_state;
433}
434
435void EmulatedDevices::TriggerOnChange(DeviceTriggerType type) {
436 for (const auto& poller_pair : callback_list) {
437 const InterfaceUpdateCallback& poller = poller_pair.second;
438 if (poller.on_change) {
439 poller.on_change(type);
440 }
441 }
442}
443
444int EmulatedDevices::SetCallback(InterfaceUpdateCallback update_callback) {
445 std::lock_guard lock{mutex};
446 callback_list.insert_or_assign(last_callback_key, std::move(update_callback));
447 return last_callback_key++;
448}
449
450void EmulatedDevices::DeleteCallback(int key) {
451 std::lock_guard lock{mutex};
452 const auto& iterator = callback_list.find(key);
453 if (iterator == callback_list.end()) {
454 LOG_ERROR(Input, "Tried to delete non-existent callback {}", key);
455 return;
456 }
457 callback_list.erase(iterator);
458}
459} // namespace Core::HID
diff --git a/src/core/hid/emulated_devices.h b/src/core/hid/emulated_devices.h
new file mode 100644
index 000000000..790d3b411
--- /dev/null
+++ b/src/core/hid/emulated_devices.h
@@ -0,0 +1,210 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <array>
8#include <functional>
9#include <memory>
10#include <mutex>
11#include <unordered_map>
12
13#include "common/common_types.h"
14#include "common/input.h"
15#include "common/param_package.h"
16#include "common/settings.h"
17#include "core/hid/hid_types.h"
18
19namespace Core::HID {
20using KeyboardDevices = std::array<std::unique_ptr<Common::Input::InputDevice>,
21 Settings::NativeKeyboard::NumKeyboardKeys>;
22using KeyboardModifierDevices = std::array<std::unique_ptr<Common::Input::InputDevice>,
23 Settings::NativeKeyboard::NumKeyboardMods>;
24using MouseButtonDevices = std::array<std::unique_ptr<Common::Input::InputDevice>,
25 Settings::NativeMouseButton::NumMouseButtons>;
26using MouseAnalogDevices = std::array<std::unique_ptr<Common::Input::InputDevice>,
27 Settings::NativeMouseWheel::NumMouseWheels>;
28using MouseStickDevice = std::unique_ptr<Common::Input::InputDevice>;
29
30using MouseButtonParams =
31 std::array<Common::ParamPackage, Settings::NativeMouseButton::NumMouseButtons>;
32
33using KeyboardValues =
34 std::array<Common::Input::ButtonStatus, Settings::NativeKeyboard::NumKeyboardKeys>;
35using KeyboardModifierValues =
36 std::array<Common::Input::ButtonStatus, Settings::NativeKeyboard::NumKeyboardMods>;
37using MouseButtonValues =
38 std::array<Common::Input::ButtonStatus, Settings::NativeMouseButton::NumMouseButtons>;
39using MouseAnalogValues =
40 std::array<Common::Input::AnalogStatus, Settings::NativeMouseWheel::NumMouseWheels>;
41using MouseStickValue = Common::Input::TouchStatus;
42
43struct MousePosition {
44 f32 x;
45 f32 y;
46};
47
48struct DeviceStatus {
49 // Data from input_common
50 KeyboardValues keyboard_values{};
51 KeyboardModifierValues keyboard_moddifier_values{};
52 MouseButtonValues mouse_button_values{};
53 MouseAnalogValues mouse_analog_values{};
54 MouseStickValue mouse_stick_value{};
55
56 // Data for HID serices
57 KeyboardKey keyboard_state{};
58 KeyboardModifier keyboard_moddifier_state{};
59 MouseButton mouse_button_state{};
60 MousePosition mouse_position_state{};
61 AnalogStickState mouse_wheel_state{};
62};
63
64enum class DeviceTriggerType {
65 Keyboard,
66 KeyboardModdifier,
67 Mouse,
68};
69
70struct InterfaceUpdateCallback {
71 std::function<void(DeviceTriggerType)> on_change;
72};
73
74class EmulatedDevices {
75public:
76 /**
77 * Contains all input data related to external devices that aren't necesarily a controller
78 * This includes devices such as the keyboard or mouse
79 */
80 explicit EmulatedDevices();
81 ~EmulatedDevices();
82
83 YUZU_NON_COPYABLE(EmulatedDevices);
84 YUZU_NON_MOVEABLE(EmulatedDevices);
85
86 /// Removes all callbacks created from input devices
87 void UnloadInput();
88
89 /**
90 * Sets the emulated devices into configuring mode
91 * This prevents the modification of the HID state of the emulated devices by input commands
92 */
93 void EnableConfiguration();
94
95 /// Returns the emulated devices into normal mode, allowing the modification of the HID state
96 void DisableConfiguration();
97
98 /// Returns true if the emulated device is in configuring mode
99 bool IsConfiguring() const;
100
101 /// Reload all input devices
102 void ReloadInput();
103
104 /// Overrides current mapped devices with the stored configuration and reloads all input devices
105 void ReloadFromSettings();
106
107 /// Saves the current mapped configuration
108 void SaveCurrentConfig();
109
110 /// Reverts any mapped changes made that weren't saved
111 void RestoreConfig();
112
113 /// Returns the latest status of button input from the keyboard with parameters
114 KeyboardValues GetKeyboardValues() const;
115
116 /// Returns the latest status of button input from the keyboard modifiers with parameters
117 KeyboardModifierValues GetKeyboardModdifierValues() const;
118
119 /// Returns the latest status of button input from the mouse with parameters
120 MouseButtonValues GetMouseButtonsValues() const;
121
122 /// Returns the latest status of button input from the keyboard
123 KeyboardKey GetKeyboard() const;
124
125 /// Returns the latest status of button input from the keyboard modifiers
126 KeyboardModifier GetKeyboardModifier() const;
127
128 /// Returns the latest status of button input from the mouse
129 MouseButton GetMouseButtons() const;
130
131 /// Returns the latest mouse coordinates
132 MousePosition GetMousePosition() const;
133
134 /// Returns the latest mouse wheel change
135 AnalogStickState GetMouseWheel() const;
136
137 /**
138 * Adds a callback to the list of events
139 * @param update_callback InterfaceUpdateCallback that will be triggered
140 * @return an unique key corresponding to the callback index in the list
141 */
142 int SetCallback(InterfaceUpdateCallback update_callback);
143
144 /**
145 * Removes a callback from the list stopping any future events to this object
146 * @param key Key corresponding to the callback index in the list
147 */
148 void DeleteCallback(int key);
149
150private:
151 /// Helps assigning a value to keyboard_state
152 void UpdateKey(std::size_t key_index, bool status);
153
154 /**
155 * Updates the touch status of the keyboard device
156 * @param callback A CallbackStatus containing the key status
157 * @param index key ID to be updated
158 */
159 void SetKeyboardButton(const Common::Input::CallbackStatus& callback, std::size_t index);
160
161 /**
162 * Updates the keyboard status of the keyboard device
163 * @param callback A CallbackStatus containing the modifier key status
164 * @param index modifier key ID to be updated
165 */
166 void SetKeyboardModifier(const Common::Input::CallbackStatus& callback, std::size_t index);
167
168 /**
169 * Updates the mouse button status of the mouse device
170 * @param callback A CallbackStatus containing the button status
171 * @param index Button ID to be updated
172 */
173 void SetMouseButton(const Common::Input::CallbackStatus& callback, std::size_t index);
174
175 /**
176 * Updates the mouse wheel status of the mouse device
177 * @param callback A CallbackStatus containing the wheel status
178 * @param index wheel ID to be updated
179 */
180 void SetMouseAnalog(const Common::Input::CallbackStatus& callback, std::size_t index);
181
182 /**
183 * Updates the mouse position status of the mouse device
184 * @param callback A CallbackStatus containing the position status
185 */
186 void SetMouseStick(const Common::Input::CallbackStatus& callback);
187
188 /**
189 * Triggers a callback that something has changed on the device status
190 * @param type Input type of the event to trigger
191 */
192 void TriggerOnChange(DeviceTriggerType type);
193
194 bool is_configuring{false};
195
196 KeyboardDevices keyboard_devices;
197 KeyboardModifierDevices keyboard_modifier_devices;
198 MouseButtonDevices mouse_button_devices;
199 MouseAnalogDevices mouse_analog_devices;
200 MouseStickDevice mouse_stick_device;
201
202 mutable std::mutex mutex;
203 std::unordered_map<int, InterfaceUpdateCallback> callback_list;
204 int last_callback_key = 0;
205
206 // Stores the current status of all external device input
207 DeviceStatus device_status;
208};
209
210} // namespace Core::HID
diff --git a/src/core/hid/hid_core.cpp b/src/core/hid/hid_core.cpp
new file mode 100644
index 000000000..a1c3bbb57
--- /dev/null
+++ b/src/core/hid/hid_core.cpp
@@ -0,0 +1,214 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "common/assert.h"
6#include "core/hid/emulated_console.h"
7#include "core/hid/emulated_controller.h"
8#include "core/hid/emulated_devices.h"
9#include "core/hid/hid_core.h"
10
11namespace Core::HID {
12
13HIDCore::HIDCore()
14 : player_1{std::make_unique<EmulatedController>(NpadIdType::Player1)},
15 player_2{std::make_unique<EmulatedController>(NpadIdType::Player2)},
16 player_3{std::make_unique<EmulatedController>(NpadIdType::Player3)},
17 player_4{std::make_unique<EmulatedController>(NpadIdType::Player4)},
18 player_5{std::make_unique<EmulatedController>(NpadIdType::Player5)},
19 player_6{std::make_unique<EmulatedController>(NpadIdType::Player6)},
20 player_7{std::make_unique<EmulatedController>(NpadIdType::Player7)},
21 player_8{std::make_unique<EmulatedController>(NpadIdType::Player8)},
22 other{std::make_unique<EmulatedController>(NpadIdType::Other)},
23 handheld{std::make_unique<EmulatedController>(NpadIdType::Handheld)},
24 console{std::make_unique<EmulatedConsole>()}, devices{std::make_unique<EmulatedDevices>()} {}
25
26HIDCore::~HIDCore() = default;
27
28EmulatedController* HIDCore::GetEmulatedController(NpadIdType npad_id_type) {
29 switch (npad_id_type) {
30 case NpadIdType::Player1:
31 return player_1.get();
32 case NpadIdType::Player2:
33 return player_2.get();
34 case NpadIdType::Player3:
35 return player_3.get();
36 case NpadIdType::Player4:
37 return player_4.get();
38 case NpadIdType::Player5:
39 return player_5.get();
40 case NpadIdType::Player6:
41 return player_6.get();
42 case NpadIdType::Player7:
43 return player_7.get();
44 case NpadIdType::Player8:
45 return player_8.get();
46 case NpadIdType::Other:
47 return other.get();
48 case NpadIdType::Handheld:
49 return handheld.get();
50 case NpadIdType::Invalid:
51 default:
52 UNREACHABLE_MSG("Invalid NpadIdType={}", npad_id_type);
53 return nullptr;
54 }
55}
56
57const EmulatedController* HIDCore::GetEmulatedController(NpadIdType npad_id_type) const {
58 switch (npad_id_type) {
59 case NpadIdType::Player1:
60 return player_1.get();
61 case NpadIdType::Player2:
62 return player_2.get();
63 case NpadIdType::Player3:
64 return player_3.get();
65 case NpadIdType::Player4:
66 return player_4.get();
67 case NpadIdType::Player5:
68 return player_5.get();
69 case NpadIdType::Player6:
70 return player_6.get();
71 case NpadIdType::Player7:
72 return player_7.get();
73 case NpadIdType::Player8:
74 return player_8.get();
75 case NpadIdType::Other:
76 return other.get();
77 case NpadIdType::Handheld:
78 return handheld.get();
79 case NpadIdType::Invalid:
80 default:
81 UNREACHABLE_MSG("Invalid NpadIdType={}", npad_id_type);
82 return nullptr;
83 }
84}
85EmulatedConsole* HIDCore::GetEmulatedConsole() {
86 return console.get();
87}
88
89const EmulatedConsole* HIDCore::GetEmulatedConsole() const {
90 return console.get();
91}
92
93EmulatedDevices* HIDCore::GetEmulatedDevices() {
94 return devices.get();
95}
96
97const EmulatedDevices* HIDCore::GetEmulatedDevices() const {
98 return devices.get();
99}
100
101EmulatedController* HIDCore::GetEmulatedControllerByIndex(std::size_t index) {
102 return GetEmulatedController(IndexToNpadIdType(index));
103}
104
105const EmulatedController* HIDCore::GetEmulatedControllerByIndex(std::size_t index) const {
106 return GetEmulatedController(IndexToNpadIdType(index));
107}
108
109void HIDCore::SetSupportedStyleTag(NpadStyleTag style_tag) {
110 supported_style_tag.raw = style_tag.raw;
111 player_1->SetSupportedNpadStyleTag(supported_style_tag);
112 player_2->SetSupportedNpadStyleTag(supported_style_tag);
113 player_3->SetSupportedNpadStyleTag(supported_style_tag);
114 player_4->SetSupportedNpadStyleTag(supported_style_tag);
115 player_5->SetSupportedNpadStyleTag(supported_style_tag);
116 player_6->SetSupportedNpadStyleTag(supported_style_tag);
117 player_7->SetSupportedNpadStyleTag(supported_style_tag);
118 player_8->SetSupportedNpadStyleTag(supported_style_tag);
119 other->SetSupportedNpadStyleTag(supported_style_tag);
120 handheld->SetSupportedNpadStyleTag(supported_style_tag);
121}
122
123NpadStyleTag HIDCore::GetSupportedStyleTag() const {
124 return supported_style_tag;
125}
126
127s8 HIDCore::GetPlayerCount() const {
128 s8 active_players = 0;
129 for (std::size_t player_index = 0; player_index < available_controllers - 2; ++player_index) {
130 const auto* const controller = GetEmulatedControllerByIndex(player_index);
131 if (controller->IsConnected()) {
132 active_players++;
133 }
134 }
135 return active_players;
136}
137
138NpadIdType HIDCore::GetFirstNpadId() const {
139 for (std::size_t player_index = 0; player_index < available_controllers; ++player_index) {
140 const auto* const controller = GetEmulatedControllerByIndex(player_index);
141 if (controller->IsConnected()) {
142 return controller->GetNpadIdType();
143 }
144 }
145 return NpadIdType::Player1;
146}
147
148NpadIdType HIDCore::GetFirstDisconnectedNpadId() const {
149 for (std::size_t player_index = 0; player_index < available_controllers; ++player_index) {
150 const auto* const controller = GetEmulatedControllerByIndex(player_index);
151 if (!controller->IsConnected()) {
152 return controller->GetNpadIdType();
153 }
154 }
155 return NpadIdType::Player1;
156}
157
158void HIDCore::EnableAllControllerConfiguration() {
159 player_1->EnableConfiguration();
160 player_2->EnableConfiguration();
161 player_3->EnableConfiguration();
162 player_4->EnableConfiguration();
163 player_5->EnableConfiguration();
164 player_6->EnableConfiguration();
165 player_7->EnableConfiguration();
166 player_8->EnableConfiguration();
167 other->EnableConfiguration();
168 handheld->EnableConfiguration();
169}
170
171void HIDCore::DisableAllControllerConfiguration() {
172 player_1->DisableConfiguration();
173 player_2->DisableConfiguration();
174 player_3->DisableConfiguration();
175 player_4->DisableConfiguration();
176 player_5->DisableConfiguration();
177 player_6->DisableConfiguration();
178 player_7->DisableConfiguration();
179 player_8->DisableConfiguration();
180 other->DisableConfiguration();
181 handheld->DisableConfiguration();
182}
183
184void HIDCore::ReloadInputDevices() {
185 player_1->ReloadFromSettings();
186 player_2->ReloadFromSettings();
187 player_3->ReloadFromSettings();
188 player_4->ReloadFromSettings();
189 player_5->ReloadFromSettings();
190 player_6->ReloadFromSettings();
191 player_7->ReloadFromSettings();
192 player_8->ReloadFromSettings();
193 other->ReloadFromSettings();
194 handheld->ReloadFromSettings();
195 console->ReloadFromSettings();
196 devices->ReloadFromSettings();
197}
198
199void HIDCore::UnloadInputDevices() {
200 player_1->UnloadInput();
201 player_2->UnloadInput();
202 player_3->UnloadInput();
203 player_4->UnloadInput();
204 player_5->UnloadInput();
205 player_6->UnloadInput();
206 player_7->UnloadInput();
207 player_8->UnloadInput();
208 other->UnloadInput();
209 handheld->UnloadInput();
210 console->UnloadInput();
211 devices->UnloadInput();
212}
213
214} // namespace Core::HID
diff --git a/src/core/hid/hid_core.h b/src/core/hid/hid_core.h
new file mode 100644
index 000000000..837f7de49
--- /dev/null
+++ b/src/core/hid/hid_core.h
@@ -0,0 +1,82 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <memory>
8
9#include "core/hid/hid_types.h"
10
11namespace Core::HID {
12class EmulatedConsole;
13class EmulatedController;
14class EmulatedDevices;
15} // namespace Core::HID
16
17namespace Core::HID {
18
19class HIDCore {
20public:
21 explicit HIDCore();
22 ~HIDCore();
23
24 YUZU_NON_COPYABLE(HIDCore);
25 YUZU_NON_MOVEABLE(HIDCore);
26
27 EmulatedController* GetEmulatedController(NpadIdType npad_id_type);
28 const EmulatedController* GetEmulatedController(NpadIdType npad_id_type) const;
29
30 EmulatedController* GetEmulatedControllerByIndex(std::size_t index);
31 const EmulatedController* GetEmulatedControllerByIndex(std::size_t index) const;
32
33 EmulatedConsole* GetEmulatedConsole();
34 const EmulatedConsole* GetEmulatedConsole() const;
35
36 EmulatedDevices* GetEmulatedDevices();
37 const EmulatedDevices* GetEmulatedDevices() const;
38
39 void SetSupportedStyleTag(NpadStyleTag style_tag);
40 NpadStyleTag GetSupportedStyleTag() const;
41
42 /// Counts the connected players from P1-P8
43 s8 GetPlayerCount() const;
44
45 /// Returns the first connected npad id
46 NpadIdType GetFirstNpadId() const;
47
48 /// Returns the first disconnected npad id
49 NpadIdType GetFirstDisconnectedNpadId() const;
50
51 /// Sets all emulated controllers into configuring mode.
52 void EnableAllControllerConfiguration();
53
54 /// Sets all emulated controllers into normal mode.
55 void DisableAllControllerConfiguration();
56
57 /// Reloads all input devices from settings
58 void ReloadInputDevices();
59
60 /// Removes all callbacks from input common
61 void UnloadInputDevices();
62
63 /// Number of emulated controllers
64 static constexpr std::size_t available_controllers{10};
65
66private:
67 std::unique_ptr<EmulatedController> player_1;
68 std::unique_ptr<EmulatedController> player_2;
69 std::unique_ptr<EmulatedController> player_3;
70 std::unique_ptr<EmulatedController> player_4;
71 std::unique_ptr<EmulatedController> player_5;
72 std::unique_ptr<EmulatedController> player_6;
73 std::unique_ptr<EmulatedController> player_7;
74 std::unique_ptr<EmulatedController> player_8;
75 std::unique_ptr<EmulatedController> other;
76 std::unique_ptr<EmulatedController> handheld;
77 std::unique_ptr<EmulatedConsole> console;
78 std::unique_ptr<EmulatedDevices> devices;
79 NpadStyleTag supported_style_tag{NpadStyleSet::All};
80};
81
82} // namespace Core::HID
diff --git a/src/core/hid/hid_types.h b/src/core/hid/hid_types.h
new file mode 100644
index 000000000..7c12f01fc
--- /dev/null
+++ b/src/core/hid/hid_types.h
@@ -0,0 +1,635 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "common/bit_field.h"
8#include "common/common_funcs.h"
9#include "common/common_types.h"
10#include "common/point.h"
11#include "common/uuid.h"
12
13namespace Core::HID {
14
15enum class DeviceIndex : u8 {
16 Left = 0,
17 Right = 1,
18 None = 2,
19 MaxDeviceIndex = 3,
20};
21
22// This is nn::hid::NpadButton
23enum class NpadButton : u64 {
24 None = 0,
25 A = 1U << 0,
26 B = 1U << 1,
27 X = 1U << 2,
28 Y = 1U << 3,
29 StickL = 1U << 4,
30 StickR = 1U << 5,
31 L = 1U << 6,
32 R = 1U << 7,
33 ZL = 1U << 8,
34 ZR = 1U << 9,
35 Plus = 1U << 10,
36 Minus = 1U << 11,
37
38 Left = 1U << 12,
39 Up = 1U << 13,
40 Right = 1U << 14,
41 Down = 1U << 15,
42
43 StickLLeft = 1U << 16,
44 StickLUp = 1U << 17,
45 StickLRight = 1U << 18,
46 StickLDown = 1U << 19,
47
48 StickRLeft = 1U << 20,
49 StickRUp = 1U << 21,
50 StickRRight = 1U << 22,
51 StickRDown = 1U << 23,
52
53 LeftSL = 1U << 24,
54 LeftSR = 1U << 25,
55
56 RightSL = 1U << 26,
57 RightSR = 1U << 27,
58
59 Palma = 1U << 28,
60 Verification = 1U << 29,
61 HandheldLeftB = 1U << 30,
62 LagonCLeft = 1U << 31,
63 LagonCUp = 1ULL << 32,
64 LagonCRight = 1ULL << 33,
65 LagonCDown = 1ULL << 34,
66
67 All = 0xFFFFFFFFFFFFFFFFULL,
68};
69DECLARE_ENUM_FLAG_OPERATORS(NpadButton);
70
71enum class KeyboardKeyIndex : u32 {
72 A = 4,
73 B = 5,
74 C = 6,
75 D = 7,
76 E = 8,
77 F = 9,
78 G = 10,
79 H = 11,
80 I = 12,
81 J = 13,
82 K = 14,
83 L = 15,
84 M = 16,
85 N = 17,
86 O = 18,
87 P = 19,
88 Q = 20,
89 R = 21,
90 S = 22,
91 T = 23,
92 U = 24,
93 V = 25,
94 W = 26,
95 X = 27,
96 Y = 28,
97 Z = 29,
98 D1 = 30,
99 D2 = 31,
100 D3 = 32,
101 D4 = 33,
102 D5 = 34,
103 D6 = 35,
104 D7 = 36,
105 D8 = 37,
106 D9 = 38,
107 D0 = 39,
108 Return = 40,
109 Escape = 41,
110 Backspace = 42,
111 Tab = 43,
112 Space = 44,
113 Minus = 45,
114 Plus = 46,
115 OpenBracket = 47,
116 CloseBracket = 48,
117 Pipe = 49,
118 Tilde = 50,
119 Semicolon = 51,
120 Quote = 52,
121 Backquote = 53,
122 Comma = 54,
123 Period = 55,
124 Slash = 56,
125 CapsLock = 57,
126 F1 = 58,
127 F2 = 59,
128 F3 = 60,
129 F4 = 61,
130 F5 = 62,
131 F6 = 63,
132 F7 = 64,
133 F8 = 65,
134 F9 = 66,
135 F10 = 67,
136 F11 = 68,
137 F12 = 69,
138 PrintScreen = 70,
139 ScrollLock = 71,
140 Pause = 72,
141 Insert = 73,
142 Home = 74,
143 PageUp = 75,
144 Delete = 76,
145 End = 77,
146 PageDown = 78,
147 RightArrow = 79,
148 LeftArrow = 80,
149 DownArrow = 81,
150 UpArrow = 82,
151 NumLock = 83,
152 NumPadDivide = 84,
153 NumPadMultiply = 85,
154 NumPadSubtract = 86,
155 NumPadAdd = 87,
156 NumPadEnter = 88,
157 NumPad1 = 89,
158 NumPad2 = 90,
159 NumPad3 = 91,
160 NumPad4 = 92,
161 NumPad5 = 93,
162 NumPad6 = 94,
163 NumPad7 = 95,
164 NumPad8 = 96,
165 NumPad9 = 97,
166 NumPad0 = 98,
167 NumPadDot = 99,
168 Backslash = 100,
169 Application = 101,
170 Power = 102,
171 NumPadEquals = 103,
172 F13 = 104,
173 F14 = 105,
174 F15 = 106,
175 F16 = 107,
176 F17 = 108,
177 F18 = 109,
178 F19 = 110,
179 F20 = 111,
180 F21 = 112,
181 F22 = 113,
182 F23 = 114,
183 F24 = 115,
184 NumPadComma = 133,
185 Ro = 135,
186 KatakanaHiragana = 136,
187 Yen = 137,
188 Henkan = 138,
189 Muhenkan = 139,
190 NumPadCommaPc98 = 140,
191 HangulEnglish = 144,
192 Hanja = 145,
193 Katakana = 146,
194 Hiragana = 147,
195 ZenkakuHankaku = 148,
196 LeftControl = 224,
197 LeftShift = 225,
198 LeftAlt = 226,
199 LeftGui = 227,
200 RightControl = 228,
201 RightShift = 229,
202 RightAlt = 230,
203 RightGui = 231,
204};
205
206// This is nn::hid::NpadIdType
207enum class NpadIdType : u32 {
208 Player1 = 0x0,
209 Player2 = 0x1,
210 Player3 = 0x2,
211 Player4 = 0x3,
212 Player5 = 0x4,
213 Player6 = 0x5,
214 Player7 = 0x6,
215 Player8 = 0x7,
216 Other = 0x10,
217 Handheld = 0x20,
218
219 Invalid = 0xFFFFFFFF,
220};
221
222// This is nn::hid::NpadStyleIndex
223enum class NpadStyleIndex : u8 {
224 None = 0,
225 ProController = 3,
226 Handheld = 4,
227 HandheldNES = 4,
228 JoyconDual = 5,
229 JoyconLeft = 6,
230 JoyconRight = 7,
231 GameCube = 8,
232 Pokeball = 9,
233 NES = 10,
234 SNES = 12,
235 N64 = 13,
236 SegaGenesis = 14,
237 SystemExt = 32,
238 System = 33,
239 MaxNpadType = 34,
240};
241
242// This is nn::hid::NpadStyleSet
243enum class NpadStyleSet : u32 {
244 None = 0,
245 Fullkey = 1U << 0,
246 Handheld = 1U << 1,
247 JoyDual = 1U << 2,
248 JoyLeft = 1U << 3,
249 JoyRight = 1U << 4,
250 Gc = 1U << 5,
251 Palma = 1U << 6,
252 Lark = 1U << 7,
253 HandheldLark = 1U << 8,
254 Lucia = 1U << 9,
255 Lagoon = 1U << 10,
256 Lager = 1U << 11,
257 SystemExt = 1U << 29,
258 System = 1U << 30,
259
260 All = 0xFFFFFFFFU,
261};
262static_assert(sizeof(NpadStyleSet) == 4, "NpadStyleSet is an invalid size");
263
264// This is nn::hid::VibrationDevicePosition
265enum class VibrationDevicePosition : u32 {
266 None = 0,
267 Left = 1,
268 Right = 2,
269};
270
271// This is nn::hid::VibrationDeviceType
272enum class VibrationDeviceType : u32 {
273 Unknown = 0,
274 LinearResonantActuator = 1,
275 GcErm = 2,
276};
277
278// This is nn::hid::VibrationGcErmCommand
279enum class VibrationGcErmCommand : u64 {
280 Stop = 0,
281 Start = 1,
282 StopHard = 2,
283};
284
285// This is nn::hid::NpadStyleTag
286struct NpadStyleTag {
287 union {
288 NpadStyleSet raw{};
289
290 BitField<0, 1, u32> fullkey;
291 BitField<1, 1, u32> handheld;
292 BitField<2, 1, u32> joycon_dual;
293 BitField<3, 1, u32> joycon_left;
294 BitField<4, 1, u32> joycon_right;
295 BitField<5, 1, u32> gamecube;
296 BitField<6, 1, u32> palma;
297 BitField<7, 1, u32> lark;
298 BitField<8, 1, u32> handheld_lark;
299 BitField<9, 1, u32> lucia;
300 BitField<10, 1, u32> lagoon;
301 BitField<11, 1, u32> lager;
302 BitField<29, 1, u32> system_ext;
303 BitField<30, 1, u32> system;
304 };
305};
306static_assert(sizeof(NpadStyleTag) == 4, "NpadStyleTag is an invalid size");
307
308// This is nn::hid::TouchAttribute
309struct TouchAttribute {
310 union {
311 u32 raw{};
312 BitField<0, 1, u32> start_touch;
313 BitField<1, 1, u32> end_touch;
314 };
315};
316static_assert(sizeof(TouchAttribute) == 0x4, "TouchAttribute is an invalid size");
317
318// This is nn::hid::TouchState
319struct TouchState {
320 u64 delta_time;
321 TouchAttribute attribute;
322 u32 finger;
323 Common::Point<u32> position;
324 u32 diameter_x;
325 u32 diameter_y;
326 u32 rotation_angle;
327};
328static_assert(sizeof(TouchState) == 0x28, "Touchstate is an invalid size");
329
330// This is nn::hid::NpadControllerColor
331struct NpadControllerColor {
332 u32 body;
333 u32 button;
334};
335static_assert(sizeof(NpadControllerColor) == 8, "NpadControllerColor is an invalid size");
336
337// This is nn::hid::AnalogStickState
338struct AnalogStickState {
339 s32 x;
340 s32 y;
341};
342static_assert(sizeof(AnalogStickState) == 8, "AnalogStickState is an invalid size");
343
344// This is nn::hid::server::NpadGcTriggerState
345struct NpadGcTriggerState {
346 s64 sampling_number{};
347 s32 left{};
348 s32 right{};
349};
350static_assert(sizeof(NpadGcTriggerState) == 0x10, "NpadGcTriggerState is an invalid size");
351
352// This is nn::hid::system::NpadBatteryLevel
353using NpadBatteryLevel = u32;
354static_assert(sizeof(NpadBatteryLevel) == 0x4, "NpadBatteryLevel is an invalid size");
355
356// This is nn::hid::system::NpadPowerInfo
357struct NpadPowerInfo {
358 bool is_powered;
359 bool is_charging;
360 INSERT_PADDING_BYTES(0x6);
361 NpadBatteryLevel battery_level;
362};
363static_assert(sizeof(NpadPowerInfo) == 0xC, "NpadPowerInfo is an invalid size");
364
365struct LedPattern {
366 explicit LedPattern(u64 light1, u64 light2, u64 light3, u64 light4) {
367 position1.Assign(light1);
368 position2.Assign(light2);
369 position3.Assign(light3);
370 position4.Assign(light4);
371 }
372 union {
373 u64 raw{};
374 BitField<0, 1, u64> position1;
375 BitField<1, 1, u64> position2;
376 BitField<2, 1, u64> position3;
377 BitField<3, 1, u64> position4;
378 };
379};
380
381struct NpadButtonState {
382 union {
383 NpadButton raw{};
384
385 // Buttons
386 BitField<0, 1, u64> a;
387 BitField<1, 1, u64> b;
388 BitField<2, 1, u64> x;
389 BitField<3, 1, u64> y;
390 BitField<4, 1, u64> stick_l;
391 BitField<5, 1, u64> stick_r;
392 BitField<6, 1, u64> l;
393 BitField<7, 1, u64> r;
394 BitField<8, 1, u64> zl;
395 BitField<9, 1, u64> zr;
396 BitField<10, 1, u64> plus;
397 BitField<11, 1, u64> minus;
398
399 // D-Pad
400 BitField<12, 1, u64> left;
401 BitField<13, 1, u64> up;
402 BitField<14, 1, u64> right;
403 BitField<15, 1, u64> down;
404
405 // Left JoyStick
406 BitField<16, 1, u64> stick_l_left;
407 BitField<17, 1, u64> stick_l_up;
408 BitField<18, 1, u64> stick_l_right;
409 BitField<19, 1, u64> stick_l_down;
410
411 // Right JoyStick
412 BitField<20, 1, u64> stick_r_left;
413 BitField<21, 1, u64> stick_r_up;
414 BitField<22, 1, u64> stick_r_right;
415 BitField<23, 1, u64> stick_r_down;
416
417 BitField<24, 1, u64> left_sl;
418 BitField<25, 1, u64> left_sr;
419
420 BitField<26, 1, u64> right_sl;
421 BitField<27, 1, u64> right_sr;
422
423 BitField<28, 1, u64> palma;
424 BitField<29, 1, u64> verification;
425 BitField<30, 1, u64> handheld_left_b;
426 BitField<31, 1, u64> lagon_c_left;
427 BitField<32, 1, u64> lagon_c_up;
428 BitField<33, 1, u64> lagon_c_right;
429 BitField<34, 1, u64> lagon_c_down;
430 };
431};
432static_assert(sizeof(NpadButtonState) == 0x8, "NpadButtonState has incorrect size.");
433
434// This is nn::hid::DebugPadButton
435struct DebugPadButton {
436 union {
437 u32 raw{};
438 BitField<0, 1, u32> a;
439 BitField<1, 1, u32> b;
440 BitField<2, 1, u32> x;
441 BitField<3, 1, u32> y;
442 BitField<4, 1, u32> l;
443 BitField<5, 1, u32> r;
444 BitField<6, 1, u32> zl;
445 BitField<7, 1, u32> zr;
446 BitField<8, 1, u32> plus;
447 BitField<9, 1, u32> minus;
448 BitField<10, 1, u32> d_left;
449 BitField<11, 1, u32> d_up;
450 BitField<12, 1, u32> d_right;
451 BitField<13, 1, u32> d_down;
452 };
453};
454static_assert(sizeof(DebugPadButton) == 0x4, "DebugPadButton is an invalid size");
455
456// This is nn::hid::ConsoleSixAxisSensorHandle
457struct ConsoleSixAxisSensorHandle {
458 u8 unknown_1;
459 u8 unknown_2;
460 INSERT_PADDING_BYTES_NOINIT(2);
461};
462static_assert(sizeof(ConsoleSixAxisSensorHandle) == 4,
463 "ConsoleSixAxisSensorHandle is an invalid size");
464
465// This is nn::hid::SixAxisSensorHandle
466struct SixAxisSensorHandle {
467 NpadStyleIndex npad_type;
468 u8 npad_id;
469 DeviceIndex device_index;
470 INSERT_PADDING_BYTES_NOINIT(1);
471};
472static_assert(sizeof(SixAxisSensorHandle) == 4, "SixAxisSensorHandle is an invalid size");
473
474struct SixAxisSensorFusionParameters {
475 f32 parameter1;
476 f32 parameter2;
477};
478static_assert(sizeof(SixAxisSensorFusionParameters) == 8,
479 "SixAxisSensorFusionParameters is an invalid size");
480
481// This is nn::hid::VibrationDeviceHandle
482struct VibrationDeviceHandle {
483 NpadStyleIndex npad_type;
484 u8 npad_id;
485 DeviceIndex device_index;
486 INSERT_PADDING_BYTES_NOINIT(1);
487};
488static_assert(sizeof(VibrationDeviceHandle) == 4, "SixAxisSensorHandle is an invalid size");
489
490// This is nn::hid::VibrationValue
491struct VibrationValue {
492 f32 low_amplitude;
493 f32 low_frequency;
494 f32 high_amplitude;
495 f32 high_frequency;
496};
497static_assert(sizeof(VibrationValue) == 0x10, "VibrationValue has incorrect size.");
498
499// This is nn::hid::VibrationDeviceInfo
500struct VibrationDeviceInfo {
501 VibrationDeviceType type{};
502 VibrationDevicePosition position{};
503};
504static_assert(sizeof(VibrationDeviceInfo) == 0x8, "VibrationDeviceInfo has incorrect size.");
505
506// This is nn::hid::KeyboardModifier
507struct KeyboardModifier {
508 union {
509 u32 raw{};
510 BitField<0, 1, u32> control;
511 BitField<1, 1, u32> shift;
512 BitField<2, 1, u32> left_alt;
513 BitField<3, 1, u32> right_alt;
514 BitField<4, 1, u32> gui;
515 BitField<8, 1, u32> caps_lock;
516 BitField<9, 1, u32> scroll_lock;
517 BitField<10, 1, u32> num_lock;
518 BitField<11, 1, u32> katakana;
519 BitField<12, 1, u32> hiragana;
520 };
521};
522
523static_assert(sizeof(KeyboardModifier) == 0x4, "KeyboardModifier is an invalid size");
524
525// This is nn::hid::KeyboardAttribute
526struct KeyboardAttribute {
527 union {
528 u32 raw{};
529 BitField<0, 1, u32> is_connected;
530 };
531};
532static_assert(sizeof(KeyboardAttribute) == 0x4, "KeyboardAttribute is an invalid size");
533
534// This is nn::hid::KeyboardKey
535struct KeyboardKey {
536 // This should be a 256 bit flag
537 std::array<u8, 32> key;
538};
539static_assert(sizeof(KeyboardKey) == 0x20, "KeyboardKey is an invalid size");
540
541// This is nn::hid::MouseButton
542struct MouseButton {
543 union {
544 u32_le raw{};
545 BitField<0, 1, u32> left;
546 BitField<1, 1, u32> right;
547 BitField<2, 1, u32> middle;
548 BitField<3, 1, u32> forward;
549 BitField<4, 1, u32> back;
550 };
551};
552static_assert(sizeof(MouseButton) == 0x4, "MouseButton is an invalid size");
553
554// This is nn::hid::MouseAttribute
555struct MouseAttribute {
556 union {
557 u32 raw{};
558 BitField<0, 1, u32> transferable;
559 BitField<1, 1, u32> is_connected;
560 };
561};
562static_assert(sizeof(MouseAttribute) == 0x4, "MouseAttribute is an invalid size");
563
564// This is nn::hid::detail::MouseState
565struct MouseState {
566 s64 sampling_number;
567 s32 x;
568 s32 y;
569 s32 delta_x;
570 s32 delta_y;
571 // Axis Order in HW is switched for the wheel
572 s32 delta_wheel_y;
573 s32 delta_wheel_x;
574 MouseButton button;
575 MouseAttribute attribute;
576};
577static_assert(sizeof(MouseState) == 0x28, "MouseState is an invalid size");
578
579/// Converts a NpadIdType to an array index.
580constexpr size_t NpadIdTypeToIndex(NpadIdType npad_id_type) {
581 switch (npad_id_type) {
582 case NpadIdType::Player1:
583 return 0;
584 case NpadIdType::Player2:
585 return 1;
586 case NpadIdType::Player3:
587 return 2;
588 case NpadIdType::Player4:
589 return 3;
590 case NpadIdType::Player5:
591 return 4;
592 case NpadIdType::Player6:
593 return 5;
594 case NpadIdType::Player7:
595 return 6;
596 case NpadIdType::Player8:
597 return 7;
598 case NpadIdType::Handheld:
599 return 8;
600 case NpadIdType::Other:
601 return 9;
602 default:
603 return 0;
604 }
605}
606
607/// Converts an array index to a NpadIdType
608constexpr NpadIdType IndexToNpadIdType(size_t index) {
609 switch (index) {
610 case 0:
611 return NpadIdType::Player1;
612 case 1:
613 return NpadIdType::Player2;
614 case 2:
615 return NpadIdType::Player3;
616 case 3:
617 return NpadIdType::Player4;
618 case 4:
619 return NpadIdType::Player5;
620 case 5:
621 return NpadIdType::Player6;
622 case 6:
623 return NpadIdType::Player7;
624 case 7:
625 return NpadIdType::Player8;
626 case 8:
627 return NpadIdType::Handheld;
628 case 9:
629 return NpadIdType::Other;
630 default:
631 return NpadIdType::Invalid;
632 }
633}
634
635} // namespace Core::HID
diff --git a/src/core/hid/input_converter.cpp b/src/core/hid/input_converter.cpp
new file mode 100644
index 000000000..f5acff6e0
--- /dev/null
+++ b/src/core/hid/input_converter.cpp
@@ -0,0 +1,383 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included
4
5#include <random>
6
7#include "common/input.h"
8#include "core/hid/input_converter.h"
9
10namespace Core::HID {
11
12Common::Input::BatteryStatus TransformToBattery(const Common::Input::CallbackStatus& callback) {
13 Common::Input::BatteryStatus battery{Common::Input::BatteryStatus::None};
14 switch (callback.type) {
15 case Common::Input::InputType::Analog:
16 case Common::Input::InputType::Trigger: {
17 const auto value = TransformToTrigger(callback).analog.value;
18 battery = Common::Input::BatteryLevel::Empty;
19 if (value > 0.2f) {
20 battery = Common::Input::BatteryLevel::Critical;
21 }
22 if (value > 0.4f) {
23 battery = Common::Input::BatteryLevel::Low;
24 }
25 if (value > 0.6f) {
26 battery = Common::Input::BatteryLevel::Medium;
27 }
28 if (value > 0.8f) {
29 battery = Common::Input::BatteryLevel::Full;
30 }
31 if (value >= 1.0f) {
32 battery = Common::Input::BatteryLevel::Charging;
33 }
34 break;
35 }
36 case Common::Input::InputType::Button:
37 battery = callback.button_status.value ? Common::Input::BatteryLevel::Charging
38 : Common::Input::BatteryLevel::Critical;
39 break;
40 case Common::Input::InputType::Battery:
41 battery = callback.battery_status;
42 break;
43 default:
44 LOG_ERROR(Input, "Conversion from type {} to battery not implemented", callback.type);
45 break;
46 }
47
48 return battery;
49}
50
51Common::Input::ButtonStatus TransformToButton(const Common::Input::CallbackStatus& callback) {
52 Common::Input::ButtonStatus status{};
53 switch (callback.type) {
54 case Common::Input::InputType::Analog:
55 case Common::Input::InputType::Trigger:
56 status.value = TransformToTrigger(callback).pressed.value;
57 break;
58 case Common::Input::InputType::Button:
59 status = callback.button_status;
60 break;
61 default:
62 LOG_ERROR(Input, "Conversion from type {} to button not implemented", callback.type);
63 break;
64 }
65
66 if (status.inverted) {
67 status.value = !status.value;
68 }
69
70 return status;
71}
72
73Common::Input::MotionStatus TransformToMotion(const Common::Input::CallbackStatus& callback) {
74 Common::Input::MotionStatus status{};
75 switch (callback.type) {
76 case Common::Input::InputType::Button: {
77 Common::Input::AnalogProperties properties{
78 .deadzone = 0.0f,
79 .range = 1.0f,
80 .offset = 0.0f,
81 };
82 status.delta_timestamp = 5000;
83 status.force_update = true;
84 status.accel.x = {
85 .value = 0.0f,
86 .raw_value = 0.0f,
87 .properties = properties,
88 };
89 status.accel.y = {
90 .value = 0.0f,
91 .raw_value = 0.0f,
92 .properties = properties,
93 };
94 status.accel.z = {
95 .value = 0.0f,
96 .raw_value = -1.0f,
97 .properties = properties,
98 };
99 status.gyro.x = {
100 .value = 0.0f,
101 .raw_value = 0.0f,
102 .properties = properties,
103 };
104 status.gyro.y = {
105 .value = 0.0f,
106 .raw_value = 0.0f,
107 .properties = properties,
108 };
109 status.gyro.z = {
110 .value = 0.0f,
111 .raw_value = 0.0f,
112 .properties = properties,
113 };
114 if (TransformToButton(callback).value) {
115 std::random_device device;
116 std::mt19937 gen(device());
117 std::uniform_int_distribution<s16> distribution(-1000, 1000);
118 status.accel.x.raw_value = static_cast<f32>(distribution(gen)) * 0.001f;
119 status.accel.y.raw_value = static_cast<f32>(distribution(gen)) * 0.001f;
120 status.accel.z.raw_value = static_cast<f32>(distribution(gen)) * 0.001f;
121 status.gyro.x.raw_value = static_cast<f32>(distribution(gen)) * 0.001f;
122 status.gyro.y.raw_value = static_cast<f32>(distribution(gen)) * 0.001f;
123 status.gyro.z.raw_value = static_cast<f32>(distribution(gen)) * 0.001f;
124 }
125 break;
126 }
127 case Common::Input::InputType::Motion:
128 status = callback.motion_status;
129 break;
130 default:
131 LOG_ERROR(Input, "Conversion from type {} to motion not implemented", callback.type);
132 break;
133 }
134 SanitizeAnalog(status.accel.x, false);
135 SanitizeAnalog(status.accel.y, false);
136 SanitizeAnalog(status.accel.z, false);
137 SanitizeAnalog(status.gyro.x, false);
138 SanitizeAnalog(status.gyro.y, false);
139 SanitizeAnalog(status.gyro.z, false);
140
141 return status;
142}
143
144Common::Input::StickStatus TransformToStick(const Common::Input::CallbackStatus& callback) {
145 Common::Input::StickStatus status{};
146
147 switch (callback.type) {
148 case Common::Input::InputType::Stick:
149 status = callback.stick_status;
150 break;
151 default:
152 LOG_ERROR(Input, "Conversion from type {} to stick not implemented", callback.type);
153 break;
154 }
155
156 SanitizeStick(status.x, status.y, true);
157 const auto& properties_x = status.x.properties;
158 const auto& properties_y = status.y.properties;
159 const float x = status.x.value;
160 const float y = status.y.value;
161
162 // Set directional buttons
163 status.right = x > properties_x.threshold;
164 status.left = x < -properties_x.threshold;
165 status.up = y > properties_y.threshold;
166 status.down = y < -properties_y.threshold;
167
168 return status;
169}
170
171Common::Input::TouchStatus TransformToTouch(const Common::Input::CallbackStatus& callback) {
172 Common::Input::TouchStatus status{};
173
174 switch (callback.type) {
175 case Common::Input::InputType::Touch:
176 status = callback.touch_status;
177 break;
178 case Common::Input::InputType::Stick:
179 status.x = callback.stick_status.x;
180 status.y = callback.stick_status.y;
181 break;
182 default:
183 LOG_ERROR(Input, "Conversion from type {} to touch not implemented", callback.type);
184 break;
185 }
186
187 SanitizeAnalog(status.x, true);
188 SanitizeAnalog(status.y, true);
189 float& x = status.x.value;
190 float& y = status.y.value;
191
192 // Adjust if value is inverted
193 x = status.x.properties.inverted ? 1.0f + x : x;
194 y = status.y.properties.inverted ? 1.0f + y : y;
195
196 // clamp value
197 x = std::clamp(x, 0.0f, 1.0f);
198 y = std::clamp(y, 0.0f, 1.0f);
199
200 if (status.pressed.inverted) {
201 status.pressed.value = !status.pressed.value;
202 }
203
204 return status;
205}
206
207Common::Input::TriggerStatus TransformToTrigger(const Common::Input::CallbackStatus& callback) {
208 Common::Input::TriggerStatus status{};
209 float& raw_value = status.analog.raw_value;
210 bool calculate_button_value = true;
211
212 switch (callback.type) {
213 case Common::Input::InputType::Analog:
214 status.analog.properties = callback.analog_status.properties;
215 raw_value = callback.analog_status.raw_value;
216 break;
217 case Common::Input::InputType::Button:
218 status.analog.properties.range = 1.0f;
219 status.analog.properties.inverted = callback.button_status.inverted;
220 raw_value = callback.button_status.value ? 1.0f : 0.0f;
221 break;
222 case Common::Input::InputType::Trigger:
223 status = callback.trigger_status;
224 calculate_button_value = false;
225 break;
226 default:
227 LOG_ERROR(Input, "Conversion from type {} to trigger not implemented", callback.type);
228 break;
229 }
230
231 SanitizeAnalog(status.analog, true);
232 const auto& properties = status.analog.properties;
233 float& value = status.analog.value;
234
235 // Set button status
236 if (calculate_button_value) {
237 status.pressed.value = value > properties.threshold;
238 }
239
240 // Adjust if value is inverted
241 value = properties.inverted ? 1.0f + value : value;
242
243 // clamp value
244 value = std::clamp(value, 0.0f, 1.0f);
245
246 return status;
247}
248
249Common::Input::AnalogStatus TransformToAnalog(const Common::Input::CallbackStatus& callback) {
250 Common::Input::AnalogStatus status{};
251
252 switch (callback.type) {
253 case Common::Input::InputType::Analog:
254 status.properties = callback.analog_status.properties;
255 status.raw_value = callback.analog_status.raw_value;
256 break;
257 default:
258 LOG_ERROR(Input, "Conversion from type {} to analog not implemented", callback.type);
259 break;
260 }
261
262 SanitizeAnalog(status, false);
263
264 // Adjust if value is inverted
265 status.value = status.properties.inverted ? -status.value : status.value;
266
267 return status;
268}
269
270void SanitizeAnalog(Common::Input::AnalogStatus& analog, bool clamp_value) {
271 const auto& properties = analog.properties;
272 float& raw_value = analog.raw_value;
273 float& value = analog.value;
274
275 if (!std::isnormal(raw_value)) {
276 raw_value = 0;
277 }
278
279 // Apply center offset
280 raw_value -= properties.offset;
281
282 // Set initial values to be formated
283 value = raw_value;
284
285 // Calculate vector size
286 const float r = std::abs(value);
287
288 // Return zero if value is smaller than the deadzone
289 if (r <= properties.deadzone || properties.deadzone == 1.0f) {
290 analog.value = 0;
291 return;
292 }
293
294 // Adjust range of value
295 const float deadzone_factor =
296 1.0f / r * (r - properties.deadzone) / (1.0f - properties.deadzone);
297 value = value * deadzone_factor / properties.range;
298
299 // Invert direction if needed
300 if (properties.inverted) {
301 value = -value;
302 }
303
304 // Clamp value
305 if (clamp_value) {
306 value = std::clamp(value, -1.0f, 1.0f);
307 }
308}
309
310void SanitizeStick(Common::Input::AnalogStatus& analog_x, Common::Input::AnalogStatus& analog_y,
311 bool clamp_value) {
312 const auto& properties_x = analog_x.properties;
313 const auto& properties_y = analog_y.properties;
314 float& raw_x = analog_x.raw_value;
315 float& raw_y = analog_y.raw_value;
316 float& x = analog_x.value;
317 float& y = analog_y.value;
318
319 if (!std::isnormal(raw_x)) {
320 raw_x = 0;
321 }
322 if (!std::isnormal(raw_y)) {
323 raw_y = 0;
324 }
325
326 // Apply center offset
327 raw_x += properties_x.offset;
328 raw_y += properties_y.offset;
329
330 // Apply X scale correction from offset
331 if (std::abs(properties_x.offset) < 0.5f) {
332 if (raw_x > 0) {
333 raw_x /= 1 + properties_x.offset;
334 } else {
335 raw_x /= 1 - properties_x.offset;
336 }
337 }
338
339 // Apply Y scale correction from offset
340 if (std::abs(properties_y.offset) < 0.5f) {
341 if (raw_y > 0) {
342 raw_y /= 1 + properties_y.offset;
343 } else {
344 raw_y /= 1 - properties_y.offset;
345 }
346 }
347
348 // Invert direction if needed
349 raw_x = properties_x.inverted ? -raw_x : raw_x;
350 raw_y = properties_y.inverted ? -raw_y : raw_y;
351
352 // Set initial values to be formated
353 x = raw_x;
354 y = raw_y;
355
356 // Calculate vector size
357 float r = x * x + y * y;
358 r = std::sqrt(r);
359
360 // TODO(German77): Use deadzone and range of both axis
361
362 // Return zero if values are smaller than the deadzone
363 if (r <= properties_x.deadzone || properties_x.deadzone >= 1.0f) {
364 x = 0;
365 y = 0;
366 return;
367 }
368
369 // Adjust range of joystick
370 const float deadzone_factor =
371 1.0f / r * (r - properties_x.deadzone) / (1.0f - properties_x.deadzone);
372 x = x * deadzone_factor / properties_x.range;
373 y = y * deadzone_factor / properties_x.range;
374 r = r * deadzone_factor / properties_x.range;
375
376 // Normalize joystick
377 if (clamp_value && r > 1.0f) {
378 x /= r;
379 y /= r;
380 }
381}
382
383} // namespace Core::HID
diff --git a/src/core/hid/input_converter.h b/src/core/hid/input_converter.h
new file mode 100644
index 000000000..d24582226
--- /dev/null
+++ b/src/core/hid/input_converter.h
@@ -0,0 +1,96 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included
4
5#pragma once
6
7namespace Common::Input {
8struct CallbackStatus;
9enum class BatteryLevel : u32;
10using BatteryStatus = BatteryLevel;
11struct AnalogStatus;
12struct ButtonStatus;
13struct MotionStatus;
14struct StickStatus;
15struct TouchStatus;
16struct TriggerStatus;
17}; // namespace Common::Input
18
19namespace Core::HID {
20
21/**
22 * Converts raw input data into a valid battery status.
23 *
24 * @param callback Supported callbacks: Analog, Battery, Trigger.
25 * @return A valid BatteryStatus object.
26 */
27Common::Input::BatteryStatus TransformToBattery(const Common::Input::CallbackStatus& callback);
28
29/**
30 * Converts raw input data into a valid button status. Applies invert properties to the output.
31 *
32 * @param callback Supported callbacks: Analog, Button, Trigger.
33 * @return A valid TouchStatus object.
34 */
35Common::Input::ButtonStatus TransformToButton(const Common::Input::CallbackStatus& callback);
36
37/**
38 * Converts raw input data into a valid motion status.
39 *
40 * @param callback Supported callbacks: Motion.
41 * @return A valid TouchStatus object.
42 */
43Common::Input::MotionStatus TransformToMotion(const Common::Input::CallbackStatus& callback);
44
45/**
46 * Converts raw input data into a valid stick status. Applies offset, deadzone, range and invert
47 * properties to the output.
48 *
49 * @param callback Supported callbacks: Stick.
50 * @return A valid StickStatus object.
51 */
52Common::Input::StickStatus TransformToStick(const Common::Input::CallbackStatus& callback);
53
54/**
55 * Converts raw input data into a valid touch status.
56 *
57 * @param callback Supported callbacks: Touch.
58 * @return A valid TouchStatus object.
59 */
60Common::Input::TouchStatus TransformToTouch(const Common::Input::CallbackStatus& callback);
61
62/**
63 * Converts raw input data into a valid trigger status. Applies offset, deadzone, range and
64 * invert properties to the output. Button status uses the threshold property if necessary.
65 *
66 * @param callback Supported callbacks: Analog, Button, Trigger.
67 * @return A valid TriggerStatus object.
68 */
69Common::Input::TriggerStatus TransformToTrigger(const Common::Input::CallbackStatus& callback);
70
71/**
72 * Converts raw input data into a valid analog status. Applies offset, deadzone, range and
73 * invert properties to the output.
74 *
75 * @param callback Supported callbacks: Analog.
76 * @return A valid AnalogStatus object.
77 */
78Common::Input::AnalogStatus TransformToAnalog(const Common::Input::CallbackStatus& callback);
79
80/**
81 * Converts raw analog data into a valid analog value
82 * @param analog An analog object containing raw data and properties
83 * @param clamp_value determines if the value needs to be clamped between -1.0f and 1.0f.
84 */
85void SanitizeAnalog(Common::Input::AnalogStatus& analog, bool clamp_value);
86
87/**
88 * Converts raw stick data into a valid stick value
89 * @param analog_x raw analog data and properties for the x-axis
90 * @param analog_y raw analog data and properties for the y-axis
91 * @param clamp_value bool that determines if the value needs to be clamped into the unit circle.
92 */
93void SanitizeStick(Common::Input::AnalogStatus& analog_x, Common::Input::AnalogStatus& analog_y,
94 bool clamp_value);
95
96} // namespace Core::HID
diff --git a/src/core/frontend/input_interpreter.cpp b/src/core/hid/input_interpreter.cpp
index 9f6a90e8f..2dbda8814 100644
--- a/src/core/frontend/input_interpreter.cpp
+++ b/src/core/hid/input_interpreter.cpp
@@ -3,7 +3,8 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "core/core.h" 5#include "core/core.h"
6#include "core/frontend/input_interpreter.h" 6#include "core/hid/hid_types.h"
7#include "core/hid/input_interpreter.h"
7#include "core/hle/service/hid/controllers/npad.h" 8#include "core/hle/service/hid/controllers/npad.h"
8#include "core/hle/service/hid/hid.h" 9#include "core/hle/service/hid/hid.h"
9#include "core/hle/service/sm/sm.h" 10#include "core/hle/service/sm/sm.h"
@@ -19,7 +20,7 @@ InputInterpreter::InputInterpreter(Core::System& system)
19InputInterpreter::~InputInterpreter() = default; 20InputInterpreter::~InputInterpreter() = default;
20 21
21void InputInterpreter::PollInput() { 22void InputInterpreter::PollInput() {
22 const u32 button_state = npad.GetAndResetPressState(); 23 const auto button_state = npad.GetAndResetPressState();
23 24
24 previous_index = current_index; 25 previous_index = current_index;
25 current_index = (current_index + 1) % button_states.size(); 26 current_index = (current_index + 1) % button_states.size();
@@ -31,32 +32,30 @@ void InputInterpreter::ResetButtonStates() {
31 previous_index = 0; 32 previous_index = 0;
32 current_index = 0; 33 current_index = 0;
33 34
34 button_states[0] = 0xFFFFFFFF; 35 button_states[0] = Core::HID::NpadButton::All;
35 36
36 for (std::size_t i = 1; i < button_states.size(); ++i) { 37 for (std::size_t i = 1; i < button_states.size(); ++i) {
37 button_states[i] = 0; 38 button_states[i] = Core::HID::NpadButton::None;
38 } 39 }
39} 40}
40 41
41bool InputInterpreter::IsButtonPressed(HIDButton button) const { 42bool InputInterpreter::IsButtonPressed(Core::HID::NpadButton button) const {
42 return (button_states[current_index] & (1U << static_cast<u8>(button))) != 0; 43 return True(button_states[current_index] & button);
43} 44}
44 45
45bool InputInterpreter::IsButtonPressedOnce(HIDButton button) const { 46bool InputInterpreter::IsButtonPressedOnce(Core::HID::NpadButton button) const {
46 const bool current_press = 47 const bool current_press = True(button_states[current_index] & button);
47 (button_states[current_index] & (1U << static_cast<u8>(button))) != 0; 48 const bool previous_press = True(button_states[previous_index] & button);
48 const bool previous_press =
49 (button_states[previous_index] & (1U << static_cast<u8>(button))) != 0;
50 49
51 return current_press && !previous_press; 50 return current_press && !previous_press;
52} 51}
53 52
54bool InputInterpreter::IsButtonHeld(HIDButton button) const { 53bool InputInterpreter::IsButtonHeld(Core::HID::NpadButton button) const {
55 u32 held_buttons{button_states[0]}; 54 Core::HID::NpadButton held_buttons{button_states[0]};
56 55
57 for (std::size_t i = 1; i < button_states.size(); ++i) { 56 for (std::size_t i = 1; i < button_states.size(); ++i) {
58 held_buttons &= button_states[i]; 57 held_buttons &= button_states[i];
59 } 58 }
60 59
61 return (held_buttons & (1U << static_cast<u8>(button))) != 0; 60 return True(held_buttons & button);
62} 61}
diff --git a/src/core/frontend/input_interpreter.h b/src/core/hid/input_interpreter.h
index 9495e3daf..70c34d474 100644
--- a/src/core/frontend/input_interpreter.h
+++ b/src/core/hid/input_interpreter.h
@@ -12,46 +12,14 @@ namespace Core {
12class System; 12class System;
13} 13}
14 14
15namespace Core::HID {
16enum class NpadButton : u64;
17}
18
15namespace Service::HID { 19namespace Service::HID {
16class Controller_NPad; 20class Controller_NPad;
17} 21}
18 22
19enum class HIDButton : u8 {
20 A,
21 B,
22 X,
23 Y,
24 LStick,
25 RStick,
26 L,
27 R,
28 ZL,
29 ZR,
30 Plus,
31 Minus,
32
33 DLeft,
34 DUp,
35 DRight,
36 DDown,
37
38 LStickLeft,
39 LStickUp,
40 LStickRight,
41 LStickDown,
42
43 RStickLeft,
44 RStickUp,
45 RStickRight,
46 RStickDown,
47
48 LeftSL,
49 LeftSR,
50
51 RightSL,
52 RightSR,
53};
54
55/** 23/**
56 * The InputInterpreter class interfaces with HID to retrieve button press states. 24 * The InputInterpreter class interfaces with HID to retrieve button press states.
57 * Input is intended to be polled every 50ms so that a button is considered to be 25 * Input is intended to be polled every 50ms so that a button is considered to be
@@ -76,7 +44,7 @@ public:
76 * 44 *
77 * @returns True when the button is pressed. 45 * @returns True when the button is pressed.
78 */ 46 */
79 [[nodiscard]] bool IsButtonPressed(HIDButton button) const; 47 [[nodiscard]] bool IsButtonPressed(Core::HID::NpadButton button) const;
80 48
81 /** 49 /**
82 * Checks whether any of the buttons in the parameter list is pressed. 50 * Checks whether any of the buttons in the parameter list is pressed.
@@ -85,7 +53,7 @@ public:
85 * 53 *
86 * @returns True when at least one of the buttons is pressed. 54 * @returns True when at least one of the buttons is pressed.
87 */ 55 */
88 template <HIDButton... T> 56 template <Core::HID::NpadButton... T>
89 [[nodiscard]] bool IsAnyButtonPressed() { 57 [[nodiscard]] bool IsAnyButtonPressed() {
90 return (IsButtonPressed(T) || ...); 58 return (IsButtonPressed(T) || ...);
91 } 59 }
@@ -98,7 +66,7 @@ public:
98 * 66 *
99 * @returns True when the button is pressed once. 67 * @returns True when the button is pressed once.
100 */ 68 */
101 [[nodiscard]] bool IsButtonPressedOnce(HIDButton button) const; 69 [[nodiscard]] bool IsButtonPressedOnce(Core::HID::NpadButton button) const;
102 70
103 /** 71 /**
104 * Checks whether any of the buttons in the parameter list is pressed once. 72 * Checks whether any of the buttons in the parameter list is pressed once.
@@ -107,7 +75,7 @@ public:
107 * 75 *
108 * @returns True when at least one of the buttons is pressed once. 76 * @returns True when at least one of the buttons is pressed once.
109 */ 77 */
110 template <HIDButton... T> 78 template <Core::HID::NpadButton... T>
111 [[nodiscard]] bool IsAnyButtonPressedOnce() const { 79 [[nodiscard]] bool IsAnyButtonPressedOnce() const {
112 return (IsButtonPressedOnce(T) || ...); 80 return (IsButtonPressedOnce(T) || ...);
113 } 81 }
@@ -119,7 +87,7 @@ public:
119 * 87 *
120 * @returns True when the button is held down. 88 * @returns True when the button is held down.
121 */ 89 */
122 [[nodiscard]] bool IsButtonHeld(HIDButton button) const; 90 [[nodiscard]] bool IsButtonHeld(Core::HID::NpadButton button) const;
123 91
124 /** 92 /**
125 * Checks whether any of the buttons in the parameter list is held down. 93 * Checks whether any of the buttons in the parameter list is held down.
@@ -128,7 +96,7 @@ public:
128 * 96 *
129 * @returns True when at least one of the buttons is held down. 97 * @returns True when at least one of the buttons is held down.
130 */ 98 */
131 template <HIDButton... T> 99 template <Core::HID::NpadButton... T>
132 [[nodiscard]] bool IsAnyButtonHeld() const { 100 [[nodiscard]] bool IsAnyButtonHeld() const {
133 return (IsButtonHeld(T) || ...); 101 return (IsButtonHeld(T) || ...);
134 } 102 }
@@ -137,7 +105,7 @@ private:
137 Service::HID::Controller_NPad& npad; 105 Service::HID::Controller_NPad& npad;
138 106
139 /// Stores 9 consecutive button states polled from HID. 107 /// Stores 9 consecutive button states polled from HID.
140 std::array<u32, 9> button_states{}; 108 std::array<Core::HID::NpadButton, 9> button_states{};
141 109
142 std::size_t previous_index{}; 110 std::size_t previous_index{};
143 std::size_t current_index{}; 111 std::size_t current_index{};
diff --git a/src/input_common/motion_input.cpp b/src/core/hid/motion_input.cpp
index 1c9d561c0..c25fea966 100644
--- a/src/input_common/motion_input.cpp
+++ b/src/core/hid/motion_input.cpp
@@ -2,13 +2,21 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included 3// Refer to the license.txt file included
4 4
5#include <random>
6#include "common/math_util.h" 5#include "common/math_util.h"
7#include "input_common/motion_input.h" 6#include "core/hid/motion_input.h"
8 7
9namespace InputCommon { 8namespace Core::HID {
10 9
11MotionInput::MotionInput(f32 new_kp, f32 new_ki, f32 new_kd) : kp(new_kp), ki(new_ki), kd(new_kd) {} 10MotionInput::MotionInput() {
11 // Initialize PID constants with default values
12 SetPID(0.3f, 0.005f, 0.0f);
13}
14
15void MotionInput::SetPID(f32 new_kp, f32 new_ki, f32 new_kd) {
16 kp = new_kp;
17 ki = new_ki;
18 kd = new_kd;
19}
12 20
13void MotionInput::SetAcceleration(const Common::Vec3f& acceleration) { 21void MotionInput::SetAcceleration(const Common::Vec3f& acceleration) {
14 accel = acceleration; 22 accel = acceleration;
@@ -65,6 +73,8 @@ void MotionInput::UpdateRotation(u64 elapsed_time) {
65 rotations += gyro * sample_period; 73 rotations += gyro * sample_period;
66} 74}
67 75
76// Based on Madgwick's implementation of Mayhony's AHRS algorithm.
77// https://github.com/xioTechnologies/Open-Source-AHRS-With-x-IMU/blob/master/x-IMU%20IMU%20and%20AHRS%20Algorithms/x-IMU%20IMU%20and%20AHRS%20Algorithms/AHRS/MahonyAHRS.cs
68void MotionInput::UpdateOrientation(u64 elapsed_time) { 78void MotionInput::UpdateOrientation(u64 elapsed_time) {
69 if (!IsCalibrated(0.1f)) { 79 if (!IsCalibrated(0.1f)) {
70 ResetOrientation(); 80 ResetOrientation();
@@ -190,43 +200,6 @@ Common::Vec3f MotionInput::GetRotations() const {
190 return rotations; 200 return rotations;
191} 201}
192 202
193Input::MotionStatus MotionInput::GetMotion() const {
194 const Common::Vec3f gyroscope = GetGyroscope();
195 const Common::Vec3f accelerometer = GetAcceleration();
196 const Common::Vec3f rotation = GetRotations();
197 const std::array<Common::Vec3f, 3> orientation = GetOrientation();
198 const Common::Quaternion<f32> quaternion = GetQuaternion();
199 return {accelerometer, gyroscope, rotation, orientation, quaternion};
200}
201
202Input::MotionStatus MotionInput::GetRandomMotion(int accel_magnitude, int gyro_magnitude) const {
203 std::random_device device;
204 std::mt19937 gen(device());
205 std::uniform_int_distribution<s16> distribution(-1000, 1000);
206 const Common::Vec3f gyroscope{
207 static_cast<f32>(distribution(gen)) * 0.001f,
208 static_cast<f32>(distribution(gen)) * 0.001f,
209 static_cast<f32>(distribution(gen)) * 0.001f,
210 };
211 const Common::Vec3f accelerometer{
212 static_cast<f32>(distribution(gen)) * 0.001f,
213 static_cast<f32>(distribution(gen)) * 0.001f,
214 static_cast<f32>(distribution(gen)) * 0.001f,
215 };
216 constexpr Common::Vec3f rotation;
217 constexpr std::array orientation{
218 Common::Vec3f{1.0f, 0.0f, 0.0f},
219 Common::Vec3f{0.0f, 1.0f, 0.0f},
220 Common::Vec3f{0.0f, 0.0f, 1.0f},
221 };
222 constexpr Common::Quaternion<f32> quaternion{
223 {0.0f, 0.0f, 0.0f},
224 1.0f,
225 };
226 return {accelerometer * accel_magnitude, gyroscope * gyro_magnitude, rotation, orientation,
227 quaternion};
228}
229
230void MotionInput::ResetOrientation() { 203void MotionInput::ResetOrientation() {
231 if (!reset_enabled || only_accelerometer) { 204 if (!reset_enabled || only_accelerometer) {
232 return; 205 return;
@@ -304,4 +277,4 @@ void MotionInput::SetOrientationFromAccelerometer() {
304 quat = quat.Normalized(); 277 quat = quat.Normalized();
305 } 278 }
306} 279}
307} // namespace InputCommon 280} // namespace Core::HID
diff --git a/src/input_common/motion_input.h b/src/core/hid/motion_input.h
index efe74cf19..5b5b420bb 100644
--- a/src/input_common/motion_input.h
+++ b/src/core/hid/motion_input.h
@@ -7,13 +7,12 @@
7#include "common/common_types.h" 7#include "common/common_types.h"
8#include "common/quaternion.h" 8#include "common/quaternion.h"
9#include "common/vector_math.h" 9#include "common/vector_math.h"
10#include "core/frontend/input.h"
11 10
12namespace InputCommon { 11namespace Core::HID {
13 12
14class MotionInput { 13class MotionInput {
15public: 14public:
16 explicit MotionInput(f32 new_kp, f32 new_ki, f32 new_kd); 15 explicit MotionInput();
17 16
18 MotionInput(const MotionInput&) = default; 17 MotionInput(const MotionInput&) = default;
19 MotionInput& operator=(const MotionInput&) = default; 18 MotionInput& operator=(const MotionInput&) = default;
@@ -21,6 +20,7 @@ public:
21 MotionInput(MotionInput&&) = default; 20 MotionInput(MotionInput&&) = default;
22 MotionInput& operator=(MotionInput&&) = default; 21 MotionInput& operator=(MotionInput&&) = default;
23 22
23 void SetPID(f32 new_kp, f32 new_ki, f32 new_kd);
24 void SetAcceleration(const Common::Vec3f& acceleration); 24 void SetAcceleration(const Common::Vec3f& acceleration);
25 void SetGyroscope(const Common::Vec3f& gyroscope); 25 void SetGyroscope(const Common::Vec3f& gyroscope);
26 void SetQuaternion(const Common::Quaternion<f32>& quaternion); 26 void SetQuaternion(const Common::Quaternion<f32>& quaternion);
@@ -38,9 +38,6 @@ public:
38 [[nodiscard]] Common::Vec3f GetGyroscope() const; 38 [[nodiscard]] Common::Vec3f GetGyroscope() const;
39 [[nodiscard]] Common::Vec3f GetRotations() const; 39 [[nodiscard]] Common::Vec3f GetRotations() const;
40 [[nodiscard]] Common::Quaternion<f32> GetQuaternion() const; 40 [[nodiscard]] Common::Quaternion<f32> GetQuaternion() const;
41 [[nodiscard]] Input::MotionStatus GetMotion() const;
42 [[nodiscard]] Input::MotionStatus GetRandomMotion(int accel_magnitude,
43 int gyro_magnitude) const;
44 41
45 [[nodiscard]] bool IsMoving(f32 sensitivity) const; 42 [[nodiscard]] bool IsMoving(f32 sensitivity) const;
46 [[nodiscard]] bool IsCalibrated(f32 sensitivity) const; 43 [[nodiscard]] bool IsCalibrated(f32 sensitivity) const;
@@ -59,16 +56,32 @@ private:
59 Common::Vec3f integral_error; 56 Common::Vec3f integral_error;
60 Common::Vec3f derivative_error; 57 Common::Vec3f derivative_error;
61 58
59 // Quaternion containing the device orientation
62 Common::Quaternion<f32> quat{{0.0f, 0.0f, -1.0f}, 0.0f}; 60 Common::Quaternion<f32> quat{{0.0f, 0.0f, -1.0f}, 0.0f};
61
62 // Number of full rotations in each axis
63 Common::Vec3f rotations; 63 Common::Vec3f rotations;
64
65 // Acceleration vector measurement in G force
64 Common::Vec3f accel; 66 Common::Vec3f accel;
67
68 // Gyroscope vector measurement in radians/s.
65 Common::Vec3f gyro; 69 Common::Vec3f gyro;
70
71 // Vector to be substracted from gyro measurements
66 Common::Vec3f gyro_drift; 72 Common::Vec3f gyro_drift;
67 73
74 // Minimum gyro amplitude to detect if the device is moving
68 f32 gyro_threshold = 0.0f; 75 f32 gyro_threshold = 0.0f;
76
77 // Number of invalid sequential data
69 u32 reset_counter = 0; 78 u32 reset_counter = 0;
79
80 // If the provided data is invalid the device will be autocalibrated
70 bool reset_enabled = true; 81 bool reset_enabled = true;
82
83 // Use accelerometer values to calculate position
71 bool only_accelerometer = true; 84 bool only_accelerometer = true;
72}; 85};
73 86
74} // namespace InputCommon 87} // namespace Core::HID
diff --git a/src/core/hle/kernel/init/init_slab_setup.cpp b/src/core/hle/kernel/init/init_slab_setup.cpp
index 8ff0f695d..36fc0944a 100644
--- a/src/core/hle/kernel/init/init_slab_setup.cpp
+++ b/src/core/hle/kernel/init/init_slab_setup.cpp
@@ -9,6 +9,7 @@
9#include "core/core.h" 9#include "core/core.h"
10#include "core/hardware_properties.h" 10#include "core/hardware_properties.h"
11#include "core/hle/kernel/init/init_slab_setup.h" 11#include "core/hle/kernel/init/init_slab_setup.h"
12#include "core/hle/kernel/k_code_memory.h"
12#include "core/hle/kernel/k_event.h" 13#include "core/hle/kernel/k_event.h"
13#include "core/hle/kernel/k_memory_layout.h" 14#include "core/hle/kernel/k_memory_layout.h"
14#include "core/hle/kernel/k_memory_manager.h" 15#include "core/hle/kernel/k_memory_manager.h"
@@ -32,6 +33,7 @@ namespace Kernel::Init {
32 HANDLER(KPort, (SLAB_COUNT(KPort)), ##__VA_ARGS__) \ 33 HANDLER(KPort, (SLAB_COUNT(KPort)), ##__VA_ARGS__) \
33 HANDLER(KSharedMemory, (SLAB_COUNT(KSharedMemory)), ##__VA_ARGS__) \ 34 HANDLER(KSharedMemory, (SLAB_COUNT(KSharedMemory)), ##__VA_ARGS__) \
34 HANDLER(KTransferMemory, (SLAB_COUNT(KTransferMemory)), ##__VA_ARGS__) \ 35 HANDLER(KTransferMemory, (SLAB_COUNT(KTransferMemory)), ##__VA_ARGS__) \
36 HANDLER(KCodeMemory, (SLAB_COUNT(KCodeMemory)), ##__VA_ARGS__) \
35 HANDLER(KSession, (SLAB_COUNT(KSession)), ##__VA_ARGS__) \ 37 HANDLER(KSession, (SLAB_COUNT(KSession)), ##__VA_ARGS__) \
36 HANDLER(KResourceLimit, (SLAB_COUNT(KResourceLimit)), ##__VA_ARGS__) 38 HANDLER(KResourceLimit, (SLAB_COUNT(KResourceLimit)), ##__VA_ARGS__)
37 39
diff --git a/src/core/hle/kernel/k_address_arbiter.cpp b/src/core/hle/kernel/k_address_arbiter.cpp
index 1b429bc1e..783c69858 100644
--- a/src/core/hle/kernel/k_address_arbiter.cpp
+++ b/src/core/hle/kernel/k_address_arbiter.cpp
@@ -8,6 +8,7 @@
8#include "core/hle/kernel/k_scheduler.h" 8#include "core/hle/kernel/k_scheduler.h"
9#include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h" 9#include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h"
10#include "core/hle/kernel/k_thread.h" 10#include "core/hle/kernel/k_thread.h"
11#include "core/hle/kernel/k_thread_queue.h"
11#include "core/hle/kernel/kernel.h" 12#include "core/hle/kernel/kernel.h"
12#include "core/hle/kernel/svc_results.h" 13#include "core/hle/kernel/svc_results.h"
13#include "core/hle/kernel/time_manager.h" 14#include "core/hle/kernel/time_manager.h"
@@ -28,7 +29,7 @@ bool ReadFromUser(Core::System& system, s32* out, VAddr address) {
28 29
29bool DecrementIfLessThan(Core::System& system, s32* out, VAddr address, s32 value) { 30bool DecrementIfLessThan(Core::System& system, s32* out, VAddr address, s32 value) {
30 auto& monitor = system.Monitor(); 31 auto& monitor = system.Monitor();
31 const auto current_core = system.CurrentCoreIndex(); 32 const auto current_core = system.Kernel().CurrentPhysicalCoreIndex();
32 33
33 // TODO(bunnei): We should disable interrupts here via KScopedInterruptDisable. 34 // TODO(bunnei): We should disable interrupts here via KScopedInterruptDisable.
34 // TODO(bunnei): We should call CanAccessAtomic(..) here. 35 // TODO(bunnei): We should call CanAccessAtomic(..) here.
@@ -58,7 +59,7 @@ bool DecrementIfLessThan(Core::System& system, s32* out, VAddr address, s32 valu
58 59
59bool UpdateIfEqual(Core::System& system, s32* out, VAddr address, s32 value, s32 new_value) { 60bool UpdateIfEqual(Core::System& system, s32* out, VAddr address, s32 value, s32 new_value) {
60 auto& monitor = system.Monitor(); 61 auto& monitor = system.Monitor();
61 const auto current_core = system.CurrentCoreIndex(); 62 const auto current_core = system.Kernel().CurrentPhysicalCoreIndex();
62 63
63 // TODO(bunnei): We should disable interrupts here via KScopedInterruptDisable. 64 // TODO(bunnei): We should disable interrupts here via KScopedInterruptDisable.
64 // TODO(bunnei): We should call CanAccessAtomic(..) here. 65 // TODO(bunnei): We should call CanAccessAtomic(..) here.
@@ -85,6 +86,27 @@ bool UpdateIfEqual(Core::System& system, s32* out, VAddr address, s32 value, s32
85 return true; 86 return true;
86} 87}
87 88
89class ThreadQueueImplForKAddressArbiter final : public KThreadQueue {
90public:
91 explicit ThreadQueueImplForKAddressArbiter(KernelCore& kernel_, KAddressArbiter::ThreadTree* t)
92 : KThreadQueue(kernel_), m_tree(t) {}
93
94 void CancelWait(KThread* waiting_thread, ResultCode wait_result,
95 bool cancel_timer_task) override {
96 // If the thread is waiting on an address arbiter, remove it from the tree.
97 if (waiting_thread->IsWaitingForAddressArbiter()) {
98 m_tree->erase(m_tree->iterator_to(*waiting_thread));
99 waiting_thread->ClearAddressArbiter();
100 }
101
102 // Invoke the base cancel wait handler.
103 KThreadQueue::CancelWait(waiting_thread, wait_result, cancel_timer_task);
104 }
105
106private:
107 KAddressArbiter::ThreadTree* m_tree;
108};
109
88} // namespace 110} // namespace
89 111
90ResultCode KAddressArbiter::Signal(VAddr addr, s32 count) { 112ResultCode KAddressArbiter::Signal(VAddr addr, s32 count) {
@@ -96,14 +118,14 @@ ResultCode KAddressArbiter::Signal(VAddr addr, s32 count) {
96 auto it = thread_tree.nfind_light({addr, -1}); 118 auto it = thread_tree.nfind_light({addr, -1});
97 while ((it != thread_tree.end()) && (count <= 0 || num_waiters < count) && 119 while ((it != thread_tree.end()) && (count <= 0 || num_waiters < count) &&
98 (it->GetAddressArbiterKey() == addr)) { 120 (it->GetAddressArbiterKey() == addr)) {
121 // End the thread's wait.
99 KThread* target_thread = std::addressof(*it); 122 KThread* target_thread = std::addressof(*it);
100 target_thread->SetSyncedObject(nullptr, ResultSuccess); 123 target_thread->EndWait(ResultSuccess);
101 124
102 ASSERT(target_thread->IsWaitingForAddressArbiter()); 125 ASSERT(target_thread->IsWaitingForAddressArbiter());
103 target_thread->Wakeup(); 126 target_thread->ClearAddressArbiter();
104 127
105 it = thread_tree.erase(it); 128 it = thread_tree.erase(it);
106 target_thread->ClearAddressArbiter();
107 ++num_waiters; 129 ++num_waiters;
108 } 130 }
109 } 131 }
@@ -129,14 +151,14 @@ ResultCode KAddressArbiter::SignalAndIncrementIfEqual(VAddr addr, s32 value, s32
129 auto it = thread_tree.nfind_light({addr, -1}); 151 auto it = thread_tree.nfind_light({addr, -1});
130 while ((it != thread_tree.end()) && (count <= 0 || num_waiters < count) && 152 while ((it != thread_tree.end()) && (count <= 0 || num_waiters < count) &&
131 (it->GetAddressArbiterKey() == addr)) { 153 (it->GetAddressArbiterKey() == addr)) {
154 // End the thread's wait.
132 KThread* target_thread = std::addressof(*it); 155 KThread* target_thread = std::addressof(*it);
133 target_thread->SetSyncedObject(nullptr, ResultSuccess); 156 target_thread->EndWait(ResultSuccess);
134 157
135 ASSERT(target_thread->IsWaitingForAddressArbiter()); 158 ASSERT(target_thread->IsWaitingForAddressArbiter());
136 target_thread->Wakeup(); 159 target_thread->ClearAddressArbiter();
137 160
138 it = thread_tree.erase(it); 161 it = thread_tree.erase(it);
139 target_thread->ClearAddressArbiter();
140 ++num_waiters; 162 ++num_waiters;
141 } 163 }
142 } 164 }
@@ -197,14 +219,14 @@ ResultCode KAddressArbiter::SignalAndModifyByWaitingCountIfEqual(VAddr addr, s32
197 219
198 while ((it != thread_tree.end()) && (count <= 0 || num_waiters < count) && 220 while ((it != thread_tree.end()) && (count <= 0 || num_waiters < count) &&
199 (it->GetAddressArbiterKey() == addr)) { 221 (it->GetAddressArbiterKey() == addr)) {
222 // End the thread's wait.
200 KThread* target_thread = std::addressof(*it); 223 KThread* target_thread = std::addressof(*it);
201 target_thread->SetSyncedObject(nullptr, ResultSuccess); 224 target_thread->EndWait(ResultSuccess);
202 225
203 ASSERT(target_thread->IsWaitingForAddressArbiter()); 226 ASSERT(target_thread->IsWaitingForAddressArbiter());
204 target_thread->Wakeup(); 227 target_thread->ClearAddressArbiter();
205 228
206 it = thread_tree.erase(it); 229 it = thread_tree.erase(it);
207 target_thread->ClearAddressArbiter();
208 ++num_waiters; 230 ++num_waiters;
209 } 231 }
210 } 232 }
@@ -214,6 +236,7 @@ ResultCode KAddressArbiter::SignalAndModifyByWaitingCountIfEqual(VAddr addr, s32
214ResultCode KAddressArbiter::WaitIfLessThan(VAddr addr, s32 value, bool decrement, s64 timeout) { 236ResultCode KAddressArbiter::WaitIfLessThan(VAddr addr, s32 value, bool decrement, s64 timeout) {
215 // Prepare to wait. 237 // Prepare to wait.
216 KThread* cur_thread = kernel.CurrentScheduler()->GetCurrentThread(); 238 KThread* cur_thread = kernel.CurrentScheduler()->GetCurrentThread();
239 ThreadQueueImplForKAddressArbiter wait_queue(kernel, std::addressof(thread_tree));
217 240
218 { 241 {
219 KScopedSchedulerLockAndSleep slp{kernel, cur_thread, timeout}; 242 KScopedSchedulerLockAndSleep slp{kernel, cur_thread, timeout};
@@ -224,9 +247,6 @@ ResultCode KAddressArbiter::WaitIfLessThan(VAddr addr, s32 value, bool decrement
224 return ResultTerminationRequested; 247 return ResultTerminationRequested;
225 } 248 }
226 249
227 // Set the synced object.
228 cur_thread->SetSyncedObject(nullptr, ResultTimedOut);
229
230 // Read the value from userspace. 250 // Read the value from userspace.
231 s32 user_value{}; 251 s32 user_value{};
232 bool succeeded{}; 252 bool succeeded{};
@@ -256,31 +276,20 @@ ResultCode KAddressArbiter::WaitIfLessThan(VAddr addr, s32 value, bool decrement
256 // Set the arbiter. 276 // Set the arbiter.
257 cur_thread->SetAddressArbiter(&thread_tree, addr); 277 cur_thread->SetAddressArbiter(&thread_tree, addr);
258 thread_tree.insert(*cur_thread); 278 thread_tree.insert(*cur_thread);
259 cur_thread->SetState(ThreadState::Waiting);
260 cur_thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::Arbitration);
261 }
262
263 // Cancel the timer wait.
264 kernel.TimeManager().UnscheduleTimeEvent(cur_thread);
265 279
266 // Remove from the address arbiter. 280 // Wait for the thread to finish.
267 { 281 cur_thread->BeginWait(std::addressof(wait_queue));
268 KScopedSchedulerLock sl(kernel); 282 cur_thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::Arbitration);
269
270 if (cur_thread->IsWaitingForAddressArbiter()) {
271 thread_tree.erase(thread_tree.iterator_to(*cur_thread));
272 cur_thread->ClearAddressArbiter();
273 }
274 } 283 }
275 284
276 // Get the result. 285 // Get the result.
277 KSynchronizationObject* dummy{}; 286 return cur_thread->GetWaitResult();
278 return cur_thread->GetWaitResult(&dummy);
279} 287}
280 288
281ResultCode KAddressArbiter::WaitIfEqual(VAddr addr, s32 value, s64 timeout) { 289ResultCode KAddressArbiter::WaitIfEqual(VAddr addr, s32 value, s64 timeout) {
282 // Prepare to wait. 290 // Prepare to wait.
283 KThread* cur_thread = kernel.CurrentScheduler()->GetCurrentThread(); 291 KThread* cur_thread = kernel.CurrentScheduler()->GetCurrentThread();
292 ThreadQueueImplForKAddressArbiter wait_queue(kernel, std::addressof(thread_tree));
284 293
285 { 294 {
286 KScopedSchedulerLockAndSleep slp{kernel, cur_thread, timeout}; 295 KScopedSchedulerLockAndSleep slp{kernel, cur_thread, timeout};
@@ -291,9 +300,6 @@ ResultCode KAddressArbiter::WaitIfEqual(VAddr addr, s32 value, s64 timeout) {
291 return ResultTerminationRequested; 300 return ResultTerminationRequested;
292 } 301 }
293 302
294 // Set the synced object.
295 cur_thread->SetSyncedObject(nullptr, ResultTimedOut);
296
297 // Read the value from userspace. 303 // Read the value from userspace.
298 s32 user_value{}; 304 s32 user_value{};
299 if (!ReadFromUser(system, &user_value, addr)) { 305 if (!ReadFromUser(system, &user_value, addr)) {
@@ -316,26 +322,14 @@ ResultCode KAddressArbiter::WaitIfEqual(VAddr addr, s32 value, s64 timeout) {
316 // Set the arbiter. 322 // Set the arbiter.
317 cur_thread->SetAddressArbiter(&thread_tree, addr); 323 cur_thread->SetAddressArbiter(&thread_tree, addr);
318 thread_tree.insert(*cur_thread); 324 thread_tree.insert(*cur_thread);
319 cur_thread->SetState(ThreadState::Waiting);
320 cur_thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::Arbitration);
321 }
322
323 // Cancel the timer wait.
324 kernel.TimeManager().UnscheduleTimeEvent(cur_thread);
325 325
326 // Remove from the address arbiter. 326 // Wait for the thread to finish.
327 { 327 cur_thread->BeginWait(std::addressof(wait_queue));
328 KScopedSchedulerLock sl(kernel); 328 cur_thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::Arbitration);
329
330 if (cur_thread->IsWaitingForAddressArbiter()) {
331 thread_tree.erase(thread_tree.iterator_to(*cur_thread));
332 cur_thread->ClearAddressArbiter();
333 }
334 } 329 }
335 330
336 // Get the result. 331 // Get the result.
337 KSynchronizationObject* dummy{}; 332 return cur_thread->GetWaitResult();
338 return cur_thread->GetWaitResult(&dummy);
339} 333}
340 334
341} // namespace Kernel 335} // namespace Kernel
diff --git a/src/core/hle/kernel/k_auto_object.h b/src/core/hle/kernel/k_auto_object.h
index e4fcdbc67..165b76747 100644
--- a/src/core/hle/kernel/k_auto_object.h
+++ b/src/core/hle/kernel/k_auto_object.h
@@ -170,6 +170,10 @@ public:
170 } 170 }
171 } 171 }
172 172
173 const std::string& GetName() const {
174 return name;
175 }
176
173private: 177private:
174 void RegisterWithKernel(); 178 void RegisterWithKernel();
175 void UnregisterWithKernel(); 179 void UnregisterWithKernel();
diff --git a/src/core/hle/kernel/k_class_token.cpp b/src/core/hle/kernel/k_class_token.cpp
index 0be0027be..21e2fe494 100644
--- a/src/core/hle/kernel/k_class_token.cpp
+++ b/src/core/hle/kernel/k_class_token.cpp
@@ -6,6 +6,7 @@
6#include "core/hle/kernel/k_class_token.h" 6#include "core/hle/kernel/k_class_token.h"
7#include "core/hle/kernel/k_client_port.h" 7#include "core/hle/kernel/k_client_port.h"
8#include "core/hle/kernel/k_client_session.h" 8#include "core/hle/kernel/k_client_session.h"
9#include "core/hle/kernel/k_code_memory.h"
9#include "core/hle/kernel/k_event.h" 10#include "core/hle/kernel/k_event.h"
10#include "core/hle/kernel/k_port.h" 11#include "core/hle/kernel/k_port.h"
11#include "core/hle/kernel/k_process.h" 12#include "core/hle/kernel/k_process.h"
@@ -48,7 +49,7 @@ static_assert(ClassToken<KWritableEvent> == 0b10001001'00000000);
48static_assert(ClassToken<KTransferMemory> == 0b10010001'00000000); 49static_assert(ClassToken<KTransferMemory> == 0b10010001'00000000);
49// static_assert(ClassToken<KDeviceAddressSpace> == 0b01100001'00000000); 50// static_assert(ClassToken<KDeviceAddressSpace> == 0b01100001'00000000);
50// static_assert(ClassToken<KSessionRequest> == 0b10100001'00000000); 51// static_assert(ClassToken<KSessionRequest> == 0b10100001'00000000);
51// static_assert(ClassToken<KCodeMemory> == 0b11000001'00000000); 52static_assert(ClassToken<KCodeMemory> == 0b11000001'00000000);
52 53
53// Ensure that the token hierarchy is correct. 54// Ensure that the token hierarchy is correct.
54 55
@@ -79,7 +80,7 @@ static_assert(ClassToken<KWritableEvent> == ((0b10001001 << 8) | ClassToken<KAut
79static_assert(ClassToken<KTransferMemory> == ((0b10010001 << 8) | ClassToken<KAutoObject>)); 80static_assert(ClassToken<KTransferMemory> == ((0b10010001 << 8) | ClassToken<KAutoObject>));
80// static_assert(ClassToken<KDeviceAddressSpace> == ((0b01100001 << 8) | ClassToken<KAutoObject>)); 81// static_assert(ClassToken<KDeviceAddressSpace> == ((0b01100001 << 8) | ClassToken<KAutoObject>));
81// static_assert(ClassToken<KSessionRequest> == ((0b10100001 << 8) | ClassToken<KAutoObject>)); 82// static_assert(ClassToken<KSessionRequest> == ((0b10100001 << 8) | ClassToken<KAutoObject>));
82// static_assert(ClassToken<KCodeMemory> == ((0b11000001 << 8) | ClassToken<KAutoObject>)); 83static_assert(ClassToken<KCodeMemory> == ((0b11000001 << 8) | ClassToken<KAutoObject>));
83 84
84// Ensure that the token hierarchy reflects the class hierarchy. 85// Ensure that the token hierarchy reflects the class hierarchy.
85 86
diff --git a/src/core/hle/kernel/k_code_memory.cpp b/src/core/hle/kernel/k_code_memory.cpp
new file mode 100644
index 000000000..d69f7ffb7
--- /dev/null
+++ b/src/core/hle/kernel/k_code_memory.cpp
@@ -0,0 +1,146 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "common/common_types.h"
6#include "core/device_memory.h"
7#include "core/hle/kernel/k_auto_object.h"
8#include "core/hle/kernel/k_code_memory.h"
9#include "core/hle/kernel/k_light_lock.h"
10#include "core/hle/kernel/k_memory_block.h"
11#include "core/hle/kernel/k_page_linked_list.h"
12#include "core/hle/kernel/k_page_table.h"
13#include "core/hle/kernel/k_process.h"
14#include "core/hle/kernel/slab_helpers.h"
15#include "core/hle/kernel/svc_types.h"
16#include "core/hle/result.h"
17
18namespace Kernel {
19
20KCodeMemory::KCodeMemory(KernelCore& kernel_)
21 : KAutoObjectWithSlabHeapAndContainer{kernel_}, m_lock(kernel_) {}
22
23ResultCode KCodeMemory::Initialize(Core::DeviceMemory& device_memory, VAddr addr, size_t size) {
24 // Set members.
25 m_owner = kernel.CurrentProcess();
26
27 // Get the owner page table.
28 auto& page_table = m_owner->PageTable();
29
30 // Construct the page group.
31 KMemoryInfo kBlockInfo = page_table.QueryInfo(addr);
32 m_page_group = KPageLinkedList(kBlockInfo.GetAddress(), kBlockInfo.GetNumPages());
33
34 // Lock the memory.
35 R_TRY(page_table.LockForCodeMemory(addr, size))
36
37 // Clear the memory.
38 for (const auto& block : m_page_group.Nodes()) {
39 std::memset(device_memory.GetPointer(block.GetAddress()), 0xFF, block.GetSize());
40 }
41
42 // Set remaining tracking members.
43 m_address = addr;
44 m_is_initialized = true;
45 m_is_owner_mapped = false;
46 m_is_mapped = false;
47
48 // We succeeded.
49 return ResultSuccess;
50}
51
52void KCodeMemory::Finalize() {
53 // Unlock.
54 if (!m_is_mapped && !m_is_owner_mapped) {
55 const size_t size = m_page_group.GetNumPages() * PageSize;
56 m_owner->PageTable().UnlockForCodeMemory(m_address, size);
57 }
58}
59
60ResultCode KCodeMemory::Map(VAddr address, size_t size) {
61 // Validate the size.
62 R_UNLESS(m_page_group.GetNumPages() == Common::DivideUp(size, PageSize), ResultInvalidSize);
63
64 // Lock ourselves.
65 KScopedLightLock lk(m_lock);
66
67 // Ensure we're not already mapped.
68 R_UNLESS(!m_is_mapped, ResultInvalidState);
69
70 // Map the memory.
71 R_TRY(kernel.CurrentProcess()->PageTable().MapPages(
72 address, m_page_group, KMemoryState::CodeOut, KMemoryPermission::UserReadWrite));
73
74 // Mark ourselves as mapped.
75 m_is_mapped = true;
76
77 return ResultSuccess;
78}
79
80ResultCode KCodeMemory::Unmap(VAddr address, size_t size) {
81 // Validate the size.
82 R_UNLESS(m_page_group.GetNumPages() == Common::DivideUp(size, PageSize), ResultInvalidSize);
83
84 // Lock ourselves.
85 KScopedLightLock lk(m_lock);
86
87 // Unmap the memory.
88 R_TRY(kernel.CurrentProcess()->PageTable().UnmapPages(address, m_page_group,
89 KMemoryState::CodeOut));
90
91 // Mark ourselves as unmapped.
92 m_is_mapped = false;
93
94 return ResultSuccess;
95}
96
97ResultCode KCodeMemory::MapToOwner(VAddr address, size_t size, Svc::MemoryPermission perm) {
98 // Validate the size.
99 R_UNLESS(m_page_group.GetNumPages() == Common::DivideUp(size, PageSize), ResultInvalidSize);
100
101 // Lock ourselves.
102 KScopedLightLock lk(m_lock);
103
104 // Ensure we're not already mapped.
105 R_UNLESS(!m_is_owner_mapped, ResultInvalidState);
106
107 // Convert the memory permission.
108 KMemoryPermission k_perm{};
109 switch (perm) {
110 case Svc::MemoryPermission::Read:
111 k_perm = KMemoryPermission::UserRead;
112 break;
113 case Svc::MemoryPermission::ReadExecute:
114 k_perm = KMemoryPermission::UserReadExecute;
115 break;
116 default:
117 break;
118 }
119
120 // Map the memory.
121 R_TRY(
122 m_owner->PageTable().MapPages(address, m_page_group, KMemoryState::GeneratedCode, k_perm));
123
124 // Mark ourselves as mapped.
125 m_is_owner_mapped = true;
126
127 return ResultSuccess;
128}
129
130ResultCode KCodeMemory::UnmapFromOwner(VAddr address, size_t size) {
131 // Validate the size.
132 R_UNLESS(m_page_group.GetNumPages() == Common::DivideUp(size, PageSize), ResultInvalidSize);
133
134 // Lock ourselves.
135 KScopedLightLock lk(m_lock);
136
137 // Unmap the memory.
138 R_TRY(m_owner->PageTable().UnmapPages(address, m_page_group, KMemoryState::GeneratedCode));
139
140 // Mark ourselves as unmapped.
141 m_is_owner_mapped = false;
142
143 return ResultSuccess;
144}
145
146} // namespace Kernel \ No newline at end of file
diff --git a/src/core/hle/kernel/k_code_memory.h b/src/core/hle/kernel/k_code_memory.h
new file mode 100644
index 000000000..e0ba19a53
--- /dev/null
+++ b/src/core/hle/kernel/k_code_memory.h
@@ -0,0 +1,66 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "common/common_types.h"
8#include "core/device_memory.h"
9#include "core/hle/kernel/k_auto_object.h"
10#include "core/hle/kernel/k_light_lock.h"
11#include "core/hle/kernel/k_page_linked_list.h"
12#include "core/hle/kernel/k_process.h"
13#include "core/hle/kernel/slab_helpers.h"
14#include "core/hle/kernel/svc_types.h"
15#include "core/hle/result.h"
16
17namespace Kernel {
18
19enum class CodeMemoryOperation : u32 {
20 Map = 0,
21 MapToOwner = 1,
22 Unmap = 2,
23 UnmapFromOwner = 3,
24};
25
26class KCodeMemory final
27 : public KAutoObjectWithSlabHeapAndContainer<KCodeMemory, KAutoObjectWithList> {
28 KERNEL_AUTOOBJECT_TRAITS(KCodeMemory, KAutoObject);
29
30public:
31 explicit KCodeMemory(KernelCore& kernel_);
32
33 ResultCode Initialize(Core::DeviceMemory& device_memory, VAddr address, size_t size);
34 void Finalize();
35
36 ResultCode Map(VAddr address, size_t size);
37 ResultCode Unmap(VAddr address, size_t size);
38 ResultCode MapToOwner(VAddr address, size_t size, Svc::MemoryPermission perm);
39 ResultCode UnmapFromOwner(VAddr address, size_t size);
40
41 bool IsInitialized() const {
42 return m_is_initialized;
43 }
44 static void PostDestroy([[maybe_unused]] uintptr_t arg) {}
45
46 KProcess* GetOwner() const {
47 return m_owner;
48 }
49 VAddr GetSourceAddress() const {
50 return m_address;
51 }
52 size_t GetSize() const {
53 return m_is_initialized ? m_page_group.GetNumPages() * PageSize : 0;
54 }
55
56private:
57 KPageLinkedList m_page_group{};
58 KProcess* m_owner{};
59 VAddr m_address{};
60 KLightLock m_lock;
61 bool m_is_initialized{};
62 bool m_is_owner_mapped{};
63 bool m_is_mapped{};
64};
65
66} // namespace Kernel
diff --git a/src/core/hle/kernel/k_condition_variable.cpp b/src/core/hle/kernel/k_condition_variable.cpp
index 7fa9b8cc3..aadcc297a 100644
--- a/src/core/hle/kernel/k_condition_variable.cpp
+++ b/src/core/hle/kernel/k_condition_variable.cpp
@@ -11,6 +11,7 @@
11#include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h" 11#include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h"
12#include "core/hle/kernel/k_synchronization_object.h" 12#include "core/hle/kernel/k_synchronization_object.h"
13#include "core/hle/kernel/k_thread.h" 13#include "core/hle/kernel/k_thread.h"
14#include "core/hle/kernel/k_thread_queue.h"
14#include "core/hle/kernel/kernel.h" 15#include "core/hle/kernel/kernel.h"
15#include "core/hle/kernel/svc_common.h" 16#include "core/hle/kernel/svc_common.h"
16#include "core/hle/kernel/svc_results.h" 17#include "core/hle/kernel/svc_results.h"
@@ -33,7 +34,7 @@ bool WriteToUser(Core::System& system, VAddr address, const u32* p) {
33bool UpdateLockAtomic(Core::System& system, u32* out, VAddr address, u32 if_zero, 34bool UpdateLockAtomic(Core::System& system, u32* out, VAddr address, u32 if_zero,
34 u32 new_orr_mask) { 35 u32 new_orr_mask) {
35 auto& monitor = system.Monitor(); 36 auto& monitor = system.Monitor();
36 const auto current_core = system.CurrentCoreIndex(); 37 const auto current_core = system.Kernel().CurrentPhysicalCoreIndex();
37 38
38 // Load the value from the address. 39 // Load the value from the address.
39 const auto expected = monitor.ExclusiveRead32(current_core, address); 40 const auto expected = monitor.ExclusiveRead32(current_core, address);
@@ -57,6 +58,48 @@ bool UpdateLockAtomic(Core::System& system, u32* out, VAddr address, u32 if_zero
57 return true; 58 return true;
58} 59}
59 60
61class ThreadQueueImplForKConditionVariableWaitForAddress final : public KThreadQueue {
62public:
63 explicit ThreadQueueImplForKConditionVariableWaitForAddress(KernelCore& kernel_)
64 : KThreadQueue(kernel_) {}
65
66 void CancelWait(KThread* waiting_thread, ResultCode wait_result,
67 bool cancel_timer_task) override {
68 // Remove the thread as a waiter from its owner.
69 waiting_thread->GetLockOwner()->RemoveWaiter(waiting_thread);
70
71 // Invoke the base cancel wait handler.
72 KThreadQueue::CancelWait(waiting_thread, wait_result, cancel_timer_task);
73 }
74};
75
76class ThreadQueueImplForKConditionVariableWaitConditionVariable final : public KThreadQueue {
77private:
78 KConditionVariable::ThreadTree* m_tree;
79
80public:
81 explicit ThreadQueueImplForKConditionVariableWaitConditionVariable(
82 KernelCore& kernel_, KConditionVariable::ThreadTree* t)
83 : KThreadQueue(kernel_), m_tree(t) {}
84
85 void CancelWait(KThread* waiting_thread, ResultCode wait_result,
86 bool cancel_timer_task) override {
87 // Remove the thread as a waiter from its owner.
88 if (KThread* owner = waiting_thread->GetLockOwner(); owner != nullptr) {
89 owner->RemoveWaiter(waiting_thread);
90 }
91
92 // If the thread is waiting on a condvar, remove it from the tree.
93 if (waiting_thread->IsWaitingForConditionVariable()) {
94 m_tree->erase(m_tree->iterator_to(*waiting_thread));
95 waiting_thread->ClearConditionVariable();
96 }
97
98 // Invoke the base cancel wait handler.
99 KThreadQueue::CancelWait(waiting_thread, wait_result, cancel_timer_task);
100 }
101};
102
60} // namespace 103} // namespace
61 104
62KConditionVariable::KConditionVariable(Core::System& system_) 105KConditionVariable::KConditionVariable(Core::System& system_)
@@ -78,84 +121,77 @@ ResultCode KConditionVariable::SignalToAddress(VAddr addr) {
78 121
79 // Determine the next tag. 122 // Determine the next tag.
80 u32 next_value{}; 123 u32 next_value{};
81 if (next_owner_thread) { 124 if (next_owner_thread != nullptr) {
82 next_value = next_owner_thread->GetAddressKeyValue(); 125 next_value = next_owner_thread->GetAddressKeyValue();
83 if (num_waiters > 1) { 126 if (num_waiters > 1) {
84 next_value |= Svc::HandleWaitMask; 127 next_value |= Svc::HandleWaitMask;
85 } 128 }
86 129
87 next_owner_thread->SetSyncedObject(nullptr, ResultSuccess); 130 // Write the value to userspace.
88 next_owner_thread->Wakeup(); 131 ResultCode result{ResultSuccess};
89 } 132 if (WriteToUser(system, addr, std::addressof(next_value))) [[likely]] {
90 133 result = ResultSuccess;
91 // Write the value to userspace. 134 } else {
92 if (!WriteToUser(system, addr, std::addressof(next_value))) { 135 result = ResultInvalidCurrentMemory;
93 if (next_owner_thread) {
94 next_owner_thread->SetSyncedObject(nullptr, ResultInvalidCurrentMemory);
95 } 136 }
96 137
97 return ResultInvalidCurrentMemory; 138 // Signal the next owner thread.
139 next_owner_thread->EndWait(result);
140 return result;
141 } else {
142 // Just write the value to userspace.
143 R_UNLESS(WriteToUser(system, addr, std::addressof(next_value)),
144 ResultInvalidCurrentMemory);
145
146 return ResultSuccess;
98 } 147 }
99 } 148 }
100
101 return ResultSuccess;
102} 149}
103 150
104ResultCode KConditionVariable::WaitForAddress(Handle handle, VAddr addr, u32 value) { 151ResultCode KConditionVariable::WaitForAddress(Handle handle, VAddr addr, u32 value) {
105 KThread* cur_thread = kernel.CurrentScheduler()->GetCurrentThread(); 152 KThread* cur_thread = kernel.CurrentScheduler()->GetCurrentThread();
153 ThreadQueueImplForKConditionVariableWaitForAddress wait_queue(kernel);
106 154
107 // Wait for the address. 155 // Wait for the address.
156 KThread* owner_thread{};
108 { 157 {
109 KScopedAutoObject<KThread> owner_thread; 158 KScopedSchedulerLock sl(kernel);
110 ASSERT(owner_thread.IsNull());
111 {
112 KScopedSchedulerLock sl(kernel);
113 cur_thread->SetSyncedObject(nullptr, ResultSuccess);
114 159
115 // Check if the thread should terminate. 160 // Check if the thread should terminate.
116 R_UNLESS(!cur_thread->IsTerminationRequested(), ResultTerminationRequested); 161 R_UNLESS(!cur_thread->IsTerminationRequested(), ResultTerminationRequested);
117 162
118 { 163 // Read the tag from userspace.
119 // Read the tag from userspace. 164 u32 test_tag{};
120 u32 test_tag{}; 165 R_UNLESS(ReadFromUser(system, std::addressof(test_tag), addr), ResultInvalidCurrentMemory);
121 R_UNLESS(ReadFromUser(system, std::addressof(test_tag), addr),
122 ResultInvalidCurrentMemory);
123
124 // If the tag isn't the handle (with wait mask), we're done.
125 R_UNLESS(test_tag == (handle | Svc::HandleWaitMask), ResultSuccess);
126
127 // Get the lock owner thread.
128 owner_thread =
129 kernel.CurrentProcess()->GetHandleTable().GetObjectWithoutPseudoHandle<KThread>(
130 handle);
131 R_UNLESS(owner_thread.IsNotNull(), ResultInvalidHandle);
132
133 // Update the lock.
134 cur_thread->SetAddressKey(addr, value);
135 owner_thread->AddWaiter(cur_thread);
136 cur_thread->SetState(ThreadState::Waiting);
137 cur_thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::ConditionVar);
138 cur_thread->SetMutexWaitAddressForDebugging(addr);
139 }
140 }
141 ASSERT(owner_thread.IsNotNull());
142 }
143 166
144 // Remove the thread as a waiter from the lock owner. 167 // If the tag isn't the handle (with wait mask), we're done.
145 { 168 R_SUCCEED_IF(test_tag != (handle | Svc::HandleWaitMask));
146 KScopedSchedulerLock sl(kernel); 169
147 KThread* owner_thread = cur_thread->GetLockOwner(); 170 // Get the lock owner thread.
148 if (owner_thread != nullptr) { 171 owner_thread = kernel.CurrentProcess()
149 owner_thread->RemoveWaiter(cur_thread); 172 ->GetHandleTable()
150 } 173 .GetObjectWithoutPseudoHandle<KThread>(handle)
174 .ReleasePointerUnsafe();
175 R_UNLESS(owner_thread != nullptr, ResultInvalidHandle);
176
177 // Update the lock.
178 cur_thread->SetAddressKey(addr, value);
179 owner_thread->AddWaiter(cur_thread);
180
181 // Begin waiting.
182 cur_thread->BeginWait(std::addressof(wait_queue));
183 cur_thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::ConditionVar);
184 cur_thread->SetMutexWaitAddressForDebugging(addr);
151 } 185 }
152 186
187 // Close our reference to the owner thread, now that the wait is over.
188 owner_thread->Close();
189
153 // Get the wait result. 190 // Get the wait result.
154 KSynchronizationObject* dummy{}; 191 return cur_thread->GetWaitResult();
155 return cur_thread->GetWaitResult(std::addressof(dummy));
156} 192}
157 193
158KThread* KConditionVariable::SignalImpl(KThread* thread) { 194void KConditionVariable::SignalImpl(KThread* thread) {
159 // Check pre-conditions. 195 // Check pre-conditions.
160 ASSERT(kernel.GlobalSchedulerContext().IsLocked()); 196 ASSERT(kernel.GlobalSchedulerContext().IsLocked());
161 197
@@ -169,18 +205,16 @@ KThread* KConditionVariable::SignalImpl(KThread* thread) {
169 // TODO(bunnei): We should disable interrupts here via KScopedInterruptDisable. 205 // TODO(bunnei): We should disable interrupts here via KScopedInterruptDisable.
170 // TODO(bunnei): We should call CanAccessAtomic(..) here. 206 // TODO(bunnei): We should call CanAccessAtomic(..) here.
171 can_access = true; 207 can_access = true;
172 if (can_access) { 208 if (can_access) [[likely]] {
173 UpdateLockAtomic(system, std::addressof(prev_tag), address, own_tag, 209 UpdateLockAtomic(system, std::addressof(prev_tag), address, own_tag,
174 Svc::HandleWaitMask); 210 Svc::HandleWaitMask);
175 } 211 }
176 } 212 }
177 213
178 KThread* thread_to_close = nullptr; 214 if (can_access) [[likely]] {
179 if (can_access) {
180 if (prev_tag == Svc::InvalidHandle) { 215 if (prev_tag == Svc::InvalidHandle) {
181 // If nobody held the lock previously, we're all good. 216 // If nobody held the lock previously, we're all good.
182 thread->SetSyncedObject(nullptr, ResultSuccess); 217 thread->EndWait(ResultSuccess);
183 thread->Wakeup();
184 } else { 218 } else {
185 // Get the previous owner. 219 // Get the previous owner.
186 KThread* owner_thread = kernel.CurrentProcess() 220 KThread* owner_thread = kernel.CurrentProcess()
@@ -189,33 +223,22 @@ KThread* KConditionVariable::SignalImpl(KThread* thread) {
189 static_cast<Handle>(prev_tag & ~Svc::HandleWaitMask)) 223 static_cast<Handle>(prev_tag & ~Svc::HandleWaitMask))
190 .ReleasePointerUnsafe(); 224 .ReleasePointerUnsafe();
191 225
192 if (owner_thread) { 226 if (owner_thread) [[likely]] {
193 // Add the thread as a waiter on the owner. 227 // Add the thread as a waiter on the owner.
194 owner_thread->AddWaiter(thread); 228 owner_thread->AddWaiter(thread);
195 thread_to_close = owner_thread; 229 owner_thread->Close();
196 } else { 230 } else {
197 // The lock was tagged with a thread that doesn't exist. 231 // The lock was tagged with a thread that doesn't exist.
198 thread->SetSyncedObject(nullptr, ResultInvalidState); 232 thread->EndWait(ResultInvalidState);
199 thread->Wakeup();
200 } 233 }
201 } 234 }
202 } else { 235 } else {
203 // If the address wasn't accessible, note so. 236 // If the address wasn't accessible, note so.
204 thread->SetSyncedObject(nullptr, ResultInvalidCurrentMemory); 237 thread->EndWait(ResultInvalidCurrentMemory);
205 thread->Wakeup();
206 } 238 }
207
208 return thread_to_close;
209} 239}
210 240
211void KConditionVariable::Signal(u64 cv_key, s32 count) { 241void KConditionVariable::Signal(u64 cv_key, s32 count) {
212 // Prepare for signaling.
213 constexpr int MaxThreads = 16;
214
215 KLinkedList<KThread> thread_list{kernel};
216 std::array<KThread*, MaxThreads> thread_array;
217 s32 num_to_close{};
218
219 // Perform signaling. 242 // Perform signaling.
220 s32 num_waiters{}; 243 s32 num_waiters{};
221 { 244 {
@@ -226,14 +249,7 @@ void KConditionVariable::Signal(u64 cv_key, s32 count) {
226 (it->GetConditionVariableKey() == cv_key)) { 249 (it->GetConditionVariableKey() == cv_key)) {
227 KThread* target_thread = std::addressof(*it); 250 KThread* target_thread = std::addressof(*it);
228 251
229 if (KThread* thread = SignalImpl(target_thread); thread != nullptr) { 252 this->SignalImpl(target_thread);
230 if (num_to_close < MaxThreads) {
231 thread_array[num_to_close++] = thread;
232 } else {
233 thread_list.push_back(*thread);
234 }
235 }
236
237 it = thread_tree.erase(it); 253 it = thread_tree.erase(it);
238 target_thread->ClearConditionVariable(); 254 target_thread->ClearConditionVariable();
239 ++num_waiters; 255 ++num_waiters;
@@ -245,27 +261,16 @@ void KConditionVariable::Signal(u64 cv_key, s32 count) {
245 WriteToUser(system, cv_key, std::addressof(has_waiter_flag)); 261 WriteToUser(system, cv_key, std::addressof(has_waiter_flag));
246 } 262 }
247 } 263 }
248
249 // Close threads in the array.
250 for (auto i = 0; i < num_to_close; ++i) {
251 thread_array[i]->Close();
252 }
253
254 // Close threads in the list.
255 for (auto it = thread_list.begin(); it != thread_list.end(); it = thread_list.erase(it)) {
256 (*it).Close();
257 }
258} 264}
259 265
260ResultCode KConditionVariable::Wait(VAddr addr, u64 key, u32 value, s64 timeout) { 266ResultCode KConditionVariable::Wait(VAddr addr, u64 key, u32 value, s64 timeout) {
261 // Prepare to wait. 267 // Prepare to wait.
262 KThread* cur_thread = kernel.CurrentScheduler()->GetCurrentThread(); 268 KThread* cur_thread = GetCurrentThreadPointer(kernel);
269 ThreadQueueImplForKConditionVariableWaitConditionVariable wait_queue(
270 kernel, std::addressof(thread_tree));
263 271
264 { 272 {
265 KScopedSchedulerLockAndSleep slp{kernel, cur_thread, timeout}; 273 KScopedSchedulerLockAndSleep slp(kernel, cur_thread, timeout);
266
267 // Set the synced object.
268 cur_thread->SetSyncedObject(nullptr, ResultTimedOut);
269 274
270 // Check that the thread isn't terminating. 275 // Check that the thread isn't terminating.
271 if (cur_thread->IsTerminationRequested()) { 276 if (cur_thread->IsTerminationRequested()) {
@@ -290,8 +295,7 @@ ResultCode KConditionVariable::Wait(VAddr addr, u64 key, u32 value, s64 timeout)
290 } 295 }
291 296
292 // Wake up the next owner. 297 // Wake up the next owner.
293 next_owner_thread->SetSyncedObject(nullptr, ResultSuccess); 298 next_owner_thread->EndWait(ResultSuccess);
294 next_owner_thread->Wakeup();
295 } 299 }
296 300
297 // Write to the cv key. 301 // Write to the cv key.
@@ -308,40 +312,21 @@ ResultCode KConditionVariable::Wait(VAddr addr, u64 key, u32 value, s64 timeout)
308 } 312 }
309 } 313 }
310 314
311 // Update condition variable tracking. 315 // If timeout is zero, time out.
312 { 316 R_UNLESS(timeout != 0, ResultTimedOut);
313 cur_thread->SetConditionVariable(std::addressof(thread_tree), addr, key, value);
314 thread_tree.insert(*cur_thread);
315 }
316 317
317 // If the timeout is non-zero, set the thread as waiting. 318 // Update condition variable tracking.
318 if (timeout != 0) { 319 cur_thread->SetConditionVariable(std::addressof(thread_tree), addr, key, value);
319 cur_thread->SetState(ThreadState::Waiting); 320 thread_tree.insert(*cur_thread);
320 cur_thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::ConditionVar);
321 cur_thread->SetMutexWaitAddressForDebugging(addr);
322 }
323 }
324
325 // Cancel the timer wait.
326 kernel.TimeManager().UnscheduleTimeEvent(cur_thread);
327
328 // Remove from the condition variable.
329 {
330 KScopedSchedulerLock sl(kernel);
331
332 if (KThread* owner = cur_thread->GetLockOwner(); owner != nullptr) {
333 owner->RemoveWaiter(cur_thread);
334 }
335 321
336 if (cur_thread->IsWaitingForConditionVariable()) { 322 // Begin waiting.
337 thread_tree.erase(thread_tree.iterator_to(*cur_thread)); 323 cur_thread->BeginWait(std::addressof(wait_queue));
338 cur_thread->ClearConditionVariable(); 324 cur_thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::ConditionVar);
339 } 325 cur_thread->SetMutexWaitAddressForDebugging(addr);
340 } 326 }
341 327
342 // Get the result. 328 // Get the wait result.
343 KSynchronizationObject* dummy{}; 329 return cur_thread->GetWaitResult();
344 return cur_thread->GetWaitResult(std::addressof(dummy));
345} 330}
346 331
347} // namespace Kernel 332} // namespace Kernel
diff --git a/src/core/hle/kernel/k_condition_variable.h b/src/core/hle/kernel/k_condition_variable.h
index 861dbd420..5e4815d08 100644
--- a/src/core/hle/kernel/k_condition_variable.h
+++ b/src/core/hle/kernel/k_condition_variable.h
@@ -34,7 +34,7 @@ public:
34 [[nodiscard]] ResultCode Wait(VAddr addr, u64 key, u32 value, s64 timeout); 34 [[nodiscard]] ResultCode Wait(VAddr addr, u64 key, u32 value, s64 timeout);
35 35
36private: 36private:
37 [[nodiscard]] KThread* SignalImpl(KThread* thread); 37 void SignalImpl(KThread* thread);
38 38
39 ThreadTree thread_tree; 39 ThreadTree thread_tree;
40 40
diff --git a/src/core/hle/kernel/k_handle_table.cpp b/src/core/hle/kernel/k_handle_table.cpp
index e90fc0628..cf95f0852 100644
--- a/src/core/hle/kernel/k_handle_table.cpp
+++ b/src/core/hle/kernel/k_handle_table.cpp
@@ -13,6 +13,7 @@ ResultCode KHandleTable::Finalize() {
13 // Get the table and clear our record of it. 13 // Get the table and clear our record of it.
14 u16 saved_table_size = 0; 14 u16 saved_table_size = 0;
15 { 15 {
16 KScopedDisableDispatch dd(kernel);
16 KScopedSpinLock lk(m_lock); 17 KScopedSpinLock lk(m_lock);
17 18
18 std::swap(m_table_size, saved_table_size); 19 std::swap(m_table_size, saved_table_size);
@@ -43,6 +44,7 @@ bool KHandleTable::Remove(Handle handle) {
43 // Find the object and free the entry. 44 // Find the object and free the entry.
44 KAutoObject* obj = nullptr; 45 KAutoObject* obj = nullptr;
45 { 46 {
47 KScopedDisableDispatch dd(kernel);
46 KScopedSpinLock lk(m_lock); 48 KScopedSpinLock lk(m_lock);
47 49
48 if (this->IsValidHandle(handle)) { 50 if (this->IsValidHandle(handle)) {
@@ -62,6 +64,7 @@ bool KHandleTable::Remove(Handle handle) {
62} 64}
63 65
64ResultCode KHandleTable::Add(Handle* out_handle, KAutoObject* obj, u16 type) { 66ResultCode KHandleTable::Add(Handle* out_handle, KAutoObject* obj, u16 type) {
67 KScopedDisableDispatch dd(kernel);
65 KScopedSpinLock lk(m_lock); 68 KScopedSpinLock lk(m_lock);
66 69
67 // Never exceed our capacity. 70 // Never exceed our capacity.
@@ -84,6 +87,7 @@ ResultCode KHandleTable::Add(Handle* out_handle, KAutoObject* obj, u16 type) {
84} 87}
85 88
86ResultCode KHandleTable::Reserve(Handle* out_handle) { 89ResultCode KHandleTable::Reserve(Handle* out_handle) {
90 KScopedDisableDispatch dd(kernel);
87 KScopedSpinLock lk(m_lock); 91 KScopedSpinLock lk(m_lock);
88 92
89 // Never exceed our capacity. 93 // Never exceed our capacity.
@@ -94,6 +98,7 @@ ResultCode KHandleTable::Reserve(Handle* out_handle) {
94} 98}
95 99
96void KHandleTable::Unreserve(Handle handle) { 100void KHandleTable::Unreserve(Handle handle) {
101 KScopedDisableDispatch dd(kernel);
97 KScopedSpinLock lk(m_lock); 102 KScopedSpinLock lk(m_lock);
98 103
99 // Unpack the handle. 104 // Unpack the handle.
@@ -112,6 +117,7 @@ void KHandleTable::Unreserve(Handle handle) {
112} 117}
113 118
114void KHandleTable::Register(Handle handle, KAutoObject* obj, u16 type) { 119void KHandleTable::Register(Handle handle, KAutoObject* obj, u16 type) {
120 KScopedDisableDispatch dd(kernel);
115 KScopedSpinLock lk(m_lock); 121 KScopedSpinLock lk(m_lock);
116 122
117 // Unpack the handle. 123 // Unpack the handle.
diff --git a/src/core/hle/kernel/k_handle_table.h b/src/core/hle/kernel/k_handle_table.h
index 95ec905ae..4b114ec2f 100644
--- a/src/core/hle/kernel/k_handle_table.h
+++ b/src/core/hle/kernel/k_handle_table.h
@@ -68,6 +68,7 @@ public:
68 template <typename T = KAutoObject> 68 template <typename T = KAutoObject>
69 KScopedAutoObject<T> GetObjectWithoutPseudoHandle(Handle handle) const { 69 KScopedAutoObject<T> GetObjectWithoutPseudoHandle(Handle handle) const {
70 // Lock and look up in table. 70 // Lock and look up in table.
71 KScopedDisableDispatch dd(kernel);
71 KScopedSpinLock lk(m_lock); 72 KScopedSpinLock lk(m_lock);
72 73
73 if constexpr (std::is_same_v<T, KAutoObject>) { 74 if constexpr (std::is_same_v<T, KAutoObject>) {
@@ -122,6 +123,7 @@ public:
122 size_t num_opened; 123 size_t num_opened;
123 { 124 {
124 // Lock the table. 125 // Lock the table.
126 KScopedDisableDispatch dd(kernel);
125 KScopedSpinLock lk(m_lock); 127 KScopedSpinLock lk(m_lock);
126 for (num_opened = 0; num_opened < num_handles; num_opened++) { 128 for (num_opened = 0; num_opened < num_handles; num_opened++) {
127 // Get the current handle. 129 // Get the current handle.
diff --git a/src/core/hle/kernel/k_light_condition_variable.cpp b/src/core/hle/kernel/k_light_condition_variable.cpp
new file mode 100644
index 000000000..a8001fffc
--- /dev/null
+++ b/src/core/hle/kernel/k_light_condition_variable.cpp
@@ -0,0 +1,80 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/hle/kernel/k_light_condition_variable.h"
6#include "core/hle/kernel/k_scheduler.h"
7#include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h"
8#include "core/hle/kernel/k_thread_queue.h"
9#include "core/hle/kernel/svc_results.h"
10
11namespace Kernel {
12
13namespace {
14
15class ThreadQueueImplForKLightConditionVariable final : public KThreadQueue {
16public:
17 ThreadQueueImplForKLightConditionVariable(KernelCore& kernel_, KThread::WaiterList* wl,
18 bool term)
19 : KThreadQueue(kernel_), m_wait_list(wl), m_allow_terminating_thread(term) {}
20
21 void CancelWait(KThread* waiting_thread, ResultCode wait_result,
22 bool cancel_timer_task) override {
23 // Only process waits if we're allowed to.
24 if (ResultTerminationRequested == wait_result && m_allow_terminating_thread) {
25 return;
26 }
27
28 // Remove the thread from the waiting thread from the light condition variable.
29 m_wait_list->erase(m_wait_list->iterator_to(*waiting_thread));
30
31 // Invoke the base cancel wait handler.
32 KThreadQueue::CancelWait(waiting_thread, wait_result, cancel_timer_task);
33 }
34
35private:
36 KThread::WaiterList* m_wait_list;
37 bool m_allow_terminating_thread;
38};
39
40} // namespace
41
42void KLightConditionVariable::Wait(KLightLock* lock, s64 timeout, bool allow_terminating_thread) {
43 // Create thread queue.
44 KThread* owner = GetCurrentThreadPointer(kernel);
45
46 ThreadQueueImplForKLightConditionVariable wait_queue(kernel, std::addressof(wait_list),
47 allow_terminating_thread);
48
49 // Sleep the thread.
50 {
51 KScopedSchedulerLockAndSleep lk(kernel, owner, timeout);
52
53 if (!allow_terminating_thread && owner->IsTerminationRequested()) {
54 lk.CancelSleep();
55 return;
56 }
57
58 lock->Unlock();
59
60 // Add the thread to the queue.
61 wait_list.push_back(*owner);
62
63 // Begin waiting.
64 owner->BeginWait(std::addressof(wait_queue));
65 }
66
67 // Re-acquire the lock.
68 lock->Lock();
69}
70
71void KLightConditionVariable::Broadcast() {
72 KScopedSchedulerLock lk(kernel);
73
74 // Signal all threads.
75 for (auto it = wait_list.begin(); it != wait_list.end(); it = wait_list.erase(it)) {
76 it->EndWait(ResultSuccess);
77 }
78}
79
80} // namespace Kernel
diff --git a/src/core/hle/kernel/k_light_condition_variable.h b/src/core/hle/kernel/k_light_condition_variable.h
index fb0ad783a..5d6d7f128 100644
--- a/src/core/hle/kernel/k_light_condition_variable.h
+++ b/src/core/hle/kernel/k_light_condition_variable.h
@@ -2,72 +2,24 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5// This file references various implementation details from Atmosphere, an open-source firmware for
6// the Nintendo Switch. Copyright 2018-2020 Atmosphere-NX.
7
8#pragma once 5#pragma once
9 6
10#include "common/common_types.h" 7#include "common/common_types.h"
11#include "core/hle/kernel/k_scheduler.h" 8#include "core/hle/kernel/k_thread.h"
12#include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h"
13#include "core/hle/kernel/time_manager.h"
14 9
15namespace Kernel { 10namespace Kernel {
11
16class KernelCore; 12class KernelCore;
13class KLightLock;
17 14
18class KLightConditionVariable { 15class KLightConditionVariable {
19public: 16public:
20 explicit KLightConditionVariable(KernelCore& kernel_) : kernel{kernel_} {} 17 explicit KLightConditionVariable(KernelCore& kernel_) : kernel{kernel_} {}
21 18
22 void Wait(KLightLock* lock, s64 timeout = -1, bool allow_terminating_thread = true) { 19 void Wait(KLightLock* lock, s64 timeout = -1, bool allow_terminating_thread = true);
23 WaitImpl(lock, timeout, allow_terminating_thread); 20 void Broadcast();
24 }
25
26 void Broadcast() {
27 KScopedSchedulerLock lk{kernel};
28
29 // Signal all threads.
30 for (auto& thread : wait_list) {
31 thread.SetState(ThreadState::Runnable);
32 }
33 }
34 21
35private: 22private:
36 void WaitImpl(KLightLock* lock, s64 timeout, bool allow_terminating_thread) {
37 KThread* owner = GetCurrentThreadPointer(kernel);
38
39 // Sleep the thread.
40 {
41 KScopedSchedulerLockAndSleep lk{kernel, owner, timeout};
42
43 if (!allow_terminating_thread && owner->IsTerminationRequested()) {
44 lk.CancelSleep();
45 return;
46 }
47
48 lock->Unlock();
49
50 // Set the thread as waiting.
51 GetCurrentThread(kernel).SetState(ThreadState::Waiting);
52
53 // Add the thread to the queue.
54 wait_list.push_back(GetCurrentThread(kernel));
55 }
56
57 // Remove the thread from the wait list.
58 {
59 KScopedSchedulerLock sl{kernel};
60
61 wait_list.erase(wait_list.iterator_to(GetCurrentThread(kernel)));
62 }
63
64 // Cancel the task that the sleep setup.
65 kernel.TimeManager().UnscheduleTimeEvent(owner);
66
67 // Re-acquire the lock.
68 lock->Lock();
69 }
70
71 KernelCore& kernel; 23 KernelCore& kernel;
72 KThread::WaiterList wait_list{}; 24 KThread::WaiterList wait_list{};
73}; 25};
diff --git a/src/core/hle/kernel/k_light_lock.cpp b/src/core/hle/kernel/k_light_lock.cpp
index 0896e705f..4620342eb 100644
--- a/src/core/hle/kernel/k_light_lock.cpp
+++ b/src/core/hle/kernel/k_light_lock.cpp
@@ -5,44 +5,59 @@
5#include "core/hle/kernel/k_light_lock.h" 5#include "core/hle/kernel/k_light_lock.h"
6#include "core/hle/kernel/k_scheduler.h" 6#include "core/hle/kernel/k_scheduler.h"
7#include "core/hle/kernel/k_thread.h" 7#include "core/hle/kernel/k_thread.h"
8#include "core/hle/kernel/k_thread_queue.h"
8#include "core/hle/kernel/kernel.h" 9#include "core/hle/kernel/kernel.h"
9 10
10namespace Kernel { 11namespace Kernel {
11 12
13namespace {
14
15class ThreadQueueImplForKLightLock final : public KThreadQueue {
16public:
17 explicit ThreadQueueImplForKLightLock(KernelCore& kernel_) : KThreadQueue(kernel_) {}
18
19 void CancelWait(KThread* waiting_thread, ResultCode wait_result,
20 bool cancel_timer_task) override {
21 // Remove the thread as a waiter from its owner.
22 if (KThread* owner = waiting_thread->GetLockOwner(); owner != nullptr) {
23 owner->RemoveWaiter(waiting_thread);
24 }
25
26 // Invoke the base cancel wait handler.
27 KThreadQueue::CancelWait(waiting_thread, wait_result, cancel_timer_task);
28 }
29};
30
31} // namespace
32
12void KLightLock::Lock() { 33void KLightLock::Lock() {
13 const uintptr_t cur_thread = reinterpret_cast<uintptr_t>(GetCurrentThreadPointer(kernel)); 34 const uintptr_t cur_thread = reinterpret_cast<uintptr_t>(GetCurrentThreadPointer(kernel));
14 const uintptr_t cur_thread_tag = (cur_thread | 1);
15 35
16 while (true) { 36 while (true) {
17 uintptr_t old_tag = tag.load(std::memory_order_relaxed); 37 uintptr_t old_tag = tag.load(std::memory_order_relaxed);
18 38
19 while (!tag.compare_exchange_weak(old_tag, (old_tag == 0) ? cur_thread : old_tag | 1, 39 while (!tag.compare_exchange_weak(old_tag, (old_tag == 0) ? cur_thread : (old_tag | 1),
20 std::memory_order_acquire)) { 40 std::memory_order_acquire)) {
21 if ((old_tag | 1) == cur_thread_tag) {
22 return;
23 }
24 } 41 }
25 42
26 if ((old_tag == 0) || ((old_tag | 1) == cur_thread_tag)) { 43 if (old_tag == 0 || this->LockSlowPath(old_tag | 1, cur_thread)) {
27 break; 44 break;
28 } 45 }
29
30 LockSlowPath(old_tag | 1, cur_thread);
31 } 46 }
32} 47}
33 48
34void KLightLock::Unlock() { 49void KLightLock::Unlock() {
35 const uintptr_t cur_thread = reinterpret_cast<uintptr_t>(GetCurrentThreadPointer(kernel)); 50 const uintptr_t cur_thread = reinterpret_cast<uintptr_t>(GetCurrentThreadPointer(kernel));
51
36 uintptr_t expected = cur_thread; 52 uintptr_t expected = cur_thread;
37 do { 53 if (!tag.compare_exchange_strong(expected, 0, std::memory_order_release)) {
38 if (expected != cur_thread) { 54 this->UnlockSlowPath(cur_thread);
39 return UnlockSlowPath(cur_thread); 55 }
40 }
41 } while (!tag.compare_exchange_weak(expected, 0, std::memory_order_release));
42} 56}
43 57
44void KLightLock::LockSlowPath(uintptr_t _owner, uintptr_t _cur_thread) { 58bool KLightLock::LockSlowPath(uintptr_t _owner, uintptr_t _cur_thread) {
45 KThread* cur_thread = reinterpret_cast<KThread*>(_cur_thread); 59 KThread* cur_thread = reinterpret_cast<KThread*>(_cur_thread);
60 ThreadQueueImplForKLightLock wait_queue(kernel);
46 61
47 // Pend the current thread waiting on the owner thread. 62 // Pend the current thread waiting on the owner thread.
48 { 63 {
@@ -50,7 +65,7 @@ void KLightLock::LockSlowPath(uintptr_t _owner, uintptr_t _cur_thread) {
50 65
51 // Ensure we actually have locking to do. 66 // Ensure we actually have locking to do.
52 if (tag.load(std::memory_order_relaxed) != _owner) { 67 if (tag.load(std::memory_order_relaxed) != _owner) {
53 return; 68 return false;
54 } 69 }
55 70
56 // Add the current thread as a waiter on the owner. 71 // Add the current thread as a waiter on the owner.
@@ -58,22 +73,15 @@ void KLightLock::LockSlowPath(uintptr_t _owner, uintptr_t _cur_thread) {
58 cur_thread->SetAddressKey(reinterpret_cast<uintptr_t>(std::addressof(tag))); 73 cur_thread->SetAddressKey(reinterpret_cast<uintptr_t>(std::addressof(tag)));
59 owner_thread->AddWaiter(cur_thread); 74 owner_thread->AddWaiter(cur_thread);
60 75
61 // Set thread states. 76 // Begin waiting to hold the lock.
62 cur_thread->SetState(ThreadState::Waiting); 77 cur_thread->BeginWait(std::addressof(wait_queue));
63 78
64 if (owner_thread->IsSuspended()) { 79 if (owner_thread->IsSuspended()) {
65 owner_thread->ContinueIfHasKernelWaiters(); 80 owner_thread->ContinueIfHasKernelWaiters();
66 } 81 }
67 } 82 }
68 83
69 // We're no longer waiting on the lock owner. 84 return true;
70 {
71 KScopedSchedulerLock sl{kernel};
72
73 if (KThread* owner_thread = cur_thread->GetLockOwner(); owner_thread != nullptr) {
74 owner_thread->RemoveWaiter(cur_thread);
75 }
76 }
77} 85}
78 86
79void KLightLock::UnlockSlowPath(uintptr_t _cur_thread) { 87void KLightLock::UnlockSlowPath(uintptr_t _cur_thread) {
@@ -81,22 +89,20 @@ void KLightLock::UnlockSlowPath(uintptr_t _cur_thread) {
81 89
82 // Unlock. 90 // Unlock.
83 { 91 {
84 KScopedSchedulerLock sl{kernel}; 92 KScopedSchedulerLock sl(kernel);
85 93
86 // Get the next owner. 94 // Get the next owner.
87 s32 num_waiters = 0; 95 s32 num_waiters;
88 KThread* next_owner = owner_thread->RemoveWaiterByKey( 96 KThread* next_owner = owner_thread->RemoveWaiterByKey(
89 std::addressof(num_waiters), reinterpret_cast<uintptr_t>(std::addressof(tag))); 97 std::addressof(num_waiters), reinterpret_cast<uintptr_t>(std::addressof(tag)));
90 98
91 // Pass the lock to the next owner. 99 // Pass the lock to the next owner.
92 uintptr_t next_tag = 0; 100 uintptr_t next_tag = 0;
93 if (next_owner != nullptr) { 101 if (next_owner != nullptr) {
94 next_tag = reinterpret_cast<uintptr_t>(next_owner); 102 next_tag =
95 if (num_waiters > 1) { 103 reinterpret_cast<uintptr_t>(next_owner) | static_cast<uintptr_t>(num_waiters > 1);
96 next_tag |= 0x1;
97 }
98 104
99 next_owner->SetState(ThreadState::Runnable); 105 next_owner->EndWait(ResultSuccess);
100 106
101 if (next_owner->IsSuspended()) { 107 if (next_owner->IsSuspended()) {
102 next_owner->ContinueIfHasKernelWaiters(); 108 next_owner->ContinueIfHasKernelWaiters();
@@ -110,7 +116,7 @@ void KLightLock::UnlockSlowPath(uintptr_t _cur_thread) {
110 } 116 }
111 117
112 // Write the new tag value. 118 // Write the new tag value.
113 tag.store(next_tag); 119 tag.store(next_tag, std::memory_order_release);
114 } 120 }
115} 121}
116 122
diff --git a/src/core/hle/kernel/k_light_lock.h b/src/core/hle/kernel/k_light_lock.h
index ad853661d..4163b8a85 100644
--- a/src/core/hle/kernel/k_light_lock.h
+++ b/src/core/hle/kernel/k_light_lock.h
@@ -20,7 +20,7 @@ public:
20 20
21 void Unlock(); 21 void Unlock();
22 22
23 void LockSlowPath(uintptr_t owner, uintptr_t cur_thread); 23 bool LockSlowPath(uintptr_t owner, uintptr_t cur_thread);
24 24
25 void UnlockSlowPath(uintptr_t cur_thread); 25 void UnlockSlowPath(uintptr_t cur_thread);
26 26
diff --git a/src/core/hle/kernel/k_memory_block.h b/src/core/hle/kernel/k_memory_block.h
index a7fdb5fb8..fd491146f 100644
--- a/src/core/hle/kernel/k_memory_block.h
+++ b/src/core/hle/kernel/k_memory_block.h
@@ -131,6 +131,26 @@ enum class KMemoryPermission : u8 {
131 131
132 UserMask = static_cast<u8>(Svc::MemoryPermission::Read | Svc::MemoryPermission::Write | 132 UserMask = static_cast<u8>(Svc::MemoryPermission::Read | Svc::MemoryPermission::Write |
133 Svc::MemoryPermission::Execute), 133 Svc::MemoryPermission::Execute),
134
135 KernelShift = 3,
136
137 KernelRead = Read << KernelShift,
138 KernelWrite = Write << KernelShift,
139 KernelExecute = Execute << KernelShift,
140
141 NotMapped = (1 << (2 * KernelShift)),
142
143 KernelReadWrite = KernelRead | KernelWrite,
144 KernelReadExecute = KernelRead | KernelExecute,
145
146 UserRead = Read | KernelRead,
147 UserWrite = Write | KernelWrite,
148 UserExecute = Execute,
149
150 UserReadWrite = UserRead | UserWrite,
151 UserReadExecute = UserRead | UserExecute,
152
153 IpcLockChangeMask = NotMapped | UserReadWrite
134}; 154};
135DECLARE_ENUM_FLAG_OPERATORS(KMemoryPermission); 155DECLARE_ENUM_FLAG_OPERATORS(KMemoryPermission);
136 156
diff --git a/src/core/hle/kernel/k_page_linked_list.h b/src/core/hle/kernel/k_page_linked_list.h
index 3362fb236..0e2ae582a 100644
--- a/src/core/hle/kernel/k_page_linked_list.h
+++ b/src/core/hle/kernel/k_page_linked_list.h
@@ -27,6 +27,10 @@ public:
27 return num_pages; 27 return num_pages;
28 } 28 }
29 29
30 constexpr std::size_t GetSize() const {
31 return GetNumPages() * PageSize;
32 }
33
30 private: 34 private:
31 u64 addr{}; 35 u64 addr{};
32 std::size_t num_pages{}; 36 std::size_t num_pages{};
diff --git a/src/core/hle/kernel/k_page_table.cpp b/src/core/hle/kernel/k_page_table.cpp
index 526b87241..99982e5a3 100644
--- a/src/core/hle/kernel/k_page_table.cpp
+++ b/src/core/hle/kernel/k_page_table.cpp
@@ -368,6 +368,33 @@ ResultCode KPageTable::UnmapProcessCodeMemory(VAddr dst_addr, VAddr src_addr, st
368 return ResultSuccess; 368 return ResultSuccess;
369} 369}
370 370
371ResultCode KPageTable::UnmapProcessMemory(VAddr dst_addr, std::size_t size,
372 KPageTable& src_page_table, VAddr src_addr) {
373 std::lock_guard lock{page_table_lock};
374
375 const std::size_t num_pages{size / PageSize};
376
377 // Check that the memory is mapped in the destination process.
378 size_t num_allocator_blocks;
379 R_TRY(CheckMemoryState(&num_allocator_blocks, dst_addr, size, KMemoryState::All,
380 KMemoryState::SharedCode, KMemoryPermission::UserReadWrite,
381 KMemoryPermission::UserReadWrite, KMemoryAttribute::All,
382 KMemoryAttribute::None));
383
384 // Check that the memory is mapped in the source process.
385 R_TRY(src_page_table.CheckMemoryState(src_addr, size, KMemoryState::FlagCanMapProcess,
386 KMemoryState::FlagCanMapProcess, KMemoryPermission::None,
387 KMemoryPermission::None, KMemoryAttribute::All,
388 KMemoryAttribute::None));
389
390 CASCADE_CODE(Operate(dst_addr, num_pages, KMemoryPermission::None, OperationType::Unmap));
391
392 // Apply the memory block update.
393 block_manager->Update(dst_addr, num_pages, KMemoryState::Free, KMemoryPermission::None,
394 KMemoryAttribute::None);
395
396 return ResultSuccess;
397}
371void KPageTable::MapPhysicalMemory(KPageLinkedList& page_linked_list, VAddr start, VAddr end) { 398void KPageTable::MapPhysicalMemory(KPageLinkedList& page_linked_list, VAddr start, VAddr end) {
372 auto node{page_linked_list.Nodes().begin()}; 399 auto node{page_linked_list.Nodes().begin()};
373 PAddr map_addr{node->GetAddress()}; 400 PAddr map_addr{node->GetAddress()};
@@ -685,8 +712,8 @@ ResultCode KPageTable::UnmapPages(VAddr addr, KPageLinkedList& page_linked_list,
685 return ResultSuccess; 712 return ResultSuccess;
686} 713}
687 714
688ResultCode KPageTable::SetCodeMemoryPermission(VAddr addr, std::size_t size, 715ResultCode KPageTable::SetProcessMemoryPermission(VAddr addr, std::size_t size,
689 KMemoryPermission perm) { 716 KMemoryPermission perm) {
690 717
691 std::lock_guard lock{page_table_lock}; 718 std::lock_guard lock{page_table_lock};
692 719
@@ -942,6 +969,60 @@ ResultCode KPageTable::UnlockForDeviceAddressSpace(VAddr addr, std::size_t size)
942 return ResultSuccess; 969 return ResultSuccess;
943} 970}
944 971
972ResultCode KPageTable::LockForCodeMemory(VAddr addr, std::size_t size) {
973 std::lock_guard lock{page_table_lock};
974
975 KMemoryPermission new_perm = KMemoryPermission::NotMapped | KMemoryPermission::KernelReadWrite;
976
977 KMemoryPermission old_perm{};
978
979 if (const ResultCode result{CheckMemoryState(
980 nullptr, &old_perm, nullptr, addr, size, KMemoryState::FlagCanCodeMemory,
981 KMemoryState::FlagCanCodeMemory, KMemoryPermission::Mask,
982 KMemoryPermission::UserReadWrite, KMemoryAttribute::All, KMemoryAttribute::None)};
983 result.IsError()) {
984 return result;
985 }
986
987 new_perm = (new_perm != KMemoryPermission::None) ? new_perm : old_perm;
988
989 block_manager->UpdateLock(
990 addr, size / PageSize,
991 [](KMemoryBlockManager::iterator block, KMemoryPermission permission) {
992 block->ShareToDevice(permission);
993 },
994 new_perm);
995
996 return ResultSuccess;
997}
998
999ResultCode KPageTable::UnlockForCodeMemory(VAddr addr, std::size_t size) {
1000 std::lock_guard lock{page_table_lock};
1001
1002 KMemoryPermission new_perm = KMemoryPermission::UserReadWrite;
1003
1004 KMemoryPermission old_perm{};
1005
1006 if (const ResultCode result{CheckMemoryState(
1007 nullptr, &old_perm, nullptr, addr, size, KMemoryState::FlagCanCodeMemory,
1008 KMemoryState::FlagCanCodeMemory, KMemoryPermission::None, KMemoryPermission::None,
1009 KMemoryAttribute::All, KMemoryAttribute::Locked)};
1010 result.IsError()) {
1011 return result;
1012 }
1013
1014 new_perm = (new_perm != KMemoryPermission::None) ? new_perm : old_perm;
1015
1016 block_manager->UpdateLock(
1017 addr, size / PageSize,
1018 [](KMemoryBlockManager::iterator block, KMemoryPermission permission) {
1019 block->UnshareToDevice(permission);
1020 },
1021 new_perm);
1022
1023 return ResultSuccess;
1024}
1025
945ResultCode KPageTable::InitializeMemoryLayout(VAddr start, VAddr end) { 1026ResultCode KPageTable::InitializeMemoryLayout(VAddr start, VAddr end) {
946 block_manager = std::make_unique<KMemoryBlockManager>(start, end); 1027 block_manager = std::make_unique<KMemoryBlockManager>(start, end);
947 1028
@@ -1231,4 +1312,42 @@ ResultCode KPageTable::CheckMemoryState(KMemoryState* out_state, KMemoryPermissi
1231 return ResultSuccess; 1312 return ResultSuccess;
1232} 1313}
1233 1314
1315ResultCode KPageTable::CheckMemoryState(size_t* out_blocks_needed, VAddr addr, size_t size,
1316 KMemoryState state_mask, KMemoryState state,
1317 KMemoryPermission perm_mask, KMemoryPermission perm,
1318 KMemoryAttribute attr_mask, KMemoryAttribute attr) const {
1319 // Get information about the first block.
1320 const VAddr last_addr = addr + size - 1;
1321 KMemoryBlockManager::const_iterator it{block_manager->FindIterator(addr)};
1322 KMemoryInfo info = it->GetMemoryInfo();
1323
1324 // If the start address isn't aligned, we need a block.
1325 const size_t blocks_for_start_align =
1326 (Common::AlignDown(addr, PageSize) != info.GetAddress()) ? 1 : 0;
1327
1328 while (true) {
1329 // Validate against the provided masks.
1330 R_TRY(CheckMemoryState(info, state_mask, state, perm_mask, perm, attr_mask, attr));
1331
1332 // Break once we're done.
1333 if (last_addr <= info.GetLastAddress()) {
1334 break;
1335 }
1336
1337 // Advance our iterator.
1338 it++;
1339 info = it->GetMemoryInfo();
1340 }
1341
1342 // If the end address isn't aligned, we need a block.
1343 const size_t blocks_for_end_align =
1344 (Common::AlignUp(addr + size, PageSize) != info.GetEndAddress()) ? 1 : 0;
1345
1346 if (out_blocks_needed != nullptr) {
1347 *out_blocks_needed = blocks_for_start_align + blocks_for_end_align;
1348 }
1349
1350 return ResultSuccess;
1351}
1352
1234} // namespace Kernel 1353} // namespace Kernel
diff --git a/src/core/hle/kernel/k_page_table.h b/src/core/hle/kernel/k_page_table.h
index 770c4841c..d784aa67e 100644
--- a/src/core/hle/kernel/k_page_table.h
+++ b/src/core/hle/kernel/k_page_table.h
@@ -33,6 +33,8 @@ public:
33 KMemoryPermission perm); 33 KMemoryPermission perm);
34 ResultCode MapProcessCodeMemory(VAddr dst_addr, VAddr src_addr, std::size_t size); 34 ResultCode MapProcessCodeMemory(VAddr dst_addr, VAddr src_addr, std::size_t size);
35 ResultCode UnmapProcessCodeMemory(VAddr dst_addr, VAddr src_addr, std::size_t size); 35 ResultCode UnmapProcessCodeMemory(VAddr dst_addr, VAddr src_addr, std::size_t size);
36 ResultCode UnmapProcessMemory(VAddr dst_addr, std::size_t size, KPageTable& src_page_table,
37 VAddr src_addr);
36 ResultCode MapPhysicalMemory(VAddr addr, std::size_t size); 38 ResultCode MapPhysicalMemory(VAddr addr, std::size_t size);
37 ResultCode UnmapPhysicalMemory(VAddr addr, std::size_t size); 39 ResultCode UnmapPhysicalMemory(VAddr addr, std::size_t size);
38 ResultCode UnmapMemory(VAddr addr, std::size_t size); 40 ResultCode UnmapMemory(VAddr addr, std::size_t size);
@@ -41,7 +43,7 @@ public:
41 ResultCode MapPages(VAddr addr, KPageLinkedList& page_linked_list, KMemoryState state, 43 ResultCode MapPages(VAddr addr, KPageLinkedList& page_linked_list, KMemoryState state,
42 KMemoryPermission perm); 44 KMemoryPermission perm);
43 ResultCode UnmapPages(VAddr addr, KPageLinkedList& page_linked_list, KMemoryState state); 45 ResultCode UnmapPages(VAddr addr, KPageLinkedList& page_linked_list, KMemoryState state);
44 ResultCode SetCodeMemoryPermission(VAddr addr, std::size_t size, KMemoryPermission perm); 46 ResultCode SetProcessMemoryPermission(VAddr addr, std::size_t size, KMemoryPermission perm);
45 KMemoryInfo QueryInfo(VAddr addr); 47 KMemoryInfo QueryInfo(VAddr addr);
46 ResultCode ReserveTransferMemory(VAddr addr, std::size_t size, KMemoryPermission perm); 48 ResultCode ReserveTransferMemory(VAddr addr, std::size_t size, KMemoryPermission perm);
47 ResultCode ResetTransferMemory(VAddr addr, std::size_t size); 49 ResultCode ResetTransferMemory(VAddr addr, std::size_t size);
@@ -55,6 +57,8 @@ public:
55 KMemoryPermission perm, PAddr map_addr = 0); 57 KMemoryPermission perm, PAddr map_addr = 0);
56 ResultCode LockForDeviceAddressSpace(VAddr addr, std::size_t size); 58 ResultCode LockForDeviceAddressSpace(VAddr addr, std::size_t size);
57 ResultCode UnlockForDeviceAddressSpace(VAddr addr, std::size_t size); 59 ResultCode UnlockForDeviceAddressSpace(VAddr addr, std::size_t size);
60 ResultCode LockForCodeMemory(VAddr addr, std::size_t size);
61 ResultCode UnlockForCodeMemory(VAddr addr, std::size_t size);
58 62
59 Common::PageTable& PageTableImpl() { 63 Common::PageTable& PageTableImpl() {
60 return page_table_impl; 64 return page_table_impl;
@@ -115,6 +119,10 @@ private:
115 return CheckMemoryState(nullptr, nullptr, nullptr, addr, size, state_mask, state, perm_mask, 119 return CheckMemoryState(nullptr, nullptr, nullptr, addr, size, state_mask, state, perm_mask,
116 perm, attr_mask, attr, ignore_attr); 120 perm, attr_mask, attr, ignore_attr);
117 } 121 }
122 ResultCode CheckMemoryState(size_t* out_blocks_needed, VAddr addr, size_t size,
123 KMemoryState state_mask, KMemoryState state,
124 KMemoryPermission perm_mask, KMemoryPermission perm,
125 KMemoryAttribute attr_mask, KMemoryAttribute attr) const;
118 126
119 std::recursive_mutex page_table_lock; 127 std::recursive_mutex page_table_lock;
120 std::unique_ptr<KMemoryBlockManager> block_manager; 128 std::unique_ptr<KMemoryBlockManager> block_manager;
diff --git a/src/core/hle/kernel/k_process.cpp b/src/core/hle/kernel/k_process.cpp
index 76fd8c285..90dda40dc 100644
--- a/src/core/hle/kernel/k_process.cpp
+++ b/src/core/hle/kernel/k_process.cpp
@@ -60,6 +60,7 @@ void SetupMainThread(Core::System& system, KProcess& owner_process, u32 priority
60 thread->GetContext64().cpu_registers[0] = 0; 60 thread->GetContext64().cpu_registers[0] = 0;
61 thread->GetContext32().cpu_registers[1] = thread_handle; 61 thread->GetContext32().cpu_registers[1] = thread_handle;
62 thread->GetContext64().cpu_registers[1] = thread_handle; 62 thread->GetContext64().cpu_registers[1] = thread_handle;
63 thread->DisableDispatch();
63 64
64 auto& kernel = system.Kernel(); 65 auto& kernel = system.Kernel();
65 // Threads by default are dormant, wake up the main thread so it runs when the scheduler fires 66 // Threads by default are dormant, wake up the main thread so it runs when the scheduler fires
@@ -227,12 +228,15 @@ void KProcess::PinCurrentThread() {
227 const s32 core_id = GetCurrentCoreId(kernel); 228 const s32 core_id = GetCurrentCoreId(kernel);
228 KThread* cur_thread = GetCurrentThreadPointer(kernel); 229 KThread* cur_thread = GetCurrentThreadPointer(kernel);
229 230
230 // Pin it. 231 // If the thread isn't terminated, pin it.
231 PinThread(core_id, cur_thread); 232 if (!cur_thread->IsTerminationRequested()) {
232 cur_thread->Pin(); 233 // Pin it.
234 PinThread(core_id, cur_thread);
235 cur_thread->Pin();
233 236
234 // An update is needed. 237 // An update is needed.
235 KScheduler::SetSchedulerUpdateNeeded(kernel); 238 KScheduler::SetSchedulerUpdateNeeded(kernel);
239 }
236} 240}
237 241
238void KProcess::UnpinCurrentThread() { 242void KProcess::UnpinCurrentThread() {
@@ -250,6 +254,20 @@ void KProcess::UnpinCurrentThread() {
250 KScheduler::SetSchedulerUpdateNeeded(kernel); 254 KScheduler::SetSchedulerUpdateNeeded(kernel);
251} 255}
252 256
257void KProcess::UnpinThread(KThread* thread) {
258 ASSERT(kernel.GlobalSchedulerContext().IsLocked());
259
260 // Get the thread's core id.
261 const auto core_id = thread->GetActiveCore();
262
263 // Unpin it.
264 UnpinThread(core_id, thread);
265 thread->Unpin();
266
267 // An update is needed.
268 KScheduler::SetSchedulerUpdateNeeded(kernel);
269}
270
253ResultCode KProcess::AddSharedMemory(KSharedMemory* shmem, [[maybe_unused]] VAddr address, 271ResultCode KProcess::AddSharedMemory(KSharedMemory* shmem, [[maybe_unused]] VAddr address,
254 [[maybe_unused]] size_t size) { 272 [[maybe_unused]] size_t size) {
255 // Lock ourselves, to prevent concurrent access. 273 // Lock ourselves, to prevent concurrent access.
@@ -528,7 +546,7 @@ void KProcess::LoadModule(CodeSet code_set, VAddr base_addr) {
528 std::lock_guard lock{HLE::g_hle_lock}; 546 std::lock_guard lock{HLE::g_hle_lock};
529 const auto ReprotectSegment = [&](const CodeSet::Segment& segment, 547 const auto ReprotectSegment = [&](const CodeSet::Segment& segment,
530 KMemoryPermission permission) { 548 KMemoryPermission permission) {
531 page_table->SetCodeMemoryPermission(segment.addr + base_addr, segment.size, permission); 549 page_table->SetProcessMemoryPermission(segment.addr + base_addr, segment.size, permission);
532 }; 550 };
533 551
534 kernel.System().Memory().WriteBlock(*this, base_addr, code_set.memory.data(), 552 kernel.System().Memory().WriteBlock(*this, base_addr, code_set.memory.data(),
diff --git a/src/core/hle/kernel/k_process.h b/src/core/hle/kernel/k_process.h
index 8a8c1fcbb..cb93c7e24 100644
--- a/src/core/hle/kernel/k_process.h
+++ b/src/core/hle/kernel/k_process.h
@@ -347,6 +347,7 @@ public:
347 347
348 void PinCurrentThread(); 348 void PinCurrentThread();
349 void UnpinCurrentThread(); 349 void UnpinCurrentThread();
350 void UnpinThread(KThread* thread);
350 351
351 KLightLock& GetStateLock() { 352 KLightLock& GetStateLock() {
352 return state_lock; 353 return state_lock;
diff --git a/src/core/hle/kernel/k_scheduler.cpp b/src/core/hle/kernel/k_scheduler.cpp
index 6a7d80d03..277201de4 100644
--- a/src/core/hle/kernel/k_scheduler.cpp
+++ b/src/core/hle/kernel/k_scheduler.cpp
@@ -240,8 +240,8 @@ void KScheduler::OnThreadPriorityChanged(KernelCore& kernel, KThread* thread, s3
240 240
241 // If the thread is runnable, we want to change its priority in the queue. 241 // If the thread is runnable, we want to change its priority in the queue.
242 if (thread->GetRawState() == ThreadState::Runnable) { 242 if (thread->GetRawState() == ThreadState::Runnable) {
243 GetPriorityQueue(kernel).ChangePriority( 243 GetPriorityQueue(kernel).ChangePriority(old_priority,
244 old_priority, thread == kernel.CurrentScheduler()->GetCurrentThread(), thread); 244 thread == kernel.GetCurrentEmuThread(), thread);
245 IncrementScheduledCount(thread); 245 IncrementScheduledCount(thread);
246 SetSchedulerUpdateNeeded(kernel); 246 SetSchedulerUpdateNeeded(kernel);
247 } 247 }
@@ -360,7 +360,7 @@ void KScheduler::RotateScheduledQueue(s32 cpu_core_id, s32 priority) {
360} 360}
361 361
362bool KScheduler::CanSchedule(KernelCore& kernel) { 362bool KScheduler::CanSchedule(KernelCore& kernel) {
363 return kernel.CurrentScheduler()->GetCurrentThread()->GetDisableDispatchCount() <= 1; 363 return kernel.GetCurrentEmuThread()->GetDisableDispatchCount() <= 1;
364} 364}
365 365
366bool KScheduler::IsSchedulerUpdateNeeded(const KernelCore& kernel) { 366bool KScheduler::IsSchedulerUpdateNeeded(const KernelCore& kernel) {
@@ -376,20 +376,30 @@ void KScheduler::ClearSchedulerUpdateNeeded(KernelCore& kernel) {
376} 376}
377 377
378void KScheduler::DisableScheduling(KernelCore& kernel) { 378void KScheduler::DisableScheduling(KernelCore& kernel) {
379 if (auto* scheduler = kernel.CurrentScheduler(); scheduler) { 379 // If we are shutting down the kernel, none of this is relevant anymore.
380 ASSERT(scheduler->GetCurrentThread()->GetDisableDispatchCount() >= 0); 380 if (kernel.IsShuttingDown()) {
381 scheduler->GetCurrentThread()->DisableDispatch(); 381 return;
382 } 382 }
383
384 ASSERT(GetCurrentThreadPointer(kernel)->GetDisableDispatchCount() >= 0);
385 GetCurrentThreadPointer(kernel)->DisableDispatch();
383} 386}
384 387
385void KScheduler::EnableScheduling(KernelCore& kernel, u64 cores_needing_scheduling) { 388void KScheduler::EnableScheduling(KernelCore& kernel, u64 cores_needing_scheduling) {
386 if (auto* scheduler = kernel.CurrentScheduler(); scheduler) { 389 // If we are shutting down the kernel, none of this is relevant anymore.
387 ASSERT(scheduler->GetCurrentThread()->GetDisableDispatchCount() >= 1); 390 if (kernel.IsShuttingDown()) {
388 if (scheduler->GetCurrentThread()->GetDisableDispatchCount() >= 1) { 391 return;
389 scheduler->GetCurrentThread()->EnableDispatch(); 392 }
390 } 393
394 auto* current_thread = GetCurrentThreadPointer(kernel);
395
396 ASSERT(current_thread->GetDisableDispatchCount() >= 1);
397
398 if (current_thread->GetDisableDispatchCount() > 1) {
399 current_thread->EnableDispatch();
400 } else {
401 RescheduleCores(kernel, cores_needing_scheduling);
391 } 402 }
392 RescheduleCores(kernel, cores_needing_scheduling);
393} 403}
394 404
395u64 KScheduler::UpdateHighestPriorityThreads(KernelCore& kernel) { 405u64 KScheduler::UpdateHighestPriorityThreads(KernelCore& kernel) {
@@ -617,13 +627,17 @@ KScheduler::KScheduler(Core::System& system_, s32 core_id_) : system{system_}, c
617 state.highest_priority_thread = nullptr; 627 state.highest_priority_thread = nullptr;
618} 628}
619 629
620KScheduler::~KScheduler() { 630void KScheduler::Finalize() {
621 if (idle_thread) { 631 if (idle_thread) {
622 idle_thread->Close(); 632 idle_thread->Close();
623 idle_thread = nullptr; 633 idle_thread = nullptr;
624 } 634 }
625} 635}
626 636
637KScheduler::~KScheduler() {
638 ASSERT(!idle_thread);
639}
640
627KThread* KScheduler::GetCurrentThread() const { 641KThread* KScheduler::GetCurrentThread() const {
628 if (auto result = current_thread.load(); result) { 642 if (auto result = current_thread.load(); result) {
629 return result; 643 return result;
@@ -642,10 +656,12 @@ void KScheduler::RescheduleCurrentCore() {
642 if (phys_core.IsInterrupted()) { 656 if (phys_core.IsInterrupted()) {
643 phys_core.ClearInterrupt(); 657 phys_core.ClearInterrupt();
644 } 658 }
659
645 guard.Lock(); 660 guard.Lock();
646 if (state.needs_scheduling.load()) { 661 if (state.needs_scheduling.load()) {
647 Schedule(); 662 Schedule();
648 } else { 663 } else {
664 GetCurrentThread()->EnableDispatch();
649 guard.Unlock(); 665 guard.Unlock();
650 } 666 }
651} 667}
@@ -655,26 +671,33 @@ void KScheduler::OnThreadStart() {
655} 671}
656 672
657void KScheduler::Unload(KThread* thread) { 673void KScheduler::Unload(KThread* thread) {
674 ASSERT(thread);
675
658 LOG_TRACE(Kernel, "core {}, unload thread {}", core_id, thread ? thread->GetName() : "nullptr"); 676 LOG_TRACE(Kernel, "core {}, unload thread {}", core_id, thread ? thread->GetName() : "nullptr");
659 677
660 if (thread) { 678 if (thread->IsCallingSvc()) {
661 if (thread->IsCallingSvc()) { 679 thread->ClearIsCallingSvc();
662 thread->ClearIsCallingSvc(); 680 }
663 } 681
664 if (!thread->IsTerminationRequested()) { 682 auto& physical_core = system.Kernel().PhysicalCore(core_id);
665 prev_thread = thread; 683 if (!physical_core.IsInitialized()) {
666 684 return;
667 Core::ARM_Interface& cpu_core = system.ArmInterface(core_id); 685 }
668 cpu_core.SaveContext(thread->GetContext32()); 686
669 cpu_core.SaveContext(thread->GetContext64()); 687 Core::ARM_Interface& cpu_core = physical_core.ArmInterface();
670 // Save the TPIDR_EL0 system register in case it was modified. 688 cpu_core.SaveContext(thread->GetContext32());
671 thread->SetTPIDR_EL0(cpu_core.GetTPIDR_EL0()); 689 cpu_core.SaveContext(thread->GetContext64());
672 cpu_core.ClearExclusiveState(); 690 // Save the TPIDR_EL0 system register in case it was modified.
673 } else { 691 thread->SetTPIDR_EL0(cpu_core.GetTPIDR_EL0());
674 prev_thread = nullptr; 692 cpu_core.ClearExclusiveState();
675 } 693
676 thread->context_guard.Unlock(); 694 if (!thread->IsTerminationRequested() && thread->GetActiveCore() == core_id) {
695 prev_thread = thread;
696 } else {
697 prev_thread = nullptr;
677 } 698 }
699
700 thread->context_guard.Unlock();
678} 701}
679 702
680void KScheduler::Reload(KThread* thread) { 703void KScheduler::Reload(KThread* thread) {
@@ -683,11 +706,6 @@ void KScheduler::Reload(KThread* thread) {
683 if (thread) { 706 if (thread) {
684 ASSERT_MSG(thread->GetState() == ThreadState::Runnable, "Thread must be runnable."); 707 ASSERT_MSG(thread->GetState() == ThreadState::Runnable, "Thread must be runnable.");
685 708
686 auto* const thread_owner_process = thread->GetOwnerProcess();
687 if (thread_owner_process != nullptr) {
688 system.Kernel().MakeCurrentProcess(thread_owner_process);
689 }
690
691 Core::ARM_Interface& cpu_core = system.ArmInterface(core_id); 709 Core::ARM_Interface& cpu_core = system.ArmInterface(core_id);
692 cpu_core.LoadContext(thread->GetContext32()); 710 cpu_core.LoadContext(thread->GetContext32());
693 cpu_core.LoadContext(thread->GetContext64()); 711 cpu_core.LoadContext(thread->GetContext64());
@@ -705,7 +723,7 @@ void KScheduler::SwitchContextStep2() {
705} 723}
706 724
707void KScheduler::ScheduleImpl() { 725void KScheduler::ScheduleImpl() {
708 KThread* previous_thread = current_thread.load(); 726 KThread* previous_thread = GetCurrentThread();
709 KThread* next_thread = state.highest_priority_thread; 727 KThread* next_thread = state.highest_priority_thread;
710 728
711 state.needs_scheduling = false; 729 state.needs_scheduling = false;
@@ -717,10 +735,15 @@ void KScheduler::ScheduleImpl() {
717 735
718 // If we're not actually switching thread, there's nothing to do. 736 // If we're not actually switching thread, there's nothing to do.
719 if (next_thread == current_thread.load()) { 737 if (next_thread == current_thread.load()) {
738 previous_thread->EnableDispatch();
720 guard.Unlock(); 739 guard.Unlock();
721 return; 740 return;
722 } 741 }
723 742
743 if (next_thread->GetCurrentCore() != core_id) {
744 next_thread->SetCurrentCore(core_id);
745 }
746
724 current_thread.store(next_thread); 747 current_thread.store(next_thread);
725 748
726 KProcess* const previous_process = system.Kernel().CurrentProcess(); 749 KProcess* const previous_process = system.Kernel().CurrentProcess();
@@ -731,11 +754,7 @@ void KScheduler::ScheduleImpl() {
731 Unload(previous_thread); 754 Unload(previous_thread);
732 755
733 std::shared_ptr<Common::Fiber>* old_context; 756 std::shared_ptr<Common::Fiber>* old_context;
734 if (previous_thread != nullptr) { 757 old_context = &previous_thread->GetHostContext();
735 old_context = &previous_thread->GetHostContext();
736 } else {
737 old_context = &idle_thread->GetHostContext();
738 }
739 guard.Unlock(); 758 guard.Unlock();
740 759
741 Common::Fiber::YieldTo(*old_context, *switch_fiber); 760 Common::Fiber::YieldTo(*old_context, *switch_fiber);
diff --git a/src/core/hle/kernel/k_scheduler.h b/src/core/hle/kernel/k_scheduler.h
index 7df288438..82fcd99e7 100644
--- a/src/core/hle/kernel/k_scheduler.h
+++ b/src/core/hle/kernel/k_scheduler.h
@@ -33,6 +33,8 @@ public:
33 explicit KScheduler(Core::System& system_, s32 core_id_); 33 explicit KScheduler(Core::System& system_, s32 core_id_);
34 ~KScheduler(); 34 ~KScheduler();
35 35
36 void Finalize();
37
36 /// Reschedules to the next available thread (call after current thread is suspended) 38 /// Reschedules to the next available thread (call after current thread is suspended)
37 void RescheduleCurrentCore(); 39 void RescheduleCurrentCore();
38 40
diff --git a/src/core/hle/kernel/k_scheduler_lock.h b/src/core/hle/kernel/k_scheduler_lock.h
index c571f2992..93c47f1b1 100644
--- a/src/core/hle/kernel/k_scheduler_lock.h
+++ b/src/core/hle/kernel/k_scheduler_lock.h
@@ -23,6 +23,11 @@ public:
23 } 23 }
24 24
25 void Lock() { 25 void Lock() {
26 // If we are shutting down the kernel, none of this is relevant anymore.
27 if (kernel.IsShuttingDown()) {
28 return;
29 }
30
26 if (IsLockedByCurrentThread()) { 31 if (IsLockedByCurrentThread()) {
27 // If we already own the lock, we can just increment the count. 32 // If we already own the lock, we can just increment the count.
28 ASSERT(lock_count > 0); 33 ASSERT(lock_count > 0);
@@ -43,6 +48,11 @@ public:
43 } 48 }
44 49
45 void Unlock() { 50 void Unlock() {
51 // If we are shutting down the kernel, none of this is relevant anymore.
52 if (kernel.IsShuttingDown()) {
53 return;
54 }
55
46 ASSERT(IsLockedByCurrentThread()); 56 ASSERT(IsLockedByCurrentThread());
47 ASSERT(lock_count > 0); 57 ASSERT(lock_count > 0);
48 58
diff --git a/src/core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h b/src/core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h
index 61dc2858f..2995c492d 100644
--- a/src/core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h
+++ b/src/core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h
@@ -8,6 +8,7 @@
8#pragma once 8#pragma once
9 9
10#include "common/common_types.h" 10#include "common/common_types.h"
11#include "core/hle/kernel/global_scheduler_context.h"
11#include "core/hle/kernel/k_thread.h" 12#include "core/hle/kernel/k_thread.h"
12#include "core/hle/kernel/kernel.h" 13#include "core/hle/kernel/kernel.h"
13#include "core/hle/kernel/time_manager.h" 14#include "core/hle/kernel/time_manager.h"
diff --git a/src/core/hle/kernel/k_server_session.cpp b/src/core/hle/kernel/k_server_session.cpp
index 2bd53ccbd..d4e4a6b06 100644
--- a/src/core/hle/kernel/k_server_session.cpp
+++ b/src/core/hle/kernel/k_server_session.cpp
@@ -175,8 +175,7 @@ ResultCode KServerSession::CompleteSyncRequest(HLERequestContext& context) {
175 { 175 {
176 KScopedSchedulerLock lock(kernel); 176 KScopedSchedulerLock lock(kernel);
177 if (!context.IsThreadWaiting()) { 177 if (!context.IsThreadWaiting()) {
178 context.GetThread().Wakeup(); 178 context.GetThread().EndWait(result);
179 context.GetThread().SetSyncedObject(nullptr, result);
180 } 179 }
181 } 180 }
182 181
diff --git a/src/core/hle/kernel/k_synchronization_object.cpp b/src/core/hle/kernel/k_synchronization_object.cpp
index f168b4f21..e4c5eb74f 100644
--- a/src/core/hle/kernel/k_synchronization_object.cpp
+++ b/src/core/hle/kernel/k_synchronization_object.cpp
@@ -8,11 +8,66 @@
8#include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h" 8#include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h"
9#include "core/hle/kernel/k_synchronization_object.h" 9#include "core/hle/kernel/k_synchronization_object.h"
10#include "core/hle/kernel/k_thread.h" 10#include "core/hle/kernel/k_thread.h"
11#include "core/hle/kernel/k_thread_queue.h"
11#include "core/hle/kernel/kernel.h" 12#include "core/hle/kernel/kernel.h"
12#include "core/hle/kernel/svc_results.h" 13#include "core/hle/kernel/svc_results.h"
13 14
14namespace Kernel { 15namespace Kernel {
15 16
17namespace {
18
19class ThreadQueueImplForKSynchronizationObjectWait final : public KThreadQueueWithoutEndWait {
20public:
21 ThreadQueueImplForKSynchronizationObjectWait(KernelCore& kernel_, KSynchronizationObject** o,
22 KSynchronizationObject::ThreadListNode* n, s32 c)
23 : KThreadQueueWithoutEndWait(kernel_), m_objects(o), m_nodes(n), m_count(c) {}
24
25 void NotifyAvailable(KThread* waiting_thread, KSynchronizationObject* signaled_object,
26 ResultCode wait_result) override {
27 // Determine the sync index, and unlink all nodes.
28 s32 sync_index = -1;
29 for (auto i = 0; i < m_count; ++i) {
30 // Check if this is the signaled object.
31 if (m_objects[i] == signaled_object && sync_index == -1) {
32 sync_index = i;
33 }
34
35 // Unlink the current node from the current object.
36 m_objects[i]->UnlinkNode(std::addressof(m_nodes[i]));
37 }
38
39 // Set the waiting thread's sync index.
40 waiting_thread->SetSyncedIndex(sync_index);
41
42 // Set the waiting thread as not cancellable.
43 waiting_thread->ClearCancellable();
44
45 // Invoke the base end wait handler.
46 KThreadQueue::EndWait(waiting_thread, wait_result);
47 }
48
49 void CancelWait(KThread* waiting_thread, ResultCode wait_result,
50 bool cancel_timer_task) override {
51 // Remove all nodes from our list.
52 for (auto i = 0; i < m_count; ++i) {
53 m_objects[i]->UnlinkNode(std::addressof(m_nodes[i]));
54 }
55
56 // Set the waiting thread as not cancellable.
57 waiting_thread->ClearCancellable();
58
59 // Invoke the base cancel wait handler.
60 KThreadQueue::CancelWait(waiting_thread, wait_result, cancel_timer_task);
61 }
62
63private:
64 KSynchronizationObject** m_objects;
65 KSynchronizationObject::ThreadListNode* m_nodes;
66 s32 m_count;
67};
68
69} // namespace
70
16void KSynchronizationObject::Finalize() { 71void KSynchronizationObject::Finalize() {
17 this->OnFinalizeSynchronizationObject(); 72 this->OnFinalizeSynchronizationObject();
18 KAutoObject::Finalize(); 73 KAutoObject::Finalize();
@@ -25,11 +80,19 @@ ResultCode KSynchronizationObject::Wait(KernelCore& kernel_ctx, s32* out_index,
25 std::vector<ThreadListNode> thread_nodes(num_objects); 80 std::vector<ThreadListNode> thread_nodes(num_objects);
26 81
27 // Prepare for wait. 82 // Prepare for wait.
28 KThread* thread = kernel_ctx.CurrentScheduler()->GetCurrentThread(); 83 KThread* thread = GetCurrentThreadPointer(kernel_ctx);
84 ThreadQueueImplForKSynchronizationObjectWait wait_queue(kernel_ctx, objects,
85 thread_nodes.data(), num_objects);
29 86
30 { 87 {
31 // Setup the scheduling lock and sleep. 88 // Setup the scheduling lock and sleep.
32 KScopedSchedulerLockAndSleep slp{kernel_ctx, thread, timeout}; 89 KScopedSchedulerLockAndSleep slp(kernel_ctx, thread, timeout);
90
91 // Check if the thread should terminate.
92 if (thread->IsTerminationRequested()) {
93 slp.CancelSleep();
94 return ResultTerminationRequested;
95 }
33 96
34 // Check if any of the objects are already signaled. 97 // Check if any of the objects are already signaled.
35 for (auto i = 0; i < num_objects; ++i) { 98 for (auto i = 0; i < num_objects; ++i) {
@@ -48,12 +111,6 @@ ResultCode KSynchronizationObject::Wait(KernelCore& kernel_ctx, s32* out_index,
48 return ResultTimedOut; 111 return ResultTimedOut;
49 } 112 }
50 113
51 // Check if the thread should terminate.
52 if (thread->IsTerminationRequested()) {
53 slp.CancelSleep();
54 return ResultTerminationRequested;
55 }
56
57 // Check if waiting was canceled. 114 // Check if waiting was canceled.
58 if (thread->IsWaitCancelled()) { 115 if (thread->IsWaitCancelled()) {
59 slp.CancelSleep(); 116 slp.CancelSleep();
@@ -66,73 +123,25 @@ ResultCode KSynchronizationObject::Wait(KernelCore& kernel_ctx, s32* out_index,
66 thread_nodes[i].thread = thread; 123 thread_nodes[i].thread = thread;
67 thread_nodes[i].next = nullptr; 124 thread_nodes[i].next = nullptr;
68 125
69 if (objects[i]->thread_list_tail == nullptr) { 126 objects[i]->LinkNode(std::addressof(thread_nodes[i]));
70 objects[i]->thread_list_head = std::addressof(thread_nodes[i]);
71 } else {
72 objects[i]->thread_list_tail->next = std::addressof(thread_nodes[i]);
73 }
74
75 objects[i]->thread_list_tail = std::addressof(thread_nodes[i]);
76 } 127 }
77 128
78 // For debugging only 129 // Mark the thread as cancellable.
79 thread->SetWaitObjectsForDebugging({objects, static_cast<std::size_t>(num_objects)});
80
81 // Mark the thread as waiting.
82 thread->SetCancellable(); 130 thread->SetCancellable();
83 thread->SetSyncedObject(nullptr, ResultTimedOut);
84 thread->SetState(ThreadState::Waiting);
85 thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::Synchronization);
86 }
87 131
88 // The lock/sleep is done, so we should be able to get our result. 132 // Clear the thread's synced index.
133 thread->SetSyncedIndex(-1);
89 134
90 // Thread is no longer cancellable. 135 // Wait for an object to be signaled.
91 thread->ClearCancellable(); 136 thread->BeginWait(std::addressof(wait_queue));
92 137 thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::Synchronization);
93 // For debugging only 138 }
94 thread->SetWaitObjectsForDebugging({});
95 139
96 // Cancel the timer as needed. 140 // Set the output index.
97 kernel_ctx.TimeManager().UnscheduleTimeEvent(thread); 141 *out_index = thread->GetSyncedIndex();
98 142
99 // Get the wait result. 143 // Get the wait result.
100 ResultCode wait_result{ResultSuccess}; 144 return thread->GetWaitResult();
101 s32 sync_index = -1;
102 {
103 KScopedSchedulerLock lock(kernel_ctx);
104 KSynchronizationObject* synced_obj;
105 wait_result = thread->GetWaitResult(std::addressof(synced_obj));
106
107 for (auto i = 0; i < num_objects; ++i) {
108 // Unlink the object from the list.
109 ThreadListNode* prev_ptr =
110 reinterpret_cast<ThreadListNode*>(std::addressof(objects[i]->thread_list_head));
111 ThreadListNode* prev_val = nullptr;
112 ThreadListNode *prev, *tail_prev;
113
114 do {
115 prev = prev_ptr;
116 prev_ptr = prev_ptr->next;
117 tail_prev = prev_val;
118 prev_val = prev_ptr;
119 } while (prev_ptr != std::addressof(thread_nodes[i]));
120
121 if (objects[i]->thread_list_tail == std::addressof(thread_nodes[i])) {
122 objects[i]->thread_list_tail = tail_prev;
123 }
124
125 prev->next = thread_nodes[i].next;
126
127 if (objects[i] == synced_obj) {
128 sync_index = i;
129 }
130 }
131 }
132
133 // Set output.
134 *out_index = sync_index;
135 return wait_result;
136} 145}
137 146
138KSynchronizationObject::KSynchronizationObject(KernelCore& kernel_) 147KSynchronizationObject::KSynchronizationObject(KernelCore& kernel_)
@@ -141,7 +150,7 @@ KSynchronizationObject::KSynchronizationObject(KernelCore& kernel_)
141KSynchronizationObject::~KSynchronizationObject() = default; 150KSynchronizationObject::~KSynchronizationObject() = default;
142 151
143void KSynchronizationObject::NotifyAvailable(ResultCode result) { 152void KSynchronizationObject::NotifyAvailable(ResultCode result) {
144 KScopedSchedulerLock lock(kernel); 153 KScopedSchedulerLock sl(kernel);
145 154
146 // If we're not signaled, we've nothing to notify. 155 // If we're not signaled, we've nothing to notify.
147 if (!this->IsSignaled()) { 156 if (!this->IsSignaled()) {
@@ -150,11 +159,7 @@ void KSynchronizationObject::NotifyAvailable(ResultCode result) {
150 159
151 // Iterate over each thread. 160 // Iterate over each thread.
152 for (auto* cur_node = thread_list_head; cur_node != nullptr; cur_node = cur_node->next) { 161 for (auto* cur_node = thread_list_head; cur_node != nullptr; cur_node = cur_node->next) {
153 KThread* thread = cur_node->thread; 162 cur_node->thread->NotifyAvailable(this, result);
154 if (thread->GetState() == ThreadState::Waiting) {
155 thread->SetSyncedObject(this, result);
156 thread->SetState(ThreadState::Runnable);
157 }
158 } 163 }
159} 164}
160 165
diff --git a/src/core/hle/kernel/k_synchronization_object.h b/src/core/hle/kernel/k_synchronization_object.h
index 898e58e16..ec235437b 100644
--- a/src/core/hle/kernel/k_synchronization_object.h
+++ b/src/core/hle/kernel/k_synchronization_object.h
@@ -35,6 +35,38 @@ public:
35 35
36 [[nodiscard]] std::vector<KThread*> GetWaitingThreadsForDebugging() const; 36 [[nodiscard]] std::vector<KThread*> GetWaitingThreadsForDebugging() const;
37 37
38 void LinkNode(ThreadListNode* node_) {
39 // Link the node to the list.
40 if (thread_list_tail == nullptr) {
41 thread_list_head = node_;
42 } else {
43 thread_list_tail->next = node_;
44 }
45
46 thread_list_tail = node_;
47 }
48
49 void UnlinkNode(ThreadListNode* node_) {
50 // Unlink the node from the list.
51 ThreadListNode* prev_ptr =
52 reinterpret_cast<ThreadListNode*>(std::addressof(thread_list_head));
53 ThreadListNode* prev_val = nullptr;
54 ThreadListNode *prev, *tail_prev;
55
56 do {
57 prev = prev_ptr;
58 prev_ptr = prev_ptr->next;
59 tail_prev = prev_val;
60 prev_val = prev_ptr;
61 } while (prev_ptr != node_);
62
63 if (thread_list_tail == node_) {
64 thread_list_tail = tail_prev;
65 }
66
67 prev->next = node_->next;
68 }
69
38protected: 70protected:
39 explicit KSynchronizationObject(KernelCore& kernel); 71 explicit KSynchronizationObject(KernelCore& kernel);
40 ~KSynchronizationObject() override; 72 ~KSynchronizationObject() override;
diff --git a/src/core/hle/kernel/k_thread.cpp b/src/core/hle/kernel/k_thread.cpp
index db65ce79a..752592e2e 100644
--- a/src/core/hle/kernel/k_thread.cpp
+++ b/src/core/hle/kernel/k_thread.cpp
@@ -13,6 +13,9 @@
13#include "common/common_types.h" 13#include "common/common_types.h"
14#include "common/fiber.h" 14#include "common/fiber.h"
15#include "common/logging/log.h" 15#include "common/logging/log.h"
16#include "common/scope_exit.h"
17#include "common/settings.h"
18#include "common/thread_queue_list.h"
16#include "core/core.h" 19#include "core/core.h"
17#include "core/cpu_manager.h" 20#include "core/cpu_manager.h"
18#include "core/hardware_properties.h" 21#include "core/hardware_properties.h"
@@ -56,6 +59,34 @@ static void ResetThreadContext64(Core::ARM_Interface::ThreadContext64& context,
56 59
57namespace Kernel { 60namespace Kernel {
58 61
62namespace {
63
64class ThreadQueueImplForKThreadSleep final : public KThreadQueueWithoutEndWait {
65public:
66 explicit ThreadQueueImplForKThreadSleep(KernelCore& kernel_)
67 : KThreadQueueWithoutEndWait(kernel_) {}
68};
69
70class ThreadQueueImplForKThreadSetProperty final : public KThreadQueue {
71public:
72 explicit ThreadQueueImplForKThreadSetProperty(KernelCore& kernel_, KThread::WaiterList* wl)
73 : KThreadQueue(kernel_), m_wait_list(wl) {}
74
75 void CancelWait(KThread* waiting_thread, ResultCode wait_result,
76 bool cancel_timer_task) override {
77 // Remove the thread from the wait list.
78 m_wait_list->erase(m_wait_list->iterator_to(*waiting_thread));
79
80 // Invoke the base cancel wait handler.
81 KThreadQueue::CancelWait(waiting_thread, wait_result, cancel_timer_task);
82 }
83
84private:
85 KThread::WaiterList* m_wait_list;
86};
87
88} // namespace
89
59KThread::KThread(KernelCore& kernel_) 90KThread::KThread(KernelCore& kernel_)
60 : KAutoObjectWithSlabHeapAndContainer{kernel_}, activity_pause_lock{kernel_} {} 91 : KAutoObjectWithSlabHeapAndContainer{kernel_}, activity_pause_lock{kernel_} {}
61KThread::~KThread() = default; 92KThread::~KThread() = default;
@@ -82,6 +113,8 @@ ResultCode KThread::Initialize(KThreadFunction func, uintptr_t arg, VAddr user_s
82 [[fallthrough]]; 113 [[fallthrough]];
83 case ThreadType::HighPriority: 114 case ThreadType::HighPriority:
84 [[fallthrough]]; 115 [[fallthrough]];
116 case ThreadType::Dummy:
117 [[fallthrough]];
85 case ThreadType::User: 118 case ThreadType::User:
86 ASSERT(((owner == nullptr) || 119 ASSERT(((owner == nullptr) ||
87 (owner->GetCoreMask() | (1ULL << virt_core)) == owner->GetCoreMask())); 120 (owner->GetCoreMask() | (1ULL << virt_core)) == owner->GetCoreMask()));
@@ -127,11 +160,8 @@ ResultCode KThread::Initialize(KThreadFunction func, uintptr_t arg, VAddr user_s
127 priority = prio; 160 priority = prio;
128 base_priority = prio; 161 base_priority = prio;
129 162
130 // Set sync object and waiting lock to null.
131 synced_object = nullptr;
132
133 // Initialize sleeping queue. 163 // Initialize sleeping queue.
134 sleeping_queue = nullptr; 164 wait_queue = nullptr;
135 165
136 // Set suspend flags. 166 // Set suspend flags.
137 suspend_request_flags = 0; 167 suspend_request_flags = 0;
@@ -184,7 +214,7 @@ ResultCode KThread::Initialize(KThreadFunction func, uintptr_t arg, VAddr user_s
184 // Setup the stack parameters. 214 // Setup the stack parameters.
185 StackParameters& sp = GetStackParameters(); 215 StackParameters& sp = GetStackParameters();
186 sp.cur_thread = this; 216 sp.cur_thread = this;
187 sp.disable_count = 1; 217 sp.disable_count = 0;
188 SetInExceptionHandler(); 218 SetInExceptionHandler();
189 219
190 // Set thread ID. 220 // Set thread ID.
@@ -211,15 +241,16 @@ ResultCode KThread::InitializeThread(KThread* thread, KThreadFunction func, uint
211 // Initialize the thread. 241 // Initialize the thread.
212 R_TRY(thread->Initialize(func, arg, user_stack_top, prio, core, owner, type)); 242 R_TRY(thread->Initialize(func, arg, user_stack_top, prio, core, owner, type));
213 243
214 // Initialize host context. 244 // Initialize emulation parameters.
215 thread->host_context = 245 thread->host_context =
216 std::make_shared<Common::Fiber>(std::move(init_func), init_func_parameter); 246 std::make_shared<Common::Fiber>(std::move(init_func), init_func_parameter);
247 thread->is_single_core = !Settings::values.use_multi_core.GetValue();
217 248
218 return ResultSuccess; 249 return ResultSuccess;
219} 250}
220 251
221ResultCode KThread::InitializeDummyThread(KThread* thread) { 252ResultCode KThread::InitializeDummyThread(KThread* thread) {
222 return thread->Initialize({}, {}, {}, DefaultThreadPriority, 3, {}, ThreadType::Main); 253 return thread->Initialize({}, {}, {}, DefaultThreadPriority, 3, {}, ThreadType::Dummy);
223} 254}
224 255
225ResultCode KThread::InitializeIdleThread(Core::System& system, KThread* thread, s32 virt_core) { 256ResultCode KThread::InitializeIdleThread(Core::System& system, KThread* thread, s32 virt_core) {
@@ -273,11 +304,14 @@ void KThread::Finalize() {
273 304
274 auto it = waiter_list.begin(); 305 auto it = waiter_list.begin();
275 while (it != waiter_list.end()) { 306 while (it != waiter_list.end()) {
276 // The thread shouldn't be a kernel waiter. 307 // Clear the lock owner
277 it->SetLockOwner(nullptr); 308 it->SetLockOwner(nullptr);
278 it->SetSyncedObject(nullptr, ResultInvalidState); 309
279 it->Wakeup(); 310 // Erase the waiter from our list.
280 it = waiter_list.erase(it); 311 it = waiter_list.erase(it);
312
313 // Cancel the thread's wait.
314 it->CancelWait(ResultInvalidState, true);
281 } 315 }
282 } 316 }
283 317
@@ -294,15 +328,12 @@ bool KThread::IsSignaled() const {
294 return signaled; 328 return signaled;
295} 329}
296 330
297void KThread::Wakeup() { 331void KThread::OnTimer() {
298 KScopedSchedulerLock sl{kernel}; 332 ASSERT(kernel.GlobalSchedulerContext().IsLocked());
299 333
334 // If we're waiting, cancel the wait.
300 if (GetState() == ThreadState::Waiting) { 335 if (GetState() == ThreadState::Waiting) {
301 if (sleeping_queue != nullptr) { 336 wait_queue->CancelWait(this, ResultTimedOut, false);
302 sleeping_queue->WakeupThread(this);
303 } else {
304 SetState(ThreadState::Runnable);
305 }
306 } 337 }
307} 338}
308 339
@@ -327,7 +358,7 @@ void KThread::StartTermination() {
327 358
328 // Signal. 359 // Signal.
329 signaled = true; 360 signaled = true;
330 NotifyAvailable(); 361 KSynchronizationObject::NotifyAvailable();
331 362
332 // Clear previous thread in KScheduler. 363 // Clear previous thread in KScheduler.
333 KScheduler::ClearPreviousThread(kernel, this); 364 KScheduler::ClearPreviousThread(kernel, this);
@@ -475,30 +506,32 @@ ResultCode KThread::GetPhysicalCoreMask(s32* out_ideal_core, u64* out_affinity_m
475 return ResultSuccess; 506 return ResultSuccess;
476} 507}
477 508
478ResultCode KThread::SetCoreMask(s32 cpu_core_id, u64 v_affinity_mask) { 509ResultCode KThread::SetCoreMask(s32 core_id_, u64 v_affinity_mask) {
479 ASSERT(parent != nullptr); 510 ASSERT(parent != nullptr);
480 ASSERT(v_affinity_mask != 0); 511 ASSERT(v_affinity_mask != 0);
481 KScopedLightLock lk{activity_pause_lock}; 512 KScopedLightLock lk(activity_pause_lock);
482 513
483 // Set the core mask. 514 // Set the core mask.
484 u64 p_affinity_mask = 0; 515 u64 p_affinity_mask = 0;
485 { 516 {
486 KScopedSchedulerLock sl{kernel}; 517 KScopedSchedulerLock sl(kernel);
487 ASSERT(num_core_migration_disables >= 0); 518 ASSERT(num_core_migration_disables >= 0);
488 519
489 // If the core id is no-update magic, preserve the ideal core id. 520 // If we're updating, set our ideal virtual core.
490 if (cpu_core_id == Svc::IdealCoreNoUpdate) { 521 if (core_id_ != Svc::IdealCoreNoUpdate) {
491 cpu_core_id = virtual_ideal_core_id; 522 virtual_ideal_core_id = core_id_;
492 R_UNLESS(((1ULL << cpu_core_id) & v_affinity_mask) != 0, ResultInvalidCombination); 523 } else {
524 // Preserve our ideal core id.
525 core_id_ = virtual_ideal_core_id;
526 R_UNLESS(((1ULL << core_id_) & v_affinity_mask) != 0, ResultInvalidCombination);
493 } 527 }
494 528
495 // Set the virtual core/affinity mask. 529 // Set our affinity mask.
496 virtual_ideal_core_id = cpu_core_id;
497 virtual_affinity_mask = v_affinity_mask; 530 virtual_affinity_mask = v_affinity_mask;
498 531
499 // Translate the virtual core to a physical core. 532 // Translate the virtual core to a physical core.
500 if (cpu_core_id >= 0) { 533 if (core_id_ >= 0) {
501 cpu_core_id = Core::Hardware::VirtualToPhysicalCoreMap[cpu_core_id]; 534 core_id_ = Core::Hardware::VirtualToPhysicalCoreMap[core_id_];
502 } 535 }
503 536
504 // Translate the virtual affinity mask to a physical one. 537 // Translate the virtual affinity mask to a physical one.
@@ -513,7 +546,7 @@ ResultCode KThread::SetCoreMask(s32 cpu_core_id, u64 v_affinity_mask) {
513 const KAffinityMask old_mask = physical_affinity_mask; 546 const KAffinityMask old_mask = physical_affinity_mask;
514 547
515 // Set our new ideals. 548 // Set our new ideals.
516 physical_ideal_core_id = cpu_core_id; 549 physical_ideal_core_id = core_id_;
517 physical_affinity_mask.SetAffinityMask(p_affinity_mask); 550 physical_affinity_mask.SetAffinityMask(p_affinity_mask);
518 551
519 if (physical_affinity_mask.GetAffinityMask() != old_mask.GetAffinityMask()) { 552 if (physical_affinity_mask.GetAffinityMask() != old_mask.GetAffinityMask()) {
@@ -531,18 +564,18 @@ ResultCode KThread::SetCoreMask(s32 cpu_core_id, u64 v_affinity_mask) {
531 } 564 }
532 } else { 565 } else {
533 // Otherwise, we edit the original affinity for restoration later. 566 // Otherwise, we edit the original affinity for restoration later.
534 original_physical_ideal_core_id = cpu_core_id; 567 original_physical_ideal_core_id = core_id_;
535 original_physical_affinity_mask.SetAffinityMask(p_affinity_mask); 568 original_physical_affinity_mask.SetAffinityMask(p_affinity_mask);
536 } 569 }
537 } 570 }
538 571
539 // Update the pinned waiter list. 572 // Update the pinned waiter list.
573 ThreadQueueImplForKThreadSetProperty wait_queue_(kernel, std::addressof(pinned_waiter_list));
540 { 574 {
541 bool retry_update{}; 575 bool retry_update{};
542 bool thread_is_pinned{};
543 do { 576 do {
544 // Lock the scheduler. 577 // Lock the scheduler.
545 KScopedSchedulerLock sl{kernel}; 578 KScopedSchedulerLock sl(kernel);
546 579
547 // Don't do any further management if our termination has been requested. 580 // Don't do any further management if our termination has been requested.
548 R_SUCCEED_IF(IsTerminationRequested()); 581 R_SUCCEED_IF(IsTerminationRequested());
@@ -570,12 +603,9 @@ ResultCode KThread::SetCoreMask(s32 cpu_core_id, u64 v_affinity_mask) {
570 R_UNLESS(!GetCurrentThread(kernel).IsTerminationRequested(), 603 R_UNLESS(!GetCurrentThread(kernel).IsTerminationRequested(),
571 ResultTerminationRequested); 604 ResultTerminationRequested);
572 605
573 // Note that the thread was pinned.
574 thread_is_pinned = true;
575
576 // Wait until the thread isn't pinned any more. 606 // Wait until the thread isn't pinned any more.
577 pinned_waiter_list.push_back(GetCurrentThread(kernel)); 607 pinned_waiter_list.push_back(GetCurrentThread(kernel));
578 GetCurrentThread(kernel).SetState(ThreadState::Waiting); 608 GetCurrentThread(kernel).BeginWait(std::addressof(wait_queue_));
579 } else { 609 } else {
580 // If the thread isn't pinned, release the scheduler lock and retry until it's 610 // If the thread isn't pinned, release the scheduler lock and retry until it's
581 // not current. 611 // not current.
@@ -583,16 +613,6 @@ ResultCode KThread::SetCoreMask(s32 cpu_core_id, u64 v_affinity_mask) {
583 } 613 }
584 } 614 }
585 } while (retry_update); 615 } while (retry_update);
586
587 // If the thread was pinned, it no longer is, and we should remove the current thread from
588 // our waiter list.
589 if (thread_is_pinned) {
590 // Lock the scheduler.
591 KScopedSchedulerLock sl{kernel};
592
593 // Remove from the list.
594 pinned_waiter_list.erase(pinned_waiter_list.iterator_to(GetCurrentThread(kernel)));
595 }
596 } 616 }
597 617
598 return ResultSuccess; 618 return ResultSuccess;
@@ -641,15 +661,9 @@ void KThread::WaitCancel() {
641 KScopedSchedulerLock sl{kernel}; 661 KScopedSchedulerLock sl{kernel};
642 662
643 // Check if we're waiting and cancellable. 663 // Check if we're waiting and cancellable.
644 if (GetState() == ThreadState::Waiting && cancellable) { 664 if (this->GetState() == ThreadState::Waiting && cancellable) {
645 if (sleeping_queue != nullptr) { 665 wait_cancelled = false;
646 sleeping_queue->WakeupThread(this); 666 wait_queue->CancelWait(this, ResultCancelled, true);
647 wait_cancelled = true;
648 } else {
649 SetSyncedObject(nullptr, ResultCancelled);
650 SetState(ThreadState::Runnable);
651 wait_cancelled = false;
652 }
653 } else { 667 } else {
654 // Otherwise, note that we cancelled a wait. 668 // Otherwise, note that we cancelled a wait.
655 wait_cancelled = true; 669 wait_cancelled = true;
@@ -700,60 +714,59 @@ ResultCode KThread::SetActivity(Svc::ThreadActivity activity) {
700 // Set the activity. 714 // Set the activity.
701 { 715 {
702 // Lock the scheduler. 716 // Lock the scheduler.
703 KScopedSchedulerLock sl{kernel}; 717 KScopedSchedulerLock sl(kernel);
704 718
705 // Verify our state. 719 // Verify our state.
706 const auto cur_state = GetState(); 720 const auto cur_state = this->GetState();
707 R_UNLESS((cur_state == ThreadState::Waiting || cur_state == ThreadState::Runnable), 721 R_UNLESS((cur_state == ThreadState::Waiting || cur_state == ThreadState::Runnable),
708 ResultInvalidState); 722 ResultInvalidState);
709 723
710 // Either pause or resume. 724 // Either pause or resume.
711 if (activity == Svc::ThreadActivity::Paused) { 725 if (activity == Svc::ThreadActivity::Paused) {
712 // Verify that we're not suspended. 726 // Verify that we're not suspended.
713 R_UNLESS(!IsSuspendRequested(SuspendType::Thread), ResultInvalidState); 727 R_UNLESS(!this->IsSuspendRequested(SuspendType::Thread), ResultInvalidState);
714 728
715 // Suspend. 729 // Suspend.
716 RequestSuspend(SuspendType::Thread); 730 this->RequestSuspend(SuspendType::Thread);
717 } else { 731 } else {
718 ASSERT(activity == Svc::ThreadActivity::Runnable); 732 ASSERT(activity == Svc::ThreadActivity::Runnable);
719 733
720 // Verify that we're suspended. 734 // Verify that we're suspended.
721 R_UNLESS(IsSuspendRequested(SuspendType::Thread), ResultInvalidState); 735 R_UNLESS(this->IsSuspendRequested(SuspendType::Thread), ResultInvalidState);
722 736
723 // Resume. 737 // Resume.
724 Resume(SuspendType::Thread); 738 this->Resume(SuspendType::Thread);
725 } 739 }
726 } 740 }
727 741
728 // If the thread is now paused, update the pinned waiter list. 742 // If the thread is now paused, update the pinned waiter list.
729 if (activity == Svc::ThreadActivity::Paused) { 743 if (activity == Svc::ThreadActivity::Paused) {
730 bool thread_is_pinned{}; 744 ThreadQueueImplForKThreadSetProperty wait_queue_(kernel,
731 bool thread_is_current{}; 745 std::addressof(pinned_waiter_list));
746
747 bool thread_is_current;
732 do { 748 do {
733 // Lock the scheduler. 749 // Lock the scheduler.
734 KScopedSchedulerLock sl{kernel}; 750 KScopedSchedulerLock sl(kernel);
735 751
736 // Don't do any further management if our termination has been requested. 752 // Don't do any further management if our termination has been requested.
737 R_SUCCEED_IF(IsTerminationRequested()); 753 R_SUCCEED_IF(this->IsTerminationRequested());
754
755 // By default, treat the thread as not current.
756 thread_is_current = false;
738 757
739 // Check whether the thread is pinned. 758 // Check whether the thread is pinned.
740 if (GetStackParameters().is_pinned) { 759 if (this->GetStackParameters().is_pinned) {
741 // Verify that the current thread isn't terminating. 760 // Verify that the current thread isn't terminating.
742 R_UNLESS(!GetCurrentThread(kernel).IsTerminationRequested(), 761 R_UNLESS(!GetCurrentThread(kernel).IsTerminationRequested(),
743 ResultTerminationRequested); 762 ResultTerminationRequested);
744 763
745 // Note that the thread was pinned and not current.
746 thread_is_pinned = true;
747 thread_is_current = false;
748
749 // Wait until the thread isn't pinned any more. 764 // Wait until the thread isn't pinned any more.
750 pinned_waiter_list.push_back(GetCurrentThread(kernel)); 765 pinned_waiter_list.push_back(GetCurrentThread(kernel));
751 GetCurrentThread(kernel).SetState(ThreadState::Waiting); 766 GetCurrentThread(kernel).BeginWait(std::addressof(wait_queue_));
752 } else { 767 } else {
753 // Check if the thread is currently running. 768 // Check if the thread is currently running.
754 // If it is, we'll need to retry. 769 // If it is, we'll need to retry.
755 thread_is_current = false;
756
757 for (auto i = 0; i < static_cast<s32>(Core::Hardware::NUM_CPU_CORES); ++i) { 770 for (auto i = 0; i < static_cast<s32>(Core::Hardware::NUM_CPU_CORES); ++i) {
758 if (kernel.Scheduler(i).GetCurrentThread() == this) { 771 if (kernel.Scheduler(i).GetCurrentThread() == this) {
759 thread_is_current = true; 772 thread_is_current = true;
@@ -762,16 +775,6 @@ ResultCode KThread::SetActivity(Svc::ThreadActivity activity) {
762 } 775 }
763 } 776 }
764 } while (thread_is_current); 777 } while (thread_is_current);
765
766 // If the thread was pinned, it no longer is, and we should remove the current thread from
767 // our waiter list.
768 if (thread_is_pinned) {
769 // Lock the scheduler.
770 KScopedSchedulerLock sl{kernel};
771
772 // Remove from the list.
773 pinned_waiter_list.erase(pinned_waiter_list.iterator_to(GetCurrentThread(kernel)));
774 }
775 } 778 }
776 779
777 return ResultSuccess; 780 return ResultSuccess;
@@ -966,6 +969,9 @@ ResultCode KThread::Run() {
966 969
967 // Set our state and finish. 970 // Set our state and finish.
968 SetState(ThreadState::Runnable); 971 SetState(ThreadState::Runnable);
972
973 DisableDispatch();
974
969 return ResultSuccess; 975 return ResultSuccess;
970 } 976 }
971} 977}
@@ -996,27 +1002,61 @@ ResultCode KThread::Sleep(s64 timeout) {
996 ASSERT(this == GetCurrentThreadPointer(kernel)); 1002 ASSERT(this == GetCurrentThreadPointer(kernel));
997 ASSERT(timeout > 0); 1003 ASSERT(timeout > 0);
998 1004
1005 ThreadQueueImplForKThreadSleep wait_queue_(kernel);
999 { 1006 {
1000 // Setup the scheduling lock and sleep. 1007 // Setup the scheduling lock and sleep.
1001 KScopedSchedulerLockAndSleep slp{kernel, this, timeout}; 1008 KScopedSchedulerLockAndSleep slp(kernel, this, timeout);
1002 1009
1003 // Check if the thread should terminate. 1010 // Check if the thread should terminate.
1004 if (IsTerminationRequested()) { 1011 if (this->IsTerminationRequested()) {
1005 slp.CancelSleep(); 1012 slp.CancelSleep();
1006 return ResultTerminationRequested; 1013 return ResultTerminationRequested;
1007 } 1014 }
1008 1015
1009 // Mark the thread as waiting. 1016 // Wait for the sleep to end.
1010 SetState(ThreadState::Waiting); 1017 this->BeginWait(std::addressof(wait_queue_));
1011 SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::Sleep); 1018 SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::Sleep);
1012 } 1019 }
1013 1020
1014 // The lock/sleep is done. 1021 return ResultSuccess;
1022}
1015 1023
1016 // Cancel the timer. 1024void KThread::BeginWait(KThreadQueue* queue) {
1017 kernel.TimeManager().UnscheduleTimeEvent(this); 1025 // Set our state as waiting.
1026 SetState(ThreadState::Waiting);
1018 1027
1019 return ResultSuccess; 1028 // Set our wait queue.
1029 wait_queue = queue;
1030}
1031
1032void KThread::NotifyAvailable(KSynchronizationObject* signaled_object, ResultCode wait_result_) {
1033 // Lock the scheduler.
1034 KScopedSchedulerLock sl(kernel);
1035
1036 // If we're waiting, notify our queue that we're available.
1037 if (GetState() == ThreadState::Waiting) {
1038 wait_queue->NotifyAvailable(this, signaled_object, wait_result_);
1039 }
1040}
1041
1042void KThread::EndWait(ResultCode wait_result_) {
1043 // Lock the scheduler.
1044 KScopedSchedulerLock sl(kernel);
1045
1046 // If we're waiting, notify our queue that we're available.
1047 if (GetState() == ThreadState::Waiting) {
1048 wait_queue->EndWait(this, wait_result_);
1049 }
1050}
1051
1052void KThread::CancelWait(ResultCode wait_result_, bool cancel_timer_task) {
1053 // Lock the scheduler.
1054 KScopedSchedulerLock sl(kernel);
1055
1056 // If we're waiting, notify our queue that we're available.
1057 if (GetState() == ThreadState::Waiting) {
1058 wait_queue->CancelWait(this, wait_result_, cancel_timer_task);
1059 }
1020} 1060}
1021 1061
1022void KThread::SetState(ThreadState state) { 1062void KThread::SetState(ThreadState state) {
@@ -1050,4 +1090,26 @@ s32 GetCurrentCoreId(KernelCore& kernel) {
1050 return GetCurrentThread(kernel).GetCurrentCore(); 1090 return GetCurrentThread(kernel).GetCurrentCore();
1051} 1091}
1052 1092
1093KScopedDisableDispatch::~KScopedDisableDispatch() {
1094 // If we are shutting down the kernel, none of this is relevant anymore.
1095 if (kernel.IsShuttingDown()) {
1096 return;
1097 }
1098
1099 // Skip the reschedule if single-core, as dispatch tracking is disabled here.
1100 if (!Settings::values.use_multi_core.GetValue()) {
1101 return;
1102 }
1103
1104 if (GetCurrentThread(kernel).GetDisableDispatchCount() <= 1) {
1105 auto scheduler = kernel.CurrentScheduler();
1106
1107 if (scheduler) {
1108 scheduler->RescheduleCurrentCore();
1109 }
1110 } else {
1111 GetCurrentThread(kernel).EnableDispatch();
1112 }
1113}
1114
1053} // namespace Kernel 1115} // namespace Kernel
diff --git a/src/core/hle/kernel/k_thread.h b/src/core/hle/kernel/k_thread.h
index c77f44ad4..c8a08bd71 100644
--- a/src/core/hle/kernel/k_thread.h
+++ b/src/core/hle/kernel/k_thread.h
@@ -48,6 +48,7 @@ enum class ThreadType : u32 {
48 Kernel = 1, 48 Kernel = 1,
49 HighPriority = 2, 49 HighPriority = 2,
50 User = 3, 50 User = 3,
51 Dummy = 100, // Special thread type for emulation purposes only
51}; 52};
52DECLARE_ENUM_FLAG_OPERATORS(ThreadType); 53DECLARE_ENUM_FLAG_OPERATORS(ThreadType);
53 54
@@ -161,8 +162,6 @@ public:
161 } 162 }
162 } 163 }
163 164
164 void Wakeup();
165
166 void SetBasePriority(s32 value); 165 void SetBasePriority(s32 value);
167 166
168 [[nodiscard]] ResultCode Run(); 167 [[nodiscard]] ResultCode Run();
@@ -197,13 +196,19 @@ public:
197 196
198 void Suspend(); 197 void Suspend();
199 198
200 void SetSyncedObject(KSynchronizationObject* obj, ResultCode wait_res) { 199 constexpr void SetSyncedIndex(s32 index) {
201 synced_object = obj; 200 synced_index = index;
201 }
202
203 [[nodiscard]] constexpr s32 GetSyncedIndex() const {
204 return synced_index;
205 }
206
207 constexpr void SetWaitResult(ResultCode wait_res) {
202 wait_result = wait_res; 208 wait_result = wait_res;
203 } 209 }
204 210
205 [[nodiscard]] ResultCode GetWaitResult(KSynchronizationObject** out) const { 211 [[nodiscard]] constexpr ResultCode GetWaitResult() const {
206 *out = synced_object;
207 return wait_result; 212 return wait_result;
208 } 213 }
209 214
@@ -374,6 +379,8 @@ public:
374 379
375 [[nodiscard]] bool IsSignaled() const override; 380 [[nodiscard]] bool IsSignaled() const override;
376 381
382 void OnTimer();
383
377 static void PostDestroy(uintptr_t arg); 384 static void PostDestroy(uintptr_t arg);
378 385
379 [[nodiscard]] static ResultCode InitializeDummyThread(KThread* thread); 386 [[nodiscard]] static ResultCode InitializeDummyThread(KThread* thread);
@@ -446,20 +453,39 @@ public:
446 return per_core_priority_queue_entry[core]; 453 return per_core_priority_queue_entry[core];
447 } 454 }
448 455
449 void SetSleepingQueue(KThreadQueue* q) { 456 [[nodiscard]] bool IsKernelThread() const {
450 sleeping_queue = q; 457 return GetActiveCore() == 3;
458 }
459
460 [[nodiscard]] bool IsDispatchTrackingDisabled() const {
461 return is_single_core || IsKernelThread();
451 } 462 }
452 463
453 [[nodiscard]] s32 GetDisableDispatchCount() const { 464 [[nodiscard]] s32 GetDisableDispatchCount() const {
465 if (IsDispatchTrackingDisabled()) {
466 // TODO(bunnei): Until kernel threads are emulated, we cannot enable/disable dispatch.
467 return 1;
468 }
469
454 return this->GetStackParameters().disable_count; 470 return this->GetStackParameters().disable_count;
455 } 471 }
456 472
457 void DisableDispatch() { 473 void DisableDispatch() {
474 if (IsDispatchTrackingDisabled()) {
475 // TODO(bunnei): Until kernel threads are emulated, we cannot enable/disable dispatch.
476 return;
477 }
478
458 ASSERT(GetCurrentThread(kernel).GetDisableDispatchCount() >= 0); 479 ASSERT(GetCurrentThread(kernel).GetDisableDispatchCount() >= 0);
459 this->GetStackParameters().disable_count++; 480 this->GetStackParameters().disable_count++;
460 } 481 }
461 482
462 void EnableDispatch() { 483 void EnableDispatch() {
484 if (IsDispatchTrackingDisabled()) {
485 // TODO(bunnei): Until kernel threads are emulated, we cannot enable/disable dispatch.
486 return;
487 }
488
463 ASSERT(GetCurrentThread(kernel).GetDisableDispatchCount() > 0); 489 ASSERT(GetCurrentThread(kernel).GetDisableDispatchCount() > 0);
464 this->GetStackParameters().disable_count--; 490 this->GetStackParameters().disable_count--;
465 } 491 }
@@ -573,6 +599,15 @@ public:
573 address_key_value = val; 599 address_key_value = val;
574 } 600 }
575 601
602 void ClearWaitQueue() {
603 wait_queue = nullptr;
604 }
605
606 void BeginWait(KThreadQueue* queue);
607 void NotifyAvailable(KSynchronizationObject* signaled_object, ResultCode wait_result_);
608 void EndWait(ResultCode wait_result_);
609 void CancelWait(ResultCode wait_result_, bool cancel_timer_task);
610
576 [[nodiscard]] bool HasWaiters() const { 611 [[nodiscard]] bool HasWaiters() const {
577 return !waiter_list.empty(); 612 return !waiter_list.empty();
578 } 613 }
@@ -667,7 +702,6 @@ private:
667 KAffinityMask physical_affinity_mask{}; 702 KAffinityMask physical_affinity_mask{};
668 u64 thread_id{}; 703 u64 thread_id{};
669 std::atomic<s64> cpu_time{}; 704 std::atomic<s64> cpu_time{};
670 KSynchronizationObject* synced_object{};
671 VAddr address_key{}; 705 VAddr address_key{};
672 KProcess* parent{}; 706 KProcess* parent{};
673 VAddr kernel_stack_top{}; 707 VAddr kernel_stack_top{};
@@ -677,13 +711,14 @@ private:
677 s64 schedule_count{}; 711 s64 schedule_count{};
678 s64 last_scheduled_tick{}; 712 s64 last_scheduled_tick{};
679 std::array<QueueEntry, Core::Hardware::NUM_CPU_CORES> per_core_priority_queue_entry{}; 713 std::array<QueueEntry, Core::Hardware::NUM_CPU_CORES> per_core_priority_queue_entry{};
680 KThreadQueue* sleeping_queue{}; 714 KThreadQueue* wait_queue{};
681 WaiterList waiter_list{}; 715 WaiterList waiter_list{};
682 WaiterList pinned_waiter_list{}; 716 WaiterList pinned_waiter_list{};
683 KThread* lock_owner{}; 717 KThread* lock_owner{};
684 u32 address_key_value{}; 718 u32 address_key_value{};
685 u32 suspend_request_flags{}; 719 u32 suspend_request_flags{};
686 u32 suspend_allowed_flags{}; 720 u32 suspend_allowed_flags{};
721 s32 synced_index{};
687 ResultCode wait_result{ResultSuccess}; 722 ResultCode wait_result{ResultSuccess};
688 s32 base_priority{}; 723 s32 base_priority{};
689 s32 physical_ideal_core_id{}; 724 s32 physical_ideal_core_id{};
@@ -708,6 +743,7 @@ private:
708 743
709 // For emulation 744 // For emulation
710 std::shared_ptr<Common::Fiber> host_context{}; 745 std::shared_ptr<Common::Fiber> host_context{};
746 bool is_single_core{};
711 747
712 // For debugging 748 // For debugging
713 std::vector<KSynchronizationObject*> wait_objects_for_debugging; 749 std::vector<KSynchronizationObject*> wait_objects_for_debugging;
@@ -752,4 +788,20 @@ public:
752 } 788 }
753}; 789};
754 790
791class KScopedDisableDispatch {
792public:
793 [[nodiscard]] explicit KScopedDisableDispatch(KernelCore& kernel_) : kernel{kernel_} {
794 // If we are shutting down the kernel, none of this is relevant anymore.
795 if (kernel.IsShuttingDown()) {
796 return;
797 }
798 GetCurrentThread(kernel).DisableDispatch();
799 }
800
801 ~KScopedDisableDispatch();
802
803private:
804 KernelCore& kernel;
805};
806
755} // namespace Kernel 807} // namespace Kernel
diff --git a/src/core/hle/kernel/k_thread_queue.cpp b/src/core/hle/kernel/k_thread_queue.cpp
new file mode 100644
index 000000000..d5248b547
--- /dev/null
+++ b/src/core/hle/kernel/k_thread_queue.cpp
@@ -0,0 +1,49 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/hle/kernel/k_thread_queue.h"
6#include "core/hle/kernel/kernel.h"
7#include "core/hle/kernel/time_manager.h"
8
9namespace Kernel {
10
11void KThreadQueue::NotifyAvailable([[maybe_unused]] KThread* waiting_thread,
12 [[maybe_unused]] KSynchronizationObject* signaled_object,
13 [[maybe_unused]] ResultCode wait_result) {}
14
15void KThreadQueue::EndWait(KThread* waiting_thread, ResultCode wait_result) {
16 // Set the thread's wait result.
17 waiting_thread->SetWaitResult(wait_result);
18
19 // Set the thread as runnable.
20 waiting_thread->SetState(ThreadState::Runnable);
21
22 // Clear the thread's wait queue.
23 waiting_thread->ClearWaitQueue();
24
25 // Cancel the thread task.
26 kernel.TimeManager().UnscheduleTimeEvent(waiting_thread);
27}
28
29void KThreadQueue::CancelWait(KThread* waiting_thread, ResultCode wait_result,
30 bool cancel_timer_task) {
31 // Set the thread's wait result.
32 waiting_thread->SetWaitResult(wait_result);
33
34 // Set the thread as runnable.
35 waiting_thread->SetState(ThreadState::Runnable);
36
37 // Clear the thread's wait queue.
38 waiting_thread->ClearWaitQueue();
39
40 // Cancel the thread task.
41 if (cancel_timer_task) {
42 kernel.TimeManager().UnscheduleTimeEvent(waiting_thread);
43 }
44}
45
46void KThreadQueueWithoutEndWait::EndWait([[maybe_unused]] KThread* waiting_thread,
47 [[maybe_unused]] ResultCode wait_result) {}
48
49} // namespace Kernel
diff --git a/src/core/hle/kernel/k_thread_queue.h b/src/core/hle/kernel/k_thread_queue.h
index 35d471dc5..ccb718e49 100644
--- a/src/core/hle/kernel/k_thread_queue.h
+++ b/src/core/hle/kernel/k_thread_queue.h
@@ -4,6 +4,7 @@
4 4
5#pragma once 5#pragma once
6 6
7#include "core/hle/kernel/k_scheduler.h"
7#include "core/hle/kernel/k_thread.h" 8#include "core/hle/kernel/k_thread.h"
8 9
9namespace Kernel { 10namespace Kernel {
@@ -11,71 +12,24 @@ namespace Kernel {
11class KThreadQueue { 12class KThreadQueue {
12public: 13public:
13 explicit KThreadQueue(KernelCore& kernel_) : kernel{kernel_} {} 14 explicit KThreadQueue(KernelCore& kernel_) : kernel{kernel_} {}
15 virtual ~KThreadQueue() = default;
14 16
15 bool IsEmpty() const { 17 virtual void NotifyAvailable(KThread* waiting_thread, KSynchronizationObject* signaled_object,
16 return wait_list.empty(); 18 ResultCode wait_result);
17 } 19 virtual void EndWait(KThread* waiting_thread, ResultCode wait_result);
18 20 virtual void CancelWait(KThread* waiting_thread, ResultCode wait_result,
19 KThread::WaiterList::iterator begin() { 21 bool cancel_timer_task);
20 return wait_list.begin();
21 }
22 KThread::WaiterList::iterator end() {
23 return wait_list.end();
24 }
25
26 bool SleepThread(KThread* t) {
27 KScopedSchedulerLock sl{kernel};
28
29 // If the thread needs terminating, don't enqueue it.
30 if (t->IsTerminationRequested()) {
31 return false;
32 }
33
34 // Set the thread's queue and mark it as waiting.
35 t->SetSleepingQueue(this);
36 t->SetState(ThreadState::Waiting);
37
38 // Add the thread to the queue.
39 wait_list.push_back(*t);
40
41 return true;
42 }
43
44 void WakeupThread(KThread* t) {
45 KScopedSchedulerLock sl{kernel};
46
47 // Remove the thread from the queue.
48 wait_list.erase(wait_list.iterator_to(*t));
49
50 // Mark the thread as no longer sleeping.
51 t->SetState(ThreadState::Runnable);
52 t->SetSleepingQueue(nullptr);
53 }
54
55 KThread* WakeupFrontThread() {
56 KScopedSchedulerLock sl{kernel};
57
58 if (wait_list.empty()) {
59 return nullptr;
60 } else {
61 // Remove the thread from the queue.
62 auto it = wait_list.begin();
63 KThread* thread = std::addressof(*it);
64 wait_list.erase(it);
65
66 ASSERT(thread->GetState() == ThreadState::Waiting);
67
68 // Mark the thread as no longer sleeping.
69 thread->SetState(ThreadState::Runnable);
70 thread->SetSleepingQueue(nullptr);
71
72 return thread;
73 }
74 }
75 22
76private: 23private:
77 KernelCore& kernel; 24 KernelCore& kernel;
78 KThread::WaiterList wait_list{}; 25 KThread::WaiterList wait_list{};
79}; 26};
80 27
28class KThreadQueueWithoutEndWait : public KThreadQueue {
29public:
30 explicit KThreadQueueWithoutEndWait(KernelCore& kernel_) : KThreadQueue(kernel_) {}
31
32 void EndWait(KThread* waiting_thread, ResultCode wait_result) override final;
33};
34
81} // namespace Kernel 35} // namespace Kernel
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index e42a6d36f..2e4e4cb1c 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -14,6 +14,7 @@
14#include "common/assert.h" 14#include "common/assert.h"
15#include "common/logging/log.h" 15#include "common/logging/log.h"
16#include "common/microprofile.h" 16#include "common/microprofile.h"
17#include "common/scope_exit.h"
17#include "common/thread.h" 18#include "common/thread.h"
18#include "common/thread_worker.h" 19#include "common/thread_worker.h"
19#include "core/arm/arm_interface.h" 20#include "core/arm/arm_interface.h"
@@ -83,12 +84,16 @@ struct KernelCore::Impl {
83 } 84 }
84 85
85 void InitializeCores() { 86 void InitializeCores() {
86 for (auto& core : cores) { 87 for (u32 core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) {
87 core.Initialize(current_process->Is64BitProcess()); 88 cores[core_id].Initialize(current_process->Is64BitProcess());
89 system.Memory().SetCurrentPageTable(*current_process, core_id);
88 } 90 }
89 } 91 }
90 92
91 void Shutdown() { 93 void Shutdown() {
94 is_shutting_down.store(true, std::memory_order_relaxed);
95 SCOPE_EXIT({ is_shutting_down.store(false, std::memory_order_relaxed); });
96
92 process_list.clear(); 97 process_list.clear();
93 98
94 // Close all open server ports. 99 // Close all open server ports.
@@ -123,15 +128,6 @@ struct KernelCore::Impl {
123 next_user_process_id = KProcess::ProcessIDMin; 128 next_user_process_id = KProcess::ProcessIDMin;
124 next_thread_id = 1; 129 next_thread_id = 1;
125 130
126 for (u32 core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) {
127 if (suspend_threads[core_id]) {
128 suspend_threads[core_id]->Close();
129 suspend_threads[core_id] = nullptr;
130 }
131
132 schedulers[core_id].reset();
133 }
134
135 cores.clear(); 131 cores.clear();
136 132
137 global_handle_table->Finalize(); 133 global_handle_table->Finalize();
@@ -159,6 +155,16 @@ struct KernelCore::Impl {
159 CleanupObject(time_shared_mem); 155 CleanupObject(time_shared_mem);
160 CleanupObject(system_resource_limit); 156 CleanupObject(system_resource_limit);
161 157
158 for (u32 core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) {
159 if (suspend_threads[core_id]) {
160 suspend_threads[core_id]->Close();
161 suspend_threads[core_id] = nullptr;
162 }
163
164 schedulers[core_id]->Finalize();
165 schedulers[core_id].reset();
166 }
167
162 // Next host thead ID to use, 0-3 IDs represent core threads, >3 represent others 168 // Next host thead ID to use, 0-3 IDs represent core threads, >3 represent others
163 next_host_thread_id = Core::Hardware::NUM_CPU_CORES; 169 next_host_thread_id = Core::Hardware::NUM_CPU_CORES;
164 170
@@ -245,13 +251,11 @@ struct KernelCore::Impl {
245 KScopedSchedulerLock lock(kernel); 251 KScopedSchedulerLock lock(kernel);
246 global_scheduler_context->PreemptThreads(); 252 global_scheduler_context->PreemptThreads();
247 } 253 }
248 const auto time_interval = std::chrono::nanoseconds{ 254 const auto time_interval = std::chrono::nanoseconds{std::chrono::milliseconds(10)};
249 Core::Timing::msToCycles(std::chrono::milliseconds(10))};
250 system.CoreTiming().ScheduleEvent(time_interval, preemption_event); 255 system.CoreTiming().ScheduleEvent(time_interval, preemption_event);
251 }); 256 });
252 257
253 const auto time_interval = 258 const auto time_interval = std::chrono::nanoseconds{std::chrono::milliseconds(10)};
254 std::chrono::nanoseconds{Core::Timing::msToCycles(std::chrono::milliseconds(10))};
255 system.CoreTiming().ScheduleEvent(time_interval, preemption_event); 259 system.CoreTiming().ScheduleEvent(time_interval, preemption_event);
256 } 260 }
257 261
@@ -267,14 +271,6 @@ struct KernelCore::Impl {
267 271
268 void MakeCurrentProcess(KProcess* process) { 272 void MakeCurrentProcess(KProcess* process) {
269 current_process = process; 273 current_process = process;
270 if (process == nullptr) {
271 return;
272 }
273
274 const u32 core_id = GetCurrentHostThreadID();
275 if (core_id < Core::Hardware::NUM_CPU_CORES) {
276 system.Memory().SetCurrentPageTable(*process, core_id);
277 }
278 } 274 }
279 275
280 static inline thread_local u32 host_thread_id = UINT32_MAX; 276 static inline thread_local u32 host_thread_id = UINT32_MAX;
@@ -300,15 +296,16 @@ struct KernelCore::Impl {
300 // Gets the dummy KThread for the caller, allocating a new one if this is the first time 296 // Gets the dummy KThread for the caller, allocating a new one if this is the first time
301 KThread* GetHostDummyThread() { 297 KThread* GetHostDummyThread() {
302 auto make_thread = [this]() { 298 auto make_thread = [this]() {
303 std::unique_ptr<KThread> thread = std::make_unique<KThread>(system.Kernel()); 299 std::lock_guard lk(dummy_thread_lock);
300 auto& thread = dummy_threads.emplace_back(std::make_unique<KThread>(system.Kernel()));
304 KAutoObject::Create(thread.get()); 301 KAutoObject::Create(thread.get());
305 ASSERT(KThread::InitializeDummyThread(thread.get()).IsSuccess()); 302 ASSERT(KThread::InitializeDummyThread(thread.get()).IsSuccess());
306 thread->SetName(fmt::format("DummyThread:{}", GetHostThreadId())); 303 thread->SetName(fmt::format("DummyThread:{}", GetHostThreadId()));
307 return thread; 304 return thread.get();
308 }; 305 };
309 306
310 thread_local auto thread = make_thread(); 307 thread_local KThread* saved_thread = make_thread();
311 return thread.get(); 308 return saved_thread;
312 } 309 }
313 310
314 /// Registers a CPU core thread by allocating a host thread ID for it 311 /// Registers a CPU core thread by allocating a host thread ID for it
@@ -343,7 +340,16 @@ struct KernelCore::Impl {
343 is_phantom_mode_for_singlecore = value; 340 is_phantom_mode_for_singlecore = value;
344 } 341 }
345 342
343 bool IsShuttingDown() const {
344 return is_shutting_down.load(std::memory_order_relaxed);
345 }
346
346 KThread* GetCurrentEmuThread() { 347 KThread* GetCurrentEmuThread() {
348 // If we are shutting down the kernel, none of this is relevant anymore.
349 if (IsShuttingDown()) {
350 return {};
351 }
352
347 const auto thread_id = GetCurrentHostThreadID(); 353 const auto thread_id = GetCurrentHostThreadID();
348 if (thread_id >= Core::Hardware::NUM_CPU_CORES) { 354 if (thread_id >= Core::Hardware::NUM_CPU_CORES) {
349 return GetHostDummyThread(); 355 return GetHostDummyThread();
@@ -695,6 +701,12 @@ struct KernelCore::Impl {
695 return port; 701 return port;
696 } 702 }
697 703
704 std::mutex server_ports_lock;
705 std::mutex server_sessions_lock;
706 std::mutex registered_objects_lock;
707 std::mutex registered_in_use_objects_lock;
708 std::mutex dummy_thread_lock;
709
698 std::atomic<u32> next_object_id{0}; 710 std::atomic<u32> next_object_id{0};
699 std::atomic<u64> next_kernel_process_id{KProcess::InitialKIPIDMin}; 711 std::atomic<u64> next_kernel_process_id{KProcess::InitialKIPIDMin};
700 std::atomic<u64> next_user_process_id{KProcess::ProcessIDMin}; 712 std::atomic<u64> next_user_process_id{KProcess::ProcessIDMin};
@@ -725,10 +737,6 @@ struct KernelCore::Impl {
725 std::unordered_set<KServerSession*> server_sessions; 737 std::unordered_set<KServerSession*> server_sessions;
726 std::unordered_set<KAutoObject*> registered_objects; 738 std::unordered_set<KAutoObject*> registered_objects;
727 std::unordered_set<KAutoObject*> registered_in_use_objects; 739 std::unordered_set<KAutoObject*> registered_in_use_objects;
728 std::mutex server_ports_lock;
729 std::mutex server_sessions_lock;
730 std::mutex registered_objects_lock;
731 std::mutex registered_in_use_objects_lock;
732 740
733 std::unique_ptr<Core::ExclusiveMonitor> exclusive_monitor; 741 std::unique_ptr<Core::ExclusiveMonitor> exclusive_monitor;
734 std::vector<Kernel::PhysicalCore> cores; 742 std::vector<Kernel::PhysicalCore> cores;
@@ -753,7 +761,11 @@ struct KernelCore::Impl {
753 std::array<Core::CPUInterruptHandler, Core::Hardware::NUM_CPU_CORES> interrupts{}; 761 std::array<Core::CPUInterruptHandler, Core::Hardware::NUM_CPU_CORES> interrupts{};
754 std::array<std::unique_ptr<Kernel::KScheduler>, Core::Hardware::NUM_CPU_CORES> schedulers{}; 762 std::array<std::unique_ptr<Kernel::KScheduler>, Core::Hardware::NUM_CPU_CORES> schedulers{};
755 763
764 // Specifically tracked to be automatically destroyed with kernel
765 std::vector<std::unique_ptr<KThread>> dummy_threads;
766
756 bool is_multicore{}; 767 bool is_multicore{};
768 std::atomic_bool is_shutting_down{};
757 bool is_phantom_mode_for_singlecore{}; 769 bool is_phantom_mode_for_singlecore{};
758 u32 single_core_thread_id{}; 770 u32 single_core_thread_id{};
759 771
@@ -839,16 +851,20 @@ const Kernel::PhysicalCore& KernelCore::PhysicalCore(std::size_t id) const {
839 return impl->cores[id]; 851 return impl->cores[id];
840} 852}
841 853
854size_t KernelCore::CurrentPhysicalCoreIndex() const {
855 const u32 core_id = impl->GetCurrentHostThreadID();
856 if (core_id >= Core::Hardware::NUM_CPU_CORES) {
857 return Core::Hardware::NUM_CPU_CORES - 1;
858 }
859 return core_id;
860}
861
842Kernel::PhysicalCore& KernelCore::CurrentPhysicalCore() { 862Kernel::PhysicalCore& KernelCore::CurrentPhysicalCore() {
843 u32 core_id = impl->GetCurrentHostThreadID(); 863 return impl->cores[CurrentPhysicalCoreIndex()];
844 ASSERT(core_id < Core::Hardware::NUM_CPU_CORES);
845 return impl->cores[core_id];
846} 864}
847 865
848const Kernel::PhysicalCore& KernelCore::CurrentPhysicalCore() const { 866const Kernel::PhysicalCore& KernelCore::CurrentPhysicalCore() const {
849 u32 core_id = impl->GetCurrentHostThreadID(); 867 return impl->cores[CurrentPhysicalCoreIndex()];
850 ASSERT(core_id < Core::Hardware::NUM_CPU_CORES);
851 return impl->cores[core_id];
852} 868}
853 869
854Kernel::KScheduler* KernelCore::CurrentScheduler() { 870Kernel::KScheduler* KernelCore::CurrentScheduler() {
@@ -1051,6 +1067,9 @@ void KernelCore::Suspend(bool in_suspention) {
1051 impl->suspend_threads[core_id]->SetState(state); 1067 impl->suspend_threads[core_id]->SetState(state);
1052 impl->suspend_threads[core_id]->SetWaitReasonForDebugging( 1068 impl->suspend_threads[core_id]->SetWaitReasonForDebugging(
1053 ThreadWaitReasonForDebugging::Suspended); 1069 ThreadWaitReasonForDebugging::Suspended);
1070 if (!should_suspend) {
1071 impl->suspend_threads[core_id]->DisableDispatch();
1072 }
1054 } 1073 }
1055 } 1074 }
1056} 1075}
@@ -1059,19 +1078,21 @@ bool KernelCore::IsMulticore() const {
1059 return impl->is_multicore; 1078 return impl->is_multicore;
1060} 1079}
1061 1080
1081bool KernelCore::IsShuttingDown() const {
1082 return impl->IsShuttingDown();
1083}
1084
1062void KernelCore::ExceptionalExit() { 1085void KernelCore::ExceptionalExit() {
1063 exception_exited = true; 1086 exception_exited = true;
1064 Suspend(true); 1087 Suspend(true);
1065} 1088}
1066 1089
1067void KernelCore::EnterSVCProfile() { 1090void KernelCore::EnterSVCProfile() {
1068 std::size_t core = impl->GetCurrentHostThreadID(); 1091 impl->svc_ticks[CurrentPhysicalCoreIndex()] = MicroProfileEnter(MICROPROFILE_TOKEN(Kernel_SVC));
1069 impl->svc_ticks[core] = MicroProfileEnter(MICROPROFILE_TOKEN(Kernel_SVC));
1070} 1092}
1071 1093
1072void KernelCore::ExitSVCProfile() { 1094void KernelCore::ExitSVCProfile() {
1073 std::size_t core = impl->GetCurrentHostThreadID(); 1095 MicroProfileLeave(MICROPROFILE_TOKEN(Kernel_SVC), impl->svc_ticks[CurrentPhysicalCoreIndex()]);
1074 MicroProfileLeave(MICROPROFILE_TOKEN(Kernel_SVC), impl->svc_ticks[core]);
1075} 1096}
1076 1097
1077std::weak_ptr<Kernel::ServiceThread> KernelCore::CreateServiceThread(const std::string& name) { 1098std::weak_ptr<Kernel::ServiceThread> KernelCore::CreateServiceThread(const std::string& name) {
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h
index d2ceae950..b9b423908 100644
--- a/src/core/hle/kernel/kernel.h
+++ b/src/core/hle/kernel/kernel.h
@@ -53,6 +53,7 @@ class KSharedMemoryInfo;
53class KThread; 53class KThread;
54class KTransferMemory; 54class KTransferMemory;
55class KWritableEvent; 55class KWritableEvent;
56class KCodeMemory;
56class PhysicalCore; 57class PhysicalCore;
57class ServiceThread; 58class ServiceThread;
58class Synchronization; 59class Synchronization;
@@ -148,6 +149,9 @@ public:
148 /// Gets the an instance of the respective physical CPU core. 149 /// Gets the an instance of the respective physical CPU core.
149 const Kernel::PhysicalCore& PhysicalCore(std::size_t id) const; 150 const Kernel::PhysicalCore& PhysicalCore(std::size_t id) const;
150 151
152 /// Gets the current physical core index for the running host thread.
153 std::size_t CurrentPhysicalCoreIndex() const;
154
151 /// Gets the sole instance of the Scheduler at the current running core. 155 /// Gets the sole instance of the Scheduler at the current running core.
152 Kernel::KScheduler* CurrentScheduler(); 156 Kernel::KScheduler* CurrentScheduler();
153 157
@@ -271,6 +275,8 @@ public:
271 275
272 bool IsMulticore() const; 276 bool IsMulticore() const;
273 277
278 bool IsShuttingDown() const;
279
274 void EnterSVCProfile(); 280 void EnterSVCProfile();
275 281
276 void ExitSVCProfile(); 282 void ExitSVCProfile();
@@ -326,6 +332,8 @@ public:
326 return slab_heap_container->transfer_memory; 332 return slab_heap_container->transfer_memory;
327 } else if constexpr (std::is_same_v<T, KWritableEvent>) { 333 } else if constexpr (std::is_same_v<T, KWritableEvent>) {
328 return slab_heap_container->writeable_event; 334 return slab_heap_container->writeable_event;
335 } else if constexpr (std::is_same_v<T, KCodeMemory>) {
336 return slab_heap_container->code_memory;
329 } 337 }
330 } 338 }
331 339
@@ -377,6 +385,7 @@ private:
377 KSlabHeap<KThread> thread; 385 KSlabHeap<KThread> thread;
378 KSlabHeap<KTransferMemory> transfer_memory; 386 KSlabHeap<KTransferMemory> transfer_memory;
379 KSlabHeap<KWritableEvent> writeable_event; 387 KSlabHeap<KWritableEvent> writeable_event;
388 KSlabHeap<KCodeMemory> code_memory;
380 }; 389 };
381 390
382 std::unique_ptr<SlabHeapContainer> slab_heap_container; 391 std::unique_ptr<SlabHeapContainer> slab_heap_container;
diff --git a/src/core/hle/kernel/service_thread.cpp b/src/core/hle/kernel/service_thread.cpp
index 6721b6276..03f3dec10 100644
--- a/src/core/hle/kernel/service_thread.cpp
+++ b/src/core/hle/kernel/service_thread.cpp
@@ -25,24 +25,27 @@ public:
25 void QueueSyncRequest(KSession& session, std::shared_ptr<HLERequestContext>&& context); 25 void QueueSyncRequest(KSession& session, std::shared_ptr<HLERequestContext>&& context);
26 26
27private: 27private:
28 std::vector<std::thread> threads; 28 std::vector<std::jthread> threads;
29 std::queue<std::function<void()>> requests; 29 std::queue<std::function<void()>> requests;
30 std::mutex queue_mutex; 30 std::mutex queue_mutex;
31 std::condition_variable condition; 31 std::condition_variable_any condition;
32 const std::string service_name; 32 const std::string service_name;
33 bool stop{};
34}; 33};
35 34
36ServiceThread::Impl::Impl(KernelCore& kernel, std::size_t num_threads, const std::string& name) 35ServiceThread::Impl::Impl(KernelCore& kernel, std::size_t num_threads, const std::string& name)
37 : service_name{name} { 36 : service_name{name} {
38 for (std::size_t i = 0; i < num_threads; ++i) 37 for (std::size_t i = 0; i < num_threads; ++i) {
39 threads.emplace_back([this, &kernel] { 38 threads.emplace_back([this, &kernel](std::stop_token stop_token) {
40 Common::SetCurrentThreadName(std::string{"yuzu:HleService:" + service_name}.c_str()); 39 Common::SetCurrentThreadName(std::string{"yuzu:HleService:" + service_name}.c_str());
41 40
42 // Wait for first request before trying to acquire a render context 41 // Wait for first request before trying to acquire a render context
43 { 42 {
44 std::unique_lock lock{queue_mutex}; 43 std::unique_lock lock{queue_mutex};
45 condition.wait(lock, [this] { return stop || !requests.empty(); }); 44 condition.wait(lock, stop_token, [this] { return !requests.empty(); });
45 }
46
47 if (stop_token.stop_requested()) {
48 return;
46 } 49 }
47 50
48 kernel.RegisterHostThread(); 51 kernel.RegisterHostThread();
@@ -52,10 +55,16 @@ ServiceThread::Impl::Impl(KernelCore& kernel, std::size_t num_threads, const std
52 55
53 { 56 {
54 std::unique_lock lock{queue_mutex}; 57 std::unique_lock lock{queue_mutex};
55 condition.wait(lock, [this] { return stop || !requests.empty(); }); 58 condition.wait(lock, stop_token, [this] { return !requests.empty(); });
56 if (stop || requests.empty()) { 59
60 if (stop_token.stop_requested()) {
57 return; 61 return;
58 } 62 }
63
64 if (requests.empty()) {
65 continue;
66 }
67
59 task = std::move(requests.front()); 68 task = std::move(requests.front());
60 requests.pop(); 69 requests.pop();
61 } 70 }
@@ -63,6 +72,7 @@ ServiceThread::Impl::Impl(KernelCore& kernel, std::size_t num_threads, const std
63 task(); 72 task();
64 } 73 }
65 }); 74 });
75 }
66} 76}
67 77
68void ServiceThread::Impl::QueueSyncRequest(KSession& session, 78void ServiceThread::Impl::QueueSyncRequest(KSession& session,
@@ -88,12 +98,9 @@ void ServiceThread::Impl::QueueSyncRequest(KSession& session,
88} 98}
89 99
90ServiceThread::Impl::~Impl() { 100ServiceThread::Impl::~Impl() {
91 {
92 std::unique_lock lock{queue_mutex};
93 stop = true;
94 }
95 condition.notify_all(); 101 condition.notify_all();
96 for (std::thread& thread : threads) { 102 for (auto& thread : threads) {
103 thread.request_stop();
97 thread.join(); 104 thread.join();
98 } 105 }
99} 106}
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp
index f9d99bc51..a9f7438ea 100644
--- a/src/core/hle/kernel/svc.cpp
+++ b/src/core/hle/kernel/svc.cpp
@@ -18,6 +18,7 @@
18#include "core/core_timing.h" 18#include "core/core_timing.h"
19#include "core/hle/kernel/k_client_port.h" 19#include "core/hle/kernel/k_client_port.h"
20#include "core/hle/kernel/k_client_session.h" 20#include "core/hle/kernel/k_client_session.h"
21#include "core/hle/kernel/k_code_memory.h"
21#include "core/hle/kernel/k_event.h" 22#include "core/hle/kernel/k_event.h"
22#include "core/hle/kernel/k_handle_table.h" 23#include "core/hle/kernel/k_handle_table.h"
23#include "core/hle/kernel/k_memory_block.h" 24#include "core/hle/kernel/k_memory_block.h"
@@ -31,6 +32,7 @@
31#include "core/hle/kernel/k_shared_memory.h" 32#include "core/hle/kernel/k_shared_memory.h"
32#include "core/hle/kernel/k_synchronization_object.h" 33#include "core/hle/kernel/k_synchronization_object.h"
33#include "core/hle/kernel/k_thread.h" 34#include "core/hle/kernel/k_thread.h"
35#include "core/hle/kernel/k_thread_queue.h"
34#include "core/hle/kernel/k_transfer_memory.h" 36#include "core/hle/kernel/k_transfer_memory.h"
35#include "core/hle/kernel/k_writable_event.h" 37#include "core/hle/kernel/k_writable_event.h"
36#include "core/hle/kernel/kernel.h" 38#include "core/hle/kernel/kernel.h"
@@ -307,26 +309,29 @@ static ResultCode ConnectToNamedPort32(Core::System& system, Handle* out_handle,
307 309
308/// Makes a blocking IPC call to an OS service. 310/// Makes a blocking IPC call to an OS service.
309static ResultCode SendSyncRequest(Core::System& system, Handle handle) { 311static ResultCode SendSyncRequest(Core::System& system, Handle handle) {
310
311 auto& kernel = system.Kernel(); 312 auto& kernel = system.Kernel();
312 313
314 // Create the wait queue.
315 KThreadQueue wait_queue(kernel);
316
317 // Get the client session from its handle.
318 KScopedAutoObject session =
319 kernel.CurrentProcess()->GetHandleTable().GetObject<KClientSession>(handle);
320 R_UNLESS(session.IsNotNull(), ResultInvalidHandle);
321
322 LOG_TRACE(Kernel_SVC, "called handle=0x{:08X}({})", handle, session->GetName());
323
313 auto thread = kernel.CurrentScheduler()->GetCurrentThread(); 324 auto thread = kernel.CurrentScheduler()->GetCurrentThread();
314 { 325 {
315 KScopedSchedulerLock lock(kernel); 326 KScopedSchedulerLock lock(kernel);
316 thread->SetState(ThreadState::Waiting); 327
317 thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::IPC); 328 // This is a synchronous request, so we should wait for our request to complete.
318 329 GetCurrentThread(kernel).BeginWait(std::addressof(wait_queue));
319 { 330 GetCurrentThread(kernel).SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::IPC);
320 KScopedAutoObject session = 331 session->SendSyncRequest(&GetCurrentThread(kernel), system.Memory(), system.CoreTiming());
321 kernel.CurrentProcess()->GetHandleTable().GetObject<KClientSession>(handle);
322 R_UNLESS(session.IsNotNull(), ResultInvalidHandle);
323 LOG_TRACE(Kernel_SVC, "called handle=0x{:08X}({})", handle, session->GetName());
324 session->SendSyncRequest(thread, system.Memory(), system.CoreTiming());
325 }
326 } 332 }
327 333
328 KSynchronizationObject* dummy{}; 334 return thread->GetWaitResult();
329 return thread->GetWaitResult(std::addressof(dummy));
330} 335}
331 336
332static ResultCode SendSyncRequest32(Core::System& system, Handle handle) { 337static ResultCode SendSyncRequest32(Core::System& system, Handle handle) {
@@ -873,7 +878,7 @@ static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, Handle
873 const u64 thread_ticks = current_thread->GetCpuTime(); 878 const u64 thread_ticks = current_thread->GetCpuTime();
874 879
875 out_ticks = thread_ticks + (core_timing.GetCPUTicks() - prev_ctx_ticks); 880 out_ticks = thread_ticks + (core_timing.GetCPUTicks() - prev_ctx_ticks);
876 } else if (same_thread && info_sub_id == system.CurrentCoreIndex()) { 881 } else if (same_thread && info_sub_id == system.Kernel().CurrentPhysicalCoreIndex()) {
877 out_ticks = core_timing.GetCPUTicks() - prev_ctx_ticks; 882 out_ticks = core_timing.GetCPUTicks() - prev_ctx_ticks;
878 } 883 }
879 884
@@ -887,7 +892,8 @@ static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, Handle
887 return ResultInvalidHandle; 892 return ResultInvalidHandle;
888 } 893 }
889 894
890 if (info_sub_id != 0xFFFFFFFFFFFFFFFF && info_sub_id != system.CurrentCoreIndex()) { 895 if (info_sub_id != 0xFFFFFFFFFFFFFFFF &&
896 info_sub_id != system.Kernel().CurrentPhysicalCoreIndex()) {
891 LOG_ERROR(Kernel_SVC, "Core is not the current core, got {}", info_sub_id); 897 LOG_ERROR(Kernel_SVC, "Core is not the current core, got {}", info_sub_id);
892 return ResultInvalidCombination; 898 return ResultInvalidCombination;
893 } 899 }
@@ -1169,6 +1175,8 @@ static u32 GetCurrentProcessorNumber32(Core::System& system) {
1169 return GetCurrentProcessorNumber(system); 1175 return GetCurrentProcessorNumber(system);
1170} 1176}
1171 1177
1178namespace {
1179
1172constexpr bool IsValidSharedMemoryPermission(Svc::MemoryPermission perm) { 1180constexpr bool IsValidSharedMemoryPermission(Svc::MemoryPermission perm) {
1173 switch (perm) { 1181 switch (perm) {
1174 case Svc::MemoryPermission::Read: 1182 case Svc::MemoryPermission::Read:
@@ -1179,10 +1187,40 @@ constexpr bool IsValidSharedMemoryPermission(Svc::MemoryPermission perm) {
1179 } 1187 }
1180} 1188}
1181 1189
1182constexpr bool IsValidRemoteSharedMemoryPermission(Svc::MemoryPermission perm) { 1190[[maybe_unused]] constexpr bool IsValidRemoteSharedMemoryPermission(Svc::MemoryPermission perm) {
1183 return IsValidSharedMemoryPermission(perm) || perm == Svc::MemoryPermission::DontCare; 1191 return IsValidSharedMemoryPermission(perm) || perm == Svc::MemoryPermission::DontCare;
1184} 1192}
1185 1193
1194constexpr bool IsValidProcessMemoryPermission(Svc::MemoryPermission perm) {
1195 switch (perm) {
1196 case Svc::MemoryPermission::None:
1197 case Svc::MemoryPermission::Read:
1198 case Svc::MemoryPermission::ReadWrite:
1199 case Svc::MemoryPermission::ReadExecute:
1200 return true;
1201 default:
1202 return false;
1203 }
1204}
1205
1206constexpr bool IsValidMapCodeMemoryPermission(Svc::MemoryPermission perm) {
1207 return perm == Svc::MemoryPermission::ReadWrite;
1208}
1209
1210constexpr bool IsValidMapToOwnerCodeMemoryPermission(Svc::MemoryPermission perm) {
1211 return perm == Svc::MemoryPermission::Read || perm == Svc::MemoryPermission::ReadExecute;
1212}
1213
1214constexpr bool IsValidUnmapCodeMemoryPermission(Svc::MemoryPermission perm) {
1215 return perm == Svc::MemoryPermission::None;
1216}
1217
1218constexpr bool IsValidUnmapFromOwnerCodeMemoryPermission(Svc::MemoryPermission perm) {
1219 return perm == Svc::MemoryPermission::None;
1220}
1221
1222} // Anonymous namespace
1223
1186static ResultCode MapSharedMemory(Core::System& system, Handle shmem_handle, VAddr address, 1224static ResultCode MapSharedMemory(Core::System& system, Handle shmem_handle, VAddr address,
1187 u64 size, Svc::MemoryPermission map_perm) { 1225 u64 size, Svc::MemoryPermission map_perm) {
1188 LOG_TRACE(Kernel_SVC, 1226 LOG_TRACE(Kernel_SVC,
@@ -1262,6 +1300,223 @@ static ResultCode UnmapSharedMemory32(Core::System& system, Handle shmem_handle,
1262 return UnmapSharedMemory(system, shmem_handle, address, size); 1300 return UnmapSharedMemory(system, shmem_handle, address, size);
1263} 1301}
1264 1302
1303static ResultCode SetProcessMemoryPermission(Core::System& system, Handle process_handle,
1304 VAddr address, u64 size, Svc::MemoryPermission perm) {
1305 LOG_TRACE(Kernel_SVC,
1306 "called, process_handle=0x{:X}, addr=0x{:X}, size=0x{:X}, permissions=0x{:08X}",
1307 process_handle, address, size, perm);
1308
1309 // Validate the address/size.
1310 R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress);
1311 R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize);
1312 R_UNLESS(size > 0, ResultInvalidSize);
1313 R_UNLESS((address < address + size), ResultInvalidCurrentMemory);
1314
1315 // Validate the memory permission.
1316 R_UNLESS(IsValidProcessMemoryPermission(perm), ResultInvalidNewMemoryPermission);
1317
1318 // Get the process from its handle.
1319 KScopedAutoObject process =
1320 system.CurrentProcess()->GetHandleTable().GetObject<KProcess>(process_handle);
1321 R_UNLESS(process.IsNotNull(), ResultInvalidHandle);
1322
1323 // Validate that the address is in range.
1324 auto& page_table = process->PageTable();
1325 R_UNLESS(page_table.Contains(address, size), ResultInvalidCurrentMemory);
1326
1327 // Set the memory permission.
1328 return page_table.SetProcessMemoryPermission(address, size, ConvertToKMemoryPermission(perm));
1329}
1330
1331static ResultCode MapProcessMemory(Core::System& system, VAddr dst_address, Handle process_handle,
1332 VAddr src_address, u64 size) {
1333 LOG_TRACE(Kernel_SVC,
1334 "called, dst_address=0x{:X}, process_handle=0x{:X}, src_address=0x{:X}, size=0x{:X}",
1335 dst_address, process_handle, src_address, size);
1336
1337 // Validate the address/size.
1338 R_UNLESS(Common::IsAligned(dst_address, PageSize), ResultInvalidAddress);
1339 R_UNLESS(Common::IsAligned(src_address, PageSize), ResultInvalidAddress);
1340 R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize);
1341 R_UNLESS(size > 0, ResultInvalidSize);
1342 R_UNLESS((dst_address < dst_address + size), ResultInvalidCurrentMemory);
1343 R_UNLESS((src_address < src_address + size), ResultInvalidCurrentMemory);
1344
1345 // Get the processes.
1346 KProcess* dst_process = system.CurrentProcess();
1347 KScopedAutoObject src_process =
1348 dst_process->GetHandleTable().GetObjectWithoutPseudoHandle<KProcess>(process_handle);
1349 R_UNLESS(src_process.IsNotNull(), ResultInvalidHandle);
1350
1351 // Get the page tables.
1352 auto& dst_pt = dst_process->PageTable();
1353 auto& src_pt = src_process->PageTable();
1354
1355 // Validate that the mapping is in range.
1356 R_UNLESS(src_pt.Contains(src_address, size), ResultInvalidCurrentMemory);
1357 R_UNLESS(dst_pt.CanContain(dst_address, size, KMemoryState::SharedCode),
1358 ResultInvalidMemoryRegion);
1359
1360 // Create a new page group.
1361 KMemoryInfo kBlockInfo = dst_pt.QueryInfo(dst_address);
1362 KPageLinkedList pg(kBlockInfo.GetAddress(), kBlockInfo.GetNumPages());
1363
1364 // Map the group.
1365 R_TRY(dst_pt.MapPages(dst_address, pg, KMemoryState::SharedCode,
1366 KMemoryPermission::UserReadWrite));
1367
1368 return ResultSuccess;
1369}
1370
1371static ResultCode UnmapProcessMemory(Core::System& system, VAddr dst_address, Handle process_handle,
1372 VAddr src_address, u64 size) {
1373 LOG_TRACE(Kernel_SVC,
1374 "called, dst_address=0x{:X}, process_handle=0x{:X}, src_address=0x{:X}, size=0x{:X}",
1375 dst_address, process_handle, src_address, size);
1376
1377 // Validate the address/size.
1378 R_UNLESS(Common::IsAligned(dst_address, PageSize), ResultInvalidAddress);
1379 R_UNLESS(Common::IsAligned(src_address, PageSize), ResultInvalidAddress);
1380 R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize);
1381 R_UNLESS(size > 0, ResultInvalidSize);
1382 R_UNLESS((dst_address < dst_address + size), ResultInvalidCurrentMemory);
1383 R_UNLESS((src_address < src_address + size), ResultInvalidCurrentMemory);
1384
1385 // Get the processes.
1386 KProcess* dst_process = system.CurrentProcess();
1387 KScopedAutoObject src_process =
1388 dst_process->GetHandleTable().GetObjectWithoutPseudoHandle<KProcess>(process_handle);
1389 R_UNLESS(src_process.IsNotNull(), ResultInvalidHandle);
1390
1391 // Get the page tables.
1392 auto& dst_pt = dst_process->PageTable();
1393 auto& src_pt = src_process->PageTable();
1394
1395 // Validate that the mapping is in range.
1396 R_UNLESS(src_pt.Contains(src_address, size), ResultInvalidCurrentMemory);
1397 R_UNLESS(dst_pt.CanContain(dst_address, size, KMemoryState::SharedCode),
1398 ResultInvalidMemoryRegion);
1399
1400 // Unmap the memory.
1401 R_TRY(dst_pt.UnmapProcessMemory(dst_address, size, src_pt, src_address));
1402
1403 return ResultSuccess;
1404}
1405
1406static ResultCode CreateCodeMemory(Core::System& system, Handle* out, VAddr address, size_t size) {
1407 LOG_TRACE(Kernel_SVC, "called, handle_out=0x{:X}, address=0x{:X}, size=0x{:X}",
1408 static_cast<void*>(out), address, size);
1409 // Get kernel instance.
1410 auto& kernel = system.Kernel();
1411
1412 // Validate address / size.
1413 R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress);
1414 R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize);
1415 R_UNLESS(size > 0, ResultInvalidSize);
1416 R_UNLESS((address < address + size), ResultInvalidCurrentMemory);
1417
1418 // Create the code memory.
1419
1420 KCodeMemory* code_mem = KCodeMemory::Create(kernel);
1421 R_UNLESS(code_mem != nullptr, ResultOutOfResource);
1422
1423 // Verify that the region is in range.
1424 R_UNLESS(system.CurrentProcess()->PageTable().Contains(address, size),
1425 ResultInvalidCurrentMemory);
1426
1427 // Initialize the code memory.
1428 R_TRY(code_mem->Initialize(system.DeviceMemory(), address, size));
1429
1430 // Register the code memory.
1431 KCodeMemory::Register(kernel, code_mem);
1432
1433 // Add the code memory to the handle table.
1434 R_TRY(system.CurrentProcess()->GetHandleTable().Add(out, code_mem));
1435
1436 code_mem->Close();
1437
1438 return ResultSuccess;
1439}
1440
1441static ResultCode ControlCodeMemory(Core::System& system, Handle code_memory_handle, u32 operation,
1442 VAddr address, size_t size, Svc::MemoryPermission perm) {
1443
1444 LOG_TRACE(Kernel_SVC,
1445 "called, code_memory_handle=0x{:X}, operation=0x{:X}, address=0x{:X}, size=0x{:X}, "
1446 "permission=0x{:X}",
1447 code_memory_handle, operation, address, size, perm);
1448
1449 // Validate the address / size.
1450 R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress);
1451 R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize);
1452 R_UNLESS(size > 0, ResultInvalidSize);
1453 R_UNLESS((address < address + size), ResultInvalidCurrentMemory);
1454
1455 // Get the code memory from its handle.
1456 KScopedAutoObject code_mem =
1457 system.CurrentProcess()->GetHandleTable().GetObject<KCodeMemory>(code_memory_handle);
1458 R_UNLESS(code_mem.IsNotNull(), ResultInvalidHandle);
1459
1460 // NOTE: Here, Atmosphere extends the SVC to allow code memory operations on one's own process.
1461 // This enables homebrew usage of these SVCs for JIT.
1462
1463 // Perform the operation.
1464 switch (static_cast<CodeMemoryOperation>(operation)) {
1465 case CodeMemoryOperation::Map: {
1466 // Check that the region is in range.
1467 R_UNLESS(
1468 system.CurrentProcess()->PageTable().CanContain(address, size, KMemoryState::CodeOut),
1469 ResultInvalidMemoryRegion);
1470
1471 // Check the memory permission.
1472 R_UNLESS(IsValidMapCodeMemoryPermission(perm), ResultInvalidNewMemoryPermission);
1473
1474 // Map the memory.
1475 R_TRY(code_mem->Map(address, size));
1476 } break;
1477 case CodeMemoryOperation::Unmap: {
1478 // Check that the region is in range.
1479 R_UNLESS(
1480 system.CurrentProcess()->PageTable().CanContain(address, size, KMemoryState::CodeOut),
1481 ResultInvalidMemoryRegion);
1482
1483 // Check the memory permission.
1484 R_UNLESS(IsValidUnmapCodeMemoryPermission(perm), ResultInvalidNewMemoryPermission);
1485
1486 // Unmap the memory.
1487 R_TRY(code_mem->Unmap(address, size));
1488 } break;
1489 case CodeMemoryOperation::MapToOwner: {
1490 // Check that the region is in range.
1491 R_UNLESS(code_mem->GetOwner()->PageTable().CanContain(address, size,
1492 KMemoryState::GeneratedCode),
1493 ResultInvalidMemoryRegion);
1494
1495 // Check the memory permission.
1496 R_UNLESS(IsValidMapToOwnerCodeMemoryPermission(perm), ResultInvalidNewMemoryPermission);
1497
1498 // Map the memory to its owner.
1499 R_TRY(code_mem->MapToOwner(address, size, perm));
1500 } break;
1501 case CodeMemoryOperation::UnmapFromOwner: {
1502 // Check that the region is in range.
1503 R_UNLESS(code_mem->GetOwner()->PageTable().CanContain(address, size,
1504 KMemoryState::GeneratedCode),
1505 ResultInvalidMemoryRegion);
1506
1507 // Check the memory permission.
1508 R_UNLESS(IsValidUnmapFromOwnerCodeMemoryPermission(perm), ResultInvalidNewMemoryPermission);
1509
1510 // Unmap the memory from its owner.
1511 R_TRY(code_mem->UnmapFromOwner(address, size));
1512 } break;
1513 default:
1514 return ResultInvalidEnumValue;
1515 }
1516
1517 return ResultSuccess;
1518}
1519
1265static ResultCode QueryProcessMemory(Core::System& system, VAddr memory_info_address, 1520static ResultCode QueryProcessMemory(Core::System& system, VAddr memory_info_address,
1266 VAddr page_info_address, Handle process_handle, 1521 VAddr page_info_address, Handle process_handle,
1267 VAddr address) { 1522 VAddr address) {
@@ -1459,10 +1714,14 @@ static void ExitProcess32(Core::System& system) {
1459 ExitProcess(system); 1714 ExitProcess(system);
1460} 1715}
1461 1716
1462static constexpr bool IsValidVirtualCoreId(int32_t core_id) { 1717namespace {
1718
1719constexpr bool IsValidVirtualCoreId(int32_t core_id) {
1463 return (0 <= core_id && core_id < static_cast<int32_t>(Core::Hardware::NUM_CPU_CORES)); 1720 return (0 <= core_id && core_id < static_cast<int32_t>(Core::Hardware::NUM_CPU_CORES));
1464} 1721}
1465 1722
1723} // Anonymous namespace
1724
1466/// Creates a new thread 1725/// Creates a new thread
1467static ResultCode CreateThread(Core::System& system, Handle* out_handle, VAddr entry_point, u64 arg, 1726static ResultCode CreateThread(Core::System& system, Handle* out_handle, VAddr entry_point, u64 arg,
1468 VAddr stack_bottom, u32 priority, s32 core_id) { 1727 VAddr stack_bottom, u32 priority, s32 core_id) {
@@ -1846,7 +2105,9 @@ static ResultCode ResetSignal32(Core::System& system, Handle handle) {
1846 return ResetSignal(system, handle); 2105 return ResetSignal(system, handle);
1847} 2106}
1848 2107
1849static constexpr bool IsValidTransferMemoryPermission(MemoryPermission perm) { 2108namespace {
2109
2110constexpr bool IsValidTransferMemoryPermission(MemoryPermission perm) {
1850 switch (perm) { 2111 switch (perm) {
1851 case MemoryPermission::None: 2112 case MemoryPermission::None:
1852 case MemoryPermission::Read: 2113 case MemoryPermission::Read:
@@ -1857,6 +2118,8 @@ static constexpr bool IsValidTransferMemoryPermission(MemoryPermission perm) {
1857 } 2118 }
1858} 2119}
1859 2120
2121} // Anonymous namespace
2122
1860/// Creates a TransferMemory object 2123/// Creates a TransferMemory object
1861static ResultCode CreateTransferMemory(Core::System& system, Handle* out, VAddr address, u64 size, 2124static ResultCode CreateTransferMemory(Core::System& system, Handle* out, VAddr address, u64 size,
1862 MemoryPermission map_perm) { 2125 MemoryPermission map_perm) {
@@ -2548,8 +2811,8 @@ static const FunctionDef SVC_Table_64[] = {
2548 {0x48, nullptr, "MapPhysicalMemoryUnsafe"}, 2811 {0x48, nullptr, "MapPhysicalMemoryUnsafe"},
2549 {0x49, nullptr, "UnmapPhysicalMemoryUnsafe"}, 2812 {0x49, nullptr, "UnmapPhysicalMemoryUnsafe"},
2550 {0x4A, nullptr, "SetUnsafeLimit"}, 2813 {0x4A, nullptr, "SetUnsafeLimit"},
2551 {0x4B, nullptr, "CreateCodeMemory"}, 2814 {0x4B, SvcWrap64<CreateCodeMemory>, "CreateCodeMemory"},
2552 {0x4C, nullptr, "ControlCodeMemory"}, 2815 {0x4C, SvcWrap64<ControlCodeMemory>, "ControlCodeMemory"},
2553 {0x4D, nullptr, "SleepSystem"}, 2816 {0x4D, nullptr, "SleepSystem"},
2554 {0x4E, nullptr, "ReadWriteRegister"}, 2817 {0x4E, nullptr, "ReadWriteRegister"},
2555 {0x4F, nullptr, "SetProcessActivity"}, 2818 {0x4F, nullptr, "SetProcessActivity"},
@@ -2588,9 +2851,9 @@ static const FunctionDef SVC_Table_64[] = {
2588 {0x70, nullptr, "CreatePort"}, 2851 {0x70, nullptr, "CreatePort"},
2589 {0x71, nullptr, "ManageNamedPort"}, 2852 {0x71, nullptr, "ManageNamedPort"},
2590 {0x72, nullptr, "ConnectToPort"}, 2853 {0x72, nullptr, "ConnectToPort"},
2591 {0x73, nullptr, "SetProcessMemoryPermission"}, 2854 {0x73, SvcWrap64<SetProcessMemoryPermission>, "SetProcessMemoryPermission"},
2592 {0x74, nullptr, "MapProcessMemory"}, 2855 {0x74, SvcWrap64<MapProcessMemory>, "MapProcessMemory"},
2593 {0x75, nullptr, "UnmapProcessMemory"}, 2856 {0x75, SvcWrap64<UnmapProcessMemory>, "UnmapProcessMemory"},
2594 {0x76, SvcWrap64<QueryProcessMemory>, "QueryProcessMemory"}, 2857 {0x76, SvcWrap64<QueryProcessMemory>, "QueryProcessMemory"},
2595 {0x77, SvcWrap64<MapProcessCodeMemory>, "MapProcessCodeMemory"}, 2858 {0x77, SvcWrap64<MapProcessCodeMemory>, "MapProcessCodeMemory"},
2596 {0x78, SvcWrap64<UnmapProcessCodeMemory>, "UnmapProcessCodeMemory"}, 2859 {0x78, SvcWrap64<UnmapProcessCodeMemory>, "UnmapProcessCodeMemory"},
diff --git a/src/core/hle/kernel/svc_wrap.h b/src/core/hle/kernel/svc_wrap.h
index 6e62e656f..86255fe6d 100644
--- a/src/core/hle/kernel/svc_wrap.h
+++ b/src/core/hle/kernel/svc_wrap.h
@@ -73,6 +73,23 @@ void SvcWrap64(Core::System& system) {
73 .raw); 73 .raw);
74} 74}
75 75
76// Used by MapProcessMemory and UnmapProcessMemory
77template <ResultCode func(Core::System&, u64, u32, u64, u64)>
78void SvcWrap64(Core::System& system) {
79 FuncReturn(system, func(system, Param(system, 0), static_cast<u32>(Param(system, 1)),
80 Param(system, 2), Param(system, 3))
81 .raw);
82}
83
84// Used by ControlCodeMemory
85template <ResultCode func(Core::System&, Handle, u32, u64, u64, Svc::MemoryPermission)>
86void SvcWrap64(Core::System& system) {
87 FuncReturn(system, func(system, static_cast<Handle>(Param(system, 0)),
88 static_cast<u32>(Param(system, 1)), Param(system, 2), Param(system, 3),
89 static_cast<Svc::MemoryPermission>(Param(system, 4)))
90 .raw);
91}
92
76template <ResultCode func(Core::System&, u32*)> 93template <ResultCode func(Core::System&, u32*)>
77void SvcWrap64(Core::System& system) { 94void SvcWrap64(Core::System& system) {
78 u32 param = 0; 95 u32 param = 0;
@@ -301,6 +318,16 @@ void SvcWrap64(Core::System& system) {
301 FuncReturn(system, retval); 318 FuncReturn(system, retval);
302} 319}
303 320
321// Used by CreateCodeMemory
322template <ResultCode func(Core::System&, Handle*, u64, u64)>
323void SvcWrap64(Core::System& system) {
324 u32 param_1 = 0;
325 const u32 retval = func(system, &param_1, Param(system, 1), Param(system, 2)).raw;
326
327 system.CurrentArmInterface().SetReg(1, param_1);
328 FuncReturn(system, retval);
329}
330
304template <ResultCode func(Core::System&, Handle*, u64, u32, u32)> 331template <ResultCode func(Core::System&, Handle*, u64, u32, u32)>
305void SvcWrap64(Core::System& system) { 332void SvcWrap64(Core::System& system) {
306 u32 param_1 = 0; 333 u32 param_1 = 0;
diff --git a/src/core/hle/kernel/time_manager.cpp b/src/core/hle/kernel/time_manager.cpp
index 8cd7279a3..aa985d820 100644
--- a/src/core/hle/kernel/time_manager.cpp
+++ b/src/core/hle/kernel/time_manager.cpp
@@ -5,6 +5,7 @@
5#include "common/assert.h" 5#include "common/assert.h"
6#include "core/core.h" 6#include "core/core.h"
7#include "core/core_timing.h" 7#include "core/core_timing.h"
8#include "core/hle/kernel/k_scheduler.h"
8#include "core/hle/kernel/k_thread.h" 9#include "core/hle/kernel/k_thread.h"
9#include "core/hle/kernel/time_manager.h" 10#include "core/hle/kernel/time_manager.h"
10 11
@@ -15,7 +16,10 @@ TimeManager::TimeManager(Core::System& system_) : system{system_} {
15 Core::Timing::CreateEvent("Kernel::TimeManagerCallback", 16 Core::Timing::CreateEvent("Kernel::TimeManagerCallback",
16 [this](std::uintptr_t thread_handle, std::chrono::nanoseconds) { 17 [this](std::uintptr_t thread_handle, std::chrono::nanoseconds) {
17 KThread* thread = reinterpret_cast<KThread*>(thread_handle); 18 KThread* thread = reinterpret_cast<KThread*>(thread_handle);
18 thread->Wakeup(); 19 {
20 KScopedSchedulerLock sl(system.Kernel());
21 thread->OnTimer();
22 }
19 }); 23 });
20} 24}
21 25
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp
index aee8d4f93..e60661fe1 100644
--- a/src/core/hle/service/am/am.cpp
+++ b/src/core/hle/service/am/am.cpp
@@ -30,6 +30,7 @@
30#include "core/hle/service/apm/apm_controller.h" 30#include "core/hle/service/apm/apm_controller.h"
31#include "core/hle/service/apm/apm_interface.h" 31#include "core/hle/service/apm/apm_interface.h"
32#include "core/hle/service/bcat/backend/backend.h" 32#include "core/hle/service/bcat/backend/backend.h"
33#include "core/hle/service/caps/caps.h"
33#include "core/hle/service/filesystem/filesystem.h" 34#include "core/hle/service/filesystem/filesystem.h"
34#include "core/hle/service/ns/ns.h" 35#include "core/hle/service/ns/ns.h"
35#include "core/hle/service/nvflinger/nvflinger.h" 36#include "core/hle/service/nvflinger/nvflinger.h"
@@ -298,7 +299,7 @@ ISelfController::ISelfController(Core::System& system_, NVFlinger::NVFlinger& nv
298 {91, &ISelfController::GetAccumulatedSuspendedTickChangedEvent, "GetAccumulatedSuspendedTickChangedEvent"}, 299 {91, &ISelfController::GetAccumulatedSuspendedTickChangedEvent, "GetAccumulatedSuspendedTickChangedEvent"},
299 {100, &ISelfController::SetAlbumImageTakenNotificationEnabled, "SetAlbumImageTakenNotificationEnabled"}, 300 {100, &ISelfController::SetAlbumImageTakenNotificationEnabled, "SetAlbumImageTakenNotificationEnabled"},
300 {110, nullptr, "SetApplicationAlbumUserData"}, 301 {110, nullptr, "SetApplicationAlbumUserData"},
301 {120, nullptr, "SaveCurrentScreenshot"}, 302 {120, &ISelfController::SaveCurrentScreenshot, "SaveCurrentScreenshot"},
302 {130, nullptr, "SetRecordVolumeMuted"}, 303 {130, nullptr, "SetRecordVolumeMuted"},
303 {1000, nullptr, "GetDebugStorageChannel"}, 304 {1000, nullptr, "GetDebugStorageChannel"},
304 }; 305 };
@@ -579,6 +580,17 @@ void ISelfController::SetAlbumImageTakenNotificationEnabled(Kernel::HLERequestCo
579 rb.Push(ResultSuccess); 580 rb.Push(ResultSuccess);
580} 581}
581 582
583void ISelfController::SaveCurrentScreenshot(Kernel::HLERequestContext& ctx) {
584 IPC::RequestParser rp{ctx};
585
586 const auto album_report_option = rp.PopEnum<Capture::AlbumReportOption>();
587
588 LOG_WARNING(Service_AM, "(STUBBED) called. album_report_option={}", album_report_option);
589
590 IPC::ResponseBuilder rb{ctx, 2};
591 rb.Push(ResultSuccess);
592}
593
582AppletMessageQueue::AppletMessageQueue(Core::System& system) 594AppletMessageQueue::AppletMessageQueue(Core::System& system)
583 : service_context{system, "AppletMessageQueue"} { 595 : service_context{system, "AppletMessageQueue"} {
584 on_new_message = service_context.CreateEvent("AMMessageQueue:OnMessageReceived"); 596 on_new_message = service_context.CreateEvent("AMMessageQueue:OnMessageReceived");
diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h
index 202d20757..2a578aea5 100644
--- a/src/core/hle/service/am/am.h
+++ b/src/core/hle/service/am/am.h
@@ -151,6 +151,7 @@ private:
151 void GetAccumulatedSuspendedTickValue(Kernel::HLERequestContext& ctx); 151 void GetAccumulatedSuspendedTickValue(Kernel::HLERequestContext& ctx);
152 void GetAccumulatedSuspendedTickChangedEvent(Kernel::HLERequestContext& ctx); 152 void GetAccumulatedSuspendedTickChangedEvent(Kernel::HLERequestContext& ctx);
153 void SetAlbumImageTakenNotificationEnabled(Kernel::HLERequestContext& ctx); 153 void SetAlbumImageTakenNotificationEnabled(Kernel::HLERequestContext& ctx);
154 void SaveCurrentScreenshot(Kernel::HLERequestContext& ctx);
154 155
155 enum class ScreenshotPermission : u32 { 156 enum class ScreenshotPermission : u32 {
156 Inherit = 0, 157 Inherit = 0,
diff --git a/src/core/hle/service/am/applets/applet_controller.cpp b/src/core/hle/service/am/applets/applet_controller.cpp
index 2721679c1..d073f2210 100644
--- a/src/core/hle/service/am/applets/applet_controller.cpp
+++ b/src/core/hle/service/am/applets/applet_controller.cpp
@@ -10,6 +10,9 @@
10#include "common/string_util.h" 10#include "common/string_util.h"
11#include "core/core.h" 11#include "core/core.h"
12#include "core/frontend/applets/controller.h" 12#include "core/frontend/applets/controller.h"
13#include "core/hid/emulated_controller.h"
14#include "core/hid/hid_core.h"
15#include "core/hid/hid_types.h"
13#include "core/hle/result.h" 16#include "core/hle/result.h"
14#include "core/hle/service/am/am.h" 17#include "core/hle/service/am/am.h"
15#include "core/hle/service/am/applets/applet_controller.h" 18#include "core/hle/service/am/applets/applet_controller.h"
@@ -25,7 +28,7 @@ namespace Service::AM::Applets {
25static Core::Frontend::ControllerParameters ConvertToFrontendParameters( 28static Core::Frontend::ControllerParameters ConvertToFrontendParameters(
26 ControllerSupportArgPrivate private_arg, ControllerSupportArgHeader header, bool enable_text, 29 ControllerSupportArgPrivate private_arg, ControllerSupportArgHeader header, bool enable_text,
27 std::vector<IdentificationColor> identification_colors, std::vector<ExplainText> text) { 30 std::vector<IdentificationColor> identification_colors, std::vector<ExplainText> text) {
28 HID::Controller_NPad::NpadStyleSet npad_style_set; 31 Core::HID::NpadStyleTag npad_style_set;
29 npad_style_set.raw = private_arg.style_set; 32 npad_style_set.raw = private_arg.style_set;
30 33
31 return { 34 return {
@@ -243,19 +246,11 @@ void Controller::Execute() {
243void Controller::ConfigurationComplete() { 246void Controller::ConfigurationComplete() {
244 ControllerSupportResultInfo result_info{}; 247 ControllerSupportResultInfo result_info{};
245 248
246 const auto& players = Settings::values.players.GetValue();
247
248 // If enable_single_mode is enabled, player_count is 1 regardless of any other parameters. 249 // If enable_single_mode is enabled, player_count is 1 regardless of any other parameters.
249 // Otherwise, only count connected players from P1-P8. 250 // Otherwise, only count connected players from P1-P8.
250 result_info.player_count = 251 result_info.player_count = is_single_mode ? 1 : system.HIDCore().GetPlayerCount();
251 is_single_mode 252
252 ? 1 253 result_info.selected_id = static_cast<u32>(system.HIDCore().GetFirstNpadId());
253 : static_cast<s8>(std::count_if(players.begin(), players.end() - 2,
254 [](const auto& player) { return player.connected; }));
255
256 result_info.selected_id = HID::Controller_NPad::IndexToNPad(std::distance(
257 players.begin(), std::find_if(players.begin(), players.end(),
258 [](const auto& player) { return player.connected; })));
259 254
260 result_info.result = 0; 255 result_info.result = 0;
261 256
diff --git a/src/core/hle/service/am/applets/applet_controller.h b/src/core/hle/service/am/applets/applet_controller.h
index 0a34c4fc0..1a832505e 100644
--- a/src/core/hle/service/am/applets/applet_controller.h
+++ b/src/core/hle/service/am/applets/applet_controller.h
@@ -16,6 +16,10 @@ namespace Core {
16class System; 16class System;
17} 17}
18 18
19namespace Core::HID {
20enum class NpadStyleSet : u32;
21}
22
19namespace Service::AM::Applets { 23namespace Service::AM::Applets {
20 24
21using IdentificationColor = std::array<u8, 4>; 25using IdentificationColor = std::array<u8, 4>;
@@ -52,7 +56,7 @@ struct ControllerSupportArgPrivate {
52 bool flag_1{}; 56 bool flag_1{};
53 ControllerSupportMode mode{}; 57 ControllerSupportMode mode{};
54 ControllerSupportCaller caller{}; 58 ControllerSupportCaller caller{};
55 u32 style_set{}; 59 Core::HID::NpadStyleSet style_set{};
56 u32 joy_hold_type{}; 60 u32 joy_hold_type{};
57}; 61};
58static_assert(sizeof(ControllerSupportArgPrivate) == 0x14, 62static_assert(sizeof(ControllerSupportArgPrivate) == 0x14,
diff --git a/src/core/hle/service/am/applets/applets.cpp b/src/core/hle/service/am/applets/applets.cpp
index 7320b1c0f..134ac1ee2 100644
--- a/src/core/hle/service/am/applets/applets.cpp
+++ b/src/core/hle/service/am/applets/applets.cpp
@@ -231,7 +231,7 @@ void AppletManager::SetDefaultAppletFrontendSet() {
231void AppletManager::SetDefaultAppletsIfMissing() { 231void AppletManager::SetDefaultAppletsIfMissing() {
232 if (frontend.controller == nullptr) { 232 if (frontend.controller == nullptr) {
233 frontend.controller = 233 frontend.controller =
234 std::make_unique<Core::Frontend::DefaultControllerApplet>(system.ServiceManager()); 234 std::make_unique<Core::Frontend::DefaultControllerApplet>(system.HIDCore());
235 } 235 }
236 236
237 if (frontend.error == nullptr) { 237 if (frontend.error == nullptr) {
diff --git a/src/core/hle/service/audio/hwopus.cpp b/src/core/hle/service/audio/hwopus.cpp
index 7da1f2969..981b6c996 100644
--- a/src/core/hle/service/audio/hwopus.cpp
+++ b/src/core/hle/service/audio/hwopus.cpp
@@ -96,7 +96,7 @@ private:
96 96
97 bool DecodeOpusData(u32& consumed, u32& sample_count, const std::vector<u8>& input, 97 bool DecodeOpusData(u32& consumed, u32& sample_count, const std::vector<u8>& input,
98 std::vector<opus_int16>& output, u64* out_performance_time) const { 98 std::vector<opus_int16>& output, u64* out_performance_time) const {
99 const auto start_time = std::chrono::high_resolution_clock::now(); 99 const auto start_time = std::chrono::steady_clock::now();
100 const std::size_t raw_output_sz = output.size() * sizeof(opus_int16); 100 const std::size_t raw_output_sz = output.size() * sizeof(opus_int16);
101 if (sizeof(OpusPacketHeader) > input.size()) { 101 if (sizeof(OpusPacketHeader) > input.size()) {
102 LOG_ERROR(Audio, "Input is smaller than the header size, header_sz={}, input_sz={}", 102 LOG_ERROR(Audio, "Input is smaller than the header size, header_sz={}, input_sz={}",
@@ -135,7 +135,7 @@ private:
135 return false; 135 return false;
136 } 136 }
137 137
138 const auto end_time = std::chrono::high_resolution_clock::now() - start_time; 138 const auto end_time = std::chrono::steady_clock::now() - start_time;
139 sample_count = out_sample_count; 139 sample_count = out_sample_count;
140 consumed = static_cast<u32>(sizeof(OpusPacketHeader) + hdr.size); 140 consumed = static_cast<u32>(sizeof(OpusPacketHeader) + hdr.size);
141 if (out_performance_time != nullptr) { 141 if (out_performance_time != nullptr) {
diff --git a/src/core/hle/service/caps/caps.h b/src/core/hle/service/caps/caps.h
index b18adcb9d..7254055e6 100644
--- a/src/core/hle/service/caps/caps.h
+++ b/src/core/hle/service/caps/caps.h
@@ -24,7 +24,7 @@ enum class AlbumImageOrientation {
24 Orientation3 = 3, 24 Orientation3 = 3,
25}; 25};
26 26
27enum class AlbumReportOption { 27enum class AlbumReportOption : s32 {
28 Disable = 0, 28 Disable = 0,
29 Enable = 1, 29 Enable = 1,
30}; 30};
diff --git a/src/core/hle/service/friend/friend.cpp b/src/core/hle/service/friend/friend.cpp
index 68c9240ae..3c36f4085 100644
--- a/src/core/hle/service/friend/friend.cpp
+++ b/src/core/hle/service/friend/friend.cpp
@@ -17,10 +17,11 @@ namespace Service::Friend {
17 17
18class IFriendService final : public ServiceFramework<IFriendService> { 18class IFriendService final : public ServiceFramework<IFriendService> {
19public: 19public:
20 explicit IFriendService(Core::System& system_) : ServiceFramework{system_, "IFriendService"} { 20 explicit IFriendService(Core::System& system_)
21 : ServiceFramework{system_, "IFriendService"}, service_context{system, "IFriendService"} {
21 // clang-format off 22 // clang-format off
22 static const FunctionInfo functions[] = { 23 static const FunctionInfo functions[] = {
23 {0, nullptr, "GetCompletionEvent"}, 24 {0, &IFriendService::GetCompletionEvent, "GetCompletionEvent"},
24 {1, nullptr, "Cancel"}, 25 {1, nullptr, "Cancel"},
25 {10100, nullptr, "GetFriendListIds"}, 26 {10100, nullptr, "GetFriendListIds"},
26 {10101, &IFriendService::GetFriendList, "GetFriendList"}, 27 {10101, &IFriendService::GetFriendList, "GetFriendList"},
@@ -109,6 +110,12 @@ public:
109 // clang-format on 110 // clang-format on
110 111
111 RegisterHandlers(functions); 112 RegisterHandlers(functions);
113
114 completion_event = service_context.CreateEvent("IFriendService:CompletionEvent");
115 }
116
117 ~IFriendService() override {
118 service_context.CloseEvent(completion_event);
112 } 119 }
113 120
114private: 121private:
@@ -129,6 +136,14 @@ private:
129 }; 136 };
130 static_assert(sizeof(SizedFriendFilter) == 0x10, "SizedFriendFilter is an invalid size"); 137 static_assert(sizeof(SizedFriendFilter) == 0x10, "SizedFriendFilter is an invalid size");
131 138
139 void GetCompletionEvent(Kernel::HLERequestContext& ctx) {
140 LOG_DEBUG(Service_Friend, "called");
141
142 IPC::ResponseBuilder rb{ctx, 2, 1};
143 rb.Push(ResultSuccess);
144 rb.PushCopyObjects(completion_event->GetReadableEvent());
145 }
146
132 void GetBlockedUserListIds(Kernel::HLERequestContext& ctx) { 147 void GetBlockedUserListIds(Kernel::HLERequestContext& ctx) {
133 // This is safe to stub, as there should be no adverse consequences from reporting no 148 // This is safe to stub, as there should be no adverse consequences from reporting no
134 // blocked users. 149 // blocked users.
@@ -179,6 +194,10 @@ private:
179 rb.Push<u32>(0); // Friend count 194 rb.Push<u32>(0); // Friend count
180 // TODO(ogniK): Return a buffer of u64s which are the "NetworkServiceAccountId" 195 // TODO(ogniK): Return a buffer of u64s which are the "NetworkServiceAccountId"
181 } 196 }
197
198 KernelHelpers::ServiceContext service_context;
199
200 Kernel::KEvent* completion_event;
182}; 201};
183 202
184class INotificationService final : public ServiceFramework<INotificationService> { 203class INotificationService final : public ServiceFramework<INotificationService> {
diff --git a/src/core/hle/service/glue/glue.cpp b/src/core/hle/service/glue/glue.cpp
index a08dc9758..b24d469cf 100644
--- a/src/core/hle/service/glue/glue.cpp
+++ b/src/core/hle/service/glue/glue.cpp
@@ -8,6 +8,7 @@
8#include "core/hle/service/glue/bgtc.h" 8#include "core/hle/service/glue/bgtc.h"
9#include "core/hle/service/glue/ectx.h" 9#include "core/hle/service/glue/ectx.h"
10#include "core/hle/service/glue/glue.h" 10#include "core/hle/service/glue/glue.h"
11#include "core/hle/service/glue/notif.h"
11 12
12namespace Service::Glue { 13namespace Service::Glue {
13 14
@@ -24,6 +25,9 @@ void InstallInterfaces(Core::System& system) {
24 25
25 // Error Context 26 // Error Context
26 std::make_shared<ECTX_AW>(system)->InstallAsService(system.ServiceManager()); 27 std::make_shared<ECTX_AW>(system)->InstallAsService(system.ServiceManager());
28
29 // Notification Services for application
30 std::make_shared<NOTIF_A>(system)->InstallAsService(system.ServiceManager());
27} 31}
28 32
29} // namespace Service::Glue 33} // namespace Service::Glue
diff --git a/src/core/hle/service/glue/notif.cpp b/src/core/hle/service/glue/notif.cpp
new file mode 100644
index 000000000..c559ec9df
--- /dev/null
+++ b/src/core/hle/service/glue/notif.cpp
@@ -0,0 +1,44 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/hle/ipc_helpers.h"
6#include "core/hle/service/glue/notif.h"
7
8namespace Service::Glue {
9
10NOTIF_A::NOTIF_A(Core::System& system_) : ServiceFramework{system_, "notif:a"} {
11 // clang-format off
12 static const FunctionInfo functions[] = {
13 {500, nullptr, "RegisterAlarmSetting"},
14 {510, nullptr, "UpdateAlarmSetting"},
15 {520, &NOTIF_A::ListAlarmSettings, "ListAlarmSettings"},
16 {530, nullptr, "LoadApplicationParameter"},
17 {540, nullptr, "DeleteAlarmSetting"},
18 {1000, &NOTIF_A::Initialize, "Initialize"},
19 };
20 // clang-format on
21
22 RegisterHandlers(functions);
23}
24
25NOTIF_A::~NOTIF_A() = default;
26
27void NOTIF_A::ListAlarmSettings(Kernel::HLERequestContext& ctx) {
28 // Returns an array of AlarmSetting
29 constexpr s32 alarm_count = 0;
30
31 LOG_WARNING(Service_NOTIF, "(STUBBED) called");
32
33 IPC::ResponseBuilder rb{ctx, 3};
34 rb.Push(ResultSuccess);
35 rb.Push(alarm_count);
36}
37
38void NOTIF_A::Initialize(Kernel::HLERequestContext& ctx) {
39 LOG_WARNING(Service_NOTIF, "(STUBBED) called");
40 IPC::ResponseBuilder rb{ctx, 2};
41 rb.Push(ResultSuccess);
42}
43
44} // namespace Service::Glue
diff --git a/src/core/hle/service/glue/notif.h b/src/core/hle/service/glue/notif.h
new file mode 100644
index 000000000..6ecf2015c
--- /dev/null
+++ b/src/core/hle/service/glue/notif.h
@@ -0,0 +1,25 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "core/hle/service/service.h"
8
9namespace Core {
10class System;
11}
12
13namespace Service::Glue {
14
15class NOTIF_A final : public ServiceFramework<NOTIF_A> {
16public:
17 explicit NOTIF_A(Core::System& system_);
18 ~NOTIF_A() override;
19
20private:
21 void ListAlarmSettings(Kernel::HLERequestContext& ctx);
22 void Initialize(Kernel::HLERequestContext& ctx);
23};
24
25} // namespace Service::Glue
diff --git a/src/core/hle/service/hid/controllers/console_sixaxis.cpp b/src/core/hle/service/hid/controllers/console_sixaxis.cpp
index bda6e2557..f0f3105dc 100644
--- a/src/core/hle/service/hid/controllers/console_sixaxis.cpp
+++ b/src/core/hle/service/hid/controllers/console_sixaxis.cpp
@@ -4,13 +4,18 @@
4 4
5#include "common/settings.h" 5#include "common/settings.h"
6#include "core/core_timing.h" 6#include "core/core_timing.h"
7#include "core/hid/emulated_console.h"
8#include "core/hid/hid_core.h"
7#include "core/hle/service/hid/controllers/console_sixaxis.h" 9#include "core/hle/service/hid/controllers/console_sixaxis.h"
8 10
9namespace Service::HID { 11namespace Service::HID {
10constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3C200; 12constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3C200;
11 13
12Controller_ConsoleSixAxis::Controller_ConsoleSixAxis(Core::System& system_) 14Controller_ConsoleSixAxis::Controller_ConsoleSixAxis(Core::HID::HIDCore& hid_core_)
13 : ControllerBase{system_} {} 15 : ControllerBase{hid_core_} {
16 console = hid_core.GetEmulatedConsole();
17}
18
14Controller_ConsoleSixAxis::~Controller_ConsoleSixAxis() = default; 19Controller_ConsoleSixAxis::~Controller_ConsoleSixAxis() = default;
15 20
16void Controller_ConsoleSixAxis::OnInit() {} 21void Controller_ConsoleSixAxis::OnInit() {}
@@ -19,44 +24,31 @@ void Controller_ConsoleSixAxis::OnRelease() {}
19 24
20void Controller_ConsoleSixAxis::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, 25void Controller_ConsoleSixAxis::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data,
21 std::size_t size) { 26 std::size_t size) {
22 seven_six_axis.header.timestamp = core_timing.GetCPUTicks();
23 seven_six_axis.header.total_entry_count = 17;
24
25 if (!IsControllerActivated() || !is_transfer_memory_set) { 27 if (!IsControllerActivated() || !is_transfer_memory_set) {
26 seven_six_axis.header.entry_count = 0; 28 seven_sixaxis_lifo.buffer_count = 0;
27 seven_six_axis.header.last_entry_index = 0; 29 seven_sixaxis_lifo.buffer_tail = 0;
28 return; 30 return;
29 } 31 }
30 seven_six_axis.header.entry_count = 16;
31
32 const auto& last_entry =
33 seven_six_axis.sevensixaxis_states[seven_six_axis.header.last_entry_index];
34 seven_six_axis.header.last_entry_index = (seven_six_axis.header.last_entry_index + 1) % 17;
35 auto& cur_entry = seven_six_axis.sevensixaxis_states[seven_six_axis.header.last_entry_index];
36 32
37 cur_entry.sampling_number = last_entry.sampling_number + 1; 33 const auto& last_entry = seven_sixaxis_lifo.ReadCurrentEntry().state;
38 cur_entry.sampling_number2 = cur_entry.sampling_number; 34 next_seven_sixaxis_state.sampling_number = last_entry.sampling_number + 1;
39 35
40 // Try to read sixaxis sensor states 36 // Try to read sixaxis sensor states
41 MotionDevice motion_device{}; 37 const auto motion_status = console->GetMotion();
42 const auto& device = motions[0]; 38
43 if (device) { 39 console_six_axis.is_seven_six_axis_sensor_at_rest = motion_status.is_at_rest;
44 std::tie(motion_device.accel, motion_device.gyro, motion_device.rotation,
45 motion_device.orientation, motion_device.quaternion) = device->GetStatus();
46 console_six_axis.is_seven_six_axis_sensor_at_rest = motion_device.gyro.Length2() < 0.0001f;
47 }
48 40
49 cur_entry.accel = motion_device.accel; 41 next_seven_sixaxis_state.accel = motion_status.accel;
50 // Zero gyro values as they just mess up with the camera 42 // Zero gyro values as they just mess up with the camera
51 // Note: Probably a correct sensivity setting must be set 43 // Note: Probably a correct sensivity setting must be set
52 cur_entry.gyro = {}; 44 next_seven_sixaxis_state.gyro = {};
53 cur_entry.quaternion = { 45 next_seven_sixaxis_state.quaternion = {
54 { 46 {
55 motion_device.quaternion.xyz.y, 47 motion_status.quaternion.xyz.y,
56 motion_device.quaternion.xyz.x, 48 motion_status.quaternion.xyz.x,
57 -motion_device.quaternion.w, 49 -motion_status.quaternion.w,
58 }, 50 },
59 -motion_device.quaternion.xyz.z, 51 -motion_status.quaternion.xyz.z,
60 }; 52 };
61 53
62 console_six_axis.sampling_number++; 54 console_six_axis.sampling_number++;
@@ -67,14 +59,8 @@ void Controller_ConsoleSixAxis::OnUpdate(const Core::Timing::CoreTiming& core_ti
67 // Update console six axis shared memory 59 // Update console six axis shared memory
68 std::memcpy(data + SHARED_MEMORY_OFFSET, &console_six_axis, sizeof(console_six_axis)); 60 std::memcpy(data + SHARED_MEMORY_OFFSET, &console_six_axis, sizeof(console_six_axis));
69 // Update seven six axis transfer memory 61 // Update seven six axis transfer memory
70 std::memcpy(transfer_memory, &seven_six_axis, sizeof(seven_six_axis)); 62 seven_sixaxis_lifo.WriteNextEntry(next_seven_sixaxis_state);
71} 63 std::memcpy(transfer_memory, &seven_sixaxis_lifo, sizeof(seven_sixaxis_lifo));
72
73void Controller_ConsoleSixAxis::OnLoadInputDevices() {
74 const auto player = Settings::values.players.GetValue()[0];
75 std::transform(player.motions.begin() + Settings::NativeMotion::MOTION_HID_BEGIN,
76 player.motions.begin() + Settings::NativeMotion::MOTION_HID_END, motions.begin(),
77 Input::CreateDevice<Input::MotionDevice>);
78} 64}
79 65
80void Controller_ConsoleSixAxis::SetTransferMemoryPointer(u8* t_mem) { 66void Controller_ConsoleSixAxis::SetTransferMemoryPointer(u8* t_mem) {
@@ -83,8 +69,7 @@ void Controller_ConsoleSixAxis::SetTransferMemoryPointer(u8* t_mem) {
83} 69}
84 70
85void Controller_ConsoleSixAxis::ResetTimestamp() { 71void Controller_ConsoleSixAxis::ResetTimestamp() {
86 auto& cur_entry = seven_six_axis.sevensixaxis_states[seven_six_axis.header.last_entry_index]; 72 seven_sixaxis_lifo.buffer_count = 0;
87 cur_entry.sampling_number = 0; 73 seven_sixaxis_lifo.buffer_tail = 0;
88 cur_entry.sampling_number2 = 0;
89} 74}
90} // namespace Service::HID 75} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/console_sixaxis.h b/src/core/hle/service/hid/controllers/console_sixaxis.h
index fd8a427af..279241858 100644
--- a/src/core/hle/service/hid/controllers/console_sixaxis.h
+++ b/src/core/hle/service/hid/controllers/console_sixaxis.h
@@ -5,16 +5,21 @@
5#pragma once 5#pragma once
6 6
7#include <array> 7#include <array>
8#include "common/bit_field.h" 8
9#include "common/common_types.h" 9#include "common/common_types.h"
10#include "common/quaternion.h" 10#include "common/quaternion.h"
11#include "core/frontend/input.h" 11#include "core/hid/hid_types.h"
12#include "core/hle/service/hid/controllers/controller_base.h" 12#include "core/hle/service/hid/controllers/controller_base.h"
13#include "core/hle/service/hid/ring_lifo.h"
14
15namespace Core::HID {
16class EmulatedConsole;
17} // namespace Core::HID
13 18
14namespace Service::HID { 19namespace Service::HID {
15class Controller_ConsoleSixAxis final : public ControllerBase { 20class Controller_ConsoleSixAxis final : public ControllerBase {
16public: 21public:
17 explicit Controller_ConsoleSixAxis(Core::System& system_); 22 explicit Controller_ConsoleSixAxis(Core::HID::HIDCore& hid_core_);
18 ~Controller_ConsoleSixAxis() override; 23 ~Controller_ConsoleSixAxis() override;
19 24
20 // Called when the controller is initialized 25 // Called when the controller is initialized
@@ -26,9 +31,6 @@ public:
26 // When the controller is requesting an update for the shared memory 31 // When the controller is requesting an update for the shared memory
27 void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, size_t size) override; 32 void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, size_t size) override;
28 33
29 // Called when input devices should be loaded
30 void OnLoadInputDevices() override;
31
32 // Called on InitializeSevenSixAxisSensor 34 // Called on InitializeSevenSixAxisSensor
33 void SetTransferMemoryPointer(u8* t_mem); 35 void SetTransferMemoryPointer(u8* t_mem);
34 36
@@ -38,43 +40,31 @@ public:
38private: 40private:
39 struct SevenSixAxisState { 41 struct SevenSixAxisState {
40 INSERT_PADDING_WORDS(4); // unused 42 INSERT_PADDING_WORDS(4); // unused
41 s64_le sampling_number{}; 43 s64 sampling_number{};
42 s64_le sampling_number2{};
43 u64 unknown{}; 44 u64 unknown{};
44 Common::Vec3f accel{}; 45 Common::Vec3f accel{};
45 Common::Vec3f gyro{}; 46 Common::Vec3f gyro{};
46 Common::Quaternion<f32> quaternion{}; 47 Common::Quaternion<f32> quaternion{};
47 }; 48 };
48 static_assert(sizeof(SevenSixAxisState) == 0x50, "SevenSixAxisState is an invalid size"); 49 static_assert(sizeof(SevenSixAxisState) == 0x48, "SevenSixAxisState is an invalid size");
49
50 struct SevenSixAxisMemory {
51 CommonHeader header{};
52 std::array<SevenSixAxisState, 0x21> sevensixaxis_states{};
53 };
54 static_assert(sizeof(SevenSixAxisMemory) == 0xA70, "SevenSixAxisMemory is an invalid size");
55 50
51 // This is nn::hid::detail::ConsoleSixAxisSensorSharedMemoryFormat
56 struct ConsoleSharedMemory { 52 struct ConsoleSharedMemory {
57 u64_le sampling_number{}; 53 u64 sampling_number{};
58 bool is_seven_six_axis_sensor_at_rest{}; 54 bool is_seven_six_axis_sensor_at_rest{};
55 INSERT_PADDING_BYTES(4); // padding
59 f32 verticalization_error{}; 56 f32 verticalization_error{};
60 Common::Vec3f gyro_bias{}; 57 Common::Vec3f gyro_bias{};
61 }; 58 };
62 static_assert(sizeof(ConsoleSharedMemory) == 0x20, "ConsoleSharedMemory is an invalid size"); 59 static_assert(sizeof(ConsoleSharedMemory) == 0x20, "ConsoleSharedMemory is an invalid size");
63 60
64 struct MotionDevice { 61 Lifo<SevenSixAxisState, 0x21> seven_sixaxis_lifo{};
65 Common::Vec3f accel; 62 static_assert(sizeof(seven_sixaxis_lifo) == 0xA70, "SevenSixAxisState is an invalid size");
66 Common::Vec3f gyro;
67 Common::Vec3f rotation;
68 std::array<Common::Vec3f, 3> orientation;
69 Common::Quaternion<f32> quaternion;
70 };
71 63
72 using MotionArray = 64 Core::HID::EmulatedConsole* console;
73 std::array<std::unique_ptr<Input::MotionDevice>, Settings::NativeMotion::NUM_MOTIONS_HID>;
74 MotionArray motions;
75 u8* transfer_memory = nullptr; 65 u8* transfer_memory = nullptr;
76 bool is_transfer_memory_set = false; 66 bool is_transfer_memory_set = false;
77 ConsoleSharedMemory console_six_axis{}; 67 ConsoleSharedMemory console_six_axis{};
78 SevenSixAxisMemory seven_six_axis{}; 68 SevenSixAxisState next_seven_sixaxis_state{};
79}; 69};
80} // namespace Service::HID 70} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/controller_base.cpp b/src/core/hle/service/hid/controllers/controller_base.cpp
index 9d1e6db6a..788ae9ae7 100644
--- a/src/core/hle/service/hid/controllers/controller_base.cpp
+++ b/src/core/hle/service/hid/controllers/controller_base.cpp
@@ -6,12 +6,12 @@
6 6
7namespace Service::HID { 7namespace Service::HID {
8 8
9ControllerBase::ControllerBase(Core::System& system_) : system(system_) {} 9ControllerBase::ControllerBase(Core::HID::HIDCore& hid_core_) : hid_core(hid_core_) {}
10ControllerBase::~ControllerBase() = default; 10ControllerBase::~ControllerBase() = default;
11 11
12void ControllerBase::ActivateController() { 12void ControllerBase::ActivateController() {
13 if (is_activated) { 13 if (is_activated) {
14 OnRelease(); 14 return;
15 } 15 }
16 is_activated = true; 16 is_activated = true;
17 OnInit(); 17 OnInit();
diff --git a/src/core/hle/service/hid/controllers/controller_base.h b/src/core/hle/service/hid/controllers/controller_base.h
index 1556fb08e..7450eb20a 100644
--- a/src/core/hle/service/hid/controllers/controller_base.h
+++ b/src/core/hle/service/hid/controllers/controller_base.h
@@ -11,14 +11,14 @@ namespace Core::Timing {
11class CoreTiming; 11class CoreTiming;
12} 12}
13 13
14namespace Core { 14namespace Core::HID {
15class System; 15class HIDCore;
16} 16}
17 17
18namespace Service::HID { 18namespace Service::HID {
19class ControllerBase { 19class ControllerBase {
20public: 20public:
21 explicit ControllerBase(Core::System& system_); 21 explicit ControllerBase(Core::HID::HIDCore& hid_core_);
22 virtual ~ControllerBase(); 22 virtual ~ControllerBase();
23 23
24 // Called when the controller is initialized 24 // Called when the controller is initialized
@@ -35,26 +35,17 @@ public:
35 virtual void OnMotionUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, 35 virtual void OnMotionUpdate(const Core::Timing::CoreTiming& core_timing, u8* data,
36 std::size_t size) {} 36 std::size_t size) {}
37 37
38 // Called when input devices should be loaded
39 virtual void OnLoadInputDevices() = 0;
40
41 void ActivateController(); 38 void ActivateController();
42 39
43 void DeactivateController(); 40 void DeactivateController();
44 41
45 bool IsControllerActivated() const; 42 bool IsControllerActivated() const;
46 43
44 static const std::size_t hid_entry_count = 17;
45
47protected: 46protected:
48 bool is_activated{false}; 47 bool is_activated{false};
49 48
50 struct CommonHeader { 49 Core::HID::HIDCore& hid_core;
51 s64_le timestamp;
52 s64_le total_entry_count;
53 s64_le last_entry_index;
54 s64_le entry_count;
55 };
56 static_assert(sizeof(CommonHeader) == 0x20, "CommonHeader is an invalid size");
57
58 Core::System& system;
59}; 50};
60} // namespace Service::HID 51} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/debug_pad.cpp b/src/core/hle/service/hid/controllers/debug_pad.cpp
index d439b8fb0..6a6fb9cab 100644
--- a/src/core/hle/service/hid/controllers/debug_pad.cpp
+++ b/src/core/hle/service/hid/controllers/debug_pad.cpp
@@ -6,15 +6,19 @@
6#include "common/common_types.h" 6#include "common/common_types.h"
7#include "common/settings.h" 7#include "common/settings.h"
8#include "core/core_timing.h" 8#include "core/core_timing.h"
9#include "core/hid/emulated_controller.h"
10#include "core/hid/hid_core.h"
11#include "core/hid/hid_types.h"
9#include "core/hle/service/hid/controllers/debug_pad.h" 12#include "core/hle/service/hid/controllers/debug_pad.h"
10 13
11namespace Service::HID { 14namespace Service::HID {
15constexpr std::size_t SHARED_MEMORY_OFFSET = 0x00000;
12 16
13constexpr s32 HID_JOYSTICK_MAX = 0x7fff; 17Controller_DebugPad::Controller_DebugPad(Core::HID::HIDCore& hid_core_)
14[[maybe_unused]] constexpr s32 HID_JOYSTICK_MIN = -0x7fff; 18 : ControllerBase{hid_core_} {
15enum class JoystickId : std::size_t { Joystick_Left, Joystick_Right }; 19 controller = hid_core.GetEmulatedController(Core::HID::NpadIdType::Other);
20}
16 21
17Controller_DebugPad::Controller_DebugPad(Core::System& system_) : ControllerBase{system_} {}
18Controller_DebugPad::~Controller_DebugPad() = default; 22Controller_DebugPad::~Controller_DebugPad() = default;
19 23
20void Controller_DebugPad::OnInit() {} 24void Controller_DebugPad::OnInit() {}
@@ -23,63 +27,29 @@ void Controller_DebugPad::OnRelease() {}
23 27
24void Controller_DebugPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, 28void Controller_DebugPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data,
25 std::size_t size) { 29 std::size_t size) {
26 shared_memory.header.timestamp = core_timing.GetCPUTicks();
27 shared_memory.header.total_entry_count = 17;
28
29 if (!IsControllerActivated()) { 30 if (!IsControllerActivated()) {
30 shared_memory.header.entry_count = 0; 31 debug_pad_lifo.buffer_count = 0;
31 shared_memory.header.last_entry_index = 0; 32 debug_pad_lifo.buffer_tail = 0;
33 std::memcpy(data + SHARED_MEMORY_OFFSET, &debug_pad_lifo, sizeof(debug_pad_lifo));
32 return; 34 return;
33 } 35 }
34 shared_memory.header.entry_count = 16;
35 36
36 const auto& last_entry = shared_memory.pad_states[shared_memory.header.last_entry_index]; 37 const auto& last_entry = debug_pad_lifo.ReadCurrentEntry().state;
37 shared_memory.header.last_entry_index = (shared_memory.header.last_entry_index + 1) % 17; 38 next_state.sampling_number = last_entry.sampling_number + 1;
38 auto& cur_entry = shared_memory.pad_states[shared_memory.header.last_entry_index];
39
40 cur_entry.sampling_number = last_entry.sampling_number + 1;
41 cur_entry.sampling_number2 = cur_entry.sampling_number;
42 39
43 if (Settings::values.debug_pad_enabled) { 40 if (Settings::values.debug_pad_enabled) {
44 cur_entry.attribute.connected.Assign(1); 41 next_state.attribute.connected.Assign(1);
45 auto& pad = cur_entry.pad_state;
46 42
47 using namespace Settings::NativeButton; 43 const auto& button_state = controller->GetDebugPadButtons();
48 pad.a.Assign(buttons[A - BUTTON_HID_BEGIN]->GetStatus()); 44 const auto& stick_state = controller->GetSticks();
49 pad.b.Assign(buttons[B - BUTTON_HID_BEGIN]->GetStatus());
50 pad.x.Assign(buttons[X - BUTTON_HID_BEGIN]->GetStatus());
51 pad.y.Assign(buttons[Y - BUTTON_HID_BEGIN]->GetStatus());
52 pad.l.Assign(buttons[L - BUTTON_HID_BEGIN]->GetStatus());
53 pad.r.Assign(buttons[R - BUTTON_HID_BEGIN]->GetStatus());
54 pad.zl.Assign(buttons[ZL - BUTTON_HID_BEGIN]->GetStatus());
55 pad.zr.Assign(buttons[ZR - BUTTON_HID_BEGIN]->GetStatus());
56 pad.plus.Assign(buttons[Plus - BUTTON_HID_BEGIN]->GetStatus());
57 pad.minus.Assign(buttons[Minus - BUTTON_HID_BEGIN]->GetStatus());
58 pad.d_left.Assign(buttons[DLeft - BUTTON_HID_BEGIN]->GetStatus());
59 pad.d_up.Assign(buttons[DUp - BUTTON_HID_BEGIN]->GetStatus());
60 pad.d_right.Assign(buttons[DRight - BUTTON_HID_BEGIN]->GetStatus());
61 pad.d_down.Assign(buttons[DDown - BUTTON_HID_BEGIN]->GetStatus());
62 45
63 const auto [stick_l_x_f, stick_l_y_f] = 46 next_state.pad_state = button_state;
64 analogs[static_cast<std::size_t>(JoystickId::Joystick_Left)]->GetStatus(); 47 next_state.l_stick = stick_state.left;
65 const auto [stick_r_x_f, stick_r_y_f] = 48 next_state.r_stick = stick_state.right;
66 analogs[static_cast<std::size_t>(JoystickId::Joystick_Right)]->GetStatus();
67 cur_entry.l_stick.x = static_cast<s32>(stick_l_x_f * HID_JOYSTICK_MAX);
68 cur_entry.l_stick.y = static_cast<s32>(stick_l_y_f * HID_JOYSTICK_MAX);
69 cur_entry.r_stick.x = static_cast<s32>(stick_r_x_f * HID_JOYSTICK_MAX);
70 cur_entry.r_stick.y = static_cast<s32>(stick_r_y_f * HID_JOYSTICK_MAX);
71 } 49 }
72 50
73 std::memcpy(data, &shared_memory, sizeof(SharedMemory)); 51 debug_pad_lifo.WriteNextEntry(next_state);
52 std::memcpy(data + SHARED_MEMORY_OFFSET, &debug_pad_lifo, sizeof(debug_pad_lifo));
74} 53}
75 54
76void Controller_DebugPad::OnLoadInputDevices() {
77 std::transform(Settings::values.debug_pad_buttons.begin(),
78 Settings::values.debug_pad_buttons.begin() +
79 Settings::NativeButton::NUM_BUTTONS_HID,
80 buttons.begin(), Input::CreateDevice<Input::ButtonDevice>);
81 std::transform(Settings::values.debug_pad_analogs.begin(),
82 Settings::values.debug_pad_analogs.end(), analogs.begin(),
83 Input::CreateDevice<Input::AnalogDevice>);
84}
85} // namespace Service::HID 55} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/debug_pad.h b/src/core/hle/service/hid/controllers/debug_pad.h
index 1b1645184..afe374fc2 100644
--- a/src/core/hle/service/hid/controllers/debug_pad.h
+++ b/src/core/hle/service/hid/controllers/debug_pad.h
@@ -8,15 +8,20 @@
8#include "common/bit_field.h" 8#include "common/bit_field.h"
9#include "common/common_funcs.h" 9#include "common/common_funcs.h"
10#include "common/common_types.h" 10#include "common/common_types.h"
11#include "common/settings.h"
12#include "common/swap.h" 11#include "common/swap.h"
13#include "core/frontend/input.h"
14#include "core/hle/service/hid/controllers/controller_base.h" 12#include "core/hle/service/hid/controllers/controller_base.h"
13#include "core/hle/service/hid/ring_lifo.h"
14
15namespace Core::HID {
16class EmulatedController;
17struct DebugPadButton;
18struct AnalogStickState;
19} // namespace Core::HID
15 20
16namespace Service::HID { 21namespace Service::HID {
17class Controller_DebugPad final : public ControllerBase { 22class Controller_DebugPad final : public ControllerBase {
18public: 23public:
19 explicit Controller_DebugPad(Core::System& system_); 24 explicit Controller_DebugPad(Core::HID::HIDCore& hid_core_);
20 ~Controller_DebugPad() override; 25 ~Controller_DebugPad() override;
21 26
22 // Called when the controller is initialized 27 // Called when the controller is initialized
@@ -28,66 +33,31 @@ public:
28 // When the controller is requesting an update for the shared memory 33 // When the controller is requesting an update for the shared memory
29 void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t size) override; 34 void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t size) override;
30 35
31 // Called when input devices should be loaded
32 void OnLoadInputDevices() override;
33
34private: 36private:
35 struct AnalogStick { 37 // This is nn::hid::DebugPadAttribute
36 s32_le x; 38 struct DebugPadAttribute {
37 s32_le y;
38 };
39 static_assert(sizeof(AnalogStick) == 0x8);
40
41 struct PadState {
42 union {
43 u32_le raw{};
44 BitField<0, 1, u32> a;
45 BitField<1, 1, u32> b;
46 BitField<2, 1, u32> x;
47 BitField<3, 1, u32> y;
48 BitField<4, 1, u32> l;
49 BitField<5, 1, u32> r;
50 BitField<6, 1, u32> zl;
51 BitField<7, 1, u32> zr;
52 BitField<8, 1, u32> plus;
53 BitField<9, 1, u32> minus;
54 BitField<10, 1, u32> d_left;
55 BitField<11, 1, u32> d_up;
56 BitField<12, 1, u32> d_right;
57 BitField<13, 1, u32> d_down;
58 };
59 };
60 static_assert(sizeof(PadState) == 0x4, "PadState is an invalid size");
61
62 struct Attributes {
63 union { 39 union {
64 u32_le raw{}; 40 u32 raw{};
65 BitField<0, 1, u32> connected; 41 BitField<0, 1, u32> connected;
66 }; 42 };
67 }; 43 };
68 static_assert(sizeof(Attributes) == 0x4, "Attributes is an invalid size"); 44 static_assert(sizeof(DebugPadAttribute) == 0x4, "DebugPadAttribute is an invalid size");
69 45
70 struct PadStates { 46 // This is nn::hid::DebugPadState
71 s64_le sampling_number; 47 struct DebugPadState {
72 s64_le sampling_number2; 48 s64 sampling_number;
73 Attributes attribute; 49 DebugPadAttribute attribute;
74 PadState pad_state; 50 Core::HID::DebugPadButton pad_state;
75 AnalogStick r_stick; 51 Core::HID::AnalogStickState r_stick;
76 AnalogStick l_stick; 52 Core::HID::AnalogStickState l_stick;
77 }; 53 };
78 static_assert(sizeof(PadStates) == 0x28, "PadStates is an invalid state"); 54 static_assert(sizeof(DebugPadState) == 0x20, "DebugPadState is an invalid state");
79 55
80 struct SharedMemory { 56 // This is nn::hid::detail::DebugPadLifo
81 CommonHeader header; 57 Lifo<DebugPadState, hid_entry_count> debug_pad_lifo{};
82 std::array<PadStates, 17> pad_states; 58 static_assert(sizeof(debug_pad_lifo) == 0x2C8, "debug_pad_lifo is an invalid size");
83 INSERT_PADDING_BYTES(0x138); 59 DebugPadState next_state{};
84 };
85 static_assert(sizeof(SharedMemory) == 0x400, "SharedMemory is an invalid size");
86 SharedMemory shared_memory{};
87 60
88 std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeButton::NUM_BUTTONS_HID> 61 Core::HID::EmulatedController* controller;
89 buttons;
90 std::array<std::unique_ptr<Input::AnalogDevice>, Settings::NativeAnalog::NUM_STICKS_HID>
91 analogs;
92}; 62};
93} // namespace Service::HID 63} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/gesture.cpp b/src/core/hle/service/hid/controllers/gesture.cpp
index 764abb5b6..fe895c4f6 100644
--- a/src/core/hle/service/hid/controllers/gesture.cpp
+++ b/src/core/hle/service/hid/controllers/gesture.cpp
@@ -7,6 +7,7 @@
7#include "common/settings.h" 7#include "common/settings.h"
8#include "core/core_timing.h" 8#include "core/core_timing.h"
9#include "core/frontend/emu_window.h" 9#include "core/frontend/emu_window.h"
10#include "core/hid/hid_core.h"
10#include "core/hle/service/hid/controllers/gesture.h" 11#include "core/hle/service/hid/controllers/gesture.h"
11 12
12namespace Service::HID { 13namespace Service::HID {
@@ -23,16 +24,14 @@ constexpr f32 Square(s32 num) {
23 return static_cast<f32>(num * num); 24 return static_cast<f32>(num * num);
24} 25}
25 26
26Controller_Gesture::Controller_Gesture(Core::System& system_) : ControllerBase(system_) {} 27Controller_Gesture::Controller_Gesture(Core::HID::HIDCore& hid_core_) : ControllerBase(hid_core_) {
28 console = hid_core.GetEmulatedConsole();
29}
27Controller_Gesture::~Controller_Gesture() = default; 30Controller_Gesture::~Controller_Gesture() = default;
28 31
29void Controller_Gesture::OnInit() { 32void Controller_Gesture::OnInit() {
30 for (std::size_t id = 0; id < MAX_FINGERS; ++id) { 33 gesture_lifo.buffer_count = 0;
31 mouse_finger_id[id] = MAX_POINTS; 34 gesture_lifo.buffer_tail = 0;
32 keyboard_finger_id[id] = MAX_POINTS;
33 udp_finger_id[id] = MAX_POINTS;
34 }
35 shared_memory.header.entry_count = 0;
36 force_update = true; 35 force_update = true;
37} 36}
38 37
@@ -40,50 +39,38 @@ void Controller_Gesture::OnRelease() {}
40 39
41void Controller_Gesture::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, 40void Controller_Gesture::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data,
42 std::size_t size) { 41 std::size_t size) {
43 shared_memory.header.timestamp = core_timing.GetCPUTicks();
44 shared_memory.header.total_entry_count = 17;
45
46 if (!IsControllerActivated()) { 42 if (!IsControllerActivated()) {
47 shared_memory.header.entry_count = 0; 43 gesture_lifo.buffer_count = 0;
48 shared_memory.header.last_entry_index = 0; 44 gesture_lifo.buffer_tail = 0;
45 std::memcpy(data + SHARED_MEMORY_OFFSET, &gesture_lifo, sizeof(gesture_lifo));
49 return; 46 return;
50 } 47 }
51 48
52 ReadTouchInput(); 49 ReadTouchInput();
53 50
54 GestureProperties gesture = GetGestureProperties(); 51 GestureProperties gesture = GetGestureProperties();
55 f32 time_difference = static_cast<f32>(shared_memory.header.timestamp - last_update_timestamp) / 52 f32 time_difference =
56 (1000 * 1000 * 1000); 53 static_cast<f32>(gesture_lifo.timestamp - last_update_timestamp) / (1000 * 1000 * 1000);
57 54
58 // Only update if necesary 55 // Only update if necesary
59 if (!ShouldUpdateGesture(gesture, time_difference)) { 56 if (!ShouldUpdateGesture(gesture, time_difference)) {
60 return; 57 return;
61 } 58 }
62 59
63 last_update_timestamp = shared_memory.header.timestamp; 60 last_update_timestamp = gesture_lifo.timestamp;
64 UpdateGestureSharedMemory(data, size, gesture, time_difference); 61 UpdateGestureSharedMemory(data, size, gesture, time_difference);
65} 62}
66 63
67void Controller_Gesture::ReadTouchInput() { 64void Controller_Gesture::ReadTouchInput() {
68 const Input::TouchStatus& mouse_status = touch_mouse_device->GetStatus(); 65 const auto touch_status = console->GetTouch();
69 const Input::TouchStatus& udp_status = touch_udp_device->GetStatus(); 66 for (std::size_t id = 0; id < fingers.size(); ++id) {
70 for (std::size_t id = 0; id < mouse_status.size(); ++id) { 67 fingers[id] = touch_status[id];
71 mouse_finger_id[id] = UpdateTouchInputEvent(mouse_status[id], mouse_finger_id[id]);
72 udp_finger_id[id] = UpdateTouchInputEvent(udp_status[id], udp_finger_id[id]);
73 }
74
75 if (Settings::values.use_touch_from_button) {
76 const Input::TouchStatus& keyboard_status = touch_btn_device->GetStatus();
77 for (std::size_t id = 0; id < mouse_status.size(); ++id) {
78 keyboard_finger_id[id] =
79 UpdateTouchInputEvent(keyboard_status[id], keyboard_finger_id[id]);
80 }
81 } 68 }
82} 69}
83 70
84bool Controller_Gesture::ShouldUpdateGesture(const GestureProperties& gesture, 71bool Controller_Gesture::ShouldUpdateGesture(const GestureProperties& gesture,
85 f32 time_difference) { 72 f32 time_difference) {
86 const auto& last_entry = shared_memory.gesture_states[shared_memory.header.last_entry_index]; 73 const auto& last_entry = GetLastGestureEntry();
87 if (force_update) { 74 if (force_update) {
88 force_update = false; 75 force_update = false;
89 return true; 76 return true;
@@ -97,7 +84,7 @@ bool Controller_Gesture::ShouldUpdateGesture(const GestureProperties& gesture,
97 } 84 }
98 85
99 // Update on press and hold event after 0.5 seconds 86 // Update on press and hold event after 0.5 seconds
100 if (last_entry.type == TouchType::Touch && last_entry.point_count == 1 && 87 if (last_entry.type == GestureType::Touch && last_entry.point_count == 1 &&
101 time_difference > press_delay) { 88 time_difference > press_delay) {
102 return enable_press_and_tap; 89 return enable_press_and_tap;
103 } 90 }
@@ -108,27 +95,19 @@ bool Controller_Gesture::ShouldUpdateGesture(const GestureProperties& gesture,
108void Controller_Gesture::UpdateGestureSharedMemory(u8* data, std::size_t size, 95void Controller_Gesture::UpdateGestureSharedMemory(u8* data, std::size_t size,
109 GestureProperties& gesture, 96 GestureProperties& gesture,
110 f32 time_difference) { 97 f32 time_difference) {
111 TouchType type = TouchType::Idle; 98 GestureType type = GestureType::Idle;
112 Attribute attributes{}; 99 GestureAttribute attributes{};
113 100
114 const auto& last_entry = shared_memory.gesture_states[shared_memory.header.last_entry_index]; 101 const auto& last_entry = gesture_lifo.ReadCurrentEntry().state;
115 shared_memory.header.last_entry_index = (shared_memory.header.last_entry_index + 1) % 17;
116 auto& cur_entry = shared_memory.gesture_states[shared_memory.header.last_entry_index];
117 102
118 if (shared_memory.header.entry_count < 16) { 103 // Reset next state to default
119 shared_memory.header.entry_count++; 104 next_state.sampling_number = last_entry.sampling_number + 1;
120 } 105 next_state.delta = {};
121 106 next_state.vel_x = 0;
122 cur_entry.sampling_number = last_entry.sampling_number + 1; 107 next_state.vel_y = 0;
123 cur_entry.sampling_number2 = cur_entry.sampling_number; 108 next_state.direction = GestureDirection::None;
124 109 next_state.rotation_angle = 0;
125 // Reset values to default 110 next_state.scale = 0;
126 cur_entry.delta = {};
127 cur_entry.vel_x = 0;
128 cur_entry.vel_y = 0;
129 cur_entry.direction = Direction::None;
130 cur_entry.rotation_angle = 0;
131 cur_entry.scale = 0;
132 111
133 if (gesture.active_points > 0) { 112 if (gesture.active_points > 0) {
134 if (last_gesture.active_points == 0) { 113 if (last_gesture.active_points == 0) {
@@ -141,46 +120,47 @@ void Controller_Gesture::UpdateGestureSharedMemory(u8* data, std::size_t size,
141 } 120 }
142 121
143 // Apply attributes 122 // Apply attributes
144 cur_entry.detection_count = gesture.detection_count; 123 next_state.detection_count = gesture.detection_count;
145 cur_entry.type = type; 124 next_state.type = type;
146 cur_entry.attributes = attributes; 125 next_state.attributes = attributes;
147 cur_entry.pos = gesture.mid_point; 126 next_state.pos = gesture.mid_point;
148 cur_entry.point_count = static_cast<s32>(gesture.active_points); 127 next_state.point_count = static_cast<s32>(gesture.active_points);
149 cur_entry.points = gesture.points; 128 next_state.points = gesture.points;
150 last_gesture = gesture; 129 last_gesture = gesture;
151 130
152 std::memcpy(data + SHARED_MEMORY_OFFSET, &shared_memory, sizeof(SharedMemory)); 131 gesture_lifo.WriteNextEntry(next_state);
132 std::memcpy(data + SHARED_MEMORY_OFFSET, &gesture_lifo, sizeof(gesture_lifo));
153} 133}
154 134
155void Controller_Gesture::NewGesture(GestureProperties& gesture, TouchType& type, 135void Controller_Gesture::NewGesture(GestureProperties& gesture, GestureType& type,
156 Attribute& attributes) { 136 GestureAttribute& attributes) {
157 const auto& last_entry = GetLastGestureEntry(); 137 const auto& last_entry = GetLastGestureEntry();
158 138
159 gesture.detection_count++; 139 gesture.detection_count++;
160 type = TouchType::Touch; 140 type = GestureType::Touch;
161 141
162 // New touch after cancel is not considered new 142 // New touch after cancel is not considered new
163 if (last_entry.type != TouchType::Cancel) { 143 if (last_entry.type != GestureType::Cancel) {
164 attributes.is_new_touch.Assign(1); 144 attributes.is_new_touch.Assign(1);
165 enable_press_and_tap = true; 145 enable_press_and_tap = true;
166 } 146 }
167} 147}
168 148
169void Controller_Gesture::UpdateExistingGesture(GestureProperties& gesture, TouchType& type, 149void Controller_Gesture::UpdateExistingGesture(GestureProperties& gesture, GestureType& type,
170 f32 time_difference) { 150 f32 time_difference) {
171 const auto& last_entry = GetLastGestureEntry(); 151 const auto& last_entry = GetLastGestureEntry();
172 152
173 // Promote to pan type if touch moved 153 // Promote to pan type if touch moved
174 for (size_t id = 0; id < MAX_POINTS; id++) { 154 for (size_t id = 0; id < MAX_POINTS; id++) {
175 if (gesture.points[id] != last_gesture.points[id]) { 155 if (gesture.points[id] != last_gesture.points[id]) {
176 type = TouchType::Pan; 156 type = GestureType::Pan;
177 break; 157 break;
178 } 158 }
179 } 159 }
180 160
181 // Number of fingers changed cancel the last event and clear data 161 // Number of fingers changed cancel the last event and clear data
182 if (gesture.active_points != last_gesture.active_points) { 162 if (gesture.active_points != last_gesture.active_points) {
183 type = TouchType::Cancel; 163 type = GestureType::Cancel;
184 enable_press_and_tap = false; 164 enable_press_and_tap = false;
185 gesture.active_points = 0; 165 gesture.active_points = 0;
186 gesture.mid_point = {}; 166 gesture.mid_point = {};
@@ -189,41 +169,41 @@ void Controller_Gesture::UpdateExistingGesture(GestureProperties& gesture, Touch
189 } 169 }
190 170
191 // Calculate extra parameters of panning 171 // Calculate extra parameters of panning
192 if (type == TouchType::Pan) { 172 if (type == GestureType::Pan) {
193 UpdatePanEvent(gesture, last_gesture, type, time_difference); 173 UpdatePanEvent(gesture, last_gesture, type, time_difference);
194 return; 174 return;
195 } 175 }
196 176
197 // Promote to press type 177 // Promote to press type
198 if (last_entry.type == TouchType::Touch) { 178 if (last_entry.type == GestureType::Touch) {
199 type = TouchType::Press; 179 type = GestureType::Press;
200 } 180 }
201} 181}
202 182
203void Controller_Gesture::EndGesture(GestureProperties& gesture, 183void Controller_Gesture::EndGesture(GestureProperties& gesture,
204 GestureProperties& last_gesture_props, TouchType& type, 184 GestureProperties& last_gesture_props, GestureType& type,
205 Attribute& attributes, f32 time_difference) { 185 GestureAttribute& attributes, f32 time_difference) {
206 const auto& last_entry = GetLastGestureEntry(); 186 const auto& last_entry = GetLastGestureEntry();
207 187
208 if (last_gesture_props.active_points != 0) { 188 if (last_gesture_props.active_points != 0) {
209 switch (last_entry.type) { 189 switch (last_entry.type) {
210 case TouchType::Touch: 190 case GestureType::Touch:
211 if (enable_press_and_tap) { 191 if (enable_press_and_tap) {
212 SetTapEvent(gesture, last_gesture_props, type, attributes); 192 SetTapEvent(gesture, last_gesture_props, type, attributes);
213 return; 193 return;
214 } 194 }
215 type = TouchType::Cancel; 195 type = GestureType::Cancel;
216 force_update = true; 196 force_update = true;
217 break; 197 break;
218 case TouchType::Press: 198 case GestureType::Press:
219 case TouchType::Tap: 199 case GestureType::Tap:
220 case TouchType::Swipe: 200 case GestureType::Swipe:
221 case TouchType::Pinch: 201 case GestureType::Pinch:
222 case TouchType::Rotate: 202 case GestureType::Rotate:
223 type = TouchType::Complete; 203 type = GestureType::Complete;
224 force_update = true; 204 force_update = true;
225 break; 205 break;
226 case TouchType::Pan: 206 case GestureType::Pan:
227 EndPanEvent(gesture, last_gesture_props, type, time_difference); 207 EndPanEvent(gesture, last_gesture_props, type, time_difference);
228 break; 208 break;
229 default: 209 default:
@@ -231,15 +211,15 @@ void Controller_Gesture::EndGesture(GestureProperties& gesture,
231 } 211 }
232 return; 212 return;
233 } 213 }
234 if (last_entry.type == TouchType::Complete || last_entry.type == TouchType::Cancel) { 214 if (last_entry.type == GestureType::Complete || last_entry.type == GestureType::Cancel) {
235 gesture.detection_count++; 215 gesture.detection_count++;
236 } 216 }
237} 217}
238 218
239void Controller_Gesture::SetTapEvent(GestureProperties& gesture, 219void Controller_Gesture::SetTapEvent(GestureProperties& gesture,
240 GestureProperties& last_gesture_props, TouchType& type, 220 GestureProperties& last_gesture_props, GestureType& type,
241 Attribute& attributes) { 221 GestureAttribute& attributes) {
242 type = TouchType::Tap; 222 type = GestureType::Tap;
243 gesture = last_gesture_props; 223 gesture = last_gesture_props;
244 force_update = true; 224 force_update = true;
245 f32 tap_time_difference = 225 f32 tap_time_difference =
@@ -251,44 +231,42 @@ void Controller_Gesture::SetTapEvent(GestureProperties& gesture,
251} 231}
252 232
253void Controller_Gesture::UpdatePanEvent(GestureProperties& gesture, 233void Controller_Gesture::UpdatePanEvent(GestureProperties& gesture,
254 GestureProperties& last_gesture_props, TouchType& type, 234 GestureProperties& last_gesture_props, GestureType& type,
255 f32 time_difference) { 235 f32 time_difference) {
256 auto& cur_entry = shared_memory.gesture_states[shared_memory.header.last_entry_index];
257 const auto& last_entry = GetLastGestureEntry(); 236 const auto& last_entry = GetLastGestureEntry();
258 237
259 cur_entry.delta = gesture.mid_point - last_entry.pos; 238 next_state.delta = gesture.mid_point - last_entry.pos;
260 cur_entry.vel_x = static_cast<f32>(cur_entry.delta.x) / time_difference; 239 next_state.vel_x = static_cast<f32>(next_state.delta.x) / time_difference;
261 cur_entry.vel_y = static_cast<f32>(cur_entry.delta.y) / time_difference; 240 next_state.vel_y = static_cast<f32>(next_state.delta.y) / time_difference;
262 last_pan_time_difference = time_difference; 241 last_pan_time_difference = time_difference;
263 242
264 // Promote to pinch type 243 // Promote to pinch type
265 if (std::abs(gesture.average_distance - last_gesture_props.average_distance) > 244 if (std::abs(gesture.average_distance - last_gesture_props.average_distance) >
266 pinch_threshold) { 245 pinch_threshold) {
267 type = TouchType::Pinch; 246 type = GestureType::Pinch;
268 cur_entry.scale = gesture.average_distance / last_gesture_props.average_distance; 247 next_state.scale = gesture.average_distance / last_gesture_props.average_distance;
269 } 248 }
270 249
271 const f32 angle_between_two_lines = std::atan((gesture.angle - last_gesture_props.angle) / 250 const f32 angle_between_two_lines = std::atan((gesture.angle - last_gesture_props.angle) /
272 (1 + (gesture.angle * last_gesture_props.angle))); 251 (1 + (gesture.angle * last_gesture_props.angle)));
273 // Promote to rotate type 252 // Promote to rotate type
274 if (std::abs(angle_between_two_lines) > angle_threshold) { 253 if (std::abs(angle_between_two_lines) > angle_threshold) {
275 type = TouchType::Rotate; 254 type = GestureType::Rotate;
276 cur_entry.scale = 0; 255 next_state.scale = 0;
277 cur_entry.rotation_angle = angle_between_two_lines * 180.0f / Common::PI; 256 next_state.rotation_angle = angle_between_two_lines * 180.0f / Common::PI;
278 } 257 }
279} 258}
280 259
281void Controller_Gesture::EndPanEvent(GestureProperties& gesture, 260void Controller_Gesture::EndPanEvent(GestureProperties& gesture,
282 GestureProperties& last_gesture_props, TouchType& type, 261 GestureProperties& last_gesture_props, GestureType& type,
283 f32 time_difference) { 262 f32 time_difference) {
284 auto& cur_entry = shared_memory.gesture_states[shared_memory.header.last_entry_index];
285 const auto& last_entry = GetLastGestureEntry(); 263 const auto& last_entry = GetLastGestureEntry();
286 cur_entry.vel_x = 264 next_state.vel_x =
287 static_cast<f32>(last_entry.delta.x) / (last_pan_time_difference + time_difference); 265 static_cast<f32>(last_entry.delta.x) / (last_pan_time_difference + time_difference);
288 cur_entry.vel_y = 266 next_state.vel_y =
289 static_cast<f32>(last_entry.delta.y) / (last_pan_time_difference + time_difference); 267 static_cast<f32>(last_entry.delta.y) / (last_pan_time_difference + time_difference);
290 const f32 curr_vel = 268 const f32 curr_vel =
291 std::sqrt((cur_entry.vel_x * cur_entry.vel_x) + (cur_entry.vel_y * cur_entry.vel_y)); 269 std::sqrt((next_state.vel_x * next_state.vel_x) + (next_state.vel_y * next_state.vel_y));
292 270
293 // Set swipe event with parameters 271 // Set swipe event with parameters
294 if (curr_vel > swipe_threshold) { 272 if (curr_vel > swipe_threshold) {
@@ -297,105 +275,50 @@ void Controller_Gesture::EndPanEvent(GestureProperties& gesture,
297 } 275 }
298 276
299 // End panning without swipe 277 // End panning without swipe
300 type = TouchType::Complete; 278 type = GestureType::Complete;
301 cur_entry.vel_x = 0; 279 next_state.vel_x = 0;
302 cur_entry.vel_y = 0; 280 next_state.vel_y = 0;
303 force_update = true; 281 force_update = true;
304} 282}
305 283
306void Controller_Gesture::SetSwipeEvent(GestureProperties& gesture, 284void Controller_Gesture::SetSwipeEvent(GestureProperties& gesture,
307 GestureProperties& last_gesture_props, TouchType& type) { 285 GestureProperties& last_gesture_props, GestureType& type) {
308 auto& cur_entry = shared_memory.gesture_states[shared_memory.header.last_entry_index];
309 const auto& last_entry = GetLastGestureEntry(); 286 const auto& last_entry = GetLastGestureEntry();
310 287
311 type = TouchType::Swipe; 288 type = GestureType::Swipe;
312 gesture = last_gesture_props; 289 gesture = last_gesture_props;
313 force_update = true; 290 force_update = true;
314 cur_entry.delta = last_entry.delta; 291 next_state.delta = last_entry.delta;
315 292
316 if (std::abs(cur_entry.delta.x) > std::abs(cur_entry.delta.y)) { 293 if (std::abs(next_state.delta.x) > std::abs(next_state.delta.y)) {
317 if (cur_entry.delta.x > 0) { 294 if (next_state.delta.x > 0) {
318 cur_entry.direction = Direction::Right; 295 next_state.direction = GestureDirection::Right;
319 return; 296 return;
320 } 297 }
321 cur_entry.direction = Direction::Left; 298 next_state.direction = GestureDirection::Left;
322 return; 299 return;
323 } 300 }
324 if (cur_entry.delta.y > 0) { 301 if (next_state.delta.y > 0) {
325 cur_entry.direction = Direction::Down; 302 next_state.direction = GestureDirection::Down;
326 return; 303 return;
327 } 304 }
328 cur_entry.direction = Direction::Up; 305 next_state.direction = GestureDirection::Up;
329}
330
331void Controller_Gesture::OnLoadInputDevices() {
332 touch_mouse_device = Input::CreateDevice<Input::TouchDevice>("engine:emu_window");
333 touch_udp_device = Input::CreateDevice<Input::TouchDevice>("engine:cemuhookudp");
334 touch_btn_device = Input::CreateDevice<Input::TouchDevice>("engine:touch_from_button");
335}
336
337std::optional<std::size_t> Controller_Gesture::GetUnusedFingerID() const {
338 // Dont assign any touch input to a point if disabled
339 if (!Settings::values.touchscreen.enabled) {
340 return std::nullopt;
341 }
342 std::size_t first_free_id = 0;
343 while (first_free_id < MAX_POINTS) {
344 if (!fingers[first_free_id].pressed) {
345 return first_free_id;
346 } else {
347 first_free_id++;
348 }
349 }
350 return std::nullopt;
351}
352
353Controller_Gesture::GestureState& Controller_Gesture::GetLastGestureEntry() {
354 return shared_memory.gesture_states[(shared_memory.header.last_entry_index + 16) % 17];
355} 306}
356 307
357const Controller_Gesture::GestureState& Controller_Gesture::GetLastGestureEntry() const { 308const Controller_Gesture::GestureState& Controller_Gesture::GetLastGestureEntry() const {
358 return shared_memory.gesture_states[(shared_memory.header.last_entry_index + 16) % 17]; 309 return gesture_lifo.ReadCurrentEntry().state;
359}
360
361std::size_t Controller_Gesture::UpdateTouchInputEvent(
362 const std::tuple<float, float, bool>& touch_input, std::size_t finger_id) {
363 const auto& [x, y, pressed] = touch_input;
364 if (finger_id > MAX_POINTS) {
365 LOG_ERROR(Service_HID, "Invalid finger id {}", finger_id);
366 return MAX_POINTS;
367 }
368 if (pressed) {
369 if (finger_id == MAX_POINTS) {
370 const auto first_free_id = GetUnusedFingerID();
371 if (!first_free_id) {
372 // Invalid finger id do nothing
373 return MAX_POINTS;
374 }
375 finger_id = first_free_id.value();
376 fingers[finger_id].pressed = true;
377 }
378 fingers[finger_id].pos = {x, y};
379 return finger_id;
380 }
381
382 if (finger_id != MAX_POINTS) {
383 fingers[finger_id].pressed = false;
384 }
385
386 return MAX_POINTS;
387} 310}
388 311
389Controller_Gesture::GestureProperties Controller_Gesture::GetGestureProperties() { 312Controller_Gesture::GestureProperties Controller_Gesture::GetGestureProperties() {
390 GestureProperties gesture; 313 GestureProperties gesture;
391 std::array<Finger, MAX_POINTS> active_fingers; 314 std::array<Core::HID::TouchFinger, MAX_POINTS> active_fingers;
392 const auto end_iter = std::copy_if(fingers.begin(), fingers.end(), active_fingers.begin(), 315 const auto end_iter = std::copy_if(fingers.begin(), fingers.end(), active_fingers.begin(),
393 [](const auto& finger) { return finger.pressed; }); 316 [](const auto& finger) { return finger.pressed; });
394 gesture.active_points = 317 gesture.active_points =
395 static_cast<std::size_t>(std::distance(active_fingers.begin(), end_iter)); 318 static_cast<std::size_t>(std::distance(active_fingers.begin(), end_iter));
396 319
397 for (size_t id = 0; id < gesture.active_points; ++id) { 320 for (size_t id = 0; id < gesture.active_points; ++id) {
398 const auto& [active_x, active_y] = active_fingers[id].pos; 321 const auto& [active_x, active_y] = active_fingers[id].position;
399 gesture.points[id] = { 322 gesture.points[id] = {
400 .x = static_cast<s32>(active_x * Layout::ScreenUndocked::Width), 323 .x = static_cast<s32>(active_x * Layout::ScreenUndocked::Width),
401 .y = static_cast<s32>(active_y * Layout::ScreenUndocked::Height), 324 .y = static_cast<s32>(active_y * Layout::ScreenUndocked::Height),
diff --git a/src/core/hle/service/hid/controllers/gesture.h b/src/core/hle/service/hid/controllers/gesture.h
index 7e7ae6625..0936a3fa3 100644
--- a/src/core/hle/service/hid/controllers/gesture.h
+++ b/src/core/hle/service/hid/controllers/gesture.h
@@ -8,13 +8,14 @@
8#include "common/bit_field.h" 8#include "common/bit_field.h"
9#include "common/common_types.h" 9#include "common/common_types.h"
10#include "common/point.h" 10#include "common/point.h"
11#include "core/frontend/input.h" 11#include "core/hid/emulated_console.h"
12#include "core/hle/service/hid/controllers/controller_base.h" 12#include "core/hle/service/hid/controllers/controller_base.h"
13#include "core/hle/service/hid/ring_lifo.h"
13 14
14namespace Service::HID { 15namespace Service::HID {
15class Controller_Gesture final : public ControllerBase { 16class Controller_Gesture final : public ControllerBase {
16public: 17public:
17 explicit Controller_Gesture(Core::System& system_); 18 explicit Controller_Gesture(Core::HID::HIDCore& hid_core_);
18 ~Controller_Gesture() override; 19 ~Controller_Gesture() override;
19 20
20 // Called when the controller is initialized 21 // Called when the controller is initialized
@@ -26,14 +27,12 @@ public:
26 // When the controller is requesting an update for the shared memory 27 // When the controller is requesting an update for the shared memory
27 void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, size_t size) override; 28 void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, size_t size) override;
28 29
29 // Called when input devices should be loaded
30 void OnLoadInputDevices() override;
31
32private: 30private:
33 static constexpr size_t MAX_FINGERS = 16; 31 static constexpr size_t MAX_FINGERS = 16;
34 static constexpr size_t MAX_POINTS = 4; 32 static constexpr size_t MAX_POINTS = 4;
35 33
36 enum class TouchType : u32 { 34 // This is nn::hid::GestureType
35 enum class GestureType : u32 {
37 Idle, // Nothing touching the screen 36 Idle, // Nothing touching the screen
38 Complete, // Set at the end of a touch event 37 Complete, // Set at the end of a touch event
39 Cancel, // Set when the number of fingers change 38 Cancel, // Set when the number of fingers change
@@ -46,7 +45,8 @@ private:
46 Rotate, // All points rotating from the midpoint 45 Rotate, // All points rotating from the midpoint
47 }; 46 };
48 47
49 enum class Direction : u32 { 48 // This is nn::hid::GestureDirection
49 enum class GestureDirection : u32 {
50 None, 50 None,
51 Left, 51 Left,
52 Up, 52 Up,
@@ -54,51 +54,41 @@ private:
54 Down, 54 Down,
55 }; 55 };
56 56
57 struct Attribute { 57 // This is nn::hid::GestureAttribute
58 struct GestureAttribute {
58 union { 59 union {
59 u32_le raw{}; 60 u32 raw{};
60 61
61 BitField<4, 1, u32> is_new_touch; 62 BitField<4, 1, u32> is_new_touch;
62 BitField<8, 1, u32> is_double_tap; 63 BitField<8, 1, u32> is_double_tap;
63 }; 64 };
64 }; 65 };
65 static_assert(sizeof(Attribute) == 4, "Attribute is an invalid size"); 66 static_assert(sizeof(GestureAttribute) == 4, "GestureAttribute is an invalid size");
66 67
68 // This is nn::hid::GestureState
67 struct GestureState { 69 struct GestureState {
68 s64_le sampling_number; 70 s64 sampling_number;
69 s64_le sampling_number2; 71 s64 detection_count;
70 s64_le detection_count; 72 GestureType type;
71 TouchType type; 73 GestureDirection direction;
72 Direction direction; 74 Common::Point<s32> pos;
73 Common::Point<s32_le> pos; 75 Common::Point<s32> delta;
74 Common::Point<s32_le> delta;
75 f32 vel_x; 76 f32 vel_x;
76 f32 vel_y; 77 f32 vel_y;
77 Attribute attributes; 78 GestureAttribute attributes;
78 f32 scale; 79 f32 scale;
79 f32 rotation_angle; 80 f32 rotation_angle;
80 s32_le point_count; 81 s32 point_count;
81 std::array<Common::Point<s32_le>, 4> points; 82 std::array<Common::Point<s32>, 4> points;
82 };
83 static_assert(sizeof(GestureState) == 0x68, "GestureState is an invalid size");
84
85 struct SharedMemory {
86 CommonHeader header;
87 std::array<GestureState, 17> gesture_states;
88 };
89 static_assert(sizeof(SharedMemory) == 0x708, "SharedMemory is an invalid size");
90
91 struct Finger {
92 Common::Point<f32> pos{};
93 bool pressed{};
94 }; 83 };
84 static_assert(sizeof(GestureState) == 0x60, "GestureState is an invalid size");
95 85
96 struct GestureProperties { 86 struct GestureProperties {
97 std::array<Common::Point<s32_le>, MAX_POINTS> points{}; 87 std::array<Common::Point<s32>, MAX_POINTS> points{};
98 std::size_t active_points{}; 88 std::size_t active_points{};
99 Common::Point<s32_le> mid_point{}; 89 Common::Point<s32> mid_point{};
100 s64_le detection_count{}; 90 s64 detection_count{};
101 u64_le delta_time{}; 91 u64 delta_time{};
102 f32 average_distance{}; 92 f32 average_distance{};
103 f32 angle{}; 93 f32 angle{};
104 }; 94 };
@@ -114,61 +104,48 @@ private:
114 f32 time_difference); 104 f32 time_difference);
115 105
116 // Initializes new gesture 106 // Initializes new gesture
117 void NewGesture(GestureProperties& gesture, TouchType& type, Attribute& attributes); 107 void NewGesture(GestureProperties& gesture, GestureType& type, GestureAttribute& attributes);
118 108
119 // Updates existing gesture state 109 // Updates existing gesture state
120 void UpdateExistingGesture(GestureProperties& gesture, TouchType& type, f32 time_difference); 110 void UpdateExistingGesture(GestureProperties& gesture, GestureType& type, f32 time_difference);
121 111
122 // Terminates exiting gesture 112 // Terminates exiting gesture
123 void EndGesture(GestureProperties& gesture, GestureProperties& last_gesture_props, 113 void EndGesture(GestureProperties& gesture, GestureProperties& last_gesture_props,
124 TouchType& type, Attribute& attributes, f32 time_difference); 114 GestureType& type, GestureAttribute& attributes, f32 time_difference);
125 115
126 // Set current event to a tap event 116 // Set current event to a tap event
127 void SetTapEvent(GestureProperties& gesture, GestureProperties& last_gesture_props, 117 void SetTapEvent(GestureProperties& gesture, GestureProperties& last_gesture_props,
128 TouchType& type, Attribute& attributes); 118 GestureType& type, GestureAttribute& attributes);
129 119
130 // Calculates and set the extra parameters related to a pan event 120 // Calculates and set the extra parameters related to a pan event
131 void UpdatePanEvent(GestureProperties& gesture, GestureProperties& last_gesture_props, 121 void UpdatePanEvent(GestureProperties& gesture, GestureProperties& last_gesture_props,
132 TouchType& type, f32 time_difference); 122 GestureType& type, f32 time_difference);
133 123
134 // Terminates the pan event 124 // Terminates the pan event
135 void EndPanEvent(GestureProperties& gesture, GestureProperties& last_gesture_props, 125 void EndPanEvent(GestureProperties& gesture, GestureProperties& last_gesture_props,
136 TouchType& type, f32 time_difference); 126 GestureType& type, f32 time_difference);
137 127
138 // Set current event to a swipe event 128 // Set current event to a swipe event
139 void SetSwipeEvent(GestureProperties& gesture, GestureProperties& last_gesture_props, 129 void SetSwipeEvent(GestureProperties& gesture, GestureProperties& last_gesture_props,
140 TouchType& type); 130 GestureType& type);
141
142 // Returns an unused finger id, if there is no fingers available std::nullopt is returned.
143 [[nodiscard]] std::optional<size_t> GetUnusedFingerID() const;
144 131
145 // Retrieves the last gesture entry, as indicated by shared memory indices. 132 // Retrieves the last gesture entry, as indicated by shared memory indices.
146 [[nodiscard]] GestureState& GetLastGestureEntry();
147 [[nodiscard]] const GestureState& GetLastGestureEntry() const; 133 [[nodiscard]] const GestureState& GetLastGestureEntry() const;
148 134
149 /**
150 * If the touch is new it tries to assign a new finger id, if there is no fingers available no
151 * changes will be made. Updates the coordinates if the finger id it's already set. If the touch
152 * ends delays the output by one frame to set the end_touch flag before finally freeing the
153 * finger id
154 */
155 size_t UpdateTouchInputEvent(const std::tuple<float, float, bool>& touch_input,
156 size_t finger_id);
157
158 // Returns the average distance, angle and middle point of the active fingers 135 // Returns the average distance, angle and middle point of the active fingers
159 GestureProperties GetGestureProperties(); 136 GestureProperties GetGestureProperties();
160 137
161 SharedMemory shared_memory{}; 138 // This is nn::hid::detail::GestureLifo
162 std::unique_ptr<Input::TouchDevice> touch_mouse_device; 139 Lifo<GestureState, hid_entry_count> gesture_lifo{};
163 std::unique_ptr<Input::TouchDevice> touch_udp_device; 140 static_assert(sizeof(gesture_lifo) == 0x708, "gesture_lifo is an invalid size");
164 std::unique_ptr<Input::TouchDevice> touch_btn_device; 141 GestureState next_state{};
165 std::array<size_t, MAX_FINGERS> mouse_finger_id{}; 142
166 std::array<size_t, MAX_FINGERS> keyboard_finger_id{}; 143 Core::HID::EmulatedConsole* console;
167 std::array<size_t, MAX_FINGERS> udp_finger_id{}; 144
168 std::array<Finger, MAX_POINTS> fingers{}; 145 std::array<Core::HID::TouchFinger, MAX_POINTS> fingers{};
169 GestureProperties last_gesture{}; 146 GestureProperties last_gesture{};
170 s64_le last_update_timestamp{}; 147 s64 last_update_timestamp{};
171 s64_le last_tap_timestamp{}; 148 s64 last_tap_timestamp{};
172 f32 last_pan_time_difference{}; 149 f32 last_pan_time_difference{};
173 bool force_update{false}; 150 bool force_update{false};
174 bool enable_press_and_tap{false}; 151 bool enable_press_and_tap{false};
diff --git a/src/core/hle/service/hid/controllers/keyboard.cpp b/src/core/hle/service/hid/controllers/keyboard.cpp
index c6c620008..9588a6910 100644
--- a/src/core/hle/service/hid/controllers/keyboard.cpp
+++ b/src/core/hle/service/hid/controllers/keyboard.cpp
@@ -6,13 +6,18 @@
6#include "common/common_types.h" 6#include "common/common_types.h"
7#include "common/settings.h" 7#include "common/settings.h"
8#include "core/core_timing.h" 8#include "core/core_timing.h"
9#include "core/hid/emulated_devices.h"
10#include "core/hid/hid_core.h"
9#include "core/hle/service/hid/controllers/keyboard.h" 11#include "core/hle/service/hid/controllers/keyboard.h"
10 12
11namespace Service::HID { 13namespace Service::HID {
12constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3800; 14constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3800;
13constexpr u8 KEYS_PER_BYTE = 8;
14 15
15Controller_Keyboard::Controller_Keyboard(Core::System& system_) : ControllerBase{system_} {} 16Controller_Keyboard::Controller_Keyboard(Core::HID::HIDCore& hid_core_)
17 : ControllerBase{hid_core_} {
18 emulated_devices = hid_core.GetEmulatedDevices();
19}
20
16Controller_Keyboard::~Controller_Keyboard() = default; 21Controller_Keyboard::~Controller_Keyboard() = default;
17 22
18void Controller_Keyboard::OnInit() {} 23void Controller_Keyboard::OnInit() {}
@@ -21,51 +26,27 @@ void Controller_Keyboard::OnRelease() {}
21 26
22void Controller_Keyboard::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, 27void Controller_Keyboard::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data,
23 std::size_t size) { 28 std::size_t size) {
24 shared_memory.header.timestamp = core_timing.GetCPUTicks();
25 shared_memory.header.total_entry_count = 17;
26
27 if (!IsControllerActivated()) { 29 if (!IsControllerActivated()) {
28 shared_memory.header.entry_count = 0; 30 keyboard_lifo.buffer_count = 0;
29 shared_memory.header.last_entry_index = 0; 31 keyboard_lifo.buffer_tail = 0;
32 std::memcpy(data + SHARED_MEMORY_OFFSET, &keyboard_lifo, sizeof(keyboard_lifo));
30 return; 33 return;
31 } 34 }
32 shared_memory.header.entry_count = 16;
33
34 const auto& last_entry = shared_memory.pad_states[shared_memory.header.last_entry_index];
35 shared_memory.header.last_entry_index = (shared_memory.header.last_entry_index + 1) % 17;
36 auto& cur_entry = shared_memory.pad_states[shared_memory.header.last_entry_index];
37 35
38 cur_entry.sampling_number = last_entry.sampling_number + 1; 36 const auto& last_entry = keyboard_lifo.ReadCurrentEntry().state;
39 cur_entry.sampling_number2 = cur_entry.sampling_number; 37 next_state.sampling_number = last_entry.sampling_number + 1;
40 38
41 cur_entry.key.fill(0);
42 if (Settings::values.keyboard_enabled) { 39 if (Settings::values.keyboard_enabled) {
43 for (std::size_t i = 0; i < keyboard_keys.size(); ++i) { 40 const auto& keyboard_state = emulated_devices->GetKeyboard();
44 auto& entry = cur_entry.key[i / KEYS_PER_BYTE]; 41 const auto& keyboard_modifier_state = emulated_devices->GetKeyboardModifier();
45 entry = static_cast<u8>(entry | (keyboard_keys[i]->GetStatus() << (i % KEYS_PER_BYTE)));
46 }
47 42
48 using namespace Settings::NativeKeyboard; 43 next_state.key = keyboard_state;
49 44 next_state.modifier = keyboard_modifier_state;
50 // TODO: Assign the correct key to all modifiers 45 next_state.attribute.is_connected.Assign(1);
51 cur_entry.modifier.control.Assign(keyboard_mods[LeftControl]->GetStatus());
52 cur_entry.modifier.shift.Assign(keyboard_mods[LeftShift]->GetStatus());
53 cur_entry.modifier.left_alt.Assign(keyboard_mods[LeftAlt]->GetStatus());
54 cur_entry.modifier.right_alt.Assign(keyboard_mods[RightAlt]->GetStatus());
55 cur_entry.modifier.gui.Assign(0);
56 cur_entry.modifier.caps_lock.Assign(keyboard_mods[CapsLock]->GetStatus());
57 cur_entry.modifier.scroll_lock.Assign(keyboard_mods[ScrollLock]->GetStatus());
58 cur_entry.modifier.num_lock.Assign(keyboard_mods[NumLock]->GetStatus());
59 cur_entry.modifier.katakana.Assign(0);
60 cur_entry.modifier.hiragana.Assign(0);
61 } 46 }
62 std::memcpy(data + SHARED_MEMORY_OFFSET, &shared_memory, sizeof(SharedMemory));
63}
64 47
65void Controller_Keyboard::OnLoadInputDevices() { 48 keyboard_lifo.WriteNextEntry(next_state);
66 std::transform(Settings::values.keyboard_keys.begin(), Settings::values.keyboard_keys.end(), 49 std::memcpy(data + SHARED_MEMORY_OFFSET, &keyboard_lifo, sizeof(keyboard_lifo));
67 keyboard_keys.begin(), Input::CreateDevice<Input::ButtonDevice>);
68 std::transform(Settings::values.keyboard_mods.begin(), Settings::values.keyboard_mods.end(),
69 keyboard_mods.begin(), Input::CreateDevice<Input::ButtonDevice>);
70} 50}
51
71} // namespace Service::HID 52} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/keyboard.h b/src/core/hle/service/hid/controllers/keyboard.h
index 172a80e9c..cf62d3896 100644
--- a/src/core/hle/service/hid/controllers/keyboard.h
+++ b/src/core/hle/service/hid/controllers/keyboard.h
@@ -8,15 +8,20 @@
8#include "common/bit_field.h" 8#include "common/bit_field.h"
9#include "common/common_funcs.h" 9#include "common/common_funcs.h"
10#include "common/common_types.h" 10#include "common/common_types.h"
11#include "common/settings.h"
12#include "common/swap.h" 11#include "common/swap.h"
13#include "core/frontend/input.h"
14#include "core/hle/service/hid/controllers/controller_base.h" 12#include "core/hle/service/hid/controllers/controller_base.h"
13#include "core/hle/service/hid/ring_lifo.h"
14
15namespace Core::HID {
16class EmulatedDevices;
17struct KeyboardModifier;
18struct KeyboardKey;
19} // namespace Core::HID
15 20
16namespace Service::HID { 21namespace Service::HID {
17class Controller_Keyboard final : public ControllerBase { 22class Controller_Keyboard final : public ControllerBase {
18public: 23public:
19 explicit Controller_Keyboard(Core::System& system_); 24 explicit Controller_Keyboard(Core::HID::HIDCore& hid_core_);
20 ~Controller_Keyboard() override; 25 ~Controller_Keyboard() override;
21 26
22 // Called when the controller is initialized 27 // Called when the controller is initialized
@@ -28,47 +33,21 @@ public:
28 // When the controller is requesting an update for the shared memory 33 // When the controller is requesting an update for the shared memory
29 void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t size) override; 34 void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t size) override;
30 35
31 // Called when input devices should be loaded
32 void OnLoadInputDevices() override;
33
34private: 36private:
35 struct Modifiers { 37 // This is nn::hid::detail::KeyboardState
36 union {
37 u32_le raw{};
38 BitField<0, 1, u32> control;
39 BitField<1, 1, u32> shift;
40 BitField<2, 1, u32> left_alt;
41 BitField<3, 1, u32> right_alt;
42 BitField<4, 1, u32> gui;
43 BitField<8, 1, u32> caps_lock;
44 BitField<9, 1, u32> scroll_lock;
45 BitField<10, 1, u32> num_lock;
46 BitField<11, 1, u32> katakana;
47 BitField<12, 1, u32> hiragana;
48 };
49 };
50 static_assert(sizeof(Modifiers) == 0x4, "Modifiers is an invalid size");
51
52 struct KeyboardState { 38 struct KeyboardState {
53 s64_le sampling_number; 39 s64 sampling_number;
54 s64_le sampling_number2; 40 Core::HID::KeyboardModifier modifier;
55 41 Core::HID::KeyboardAttribute attribute;
56 Modifiers modifier; 42 Core::HID::KeyboardKey key;
57 std::array<u8, 32> key;
58 }; 43 };
59 static_assert(sizeof(KeyboardState) == 0x38, "KeyboardState is an invalid size"); 44 static_assert(sizeof(KeyboardState) == 0x30, "KeyboardState is an invalid size");
60 45
61 struct SharedMemory { 46 // This is nn::hid::detail::KeyboardLifo
62 CommonHeader header; 47 Lifo<KeyboardState, hid_entry_count> keyboard_lifo{};
63 std::array<KeyboardState, 17> pad_states; 48 static_assert(sizeof(keyboard_lifo) == 0x3D8, "keyboard_lifo is an invalid size");
64 INSERT_PADDING_BYTES(0x28); 49 KeyboardState next_state{};
65 };
66 static_assert(sizeof(SharedMemory) == 0x400, "SharedMemory is an invalid size");
67 SharedMemory shared_memory{};
68 50
69 std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeKeyboard::NumKeyboardKeys> 51 Core::HID::EmulatedDevices* emulated_devices;
70 keyboard_keys;
71 std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeKeyboard::NumKeyboardMods>
72 keyboard_mods;
73}; 52};
74} // namespace Service::HID 53} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/mouse.cpp b/src/core/hle/service/hid/controllers/mouse.cpp
index 544a71948..ba79888ae 100644
--- a/src/core/hle/service/hid/controllers/mouse.cpp
+++ b/src/core/hle/service/hid/controllers/mouse.cpp
@@ -6,12 +6,17 @@
6#include "common/common_types.h" 6#include "common/common_types.h"
7#include "core/core_timing.h" 7#include "core/core_timing.h"
8#include "core/frontend/emu_window.h" 8#include "core/frontend/emu_window.h"
9#include "core/hid/emulated_devices.h"
10#include "core/hid/hid_core.h"
9#include "core/hle/service/hid/controllers/mouse.h" 11#include "core/hle/service/hid/controllers/mouse.h"
10 12
11namespace Service::HID { 13namespace Service::HID {
12constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3400; 14constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3400;
13 15
14Controller_Mouse::Controller_Mouse(Core::System& system_) : ControllerBase{system_} {} 16Controller_Mouse::Controller_Mouse(Core::HID::HIDCore& hid_core_) : ControllerBase{hid_core_} {
17 emulated_devices = hid_core.GetEmulatedDevices();
18}
19
15Controller_Mouse::~Controller_Mouse() = default; 20Controller_Mouse::~Controller_Mouse() = default;
16 21
17void Controller_Mouse::OnInit() {} 22void Controller_Mouse::OnInit() {}
@@ -19,50 +24,35 @@ void Controller_Mouse::OnRelease() {}
19 24
20void Controller_Mouse::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, 25void Controller_Mouse::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data,
21 std::size_t size) { 26 std::size_t size) {
22 shared_memory.header.timestamp = core_timing.GetCPUTicks();
23 shared_memory.header.total_entry_count = 17;
24
25 if (!IsControllerActivated()) { 27 if (!IsControllerActivated()) {
26 shared_memory.header.entry_count = 0; 28 mouse_lifo.buffer_count = 0;
27 shared_memory.header.last_entry_index = 0; 29 mouse_lifo.buffer_tail = 0;
30 std::memcpy(data + SHARED_MEMORY_OFFSET, &mouse_lifo, sizeof(mouse_lifo));
28 return; 31 return;
29 } 32 }
30 shared_memory.header.entry_count = 16;
31 33
32 auto& last_entry = shared_memory.mouse_states[shared_memory.header.last_entry_index]; 34 const auto& last_entry = mouse_lifo.ReadCurrentEntry().state;
33 shared_memory.header.last_entry_index = (shared_memory.header.last_entry_index + 1) % 17; 35 next_state.sampling_number = last_entry.sampling_number + 1;
34 auto& cur_entry = shared_memory.mouse_states[shared_memory.header.last_entry_index];
35 36
36 cur_entry.sampling_number = last_entry.sampling_number + 1; 37 next_state.attribute.raw = 0;
37 cur_entry.sampling_number2 = cur_entry.sampling_number;
38
39 cur_entry.attribute.raw = 0;
40 if (Settings::values.mouse_enabled) { 38 if (Settings::values.mouse_enabled) {
41 const auto [px, py, sx, sy] = mouse_device->GetStatus(); 39 const auto& mouse_button_state = emulated_devices->GetMouseButtons();
42 const auto x = static_cast<s32>(px * Layout::ScreenUndocked::Width); 40 const auto& mouse_position_state = emulated_devices->GetMousePosition();
43 const auto y = static_cast<s32>(py * Layout::ScreenUndocked::Height); 41 const auto& mouse_wheel_state = emulated_devices->GetMouseWheel();
44 cur_entry.x = x; 42 next_state.attribute.is_connected.Assign(1);
45 cur_entry.y = y; 43 next_state.x = static_cast<s32>(mouse_position_state.x * Layout::ScreenUndocked::Width);
46 cur_entry.delta_x = x - last_entry.x; 44 next_state.y = static_cast<s32>(mouse_position_state.y * Layout::ScreenUndocked::Height);
47 cur_entry.delta_y = y - last_entry.y; 45 next_state.delta_x = next_state.x - last_entry.x;
48 cur_entry.mouse_wheel_x = sx; 46 next_state.delta_y = next_state.y - last_entry.y;
49 cur_entry.mouse_wheel_y = sy; 47 next_state.delta_wheel_x = mouse_wheel_state.x - last_mouse_wheel_state.x;
50 cur_entry.attribute.is_connected.Assign(1); 48 next_state.delta_wheel_y = mouse_wheel_state.y - last_mouse_wheel_state.y;
51 49
52 using namespace Settings::NativeMouseButton; 50 last_mouse_wheel_state = mouse_wheel_state;
53 cur_entry.button.left.Assign(mouse_button_devices[Left]->GetStatus()); 51 next_state.button = mouse_button_state;
54 cur_entry.button.right.Assign(mouse_button_devices[Right]->GetStatus());
55 cur_entry.button.middle.Assign(mouse_button_devices[Middle]->GetStatus());
56 cur_entry.button.forward.Assign(mouse_button_devices[Forward]->GetStatus());
57 cur_entry.button.back.Assign(mouse_button_devices[Back]->GetStatus());
58 } 52 }
59 53
60 std::memcpy(data + SHARED_MEMORY_OFFSET, &shared_memory, sizeof(SharedMemory)); 54 mouse_lifo.WriteNextEntry(next_state);
55 std::memcpy(data + SHARED_MEMORY_OFFSET, &mouse_lifo, sizeof(mouse_lifo));
61} 56}
62 57
63void Controller_Mouse::OnLoadInputDevices() {
64 mouse_device = Input::CreateDevice<Input::MouseDevice>(Settings::values.mouse_device);
65 std::transform(Settings::values.mouse_buttons.begin(), Settings::values.mouse_buttons.end(),
66 mouse_button_devices.begin(), Input::CreateDevice<Input::ButtonDevice>);
67}
68} // namespace Service::HID 58} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/mouse.h b/src/core/hle/service/hid/controllers/mouse.h
index 3d391a798..7559fc78d 100644
--- a/src/core/hle/service/hid/controllers/mouse.h
+++ b/src/core/hle/service/hid/controllers/mouse.h
@@ -7,15 +7,20 @@
7#include <array> 7#include <array>
8#include "common/bit_field.h" 8#include "common/bit_field.h"
9#include "common/common_types.h" 9#include "common/common_types.h"
10#include "common/settings.h"
11#include "common/swap.h" 10#include "common/swap.h"
12#include "core/frontend/input.h"
13#include "core/hle/service/hid/controllers/controller_base.h" 11#include "core/hle/service/hid/controllers/controller_base.h"
12#include "core/hle/service/hid/ring_lifo.h"
13
14namespace Core::HID {
15class EmulatedDevices;
16struct MouseState;
17struct AnalogStickState;
18} // namespace Core::HID
14 19
15namespace Service::HID { 20namespace Service::HID {
16class Controller_Mouse final : public ControllerBase { 21class Controller_Mouse final : public ControllerBase {
17public: 22public:
18 explicit Controller_Mouse(Core::System& system_); 23 explicit Controller_Mouse(Core::HID::HIDCore& hid_core_);
19 ~Controller_Mouse() override; 24 ~Controller_Mouse() override;
20 25
21 // Called when the controller is initialized 26 // Called when the controller is initialized
@@ -27,53 +32,13 @@ public:
27 // When the controller is requesting an update for the shared memory 32 // When the controller is requesting an update for the shared memory
28 void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t size) override; 33 void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t size) override;
29 34
30 // Called when input devices should be loaded
31 void OnLoadInputDevices() override;
32
33private: 35private:
34 struct Buttons { 36 // This is nn::hid::detail::MouseLifo
35 union { 37 Lifo<Core::HID::MouseState, hid_entry_count> mouse_lifo{};
36 u32_le raw{}; 38 static_assert(sizeof(mouse_lifo) == 0x350, "mouse_lifo is an invalid size");
37 BitField<0, 1, u32> left; 39 Core::HID::MouseState next_state{};
38 BitField<1, 1, u32> right;
39 BitField<2, 1, u32> middle;
40 BitField<3, 1, u32> forward;
41 BitField<4, 1, u32> back;
42 };
43 };
44 static_assert(sizeof(Buttons) == 0x4, "Buttons is an invalid size");
45
46 struct Attributes {
47 union {
48 u32_le raw{};
49 BitField<0, 1, u32> transferable;
50 BitField<1, 1, u32> is_connected;
51 };
52 };
53 static_assert(sizeof(Attributes) == 0x4, "Attributes is an invalid size");
54
55 struct MouseState {
56 s64_le sampling_number;
57 s64_le sampling_number2;
58 s32_le x;
59 s32_le y;
60 s32_le delta_x;
61 s32_le delta_y;
62 s32_le mouse_wheel_x;
63 s32_le mouse_wheel_y;
64 Buttons button;
65 Attributes attribute;
66 };
67 static_assert(sizeof(MouseState) == 0x30, "MouseState is an invalid size");
68
69 struct SharedMemory {
70 CommonHeader header;
71 std::array<MouseState, 17> mouse_states;
72 };
73 SharedMemory shared_memory{};
74 40
75 std::unique_ptr<Input::MouseDevice> mouse_device; 41 Core::HID::AnalogStickState last_mouse_wheel_state;
76 std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeMouseButton::NumMouseButtons> 42 Core::HID::EmulatedDevices* emulated_devices;
77 mouse_button_devices;
78}; 43};
79} // namespace Service::HID 44} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp
index 196876810..2705e9dcb 100644
--- a/src/core/hle/service/hid/controllers/npad.cpp
+++ b/src/core/hle/service/hid/controllers/npad.cpp
@@ -10,9 +10,9 @@
10#include "common/common_types.h" 10#include "common/common_types.h"
11#include "common/logging/log.h" 11#include "common/logging/log.h"
12#include "common/settings.h" 12#include "common/settings.h"
13#include "core/core.h"
14#include "core/core_timing.h" 13#include "core/core_timing.h"
15#include "core/frontend/input.h" 14#include "core/hid/emulated_controller.h"
15#include "core/hid/hid_core.h"
16#include "core/hle/kernel/k_event.h" 16#include "core/hle/kernel/k_event.h"
17#include "core/hle/kernel/k_readable_event.h" 17#include "core/hle/kernel/k_readable_event.h"
18#include "core/hle/kernel/k_writable_event.h" 18#include "core/hle/kernel/k_writable_event.h"
@@ -20,120 +20,26 @@
20#include "core/hle/service/kernel_helpers.h" 20#include "core/hle/service/kernel_helpers.h"
21 21
22namespace Service::HID { 22namespace Service::HID {
23constexpr s32 HID_JOYSTICK_MAX = 0x7fff;
24constexpr s32 HID_TRIGGER_MAX = 0x7fff;
25[[maybe_unused]] constexpr s32 HID_JOYSTICK_MIN = -0x7fff;
26constexpr std::size_t NPAD_OFFSET = 0x9A00; 23constexpr std::size_t NPAD_OFFSET = 0x9A00;
27constexpr u32 BATTERY_FULL = 2; 24constexpr std::array<Core::HID::NpadIdType, 10> npad_id_list{
28constexpr u32 MAX_NPAD_ID = 7; 25 Core::HID::NpadIdType::Player1, Core::HID::NpadIdType::Player2, Core::HID::NpadIdType::Player3,
29constexpr std::size_t HANDHELD_INDEX = 8; 26 Core::HID::NpadIdType::Player4, Core::HID::NpadIdType::Player5, Core::HID::NpadIdType::Player6,
30constexpr std::array<u32, 10> npad_id_list{ 27 Core::HID::NpadIdType::Player7, Core::HID::NpadIdType::Player8, Core::HID::NpadIdType::Other,
31 0, 1, 2, 3, 4, 5, 6, 7, NPAD_HANDHELD, NPAD_UNKNOWN, 28 Core::HID::NpadIdType::Handheld,
32}; 29};
33 30
34enum class JoystickId : std::size_t { 31bool Controller_NPad::IsNpadIdValid(Core::HID::NpadIdType npad_id) {
35 Joystick_Left,
36 Joystick_Right,
37};
38
39Controller_NPad::NPadControllerType Controller_NPad::MapSettingsTypeToNPad(
40 Settings::ControllerType type) {
41 switch (type) {
42 case Settings::ControllerType::ProController:
43 return NPadControllerType::ProController;
44 case Settings::ControllerType::DualJoyconDetached:
45 return NPadControllerType::JoyDual;
46 case Settings::ControllerType::LeftJoycon:
47 return NPadControllerType::JoyLeft;
48 case Settings::ControllerType::RightJoycon:
49 return NPadControllerType::JoyRight;
50 case Settings::ControllerType::Handheld:
51 return NPadControllerType::Handheld;
52 case Settings::ControllerType::GameCube:
53 return NPadControllerType::GameCube;
54 default:
55 UNREACHABLE();
56 return NPadControllerType::ProController;
57 }
58}
59
60Settings::ControllerType Controller_NPad::MapNPadToSettingsType(
61 Controller_NPad::NPadControllerType type) {
62 switch (type) {
63 case NPadControllerType::ProController:
64 return Settings::ControllerType::ProController;
65 case NPadControllerType::JoyDual:
66 return Settings::ControllerType::DualJoyconDetached;
67 case NPadControllerType::JoyLeft:
68 return Settings::ControllerType::LeftJoycon;
69 case NPadControllerType::JoyRight:
70 return Settings::ControllerType::RightJoycon;
71 case NPadControllerType::Handheld:
72 return Settings::ControllerType::Handheld;
73 case NPadControllerType::GameCube:
74 return Settings::ControllerType::GameCube;
75 default:
76 UNREACHABLE();
77 return Settings::ControllerType::ProController;
78 }
79}
80
81std::size_t Controller_NPad::NPadIdToIndex(u32 npad_id) {
82 switch (npad_id) { 32 switch (npad_id) {
83 case 0: 33 case Core::HID::NpadIdType::Player1:
84 case 1: 34 case Core::HID::NpadIdType::Player2:
85 case 2: 35 case Core::HID::NpadIdType::Player3:
86 case 3: 36 case Core::HID::NpadIdType::Player4:
87 case 4: 37 case Core::HID::NpadIdType::Player5:
88 case 5: 38 case Core::HID::NpadIdType::Player6:
89 case 6: 39 case Core::HID::NpadIdType::Player7:
90 case 7: 40 case Core::HID::NpadIdType::Player8:
91 return npad_id; 41 case Core::HID::NpadIdType::Other:
92 case HANDHELD_INDEX: 42 case Core::HID::NpadIdType::Handheld:
93 case NPAD_HANDHELD:
94 return HANDHELD_INDEX;
95 case 9:
96 case NPAD_UNKNOWN:
97 return 9;
98 default:
99 UNIMPLEMENTED_MSG("Unknown npad id {}", npad_id);
100 return 0;
101 }
102}
103
104u32 Controller_NPad::IndexToNPad(std::size_t index) {
105 switch (index) {
106 case 0:
107 case 1:
108 case 2:
109 case 3:
110 case 4:
111 case 5:
112 case 6:
113 case 7:
114 return static_cast<u32>(index);
115 case HANDHELD_INDEX:
116 return NPAD_HANDHELD;
117 case 9:
118 return NPAD_UNKNOWN;
119 default:
120 UNIMPLEMENTED_MSG("Unknown npad index {}", index);
121 return 0;
122 }
123}
124
125bool Controller_NPad::IsNpadIdValid(u32 npad_id) {
126 switch (npad_id) {
127 case 0:
128 case 1:
129 case 2:
130 case 3:
131 case 4:
132 case 5:
133 case 6:
134 case 7:
135 case NPAD_UNKNOWN:
136 case NPAD_HANDHELD:
137 return true; 43 return true;
138 default: 44 default:
139 LOG_ERROR(Service_HID, "Invalid npad id {}", npad_id); 45 LOG_ERROR(Service_HID, "Invalid npad id {}", npad_id);
@@ -141,305 +47,337 @@ bool Controller_NPad::IsNpadIdValid(u32 npad_id) {
141 } 47 }
142} 48}
143 49
144bool Controller_NPad::IsDeviceHandleValid(const DeviceHandle& device_handle) { 50bool Controller_NPad::IsDeviceHandleValid(const Core::HID::VibrationDeviceHandle& device_handle) {
145 return IsNpadIdValid(device_handle.npad_id) && 51 return IsNpadIdValid(static_cast<Core::HID::NpadIdType>(device_handle.npad_id)) &&
146 device_handle.npad_type < NpadType::MaxNpadType && 52 device_handle.npad_type < Core::HID::NpadStyleIndex::MaxNpadType &&
147 device_handle.device_index < DeviceIndex::MaxDeviceIndex; 53 device_handle.device_index < Core::HID::DeviceIndex::MaxDeviceIndex;
148} 54}
149 55
150Controller_NPad::Controller_NPad(Core::System& system_, 56bool Controller_NPad::IsDeviceHandleValid(const Core::HID::SixAxisSensorHandle& device_handle) {
57 return IsNpadIdValid(static_cast<Core::HID::NpadIdType>(device_handle.npad_id)) &&
58 device_handle.npad_type < Core::HID::NpadStyleIndex::MaxNpadType &&
59 device_handle.device_index < Core::HID::DeviceIndex::MaxDeviceIndex;
60}
61
62Controller_NPad::Controller_NPad(Core::HID::HIDCore& hid_core_,
151 KernelHelpers::ServiceContext& service_context_) 63 KernelHelpers::ServiceContext& service_context_)
152 : ControllerBase{system_}, service_context{service_context_} { 64 : ControllerBase{hid_core_}, service_context{service_context_} {
153 latest_vibration_values.fill({DEFAULT_VIBRATION_VALUE, DEFAULT_VIBRATION_VALUE}); 65 for (std::size_t i = 0; i < controller_data.size(); ++i) {
66 auto& controller = controller_data[i];
67 controller.device = hid_core.GetEmulatedControllerByIndex(i);
68 controller.vibration[Core::HID::EmulatedDeviceIndex::LeftIndex].latest_vibration_value =
69 DEFAULT_VIBRATION_VALUE;
70 controller.vibration[Core::HID::EmulatedDeviceIndex::RightIndex].latest_vibration_value =
71 DEFAULT_VIBRATION_VALUE;
72 Core::HID::ControllerUpdateCallback engine_callback{
73 .on_change = [this,
74 i](Core::HID::ControllerTriggerType type) { ControllerUpdate(type, i); },
75 .is_npad_service = true,
76 };
77 controller.callback_key = controller.device->SetCallback(engine_callback);
78 }
154} 79}
155 80
156Controller_NPad::~Controller_NPad() { 81Controller_NPad::~Controller_NPad() {
82 for (std::size_t i = 0; i < controller_data.size(); ++i) {
83 auto& controller = controller_data[i];
84 controller.device->DeleteCallback(controller.callback_key);
85 }
157 OnRelease(); 86 OnRelease();
158} 87}
159 88
160void Controller_NPad::InitNewlyAddedController(std::size_t controller_idx) { 89void Controller_NPad::ControllerUpdate(Core::HID::ControllerTriggerType type,
161 const auto controller_type = connected_controllers[controller_idx].type; 90 std::size_t controller_idx) {
162 auto& controller = shared_memory_entries[controller_idx]; 91 if (type == Core::HID::ControllerTriggerType::All) {
163 if (controller_type == NPadControllerType::None) { 92 ControllerUpdate(Core::HID::ControllerTriggerType::Connected, controller_idx);
164 styleset_changed_events[controller_idx]->GetWritableEvent().Signal(); 93 ControllerUpdate(Core::HID::ControllerTriggerType::Battery, controller_idx);
94 return;
95 }
96 if (controller_idx >= controller_data.size()) {
97 return;
98 }
99
100 auto& controller = controller_data[controller_idx];
101 const auto is_connected = controller.device->IsConnected();
102 const auto npad_type = controller.device->GetNpadStyleIndex();
103 const auto npad_id = controller.device->GetNpadIdType();
104 switch (type) {
105 case Core::HID::ControllerTriggerType::Connected:
106 case Core::HID::ControllerTriggerType::Disconnected:
107 if (is_connected == controller.is_connected) {
108 return;
109 }
110 UpdateControllerAt(npad_type, npad_id, is_connected);
111 break;
112 case Core::HID::ControllerTriggerType::Battery: {
113 if (!controller.device->IsConnected()) {
114 return;
115 }
116 auto& shared_memory = controller.shared_memory_entry;
117 const auto& battery_level = controller.device->GetBattery();
118 shared_memory.battery_level_dual = battery_level.dual.battery_level;
119 shared_memory.battery_level_left = battery_level.left.battery_level;
120 shared_memory.battery_level_right = battery_level.right.battery_level;
121 break;
122 }
123 default:
124 break;
125 }
126}
127
128void Controller_NPad::InitNewlyAddedController(Core::HID::NpadIdType npad_id) {
129 auto& controller = GetControllerFromNpadIdType(npad_id);
130 if (!IsControllerSupported(controller.device->GetNpadStyleIndex())) {
165 return; 131 return;
166 } 132 }
167 controller.style_set.raw = 0; // Zero out 133 LOG_DEBUG(Service_HID, "Npad connected {}", npad_id);
168 controller.device_type.raw = 0; 134 const auto controller_type = controller.device->GetNpadStyleIndex();
169 controller.system_properties.raw = 0; 135 auto& shared_memory = controller.shared_memory_entry;
136 if (controller_type == Core::HID::NpadStyleIndex::None) {
137 controller.styleset_changed_event->GetWritableEvent().Signal();
138 return;
139 }
140 shared_memory.style_tag.raw = Core::HID::NpadStyleSet::None;
141 shared_memory.device_type.raw = 0;
142 shared_memory.system_properties.raw = 0;
170 switch (controller_type) { 143 switch (controller_type) {
171 case NPadControllerType::None: 144 case Core::HID::NpadStyleIndex::None:
172 UNREACHABLE(); 145 UNREACHABLE();
173 break; 146 break;
174 case NPadControllerType::ProController: 147 case Core::HID::NpadStyleIndex::ProController:
175 controller.style_set.fullkey.Assign(1); 148 shared_memory.style_tag.fullkey.Assign(1);
176 controller.device_type.fullkey.Assign(1); 149 shared_memory.device_type.fullkey.Assign(1);
177 controller.system_properties.is_vertical.Assign(1); 150 shared_memory.system_properties.is_vertical.Assign(1);
178 controller.system_properties.use_plus.Assign(1); 151 shared_memory.system_properties.use_plus.Assign(1);
179 controller.system_properties.use_minus.Assign(1); 152 shared_memory.system_properties.use_minus.Assign(1);
180 controller.assignment_mode = NpadAssignments::Single; 153 shared_memory.applet_footer.type = AppletFooterUiType::SwitchProController;
181 controller.footer_type = AppletFooterUiType::SwitchProController;
182 break; 154 break;
183 case NPadControllerType::Handheld: 155 case Core::HID::NpadStyleIndex::Handheld:
184 controller.style_set.handheld.Assign(1); 156 shared_memory.style_tag.handheld.Assign(1);
185 controller.device_type.handheld_left.Assign(1); 157 shared_memory.device_type.handheld_left.Assign(1);
186 controller.device_type.handheld_right.Assign(1); 158 shared_memory.device_type.handheld_right.Assign(1);
187 controller.system_properties.is_vertical.Assign(1); 159 shared_memory.system_properties.is_vertical.Assign(1);
188 controller.system_properties.use_plus.Assign(1); 160 shared_memory.system_properties.use_plus.Assign(1);
189 controller.system_properties.use_minus.Assign(1); 161 shared_memory.system_properties.use_minus.Assign(1);
190 controller.assignment_mode = NpadAssignments::Dual; 162 shared_memory.system_properties.use_directional_buttons.Assign(1);
191 controller.footer_type = AppletFooterUiType::HandheldJoyConLeftJoyConRight; 163 shared_memory.assignment_mode = NpadJoyAssignmentMode::Dual;
164 shared_memory.applet_footer.type = AppletFooterUiType::HandheldJoyConLeftJoyConRight;
165 break;
166 case Core::HID::NpadStyleIndex::JoyconDual:
167 shared_memory.style_tag.joycon_dual.Assign(1);
168 if (controller.is_dual_left_connected) {
169 shared_memory.device_type.joycon_left.Assign(1);
170 shared_memory.system_properties.use_minus.Assign(1);
171 }
172 if (controller.is_dual_right_connected) {
173 shared_memory.device_type.joycon_right.Assign(1);
174 shared_memory.system_properties.use_plus.Assign(1);
175 }
176 shared_memory.system_properties.use_directional_buttons.Assign(1);
177 shared_memory.system_properties.is_vertical.Assign(1);
178 shared_memory.assignment_mode = NpadJoyAssignmentMode::Dual;
179 if (controller.is_dual_left_connected && controller.is_dual_right_connected) {
180 shared_memory.applet_footer.type = AppletFooterUiType::JoyDual;
181 } else if (controller.is_dual_left_connected) {
182 shared_memory.applet_footer.type = AppletFooterUiType::JoyDualLeftOnly;
183 } else {
184 shared_memory.applet_footer.type = AppletFooterUiType::JoyDualRightOnly;
185 }
192 break; 186 break;
193 case NPadControllerType::JoyDual: 187 case Core::HID::NpadStyleIndex::JoyconLeft:
194 controller.style_set.joycon_dual.Assign(1); 188 shared_memory.style_tag.joycon_left.Assign(1);
195 controller.device_type.joycon_left.Assign(1); 189 shared_memory.device_type.joycon_left.Assign(1);
196 controller.device_type.joycon_right.Assign(1); 190 shared_memory.system_properties.is_horizontal.Assign(1);
197 controller.system_properties.is_vertical.Assign(1); 191 shared_memory.system_properties.use_minus.Assign(1);
198 controller.system_properties.use_plus.Assign(1); 192 shared_memory.applet_footer.type = AppletFooterUiType::JoyLeftHorizontal;
199 controller.system_properties.use_minus.Assign(1);
200 controller.assignment_mode = NpadAssignments::Dual;
201 controller.footer_type = AppletFooterUiType::JoyDual;
202 break; 193 break;
203 case NPadControllerType::JoyLeft: 194 case Core::HID::NpadStyleIndex::JoyconRight:
204 controller.style_set.joycon_left.Assign(1); 195 shared_memory.style_tag.joycon_right.Assign(1);
205 controller.device_type.joycon_left.Assign(1); 196 shared_memory.device_type.joycon_right.Assign(1);
206 controller.system_properties.is_horizontal.Assign(1); 197 shared_memory.system_properties.is_horizontal.Assign(1);
207 controller.system_properties.use_minus.Assign(1); 198 shared_memory.system_properties.use_plus.Assign(1);
208 controller.assignment_mode = NpadAssignments::Single; 199 shared_memory.applet_footer.type = AppletFooterUiType::JoyRightHorizontal;
209 controller.footer_type = AppletFooterUiType::JoyLeftHorizontal;
210 break; 200 break;
211 case NPadControllerType::JoyRight: 201 case Core::HID::NpadStyleIndex::GameCube:
212 controller.style_set.joycon_right.Assign(1); 202 shared_memory.style_tag.gamecube.Assign(1);
213 controller.device_type.joycon_right.Assign(1); 203 shared_memory.device_type.fullkey.Assign(1);
214 controller.system_properties.is_horizontal.Assign(1); 204 shared_memory.system_properties.is_vertical.Assign(1);
215 controller.system_properties.use_plus.Assign(1); 205 shared_memory.system_properties.use_plus.Assign(1);
216 controller.assignment_mode = NpadAssignments::Single;
217 controller.footer_type = AppletFooterUiType::JoyRightHorizontal;
218 break; 206 break;
219 case NPadControllerType::GameCube: 207 case Core::HID::NpadStyleIndex::Pokeball:
220 controller.style_set.gamecube.Assign(1); 208 shared_memory.style_tag.palma.Assign(1);
221 // The GC Controller behaves like a wired Pro Controller 209 shared_memory.device_type.palma.Assign(1);
222 controller.device_type.fullkey.Assign(1);
223 controller.system_properties.is_vertical.Assign(1);
224 controller.system_properties.use_plus.Assign(1);
225 break; 210 break;
226 case NPadControllerType::Pokeball: 211 case Core::HID::NpadStyleIndex::NES:
227 controller.style_set.palma.Assign(1); 212 shared_memory.style_tag.lark.Assign(1);
228 controller.device_type.palma.Assign(1); 213 shared_memory.device_type.fullkey.Assign(1);
229 controller.assignment_mode = NpadAssignments::Single; 214 break;
215 case Core::HID::NpadStyleIndex::SNES:
216 shared_memory.style_tag.lucia.Assign(1);
217 shared_memory.device_type.fullkey.Assign(1);
218 shared_memory.applet_footer.type = AppletFooterUiType::Lucia;
219 break;
220 case Core::HID::NpadStyleIndex::N64:
221 shared_memory.style_tag.lagoon.Assign(1);
222 shared_memory.device_type.fullkey.Assign(1);
223 shared_memory.applet_footer.type = AppletFooterUiType::Lagon;
224 break;
225 case Core::HID::NpadStyleIndex::SegaGenesis:
226 shared_memory.style_tag.lager.Assign(1);
227 shared_memory.device_type.fullkey.Assign(1);
228 break;
229 default:
230 break; 230 break;
231 } 231 }
232 232
233 controller.fullkey_color.attribute = ColorAttributes::Ok; 233 const auto& body_colors = controller.device->GetColors();
234 controller.fullkey_color.fullkey.body = 0;
235 controller.fullkey_color.fullkey.button = 0;
236 234
237 controller.joycon_color.attribute = ColorAttributes::Ok; 235 shared_memory.fullkey_color.attribute = ColorAttribute::Ok;
238 controller.joycon_color.left.body = 236 shared_memory.fullkey_color.fullkey = body_colors.fullkey;
239 Settings::values.players.GetValue()[controller_idx].body_color_left;
240 controller.joycon_color.left.button =
241 Settings::values.players.GetValue()[controller_idx].button_color_left;
242 controller.joycon_color.right.body =
243 Settings::values.players.GetValue()[controller_idx].body_color_right;
244 controller.joycon_color.right.button =
245 Settings::values.players.GetValue()[controller_idx].button_color_right;
246 237
247 // TODO: Investigate when we should report all batery types 238 shared_memory.joycon_color.attribute = ColorAttribute::Ok;
248 controller.battery_level_dual = BATTERY_FULL; 239 shared_memory.joycon_color.left = body_colors.left;
249 controller.battery_level_left = BATTERY_FULL; 240 shared_memory.joycon_color.right = body_colors.right;
250 controller.battery_level_right = BATTERY_FULL;
251 241
252 SignalStyleSetChangedEvent(IndexToNPad(controller_idx)); 242 // TODO: Investigate when we should report all batery types
243 const auto& battery_level = controller.device->GetBattery();
244 shared_memory.battery_level_dual = battery_level.dual.battery_level;
245 shared_memory.battery_level_left = battery_level.left.battery_level;
246 shared_memory.battery_level_right = battery_level.right.battery_level;
247
248 controller.is_connected = true;
249 controller.device->Connect();
250 SignalStyleSetChangedEvent(npad_id);
251 WriteEmptyEntry(controller.shared_memory_entry);
253} 252}
254 253
255void Controller_NPad::OnInit() { 254void Controller_NPad::OnInit() {
256 for (std::size_t i = 0; i < styleset_changed_events.size(); ++i) {
257 styleset_changed_events[i] =
258 service_context.CreateEvent(fmt::format("npad:NpadStyleSetChanged_{}", i));
259 }
260
261 if (!IsControllerActivated()) { 255 if (!IsControllerActivated()) {
262 return; 256 return;
263 } 257 }
264 258
265 OnLoadInputDevices(); 259 for (std::size_t i = 0; i < controller_data.size(); ++i) {
266 260 auto& controller = controller_data[i];
267 if (style.raw == 0) { 261 controller.styleset_changed_event =
268 // We want to support all controllers 262 service_context.CreateEvent(fmt::format("npad:NpadStyleSetChanged_{}", i));
269 style.handheld.Assign(1);
270 style.joycon_left.Assign(1);
271 style.joycon_right.Assign(1);
272 style.joycon_dual.Assign(1);
273 style.fullkey.Assign(1);
274 style.gamecube.Assign(1);
275 style.palma.Assign(1);
276 }
277
278 std::transform(Settings::values.players.GetValue().begin(),
279 Settings::values.players.GetValue().end(), connected_controllers.begin(),
280 [](const Settings::PlayerInput& player) {
281 return ControllerHolder{MapSettingsTypeToNPad(player.controller_type),
282 player.connected};
283 });
284
285 // Connect the Player 1 or Handheld controller if none are connected.
286 if (std::none_of(connected_controllers.begin(), connected_controllers.end(),
287 [](const ControllerHolder& controller) { return controller.is_connected; })) {
288 const auto controller =
289 MapSettingsTypeToNPad(Settings::values.players.GetValue()[0].controller_type);
290 if (controller == NPadControllerType::Handheld) {
291 Settings::values.players.GetValue()[HANDHELD_INDEX].connected = true;
292 connected_controllers[HANDHELD_INDEX] = {controller, true};
293 } else {
294 Settings::values.players.GetValue()[0].connected = true;
295 connected_controllers[0] = {controller, true};
296 }
297 } 263 }
298 264
299 // Account for handheld 265 if (hid_core.GetSupportedStyleTag().raw == Core::HID::NpadStyleSet::None) {
300 if (connected_controllers[HANDHELD_INDEX].is_connected) { 266 // We want to support all controllers
301 connected_controllers[HANDHELD_INDEX].type = NPadControllerType::Handheld; 267 hid_core.SetSupportedStyleTag({Core::HID::NpadStyleSet::All});
302 } 268 }
303 269
304 supported_npad_id_types.resize(npad_id_list.size()); 270 supported_npad_id_types.resize(npad_id_list.size());
305 std::memcpy(supported_npad_id_types.data(), npad_id_list.data(), 271 std::memcpy(supported_npad_id_types.data(), npad_id_list.data(),
306 npad_id_list.size() * sizeof(u32)); 272 npad_id_list.size() * sizeof(Core::HID::NpadIdType));
273
274 // Prefill controller buffers
275 for (auto& controller : controller_data) {
276 auto& npad = controller.shared_memory_entry;
277 npad.fullkey_color = {
278 .attribute = ColorAttribute::NoController,
279 .fullkey = {},
280 };
281 npad.joycon_color = {
282 .attribute = ColorAttribute::NoController,
283 .left = {},
284 .right = {},
285 };
286 // HW seems to initialize the first 19 entries
287 for (std::size_t i = 0; i < 19; ++i) {
288 WriteEmptyEntry(npad);
289 }
290 }
307 291
308 for (std::size_t i = 0; i < connected_controllers.size(); ++i) { 292 // Connect controllers
309 const auto& controller = connected_controllers[i]; 293 for (auto& controller : controller_data) {
310 if (controller.is_connected) { 294 const auto& device = controller.device;
311 AddNewControllerAt(controller.type, i); 295 if (device->IsConnected()) {
296 AddNewControllerAt(device->GetNpadStyleIndex(), device->GetNpadIdType());
312 } 297 }
313 } 298 }
314} 299}
315 300
316void Controller_NPad::OnLoadInputDevices() { 301void Controller_NPad::WriteEmptyEntry(NpadInternalState& npad) {
317 const auto& players = Settings::values.players.GetValue(); 302 NPadGenericState dummy_pad_state{};
303 NpadGcTriggerState dummy_gc_state{};
304 dummy_pad_state.sampling_number = npad.fullkey_lifo.ReadCurrentEntry().sampling_number + 1;
305 npad.fullkey_lifo.WriteNextEntry(dummy_pad_state);
306 dummy_pad_state.sampling_number = npad.handheld_lifo.ReadCurrentEntry().sampling_number + 1;
307 npad.handheld_lifo.WriteNextEntry(dummy_pad_state);
308 dummy_pad_state.sampling_number = npad.joy_dual_lifo.ReadCurrentEntry().sampling_number + 1;
309 npad.joy_dual_lifo.WriteNextEntry(dummy_pad_state);
310 dummy_pad_state.sampling_number = npad.joy_left_lifo.ReadCurrentEntry().sampling_number + 1;
311 npad.joy_left_lifo.WriteNextEntry(dummy_pad_state);
312 dummy_pad_state.sampling_number = npad.joy_right_lifo.ReadCurrentEntry().sampling_number + 1;
313 npad.joy_right_lifo.WriteNextEntry(dummy_pad_state);
314 dummy_pad_state.sampling_number = npad.palma_lifo.ReadCurrentEntry().sampling_number + 1;
315 npad.palma_lifo.WriteNextEntry(dummy_pad_state);
316 dummy_pad_state.sampling_number = npad.system_ext_lifo.ReadCurrentEntry().sampling_number + 1;
317 npad.system_ext_lifo.WriteNextEntry(dummy_pad_state);
318 dummy_gc_state.sampling_number = npad.gc_trigger_lifo.ReadCurrentEntry().sampling_number + 1;
319 npad.gc_trigger_lifo.WriteNextEntry(dummy_gc_state);
320}
318 321
319 std::lock_guard lock{mutex}; 322void Controller_NPad::OnRelease() {
320 for (std::size_t i = 0; i < players.size(); ++i) { 323 for (std::size_t i = 0; i < controller_data.size(); ++i) {
321 std::transform(players[i].buttons.begin() + Settings::NativeButton::BUTTON_HID_BEGIN, 324 auto& controller = controller_data[i];
322 players[i].buttons.begin() + Settings::NativeButton::BUTTON_HID_END, 325 service_context.CloseEvent(controller.styleset_changed_event);
323 buttons[i].begin(), Input::CreateDevice<Input::ButtonDevice>); 326 for (std::size_t device_idx = 0; device_idx < controller.vibration.size(); ++device_idx) {
324 std::transform(players[i].analogs.begin() + Settings::NativeAnalog::STICK_HID_BEGIN, 327 VibrateControllerAtIndex(controller.device->GetNpadIdType(), device_idx, {});
325 players[i].analogs.begin() + Settings::NativeAnalog::STICK_HID_END,
326 sticks[i].begin(), Input::CreateDevice<Input::AnalogDevice>);
327 std::transform(players[i].vibrations.begin() +
328 Settings::NativeVibration::VIBRATION_HID_BEGIN,
329 players[i].vibrations.begin() + Settings::NativeVibration::VIBRATION_HID_END,
330 vibrations[i].begin(), Input::CreateDevice<Input::VibrationDevice>);
331 std::transform(players[i].motions.begin() + Settings::NativeMotion::MOTION_HID_BEGIN,
332 players[i].motions.begin() + Settings::NativeMotion::MOTION_HID_END,
333 motions[i].begin(), Input::CreateDevice<Input::MotionDevice>);
334 for (std::size_t device_idx = 0; device_idx < vibrations[i].size(); ++device_idx) {
335 InitializeVibrationDeviceAtIndex(i, device_idx);
336 } 328 }
337 } 329 }
338} 330}
339 331
340void Controller_NPad::OnRelease() { 332void Controller_NPad::RequestPadStateUpdate(Core::HID::NpadIdType npad_id) {
341 for (std::size_t npad_idx = 0; npad_idx < vibrations.size(); ++npad_idx) { 333 std::lock_guard lock{mutex};
342 for (std::size_t device_idx = 0; device_idx < vibrations[npad_idx].size(); ++device_idx) { 334 auto& controller = GetControllerFromNpadIdType(npad_id);
343 VibrateControllerAtIndex(npad_idx, device_idx, {}); 335 const auto controller_type = controller.device->GetNpadStyleIndex();
344 } 336 if (!controller.device->IsConnected()) {
337 return;
345 } 338 }
346 339
347 for (std::size_t i = 0; i < styleset_changed_events.size(); ++i) { 340 auto& pad_entry = controller.npad_pad_state;
348 service_context.CloseEvent(styleset_changed_events[i]); 341 auto& trigger_entry = controller.npad_trigger_state;
342 const auto button_state = controller.device->GetNpadButtons();
343 const auto stick_state = controller.device->GetSticks();
344
345 using btn = Core::HID::NpadButton;
346 pad_entry.npad_buttons.raw = btn::None;
347 if (controller_type != Core::HID::NpadStyleIndex::JoyconLeft) {
348 constexpr btn right_button_mask = btn::A | btn::B | btn::X | btn::Y | btn::StickR | btn::R |
349 btn::ZR | btn::Plus | btn::StickRLeft | btn::StickRUp |
350 btn::StickRRight | btn::StickRDown;
351 pad_entry.npad_buttons.raw = button_state.raw & right_button_mask;
352 pad_entry.r_stick = stick_state.right;
349 } 353 }
350}
351 354
352void Controller_NPad::RequestPadStateUpdate(u32 npad_id) { 355 if (controller_type != Core::HID::NpadStyleIndex::JoyconRight) {
353 std::lock_guard lock{mutex}; 356 constexpr btn left_button_mask =
357 btn::Left | btn::Up | btn::Right | btn::Down | btn::StickL | btn::L | btn::ZL |
358 btn::Minus | btn::StickLLeft | btn::StickLUp | btn::StickLRight | btn::StickLDown;
359 pad_entry.npad_buttons.raw |= button_state.raw & left_button_mask;
360 pad_entry.l_stick = stick_state.left;
361 }
354 362
355 const auto controller_idx = NPadIdToIndex(npad_id); 363 if (controller_type == Core::HID::NpadStyleIndex::JoyconLeft) {
356 const auto controller_type = connected_controllers[controller_idx].type; 364 pad_entry.npad_buttons.left_sl.Assign(button_state.left_sl);
357 if (!connected_controllers[controller_idx].is_connected) { 365 pad_entry.npad_buttons.left_sr.Assign(button_state.left_sr);
358 return;
359 } 366 }
360 auto& pad_state = npad_pad_states[controller_idx].pad_states; 367
361 auto& lstick_entry = npad_pad_states[controller_idx].l_stick; 368 if (controller_type == Core::HID::NpadStyleIndex::JoyconRight) {
362 auto& rstick_entry = npad_pad_states[controller_idx].r_stick; 369 pad_entry.npad_buttons.right_sl.Assign(button_state.right_sl);
363 auto& trigger_entry = npad_trigger_states[controller_idx]; 370 pad_entry.npad_buttons.right_sr.Assign(button_state.right_sr);
364 const auto& button_state = buttons[controller_idx]; 371 }
365 const auto& analog_state = sticks[controller_idx]; 372
366 const auto [stick_l_x_f, stick_l_y_f] = 373 if (controller_type == Core::HID::NpadStyleIndex::GameCube) {
367 analog_state[static_cast<std::size_t>(JoystickId::Joystick_Left)]->GetStatus(); 374 const auto& trigger_state = controller.device->GetTriggers();
368 const auto [stick_r_x_f, stick_r_y_f] = 375 trigger_entry.l_analog = trigger_state.left;
369 analog_state[static_cast<std::size_t>(JoystickId::Joystick_Right)]->GetStatus(); 376 trigger_entry.r_analog = trigger_state.right;
370 377 pad_entry.npad_buttons.zl.Assign(false);
371 using namespace Settings::NativeButton; 378 pad_entry.npad_buttons.zr.Assign(button_state.r);
372 if (controller_type != NPadControllerType::JoyLeft) { 379 pad_entry.npad_buttons.l.Assign(button_state.zl);
373 pad_state.a.Assign(button_state[A - BUTTON_HID_BEGIN]->GetStatus()); 380 pad_entry.npad_buttons.r.Assign(button_state.zr);
374 pad_state.b.Assign(button_state[B - BUTTON_HID_BEGIN]->GetStatus());
375 pad_state.x.Assign(button_state[X - BUTTON_HID_BEGIN]->GetStatus());
376 pad_state.y.Assign(button_state[Y - BUTTON_HID_BEGIN]->GetStatus());
377 pad_state.r_stick.Assign(button_state[RStick - BUTTON_HID_BEGIN]->GetStatus());
378 pad_state.r.Assign(button_state[R - BUTTON_HID_BEGIN]->GetStatus());
379 pad_state.zr.Assign(button_state[ZR - BUTTON_HID_BEGIN]->GetStatus());
380 pad_state.plus.Assign(button_state[Plus - BUTTON_HID_BEGIN]->GetStatus());
381
382 pad_state.r_stick_right.Assign(
383 analog_state[static_cast<std::size_t>(JoystickId::Joystick_Right)]
384 ->GetAnalogDirectionStatus(Input::AnalogDirection::RIGHT));
385 pad_state.r_stick_left.Assign(
386 analog_state[static_cast<std::size_t>(JoystickId::Joystick_Right)]
387 ->GetAnalogDirectionStatus(Input::AnalogDirection::LEFT));
388 pad_state.r_stick_up.Assign(
389 analog_state[static_cast<std::size_t>(JoystickId::Joystick_Right)]
390 ->GetAnalogDirectionStatus(Input::AnalogDirection::UP));
391 pad_state.r_stick_down.Assign(
392 analog_state[static_cast<std::size_t>(JoystickId::Joystick_Right)]
393 ->GetAnalogDirectionStatus(Input::AnalogDirection::DOWN));
394 rstick_entry.x = static_cast<s32>(stick_r_x_f * HID_JOYSTICK_MAX);
395 rstick_entry.y = static_cast<s32>(stick_r_y_f * HID_JOYSTICK_MAX);
396 }
397
398 if (controller_type != NPadControllerType::JoyRight) {
399 pad_state.d_left.Assign(button_state[DLeft - BUTTON_HID_BEGIN]->GetStatus());
400 pad_state.d_up.Assign(button_state[DUp - BUTTON_HID_BEGIN]->GetStatus());
401 pad_state.d_right.Assign(button_state[DRight - BUTTON_HID_BEGIN]->GetStatus());
402 pad_state.d_down.Assign(button_state[DDown - BUTTON_HID_BEGIN]->GetStatus());
403 pad_state.l_stick.Assign(button_state[LStick - BUTTON_HID_BEGIN]->GetStatus());
404 pad_state.l.Assign(button_state[L - BUTTON_HID_BEGIN]->GetStatus());
405 pad_state.zl.Assign(button_state[ZL - BUTTON_HID_BEGIN]->GetStatus());
406 pad_state.minus.Assign(button_state[Minus - BUTTON_HID_BEGIN]->GetStatus());
407
408 pad_state.l_stick_right.Assign(
409 analog_state[static_cast<std::size_t>(JoystickId::Joystick_Left)]
410 ->GetAnalogDirectionStatus(Input::AnalogDirection::RIGHT));
411 pad_state.l_stick_left.Assign(
412 analog_state[static_cast<std::size_t>(JoystickId::Joystick_Left)]
413 ->GetAnalogDirectionStatus(Input::AnalogDirection::LEFT));
414 pad_state.l_stick_up.Assign(
415 analog_state[static_cast<std::size_t>(JoystickId::Joystick_Left)]
416 ->GetAnalogDirectionStatus(Input::AnalogDirection::UP));
417 pad_state.l_stick_down.Assign(
418 analog_state[static_cast<std::size_t>(JoystickId::Joystick_Left)]
419 ->GetAnalogDirectionStatus(Input::AnalogDirection::DOWN));
420 lstick_entry.x = static_cast<s32>(stick_l_x_f * HID_JOYSTICK_MAX);
421 lstick_entry.y = static_cast<s32>(stick_l_y_f * HID_JOYSTICK_MAX);
422 }
423
424 if (controller_type == NPadControllerType::JoyLeft) {
425 pad_state.left_sl.Assign(button_state[SL - BUTTON_HID_BEGIN]->GetStatus());
426 pad_state.left_sr.Assign(button_state[SR - BUTTON_HID_BEGIN]->GetStatus());
427 }
428
429 if (controller_type == NPadControllerType::JoyRight) {
430 pad_state.right_sl.Assign(button_state[SL - BUTTON_HID_BEGIN]->GetStatus());
431 pad_state.right_sr.Assign(button_state[SR - BUTTON_HID_BEGIN]->GetStatus());
432 }
433
434 if (controller_type == NPadControllerType::GameCube) {
435 trigger_entry.l_analog = static_cast<s32>(
436 button_state[ZL - BUTTON_HID_BEGIN]->GetStatus() ? HID_TRIGGER_MAX : 0);
437 trigger_entry.r_analog = static_cast<s32>(
438 button_state[ZR - BUTTON_HID_BEGIN]->GetStatus() ? HID_TRIGGER_MAX : 0);
439 pad_state.zl.Assign(false);
440 pad_state.zr.Assign(button_state[R - BUTTON_HID_BEGIN]->GetStatus());
441 pad_state.l.Assign(button_state[ZL - BUTTON_HID_BEGIN]->GetStatus());
442 pad_state.r.Assign(button_state[ZR - BUTTON_HID_BEGIN]->GetStatus());
443 } 381 }
444} 382}
445 383
@@ -448,173 +386,136 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8*
448 if (!IsControllerActivated()) { 386 if (!IsControllerActivated()) {
449 return; 387 return;
450 } 388 }
451 for (std::size_t i = 0; i < shared_memory_entries.size(); ++i) {
452 auto& npad = shared_memory_entries[i];
453 const std::array<NPadGeneric*, 7> controller_npads{
454 &npad.fullkey_states, &npad.handheld_states, &npad.joy_dual_states,
455 &npad.joy_left_states, &npad.joy_right_states, &npad.palma_states,
456 &npad.system_ext_states};
457
458 // There is the posibility to have more controllers with analog triggers
459 const std::array<TriggerGeneric*, 1> controller_triggers{
460 &npad.gc_trigger_states,
461 };
462
463 for (auto* main_controller : controller_npads) {
464 main_controller->common.entry_count = 16;
465 main_controller->common.total_entry_count = 17;
466
467 const auto& last_entry =
468 main_controller->npad[main_controller->common.last_entry_index];
469
470 main_controller->common.timestamp = core_timing.GetCPUTicks();
471 main_controller->common.last_entry_index =
472 (main_controller->common.last_entry_index + 1) % 17;
473
474 auto& cur_entry = main_controller->npad[main_controller->common.last_entry_index];
475
476 cur_entry.timestamp = last_entry.timestamp + 1;
477 cur_entry.timestamp2 = cur_entry.timestamp;
478 }
479
480 for (auto* analog_trigger : controller_triggers) {
481 analog_trigger->entry_count = 16;
482 analog_trigger->total_entry_count = 17;
483
484 const auto& last_entry = analog_trigger->trigger[analog_trigger->last_entry_index];
485
486 analog_trigger->timestamp = core_timing.GetCPUTicks();
487 analog_trigger->last_entry_index = (analog_trigger->last_entry_index + 1) % 17;
488
489 auto& cur_entry = analog_trigger->trigger[analog_trigger->last_entry_index];
490 389
491 cur_entry.timestamp = last_entry.timestamp + 1; 390 for (std::size_t i = 0; i < controller_data.size(); ++i) {
492 cur_entry.timestamp2 = cur_entry.timestamp; 391 auto& controller = controller_data[i];
493 } 392 auto& npad = controller.shared_memory_entry;
494 393
495 const auto& controller_type = connected_controllers[i].type; 394 const auto& controller_type = controller.device->GetNpadStyleIndex();
496 395
497 if (controller_type == NPadControllerType::None || !connected_controllers[i].is_connected) { 396 if (controller_type == Core::HID::NpadStyleIndex::None ||
397 !controller.device->IsConnected()) {
398 // Refresh shared memory
399 std::memcpy(data + NPAD_OFFSET + (i * sizeof(NpadInternalState)),
400 &controller.shared_memory_entry, sizeof(NpadInternalState));
498 continue; 401 continue;
499 } 402 }
500 const u32 npad_index = static_cast<u32>(i);
501
502 RequestPadStateUpdate(npad_index);
503 auto& pad_state = npad_pad_states[npad_index];
504 auto& trigger_state = npad_trigger_states[npad_index];
505
506 auto& main_controller =
507 npad.fullkey_states.npad[npad.fullkey_states.common.last_entry_index];
508 auto& handheld_entry =
509 npad.handheld_states.npad[npad.handheld_states.common.last_entry_index];
510 auto& dual_entry = npad.joy_dual_states.npad[npad.joy_dual_states.common.last_entry_index];
511 auto& left_entry = npad.joy_left_states.npad[npad.joy_left_states.common.last_entry_index];
512 auto& right_entry =
513 npad.joy_right_states.npad[npad.joy_right_states.common.last_entry_index];
514 auto& pokeball_entry = npad.palma_states.npad[npad.palma_states.common.last_entry_index];
515 auto& libnx_entry =
516 npad.system_ext_states.npad[npad.system_ext_states.common.last_entry_index];
517 auto& trigger_entry =
518 npad.gc_trigger_states.trigger[npad.gc_trigger_states.last_entry_index];
519
520 libnx_entry.connection_status.raw = 0;
521 libnx_entry.connection_status.is_connected.Assign(1);
522 403
404 RequestPadStateUpdate(controller.device->GetNpadIdType());
405 auto& pad_state = controller.npad_pad_state;
406 auto& libnx_state = controller.npad_libnx_state;
407 auto& trigger_state = controller.npad_trigger_state;
408
409 // LibNX exclusively uses this section, so we always update it since LibNX doesn't activate
410 // any controllers.
411 libnx_state.connection_status.raw = 0;
412 libnx_state.connection_status.is_connected.Assign(1);
523 switch (controller_type) { 413 switch (controller_type) {
524 case NPadControllerType::None: 414 case Core::HID::NpadStyleIndex::None:
525 UNREACHABLE(); 415 UNREACHABLE();
526 break; 416 break;
527 case NPadControllerType::ProController: 417 case Core::HID::NpadStyleIndex::ProController:
528 main_controller.connection_status.raw = 0; 418 case Core::HID::NpadStyleIndex::NES:
529 main_controller.connection_status.is_connected.Assign(1); 419 case Core::HID::NpadStyleIndex::SNES:
530 main_controller.connection_status.is_wired.Assign(1); 420 case Core::HID::NpadStyleIndex::N64:
531 main_controller.pad.pad_states.raw = pad_state.pad_states.raw; 421 case Core::HID::NpadStyleIndex::SegaGenesis:
532 main_controller.pad.l_stick = pad_state.l_stick; 422 pad_state.connection_status.raw = 0;
533 main_controller.pad.r_stick = pad_state.r_stick; 423 pad_state.connection_status.is_connected.Assign(1);
534 424 pad_state.connection_status.is_wired.Assign(1);
535 libnx_entry.connection_status.is_wired.Assign(1); 425
426 libnx_state.connection_status.is_wired.Assign(1);
427 pad_state.sampling_number =
428 npad.fullkey_lifo.ReadCurrentEntry().state.sampling_number + 1;
429 npad.fullkey_lifo.WriteNextEntry(pad_state);
536 break; 430 break;
537 case NPadControllerType::Handheld: 431 case Core::HID::NpadStyleIndex::Handheld:
538 handheld_entry.connection_status.raw = 0; 432 pad_state.connection_status.raw = 0;
539 handheld_entry.connection_status.is_connected.Assign(1); 433 pad_state.connection_status.is_connected.Assign(1);
540 handheld_entry.connection_status.is_wired.Assign(1); 434 pad_state.connection_status.is_wired.Assign(1);
541 handheld_entry.connection_status.is_left_connected.Assign(1); 435 pad_state.connection_status.is_left_connected.Assign(1);
542 handheld_entry.connection_status.is_right_connected.Assign(1); 436 pad_state.connection_status.is_right_connected.Assign(1);
543 handheld_entry.connection_status.is_left_wired.Assign(1); 437 pad_state.connection_status.is_left_wired.Assign(1);
544 handheld_entry.connection_status.is_right_wired.Assign(1); 438 pad_state.connection_status.is_right_wired.Assign(1);
545 handheld_entry.pad.pad_states.raw = pad_state.pad_states.raw; 439
546 handheld_entry.pad.l_stick = pad_state.l_stick; 440 libnx_state.connection_status.is_wired.Assign(1);
547 handheld_entry.pad.r_stick = pad_state.r_stick; 441 libnx_state.connection_status.is_left_connected.Assign(1);
548 442 libnx_state.connection_status.is_right_connected.Assign(1);
549 libnx_entry.connection_status.is_wired.Assign(1); 443 libnx_state.connection_status.is_left_wired.Assign(1);
550 libnx_entry.connection_status.is_left_connected.Assign(1); 444 libnx_state.connection_status.is_right_wired.Assign(1);
551 libnx_entry.connection_status.is_right_connected.Assign(1); 445 pad_state.sampling_number =
552 libnx_entry.connection_status.is_left_wired.Assign(1); 446 npad.handheld_lifo.ReadCurrentEntry().state.sampling_number + 1;
553 libnx_entry.connection_status.is_right_wired.Assign(1); 447 npad.handheld_lifo.WriteNextEntry(pad_state);
554 break; 448 break;
555 case NPadControllerType::JoyDual: 449 case Core::HID::NpadStyleIndex::JoyconDual:
556 dual_entry.connection_status.raw = 0; 450 pad_state.connection_status.raw = 0;
557 dual_entry.connection_status.is_connected.Assign(1); 451 pad_state.connection_status.is_connected.Assign(1);
558 dual_entry.connection_status.is_left_connected.Assign(1); 452 if (controller.is_dual_left_connected) {
559 dual_entry.connection_status.is_right_connected.Assign(1); 453 pad_state.connection_status.is_left_connected.Assign(1);
560 dual_entry.pad.pad_states.raw = pad_state.pad_states.raw; 454 libnx_state.connection_status.is_left_connected.Assign(1);
561 dual_entry.pad.l_stick = pad_state.l_stick; 455 }
562 dual_entry.pad.r_stick = pad_state.r_stick; 456 if (controller.is_dual_right_connected) {
563 457 pad_state.connection_status.is_right_connected.Assign(1);
564 libnx_entry.connection_status.is_left_connected.Assign(1); 458 libnx_state.connection_status.is_right_connected.Assign(1);
565 libnx_entry.connection_status.is_right_connected.Assign(1); 459 }
460
461 pad_state.sampling_number =
462 npad.joy_dual_lifo.ReadCurrentEntry().state.sampling_number + 1;
463 npad.joy_dual_lifo.WriteNextEntry(pad_state);
566 break; 464 break;
567 case NPadControllerType::JoyLeft: 465 case Core::HID::NpadStyleIndex::JoyconLeft:
568 left_entry.connection_status.raw = 0; 466 pad_state.connection_status.raw = 0;
569 left_entry.connection_status.is_connected.Assign(1); 467 pad_state.connection_status.is_connected.Assign(1);
570 left_entry.connection_status.is_left_connected.Assign(1); 468 pad_state.connection_status.is_left_connected.Assign(1);
571 left_entry.pad.pad_states.raw = pad_state.pad_states.raw; 469
572 left_entry.pad.l_stick = pad_state.l_stick; 470 libnx_state.connection_status.is_left_connected.Assign(1);
573 left_entry.pad.r_stick = pad_state.r_stick; 471 pad_state.sampling_number =
574 472 npad.joy_left_lifo.ReadCurrentEntry().state.sampling_number + 1;
575 libnx_entry.connection_status.is_left_connected.Assign(1); 473 npad.joy_left_lifo.WriteNextEntry(pad_state);
576 break; 474 break;
577 case NPadControllerType::JoyRight: 475 case Core::HID::NpadStyleIndex::JoyconRight:
578 right_entry.connection_status.raw = 0; 476 pad_state.connection_status.raw = 0;
579 right_entry.connection_status.is_connected.Assign(1); 477 pad_state.connection_status.is_connected.Assign(1);
580 right_entry.connection_status.is_right_connected.Assign(1); 478 pad_state.connection_status.is_right_connected.Assign(1);
581 right_entry.pad.pad_states.raw = pad_state.pad_states.raw; 479
582 right_entry.pad.l_stick = pad_state.l_stick; 480 libnx_state.connection_status.is_right_connected.Assign(1);
583 right_entry.pad.r_stick = pad_state.r_stick; 481 pad_state.sampling_number =
584 482 npad.joy_right_lifo.ReadCurrentEntry().state.sampling_number + 1;
585 libnx_entry.connection_status.is_right_connected.Assign(1); 483 npad.joy_right_lifo.WriteNextEntry(pad_state);
586 break; 484 break;
587 case NPadControllerType::GameCube: 485 case Core::HID::NpadStyleIndex::GameCube:
588 main_controller.connection_status.raw = 0; 486 pad_state.connection_status.raw = 0;
589 main_controller.connection_status.is_connected.Assign(1); 487 pad_state.connection_status.is_connected.Assign(1);
590 main_controller.connection_status.is_wired.Assign(1); 488 pad_state.connection_status.is_wired.Assign(1);
591 main_controller.pad.pad_states.raw = pad_state.pad_states.raw; 489
592 main_controller.pad.l_stick = pad_state.l_stick; 490 libnx_state.connection_status.is_wired.Assign(1);
593 main_controller.pad.r_stick = pad_state.r_stick; 491 pad_state.sampling_number =
594 trigger_entry.l_analog = trigger_state.l_analog; 492 npad.fullkey_lifo.ReadCurrentEntry().state.sampling_number + 1;
595 trigger_entry.r_analog = trigger_state.r_analog; 493 trigger_state.sampling_number =
596 494 npad.gc_trigger_lifo.ReadCurrentEntry().state.sampling_number + 1;
597 libnx_entry.connection_status.is_wired.Assign(1); 495 npad.fullkey_lifo.WriteNextEntry(pad_state);
496 npad.gc_trigger_lifo.WriteNextEntry(trigger_state);
598 break; 497 break;
599 case NPadControllerType::Pokeball: 498 case Core::HID::NpadStyleIndex::Pokeball:
600 pokeball_entry.connection_status.raw = 0; 499 pad_state.connection_status.raw = 0;
601 pokeball_entry.connection_status.is_connected.Assign(1); 500 pad_state.connection_status.is_connected.Assign(1);
602 pokeball_entry.pad.pad_states.raw = pad_state.pad_states.raw; 501 pad_state.sampling_number =
603 pokeball_entry.pad.l_stick = pad_state.l_stick; 502 npad.palma_lifo.ReadCurrentEntry().state.sampling_number + 1;
604 pokeball_entry.pad.r_stick = pad_state.r_stick; 503 npad.palma_lifo.WriteNextEntry(pad_state);
504 break;
505 default:
605 break; 506 break;
606 } 507 }
607 508
608 // LibNX exclusively uses this section, so we always update it since LibNX doesn't activate 509 libnx_state.npad_buttons.raw = pad_state.npad_buttons.raw;
609 // any controllers. 510 libnx_state.l_stick = pad_state.l_stick;
610 libnx_entry.pad.pad_states.raw = pad_state.pad_states.raw; 511 libnx_state.r_stick = pad_state.r_stick;
611 libnx_entry.pad.l_stick = pad_state.l_stick; 512 npad.system_ext_lifo.WriteNextEntry(pad_state);
612 libnx_entry.pad.r_stick = pad_state.r_stick; 513
514 press_state |= static_cast<u64>(pad_state.npad_buttons.raw);
613 515
614 press_state |= static_cast<u32>(pad_state.pad_states.raw); 516 std::memcpy(data + NPAD_OFFSET + (i * sizeof(NpadInternalState)),
517 &controller.shared_memory_entry, sizeof(NpadInternalState));
615 } 518 }
616 std::memcpy(data + NPAD_OFFSET, shared_memory_entries.data(),
617 shared_memory_entries.size() * sizeof(NPadEntry));
618} 519}
619 520
620void Controller_NPad::OnMotionUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, 521void Controller_NPad::OnMotionUpdate(const Core::Timing::CoreTiming& core_timing, u8* data,
@@ -622,145 +523,138 @@ void Controller_NPad::OnMotionUpdate(const Core::Timing::CoreTiming& core_timing
622 if (!IsControllerActivated()) { 523 if (!IsControllerActivated()) {
623 return; 524 return;
624 } 525 }
625 for (std::size_t i = 0; i < shared_memory_entries.size(); ++i) {
626 auto& npad = shared_memory_entries[i];
627
628 const auto& controller_type = connected_controllers[i].type;
629 526
630 if (controller_type == NPadControllerType::None || !connected_controllers[i].is_connected) { 527 for (std::size_t i = 0; i < controller_data.size(); ++i) {
631 continue; 528 auto& controller = controller_data[i];
632 }
633
634 const std::array<SixAxisGeneric*, 6> controller_sixaxes{
635 &npad.sixaxis_fullkey, &npad.sixaxis_handheld, &npad.sixaxis_dual_left,
636 &npad.sixaxis_dual_right, &npad.sixaxis_left, &npad.sixaxis_right,
637 };
638
639 for (auto* sixaxis_sensor : controller_sixaxes) {
640 sixaxis_sensor->common.entry_count = 16;
641 sixaxis_sensor->common.total_entry_count = 17;
642
643 const auto& last_entry =
644 sixaxis_sensor->sixaxis[sixaxis_sensor->common.last_entry_index];
645
646 sixaxis_sensor->common.timestamp = core_timing.GetCPUTicks();
647 sixaxis_sensor->common.last_entry_index =
648 (sixaxis_sensor->common.last_entry_index + 1) % 17;
649 529
650 auto& cur_entry = sixaxis_sensor->sixaxis[sixaxis_sensor->common.last_entry_index]; 530 const auto& controller_type = controller.device->GetNpadStyleIndex();
651 531
652 cur_entry.timestamp = last_entry.timestamp + 1; 532 if (controller_type == Core::HID::NpadStyleIndex::None ||
653 cur_entry.timestamp2 = cur_entry.timestamp; 533 !controller.device->IsConnected()) {
534 continue;
654 } 535 }
655 536
656 // Try to read sixaxis sensor states 537 auto& npad = controller.shared_memory_entry;
657 std::array<MotionDevice, 2> motion_devices; 538 const auto& motion_state = controller.device->GetMotions();
658 539 auto& sixaxis_fullkey_state = controller.sixaxis_fullkey_state;
659 if (sixaxis_sensors_enabled && Settings::values.motion_enabled.GetValue()) { 540 auto& sixaxis_handheld_state = controller.sixaxis_handheld_state;
660 sixaxis_at_rest = true; 541 auto& sixaxis_dual_left_state = controller.sixaxis_dual_left_state;
661 for (std::size_t e = 0; e < motion_devices.size(); ++e) { 542 auto& sixaxis_dual_right_state = controller.sixaxis_dual_right_state;
662 const auto& device = motions[i][e]; 543 auto& sixaxis_left_lifo_state = controller.sixaxis_left_lifo_state;
663 if (device) { 544 auto& sixaxis_right_lifo_state = controller.sixaxis_right_lifo_state;
664 std::tie(motion_devices[e].accel, motion_devices[e].gyro, 545
665 motion_devices[e].rotation, motion_devices[e].orientation, 546 if (controller.sixaxis_sensor_enabled && Settings::values.motion_enabled.GetValue()) {
666 motion_devices[e].quaternion) = device->GetStatus(); 547 controller.sixaxis_at_rest = true;
667 sixaxis_at_rest = sixaxis_at_rest && motion_devices[e].gyro.Length2() < 0.0001f; 548 for (std::size_t e = 0; e < motion_state.size(); ++e) {
668 } 549 controller.sixaxis_at_rest =
550 controller.sixaxis_at_rest && motion_state[e].is_at_rest;
669 } 551 }
670 } 552 }
671 553
672 auto& full_sixaxis_entry =
673 npad.sixaxis_fullkey.sixaxis[npad.sixaxis_fullkey.common.last_entry_index];
674 auto& handheld_sixaxis_entry =
675 npad.sixaxis_handheld.sixaxis[npad.sixaxis_handheld.common.last_entry_index];
676 auto& dual_left_sixaxis_entry =
677 npad.sixaxis_dual_left.sixaxis[npad.sixaxis_dual_left.common.last_entry_index];
678 auto& dual_right_sixaxis_entry =
679 npad.sixaxis_dual_right.sixaxis[npad.sixaxis_dual_right.common.last_entry_index];
680 auto& left_sixaxis_entry =
681 npad.sixaxis_left.sixaxis[npad.sixaxis_left.common.last_entry_index];
682 auto& right_sixaxis_entry =
683 npad.sixaxis_right.sixaxis[npad.sixaxis_right.common.last_entry_index];
684
685 switch (controller_type) { 554 switch (controller_type) {
686 case NPadControllerType::None: 555 case Core::HID::NpadStyleIndex::None:
687 UNREACHABLE(); 556 UNREACHABLE();
688 break; 557 break;
689 case NPadControllerType::ProController: 558 case Core::HID::NpadStyleIndex::ProController:
690 full_sixaxis_entry.attribute.raw = 0; 559 sixaxis_fullkey_state.attribute.raw = 0;
691 if (sixaxis_sensors_enabled && motions[i][0]) { 560 if (controller.sixaxis_sensor_enabled) {
692 full_sixaxis_entry.attribute.is_connected.Assign(1); 561 sixaxis_fullkey_state.attribute.is_connected.Assign(1);
693 full_sixaxis_entry.accel = motion_devices[0].accel; 562 sixaxis_fullkey_state.accel = motion_state[0].accel;
694 full_sixaxis_entry.gyro = motion_devices[0].gyro; 563 sixaxis_fullkey_state.gyro = motion_state[0].gyro;
695 full_sixaxis_entry.rotation = motion_devices[0].rotation; 564 sixaxis_fullkey_state.rotation = motion_state[0].rotation;
696 full_sixaxis_entry.orientation = motion_devices[0].orientation; 565 sixaxis_fullkey_state.orientation = motion_state[0].orientation;
697 } 566 }
698 break; 567 break;
699 case NPadControllerType::Handheld: 568 case Core::HID::NpadStyleIndex::Handheld:
700 handheld_sixaxis_entry.attribute.raw = 0; 569 sixaxis_handheld_state.attribute.raw = 0;
701 if (sixaxis_sensors_enabled && motions[i][0]) { 570 if (controller.sixaxis_sensor_enabled) {
702 handheld_sixaxis_entry.attribute.is_connected.Assign(1); 571 sixaxis_handheld_state.attribute.is_connected.Assign(1);
703 handheld_sixaxis_entry.accel = motion_devices[0].accel; 572 sixaxis_handheld_state.accel = motion_state[0].accel;
704 handheld_sixaxis_entry.gyro = motion_devices[0].gyro; 573 sixaxis_handheld_state.gyro = motion_state[0].gyro;
705 handheld_sixaxis_entry.rotation = motion_devices[0].rotation; 574 sixaxis_handheld_state.rotation = motion_state[0].rotation;
706 handheld_sixaxis_entry.orientation = motion_devices[0].orientation; 575 sixaxis_handheld_state.orientation = motion_state[0].orientation;
707 } 576 }
708 break; 577 break;
709 case NPadControllerType::JoyDual: 578 case Core::HID::NpadStyleIndex::JoyconDual:
710 dual_left_sixaxis_entry.attribute.raw = 0; 579 sixaxis_dual_left_state.attribute.raw = 0;
711 dual_right_sixaxis_entry.attribute.raw = 0; 580 sixaxis_dual_right_state.attribute.raw = 0;
712 if (sixaxis_sensors_enabled && motions[i][0]) { 581 if (controller.sixaxis_sensor_enabled) {
713 // Set motion for the left joycon 582 // Set motion for the left joycon
714 dual_left_sixaxis_entry.attribute.is_connected.Assign(1); 583 sixaxis_dual_left_state.attribute.is_connected.Assign(1);
715 dual_left_sixaxis_entry.accel = motion_devices[0].accel; 584 sixaxis_dual_left_state.accel = motion_state[0].accel;
716 dual_left_sixaxis_entry.gyro = motion_devices[0].gyro; 585 sixaxis_dual_left_state.gyro = motion_state[0].gyro;
717 dual_left_sixaxis_entry.rotation = motion_devices[0].rotation; 586 sixaxis_dual_left_state.rotation = motion_state[0].rotation;
718 dual_left_sixaxis_entry.orientation = motion_devices[0].orientation; 587 sixaxis_dual_left_state.orientation = motion_state[0].orientation;
719 } 588 }
720 if (sixaxis_sensors_enabled && motions[i][1]) { 589 if (controller.sixaxis_sensor_enabled) {
721 // Set motion for the right joycon 590 // Set motion for the right joycon
722 dual_right_sixaxis_entry.attribute.is_connected.Assign(1); 591 sixaxis_dual_right_state.attribute.is_connected.Assign(1);
723 dual_right_sixaxis_entry.accel = motion_devices[1].accel; 592 sixaxis_dual_right_state.accel = motion_state[1].accel;
724 dual_right_sixaxis_entry.gyro = motion_devices[1].gyro; 593 sixaxis_dual_right_state.gyro = motion_state[1].gyro;
725 dual_right_sixaxis_entry.rotation = motion_devices[1].rotation; 594 sixaxis_dual_right_state.rotation = motion_state[1].rotation;
726 dual_right_sixaxis_entry.orientation = motion_devices[1].orientation; 595 sixaxis_dual_right_state.orientation = motion_state[1].orientation;
727 } 596 }
728 break; 597 break;
729 case NPadControllerType::JoyLeft: 598 case Core::HID::NpadStyleIndex::JoyconLeft:
730 left_sixaxis_entry.attribute.raw = 0; 599 sixaxis_left_lifo_state.attribute.raw = 0;
731 if (sixaxis_sensors_enabled && motions[i][0]) { 600 if (controller.sixaxis_sensor_enabled) {
732 left_sixaxis_entry.attribute.is_connected.Assign(1); 601 sixaxis_left_lifo_state.attribute.is_connected.Assign(1);
733 left_sixaxis_entry.accel = motion_devices[0].accel; 602 sixaxis_left_lifo_state.accel = motion_state[0].accel;
734 left_sixaxis_entry.gyro = motion_devices[0].gyro; 603 sixaxis_left_lifo_state.gyro = motion_state[0].gyro;
735 left_sixaxis_entry.rotation = motion_devices[0].rotation; 604 sixaxis_left_lifo_state.rotation = motion_state[0].rotation;
736 left_sixaxis_entry.orientation = motion_devices[0].orientation; 605 sixaxis_left_lifo_state.orientation = motion_state[0].orientation;
737 } 606 }
738 break; 607 break;
739 case NPadControllerType::JoyRight: 608 case Core::HID::NpadStyleIndex::JoyconRight:
740 right_sixaxis_entry.attribute.raw = 0; 609 sixaxis_right_lifo_state.attribute.raw = 0;
741 if (sixaxis_sensors_enabled && motions[i][1]) { 610 if (controller.sixaxis_sensor_enabled) {
742 right_sixaxis_entry.attribute.is_connected.Assign(1); 611 sixaxis_right_lifo_state.attribute.is_connected.Assign(1);
743 right_sixaxis_entry.accel = motion_devices[1].accel; 612 sixaxis_right_lifo_state.accel = motion_state[1].accel;
744 right_sixaxis_entry.gyro = motion_devices[1].gyro; 613 sixaxis_right_lifo_state.gyro = motion_state[1].gyro;
745 right_sixaxis_entry.rotation = motion_devices[1].rotation; 614 sixaxis_right_lifo_state.rotation = motion_state[1].rotation;
746 right_sixaxis_entry.orientation = motion_devices[1].orientation; 615 sixaxis_right_lifo_state.orientation = motion_state[1].orientation;
747 } 616 }
748 break; 617 break;
749 case NPadControllerType::GameCube: 618 default:
750 case NPadControllerType::Pokeball:
751 break; 619 break;
752 } 620 }
621
622 sixaxis_fullkey_state.sampling_number =
623 npad.sixaxis_fullkey_lifo.ReadCurrentEntry().state.sampling_number + 1;
624 sixaxis_handheld_state.sampling_number =
625 npad.sixaxis_handheld_lifo.ReadCurrentEntry().state.sampling_number + 1;
626 sixaxis_dual_left_state.sampling_number =
627 npad.sixaxis_dual_left_lifo.ReadCurrentEntry().state.sampling_number + 1;
628 sixaxis_dual_right_state.sampling_number =
629 npad.sixaxis_dual_right_lifo.ReadCurrentEntry().state.sampling_number + 1;
630 sixaxis_left_lifo_state.sampling_number =
631 npad.sixaxis_left_lifo.ReadCurrentEntry().state.sampling_number + 1;
632 sixaxis_right_lifo_state.sampling_number =
633 npad.sixaxis_right_lifo.ReadCurrentEntry().state.sampling_number + 1;
634
635 if (Core::HID::IndexToNpadIdType(i) == Core::HID::NpadIdType::Handheld) {
636 // This buffer only is updated on handheld on HW
637 npad.sixaxis_handheld_lifo.WriteNextEntry(sixaxis_handheld_state);
638 } else {
639 // Handheld doesn't update this buffer on HW
640 npad.sixaxis_fullkey_lifo.WriteNextEntry(sixaxis_fullkey_state);
641 }
642
643 npad.sixaxis_dual_left_lifo.WriteNextEntry(sixaxis_dual_left_state);
644 npad.sixaxis_dual_right_lifo.WriteNextEntry(sixaxis_dual_right_state);
645 npad.sixaxis_left_lifo.WriteNextEntry(sixaxis_left_lifo_state);
646 npad.sixaxis_right_lifo.WriteNextEntry(sixaxis_right_lifo_state);
647 std::memcpy(data + NPAD_OFFSET + (i * sizeof(NpadInternalState)),
648 &controller.shared_memory_entry, sizeof(NpadInternalState));
753 } 649 }
754 std::memcpy(data + NPAD_OFFSET, shared_memory_entries.data(),
755 shared_memory_entries.size() * sizeof(NPadEntry));
756} 650}
757 651
758void Controller_NPad::SetSupportedStyleSet(NpadStyleSet style_set) { 652void Controller_NPad::SetSupportedStyleSet(Core::HID::NpadStyleTag style_set) {
759 style.raw = style_set.raw; 653 hid_core.SetSupportedStyleTag(style_set);
760} 654}
761 655
762Controller_NPad::NpadStyleSet Controller_NPad::GetSupportedStyleSet() const { 656Core::HID::NpadStyleTag Controller_NPad::GetSupportedStyleSet() const {
763 return style; 657 return hid_core.GetSupportedStyleTag();
764} 658}
765 659
766void Controller_NPad::SetSupportedNpadIdTypes(u8* data, std::size_t length) { 660void Controller_NPad::SetSupportedNpadIdTypes(u8* data, std::size_t length) {
@@ -779,11 +673,11 @@ std::size_t Controller_NPad::GetSupportedNpadIdTypesSize() const {
779 return supported_npad_id_types.size(); 673 return supported_npad_id_types.size();
780} 674}
781 675
782void Controller_NPad::SetHoldType(NpadHoldType joy_hold_type) { 676void Controller_NPad::SetHoldType(NpadJoyHoldType joy_hold_type) {
783 hold_type = joy_hold_type; 677 hold_type = joy_hold_type;
784} 678}
785 679
786Controller_NPad::NpadHoldType Controller_NPad::GetHoldType() const { 680Controller_NPad::NpadJoyHoldType Controller_NPad::GetHoldType() const {
787 return hold_type; 681 return hold_type;
788} 682}
789 683
@@ -803,29 +697,91 @@ Controller_NPad::NpadCommunicationMode Controller_NPad::GetNpadCommunicationMode
803 return communication_mode; 697 return communication_mode;
804} 698}
805 699
806void Controller_NPad::SetNpadMode(u32 npad_id, NpadAssignments assignment_mode) { 700void Controller_NPad::SetNpadMode(Core::HID::NpadIdType npad_id, NpadJoyDeviceType npad_device_type,
807 const std::size_t npad_index = NPadIdToIndex(npad_id); 701 NpadJoyAssignmentMode assignment_mode) {
808 ASSERT(npad_index < shared_memory_entries.size()); 702 if (!IsNpadIdValid(npad_id)) {
809 if (shared_memory_entries[npad_index].assignment_mode != assignment_mode) { 703 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
810 shared_memory_entries[npad_index].assignment_mode = assignment_mode; 704 return;
705 }
706
707 auto& controller = GetControllerFromNpadIdType(npad_id);
708 if (controller.shared_memory_entry.assignment_mode != assignment_mode) {
709 controller.shared_memory_entry.assignment_mode = assignment_mode;
710 }
711
712 if (!controller.device->IsConnected()) {
713 return;
714 }
715
716 if (assignment_mode == NpadJoyAssignmentMode::Dual) {
717 if (controller.device->GetNpadStyleIndex() == Core::HID::NpadStyleIndex::JoyconLeft) {
718 DisconnectNpad(npad_id);
719 controller.is_dual_left_connected = true;
720 controller.is_dual_right_connected = false;
721 UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconDual, npad_id, true);
722 return;
723 }
724 if (controller.device->GetNpadStyleIndex() == Core::HID::NpadStyleIndex::JoyconRight) {
725 DisconnectNpad(npad_id);
726 controller.is_dual_left_connected = false;
727 controller.is_dual_right_connected = true;
728 UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconDual, npad_id, true);
729 return;
730 }
731 return;
732 }
733
734 // This is for NpadJoyAssignmentMode::Single
735
736 // Only JoyconDual get affected by this function
737 if (controller.device->GetNpadStyleIndex() != Core::HID::NpadStyleIndex::JoyconDual) {
738 return;
739 }
740
741 if (controller.is_dual_left_connected && !controller.is_dual_right_connected) {
742 DisconnectNpad(npad_id);
743 UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconLeft, npad_id, true);
744 return;
745 }
746 if (!controller.is_dual_left_connected && controller.is_dual_right_connected) {
747 DisconnectNpad(npad_id);
748 UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconRight, npad_id, true);
749 return;
750 }
751
752 // We have two controllers connected to the same npad_id we need to split them
753 const auto npad_id_2 = hid_core.GetFirstDisconnectedNpadId();
754 auto& controller_2 = GetControllerFromNpadIdType(npad_id_2);
755 DisconnectNpad(npad_id);
756 if (npad_device_type == NpadJoyDeviceType::Left) {
757 UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconLeft, npad_id, true);
758 controller_2.is_dual_left_connected = false;
759 controller_2.is_dual_right_connected = true;
760 UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconDual, npad_id_2, true);
761 } else {
762 UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconRight, npad_id, true);
763 controller_2.is_dual_left_connected = true;
764 controller_2.is_dual_right_connected = false;
765 UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconDual, npad_id_2, true);
811 } 766 }
812} 767}
813 768
814bool Controller_NPad::VibrateControllerAtIndex(std::size_t npad_index, std::size_t device_index, 769bool Controller_NPad::VibrateControllerAtIndex(Core::HID::NpadIdType npad_id,
815 const VibrationValue& vibration_value) { 770 std::size_t device_index,
816 if (!connected_controllers[npad_index].is_connected || !vibrations[npad_index][device_index]) { 771 const Core::HID::VibrationValue& vibration_value) {
772 auto& controller = GetControllerFromNpadIdType(npad_id);
773 if (!controller.device->IsConnected()) {
817 return false; 774 return false;
818 } 775 }
819 776
820 const auto& player = Settings::values.players.GetValue()[npad_index]; 777 if (!controller.device->IsVibrationEnabled()) {
821 778 if (controller.vibration[device_index].latest_vibration_value.low_amplitude != 0.0f ||
822 if (!player.vibration_enabled) { 779 controller.vibration[device_index].latest_vibration_value.high_amplitude != 0.0f) {
823 if (latest_vibration_values[npad_index][device_index].amp_low != 0.0f ||
824 latest_vibration_values[npad_index][device_index].amp_high != 0.0f) {
825 // Send an empty vibration to stop any vibrations. 780 // Send an empty vibration to stop any vibrations.
826 vibrations[npad_index][device_index]->SetRumblePlay(0.0f, 160.0f, 0.0f, 320.0f); 781 Core::HID::VibrationValue vibration{0.0f, 160.0f, 0.0f, 320.0f};
782 controller.device->SetVibration(device_index, vibration);
827 // Then reset the vibration value to its default value. 783 // Then reset the vibration value to its default value.
828 latest_vibration_values[npad_index][device_index] = DEFAULT_VIBRATION_VALUE; 784 controller.vibration[device_index].latest_vibration_value = DEFAULT_VIBRATION_VALUE;
829 } 785 }
830 786
831 return false; 787 return false;
@@ -839,27 +795,25 @@ bool Controller_NPad::VibrateControllerAtIndex(std::size_t npad_index, std::size
839 const auto now = steady_clock::now(); 795 const auto now = steady_clock::now();
840 796
841 // Filter out non-zero vibrations that are within 10ms of each other. 797 // Filter out non-zero vibrations that are within 10ms of each other.
842 if ((vibration_value.amp_low != 0.0f || vibration_value.amp_high != 0.0f) && 798 if ((vibration_value.low_amplitude != 0.0f || vibration_value.high_amplitude != 0.0f) &&
843 duration_cast<milliseconds>(now - last_vibration_timepoints[npad_index][device_index]) < 799 duration_cast<milliseconds>(
800 now - controller.vibration[device_index].last_vibration_timepoint) <
844 milliseconds(10)) { 801 milliseconds(10)) {
845 return false; 802 return false;
846 } 803 }
847 804
848 last_vibration_timepoints[npad_index][device_index] = now; 805 controller.vibration[device_index].last_vibration_timepoint = now;
849 } 806 }
850 807
851 auto& vibration = vibrations[npad_index][device_index]; 808 Core::HID::VibrationValue vibration{
852 const auto player_vibration_strength = static_cast<f32>(player.vibration_strength); 809 vibration_value.low_amplitude, vibration_value.low_frequency,
853 const auto amp_low = 810 vibration_value.high_amplitude, vibration_value.high_frequency};
854 std::min(vibration_value.amp_low * player_vibration_strength / 100.0f, 1.0f); 811 return controller.device->SetVibration(device_index, vibration);
855 const auto amp_high =
856 std::min(vibration_value.amp_high * player_vibration_strength / 100.0f, 1.0f);
857 return vibration->SetRumblePlay(amp_low, vibration_value.freq_low, amp_high,
858 vibration_value.freq_high);
859} 812}
860 813
861void Controller_NPad::VibrateController(const DeviceHandle& vibration_device_handle, 814void Controller_NPad::VibrateController(
862 const VibrationValue& vibration_value) { 815 const Core::HID::VibrationDeviceHandle& vibration_device_handle,
816 const Core::HID::VibrationValue& vibration_value) {
863 if (!IsDeviceHandleValid(vibration_device_handle)) { 817 if (!IsDeviceHandleValid(vibration_device_handle)) {
864 return; 818 return;
865 } 819 }
@@ -868,42 +822,45 @@ void Controller_NPad::VibrateController(const DeviceHandle& vibration_device_han
868 return; 822 return;
869 } 823 }
870 824
871 const auto npad_index = NPadIdToIndex(vibration_device_handle.npad_id); 825 auto& controller = GetControllerFromHandle(vibration_device_handle);
872 const auto device_index = static_cast<std::size_t>(vibration_device_handle.device_index); 826 const auto device_index = static_cast<std::size_t>(vibration_device_handle.device_index);
873 827
874 if (!vibration_devices_mounted[npad_index][device_index] || 828 if (!controller.vibration[device_index].device_mounted || !controller.device->IsConnected()) {
875 !connected_controllers[npad_index].is_connected) {
876 return; 829 return;
877 } 830 }
878 831
879 if (vibration_device_handle.device_index == DeviceIndex::None) { 832 if (vibration_device_handle.device_index == Core::HID::DeviceIndex::None) {
880 UNREACHABLE_MSG("DeviceIndex should never be None!"); 833 UNREACHABLE_MSG("DeviceIndex should never be None!");
881 return; 834 return;
882 } 835 }
883 836
884 // Some games try to send mismatched parameters in the device handle, block these. 837 // Some games try to send mismatched parameters in the device handle, block these.
885 if ((connected_controllers[npad_index].type == NPadControllerType::JoyLeft && 838 if ((controller.device->GetNpadStyleIndex() == Core::HID::NpadStyleIndex::JoyconLeft &&
886 (vibration_device_handle.npad_type == NpadType::JoyconRight || 839 (vibration_device_handle.npad_type == Core::HID::NpadStyleIndex::JoyconRight ||
887 vibration_device_handle.device_index == DeviceIndex::Right)) || 840 vibration_device_handle.device_index == Core::HID::DeviceIndex::Right)) ||
888 (connected_controllers[npad_index].type == NPadControllerType::JoyRight && 841 (controller.device->GetNpadStyleIndex() == Core::HID::NpadStyleIndex::JoyconRight &&
889 (vibration_device_handle.npad_type == NpadType::JoyconLeft || 842 (vibration_device_handle.npad_type == Core::HID::NpadStyleIndex::JoyconLeft ||
890 vibration_device_handle.device_index == DeviceIndex::Left))) { 843 vibration_device_handle.device_index == Core::HID::DeviceIndex::Left))) {
891 return; 844 return;
892 } 845 }
893 846
894 // Filter out vibrations with equivalent values to reduce unnecessary state changes. 847 // Filter out vibrations with equivalent values to reduce unnecessary state changes.
895 if (vibration_value.amp_low == latest_vibration_values[npad_index][device_index].amp_low && 848 if (vibration_value.low_amplitude ==
896 vibration_value.amp_high == latest_vibration_values[npad_index][device_index].amp_high) { 849 controller.vibration[device_index].latest_vibration_value.low_amplitude &&
850 vibration_value.high_amplitude ==
851 controller.vibration[device_index].latest_vibration_value.high_amplitude) {
897 return; 852 return;
898 } 853 }
899 854
900 if (VibrateControllerAtIndex(npad_index, device_index, vibration_value)) { 855 if (VibrateControllerAtIndex(controller.device->GetNpadIdType(), device_index,
901 latest_vibration_values[npad_index][device_index] = vibration_value; 856 vibration_value)) {
857 controller.vibration[device_index].latest_vibration_value = vibration_value;
902 } 858 }
903} 859}
904 860
905void Controller_NPad::VibrateControllers(const std::vector<DeviceHandle>& vibration_device_handles, 861void Controller_NPad::VibrateControllers(
906 const std::vector<VibrationValue>& vibration_values) { 862 const std::vector<Core::HID::VibrationDeviceHandle>& vibration_device_handles,
863 const std::vector<Core::HID::VibrationValue>& vibration_values) {
907 if (!Settings::values.vibration_enabled.GetValue() && !permit_vibration_session_enabled) { 864 if (!Settings::values.vibration_enabled.GetValue() && !permit_vibration_session_enabled) {
908 return; 865 return;
909 } 866 }
@@ -918,168 +875,285 @@ void Controller_NPad::VibrateControllers(const std::vector<DeviceHandle>& vibrat
918 } 875 }
919} 876}
920 877
921Controller_NPad::VibrationValue Controller_NPad::GetLastVibration( 878Core::HID::VibrationValue Controller_NPad::GetLastVibration(
922 const DeviceHandle& vibration_device_handle) const { 879 const Core::HID::VibrationDeviceHandle& vibration_device_handle) const {
923 if (!IsDeviceHandleValid(vibration_device_handle)) { 880 if (!IsDeviceHandleValid(vibration_device_handle)) {
924 return {}; 881 return {};
925 } 882 }
926 883
927 const auto npad_index = NPadIdToIndex(vibration_device_handle.npad_id); 884 const auto& controller = GetControllerFromHandle(vibration_device_handle);
928 const auto device_index = static_cast<std::size_t>(vibration_device_handle.device_index); 885 const auto device_index = static_cast<std::size_t>(vibration_device_handle.device_index);
929 return latest_vibration_values[npad_index][device_index]; 886 return controller.vibration[device_index].latest_vibration_value;
930} 887}
931 888
932void Controller_NPad::InitializeVibrationDevice(const DeviceHandle& vibration_device_handle) { 889void Controller_NPad::InitializeVibrationDevice(
890 const Core::HID::VibrationDeviceHandle& vibration_device_handle) {
933 if (!IsDeviceHandleValid(vibration_device_handle)) { 891 if (!IsDeviceHandleValid(vibration_device_handle)) {
934 return; 892 return;
935 } 893 }
936 894
937 const auto npad_index = NPadIdToIndex(vibration_device_handle.npad_id); 895 const auto npad_index = static_cast<Core::HID::NpadIdType>(vibration_device_handle.npad_id);
938 const auto device_index = static_cast<std::size_t>(vibration_device_handle.device_index); 896 const auto device_index = static_cast<std::size_t>(vibration_device_handle.device_index);
939 InitializeVibrationDeviceAtIndex(npad_index, device_index); 897 InitializeVibrationDeviceAtIndex(npad_index, device_index);
940} 898}
941 899
942void Controller_NPad::InitializeVibrationDeviceAtIndex(std::size_t npad_index, 900void Controller_NPad::InitializeVibrationDeviceAtIndex(Core::HID::NpadIdType npad_id,
943 std::size_t device_index) { 901 std::size_t device_index) {
902 auto& controller = GetControllerFromNpadIdType(npad_id);
944 if (!Settings::values.vibration_enabled.GetValue()) { 903 if (!Settings::values.vibration_enabled.GetValue()) {
945 vibration_devices_mounted[npad_index][device_index] = false; 904 controller.vibration[device_index].device_mounted = false;
946 return; 905 return;
947 } 906 }
948 907
949 if (vibrations[npad_index][device_index]) { 908 controller.vibration[device_index].device_mounted =
950 vibration_devices_mounted[npad_index][device_index] = 909 controller.device->TestVibration(device_index);
951 vibrations[npad_index][device_index]->GetStatus() == 1;
952 } else {
953 vibration_devices_mounted[npad_index][device_index] = false;
954 }
955} 910}
956 911
957void Controller_NPad::SetPermitVibrationSession(bool permit_vibration_session) { 912void Controller_NPad::SetPermitVibrationSession(bool permit_vibration_session) {
958 permit_vibration_session_enabled = permit_vibration_session; 913 permit_vibration_session_enabled = permit_vibration_session;
959} 914}
960 915
961bool Controller_NPad::IsVibrationDeviceMounted(const DeviceHandle& vibration_device_handle) const { 916bool Controller_NPad::IsVibrationDeviceMounted(
917 const Core::HID::VibrationDeviceHandle& vibration_device_handle) const {
962 if (!IsDeviceHandleValid(vibration_device_handle)) { 918 if (!IsDeviceHandleValid(vibration_device_handle)) {
963 return false; 919 return false;
964 } 920 }
965 921
966 const auto npad_index = NPadIdToIndex(vibration_device_handle.npad_id); 922 const auto& controller = GetControllerFromHandle(vibration_device_handle);
967 const auto device_index = static_cast<std::size_t>(vibration_device_handle.device_index); 923 const auto device_index = static_cast<std::size_t>(vibration_device_handle.device_index);
968 return vibration_devices_mounted[npad_index][device_index]; 924 return controller.vibration[device_index].device_mounted;
969} 925}
970 926
971Kernel::KReadableEvent& Controller_NPad::GetStyleSetChangedEvent(u32 npad_id) { 927Kernel::KReadableEvent& Controller_NPad::GetStyleSetChangedEvent(Core::HID::NpadIdType npad_id) {
972 return styleset_changed_events[NPadIdToIndex(npad_id)]->GetReadableEvent(); 928 if (!IsNpadIdValid(npad_id)) {
929 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
930 // Fallback to player 1
931 const auto& controller = GetControllerFromNpadIdType(Core::HID::NpadIdType::Player1);
932 return controller.styleset_changed_event->GetReadableEvent();
933 }
934
935 const auto& controller = GetControllerFromNpadIdType(npad_id);
936 return controller.styleset_changed_event->GetReadableEvent();
973} 937}
974 938
975void Controller_NPad::SignalStyleSetChangedEvent(u32 npad_id) const { 939void Controller_NPad::SignalStyleSetChangedEvent(Core::HID::NpadIdType npad_id) const {
976 styleset_changed_events[NPadIdToIndex(npad_id)]->GetWritableEvent().Signal(); 940 const auto& controller = GetControllerFromNpadIdType(npad_id);
941 controller.styleset_changed_event->GetWritableEvent().Signal();
977} 942}
978 943
979void Controller_NPad::AddNewControllerAt(NPadControllerType controller, std::size_t npad_index) { 944void Controller_NPad::AddNewControllerAt(Core::HID::NpadStyleIndex controller,
980 UpdateControllerAt(controller, npad_index, true); 945 Core::HID::NpadIdType npad_id) {
946 UpdateControllerAt(controller, npad_id, true);
981} 947}
982 948
983void Controller_NPad::UpdateControllerAt(NPadControllerType controller, std::size_t npad_index, 949void Controller_NPad::UpdateControllerAt(Core::HID::NpadStyleIndex type,
984 bool connected) { 950 Core::HID::NpadIdType npad_id, bool connected) {
951 auto& controller = GetControllerFromNpadIdType(npad_id);
985 if (!connected) { 952 if (!connected) {
986 DisconnectNpadAtIndex(npad_index); 953 DisconnectNpad(npad_id);
987 return;
988 }
989
990 if (controller == NPadControllerType::Handheld && npad_index == HANDHELD_INDEX) {
991 Settings::values.players.GetValue()[HANDHELD_INDEX].controller_type =
992 MapNPadToSettingsType(controller);
993 Settings::values.players.GetValue()[HANDHELD_INDEX].connected = true;
994 connected_controllers[HANDHELD_INDEX] = {controller, true};
995 InitNewlyAddedController(HANDHELD_INDEX);
996 return; 954 return;
997 } 955 }
998 956
999 Settings::values.players.GetValue()[npad_index].controller_type = 957 controller.device->SetNpadStyleIndex(type);
1000 MapNPadToSettingsType(controller); 958 InitNewlyAddedController(npad_id);
1001 Settings::values.players.GetValue()[npad_index].connected = true;
1002 connected_controllers[npad_index] = {controller, true};
1003 InitNewlyAddedController(npad_index);
1004} 959}
1005 960
1006void Controller_NPad::DisconnectNpad(u32 npad_id) { 961void Controller_NPad::DisconnectNpad(Core::HID::NpadIdType npad_id) {
1007 DisconnectNpadAtIndex(NPadIdToIndex(npad_id)); 962 if (!IsNpadIdValid(npad_id)) {
1008} 963 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
964 return;
965 }
1009 966
1010void Controller_NPad::DisconnectNpadAtIndex(std::size_t npad_index) { 967 LOG_DEBUG(Service_HID, "Npad disconnected {}", npad_id);
1011 for (std::size_t device_idx = 0; device_idx < vibrations[npad_index].size(); ++device_idx) { 968 auto& controller = GetControllerFromNpadIdType(npad_id);
969 for (std::size_t device_idx = 0; device_idx < controller.vibration.size(); ++device_idx) {
1012 // Send an empty vibration to stop any vibrations. 970 // Send an empty vibration to stop any vibrations.
1013 VibrateControllerAtIndex(npad_index, device_idx, {}); 971 VibrateControllerAtIndex(npad_id, device_idx, {});
1014 vibration_devices_mounted[npad_index][device_idx] = false; 972 controller.vibration[device_idx].device_mounted = false;
1015 } 973 }
1016 974
1017 Settings::values.players.GetValue()[npad_index].connected = false; 975 auto& shared_memory_entry = controller.shared_memory_entry;
1018 connected_controllers[npad_index].is_connected = false; 976 // Don't reset shared_memory_entry.assignment_mode this value is persistent
1019 977 shared_memory_entry.style_tag.raw = Core::HID::NpadStyleSet::None; // Zero out
1020 auto& controller = shared_memory_entries[npad_index]; 978 shared_memory_entry.device_type.raw = 0;
1021 controller.style_set.raw = 0; // Zero out 979 shared_memory_entry.system_properties.raw = 0;
1022 controller.device_type.raw = 0; 980 shared_memory_entry.button_properties.raw = 0;
1023 controller.system_properties.raw = 0; 981 shared_memory_entry.battery_level_dual = 0;
1024 controller.button_properties.raw = 0; 982 shared_memory_entry.battery_level_left = 0;
1025 controller.battery_level_dual = 0; 983 shared_memory_entry.battery_level_right = 0;
1026 controller.battery_level_left = 0; 984 shared_memory_entry.fullkey_color = {
1027 controller.battery_level_right = 0; 985 .attribute = ColorAttribute::NoController,
1028 controller.fullkey_color = {}; 986 .fullkey = {},
1029 controller.joycon_color = {}; 987 };
1030 controller.assignment_mode = NpadAssignments::Dual; 988 shared_memory_entry.joycon_color = {
1031 controller.footer_type = AppletFooterUiType::None; 989 .attribute = ColorAttribute::NoController,
990 .left = {},
991 .right = {},
992 };
993 shared_memory_entry.applet_footer.type = AppletFooterUiType::None;
994
995 controller.is_dual_left_connected = true;
996 controller.is_dual_right_connected = true;
997 controller.is_connected = false;
998 controller.device->Disconnect();
999 SignalStyleSetChangedEvent(npad_id);
1000 WriteEmptyEntry(controller.shared_memory_entry);
1001}
1032 1002
1033 SignalStyleSetChangedEvent(IndexToNPad(npad_index)); 1003void Controller_NPad::SetGyroscopeZeroDriftMode(Core::HID::SixAxisSensorHandle sixaxis_handle,
1004 GyroscopeZeroDriftMode drift_mode) {
1005 if (!IsDeviceHandleValid(sixaxis_handle)) {
1006 LOG_ERROR(Service_HID, "Invalid handle");
1007 return;
1008 }
1009 auto& controller = GetControllerFromHandle(sixaxis_handle);
1010 controller.gyroscope_zero_drift_mode = drift_mode;
1034} 1011}
1035 1012
1036void Controller_NPad::SetGyroscopeZeroDriftMode(GyroscopeZeroDriftMode drift_mode) { 1013Controller_NPad::GyroscopeZeroDriftMode Controller_NPad::GetGyroscopeZeroDriftMode(
1037 gyroscope_zero_drift_mode = drift_mode; 1014 Core::HID::SixAxisSensorHandle sixaxis_handle) const {
1015 if (!IsDeviceHandleValid(sixaxis_handle)) {
1016 LOG_ERROR(Service_HID, "Invalid handle");
1017 // Return the default value
1018 return GyroscopeZeroDriftMode::Standard;
1019 }
1020 const auto& controller = GetControllerFromHandle(sixaxis_handle);
1021 return controller.gyroscope_zero_drift_mode;
1038} 1022}
1039 1023
1040Controller_NPad::GyroscopeZeroDriftMode Controller_NPad::GetGyroscopeZeroDriftMode() const { 1024bool Controller_NPad::IsSixAxisSensorAtRest(Core::HID::SixAxisSensorHandle sixaxis_handle) const {
1041 return gyroscope_zero_drift_mode; 1025 if (!IsDeviceHandleValid(sixaxis_handle)) {
1026 LOG_ERROR(Service_HID, "Invalid handle");
1027 // Return the default value
1028 return true;
1029 }
1030 const auto& controller = GetControllerFromHandle(sixaxis_handle);
1031 return controller.sixaxis_at_rest;
1042} 1032}
1043 1033
1044bool Controller_NPad::IsSixAxisSensorAtRest() const { 1034void Controller_NPad::SetSixAxisEnabled(Core::HID::SixAxisSensorHandle sixaxis_handle,
1045 return sixaxis_at_rest; 1035 bool sixaxis_status) {
1036 if (!IsDeviceHandleValid(sixaxis_handle)) {
1037 LOG_ERROR(Service_HID, "Invalid handle");
1038 return;
1039 }
1040 auto& controller = GetControllerFromHandle(sixaxis_handle);
1041 controller.sixaxis_sensor_enabled = sixaxis_status;
1046} 1042}
1047 1043
1048void Controller_NPad::SetSixAxisEnabled(bool six_axis_status) { 1044void Controller_NPad::SetSixAxisFusionEnabled(Core::HID::SixAxisSensorHandle sixaxis_handle,
1049 sixaxis_sensors_enabled = six_axis_status; 1045 bool sixaxis_fusion_status) {
1046 if (!IsDeviceHandleValid(sixaxis_handle)) {
1047 LOG_ERROR(Service_HID, "Invalid handle");
1048 return;
1049 }
1050 auto& controller = GetControllerFromHandle(sixaxis_handle);
1051 controller.sixaxis_fusion_enabled = sixaxis_fusion_status;
1050} 1052}
1051 1053
1052void Controller_NPad::SetSixAxisFusionParameters(f32 parameter1, f32 parameter2) { 1054void Controller_NPad::SetSixAxisFusionParameters(
1053 sixaxis_fusion_parameter1 = parameter1; 1055 Core::HID::SixAxisSensorHandle sixaxis_handle,
1054 sixaxis_fusion_parameter2 = parameter2; 1056 Core::HID::SixAxisSensorFusionParameters sixaxis_fusion_parameters) {
1057 if (!IsDeviceHandleValid(sixaxis_handle)) {
1058 LOG_ERROR(Service_HID, "Invalid handle");
1059 return;
1060 }
1061 auto& controller = GetControllerFromHandle(sixaxis_handle);
1062 controller.sixaxis_fusion = sixaxis_fusion_parameters;
1055} 1063}
1056 1064
1057std::pair<f32, f32> Controller_NPad::GetSixAxisFusionParameters() { 1065Core::HID::SixAxisSensorFusionParameters Controller_NPad::GetSixAxisFusionParameters(
1058 return { 1066 Core::HID::SixAxisSensorHandle sixaxis_handle) {
1059 sixaxis_fusion_parameter1, 1067 if (!IsDeviceHandleValid(sixaxis_handle)) {
1060 sixaxis_fusion_parameter2, 1068 LOG_ERROR(Service_HID, "Invalid handle");
1061 }; 1069 // Since these parameters are unknow just return zeros
1070 return {};
1071 }
1072 auto& controller = GetControllerFromHandle(sixaxis_handle);
1073 return controller.sixaxis_fusion;
1062} 1074}
1063 1075
1064void Controller_NPad::ResetSixAxisFusionParameters() { 1076void Controller_NPad::ResetSixAxisFusionParameters(Core::HID::SixAxisSensorHandle sixaxis_handle) {
1065 sixaxis_fusion_parameter1 = 0.0f; 1077 if (!IsDeviceHandleValid(sixaxis_handle)) {
1066 sixaxis_fusion_parameter2 = 0.0f; 1078 LOG_ERROR(Service_HID, "Invalid handle");
1079 return;
1080 }
1081 auto& controller = GetControllerFromHandle(sixaxis_handle);
1082 // Since these parameters are unknow just fill with zeros
1083 controller.sixaxis_fusion = {};
1067} 1084}
1068 1085
1069void Controller_NPad::MergeSingleJoyAsDualJoy(u32 npad_id_1, u32 npad_id_2) { 1086void Controller_NPad::MergeSingleJoyAsDualJoy(Core::HID::NpadIdType npad_id_1,
1070 const auto npad_index_1 = NPadIdToIndex(npad_id_1); 1087 Core::HID::NpadIdType npad_id_2) {
1071 const auto npad_index_2 = NPadIdToIndex(npad_id_2); 1088 if (!IsNpadIdValid(npad_id_1) || !IsNpadIdValid(npad_id_2)) {
1089 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id_1:{}, npad_id_2:{}", npad_id_1,
1090 npad_id_2);
1091 return;
1092 }
1093 auto& controller_1 = GetControllerFromNpadIdType(npad_id_1);
1094 auto& controller_2 = GetControllerFromNpadIdType(npad_id_2);
1095 const auto controller_style_1 = controller_1.device->GetNpadStyleIndex();
1096 const auto controller_style_2 = controller_2.device->GetNpadStyleIndex();
1097 bool merge_controllers = false;
1072 1098
1073 // If the controllers at both npad indices form a pair of left and right joycons, merge them. 1099 // If the controllers at both npad indices form a pair of left and right joycons, merge them.
1074 // Otherwise, do nothing. 1100 // Otherwise, do nothing.
1075 if ((connected_controllers[npad_index_1].type == NPadControllerType::JoyLeft && 1101 if (controller_style_1 == Core::HID::NpadStyleIndex::JoyconLeft &&
1076 connected_controllers[npad_index_2].type == NPadControllerType::JoyRight) || 1102 controller_style_2 == Core::HID::NpadStyleIndex::JoyconRight) {
1077 (connected_controllers[npad_index_2].type == NPadControllerType::JoyLeft && 1103 merge_controllers = true;
1078 connected_controllers[npad_index_1].type == NPadControllerType::JoyRight)) { 1104 }
1105 if (controller_style_2 == Core::HID::NpadStyleIndex::JoyconLeft &&
1106 controller_style_1 == Core::HID::NpadStyleIndex::JoyconRight) {
1107 merge_controllers = true;
1108 }
1109 if (controller_style_1 == Core::HID::NpadStyleIndex::JoyconDual &&
1110 controller_style_2 == Core::HID::NpadStyleIndex::JoyconRight &&
1111 controller_1.is_dual_left_connected && !controller_1.is_dual_right_connected) {
1112 merge_controllers = true;
1113 }
1114 if (controller_style_1 == Core::HID::NpadStyleIndex::JoyconDual &&
1115 controller_style_2 == Core::HID::NpadStyleIndex::JoyconLeft &&
1116 !controller_1.is_dual_left_connected && controller_1.is_dual_right_connected) {
1117 merge_controllers = true;
1118 }
1119 if (controller_style_2 == Core::HID::NpadStyleIndex::JoyconDual &&
1120 controller_style_1 == Core::HID::NpadStyleIndex::JoyconRight &&
1121 controller_2.is_dual_left_connected && !controller_2.is_dual_right_connected) {
1122 merge_controllers = true;
1123 }
1124 if (controller_style_2 == Core::HID::NpadStyleIndex::JoyconDual &&
1125 controller_style_1 == Core::HID::NpadStyleIndex::JoyconLeft &&
1126 !controller_2.is_dual_left_connected && controller_2.is_dual_right_connected) {
1127 merge_controllers = true;
1128 }
1129 if (controller_style_1 == Core::HID::NpadStyleIndex::JoyconDual &&
1130 controller_style_2 == Core::HID::NpadStyleIndex::JoyconDual &&
1131 controller_1.is_dual_left_connected && !controller_1.is_dual_right_connected &&
1132 !controller_2.is_dual_left_connected && controller_2.is_dual_right_connected) {
1133 merge_controllers = true;
1134 }
1135 if (controller_style_1 == Core::HID::NpadStyleIndex::JoyconDual &&
1136 controller_style_2 == Core::HID::NpadStyleIndex::JoyconDual &&
1137 !controller_1.is_dual_left_connected && controller_1.is_dual_right_connected &&
1138 controller_2.is_dual_left_connected && !controller_2.is_dual_right_connected) {
1139 merge_controllers = true;
1140 }
1141
1142 if (merge_controllers) {
1079 // Disconnect the joycon at the second id and connect the dual joycon at the first index. 1143 // Disconnect the joycon at the second id and connect the dual joycon at the first index.
1080 DisconnectNpad(npad_id_2); 1144 DisconnectNpad(npad_id_2);
1081 AddNewControllerAt(NPadControllerType::JoyDual, npad_index_1); 1145 controller_1.is_dual_left_connected = true;
1146 controller_1.is_dual_right_connected = true;
1147 AddNewControllerAt(Core::HID::NpadStyleIndex::JoyconDual, npad_id_1);
1148 return;
1082 } 1149 }
1150 LOG_WARNING(Service_HID,
1151 "Controllers can't be merged npad_id_1:{}, npad_id_2:{}, type_1:{}, type_2:{}, "
1152 "dual_1(left/right):{}/{}, dual_2(left/right):{}/{}",
1153 npad_id_1, npad_id_2, controller_1.device->GetNpadStyleIndex(),
1154 controller_2.device->GetNpadStyleIndex(), controller_1.is_dual_left_connected,
1155 controller_1.is_dual_right_connected, controller_2.is_dual_left_connected,
1156 controller_2.is_dual_right_connected);
1083} 1157}
1084 1158
1085void Controller_NPad::StartLRAssignmentMode() { 1159void Controller_NPad::StartLRAssignmentMode() {
@@ -1092,61 +1166,66 @@ void Controller_NPad::StopLRAssignmentMode() {
1092 is_in_lr_assignment_mode = false; 1166 is_in_lr_assignment_mode = false;
1093} 1167}
1094 1168
1095bool Controller_NPad::SwapNpadAssignment(u32 npad_id_1, u32 npad_id_2) { 1169bool Controller_NPad::SwapNpadAssignment(Core::HID::NpadIdType npad_id_1,
1096 if (npad_id_1 == NPAD_HANDHELD || npad_id_2 == NPAD_HANDHELD || npad_id_1 == NPAD_UNKNOWN || 1170 Core::HID::NpadIdType npad_id_2) {
1097 npad_id_2 == NPAD_UNKNOWN) { 1171 if (!IsNpadIdValid(npad_id_1) || !IsNpadIdValid(npad_id_2)) {
1172 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id_1:{}, npad_id_2:{}", npad_id_1,
1173 npad_id_2);
1174 return false;
1175 }
1176 if (npad_id_1 == Core::HID::NpadIdType::Handheld ||
1177 npad_id_2 == Core::HID::NpadIdType::Handheld || npad_id_1 == Core::HID::NpadIdType::Other ||
1178 npad_id_2 == Core::HID::NpadIdType::Other) {
1098 return true; 1179 return true;
1099 } 1180 }
1100 const auto npad_index_1 = NPadIdToIndex(npad_id_1); 1181 const auto& controller_1 = GetControllerFromNpadIdType(npad_id_1).device;
1101 const auto npad_index_2 = NPadIdToIndex(npad_id_2); 1182 const auto& controller_2 = GetControllerFromNpadIdType(npad_id_2).device;
1102 1183 const auto type_index_1 = controller_1->GetNpadStyleIndex();
1103 if (!IsControllerSupported(connected_controllers[npad_index_1].type) || 1184 const auto type_index_2 = controller_2->GetNpadStyleIndex();
1104 !IsControllerSupported(connected_controllers[npad_index_2].type)) { 1185 const auto is_connected_1 = controller_1->IsConnected();
1186 const auto is_connected_2 = controller_2->IsConnected();
1187
1188 if (!IsControllerSupported(type_index_1) && is_connected_1) {
1189 return false;
1190 }
1191 if (!IsControllerSupported(type_index_2) && is_connected_2) {
1105 return false; 1192 return false;
1106 } 1193 }
1107 1194
1108 std::swap(connected_controllers[npad_index_1].type, connected_controllers[npad_index_2].type); 1195 UpdateControllerAt(type_index_2, npad_id_1, is_connected_2);
1109 1196 UpdateControllerAt(type_index_1, npad_id_2, is_connected_1);
1110 AddNewControllerAt(connected_controllers[npad_index_1].type, npad_index_1);
1111 AddNewControllerAt(connected_controllers[npad_index_2].type, npad_index_2);
1112 1197
1113 return true; 1198 return true;
1114} 1199}
1115 1200
1116Controller_NPad::LedPattern Controller_NPad::GetLedPattern(u32 npad_id) { 1201Core::HID::LedPattern Controller_NPad::GetLedPattern(Core::HID::NpadIdType npad_id) {
1117 if (npad_id == npad_id_list.back() || npad_id == npad_id_list[npad_id_list.size() - 2]) { 1202 if (!IsNpadIdValid(npad_id)) {
1118 // These are controllers without led patterns 1203 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
1119 return LedPattern{0, 0, 0, 0}; 1204 return Core::HID::LedPattern{0, 0, 0, 0};
1120 }
1121 switch (npad_id) {
1122 case 0:
1123 return LedPattern{1, 0, 0, 0};
1124 case 1:
1125 return LedPattern{1, 1, 0, 0};
1126 case 2:
1127 return LedPattern{1, 1, 1, 0};
1128 case 3:
1129 return LedPattern{1, 1, 1, 1};
1130 case 4:
1131 return LedPattern{1, 0, 0, 1};
1132 case 5:
1133 return LedPattern{1, 0, 1, 0};
1134 case 6:
1135 return LedPattern{1, 0, 1, 1};
1136 case 7:
1137 return LedPattern{0, 1, 1, 0};
1138 default:
1139 return LedPattern{0, 0, 0, 0};
1140 } 1205 }
1206 const auto& controller = GetControllerFromNpadIdType(npad_id).device;
1207 return controller->GetLedPattern();
1141} 1208}
1142 1209
1143bool Controller_NPad::IsUnintendedHomeButtonInputProtectionEnabled(u32 npad_id) const { 1210bool Controller_NPad::IsUnintendedHomeButtonInputProtectionEnabled(
1144 return unintended_home_button_input_protection[NPadIdToIndex(npad_id)]; 1211 Core::HID::NpadIdType npad_id) const {
1212 if (!IsNpadIdValid(npad_id)) {
1213 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
1214 // Return the default value
1215 return false;
1216 }
1217 const auto& controller = GetControllerFromNpadIdType(npad_id);
1218 return controller.unintended_home_button_input_protection;
1145} 1219}
1146 1220
1147void Controller_NPad::SetUnintendedHomeButtonInputProtectionEnabled(bool is_protection_enabled, 1221void Controller_NPad::SetUnintendedHomeButtonInputProtectionEnabled(bool is_protection_enabled,
1148 u32 npad_id) { 1222 Core::HID::NpadIdType npad_id) {
1149 unintended_home_button_input_protection[NPadIdToIndex(npad_id)] = is_protection_enabled; 1223 if (!IsNpadIdValid(npad_id)) {
1224 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
1225 return;
1226 }
1227 auto& controller = GetControllerFromNpadIdType(npad_id);
1228 controller.unintended_home_button_input_protection = is_protection_enabled;
1150} 1229}
1151 1230
1152void Controller_NPad::SetAnalogStickUseCenterClamp(bool use_center_clamp) { 1231void Controller_NPad::SetAnalogStickUseCenterClamp(bool use_center_clamp) {
@@ -1154,49 +1233,51 @@ void Controller_NPad::SetAnalogStickUseCenterClamp(bool use_center_clamp) {
1154} 1233}
1155 1234
1156void Controller_NPad::ClearAllConnectedControllers() { 1235void Controller_NPad::ClearAllConnectedControllers() {
1157 for (auto& controller : connected_controllers) { 1236 for (auto& controller : controller_data) {
1158 if (controller.is_connected && controller.type != NPadControllerType::None) { 1237 if (controller.device->IsConnected() &&
1159 controller.type = NPadControllerType::None; 1238 controller.device->GetNpadStyleIndex() != Core::HID::NpadStyleIndex::None) {
1160 controller.is_connected = false; 1239 controller.device->Disconnect();
1240 controller.device->SetNpadStyleIndex(Core::HID::NpadStyleIndex::None);
1161 } 1241 }
1162 } 1242 }
1163} 1243}
1164 1244
1165void Controller_NPad::DisconnectAllConnectedControllers() { 1245void Controller_NPad::DisconnectAllConnectedControllers() {
1166 for (auto& controller : connected_controllers) { 1246 for (auto& controller : controller_data) {
1167 controller.is_connected = false; 1247 controller.device->Disconnect();
1168 } 1248 }
1169} 1249}
1170 1250
1171void Controller_NPad::ConnectAllDisconnectedControllers() { 1251void Controller_NPad::ConnectAllDisconnectedControllers() {
1172 for (auto& controller : connected_controllers) { 1252 for (auto& controller : controller_data) {
1173 if (controller.type != NPadControllerType::None && !controller.is_connected) { 1253 if (controller.device->GetNpadStyleIndex() != Core::HID::NpadStyleIndex::None &&
1174 controller.is_connected = true; 1254 !controller.device->IsConnected()) {
1255 controller.device->Connect();
1175 } 1256 }
1176 } 1257 }
1177} 1258}
1178 1259
1179void Controller_NPad::ClearAllControllers() { 1260void Controller_NPad::ClearAllControllers() {
1180 for (auto& controller : connected_controllers) { 1261 for (auto& controller : controller_data) {
1181 controller.type = NPadControllerType::None; 1262 controller.device->Disconnect();
1182 controller.is_connected = false; 1263 controller.device->SetNpadStyleIndex(Core::HID::NpadStyleIndex::None);
1183 } 1264 }
1184} 1265}
1185 1266
1186u32 Controller_NPad::GetAndResetPressState() { 1267Core::HID::NpadButton Controller_NPad::GetAndResetPressState() {
1187 return press_state.exchange(0); 1268 return static_cast<Core::HID::NpadButton>(press_state.exchange(0));
1188} 1269}
1189 1270
1190bool Controller_NPad::IsControllerSupported(NPadControllerType controller) const { 1271bool Controller_NPad::IsControllerSupported(Core::HID::NpadStyleIndex controller) const {
1191 if (controller == NPadControllerType::Handheld) { 1272 if (controller == Core::HID::NpadStyleIndex::Handheld) {
1192 const bool support_handheld = 1273 const bool support_handheld =
1193 std::find(supported_npad_id_types.begin(), supported_npad_id_types.end(), 1274 std::find(supported_npad_id_types.begin(), supported_npad_id_types.end(),
1194 NPAD_HANDHELD) != supported_npad_id_types.end(); 1275 Core::HID::NpadIdType::Handheld) != supported_npad_id_types.end();
1195 // Handheld is not even a supported type, lets stop here 1276 // Handheld is not even a supported type, lets stop here
1196 if (!support_handheld) { 1277 if (!support_handheld) {
1197 return false; 1278 return false;
1198 } 1279 }
1199 // Handheld should not be supported in docked mode 1280 // Handheld shouldn't be supported in docked mode
1200 if (Settings::values.use_docked_mode.GetValue()) { 1281 if (Settings::values.use_docked_mode.GetValue()) {
1201 return false; 1282 return false;
1202 } 1283 }
@@ -1205,20 +1286,31 @@ bool Controller_NPad::IsControllerSupported(NPadControllerType controller) const
1205 } 1286 }
1206 1287
1207 if (std::any_of(supported_npad_id_types.begin(), supported_npad_id_types.end(), 1288 if (std::any_of(supported_npad_id_types.begin(), supported_npad_id_types.end(),
1208 [](u32 npad_id) { return npad_id <= MAX_NPAD_ID; })) { 1289 [](Core::HID::NpadIdType npad_id) {
1290 return npad_id <= Core::HID::NpadIdType::Player8;
1291 })) {
1292 Core::HID::NpadStyleTag style = GetSupportedStyleSet();
1209 switch (controller) { 1293 switch (controller) {
1210 case NPadControllerType::ProController: 1294 case Core::HID::NpadStyleIndex::ProController:
1211 return style.fullkey; 1295 return style.fullkey;
1212 case NPadControllerType::JoyDual: 1296 case Core::HID::NpadStyleIndex::JoyconDual:
1213 return style.joycon_dual; 1297 return style.joycon_dual;
1214 case NPadControllerType::JoyLeft: 1298 case Core::HID::NpadStyleIndex::JoyconLeft:
1215 return style.joycon_left; 1299 return style.joycon_left;
1216 case NPadControllerType::JoyRight: 1300 case Core::HID::NpadStyleIndex::JoyconRight:
1217 return style.joycon_right; 1301 return style.joycon_right;
1218 case NPadControllerType::GameCube: 1302 case Core::HID::NpadStyleIndex::GameCube:
1219 return style.gamecube; 1303 return style.gamecube;
1220 case NPadControllerType::Pokeball: 1304 case Core::HID::NpadStyleIndex::Pokeball:
1221 return style.palma; 1305 return style.palma;
1306 case Core::HID::NpadStyleIndex::NES:
1307 return style.lark;
1308 case Core::HID::NpadStyleIndex::SNES:
1309 return style.lucia;
1310 case Core::HID::NpadStyleIndex::N64:
1311 return style.lagoon;
1312 case Core::HID::NpadStyleIndex::SegaGenesis:
1313 return style.lager;
1222 default: 1314 default:
1223 return false; 1315 return false;
1224 } 1316 }
@@ -1227,4 +1319,48 @@ bool Controller_NPad::IsControllerSupported(NPadControllerType controller) const
1227 return false; 1319 return false;
1228} 1320}
1229 1321
1322Controller_NPad::NpadControllerData& Controller_NPad::GetControllerFromHandle(
1323 const Core::HID::SixAxisSensorHandle& device_handle) {
1324 const auto npad_id = static_cast<Core::HID::NpadIdType>(device_handle.npad_id);
1325 return GetControllerFromNpadIdType(npad_id);
1326}
1327
1328const Controller_NPad::NpadControllerData& Controller_NPad::GetControllerFromHandle(
1329 const Core::HID::SixAxisSensorHandle& device_handle) const {
1330 const auto npad_id = static_cast<Core::HID::NpadIdType>(device_handle.npad_id);
1331 return GetControllerFromNpadIdType(npad_id);
1332}
1333
1334Controller_NPad::NpadControllerData& Controller_NPad::GetControllerFromHandle(
1335 const Core::HID::VibrationDeviceHandle& device_handle) {
1336 const auto npad_id = static_cast<Core::HID::NpadIdType>(device_handle.npad_id);
1337 return GetControllerFromNpadIdType(npad_id);
1338}
1339
1340const Controller_NPad::NpadControllerData& Controller_NPad::GetControllerFromHandle(
1341 const Core::HID::VibrationDeviceHandle& device_handle) const {
1342 const auto npad_id = static_cast<Core::HID::NpadIdType>(device_handle.npad_id);
1343 return GetControllerFromNpadIdType(npad_id);
1344}
1345
1346Controller_NPad::NpadControllerData& Controller_NPad::GetControllerFromNpadIdType(
1347 Core::HID::NpadIdType npad_id) {
1348 if (!IsNpadIdValid(npad_id)) {
1349 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
1350 npad_id = Core::HID::NpadIdType::Player1;
1351 }
1352 const auto npad_index = Core::HID::NpadIdTypeToIndex(npad_id);
1353 return controller_data[npad_index];
1354}
1355
1356const Controller_NPad::NpadControllerData& Controller_NPad::GetControllerFromNpadIdType(
1357 Core::HID::NpadIdType npad_id) const {
1358 if (!IsNpadIdValid(npad_id)) {
1359 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
1360 npad_id = Core::HID::NpadIdType::Player1;
1361 }
1362 const auto npad_index = Core::HID::NpadIdTypeToIndex(npad_id);
1363 return controller_data[npad_index];
1364}
1365
1230} // namespace Service::HID 1366} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/npad.h b/src/core/hle/service/hid/controllers/npad.h
index 9ee146caf..63281cb35 100644
--- a/src/core/hle/service/hid/controllers/npad.h
+++ b/src/core/hle/service/hid/controllers/npad.h
@@ -11,9 +11,14 @@
11#include "common/bit_field.h" 11#include "common/bit_field.h"
12#include "common/common_types.h" 12#include "common/common_types.h"
13#include "common/quaternion.h" 13#include "common/quaternion.h"
14#include "common/settings.h" 14#include "core/hid/hid_types.h"
15#include "core/frontend/input.h"
16#include "core/hle/service/hid/controllers/controller_base.h" 15#include "core/hle/service/hid/controllers/controller_base.h"
16#include "core/hle/service/hid/ring_lifo.h"
17
18namespace Core::HID {
19class EmulatedController;
20enum class ControllerTriggerType;
21} // namespace Core::HID
17 22
18namespace Kernel { 23namespace Kernel {
19class KEvent; 24class KEvent;
@@ -26,12 +31,9 @@ class ServiceContext;
26 31
27namespace Service::HID { 32namespace Service::HID {
28 33
29constexpr u32 NPAD_HANDHELD = 32;
30constexpr u32 NPAD_UNKNOWN = 16; // TODO(ogniK): What is this?
31
32class Controller_NPad final : public ControllerBase { 34class Controller_NPad final : public ControllerBase {
33public: 35public:
34 explicit Controller_NPad(Core::System& system_, 36 explicit Controller_NPad(Core::HID::HIDCore& hid_core_,
35 KernelHelpers::ServiceContext& service_context_); 37 KernelHelpers::ServiceContext& service_context_);
36 ~Controller_NPad() override; 38 ~Controller_NPad() override;
37 39
@@ -48,60 +50,39 @@ public:
48 void OnMotionUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, 50 void OnMotionUpdate(const Core::Timing::CoreTiming& core_timing, u8* data,
49 std::size_t size) override; 51 std::size_t size) override;
50 52
51 // Called when input devices should be loaded 53 // This is nn::hid::GyroscopeZeroDriftMode
52 void OnLoadInputDevices() override;
53
54 enum class NPadControllerType {
55 None,
56 ProController,
57 Handheld,
58 JoyDual,
59 JoyLeft,
60 JoyRight,
61 GameCube,
62 Pokeball,
63 };
64
65 enum class NpadType : u8 {
66 ProController = 3,
67 Handheld = 4,
68 JoyconDual = 5,
69 JoyconLeft = 6,
70 JoyconRight = 7,
71 GameCube = 8,
72 Pokeball = 9,
73 MaxNpadType = 10,
74 };
75
76 enum class DeviceIndex : u8 {
77 Left = 0,
78 Right = 1,
79 None = 2,
80 MaxDeviceIndex = 3,
81 };
82
83 enum class GyroscopeZeroDriftMode : u32 { 54 enum class GyroscopeZeroDriftMode : u32 {
84 Loose = 0, 55 Loose = 0,
85 Standard = 1, 56 Standard = 1,
86 Tight = 2, 57 Tight = 2,
87 }; 58 };
88 59
89 enum class NpadHoldType : u64 { 60 // This is nn::hid::NpadJoyHoldType
61 enum class NpadJoyHoldType : u64 {
90 Vertical = 0, 62 Vertical = 0,
91 Horizontal = 1, 63 Horizontal = 1,
92 }; 64 };
93 65
94 enum class NpadAssignments : u32 { 66 // This is nn::hid::NpadJoyAssignmentMode
67 enum class NpadJoyAssignmentMode : u32 {
95 Dual = 0, 68 Dual = 0,
96 Single = 1, 69 Single = 1,
97 }; 70 };
98 71
72 // This is nn::hid::NpadJoyDeviceType
73 enum class NpadJoyDeviceType : s64 {
74 Left = 0,
75 Right = 1,
76 };
77
78 // This is nn::hid::NpadHandheldActivationMode
99 enum class NpadHandheldActivationMode : u64 { 79 enum class NpadHandheldActivationMode : u64 {
100 Dual = 0, 80 Dual = 0,
101 Single = 1, 81 Single = 1,
102 None = 2, 82 None = 2,
103 }; 83 };
104 84
85 // This is nn::hid::NpadCommunicationMode
105 enum class NpadCommunicationMode : u64 { 86 enum class NpadCommunicationMode : u64 {
106 Mode_5ms = 0, 87 Mode_5ms = 0,
107 Mode_10ms = 1, 88 Mode_10ms = 1,
@@ -109,74 +90,22 @@ public:
109 Default = 3, 90 Default = 3,
110 }; 91 };
111 92
112 struct DeviceHandle { 93 static constexpr Core::HID::VibrationValue DEFAULT_VIBRATION_VALUE{
113 NpadType npad_type; 94 .low_amplitude = 0.0f,
114 u8 npad_id; 95 .low_frequency = 160.0f,
115 DeviceIndex device_index; 96 .high_amplitude = 0.0f,
116 INSERT_PADDING_BYTES_NOINIT(1); 97 .high_frequency = 320.0f,
117 }; 98 };
118 static_assert(sizeof(DeviceHandle) == 4, "DeviceHandle is an invalid size");
119 99
120 struct NpadStyleSet { 100 void SetSupportedStyleSet(Core::HID::NpadStyleTag style_set);
121 union { 101 Core::HID::NpadStyleTag GetSupportedStyleSet() const;
122 u32_le raw{};
123
124 BitField<0, 1, u32> fullkey;
125 BitField<1, 1, u32> handheld;
126 BitField<2, 1, u32> joycon_dual;
127 BitField<3, 1, u32> joycon_left;
128 BitField<4, 1, u32> joycon_right;
129 BitField<5, 1, u32> gamecube;
130 BitField<6, 1, u32> palma;
131 BitField<7, 1, u32> lark;
132 BitField<8, 1, u32> handheld_lark;
133 BitField<9, 1, u32> lucia;
134 BitField<29, 1, u32> system_ext;
135 BitField<30, 1, u32> system;
136 };
137 };
138 static_assert(sizeof(NpadStyleSet) == 4, "NpadStyleSet is an invalid size");
139
140 struct VibrationValue {
141 f32 amp_low;
142 f32 freq_low;
143 f32 amp_high;
144 f32 freq_high;
145 };
146 static_assert(sizeof(VibrationValue) == 0x10, "Vibration is an invalid size");
147
148 static constexpr VibrationValue DEFAULT_VIBRATION_VALUE{
149 .amp_low = 0.0f,
150 .freq_low = 160.0f,
151 .amp_high = 0.0f,
152 .freq_high = 320.0f,
153 };
154
155 struct LedPattern {
156 explicit LedPattern(u64 light1, u64 light2, u64 light3, u64 light4) {
157 position1.Assign(light1);
158 position2.Assign(light2);
159 position3.Assign(light3);
160 position4.Assign(light4);
161 }
162 union {
163 u64 raw{};
164 BitField<0, 1, u64> position1;
165 BitField<1, 1, u64> position2;
166 BitField<2, 1, u64> position3;
167 BitField<3, 1, u64> position4;
168 };
169 };
170
171 void SetSupportedStyleSet(NpadStyleSet style_set);
172 NpadStyleSet GetSupportedStyleSet() const;
173 102
174 void SetSupportedNpadIdTypes(u8* data, std::size_t length); 103 void SetSupportedNpadIdTypes(u8* data, std::size_t length);
175 void GetSupportedNpadIdTypes(u32* data, std::size_t max_length); 104 void GetSupportedNpadIdTypes(u32* data, std::size_t max_length);
176 std::size_t GetSupportedNpadIdTypesSize() const; 105 std::size_t GetSupportedNpadIdTypesSize() const;
177 106
178 void SetHoldType(NpadHoldType joy_hold_type); 107 void SetHoldType(NpadJoyHoldType joy_hold_type);
179 NpadHoldType GetHoldType() const; 108 NpadJoyHoldType GetHoldType() const;
180 109
181 void SetNpadHandheldActivationMode(NpadHandheldActivationMode activation_mode); 110 void SetNpadHandheldActivationMode(NpadHandheldActivationMode activation_mode);
182 NpadHandheldActivationMode GetNpadHandheldActivationMode() const; 111 NpadHandheldActivationMode GetNpadHandheldActivationMode() const;
@@ -184,162 +113,107 @@ public:
184 void SetNpadCommunicationMode(NpadCommunicationMode communication_mode_); 113 void SetNpadCommunicationMode(NpadCommunicationMode communication_mode_);
185 NpadCommunicationMode GetNpadCommunicationMode() const; 114 NpadCommunicationMode GetNpadCommunicationMode() const;
186 115
187 void SetNpadMode(u32 npad_id, NpadAssignments assignment_mode); 116 void SetNpadMode(Core::HID::NpadIdType npad_id, NpadJoyDeviceType npad_device_type,
117 NpadJoyAssignmentMode assignment_mode);
188 118
189 bool VibrateControllerAtIndex(std::size_t npad_index, std::size_t device_index, 119 bool VibrateControllerAtIndex(Core::HID::NpadIdType npad_id, std::size_t device_index,
190 const VibrationValue& vibration_value); 120 const Core::HID::VibrationValue& vibration_value);
191 121
192 void VibrateController(const DeviceHandle& vibration_device_handle, 122 void VibrateController(const Core::HID::VibrationDeviceHandle& vibration_device_handle,
193 const VibrationValue& vibration_value); 123 const Core::HID::VibrationValue& vibration_value);
194 124
195 void VibrateControllers(const std::vector<DeviceHandle>& vibration_device_handles, 125 void VibrateControllers(
196 const std::vector<VibrationValue>& vibration_values); 126 const std::vector<Core::HID::VibrationDeviceHandle>& vibration_device_handles,
127 const std::vector<Core::HID::VibrationValue>& vibration_values);
197 128
198 VibrationValue GetLastVibration(const DeviceHandle& vibration_device_handle) const; 129 Core::HID::VibrationValue GetLastVibration(
130 const Core::HID::VibrationDeviceHandle& vibration_device_handle) const;
199 131
200 void InitializeVibrationDevice(const DeviceHandle& vibration_device_handle); 132 void InitializeVibrationDevice(const Core::HID::VibrationDeviceHandle& vibration_device_handle);
201 133
202 void InitializeVibrationDeviceAtIndex(std::size_t npad_index, std::size_t device_index); 134 void InitializeVibrationDeviceAtIndex(Core::HID::NpadIdType npad_id, std::size_t device_index);
203 135
204 void SetPermitVibrationSession(bool permit_vibration_session); 136 void SetPermitVibrationSession(bool permit_vibration_session);
205 137
206 bool IsVibrationDeviceMounted(const DeviceHandle& vibration_device_handle) const; 138 bool IsVibrationDeviceMounted(
139 const Core::HID::VibrationDeviceHandle& vibration_device_handle) const;
207 140
208 Kernel::KReadableEvent& GetStyleSetChangedEvent(u32 npad_id); 141 Kernel::KReadableEvent& GetStyleSetChangedEvent(Core::HID::NpadIdType npad_id);
209 void SignalStyleSetChangedEvent(u32 npad_id) const; 142 void SignalStyleSetChangedEvent(Core::HID::NpadIdType npad_id) const;
210 143
211 // Adds a new controller at an index. 144 // Adds a new controller at an index.
212 void AddNewControllerAt(NPadControllerType controller, std::size_t npad_index); 145 void AddNewControllerAt(Core::HID::NpadStyleIndex controller, Core::HID::NpadIdType npad_id);
213 // Adds a new controller at an index with connection status. 146 // Adds a new controller at an index with connection status.
214 void UpdateControllerAt(NPadControllerType controller, std::size_t npad_index, bool connected); 147 void UpdateControllerAt(Core::HID::NpadStyleIndex controller, Core::HID::NpadIdType npad_id,
215 148 bool connected);
216 void DisconnectNpad(u32 npad_id); 149
217 void DisconnectNpadAtIndex(std::size_t index); 150 void DisconnectNpad(Core::HID::NpadIdType npad_id);
218 151
219 void SetGyroscopeZeroDriftMode(GyroscopeZeroDriftMode drift_mode); 152 void SetGyroscopeZeroDriftMode(Core::HID::SixAxisSensorHandle sixaxis_handle,
220 GyroscopeZeroDriftMode GetGyroscopeZeroDriftMode() const; 153 GyroscopeZeroDriftMode drift_mode);
221 bool IsSixAxisSensorAtRest() const; 154 GyroscopeZeroDriftMode GetGyroscopeZeroDriftMode(
222 void SetSixAxisEnabled(bool six_axis_status); 155 Core::HID::SixAxisSensorHandle sixaxis_handle) const;
223 void SetSixAxisFusionParameters(f32 parameter1, f32 parameter2); 156 bool IsSixAxisSensorAtRest(Core::HID::SixAxisSensorHandle sixaxis_handle) const;
224 std::pair<f32, f32> GetSixAxisFusionParameters(); 157 void SetSixAxisEnabled(Core::HID::SixAxisSensorHandle sixaxis_handle, bool sixaxis_status);
225 void ResetSixAxisFusionParameters(); 158 void SetSixAxisFusionEnabled(Core::HID::SixAxisSensorHandle sixaxis_handle,
226 LedPattern GetLedPattern(u32 npad_id); 159 bool sixaxis_fusion_status);
227 bool IsUnintendedHomeButtonInputProtectionEnabled(u32 npad_id) const; 160 void SetSixAxisFusionParameters(
228 void SetUnintendedHomeButtonInputProtectionEnabled(bool is_protection_enabled, u32 npad_id); 161 Core::HID::SixAxisSensorHandle sixaxis_handle,
162 Core::HID::SixAxisSensorFusionParameters sixaxis_fusion_parameters);
163 Core::HID::SixAxisSensorFusionParameters GetSixAxisFusionParameters(
164 Core::HID::SixAxisSensorHandle sixaxis_handle);
165 void ResetSixAxisFusionParameters(Core::HID::SixAxisSensorHandle sixaxis_handle);
166 Core::HID::LedPattern GetLedPattern(Core::HID::NpadIdType npad_id);
167 bool IsUnintendedHomeButtonInputProtectionEnabled(Core::HID::NpadIdType npad_id) const;
168 void SetUnintendedHomeButtonInputProtectionEnabled(bool is_protection_enabled,
169 Core::HID::NpadIdType npad_id);
229 void SetAnalogStickUseCenterClamp(bool use_center_clamp); 170 void SetAnalogStickUseCenterClamp(bool use_center_clamp);
230 void ClearAllConnectedControllers(); 171 void ClearAllConnectedControllers();
231 void DisconnectAllConnectedControllers(); 172 void DisconnectAllConnectedControllers();
232 void ConnectAllDisconnectedControllers(); 173 void ConnectAllDisconnectedControllers();
233 void ClearAllControllers(); 174 void ClearAllControllers();
234 175
235 void MergeSingleJoyAsDualJoy(u32 npad_id_1, u32 npad_id_2); 176 void MergeSingleJoyAsDualJoy(Core::HID::NpadIdType npad_id_1, Core::HID::NpadIdType npad_id_2);
236 void StartLRAssignmentMode(); 177 void StartLRAssignmentMode();
237 void StopLRAssignmentMode(); 178 void StopLRAssignmentMode();
238 bool SwapNpadAssignment(u32 npad_id_1, u32 npad_id_2); 179 bool SwapNpadAssignment(Core::HID::NpadIdType npad_id_1, Core::HID::NpadIdType npad_id_2);
239 180
240 // Logical OR for all buttons presses on all controllers 181 // Logical OR for all buttons presses on all controllers
241 // Specifically for cheat engine and other features. 182 // Specifically for cheat engine and other features.
242 u32 GetAndResetPressState(); 183 Core::HID::NpadButton GetAndResetPressState();
243 184
244 static Controller_NPad::NPadControllerType MapSettingsTypeToNPad(Settings::ControllerType type); 185 static bool IsNpadIdValid(Core::HID::NpadIdType npad_id);
245 static Settings::ControllerType MapNPadToSettingsType(Controller_NPad::NPadControllerType type); 186 static bool IsDeviceHandleValid(const Core::HID::SixAxisSensorHandle& device_handle);
246 static std::size_t NPadIdToIndex(u32 npad_id); 187 static bool IsDeviceHandleValid(const Core::HID::VibrationDeviceHandle& device_handle);
247 static u32 IndexToNPad(std::size_t index);
248 static bool IsNpadIdValid(u32 npad_id);
249 static bool IsDeviceHandleValid(const DeviceHandle& device_handle);
250 188
251private: 189private:
252 struct CommonHeader { 190 // This is nn::hid::detail::ColorAttribute
253 s64_le timestamp; 191 enum class ColorAttribute : u32 {
254 s64_le total_entry_count;
255 s64_le last_entry_index;
256 s64_le entry_count;
257 };
258 static_assert(sizeof(CommonHeader) == 0x20, "CommonHeader is an invalid size");
259
260 enum class ColorAttributes : u32_le {
261 Ok = 0, 192 Ok = 0,
262 ReadError = 1, 193 ReadError = 1,
263 NoController = 2, 194 NoController = 2,
264 }; 195 };
265 static_assert(sizeof(ColorAttributes) == 4, "ColorAttributes is an invalid size"); 196 static_assert(sizeof(ColorAttribute) == 4, "ColorAttribute is an invalid size");
266 197
267 struct ControllerColor { 198 // This is nn::hid::detail::NpadFullKeyColorState
268 u32_le body; 199 struct NpadFullKeyColorState {
269 u32_le button; 200 ColorAttribute attribute;
201 Core::HID::NpadControllerColor fullkey;
270 }; 202 };
271 static_assert(sizeof(ControllerColor) == 8, "ControllerColor is an invalid size"); 203 static_assert(sizeof(NpadFullKeyColorState) == 0xC, "NpadFullKeyColorState is an invalid size");
272 204
273 struct FullKeyColor { 205 // This is nn::hid::detail::NpadJoyColorState
274 ColorAttributes attribute; 206 struct NpadJoyColorState {
275 ControllerColor fullkey; 207 ColorAttribute attribute;
208 Core::HID::NpadControllerColor left;
209 Core::HID::NpadControllerColor right;
276 }; 210 };
277 static_assert(sizeof(FullKeyColor) == 0xC, "FullKeyColor is an invalid size"); 211 static_assert(sizeof(NpadJoyColorState) == 0x14, "NpadJoyColorState is an invalid size");
278 212
279 struct JoyconColor { 213 // This is nn::hid::NpadAttribute
280 ColorAttributes attribute; 214 struct NpadAttribute {
281 ControllerColor left;
282 ControllerColor right;
283 };
284 static_assert(sizeof(JoyconColor) == 0x14, "JoyconColor is an invalid size");
285
286 struct ControllerPadState {
287 union {
288 u64_le raw{};
289 // Button states
290 BitField<0, 1, u64> a;
291 BitField<1, 1, u64> b;
292 BitField<2, 1, u64> x;
293 BitField<3, 1, u64> y;
294 BitField<4, 1, u64> l_stick;
295 BitField<5, 1, u64> r_stick;
296 BitField<6, 1, u64> l;
297 BitField<7, 1, u64> r;
298 BitField<8, 1, u64> zl;
299 BitField<9, 1, u64> zr;
300 BitField<10, 1, u64> plus;
301 BitField<11, 1, u64> minus;
302
303 // D-Pad
304 BitField<12, 1, u64> d_left;
305 BitField<13, 1, u64> d_up;
306 BitField<14, 1, u64> d_right;
307 BitField<15, 1, u64> d_down;
308
309 // Left JoyStick
310 BitField<16, 1, u64> l_stick_left;
311 BitField<17, 1, u64> l_stick_up;
312 BitField<18, 1, u64> l_stick_right;
313 BitField<19, 1, u64> l_stick_down;
314
315 // Right JoyStick
316 BitField<20, 1, u64> r_stick_left;
317 BitField<21, 1, u64> r_stick_up;
318 BitField<22, 1, u64> r_stick_right;
319 BitField<23, 1, u64> r_stick_down;
320
321 // Not always active?
322 BitField<24, 1, u64> left_sl;
323 BitField<25, 1, u64> left_sr;
324
325 BitField<26, 1, u64> right_sl;
326 BitField<27, 1, u64> right_sr;
327
328 BitField<28, 1, u64> palma;
329 BitField<30, 1, u64> handheld_left_b;
330 };
331 };
332 static_assert(sizeof(ControllerPadState) == 8, "ControllerPadState is an invalid size");
333
334 struct AnalogPosition {
335 s32_le x;
336 s32_le y;
337 };
338 static_assert(sizeof(AnalogPosition) == 8, "AnalogPosition is an invalid size");
339
340 struct ConnectionState {
341 union { 215 union {
342 u32_le raw{}; 216 u32 raw{};
343 BitField<0, 1, u32> is_connected; 217 BitField<0, 1, u32> is_connected;
344 BitField<1, 1, u32> is_wired; 218 BitField<1, 1, u32> is_wired;
345 BitField<2, 1, u32> is_left_connected; 219 BitField<2, 1, u32> is_left_connected;
@@ -348,79 +222,60 @@ private:
348 BitField<5, 1, u32> is_right_wired; 222 BitField<5, 1, u32> is_right_wired;
349 }; 223 };
350 }; 224 };
351 static_assert(sizeof(ConnectionState) == 4, "ConnectionState is an invalid size"); 225 static_assert(sizeof(NpadAttribute) == 4, "NpadAttribute is an invalid size");
352 226
353 struct ControllerPad { 227 // This is nn::hid::NpadFullKeyState
354 ControllerPadState pad_states; 228 // This is nn::hid::NpadHandheldState
355 AnalogPosition l_stick; 229 // This is nn::hid::NpadJoyDualState
356 AnalogPosition r_stick; 230 // This is nn::hid::NpadJoyLeftState
357 }; 231 // This is nn::hid::NpadJoyRightState
358 static_assert(sizeof(ControllerPad) == 0x18, "ControllerPad is an invalid size"); 232 // This is nn::hid::NpadPalmaState
359 233 // This is nn::hid::NpadSystemExtState
360 struct GenericStates { 234 struct NPadGenericState {
361 s64_le timestamp; 235 s64_le sampling_number;
362 s64_le timestamp2; 236 Core::HID::NpadButtonState npad_buttons;
363 ControllerPad pad; 237 Core::HID::AnalogStickState l_stick;
364 ConnectionState connection_status; 238 Core::HID::AnalogStickState r_stick;
365 }; 239 NpadAttribute connection_status;
366 static_assert(sizeof(GenericStates) == 0x30, "NPadGenericStates is an invalid size"); 240 INSERT_PADDING_BYTES(4); // Reserved
367
368 struct NPadGeneric {
369 CommonHeader common;
370 std::array<GenericStates, 17> npad;
371 }; 241 };
372 static_assert(sizeof(NPadGeneric) == 0x350, "NPadGeneric is an invalid size"); 242 static_assert(sizeof(NPadGenericState) == 0x28, "NPadGenericState is an invalid size");
373 243
374 struct SixAxisAttributes { 244 // This is nn::hid::SixAxisSensorAttribute
245 struct SixAxisSensorAttribute {
375 union { 246 union {
376 u32_le raw{}; 247 u32 raw{};
377 BitField<0, 1, u32> is_connected; 248 BitField<0, 1, u32> is_connected;
378 BitField<1, 1, u32> is_interpolated; 249 BitField<1, 1, u32> is_interpolated;
379 }; 250 };
380 }; 251 };
381 static_assert(sizeof(SixAxisAttributes) == 4, "SixAxisAttributes is an invalid size"); 252 static_assert(sizeof(SixAxisSensorAttribute) == 4, "SixAxisSensorAttribute is an invalid size");
382 253
383 struct SixAxisStates { 254 // This is nn::hid::SixAxisSensorState
384 s64_le timestamp{}; 255 struct SixAxisSensorState {
385 INSERT_PADDING_WORDS(2); 256 s64 delta_time{};
386 s64_le timestamp2{}; 257 s64 sampling_number{};
387 Common::Vec3f accel{}; 258 Common::Vec3f accel{};
388 Common::Vec3f gyro{}; 259 Common::Vec3f gyro{};
389 Common::Vec3f rotation{}; 260 Common::Vec3f rotation{};
390 std::array<Common::Vec3f, 3> orientation{}; 261 std::array<Common::Vec3f, 3> orientation{};
391 SixAxisAttributes attribute; 262 SixAxisSensorAttribute attribute;
392 INSERT_PADDING_BYTES(4); // Reserved 263 INSERT_PADDING_BYTES(4); // Reserved
393 }; 264 };
394 static_assert(sizeof(SixAxisStates) == 0x68, "SixAxisStates is an invalid size"); 265 static_assert(sizeof(SixAxisSensorState) == 0x60, "SixAxisSensorState is an invalid size");
395
396 struct SixAxisGeneric {
397 CommonHeader common{};
398 std::array<SixAxisStates, 17> sixaxis{};
399 };
400 static_assert(sizeof(SixAxisGeneric) == 0x708, "SixAxisGeneric is an invalid size");
401 266
402 struct TriggerState { 267 // This is nn::hid::server::NpadGcTriggerState
403 s64_le timestamp{}; 268 struct NpadGcTriggerState {
404 s64_le timestamp2{}; 269 s64 sampling_number{};
405 s32_le l_analog{}; 270 s32 l_analog{};
406 s32_le r_analog{}; 271 s32 r_analog{};
407 }; 272 };
408 static_assert(sizeof(TriggerState) == 0x18, "TriggerState is an invalid size"); 273 static_assert(sizeof(NpadGcTriggerState) == 0x10, "NpadGcTriggerState is an invalid size");
409
410 struct TriggerGeneric {
411 INSERT_PADDING_BYTES(0x4);
412 s64_le timestamp;
413 INSERT_PADDING_BYTES(0x4);
414 s64_le total_entry_count;
415 s64_le last_entry_index;
416 s64_le entry_count;
417 std::array<TriggerState, 17> trigger{};
418 };
419 static_assert(sizeof(TriggerGeneric) == 0x1C8, "TriggerGeneric is an invalid size");
420 274
275 // This is nn::hid::NpadSystemProperties
421 struct NPadSystemProperties { 276 struct NPadSystemProperties {
422 union { 277 union {
423 s64_le raw{}; 278 s64 raw{};
424 BitField<0, 1, s64> is_charging_joy_dual; 279 BitField<0, 1, s64> is_charging_joy_dual;
425 BitField<1, 1, s64> is_charging_joy_left; 280 BitField<1, 1, s64> is_charging_joy_left;
426 BitField<2, 1, s64> is_charging_joy_right; 281 BitField<2, 1, s64> is_charging_joy_right;
@@ -438,17 +293,20 @@ private:
438 }; 293 };
439 static_assert(sizeof(NPadSystemProperties) == 0x8, "NPadSystemProperties is an invalid size"); 294 static_assert(sizeof(NPadSystemProperties) == 0x8, "NPadSystemProperties is an invalid size");
440 295
441 struct NPadButtonProperties { 296 // This is nn::hid::NpadSystemButtonProperties
297 struct NpadSystemButtonProperties {
442 union { 298 union {
443 s32_le raw{}; 299 s32 raw{};
444 BitField<0, 1, s32> is_home_button_protection_enabled; 300 BitField<0, 1, s32> is_home_button_protection_enabled;
445 }; 301 };
446 }; 302 };
447 static_assert(sizeof(NPadButtonProperties) == 0x4, "NPadButtonProperties is an invalid size"); 303 static_assert(sizeof(NpadSystemButtonProperties) == 0x4,
304 "NPadButtonProperties is an invalid size");
448 305
449 struct NPadDevice { 306 // This is nn::hid::system::DeviceType
307 struct DeviceType {
450 union { 308 union {
451 u32_le raw{}; 309 u32 raw{};
452 BitField<0, 1, s32> fullkey; 310 BitField<0, 1, s32> fullkey;
453 BitField<1, 1, s32> debug_pad; 311 BitField<1, 1, s32> debug_pad;
454 BitField<2, 1, s32> handheld_left; 312 BitField<2, 1, s32> handheld_left;
@@ -465,26 +323,29 @@ private:
465 BitField<13, 1, s32> handheld_lark_nes_left; 323 BitField<13, 1, s32> handheld_lark_nes_left;
466 BitField<14, 1, s32> handheld_lark_nes_right; 324 BitField<14, 1, s32> handheld_lark_nes_right;
467 BitField<15, 1, s32> lucia; 325 BitField<15, 1, s32> lucia;
326 BitField<16, 1, s32> lagon;
327 BitField<17, 1, s32> lager;
468 BitField<31, 1, s32> system; 328 BitField<31, 1, s32> system;
469 }; 329 };
470 }; 330 };
471 331
472 struct MotionDevice { 332 // This is nn::hid::detail::NfcXcdDeviceHandleStateImpl
473 Common::Vec3f accel; 333 struct NfcXcdDeviceHandleStateImpl {
474 Common::Vec3f gyro; 334 u64 handle;
475 Common::Vec3f rotation; 335 bool is_available;
476 std::array<Common::Vec3f, 3> orientation; 336 bool is_activated;
477 Common::Quaternion<f32> quaternion; 337 INSERT_PADDING_BYTES(0x6); // Reserved
478 }; 338 u64 sampling_number;
479
480 struct NfcXcdHandle {
481 INSERT_PADDING_BYTES(0x60);
482 }; 339 };
340 static_assert(sizeof(NfcXcdDeviceHandleStateImpl) == 0x18,
341 "NfcXcdDeviceHandleStateImpl is an invalid size");
483 342
343 // This is nn::hid::system::AppletFooterUiAttributesSet
484 struct AppletFooterUiAttributes { 344 struct AppletFooterUiAttributes {
485 INSERT_PADDING_BYTES(0x4); 345 INSERT_PADDING_BYTES(0x4);
486 }; 346 };
487 347
348 // This is nn::hid::system::AppletFooterUiType
488 enum class AppletFooterUiType : u8 { 349 enum class AppletFooterUiType : u8 {
489 None = 0, 350 None = 0,
490 HandheldNone = 1, 351 HandheldNone = 1,
@@ -510,95 +371,153 @@ private:
510 Lagon = 21, 371 Lagon = 21,
511 }; 372 };
512 373
513 struct NPadEntry { 374 struct AppletFooterUi {
514 NpadStyleSet style_set; 375 AppletFooterUiAttributes attributes;
515 NpadAssignments assignment_mode; 376 AppletFooterUiType type;
516 FullKeyColor fullkey_color; 377 INSERT_PADDING_BYTES(0x5B); // Reserved
517 JoyconColor joycon_color; 378 };
518 379 static_assert(sizeof(AppletFooterUi) == 0x60, "AppletFooterUi is an invalid size");
519 NPadGeneric fullkey_states; 380
520 NPadGeneric handheld_states; 381 // This is nn::hid::NpadLarkType
521 NPadGeneric joy_dual_states; 382 enum class NpadLarkType : u32 {
522 NPadGeneric joy_left_states; 383 Invalid,
523 NPadGeneric joy_right_states; 384 H1,
524 NPadGeneric palma_states; 385 H2,
525 NPadGeneric system_ext_states; 386 NL,
526 SixAxisGeneric sixaxis_fullkey; 387 NR,
527 SixAxisGeneric sixaxis_handheld; 388 };
528 SixAxisGeneric sixaxis_dual_left; 389
529 SixAxisGeneric sixaxis_dual_right; 390 // This is nn::hid::NpadLuciaType
530 SixAxisGeneric sixaxis_left; 391 enum class NpadLuciaType : u32 {
531 SixAxisGeneric sixaxis_right; 392 Invalid,
532 NPadDevice device_type; 393 J,
533 INSERT_PADDING_BYTES(0x4); // reserved 394 E,
395 U,
396 };
397
398 // This is nn::hid::NpadLagonType
399 enum class NpadLagonType : u32 {
400 Invalid,
401 };
402
403 // This is nn::hid::NpadLagerType
404 enum class NpadLagerType : u32 {
405 Invalid,
406 J,
407 E,
408 U,
409 };
410
411 // This is nn::hid::detail::NpadInternalState
412 struct NpadInternalState {
413 Core::HID::NpadStyleTag style_tag;
414 NpadJoyAssignmentMode assignment_mode;
415 NpadFullKeyColorState fullkey_color;
416 NpadJoyColorState joycon_color;
417 Lifo<NPadGenericState, hid_entry_count> fullkey_lifo;
418 Lifo<NPadGenericState, hid_entry_count> handheld_lifo;
419 Lifo<NPadGenericState, hid_entry_count> joy_dual_lifo;
420 Lifo<NPadGenericState, hid_entry_count> joy_left_lifo;
421 Lifo<NPadGenericState, hid_entry_count> joy_right_lifo;
422 Lifo<NPadGenericState, hid_entry_count> palma_lifo;
423 Lifo<NPadGenericState, hid_entry_count> system_ext_lifo;
424 Lifo<SixAxisSensorState, hid_entry_count> sixaxis_fullkey_lifo;
425 Lifo<SixAxisSensorState, hid_entry_count> sixaxis_handheld_lifo;
426 Lifo<SixAxisSensorState, hid_entry_count> sixaxis_dual_left_lifo;
427 Lifo<SixAxisSensorState, hid_entry_count> sixaxis_dual_right_lifo;
428 Lifo<SixAxisSensorState, hid_entry_count> sixaxis_left_lifo;
429 Lifo<SixAxisSensorState, hid_entry_count> sixaxis_right_lifo;
430 DeviceType device_type;
431 INSERT_PADDING_BYTES(0x4); // Reserved
534 NPadSystemProperties system_properties; 432 NPadSystemProperties system_properties;
535 NPadButtonProperties button_properties; 433 NpadSystemButtonProperties button_properties;
536 u32 battery_level_dual; 434 Core::HID::NpadBatteryLevel battery_level_dual;
537 u32 battery_level_left; 435 Core::HID::NpadBatteryLevel battery_level_left;
538 u32 battery_level_right; 436 Core::HID::NpadBatteryLevel battery_level_right;
539 AppletFooterUiAttributes footer_attributes; 437 union {
540 AppletFooterUiType footer_type; 438 Lifo<NfcXcdDeviceHandleStateImpl, 0x2> nfc_xcd_device_lifo{};
541 // nfc_states needs to be checked switchbrew does not match with HW 439 AppletFooterUi applet_footer;
542 NfcXcdHandle nfc_states; 440 };
543 INSERT_PADDING_BYTES(0x8); // Mutex 441 INSERT_PADDING_BYTES(0x20); // Unknown
544 TriggerGeneric gc_trigger_states; 442 Lifo<NpadGcTriggerState, hid_entry_count> gc_trigger_lifo;
545 INSERT_PADDING_BYTES(0xc1f); 443 NpadLarkType lark_type_l_and_main;
546 }; 444 NpadLarkType lark_type_r;
547 static_assert(sizeof(NPadEntry) == 0x5000, "NPadEntry is an invalid size"); 445 NpadLuciaType lucia_type;
548 446 NpadLagonType lagon_type;
549 struct ControllerHolder { 447 NpadLagerType lager_type;
550 NPadControllerType type; 448 // FW 13.x Investigate there is some sort of bitflag related to joycons
551 bool is_connected; 449 INSERT_PADDING_BYTES(0x4);
552 }; 450 INSERT_PADDING_BYTES(0xc08); // Unknown
553 451 };
554 void InitNewlyAddedController(std::size_t controller_idx); 452 static_assert(sizeof(NpadInternalState) == 0x5000, "NpadInternalState is an invalid size");
555 bool IsControllerSupported(NPadControllerType controller) const; 453
556 void RequestPadStateUpdate(u32 npad_id); 454 struct VibrationData {
557 455 bool device_mounted{};
558 std::atomic<u32> press_state{}; 456 Core::HID::VibrationValue latest_vibration_value{};
559 457 std::chrono::steady_clock::time_point last_vibration_timepoint{};
560 NpadStyleSet style{}; 458 };
561 std::array<NPadEntry, 10> shared_memory_entries{}; 459
562 using ButtonArray = std::array< 460 struct NpadControllerData {
563 std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeButton::NUM_BUTTONS_HID>, 461 Core::HID::EmulatedController* device;
564 10>; 462 Kernel::KEvent* styleset_changed_event{};
565 using StickArray = std::array< 463 NpadInternalState shared_memory_entry{};
566 std::array<std::unique_ptr<Input::AnalogDevice>, Settings::NativeAnalog::NUM_STICKS_HID>, 464
567 10>; 465 std::array<VibrationData, 2> vibration{};
568 using VibrationArray = std::array<std::array<std::unique_ptr<Input::VibrationDevice>, 466 bool unintended_home_button_input_protection{};
569 Settings::NativeVibration::NUM_VIBRATIONS_HID>, 467 bool is_connected{};
570 10>; 468
571 using MotionArray = std::array< 469 // Dual joycons can have only one side connected
572 std::array<std::unique_ptr<Input::MotionDevice>, Settings::NativeMotion::NUM_MOTIONS_HID>, 470 bool is_dual_left_connected{true};
573 10>; 471 bool is_dual_right_connected{true};
574 472
473 // Motion parameters
474 bool sixaxis_at_rest{true};
475 bool sixaxis_sensor_enabled{true};
476 bool sixaxis_fusion_enabled{false};
477 Core::HID::SixAxisSensorFusionParameters sixaxis_fusion{};
478 GyroscopeZeroDriftMode gyroscope_zero_drift_mode{GyroscopeZeroDriftMode::Standard};
479
480 // Current pad state
481 NPadGenericState npad_pad_state{};
482 NPadGenericState npad_libnx_state{};
483 NpadGcTriggerState npad_trigger_state{};
484 SixAxisSensorState sixaxis_fullkey_state{};
485 SixAxisSensorState sixaxis_handheld_state{};
486 SixAxisSensorState sixaxis_dual_left_state{};
487 SixAxisSensorState sixaxis_dual_right_state{};
488 SixAxisSensorState sixaxis_left_lifo_state{};
489 SixAxisSensorState sixaxis_right_lifo_state{};
490 int callback_key;
491 };
492
493 void ControllerUpdate(Core::HID::ControllerTriggerType type, std::size_t controller_idx);
494 void InitNewlyAddedController(Core::HID::NpadIdType npad_id);
495 bool IsControllerSupported(Core::HID::NpadStyleIndex controller) const;
496 void RequestPadStateUpdate(Core::HID::NpadIdType npad_id);
497 void WriteEmptyEntry(NpadInternalState& npad);
498
499 NpadControllerData& GetControllerFromHandle(
500 const Core::HID::SixAxisSensorHandle& device_handle);
501 const NpadControllerData& GetControllerFromHandle(
502 const Core::HID::SixAxisSensorHandle& device_handle) const;
503 NpadControllerData& GetControllerFromHandle(
504 const Core::HID::VibrationDeviceHandle& device_handle);
505 const NpadControllerData& GetControllerFromHandle(
506 const Core::HID::VibrationDeviceHandle& device_handle) const;
507 NpadControllerData& GetControllerFromNpadIdType(Core::HID::NpadIdType npad_id);
508 const NpadControllerData& GetControllerFromNpadIdType(Core::HID::NpadIdType npad_id) const;
509
510 std::atomic<u64> press_state{};
511
512 std::array<NpadControllerData, 10> controller_data{};
575 KernelHelpers::ServiceContext& service_context; 513 KernelHelpers::ServiceContext& service_context;
576 std::mutex mutex; 514 std::mutex mutex;
577 ButtonArray buttons; 515 std::vector<Core::HID::NpadIdType> supported_npad_id_types{};
578 StickArray sticks; 516 NpadJoyHoldType hold_type{NpadJoyHoldType::Vertical};
579 VibrationArray vibrations;
580 MotionArray motions;
581 std::vector<u32> supported_npad_id_types{};
582 NpadHoldType hold_type{NpadHoldType::Vertical};
583 NpadHandheldActivationMode handheld_activation_mode{NpadHandheldActivationMode::Dual}; 517 NpadHandheldActivationMode handheld_activation_mode{NpadHandheldActivationMode::Dual};
584 NpadCommunicationMode communication_mode{NpadCommunicationMode::Default}; 518 NpadCommunicationMode communication_mode{NpadCommunicationMode::Default};
585 // Each controller should have their own styleset changed event
586 std::array<Kernel::KEvent*, 10> styleset_changed_events{};
587 std::array<std::array<std::chrono::steady_clock::time_point, 2>, 10>
588 last_vibration_timepoints{};
589 std::array<std::array<VibrationValue, 2>, 10> latest_vibration_values{};
590 bool permit_vibration_session_enabled{false}; 519 bool permit_vibration_session_enabled{false};
591 std::array<std::array<bool, 2>, 10> vibration_devices_mounted{};
592 std::array<ControllerHolder, 10> connected_controllers{};
593 std::array<bool, 10> unintended_home_button_input_protection{};
594 bool analog_stick_use_center_clamp{}; 520 bool analog_stick_use_center_clamp{};
595 GyroscopeZeroDriftMode gyroscope_zero_drift_mode{GyroscopeZeroDriftMode::Standard};
596 bool sixaxis_sensors_enabled{true};
597 f32 sixaxis_fusion_parameter1{};
598 f32 sixaxis_fusion_parameter2{};
599 bool sixaxis_at_rest{true};
600 std::array<ControllerPad, 10> npad_pad_states{};
601 std::array<TriggerState, 10> npad_trigger_states{};
602 bool is_in_lr_assignment_mode{false}; 521 bool is_in_lr_assignment_mode{false};
603}; 522};
604} // namespace Service::HID 523} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/stubbed.cpp b/src/core/hle/service/hid/controllers/stubbed.cpp
index 772c20453..b7d7a5756 100644
--- a/src/core/hle/service/hid/controllers/stubbed.cpp
+++ b/src/core/hle/service/hid/controllers/stubbed.cpp
@@ -5,11 +5,12 @@
5#include <cstring> 5#include <cstring>
6#include "common/common_types.h" 6#include "common/common_types.h"
7#include "core/core_timing.h" 7#include "core/core_timing.h"
8#include "core/hid/hid_core.h"
8#include "core/hle/service/hid/controllers/stubbed.h" 9#include "core/hle/service/hid/controllers/stubbed.h"
9 10
10namespace Service::HID { 11namespace Service::HID {
11 12
12Controller_Stubbed::Controller_Stubbed(Core::System& system_) : ControllerBase{system_} {} 13Controller_Stubbed::Controller_Stubbed(Core::HID::HIDCore& hid_core_) : ControllerBase{hid_core_} {}
13Controller_Stubbed::~Controller_Stubbed() = default; 14Controller_Stubbed::~Controller_Stubbed() = default;
14 15
15void Controller_Stubbed::OnInit() {} 16void Controller_Stubbed::OnInit() {}
@@ -31,10 +32,9 @@ void Controller_Stubbed::OnUpdate(const Core::Timing::CoreTiming& core_timing, u
31 std::memcpy(data + common_offset, &header, sizeof(CommonHeader)); 32 std::memcpy(data + common_offset, &header, sizeof(CommonHeader));
32} 33}
33 34
34void Controller_Stubbed::OnLoadInputDevices() {}
35
36void Controller_Stubbed::SetCommonHeaderOffset(std::size_t off) { 35void Controller_Stubbed::SetCommonHeaderOffset(std::size_t off) {
37 common_offset = off; 36 common_offset = off;
38 smart_update = true; 37 smart_update = true;
39} 38}
39
40} // namespace Service::HID 40} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/stubbed.h b/src/core/hle/service/hid/controllers/stubbed.h
index 21092af0d..0044a4efa 100644
--- a/src/core/hle/service/hid/controllers/stubbed.h
+++ b/src/core/hle/service/hid/controllers/stubbed.h
@@ -10,7 +10,7 @@
10namespace Service::HID { 10namespace Service::HID {
11class Controller_Stubbed final : public ControllerBase { 11class Controller_Stubbed final : public ControllerBase {
12public: 12public:
13 explicit Controller_Stubbed(Core::System& system_); 13 explicit Controller_Stubbed(Core::HID::HIDCore& hid_core_);
14 ~Controller_Stubbed() override; 14 ~Controller_Stubbed() override;
15 15
16 // Called when the controller is initialized 16 // Called when the controller is initialized
@@ -22,12 +22,17 @@ public:
22 // When the controller is requesting an update for the shared memory 22 // When the controller is requesting an update for the shared memory
23 void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t size) override; 23 void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t size) override;
24 24
25 // Called when input devices should be loaded
26 void OnLoadInputDevices() override;
27
28 void SetCommonHeaderOffset(std::size_t off); 25 void SetCommonHeaderOffset(std::size_t off);
29 26
30private: 27private:
28 struct CommonHeader {
29 s64 timestamp;
30 s64 total_entry_count;
31 s64 last_entry_index;
32 s64 entry_count;
33 };
34 static_assert(sizeof(CommonHeader) == 0x20, "CommonHeader is an invalid size");
35
31 bool smart_update{}; 36 bool smart_update{};
32 std::size_t common_offset{}; 37 std::size_t common_offset{};
33}; 38};
diff --git a/src/core/hle/service/hid/controllers/touchscreen.cpp b/src/core/hle/service/hid/controllers/touchscreen.cpp
index 6ef17acc5..48978e5c6 100644
--- a/src/core/hle/service/hid/controllers/touchscreen.cpp
+++ b/src/core/hle/service/hid/controllers/touchscreen.cpp
@@ -7,72 +7,82 @@
7#include "common/common_types.h" 7#include "common/common_types.h"
8#include "common/logging/log.h" 8#include "common/logging/log.h"
9#include "common/settings.h" 9#include "common/settings.h"
10#include "core/core.h"
10#include "core/core_timing.h" 11#include "core/core_timing.h"
11#include "core/frontend/emu_window.h" 12#include "core/frontend/emu_window.h"
12#include "core/frontend/input.h" 13#include "core/hid/emulated_console.h"
14#include "core/hid/hid_core.h"
13#include "core/hle/service/hid/controllers/touchscreen.h" 15#include "core/hle/service/hid/controllers/touchscreen.h"
14 16
15namespace Service::HID { 17namespace Service::HID {
16constexpr std::size_t SHARED_MEMORY_OFFSET = 0x400; 18constexpr std::size_t SHARED_MEMORY_OFFSET = 0x400;
17 19
18Controller_Touchscreen::Controller_Touchscreen(Core::System& system_) : ControllerBase{system_} {} 20Controller_Touchscreen::Controller_Touchscreen(Core::HID::HIDCore& hid_core_)
21 : ControllerBase{hid_core_} {
22 console = hid_core.GetEmulatedConsole();
23}
24
19Controller_Touchscreen::~Controller_Touchscreen() = default; 25Controller_Touchscreen::~Controller_Touchscreen() = default;
20 26
21void Controller_Touchscreen::OnInit() { 27void Controller_Touchscreen::OnInit() {}
22 for (std::size_t id = 0; id < MAX_FINGERS; ++id) {
23 mouse_finger_id[id] = MAX_FINGERS;
24 keyboard_finger_id[id] = MAX_FINGERS;
25 udp_finger_id[id] = MAX_FINGERS;
26 }
27}
28 28
29void Controller_Touchscreen::OnRelease() {} 29void Controller_Touchscreen::OnRelease() {}
30 30
31void Controller_Touchscreen::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, 31void Controller_Touchscreen::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data,
32 std::size_t size) { 32 std::size_t size) {
33 shared_memory.header.timestamp = core_timing.GetCPUTicks(); 33 touch_screen_lifo.timestamp = core_timing.GetCPUTicks();
34 shared_memory.header.total_entry_count = 17;
35 34
36 if (!IsControllerActivated()) { 35 if (!IsControllerActivated()) {
37 shared_memory.header.entry_count = 0; 36 touch_screen_lifo.buffer_count = 0;
38 shared_memory.header.last_entry_index = 0; 37 touch_screen_lifo.buffer_tail = 0;
38 std::memcpy(data, &touch_screen_lifo, sizeof(touch_screen_lifo));
39 return; 39 return;
40 } 40 }
41 shared_memory.header.entry_count = 16;
42 41
43 const auto& last_entry = 42 const auto touch_status = console->GetTouch();
44 shared_memory.shared_memory_entries[shared_memory.header.last_entry_index]; 43 for (std::size_t id = 0; id < MAX_FINGERS; id++) {
45 shared_memory.header.last_entry_index = (shared_memory.header.last_entry_index + 1) % 17; 44 const auto& current_touch = touch_status[id];
46 auto& cur_entry = shared_memory.shared_memory_entries[shared_memory.header.last_entry_index]; 45 auto& finger = fingers[id];
46 finger.position = current_touch.position;
47 finger.id = current_touch.id;
47 48
48 cur_entry.sampling_number = last_entry.sampling_number + 1; 49 if (finger.attribute.start_touch) {
49 cur_entry.sampling_number2 = cur_entry.sampling_number; 50 finger.attribute.raw = 0;
51 continue;
52 }
50 53
51 const Input::TouchStatus& mouse_status = touch_mouse_device->GetStatus(); 54 if (finger.attribute.end_touch) {
52 const Input::TouchStatus& udp_status = touch_udp_device->GetStatus(); 55 finger.attribute.raw = 0;
53 for (std::size_t id = 0; id < mouse_status.size(); ++id) { 56 finger.pressed = false;
54 mouse_finger_id[id] = UpdateTouchInputEvent(mouse_status[id], mouse_finger_id[id]); 57 continue;
55 udp_finger_id[id] = UpdateTouchInputEvent(udp_status[id], udp_finger_id[id]); 58 }
56 } 59
60 if (!finger.pressed && current_touch.pressed) {
61 finger.attribute.start_touch.Assign(1);
62 finger.pressed = true;
63 continue;
64 }
57 65
58 if (Settings::values.use_touch_from_button) { 66 if (finger.pressed && !current_touch.pressed) {
59 const Input::TouchStatus& keyboard_status = touch_btn_device->GetStatus(); 67 finger.attribute.raw = 0;
60 for (std::size_t id = 0; id < mouse_status.size(); ++id) { 68 finger.attribute.end_touch.Assign(1);
61 keyboard_finger_id[id] =
62 UpdateTouchInputEvent(keyboard_status[id], keyboard_finger_id[id]);
63 } 69 }
64 } 70 }
65 71
66 std::array<Finger, 16> active_fingers; 72 std::array<Core::HID::TouchFinger, MAX_FINGERS> active_fingers;
67 const auto end_iter = std::copy_if(fingers.begin(), fingers.end(), active_fingers.begin(), 73 const auto end_iter = std::copy_if(fingers.begin(), fingers.end(), active_fingers.begin(),
68 [](const auto& finger) { return finger.pressed; }); 74 [](const auto& finger) { return finger.pressed; });
69 const auto active_fingers_count = 75 const auto active_fingers_count =
70 static_cast<std::size_t>(std::distance(active_fingers.begin(), end_iter)); 76 static_cast<std::size_t>(std::distance(active_fingers.begin(), end_iter));
71 77
72 const u64 tick = core_timing.GetCPUTicks(); 78 const u64 tick = core_timing.GetCPUTicks();
73 cur_entry.entry_count = static_cast<s32_le>(active_fingers_count); 79 const auto& last_entry = touch_screen_lifo.ReadCurrentEntry().state;
80
81 next_state.sampling_number = last_entry.sampling_number + 1;
82 next_state.entry_count = static_cast<s32>(active_fingers_count);
83
74 for (std::size_t id = 0; id < MAX_FINGERS; ++id) { 84 for (std::size_t id = 0; id < MAX_FINGERS; ++id) {
75 auto& touch_entry = cur_entry.states[id]; 85 auto& touch_entry = next_state.states[id];
76 if (id < active_fingers_count) { 86 if (id < active_fingers_count) {
77 const auto& [active_x, active_y] = active_fingers[id].position; 87 const auto& [active_x, active_y] = active_fingers[id].position;
78 touch_entry.position = { 88 touch_entry.position = {
@@ -97,66 +107,9 @@ void Controller_Touchscreen::OnUpdate(const Core::Timing::CoreTiming& core_timin
97 touch_entry.finger = 0; 107 touch_entry.finger = 0;
98 } 108 }
99 } 109 }
100 std::memcpy(data + SHARED_MEMORY_OFFSET, &shared_memory, sizeof(TouchScreenSharedMemory));
101}
102
103void Controller_Touchscreen::OnLoadInputDevices() {
104 touch_mouse_device = Input::CreateDevice<Input::TouchDevice>("engine:emu_window");
105 touch_udp_device = Input::CreateDevice<Input::TouchDevice>("engine:cemuhookudp");
106 touch_btn_device = Input::CreateDevice<Input::TouchDevice>("engine:touch_from_button");
107}
108
109std::optional<std::size_t> Controller_Touchscreen::GetUnusedFingerID() const {
110 // Dont assign any touch input to a finger if disabled
111 if (!Settings::values.touchscreen.enabled) {
112 return std::nullopt;
113 }
114 std::size_t first_free_id = 0;
115 while (first_free_id < MAX_FINGERS) {
116 if (!fingers[first_free_id].pressed) {
117 return first_free_id;
118 } else {
119 first_free_id++;
120 }
121 }
122 return std::nullopt;
123}
124
125std::size_t Controller_Touchscreen::UpdateTouchInputEvent(
126 const std::tuple<float, float, bool>& touch_input, std::size_t finger_id) {
127 const auto& [x, y, pressed] = touch_input;
128 if (finger_id > MAX_FINGERS) {
129 LOG_ERROR(Service_HID, "Invalid finger id {}", finger_id);
130 return MAX_FINGERS;
131 }
132 if (pressed) {
133 Attributes attribute{};
134 if (finger_id == MAX_FINGERS) {
135 const auto first_free_id = GetUnusedFingerID();
136 if (!first_free_id) {
137 // Invalid finger id do nothing
138 return MAX_FINGERS;
139 }
140 finger_id = first_free_id.value();
141 fingers[finger_id].pressed = true;
142 fingers[finger_id].id = static_cast<u32_le>(finger_id);
143 attribute.start_touch.Assign(1);
144 }
145 fingers[finger_id].position = {x, y};
146 fingers[finger_id].attribute = attribute;
147 return finger_id;
148 }
149
150 if (finger_id != MAX_FINGERS) {
151 if (!fingers[finger_id].attribute.end_touch) {
152 fingers[finger_id].attribute.end_touch.Assign(1);
153 fingers[finger_id].attribute.start_touch.Assign(0);
154 return finger_id;
155 }
156 fingers[finger_id].pressed = false;
157 }
158 110
159 return MAX_FINGERS; 111 touch_screen_lifo.WriteNextEntry(next_state);
112 std::memcpy(data + SHARED_MEMORY_OFFSET, &touch_screen_lifo, sizeof(touch_screen_lifo));
160} 113}
161 114
162} // namespace Service::HID 115} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/touchscreen.h b/src/core/hle/service/hid/controllers/touchscreen.h
index 8e9b40c0a..708dde4f0 100644
--- a/src/core/hle/service/hid/controllers/touchscreen.h
+++ b/src/core/hle/service/hid/controllers/touchscreen.h
@@ -9,18 +9,25 @@
9#include "common/common_types.h" 9#include "common/common_types.h"
10#include "common/point.h" 10#include "common/point.h"
11#include "common/swap.h" 11#include "common/swap.h"
12#include "core/frontend/input.h" 12#include "core/hid/hid_types.h"
13#include "core/hle/service/hid/controllers/controller_base.h" 13#include "core/hle/service/hid/controllers/controller_base.h"
14#include "core/hle/service/hid/ring_lifo.h"
15
16namespace Core::HID {
17class EmulatedConsole;
18} // namespace Core::HID
14 19
15namespace Service::HID { 20namespace Service::HID {
16class Controller_Touchscreen final : public ControllerBase { 21class Controller_Touchscreen final : public ControllerBase {
17public: 22public:
23 // This is nn::hid::TouchScreenModeForNx
18 enum class TouchScreenModeForNx : u8 { 24 enum class TouchScreenModeForNx : u8 {
19 UseSystemSetting, 25 UseSystemSetting,
20 Finger, 26 Finger,
21 Heat2, 27 Heat2,
22 }; 28 };
23 29
30 // This is nn::hid::TouchScreenConfigurationForNx
24 struct TouchScreenConfigurationForNx { 31 struct TouchScreenConfigurationForNx {
25 TouchScreenModeForNx mode; 32 TouchScreenModeForNx mode;
26 INSERT_PADDING_BYTES_NOINIT(0x7); 33 INSERT_PADDING_BYTES_NOINIT(0x7);
@@ -29,7 +36,7 @@ public:
29 static_assert(sizeof(TouchScreenConfigurationForNx) == 0x17, 36 static_assert(sizeof(TouchScreenConfigurationForNx) == 0x17,
30 "TouchScreenConfigurationForNx is an invalid size"); 37 "TouchScreenConfigurationForNx is an invalid size");
31 38
32 explicit Controller_Touchscreen(Core::System& system_); 39 explicit Controller_Touchscreen(Core::HID::HIDCore& hid_core_);
33 ~Controller_Touchscreen() override; 40 ~Controller_Touchscreen() override;
34 41
35 // Called when the controller is initialized 42 // Called when the controller is initialized
@@ -41,73 +48,24 @@ public:
41 // When the controller is requesting an update for the shared memory 48 // When the controller is requesting an update for the shared memory
42 void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t size) override; 49 void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t size) override;
43 50
44 // Called when input devices should be loaded
45 void OnLoadInputDevices() override;
46
47private: 51private:
48 static constexpr std::size_t MAX_FINGERS = 16; 52 static constexpr std::size_t MAX_FINGERS = 16;
49 53
50 // Returns an unused finger id, if there is no fingers available std::nullopt will be returned 54 // This is nn::hid::TouchScreenState
51 std::optional<std::size_t> GetUnusedFingerID() const; 55 struct TouchScreenState {
52 56 s64 sampling_number;
53 // If the touch is new it tries to assing a new finger id, if there is no fingers avaliable no 57 s32 entry_count;
54 // changes will be made. Updates the coordinates if the finger id it's already set. If the touch 58 INSERT_PADDING_BYTES(4); // Reserved
55 // ends delays the output by one frame to set the end_touch flag before finally freeing the 59 std::array<Core::HID::TouchState, MAX_FINGERS> states;
56 // finger id
57 std::size_t UpdateTouchInputEvent(const std::tuple<float, float, bool>& touch_input,
58 std::size_t finger_id);
59
60 struct Attributes {
61 union {
62 u32 raw{};
63 BitField<0, 1, u32> start_touch;
64 BitField<1, 1, u32> end_touch;
65 };
66 };
67 static_assert(sizeof(Attributes) == 0x4, "Attributes is an invalid size");
68
69 struct TouchState {
70 u64_le delta_time;
71 Attributes attribute;
72 u32_le finger;
73 Common::Point<u32_le> position;
74 u32_le diameter_x;
75 u32_le diameter_y;
76 u32_le rotation_angle;
77 }; 60 };
78 static_assert(sizeof(TouchState) == 0x28, "Touchstate is an invalid size"); 61 static_assert(sizeof(TouchScreenState) == 0x290, "TouchScreenState is an invalid size");
79 62
80 struct TouchScreenEntry { 63 // This is nn::hid::detail::TouchScreenLifo
81 s64_le sampling_number; 64 Lifo<TouchScreenState, hid_entry_count> touch_screen_lifo{};
82 s64_le sampling_number2; 65 static_assert(sizeof(touch_screen_lifo) == 0x2C38, "touch_screen_lifo is an invalid size");
83 s32_le entry_count; 66 TouchScreenState next_state{};
84 std::array<TouchState, MAX_FINGERS> states;
85 };
86 static_assert(sizeof(TouchScreenEntry) == 0x298, "TouchScreenEntry is an invalid size");
87
88 struct TouchScreenSharedMemory {
89 CommonHeader header;
90 std::array<TouchScreenEntry, 17> shared_memory_entries{};
91 INSERT_PADDING_BYTES(0x3c8);
92 };
93 static_assert(sizeof(TouchScreenSharedMemory) == 0x3000,
94 "TouchScreenSharedMemory is an invalid size");
95
96 struct Finger {
97 u64_le last_touch{};
98 Common::Point<float> position;
99 u32_le id{};
100 bool pressed{};
101 Attributes attribute;
102 };
103 67
104 TouchScreenSharedMemory shared_memory{}; 68 std::array<Core::HID::TouchFinger, MAX_FINGERS> fingers;
105 std::unique_ptr<Input::TouchDevice> touch_mouse_device; 69 Core::HID::EmulatedConsole* console;
106 std::unique_ptr<Input::TouchDevice> touch_udp_device;
107 std::unique_ptr<Input::TouchDevice> touch_btn_device;
108 std::array<std::size_t, MAX_FINGERS> mouse_finger_id;
109 std::array<std::size_t, MAX_FINGERS> keyboard_finger_id;
110 std::array<std::size_t, MAX_FINGERS> udp_finger_id;
111 std::array<Finger, MAX_FINGERS> fingers;
112}; 70};
113} // namespace Service::HID 71} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/xpad.cpp b/src/core/hle/service/hid/controllers/xpad.cpp
index 41dc22cf9..e4da16466 100644
--- a/src/core/hle/service/hid/controllers/xpad.cpp
+++ b/src/core/hle/service/hid/controllers/xpad.cpp
@@ -5,12 +5,13 @@
5#include <cstring> 5#include <cstring>
6#include "common/common_types.h" 6#include "common/common_types.h"
7#include "core/core_timing.h" 7#include "core/core_timing.h"
8#include "core/hid/hid_core.h"
8#include "core/hle/service/hid/controllers/xpad.h" 9#include "core/hle/service/hid/controllers/xpad.h"
9 10
10namespace Service::HID { 11namespace Service::HID {
11constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3C00; 12constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3C00;
12 13
13Controller_XPad::Controller_XPad(Core::System& system_) : ControllerBase{system_} {} 14Controller_XPad::Controller_XPad(Core::HID::HIDCore& hid_core_) : ControllerBase{hid_core_} {}
14Controller_XPad::~Controller_XPad() = default; 15Controller_XPad::~Controller_XPad() = default;
15 16
16void Controller_XPad::OnInit() {} 17void Controller_XPad::OnInit() {}
@@ -19,28 +20,19 @@ void Controller_XPad::OnRelease() {}
19 20
20void Controller_XPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, 21void Controller_XPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data,
21 std::size_t size) { 22 std::size_t size) {
22 for (auto& xpad_entry : shared_memory.shared_memory_entries) { 23 if (!IsControllerActivated()) {
23 xpad_entry.header.timestamp = core_timing.GetCPUTicks(); 24 basic_xpad_lifo.buffer_count = 0;
24 xpad_entry.header.total_entry_count = 17; 25 basic_xpad_lifo.buffer_tail = 0;
25 26 std::memcpy(data + SHARED_MEMORY_OFFSET, &basic_xpad_lifo, sizeof(basic_xpad_lifo));
26 if (!IsControllerActivated()) { 27 return;
27 xpad_entry.header.entry_count = 0;
28 xpad_entry.header.last_entry_index = 0;
29 return;
30 }
31 xpad_entry.header.entry_count = 16;
32
33 const auto& last_entry = xpad_entry.pad_states[xpad_entry.header.last_entry_index];
34 xpad_entry.header.last_entry_index = (xpad_entry.header.last_entry_index + 1) % 17;
35 auto& cur_entry = xpad_entry.pad_states[xpad_entry.header.last_entry_index];
36
37 cur_entry.sampling_number = last_entry.sampling_number + 1;
38 cur_entry.sampling_number2 = cur_entry.sampling_number;
39 } 28 }
29
30 const auto& last_entry = basic_xpad_lifo.ReadCurrentEntry().state;
31 next_state.sampling_number = last_entry.sampling_number + 1;
40 // TODO(ogniK): Update xpad states 32 // TODO(ogniK): Update xpad states
41 33
42 std::memcpy(data + SHARED_MEMORY_OFFSET, &shared_memory, sizeof(SharedMemory)); 34 basic_xpad_lifo.WriteNextEntry(next_state);
35 std::memcpy(data + SHARED_MEMORY_OFFSET, &basic_xpad_lifo, sizeof(basic_xpad_lifo));
43} 36}
44 37
45void Controller_XPad::OnLoadInputDevices() {}
46} // namespace Service::HID 38} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/xpad.h b/src/core/hle/service/hid/controllers/xpad.h
index f9ab5facf..ba8db8d9d 100644
--- a/src/core/hle/service/hid/controllers/xpad.h
+++ b/src/core/hle/service/hid/controllers/xpad.h
@@ -8,12 +8,14 @@
8#include "common/common_funcs.h" 8#include "common/common_funcs.h"
9#include "common/common_types.h" 9#include "common/common_types.h"
10#include "common/swap.h" 10#include "common/swap.h"
11#include "core/hid/hid_types.h"
11#include "core/hle/service/hid/controllers/controller_base.h" 12#include "core/hle/service/hid/controllers/controller_base.h"
13#include "core/hle/service/hid/ring_lifo.h"
12 14
13namespace Service::HID { 15namespace Service::HID {
14class Controller_XPad final : public ControllerBase { 16class Controller_XPad final : public ControllerBase {
15public: 17public:
16 explicit Controller_XPad(Core::System& system_); 18 explicit Controller_XPad(Core::HID::HIDCore& hid_core_);
17 ~Controller_XPad() override; 19 ~Controller_XPad() override;
18 20
19 // Called when the controller is initialized 21 // Called when the controller is initialized
@@ -25,13 +27,11 @@ public:
25 // When the controller is requesting an update for the shared memory 27 // When the controller is requesting an update for the shared memory
26 void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t size) override; 28 void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t size) override;
27 29
28 // Called when input devices should be loaded
29 void OnLoadInputDevices() override;
30
31private: 30private:
32 struct Attributes { 31 // This is nn::hid::BasicXpadAttributeSet
32 struct BasicXpadAttributeSet {
33 union { 33 union {
34 u32_le raw{}; 34 u32 raw{};
35 BitField<0, 1, u32> is_connected; 35 BitField<0, 1, u32> is_connected;
36 BitField<1, 1, u32> is_wired; 36 BitField<1, 1, u32> is_wired;
37 BitField<2, 1, u32> is_left_connected; 37 BitField<2, 1, u32> is_left_connected;
@@ -40,11 +40,12 @@ private:
40 BitField<5, 1, u32> is_right_wired; 40 BitField<5, 1, u32> is_right_wired;
41 }; 41 };
42 }; 42 };
43 static_assert(sizeof(Attributes) == 4, "Attributes is an invalid size"); 43 static_assert(sizeof(BasicXpadAttributeSet) == 4, "BasicXpadAttributeSet is an invalid size");
44 44
45 struct Buttons { 45 // This is nn::hid::BasicXpadButtonSet
46 struct BasicXpadButtonSet {
46 union { 47 union {
47 u32_le raw{}; 48 u32 raw{};
48 // Button states 49 // Button states
49 BitField<0, 1, u32> a; 50 BitField<0, 1, u32> a;
50 BitField<1, 1, u32> b; 51 BitField<1, 1, u32> b;
@@ -88,35 +89,21 @@ private:
88 BitField<30, 1, u32> handheld_left_b; 89 BitField<30, 1, u32> handheld_left_b;
89 }; 90 };
90 }; 91 };
91 static_assert(sizeof(Buttons) == 4, "Buttons is an invalid size"); 92 static_assert(sizeof(BasicXpadButtonSet) == 4, "BasicXpadButtonSet is an invalid size");
92 93
93 struct AnalogStick { 94 // This is nn::hid::detail::BasicXpadState
94 s32_le x; 95 struct BasicXpadState {
95 s32_le y; 96 s64 sampling_number;
96 }; 97 BasicXpadAttributeSet attributes;
97 static_assert(sizeof(AnalogStick) == 0x8, "AnalogStick is an invalid size"); 98 BasicXpadButtonSet pad_states;
98 99 Core::HID::AnalogStickState l_stick;
99 struct XPadState { 100 Core::HID::AnalogStickState r_stick;
100 s64_le sampling_number;
101 s64_le sampling_number2;
102 Attributes attributes;
103 Buttons pad_states;
104 AnalogStick l_stick;
105 AnalogStick r_stick;
106 }; 101 };
107 static_assert(sizeof(XPadState) == 0x28, "XPadState is an invalid size"); 102 static_assert(sizeof(BasicXpadState) == 0x20, "BasicXpadState is an invalid size");
108 103
109 struct XPadEntry { 104 // This is nn::hid::detail::BasicXpadLifo
110 CommonHeader header; 105 Lifo<BasicXpadState, hid_entry_count> basic_xpad_lifo{};
111 std::array<XPadState, 17> pad_states{}; 106 static_assert(sizeof(basic_xpad_lifo) == 0x2C8, "basic_xpad_lifo is an invalid size");
112 INSERT_PADDING_BYTES(0x138); 107 BasicXpadState next_state{};
113 };
114 static_assert(sizeof(XPadEntry) == 0x400, "XPadEntry is an invalid size");
115
116 struct SharedMemory {
117 std::array<XPadEntry, 4> shared_memory_entries{};
118 };
119 static_assert(sizeof(SharedMemory) == 0x1000, "SharedMemory is an invalid size");
120 SharedMemory shared_memory{};
121}; 108};
122} // namespace Service::HID 109} // namespace Service::HID
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp
index 10c64d41a..7163e1a4e 100644
--- a/src/core/hle/service/hid/hid.cpp
+++ b/src/core/hle/service/hid/hid.cpp
@@ -8,7 +8,7 @@
8#include "common/settings.h" 8#include "common/settings.h"
9#include "core/core.h" 9#include "core/core.h"
10#include "core/core_timing.h" 10#include "core/core_timing.h"
11#include "core/frontend/input.h" 11#include "core/hid/hid_core.h"
12#include "core/hle/ipc_helpers.h" 12#include "core/hle/ipc_helpers.h"
13#include "core/hle/kernel/k_readable_event.h" 13#include "core/hle/kernel/k_readable_event.h"
14#include "core/hle/kernel/k_shared_memory.h" 14#include "core/hle/kernel/k_shared_memory.h"
@@ -34,10 +34,10 @@
34namespace Service::HID { 34namespace Service::HID {
35 35
36// Updating period for each HID device. 36// Updating period for each HID device.
37// HID is polled every 15ms, this value was derived from 37// Period time is obtained by measuring the number of samples in a second on HW using a homebrew
38// https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering#joy-con-status-data-packet 38constexpr auto pad_update_ns = std::chrono::nanoseconds{4 * 1000 * 1000}; // (4ms, 250Hz)
39constexpr auto pad_update_ns = std::chrono::nanoseconds{1000 * 1000}; // (1ms, 1000Hz) 39constexpr auto mouse_keyboard_update_ns = std::chrono::nanoseconds{8 * 1000 * 1000}; // (8ms, 125Hz)
40constexpr auto motion_update_ns = std::chrono::nanoseconds{15 * 1000 * 1000}; // (15ms, 66.666Hz) 40constexpr auto motion_update_ns = std::chrono::nanoseconds{5 * 1000 * 1000}; // (5ms, 200Hz)
41constexpr std::size_t SHARED_MEMORY_SIZE = 0x40000; 41constexpr std::size_t SHARED_MEMORY_SIZE = 0x40000;
42 42
43IAppletResource::IAppletResource(Core::System& system_, 43IAppletResource::IAppletResource(Core::System& system_,
@@ -79,17 +79,24 @@ IAppletResource::IAppletResource(Core::System& system_,
79 const auto guard = LockService(); 79 const auto guard = LockService();
80 UpdateControllers(user_data, ns_late); 80 UpdateControllers(user_data, ns_late);
81 }); 81 });
82 mouse_keyboard_update_event = Core::Timing::CreateEvent(
83 "HID::UpdateMouseKeyboardCallback",
84 [this](std::uintptr_t user_data, std::chrono::nanoseconds ns_late) {
85 const auto guard = LockService();
86 UpdateMouseKeyboard(user_data, ns_late);
87 });
82 motion_update_event = Core::Timing::CreateEvent( 88 motion_update_event = Core::Timing::CreateEvent(
83 "HID::MotionPadCallback", 89 "HID::UpdateMotionCallback",
84 [this](std::uintptr_t user_data, std::chrono::nanoseconds ns_late) { 90 [this](std::uintptr_t user_data, std::chrono::nanoseconds ns_late) {
85 const auto guard = LockService(); 91 const auto guard = LockService();
86 UpdateMotion(user_data, ns_late); 92 UpdateMotion(user_data, ns_late);
87 }); 93 });
88 94
89 system.CoreTiming().ScheduleEvent(pad_update_ns, pad_update_event); 95 system.CoreTiming().ScheduleEvent(pad_update_ns, pad_update_event);
96 system.CoreTiming().ScheduleEvent(mouse_keyboard_update_ns, mouse_keyboard_update_event);
90 system.CoreTiming().ScheduleEvent(motion_update_ns, motion_update_event); 97 system.CoreTiming().ScheduleEvent(motion_update_ns, motion_update_event);
91 98
92 ReloadInputDevices(); 99 system.HIDCore().ReloadInputDevices();
93} 100}
94 101
95void IAppletResource::ActivateController(HidController controller) { 102void IAppletResource::ActivateController(HidController controller) {
@@ -102,6 +109,7 @@ void IAppletResource::DeactivateController(HidController controller) {
102 109
103IAppletResource::~IAppletResource() { 110IAppletResource::~IAppletResource() {
104 system.CoreTiming().UnscheduleEvent(pad_update_event, 0); 111 system.CoreTiming().UnscheduleEvent(pad_update_event, 0);
112 system.CoreTiming().UnscheduleEvent(mouse_keyboard_update_event, 0);
105 system.CoreTiming().UnscheduleEvent(motion_update_event, 0); 113 system.CoreTiming().UnscheduleEvent(motion_update_event, 0);
106} 114}
107 115
@@ -117,23 +125,44 @@ void IAppletResource::UpdateControllers(std::uintptr_t user_data,
117 std::chrono::nanoseconds ns_late) { 125 std::chrono::nanoseconds ns_late) {
118 auto& core_timing = system.CoreTiming(); 126 auto& core_timing = system.CoreTiming();
119 127
120 const bool should_reload = Settings::values.is_device_reload_pending.exchange(false);
121 for (const auto& controller : controllers) { 128 for (const auto& controller : controllers) {
122 if (should_reload) { 129 // Keyboard has it's own update event
123 controller->OnLoadInputDevices(); 130 if (controller == controllers[static_cast<size_t>(HidController::Keyboard)]) {
131 continue;
132 }
133 // Mouse has it's own update event
134 if (controller == controllers[static_cast<size_t>(HidController::Mouse)]) {
135 continue;
124 } 136 }
125 controller->OnUpdate(core_timing, system.Kernel().GetHidSharedMem().GetPointer(), 137 controller->OnUpdate(core_timing, system.Kernel().GetHidSharedMem().GetPointer(),
126 SHARED_MEMORY_SIZE); 138 SHARED_MEMORY_SIZE);
127 } 139 }
128 140
129 // If ns_late is higher than the update rate ignore the delay 141 // If ns_late is higher than the update rate ignore the delay
130 if (ns_late > motion_update_ns) { 142 if (ns_late > pad_update_ns) {
131 ns_late = {}; 143 ns_late = {};
132 } 144 }
133 145
134 core_timing.ScheduleEvent(pad_update_ns - ns_late, pad_update_event); 146 core_timing.ScheduleEvent(pad_update_ns - ns_late, pad_update_event);
135} 147}
136 148
149void IAppletResource::UpdateMouseKeyboard(std::uintptr_t user_data,
150 std::chrono::nanoseconds ns_late) {
151 auto& core_timing = system.CoreTiming();
152
153 controllers[static_cast<size_t>(HidController::Mouse)]->OnUpdate(
154 core_timing, system.Kernel().GetHidSharedMem().GetPointer(), SHARED_MEMORY_SIZE);
155 controllers[static_cast<size_t>(HidController::Keyboard)]->OnUpdate(
156 core_timing, system.Kernel().GetHidSharedMem().GetPointer(), SHARED_MEMORY_SIZE);
157
158 // If ns_late is higher than the update rate ignore the delay
159 if (ns_late > mouse_keyboard_update_ns) {
160 ns_late = {};
161 }
162
163 core_timing.ScheduleEvent(mouse_keyboard_update_ns - ns_late, mouse_keyboard_update_event);
164}
165
137void IAppletResource::UpdateMotion(std::uintptr_t user_data, std::chrono::nanoseconds ns_late) { 166void IAppletResource::UpdateMotion(std::uintptr_t user_data, std::chrono::nanoseconds ns_late) {
138 auto& core_timing = system.CoreTiming(); 167 auto& core_timing = system.CoreTiming();
139 168
@@ -166,7 +195,7 @@ public:
166private: 195private:
167 void InitializeVibrationDevice(Kernel::HLERequestContext& ctx) { 196 void InitializeVibrationDevice(Kernel::HLERequestContext& ctx) {
168 IPC::RequestParser rp{ctx}; 197 IPC::RequestParser rp{ctx};
169 const auto vibration_device_handle{rp.PopRaw<Controller_NPad::DeviceHandle>()}; 198 const auto vibration_device_handle{rp.PopRaw<Core::HID::VibrationDeviceHandle>()};
170 199
171 if (applet_resource != nullptr) { 200 if (applet_resource != nullptr) {
172 applet_resource->GetController<Controller_NPad>(HidController::NPad) 201 applet_resource->GetController<Controller_NPad>(HidController::NPad)
@@ -264,8 +293,8 @@ Hid::Hid(Core::System& system_)
264 {132, &Hid::EnableUnintendedHomeButtonInputProtection, "EnableUnintendedHomeButtonInputProtection"}, 293 {132, &Hid::EnableUnintendedHomeButtonInputProtection, "EnableUnintendedHomeButtonInputProtection"},
265 {133, nullptr, "SetNpadJoyAssignmentModeSingleWithDestination"}, 294 {133, nullptr, "SetNpadJoyAssignmentModeSingleWithDestination"},
266 {134, &Hid::SetNpadAnalogStickUseCenterClamp, "SetNpadAnalogStickUseCenterClamp"}, 295 {134, &Hid::SetNpadAnalogStickUseCenterClamp, "SetNpadAnalogStickUseCenterClamp"},
267 {135, nullptr, "SetNpadCaptureButtonAssignment"}, 296 {135, &Hid::SetNpadCaptureButtonAssignment, "SetNpadCaptureButtonAssignment"},
268 {136, nullptr, "ClearNpadCaptureButtonAssignment"}, 297 {136, &Hid::ClearNpadCaptureButtonAssignment, "ClearNpadCaptureButtonAssignment"},
269 {200, &Hid::GetVibrationDeviceInfo, "GetVibrationDeviceInfo"}, 298 {200, &Hid::GetVibrationDeviceInfo, "GetVibrationDeviceInfo"},
270 {201, &Hid::SendVibrationValue, "SendVibrationValue"}, 299 {201, &Hid::SendVibrationValue, "SendVibrationValue"},
271 {202, &Hid::GetActualVibrationValue, "GetActualVibrationValue"}, 300 {202, &Hid::GetActualVibrationValue, "GetActualVibrationValue"},
@@ -422,6 +451,7 @@ void Hid::ActivateXpad(Kernel::HLERequestContext& ctx) {
422 INSERT_PADDING_WORDS_NOINIT(1); 451 INSERT_PADDING_WORDS_NOINIT(1);
423 u64 applet_resource_user_id; 452 u64 applet_resource_user_id;
424 }; 453 };
454 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
425 455
426 const auto parameters{rp.PopRaw<Parameters>()}; 456 const auto parameters{rp.PopRaw<Parameters>()};
427 457
@@ -448,19 +478,18 @@ void Hid::GetXpadIDs(Kernel::HLERequestContext& ctx) {
448void Hid::ActivateSixAxisSensor(Kernel::HLERequestContext& ctx) { 478void Hid::ActivateSixAxisSensor(Kernel::HLERequestContext& ctx) {
449 IPC::RequestParser rp{ctx}; 479 IPC::RequestParser rp{ctx};
450 struct Parameters { 480 struct Parameters {
451 Controller_NPad::DeviceHandle sixaxis_handle; 481 u32 basic_xpad_id;
452 INSERT_PADDING_WORDS_NOINIT(1); 482 INSERT_PADDING_WORDS_NOINIT(1);
453 u64 applet_resource_user_id; 483 u64 applet_resource_user_id;
454 }; 484 };
485 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
455 486
456 const auto parameters{rp.PopRaw<Parameters>()}; 487 const auto parameters{rp.PopRaw<Parameters>()};
457 488
458 applet_resource->GetController<Controller_NPad>(HidController::NPad).SetSixAxisEnabled(true); 489 // This function does nothing on 10.0.0+
459 490
460 LOG_DEBUG(Service_HID, 491 LOG_WARNING(Service_HID, "(STUBBED) called, basic_xpad_id={}, applet_resource_user_id={}",
461 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", 492 parameters.basic_xpad_id, parameters.applet_resource_user_id);
462 parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
463 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
464 493
465 IPC::ResponseBuilder rb{ctx, 2}; 494 IPC::ResponseBuilder rb{ctx, 2};
466 rb.Push(ResultSuccess); 495 rb.Push(ResultSuccess);
@@ -469,19 +498,18 @@ void Hid::ActivateSixAxisSensor(Kernel::HLERequestContext& ctx) {
469void Hid::DeactivateSixAxisSensor(Kernel::HLERequestContext& ctx) { 498void Hid::DeactivateSixAxisSensor(Kernel::HLERequestContext& ctx) {
470 IPC::RequestParser rp{ctx}; 499 IPC::RequestParser rp{ctx};
471 struct Parameters { 500 struct Parameters {
472 Controller_NPad::DeviceHandle sixaxis_handle; 501 u32 basic_xpad_id;
473 INSERT_PADDING_WORDS_NOINIT(1); 502 INSERT_PADDING_WORDS_NOINIT(1);
474 u64 applet_resource_user_id; 503 u64 applet_resource_user_id;
475 }; 504 };
505 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
476 506
477 const auto parameters{rp.PopRaw<Parameters>()}; 507 const auto parameters{rp.PopRaw<Parameters>()};
478 508
479 applet_resource->GetController<Controller_NPad>(HidController::NPad).SetSixAxisEnabled(false); 509 // This function does nothing on 10.0.0+
480 510
481 LOG_DEBUG(Service_HID, 511 LOG_WARNING(Service_HID, "(STUBBED) called, basic_xpad_id={}, applet_resource_user_id={}",
482 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", 512 parameters.basic_xpad_id, parameters.applet_resource_user_id);
483 parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
484 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
485 513
486 IPC::ResponseBuilder rb{ctx, 2}; 514 IPC::ResponseBuilder rb{ctx, 2};
487 rb.Push(ResultSuccess); 515 rb.Push(ResultSuccess);
@@ -490,14 +518,16 @@ void Hid::DeactivateSixAxisSensor(Kernel::HLERequestContext& ctx) {
490void Hid::StartSixAxisSensor(Kernel::HLERequestContext& ctx) { 518void Hid::StartSixAxisSensor(Kernel::HLERequestContext& ctx) {
491 IPC::RequestParser rp{ctx}; 519 IPC::RequestParser rp{ctx};
492 struct Parameters { 520 struct Parameters {
493 Controller_NPad::DeviceHandle sixaxis_handle; 521 Core::HID::SixAxisSensorHandle sixaxis_handle;
494 INSERT_PADDING_WORDS_NOINIT(1); 522 INSERT_PADDING_WORDS_NOINIT(1);
495 u64 applet_resource_user_id; 523 u64 applet_resource_user_id;
496 }; 524 };
525 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
497 526
498 const auto parameters{rp.PopRaw<Parameters>()}; 527 const auto parameters{rp.PopRaw<Parameters>()};
499 528
500 applet_resource->GetController<Controller_NPad>(HidController::NPad).SetSixAxisEnabled(true); 529 applet_resource->GetController<Controller_NPad>(HidController::NPad)
530 .SetSixAxisEnabled(parameters.sixaxis_handle, true);
501 531
502 LOG_DEBUG(Service_HID, 532 LOG_DEBUG(Service_HID,
503 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", 533 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
@@ -511,14 +541,16 @@ void Hid::StartSixAxisSensor(Kernel::HLERequestContext& ctx) {
511void Hid::StopSixAxisSensor(Kernel::HLERequestContext& ctx) { 541void Hid::StopSixAxisSensor(Kernel::HLERequestContext& ctx) {
512 IPC::RequestParser rp{ctx}; 542 IPC::RequestParser rp{ctx};
513 struct Parameters { 543 struct Parameters {
514 Controller_NPad::DeviceHandle sixaxis_handle; 544 Core::HID::SixAxisSensorHandle sixaxis_handle;
515 INSERT_PADDING_WORDS_NOINIT(1); 545 INSERT_PADDING_WORDS_NOINIT(1);
516 u64 applet_resource_user_id; 546 u64 applet_resource_user_id;
517 }; 547 };
548 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
518 549
519 const auto parameters{rp.PopRaw<Parameters>()}; 550 const auto parameters{rp.PopRaw<Parameters>()};
520 551
521 applet_resource->GetController<Controller_NPad>(HidController::NPad).SetSixAxisEnabled(false); 552 applet_resource->GetController<Controller_NPad>(HidController::NPad)
553 .SetSixAxisEnabled(parameters.sixaxis_handle, false);
522 554
523 LOG_DEBUG(Service_HID, 555 LOG_DEBUG(Service_HID,
524 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", 556 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
@@ -534,19 +566,23 @@ void Hid::EnableSixAxisSensorFusion(Kernel::HLERequestContext& ctx) {
534 struct Parameters { 566 struct Parameters {
535 bool enable_sixaxis_sensor_fusion; 567 bool enable_sixaxis_sensor_fusion;
536 INSERT_PADDING_BYTES_NOINIT(3); 568 INSERT_PADDING_BYTES_NOINIT(3);
537 Controller_NPad::DeviceHandle sixaxis_handle; 569 Core::HID::SixAxisSensorHandle sixaxis_handle;
538 u64 applet_resource_user_id; 570 u64 applet_resource_user_id;
539 }; 571 };
540 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); 572 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
541 573
542 const auto parameters{rp.PopRaw<Parameters>()}; 574 const auto parameters{rp.PopRaw<Parameters>()};
543 575
544 LOG_WARNING(Service_HID, 576 applet_resource->GetController<Controller_NPad>(HidController::NPad)
545 "(STUBBED) called, enable_sixaxis_sensor_fusion={}, npad_type={}, npad_id={}, " 577 .SetSixAxisFusionEnabled(parameters.sixaxis_handle,
546 "device_index={}, applet_resource_user_id={}", 578 parameters.enable_sixaxis_sensor_fusion);
547 parameters.enable_sixaxis_sensor_fusion, parameters.sixaxis_handle.npad_type, 579
548 parameters.sixaxis_handle.npad_id, parameters.sixaxis_handle.device_index, 580 LOG_DEBUG(Service_HID,
549 parameters.applet_resource_user_id); 581 "called, enable_sixaxis_sensor_fusion={}, npad_type={}, npad_id={}, "
582 "device_index={}, applet_resource_user_id={}",
583 parameters.enable_sixaxis_sensor_fusion, parameters.sixaxis_handle.npad_type,
584 parameters.sixaxis_handle.npad_id, parameters.sixaxis_handle.device_index,
585 parameters.applet_resource_user_id);
550 586
551 IPC::ResponseBuilder rb{ctx, 2}; 587 IPC::ResponseBuilder rb{ctx, 2};
552 rb.Push(ResultSuccess); 588 rb.Push(ResultSuccess);
@@ -555,9 +591,9 @@ void Hid::EnableSixAxisSensorFusion(Kernel::HLERequestContext& ctx) {
555void Hid::SetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) { 591void Hid::SetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) {
556 IPC::RequestParser rp{ctx}; 592 IPC::RequestParser rp{ctx};
557 struct Parameters { 593 struct Parameters {
558 Controller_NPad::DeviceHandle sixaxis_handle; 594 Core::HID::SixAxisSensorHandle sixaxis_handle;
559 f32 parameter1; 595 Core::HID::SixAxisSensorFusionParameters sixaxis_fusion;
560 f32 parameter2; 596 INSERT_PADDING_WORDS_NOINIT(1);
561 u64 applet_resource_user_id; 597 u64 applet_resource_user_id;
562 }; 598 };
563 static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size."); 599 static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size.");
@@ -565,14 +601,14 @@ void Hid::SetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) {
565 const auto parameters{rp.PopRaw<Parameters>()}; 601 const auto parameters{rp.PopRaw<Parameters>()};
566 602
567 applet_resource->GetController<Controller_NPad>(HidController::NPad) 603 applet_resource->GetController<Controller_NPad>(HidController::NPad)
568 .SetSixAxisFusionParameters(parameters.parameter1, parameters.parameter2); 604 .SetSixAxisFusionParameters(parameters.sixaxis_handle, parameters.sixaxis_fusion);
569 605
570 LOG_WARNING(Service_HID, 606 LOG_DEBUG(Service_HID,
571 "(STUBBED) called, npad_type={}, npad_id={}, device_index={}, parameter1={}, " 607 "called, npad_type={}, npad_id={}, device_index={}, parameter1={}, "
572 "parameter2={}, applet_resource_user_id={}", 608 "parameter2={}, applet_resource_user_id={}",
573 parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, 609 parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
574 parameters.sixaxis_handle.device_index, parameters.parameter1, 610 parameters.sixaxis_handle.device_index, parameters.sixaxis_fusion.parameter1,
575 parameters.parameter2, parameters.applet_resource_user_id); 611 parameters.sixaxis_fusion.parameter2, parameters.applet_resource_user_id);
576 612
577 IPC::ResponseBuilder rb{ctx, 2}; 613 IPC::ResponseBuilder rb{ctx, 2};
578 rb.Push(ResultSuccess); 614 rb.Push(ResultSuccess);
@@ -581,35 +617,33 @@ void Hid::SetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) {
581void Hid::GetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) { 617void Hid::GetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) {
582 IPC::RequestParser rp{ctx}; 618 IPC::RequestParser rp{ctx};
583 struct Parameters { 619 struct Parameters {
584 Controller_NPad::DeviceHandle sixaxis_handle; 620 Core::HID::SixAxisSensorHandle sixaxis_handle;
621 INSERT_PADDING_WORDS_NOINIT(1);
585 u64 applet_resource_user_id; 622 u64 applet_resource_user_id;
586 }; 623 };
587 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); 624 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
588 625
589 f32 parameter1 = 0;
590 f32 parameter2 = 0;
591 const auto parameters{rp.PopRaw<Parameters>()}; 626 const auto parameters{rp.PopRaw<Parameters>()};
592 627
593 std::tie(parameter1, parameter2) = 628 const auto sixaxis_fusion_parameters =
594 applet_resource->GetController<Controller_NPad>(HidController::NPad) 629 applet_resource->GetController<Controller_NPad>(HidController::NPad)
595 .GetSixAxisFusionParameters(); 630 .GetSixAxisFusionParameters(parameters.sixaxis_handle);
596 631
597 LOG_WARNING( 632 LOG_DEBUG(Service_HID,
598 Service_HID, 633 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
599 "(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", 634 parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
600 parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, 635 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
601 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
602 636
603 IPC::ResponseBuilder rb{ctx, 4}; 637 IPC::ResponseBuilder rb{ctx, 4};
604 rb.Push(ResultSuccess); 638 rb.Push(ResultSuccess);
605 rb.Push(parameter1); 639 rb.PushRaw(sixaxis_fusion_parameters);
606 rb.Push(parameter2);
607} 640}
608 641
609void Hid::ResetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) { 642void Hid::ResetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) {
610 IPC::RequestParser rp{ctx}; 643 IPC::RequestParser rp{ctx};
611 struct Parameters { 644 struct Parameters {
612 Controller_NPad::DeviceHandle sixaxis_handle; 645 Core::HID::SixAxisSensorHandle sixaxis_handle;
646 INSERT_PADDING_WORDS_NOINIT(1);
613 u64 applet_resource_user_id; 647 u64 applet_resource_user_id;
614 }; 648 };
615 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); 649 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
@@ -617,13 +651,12 @@ void Hid::ResetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) {
617 const auto parameters{rp.PopRaw<Parameters>()}; 651 const auto parameters{rp.PopRaw<Parameters>()};
618 652
619 applet_resource->GetController<Controller_NPad>(HidController::NPad) 653 applet_resource->GetController<Controller_NPad>(HidController::NPad)
620 .ResetSixAxisFusionParameters(); 654 .ResetSixAxisFusionParameters(parameters.sixaxis_handle);
621 655
622 LOG_WARNING( 656 LOG_DEBUG(Service_HID,
623 Service_HID, 657 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
624 "(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", 658 parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
625 parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, 659 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
626 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
627 660
628 IPC::ResponseBuilder rb{ctx, 2}; 661 IPC::ResponseBuilder rb{ctx, 2};
629 rb.Push(ResultSuccess); 662 rb.Push(ResultSuccess);
@@ -631,12 +664,12 @@ void Hid::ResetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) {
631 664
632void Hid::SetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) { 665void Hid::SetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) {
633 IPC::RequestParser rp{ctx}; 666 IPC::RequestParser rp{ctx};
634 const auto sixaxis_handle{rp.PopRaw<Controller_NPad::DeviceHandle>()}; 667 const auto sixaxis_handle{rp.PopRaw<Core::HID::SixAxisSensorHandle>()};
635 const auto drift_mode{rp.PopEnum<Controller_NPad::GyroscopeZeroDriftMode>()}; 668 const auto drift_mode{rp.PopEnum<Controller_NPad::GyroscopeZeroDriftMode>()};
636 const auto applet_resource_user_id{rp.Pop<u64>()}; 669 const auto applet_resource_user_id{rp.Pop<u64>()};
637 670
638 applet_resource->GetController<Controller_NPad>(HidController::NPad) 671 applet_resource->GetController<Controller_NPad>(HidController::NPad)
639 .SetGyroscopeZeroDriftMode(drift_mode); 672 .SetGyroscopeZeroDriftMode(sixaxis_handle, drift_mode);
640 673
641 LOG_DEBUG(Service_HID, 674 LOG_DEBUG(Service_HID,
642 "called, npad_type={}, npad_id={}, device_index={}, drift_mode={}, " 675 "called, npad_type={}, npad_id={}, device_index={}, drift_mode={}, "
@@ -651,10 +684,11 @@ void Hid::SetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) {
651void Hid::GetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) { 684void Hid::GetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) {
652 IPC::RequestParser rp{ctx}; 685 IPC::RequestParser rp{ctx};
653 struct Parameters { 686 struct Parameters {
654 Controller_NPad::DeviceHandle sixaxis_handle; 687 Core::HID::SixAxisSensorHandle sixaxis_handle;
655 INSERT_PADDING_WORDS_NOINIT(1); 688 INSERT_PADDING_WORDS_NOINIT(1);
656 u64 applet_resource_user_id; 689 u64 applet_resource_user_id;
657 }; 690 };
691 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
658 692
659 const auto parameters{rp.PopRaw<Parameters>()}; 693 const auto parameters{rp.PopRaw<Parameters>()};
660 694
@@ -666,21 +700,23 @@ void Hid::GetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) {
666 IPC::ResponseBuilder rb{ctx, 3}; 700 IPC::ResponseBuilder rb{ctx, 3};
667 rb.Push(ResultSuccess); 701 rb.Push(ResultSuccess);
668 rb.PushEnum(applet_resource->GetController<Controller_NPad>(HidController::NPad) 702 rb.PushEnum(applet_resource->GetController<Controller_NPad>(HidController::NPad)
669 .GetGyroscopeZeroDriftMode()); 703 .GetGyroscopeZeroDriftMode(parameters.sixaxis_handle));
670} 704}
671 705
672void Hid::ResetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) { 706void Hid::ResetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) {
673 IPC::RequestParser rp{ctx}; 707 IPC::RequestParser rp{ctx};
674 struct Parameters { 708 struct Parameters {
675 Controller_NPad::DeviceHandle sixaxis_handle; 709 Core::HID::SixAxisSensorHandle sixaxis_handle;
676 INSERT_PADDING_WORDS_NOINIT(1); 710 INSERT_PADDING_WORDS_NOINIT(1);
677 u64 applet_resource_user_id; 711 u64 applet_resource_user_id;
678 }; 712 };
713 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
679 714
680 const auto parameters{rp.PopRaw<Parameters>()}; 715 const auto parameters{rp.PopRaw<Parameters>()};
716 const auto drift_mode{Controller_NPad::GyroscopeZeroDriftMode::Standard};
681 717
682 applet_resource->GetController<Controller_NPad>(HidController::NPad) 718 applet_resource->GetController<Controller_NPad>(HidController::NPad)
683 .SetGyroscopeZeroDriftMode(Controller_NPad::GyroscopeZeroDriftMode::Standard); 719 .SetGyroscopeZeroDriftMode(parameters.sixaxis_handle, drift_mode);
684 720
685 LOG_DEBUG(Service_HID, 721 LOG_DEBUG(Service_HID,
686 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", 722 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
@@ -694,10 +730,11 @@ void Hid::ResetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) {
694void Hid::IsSixAxisSensorAtRest(Kernel::HLERequestContext& ctx) { 730void Hid::IsSixAxisSensorAtRest(Kernel::HLERequestContext& ctx) {
695 IPC::RequestParser rp{ctx}; 731 IPC::RequestParser rp{ctx};
696 struct Parameters { 732 struct Parameters {
697 Controller_NPad::DeviceHandle sixaxis_handle; 733 Core::HID::SixAxisSensorHandle sixaxis_handle;
698 INSERT_PADDING_WORDS_NOINIT(1); 734 INSERT_PADDING_WORDS_NOINIT(1);
699 u64 applet_resource_user_id; 735 u64 applet_resource_user_id;
700 }; 736 };
737 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
701 738
702 const auto parameters{rp.PopRaw<Parameters>()}; 739 const auto parameters{rp.PopRaw<Parameters>()};
703 740
@@ -709,16 +746,17 @@ void Hid::IsSixAxisSensorAtRest(Kernel::HLERequestContext& ctx) {
709 IPC::ResponseBuilder rb{ctx, 3}; 746 IPC::ResponseBuilder rb{ctx, 3};
710 rb.Push(ResultSuccess); 747 rb.Push(ResultSuccess);
711 rb.Push(applet_resource->GetController<Controller_NPad>(HidController::NPad) 748 rb.Push(applet_resource->GetController<Controller_NPad>(HidController::NPad)
712 .IsSixAxisSensorAtRest()); 749 .IsSixAxisSensorAtRest(parameters.sixaxis_handle));
713} 750}
714 751
715void Hid::IsFirmwareUpdateAvailableForSixAxisSensor(Kernel::HLERequestContext& ctx) { 752void Hid::IsFirmwareUpdateAvailableForSixAxisSensor(Kernel::HLERequestContext& ctx) {
716 IPC::RequestParser rp{ctx}; 753 IPC::RequestParser rp{ctx};
717 struct Parameters { 754 struct Parameters {
718 Controller_NPad::DeviceHandle sixaxis_handle; 755 Core::HID::SixAxisSensorHandle sixaxis_handle;
719 INSERT_PADDING_WORDS_NOINIT(1); 756 INSERT_PADDING_WORDS_NOINIT(1);
720 u64 applet_resource_user_id; 757 u64 applet_resource_user_id;
721 }; 758 };
759 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
722 760
723 const auto parameters{rp.PopRaw<Parameters>()}; 761 const auto parameters{rp.PopRaw<Parameters>()};
724 762
@@ -740,13 +778,14 @@ void Hid::ActivateGesture(Kernel::HLERequestContext& ctx) {
740 INSERT_PADDING_WORDS_NOINIT(1); 778 INSERT_PADDING_WORDS_NOINIT(1);
741 u64 applet_resource_user_id; 779 u64 applet_resource_user_id;
742 }; 780 };
781 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
743 782
744 const auto parameters{rp.PopRaw<Parameters>()}; 783 const auto parameters{rp.PopRaw<Parameters>()};
745 784
746 applet_resource->ActivateController(HidController::Gesture); 785 applet_resource->ActivateController(HidController::Gesture);
747 786
748 LOG_DEBUG(Service_HID, "called, unknown={}, applet_resource_user_id={}", parameters.unknown, 787 LOG_WARNING(Service_HID, "(STUBBED) called, unknown={}, applet_resource_user_id={}",
749 parameters.applet_resource_user_id); 788 parameters.unknown, parameters.applet_resource_user_id);
750 789
751 IPC::ResponseBuilder rb{ctx, 2}; 790 IPC::ResponseBuilder rb{ctx, 2};
752 rb.Push(ResultSuccess); 791 rb.Push(ResultSuccess);
@@ -754,12 +793,20 @@ void Hid::ActivateGesture(Kernel::HLERequestContext& ctx) {
754 793
755void Hid::SetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx) { 794void Hid::SetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx) {
756 IPC::RequestParser rp{ctx}; 795 IPC::RequestParser rp{ctx};
757 const auto supported_styleset{rp.Pop<u32>()}; 796 struct Parameters {
797 Core::HID::NpadStyleSet supported_styleset;
798 INSERT_PADDING_WORDS_NOINIT(1);
799 u64 applet_resource_user_id;
800 };
801 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
802
803 const auto parameters{rp.PopRaw<Parameters>()};
758 804
759 applet_resource->GetController<Controller_NPad>(HidController::NPad) 805 applet_resource->GetController<Controller_NPad>(HidController::NPad)
760 .SetSupportedStyleSet({supported_styleset}); 806 .SetSupportedStyleSet({parameters.supported_styleset});
761 807
762 LOG_DEBUG(Service_HID, "called, supported_styleset={}", supported_styleset); 808 LOG_DEBUG(Service_HID, "called, supported_styleset={}, applet_resource_user_id={}",
809 parameters.supported_styleset, parameters.applet_resource_user_id);
763 810
764 IPC::ResponseBuilder rb{ctx, 2}; 811 IPC::ResponseBuilder rb{ctx, 2};
765 rb.Push(ResultSuccess); 812 rb.Push(ResultSuccess);
@@ -773,9 +820,9 @@ void Hid::GetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx) {
773 820
774 IPC::ResponseBuilder rb{ctx, 3}; 821 IPC::ResponseBuilder rb{ctx, 3};
775 rb.Push(ResultSuccess); 822 rb.Push(ResultSuccess);
776 rb.Push(applet_resource->GetController<Controller_NPad>(HidController::NPad) 823 rb.PushEnum(applet_resource->GetController<Controller_NPad>(HidController::NPad)
777 .GetSupportedStyleSet() 824 .GetSupportedStyleSet()
778 .raw); 825 .raw);
779} 826}
780 827
781void Hid::SetSupportedNpadIdType(Kernel::HLERequestContext& ctx) { 828void Hid::SetSupportedNpadIdType(Kernel::HLERequestContext& ctx) {
@@ -818,11 +865,12 @@ void Hid::DeactivateNpad(Kernel::HLERequestContext& ctx) {
818void Hid::AcquireNpadStyleSetUpdateEventHandle(Kernel::HLERequestContext& ctx) { 865void Hid::AcquireNpadStyleSetUpdateEventHandle(Kernel::HLERequestContext& ctx) {
819 IPC::RequestParser rp{ctx}; 866 IPC::RequestParser rp{ctx};
820 struct Parameters { 867 struct Parameters {
821 u32 npad_id; 868 Core::HID::NpadIdType npad_id;
822 INSERT_PADDING_WORDS_NOINIT(1); 869 INSERT_PADDING_WORDS_NOINIT(1);
823 u64 applet_resource_user_id; 870 u64 applet_resource_user_id;
824 u64 unknown; 871 u64 unknown;
825 }; 872 };
873 static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size.");
826 874
827 const auto parameters{rp.PopRaw<Parameters>()}; 875 const auto parameters{rp.PopRaw<Parameters>()};
828 876
@@ -838,10 +886,11 @@ void Hid::AcquireNpadStyleSetUpdateEventHandle(Kernel::HLERequestContext& ctx) {
838void Hid::DisconnectNpad(Kernel::HLERequestContext& ctx) { 886void Hid::DisconnectNpad(Kernel::HLERequestContext& ctx) {
839 IPC::RequestParser rp{ctx}; 887 IPC::RequestParser rp{ctx};
840 struct Parameters { 888 struct Parameters {
841 u32 npad_id; 889 Core::HID::NpadIdType npad_id;
842 INSERT_PADDING_WORDS_NOINIT(1); 890 INSERT_PADDING_WORDS_NOINIT(1);
843 u64 applet_resource_user_id; 891 u64 applet_resource_user_id;
844 }; 892 };
893 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
845 894
846 const auto parameters{rp.PopRaw<Parameters>()}; 895 const auto parameters{rp.PopRaw<Parameters>()};
847 896
@@ -857,7 +906,7 @@ void Hid::DisconnectNpad(Kernel::HLERequestContext& ctx) {
857 906
858void Hid::GetPlayerLedPattern(Kernel::HLERequestContext& ctx) { 907void Hid::GetPlayerLedPattern(Kernel::HLERequestContext& ctx) {
859 IPC::RequestParser rp{ctx}; 908 IPC::RequestParser rp{ctx};
860 const auto npad_id{rp.Pop<u32>()}; 909 const auto npad_id{rp.PopEnum<Core::HID::NpadIdType>()};
861 910
862 LOG_DEBUG(Service_HID, "called, npad_id={}", npad_id); 911 LOG_DEBUG(Service_HID, "called, npad_id={}", npad_id);
863 912
@@ -872,16 +921,17 @@ void Hid::ActivateNpadWithRevision(Kernel::HLERequestContext& ctx) {
872 // Should have no effect with how our npad sets up the data 921 // Should have no effect with how our npad sets up the data
873 IPC::RequestParser rp{ctx}; 922 IPC::RequestParser rp{ctx};
874 struct Parameters { 923 struct Parameters {
875 u32 unknown; 924 s32 revision;
876 INSERT_PADDING_WORDS_NOINIT(1); 925 INSERT_PADDING_WORDS_NOINIT(1);
877 u64 applet_resource_user_id; 926 u64 applet_resource_user_id;
878 }; 927 };
928 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
879 929
880 const auto parameters{rp.PopRaw<Parameters>()}; 930 const auto parameters{rp.PopRaw<Parameters>()};
881 931
882 applet_resource->ActivateController(HidController::NPad); 932 applet_resource->ActivateController(HidController::NPad);
883 933
884 LOG_DEBUG(Service_HID, "called, unknown={}, applet_resource_user_id={}", parameters.unknown, 934 LOG_DEBUG(Service_HID, "called, revision={}, applet_resource_user_id={}", parameters.revision,
885 parameters.applet_resource_user_id); 935 parameters.applet_resource_user_id);
886 936
887 IPC::ResponseBuilder rb{ctx, 2}; 937 IPC::ResponseBuilder rb{ctx, 2};
@@ -891,7 +941,7 @@ void Hid::ActivateNpadWithRevision(Kernel::HLERequestContext& ctx) {
891void Hid::SetNpadJoyHoldType(Kernel::HLERequestContext& ctx) { 941void Hid::SetNpadJoyHoldType(Kernel::HLERequestContext& ctx) {
892 IPC::RequestParser rp{ctx}; 942 IPC::RequestParser rp{ctx};
893 const auto applet_resource_user_id{rp.Pop<u64>()}; 943 const auto applet_resource_user_id{rp.Pop<u64>()};
894 const auto hold_type{rp.PopEnum<Controller_NPad::NpadHoldType>()}; 944 const auto hold_type{rp.PopEnum<Controller_NPad::NpadJoyHoldType>()};
895 945
896 applet_resource->GetController<Controller_NPad>(HidController::NPad).SetHoldType(hold_type); 946 applet_resource->GetController<Controller_NPad>(HidController::NPad).SetHoldType(hold_type);
897 947
@@ -916,42 +966,44 @@ void Hid::GetNpadJoyHoldType(Kernel::HLERequestContext& ctx) {
916void Hid::SetNpadJoyAssignmentModeSingleByDefault(Kernel::HLERequestContext& ctx) { 966void Hid::SetNpadJoyAssignmentModeSingleByDefault(Kernel::HLERequestContext& ctx) {
917 IPC::RequestParser rp{ctx}; 967 IPC::RequestParser rp{ctx};
918 struct Parameters { 968 struct Parameters {
919 u32 npad_id; 969 Core::HID::NpadIdType npad_id;
920 INSERT_PADDING_WORDS_NOINIT(1); 970 INSERT_PADDING_WORDS_NOINIT(1);
921 u64 applet_resource_user_id; 971 u64 applet_resource_user_id;
922 }; 972 };
973 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
923 974
924 const auto parameters{rp.PopRaw<Parameters>()}; 975 const auto parameters{rp.PopRaw<Parameters>()};
925 976
926 applet_resource->GetController<Controller_NPad>(HidController::NPad) 977 applet_resource->GetController<Controller_NPad>(HidController::NPad)
927 .SetNpadMode(parameters.npad_id, Controller_NPad::NpadAssignments::Single); 978 .SetNpadMode(parameters.npad_id, Controller_NPad::NpadJoyDeviceType::Left,
979 Controller_NPad::NpadJoyAssignmentMode::Single);
928 980
929 LOG_WARNING(Service_HID, "(STUBBED) called, npad_id={}, applet_resource_user_id={}", 981 LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}", parameters.npad_id,
930 parameters.npad_id, parameters.applet_resource_user_id); 982 parameters.applet_resource_user_id);
931 983
932 IPC::ResponseBuilder rb{ctx, 2}; 984 IPC::ResponseBuilder rb{ctx, 2};
933 rb.Push(ResultSuccess); 985 rb.Push(ResultSuccess);
934} 986}
935 987
936void Hid::SetNpadJoyAssignmentModeSingle(Kernel::HLERequestContext& ctx) { 988void Hid::SetNpadJoyAssignmentModeSingle(Kernel::HLERequestContext& ctx) {
937 // TODO: Check the differences between this and SetNpadJoyAssignmentModeSingleByDefault
938 IPC::RequestParser rp{ctx}; 989 IPC::RequestParser rp{ctx};
939 struct Parameters { 990 struct Parameters {
940 u32 npad_id; 991 Core::HID::NpadIdType npad_id;
941 INSERT_PADDING_WORDS_NOINIT(1); 992 INSERT_PADDING_WORDS_NOINIT(1);
942 u64 applet_resource_user_id; 993 u64 applet_resource_user_id;
943 u64 npad_joy_device_type; 994 Controller_NPad::NpadJoyDeviceType npad_joy_device_type;
944 }; 995 };
996 static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size.");
945 997
946 const auto parameters{rp.PopRaw<Parameters>()}; 998 const auto parameters{rp.PopRaw<Parameters>()};
947 999
948 applet_resource->GetController<Controller_NPad>(HidController::NPad) 1000 applet_resource->GetController<Controller_NPad>(HidController::NPad)
949 .SetNpadMode(parameters.npad_id, Controller_NPad::NpadAssignments::Single); 1001 .SetNpadMode(parameters.npad_id, parameters.npad_joy_device_type,
1002 Controller_NPad::NpadJoyAssignmentMode::Single);
950 1003
951 LOG_WARNING(Service_HID, 1004 LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}, npad_joy_device_type={}",
952 "(STUBBED) called, npad_id={}, applet_resource_user_id={}, npad_joy_device_type={}", 1005 parameters.npad_id, parameters.applet_resource_user_id,
953 parameters.npad_id, parameters.applet_resource_user_id, 1006 parameters.npad_joy_device_type);
954 parameters.npad_joy_device_type);
955 1007
956 IPC::ResponseBuilder rb{ctx, 2}; 1008 IPC::ResponseBuilder rb{ctx, 2};
957 rb.Push(ResultSuccess); 1009 rb.Push(ResultSuccess);
@@ -960,18 +1012,19 @@ void Hid::SetNpadJoyAssignmentModeSingle(Kernel::HLERequestContext& ctx) {
960void Hid::SetNpadJoyAssignmentModeDual(Kernel::HLERequestContext& ctx) { 1012void Hid::SetNpadJoyAssignmentModeDual(Kernel::HLERequestContext& ctx) {
961 IPC::RequestParser rp{ctx}; 1013 IPC::RequestParser rp{ctx};
962 struct Parameters { 1014 struct Parameters {
963 u32 npad_id; 1015 Core::HID::NpadIdType npad_id;
964 INSERT_PADDING_WORDS_NOINIT(1); 1016 INSERT_PADDING_WORDS_NOINIT(1);
965 u64 applet_resource_user_id; 1017 u64 applet_resource_user_id;
966 }; 1018 };
1019 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
967 1020
968 const auto parameters{rp.PopRaw<Parameters>()}; 1021 const auto parameters{rp.PopRaw<Parameters>()};
969 1022
970 applet_resource->GetController<Controller_NPad>(HidController::NPad) 1023 applet_resource->GetController<Controller_NPad>(HidController::NPad)
971 .SetNpadMode(parameters.npad_id, Controller_NPad::NpadAssignments::Dual); 1024 .SetNpadMode(parameters.npad_id, {}, Controller_NPad::NpadJoyAssignmentMode::Dual);
972 1025
973 LOG_WARNING(Service_HID, "(STUBBED) called, npad_id={}, applet_resource_user_id={}", 1026 LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}", parameters.npad_id,
974 parameters.npad_id, parameters.applet_resource_user_id); 1027 parameters.applet_resource_user_id);
975 1028
976 IPC::ResponseBuilder rb{ctx, 2}; 1029 IPC::ResponseBuilder rb{ctx, 2};
977 rb.Push(ResultSuccess); 1030 rb.Push(ResultSuccess);
@@ -979,8 +1032,8 @@ void Hid::SetNpadJoyAssignmentModeDual(Kernel::HLERequestContext& ctx) {
979 1032
980void Hid::MergeSingleJoyAsDualJoy(Kernel::HLERequestContext& ctx) { 1033void Hid::MergeSingleJoyAsDualJoy(Kernel::HLERequestContext& ctx) {
981 IPC::RequestParser rp{ctx}; 1034 IPC::RequestParser rp{ctx};
982 const auto npad_id_1{rp.Pop<u32>()}; 1035 const auto npad_id_1{rp.PopEnum<Core::HID::NpadIdType>()};
983 const auto npad_id_2{rp.Pop<u32>()}; 1036 const auto npad_id_2{rp.PopEnum<Core::HID::NpadIdType>()};
984 const auto applet_resource_user_id{rp.Pop<u64>()}; 1037 const auto applet_resource_user_id{rp.Pop<u64>()};
985 1038
986 applet_resource->GetController<Controller_NPad>(HidController::NPad) 1039 applet_resource->GetController<Controller_NPad>(HidController::NPad)
@@ -1046,8 +1099,8 @@ void Hid::GetNpadHandheldActivationMode(Kernel::HLERequestContext& ctx) {
1046 1099
1047void Hid::SwapNpadAssignment(Kernel::HLERequestContext& ctx) { 1100void Hid::SwapNpadAssignment(Kernel::HLERequestContext& ctx) {
1048 IPC::RequestParser rp{ctx}; 1101 IPC::RequestParser rp{ctx};
1049 const auto npad_id_1{rp.Pop<u32>()}; 1102 const auto npad_id_1{rp.PopEnum<Core::HID::NpadIdType>()};
1050 const auto npad_id_2{rp.Pop<u32>()}; 1103 const auto npad_id_2{rp.PopEnum<Core::HID::NpadIdType>()};
1051 const auto applet_resource_user_id{rp.Pop<u64>()}; 1104 const auto applet_resource_user_id{rp.Pop<u64>()};
1052 1105
1053 const bool res = applet_resource->GetController<Controller_NPad>(HidController::NPad) 1106 const bool res = applet_resource->GetController<Controller_NPad>(HidController::NPad)
@@ -1068,10 +1121,11 @@ void Hid::SwapNpadAssignment(Kernel::HLERequestContext& ctx) {
1068void Hid::IsUnintendedHomeButtonInputProtectionEnabled(Kernel::HLERequestContext& ctx) { 1121void Hid::IsUnintendedHomeButtonInputProtectionEnabled(Kernel::HLERequestContext& ctx) {
1069 IPC::RequestParser rp{ctx}; 1122 IPC::RequestParser rp{ctx};
1070 struct Parameters { 1123 struct Parameters {
1071 u32 npad_id; 1124 Core::HID::NpadIdType npad_id;
1072 INSERT_PADDING_WORDS_NOINIT(1); 1125 INSERT_PADDING_WORDS_NOINIT(1);
1073 u64 applet_resource_user_id; 1126 u64 applet_resource_user_id;
1074 }; 1127 };
1128 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
1075 1129
1076 const auto parameters{rp.PopRaw<Parameters>()}; 1130 const auto parameters{rp.PopRaw<Parameters>()};
1077 1131
@@ -1089,9 +1143,10 @@ void Hid::EnableUnintendedHomeButtonInputProtection(Kernel::HLERequestContext& c
1089 struct Parameters { 1143 struct Parameters {
1090 bool unintended_home_button_input_protection; 1144 bool unintended_home_button_input_protection;
1091 INSERT_PADDING_BYTES_NOINIT(3); 1145 INSERT_PADDING_BYTES_NOINIT(3);
1092 u32 npad_id; 1146 Core::HID::NpadIdType npad_id;
1093 u64 applet_resource_user_id; 1147 u64 applet_resource_user_id;
1094 }; 1148 };
1149 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
1095 1150
1096 const auto parameters{rp.PopRaw<Parameters>()}; 1151 const auto parameters{rp.PopRaw<Parameters>()};
1097 1152
@@ -1113,6 +1168,7 @@ void Hid::SetNpadAnalogStickUseCenterClamp(Kernel::HLERequestContext& ctx) {
1113 IPC::RequestParser rp{ctx}; 1168 IPC::RequestParser rp{ctx};
1114 struct Parameters { 1169 struct Parameters {
1115 bool analog_stick_use_center_clamp; 1170 bool analog_stick_use_center_clamp;
1171 INSERT_PADDING_BYTES_NOINIT(7);
1116 u64 applet_resource_user_id; 1172 u64 applet_resource_user_id;
1117 }; 1173 };
1118 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); 1174 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
@@ -1130,40 +1186,71 @@ void Hid::SetNpadAnalogStickUseCenterClamp(Kernel::HLERequestContext& ctx) {
1130 rb.Push(ResultSuccess); 1186 rb.Push(ResultSuccess);
1131} 1187}
1132 1188
1189void Hid::SetNpadCaptureButtonAssignment(Kernel::HLERequestContext& ctx) {
1190 IPC::RequestParser rp{ctx};
1191 struct Parameters {
1192 Core::HID::NpadStyleSet npad_styleset;
1193 INSERT_PADDING_WORDS_NOINIT(1);
1194 u64 applet_resource_user_id;
1195 Core::HID::NpadButton button;
1196 };
1197 static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size.");
1198
1199 const auto parameters{rp.PopRaw<Parameters>()};
1200
1201 LOG_WARNING(Service_HID,
1202 "(STUBBED) called, npad_styleset={}, applet_resource_user_id={}, button={}",
1203 parameters.npad_styleset, parameters.applet_resource_user_id, parameters.button);
1204
1205 IPC::ResponseBuilder rb{ctx, 2};
1206 rb.Push(ResultSuccess);
1207}
1208
1209void Hid::ClearNpadCaptureButtonAssignment(Kernel::HLERequestContext& ctx) {
1210 IPC::RequestParser rp{ctx};
1211 const auto applet_resource_user_id{rp.Pop<u64>()};
1212
1213 LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}",
1214 applet_resource_user_id);
1215
1216 IPC::ResponseBuilder rb{ctx, 2};
1217 rb.Push(ResultSuccess);
1218}
1219
1133void Hid::GetVibrationDeviceInfo(Kernel::HLERequestContext& ctx) { 1220void Hid::GetVibrationDeviceInfo(Kernel::HLERequestContext& ctx) {
1134 IPC::RequestParser rp{ctx}; 1221 IPC::RequestParser rp{ctx};
1135 const auto vibration_device_handle{rp.PopRaw<Controller_NPad::DeviceHandle>()}; 1222 const auto vibration_device_handle{rp.PopRaw<Core::HID::VibrationDeviceHandle>()};
1136 1223
1137 VibrationDeviceInfo vibration_device_info; 1224 Core::HID::VibrationDeviceInfo vibration_device_info;
1138 1225
1139 switch (vibration_device_handle.npad_type) { 1226 switch (vibration_device_handle.npad_type) {
1140 case Controller_NPad::NpadType::ProController: 1227 case Core::HID::NpadStyleIndex::ProController:
1141 case Controller_NPad::NpadType::Handheld: 1228 case Core::HID::NpadStyleIndex::Handheld:
1142 case Controller_NPad::NpadType::JoyconDual: 1229 case Core::HID::NpadStyleIndex::JoyconDual:
1143 case Controller_NPad::NpadType::JoyconLeft: 1230 case Core::HID::NpadStyleIndex::JoyconLeft:
1144 case Controller_NPad::NpadType::JoyconRight: 1231 case Core::HID::NpadStyleIndex::JoyconRight:
1145 default: 1232 default:
1146 vibration_device_info.type = VibrationDeviceType::LinearResonantActuator; 1233 vibration_device_info.type = Core::HID::VibrationDeviceType::LinearResonantActuator;
1147 break; 1234 break;
1148 case Controller_NPad::NpadType::GameCube: 1235 case Core::HID::NpadStyleIndex::GameCube:
1149 vibration_device_info.type = VibrationDeviceType::GcErm; 1236 vibration_device_info.type = Core::HID::VibrationDeviceType::GcErm;
1150 break; 1237 break;
1151 case Controller_NPad::NpadType::Pokeball: 1238 case Core::HID::NpadStyleIndex::Pokeball:
1152 vibration_device_info.type = VibrationDeviceType::Unknown; 1239 vibration_device_info.type = Core::HID::VibrationDeviceType::Unknown;
1153 break; 1240 break;
1154 } 1241 }
1155 1242
1156 switch (vibration_device_handle.device_index) { 1243 switch (vibration_device_handle.device_index) {
1157 case Controller_NPad::DeviceIndex::Left: 1244 case Core::HID::DeviceIndex::Left:
1158 vibration_device_info.position = VibrationDevicePosition::Left; 1245 vibration_device_info.position = Core::HID::VibrationDevicePosition::Left;
1159 break; 1246 break;
1160 case Controller_NPad::DeviceIndex::Right: 1247 case Core::HID::DeviceIndex::Right:
1161 vibration_device_info.position = VibrationDevicePosition::Right; 1248 vibration_device_info.position = Core::HID::VibrationDevicePosition::Right;
1162 break; 1249 break;
1163 case Controller_NPad::DeviceIndex::None: 1250 case Core::HID::DeviceIndex::None:
1164 default: 1251 default:
1165 UNREACHABLE_MSG("DeviceIndex should never be None!"); 1252 UNREACHABLE_MSG("DeviceIndex should never be None!");
1166 vibration_device_info.position = VibrationDevicePosition::None; 1253 vibration_device_info.position = Core::HID::VibrationDevicePosition::None;
1167 break; 1254 break;
1168 } 1255 }
1169 1256
@@ -1178,11 +1265,12 @@ void Hid::GetVibrationDeviceInfo(Kernel::HLERequestContext& ctx) {
1178void Hid::SendVibrationValue(Kernel::HLERequestContext& ctx) { 1265void Hid::SendVibrationValue(Kernel::HLERequestContext& ctx) {
1179 IPC::RequestParser rp{ctx}; 1266 IPC::RequestParser rp{ctx};
1180 struct Parameters { 1267 struct Parameters {
1181 Controller_NPad::DeviceHandle vibration_device_handle; 1268 Core::HID::VibrationDeviceHandle vibration_device_handle;
1182 Controller_NPad::VibrationValue vibration_value; 1269 Core::HID::VibrationValue vibration_value;
1183 INSERT_PADDING_WORDS_NOINIT(1); 1270 INSERT_PADDING_WORDS_NOINIT(1);
1184 u64 applet_resource_user_id; 1271 u64 applet_resource_user_id;
1185 }; 1272 };
1273 static_assert(sizeof(Parameters) == 0x20, "Parameters has incorrect size.");
1186 1274
1187 const auto parameters{rp.PopRaw<Parameters>()}; 1275 const auto parameters{rp.PopRaw<Parameters>()};
1188 1276
@@ -1202,10 +1290,11 @@ void Hid::SendVibrationValue(Kernel::HLERequestContext& ctx) {
1202void Hid::GetActualVibrationValue(Kernel::HLERequestContext& ctx) { 1290void Hid::GetActualVibrationValue(Kernel::HLERequestContext& ctx) {
1203 IPC::RequestParser rp{ctx}; 1291 IPC::RequestParser rp{ctx};
1204 struct Parameters { 1292 struct Parameters {
1205 Controller_NPad::DeviceHandle vibration_device_handle; 1293 Core::HID::VibrationDeviceHandle vibration_device_handle;
1206 INSERT_PADDING_WORDS_NOINIT(1); 1294 INSERT_PADDING_WORDS_NOINIT(1);
1207 u64 applet_resource_user_id; 1295 u64 applet_resource_user_id;
1208 }; 1296 };
1297 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
1209 1298
1210 const auto parameters{rp.PopRaw<Parameters>()}; 1299 const auto parameters{rp.PopRaw<Parameters>()};
1211 1300
@@ -1256,10 +1345,10 @@ void Hid::SendVibrationValues(Kernel::HLERequestContext& ctx) {
1256 const auto handles = ctx.ReadBuffer(0); 1345 const auto handles = ctx.ReadBuffer(0);
1257 const auto vibrations = ctx.ReadBuffer(1); 1346 const auto vibrations = ctx.ReadBuffer(1);
1258 1347
1259 std::vector<Controller_NPad::DeviceHandle> vibration_device_handles( 1348 std::vector<Core::HID::VibrationDeviceHandle> vibration_device_handles(
1260 handles.size() / sizeof(Controller_NPad::DeviceHandle)); 1349 handles.size() / sizeof(Core::HID::VibrationDeviceHandle));
1261 std::vector<Controller_NPad::VibrationValue> vibration_values( 1350 std::vector<Core::HID::VibrationValue> vibration_values(vibrations.size() /
1262 vibrations.size() / sizeof(Controller_NPad::VibrationValue)); 1351 sizeof(Core::HID::VibrationValue));
1263 1352
1264 std::memcpy(vibration_device_handles.data(), handles.data(), handles.size()); 1353 std::memcpy(vibration_device_handles.data(), handles.data(), handles.size());
1265 std::memcpy(vibration_values.data(), vibrations.data(), vibrations.size()); 1354 std::memcpy(vibration_values.data(), vibrations.data(), vibrations.size());
@@ -1276,9 +1365,10 @@ void Hid::SendVibrationValues(Kernel::HLERequestContext& ctx) {
1276void Hid::SendVibrationGcErmCommand(Kernel::HLERequestContext& ctx) { 1365void Hid::SendVibrationGcErmCommand(Kernel::HLERequestContext& ctx) {
1277 IPC::RequestParser rp{ctx}; 1366 IPC::RequestParser rp{ctx};
1278 struct Parameters { 1367 struct Parameters {
1279 Controller_NPad::DeviceHandle vibration_device_handle; 1368 Core::HID::VibrationDeviceHandle vibration_device_handle;
1369 INSERT_PADDING_WORDS_NOINIT(1);
1280 u64 applet_resource_user_id; 1370 u64 applet_resource_user_id;
1281 VibrationGcErmCommand gc_erm_command; 1371 Core::HID::VibrationGcErmCommand gc_erm_command;
1282 }; 1372 };
1283 static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size."); 1373 static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size.");
1284 1374
@@ -1292,26 +1382,26 @@ void Hid::SendVibrationGcErmCommand(Kernel::HLERequestContext& ctx) {
1292 */ 1382 */
1293 const auto vibration_value = [parameters] { 1383 const auto vibration_value = [parameters] {
1294 switch (parameters.gc_erm_command) { 1384 switch (parameters.gc_erm_command) {
1295 case VibrationGcErmCommand::Stop: 1385 case Core::HID::VibrationGcErmCommand::Stop:
1296 return Controller_NPad::VibrationValue{ 1386 return Core::HID::VibrationValue{
1297 .amp_low = 0.0f, 1387 .low_amplitude = 0.0f,
1298 .freq_low = 160.0f, 1388 .low_frequency = 160.0f,
1299 .amp_high = 0.0f, 1389 .high_amplitude = 0.0f,
1300 .freq_high = 320.0f, 1390 .high_frequency = 320.0f,
1301 }; 1391 };
1302 case VibrationGcErmCommand::Start: 1392 case Core::HID::VibrationGcErmCommand::Start:
1303 return Controller_NPad::VibrationValue{ 1393 return Core::HID::VibrationValue{
1304 .amp_low = 1.0f, 1394 .low_amplitude = 1.0f,
1305 .freq_low = 160.0f, 1395 .low_frequency = 160.0f,
1306 .amp_high = 1.0f, 1396 .high_amplitude = 1.0f,
1307 .freq_high = 320.0f, 1397 .high_frequency = 320.0f,
1308 }; 1398 };
1309 case VibrationGcErmCommand::StopHard: 1399 case Core::HID::VibrationGcErmCommand::StopHard:
1310 return Controller_NPad::VibrationValue{ 1400 return Core::HID::VibrationValue{
1311 .amp_low = 0.0f, 1401 .low_amplitude = 0.0f,
1312 .freq_low = 0.0f, 1402 .low_frequency = 0.0f,
1313 .amp_high = 0.0f, 1403 .high_amplitude = 0.0f,
1314 .freq_high = 0.0f, 1404 .high_frequency = 0.0f,
1315 }; 1405 };
1316 default: 1406 default:
1317 return Controller_NPad::DEFAULT_VIBRATION_VALUE; 1407 return Controller_NPad::DEFAULT_VIBRATION_VALUE;
@@ -1336,7 +1426,7 @@ void Hid::SendVibrationGcErmCommand(Kernel::HLERequestContext& ctx) {
1336void Hid::GetActualVibrationGcErmCommand(Kernel::HLERequestContext& ctx) { 1426void Hid::GetActualVibrationGcErmCommand(Kernel::HLERequestContext& ctx) {
1337 IPC::RequestParser rp{ctx}; 1427 IPC::RequestParser rp{ctx};
1338 struct Parameters { 1428 struct Parameters {
1339 Controller_NPad::DeviceHandle vibration_device_handle; 1429 Core::HID::VibrationDeviceHandle vibration_device_handle;
1340 INSERT_PADDING_WORDS_NOINIT(1); 1430 INSERT_PADDING_WORDS_NOINIT(1);
1341 u64 applet_resource_user_id; 1431 u64 applet_resource_user_id;
1342 }; 1432 };
@@ -1347,8 +1437,8 @@ void Hid::GetActualVibrationGcErmCommand(Kernel::HLERequestContext& ctx) {
1347 .GetLastVibration(parameters.vibration_device_handle); 1437 .GetLastVibration(parameters.vibration_device_handle);
1348 1438
1349 const auto gc_erm_command = [last_vibration] { 1439 const auto gc_erm_command = [last_vibration] {
1350 if (last_vibration.amp_low != 0.0f || last_vibration.amp_high != 0.0f) { 1440 if (last_vibration.low_amplitude != 0.0f || last_vibration.high_amplitude != 0.0f) {
1351 return VibrationGcErmCommand::Start; 1441 return Core::HID::VibrationGcErmCommand::Start;
1352 } 1442 }
1353 1443
1354 /** 1444 /**
@@ -1357,11 +1447,11 @@ void Hid::GetActualVibrationGcErmCommand(Kernel::HLERequestContext& ctx) {
1357 * SendVibrationGcErmCommand, in order to differentiate between Stop and StopHard commands. 1447 * SendVibrationGcErmCommand, in order to differentiate between Stop and StopHard commands.
1358 * This is done to reuse the controller vibration functions made for regular controllers. 1448 * This is done to reuse the controller vibration functions made for regular controllers.
1359 */ 1449 */
1360 if (last_vibration.freq_low == 0.0f && last_vibration.freq_high == 0.0f) { 1450 if (last_vibration.low_frequency == 0.0f && last_vibration.high_frequency == 0.0f) {
1361 return VibrationGcErmCommand::StopHard; 1451 return Core::HID::VibrationGcErmCommand::StopHard;
1362 } 1452 }
1363 1453
1364 return VibrationGcErmCommand::Stop; 1454 return Core::HID::VibrationGcErmCommand::Stop;
1365 }(); 1455 }();
1366 1456
1367 LOG_DEBUG(Service_HID, 1457 LOG_DEBUG(Service_HID,
@@ -1401,10 +1491,11 @@ void Hid::EndPermitVibrationSession(Kernel::HLERequestContext& ctx) {
1401void Hid::IsVibrationDeviceMounted(Kernel::HLERequestContext& ctx) { 1491void Hid::IsVibrationDeviceMounted(Kernel::HLERequestContext& ctx) {
1402 IPC::RequestParser rp{ctx}; 1492 IPC::RequestParser rp{ctx};
1403 struct Parameters { 1493 struct Parameters {
1404 Controller_NPad::DeviceHandle vibration_device_handle; 1494 Core::HID::VibrationDeviceHandle vibration_device_handle;
1405 INSERT_PADDING_WORDS_NOINIT(1); 1495 INSERT_PADDING_WORDS_NOINIT(1);
1406 u64 applet_resource_user_id; 1496 u64 applet_resource_user_id;
1407 }; 1497 };
1498 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
1408 1499
1409 const auto parameters{rp.PopRaw<Parameters>()}; 1500 const auto parameters{rp.PopRaw<Parameters>()};
1410 1501
@@ -1435,18 +1526,18 @@ void Hid::ActivateConsoleSixAxisSensor(Kernel::HLERequestContext& ctx) {
1435void Hid::StartConsoleSixAxisSensor(Kernel::HLERequestContext& ctx) { 1526void Hid::StartConsoleSixAxisSensor(Kernel::HLERequestContext& ctx) {
1436 IPC::RequestParser rp{ctx}; 1527 IPC::RequestParser rp{ctx};
1437 struct Parameters { 1528 struct Parameters {
1438 Controller_NPad::DeviceHandle sixaxis_handle; 1529 Core::HID::ConsoleSixAxisSensorHandle console_sixaxis_handle;
1439 INSERT_PADDING_WORDS_NOINIT(1); 1530 INSERT_PADDING_WORDS_NOINIT(1);
1440 u64 applet_resource_user_id; 1531 u64 applet_resource_user_id;
1441 }; 1532 };
1533 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
1442 1534
1443 const auto parameters{rp.PopRaw<Parameters>()}; 1535 const auto parameters{rp.PopRaw<Parameters>()};
1444 1536
1445 LOG_WARNING( 1537 LOG_WARNING(Service_HID,
1446 Service_HID, 1538 "(STUBBED) called, unknown_1={}, unknown_2={}, applet_resource_user_id={}",
1447 "(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", 1539 parameters.console_sixaxis_handle.unknown_1,
1448 parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, 1540 parameters.console_sixaxis_handle.unknown_2, parameters.applet_resource_user_id);
1449 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
1450 1541
1451 IPC::ResponseBuilder rb{ctx, 2}; 1542 IPC::ResponseBuilder rb{ctx, 2};
1452 rb.Push(ResultSuccess); 1543 rb.Push(ResultSuccess);
@@ -1455,18 +1546,18 @@ void Hid::StartConsoleSixAxisSensor(Kernel::HLERequestContext& ctx) {
1455void Hid::StopConsoleSixAxisSensor(Kernel::HLERequestContext& ctx) { 1546void Hid::StopConsoleSixAxisSensor(Kernel::HLERequestContext& ctx) {
1456 IPC::RequestParser rp{ctx}; 1547 IPC::RequestParser rp{ctx};
1457 struct Parameters { 1548 struct Parameters {
1458 Controller_NPad::DeviceHandle sixaxis_handle; 1549 Core::HID::ConsoleSixAxisSensorHandle console_sixaxis_handle;
1459 INSERT_PADDING_WORDS_NOINIT(1); 1550 INSERT_PADDING_WORDS_NOINIT(1);
1460 u64 applet_resource_user_id; 1551 u64 applet_resource_user_id;
1461 }; 1552 };
1553 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
1462 1554
1463 const auto parameters{rp.PopRaw<Parameters>()}; 1555 const auto parameters{rp.PopRaw<Parameters>()};
1464 1556
1465 LOG_WARNING( 1557 LOG_WARNING(Service_HID,
1466 Service_HID, 1558 "(STUBBED) called, unknown_1={}, unknown_2={}, applet_resource_user_id={}",
1467 "(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", 1559 parameters.console_sixaxis_handle.unknown_1,
1468 parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, 1560 parameters.console_sixaxis_handle.unknown_2, parameters.applet_resource_user_id);
1469 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
1470 1561
1471 IPC::ResponseBuilder rb{ctx, 2}; 1562 IPC::ResponseBuilder rb{ctx, 2};
1472 rb.Push(ResultSuccess); 1563 rb.Push(ResultSuccess);
@@ -1620,10 +1711,8 @@ void Hid::SetNpadCommunicationMode(Kernel::HLERequestContext& ctx) {
1620 1711
1621void Hid::GetNpadCommunicationMode(Kernel::HLERequestContext& ctx) { 1712void Hid::GetNpadCommunicationMode(Kernel::HLERequestContext& ctx) {
1622 IPC::RequestParser rp{ctx}; 1713 IPC::RequestParser rp{ctx};
1623 const auto applet_resource_user_id{rp.Pop<u64>()};
1624 1714
1625 LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}", 1715 LOG_WARNING(Service_HID, "(STUBBED) called");
1626 applet_resource_user_id);
1627 1716
1628 IPC::ResponseBuilder rb{ctx, 4}; 1717 IPC::ResponseBuilder rb{ctx, 4};
1629 rb.Push(ResultSuccess); 1718 rb.Push(ResultSuccess);
@@ -1825,7 +1914,7 @@ public:
1825 {317, nullptr, "GetNpadLeftRightInterfaceType"}, 1914 {317, nullptr, "GetNpadLeftRightInterfaceType"},
1826 {318, nullptr, "HasBattery"}, 1915 {318, nullptr, "HasBattery"},
1827 {319, nullptr, "HasLeftRightBattery"}, 1916 {319, nullptr, "HasLeftRightBattery"},
1828 {321, nullptr, "GetUniquePadsFromNpad"}, 1917 {321, &HidSys::GetUniquePadsFromNpad, "GetUniquePadsFromNpad"},
1829 {322, nullptr, "GetIrSensorState"}, 1918 {322, nullptr, "GetIrSensorState"},
1830 {323, nullptr, "GetXcdHandleForNpadWithIrSensor"}, 1919 {323, nullptr, "GetXcdHandleForNpadWithIrSensor"},
1831 {324, nullptr, "GetUniquePadButtonSet"}, 1920 {324, nullptr, "GetUniquePadButtonSet"},
@@ -1996,6 +2085,18 @@ private:
1996 IPC::ResponseBuilder rb{ctx, 2}; 2085 IPC::ResponseBuilder rb{ctx, 2};
1997 rb.Push(ResultSuccess); 2086 rb.Push(ResultSuccess);
1998 } 2087 }
2088
2089 void GetUniquePadsFromNpad(Kernel::HLERequestContext& ctx) {
2090 IPC::RequestParser rp{ctx};
2091 const auto npad_id_type{rp.PopEnum<Core::HID::NpadIdType>()};
2092
2093 const s64 total_entries = 0;
2094 LOG_WARNING(Service_HID, "(STUBBED) called, npad_id_type={}", npad_id_type);
2095
2096 IPC::ResponseBuilder rb{ctx, 3};
2097 rb.Push(ResultSuccess);
2098 rb.Push(total_entries);
2099 }
1999}; 2100};
2000 2101
2001class HidTmp final : public ServiceFramework<HidTmp> { 2102class HidTmp final : public ServiceFramework<HidTmp> {
@@ -2037,10 +2138,6 @@ public:
2037 } 2138 }
2038}; 2139};
2039 2140
2040void ReloadInputDevices() {
2041 Settings::values.is_device_reload_pending.store(true);
2042}
2043
2044void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) { 2141void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) {
2045 std::make_shared<Hid>(system)->InstallAsService(service_manager); 2142 std::make_shared<Hid>(system)->InstallAsService(service_manager);
2046 std::make_shared<HidBus>(system)->InstallAsService(service_manager); 2143 std::make_shared<HidBus>(system)->InstallAsService(service_manager);
diff --git a/src/core/hle/service/hid/hid.h b/src/core/hle/service/hid/hid.h
index b1fe75e94..d290df161 100644
--- a/src/core/hle/service/hid/hid.h
+++ b/src/core/hle/service/hid/hid.h
@@ -60,21 +60,23 @@ public:
60private: 60private:
61 template <typename T> 61 template <typename T>
62 void MakeController(HidController controller) { 62 void MakeController(HidController controller) {
63 controllers[static_cast<std::size_t>(controller)] = std::make_unique<T>(system); 63 controllers[static_cast<std::size_t>(controller)] = std::make_unique<T>(system.HIDCore());
64 } 64 }
65 template <typename T> 65 template <typename T>
66 void MakeControllerWithServiceContext(HidController controller) { 66 void MakeControllerWithServiceContext(HidController controller) {
67 controllers[static_cast<std::size_t>(controller)] = 67 controllers[static_cast<std::size_t>(controller)] =
68 std::make_unique<T>(system, service_context); 68 std::make_unique<T>(system.HIDCore(), service_context);
69 } 69 }
70 70
71 void GetSharedMemoryHandle(Kernel::HLERequestContext& ctx); 71 void GetSharedMemoryHandle(Kernel::HLERequestContext& ctx);
72 void UpdateControllers(std::uintptr_t user_data, std::chrono::nanoseconds ns_late); 72 void UpdateControllers(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
73 void UpdateMouseKeyboard(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
73 void UpdateMotion(std::uintptr_t user_data, std::chrono::nanoseconds ns_late); 74 void UpdateMotion(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
74 75
75 KernelHelpers::ServiceContext& service_context; 76 KernelHelpers::ServiceContext& service_context;
76 77
77 std::shared_ptr<Core::Timing::EventType> pad_update_event; 78 std::shared_ptr<Core::Timing::EventType> pad_update_event;
79 std::shared_ptr<Core::Timing::EventType> mouse_keyboard_update_event;
78 std::shared_ptr<Core::Timing::EventType> motion_update_event; 80 std::shared_ptr<Core::Timing::EventType> motion_update_event;
79 81
80 std::array<std::unique_ptr<ControllerBase>, static_cast<size_t>(HidController::MaxControllers)> 82 std::array<std::unique_ptr<ControllerBase>, static_cast<size_t>(HidController::MaxControllers)>
@@ -134,6 +136,8 @@ private:
134 void IsUnintendedHomeButtonInputProtectionEnabled(Kernel::HLERequestContext& ctx); 136 void IsUnintendedHomeButtonInputProtectionEnabled(Kernel::HLERequestContext& ctx);
135 void EnableUnintendedHomeButtonInputProtection(Kernel::HLERequestContext& ctx); 137 void EnableUnintendedHomeButtonInputProtection(Kernel::HLERequestContext& ctx);
136 void SetNpadAnalogStickUseCenterClamp(Kernel::HLERequestContext& ctx); 138 void SetNpadAnalogStickUseCenterClamp(Kernel::HLERequestContext& ctx);
139 void SetNpadCaptureButtonAssignment(Kernel::HLERequestContext& ctx);
140 void ClearNpadCaptureButtonAssignment(Kernel::HLERequestContext& ctx);
137 void GetVibrationDeviceInfo(Kernel::HLERequestContext& ctx); 141 void GetVibrationDeviceInfo(Kernel::HLERequestContext& ctx);
138 void SendVibrationValue(Kernel::HLERequestContext& ctx); 142 void SendVibrationValue(Kernel::HLERequestContext& ctx);
139 void GetActualVibrationValue(Kernel::HLERequestContext& ctx); 143 void GetActualVibrationValue(Kernel::HLERequestContext& ctx);
@@ -161,38 +165,11 @@ private:
161 void GetNpadCommunicationMode(Kernel::HLERequestContext& ctx); 165 void GetNpadCommunicationMode(Kernel::HLERequestContext& ctx);
162 void SetTouchScreenConfiguration(Kernel::HLERequestContext& ctx); 166 void SetTouchScreenConfiguration(Kernel::HLERequestContext& ctx);
163 167
164 enum class VibrationDeviceType : u32 {
165 Unknown = 0,
166 LinearResonantActuator = 1,
167 GcErm = 2,
168 };
169
170 enum class VibrationDevicePosition : u32 {
171 None = 0,
172 Left = 1,
173 Right = 2,
174 };
175
176 enum class VibrationGcErmCommand : u64 {
177 Stop = 0,
178 Start = 1,
179 StopHard = 2,
180 };
181
182 struct VibrationDeviceInfo {
183 VibrationDeviceType type{};
184 VibrationDevicePosition position{};
185 };
186 static_assert(sizeof(VibrationDeviceInfo) == 0x8, "VibrationDeviceInfo has incorrect size.");
187
188 std::shared_ptr<IAppletResource> applet_resource; 168 std::shared_ptr<IAppletResource> applet_resource;
189 169
190 KernelHelpers::ServiceContext service_context; 170 KernelHelpers::ServiceContext service_context;
191}; 171};
192 172
193/// Reload input devices. Used when input configuration changed
194void ReloadInputDevices();
195
196/// Registers all HID services with the specified service manager. 173/// Registers all HID services with the specified service manager.
197void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system); 174void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system);
198 175
diff --git a/src/core/hle/service/hid/ring_lifo.h b/src/core/hle/service/hid/ring_lifo.h
new file mode 100644
index 000000000..44c20d967
--- /dev/null
+++ b/src/core/hle/service/hid/ring_lifo.h
@@ -0,0 +1,54 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included
4
5#pragma once
6
7#include <array>
8
9#include "common/common_types.h"
10
11namespace Service::HID {
12
13template <typename State>
14struct AtomicStorage {
15 s64 sampling_number;
16 State state;
17};
18
19template <typename State, std::size_t max_buffer_size>
20struct Lifo {
21 s64 timestamp{};
22 s64 total_buffer_count = static_cast<s64>(max_buffer_size);
23 s64 buffer_tail{};
24 s64 buffer_count{};
25 std::array<AtomicStorage<State>, max_buffer_size> entries{};
26
27 const AtomicStorage<State>& ReadCurrentEntry() const {
28 return entries[buffer_tail];
29 }
30
31 const AtomicStorage<State>& ReadPreviousEntry() const {
32 return entries[GetPreviousEntryIndex()];
33 }
34
35 std::size_t GetPreviousEntryIndex() const {
36 return static_cast<size_t>((buffer_tail + total_buffer_count - 1) % total_buffer_count);
37 }
38
39 std::size_t GetNextEntryIndex() const {
40 return static_cast<size_t>((buffer_tail + 1) % total_buffer_count);
41 }
42
43 void WriteNextEntry(const State& new_state) {
44 if (buffer_count < total_buffer_count - 1) {
45 buffer_count++;
46 }
47 buffer_tail = GetNextEntryIndex();
48 const auto& previous_entry = ReadPreviousEntry();
49 entries[buffer_tail].sampling_number = previous_entry.sampling_number + 1;
50 entries[buffer_tail].state = new_state;
51 }
52};
53
54} // namespace Service::HID
diff --git a/src/core/hle/service/ldr/ldr.cpp b/src/core/hle/service/ldr/ldr.cpp
index 32eff3b2a..3782703d2 100644
--- a/src/core/hle/service/ldr/ldr.cpp
+++ b/src/core/hle/service/ldr/ldr.cpp
@@ -396,12 +396,12 @@ public:
396 CopyCode(nro_addr + nro_header.segment_headers[DATA_INDEX].memory_offset, data_start, 396 CopyCode(nro_addr + nro_header.segment_headers[DATA_INDEX].memory_offset, data_start,
397 nro_header.segment_headers[DATA_INDEX].memory_size); 397 nro_header.segment_headers[DATA_INDEX].memory_size);
398 398
399 CASCADE_CODE(process->PageTable().SetCodeMemoryPermission( 399 CASCADE_CODE(process->PageTable().SetProcessMemoryPermission(
400 text_start, ro_start - text_start, Kernel::KMemoryPermission::ReadAndExecute)); 400 text_start, ro_start - text_start, Kernel::KMemoryPermission::ReadAndExecute));
401 CASCADE_CODE(process->PageTable().SetCodeMemoryPermission(ro_start, data_start - ro_start, 401 CASCADE_CODE(process->PageTable().SetProcessMemoryPermission(
402 Kernel::KMemoryPermission::Read)); 402 ro_start, data_start - ro_start, Kernel::KMemoryPermission::Read));
403 403
404 return process->PageTable().SetCodeMemoryPermission( 404 return process->PageTable().SetProcessMemoryPermission(
405 data_start, bss_end_addr - data_start, Kernel::KMemoryPermission::ReadAndWrite); 405 data_start, bss_end_addr - data_start, Kernel::KMemoryPermission::ReadAndWrite);
406 } 406 }
407 407
diff --git a/src/core/hle/service/ns/ns.cpp b/src/core/hle/service/ns/ns.cpp
index 64ffc8572..382ddcae5 100644
--- a/src/core/hle/service/ns/ns.cpp
+++ b/src/core/hle/service/ns/ns.cpp
@@ -12,6 +12,7 @@
12#include "core/hle/service/ns/errors.h" 12#include "core/hle/service/ns/errors.h"
13#include "core/hle/service/ns/language.h" 13#include "core/hle/service/ns/language.h"
14#include "core/hle/service/ns/ns.h" 14#include "core/hle/service/ns/ns.h"
15#include "core/hle/service/ns/pdm_qry.h"
15#include "core/hle/service/ns/pl_u.h" 16#include "core/hle/service/ns/pl_u.h"
16#include "core/hle/service/set/set.h" 17#include "core/hle/service/set/set.h"
17 18
@@ -570,11 +571,29 @@ IFactoryResetInterface::IFactoryResetInterface(Core::System& system_)
570 571
571IFactoryResetInterface::~IFactoryResetInterface() = default; 572IFactoryResetInterface::~IFactoryResetInterface() = default;
572 573
574IReadOnlyApplicationControlDataInterface::IReadOnlyApplicationControlDataInterface(
575 Core::System& system_)
576 : ServiceFramework{system_, "IReadOnlyApplicationControlDataInterface"} {
577 // clang-format off
578 static const FunctionInfo functions[] = {
579 {0, nullptr, "GetApplicationControlData"},
580 {1, nullptr, "GetApplicationDesiredLanguage"},
581 {2, nullptr, "ConvertApplicationLanguageToLanguageCode"},
582 {3, nullptr, "ConvertLanguageCodeToApplicationLanguage"},
583 {4, nullptr, "SelectApplicationDesiredLanguage"},
584 };
585 // clang-format on
586
587 RegisterHandlers(functions);
588}
589
590IReadOnlyApplicationControlDataInterface::~IReadOnlyApplicationControlDataInterface() = default;
591
573NS::NS(const char* name, Core::System& system_) : ServiceFramework{system_, name} { 592NS::NS(const char* name, Core::System& system_) : ServiceFramework{system_, name} {
574 // clang-format off 593 // clang-format off
575 static const FunctionInfo functions[] = { 594 static const FunctionInfo functions[] = {
576 {7988, nullptr, "GetDynamicRightsInterface"}, 595 {7988, nullptr, "GetDynamicRightsInterface"},
577 {7989, nullptr, "GetReadOnlyApplicationControlDataInterface"}, 596 {7989, &NS::PushInterface<IReadOnlyApplicationControlDataInterface>, "GetReadOnlyApplicationControlDataInterface"},
578 {7991, nullptr, "GetReadOnlyApplicationRecordInterface"}, 597 {7991, nullptr, "GetReadOnlyApplicationRecordInterface"},
579 {7992, &NS::PushInterface<IECommerceInterface>, "GetECommerceInterface"}, 598 {7992, &NS::PushInterface<IECommerceInterface>, "GetECommerceInterface"},
580 {7993, &NS::PushInterface<IApplicationVersionInterface>, "GetApplicationVersionInterface"}, 599 {7993, &NS::PushInterface<IApplicationVersionInterface>, "GetApplicationVersionInterface"},
@@ -738,6 +757,8 @@ void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system
738 std::make_shared<NS_SU>(system)->InstallAsService(service_manager); 757 std::make_shared<NS_SU>(system)->InstallAsService(service_manager);
739 std::make_shared<NS_VM>(system)->InstallAsService(service_manager); 758 std::make_shared<NS_VM>(system)->InstallAsService(service_manager);
740 759
760 std::make_shared<PDM_QRY>(system)->InstallAsService(service_manager);
761
741 std::make_shared<PL_U>(system)->InstallAsService(service_manager); 762 std::make_shared<PL_U>(system)->InstallAsService(service_manager);
742} 763}
743 764
diff --git a/src/core/hle/service/ns/ns.h b/src/core/hle/service/ns/ns.h
index 218eec3ec..43540b0fb 100644
--- a/src/core/hle/service/ns/ns.h
+++ b/src/core/hle/service/ns/ns.h
@@ -74,6 +74,13 @@ public:
74 ~IFactoryResetInterface() override; 74 ~IFactoryResetInterface() override;
75}; 75};
76 76
77class IReadOnlyApplicationControlDataInterface final
78 : public ServiceFramework<IReadOnlyApplicationControlDataInterface> {
79public:
80 explicit IReadOnlyApplicationControlDataInterface(Core::System& system_);
81 ~IReadOnlyApplicationControlDataInterface() override;
82};
83
77class NS final : public ServiceFramework<NS> { 84class NS final : public ServiceFramework<NS> {
78public: 85public:
79 explicit NS(const char* name, Core::System& system_); 86 explicit NS(const char* name, Core::System& system_);
diff --git a/src/core/hle/service/ns/pdm_qry.cpp b/src/core/hle/service/ns/pdm_qry.cpp
new file mode 100644
index 000000000..e2fab5c3f
--- /dev/null
+++ b/src/core/hle/service/ns/pdm_qry.cpp
@@ -0,0 +1,69 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included
4
5#include <memory>
6
7#include "common/logging/log.h"
8#include "common/uuid.h"
9#include "core/hle/ipc_helpers.h"
10#include "core/hle/service/ns/pdm_qry.h"
11#include "core/hle/service/service.h"
12#include "core/hle/service/sm/sm.h"
13
14namespace Service::NS {
15
16PDM_QRY::PDM_QRY(Core::System& system_) : ServiceFramework{system_, "pdm:qry"} {
17 // clang-format off
18 static const FunctionInfo functions[] = {
19 {0, nullptr, "QueryAppletEvent"},
20 {1, nullptr, "QueryPlayStatistics"},
21 {2, nullptr, "QueryPlayStatisticsByUserAccountId"},
22 {3, nullptr, "QueryPlayStatisticsByNetworkServiceAccountId"},
23 {4, nullptr, "QueryPlayStatisticsByApplicationId"},
24 {5, &PDM_QRY::QueryPlayStatisticsByApplicationIdAndUserAccountId, "QueryPlayStatisticsByApplicationIdAndUserAccountId"},
25 {6, nullptr, "QueryPlayStatisticsByApplicationIdAndNetworkServiceAccountId"},
26 {7, nullptr, "QueryLastPlayTimeV0"},
27 {8, nullptr, "QueryPlayEvent"},
28 {9, nullptr, "GetAvailablePlayEventRange"},
29 {10, nullptr, "QueryAccountEvent"},
30 {11, nullptr, "QueryAccountPlayEvent"},
31 {12, nullptr, "GetAvailableAccountPlayEventRange"},
32 {13, nullptr, "QueryApplicationPlayStatisticsForSystemV0"},
33 {14, nullptr, "QueryRecentlyPlayedApplication"},
34 {15, nullptr, "GetRecentlyPlayedApplicationUpdateEvent"},
35 {16, nullptr, "QueryApplicationPlayStatisticsByUserAccountIdForSystemV0"},
36 {17, nullptr, "QueryLastPlayTime"},
37 {18, nullptr, "QueryApplicationPlayStatisticsForSystem"},
38 {19, nullptr, "QueryApplicationPlayStatisticsByUserAccountIdForSystem"},
39 };
40 // clang-format on
41
42 RegisterHandlers(functions);
43}
44
45PDM_QRY::~PDM_QRY() = default;
46
47void PDM_QRY::QueryPlayStatisticsByApplicationIdAndUserAccountId(Kernel::HLERequestContext& ctx) {
48 IPC::RequestParser rp{ctx};
49 const auto unknown = rp.Pop<bool>();
50 rp.Pop<u8>(); // Padding
51 const auto application_id = rp.Pop<u64>();
52 const auto user_account_uid = rp.PopRaw<Common::UUID>();
53
54 // TODO(German77): Read statistics of the game
55 PlayStatistics statistics{
56 .application_id = application_id,
57 .total_launches = 1,
58 };
59
60 LOG_WARNING(Service_NS,
61 "(STUBBED) called. unknown={}. application_id=0x{:016X}, user_account_uid=0x{}",
62 unknown, application_id, user_account_uid.Format());
63
64 IPC::ResponseBuilder rb{ctx, 12};
65 rb.Push(ResultSuccess);
66 rb.PushRaw(statistics);
67}
68
69} // namespace Service::NS
diff --git a/src/core/hle/service/ns/pdm_qry.h b/src/core/hle/service/ns/pdm_qry.h
new file mode 100644
index 000000000..516136314
--- /dev/null
+++ b/src/core/hle/service/ns/pdm_qry.h
@@ -0,0 +1,33 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included
4
5#pragma once
6
7#include "core/hle/service/service.h"
8
9namespace Service::NS {
10
11struct PlayStatistics {
12 u64 application_id{};
13 u32 first_entry_index{};
14 u32 first_timestamp_user{};
15 u32 first_timestamp_network{};
16 u32 last_entry_index{};
17 u32 last_timestamp_user{};
18 u32 last_timestamp_network{};
19 u32 play_time_in_minutes{};
20 u32 total_launches{};
21};
22static_assert(sizeof(PlayStatistics) == 0x28, "PlayStatistics is an invalid size");
23
24class PDM_QRY final : public ServiceFramework<PDM_QRY> {
25public:
26 explicit PDM_QRY(Core::System& system_);
27 ~PDM_QRY() override;
28
29private:
30 void QueryPlayStatisticsByApplicationIdAndUserAccountId(Kernel::HLERequestContext& ctx);
31};
32
33} // namespace Service::NS
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp
index 0d7d4ad03..8314d1ec2 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp
@@ -20,8 +20,12 @@ NvResult nvhost_nvdec::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>&
20 switch (command.group) { 20 switch (command.group) {
21 case 0x0: 21 case 0x0:
22 switch (command.cmd) { 22 switch (command.cmd) {
23 case 0x1: 23 case 0x1: {
24 return Submit(input, output); 24 if (!fd_to_id.contains(fd)) {
25 fd_to_id[fd] = next_id++;
26 }
27 return Submit(fd, input, output);
28 }
25 case 0x2: 29 case 0x2:
26 return GetSyncpoint(input, output); 30 return GetSyncpoint(input, output);
27 case 0x3: 31 case 0x3:
@@ -66,7 +70,10 @@ void nvhost_nvdec::OnOpen(DeviceFD fd) {}
66 70
67void nvhost_nvdec::OnClose(DeviceFD fd) { 71void nvhost_nvdec::OnClose(DeviceFD fd) {
68 LOG_INFO(Service_NVDRV, "NVDEC video stream ended"); 72 LOG_INFO(Service_NVDRV, "NVDEC video stream ended");
69 system.GPU().ClearCdmaInstance(); 73 const auto iter = fd_to_id.find(fd);
74 if (iter != fd_to_id.end()) {
75 system.GPU().ClearCdmaInstance(iter->second);
76 }
70} 77}
71 78
72} // namespace Service::Nvidia::Devices 79} // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h
index 523d96e3a..a507c4d0a 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h
@@ -24,6 +24,9 @@ public:
24 24
25 void OnOpen(DeviceFD fd) override; 25 void OnOpen(DeviceFD fd) override;
26 void OnClose(DeviceFD fd) override; 26 void OnClose(DeviceFD fd) override;
27
28private:
29 u32 next_id{};
27}; 30};
28 31
29} // namespace Service::Nvidia::Devices 32} // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp
index e61261f98..8a05f0668 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp
@@ -59,7 +59,8 @@ NvResult nvhost_nvdec_common::SetNVMAPfd(const std::vector<u8>& input) {
59 return NvResult::Success; 59 return NvResult::Success;
60} 60}
61 61
62NvResult nvhost_nvdec_common::Submit(const std::vector<u8>& input, std::vector<u8>& output) { 62NvResult nvhost_nvdec_common::Submit(DeviceFD fd, const std::vector<u8>& input,
63 std::vector<u8>& output) {
63 IoctlSubmit params{}; 64 IoctlSubmit params{};
64 std::memcpy(&params, input.data(), sizeof(IoctlSubmit)); 65 std::memcpy(&params, input.data(), sizeof(IoctlSubmit));
65 LOG_DEBUG(Service_NVDRV, "called NVDEC Submit, cmd_buffer_count={}", params.cmd_buffer_count); 66 LOG_DEBUG(Service_NVDRV, "called NVDEC Submit, cmd_buffer_count={}", params.cmd_buffer_count);
@@ -93,7 +94,7 @@ NvResult nvhost_nvdec_common::Submit(const std::vector<u8>& input, std::vector<u
93 Tegra::ChCommandHeaderList cmdlist(cmd_buffer.word_count); 94 Tegra::ChCommandHeaderList cmdlist(cmd_buffer.word_count);
94 system.Memory().ReadBlock(object->addr + cmd_buffer.offset, cmdlist.data(), 95 system.Memory().ReadBlock(object->addr + cmd_buffer.offset, cmdlist.data(),
95 cmdlist.size() * sizeof(u32)); 96 cmdlist.size() * sizeof(u32));
96 gpu.PushCommandBuffer(cmdlist); 97 gpu.PushCommandBuffer(fd_to_id[fd], cmdlist);
97 } 98 }
98 std::memcpy(output.data(), &params, sizeof(IoctlSubmit)); 99 std::memcpy(output.data(), &params, sizeof(IoctlSubmit));
99 // Some games expect command_buffers to be written back 100 // Some games expect command_buffers to be written back
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h
index 351625c17..e28c54df6 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h
@@ -104,13 +104,14 @@ protected:
104 104
105 /// Ioctl command implementations 105 /// Ioctl command implementations
106 NvResult SetNVMAPfd(const std::vector<u8>& input); 106 NvResult SetNVMAPfd(const std::vector<u8>& input);
107 NvResult Submit(const std::vector<u8>& input, std::vector<u8>& output); 107 NvResult Submit(DeviceFD fd, const std::vector<u8>& input, std::vector<u8>& output);
108 NvResult GetSyncpoint(const std::vector<u8>& input, std::vector<u8>& output); 108 NvResult GetSyncpoint(const std::vector<u8>& input, std::vector<u8>& output);
109 NvResult GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output); 109 NvResult GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output);
110 NvResult MapBuffer(const std::vector<u8>& input, std::vector<u8>& output); 110 NvResult MapBuffer(const std::vector<u8>& input, std::vector<u8>& output);
111 NvResult UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& output); 111 NvResult UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& output);
112 NvResult SetSubmitTimeout(const std::vector<u8>& input, std::vector<u8>& output); 112 NvResult SetSubmitTimeout(const std::vector<u8>& input, std::vector<u8>& output);
113 113
114 std::unordered_map<DeviceFD, u32> fd_to_id{};
114 s32_le nvmap_fd{}; 115 s32_le nvmap_fd{};
115 u32_le submit_timeout{}; 116 u32_le submit_timeout{};
116 std::shared_ptr<nvmap> nvmap_dev; 117 std::shared_ptr<nvmap> nvmap_dev;
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp b/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp
index eac4dd530..76b39806f 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp
@@ -21,7 +21,10 @@ NvResult nvhost_vic::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& i
21 case 0x0: 21 case 0x0:
22 switch (command.cmd) { 22 switch (command.cmd) {
23 case 0x1: 23 case 0x1:
24 return Submit(input, output); 24 if (!fd_to_id.contains(fd)) {
25 fd_to_id[fd] = next_id++;
26 }
27 return Submit(fd, input, output);
25 case 0x2: 28 case 0x2:
26 return GetSyncpoint(input, output); 29 return GetSyncpoint(input, output);
27 case 0x3: 30 case 0x3:
@@ -65,7 +68,10 @@ NvResult nvhost_vic::Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>& i
65void nvhost_vic::OnOpen(DeviceFD fd) {} 68void nvhost_vic::OnOpen(DeviceFD fd) {}
66 69
67void nvhost_vic::OnClose(DeviceFD fd) { 70void nvhost_vic::OnClose(DeviceFD fd) {
68 system.GPU().ClearCdmaInstance(); 71 const auto iter = fd_to_id.find(fd);
72 if (iter != fd_to_id.end()) {
73 system.GPU().ClearCdmaInstance(iter->second);
74 }
69} 75}
70 76
71} // namespace Service::Nvidia::Devices 77} // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_vic.h b/src/core/hle/service/nvdrv/devices/nvhost_vic.h
index 6d7fda9d1..c9732c037 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_vic.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_vic.h
@@ -23,5 +23,8 @@ public:
23 23
24 void OnOpen(DeviceFD fd) override; 24 void OnOpen(DeviceFD fd) override;
25 void OnClose(DeviceFD fd) override; 25 void OnClose(DeviceFD fd) override;
26
27private:
28 u32 next_id{};
26}; 29};
27} // namespace Service::Nvidia::Devices 30} // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/nvdata.h b/src/core/hle/service/nvdrv/nvdata.h
index 3294bc0e7..5ab221fc1 100644
--- a/src/core/hle/service/nvdrv/nvdata.h
+++ b/src/core/hle/service/nvdrv/nvdata.h
@@ -1,3 +1,7 @@
1// Copyright 2019 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
1#pragma once 5#pragma once
2 6
3#include <array> 7#include <array>
diff --git a/src/core/hle/service/pm/pm.cpp b/src/core/hle/service/pm/pm.cpp
index 88fc5b5cc..277abc17a 100644
--- a/src/core/hle/service/pm/pm.cpp
+++ b/src/core/hle/service/pm/pm.cpp
@@ -13,7 +13,12 @@ namespace Service::PM {
13 13
14namespace { 14namespace {
15 15
16constexpr ResultCode ERROR_PROCESS_NOT_FOUND{ErrorModule::PM, 1}; 16constexpr ResultCode ResultProcessNotFound{ErrorModule::PM, 1};
17[[maybe_unused]] constexpr ResultCode ResultAlreadyStarted{ErrorModule::PM, 2};
18[[maybe_unused]] constexpr ResultCode ResultNotTerminated{ErrorModule::PM, 3};
19[[maybe_unused]] constexpr ResultCode ResultDebugHookInUse{ErrorModule::PM, 4};
20[[maybe_unused]] constexpr ResultCode ResultApplicationRunning{ErrorModule::PM, 5};
21[[maybe_unused]] constexpr ResultCode ResultInvalidSize{ErrorModule::PM, 6};
17 22
18constexpr u64 NO_PROCESS_FOUND_PID{0}; 23constexpr u64 NO_PROCESS_FOUND_PID{0};
19 24
@@ -95,18 +100,18 @@ public:
95private: 100private:
96 void GetProcessId(Kernel::HLERequestContext& ctx) { 101 void GetProcessId(Kernel::HLERequestContext& ctx) {
97 IPC::RequestParser rp{ctx}; 102 IPC::RequestParser rp{ctx};
98 const auto title_id = rp.PopRaw<u64>(); 103 const auto program_id = rp.PopRaw<u64>();
99 104
100 LOG_DEBUG(Service_PM, "called, title_id={:016X}", title_id); 105 LOG_DEBUG(Service_PM, "called, program_id={:016X}", program_id);
101 106
102 const auto process = 107 const auto process =
103 SearchProcessList(kernel.GetProcessList(), [title_id](const auto& proc) { 108 SearchProcessList(kernel.GetProcessList(), [program_id](const auto& proc) {
104 return proc->GetProgramID() == title_id; 109 return proc->GetProgramID() == program_id;
105 }); 110 });
106 111
107 if (!process.has_value()) { 112 if (!process.has_value()) {
108 IPC::ResponseBuilder rb{ctx, 2}; 113 IPC::ResponseBuilder rb{ctx, 2};
109 rb.Push(ERROR_PROCESS_NOT_FOUND); 114 rb.Push(ResultProcessNotFound);
110 return; 115 return;
111 } 116 }
112 117
@@ -128,13 +133,16 @@ public:
128 explicit Info(Core::System& system_, const std::vector<Kernel::KProcess*>& process_list_) 133 explicit Info(Core::System& system_, const std::vector<Kernel::KProcess*>& process_list_)
129 : ServiceFramework{system_, "pm:info"}, process_list{process_list_} { 134 : ServiceFramework{system_, "pm:info"}, process_list{process_list_} {
130 static const FunctionInfo functions[] = { 135 static const FunctionInfo functions[] = {
131 {0, &Info::GetTitleId, "GetTitleId"}, 136 {0, &Info::GetProgramId, "GetProgramId"},
137 {65000, &Info::AtmosphereGetProcessId, "AtmosphereGetProcessId"},
138 {65001, nullptr, "AtmosphereHasLaunchedProgram"},
139 {65002, nullptr, "AtmosphereGetProcessInfo"},
132 }; 140 };
133 RegisterHandlers(functions); 141 RegisterHandlers(functions);
134 } 142 }
135 143
136private: 144private:
137 void GetTitleId(Kernel::HLERequestContext& ctx) { 145 void GetProgramId(Kernel::HLERequestContext& ctx) {
138 IPC::RequestParser rp{ctx}; 146 IPC::RequestParser rp{ctx};
139 const auto process_id = rp.PopRaw<u64>(); 147 const auto process_id = rp.PopRaw<u64>();
140 148
@@ -146,7 +154,7 @@ private:
146 154
147 if (!process.has_value()) { 155 if (!process.has_value()) {
148 IPC::ResponseBuilder rb{ctx, 2}; 156 IPC::ResponseBuilder rb{ctx, 2};
149 rb.Push(ERROR_PROCESS_NOT_FOUND); 157 rb.Push(ResultProcessNotFound);
150 return; 158 return;
151 } 159 }
152 160
@@ -155,6 +163,27 @@ private:
155 rb.Push((*process)->GetProgramID()); 163 rb.Push((*process)->GetProgramID());
156 } 164 }
157 165
166 void AtmosphereGetProcessId(Kernel::HLERequestContext& ctx) {
167 IPC::RequestParser rp{ctx};
168 const auto program_id = rp.PopRaw<u64>();
169
170 LOG_DEBUG(Service_PM, "called, program_id={:016X}", program_id);
171
172 const auto process = SearchProcessList(process_list, [program_id](const auto& proc) {
173 return proc->GetProgramID() == program_id;
174 });
175
176 if (!process.has_value()) {
177 IPC::ResponseBuilder rb{ctx, 2};
178 rb.Push(ResultProcessNotFound);
179 return;
180 }
181
182 IPC::ResponseBuilder rb{ctx, 4};
183 rb.Push(ResultSuccess);
184 rb.Push((*process)->GetProcessID());
185 }
186
158 const std::vector<Kernel::KProcess*>& process_list; 187 const std::vector<Kernel::KProcess*>& process_list;
159}; 188};
160 189
diff --git a/src/core/loader/deconstructed_rom_directory.cpp b/src/core/loader/deconstructed_rom_directory.cpp
index 1b5aca65d..b47e3bf69 100644
--- a/src/core/loader/deconstructed_rom_directory.cpp
+++ b/src/core/loader/deconstructed_rom_directory.cpp
@@ -125,8 +125,9 @@ AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirect
125 } 125 }
126 metadata.Print(); 126 metadata.Print();
127 127
128 const auto static_modules = {"rtld", "main", "subsdk0", "subsdk1", "subsdk2", "subsdk3", 128 const auto static_modules = {"rtld", "main", "subsdk0", "subsdk1", "subsdk2",
129 "subsdk4", "subsdk5", "subsdk6", "subsdk7", "sdk"}; 129 "subsdk3", "subsdk4", "subsdk5", "subsdk6", "subsdk7",
130 "subsdk8", "subsdk9", "sdk"};
130 131
131 // Use the NSO module loader to figure out the code layout 132 // Use the NSO module loader to figure out the code layout
132 std::size_t code_size{}; 133 std::size_t code_size{};
diff --git a/src/core/memory/cheat_engine.cpp b/src/core/memory/cheat_engine.cpp
index 20f0e90f5..12446c9ac 100644
--- a/src/core/memory/cheat_engine.cpp
+++ b/src/core/memory/cheat_engine.cpp
@@ -19,7 +19,6 @@
19namespace Core::Memory { 19namespace Core::Memory {
20namespace { 20namespace {
21constexpr auto CHEAT_ENGINE_NS = std::chrono::nanoseconds{1000000000 / 12}; 21constexpr auto CHEAT_ENGINE_NS = std::chrono::nanoseconds{1000000000 / 12};
22constexpr u32 KEYPAD_BITMASK = 0x3FFFFFF;
23 22
24std::string_view ExtractName(std::string_view data, std::size_t start_index, char match) { 23std::string_view ExtractName(std::string_view data, std::size_t start_index, char match) {
25 auto end_index = start_index; 24 auto end_index = start_index;
@@ -61,7 +60,7 @@ u64 StandardVmCallbacks::HidKeysDown() {
61 applet_resource 60 applet_resource
62 ->GetController<Service::HID::Controller_NPad>(Service::HID::HidController::NPad) 61 ->GetController<Service::HID::Controller_NPad>(Service::HID::HidController::NPad)
63 .GetAndResetPressState(); 62 .GetAndResetPressState();
64 return press_state & KEYPAD_BITMASK; 63 return static_cast<u64>(press_state & HID::NpadButton::All);
65} 64}
66 65
67void StandardVmCallbacks::DebugLog(u8 id, u64 value) { 66void StandardVmCallbacks::DebugLog(u8 id, u64 value) {
diff --git a/src/core/perf_stats.h b/src/core/perf_stats.h
index a2541906f..816202588 100644
--- a/src/core/perf_stats.h
+++ b/src/core/perf_stats.h
@@ -33,7 +33,7 @@ public:
33 explicit PerfStats(u64 title_id_); 33 explicit PerfStats(u64 title_id_);
34 ~PerfStats(); 34 ~PerfStats();
35 35
36 using Clock = std::chrono::high_resolution_clock; 36 using Clock = std::chrono::steady_clock;
37 37
38 void BeginSystemFrame(); 38 void BeginSystemFrame();
39 void EndSystemFrame(); 39 void EndSystemFrame();
@@ -87,7 +87,7 @@ private:
87 87
88class SpeedLimiter { 88class SpeedLimiter {
89public: 89public:
90 using Clock = std::chrono::high_resolution_clock; 90 using Clock = std::chrono::steady_clock;
91 91
92 void DoSpeedLimiting(std::chrono::microseconds current_system_time_us); 92 void DoSpeedLimiting(std::chrono::microseconds current_system_time_us);
93 93
diff --git a/src/input_common/CMakeLists.txt b/src/input_common/CMakeLists.txt
index dd13d948f..d4fa69a77 100644
--- a/src/input_common/CMakeLists.txt
+++ b/src/input_common/CMakeLists.txt
@@ -1,36 +1,32 @@
1add_library(input_common STATIC 1add_library(input_common STATIC
2 analog_from_button.cpp 2 drivers/gc_adapter.cpp
3 analog_from_button.h 3 drivers/gc_adapter.h
4 keyboard.cpp 4 drivers/keyboard.cpp
5 keyboard.h 5 drivers/keyboard.h
6 drivers/mouse.cpp
7 drivers/mouse.h
8 drivers/sdl_driver.cpp
9 drivers/sdl_driver.h
10 drivers/tas_input.cpp
11 drivers/tas_input.h
12 drivers/touch_screen.cpp
13 drivers/touch_screen.h
14 drivers/udp_client.cpp
15 drivers/udp_client.h
16 helpers/stick_from_buttons.cpp
17 helpers/stick_from_buttons.h
18 helpers/touch_from_buttons.cpp
19 helpers/touch_from_buttons.h
20 helpers/udp_protocol.cpp
21 helpers/udp_protocol.h
22 input_engine.cpp
23 input_engine.h
24 input_mapping.cpp
25 input_mapping.h
26 input_poller.cpp
27 input_poller.h
6 main.cpp 28 main.cpp
7 main.h 29 main.h
8 motion_from_button.cpp
9 motion_from_button.h
10 motion_input.cpp
11 motion_input.h
12 touch_from_button.cpp
13 touch_from_button.h
14 gcadapter/gc_adapter.cpp
15 gcadapter/gc_adapter.h
16 gcadapter/gc_poller.cpp
17 gcadapter/gc_poller.h
18 mouse/mouse_input.cpp
19 mouse/mouse_input.h
20 mouse/mouse_poller.cpp
21 mouse/mouse_poller.h
22 sdl/sdl.cpp
23 sdl/sdl.h
24 tas/tas_input.cpp
25 tas/tas_input.h
26 tas/tas_poller.cpp
27 tas/tas_poller.h
28 udp/client.cpp
29 udp/client.h
30 udp/protocol.cpp
31 udp/protocol.h
32 udp/udp.cpp
33 udp/udp.h
34) 30)
35 31
36if (MSVC) 32if (MSVC)
@@ -57,8 +53,8 @@ endif()
57 53
58if (ENABLE_SDL2) 54if (ENABLE_SDL2)
59 target_sources(input_common PRIVATE 55 target_sources(input_common PRIVATE
60 sdl/sdl_impl.cpp 56 drivers/sdl_driver.cpp
61 sdl/sdl_impl.h 57 drivers/sdl_driver.h
62 ) 58 )
63 target_link_libraries(input_common PRIVATE SDL2) 59 target_link_libraries(input_common PRIVATE SDL2)
64 target_compile_definitions(input_common PRIVATE HAVE_SDL2) 60 target_compile_definitions(input_common PRIVATE HAVE_SDL2)
diff --git a/src/input_common/analog_from_button.cpp b/src/input_common/analog_from_button.cpp
deleted file mode 100755
index 2fafd077f..000000000
--- a/src/input_common/analog_from_button.cpp
+++ /dev/null
@@ -1,238 +0,0 @@
1// Copyright 2017 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <atomic>
6#include <chrono>
7#include <cmath>
8#include <thread>
9#include "common/math_util.h"
10#include "common/settings.h"
11#include "input_common/analog_from_button.h"
12
13namespace InputCommon {
14
15class Analog final : public Input::AnalogDevice {
16public:
17 using Button = std::unique_ptr<Input::ButtonDevice>;
18
19 Analog(Button up_, Button down_, Button left_, Button right_, Button modifier_,
20 float modifier_scale_, float modifier_angle_)
21 : up(std::move(up_)), down(std::move(down_)), left(std::move(left_)),
22 right(std::move(right_)), modifier(std::move(modifier_)), modifier_scale(modifier_scale_),
23 modifier_angle(modifier_angle_) {
24 Input::InputCallback<bool> callbacks{
25 [this]([[maybe_unused]] bool status) { UpdateStatus(); }};
26 up->SetCallback(callbacks);
27 down->SetCallback(callbacks);
28 left->SetCallback(callbacks);
29 right->SetCallback(callbacks);
30 modifier->SetCallback(callbacks);
31 }
32
33 bool IsAngleGreater(float old_angle, float new_angle) const {
34 constexpr float TAU = Common::PI * 2.0f;
35 // Use wider angle to ease the transition.
36 constexpr float aperture = TAU * 0.15f;
37 const float top_limit = new_angle + aperture;
38 return (old_angle > new_angle && old_angle <= top_limit) ||
39 (old_angle + TAU > new_angle && old_angle + TAU <= top_limit);
40 }
41
42 bool IsAngleSmaller(float old_angle, float new_angle) const {
43 constexpr float TAU = Common::PI * 2.0f;
44 // Use wider angle to ease the transition.
45 constexpr float aperture = TAU * 0.15f;
46 const float bottom_limit = new_angle - aperture;
47 return (old_angle >= bottom_limit && old_angle < new_angle) ||
48 (old_angle - TAU >= bottom_limit && old_angle - TAU < new_angle);
49 }
50
51 float GetAngle(std::chrono::time_point<std::chrono::steady_clock> now) const {
52 constexpr float TAU = Common::PI * 2.0f;
53 float new_angle = angle;
54
55 auto time_difference = static_cast<float>(
56 std::chrono::duration_cast<std::chrono::microseconds>(now - last_update).count());
57 time_difference /= 1000.0f * 1000.0f;
58 if (time_difference > 0.5f) {
59 time_difference = 0.5f;
60 }
61
62 if (IsAngleGreater(new_angle, goal_angle)) {
63 new_angle -= modifier_angle * time_difference;
64 if (new_angle < 0) {
65 new_angle += TAU;
66 }
67 if (!IsAngleGreater(new_angle, goal_angle)) {
68 return goal_angle;
69 }
70 } else if (IsAngleSmaller(new_angle, goal_angle)) {
71 new_angle += modifier_angle * time_difference;
72 if (new_angle >= TAU) {
73 new_angle -= TAU;
74 }
75 if (!IsAngleSmaller(new_angle, goal_angle)) {
76 return goal_angle;
77 }
78 } else {
79 return goal_angle;
80 }
81 return new_angle;
82 }
83
84 void SetGoalAngle(bool r, bool l, bool u, bool d) {
85 // Move to the right
86 if (r && !u && !d) {
87 goal_angle = 0.0f;
88 }
89
90 // Move to the upper right
91 if (r && u && !d) {
92 goal_angle = Common::PI * 0.25f;
93 }
94
95 // Move up
96 if (u && !l && !r) {
97 goal_angle = Common::PI * 0.5f;
98 }
99
100 // Move to the upper left
101 if (l && u && !d) {
102 goal_angle = Common::PI * 0.75f;
103 }
104
105 // Move to the left
106 if (l && !u && !d) {
107 goal_angle = Common::PI;
108 }
109
110 // Move to the bottom left
111 if (l && !u && d) {
112 goal_angle = Common::PI * 1.25f;
113 }
114
115 // Move down
116 if (d && !l && !r) {
117 goal_angle = Common::PI * 1.5f;
118 }
119
120 // Move to the bottom right
121 if (r && !u && d) {
122 goal_angle = Common::PI * 1.75f;
123 }
124 }
125
126 void UpdateStatus() {
127 const float coef = modifier->GetStatus() ? modifier_scale : 1.0f;
128
129 bool r = right->GetStatus();
130 bool l = left->GetStatus();
131 bool u = up->GetStatus();
132 bool d = down->GetStatus();
133
134 // Eliminate contradictory movements
135 if (r && l) {
136 r = false;
137 l = false;
138 }
139 if (u && d) {
140 u = false;
141 d = false;
142 }
143
144 // Move if a key is pressed
145 if (r || l || u || d) {
146 amplitude = coef;
147 } else {
148 amplitude = 0;
149 }
150
151 const auto now = std::chrono::steady_clock::now();
152 const auto time_difference = static_cast<u64>(
153 std::chrono::duration_cast<std::chrono::milliseconds>(now - last_update).count());
154
155 if (time_difference < 10) {
156 // Disable analog mode if inputs are too fast
157 SetGoalAngle(r, l, u, d);
158 angle = goal_angle;
159 } else {
160 angle = GetAngle(now);
161 SetGoalAngle(r, l, u, d);
162 }
163
164 last_update = now;
165 }
166
167 std::tuple<float, float> GetStatus() const override {
168 if (Settings::values.emulate_analog_keyboard) {
169 const auto now = std::chrono::steady_clock::now();
170 float angle_ = GetAngle(now);
171 return std::make_tuple(std::cos(angle_) * amplitude, std::sin(angle_) * amplitude);
172 }
173 constexpr float SQRT_HALF = 0.707106781f;
174 int x = 0, y = 0;
175 if (right->GetStatus()) {
176 ++x;
177 }
178 if (left->GetStatus()) {
179 --x;
180 }
181 if (up->GetStatus()) {
182 ++y;
183 }
184 if (down->GetStatus()) {
185 --y;
186 }
187 const float coef = modifier->GetStatus() ? modifier_scale : 1.0f;
188 return std::make_tuple(static_cast<float>(x) * coef * (y == 0 ? 1.0f : SQRT_HALF),
189 static_cast<float>(y) * coef * (x == 0 ? 1.0f : SQRT_HALF));
190 }
191
192 Input::AnalogProperties GetAnalogProperties() const override {
193 return {modifier_scale, 1.0f, 0.5f};
194 }
195
196 bool GetAnalogDirectionStatus(Input::AnalogDirection direction) const override {
197 switch (direction) {
198 case Input::AnalogDirection::RIGHT:
199 return right->GetStatus();
200 case Input::AnalogDirection::LEFT:
201 return left->GetStatus();
202 case Input::AnalogDirection::UP:
203 return up->GetStatus();
204 case Input::AnalogDirection::DOWN:
205 return down->GetStatus();
206 }
207 return false;
208 }
209
210private:
211 Button up;
212 Button down;
213 Button left;
214 Button right;
215 Button modifier;
216 float modifier_scale;
217 float modifier_angle;
218 float angle{};
219 float goal_angle{};
220 float amplitude{};
221 std::chrono::time_point<std::chrono::steady_clock> last_update;
222};
223
224std::unique_ptr<Input::AnalogDevice> AnalogFromButton::Create(const Common::ParamPackage& params) {
225 const std::string null_engine = Common::ParamPackage{{"engine", "null"}}.Serialize();
226 auto up = Input::CreateDevice<Input::ButtonDevice>(params.Get("up", null_engine));
227 auto down = Input::CreateDevice<Input::ButtonDevice>(params.Get("down", null_engine));
228 auto left = Input::CreateDevice<Input::ButtonDevice>(params.Get("left", null_engine));
229 auto right = Input::CreateDevice<Input::ButtonDevice>(params.Get("right", null_engine));
230 auto modifier = Input::CreateDevice<Input::ButtonDevice>(params.Get("modifier", null_engine));
231 auto modifier_scale = params.Get("modifier_scale", 0.5f);
232 auto modifier_angle = params.Get("modifier_angle", 5.5f);
233 return std::make_unique<Analog>(std::move(up), std::move(down), std::move(left),
234 std::move(right), std::move(modifier), modifier_scale,
235 modifier_angle);
236}
237
238} // namespace InputCommon
diff --git a/src/input_common/gcadapter/gc_adapter.cpp b/src/input_common/drivers/gc_adapter.cpp
index a2f1bb67c..7ab4540a8 100644
--- a/src/input_common/gcadapter/gc_adapter.cpp
+++ b/src/input_common/drivers/gc_adapter.cpp
@@ -2,47 +2,103 @@
2// Licensed under GPLv2+ 2// Licensed under GPLv2+
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <chrono> 5#include <fmt/format.h>
6#include <thread>
7
8#include <libusb.h> 6#include <libusb.h>
9 7
10#include "common/logging/log.h" 8#include "common/logging/log.h"
11#include "common/param_package.h" 9#include "common/param_package.h"
12#include "common/settings_input.h" 10#include "common/settings_input.h"
13#include "input_common/gcadapter/gc_adapter.h" 11#include "common/thread.h"
12#include "input_common/drivers/gc_adapter.h"
13
14namespace InputCommon {
15
16class LibUSBContext {
17public:
18 explicit LibUSBContext() {
19 init_result = libusb_init(&ctx);
20 }
21
22 ~LibUSBContext() {
23 libusb_exit(ctx);
24 }
25
26 LibUSBContext& operator=(const LibUSBContext&) = delete;
27 LibUSBContext(const LibUSBContext&) = delete;
28
29 LibUSBContext& operator=(LibUSBContext&&) noexcept = delete;
30 LibUSBContext(LibUSBContext&&) noexcept = delete;
31
32 [[nodiscard]] int InitResult() const noexcept {
33 return init_result;
34 }
35
36 [[nodiscard]] libusb_context* get() noexcept {
37 return ctx;
38 }
39
40private:
41 libusb_context* ctx;
42 int init_result{};
43};
44
45class LibUSBDeviceHandle {
46public:
47 explicit LibUSBDeviceHandle(libusb_context* ctx, uint16_t vid, uint16_t pid) noexcept {
48 handle = libusb_open_device_with_vid_pid(ctx, vid, pid);
49 }
50
51 ~LibUSBDeviceHandle() noexcept {
52 if (handle) {
53 libusb_release_interface(handle, 1);
54 libusb_close(handle);
55 }
56 }
14 57
15namespace GCAdapter { 58 LibUSBDeviceHandle& operator=(const LibUSBDeviceHandle&) = delete;
59 LibUSBDeviceHandle(const LibUSBDeviceHandle&) = delete;
16 60
17Adapter::Adapter() { 61 LibUSBDeviceHandle& operator=(LibUSBDeviceHandle&&) noexcept = delete;
18 if (usb_adapter_handle != nullptr) { 62 LibUSBDeviceHandle(LibUSBDeviceHandle&&) noexcept = delete;
63
64 [[nodiscard]] libusb_device_handle* get() noexcept {
65 return handle;
66 }
67
68private:
69 libusb_device_handle* handle{};
70};
71
72GCAdapter::GCAdapter(std::string input_engine_) : InputEngine(std::move(input_engine_)) {
73 if (usb_adapter_handle) {
19 return; 74 return;
20 } 75 }
21 LOG_INFO(Input, "GC Adapter Initialization started"); 76 LOG_DEBUG(Input, "Initialization started");
22 77
23 const int init_res = libusb_init(&libusb_ctx); 78 libusb_ctx = std::make_unique<LibUSBContext>();
79 const int init_res = libusb_ctx->InitResult();
24 if (init_res == LIBUSB_SUCCESS) { 80 if (init_res == LIBUSB_SUCCESS) {
25 adapter_scan_thread = std::thread(&Adapter::AdapterScanThread, this); 81 adapter_scan_thread =
82 std::jthread([this](std::stop_token stop_token) { AdapterScanThread(stop_token); });
26 } else { 83 } else {
27 LOG_ERROR(Input, "libusb could not be initialized. failed with error = {}", init_res); 84 LOG_ERROR(Input, "libusb could not be initialized. failed with error = {}", init_res);
28 } 85 }
29} 86}
30 87
31Adapter::~Adapter() { 88GCAdapter::~GCAdapter() {
32 Reset(); 89 Reset();
33} 90}
34 91
35void Adapter::AdapterInputThread() { 92void GCAdapter::AdapterInputThread(std::stop_token stop_token) {
36 LOG_DEBUG(Input, "GC Adapter input thread started"); 93 LOG_DEBUG(Input, "Input thread started");
94 Common::SetCurrentThreadName("yuzu:input:GCAdapter");
37 s32 payload_size{}; 95 s32 payload_size{};
38 AdapterPayload adapter_payload{}; 96 AdapterPayload adapter_payload{};
39 97
40 if (adapter_scan_thread.joinable()) { 98 adapter_scan_thread = {};
41 adapter_scan_thread.join();
42 }
43 99
44 while (adapter_input_thread_running) { 100 while (!stop_token.stop_requested()) {
45 libusb_interrupt_transfer(usb_adapter_handle, input_endpoint, adapter_payload.data(), 101 libusb_interrupt_transfer(usb_adapter_handle->get(), input_endpoint, adapter_payload.data(),
46 static_cast<s32>(adapter_payload.size()), &payload_size, 16); 102 static_cast<s32>(adapter_payload.size()), &payload_size, 16);
47 if (IsPayloadCorrect(adapter_payload, payload_size)) { 103 if (IsPayloadCorrect(adapter_payload, payload_size)) {
48 UpdateControllers(adapter_payload); 104 UpdateControllers(adapter_payload);
@@ -52,19 +108,20 @@ void Adapter::AdapterInputThread() {
52 } 108 }
53 109
54 if (restart_scan_thread) { 110 if (restart_scan_thread) {
55 adapter_scan_thread = std::thread(&Adapter::AdapterScanThread, this); 111 adapter_scan_thread =
112 std::jthread([this](std::stop_token token) { AdapterScanThread(token); });
56 restart_scan_thread = false; 113 restart_scan_thread = false;
57 } 114 }
58} 115}
59 116
60bool Adapter::IsPayloadCorrect(const AdapterPayload& adapter_payload, s32 payload_size) { 117bool GCAdapter::IsPayloadCorrect(const AdapterPayload& adapter_payload, s32 payload_size) {
61 if (payload_size != static_cast<s32>(adapter_payload.size()) || 118 if (payload_size != static_cast<s32>(adapter_payload.size()) ||
62 adapter_payload[0] != LIBUSB_DT_HID) { 119 adapter_payload[0] != LIBUSB_DT_HID) {
63 LOG_DEBUG(Input, "Error reading payload (size: {}, type: {:02x})", payload_size, 120 LOG_DEBUG(Input, "Error reading payload (size: {}, type: {:02x})", payload_size,
64 adapter_payload[0]); 121 adapter_payload[0]);
65 if (input_error_counter++ > 20) { 122 if (input_error_counter++ > 20) {
66 LOG_ERROR(Input, "GC adapter timeout, Is the adapter connected?"); 123 LOG_ERROR(Input, "Timeout, Is the adapter connected?");
67 adapter_input_thread_running = false; 124 adapter_input_thread.request_stop();
68 restart_scan_thread = true; 125 restart_scan_thread = true;
69 } 126 }
70 return false; 127 return false;
@@ -74,7 +131,7 @@ bool Adapter::IsPayloadCorrect(const AdapterPayload& adapter_payload, s32 payloa
74 return true; 131 return true;
75} 132}
76 133
77void Adapter::UpdateControllers(const AdapterPayload& adapter_payload) { 134void GCAdapter::UpdateControllers(const AdapterPayload& adapter_payload) {
78 for (std::size_t port = 0; port < pads.size(); ++port) { 135 for (std::size_t port = 0; port < pads.size(); ++port) {
79 const std::size_t offset = 1 + (9 * port); 136 const std::size_t offset = 1 + (9 * port);
80 const auto type = static_cast<ControllerTypes>(adapter_payload[offset] >> 4); 137 const auto type = static_cast<ControllerTypes>(adapter_payload[offset] >> 4);
@@ -84,23 +141,24 @@ void Adapter::UpdateControllers(const AdapterPayload& adapter_payload) {
84 const u8 b2 = adapter_payload[offset + 2]; 141 const u8 b2 = adapter_payload[offset + 2];
85 UpdateStateButtons(port, b1, b2); 142 UpdateStateButtons(port, b1, b2);
86 UpdateStateAxes(port, adapter_payload); 143 UpdateStateAxes(port, adapter_payload);
87 if (configuring) {
88 UpdateYuzuSettings(port);
89 }
90 } 144 }
91 } 145 }
92} 146}
93 147
94void Adapter::UpdatePadType(std::size_t port, ControllerTypes pad_type) { 148void GCAdapter::UpdatePadType(std::size_t port, ControllerTypes pad_type) {
95 if (pads[port].type == pad_type) { 149 if (pads[port].type == pad_type) {
96 return; 150 return;
97 } 151 }
98 // Device changed reset device and set new type 152 // Device changed reset device and set new type
99 ResetDevice(port); 153 pads[port].axis_origin = {};
154 pads[port].reset_origin_counter = {};
155 pads[port].enable_vibration = {};
156 pads[port].rumble_amplitude = {};
100 pads[port].type = pad_type; 157 pads[port].type = pad_type;
101} 158}
102 159
103void Adapter::UpdateStateButtons(std::size_t port, u8 b1, u8 b2) { 160void GCAdapter::UpdateStateButtons(std::size_t port, [[maybe_unused]] u8 b1,
161 [[maybe_unused]] u8 b2) {
104 if (port >= pads.size()) { 162 if (port >= pads.size()) {
105 return; 163 return;
106 } 164 }
@@ -116,25 +174,21 @@ void Adapter::UpdateStateButtons(std::size_t port, u8 b1, u8 b2) {
116 PadButton::TriggerR, 174 PadButton::TriggerR,
117 PadButton::TriggerL, 175 PadButton::TriggerL,
118 }; 176 };
119 pads[port].buttons = 0; 177
120 for (std::size_t i = 0; i < b1_buttons.size(); ++i) { 178 for (std::size_t i = 0; i < b1_buttons.size(); ++i) {
121 if ((b1 & (1U << i)) != 0) { 179 const bool button_status = (b1 & (1U << i)) != 0;
122 pads[port].buttons = 180 const int button = static_cast<int>(b1_buttons[i]);
123 static_cast<u16>(pads[port].buttons | static_cast<u16>(b1_buttons[i])); 181 SetButton(pads[port].identifier, button, button_status);
124 pads[port].last_button = b1_buttons[i];
125 }
126 } 182 }
127 183
128 for (std::size_t j = 0; j < b2_buttons.size(); ++j) { 184 for (std::size_t j = 0; j < b2_buttons.size(); ++j) {
129 if ((b2 & (1U << j)) != 0) { 185 const bool button_status = (b2 & (1U << j)) != 0;
130 pads[port].buttons = 186 const int button = static_cast<int>(b2_buttons[j]);
131 static_cast<u16>(pads[port].buttons | static_cast<u16>(b2_buttons[j])); 187 SetButton(pads[port].identifier, button, button_status);
132 pads[port].last_button = b2_buttons[j];
133 }
134 } 188 }
135} 189}
136 190
137void Adapter::UpdateStateAxes(std::size_t port, const AdapterPayload& adapter_payload) { 191void GCAdapter::UpdateStateAxes(std::size_t port, const AdapterPayload& adapter_payload) {
138 if (port >= pads.size()) { 192 if (port >= pads.size()) {
139 return; 193 return;
140 } 194 }
@@ -155,134 +209,63 @@ void Adapter::UpdateStateAxes(std::size_t port, const AdapterPayload& adapter_pa
155 pads[port].axis_origin[index] = axis_value; 209 pads[port].axis_origin[index] = axis_value;
156 pads[port].reset_origin_counter++; 210 pads[port].reset_origin_counter++;
157 } 211 }
158 pads[port].axis_values[index] = 212 const f32 axis_status = (axis_value - pads[port].axis_origin[index]) / 100.0f;
159 static_cast<s16>(axis_value - pads[port].axis_origin[index]); 213 SetAxis(pads[port].identifier, static_cast<int>(index), axis_status);
160 }
161}
162
163void Adapter::UpdateYuzuSettings(std::size_t port) {
164 if (port >= pads.size()) {
165 return;
166 }
167
168 constexpr u8 axis_threshold = 50;
169 GCPadStatus pad_status = {.port = port};
170
171 if (pads[port].buttons != 0) {
172 pad_status.button = pads[port].last_button;
173 pad_queue.Push(pad_status);
174 }
175
176 // Accounting for a threshold here to ensure an intentional press
177 for (std::size_t i = 0; i < pads[port].axis_values.size(); ++i) {
178 const s16 value = pads[port].axis_values[i];
179
180 if (value > axis_threshold || value < -axis_threshold) {
181 pad_status.axis = static_cast<PadAxes>(i);
182 pad_status.axis_value = value;
183 pad_status.axis_threshold = axis_threshold;
184 pad_queue.Push(pad_status);
185 }
186 }
187}
188
189void Adapter::UpdateVibrations() {
190 // Use 8 states to keep the switching between on/off fast enough for
191 // a human to not notice the difference between switching from on/off
192 // More states = more rumble strengths = slower update time
193 constexpr u8 vibration_states = 8;
194
195 vibration_counter = (vibration_counter + 1) % vibration_states;
196
197 for (GCController& pad : pads) {
198 const bool vibrate = pad.rumble_amplitude > vibration_counter;
199 vibration_changed |= vibrate != pad.enable_vibration;
200 pad.enable_vibration = vibrate;
201 }
202 SendVibrations();
203}
204
205void Adapter::SendVibrations() {
206 if (!rumble_enabled || !vibration_changed) {
207 return;
208 }
209 s32 size{};
210 constexpr u8 rumble_command = 0x11;
211 const u8 p1 = pads[0].enable_vibration;
212 const u8 p2 = pads[1].enable_vibration;
213 const u8 p3 = pads[2].enable_vibration;
214 const u8 p4 = pads[3].enable_vibration;
215 std::array<u8, 5> payload = {rumble_command, p1, p2, p3, p4};
216 const int err = libusb_interrupt_transfer(usb_adapter_handle, output_endpoint, payload.data(),
217 static_cast<s32>(payload.size()), &size, 16);
218 if (err) {
219 LOG_DEBUG(Input, "Adapter libusb write failed: {}", libusb_error_name(err));
220 if (output_error_counter++ > 5) {
221 LOG_ERROR(Input, "GC adapter output timeout, Rumble disabled");
222 rumble_enabled = false;
223 }
224 return;
225 } 214 }
226 output_error_counter = 0;
227 vibration_changed = false;
228} 215}
229 216
230bool Adapter::RumblePlay(std::size_t port, u8 amplitude) { 217void GCAdapter::AdapterScanThread(std::stop_token stop_token) {
231 pads[port].rumble_amplitude = amplitude; 218 Common::SetCurrentThreadName("yuzu:input:ScanGCAdapter");
232 219 usb_adapter_handle = nullptr;
233 return rumble_enabled; 220 pads = {};
234} 221 while (!stop_token.stop_requested() && !Setup()) {
235 222 std::this_thread::sleep_for(std::chrono::seconds(2));
236void Adapter::AdapterScanThread() {
237 adapter_scan_thread_running = true;
238 adapter_input_thread_running = false;
239 if (adapter_input_thread.joinable()) {
240 adapter_input_thread.join();
241 }
242 ClearLibusbHandle();
243 ResetDevices();
244 while (adapter_scan_thread_running && !adapter_input_thread_running) {
245 Setup();
246 std::this_thread::sleep_for(std::chrono::seconds(1));
247 } 223 }
248} 224}
249 225
250void Adapter::Setup() { 226bool GCAdapter::Setup() {
251 usb_adapter_handle = libusb_open_device_with_vid_pid(libusb_ctx, 0x057e, 0x0337); 227 constexpr u16 nintendo_vid = 0x057e;
252 228 constexpr u16 gc_adapter_pid = 0x0337;
253 if (usb_adapter_handle == NULL) { 229 usb_adapter_handle =
254 return; 230 std::make_unique<LibUSBDeviceHandle>(libusb_ctx->get(), nintendo_vid, gc_adapter_pid);
231 if (!usb_adapter_handle->get()) {
232 return false;
255 } 233 }
256 if (!CheckDeviceAccess()) { 234 if (!CheckDeviceAccess()) {
257 ClearLibusbHandle(); 235 usb_adapter_handle = nullptr;
258 return; 236 return false;
259 } 237 }
260 238
261 libusb_device* device = libusb_get_device(usb_adapter_handle); 239 libusb_device* const device = libusb_get_device(usb_adapter_handle->get());
262 240
263 LOG_INFO(Input, "GC adapter is now connected"); 241 LOG_INFO(Input, "GC adapter is now connected");
264 // GC Adapter found and accessible, registering it 242 // GC Adapter found and accessible, registering it
265 if (GetGCEndpoint(device)) { 243 if (GetGCEndpoint(device)) {
266 adapter_scan_thread_running = false;
267 adapter_input_thread_running = true;
268 rumble_enabled = true; 244 rumble_enabled = true;
269 input_error_counter = 0; 245 input_error_counter = 0;
270 output_error_counter = 0; 246 output_error_counter = 0;
271 adapter_input_thread = std::thread(&Adapter::AdapterInputThread, this);
272 }
273}
274 247
275bool Adapter::CheckDeviceAccess() { 248 std::size_t port = 0;
276 // This fixes payload problems from offbrand GCAdapters 249 for (GCController& pad : pads) {
277 const s32 control_transfer_error = 250 pad.identifier = {
278 libusb_control_transfer(usb_adapter_handle, 0x21, 11, 0x0001, 0, nullptr, 0, 1000); 251 .guid = Common::UUID{Common::INVALID_UUID},
279 if (control_transfer_error < 0) { 252 .port = port++,
280 LOG_ERROR(Input, "libusb_control_transfer failed with error= {}", control_transfer_error); 253 .pad = 0,
254 };
255 PreSetController(pad.identifier);
256 }
257
258 adapter_input_thread =
259 std::jthread([this](std::stop_token stop_token) { AdapterInputThread(stop_token); });
260 return true;
281 } 261 }
262 return false;
263}
282 264
283 s32 kernel_driver_error = libusb_kernel_driver_active(usb_adapter_handle, 0); 265bool GCAdapter::CheckDeviceAccess() {
266 s32 kernel_driver_error = libusb_kernel_driver_active(usb_adapter_handle->get(), 0);
284 if (kernel_driver_error == 1) { 267 if (kernel_driver_error == 1) {
285 kernel_driver_error = libusb_detach_kernel_driver(usb_adapter_handle, 0); 268 kernel_driver_error = libusb_detach_kernel_driver(usb_adapter_handle->get(), 0);
286 if (kernel_driver_error != 0 && kernel_driver_error != LIBUSB_ERROR_NOT_SUPPORTED) { 269 if (kernel_driver_error != 0 && kernel_driver_error != LIBUSB_ERROR_NOT_SUPPORTED) {
287 LOG_ERROR(Input, "libusb_detach_kernel_driver failed with error = {}", 270 LOG_ERROR(Input, "libusb_detach_kernel_driver failed with error = {}",
288 kernel_driver_error); 271 kernel_driver_error);
@@ -290,23 +273,28 @@ bool Adapter::CheckDeviceAccess() {
290 } 273 }
291 274
292 if (kernel_driver_error && kernel_driver_error != LIBUSB_ERROR_NOT_SUPPORTED) { 275 if (kernel_driver_error && kernel_driver_error != LIBUSB_ERROR_NOT_SUPPORTED) {
293 libusb_close(usb_adapter_handle);
294 usb_adapter_handle = nullptr; 276 usb_adapter_handle = nullptr;
295 return false; 277 return false;
296 } 278 }
297 279
298 const int interface_claim_error = libusb_claim_interface(usb_adapter_handle, 0); 280 const int interface_claim_error = libusb_claim_interface(usb_adapter_handle->get(), 0);
299 if (interface_claim_error) { 281 if (interface_claim_error) {
300 LOG_ERROR(Input, "libusb_claim_interface failed with error = {}", interface_claim_error); 282 LOG_ERROR(Input, "libusb_claim_interface failed with error = {}", interface_claim_error);
301 libusb_close(usb_adapter_handle);
302 usb_adapter_handle = nullptr; 283 usb_adapter_handle = nullptr;
303 return false; 284 return false;
304 } 285 }
305 286
287 // This fixes payload problems from offbrand GCAdapters
288 const s32 control_transfer_error =
289 libusb_control_transfer(usb_adapter_handle->get(), 0x21, 11, 0x0001, 0, nullptr, 0, 1000);
290 if (control_transfer_error < 0) {
291 LOG_ERROR(Input, "libusb_control_transfer failed with error= {}", control_transfer_error);
292 }
293
306 return true; 294 return true;
307} 295}
308 296
309bool Adapter::GetGCEndpoint(libusb_device* device) { 297bool GCAdapter::GetGCEndpoint(libusb_device* device) {
310 libusb_config_descriptor* config = nullptr; 298 libusb_config_descriptor* config = nullptr;
311 const int config_descriptor_return = libusb_get_config_descriptor(device, 0, &config); 299 const int config_descriptor_return = libusb_get_config_descriptor(device, 0, &config);
312 if (config_descriptor_return != LIBUSB_SUCCESS) { 300 if (config_descriptor_return != LIBUSB_SUCCESS) {
@@ -332,77 +320,95 @@ bool Adapter::GetGCEndpoint(libusb_device* device) {
332 // This transfer seems to be responsible for clearing the state of the adapter 320 // This transfer seems to be responsible for clearing the state of the adapter
333 // Used to clear the "busy" state of when the device is unexpectedly unplugged 321 // Used to clear the "busy" state of when the device is unexpectedly unplugged
334 unsigned char clear_payload = 0x13; 322 unsigned char clear_payload = 0x13;
335 libusb_interrupt_transfer(usb_adapter_handle, output_endpoint, &clear_payload, 323 libusb_interrupt_transfer(usb_adapter_handle->get(), output_endpoint, &clear_payload,
336 sizeof(clear_payload), nullptr, 16); 324 sizeof(clear_payload), nullptr, 16);
337 return true; 325 return true;
338} 326}
339 327
340void Adapter::JoinThreads() { 328Common::Input::VibrationError GCAdapter::SetRumble(
341 restart_scan_thread = false; 329 const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) {
342 adapter_input_thread_running = false; 330 const auto mean_amplitude = (vibration.low_amplitude + vibration.high_amplitude) * 0.5f;
343 adapter_scan_thread_running = false; 331 const auto processed_amplitude =
332 static_cast<u8>((mean_amplitude + std::pow(mean_amplitude, 0.3f)) * 0.5f * 0x8);
344 333
345 if (adapter_scan_thread.joinable()) { 334 pads[identifier.port].rumble_amplitude = processed_amplitude;
346 adapter_scan_thread.join();
347 }
348 335
349 if (adapter_input_thread.joinable()) { 336 if (!rumble_enabled) {
350 adapter_input_thread.join(); 337 return Common::Input::VibrationError::Disabled;
351 } 338 }
339 return Common::Input::VibrationError::None;
352} 340}
353 341
354void Adapter::ClearLibusbHandle() { 342void GCAdapter::UpdateVibrations() {
355 if (usb_adapter_handle) { 343 // Use 8 states to keep the switching between on/off fast enough for
356 libusb_release_interface(usb_adapter_handle, 1); 344 // a human to feel different vibration strenght
357 libusb_close(usb_adapter_handle); 345 // More states == more rumble strengths == slower update time
358 usb_adapter_handle = nullptr; 346 constexpr u8 vibration_states = 8;
347
348 vibration_counter = (vibration_counter + 1) % vibration_states;
349
350 for (GCController& pad : pads) {
351 const bool vibrate = pad.rumble_amplitude > vibration_counter;
352 vibration_changed |= vibrate != pad.enable_vibration;
353 pad.enable_vibration = vibrate;
359 } 354 }
355 SendVibrations();
360} 356}
361 357
362void Adapter::ResetDevices() { 358void GCAdapter::SendVibrations() {
363 for (std::size_t i = 0; i < pads.size(); ++i) { 359 if (!rumble_enabled || !vibration_changed) {
364 ResetDevice(i); 360 return;
361 }
362 s32 size{};
363 constexpr u8 rumble_command = 0x11;
364 const u8 p1 = pads[0].enable_vibration;
365 const u8 p2 = pads[1].enable_vibration;
366 const u8 p3 = pads[2].enable_vibration;
367 const u8 p4 = pads[3].enable_vibration;
368 std::array<u8, 5> payload = {rumble_command, p1, p2, p3, p4};
369 const int err =
370 libusb_interrupt_transfer(usb_adapter_handle->get(), output_endpoint, payload.data(),
371 static_cast<s32>(payload.size()), &size, 16);
372 if (err) {
373 LOG_DEBUG(Input, "Libusb write failed: {}", libusb_error_name(err));
374 if (output_error_counter++ > 5) {
375 LOG_ERROR(Input, "Output timeout, Rumble disabled");
376 rumble_enabled = false;
377 }
378 return;
365 } 379 }
380 output_error_counter = 0;
381 vibration_changed = false;
366} 382}
367 383
368void Adapter::ResetDevice(std::size_t port) { 384bool GCAdapter::DeviceConnected(std::size_t port) const {
369 pads[port].type = ControllerTypes::None; 385 return pads[port].type != ControllerTypes::None;
370 pads[port].enable_vibration = false;
371 pads[port].rumble_amplitude = 0;
372 pads[port].buttons = 0;
373 pads[port].last_button = PadButton::Undefined;
374 pads[port].axis_values.fill(0);
375 pads[port].reset_origin_counter = 0;
376} 386}
377 387
378void Adapter::Reset() { 388void GCAdapter::Reset() {
379 JoinThreads(); 389 adapter_scan_thread = {};
380 ClearLibusbHandle(); 390 adapter_input_thread = {};
381 ResetDevices(); 391 usb_adapter_handle = nullptr;
382 392 pads = {};
383 if (libusb_ctx) { 393 libusb_ctx = nullptr;
384 libusb_exit(libusb_ctx);
385 }
386} 394}
387 395
388std::vector<Common::ParamPackage> Adapter::GetInputDevices() const { 396std::vector<Common::ParamPackage> GCAdapter::GetInputDevices() const {
389 std::vector<Common::ParamPackage> devices; 397 std::vector<Common::ParamPackage> devices;
390 for (std::size_t port = 0; port < pads.size(); ++port) { 398 for (std::size_t port = 0; port < pads.size(); ++port) {
391 if (!DeviceConnected(port)) { 399 if (!DeviceConnected(port)) {
392 continue; 400 continue;
393 } 401 }
394 std::string name = fmt::format("Gamecube Controller {}", port + 1); 402 Common::ParamPackage identifier{};
395 devices.emplace_back(Common::ParamPackage{ 403 identifier.Set("engine", GetEngineName());
396 {"class", "gcpad"}, 404 identifier.Set("display", fmt::format("Gamecube Controller {}", port + 1));
397 {"display", std::move(name)}, 405 identifier.Set("port", static_cast<int>(port));
398 {"port", std::to_string(port)}, 406 devices.emplace_back(identifier);
399 });
400 } 407 }
401 return devices; 408 return devices;
402} 409}
403 410
404InputCommon::ButtonMapping Adapter::GetButtonMappingForDevice( 411ButtonMapping GCAdapter::GetButtonMappingForDevice(const Common::ParamPackage& params) {
405 const Common::ParamPackage& params) const {
406 // This list is missing ZL/ZR since those are not considered buttons. 412 // This list is missing ZL/ZR since those are not considered buttons.
407 // We will add those afterwards 413 // We will add those afterwards
408 // This list also excludes any button that can't be really mapped 414 // This list also excludes any button that can't be really mapped
@@ -425,47 +431,49 @@ InputCommon::ButtonMapping Adapter::GetButtonMappingForDevice(
425 return {}; 431 return {};
426 } 432 }
427 433
428 InputCommon::ButtonMapping mapping{}; 434 ButtonMapping mapping{};
429 for (const auto& [switch_button, gcadapter_button] : switch_to_gcadapter_button) { 435 for (const auto& [switch_button, gcadapter_button] : switch_to_gcadapter_button) {
430 Common::ParamPackage button_params({{"engine", "gcpad"}}); 436 Common::ParamPackage button_params{};
437 button_params.Set("engine", GetEngineName());
431 button_params.Set("port", params.Get("port", 0)); 438 button_params.Set("port", params.Get("port", 0));
432 button_params.Set("button", static_cast<int>(gcadapter_button)); 439 button_params.Set("button", static_cast<int>(gcadapter_button));
433 mapping.insert_or_assign(switch_button, std::move(button_params)); 440 mapping.insert_or_assign(switch_button, std::move(button_params));
434 } 441 }
435 442
436 // Add the missing bindings for ZL/ZR 443 // Add the missing bindings for ZL/ZR
437 static constexpr std::array<std::pair<Settings::NativeButton::Values, PadAxes>, 2> 444 static constexpr std::array<std::tuple<Settings::NativeButton::Values, PadButton, PadAxes>, 2>
438 switch_to_gcadapter_axis = { 445 switch_to_gcadapter_axis = {
439 std::pair{Settings::NativeButton::ZL, PadAxes::TriggerLeft}, 446 std::tuple{Settings::NativeButton::ZL, PadButton::TriggerL, PadAxes::TriggerLeft},
440 {Settings::NativeButton::ZR, PadAxes::TriggerRight}, 447 {Settings::NativeButton::ZR, PadButton::TriggerR, PadAxes::TriggerRight},
441 }; 448 };
442 for (const auto& [switch_button, gcadapter_axis] : switch_to_gcadapter_axis) { 449 for (const auto& [switch_button, gcadapter_buton, gcadapter_axis] : switch_to_gcadapter_axis) {
443 Common::ParamPackage button_params({{"engine", "gcpad"}}); 450 Common::ParamPackage button_params{};
451 button_params.Set("engine", GetEngineName());
444 button_params.Set("port", params.Get("port", 0)); 452 button_params.Set("port", params.Get("port", 0));
445 button_params.Set("button", static_cast<s32>(PadButton::Stick)); 453 button_params.Set("button", static_cast<s32>(gcadapter_buton));
446 button_params.Set("axis", static_cast<s32>(gcadapter_axis)); 454 button_params.Set("axis", static_cast<s32>(gcadapter_axis));
447 button_params.Set("threshold", 0.5f); 455 button_params.Set("threshold", 0.5f);
456 button_params.Set("range", 1.9f);
448 button_params.Set("direction", "+"); 457 button_params.Set("direction", "+");
449 mapping.insert_or_assign(switch_button, std::move(button_params)); 458 mapping.insert_or_assign(switch_button, std::move(button_params));
450 } 459 }
451 return mapping; 460 return mapping;
452} 461}
453 462
454InputCommon::AnalogMapping Adapter::GetAnalogMappingForDevice( 463AnalogMapping GCAdapter::GetAnalogMappingForDevice(const Common::ParamPackage& params) {
455 const Common::ParamPackage& params) const {
456 if (!params.Has("port")) { 464 if (!params.Has("port")) {
457 return {}; 465 return {};
458 } 466 }
459 467
460 InputCommon::AnalogMapping mapping = {}; 468 AnalogMapping mapping = {};
461 Common::ParamPackage left_analog_params; 469 Common::ParamPackage left_analog_params;
462 left_analog_params.Set("engine", "gcpad"); 470 left_analog_params.Set("engine", GetEngineName());
463 left_analog_params.Set("port", params.Get("port", 0)); 471 left_analog_params.Set("port", params.Get("port", 0));
464 left_analog_params.Set("axis_x", static_cast<int>(PadAxes::StickX)); 472 left_analog_params.Set("axis_x", static_cast<int>(PadAxes::StickX));
465 left_analog_params.Set("axis_y", static_cast<int>(PadAxes::StickY)); 473 left_analog_params.Set("axis_y", static_cast<int>(PadAxes::StickY));
466 mapping.insert_or_assign(Settings::NativeAnalog::LStick, std::move(left_analog_params)); 474 mapping.insert_or_assign(Settings::NativeAnalog::LStick, std::move(left_analog_params));
467 Common::ParamPackage right_analog_params; 475 Common::ParamPackage right_analog_params;
468 right_analog_params.Set("engine", "gcpad"); 476 right_analog_params.Set("engine", GetEngineName());
469 right_analog_params.Set("port", params.Get("port", 0)); 477 right_analog_params.Set("port", params.Get("port", 0));
470 right_analog_params.Set("axis_x", static_cast<int>(PadAxes::SubstickX)); 478 right_analog_params.Set("axis_x", static_cast<int>(PadAxes::SubstickX));
471 right_analog_params.Set("axis_y", static_cast<int>(PadAxes::SubstickY)); 479 right_analog_params.Set("axis_y", static_cast<int>(PadAxes::SubstickY));
@@ -473,34 +481,47 @@ InputCommon::AnalogMapping Adapter::GetAnalogMappingForDevice(
473 return mapping; 481 return mapping;
474} 482}
475 483
476bool Adapter::DeviceConnected(std::size_t port) const { 484Common::Input::ButtonNames GCAdapter::GetUIButtonName(const Common::ParamPackage& params) const {
477 return pads[port].type != ControllerTypes::None; 485 PadButton button = static_cast<PadButton>(params.Get("button", 0));
478} 486 switch (button) {
479 487 case PadButton::ButtonLeft:
480void Adapter::BeginConfiguration() { 488 return Common::Input::ButtonNames::ButtonLeft;
481 pad_queue.Clear(); 489 case PadButton::ButtonRight:
482 configuring = true; 490 return Common::Input::ButtonNames::ButtonRight;
483} 491 case PadButton::ButtonDown:
484 492 return Common::Input::ButtonNames::ButtonDown;
485void Adapter::EndConfiguration() { 493 case PadButton::ButtonUp:
486 pad_queue.Clear(); 494 return Common::Input::ButtonNames::ButtonUp;
487 configuring = false; 495 case PadButton::TriggerZ:
488} 496 return Common::Input::ButtonNames::TriggerZ;
489 497 case PadButton::TriggerR:
490Common::SPSCQueue<GCPadStatus>& Adapter::GetPadQueue() { 498 return Common::Input::ButtonNames::TriggerR;
491 return pad_queue; 499 case PadButton::TriggerL:
492} 500 return Common::Input::ButtonNames::TriggerL;
493 501 case PadButton::ButtonA:
494const Common::SPSCQueue<GCPadStatus>& Adapter::GetPadQueue() const { 502 return Common::Input::ButtonNames::ButtonA;
495 return pad_queue; 503 case PadButton::ButtonB:
504 return Common::Input::ButtonNames::ButtonB;
505 case PadButton::ButtonX:
506 return Common::Input::ButtonNames::ButtonX;
507 case PadButton::ButtonY:
508 return Common::Input::ButtonNames::ButtonY;
509 case PadButton::ButtonStart:
510 return Common::Input::ButtonNames::ButtonStart;
511 default:
512 return Common::Input::ButtonNames::Undefined;
513 }
496} 514}
497 515
498GCController& Adapter::GetPadState(std::size_t port) { 516Common::Input::ButtonNames GCAdapter::GetUIName(const Common::ParamPackage& params) const {
499 return pads.at(port); 517 if (params.Has("button")) {
500} 518 return GetUIButtonName(params);
519 }
520 if (params.Has("axis")) {
521 return Common::Input::ButtonNames::Value;
522 }
501 523
502const GCController& Adapter::GetPadState(std::size_t port) const { 524 return Common::Input::ButtonNames::Invalid;
503 return pads.at(port);
504} 525}
505 526
506} // namespace GCAdapter 527} // namespace InputCommon
diff --git a/src/input_common/drivers/gc_adapter.h b/src/input_common/drivers/gc_adapter.h
new file mode 100644
index 000000000..7ce1912a3
--- /dev/null
+++ b/src/input_common/drivers/gc_adapter.h
@@ -0,0 +1,135 @@
1// Copyright 2014 Dolphin Emulator Project
2// Licensed under GPLv2+
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <array>
8#include <memory>
9#include <mutex>
10#include <stop_token>
11#include <string>
12#include <thread>
13
14#include "input_common/input_engine.h"
15
16struct libusb_context;
17struct libusb_device;
18struct libusb_device_handle;
19
20namespace InputCommon {
21
22class LibUSBContext;
23class LibUSBDeviceHandle;
24
25class GCAdapter : public InputEngine {
26public:
27 explicit GCAdapter(std::string input_engine_);
28 ~GCAdapter() override;
29
30 Common::Input::VibrationError SetRumble(
31 const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) override;
32
33 /// Used for automapping features
34 std::vector<Common::ParamPackage> GetInputDevices() const override;
35 ButtonMapping GetButtonMappingForDevice(const Common::ParamPackage& params) override;
36 AnalogMapping GetAnalogMappingForDevice(const Common::ParamPackage& params) override;
37 Common::Input::ButtonNames GetUIName(const Common::ParamPackage& params) const override;
38
39private:
40 enum class PadButton {
41 Undefined = 0x0000,
42 ButtonLeft = 0x0001,
43 ButtonRight = 0x0002,
44 ButtonDown = 0x0004,
45 ButtonUp = 0x0008,
46 TriggerZ = 0x0010,
47 TriggerR = 0x0020,
48 TriggerL = 0x0040,
49 ButtonA = 0x0100,
50 ButtonB = 0x0200,
51 ButtonX = 0x0400,
52 ButtonY = 0x0800,
53 ButtonStart = 0x1000,
54 };
55
56 enum class PadAxes : u8 {
57 StickX,
58 StickY,
59 SubstickX,
60 SubstickY,
61 TriggerLeft,
62 TriggerRight,
63 Undefined,
64 };
65
66 enum class ControllerTypes {
67 None,
68 Wired,
69 Wireless,
70 };
71
72 struct GCController {
73 ControllerTypes type = ControllerTypes::None;
74 PadIdentifier identifier{};
75 bool enable_vibration = false;
76 u8 rumble_amplitude{};
77 std::array<u8, 6> axis_origin{};
78 u8 reset_origin_counter{};
79 };
80
81 using AdapterPayload = std::array<u8, 37>;
82
83 void UpdatePadType(std::size_t port, ControllerTypes pad_type);
84 void UpdateControllers(const AdapterPayload& adapter_payload);
85 void UpdateStateButtons(std::size_t port, u8 b1, u8 b2);
86 void UpdateStateAxes(std::size_t port, const AdapterPayload& adapter_payload);
87
88 void AdapterInputThread(std::stop_token stop_token);
89
90 void AdapterScanThread(std::stop_token stop_token);
91
92 bool IsPayloadCorrect(const AdapterPayload& adapter_payload, s32 payload_size);
93
94 /// For use in initialization, querying devices to find the adapter
95 bool Setup();
96
97 /// Returns true if we successfully gain access to GC Adapter
98 bool CheckDeviceAccess();
99
100 /// Captures GC Adapter endpoint address
101 /// Returns true if the endpoint was set correctly
102 bool GetGCEndpoint(libusb_device* device);
103
104 /// Returns true if there is a device connected to port
105 bool DeviceConnected(std::size_t port) const;
106
107 /// For shutting down, clear all data, join all threads, release usb
108 void Reset();
109
110 void UpdateVibrations();
111
112 /// Updates vibration state of all controllers
113 void SendVibrations();
114
115 Common::Input::ButtonNames GetUIButtonName(const Common::ParamPackage& params) const;
116
117 std::unique_ptr<LibUSBDeviceHandle> usb_adapter_handle;
118 std::array<GCController, 4> pads;
119
120 std::jthread adapter_input_thread;
121 std::jthread adapter_scan_thread;
122 bool restart_scan_thread{};
123
124 std::unique_ptr<LibUSBContext> libusb_ctx;
125
126 u8 input_endpoint{0};
127 u8 output_endpoint{0};
128 u8 input_error_counter{0};
129 u8 output_error_counter{0};
130 int vibration_counter{0};
131
132 bool rumble_enabled{true};
133 bool vibration_changed{true};
134};
135} // namespace InputCommon
diff --git a/src/input_common/drivers/keyboard.cpp b/src/input_common/drivers/keyboard.cpp
new file mode 100644
index 000000000..4c1e5bbec
--- /dev/null
+++ b/src/input_common/drivers/keyboard.cpp
@@ -0,0 +1,112 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included
4
5#include "common/param_package.h"
6#include "common/settings_input.h"
7#include "input_common/drivers/keyboard.h"
8
9namespace InputCommon {
10
11constexpr PadIdentifier key_identifier = {
12 .guid = Common::UUID{Common::INVALID_UUID},
13 .port = 0,
14 .pad = 0,
15};
16constexpr PadIdentifier keyboard_key_identifier = {
17 .guid = Common::UUID{Common::INVALID_UUID},
18 .port = 1,
19 .pad = 0,
20};
21constexpr PadIdentifier keyboard_modifier_identifier = {
22 .guid = Common::UUID{Common::INVALID_UUID},
23 .port = 1,
24 .pad = 1,
25};
26
27Keyboard::Keyboard(std::string input_engine_) : InputEngine(std::move(input_engine_)) {
28 // Keyboard is broken into 3 diferent sets:
29 // key: Unfiltered intended for controllers.
30 // keyboard_key: Allows only Settings::NativeKeyboard::Keys intended for keyboard emulation.
31 // keyboard_modifier: Allows only Settings::NativeKeyboard::Modifiers intended for keyboard
32 // emulation.
33 PreSetController(key_identifier);
34 PreSetController(keyboard_key_identifier);
35 PreSetController(keyboard_modifier_identifier);
36}
37
38void Keyboard::PressKey(int key_code) {
39 SetButton(key_identifier, key_code, true);
40}
41
42void Keyboard::ReleaseKey(int key_code) {
43 SetButton(key_identifier, key_code, false);
44}
45
46void Keyboard::PressKeyboardKey(int key_index) {
47 if (key_index == Settings::NativeKeyboard::None) {
48 return;
49 }
50 SetButton(keyboard_key_identifier, key_index, true);
51}
52
53void Keyboard::ReleaseKeyboardKey(int key_index) {
54 if (key_index == Settings::NativeKeyboard::None) {
55 return;
56 }
57 SetButton(keyboard_key_identifier, key_index, false);
58}
59
60void Keyboard::SetKeyboardModifiers(int key_modifiers) {
61 for (int i = 0; i < 32; ++i) {
62 bool key_value = ((key_modifiers >> i) & 0x1) != 0;
63 SetButton(keyboard_modifier_identifier, i, key_value);
64 // Use the modifier to press the key button equivalent
65 switch (i) {
66 case Settings::NativeKeyboard::LeftControl:
67 SetButton(keyboard_key_identifier, Settings::NativeKeyboard::LeftControlKey, key_value);
68 break;
69 case Settings::NativeKeyboard::LeftShift:
70 SetButton(keyboard_key_identifier, Settings::NativeKeyboard::LeftShiftKey, key_value);
71 break;
72 case Settings::NativeKeyboard::LeftAlt:
73 SetButton(keyboard_key_identifier, Settings::NativeKeyboard::LeftAltKey, key_value);
74 break;
75 case Settings::NativeKeyboard::LeftMeta:
76 SetButton(keyboard_key_identifier, Settings::NativeKeyboard::LeftMetaKey, key_value);
77 break;
78 case Settings::NativeKeyboard::RightControl:
79 SetButton(keyboard_key_identifier, Settings::NativeKeyboard::RightControlKey,
80 key_value);
81 break;
82 case Settings::NativeKeyboard::RightShift:
83 SetButton(keyboard_key_identifier, Settings::NativeKeyboard::RightShiftKey, key_value);
84 break;
85 case Settings::NativeKeyboard::RightAlt:
86 SetButton(keyboard_key_identifier, Settings::NativeKeyboard::RightAltKey, key_value);
87 break;
88 case Settings::NativeKeyboard::RightMeta:
89 SetButton(keyboard_key_identifier, Settings::NativeKeyboard::RightMetaKey, key_value);
90 break;
91 default:
92 // Other modifier keys should be pressed with PressKey since they stay enabled until
93 // next press
94 break;
95 }
96 }
97}
98
99void Keyboard::ReleaseAllKeys() {
100 ResetButtonState();
101}
102
103std::vector<Common::ParamPackage> Keyboard::GetInputDevices() const {
104 std::vector<Common::ParamPackage> devices;
105 devices.emplace_back(Common::ParamPackage{
106 {"engine", GetEngineName()},
107 {"display", "Keyboard Only"},
108 });
109 return devices;
110}
111
112} // namespace InputCommon
diff --git a/src/input_common/drivers/keyboard.h b/src/input_common/drivers/keyboard.h
new file mode 100644
index 000000000..3856c882c
--- /dev/null
+++ b/src/input_common/drivers/keyboard.h
@@ -0,0 +1,56 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included
4
5#pragma once
6
7#include "input_common/input_engine.h"
8
9namespace InputCommon {
10
11/**
12 * A button device factory representing a keyboard. It receives keyboard events and forward them
13 * to all button devices it created.
14 */
15class Keyboard final : public InputEngine {
16public:
17 explicit Keyboard(std::string input_engine_);
18
19 /**
20 * Sets the status of all buttons bound with the key to pressed
21 * @param key_code the code of the key to press
22 */
23 void PressKey(int key_code);
24
25 /**
26 * Sets the status of all buttons bound with the key to released
27 * @param key_code the code of the key to release
28 */
29 void ReleaseKey(int key_code);
30
31 /**
32 * Sets the status of the keyboard key to pressed
33 * @param key_index index of the key to press
34 */
35 void PressKeyboardKey(int key_index);
36
37 /**
38 * Sets the status of the keyboard key to released
39 * @param key_index index of the key to release
40 */
41 void ReleaseKeyboardKey(int key_index);
42
43 /**
44 * Sets the status of all keyboard modifier keys
45 * @param key_modifiers the code of the key to release
46 */
47 void SetKeyboardModifiers(int key_modifiers);
48
49 /// Sets all keys to the non pressed state
50 void ReleaseAllKeys();
51
52 /// Used for automapping features
53 std::vector<Common::ParamPackage> GetInputDevices() const override;
54};
55
56} // namespace InputCommon
diff --git a/src/input_common/drivers/mouse.cpp b/src/input_common/drivers/mouse.cpp
new file mode 100644
index 000000000..aa69216c8
--- /dev/null
+++ b/src/input_common/drivers/mouse.cpp
@@ -0,0 +1,185 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included
4
5#include <stop_token>
6#include <thread>
7#include <fmt/format.h>
8
9#include "common/param_package.h"
10#include "common/settings.h"
11#include "common/thread.h"
12#include "input_common/drivers/mouse.h"
13
14namespace InputCommon {
15constexpr int mouse_axis_x = 0;
16constexpr int mouse_axis_y = 1;
17constexpr int wheel_axis_x = 2;
18constexpr int wheel_axis_y = 3;
19constexpr int touch_axis_x = 10;
20constexpr int touch_axis_y = 11;
21constexpr PadIdentifier identifier = {
22 .guid = Common::UUID{Common::INVALID_UUID},
23 .port = 0,
24 .pad = 0,
25};
26
27Mouse::Mouse(std::string input_engine_) : InputEngine(std::move(input_engine_)) {
28 PreSetController(identifier);
29 PreSetAxis(identifier, mouse_axis_x);
30 PreSetAxis(identifier, mouse_axis_y);
31 PreSetAxis(identifier, wheel_axis_x);
32 PreSetAxis(identifier, wheel_axis_y);
33 PreSetAxis(identifier, touch_axis_x);
34 PreSetAxis(identifier, touch_axis_x);
35 update_thread = std::jthread([this](std::stop_token stop_token) { UpdateThread(stop_token); });
36}
37
38void Mouse::UpdateThread(std::stop_token stop_token) {
39 Common::SetCurrentThreadName("yuzu:input:Mouse");
40 constexpr int update_time = 10;
41 while (!stop_token.stop_requested()) {
42 if (Settings::values.mouse_panning && !Settings::values.mouse_enabled) {
43 // Slow movement by 4%
44 last_mouse_change *= 0.96f;
45 const float sensitivity =
46 Settings::values.mouse_panning_sensitivity.GetValue() * 0.022f;
47 SetAxis(identifier, mouse_axis_x, last_mouse_change.x * sensitivity);
48 SetAxis(identifier, mouse_axis_y, -last_mouse_change.y * sensitivity);
49 }
50
51 if (mouse_panning_timout++ > 20) {
52 StopPanning();
53 }
54 std::this_thread::sleep_for(std::chrono::milliseconds(update_time));
55 }
56}
57
58void Mouse::MouseMove(int x, int y, f32 touch_x, f32 touch_y, int center_x, int center_y) {
59 // If native mouse is enabled just set the screen coordinates
60 if (Settings::values.mouse_enabled) {
61 SetAxis(identifier, mouse_axis_x, touch_x);
62 SetAxis(identifier, mouse_axis_y, touch_y);
63 return;
64 }
65
66 SetAxis(identifier, touch_axis_x, touch_x);
67 SetAxis(identifier, touch_axis_y, touch_y);
68
69 if (Settings::values.mouse_panning) {
70 auto mouse_change =
71 (Common::MakeVec(x, y) - Common::MakeVec(center_x, center_y)).Cast<float>();
72 mouse_panning_timout = 0;
73
74 const auto move_distance = mouse_change.Length();
75 if (move_distance == 0) {
76 return;
77 }
78
79 // Make slow movements at least 3 units on lenght
80 if (move_distance < 3.0f) {
81 // Normalize value
82 mouse_change /= move_distance;
83 mouse_change *= 3.0f;
84 }
85
86 // Average mouse movements
87 last_mouse_change = (last_mouse_change * 0.91f) + (mouse_change * 0.09f);
88
89 const auto last_move_distance = last_mouse_change.Length();
90
91 // Make fast movements clamp to 8 units on lenght
92 if (last_move_distance > 8.0f) {
93 // Normalize value
94 last_mouse_change /= last_move_distance;
95 last_mouse_change *= 8.0f;
96 }
97
98 // Ignore average if it's less than 1 unit and use current movement value
99 if (last_move_distance < 1.0f) {
100 last_mouse_change = mouse_change / mouse_change.Length();
101 }
102
103 return;
104 }
105
106 if (button_pressed) {
107 const auto mouse_move = Common::MakeVec<int>(x, y) - mouse_origin;
108 const float sensitivity = Settings::values.mouse_panning_sensitivity.GetValue() * 0.0012f;
109 SetAxis(identifier, mouse_axis_x, static_cast<float>(mouse_move.x) * sensitivity);
110 SetAxis(identifier, mouse_axis_y, static_cast<float>(-mouse_move.y) * sensitivity);
111 }
112}
113
114void Mouse::PressButton(int x, int y, f32 touch_x, f32 touch_y, MouseButton button) {
115 SetAxis(identifier, touch_axis_x, touch_x);
116 SetAxis(identifier, touch_axis_y, touch_y);
117 SetButton(identifier, static_cast<int>(button), true);
118 // Set initial analog parameters
119 mouse_origin = {x, y};
120 last_mouse_position = {x, y};
121 button_pressed = true;
122}
123
124void Mouse::ReleaseButton(MouseButton button) {
125 SetButton(identifier, static_cast<int>(button), false);
126
127 if (!Settings::values.mouse_panning && !Settings::values.mouse_enabled) {
128 SetAxis(identifier, mouse_axis_x, 0);
129 SetAxis(identifier, mouse_axis_y, 0);
130 }
131 button_pressed = false;
132}
133
134void Mouse::MouseWheelChange(int x, int y) {
135 wheel_position.x += x;
136 wheel_position.y += y;
137 SetAxis(identifier, wheel_axis_x, static_cast<f32>(wheel_position.x));
138 SetAxis(identifier, wheel_axis_y, static_cast<f32>(wheel_position.y));
139}
140
141void Mouse::ReleaseAllButtons() {
142 ResetButtonState();
143 button_pressed = false;
144}
145
146void Mouse::StopPanning() {
147 last_mouse_change = {};
148}
149
150std::vector<Common::ParamPackage> Mouse::GetInputDevices() const {
151 std::vector<Common::ParamPackage> devices;
152 devices.emplace_back(Common::ParamPackage{
153 {"engine", GetEngineName()},
154 {"display", "Keyboard/Mouse"},
155 });
156 return devices;
157}
158
159AnalogMapping Mouse::GetAnalogMappingForDevice(
160 [[maybe_unused]] const Common::ParamPackage& params) {
161 // Only overwrite different buttons from default
162 AnalogMapping mapping = {};
163 Common::ParamPackage right_analog_params;
164 right_analog_params.Set("engine", GetEngineName());
165 right_analog_params.Set("axis_x", 0);
166 right_analog_params.Set("axis_y", 1);
167 right_analog_params.Set("threshold", 0.5f);
168 right_analog_params.Set("range", 1.0f);
169 right_analog_params.Set("deadzone", 0.0f);
170 mapping.insert_or_assign(Settings::NativeAnalog::RStick, std::move(right_analog_params));
171 return mapping;
172}
173
174Common::Input::ButtonNames Mouse::GetUIName(const Common::ParamPackage& params) const {
175 if (params.Has("button")) {
176 return Common::Input::ButtonNames::Value;
177 }
178 if (params.Has("axis")) {
179 return Common::Input::ButtonNames::Value;
180 }
181
182 return Common::Input::ButtonNames::Invalid;
183}
184
185} // namespace InputCommon
diff --git a/src/input_common/drivers/mouse.h b/src/input_common/drivers/mouse.h
new file mode 100644
index 000000000..040446178
--- /dev/null
+++ b/src/input_common/drivers/mouse.h
@@ -0,0 +1,81 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included
4
5#pragma once
6
7#include <stop_token>
8#include <thread>
9
10#include "common/vector_math.h"
11#include "input_common/input_engine.h"
12
13namespace InputCommon {
14
15enum class MouseButton {
16 Left,
17 Right,
18 Wheel,
19 Backward,
20 Forward,
21 Task,
22 Extra,
23 Undefined,
24};
25
26/**
27 * A button device factory representing a keyboard. It receives keyboard events and forward them
28 * to all button devices it created.
29 */
30class Mouse final : public InputEngine {
31public:
32 explicit Mouse(std::string input_engine_);
33
34 /**
35 * Signals that mouse has moved.
36 * @param x the x-coordinate of the cursor
37 * @param y the y-coordinate of the cursor
38 * @param center_x the x-coordinate of the middle of the screen
39 * @param center_y the y-coordinate of the middle of the screen
40 */
41 void MouseMove(int x, int y, f32 touch_x, f32 touch_y, int center_x, int center_y);
42
43 /**
44 * Sets the status of all buttons bound with the key to pressed
45 * @param key_code the code of the key to press
46 */
47 void PressButton(int x, int y, f32 touch_x, f32 touch_y, MouseButton button);
48
49 /**
50 * Sets the status of all buttons bound with the key to released
51 * @param key_code the code of the key to release
52 */
53 void ReleaseButton(MouseButton button);
54
55 /**
56 * Sets the status of the mouse wheel
57 * @param x delta movement in the x direction
58 * @param y delta movement in the y direction
59 */
60 void MouseWheelChange(int x, int y);
61
62 void ReleaseAllButtons();
63
64 std::vector<Common::ParamPackage> GetInputDevices() const override;
65 AnalogMapping GetAnalogMappingForDevice(const Common::ParamPackage& params) override;
66 Common::Input::ButtonNames GetUIName(const Common::ParamPackage& params) const override;
67
68private:
69 void UpdateThread(std::stop_token stop_token);
70 void StopPanning();
71
72 Common::Vec2<int> mouse_origin;
73 Common::Vec2<int> last_mouse_position;
74 Common::Vec2<float> last_mouse_change;
75 Common::Vec2<int> wheel_position;
76 bool button_pressed;
77 int mouse_panning_timout{};
78 std::jthread update_thread;
79};
80
81} // namespace InputCommon
diff --git a/src/input_common/drivers/sdl_driver.cpp b/src/input_common/drivers/sdl_driver.cpp
new file mode 100644
index 000000000..0cda9df62
--- /dev/null
+++ b/src/input_common/drivers/sdl_driver.cpp
@@ -0,0 +1,926 @@
1// Copyright 2018 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "common/logging/log.h"
6#include "common/math_util.h"
7#include "common/param_package.h"
8#include "common/settings.h"
9#include "common/thread.h"
10#include "common/vector_math.h"
11#include "input_common/drivers/sdl_driver.h"
12
13namespace InputCommon {
14
15namespace {
16std::string GetGUID(SDL_Joystick* joystick) {
17 const SDL_JoystickGUID guid = SDL_JoystickGetGUID(joystick);
18 char guid_str[33];
19 SDL_JoystickGetGUIDString(guid, guid_str, sizeof(guid_str));
20 return guid_str;
21}
22} // Anonymous namespace
23
24static int SDLEventWatcher(void* user_data, SDL_Event* event) {
25 auto* const sdl_state = static_cast<SDLDriver*>(user_data);
26
27 sdl_state->HandleGameControllerEvent(*event);
28
29 return 0;
30}
31
32class SDLJoystick {
33public:
34 SDLJoystick(std::string guid_, int port_, SDL_Joystick* joystick,
35 SDL_GameController* game_controller)
36 : guid{std::move(guid_)}, port{port_}, sdl_joystick{joystick, &SDL_JoystickClose},
37 sdl_controller{game_controller, &SDL_GameControllerClose} {
38 EnableMotion();
39 }
40
41 void EnableMotion() {
42 if (sdl_controller) {
43 SDL_GameController* controller = sdl_controller.get();
44 if (SDL_GameControllerHasSensor(controller, SDL_SENSOR_ACCEL) && !has_accel) {
45 SDL_GameControllerSetSensorEnabled(controller, SDL_SENSOR_ACCEL, SDL_TRUE);
46 has_accel = true;
47 }
48 if (SDL_GameControllerHasSensor(controller, SDL_SENSOR_GYRO) && !has_gyro) {
49 SDL_GameControllerSetSensorEnabled(controller, SDL_SENSOR_GYRO, SDL_TRUE);
50 has_gyro = true;
51 }
52 }
53 }
54
55 bool HasGyro() const {
56 return has_gyro;
57 }
58
59 bool HasAccel() const {
60 return has_accel;
61 }
62
63 bool UpdateMotion(SDL_ControllerSensorEvent event) {
64 constexpr float gravity_constant = 9.80665f;
65 std::lock_guard lock{mutex};
66 const u64 time_difference = event.timestamp - last_motion_update;
67 last_motion_update = event.timestamp;
68 switch (event.sensor) {
69 case SDL_SENSOR_ACCEL: {
70 motion.accel_x = -event.data[0] / gravity_constant;
71 motion.accel_y = event.data[2] / gravity_constant;
72 motion.accel_z = -event.data[1] / gravity_constant;
73 break;
74 }
75 case SDL_SENSOR_GYRO: {
76 motion.gyro_x = event.data[0] / (Common::PI * 2);
77 motion.gyro_y = -event.data[2] / (Common::PI * 2);
78 motion.gyro_z = event.data[1] / (Common::PI * 2);
79 break;
80 }
81 }
82
83 // Ignore duplicated timestamps
84 if (time_difference == 0) {
85 return false;
86 }
87 motion.delta_timestamp = time_difference * 1000;
88 return true;
89 }
90
91 const BasicMotion& GetMotion() const {
92 return motion;
93 }
94
95 bool RumblePlay(const Common::Input::VibrationStatus vibration) {
96 constexpr u32 rumble_max_duration_ms = 1000;
97 if (sdl_controller) {
98 return SDL_GameControllerRumble(
99 sdl_controller.get(), static_cast<u16>(vibration.low_amplitude),
100 static_cast<u16>(vibration.high_amplitude), rumble_max_duration_ms) != -1;
101 } else if (sdl_joystick) {
102 return SDL_JoystickRumble(sdl_joystick.get(), static_cast<u16>(vibration.low_amplitude),
103 static_cast<u16>(vibration.high_amplitude),
104 rumble_max_duration_ms) != -1;
105 }
106
107 return false;
108 }
109
110 bool HasHDRumble() const {
111 if (sdl_controller) {
112 return (SDL_GameControllerGetType(sdl_controller.get()) ==
113 SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_PRO);
114 }
115 return false;
116 }
117 /**
118 * The Pad identifier of the joystick
119 */
120 const PadIdentifier GetPadIdentifier() const {
121 return {
122 .guid = Common::UUID{guid},
123 .port = static_cast<std::size_t>(port),
124 .pad = 0,
125 };
126 }
127
128 /**
129 * The guid of the joystick
130 */
131 const std::string& GetGUID() const {
132 return guid;
133 }
134
135 /**
136 * The number of joystick from the same type that were connected before this joystick
137 */
138 int GetPort() const {
139 return port;
140 }
141
142 SDL_Joystick* GetSDLJoystick() const {
143 return sdl_joystick.get();
144 }
145
146 SDL_GameController* GetSDLGameController() const {
147 return sdl_controller.get();
148 }
149
150 void SetSDLJoystick(SDL_Joystick* joystick, SDL_GameController* controller) {
151 sdl_joystick.reset(joystick);
152 sdl_controller.reset(controller);
153 }
154
155 bool IsJoyconLeft() const {
156 const std::string controller_name = GetControllerName();
157 if (std::strstr(controller_name.c_str(), "Joy-Con Left") != nullptr) {
158 return true;
159 }
160 if (std::strstr(controller_name.c_str(), "Joy-Con (L)") != nullptr) {
161 return true;
162 }
163 return false;
164 }
165
166 bool IsJoyconRight() const {
167 const std::string controller_name = GetControllerName();
168 if (std::strstr(controller_name.c_str(), "Joy-Con Right") != nullptr) {
169 return true;
170 }
171 if (std::strstr(controller_name.c_str(), "Joy-Con (R)") != nullptr) {
172 return true;
173 }
174 return false;
175 }
176
177 BatteryLevel GetBatteryLevel() {
178 const auto level = SDL_JoystickCurrentPowerLevel(sdl_joystick.get());
179 switch (level) {
180 case SDL_JOYSTICK_POWER_EMPTY:
181 return BatteryLevel::Empty;
182 case SDL_JOYSTICK_POWER_LOW:
183 return BatteryLevel::Critical;
184 case SDL_JOYSTICK_POWER_MEDIUM:
185 return BatteryLevel::Low;
186 case SDL_JOYSTICK_POWER_FULL:
187 return BatteryLevel::Medium;
188 case SDL_JOYSTICK_POWER_MAX:
189 return BatteryLevel::Full;
190 case SDL_JOYSTICK_POWER_UNKNOWN:
191 case SDL_JOYSTICK_POWER_WIRED:
192 default:
193 return BatteryLevel::Charging;
194 }
195 }
196
197 std::string GetControllerName() const {
198 if (sdl_controller) {
199 switch (SDL_GameControllerGetType(sdl_controller.get())) {
200 case SDL_CONTROLLER_TYPE_XBOX360:
201 return "XBox 360 Controller";
202 case SDL_CONTROLLER_TYPE_XBOXONE:
203 return "XBox One Controller";
204 default:
205 break;
206 }
207 const auto name = SDL_GameControllerName(sdl_controller.get());
208 if (name) {
209 return name;
210 }
211 }
212
213 if (sdl_joystick) {
214 const auto name = SDL_JoystickName(sdl_joystick.get());
215 if (name) {
216 return name;
217 }
218 }
219
220 return "Unknown";
221 }
222
223private:
224 std::string guid;
225 int port;
226 std::unique_ptr<SDL_Joystick, decltype(&SDL_JoystickClose)> sdl_joystick;
227 std::unique_ptr<SDL_GameController, decltype(&SDL_GameControllerClose)> sdl_controller;
228 mutable std::mutex mutex;
229
230 u64 last_motion_update{};
231 bool has_gyro{false};
232 bool has_accel{false};
233 BasicMotion motion;
234};
235
236std::shared_ptr<SDLJoystick> SDLDriver::GetSDLJoystickByGUID(const std::string& guid, int port) {
237 std::lock_guard lock{joystick_map_mutex};
238 const auto it = joystick_map.find(guid);
239
240 if (it != joystick_map.end()) {
241 while (it->second.size() <= static_cast<std::size_t>(port)) {
242 auto joystick = std::make_shared<SDLJoystick>(guid, static_cast<int>(it->second.size()),
243 nullptr, nullptr);
244 it->second.emplace_back(std::move(joystick));
245 }
246
247 return it->second[static_cast<std::size_t>(port)];
248 }
249
250 auto joystick = std::make_shared<SDLJoystick>(guid, 0, nullptr, nullptr);
251
252 return joystick_map[guid].emplace_back(std::move(joystick));
253}
254
255std::shared_ptr<SDLJoystick> SDLDriver::GetSDLJoystickBySDLID(SDL_JoystickID sdl_id) {
256 auto sdl_joystick = SDL_JoystickFromInstanceID(sdl_id);
257 const std::string guid = GetGUID(sdl_joystick);
258
259 std::lock_guard lock{joystick_map_mutex};
260 const auto map_it = joystick_map.find(guid);
261
262 if (map_it == joystick_map.end()) {
263 return nullptr;
264 }
265
266 const auto vec_it = std::find_if(map_it->second.begin(), map_it->second.end(),
267 [&sdl_joystick](const auto& joystick) {
268 return joystick->GetSDLJoystick() == sdl_joystick;
269 });
270
271 if (vec_it == map_it->second.end()) {
272 return nullptr;
273 }
274
275 return *vec_it;
276}
277
278void SDLDriver::InitJoystick(int joystick_index) {
279 SDL_Joystick* sdl_joystick = SDL_JoystickOpen(joystick_index);
280 SDL_GameController* sdl_gamecontroller = nullptr;
281
282 if (SDL_IsGameController(joystick_index)) {
283 sdl_gamecontroller = SDL_GameControllerOpen(joystick_index);
284 }
285
286 if (!sdl_joystick) {
287 LOG_ERROR(Input, "Failed to open joystick {}", joystick_index);
288 return;
289 }
290
291 const std::string guid = GetGUID(sdl_joystick);
292
293 std::lock_guard lock{joystick_map_mutex};
294 if (joystick_map.find(guid) == joystick_map.end()) {
295 auto joystick = std::make_shared<SDLJoystick>(guid, 0, sdl_joystick, sdl_gamecontroller);
296 PreSetController(joystick->GetPadIdentifier());
297 SetBattery(joystick->GetPadIdentifier(), joystick->GetBatteryLevel());
298 joystick_map[guid].emplace_back(std::move(joystick));
299 return;
300 }
301
302 auto& joystick_guid_list = joystick_map[guid];
303 const auto joystick_it =
304 std::find_if(joystick_guid_list.begin(), joystick_guid_list.end(),
305 [](const auto& joystick) { return !joystick->GetSDLJoystick(); });
306
307 if (joystick_it != joystick_guid_list.end()) {
308 (*joystick_it)->SetSDLJoystick(sdl_joystick, sdl_gamecontroller);
309 return;
310 }
311
312 const int port = static_cast<int>(joystick_guid_list.size());
313 auto joystick = std::make_shared<SDLJoystick>(guid, port, sdl_joystick, sdl_gamecontroller);
314 PreSetController(joystick->GetPadIdentifier());
315 SetBattery(joystick->GetPadIdentifier(), joystick->GetBatteryLevel());
316 joystick_guid_list.emplace_back(std::move(joystick));
317}
318
319void SDLDriver::CloseJoystick(SDL_Joystick* sdl_joystick) {
320 const std::string guid = GetGUID(sdl_joystick);
321
322 std::lock_guard lock{joystick_map_mutex};
323 // This call to guid is safe since the joystick is guaranteed to be in the map
324 const auto& joystick_guid_list = joystick_map[guid];
325 const auto joystick_it = std::find_if(joystick_guid_list.begin(), joystick_guid_list.end(),
326 [&sdl_joystick](const auto& joystick) {
327 return joystick->GetSDLJoystick() == sdl_joystick;
328 });
329
330 if (joystick_it != joystick_guid_list.end()) {
331 (*joystick_it)->SetSDLJoystick(nullptr, nullptr);
332 }
333}
334
335void SDLDriver::HandleGameControllerEvent(const SDL_Event& event) {
336 switch (event.type) {
337 case SDL_JOYBUTTONUP: {
338 if (const auto joystick = GetSDLJoystickBySDLID(event.jbutton.which)) {
339 const PadIdentifier identifier = joystick->GetPadIdentifier();
340 SetButton(identifier, event.jbutton.button, false);
341 }
342 break;
343 }
344 case SDL_JOYBUTTONDOWN: {
345 if (const auto joystick = GetSDLJoystickBySDLID(event.jbutton.which)) {
346 const PadIdentifier identifier = joystick->GetPadIdentifier();
347 SetButton(identifier, event.jbutton.button, true);
348 }
349 break;
350 }
351 case SDL_JOYHATMOTION: {
352 if (const auto joystick = GetSDLJoystickBySDLID(event.jhat.which)) {
353 const PadIdentifier identifier = joystick->GetPadIdentifier();
354 SetHatButton(identifier, event.jhat.hat, event.jhat.value);
355 }
356 break;
357 }
358 case SDL_JOYAXISMOTION: {
359 if (const auto joystick = GetSDLJoystickBySDLID(event.jaxis.which)) {
360 const PadIdentifier identifier = joystick->GetPadIdentifier();
361 SetAxis(identifier, event.jaxis.axis, event.jaxis.value / 32767.0f);
362 }
363 break;
364 }
365 case SDL_CONTROLLERSENSORUPDATE: {
366 if (auto joystick = GetSDLJoystickBySDLID(event.csensor.which)) {
367 if (joystick->UpdateMotion(event.csensor)) {
368 const PadIdentifier identifier = joystick->GetPadIdentifier();
369 SetMotion(identifier, 0, joystick->GetMotion());
370 }
371 }
372 break;
373 }
374 case SDL_JOYDEVICEREMOVED:
375 LOG_DEBUG(Input, "Controller removed with Instance_ID {}", event.jdevice.which);
376 CloseJoystick(SDL_JoystickFromInstanceID(event.jdevice.which));
377 break;
378 case SDL_JOYDEVICEADDED:
379 LOG_DEBUG(Input, "Controller connected with device index {}", event.jdevice.which);
380 InitJoystick(event.jdevice.which);
381 break;
382 }
383}
384
385void SDLDriver::CloseJoysticks() {
386 std::lock_guard lock{joystick_map_mutex};
387 joystick_map.clear();
388}
389
390SDLDriver::SDLDriver(std::string input_engine_) : InputEngine(std::move(input_engine_)) {
391 if (!Settings::values.enable_raw_input) {
392 // Disable raw input. When enabled this setting causes SDL to die when a web applet opens
393 SDL_SetHint(SDL_HINT_JOYSTICK_RAWINPUT, "0");
394 }
395
396 // Prevent SDL from adding undesired axis
397 SDL_SetHint(SDL_HINT_ACCELEROMETER_AS_JOYSTICK, "0");
398
399 // Enable HIDAPI rumble. This prevents SDL from disabling motion on PS4 and PS5 controllers
400 SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS4_RUMBLE, "1");
401 SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS5_RUMBLE, "1");
402 SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1");
403
404 // Use hidapi driver for joycons. This will allow joycons to be detected as a GameController and
405 // not a generic one
406 SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_JOY_CONS, "1");
407
408 // Disable hidapi driver for xbox. Already default on Windows, this causes conflict with native
409 // driver on Linux.
410 SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_XBOX, "0");
411
412 // If the frontend is going to manage the event loop, then we don't start one here
413 start_thread = SDL_WasInit(SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER) == 0;
414 if (start_thread && SDL_Init(SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER) < 0) {
415 LOG_CRITICAL(Input, "SDL_Init failed with: {}", SDL_GetError());
416 return;
417 }
418
419 SDL_AddEventWatch(&SDLEventWatcher, this);
420
421 initialized = true;
422 if (start_thread) {
423 poll_thread = std::thread([this] {
424 Common::SetCurrentThreadName("yuzu:input:SDL");
425 using namespace std::chrono_literals;
426 while (initialized) {
427 SDL_PumpEvents();
428 std::this_thread::sleep_for(1ms);
429 }
430 });
431 }
432 // Because the events for joystick connection happens before we have our event watcher added, we
433 // can just open all the joysticks right here
434 for (int i = 0; i < SDL_NumJoysticks(); ++i) {
435 InitJoystick(i);
436 }
437}
438
439SDLDriver::~SDLDriver() {
440 CloseJoysticks();
441 SDL_DelEventWatch(&SDLEventWatcher, this);
442
443 initialized = false;
444 if (start_thread) {
445 poll_thread.join();
446 SDL_QuitSubSystem(SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER);
447 }
448}
449
450std::vector<Common::ParamPackage> SDLDriver::GetInputDevices() const {
451 std::vector<Common::ParamPackage> devices;
452 std::unordered_map<int, std::shared_ptr<SDLJoystick>> joycon_pairs;
453 for (const auto& [key, value] : joystick_map) {
454 for (const auto& joystick : value) {
455 if (!joystick->GetSDLJoystick()) {
456 continue;
457 }
458 const std::string name =
459 fmt::format("{} {}", joystick->GetControllerName(), joystick->GetPort());
460 devices.emplace_back(Common::ParamPackage{
461 {"engine", GetEngineName()},
462 {"display", std::move(name)},
463 {"guid", joystick->GetGUID()},
464 {"port", std::to_string(joystick->GetPort())},
465 });
466 if (joystick->IsJoyconLeft()) {
467 joycon_pairs.insert_or_assign(joystick->GetPort(), joystick);
468 }
469 }
470 }
471
472 // Add dual controllers
473 for (const auto& [key, value] : joystick_map) {
474 for (const auto& joystick : value) {
475 if (joystick->IsJoyconRight()) {
476 if (!joycon_pairs.contains(joystick->GetPort())) {
477 continue;
478 }
479 const auto joystick2 = joycon_pairs.at(joystick->GetPort());
480
481 const std::string name =
482 fmt::format("{} {}", "Nintendo Dual Joy-Con", joystick->GetPort());
483 devices.emplace_back(Common::ParamPackage{
484 {"engine", GetEngineName()},
485 {"display", std::move(name)},
486 {"guid", joystick->GetGUID()},
487 {"guid2", joystick2->GetGUID()},
488 {"port", std::to_string(joystick->GetPort())},
489 });
490 }
491 }
492 }
493 return devices;
494}
495
496Common::Input::VibrationError SDLDriver::SetRumble(
497 const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) {
498 const auto joystick =
499 GetSDLJoystickByGUID(identifier.guid.Format(), static_cast<int>(identifier.port));
500 const auto process_amplitude_exp = [](f32 amplitude, f32 factor) {
501 return (amplitude + std::pow(amplitude, factor)) * 0.5f * 0xFFFF;
502 };
503
504 // Default exponential curve for rumble
505 f32 factor = 0.35f;
506
507 // If vibration is set as a linear output use a flatter value
508 if (vibration.type == Common::Input::VibrationAmplificationType::Linear) {
509 factor = 0.5f;
510 }
511
512 // Amplitude for HD rumble needs no modification
513 if (joystick->HasHDRumble()) {
514 factor = 1.0f;
515 }
516
517 const Common::Input::VibrationStatus new_vibration{
518 .low_amplitude = process_amplitude_exp(vibration.low_amplitude, factor),
519 .low_frequency = vibration.low_frequency,
520 .high_amplitude = process_amplitude_exp(vibration.high_amplitude, factor),
521 .high_frequency = vibration.high_frequency,
522 .type = Common::Input::VibrationAmplificationType::Exponential,
523 };
524
525 if (!joystick->RumblePlay(new_vibration)) {
526 return Common::Input::VibrationError::Unknown;
527 }
528
529 return Common::Input::VibrationError::None;
530}
531
532Common::ParamPackage SDLDriver::BuildAnalogParamPackageForButton(int port, std::string guid,
533 s32 axis, float value) const {
534 Common::ParamPackage params{};
535 params.Set("engine", GetEngineName());
536 params.Set("port", port);
537 params.Set("guid", std::move(guid));
538 params.Set("axis", axis);
539 params.Set("threshold", "0.5");
540 params.Set("invert", value < 0 ? "-" : "+");
541 return params;
542}
543
544Common::ParamPackage SDLDriver::BuildButtonParamPackageForButton(int port, std::string guid,
545 s32 button) const {
546 Common::ParamPackage params{};
547 params.Set("engine", GetEngineName());
548 params.Set("port", port);
549 params.Set("guid", std::move(guid));
550 params.Set("button", button);
551 return params;
552}
553
554Common::ParamPackage SDLDriver::BuildHatParamPackageForButton(int port, std::string guid, s32 hat,
555 u8 value) const {
556 Common::ParamPackage params{};
557 params.Set("engine", GetEngineName());
558 params.Set("port", port);
559 params.Set("guid", std::move(guid));
560 params.Set("hat", hat);
561 params.Set("direction", GetHatButtonName(value));
562 return params;
563}
564
565Common::ParamPackage SDLDriver::BuildMotionParam(int port, std::string guid) const {
566 Common::ParamPackage params{};
567 params.Set("engine", GetEngineName());
568 params.Set("motion", 0);
569 params.Set("port", port);
570 params.Set("guid", std::move(guid));
571 return params;
572}
573
574Common::ParamPackage SDLDriver::BuildParamPackageForBinding(
575 int port, const std::string& guid, const SDL_GameControllerButtonBind& binding) const {
576 switch (binding.bindType) {
577 case SDL_CONTROLLER_BINDTYPE_NONE:
578 break;
579 case SDL_CONTROLLER_BINDTYPE_AXIS:
580 return BuildAnalogParamPackageForButton(port, guid, binding.value.axis);
581 case SDL_CONTROLLER_BINDTYPE_BUTTON:
582 return BuildButtonParamPackageForButton(port, guid, binding.value.button);
583 case SDL_CONTROLLER_BINDTYPE_HAT:
584 return BuildHatParamPackageForButton(port, guid, binding.value.hat.hat,
585 static_cast<u8>(binding.value.hat.hat_mask));
586 }
587 return {};
588}
589
590Common::ParamPackage SDLDriver::BuildParamPackageForAnalog(PadIdentifier identifier, int axis_x,
591 int axis_y, float offset_x,
592 float offset_y) const {
593 Common::ParamPackage params;
594 params.Set("engine", GetEngineName());
595 params.Set("port", static_cast<int>(identifier.port));
596 params.Set("guid", identifier.guid.Format());
597 params.Set("axis_x", axis_x);
598 params.Set("axis_y", axis_y);
599 params.Set("offset_x", offset_x);
600 params.Set("offset_y", offset_y);
601 params.Set("invert_x", "+");
602 params.Set("invert_y", "+");
603 return params;
604}
605
606ButtonMapping SDLDriver::GetButtonMappingForDevice(const Common::ParamPackage& params) {
607 if (!params.Has("guid") || !params.Has("port")) {
608 return {};
609 }
610 const auto joystick = GetSDLJoystickByGUID(params.Get("guid", ""), params.Get("port", 0));
611
612 auto* controller = joystick->GetSDLGameController();
613 if (controller == nullptr) {
614 return {};
615 }
616
617 // This list is missing ZL/ZR since those are not considered buttons in SDL GameController.
618 // We will add those afterwards
619 // This list also excludes Screenshot since theres not really a mapping for that
620 ButtonBindings switch_to_sdl_button;
621
622 if (SDL_GameControllerGetType(controller) == SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_PRO) {
623 switch_to_sdl_button = GetNintendoButtonBinding(joystick);
624 } else {
625 switch_to_sdl_button = GetDefaultButtonBinding();
626 }
627
628 // Add the missing bindings for ZL/ZR
629 static constexpr ZButtonBindings switch_to_sdl_axis{{
630 {Settings::NativeButton::ZL, SDL_CONTROLLER_AXIS_TRIGGERLEFT},
631 {Settings::NativeButton::ZR, SDL_CONTROLLER_AXIS_TRIGGERRIGHT},
632 }};
633
634 // Parameters contain two joysticks return dual
635 if (params.Has("guid2")) {
636 const auto joystick2 = GetSDLJoystickByGUID(params.Get("guid2", ""), params.Get("port", 0));
637
638 if (joystick2->GetSDLGameController() != nullptr) {
639 return GetDualControllerMapping(joystick, joystick2, switch_to_sdl_button,
640 switch_to_sdl_axis);
641 }
642 }
643
644 return GetSingleControllerMapping(joystick, switch_to_sdl_button, switch_to_sdl_axis);
645}
646
647ButtonBindings SDLDriver::GetDefaultButtonBinding() const {
648 return {
649 std::pair{Settings::NativeButton::A, SDL_CONTROLLER_BUTTON_B},
650 {Settings::NativeButton::B, SDL_CONTROLLER_BUTTON_A},
651 {Settings::NativeButton::X, SDL_CONTROLLER_BUTTON_Y},
652 {Settings::NativeButton::Y, SDL_CONTROLLER_BUTTON_X},
653 {Settings::NativeButton::LStick, SDL_CONTROLLER_BUTTON_LEFTSTICK},
654 {Settings::NativeButton::RStick, SDL_CONTROLLER_BUTTON_RIGHTSTICK},
655 {Settings::NativeButton::L, SDL_CONTROLLER_BUTTON_LEFTSHOULDER},
656 {Settings::NativeButton::R, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER},
657 {Settings::NativeButton::Plus, SDL_CONTROLLER_BUTTON_START},
658 {Settings::NativeButton::Minus, SDL_CONTROLLER_BUTTON_BACK},
659 {Settings::NativeButton::DLeft, SDL_CONTROLLER_BUTTON_DPAD_LEFT},
660 {Settings::NativeButton::DUp, SDL_CONTROLLER_BUTTON_DPAD_UP},
661 {Settings::NativeButton::DRight, SDL_CONTROLLER_BUTTON_DPAD_RIGHT},
662 {Settings::NativeButton::DDown, SDL_CONTROLLER_BUTTON_DPAD_DOWN},
663 {Settings::NativeButton::SL, SDL_CONTROLLER_BUTTON_LEFTSHOULDER},
664 {Settings::NativeButton::SR, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER},
665 {Settings::NativeButton::Home, SDL_CONTROLLER_BUTTON_GUIDE},
666 };
667}
668
669ButtonBindings SDLDriver::GetNintendoButtonBinding(
670 const std::shared_ptr<SDLJoystick>& joystick) const {
671 // Default SL/SR mapping for pro controllers
672 auto sl_button = SDL_CONTROLLER_BUTTON_LEFTSHOULDER;
673 auto sr_button = SDL_CONTROLLER_BUTTON_RIGHTSHOULDER;
674
675 if (joystick->IsJoyconLeft()) {
676 sl_button = SDL_CONTROLLER_BUTTON_PADDLE2;
677 sr_button = SDL_CONTROLLER_BUTTON_PADDLE4;
678 }
679 if (joystick->IsJoyconRight()) {
680 sl_button = SDL_CONTROLLER_BUTTON_PADDLE3;
681 sr_button = SDL_CONTROLLER_BUTTON_PADDLE1;
682 }
683
684 return {
685 std::pair{Settings::NativeButton::A, SDL_CONTROLLER_BUTTON_A},
686 {Settings::NativeButton::B, SDL_CONTROLLER_BUTTON_B},
687 {Settings::NativeButton::X, SDL_CONTROLLER_BUTTON_X},
688 {Settings::NativeButton::Y, SDL_CONTROLLER_BUTTON_Y},
689 {Settings::NativeButton::LStick, SDL_CONTROLLER_BUTTON_LEFTSTICK},
690 {Settings::NativeButton::RStick, SDL_CONTROLLER_BUTTON_RIGHTSTICK},
691 {Settings::NativeButton::L, SDL_CONTROLLER_BUTTON_LEFTSHOULDER},
692 {Settings::NativeButton::R, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER},
693 {Settings::NativeButton::Plus, SDL_CONTROLLER_BUTTON_START},
694 {Settings::NativeButton::Minus, SDL_CONTROLLER_BUTTON_BACK},
695 {Settings::NativeButton::DLeft, SDL_CONTROLLER_BUTTON_DPAD_LEFT},
696 {Settings::NativeButton::DUp, SDL_CONTROLLER_BUTTON_DPAD_UP},
697 {Settings::NativeButton::DRight, SDL_CONTROLLER_BUTTON_DPAD_RIGHT},
698 {Settings::NativeButton::DDown, SDL_CONTROLLER_BUTTON_DPAD_DOWN},
699 {Settings::NativeButton::SL, sl_button},
700 {Settings::NativeButton::SR, sr_button},
701 {Settings::NativeButton::Home, SDL_CONTROLLER_BUTTON_GUIDE},
702 };
703}
704
705ButtonMapping SDLDriver::GetSingleControllerMapping(
706 const std::shared_ptr<SDLJoystick>& joystick, const ButtonBindings& switch_to_sdl_button,
707 const ZButtonBindings& switch_to_sdl_axis) const {
708 ButtonMapping mapping;
709 mapping.reserve(switch_to_sdl_button.size() + switch_to_sdl_axis.size());
710 auto* controller = joystick->GetSDLGameController();
711
712 for (const auto& [switch_button, sdl_button] : switch_to_sdl_button) {
713 const auto& binding = SDL_GameControllerGetBindForButton(controller, sdl_button);
714 mapping.insert_or_assign(
715 switch_button,
716 BuildParamPackageForBinding(joystick->GetPort(), joystick->GetGUID(), binding));
717 }
718 for (const auto& [switch_button, sdl_axis] : switch_to_sdl_axis) {
719 const auto& binding = SDL_GameControllerGetBindForAxis(controller, sdl_axis);
720 mapping.insert_or_assign(
721 switch_button,
722 BuildParamPackageForBinding(joystick->GetPort(), joystick->GetGUID(), binding));
723 }
724
725 return mapping;
726}
727
728ButtonMapping SDLDriver::GetDualControllerMapping(const std::shared_ptr<SDLJoystick>& joystick,
729 const std::shared_ptr<SDLJoystick>& joystick2,
730 const ButtonBindings& switch_to_sdl_button,
731 const ZButtonBindings& switch_to_sdl_axis) const {
732 ButtonMapping mapping;
733 mapping.reserve(switch_to_sdl_button.size() + switch_to_sdl_axis.size());
734 auto* controller = joystick->GetSDLGameController();
735 auto* controller2 = joystick2->GetSDLGameController();
736
737 for (const auto& [switch_button, sdl_button] : switch_to_sdl_button) {
738 if (IsButtonOnLeftSide(switch_button)) {
739 const auto& binding = SDL_GameControllerGetBindForButton(controller2, sdl_button);
740 mapping.insert_or_assign(
741 switch_button,
742 BuildParamPackageForBinding(joystick2->GetPort(), joystick2->GetGUID(), binding));
743 continue;
744 }
745 const auto& binding = SDL_GameControllerGetBindForButton(controller, sdl_button);
746 mapping.insert_or_assign(
747 switch_button,
748 BuildParamPackageForBinding(joystick->GetPort(), joystick->GetGUID(), binding));
749 }
750 for (const auto& [switch_button, sdl_axis] : switch_to_sdl_axis) {
751 if (IsButtonOnLeftSide(switch_button)) {
752 const auto& binding = SDL_GameControllerGetBindForAxis(controller2, sdl_axis);
753 mapping.insert_or_assign(
754 switch_button,
755 BuildParamPackageForBinding(joystick2->GetPort(), joystick2->GetGUID(), binding));
756 continue;
757 }
758 const auto& binding = SDL_GameControllerGetBindForAxis(controller, sdl_axis);
759 mapping.insert_or_assign(
760 switch_button,
761 BuildParamPackageForBinding(joystick->GetPort(), joystick->GetGUID(), binding));
762 }
763
764 return mapping;
765}
766
767bool SDLDriver::IsButtonOnLeftSide(Settings::NativeButton::Values button) const {
768 switch (button) {
769 case Settings::NativeButton::DDown:
770 case Settings::NativeButton::DLeft:
771 case Settings::NativeButton::DRight:
772 case Settings::NativeButton::DUp:
773 case Settings::NativeButton::L:
774 case Settings::NativeButton::LStick:
775 case Settings::NativeButton::Minus:
776 case Settings::NativeButton::Screenshot:
777 case Settings::NativeButton::ZL:
778 return true;
779 default:
780 return false;
781 }
782}
783
784AnalogMapping SDLDriver::GetAnalogMappingForDevice(const Common::ParamPackage& params) {
785 if (!params.Has("guid") || !params.Has("port")) {
786 return {};
787 }
788 const auto joystick = GetSDLJoystickByGUID(params.Get("guid", ""), params.Get("port", 0));
789 const auto joystick2 = GetSDLJoystickByGUID(params.Get("guid2", ""), params.Get("port", 0));
790 auto* controller = joystick->GetSDLGameController();
791 if (controller == nullptr) {
792 return {};
793 }
794
795 AnalogMapping mapping = {};
796 const auto& binding_left_x =
797 SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_LEFTX);
798 const auto& binding_left_y =
799 SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_LEFTY);
800 if (params.Has("guid2")) {
801 const auto identifier = joystick2->GetPadIdentifier();
802 PreSetController(identifier);
803 PreSetAxis(identifier, binding_left_x.value.axis);
804 PreSetAxis(identifier, binding_left_y.value.axis);
805 const auto left_offset_x = -GetAxis(identifier, binding_left_x.value.axis);
806 const auto left_offset_y = -GetAxis(identifier, binding_left_y.value.axis);
807 mapping.insert_or_assign(Settings::NativeAnalog::LStick,
808 BuildParamPackageForAnalog(identifier, binding_left_x.value.axis,
809 binding_left_y.value.axis,
810 left_offset_x, left_offset_y));
811 } else {
812 const auto identifier = joystick->GetPadIdentifier();
813 PreSetController(identifier);
814 PreSetAxis(identifier, binding_left_x.value.axis);
815 PreSetAxis(identifier, binding_left_y.value.axis);
816 const auto left_offset_x = -GetAxis(identifier, binding_left_x.value.axis);
817 const auto left_offset_y = -GetAxis(identifier, binding_left_y.value.axis);
818 mapping.insert_or_assign(Settings::NativeAnalog::LStick,
819 BuildParamPackageForAnalog(identifier, binding_left_x.value.axis,
820 binding_left_y.value.axis,
821 left_offset_x, left_offset_y));
822 }
823 const auto& binding_right_x =
824 SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_RIGHTX);
825 const auto& binding_right_y =
826 SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_RIGHTY);
827 const auto identifier = joystick->GetPadIdentifier();
828 PreSetController(identifier);
829 PreSetAxis(identifier, binding_right_x.value.axis);
830 PreSetAxis(identifier, binding_right_y.value.axis);
831 const auto right_offset_x = -GetAxis(identifier, binding_right_x.value.axis);
832 const auto right_offset_y = -GetAxis(identifier, binding_right_y.value.axis);
833 mapping.insert_or_assign(Settings::NativeAnalog::RStick,
834 BuildParamPackageForAnalog(identifier, binding_right_x.value.axis,
835 binding_right_y.value.axis, right_offset_x,
836 right_offset_y));
837 return mapping;
838}
839
840MotionMapping SDLDriver::GetMotionMappingForDevice(const Common::ParamPackage& params) {
841 if (!params.Has("guid") || !params.Has("port")) {
842 return {};
843 }
844 const auto joystick = GetSDLJoystickByGUID(params.Get("guid", ""), params.Get("port", 0));
845 const auto joystick2 = GetSDLJoystickByGUID(params.Get("guid2", ""), params.Get("port", 0));
846 auto* controller = joystick->GetSDLGameController();
847 if (controller == nullptr) {
848 return {};
849 }
850
851 MotionMapping mapping = {};
852 joystick->EnableMotion();
853
854 if (joystick->HasGyro() || joystick->HasAccel()) {
855 mapping.insert_or_assign(Settings::NativeMotion::MotionRight,
856 BuildMotionParam(joystick->GetPort(), joystick->GetGUID()));
857 }
858 if (params.Has("guid2")) {
859 joystick2->EnableMotion();
860 if (joystick2->HasGyro() || joystick2->HasAccel()) {
861 mapping.insert_or_assign(Settings::NativeMotion::MotionLeft,
862 BuildMotionParam(joystick2->GetPort(), joystick2->GetGUID()));
863 }
864 } else {
865 if (joystick->HasGyro() || joystick->HasAccel()) {
866 mapping.insert_or_assign(Settings::NativeMotion::MotionLeft,
867 BuildMotionParam(joystick->GetPort(), joystick->GetGUID()));
868 }
869 }
870
871 return mapping;
872}
873
874Common::Input::ButtonNames SDLDriver::GetUIName(const Common::ParamPackage& params) const {
875 if (params.Has("button")) {
876 // TODO(German77): Find how to substitue the values for real button names
877 return Common::Input::ButtonNames::Value;
878 }
879 if (params.Has("hat")) {
880 return Common::Input::ButtonNames::Value;
881 }
882 if (params.Has("axis")) {
883 return Common::Input::ButtonNames::Value;
884 }
885 if (params.Has("axis_x") && params.Has("axis_y") && params.Has("axis_z")) {
886 return Common::Input::ButtonNames::Value;
887 }
888 if (params.Has("motion")) {
889 return Common::Input::ButtonNames::Engine;
890 }
891
892 return Common::Input::ButtonNames::Invalid;
893}
894
895std::string SDLDriver::GetHatButtonName(u8 direction_value) const {
896 switch (direction_value) {
897 case SDL_HAT_UP:
898 return "up";
899 case SDL_HAT_DOWN:
900 return "down";
901 case SDL_HAT_LEFT:
902 return "left";
903 case SDL_HAT_RIGHT:
904 return "right";
905 default:
906 return {};
907 }
908}
909
910u8 SDLDriver::GetHatButtonId(const std::string& direction_name) const {
911 Uint8 direction;
912 if (direction_name == "up") {
913 direction = SDL_HAT_UP;
914 } else if (direction_name == "down") {
915 direction = SDL_HAT_DOWN;
916 } else if (direction_name == "left") {
917 direction = SDL_HAT_LEFT;
918 } else if (direction_name == "right") {
919 direction = SDL_HAT_RIGHT;
920 } else {
921 direction = 0;
922 }
923 return direction;
924}
925
926} // namespace InputCommon
diff --git a/src/input_common/sdl/sdl_impl.h b/src/input_common/drivers/sdl_driver.h
index 7a9ad6346..e9a5d2e26 100644
--- a/src/input_common/sdl/sdl_impl.h
+++ b/src/input_common/drivers/sdl_driver.h
@@ -5,7 +5,6 @@
5#pragma once 5#pragma once
6 6
7#include <atomic> 7#include <atomic>
8#include <memory>
9#include <mutex> 8#include <mutex>
10#include <thread> 9#include <thread>
11#include <unordered_map> 10#include <unordered_map>
@@ -13,34 +12,29 @@
13#include <SDL.h> 12#include <SDL.h>
14 13
15#include "common/common_types.h" 14#include "common/common_types.h"
16#include "common/threadsafe_queue.h" 15#include "input_common/input_engine.h"
17#include "input_common/sdl/sdl.h"
18 16
19union SDL_Event; 17union SDL_Event;
20using SDL_GameController = struct _SDL_GameController; 18using SDL_GameController = struct _SDL_GameController;
21using SDL_Joystick = struct _SDL_Joystick; 19using SDL_Joystick = struct _SDL_Joystick;
22using SDL_JoystickID = s32; 20using SDL_JoystickID = s32;
23 21
22namespace InputCommon {
23
24class SDLJoystick;
25
24using ButtonBindings = 26using ButtonBindings =
25 std::array<std::pair<Settings::NativeButton::Values, SDL_GameControllerButton>, 17>; 27 std::array<std::pair<Settings::NativeButton::Values, SDL_GameControllerButton>, 17>;
26using ZButtonBindings = 28using ZButtonBindings =
27 std::array<std::pair<Settings::NativeButton::Values, SDL_GameControllerAxis>, 2>; 29 std::array<std::pair<Settings::NativeButton::Values, SDL_GameControllerAxis>, 2>;
28 30
29namespace InputCommon::SDL { 31class SDLDriver : public InputEngine {
30
31class SDLAnalogFactory;
32class SDLButtonFactory;
33class SDLMotionFactory;
34class SDLVibrationFactory;
35class SDLJoystick;
36
37class SDLState : public State {
38public: 32public:
39 /// Initializes and registers SDL device factories 33 /// Initializes and registers SDL device factories
40 SDLState(); 34 explicit SDLDriver(std::string input_engine_);
41 35
42 /// Unregisters SDL device factories and shut them down. 36 /// Unregisters SDL device factories and shut them down.
43 ~SDLState() override; 37 ~SDLDriver() override;
44 38
45 /// Handle SDL_Events for joysticks from SDL_PollEvent 39 /// Handle SDL_Events for joysticks from SDL_PollEvent
46 void HandleGameControllerEvent(const SDL_Event& event); 40 void HandleGameControllerEvent(const SDL_Event& event);
@@ -54,18 +48,18 @@ public:
54 */ 48 */
55 std::shared_ptr<SDLJoystick> GetSDLJoystickByGUID(const std::string& guid, int port); 49 std::shared_ptr<SDLJoystick> GetSDLJoystickByGUID(const std::string& guid, int port);
56 50
57 /// Get all DevicePoller that use the SDL backend for a specific device type 51 std::vector<Common::ParamPackage> GetInputDevices() const override;
58 Pollers GetPollers(Polling::DeviceType type) override;
59
60 /// Used by the Pollers during config
61 std::atomic<bool> polling = false;
62 Common::SPSCQueue<SDL_Event> event_queue;
63
64 std::vector<Common::ParamPackage> GetInputDevices() override;
65 52
66 ButtonMapping GetButtonMappingForDevice(const Common::ParamPackage& params) override; 53 ButtonMapping GetButtonMappingForDevice(const Common::ParamPackage& params) override;
67 AnalogMapping GetAnalogMappingForDevice(const Common::ParamPackage& params) override; 54 AnalogMapping GetAnalogMappingForDevice(const Common::ParamPackage& params) override;
68 MotionMapping GetMotionMappingForDevice(const Common::ParamPackage& params) override; 55 MotionMapping GetMotionMappingForDevice(const Common::ParamPackage& params) override;
56 Common::Input::ButtonNames GetUIName(const Common::ParamPackage& params) const override;
57
58 std::string GetHatButtonName(u8 direction_value) const override;
59 u8 GetHatButtonId(const std::string& direction_name) const override;
60
61 Common::Input::VibrationError SetRumble(
62 const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) override;
69 63
70private: 64private:
71 void InitJoystick(int joystick_index); 65 void InitJoystick(int joystick_index);
@@ -74,6 +68,23 @@ private:
74 /// Needs to be called before SDL_QuitSubSystem. 68 /// Needs to be called before SDL_QuitSubSystem.
75 void CloseJoysticks(); 69 void CloseJoysticks();
76 70
71 Common::ParamPackage BuildAnalogParamPackageForButton(int port, std::string guid, s32 axis,
72 float value = 0.1f) const;
73 Common::ParamPackage BuildButtonParamPackageForButton(int port, std::string guid,
74 s32 button) const;
75
76 Common::ParamPackage BuildHatParamPackageForButton(int port, std::string guid, s32 hat,
77 u8 value) const;
78
79 Common::ParamPackage BuildMotionParam(int port, std::string guid) const;
80
81 Common::ParamPackage BuildParamPackageForBinding(
82 int port, const std::string& guid, const SDL_GameControllerButtonBind& binding) const;
83
84 Common::ParamPackage BuildParamPackageForAnalog(PadIdentifier identifier, int axis_x,
85 int axis_y, float offset_x,
86 float offset_y) const;
87
77 /// Returns the default button bindings list for generic controllers 88 /// Returns the default button bindings list for generic controllers
78 ButtonBindings GetDefaultButtonBinding() const; 89 ButtonBindings GetDefaultButtonBinding() const;
79 90
@@ -94,21 +105,13 @@ private:
94 /// Returns true if the button is on the left joycon 105 /// Returns true if the button is on the left joycon
95 bool IsButtonOnLeftSide(Settings::NativeButton::Values button) const; 106 bool IsButtonOnLeftSide(Settings::NativeButton::Values button) const;
96 107
97 // Set to true if SDL supports game controller subsystem
98 bool has_gamecontroller = false;
99
100 /// Map of GUID of a list of corresponding virtual Joysticks 108 /// Map of GUID of a list of corresponding virtual Joysticks
101 std::unordered_map<std::string, std::vector<std::shared_ptr<SDLJoystick>>> joystick_map; 109 std::unordered_map<std::string, std::vector<std::shared_ptr<SDLJoystick>>> joystick_map;
102 std::mutex joystick_map_mutex; 110 std::mutex joystick_map_mutex;
103 111
104 std::shared_ptr<SDLButtonFactory> button_factory;
105 std::shared_ptr<SDLAnalogFactory> analog_factory;
106 std::shared_ptr<SDLVibrationFactory> vibration_factory;
107 std::shared_ptr<SDLMotionFactory> motion_factory;
108
109 bool start_thread = false; 112 bool start_thread = false;
110 std::atomic<bool> initialized = false; 113 std::atomic<bool> initialized = false;
111 114
112 std::thread poll_thread; 115 std::thread poll_thread;
113}; 116};
114} // namespace InputCommon::SDL 117} // namespace InputCommon
diff --git a/src/input_common/drivers/tas_input.cpp b/src/input_common/drivers/tas_input.cpp
new file mode 100644
index 000000000..5bdd5dac3
--- /dev/null
+++ b/src/input_common/drivers/tas_input.cpp
@@ -0,0 +1,320 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2+
3// Refer to the license.txt file included.
4
5#include <cstring>
6#include <fmt/format.h>
7
8#include "common/fs/file.h"
9#include "common/fs/fs_types.h"
10#include "common/fs/path_util.h"
11#include "common/logging/log.h"
12#include "common/settings.h"
13#include "input_common/drivers/tas_input.h"
14
15namespace InputCommon::TasInput {
16
17enum class Tas::TasAxis : u8 {
18 StickX,
19 StickY,
20 SubstickX,
21 SubstickY,
22 Undefined,
23};
24
25// Supported keywords and buttons from a TAS file
26constexpr std::array<std::pair<std::string_view, TasButton>, 20> text_to_tas_button = {
27 std::pair{"KEY_A", TasButton::BUTTON_A},
28 {"KEY_B", TasButton::BUTTON_B},
29 {"KEY_X", TasButton::BUTTON_X},
30 {"KEY_Y", TasButton::BUTTON_Y},
31 {"KEY_LSTICK", TasButton::STICK_L},
32 {"KEY_RSTICK", TasButton::STICK_R},
33 {"KEY_L", TasButton::TRIGGER_L},
34 {"KEY_R", TasButton::TRIGGER_R},
35 {"KEY_PLUS", TasButton::BUTTON_PLUS},
36 {"KEY_MINUS", TasButton::BUTTON_MINUS},
37 {"KEY_DLEFT", TasButton::BUTTON_LEFT},
38 {"KEY_DUP", TasButton::BUTTON_UP},
39 {"KEY_DRIGHT", TasButton::BUTTON_RIGHT},
40 {"KEY_DDOWN", TasButton::BUTTON_DOWN},
41 {"KEY_SL", TasButton::BUTTON_SL},
42 {"KEY_SR", TasButton::BUTTON_SR},
43 {"KEY_CAPTURE", TasButton::BUTTON_CAPTURE},
44 {"KEY_HOME", TasButton::BUTTON_HOME},
45 {"KEY_ZL", TasButton::TRIGGER_ZL},
46 {"KEY_ZR", TasButton::TRIGGER_ZR},
47};
48
49Tas::Tas(std::string input_engine_) : InputEngine(std::move(input_engine_)) {
50 for (size_t player_index = 0; player_index < PLAYER_NUMBER; player_index++) {
51 PadIdentifier identifier{
52 .guid = Common::UUID{},
53 .port = player_index,
54 .pad = 0,
55 };
56 PreSetController(identifier);
57 }
58 ClearInput();
59 if (!Settings::values.tas_enable) {
60 needs_reset = true;
61 return;
62 }
63 LoadTasFiles();
64}
65
66Tas::~Tas() {
67 Stop();
68}
69
70void Tas::LoadTasFiles() {
71 script_length = 0;
72 for (size_t i = 0; i < commands.size(); i++) {
73 LoadTasFile(i, 0);
74 if (commands[i].size() > script_length) {
75 script_length = commands[i].size();
76 }
77 }
78}
79
80void Tas::LoadTasFile(size_t player_index, size_t file_index) {
81 commands[player_index].clear();
82
83 std::string file = Common::FS::ReadStringFromFile(
84 Common::FS::GetYuzuPath(Common::FS::YuzuPath::TASDir) /
85 fmt::format("script{}-{}.txt", file_index, player_index + 1),
86 Common::FS::FileType::BinaryFile);
87 std::istringstream command_line(file);
88 std::string line;
89 int frame_no = 0;
90 while (std::getline(command_line, line, '\n')) {
91 if (line.empty()) {
92 continue;
93 }
94
95 std::vector<std::string> seg_list;
96 {
97 std::istringstream line_stream(line);
98 std::string segment;
99 while (std::getline(line_stream, segment, ' ')) {
100 seg_list.push_back(std::move(segment));
101 }
102 }
103
104 if (seg_list.size() < 4) {
105 continue;
106 }
107
108 const auto num_frames = std::stoi(seg_list[0]);
109 while (frame_no < num_frames) {
110 commands[player_index].emplace_back();
111 frame_no++;
112 }
113
114 TASCommand command = {
115 .buttons = ReadCommandButtons(seg_list[1]),
116 .l_axis = ReadCommandAxis(seg_list[2]),
117 .r_axis = ReadCommandAxis(seg_list[3]),
118 };
119 commands[player_index].push_back(command);
120 frame_no++;
121 }
122 LOG_INFO(Input, "TAS file loaded! {} frames", frame_no);
123}
124
125void Tas::WriteTasFile(std::u8string_view file_name) {
126 std::string output_text;
127 for (size_t frame = 0; frame < record_commands.size(); frame++) {
128 const TASCommand& line = record_commands[frame];
129 output_text += fmt::format("{} {} {} {}\n", frame, WriteCommandButtons(line.buttons),
130 WriteCommandAxis(line.l_axis), WriteCommandAxis(line.r_axis));
131 }
132
133 const auto tas_file_name = Common::FS::GetYuzuPath(Common::FS::YuzuPath::TASDir) / file_name;
134 const auto bytes_written =
135 Common::FS::WriteStringToFile(tas_file_name, Common::FS::FileType::TextFile, output_text);
136 if (bytes_written == output_text.size()) {
137 LOG_INFO(Input, "TAS file written to file!");
138 } else {
139 LOG_ERROR(Input, "Writing the TAS-file has failed! {} / {} bytes written", bytes_written,
140 output_text.size());
141 }
142}
143
144void Tas::RecordInput(u64 buttons, TasAnalog left_axis, TasAnalog right_axis) {
145 last_input = {
146 .buttons = buttons,
147 .l_axis = left_axis,
148 .r_axis = right_axis,
149 };
150}
151
152std::tuple<TasState, size_t, size_t> Tas::GetStatus() const {
153 TasState state;
154 if (is_recording) {
155 return {TasState::Recording, 0, record_commands.size()};
156 }
157
158 if (is_running) {
159 state = TasState::Running;
160 } else {
161 state = TasState::Stopped;
162 }
163
164 return {state, current_command, script_length};
165}
166
167void Tas::UpdateThread() {
168 if (!Settings::values.tas_enable) {
169 if (is_running) {
170 Stop();
171 }
172 return;
173 }
174
175 if (is_recording) {
176 record_commands.push_back(last_input);
177 }
178 if (needs_reset) {
179 current_command = 0;
180 needs_reset = false;
181 LoadTasFiles();
182 LOG_DEBUG(Input, "tas_reset done");
183 }
184
185 if (!is_running) {
186 ClearInput();
187 return;
188 }
189 if (current_command < script_length) {
190 LOG_DEBUG(Input, "Playing TAS {}/{}", current_command, script_length);
191 const size_t frame = current_command++;
192 for (size_t player_index = 0; player_index < commands.size(); player_index++) {
193 TASCommand command{};
194 if (frame < commands[player_index].size()) {
195 command = commands[player_index][frame];
196 }
197
198 PadIdentifier identifier{
199 .guid = Common::UUID{},
200 .port = player_index,
201 .pad = 0,
202 };
203 for (std::size_t i = 0; i < sizeof(command.buttons) * 8; ++i) {
204 const bool button_status = (command.buttons & (1LLU << i)) != 0;
205 const int button = static_cast<int>(i);
206 SetButton(identifier, button, button_status);
207 }
208 SetTasAxis(identifier, TasAxis::StickX, command.l_axis.x);
209 SetTasAxis(identifier, TasAxis::StickY, command.l_axis.y);
210 SetTasAxis(identifier, TasAxis::SubstickX, command.r_axis.x);
211 SetTasAxis(identifier, TasAxis::SubstickY, command.r_axis.y);
212 }
213 } else {
214 is_running = Settings::values.tas_loop.GetValue();
215 LoadTasFiles();
216 current_command = 0;
217 ClearInput();
218 }
219}
220
221void Tas::ClearInput() {
222 ResetButtonState();
223 ResetAnalogState();
224}
225
226TasAnalog Tas::ReadCommandAxis(const std::string& line) const {
227 std::vector<std::string> seg_list;
228 {
229 std::istringstream line_stream(line);
230 std::string segment;
231 while (std::getline(line_stream, segment, ';')) {
232 seg_list.push_back(std::move(segment));
233 }
234 }
235
236 const float x = std::stof(seg_list.at(0)) / 32767.0f;
237 const float y = std::stof(seg_list.at(1)) / 32767.0f;
238
239 return {x, y};
240}
241
242u64 Tas::ReadCommandButtons(const std::string& line) const {
243 std::istringstream button_text(line);
244 std::string button_line;
245 u64 buttons = 0;
246 while (std::getline(button_text, button_line, ';')) {
247 for (const auto& [text, tas_button] : text_to_tas_button) {
248 if (text == button_line) {
249 buttons |= static_cast<u64>(tas_button);
250 break;
251 }
252 }
253 }
254 return buttons;
255}
256
257std::string Tas::WriteCommandButtons(u64 buttons) const {
258 std::string returns;
259 for (const auto& [text_button, tas_button] : text_to_tas_button) {
260 if ((buttons & static_cast<u64>(tas_button)) != 0) {
261 returns += fmt::format("{};", text_button);
262 }
263 }
264 return returns.empty() ? "NONE" : returns;
265}
266
267std::string Tas::WriteCommandAxis(TasAnalog analog) const {
268 return fmt::format("{};{}", analog.x * 32767, analog.y * 32767);
269}
270
271void Tas::SetTasAxis(const PadIdentifier& identifier, TasAxis axis, f32 value) {
272 SetAxis(identifier, static_cast<int>(axis), value);
273}
274
275void Tas::StartStop() {
276 if (!Settings::values.tas_enable) {
277 return;
278 }
279 if (is_running) {
280 Stop();
281 } else {
282 is_running = true;
283 }
284}
285
286void Tas::Stop() {
287 is_running = false;
288}
289
290void Tas::Reset() {
291 if (!Settings::values.tas_enable) {
292 return;
293 }
294 needs_reset = true;
295}
296
297bool Tas::Record() {
298 if (!Settings::values.tas_enable) {
299 return true;
300 }
301 is_recording = !is_recording;
302 return is_recording;
303}
304
305void Tas::SaveRecording(bool overwrite_file) {
306 if (is_recording) {
307 return;
308 }
309 if (record_commands.empty()) {
310 return;
311 }
312 WriteTasFile(u8"record.txt");
313 if (overwrite_file) {
314 WriteTasFile(u8"script0-1.txt");
315 }
316 needs_reset = true;
317 record_commands.clear();
318}
319
320} // namespace InputCommon::TasInput
diff --git a/src/input_common/drivers/tas_input.h b/src/input_common/drivers/tas_input.h
new file mode 100644
index 000000000..4b4e6c417
--- /dev/null
+++ b/src/input_common/drivers/tas_input.h
@@ -0,0 +1,201 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <array>
8#include <string>
9#include <vector>
10
11#include "common/common_types.h"
12#include "input_common/input_engine.h"
13
14/*
15To play back TAS scripts on Yuzu, select the folder with scripts in the configuration menu below
16Tools -> Configure TAS. The file itself has normal text format and has to be called script0-1.txt
17for controller 1, script0-2.txt for controller 2 and so forth (with max. 8 players).
18
19A script file has the same format as TAS-nx uses, so final files will look like this:
20
211 KEY_B 0;0 0;0
226 KEY_ZL 0;0 0;0
2341 KEY_ZL;KEY_Y 0;0 0;0
2443 KEY_X;KEY_A 32767;0 0;0
2544 KEY_A 32767;0 0;0
2645 KEY_A 32767;0 0;0
2746 KEY_A 32767;0 0;0
2847 KEY_A 32767;0 0;0
29
30After placing the file at the correct location, it can be read into Yuzu with the (default) hotkey
31CTRL+F6 (refresh). In the bottom left corner, it will display the amount of frames the script file
32has. Playback can be started or stopped using CTRL+F5.
33
34However, for playback to actually work, the correct input device has to be selected: In the Controls
35menu, select TAS from the device list for the controller that the script should be played on.
36
37Recording a new script file is really simple: Just make sure that the proper device (not TAS) is
38connected on P1, and press CTRL+F7 to start recording. When done, just press the same keystroke
39again (CTRL+F7). The new script will be saved at the location previously selected, as the filename
40record.txt.
41
42For debugging purposes, the common controller debugger can be used (View -> Debugging -> Controller
43P1).
44*/
45
46namespace InputCommon::TasInput {
47
48constexpr size_t PLAYER_NUMBER = 10;
49
50enum class TasButton : u64 {
51 BUTTON_A = 1U << 0,
52 BUTTON_B = 1U << 1,
53 BUTTON_X = 1U << 2,
54 BUTTON_Y = 1U << 3,
55 STICK_L = 1U << 4,
56 STICK_R = 1U << 5,
57 TRIGGER_L = 1U << 6,
58 TRIGGER_R = 1U << 7,
59 TRIGGER_ZL = 1U << 8,
60 TRIGGER_ZR = 1U << 9,
61 BUTTON_PLUS = 1U << 10,
62 BUTTON_MINUS = 1U << 11,
63 BUTTON_LEFT = 1U << 12,
64 BUTTON_UP = 1U << 13,
65 BUTTON_RIGHT = 1U << 14,
66 BUTTON_DOWN = 1U << 15,
67 BUTTON_SL = 1U << 16,
68 BUTTON_SR = 1U << 17,
69 BUTTON_HOME = 1U << 18,
70 BUTTON_CAPTURE = 1U << 19,
71};
72
73struct TasAnalog {
74 float x{};
75 float y{};
76};
77
78enum class TasState {
79 Running,
80 Recording,
81 Stopped,
82};
83
84class Tas final : public InputEngine {
85public:
86 explicit Tas(std::string input_engine_);
87 ~Tas() override;
88
89 /**
90 * Changes the input status that will be stored in each frame
91 * @param buttons Bitfield with the status of the buttons
92 * @param left_axis Value of the left axis
93 * @param right_axis Value of the right axis
94 */
95 void RecordInput(u64 buttons, TasAnalog left_axis, TasAnalog right_axis);
96
97 // Main loop that records or executes input
98 void UpdateThread();
99
100 // Sets the flag to start or stop the TAS command execution and swaps controllers profiles
101 void StartStop();
102
103 // Stop the TAS and reverts any controller profile
104 void Stop();
105
106 // Sets the flag to reload the file and start from the beginning in the next update
107 void Reset();
108
109 /**
110 * Sets the flag to enable or disable recording of inputs
111 * @returns true if the current recording status is enabled
112 */
113 bool Record();
114
115 /**
116 * Saves contents of record_commands on a file
117 * @param overwrite_file Indicates if player 1 should be overwritten
118 */
119 void SaveRecording(bool overwrite_file);
120
121 /**
122 * Returns the current status values of TAS playback/recording
123 * @returns A Tuple of
124 * TasState indicating the current state out of Running ;
125 * Current playback progress ;
126 * Total length of script file currently loaded or being recorded
127 */
128 std::tuple<TasState, size_t, size_t> GetStatus() const;
129
130private:
131 enum class TasAxis : u8;
132
133 struct TASCommand {
134 u64 buttons{};
135 TasAnalog l_axis{};
136 TasAnalog r_axis{};
137 };
138
139 /// Loads TAS files from all players
140 void LoadTasFiles();
141
142 /**
143 * Loads TAS file from the specified player
144 * @param player_index Player number to save the script
145 * @param file_index Script number of the file
146 */
147 void LoadTasFile(size_t player_index, size_t file_index);
148
149 /**
150 * Writes a TAS file from the recorded commands
151 * @param file_name Name of the file to be written
152 */
153 void WriteTasFile(std::u8string_view file_name);
154
155 /**
156 * Parses a string containing the axis values. X and Y have a range from -32767 to 32767
157 * @param line String containing axis values with the following format "x;y"
158 * @returns A TAS analog object with axis values with range from -1.0 to 1.0
159 */
160 TasAnalog ReadCommandAxis(const std::string& line) const;
161
162 /**
163 * Parses a string containing the button values. Each button is represented by it's text format
164 * specified in text_to_tas_button array
165 * @param line string containing button name with the following format "a;b;c;d..."
166 * @returns A u64 with each bit representing the status of a button
167 */
168 u64 ReadCommandButtons(const std::string& line) const;
169
170 /**
171 * Reset state of all players
172 */
173 void ClearInput();
174
175 /**
176 * Converts an u64 containing the button status into the text equivalent
177 * @param buttons Bitfield with the status of the buttons
178 * @returns A string with the name of the buttons to be written to the file
179 */
180 std::string WriteCommandButtons(u64 buttons) const;
181
182 /**
183 * Converts an TAS analog object containing the axis status into the text equivalent
184 * @param analog Value of the axis
185 * @returns A string with the value of the axis to be written to the file
186 */
187 std::string WriteCommandAxis(TasAnalog analog) const;
188
189 /// Sets an axis for a particular pad to the given value.
190 void SetTasAxis(const PadIdentifier& identifier, TasAxis axis, f32 value);
191
192 size_t script_length{0};
193 bool is_recording{false};
194 bool is_running{false};
195 bool needs_reset{false};
196 std::array<std::vector<TASCommand>, PLAYER_NUMBER> commands{};
197 std::vector<TASCommand> record_commands{};
198 size_t current_command{0};
199 TASCommand last_input{}; // only used for recording
200};
201} // namespace InputCommon::TasInput
diff --git a/src/input_common/drivers/touch_screen.cpp b/src/input_common/drivers/touch_screen.cpp
new file mode 100644
index 000000000..880781825
--- /dev/null
+++ b/src/input_common/drivers/touch_screen.cpp
@@ -0,0 +1,53 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included
4
5#include "common/param_package.h"
6#include "input_common/drivers/touch_screen.h"
7
8namespace InputCommon {
9
10constexpr PadIdentifier identifier = {
11 .guid = Common::UUID{Common::INVALID_UUID},
12 .port = 0,
13 .pad = 0,
14};
15
16TouchScreen::TouchScreen(std::string input_engine_) : InputEngine(std::move(input_engine_)) {
17 PreSetController(identifier);
18}
19
20void TouchScreen::TouchMoved(float x, float y, std::size_t finger) {
21 if (finger >= 16) {
22 return;
23 }
24 TouchPressed(x, y, finger);
25}
26
27void TouchScreen::TouchPressed(float x, float y, std::size_t finger) {
28 if (finger >= 16) {
29 return;
30 }
31 SetButton(identifier, static_cast<int>(finger), true);
32 SetAxis(identifier, static_cast<int>(finger * 2), x);
33 SetAxis(identifier, static_cast<int>(finger * 2 + 1), y);
34}
35
36void TouchScreen::TouchReleased(std::size_t finger) {
37 if (finger >= 16) {
38 return;
39 }
40 SetButton(identifier, static_cast<int>(finger), false);
41 SetAxis(identifier, static_cast<int>(finger * 2), 0.0f);
42 SetAxis(identifier, static_cast<int>(finger * 2 + 1), 0.0f);
43}
44
45void TouchScreen::ReleaseAllTouch() {
46 for (int index = 0; index < 16; ++index) {
47 SetButton(identifier, index, false);
48 SetAxis(identifier, index * 2, 0.0f);
49 SetAxis(identifier, index * 2 + 1, 0.0f);
50 }
51}
52
53} // namespace InputCommon
diff --git a/src/input_common/drivers/touch_screen.h b/src/input_common/drivers/touch_screen.h
new file mode 100644
index 000000000..bf395c40b
--- /dev/null
+++ b/src/input_common/drivers/touch_screen.h
@@ -0,0 +1,44 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included
4
5#pragma once
6
7#include "input_common/input_engine.h"
8
9namespace InputCommon {
10
11/**
12 * A button device factory representing a keyboard. It receives keyboard events and forward them
13 * to all button devices it created.
14 */
15class TouchScreen final : public InputEngine {
16public:
17 explicit TouchScreen(std::string input_engine_);
18
19 /**
20 * Signals that mouse has moved.
21 * @param x the x-coordinate of the cursor
22 * @param y the y-coordinate of the cursor
23 * @param center_x the x-coordinate of the middle of the screen
24 * @param center_y the y-coordinate of the middle of the screen
25 */
26 void TouchMoved(float x, float y, std::size_t finger);
27
28 /**
29 * Sets the status of all buttons bound with the key to pressed
30 * @param key_code the code of the key to press
31 */
32 void TouchPressed(float x, float y, std::size_t finger);
33
34 /**
35 * Sets the status of all buttons bound with the key to released
36 * @param key_code the code of the key to release
37 */
38 void TouchReleased(std::size_t finger);
39
40 /// Resets all inputs to their initial value
41 void ReleaseAllTouch();
42};
43
44} // namespace InputCommon
diff --git a/src/input_common/drivers/udp_client.cpp b/src/input_common/drivers/udp_client.cpp
new file mode 100644
index 000000000..4ab991a7d
--- /dev/null
+++ b/src/input_common/drivers/udp_client.cpp
@@ -0,0 +1,591 @@
1// Copyright 2018 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <random>
6#include <boost/asio.hpp>
7#include <fmt/format.h>
8
9#include "common/logging/log.h"
10#include "common/param_package.h"
11#include "common/settings.h"
12#include "input_common/drivers/udp_client.h"
13#include "input_common/helpers/udp_protocol.h"
14
15using boost::asio::ip::udp;
16
17namespace InputCommon::CemuhookUDP {
18
19struct SocketCallback {
20 std::function<void(Response::Version)> version;
21 std::function<void(Response::PortInfo)> port_info;
22 std::function<void(Response::PadData)> pad_data;
23};
24
25class Socket {
26public:
27 using clock = std::chrono::system_clock;
28
29 explicit Socket(const std::string& host, u16 port, SocketCallback callback_)
30 : callback(std::move(callback_)), timer(io_service),
31 socket(io_service, udp::endpoint(udp::v4(), 0)), client_id(GenerateRandomClientId()) {
32 boost::system::error_code ec{};
33 auto ipv4 = boost::asio::ip::make_address_v4(host, ec);
34 if (ec.value() != boost::system::errc::success) {
35 LOG_ERROR(Input, "Invalid IPv4 address \"{}\" provided to socket", host);
36 ipv4 = boost::asio::ip::address_v4{};
37 }
38
39 send_endpoint = {udp::endpoint(ipv4, port)};
40 }
41
42 void Stop() {
43 io_service.stop();
44 }
45
46 void Loop() {
47 io_service.run();
48 }
49
50 void StartSend(const clock::time_point& from) {
51 timer.expires_at(from + std::chrono::seconds(3));
52 timer.async_wait([this](const boost::system::error_code& error) { HandleSend(error); });
53 }
54
55 void StartReceive() {
56 socket.async_receive_from(
57 boost::asio::buffer(receive_buffer), receive_endpoint,
58 [this](const boost::system::error_code& error, std::size_t bytes_transferred) {
59 HandleReceive(error, bytes_transferred);
60 });
61 }
62
63private:
64 u32 GenerateRandomClientId() const {
65 std::random_device device;
66 return device();
67 }
68
69 void HandleReceive(const boost::system::error_code&, std::size_t bytes_transferred) {
70 if (auto type = Response::Validate(receive_buffer.data(), bytes_transferred)) {
71 switch (*type) {
72 case Type::Version: {
73 Response::Version version;
74 std::memcpy(&version, &receive_buffer[sizeof(Header)], sizeof(Response::Version));
75 callback.version(std::move(version));
76 break;
77 }
78 case Type::PortInfo: {
79 Response::PortInfo port_info;
80 std::memcpy(&port_info, &receive_buffer[sizeof(Header)],
81 sizeof(Response::PortInfo));
82 callback.port_info(std::move(port_info));
83 break;
84 }
85 case Type::PadData: {
86 Response::PadData pad_data;
87 std::memcpy(&pad_data, &receive_buffer[sizeof(Header)], sizeof(Response::PadData));
88 callback.pad_data(std::move(pad_data));
89 break;
90 }
91 }
92 }
93 StartReceive();
94 }
95
96 void HandleSend(const boost::system::error_code&) {
97 boost::system::error_code _ignored{};
98 // Send a request for getting port info for the pad
99 const Request::PortInfo port_info{4, {0, 1, 2, 3}};
100 const auto port_message = Request::Create(port_info, client_id);
101 std::memcpy(&send_buffer1, &port_message, PORT_INFO_SIZE);
102 socket.send_to(boost::asio::buffer(send_buffer1), send_endpoint, {}, _ignored);
103
104 // Send a request for getting pad data for the pad
105 const Request::PadData pad_data{
106 Request::RegisterFlags::AllPads,
107 0,
108 EMPTY_MAC_ADDRESS,
109 };
110 const auto pad_message = Request::Create(pad_data, client_id);
111 std::memcpy(send_buffer2.data(), &pad_message, PAD_DATA_SIZE);
112 socket.send_to(boost::asio::buffer(send_buffer2), send_endpoint, {}, _ignored);
113 StartSend(timer.expiry());
114 }
115
116 SocketCallback callback;
117 boost::asio::io_service io_service;
118 boost::asio::basic_waitable_timer<clock> timer;
119 udp::socket socket;
120
121 const u32 client_id;
122
123 static constexpr std::size_t PORT_INFO_SIZE = sizeof(Message<Request::PortInfo>);
124 static constexpr std::size_t PAD_DATA_SIZE = sizeof(Message<Request::PadData>);
125 std::array<u8, PORT_INFO_SIZE> send_buffer1;
126 std::array<u8, PAD_DATA_SIZE> send_buffer2;
127 udp::endpoint send_endpoint;
128
129 std::array<u8, MAX_PACKET_SIZE> receive_buffer;
130 udp::endpoint receive_endpoint;
131};
132
133static void SocketLoop(Socket* socket) {
134 socket->StartReceive();
135 socket->StartSend(Socket::clock::now());
136 socket->Loop();
137}
138
139UDPClient::UDPClient(std::string input_engine_) : InputEngine(std::move(input_engine_)) {
140 LOG_INFO(Input, "Udp Initialization started");
141 ReloadSockets();
142}
143
144UDPClient::~UDPClient() {
145 Reset();
146}
147
148UDPClient::ClientConnection::ClientConnection() = default;
149
150UDPClient::ClientConnection::~ClientConnection() = default;
151
152void UDPClient::ReloadSockets() {
153 Reset();
154
155 std::stringstream servers_ss(Settings::values.udp_input_servers.GetValue());
156 std::string server_token;
157 std::size_t client = 0;
158 while (std::getline(servers_ss, server_token, ',')) {
159 if (client == MAX_UDP_CLIENTS) {
160 break;
161 }
162 std::stringstream server_ss(server_token);
163 std::string token;
164 std::getline(server_ss, token, ':');
165 std::string udp_input_address = token;
166 std::getline(server_ss, token, ':');
167 char* temp;
168 const u16 udp_input_port = static_cast<u16>(std::strtol(token.c_str(), &temp, 0));
169 if (*temp != '\0') {
170 LOG_ERROR(Input, "Port number is not valid {}", token);
171 continue;
172 }
173
174 const std::size_t client_number = GetClientNumber(udp_input_address, udp_input_port);
175 if (client_number != MAX_UDP_CLIENTS) {
176 LOG_ERROR(Input, "Duplicated UDP servers found");
177 continue;
178 }
179 StartCommunication(client++, udp_input_address, udp_input_port);
180 }
181}
182
183std::size_t UDPClient::GetClientNumber(std::string_view host, u16 port) const {
184 for (std::size_t client = 0; client < clients.size(); client++) {
185 if (clients[client].active == -1) {
186 continue;
187 }
188 if (clients[client].host == host && clients[client].port == port) {
189 return client;
190 }
191 }
192 return MAX_UDP_CLIENTS;
193}
194
195void UDPClient::OnVersion([[maybe_unused]] Response::Version data) {
196 LOG_TRACE(Input, "Version packet received: {}", data.version);
197}
198
199void UDPClient::OnPortInfo([[maybe_unused]] Response::PortInfo data) {
200 LOG_TRACE(Input, "PortInfo packet received: {}", data.model);
201}
202
203void UDPClient::OnPadData(Response::PadData data, std::size_t client) {
204 const std::size_t pad_index = (client * PADS_PER_CLIENT) + data.info.id;
205
206 if (pad_index >= pads.size()) {
207 LOG_ERROR(Input, "Invalid pad id {}", data.info.id);
208 return;
209 }
210
211 LOG_TRACE(Input, "PadData packet received");
212 if (data.packet_counter == pads[pad_index].packet_sequence) {
213 LOG_WARNING(
214 Input,
215 "PadData packet dropped because its stale info. Current count: {} Packet count: {}",
216 pads[pad_index].packet_sequence, data.packet_counter);
217 pads[pad_index].connected = false;
218 return;
219 }
220
221 clients[client].active = 1;
222 pads[pad_index].connected = true;
223 pads[pad_index].packet_sequence = data.packet_counter;
224
225 const auto now = std::chrono::steady_clock::now();
226 const auto time_difference = static_cast<u64>(
227 std::chrono::duration_cast<std::chrono::microseconds>(now - pads[pad_index].last_update)
228 .count());
229 pads[pad_index].last_update = now;
230
231 // Gyroscope values are not it the correct scale from better joy.
232 // Dividing by 312 allows us to make one full turn = 1 turn
233 // This must be a configurable valued called sensitivity
234 const float gyro_scale = 1.0f / 312.0f;
235
236 const BasicMotion motion{
237 .gyro_x = data.gyro.pitch * gyro_scale,
238 .gyro_y = data.gyro.roll * gyro_scale,
239 .gyro_z = -data.gyro.yaw * gyro_scale,
240 .accel_x = data.accel.x,
241 .accel_y = -data.accel.z,
242 .accel_z = data.accel.y,
243 .delta_timestamp = time_difference,
244 };
245 const PadIdentifier identifier = GetPadIdentifier(pad_index);
246 SetMotion(identifier, 0, motion);
247
248 for (std::size_t id = 0; id < data.touch.size(); ++id) {
249 const auto touch_pad = data.touch[id];
250 const auto touch_axis_x_id =
251 static_cast<int>(id == 0 ? PadAxes::Touch1X : PadAxes::Touch2X);
252 const auto touch_axis_y_id =
253 static_cast<int>(id == 0 ? PadAxes::Touch1Y : PadAxes::Touch2Y);
254 const auto touch_button_id =
255 static_cast<int>(id == 0 ? PadButton::Touch1 : PadButton::touch2);
256
257 // TODO: Use custom calibration per device
258 const Common::ParamPackage touch_param(Settings::values.touch_device.GetValue());
259 const u16 min_x = static_cast<u16>(touch_param.Get("min_x", 100));
260 const u16 min_y = static_cast<u16>(touch_param.Get("min_y", 50));
261 const u16 max_x = static_cast<u16>(touch_param.Get("max_x", 1800));
262 const u16 max_y = static_cast<u16>(touch_param.Get("max_y", 850));
263
264 const f32 x =
265 static_cast<f32>(std::clamp(static_cast<u16>(touch_pad.x), min_x, max_x) - min_x) /
266 static_cast<f32>(max_x - min_x);
267 const f32 y =
268 static_cast<f32>(std::clamp(static_cast<u16>(touch_pad.y), min_y, max_y) - min_y) /
269 static_cast<f32>(max_y - min_y);
270
271 if (touch_pad.is_active) {
272 SetAxis(identifier, touch_axis_x_id, x);
273 SetAxis(identifier, touch_axis_y_id, y);
274 SetButton(identifier, touch_button_id, true);
275 continue;
276 }
277 SetAxis(identifier, touch_axis_x_id, 0);
278 SetAxis(identifier, touch_axis_y_id, 0);
279 SetButton(identifier, touch_button_id, false);
280 }
281
282 SetAxis(identifier, static_cast<int>(PadAxes::LeftStickX),
283 (data.left_stick_x - 127.0f) / 127.0f);
284 SetAxis(identifier, static_cast<int>(PadAxes::LeftStickY),
285 (data.left_stick_y - 127.0f) / 127.0f);
286 SetAxis(identifier, static_cast<int>(PadAxes::RightStickX),
287 (data.right_stick_x - 127.0f) / 127.0f);
288 SetAxis(identifier, static_cast<int>(PadAxes::RightStickY),
289 (data.right_stick_y - 127.0f) / 127.0f);
290
291 static constexpr std::array<PadButton, 16> buttons{
292 PadButton::Share, PadButton::L3, PadButton::R3, PadButton::Options,
293 PadButton::Up, PadButton::Right, PadButton::Down, PadButton::Left,
294 PadButton::L2, PadButton::R2, PadButton::L1, PadButton::R1,
295 PadButton::Triangle, PadButton::Circle, PadButton::Cross, PadButton::Square};
296
297 for (std::size_t i = 0; i < buttons.size(); ++i) {
298 const bool button_status = (data.digital_button & (1U << i)) != 0;
299 const int button = static_cast<int>(buttons[i]);
300 SetButton(identifier, button, button_status);
301 }
302}
303
304void UDPClient::StartCommunication(std::size_t client, const std::string& host, u16 port) {
305 SocketCallback callback{[this](Response::Version version) { OnVersion(version); },
306 [this](Response::PortInfo info) { OnPortInfo(info); },
307 [this, client](Response::PadData data) { OnPadData(data, client); }};
308 LOG_INFO(Input, "Starting communication with UDP input server on {}:{}", host, port);
309 clients[client].uuid = GetHostUUID(host);
310 clients[client].host = host;
311 clients[client].port = port;
312 clients[client].active = 0;
313 clients[client].socket = std::make_unique<Socket>(host, port, callback);
314 clients[client].thread = std::thread{SocketLoop, clients[client].socket.get()};
315 for (std::size_t index = 0; index < PADS_PER_CLIENT; ++index) {
316 const PadIdentifier identifier = GetPadIdentifier(client * PADS_PER_CLIENT + index);
317 PreSetController(identifier);
318 }
319}
320
321const PadIdentifier UDPClient::GetPadIdentifier(std::size_t pad_index) const {
322 const std::size_t client = pad_index / PADS_PER_CLIENT;
323 return {
324 .guid = clients[client].uuid,
325 .port = static_cast<std::size_t>(clients[client].port),
326 .pad = pad_index,
327 };
328}
329
330const Common::UUID UDPClient::GetHostUUID(const std::string host) const {
331 const auto ip = boost::asio::ip::address_v4::from_string(host);
332 const auto hex_host = fmt::format("{:06x}", ip.to_ulong());
333 return Common::UUID{hex_host};
334}
335
336void UDPClient::Reset() {
337 for (auto& client : clients) {
338 if (client.thread.joinable()) {
339 client.active = -1;
340 client.socket->Stop();
341 client.thread.join();
342 }
343 }
344}
345
346std::vector<Common::ParamPackage> UDPClient::GetInputDevices() const {
347 std::vector<Common::ParamPackage> devices;
348 if (!Settings::values.enable_udp_controller) {
349 return devices;
350 }
351 for (std::size_t client = 0; client < clients.size(); client++) {
352 if (clients[client].active != 1) {
353 continue;
354 }
355 for (std::size_t index = 0; index < PADS_PER_CLIENT; ++index) {
356 const std::size_t pad_index = client * PADS_PER_CLIENT + index;
357 if (!pads[pad_index].connected) {
358 continue;
359 }
360 const auto pad_identifier = GetPadIdentifier(pad_index);
361 Common::ParamPackage identifier{};
362 identifier.Set("engine", GetEngineName());
363 identifier.Set("display", fmt::format("UDP Controller {}", pad_identifier.pad));
364 identifier.Set("guid", pad_identifier.guid.Format());
365 identifier.Set("port", static_cast<int>(pad_identifier.port));
366 identifier.Set("pad", static_cast<int>(pad_identifier.pad));
367 devices.emplace_back(identifier);
368 }
369 }
370 return devices;
371}
372
373ButtonMapping UDPClient::GetButtonMappingForDevice(const Common::ParamPackage& params) {
374 // This list excludes any button that can't be really mapped
375 static constexpr std::array<std::pair<Settings::NativeButton::Values, PadButton>, 18>
376 switch_to_dsu_button = {
377 std::pair{Settings::NativeButton::A, PadButton::Circle},
378 {Settings::NativeButton::B, PadButton::Cross},
379 {Settings::NativeButton::X, PadButton::Triangle},
380 {Settings::NativeButton::Y, PadButton::Square},
381 {Settings::NativeButton::Plus, PadButton::Options},
382 {Settings::NativeButton::Minus, PadButton::Share},
383 {Settings::NativeButton::DLeft, PadButton::Left},
384 {Settings::NativeButton::DUp, PadButton::Up},
385 {Settings::NativeButton::DRight, PadButton::Right},
386 {Settings::NativeButton::DDown, PadButton::Down},
387 {Settings::NativeButton::L, PadButton::L1},
388 {Settings::NativeButton::R, PadButton::R1},
389 {Settings::NativeButton::ZL, PadButton::L2},
390 {Settings::NativeButton::ZR, PadButton::R2},
391 {Settings::NativeButton::SL, PadButton::L2},
392 {Settings::NativeButton::SR, PadButton::R2},
393 {Settings::NativeButton::LStick, PadButton::L3},
394 {Settings::NativeButton::RStick, PadButton::R3},
395 };
396 if (!params.Has("guid") || !params.Has("port") || !params.Has("pad")) {
397 return {};
398 }
399
400 ButtonMapping mapping{};
401 for (const auto& [switch_button, dsu_button] : switch_to_dsu_button) {
402 Common::ParamPackage button_params{};
403 button_params.Set("engine", GetEngineName());
404 button_params.Set("guid", params.Get("guid", ""));
405 button_params.Set("port", params.Get("port", 0));
406 button_params.Set("pad", params.Get("pad", 0));
407 button_params.Set("button", static_cast<int>(dsu_button));
408 mapping.insert_or_assign(switch_button, std::move(button_params));
409 }
410
411 return mapping;
412}
413
414AnalogMapping UDPClient::GetAnalogMappingForDevice(const Common::ParamPackage& params) {
415 if (!params.Has("guid") || !params.Has("port") || !params.Has("pad")) {
416 return {};
417 }
418
419 AnalogMapping mapping = {};
420 Common::ParamPackage left_analog_params;
421 left_analog_params.Set("engine", GetEngineName());
422 left_analog_params.Set("guid", params.Get("guid", ""));
423 left_analog_params.Set("port", params.Get("port", 0));
424 left_analog_params.Set("pad", params.Get("pad", 0));
425 left_analog_params.Set("axis_x", static_cast<int>(PadAxes::LeftStickX));
426 left_analog_params.Set("axis_y", static_cast<int>(PadAxes::LeftStickY));
427 mapping.insert_or_assign(Settings::NativeAnalog::LStick, std::move(left_analog_params));
428 Common::ParamPackage right_analog_params;
429 right_analog_params.Set("engine", GetEngineName());
430 right_analog_params.Set("guid", params.Get("guid", ""));
431 right_analog_params.Set("port", params.Get("port", 0));
432 right_analog_params.Set("pad", params.Get("pad", 0));
433 right_analog_params.Set("axis_x", static_cast<int>(PadAxes::RightStickX));
434 right_analog_params.Set("axis_y", static_cast<int>(PadAxes::RightStickY));
435 mapping.insert_or_assign(Settings::NativeAnalog::RStick, std::move(right_analog_params));
436 return mapping;
437}
438
439MotionMapping UDPClient::GetMotionMappingForDevice(const Common::ParamPackage& params) {
440 if (!params.Has("guid") || !params.Has("port") || !params.Has("pad")) {
441 return {};
442 }
443
444 MotionMapping mapping = {};
445 Common::ParamPackage motion_params;
446 motion_params.Set("engine", GetEngineName());
447 motion_params.Set("guid", params.Get("guid", ""));
448 motion_params.Set("port", params.Get("port", 0));
449 motion_params.Set("pad", params.Get("pad", 0));
450 motion_params.Set("motion", 0);
451 mapping.insert_or_assign(Settings::NativeMotion::MotionLeft, std::move(motion_params));
452 mapping.insert_or_assign(Settings::NativeMotion::MotionRight, std::move(motion_params));
453 return mapping;
454}
455
456Common::Input::ButtonNames UDPClient::GetUIButtonName(const Common::ParamPackage& params) const {
457 PadButton button = static_cast<PadButton>(params.Get("button", 0));
458 switch (button) {
459 case PadButton::Left:
460 return Common::Input::ButtonNames::ButtonLeft;
461 case PadButton::Right:
462 return Common::Input::ButtonNames::ButtonRight;
463 case PadButton::Down:
464 return Common::Input::ButtonNames::ButtonDown;
465 case PadButton::Up:
466 return Common::Input::ButtonNames::ButtonUp;
467 case PadButton::L1:
468 return Common::Input::ButtonNames::L1;
469 case PadButton::L2:
470 return Common::Input::ButtonNames::L2;
471 case PadButton::L3:
472 return Common::Input::ButtonNames::L3;
473 case PadButton::R1:
474 return Common::Input::ButtonNames::R1;
475 case PadButton::R2:
476 return Common::Input::ButtonNames::R2;
477 case PadButton::R3:
478 return Common::Input::ButtonNames::R3;
479 case PadButton::Circle:
480 return Common::Input::ButtonNames::Circle;
481 case PadButton::Cross:
482 return Common::Input::ButtonNames::Cross;
483 case PadButton::Square:
484 return Common::Input::ButtonNames::Square;
485 case PadButton::Triangle:
486 return Common::Input::ButtonNames::Triangle;
487 case PadButton::Share:
488 return Common::Input::ButtonNames::Share;
489 case PadButton::Options:
490 return Common::Input::ButtonNames::Options;
491 default:
492 return Common::Input::ButtonNames::Undefined;
493 }
494}
495
496Common::Input::ButtonNames UDPClient::GetUIName(const Common::ParamPackage& params) const {
497 if (params.Has("button")) {
498 return GetUIButtonName(params);
499 }
500 if (params.Has("axis")) {
501 return Common::Input::ButtonNames::Value;
502 }
503 if (params.Has("motion")) {
504 return Common::Input::ButtonNames::Engine;
505 }
506
507 return Common::Input::ButtonNames::Invalid;
508}
509
510void TestCommunication(const std::string& host, u16 port,
511 const std::function<void()>& success_callback,
512 const std::function<void()>& failure_callback) {
513 std::thread([=] {
514 Common::Event success_event;
515 SocketCallback callback{
516 .version = [](Response::Version) {},
517 .port_info = [](Response::PortInfo) {},
518 .pad_data = [&](Response::PadData) { success_event.Set(); },
519 };
520 Socket socket{host, port, std::move(callback)};
521 std::thread worker_thread{SocketLoop, &socket};
522 const bool result =
523 success_event.WaitUntil(std::chrono::steady_clock::now() + std::chrono::seconds(10));
524 socket.Stop();
525 worker_thread.join();
526 if (result) {
527 success_callback();
528 } else {
529 failure_callback();
530 }
531 }).detach();
532}
533
534CalibrationConfigurationJob::CalibrationConfigurationJob(
535 const std::string& host, u16 port, std::function<void(Status)> status_callback,
536 std::function<void(u16, u16, u16, u16)> data_callback) {
537
538 std::thread([=, this] {
539 Status current_status{Status::Initialized};
540 SocketCallback callback{
541 [](Response::Version) {}, [](Response::PortInfo) {},
542 [&](Response::PadData data) {
543 static constexpr u16 CALIBRATION_THRESHOLD = 100;
544 static constexpr u16 MAX_VALUE = UINT16_MAX;
545
546 if (current_status == Status::Initialized) {
547 // Receiving data means the communication is ready now
548 current_status = Status::Ready;
549 status_callback(current_status);
550 }
551 const auto& touchpad_0 = data.touch[0];
552 if (touchpad_0.is_active == 0) {
553 return;
554 }
555 LOG_DEBUG(Input, "Current touch: {} {}", touchpad_0.x, touchpad_0.y);
556 const u16 min_x = std::min(MAX_VALUE, static_cast<u16>(touchpad_0.x));
557 const u16 min_y = std::min(MAX_VALUE, static_cast<u16>(touchpad_0.y));
558 if (current_status == Status::Ready) {
559 // First touch - min data (min_x/min_y)
560 current_status = Status::Stage1Completed;
561 status_callback(current_status);
562 }
563 if (touchpad_0.x - min_x > CALIBRATION_THRESHOLD &&
564 touchpad_0.y - min_y > CALIBRATION_THRESHOLD) {
565 // Set the current position as max value and finishes configuration
566 const u16 max_x = touchpad_0.x;
567 const u16 max_y = touchpad_0.y;
568 current_status = Status::Completed;
569 data_callback(min_x, min_y, max_x, max_y);
570 status_callback(current_status);
571
572 complete_event.Set();
573 }
574 }};
575 Socket socket{host, port, std::move(callback)};
576 std::thread worker_thread{SocketLoop, &socket};
577 complete_event.Wait();
578 socket.Stop();
579 worker_thread.join();
580 }).detach();
581}
582
583CalibrationConfigurationJob::~CalibrationConfigurationJob() {
584 Stop();
585}
586
587void CalibrationConfigurationJob::Stop() {
588 complete_event.Set();
589}
590
591} // namespace InputCommon::CemuhookUDP
diff --git a/src/input_common/udp/client.h b/src/input_common/drivers/udp_client.h
index 380f9bb76..1adc947c4 100644
--- a/src/input_common/udp/client.h
+++ b/src/input_common/drivers/udp_client.h
@@ -4,20 +4,11 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <functional>
8#include <memory>
9#include <mutex>
10#include <optional> 7#include <optional>
11#include <string> 8
12#include <thread>
13#include <tuple>
14#include "common/common_types.h" 9#include "common/common_types.h"
15#include "common/param_package.h"
16#include "common/thread.h" 10#include "common/thread.h"
17#include "common/threadsafe_queue.h" 11#include "input_common/input_engine.h"
18#include "common/vector_math.h"
19#include "core/frontend/input.h"
20#include "input_common/motion_input.h"
21 12
22namespace InputCommon::CemuhookUDP { 13namespace InputCommon::CemuhookUDP {
23 14
@@ -30,16 +21,6 @@ struct TouchPad;
30struct Version; 21struct Version;
31} // namespace Response 22} // namespace Response
32 23
33enum class PadMotion {
34 GyroX,
35 GyroY,
36 GyroZ,
37 AccX,
38 AccY,
39 AccZ,
40 Undefined,
41};
42
43enum class PadTouch { 24enum class PadTouch {
44 Click, 25 Click,
45 Undefined, 26 Undefined,
@@ -49,14 +30,10 @@ struct UDPPadStatus {
49 std::string host{"127.0.0.1"}; 30 std::string host{"127.0.0.1"};
50 u16 port{26760}; 31 u16 port{26760};
51 std::size_t pad_index{}; 32 std::size_t pad_index{};
52 PadMotion motion{PadMotion::Undefined};
53 f32 motion_value{0.0f};
54}; 33};
55 34
56struct DeviceStatus { 35struct DeviceStatus {
57 std::mutex update_mutex; 36 std::mutex update_mutex;
58 Input::MotionStatus motion_status;
59 std::tuple<float, float, bool> touch_status;
60 37
61 // calibration data for scaling the device's touch area to 3ds 38 // calibration data for scaling the device's touch area to 3ds
62 struct CalibrationData { 39 struct CalibrationData {
@@ -68,48 +45,85 @@ struct DeviceStatus {
68 std::optional<CalibrationData> touch_calibration; 45 std::optional<CalibrationData> touch_calibration;
69}; 46};
70 47
71class Client { 48/**
49 * A button device factory representing a keyboard. It receives keyboard events and forward them
50 * to all button devices it created.
51 */
52class UDPClient final : public InputEngine {
72public: 53public:
73 // Initialize the UDP client capture and read sequence 54 explicit UDPClient(std::string input_engine_);
74 Client(); 55 ~UDPClient() override;
75
76 // Close and release the client
77 ~Client();
78
79 // Used for polling
80 void BeginConfiguration();
81 void EndConfiguration();
82
83 std::vector<Common::ParamPackage> GetInputDevices() const;
84 56
85 bool DeviceConnected(std::size_t pad) const;
86 void ReloadSockets(); 57 void ReloadSockets();
87 58
88 Common::SPSCQueue<UDPPadStatus>& GetPadQueue(); 59 /// Used for automapping features
89 const Common::SPSCQueue<UDPPadStatus>& GetPadQueue() const; 60 std::vector<Common::ParamPackage> GetInputDevices() const override;
61 ButtonMapping GetButtonMappingForDevice(const Common::ParamPackage& params) override;
62 AnalogMapping GetAnalogMappingForDevice(const Common::ParamPackage& params) override;
63 MotionMapping GetMotionMappingForDevice(const Common::ParamPackage& params) override;
64 Common::Input::ButtonNames GetUIName(const Common::ParamPackage& params) const override;
90 65
91 DeviceStatus& GetPadState(const std::string& host, u16 port, std::size_t pad); 66private:
92 const DeviceStatus& GetPadState(const std::string& host, u16 port, std::size_t pad) const; 67 enum class PadButton {
68 Undefined = 0x0000,
69 Share = 0x0001,
70 L3 = 0x0002,
71 R3 = 0x0004,
72 Options = 0x0008,
73 Up = 0x0010,
74 Right = 0x0020,
75 Down = 0x0040,
76 Left = 0x0080,
77 L2 = 0x0100,
78 R2 = 0x0200,
79 L1 = 0x0400,
80 R1 = 0x0800,
81 Triangle = 0x1000,
82 Circle = 0x2000,
83 Cross = 0x4000,
84 Square = 0x8000,
85 Touch1 = 0x10000,
86 touch2 = 0x20000,
87 };
93 88
94 Input::TouchStatus& GetTouchState(); 89 enum class PadAxes : u8 {
95 const Input::TouchStatus& GetTouchState() const; 90 LeftStickX,
91 LeftStickY,
92 RightStickX,
93 RightStickY,
94 AnalogLeft,
95 AnalogDown,
96 AnalogRight,
97 AnalogUp,
98 AnalogSquare,
99 AnalogCross,
100 AnalogCircle,
101 AnalogTriangle,
102 AnalogR1,
103 AnalogL1,
104 AnalogR2,
105 AnalogL3,
106 AnalogR3,
107 Touch1X,
108 Touch1Y,
109 Touch2X,
110 Touch2Y,
111 Undefined,
112 };
96 113
97private:
98 struct PadData { 114 struct PadData {
99 std::size_t pad_index{}; 115 std::size_t pad_index{};
100 bool connected{}; 116 bool connected{};
101 DeviceStatus status; 117 DeviceStatus status;
102 u64 packet_sequence{}; 118 u64 packet_sequence{};
103 119
104 // Realtime values
105 // motion is initalized with PID values for drift correction on joycons
106 InputCommon::MotionInput motion{0.3f, 0.005f, 0.0f};
107 std::chrono::time_point<std::chrono::steady_clock> last_update; 120 std::chrono::time_point<std::chrono::steady_clock> last_update;
108 }; 121 };
109 122
110 struct ClientConnection { 123 struct ClientConnection {
111 ClientConnection(); 124 ClientConnection();
112 ~ClientConnection(); 125 ~ClientConnection();
126 Common::UUID uuid{"7F000001"};
113 std::string host{"127.0.0.1"}; 127 std::string host{"127.0.0.1"};
114 u16 port{26760}; 128 u16 port{26760};
115 s8 active{-1}; 129 s8 active{-1};
@@ -127,28 +141,16 @@ private:
127 void OnPortInfo(Response::PortInfo); 141 void OnPortInfo(Response::PortInfo);
128 void OnPadData(Response::PadData, std::size_t client); 142 void OnPadData(Response::PadData, std::size_t client);
129 void StartCommunication(std::size_t client, const std::string& host, u16 port); 143 void StartCommunication(std::size_t client, const std::string& host, u16 port);
130 void UpdateYuzuSettings(std::size_t client, std::size_t pad_index, 144 const PadIdentifier GetPadIdentifier(std::size_t pad_index) const;
131 const Common::Vec3<float>& acc, const Common::Vec3<float>& gyro); 145 const Common::UUID GetHostUUID(const std::string host) const;
132
133 // Returns an unused finger id, if there is no fingers available std::nullopt will be
134 // returned
135 std::optional<std::size_t> GetUnusedFingerID() const;
136
137 // Merges and updates all touch inputs into the touch_status array
138 void UpdateTouchInput(Response::TouchPad& touch_pad, std::size_t client, std::size_t id);
139 146
140 bool configuring = false; 147 Common::Input::ButtonNames GetUIButtonName(const Common::ParamPackage& params) const;
141 148
142 // Allocate clients for 8 udp servers 149 // Allocate clients for 8 udp servers
143 static constexpr std::size_t MAX_UDP_CLIENTS = 8; 150 static constexpr std::size_t MAX_UDP_CLIENTS = 8;
144 static constexpr std::size_t PADS_PER_CLIENT = 4; 151 static constexpr std::size_t PADS_PER_CLIENT = 4;
145 // Each client can have up 2 touch inputs
146 static constexpr std::size_t MAX_TOUCH_FINGERS = MAX_UDP_CLIENTS * 2;
147 std::array<PadData, MAX_UDP_CLIENTS * PADS_PER_CLIENT> pads{}; 152 std::array<PadData, MAX_UDP_CLIENTS * PADS_PER_CLIENT> pads{};
148 std::array<ClientConnection, MAX_UDP_CLIENTS> clients{}; 153 std::array<ClientConnection, MAX_UDP_CLIENTS> clients{};
149 Common::SPSCQueue<UDPPadStatus> pad_queue{};
150 Input::TouchStatus touch_status{};
151 std::array<std::size_t, MAX_TOUCH_FINGERS> finger_id{};
152}; 154};
153 155
154/// An async job allowing configuration of the touchpad calibration. 156/// An async job allowing configuration of the touchpad calibration.
diff --git a/src/input_common/gcadapter/gc_adapter.h b/src/input_common/gcadapter/gc_adapter.h
deleted file mode 100644
index e5de5e94f..000000000
--- a/src/input_common/gcadapter/gc_adapter.h
+++ /dev/null
@@ -1,168 +0,0 @@
1// Copyright 2014 Dolphin Emulator Project
2// Licensed under GPLv2+
3// Refer to the license.txt file included.
4
5#pragma once
6#include <algorithm>
7#include <functional>
8#include <mutex>
9#include <thread>
10#include <unordered_map>
11#include "common/common_types.h"
12#include "common/threadsafe_queue.h"
13#include "input_common/main.h"
14
15struct libusb_context;
16struct libusb_device;
17struct libusb_device_handle;
18
19namespace GCAdapter {
20
21enum class PadButton {
22 Undefined = 0x0000,
23 ButtonLeft = 0x0001,
24 ButtonRight = 0x0002,
25 ButtonDown = 0x0004,
26 ButtonUp = 0x0008,
27 TriggerZ = 0x0010,
28 TriggerR = 0x0020,
29 TriggerL = 0x0040,
30 ButtonA = 0x0100,
31 ButtonB = 0x0200,
32 ButtonX = 0x0400,
33 ButtonY = 0x0800,
34 ButtonStart = 0x1000,
35 // Below is for compatibility with "AxisButton" type
36 Stick = 0x2000,
37};
38
39enum class PadAxes : u8 {
40 StickX,
41 StickY,
42 SubstickX,
43 SubstickY,
44 TriggerLeft,
45 TriggerRight,
46 Undefined,
47};
48
49enum class ControllerTypes {
50 None,
51 Wired,
52 Wireless,
53};
54
55struct GCPadStatus {
56 std::size_t port{};
57
58 PadButton button{PadButton::Undefined}; // Or-ed PAD_BUTTON_* and PAD_TRIGGER_* bits
59
60 PadAxes axis{PadAxes::Undefined};
61 s16 axis_value{};
62 u8 axis_threshold{50};
63};
64
65struct GCController {
66 ControllerTypes type{};
67 bool enable_vibration{};
68 u8 rumble_amplitude{};
69 u16 buttons{};
70 PadButton last_button{};
71 std::array<s16, 6> axis_values{};
72 std::array<u8, 6> axis_origin{};
73 u8 reset_origin_counter{};
74};
75
76class Adapter {
77public:
78 Adapter();
79 ~Adapter();
80
81 /// Request a vibration for a controller
82 bool RumblePlay(std::size_t port, u8 amplitude);
83
84 /// Used for polling
85 void BeginConfiguration();
86 void EndConfiguration();
87
88 Common::SPSCQueue<GCPadStatus>& GetPadQueue();
89 const Common::SPSCQueue<GCPadStatus>& GetPadQueue() const;
90
91 GCController& GetPadState(std::size_t port);
92 const GCController& GetPadState(std::size_t port) const;
93
94 /// Returns true if there is a device connected to port
95 bool DeviceConnected(std::size_t port) const;
96
97 /// Used for automapping features
98 std::vector<Common::ParamPackage> GetInputDevices() const;
99 InputCommon::ButtonMapping GetButtonMappingForDevice(const Common::ParamPackage& params) const;
100 InputCommon::AnalogMapping GetAnalogMappingForDevice(const Common::ParamPackage& params) const;
101
102private:
103 using AdapterPayload = std::array<u8, 37>;
104
105 void UpdatePadType(std::size_t port, ControllerTypes pad_type);
106 void UpdateControllers(const AdapterPayload& adapter_payload);
107 void UpdateYuzuSettings(std::size_t port);
108 void UpdateStateButtons(std::size_t port, u8 b1, u8 b2);
109 void UpdateStateAxes(std::size_t port, const AdapterPayload& adapter_payload);
110 void UpdateVibrations();
111
112 void AdapterInputThread();
113
114 void AdapterScanThread();
115
116 bool IsPayloadCorrect(const AdapterPayload& adapter_payload, s32 payload_size);
117
118 // Updates vibration state of all controllers
119 void SendVibrations();
120
121 /// For use in initialization, querying devices to find the adapter
122 void Setup();
123
124 /// Resets status of all GC controller devices to a disconnected state
125 void ResetDevices();
126
127 /// Resets status of device connected to a disconnected state
128 void ResetDevice(std::size_t port);
129
130 /// Returns true if we successfully gain access to GC Adapter
131 bool CheckDeviceAccess();
132
133 /// Captures GC Adapter endpoint address
134 /// Returns true if the endpoint was set correctly
135 bool GetGCEndpoint(libusb_device* device);
136
137 /// For shutting down, clear all data, join all threads, release usb
138 void Reset();
139
140 // Join all threads
141 void JoinThreads();
142
143 // Release usb handles
144 void ClearLibusbHandle();
145
146 libusb_device_handle* usb_adapter_handle = nullptr;
147 std::array<GCController, 4> pads;
148 Common::SPSCQueue<GCPadStatus> pad_queue;
149
150 std::thread adapter_input_thread;
151 std::thread adapter_scan_thread;
152 bool adapter_input_thread_running;
153 bool adapter_scan_thread_running;
154 bool restart_scan_thread;
155
156 libusb_context* libusb_ctx;
157
158 u8 input_endpoint{0};
159 u8 output_endpoint{0};
160 u8 input_error_counter{0};
161 u8 output_error_counter{0};
162 int vibration_counter{0};
163
164 bool configuring{false};
165 bool rumble_enabled{true};
166 bool vibration_changed{true};
167};
168} // namespace GCAdapter
diff --git a/src/input_common/gcadapter/gc_poller.cpp b/src/input_common/gcadapter/gc_poller.cpp
deleted file mode 100644
index 1b6ded8d6..000000000
--- a/src/input_common/gcadapter/gc_poller.cpp
+++ /dev/null
@@ -1,356 +0,0 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <atomic>
6#include <list>
7#include <mutex>
8#include <utility>
9#include "common/assert.h"
10#include "common/threadsafe_queue.h"
11#include "input_common/gcadapter/gc_adapter.h"
12#include "input_common/gcadapter/gc_poller.h"
13
14namespace InputCommon {
15
16class GCButton final : public Input::ButtonDevice {
17public:
18 explicit GCButton(u32 port_, s32 button_, const GCAdapter::Adapter* adapter)
19 : port(port_), button(button_), gcadapter(adapter) {}
20
21 ~GCButton() override;
22
23 bool GetStatus() const override {
24 if (gcadapter->DeviceConnected(port)) {
25 return (gcadapter->GetPadState(port).buttons & button) != 0;
26 }
27 return false;
28 }
29
30private:
31 const u32 port;
32 const s32 button;
33 const GCAdapter::Adapter* gcadapter;
34};
35
36class GCAxisButton final : public Input::ButtonDevice {
37public:
38 explicit GCAxisButton(u32 port_, u32 axis_, float threshold_, bool trigger_if_greater_,
39 const GCAdapter::Adapter* adapter)
40 : port(port_), axis(axis_), threshold(threshold_), trigger_if_greater(trigger_if_greater_),
41 gcadapter(adapter) {}
42
43 bool GetStatus() const override {
44 if (gcadapter->DeviceConnected(port)) {
45 const float current_axis_value = gcadapter->GetPadState(port).axis_values.at(axis);
46 const float axis_value = current_axis_value / 128.0f;
47 if (trigger_if_greater) {
48 // TODO: Might be worthwile to set a slider for the trigger threshold. It is
49 // currently always set to 0.5 in configure_input_player.cpp ZL/ZR HandleClick
50 return axis_value > threshold;
51 }
52 return axis_value < -threshold;
53 }
54 return false;
55 }
56
57private:
58 const u32 port;
59 const u32 axis;
60 float threshold;
61 bool trigger_if_greater;
62 const GCAdapter::Adapter* gcadapter;
63};
64
65GCButtonFactory::GCButtonFactory(std::shared_ptr<GCAdapter::Adapter> adapter_)
66 : adapter(std::move(adapter_)) {}
67
68GCButton::~GCButton() = default;
69
70std::unique_ptr<Input::ButtonDevice> GCButtonFactory::Create(const Common::ParamPackage& params) {
71 const auto button_id = params.Get("button", 0);
72 const auto port = static_cast<u32>(params.Get("port", 0));
73
74 constexpr s32 PAD_STICK_ID = static_cast<s32>(GCAdapter::PadButton::Stick);
75
76 // button is not an axis/stick button
77 if (button_id != PAD_STICK_ID) {
78 return std::make_unique<GCButton>(port, button_id, adapter.get());
79 }
80
81 // For Axis buttons, used by the binary sticks.
82 if (button_id == PAD_STICK_ID) {
83 const int axis = params.Get("axis", 0);
84 const float threshold = params.Get("threshold", 0.25f);
85 const std::string direction_name = params.Get("direction", "");
86 bool trigger_if_greater;
87 if (direction_name == "+") {
88 trigger_if_greater = true;
89 } else if (direction_name == "-") {
90 trigger_if_greater = false;
91 } else {
92 trigger_if_greater = true;
93 LOG_ERROR(Input, "Unknown direction {}", direction_name);
94 }
95 return std::make_unique<GCAxisButton>(port, axis, threshold, trigger_if_greater,
96 adapter.get());
97 }
98
99 return nullptr;
100}
101
102Common::ParamPackage GCButtonFactory::GetNextInput() const {
103 Common::ParamPackage params;
104 GCAdapter::GCPadStatus pad;
105 auto& queue = adapter->GetPadQueue();
106 while (queue.Pop(pad)) {
107 // This while loop will break on the earliest detected button
108 params.Set("engine", "gcpad");
109 params.Set("port", static_cast<s32>(pad.port));
110 if (pad.button != GCAdapter::PadButton::Undefined) {
111 params.Set("button", static_cast<u16>(pad.button));
112 }
113
114 // For Axis button implementation
115 if (pad.axis != GCAdapter::PadAxes::Undefined) {
116 params.Set("axis", static_cast<u8>(pad.axis));
117 params.Set("button", static_cast<u16>(GCAdapter::PadButton::Stick));
118 params.Set("threshold", "0.25");
119 if (pad.axis_value > 0) {
120 params.Set("direction", "+");
121 } else {
122 params.Set("direction", "-");
123 }
124 break;
125 }
126 }
127 return params;
128}
129
130void GCButtonFactory::BeginConfiguration() {
131 polling = true;
132 adapter->BeginConfiguration();
133}
134
135void GCButtonFactory::EndConfiguration() {
136 polling = false;
137 adapter->EndConfiguration();
138}
139
140class GCAnalog final : public Input::AnalogDevice {
141public:
142 explicit GCAnalog(u32 port_, u32 axis_x_, u32 axis_y_, bool invert_x_, bool invert_y_,
143 float deadzone_, float range_, const GCAdapter::Adapter* adapter)
144 : port(port_), axis_x(axis_x_), axis_y(axis_y_), invert_x(invert_x_), invert_y(invert_y_),
145 deadzone(deadzone_), range(range_), gcadapter(adapter) {}
146
147 float GetAxis(u32 axis) const {
148 if (gcadapter->DeviceConnected(port)) {
149 std::lock_guard lock{mutex};
150 const auto axis_value =
151 static_cast<float>(gcadapter->GetPadState(port).axis_values.at(axis));
152 return (axis_value) / (100.0f * range);
153 }
154 return 0.0f;
155 }
156
157 std::pair<float, float> GetAnalog(u32 analog_axis_x, u32 analog_axis_y) const {
158 float x = GetAxis(analog_axis_x);
159 float y = GetAxis(analog_axis_y);
160 if (invert_x) {
161 x = -x;
162 }
163 if (invert_y) {
164 y = -y;
165 }
166 // Make sure the coordinates are in the unit circle,
167 // otherwise normalize it.
168 float r = x * x + y * y;
169 if (r > 1.0f) {
170 r = std::sqrt(r);
171 x /= r;
172 y /= r;
173 }
174
175 return {x, y};
176 }
177
178 std::tuple<float, float> GetStatus() const override {
179 const auto [x, y] = GetAnalog(axis_x, axis_y);
180 const float r = std::sqrt((x * x) + (y * y));
181 if (r > deadzone) {
182 return {x / r * (r - deadzone) / (1 - deadzone),
183 y / r * (r - deadzone) / (1 - deadzone)};
184 }
185 return {0.0f, 0.0f};
186 }
187
188 std::tuple<float, float> GetRawStatus() const override {
189 const float x = GetAxis(axis_x);
190 const float y = GetAxis(axis_y);
191 return {x, y};
192 }
193
194 Input::AnalogProperties GetAnalogProperties() const override {
195 return {deadzone, range, 0.5f};
196 }
197
198 bool GetAnalogDirectionStatus(Input::AnalogDirection direction) const override {
199 const auto [x, y] = GetStatus();
200 const float directional_deadzone = 0.5f;
201 switch (direction) {
202 case Input::AnalogDirection::RIGHT:
203 return x > directional_deadzone;
204 case Input::AnalogDirection::LEFT:
205 return x < -directional_deadzone;
206 case Input::AnalogDirection::UP:
207 return y > directional_deadzone;
208 case Input::AnalogDirection::DOWN:
209 return y < -directional_deadzone;
210 }
211 return false;
212 }
213
214private:
215 const u32 port;
216 const u32 axis_x;
217 const u32 axis_y;
218 const bool invert_x;
219 const bool invert_y;
220 const float deadzone;
221 const float range;
222 const GCAdapter::Adapter* gcadapter;
223 mutable std::mutex mutex;
224};
225
226/// An analog device factory that creates analog devices from GC Adapter
227GCAnalogFactory::GCAnalogFactory(std::shared_ptr<GCAdapter::Adapter> adapter_)
228 : adapter(std::move(adapter_)) {}
229
230/**
231 * Creates analog device from joystick axes
232 * @param params contains parameters for creating the device:
233 * - "port": the nth gcpad on the adapter
234 * - "axis_x": the index of the axis to be bind as x-axis
235 * - "axis_y": the index of the axis to be bind as y-axis
236 */
237std::unique_ptr<Input::AnalogDevice> GCAnalogFactory::Create(const Common::ParamPackage& params) {
238 const auto port = static_cast<u32>(params.Get("port", 0));
239 const auto axis_x = static_cast<u32>(params.Get("axis_x", 0));
240 const auto axis_y = static_cast<u32>(params.Get("axis_y", 1));
241 const auto deadzone = std::clamp(params.Get("deadzone", 0.0f), 0.0f, 1.0f);
242 const auto range = std::clamp(params.Get("range", 1.0f), 0.50f, 1.50f);
243 const std::string invert_x_value = params.Get("invert_x", "+");
244 const std::string invert_y_value = params.Get("invert_y", "+");
245 const bool invert_x = invert_x_value == "-";
246 const bool invert_y = invert_y_value == "-";
247
248 return std::make_unique<GCAnalog>(port, axis_x, axis_y, invert_x, invert_y, deadzone, range,
249 adapter.get());
250}
251
252void GCAnalogFactory::BeginConfiguration() {
253 polling = true;
254 adapter->BeginConfiguration();
255}
256
257void GCAnalogFactory::EndConfiguration() {
258 polling = false;
259 adapter->EndConfiguration();
260}
261
262Common::ParamPackage GCAnalogFactory::GetNextInput() {
263 GCAdapter::GCPadStatus pad;
264 Common::ParamPackage params;
265 auto& queue = adapter->GetPadQueue();
266 while (queue.Pop(pad)) {
267 if (pad.button != GCAdapter::PadButton::Undefined) {
268 params.Set("engine", "gcpad");
269 params.Set("port", static_cast<s32>(pad.port));
270 params.Set("button", static_cast<u16>(pad.button));
271 return params;
272 }
273 if (pad.axis == GCAdapter::PadAxes::Undefined ||
274 std::abs(static_cast<float>(pad.axis_value) / 128.0f) < 0.1f) {
275 continue;
276 }
277 // An analog device needs two axes, so we need to store the axis for later and wait for
278 // a second input event. The axes also must be from the same joystick.
279 const u8 axis = static_cast<u8>(pad.axis);
280 if (axis == 0 || axis == 1) {
281 analog_x_axis = 0;
282 analog_y_axis = 1;
283 controller_number = static_cast<s32>(pad.port);
284 break;
285 }
286 if (axis == 2 || axis == 3) {
287 analog_x_axis = 2;
288 analog_y_axis = 3;
289 controller_number = static_cast<s32>(pad.port);
290 break;
291 }
292
293 if (analog_x_axis == -1) {
294 analog_x_axis = axis;
295 controller_number = static_cast<s32>(pad.port);
296 } else if (analog_y_axis == -1 && analog_x_axis != axis &&
297 controller_number == static_cast<s32>(pad.port)) {
298 analog_y_axis = axis;
299 break;
300 }
301 }
302 if (analog_x_axis != -1 && analog_y_axis != -1) {
303 params.Set("engine", "gcpad");
304 params.Set("port", controller_number);
305 params.Set("axis_x", analog_x_axis);
306 params.Set("axis_y", analog_y_axis);
307 params.Set("invert_x", "+");
308 params.Set("invert_y", "+");
309 analog_x_axis = -1;
310 analog_y_axis = -1;
311 controller_number = -1;
312 return params;
313 }
314 return params;
315}
316
317class GCVibration final : public Input::VibrationDevice {
318public:
319 explicit GCVibration(u32 port_, GCAdapter::Adapter* adapter)
320 : port(port_), gcadapter(adapter) {}
321
322 u8 GetStatus() const override {
323 return gcadapter->RumblePlay(port, 0);
324 }
325
326 bool SetRumblePlay(f32 amp_low, [[maybe_unused]] f32 freq_low, f32 amp_high,
327 [[maybe_unused]] f32 freq_high) const override {
328 const auto mean_amplitude = (amp_low + amp_high) * 0.5f;
329 const auto processed_amplitude =
330 static_cast<u8>((mean_amplitude + std::pow(mean_amplitude, 0.3f)) * 0.5f * 0x8);
331
332 return gcadapter->RumblePlay(port, processed_amplitude);
333 }
334
335private:
336 const u32 port;
337 GCAdapter::Adapter* gcadapter;
338};
339
340/// An vibration device factory that creates vibration devices from GC Adapter
341GCVibrationFactory::GCVibrationFactory(std::shared_ptr<GCAdapter::Adapter> adapter_)
342 : adapter(std::move(adapter_)) {}
343
344/**
345 * Creates a vibration device from a joystick
346 * @param params contains parameters for creating the device:
347 * - "port": the nth gcpad on the adapter
348 */
349std::unique_ptr<Input::VibrationDevice> GCVibrationFactory::Create(
350 const Common::ParamPackage& params) {
351 const auto port = static_cast<u32>(params.Get("port", 0));
352
353 return std::make_unique<GCVibration>(port, adapter.get());
354}
355
356} // namespace InputCommon
diff --git a/src/input_common/gcadapter/gc_poller.h b/src/input_common/gcadapter/gc_poller.h
deleted file mode 100644
index d1271e3ea..000000000
--- a/src/input_common/gcadapter/gc_poller.h
+++ /dev/null
@@ -1,78 +0,0 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <memory>
8#include "core/frontend/input.h"
9#include "input_common/gcadapter/gc_adapter.h"
10
11namespace InputCommon {
12
13/**
14 * A button device factory representing a gcpad. It receives gcpad events and forward them
15 * to all button devices it created.
16 */
17class GCButtonFactory final : public Input::Factory<Input::ButtonDevice> {
18public:
19 explicit GCButtonFactory(std::shared_ptr<GCAdapter::Adapter> adapter_);
20
21 /**
22 * Creates a button device from a button press
23 * @param params contains parameters for creating the device:
24 * - "code": the code of the key to bind with the button
25 */
26 std::unique_ptr<Input::ButtonDevice> Create(const Common::ParamPackage& params) override;
27
28 Common::ParamPackage GetNextInput() const;
29
30 /// For device input configuration/polling
31 void BeginConfiguration();
32 void EndConfiguration();
33
34 bool IsPolling() const {
35 return polling;
36 }
37
38private:
39 std::shared_ptr<GCAdapter::Adapter> adapter;
40 bool polling = false;
41};
42
43/// An analog device factory that creates analog devices from GC Adapter
44class GCAnalogFactory final : public Input::Factory<Input::AnalogDevice> {
45public:
46 explicit GCAnalogFactory(std::shared_ptr<GCAdapter::Adapter> adapter_);
47
48 std::unique_ptr<Input::AnalogDevice> Create(const Common::ParamPackage& params) override;
49 Common::ParamPackage GetNextInput();
50
51 /// For device input configuration/polling
52 void BeginConfiguration();
53 void EndConfiguration();
54
55 bool IsPolling() const {
56 return polling;
57 }
58
59private:
60 std::shared_ptr<GCAdapter::Adapter> adapter;
61 int analog_x_axis = -1;
62 int analog_y_axis = -1;
63 int controller_number = -1;
64 bool polling = false;
65};
66
67/// A vibration device factory creates vibration devices from GC Adapter
68class GCVibrationFactory final : public Input::Factory<Input::VibrationDevice> {
69public:
70 explicit GCVibrationFactory(std::shared_ptr<GCAdapter::Adapter> adapter_);
71
72 std::unique_ptr<Input::VibrationDevice> Create(const Common::ParamPackage& params) override;
73
74private:
75 std::shared_ptr<GCAdapter::Adapter> adapter;
76};
77
78} // namespace InputCommon
diff --git a/src/input_common/helpers/stick_from_buttons.cpp b/src/input_common/helpers/stick_from_buttons.cpp
new file mode 100644
index 000000000..e23394f5f
--- /dev/null
+++ b/src/input_common/helpers/stick_from_buttons.cpp
@@ -0,0 +1,317 @@
1// Copyright 2017 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <chrono>
6#include <cmath>
7#include "common/math_util.h"
8#include "common/settings.h"
9#include "input_common/helpers/stick_from_buttons.h"
10
11namespace InputCommon {
12
13class Stick final : public Common::Input::InputDevice {
14public:
15 using Button = std::unique_ptr<Common::Input::InputDevice>;
16
17 Stick(Button up_, Button down_, Button left_, Button right_, Button modifier_,
18 float modifier_scale_, float modifier_angle_)
19 : up(std::move(up_)), down(std::move(down_)), left(std::move(left_)),
20 right(std::move(right_)), modifier(std::move(modifier_)), modifier_scale(modifier_scale_),
21 modifier_angle(modifier_angle_) {
22 up->SetCallback({
23 .on_change =
24 [this](const Common::Input::CallbackStatus& callback_) {
25 UpdateUpButtonStatus(callback_);
26 },
27 });
28 down->SetCallback({
29 .on_change =
30 [this](const Common::Input::CallbackStatus& callback_) {
31 UpdateDownButtonStatus(callback_);
32 },
33 });
34 left->SetCallback({
35 .on_change =
36 [this](const Common::Input::CallbackStatus& callback_) {
37 UpdateLeftButtonStatus(callback_);
38 },
39 });
40 right->SetCallback({
41 .on_change =
42 [this](const Common::Input::CallbackStatus& callback_) {
43 UpdateRightButtonStatus(callback_);
44 },
45 });
46 modifier->SetCallback({
47 .on_change =
48 [this](const Common::Input::CallbackStatus& callback_) {
49 UpdateModButtonStatus(callback_);
50 },
51 });
52 last_x_axis_value = 0.0f;
53 last_y_axis_value = 0.0f;
54 }
55
56 bool IsAngleGreater(float old_angle, float new_angle) const {
57 constexpr float TAU = Common::PI * 2.0f;
58 // Use wider angle to ease the transition.
59 constexpr float aperture = TAU * 0.15f;
60 const float top_limit = new_angle + aperture;
61 return (old_angle > new_angle && old_angle <= top_limit) ||
62 (old_angle + TAU > new_angle && old_angle + TAU <= top_limit);
63 }
64
65 bool IsAngleSmaller(float old_angle, float new_angle) const {
66 constexpr float TAU = Common::PI * 2.0f;
67 // Use wider angle to ease the transition.
68 constexpr float aperture = TAU * 0.15f;
69 const float bottom_limit = new_angle - aperture;
70 return (old_angle >= bottom_limit && old_angle < new_angle) ||
71 (old_angle - TAU >= bottom_limit && old_angle - TAU < new_angle);
72 }
73
74 float GetAngle(std::chrono::time_point<std::chrono::steady_clock> now) const {
75 constexpr float TAU = Common::PI * 2.0f;
76 float new_angle = angle;
77
78 auto time_difference = static_cast<float>(
79 std::chrono::duration_cast<std::chrono::microseconds>(now - last_update).count());
80 time_difference /= 1000.0f * 1000.0f;
81 if (time_difference > 0.5f) {
82 time_difference = 0.5f;
83 }
84
85 if (IsAngleGreater(new_angle, goal_angle)) {
86 new_angle -= modifier_angle * time_difference;
87 if (new_angle < 0) {
88 new_angle += TAU;
89 }
90 if (!IsAngleGreater(new_angle, goal_angle)) {
91 return goal_angle;
92 }
93 } else if (IsAngleSmaller(new_angle, goal_angle)) {
94 new_angle += modifier_angle * time_difference;
95 if (new_angle >= TAU) {
96 new_angle -= TAU;
97 }
98 if (!IsAngleSmaller(new_angle, goal_angle)) {
99 return goal_angle;
100 }
101 } else {
102 return goal_angle;
103 }
104 return new_angle;
105 }
106
107 void SetGoalAngle(bool r, bool l, bool u, bool d) {
108 // Move to the right
109 if (r && !u && !d) {
110 goal_angle = 0.0f;
111 }
112
113 // Move to the upper right
114 if (r && u && !d) {
115 goal_angle = Common::PI * 0.25f;
116 }
117
118 // Move up
119 if (u && !l && !r) {
120 goal_angle = Common::PI * 0.5f;
121 }
122
123 // Move to the upper left
124 if (l && u && !d) {
125 goal_angle = Common::PI * 0.75f;
126 }
127
128 // Move to the left
129 if (l && !u && !d) {
130 goal_angle = Common::PI;
131 }
132
133 // Move to the bottom left
134 if (l && !u && d) {
135 goal_angle = Common::PI * 1.25f;
136 }
137
138 // Move down
139 if (d && !l && !r) {
140 goal_angle = Common::PI * 1.5f;
141 }
142
143 // Move to the bottom right
144 if (r && !u && d) {
145 goal_angle = Common::PI * 1.75f;
146 }
147 }
148
149 void UpdateUpButtonStatus(const Common::Input::CallbackStatus& button_callback) {
150 up_status = button_callback.button_status.value;
151 UpdateStatus();
152 }
153
154 void UpdateDownButtonStatus(const Common::Input::CallbackStatus& button_callback) {
155 down_status = button_callback.button_status.value;
156 UpdateStatus();
157 }
158
159 void UpdateLeftButtonStatus(const Common::Input::CallbackStatus& button_callback) {
160 left_status = button_callback.button_status.value;
161 UpdateStatus();
162 }
163
164 void UpdateRightButtonStatus(const Common::Input::CallbackStatus& button_callback) {
165 right_status = button_callback.button_status.value;
166 UpdateStatus();
167 }
168
169 void UpdateModButtonStatus(const Common::Input::CallbackStatus& button_callback) {
170 modifier_status = button_callback.button_status.value;
171 UpdateStatus();
172 }
173
174 void UpdateStatus() {
175 const float coef = modifier_status ? modifier_scale : 1.0f;
176
177 bool r = right_status;
178 bool l = left_status;
179 bool u = up_status;
180 bool d = down_status;
181
182 // Eliminate contradictory movements
183 if (r && l) {
184 r = false;
185 l = false;
186 }
187 if (u && d) {
188 u = false;
189 d = false;
190 }
191
192 // Move if a key is pressed
193 if (r || l || u || d) {
194 amplitude = coef;
195 } else {
196 amplitude = 0;
197 }
198
199 const auto now = std::chrono::steady_clock::now();
200 const auto time_difference = static_cast<u64>(
201 std::chrono::duration_cast<std::chrono::milliseconds>(now - last_update).count());
202
203 if (time_difference < 10) {
204 // Disable analog mode if inputs are too fast
205 SetGoalAngle(r, l, u, d);
206 angle = goal_angle;
207 } else {
208 angle = GetAngle(now);
209 SetGoalAngle(r, l, u, d);
210 }
211
212 last_update = now;
213 Common::Input::CallbackStatus status{
214 .type = Common::Input::InputType::Stick,
215 .stick_status = GetStatus(),
216 };
217 last_x_axis_value = status.stick_status.x.raw_value;
218 last_y_axis_value = status.stick_status.y.raw_value;
219 TriggerOnChange(status);
220 }
221
222 void ForceUpdate() override {
223 up->ForceUpdate();
224 down->ForceUpdate();
225 left->ForceUpdate();
226 right->ForceUpdate();
227 modifier->ForceUpdate();
228 }
229
230 void SoftUpdate() override {
231 Common::Input::CallbackStatus status{
232 .type = Common::Input::InputType::Stick,
233 .stick_status = GetStatus(),
234 };
235 if (last_x_axis_value == status.stick_status.x.raw_value &&
236 last_y_axis_value == status.stick_status.y.raw_value) {
237 return;
238 }
239 last_x_axis_value = status.stick_status.x.raw_value;
240 last_y_axis_value = status.stick_status.y.raw_value;
241 TriggerOnChange(status);
242 }
243
244 Common::Input::StickStatus GetStatus() const {
245 Common::Input::StickStatus status{};
246 status.x.properties = properties;
247 status.y.properties = properties;
248 if (Settings::values.emulate_analog_keyboard) {
249 const auto now = std::chrono::steady_clock::now();
250 float angle_ = GetAngle(now);
251 status.x.raw_value = std::cos(angle_) * amplitude;
252 status.y.raw_value = std::sin(angle_) * amplitude;
253 return status;
254 }
255 constexpr float SQRT_HALF = 0.707106781f;
256 int x = 0, y = 0;
257 if (right_status) {
258 ++x;
259 }
260 if (left_status) {
261 --x;
262 }
263 if (up_status) {
264 ++y;
265 }
266 if (down_status) {
267 --y;
268 }
269 const float coef = modifier_status ? modifier_scale : 1.0f;
270 status.x.raw_value = static_cast<float>(x) * coef * (y == 0 ? 1.0f : SQRT_HALF);
271 status.y.raw_value = static_cast<float>(y) * coef * (x == 0 ? 1.0f : SQRT_HALF);
272 return status;
273 }
274
275private:
276 Button up;
277 Button down;
278 Button left;
279 Button right;
280 Button modifier;
281 float modifier_scale{};
282 float modifier_angle{};
283 float angle{};
284 float goal_angle{};
285 float amplitude{};
286 bool up_status{};
287 bool down_status{};
288 bool left_status{};
289 bool right_status{};
290 bool modifier_status{};
291 float last_x_axis_value{};
292 float last_y_axis_value{};
293 const Common::Input::AnalogProperties properties{0.0f, 1.0f, 0.5f, 0.0f, false};
294 std::chrono::time_point<std::chrono::steady_clock> last_update;
295};
296
297std::unique_ptr<Common::Input::InputDevice> StickFromButton::Create(
298 const Common::ParamPackage& params) {
299 const std::string null_engine = Common::ParamPackage{{"engine", "null"}}.Serialize();
300 auto up = Common::Input::CreateDeviceFromString<Common::Input::InputDevice>(
301 params.Get("up", null_engine));
302 auto down = Common::Input::CreateDeviceFromString<Common::Input::InputDevice>(
303 params.Get("down", null_engine));
304 auto left = Common::Input::CreateDeviceFromString<Common::Input::InputDevice>(
305 params.Get("left", null_engine));
306 auto right = Common::Input::CreateDeviceFromString<Common::Input::InputDevice>(
307 params.Get("right", null_engine));
308 auto modifier = Common::Input::CreateDeviceFromString<Common::Input::InputDevice>(
309 params.Get("modifier", null_engine));
310 auto modifier_scale = params.Get("modifier_scale", 0.5f);
311 auto modifier_angle = params.Get("modifier_angle", 5.5f);
312 return std::make_unique<Stick>(std::move(up), std::move(down), std::move(left),
313 std::move(right), std::move(modifier), modifier_scale,
314 modifier_angle);
315}
316
317} // namespace InputCommon
diff --git a/src/input_common/analog_from_button.h b/src/input_common/helpers/stick_from_buttons.h
index bbd583dd9..437ace4f7 100755..100644
--- a/src/input_common/analog_from_button.h
+++ b/src/input_common/helpers/stick_from_buttons.h
@@ -4,8 +4,7 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <memory> 7#include "common/input.h"
8#include "core/frontend/input.h"
9 8
10namespace InputCommon { 9namespace InputCommon {
11 10
@@ -13,7 +12,7 @@ namespace InputCommon {
13 * An analog device factory that takes direction button devices and combines them into a analog 12 * An analog device factory that takes direction button devices and combines them into a analog
14 * device. 13 * device.
15 */ 14 */
16class AnalogFromButton final : public Input::Factory<Input::AnalogDevice> { 15class StickFromButton final : public Common::Input::Factory<Common::Input::InputDevice> {
17public: 16public:
18 /** 17 /**
19 * Creates an analog device from direction button devices 18 * Creates an analog device from direction button devices
@@ -25,7 +24,7 @@ public:
25 * - "modifier": a serialized ParamPackage for creating a button device as the modifier 24 * - "modifier": a serialized ParamPackage for creating a button device as the modifier
26 * - "modifier_scale": a float for the multiplier the modifier gives to the position 25 * - "modifier_scale": a float for the multiplier the modifier gives to the position
27 */ 26 */
28 std::unique_ptr<Input::AnalogDevice> Create(const Common::ParamPackage& params) override; 27 std::unique_ptr<Common::Input::InputDevice> Create(const Common::ParamPackage& params) override;
29}; 28};
30 29
31} // namespace InputCommon 30} // namespace InputCommon
diff --git a/src/input_common/helpers/touch_from_buttons.cpp b/src/input_common/helpers/touch_from_buttons.cpp
new file mode 100644
index 000000000..ece1e3b32
--- /dev/null
+++ b/src/input_common/helpers/touch_from_buttons.cpp
@@ -0,0 +1,84 @@
1// Copyright 2020 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <algorithm>
6#include "common/settings.h"
7#include "core/frontend/framebuffer_layout.h"
8#include "input_common/helpers/touch_from_buttons.h"
9
10namespace InputCommon {
11
12class TouchFromButtonDevice final : public Common::Input::InputDevice {
13public:
14 using Button = std::unique_ptr<Common::Input::InputDevice>;
15 TouchFromButtonDevice(Button button_, int touch_id_, float x_, float y_)
16 : button(std::move(button_)), touch_id(touch_id_), x(x_), y(y_) {
17 last_button_value = false;
18 button->SetCallback({
19 .on_change =
20 [this](const Common::Input::CallbackStatus& callback_) {
21 UpdateButtonStatus(callback_);
22 },
23 });
24 button->ForceUpdate();
25 }
26
27 void ForceUpdate() override {
28 button->ForceUpdate();
29 }
30
31 Common::Input::TouchStatus GetStatus(bool pressed) const {
32 const Common::Input::ButtonStatus button_status{
33 .value = pressed,
34 };
35 Common::Input::TouchStatus status{
36 .pressed = button_status,
37 .x = {},
38 .y = {},
39 .id = touch_id,
40 };
41 status.x.properties = properties;
42 status.y.properties = properties;
43
44 if (!pressed) {
45 return status;
46 }
47
48 status.x.raw_value = x;
49 status.y.raw_value = y;
50 return status;
51 }
52
53 void UpdateButtonStatus(const Common::Input::CallbackStatus& button_callback) {
54 const Common::Input::CallbackStatus status{
55 .type = Common::Input::InputType::Touch,
56 .touch_status = GetStatus(button_callback.button_status.value),
57 };
58 if (last_button_value != button_callback.button_status.value) {
59 last_button_value = button_callback.button_status.value;
60 TriggerOnChange(status);
61 }
62 }
63
64private:
65 Button button;
66 bool last_button_value;
67 const int touch_id;
68 const float x;
69 const float y;
70 const Common::Input::AnalogProperties properties{0.0f, 1.0f, 0.5f, 0.0f, false};
71};
72
73std::unique_ptr<Common::Input::InputDevice> TouchFromButton::Create(
74 const Common::ParamPackage& params) {
75 const std::string null_engine = Common::ParamPackage{{"engine", "null"}}.Serialize();
76 auto button = Common::Input::CreateDeviceFromString<Common::Input::InputDevice>(
77 params.Get("button", null_engine));
78 const auto touch_id = params.Get("touch_id", 0);
79 const float x = params.Get("x", 0.0f) / 1280.0f;
80 const float y = params.Get("y", 0.0f) / 720.0f;
81 return std::make_unique<TouchFromButtonDevice>(std::move(button), touch_id, x, y);
82}
83
84} // namespace InputCommon
diff --git a/src/input_common/touch_from_button.h b/src/input_common/helpers/touch_from_buttons.h
index 8b4d1aa96..628f18215 100644
--- a/src/input_common/touch_from_button.h
+++ b/src/input_common/helpers/touch_from_buttons.h
@@ -4,20 +4,19 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <memory> 7#include "common/input.h"
8#include "core/frontend/input.h"
9 8
10namespace InputCommon { 9namespace InputCommon {
11 10
12/** 11/**
13 * A touch device factory that takes a list of button devices and combines them into a touch device. 12 * A touch device factory that takes a list of button devices and combines them into a touch device.
14 */ 13 */
15class TouchFromButtonFactory final : public Input::Factory<Input::TouchDevice> { 14class TouchFromButton final : public Common::Input::Factory<Common::Input::InputDevice> {
16public: 15public:
17 /** 16 /**
18 * Creates a touch device from a list of button devices 17 * Creates a touch device from a list of button devices
19 */ 18 */
20 std::unique_ptr<Input::TouchDevice> Create(const Common::ParamPackage& params) override; 19 std::unique_ptr<Common::Input::InputDevice> Create(const Common::ParamPackage& params) override;
21}; 20};
22 21
23} // namespace InputCommon 22} // namespace InputCommon
diff --git a/src/input_common/udp/protocol.cpp b/src/input_common/helpers/udp_protocol.cpp
index 5e50bd612..cdeab7e11 100644
--- a/src/input_common/udp/protocol.cpp
+++ b/src/input_common/helpers/udp_protocol.cpp
@@ -5,7 +5,7 @@
5#include <cstddef> 5#include <cstddef>
6#include <cstring> 6#include <cstring>
7#include "common/logging/log.h" 7#include "common/logging/log.h"
8#include "input_common/udp/protocol.h" 8#include "input_common/helpers/udp_protocol.h"
9 9
10namespace InputCommon::CemuhookUDP { 10namespace InputCommon::CemuhookUDP {
11 11
diff --git a/src/input_common/udp/protocol.h b/src/input_common/helpers/udp_protocol.h
index 1bdc9209e..bcba12c58 100644
--- a/src/input_common/udp/protocol.h
+++ b/src/input_common/helpers/udp_protocol.h
@@ -56,6 +56,12 @@ constexpr Type GetMessageType();
56 56
57namespace Request { 57namespace Request {
58 58
59enum RegisterFlags : u8 {
60 AllPads,
61 PadID,
62 PadMACAdddress,
63};
64
59struct Version {}; 65struct Version {};
60/** 66/**
61 * Requests the server to send information about what controllers are plugged into the ports 67 * Requests the server to send information about what controllers are plugged into the ports
@@ -77,13 +83,8 @@ static_assert(std::is_trivially_copyable_v<PortInfo>,
77 * timeout seems to be 5 seconds. 83 * timeout seems to be 5 seconds.
78 */ 84 */
79struct PadData { 85struct PadData {
80 enum class Flags : u8 {
81 AllPorts,
82 Id,
83 Mac,
84 };
85 /// Determines which method will be used as a look up for the controller 86 /// Determines which method will be used as a look up for the controller
86 Flags flags{}; 87 RegisterFlags flags{};
87 /// Index of the port of the controller to retrieve data about 88 /// Index of the port of the controller to retrieve data about
88 u8 port_id{}; 89 u8 port_id{};
89 /// Mac address of the controller to retrieve data about 90 /// Mac address of the controller to retrieve data about
@@ -113,6 +114,36 @@ Message<T> Create(const T data, const u32 client_id = 0) {
113 114
114namespace Response { 115namespace Response {
115 116
117enum class ConnectionType : u8 {
118 None,
119 Usb,
120 Bluetooth,
121};
122
123enum class State : u8 {
124 Disconnected,
125 Reserved,
126 Connected,
127};
128
129enum class Model : u8 {
130 None,
131 PartialGyro,
132 FullGyro,
133 Generic,
134};
135
136enum class Battery : u8 {
137 None = 0x00,
138 Dying = 0x01,
139 Low = 0x02,
140 Medium = 0x03,
141 High = 0x04,
142 Full = 0x05,
143 Charging = 0xEE,
144 Charged = 0xEF,
145};
146
116struct Version { 147struct Version {
117 u16_le version{}; 148 u16_le version{};
118}; 149};
@@ -122,11 +153,11 @@ static_assert(std::is_trivially_copyable_v<Version>,
122 153
123struct PortInfo { 154struct PortInfo {
124 u8 id{}; 155 u8 id{};
125 u8 state{}; 156 State state{};
126 u8 model{}; 157 Model model{};
127 u8 connection_type{}; 158 ConnectionType connection_type{};
128 MacAddress mac; 159 MacAddress mac;
129 u8 battery{}; 160 Battery battery{};
130 u8 is_pad_active{}; 161 u8 is_pad_active{};
131}; 162};
132static_assert(sizeof(PortInfo) == 12, "UDP Response PortInfo struct has wrong size"); 163static_assert(sizeof(PortInfo) == 12, "UDP Response PortInfo struct has wrong size");
@@ -177,18 +208,18 @@ struct PadData {
177 u8 right_stick_y{}; 208 u8 right_stick_y{};
178 209
179 struct AnalogButton { 210 struct AnalogButton {
180 u8 button_8{}; 211 u8 button_dpad_left_analog{};
181 u8 button_7{}; 212 u8 button_dpad_down_analog{};
182 u8 button_6{}; 213 u8 button_dpad_right_analog{};
183 u8 button_5{}; 214 u8 button_dpad_up_analog{};
184 u8 button_12{}; 215 u8 button_square_analog{};
185 u8 button_11{}; 216 u8 button_cross_analog{};
186 u8 button_10{}; 217 u8 button_circle_analog{};
187 u8 button_9{}; 218 u8 button_triangle_analog{};
188 u8 button_16{}; 219 u8 button_r1_analog{};
189 u8 button_15{}; 220 u8 button_l1_analog{};
190 u8 button_14{}; 221 u8 trigger_r2{};
191 u8 button_13{}; 222 u8 trigger_l2{};
192 } analog_button; 223 } analog_button;
193 224
194 std::array<TouchPad, 2> touch; 225 std::array<TouchPad, 2> touch;
diff --git a/src/input_common/input_engine.cpp b/src/input_common/input_engine.cpp
new file mode 100644
index 000000000..9c17ca4f7
--- /dev/null
+++ b/src/input_common/input_engine.cpp
@@ -0,0 +1,362 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included
4
5#include "common/logging/log.h"
6#include "common/param_package.h"
7#include "input_common/input_engine.h"
8
9namespace InputCommon {
10
11void InputEngine::PreSetController(const PadIdentifier& identifier) {
12 std::lock_guard lock{mutex};
13 controller_list.try_emplace(identifier);
14}
15
16void InputEngine::PreSetButton(const PadIdentifier& identifier, int button) {
17 std::lock_guard lock{mutex};
18 ControllerData& controller = controller_list.at(identifier);
19 controller.buttons.try_emplace(button, false);
20}
21
22void InputEngine::PreSetHatButton(const PadIdentifier& identifier, int button) {
23 std::lock_guard lock{mutex};
24 ControllerData& controller = controller_list.at(identifier);
25 controller.hat_buttons.try_emplace(button, u8{0});
26}
27
28void InputEngine::PreSetAxis(const PadIdentifier& identifier, int axis) {
29 std::lock_guard lock{mutex};
30 ControllerData& controller = controller_list.at(identifier);
31 controller.axes.try_emplace(axis, 0.0f);
32}
33
34void InputEngine::PreSetMotion(const PadIdentifier& identifier, int motion) {
35 std::lock_guard lock{mutex};
36 ControllerData& controller = controller_list.at(identifier);
37 controller.motions.try_emplace(motion);
38}
39
40void InputEngine::SetButton(const PadIdentifier& identifier, int button, bool value) {
41 {
42 std::lock_guard lock{mutex};
43 ControllerData& controller = controller_list.at(identifier);
44 if (!configuring) {
45 controller.buttons.insert_or_assign(button, value);
46 }
47 }
48 TriggerOnButtonChange(identifier, button, value);
49}
50
51void InputEngine::SetHatButton(const PadIdentifier& identifier, int button, u8 value) {
52 {
53 std::lock_guard lock{mutex};
54 ControllerData& controller = controller_list.at(identifier);
55 if (!configuring) {
56 controller.hat_buttons.insert_or_assign(button, value);
57 }
58 }
59 TriggerOnHatButtonChange(identifier, button, value);
60}
61
62void InputEngine::SetAxis(const PadIdentifier& identifier, int axis, f32 value) {
63 {
64 std::lock_guard lock{mutex};
65 ControllerData& controller = controller_list.at(identifier);
66 if (!configuring) {
67 controller.axes.insert_or_assign(axis, value);
68 }
69 }
70 TriggerOnAxisChange(identifier, axis, value);
71}
72
73void InputEngine::SetBattery(const PadIdentifier& identifier, BatteryLevel value) {
74 {
75 std::lock_guard lock{mutex};
76 ControllerData& controller = controller_list.at(identifier);
77 if (!configuring) {
78 controller.battery = value;
79 }
80 }
81 TriggerOnBatteryChange(identifier, value);
82}
83
84void InputEngine::SetMotion(const PadIdentifier& identifier, int motion, const BasicMotion& value) {
85 {
86 std::lock_guard lock{mutex};
87 ControllerData& controller = controller_list.at(identifier);
88 if (!configuring) {
89 controller.motions.insert_or_assign(motion, value);
90 }
91 }
92 TriggerOnMotionChange(identifier, motion, value);
93}
94
95bool InputEngine::GetButton(const PadIdentifier& identifier, int button) const {
96 std::lock_guard lock{mutex};
97 const auto controller_iter = controller_list.find(identifier);
98 if (controller_iter == controller_list.cend()) {
99 LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.Format(),
100 identifier.pad, identifier.port);
101 return false;
102 }
103 const ControllerData& controller = controller_iter->second;
104 const auto button_iter = controller.buttons.find(button);
105 if (button_iter == controller.buttons.cend()) {
106 LOG_ERROR(Input, "Invalid button {}", button);
107 return false;
108 }
109 return button_iter->second;
110}
111
112bool InputEngine::GetHatButton(const PadIdentifier& identifier, int button, u8 direction) const {
113 std::lock_guard lock{mutex};
114 const auto controller_iter = controller_list.find(identifier);
115 if (controller_iter == controller_list.cend()) {
116 LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.Format(),
117 identifier.pad, identifier.port);
118 return false;
119 }
120 const ControllerData& controller = controller_iter->second;
121 const auto hat_iter = controller.hat_buttons.find(button);
122 if (hat_iter == controller.hat_buttons.cend()) {
123 LOG_ERROR(Input, "Invalid hat button {}", button);
124 return false;
125 }
126 return (hat_iter->second & direction) != 0;
127}
128
129f32 InputEngine::GetAxis(const PadIdentifier& identifier, int axis) const {
130 std::lock_guard lock{mutex};
131 const auto controller_iter = controller_list.find(identifier);
132 if (controller_iter == controller_list.cend()) {
133 LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.Format(),
134 identifier.pad, identifier.port);
135 return 0.0f;
136 }
137 const ControllerData& controller = controller_iter->second;
138 const auto axis_iter = controller.axes.find(axis);
139 if (axis_iter == controller.axes.cend()) {
140 LOG_ERROR(Input, "Invalid axis {}", axis);
141 return 0.0f;
142 }
143 return axis_iter->second;
144}
145
146BatteryLevel InputEngine::GetBattery(const PadIdentifier& identifier) const {
147 std::lock_guard lock{mutex};
148 const auto controller_iter = controller_list.find(identifier);
149 if (controller_iter == controller_list.cend()) {
150 LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.Format(),
151 identifier.pad, identifier.port);
152 return BatteryLevel::Charging;
153 }
154 const ControllerData& controller = controller_iter->second;
155 return controller.battery;
156}
157
158BasicMotion InputEngine::GetMotion(const PadIdentifier& identifier, int motion) const {
159 std::lock_guard lock{mutex};
160 const auto controller_iter = controller_list.find(identifier);
161 if (controller_iter == controller_list.cend()) {
162 LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.Format(),
163 identifier.pad, identifier.port);
164 return {};
165 }
166 const ControllerData& controller = controller_iter->second;
167 return controller.motions.at(motion);
168}
169
170void InputEngine::ResetButtonState() {
171 for (const auto& controller : controller_list) {
172 for (const auto& button : controller.second.buttons) {
173 SetButton(controller.first, button.first, false);
174 }
175 for (const auto& button : controller.second.hat_buttons) {
176 SetHatButton(controller.first, button.first, false);
177 }
178 }
179}
180
181void InputEngine::ResetAnalogState() {
182 for (const auto& controller : controller_list) {
183 for (const auto& axis : controller.second.axes) {
184 SetAxis(controller.first, axis.first, 0.0);
185 }
186 }
187}
188
189void InputEngine::TriggerOnButtonChange(const PadIdentifier& identifier, int button, bool value) {
190 std::lock_guard lock{mutex_callback};
191 for (const auto& poller_pair : callback_list) {
192 const InputIdentifier& poller = poller_pair.second;
193 if (!IsInputIdentifierEqual(poller, identifier, EngineInputType::Button, button)) {
194 continue;
195 }
196 if (poller.callback.on_change) {
197 poller.callback.on_change();
198 }
199 }
200 if (!configuring || !mapping_callback.on_data) {
201 return;
202 }
203
204 PreSetButton(identifier, button);
205 if (value == GetButton(identifier, button)) {
206 return;
207 }
208 mapping_callback.on_data(MappingData{
209 .engine = GetEngineName(),
210 .pad = identifier,
211 .type = EngineInputType::Button,
212 .index = button,
213 .button_value = value,
214 });
215}
216
217void InputEngine::TriggerOnHatButtonChange(const PadIdentifier& identifier, int button, u8 value) {
218 std::lock_guard lock{mutex_callback};
219 for (const auto& poller_pair : callback_list) {
220 const InputIdentifier& poller = poller_pair.second;
221 if (!IsInputIdentifierEqual(poller, identifier, EngineInputType::HatButton, button)) {
222 continue;
223 }
224 if (poller.callback.on_change) {
225 poller.callback.on_change();
226 }
227 }
228 if (!configuring || !mapping_callback.on_data) {
229 return;
230 }
231 for (std::size_t index = 1; index < 0xff; index <<= 1) {
232 bool button_value = (value & index) != 0;
233 if (button_value == GetHatButton(identifier, button, static_cast<u8>(index))) {
234 continue;
235 }
236 mapping_callback.on_data(MappingData{
237 .engine = GetEngineName(),
238 .pad = identifier,
239 .type = EngineInputType::HatButton,
240 .index = button,
241 .hat_name = GetHatButtonName(static_cast<u8>(index)),
242 });
243 }
244}
245
246void InputEngine::TriggerOnAxisChange(const PadIdentifier& identifier, int axis, f32 value) {
247 std::lock_guard lock{mutex_callback};
248 for (const auto& poller_pair : callback_list) {
249 const InputIdentifier& poller = poller_pair.second;
250 if (!IsInputIdentifierEqual(poller, identifier, EngineInputType::Analog, axis)) {
251 continue;
252 }
253 if (poller.callback.on_change) {
254 poller.callback.on_change();
255 }
256 }
257 if (!configuring || !mapping_callback.on_data) {
258 return;
259 }
260 if (std::abs(value - GetAxis(identifier, axis)) < 0.5f) {
261 return;
262 }
263 mapping_callback.on_data(MappingData{
264 .engine = GetEngineName(),
265 .pad = identifier,
266 .type = EngineInputType::Analog,
267 .index = axis,
268 .axis_value = value,
269 });
270}
271
272void InputEngine::TriggerOnBatteryChange(const PadIdentifier& identifier,
273 [[maybe_unused]] BatteryLevel value) {
274 std::lock_guard lock{mutex_callback};
275 for (const auto& poller_pair : callback_list) {
276 const InputIdentifier& poller = poller_pair.second;
277 if (!IsInputIdentifierEqual(poller, identifier, EngineInputType::Battery, 0)) {
278 continue;
279 }
280 if (poller.callback.on_change) {
281 poller.callback.on_change();
282 }
283 }
284}
285
286void InputEngine::TriggerOnMotionChange(const PadIdentifier& identifier, int motion,
287 const BasicMotion& value) {
288 std::lock_guard lock{mutex_callback};
289 for (const auto& poller_pair : callback_list) {
290 const InputIdentifier& poller = poller_pair.second;
291 if (!IsInputIdentifierEqual(poller, identifier, EngineInputType::Motion, motion)) {
292 continue;
293 }
294 if (poller.callback.on_change) {
295 poller.callback.on_change();
296 }
297 }
298 if (!configuring || !mapping_callback.on_data) {
299 return;
300 }
301 if (std::abs(value.gyro_x) < 0.6f && std::abs(value.gyro_y) < 0.6f &&
302 std::abs(value.gyro_z) < 0.6f) {
303 return;
304 }
305 mapping_callback.on_data(MappingData{
306 .engine = GetEngineName(),
307 .pad = identifier,
308 .type = EngineInputType::Motion,
309 .index = motion,
310 .motion_value = value,
311 });
312}
313
314bool InputEngine::IsInputIdentifierEqual(const InputIdentifier& input_identifier,
315 const PadIdentifier& identifier, EngineInputType type,
316 int index) const {
317 if (input_identifier.type != type) {
318 return false;
319 }
320 if (input_identifier.index != index) {
321 return false;
322 }
323 if (input_identifier.identifier != identifier) {
324 return false;
325 }
326 return true;
327}
328
329void InputEngine::BeginConfiguration() {
330 configuring = true;
331}
332
333void InputEngine::EndConfiguration() {
334 configuring = false;
335}
336
337const std::string& InputEngine::GetEngineName() const {
338 return input_engine;
339}
340
341int InputEngine::SetCallback(InputIdentifier input_identifier) {
342 std::lock_guard lock{mutex_callback};
343 callback_list.insert_or_assign(last_callback_key, std::move(input_identifier));
344 return last_callback_key++;
345}
346
347void InputEngine::SetMappingCallback(MappingCallback callback) {
348 std::lock_guard lock{mutex_callback};
349 mapping_callback = std::move(callback);
350}
351
352void InputEngine::DeleteCallback(int key) {
353 std::lock_guard lock{mutex_callback};
354 const auto& iterator = callback_list.find(key);
355 if (iterator == callback_list.end()) {
356 LOG_ERROR(Input, "Tried to delete non-existent callback {}", key);
357 return;
358 }
359 callback_list.erase(iterator);
360}
361
362} // namespace InputCommon
diff --git a/src/input_common/input_engine.h b/src/input_common/input_engine.h
new file mode 100644
index 000000000..390581c94
--- /dev/null
+++ b/src/input_common/input_engine.h
@@ -0,0 +1,229 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included
4
5#pragma once
6
7#include <functional>
8#include <mutex>
9#include <unordered_map>
10
11#include "common/common_types.h"
12#include "common/input.h"
13#include "common/param_package.h"
14#include "common/uuid.h"
15#include "input_common/main.h"
16
17// Pad Identifier of data source
18struct PadIdentifier {
19 Common::UUID guid{};
20 std::size_t port{};
21 std::size_t pad{};
22
23 friend constexpr bool operator==(const PadIdentifier&, const PadIdentifier&) = default;
24};
25
26// Basic motion data containing data from the sensors and a timestamp in microseconds
27struct BasicMotion {
28 float gyro_x{};
29 float gyro_y{};
30 float gyro_z{};
31 float accel_x{};
32 float accel_y{};
33 float accel_z{};
34 u64 delta_timestamp{};
35};
36
37// Stages of a battery charge
38enum class BatteryLevel {
39 Empty,
40 Critical,
41 Low,
42 Medium,
43 Full,
44 Charging,
45};
46
47// Types of input that are stored in the engine
48enum class EngineInputType {
49 None,
50 Button,
51 HatButton,
52 Analog,
53 Motion,
54 Battery,
55};
56
57namespace std {
58// Hash used to create lists from PadIdentifier data
59template <>
60struct hash<PadIdentifier> {
61 size_t operator()(const PadIdentifier& pad_id) const noexcept {
62 u64 hash_value = pad_id.guid.uuid[1] ^ pad_id.guid.uuid[0];
63 hash_value ^= (static_cast<u64>(pad_id.port) << 32);
64 hash_value ^= static_cast<u64>(pad_id.pad);
65 return static_cast<size_t>(hash_value);
66 }
67};
68
69} // namespace std
70
71namespace InputCommon {
72
73// Data from the engine and device needed for creating a ParamPackage
74struct MappingData {
75 std::string engine{};
76 PadIdentifier pad{};
77 EngineInputType type{};
78 int index{};
79 bool button_value{};
80 std::string hat_name{};
81 f32 axis_value{};
82 BasicMotion motion_value{};
83};
84
85// Triggered if data changed on the controller
86struct UpdateCallback {
87 std::function<void()> on_change;
88};
89
90// Triggered if data changed on the controller and the engine is on configuring mode
91struct MappingCallback {
92 std::function<void(MappingData)> on_data;
93};
94
95// Input Identifier of data source
96struct InputIdentifier {
97 PadIdentifier identifier;
98 EngineInputType type;
99 int index;
100 UpdateCallback callback;
101};
102
103class InputEngine {
104public:
105 explicit InputEngine(std::string input_engine_) : input_engine{std::move(input_engine_)} {}
106
107 virtual ~InputEngine() = default;
108
109 // Enable configuring mode for mapping
110 void BeginConfiguration();
111
112 // Disable configuring mode for mapping
113 void EndConfiguration();
114
115 // Sets a led pattern for a controller
116 virtual void SetLeds([[maybe_unused]] const PadIdentifier& identifier,
117 [[maybe_unused]] const Common::Input::LedStatus& led_status) {}
118
119 // Sets rumble to a controller
120 virtual Common::Input::VibrationError SetRumble(
121 [[maybe_unused]] const PadIdentifier& identifier,
122 [[maybe_unused]] const Common::Input::VibrationStatus& vibration) {
123 return Common::Input::VibrationError::NotSupported;
124 }
125
126 // Sets polling mode to a controller
127 virtual Common::Input::PollingError SetPollingMode(
128 [[maybe_unused]] const PadIdentifier& identifier,
129 [[maybe_unused]] const Common::Input::PollingMode vibration) {
130 return Common::Input::PollingError::NotSupported;
131 }
132
133 // Returns the engine name
134 [[nodiscard]] const std::string& GetEngineName() const;
135
136 /// Used for automapping features
137 virtual std::vector<Common::ParamPackage> GetInputDevices() const {
138 return {};
139 }
140
141 /// Retrieves the button mappings for the given device
142 virtual ButtonMapping GetButtonMappingForDevice(
143 [[maybe_unused]] const Common::ParamPackage& params) {
144 return {};
145 }
146
147 /// Retrieves the analog mappings for the given device
148 virtual AnalogMapping GetAnalogMappingForDevice(
149 [[maybe_unused]] const Common::ParamPackage& params) {
150 return {};
151 }
152
153 /// Retrieves the motion mappings for the given device
154 virtual MotionMapping GetMotionMappingForDevice(
155 [[maybe_unused]] const Common::ParamPackage& params) {
156 return {};
157 }
158
159 /// Retrieves the name of the given input.
160 virtual Common::Input::ButtonNames GetUIName(
161 [[maybe_unused]] const Common::ParamPackage& params) const {
162 return Common::Input::ButtonNames::Engine;
163 }
164
165 /// Retrieves the index number of the given hat button direction
166 virtual u8 GetHatButtonId([[maybe_unused]] const std::string& direction_name) const {
167 return 0;
168 }
169
170 void PreSetController(const PadIdentifier& identifier);
171 void PreSetButton(const PadIdentifier& identifier, int button);
172 void PreSetHatButton(const PadIdentifier& identifier, int button);
173 void PreSetAxis(const PadIdentifier& identifier, int axis);
174 void PreSetMotion(const PadIdentifier& identifier, int motion);
175 void ResetButtonState();
176 void ResetAnalogState();
177
178 bool GetButton(const PadIdentifier& identifier, int button) const;
179 bool GetHatButton(const PadIdentifier& identifier, int button, u8 direction) const;
180 f32 GetAxis(const PadIdentifier& identifier, int axis) const;
181 BatteryLevel GetBattery(const PadIdentifier& identifier) const;
182 BasicMotion GetMotion(const PadIdentifier& identifier, int motion) const;
183
184 int SetCallback(InputIdentifier input_identifier);
185 void SetMappingCallback(MappingCallback callback);
186 void DeleteCallback(int key);
187
188protected:
189 void SetButton(const PadIdentifier& identifier, int button, bool value);
190 void SetHatButton(const PadIdentifier& identifier, int button, u8 value);
191 void SetAxis(const PadIdentifier& identifier, int axis, f32 value);
192 void SetBattery(const PadIdentifier& identifier, BatteryLevel value);
193 void SetMotion(const PadIdentifier& identifier, int motion, const BasicMotion& value);
194
195 virtual std::string GetHatButtonName([[maybe_unused]] u8 direction_value) const {
196 return "Unknown";
197 }
198
199private:
200 struct ControllerData {
201 std::unordered_map<int, bool> buttons;
202 std::unordered_map<int, u8> hat_buttons;
203 std::unordered_map<int, float> axes;
204 std::unordered_map<int, BasicMotion> motions;
205 BatteryLevel battery{};
206 };
207
208 void TriggerOnButtonChange(const PadIdentifier& identifier, int button, bool value);
209 void TriggerOnHatButtonChange(const PadIdentifier& identifier, int button, u8 value);
210 void TriggerOnAxisChange(const PadIdentifier& identifier, int axis, f32 value);
211 void TriggerOnBatteryChange(const PadIdentifier& identifier, BatteryLevel value);
212 void TriggerOnMotionChange(const PadIdentifier& identifier, int motion,
213 const BasicMotion& value);
214
215 bool IsInputIdentifierEqual(const InputIdentifier& input_identifier,
216 const PadIdentifier& identifier, EngineInputType type,
217 int index) const;
218
219 mutable std::mutex mutex;
220 mutable std::mutex mutex_callback;
221 bool configuring{false};
222 const std::string input_engine;
223 int last_callback_key = 0;
224 std::unordered_map<PadIdentifier, ControllerData> controller_list;
225 std::unordered_map<int, InputIdentifier> callback_list;
226 MappingCallback mapping_callback;
227};
228
229} // namespace InputCommon
diff --git a/src/input_common/input_mapping.cpp b/src/input_common/input_mapping.cpp
new file mode 100644
index 000000000..6e0024b2d
--- /dev/null
+++ b/src/input_common/input_mapping.cpp
@@ -0,0 +1,207 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included
4
5#include "common/common_types.h"
6#include "common/settings.h"
7#include "input_common/input_engine.h"
8#include "input_common/input_mapping.h"
9
10namespace InputCommon {
11
12MappingFactory::MappingFactory() {}
13
14void MappingFactory::BeginMapping(Polling::InputType type) {
15 is_enabled = true;
16 input_type = type;
17 input_queue.Clear();
18 first_axis = -1;
19 second_axis = -1;
20}
21
22[[nodiscard]] const Common::ParamPackage MappingFactory::GetNextInput() {
23 Common::ParamPackage input;
24 input_queue.Pop(input);
25 return input;
26}
27
28void MappingFactory::RegisterInput(const MappingData& data) {
29 if (!is_enabled) {
30 return;
31 }
32 if (!IsDriverValid(data)) {
33 return;
34 }
35
36 switch (input_type) {
37 case Polling::InputType::Button:
38 RegisterButton(data);
39 return;
40 case Polling::InputType::Stick:
41 RegisterStick(data);
42 return;
43 case Polling::InputType::Motion:
44 RegisterMotion(data);
45 return;
46 default:
47 return;
48 }
49}
50
51void MappingFactory::StopMapping() {
52 is_enabled = false;
53 input_type = Polling::InputType::None;
54 input_queue.Clear();
55}
56
57void MappingFactory::RegisterButton(const MappingData& data) {
58 Common::ParamPackage new_input;
59 new_input.Set("engine", data.engine);
60 if (data.pad.guid != Common::UUID{}) {
61 new_input.Set("guid", data.pad.guid.Format());
62 }
63 new_input.Set("port", static_cast<int>(data.pad.port));
64 new_input.Set("pad", static_cast<int>(data.pad.pad));
65
66 switch (data.type) {
67 case EngineInputType::Button:
68 // Workaround for old compatibility
69 if (data.engine == "keyboard") {
70 new_input.Set("code", data.index);
71 break;
72 }
73 new_input.Set("button", data.index);
74 break;
75 case EngineInputType::HatButton:
76 new_input.Set("hat", data.index);
77 new_input.Set("direction", data.hat_name);
78 break;
79 case EngineInputType::Analog:
80 // Ignore mouse axis when mapping buttons
81 if (data.engine == "mouse") {
82 return;
83 }
84 new_input.Set("axis", data.index);
85 new_input.Set("threshold", 0.5f);
86 break;
87 default:
88 return;
89 }
90 input_queue.Push(new_input);
91}
92
93void MappingFactory::RegisterStick(const MappingData& data) {
94 Common::ParamPackage new_input;
95 new_input.Set("engine", data.engine);
96 if (data.pad.guid != Common::UUID{}) {
97 new_input.Set("guid", data.pad.guid.Format());
98 }
99 new_input.Set("port", static_cast<int>(data.pad.port));
100 new_input.Set("pad", static_cast<int>(data.pad.pad));
101
102 // If engine is mouse map the mouse position as a joystick
103 if (data.engine == "mouse") {
104 new_input.Set("axis_x", 0);
105 new_input.Set("axis_y", 1);
106 new_input.Set("threshold", 0.5f);
107 new_input.Set("range", 1.0f);
108 new_input.Set("deadzone", 0.0f);
109 input_queue.Push(new_input);
110 return;
111 }
112
113 switch (data.type) {
114 case EngineInputType::Button:
115 case EngineInputType::HatButton:
116 RegisterButton(data);
117 return;
118 case EngineInputType::Analog:
119 if (first_axis == data.index) {
120 return;
121 }
122 if (first_axis == -1) {
123 first_axis = data.index;
124 return;
125 }
126 new_input.Set("axis_x", first_axis);
127 new_input.Set("axis_y", data.index);
128 new_input.Set("threshold", 0.5f);
129 new_input.Set("range", 0.95f);
130 new_input.Set("deadzone", 0.15f);
131 break;
132 default:
133 return;
134 }
135 input_queue.Push(new_input);
136}
137
138void MappingFactory::RegisterMotion(const MappingData& data) {
139 Common::ParamPackage new_input;
140 new_input.Set("engine", data.engine);
141 if (data.pad.guid != Common::UUID{}) {
142 new_input.Set("guid", data.pad.guid.Format());
143 }
144 new_input.Set("port", static_cast<int>(data.pad.port));
145 new_input.Set("pad", static_cast<int>(data.pad.pad));
146 switch (data.type) {
147 case EngineInputType::Button:
148 case EngineInputType::HatButton:
149 RegisterButton(data);
150 return;
151 case EngineInputType::Analog:
152 if (first_axis == data.index) {
153 return;
154 }
155 if (second_axis == data.index) {
156 return;
157 }
158 if (first_axis == -1) {
159 first_axis = data.index;
160 return;
161 }
162 if (second_axis == -1) {
163 second_axis = data.index;
164 return;
165 }
166 new_input.Set("axis_x", first_axis);
167 new_input.Set("axis_y", second_axis);
168 new_input.Set("axis_z", data.index);
169 new_input.Set("range", 1.0f);
170 new_input.Set("deadzone", 0.20f);
171 break;
172 case EngineInputType::Motion:
173 new_input.Set("motion", data.index);
174 break;
175 default:
176 return;
177 }
178 input_queue.Push(new_input);
179}
180
181bool MappingFactory::IsDriverValid(const MappingData& data) const {
182 // Only port 0 can be mapped on the keyboard
183 if (data.engine == "keyboard" && data.pad.port != 0) {
184 return false;
185 }
186 // To prevent mapping with two devices we disable any UDP except motion
187 if (!Settings::values.enable_udp_controller && data.engine == "cemuhookudp" &&
188 data.type != EngineInputType::Motion) {
189 return false;
190 }
191 // The following drivers don't need to be mapped
192 if (data.engine == "tas") {
193 return false;
194 }
195 if (data.engine == "touch") {
196 return false;
197 }
198 if (data.engine == "touch_from_button") {
199 return false;
200 }
201 if (data.engine == "analog_from_button") {
202 return false;
203 }
204 return true;
205}
206
207} // namespace InputCommon
diff --git a/src/input_common/input_mapping.h b/src/input_common/input_mapping.h
new file mode 100644
index 000000000..93564b5f8
--- /dev/null
+++ b/src/input_common/input_mapping.h
@@ -0,0 +1,83 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included
4
5#pragma once
6#include "common/threadsafe_queue.h"
7
8namespace InputCommon {
9class InputEngine;
10struct MappingData;
11
12class MappingFactory {
13public:
14 MappingFactory();
15
16 /**
17 * Resets all variables to begin the mapping process
18 * @param type type of input desired to be returned
19 */
20 void BeginMapping(Polling::InputType type);
21
22 /// Returns an input event with mapping information from the input_queue
23 [[nodiscard]] const Common::ParamPackage GetNextInput();
24
25 /**
26 * Registers mapping input data from the driver
27 * @param data A struct containing all the information needed to create a proper
28 * ParamPackage
29 */
30 void RegisterInput(const MappingData& data);
31
32 /// Stop polling from all backends
33 void StopMapping();
34
35private:
36 /**
37 * If provided data satisfies the requirements it will push an element to the input_queue
38 * Supported input:
39 * - Button: Creates a basic button ParamPackage
40 * - HatButton: Creates a basic hat button ParamPackage
41 * - Analog: Creates a basic analog ParamPackage
42 * @param data A struct containing all the information needed to create a proper
43 * ParamPackage
44 */
45 void RegisterButton(const MappingData& data);
46
47 /**
48 * If provided data satisfies the requirements it will push an element to the input_queue
49 * Supported input:
50 * - Button, HatButton: Pass the data to RegisterButton
51 * - Analog: Stores the first axis and on the second axis creates a basic stick ParamPackage
52 * @param data A struct containing all the information needed to create a proper
53 * ParamPackage
54 */
55 void RegisterStick(const MappingData& data);
56
57 /**
58 * If provided data satisfies the requirements it will push an element to the input_queue
59 * Supported input:
60 * - Button, HatButton: Pass the data to RegisterButton
61 * - Analog: Stores the first two axis and on the third axis creates a basic Motion
62 * ParamPackage
63 * - Motion: Creates a basic Motion ParamPackage
64 * @param data A struct containing all the information needed to create a proper
65 * ParamPackage
66 */
67 void RegisterMotion(const MappingData& data);
68
69 /**
70 * Returns true if driver can be mapped
71 * @param data A struct containing all the information needed to create a proper
72 * ParamPackage
73 */
74 bool IsDriverValid(const MappingData& data) const;
75
76 Common::SPSCQueue<Common::ParamPackage> input_queue;
77 Polling::InputType input_type{Polling::InputType::None};
78 bool is_enabled{};
79 int first_axis = -1;
80 int second_axis = -1;
81};
82
83} // namespace InputCommon
diff --git a/src/input_common/input_poller.cpp b/src/input_common/input_poller.cpp
new file mode 100644
index 000000000..7b370335f
--- /dev/null
+++ b/src/input_common/input_poller.cpp
@@ -0,0 +1,970 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included
4
5#include "common/common_types.h"
6#include "common/input.h"
7
8#include "input_common/input_engine.h"
9#include "input_common/input_poller.h"
10
11namespace InputCommon {
12
13class DummyInput final : public Common::Input::InputDevice {
14public:
15 explicit DummyInput() = default;
16};
17
18class InputFromButton final : public Common::Input::InputDevice {
19public:
20 explicit InputFromButton(PadIdentifier identifier_, int button_, bool toggle_, bool inverted_,
21 InputEngine* input_engine_)
22 : identifier(identifier_), button(button_), toggle(toggle_), inverted(inverted_),
23 input_engine(input_engine_) {
24 UpdateCallback engine_callback{[this]() { OnChange(); }};
25 const InputIdentifier input_identifier{
26 .identifier = identifier,
27 .type = EngineInputType::Button,
28 .index = button,
29 .callback = engine_callback,
30 };
31 last_button_value = false;
32 callback_key = input_engine->SetCallback(input_identifier);
33 }
34
35 ~InputFromButton() override {
36 input_engine->DeleteCallback(callback_key);
37 }
38
39 Common::Input::ButtonStatus GetStatus() const {
40 return {
41 .value = input_engine->GetButton(identifier, button),
42 .inverted = inverted,
43 .toggle = toggle,
44 };
45 }
46
47 void ForceUpdate() override {
48 const Common::Input::CallbackStatus status{
49 .type = Common::Input::InputType::Button,
50 .button_status = GetStatus(),
51 };
52
53 last_button_value = status.button_status.value;
54 TriggerOnChange(status);
55 }
56
57 void OnChange() {
58 const Common::Input::CallbackStatus status{
59 .type = Common::Input::InputType::Button,
60 .button_status = GetStatus(),
61 };
62
63 if (status.button_status.value != last_button_value) {
64 last_button_value = status.button_status.value;
65 TriggerOnChange(status);
66 }
67 }
68
69private:
70 const PadIdentifier identifier;
71 const int button;
72 const bool toggle;
73 const bool inverted;
74 int callback_key;
75 bool last_button_value;
76 InputEngine* input_engine;
77};
78
79class InputFromHatButton final : public Common::Input::InputDevice {
80public:
81 explicit InputFromHatButton(PadIdentifier identifier_, int button_, u8 direction_, bool toggle_,
82 bool inverted_, InputEngine* input_engine_)
83 : identifier(identifier_), button(button_), direction(direction_), toggle(toggle_),
84 inverted(inverted_), input_engine(input_engine_) {
85 UpdateCallback engine_callback{[this]() { OnChange(); }};
86 const InputIdentifier input_identifier{
87 .identifier = identifier,
88 .type = EngineInputType::HatButton,
89 .index = button,
90 .callback = engine_callback,
91 };
92 last_button_value = false;
93 callback_key = input_engine->SetCallback(input_identifier);
94 }
95
96 ~InputFromHatButton() override {
97 input_engine->DeleteCallback(callback_key);
98 }
99
100 Common::Input::ButtonStatus GetStatus() const {
101 return {
102 .value = input_engine->GetHatButton(identifier, button, direction),
103 .inverted = inverted,
104 .toggle = toggle,
105 };
106 }
107
108 void ForceUpdate() override {
109 const Common::Input::CallbackStatus status{
110 .type = Common::Input::InputType::Button,
111 .button_status = GetStatus(),
112 };
113
114 last_button_value = status.button_status.value;
115 TriggerOnChange(status);
116 }
117
118 void OnChange() {
119 const Common::Input::CallbackStatus status{
120 .type = Common::Input::InputType::Button,
121 .button_status = GetStatus(),
122 };
123
124 if (status.button_status.value != last_button_value) {
125 last_button_value = status.button_status.value;
126 TriggerOnChange(status);
127 }
128 }
129
130private:
131 const PadIdentifier identifier;
132 const int button;
133 const u8 direction;
134 const bool toggle;
135 const bool inverted;
136 int callback_key;
137 bool last_button_value;
138 InputEngine* input_engine;
139};
140
141class InputFromStick final : public Common::Input::InputDevice {
142public:
143 explicit InputFromStick(PadIdentifier identifier_, int axis_x_, int axis_y_,
144 Common::Input::AnalogProperties properties_x_,
145 Common::Input::AnalogProperties properties_y_,
146 InputEngine* input_engine_)
147 : identifier(identifier_), axis_x(axis_x_), axis_y(axis_y_), properties_x(properties_x_),
148 properties_y(properties_y_),
149 input_engine(input_engine_), invert_axis_y{input_engine_->GetEngineName() == "sdl"} {
150 UpdateCallback engine_callback{[this]() { OnChange(); }};
151 const InputIdentifier x_input_identifier{
152 .identifier = identifier,
153 .type = EngineInputType::Analog,
154 .index = axis_x,
155 .callback = engine_callback,
156 };
157 const InputIdentifier y_input_identifier{
158 .identifier = identifier,
159 .type = EngineInputType::Analog,
160 .index = axis_y,
161 .callback = engine_callback,
162 };
163 last_axis_x_value = 0.0f;
164 last_axis_y_value = 0.0f;
165 callback_key_x = input_engine->SetCallback(x_input_identifier);
166 callback_key_y = input_engine->SetCallback(y_input_identifier);
167 }
168
169 ~InputFromStick() override {
170 input_engine->DeleteCallback(callback_key_x);
171 input_engine->DeleteCallback(callback_key_y);
172 }
173
174 Common::Input::StickStatus GetStatus() const {
175 Common::Input::StickStatus status;
176 status.x = {
177 .raw_value = input_engine->GetAxis(identifier, axis_x),
178 .properties = properties_x,
179 };
180 status.y = {
181 .raw_value = input_engine->GetAxis(identifier, axis_y),
182 .properties = properties_y,
183 };
184 // This is a workaround too keep compatibility with old yuzu versions. Vertical axis is
185 // inverted on SDL compared to Nintendo
186 if (invert_axis_y) {
187 status.y.raw_value = -status.y.raw_value;
188 }
189 return status;
190 }
191
192 void ForceUpdate() override {
193 const Common::Input::CallbackStatus status{
194 .type = Common::Input::InputType::Stick,
195 .stick_status = GetStatus(),
196 };
197
198 last_axis_x_value = status.stick_status.x.raw_value;
199 last_axis_y_value = status.stick_status.y.raw_value;
200 TriggerOnChange(status);
201 }
202
203 void OnChange() {
204 const Common::Input::CallbackStatus status{
205 .type = Common::Input::InputType::Stick,
206 .stick_status = GetStatus(),
207 };
208
209 if (status.stick_status.x.raw_value != last_axis_x_value ||
210 status.stick_status.y.raw_value != last_axis_y_value) {
211 last_axis_x_value = status.stick_status.x.raw_value;
212 last_axis_y_value = status.stick_status.y.raw_value;
213 TriggerOnChange(status);
214 }
215 }
216
217private:
218 const PadIdentifier identifier;
219 const int axis_x;
220 const int axis_y;
221 const Common::Input::AnalogProperties properties_x;
222 const Common::Input::AnalogProperties properties_y;
223 int callback_key_x;
224 int callback_key_y;
225 float last_axis_x_value;
226 float last_axis_y_value;
227 InputEngine* input_engine;
228 const bool invert_axis_y;
229};
230
231class InputFromTouch final : public Common::Input::InputDevice {
232public:
233 explicit InputFromTouch(PadIdentifier identifier_, int touch_id_, int button_, bool toggle_,
234 bool inverted_, int axis_x_, int axis_y_,
235 Common::Input::AnalogProperties properties_x_,
236 Common::Input::AnalogProperties properties_y_,
237 InputEngine* input_engine_)
238 : identifier(identifier_), touch_id(touch_id_), button(button_), toggle(toggle_),
239 inverted(inverted_), axis_x(axis_x_), axis_y(axis_y_), properties_x(properties_x_),
240 properties_y(properties_y_), input_engine(input_engine_) {
241 UpdateCallback engine_callback{[this]() { OnChange(); }};
242 const InputIdentifier button_input_identifier{
243 .identifier = identifier,
244 .type = EngineInputType::Button,
245 .index = button,
246 .callback = engine_callback,
247 };
248 const InputIdentifier x_input_identifier{
249 .identifier = identifier,
250 .type = EngineInputType::Analog,
251 .index = axis_x,
252 .callback = engine_callback,
253 };
254 const InputIdentifier y_input_identifier{
255 .identifier = identifier,
256 .type = EngineInputType::Analog,
257 .index = axis_y,
258 .callback = engine_callback,
259 };
260 last_axis_x_value = 0.0f;
261 last_axis_y_value = 0.0f;
262 last_button_value = false;
263 callback_key_button = input_engine->SetCallback(button_input_identifier);
264 callback_key_x = input_engine->SetCallback(x_input_identifier);
265 callback_key_y = input_engine->SetCallback(y_input_identifier);
266 }
267
268 ~InputFromTouch() override {
269 input_engine->DeleteCallback(callback_key_button);
270 input_engine->DeleteCallback(callback_key_x);
271 input_engine->DeleteCallback(callback_key_y);
272 }
273
274 Common::Input::TouchStatus GetStatus() const {
275 Common::Input::TouchStatus status;
276 status.id = touch_id;
277 status.pressed = {
278 .value = input_engine->GetButton(identifier, button),
279 .inverted = inverted,
280 .toggle = toggle,
281 };
282 status.x = {
283 .raw_value = input_engine->GetAxis(identifier, axis_x),
284 .properties = properties_x,
285 };
286 status.y = {
287 .raw_value = input_engine->GetAxis(identifier, axis_y),
288 .properties = properties_y,
289 };
290 return status;
291 }
292
293 void OnChange() {
294 const Common::Input::CallbackStatus status{
295 .type = Common::Input::InputType::Touch,
296 .touch_status = GetStatus(),
297 };
298
299 if (status.touch_status.x.raw_value != last_axis_x_value ||
300 status.touch_status.y.raw_value != last_axis_y_value ||
301 status.touch_status.pressed.value != last_button_value) {
302 last_axis_x_value = status.touch_status.x.raw_value;
303 last_axis_y_value = status.touch_status.y.raw_value;
304 last_button_value = status.touch_status.pressed.value;
305 TriggerOnChange(status);
306 }
307 }
308
309private:
310 const PadIdentifier identifier;
311 const int touch_id;
312 const int button;
313 const bool toggle;
314 const bool inverted;
315 const int axis_x;
316 const int axis_y;
317 const Common::Input::AnalogProperties properties_x;
318 const Common::Input::AnalogProperties properties_y;
319 int callback_key_button;
320 int callback_key_x;
321 int callback_key_y;
322 bool last_button_value;
323 float last_axis_x_value;
324 float last_axis_y_value;
325 InputEngine* input_engine;
326};
327
328class InputFromTrigger final : public Common::Input::InputDevice {
329public:
330 explicit InputFromTrigger(PadIdentifier identifier_, int button_, bool toggle_, bool inverted_,
331 int axis_, Common::Input::AnalogProperties properties_,
332 InputEngine* input_engine_)
333 : identifier(identifier_), button(button_), toggle(toggle_), inverted(inverted_),
334 axis(axis_), properties(properties_), input_engine(input_engine_) {
335 UpdateCallback engine_callback{[this]() { OnChange(); }};
336 const InputIdentifier button_input_identifier{
337 .identifier = identifier,
338 .type = EngineInputType::Button,
339 .index = button,
340 .callback = engine_callback,
341 };
342 const InputIdentifier axis_input_identifier{
343 .identifier = identifier,
344 .type = EngineInputType::Analog,
345 .index = axis,
346 .callback = engine_callback,
347 };
348 last_axis_value = 0.0f;
349 last_button_value = false;
350 callback_key_button = input_engine->SetCallback(button_input_identifier);
351 axis_callback_key = input_engine->SetCallback(axis_input_identifier);
352 }
353
354 ~InputFromTrigger() override {
355 input_engine->DeleteCallback(callback_key_button);
356 input_engine->DeleteCallback(axis_callback_key);
357 }
358
359 Common::Input::TriggerStatus GetStatus() const {
360 const Common::Input::AnalogStatus analog_status{
361 .raw_value = input_engine->GetAxis(identifier, axis),
362 .properties = properties,
363 };
364 const Common::Input::ButtonStatus button_status{
365 .value = input_engine->GetButton(identifier, button),
366 .inverted = inverted,
367 .toggle = toggle,
368 };
369 return {
370 .analog = analog_status,
371 .pressed = button_status,
372 };
373 }
374
375 void OnChange() {
376 const Common::Input::CallbackStatus status{
377 .type = Common::Input::InputType::Trigger,
378 .trigger_status = GetStatus(),
379 };
380
381 if (status.trigger_status.analog.raw_value != last_axis_value ||
382 status.trigger_status.pressed.value != last_button_value) {
383 last_axis_value = status.trigger_status.analog.raw_value;
384 last_button_value = status.trigger_status.pressed.value;
385 TriggerOnChange(status);
386 }
387 }
388
389private:
390 const PadIdentifier identifier;
391 const int button;
392 const bool toggle;
393 const bool inverted;
394 const int axis;
395 const Common::Input::AnalogProperties properties;
396 int callback_key_button;
397 int axis_callback_key;
398 bool last_button_value;
399 float last_axis_value;
400 InputEngine* input_engine;
401};
402
403class InputFromAnalog final : public Common::Input::InputDevice {
404public:
405 explicit InputFromAnalog(PadIdentifier identifier_, int axis_,
406 Common::Input::AnalogProperties properties_,
407 InputEngine* input_engine_)
408 : identifier(identifier_), axis(axis_), properties(properties_),
409 input_engine(input_engine_) {
410 UpdateCallback engine_callback{[this]() { OnChange(); }};
411 const InputIdentifier input_identifier{
412 .identifier = identifier,
413 .type = EngineInputType::Analog,
414 .index = axis,
415 .callback = engine_callback,
416 };
417 last_axis_value = 0.0f;
418 callback_key = input_engine->SetCallback(input_identifier);
419 }
420
421 ~InputFromAnalog() override {
422 input_engine->DeleteCallback(callback_key);
423 }
424
425 Common::Input::AnalogStatus GetStatus() const {
426 return {
427 .raw_value = input_engine->GetAxis(identifier, axis),
428 .properties = properties,
429 };
430 }
431
432 void OnChange() {
433 const Common::Input::CallbackStatus status{
434 .type = Common::Input::InputType::Analog,
435 .analog_status = GetStatus(),
436 };
437
438 if (status.analog_status.raw_value != last_axis_value) {
439 last_axis_value = status.analog_status.raw_value;
440 TriggerOnChange(status);
441 }
442 }
443
444private:
445 const PadIdentifier identifier;
446 const int axis;
447 const Common::Input::AnalogProperties properties;
448 int callback_key;
449 float last_axis_value;
450 InputEngine* input_engine;
451};
452
453class InputFromBattery final : public Common::Input::InputDevice {
454public:
455 explicit InputFromBattery(PadIdentifier identifier_, InputEngine* input_engine_)
456 : identifier(identifier_), input_engine(input_engine_) {
457 UpdateCallback engine_callback{[this]() { OnChange(); }};
458 const InputIdentifier input_identifier{
459 .identifier = identifier,
460 .type = EngineInputType::Battery,
461 .index = 0,
462 .callback = engine_callback,
463 };
464 last_battery_value = Common::Input::BatteryStatus::Charging;
465 callback_key = input_engine->SetCallback(input_identifier);
466 }
467
468 ~InputFromBattery() override {
469 input_engine->DeleteCallback(callback_key);
470 }
471
472 Common::Input::BatteryStatus GetStatus() const {
473 return static_cast<Common::Input::BatteryLevel>(input_engine->GetBattery(identifier));
474 }
475
476 void ForceUpdate() override {
477 const Common::Input::CallbackStatus status{
478 .type = Common::Input::InputType::Battery,
479 .battery_status = GetStatus(),
480 };
481
482 last_battery_value = status.battery_status;
483 TriggerOnChange(status);
484 }
485
486 void OnChange() {
487 const Common::Input::CallbackStatus status{
488 .type = Common::Input::InputType::Battery,
489 .battery_status = GetStatus(),
490 };
491
492 if (status.battery_status != last_battery_value) {
493 last_battery_value = status.battery_status;
494 TriggerOnChange(status);
495 }
496 }
497
498private:
499 const PadIdentifier identifier;
500 int callback_key;
501 Common::Input::BatteryStatus last_battery_value;
502 InputEngine* input_engine;
503};
504
505class InputFromMotion final : public Common::Input::InputDevice {
506public:
507 explicit InputFromMotion(PadIdentifier identifier_, int motion_sensor_,
508 InputEngine* input_engine_)
509 : identifier(identifier_), motion_sensor(motion_sensor_), input_engine(input_engine_) {
510 UpdateCallback engine_callback{[this]() { OnChange(); }};
511 const InputIdentifier input_identifier{
512 .identifier = identifier,
513 .type = EngineInputType::Motion,
514 .index = motion_sensor,
515 .callback = engine_callback,
516 };
517 callback_key = input_engine->SetCallback(input_identifier);
518 }
519
520 ~InputFromMotion() override {
521 input_engine->DeleteCallback(callback_key);
522 }
523
524 Common::Input::MotionStatus GetStatus() const {
525 const auto basic_motion = input_engine->GetMotion(identifier, motion_sensor);
526 Common::Input::MotionStatus status{};
527 const Common::Input::AnalogProperties properties = {
528 .deadzone = 0.001f,
529 .range = 1.0f,
530 .offset = 0.0f,
531 };
532 status.accel.x = {.raw_value = basic_motion.accel_x, .properties = properties};
533 status.accel.y = {.raw_value = basic_motion.accel_y, .properties = properties};
534 status.accel.z = {.raw_value = basic_motion.accel_z, .properties = properties};
535 status.gyro.x = {.raw_value = basic_motion.gyro_x, .properties = properties};
536 status.gyro.y = {.raw_value = basic_motion.gyro_y, .properties = properties};
537 status.gyro.z = {.raw_value = basic_motion.gyro_z, .properties = properties};
538 status.delta_timestamp = basic_motion.delta_timestamp;
539 return status;
540 }
541
542 void OnChange() {
543 const Common::Input::CallbackStatus status{
544 .type = Common::Input::InputType::Motion,
545 .motion_status = GetStatus(),
546 };
547
548 TriggerOnChange(status);
549 }
550
551private:
552 const PadIdentifier identifier;
553 const int motion_sensor;
554 int callback_key;
555 InputEngine* input_engine;
556};
557
558class InputFromAxisMotion final : public Common::Input::InputDevice {
559public:
560 explicit InputFromAxisMotion(PadIdentifier identifier_, int axis_x_, int axis_y_, int axis_z_,
561 Common::Input::AnalogProperties properties_x_,
562 Common::Input::AnalogProperties properties_y_,
563 Common::Input::AnalogProperties properties_z_,
564 InputEngine* input_engine_)
565 : identifier(identifier_), axis_x(axis_x_), axis_y(axis_y_), axis_z(axis_z_),
566 properties_x(properties_x_), properties_y(properties_y_), properties_z(properties_z_),
567 input_engine(input_engine_) {
568 UpdateCallback engine_callback{[this]() { OnChange(); }};
569 const InputIdentifier x_input_identifier{
570 .identifier = identifier,
571 .type = EngineInputType::Analog,
572 .index = axis_x,
573 .callback = engine_callback,
574 };
575 const InputIdentifier y_input_identifier{
576 .identifier = identifier,
577 .type = EngineInputType::Analog,
578 .index = axis_y,
579 .callback = engine_callback,
580 };
581 const InputIdentifier z_input_identifier{
582 .identifier = identifier,
583 .type = EngineInputType::Analog,
584 .index = axis_z,
585 .callback = engine_callback,
586 };
587 last_axis_x_value = 0.0f;
588 last_axis_y_value = 0.0f;
589 last_axis_z_value = 0.0f;
590 callback_key_x = input_engine->SetCallback(x_input_identifier);
591 callback_key_y = input_engine->SetCallback(y_input_identifier);
592 callback_key_z = input_engine->SetCallback(z_input_identifier);
593 }
594
595 ~InputFromAxisMotion() override {
596 input_engine->DeleteCallback(callback_key_x);
597 input_engine->DeleteCallback(callback_key_y);
598 input_engine->DeleteCallback(callback_key_z);
599 }
600
601 Common::Input::MotionStatus GetStatus() const {
602 Common::Input::MotionStatus status{};
603 status.gyro.x = {
604 .raw_value = input_engine->GetAxis(identifier, axis_x),
605 .properties = properties_x,
606 };
607 status.gyro.y = {
608 .raw_value = input_engine->GetAxis(identifier, axis_y),
609 .properties = properties_y,
610 };
611 status.gyro.z = {
612 .raw_value = input_engine->GetAxis(identifier, axis_z),
613 .properties = properties_z,
614 };
615 status.delta_timestamp = 5000;
616 status.force_update = true;
617 return status;
618 }
619
620 void ForceUpdate() override {
621 const Common::Input::CallbackStatus status{
622 .type = Common::Input::InputType::Motion,
623 .motion_status = GetStatus(),
624 };
625
626 last_axis_x_value = status.motion_status.gyro.x.raw_value;
627 last_axis_y_value = status.motion_status.gyro.y.raw_value;
628 last_axis_z_value = status.motion_status.gyro.z.raw_value;
629 TriggerOnChange(status);
630 }
631
632 void OnChange() {
633 const Common::Input::CallbackStatus status{
634 .type = Common::Input::InputType::Motion,
635 .motion_status = GetStatus(),
636 };
637
638 if (status.motion_status.gyro.x.raw_value != last_axis_x_value ||
639 status.motion_status.gyro.y.raw_value != last_axis_y_value ||
640 status.motion_status.gyro.z.raw_value != last_axis_z_value) {
641 last_axis_x_value = status.motion_status.gyro.x.raw_value;
642 last_axis_y_value = status.motion_status.gyro.y.raw_value;
643 last_axis_z_value = status.motion_status.gyro.z.raw_value;
644 TriggerOnChange(status);
645 }
646 }
647
648private:
649 const PadIdentifier identifier;
650 const int axis_x;
651 const int axis_y;
652 const int axis_z;
653 const Common::Input::AnalogProperties properties_x;
654 const Common::Input::AnalogProperties properties_y;
655 const Common::Input::AnalogProperties properties_z;
656 int callback_key_x;
657 int callback_key_y;
658 int callback_key_z;
659 float last_axis_x_value;
660 float last_axis_y_value;
661 float last_axis_z_value;
662 InputEngine* input_engine;
663};
664
665class OutputFromIdentifier final : public Common::Input::OutputDevice {
666public:
667 explicit OutputFromIdentifier(PadIdentifier identifier_, InputEngine* input_engine_)
668 : identifier(identifier_), input_engine(input_engine_) {}
669
670 void SetLED(const Common::Input::LedStatus& led_status) override {
671 input_engine->SetLeds(identifier, led_status);
672 }
673
674 Common::Input::VibrationError SetVibration(
675 const Common::Input::VibrationStatus& vibration_status) override {
676 return input_engine->SetRumble(identifier, vibration_status);
677 }
678
679 Common::Input::PollingError SetPollingMode(Common::Input::PollingMode polling_mode) override {
680 return input_engine->SetPollingMode(identifier, polling_mode);
681 }
682
683private:
684 const PadIdentifier identifier;
685 InputEngine* input_engine;
686};
687
688std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateButtonDevice(
689 const Common::ParamPackage& params) {
690 const PadIdentifier identifier = {
691 .guid = Common::UUID{params.Get("guid", "")},
692 .port = static_cast<std::size_t>(params.Get("port", 0)),
693 .pad = static_cast<std::size_t>(params.Get("pad", 0)),
694 };
695
696 const auto button_id = params.Get("button", 0);
697 const auto keyboard_key = params.Get("code", 0);
698 const auto toggle = params.Get("toggle", false);
699 const auto inverted = params.Get("inverted", false);
700 input_engine->PreSetController(identifier);
701 input_engine->PreSetButton(identifier, button_id);
702 input_engine->PreSetButton(identifier, keyboard_key);
703 if (keyboard_key != 0) {
704 return std::make_unique<InputFromButton>(identifier, keyboard_key, toggle, inverted,
705 input_engine.get());
706 }
707 return std::make_unique<InputFromButton>(identifier, button_id, toggle, inverted,
708 input_engine.get());
709}
710
711std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateHatButtonDevice(
712 const Common::ParamPackage& params) {
713 const PadIdentifier identifier = {
714 .guid = Common::UUID{params.Get("guid", "")},
715 .port = static_cast<std::size_t>(params.Get("port", 0)),
716 .pad = static_cast<std::size_t>(params.Get("pad", 0)),
717 };
718
719 const auto button_id = params.Get("hat", 0);
720 const auto direction = input_engine->GetHatButtonId(params.Get("direction", ""));
721 const auto toggle = params.Get("toggle", false);
722 const auto inverted = params.Get("inverted", false);
723
724 input_engine->PreSetController(identifier);
725 input_engine->PreSetHatButton(identifier, button_id);
726 return std::make_unique<InputFromHatButton>(identifier, button_id, direction, toggle, inverted,
727 input_engine.get());
728}
729
730std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateStickDevice(
731 const Common::ParamPackage& params) {
732 const auto deadzone = std::clamp(params.Get("deadzone", 0.15f), 0.0f, 1.0f);
733 const auto range = std::clamp(params.Get("range", 1.0f), 0.25f, 1.50f);
734 const auto threshold = std::clamp(params.Get("threshold", 0.5f), 0.0f, 1.0f);
735 const PadIdentifier identifier = {
736 .guid = Common::UUID{params.Get("guid", "")},
737 .port = static_cast<std::size_t>(params.Get("port", 0)),
738 .pad = static_cast<std::size_t>(params.Get("pad", 0)),
739 };
740
741 const auto axis_x = params.Get("axis_x", 0);
742 const Common::Input::AnalogProperties properties_x = {
743 .deadzone = deadzone,
744 .range = range,
745 .threshold = threshold,
746 .offset = std::clamp(params.Get("offset_x", 0.0f), -1.0f, 1.0f),
747 .inverted = params.Get("invert_x", "+") == "-",
748 };
749
750 const auto axis_y = params.Get("axis_y", 1);
751 const Common::Input::AnalogProperties properties_y = {
752 .deadzone = deadzone,
753 .range = range,
754 .threshold = threshold,
755 .offset = std::clamp(params.Get("offset_y", 0.0f), -1.0f, 1.0f),
756 .inverted = params.Get("invert_y", "+") != "+",
757 };
758 input_engine->PreSetController(identifier);
759 input_engine->PreSetAxis(identifier, axis_x);
760 input_engine->PreSetAxis(identifier, axis_y);
761 return std::make_unique<InputFromStick>(identifier, axis_x, axis_y, properties_x, properties_y,
762 input_engine.get());
763}
764
765std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateAnalogDevice(
766 const Common::ParamPackage& params) {
767 const PadIdentifier identifier = {
768 .guid = Common::UUID{params.Get("guid", "")},
769 .port = static_cast<std::size_t>(params.Get("port", 0)),
770 .pad = static_cast<std::size_t>(params.Get("pad", 0)),
771 };
772
773 const auto axis = params.Get("axis", 0);
774 const Common::Input::AnalogProperties properties = {
775 .deadzone = std::clamp(params.Get("deadzone", 0.0f), 0.0f, 1.0f),
776 .range = std::clamp(params.Get("range", 1.0f), 0.25f, 1.50f),
777 .threshold = std::clamp(params.Get("threshold", 0.5f), 0.0f, 1.0f),
778 .offset = std::clamp(params.Get("offset", 0.0f), -1.0f, 1.0f),
779 .inverted = params.Get("invert", "+") == "-",
780 };
781 input_engine->PreSetController(identifier);
782 input_engine->PreSetAxis(identifier, axis);
783 return std::make_unique<InputFromAnalog>(identifier, axis, properties, input_engine.get());
784}
785
786std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateTriggerDevice(
787 const Common::ParamPackage& params) {
788 const PadIdentifier identifier = {
789 .guid = Common::UUID{params.Get("guid", "")},
790 .port = static_cast<std::size_t>(params.Get("port", 0)),
791 .pad = static_cast<std::size_t>(params.Get("pad", 0)),
792 };
793
794 const auto button = params.Get("button", 0);
795 const auto toggle = params.Get("toggle", false);
796 const auto inverted = params.Get("inverted", false);
797
798 const auto axis = params.Get("axis", 0);
799 const Common::Input::AnalogProperties properties = {
800 .deadzone = std::clamp(params.Get("deadzone", 0.0f), 0.0f, 1.0f),
801 .range = std::clamp(params.Get("range", 1.0f), 0.25f, 2.50f),
802 .threshold = std::clamp(params.Get("threshold", 0.5f), 0.0f, 1.0f),
803 .offset = std::clamp(params.Get("offset", 0.0f), -1.0f, 1.0f),
804 .inverted = params.Get("invert", false) != 0,
805 };
806 input_engine->PreSetController(identifier);
807 input_engine->PreSetAxis(identifier, axis);
808 input_engine->PreSetButton(identifier, button);
809 return std::make_unique<InputFromTrigger>(identifier, button, toggle, inverted, axis,
810 properties, input_engine.get());
811}
812
813std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateTouchDevice(
814 const Common::ParamPackage& params) {
815 const auto touch_id = params.Get("touch_id", 0);
816 const auto deadzone = std::clamp(params.Get("deadzone", 0.0f), 0.0f, 1.0f);
817 const auto range = std::clamp(params.Get("range", 1.0f), 0.25f, 1.50f);
818 const auto threshold = std::clamp(params.Get("threshold", 0.5f), 0.0f, 1.0f);
819 const PadIdentifier identifier = {
820 .guid = Common::UUID{params.Get("guid", "")},
821 .port = static_cast<std::size_t>(params.Get("port", 0)),
822 .pad = static_cast<std::size_t>(params.Get("pad", 0)),
823 };
824
825 const auto button = params.Get("button", 0);
826 const auto toggle = params.Get("toggle", false);
827 const auto inverted = params.Get("inverted", false);
828
829 const auto axis_x = params.Get("axis_x", 0);
830 const Common::Input::AnalogProperties properties_x = {
831 .deadzone = deadzone,
832 .range = range,
833 .threshold = threshold,
834 .offset = std::clamp(params.Get("offset_x", 0.0f), -1.0f, 1.0f),
835 .inverted = params.Get("invert_x", "+") == "-",
836 };
837
838 const auto axis_y = params.Get("axis_y", 1);
839 const Common::Input::AnalogProperties properties_y = {
840 .deadzone = deadzone,
841 .range = range,
842 .threshold = threshold,
843 .offset = std::clamp(params.Get("offset_y", 0.0f), -1.0f, 1.0f),
844 .inverted = params.Get("invert_y", false) != 0,
845 };
846 input_engine->PreSetController(identifier);
847 input_engine->PreSetAxis(identifier, axis_x);
848 input_engine->PreSetAxis(identifier, axis_y);
849 input_engine->PreSetButton(identifier, button);
850 return std::make_unique<InputFromTouch>(identifier, touch_id, button, toggle, inverted, axis_x,
851 axis_y, properties_x, properties_y, input_engine.get());
852}
853
854std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateBatteryDevice(
855 const Common::ParamPackage& params) {
856 const PadIdentifier identifier = {
857 .guid = Common::UUID{params.Get("guid", "")},
858 .port = static_cast<std::size_t>(params.Get("port", 0)),
859 .pad = static_cast<std::size_t>(params.Get("pad", 0)),
860 };
861
862 input_engine->PreSetController(identifier);
863 return std::make_unique<InputFromBattery>(identifier, input_engine.get());
864}
865
866std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateMotionDevice(
867 Common::ParamPackage params) {
868 const PadIdentifier identifier = {
869 .guid = Common::UUID{params.Get("guid", "")},
870 .port = static_cast<std::size_t>(params.Get("port", 0)),
871 .pad = static_cast<std::size_t>(params.Get("pad", 0)),
872 };
873
874 if (params.Has("motion")) {
875 const auto motion_sensor = params.Get("motion", 0);
876 input_engine->PreSetController(identifier);
877 input_engine->PreSetMotion(identifier, motion_sensor);
878 return std::make_unique<InputFromMotion>(identifier, motion_sensor, input_engine.get());
879 }
880
881 const auto deadzone = std::clamp(params.Get("deadzone", 0.15f), 0.0f, 1.0f);
882 const auto range = std::clamp(params.Get("range", 1.0f), 0.25f, 1.50f);
883 const auto threshold = std::clamp(params.Get("threshold", 0.5f), 0.0f, 1.0f);
884
885 const auto axis_x = params.Get("axis_x", 0);
886 const Common::Input::AnalogProperties properties_x = {
887 .deadzone = deadzone,
888 .range = range,
889 .threshold = threshold,
890 .offset = std::clamp(params.Get("offset_x", 0.0f), -1.0f, 1.0f),
891 .inverted = params.Get("invert_x", "+") == "-",
892 };
893
894 const auto axis_y = params.Get("axis_y", 1);
895 const Common::Input::AnalogProperties properties_y = {
896 .deadzone = deadzone,
897 .range = range,
898 .threshold = threshold,
899 .offset = std::clamp(params.Get("offset_y", 0.0f), -1.0f, 1.0f),
900 .inverted = params.Get("invert_y", "+") != "+",
901 };
902
903 const auto axis_z = params.Get("axis_z", 1);
904 const Common::Input::AnalogProperties properties_z = {
905 .deadzone = deadzone,
906 .range = range,
907 .threshold = threshold,
908 .offset = std::clamp(params.Get("offset_z", 0.0f), -1.0f, 1.0f),
909 .inverted = params.Get("invert_z", "+") != "+",
910 };
911 input_engine->PreSetController(identifier);
912 input_engine->PreSetAxis(identifier, axis_x);
913 input_engine->PreSetAxis(identifier, axis_y);
914 input_engine->PreSetAxis(identifier, axis_z);
915 return std::make_unique<InputFromAxisMotion>(identifier, axis_x, axis_y, axis_z, properties_x,
916 properties_y, properties_z, input_engine.get());
917}
918
919InputFactory::InputFactory(std::shared_ptr<InputEngine> input_engine_)
920 : input_engine(std::move(input_engine_)) {}
921
922std::unique_ptr<Common::Input::InputDevice> InputFactory::Create(
923 const Common::ParamPackage& params) {
924 if (params.Has("battery")) {
925 return CreateBatteryDevice(params);
926 }
927 if (params.Has("button") && params.Has("axis")) {
928 return CreateTriggerDevice(params);
929 }
930 if (params.Has("button") && params.Has("axis_x") && params.Has("axis_y")) {
931 return CreateTouchDevice(params);
932 }
933 if (params.Has("button") || params.Has("code")) {
934 return CreateButtonDevice(params);
935 }
936 if (params.Has("hat")) {
937 return CreateHatButtonDevice(params);
938 }
939 if (params.Has("axis_x") && params.Has("axis_y") && params.Has("axis_z")) {
940 return CreateMotionDevice(params);
941 }
942 if (params.Has("motion")) {
943 return CreateMotionDevice(params);
944 }
945 if (params.Has("axis_x") && params.Has("axis_y")) {
946 return CreateStickDevice(params);
947 }
948 if (params.Has("axis")) {
949 return CreateAnalogDevice(params);
950 }
951 LOG_ERROR(Input, "Invalid parameters given");
952 return std::make_unique<DummyInput>();
953}
954
955OutputFactory::OutputFactory(std::shared_ptr<InputEngine> input_engine_)
956 : input_engine(std::move(input_engine_)) {}
957
958std::unique_ptr<Common::Input::OutputDevice> OutputFactory::Create(
959 const Common::ParamPackage& params) {
960 const PadIdentifier identifier = {
961 .guid = Common::UUID{params.Get("guid", "")},
962 .port = static_cast<std::size_t>(params.Get("port", 0)),
963 .pad = static_cast<std::size_t>(params.Get("pad", 0)),
964 };
965
966 input_engine->PreSetController(identifier);
967 return std::make_unique<OutputFromIdentifier>(identifier, input_engine.get());
968}
969
970} // namespace InputCommon
diff --git a/src/input_common/input_poller.h b/src/input_common/input_poller.h
new file mode 100644
index 000000000..8a0977d58
--- /dev/null
+++ b/src/input_common/input_poller.h
@@ -0,0 +1,217 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included
4
5#pragma once
6
7namespace Input {
8class InputDevice;
9
10template <typename InputDevice>
11class Factory;
12}; // namespace Input
13
14namespace InputCommon {
15class InputEngine;
16
17class OutputFactory final : public Common::Input::Factory<Common::Input::OutputDevice> {
18public:
19 explicit OutputFactory(std::shared_ptr<InputEngine> input_engine_);
20
21 /**
22 * Creates an output device from the parameters given.
23 * @param params contains parameters for creating the device:
24 * - "guid" text string for identifying controllers
25 * - "port": port of the connected device
26 * - "pad": slot of the connected controller
27 * @returns a unique output device with the parameters specified
28 */
29 std::unique_ptr<Common::Input::OutputDevice> Create(
30 const Common::ParamPackage& params) override;
31
32private:
33 std::shared_ptr<InputEngine> input_engine;
34};
35
36/**
37 * An Input factory. It receives input events and forward them to all input devices it created.
38 */
39class InputFactory final : public Common::Input::Factory<Common::Input::InputDevice> {
40public:
41 explicit InputFactory(std::shared_ptr<InputEngine> input_engine_);
42
43 /**
44 * Creates an input device from the parameters given. Identifies the type of input to be
45 * returned if it contains the following parameters:
46 * - button: Contains "button" or "code"
47 * - hat_button: Contains "hat"
48 * - analog: Contains "axis"
49 * - trigger: Contains "button" and "axis"
50 * - stick: Contains "axis_x" and "axis_y"
51 * - motion: Contains "axis_x", "axis_y" and "axis_z"
52 * - motion: Contains "motion"
53 * - touch: Contains "button", "axis_x" and "axis_y"
54 * - battery: Contains "battery"
55 * - output: Contains "output"
56 * @param params contains parameters for creating the device:
57 * - "code": the code of the keyboard key to bind with the input
58 * - "button": same as "code" but for controller buttons
59 * - "hat": similar as "button" but it's a group of hat buttons from SDL
60 * - "axis": the axis number of the axis to bind with the input
61 * - "motion": the motion number of the motion to bind with the input
62 * - "axis_x": same as axis but specifying horizontal direction
63 * - "axis_y": same as axis but specifying vertical direction
64 * - "axis_z": same as axis but specifying forward direction
65 * - "battery": Only used as a placeholder to set the input type
66 * @returns a unique input device with the parameters specified
67 */
68 std::unique_ptr<Common::Input::InputDevice> Create(const Common::ParamPackage& params) override;
69
70private:
71 /**
72 * Creates a button device from the parameters given.
73 * @param params contains parameters for creating the device:
74 * - "code": the code of the keyboard key to bind with the input
75 * - "button": same as "code" but for controller buttons
76 * - "toggle": press once to enable, press again to disable
77 * - "inverted": inverts the output of the button
78 * - "guid": text string for identifying controllers
79 * - "port": port of the connected device
80 * - "pad": slot of the connected controller
81 * @returns a unique input device with the parameters specified
82 */
83 std::unique_ptr<Common::Input::InputDevice> CreateButtonDevice(
84 const Common::ParamPackage& params);
85
86 /**
87 * Creates a hat button device from the parameters given.
88 * @param params contains parameters for creating the device:
89 * - "button": the controller hat id to bind with the input
90 * - "direction": the direction id to be detected
91 * - "toggle": press once to enable, press again to disable
92 * - "inverted": inverts the output of the button
93 * - "guid": text string for identifying controllers
94 * - "port": port of the connected device
95 * - "pad": slot of the connected controller
96 * @returns a unique input device with the parameters specified
97 */
98 std::unique_ptr<Common::Input::InputDevice> CreateHatButtonDevice(
99 const Common::ParamPackage& params);
100
101 /**
102 * Creates a stick device from the parameters given.
103 * @param params contains parameters for creating the device:
104 * - "axis_x": the controller horizontal axis id to bind with the input
105 * - "axis_y": the controller vertical axis id to bind with the input
106 * - "deadzone": the minimum required value to be detected
107 * - "range": the maximum value required to reach 100%
108 * - "threshold": the minimum required value to considered pressed
109 * - "offset_x": the amount of offset in the x axis
110 * - "offset_y": the amount of offset in the y axis
111 * - "invert_x": inverts the sign of the horizontal axis
112 * - "invert_y": inverts the sign of the vertical axis
113 * - "guid": text string for identifying controllers
114 * - "port": port of the connected device
115 * - "pad": slot of the connected controller
116 * @returns a unique input device with the parameters specified
117 */
118 std::unique_ptr<Common::Input::InputDevice> CreateStickDevice(
119 const Common::ParamPackage& params);
120
121 /**
122 * Creates an analog device from the parameters given.
123 * @param params contains parameters for creating the device:
124 * - "axis": the controller axis id to bind with the input
125 * - "deadzone": the minimum required value to be detected
126 * - "range": the maximum value required to reach 100%
127 * - "threshold": the minimum required value to considered pressed
128 * - "offset": the amount of offset in the axis
129 * - "invert": inverts the sign of the axis
130 * - "guid": text string for identifying controllers
131 * - "port": port of the connected device
132 * - "pad": slot of the connected controller
133 * @returns a unique input device with the parameters specified
134 */
135 std::unique_ptr<Common::Input::InputDevice> CreateAnalogDevice(
136 const Common::ParamPackage& params);
137
138 /**
139 * Creates a trigger device from the parameters given.
140 * @param params contains parameters for creating the device:
141 * - "button": the controller hat id to bind with the input
142 * - "direction": the direction id to be detected
143 * - "toggle": press once to enable, press again to disable
144 * - "inverted": inverts the output of the button
145 * - "axis": the controller axis id to bind with the input
146 * - "deadzone": the minimum required value to be detected
147 * - "range": the maximum value required to reach 100%
148 * - "threshold": the minimum required value to considered pressed
149 * - "offset": the amount of offset in the axis
150 * - "invert": inverts the sign of the axis
151 * - "guid": text string for identifying controllers
152 * - "port": port of the connected device
153 * - "pad": slot of the connected controller
154 * @returns a unique input device with the parameters specified
155 */
156 std::unique_ptr<Common::Input::InputDevice> CreateTriggerDevice(
157 const Common::ParamPackage& params);
158
159 /**
160 * Creates a touch device from the parameters given.
161 * @param params contains parameters for creating the device:
162 * - "button": the controller hat id to bind with the input
163 * - "direction": the direction id to be detected
164 * - "toggle": press once to enable, press again to disable
165 * - "inverted": inverts the output of the button
166 * - "axis_x": the controller horizontal axis id to bind with the input
167 * - "axis_y": the controller vertical axis id to bind with the input
168 * - "deadzone": the minimum required value to be detected
169 * - "range": the maximum value required to reach 100%
170 * - "threshold": the minimum required value to considered pressed
171 * - "offset_x": the amount of offset in the x axis
172 * - "offset_y": the amount of offset in the y axis
173 * - "invert_x": inverts the sign of the horizontal axis
174 * - "invert_y": inverts the sign of the vertical axis
175 * - "guid": text string for identifying controllers
176 * - "port": port of the connected device
177 * - "pad": slot of the connected controller
178 * @returns a unique input device with the parameters specified
179 */
180 std::unique_ptr<Common::Input::InputDevice> CreateTouchDevice(
181 const Common::ParamPackage& params);
182
183 /**
184 * Creates a battery device from the parameters given.
185 * @param params contains parameters for creating the device:
186 * - "guid": text string for identifying controllers
187 * - "port": port of the connected device
188 * - "pad": slot of the connected controller
189 * @returns a unique input device with the parameters specified
190 */
191 std::unique_ptr<Common::Input::InputDevice> CreateBatteryDevice(
192 const Common::ParamPackage& params);
193
194 /**
195 * Creates a motion device from the parameters given.
196 * @param params contains parameters for creating the device:
197 * - "axis_x": the controller horizontal axis id to bind with the input
198 * - "axis_y": the controller vertical axis id to bind with the input
199 * - "axis_z": the controller forward axis id to bind with the input
200 * - "deadzone": the minimum required value to be detected
201 * - "range": the maximum value required to reach 100%
202 * - "offset_x": the amount of offset in the x axis
203 * - "offset_y": the amount of offset in the y axis
204 * - "offset_z": the amount of offset in the z axis
205 * - "invert_x": inverts the sign of the horizontal axis
206 * - "invert_y": inverts the sign of the vertical axis
207 * - "invert_z": inverts the sign of the forward axis
208 * - "guid": text string for identifying controllers
209 * - "port": port of the connected device
210 * - "pad": slot of the connected controller
211 * @returns a unique input device with the parameters specified
212 */
213 std::unique_ptr<Common::Input::InputDevice> CreateMotionDevice(Common::ParamPackage params);
214
215 std::shared_ptr<InputEngine> input_engine;
216};
217} // namespace InputCommon
diff --git a/src/input_common/keyboard.cpp b/src/input_common/keyboard.cpp
deleted file mode 100644
index 8261e76fd..000000000
--- a/src/input_common/keyboard.cpp
+++ /dev/null
@@ -1,121 +0,0 @@
1// Copyright 2017 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <atomic>
6#include <list>
7#include <mutex>
8#include <utility>
9#include "input_common/keyboard.h"
10
11namespace InputCommon {
12
13class KeyButton final : public Input::ButtonDevice {
14public:
15 explicit KeyButton(std::shared_ptr<KeyButtonList> key_button_list_, bool toggle_)
16 : key_button_list(std::move(key_button_list_)), toggle(toggle_) {}
17
18 ~KeyButton() override;
19
20 bool GetStatus() const override {
21 if (toggle) {
22 return toggled_status.load(std::memory_order_relaxed);
23 }
24 return status.load();
25 }
26
27 void ToggleButton() {
28 if (lock) {
29 return;
30 }
31 lock = true;
32 const bool old_toggle_status = toggled_status.load();
33 toggled_status.store(!old_toggle_status);
34 }
35
36 void UnlockButton() {
37 lock = false;
38 }
39
40 friend class KeyButtonList;
41
42private:
43 std::shared_ptr<KeyButtonList> key_button_list;
44 std::atomic<bool> status{false};
45 std::atomic<bool> toggled_status{false};
46 bool lock{false};
47 const bool toggle;
48};
49
50struct KeyButtonPair {
51 int key_code;
52 KeyButton* key_button;
53};
54
55class KeyButtonList {
56public:
57 void AddKeyButton(int key_code, KeyButton* key_button) {
58 std::lock_guard guard{mutex};
59 list.push_back(KeyButtonPair{key_code, key_button});
60 }
61
62 void RemoveKeyButton(const KeyButton* key_button) {
63 std::lock_guard guard{mutex};
64 list.remove_if(
65 [key_button](const KeyButtonPair& pair) { return pair.key_button == key_button; });
66 }
67
68 void ChangeKeyStatus(int key_code, bool pressed) {
69 std::lock_guard guard{mutex};
70 for (const KeyButtonPair& pair : list) {
71 if (pair.key_code == key_code) {
72 pair.key_button->status.store(pressed);
73 if (pressed) {
74 pair.key_button->ToggleButton();
75 } else {
76 pair.key_button->UnlockButton();
77 }
78 pair.key_button->TriggerOnChange();
79 }
80 }
81 }
82
83 void ChangeAllKeyStatus(bool pressed) {
84 std::lock_guard guard{mutex};
85 for (const KeyButtonPair& pair : list) {
86 pair.key_button->status.store(pressed);
87 }
88 }
89
90private:
91 std::mutex mutex;
92 std::list<KeyButtonPair> list;
93};
94
95Keyboard::Keyboard() : key_button_list{std::make_shared<KeyButtonList>()} {}
96
97KeyButton::~KeyButton() {
98 key_button_list->RemoveKeyButton(this);
99}
100
101std::unique_ptr<Input::ButtonDevice> Keyboard::Create(const Common::ParamPackage& params) {
102 const int key_code = params.Get("code", 0);
103 const bool toggle = params.Get("toggle", false);
104 std::unique_ptr<KeyButton> button = std::make_unique<KeyButton>(key_button_list, toggle);
105 key_button_list->AddKeyButton(key_code, button.get());
106 return button;
107}
108
109void Keyboard::PressKey(int key_code) {
110 key_button_list->ChangeKeyStatus(key_code, true);
111}
112
113void Keyboard::ReleaseKey(int key_code) {
114 key_button_list->ChangeKeyStatus(key_code, false);
115}
116
117void Keyboard::ReleaseAllKeys() {
118 key_button_list->ChangeAllKeyStatus(false);
119}
120
121} // namespace InputCommon
diff --git a/src/input_common/keyboard.h b/src/input_common/keyboard.h
deleted file mode 100644
index 861950472..000000000
--- a/src/input_common/keyboard.h
+++ /dev/null
@@ -1,47 +0,0 @@
1// Copyright 2017 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <memory>
8#include "core/frontend/input.h"
9
10namespace InputCommon {
11
12class KeyButtonList;
13
14/**
15 * A button device factory representing a keyboard. It receives keyboard events and forward them
16 * to all button devices it created.
17 */
18class Keyboard final : public Input::Factory<Input::ButtonDevice> {
19public:
20 Keyboard();
21
22 /**
23 * Creates a button device from a keyboard key
24 * @param params contains parameters for creating the device:
25 * - "code": the code of the key to bind with the button
26 */
27 std::unique_ptr<Input::ButtonDevice> Create(const Common::ParamPackage& params) override;
28
29 /**
30 * Sets the status of all buttons bound with the key to pressed
31 * @param key_code the code of the key to press
32 */
33 void PressKey(int key_code);
34
35 /**
36 * Sets the status of all buttons bound with the key to released
37 * @param key_code the code of the key to release
38 */
39 void ReleaseKey(int key_code);
40
41 void ReleaseAllKeys();
42
43private:
44 std::shared_ptr<KeyButtonList> key_button_list;
45};
46
47} // namespace InputCommon
diff --git a/src/input_common/main.cpp b/src/input_common/main.cpp
index f3907c65a..940744c5f 100644
--- a/src/input_common/main.cpp
+++ b/src/input_common/main.cpp
@@ -4,146 +4,173 @@
4 4
5#include <memory> 5#include <memory>
6#include <thread> 6#include <thread>
7#include "common/input.h"
7#include "common/param_package.h" 8#include "common/param_package.h"
8#include "common/settings.h" 9#include "input_common/drivers/gc_adapter.h"
9#include "input_common/analog_from_button.h" 10#include "input_common/drivers/keyboard.h"
10#include "input_common/gcadapter/gc_adapter.h" 11#include "input_common/drivers/mouse.h"
11#include "input_common/gcadapter/gc_poller.h" 12#include "input_common/drivers/tas_input.h"
12#include "input_common/keyboard.h" 13#include "input_common/drivers/touch_screen.h"
14#include "input_common/drivers/udp_client.h"
15#include "input_common/helpers/stick_from_buttons.h"
16#include "input_common/helpers/touch_from_buttons.h"
17#include "input_common/input_engine.h"
18#include "input_common/input_mapping.h"
19#include "input_common/input_poller.h"
13#include "input_common/main.h" 20#include "input_common/main.h"
14#include "input_common/motion_from_button.h"
15#include "input_common/mouse/mouse_input.h"
16#include "input_common/mouse/mouse_poller.h"
17#include "input_common/tas/tas_input.h"
18#include "input_common/tas/tas_poller.h"
19#include "input_common/touch_from_button.h"
20#include "input_common/udp/client.h"
21#include "input_common/udp/udp.h"
22#ifdef HAVE_SDL2 21#ifdef HAVE_SDL2
23#include "input_common/sdl/sdl.h" 22#include "input_common/drivers/sdl_driver.h"
24#endif 23#endif
25 24
26namespace InputCommon { 25namespace InputCommon {
27 26
28struct InputSubsystem::Impl { 27struct InputSubsystem::Impl {
29 void Initialize() { 28 void Initialize() {
30 gcadapter = std::make_shared<GCAdapter::Adapter>(); 29 mapping_factory = std::make_shared<MappingFactory>();
31 gcbuttons = std::make_shared<GCButtonFactory>(gcadapter); 30 MappingCallback mapping_callback{[this](MappingData data) { RegisterInput(data); }};
32 Input::RegisterFactory<Input::ButtonDevice>("gcpad", gcbuttons); 31
33 gcanalog = std::make_shared<GCAnalogFactory>(gcadapter); 32 keyboard = std::make_shared<Keyboard>("keyboard");
34 Input::RegisterFactory<Input::AnalogDevice>("gcpad", gcanalog); 33 keyboard->SetMappingCallback(mapping_callback);
35 gcvibration = std::make_shared<GCVibrationFactory>(gcadapter); 34 keyboard_factory = std::make_shared<InputFactory>(keyboard);
36 Input::RegisterFactory<Input::VibrationDevice>("gcpad", gcvibration); 35 keyboard_output_factory = std::make_shared<OutputFactory>(keyboard);
37 36 Common::Input::RegisterFactory<Common::Input::InputDevice>(keyboard->GetEngineName(),
38 keyboard = std::make_shared<Keyboard>(); 37 keyboard_factory);
39 Input::RegisterFactory<Input::ButtonDevice>("keyboard", keyboard); 38 Common::Input::RegisterFactory<Common::Input::OutputDevice>(keyboard->GetEngineName(),
40 Input::RegisterFactory<Input::AnalogDevice>("analog_from_button", 39 keyboard_output_factory);
41 std::make_shared<AnalogFromButton>()); 40
42 Input::RegisterFactory<Input::MotionDevice>("keyboard", 41 mouse = std::make_shared<Mouse>("mouse");
43 std::make_shared<MotionFromButton>()); 42 mouse->SetMappingCallback(mapping_callback);
44 Input::RegisterFactory<Input::TouchDevice>("touch_from_button", 43 mouse_factory = std::make_shared<InputFactory>(mouse);
45 std::make_shared<TouchFromButtonFactory>()); 44 mouse_output_factory = std::make_shared<OutputFactory>(mouse);
45 Common::Input::RegisterFactory<Common::Input::InputDevice>(mouse->GetEngineName(),
46 mouse_factory);
47 Common::Input::RegisterFactory<Common::Input::OutputDevice>(mouse->GetEngineName(),
48 mouse_output_factory);
49
50 touch_screen = std::make_shared<TouchScreen>("touch");
51 touch_screen_factory = std::make_shared<InputFactory>(touch_screen);
52 Common::Input::RegisterFactory<Common::Input::InputDevice>(touch_screen->GetEngineName(),
53 touch_screen_factory);
54
55 gcadapter = std::make_shared<GCAdapter>("gcpad");
56 gcadapter->SetMappingCallback(mapping_callback);
57 gcadapter_input_factory = std::make_shared<InputFactory>(gcadapter);
58 gcadapter_output_factory = std::make_shared<OutputFactory>(gcadapter);
59 Common::Input::RegisterFactory<Common::Input::InputDevice>(gcadapter->GetEngineName(),
60 gcadapter_input_factory);
61 Common::Input::RegisterFactory<Common::Input::OutputDevice>(gcadapter->GetEngineName(),
62 gcadapter_output_factory);
63
64 udp_client = std::make_shared<CemuhookUDP::UDPClient>("cemuhookudp");
65 udp_client->SetMappingCallback(mapping_callback);
66 udp_client_input_factory = std::make_shared<InputFactory>(udp_client);
67 udp_client_output_factory = std::make_shared<OutputFactory>(udp_client);
68 Common::Input::RegisterFactory<Common::Input::InputDevice>(udp_client->GetEngineName(),
69 udp_client_input_factory);
70 Common::Input::RegisterFactory<Common::Input::OutputDevice>(udp_client->GetEngineName(),
71 udp_client_output_factory);
72
73 tas_input = std::make_shared<TasInput::Tas>("tas");
74 tas_input->SetMappingCallback(mapping_callback);
75 tas_input_factory = std::make_shared<InputFactory>(tas_input);
76 tas_output_factory = std::make_shared<OutputFactory>(tas_input);
77 Common::Input::RegisterFactory<Common::Input::InputDevice>(tas_input->GetEngineName(),
78 tas_input_factory);
79 Common::Input::RegisterFactory<Common::Input::OutputDevice>(tas_input->GetEngineName(),
80 tas_output_factory);
46 81
47#ifdef HAVE_SDL2 82#ifdef HAVE_SDL2
48 sdl = SDL::Init(); 83 sdl = std::make_shared<SDLDriver>("sdl");
84 sdl->SetMappingCallback(mapping_callback);
85 sdl_input_factory = std::make_shared<InputFactory>(sdl);
86 sdl_output_factory = std::make_shared<OutputFactory>(sdl);
87 Common::Input::RegisterFactory<Common::Input::InputDevice>(sdl->GetEngineName(),
88 sdl_input_factory);
89 Common::Input::RegisterFactory<Common::Input::OutputDevice>(sdl->GetEngineName(),
90 sdl_output_factory);
49#endif 91#endif
50 92
51 udp = std::make_shared<InputCommon::CemuhookUDP::Client>(); 93 Common::Input::RegisterFactory<Common::Input::InputDevice>(
52 udpmotion = std::make_shared<UDPMotionFactory>(udp); 94 "touch_from_button", std::make_shared<TouchFromButton>());
53 Input::RegisterFactory<Input::MotionDevice>("cemuhookudp", udpmotion); 95 Common::Input::RegisterFactory<Common::Input::InputDevice>(
54 udptouch = std::make_shared<UDPTouchFactory>(udp); 96 "analog_from_button", std::make_shared<StickFromButton>());
55 Input::RegisterFactory<Input::TouchDevice>("cemuhookudp", udptouch);
56
57 mouse = std::make_shared<MouseInput::Mouse>();
58 mousebuttons = std::make_shared<MouseButtonFactory>(mouse);
59 Input::RegisterFactory<Input::ButtonDevice>("mouse", mousebuttons);
60 mouseanalog = std::make_shared<MouseAnalogFactory>(mouse);
61 Input::RegisterFactory<Input::AnalogDevice>("mouse", mouseanalog);
62 mousemotion = std::make_shared<MouseMotionFactory>(mouse);
63 Input::RegisterFactory<Input::MotionDevice>("mouse", mousemotion);
64 mousetouch = std::make_shared<MouseTouchFactory>(mouse);
65 Input::RegisterFactory<Input::TouchDevice>("mouse", mousetouch);
66
67 tas = std::make_shared<TasInput::Tas>();
68 tasbuttons = std::make_shared<TasButtonFactory>(tas);
69 Input::RegisterFactory<Input::ButtonDevice>("tas", tasbuttons);
70 tasanalog = std::make_shared<TasAnalogFactory>(tas);
71 Input::RegisterFactory<Input::AnalogDevice>("tas", tasanalog);
72 } 97 }
73 98
74 void Shutdown() { 99 void Shutdown() {
75 Input::UnregisterFactory<Input::ButtonDevice>("keyboard"); 100 Common::Input::UnregisterFactory<Common::Input::InputDevice>(keyboard->GetEngineName());
76 Input::UnregisterFactory<Input::MotionDevice>("keyboard"); 101 Common::Input::UnregisterFactory<Common::Input::OutputDevice>(keyboard->GetEngineName());
77 keyboard.reset(); 102 keyboard.reset();
78 Input::UnregisterFactory<Input::AnalogDevice>("analog_from_button");
79 Input::UnregisterFactory<Input::TouchDevice>("touch_from_button");
80#ifdef HAVE_SDL2
81 sdl.reset();
82#endif
83 Input::UnregisterFactory<Input::ButtonDevice>("gcpad");
84 Input::UnregisterFactory<Input::AnalogDevice>("gcpad");
85 Input::UnregisterFactory<Input::VibrationDevice>("gcpad");
86 103
87 gcbuttons.reset(); 104 Common::Input::UnregisterFactory<Common::Input::InputDevice>(mouse->GetEngineName());
88 gcanalog.reset(); 105 Common::Input::UnregisterFactory<Common::Input::OutputDevice>(mouse->GetEngineName());
89 gcvibration.reset(); 106 mouse.reset();
90 107
91 Input::UnregisterFactory<Input::MotionDevice>("cemuhookudp"); 108 Common::Input::UnregisterFactory<Common::Input::InputDevice>(touch_screen->GetEngineName());
92 Input::UnregisterFactory<Input::TouchDevice>("cemuhookudp"); 109 touch_screen.reset();
93 110
94 udpmotion.reset(); 111 Common::Input::UnregisterFactory<Common::Input::InputDevice>(gcadapter->GetEngineName());
95 udptouch.reset(); 112 Common::Input::UnregisterFactory<Common::Input::OutputDevice>(gcadapter->GetEngineName());
113 gcadapter.reset();
96 114
97 Input::UnregisterFactory<Input::ButtonDevice>("mouse"); 115 Common::Input::UnregisterFactory<Common::Input::InputDevice>(udp_client->GetEngineName());
98 Input::UnregisterFactory<Input::AnalogDevice>("mouse"); 116 Common::Input::UnregisterFactory<Common::Input::OutputDevice>(udp_client->GetEngineName());
99 Input::UnregisterFactory<Input::MotionDevice>("mouse"); 117 udp_client.reset();
100 Input::UnregisterFactory<Input::TouchDevice>("mouse");
101 118
102 mousebuttons.reset(); 119 Common::Input::UnregisterFactory<Common::Input::InputDevice>(tas_input->GetEngineName());
103 mouseanalog.reset(); 120 Common::Input::UnregisterFactory<Common::Input::OutputDevice>(tas_input->GetEngineName());
104 mousemotion.reset(); 121 tas_input.reset();
105 mousetouch.reset();
106 122
107 Input::UnregisterFactory<Input::ButtonDevice>("tas"); 123#ifdef HAVE_SDL2
108 Input::UnregisterFactory<Input::AnalogDevice>("tas"); 124 Common::Input::UnregisterFactory<Common::Input::InputDevice>(sdl->GetEngineName());
125 Common::Input::UnregisterFactory<Common::Input::OutputDevice>(sdl->GetEngineName());
126 sdl.reset();
127#endif
109 128
110 tasbuttons.reset(); 129 Common::Input::UnregisterFactory<Common::Input::InputDevice>("touch_from_button");
111 tasanalog.reset(); 130 Common::Input::UnregisterFactory<Common::Input::InputDevice>("analog_from_button");
112 } 131 }
113 132
114 [[nodiscard]] std::vector<Common::ParamPackage> GetInputDevices() const { 133 [[nodiscard]] std::vector<Common::ParamPackage> GetInputDevices() const {
115 std::vector<Common::ParamPackage> devices = { 134 std::vector<Common::ParamPackage> devices = {
116 Common::ParamPackage{{"display", "Any"}, {"class", "any"}}, 135 Common::ParamPackage{{"display", "Any"}, {"engine", "any"}},
117 Common::ParamPackage{{"display", "Keyboard/Mouse"}, {"class", "keyboard"}},
118 }; 136 };
119 if (Settings::values.tas_enable) { 137
120 devices.emplace_back( 138 auto keyboard_devices = keyboard->GetInputDevices();
121 Common::ParamPackage{{"display", "TAS Controller"}, {"class", "tas"}}); 139 devices.insert(devices.end(), keyboard_devices.begin(), keyboard_devices.end());
122 } 140 auto mouse_devices = mouse->GetInputDevices();
141 devices.insert(devices.end(), mouse_devices.begin(), mouse_devices.end());
142 auto gcadapter_devices = gcadapter->GetInputDevices();
143 devices.insert(devices.end(), gcadapter_devices.begin(), gcadapter_devices.end());
144 auto udp_devices = udp_client->GetInputDevices();
145 devices.insert(devices.end(), udp_devices.begin(), udp_devices.end());
123#ifdef HAVE_SDL2 146#ifdef HAVE_SDL2
124 auto sdl_devices = sdl->GetInputDevices(); 147 auto sdl_devices = sdl->GetInputDevices();
125 devices.insert(devices.end(), sdl_devices.begin(), sdl_devices.end()); 148 devices.insert(devices.end(), sdl_devices.begin(), sdl_devices.end());
126#endif 149#endif
127 auto udp_devices = udp->GetInputDevices(); 150
128 devices.insert(devices.end(), udp_devices.begin(), udp_devices.end());
129 auto gcpad_devices = gcadapter->GetInputDevices();
130 devices.insert(devices.end(), gcpad_devices.begin(), gcpad_devices.end());
131 return devices; 151 return devices;
132 } 152 }
133 153
134 [[nodiscard]] AnalogMapping GetAnalogMappingForDevice( 154 [[nodiscard]] AnalogMapping GetAnalogMappingForDevice(
135 const Common::ParamPackage& params) const { 155 const Common::ParamPackage& params) const {
136 if (!params.Has("class") || params.Get("class", "") == "any") { 156 if (!params.Has("engine") || params.Get("engine", "") == "any") {
137 return {}; 157 return {};
138 } 158 }
139 if (params.Get("class", "") == "gcpad") { 159 const std::string engine = params.Get("engine", "");
160 if (engine == mouse->GetEngineName()) {
161 return mouse->GetAnalogMappingForDevice(params);
162 }
163 if (engine == gcadapter->GetEngineName()) {
140 return gcadapter->GetAnalogMappingForDevice(params); 164 return gcadapter->GetAnalogMappingForDevice(params);
141 } 165 }
142 if (params.Get("class", "") == "tas") { 166 if (engine == udp_client->GetEngineName()) {
143 return tas->GetAnalogMappingForDevice(params); 167 return udp_client->GetAnalogMappingForDevice(params);
168 }
169 if (engine == tas_input->GetEngineName()) {
170 return tas_input->GetAnalogMappingForDevice(params);
144 } 171 }
145#ifdef HAVE_SDL2 172#ifdef HAVE_SDL2
146 if (params.Get("class", "") == "sdl") { 173 if (engine == sdl->GetEngineName()) {
147 return sdl->GetAnalogMappingForDevice(params); 174 return sdl->GetAnalogMappingForDevice(params);
148 } 175 }
149#endif 176#endif
@@ -152,17 +179,21 @@ struct InputSubsystem::Impl {
152 179
153 [[nodiscard]] ButtonMapping GetButtonMappingForDevice( 180 [[nodiscard]] ButtonMapping GetButtonMappingForDevice(
154 const Common::ParamPackage& params) const { 181 const Common::ParamPackage& params) const {
155 if (!params.Has("class") || params.Get("class", "") == "any") { 182 if (!params.Has("engine") || params.Get("engine", "") == "any") {
156 return {}; 183 return {};
157 } 184 }
158 if (params.Get("class", "") == "gcpad") { 185 const std::string engine = params.Get("engine", "");
186 if (engine == gcadapter->GetEngineName()) {
159 return gcadapter->GetButtonMappingForDevice(params); 187 return gcadapter->GetButtonMappingForDevice(params);
160 } 188 }
161 if (params.Get("class", "") == "tas") { 189 if (engine == udp_client->GetEngineName()) {
162 return tas->GetButtonMappingForDevice(params); 190 return udp_client->GetButtonMappingForDevice(params);
191 }
192 if (engine == tas_input->GetEngineName()) {
193 return tas_input->GetButtonMappingForDevice(params);
163 } 194 }
164#ifdef HAVE_SDL2 195#ifdef HAVE_SDL2
165 if (params.Get("class", "") == "sdl") { 196 if (engine == sdl->GetEngineName()) {
166 return sdl->GetButtonMappingForDevice(params); 197 return sdl->GetButtonMappingForDevice(params);
167 } 198 }
168#endif 199#endif
@@ -171,40 +202,119 @@ struct InputSubsystem::Impl {
171 202
172 [[nodiscard]] MotionMapping GetMotionMappingForDevice( 203 [[nodiscard]] MotionMapping GetMotionMappingForDevice(
173 const Common::ParamPackage& params) const { 204 const Common::ParamPackage& params) const {
174 if (!params.Has("class") || params.Get("class", "") == "any") { 205 if (!params.Has("engine") || params.Get("engine", "") == "any") {
175 return {}; 206 return {};
176 } 207 }
177 if (params.Get("class", "") == "cemuhookudp") { 208 const std::string engine = params.Get("engine", "");
178 // TODO return the correct motion device 209 if (engine == udp_client->GetEngineName()) {
179 return {}; 210 return udp_client->GetMotionMappingForDevice(params);
180 } 211 }
181#ifdef HAVE_SDL2 212#ifdef HAVE_SDL2
182 if (params.Get("class", "") == "sdl") { 213 if (engine == sdl->GetEngineName()) {
183 return sdl->GetMotionMappingForDevice(params); 214 return sdl->GetMotionMappingForDevice(params);
184 } 215 }
185#endif 216#endif
186 return {}; 217 return {};
187 } 218 }
188 219
220 Common::Input::ButtonNames GetButtonName(const Common::ParamPackage& params) const {
221 if (!params.Has("engine") || params.Get("engine", "") == "any") {
222 return Common::Input::ButtonNames::Undefined;
223 }
224 const std::string engine = params.Get("engine", "");
225 if (engine == mouse->GetEngineName()) {
226 return mouse->GetUIName(params);
227 }
228 if (engine == gcadapter->GetEngineName()) {
229 return gcadapter->GetUIName(params);
230 }
231 if (engine == udp_client->GetEngineName()) {
232 return udp_client->GetUIName(params);
233 }
234 if (engine == tas_input->GetEngineName()) {
235 return tas_input->GetUIName(params);
236 }
237#ifdef HAVE_SDL2
238 if (engine == sdl->GetEngineName()) {
239 return sdl->GetUIName(params);
240 }
241#endif
242 return Common::Input::ButtonNames::Invalid;
243 }
244
245 bool IsController(const Common::ParamPackage& params) {
246 const std::string engine = params.Get("engine", "");
247 if (engine == mouse->GetEngineName()) {
248 return true;
249 }
250 if (engine == gcadapter->GetEngineName()) {
251 return true;
252 }
253 if (engine == udp_client->GetEngineName()) {
254 return true;
255 }
256 if (engine == tas_input->GetEngineName()) {
257 return true;
258 }
259#ifdef HAVE_SDL2
260 if (engine == sdl->GetEngineName()) {
261 return true;
262 }
263#endif
264 return false;
265 }
266
267 void BeginConfiguration() {
268 keyboard->BeginConfiguration();
269 mouse->BeginConfiguration();
270 gcadapter->BeginConfiguration();
271 udp_client->BeginConfiguration();
272#ifdef HAVE_SDL2
273 sdl->BeginConfiguration();
274#endif
275 }
276
277 void EndConfiguration() {
278 keyboard->EndConfiguration();
279 mouse->EndConfiguration();
280 gcadapter->EndConfiguration();
281 udp_client->EndConfiguration();
282#ifdef HAVE_SDL2
283 sdl->EndConfiguration();
284#endif
285 }
286
287 void RegisterInput(MappingData data) {
288 mapping_factory->RegisterInput(data);
289 }
290
291 std::shared_ptr<MappingFactory> mapping_factory;
292
189 std::shared_ptr<Keyboard> keyboard; 293 std::shared_ptr<Keyboard> keyboard;
294 std::shared_ptr<Mouse> mouse;
295 std::shared_ptr<GCAdapter> gcadapter;
296 std::shared_ptr<TouchScreen> touch_screen;
297 std::shared_ptr<TasInput::Tas> tas_input;
298 std::shared_ptr<CemuhookUDP::UDPClient> udp_client;
299
300 std::shared_ptr<InputFactory> keyboard_factory;
301 std::shared_ptr<InputFactory> mouse_factory;
302 std::shared_ptr<InputFactory> gcadapter_input_factory;
303 std::shared_ptr<InputFactory> touch_screen_factory;
304 std::shared_ptr<InputFactory> udp_client_input_factory;
305 std::shared_ptr<InputFactory> tas_input_factory;
306
307 std::shared_ptr<OutputFactory> keyboard_output_factory;
308 std::shared_ptr<OutputFactory> mouse_output_factory;
309 std::shared_ptr<OutputFactory> gcadapter_output_factory;
310 std::shared_ptr<OutputFactory> udp_client_output_factory;
311 std::shared_ptr<OutputFactory> tas_output_factory;
312
190#ifdef HAVE_SDL2 313#ifdef HAVE_SDL2
191 std::unique_ptr<SDL::State> sdl; 314 std::shared_ptr<SDLDriver> sdl;
315 std::shared_ptr<InputFactory> sdl_input_factory;
316 std::shared_ptr<OutputFactory> sdl_output_factory;
192#endif 317#endif
193 std::shared_ptr<GCButtonFactory> gcbuttons;
194 std::shared_ptr<GCAnalogFactory> gcanalog;
195 std::shared_ptr<GCVibrationFactory> gcvibration;
196 std::shared_ptr<UDPMotionFactory> udpmotion;
197 std::shared_ptr<UDPTouchFactory> udptouch;
198 std::shared_ptr<MouseButtonFactory> mousebuttons;
199 std::shared_ptr<MouseAnalogFactory> mouseanalog;
200 std::shared_ptr<MouseMotionFactory> mousemotion;
201 std::shared_ptr<MouseTouchFactory> mousetouch;
202 std::shared_ptr<TasButtonFactory> tasbuttons;
203 std::shared_ptr<TasAnalogFactory> tasanalog;
204 std::shared_ptr<CemuhookUDP::Client> udp;
205 std::shared_ptr<GCAdapter::Adapter> gcadapter;
206 std::shared_ptr<MouseInput::Mouse> mouse;
207 std::shared_ptr<TasInput::Tas> tas;
208}; 318};
209 319
210InputSubsystem::InputSubsystem() : impl{std::make_unique<Impl>()} {} 320InputSubsystem::InputSubsystem() : impl{std::make_unique<Impl>()} {}
@@ -227,20 +337,28 @@ const Keyboard* InputSubsystem::GetKeyboard() const {
227 return impl->keyboard.get(); 337 return impl->keyboard.get();
228} 338}
229 339
230MouseInput::Mouse* InputSubsystem::GetMouse() { 340Mouse* InputSubsystem::GetMouse() {
231 return impl->mouse.get(); 341 return impl->mouse.get();
232} 342}
233 343
234const MouseInput::Mouse* InputSubsystem::GetMouse() const { 344const Mouse* InputSubsystem::GetMouse() const {
235 return impl->mouse.get(); 345 return impl->mouse.get();
236} 346}
237 347
348TouchScreen* InputSubsystem::GetTouchScreen() {
349 return impl->touch_screen.get();
350}
351
352const TouchScreen* InputSubsystem::GetTouchScreen() const {
353 return impl->touch_screen.get();
354}
355
238TasInput::Tas* InputSubsystem::GetTas() { 356TasInput::Tas* InputSubsystem::GetTas() {
239 return impl->tas.get(); 357 return impl->tas_input.get();
240} 358}
241 359
242const TasInput::Tas* InputSubsystem::GetTas() const { 360const TasInput::Tas* InputSubsystem::GetTas() const {
243 return impl->tas.get(); 361 return impl->tas_input.get();
244} 362}
245 363
246std::vector<Common::ParamPackage> InputSubsystem::GetInputDevices() const { 364std::vector<Common::ParamPackage> InputSubsystem::GetInputDevices() const {
@@ -259,100 +377,30 @@ MotionMapping InputSubsystem::GetMotionMappingForDevice(const Common::ParamPacka
259 return impl->GetMotionMappingForDevice(device); 377 return impl->GetMotionMappingForDevice(device);
260} 378}
261 379
262GCAnalogFactory* InputSubsystem::GetGCAnalogs() { 380Common::Input::ButtonNames InputSubsystem::GetButtonName(const Common::ParamPackage& params) const {
263 return impl->gcanalog.get(); 381 return impl->GetButtonName(params);
264}
265
266const GCAnalogFactory* InputSubsystem::GetGCAnalogs() const {
267 return impl->gcanalog.get();
268}
269
270GCButtonFactory* InputSubsystem::GetGCButtons() {
271 return impl->gcbuttons.get();
272}
273
274const GCButtonFactory* InputSubsystem::GetGCButtons() const {
275 return impl->gcbuttons.get();
276}
277
278UDPMotionFactory* InputSubsystem::GetUDPMotions() {
279 return impl->udpmotion.get();
280}
281
282const UDPMotionFactory* InputSubsystem::GetUDPMotions() const {
283 return impl->udpmotion.get();
284}
285
286UDPTouchFactory* InputSubsystem::GetUDPTouch() {
287 return impl->udptouch.get();
288}
289
290const UDPTouchFactory* InputSubsystem::GetUDPTouch() const {
291 return impl->udptouch.get();
292}
293
294MouseButtonFactory* InputSubsystem::GetMouseButtons() {
295 return impl->mousebuttons.get();
296} 382}
297 383
298const MouseButtonFactory* InputSubsystem::GetMouseButtons() const { 384bool InputSubsystem::IsController(const Common::ParamPackage& params) const {
299 return impl->mousebuttons.get(); 385 return impl->IsController(params);
300} 386}
301 387
302MouseAnalogFactory* InputSubsystem::GetMouseAnalogs() { 388void InputSubsystem::ReloadInputDevices() {
303 return impl->mouseanalog.get(); 389 impl->udp_client.get()->ReloadSockets();
304}
305
306const MouseAnalogFactory* InputSubsystem::GetMouseAnalogs() const {
307 return impl->mouseanalog.get();
308}
309
310MouseMotionFactory* InputSubsystem::GetMouseMotions() {
311 return impl->mousemotion.get();
312}
313
314const MouseMotionFactory* InputSubsystem::GetMouseMotions() const {
315 return impl->mousemotion.get();
316}
317
318MouseTouchFactory* InputSubsystem::GetMouseTouch() {
319 return impl->mousetouch.get();
320}
321
322const MouseTouchFactory* InputSubsystem::GetMouseTouch() const {
323 return impl->mousetouch.get();
324}
325
326TasButtonFactory* InputSubsystem::GetTasButtons() {
327 return impl->tasbuttons.get();
328}
329
330const TasButtonFactory* InputSubsystem::GetTasButtons() const {
331 return impl->tasbuttons.get();
332}
333
334TasAnalogFactory* InputSubsystem::GetTasAnalogs() {
335 return impl->tasanalog.get();
336} 390}
337 391
338const TasAnalogFactory* InputSubsystem::GetTasAnalogs() const { 392void InputSubsystem::BeginMapping(Polling::InputType type) {
339 return impl->tasanalog.get(); 393 impl->BeginConfiguration();
394 impl->mapping_factory->BeginMapping(type);
340} 395}
341 396
342void InputSubsystem::ReloadInputDevices() { 397const Common::ParamPackage InputSubsystem::GetNextInput() const {
343 if (!impl->udp) { 398 return impl->mapping_factory->GetNextInput();
344 return;
345 }
346 impl->udp->ReloadSockets();
347} 399}
348 400
349std::vector<std::unique_ptr<Polling::DevicePoller>> InputSubsystem::GetPollers( 401void InputSubsystem::StopMapping() const {
350 [[maybe_unused]] Polling::DeviceType type) const { 402 impl->EndConfiguration();
351#ifdef HAVE_SDL2 403 impl->mapping_factory->StopMapping();
352 return impl->sdl->GetPollers(type);
353#else
354 return {};
355#endif
356} 404}
357 405
358std::string GenerateKeyboardParam(int key_code) { 406std::string GenerateKeyboardParam(int key_code) {
diff --git a/src/input_common/main.h b/src/input_common/main.h
index 6390d3f09..c6f97f691 100644
--- a/src/input_common/main.h
+++ b/src/input_common/main.h
@@ -13,6 +13,10 @@ namespace Common {
13class ParamPackage; 13class ParamPackage;
14} 14}
15 15
16namespace Common::Input {
17enum class ButtonNames;
18}
19
16namespace Settings::NativeAnalog { 20namespace Settings::NativeAnalog {
17enum Values : int; 21enum Values : int;
18} 22}
@@ -25,56 +29,26 @@ namespace Settings::NativeMotion {
25enum Values : int; 29enum Values : int;
26} 30}
27 31
28namespace MouseInput { 32namespace InputCommon {
33class Keyboard;
29class Mouse; 34class Mouse;
30} 35class TouchScreen;
36struct MappingData;
37} // namespace InputCommon
31 38
32namespace TasInput { 39namespace InputCommon::TasInput {
33class Tas; 40class Tas;
34} 41} // namespace InputCommon::TasInput
35 42
36namespace InputCommon { 43namespace InputCommon {
37namespace Polling { 44namespace Polling {
38 45/// Type of input desired for mapping purposes
39enum class DeviceType { Button, AnalogPreferred, Motion }; 46enum class InputType { None, Button, Stick, Motion, Touch };
40
41/**
42 * A class that can be used to get inputs from an input device like controllers without having to
43 * poll the device's status yourself
44 */
45class DevicePoller {
46public:
47 virtual ~DevicePoller() = default;
48 /// Setup and start polling for inputs, should be called before GetNextInput
49 /// If a device_id is provided, events should be filtered to only include events from this
50 /// device id
51 virtual void Start(const std::string& device_id = "") = 0;
52 /// Stop polling
53 virtual void Stop() = 0;
54 /**
55 * Every call to this function returns the next input recorded since calling Start
56 * @return A ParamPackage of the recorded input, which can be used to create an InputDevice.
57 * If there has been no input, the package is empty
58 */
59 virtual Common::ParamPackage GetNextInput() = 0;
60};
61} // namespace Polling 47} // namespace Polling
62 48
63class GCAnalogFactory;
64class GCButtonFactory;
65class UDPMotionFactory;
66class UDPTouchFactory;
67class MouseButtonFactory;
68class MouseAnalogFactory;
69class MouseMotionFactory;
70class MouseTouchFactory;
71class TasButtonFactory;
72class TasAnalogFactory;
73class Keyboard;
74
75/** 49/**
76 * Given a ParamPackage for a Device returned from `GetInputDevices`, attempt to get the default 50 * Given a ParamPackage for a Device returned from `GetInputDevices`, attempt to get the default
77 * mapping for the device. This is currently only implemented for the SDL backend devices. 51 * mapping for the device.
78 */ 52 */
79using AnalogMapping = std::unordered_map<Settings::NativeAnalog::Values, Common::ParamPackage>; 53using AnalogMapping = std::unordered_map<Settings::NativeAnalog::Values, Common::ParamPackage>;
80using ButtonMapping = std::unordered_map<Settings::NativeButton::Values, Common::ParamPackage>; 54using ButtonMapping = std::unordered_map<Settings::NativeButton::Values, Common::ParamPackage>;
@@ -104,20 +78,27 @@ public:
104 [[nodiscard]] const Keyboard* GetKeyboard() const; 78 [[nodiscard]] const Keyboard* GetKeyboard() const;
105 79
106 /// Retrieves the underlying mouse device. 80 /// Retrieves the underlying mouse device.
107 [[nodiscard]] MouseInput::Mouse* GetMouse(); 81 [[nodiscard]] Mouse* GetMouse();
108 82
109 /// Retrieves the underlying mouse device. 83 /// Retrieves the underlying mouse device.
110 [[nodiscard]] const MouseInput::Mouse* GetMouse() const; 84 [[nodiscard]] const Mouse* GetMouse() const;
85
86 /// Retrieves the underlying touch screen device.
87 [[nodiscard]] TouchScreen* GetTouchScreen();
111 88
112 /// Retrieves the underlying tas device. 89 /// Retrieves the underlying touch screen device.
90 [[nodiscard]] const TouchScreen* GetTouchScreen() const;
91
92 /// Retrieves the underlying tas input device.
113 [[nodiscard]] TasInput::Tas* GetTas(); 93 [[nodiscard]] TasInput::Tas* GetTas();
114 94
115 /// Retrieves the underlying tas device. 95 /// Retrieves the underlying tas input device.
116 [[nodiscard]] const TasInput::Tas* GetTas() const; 96 [[nodiscard]] const TasInput::Tas* GetTas() const;
97
117 /** 98 /**
118 * Returns all available input devices that this Factory can create a new device with. 99 * Returns all available input devices that this Factory can create a new device with.
119 * Each returned ParamPackage should have a `display` field used for display, a class field for 100 * Each returned ParamPackage should have a `display` field used for display, a `engine` field
120 * backends to determine if this backend is meant to service the request and any other 101 * for backends to determine if this backend is meant to service the request and any other
121 * information needed to identify this in the backend later. 102 * information needed to identify this in the backend later.
122 */ 103 */
123 [[nodiscard]] std::vector<Common::ParamPackage> GetInputDevices() const; 104 [[nodiscard]] std::vector<Common::ParamPackage> GetInputDevices() const;
@@ -131,83 +112,34 @@ public:
131 /// Retrieves the motion mappings for the given device. 112 /// Retrieves the motion mappings for the given device.
132 [[nodiscard]] MotionMapping GetMotionMappingForDevice(const Common::ParamPackage& device) const; 113 [[nodiscard]] MotionMapping GetMotionMappingForDevice(const Common::ParamPackage& device) const;
133 114
134 /// Retrieves the underlying GameCube analog handler. 115 /// Returns an enum contaning the name to be displayed from the input engine.
135 [[nodiscard]] GCAnalogFactory* GetGCAnalogs(); 116 [[nodiscard]] Common::Input::ButtonNames GetButtonName(
117 const Common::ParamPackage& params) const;
136 118
137 /// Retrieves the underlying GameCube analog handler. 119 /// Returns true if device is a controller.
138 [[nodiscard]] const GCAnalogFactory* GetGCAnalogs() const; 120 [[nodiscard]] bool IsController(const Common::ParamPackage& params) const;
139 121
140 /// Retrieves the underlying GameCube button handler. 122 /// Reloads the input devices.
141 [[nodiscard]] GCButtonFactory* GetGCButtons(); 123 void ReloadInputDevices();
142
143 /// Retrieves the underlying GameCube button handler.
144 [[nodiscard]] const GCButtonFactory* GetGCButtons() const;
145
146 /// Retrieves the underlying udp motion handler.
147 [[nodiscard]] UDPMotionFactory* GetUDPMotions();
148
149 /// Retrieves the underlying udp motion handler.
150 [[nodiscard]] const UDPMotionFactory* GetUDPMotions() const;
151
152 /// Retrieves the underlying udp touch handler.
153 [[nodiscard]] UDPTouchFactory* GetUDPTouch();
154
155 /// Retrieves the underlying udp touch handler.
156 [[nodiscard]] const UDPTouchFactory* GetUDPTouch() const;
157
158 /// Retrieves the underlying mouse button handler.
159 [[nodiscard]] MouseButtonFactory* GetMouseButtons();
160
161 /// Retrieves the underlying mouse button handler.
162 [[nodiscard]] const MouseButtonFactory* GetMouseButtons() const;
163
164 /// Retrieves the underlying mouse analog handler.
165 [[nodiscard]] MouseAnalogFactory* GetMouseAnalogs();
166
167 /// Retrieves the underlying mouse analog handler.
168 [[nodiscard]] const MouseAnalogFactory* GetMouseAnalogs() const;
169
170 /// Retrieves the underlying mouse motion handler.
171 [[nodiscard]] MouseMotionFactory* GetMouseMotions();
172
173 /// Retrieves the underlying mouse motion handler.
174 [[nodiscard]] const MouseMotionFactory* GetMouseMotions() const;
175
176 /// Retrieves the underlying mouse touch handler.
177 [[nodiscard]] MouseTouchFactory* GetMouseTouch();
178
179 /// Retrieves the underlying mouse touch handler.
180 [[nodiscard]] const MouseTouchFactory* GetMouseTouch() const;
181
182 /// Retrieves the underlying tas button handler.
183 [[nodiscard]] TasButtonFactory* GetTasButtons();
184
185 /// Retrieves the underlying tas button handler.
186 [[nodiscard]] const TasButtonFactory* GetTasButtons() const;
187
188 /// Retrieves the underlying tas analogs handler.
189 [[nodiscard]] TasAnalogFactory* GetTasAnalogs();
190 124
191 /// Retrieves the underlying tas analogs handler. 125 /// Start polling from all backends for a desired input type.
192 [[nodiscard]] const TasAnalogFactory* GetTasAnalogs() const; 126 void BeginMapping(Polling::InputType type);
193 127
194 /// Reloads the input devices 128 /// Returns an input event with mapping information.
195 void ReloadInputDevices(); 129 [[nodiscard]] const Common::ParamPackage GetNextInput() const;
196 130
197 /// Get all DevicePoller from all backends for a specific device type 131 /// Stop polling from all backends.
198 [[nodiscard]] std::vector<std::unique_ptr<Polling::DevicePoller>> GetPollers( 132 void StopMapping() const;
199 Polling::DeviceType type) const;
200 133
201private: 134private:
202 struct Impl; 135 struct Impl;
203 std::unique_ptr<Impl> impl; 136 std::unique_ptr<Impl> impl;
204}; 137};
205 138
206/// Generates a serialized param package for creating a keyboard button device 139/// Generates a serialized param package for creating a keyboard button device.
207std::string GenerateKeyboardParam(int key_code); 140std::string GenerateKeyboardParam(int key_code);
208 141
209/// Generates a serialized param package for creating an analog device taking input from keyboard 142/// Generates a serialized param package for creating an analog device taking input from keyboard.
210std::string GenerateAnalogParamFromKeys(int key_up, int key_down, int key_left, int key_right, 143std::string GenerateAnalogParamFromKeys(int key_up, int key_down, int key_left, int key_right,
211 int key_modifier, float modifier_scale); 144 int key_modifier, float modifier_scale);
212
213} // namespace InputCommon 145} // namespace InputCommon
diff --git a/src/input_common/motion_from_button.cpp b/src/input_common/motion_from_button.cpp
deleted file mode 100644
index 29045a673..000000000
--- a/src/input_common/motion_from_button.cpp
+++ /dev/null
@@ -1,34 +0,0 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "input_common/motion_from_button.h"
6#include "input_common/motion_input.h"
7
8namespace InputCommon {
9
10class MotionKey final : public Input::MotionDevice {
11public:
12 using Button = std::unique_ptr<Input::ButtonDevice>;
13
14 explicit MotionKey(Button key_) : key(std::move(key_)) {}
15
16 Input::MotionStatus GetStatus() const override {
17
18 if (key->GetStatus()) {
19 return motion.GetRandomMotion(2, 6);
20 }
21 return motion.GetRandomMotion(0, 0);
22 }
23
24private:
25 Button key;
26 InputCommon::MotionInput motion{0.0f, 0.0f, 0.0f};
27};
28
29std::unique_ptr<Input::MotionDevice> MotionFromButton::Create(const Common::ParamPackage& params) {
30 auto key = Input::CreateDevice<Input::ButtonDevice>(params.Serialize());
31 return std::make_unique<MotionKey>(std::move(key));
32}
33
34} // namespace InputCommon
diff --git a/src/input_common/motion_from_button.h b/src/input_common/motion_from_button.h
deleted file mode 100644
index a959046fb..000000000
--- a/src/input_common/motion_from_button.h
+++ /dev/null
@@ -1,25 +0,0 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "core/frontend/input.h"
8
9namespace InputCommon {
10
11/**
12 * An motion device factory that takes a keyboard button and uses it as a random
13 * motion device.
14 */
15class MotionFromButton final : public Input::Factory<Input::MotionDevice> {
16public:
17 /**
18 * Creates an motion device from button devices
19 * @param params contains parameters for creating the device:
20 * - "key": a serialized ParamPackage for creating a button device
21 */
22 std::unique_ptr<Input::MotionDevice> Create(const Common::ParamPackage& params) override;
23};
24
25} // namespace InputCommon
diff --git a/src/input_common/mouse/mouse_input.cpp b/src/input_common/mouse/mouse_input.cpp
deleted file mode 100644
index 3b052ffb2..000000000
--- a/src/input_common/mouse/mouse_input.cpp
+++ /dev/null
@@ -1,223 +0,0 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2+
3// Refer to the license.txt file included.
4
5#include <stop_token>
6#include <thread>
7
8#include "common/settings.h"
9#include "input_common/mouse/mouse_input.h"
10
11namespace MouseInput {
12
13Mouse::Mouse() {
14 update_thread = std::jthread([this](std::stop_token stop_token) { UpdateThread(stop_token); });
15}
16
17Mouse::~Mouse() = default;
18
19void Mouse::UpdateThread(std::stop_token stop_token) {
20 constexpr int update_time = 10;
21 while (!stop_token.stop_requested()) {
22 for (MouseInfo& info : mouse_info) {
23 const Common::Vec3f angular_direction{
24 -info.tilt_direction.y,
25 0.0f,
26 -info.tilt_direction.x,
27 };
28
29 info.motion.SetGyroscope(angular_direction * info.tilt_speed);
30 info.motion.UpdateRotation(update_time * 1000);
31 info.motion.UpdateOrientation(update_time * 1000);
32 info.tilt_speed = 0;
33 info.data.motion = info.motion.GetMotion();
34 if (Settings::values.mouse_panning) {
35 info.last_mouse_change *= 0.96f;
36 info.data.axis = {static_cast<int>(16 * info.last_mouse_change.x),
37 static_cast<int>(16 * -info.last_mouse_change.y)};
38 }
39 }
40 if (configuring) {
41 UpdateYuzuSettings();
42 }
43 if (mouse_panning_timout++ > 20) {
44 StopPanning();
45 }
46 std::this_thread::sleep_for(std::chrono::milliseconds(update_time));
47 }
48}
49
50void Mouse::UpdateYuzuSettings() {
51 if (buttons == 0) {
52 return;
53 }
54
55 mouse_queue.Push(MouseStatus{
56 .button = last_button,
57 });
58}
59
60void Mouse::PressButton(int x, int y, MouseButton button_) {
61 const auto button_index = static_cast<std::size_t>(button_);
62 if (button_index >= mouse_info.size()) {
63 return;
64 }
65
66 const auto button = 1U << button_index;
67 buttons |= static_cast<u16>(button);
68 last_button = button_;
69
70 mouse_info[button_index].mouse_origin = Common::MakeVec(x, y);
71 mouse_info[button_index].last_mouse_position = Common::MakeVec(x, y);
72 mouse_info[button_index].data.pressed = true;
73}
74
75void Mouse::StopPanning() {
76 for (MouseInfo& info : mouse_info) {
77 if (Settings::values.mouse_panning) {
78 info.data.axis = {};
79 info.tilt_speed = 0;
80 info.last_mouse_change = {};
81 }
82 }
83}
84
85void Mouse::MouseMove(int x, int y, int center_x, int center_y) {
86 for (MouseInfo& info : mouse_info) {
87 if (Settings::values.mouse_panning) {
88 auto mouse_change =
89 (Common::MakeVec(x, y) - Common::MakeVec(center_x, center_y)).Cast<float>();
90 mouse_panning_timout = 0;
91
92 if (mouse_change.y == 0 && mouse_change.x == 0) {
93 continue;
94 }
95 const auto mouse_change_length = mouse_change.Length();
96 if (mouse_change_length < 3.0f) {
97 mouse_change /= mouse_change_length / 3.0f;
98 }
99
100 info.last_mouse_change = (info.last_mouse_change * 0.91f) + (mouse_change * 0.09f);
101
102 const auto last_mouse_change_length = info.last_mouse_change.Length();
103 if (last_mouse_change_length > 8.0f) {
104 info.last_mouse_change /= last_mouse_change_length / 8.0f;
105 } else if (last_mouse_change_length < 1.0f) {
106 info.last_mouse_change = mouse_change / mouse_change.Length();
107 }
108
109 info.tilt_direction = info.last_mouse_change;
110 info.tilt_speed = info.tilt_direction.Normalize() * info.sensitivity;
111 continue;
112 }
113
114 if (info.data.pressed) {
115 const auto mouse_move = Common::MakeVec(x, y) - info.mouse_origin;
116 const auto mouse_change = Common::MakeVec(x, y) - info.last_mouse_position;
117 info.last_mouse_position = Common::MakeVec(x, y);
118 info.data.axis = {mouse_move.x, -mouse_move.y};
119
120 if (mouse_change.x == 0 && mouse_change.y == 0) {
121 info.tilt_speed = 0;
122 } else {
123 info.tilt_direction = mouse_change.Cast<float>();
124 info.tilt_speed = info.tilt_direction.Normalize() * info.sensitivity;
125 }
126 }
127 }
128}
129
130void Mouse::ReleaseButton(MouseButton button_) {
131 const auto button_index = static_cast<std::size_t>(button_);
132 if (button_index >= mouse_info.size()) {
133 return;
134 }
135
136 const auto button = 1U << button_index;
137 buttons &= static_cast<u16>(0xFF - button);
138
139 mouse_info[button_index].tilt_speed = 0;
140 mouse_info[button_index].data.pressed = false;
141 mouse_info[button_index].data.axis = {0, 0};
142}
143
144void Mouse::ReleaseAllButtons() {
145 buttons = 0;
146 for (auto& info : mouse_info) {
147 info.tilt_speed = 0;
148 info.data.pressed = false;
149 info.data.axis = {0, 0};
150 }
151}
152
153void Mouse::BeginConfiguration() {
154 buttons = 0;
155 last_button = MouseButton::Undefined;
156 mouse_queue.Clear();
157 configuring = true;
158}
159
160void Mouse::EndConfiguration() {
161 buttons = 0;
162 for (MouseInfo& info : mouse_info) {
163 info.tilt_speed = 0;
164 info.data.pressed = false;
165 info.data.axis = {0, 0};
166 }
167 last_button = MouseButton::Undefined;
168 mouse_queue.Clear();
169 configuring = false;
170}
171
172bool Mouse::ToggleButton(std::size_t button_) {
173 if (button_ >= mouse_info.size()) {
174 return false;
175 }
176 const auto button = 1U << button_;
177 const bool button_state = (toggle_buttons & button) != 0;
178 const bool button_lock = (lock_buttons & button) != 0;
179
180 if (button_lock) {
181 return button_state;
182 }
183
184 lock_buttons |= static_cast<u16>(button);
185
186 if (button_state) {
187 toggle_buttons &= static_cast<u16>(0xFF - button);
188 } else {
189 toggle_buttons |= static_cast<u16>(button);
190 }
191
192 return !button_state;
193}
194
195bool Mouse::UnlockButton(std::size_t button_) {
196 if (button_ >= mouse_info.size()) {
197 return false;
198 }
199
200 const auto button = 1U << button_;
201 const bool button_state = (toggle_buttons & button) != 0;
202
203 lock_buttons &= static_cast<u16>(0xFF - button);
204
205 return button_state;
206}
207
208Common::SPSCQueue<MouseStatus>& Mouse::GetMouseQueue() {
209 return mouse_queue;
210}
211
212const Common::SPSCQueue<MouseStatus>& Mouse::GetMouseQueue() const {
213 return mouse_queue;
214}
215
216MouseData& Mouse::GetMouseState(std::size_t button) {
217 return mouse_info[button].data;
218}
219
220const MouseData& Mouse::GetMouseState(std::size_t button) const {
221 return mouse_info[button].data;
222}
223} // namespace MouseInput
diff --git a/src/input_common/mouse/mouse_input.h b/src/input_common/mouse/mouse_input.h
deleted file mode 100644
index c8bae99c1..000000000
--- a/src/input_common/mouse/mouse_input.h
+++ /dev/null
@@ -1,116 +0,0 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <array>
8#include <mutex>
9#include <stop_token>
10#include <thread>
11
12#include "common/common_types.h"
13#include "common/threadsafe_queue.h"
14#include "common/vector_math.h"
15#include "core/frontend/input.h"
16#include "input_common/motion_input.h"
17
18namespace MouseInput {
19
20enum class MouseButton {
21 Left,
22 Right,
23 Wheel,
24 Backward,
25 Forward,
26 Task,
27 Extra,
28 Undefined,
29};
30
31struct MouseStatus {
32 MouseButton button{MouseButton::Undefined};
33};
34
35struct MouseData {
36 bool pressed{};
37 std::array<int, 2> axis{};
38 Input::MotionStatus motion{};
39 Input::TouchStatus touch{};
40};
41
42class Mouse {
43public:
44 Mouse();
45 ~Mouse();
46
47 /// Used for polling
48 void BeginConfiguration();
49 void EndConfiguration();
50
51 /**
52 * Signals that a button is pressed.
53 * @param x the x-coordinate of the cursor
54 * @param y the y-coordinate of the cursor
55 * @param button_ the button pressed
56 */
57 void PressButton(int x, int y, MouseButton button_);
58
59 /**
60 * Signals that mouse has moved.
61 * @param x the x-coordinate of the cursor
62 * @param y the y-coordinate of the cursor
63 * @param center_x the x-coordinate of the middle of the screen
64 * @param center_y the y-coordinate of the middle of the screen
65 */
66 void MouseMove(int x, int y, int center_x, int center_y);
67
68 /**
69 * Signals that a button is released.
70 * @param button_ the button pressed
71 */
72 void ReleaseButton(MouseButton button_);
73
74 /**
75 * Signals that all buttons are released
76 */
77 void ReleaseAllButtons();
78
79 [[nodiscard]] bool ToggleButton(std::size_t button_);
80 [[nodiscard]] bool UnlockButton(std::size_t button_);
81
82 [[nodiscard]] Common::SPSCQueue<MouseStatus>& GetMouseQueue();
83 [[nodiscard]] const Common::SPSCQueue<MouseStatus>& GetMouseQueue() const;
84
85 [[nodiscard]] MouseData& GetMouseState(std::size_t button);
86 [[nodiscard]] const MouseData& GetMouseState(std::size_t button) const;
87
88private:
89 void UpdateThread(std::stop_token stop_token);
90 void UpdateYuzuSettings();
91 void StopPanning();
92
93 struct MouseInfo {
94 InputCommon::MotionInput motion{0.0f, 0.0f, 0.0f};
95 Common::Vec2<int> mouse_origin;
96 Common::Vec2<int> last_mouse_position;
97 Common::Vec2<float> last_mouse_change;
98 bool is_tilting = false;
99 float sensitivity{0.120f};
100
101 float tilt_speed = 0;
102 Common::Vec2<float> tilt_direction;
103 MouseData data;
104 };
105
106 u16 buttons{};
107 u16 toggle_buttons{};
108 u16 lock_buttons{};
109 std::jthread update_thread;
110 MouseButton last_button{MouseButton::Undefined};
111 std::array<MouseInfo, 7> mouse_info;
112 Common::SPSCQueue<MouseStatus> mouse_queue;
113 bool configuring{false};
114 int mouse_panning_timout{};
115};
116} // namespace MouseInput
diff --git a/src/input_common/mouse/mouse_poller.cpp b/src/input_common/mouse/mouse_poller.cpp
deleted file mode 100644
index 090b26972..000000000
--- a/src/input_common/mouse/mouse_poller.cpp
+++ /dev/null
@@ -1,299 +0,0 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <algorithm>
6#include <memory>
7#include <mutex>
8#include <utility>
9
10#include "common/settings.h"
11#include "common/threadsafe_queue.h"
12#include "input_common/mouse/mouse_input.h"
13#include "input_common/mouse/mouse_poller.h"
14
15namespace InputCommon {
16
17class MouseButton final : public Input::ButtonDevice {
18public:
19 explicit MouseButton(u32 button_, bool toggle_, MouseInput::Mouse* mouse_input_)
20 : button(button_), toggle(toggle_), mouse_input(mouse_input_) {}
21
22 bool GetStatus() const override {
23 const bool button_state = mouse_input->GetMouseState(button).pressed;
24 if (!toggle) {
25 return button_state;
26 }
27
28 if (button_state) {
29 return mouse_input->ToggleButton(button);
30 }
31 return mouse_input->UnlockButton(button);
32 }
33
34private:
35 const u32 button;
36 const bool toggle;
37 MouseInput::Mouse* mouse_input;
38};
39
40MouseButtonFactory::MouseButtonFactory(std::shared_ptr<MouseInput::Mouse> mouse_input_)
41 : mouse_input(std::move(mouse_input_)) {}
42
43std::unique_ptr<Input::ButtonDevice> MouseButtonFactory::Create(
44 const Common::ParamPackage& params) {
45 const auto button_id = params.Get("button", 0);
46 const auto toggle = params.Get("toggle", false);
47
48 return std::make_unique<MouseButton>(button_id, toggle, mouse_input.get());
49}
50
51Common::ParamPackage MouseButtonFactory::GetNextInput() const {
52 MouseInput::MouseStatus pad;
53 Common::ParamPackage params;
54 auto& queue = mouse_input->GetMouseQueue();
55 while (queue.Pop(pad)) {
56 // This while loop will break on the earliest detected button
57 if (pad.button != MouseInput::MouseButton::Undefined) {
58 params.Set("engine", "mouse");
59 params.Set("button", static_cast<u16>(pad.button));
60 params.Set("toggle", false);
61 return params;
62 }
63 }
64 return params;
65}
66
67void MouseButtonFactory::BeginConfiguration() {
68 polling = true;
69 mouse_input->BeginConfiguration();
70}
71
72void MouseButtonFactory::EndConfiguration() {
73 polling = false;
74 mouse_input->EndConfiguration();
75}
76
77class MouseAnalog final : public Input::AnalogDevice {
78public:
79 explicit MouseAnalog(u32 port_, u32 axis_x_, u32 axis_y_, bool invert_x_, bool invert_y_,
80 float deadzone_, float range_, const MouseInput::Mouse* mouse_input_)
81 : button(port_), axis_x(axis_x_), axis_y(axis_y_), invert_x(invert_x_), invert_y(invert_y_),
82 deadzone(deadzone_), range(range_), mouse_input(mouse_input_) {}
83
84 float GetAxis(u32 axis) const {
85 std::lock_guard lock{mutex};
86 const auto axis_value =
87 static_cast<float>(mouse_input->GetMouseState(button).axis.at(axis));
88 const float sensitivity = Settings::values.mouse_panning_sensitivity.GetValue() * 0.10f;
89 return axis_value * sensitivity / (100.0f * range);
90 }
91
92 std::pair<float, float> GetAnalog(u32 analog_axis_x, u32 analog_axis_y) const {
93 float x = GetAxis(analog_axis_x);
94 float y = GetAxis(analog_axis_y);
95 if (invert_x) {
96 x = -x;
97 }
98 if (invert_y) {
99 y = -y;
100 }
101
102 // Make sure the coordinates are in the unit circle,
103 // otherwise normalize it.
104 float r = x * x + y * y;
105 if (r > 1.0f) {
106 r = std::sqrt(r);
107 x /= r;
108 y /= r;
109 }
110
111 return {x, y};
112 }
113
114 std::tuple<float, float> GetStatus() const override {
115 const auto [x, y] = GetAnalog(axis_x, axis_y);
116 const float r = std::sqrt((x * x) + (y * y));
117 if (r > deadzone) {
118 return {x / r * (r - deadzone) / (1 - deadzone),
119 y / r * (r - deadzone) / (1 - deadzone)};
120 }
121 return {0.0f, 0.0f};
122 }
123
124 std::tuple<float, float> GetRawStatus() const override {
125 const float x = GetAxis(axis_x);
126 const float y = GetAxis(axis_y);
127 return {x, y};
128 }
129
130 Input::AnalogProperties GetAnalogProperties() const override {
131 return {deadzone, range, 0.5f};
132 }
133
134private:
135 const u32 button;
136 const u32 axis_x;
137 const u32 axis_y;
138 const bool invert_x;
139 const bool invert_y;
140 const float deadzone;
141 const float range;
142 const MouseInput::Mouse* mouse_input;
143 mutable std::mutex mutex;
144};
145
146/// An analog device factory that creates analog devices from GC Adapter
147MouseAnalogFactory::MouseAnalogFactory(std::shared_ptr<MouseInput::Mouse> mouse_input_)
148 : mouse_input(std::move(mouse_input_)) {}
149
150/**
151 * Creates analog device from joystick axes
152 * @param params contains parameters for creating the device:
153 * - "port": the nth gcpad on the adapter
154 * - "axis_x": the index of the axis to be bind as x-axis
155 * - "axis_y": the index of the axis to be bind as y-axis
156 */
157std::unique_ptr<Input::AnalogDevice> MouseAnalogFactory::Create(
158 const Common::ParamPackage& params) {
159 const auto port = static_cast<u32>(params.Get("port", 0));
160 const auto axis_x = static_cast<u32>(params.Get("axis_x", 0));
161 const auto axis_y = static_cast<u32>(params.Get("axis_y", 1));
162 const auto deadzone = std::clamp(params.Get("deadzone", 0.0f), 0.0f, 1.0f);
163 const auto range = std::clamp(params.Get("range", 1.0f), 0.50f, 1.50f);
164 const std::string invert_x_value = params.Get("invert_x", "+");
165 const std::string invert_y_value = params.Get("invert_y", "+");
166 const bool invert_x = invert_x_value == "-";
167 const bool invert_y = invert_y_value == "-";
168
169 return std::make_unique<MouseAnalog>(port, axis_x, axis_y, invert_x, invert_y, deadzone, range,
170 mouse_input.get());
171}
172
173void MouseAnalogFactory::BeginConfiguration() {
174 polling = true;
175 mouse_input->BeginConfiguration();
176}
177
178void MouseAnalogFactory::EndConfiguration() {
179 polling = false;
180 mouse_input->EndConfiguration();
181}
182
183Common::ParamPackage MouseAnalogFactory::GetNextInput() const {
184 MouseInput::MouseStatus pad;
185 Common::ParamPackage params;
186 auto& queue = mouse_input->GetMouseQueue();
187 while (queue.Pop(pad)) {
188 // This while loop will break on the earliest detected button
189 if (pad.button != MouseInput::MouseButton::Undefined) {
190 params.Set("engine", "mouse");
191 params.Set("port", static_cast<u16>(pad.button));
192 params.Set("axis_x", 0);
193 params.Set("axis_y", 1);
194 params.Set("invert_x", "+");
195 params.Set("invert_y", "+");
196 return params;
197 }
198 }
199 return params;
200}
201
202class MouseMotion final : public Input::MotionDevice {
203public:
204 explicit MouseMotion(u32 button_, const MouseInput::Mouse* mouse_input_)
205 : button(button_), mouse_input(mouse_input_) {}
206
207 Input::MotionStatus GetStatus() const override {
208 return mouse_input->GetMouseState(button).motion;
209 }
210
211private:
212 const u32 button;
213 const MouseInput::Mouse* mouse_input;
214};
215
216MouseMotionFactory::MouseMotionFactory(std::shared_ptr<MouseInput::Mouse> mouse_input_)
217 : mouse_input(std::move(mouse_input_)) {}
218
219std::unique_ptr<Input::MotionDevice> MouseMotionFactory::Create(
220 const Common::ParamPackage& params) {
221 const auto button_id = params.Get("button", 0);
222
223 return std::make_unique<MouseMotion>(button_id, mouse_input.get());
224}
225
226Common::ParamPackage MouseMotionFactory::GetNextInput() const {
227 MouseInput::MouseStatus pad;
228 Common::ParamPackage params;
229 auto& queue = mouse_input->GetMouseQueue();
230 while (queue.Pop(pad)) {
231 // This while loop will break on the earliest detected button
232 if (pad.button != MouseInput::MouseButton::Undefined) {
233 params.Set("engine", "mouse");
234 params.Set("button", static_cast<u16>(pad.button));
235 return params;
236 }
237 }
238 return params;
239}
240
241void MouseMotionFactory::BeginConfiguration() {
242 polling = true;
243 mouse_input->BeginConfiguration();
244}
245
246void MouseMotionFactory::EndConfiguration() {
247 polling = false;
248 mouse_input->EndConfiguration();
249}
250
251class MouseTouch final : public Input::TouchDevice {
252public:
253 explicit MouseTouch(u32 button_, const MouseInput::Mouse* mouse_input_)
254 : button(button_), mouse_input(mouse_input_) {}
255
256 Input::TouchStatus GetStatus() const override {
257 return mouse_input->GetMouseState(button).touch;
258 }
259
260private:
261 const u32 button;
262 const MouseInput::Mouse* mouse_input;
263};
264
265MouseTouchFactory::MouseTouchFactory(std::shared_ptr<MouseInput::Mouse> mouse_input_)
266 : mouse_input(std::move(mouse_input_)) {}
267
268std::unique_ptr<Input::TouchDevice> MouseTouchFactory::Create(const Common::ParamPackage& params) {
269 const auto button_id = params.Get("button", 0);
270
271 return std::make_unique<MouseTouch>(button_id, mouse_input.get());
272}
273
274Common::ParamPackage MouseTouchFactory::GetNextInput() const {
275 MouseInput::MouseStatus pad;
276 Common::ParamPackage params;
277 auto& queue = mouse_input->GetMouseQueue();
278 while (queue.Pop(pad)) {
279 // This while loop will break on the earliest detected button
280 if (pad.button != MouseInput::MouseButton::Undefined) {
281 params.Set("engine", "mouse");
282 params.Set("button", static_cast<u16>(pad.button));
283 return params;
284 }
285 }
286 return params;
287}
288
289void MouseTouchFactory::BeginConfiguration() {
290 polling = true;
291 mouse_input->BeginConfiguration();
292}
293
294void MouseTouchFactory::EndConfiguration() {
295 polling = false;
296 mouse_input->EndConfiguration();
297}
298
299} // namespace InputCommon
diff --git a/src/input_common/mouse/mouse_poller.h b/src/input_common/mouse/mouse_poller.h
deleted file mode 100644
index cf331293b..000000000
--- a/src/input_common/mouse/mouse_poller.h
+++ /dev/null
@@ -1,109 +0,0 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <memory>
8#include "core/frontend/input.h"
9#include "input_common/mouse/mouse_input.h"
10
11namespace InputCommon {
12
13/**
14 * A button device factory representing a mouse. It receives mouse events and forward them
15 * to all button devices it created.
16 */
17class MouseButtonFactory final : public Input::Factory<Input::ButtonDevice> {
18public:
19 explicit MouseButtonFactory(std::shared_ptr<MouseInput::Mouse> mouse_input_);
20
21 /**
22 * Creates a button device from a button press
23 * @param params contains parameters for creating the device:
24 * - "code": the code of the key to bind with the button
25 */
26 std::unique_ptr<Input::ButtonDevice> Create(const Common::ParamPackage& params) override;
27
28 Common::ParamPackage GetNextInput() const;
29
30 /// For device input configuration/polling
31 void BeginConfiguration();
32 void EndConfiguration();
33
34 bool IsPolling() const {
35 return polling;
36 }
37
38private:
39 std::shared_ptr<MouseInput::Mouse> mouse_input;
40 bool polling = false;
41};
42
43/// An analog device factory that creates analog devices from mouse
44class MouseAnalogFactory final : public Input::Factory<Input::AnalogDevice> {
45public:
46 explicit MouseAnalogFactory(std::shared_ptr<MouseInput::Mouse> mouse_input_);
47
48 std::unique_ptr<Input::AnalogDevice> Create(const Common::ParamPackage& params) override;
49
50 Common::ParamPackage GetNextInput() const;
51
52 /// For device input configuration/polling
53 void BeginConfiguration();
54 void EndConfiguration();
55
56 bool IsPolling() const {
57 return polling;
58 }
59
60private:
61 std::shared_ptr<MouseInput::Mouse> mouse_input;
62 bool polling = false;
63};
64
65/// A motion device factory that creates motion devices from mouse
66class MouseMotionFactory final : public Input::Factory<Input::MotionDevice> {
67public:
68 explicit MouseMotionFactory(std::shared_ptr<MouseInput::Mouse> mouse_input_);
69
70 std::unique_ptr<Input::MotionDevice> Create(const Common::ParamPackage& params) override;
71
72 Common::ParamPackage GetNextInput() const;
73
74 /// For device input configuration/polling
75 void BeginConfiguration();
76 void EndConfiguration();
77
78 bool IsPolling() const {
79 return polling;
80 }
81
82private:
83 std::shared_ptr<MouseInput::Mouse> mouse_input;
84 bool polling = false;
85};
86
87/// An touch device factory that creates touch devices from mouse
88class MouseTouchFactory final : public Input::Factory<Input::TouchDevice> {
89public:
90 explicit MouseTouchFactory(std::shared_ptr<MouseInput::Mouse> mouse_input_);
91
92 std::unique_ptr<Input::TouchDevice> Create(const Common::ParamPackage& params) override;
93
94 Common::ParamPackage GetNextInput() const;
95
96 /// For device input configuration/polling
97 void BeginConfiguration();
98 void EndConfiguration();
99
100 bool IsPolling() const {
101 return polling;
102 }
103
104private:
105 std::shared_ptr<MouseInput::Mouse> mouse_input;
106 bool polling = false;
107};
108
109} // namespace InputCommon
diff --git a/src/input_common/sdl/sdl.cpp b/src/input_common/sdl/sdl.cpp
deleted file mode 100644
index 644db3448..000000000
--- a/src/input_common/sdl/sdl.cpp
+++ /dev/null
@@ -1,19 +0,0 @@
1// Copyright 2018 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "input_common/sdl/sdl.h"
6#ifdef HAVE_SDL2
7#include "input_common/sdl/sdl_impl.h"
8#endif
9
10namespace InputCommon::SDL {
11
12std::unique_ptr<State> Init() {
13#ifdef HAVE_SDL2
14 return std::make_unique<SDLState>();
15#else
16 return std::make_unique<NullState>();
17#endif
18}
19} // namespace InputCommon::SDL
diff --git a/src/input_common/sdl/sdl.h b/src/input_common/sdl/sdl.h
deleted file mode 100644
index b5d41bba4..000000000
--- a/src/input_common/sdl/sdl.h
+++ /dev/null
@@ -1,51 +0,0 @@
1// Copyright 2018 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <memory>
8#include <vector>
9#include "common/param_package.h"
10#include "input_common/main.h"
11
12namespace InputCommon::Polling {
13class DevicePoller;
14enum class DeviceType;
15} // namespace InputCommon::Polling
16
17namespace InputCommon::SDL {
18
19class State {
20public:
21 using Pollers = std::vector<std::unique_ptr<Polling::DevicePoller>>;
22
23 /// Unregisters SDL device factories and shut them down.
24 virtual ~State() = default;
25
26 virtual Pollers GetPollers(Polling::DeviceType) {
27 return {};
28 }
29
30 virtual std::vector<Common::ParamPackage> GetInputDevices() {
31 return {};
32 }
33
34 virtual ButtonMapping GetButtonMappingForDevice(const Common::ParamPackage&) {
35 return {};
36 }
37 virtual AnalogMapping GetAnalogMappingForDevice(const Common::ParamPackage&) {
38 return {};
39 }
40 virtual MotionMapping GetMotionMappingForDevice(const Common::ParamPackage&) {
41 return {};
42 }
43};
44
45class NullState : public State {
46public:
47};
48
49std::unique_ptr<State> Init();
50
51} // namespace InputCommon::SDL
diff --git a/src/input_common/sdl/sdl_impl.cpp b/src/input_common/sdl/sdl_impl.cpp
deleted file mode 100644
index ecb00d428..000000000
--- a/src/input_common/sdl/sdl_impl.cpp
+++ /dev/null
@@ -1,1658 +0,0 @@
1// Copyright 2018 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <algorithm>
6#include <array>
7#include <atomic>
8#include <chrono>
9#include <cmath>
10#include <functional>
11#include <mutex>
12#include <optional>
13#include <sstream>
14#include <string>
15#include <thread>
16#include <tuple>
17#include <unordered_map>
18#include <utility>
19#include <vector>
20
21#include "common/logging/log.h"
22#include "common/math_util.h"
23#include "common/param_package.h"
24#include "common/settings.h"
25#include "common/threadsafe_queue.h"
26#include "core/frontend/input.h"
27#include "input_common/motion_input.h"
28#include "input_common/sdl/sdl_impl.h"
29
30namespace InputCommon::SDL {
31
32namespace {
33std::string GetGUID(SDL_Joystick* joystick) {
34 const SDL_JoystickGUID guid = SDL_JoystickGetGUID(joystick);
35 char guid_str[33];
36 SDL_JoystickGetGUIDString(guid, guid_str, sizeof(guid_str));
37 return guid_str;
38}
39
40/// Creates a ParamPackage from an SDL_Event that can directly be used to create a ButtonDevice
41Common::ParamPackage SDLEventToButtonParamPackage(SDLState& state, const SDL_Event& event);
42} // Anonymous namespace
43
44static int SDLEventWatcher(void* user_data, SDL_Event* event) {
45 auto* const sdl_state = static_cast<SDLState*>(user_data);
46
47 // Don't handle the event if we are configuring
48 if (sdl_state->polling) {
49 sdl_state->event_queue.Push(*event);
50 } else {
51 sdl_state->HandleGameControllerEvent(*event);
52 }
53
54 return 0;
55}
56
57class SDLJoystick {
58public:
59 SDLJoystick(std::string guid_, int port_, SDL_Joystick* joystick,
60 SDL_GameController* game_controller)
61 : guid{std::move(guid_)}, port{port_}, sdl_joystick{joystick, &SDL_JoystickClose},
62 sdl_controller{game_controller, &SDL_GameControllerClose} {
63 EnableMotion();
64 }
65
66 void EnableMotion() {
67 if (sdl_controller) {
68 SDL_GameController* controller = sdl_controller.get();
69 if (SDL_GameControllerHasSensor(controller, SDL_SENSOR_ACCEL) && !has_accel) {
70 SDL_GameControllerSetSensorEnabled(controller, SDL_SENSOR_ACCEL, SDL_TRUE);
71 has_accel = true;
72 }
73 if (SDL_GameControllerHasSensor(controller, SDL_SENSOR_GYRO) && !has_gyro) {
74 SDL_GameControllerSetSensorEnabled(controller, SDL_SENSOR_GYRO, SDL_TRUE);
75 has_gyro = true;
76 }
77 }
78 }
79
80 void SetButton(int button, bool value) {
81 std::lock_guard lock{mutex};
82 state.buttons.insert_or_assign(button, value);
83 }
84
85 void PreSetButton(int button) {
86 if (!state.buttons.contains(button)) {
87 SetButton(button, false);
88 }
89 }
90
91 void SetMotion(SDL_ControllerSensorEvent event) {
92 constexpr float gravity_constant = 9.80665f;
93 std::lock_guard lock{mutex};
94 u64 time_difference = event.timestamp - last_motion_update;
95 last_motion_update = event.timestamp;
96 switch (event.sensor) {
97 case SDL_SENSOR_ACCEL: {
98 const Common::Vec3f acceleration = {-event.data[0], event.data[2], -event.data[1]};
99 motion.SetAcceleration(acceleration / gravity_constant);
100 break;
101 }
102 case SDL_SENSOR_GYRO: {
103 const Common::Vec3f gyroscope = {event.data[0], -event.data[2], event.data[1]};
104 motion.SetGyroscope(gyroscope / (Common::PI * 2));
105 break;
106 }
107 }
108
109 // Ignore duplicated timestamps
110 if (time_difference == 0) {
111 return;
112 }
113
114 motion.SetGyroThreshold(0.0001f);
115 motion.UpdateRotation(time_difference * 1000);
116 motion.UpdateOrientation(time_difference * 1000);
117 }
118
119 bool GetButton(int button) const {
120 std::lock_guard lock{mutex};
121 return state.buttons.at(button);
122 }
123
124 bool ToggleButton(int button) {
125 std::lock_guard lock{mutex};
126
127 if (!state.toggle_buttons.contains(button) || !state.lock_buttons.contains(button)) {
128 state.toggle_buttons.insert_or_assign(button, false);
129 state.lock_buttons.insert_or_assign(button, false);
130 }
131
132 const bool button_state = state.toggle_buttons.at(button);
133 const bool button_lock = state.lock_buttons.at(button);
134
135 if (button_lock) {
136 return button_state;
137 }
138
139 state.lock_buttons.insert_or_assign(button, true);
140
141 if (button_state) {
142 state.toggle_buttons.insert_or_assign(button, false);
143 } else {
144 state.toggle_buttons.insert_or_assign(button, true);
145 }
146
147 return !button_state;
148 }
149
150 bool UnlockButton(int button) {
151 std::lock_guard lock{mutex};
152 if (!state.toggle_buttons.contains(button)) {
153 return false;
154 }
155 state.lock_buttons.insert_or_assign(button, false);
156 return state.toggle_buttons.at(button);
157 }
158
159 void SetAxis(int axis, Sint16 value) {
160 std::lock_guard lock{mutex};
161 state.axes.insert_or_assign(axis, value);
162 }
163
164 void PreSetAxis(int axis) {
165 if (!state.axes.contains(axis)) {
166 SetAxis(axis, 0);
167 }
168 }
169
170 float GetAxis(int axis, float range, float offset) const {
171 std::lock_guard lock{mutex};
172 const float value = static_cast<float>(state.axes.at(axis)) / 32767.0f;
173 const float offset_scale = (value + offset) > 0.0f ? 1.0f + offset : 1.0f - offset;
174 return (value + offset) / range / offset_scale;
175 }
176
177 bool RumblePlay(u16 amp_low, u16 amp_high) {
178 constexpr u32 rumble_max_duration_ms = 1000;
179
180 if (sdl_controller) {
181 return SDL_GameControllerRumble(sdl_controller.get(), amp_low, amp_high,
182 rumble_max_duration_ms) != -1;
183 } else if (sdl_joystick) {
184 return SDL_JoystickRumble(sdl_joystick.get(), amp_low, amp_high,
185 rumble_max_duration_ms) != -1;
186 }
187
188 return false;
189 }
190
191 std::tuple<float, float> GetAnalog(int axis_x, int axis_y, float range, float offset_x,
192 float offset_y) const {
193 float x = GetAxis(axis_x, range, offset_x);
194 float y = GetAxis(axis_y, range, offset_y);
195 y = -y; // 3DS uses an y-axis inverse from SDL
196
197 // Make sure the coordinates are in the unit circle,
198 // otherwise normalize it.
199 float r = x * x + y * y;
200 if (r > 1.0f) {
201 r = std::sqrt(r);
202 x /= r;
203 y /= r;
204 }
205
206 return std::make_tuple(x, y);
207 }
208
209 bool HasGyro() const {
210 return has_gyro;
211 }
212
213 bool HasAccel() const {
214 return has_accel;
215 }
216
217 const MotionInput& GetMotion() const {
218 return motion;
219 }
220
221 void SetHat(int hat, Uint8 direction) {
222 std::lock_guard lock{mutex};
223 state.hats.insert_or_assign(hat, direction);
224 }
225
226 bool GetHatDirection(int hat, Uint8 direction) const {
227 std::lock_guard lock{mutex};
228 return (state.hats.at(hat) & direction) != 0;
229 }
230 /**
231 * The guid of the joystick
232 */
233 const std::string& GetGUID() const {
234 return guid;
235 }
236
237 /**
238 * The number of joystick from the same type that were connected before this joystick
239 */
240 int GetPort() const {
241 return port;
242 }
243
244 SDL_Joystick* GetSDLJoystick() const {
245 return sdl_joystick.get();
246 }
247
248 SDL_GameController* GetSDLGameController() const {
249 return sdl_controller.get();
250 }
251
252 void SetSDLJoystick(SDL_Joystick* joystick, SDL_GameController* controller) {
253 sdl_joystick.reset(joystick);
254 sdl_controller.reset(controller);
255 }
256
257 bool IsJoyconLeft() const {
258 const std::string controller_name = GetControllerName();
259 if (std::strstr(controller_name.c_str(), "Joy-Con Left") != nullptr) {
260 return true;
261 }
262 if (std::strstr(controller_name.c_str(), "Joy-Con (L)") != nullptr) {
263 return true;
264 }
265 return false;
266 }
267
268 bool IsJoyconRight() const {
269 const std::string controller_name = GetControllerName();
270 if (std::strstr(controller_name.c_str(), "Joy-Con Right") != nullptr) {
271 return true;
272 }
273 if (std::strstr(controller_name.c_str(), "Joy-Con (R)") != nullptr) {
274 return true;
275 }
276 return false;
277 }
278
279 std::string GetControllerName() const {
280 if (sdl_controller) {
281 switch (SDL_GameControllerGetType(sdl_controller.get())) {
282 case SDL_CONTROLLER_TYPE_XBOX360:
283 return "XBox 360 Controller";
284 case SDL_CONTROLLER_TYPE_XBOXONE:
285 return "XBox One Controller";
286 default:
287 break;
288 }
289 const auto name = SDL_GameControllerName(sdl_controller.get());
290 if (name) {
291 return name;
292 }
293 }
294
295 if (sdl_joystick) {
296 const auto name = SDL_JoystickName(sdl_joystick.get());
297 if (name) {
298 return name;
299 }
300 }
301
302 return "Unknown";
303 }
304
305private:
306 struct State {
307 std::unordered_map<int, bool> buttons;
308 std::unordered_map<int, bool> toggle_buttons{};
309 std::unordered_map<int, bool> lock_buttons{};
310 std::unordered_map<int, Sint16> axes;
311 std::unordered_map<int, Uint8> hats;
312 } state;
313 std::string guid;
314 int port;
315 std::unique_ptr<SDL_Joystick, decltype(&SDL_JoystickClose)> sdl_joystick;
316 std::unique_ptr<SDL_GameController, decltype(&SDL_GameControllerClose)> sdl_controller;
317 mutable std::mutex mutex;
318
319 // Motion is initialized with the PID values
320 MotionInput motion{0.3f, 0.005f, 0.0f};
321 u64 last_motion_update{};
322 bool has_gyro{false};
323 bool has_accel{false};
324};
325
326std::shared_ptr<SDLJoystick> SDLState::GetSDLJoystickByGUID(const std::string& guid, int port) {
327 std::lock_guard lock{joystick_map_mutex};
328 const auto it = joystick_map.find(guid);
329
330 if (it != joystick_map.end()) {
331 while (it->second.size() <= static_cast<std::size_t>(port)) {
332 auto joystick = std::make_shared<SDLJoystick>(guid, static_cast<int>(it->second.size()),
333 nullptr, nullptr);
334 it->second.emplace_back(std::move(joystick));
335 }
336
337 return it->second[static_cast<std::size_t>(port)];
338 }
339
340 auto joystick = std::make_shared<SDLJoystick>(guid, 0, nullptr, nullptr);
341
342 return joystick_map[guid].emplace_back(std::move(joystick));
343}
344
345std::shared_ptr<SDLJoystick> SDLState::GetSDLJoystickBySDLID(SDL_JoystickID sdl_id) {
346 auto sdl_joystick = SDL_JoystickFromInstanceID(sdl_id);
347 const std::string guid = GetGUID(sdl_joystick);
348
349 std::lock_guard lock{joystick_map_mutex};
350 const auto map_it = joystick_map.find(guid);
351
352 if (map_it == joystick_map.end()) {
353 return nullptr;
354 }
355
356 const auto vec_it = std::find_if(map_it->second.begin(), map_it->second.end(),
357 [&sdl_joystick](const auto& joystick) {
358 return joystick->GetSDLJoystick() == sdl_joystick;
359 });
360
361 if (vec_it == map_it->second.end()) {
362 return nullptr;
363 }
364
365 return *vec_it;
366}
367
368void SDLState::InitJoystick(int joystick_index) {
369 SDL_Joystick* sdl_joystick = SDL_JoystickOpen(joystick_index);
370 SDL_GameController* sdl_gamecontroller = nullptr;
371
372 if (SDL_IsGameController(joystick_index)) {
373 sdl_gamecontroller = SDL_GameControllerOpen(joystick_index);
374 }
375
376 if (!sdl_joystick) {
377 LOG_ERROR(Input, "Failed to open joystick {}", joystick_index);
378 return;
379 }
380
381 const std::string guid = GetGUID(sdl_joystick);
382
383 std::lock_guard lock{joystick_map_mutex};
384 if (joystick_map.find(guid) == joystick_map.end()) {
385 auto joystick = std::make_shared<SDLJoystick>(guid, 0, sdl_joystick, sdl_gamecontroller);
386 joystick_map[guid].emplace_back(std::move(joystick));
387 return;
388 }
389
390 auto& joystick_guid_list = joystick_map[guid];
391 const auto joystick_it =
392 std::find_if(joystick_guid_list.begin(), joystick_guid_list.end(),
393 [](const auto& joystick) { return !joystick->GetSDLJoystick(); });
394
395 if (joystick_it != joystick_guid_list.end()) {
396 (*joystick_it)->SetSDLJoystick(sdl_joystick, sdl_gamecontroller);
397 return;
398 }
399
400 const int port = static_cast<int>(joystick_guid_list.size());
401 auto joystick = std::make_shared<SDLJoystick>(guid, port, sdl_joystick, sdl_gamecontroller);
402 joystick_guid_list.emplace_back(std::move(joystick));
403}
404
405void SDLState::CloseJoystick(SDL_Joystick* sdl_joystick) {
406 const std::string guid = GetGUID(sdl_joystick);
407
408 std::lock_guard lock{joystick_map_mutex};
409 // This call to guid is safe since the joystick is guaranteed to be in the map
410 const auto& joystick_guid_list = joystick_map[guid];
411 const auto joystick_it = std::find_if(joystick_guid_list.begin(), joystick_guid_list.end(),
412 [&sdl_joystick](const auto& joystick) {
413 return joystick->GetSDLJoystick() == sdl_joystick;
414 });
415
416 if (joystick_it != joystick_guid_list.end()) {
417 (*joystick_it)->SetSDLJoystick(nullptr, nullptr);
418 }
419}
420
421void SDLState::HandleGameControllerEvent(const SDL_Event& event) {
422 switch (event.type) {
423 case SDL_JOYBUTTONUP: {
424 if (auto joystick = GetSDLJoystickBySDLID(event.jbutton.which)) {
425 joystick->SetButton(event.jbutton.button, false);
426 }
427 break;
428 }
429 case SDL_JOYBUTTONDOWN: {
430 if (auto joystick = GetSDLJoystickBySDLID(event.jbutton.which)) {
431 joystick->SetButton(event.jbutton.button, true);
432 }
433 break;
434 }
435 case SDL_JOYHATMOTION: {
436 if (auto joystick = GetSDLJoystickBySDLID(event.jhat.which)) {
437 joystick->SetHat(event.jhat.hat, event.jhat.value);
438 }
439 break;
440 }
441 case SDL_JOYAXISMOTION: {
442 if (auto joystick = GetSDLJoystickBySDLID(event.jaxis.which)) {
443 joystick->SetAxis(event.jaxis.axis, event.jaxis.value);
444 }
445 break;
446 }
447 case SDL_CONTROLLERSENSORUPDATE: {
448 if (auto joystick = GetSDLJoystickBySDLID(event.csensor.which)) {
449 joystick->SetMotion(event.csensor);
450 }
451 break;
452 }
453 case SDL_JOYDEVICEREMOVED:
454 LOG_DEBUG(Input, "Controller removed with Instance_ID {}", event.jdevice.which);
455 CloseJoystick(SDL_JoystickFromInstanceID(event.jdevice.which));
456 break;
457 case SDL_JOYDEVICEADDED:
458 LOG_DEBUG(Input, "Controller connected with device index {}", event.jdevice.which);
459 InitJoystick(event.jdevice.which);
460 break;
461 }
462}
463
464void SDLState::CloseJoysticks() {
465 std::lock_guard lock{joystick_map_mutex};
466 joystick_map.clear();
467}
468
469class SDLButton final : public Input::ButtonDevice {
470public:
471 explicit SDLButton(std::shared_ptr<SDLJoystick> joystick_, int button_, bool toggle_)
472 : joystick(std::move(joystick_)), button(button_), toggle(toggle_) {}
473
474 bool GetStatus() const override {
475 const bool button_state = joystick->GetButton(button);
476 if (!toggle) {
477 return button_state;
478 }
479
480 if (button_state) {
481 return joystick->ToggleButton(button);
482 }
483 return joystick->UnlockButton(button);
484 }
485
486private:
487 std::shared_ptr<SDLJoystick> joystick;
488 int button;
489 bool toggle;
490};
491
492class SDLDirectionButton final : public Input::ButtonDevice {
493public:
494 explicit SDLDirectionButton(std::shared_ptr<SDLJoystick> joystick_, int hat_, Uint8 direction_)
495 : joystick(std::move(joystick_)), hat(hat_), direction(direction_) {}
496
497 bool GetStatus() const override {
498 return joystick->GetHatDirection(hat, direction);
499 }
500
501private:
502 std::shared_ptr<SDLJoystick> joystick;
503 int hat;
504 Uint8 direction;
505};
506
507class SDLAxisButton final : public Input::ButtonDevice {
508public:
509 explicit SDLAxisButton(std::shared_ptr<SDLJoystick> joystick_, int axis_, float threshold_,
510 bool trigger_if_greater_)
511 : joystick(std::move(joystick_)), axis(axis_), threshold(threshold_),
512 trigger_if_greater(trigger_if_greater_) {}
513
514 bool GetStatus() const override {
515 const float axis_value = joystick->GetAxis(axis, 1.0f, 0.0f);
516 if (trigger_if_greater) {
517 return axis_value > threshold;
518 }
519 return axis_value < threshold;
520 }
521
522private:
523 std::shared_ptr<SDLJoystick> joystick;
524 int axis;
525 float threshold;
526 bool trigger_if_greater;
527};
528
529class SDLAnalog final : public Input::AnalogDevice {
530public:
531 explicit SDLAnalog(std::shared_ptr<SDLJoystick> joystick_, int axis_x_, int axis_y_,
532 bool invert_x_, bool invert_y_, float deadzone_, float range_,
533 float offset_x_, float offset_y_)
534 : joystick(std::move(joystick_)), axis_x(axis_x_), axis_y(axis_y_), invert_x(invert_x_),
535 invert_y(invert_y_), deadzone(deadzone_), range(range_), offset_x(offset_x_),
536 offset_y(offset_y_) {}
537
538 std::tuple<float, float> GetStatus() const override {
539 auto [x, y] = joystick->GetAnalog(axis_x, axis_y, range, offset_x, offset_y);
540 const float r = std::sqrt((x * x) + (y * y));
541 if (invert_x) {
542 x = -x;
543 }
544 if (invert_y) {
545 y = -y;
546 }
547
548 if (r > deadzone) {
549 return std::make_tuple(x / r * (r - deadzone) / (1 - deadzone),
550 y / r * (r - deadzone) / (1 - deadzone));
551 }
552 return {};
553 }
554
555 std::tuple<float, float> GetRawStatus() const override {
556 const float x = joystick->GetAxis(axis_x, range, offset_x);
557 const float y = joystick->GetAxis(axis_y, range, offset_y);
558 return {x, -y};
559 }
560
561 Input::AnalogProperties GetAnalogProperties() const override {
562 return {deadzone, range, 0.5f};
563 }
564
565 bool GetAnalogDirectionStatus(Input::AnalogDirection direction) const override {
566 const auto [x, y] = GetStatus();
567 const float directional_deadzone = 0.5f;
568 switch (direction) {
569 case Input::AnalogDirection::RIGHT:
570 return x > directional_deadzone;
571 case Input::AnalogDirection::LEFT:
572 return x < -directional_deadzone;
573 case Input::AnalogDirection::UP:
574 return y > directional_deadzone;
575 case Input::AnalogDirection::DOWN:
576 return y < -directional_deadzone;
577 }
578 return false;
579 }
580
581private:
582 std::shared_ptr<SDLJoystick> joystick;
583 const int axis_x;
584 const int axis_y;
585 const bool invert_x;
586 const bool invert_y;
587 const float deadzone;
588 const float range;
589 const float offset_x;
590 const float offset_y;
591};
592
593class SDLVibration final : public Input::VibrationDevice {
594public:
595 explicit SDLVibration(std::shared_ptr<SDLJoystick> joystick_)
596 : joystick(std::move(joystick_)) {}
597
598 u8 GetStatus() const override {
599 joystick->RumblePlay(1, 1);
600 return joystick->RumblePlay(0, 0);
601 }
602
603 bool SetRumblePlay(f32 amp_low, [[maybe_unused]] f32 freq_low, f32 amp_high,
604 [[maybe_unused]] f32 freq_high) const override {
605 const auto process_amplitude = [](f32 amplitude) {
606 return static_cast<u16>((amplitude + std::pow(amplitude, 0.3f)) * 0.5f * 0xFFFF);
607 };
608
609 const auto processed_amp_low = process_amplitude(amp_low);
610 const auto processed_amp_high = process_amplitude(amp_high);
611
612 return joystick->RumblePlay(processed_amp_low, processed_amp_high);
613 }
614
615private:
616 std::shared_ptr<SDLJoystick> joystick;
617};
618
619class SDLMotion final : public Input::MotionDevice {
620public:
621 explicit SDLMotion(std::shared_ptr<SDLJoystick> joystick_) : joystick(std::move(joystick_)) {}
622
623 Input::MotionStatus GetStatus() const override {
624 return joystick->GetMotion().GetMotion();
625 }
626
627private:
628 std::shared_ptr<SDLJoystick> joystick;
629};
630
631class SDLDirectionMotion final : public Input::MotionDevice {
632public:
633 explicit SDLDirectionMotion(std::shared_ptr<SDLJoystick> joystick_, int hat_, Uint8 direction_)
634 : joystick(std::move(joystick_)), hat(hat_), direction(direction_) {}
635
636 Input::MotionStatus GetStatus() const override {
637 if (joystick->GetHatDirection(hat, direction)) {
638 return joystick->GetMotion().GetRandomMotion(2, 6);
639 }
640 return joystick->GetMotion().GetRandomMotion(0, 0);
641 }
642
643private:
644 std::shared_ptr<SDLJoystick> joystick;
645 int hat;
646 Uint8 direction;
647};
648
649class SDLAxisMotion final : public Input::MotionDevice {
650public:
651 explicit SDLAxisMotion(std::shared_ptr<SDLJoystick> joystick_, int axis_, float threshold_,
652 bool trigger_if_greater_)
653 : joystick(std::move(joystick_)), axis(axis_), threshold(threshold_),
654 trigger_if_greater(trigger_if_greater_) {}
655
656 Input::MotionStatus GetStatus() const override {
657 const float axis_value = joystick->GetAxis(axis, 1.0f, 0.0f);
658 bool trigger = axis_value < threshold;
659 if (trigger_if_greater) {
660 trigger = axis_value > threshold;
661 }
662
663 if (trigger) {
664 return joystick->GetMotion().GetRandomMotion(2, 6);
665 }
666 return joystick->GetMotion().GetRandomMotion(0, 0);
667 }
668
669private:
670 std::shared_ptr<SDLJoystick> joystick;
671 int axis;
672 float threshold;
673 bool trigger_if_greater;
674};
675
676class SDLButtonMotion final : public Input::MotionDevice {
677public:
678 explicit SDLButtonMotion(std::shared_ptr<SDLJoystick> joystick_, int button_)
679 : joystick(std::move(joystick_)), button(button_) {}
680
681 Input::MotionStatus GetStatus() const override {
682 if (joystick->GetButton(button)) {
683 return joystick->GetMotion().GetRandomMotion(2, 6);
684 }
685 return joystick->GetMotion().GetRandomMotion(0, 0);
686 }
687
688private:
689 std::shared_ptr<SDLJoystick> joystick;
690 int button;
691};
692
693/// A button device factory that creates button devices from SDL joystick
694class SDLButtonFactory final : public Input::Factory<Input::ButtonDevice> {
695public:
696 explicit SDLButtonFactory(SDLState& state_) : state(state_) {}
697
698 /**
699 * Creates a button device from a joystick button
700 * @param params contains parameters for creating the device:
701 * - "guid": the guid of the joystick to bind
702 * - "port": the nth joystick of the same type to bind
703 * - "button"(optional): the index of the button to bind
704 * - "hat"(optional): the index of the hat to bind as direction buttons
705 * - "axis"(optional): the index of the axis to bind
706 * - "direction"(only used for hat): the direction name of the hat to bind. Can be "up",
707 * "down", "left" or "right"
708 * - "threshold"(only used for axis): a float value in (-1.0, 1.0) which the button is
709 * triggered if the axis value crosses
710 * - "direction"(only used for axis): "+" means the button is triggered when the axis
711 * value is greater than the threshold; "-" means the button is triggered when the axis
712 * value is smaller than the threshold
713 */
714 std::unique_ptr<Input::ButtonDevice> Create(const Common::ParamPackage& params) override {
715 const std::string guid = params.Get("guid", "0");
716 const int port = params.Get("port", 0);
717 const auto toggle = params.Get("toggle", false);
718
719 auto joystick = state.GetSDLJoystickByGUID(guid, port);
720
721 if (params.Has("hat")) {
722 const int hat = params.Get("hat", 0);
723 const std::string direction_name = params.Get("direction", "");
724 Uint8 direction;
725 if (direction_name == "up") {
726 direction = SDL_HAT_UP;
727 } else if (direction_name == "down") {
728 direction = SDL_HAT_DOWN;
729 } else if (direction_name == "left") {
730 direction = SDL_HAT_LEFT;
731 } else if (direction_name == "right") {
732 direction = SDL_HAT_RIGHT;
733 } else {
734 direction = 0;
735 }
736 // This is necessary so accessing GetHat with hat won't crash
737 joystick->SetHat(hat, SDL_HAT_CENTERED);
738 return std::make_unique<SDLDirectionButton>(joystick, hat, direction);
739 }
740
741 if (params.Has("axis")) {
742 const int axis = params.Get("axis", 0);
743 // Convert range from (0.0, 1.0) to (-1.0, 1.0)
744 const float threshold = (params.Get("threshold", 0.5f) - 0.5f) * 2.0f;
745 const std::string direction_name = params.Get("direction", "");
746 bool trigger_if_greater;
747 if (direction_name == "+") {
748 trigger_if_greater = true;
749 } else if (direction_name == "-") {
750 trigger_if_greater = false;
751 } else {
752 trigger_if_greater = true;
753 LOG_ERROR(Input, "Unknown direction {}", direction_name);
754 }
755 // This is necessary so accessing GetAxis with axis won't crash
756 joystick->PreSetAxis(axis);
757 return std::make_unique<SDLAxisButton>(joystick, axis, threshold, trigger_if_greater);
758 }
759
760 const int button = params.Get("button", 0);
761 // This is necessary so accessing GetButton with button won't crash
762 joystick->PreSetButton(button);
763 return std::make_unique<SDLButton>(joystick, button, toggle);
764 }
765
766private:
767 SDLState& state;
768};
769
770/// An analog device factory that creates analog devices from SDL joystick
771class SDLAnalogFactory final : public Input::Factory<Input::AnalogDevice> {
772public:
773 explicit SDLAnalogFactory(SDLState& state_) : state(state_) {}
774 /**
775 * Creates an analog device from joystick axes
776 * @param params contains parameters for creating the device:
777 * - "guid": the guid of the joystick to bind
778 * - "port": the nth joystick of the same type
779 * - "axis_x": the index of the axis to be bind as x-axis
780 * - "axis_y": the index of the axis to be bind as y-axis
781 */
782 std::unique_ptr<Input::AnalogDevice> Create(const Common::ParamPackage& params) override {
783 const std::string guid = params.Get("guid", "0");
784 const int port = params.Get("port", 0);
785 const int axis_x = params.Get("axis_x", 0);
786 const int axis_y = params.Get("axis_y", 1);
787 const float deadzone = std::clamp(params.Get("deadzone", 0.0f), 0.0f, 1.0f);
788 const float range = std::clamp(params.Get("range", 1.0f), 0.50f, 1.50f);
789 const std::string invert_x_value = params.Get("invert_x", "+");
790 const std::string invert_y_value = params.Get("invert_y", "+");
791 const bool invert_x = invert_x_value == "-";
792 const bool invert_y = invert_y_value == "-";
793 const float offset_x = std::clamp(params.Get("offset_x", 0.0f), -0.99f, 0.99f);
794 const float offset_y = std::clamp(params.Get("offset_y", 0.0f), -0.99f, 0.99f);
795 auto joystick = state.GetSDLJoystickByGUID(guid, port);
796
797 // This is necessary so accessing GetAxis with axis_x and axis_y won't crash
798 joystick->PreSetAxis(axis_x);
799 joystick->PreSetAxis(axis_y);
800 return std::make_unique<SDLAnalog>(joystick, axis_x, axis_y, invert_x, invert_y, deadzone,
801 range, offset_x, offset_y);
802 }
803
804private:
805 SDLState& state;
806};
807
808/// An vibration device factory that creates vibration devices from SDL joystick
809class SDLVibrationFactory final : public Input::Factory<Input::VibrationDevice> {
810public:
811 explicit SDLVibrationFactory(SDLState& state_) : state(state_) {}
812 /**
813 * Creates a vibration device from a joystick
814 * @param params contains parameters for creating the device:
815 * - "guid": the guid of the joystick to bind
816 * - "port": the nth joystick of the same type
817 */
818 std::unique_ptr<Input::VibrationDevice> Create(const Common::ParamPackage& params) override {
819 const std::string guid = params.Get("guid", "0");
820 const int port = params.Get("port", 0);
821 return std::make_unique<SDLVibration>(state.GetSDLJoystickByGUID(guid, port));
822 }
823
824private:
825 SDLState& state;
826};
827
828/// A motion device factory that creates motion devices from SDL joystick
829class SDLMotionFactory final : public Input::Factory<Input::MotionDevice> {
830public:
831 explicit SDLMotionFactory(SDLState& state_) : state(state_) {}
832 /**
833 * Creates motion device from joystick axes
834 * @param params contains parameters for creating the device:
835 * - "guid": the guid of the joystick to bind
836 * - "port": the nth joystick of the same type
837 */
838 std::unique_ptr<Input::MotionDevice> Create(const Common::ParamPackage& params) override {
839 const std::string guid = params.Get("guid", "0");
840 const int port = params.Get("port", 0);
841
842 auto joystick = state.GetSDLJoystickByGUID(guid, port);
843
844 if (params.Has("motion")) {
845 return std::make_unique<SDLMotion>(joystick);
846 }
847
848 if (params.Has("hat")) {
849 const int hat = params.Get("hat", 0);
850 const std::string direction_name = params.Get("direction", "");
851 Uint8 direction;
852 if (direction_name == "up") {
853 direction = SDL_HAT_UP;
854 } else if (direction_name == "down") {
855 direction = SDL_HAT_DOWN;
856 } else if (direction_name == "left") {
857 direction = SDL_HAT_LEFT;
858 } else if (direction_name == "right") {
859 direction = SDL_HAT_RIGHT;
860 } else {
861 direction = 0;
862 }
863 // This is necessary so accessing GetHat with hat won't crash
864 joystick->SetHat(hat, SDL_HAT_CENTERED);
865 return std::make_unique<SDLDirectionMotion>(joystick, hat, direction);
866 }
867
868 if (params.Has("axis")) {
869 const int axis = params.Get("axis", 0);
870 const float threshold = params.Get("threshold", 0.5f);
871 const std::string direction_name = params.Get("direction", "");
872 bool trigger_if_greater;
873 if (direction_name == "+") {
874 trigger_if_greater = true;
875 } else if (direction_name == "-") {
876 trigger_if_greater = false;
877 } else {
878 trigger_if_greater = true;
879 LOG_ERROR(Input, "Unknown direction {}", direction_name);
880 }
881 // This is necessary so accessing GetAxis with axis won't crash
882 joystick->PreSetAxis(axis);
883 return std::make_unique<SDLAxisMotion>(joystick, axis, threshold, trigger_if_greater);
884 }
885
886 const int button = params.Get("button", 0);
887 // This is necessary so accessing GetButton with button won't crash
888 joystick->PreSetButton(button);
889 return std::make_unique<SDLButtonMotion>(joystick, button);
890 }
891
892private:
893 SDLState& state;
894};
895
896SDLState::SDLState() {
897 using namespace Input;
898 button_factory = std::make_shared<SDLButtonFactory>(*this);
899 analog_factory = std::make_shared<SDLAnalogFactory>(*this);
900 vibration_factory = std::make_shared<SDLVibrationFactory>(*this);
901 motion_factory = std::make_shared<SDLMotionFactory>(*this);
902 RegisterFactory<ButtonDevice>("sdl", button_factory);
903 RegisterFactory<AnalogDevice>("sdl", analog_factory);
904 RegisterFactory<VibrationDevice>("sdl", vibration_factory);
905 RegisterFactory<MotionDevice>("sdl", motion_factory);
906
907 if (!Settings::values.enable_raw_input) {
908 // Disable raw input. When enabled this setting causes SDL to die when a web applet opens
909 SDL_SetHint(SDL_HINT_JOYSTICK_RAWINPUT, "0");
910 }
911
912 // Enable HIDAPI rumble. This prevents SDL from disabling motion on PS4 and PS5 controllers
913 SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS4_RUMBLE, "1");
914 SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS5_RUMBLE, "1");
915
916 // Tell SDL2 to use the hidapi driver. This will allow joycons to be detected as a
917 // GameController and not a generic one
918 SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_JOY_CONS, "1");
919
920 // Turn off Pro controller home led
921 SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_SWITCH_HOME_LED, "0");
922
923 // If the frontend is going to manage the event loop, then we don't start one here
924 start_thread = SDL_WasInit(SDL_INIT_JOYSTICK) == 0;
925 if (start_thread && SDL_Init(SDL_INIT_JOYSTICK) < 0) {
926 LOG_CRITICAL(Input, "SDL_Init(SDL_INIT_JOYSTICK) failed with: {}", SDL_GetError());
927 return;
928 }
929 has_gamecontroller = SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER) != 0;
930 if (SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1") == SDL_FALSE) {
931 LOG_ERROR(Input, "Failed to set hint for background events with: {}", SDL_GetError());
932 }
933
934 SDL_AddEventWatch(&SDLEventWatcher, this);
935
936 initialized = true;
937 if (start_thread) {
938 poll_thread = std::thread([this] {
939 using namespace std::chrono_literals;
940 while (initialized) {
941 SDL_PumpEvents();
942 std::this_thread::sleep_for(1ms);
943 }
944 });
945 }
946 // Because the events for joystick connection happens before we have our event watcher added, we
947 // can just open all the joysticks right here
948 for (int i = 0; i < SDL_NumJoysticks(); ++i) {
949 InitJoystick(i);
950 }
951}
952
953SDLState::~SDLState() {
954 using namespace Input;
955 UnregisterFactory<ButtonDevice>("sdl");
956 UnregisterFactory<AnalogDevice>("sdl");
957 UnregisterFactory<VibrationDevice>("sdl");
958 UnregisterFactory<MotionDevice>("sdl");
959
960 CloseJoysticks();
961 SDL_DelEventWatch(&SDLEventWatcher, this);
962
963 initialized = false;
964 if (start_thread) {
965 poll_thread.join();
966 SDL_QuitSubSystem(SDL_INIT_JOYSTICK);
967 }
968}
969
970std::vector<Common::ParamPackage> SDLState::GetInputDevices() {
971 std::scoped_lock lock(joystick_map_mutex);
972 std::vector<Common::ParamPackage> devices;
973 std::unordered_map<int, std::shared_ptr<SDLJoystick>> joycon_pairs;
974 for (const auto& [key, value] : joystick_map) {
975 for (const auto& joystick : value) {
976 if (!joystick->GetSDLJoystick()) {
977 continue;
978 }
979 std::string name =
980 fmt::format("{} {}", joystick->GetControllerName(), joystick->GetPort());
981 devices.emplace_back(Common::ParamPackage{
982 {"class", "sdl"},
983 {"display", std::move(name)},
984 {"guid", joystick->GetGUID()},
985 {"port", std::to_string(joystick->GetPort())},
986 });
987 if (joystick->IsJoyconLeft()) {
988 joycon_pairs.insert_or_assign(joystick->GetPort(), joystick);
989 }
990 }
991 }
992
993 // Add dual controllers
994 for (const auto& [key, value] : joystick_map) {
995 for (const auto& joystick : value) {
996 if (joystick->IsJoyconRight()) {
997 if (!joycon_pairs.contains(joystick->GetPort())) {
998 continue;
999 }
1000 const auto joystick2 = joycon_pairs.at(joystick->GetPort());
1001
1002 std::string name =
1003 fmt::format("{} {}", "Nintendo Dual Joy-Con", joystick->GetPort());
1004 devices.emplace_back(Common::ParamPackage{
1005 {"class", "sdl"},
1006 {"display", std::move(name)},
1007 {"guid", joystick->GetGUID()},
1008 {"guid2", joystick2->GetGUID()},
1009 {"port", std::to_string(joystick->GetPort())},
1010 });
1011 }
1012 }
1013 }
1014 return devices;
1015}
1016
1017namespace {
1018Common::ParamPackage BuildAnalogParamPackageForButton(int port, std::string guid, s32 axis,
1019 float value = 0.1f) {
1020 Common::ParamPackage params({{"engine", "sdl"}});
1021 params.Set("port", port);
1022 params.Set("guid", std::move(guid));
1023 params.Set("axis", axis);
1024 params.Set("threshold", "0.5");
1025 if (value > 0) {
1026 params.Set("direction", "+");
1027 } else {
1028 params.Set("direction", "-");
1029 }
1030 return params;
1031}
1032
1033Common::ParamPackage BuildButtonParamPackageForButton(int port, std::string guid, s32 button) {
1034 Common::ParamPackage params({{"engine", "sdl"}});
1035 params.Set("port", port);
1036 params.Set("guid", std::move(guid));
1037 params.Set("button", button);
1038 params.Set("toggle", false);
1039 return params;
1040}
1041
1042Common::ParamPackage BuildHatParamPackageForButton(int port, std::string guid, s32 hat, s32 value) {
1043 Common::ParamPackage params({{"engine", "sdl"}});
1044
1045 params.Set("port", port);
1046 params.Set("guid", std::move(guid));
1047 params.Set("hat", hat);
1048 switch (value) {
1049 case SDL_HAT_UP:
1050 params.Set("direction", "up");
1051 break;
1052 case SDL_HAT_DOWN:
1053 params.Set("direction", "down");
1054 break;
1055 case SDL_HAT_LEFT:
1056 params.Set("direction", "left");
1057 break;
1058 case SDL_HAT_RIGHT:
1059 params.Set("direction", "right");
1060 break;
1061 default:
1062 return {};
1063 }
1064 return params;
1065}
1066
1067Common::ParamPackage BuildMotionParam(int port, std::string guid) {
1068 Common::ParamPackage params({{"engine", "sdl"}, {"motion", "0"}});
1069 params.Set("port", port);
1070 params.Set("guid", std::move(guid));
1071 return params;
1072}
1073
1074Common::ParamPackage SDLEventToButtonParamPackage(SDLState& state, const SDL_Event& event) {
1075 switch (event.type) {
1076 case SDL_JOYAXISMOTION: {
1077 if (const auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which)) {
1078 return BuildAnalogParamPackageForButton(joystick->GetPort(), joystick->GetGUID(),
1079 static_cast<s32>(event.jaxis.axis),
1080 event.jaxis.value);
1081 }
1082 break;
1083 }
1084 case SDL_JOYBUTTONUP: {
1085 if (const auto joystick = state.GetSDLJoystickBySDLID(event.jbutton.which)) {
1086 return BuildButtonParamPackageForButton(joystick->GetPort(), joystick->GetGUID(),
1087 static_cast<s32>(event.jbutton.button));
1088 }
1089 break;
1090 }
1091 case SDL_JOYHATMOTION: {
1092 if (const auto joystick = state.GetSDLJoystickBySDLID(event.jhat.which)) {
1093 return BuildHatParamPackageForButton(joystick->GetPort(), joystick->GetGUID(),
1094 static_cast<s32>(event.jhat.hat),
1095 static_cast<s32>(event.jhat.value));
1096 }
1097 break;
1098 }
1099 }
1100 return {};
1101}
1102
1103Common::ParamPackage SDLEventToMotionParamPackage(SDLState& state, const SDL_Event& event) {
1104 switch (event.type) {
1105 case SDL_JOYAXISMOTION: {
1106 if (const auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which)) {
1107 return BuildAnalogParamPackageForButton(joystick->GetPort(), joystick->GetGUID(),
1108 static_cast<s32>(event.jaxis.axis),
1109 event.jaxis.value);
1110 }
1111 break;
1112 }
1113 case SDL_JOYBUTTONUP: {
1114 if (const auto joystick = state.GetSDLJoystickBySDLID(event.jbutton.which)) {
1115 return BuildButtonParamPackageForButton(joystick->GetPort(), joystick->GetGUID(),
1116 static_cast<s32>(event.jbutton.button));
1117 }
1118 break;
1119 }
1120 case SDL_JOYHATMOTION: {
1121 if (const auto joystick = state.GetSDLJoystickBySDLID(event.jhat.which)) {
1122 return BuildHatParamPackageForButton(joystick->GetPort(), joystick->GetGUID(),
1123 static_cast<s32>(event.jhat.hat),
1124 static_cast<s32>(event.jhat.value));
1125 }
1126 break;
1127 }
1128 case SDL_CONTROLLERSENSORUPDATE: {
1129 bool is_motion_shaking = false;
1130 constexpr float gyro_threshold = 5.0f;
1131 constexpr float accel_threshold = 11.0f;
1132 if (event.csensor.sensor == SDL_SENSOR_ACCEL) {
1133 const Common::Vec3f acceleration = {-event.csensor.data[0], event.csensor.data[2],
1134 -event.csensor.data[1]};
1135 if (acceleration.Length() > accel_threshold) {
1136 is_motion_shaking = true;
1137 }
1138 }
1139
1140 if (event.csensor.sensor == SDL_SENSOR_GYRO) {
1141 const Common::Vec3f gyroscope = {event.csensor.data[0], -event.csensor.data[2],
1142 event.csensor.data[1]};
1143 if (gyroscope.Length() > gyro_threshold) {
1144 is_motion_shaking = true;
1145 }
1146 }
1147
1148 if (!is_motion_shaking) {
1149 break;
1150 }
1151
1152 if (const auto joystick = state.GetSDLJoystickBySDLID(event.csensor.which)) {
1153 return BuildMotionParam(joystick->GetPort(), joystick->GetGUID());
1154 }
1155 break;
1156 }
1157 }
1158 return {};
1159}
1160
1161Common::ParamPackage BuildParamPackageForBinding(int port, const std::string& guid,
1162 const SDL_GameControllerButtonBind& binding) {
1163 switch (binding.bindType) {
1164 case SDL_CONTROLLER_BINDTYPE_NONE:
1165 break;
1166 case SDL_CONTROLLER_BINDTYPE_AXIS:
1167 return BuildAnalogParamPackageForButton(port, guid, binding.value.axis);
1168 case SDL_CONTROLLER_BINDTYPE_BUTTON:
1169 return BuildButtonParamPackageForButton(port, guid, binding.value.button);
1170 case SDL_CONTROLLER_BINDTYPE_HAT:
1171 return BuildHatParamPackageForButton(port, guid, binding.value.hat.hat,
1172 binding.value.hat.hat_mask);
1173 }
1174 return {};
1175}
1176
1177Common::ParamPackage BuildParamPackageForAnalog(int port, const std::string& guid, int axis_x,
1178 int axis_y, float offset_x, float offset_y) {
1179 Common::ParamPackage params;
1180 params.Set("engine", "sdl");
1181 params.Set("port", port);
1182 params.Set("guid", guid);
1183 params.Set("axis_x", axis_x);
1184 params.Set("axis_y", axis_y);
1185 params.Set("offset_x", offset_x);
1186 params.Set("offset_y", offset_y);
1187 params.Set("invert_x", "+");
1188 params.Set("invert_y", "+");
1189 return params;
1190}
1191} // Anonymous namespace
1192
1193ButtonMapping SDLState::GetButtonMappingForDevice(const Common::ParamPackage& params) {
1194 if (!params.Has("guid") || !params.Has("port")) {
1195 return {};
1196 }
1197 const auto joystick = GetSDLJoystickByGUID(params.Get("guid", ""), params.Get("port", 0));
1198
1199 auto* controller = joystick->GetSDLGameController();
1200 if (controller == nullptr) {
1201 return {};
1202 }
1203
1204 // This list is missing ZL/ZR since those are not considered buttons in SDL GameController.
1205 // We will add those afterwards
1206 // This list also excludes Screenshot since theres not really a mapping for that
1207 ButtonBindings switch_to_sdl_button;
1208
1209 if (SDL_GameControllerGetType(controller) == SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_PRO) {
1210 switch_to_sdl_button = GetNintendoButtonBinding(joystick);
1211 } else {
1212 switch_to_sdl_button = GetDefaultButtonBinding();
1213 }
1214
1215 // Add the missing bindings for ZL/ZR
1216 static constexpr ZButtonBindings switch_to_sdl_axis{{
1217 {Settings::NativeButton::ZL, SDL_CONTROLLER_AXIS_TRIGGERLEFT},
1218 {Settings::NativeButton::ZR, SDL_CONTROLLER_AXIS_TRIGGERRIGHT},
1219 }};
1220
1221 // Parameters contain two joysticks return dual
1222 if (params.Has("guid2")) {
1223 const auto joystick2 = GetSDLJoystickByGUID(params.Get("guid2", ""), params.Get("port", 0));
1224
1225 if (joystick2->GetSDLGameController() != nullptr) {
1226 return GetDualControllerMapping(joystick, joystick2, switch_to_sdl_button,
1227 switch_to_sdl_axis);
1228 }
1229 }
1230
1231 return GetSingleControllerMapping(joystick, switch_to_sdl_button, switch_to_sdl_axis);
1232}
1233
1234ButtonBindings SDLState::GetDefaultButtonBinding() const {
1235 return {
1236 std::pair{Settings::NativeButton::A, SDL_CONTROLLER_BUTTON_B},
1237 {Settings::NativeButton::B, SDL_CONTROLLER_BUTTON_A},
1238 {Settings::NativeButton::X, SDL_CONTROLLER_BUTTON_Y},
1239 {Settings::NativeButton::Y, SDL_CONTROLLER_BUTTON_X},
1240 {Settings::NativeButton::LStick, SDL_CONTROLLER_BUTTON_LEFTSTICK},
1241 {Settings::NativeButton::RStick, SDL_CONTROLLER_BUTTON_RIGHTSTICK},
1242 {Settings::NativeButton::L, SDL_CONTROLLER_BUTTON_LEFTSHOULDER},
1243 {Settings::NativeButton::R, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER},
1244 {Settings::NativeButton::Plus, SDL_CONTROLLER_BUTTON_START},
1245 {Settings::NativeButton::Minus, SDL_CONTROLLER_BUTTON_BACK},
1246 {Settings::NativeButton::DLeft, SDL_CONTROLLER_BUTTON_DPAD_LEFT},
1247 {Settings::NativeButton::DUp, SDL_CONTROLLER_BUTTON_DPAD_UP},
1248 {Settings::NativeButton::DRight, SDL_CONTROLLER_BUTTON_DPAD_RIGHT},
1249 {Settings::NativeButton::DDown, SDL_CONTROLLER_BUTTON_DPAD_DOWN},
1250 {Settings::NativeButton::SL, SDL_CONTROLLER_BUTTON_LEFTSHOULDER},
1251 {Settings::NativeButton::SR, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER},
1252 {Settings::NativeButton::Home, SDL_CONTROLLER_BUTTON_GUIDE},
1253 };
1254}
1255
1256ButtonBindings SDLState::GetNintendoButtonBinding(
1257 const std::shared_ptr<SDLJoystick>& joystick) const {
1258 // Default SL/SR mapping for pro controllers
1259 auto sl_button = SDL_CONTROLLER_BUTTON_LEFTSHOULDER;
1260 auto sr_button = SDL_CONTROLLER_BUTTON_RIGHTSHOULDER;
1261
1262 if (joystick->IsJoyconLeft()) {
1263 sl_button = SDL_CONTROLLER_BUTTON_PADDLE2;
1264 sr_button = SDL_CONTROLLER_BUTTON_PADDLE4;
1265 }
1266 if (joystick->IsJoyconRight()) {
1267 sl_button = SDL_CONTROLLER_BUTTON_PADDLE3;
1268 sr_button = SDL_CONTROLLER_BUTTON_PADDLE1;
1269 }
1270
1271 return {
1272 std::pair{Settings::NativeButton::A, SDL_CONTROLLER_BUTTON_A},
1273 {Settings::NativeButton::B, SDL_CONTROLLER_BUTTON_B},
1274 {Settings::NativeButton::X, SDL_CONTROLLER_BUTTON_X},
1275 {Settings::NativeButton::Y, SDL_CONTROLLER_BUTTON_Y},
1276 {Settings::NativeButton::LStick, SDL_CONTROLLER_BUTTON_LEFTSTICK},
1277 {Settings::NativeButton::RStick, SDL_CONTROLLER_BUTTON_RIGHTSTICK},
1278 {Settings::NativeButton::L, SDL_CONTROLLER_BUTTON_LEFTSHOULDER},
1279 {Settings::NativeButton::R, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER},
1280 {Settings::NativeButton::Plus, SDL_CONTROLLER_BUTTON_START},
1281 {Settings::NativeButton::Minus, SDL_CONTROLLER_BUTTON_BACK},
1282 {Settings::NativeButton::DLeft, SDL_CONTROLLER_BUTTON_DPAD_LEFT},
1283 {Settings::NativeButton::DUp, SDL_CONTROLLER_BUTTON_DPAD_UP},
1284 {Settings::NativeButton::DRight, SDL_CONTROLLER_BUTTON_DPAD_RIGHT},
1285 {Settings::NativeButton::DDown, SDL_CONTROLLER_BUTTON_DPAD_DOWN},
1286 {Settings::NativeButton::SL, sl_button},
1287 {Settings::NativeButton::SR, sr_button},
1288 {Settings::NativeButton::Home, SDL_CONTROLLER_BUTTON_GUIDE},
1289 };
1290}
1291
1292ButtonMapping SDLState::GetSingleControllerMapping(
1293 const std::shared_ptr<SDLJoystick>& joystick, const ButtonBindings& switch_to_sdl_button,
1294 const ZButtonBindings& switch_to_sdl_axis) const {
1295 ButtonMapping mapping;
1296 mapping.reserve(switch_to_sdl_button.size() + switch_to_sdl_axis.size());
1297 auto* controller = joystick->GetSDLGameController();
1298
1299 for (const auto& [switch_button, sdl_button] : switch_to_sdl_button) {
1300 const auto& binding = SDL_GameControllerGetBindForButton(controller, sdl_button);
1301 mapping.insert_or_assign(
1302 switch_button,
1303 BuildParamPackageForBinding(joystick->GetPort(), joystick->GetGUID(), binding));
1304 }
1305 for (const auto& [switch_button, sdl_axis] : switch_to_sdl_axis) {
1306 const auto& binding = SDL_GameControllerGetBindForAxis(controller, sdl_axis);
1307 mapping.insert_or_assign(
1308 switch_button,
1309 BuildParamPackageForBinding(joystick->GetPort(), joystick->GetGUID(), binding));
1310 }
1311
1312 return mapping;
1313}
1314
1315ButtonMapping SDLState::GetDualControllerMapping(const std::shared_ptr<SDLJoystick>& joystick,
1316 const std::shared_ptr<SDLJoystick>& joystick2,
1317 const ButtonBindings& switch_to_sdl_button,
1318 const ZButtonBindings& switch_to_sdl_axis) const {
1319 ButtonMapping mapping;
1320 mapping.reserve(switch_to_sdl_button.size() + switch_to_sdl_axis.size());
1321 auto* controller = joystick->GetSDLGameController();
1322 auto* controller2 = joystick2->GetSDLGameController();
1323
1324 for (const auto& [switch_button, sdl_button] : switch_to_sdl_button) {
1325 if (IsButtonOnLeftSide(switch_button)) {
1326 const auto& binding = SDL_GameControllerGetBindForButton(controller2, sdl_button);
1327 mapping.insert_or_assign(
1328 switch_button,
1329 BuildParamPackageForBinding(joystick2->GetPort(), joystick2->GetGUID(), binding));
1330 continue;
1331 }
1332 const auto& binding = SDL_GameControllerGetBindForButton(controller, sdl_button);
1333 mapping.insert_or_assign(
1334 switch_button,
1335 BuildParamPackageForBinding(joystick->GetPort(), joystick->GetGUID(), binding));
1336 }
1337 for (const auto& [switch_button, sdl_axis] : switch_to_sdl_axis) {
1338 if (IsButtonOnLeftSide(switch_button)) {
1339 const auto& binding = SDL_GameControllerGetBindForAxis(controller2, sdl_axis);
1340 mapping.insert_or_assign(
1341 switch_button,
1342 BuildParamPackageForBinding(joystick2->GetPort(), joystick2->GetGUID(), binding));
1343 continue;
1344 }
1345 const auto& binding = SDL_GameControllerGetBindForAxis(controller, sdl_axis);
1346 mapping.insert_or_assign(
1347 switch_button,
1348 BuildParamPackageForBinding(joystick->GetPort(), joystick->GetGUID(), binding));
1349 }
1350
1351 return mapping;
1352}
1353
1354bool SDLState::IsButtonOnLeftSide(Settings::NativeButton::Values button) const {
1355 switch (button) {
1356 case Settings::NativeButton::DDown:
1357 case Settings::NativeButton::DLeft:
1358 case Settings::NativeButton::DRight:
1359 case Settings::NativeButton::DUp:
1360 case Settings::NativeButton::L:
1361 case Settings::NativeButton::LStick:
1362 case Settings::NativeButton::Minus:
1363 case Settings::NativeButton::Screenshot:
1364 case Settings::NativeButton::ZL:
1365 return true;
1366 default:
1367 return false;
1368 }
1369}
1370
1371AnalogMapping SDLState::GetAnalogMappingForDevice(const Common::ParamPackage& params) {
1372 if (!params.Has("guid") || !params.Has("port")) {
1373 return {};
1374 }
1375 const auto joystick = GetSDLJoystickByGUID(params.Get("guid", ""), params.Get("port", 0));
1376 const auto joystick2 = GetSDLJoystickByGUID(params.Get("guid2", ""), params.Get("port", 0));
1377 auto* controller = joystick->GetSDLGameController();
1378 if (controller == nullptr) {
1379 return {};
1380 }
1381
1382 AnalogMapping mapping = {};
1383 const auto& binding_left_x =
1384 SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_LEFTX);
1385 const auto& binding_left_y =
1386 SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_LEFTY);
1387 if (params.Has("guid2")) {
1388 joystick2->PreSetAxis(binding_left_x.value.axis);
1389 joystick2->PreSetAxis(binding_left_y.value.axis);
1390 const auto left_offset_x = -joystick2->GetAxis(binding_left_x.value.axis, 1.0f, 0);
1391 const auto left_offset_y = -joystick2->GetAxis(binding_left_y.value.axis, 1.0f, 0);
1392 mapping.insert_or_assign(
1393 Settings::NativeAnalog::LStick,
1394 BuildParamPackageForAnalog(joystick2->GetPort(), joystick2->GetGUID(),
1395 binding_left_x.value.axis, binding_left_y.value.axis,
1396 left_offset_x, left_offset_y));
1397 } else {
1398 joystick->PreSetAxis(binding_left_x.value.axis);
1399 joystick->PreSetAxis(binding_left_y.value.axis);
1400 const auto left_offset_x = -joystick->GetAxis(binding_left_x.value.axis, 1.0f, 0);
1401 const auto left_offset_y = -joystick->GetAxis(binding_left_y.value.axis, 1.0f, 0);
1402 mapping.insert_or_assign(
1403 Settings::NativeAnalog::LStick,
1404 BuildParamPackageForAnalog(joystick->GetPort(), joystick->GetGUID(),
1405 binding_left_x.value.axis, binding_left_y.value.axis,
1406 left_offset_x, left_offset_y));
1407 }
1408 const auto& binding_right_x =
1409 SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_RIGHTX);
1410 const auto& binding_right_y =
1411 SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_RIGHTY);
1412 joystick->PreSetAxis(binding_right_x.value.axis);
1413 joystick->PreSetAxis(binding_right_y.value.axis);
1414 const auto right_offset_x = -joystick->GetAxis(binding_right_x.value.axis, 1.0f, 0);
1415 const auto right_offset_y = -joystick->GetAxis(binding_right_y.value.axis, 1.0f, 0);
1416 mapping.insert_or_assign(Settings::NativeAnalog::RStick,
1417 BuildParamPackageForAnalog(joystick->GetPort(), joystick->GetGUID(),
1418 binding_right_x.value.axis,
1419 binding_right_y.value.axis, right_offset_x,
1420 right_offset_y));
1421 return mapping;
1422}
1423
1424MotionMapping SDLState::GetMotionMappingForDevice(const Common::ParamPackage& params) {
1425 if (!params.Has("guid") || !params.Has("port")) {
1426 return {};
1427 }
1428 const auto joystick = GetSDLJoystickByGUID(params.Get("guid", ""), params.Get("port", 0));
1429 const auto joystick2 = GetSDLJoystickByGUID(params.Get("guid2", ""), params.Get("port", 0));
1430 auto* controller = joystick->GetSDLGameController();
1431 if (controller == nullptr) {
1432 return {};
1433 }
1434
1435 MotionMapping mapping = {};
1436 joystick->EnableMotion();
1437
1438 if (joystick->HasGyro() || joystick->HasAccel()) {
1439 mapping.insert_or_assign(Settings::NativeMotion::MotionRight,
1440 BuildMotionParam(joystick->GetPort(), joystick->GetGUID()));
1441 }
1442 if (params.Has("guid2")) {
1443 joystick2->EnableMotion();
1444 if (joystick2->HasGyro() || joystick2->HasAccel()) {
1445 mapping.insert_or_assign(Settings::NativeMotion::MotionLeft,
1446 BuildMotionParam(joystick2->GetPort(), joystick2->GetGUID()));
1447 }
1448 } else {
1449 if (joystick->HasGyro() || joystick->HasAccel()) {
1450 mapping.insert_or_assign(Settings::NativeMotion::MotionLeft,
1451 BuildMotionParam(joystick->GetPort(), joystick->GetGUID()));
1452 }
1453 }
1454
1455 return mapping;
1456}
1457namespace Polling {
1458class SDLPoller : public InputCommon::Polling::DevicePoller {
1459public:
1460 explicit SDLPoller(SDLState& state_) : state(state_) {}
1461
1462 void Start([[maybe_unused]] const std::string& device_id) override {
1463 state.event_queue.Clear();
1464 state.polling = true;
1465 }
1466
1467 void Stop() override {
1468 state.polling = false;
1469 }
1470
1471protected:
1472 SDLState& state;
1473};
1474
1475class SDLButtonPoller final : public SDLPoller {
1476public:
1477 explicit SDLButtonPoller(SDLState& state_) : SDLPoller(state_) {}
1478
1479 Common::ParamPackage GetNextInput() override {
1480 SDL_Event event;
1481 while (state.event_queue.Pop(event)) {
1482 const auto package = FromEvent(event);
1483 if (package) {
1484 return *package;
1485 }
1486 }
1487 return {};
1488 }
1489 [[nodiscard]] std::optional<Common::ParamPackage> FromEvent(SDL_Event& event) {
1490 switch (event.type) {
1491 case SDL_JOYAXISMOTION:
1492 if (!axis_memory.count(event.jaxis.which) ||
1493 !axis_memory[event.jaxis.which].count(event.jaxis.axis)) {
1494 axis_memory[event.jaxis.which][event.jaxis.axis] = event.jaxis.value;
1495 axis_event_count[event.jaxis.which][event.jaxis.axis] = 1;
1496 break;
1497 } else {
1498 axis_event_count[event.jaxis.which][event.jaxis.axis]++;
1499 // The joystick and axis exist in our map if we take this branch, so no checks
1500 // needed
1501 if (std::abs(
1502 (event.jaxis.value - axis_memory[event.jaxis.which][event.jaxis.axis]) /
1503 32767.0) < 0.5) {
1504 break;
1505 } else {
1506 if (axis_event_count[event.jaxis.which][event.jaxis.axis] == 2 &&
1507 IsAxisAtPole(event.jaxis.value) &&
1508 IsAxisAtPole(axis_memory[event.jaxis.which][event.jaxis.axis])) {
1509 // If we have exactly two events and both are near a pole, this is
1510 // likely a digital input masquerading as an analog axis; Instead of
1511 // trying to look at the direction the axis travelled, assume the first
1512 // event was press and the second was release; This should handle most
1513 // digital axes while deferring to the direction of travel for analog
1514 // axes
1515 event.jaxis.value = static_cast<Sint16>(
1516 std::copysign(32767, axis_memory[event.jaxis.which][event.jaxis.axis]));
1517 } else {
1518 // There are more than two events, so this is likely a true analog axis,
1519 // check the direction it travelled
1520 event.jaxis.value = static_cast<Sint16>(std::copysign(
1521 32767,
1522 event.jaxis.value - axis_memory[event.jaxis.which][event.jaxis.axis]));
1523 }
1524 axis_memory.clear();
1525 axis_event_count.clear();
1526 }
1527 }
1528 [[fallthrough]];
1529 case SDL_JOYBUTTONUP:
1530 case SDL_JOYHATMOTION:
1531 return {SDLEventToButtonParamPackage(state, event)};
1532 }
1533 return std::nullopt;
1534 }
1535
1536private:
1537 // Determine whether an axis value is close to an extreme or center
1538 // Some controllers have a digital D-Pad as a pair of analog sticks, with 3 possible values per
1539 // axis, which is why the center must be considered a pole
1540 bool IsAxisAtPole(int16_t value) const {
1541 return std::abs(value) >= 32767 || std::abs(value) < 327;
1542 }
1543 std::unordered_map<SDL_JoystickID, std::unordered_map<uint8_t, int16_t>> axis_memory;
1544 std::unordered_map<SDL_JoystickID, std::unordered_map<uint8_t, uint32_t>> axis_event_count;
1545};
1546
1547class SDLMotionPoller final : public SDLPoller {
1548public:
1549 explicit SDLMotionPoller(SDLState& state_) : SDLPoller(state_) {}
1550
1551 Common::ParamPackage GetNextInput() override {
1552 SDL_Event event;
1553 while (state.event_queue.Pop(event)) {
1554 const auto package = FromEvent(event);
1555 if (package) {
1556 return *package;
1557 }
1558 }
1559 return {};
1560 }
1561 [[nodiscard]] std::optional<Common::ParamPackage> FromEvent(const SDL_Event& event) const {
1562 switch (event.type) {
1563 case SDL_JOYAXISMOTION:
1564 if (std::abs(event.jaxis.value / 32767.0) < 0.5) {
1565 break;
1566 }
1567 [[fallthrough]];
1568 case SDL_JOYBUTTONUP:
1569 case SDL_JOYHATMOTION:
1570 case SDL_CONTROLLERSENSORUPDATE:
1571 return {SDLEventToMotionParamPackage(state, event)};
1572 }
1573 return std::nullopt;
1574 }
1575};
1576
1577/**
1578 * Attempts to match the press to a controller joy axis (left/right stick) and if a match
1579 * isn't found, checks if the event matches anything from SDLButtonPoller and uses that
1580 * instead
1581 */
1582class SDLAnalogPreferredPoller final : public SDLPoller {
1583public:
1584 explicit SDLAnalogPreferredPoller(SDLState& state_)
1585 : SDLPoller(state_), button_poller(state_) {}
1586
1587 void Start(const std::string& device_id) override {
1588 SDLPoller::Start(device_id);
1589 // Reset stored axes
1590 first_axis = -1;
1591 }
1592
1593 Common::ParamPackage GetNextInput() override {
1594 SDL_Event event;
1595 while (state.event_queue.Pop(event)) {
1596 if (event.type != SDL_JOYAXISMOTION) {
1597 // Check for a button press
1598 auto button_press = button_poller.FromEvent(event);
1599 if (button_press) {
1600 return *button_press;
1601 }
1602 continue;
1603 }
1604 const auto axis = event.jaxis.axis;
1605
1606 // Filter out axis events that are below a threshold
1607 if (std::abs(event.jaxis.value / 32767.0) < 0.5) {
1608 continue;
1609 }
1610
1611 // Filter out axis events that are the same
1612 if (first_axis == axis) {
1613 continue;
1614 }
1615
1616 // In order to return a complete analog param, we need inputs for both axes.
1617 // If the first axis isn't set we set the value then wait till next event
1618 if (first_axis == -1) {
1619 first_axis = axis;
1620 continue;
1621 }
1622
1623 if (const auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which)) {
1624 // Set offset to zero since the joystick is not on center
1625 auto params = BuildParamPackageForAnalog(joystick->GetPort(), joystick->GetGUID(),
1626 first_axis, axis, 0, 0);
1627 first_axis = -1;
1628 return params;
1629 }
1630 }
1631 return {};
1632 }
1633
1634private:
1635 int first_axis = -1;
1636 SDLButtonPoller button_poller;
1637};
1638} // namespace Polling
1639
1640SDLState::Pollers SDLState::GetPollers(InputCommon::Polling::DeviceType type) {
1641 Pollers pollers;
1642
1643 switch (type) {
1644 case InputCommon::Polling::DeviceType::AnalogPreferred:
1645 pollers.emplace_back(std::make_unique<Polling::SDLAnalogPreferredPoller>(*this));
1646 break;
1647 case InputCommon::Polling::DeviceType::Button:
1648 pollers.emplace_back(std::make_unique<Polling::SDLButtonPoller>(*this));
1649 break;
1650 case InputCommon::Polling::DeviceType::Motion:
1651 pollers.emplace_back(std::make_unique<Polling::SDLMotionPoller>(*this));
1652 break;
1653 }
1654
1655 return pollers;
1656}
1657
1658} // namespace InputCommon::SDL
diff --git a/src/input_common/tas/tas_input.cpp b/src/input_common/tas/tas_input.cpp
deleted file mode 100644
index 1598092b6..000000000
--- a/src/input_common/tas/tas_input.cpp
+++ /dev/null
@@ -1,455 +0,0 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2+
3// Refer to the license.txt file included.
4
5#include <cstring>
6#include <regex>
7
8#include "common/fs/file.h"
9#include "common/fs/fs_types.h"
10#include "common/fs/path_util.h"
11#include "common/logging/log.h"
12#include "common/settings.h"
13#include "input_common/tas/tas_input.h"
14
15namespace TasInput {
16
17// Supported keywords and buttons from a TAS file
18constexpr std::array<std::pair<std::string_view, TasButton>, 20> text_to_tas_button = {
19 std::pair{"KEY_A", TasButton::BUTTON_A},
20 {"KEY_B", TasButton::BUTTON_B},
21 {"KEY_X", TasButton::BUTTON_X},
22 {"KEY_Y", TasButton::BUTTON_Y},
23 {"KEY_LSTICK", TasButton::STICK_L},
24 {"KEY_RSTICK", TasButton::STICK_R},
25 {"KEY_L", TasButton::TRIGGER_L},
26 {"KEY_R", TasButton::TRIGGER_R},
27 {"KEY_PLUS", TasButton::BUTTON_PLUS},
28 {"KEY_MINUS", TasButton::BUTTON_MINUS},
29 {"KEY_DLEFT", TasButton::BUTTON_LEFT},
30 {"KEY_DUP", TasButton::BUTTON_UP},
31 {"KEY_DRIGHT", TasButton::BUTTON_RIGHT},
32 {"KEY_DDOWN", TasButton::BUTTON_DOWN},
33 {"KEY_SL", TasButton::BUTTON_SL},
34 {"KEY_SR", TasButton::BUTTON_SR},
35 {"KEY_CAPTURE", TasButton::BUTTON_CAPTURE},
36 {"KEY_HOME", TasButton::BUTTON_HOME},
37 {"KEY_ZL", TasButton::TRIGGER_ZL},
38 {"KEY_ZR", TasButton::TRIGGER_ZR},
39};
40
41Tas::Tas() {
42 if (!Settings::values.tas_enable) {
43 needs_reset = true;
44 return;
45 }
46 LoadTasFiles();
47}
48
49Tas::~Tas() {
50 Stop();
51};
52
53void Tas::LoadTasFiles() {
54 script_length = 0;
55 for (size_t i = 0; i < commands.size(); i++) {
56 LoadTasFile(i);
57 if (commands[i].size() > script_length) {
58 script_length = commands[i].size();
59 }
60 }
61}
62
63void Tas::LoadTasFile(size_t player_index) {
64 if (!commands[player_index].empty()) {
65 commands[player_index].clear();
66 }
67 std::string file =
68 Common::FS::ReadStringFromFile(Common::FS::GetYuzuPath(Common::FS::YuzuPath::TASDir) /
69 fmt::format("script0-{}.txt", player_index + 1),
70 Common::FS::FileType::BinaryFile);
71 std::stringstream command_line(file);
72 std::string line;
73 int frame_no = 0;
74 while (std::getline(command_line, line, '\n')) {
75 if (line.empty()) {
76 continue;
77 }
78 LOG_DEBUG(Input, "Loading line: {}", line);
79 std::smatch m;
80
81 std::stringstream linestream(line);
82 std::string segment;
83 std::vector<std::string> seglist;
84
85 while (std::getline(linestream, segment, ' ')) {
86 seglist.push_back(segment);
87 }
88
89 if (seglist.size() < 4) {
90 continue;
91 }
92
93 while (frame_no < std::stoi(seglist.at(0))) {
94 commands[player_index].push_back({});
95 frame_no++;
96 }
97
98 TASCommand command = {
99 .buttons = ReadCommandButtons(seglist.at(1)),
100 .l_axis = ReadCommandAxis(seglist.at(2)),
101 .r_axis = ReadCommandAxis(seglist.at(3)),
102 };
103 commands[player_index].push_back(command);
104 frame_no++;
105 }
106 LOG_INFO(Input, "TAS file loaded! {} frames", frame_no);
107}
108
109void Tas::WriteTasFile(std::u8string file_name) {
110 std::string output_text;
111 for (size_t frame = 0; frame < record_commands.size(); frame++) {
112 if (!output_text.empty()) {
113 output_text += "\n";
114 }
115 const TASCommand& line = record_commands[frame];
116 output_text += std::to_string(frame) + " " + WriteCommandButtons(line.buttons) + " " +
117 WriteCommandAxis(line.l_axis) + " " + WriteCommandAxis(line.r_axis);
118 }
119 const auto bytes_written = Common::FS::WriteStringToFile(
120 Common::FS::GetYuzuPath(Common::FS::YuzuPath::TASDir) / file_name,
121 Common::FS::FileType::TextFile, output_text);
122 if (bytes_written == output_text.size()) {
123 LOG_INFO(Input, "TAS file written to file!");
124 } else {
125 LOG_ERROR(Input, "Writing the TAS-file has failed! {} / {} bytes written", bytes_written,
126 output_text.size());
127 }
128}
129
130std::pair<float, float> Tas::FlipAxisY(std::pair<float, float> old) {
131 auto [x, y] = old;
132 return {x, -y};
133}
134
135void Tas::RecordInput(u32 buttons, const std::array<std::pair<float, float>, 2>& axes) {
136 last_input = {buttons, FlipAxisY(axes[0]), FlipAxisY(axes[1])};
137}
138
139std::tuple<TasState, size_t, size_t> Tas::GetStatus() const {
140 TasState state;
141 if (is_recording) {
142 return {TasState::Recording, 0, record_commands.size()};
143 }
144
145 if (is_running) {
146 state = TasState::Running;
147 } else {
148 state = TasState::Stopped;
149 }
150
151 return {state, current_command, script_length};
152}
153
154std::string Tas::DebugButtons(u32 buttons) const {
155 return fmt::format("{{ {} }}", TasInput::Tas::ButtonsToString(buttons));
156}
157
158std::string Tas::DebugJoystick(float x, float y) const {
159 return fmt::format("[ {} , {} ]", std::to_string(x), std::to_string(y));
160}
161
162std::string Tas::DebugInput(const TasData& data) const {
163 return fmt::format("{{ {} , {} , {} }}", DebugButtons(data.buttons),
164 DebugJoystick(data.axis[0], data.axis[1]),
165 DebugJoystick(data.axis[2], data.axis[3]));
166}
167
168std::string Tas::DebugInputs(const std::array<TasData, PLAYER_NUMBER>& arr) const {
169 std::string returns = "[ ";
170 for (size_t i = 0; i < arr.size(); i++) {
171 returns += DebugInput(arr[i]);
172 if (i != arr.size() - 1) {
173 returns += " , ";
174 }
175 }
176 return returns + "]";
177}
178
179std::string Tas::ButtonsToString(u32 button) const {
180 std::string returns;
181 for (auto [text_button, tas_button] : text_to_tas_button) {
182 if ((button & static_cast<u32>(tas_button)) != 0)
183 returns += fmt::format(", {}", text_button.substr(4));
184 }
185 return returns.empty() ? "" : returns.substr(2);
186}
187
188void Tas::UpdateThread() {
189 if (!Settings::values.tas_enable) {
190 if (is_running) {
191 Stop();
192 }
193 return;
194 }
195
196 if (is_recording) {
197 record_commands.push_back(last_input);
198 }
199 if (needs_reset) {
200 current_command = 0;
201 needs_reset = false;
202 LoadTasFiles();
203 LOG_DEBUG(Input, "tas_reset done");
204 }
205
206 if (!is_running) {
207 tas_data.fill({});
208 return;
209 }
210 if (current_command < script_length) {
211 LOG_DEBUG(Input, "Playing TAS {}/{}", current_command, script_length);
212 size_t frame = current_command++;
213 for (size_t i = 0; i < commands.size(); i++) {
214 if (frame < commands[i].size()) {
215 TASCommand command = commands[i][frame];
216 tas_data[i].buttons = command.buttons;
217 auto [l_axis_x, l_axis_y] = command.l_axis;
218 tas_data[i].axis[0] = l_axis_x;
219 tas_data[i].axis[1] = l_axis_y;
220 auto [r_axis_x, r_axis_y] = command.r_axis;
221 tas_data[i].axis[2] = r_axis_x;
222 tas_data[i].axis[3] = r_axis_y;
223 } else {
224 tas_data[i] = {};
225 }
226 }
227 } else {
228 is_running = Settings::values.tas_loop.GetValue();
229 current_command = 0;
230 tas_data.fill({});
231 if (!is_running) {
232 SwapToStoredController();
233 }
234 }
235 LOG_DEBUG(Input, "TAS inputs: {}", DebugInputs(tas_data));
236}
237
238TasAnalog Tas::ReadCommandAxis(const std::string& line) const {
239 std::stringstream linestream(line);
240 std::string segment;
241 std::vector<std::string> seglist;
242
243 while (std::getline(linestream, segment, ';')) {
244 seglist.push_back(segment);
245 }
246
247 const float x = std::stof(seglist.at(0)) / 32767.0f;
248 const float y = std::stof(seglist.at(1)) / 32767.0f;
249
250 return {x, y};
251}
252
253u32 Tas::ReadCommandButtons(const std::string& data) const {
254 std::stringstream button_text(data);
255 std::string line;
256 u32 buttons = 0;
257 while (std::getline(button_text, line, ';')) {
258 for (auto [text, tas_button] : text_to_tas_button) {
259 if (text == line) {
260 buttons |= static_cast<u32>(tas_button);
261 break;
262 }
263 }
264 }
265 return buttons;
266}
267
268std::string Tas::WriteCommandAxis(TasAnalog data) const {
269 auto [x, y] = data;
270 std::string line;
271 line += std::to_string(static_cast<int>(x * 32767));
272 line += ";";
273 line += std::to_string(static_cast<int>(y * 32767));
274 return line;
275}
276
277std::string Tas::WriteCommandButtons(u32 data) const {
278 if (data == 0) {
279 return "NONE";
280 }
281
282 std::string line;
283 u32 index = 0;
284 while (data > 0) {
285 if ((data & 1) == 1) {
286 for (auto [text, tas_button] : text_to_tas_button) {
287 if (tas_button == static_cast<TasButton>(1 << index)) {
288 if (line.size() > 0) {
289 line += ";";
290 }
291 line += text;
292 break;
293 }
294 }
295 }
296 index++;
297 data >>= 1;
298 }
299 return line;
300}
301
302void Tas::StartStop() {
303 if (!Settings::values.tas_enable) {
304 return;
305 }
306 if (is_running) {
307 Stop();
308 } else {
309 is_running = true;
310 SwapToTasController();
311 }
312}
313
314void Tas::Stop() {
315 is_running = false;
316 SwapToStoredController();
317}
318
319void Tas::SwapToTasController() {
320 if (!Settings::values.tas_swap_controllers) {
321 return;
322 }
323 auto& players = Settings::values.players.GetValue();
324 for (std::size_t index = 0; index < players.size(); index++) {
325 auto& player = players[index];
326 player_mappings[index] = player;
327
328 // Only swap active controllers
329 if (!player.connected) {
330 continue;
331 }
332
333 Common::ParamPackage tas_param;
334 tas_param.Set("pad", static_cast<u8>(index));
335 auto button_mapping = GetButtonMappingForDevice(tas_param);
336 auto analog_mapping = GetAnalogMappingForDevice(tas_param);
337 auto& buttons = player.buttons;
338 auto& analogs = player.analogs;
339
340 for (std::size_t i = 0; i < buttons.size(); ++i) {
341 buttons[i] = button_mapping[static_cast<Settings::NativeButton::Values>(i)].Serialize();
342 }
343 for (std::size_t i = 0; i < analogs.size(); ++i) {
344 analogs[i] = analog_mapping[static_cast<Settings::NativeAnalog::Values>(i)].Serialize();
345 }
346 }
347 is_old_input_saved = true;
348 Settings::values.is_device_reload_pending.store(true);
349}
350
351void Tas::SwapToStoredController() {
352 if (!is_old_input_saved) {
353 return;
354 }
355 auto& players = Settings::values.players.GetValue();
356 for (std::size_t index = 0; index < players.size(); index++) {
357 players[index] = player_mappings[index];
358 }
359 is_old_input_saved = false;
360 Settings::values.is_device_reload_pending.store(true);
361}
362
363void Tas::Reset() {
364 if (!Settings::values.tas_enable) {
365 return;
366 }
367 needs_reset = true;
368}
369
370bool Tas::Record() {
371 if (!Settings::values.tas_enable) {
372 return true;
373 }
374 is_recording = !is_recording;
375 return is_recording;
376}
377
378void Tas::SaveRecording(bool overwrite_file) {
379 if (is_recording) {
380 return;
381 }
382 if (record_commands.empty()) {
383 return;
384 }
385 WriteTasFile(u8"record.txt");
386 if (overwrite_file) {
387 WriteTasFile(u8"script0-1.txt");
388 }
389 needs_reset = true;
390 record_commands.clear();
391}
392
393InputCommon::ButtonMapping Tas::GetButtonMappingForDevice(
394 const Common::ParamPackage& params) const {
395 // This list is missing ZL/ZR since those are not considered buttons.
396 // We will add those afterwards
397 // This list also excludes any button that can't be really mapped
398 static constexpr std::array<std::pair<Settings::NativeButton::Values, TasButton>, 20>
399 switch_to_tas_button = {
400 std::pair{Settings::NativeButton::A, TasButton::BUTTON_A},
401 {Settings::NativeButton::B, TasButton::BUTTON_B},
402 {Settings::NativeButton::X, TasButton::BUTTON_X},
403 {Settings::NativeButton::Y, TasButton::BUTTON_Y},
404 {Settings::NativeButton::LStick, TasButton::STICK_L},
405 {Settings::NativeButton::RStick, TasButton::STICK_R},
406 {Settings::NativeButton::L, TasButton::TRIGGER_L},
407 {Settings::NativeButton::R, TasButton::TRIGGER_R},
408 {Settings::NativeButton::Plus, TasButton::BUTTON_PLUS},
409 {Settings::NativeButton::Minus, TasButton::BUTTON_MINUS},
410 {Settings::NativeButton::DLeft, TasButton::BUTTON_LEFT},
411 {Settings::NativeButton::DUp, TasButton::BUTTON_UP},
412 {Settings::NativeButton::DRight, TasButton::BUTTON_RIGHT},
413 {Settings::NativeButton::DDown, TasButton::BUTTON_DOWN},
414 {Settings::NativeButton::SL, TasButton::BUTTON_SL},
415 {Settings::NativeButton::SR, TasButton::BUTTON_SR},
416 {Settings::NativeButton::Screenshot, TasButton::BUTTON_CAPTURE},
417 {Settings::NativeButton::Home, TasButton::BUTTON_HOME},
418 {Settings::NativeButton::ZL, TasButton::TRIGGER_ZL},
419 {Settings::NativeButton::ZR, TasButton::TRIGGER_ZR},
420 };
421
422 InputCommon::ButtonMapping mapping{};
423 for (const auto& [switch_button, tas_button] : switch_to_tas_button) {
424 Common::ParamPackage button_params({{"engine", "tas"}});
425 button_params.Set("pad", params.Get("pad", 0));
426 button_params.Set("button", static_cast<int>(tas_button));
427 mapping.insert_or_assign(switch_button, std::move(button_params));
428 }
429
430 return mapping;
431}
432
433InputCommon::AnalogMapping Tas::GetAnalogMappingForDevice(
434 const Common::ParamPackage& params) const {
435
436 InputCommon::AnalogMapping mapping = {};
437 Common::ParamPackage left_analog_params;
438 left_analog_params.Set("engine", "tas");
439 left_analog_params.Set("pad", params.Get("pad", 0));
440 left_analog_params.Set("axis_x", static_cast<int>(TasAxes::StickX));
441 left_analog_params.Set("axis_y", static_cast<int>(TasAxes::StickY));
442 mapping.insert_or_assign(Settings::NativeAnalog::LStick, std::move(left_analog_params));
443 Common::ParamPackage right_analog_params;
444 right_analog_params.Set("engine", "tas");
445 right_analog_params.Set("pad", params.Get("pad", 0));
446 right_analog_params.Set("axis_x", static_cast<int>(TasAxes::SubstickX));
447 right_analog_params.Set("axis_y", static_cast<int>(TasAxes::SubstickY));
448 mapping.insert_or_assign(Settings::NativeAnalog::RStick, std::move(right_analog_params));
449 return mapping;
450}
451
452const TasData& Tas::GetTasState(std::size_t pad) const {
453 return tas_data[pad];
454}
455} // namespace TasInput
diff --git a/src/input_common/tas/tas_input.h b/src/input_common/tas/tas_input.h
deleted file mode 100644
index 3e2db8f00..000000000
--- a/src/input_common/tas/tas_input.h
+++ /dev/null
@@ -1,237 +0,0 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <array>
8
9#include "common/common_types.h"
10#include "common/settings_input.h"
11#include "core/frontend/input.h"
12#include "input_common/main.h"
13
14/*
15To play back TAS scripts on Yuzu, select the folder with scripts in the configuration menu below
16Tools -> Configure TAS. The file itself has normal text format and has to be called script0-1.txt
17for controller 1, script0-2.txt for controller 2 and so forth (with max. 8 players).
18
19A script file has the same format as TAS-nx uses, so final files will look like this:
20
211 KEY_B 0;0 0;0
226 KEY_ZL 0;0 0;0
2341 KEY_ZL;KEY_Y 0;0 0;0
2443 KEY_X;KEY_A 32767;0 0;0
2544 KEY_A 32767;0 0;0
2645 KEY_A 32767;0 0;0
2746 KEY_A 32767;0 0;0
2847 KEY_A 32767;0 0;0
29
30After placing the file at the correct location, it can be read into Yuzu with the (default) hotkey
31CTRL+F6 (refresh). In the bottom left corner, it will display the amount of frames the script file
32has. Playback can be started or stopped using CTRL+F5.
33
34However, for playback to actually work, the correct input device has to be selected: In the Controls
35menu, select TAS from the device list for the controller that the script should be played on.
36
37Recording a new script file is really simple: Just make sure that the proper device (not TAS) is
38connected on P1, and press CTRL+F7 to start recording. When done, just press the same keystroke
39again (CTRL+F7). The new script will be saved at the location previously selected, as the filename
40record.txt.
41
42For debugging purposes, the common controller debugger can be used (View -> Debugging -> Controller
43P1).
44*/
45
46namespace TasInput {
47
48constexpr size_t PLAYER_NUMBER = 8;
49
50using TasAnalog = std::pair<float, float>;
51
52enum class TasState {
53 Running,
54 Recording,
55 Stopped,
56};
57
58enum class TasButton : u32 {
59 BUTTON_A = 1U << 0,
60 BUTTON_B = 1U << 1,
61 BUTTON_X = 1U << 2,
62 BUTTON_Y = 1U << 3,
63 STICK_L = 1U << 4,
64 STICK_R = 1U << 5,
65 TRIGGER_L = 1U << 6,
66 TRIGGER_R = 1U << 7,
67 TRIGGER_ZL = 1U << 8,
68 TRIGGER_ZR = 1U << 9,
69 BUTTON_PLUS = 1U << 10,
70 BUTTON_MINUS = 1U << 11,
71 BUTTON_LEFT = 1U << 12,
72 BUTTON_UP = 1U << 13,
73 BUTTON_RIGHT = 1U << 14,
74 BUTTON_DOWN = 1U << 15,
75 BUTTON_SL = 1U << 16,
76 BUTTON_SR = 1U << 17,
77 BUTTON_HOME = 1U << 18,
78 BUTTON_CAPTURE = 1U << 19,
79};
80
81enum class TasAxes : u8 {
82 StickX,
83 StickY,
84 SubstickX,
85 SubstickY,
86 Undefined,
87};
88
89struct TasData {
90 u32 buttons{};
91 std::array<float, 4> axis{};
92};
93
94class Tas {
95public:
96 Tas();
97 ~Tas();
98
99 // Changes the input status that will be stored in each frame
100 void RecordInput(u32 buttons, const std::array<std::pair<float, float>, 2>& axes);
101
102 // Main loop that records or executes input
103 void UpdateThread();
104
105 // Sets the flag to start or stop the TAS command excecution and swaps controllers profiles
106 void StartStop();
107
108 // Stop the TAS and reverts any controller profile
109 void Stop();
110
111 // Sets the flag to reload the file and start from the begining in the next update
112 void Reset();
113
114 /**
115 * Sets the flag to enable or disable recording of inputs
116 * @return Returns true if the current recording status is enabled
117 */
118 bool Record();
119
120 // Saves contents of record_commands on a file if overwrite is enabled player 1 will be
121 // overwritten with the recorded commands
122 void SaveRecording(bool overwrite_file);
123
124 /**
125 * Returns the current status values of TAS playback/recording
126 * @return Tuple of
127 * TasState indicating the current state out of Running, Recording or Stopped ;
128 * Current playback progress or amount of frames (so far) for Recording ;
129 * Total length of script file currently loaded or amount of frames (so far) for Recording
130 */
131 std::tuple<TasState, size_t, size_t> GetStatus() const;
132
133 // Retuns an array of the default button mappings
134 InputCommon::ButtonMapping GetButtonMappingForDevice(const Common::ParamPackage& params) const;
135
136 // Retuns an array of the default analog mappings
137 InputCommon::AnalogMapping GetAnalogMappingForDevice(const Common::ParamPackage& params) const;
138 [[nodiscard]] const TasData& GetTasState(std::size_t pad) const;
139
140private:
141 struct TASCommand {
142 u32 buttons{};
143 TasAnalog l_axis{};
144 TasAnalog r_axis{};
145 };
146
147 // Loads TAS files from all players
148 void LoadTasFiles();
149
150 // Loads TAS file from the specified player
151 void LoadTasFile(size_t player_index);
152
153 // Writes a TAS file from the recorded commands
154 void WriteTasFile(std::u8string file_name);
155
156 /**
157 * Parses a string containing the axis values with the following format "x;y"
158 * X and Y have a range from -32767 to 32767
159 * @return Returns a TAS analog object with axis values with range from -1.0 to 1.0
160 */
161 TasAnalog ReadCommandAxis(const std::string& line) const;
162
163 /**
164 * Parses a string containing the button values with the following format "a;b;c;d..."
165 * Each button is represented by it's text format specified in text_to_tas_button array
166 * @return Returns a u32 with each bit representing the status of a button
167 */
168 u32 ReadCommandButtons(const std::string& line) const;
169
170 /**
171 * Converts an u32 containing the button status into the text equivalent
172 * @return Returns a string with the name of the buttons to be written to the file
173 */
174 std::string WriteCommandButtons(u32 data) const;
175
176 /**
177 * Converts an TAS analog object containing the axis status into the text equivalent
178 * @return Returns a string with the value of the axis to be written to the file
179 */
180 std::string WriteCommandAxis(TasAnalog data) const;
181
182 // Inverts the Y axis polarity
183 std::pair<float, float> FlipAxisY(std::pair<float, float> old);
184
185 /**
186 * Converts an u32 containing the button status into the text equivalent
187 * @return Returns a string with the name of the buttons to be printed on console
188 */
189 std::string DebugButtons(u32 buttons) const;
190
191 /**
192 * Converts an TAS analog object containing the axis status into the text equivalent
193 * @return Returns a string with the value of the axis to be printed on console
194 */
195 std::string DebugJoystick(float x, float y) const;
196
197 /**
198 * Converts the given TAS status into the text equivalent
199 * @return Returns a string with the value of the TAS status to be printed on console
200 */
201 std::string DebugInput(const TasData& data) const;
202
203 /**
204 * Converts the given TAS status of multiple players into the text equivalent
205 * @return Returns a string with the value of the status of all TAS players to be printed on
206 * console
207 */
208 std::string DebugInputs(const std::array<TasData, PLAYER_NUMBER>& arr) const;
209
210 /**
211 * Converts an u32 containing the button status into the text equivalent
212 * @return Returns a string with the name of the buttons
213 */
214 std::string ButtonsToString(u32 button) const;
215
216 // Stores current controller configuration and sets a TAS controller for every active controller
217 // to the current config
218 void SwapToTasController();
219
220 // Sets the stored controller configuration to the current config
221 void SwapToStoredController();
222
223 size_t script_length{0};
224 std::array<TasData, PLAYER_NUMBER> tas_data;
225 bool is_old_input_saved{false};
226 bool is_recording{false};
227 bool is_running{false};
228 bool needs_reset{false};
229 std::array<std::vector<TASCommand>, PLAYER_NUMBER> commands{};
230 std::vector<TASCommand> record_commands{};
231 size_t current_command{0};
232 TASCommand last_input{}; // only used for recording
233
234 // Old settings for swapping controllers
235 std::array<Settings::PlayerInput, 10> player_mappings;
236};
237} // namespace TasInput
diff --git a/src/input_common/tas/tas_poller.cpp b/src/input_common/tas/tas_poller.cpp
deleted file mode 100644
index 15810d6b0..000000000
--- a/src/input_common/tas/tas_poller.cpp
+++ /dev/null
@@ -1,101 +0,0 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <mutex>
6#include <utility>
7
8#include "common/settings.h"
9#include "common/threadsafe_queue.h"
10#include "input_common/tas/tas_input.h"
11#include "input_common/tas/tas_poller.h"
12
13namespace InputCommon {
14
15class TasButton final : public Input::ButtonDevice {
16public:
17 explicit TasButton(u32 button_, u32 pad_, const TasInput::Tas* tas_input_)
18 : button(button_), pad(pad_), tas_input(tas_input_) {}
19
20 bool GetStatus() const override {
21 return (tas_input->GetTasState(pad).buttons & button) != 0;
22 }
23
24private:
25 const u32 button;
26 const u32 pad;
27 const TasInput::Tas* tas_input;
28};
29
30TasButtonFactory::TasButtonFactory(std::shared_ptr<TasInput::Tas> tas_input_)
31 : tas_input(std::move(tas_input_)) {}
32
33std::unique_ptr<Input::ButtonDevice> TasButtonFactory::Create(const Common::ParamPackage& params) {
34 const auto button_id = params.Get("button", 0);
35 const auto pad = params.Get("pad", 0);
36
37 return std::make_unique<TasButton>(button_id, pad, tas_input.get());
38}
39
40class TasAnalog final : public Input::AnalogDevice {
41public:
42 explicit TasAnalog(u32 pad_, u32 axis_x_, u32 axis_y_, const TasInput::Tas* tas_input_)
43 : pad(pad_), axis_x(axis_x_), axis_y(axis_y_), tas_input(tas_input_) {}
44
45 float GetAxis(u32 axis) const {
46 std::lock_guard lock{mutex};
47 return tas_input->GetTasState(pad).axis.at(axis);
48 }
49
50 std::pair<float, float> GetAnalog(u32 analog_axis_x, u32 analog_axis_y) const {
51 float x = GetAxis(analog_axis_x);
52 float y = GetAxis(analog_axis_y);
53
54 // Make sure the coordinates are in the unit circle,
55 // otherwise normalize it.
56 float r = x * x + y * y;
57 if (r > 1.0f) {
58 r = std::sqrt(r);
59 x /= r;
60 y /= r;
61 }
62
63 return {x, y};
64 }
65
66 std::tuple<float, float> GetStatus() const override {
67 return GetAnalog(axis_x, axis_y);
68 }
69
70 Input::AnalogProperties GetAnalogProperties() const override {
71 return {0.0f, 1.0f, 0.5f};
72 }
73
74private:
75 const u32 pad;
76 const u32 axis_x;
77 const u32 axis_y;
78 const TasInput::Tas* tas_input;
79 mutable std::mutex mutex;
80};
81
82/// An analog device factory that creates analog devices from GC Adapter
83TasAnalogFactory::TasAnalogFactory(std::shared_ptr<TasInput::Tas> tas_input_)
84 : tas_input(std::move(tas_input_)) {}
85
86/**
87 * Creates analog device from joystick axes
88 * @param params contains parameters for creating the device:
89 * - "port": the nth gcpad on the adapter
90 * - "axis_x": the index of the axis to be bind as x-axis
91 * - "axis_y": the index of the axis to be bind as y-axis
92 */
93std::unique_ptr<Input::AnalogDevice> TasAnalogFactory::Create(const Common::ParamPackage& params) {
94 const auto pad = static_cast<u32>(params.Get("pad", 0));
95 const auto axis_x = static_cast<u32>(params.Get("axis_x", 0));
96 const auto axis_y = static_cast<u32>(params.Get("axis_y", 1));
97
98 return std::make_unique<TasAnalog>(pad, axis_x, axis_y, tas_input.get());
99}
100
101} // namespace InputCommon
diff --git a/src/input_common/tas/tas_poller.h b/src/input_common/tas/tas_poller.h
deleted file mode 100644
index 09e426cef..000000000
--- a/src/input_common/tas/tas_poller.h
+++ /dev/null
@@ -1,43 +0,0 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <memory>
8#include "core/frontend/input.h"
9#include "input_common/tas/tas_input.h"
10
11namespace InputCommon {
12
13/**
14 * A button device factory representing a tas bot. It receives tas events and forward them
15 * to all button devices it created.
16 */
17class TasButtonFactory final : public Input::Factory<Input::ButtonDevice> {
18public:
19 explicit TasButtonFactory(std::shared_ptr<TasInput::Tas> tas_input_);
20
21 /**
22 * Creates a button device from a button press
23 * @param params contains parameters for creating the device:
24 * - "code": the code of the key to bind with the button
25 */
26 std::unique_ptr<Input::ButtonDevice> Create(const Common::ParamPackage& params) override;
27
28private:
29 std::shared_ptr<TasInput::Tas> tas_input;
30};
31
32/// An analog device factory that creates analog devices from tas
33class TasAnalogFactory final : public Input::Factory<Input::AnalogDevice> {
34public:
35 explicit TasAnalogFactory(std::shared_ptr<TasInput::Tas> tas_input_);
36
37 std::unique_ptr<Input::AnalogDevice> Create(const Common::ParamPackage& params) override;
38
39private:
40 std::shared_ptr<TasInput::Tas> tas_input;
41};
42
43} // namespace InputCommon
diff --git a/src/input_common/touch_from_button.cpp b/src/input_common/touch_from_button.cpp
deleted file mode 100644
index 7878a56d7..000000000
--- a/src/input_common/touch_from_button.cpp
+++ /dev/null
@@ -1,53 +0,0 @@
1// Copyright 2020 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <algorithm>
6#include "common/settings.h"
7#include "core/frontend/framebuffer_layout.h"
8#include "input_common/touch_from_button.h"
9
10namespace InputCommon {
11
12class TouchFromButtonDevice final : public Input::TouchDevice {
13public:
14 TouchFromButtonDevice() {
15 const auto button_index =
16 static_cast<u64>(Settings::values.touch_from_button_map_index.GetValue());
17 const auto& buttons = Settings::values.touch_from_button_maps[button_index].buttons;
18
19 for (const auto& config_entry : buttons) {
20 const Common::ParamPackage package{config_entry};
21 map.emplace_back(
22 Input::CreateDevice<Input::ButtonDevice>(config_entry),
23 std::clamp(package.Get("x", 0), 0, static_cast<int>(Layout::ScreenUndocked::Width)),
24 std::clamp(package.Get("y", 0), 0,
25 static_cast<int>(Layout::ScreenUndocked::Height)));
26 }
27 }
28
29 Input::TouchStatus GetStatus() const override {
30 Input::TouchStatus touch_status{};
31 for (std::size_t id = 0; id < map.size() && id < touch_status.size(); ++id) {
32 const bool state = std::get<0>(map[id])->GetStatus();
33 if (state) {
34 const float x = static_cast<float>(std::get<1>(map[id])) /
35 static_cast<int>(Layout::ScreenUndocked::Width);
36 const float y = static_cast<float>(std::get<2>(map[id])) /
37 static_cast<int>(Layout::ScreenUndocked::Height);
38 touch_status[id] = {x, y, true};
39 }
40 }
41 return touch_status;
42 }
43
44private:
45 // A vector of the mapped button, its x and its y-coordinate
46 std::vector<std::tuple<std::unique_ptr<Input::ButtonDevice>, int, int>> map;
47};
48
49std::unique_ptr<Input::TouchDevice> TouchFromButtonFactory::Create(const Common::ParamPackage&) {
50 return std::make_unique<TouchFromButtonDevice>();
51}
52
53} // namespace InputCommon
diff --git a/src/input_common/udp/client.cpp b/src/input_common/udp/client.cpp
deleted file mode 100644
index b9512aa2e..000000000
--- a/src/input_common/udp/client.cpp
+++ /dev/null
@@ -1,526 +0,0 @@
1// Copyright 2018 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <chrono>
6#include <cstring>
7#include <functional>
8#include <random>
9#include <thread>
10#include <boost/asio.hpp>
11#include "common/logging/log.h"
12#include "common/settings.h"
13#include "input_common/udp/client.h"
14#include "input_common/udp/protocol.h"
15
16using boost::asio::ip::udp;
17
18namespace InputCommon::CemuhookUDP {
19
20struct SocketCallback {
21 std::function<void(Response::Version)> version;
22 std::function<void(Response::PortInfo)> port_info;
23 std::function<void(Response::PadData)> pad_data;
24};
25
26class Socket {
27public:
28 using clock = std::chrono::system_clock;
29
30 explicit Socket(const std::string& host, u16 port, SocketCallback callback_)
31 : callback(std::move(callback_)), timer(io_service),
32 socket(io_service, udp::endpoint(udp::v4(), 0)), client_id(GenerateRandomClientId()) {
33 boost::system::error_code ec{};
34 auto ipv4 = boost::asio::ip::make_address_v4(host, ec);
35 if (ec.value() != boost::system::errc::success) {
36 LOG_ERROR(Input, "Invalid IPv4 address \"{}\" provided to socket", host);
37 ipv4 = boost::asio::ip::address_v4{};
38 }
39
40 send_endpoint = {udp::endpoint(ipv4, port)};
41 }
42
43 void Stop() {
44 io_service.stop();
45 }
46
47 void Loop() {
48 io_service.run();
49 }
50
51 void StartSend(const clock::time_point& from) {
52 timer.expires_at(from + std::chrono::seconds(3));
53 timer.async_wait([this](const boost::system::error_code& error) { HandleSend(error); });
54 }
55
56 void StartReceive() {
57 socket.async_receive_from(
58 boost::asio::buffer(receive_buffer), receive_endpoint,
59 [this](const boost::system::error_code& error, std::size_t bytes_transferred) {
60 HandleReceive(error, bytes_transferred);
61 });
62 }
63
64private:
65 u32 GenerateRandomClientId() const {
66 std::random_device device;
67 return device();
68 }
69
70 void HandleReceive(const boost::system::error_code&, std::size_t bytes_transferred) {
71 if (auto type = Response::Validate(receive_buffer.data(), bytes_transferred)) {
72 switch (*type) {
73 case Type::Version: {
74 Response::Version version;
75 std::memcpy(&version, &receive_buffer[sizeof(Header)], sizeof(Response::Version));
76 callback.version(std::move(version));
77 break;
78 }
79 case Type::PortInfo: {
80 Response::PortInfo port_info;
81 std::memcpy(&port_info, &receive_buffer[sizeof(Header)],
82 sizeof(Response::PortInfo));
83 callback.port_info(std::move(port_info));
84 break;
85 }
86 case Type::PadData: {
87 Response::PadData pad_data;
88 std::memcpy(&pad_data, &receive_buffer[sizeof(Header)], sizeof(Response::PadData));
89 SanitizeMotion(pad_data);
90 callback.pad_data(std::move(pad_data));
91 break;
92 }
93 }
94 }
95 StartReceive();
96 }
97
98 void HandleSend(const boost::system::error_code&) {
99 boost::system::error_code _ignored{};
100 // Send a request for getting port info for the pad
101 const Request::PortInfo port_info{4, {0, 1, 2, 3}};
102 const auto port_message = Request::Create(port_info, client_id);
103 std::memcpy(&send_buffer1, &port_message, PORT_INFO_SIZE);
104 socket.send_to(boost::asio::buffer(send_buffer1), send_endpoint, {}, _ignored);
105
106 // Send a request for getting pad data for the pad
107 const Request::PadData pad_data{
108 Request::PadData::Flags::AllPorts,
109 0,
110 EMPTY_MAC_ADDRESS,
111 };
112 const auto pad_message = Request::Create(pad_data, client_id);
113 std::memcpy(send_buffer2.data(), &pad_message, PAD_DATA_SIZE);
114 socket.send_to(boost::asio::buffer(send_buffer2), send_endpoint, {}, _ignored);
115 StartSend(timer.expiry());
116 }
117
118 void SanitizeMotion(Response::PadData& data) {
119 // Zero out any non number value
120 if (!std::isnormal(data.gyro.pitch)) {
121 data.gyro.pitch = 0;
122 }
123 if (!std::isnormal(data.gyro.roll)) {
124 data.gyro.roll = 0;
125 }
126 if (!std::isnormal(data.gyro.yaw)) {
127 data.gyro.yaw = 0;
128 }
129 if (!std::isnormal(data.accel.x)) {
130 data.accel.x = 0;
131 }
132 if (!std::isnormal(data.accel.y)) {
133 data.accel.y = 0;
134 }
135 if (!std::isnormal(data.accel.z)) {
136 data.accel.z = 0;
137 }
138 }
139
140 SocketCallback callback;
141 boost::asio::io_service io_service;
142 boost::asio::basic_waitable_timer<clock> timer;
143 udp::socket socket;
144
145 const u32 client_id;
146
147 static constexpr std::size_t PORT_INFO_SIZE = sizeof(Message<Request::PortInfo>);
148 static constexpr std::size_t PAD_DATA_SIZE = sizeof(Message<Request::PadData>);
149 std::array<u8, PORT_INFO_SIZE> send_buffer1;
150 std::array<u8, PAD_DATA_SIZE> send_buffer2;
151 udp::endpoint send_endpoint;
152
153 std::array<u8, MAX_PACKET_SIZE> receive_buffer;
154 udp::endpoint receive_endpoint;
155};
156
157static void SocketLoop(Socket* socket) {
158 socket->StartReceive();
159 socket->StartSend(Socket::clock::now());
160 socket->Loop();
161}
162
163Client::Client() {
164 LOG_INFO(Input, "Udp Initialization started");
165 finger_id.fill(MAX_TOUCH_FINGERS);
166 ReloadSockets();
167}
168
169Client::~Client() {
170 Reset();
171}
172
173Client::ClientConnection::ClientConnection() = default;
174
175Client::ClientConnection::~ClientConnection() = default;
176
177std::vector<Common::ParamPackage> Client::GetInputDevices() const {
178 std::vector<Common::ParamPackage> devices;
179 for (std::size_t pad = 0; pad < pads.size(); pad++) {
180 if (!DeviceConnected(pad)) {
181 continue;
182 }
183 std::string name = fmt::format("UDP Controller {}", pad);
184 devices.emplace_back(Common::ParamPackage{
185 {"class", "cemuhookudp"},
186 {"display", std::move(name)},
187 {"port", std::to_string(pad)},
188 });
189 }
190 return devices;
191}
192
193bool Client::DeviceConnected(std::size_t pad) const {
194 // Use last timestamp to detect if the socket has stopped sending data
195 const auto now = std::chrono::steady_clock::now();
196 const auto time_difference = static_cast<u64>(
197 std::chrono::duration_cast<std::chrono::milliseconds>(now - pads[pad].last_update).count());
198 return time_difference < 1000 && pads[pad].connected;
199}
200
201void Client::ReloadSockets() {
202 Reset();
203
204 std::stringstream servers_ss(static_cast<std::string>(Settings::values.udp_input_servers));
205 std::string server_token;
206 std::size_t client = 0;
207 while (std::getline(servers_ss, server_token, ',')) {
208 if (client == MAX_UDP_CLIENTS) {
209 break;
210 }
211 std::stringstream server_ss(server_token);
212 std::string token;
213 std::getline(server_ss, token, ':');
214 std::string udp_input_address = token;
215 std::getline(server_ss, token, ':');
216 char* temp;
217 const u16 udp_input_port = static_cast<u16>(std::strtol(token.c_str(), &temp, 0));
218 if (*temp != '\0') {
219 LOG_ERROR(Input, "Port number is not valid {}", token);
220 continue;
221 }
222
223 const std::size_t client_number = GetClientNumber(udp_input_address, udp_input_port);
224 if (client_number != MAX_UDP_CLIENTS) {
225 LOG_ERROR(Input, "Duplicated UDP servers found");
226 continue;
227 }
228 StartCommunication(client++, udp_input_address, udp_input_port);
229 }
230}
231
232std::size_t Client::GetClientNumber(std::string_view host, u16 port) const {
233 for (std::size_t client = 0; client < clients.size(); client++) {
234 if (clients[client].active == -1) {
235 continue;
236 }
237 if (clients[client].host == host && clients[client].port == port) {
238 return client;
239 }
240 }
241 return MAX_UDP_CLIENTS;
242}
243
244void Client::OnVersion([[maybe_unused]] Response::Version data) {
245 LOG_TRACE(Input, "Version packet received: {}", data.version);
246}
247
248void Client::OnPortInfo([[maybe_unused]] Response::PortInfo data) {
249 LOG_TRACE(Input, "PortInfo packet received: {}", data.model);
250}
251
252void Client::OnPadData(Response::PadData data, std::size_t client) {
253 const std::size_t pad_index = (client * PADS_PER_CLIENT) + data.info.id;
254
255 if (pad_index >= pads.size()) {
256 LOG_ERROR(Input, "Invalid pad id {}", data.info.id);
257 return;
258 }
259
260 LOG_TRACE(Input, "PadData packet received");
261 if (data.packet_counter == pads[pad_index].packet_sequence) {
262 LOG_WARNING(
263 Input,
264 "PadData packet dropped because its stale info. Current count: {} Packet count: {}",
265 pads[pad_index].packet_sequence, data.packet_counter);
266 pads[pad_index].connected = false;
267 return;
268 }
269
270 clients[client].active = 1;
271 pads[pad_index].connected = true;
272 pads[pad_index].packet_sequence = data.packet_counter;
273
274 const auto now = std::chrono::steady_clock::now();
275 const auto time_difference = static_cast<u64>(
276 std::chrono::duration_cast<std::chrono::microseconds>(now - pads[pad_index].last_update)
277 .count());
278 pads[pad_index].last_update = now;
279
280 const Common::Vec3f raw_gyroscope = {data.gyro.pitch, data.gyro.roll, -data.gyro.yaw};
281 pads[pad_index].motion.SetAcceleration({data.accel.x, -data.accel.z, data.accel.y});
282 // Gyroscope values are not it the correct scale from better joy.
283 // Dividing by 312 allows us to make one full turn = 1 turn
284 // This must be a configurable valued called sensitivity
285 pads[pad_index].motion.SetGyroscope(raw_gyroscope / 312.0f);
286 pads[pad_index].motion.UpdateRotation(time_difference);
287 pads[pad_index].motion.UpdateOrientation(time_difference);
288
289 {
290 std::lock_guard guard(pads[pad_index].status.update_mutex);
291 pads[pad_index].status.motion_status = pads[pad_index].motion.GetMotion();
292
293 for (std::size_t id = 0; id < data.touch.size(); ++id) {
294 UpdateTouchInput(data.touch[id], client, id);
295 }
296
297 if (configuring) {
298 const Common::Vec3f gyroscope = pads[pad_index].motion.GetGyroscope();
299 const Common::Vec3f accelerometer = pads[pad_index].motion.GetAcceleration();
300 UpdateYuzuSettings(client, data.info.id, accelerometer, gyroscope);
301 }
302 }
303}
304
305void Client::StartCommunication(std::size_t client, const std::string& host, u16 port) {
306 SocketCallback callback{[this](Response::Version version) { OnVersion(version); },
307 [this](Response::PortInfo info) { OnPortInfo(info); },
308 [this, client](Response::PadData data) { OnPadData(data, client); }};
309 LOG_INFO(Input, "Starting communication with UDP input server on {}:{}", host, port);
310 clients[client].host = host;
311 clients[client].port = port;
312 clients[client].active = 0;
313 clients[client].socket = std::make_unique<Socket>(host, port, callback);
314 clients[client].thread = std::thread{SocketLoop, clients[client].socket.get()};
315
316 // Set motion parameters
317 // SetGyroThreshold value should be dependent on GyroscopeZeroDriftMode
318 // Real HW values are unknown, 0.0001 is an approximate to Standard
319 for (std::size_t pad = 0; pad < PADS_PER_CLIENT; pad++) {
320 pads[client * PADS_PER_CLIENT + pad].motion.SetGyroThreshold(0.0001f);
321 }
322}
323
324void Client::Reset() {
325 for (auto& client : clients) {
326 if (client.thread.joinable()) {
327 client.active = -1;
328 client.socket->Stop();
329 client.thread.join();
330 }
331 }
332}
333
334void Client::UpdateYuzuSettings(std::size_t client, std::size_t pad_index,
335 const Common::Vec3<float>& acc, const Common::Vec3<float>& gyro) {
336 if (gyro.Length() > 0.2f) {
337 LOG_DEBUG(Input, "UDP Controller {}: gyro=({}, {}, {}), accel=({}, {}, {})", client,
338 gyro[0], gyro[1], gyro[2], acc[0], acc[1], acc[2]);
339 }
340 UDPPadStatus pad{
341 .host = clients[client].host,
342 .port = clients[client].port,
343 .pad_index = pad_index,
344 };
345 for (std::size_t i = 0; i < 3; ++i) {
346 if (gyro[i] > 5.0f || gyro[i] < -5.0f) {
347 pad.motion = static_cast<PadMotion>(i);
348 pad.motion_value = gyro[i];
349 pad_queue.Push(pad);
350 }
351 if (acc[i] > 1.75f || acc[i] < -1.75f) {
352 pad.motion = static_cast<PadMotion>(i + 3);
353 pad.motion_value = acc[i];
354 pad_queue.Push(pad);
355 }
356 }
357}
358
359std::optional<std::size_t> Client::GetUnusedFingerID() const {
360 std::size_t first_free_id = 0;
361 while (first_free_id < MAX_TOUCH_FINGERS) {
362 if (!std::get<2>(touch_status[first_free_id])) {
363 return first_free_id;
364 } else {
365 first_free_id++;
366 }
367 }
368 return std::nullopt;
369}
370
371void Client::UpdateTouchInput(Response::TouchPad& touch_pad, std::size_t client, std::size_t id) {
372 // TODO: Use custom calibration per device
373 const Common::ParamPackage touch_param(Settings::values.touch_device.GetValue());
374 const u16 min_x = static_cast<u16>(touch_param.Get("min_x", 100));
375 const u16 min_y = static_cast<u16>(touch_param.Get("min_y", 50));
376 const u16 max_x = static_cast<u16>(touch_param.Get("max_x", 1800));
377 const u16 max_y = static_cast<u16>(touch_param.Get("max_y", 850));
378 const std::size_t touch_id = client * 2 + id;
379 if (touch_pad.is_active) {
380 if (finger_id[touch_id] == MAX_TOUCH_FINGERS) {
381 const auto first_free_id = GetUnusedFingerID();
382 if (!first_free_id) {
383 // Invalid finger id skip to next input
384 return;
385 }
386 finger_id[touch_id] = *first_free_id;
387 }
388 auto& [x, y, pressed] = touch_status[finger_id[touch_id]];
389 x = static_cast<float>(std::clamp(static_cast<u16>(touch_pad.x), min_x, max_x) - min_x) /
390 static_cast<float>(max_x - min_x);
391 y = static_cast<float>(std::clamp(static_cast<u16>(touch_pad.y), min_y, max_y) - min_y) /
392 static_cast<float>(max_y - min_y);
393 pressed = true;
394 return;
395 }
396
397 if (finger_id[touch_id] != MAX_TOUCH_FINGERS) {
398 touch_status[finger_id[touch_id]] = {};
399 finger_id[touch_id] = MAX_TOUCH_FINGERS;
400 }
401}
402
403void Client::BeginConfiguration() {
404 pad_queue.Clear();
405 configuring = true;
406}
407
408void Client::EndConfiguration() {
409 pad_queue.Clear();
410 configuring = false;
411}
412
413DeviceStatus& Client::GetPadState(const std::string& host, u16 port, std::size_t pad) {
414 const std::size_t client_number = GetClientNumber(host, port);
415 if (client_number == MAX_UDP_CLIENTS || pad >= PADS_PER_CLIENT) {
416 return pads[0].status;
417 }
418 return pads[(client_number * PADS_PER_CLIENT) + pad].status;
419}
420
421const DeviceStatus& Client::GetPadState(const std::string& host, u16 port, std::size_t pad) const {
422 const std::size_t client_number = GetClientNumber(host, port);
423 if (client_number == MAX_UDP_CLIENTS || pad >= PADS_PER_CLIENT) {
424 return pads[0].status;
425 }
426 return pads[(client_number * PADS_PER_CLIENT) + pad].status;
427}
428
429Input::TouchStatus& Client::GetTouchState() {
430 return touch_status;
431}
432
433const Input::TouchStatus& Client::GetTouchState() const {
434 return touch_status;
435}
436
437Common::SPSCQueue<UDPPadStatus>& Client::GetPadQueue() {
438 return pad_queue;
439}
440
441const Common::SPSCQueue<UDPPadStatus>& Client::GetPadQueue() const {
442 return pad_queue;
443}
444
445void TestCommunication(const std::string& host, u16 port,
446 const std::function<void()>& success_callback,
447 const std::function<void()>& failure_callback) {
448 std::thread([=] {
449 Common::Event success_event;
450 SocketCallback callback{
451 .version = [](Response::Version) {},
452 .port_info = [](Response::PortInfo) {},
453 .pad_data = [&](Response::PadData) { success_event.Set(); },
454 };
455 Socket socket{host, port, std::move(callback)};
456 std::thread worker_thread{SocketLoop, &socket};
457 const bool result =
458 success_event.WaitUntil(std::chrono::steady_clock::now() + std::chrono::seconds(10));
459 socket.Stop();
460 worker_thread.join();
461 if (result) {
462 success_callback();
463 } else {
464 failure_callback();
465 }
466 }).detach();
467}
468
469CalibrationConfigurationJob::CalibrationConfigurationJob(
470 const std::string& host, u16 port, std::function<void(Status)> status_callback,
471 std::function<void(u16, u16, u16, u16)> data_callback) {
472
473 std::thread([=, this] {
474 Status current_status{Status::Initialized};
475 SocketCallback callback{
476 [](Response::Version) {}, [](Response::PortInfo) {},
477 [&](Response::PadData data) {
478 static constexpr u16 CALIBRATION_THRESHOLD = 100;
479 static constexpr u16 MAX_VALUE = UINT16_MAX;
480
481 if (current_status == Status::Initialized) {
482 // Receiving data means the communication is ready now
483 current_status = Status::Ready;
484 status_callback(current_status);
485 }
486 const auto& touchpad_0 = data.touch[0];
487 if (touchpad_0.is_active == 0) {
488 return;
489 }
490 LOG_DEBUG(Input, "Current touch: {} {}", touchpad_0.x, touchpad_0.y);
491 const u16 min_x = std::min(MAX_VALUE, static_cast<u16>(touchpad_0.x));
492 const u16 min_y = std::min(MAX_VALUE, static_cast<u16>(touchpad_0.y));
493 if (current_status == Status::Ready) {
494 // First touch - min data (min_x/min_y)
495 current_status = Status::Stage1Completed;
496 status_callback(current_status);
497 }
498 if (touchpad_0.x - min_x > CALIBRATION_THRESHOLD &&
499 touchpad_0.y - min_y > CALIBRATION_THRESHOLD) {
500 // Set the current position as max value and finishes configuration
501 const u16 max_x = touchpad_0.x;
502 const u16 max_y = touchpad_0.y;
503 current_status = Status::Completed;
504 data_callback(min_x, min_y, max_x, max_y);
505 status_callback(current_status);
506
507 complete_event.Set();
508 }
509 }};
510 Socket socket{host, port, std::move(callback)};
511 std::thread worker_thread{SocketLoop, &socket};
512 complete_event.Wait();
513 socket.Stop();
514 worker_thread.join();
515 }).detach();
516}
517
518CalibrationConfigurationJob::~CalibrationConfigurationJob() {
519 Stop();
520}
521
522void CalibrationConfigurationJob::Stop() {
523 complete_event.Set();
524}
525
526} // namespace InputCommon::CemuhookUDP
diff --git a/src/input_common/udp/udp.cpp b/src/input_common/udp/udp.cpp
deleted file mode 100644
index 9829da6f0..000000000
--- a/src/input_common/udp/udp.cpp
+++ /dev/null
@@ -1,110 +0,0 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <mutex>
6#include <utility>
7#include "common/assert.h"
8#include "common/threadsafe_queue.h"
9#include "input_common/udp/client.h"
10#include "input_common/udp/udp.h"
11
12namespace InputCommon {
13
14class UDPMotion final : public Input::MotionDevice {
15public:
16 explicit UDPMotion(std::string ip_, u16 port_, u16 pad_, CemuhookUDP::Client* client_)
17 : ip(std::move(ip_)), port(port_), pad(pad_), client(client_) {}
18
19 Input::MotionStatus GetStatus() const override {
20 return client->GetPadState(ip, port, pad).motion_status;
21 }
22
23private:
24 const std::string ip;
25 const u16 port;
26 const u16 pad;
27 CemuhookUDP::Client* client;
28 mutable std::mutex mutex;
29};
30
31/// A motion device factory that creates motion devices from a UDP client
32UDPMotionFactory::UDPMotionFactory(std::shared_ptr<CemuhookUDP::Client> client_)
33 : client(std::move(client_)) {}
34
35/**
36 * Creates motion device
37 * @param params contains parameters for creating the device:
38 * - "port": the UDP port number
39 */
40std::unique_ptr<Input::MotionDevice> UDPMotionFactory::Create(const Common::ParamPackage& params) {
41 auto ip = params.Get("ip", "127.0.0.1");
42 const auto port = static_cast<u16>(params.Get("port", 26760));
43 const auto pad = static_cast<u16>(params.Get("pad_index", 0));
44
45 return std::make_unique<UDPMotion>(std::move(ip), port, pad, client.get());
46}
47
48void UDPMotionFactory::BeginConfiguration() {
49 polling = true;
50 client->BeginConfiguration();
51}
52
53void UDPMotionFactory::EndConfiguration() {
54 polling = false;
55 client->EndConfiguration();
56}
57
58Common::ParamPackage UDPMotionFactory::GetNextInput() {
59 Common::ParamPackage params;
60 CemuhookUDP::UDPPadStatus pad;
61 auto& queue = client->GetPadQueue();
62 while (queue.Pop(pad)) {
63 if (pad.motion == CemuhookUDP::PadMotion::Undefined || std::abs(pad.motion_value) < 1) {
64 continue;
65 }
66 params.Set("engine", "cemuhookudp");
67 params.Set("ip", pad.host);
68 params.Set("port", static_cast<u16>(pad.port));
69 params.Set("pad_index", static_cast<u16>(pad.pad_index));
70 params.Set("motion", static_cast<u16>(pad.motion));
71 return params;
72 }
73 return params;
74}
75
76class UDPTouch final : public Input::TouchDevice {
77public:
78 explicit UDPTouch(std::string ip_, u16 port_, u16 pad_, CemuhookUDP::Client* client_)
79 : ip(std::move(ip_)), port(port_), pad(pad_), client(client_) {}
80
81 Input::TouchStatus GetStatus() const override {
82 return client->GetTouchState();
83 }
84
85private:
86 const std::string ip;
87 [[maybe_unused]] const u16 port;
88 [[maybe_unused]] const u16 pad;
89 CemuhookUDP::Client* client;
90 mutable std::mutex mutex;
91};
92
93/// A motion device factory that creates motion devices from a UDP client
94UDPTouchFactory::UDPTouchFactory(std::shared_ptr<CemuhookUDP::Client> client_)
95 : client(std::move(client_)) {}
96
97/**
98 * Creates motion device
99 * @param params contains parameters for creating the device:
100 * - "port": the UDP port number
101 */
102std::unique_ptr<Input::TouchDevice> UDPTouchFactory::Create(const Common::ParamPackage& params) {
103 auto ip = params.Get("ip", "127.0.0.1");
104 const auto port = static_cast<u16>(params.Get("port", 26760));
105 const auto pad = static_cast<u16>(params.Get("pad_index", 0));
106
107 return std::make_unique<UDPTouch>(std::move(ip), port, pad, client.get());
108}
109
110} // namespace InputCommon
diff --git a/src/input_common/udp/udp.h b/src/input_common/udp/udp.h
deleted file mode 100644
index ea3fd4175..000000000
--- a/src/input_common/udp/udp.h
+++ /dev/null
@@ -1,57 +0,0 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <memory>
8#include "core/frontend/input.h"
9#include "input_common/udp/client.h"
10
11namespace InputCommon {
12
13/// A motion device factory that creates motion devices from udp clients
14class UDPMotionFactory final : public Input::Factory<Input::MotionDevice> {
15public:
16 explicit UDPMotionFactory(std::shared_ptr<CemuhookUDP::Client> client_);
17
18 std::unique_ptr<Input::MotionDevice> Create(const Common::ParamPackage& params) override;
19
20 Common::ParamPackage GetNextInput();
21
22 /// For device input configuration/polling
23 void BeginConfiguration();
24 void EndConfiguration();
25
26 bool IsPolling() const {
27 return polling;
28 }
29
30private:
31 std::shared_ptr<CemuhookUDP::Client> client;
32 bool polling = false;
33};
34
35/// A touch device factory that creates touch devices from udp clients
36class UDPTouchFactory final : public Input::Factory<Input::TouchDevice> {
37public:
38 explicit UDPTouchFactory(std::shared_ptr<CemuhookUDP::Client> client_);
39
40 std::unique_ptr<Input::TouchDevice> Create(const Common::ParamPackage& params) override;
41
42 Common::ParamPackage GetNextInput();
43
44 /// For device input configuration/polling
45 void BeginConfiguration();
46 void EndConfiguration();
47
48 bool IsPolling() const {
49 return polling;
50 }
51
52private:
53 std::shared_ptr<CemuhookUDP::Client> client;
54 bool polling = false;
55};
56
57} // namespace InputCommon
diff --git a/src/shader_recompiler/CMakeLists.txt b/src/shader_recompiler/CMakeLists.txt
index bc3df80c8..4c76ce1ea 100644
--- a/src/shader_recompiler/CMakeLists.txt
+++ b/src/shader_recompiler/CMakeLists.txt
@@ -1,7 +1,5 @@
1add_library(shader_recompiler STATIC 1add_library(shader_recompiler STATIC
2 backend/bindings.h 2 backend/bindings.h
3 backend/glasm/emit_context.cpp
4 backend/glasm/emit_context.h
5 backend/glasm/emit_glasm.cpp 3 backend/glasm/emit_glasm.cpp
6 backend/glasm/emit_glasm.h 4 backend/glasm/emit_glasm.h
7 backend/glasm/emit_glasm_barriers.cpp 5 backend/glasm/emit_glasm_barriers.cpp
@@ -22,10 +20,10 @@ add_library(shader_recompiler STATIC
22 backend/glasm/emit_glasm_special.cpp 20 backend/glasm/emit_glasm_special.cpp
23 backend/glasm/emit_glasm_undefined.cpp 21 backend/glasm/emit_glasm_undefined.cpp
24 backend/glasm/emit_glasm_warp.cpp 22 backend/glasm/emit_glasm_warp.cpp
23 backend/glasm/glasm_emit_context.cpp
24 backend/glasm/glasm_emit_context.h
25 backend/glasm/reg_alloc.cpp 25 backend/glasm/reg_alloc.cpp
26 backend/glasm/reg_alloc.h 26 backend/glasm/reg_alloc.h
27 backend/glsl/emit_context.cpp
28 backend/glsl/emit_context.h
29 backend/glsl/emit_glsl.cpp 27 backend/glsl/emit_glsl.cpp
30 backend/glsl/emit_glsl.h 28 backend/glsl/emit_glsl.h
31 backend/glsl/emit_glsl_atomic.cpp 29 backend/glsl/emit_glsl_atomic.cpp
@@ -47,10 +45,10 @@ add_library(shader_recompiler STATIC
47 backend/glsl/emit_glsl_special.cpp 45 backend/glsl/emit_glsl_special.cpp
48 backend/glsl/emit_glsl_undefined.cpp 46 backend/glsl/emit_glsl_undefined.cpp
49 backend/glsl/emit_glsl_warp.cpp 47 backend/glsl/emit_glsl_warp.cpp
48 backend/glsl/glsl_emit_context.cpp
49 backend/glsl/glsl_emit_context.h
50 backend/glsl/var_alloc.cpp 50 backend/glsl/var_alloc.cpp
51 backend/glsl/var_alloc.h 51 backend/glsl/var_alloc.h
52 backend/spirv/emit_context.cpp
53 backend/spirv/emit_context.h
54 backend/spirv/emit_spirv.cpp 52 backend/spirv/emit_spirv.cpp
55 backend/spirv/emit_spirv.h 53 backend/spirv/emit_spirv.h
56 backend/spirv/emit_spirv_atomic.cpp 54 backend/spirv/emit_spirv_atomic.cpp
@@ -72,6 +70,8 @@ add_library(shader_recompiler STATIC
72 backend/spirv/emit_spirv_special.cpp 70 backend/spirv/emit_spirv_special.cpp
73 backend/spirv/emit_spirv_undefined.cpp 71 backend/spirv/emit_spirv_undefined.cpp
74 backend/spirv/emit_spirv_warp.cpp 72 backend/spirv/emit_spirv_warp.cpp
73 backend/spirv/spirv_emit_context.cpp
74 backend/spirv/spirv_emit_context.h
75 environment.h 75 environment.h
76 exception.h 76 exception.h
77 frontend/ir/abstract_syntax_list.h 77 frontend/ir/abstract_syntax_list.h
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm.cpp b/src/shader_recompiler/backend/glasm/emit_glasm.cpp
index 004658546..42eff443f 100644
--- a/src/shader_recompiler/backend/glasm/emit_glasm.cpp
+++ b/src/shader_recompiler/backend/glasm/emit_glasm.cpp
@@ -9,9 +9,9 @@
9#include "common/div_ceil.h" 9#include "common/div_ceil.h"
10#include "common/settings.h" 10#include "common/settings.h"
11#include "shader_recompiler/backend/bindings.h" 11#include "shader_recompiler/backend/bindings.h"
12#include "shader_recompiler/backend/glasm/emit_context.h"
13#include "shader_recompiler/backend/glasm/emit_glasm.h" 12#include "shader_recompiler/backend/glasm/emit_glasm.h"
14#include "shader_recompiler/backend/glasm/emit_glasm_instructions.h" 13#include "shader_recompiler/backend/glasm/emit_glasm_instructions.h"
14#include "shader_recompiler/backend/glasm/glasm_emit_context.h"
15#include "shader_recompiler/frontend/ir/ir_emitter.h" 15#include "shader_recompiler/frontend/ir/ir_emitter.h"
16#include "shader_recompiler/frontend/ir/program.h" 16#include "shader_recompiler/frontend/ir/program.h"
17#include "shader_recompiler/profile.h" 17#include "shader_recompiler/profile.h"
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_barriers.cpp b/src/shader_recompiler/backend/glasm/emit_glasm_barriers.cpp
index e69de29bb..c0b97683e 100644
--- a/src/shader_recompiler/backend/glasm/emit_glasm_barriers.cpp
+++ b/src/shader_recompiler/backend/glasm/emit_glasm_barriers.cpp
@@ -0,0 +1,22 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "shader_recompiler/backend/glasm/emit_glasm_instructions.h"
6#include "shader_recompiler/backend/glasm/glasm_emit_context.h"
7
8namespace Shader::Backend::GLASM {
9
10void EmitBarrier(EmitContext& ctx) {
11 ctx.Add("BAR;");
12}
13
14void EmitWorkgroupMemoryBarrier(EmitContext& ctx) {
15 ctx.Add("MEMBAR.CTA;");
16}
17
18void EmitDeviceMemoryBarrier(EmitContext& ctx) {
19 ctx.Add("MEMBAR;");
20}
21
22} // namespace Shader::Backend::GLASM
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_bitwise_conversion.cpp b/src/shader_recompiler/backend/glasm/emit_glasm_bitwise_conversion.cpp
index 9201ccd39..3bfcbbe65 100644
--- a/src/shader_recompiler/backend/glasm/emit_glasm_bitwise_conversion.cpp
+++ b/src/shader_recompiler/backend/glasm/emit_glasm_bitwise_conversion.cpp
@@ -2,8 +2,8 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "shader_recompiler/backend/glasm/emit_context.h"
6#include "shader_recompiler/backend/glasm/emit_glasm_instructions.h" 5#include "shader_recompiler/backend/glasm/emit_glasm_instructions.h"
6#include "shader_recompiler/backend/glasm/glasm_emit_context.h"
7#include "shader_recompiler/frontend/ir/value.h" 7#include "shader_recompiler/frontend/ir/value.h"
8 8
9namespace Shader::Backend::GLASM { 9namespace Shader::Backend::GLASM {
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_composite.cpp b/src/shader_recompiler/backend/glasm/emit_glasm_composite.cpp
index bff0b7c1c..babbe6654 100644
--- a/src/shader_recompiler/backend/glasm/emit_glasm_composite.cpp
+++ b/src/shader_recompiler/backend/glasm/emit_glasm_composite.cpp
@@ -2,8 +2,8 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "shader_recompiler/backend/glasm/emit_context.h"
6#include "shader_recompiler/backend/glasm/emit_glasm_instructions.h" 5#include "shader_recompiler/backend/glasm/emit_glasm_instructions.h"
6#include "shader_recompiler/backend/glasm/glasm_emit_context.h"
7#include "shader_recompiler/frontend/ir/value.h" 7#include "shader_recompiler/frontend/ir/value.h"
8 8
9namespace Shader::Backend::GLASM { 9namespace Shader::Backend::GLASM {
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_context_get_set.cpp b/src/shader_recompiler/backend/glasm/emit_glasm_context_get_set.cpp
index 02c9dc6d7..081b2c8e0 100644
--- a/src/shader_recompiler/backend/glasm/emit_glasm_context_get_set.cpp
+++ b/src/shader_recompiler/backend/glasm/emit_glasm_context_get_set.cpp
@@ -4,8 +4,8 @@
4 4
5#include <string_view> 5#include <string_view>
6 6
7#include "shader_recompiler/backend/glasm/emit_context.h"
8#include "shader_recompiler/backend/glasm/emit_glasm_instructions.h" 7#include "shader_recompiler/backend/glasm/emit_glasm_instructions.h"
8#include "shader_recompiler/backend/glasm/glasm_emit_context.h"
9#include "shader_recompiler/frontend/ir/value.h" 9#include "shader_recompiler/frontend/ir/value.h"
10#include "shader_recompiler/profile.h" 10#include "shader_recompiler/profile.h"
11#include "shader_recompiler/shader_info.h" 11#include "shader_recompiler/shader_info.h"
@@ -335,6 +335,35 @@ void EmitSetFragDepth(EmitContext& ctx, ScalarF32 value) {
335 ctx.Add("MOV.F result.depth.z,{};", value); 335 ctx.Add("MOV.F result.depth.z,{};", value);
336} 336}
337 337
338void EmitWorkgroupId(EmitContext& ctx, IR::Inst& inst) {
339 ctx.Add("MOV.S {},invocation.groupid;", inst);
340}
341
342void EmitLocalInvocationId(EmitContext& ctx, IR::Inst& inst) {
343 ctx.Add("MOV.S {},invocation.localid;", inst);
344}
345
346void EmitInvocationId(EmitContext& ctx, IR::Inst& inst) {
347 ctx.Add("MOV.S {}.x,primitive_invocation.x;", inst);
348}
349
350void EmitSampleId(EmitContext& ctx, IR::Inst& inst) {
351 ctx.Add("MOV.S {}.x,fragment.sampleid.x;", inst);
352}
353
354void EmitIsHelperInvocation(EmitContext& ctx, IR::Inst& inst) {
355 ctx.Add("MOV.S {}.x,fragment.helperthread.x;", inst);
356}
357
358void EmitYDirection(EmitContext& ctx, IR::Inst& inst) {
359 ctx.uses_y_direction = true;
360 ctx.Add("MOV.F {}.x,y_direction[0].w;", inst);
361}
362
363void EmitResolutionDownFactor(EmitContext& ctx, IR::Inst& inst) {
364 ctx.Add("MOV.F {}.x,scaling[0].z;", inst);
365}
366
338void EmitLoadLocal(EmitContext& ctx, IR::Inst& inst, ScalarU32 word_offset) { 367void EmitLoadLocal(EmitContext& ctx, IR::Inst& inst, ScalarU32 word_offset) {
339 ctx.Add("MOV.U {},lmem[{}].x;", inst, word_offset); 368 ctx.Add("MOV.U {},lmem[{}].x;", inst, word_offset);
340} 369}
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_control_flow.cpp b/src/shader_recompiler/backend/glasm/emit_glasm_control_flow.cpp
index e69de29bb..8a14fc8d9 100644
--- a/src/shader_recompiler/backend/glasm/emit_glasm_control_flow.cpp
+++ b/src/shader_recompiler/backend/glasm/emit_glasm_control_flow.cpp
@@ -0,0 +1,18 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "shader_recompiler/backend/glasm/emit_glasm_instructions.h"
6#include "shader_recompiler/backend/glasm/glasm_emit_context.h"
7
8namespace Shader::Backend::GLASM {
9
10void EmitJoin(EmitContext&) {
11 throw NotImplementedException("Join shouldn't be emitted");
12}
13
14void EmitDemoteToHelperInvocation(EmitContext& ctx) {
15 ctx.Add("KIL TR.x;");
16}
17
18} // namespace Shader::Backend::GLASM
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_convert.cpp b/src/shader_recompiler/backend/glasm/emit_glasm_convert.cpp
index ccdf1cbc8..4cff70fe4 100644
--- a/src/shader_recompiler/backend/glasm/emit_glasm_convert.cpp
+++ b/src/shader_recompiler/backend/glasm/emit_glasm_convert.cpp
@@ -4,8 +4,8 @@
4 4
5#include <string_view> 5#include <string_view>
6 6
7#include "shader_recompiler/backend/glasm/emit_context.h"
8#include "shader_recompiler/backend/glasm/emit_glasm_instructions.h" 7#include "shader_recompiler/backend/glasm/emit_glasm_instructions.h"
8#include "shader_recompiler/backend/glasm/glasm_emit_context.h"
9#include "shader_recompiler/frontend/ir/modifiers.h" 9#include "shader_recompiler/frontend/ir/modifiers.h"
10#include "shader_recompiler/frontend/ir/value.h" 10#include "shader_recompiler/frontend/ir/value.h"
11 11
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_floating_point.cpp b/src/shader_recompiler/backend/glasm/emit_glasm_floating_point.cpp
index 4ed58619d..356640471 100644
--- a/src/shader_recompiler/backend/glasm/emit_glasm_floating_point.cpp
+++ b/src/shader_recompiler/backend/glasm/emit_glasm_floating_point.cpp
@@ -4,8 +4,8 @@
4 4
5#include <string_view> 5#include <string_view>
6 6
7#include "shader_recompiler/backend/glasm/emit_context.h"
8#include "shader_recompiler/backend/glasm/emit_glasm_instructions.h" 7#include "shader_recompiler/backend/glasm/emit_glasm_instructions.h"
8#include "shader_recompiler/backend/glasm/glasm_emit_context.h"
9#include "shader_recompiler/frontend/ir/modifiers.h" 9#include "shader_recompiler/frontend/ir/modifiers.h"
10#include "shader_recompiler/frontend/ir/value.h" 10#include "shader_recompiler/frontend/ir/value.h"
11 11
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_image.cpp b/src/shader_recompiler/backend/glasm/emit_glasm_image.cpp
index d325d31c7..237a5af3f 100644
--- a/src/shader_recompiler/backend/glasm/emit_glasm_image.cpp
+++ b/src/shader_recompiler/backend/glasm/emit_glasm_image.cpp
@@ -4,8 +4,8 @@
4 4
5#include <utility> 5#include <utility>
6 6
7#include "shader_recompiler/backend/glasm/emit_context.h"
8#include "shader_recompiler/backend/glasm/emit_glasm_instructions.h" 7#include "shader_recompiler/backend/glasm/emit_glasm_instructions.h"
8#include "shader_recompiler/backend/glasm/glasm_emit_context.h"
9#include "shader_recompiler/frontend/ir/modifiers.h" 9#include "shader_recompiler/frontend/ir/modifiers.h"
10#include "shader_recompiler/frontend/ir/value.h" 10#include "shader_recompiler/frontend/ir/value.h"
11 11
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_integer.cpp b/src/shader_recompiler/backend/glasm/emit_glasm_integer.cpp
index 8aa494a4d..f698b8b9b 100644
--- a/src/shader_recompiler/backend/glasm/emit_glasm_integer.cpp
+++ b/src/shader_recompiler/backend/glasm/emit_glasm_integer.cpp
@@ -2,8 +2,8 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "shader_recompiler/backend/glasm/emit_context.h"
6#include "shader_recompiler/backend/glasm/emit_glasm_instructions.h" 5#include "shader_recompiler/backend/glasm/emit_glasm_instructions.h"
6#include "shader_recompiler/backend/glasm/glasm_emit_context.h"
7#include "shader_recompiler/frontend/ir/value.h" 7#include "shader_recompiler/frontend/ir/value.h"
8 8
9namespace Shader::Backend::GLASM { 9namespace Shader::Backend::GLASM {
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_logical.cpp b/src/shader_recompiler/backend/glasm/emit_glasm_logical.cpp
index e69de29bb..eed7bfec2 100644
--- a/src/shader_recompiler/backend/glasm/emit_glasm_logical.cpp
+++ b/src/shader_recompiler/backend/glasm/emit_glasm_logical.cpp
@@ -0,0 +1,26 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "shader_recompiler/backend/glasm/emit_glasm_instructions.h"
6#include "shader_recompiler/backend/glasm/glasm_emit_context.h"
7
8namespace Shader::Backend::GLASM {
9
10void EmitLogicalOr(EmitContext& ctx, IR::Inst& inst, ScalarS32 a, ScalarS32 b) {
11 ctx.Add("OR.S {},{},{};", inst, a, b);
12}
13
14void EmitLogicalAnd(EmitContext& ctx, IR::Inst& inst, ScalarS32 a, ScalarS32 b) {
15 ctx.Add("AND.S {},{},{};", inst, a, b);
16}
17
18void EmitLogicalXor(EmitContext& ctx, IR::Inst& inst, ScalarS32 a, ScalarS32 b) {
19 ctx.Add("XOR.S {},{},{};", inst, a, b);
20}
21
22void EmitLogicalNot(EmitContext& ctx, IR::Inst& inst, ScalarS32 value) {
23 ctx.Add("SEQ.S {},{},0;", inst, value);
24}
25
26} // namespace Shader::Backend::GLASM
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_memory.cpp b/src/shader_recompiler/backend/glasm/emit_glasm_memory.cpp
index af9fac7c1..f135b67f5 100644
--- a/src/shader_recompiler/backend/glasm/emit_glasm_memory.cpp
+++ b/src/shader_recompiler/backend/glasm/emit_glasm_memory.cpp
@@ -4,8 +4,8 @@
4 4
5#include <string_view> 5#include <string_view>
6 6
7#include "shader_recompiler/backend/glasm/emit_context.h"
8#include "shader_recompiler/backend/glasm/emit_glasm_instructions.h" 7#include "shader_recompiler/backend/glasm/emit_glasm_instructions.h"
8#include "shader_recompiler/backend/glasm/glasm_emit_context.h"
9#include "shader_recompiler/frontend/ir/program.h" 9#include "shader_recompiler/frontend/ir/program.h"
10#include "shader_recompiler/frontend/ir/value.h" 10#include "shader_recompiler/frontend/ir/value.h"
11#include "shader_recompiler/runtime_info.h" 11#include "shader_recompiler/runtime_info.h"
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_not_implemented.cpp b/src/shader_recompiler/backend/glasm/emit_glasm_not_implemented.cpp
index 681aeda8d..86287ee3f 100644
--- a/src/shader_recompiler/backend/glasm/emit_glasm_not_implemented.cpp
+++ b/src/shader_recompiler/backend/glasm/emit_glasm_not_implemented.cpp
@@ -4,8 +4,8 @@
4 4
5#include <string_view> 5#include <string_view>
6 6
7#include "shader_recompiler/backend/glasm/emit_context.h"
8#include "shader_recompiler/backend/glasm/emit_glasm_instructions.h" 7#include "shader_recompiler/backend/glasm/emit_glasm_instructions.h"
8#include "shader_recompiler/backend/glasm/glasm_emit_context.h"
9#include "shader_recompiler/frontend/ir/program.h" 9#include "shader_recompiler/frontend/ir/program.h"
10#include "shader_recompiler/frontend/ir/value.h" 10#include "shader_recompiler/frontend/ir/value.h"
11 11
@@ -17,110 +17,6 @@ namespace Shader::Backend::GLASM {
17 17
18#define NotImplemented() throw NotImplementedException("GLASM instruction {}", __LINE__) 18#define NotImplemented() throw NotImplementedException("GLASM instruction {}", __LINE__)
19 19
20static void DefinePhi(EmitContext& ctx, IR::Inst& phi) {
21 switch (phi.Type()) {
22 case IR::Type::U1:
23 case IR::Type::U32:
24 case IR::Type::F32:
25 ctx.reg_alloc.Define(phi);
26 break;
27 case IR::Type::U64:
28 case IR::Type::F64:
29 ctx.reg_alloc.LongDefine(phi);
30 break;
31 default:
32 throw NotImplementedException("Phi node type {}", phi.Type());
33 }
34}
35
36void EmitPhi(EmitContext& ctx, IR::Inst& phi) {
37 const size_t num_args{phi.NumArgs()};
38 for (size_t i = 0; i < num_args; ++i) {
39 ctx.reg_alloc.Consume(phi.Arg(i));
40 }
41 if (!phi.Definition<Id>().is_valid) {
42 // The phi node wasn't forward defined
43 DefinePhi(ctx, phi);
44 }
45}
46
47void EmitVoid(EmitContext&) {}
48
49void EmitReference(EmitContext& ctx, const IR::Value& value) {
50 ctx.reg_alloc.Consume(value);
51}
52
53void EmitPhiMove(EmitContext& ctx, const IR::Value& phi_value, const IR::Value& value) {
54 IR::Inst& phi{RegAlloc::AliasInst(*phi_value.Inst())};
55 if (!phi.Definition<Id>().is_valid) {
56 // The phi node wasn't forward defined
57 DefinePhi(ctx, phi);
58 }
59 const Register phi_reg{ctx.reg_alloc.Consume(IR::Value{&phi})};
60 const Value eval_value{ctx.reg_alloc.Consume(value)};
61
62 if (phi_reg == eval_value) {
63 return;
64 }
65 switch (phi.Flags<IR::Type>()) {
66 case IR::Type::U1:
67 case IR::Type::U32:
68 case IR::Type::F32:
69 ctx.Add("MOV.S {}.x,{};", phi_reg, ScalarS32{eval_value});
70 break;
71 case IR::Type::U64:
72 case IR::Type::F64:
73 ctx.Add("MOV.U64 {}.x,{};", phi_reg, ScalarRegister{eval_value});
74 break;
75 default:
76 throw NotImplementedException("Phi node type {}", phi.Type());
77 }
78}
79
80void EmitJoin(EmitContext& ctx) {
81 NotImplemented();
82}
83
84void EmitDemoteToHelperInvocation(EmitContext& ctx) {
85 ctx.Add("KIL TR.x;");
86}
87
88void EmitBarrier(EmitContext& ctx) {
89 ctx.Add("BAR;");
90}
91
92void EmitWorkgroupMemoryBarrier(EmitContext& ctx) {
93 ctx.Add("MEMBAR.CTA;");
94}
95
96void EmitDeviceMemoryBarrier(EmitContext& ctx) {
97 ctx.Add("MEMBAR;");
98}
99
100void EmitPrologue(EmitContext& ctx) {
101 // TODO
102}
103
104void EmitEpilogue(EmitContext& ctx) {
105 // TODO
106}
107
108void EmitEmitVertex(EmitContext& ctx, ScalarS32 stream) {
109 if (stream.type == Type::U32 && stream.imm_u32 == 0) {
110 ctx.Add("EMIT;");
111 } else {
112 ctx.Add("EMITS {};", stream);
113 }
114}
115
116void EmitEndPrimitive(EmitContext& ctx, const IR::Value& stream) {
117 if (!stream.IsImmediate()) {
118 LOG_WARNING(Shader_GLASM, "Stream is not immediate");
119 }
120 ctx.reg_alloc.Consume(stream);
121 ctx.Add("ENDPRIM;");
122}
123
124void EmitGetRegister(EmitContext& ctx) { 20void EmitGetRegister(EmitContext& ctx) {
125 NotImplemented(); 21 NotImplemented();
126} 22}
@@ -185,55 +81,6 @@ void EmitSetOFlag(EmitContext& ctx) {
185 NotImplemented(); 81 NotImplemented();
186} 82}
187 83
188void EmitWorkgroupId(EmitContext& ctx, IR::Inst& inst) {
189 ctx.Add("MOV.S {},invocation.groupid;", inst);
190}
191
192void EmitLocalInvocationId(EmitContext& ctx, IR::Inst& inst) {
193 ctx.Add("MOV.S {},invocation.localid;", inst);
194}
195
196void EmitInvocationId(EmitContext& ctx, IR::Inst& inst) {
197 ctx.Add("MOV.S {}.x,primitive_invocation.x;", inst);
198}
199
200void EmitSampleId(EmitContext& ctx, IR::Inst& inst) {
201 ctx.Add("MOV.S {}.x,fragment.sampleid.x;", inst);
202}
203
204void EmitIsHelperInvocation(EmitContext& ctx, IR::Inst& inst) {
205 ctx.Add("MOV.S {}.x,fragment.helperthread.x;", inst);
206}
207
208void EmitYDirection(EmitContext& ctx, IR::Inst& inst) {
209 ctx.uses_y_direction = true;
210 ctx.Add("MOV.F {}.x,y_direction[0].w;", inst);
211}
212
213void EmitResolutionDownFactor(EmitContext& ctx, IR::Inst& inst) {
214 ctx.Add("MOV.F {}.x,scaling[0].z;", inst);
215}
216
217void EmitUndefU1(EmitContext& ctx, IR::Inst& inst) {
218 ctx.Add("MOV.S {}.x,0;", inst);
219}
220
221void EmitUndefU8(EmitContext& ctx, IR::Inst& inst) {
222 ctx.Add("MOV.S {}.x,0;", inst);
223}
224
225void EmitUndefU16(EmitContext& ctx, IR::Inst& inst) {
226 ctx.Add("MOV.S {}.x,0;", inst);
227}
228
229void EmitUndefU32(EmitContext& ctx, IR::Inst& inst) {
230 ctx.Add("MOV.S {}.x,0;", inst);
231}
232
233void EmitUndefU64(EmitContext& ctx, IR::Inst& inst) {
234 ctx.LongAdd("MOV.S64 {}.x,0;", inst);
235}
236
237void EmitGetZeroFromOp(EmitContext& ctx) { 84void EmitGetZeroFromOp(EmitContext& ctx) {
238 NotImplemented(); 85 NotImplemented();
239} 86}
@@ -258,20 +105,4 @@ void EmitGetInBoundsFromOp(EmitContext& ctx) {
258 NotImplemented(); 105 NotImplemented();
259} 106}
260 107
261void EmitLogicalOr(EmitContext& ctx, IR::Inst& inst, ScalarS32 a, ScalarS32 b) {
262 ctx.Add("OR.S {},{},{};", inst, a, b);
263}
264
265void EmitLogicalAnd(EmitContext& ctx, IR::Inst& inst, ScalarS32 a, ScalarS32 b) {
266 ctx.Add("AND.S {},{},{};", inst, a, b);
267}
268
269void EmitLogicalXor(EmitContext& ctx, IR::Inst& inst, ScalarS32 a, ScalarS32 b) {
270 ctx.Add("XOR.S {},{},{};", inst, a, b);
271}
272
273void EmitLogicalNot(EmitContext& ctx, IR::Inst& inst, ScalarS32 value) {
274 ctx.Add("SEQ.S {},{},0;", inst, value);
275}
276
277} // namespace Shader::Backend::GLASM 108} // namespace Shader::Backend::GLASM
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_select.cpp b/src/shader_recompiler/backend/glasm/emit_glasm_select.cpp
index 68fff613c..dc441c56d 100644
--- a/src/shader_recompiler/backend/glasm/emit_glasm_select.cpp
+++ b/src/shader_recompiler/backend/glasm/emit_glasm_select.cpp
@@ -3,8 +3,8 @@
3// Licensed under GPLv2 or any later version 3// Licensed under GPLv2 or any later version
4// Refer to the license.txt file included. 4// Refer to the license.txt file included.
5 5
6#include "shader_recompiler/backend/glasm/emit_context.h"
7#include "shader_recompiler/backend/glasm/emit_glasm_instructions.h" 6#include "shader_recompiler/backend/glasm/emit_glasm_instructions.h"
7#include "shader_recompiler/backend/glasm/glasm_emit_context.h"
8#include "shader_recompiler/frontend/ir/value.h" 8#include "shader_recompiler/frontend/ir/value.h"
9 9
10namespace Shader::Backend::GLASM { 10namespace Shader::Backend::GLASM {
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_shared_memory.cpp b/src/shader_recompiler/backend/glasm/emit_glasm_shared_memory.cpp
index c1498f449..39e1c6c3a 100644
--- a/src/shader_recompiler/backend/glasm/emit_glasm_shared_memory.cpp
+++ b/src/shader_recompiler/backend/glasm/emit_glasm_shared_memory.cpp
@@ -3,8 +3,8 @@
3// Licensed under GPLv2 or any later version 3// Licensed under GPLv2 or any later version
4// Refer to the license.txt file included. 4// Refer to the license.txt file included.
5 5
6#include "shader_recompiler/backend/glasm/emit_context.h"
7#include "shader_recompiler/backend/glasm/emit_glasm_instructions.h" 6#include "shader_recompiler/backend/glasm/emit_glasm_instructions.h"
7#include "shader_recompiler/backend/glasm/glasm_emit_context.h"
8#include "shader_recompiler/frontend/ir/value.h" 8#include "shader_recompiler/frontend/ir/value.h"
9 9
10namespace Shader::Backend::GLASM { 10namespace Shader::Backend::GLASM {
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_special.cpp b/src/shader_recompiler/backend/glasm/emit_glasm_special.cpp
index e69de29bb..e7a5fb13a 100644
--- a/src/shader_recompiler/backend/glasm/emit_glasm_special.cpp
+++ b/src/shader_recompiler/backend/glasm/emit_glasm_special.cpp
@@ -0,0 +1,95 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "shader_recompiler/backend/glasm/emit_glasm_instructions.h"
6#include "shader_recompiler/backend/glasm/glasm_emit_context.h"
7#include "shader_recompiler/frontend/ir/value.h"
8
9namespace Shader::Backend::GLASM {
10
11static void DefinePhi(EmitContext& ctx, IR::Inst& phi) {
12 switch (phi.Type()) {
13 case IR::Type::U1:
14 case IR::Type::U32:
15 case IR::Type::F32:
16 ctx.reg_alloc.Define(phi);
17 break;
18 case IR::Type::U64:
19 case IR::Type::F64:
20 ctx.reg_alloc.LongDefine(phi);
21 break;
22 default:
23 throw NotImplementedException("Phi node type {}", phi.Type());
24 }
25}
26
27void EmitPhi(EmitContext& ctx, IR::Inst& phi) {
28 const size_t num_args{phi.NumArgs()};
29 for (size_t i = 0; i < num_args; ++i) {
30 ctx.reg_alloc.Consume(phi.Arg(i));
31 }
32 if (!phi.Definition<Id>().is_valid) {
33 // The phi node wasn't forward defined
34 DefinePhi(ctx, phi);
35 }
36}
37
38void EmitVoid(EmitContext&) {}
39
40void EmitReference(EmitContext& ctx, const IR::Value& value) {
41 ctx.reg_alloc.Consume(value);
42}
43
44void EmitPhiMove(EmitContext& ctx, const IR::Value& phi_value, const IR::Value& value) {
45 IR::Inst& phi{RegAlloc::AliasInst(*phi_value.Inst())};
46 if (!phi.Definition<Id>().is_valid) {
47 // The phi node wasn't forward defined
48 DefinePhi(ctx, phi);
49 }
50 const Register phi_reg{ctx.reg_alloc.Consume(IR::Value{&phi})};
51 const Value eval_value{ctx.reg_alloc.Consume(value)};
52
53 if (phi_reg == eval_value) {
54 return;
55 }
56 switch (phi.Flags<IR::Type>()) {
57 case IR::Type::U1:
58 case IR::Type::U32:
59 case IR::Type::F32:
60 ctx.Add("MOV.S {}.x,{};", phi_reg, ScalarS32{eval_value});
61 break;
62 case IR::Type::U64:
63 case IR::Type::F64:
64 ctx.Add("MOV.U64 {}.x,{};", phi_reg, ScalarRegister{eval_value});
65 break;
66 default:
67 throw NotImplementedException("Phi node type {}", phi.Type());
68 }
69}
70
71void EmitPrologue(EmitContext&) {
72 // TODO
73}
74
75void EmitEpilogue(EmitContext&) {
76 // TODO
77}
78
79void EmitEmitVertex(EmitContext& ctx, ScalarS32 stream) {
80 if (stream.type == Type::U32 && stream.imm_u32 == 0) {
81 ctx.Add("EMIT;");
82 } else {
83 ctx.Add("EMITS {};", stream);
84 }
85}
86
87void EmitEndPrimitive(EmitContext& ctx, const IR::Value& stream) {
88 if (!stream.IsImmediate()) {
89 LOG_WARNING(Shader_GLASM, "Stream is not immediate");
90 }
91 ctx.reg_alloc.Consume(stream);
92 ctx.Add("ENDPRIM;");
93}
94
95} // namespace Shader::Backend::GLASM
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_undefined.cpp b/src/shader_recompiler/backend/glasm/emit_glasm_undefined.cpp
index e69de29bb..875e9d991 100644
--- a/src/shader_recompiler/backend/glasm/emit_glasm_undefined.cpp
+++ b/src/shader_recompiler/backend/glasm/emit_glasm_undefined.cpp
@@ -0,0 +1,30 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "shader_recompiler/backend/glasm/emit_glasm_instructions.h"
6#include "shader_recompiler/backend/glasm/glasm_emit_context.h"
7
8namespace Shader::Backend::GLASM {
9
10void EmitUndefU1(EmitContext& ctx, IR::Inst& inst) {
11 ctx.Add("MOV.S {}.x,0;", inst);
12}
13
14void EmitUndefU8(EmitContext& ctx, IR::Inst& inst) {
15 ctx.Add("MOV.S {}.x,0;", inst);
16}
17
18void EmitUndefU16(EmitContext& ctx, IR::Inst& inst) {
19 ctx.Add("MOV.S {}.x,0;", inst);
20}
21
22void EmitUndefU32(EmitContext& ctx, IR::Inst& inst) {
23 ctx.Add("MOV.S {}.x,0;", inst);
24}
25
26void EmitUndefU64(EmitContext& ctx, IR::Inst& inst) {
27 ctx.LongAdd("MOV.S64 {}.x,0;", inst);
28}
29
30} // namespace Shader::Backend::GLASM
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_warp.cpp b/src/shader_recompiler/backend/glasm/emit_glasm_warp.cpp
index 544d475b4..32e0dd923 100644
--- a/src/shader_recompiler/backend/glasm/emit_glasm_warp.cpp
+++ b/src/shader_recompiler/backend/glasm/emit_glasm_warp.cpp
@@ -2,8 +2,8 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "shader_recompiler/backend/glasm/emit_context.h"
6#include "shader_recompiler/backend/glasm/emit_glasm_instructions.h" 5#include "shader_recompiler/backend/glasm/emit_glasm_instructions.h"
6#include "shader_recompiler/backend/glasm/glasm_emit_context.h"
7#include "shader_recompiler/frontend/ir/value.h" 7#include "shader_recompiler/frontend/ir/value.h"
8#include "shader_recompiler/profile.h" 8#include "shader_recompiler/profile.h"
9 9
diff --git a/src/shader_recompiler/backend/glasm/emit_context.cpp b/src/shader_recompiler/backend/glasm/glasm_emit_context.cpp
index 8fd459dfe..0401953f7 100644
--- a/src/shader_recompiler/backend/glasm/emit_context.cpp
+++ b/src/shader_recompiler/backend/glasm/glasm_emit_context.cpp
@@ -5,8 +5,8 @@
5#include <string_view> 5#include <string_view>
6 6
7#include "shader_recompiler/backend/bindings.h" 7#include "shader_recompiler/backend/bindings.h"
8#include "shader_recompiler/backend/glasm/emit_context.h"
9#include "shader_recompiler/backend/glasm/emit_glasm.h" 8#include "shader_recompiler/backend/glasm/emit_glasm.h"
9#include "shader_recompiler/backend/glasm/glasm_emit_context.h"
10#include "shader_recompiler/frontend/ir/program.h" 10#include "shader_recompiler/frontend/ir/program.h"
11#include "shader_recompiler/profile.h" 11#include "shader_recompiler/profile.h"
12#include "shader_recompiler/runtime_info.h" 12#include "shader_recompiler/runtime_info.h"
diff --git a/src/shader_recompiler/backend/glasm/emit_context.h b/src/shader_recompiler/backend/glasm/glasm_emit_context.h
index 8433e5c00..8433e5c00 100644
--- a/src/shader_recompiler/backend/glasm/emit_context.h
+++ b/src/shader_recompiler/backend/glasm/glasm_emit_context.h
diff --git a/src/shader_recompiler/backend/glasm/reg_alloc.cpp b/src/shader_recompiler/backend/glasm/reg_alloc.cpp
index 4c046db6e..201e428c1 100644
--- a/src/shader_recompiler/backend/glasm/reg_alloc.cpp
+++ b/src/shader_recompiler/backend/glasm/reg_alloc.cpp
@@ -6,7 +6,7 @@
6 6
7#include <fmt/format.h> 7#include <fmt/format.h>
8 8
9#include "shader_recompiler/backend/glasm/emit_context.h" 9#include "shader_recompiler/backend/glasm/glasm_emit_context.h"
10#include "shader_recompiler/backend/glasm/reg_alloc.h" 10#include "shader_recompiler/backend/glasm/reg_alloc.h"
11#include "shader_recompiler/exception.h" 11#include "shader_recompiler/exception.h"
12#include "shader_recompiler/frontend/ir/value.h" 12#include "shader_recompiler/frontend/ir/value.h"
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl.cpp b/src/shader_recompiler/backend/glsl/emit_glsl.cpp
index 8a430d573..78b2eeaa2 100644
--- a/src/shader_recompiler/backend/glsl/emit_glsl.cpp
+++ b/src/shader_recompiler/backend/glsl/emit_glsl.cpp
@@ -9,9 +9,9 @@
9 9
10#include "common/div_ceil.h" 10#include "common/div_ceil.h"
11#include "common/settings.h" 11#include "common/settings.h"
12#include "shader_recompiler/backend/glsl/emit_context.h"
13#include "shader_recompiler/backend/glsl/emit_glsl.h" 12#include "shader_recompiler/backend/glsl/emit_glsl.h"
14#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h" 13#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
14#include "shader_recompiler/backend/glsl/glsl_emit_context.h"
15#include "shader_recompiler/frontend/ir/ir_emitter.h" 15#include "shader_recompiler/frontend/ir/ir_emitter.h"
16 16
17namespace Shader::Backend::GLSL { 17namespace Shader::Backend::GLSL {
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_atomic.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_atomic.cpp
index 772acc5a4..dc377b053 100644
--- a/src/shader_recompiler/backend/glsl/emit_glsl_atomic.cpp
+++ b/src/shader_recompiler/backend/glsl/emit_glsl_atomic.cpp
@@ -4,8 +4,8 @@
4 4
5#include <string_view> 5#include <string_view>
6 6
7#include "shader_recompiler/backend/glsl/emit_context.h"
8#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h" 7#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
8#include "shader_recompiler/backend/glsl/glsl_emit_context.h"
9#include "shader_recompiler/frontend/ir/value.h" 9#include "shader_recompiler/frontend/ir/value.h"
10 10
11namespace Shader::Backend::GLSL { 11namespace Shader::Backend::GLSL {
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_barriers.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_barriers.cpp
index e1d1b558e..8a9faa394 100644
--- a/src/shader_recompiler/backend/glsl/emit_glsl_barriers.cpp
+++ b/src/shader_recompiler/backend/glsl/emit_glsl_barriers.cpp
@@ -2,8 +2,8 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "shader_recompiler/backend/glsl/emit_context.h"
6#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h" 5#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
6#include "shader_recompiler/backend/glsl/glsl_emit_context.h"
7#include "shader_recompiler/frontend/ir/value.h" 7#include "shader_recompiler/frontend/ir/value.h"
8 8
9namespace Shader::Backend::GLSL { 9namespace Shader::Backend::GLSL {
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_bitwise_conversion.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_bitwise_conversion.cpp
index 3c1714e89..0f2668d9e 100644
--- a/src/shader_recompiler/backend/glsl/emit_glsl_bitwise_conversion.cpp
+++ b/src/shader_recompiler/backend/glsl/emit_glsl_bitwise_conversion.cpp
@@ -4,8 +4,8 @@
4 4
5#include <string_view> 5#include <string_view>
6 6
7#include "shader_recompiler/backend/glsl/emit_context.h"
8#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h" 7#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
8#include "shader_recompiler/backend/glsl/glsl_emit_context.h"
9#include "shader_recompiler/frontend/ir/value.h" 9#include "shader_recompiler/frontend/ir/value.h"
10 10
11namespace Shader::Backend::GLSL { 11namespace Shader::Backend::GLSL {
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_composite.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_composite.cpp
index 49a66e3ec..98cc57e58 100644
--- a/src/shader_recompiler/backend/glsl/emit_glsl_composite.cpp
+++ b/src/shader_recompiler/backend/glsl/emit_glsl_composite.cpp
@@ -4,8 +4,8 @@
4 4
5#include <string_view> 5#include <string_view>
6 6
7#include "shader_recompiler/backend/glsl/emit_context.h"
8#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h" 7#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
8#include "shader_recompiler/backend/glsl/glsl_emit_context.h"
9#include "shader_recompiler/frontend/ir/value.h" 9#include "shader_recompiler/frontend/ir/value.h"
10 10
11namespace Shader::Backend::GLSL { 11namespace Shader::Backend::GLSL {
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_context_get_set.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_context_get_set.cpp
index 4c26f3829..1920047f4 100644
--- a/src/shader_recompiler/backend/glsl/emit_glsl_context_get_set.cpp
+++ b/src/shader_recompiler/backend/glsl/emit_glsl_context_get_set.cpp
@@ -4,8 +4,8 @@
4 4
5#include <string_view> 5#include <string_view>
6 6
7#include "shader_recompiler/backend/glsl/emit_context.h"
8#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h" 7#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
8#include "shader_recompiler/backend/glsl/glsl_emit_context.h"
9#include "shader_recompiler/frontend/ir/value.h" 9#include "shader_recompiler/frontend/ir/value.h"
10#include "shader_recompiler/profile.h" 10#include "shader_recompiler/profile.h"
11#include "shader_recompiler/runtime_info.h" 11#include "shader_recompiler/runtime_info.h"
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_control_flow.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_control_flow.cpp
index 53f8896be..c86465e8b 100644
--- a/src/shader_recompiler/backend/glsl/emit_glsl_control_flow.cpp
+++ b/src/shader_recompiler/backend/glsl/emit_glsl_control_flow.cpp
@@ -4,8 +4,8 @@
4 4
5#include <string_view> 5#include <string_view>
6 6
7#include "shader_recompiler/backend/glsl/emit_context.h"
8#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h" 7#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
8#include "shader_recompiler/backend/glsl/glsl_emit_context.h"
9#include "shader_recompiler/exception.h" 9#include "shader_recompiler/exception.h"
10 10
11namespace Shader::Backend::GLSL { 11namespace Shader::Backend::GLSL {
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_convert.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_convert.cpp
index eeae6562c..ce6ea1bb7 100644
--- a/src/shader_recompiler/backend/glsl/emit_glsl_convert.cpp
+++ b/src/shader_recompiler/backend/glsl/emit_glsl_convert.cpp
@@ -4,8 +4,8 @@
4 4
5#include <string_view> 5#include <string_view>
6 6
7#include "shader_recompiler/backend/glsl/emit_context.h"
8#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h" 7#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
8#include "shader_recompiler/backend/glsl/glsl_emit_context.h"
9#include "shader_recompiler/frontend/ir/value.h" 9#include "shader_recompiler/frontend/ir/value.h"
10 10
11namespace Shader::Backend::GLSL { 11namespace Shader::Backend::GLSL {
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_floating_point.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_floating_point.cpp
index d423bfb1b..b765a251b 100644
--- a/src/shader_recompiler/backend/glsl/emit_glsl_floating_point.cpp
+++ b/src/shader_recompiler/backend/glsl/emit_glsl_floating_point.cpp
@@ -4,8 +4,8 @@
4 4
5#include <string_view> 5#include <string_view>
6 6
7#include "shader_recompiler/backend/glsl/emit_context.h"
8#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h" 7#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
8#include "shader_recompiler/backend/glsl/glsl_emit_context.h"
9#include "shader_recompiler/frontend/ir/modifiers.h" 9#include "shader_recompiler/frontend/ir/modifiers.h"
10#include "shader_recompiler/frontend/ir/value.h" 10#include "shader_recompiler/frontend/ir/value.h"
11 11
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_image.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_image.cpp
index 2f78d0267..fae2e397a 100644
--- a/src/shader_recompiler/backend/glsl/emit_glsl_image.cpp
+++ b/src/shader_recompiler/backend/glsl/emit_glsl_image.cpp
@@ -4,8 +4,8 @@
4 4
5#include <string_view> 5#include <string_view>
6 6
7#include "shader_recompiler/backend/glsl/emit_context.h"
8#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h" 7#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
8#include "shader_recompiler/backend/glsl/glsl_emit_context.h"
9#include "shader_recompiler/frontend/ir/modifiers.h" 9#include "shader_recompiler/frontend/ir/modifiers.h"
10#include "shader_recompiler/frontend/ir/value.h" 10#include "shader_recompiler/frontend/ir/value.h"
11#include "shader_recompiler/profile.h" 11#include "shader_recompiler/profile.h"
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_integer.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_integer.cpp
index 88c1d4c5e..44060df33 100644
--- a/src/shader_recompiler/backend/glsl/emit_glsl_integer.cpp
+++ b/src/shader_recompiler/backend/glsl/emit_glsl_integer.cpp
@@ -4,8 +4,8 @@
4 4
5#include <string_view> 5#include <string_view>
6 6
7#include "shader_recompiler/backend/glsl/emit_context.h"
8#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h" 7#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
8#include "shader_recompiler/backend/glsl/glsl_emit_context.h"
9#include "shader_recompiler/frontend/ir/value.h" 9#include "shader_recompiler/frontend/ir/value.h"
10 10
11namespace Shader::Backend::GLSL { 11namespace Shader::Backend::GLSL {
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_logical.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_logical.cpp
index 338ff4bd6..742fec9cf 100644
--- a/src/shader_recompiler/backend/glsl/emit_glsl_logical.cpp
+++ b/src/shader_recompiler/backend/glsl/emit_glsl_logical.cpp
@@ -4,8 +4,8 @@
4 4
5#include <string_view> 5#include <string_view>
6 6
7#include "shader_recompiler/backend/glsl/emit_context.h"
8#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h" 7#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
8#include "shader_recompiler/backend/glsl/glsl_emit_context.h"
9#include "shader_recompiler/frontend/ir/value.h" 9#include "shader_recompiler/frontend/ir/value.h"
10 10
11namespace Shader::Backend::GLSL { 11namespace Shader::Backend::GLSL {
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_memory.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_memory.cpp
index e3957491f..9fd41b4fd 100644
--- a/src/shader_recompiler/backend/glsl/emit_glsl_memory.cpp
+++ b/src/shader_recompiler/backend/glsl/emit_glsl_memory.cpp
@@ -4,8 +4,8 @@
4 4
5#include <string_view> 5#include <string_view>
6 6
7#include "shader_recompiler/backend/glsl/emit_context.h"
8#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h" 7#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
8#include "shader_recompiler/backend/glsl/glsl_emit_context.h"
9#include "shader_recompiler/frontend/ir/value.h" 9#include "shader_recompiler/frontend/ir/value.h"
10#include "shader_recompiler/profile.h" 10#include "shader_recompiler/profile.h"
11 11
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_not_implemented.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_not_implemented.cpp
index f420fe388..4ebdfb3bc 100644
--- a/src/shader_recompiler/backend/glsl/emit_glsl_not_implemented.cpp
+++ b/src/shader_recompiler/backend/glsl/emit_glsl_not_implemented.cpp
@@ -4,8 +4,8 @@
4 4
5#include <string_view> 5#include <string_view>
6 6
7#include "shader_recompiler/backend/glsl/emit_context.h"
8#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h" 7#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
8#include "shader_recompiler/backend/glsl/glsl_emit_context.h"
9#include "shader_recompiler/frontend/ir/value.h" 9#include "shader_recompiler/frontend/ir/value.h"
10 10
11#ifdef _MSC_VER 11#ifdef _MSC_VER
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_select.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_select.cpp
index 49fba9073..b1e486e5f 100644
--- a/src/shader_recompiler/backend/glsl/emit_glsl_select.cpp
+++ b/src/shader_recompiler/backend/glsl/emit_glsl_select.cpp
@@ -4,8 +4,8 @@
4 4
5#include <string_view> 5#include <string_view>
6 6
7#include "shader_recompiler/backend/glsl/emit_context.h"
8#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h" 7#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
8#include "shader_recompiler/backend/glsl/glsl_emit_context.h"
9#include "shader_recompiler/frontend/ir/value.h" 9#include "shader_recompiler/frontend/ir/value.h"
10 10
11namespace Shader::Backend::GLSL { 11namespace Shader::Backend::GLSL {
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_shared_memory.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_shared_memory.cpp
index 518b78f06..74ae345e5 100644
--- a/src/shader_recompiler/backend/glsl/emit_glsl_shared_memory.cpp
+++ b/src/shader_recompiler/backend/glsl/emit_glsl_shared_memory.cpp
@@ -4,8 +4,8 @@
4 4
5#include <string_view> 5#include <string_view>
6 6
7#include "shader_recompiler/backend/glsl/emit_context.h"
8#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h" 7#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
8#include "shader_recompiler/backend/glsl/glsl_emit_context.h"
9#include "shader_recompiler/frontend/ir/value.h" 9#include "shader_recompiler/frontend/ir/value.h"
10 10
11namespace Shader::Backend::GLSL { 11namespace Shader::Backend::GLSL {
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_special.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_special.cpp
index 67f9dad68..b8ddafe48 100644
--- a/src/shader_recompiler/backend/glsl/emit_glsl_special.cpp
+++ b/src/shader_recompiler/backend/glsl/emit_glsl_special.cpp
@@ -4,8 +4,8 @@
4 4
5#include <string_view> 5#include <string_view>
6 6
7#include "shader_recompiler/backend/glsl/emit_context.h"
8#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h" 7#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
8#include "shader_recompiler/backend/glsl/glsl_emit_context.h"
9#include "shader_recompiler/frontend/ir/program.h" 9#include "shader_recompiler/frontend/ir/program.h"
10#include "shader_recompiler/frontend/ir/value.h" 10#include "shader_recompiler/frontend/ir/value.h"
11#include "shader_recompiler/profile.h" 11#include "shader_recompiler/profile.h"
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_undefined.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_undefined.cpp
index 15bf02dd6..cace1db85 100644
--- a/src/shader_recompiler/backend/glsl/emit_glsl_undefined.cpp
+++ b/src/shader_recompiler/backend/glsl/emit_glsl_undefined.cpp
@@ -4,8 +4,8 @@
4 4
5#include <string_view> 5#include <string_view>
6 6
7#include "shader_recompiler/backend/glsl/emit_context.h"
8#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h" 7#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
8#include "shader_recompiler/backend/glsl/glsl_emit_context.h"
9 9
10namespace Shader::Backend::GLSL { 10namespace Shader::Backend::GLSL {
11 11
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_warp.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_warp.cpp
index cd285e2c8..6e01979b4 100644
--- a/src/shader_recompiler/backend/glsl/emit_glsl_warp.cpp
+++ b/src/shader_recompiler/backend/glsl/emit_glsl_warp.cpp
@@ -4,8 +4,8 @@
4 4
5#include <string_view> 5#include <string_view>
6 6
7#include "shader_recompiler/backend/glsl/emit_context.h"
8#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h" 7#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
8#include "shader_recompiler/backend/glsl/glsl_emit_context.h"
9#include "shader_recompiler/frontend/ir/value.h" 9#include "shader_recompiler/frontend/ir/value.h"
10#include "shader_recompiler/profile.h" 10#include "shader_recompiler/profile.h"
11 11
diff --git a/src/shader_recompiler/backend/glsl/emit_context.cpp b/src/shader_recompiler/backend/glsl/glsl_emit_context.cpp
index 97bd59302..1de017e76 100644
--- a/src/shader_recompiler/backend/glsl/emit_context.cpp
+++ b/src/shader_recompiler/backend/glsl/glsl_emit_context.cpp
@@ -3,7 +3,7 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "shader_recompiler/backend/bindings.h" 5#include "shader_recompiler/backend/bindings.h"
6#include "shader_recompiler/backend/glsl/emit_context.h" 6#include "shader_recompiler/backend/glsl/glsl_emit_context.h"
7#include "shader_recompiler/frontend/ir/program.h" 7#include "shader_recompiler/frontend/ir/program.h"
8#include "shader_recompiler/profile.h" 8#include "shader_recompiler/profile.h"
9#include "shader_recompiler/runtime_info.h" 9#include "shader_recompiler/runtime_info.h"
diff --git a/src/shader_recompiler/backend/glsl/emit_context.h b/src/shader_recompiler/backend/glsl/glsl_emit_context.h
index d9b639d29..d9b639d29 100644
--- a/src/shader_recompiler/backend/glsl/emit_context.h
+++ b/src/shader_recompiler/backend/glsl/glsl_emit_context.h
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv.cpp b/src/shader_recompiler/backend/spirv/emit_spirv.cpp
index d7a86e270..6ce7ed12a 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv.cpp
@@ -11,6 +11,7 @@
11#include "common/settings.h" 11#include "common/settings.h"
12#include "shader_recompiler/backend/spirv/emit_spirv.h" 12#include "shader_recompiler/backend/spirv/emit_spirv.h"
13#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h" 13#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
14#include "shader_recompiler/backend/spirv/spirv_emit_context.h"
14#include "shader_recompiler/frontend/ir/basic_block.h" 15#include "shader_recompiler/frontend/ir/basic_block.h"
15#include "shader_recompiler/frontend/ir/program.h" 16#include "shader_recompiler/frontend/ir/program.h"
16 17
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv.h b/src/shader_recompiler/backend/spirv/emit_spirv.h
index 4b25534ce..b412957c7 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv.h
+++ b/src/shader_recompiler/backend/spirv/emit_spirv.h
@@ -6,13 +6,11 @@
6 6
7#include <vector> 7#include <vector>
8 8
9#include <sirit/sirit.h>
10
11#include "common/common_types.h" 9#include "common/common_types.h"
12#include "shader_recompiler/backend/bindings.h" 10#include "shader_recompiler/backend/bindings.h"
13#include "shader_recompiler/backend/spirv/emit_context.h"
14#include "shader_recompiler/frontend/ir/program.h" 11#include "shader_recompiler/frontend/ir/program.h"
15#include "shader_recompiler/profile.h" 12#include "shader_recompiler/profile.h"
13#include "shader_recompiler/runtime_info.h"
16 14
17namespace Shader::Backend::SPIRV { 15namespace Shader::Backend::SPIRV {
18 16
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_atomic.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_atomic.cpp
index 9af8bb9e1..0d37b405c 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_atomic.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_atomic.cpp
@@ -4,6 +4,7 @@
4 4
5#include "shader_recompiler/backend/spirv/emit_spirv.h" 5#include "shader_recompiler/backend/spirv/emit_spirv.h"
6#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h" 6#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
7#include "shader_recompiler/backend/spirv/spirv_emit_context.h"
7 8
8namespace Shader::Backend::SPIRV { 9namespace Shader::Backend::SPIRV {
9namespace { 10namespace {
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_barriers.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_barriers.cpp
index e0b52a001..9ce95a41b 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_barriers.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_barriers.cpp
@@ -4,6 +4,7 @@
4 4
5#include "shader_recompiler/backend/spirv/emit_spirv.h" 5#include "shader_recompiler/backend/spirv/emit_spirv.h"
6#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h" 6#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
7#include "shader_recompiler/backend/spirv/spirv_emit_context.h"
7#include "shader_recompiler/frontend/ir/modifiers.h" 8#include "shader_recompiler/frontend/ir/modifiers.h"
8 9
9namespace Shader::Backend::SPIRV { 10namespace Shader::Backend::SPIRV {
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_bitwise_conversion.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_bitwise_conversion.cpp
index bb11f4f4e..02d1e63f7 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_bitwise_conversion.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_bitwise_conversion.cpp
@@ -4,6 +4,7 @@
4 4
5#include "shader_recompiler/backend/spirv/emit_spirv.h" 5#include "shader_recompiler/backend/spirv/emit_spirv.h"
6#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h" 6#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
7#include "shader_recompiler/backend/spirv/spirv_emit_context.h"
7 8
8namespace Shader::Backend::SPIRV { 9namespace Shader::Backend::SPIRV {
9 10
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_composite.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_composite.cpp
index 10ff4ecab..5c3e1ee2b 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_composite.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_composite.cpp
@@ -4,6 +4,7 @@
4 4
5#include "shader_recompiler/backend/spirv/emit_spirv.h" 5#include "shader_recompiler/backend/spirv/emit_spirv.h"
6#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h" 6#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
7#include "shader_recompiler/backend/spirv/spirv_emit_context.h"
7#include "shader_recompiler/frontend/ir/modifiers.h" 8#include "shader_recompiler/frontend/ir/modifiers.h"
8 9
9namespace Shader::Backend::SPIRV { 10namespace Shader::Backend::SPIRV {
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp
index bac683ae1..ad84966b5 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp
@@ -7,6 +7,7 @@
7 7
8#include "shader_recompiler/backend/spirv/emit_spirv.h" 8#include "shader_recompiler/backend/spirv/emit_spirv.h"
9#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h" 9#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
10#include "shader_recompiler/backend/spirv/spirv_emit_context.h"
10 11
11namespace Shader::Backend::SPIRV { 12namespace Shader::Backend::SPIRV {
12namespace { 13namespace {
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_control_flow.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_control_flow.cpp
index d33486f28..1eca3aa85 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_control_flow.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_control_flow.cpp
@@ -4,6 +4,7 @@
4 4
5#include "shader_recompiler/backend/spirv/emit_spirv.h" 5#include "shader_recompiler/backend/spirv/emit_spirv.h"
6#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h" 6#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
7#include "shader_recompiler/backend/spirv/spirv_emit_context.h"
7 8
8namespace Shader::Backend::SPIRV { 9namespace Shader::Backend::SPIRV {
9 10
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_convert.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_convert.cpp
index fd42b7a16..832de2452 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_convert.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_convert.cpp
@@ -4,6 +4,7 @@
4 4
5#include "shader_recompiler/backend/spirv/emit_spirv.h" 5#include "shader_recompiler/backend/spirv/emit_spirv.h"
6#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h" 6#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
7#include "shader_recompiler/backend/spirv/spirv_emit_context.h"
7 8
8namespace Shader::Backend::SPIRV { 9namespace Shader::Backend::SPIRV {
9namespace { 10namespace {
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_floating_point.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_floating_point.cpp
index 61cf25f9c..0cdc46495 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_floating_point.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_floating_point.cpp
@@ -4,6 +4,7 @@
4 4
5#include "shader_recompiler/backend/spirv/emit_spirv.h" 5#include "shader_recompiler/backend/spirv/emit_spirv.h"
6#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h" 6#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
7#include "shader_recompiler/backend/spirv/spirv_emit_context.h"
7#include "shader_recompiler/frontend/ir/modifiers.h" 8#include "shader_recompiler/frontend/ir/modifiers.h"
8 9
9namespace Shader::Backend::SPIRV { 10namespace Shader::Backend::SPIRV {
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp
index 4d168a96d..d18d5f1d5 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp
@@ -6,6 +6,7 @@
6 6
7#include "shader_recompiler/backend/spirv/emit_spirv.h" 7#include "shader_recompiler/backend/spirv/emit_spirv.h"
8#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h" 8#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
9#include "shader_recompiler/backend/spirv/spirv_emit_context.h"
9#include "shader_recompiler/frontend/ir/modifiers.h" 10#include "shader_recompiler/frontend/ir/modifiers.h"
10 11
11namespace Shader::Backend::SPIRV { 12namespace Shader::Backend::SPIRV {
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_image_atomic.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_image_atomic.cpp
index d7f1a365a..a96190bc6 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_image_atomic.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_image_atomic.cpp
@@ -4,6 +4,7 @@
4 4
5#include "shader_recompiler/backend/spirv/emit_spirv.h" 5#include "shader_recompiler/backend/spirv/emit_spirv.h"
6#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h" 6#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
7#include "shader_recompiler/backend/spirv/spirv_emit_context.h"
7#include "shader_recompiler/frontend/ir/modifiers.h" 8#include "shader_recompiler/frontend/ir/modifiers.h"
8 9
9namespace Shader::Backend::SPIRV { 10namespace Shader::Backend::SPIRV {
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_integer.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_integer.cpp
index 50277eec3..44521f539 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_integer.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_integer.cpp
@@ -4,6 +4,7 @@
4 4
5#include "shader_recompiler/backend/spirv/emit_spirv.h" 5#include "shader_recompiler/backend/spirv/emit_spirv.h"
6#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h" 6#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
7#include "shader_recompiler/backend/spirv/spirv_emit_context.h"
7 8
8namespace Shader::Backend::SPIRV { 9namespace Shader::Backend::SPIRV {
9namespace { 10namespace {
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_logical.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_logical.cpp
index b9a9500fc..47745f7ee 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_logical.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_logical.cpp
@@ -4,6 +4,7 @@
4 4
5#include "shader_recompiler/backend/spirv/emit_spirv.h" 5#include "shader_recompiler/backend/spirv/emit_spirv.h"
6#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h" 6#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
7#include "shader_recompiler/backend/spirv/spirv_emit_context.h"
7 8
8namespace Shader::Backend::SPIRV { 9namespace Shader::Backend::SPIRV {
9 10
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_memory.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_memory.cpp
index 679ee2684..175f4be19 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_memory.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_memory.cpp
@@ -6,6 +6,7 @@
6 6
7#include "shader_recompiler/backend/spirv/emit_spirv.h" 7#include "shader_recompiler/backend/spirv/emit_spirv.h"
8#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h" 8#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
9#include "shader_recompiler/backend/spirv/spirv_emit_context.h"
9 10
10namespace Shader::Backend::SPIRV { 11namespace Shader::Backend::SPIRV {
11namespace { 12namespace {
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_select.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_select.cpp
index c5b4f4720..48caf1ffc 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_select.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_select.cpp
@@ -4,6 +4,7 @@
4 4
5#include "shader_recompiler/backend/spirv/emit_spirv.h" 5#include "shader_recompiler/backend/spirv/emit_spirv.h"
6#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h" 6#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
7#include "shader_recompiler/backend/spirv/spirv_emit_context.h"
7 8
8namespace Shader::Backend::SPIRV { 9namespace Shader::Backend::SPIRV {
9 10
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_shared_memory.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_shared_memory.cpp
index 9a79fc7a2..330c9052c 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_shared_memory.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_shared_memory.cpp
@@ -4,6 +4,7 @@
4 4
5#include "shader_recompiler/backend/spirv/emit_spirv.h" 5#include "shader_recompiler/backend/spirv/emit_spirv.h"
6#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h" 6#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
7#include "shader_recompiler/backend/spirv/spirv_emit_context.h"
7 8
8namespace Shader::Backend::SPIRV { 9namespace Shader::Backend::SPIRV {
9namespace { 10namespace {
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_special.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_special.cpp
index 9e7eb3cb1..d96a17583 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_special.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_special.cpp
@@ -4,6 +4,7 @@
4 4
5#include "shader_recompiler/backend/spirv/emit_spirv.h" 5#include "shader_recompiler/backend/spirv/emit_spirv.h"
6#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h" 6#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
7#include "shader_recompiler/backend/spirv/spirv_emit_context.h"
7 8
8namespace Shader::Backend::SPIRV { 9namespace Shader::Backend::SPIRV {
9namespace { 10namespace {
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_undefined.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_undefined.cpp
index c9f469e90..b5766fc52 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_undefined.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_undefined.cpp
@@ -4,6 +4,7 @@
4 4
5#include "shader_recompiler/backend/spirv/emit_spirv.h" 5#include "shader_recompiler/backend/spirv/emit_spirv.h"
6#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h" 6#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
7#include "shader_recompiler/backend/spirv/spirv_emit_context.h"
7 8
8namespace Shader::Backend::SPIRV { 9namespace Shader::Backend::SPIRV {
9 10
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_warp.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_warp.cpp
index cef52c56e..7034228bf 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_warp.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_warp.cpp
@@ -4,6 +4,7 @@
4 4
5#include "shader_recompiler/backend/spirv/emit_spirv.h" 5#include "shader_recompiler/backend/spirv/emit_spirv.h"
6#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h" 6#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
7#include "shader_recompiler/backend/spirv/spirv_emit_context.h"
7 8
8namespace Shader::Backend::SPIRV { 9namespace Shader::Backend::SPIRV {
9namespace { 10namespace {
diff --git a/src/shader_recompiler/backend/spirv/emit_context.cpp b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp
index 723455462..4b6f792bf 100644
--- a/src/shader_recompiler/backend/spirv/emit_context.cpp
+++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp
@@ -13,8 +13,8 @@
13 13
14#include "common/common_types.h" 14#include "common/common_types.h"
15#include "common/div_ceil.h" 15#include "common/div_ceil.h"
16#include "shader_recompiler/backend/spirv/emit_context.h"
17#include "shader_recompiler/backend/spirv/emit_spirv.h" 16#include "shader_recompiler/backend/spirv/emit_spirv.h"
17#include "shader_recompiler/backend/spirv/spirv_emit_context.h"
18 18
19namespace Shader::Backend::SPIRV { 19namespace Shader::Backend::SPIRV {
20namespace { 20namespace {
diff --git a/src/shader_recompiler/backend/spirv/emit_context.h b/src/shader_recompiler/backend/spirv/spirv_emit_context.h
index 63f8185d9..63f8185d9 100644
--- a/src/shader_recompiler/backend/spirv/emit_context.h
+++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.h
diff --git a/src/shader_recompiler/environment.h b/src/shader_recompiler/environment.h
index 8369d0d84..b4df73e8a 100644
--- a/src/shader_recompiler/environment.h
+++ b/src/shader_recompiler/environment.h
@@ -1,3 +1,7 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
1#pragma once 5#pragma once
2 6
3#include <array> 7#include <array>
diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt
index 91a30fef7..6a6325e38 100644
--- a/src/video_core/CMakeLists.txt
+++ b/src/video_core/CMakeLists.txt
@@ -3,6 +3,7 @@ add_subdirectory(host_shaders)
3if(LIBVA_FOUND) 3if(LIBVA_FOUND)
4 set_source_files_properties(command_classes/codecs/codec.cpp 4 set_source_files_properties(command_classes/codecs/codec.cpp
5 PROPERTIES COMPILE_DEFINITIONS LIBVA_FOUND=1) 5 PROPERTIES COMPILE_DEFINITIONS LIBVA_FOUND=1)
6 list(APPEND FFmpeg_LIBRARIES ${LIBVA_LIBRARIES})
6endif() 7endif()
7 8
8add_library(video_core STATIC 9add_library(video_core STATIC
diff --git a/src/video_core/command_classes/codecs/codec.cpp b/src/video_core/command_classes/codecs/codec.cpp
index 916277811..2a532b883 100644
--- a/src/video_core/command_classes/codecs/codec.cpp
+++ b/src/video_core/command_classes/codecs/codec.cpp
@@ -2,6 +2,8 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <algorithm>
6#include <cstdio>
5#include <fstream> 7#include <fstream>
6#include <vector> 8#include <vector>
7#include "common/assert.h" 9#include "common/assert.h"
@@ -15,12 +17,28 @@
15 17
16extern "C" { 18extern "C" {
17#include <libavutil/opt.h> 19#include <libavutil/opt.h>
20#ifdef LIBVA_FOUND
21// for querying VAAPI driver information
22#include <libavutil/hwcontext_vaapi.h>
23#endif
18} 24}
19 25
20namespace Tegra { 26namespace Tegra {
21namespace { 27namespace {
22constexpr AVPixelFormat PREFERRED_GPU_FMT = AV_PIX_FMT_NV12; 28constexpr AVPixelFormat PREFERRED_GPU_FMT = AV_PIX_FMT_NV12;
23constexpr AVPixelFormat PREFERRED_CPU_FMT = AV_PIX_FMT_YUV420P; 29constexpr AVPixelFormat PREFERRED_CPU_FMT = AV_PIX_FMT_YUV420P;
30constexpr std::array PREFERRED_GPU_DECODERS = {
31 AV_HWDEVICE_TYPE_CUDA,
32#ifdef _WIN32
33 AV_HWDEVICE_TYPE_D3D11VA,
34 AV_HWDEVICE_TYPE_DXVA2,
35#elif defined(__linux__)
36 AV_HWDEVICE_TYPE_VAAPI,
37 AV_HWDEVICE_TYPE_VDPAU,
38#endif
39 // last resort for Linux Flatpak (w/ NVIDIA)
40 AV_HWDEVICE_TYPE_VULKAN,
41};
24 42
25void AVPacketDeleter(AVPacket* ptr) { 43void AVPacketDeleter(AVPacket* ptr) {
26 av_packet_free(&ptr); 44 av_packet_free(&ptr);
@@ -59,46 +77,50 @@ Codec::~Codec() {
59 av_buffer_unref(&av_gpu_decoder); 77 av_buffer_unref(&av_gpu_decoder);
60} 78}
61 79
80// List all the currently available hwcontext in ffmpeg
81static std::vector<AVHWDeviceType> ListSupportedContexts() {
82 std::vector<AVHWDeviceType> contexts{};
83 AVHWDeviceType current_device_type = AV_HWDEVICE_TYPE_NONE;
84 do {
85 current_device_type = av_hwdevice_iterate_types(current_device_type);
86 contexts.push_back(current_device_type);
87 } while (current_device_type != AV_HWDEVICE_TYPE_NONE);
88 return contexts;
89}
90
62bool Codec::CreateGpuAvDevice() { 91bool Codec::CreateGpuAvDevice() {
63#if defined(LIBVA_FOUND)
64 static constexpr std::array<const char*, 3> VAAPI_DRIVERS = {
65 "i915",
66 "iHD",
67 "amdgpu",
68 };
69 AVDictionary* hwdevice_options = nullptr;
70 av_dict_set(&hwdevice_options, "connection_type", "drm", 0);
71 for (const auto& driver : VAAPI_DRIVERS) {
72 av_dict_set(&hwdevice_options, "kernel_driver", driver, 0);
73 const int hwdevice_error = av_hwdevice_ctx_create(&av_gpu_decoder, AV_HWDEVICE_TYPE_VAAPI,
74 nullptr, hwdevice_options, 0);
75 if (hwdevice_error >= 0) {
76 LOG_INFO(Service_NVDRV, "Using VA-API with {}", driver);
77 av_dict_free(&hwdevice_options);
78 av_codec_ctx->pix_fmt = AV_PIX_FMT_VAAPI;
79 return true;
80 }
81 LOG_DEBUG(Service_NVDRV, "VA-API av_hwdevice_ctx_create failed {}", hwdevice_error);
82 }
83 LOG_DEBUG(Service_NVDRV, "VA-API av_hwdevice_ctx_create failed for all drivers");
84 av_dict_free(&hwdevice_options);
85#endif
86 static constexpr auto HW_CONFIG_METHOD = AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX; 92 static constexpr auto HW_CONFIG_METHOD = AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX;
87 static constexpr std::array GPU_DECODER_TYPES{ 93 static const auto supported_contexts = ListSupportedContexts();
88 AV_HWDEVICE_TYPE_CUDA, 94 for (const auto& type : PREFERRED_GPU_DECODERS) {
89#ifdef _WIN32 95 if (std::none_of(supported_contexts.begin(), supported_contexts.end(),
90 AV_HWDEVICE_TYPE_D3D11VA, 96 [&type](const auto& context) { return context == type; })) {
91#else 97 LOG_DEBUG(Service_NVDRV, "{} explicitly unsupported", av_hwdevice_get_type_name(type));
92 AV_HWDEVICE_TYPE_VDPAU, 98 continue;
93#endif 99 }
94 };
95 for (const auto& type : GPU_DECODER_TYPES) {
96 const int hwdevice_res = av_hwdevice_ctx_create(&av_gpu_decoder, type, nullptr, nullptr, 0); 100 const int hwdevice_res = av_hwdevice_ctx_create(&av_gpu_decoder, type, nullptr, nullptr, 0);
97 if (hwdevice_res < 0) { 101 if (hwdevice_res < 0) {
98 LOG_DEBUG(Service_NVDRV, "{} av_hwdevice_ctx_create failed {}", 102 LOG_DEBUG(Service_NVDRV, "{} av_hwdevice_ctx_create failed {}",
99 av_hwdevice_get_type_name(type), hwdevice_res); 103 av_hwdevice_get_type_name(type), hwdevice_res);
100 continue; 104 continue;
101 } 105 }
106#ifdef LIBVA_FOUND
107 if (type == AV_HWDEVICE_TYPE_VAAPI) {
108 // we need to determine if this is an impersonated VAAPI driver
109 AVHWDeviceContext* hwctx =
110 static_cast<AVHWDeviceContext*>(static_cast<void*>(av_gpu_decoder->data));
111 AVVAAPIDeviceContext* vactx = static_cast<AVVAAPIDeviceContext*>(hwctx->hwctx);
112 const char* vendor_name = vaQueryVendorString(vactx->display);
113 if (strstr(vendor_name, "VDPAU backend")) {
114 // VDPAU impersonated VAAPI impl's are super buggy, we need to skip them
115 LOG_DEBUG(Service_NVDRV, "Skipping vdapu impersonated VAAPI driver");
116 continue;
117 } else {
118 // according to some user testing, certain vaapi driver (Intel?) could be buggy
119 // so let's log the driver name which may help the developers/supporters
120 LOG_DEBUG(Service_NVDRV, "Using VAAPI driver: {}", vendor_name);
121 }
122 }
123#endif
102 for (int i = 0;; i++) { 124 for (int i = 0;; i++) {
103 const AVCodecHWConfig* config = avcodec_get_hw_config(av_codec, i); 125 const AVCodecHWConfig* config = avcodec_get_hw_config(av_codec, i);
104 if (!config) { 126 if (!config) {
diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp
index ab7c21a49..8788f5148 100644
--- a/src/video_core/gpu.cpp
+++ b/src/video_core/gpu.cpp
@@ -185,16 +185,6 @@ struct GPU::Impl {
185 return *dma_pusher; 185 return *dma_pusher;
186 } 186 }
187 187
188 /// Returns a reference to the GPU CDMA pusher.
189 [[nodiscard]] Tegra::CDmaPusher& CDmaPusher() {
190 return *cdma_pusher;
191 }
192
193 /// Returns a const reference to the GPU CDMA pusher.
194 [[nodiscard]] const Tegra::CDmaPusher& CDmaPusher() const {
195 return *cdma_pusher;
196 }
197
198 /// Returns a reference to the underlying renderer. 188 /// Returns a reference to the underlying renderer.
199 [[nodiscard]] VideoCore::RendererBase& Renderer() { 189 [[nodiscard]] VideoCore::RendererBase& Renderer() {
200 return *renderer; 190 return *renderer;
@@ -338,25 +328,27 @@ struct GPU::Impl {
338 } 328 }
339 329
340 /// Push GPU command buffer entries to be processed 330 /// Push GPU command buffer entries to be processed
341 void PushCommandBuffer(Tegra::ChCommandHeaderList& entries) { 331 void PushCommandBuffer(u32 id, Tegra::ChCommandHeaderList& entries) {
342 if (!use_nvdec) { 332 if (!use_nvdec) {
343 return; 333 return;
344 } 334 }
345 335
346 if (!cdma_pusher) { 336 if (!cdma_pushers.contains(id)) {
347 cdma_pusher = std::make_unique<Tegra::CDmaPusher>(gpu); 337 cdma_pushers.insert_or_assign(id, std::make_unique<Tegra::CDmaPusher>(gpu));
348 } 338 }
349 339
350 // SubmitCommandBuffer would make the nvdec operations async, this is not currently working 340 // SubmitCommandBuffer would make the nvdec operations async, this is not currently working
351 // TODO(ameerj): RE proper async nvdec operation 341 // TODO(ameerj): RE proper async nvdec operation
352 // gpu_thread.SubmitCommandBuffer(std::move(entries)); 342 // gpu_thread.SubmitCommandBuffer(std::move(entries));
353 343 cdma_pushers[id]->ProcessEntries(std::move(entries));
354 cdma_pusher->ProcessEntries(std::move(entries));
355 } 344 }
356 345
357 /// Frees the CDMAPusher instance to free up resources 346 /// Frees the CDMAPusher instance to free up resources
358 void ClearCdmaInstance() { 347 void ClearCdmaInstance(u32 id) {
359 cdma_pusher.reset(); 348 const auto iter = cdma_pushers.find(id);
349 if (iter != cdma_pushers.end()) {
350 cdma_pushers.erase(iter);
351 }
360 } 352 }
361 353
362 /// Swap buffers (render frame) 354 /// Swap buffers (render frame)
@@ -659,7 +651,7 @@ struct GPU::Impl {
659 Core::System& system; 651 Core::System& system;
660 std::unique_ptr<Tegra::MemoryManager> memory_manager; 652 std::unique_ptr<Tegra::MemoryManager> memory_manager;
661 std::unique_ptr<Tegra::DmaPusher> dma_pusher; 653 std::unique_ptr<Tegra::DmaPusher> dma_pusher;
662 std::unique_ptr<Tegra::CDmaPusher> cdma_pusher; 654 std::map<u32, std::unique_ptr<Tegra::CDmaPusher>> cdma_pushers;
663 std::unique_ptr<VideoCore::RendererBase> renderer; 655 std::unique_ptr<VideoCore::RendererBase> renderer;
664 VideoCore::RasterizerInterface* rasterizer = nullptr; 656 VideoCore::RasterizerInterface* rasterizer = nullptr;
665 const bool use_nvdec; 657 const bool use_nvdec;
@@ -811,14 +803,6 @@ const Tegra::DmaPusher& GPU::DmaPusher() const {
811 return impl->DmaPusher(); 803 return impl->DmaPusher();
812} 804}
813 805
814Tegra::CDmaPusher& GPU::CDmaPusher() {
815 return impl->CDmaPusher();
816}
817
818const Tegra::CDmaPusher& GPU::CDmaPusher() const {
819 return impl->CDmaPusher();
820}
821
822VideoCore::RendererBase& GPU::Renderer() { 806VideoCore::RendererBase& GPU::Renderer() {
823 return impl->Renderer(); 807 return impl->Renderer();
824} 808}
@@ -887,12 +871,12 @@ void GPU::PushGPUEntries(Tegra::CommandList&& entries) {
887 impl->PushGPUEntries(std::move(entries)); 871 impl->PushGPUEntries(std::move(entries));
888} 872}
889 873
890void GPU::PushCommandBuffer(Tegra::ChCommandHeaderList& entries) { 874void GPU::PushCommandBuffer(u32 id, Tegra::ChCommandHeaderList& entries) {
891 impl->PushCommandBuffer(entries); 875 impl->PushCommandBuffer(id, entries);
892} 876}
893 877
894void GPU::ClearCdmaInstance() { 878void GPU::ClearCdmaInstance(u32 id) {
895 impl->ClearCdmaInstance(); 879 impl->ClearCdmaInstance(id);
896} 880}
897 881
898void GPU::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) { 882void GPU::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h
index 05e5c94f3..500411176 100644
--- a/src/video_core/gpu.h
+++ b/src/video_core/gpu.h
@@ -83,6 +83,7 @@ enum class DepthFormat : u32 {
83 S8_UINT_Z24_UNORM = 0x14, 83 S8_UINT_Z24_UNORM = 0x14,
84 D24X8_UNORM = 0x15, 84 D24X8_UNORM = 0x15,
85 D24S8_UNORM = 0x16, 85 D24S8_UNORM = 0x16,
86 S8_UINT = 0x17,
86 D24C8_UNORM = 0x18, 87 D24C8_UNORM = 0x18,
87 D32_FLOAT_S8X24_UINT = 0x19, 88 D32_FLOAT_S8X24_UINT = 0x19,
88}; 89};
@@ -241,10 +242,10 @@ public:
241 void PushGPUEntries(Tegra::CommandList&& entries); 242 void PushGPUEntries(Tegra::CommandList&& entries);
242 243
243 /// Push GPU command buffer entries to be processed 244 /// Push GPU command buffer entries to be processed
244 void PushCommandBuffer(Tegra::ChCommandHeaderList& entries); 245 void PushCommandBuffer(u32 id, Tegra::ChCommandHeaderList& entries);
245 246
246 /// Frees the CDMAPusher instance to free up resources 247 /// Frees the CDMAPusher instance to free up resources
247 void ClearCdmaInstance(); 248 void ClearCdmaInstance(u32 id);
248 249
249 /// Swap buffers (render frame) 250 /// Swap buffers (render frame)
250 void SwapBuffers(const Tegra::FramebufferConfig* framebuffer); 251 void SwapBuffers(const Tegra::FramebufferConfig* framebuffer);
diff --git a/src/video_core/host_shaders/CMakeLists.txt b/src/video_core/host_shaders/CMakeLists.txt
index d779a967a..fd3e41434 100644
--- a/src/video_core/host_shaders/CMakeLists.txt
+++ b/src/video_core/host_shaders/CMakeLists.txt
@@ -10,6 +10,8 @@ set(SHADER_FILES
10 astc_decoder.comp 10 astc_decoder.comp
11 block_linear_unswizzle_2d.comp 11 block_linear_unswizzle_2d.comp
12 block_linear_unswizzle_3d.comp 12 block_linear_unswizzle_3d.comp
13 convert_abgr8_to_d24s8.frag
14 convert_d24s8_to_abgr8.frag
13 convert_depth_to_float.frag 15 convert_depth_to_float.frag
14 convert_float_to_depth.frag 16 convert_float_to_depth.frag
15 full_screen_triangle.vert 17 full_screen_triangle.vert
diff --git a/src/video_core/host_shaders/convert_abgr8_to_d24s8.frag b/src/video_core/host_shaders/convert_abgr8_to_d24s8.frag
new file mode 100644
index 000000000..ea055ddad
--- /dev/null
+++ b/src/video_core/host_shaders/convert_abgr8_to_d24s8.frag
@@ -0,0 +1,18 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#version 450
6#extension GL_ARB_shader_stencil_export : require
7
8layout(binding = 0) uniform sampler2D color_texture;
9
10void main() {
11 ivec2 coord = ivec2(gl_FragCoord.xy);
12 uvec4 color = uvec4(texelFetch(color_texture, coord, 0).abgr * (exp2(8) - 1.0f));
13 uvec4 bytes = color << uvec4(24, 16, 8, 0);
14 uint depth_stencil_unorm = bytes.x | bytes.y | bytes.z | bytes.w;
15
16 gl_FragDepth = float(depth_stencil_unorm & 0x00FFFFFFu) / (exp2(24.0) - 1.0f);
17 gl_FragStencilRefARB = int(depth_stencil_unorm >> 24);
18}
diff --git a/src/video_core/host_shaders/convert_d24s8_to_abgr8.frag b/src/video_core/host_shaders/convert_d24s8_to_abgr8.frag
new file mode 100644
index 000000000..94368fb59
--- /dev/null
+++ b/src/video_core/host_shaders/convert_d24s8_to_abgr8.frag
@@ -0,0 +1,23 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#version 450
6
7layout(binding = 0) uniform sampler2D depth_tex;
8layout(binding = 1) uniform isampler2D stencil_tex;
9
10layout(location = 0) out vec4 color;
11
12void main() {
13 ivec2 coord = ivec2(gl_FragCoord.xy);
14 uint depth = uint(textureLod(depth_tex, coord, 0).r * (exp2(24.0) - 1.0f));
15 uint stencil = uint(textureLod(stencil_tex, coord, 0).r);
16
17 highp uint depth_val =
18 uint(textureLod(depth_tex, coord, 0).r * (exp2(32.0) - 1.0));
19 lowp uint stencil_val = textureLod(stencil_tex, coord, 0).r;
20 highp uvec4 components =
21 uvec4(stencil_val, (uvec3(depth_val) >> uvec3(24u, 16u, 8u)) & 0x000000FFu);
22 color.abgr = vec4(components) / (exp2(8.0) - 1.0);
23}
diff --git a/src/video_core/renderer_opengl/gl_texture_cache.cpp b/src/video_core/renderer_opengl/gl_texture_cache.cpp
index 6956535e5..14e6522f2 100644
--- a/src/video_core/renderer_opengl/gl_texture_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_texture_cache.cpp
@@ -9,6 +9,7 @@
9 9
10#include <glad/glad.h> 10#include <glad/glad.h>
11 11
12#include "common/bit_util.h"
12#include "common/literals.h" 13#include "common/literals.h"
13#include "common/settings.h" 14#include "common/settings.h"
14#include "video_core/renderer_opengl/gl_device.h" 15#include "video_core/renderer_opengl/gl_device.h"
@@ -148,6 +149,8 @@ GLenum AttachmentType(PixelFormat format) {
148 switch (const SurfaceType type = VideoCore::Surface::GetFormatType(format); type) { 149 switch (const SurfaceType type = VideoCore::Surface::GetFormatType(format); type) {
149 case SurfaceType::Depth: 150 case SurfaceType::Depth:
150 return GL_DEPTH_ATTACHMENT; 151 return GL_DEPTH_ATTACHMENT;
152 case SurfaceType::Stencil:
153 return GL_STENCIL_ATTACHMENT;
151 case SurfaceType::DepthStencil: 154 case SurfaceType::DepthStencil:
152 return GL_DEPTH_STENCIL_ATTACHMENT; 155 return GL_DEPTH_STENCIL_ATTACHMENT;
153 default: 156 default:
@@ -317,13 +320,12 @@ void AttachTexture(GLuint fbo, GLenum attachment, const ImageView* image_view) {
317 } 320 }
318} 321}
319 322
320OGLTexture MakeImage(const VideoCommon::ImageInfo& info, GLenum gl_internal_format) { 323OGLTexture MakeImage(const VideoCommon::ImageInfo& info, GLenum gl_internal_format,
324 GLsizei gl_num_levels) {
321 const GLenum target = ImageTarget(info); 325 const GLenum target = ImageTarget(info);
322 const GLsizei width = info.size.width; 326 const GLsizei width = info.size.width;
323 const GLsizei height = info.size.height; 327 const GLsizei height = info.size.height;
324 const GLsizei depth = info.size.depth; 328 const GLsizei depth = info.size.depth;
325 const int max_host_mip_levels = std::bit_width(info.size.width);
326 const GLsizei num_levels = std::min(info.resources.levels, max_host_mip_levels);
327 const GLsizei num_layers = info.resources.layers; 329 const GLsizei num_layers = info.resources.layers;
328 const GLsizei num_samples = info.num_samples; 330 const GLsizei num_samples = info.num_samples;
329 331
@@ -335,10 +337,10 @@ OGLTexture MakeImage(const VideoCommon::ImageInfo& info, GLenum gl_internal_form
335 } 337 }
336 switch (target) { 338 switch (target) {
337 case GL_TEXTURE_1D_ARRAY: 339 case GL_TEXTURE_1D_ARRAY:
338 glTextureStorage2D(handle, num_levels, gl_internal_format, width, num_layers); 340 glTextureStorage2D(handle, gl_num_levels, gl_internal_format, width, num_layers);
339 break; 341 break;
340 case GL_TEXTURE_2D_ARRAY: 342 case GL_TEXTURE_2D_ARRAY:
341 glTextureStorage3D(handle, num_levels, gl_internal_format, width, height, num_layers); 343 glTextureStorage3D(handle, gl_num_levels, gl_internal_format, width, height, num_layers);
342 break; 344 break;
343 case GL_TEXTURE_2D_MULTISAMPLE_ARRAY: { 345 case GL_TEXTURE_2D_MULTISAMPLE_ARRAY: {
344 // TODO: Where should 'fixedsamplelocations' come from? 346 // TODO: Where should 'fixedsamplelocations' come from?
@@ -348,10 +350,10 @@ OGLTexture MakeImage(const VideoCommon::ImageInfo& info, GLenum gl_internal_form
348 break; 350 break;
349 } 351 }
350 case GL_TEXTURE_RECTANGLE: 352 case GL_TEXTURE_RECTANGLE:
351 glTextureStorage2D(handle, num_levels, gl_internal_format, width, height); 353 glTextureStorage2D(handle, gl_num_levels, gl_internal_format, width, height);
352 break; 354 break;
353 case GL_TEXTURE_3D: 355 case GL_TEXTURE_3D:
354 glTextureStorage3D(handle, num_levels, gl_internal_format, width, height, depth); 356 glTextureStorage3D(handle, gl_num_levels, gl_internal_format, width, height, depth);
355 break; 357 break;
356 case GL_TEXTURE_BUFFER: 358 case GL_TEXTURE_BUFFER:
357 UNREACHABLE(); 359 UNREACHABLE();
@@ -397,9 +399,6 @@ OGLTexture MakeImage(const VideoCommon::ImageInfo& info, GLenum gl_internal_form
397 return GL_R32UI; 399 return GL_R32UI;
398} 400}
399 401
400[[nodiscard]] u32 NextPow2(u32 value) {
401 return 1U << (32U - std::countl_zero(value - 1U));
402}
403} // Anonymous namespace 402} // Anonymous namespace
404 403
405ImageBufferMap::~ImageBufferMap() { 404ImageBufferMap::~ImageBufferMap() {
@@ -526,8 +525,8 @@ void TextureCacheRuntime::CopyImage(Image& dst_image, Image& src_image,
526 } 525 }
527} 526}
528 527
529void TextureCacheRuntime::ConvertImage(Image& dst, Image& src, 528void TextureCacheRuntime::ReinterpretImage(Image& dst, Image& src,
530 std::span<const VideoCommon::ImageCopy> copies) { 529 std::span<const VideoCommon::ImageCopy> copies) {
531 LOG_DEBUG(Render_OpenGL, "Converting {} to {}", src.info.format, dst.info.format); 530 LOG_DEBUG(Render_OpenGL, "Converting {} to {}", src.info.format, dst.info.format);
532 format_conversion_pass.ConvertImage(dst, src, copies); 531 format_conversion_pass.ConvertImage(dst, src, copies);
533} 532}
@@ -696,7 +695,9 @@ Image::Image(TextureCacheRuntime& runtime_, const VideoCommon::ImageInfo& info_,
696 gl_format = tuple.format; 695 gl_format = tuple.format;
697 gl_type = tuple.type; 696 gl_type = tuple.type;
698 } 697 }
699 texture = MakeImage(info, gl_internal_format); 698 const int max_host_mip_levels = std::bit_width(info.size.width);
699 gl_num_levels = std::min(info.resources.levels, max_host_mip_levels);
700 texture = MakeImage(info, gl_internal_format, gl_num_levels);
700 current_texture = texture.handle; 701 current_texture = texture.handle;
701 if (runtime->device.HasDebuggingToolAttached()) { 702 if (runtime->device.HasDebuggingToolAttached()) {
702 const std::string name = VideoCommon::Name(*this); 703 const std::string name = VideoCommon::Name(*this);
@@ -724,6 +725,9 @@ void Image::UploadMemory(const ImageBufferMap& map,
724 u32 current_image_height = std::numeric_limits<u32>::max(); 725 u32 current_image_height = std::numeric_limits<u32>::max();
725 726
726 for (const VideoCommon::BufferImageCopy& copy : copies) { 727 for (const VideoCommon::BufferImageCopy& copy : copies) {
728 if (copy.image_subresource.base_level >= gl_num_levels) {
729 continue;
730 }
727 if (current_row_length != copy.buffer_row_length) { 731 if (current_row_length != copy.buffer_row_length) {
728 current_row_length = copy.buffer_row_length; 732 current_row_length = copy.buffer_row_length;
729 glPixelStorei(GL_UNPACK_ROW_LENGTH, current_row_length); 733 glPixelStorei(GL_UNPACK_ROW_LENGTH, current_row_length);
@@ -753,6 +757,9 @@ void Image::DownloadMemory(ImageBufferMap& map,
753 u32 current_image_height = std::numeric_limits<u32>::max(); 757 u32 current_image_height = std::numeric_limits<u32>::max();
754 758
755 for (const VideoCommon::BufferImageCopy& copy : copies) { 759 for (const VideoCommon::BufferImageCopy& copy : copies) {
760 if (copy.image_subresource.base_level >= gl_num_levels) {
761 continue;
762 }
756 if (current_row_length != copy.buffer_row_length) { 763 if (current_row_length != copy.buffer_row_length) {
757 current_row_length = copy.buffer_row_length; 764 current_row_length = copy.buffer_row_length;
758 glPixelStorei(GL_PACK_ROW_LENGTH, current_row_length); 765 glPixelStorei(GL_PACK_ROW_LENGTH, current_row_length);
@@ -792,7 +799,7 @@ GLuint Image::StorageHandle() noexcept {
792 } 799 }
793 store_view.Create(); 800 store_view.Create();
794 glTextureView(store_view.handle, ImageTarget(info), current_texture, GL_RGBA8, 0, 801 glTextureView(store_view.handle, ImageTarget(info), current_texture, GL_RGBA8, 0,
795 info.resources.levels, 0, info.resources.layers); 802 gl_num_levels, 0, info.resources.layers);
796 return store_view.handle; 803 return store_view.handle;
797 default: 804 default:
798 return current_texture; 805 return current_texture;
@@ -907,6 +914,8 @@ void Image::Scale(bool up_scale) {
907 return GL_COLOR_ATTACHMENT0; 914 return GL_COLOR_ATTACHMENT0;
908 case SurfaceType::Depth: 915 case SurfaceType::Depth:
909 return GL_DEPTH_ATTACHMENT; 916 return GL_DEPTH_ATTACHMENT;
917 case SurfaceType::Stencil:
918 return GL_STENCIL_ATTACHMENT;
910 case SurfaceType::DepthStencil: 919 case SurfaceType::DepthStencil:
911 return GL_DEPTH_STENCIL_ATTACHMENT; 920 return GL_DEPTH_STENCIL_ATTACHMENT;
912 default: 921 default:
@@ -920,8 +929,10 @@ void Image::Scale(bool up_scale) {
920 return GL_COLOR_BUFFER_BIT; 929 return GL_COLOR_BUFFER_BIT;
921 case SurfaceType::Depth: 930 case SurfaceType::Depth:
922 return GL_DEPTH_BUFFER_BIT; 931 return GL_DEPTH_BUFFER_BIT;
932 case SurfaceType::Stencil:
933 return GL_STENCIL_BUFFER_BIT;
923 case SurfaceType::DepthStencil: 934 case SurfaceType::DepthStencil:
924 return GL_STENCIL_BUFFER_BIT | GL_DEPTH_BUFFER_BIT; 935 return GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT;
925 default: 936 default:
926 UNREACHABLE(); 937 UNREACHABLE();
927 return GL_COLOR_BUFFER_BIT; 938 return GL_COLOR_BUFFER_BIT;
@@ -933,8 +944,10 @@ void Image::Scale(bool up_scale) {
933 return 0; 944 return 0;
934 case SurfaceType::Depth: 945 case SurfaceType::Depth:
935 return 1; 946 return 1;
936 case SurfaceType::DepthStencil: 947 case SurfaceType::Stencil:
937 return 2; 948 return 2;
949 case SurfaceType::DepthStencil:
950 return 3;
938 default: 951 default:
939 UNREACHABLE(); 952 UNREACHABLE();
940 return 0; 953 return 0;
@@ -956,7 +969,7 @@ void Image::Scale(bool up_scale) {
956 auto dst_info = info; 969 auto dst_info = info;
957 dst_info.size.width = scaled_width; 970 dst_info.size.width = scaled_width;
958 dst_info.size.height = scaled_height; 971 dst_info.size.height = scaled_height;
959 upscaled_backup = MakeImage(dst_info, gl_internal_format); 972 upscaled_backup = MakeImage(dst_info, gl_internal_format, gl_num_levels);
960 } 973 }
961 const u32 src_width = up_scale ? original_width : scaled_width; 974 const u32 src_width = up_scale ? original_width : scaled_width;
962 const u32 src_height = up_scale ? original_height : scaled_height; 975 const u32 src_height = up_scale ? original_height : scaled_height;
@@ -1264,10 +1277,20 @@ Framebuffer::Framebuffer(TextureCacheRuntime& runtime, std::span<ImageView*, NUM
1264 } 1277 }
1265 1278
1266 if (const ImageView* const image_view = depth_buffer; image_view) { 1279 if (const ImageView* const image_view = depth_buffer; image_view) {
1267 if (GetFormatType(image_view->format) == SurfaceType::DepthStencil) { 1280 switch (GetFormatType(image_view->format)) {
1281 case SurfaceType::Depth:
1282 buffer_bits |= GL_DEPTH_BUFFER_BIT;
1283 break;
1284 case SurfaceType::Stencil:
1285 buffer_bits |= GL_STENCIL_BUFFER_BIT;
1286 break;
1287 case SurfaceType::DepthStencil:
1268 buffer_bits |= GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT; 1288 buffer_bits |= GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT;
1269 } else { 1289 break;
1290 default:
1291 UNREACHABLE();
1270 buffer_bits |= GL_DEPTH_BUFFER_BIT; 1292 buffer_bits |= GL_DEPTH_BUFFER_BIT;
1293 break;
1271 } 1294 }
1272 const GLenum attachment = AttachmentType(image_view->format); 1295 const GLenum attachment = AttachmentType(image_view->format);
1273 AttachTexture(handle, attachment, image_view); 1296 AttachTexture(handle, attachment, image_view);
@@ -1308,7 +1331,7 @@ void FormatConversionPass::ConvertImage(Image& dst_image, Image& src_image,
1308 const u32 copy_size = region.width * region.height * region.depth * img_bpp; 1331 const u32 copy_size = region.width * region.height * region.depth * img_bpp;
1309 if (pbo_size < copy_size) { 1332 if (pbo_size < copy_size) {
1310 intermediate_pbo.Create(); 1333 intermediate_pbo.Create();
1311 pbo_size = NextPow2(copy_size); 1334 pbo_size = Common::NextPow2(copy_size);
1312 glNamedBufferData(intermediate_pbo.handle, pbo_size, nullptr, GL_STREAM_COPY); 1335 glNamedBufferData(intermediate_pbo.handle, pbo_size, nullptr, GL_STREAM_COPY);
1313 } 1336 }
1314 // Copy from source to PBO 1337 // Copy from source to PBO
diff --git a/src/video_core/renderer_opengl/gl_texture_cache.h b/src/video_core/renderer_opengl/gl_texture_cache.h
index 578f8d523..37d5e6a6b 100644
--- a/src/video_core/renderer_opengl/gl_texture_cache.h
+++ b/src/video_core/renderer_opengl/gl_texture_cache.h
@@ -84,9 +84,13 @@ public:
84 84
85 u64 GetDeviceLocalMemory() const; 85 u64 GetDeviceLocalMemory() const;
86 86
87 bool ShouldReinterpret([[maybe_unused]] Image& dst, [[maybe_unused]] Image& src) {
88 return true;
89 }
90
87 void CopyImage(Image& dst, Image& src, std::span<const VideoCommon::ImageCopy> copies); 91 void CopyImage(Image& dst, Image& src, std::span<const VideoCommon::ImageCopy> copies);
88 92
89 void ConvertImage(Image& dst, Image& src, std::span<const VideoCommon::ImageCopy> copies); 93 void ReinterpretImage(Image& dst, Image& src, std::span<const VideoCommon::ImageCopy> copies);
90 94
91 void ConvertImage(Framebuffer* dst, ImageView& dst_view, ImageView& src_view, bool rescaled) { 95 void ConvertImage(Framebuffer* dst, ImageView& dst_view, ImageView& src_view, bool rescaled) {
92 UNIMPLEMENTED(); 96 UNIMPLEMENTED();
@@ -164,8 +168,8 @@ private:
164 168
165 std::array<GLuint, Shader::NUM_TEXTURE_TYPES> null_image_views{}; 169 std::array<GLuint, Shader::NUM_TEXTURE_TYPES> null_image_views{};
166 170
167 std::array<OGLFramebuffer, 3> rescale_draw_fbos; 171 std::array<OGLFramebuffer, 4> rescale_draw_fbos;
168 std::array<OGLFramebuffer, 3> rescale_read_fbos; 172 std::array<OGLFramebuffer, 4> rescale_read_fbos;
169 const Settings::ResolutionScalingInfo& resolution; 173 const Settings::ResolutionScalingInfo& resolution;
170}; 174};
171 175
@@ -221,6 +225,7 @@ private:
221 GLenum gl_internal_format = GL_NONE; 225 GLenum gl_internal_format = GL_NONE;
222 GLenum gl_format = GL_NONE; 226 GLenum gl_format = GL_NONE;
223 GLenum gl_type = GL_NONE; 227 GLenum gl_type = GL_NONE;
228 GLsizei gl_num_levels{};
224 TextureCacheRuntime* runtime{}; 229 TextureCacheRuntime* runtime{};
225 GLuint current_texture{}; 230 GLuint current_texture{};
226}; 231};
@@ -338,7 +343,6 @@ struct TextureCacheParams {
338 static constexpr bool FRAMEBUFFER_BLITS = true; 343 static constexpr bool FRAMEBUFFER_BLITS = true;
339 static constexpr bool HAS_EMULATED_COPIES = true; 344 static constexpr bool HAS_EMULATED_COPIES = true;
340 static constexpr bool HAS_DEVICE_MEMORY_INFO = true; 345 static constexpr bool HAS_DEVICE_MEMORY_INFO = true;
341 static constexpr bool HAS_PIXEL_FORMAT_CONVERSIONS = true;
342 346
343 using Runtime = OpenGL::TextureCacheRuntime; 347 using Runtime = OpenGL::TextureCacheRuntime;
344 using Image = OpenGL::Image; 348 using Image = OpenGL::Image;
diff --git a/src/video_core/renderer_opengl/maxwell_to_gl.h b/src/video_core/renderer_opengl/maxwell_to_gl.h
index 39158aa3e..daba42ed9 100644
--- a/src/video_core/renderer_opengl/maxwell_to_gl.h
+++ b/src/video_core/renderer_opengl/maxwell_to_gl.h
@@ -108,6 +108,7 @@ constexpr std::array<FormatTuple, VideoCore::Surface::MaxPixelFormat> FORMAT_TAB
108 {GL_RGB9_E5, GL_RGB, GL_UNSIGNED_INT_5_9_9_9_REV}, // E5B9G9R9_FLOAT 108 {GL_RGB9_E5, GL_RGB, GL_UNSIGNED_INT_5_9_9_9_REV}, // E5B9G9R9_FLOAT
109 {GL_DEPTH_COMPONENT32F, GL_DEPTH_COMPONENT, GL_FLOAT}, // D32_FLOAT 109 {GL_DEPTH_COMPONENT32F, GL_DEPTH_COMPONENT, GL_FLOAT}, // D32_FLOAT
110 {GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT}, // D16_UNORM 110 {GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT}, // D16_UNORM
111 {GL_STENCIL_INDEX8, GL_STENCIL, GL_UNSIGNED_BYTE}, // S8_UINT
111 {GL_DEPTH24_STENCIL8, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8}, // D24_UNORM_S8_UINT 112 {GL_DEPTH24_STENCIL8, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8}, // D24_UNORM_S8_UINT
112 {GL_DEPTH24_STENCIL8, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8}, // S8_UINT_D24_UNORM 113 {GL_DEPTH24_STENCIL8, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8}, // S8_UINT_D24_UNORM
113 {GL_DEPTH32F_STENCIL8, GL_DEPTH_STENCIL, 114 {GL_DEPTH32F_STENCIL8, GL_DEPTH_STENCIL,
diff --git a/src/video_core/renderer_vulkan/blit_image.cpp b/src/video_core/renderer_vulkan/blit_image.cpp
index b3884a4f5..9a38b6b34 100644
--- a/src/video_core/renderer_vulkan/blit_image.cpp
+++ b/src/video_core/renderer_vulkan/blit_image.cpp
@@ -4,6 +4,8 @@
4 4
5#include <algorithm> 5#include <algorithm>
6 6
7#include "video_core/host_shaders/convert_abgr8_to_d24s8_frag_spv.h"
8#include "video_core/host_shaders/convert_d24s8_to_abgr8_frag_spv.h"
7#include "video_core/host_shaders/convert_depth_to_float_frag_spv.h" 9#include "video_core/host_shaders/convert_depth_to_float_frag_spv.h"
8#include "video_core/host_shaders/convert_float_to_depth_frag_spv.h" 10#include "video_core/host_shaders/convert_float_to_depth_frag_spv.h"
9#include "video_core/host_shaders/full_screen_triangle_vert_spv.h" 11#include "video_core/host_shaders/full_screen_triangle_vert_spv.h"
@@ -354,6 +356,8 @@ BlitImageHelper::BlitImageHelper(const Device& device_, VKScheduler& scheduler_,
354 blit_color_to_color_frag(BuildShader(device, VULKAN_BLIT_COLOR_FLOAT_FRAG_SPV)), 356 blit_color_to_color_frag(BuildShader(device, VULKAN_BLIT_COLOR_FLOAT_FRAG_SPV)),
355 convert_depth_to_float_frag(BuildShader(device, CONVERT_DEPTH_TO_FLOAT_FRAG_SPV)), 357 convert_depth_to_float_frag(BuildShader(device, CONVERT_DEPTH_TO_FLOAT_FRAG_SPV)),
356 convert_float_to_depth_frag(BuildShader(device, CONVERT_FLOAT_TO_DEPTH_FRAG_SPV)), 358 convert_float_to_depth_frag(BuildShader(device, CONVERT_FLOAT_TO_DEPTH_FRAG_SPV)),
359 convert_abgr8_to_d24s8_frag(BuildShader(device, CONVERT_ABGR8_TO_D24S8_FRAG_SPV)),
360 convert_d24s8_to_abgr8_frag(BuildShader(device, CONVERT_D24S8_TO_ABGR8_FRAG_SPV)),
357 linear_sampler(device.GetLogical().CreateSampler(SAMPLER_CREATE_INFO<VK_FILTER_LINEAR>)), 361 linear_sampler(device.GetLogical().CreateSampler(SAMPLER_CREATE_INFO<VK_FILTER_LINEAR>)),
358 nearest_sampler(device.GetLogical().CreateSampler(SAMPLER_CREATE_INFO<VK_FILTER_NEAREST>)) { 362 nearest_sampler(device.GetLogical().CreateSampler(SAMPLER_CREATE_INFO<VK_FILTER_NEAREST>)) {
359 if (device.IsExtShaderStencilExportSupported()) { 363 if (device.IsExtShaderStencilExportSupported()) {
@@ -448,6 +452,22 @@ void BlitImageHelper::ConvertR16ToD16(const Framebuffer* dst_framebuffer,
448 Convert(*convert_r16_to_d16_pipeline, dst_framebuffer, src_image_view, up_scale, down_shift); 452 Convert(*convert_r16_to_d16_pipeline, dst_framebuffer, src_image_view, up_scale, down_shift);
449} 453}
450 454
455void BlitImageHelper::ConvertABGR8ToD24S8(const Framebuffer* dst_framebuffer,
456 ImageView& src_image_view, u32 up_scale, u32 down_shift) {
457 ConvertPipelineDepthTargetEx(convert_abgr8_to_d24s8_pipeline, dst_framebuffer->RenderPass(),
458 convert_abgr8_to_d24s8_frag, true);
459 ConvertColor(*convert_abgr8_to_d24s8_pipeline, dst_framebuffer, src_image_view, up_scale,
460 down_shift);
461}
462
463void BlitImageHelper::ConvertD24S8ToABGR8(const Framebuffer* dst_framebuffer,
464 ImageView& src_image_view, u32 up_scale, u32 down_shift) {
465 ConvertPipelineColorTargetEx(convert_d24s8_to_abgr8_pipeline, dst_framebuffer->RenderPass(),
466 convert_d24s8_to_abgr8_frag, false);
467 ConvertDepthStencil(*convert_d24s8_to_abgr8_pipeline, dst_framebuffer, src_image_view, up_scale,
468 down_shift);
469}
470
451void BlitImageHelper::Convert(VkPipeline pipeline, const Framebuffer* dst_framebuffer, 471void BlitImageHelper::Convert(VkPipeline pipeline, const Framebuffer* dst_framebuffer,
452 const ImageView& src_image_view, u32 up_scale, u32 down_shift) { 472 const ImageView& src_image_view, u32 up_scale, u32 down_shift) {
453 const VkPipelineLayout layout = *one_texture_pipeline_layout; 473 const VkPipelineLayout layout = *one_texture_pipeline_layout;
@@ -495,6 +515,101 @@ void BlitImageHelper::Convert(VkPipeline pipeline, const Framebuffer* dst_frameb
495 scheduler.InvalidateState(); 515 scheduler.InvalidateState();
496} 516}
497 517
518void BlitImageHelper::ConvertColor(VkPipeline pipeline, const Framebuffer* dst_framebuffer,
519 ImageView& src_image_view, u32 up_scale, u32 down_shift) {
520 const VkPipelineLayout layout = *one_texture_pipeline_layout;
521 const VkImageView src_view = src_image_view.ColorView();
522 const VkSampler sampler = *nearest_sampler;
523 const VkExtent2D extent{
524 .width = std::max((src_image_view.size.width * up_scale) >> down_shift, 1U),
525 .height = std::max((src_image_view.size.height * up_scale) >> down_shift, 1U),
526 };
527 scheduler.RequestRenderpass(dst_framebuffer);
528 scheduler.Record([pipeline, layout, sampler, src_view, extent, up_scale, down_shift,
529 this](vk::CommandBuffer cmdbuf) {
530 const VkOffset2D offset{
531 .x = 0,
532 .y = 0,
533 };
534 const VkViewport viewport{
535 .x = 0.0f,
536 .y = 0.0f,
537 .width = static_cast<float>(extent.width),
538 .height = static_cast<float>(extent.height),
539 .minDepth = 0.0f,
540 .maxDepth = 0.0f,
541 };
542 const VkRect2D scissor{
543 .offset = offset,
544 .extent = extent,
545 };
546 const PushConstants push_constants{
547 .tex_scale = {viewport.width, viewport.height},
548 .tex_offset = {0.0f, 0.0f},
549 };
550 const VkDescriptorSet descriptor_set = one_texture_descriptor_allocator.Commit();
551 UpdateOneTextureDescriptorSet(device, descriptor_set, sampler, src_view);
552
553 // TODO: Barriers
554 cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
555 cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, layout, 0, descriptor_set,
556 nullptr);
557 cmdbuf.SetViewport(0, viewport);
558 cmdbuf.SetScissor(0, scissor);
559 cmdbuf.PushConstants(layout, VK_SHADER_STAGE_VERTEX_BIT, push_constants);
560 cmdbuf.Draw(3, 1, 0, 0);
561 });
562 scheduler.InvalidateState();
563}
564
565void BlitImageHelper::ConvertDepthStencil(VkPipeline pipeline, const Framebuffer* dst_framebuffer,
566 ImageView& src_image_view, u32 up_scale, u32 down_shift) {
567 const VkPipelineLayout layout = *two_textures_pipeline_layout;
568 const VkImageView src_depth_view = src_image_view.DepthView();
569 const VkImageView src_stencil_view = src_image_view.StencilView();
570 const VkSampler sampler = *nearest_sampler;
571 const VkExtent2D extent{
572 .width = std::max((src_image_view.size.width * up_scale) >> down_shift, 1U),
573 .height = std::max((src_image_view.size.height * up_scale) >> down_shift, 1U),
574 };
575 scheduler.RequestRenderpass(dst_framebuffer);
576 scheduler.Record([pipeline, layout, sampler, src_depth_view, src_stencil_view, extent, up_scale,
577 down_shift, this](vk::CommandBuffer cmdbuf) {
578 const VkOffset2D offset{
579 .x = 0,
580 .y = 0,
581 };
582 const VkViewport viewport{
583 .x = 0.0f,
584 .y = 0.0f,
585 .width = static_cast<float>(extent.width),
586 .height = static_cast<float>(extent.height),
587 .minDepth = 0.0f,
588 .maxDepth = 0.0f,
589 };
590 const VkRect2D scissor{
591 .offset = offset,
592 .extent = extent,
593 };
594 const PushConstants push_constants{
595 .tex_scale = {viewport.width, viewport.height},
596 .tex_offset = {0.0f, 0.0f},
597 };
598 const VkDescriptorSet descriptor_set = two_textures_descriptor_allocator.Commit();
599 UpdateTwoTexturesDescriptorSet(device, descriptor_set, sampler, src_depth_view,
600 src_stencil_view);
601 // TODO: Barriers
602 cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
603 cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, layout, 0, descriptor_set,
604 nullptr);
605 cmdbuf.SetViewport(0, viewport);
606 cmdbuf.SetScissor(0, scissor);
607 cmdbuf.PushConstants(layout, VK_SHADER_STAGE_VERTEX_BIT, push_constants);
608 cmdbuf.Draw(3, 1, 0, 0);
609 });
610 scheduler.InvalidateState();
611}
612
498VkPipeline BlitImageHelper::FindOrEmplaceColorPipeline(const BlitImagePipelineKey& key) { 613VkPipeline BlitImageHelper::FindOrEmplaceColorPipeline(const BlitImagePipelineKey& key) {
499 const auto it = std::ranges::find(blit_color_keys, key); 614 const auto it = std::ranges::find(blit_color_keys, key);
500 if (it != blit_color_keys.end()) { 615 if (it != blit_color_keys.end()) {
@@ -636,4 +751,44 @@ void BlitImageHelper::ConvertColorToDepthPipeline(vk::Pipeline& pipeline, VkRend
636 }); 751 });
637} 752}
638 753
754void BlitImageHelper::ConvertPipelineEx(vk::Pipeline& pipeline, VkRenderPass renderpass,
755 vk::ShaderModule& module, bool is_target_depth,
756 bool single_texture) {
757 if (pipeline) {
758 return;
759 }
760 const std::array stages = MakeStages(*full_screen_vert, *module);
761 pipeline = device.GetLogical().CreateGraphicsPipeline({
762 .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
763 .pNext = nullptr,
764 .flags = 0,
765 .stageCount = static_cast<u32>(stages.size()),
766 .pStages = stages.data(),
767 .pVertexInputState = &PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
768 .pInputAssemblyState = &PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
769 .pTessellationState = nullptr,
770 .pViewportState = &PIPELINE_VIEWPORT_STATE_CREATE_INFO,
771 .pRasterizationState = &PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
772 .pMultisampleState = &PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
773 .pDepthStencilState = is_target_depth ? &PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO : nullptr,
774 .pColorBlendState = &PIPELINE_COLOR_BLEND_STATE_GENERIC_CREATE_INFO,
775 .pDynamicState = &PIPELINE_DYNAMIC_STATE_CREATE_INFO,
776 .layout = single_texture ? *one_texture_pipeline_layout : *two_textures_pipeline_layout,
777 .renderPass = renderpass,
778 .subpass = 0,
779 .basePipelineHandle = VK_NULL_HANDLE,
780 .basePipelineIndex = 0,
781 });
782}
783
784void BlitImageHelper::ConvertPipelineColorTargetEx(vk::Pipeline& pipeline, VkRenderPass renderpass,
785 vk::ShaderModule& module, bool single_texture) {
786 ConvertPipelineEx(pipeline, renderpass, module, false, single_texture);
787}
788
789void BlitImageHelper::ConvertPipelineDepthTargetEx(vk::Pipeline& pipeline, VkRenderPass renderpass,
790 vk::ShaderModule& module, bool single_texture) {
791 ConvertPipelineEx(pipeline, renderpass, module, true, single_texture);
792}
793
639} // namespace Vulkan 794} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/blit_image.h b/src/video_core/renderer_vulkan/blit_image.h
index d77f76678..b1a717090 100644
--- a/src/video_core/renderer_vulkan/blit_image.h
+++ b/src/video_core/renderer_vulkan/blit_image.h
@@ -56,10 +56,22 @@ public:
56 void ConvertR16ToD16(const Framebuffer* dst_framebuffer, const ImageView& src_image_view, 56 void ConvertR16ToD16(const Framebuffer* dst_framebuffer, const ImageView& src_image_view,
57 u32 up_scale, u32 down_shift); 57 u32 up_scale, u32 down_shift);
58 58
59 void ConvertABGR8ToD24S8(const Framebuffer* dst_framebuffer, ImageView& src_image_view,
60 u32 up_scale, u32 down_shift);
61
62 void ConvertD24S8ToABGR8(const Framebuffer* dst_framebuffer, ImageView& src_image_view,
63 u32 up_scale, u32 down_shift);
64
59private: 65private:
60 void Convert(VkPipeline pipeline, const Framebuffer* dst_framebuffer, 66 void Convert(VkPipeline pipeline, const Framebuffer* dst_framebuffer,
61 const ImageView& src_image_view, u32 up_scale, u32 down_shift); 67 const ImageView& src_image_view, u32 up_scale, u32 down_shift);
62 68
69 void ConvertColor(VkPipeline pipeline, const Framebuffer* dst_framebuffer,
70 ImageView& src_image_view, u32 up_scale, u32 down_shift);
71
72 void ConvertDepthStencil(VkPipeline pipeline, const Framebuffer* dst_framebuffer,
73 ImageView& src_image_view, u32 up_scale, u32 down_shift);
74
63 [[nodiscard]] VkPipeline FindOrEmplaceColorPipeline(const BlitImagePipelineKey& key); 75 [[nodiscard]] VkPipeline FindOrEmplaceColorPipeline(const BlitImagePipelineKey& key);
64 76
65 [[nodiscard]] VkPipeline FindOrEmplaceDepthStencilPipeline(const BlitImagePipelineKey& key); 77 [[nodiscard]] VkPipeline FindOrEmplaceDepthStencilPipeline(const BlitImagePipelineKey& key);
@@ -68,6 +80,15 @@ private:
68 80
69 void ConvertColorToDepthPipeline(vk::Pipeline& pipeline, VkRenderPass renderpass); 81 void ConvertColorToDepthPipeline(vk::Pipeline& pipeline, VkRenderPass renderpass);
70 82
83 void ConvertPipelineEx(vk::Pipeline& pipeline, VkRenderPass renderpass,
84 vk::ShaderModule& module, bool is_target_depth, bool single_texture);
85
86 void ConvertPipelineColorTargetEx(vk::Pipeline& pipeline, VkRenderPass renderpass,
87 vk::ShaderModule& module, bool single_texture);
88
89 void ConvertPipelineDepthTargetEx(vk::Pipeline& pipeline, VkRenderPass renderpass,
90 vk::ShaderModule& module, bool single_texture);
91
71 const Device& device; 92 const Device& device;
72 VKScheduler& scheduler; 93 VKScheduler& scheduler;
73 StateTracker& state_tracker; 94 StateTracker& state_tracker;
@@ -83,6 +104,8 @@ private:
83 vk::ShaderModule blit_depth_stencil_frag; 104 vk::ShaderModule blit_depth_stencil_frag;
84 vk::ShaderModule convert_depth_to_float_frag; 105 vk::ShaderModule convert_depth_to_float_frag;
85 vk::ShaderModule convert_float_to_depth_frag; 106 vk::ShaderModule convert_float_to_depth_frag;
107 vk::ShaderModule convert_abgr8_to_d24s8_frag;
108 vk::ShaderModule convert_d24s8_to_abgr8_frag;
86 vk::Sampler linear_sampler; 109 vk::Sampler linear_sampler;
87 vk::Sampler nearest_sampler; 110 vk::Sampler nearest_sampler;
88 111
@@ -94,6 +117,8 @@ private:
94 vk::Pipeline convert_r32_to_d32_pipeline; 117 vk::Pipeline convert_r32_to_d32_pipeline;
95 vk::Pipeline convert_d16_to_r16_pipeline; 118 vk::Pipeline convert_d16_to_r16_pipeline;
96 vk::Pipeline convert_r16_to_d16_pipeline; 119 vk::Pipeline convert_r16_to_d16_pipeline;
120 vk::Pipeline convert_abgr8_to_d24s8_pipeline;
121 vk::Pipeline convert_d24s8_to_abgr8_pipeline;
97}; 122};
98 123
99} // namespace Vulkan 124} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/maxwell_to_vk.cpp b/src/video_core/renderer_vulkan/maxwell_to_vk.cpp
index 68a23b602..751e4792b 100644
--- a/src/video_core/renderer_vulkan/maxwell_to_vk.cpp
+++ b/src/video_core/renderer_vulkan/maxwell_to_vk.cpp
@@ -162,7 +162,7 @@ struct FormatTuple {
162 {VK_FORMAT_UNDEFINED}, // R16_SINT 162 {VK_FORMAT_UNDEFINED}, // R16_SINT
163 {VK_FORMAT_R16G16_UNORM, Attachable | Storage}, // R16G16_UNORM 163 {VK_FORMAT_R16G16_UNORM, Attachable | Storage}, // R16G16_UNORM
164 {VK_FORMAT_R16G16_SFLOAT, Attachable | Storage}, // R16G16_FLOAT 164 {VK_FORMAT_R16G16_SFLOAT, Attachable | Storage}, // R16G16_FLOAT
165 {VK_FORMAT_UNDEFINED}, // R16G16_UINT 165 {VK_FORMAT_R16G16_UINT, Attachable | Storage}, // R16G16_UINT
166 {VK_FORMAT_R16G16_SINT, Attachable | Storage}, // R16G16_SINT 166 {VK_FORMAT_R16G16_SINT, Attachable | Storage}, // R16G16_SINT
167 {VK_FORMAT_R16G16_SNORM, Attachable | Storage}, // R16G16_SNORM 167 {VK_FORMAT_R16G16_SNORM, Attachable | Storage}, // R16G16_SNORM
168 {VK_FORMAT_UNDEFINED}, // R32G32B32_FLOAT 168 {VK_FORMAT_UNDEFINED}, // R32G32B32_FLOAT
@@ -176,8 +176,8 @@ struct FormatTuple {
176 {VK_FORMAT_R32_UINT, Attachable | Storage}, // R32_UINT 176 {VK_FORMAT_R32_UINT, Attachable | Storage}, // R32_UINT
177 {VK_FORMAT_R32_SINT, Attachable | Storage}, // R32_SINT 177 {VK_FORMAT_R32_SINT, Attachable | Storage}, // R32_SINT
178 {VK_FORMAT_ASTC_8x8_UNORM_BLOCK}, // ASTC_2D_8X8_UNORM 178 {VK_FORMAT_ASTC_8x8_UNORM_BLOCK}, // ASTC_2D_8X8_UNORM
179 {VK_FORMAT_UNDEFINED}, // ASTC_2D_8X5_UNORM 179 {VK_FORMAT_ASTC_8x5_UNORM_BLOCK}, // ASTC_2D_8X5_UNORM
180 {VK_FORMAT_UNDEFINED}, // ASTC_2D_5X4_UNORM 180 {VK_FORMAT_ASTC_5x4_UNORM_BLOCK}, // ASTC_2D_5X4_UNORM
181 {VK_FORMAT_B8G8R8A8_SRGB, Attachable}, // B8G8R8A8_SRGB 181 {VK_FORMAT_B8G8R8A8_SRGB, Attachable}, // B8G8R8A8_SRGB
182 {VK_FORMAT_BC1_RGBA_SRGB_BLOCK}, // BC1_RGBA_SRGB 182 {VK_FORMAT_BC1_RGBA_SRGB_BLOCK}, // BC1_RGBA_SRGB
183 {VK_FORMAT_BC2_SRGB_BLOCK}, // BC2_SRGB 183 {VK_FORMAT_BC2_SRGB_BLOCK}, // BC2_SRGB
@@ -208,6 +208,9 @@ struct FormatTuple {
208 {VK_FORMAT_D32_SFLOAT, Attachable}, // D32_FLOAT 208 {VK_FORMAT_D32_SFLOAT, Attachable}, // D32_FLOAT
209 {VK_FORMAT_D16_UNORM, Attachable}, // D16_UNORM 209 {VK_FORMAT_D16_UNORM, Attachable}, // D16_UNORM
210 210
211 // Stencil formats
212 {VK_FORMAT_S8_UINT, Attachable}, // S8_UINT
213
211 // DepthStencil formats 214 // DepthStencil formats
212 {VK_FORMAT_D24_UNORM_S8_UINT, Attachable}, // D24_UNORM_S8_UINT 215 {VK_FORMAT_D24_UNORM_S8_UINT, Attachable}, // D24_UNORM_S8_UINT
213 {VK_FORMAT_D24_UNORM_S8_UINT, Attachable}, // S8_UINT_D24_UNORM (emulated) 216 {VK_FORMAT_D24_UNORM_S8_UINT, Attachable}, // S8_UINT_D24_UNORM (emulated)
diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp
index 407fd2a15..197cba8e3 100644
--- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp
@@ -8,6 +8,7 @@
8#include <vector> 8#include <vector>
9 9
10#include "common/bit_cast.h" 10#include "common/bit_cast.h"
11#include "common/bit_util.h"
11#include "common/settings.h" 12#include "common/settings.h"
12 13
13#include "video_core/engines/fermi_2d.h" 14#include "video_core/engines/fermi_2d.h"
@@ -102,6 +103,7 @@ constexpr VkBorderColor ConvertBorderColor(const std::array<float, 4>& color) {
102 usage |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; 103 usage |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
103 break; 104 break;
104 case VideoCore::Surface::SurfaceType::Depth: 105 case VideoCore::Surface::SurfaceType::Depth:
106 case VideoCore::Surface::SurfaceType::Stencil:
105 case VideoCore::Surface::SurfaceType::DepthStencil: 107 case VideoCore::Surface::SurfaceType::DepthStencil:
106 usage |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; 108 usage |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
107 break; 109 break;
@@ -173,6 +175,8 @@ constexpr VkBorderColor ConvertBorderColor(const std::array<float, 4>& color) {
173 return VK_IMAGE_ASPECT_COLOR_BIT; 175 return VK_IMAGE_ASPECT_COLOR_BIT;
174 case VideoCore::Surface::SurfaceType::Depth: 176 case VideoCore::Surface::SurfaceType::Depth:
175 return VK_IMAGE_ASPECT_DEPTH_BIT; 177 return VK_IMAGE_ASPECT_DEPTH_BIT;
178 case VideoCore::Surface::SurfaceType::Stencil:
179 return VK_IMAGE_ASPECT_STENCIL_BIT;
176 case VideoCore::Surface::SurfaceType::DepthStencil: 180 case VideoCore::Surface::SurfaceType::DepthStencil:
177 return VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT; 181 return VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT;
178 default: 182 default:
@@ -195,6 +199,8 @@ constexpr VkBorderColor ConvertBorderColor(const std::array<float, 4>& color) {
195 case PixelFormat::D16_UNORM: 199 case PixelFormat::D16_UNORM:
196 case PixelFormat::D32_FLOAT: 200 case PixelFormat::D32_FLOAT:
197 return VK_IMAGE_ASPECT_DEPTH_BIT; 201 return VK_IMAGE_ASPECT_DEPTH_BIT;
202 case PixelFormat::S8_UINT:
203 return VK_IMAGE_ASPECT_STENCIL_BIT;
198 default: 204 default:
199 return VK_IMAGE_ASPECT_COLOR_BIT; 205 return VK_IMAGE_ASPECT_COLOR_BIT;
200 } 206 }
@@ -308,6 +314,19 @@ constexpr VkBorderColor ConvertBorderColor(const std::array<float, 4>& color) {
308 }; 314 };
309} 315}
310 316
317[[nodiscard]] VkBufferImageCopy MakeBufferImageCopy(const VideoCommon::ImageCopy& copy, bool is_src,
318 VkImageAspectFlags aspect_mask) noexcept {
319 return VkBufferImageCopy{
320 .bufferOffset = 0,
321 .bufferRowLength = 0,
322 .bufferImageHeight = 0,
323 .imageSubresource = MakeImageSubresourceLayers(
324 is_src ? copy.src_subresource : copy.dst_subresource, aspect_mask),
325 .imageOffset = MakeOffset3D(is_src ? copy.src_offset : copy.dst_offset),
326 .imageExtent = MakeExtent3D(copy.extent),
327 };
328}
329
311[[maybe_unused]] [[nodiscard]] std::vector<VkBufferCopy> TransformBufferCopies( 330[[maybe_unused]] [[nodiscard]] std::vector<VkBufferCopy> TransformBufferCopies(
312 std::span<const VideoCommon::BufferCopy> copies, size_t buffer_offset) { 331 std::span<const VideoCommon::BufferCopy> copies, size_t buffer_offset) {
313 std::vector<VkBufferCopy> result(copies.size()); 332 std::vector<VkBufferCopy> result(copies.size());
@@ -754,6 +773,173 @@ StagingBufferRef TextureCacheRuntime::DownloadStagingBuffer(size_t size) {
754 return staging_buffer_pool.Request(size, MemoryUsage::Download); 773 return staging_buffer_pool.Request(size, MemoryUsage::Download);
755} 774}
756 775
776bool TextureCacheRuntime::ShouldReinterpret(Image& dst, Image& src) {
777 if (VideoCore::Surface::GetFormatType(dst.info.format) ==
778 VideoCore::Surface::SurfaceType::DepthStencil &&
779 !device.IsExtShaderStencilExportSupported()) {
780 return true;
781 }
782 if (VideoCore::Surface::GetFormatType(src.info.format) ==
783 VideoCore::Surface::SurfaceType::DepthStencil &&
784 !device.IsExtShaderStencilExportSupported()) {
785 return true;
786 }
787 if (dst.info.format == PixelFormat::D32_FLOAT_S8_UINT ||
788 src.info.format == PixelFormat::D32_FLOAT_S8_UINT) {
789 return true;
790 }
791 return false;
792}
793
794VkBuffer TextureCacheRuntime::GetTemporaryBuffer(size_t needed_size) {
795 const auto level = (8 * sizeof(size_t)) - std::countl_zero(needed_size - 1ULL);
796 if (buffer_commits[level]) {
797 return *buffers[level];
798 }
799 const auto new_size = Common::NextPow2(needed_size);
800 static constexpr VkBufferUsageFlags flags =
801 VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT |
802 VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT;
803 buffers[level] = device.GetLogical().CreateBuffer({
804 .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
805 .pNext = nullptr,
806 .flags = 0,
807 .size = new_size,
808 .usage = flags,
809 .sharingMode = VK_SHARING_MODE_EXCLUSIVE,
810 .queueFamilyIndexCount = 0,
811 .pQueueFamilyIndices = nullptr,
812 });
813 buffer_commits[level] = std::make_unique<MemoryCommit>(
814 memory_allocator.Commit(buffers[level], MemoryUsage::DeviceLocal));
815 return *buffers[level];
816}
817
818void TextureCacheRuntime::ReinterpretImage(Image& dst, Image& src,
819 std::span<const VideoCommon::ImageCopy> copies) {
820 std::vector<VkBufferImageCopy> vk_in_copies(copies.size());
821 std::vector<VkBufferImageCopy> vk_out_copies(copies.size());
822 const VkImageAspectFlags src_aspect_mask = src.AspectMask();
823 const VkImageAspectFlags dst_aspect_mask = dst.AspectMask();
824
825 std::ranges::transform(copies, vk_in_copies.begin(), [src_aspect_mask](const auto& copy) {
826 return MakeBufferImageCopy(copy, true, src_aspect_mask);
827 });
828 std::ranges::transform(copies, vk_out_copies.begin(), [dst_aspect_mask](const auto& copy) {
829 return MakeBufferImageCopy(copy, false, dst_aspect_mask);
830 });
831 const u32 img_bpp = BytesPerBlock(src.info.format);
832 size_t total_size = 0;
833 for (const auto& copy : copies) {
834 total_size += copy.extent.width * copy.extent.height * copy.extent.depth * img_bpp;
835 }
836 const VkBuffer copy_buffer = GetTemporaryBuffer(total_size);
837 const VkImage dst_image = dst.Handle();
838 const VkImage src_image = src.Handle();
839 scheduler.RequestOutsideRenderPassOperationContext();
840 scheduler.Record([dst_image, src_image, copy_buffer, src_aspect_mask, dst_aspect_mask,
841 vk_in_copies, vk_out_copies](vk::CommandBuffer cmdbuf) {
842 RangedBarrierRange dst_range;
843 RangedBarrierRange src_range;
844 for (const VkBufferImageCopy& copy : vk_in_copies) {
845 src_range.AddLayers(copy.imageSubresource);
846 }
847 for (const VkBufferImageCopy& copy : vk_out_copies) {
848 dst_range.AddLayers(copy.imageSubresource);
849 }
850 static constexpr VkMemoryBarrier READ_BARRIER{
851 .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER,
852 .pNext = nullptr,
853 .srcAccessMask = VK_ACCESS_MEMORY_WRITE_BIT,
854 .dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_TRANSFER_WRITE_BIT,
855 };
856 static constexpr VkMemoryBarrier WRITE_BARRIER{
857 .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER,
858 .pNext = nullptr,
859 .srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
860 .dstAccessMask = VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT,
861 };
862 const std::array pre_barriers{
863 VkImageMemoryBarrier{
864 .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
865 .pNext = nullptr,
866 .srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT |
867 VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT |
868 VK_ACCESS_TRANSFER_WRITE_BIT,
869 .dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT,
870 .oldLayout = VK_IMAGE_LAYOUT_GENERAL,
871 .newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
872 .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
873 .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
874 .image = src_image,
875 .subresourceRange = src_range.SubresourceRange(src_aspect_mask),
876 },
877 };
878 const std::array middle_in_barrier{
879 VkImageMemoryBarrier{
880 .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
881 .pNext = nullptr,
882 .srcAccessMask = 0,
883 .dstAccessMask = 0,
884 .oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
885 .newLayout = VK_IMAGE_LAYOUT_GENERAL,
886 .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
887 .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
888 .image = src_image,
889 .subresourceRange = src_range.SubresourceRange(src_aspect_mask),
890 },
891 };
892 const std::array middle_out_barrier{
893 VkImageMemoryBarrier{
894 .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
895 .pNext = nullptr,
896 .srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT |
897 VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT |
898 VK_ACCESS_TRANSFER_WRITE_BIT,
899 .dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
900 .oldLayout = VK_IMAGE_LAYOUT_GENERAL,
901 .newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
902 .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
903 .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
904 .image = dst_image,
905 .subresourceRange = dst_range.SubresourceRange(dst_aspect_mask),
906 },
907 };
908 const std::array post_barriers{
909 VkImageMemoryBarrier{
910 .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
911 .pNext = nullptr,
912 .srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
913 .dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT |
914 VK_ACCESS_COLOR_ATTACHMENT_READ_BIT |
915 VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT |
916 VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT |
917 VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT |
918 VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_TRANSFER_WRITE_BIT,
919 .oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
920 .newLayout = VK_IMAGE_LAYOUT_GENERAL,
921 .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
922 .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
923 .image = dst_image,
924 .subresourceRange = dst_range.SubresourceRange(dst_aspect_mask),
925 },
926 };
927 cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT,
928 0, {}, {}, pre_barriers);
929
930 cmdbuf.CopyImageToBuffer(src_image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, copy_buffer,
931 vk_in_copies);
932 cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
933 0, WRITE_BARRIER, nullptr, middle_in_barrier);
934
935 cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT,
936 0, READ_BARRIER, {}, middle_out_barrier);
937 cmdbuf.CopyBufferToImage(copy_buffer, dst_image, VK_IMAGE_LAYOUT_GENERAL, vk_out_copies);
938 cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
939 0, {}, {}, post_barriers);
940 });
941}
942
757void TextureCacheRuntime::BlitImage(Framebuffer* dst_framebuffer, ImageView& dst, ImageView& src, 943void TextureCacheRuntime::BlitImage(Framebuffer* dst_framebuffer, ImageView& dst, ImageView& src,
758 const Region2D& dst_region, const Region2D& src_region, 944 const Region2D& dst_region, const Region2D& src_region,
759 Tegra::Engines::Fermi2D::Filter filter, 945 Tegra::Engines::Fermi2D::Filter filter,
@@ -881,6 +1067,11 @@ void TextureCacheRuntime::ConvertImage(Framebuffer* dst, ImageView& dst_view, Im
881 return blit_image_helper.ConvertD16ToR16(dst, src_view, up_scale, down_shift); 1067 return blit_image_helper.ConvertD16ToR16(dst, src_view, up_scale, down_shift);
882 } 1068 }
883 break; 1069 break;
1070 case PixelFormat::A8B8G8R8_UNORM:
1071 if (src_view.format == PixelFormat::S8_UINT_D24_UNORM) {
1072 return blit_image_helper.ConvertD24S8ToABGR8(dst, src_view, up_scale, down_shift);
1073 }
1074 break;
884 case PixelFormat::R32_FLOAT: 1075 case PixelFormat::R32_FLOAT:
885 if (src_view.format == PixelFormat::D32_FLOAT) { 1076 if (src_view.format == PixelFormat::D32_FLOAT) {
886 return blit_image_helper.ConvertD32ToR32(dst, src_view, up_scale, down_shift); 1077 return blit_image_helper.ConvertD32ToR32(dst, src_view, up_scale, down_shift);
@@ -891,6 +1082,9 @@ void TextureCacheRuntime::ConvertImage(Framebuffer* dst, ImageView& dst_view, Im
891 return blit_image_helper.ConvertR16ToD16(dst, src_view, up_scale, down_shift); 1082 return blit_image_helper.ConvertR16ToD16(dst, src_view, up_scale, down_shift);
892 } 1083 }
893 break; 1084 break;
1085 case PixelFormat::S8_UINT_D24_UNORM:
1086 return blit_image_helper.ConvertABGR8ToD24S8(dst, src_view, up_scale, down_shift);
1087 break;
894 case PixelFormat::D32_FLOAT: 1088 case PixelFormat::D32_FLOAT:
895 if (src_view.format == PixelFormat::R32_FLOAT) { 1089 if (src_view.format == PixelFormat::R32_FLOAT) {
896 return blit_image_helper.ConvertR32ToD32(dst, src_view, up_scale, down_shift); 1090 return blit_image_helper.ConvertR32ToD32(dst, src_view, up_scale, down_shift);
@@ -1386,6 +1580,14 @@ VkImageView ImageView::StencilView() {
1386 return *stencil_view; 1580 return *stencil_view;
1387} 1581}
1388 1582
1583VkImageView ImageView::ColorView() {
1584 if (color_view) {
1585 return *color_view;
1586 }
1587 color_view = MakeView(VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_ASPECT_COLOR_BIT);
1588 return *color_view;
1589}
1590
1389VkImageView ImageView::StorageView(Shader::TextureType texture_type, 1591VkImageView ImageView::StorageView(Shader::TextureType texture_type,
1390 Shader::ImageFormat image_format) { 1592 Shader::ImageFormat image_format) {
1391 if (image_format == Shader::ImageFormat::Typeless) { 1593 if (image_format == Shader::ImageFormat::Typeless) {
diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.h b/src/video_core/renderer_vulkan/vk_texture_cache.h
index f5f8f9a74..753e3e8a1 100644
--- a/src/video_core/renderer_vulkan/vk_texture_cache.h
+++ b/src/video_core/renderer_vulkan/vk_texture_cache.h
@@ -61,6 +61,10 @@ public:
61 61
62 void CopyImage(Image& dst, Image& src, std::span<const VideoCommon::ImageCopy> copies); 62 void CopyImage(Image& dst, Image& src, std::span<const VideoCommon::ImageCopy> copies);
63 63
64 bool ShouldReinterpret(Image& dst, Image& src);
65
66 void ReinterpretImage(Image& dst, Image& src, std::span<const VideoCommon::ImageCopy> copies);
67
64 void ConvertImage(Framebuffer* dst, ImageView& dst_view, ImageView& src_view, bool rescaled); 68 void ConvertImage(Framebuffer* dst, ImageView& dst_view, ImageView& src_view, bool rescaled);
65 69
66 bool CanAccelerateImageUpload(Image&) const noexcept { 70 bool CanAccelerateImageUpload(Image&) const noexcept {
@@ -82,6 +86,8 @@ public:
82 return true; 86 return true;
83 } 87 }
84 88
89 [[nodiscard]] VkBuffer GetTemporaryBuffer(size_t needed_size);
90
85 const Device& device; 91 const Device& device;
86 VKScheduler& scheduler; 92 VKScheduler& scheduler;
87 MemoryAllocator& memory_allocator; 93 MemoryAllocator& memory_allocator;
@@ -90,6 +96,10 @@ public:
90 ASTCDecoderPass& astc_decoder_pass; 96 ASTCDecoderPass& astc_decoder_pass;
91 RenderPassCache& render_pass_cache; 97 RenderPassCache& render_pass_cache;
92 const Settings::ResolutionScalingInfo& resolution; 98 const Settings::ResolutionScalingInfo& resolution;
99
100 constexpr static size_t indexing_slots = 8 * sizeof(size_t);
101 std::array<vk::Buffer, indexing_slots> buffers{};
102 std::array<std::unique_ptr<MemoryCommit>, indexing_slots> buffer_commits{};
93}; 103};
94 104
95class Image : public VideoCommon::ImageBase { 105class Image : public VideoCommon::ImageBase {
@@ -174,6 +184,8 @@ public:
174 184
175 [[nodiscard]] VkImageView StencilView(); 185 [[nodiscard]] VkImageView StencilView();
176 186
187 [[nodiscard]] VkImageView ColorView();
188
177 [[nodiscard]] VkImageView StorageView(Shader::TextureType texture_type, 189 [[nodiscard]] VkImageView StorageView(Shader::TextureType texture_type,
178 Shader::ImageFormat image_format); 190 Shader::ImageFormat image_format);
179 191
@@ -214,6 +226,7 @@ private:
214 std::unique_ptr<StorageViews> storage_views; 226 std::unique_ptr<StorageViews> storage_views;
215 vk::ImageView depth_view; 227 vk::ImageView depth_view;
216 vk::ImageView stencil_view; 228 vk::ImageView stencil_view;
229 vk::ImageView color_view;
217 VkImage image_handle = VK_NULL_HANDLE; 230 VkImage image_handle = VK_NULL_HANDLE;
218 VkImageView render_target = VK_NULL_HANDLE; 231 VkImageView render_target = VK_NULL_HANDLE;
219 VkSampleCountFlagBits samples = VK_SAMPLE_COUNT_1_BIT; 232 VkSampleCountFlagBits samples = VK_SAMPLE_COUNT_1_BIT;
@@ -316,7 +329,6 @@ struct TextureCacheParams {
316 static constexpr bool FRAMEBUFFER_BLITS = false; 329 static constexpr bool FRAMEBUFFER_BLITS = false;
317 static constexpr bool HAS_EMULATED_COPIES = false; 330 static constexpr bool HAS_EMULATED_COPIES = false;
318 static constexpr bool HAS_DEVICE_MEMORY_INFO = true; 331 static constexpr bool HAS_DEVICE_MEMORY_INFO = true;
319 static constexpr bool HAS_PIXEL_FORMAT_CONVERSIONS = false;
320 332
321 using Runtime = Vulkan::TextureCacheRuntime; 333 using Runtime = Vulkan::TextureCacheRuntime;
322 using Image = Vulkan::Image; 334 using Image = Vulkan::Image;
diff --git a/src/video_core/shader_notify.cpp b/src/video_core/shader_notify.cpp
index dc6995b46..bcaf5f575 100644
--- a/src/video_core/shader_notify.cpp
+++ b/src/video_core/shader_notify.cpp
@@ -18,7 +18,7 @@ int ShaderNotify::ShadersBuilding() noexcept {
18 const int now_complete = num_complete.load(std::memory_order::relaxed); 18 const int now_complete = num_complete.load(std::memory_order::relaxed);
19 const int now_building = num_building.load(std::memory_order::relaxed); 19 const int now_building = num_building.load(std::memory_order::relaxed);
20 if (now_complete == now_building) { 20 if (now_complete == now_building) {
21 const auto now = std::chrono::high_resolution_clock::now(); 21 const auto now = std::chrono::steady_clock::now();
22 if (completed && num_complete == num_when_completed) { 22 if (completed && num_complete == num_when_completed) {
23 if (now - complete_time > TIME_TO_STOP_REPORTING) { 23 if (now - complete_time > TIME_TO_STOP_REPORTING) {
24 report_base = now_complete; 24 report_base = now_complete;
diff --git a/src/video_core/shader_notify.h b/src/video_core/shader_notify.h
index ad363bfb5..4d8d52071 100644
--- a/src/video_core/shader_notify.h
+++ b/src/video_core/shader_notify.h
@@ -28,6 +28,6 @@ private:
28 28
29 bool completed{}; 29 bool completed{};
30 int num_when_completed{}; 30 int num_when_completed{};
31 std::chrono::high_resolution_clock::time_point complete_time; 31 std::chrono::steady_clock::time_point complete_time;
32}; 32};
33} // namespace VideoCore 33} // namespace VideoCore
diff --git a/src/video_core/surface.cpp b/src/video_core/surface.cpp
index 58d262446..a36015c8c 100644
--- a/src/video_core/surface.cpp
+++ b/src/video_core/surface.cpp
@@ -82,6 +82,8 @@ PixelFormat PixelFormatFromDepthFormat(Tegra::DepthFormat format) {
82 return PixelFormat::D32_FLOAT; 82 return PixelFormat::D32_FLOAT;
83 case Tegra::DepthFormat::D16_UNORM: 83 case Tegra::DepthFormat::D16_UNORM:
84 return PixelFormat::D16_UNORM; 84 return PixelFormat::D16_UNORM;
85 case Tegra::DepthFormat::S8_UINT:
86 return PixelFormat::S8_UINT;
85 case Tegra::DepthFormat::D32_FLOAT_S8X24_UINT: 87 case Tegra::DepthFormat::D32_FLOAT_S8X24_UINT:
86 return PixelFormat::D32_FLOAT_S8_UINT; 88 return PixelFormat::D32_FLOAT_S8_UINT;
87 default: 89 default:
@@ -214,6 +216,11 @@ SurfaceType GetFormatType(PixelFormat pixel_format) {
214 } 216 }
215 217
216 if (static_cast<std::size_t>(pixel_format) < 218 if (static_cast<std::size_t>(pixel_format) <
219 static_cast<std::size_t>(PixelFormat::MaxStencilFormat)) {
220 return SurfaceType::Stencil;
221 }
222
223 if (static_cast<std::size_t>(pixel_format) <
217 static_cast<std::size_t>(PixelFormat::MaxDepthStencilFormat)) { 224 static_cast<std::size_t>(PixelFormat::MaxDepthStencilFormat)) {
218 return SurfaceType::DepthStencil; 225 return SurfaceType::DepthStencil;
219 } 226 }
diff --git a/src/video_core/surface.h b/src/video_core/surface.h
index 2ce7c7d33..33e8d24ab 100644
--- a/src/video_core/surface.h
+++ b/src/video_core/surface.h
@@ -110,8 +110,12 @@ enum class PixelFormat {
110 110
111 MaxDepthFormat, 111 MaxDepthFormat,
112 112
113 // Stencil formats
114 S8_UINT = MaxDepthFormat,
115 MaxStencilFormat,
116
113 // DepthStencil formats 117 // DepthStencil formats
114 D24_UNORM_S8_UINT = MaxDepthFormat, 118 D24_UNORM_S8_UINT = MaxStencilFormat,
115 S8_UINT_D24_UNORM, 119 S8_UINT_D24_UNORM,
116 D32_FLOAT_S8_UINT, 120 D32_FLOAT_S8_UINT,
117 121
@@ -125,8 +129,9 @@ constexpr std::size_t MaxPixelFormat = static_cast<std::size_t>(PixelFormat::Max
125enum class SurfaceType { 129enum class SurfaceType {
126 ColorTexture = 0, 130 ColorTexture = 0,
127 Depth = 1, 131 Depth = 1,
128 DepthStencil = 2, 132 Stencil = 2,
129 Invalid = 3, 133 DepthStencil = 3,
134 Invalid = 4,
130}; 135};
131 136
132enum class SurfaceTarget { 137enum class SurfaceTarget {
@@ -229,6 +234,7 @@ constexpr std::array<u32, MaxPixelFormat> BLOCK_WIDTH_TABLE = {{
229 1, // E5B9G9R9_FLOAT 234 1, // E5B9G9R9_FLOAT
230 1, // D32_FLOAT 235 1, // D32_FLOAT
231 1, // D16_UNORM 236 1, // D16_UNORM
237 1, // S8_UINT
232 1, // D24_UNORM_S8_UINT 238 1, // D24_UNORM_S8_UINT
233 1, // S8_UINT_D24_UNORM 239 1, // S8_UINT_D24_UNORM
234 1, // D32_FLOAT_S8_UINT 240 1, // D32_FLOAT_S8_UINT
@@ -328,6 +334,7 @@ constexpr std::array<u32, MaxPixelFormat> BLOCK_HEIGHT_TABLE = {{
328 1, // E5B9G9R9_FLOAT 334 1, // E5B9G9R9_FLOAT
329 1, // D32_FLOAT 335 1, // D32_FLOAT
330 1, // D16_UNORM 336 1, // D16_UNORM
337 1, // S8_UINT
331 1, // D24_UNORM_S8_UINT 338 1, // D24_UNORM_S8_UINT
332 1, // S8_UINT_D24_UNORM 339 1, // S8_UINT_D24_UNORM
333 1, // D32_FLOAT_S8_UINT 340 1, // D32_FLOAT_S8_UINT
@@ -427,6 +434,7 @@ constexpr std::array<u32, MaxPixelFormat> BITS_PER_BLOCK_TABLE = {{
427 32, // E5B9G9R9_FLOAT 434 32, // E5B9G9R9_FLOAT
428 32, // D32_FLOAT 435 32, // D32_FLOAT
429 16, // D16_UNORM 436 16, // D16_UNORM
437 8, // S8_UINT
430 32, // D24_UNORM_S8_UINT 438 32, // D24_UNORM_S8_UINT
431 32, // S8_UINT_D24_UNORM 439 32, // S8_UINT_D24_UNORM
432 64, // D32_FLOAT_S8_UINT 440 64, // D32_FLOAT_S8_UINT
diff --git a/src/video_core/texture_cache/format_lookup_table.cpp b/src/video_core/texture_cache/format_lookup_table.cpp
index ddfb726fe..afa807d5d 100644
--- a/src/video_core/texture_cache/format_lookup_table.cpp
+++ b/src/video_core/texture_cache/format_lookup_table.cpp
@@ -139,6 +139,8 @@ PixelFormat PixelFormatFromTextureInfo(TextureFormat format, ComponentType red,
139 return PixelFormat::D16_UNORM; 139 return PixelFormat::D16_UNORM;
140 case Hash(TextureFormat::S8D24, UINT, UNORM, UNORM, UNORM, LINEAR): 140 case Hash(TextureFormat::S8D24, UINT, UNORM, UNORM, UNORM, LINEAR):
141 return PixelFormat::S8_UINT_D24_UNORM; 141 return PixelFormat::S8_UINT_D24_UNORM;
142 case Hash(TextureFormat::S8D24, UINT, UNORM, UINT, UINT, LINEAR):
143 return PixelFormat::S8_UINT_D24_UNORM;
142 case Hash(TextureFormat::R8G24, UINT, UNORM, UNORM, UNORM, LINEAR): 144 case Hash(TextureFormat::R8G24, UINT, UNORM, UNORM, UNORM, LINEAR):
143 return PixelFormat::S8_UINT_D24_UNORM; 145 return PixelFormat::S8_UINT_D24_UNORM;
144 case Hash(TextureFormat::D32S8, FLOAT, UINT, UNORM, UNORM, LINEAR): 146 case Hash(TextureFormat::D32S8, FLOAT, UINT, UNORM, UNORM, LINEAR):
diff --git a/src/video_core/texture_cache/formatter.h b/src/video_core/texture_cache/formatter.h
index c6cf0583f..b2c81057b 100644
--- a/src/video_core/texture_cache/formatter.h
+++ b/src/video_core/texture_cache/formatter.h
@@ -194,6 +194,8 @@ struct fmt::formatter<VideoCore::Surface::PixelFormat> : fmt::formatter<fmt::str
194 return "D32_FLOAT"; 194 return "D32_FLOAT";
195 case PixelFormat::D16_UNORM: 195 case PixelFormat::D16_UNORM:
196 return "D16_UNORM"; 196 return "D16_UNORM";
197 case PixelFormat::S8_UINT:
198 return "S8_UINT";
197 case PixelFormat::D24_UNORM_S8_UINT: 199 case PixelFormat::D24_UNORM_S8_UINT:
198 return "D24_UNORM_S8_UINT"; 200 return "D24_UNORM_S8_UINT";
199 case PixelFormat::S8_UINT_D24_UNORM: 201 case PixelFormat::S8_UINT_D24_UNORM:
diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h
index 241f71a91..5aaeb16ca 100644
--- a/src/video_core/texture_cache/texture_cache.h
+++ b/src/video_core/texture_cache/texture_cache.h
@@ -472,9 +472,10 @@ template <class P>
472void TextureCache<P>::BlitImage(const Tegra::Engines::Fermi2D::Surface& dst, 472void TextureCache<P>::BlitImage(const Tegra::Engines::Fermi2D::Surface& dst,
473 const Tegra::Engines::Fermi2D::Surface& src, 473 const Tegra::Engines::Fermi2D::Surface& src,
474 const Tegra::Engines::Fermi2D::Config& copy) { 474 const Tegra::Engines::Fermi2D::Config& copy) {
475 const BlitImages images = GetBlitImages(dst, src); 475 const BlitImages images = GetBlitImages(dst, src, copy);
476 const ImageId dst_id = images.dst_id; 476 const ImageId dst_id = images.dst_id;
477 const ImageId src_id = images.src_id; 477 const ImageId src_id = images.src_id;
478
478 PrepareImage(src_id, false, false); 479 PrepareImage(src_id, false, false);
479 PrepareImage(dst_id, true, false); 480 PrepareImage(dst_id, true, false);
480 481
@@ -758,14 +759,18 @@ ImageId TextureCache<P>::FindImage(const ImageInfo& info, GPUVAddr gpu_addr,
758 return ImageId{}; 759 return ImageId{};
759 } 760 }
760 } 761 }
761 const bool broken_views = runtime.HasBrokenTextureViewFormats(); 762 const bool broken_views =
763 runtime.HasBrokenTextureViewFormats() || True(options & RelaxedOptions::ForceBrokenViews);
762 const bool native_bgr = runtime.HasNativeBgr(); 764 const bool native_bgr = runtime.HasNativeBgr();
763 ImageId image_id; 765 const bool flexible_formats = True(options & RelaxedOptions::Format);
766 ImageId image_id{};
767 boost::container::small_vector<ImageId, 1> image_ids;
764 const auto lambda = [&](ImageId existing_image_id, ImageBase& existing_image) { 768 const auto lambda = [&](ImageId existing_image_id, ImageBase& existing_image) {
765 if (True(existing_image.flags & ImageFlagBits::Remapped)) { 769 if (True(existing_image.flags & ImageFlagBits::Remapped)) {
766 return false; 770 return false;
767 } 771 }
768 if (info.type == ImageType::Linear || existing_image.info.type == ImageType::Linear) { 772 if (info.type == ImageType::Linear || existing_image.info.type == ImageType::Linear)
773 [[unlikely]] {
769 const bool strict_size = False(options & RelaxedOptions::Size) && 774 const bool strict_size = False(options & RelaxedOptions::Size) &&
770 True(existing_image.flags & ImageFlagBits::Strong); 775 True(existing_image.flags & ImageFlagBits::Strong);
771 const ImageInfo& existing = existing_image.info; 776 const ImageInfo& existing = existing_image.info;
@@ -774,17 +779,27 @@ ImageId TextureCache<P>::FindImage(const ImageInfo& info, GPUVAddr gpu_addr,
774 IsPitchLinearSameSize(existing, info, strict_size) && 779 IsPitchLinearSameSize(existing, info, strict_size) &&
775 IsViewCompatible(existing.format, info.format, broken_views, native_bgr)) { 780 IsViewCompatible(existing.format, info.format, broken_views, native_bgr)) {
776 image_id = existing_image_id; 781 image_id = existing_image_id;
777 return true; 782 image_ids.push_back(existing_image_id);
783 return !flexible_formats && existing.format == info.format;
778 } 784 }
779 } else if (IsSubresource(info, existing_image, gpu_addr, options, broken_views, 785 } else if (IsSubresource(info, existing_image, gpu_addr, options, broken_views,
780 native_bgr)) { 786 native_bgr)) {
781 image_id = existing_image_id; 787 image_id = existing_image_id;
782 return true; 788 image_ids.push_back(existing_image_id);
789 return !flexible_formats && existing_image.info.format == info.format;
783 } 790 }
784 return false; 791 return false;
785 }; 792 };
786 ForEachImageInRegion(*cpu_addr, CalculateGuestSizeInBytes(info), lambda); 793 ForEachImageInRegion(*cpu_addr, CalculateGuestSizeInBytes(info), lambda);
787 return image_id; 794 if (image_ids.size() <= 1) [[likely]] {
795 return image_id;
796 }
797 auto image_ids_compare = [this](ImageId a, ImageId b) {
798 auto& image_a = slot_images[a];
799 auto& image_b = slot_images[b];
800 return image_a.modification_tick < image_b.modification_tick;
801 };
802 return *std::ranges::max_element(image_ids, image_ids_compare);
788} 803}
789 804
790template <class P> 805template <class P>
@@ -1076,31 +1091,66 @@ ImageId TextureCache<P>::JoinImages(const ImageInfo& info, GPUVAddr gpu_addr, VA
1076 1091
1077template <class P> 1092template <class P>
1078typename TextureCache<P>::BlitImages TextureCache<P>::GetBlitImages( 1093typename TextureCache<P>::BlitImages TextureCache<P>::GetBlitImages(
1079 const Tegra::Engines::Fermi2D::Surface& dst, const Tegra::Engines::Fermi2D::Surface& src) { 1094 const Tegra::Engines::Fermi2D::Surface& dst, const Tegra::Engines::Fermi2D::Surface& src,
1080 static constexpr auto FIND_OPTIONS = RelaxedOptions::Format | RelaxedOptions::Samples; 1095 const Tegra::Engines::Fermi2D::Config& copy) {
1096
1097 static constexpr auto FIND_OPTIONS = RelaxedOptions::Samples;
1081 const GPUVAddr dst_addr = dst.Address(); 1098 const GPUVAddr dst_addr = dst.Address();
1082 const GPUVAddr src_addr = src.Address(); 1099 const GPUVAddr src_addr = src.Address();
1083 ImageInfo dst_info(dst); 1100 ImageInfo dst_info(dst);
1084 ImageInfo src_info(src); 1101 ImageInfo src_info(src);
1102 const bool can_be_depth_blit =
1103 dst_info.format == src_info.format && copy.filter == Tegra::Engines::Fermi2D::Filter::Point;
1085 ImageId dst_id; 1104 ImageId dst_id;
1086 ImageId src_id; 1105 ImageId src_id;
1106 RelaxedOptions try_options = FIND_OPTIONS;
1107 if (can_be_depth_blit) {
1108 try_options |= RelaxedOptions::Format;
1109 }
1087 do { 1110 do {
1088 has_deleted_images = false; 1111 has_deleted_images = false;
1089 dst_id = FindImage(dst_info, dst_addr, FIND_OPTIONS); 1112 src_id = FindImage(src_info, src_addr, try_options);
1090 src_id = FindImage(src_info, src_addr, FIND_OPTIONS); 1113 dst_id = FindImage(dst_info, dst_addr, try_options);
1091 const ImageBase* const dst_image = dst_id ? &slot_images[dst_id] : nullptr;
1092 const ImageBase* const src_image = src_id ? &slot_images[src_id] : nullptr; 1114 const ImageBase* const src_image = src_id ? &slot_images[src_id] : nullptr;
1093 DeduceBlitImages(dst_info, src_info, dst_image, src_image); 1115 if (src_image && src_image->info.num_samples > 1) {
1094 if (GetFormatType(dst_info.format) != GetFormatType(src_info.format)) { 1116 RelaxedOptions find_options{FIND_OPTIONS | RelaxedOptions::ForceBrokenViews};
1095 continue; 1117 src_id = FindOrInsertImage(src_info, src_addr, find_options);
1118 dst_id = FindOrInsertImage(dst_info, dst_addr, find_options);
1119 if (has_deleted_images) {
1120 continue;
1121 }
1122 break;
1096 } 1123 }
1097 if (!dst_id) { 1124 if (can_be_depth_blit) {
1098 dst_id = InsertImage(dst_info, dst_addr, RelaxedOptions{}); 1125 const ImageBase* const dst_image = dst_id ? &slot_images[dst_id] : nullptr;
1126 DeduceBlitImages(dst_info, src_info, dst_image, src_image);
1127 if (GetFormatType(dst_info.format) != GetFormatType(src_info.format)) {
1128 continue;
1129 }
1099 } 1130 }
1100 if (!src_id) { 1131 if (!src_id) {
1101 src_id = InsertImage(src_info, src_addr, RelaxedOptions{}); 1132 src_id = InsertImage(src_info, src_addr, RelaxedOptions{});
1102 } 1133 }
1134 if (!dst_id) {
1135 dst_id = InsertImage(dst_info, dst_addr, RelaxedOptions{});
1136 }
1103 } while (has_deleted_images); 1137 } while (has_deleted_images);
1138 const ImageBase& src_image = slot_images[src_id];
1139 const ImageBase& dst_image = slot_images[dst_id];
1140 const bool native_bgr = runtime.HasNativeBgr();
1141 if (GetFormatType(dst_info.format) != GetFormatType(dst_image.info.format) ||
1142 GetFormatType(src_info.format) != GetFormatType(src_image.info.format) ||
1143 !VideoCore::Surface::IsViewCompatible(dst_info.format, dst_image.info.format, false,
1144 native_bgr) ||
1145 !VideoCore::Surface::IsViewCompatible(src_info.format, src_image.info.format, false,
1146 native_bgr)) {
1147 // Make sure the images match the expected format.
1148 do {
1149 has_deleted_images = false;
1150 src_id = FindOrInsertImage(src_info, src_addr, RelaxedOptions{});
1151 dst_id = FindOrInsertImage(dst_info, dst_addr, RelaxedOptions{});
1152 } while (has_deleted_images);
1153 }
1104 return BlitImages{ 1154 return BlitImages{
1105 .dst_id = dst_id, 1155 .dst_id = dst_id,
1106 .src_id = src_id, 1156 .src_id = src_id,
@@ -1157,7 +1207,14 @@ template <class P>
1157ImageViewId TextureCache<P>::FindRenderTargetView(const ImageInfo& info, GPUVAddr gpu_addr, 1207ImageViewId TextureCache<P>::FindRenderTargetView(const ImageInfo& info, GPUVAddr gpu_addr,
1158 bool is_clear) { 1208 bool is_clear) {
1159 const auto options = is_clear ? RelaxedOptions::Samples : RelaxedOptions{}; 1209 const auto options = is_clear ? RelaxedOptions::Samples : RelaxedOptions{};
1160 const ImageId image_id = FindOrInsertImage(info, gpu_addr, options); 1210 ImageId image_id{};
1211 bool delete_state = has_deleted_images;
1212 do {
1213 has_deleted_images = false;
1214 image_id = FindOrInsertImage(info, gpu_addr, options);
1215 delete_state |= has_deleted_images;
1216 } while (has_deleted_images);
1217 has_deleted_images = delete_state;
1161 if (!image_id) { 1218 if (!image_id) {
1162 return NULL_IMAGE_VIEW_ID; 1219 return NULL_IMAGE_VIEW_ID;
1163 } 1220 }
@@ -1759,8 +1816,8 @@ void TextureCache<P>::CopyImage(ImageId dst_id, ImageId src_id, std::vector<Imag
1759 } 1816 }
1760 UNIMPLEMENTED_IF(dst.info.type != ImageType::e2D); 1817 UNIMPLEMENTED_IF(dst.info.type != ImageType::e2D);
1761 UNIMPLEMENTED_IF(src.info.type != ImageType::e2D); 1818 UNIMPLEMENTED_IF(src.info.type != ImageType::e2D);
1762 if constexpr (HAS_PIXEL_FORMAT_CONVERSIONS) { 1819 if (runtime.ShouldReinterpret(dst, src)) {
1763 return runtime.ConvertImage(dst, src, copies); 1820 return runtime.ReinterpretImage(dst, src, copies);
1764 } 1821 }
1765 for (const ImageCopy& copy : copies) { 1822 for (const ImageCopy& copy : copies) {
1766 UNIMPLEMENTED_IF(copy.dst_subresource.num_layers != 1); 1823 UNIMPLEMENTED_IF(copy.dst_subresource.num_layers != 1);
@@ -1780,7 +1837,13 @@ void TextureCache<P>::CopyImage(ImageId dst_id, ImageId src_id, std::vector<Imag
1780 const SubresourceExtent src_extent{.levels = 1, .layers = 1}; 1837 const SubresourceExtent src_extent{.levels = 1, .layers = 1};
1781 const SubresourceRange dst_range{.base = dst_base, .extent = dst_extent}; 1838 const SubresourceRange dst_range{.base = dst_base, .extent = dst_extent};
1782 const SubresourceRange src_range{.base = src_base, .extent = src_extent}; 1839 const SubresourceRange src_range{.base = src_base, .extent = src_extent};
1783 const ImageViewInfo dst_view_info(ImageViewType::e2D, dst.info.format, dst_range); 1840 PixelFormat dst_format = dst.info.format;
1841 if (GetFormatType(src.info.format) == SurfaceType::DepthStencil &&
1842 GetFormatType(dst_format) == SurfaceType::ColorTexture &&
1843 BytesPerBlock(dst_format) == 4) {
1844 dst_format = PixelFormat::A8B8G8R8_UNORM;
1845 }
1846 const ImageViewInfo dst_view_info(ImageViewType::e2D, dst_format, dst_range);
1784 const ImageViewInfo src_view_info(ImageViewType::e2D, src.info.format, src_range); 1847 const ImageViewInfo src_view_info(ImageViewType::e2D, src.info.format, src_range);
1785 const auto [dst_framebuffer_id, dst_view_id] = RenderTargetFromImage(dst_id, dst_view_info); 1848 const auto [dst_framebuffer_id, dst_view_id] = RenderTargetFromImage(dst_id, dst_view_info);
1786 Framebuffer* const dst_framebuffer = &slot_framebuffers[dst_framebuffer_id]; 1849 Framebuffer* const dst_framebuffer = &slot_framebuffers[dst_framebuffer_id];
diff --git a/src/video_core/texture_cache/texture_cache_base.h b/src/video_core/texture_cache/texture_cache_base.h
index a9504c0e8..7107887a6 100644
--- a/src/video_core/texture_cache/texture_cache_base.h
+++ b/src/video_core/texture_cache/texture_cache_base.h
@@ -59,8 +59,6 @@ class TextureCache {
59 static constexpr bool HAS_EMULATED_COPIES = P::HAS_EMULATED_COPIES; 59 static constexpr bool HAS_EMULATED_COPIES = P::HAS_EMULATED_COPIES;
60 /// True when the API can provide info about the memory of the device. 60 /// True when the API can provide info about the memory of the device.
61 static constexpr bool HAS_DEVICE_MEMORY_INFO = P::HAS_DEVICE_MEMORY_INFO; 61 static constexpr bool HAS_DEVICE_MEMORY_INFO = P::HAS_DEVICE_MEMORY_INFO;
62 /// True when the API provides utilities for pixel format conversions.
63 static constexpr bool HAS_PIXEL_FORMAT_CONVERSIONS = P::HAS_PIXEL_FORMAT_CONVERSIONS;
64 62
65 static constexpr u64 DEFAULT_EXPECTED_MEMORY = 1_GiB; 63 static constexpr u64 DEFAULT_EXPECTED_MEMORY = 1_GiB;
66 static constexpr u64 DEFAULT_CRITICAL_MEMORY = 2_GiB; 64 static constexpr u64 DEFAULT_CRITICAL_MEMORY = 2_GiB;
@@ -254,7 +252,8 @@ private:
254 252
255 /// Return a blit image pair from the given guest blit parameters 253 /// Return a blit image pair from the given guest blit parameters
256 [[nodiscard]] BlitImages GetBlitImages(const Tegra::Engines::Fermi2D::Surface& dst, 254 [[nodiscard]] BlitImages GetBlitImages(const Tegra::Engines::Fermi2D::Surface& dst,
257 const Tegra::Engines::Fermi2D::Surface& src); 255 const Tegra::Engines::Fermi2D::Surface& src,
256 const Tegra::Engines::Fermi2D::Config& copy);
258 257
259 /// Find or create a sampler from a guest descriptor sampler 258 /// Find or create a sampler from a guest descriptor sampler
260 [[nodiscard]] SamplerId FindSampler(const TSCEntry& config); 259 [[nodiscard]] SamplerId FindSampler(const TSCEntry& config);
diff --git a/src/video_core/texture_cache/types.h b/src/video_core/texture_cache/types.h
index 5c274abdf..5ac27b3a7 100644
--- a/src/video_core/texture_cache/types.h
+++ b/src/video_core/texture_cache/types.h
@@ -54,6 +54,7 @@ enum class RelaxedOptions : u32 {
54 Size = 1 << 0, 54 Size = 1 << 0,
55 Format = 1 << 1, 55 Format = 1 << 1,
56 Samples = 1 << 2, 56 Samples = 1 << 2,
57 ForceBrokenViews = 1 << 3,
57}; 58};
58DECLARE_ENUM_FLAG_OPERATORS(RelaxedOptions) 59DECLARE_ENUM_FLAG_OPERATORS(RelaxedOptions)
59 60
diff --git a/src/video_core/texture_cache/util.cpp b/src/video_core/texture_cache/util.cpp
index ddc9fb13a..7bd31b211 100644
--- a/src/video_core/texture_cache/util.cpp
+++ b/src/video_core/texture_cache/util.cpp
@@ -1151,6 +1151,7 @@ bool IsSubresource(const ImageInfo& candidate, const ImageBase& image, GPUVAddr
1151 1151
1152void DeduceBlitImages(ImageInfo& dst_info, ImageInfo& src_info, const ImageBase* dst, 1152void DeduceBlitImages(ImageInfo& dst_info, ImageInfo& src_info, const ImageBase* dst,
1153 const ImageBase* src) { 1153 const ImageBase* src) {
1154 const auto original_dst_format = dst_info.format;
1154 if (src && GetFormatType(src->info.format) != SurfaceType::ColorTexture) { 1155 if (src && GetFormatType(src->info.format) != SurfaceType::ColorTexture) {
1155 src_info.format = src->info.format; 1156 src_info.format = src->info.format;
1156 } 1157 }
@@ -1161,7 +1162,13 @@ void DeduceBlitImages(ImageInfo& dst_info, ImageInfo& src_info, const ImageBase*
1161 dst_info.format = src->info.format; 1162 dst_info.format = src->info.format;
1162 } 1163 }
1163 if (dst && GetFormatType(dst->info.format) != SurfaceType::ColorTexture) { 1164 if (dst && GetFormatType(dst->info.format) != SurfaceType::ColorTexture) {
1164 src_info.format = dst->info.format; 1165 if (src) {
1166 if (GetFormatType(src->info.format) == SurfaceType::ColorTexture) {
1167 dst_info.format = original_dst_format;
1168 }
1169 } else {
1170 src_info.format = dst->info.format;
1171 }
1165 } 1172 }
1166} 1173}
1167 1174
diff --git a/src/video_core/video_core.cpp b/src/video_core/video_core.cpp
index e852c817e..329bf4def 100644
--- a/src/video_core/video_core.cpp
+++ b/src/video_core/video_core.cpp
@@ -55,10 +55,4 @@ std::unique_ptr<Tegra::GPU> CreateGPU(Core::Frontend::EmuWindow& emu_window, Cor
55 } 55 }
56} 56}
57 57
58float GetResolutionScaleFactor(const RendererBase& renderer) {
59 return Settings::values.resolution_info.active
60 ? Settings::values.resolution_info.up_factor
61 : renderer.GetRenderWindow().GetFramebufferLayout().GetScalingRatio();
62}
63
64} // namespace VideoCore 58} // namespace VideoCore
diff --git a/src/video_core/video_core.h b/src/video_core/video_core.h
index f86877e86..084df641f 100644
--- a/src/video_core/video_core.h
+++ b/src/video_core/video_core.h
@@ -25,6 +25,4 @@ class RendererBase;
25/// Creates an emulated GPU instance using the given system context. 25/// Creates an emulated GPU instance using the given system context.
26std::unique_ptr<Tegra::GPU> CreateGPU(Core::Frontend::EmuWindow& emu_window, Core::System& system); 26std::unique_ptr<Tegra::GPU> CreateGPU(Core::Frontend::EmuWindow& emu_window, Core::System& system);
27 27
28float GetResolutionScaleFactor(const RendererBase& renderer);
29
30} // namespace VideoCore 28} // namespace VideoCore
diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp
index 95106f88f..7bf5b6578 100644
--- a/src/video_core/vulkan_common/vulkan_device.cpp
+++ b/src/video_core/vulkan_common/vulkan_device.cpp
@@ -21,6 +21,13 @@
21namespace Vulkan { 21namespace Vulkan {
22namespace { 22namespace {
23namespace Alternatives { 23namespace Alternatives {
24constexpr std::array STENCIL8_UINT{
25 VK_FORMAT_D16_UNORM_S8_UINT,
26 VK_FORMAT_D24_UNORM_S8_UINT,
27 VK_FORMAT_D32_SFLOAT_S8_UINT,
28 VK_FORMAT_UNDEFINED,
29};
30
24constexpr std::array DEPTH24_UNORM_STENCIL8_UINT{ 31constexpr std::array DEPTH24_UNORM_STENCIL8_UINT{
25 VK_FORMAT_D32_SFLOAT_S8_UINT, 32 VK_FORMAT_D32_SFLOAT_S8_UINT,
26 VK_FORMAT_D16_UNORM_S8_UINT, 33 VK_FORMAT_D16_UNORM_S8_UINT,
@@ -74,6 +81,8 @@ void SetNext(void**& next, T& data) {
74 81
75constexpr const VkFormat* GetFormatAlternatives(VkFormat format) { 82constexpr const VkFormat* GetFormatAlternatives(VkFormat format) {
76 switch (format) { 83 switch (format) {
84 case VK_FORMAT_S8_UINT:
85 return Alternatives::STENCIL8_UINT.data();
77 case VK_FORMAT_D24_UNORM_S8_UINT: 86 case VK_FORMAT_D24_UNORM_S8_UINT:
78 return Alternatives::DEPTH24_UNORM_STENCIL8_UINT.data(); 87 return Alternatives::DEPTH24_UNORM_STENCIL8_UINT.data();
79 case VK_FORMAT_D16_UNORM_S8_UINT: 88 case VK_FORMAT_D16_UNORM_S8_UINT:
@@ -121,6 +130,7 @@ std::unordered_map<VkFormat, VkFormatProperties> GetFormatProperties(vk::Physica
121 VK_FORMAT_R16G16_UNORM, 130 VK_FORMAT_R16G16_UNORM,
122 VK_FORMAT_R16G16_SNORM, 131 VK_FORMAT_R16G16_SNORM,
123 VK_FORMAT_R16G16_SFLOAT, 132 VK_FORMAT_R16G16_SFLOAT,
133 VK_FORMAT_R16G16_UINT,
124 VK_FORMAT_R16G16_SINT, 134 VK_FORMAT_R16G16_SINT,
125 VK_FORMAT_R16_UNORM, 135 VK_FORMAT_R16_UNORM,
126 VK_FORMAT_R16_SNORM, 136 VK_FORMAT_R16_SNORM,
@@ -145,6 +155,7 @@ std::unordered_map<VkFormat, VkFormatProperties> GetFormatProperties(vk::Physica
145 VK_FORMAT_R4G4B4A4_UNORM_PACK16, 155 VK_FORMAT_R4G4B4A4_UNORM_PACK16,
146 VK_FORMAT_D32_SFLOAT, 156 VK_FORMAT_D32_SFLOAT,
147 VK_FORMAT_D16_UNORM, 157 VK_FORMAT_D16_UNORM,
158 VK_FORMAT_S8_UINT,
148 VK_FORMAT_D16_UNORM_S8_UINT, 159 VK_FORMAT_D16_UNORM_S8_UINT,
149 VK_FORMAT_D24_UNORM_S8_UINT, 160 VK_FORMAT_D24_UNORM_S8_UINT,
150 VK_FORMAT_D32_SFLOAT_S8_UINT, 161 VK_FORMAT_D32_SFLOAT_S8_UINT,
diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt
index d62fd566f..732e8c276 100644
--- a/src/yuzu/CMakeLists.txt
+++ b/src/yuzu/CMakeLists.txt
@@ -90,9 +90,6 @@ add_executable(yuzu
90 configuration/configure_motion_touch.cpp 90 configuration/configure_motion_touch.cpp
91 configuration/configure_motion_touch.h 91 configuration/configure_motion_touch.h
92 configuration/configure_motion_touch.ui 92 configuration/configure_motion_touch.ui
93 configuration/configure_mouse_advanced.cpp
94 configuration/configure_mouse_advanced.h
95 configuration/configure_mouse_advanced.ui
96 configuration/configure_per_game.cpp 93 configuration/configure_per_game.cpp
97 configuration/configure_per_game.h 94 configuration/configure_per_game.h
98 configuration/configure_per_game.ui 95 configuration/configure_per_game.ui
@@ -155,6 +152,8 @@ add_executable(yuzu
155 main.ui 152 main.ui
156 uisettings.cpp 153 uisettings.cpp
157 uisettings.h 154 uisettings.h
155 util/controller_navigation.cpp
156 util/controller_navigation.h
158 util/limitable_input_dialog.cpp 157 util/limitable_input_dialog.cpp
159 util/limitable_input_dialog.h 158 util/limitable_input_dialog.h
160 util/overlay_dialog.cpp 159 util/overlay_dialog.cpp
diff --git a/src/yuzu/applets/qt_controller.cpp b/src/yuzu/applets/qt_controller.cpp
index bf8445a89..c5685db2e 100644
--- a/src/yuzu/applets/qt_controller.cpp
+++ b/src/yuzu/applets/qt_controller.cpp
@@ -6,8 +6,12 @@
6#include <thread> 6#include <thread>
7 7
8#include "common/assert.h" 8#include "common/assert.h"
9#include "common/param_package.h"
9#include "common/string_util.h" 10#include "common/string_util.h"
10#include "core/core.h" 11#include "core/core.h"
12#include "core/hid/emulated_controller.h"
13#include "core/hid/hid_core.h"
14#include "core/hid/hid_types.h"
11#include "core/hle/lock.h" 15#include "core/hle/lock.h"
12#include "core/hle/service/hid/controllers/npad.h" 16#include "core/hle/service/hid/controllers/npad.h"
13#include "core/hle/service/hid/hid.h" 17#include "core/hle/service/hid/hid.h"
@@ -23,49 +27,32 @@
23 27
24namespace { 28namespace {
25 29
26constexpr std::size_t HANDHELD_INDEX = 8; 30void UpdateController(Core::HID::EmulatedController* controller,
27 31 Core::HID::NpadStyleIndex controller_type, bool connected) {
28constexpr std::array<std::array<bool, 4>, 8> led_patterns{{ 32 if (controller->IsConnected(true)) {
29 {true, false, false, false}, 33 controller->Disconnect();
30 {true, true, false, false}, 34 }
31 {true, true, true, false}, 35 controller->SetNpadStyleIndex(controller_type);
32 {true, true, true, true}, 36 if (connected) {
33 {true, false, false, true}, 37 controller->Connect();
34 {true, false, true, false},
35 {true, false, true, true},
36 {false, true, true, false},
37}};
38
39void UpdateController(Settings::ControllerType controller_type, std::size_t npad_index,
40 bool connected, Core::System& system) {
41 if (!system.IsPoweredOn()) {
42 return;
43 } 38 }
44
45 auto& npad =
46 system.ServiceManager()
47 .GetService<Service::HID::Hid>("hid")
48 ->GetAppletResource()
49 ->GetController<Service::HID::Controller_NPad>(Service::HID::HidController::NPad);
50
51 npad.UpdateControllerAt(npad.MapSettingsTypeToNPad(controller_type), npad_index, connected);
52} 39}
53 40
54// Returns true if the given controller type is compatible with the given parameters. 41// Returns true if the given controller type is compatible with the given parameters.
55bool IsControllerCompatible(Settings::ControllerType controller_type, 42bool IsControllerCompatible(Core::HID::NpadStyleIndex controller_type,
56 Core::Frontend::ControllerParameters parameters) { 43 Core::Frontend::ControllerParameters parameters) {
57 switch (controller_type) { 44 switch (controller_type) {
58 case Settings::ControllerType::ProController: 45 case Core::HID::NpadStyleIndex::ProController:
59 return parameters.allow_pro_controller; 46 return parameters.allow_pro_controller;
60 case Settings::ControllerType::DualJoyconDetached: 47 case Core::HID::NpadStyleIndex::JoyconDual:
61 return parameters.allow_dual_joycons; 48 return parameters.allow_dual_joycons;
62 case Settings::ControllerType::LeftJoycon: 49 case Core::HID::NpadStyleIndex::JoyconLeft:
63 return parameters.allow_left_joycon; 50 return parameters.allow_left_joycon;
64 case Settings::ControllerType::RightJoycon: 51 case Core::HID::NpadStyleIndex::JoyconRight:
65 return parameters.allow_right_joycon; 52 return parameters.allow_right_joycon;
66 case Settings::ControllerType::Handheld: 53 case Core::HID::NpadStyleIndex::Handheld:
67 return parameters.enable_single_mode && parameters.allow_handheld; 54 return parameters.enable_single_mode && parameters.allow_handheld;
68 case Settings::ControllerType::GameCube: 55 case Core::HID::NpadStyleIndex::GameCube:
69 return parameters.allow_gamecube_controller; 56 return parameters.allow_gamecube_controller;
70 default: 57 default:
71 return false; 58 return false;
@@ -196,7 +183,7 @@ QtControllerSelectorDialog::QtControllerSelectorDialog(
196 connect(emulated_controllers[i], qOverload<int>(&QComboBox::currentIndexChanged), 183 connect(emulated_controllers[i], qOverload<int>(&QComboBox::currentIndexChanged),
197 [this, i](int index) { 184 [this, i](int index) {
198 UpdateDockedState(GetControllerTypeFromIndex(index, i) == 185 UpdateDockedState(GetControllerTypeFromIndex(index, i) ==
199 Settings::ControllerType::Handheld); 186 Core::HID::NpadStyleIndex::Handheld);
200 }); 187 });
201 } 188 }
202 } 189 }
@@ -230,7 +217,9 @@ QtControllerSelectorDialog::QtControllerSelectorDialog(
230 resize(0, 0); 217 resize(0, 0);
231} 218}
232 219
233QtControllerSelectorDialog::~QtControllerSelectorDialog() = default; 220QtControllerSelectorDialog::~QtControllerSelectorDialog() {
221 system.HIDCore().DisableAllControllerConfiguration();
222}
234 223
235int QtControllerSelectorDialog::exec() { 224int QtControllerSelectorDialog::exec() {
236 if (parameters_met && parameters.enable_single_mode) { 225 if (parameters_met && parameters.enable_single_mode) {
@@ -249,17 +238,20 @@ void QtControllerSelectorDialog::ApplyConfiguration() {
249} 238}
250 239
251void QtControllerSelectorDialog::LoadConfiguration() { 240void QtControllerSelectorDialog::LoadConfiguration() {
241 system.HIDCore().EnableAllControllerConfiguration();
242
243 const auto* handheld = system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Handheld);
252 for (std::size_t index = 0; index < NUM_PLAYERS; ++index) { 244 for (std::size_t index = 0; index < NUM_PLAYERS; ++index) {
245 const auto* controller = system.HIDCore().GetEmulatedControllerByIndex(index);
253 const auto connected = 246 const auto connected =
254 Settings::values.players.GetValue()[index].connected || 247 controller->IsConnected(true) || (index == 0 && handheld->IsConnected(true));
255 (index == 0 && Settings::values.players.GetValue()[HANDHELD_INDEX].connected);
256 player_groupboxes[index]->setChecked(connected); 248 player_groupboxes[index]->setChecked(connected);
257 connected_controller_checkboxes[index]->setChecked(connected); 249 connected_controller_checkboxes[index]->setChecked(connected);
258 emulated_controllers[index]->setCurrentIndex(GetIndexFromControllerType( 250 emulated_controllers[index]->setCurrentIndex(
259 Settings::values.players.GetValue()[index].controller_type, index)); 251 GetIndexFromControllerType(controller->GetNpadStyleIndex(true), index));
260 } 252 }
261 253
262 UpdateDockedState(Settings::values.players.GetValue()[HANDHELD_INDEX].connected); 254 UpdateDockedState(handheld->IsConnected(true));
263 255
264 ui->vibrationGroup->setChecked(Settings::values.vibration_enabled.GetValue()); 256 ui->vibrationGroup->setChecked(Settings::values.vibration_enabled.GetValue());
265 ui->motionGroup->setChecked(Settings::values.motion_enabled.GetValue()); 257 ui->motionGroup->setChecked(Settings::values.motion_enabled.GetValue());
@@ -415,33 +407,33 @@ void QtControllerSelectorDialog::SetEmulatedControllers(std::size_t player_index
415 emulated_controllers[player_index]->clear(); 407 emulated_controllers[player_index]->clear();
416 408
417 pairs.emplace_back(emulated_controllers[player_index]->count(), 409 pairs.emplace_back(emulated_controllers[player_index]->count(),
418 Settings::ControllerType::ProController); 410 Core::HID::NpadStyleIndex::ProController);
419 emulated_controllers[player_index]->addItem(tr("Pro Controller")); 411 emulated_controllers[player_index]->addItem(tr("Pro Controller"));
420 412
421 pairs.emplace_back(emulated_controllers[player_index]->count(), 413 pairs.emplace_back(emulated_controllers[player_index]->count(),
422 Settings::ControllerType::DualJoyconDetached); 414 Core::HID::NpadStyleIndex::JoyconDual);
423 emulated_controllers[player_index]->addItem(tr("Dual Joycons")); 415 emulated_controllers[player_index]->addItem(tr("Dual Joycons"));
424 416
425 pairs.emplace_back(emulated_controllers[player_index]->count(), 417 pairs.emplace_back(emulated_controllers[player_index]->count(),
426 Settings::ControllerType::LeftJoycon); 418 Core::HID::NpadStyleIndex::JoyconLeft);
427 emulated_controllers[player_index]->addItem(tr("Left Joycon")); 419 emulated_controllers[player_index]->addItem(tr("Left Joycon"));
428 420
429 pairs.emplace_back(emulated_controllers[player_index]->count(), 421 pairs.emplace_back(emulated_controllers[player_index]->count(),
430 Settings::ControllerType::RightJoycon); 422 Core::HID::NpadStyleIndex::JoyconRight);
431 emulated_controllers[player_index]->addItem(tr("Right Joycon")); 423 emulated_controllers[player_index]->addItem(tr("Right Joycon"));
432 424
433 if (player_index == 0) { 425 if (player_index == 0) {
434 pairs.emplace_back(emulated_controllers[player_index]->count(), 426 pairs.emplace_back(emulated_controllers[player_index]->count(),
435 Settings::ControllerType::Handheld); 427 Core::HID::NpadStyleIndex::Handheld);
436 emulated_controllers[player_index]->addItem(tr("Handheld")); 428 emulated_controllers[player_index]->addItem(tr("Handheld"));
437 } 429 }
438 430
439 pairs.emplace_back(emulated_controllers[player_index]->count(), 431 pairs.emplace_back(emulated_controllers[player_index]->count(),
440 Settings::ControllerType::GameCube); 432 Core::HID::NpadStyleIndex::GameCube);
441 emulated_controllers[player_index]->addItem(tr("GameCube Controller")); 433 emulated_controllers[player_index]->addItem(tr("GameCube Controller"));
442} 434}
443 435
444Settings::ControllerType QtControllerSelectorDialog::GetControllerTypeFromIndex( 436Core::HID::NpadStyleIndex QtControllerSelectorDialog::GetControllerTypeFromIndex(
445 int index, std::size_t player_index) const { 437 int index, std::size_t player_index) const {
446 const auto& pairs = index_controller_type_pairs[player_index]; 438 const auto& pairs = index_controller_type_pairs[player_index];
447 439
@@ -449,13 +441,13 @@ Settings::ControllerType QtControllerSelectorDialog::GetControllerTypeFromIndex(
449 [index](const auto& pair) { return pair.first == index; }); 441 [index](const auto& pair) { return pair.first == index; });
450 442
451 if (it == pairs.end()) { 443 if (it == pairs.end()) {
452 return Settings::ControllerType::ProController; 444 return Core::HID::NpadStyleIndex::ProController;
453 } 445 }
454 446
455 return it->second; 447 return it->second;
456} 448}
457 449
458int QtControllerSelectorDialog::GetIndexFromControllerType(Settings::ControllerType type, 450int QtControllerSelectorDialog::GetIndexFromControllerType(Core::HID::NpadStyleIndex type,
459 std::size_t player_index) const { 451 std::size_t player_index) const {
460 const auto& pairs = index_controller_type_pairs[player_index]; 452 const auto& pairs = index_controller_type_pairs[player_index];
461 453
@@ -479,16 +471,16 @@ void QtControllerSelectorDialog::UpdateControllerIcon(std::size_t player_index)
479 const QString stylesheet = [this, player_index] { 471 const QString stylesheet = [this, player_index] {
480 switch (GetControllerTypeFromIndex(emulated_controllers[player_index]->currentIndex(), 472 switch (GetControllerTypeFromIndex(emulated_controllers[player_index]->currentIndex(),
481 player_index)) { 473 player_index)) {
482 case Settings::ControllerType::ProController: 474 case Core::HID::NpadStyleIndex::ProController:
483 case Settings::ControllerType::GameCube: 475 case Core::HID::NpadStyleIndex::GameCube:
484 return QStringLiteral("image: url(:/controller/applet_pro_controller%0); "); 476 return QStringLiteral("image: url(:/controller/applet_pro_controller%0); ");
485 case Settings::ControllerType::DualJoyconDetached: 477 case Core::HID::NpadStyleIndex::JoyconDual:
486 return QStringLiteral("image: url(:/controller/applet_dual_joycon%0); "); 478 return QStringLiteral("image: url(:/controller/applet_dual_joycon%0); ");
487 case Settings::ControllerType::LeftJoycon: 479 case Core::HID::NpadStyleIndex::JoyconLeft:
488 return QStringLiteral("image: url(:/controller/applet_joycon_left%0); "); 480 return QStringLiteral("image: url(:/controller/applet_joycon_left%0); ");
489 case Settings::ControllerType::RightJoycon: 481 case Core::HID::NpadStyleIndex::JoyconRight:
490 return QStringLiteral("image: url(:/controller/applet_joycon_right%0); "); 482 return QStringLiteral("image: url(:/controller/applet_joycon_right%0); ");
491 case Settings::ControllerType::Handheld: 483 case Core::HID::NpadStyleIndex::Handheld:
492 return QStringLiteral("image: url(:/controller/applet_handheld%0); "); 484 return QStringLiteral("image: url(:/controller/applet_handheld%0); ");
493 default: 485 default:
494 return QString{}; 486 return QString{};
@@ -516,54 +508,38 @@ void QtControllerSelectorDialog::UpdateControllerIcon(std::size_t player_index)
516} 508}
517 509
518void QtControllerSelectorDialog::UpdateControllerState(std::size_t player_index) { 510void QtControllerSelectorDialog::UpdateControllerState(std::size_t player_index) {
519 auto& player = Settings::values.players.GetValue()[player_index]; 511 auto* controller = system.HIDCore().GetEmulatedControllerByIndex(player_index);
520 512
521 const auto controller_type = GetControllerTypeFromIndex( 513 const auto controller_type = GetControllerTypeFromIndex(
522 emulated_controllers[player_index]->currentIndex(), player_index); 514 emulated_controllers[player_index]->currentIndex(), player_index);
523 const auto player_connected = player_groupboxes[player_index]->isChecked() && 515 const auto player_connected = player_groupboxes[player_index]->isChecked() &&
524 controller_type != Settings::ControllerType::Handheld; 516 controller_type != Core::HID::NpadStyleIndex::Handheld;
525 517
526 if (player.controller_type == controller_type && player.connected == player_connected) { 518 if (controller->GetNpadStyleIndex(true) == controller_type &&
527 // Set vibration devices in the event that the input device has changed. 519 controller->IsConnected(true) == player_connected) {
528 ConfigureVibration::SetVibrationDevices(player_index);
529 return; 520 return;
530 } 521 }
531 522
532 // Disconnect the controller first. 523 // Disconnect the controller first.
533 UpdateController(controller_type, player_index, false, system); 524 UpdateController(controller, controller_type, false);
534
535 player.controller_type = controller_type;
536 player.connected = player_connected;
537
538 ConfigureVibration::SetVibrationDevices(player_index);
539 525
540 // Handheld 526 // Handheld
541 if (player_index == 0) { 527 if (player_index == 0) {
542 auto& handheld = Settings::values.players.GetValue()[HANDHELD_INDEX]; 528 if (controller_type == Core::HID::NpadStyleIndex::Handheld) {
543 if (controller_type == Settings::ControllerType::Handheld) { 529 auto* handheld =
544 handheld = player; 530 system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Handheld);
531 UpdateController(handheld, Core::HID::NpadStyleIndex::Handheld,
532 player_groupboxes[player_index]->isChecked());
545 } 533 }
546 handheld.connected = player_groupboxes[player_index]->isChecked() &&
547 controller_type == Settings::ControllerType::Handheld;
548 UpdateController(Settings::ControllerType::Handheld, 8, handheld.connected, system);
549 }
550
551 if (!player.connected) {
552 return;
553 } 534 }
554 535
555 // This emulates a delay between disconnecting and reconnecting controllers as some games 536 UpdateController(controller, controller_type, player_connected);
556 // do not respond to a change in controller type if it was instantaneous.
557 using namespace std::chrono_literals;
558 std::this_thread::sleep_for(60ms);
559
560 UpdateController(controller_type, player_index, player_connected, system);
561} 537}
562 538
563void QtControllerSelectorDialog::UpdateLEDPattern(std::size_t player_index) { 539void QtControllerSelectorDialog::UpdateLEDPattern(std::size_t player_index) {
564 if (!player_groupboxes[player_index]->isChecked() || 540 if (!player_groupboxes[player_index]->isChecked() ||
565 GetControllerTypeFromIndex(emulated_controllers[player_index]->currentIndex(), 541 GetControllerTypeFromIndex(emulated_controllers[player_index]->currentIndex(),
566 player_index) == Settings::ControllerType::Handheld) { 542 player_index) == Core::HID::NpadStyleIndex::Handheld) {
567 led_patterns_boxes[player_index][0]->setChecked(false); 543 led_patterns_boxes[player_index][0]->setChecked(false);
568 led_patterns_boxes[player_index][1]->setChecked(false); 544 led_patterns_boxes[player_index][1]->setChecked(false);
569 led_patterns_boxes[player_index][2]->setChecked(false); 545 led_patterns_boxes[player_index][2]->setChecked(false);
@@ -571,10 +547,12 @@ void QtControllerSelectorDialog::UpdateLEDPattern(std::size_t player_index) {
571 return; 547 return;
572 } 548 }
573 549
574 led_patterns_boxes[player_index][0]->setChecked(led_patterns[player_index][0]); 550 const auto* controller = system.HIDCore().GetEmulatedControllerByIndex(player_index);
575 led_patterns_boxes[player_index][1]->setChecked(led_patterns[player_index][1]); 551 const auto led_pattern = controller->GetLedPattern();
576 led_patterns_boxes[player_index][2]->setChecked(led_patterns[player_index][2]); 552 led_patterns_boxes[player_index][0]->setChecked(led_pattern.position1);
577 led_patterns_boxes[player_index][3]->setChecked(led_patterns[player_index][3]); 553 led_patterns_boxes[player_index][1]->setChecked(led_pattern.position2);
554 led_patterns_boxes[player_index][2]->setChecked(led_pattern.position3);
555 led_patterns_boxes[player_index][3]->setChecked(led_pattern.position4);
578} 556}
579 557
580void QtControllerSelectorDialog::UpdateBorderColor(std::size_t player_index) { 558void QtControllerSelectorDialog::UpdateBorderColor(std::size_t player_index) {
@@ -654,10 +632,9 @@ void QtControllerSelectorDialog::DisableUnsupportedPlayers() {
654 } 632 }
655 633
656 for (std::size_t index = max_supported_players; index < NUM_PLAYERS; ++index) { 634 for (std::size_t index = max_supported_players; index < NUM_PLAYERS; ++index) {
635 auto* controller = system.HIDCore().GetEmulatedControllerByIndex(index);
657 // Disconnect any unsupported players here and disable or hide them if applicable. 636 // Disconnect any unsupported players here and disable or hide them if applicable.
658 Settings::values.players.GetValue()[index].connected = false; 637 UpdateController(controller, controller->GetNpadStyleIndex(true), false);
659 UpdateController(Settings::values.players.GetValue()[index].controller_type, index, false,
660 system);
661 // Hide the player widgets when max_supported_controllers is less than or equal to 4. 638 // Hide the player widgets when max_supported_controllers is less than or equal to 4.
662 if (max_supported_players <= 4) { 639 if (max_supported_players <= 4) {
663 player_widgets[index]->hide(); 640 player_widgets[index]->hide();
diff --git a/src/yuzu/applets/qt_controller.h b/src/yuzu/applets/qt_controller.h
index 037325f50..7ab9ced3d 100644
--- a/src/yuzu/applets/qt_controller.h
+++ b/src/yuzu/applets/qt_controller.h
@@ -7,7 +7,6 @@
7#include <array> 7#include <array>
8#include <memory> 8#include <memory>
9#include <QDialog> 9#include <QDialog>
10#include "core/core.h"
11#include "core/frontend/applets/controller.h" 10#include "core/frontend/applets/controller.h"
12 11
13class GMainWindow; 12class GMainWindow;
@@ -23,14 +22,19 @@ namespace InputCommon {
23class InputSubsystem; 22class InputSubsystem;
24} 23}
25 24
26namespace Settings {
27enum class ControllerType;
28}
29
30namespace Ui { 25namespace Ui {
31class QtControllerSelectorDialog; 26class QtControllerSelectorDialog;
32} 27}
33 28
29namespace Core {
30class System;
31}
32
33namespace Core::HID {
34class HIDCore;
35enum class NpadStyleIndex : u8;
36} // namespace Core::HID
37
34class QtControllerSelectorDialog final : public QDialog { 38class QtControllerSelectorDialog final : public QDialog {
35 Q_OBJECT 39 Q_OBJECT
36 40
@@ -70,10 +74,10 @@ private:
70 void SetEmulatedControllers(std::size_t player_index); 74 void SetEmulatedControllers(std::size_t player_index);
71 75
72 // Gets the Controller Type for a given controller combobox index per player. 76 // Gets the Controller Type for a given controller combobox index per player.
73 Settings::ControllerType GetControllerTypeFromIndex(int index, std::size_t player_index) const; 77 Core::HID::NpadStyleIndex GetControllerTypeFromIndex(int index, std::size_t player_index) const;
74 78
75 // Gets the controller combobox index for a given Controller Type per player. 79 // Gets the controller combobox index for a given Controller Type per player.
76 int GetIndexFromControllerType(Settings::ControllerType type, std::size_t player_index) const; 80 int GetIndexFromControllerType(Core::HID::NpadStyleIndex type, std::size_t player_index) const;
77 81
78 // Updates the controller icons per player. 82 // Updates the controller icons per player.
79 void UpdateControllerIcon(std::size_t player_index); 83 void UpdateControllerIcon(std::size_t player_index);
@@ -135,7 +139,7 @@ private:
135 std::array<QComboBox*, NUM_PLAYERS> emulated_controllers; 139 std::array<QComboBox*, NUM_PLAYERS> emulated_controllers;
136 140
137 /// Pairs of emulated controller index and Controller Type enum per player. 141 /// Pairs of emulated controller index and Controller Type enum per player.
138 std::array<std::vector<std::pair<int, Settings::ControllerType>>, NUM_PLAYERS> 142 std::array<std::vector<std::pair<int, Core::HID::NpadStyleIndex>>, NUM_PLAYERS>
139 index_controller_type_pairs; 143 index_controller_type_pairs;
140 144
141 // Labels representing the number of connected controllers 145 // Labels representing the number of connected controllers
diff --git a/src/yuzu/applets/qt_profile_select.cpp b/src/yuzu/applets/qt_profile_select.cpp
index a56638e21..7b19f1f8d 100644
--- a/src/yuzu/applets/qt_profile_select.cpp
+++ b/src/yuzu/applets/qt_profile_select.cpp
@@ -3,6 +3,7 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <mutex> 5#include <mutex>
6#include <QApplication>
6#include <QDialogButtonBox> 7#include <QDialogButtonBox>
7#include <QHeaderView> 8#include <QHeaderView>
8#include <QLabel> 9#include <QLabel>
@@ -16,6 +17,7 @@
16#include "core/hle/lock.h" 17#include "core/hle/lock.h"
17#include "yuzu/applets/qt_profile_select.h" 18#include "yuzu/applets/qt_profile_select.h"
18#include "yuzu/main.h" 19#include "yuzu/main.h"
20#include "yuzu/util/controller_navigation.h"
19 21
20namespace { 22namespace {
21QString FormatUserEntryText(const QString& username, Common::UUID uuid) { 23QString FormatUserEntryText(const QString& username, Common::UUID uuid) {
@@ -45,7 +47,7 @@ QPixmap GetIcon(Common::UUID uuid) {
45} 47}
46} // Anonymous namespace 48} // Anonymous namespace
47 49
48QtProfileSelectionDialog::QtProfileSelectionDialog(QWidget* parent) 50QtProfileSelectionDialog::QtProfileSelectionDialog(Core::HID::HIDCore& hid_core, QWidget* parent)
49 : QDialog(parent), profile_manager(std::make_unique<Service::Account::ProfileManager>()) { 51 : QDialog(parent), profile_manager(std::make_unique<Service::Account::ProfileManager>()) {
50 outer_layout = new QVBoxLayout; 52 outer_layout = new QVBoxLayout;
51 53
@@ -65,6 +67,7 @@ QtProfileSelectionDialog::QtProfileSelectionDialog(QWidget* parent)
65 tree_view = new QTreeView; 67 tree_view = new QTreeView;
66 item_model = new QStandardItemModel(tree_view); 68 item_model = new QStandardItemModel(tree_view);
67 tree_view->setModel(item_model); 69 tree_view->setModel(item_model);
70 controller_navigation = new ControllerNavigation(hid_core, this);
68 71
69 tree_view->setAlternatingRowColors(true); 72 tree_view->setAlternatingRowColors(true);
70 tree_view->setSelectionMode(QHeaderView::SingleSelection); 73 tree_view->setSelectionMode(QHeaderView::SingleSelection);
@@ -91,6 +94,14 @@ QtProfileSelectionDialog::QtProfileSelectionDialog(QWidget* parent)
91 scroll_area->setLayout(layout); 94 scroll_area->setLayout(layout);
92 95
93 connect(tree_view, &QTreeView::clicked, this, &QtProfileSelectionDialog::SelectUser); 96 connect(tree_view, &QTreeView::clicked, this, &QtProfileSelectionDialog::SelectUser);
97 connect(controller_navigation, &ControllerNavigation::TriggerKeyboardEvent,
98 [this](Qt::Key key) {
99 if (!this->isActiveWindow()) {
100 return;
101 }
102 QKeyEvent* event = new QKeyEvent(QEvent::KeyPress, key, Qt::NoModifier);
103 QCoreApplication::postEvent(tree_view, event);
104 });
94 105
95 const auto& profiles = profile_manager->GetAllUsers(); 106 const auto& profiles = profile_manager->GetAllUsers();
96 for (const auto& user : profiles) { 107 for (const auto& user : profiles) {
@@ -113,7 +124,9 @@ QtProfileSelectionDialog::QtProfileSelectionDialog(QWidget* parent)
113 resize(550, 400); 124 resize(550, 400);
114} 125}
115 126
116QtProfileSelectionDialog::~QtProfileSelectionDialog() = default; 127QtProfileSelectionDialog::~QtProfileSelectionDialog() {
128 controller_navigation->UnloadController();
129};
117 130
118int QtProfileSelectionDialog::exec() { 131int QtProfileSelectionDialog::exec() {
119 // Skip profile selection when there's only one. 132 // Skip profile selection when there's only one.
diff --git a/src/yuzu/applets/qt_profile_select.h b/src/yuzu/applets/qt_profile_select.h
index 4e9037488..56496ed31 100644
--- a/src/yuzu/applets/qt_profile_select.h
+++ b/src/yuzu/applets/qt_profile_select.h
@@ -11,6 +11,7 @@
11#include "core/frontend/applets/profile_select.h" 11#include "core/frontend/applets/profile_select.h"
12#include "core/hle/service/acc/profile_manager.h" 12#include "core/hle/service/acc/profile_manager.h"
13 13
14class ControllerNavigation;
14class GMainWindow; 15class GMainWindow;
15class QDialogButtonBox; 16class QDialogButtonBox;
16class QGraphicsScene; 17class QGraphicsScene;
@@ -20,11 +21,15 @@ class QStandardItem;
20class QStandardItemModel; 21class QStandardItemModel;
21class QVBoxLayout; 22class QVBoxLayout;
22 23
24namespace Core::HID {
25class HIDCore;
26} // namespace Core::HID
27
23class QtProfileSelectionDialog final : public QDialog { 28class QtProfileSelectionDialog final : public QDialog {
24 Q_OBJECT 29 Q_OBJECT
25 30
26public: 31public:
27 explicit QtProfileSelectionDialog(QWidget* parent); 32 explicit QtProfileSelectionDialog(Core::HID::HIDCore& hid_core, QWidget* parent);
28 ~QtProfileSelectionDialog() override; 33 ~QtProfileSelectionDialog() override;
29 34
30 int exec() override; 35 int exec() override;
@@ -51,6 +56,7 @@ private:
51 QDialogButtonBox* buttons; 56 QDialogButtonBox* buttons;
52 57
53 std::unique_ptr<Service::Account::ProfileManager> profile_manager; 58 std::unique_ptr<Service::Account::ProfileManager> profile_manager;
59 ControllerNavigation* controller_navigation = nullptr;
54}; 60};
55 61
56class QtProfileSelector final : public QObject, public Core::Frontend::ProfileSelectApplet { 62class QtProfileSelector final : public QObject, public Core::Frontend::ProfileSelectApplet {
diff --git a/src/yuzu/applets/qt_software_keyboard.cpp b/src/yuzu/applets/qt_software_keyboard.cpp
index a83a11a95..c3857fc98 100644
--- a/src/yuzu/applets/qt_software_keyboard.cpp
+++ b/src/yuzu/applets/qt_software_keyboard.cpp
@@ -10,7 +10,10 @@
10#include "common/settings.h" 10#include "common/settings.h"
11#include "common/string_util.h" 11#include "common/string_util.h"
12#include "core/core.h" 12#include "core/core.h"
13#include "core/frontend/input_interpreter.h" 13#include "core/hid/emulated_controller.h"
14#include "core/hid/hid_core.h"
15#include "core/hid/hid_types.h"
16#include "core/hid/input_interpreter.h"
14#include "ui_qt_software_keyboard.h" 17#include "ui_qt_software_keyboard.h"
15#include "yuzu/applets/qt_software_keyboard.h" 18#include "yuzu/applets/qt_software_keyboard.h"
16#include "yuzu/main.h" 19#include "yuzu/main.h"
@@ -472,11 +475,26 @@ void QtSoftwareKeyboardDialog::open() {
472 row = 0; 475 row = 0;
473 column = 0; 476 column = 0;
474 477
475 const auto* const curr_button = 478 switch (bottom_osk_index) {
476 keyboard_buttons[static_cast<int>(bottom_osk_index)][row][column]; 479 case BottomOSKIndex::LowerCase:
480 case BottomOSKIndex::UpperCase: {
481 const auto* const curr_button =
482 keyboard_buttons[static_cast<std::size_t>(bottom_osk_index)][row][column];
483
484 // This is a workaround for setFocus() randomly not showing focus in the UI
485 QCursor::setPos(curr_button->mapToGlobal(curr_button->rect().center()));
486 break;
487 }
488 case BottomOSKIndex::NumberPad: {
489 const auto* const curr_button = numberpad_buttons[row][column];
477 490
478 // This is a workaround for setFocus() randomly not showing focus in the UI 491 // This is a workaround for setFocus() randomly not showing focus in the UI
479 QCursor::setPos(curr_button->mapToGlobal(curr_button->rect().center())); 492 QCursor::setPos(curr_button->mapToGlobal(curr_button->rect().center()));
493 break;
494 }
495 default:
496 break;
497 }
480 498
481 StartInputThread(); 499 StartInputThread();
482} 500}
@@ -484,7 +502,7 @@ void QtSoftwareKeyboardDialog::open() {
484void QtSoftwareKeyboardDialog::reject() { 502void QtSoftwareKeyboardDialog::reject() {
485 // Pressing the ESC key in a dialog calls QDialog::reject(). 503 // Pressing the ESC key in a dialog calls QDialog::reject().
486 // We will override this behavior to the "Cancel" action on the software keyboard. 504 // We will override this behavior to the "Cancel" action on the software keyboard.
487 TranslateButtonPress(HIDButton::X); 505 TranslateButtonPress(Core::HID::NpadButton::X);
488} 506}
489 507
490void QtSoftwareKeyboardDialog::keyPressEvent(QKeyEvent* event) { 508void QtSoftwareKeyboardDialog::keyPressEvent(QKeyEvent* event) {
@@ -722,7 +740,7 @@ void QtSoftwareKeyboardDialog::SetTextDrawType() {
722 740
723 connect( 741 connect(
724 ui->line_edit_osk, &QLineEdit::returnPressed, this, 742 ui->line_edit_osk, &QLineEdit::returnPressed, this,
725 [this] { TranslateButtonPress(HIDButton::Plus); }, Qt::QueuedConnection); 743 [this] { TranslateButtonPress(Core::HID::NpadButton::Plus); }, Qt::QueuedConnection);
726 744
727 ui->line_edit_osk->setPlaceholderText( 745 ui->line_edit_osk->setPlaceholderText(
728 QString::fromStdU16String(initialize_parameters.guide_text)); 746 QString::fromStdU16String(initialize_parameters.guide_text));
@@ -795,9 +813,10 @@ void QtSoftwareKeyboardDialog::SetTextDrawType() {
795} 813}
796 814
797void QtSoftwareKeyboardDialog::SetControllerImage() { 815void QtSoftwareKeyboardDialog::SetControllerImage() {
798 const auto controller_type = Settings::values.players.GetValue()[8].connected 816 const auto* handheld = system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Handheld);
799 ? Settings::values.players.GetValue()[8].controller_type 817 const auto* player_1 = system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1);
800 : Settings::values.players.GetValue()[0].controller_type; 818 const auto controller_type =
819 handheld->IsConnected() ? handheld->GetNpadStyleIndex() : player_1->GetNpadStyleIndex();
801 820
802 const QString theme = [] { 821 const QString theme = [] {
803 if (QIcon::themeName().contains(QStringLiteral("dark")) || 822 if (QIcon::themeName().contains(QStringLiteral("dark")) ||
@@ -809,8 +828,8 @@ void QtSoftwareKeyboardDialog::SetControllerImage() {
809 }(); 828 }();
810 829
811 switch (controller_type) { 830 switch (controller_type) {
812 case Settings::ControllerType::ProController: 831 case Core::HID::NpadStyleIndex::ProController:
813 case Settings::ControllerType::GameCube: 832 case Core::HID::NpadStyleIndex::GameCube:
814 ui->icon_controller->setStyleSheet( 833 ui->icon_controller->setStyleSheet(
815 QStringLiteral("image: url(:/overlay/controller_pro%1.png);").arg(theme)); 834 QStringLiteral("image: url(:/overlay/controller_pro%1.png);").arg(theme));
816 ui->icon_controller_shift->setStyleSheet( 835 ui->icon_controller_shift->setStyleSheet(
@@ -818,7 +837,7 @@ void QtSoftwareKeyboardDialog::SetControllerImage() {
818 ui->icon_controller_num->setStyleSheet( 837 ui->icon_controller_num->setStyleSheet(
819 QStringLiteral("image: url(:/overlay/controller_pro%1.png);").arg(theme)); 838 QStringLiteral("image: url(:/overlay/controller_pro%1.png);").arg(theme));
820 break; 839 break;
821 case Settings::ControllerType::DualJoyconDetached: 840 case Core::HID::NpadStyleIndex::JoyconDual:
822 ui->icon_controller->setStyleSheet( 841 ui->icon_controller->setStyleSheet(
823 QStringLiteral("image: url(:/overlay/controller_dual_joycon%1.png);").arg(theme)); 842 QStringLiteral("image: url(:/overlay/controller_dual_joycon%1.png);").arg(theme));
824 ui->icon_controller_shift->setStyleSheet( 843 ui->icon_controller_shift->setStyleSheet(
@@ -826,7 +845,7 @@ void QtSoftwareKeyboardDialog::SetControllerImage() {
826 ui->icon_controller_num->setStyleSheet( 845 ui->icon_controller_num->setStyleSheet(
827 QStringLiteral("image: url(:/overlay/controller_dual_joycon%1.png);").arg(theme)); 846 QStringLiteral("image: url(:/overlay/controller_dual_joycon%1.png);").arg(theme));
828 break; 847 break;
829 case Settings::ControllerType::LeftJoycon: 848 case Core::HID::NpadStyleIndex::JoyconLeft:
830 ui->icon_controller->setStyleSheet( 849 ui->icon_controller->setStyleSheet(
831 QStringLiteral("image: url(:/overlay/controller_single_joycon_left%1.png);") 850 QStringLiteral("image: url(:/overlay/controller_single_joycon_left%1.png);")
832 .arg(theme)); 851 .arg(theme));
@@ -837,7 +856,7 @@ void QtSoftwareKeyboardDialog::SetControllerImage() {
837 QStringLiteral("image: url(:/overlay/controller_single_joycon_left%1.png);") 856 QStringLiteral("image: url(:/overlay/controller_single_joycon_left%1.png);")
838 .arg(theme)); 857 .arg(theme));
839 break; 858 break;
840 case Settings::ControllerType::RightJoycon: 859 case Core::HID::NpadStyleIndex::JoyconRight:
841 ui->icon_controller->setStyleSheet( 860 ui->icon_controller->setStyleSheet(
842 QStringLiteral("image: url(:/overlay/controller_single_joycon_right%1.png);") 861 QStringLiteral("image: url(:/overlay/controller_single_joycon_right%1.png);")
843 .arg(theme)); 862 .arg(theme));
@@ -848,7 +867,7 @@ void QtSoftwareKeyboardDialog::SetControllerImage() {
848 QStringLiteral("image: url(:/overlay/controller_single_joycon_right%1.png);") 867 QStringLiteral("image: url(:/overlay/controller_single_joycon_right%1.png);")
849 .arg(theme)); 868 .arg(theme));
850 break; 869 break;
851 case Settings::ControllerType::Handheld: 870 case Core::HID::NpadStyleIndex::Handheld:
852 ui->icon_controller->setStyleSheet( 871 ui->icon_controller->setStyleSheet(
853 QStringLiteral("image: url(:/overlay/controller_handheld%1.png);").arg(theme)); 872 QStringLiteral("image: url(:/overlay/controller_handheld%1.png);").arg(theme));
854 ui->icon_controller_shift->setStyleSheet( 873 ui->icon_controller_shift->setStyleSheet(
@@ -1208,9 +1227,9 @@ void QtSoftwareKeyboardDialog::SetupMouseHover() {
1208 } 1227 }
1209} 1228}
1210 1229
1211template <HIDButton... T> 1230template <Core::HID::NpadButton... T>
1212void QtSoftwareKeyboardDialog::HandleButtonPressedOnce() { 1231void QtSoftwareKeyboardDialog::HandleButtonPressedOnce() {
1213 const auto f = [this](HIDButton button) { 1232 const auto f = [this](Core::HID::NpadButton button) {
1214 if (input_interpreter->IsButtonPressedOnce(button)) { 1233 if (input_interpreter->IsButtonPressedOnce(button)) {
1215 TranslateButtonPress(button); 1234 TranslateButtonPress(button);
1216 } 1235 }
@@ -1219,9 +1238,9 @@ void QtSoftwareKeyboardDialog::HandleButtonPressedOnce() {
1219 (f(T), ...); 1238 (f(T), ...);
1220} 1239}
1221 1240
1222template <HIDButton... T> 1241template <Core::HID::NpadButton... T>
1223void QtSoftwareKeyboardDialog::HandleButtonHold() { 1242void QtSoftwareKeyboardDialog::HandleButtonHold() {
1224 const auto f = [this](HIDButton button) { 1243 const auto f = [this](Core::HID::NpadButton button) {
1225 if (input_interpreter->IsButtonHeld(button)) { 1244 if (input_interpreter->IsButtonHeld(button)) {
1226 TranslateButtonPress(button); 1245 TranslateButtonPress(button);
1227 } 1246 }
@@ -1230,9 +1249,9 @@ void QtSoftwareKeyboardDialog::HandleButtonHold() {
1230 (f(T), ...); 1249 (f(T), ...);
1231} 1250}
1232 1251
1233void QtSoftwareKeyboardDialog::TranslateButtonPress(HIDButton button) { 1252void QtSoftwareKeyboardDialog::TranslateButtonPress(Core::HID::NpadButton button) {
1234 switch (button) { 1253 switch (button) {
1235 case HIDButton::A: 1254 case Core::HID::NpadButton::A:
1236 switch (bottom_osk_index) { 1255 switch (bottom_osk_index) {
1237 case BottomOSKIndex::LowerCase: 1256 case BottomOSKIndex::LowerCase:
1238 case BottomOSKIndex::UpperCase: 1257 case BottomOSKIndex::UpperCase:
@@ -1245,7 +1264,7 @@ void QtSoftwareKeyboardDialog::TranslateButtonPress(HIDButton button) {
1245 break; 1264 break;
1246 } 1265 }
1247 break; 1266 break;
1248 case HIDButton::B: 1267 case Core::HID::NpadButton::B:
1249 switch (bottom_osk_index) { 1268 switch (bottom_osk_index) {
1250 case BottomOSKIndex::LowerCase: 1269 case BottomOSKIndex::LowerCase:
1251 ui->button_backspace->click(); 1270 ui->button_backspace->click();
@@ -1260,7 +1279,7 @@ void QtSoftwareKeyboardDialog::TranslateButtonPress(HIDButton button) {
1260 break; 1279 break;
1261 } 1280 }
1262 break; 1281 break;
1263 case HIDButton::X: 1282 case Core::HID::NpadButton::X:
1264 if (is_inline) { 1283 if (is_inline) {
1265 emit SubmitInlineText(SwkbdReplyType::DecidedCancel, current_text, cursor_position); 1284 emit SubmitInlineText(SwkbdReplyType::DecidedCancel, current_text, cursor_position);
1266 } else { 1285 } else {
@@ -1271,7 +1290,7 @@ void QtSoftwareKeyboardDialog::TranslateButtonPress(HIDButton button) {
1271 emit SubmitNormalText(SwkbdResult::Cancel, std::move(text)); 1290 emit SubmitNormalText(SwkbdResult::Cancel, std::move(text));
1272 } 1291 }
1273 break; 1292 break;
1274 case HIDButton::Y: 1293 case Core::HID::NpadButton::Y:
1275 switch (bottom_osk_index) { 1294 switch (bottom_osk_index) {
1276 case BottomOSKIndex::LowerCase: 1295 case BottomOSKIndex::LowerCase:
1277 ui->button_space->click(); 1296 ui->button_space->click();
@@ -1284,8 +1303,8 @@ void QtSoftwareKeyboardDialog::TranslateButtonPress(HIDButton button) {
1284 break; 1303 break;
1285 } 1304 }
1286 break; 1305 break;
1287 case HIDButton::LStick: 1306 case Core::HID::NpadButton::StickL:
1288 case HIDButton::RStick: 1307 case Core::HID::NpadButton::StickR:
1289 switch (bottom_osk_index) { 1308 switch (bottom_osk_index) {
1290 case BottomOSKIndex::LowerCase: 1309 case BottomOSKIndex::LowerCase:
1291 ui->button_shift->click(); 1310 ui->button_shift->click();
@@ -1298,13 +1317,13 @@ void QtSoftwareKeyboardDialog::TranslateButtonPress(HIDButton button) {
1298 break; 1317 break;
1299 } 1318 }
1300 break; 1319 break;
1301 case HIDButton::L: 1320 case Core::HID::NpadButton::L:
1302 MoveTextCursorDirection(Direction::Left); 1321 MoveTextCursorDirection(Direction::Left);
1303 break; 1322 break;
1304 case HIDButton::R: 1323 case Core::HID::NpadButton::R:
1305 MoveTextCursorDirection(Direction::Right); 1324 MoveTextCursorDirection(Direction::Right);
1306 break; 1325 break;
1307 case HIDButton::Plus: 1326 case Core::HID::NpadButton::Plus:
1308 switch (bottom_osk_index) { 1327 switch (bottom_osk_index) {
1309 case BottomOSKIndex::LowerCase: 1328 case BottomOSKIndex::LowerCase:
1310 ui->button_ok->click(); 1329 ui->button_ok->click();
@@ -1319,24 +1338,24 @@ void QtSoftwareKeyboardDialog::TranslateButtonPress(HIDButton button) {
1319 break; 1338 break;
1320 } 1339 }
1321 break; 1340 break;
1322 case HIDButton::DLeft: 1341 case Core::HID::NpadButton::Left:
1323 case HIDButton::LStickLeft: 1342 case Core::HID::NpadButton::StickLLeft:
1324 case HIDButton::RStickLeft: 1343 case Core::HID::NpadButton::StickRLeft:
1325 MoveButtonDirection(Direction::Left); 1344 MoveButtonDirection(Direction::Left);
1326 break; 1345 break;
1327 case HIDButton::DUp: 1346 case Core::HID::NpadButton::Up:
1328 case HIDButton::LStickUp: 1347 case Core::HID::NpadButton::StickLUp:
1329 case HIDButton::RStickUp: 1348 case Core::HID::NpadButton::StickRUp:
1330 MoveButtonDirection(Direction::Up); 1349 MoveButtonDirection(Direction::Up);
1331 break; 1350 break;
1332 case HIDButton::DRight: 1351 case Core::HID::NpadButton::Right:
1333 case HIDButton::LStickRight: 1352 case Core::HID::NpadButton::StickLRight:
1334 case HIDButton::RStickRight: 1353 case Core::HID::NpadButton::StickRRight:
1335 MoveButtonDirection(Direction::Right); 1354 MoveButtonDirection(Direction::Right);
1336 break; 1355 break;
1337 case HIDButton::DDown: 1356 case Core::HID::NpadButton::Down:
1338 case HIDButton::LStickDown: 1357 case Core::HID::NpadButton::StickLDown:
1339 case HIDButton::RStickDown: 1358 case Core::HID::NpadButton::StickRDown:
1340 MoveButtonDirection(Direction::Down); 1359 MoveButtonDirection(Direction::Down);
1341 break; 1360 break;
1342 default: 1361 default:
@@ -1467,19 +1486,25 @@ void QtSoftwareKeyboardDialog::InputThread() {
1467 while (input_thread_running) { 1486 while (input_thread_running) {
1468 input_interpreter->PollInput(); 1487 input_interpreter->PollInput();
1469 1488
1470 HandleButtonPressedOnce<HIDButton::A, HIDButton::B, HIDButton::X, HIDButton::Y, 1489 HandleButtonPressedOnce<
1471 HIDButton::LStick, HIDButton::RStick, HIDButton::L, HIDButton::R, 1490 Core::HID::NpadButton::A, Core::HID::NpadButton::B, Core::HID::NpadButton::X,
1472 HIDButton::Plus, HIDButton::DLeft, HIDButton::DUp, 1491 Core::HID::NpadButton::Y, Core::HID::NpadButton::StickL, Core::HID::NpadButton::StickR,
1473 HIDButton::DRight, HIDButton::DDown, HIDButton::LStickLeft, 1492 Core::HID::NpadButton::L, Core::HID::NpadButton::R, Core::HID::NpadButton::Plus,
1474 HIDButton::LStickUp, HIDButton::LStickRight, HIDButton::LStickDown, 1493 Core::HID::NpadButton::Left, Core::HID::NpadButton::Up, Core::HID::NpadButton::Right,
1475 HIDButton::RStickLeft, HIDButton::RStickUp, HIDButton::RStickRight, 1494 Core::HID::NpadButton::Down, Core::HID::NpadButton::StickLLeft,
1476 HIDButton::RStickDown>(); 1495 Core::HID::NpadButton::StickLUp, Core::HID::NpadButton::StickLRight,
1477 1496 Core::HID::NpadButton::StickLDown, Core::HID::NpadButton::StickRLeft,
1478 HandleButtonHold<HIDButton::B, HIDButton::L, HIDButton::R, HIDButton::DLeft, HIDButton::DUp, 1497 Core::HID::NpadButton::StickRUp, Core::HID::NpadButton::StickRRight,
1479 HIDButton::DRight, HIDButton::DDown, HIDButton::LStickLeft, 1498 Core::HID::NpadButton::StickRDown>();
1480 HIDButton::LStickUp, HIDButton::LStickRight, HIDButton::LStickDown, 1499
1481 HIDButton::RStickLeft, HIDButton::RStickUp, HIDButton::RStickRight, 1500 HandleButtonHold<Core::HID::NpadButton::B, Core::HID::NpadButton::L,
1482 HIDButton::RStickDown>(); 1501 Core::HID::NpadButton::R, Core::HID::NpadButton::Left,
1502 Core::HID::NpadButton::Up, Core::HID::NpadButton::Right,
1503 Core::HID::NpadButton::Down, Core::HID::NpadButton::StickLLeft,
1504 Core::HID::NpadButton::StickLUp, Core::HID::NpadButton::StickLRight,
1505 Core::HID::NpadButton::StickLDown, Core::HID::NpadButton::StickRLeft,
1506 Core::HID::NpadButton::StickRUp, Core::HID::NpadButton::StickRRight,
1507 Core::HID::NpadButton::StickRDown>();
1483 1508
1484 std::this_thread::sleep_for(std::chrono::milliseconds(50)); 1509 std::this_thread::sleep_for(std::chrono::milliseconds(50));
1485 } 1510 }
diff --git a/src/yuzu/applets/qt_software_keyboard.h b/src/yuzu/applets/qt_software_keyboard.h
index 592d9c085..b030cdcf7 100644
--- a/src/yuzu/applets/qt_software_keyboard.h
+++ b/src/yuzu/applets/qt_software_keyboard.h
@@ -14,14 +14,16 @@
14 14
15#include "core/frontend/applets/software_keyboard.h" 15#include "core/frontend/applets/software_keyboard.h"
16 16
17enum class HIDButton : u8;
18
19class InputInterpreter; 17class InputInterpreter;
20 18
21namespace Core { 19namespace Core {
22class System; 20class System;
23} 21}
24 22
23namespace Core::HID {
24enum class NpadButton : u64;
25}
26
25namespace Ui { 27namespace Ui {
26class QtSoftwareKeyboardDialog; 28class QtSoftwareKeyboardDialog;
27} 29}
@@ -146,7 +148,7 @@ private:
146 * 148 *
147 * @tparam HIDButton The list of buttons that can be converted into keyboard input. 149 * @tparam HIDButton The list of buttons that can be converted into keyboard input.
148 */ 150 */
149 template <HIDButton... T> 151 template <Core::HID::NpadButton... T>
150 void HandleButtonPressedOnce(); 152 void HandleButtonPressedOnce();
151 153
152 /** 154 /**
@@ -154,7 +156,7 @@ private:
154 * 156 *
155 * @tparam HIDButton The list of buttons that can be converted into keyboard input. 157 * @tparam HIDButton The list of buttons that can be converted into keyboard input.
156 */ 158 */
157 template <HIDButton... T> 159 template <Core::HID::NpadButton... T>
158 void HandleButtonHold(); 160 void HandleButtonHold();
159 161
160 /** 162 /**
@@ -162,7 +164,7 @@ private:
162 * 164 *
163 * @param button The button press to process. 165 * @param button The button press to process.
164 */ 166 */
165 void TranslateButtonPress(HIDButton button); 167 void TranslateButtonPress(Core::HID::NpadButton button);
166 168
167 /** 169 /**
168 * Moves the focus of a button in a certain direction. 170 * Moves the focus of a button in a certain direction.
diff --git a/src/yuzu/applets/qt_web_browser.cpp b/src/yuzu/applets/qt_web_browser.cpp
index da8c6882a..cb3c5d826 100644
--- a/src/yuzu/applets/qt_web_browser.cpp
+++ b/src/yuzu/applets/qt_web_browser.cpp
@@ -14,9 +14,11 @@
14#endif 14#endif
15 15
16#include "common/fs/path_util.h" 16#include "common/fs/path_util.h"
17#include "common/param_package.h"
17#include "core/core.h" 18#include "core/core.h"
18#include "core/frontend/input_interpreter.h" 19#include "core/hid/hid_types.h"
19#include "input_common/keyboard.h" 20#include "core/hid/input_interpreter.h"
21#include "input_common/drivers/keyboard.h"
20#include "input_common/main.h" 22#include "input_common/main.h"
21#include "yuzu/applets/qt_web_browser.h" 23#include "yuzu/applets/qt_web_browser.h"
22#include "yuzu/applets/qt_web_browser_scripts.h" 24#include "yuzu/applets/qt_web_browser_scripts.h"
@@ -27,19 +29,19 @@
27 29
28namespace { 30namespace {
29 31
30constexpr int HIDButtonToKey(HIDButton button) { 32constexpr int HIDButtonToKey(Core::HID::NpadButton button) {
31 switch (button) { 33 switch (button) {
32 case HIDButton::DLeft: 34 case Core::HID::NpadButton::Left:
33 case HIDButton::LStickLeft: 35 case Core::HID::NpadButton::StickLLeft:
34 return Qt::Key_Left; 36 return Qt::Key_Left;
35 case HIDButton::DUp: 37 case Core::HID::NpadButton::Up:
36 case HIDButton::LStickUp: 38 case Core::HID::NpadButton::StickLUp:
37 return Qt::Key_Up; 39 return Qt::Key_Up;
38 case HIDButton::DRight: 40 case Core::HID::NpadButton::Right:
39 case HIDButton::LStickRight: 41 case Core::HID::NpadButton::StickLRight:
40 return Qt::Key_Right; 42 return Qt::Key_Right;
41 case HIDButton::DDown: 43 case Core::HID::NpadButton::Down:
42 case HIDButton::LStickDown: 44 case Core::HID::NpadButton::StickLDown:
43 return Qt::Key_Down; 45 return Qt::Key_Down;
44 default: 46 default:
45 return 0; 47 return 0;
@@ -208,25 +210,25 @@ void QtNXWebEngineView::keyReleaseEvent(QKeyEvent* event) {
208 } 210 }
209} 211}
210 212
211template <HIDButton... T> 213template <Core::HID::NpadButton... T>
212void QtNXWebEngineView::HandleWindowFooterButtonPressedOnce() { 214void QtNXWebEngineView::HandleWindowFooterButtonPressedOnce() {
213 const auto f = [this](HIDButton button) { 215 const auto f = [this](Core::HID::NpadButton button) {
214 if (input_interpreter->IsButtonPressedOnce(button)) { 216 if (input_interpreter->IsButtonPressedOnce(button)) {
215 page()->runJavaScript( 217 page()->runJavaScript(
216 QStringLiteral("yuzu_key_callbacks[%1] == null;").arg(static_cast<u8>(button)), 218 QStringLiteral("yuzu_key_callbacks[%1] == null;").arg(static_cast<u8>(button)),
217 [this, button](const QVariant& variant) { 219 [this, button](const QVariant& variant) {
218 if (variant.toBool()) { 220 if (variant.toBool()) {
219 switch (button) { 221 switch (button) {
220 case HIDButton::A: 222 case Core::HID::NpadButton::A:
221 SendMultipleKeyPressEvents<Qt::Key_A, Qt::Key_Space, Qt::Key_Return>(); 223 SendMultipleKeyPressEvents<Qt::Key_A, Qt::Key_Space, Qt::Key_Return>();
222 break; 224 break;
223 case HIDButton::B: 225 case Core::HID::NpadButton::B:
224 SendKeyPressEvent(Qt::Key_B); 226 SendKeyPressEvent(Qt::Key_B);
225 break; 227 break;
226 case HIDButton::X: 228 case Core::HID::NpadButton::X:
227 SendKeyPressEvent(Qt::Key_X); 229 SendKeyPressEvent(Qt::Key_X);
228 break; 230 break;
229 case HIDButton::Y: 231 case Core::HID::NpadButton::Y:
230 SendKeyPressEvent(Qt::Key_Y); 232 SendKeyPressEvent(Qt::Key_Y);
231 break; 233 break;
232 default: 234 default:
@@ -244,9 +246,9 @@ void QtNXWebEngineView::HandleWindowFooterButtonPressedOnce() {
244 (f(T), ...); 246 (f(T), ...);
245} 247}
246 248
247template <HIDButton... T> 249template <Core::HID::NpadButton... T>
248void QtNXWebEngineView::HandleWindowKeyButtonPressedOnce() { 250void QtNXWebEngineView::HandleWindowKeyButtonPressedOnce() {
249 const auto f = [this](HIDButton button) { 251 const auto f = [this](Core::HID::NpadButton button) {
250 if (input_interpreter->IsButtonPressedOnce(button)) { 252 if (input_interpreter->IsButtonPressedOnce(button)) {
251 SendKeyPressEvent(HIDButtonToKey(button)); 253 SendKeyPressEvent(HIDButtonToKey(button));
252 } 254 }
@@ -255,9 +257,9 @@ void QtNXWebEngineView::HandleWindowKeyButtonPressedOnce() {
255 (f(T), ...); 257 (f(T), ...);
256} 258}
257 259
258template <HIDButton... T> 260template <Core::HID::NpadButton... T>
259void QtNXWebEngineView::HandleWindowKeyButtonHold() { 261void QtNXWebEngineView::HandleWindowKeyButtonHold() {
260 const auto f = [this](HIDButton button) { 262 const auto f = [this](Core::HID::NpadButton button) {
261 if (input_interpreter->IsButtonHeld(button)) { 263 if (input_interpreter->IsButtonHeld(button)) {
262 SendKeyPressEvent(HIDButtonToKey(button)); 264 SendKeyPressEvent(HIDButtonToKey(button));
263 } 265 }
@@ -308,17 +310,21 @@ void QtNXWebEngineView::InputThread() {
308 while (input_thread_running) { 310 while (input_thread_running) {
309 input_interpreter->PollInput(); 311 input_interpreter->PollInput();
310 312
311 HandleWindowFooterButtonPressedOnce<HIDButton::A, HIDButton::B, HIDButton::X, HIDButton::Y, 313 HandleWindowFooterButtonPressedOnce<Core::HID::NpadButton::A, Core::HID::NpadButton::B,
312 HIDButton::L, HIDButton::R>(); 314 Core::HID::NpadButton::X, Core::HID::NpadButton::Y,
313 315 Core::HID::NpadButton::L, Core::HID::NpadButton::R>();
314 HandleWindowKeyButtonPressedOnce<HIDButton::DLeft, HIDButton::DUp, HIDButton::DRight, 316
315 HIDButton::DDown, HIDButton::LStickLeft, 317 HandleWindowKeyButtonPressedOnce<
316 HIDButton::LStickUp, HIDButton::LStickRight, 318 Core::HID::NpadButton::Left, Core::HID::NpadButton::Up, Core::HID::NpadButton::Right,
317 HIDButton::LStickDown>(); 319 Core::HID::NpadButton::Down, Core::HID::NpadButton::StickLLeft,
318 320 Core::HID::NpadButton::StickLUp, Core::HID::NpadButton::StickLRight,
319 HandleWindowKeyButtonHold<HIDButton::DLeft, HIDButton::DUp, HIDButton::DRight, 321 Core::HID::NpadButton::StickLDown>();
320 HIDButton::DDown, HIDButton::LStickLeft, HIDButton::LStickUp, 322
321 HIDButton::LStickRight, HIDButton::LStickDown>(); 323 HandleWindowKeyButtonHold<
324 Core::HID::NpadButton::Left, Core::HID::NpadButton::Up, Core::HID::NpadButton::Right,
325 Core::HID::NpadButton::Down, Core::HID::NpadButton::StickLLeft,
326 Core::HID::NpadButton::StickLUp, Core::HID::NpadButton::StickLRight,
327 Core::HID::NpadButton::StickLDown>();
322 328
323 std::this_thread::sleep_for(std::chrono::milliseconds(50)); 329 std::this_thread::sleep_for(std::chrono::milliseconds(50));
324 } 330 }
diff --git a/src/yuzu/applets/qt_web_browser.h b/src/yuzu/applets/qt_web_browser.h
index 7e9f703fc..fa18aecac 100644
--- a/src/yuzu/applets/qt_web_browser.h
+++ b/src/yuzu/applets/qt_web_browser.h
@@ -16,8 +16,6 @@
16 16
17#include "core/frontend/applets/web_browser.h" 17#include "core/frontend/applets/web_browser.h"
18 18
19enum class HIDButton : u8;
20
21class GMainWindow; 19class GMainWindow;
22class InputInterpreter; 20class InputInterpreter;
23class UrlRequestInterceptor; 21class UrlRequestInterceptor;
@@ -26,6 +24,10 @@ namespace Core {
26class System; 24class System;
27} 25}
28 26
27namespace Core::HID {
28enum class NpadButton : u64;
29}
30
29namespace InputCommon { 31namespace InputCommon {
30class InputSubsystem; 32class InputSubsystem;
31} 33}
@@ -114,7 +116,7 @@ private:
114 * 116 *
115 * @tparam HIDButton The list of buttons contained in yuzu_key_callbacks 117 * @tparam HIDButton The list of buttons contained in yuzu_key_callbacks
116 */ 118 */
117 template <HIDButton... T> 119 template <Core::HID::NpadButton... T>
118 void HandleWindowFooterButtonPressedOnce(); 120 void HandleWindowFooterButtonPressedOnce();
119 121
120 /** 122 /**
@@ -123,7 +125,7 @@ private:
123 * 125 *
124 * @tparam HIDButton The list of buttons that can be converted into keyboard input. 126 * @tparam HIDButton The list of buttons that can be converted into keyboard input.
125 */ 127 */
126 template <HIDButton... T> 128 template <Core::HID::NpadButton... T>
127 void HandleWindowKeyButtonPressedOnce(); 129 void HandleWindowKeyButtonPressedOnce();
128 130
129 /** 131 /**
@@ -132,7 +134,7 @@ private:
132 * 134 *
133 * @tparam HIDButton The list of buttons that can be converted into keyboard input. 135 * @tparam HIDButton The list of buttons that can be converted into keyboard input.
134 */ 136 */
135 template <HIDButton... T> 137 template <Core::HID::NpadButton... T>
136 void HandleWindowKeyButtonHold(); 138 void HandleWindowKeyButtonHold();
137 139
138 /** 140 /**
diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp
index 976acd176..114f17c06 100644
--- a/src/yuzu/bootmanager.cpp
+++ b/src/yuzu/bootmanager.cpp
@@ -32,10 +32,11 @@
32#include "common/settings.h" 32#include "common/settings.h"
33#include "core/core.h" 33#include "core/core.h"
34#include "core/frontend/framebuffer_layout.h" 34#include "core/frontend/framebuffer_layout.h"
35#include "input_common/keyboard.h" 35#include "input_common/drivers/keyboard.h"
36#include "input_common/drivers/mouse.h"
37#include "input_common/drivers/tas_input.h"
38#include "input_common/drivers/touch_screen.h"
36#include "input_common/main.h" 39#include "input_common/main.h"
37#include "input_common/mouse/mouse_input.h"
38#include "input_common/tas/tas_input.h"
39#include "video_core/renderer_base.h" 40#include "video_core/renderer_base.h"
40#include "video_core/video_core.h" 41#include "video_core/video_core.h"
41#include "yuzu/bootmanager.h" 42#include "yuzu/bootmanager.h"
@@ -296,13 +297,13 @@ GRenderWindow::GRenderWindow(GMainWindow* parent, EmuThread* emu_thread_,
296 layout->setContentsMargins(0, 0, 0, 0); 297 layout->setContentsMargins(0, 0, 0, 0);
297 setLayout(layout); 298 setLayout(layout);
298 input_subsystem->Initialize(); 299 input_subsystem->Initialize();
299
300 this->setMouseTracking(true); 300 this->setMouseTracking(true);
301 301
302 connect(this, &GRenderWindow::FirstFrameDisplayed, parent, &GMainWindow::OnLoadComplete); 302 connect(this, &GRenderWindow::FirstFrameDisplayed, parent, &GMainWindow::OnLoadComplete);
303 connect(this, &GRenderWindow::ExecuteProgramSignal, parent, &GMainWindow::OnExecuteProgram, 303 connect(this, &GRenderWindow::ExecuteProgramSignal, parent, &GMainWindow::OnExecuteProgram,
304 Qt::QueuedConnection); 304 Qt::QueuedConnection);
305 connect(this, &GRenderWindow::ExitSignal, parent, &GMainWindow::OnExit, Qt::QueuedConnection); 305 connect(this, &GRenderWindow::ExitSignal, parent, &GMainWindow::OnExit, Qt::QueuedConnection);
306 connect(this, &GRenderWindow::TasPlaybackStateChanged, parent, &GMainWindow::OnTasStateChanged);
306} 307}
307 308
308void GRenderWindow::ExecuteProgram(std::size_t program_index) { 309void GRenderWindow::ExecuteProgram(std::size_t program_index) {
@@ -319,10 +320,19 @@ GRenderWindow::~GRenderWindow() {
319 320
320void GRenderWindow::OnFrameDisplayed() { 321void GRenderWindow::OnFrameDisplayed() {
321 input_subsystem->GetTas()->UpdateThread(); 322 input_subsystem->GetTas()->UpdateThread();
323 const InputCommon::TasInput::TasState new_tas_state =
324 std::get<0>(input_subsystem->GetTas()->GetStatus());
325
322 if (!first_frame) { 326 if (!first_frame) {
327 last_tas_state = new_tas_state;
323 first_frame = true; 328 first_frame = true;
324 emit FirstFrameDisplayed(); 329 emit FirstFrameDisplayed();
325 } 330 }
331
332 if (new_tas_state != last_tas_state) {
333 last_tas_state = new_tas_state;
334 emit TasPlaybackStateChanged();
335 }
326} 336}
327 337
328bool GRenderWindow::IsShown() const { 338bool GRenderWindow::IsShown() const {
@@ -383,34 +393,329 @@ void GRenderWindow::closeEvent(QCloseEvent* event) {
383 QWidget::closeEvent(event); 393 QWidget::closeEvent(event);
384} 394}
385 395
396int GRenderWindow::QtKeyToSwitchKey(Qt::Key qt_key) {
397 switch (qt_key) {
398 case Qt::Key_A:
399 return Settings::NativeKeyboard::A;
400 case Qt::Key_B:
401 return Settings::NativeKeyboard::B;
402 case Qt::Key_C:
403 return Settings::NativeKeyboard::C;
404 case Qt::Key_D:
405 return Settings::NativeKeyboard::D;
406 case Qt::Key_E:
407 return Settings::NativeKeyboard::E;
408 case Qt::Key_F:
409 return Settings::NativeKeyboard::F;
410 case Qt::Key_G:
411 return Settings::NativeKeyboard::G;
412 case Qt::Key_H:
413 return Settings::NativeKeyboard::H;
414 case Qt::Key_I:
415 return Settings::NativeKeyboard::I;
416 case Qt::Key_J:
417 return Settings::NativeKeyboard::J;
418 case Qt::Key_K:
419 return Settings::NativeKeyboard::K;
420 case Qt::Key_L:
421 return Settings::NativeKeyboard::L;
422 case Qt::Key_M:
423 return Settings::NativeKeyboard::M;
424 case Qt::Key_N:
425 return Settings::NativeKeyboard::N;
426 case Qt::Key_O:
427 return Settings::NativeKeyboard::O;
428 case Qt::Key_P:
429 return Settings::NativeKeyboard::P;
430 case Qt::Key_Q:
431 return Settings::NativeKeyboard::Q;
432 case Qt::Key_R:
433 return Settings::NativeKeyboard::R;
434 case Qt::Key_S:
435 return Settings::NativeKeyboard::S;
436 case Qt::Key_T:
437 return Settings::NativeKeyboard::T;
438 case Qt::Key_U:
439 return Settings::NativeKeyboard::U;
440 case Qt::Key_V:
441 return Settings::NativeKeyboard::V;
442 case Qt::Key_W:
443 return Settings::NativeKeyboard::W;
444 case Qt::Key_X:
445 return Settings::NativeKeyboard::X;
446 case Qt::Key_Y:
447 return Settings::NativeKeyboard::Y;
448 case Qt::Key_Z:
449 return Settings::NativeKeyboard::Z;
450 case Qt::Key_1:
451 return Settings::NativeKeyboard::N1;
452 case Qt::Key_2:
453 return Settings::NativeKeyboard::N2;
454 case Qt::Key_3:
455 return Settings::NativeKeyboard::N3;
456 case Qt::Key_4:
457 return Settings::NativeKeyboard::N4;
458 case Qt::Key_5:
459 return Settings::NativeKeyboard::N5;
460 case Qt::Key_6:
461 return Settings::NativeKeyboard::N6;
462 case Qt::Key_7:
463 return Settings::NativeKeyboard::N7;
464 case Qt::Key_8:
465 return Settings::NativeKeyboard::N8;
466 case Qt::Key_9:
467 return Settings::NativeKeyboard::N9;
468 case Qt::Key_0:
469 return Settings::NativeKeyboard::N0;
470 case Qt::Key_Return:
471 return Settings::NativeKeyboard::Return;
472 case Qt::Key_Escape:
473 return Settings::NativeKeyboard::Escape;
474 case Qt::Key_Backspace:
475 return Settings::NativeKeyboard::Backspace;
476 case Qt::Key_Tab:
477 return Settings::NativeKeyboard::Tab;
478 case Qt::Key_Space:
479 return Settings::NativeKeyboard::Space;
480 case Qt::Key_Minus:
481 return Settings::NativeKeyboard::Minus;
482 case Qt::Key_Plus:
483 case Qt::Key_questiondown:
484 return Settings::NativeKeyboard::Plus;
485 case Qt::Key_BracketLeft:
486 case Qt::Key_BraceLeft:
487 return Settings::NativeKeyboard::OpenBracket;
488 case Qt::Key_BracketRight:
489 case Qt::Key_BraceRight:
490 return Settings::NativeKeyboard::CloseBracket;
491 case Qt::Key_Bar:
492 return Settings::NativeKeyboard::Pipe;
493 case Qt::Key_Dead_Tilde:
494 return Settings::NativeKeyboard::Tilde;
495 case Qt::Key_Ntilde:
496 case Qt::Key_Semicolon:
497 return Settings::NativeKeyboard::Semicolon;
498 case Qt::Key_Apostrophe:
499 return Settings::NativeKeyboard::Quote;
500 case Qt::Key_Dead_Grave:
501 return Settings::NativeKeyboard::Backquote;
502 case Qt::Key_Comma:
503 return Settings::NativeKeyboard::Comma;
504 case Qt::Key_Period:
505 return Settings::NativeKeyboard::Period;
506 case Qt::Key_Slash:
507 return Settings::NativeKeyboard::Slash;
508 case Qt::Key_CapsLock:
509 return Settings::NativeKeyboard::CapsLock;
510 case Qt::Key_F1:
511 return Settings::NativeKeyboard::F1;
512 case Qt::Key_F2:
513 return Settings::NativeKeyboard::F2;
514 case Qt::Key_F3:
515 return Settings::NativeKeyboard::F3;
516 case Qt::Key_F4:
517 return Settings::NativeKeyboard::F4;
518 case Qt::Key_F5:
519 return Settings::NativeKeyboard::F5;
520 case Qt::Key_F6:
521 return Settings::NativeKeyboard::F6;
522 case Qt::Key_F7:
523 return Settings::NativeKeyboard::F7;
524 case Qt::Key_F8:
525 return Settings::NativeKeyboard::F8;
526 case Qt::Key_F9:
527 return Settings::NativeKeyboard::F9;
528 case Qt::Key_F10:
529 return Settings::NativeKeyboard::F10;
530 case Qt::Key_F11:
531 return Settings::NativeKeyboard::F11;
532 case Qt::Key_F12:
533 return Settings::NativeKeyboard::F12;
534 case Qt::Key_Print:
535 return Settings::NativeKeyboard::PrintScreen;
536 case Qt::Key_ScrollLock:
537 return Settings::NativeKeyboard::ScrollLock;
538 case Qt::Key_Pause:
539 return Settings::NativeKeyboard::Pause;
540 case Qt::Key_Insert:
541 return Settings::NativeKeyboard::Insert;
542 case Qt::Key_Home:
543 return Settings::NativeKeyboard::Home;
544 case Qt::Key_PageUp:
545 return Settings::NativeKeyboard::PageUp;
546 case Qt::Key_Delete:
547 return Settings::NativeKeyboard::Delete;
548 case Qt::Key_End:
549 return Settings::NativeKeyboard::End;
550 case Qt::Key_PageDown:
551 return Settings::NativeKeyboard::PageDown;
552 case Qt::Key_Right:
553 return Settings::NativeKeyboard::Right;
554 case Qt::Key_Left:
555 return Settings::NativeKeyboard::Left;
556 case Qt::Key_Down:
557 return Settings::NativeKeyboard::Down;
558 case Qt::Key_Up:
559 return Settings::NativeKeyboard::Up;
560 case Qt::Key_NumLock:
561 return Settings::NativeKeyboard::NumLock;
562 // Numpad keys are missing here
563 case Qt::Key_F13:
564 return Settings::NativeKeyboard::F13;
565 case Qt::Key_F14:
566 return Settings::NativeKeyboard::F14;
567 case Qt::Key_F15:
568 return Settings::NativeKeyboard::F15;
569 case Qt::Key_F16:
570 return Settings::NativeKeyboard::F16;
571 case Qt::Key_F17:
572 return Settings::NativeKeyboard::F17;
573 case Qt::Key_F18:
574 return Settings::NativeKeyboard::F18;
575 case Qt::Key_F19:
576 return Settings::NativeKeyboard::F19;
577 case Qt::Key_F20:
578 return Settings::NativeKeyboard::F20;
579 case Qt::Key_F21:
580 return Settings::NativeKeyboard::F21;
581 case Qt::Key_F22:
582 return Settings::NativeKeyboard::F22;
583 case Qt::Key_F23:
584 return Settings::NativeKeyboard::F23;
585 case Qt::Key_F24:
586 return Settings::NativeKeyboard::F24;
587 // case Qt:::
588 // return Settings::NativeKeyboard::KPComma;
589 // case Qt:::
590 // return Settings::NativeKeyboard::Ro;
591 case Qt::Key_Hiragana_Katakana:
592 return Settings::NativeKeyboard::KatakanaHiragana;
593 case Qt::Key_yen:
594 return Settings::NativeKeyboard::Yen;
595 case Qt::Key_Henkan:
596 return Settings::NativeKeyboard::Henkan;
597 case Qt::Key_Muhenkan:
598 return Settings::NativeKeyboard::Muhenkan;
599 // case Qt:::
600 // return Settings::NativeKeyboard::NumPadCommaPc98;
601 case Qt::Key_Hangul:
602 return Settings::NativeKeyboard::HangulEnglish;
603 case Qt::Key_Hangul_Hanja:
604 return Settings::NativeKeyboard::Hanja;
605 case Qt::Key_Katakana:
606 return Settings::NativeKeyboard::KatakanaKey;
607 case Qt::Key_Hiragana:
608 return Settings::NativeKeyboard::HiraganaKey;
609 case Qt::Key_Zenkaku_Hankaku:
610 return Settings::NativeKeyboard::ZenkakuHankaku;
611 // Modifier keys are handled by the modifier property
612 default:
613 return Settings::NativeKeyboard::None;
614 }
615}
616
617int GRenderWindow::QtModifierToSwitchModifier(Qt::KeyboardModifiers qt_modifiers) {
618 int modifier = 0;
619
620 if ((qt_modifiers & Qt::KeyboardModifier::ShiftModifier) != 0) {
621 modifier |= 1 << Settings::NativeKeyboard::LeftShift;
622 }
623 if ((qt_modifiers & Qt::KeyboardModifier::ControlModifier) != 0) {
624 modifier |= 1 << Settings::NativeKeyboard::LeftControl;
625 }
626 if ((qt_modifiers & Qt::KeyboardModifier::AltModifier) != 0) {
627 modifier |= 1 << Settings::NativeKeyboard::LeftAlt;
628 }
629 if ((qt_modifiers & Qt::KeyboardModifier::MetaModifier) != 0) {
630 modifier |= 1 << Settings::NativeKeyboard::LeftMeta;
631 }
632
633 // TODO: These keys can't be obtained with Qt::KeyboardModifier
634
635 // if ((qt_modifiers & 0x10) != 0) {
636 // modifier |= 1 << Settings::NativeKeyboard::RightShift;
637 // }
638 // if ((qt_modifiers & 0x20) != 0) {
639 // modifier |= 1 << Settings::NativeKeyboard::RightControl;
640 // }
641 // if ((qt_modifiers & 0x40) != 0) {
642 // modifier |= 1 << Settings::NativeKeyboard::RightAlt;
643 // }
644 // if ((qt_modifiers & 0x80) != 0) {
645 // modifier |= 1 << Settings::NativeKeyboard::RightMeta;
646 // }
647 // if ((qt_modifiers & 0x100) != 0) {
648 // modifier |= 1 << Settings::NativeKeyboard::CapsLock;
649 // }
650 // if ((qt_modifiers & 0x200) != 0) {
651 // modifier |= 1 << Settings::NativeKeyboard::NumLock;
652 // }
653 // if ((qt_modifiers & ???) != 0) {
654 // modifier |= 1 << Settings::NativeKeyboard::ScrollLock;
655 // }
656 // if ((qt_modifiers & ???) != 0) {
657 // modifier |= 1 << Settings::NativeKeyboard::Katakana;
658 // }
659 // if ((qt_modifiers & ???) != 0) {
660 // modifier |= 1 << Settings::NativeKeyboard::Hiragana;
661 // }
662 return modifier;
663}
664
386void GRenderWindow::keyPressEvent(QKeyEvent* event) { 665void GRenderWindow::keyPressEvent(QKeyEvent* event) {
666 /**
667 * This feature can be enhanced with the following functions, but they do not provide
668 * cross-platform behavior.
669 *
670 * event->nativeVirtualKey() can distinguish between keys on the numpad.
671 * event->nativeModifiers() can distinguish between left and right keys and numlock,
672 * capslock, scroll lock.
673 */
387 if (!event->isAutoRepeat()) { 674 if (!event->isAutoRepeat()) {
675 const auto modifier = QtModifierToSwitchModifier(event->modifiers());
676 const auto key = QtKeyToSwitchKey(Qt::Key(event->key()));
677 input_subsystem->GetKeyboard()->SetKeyboardModifiers(modifier);
678 input_subsystem->GetKeyboard()->PressKeyboardKey(key);
679 // This is used for gamepads that can have any key mapped
388 input_subsystem->GetKeyboard()->PressKey(event->key()); 680 input_subsystem->GetKeyboard()->PressKey(event->key());
389 } 681 }
390} 682}
391 683
392void GRenderWindow::keyReleaseEvent(QKeyEvent* event) { 684void GRenderWindow::keyReleaseEvent(QKeyEvent* event) {
685 /**
686 * This feature can be enhanced with the following functions, but they do not provide
687 * cross-platform behavior.
688 *
689 * event->nativeVirtualKey() can distinguish between keys on the numpad.
690 * event->nativeModifiers() can distinguish between left and right buttons and numlock,
691 * capslock, scroll lock.
692 */
393 if (!event->isAutoRepeat()) { 693 if (!event->isAutoRepeat()) {
694 const auto modifier = QtModifierToSwitchModifier(event->modifiers());
695 const auto key = QtKeyToSwitchKey(Qt::Key(event->key()));
696 input_subsystem->GetKeyboard()->SetKeyboardModifiers(modifier);
697 input_subsystem->GetKeyboard()->ReleaseKeyboardKey(key);
698 // This is used for gamepads that can have any key mapped
394 input_subsystem->GetKeyboard()->ReleaseKey(event->key()); 699 input_subsystem->GetKeyboard()->ReleaseKey(event->key());
395 } 700 }
396} 701}
397 702
398MouseInput::MouseButton GRenderWindow::QtButtonToMouseButton(Qt::MouseButton button) { 703InputCommon::MouseButton GRenderWindow::QtButtonToMouseButton(Qt::MouseButton button) {
399 switch (button) { 704 switch (button) {
400 case Qt::LeftButton: 705 case Qt::LeftButton:
401 return MouseInput::MouseButton::Left; 706 return InputCommon::MouseButton::Left;
402 case Qt::RightButton: 707 case Qt::RightButton:
403 return MouseInput::MouseButton::Right; 708 return InputCommon::MouseButton::Right;
404 case Qt::MiddleButton: 709 case Qt::MiddleButton:
405 return MouseInput::MouseButton::Wheel; 710 return InputCommon::MouseButton::Wheel;
406 case Qt::BackButton: 711 case Qt::BackButton:
407 return MouseInput::MouseButton::Backward; 712 return InputCommon::MouseButton::Backward;
408 case Qt::ForwardButton: 713 case Qt::ForwardButton:
409 return MouseInput::MouseButton::Forward; 714 return InputCommon::MouseButton::Forward;
410 case Qt::TaskButton: 715 case Qt::TaskButton:
411 return MouseInput::MouseButton::Task; 716 return InputCommon::MouseButton::Task;
412 default: 717 default:
413 return MouseInput::MouseButton::Extra; 718 return InputCommon::MouseButton::Extra;
414 } 719 }
415} 720}
416 721
@@ -423,12 +728,9 @@ void GRenderWindow::mousePressEvent(QMouseEvent* event) {
423 // coordinates and map them to the current render area 728 // coordinates and map them to the current render area
424 const auto pos = mapFromGlobal(QCursor::pos()); 729 const auto pos = mapFromGlobal(QCursor::pos());
425 const auto [x, y] = ScaleTouch(pos); 730 const auto [x, y] = ScaleTouch(pos);
731 const auto [touch_x, touch_y] = MapToTouchScreen(x, y);
426 const auto button = QtButtonToMouseButton(event->button()); 732 const auto button = QtButtonToMouseButton(event->button());
427 input_subsystem->GetMouse()->PressButton(x, y, button); 733 input_subsystem->GetMouse()->PressButton(x, y, touch_x, touch_y, button);
428
429 if (event->button() == Qt::LeftButton) {
430 this->TouchPressed(x, y, 0);
431 }
432 734
433 emit MouseActivity(); 735 emit MouseActivity();
434} 736}
@@ -442,12 +744,12 @@ void GRenderWindow::mouseMoveEvent(QMouseEvent* event) {
442 // coordinates and map them to the current render area 744 // coordinates and map them to the current render area
443 const auto pos = mapFromGlobal(QCursor::pos()); 745 const auto pos = mapFromGlobal(QCursor::pos());
444 const auto [x, y] = ScaleTouch(pos); 746 const auto [x, y] = ScaleTouch(pos);
747 const auto [touch_x, touch_y] = MapToTouchScreen(x, y);
445 const int center_x = width() / 2; 748 const int center_x = width() / 2;
446 const int center_y = height() / 2; 749 const int center_y = height() / 2;
447 input_subsystem->GetMouse()->MouseMove(x, y, center_x, center_y); 750 input_subsystem->GetMouse()->MouseMove(x, y, touch_x, touch_y, center_x, center_y);
448 this->TouchMoved(x, y, 0);
449 751
450 if (Settings::values.mouse_panning) { 752 if (Settings::values.mouse_panning && !Settings::values.mouse_enabled) {
451 QCursor::setPos(mapToGlobal({center_x, center_y})); 753 QCursor::setPos(mapToGlobal({center_x, center_y}));
452 } 754 }
453 755
@@ -462,10 +764,12 @@ void GRenderWindow::mouseReleaseEvent(QMouseEvent* event) {
462 764
463 const auto button = QtButtonToMouseButton(event->button()); 765 const auto button = QtButtonToMouseButton(event->button());
464 input_subsystem->GetMouse()->ReleaseButton(button); 766 input_subsystem->GetMouse()->ReleaseButton(button);
767}
465 768
466 if (event->button() == Qt::LeftButton) { 769void GRenderWindow::wheelEvent(QWheelEvent* event) {
467 this->TouchReleased(0); 770 const int x = event->angleDelta().x();
468 } 771 const int y = event->angleDelta().y();
772 input_subsystem->GetMouse()->MouseWheelChange(x, y);
469} 773}
470 774
471void GRenderWindow::TouchBeginEvent(const QTouchEvent* event) { 775void GRenderWindow::TouchBeginEvent(const QTouchEvent* event) {
@@ -488,7 +792,7 @@ void GRenderWindow::TouchUpdateEvent(const QTouchEvent* event) {
488 for (std::size_t id = 0; id < touch_ids.size(); ++id) { 792 for (std::size_t id = 0; id < touch_ids.size(); ++id) {
489 if (!TouchExist(touch_ids[id], touch_points)) { 793 if (!TouchExist(touch_ids[id], touch_points)) {
490 touch_ids[id] = 0; 794 touch_ids[id] = 0;
491 this->TouchReleased(id + 1); 795 input_subsystem->GetTouchScreen()->TouchReleased(id);
492 } 796 }
493 } 797 }
494} 798}
@@ -497,28 +801,28 @@ void GRenderWindow::TouchEndEvent() {
497 for (std::size_t id = 0; id < touch_ids.size(); ++id) { 801 for (std::size_t id = 0; id < touch_ids.size(); ++id) {
498 if (touch_ids[id] != 0) { 802 if (touch_ids[id] != 0) {
499 touch_ids[id] = 0; 803 touch_ids[id] = 0;
500 this->TouchReleased(id + 1); 804 input_subsystem->GetTouchScreen()->TouchReleased(id);
501 } 805 }
502 } 806 }
503} 807}
504 808
505bool GRenderWindow::TouchStart(const QTouchEvent::TouchPoint& touch_point) { 809void GRenderWindow::TouchStart(const QTouchEvent::TouchPoint& touch_point) {
506 for (std::size_t id = 0; id < touch_ids.size(); ++id) { 810 for (std::size_t id = 0; id < touch_ids.size(); ++id) {
507 if (touch_ids[id] == 0) { 811 if (touch_ids[id] == 0) {
508 touch_ids[id] = touch_point.id() + 1; 812 touch_ids[id] = touch_point.id() + 1;
509 const auto [x, y] = ScaleTouch(touch_point.pos()); 813 const auto [x, y] = ScaleTouch(touch_point.pos());
510 this->TouchPressed(x, y, id + 1); 814 const auto [touch_x, touch_y] = MapToTouchScreen(x, y);
511 return true; 815 input_subsystem->GetTouchScreen()->TouchPressed(touch_x, touch_y, id);
512 } 816 }
513 } 817 }
514 return false;
515} 818}
516 819
517bool GRenderWindow::TouchUpdate(const QTouchEvent::TouchPoint& touch_point) { 820bool GRenderWindow::TouchUpdate(const QTouchEvent::TouchPoint& touch_point) {
518 for (std::size_t id = 0; id < touch_ids.size(); ++id) { 821 for (std::size_t id = 0; id < touch_ids.size(); ++id) {
519 if (touch_ids[id] == static_cast<std::size_t>(touch_point.id() + 1)) { 822 if (touch_ids[id] == static_cast<std::size_t>(touch_point.id() + 1)) {
520 const auto [x, y] = ScaleTouch(touch_point.pos()); 823 const auto [x, y] = ScaleTouch(touch_point.pos());
521 this->TouchMoved(x, y, id + 1); 824 const auto [touch_x, touch_y] = MapToTouchScreen(x, y);
825 input_subsystem->GetTouchScreen()->TouchMoved(touch_x, touch_y, id);
522 return true; 826 return true;
523 } 827 }
524 } 828 }
@@ -551,7 +855,7 @@ void GRenderWindow::focusOutEvent(QFocusEvent* event) {
551 QWidget::focusOutEvent(event); 855 QWidget::focusOutEvent(event);
552 input_subsystem->GetKeyboard()->ReleaseAllKeys(); 856 input_subsystem->GetKeyboard()->ReleaseAllKeys();
553 input_subsystem->GetMouse()->ReleaseAllButtons(); 857 input_subsystem->GetMouse()->ReleaseAllButtons();
554 this->TouchReleased(0); 858 input_subsystem->GetTouchScreen()->ReleaseAllTouch();
555} 859}
556 860
557void GRenderWindow::resizeEvent(QResizeEvent* event) { 861void GRenderWindow::resizeEvent(QResizeEvent* event) {
@@ -630,7 +934,7 @@ void GRenderWindow::ReleaseRenderTarget() {
630 934
631void GRenderWindow::CaptureScreenshot(const QString& screenshot_path) { 935void GRenderWindow::CaptureScreenshot(const QString& screenshot_path) {
632 auto& renderer = system.Renderer(); 936 auto& renderer = system.Renderer();
633 const f32 res_scale = VideoCore::GetResolutionScaleFactor(renderer); 937 const f32 res_scale = Settings::values.resolution_info.up_factor;
634 938
635 const Layout::FramebufferLayout layout{Layout::FrameLayoutFromResolutionScale(res_scale)}; 939 const Layout::FramebufferLayout layout{Layout::FrameLayoutFromResolutionScale(res_scale)};
636 screenshot_image = QImage(QSize(layout.width, layout.height), QImage::Format_RGB32); 940 screenshot_image = QImage(QSize(layout.width, layout.height), QImage::Format_RGB32);
@@ -760,7 +1064,7 @@ void GRenderWindow::showEvent(QShowEvent* event) {
760 1064
761bool GRenderWindow::eventFilter(QObject* object, QEvent* event) { 1065bool GRenderWindow::eventFilter(QObject* object, QEvent* event) {
762 if (event->type() == QEvent::HoverMove) { 1066 if (event->type() == QEvent::HoverMove) {
763 if (Settings::values.mouse_panning) { 1067 if (Settings::values.mouse_panning || Settings::values.mouse_enabled) {
764 auto* hover_event = static_cast<QMouseEvent*>(event); 1068 auto* hover_event = static_cast<QMouseEvent*>(event);
765 mouseMoveEvent(hover_event); 1069 mouseMoveEvent(hover_event);
766 return false; 1070 return false;
diff --git a/src/yuzu/bootmanager.h b/src/yuzu/bootmanager.h
index 40fd4a9d6..92297a43b 100644
--- a/src/yuzu/bootmanager.h
+++ b/src/yuzu/bootmanager.h
@@ -30,11 +30,12 @@ class System;
30 30
31namespace InputCommon { 31namespace InputCommon {
32class InputSubsystem; 32class InputSubsystem;
33}
34
35namespace MouseInput {
36enum class MouseButton; 33enum class MouseButton;
37} 34} // namespace InputCommon
35
36namespace InputCommon::TasInput {
37enum class TasState;
38} // namespace InputCommon::TasInput
38 39
39namespace VideoCore { 40namespace VideoCore {
40enum class LoadCallbackStage; 41enum class LoadCallbackStage;
@@ -157,15 +158,22 @@ public:
157 158
158 void resizeEvent(QResizeEvent* event) override; 159 void resizeEvent(QResizeEvent* event) override;
159 160
161 /// Converts a Qt keybard key into NativeKeyboard key
162 static int QtKeyToSwitchKey(Qt::Key qt_keys);
163
164 /// Converts a Qt modifier keys into NativeKeyboard modifier keys
165 static int QtModifierToSwitchModifier(Qt::KeyboardModifiers qt_modifiers);
166
160 void keyPressEvent(QKeyEvent* event) override; 167 void keyPressEvent(QKeyEvent* event) override;
161 void keyReleaseEvent(QKeyEvent* event) override; 168 void keyReleaseEvent(QKeyEvent* event) override;
162 169
163 /// Converts a Qt mouse button into MouseInput mouse button 170 /// Converts a Qt mouse button into MouseInput mouse button
164 static MouseInput::MouseButton QtButtonToMouseButton(Qt::MouseButton button); 171 static InputCommon::MouseButton QtButtonToMouseButton(Qt::MouseButton button);
165 172
166 void mousePressEvent(QMouseEvent* event) override; 173 void mousePressEvent(QMouseEvent* event) override;
167 void mouseMoveEvent(QMouseEvent* event) override; 174 void mouseMoveEvent(QMouseEvent* event) override;
168 void mouseReleaseEvent(QMouseEvent* event) override; 175 void mouseReleaseEvent(QMouseEvent* event) override;
176 void wheelEvent(QWheelEvent* event) override;
169 177
170 bool event(QEvent* event) override; 178 bool event(QEvent* event) override;
171 179
@@ -203,13 +211,14 @@ signals:
203 void ExecuteProgramSignal(std::size_t program_index); 211 void ExecuteProgramSignal(std::size_t program_index);
204 void ExitSignal(); 212 void ExitSignal();
205 void MouseActivity(); 213 void MouseActivity();
214 void TasPlaybackStateChanged();
206 215
207private: 216private:
208 void TouchBeginEvent(const QTouchEvent* event); 217 void TouchBeginEvent(const QTouchEvent* event);
209 void TouchUpdateEvent(const QTouchEvent* event); 218 void TouchUpdateEvent(const QTouchEvent* event);
210 void TouchEndEvent(); 219 void TouchEndEvent();
211 220
212 bool TouchStart(const QTouchEvent::TouchPoint& touch_point); 221 void TouchStart(const QTouchEvent::TouchPoint& touch_point);
213 bool TouchUpdate(const QTouchEvent::TouchPoint& touch_point); 222 bool TouchUpdate(const QTouchEvent::TouchPoint& touch_point);
214 bool TouchExist(std::size_t id, const QList<QTouchEvent::TouchPoint>& touch_points) const; 223 bool TouchExist(std::size_t id, const QList<QTouchEvent::TouchPoint>& touch_points) const;
215 224
@@ -236,6 +245,7 @@ private:
236 QWidget* child_widget = nullptr; 245 QWidget* child_widget = nullptr;
237 246
238 bool first_frame = false; 247 bool first_frame = false;
248 InputCommon::TasInput::TasState last_tas_state;
239 249
240 std::array<std::size_t, 16> touch_ids{}; 250 std::array<std::size_t, 16> touch_ids{};
241 251
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index 8227d06bc..0f679c37e 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -11,7 +11,6 @@
11#include "core/hle/service/acc/profile_manager.h" 11#include "core/hle/service/acc/profile_manager.h"
12#include "core/hle/service/hid/controllers/npad.h" 12#include "core/hle/service/hid/controllers/npad.h"
13#include "input_common/main.h" 13#include "input_common/main.h"
14#include "input_common/udp/client.h"
15#include "yuzu/configuration/config.h" 14#include "yuzu/configuration/config.h"
16 15
17namespace FS = Common::FS; 16namespace FS = Common::FS;
@@ -61,162 +60,6 @@ const std::array<int, 2> Config::default_stick_mod = {
61 0, 60 0,
62}; 61};
63 62
64const std::array<int, Settings::NativeMouseButton::NumMouseButtons> Config::default_mouse_buttons =
65 {
66 Qt::Key_BracketLeft, Qt::Key_BracketRight, Qt::Key_Apostrophe, Qt::Key_Minus, Qt::Key_Equal,
67};
68
69const std::array<int, Settings::NativeKeyboard::NumKeyboardKeys> Config::default_keyboard_keys = {
70 0,
71 0,
72 0,
73 0,
74 Qt::Key_A,
75 Qt::Key_B,
76 Qt::Key_C,
77 Qt::Key_D,
78 Qt::Key_E,
79 Qt::Key_F,
80 Qt::Key_G,
81 Qt::Key_H,
82 Qt::Key_I,
83 Qt::Key_J,
84 Qt::Key_K,
85 Qt::Key_L,
86 Qt::Key_M,
87 Qt::Key_N,
88 Qt::Key_O,
89 Qt::Key_P,
90 Qt::Key_Q,
91 Qt::Key_R,
92 Qt::Key_S,
93 Qt::Key_T,
94 Qt::Key_U,
95 Qt::Key_V,
96 Qt::Key_W,
97 Qt::Key_X,
98 Qt::Key_Y,
99 Qt::Key_Z,
100 Qt::Key_1,
101 Qt::Key_2,
102 Qt::Key_3,
103 Qt::Key_4,
104 Qt::Key_5,
105 Qt::Key_6,
106 Qt::Key_7,
107 Qt::Key_8,
108 Qt::Key_9,
109 Qt::Key_0,
110 Qt::Key_Enter,
111 Qt::Key_Escape,
112 Qt::Key_Backspace,
113 Qt::Key_Tab,
114 Qt::Key_Space,
115 Qt::Key_Minus,
116 Qt::Key_Equal,
117 Qt::Key_BracketLeft,
118 Qt::Key_BracketRight,
119 Qt::Key_Backslash,
120 Qt::Key_Dead_Tilde,
121 Qt::Key_Semicolon,
122 Qt::Key_Apostrophe,
123 Qt::Key_Dead_Grave,
124 Qt::Key_Comma,
125 Qt::Key_Period,
126 Qt::Key_Slash,
127 Qt::Key_CapsLock,
128
129 Qt::Key_F1,
130 Qt::Key_F2,
131 Qt::Key_F3,
132 Qt::Key_F4,
133 Qt::Key_F5,
134 Qt::Key_F6,
135 Qt::Key_F7,
136 Qt::Key_F8,
137 Qt::Key_F9,
138 Qt::Key_F10,
139 Qt::Key_F11,
140 Qt::Key_F12,
141
142 Qt::Key_SysReq,
143 Qt::Key_ScrollLock,
144 Qt::Key_Pause,
145 Qt::Key_Insert,
146 Qt::Key_Home,
147 Qt::Key_PageUp,
148 Qt::Key_Delete,
149 Qt::Key_End,
150 Qt::Key_PageDown,
151 Qt::Key_Right,
152 Qt::Key_Left,
153 Qt::Key_Down,
154 Qt::Key_Up,
155
156 Qt::Key_NumLock,
157 Qt::Key_Slash,
158 Qt::Key_Asterisk,
159 Qt::Key_Minus,
160 Qt::Key_Plus,
161 Qt::Key_Enter,
162 Qt::Key_1,
163 Qt::Key_2,
164 Qt::Key_3,
165 Qt::Key_4,
166 Qt::Key_5,
167 Qt::Key_6,
168 Qt::Key_7,
169 Qt::Key_8,
170 Qt::Key_9,
171 Qt::Key_0,
172 Qt::Key_Period,
173
174 0,
175 0,
176 Qt::Key_PowerOff,
177 Qt::Key_Equal,
178
179 Qt::Key_F13,
180 Qt::Key_F14,
181 Qt::Key_F15,
182 Qt::Key_F16,
183 Qt::Key_F17,
184 Qt::Key_F18,
185 Qt::Key_F19,
186 Qt::Key_F20,
187 Qt::Key_F21,
188 Qt::Key_F22,
189 Qt::Key_F23,
190 Qt::Key_F24,
191
192 Qt::Key_Open,
193 Qt::Key_Help,
194 Qt::Key_Menu,
195 0,
196 Qt::Key_Stop,
197 Qt::Key_AudioRepeat,
198 Qt::Key_Undo,
199 Qt::Key_Cut,
200 Qt::Key_Copy,
201 Qt::Key_Paste,
202 Qt::Key_Find,
203 Qt::Key_VolumeMute,
204 Qt::Key_VolumeUp,
205 Qt::Key_VolumeDown,
206 Qt::Key_CapsLock,
207 Qt::Key_NumLock,
208 Qt::Key_ScrollLock,
209 Qt::Key_Comma,
210
211 Qt::Key_ParenLeft,
212 Qt::Key_ParenRight,
213};
214
215const std::array<int, Settings::NativeKeyboard::NumKeyboardMods> Config::default_keyboard_mods = {
216 Qt::Key_Control, Qt::Key_Shift, Qt::Key_Alt, Qt::Key_ApplicationLeft,
217 Qt::Key_Control, Qt::Key_Shift, Qt::Key_AltGr, Qt::Key_ApplicationRight,
218};
219
220// This shouldn't have anything except static initializers (no functions). So 63// This shouldn't have anything except static initializers (no functions). So
221// QKeySequence(...).toString() is NOT ALLOWED HERE. 64// QKeySequence(...).toString() is NOT ALLOWED HERE.
222// This must be in alphabetical order according to action name as it must have the same order as 65// This must be in alphabetical order according to action name as it must have the same order as
@@ -430,18 +273,6 @@ void Config::ReadPlayerValue(std::size_t player_index) {
430 } 273 }
431 } 274 }
432 275
433 for (int i = 0; i < Settings::NativeVibration::NumVibrations; ++i) {
434 auto& player_vibrations = player.vibrations[i];
435
436 player_vibrations =
437 qt_config
438 ->value(QStringLiteral("%1").arg(player_prefix) +
439 QString::fromUtf8(Settings::NativeVibration::mapping[i]),
440 QString{})
441 .toString()
442 .toStdString();
443 }
444
445 for (int i = 0; i < Settings::NativeMotion::NumMotions; ++i) { 276 for (int i = 0; i < Settings::NativeMotion::NumMotions; ++i) {
446 const std::string default_param = InputCommon::GenerateKeyboardParam(default_motions[i]); 277 const std::string default_param = InputCommon::GenerateKeyboardParam(default_motions[i]);
447 auto& player_motions = player.motions[i]; 278 auto& player_motions = player.motions[i];
@@ -496,35 +327,10 @@ void Config::ReadDebugValues() {
496 327
497void Config::ReadKeyboardValues() { 328void Config::ReadKeyboardValues() {
498 ReadBasicSetting(Settings::values.keyboard_enabled); 329 ReadBasicSetting(Settings::values.keyboard_enabled);
499
500 std::transform(default_keyboard_keys.begin(), default_keyboard_keys.end(),
501 Settings::values.keyboard_keys.begin(), InputCommon::GenerateKeyboardParam);
502 std::transform(default_keyboard_mods.begin(), default_keyboard_mods.end(),
503 Settings::values.keyboard_keys.begin() +
504 Settings::NativeKeyboard::LeftControlKey,
505 InputCommon::GenerateKeyboardParam);
506 std::transform(default_keyboard_mods.begin(), default_keyboard_mods.end(),
507 Settings::values.keyboard_mods.begin(), InputCommon::GenerateKeyboardParam);
508} 330}
509 331
510void Config::ReadMouseValues() { 332void Config::ReadMouseValues() {
511 ReadBasicSetting(Settings::values.mouse_enabled); 333 ReadBasicSetting(Settings::values.mouse_enabled);
512
513 for (int i = 0; i < Settings::NativeMouseButton::NumMouseButtons; ++i) {
514 const std::string default_param =
515 InputCommon::GenerateKeyboardParam(default_mouse_buttons[i]);
516 auto& mouse_buttons = Settings::values.mouse_buttons[i];
517
518 mouse_buttons = qt_config
519 ->value(QStringLiteral("mouse_") +
520 QString::fromUtf8(Settings::NativeMouseButton::mapping[i]),
521 QString::fromStdString(default_param))
522 .toString()
523 .toStdString();
524 if (mouse_buttons.empty()) {
525 mouse_buttons = default_param;
526 }
527 }
528} 334}
529 335
530void Config::ReadTouchscreenValues() { 336void Config::ReadTouchscreenValues() {
@@ -574,7 +380,6 @@ void Config::ReadControlValues() {
574 380
575 ReadBasicSetting(Settings::values.tas_enable); 381 ReadBasicSetting(Settings::values.tas_enable);
576 ReadBasicSetting(Settings::values.tas_loop); 382 ReadBasicSetting(Settings::values.tas_loop);
577 ReadBasicSetting(Settings::values.tas_swap_controllers);
578 ReadBasicSetting(Settings::values.pause_tas_on_load); 383 ReadBasicSetting(Settings::values.pause_tas_on_load);
579 384
580 ReadGlobalSetting(Settings::values.use_docked_mode); 385 ReadGlobalSetting(Settings::values.use_docked_mode);
@@ -625,13 +430,12 @@ void Config::ReadMotionTouchValues() {
625 } 430 }
626 qt_config->endArray(); 431 qt_config->endArray();
627 432
628 ReadBasicSetting(Settings::values.motion_device);
629 ReadBasicSetting(Settings::values.touch_device); 433 ReadBasicSetting(Settings::values.touch_device);
630 ReadBasicSetting(Settings::values.use_touch_from_button);
631 ReadBasicSetting(Settings::values.touch_from_button_map_index); 434 ReadBasicSetting(Settings::values.touch_from_button_map_index);
632 Settings::values.touch_from_button_map_index = std::clamp( 435 Settings::values.touch_from_button_map_index = std::clamp(
633 Settings::values.touch_from_button_map_index.GetValue(), 0, num_touch_from_button_maps - 1); 436 Settings::values.touch_from_button_map_index.GetValue(), 0, num_touch_from_button_maps - 1);
634 ReadBasicSetting(Settings::values.udp_input_servers); 437 ReadBasicSetting(Settings::values.udp_input_servers);
438 ReadBasicSetting(Settings::values.enable_udp_controller);
635} 439}
636 440
637void Config::ReadCoreValues() { 441void Config::ReadCoreValues() {
@@ -704,6 +508,7 @@ void Config::ReadDebuggingValues() {
704 ReadBasicSetting(Settings::values.extended_logging); 508 ReadBasicSetting(Settings::values.extended_logging);
705 ReadBasicSetting(Settings::values.use_debug_asserts); 509 ReadBasicSetting(Settings::values.use_debug_asserts);
706 ReadBasicSetting(Settings::values.use_auto_stub); 510 ReadBasicSetting(Settings::values.use_auto_stub);
511 ReadBasicSetting(Settings::values.enable_all_controllers);
707 512
708 qt_config->endGroup(); 513 qt_config->endGroup();
709} 514}
@@ -830,6 +635,7 @@ void Config::ReadRendererValues() {
830 ReadGlobalSetting(Settings::values.max_anisotropy); 635 ReadGlobalSetting(Settings::values.max_anisotropy);
831 ReadGlobalSetting(Settings::values.use_speed_limit); 636 ReadGlobalSetting(Settings::values.use_speed_limit);
832 ReadGlobalSetting(Settings::values.speed_limit); 637 ReadGlobalSetting(Settings::values.speed_limit);
638 ReadGlobalSetting(Settings::values.fps_cap);
833 ReadGlobalSetting(Settings::values.use_disk_shader_cache); 639 ReadGlobalSetting(Settings::values.use_disk_shader_cache);
834 ReadGlobalSetting(Settings::values.gpu_accuracy); 640 ReadGlobalSetting(Settings::values.gpu_accuracy);
835 ReadGlobalSetting(Settings::values.use_asynchronous_gpu_emulation); 641 ReadGlobalSetting(Settings::values.use_asynchronous_gpu_emulation);
@@ -844,7 +650,6 @@ void Config::ReadRendererValues() {
844 ReadGlobalSetting(Settings::values.bg_blue); 650 ReadGlobalSetting(Settings::values.bg_blue);
845 651
846 if (global) { 652 if (global) {
847 ReadBasicSetting(Settings::values.fps_cap);
848 ReadBasicSetting(Settings::values.renderer_debug); 653 ReadBasicSetting(Settings::values.renderer_debug);
849 ReadBasicSetting(Settings::values.renderer_shader_feedback); 654 ReadBasicSetting(Settings::values.renderer_shader_feedback);
850 ReadBasicSetting(Settings::values.enable_nsight_aftermath); 655 ReadBasicSetting(Settings::values.enable_nsight_aftermath);
@@ -971,6 +776,7 @@ void Config::ReadUIGamelistValues() {
971 ReadBasicSetting(UISettings::values.row_1_text_id); 776 ReadBasicSetting(UISettings::values.row_1_text_id);
972 ReadBasicSetting(UISettings::values.row_2_text_id); 777 ReadBasicSetting(UISettings::values.row_2_text_id);
973 ReadBasicSetting(UISettings::values.cache_game_list); 778 ReadBasicSetting(UISettings::values.cache_game_list);
779 ReadBasicSetting(UISettings::values.favorites_expanded);
974 const int favorites_size = qt_config->beginReadArray(QStringLiteral("favorites")); 780 const int favorites_size = qt_config->beginReadArray(QStringLiteral("favorites"));
975 for (int i = 0; i < favorites_size; i++) { 781 for (int i = 0; i < favorites_size; i++) {
976 qt_config->setArrayIndex(i); 782 qt_config->setArrayIndex(i);
@@ -1075,11 +881,6 @@ void Config::SavePlayerValue(std::size_t player_index) {
1075 QString::fromStdString(player.analogs[i]), 881 QString::fromStdString(player.analogs[i]),
1076 QString::fromStdString(default_param)); 882 QString::fromStdString(default_param));
1077 } 883 }
1078 for (int i = 0; i < Settings::NativeVibration::NumVibrations; ++i) {
1079 WriteSetting(QStringLiteral("%1").arg(player_prefix) +
1080 QString::fromStdString(Settings::NativeVibration::mapping[i]),
1081 QString::fromStdString(player.vibrations[i]), QString{});
1082 }
1083 for (int i = 0; i < Settings::NativeMotion::NumMotions; ++i) { 884 for (int i = 0; i < Settings::NativeMotion::NumMotions; ++i) {
1084 const std::string default_param = InputCommon::GenerateKeyboardParam(default_motions[i]); 885 const std::string default_param = InputCommon::GenerateKeyboardParam(default_motions[i]);
1085 WriteSetting(QStringLiteral("%1").arg(player_prefix) + 886 WriteSetting(QStringLiteral("%1").arg(player_prefix) +
@@ -1111,15 +912,6 @@ void Config::SaveDebugValues() {
1111 912
1112void Config::SaveMouseValues() { 913void Config::SaveMouseValues() {
1113 WriteBasicSetting(Settings::values.mouse_enabled); 914 WriteBasicSetting(Settings::values.mouse_enabled);
1114
1115 for (int i = 0; i < Settings::NativeMouseButton::NumMouseButtons; ++i) {
1116 const std::string default_param =
1117 InputCommon::GenerateKeyboardParam(default_mouse_buttons[i]);
1118 WriteSetting(QStringLiteral("mouse_") +
1119 QString::fromStdString(Settings::NativeMouseButton::mapping[i]),
1120 QString::fromStdString(Settings::values.mouse_buttons[i]),
1121 QString::fromStdString(default_param));
1122 }
1123} 915}
1124 916
1125void Config::SaveTouchscreenValues() { 917void Config::SaveTouchscreenValues() {
@@ -1133,11 +925,10 @@ void Config::SaveTouchscreenValues() {
1133} 925}
1134 926
1135void Config::SaveMotionTouchValues() { 927void Config::SaveMotionTouchValues() {
1136 WriteBasicSetting(Settings::values.motion_device);
1137 WriteBasicSetting(Settings::values.touch_device); 928 WriteBasicSetting(Settings::values.touch_device);
1138 WriteBasicSetting(Settings::values.use_touch_from_button);
1139 WriteBasicSetting(Settings::values.touch_from_button_map_index); 929 WriteBasicSetting(Settings::values.touch_from_button_map_index);
1140 WriteBasicSetting(Settings::values.udp_input_servers); 930 WriteBasicSetting(Settings::values.udp_input_servers);
931 WriteBasicSetting(Settings::values.enable_udp_controller);
1141 932
1142 qt_config->beginWriteArray(QStringLiteral("touch_from_button_maps")); 933 qt_config->beginWriteArray(QStringLiteral("touch_from_button_maps"));
1143 for (std::size_t p = 0; p < Settings::values.touch_from_button_maps.size(); ++p) { 934 for (std::size_t p = 0; p < Settings::values.touch_from_button_maps.size(); ++p) {
@@ -1210,7 +1001,6 @@ void Config::SaveControlValues() {
1210 1001
1211 WriteBasicSetting(Settings::values.tas_enable); 1002 WriteBasicSetting(Settings::values.tas_enable);
1212 WriteBasicSetting(Settings::values.tas_loop); 1003 WriteBasicSetting(Settings::values.tas_loop);
1213 WriteBasicSetting(Settings::values.tas_swap_controllers);
1214 WriteBasicSetting(Settings::values.pause_tas_on_load); 1004 WriteBasicSetting(Settings::values.pause_tas_on_load);
1215 1005
1216 qt_config->endGroup(); 1006 qt_config->endGroup();
@@ -1263,6 +1053,7 @@ void Config::SaveDebuggingValues() {
1263 WriteBasicSetting(Settings::values.quest_flag); 1053 WriteBasicSetting(Settings::values.quest_flag);
1264 WriteBasicSetting(Settings::values.use_debug_asserts); 1054 WriteBasicSetting(Settings::values.use_debug_asserts);
1265 WriteBasicSetting(Settings::values.disable_macro_jit); 1055 WriteBasicSetting(Settings::values.disable_macro_jit);
1056 WriteBasicSetting(Settings::values.enable_all_controllers);
1266 1057
1267 qt_config->endGroup(); 1058 qt_config->endGroup();
1268} 1059}
@@ -1382,6 +1173,7 @@ void Config::SaveRendererValues() {
1382 WriteGlobalSetting(Settings::values.max_anisotropy); 1173 WriteGlobalSetting(Settings::values.max_anisotropy);
1383 WriteGlobalSetting(Settings::values.use_speed_limit); 1174 WriteGlobalSetting(Settings::values.use_speed_limit);
1384 WriteGlobalSetting(Settings::values.speed_limit); 1175 WriteGlobalSetting(Settings::values.speed_limit);
1176 WriteGlobalSetting(Settings::values.fps_cap);
1385 WriteGlobalSetting(Settings::values.use_disk_shader_cache); 1177 WriteGlobalSetting(Settings::values.use_disk_shader_cache);
1386 WriteSetting(QString::fromStdString(Settings::values.gpu_accuracy.GetLabel()), 1178 WriteSetting(QString::fromStdString(Settings::values.gpu_accuracy.GetLabel()),
1387 static_cast<u32>(Settings::values.gpu_accuracy.GetValue(global)), 1179 static_cast<u32>(Settings::values.gpu_accuracy.GetValue(global)),
@@ -1405,7 +1197,6 @@ void Config::SaveRendererValues() {
1405 WriteGlobalSetting(Settings::values.bg_blue); 1197 WriteGlobalSetting(Settings::values.bg_blue);
1406 1198
1407 if (global) { 1199 if (global) {
1408 WriteBasicSetting(Settings::values.fps_cap);
1409 WriteBasicSetting(Settings::values.renderer_debug); 1200 WriteBasicSetting(Settings::values.renderer_debug);
1410 WriteBasicSetting(Settings::values.renderer_shader_feedback); 1201 WriteBasicSetting(Settings::values.renderer_shader_feedback);
1411 WriteBasicSetting(Settings::values.enable_nsight_aftermath); 1202 WriteBasicSetting(Settings::values.enable_nsight_aftermath);
@@ -1510,6 +1301,7 @@ void Config::SaveUIGamelistValues() {
1510 WriteBasicSetting(UISettings::values.row_1_text_id); 1301 WriteBasicSetting(UISettings::values.row_1_text_id);
1511 WriteBasicSetting(UISettings::values.row_2_text_id); 1302 WriteBasicSetting(UISettings::values.row_2_text_id);
1512 WriteBasicSetting(UISettings::values.cache_game_list); 1303 WriteBasicSetting(UISettings::values.cache_game_list);
1304 WriteBasicSetting(UISettings::values.favorites_expanded);
1513 qt_config->beginWriteArray(QStringLiteral("favorites")); 1305 qt_config->beginWriteArray(QStringLiteral("favorites"));
1514 for (int i = 0; i < UISettings::values.favorited_ids.size(); i++) { 1306 for (int i = 0; i < UISettings::values.favorited_ids.size(); i++) {
1515 qt_config->setArrayIndex(i); 1307 qt_config->setArrayIndex(i);
diff --git a/src/yuzu/configuration/configure_debug.cpp b/src/yuzu/configuration/configure_debug.cpp
index 07bfa0360..633fc295b 100644
--- a/src/yuzu/configuration/configure_debug.cpp
+++ b/src/yuzu/configuration/configure_debug.cpp
@@ -42,6 +42,7 @@ void ConfigureDebug::SetConfiguration() {
42 ui->quest_flag->setChecked(Settings::values.quest_flag.GetValue()); 42 ui->quest_flag->setChecked(Settings::values.quest_flag.GetValue());
43 ui->use_debug_asserts->setChecked(Settings::values.use_debug_asserts.GetValue()); 43 ui->use_debug_asserts->setChecked(Settings::values.use_debug_asserts.GetValue());
44 ui->use_auto_stub->setChecked(Settings::values.use_auto_stub.GetValue()); 44 ui->use_auto_stub->setChecked(Settings::values.use_auto_stub.GetValue());
45 ui->enable_all_controllers->setChecked(Settings::values.enable_all_controllers.GetValue());
45 ui->enable_graphics_debugging->setEnabled(runtime_lock); 46 ui->enable_graphics_debugging->setEnabled(runtime_lock);
46 ui->enable_graphics_debugging->setChecked(Settings::values.renderer_debug.GetValue()); 47 ui->enable_graphics_debugging->setChecked(Settings::values.renderer_debug.GetValue());
47 ui->enable_shader_feedback->setEnabled(runtime_lock); 48 ui->enable_shader_feedback->setEnabled(runtime_lock);
@@ -67,6 +68,7 @@ void ConfigureDebug::ApplyConfiguration() {
67 Settings::values.quest_flag = ui->quest_flag->isChecked(); 68 Settings::values.quest_flag = ui->quest_flag->isChecked();
68 Settings::values.use_debug_asserts = ui->use_debug_asserts->isChecked(); 69 Settings::values.use_debug_asserts = ui->use_debug_asserts->isChecked();
69 Settings::values.use_auto_stub = ui->use_auto_stub->isChecked(); 70 Settings::values.use_auto_stub = ui->use_auto_stub->isChecked();
71 Settings::values.enable_all_controllers = ui->enable_all_controllers->isChecked();
70 Settings::values.renderer_debug = ui->enable_graphics_debugging->isChecked(); 72 Settings::values.renderer_debug = ui->enable_graphics_debugging->isChecked();
71 Settings::values.renderer_shader_feedback = ui->enable_shader_feedback->isChecked(); 73 Settings::values.renderer_shader_feedback = ui->enable_shader_feedback->isChecked();
72 Settings::values.cpu_debug_mode = ui->enable_cpu_debugging->isChecked(); 74 Settings::values.cpu_debug_mode = ui->enable_cpu_debugging->isChecked();
diff --git a/src/yuzu/configuration/configure_debug.ui b/src/yuzu/configuration/configure_debug.ui
index b884a56b0..0f3b51c8d 100644
--- a/src/yuzu/configuration/configure_debug.ui
+++ b/src/yuzu/configuration/configure_debug.ui
@@ -198,6 +198,13 @@
198 </property> 198 </property>
199 </widget> 199 </widget>
200 </item> 200 </item>
201 <item row="1" column="1">
202 <widget class="QCheckBox" name="enable_all_controllers">
203 <property name="text">
204 <string>Enable all Controller Types</string>
205 </property>
206 </widget>
207 </item>
201 </layout> 208 </layout>
202 </widget> 209 </widget>
203 </item> 210 </item>
diff --git a/src/yuzu/configuration/configure_debug_controller.cpp b/src/yuzu/configuration/configure_debug_controller.cpp
index 31ec48384..9a8de92a1 100644
--- a/src/yuzu/configuration/configure_debug_controller.cpp
+++ b/src/yuzu/configuration/configure_debug_controller.cpp
@@ -2,17 +2,18 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "core/core.h" 5#include "core/hid/hid_core.h"
6#include "ui_configure_debug_controller.h" 6#include "ui_configure_debug_controller.h"
7#include "yuzu/configuration/configure_debug_controller.h" 7#include "yuzu/configuration/configure_debug_controller.h"
8#include "yuzu/configuration/configure_input_player.h" 8#include "yuzu/configuration/configure_input_player.h"
9 9
10ConfigureDebugController::ConfigureDebugController(QWidget* parent, 10ConfigureDebugController::ConfigureDebugController(QWidget* parent,
11 InputCommon::InputSubsystem* input_subsystem, 11 InputCommon::InputSubsystem* input_subsystem,
12 InputProfiles* profiles, Core::System& system) 12 InputProfiles* profiles,
13 Core::HID::HIDCore& hid_core, bool is_powered_on)
13 : QDialog(parent), ui(std::make_unique<Ui::ConfigureDebugController>()), 14 : QDialog(parent), ui(std::make_unique<Ui::ConfigureDebugController>()),
14 debug_controller( 15 debug_controller(new ConfigureInputPlayer(this, 9, nullptr, input_subsystem, profiles,
15 new ConfigureInputPlayer(this, 9, nullptr, input_subsystem, profiles, system, true)) { 16 hid_core, is_powered_on, true)) {
16 ui->setupUi(this); 17 ui->setupUi(this);
17 18
18 ui->controllerLayout->addWidget(debug_controller); 19 ui->controllerLayout->addWidget(debug_controller);
diff --git a/src/yuzu/configuration/configure_debug_controller.h b/src/yuzu/configuration/configure_debug_controller.h
index 6e17c5aa0..d716edbc2 100644
--- a/src/yuzu/configuration/configure_debug_controller.h
+++ b/src/yuzu/configuration/configure_debug_controller.h
@@ -13,8 +13,8 @@ class ConfigureInputPlayer;
13 13
14class InputProfiles; 14class InputProfiles;
15 15
16namespace Core { 16namespace Core::HID {
17class System; 17class HIDCore;
18} 18}
19 19
20namespace InputCommon { 20namespace InputCommon {
@@ -30,7 +30,8 @@ class ConfigureDebugController : public QDialog {
30 30
31public: 31public:
32 explicit ConfigureDebugController(QWidget* parent, InputCommon::InputSubsystem* input_subsystem, 32 explicit ConfigureDebugController(QWidget* parent, InputCommon::InputSubsystem* input_subsystem,
33 InputProfiles* profiles, Core::System& system); 33 InputProfiles* profiles, Core::HID::HIDCore& hid_core,
34 bool is_powered_on);
34 ~ConfigureDebugController() override; 35 ~ConfigureDebugController() override;
35 36
36 void ApplyConfiguration(); 37 void ApplyConfiguration();
diff --git a/src/yuzu/configuration/configure_general.cpp b/src/yuzu/configuration/configure_general.cpp
index 7af3ea97e..566879317 100644
--- a/src/yuzu/configuration/configure_general.cpp
+++ b/src/yuzu/configuration/configure_general.cpp
@@ -30,6 +30,9 @@ ConfigureGeneral::ConfigureGeneral(const Core::System& system_, QWidget* parent)
30 30
31 connect(ui->button_reset_defaults, &QPushButton::clicked, this, 31 connect(ui->button_reset_defaults, &QPushButton::clicked, this,
32 &ConfigureGeneral::ResetDefaults); 32 &ConfigureGeneral::ResetDefaults);
33
34 ui->fps_cap_label->setVisible(Settings::IsConfiguringGlobal());
35 ui->fps_cap_combobox->setVisible(!Settings::IsConfiguringGlobal());
33} 36}
34 37
35ConfigureGeneral::~ConfigureGeneral() = default; 38ConfigureGeneral::~ConfigureGeneral() = default;
@@ -57,6 +60,11 @@ void ConfigureGeneral::SetConfiguration() {
57 } else { 60 } else {
58 ui->speed_limit->setEnabled(Settings::values.use_speed_limit.GetValue() && 61 ui->speed_limit->setEnabled(Settings::values.use_speed_limit.GetValue() &&
59 use_speed_limit != ConfigurationShared::CheckState::Global); 62 use_speed_limit != ConfigurationShared::CheckState::Global);
63
64 ui->fps_cap_combobox->setCurrentIndex(Settings::values.fps_cap.UsingGlobal() ? 0 : 1);
65 ui->fps_cap->setEnabled(!Settings::values.fps_cap.UsingGlobal());
66 ConfigurationShared::SetHighlight(ui->fps_cap_layout,
67 !Settings::values.fps_cap.UsingGlobal());
60 } 68 }
61} 69}
62 70
@@ -106,6 +114,13 @@ void ConfigureGeneral::ApplyConfiguration() {
106 Qt::Checked); 114 Qt::Checked);
107 Settings::values.speed_limit.SetValue(ui->speed_limit->value()); 115 Settings::values.speed_limit.SetValue(ui->speed_limit->value());
108 } 116 }
117
118 if (ui->fps_cap_combobox->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) {
119 Settings::values.fps_cap.SetGlobal(true);
120 } else {
121 Settings::values.fps_cap.SetGlobal(false);
122 Settings::values.fps_cap.SetValue(ui->fps_cap->value());
123 }
109 } 124 }
110} 125}
111 126
@@ -148,4 +163,9 @@ void ConfigureGeneral::SetupPerGameUI() {
148 ui->speed_limit->setEnabled(ui->toggle_speed_limit->isChecked() && 163 ui->speed_limit->setEnabled(ui->toggle_speed_limit->isChecked() &&
149 (use_speed_limit != ConfigurationShared::CheckState::Global)); 164 (use_speed_limit != ConfigurationShared::CheckState::Global));
150 }); 165 });
166
167 connect(ui->fps_cap_combobox, qOverload<int>(&QComboBox::activated), this, [this](int index) {
168 ui->fps_cap->setEnabled(index == 1);
169 ConfigurationShared::SetHighlight(ui->fps_cap_layout, index == 1);
170 });
151} 171}
diff --git a/src/yuzu/configuration/configure_general.ui b/src/yuzu/configuration/configure_general.ui
index f9f0e3ebf..112dc72b3 100644
--- a/src/yuzu/configuration/configure_general.ui
+++ b/src/yuzu/configuration/configure_general.ui
@@ -6,8 +6,8 @@
6 <rect> 6 <rect>
7 <x>0</x> 7 <x>0</x>
8 <y>0</y> 8 <y>0</y>
9 <width>329</width> 9 <width>744</width>
10 <height>407</height> 10 <height>568</height>
11 </rect> 11 </rect>
12 </property> 12 </property>
13 <property name="windowTitle"> 13 <property name="windowTitle">
@@ -28,34 +28,85 @@
28 <item> 28 <item>
29 <layout class="QVBoxLayout" name="GeneralVerticalLayout"> 29 <layout class="QVBoxLayout" name="GeneralVerticalLayout">
30 <item> 30 <item>
31 <layout class="QHBoxLayout" name="horizontalLayout_2"> 31 <widget class="QWidget" name="fps_cap_layout" native="true">
32 <item> 32 <layout class="QHBoxLayout" name="horizontalLayout" stretch="1,1">
33 <widget class="QLabel" name="fps_cap_label"> 33 <property name="leftMargin">
34 <number>0</number>
35 </property>
36 <property name="topMargin">
37 <number>0</number>
38 </property>
39 <property name="rightMargin">
40 <number>0</number>
41 </property>
42 <property name="bottomMargin">
43 <number>0</number>
44 </property>
45 <item>
46 <layout class="QHBoxLayout" name="horizontalLayout_4">
47 <item>
48 <widget class="QComboBox" name="fps_cap_combobox">
49 <property name="currentText">
50 <string>Use global framerate cap</string>
51 </property>
52 <property name="currentIndex">
53 <number>0</number>
54 </property>
55 <item>
34 <property name="text"> 56 <property name="text">
35 <string>Framerate Cap</string> 57 <string>Use global framerate cap</string>
36 </property> 58 </property>
37 <property name="toolTip"> 59 </item>
38 <string>Requires the use of the FPS Limiter Toggle hotkey to take effect.</string> 60 <item>
61 <property name="text">
62 <string>Set framerate cap:</string>
39 </property> 63 </property>
64 </item>
40 </widget> 65 </widget>
41 </item> 66 </item>
42 <item> 67 <item>
43 <widget class="QSpinBox" name="fps_cap"> 68 <widget class="QLabel" name="fps_cap_label">
44 <property name="suffix"> 69 <property name="toolTip">
45 <string>x</string> 70 <string>Requires the use of the FPS Limiter Toggle hotkey to take effect.</string>
46 </property> 71 </property>
47 <property name="minimum"> 72 <property name="text">
48 <number>1</number> 73 <string>Framerate Cap</string>
49 </property> 74 </property>
50 <property name="maximum">
51 <number>1000</number>
52 </property>
53 <property name="value">
54 <number>500</number>
55 </property>
56 </widget> 75 </widget>
57 </item> 76 </item>
77 <item>
78 <spacer name="horizontalSpacer">
79 <property name="orientation">
80 <enum>Qt::Horizontal</enum>
81 </property>
82 <property name="sizeHint" stdset="0">
83 <size>
84 <width>40</width>
85 <height>20</height>
86 </size>
87 </property>
88 </spacer>
89 </item>
90 </layout>
91 </item>
92 <item>
93 <widget class="QSpinBox" name="fps_cap">
94 <property name="suffix">
95 <string>x</string>
96 </property>
97 <property name="minimum">
98 <number>1</number>
99 </property>
100 <property name="maximum">
101 <number>1000</number>
102 </property>
103 <property name="value">
104 <number>500</number>
105 </property>
106 </widget>
107 </item>
58 </layout> 108 </layout>
109 </widget>
59 </item> 110 </item>
60 <item> 111 <item>
61 <layout class="QHBoxLayout" name="horizontalLayout_2"> 112 <layout class="QHBoxLayout" name="horizontalLayout_2">
diff --git a/src/yuzu/configuration/configure_graphics.ui b/src/yuzu/configuration/configure_graphics.ui
index 660b68c1c..9241678e4 100644
--- a/src/yuzu/configuration/configure_graphics.ui
+++ b/src/yuzu/configuration/configure_graphics.ui
@@ -429,7 +429,7 @@
429 </item> 429 </item>
430 <item> 430 <item>
431 <property name="text"> 431 <property name="text">
432 <string>AMD's FidelityFX™️ Super Resolution [Vulkan Only]</string> 432 <string>AMD FidelityFX™️ Super Resolution [Vulkan Only]</string>
433 </property> 433 </property>
434 </item> 434 </item>
435 </widget> 435 </widget>
diff --git a/src/yuzu/configuration/configure_input.cpp b/src/yuzu/configuration/configure_input.cpp
index 1599299db..d53179dbb 100644
--- a/src/yuzu/configuration/configure_input.cpp
+++ b/src/yuzu/configuration/configure_input.cpp
@@ -10,6 +10,8 @@
10#include <QTimer> 10#include <QTimer>
11 11
12#include "core/core.h" 12#include "core/core.h"
13#include "core/hid/emulated_controller.h"
14#include "core/hid/hid_core.h"
13#include "core/hle/service/am/am.h" 15#include "core/hle/service/am/am.h"
14#include "core/hle/service/am/applet_ae.h" 16#include "core/hle/service/am/applet_ae.h"
15#include "core/hle/service/am/applet_oe.h" 17#include "core/hle/service/am/applet_oe.h"
@@ -22,7 +24,6 @@
22#include "yuzu/configuration/configure_input_advanced.h" 24#include "yuzu/configuration/configure_input_advanced.h"
23#include "yuzu/configuration/configure_input_player.h" 25#include "yuzu/configuration/configure_input_player.h"
24#include "yuzu/configuration/configure_motion_touch.h" 26#include "yuzu/configuration/configure_motion_touch.h"
25#include "yuzu/configuration/configure_mouse_advanced.h"
26#include "yuzu/configuration/configure_touchscreen_advanced.h" 27#include "yuzu/configuration/configure_touchscreen_advanced.h"
27#include "yuzu/configuration/configure_vibration.h" 28#include "yuzu/configuration/configure_vibration.h"
28#include "yuzu/configuration/input_profiles.h" 29#include "yuzu/configuration/input_profiles.h"
@@ -75,23 +76,25 @@ ConfigureInput::~ConfigureInput() = default;
75 76
76void ConfigureInput::Initialize(InputCommon::InputSubsystem* input_subsystem, 77void ConfigureInput::Initialize(InputCommon::InputSubsystem* input_subsystem,
77 std::size_t max_players) { 78 std::size_t max_players) {
79 const bool is_powered_on = system.IsPoweredOn();
80 auto& hid_core = system.HIDCore();
78 player_controllers = { 81 player_controllers = {
79 new ConfigureInputPlayer(this, 0, ui->consoleInputSettings, input_subsystem, profiles.get(), 82 new ConfigureInputPlayer(this, 0, ui->consoleInputSettings, input_subsystem, profiles.get(),
80 system), 83 hid_core, is_powered_on),
81 new ConfigureInputPlayer(this, 1, ui->consoleInputSettings, input_subsystem, profiles.get(), 84 new ConfigureInputPlayer(this, 1, ui->consoleInputSettings, input_subsystem, profiles.get(),
82 system), 85 hid_core, is_powered_on),
83 new ConfigureInputPlayer(this, 2, ui->consoleInputSettings, input_subsystem, profiles.get(), 86 new ConfigureInputPlayer(this, 2, ui->consoleInputSettings, input_subsystem, profiles.get(),
84 system), 87 hid_core, is_powered_on),
85 new ConfigureInputPlayer(this, 3, ui->consoleInputSettings, input_subsystem, profiles.get(), 88 new ConfigureInputPlayer(this, 3, ui->consoleInputSettings, input_subsystem, profiles.get(),
86 system), 89 hid_core, is_powered_on),
87 new ConfigureInputPlayer(this, 4, ui->consoleInputSettings, input_subsystem, profiles.get(), 90 new ConfigureInputPlayer(this, 4, ui->consoleInputSettings, input_subsystem, profiles.get(),
88 system), 91 hid_core, is_powered_on),
89 new ConfigureInputPlayer(this, 5, ui->consoleInputSettings, input_subsystem, profiles.get(), 92 new ConfigureInputPlayer(this, 5, ui->consoleInputSettings, input_subsystem, profiles.get(),
90 system), 93 hid_core, is_powered_on),
91 new ConfigureInputPlayer(this, 6, ui->consoleInputSettings, input_subsystem, profiles.get(), 94 new ConfigureInputPlayer(this, 6, ui->consoleInputSettings, input_subsystem, profiles.get(),
92 system), 95 hid_core, is_powered_on),
93 new ConfigureInputPlayer(this, 7, ui->consoleInputSettings, input_subsystem, profiles.get(), 96 new ConfigureInputPlayer(this, 7, ui->consoleInputSettings, input_subsystem, profiles.get(),
94 system), 97 hid_core, is_powered_on),
95 }; 98 };
96 99
97 player_tabs = { 100 player_tabs = {
@@ -114,6 +117,7 @@ void ConfigureInput::Initialize(InputCommon::InputSubsystem* input_subsystem,
114 player_tabs[i]->setLayout(new QHBoxLayout(player_tabs[i])); 117 player_tabs[i]->setLayout(new QHBoxLayout(player_tabs[i]));
115 player_tabs[i]->layout()->addWidget(player_controllers[i]); 118 player_tabs[i]->layout()->addWidget(player_controllers[i]);
116 connect(player_controllers[i], &ConfigureInputPlayer::Connected, [&, i](bool is_connected) { 119 connect(player_controllers[i], &ConfigureInputPlayer::Connected, [&, i](bool is_connected) {
120 // Ensures that the controllers are always connected in sequential order
117 if (is_connected) { 121 if (is_connected) {
118 for (std::size_t index = 0; index <= i; ++index) { 122 for (std::size_t index = 0; index <= i; ++index) {
119 player_connected[index]->setChecked(is_connected); 123 player_connected[index]->setChecked(is_connected);
@@ -146,13 +150,12 @@ void ConfigureInput::Initialize(InputCommon::InputSubsystem* input_subsystem,
146 advanced = new ConfigureInputAdvanced(this); 150 advanced = new ConfigureInputAdvanced(this);
147 ui->tabAdvanced->setLayout(new QHBoxLayout(ui->tabAdvanced)); 151 ui->tabAdvanced->setLayout(new QHBoxLayout(ui->tabAdvanced));
148 ui->tabAdvanced->layout()->addWidget(advanced); 152 ui->tabAdvanced->layout()->addWidget(advanced);
149 connect(advanced, &ConfigureInputAdvanced::CallDebugControllerDialog, [this, input_subsystem] { 153
150 CallConfigureDialog<ConfigureDebugController>(*this, input_subsystem, profiles.get(), 154 connect(advanced, &ConfigureInputAdvanced::CallDebugControllerDialog,
151 system); 155 [this, input_subsystem, &hid_core, is_powered_on] {
152 }); 156 CallConfigureDialog<ConfigureDebugController>(
153 connect(advanced, &ConfigureInputAdvanced::CallMouseConfigDialog, [this, input_subsystem] { 157 *this, input_subsystem, profiles.get(), hid_core, is_powered_on);
154 CallConfigureDialog<ConfigureMouseAdvanced>(*this, input_subsystem); 158 });
155 });
156 connect(advanced, &ConfigureInputAdvanced::CallTouchscreenConfigDialog, 159 connect(advanced, &ConfigureInputAdvanced::CallTouchscreenConfigDialog,
157 [this] { CallConfigureDialog<ConfigureTouchscreenAdvanced>(*this); }); 160 [this] { CallConfigureDialog<ConfigureTouchscreenAdvanced>(*this); });
158 connect(advanced, &ConfigureInputAdvanced::CallMotionTouchConfigDialog, 161 connect(advanced, &ConfigureInputAdvanced::CallMotionTouchConfigDialog,
@@ -184,22 +187,8 @@ QList<QWidget*> ConfigureInput::GetSubTabs() const {
184void ConfigureInput::ApplyConfiguration() { 187void ConfigureInput::ApplyConfiguration() {
185 for (auto* controller : player_controllers) { 188 for (auto* controller : player_controllers) {
186 controller->ApplyConfiguration(); 189 controller->ApplyConfiguration();
187 controller->TryDisconnectSelectedController();
188 }
189
190 // This emulates a delay between disconnecting and reconnecting controllers as some games
191 // do not respond to a change in controller type if it was instantaneous.
192 using namespace std::chrono_literals;
193 std::this_thread::sleep_for(150ms);
194
195 for (auto* controller : player_controllers) {
196 controller->TryConnectSelectedController();
197 } 190 }
198 191
199 // This emulates a delay between disconnecting and reconnecting controllers as some games
200 // do not respond to a change in controller type if it was instantaneous.
201 std::this_thread::sleep_for(150ms);
202
203 advanced->ApplyConfiguration(); 192 advanced->ApplyConfiguration();
204 193
205 const bool pre_docked_mode = Settings::values.use_docked_mode.GetValue(); 194 const bool pre_docked_mode = Settings::values.use_docked_mode.GetValue();
@@ -223,8 +212,10 @@ void ConfigureInput::RetranslateUI() {
223} 212}
224 213
225void ConfigureInput::LoadConfiguration() { 214void ConfigureInput::LoadConfiguration() {
215 const auto* handheld = system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Handheld);
216
226 LoadPlayerControllerIndices(); 217 LoadPlayerControllerIndices();
227 UpdateDockedState(Settings::values.players.GetValue()[8].connected); 218 UpdateDockedState(handheld->IsConnected());
228 219
229 ui->vibrationGroup->setChecked(Settings::values.vibration_enabled.GetValue()); 220 ui->vibrationGroup->setChecked(Settings::values.vibration_enabled.GetValue());
230 ui->motionGroup->setChecked(Settings::values.motion_enabled.GetValue()); 221 ui->motionGroup->setChecked(Settings::values.motion_enabled.GetValue());
@@ -232,9 +223,16 @@ void ConfigureInput::LoadConfiguration() {
232 223
233void ConfigureInput::LoadPlayerControllerIndices() { 224void ConfigureInput::LoadPlayerControllerIndices() {
234 for (std::size_t i = 0; i < player_connected.size(); ++i) { 225 for (std::size_t i = 0; i < player_connected.size(); ++i) {
235 const auto connected = Settings::values.players.GetValue()[i].connected || 226 if (i == 0) {
236 (i == 0 && Settings::values.players.GetValue()[8].connected); 227 auto* handheld =
237 player_connected[i]->setChecked(connected); 228 system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Handheld);
229 if (handheld->IsConnected()) {
230 player_connected[i]->setChecked(true);
231 continue;
232 }
233 }
234 const auto* controller = system.HIDCore().GetEmulatedControllerByIndex(i);
235 player_connected[i]->setChecked(controller->IsConnected());
238 } 236 }
239} 237}
240 238
diff --git a/src/yuzu/configuration/configure_input_advanced.cpp b/src/yuzu/configuration/configure_input_advanced.cpp
index b30f09013..65c8e59ac 100644
--- a/src/yuzu/configuration/configure_input_advanced.cpp
+++ b/src/yuzu/configuration/configure_input_advanced.cpp
@@ -82,7 +82,6 @@ ConfigureInputAdvanced::ConfigureInputAdvanced(QWidget* parent)
82 82
83 connect(ui->debug_configure, &QPushButton::clicked, this, 83 connect(ui->debug_configure, &QPushButton::clicked, this,
84 [this] { CallDebugControllerDialog(); }); 84 [this] { CallDebugControllerDialog(); });
85 connect(ui->mouse_advanced, &QPushButton::clicked, this, [this] { CallMouseConfigDialog(); });
86 connect(ui->touchscreen_advanced, &QPushButton::clicked, this, 85 connect(ui->touchscreen_advanced, &QPushButton::clicked, this,
87 [this] { CallTouchscreenConfigDialog(); }); 86 [this] { CallTouchscreenConfigDialog(); });
88 connect(ui->buttonMotionTouch, &QPushButton::clicked, this, 87 connect(ui->buttonMotionTouch, &QPushButton::clicked, this,
@@ -131,6 +130,7 @@ void ConfigureInputAdvanced::ApplyConfiguration() {
131 static_cast<float>(ui->mouse_panning_sensitivity->value()); 130 static_cast<float>(ui->mouse_panning_sensitivity->value());
132 Settings::values.touchscreen.enabled = ui->touchscreen_enabled->isChecked(); 131 Settings::values.touchscreen.enabled = ui->touchscreen_enabled->isChecked();
133 Settings::values.enable_raw_input = ui->enable_raw_input->isChecked(); 132 Settings::values.enable_raw_input = ui->enable_raw_input->isChecked();
133 Settings::values.enable_udp_controller = ui->enable_udp_controller->isChecked();
134} 134}
135 135
136void ConfigureInputAdvanced::LoadConfiguration() { 136void ConfigureInputAdvanced::LoadConfiguration() {
@@ -161,6 +161,7 @@ void ConfigureInputAdvanced::LoadConfiguration() {
161 ui->mouse_panning_sensitivity->setValue(Settings::values.mouse_panning_sensitivity.GetValue()); 161 ui->mouse_panning_sensitivity->setValue(Settings::values.mouse_panning_sensitivity.GetValue());
162 ui->touchscreen_enabled->setChecked(Settings::values.touchscreen.enabled); 162 ui->touchscreen_enabled->setChecked(Settings::values.touchscreen.enabled);
163 ui->enable_raw_input->setChecked(Settings::values.enable_raw_input.GetValue()); 163 ui->enable_raw_input->setChecked(Settings::values.enable_raw_input.GetValue());
164 ui->enable_udp_controller->setChecked(Settings::values.enable_udp_controller.GetValue());
164 165
165 UpdateUIEnabled(); 166 UpdateUIEnabled();
166} 167}
@@ -178,7 +179,8 @@ void ConfigureInputAdvanced::RetranslateUI() {
178} 179}
179 180
180void ConfigureInputAdvanced::UpdateUIEnabled() { 181void ConfigureInputAdvanced::UpdateUIEnabled() {
181 ui->mouse_advanced->setEnabled(ui->mouse_enabled->isChecked());
182 ui->debug_configure->setEnabled(ui->debug_enabled->isChecked()); 182 ui->debug_configure->setEnabled(ui->debug_enabled->isChecked());
183 ui->touchscreen_advanced->setEnabled(ui->touchscreen_enabled->isChecked()); 183 ui->touchscreen_advanced->setEnabled(ui->touchscreen_enabled->isChecked());
184 ui->mouse_panning->setEnabled(!ui->mouse_enabled->isChecked());
185 ui->mouse_panning_sensitivity->setEnabled(!ui->mouse_enabled->isChecked());
184} 186}
diff --git a/src/yuzu/configuration/configure_input_advanced.ui b/src/yuzu/configuration/configure_input_advanced.ui
index 9095206a0..df0e4d602 100644
--- a/src/yuzu/configuration/configure_input_advanced.ui
+++ b/src/yuzu/configuration/configure_input_advanced.ui
@@ -2528,11 +2528,11 @@
2528 <number>0</number> 2528 <number>0</number>
2529 </property> 2529 </property>
2530 <item> 2530 <item>
2531 <widget class="QGroupBox" name="gridGroupBox_3"> 2531 <widget class="QGroupBox" name="emulatedDevicesGroupBox">
2532 <property name="title"> 2532 <property name="title">
2533 <string>Other</string> 2533 <string>Emulated Devices</string>
2534 </property> 2534 </property>
2535 <layout class="QGridLayout" name="gridLayout_3"> 2535 <layout class="QGridLayout" name="emulatedDevicesGridLayout">
2536 <item row="0" column="0"> 2536 <item row="0" column="0">
2537 <widget class="QCheckBox" name="keyboard_enabled"> 2537 <widget class="QCheckBox" name="keyboard_enabled">
2538 <property name="minimumSize"> 2538 <property name="minimumSize">
@@ -2547,7 +2547,7 @@
2547 </widget> 2547 </widget>
2548 </item> 2548 </item>
2549 <item row="1" column="0"> 2549 <item row="1" column="0">
2550 <widget class="QCheckBox" name="emulate_analog_keyboard"> 2550 <widget class="QCheckBox" name="mouse_enabled">
2551 <property name="minimumSize"> 2551 <property name="minimumSize">
2552 <size> 2552 <size>
2553 <width>0</width> 2553 <width>0</width>
@@ -2555,53 +2555,18 @@
2555 </size> 2555 </size>
2556 </property> 2556 </property>
2557 <property name="text"> 2557 <property name="text">
2558 <string>Emulate Analog with Keyboard Input</string> 2558 <string>Mouse</string>
2559 </property> 2559 </property>
2560 </widget> 2560 </widget>
2561 </item> 2561 </item>
2562 <item row="2" column="0"> 2562 <item row="2" column="0">
2563 <widget class="QCheckBox" name="mouse_panning"> 2563 <widget class="QCheckBox" name="touchscreen_enabled">
2564 <property name="minimumSize">
2565 <size>
2566 <width>0</width>
2567 <height>23</height>
2568 </size>
2569 </property>
2570 <property name="text"> 2564 <property name="text">
2571 <string>Enable mouse panning</string> 2565 <string>Touchscreen</string>
2572 </property> 2566 </property>
2573 </widget> 2567 </widget>
2574 </item> 2568 </item>
2575 <item row="2" column="2"> 2569 <item row="2" column="1">
2576 <widget class="QSpinBox" name="mouse_panning_sensitivity">
2577 <property name="toolTip">
2578 <string>Mouse sensitivity</string>
2579 </property>
2580 <property name="alignment">
2581 <set>Qt::AlignCenter</set>
2582 </property>
2583 <property name="suffix">
2584 <string>%</string>
2585 </property>
2586 <property name="minimum">
2587 <number>1</number>
2588 </property>
2589 <property name="maximum">
2590 <number>100</number>
2591 </property>
2592 <property name="value">
2593 <number>100</number>
2594 </property>
2595 </widget>
2596 </item>
2597 <item row="6" column="2">
2598 <widget class="QPushButton" name="touchscreen_advanced">
2599 <property name="text">
2600 <string>Advanced</string>
2601 </property>
2602 </widget>
2603 </item>
2604 <item row="3" column="1">
2605 <spacer name="horizontalSpacer_8"> 2570 <spacer name="horizontalSpacer_8">
2606 <property name="orientation"> 2571 <property name="orientation">
2607 <enum>Qt::Horizontal</enum> 2572 <enum>Qt::Horizontal</enum>
@@ -2617,80 +2582,130 @@
2617 </property> 2582 </property>
2618 </spacer> 2583 </spacer>
2619 </item> 2584 </item>
2620 <item row="3" column="2"> 2585 <item row="2" column="2">
2621 <widget class="QPushButton" name="mouse_advanced"> 2586 <widget class="QPushButton" name="touchscreen_advanced">
2622 <property name="text"> 2587 <property name="text">
2623 <string>Advanced</string> 2588 <string>Advanced</string>
2624 </property> 2589 </property>
2625 </widget> 2590 </widget>
2626 </item> 2591 </item>
2627 <item row="6" column="0">
2628 <widget class="QCheckBox" name="touchscreen_enabled">
2629 <property name="text">
2630 <string>Touchscreen</string>
2631 </property>
2632 </widget>
2633 </item>
2634 <item row="3" column="0"> 2592 <item row="3" column="0">
2635 <widget class="QCheckBox" name="mouse_enabled">
2636 <property name="minimumSize">
2637 <size>
2638 <width>0</width>
2639 <height>23</height>
2640 </size>
2641 </property>
2642 <property name="text">
2643 <string>Mouse</string>
2644 </property>
2645 </widget>
2646 </item>
2647 <item row="8" column="0">
2648 <widget class="QLabel" name="motion_touch">
2649 <property name="text">
2650 <string>Motion / Touch</string>
2651 </property>
2652 </widget>
2653 </item>
2654 <item row="8" column="2">
2655 <widget class="QPushButton" name="buttonMotionTouch">
2656 <property name="text">
2657 <string>Configure</string>
2658 </property>
2659 </widget>
2660 </item>
2661 <item row="7" column="0">
2662 <widget class="QCheckBox" name="debug_enabled"> 2593 <widget class="QCheckBox" name="debug_enabled">
2663 <property name="text"> 2594 <property name="text">
2664 <string>Debug Controller</string> 2595 <string>Debug Controller</string>
2665 </property> 2596 </property>
2666 </widget> 2597 </widget>
2667 </item> 2598 </item>
2668 <item row="7" column="2"> 2599 <item row="3" column="2">
2669 <widget class="QPushButton" name="debug_configure"> 2600 <widget class="QPushButton" name="debug_configure">
2670 <property name="text"> 2601 <property name="text">
2671 <string>Configure</string> 2602 <string>Configure</string>
2672 </property> 2603 </property>
2673 </widget> 2604 </widget>
2674 </item> 2605 </item>
2675 <item row="9" column="0">
2676 <widget class="QCheckBox" name="enable_raw_input">
2677 <property name="toolTip">
2678 <string>Requires restarting yuzu</string>
2679 </property>
2680 <property name="minimumSize">
2681 <size>
2682 <width>0</width>
2683 <height>23</height>
2684 </size>
2685 </property>
2686 <property name="text">
2687 <string>Enable XInput 8 player support (disables web applet)</string>
2688 </property>
2689 </widget>
2690 </item>
2691 </layout> 2606 </layout>
2692 </widget> 2607 </widget>
2693 </item> 2608 </item>
2609 <item>
2610 <widget class="QGroupBox" name="otherGroupBox">
2611 <property name="title">
2612 <string>Other</string>
2613 </property>
2614 <layout class="QGridLayout" name="OtherGridLayout">
2615 <item row="1" column="0">
2616 <widget class="QCheckBox" name="emulate_analog_keyboard">
2617 <property name="minimumSize">
2618 <size>
2619 <width>0</width>
2620 <height>23</height>
2621 </size>
2622 </property>
2623 <property name="text">
2624 <string>Emulate Analog with Keyboard Input</string>
2625 </property>
2626 </widget>
2627 </item>
2628 <item row="2" column="0">
2629 <widget class="QCheckBox" name="enable_raw_input">
2630 <property name="toolTip">
2631 <string>Requires restarting yuzu</string>
2632 </property>
2633 <property name="minimumSize">
2634 <size>
2635 <width>0</width>
2636 <height>23</height>
2637 </size>
2638 </property>
2639 <property name="text">
2640 <string>Enable XInput 8 player support (disables web applet)</string>
2641 </property>
2642 </widget>
2643 </item>
2644 <item row="3" column="0">
2645 <widget class="QCheckBox" name="enable_udp_controller">
2646 <property name="minimumSize">
2647 <size>
2648 <width>0</width>
2649 <height>23</height>
2650 </size>
2651 </property>
2652 <property name="text">
2653 <string>Enable UDP controllers (not needed for motion)</string>
2654 </property>
2655 </widget>
2656 </item>
2657 <item row="4" column="0">
2658 <widget class="QCheckBox" name="mouse_panning">
2659 <property name="minimumSize">
2660 <size>
2661 <width>0</width>
2662 <height>23</height>
2663 </size>
2664 </property>
2665 <property name="text">
2666 <string>Enable mouse panning</string>
2667 </property>
2668 </widget>
2669 </item>
2670 <item row="4" column="2">
2671 <widget class="QSpinBox" name="mouse_panning_sensitivity">
2672 <property name="toolTip">
2673 <string>Mouse sensitivity</string>
2674 </property>
2675 <property name="alignment">
2676 <set>Qt::AlignCenter</set>
2677 </property>
2678 <property name="suffix">
2679 <string>%</string>
2680 </property>
2681 <property name="minimum">
2682 <number>1</number>
2683 </property>
2684 <property name="maximum">
2685 <number>100</number>
2686 </property>
2687 <property name="value">
2688 <number>100</number>
2689 </property>
2690 </widget>
2691 </item>
2692 <item row="5" column="0">
2693 <widget class="QLabel" name="motion_touch">
2694 <property name="text">
2695 <string>Motion / Touch</string>
2696 </property>
2697 </widget>
2698 </item>
2699 <item row="5" column="2">
2700 <widget class="QPushButton" name="buttonMotionTouch">
2701 <property name="text">
2702 <string>Configure</string>
2703 </property>
2704 </widget>
2705 </item>
2706 </layout>
2707 </widget>
2708 </item>
2694 <item> 2709 <item>
2695 <spacer name="verticalSpacer"> 2710 <spacer name="verticalSpacer">
2696 <property name="orientation"> 2711 <property name="orientation">
diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp
index 3aab5d5f8..8a8be8e40 100644
--- a/src/yuzu/configuration/configure_input_player.cpp
+++ b/src/yuzu/configuration/configure_input_player.cpp
@@ -12,14 +12,12 @@
12#include <QMessageBox> 12#include <QMessageBox>
13#include <QTimer> 13#include <QTimer>
14#include "common/param_package.h" 14#include "common/param_package.h"
15#include "core/core.h" 15#include "core/hid/emulated_controller.h"
16#include "core/hle/service/hid/controllers/npad.h" 16#include "core/hid/hid_core.h"
17#include "core/hle/service/hid/hid.h" 17#include "core/hid/hid_types.h"
18#include "core/hle/service/sm/sm.h" 18#include "input_common/drivers/keyboard.h"
19#include "input_common/gcadapter/gc_poller.h" 19#include "input_common/drivers/mouse.h"
20#include "input_common/main.h" 20#include "input_common/main.h"
21#include "input_common/mouse/mouse_poller.h"
22#include "input_common/udp/udp.h"
23#include "ui_configure_input_player.h" 21#include "ui_configure_input_player.h"
24#include "yuzu/bootmanager.h" 22#include "yuzu/bootmanager.h"
25#include "yuzu/configuration/config.h" 23#include "yuzu/configuration/config.h"
@@ -29,8 +27,6 @@
29#include "yuzu/configuration/input_profiles.h" 27#include "yuzu/configuration/input_profiles.h"
30#include "yuzu/util/limitable_input_dialog.h" 28#include "yuzu/util/limitable_input_dialog.h"
31 29
32using namespace Service::HID;
33
34const std::array<std::string, ConfigureInputPlayer::ANALOG_SUB_BUTTONS_NUM> 30const std::array<std::string, ConfigureInputPlayer::ANALOG_SUB_BUTTONS_NUM>
35 ConfigureInputPlayer::analog_sub_buttons{{ 31 ConfigureInputPlayer::analog_sub_buttons{{
36 "up", 32 "up",
@@ -41,33 +37,8 @@ const std::array<std::string, ConfigureInputPlayer::ANALOG_SUB_BUTTONS_NUM>
41 37
42namespace { 38namespace {
43 39
44constexpr std::size_t HANDHELD_INDEX = 8;
45
46void UpdateController(Settings::ControllerType controller_type, std::size_t npad_index,
47 bool connected, Core::System& system) {
48 if (!system.IsPoweredOn()) {
49 return;
50 }
51 Service::SM::ServiceManager& sm = system.ServiceManager();
52
53 auto& npad = sm.GetService<Hid>("hid")->GetAppletResource()->GetController<Controller_NPad>(
54 HidController::NPad);
55
56 npad.UpdateControllerAt(npad.MapSettingsTypeToNPad(controller_type), npad_index, connected);
57}
58
59QString GetKeyName(int key_code) { 40QString GetKeyName(int key_code) {
60 switch (key_code) { 41 switch (key_code) {
61 case Qt::LeftButton:
62 return QObject::tr("Click 0");
63 case Qt::RightButton:
64 return QObject::tr("Click 1");
65 case Qt::MiddleButton:
66 return QObject::tr("Click 2");
67 case Qt::BackButton:
68 return QObject::tr("Click 3");
69 case Qt::ForwardButton:
70 return QObject::tr("Click 4");
71 case Qt::Key_Shift: 42 case Qt::Key_Shift:
72 return QObject::tr("Shift"); 43 return QObject::tr("Shift");
73 case Qt::Key_Control: 44 case Qt::Key_Control:
@@ -81,6 +52,61 @@ QString GetKeyName(int key_code) {
81 } 52 }
82} 53}
83 54
55QString GetButtonName(Common::Input::ButtonNames button_name) {
56 switch (button_name) {
57 case Common::Input::ButtonNames::ButtonLeft:
58 return QObject::tr("Left");
59 case Common::Input::ButtonNames::ButtonRight:
60 return QObject::tr("Right");
61 case Common::Input::ButtonNames::ButtonDown:
62 return QObject::tr("Down");
63 case Common::Input::ButtonNames::ButtonUp:
64 return QObject::tr("Up");
65 case Common::Input::ButtonNames::TriggerZ:
66 return QObject::tr("Z");
67 case Common::Input::ButtonNames::TriggerR:
68 return QObject::tr("R");
69 case Common::Input::ButtonNames::TriggerL:
70 return QObject::tr("L");
71 case Common::Input::ButtonNames::ButtonA:
72 return QObject::tr("A");
73 case Common::Input::ButtonNames::ButtonB:
74 return QObject::tr("B");
75 case Common::Input::ButtonNames::ButtonX:
76 return QObject::tr("X");
77 case Common::Input::ButtonNames::ButtonY:
78 return QObject::tr("Y");
79 case Common::Input::ButtonNames::ButtonStart:
80 return QObject::tr("Start");
81 case Common::Input::ButtonNames::L1:
82 return QObject::tr("L1");
83 case Common::Input::ButtonNames::L2:
84 return QObject::tr("L2");
85 case Common::Input::ButtonNames::L3:
86 return QObject::tr("L3");
87 case Common::Input::ButtonNames::R1:
88 return QObject::tr("R1");
89 case Common::Input::ButtonNames::R2:
90 return QObject::tr("R2");
91 case Common::Input::ButtonNames::R3:
92 return QObject::tr("R3");
93 case Common::Input::ButtonNames::Circle:
94 return QObject::tr("Circle");
95 case Common::Input::ButtonNames::Cross:
96 return QObject::tr("Cross");
97 case Common::Input::ButtonNames::Square:
98 return QObject::tr("Square");
99 case Common::Input::ButtonNames::Triangle:
100 return QObject::tr("Triangle");
101 case Common::Input::ButtonNames::Share:
102 return QObject::tr("Share");
103 case Common::Input::ButtonNames::Options:
104 return QObject::tr("Options");
105 default:
106 return QObject::tr("[undefined]");
107 }
108}
109
84void SetAnalogParam(const Common::ParamPackage& input_param, Common::ParamPackage& analog_param, 110void SetAnalogParam(const Common::ParamPackage& input_param, Common::ParamPackage& analog_param,
85 const std::string& button_name) { 111 const std::string& button_name) {
86 // The poller returned a complete axis, so set all the buttons 112 // The poller returned a complete axis, so set all the buttons
@@ -97,95 +123,75 @@ void SetAnalogParam(const Common::ParamPackage& input_param, Common::ParamPackag
97 } 123 }
98 analog_param.Set(button_name, input_param.Serialize()); 124 analog_param.Set(button_name, input_param.Serialize());
99} 125}
126} // namespace
100 127
101QString ButtonToText(const Common::ParamPackage& param) { 128QString ConfigureInputPlayer::ButtonToText(const Common::ParamPackage& param) {
102 if (!param.Has("engine")) { 129 if (!param.Has("engine")) {
103 return QObject::tr("[not set]"); 130 return QObject::tr("[not set]");
104 } 131 }
105 132
133 const QString toggle = QString::fromStdString(param.Get("toggle", false) ? "~" : "");
134 const QString inverted = QString::fromStdString(param.Get("inverted", false) ? "!" : "");
135 const auto common_button_name = input_subsystem->GetButtonName(param);
136
137 // Retrieve the names from Qt
106 if (param.Get("engine", "") == "keyboard") { 138 if (param.Get("engine", "") == "keyboard") {
107 const QString button_str = GetKeyName(param.Get("code", 0)); 139 const QString button_str = GetKeyName(param.Get("code", 0));
108 const QString toggle = QString::fromStdString(param.Get("toggle", false) ? "~" : "");
109 return QObject::tr("%1%2").arg(toggle, button_str); 140 return QObject::tr("%1%2").arg(toggle, button_str);
110 } 141 }
111 142
112 if (param.Get("engine", "") == "gcpad") { 143 if (common_button_name == Common::Input::ButtonNames::Invalid) {
113 if (param.Has("axis")) { 144 return QObject::tr("[invalid]");
114 const QString axis_str = QString::fromStdString(param.Get("axis", ""));
115 const QString direction_str = QString::fromStdString(param.Get("direction", ""));
116
117 return QObject::tr("GC Axis %1%2").arg(axis_str, direction_str);
118 }
119 if (param.Has("button")) {
120 const QString button_str = QString::number(int(std::log2(param.Get("button", 0))));
121 return QObject::tr("GC Button %1").arg(button_str);
122 }
123 return GetKeyName(param.Get("code", 0));
124 } 145 }
125 146
126 if (param.Get("engine", "") == "tas") { 147 if (common_button_name == Common::Input::ButtonNames::Engine) {
127 if (param.Has("axis")) { 148 return QString::fromStdString(param.Get("engine", ""));
128 const QString axis_str = QString::fromStdString(param.Get("axis", ""));
129
130 return QObject::tr("TAS Axis %1").arg(axis_str);
131 }
132 if (param.Has("button")) {
133 const QString button_str = QString::number(int(std::log2(param.Get("button", 0))));
134 return QObject::tr("TAS Btn %1").arg(button_str);
135 }
136 return GetKeyName(param.Get("code", 0));
137 }
138
139 if (param.Get("engine", "") == "cemuhookudp") {
140 if (param.Has("pad_index")) {
141 const QString motion_str = QString::fromStdString(param.Get("pad_index", ""));
142 return QObject::tr("Motion %1").arg(motion_str);
143 }
144 return GetKeyName(param.Get("code", 0));
145 } 149 }
146 150
147 if (param.Get("engine", "") == "sdl") { 151 if (common_button_name == Common::Input::ButtonNames::Value) {
148 if (param.Has("hat")) { 152 if (param.Has("hat")) {
149 const QString hat_str = QString::fromStdString(param.Get("hat", "")); 153 const QString hat = QString::fromStdString(param.Get("direction", ""));
150 const QString direction_str = QString::fromStdString(param.Get("direction", "")); 154 return QObject::tr("%1%2Hat %3").arg(toggle, inverted, hat);
151
152 return QObject::tr("Hat %1 %2").arg(hat_str, direction_str);
153 } 155 }
154
155 if (param.Has("axis")) { 156 if (param.Has("axis")) {
156 const QString axis_str = QString::fromStdString(param.Get("axis", "")); 157 const QString axis = QString::fromStdString(param.Get("axis", ""));
157 const QString direction_str = QString::fromStdString(param.Get("direction", "")); 158 return QObject::tr("%1%2Axis %3").arg(toggle, inverted, axis);
158
159 return QObject::tr("Axis %1%2").arg(axis_str, direction_str);
160 } 159 }
161 160 if (param.Has("axis_x") && param.Has("axis_y") && param.Has("axis_z")) {
162 if (param.Has("button")) { 161 const QString axis_x = QString::fromStdString(param.Get("axis_x", ""));
163 const QString button_str = QString::fromStdString(param.Get("button", "")); 162 const QString axis_y = QString::fromStdString(param.Get("axis_y", ""));
164 const QString toggle = QString::fromStdString(param.Get("toggle", false) ? "~" : ""); 163 const QString axis_z = QString::fromStdString(param.Get("axis_z", ""));
165 164 return QObject::tr("%1%2Axis %3,%4,%5").arg(toggle, inverted, axis_x, axis_y, axis_z);
166 return QObject::tr("%1Button %2").arg(toggle, button_str);
167 } 165 }
168
169 if (param.Has("motion")) { 166 if (param.Has("motion")) {
170 return QObject::tr("SDL Motion"); 167 const QString motion = QString::fromStdString(param.Get("motion", ""));
168 return QObject::tr("%1%2Motion %3").arg(toggle, inverted, motion);
171 } 169 }
172
173 return {};
174 }
175
176 if (param.Get("engine", "") == "mouse") {
177 if (param.Has("button")) { 170 if (param.Has("button")) {
178 const QString button_str = QString::number(int(param.Get("button", 0))); 171 const QString button = QString::fromStdString(param.Get("button", ""));
179 const QString toggle = QString::fromStdString(param.Get("toggle", false) ? "~" : ""); 172 return QObject::tr("%1%2Button %3").arg(toggle, inverted, button);
180 return QObject::tr("%1Click %2").arg(toggle, button_str);
181 } 173 }
182 return GetKeyName(param.Get("code", 0)); 174 }
175
176 QString button_name = GetButtonName(common_button_name);
177 if (param.Has("hat")) {
178 return QObject::tr("%1%2Hat %3").arg(toggle, inverted, button_name);
179 }
180 if (param.Has("axis")) {
181 return QObject::tr("%1%2Axis %3").arg(toggle, inverted, button_name);
182 }
183 if (param.Has("motion")) {
184 return QObject::tr("%1%2Axis %3").arg(toggle, inverted, button_name);
185 }
186 if (param.Has("button")) {
187 return QObject::tr("%1%2Button %3").arg(toggle, inverted, button_name);
183 } 188 }
184 189
185 return QObject::tr("[unknown]"); 190 return QObject::tr("[unknown]");
186} 191}
187 192
188QString AnalogToText(const Common::ParamPackage& param, const std::string& dir) { 193QString ConfigureInputPlayer::AnalogToText(const Common::ParamPackage& param,
194 const std::string& dir) {
189 if (!param.Has("engine")) { 195 if (!param.Has("engine")) {
190 return QObject::tr("[not set]"); 196 return QObject::tr("[not set]");
191 } 197 }
@@ -194,49 +200,69 @@ QString AnalogToText(const Common::ParamPackage& param, const std::string& dir)
194 return ButtonToText(Common::ParamPackage{param.Get(dir, "")}); 200 return ButtonToText(Common::ParamPackage{param.Get(dir, "")});
195 } 201 }
196 202
203 if (!param.Has("axis_x") || !param.Has("axis_y")) {
204 return QObject::tr("[unknown]");
205 }
206
197 const auto engine_str = param.Get("engine", ""); 207 const auto engine_str = param.Get("engine", "");
198 const QString axis_x_str = QString::fromStdString(param.Get("axis_x", "")); 208 const QString axis_x_str = QString::fromStdString(param.Get("axis_x", ""));
199 const QString axis_y_str = QString::fromStdString(param.Get("axis_y", "")); 209 const QString axis_y_str = QString::fromStdString(param.Get("axis_y", ""));
200 const bool invert_x = param.Get("invert_x", "+") == "-"; 210 const bool invert_x = param.Get("invert_x", "+") == "-";
201 const bool invert_y = param.Get("invert_y", "+") == "-"; 211 const bool invert_y = param.Get("invert_y", "+") == "-";
202 if (engine_str == "sdl" || engine_str == "gcpad" || engine_str == "mouse" ||
203 engine_str == "tas") {
204 if (dir == "modifier") {
205 return QObject::tr("[unused]");
206 }
207 212
208 if (dir == "left") { 213 if (dir == "modifier") {
209 const QString invert_x_str = QString::fromStdString(invert_x ? "+" : "-"); 214 return QObject::tr("[unused]");
210 return QObject::tr("Axis %1%2").arg(axis_x_str, invert_x_str); 215 }
211 }
212 if (dir == "right") {
213 const QString invert_x_str = QString::fromStdString(invert_x ? "-" : "+");
214 return QObject::tr("Axis %1%2").arg(axis_x_str, invert_x_str);
215 }
216 if (dir == "up") {
217 const QString invert_y_str = QString::fromStdString(invert_y ? "-" : "+");
218 return QObject::tr("Axis %1%2").arg(axis_y_str, invert_y_str);
219 }
220 if (dir == "down") {
221 const QString invert_y_str = QString::fromStdString(invert_y ? "+" : "-");
222 return QObject::tr("Axis %1%2").arg(axis_y_str, invert_y_str);
223 }
224 216
225 return {}; 217 if (dir == "left") {
218 const QString invert_x_str = QString::fromStdString(invert_x ? "+" : "-");
219 return QObject::tr("Axis %1%2").arg(axis_x_str, invert_x_str);
226 } 220 }
221 if (dir == "right") {
222 const QString invert_x_str = QString::fromStdString(invert_x ? "-" : "+");
223 return QObject::tr("Axis %1%2").arg(axis_x_str, invert_x_str);
224 }
225 if (dir == "up") {
226 const QString invert_y_str = QString::fromStdString(invert_y ? "-" : "+");
227 return QObject::tr("Axis %1%2").arg(axis_y_str, invert_y_str);
228 }
229 if (dir == "down") {
230 const QString invert_y_str = QString::fromStdString(invert_y ? "+" : "-");
231 return QObject::tr("Axis %1%2").arg(axis_y_str, invert_y_str);
232 }
233
227 return QObject::tr("[unknown]"); 234 return QObject::tr("[unknown]");
228} 235}
229} // namespace
230 236
231ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_index, 237ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_index,
232 QWidget* bottom_row, 238 QWidget* bottom_row,
233 InputCommon::InputSubsystem* input_subsystem_, 239 InputCommon::InputSubsystem* input_subsystem_,
234 InputProfiles* profiles_, Core::System& system_, 240 InputProfiles* profiles_, Core::HID::HIDCore& hid_core_,
235 bool debug) 241 bool is_powered_on_, bool debug)
236 : QWidget(parent), ui(std::make_unique<Ui::ConfigureInputPlayer>()), player_index(player_index), 242 : QWidget(parent), ui(std::make_unique<Ui::ConfigureInputPlayer>()), player_index(player_index),
237 debug(debug), input_subsystem{input_subsystem_}, profiles(profiles_), 243 debug(debug), is_powered_on{is_powered_on_}, input_subsystem{input_subsystem_},
238 timeout_timer(std::make_unique<QTimer>()), poll_timer(std::make_unique<QTimer>()), 244 profiles(profiles_), timeout_timer(std::make_unique<QTimer>()),
239 bottom_row(bottom_row), system{system_} { 245 poll_timer(std::make_unique<QTimer>()), bottom_row(bottom_row), hid_core{hid_core_} {
246 if (player_index == 0) {
247 auto* emulated_controller_p1 =
248 hid_core.GetEmulatedController(Core::HID::NpadIdType::Player1);
249 auto* emulated_controller_handheld =
250 hid_core.GetEmulatedController(Core::HID::NpadIdType::Handheld);
251 emulated_controller_p1->SaveCurrentConfig();
252 emulated_controller_p1->EnableConfiguration();
253 emulated_controller_handheld->SaveCurrentConfig();
254 emulated_controller_handheld->EnableConfiguration();
255 if (emulated_controller_handheld->IsConnected(true)) {
256 emulated_controller_p1->Disconnect();
257 emulated_controller = emulated_controller_handheld;
258 } else {
259 emulated_controller = emulated_controller_p1;
260 }
261 } else {
262 emulated_controller = hid_core.GetEmulatedControllerByIndex(player_index);
263 emulated_controller->SaveCurrentConfig();
264 emulated_controller->EnableConfiguration();
265 }
240 ui->setupUi(this); 266 ui->setupUi(this);
241 267
242 setFocusPolicy(Qt::ClickFocus); 268 setFocusPolicy(Qt::ClickFocus);
@@ -278,31 +304,7 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
278 analog_map_range_groupbox = {ui->buttonLStickRangeGroup, ui->buttonRStickRangeGroup}; 304 analog_map_range_groupbox = {ui->buttonLStickRangeGroup, ui->buttonRStickRangeGroup};
279 analog_map_range_spinbox = {ui->spinboxLStickRange, ui->spinboxRStickRange}; 305 analog_map_range_spinbox = {ui->spinboxLStickRange, ui->spinboxRStickRange};
280 306
281 const auto ConfigureButtonClick = [&](QPushButton* button, std::size_t button_id, 307 ui->controllerFrame->SetController(emulated_controller);
282 Common::ParamPackage* param, int default_val,
283 InputCommon::Polling::DeviceType type) {
284 connect(button, &QPushButton::clicked, [=, this] {
285 HandleClick(
286 button, button_id,
287 [=, this](Common::ParamPackage params) {
288 // Workaround for ZL & ZR for analog triggers like on XBOX
289 // controllers. Analog triggers (from controllers like the XBOX
290 // controller) would not work due to a different range of their
291 // signals (from 0 to 255 on analog triggers instead of -32768 to
292 // 32768 on analog joysticks). The SDL driver misinterprets analog
293 // triggers as analog joysticks.
294 // TODO: reinterpret the signal range for analog triggers to map the
295 // values correctly. This is required for the correct emulation of
296 // the analog triggers of the GameCube controller.
297 if (button == ui->buttonZL || button == ui->buttonZR) {
298 params.Set("direction", "+");
299 params.Set("threshold", "0.5");
300 }
301 *param = std::move(params);
302 },
303 type);
304 });
305 };
306 308
307 for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; ++button_id) { 309 for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; ++button_id) {
308 auto* const button = button_map[button_id]; 310 auto* const button = button_map[button_id];
@@ -311,34 +313,52 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
311 continue; 313 continue;
312 } 314 }
313 315
314 ConfigureButtonClick(button_map[button_id], button_id, &buttons_param[button_id], 316 connect(button, &QPushButton::clicked, [=, this] {
315 Config::default_buttons[button_id], 317 HandleClick(
316 InputCommon::Polling::DeviceType::Button); 318 button, button_id,
319 [=, this](Common::ParamPackage params) {
320 emulated_controller->SetButtonParam(button_id, params);
321 },
322 InputCommon::Polling::InputType::Button);
323 });
317 324
318 button->setContextMenuPolicy(Qt::CustomContextMenu); 325 button->setContextMenuPolicy(Qt::CustomContextMenu);
319 connect(button, &QPushButton::customContextMenuRequested, 326 connect(button, &QPushButton::customContextMenuRequested,
320 [=, this](const QPoint& menu_location) { 327 [=, this](const QPoint& menu_location) {
321 QMenu context_menu; 328 QMenu context_menu;
329 Common::ParamPackage param = emulated_controller->GetButtonParam(button_id);
322 context_menu.addAction(tr("Clear"), [&] { 330 context_menu.addAction(tr("Clear"), [&] {
323 buttons_param[button_id].Clear(); 331 emulated_controller->SetButtonParam(button_id, {});
324 button_map[button_id]->setText(tr("[not set]")); 332 button_map[button_id]->setText(tr("[not set]"));
325 }); 333 });
326 if (buttons_param[button_id].Has("toggle")) { 334 if (param.Has("button") || param.Has("hat")) {
327 context_menu.addAction(tr("Toggle button"), [&] { 335 context_menu.addAction(tr("Toggle button"), [&] {
328 const bool toggle_value = 336 const bool toggle_value = !param.Get("toggle", false);
329 !buttons_param[button_id].Get("toggle", false); 337 param.Set("toggle", toggle_value);
330 buttons_param[button_id].Set("toggle", toggle_value); 338 button_map[button_id]->setText(ButtonToText(param));
331 button_map[button_id]->setText(ButtonToText(buttons_param[button_id])); 339 emulated_controller->SetButtonParam(button_id, param);
340 });
341 context_menu.addAction(tr("Invert button"), [&] {
342 const bool toggle_value = !param.Get("inverted", false);
343 param.Set("inverted", toggle_value);
344 button_map[button_id]->setText(ButtonToText(param));
345 emulated_controller->SetButtonParam(button_id, param);
332 }); 346 });
333 } 347 }
334 if (buttons_param[button_id].Has("threshold")) { 348 if (param.Has("axis")) {
349 context_menu.addAction(tr("Invert axis"), [&] {
350 const bool toggle_value = !(param.Get("invert", "+") == "-");
351 param.Set("invert", toggle_value ? "-" : "+");
352 button_map[button_id]->setText(ButtonToText(param));
353 emulated_controller->SetButtonParam(button_id, param);
354 });
335 context_menu.addAction(tr("Set threshold"), [&] { 355 context_menu.addAction(tr("Set threshold"), [&] {
336 const int button_threshold = static_cast<int>( 356 const int button_threshold =
337 buttons_param[button_id].Get("threshold", 0.5f) * 100.0f); 357 static_cast<int>(param.Get("threshold", 0.5f) * 100.0f);
338 const int new_threshold = QInputDialog::getInt( 358 const int new_threshold = QInputDialog::getInt(
339 this, tr("Set threshold"), tr("Choose a value between 0% and 100%"), 359 this, tr("Set threshold"), tr("Choose a value between 0% and 100%"),
340 button_threshold, 0, 100); 360 button_threshold, 0, 100);
341 buttons_param[button_id].Set("threshold", new_threshold / 100.0f); 361 param.Set("threshold", new_threshold / 100.0f);
342 362
343 if (button_id == Settings::NativeButton::ZL) { 363 if (button_id == Settings::NativeButton::ZL) {
344 ui->sliderZLThreshold->setValue(new_threshold); 364 ui->sliderZLThreshold->setValue(new_threshold);
@@ -346,11 +366,10 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
346 if (button_id == Settings::NativeButton::ZR) { 366 if (button_id == Settings::NativeButton::ZR) {
347 ui->sliderZRThreshold->setValue(new_threshold); 367 ui->sliderZRThreshold->setValue(new_threshold);
348 } 368 }
369 emulated_controller->SetButtonParam(button_id, param);
349 }); 370 });
350 } 371 }
351
352 context_menu.exec(button_map[button_id]->mapToGlobal(menu_location)); 372 context_menu.exec(button_map[button_id]->mapToGlobal(menu_location));
353 ui->controllerFrame->SetPlayerInput(player_index, buttons_param, analogs_param);
354 }); 373 });
355 } 374 }
356 375
@@ -360,9 +379,14 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
360 continue; 379 continue;
361 } 380 }
362 381
363 ConfigureButtonClick(motion_map[motion_id], motion_id, &motions_param[motion_id], 382 connect(button, &QPushButton::clicked, [=, this] {
364 Config::default_motions[motion_id], 383 HandleClick(
365 InputCommon::Polling::DeviceType::Motion); 384 button, motion_id,
385 [=, this](Common::ParamPackage params) {
386 emulated_controller->SetMotionParam(motion_id, params);
387 },
388 InputCommon::Polling::InputType::Motion);
389 });
366 390
367 button->setContextMenuPolicy(Qt::CustomContextMenu); 391 button->setContextMenuPolicy(Qt::CustomContextMenu);
368 392
@@ -370,7 +394,7 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
370 [=, this](const QPoint& menu_location) { 394 [=, this](const QPoint& menu_location) {
371 QMenu context_menu; 395 QMenu context_menu;
372 context_menu.addAction(tr("Clear"), [&] { 396 context_menu.addAction(tr("Clear"), [&] {
373 motions_param[motion_id].Clear(); 397 emulated_controller->SetMotionParam(motion_id, {});
374 motion_map[motion_id]->setText(tr("[not set]")); 398 motion_map[motion_id]->setText(tr("[not set]"));
375 }); 399 });
376 context_menu.exec(motion_map[motion_id]->mapToGlobal(menu_location)); 400 context_menu.exec(motion_map[motion_id]->mapToGlobal(menu_location));
@@ -378,16 +402,22 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
378 } 402 }
379 403
380 connect(ui->sliderZLThreshold, &QSlider::valueChanged, [=, this] { 404 connect(ui->sliderZLThreshold, &QSlider::valueChanged, [=, this] {
381 if (buttons_param[Settings::NativeButton::ZL].Has("threshold")) { 405 Common::ParamPackage param =
406 emulated_controller->GetButtonParam(Settings::NativeButton::ZL);
407 if (param.Has("threshold")) {
382 const auto slider_value = ui->sliderZLThreshold->value(); 408 const auto slider_value = ui->sliderZLThreshold->value();
383 buttons_param[Settings::NativeButton::ZL].Set("threshold", slider_value / 100.0f); 409 param.Set("threshold", slider_value / 100.0f);
410 emulated_controller->SetButtonParam(Settings::NativeButton::ZL, param);
384 } 411 }
385 }); 412 });
386 413
387 connect(ui->sliderZRThreshold, &QSlider::valueChanged, [=, this] { 414 connect(ui->sliderZRThreshold, &QSlider::valueChanged, [=, this] {
388 if (buttons_param[Settings::NativeButton::ZR].Has("threshold")) { 415 Common::ParamPackage param =
416 emulated_controller->GetButtonParam(Settings::NativeButton::ZR);
417 if (param.Has("threshold")) {
389 const auto slider_value = ui->sliderZRThreshold->value(); 418 const auto slider_value = ui->sliderZRThreshold->value();
390 buttons_param[Settings::NativeButton::ZR].Set("threshold", slider_value / 100.0f); 419 param.Set("threshold", slider_value / 100.0f);
420 emulated_controller->SetButtonParam(Settings::NativeButton::ZR, param);
391 } 421 }
392 }); 422 });
393 423
@@ -415,45 +445,45 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
415 HandleClick( 445 HandleClick(
416 analog_map_buttons[analog_id][sub_button_id], analog_id, 446 analog_map_buttons[analog_id][sub_button_id], analog_id,
417 [=, this](const Common::ParamPackage& params) { 447 [=, this](const Common::ParamPackage& params) {
418 SetAnalogParam(params, analogs_param[analog_id], 448 Common::ParamPackage param = emulated_controller->GetStickParam(analog_id);
419 analog_sub_buttons[sub_button_id]); 449 SetAnalogParam(params, param, analog_sub_buttons[sub_button_id]);
450 emulated_controller->SetStickParam(analog_id, param);
420 }, 451 },
421 InputCommon::Polling::DeviceType::AnalogPreferred); 452 InputCommon::Polling::InputType::Stick);
422 }); 453 });
423 454
424 analog_button->setContextMenuPolicy(Qt::CustomContextMenu); 455 analog_button->setContextMenuPolicy(Qt::CustomContextMenu);
425 456
426 connect( 457 connect(analog_button, &QPushButton::customContextMenuRequested,
427 analog_button, &QPushButton::customContextMenuRequested, 458 [=, this](const QPoint& menu_location) {
428 [=, this](const QPoint& menu_location) { 459 QMenu context_menu;
429 QMenu context_menu; 460 Common::ParamPackage param = emulated_controller->GetStickParam(analog_id);
430 context_menu.addAction(tr("Clear"), [&] { 461 context_menu.addAction(tr("Clear"), [&] {
431 analogs_param[analog_id].Clear(); 462 emulated_controller->SetStickParam(analog_id, {});
432 analog_map_buttons[analog_id][sub_button_id]->setText(tr("[not set]")); 463 analog_map_buttons[analog_id][sub_button_id]->setText(tr("[not set]"));
433 }); 464 });
434 context_menu.addAction(tr("Invert axis"), [&] { 465 context_menu.addAction(tr("Invert axis"), [&] {
435 if (sub_button_id == 2 || sub_button_id == 3) { 466 if (sub_button_id == 2 || sub_button_id == 3) {
436 const bool invert_value = 467 const bool invert_value = param.Get("invert_x", "+") == "-";
437 analogs_param[analog_id].Get("invert_x", "+") == "-"; 468 const std::string invert_str = invert_value ? "+" : "-";
438 const std::string invert_str = invert_value ? "+" : "-"; 469 param.Set("invert_x", invert_str);
439 analogs_param[analog_id].Set("invert_x", invert_str); 470 emulated_controller->SetStickParam(analog_id, param);
440 } 471 }
441 if (sub_button_id == 0 || sub_button_id == 1) { 472 if (sub_button_id == 0 || sub_button_id == 1) {
442 const bool invert_value = 473 const bool invert_value = param.Get("invert_y", "+") == "-";
443 analogs_param[analog_id].Get("invert_y", "+") == "-"; 474 const std::string invert_str = invert_value ? "+" : "-";
444 const std::string invert_str = invert_value ? "+" : "-"; 475 param.Set("invert_y", invert_str);
445 analogs_param[analog_id].Set("invert_y", invert_str); 476 emulated_controller->SetStickParam(analog_id, param);
446 } 477 }
447 for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; 478 for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM;
448 ++sub_button_id) { 479 ++sub_button_id) {
449 analog_map_buttons[analog_id][sub_button_id]->setText(AnalogToText( 480 analog_map_buttons[analog_id][sub_button_id]->setText(
450 analogs_param[analog_id], analog_sub_buttons[sub_button_id])); 481 AnalogToText(param, analog_sub_buttons[sub_button_id]));
451 } 482 }
483 });
484 context_menu.exec(analog_map_buttons[analog_id][sub_button_id]->mapToGlobal(
485 menu_location));
452 }); 486 });
453 context_menu.exec(
454 analog_map_buttons[analog_id][sub_button_id]->mapToGlobal(menu_location));
455 ui->controllerFrame->SetPlayerInput(player_index, buttons_param, analogs_param);
456 });
457 } 487 }
458 488
459 // Handle clicks for the modifier buttons as well. 489 // Handle clicks for the modifier buttons as well.
@@ -461,9 +491,11 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
461 HandleClick( 491 HandleClick(
462 analog_map_modifier_button[analog_id], analog_id, 492 analog_map_modifier_button[analog_id], analog_id,
463 [=, this](const Common::ParamPackage& params) { 493 [=, this](const Common::ParamPackage& params) {
464 analogs_param[analog_id].Set("modifier", params.Serialize()); 494 Common::ParamPackage param = emulated_controller->GetStickParam(analog_id);
495 param.Set("modifier", params.Serialize());
496 emulated_controller->SetStickParam(analog_id, param);
465 }, 497 },
466 InputCommon::Polling::DeviceType::Button); 498 InputCommon::Polling::InputType::Button);
467 }); 499 });
468 500
469 analog_map_modifier_button[analog_id]->setContextMenuPolicy(Qt::CustomContextMenu); 501 analog_map_modifier_button[analog_id]->setContextMenuPolicy(Qt::CustomContextMenu);
@@ -471,18 +503,21 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
471 connect(analog_map_modifier_button[analog_id], &QPushButton::customContextMenuRequested, 503 connect(analog_map_modifier_button[analog_id], &QPushButton::customContextMenuRequested,
472 [=, this](const QPoint& menu_location) { 504 [=, this](const QPoint& menu_location) {
473 QMenu context_menu; 505 QMenu context_menu;
506 Common::ParamPackage param = emulated_controller->GetStickParam(analog_id);
474 context_menu.addAction(tr("Clear"), [&] { 507 context_menu.addAction(tr("Clear"), [&] {
475 analogs_param[analog_id].Set("modifier", ""); 508 param.Set("modifier", "");
476 analog_map_modifier_button[analog_id]->setText(tr("[not set]")); 509 analog_map_modifier_button[analog_id]->setText(tr("[not set]"));
510 emulated_controller->SetStickParam(analog_id, param);
477 }); 511 });
478 context_menu.addAction(tr("Toggle button"), [&] { 512 context_menu.addAction(tr("Toggle button"), [&] {
479 Common::ParamPackage modifier_param = 513 Common::ParamPackage modifier_param =
480 Common::ParamPackage{analogs_param[analog_id].Get("modifier", "")}; 514 Common::ParamPackage{param.Get("modifier", "")};
481 const bool toggle_value = !modifier_param.Get("toggle", false); 515 const bool toggle_value = !modifier_param.Get("toggle", false);
482 modifier_param.Set("toggle", toggle_value); 516 modifier_param.Set("toggle", toggle_value);
483 analogs_param[analog_id].Set("modifier", modifier_param.Serialize()); 517 param.Set("modifier", modifier_param.Serialize());
484 analog_map_modifier_button[analog_id]->setText( 518 analog_map_modifier_button[analog_id]->setText(
485 ButtonToText(modifier_param)); 519 ButtonToText(modifier_param));
520 emulated_controller->SetStickParam(analog_id, param);
486 }); 521 });
487 context_menu.exec( 522 context_menu.exec(
488 analog_map_modifier_button[analog_id]->mapToGlobal(menu_location)); 523 analog_map_modifier_button[analog_id]->mapToGlobal(menu_location));
@@ -490,37 +525,39 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
490 525
491 connect(analog_map_range_spinbox[analog_id], qOverload<int>(&QSpinBox::valueChanged), 526 connect(analog_map_range_spinbox[analog_id], qOverload<int>(&QSpinBox::valueChanged),
492 [=, this] { 527 [=, this] {
528 Common::ParamPackage param = emulated_controller->GetStickParam(analog_id);
493 const auto spinbox_value = analog_map_range_spinbox[analog_id]->value(); 529 const auto spinbox_value = analog_map_range_spinbox[analog_id]->value();
494 analogs_param[analog_id].Set("range", spinbox_value / 100.0f); 530 param.Set("range", spinbox_value / 100.0f);
495 ui->controllerFrame->SetPlayerInput(player_index, buttons_param, analogs_param); 531 emulated_controller->SetStickParam(analog_id, param);
496 }); 532 });
497 533
498 connect(analog_map_deadzone_slider[analog_id], &QSlider::valueChanged, [=, this] { 534 connect(analog_map_deadzone_slider[analog_id], &QSlider::valueChanged, [=, this] {
535 Common::ParamPackage param = emulated_controller->GetStickParam(analog_id);
499 const auto slider_value = analog_map_deadzone_slider[analog_id]->value(); 536 const auto slider_value = analog_map_deadzone_slider[analog_id]->value();
500 analog_map_deadzone_label[analog_id]->setText(tr("Deadzone: %1%").arg(slider_value)); 537 analog_map_deadzone_label[analog_id]->setText(tr("Deadzone: %1%").arg(slider_value));
501 analogs_param[analog_id].Set("deadzone", slider_value / 100.0f); 538 param.Set("deadzone", slider_value / 100.0f);
502 ui->controllerFrame->SetPlayerInput(player_index, buttons_param, analogs_param); 539 emulated_controller->SetStickParam(analog_id, param);
503 }); 540 });
504 541
505 connect(analog_map_modifier_slider[analog_id], &QSlider::valueChanged, [=, this] { 542 connect(analog_map_modifier_slider[analog_id], &QSlider::valueChanged, [=, this] {
543 Common::ParamPackage param = emulated_controller->GetStickParam(analog_id);
506 const auto slider_value = analog_map_modifier_slider[analog_id]->value(); 544 const auto slider_value = analog_map_modifier_slider[analog_id]->value();
507 analog_map_modifier_label[analog_id]->setText( 545 analog_map_modifier_label[analog_id]->setText(
508 tr("Modifier Range: %1%").arg(slider_value)); 546 tr("Modifier Range: %1%").arg(slider_value));
509 analogs_param[analog_id].Set("modifier_scale", slider_value / 100.0f); 547 param.Set("modifier_scale", slider_value / 100.0f);
548 emulated_controller->SetStickParam(analog_id, param);
510 }); 549 });
511 } 550 }
512 551
513 // Player Connected checkbox 552 // Player Connected checkbox
514 connect(ui->groupConnectedController, &QGroupBox::toggled, [this](bool checked) { 553 connect(ui->groupConnectedController, &QGroupBox::toggled,
515 emit Connected(checked); 554 [this](bool checked) { emit Connected(checked); });
516 ui->controllerFrame->SetConnectedStatus(checked);
517 });
518 555
519 if (player_index == 0) { 556 if (player_index == 0) {
520 connect(ui->comboControllerType, qOverload<int>(&QComboBox::currentIndexChanged), 557 connect(ui->comboControllerType, qOverload<int>(&QComboBox::currentIndexChanged),
521 [this](int index) { 558 [this](int index) {
522 emit HandheldStateChanged(GetControllerTypeFromIndex(index) == 559 emit HandheldStateChanged(GetControllerTypeFromIndex(index) ==
523 Settings::ControllerType::Handheld); 560 Core::HID::NpadStyleIndex::Handheld);
524 }); 561 });
525 } 562 }
526 563
@@ -537,18 +574,43 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
537 SetConnectableControllers(); 574 SetConnectableControllers();
538 } 575 }
539 576
540 UpdateControllerIcon();
541 UpdateControllerAvailableButtons(); 577 UpdateControllerAvailableButtons();
542 UpdateControllerEnabledButtons(); 578 UpdateControllerEnabledButtons();
543 UpdateControllerButtonNames(); 579 UpdateControllerButtonNames();
544 UpdateMotionButtons(); 580 UpdateMotionButtons();
545 connect(ui->comboControllerType, qOverload<int>(&QComboBox::currentIndexChanged), [this](int) { 581 connect(ui->comboControllerType, qOverload<int>(&QComboBox::currentIndexChanged),
546 UpdateControllerIcon(); 582 [this, player_index](int) {
547 UpdateControllerAvailableButtons(); 583 UpdateControllerAvailableButtons();
548 UpdateControllerEnabledButtons(); 584 UpdateControllerEnabledButtons();
549 UpdateControllerButtonNames(); 585 UpdateControllerButtonNames();
550 UpdateMotionButtons(); 586 UpdateMotionButtons();
551 }); 587 const Core::HID::NpadStyleIndex type =
588 GetControllerTypeFromIndex(ui->comboControllerType->currentIndex());
589
590 if (player_index == 0) {
591 auto* emulated_controller_p1 =
592 hid_core.GetEmulatedController(Core::HID::NpadIdType::Player1);
593 auto* emulated_controller_handheld =
594 hid_core.GetEmulatedController(Core::HID::NpadIdType::Handheld);
595 bool is_connected = emulated_controller->IsConnected(true);
596
597 emulated_controller_p1->SetNpadStyleIndex(type);
598 emulated_controller_handheld->SetNpadStyleIndex(type);
599 if (is_connected) {
600 if (type == Core::HID::NpadStyleIndex::Handheld) {
601 emulated_controller_p1->Disconnect();
602 emulated_controller_handheld->Connect();
603 emulated_controller = emulated_controller_handheld;
604 } else {
605 emulated_controller_handheld->Disconnect();
606 emulated_controller_p1->Connect();
607 emulated_controller = emulated_controller_p1;
608 }
609 }
610 ui->controllerFrame->SetController(emulated_controller);
611 }
612 emulated_controller->SetNpadStyleIndex(type);
613 });
552 614
553 connect(ui->comboDevices, qOverload<int>(&QComboBox::activated), this, 615 connect(ui->comboDevices, qOverload<int>(&QComboBox::activated), this,
554 &ConfigureInputPlayer::UpdateMappingWithDefaults); 616 &ConfigureInputPlayer::UpdateMappingWithDefaults);
@@ -563,62 +625,10 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
563 connect(timeout_timer.get(), &QTimer::timeout, [this] { SetPollingResult({}, true); }); 625 connect(timeout_timer.get(), &QTimer::timeout, [this] { SetPollingResult({}, true); });
564 626
565 connect(poll_timer.get(), &QTimer::timeout, [this] { 627 connect(poll_timer.get(), &QTimer::timeout, [this] {
566 Common::ParamPackage params; 628 const auto& params = input_subsystem->GetNextInput();
567 if (input_subsystem->GetGCButtons()->IsPolling()) { 629 if (params.Has("engine") && IsInputAcceptable(params)) {
568 params = input_subsystem->GetGCButtons()->GetNextInput(); 630 SetPollingResult(params, false);
569 if (params.Has("engine") && IsInputAcceptable(params)) { 631 return;
570 SetPollingResult(params, false);
571 return;
572 }
573 }
574 if (input_subsystem->GetGCAnalogs()->IsPolling()) {
575 params = input_subsystem->GetGCAnalogs()->GetNextInput();
576 if (params.Has("engine") && IsInputAcceptable(params)) {
577 SetPollingResult(params, false);
578 return;
579 }
580 }
581 if (input_subsystem->GetUDPMotions()->IsPolling()) {
582 params = input_subsystem->GetUDPMotions()->GetNextInput();
583 if (params.Has("engine")) {
584 SetPollingResult(params, false);
585 return;
586 }
587 }
588 if (input_subsystem->GetMouseButtons()->IsPolling()) {
589 params = input_subsystem->GetMouseButtons()->GetNextInput();
590 if (params.Has("engine") && IsInputAcceptable(params)) {
591 SetPollingResult(params, false);
592 return;
593 }
594 }
595 if (input_subsystem->GetMouseAnalogs()->IsPolling()) {
596 params = input_subsystem->GetMouseAnalogs()->GetNextInput();
597 if (params.Has("engine") && IsInputAcceptable(params)) {
598 SetPollingResult(params, false);
599 return;
600 }
601 }
602 if (input_subsystem->GetMouseMotions()->IsPolling()) {
603 params = input_subsystem->GetMouseMotions()->GetNextInput();
604 if (params.Has("engine") && IsInputAcceptable(params)) {
605 SetPollingResult(params, false);
606 return;
607 }
608 }
609 if (input_subsystem->GetMouseTouch()->IsPolling()) {
610 params = input_subsystem->GetMouseTouch()->GetNextInput();
611 if (params.Has("engine") && IsInputAcceptable(params)) {
612 SetPollingResult(params, false);
613 return;
614 }
615 }
616 for (auto& poller : device_pollers) {
617 params = poller->GetNextInput();
618 if (params.Has("engine") && IsInputAcceptable(params)) {
619 SetPollingResult(params, false);
620 return;
621 }
622 } 632 }
623 }); 633 });
624 634
@@ -634,110 +644,38 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
634 &ConfigureInputPlayer::SaveProfile); 644 &ConfigureInputPlayer::SaveProfile);
635 645
636 LoadConfiguration(); 646 LoadConfiguration();
637 ui->controllerFrame->SetPlayerInput(player_index, buttons_param, analogs_param);
638 ui->controllerFrame->SetConnectedStatus(ui->groupConnectedController->isChecked());
639} 647}
640 648
641ConfigureInputPlayer::~ConfigureInputPlayer() = default; 649ConfigureInputPlayer::~ConfigureInputPlayer() {
642
643void ConfigureInputPlayer::ApplyConfiguration() {
644 auto& player = Settings::values.players.GetValue()[player_index];
645 auto& buttons = debug ? Settings::values.debug_pad_buttons : player.buttons;
646 auto& analogs = debug ? Settings::values.debug_pad_analogs : player.analogs;
647
648 std::transform(buttons_param.begin(), buttons_param.end(), buttons.begin(),
649 [](const Common::ParamPackage& param) { return param.Serialize(); });
650 std::transform(analogs_param.begin(), analogs_param.end(), analogs.begin(),
651 [](const Common::ParamPackage& param) { return param.Serialize(); });
652
653 if (debug) {
654 return;
655 }
656
657 auto& motions = player.motions;
658
659 std::transform(motions_param.begin(), motions_param.end(), motions.begin(),
660 [](const Common::ParamPackage& param) { return param.Serialize(); });
661
662 // Apply configuration for handheld
663 if (player_index == 0) { 650 if (player_index == 0) {
664 auto& handheld = Settings::values.players.GetValue()[HANDHELD_INDEX]; 651 auto* emulated_controller_p1 =
665 const auto handheld_connected = handheld.connected; 652 hid_core.GetEmulatedController(Core::HID::NpadIdType::Player1);
666 handheld = player; 653 auto* emulated_controller_handheld =
667 handheld.connected = handheld_connected; 654 hid_core.GetEmulatedController(Core::HID::NpadIdType::Handheld);
655 emulated_controller_p1->DisableConfiguration();
656 emulated_controller_handheld->DisableConfiguration();
657 } else {
658 emulated_controller->DisableConfiguration();
668 } 659 }
669} 660}
670 661
671void ConfigureInputPlayer::TryConnectSelectedController() { 662void ConfigureInputPlayer::ApplyConfiguration() {
672 auto& player = Settings::values.players.GetValue()[player_index];
673
674 const auto controller_type =
675 GetControllerTypeFromIndex(ui->comboControllerType->currentIndex());
676 const auto player_connected = ui->groupConnectedController->isChecked() &&
677 controller_type != Settings::ControllerType::Handheld;
678
679 // Connect Handheld depending on Player 1's controller configuration.
680 if (player_index == 0) { 663 if (player_index == 0) {
681 auto& handheld = Settings::values.players.GetValue()[HANDHELD_INDEX]; 664 auto* emulated_controller_p1 =
682 const auto handheld_connected = ui->groupConnectedController->isChecked() && 665 hid_core.GetEmulatedController(Core::HID::NpadIdType::Player1);
683 controller_type == Settings::ControllerType::Handheld; 666 auto* emulated_controller_handheld =
684 // Connect only if handheld is going from disconnected to connected 667 hid_core.GetEmulatedController(Core::HID::NpadIdType::Handheld);
685 if (!handheld.connected && handheld_connected) { 668 emulated_controller_p1->DisableConfiguration();
686 UpdateController(controller_type, HANDHELD_INDEX, true, system); 669 emulated_controller_p1->SaveCurrentConfig();
687 } 670 emulated_controller_p1->EnableConfiguration();
688 handheld.connected = handheld_connected; 671 emulated_controller_handheld->DisableConfiguration();
689 } 672 emulated_controller_handheld->SaveCurrentConfig();
690 673 emulated_controller_handheld->EnableConfiguration();
691 if (player.controller_type == controller_type && player.connected == player_connected) {
692 // Set vibration devices in the event that the input device has changed.
693 ConfigureVibration::SetVibrationDevices(player_index);
694 return; 674 return;
695 } 675 }
696 676 emulated_controller->DisableConfiguration();
697 player.controller_type = controller_type; 677 emulated_controller->SaveCurrentConfig();
698 player.connected = player_connected; 678 emulated_controller->EnableConfiguration();
699
700 ConfigureVibration::SetVibrationDevices(player_index);
701
702 if (!player.connected) {
703 return;
704 }
705
706 UpdateController(controller_type, player_index, true, system);
707}
708
709void ConfigureInputPlayer::TryDisconnectSelectedController() {
710 const auto& player = Settings::values.players.GetValue()[player_index];
711
712 const auto controller_type =
713 GetControllerTypeFromIndex(ui->comboControllerType->currentIndex());
714 const auto player_connected = ui->groupConnectedController->isChecked() &&
715 controller_type != Settings::ControllerType::Handheld;
716
717 // Disconnect Handheld depending on Player 1's controller configuration.
718 if (player_index == 0 && player.controller_type == Settings::ControllerType::Handheld) {
719 const auto& handheld = Settings::values.players.GetValue()[HANDHELD_INDEX];
720 const auto handheld_connected = ui->groupConnectedController->isChecked() &&
721 controller_type == Settings::ControllerType::Handheld;
722 // Disconnect only if handheld is going from connected to disconnected
723 if (handheld.connected && !handheld_connected) {
724 UpdateController(controller_type, HANDHELD_INDEX, false, system);
725 }
726 return;
727 }
728
729 // Do not do anything if the controller configuration has not changed.
730 if (player.controller_type == controller_type && player.connected == player_connected) {
731 return;
732 }
733
734 // Do not disconnect if the controller is already disconnected
735 if (!player.connected) {
736 return;
737 }
738
739 // Disconnect the controller first.
740 UpdateController(controller_type, player_index, false, system);
741} 679}
742 680
743void ConfigureInputPlayer::showEvent(QShowEvent* event) { 681void ConfigureInputPlayer::showEvent(QShowEvent* event) {
@@ -762,22 +700,7 @@ void ConfigureInputPlayer::RetranslateUI() {
762} 700}
763 701
764void ConfigureInputPlayer::LoadConfiguration() { 702void ConfigureInputPlayer::LoadConfiguration() {
765 auto& player = Settings::values.players.GetValue()[player_index]; 703 emulated_controller->ReloadFromSettings();
766 if (debug) {
767 std::transform(Settings::values.debug_pad_buttons.begin(),
768 Settings::values.debug_pad_buttons.end(), buttons_param.begin(),
769 [](const std::string& str) { return Common::ParamPackage(str); });
770 std::transform(Settings::values.debug_pad_analogs.begin(),
771 Settings::values.debug_pad_analogs.end(), analogs_param.begin(),
772 [](const std::string& str) { return Common::ParamPackage(str); });
773 } else {
774 std::transform(player.buttons.begin(), player.buttons.end(), buttons_param.begin(),
775 [](const std::string& str) { return Common::ParamPackage(str); });
776 std::transform(player.analogs.begin(), player.analogs.end(), analogs_param.begin(),
777 [](const std::string& str) { return Common::ParamPackage(str); });
778 std::transform(player.motions.begin(), player.motions.end(), motions_param.begin(),
779 [](const std::string& str) { return Common::ParamPackage(str); });
780 }
781 704
782 UpdateUI(); 705 UpdateUI();
783 UpdateInputDeviceCombobox(); 706 UpdateInputDeviceCombobox();
@@ -786,14 +709,19 @@ void ConfigureInputPlayer::LoadConfiguration() {
786 return; 709 return;
787 } 710 }
788 711
789 ui->comboControllerType->setCurrentIndex(GetIndexFromControllerType(player.controller_type)); 712 const int comboBoxIndex =
790 ui->groupConnectedController->setChecked( 713 GetIndexFromControllerType(emulated_controller->GetNpadStyleIndex(true));
791 player.connected || 714 ui->comboControllerType->setCurrentIndex(comboBoxIndex);
792 (player_index == 0 && Settings::values.players.GetValue()[HANDHELD_INDEX].connected)); 715 ui->groupConnectedController->setChecked(emulated_controller->IsConnected(true));
793} 716}
794 717
795void ConfigureInputPlayer::ConnectPlayer(bool connected) { 718void ConfigureInputPlayer::ConnectPlayer(bool connected) {
796 ui->groupConnectedController->setChecked(connected); 719 ui->groupConnectedController->setChecked(connected);
720 if (connected) {
721 emulated_controller->Connect();
722 } else {
723 emulated_controller->Disconnect();
724 }
797} 725}
798 726
799void ConfigureInputPlayer::UpdateInputDeviceCombobox() { 727void ConfigureInputPlayer::UpdateInputDeviceCombobox() {
@@ -803,48 +731,64 @@ void ConfigureInputPlayer::UpdateInputDeviceCombobox() {
803 return; 731 return;
804 } 732 }
805 733
806 // Find the first button that isn't empty. 734 const auto devices =
807 const auto button_param = 735 emulated_controller->GetMappedDevices(Core::HID::EmulatedDeviceIndex::AllDevices);
808 std::find_if(buttons_param.begin(), buttons_param.end(), 736 UpdateInputDevices();
809 [](const Common::ParamPackage param) { return param.Has("engine"); });
810 const bool buttons_empty = button_param == buttons_param.end();
811 737
812 const auto current_engine = button_param->Get("engine", ""); 738 if (devices.empty()) {
813 const auto current_guid = button_param->Get("guid", ""); 739 return;
814 const auto current_port = button_param->Get("port", ""); 740 }
815 741
816 const bool is_keyboard_mouse = current_engine == "keyboard" || current_engine == "mouse"; 742 if (devices.size() > 2) {
743 ui->comboDevices->setCurrentIndex(0);
744 return;
745 }
817 746
818 UpdateInputDevices(); 747 const auto first_engine = devices[0].Get("engine", "");
748 const auto first_guid = devices[0].Get("guid", "");
749 const auto first_port = devices[0].Get("port", 0);
819 750
820 if (buttons_empty) { 751 if (devices.size() == 1) {
752 const auto devices_it =
753 std::find_if(input_devices.begin(), input_devices.end(),
754 [first_engine, first_guid, first_port](const Common::ParamPackage param) {
755 return param.Get("engine", "") == first_engine &&
756 param.Get("guid", "") == first_guid &&
757 param.Get("port", 0) == first_port;
758 });
759 const int device_index =
760 devices_it != input_devices.end()
761 ? static_cast<int>(std::distance(input_devices.begin(), devices_it))
762 : 0;
763 ui->comboDevices->setCurrentIndex(device_index);
821 return; 764 return;
822 } 765 }
823 766
824 const bool all_one_device = 767 const auto second_engine = devices[1].Get("engine", "");
825 std::all_of(buttons_param.begin(), buttons_param.end(), 768 const auto second_guid = devices[1].Get("guid", "");
826 [current_engine, current_guid, current_port, 769 const auto second_port = devices[1].Get("port", 0);
827 is_keyboard_mouse](const Common::ParamPackage param) {
828 if (is_keyboard_mouse) {
829 return !param.Has("engine") || param.Get("engine", "") == "keyboard" ||
830 param.Get("engine", "") == "mouse";
831 }
832 return !param.Has("engine") || (param.Get("engine", "") == current_engine &&
833 param.Get("guid", "") == current_guid &&
834 param.Get("port", "") == current_port);
835 });
836 770
837 if (all_one_device) { 771 const bool is_keyboard_mouse = (first_engine == "keyboard" || first_engine == "mouse") &&
838 if (is_keyboard_mouse) { 772 (second_engine == "keyboard" || second_engine == "mouse");
839 ui->comboDevices->setCurrentIndex(1); 773
840 return; 774 if (is_keyboard_mouse) {
841 } 775 ui->comboDevices->setCurrentIndex(2);
776 return;
777 }
778
779 const bool is_engine_equal = first_engine == second_engine;
780 const bool is_port_equal = first_port == second_port;
781
782 if (is_engine_equal && is_port_equal) {
842 const auto devices_it = std::find_if( 783 const auto devices_it = std::find_if(
843 input_devices.begin(), input_devices.end(), 784 input_devices.begin(), input_devices.end(),
844 [current_engine, current_guid, current_port](const Common::ParamPackage param) { 785 [first_engine, first_guid, second_guid, first_port](const Common::ParamPackage param) {
845 return param.Get("class", "") == current_engine && 786 const bool is_guid_valid =
846 param.Get("guid", "") == current_guid && 787 (param.Get("guid", "") == first_guid &&
847 param.Get("port", "") == current_port; 788 param.Get("guid2", "") == second_guid) ||
789 (param.Get("guid", "") == second_guid && param.Get("guid2", "") == first_guid);
790 return param.Get("engine", "") == first_engine && is_guid_valid &&
791 param.Get("port", 0) == first_port;
848 }); 792 });
849 const int device_index = 793 const int device_index =
850 devices_it != input_devices.end() 794 devices_it != input_devices.end()
@@ -866,8 +810,7 @@ void ConfigureInputPlayer::ClearAll() {
866 if (button == nullptr) { 810 if (button == nullptr) {
867 continue; 811 continue;
868 } 812 }
869 813 emulated_controller->SetButtonParam(button_id, {});
870 buttons_param[button_id].Clear();
871 } 814 }
872 815
873 for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; ++analog_id) { 816 for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; ++analog_id) {
@@ -876,8 +819,7 @@ void ConfigureInputPlayer::ClearAll() {
876 if (analog_button == nullptr) { 819 if (analog_button == nullptr) {
877 continue; 820 continue;
878 } 821 }
879 822 emulated_controller->SetStickParam(analog_id, {});
880 analogs_param[analog_id].Clear();
881 } 823 }
882 } 824 }
883 825
@@ -886,8 +828,7 @@ void ConfigureInputPlayer::ClearAll() {
886 if (motion_button == nullptr) { 828 if (motion_button == nullptr) {
887 continue; 829 continue;
888 } 830 }
889 831 emulated_controller->SetMotionParam(motion_id, {});
890 motions_param[motion_id].Clear();
891 } 832 }
892 833
893 UpdateUI(); 834 UpdateUI();
@@ -896,26 +837,31 @@ void ConfigureInputPlayer::ClearAll() {
896 837
897void ConfigureInputPlayer::UpdateUI() { 838void ConfigureInputPlayer::UpdateUI() {
898 for (int button = 0; button < Settings::NativeButton::NumButtons; ++button) { 839 for (int button = 0; button < Settings::NativeButton::NumButtons; ++button) {
899 button_map[button]->setText(ButtonToText(buttons_param[button])); 840 const Common::ParamPackage param = emulated_controller->GetButtonParam(button);
841 button_map[button]->setText(ButtonToText(param));
900 } 842 }
901 843
902 if (buttons_param[Settings::NativeButton::ZL].Has("threshold")) { 844 const Common::ParamPackage ZL_param =
903 const int button_threshold = static_cast<int>( 845 emulated_controller->GetButtonParam(Settings::NativeButton::ZL);
904 buttons_param[Settings::NativeButton::ZL].Get("threshold", 0.5f) * 100.0f); 846 if (ZL_param.Has("threshold")) {
847 const int button_threshold = static_cast<int>(ZL_param.Get("threshold", 0.5f) * 100.0f);
905 ui->sliderZLThreshold->setValue(button_threshold); 848 ui->sliderZLThreshold->setValue(button_threshold);
906 } 849 }
907 850
908 if (buttons_param[Settings::NativeButton::ZR].Has("threshold")) { 851 const Common::ParamPackage ZR_param =
909 const int button_threshold = static_cast<int>( 852 emulated_controller->GetButtonParam(Settings::NativeButton::ZR);
910 buttons_param[Settings::NativeButton::ZR].Get("threshold", 0.5f) * 100.0f); 853 if (ZR_param.Has("threshold")) {
854 const int button_threshold = static_cast<int>(ZR_param.Get("threshold", 0.5f) * 100.0f);
911 ui->sliderZRThreshold->setValue(button_threshold); 855 ui->sliderZRThreshold->setValue(button_threshold);
912 } 856 }
913 857
914 for (int motion_id = 0; motion_id < Settings::NativeMotion::NumMotions; ++motion_id) { 858 for (int motion_id = 0; motion_id < Settings::NativeMotion::NumMotions; ++motion_id) {
915 motion_map[motion_id]->setText(ButtonToText(motions_param[motion_id])); 859 const Common::ParamPackage param = emulated_controller->GetMotionParam(motion_id);
860 motion_map[motion_id]->setText(ButtonToText(param));
916 } 861 }
917 862
918 for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; ++analog_id) { 863 for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; ++analog_id) {
864 const Common::ParamPackage param = emulated_controller->GetStickParam(analog_id);
919 for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; ++sub_button_id) { 865 for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; ++sub_button_id) {
920 auto* const analog_button = analog_map_buttons[analog_id][sub_button_id]; 866 auto* const analog_button = analog_map_buttons[analog_id][sub_button_id];
921 867
@@ -923,12 +869,11 @@ void ConfigureInputPlayer::UpdateUI() {
923 continue; 869 continue;
924 } 870 }
925 871
926 analog_button->setText( 872 analog_button->setText(AnalogToText(param, analog_sub_buttons[sub_button_id]));
927 AnalogToText(analogs_param[analog_id], analog_sub_buttons[sub_button_id]));
928 } 873 }
929 874
930 analog_map_modifier_button[analog_id]->setText( 875 analog_map_modifier_button[analog_id]->setText(
931 ButtonToText(Common::ParamPackage{analogs_param[analog_id].Get("modifier", "")})); 876 ButtonToText(Common::ParamPackage{param.Get("modifier", "")}));
932 877
933 const auto deadzone_label = analog_map_deadzone_label[analog_id]; 878 const auto deadzone_label = analog_map_deadzone_label[analog_id];
934 const auto deadzone_slider = analog_map_deadzone_slider[analog_id]; 879 const auto deadzone_slider = analog_map_deadzone_slider[analog_id];
@@ -939,26 +884,14 @@ void ConfigureInputPlayer::UpdateUI() {
939 const auto range_spinbox = analog_map_range_spinbox[analog_id]; 884 const auto range_spinbox = analog_map_range_spinbox[analog_id];
940 885
941 int slider_value; 886 int slider_value;
942 auto& param = analogs_param[analog_id]; 887 const bool is_controller = input_subsystem->IsController(param);
943 const bool is_controller =
944 param.Get("engine", "") == "sdl" || param.Get("engine", "") == "gcpad" ||
945 param.Get("engine", "") == "mouse" || param.Get("engine", "") == "tas";
946 888
947 if (is_controller) { 889 if (is_controller) {
948 if (!param.Has("deadzone")) { 890 slider_value = static_cast<int>(param.Get("deadzone", 0.15f) * 100);
949 param.Set("deadzone", 0.1f);
950 }
951 slider_value = static_cast<int>(param.Get("deadzone", 0.1f) * 100);
952 deadzone_label->setText(tr("Deadzone: %1%").arg(slider_value)); 891 deadzone_label->setText(tr("Deadzone: %1%").arg(slider_value));
953 deadzone_slider->setValue(slider_value); 892 deadzone_slider->setValue(slider_value);
954 if (!param.Has("range")) {
955 param.Set("range", 1.0f);
956 }
957 range_spinbox->setValue(static_cast<int>(param.Get("range", 1.0f) * 100)); 893 range_spinbox->setValue(static_cast<int>(param.Get("range", 1.0f) * 100));
958 } else { 894 } else {
959 if (!param.Has("modifier_scale")) {
960 param.Set("modifier_scale", 0.5f);
961 }
962 slider_value = static_cast<int>(param.Get("modifier_scale", 0.5f) * 100); 895 slider_value = static_cast<int>(param.Get("modifier_scale", 0.5f) * 100);
963 modifier_label->setText(tr("Modifier Range: %1%").arg(slider_value)); 896 modifier_label->setText(tr("Modifier Range: %1%").arg(slider_value));
964 modifier_slider->setValue(slider_value); 897 modifier_slider->setValue(slider_value);
@@ -970,79 +903,98 @@ void ConfigureInputPlayer::UpdateUI() {
970 modifier_label->setVisible(!is_controller); 903 modifier_label->setVisible(!is_controller);
971 modifier_slider->setVisible(!is_controller); 904 modifier_slider->setVisible(!is_controller);
972 range_groupbox->setVisible(is_controller); 905 range_groupbox->setVisible(is_controller);
973 ui->controllerFrame->SetPlayerInput(player_index, buttons_param, analogs_param);
974 } 906 }
975} 907}
976 908
977void ConfigureInputPlayer::SetConnectableControllers() { 909void ConfigureInputPlayer::SetConnectableControllers() {
978 const auto add_controllers = [this](bool enable_all, 910 Core::HID::NpadStyleTag npad_style_set = hid_core.GetSupportedStyleTag();
979 Controller_NPad::NpadStyleSet npad_style_set = {}) { 911 index_controller_type_pairs.clear();
980 index_controller_type_pairs.clear(); 912 ui->comboControllerType->clear();
981 ui->comboControllerType->clear();
982
983 if (enable_all || npad_style_set.fullkey == 1) {
984 index_controller_type_pairs.emplace_back(ui->comboControllerType->count(),
985 Settings::ControllerType::ProController);
986 ui->comboControllerType->addItem(tr("Pro Controller"));
987 }
988 913
989 if (enable_all || npad_style_set.joycon_dual == 1) { 914 if (npad_style_set.fullkey == 1) {
990 index_controller_type_pairs.emplace_back(ui->comboControllerType->count(), 915 index_controller_type_pairs.emplace_back(ui->comboControllerType->count(),
991 Settings::ControllerType::DualJoyconDetached); 916 Core::HID::NpadStyleIndex::ProController);
992 ui->comboControllerType->addItem(tr("Dual Joycons")); 917 ui->comboControllerType->addItem(tr("Pro Controller"));
993 } 918 }
994 919
995 if (enable_all || npad_style_set.joycon_left == 1) { 920 if (npad_style_set.joycon_dual == 1) {
996 index_controller_type_pairs.emplace_back(ui->comboControllerType->count(), 921 index_controller_type_pairs.emplace_back(ui->comboControllerType->count(),
997 Settings::ControllerType::LeftJoycon); 922 Core::HID::NpadStyleIndex::JoyconDual);
998 ui->comboControllerType->addItem(tr("Left Joycon")); 923 ui->comboControllerType->addItem(tr("Dual Joycons"));
999 } 924 }
1000 925
1001 if (enable_all || npad_style_set.joycon_right == 1) { 926 if (npad_style_set.joycon_left == 1) {
1002 index_controller_type_pairs.emplace_back(ui->comboControllerType->count(), 927 index_controller_type_pairs.emplace_back(ui->comboControllerType->count(),
1003 Settings::ControllerType::RightJoycon); 928 Core::HID::NpadStyleIndex::JoyconLeft);
1004 ui->comboControllerType->addItem(tr("Right Joycon")); 929 ui->comboControllerType->addItem(tr("Left Joycon"));
1005 } 930 }
1006 931
1007 if (player_index == 0 && (enable_all || npad_style_set.handheld == 1)) { 932 if (npad_style_set.joycon_right == 1) {
1008 index_controller_type_pairs.emplace_back(ui->comboControllerType->count(), 933 index_controller_type_pairs.emplace_back(ui->comboControllerType->count(),
1009 Settings::ControllerType::Handheld); 934 Core::HID::NpadStyleIndex::JoyconRight);
1010 ui->comboControllerType->addItem(tr("Handheld")); 935 ui->comboControllerType->addItem(tr("Right Joycon"));
1011 } 936 }
1012 937
1013 if (enable_all || npad_style_set.gamecube == 1) { 938 if (player_index == 0 && npad_style_set.handheld == 1) {
1014 index_controller_type_pairs.emplace_back(ui->comboControllerType->count(), 939 index_controller_type_pairs.emplace_back(ui->comboControllerType->count(),
1015 Settings::ControllerType::GameCube); 940 Core::HID::NpadStyleIndex::Handheld);
1016 ui->comboControllerType->addItem(tr("GameCube Controller")); 941 ui->comboControllerType->addItem(tr("Handheld"));
1017 } 942 }
1018 }; 943
944 if (npad_style_set.gamecube == 1) {
945 index_controller_type_pairs.emplace_back(ui->comboControllerType->count(),
946 Core::HID::NpadStyleIndex::GameCube);
947 ui->comboControllerType->addItem(tr("GameCube Controller"));
948 }
1019 949
1020 if (!system.IsPoweredOn()) { 950 // Disable all unsupported controllers
1021 add_controllers(true); 951 if (!Settings::values.enable_all_controllers) {
1022 return; 952 return;
1023 } 953 }
954 if (npad_style_set.palma == 1) {
955 index_controller_type_pairs.emplace_back(ui->comboControllerType->count(),
956 Core::HID::NpadStyleIndex::Pokeball);
957 ui->comboControllerType->addItem(tr("Poke Ball Plus"));
958 }
1024 959
1025 Service::SM::ServiceManager& sm = system.ServiceManager(); 960 if (npad_style_set.lark == 1) {
961 index_controller_type_pairs.emplace_back(ui->comboControllerType->count(),
962 Core::HID::NpadStyleIndex::NES);
963 ui->comboControllerType->addItem(tr("NES Controller"));
964 }
1026 965
1027 auto& npad = sm.GetService<Hid>("hid")->GetAppletResource()->GetController<Controller_NPad>( 966 if (npad_style_set.lucia == 1) {
1028 HidController::NPad); 967 index_controller_type_pairs.emplace_back(ui->comboControllerType->count(),
968 Core::HID::NpadStyleIndex::SNES);
969 ui->comboControllerType->addItem(tr("SNES Controller"));
970 }
1029 971
1030 add_controllers(false, npad.GetSupportedStyleSet()); 972 if (npad_style_set.lagoon == 1) {
973 index_controller_type_pairs.emplace_back(ui->comboControllerType->count(),
974 Core::HID::NpadStyleIndex::N64);
975 ui->comboControllerType->addItem(tr("N64 Controller"));
976 }
977
978 if (npad_style_set.lager == 1) {
979 index_controller_type_pairs.emplace_back(ui->comboControllerType->count(),
980 Core::HID::NpadStyleIndex::SegaGenesis);
981 ui->comboControllerType->addItem(tr("Sega Genesis"));
982 }
1031} 983}
1032 984
1033Settings::ControllerType ConfigureInputPlayer::GetControllerTypeFromIndex(int index) const { 985Core::HID::NpadStyleIndex ConfigureInputPlayer::GetControllerTypeFromIndex(int index) const {
1034 const auto it = 986 const auto it =
1035 std::find_if(index_controller_type_pairs.begin(), index_controller_type_pairs.end(), 987 std::find_if(index_controller_type_pairs.begin(), index_controller_type_pairs.end(),
1036 [index](const auto& pair) { return pair.first == index; }); 988 [index](const auto& pair) { return pair.first == index; });
1037 989
1038 if (it == index_controller_type_pairs.end()) { 990 if (it == index_controller_type_pairs.end()) {
1039 return Settings::ControllerType::ProController; 991 return Core::HID::NpadStyleIndex::ProController;
1040 } 992 }
1041 993
1042 return it->second; 994 return it->second;
1043} 995}
1044 996
1045int ConfigureInputPlayer::GetIndexFromControllerType(Settings::ControllerType type) const { 997int ConfigureInputPlayer::GetIndexFromControllerType(Core::HID::NpadStyleIndex type) const {
1046 const auto it = 998 const auto it =
1047 std::find_if(index_controller_type_pairs.begin(), index_controller_type_pairs.end(), 999 std::find_if(index_controller_type_pairs.begin(), index_controller_type_pairs.end(),
1048 [type](const auto& pair) { return pair.second == type; }); 1000 [type](const auto& pair) { return pair.second == type; });
@@ -1057,52 +1009,15 @@ int ConfigureInputPlayer::GetIndexFromControllerType(Settings::ControllerType ty
1057void ConfigureInputPlayer::UpdateInputDevices() { 1009void ConfigureInputPlayer::UpdateInputDevices() {
1058 input_devices = input_subsystem->GetInputDevices(); 1010 input_devices = input_subsystem->GetInputDevices();
1059 ui->comboDevices->clear(); 1011 ui->comboDevices->clear();
1060 for (auto& device : input_devices) { 1012 for (auto device : input_devices) {
1061 const std::string display = device.Get("display", "Unknown"); 1013 ui->comboDevices->addItem(QString::fromStdString(device.Get("display", "Unknown")), {});
1062 ui->comboDevices->addItem(QString::fromStdString(display), {});
1063 if (display == "TAS") {
1064 device.Set("pad", static_cast<u8>(player_index));
1065 }
1066 } 1014 }
1067} 1015}
1068 1016
1069void ConfigureInputPlayer::UpdateControllerIcon() {
1070 // We aren't using Qt's built in theme support here since we aren't drawing an icon (and its
1071 // "nonstandard" to use an image through the icon support)
1072 const QString stylesheet = [this] {
1073 switch (GetControllerTypeFromIndex(ui->comboControllerType->currentIndex())) {
1074 case Settings::ControllerType::ProController:
1075 return QStringLiteral("image: url(:/controller/pro_controller%0)");
1076 case Settings::ControllerType::DualJoyconDetached:
1077 return QStringLiteral("image: url(:/controller/dual_joycon%0)");
1078 case Settings::ControllerType::LeftJoycon:
1079 return QStringLiteral("image: url(:/controller/single_joycon_left_vertical%0)");
1080 case Settings::ControllerType::RightJoycon:
1081 return QStringLiteral("image: url(:/controller/single_joycon_right_vertical%0)");
1082 case Settings::ControllerType::Handheld:
1083 return QStringLiteral("image: url(:/controller/handheld%0)");
1084 default:
1085 return QString{};
1086 }
1087 }();
1088
1089 const QString theme = [] {
1090 if (QIcon::themeName().contains(QStringLiteral("dark"))) {
1091 return QStringLiteral("_dark");
1092 } else if (QIcon::themeName().contains(QStringLiteral("midnight"))) {
1093 return QStringLiteral("_midnight");
1094 } else {
1095 return QString{};
1096 }
1097 }();
1098 ui->controllerFrame->SetControllerType(
1099 GetControllerTypeFromIndex(ui->comboControllerType->currentIndex()));
1100}
1101
1102void ConfigureInputPlayer::UpdateControllerAvailableButtons() { 1017void ConfigureInputPlayer::UpdateControllerAvailableButtons() {
1103 auto layout = GetControllerTypeFromIndex(ui->comboControllerType->currentIndex()); 1018 auto layout = GetControllerTypeFromIndex(ui->comboControllerType->currentIndex());
1104 if (debug) { 1019 if (debug) {
1105 layout = Settings::ControllerType::ProController; 1020 layout = Core::HID::NpadStyleIndex::ProController;
1106 } 1021 }
1107 1022
1108 // List of all the widgets that will be hidden by any of the following layouts that need 1023 // List of all the widgets that will be hidden by any of the following layouts that need
@@ -1127,15 +1042,15 @@ void ConfigureInputPlayer::UpdateControllerAvailableButtons() {
1127 1042
1128 std::vector<QWidget*> layout_hidden; 1043 std::vector<QWidget*> layout_hidden;
1129 switch (layout) { 1044 switch (layout) {
1130 case Settings::ControllerType::ProController: 1045 case Core::HID::NpadStyleIndex::ProController:
1131 case Settings::ControllerType::DualJoyconDetached: 1046 case Core::HID::NpadStyleIndex::JoyconDual:
1132 case Settings::ControllerType::Handheld: 1047 case Core::HID::NpadStyleIndex::Handheld:
1133 layout_hidden = { 1048 layout_hidden = {
1134 ui->buttonShoulderButtonsSLSR, 1049 ui->buttonShoulderButtonsSLSR,
1135 ui->horizontalSpacerShoulderButtonsWidget2, 1050 ui->horizontalSpacerShoulderButtonsWidget2,
1136 }; 1051 };
1137 break; 1052 break;
1138 case Settings::ControllerType::LeftJoycon: 1053 case Core::HID::NpadStyleIndex::JoyconLeft:
1139 layout_hidden = { 1054 layout_hidden = {
1140 ui->horizontalSpacerShoulderButtonsWidget2, 1055 ui->horizontalSpacerShoulderButtonsWidget2,
1141 ui->buttonShoulderButtonsRight, 1056 ui->buttonShoulderButtonsRight,
@@ -1143,7 +1058,7 @@ void ConfigureInputPlayer::UpdateControllerAvailableButtons() {
1143 ui->bottomRight, 1058 ui->bottomRight,
1144 }; 1059 };
1145 break; 1060 break;
1146 case Settings::ControllerType::RightJoycon: 1061 case Core::HID::NpadStyleIndex::JoyconRight:
1147 layout_hidden = { 1062 layout_hidden = {
1148 ui->horizontalSpacerShoulderButtonsWidget, 1063 ui->horizontalSpacerShoulderButtonsWidget,
1149 ui->buttonShoulderButtonsLeft, 1064 ui->buttonShoulderButtonsLeft,
@@ -1151,7 +1066,7 @@ void ConfigureInputPlayer::UpdateControllerAvailableButtons() {
1151 ui->bottomLeft, 1066 ui->bottomLeft,
1152 }; 1067 };
1153 break; 1068 break;
1154 case Settings::ControllerType::GameCube: 1069 case Core::HID::NpadStyleIndex::GameCube:
1155 layout_hidden = { 1070 layout_hidden = {
1156 ui->buttonShoulderButtonsSLSR, 1071 ui->buttonShoulderButtonsSLSR,
1157 ui->horizontalSpacerShoulderButtonsWidget2, 1072 ui->horizontalSpacerShoulderButtonsWidget2,
@@ -1159,6 +1074,8 @@ void ConfigureInputPlayer::UpdateControllerAvailableButtons() {
1159 ui->buttonMiscButtonsScreenshotGroup, 1074 ui->buttonMiscButtonsScreenshotGroup,
1160 }; 1075 };
1161 break; 1076 break;
1077 default:
1078 break;
1162 } 1079 }
1163 1080
1164 for (auto* widget : layout_hidden) { 1081 for (auto* widget : layout_hidden) {
@@ -1169,13 +1086,12 @@ void ConfigureInputPlayer::UpdateControllerAvailableButtons() {
1169void ConfigureInputPlayer::UpdateControllerEnabledButtons() { 1086void ConfigureInputPlayer::UpdateControllerEnabledButtons() {
1170 auto layout = GetControllerTypeFromIndex(ui->comboControllerType->currentIndex()); 1087 auto layout = GetControllerTypeFromIndex(ui->comboControllerType->currentIndex());
1171 if (debug) { 1088 if (debug) {
1172 layout = Settings::ControllerType::ProController; 1089 layout = Core::HID::NpadStyleIndex::ProController;
1173 } 1090 }
1174 1091
1175 // List of all the widgets that will be disabled by any of the following layouts that need 1092 // List of all the widgets that will be disabled by any of the following layouts that need
1176 // "enabled" after the controller type changes 1093 // "enabled" after the controller type changes
1177 const std::array<QWidget*, 4> layout_enable = { 1094 const std::array<QWidget*, 3> layout_enable = {
1178 ui->buttonHome,
1179 ui->buttonLStickPressedGroup, 1095 ui->buttonLStickPressedGroup,
1180 ui->groupRStickPressed, 1096 ui->groupRStickPressed,
1181 ui->buttonShoulderButtonsButtonLGroup, 1097 ui->buttonShoulderButtonsButtonLGroup,
@@ -1187,17 +1103,13 @@ void ConfigureInputPlayer::UpdateControllerEnabledButtons() {
1187 1103
1188 std::vector<QWidget*> layout_disable; 1104 std::vector<QWidget*> layout_disable;
1189 switch (layout) { 1105 switch (layout) {
1190 case Settings::ControllerType::ProController: 1106 case Core::HID::NpadStyleIndex::ProController:
1191 case Settings::ControllerType::DualJoyconDetached: 1107 case Core::HID::NpadStyleIndex::JoyconDual:
1192 case Settings::ControllerType::Handheld: 1108 case Core::HID::NpadStyleIndex::Handheld:
1193 case Settings::ControllerType::LeftJoycon: 1109 case Core::HID::NpadStyleIndex::JoyconLeft:
1194 case Settings::ControllerType::RightJoycon: 1110 case Core::HID::NpadStyleIndex::JoyconRight:
1195 // TODO(wwylele): enable this when we actually emulate it
1196 layout_disable = {
1197 ui->buttonHome,
1198 };
1199 break; 1111 break;
1200 case Settings::ControllerType::GameCube: 1112 case Core::HID::NpadStyleIndex::GameCube:
1201 layout_disable = { 1113 layout_disable = {
1202 ui->buttonHome, 1114 ui->buttonHome,
1203 ui->buttonLStickPressedGroup, 1115 ui->buttonLStickPressedGroup,
@@ -1205,6 +1117,8 @@ void ConfigureInputPlayer::UpdateControllerEnabledButtons() {
1205 ui->buttonShoulderButtonsButtonLGroup, 1117 ui->buttonShoulderButtonsButtonLGroup,
1206 }; 1118 };
1207 break; 1119 break;
1120 default:
1121 break;
1208 } 1122 }
1209 1123
1210 for (auto* widget : layout_disable) { 1124 for (auto* widget : layout_disable) {
@@ -1222,24 +1136,24 @@ void ConfigureInputPlayer::UpdateMotionButtons() {
1222 1136
1223 // Show/hide the "Motion 1/2" groupboxes depending on the currently selected controller. 1137 // Show/hide the "Motion 1/2" groupboxes depending on the currently selected controller.
1224 switch (GetControllerTypeFromIndex(ui->comboControllerType->currentIndex())) { 1138 switch (GetControllerTypeFromIndex(ui->comboControllerType->currentIndex())) {
1225 case Settings::ControllerType::ProController: 1139 case Core::HID::NpadStyleIndex::ProController:
1226 case Settings::ControllerType::LeftJoycon: 1140 case Core::HID::NpadStyleIndex::JoyconLeft:
1227 case Settings::ControllerType::Handheld: 1141 case Core::HID::NpadStyleIndex::Handheld:
1228 // Show "Motion 1" and hide "Motion 2". 1142 // Show "Motion 1" and hide "Motion 2".
1229 ui->buttonMotionLeftGroup->show(); 1143 ui->buttonMotionLeftGroup->show();
1230 ui->buttonMotionRightGroup->hide(); 1144 ui->buttonMotionRightGroup->hide();
1231 break; 1145 break;
1232 case Settings::ControllerType::RightJoycon: 1146 case Core::HID::NpadStyleIndex::JoyconRight:
1233 // Show "Motion 2" and hide "Motion 1". 1147 // Show "Motion 2" and hide "Motion 1".
1234 ui->buttonMotionLeftGroup->hide(); 1148 ui->buttonMotionLeftGroup->hide();
1235 ui->buttonMotionRightGroup->show(); 1149 ui->buttonMotionRightGroup->show();
1236 break; 1150 break;
1237 case Settings::ControllerType::GameCube: 1151 case Core::HID::NpadStyleIndex::GameCube:
1238 // Hide both "Motion 1/2". 1152 // Hide both "Motion 1/2".
1239 ui->buttonMotionLeftGroup->hide(); 1153 ui->buttonMotionLeftGroup->hide();
1240 ui->buttonMotionRightGroup->hide(); 1154 ui->buttonMotionRightGroup->hide();
1241 break; 1155 break;
1242 case Settings::ControllerType::DualJoyconDetached: 1156 case Core::HID::NpadStyleIndex::JoyconDual:
1243 default: 1157 default:
1244 // Show both "Motion 1/2". 1158 // Show both "Motion 1/2".
1245 ui->buttonMotionLeftGroup->show(); 1159 ui->buttonMotionLeftGroup->show();
@@ -1251,15 +1165,15 @@ void ConfigureInputPlayer::UpdateMotionButtons() {
1251void ConfigureInputPlayer::UpdateControllerButtonNames() { 1165void ConfigureInputPlayer::UpdateControllerButtonNames() {
1252 auto layout = GetControllerTypeFromIndex(ui->comboControllerType->currentIndex()); 1166 auto layout = GetControllerTypeFromIndex(ui->comboControllerType->currentIndex());
1253 if (debug) { 1167 if (debug) {
1254 layout = Settings::ControllerType::ProController; 1168 layout = Core::HID::NpadStyleIndex::ProController;
1255 } 1169 }
1256 1170
1257 switch (layout) { 1171 switch (layout) {
1258 case Settings::ControllerType::ProController: 1172 case Core::HID::NpadStyleIndex::ProController:
1259 case Settings::ControllerType::DualJoyconDetached: 1173 case Core::HID::NpadStyleIndex::JoyconDual:
1260 case Settings::ControllerType::Handheld: 1174 case Core::HID::NpadStyleIndex::Handheld:
1261 case Settings::ControllerType::LeftJoycon: 1175 case Core::HID::NpadStyleIndex::JoyconLeft:
1262 case Settings::ControllerType::RightJoycon: 1176 case Core::HID::NpadStyleIndex::JoyconRight:
1263 ui->buttonMiscButtonsPlusGroup->setTitle(tr("Plus")); 1177 ui->buttonMiscButtonsPlusGroup->setTitle(tr("Plus"));
1264 ui->buttonShoulderButtonsButtonZLGroup->setTitle(tr("ZL")); 1178 ui->buttonShoulderButtonsButtonZLGroup->setTitle(tr("ZL"));
1265 ui->buttonShoulderButtonsZRGroup->setTitle(tr("ZR")); 1179 ui->buttonShoulderButtonsZRGroup->setTitle(tr("ZR"));
@@ -1267,7 +1181,7 @@ void ConfigureInputPlayer::UpdateControllerButtonNames() {
1267 ui->LStick->setTitle(tr("Left Stick")); 1181 ui->LStick->setTitle(tr("Left Stick"));
1268 ui->RStick->setTitle(tr("Right Stick")); 1182 ui->RStick->setTitle(tr("Right Stick"));
1269 break; 1183 break;
1270 case Settings::ControllerType::GameCube: 1184 case Core::HID::NpadStyleIndex::GameCube:
1271 ui->buttonMiscButtonsPlusGroup->setTitle(tr("Start / Pause")); 1185 ui->buttonMiscButtonsPlusGroup->setTitle(tr("Start / Pause"));
1272 ui->buttonShoulderButtonsButtonZLGroup->setTitle(tr("L")); 1186 ui->buttonShoulderButtonsButtonZLGroup->setTitle(tr("L"));
1273 ui->buttonShoulderButtonsZRGroup->setTitle(tr("R")); 1187 ui->buttonShoulderButtonsZRGroup->setTitle(tr("R"));
@@ -1275,6 +1189,8 @@ void ConfigureInputPlayer::UpdateControllerButtonNames() {
1275 ui->LStick->setTitle(tr("Control Stick")); 1189 ui->LStick->setTitle(tr("Control Stick"));
1276 ui->RStick->setTitle(tr("C-Stick")); 1190 ui->RStick->setTitle(tr("C-Stick"));
1277 break; 1191 break;
1192 default:
1193 break;
1278 } 1194 }
1279} 1195}
1280 1196
@@ -1283,45 +1199,82 @@ void ConfigureInputPlayer::UpdateMappingWithDefaults() {
1283 return; 1199 return;
1284 } 1200 }
1285 1201
1286 if (ui->comboDevices->currentIndex() == 1) { 1202 for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; ++button_id) {
1287 // Reset keyboard bindings 1203 const auto* const button = button_map[button_id];
1204 if (button == nullptr) {
1205 continue;
1206 }
1207 emulated_controller->SetButtonParam(button_id, {});
1208 }
1209
1210 for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; ++analog_id) {
1211 for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; ++sub_button_id) {
1212 const auto* const analog_button = analog_map_buttons[analog_id][sub_button_id];
1213 if (analog_button == nullptr) {
1214 continue;
1215 }
1216 emulated_controller->SetStickParam(analog_id, {});
1217 }
1218 }
1219
1220 for (int motion_id = 0; motion_id < Settings::NativeMotion::NumMotions; ++motion_id) {
1221 const auto* const motion_button = motion_map[motion_id];
1222 if (motion_button == nullptr) {
1223 continue;
1224 }
1225 emulated_controller->SetMotionParam(motion_id, {});
1226 }
1227
1228 // Reset keyboard or mouse bindings
1229 if (ui->comboDevices->currentIndex() == 1 || ui->comboDevices->currentIndex() == 2) {
1288 for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; ++button_id) { 1230 for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; ++button_id) {
1289 buttons_param[button_id] = Common::ParamPackage{ 1231 emulated_controller->SetButtonParam(
1290 InputCommon::GenerateKeyboardParam(Config::default_buttons[button_id])}; 1232 button_id, Common::ParamPackage{InputCommon::GenerateKeyboardParam(
1233 Config::default_buttons[button_id])});
1291 } 1234 }
1292 for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; ++analog_id) { 1235 for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; ++analog_id) {
1236 Common::ParamPackage analog_param{};
1293 for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; ++sub_button_id) { 1237 for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; ++sub_button_id) {
1294 Common::ParamPackage params{InputCommon::GenerateKeyboardParam( 1238 Common::ParamPackage params{InputCommon::GenerateKeyboardParam(
1295 Config::default_analogs[analog_id][sub_button_id])}; 1239 Config::default_analogs[analog_id][sub_button_id])};
1296 SetAnalogParam(params, analogs_param[analog_id], analog_sub_buttons[sub_button_id]); 1240 SetAnalogParam(params, analog_param, analog_sub_buttons[sub_button_id]);
1297 } 1241 }
1298 1242
1299 analogs_param[analog_id].Set("modifier", InputCommon::GenerateKeyboardParam( 1243 analog_param.Set("modifier", InputCommon::GenerateKeyboardParam(
1300 Config::default_stick_mod[analog_id])); 1244 Config::default_stick_mod[analog_id]));
1245 emulated_controller->SetStickParam(analog_id, analog_param);
1301 } 1246 }
1302 1247
1303 for (int motion_id = 0; motion_id < Settings::NativeMotion::NumMotions; ++motion_id) { 1248 for (int motion_id = 0; motion_id < Settings::NativeMotion::NumMotions; ++motion_id) {
1304 motions_param[motion_id] = Common::ParamPackage{ 1249 emulated_controller->SetMotionParam(
1305 InputCommon::GenerateKeyboardParam(Config::default_motions[motion_id])}; 1250 motion_id, Common::ParamPackage{InputCommon::GenerateKeyboardParam(
1251 Config::default_motions[motion_id])});
1306 } 1252 }
1307 1253
1308 UpdateUI(); 1254 // If mouse is selected we want to override with mappings from the driver
1309 return; 1255 if (ui->comboDevices->currentIndex() == 1) {
1256 UpdateUI();
1257 return;
1258 }
1310 } 1259 }
1311 1260
1312 // Reset controller bindings 1261 // Reset controller bindings
1313 const auto& device = input_devices[ui->comboDevices->currentIndex()]; 1262 const auto& device = input_devices[ui->comboDevices->currentIndex()];
1314 auto button_mapping = input_subsystem->GetButtonMappingForDevice(device); 1263 auto button_mappings = input_subsystem->GetButtonMappingForDevice(device);
1315 auto analog_mapping = input_subsystem->GetAnalogMappingForDevice(device); 1264 auto analog_mappings = input_subsystem->GetAnalogMappingForDevice(device);
1316 auto motion_mapping = input_subsystem->GetMotionMappingForDevice(device); 1265 auto motion_mappings = input_subsystem->GetMotionMappingForDevice(device);
1317 for (std::size_t i = 0; i < buttons_param.size(); ++i) { 1266
1318 buttons_param[i] = button_mapping[static_cast<Settings::NativeButton::Values>(i)]; 1267 for (const auto& button_mapping : button_mappings) {
1268 const std::size_t index = button_mapping.first;
1269 emulated_controller->SetButtonParam(index, button_mapping.second);
1319 } 1270 }
1320 for (std::size_t i = 0; i < analogs_param.size(); ++i) { 1271 for (const auto& analog_mapping : analog_mappings) {
1321 analogs_param[i] = analog_mapping[static_cast<Settings::NativeAnalog::Values>(i)]; 1272 const std::size_t index = analog_mapping.first;
1273 emulated_controller->SetStickParam(index, analog_mapping.second);
1322 } 1274 }
1323 for (std::size_t i = 0; i < motions_param.size(); ++i) { 1275 for (const auto& motion_mapping : motion_mappings) {
1324 motions_param[i] = motion_mapping[static_cast<Settings::NativeMotion::Values>(i)]; 1276 const std::size_t index = motion_mapping.first;
1277 emulated_controller->SetMotionParam(index, motion_mapping.second);
1325 } 1278 }
1326 1279
1327 UpdateUI(); 1280 UpdateUI();
@@ -1330,7 +1283,7 @@ void ConfigureInputPlayer::UpdateMappingWithDefaults() {
1330void ConfigureInputPlayer::HandleClick( 1283void ConfigureInputPlayer::HandleClick(
1331 QPushButton* button, std::size_t button_id, 1284 QPushButton* button, std::size_t button_id,
1332 std::function<void(const Common::ParamPackage&)> new_input_setter, 1285 std::function<void(const Common::ParamPackage&)> new_input_setter,
1333 InputCommon::Polling::DeviceType type) { 1286 InputCommon::Polling::InputType type) {
1334 if (button == ui->buttonMotionLeft || button == ui->buttonMotionRight) { 1287 if (button == ui->buttonMotionLeft || button == ui->buttonMotionRight) {
1335 button->setText(tr("Shake!")); 1288 button->setText(tr("Shake!"));
1336 } else { 1289 } else {
@@ -1338,71 +1291,31 @@ void ConfigureInputPlayer::HandleClick(
1338 } 1291 }
1339 button->setFocus(); 1292 button->setFocus();
1340 1293
1341 // The first two input devices are always Any and Keyboard/Mouse. If the user filtered to a
1342 // controller, then they don't want keyboard/mouse input
1343 want_keyboard_mouse = ui->comboDevices->currentIndex() < 2;
1344
1345 input_setter = new_input_setter; 1294 input_setter = new_input_setter;
1346 1295
1347 device_pollers = input_subsystem->GetPollers(type); 1296 input_subsystem->BeginMapping(type);
1348
1349 for (auto& poller : device_pollers) {
1350 poller->Start();
1351 }
1352 1297
1353 QWidget::grabMouse(); 1298 QWidget::grabMouse();
1354 QWidget::grabKeyboard(); 1299 QWidget::grabKeyboard();
1355 1300
1356 if (type == InputCommon::Polling::DeviceType::Button) { 1301 if (type == InputCommon::Polling::InputType::Button) {
1357 input_subsystem->GetGCButtons()->BeginConfiguration();
1358 } else {
1359 input_subsystem->GetGCAnalogs()->BeginConfiguration();
1360 }
1361
1362 if (type == InputCommon::Polling::DeviceType::Motion) {
1363 input_subsystem->GetUDPMotions()->BeginConfiguration();
1364 }
1365
1366 if (type == InputCommon::Polling::DeviceType::Button) {
1367 input_subsystem->GetMouseButtons()->BeginConfiguration();
1368 } else if (type == InputCommon::Polling::DeviceType::AnalogPreferred) {
1369 input_subsystem->GetMouseAnalogs()->BeginConfiguration();
1370 } else if (type == InputCommon::Polling::DeviceType::Motion) {
1371 input_subsystem->GetMouseMotions()->BeginConfiguration();
1372 } else {
1373 input_subsystem->GetMouseTouch()->BeginConfiguration();
1374 }
1375
1376 if (type == InputCommon::Polling::DeviceType::Button) {
1377 ui->controllerFrame->BeginMappingButton(button_id); 1302 ui->controllerFrame->BeginMappingButton(button_id);
1378 } else if (type == InputCommon::Polling::DeviceType::AnalogPreferred) { 1303 } else if (type == InputCommon::Polling::InputType::Stick) {
1379 ui->controllerFrame->BeginMappingAnalog(button_id); 1304 ui->controllerFrame->BeginMappingAnalog(button_id);
1380 } 1305 }
1381 1306
1382 timeout_timer->start(2500); // Cancel after 2.5 seconds 1307 timeout_timer->start(2500); // Cancel after 2.5 seconds
1383 poll_timer->start(50); // Check for new inputs every 50ms 1308 poll_timer->start(25); // Check for new inputs every 25ms
1384} 1309}
1385 1310
1386void ConfigureInputPlayer::SetPollingResult(const Common::ParamPackage& params, bool abort) { 1311void ConfigureInputPlayer::SetPollingResult(const Common::ParamPackage& params, bool abort) {
1387 timeout_timer->stop(); 1312 timeout_timer->stop();
1388 poll_timer->stop(); 1313 poll_timer->stop();
1389 for (auto& poller : device_pollers) { 1314 input_subsystem->StopMapping();
1390 poller->Stop();
1391 }
1392 1315
1393 QWidget::releaseMouse(); 1316 QWidget::releaseMouse();
1394 QWidget::releaseKeyboard(); 1317 QWidget::releaseKeyboard();
1395 1318
1396 input_subsystem->GetGCButtons()->EndConfiguration();
1397 input_subsystem->GetGCAnalogs()->EndConfiguration();
1398
1399 input_subsystem->GetUDPMotions()->EndConfiguration();
1400
1401 input_subsystem->GetMouseButtons()->EndConfiguration();
1402 input_subsystem->GetMouseAnalogs()->EndConfiguration();
1403 input_subsystem->GetMouseMotions()->EndConfiguration();
1404 input_subsystem->GetMouseTouch()->EndConfiguration();
1405
1406 if (!abort) { 1319 if (!abort) {
1407 (*input_setter)(params); 1320 (*input_setter)(params);
1408 } 1321 }
@@ -1419,15 +1332,20 @@ bool ConfigureInputPlayer::IsInputAcceptable(const Common::ParamPackage& params)
1419 return true; 1332 return true;
1420 } 1333 }
1421 1334
1335 if (params.Has("motion")) {
1336 return true;
1337 }
1338
1422 // Keyboard/Mouse 1339 // Keyboard/Mouse
1423 if (ui->comboDevices->currentIndex() == 1) { 1340 if (ui->comboDevices->currentIndex() == 1 || ui->comboDevices->currentIndex() == 2) {
1424 return params.Get("engine", "") == "keyboard" || params.Get("engine", "") == "mouse"; 1341 return params.Get("engine", "") == "keyboard" || params.Get("engine", "") == "mouse";
1425 } 1342 }
1426 1343
1427 const auto current_input_device = input_devices[ui->comboDevices->currentIndex()]; 1344 const auto current_input_device = input_devices[ui->comboDevices->currentIndex()];
1428 return params.Get("engine", "") == current_input_device.Get("class", "") && 1345 return params.Get("engine", "") == current_input_device.Get("engine", "") &&
1429 params.Get("guid", "") == current_input_device.Get("guid", "") && 1346 (params.Get("guid", "") == current_input_device.Get("guid", "") ||
1430 params.Get("port", "") == current_input_device.Get("port", ""); 1347 params.Get("guid", "") == current_input_device.Get("guid2", "")) &&
1348 params.Get("port", 0) == current_input_device.Get("port", 0);
1431} 1349}
1432 1350
1433void ConfigureInputPlayer::mousePressEvent(QMouseEvent* event) { 1351void ConfigureInputPlayer::mousePressEvent(QMouseEvent* event) {
@@ -1436,25 +1354,17 @@ void ConfigureInputPlayer::mousePressEvent(QMouseEvent* event) {
1436 } 1354 }
1437 1355
1438 const auto button = GRenderWindow::QtButtonToMouseButton(event->button()); 1356 const auto button = GRenderWindow::QtButtonToMouseButton(event->button());
1439 input_subsystem->GetMouse()->PressButton(0, 0, button); 1357 input_subsystem->GetMouse()->PressButton(0, 0, 0, 0, button);
1440} 1358}
1441 1359
1442void ConfigureInputPlayer::keyPressEvent(QKeyEvent* event) { 1360void ConfigureInputPlayer::keyPressEvent(QKeyEvent* event) {
1361 event->ignore();
1443 if (!input_setter || !event) { 1362 if (!input_setter || !event) {
1444 return; 1363 return;
1445 } 1364 }
1446
1447 if (event->key() != Qt::Key_Escape) { 1365 if (event->key() != Qt::Key_Escape) {
1448 if (want_keyboard_mouse) { 1366 input_subsystem->GetKeyboard()->PressKey(event->key());
1449 SetPollingResult(Common::ParamPackage{InputCommon::GenerateKeyboardParam(event->key())},
1450 false);
1451 } else {
1452 // Escape key wasn't pressed and we don't want any keyboard keys, so don't stop polling
1453 return;
1454 }
1455 } 1367 }
1456
1457 SetPollingResult({}, true);
1458} 1368}
1459 1369
1460void ConfigureInputPlayer::CreateProfile() { 1370void ConfigureInputPlayer::CreateProfile() {
diff --git a/src/yuzu/configuration/configure_input_player.h b/src/yuzu/configuration/configure_input_player.h
index 39b44b8a5..47df6b3d3 100644
--- a/src/yuzu/configuration/configure_input_player.h
+++ b/src/yuzu/configuration/configure_input_player.h
@@ -29,48 +29,37 @@ class QWidget;
29 29
30class InputProfiles; 30class InputProfiles;
31 31
32namespace Core {
33class System;
34}
35
36namespace InputCommon { 32namespace InputCommon {
37class InputSubsystem; 33class InputSubsystem;
38} 34}
39 35
40namespace InputCommon::Polling { 36namespace InputCommon::Polling {
41class DevicePoller; 37enum class InputType;
42enum class DeviceType;
43} // namespace InputCommon::Polling 38} // namespace InputCommon::Polling
44 39
45namespace Ui { 40namespace Ui {
46class ConfigureInputPlayer; 41class ConfigureInputPlayer;
47} 42}
48 43
44namespace Core::HID {
45class HIDCore;
46class EmulatedController;
47enum class NpadStyleIndex : u8;
48} // namespace Core::HID
49
49class ConfigureInputPlayer : public QWidget { 50class ConfigureInputPlayer : public QWidget {
50 Q_OBJECT 51 Q_OBJECT
51 52
52public: 53public:
53 explicit ConfigureInputPlayer(QWidget* parent, std::size_t player_index, QWidget* bottom_row, 54 explicit ConfigureInputPlayer(QWidget* parent, std::size_t player_index, QWidget* bottom_row,
54 InputCommon::InputSubsystem* input_subsystem_, 55 InputCommon::InputSubsystem* input_subsystem_,
55 InputProfiles* profiles_, Core::System& system_, 56 InputProfiles* profiles_, Core::HID::HIDCore& hid_core_,
56 bool debug = false); 57 bool is_powered_on_, bool debug = false);
57 ~ConfigureInputPlayer() override; 58 ~ConfigureInputPlayer() override;
58 59
59 /// Save all button configurations to settings file. 60 /// Save all button configurations to settings file.
60 void ApplyConfiguration(); 61 void ApplyConfiguration();
61 62
62 /**
63 * Attempts to connect the currently selected controller in the HID backend.
64 * This function will not do anything if it is not connected in the frontend.
65 */
66 void TryConnectSelectedController();
67
68 /**
69 * Attempts to disconnect the currently selected controller in the HID backend.
70 * This function will not do anything if the configuration has not changed.
71 */
72 void TryDisconnectSelectedController();
73
74 /// Set the connection state checkbox (used to sync state). 63 /// Set the connection state checkbox (used to sync state).
75 void ConnectPlayer(bool connected); 64 void ConnectPlayer(bool connected);
76 65
@@ -104,6 +93,10 @@ protected:
104 void showEvent(QShowEvent* event) override; 93 void showEvent(QShowEvent* event) override;
105 94
106private: 95private:
96 QString ButtonToText(const Common::ParamPackage& param);
97
98 QString AnalogToText(const Common::ParamPackage& param, const std::string& dir);
99
107 void changeEvent(QEvent* event) override; 100 void changeEvent(QEvent* event) override;
108 void RetranslateUI(); 101 void RetranslateUI();
109 102
@@ -113,7 +106,7 @@ private:
113 /// Called when the button was pressed. 106 /// Called when the button was pressed.
114 void HandleClick(QPushButton* button, std::size_t button_id, 107 void HandleClick(QPushButton* button, std::size_t button_id,
115 std::function<void(const Common::ParamPackage&)> new_input_setter, 108 std::function<void(const Common::ParamPackage&)> new_input_setter,
116 InputCommon::Polling::DeviceType type); 109 InputCommon::Polling::InputType type);
117 110
118 /// Finish polling and configure input using the input_setter. 111 /// Finish polling and configure input using the input_setter.
119 void SetPollingResult(const Common::ParamPackage& params, bool abort); 112 void SetPollingResult(const Common::ParamPackage& params, bool abort);
@@ -134,17 +127,14 @@ private:
134 void SetConnectableControllers(); 127 void SetConnectableControllers();
135 128
136 /// Gets the Controller Type for a given controller combobox index. 129 /// Gets the Controller Type for a given controller combobox index.
137 Settings::ControllerType GetControllerTypeFromIndex(int index) const; 130 Core::HID::NpadStyleIndex GetControllerTypeFromIndex(int index) const;
138 131
139 /// Gets the controller combobox index for a given Controller Type. 132 /// Gets the controller combobox index for a given Controller Type.
140 int GetIndexFromControllerType(Settings::ControllerType type) const; 133 int GetIndexFromControllerType(Core::HID::NpadStyleIndex type) const;
141 134
142 /// Update the available input devices. 135 /// Update the available input devices.
143 void UpdateInputDevices(); 136 void UpdateInputDevices();
144 137
145 /// Update the current controller icon.
146 void UpdateControllerIcon();
147
148 /// Hides and disables controller settings based on the current controller type. 138 /// Hides and disables controller settings based on the current controller type.
149 void UpdateControllerAvailableButtons(); 139 void UpdateControllerAvailableButtons();
150 140
@@ -176,6 +166,7 @@ private:
176 166
177 std::size_t player_index; 167 std::size_t player_index;
178 bool debug; 168 bool debug;
169 bool is_powered_on;
179 170
180 InputCommon::InputSubsystem* input_subsystem; 171 InputCommon::InputSubsystem* input_subsystem;
181 172
@@ -185,7 +176,7 @@ private:
185 std::unique_ptr<QTimer> poll_timer; 176 std::unique_ptr<QTimer> poll_timer;
186 177
187 /// Stores a pair of "Connected Controllers" combobox index and Controller Type enum. 178 /// Stores a pair of "Connected Controllers" combobox index and Controller Type enum.
188 std::vector<std::pair<int, Settings::ControllerType>> index_controller_type_pairs; 179 std::vector<std::pair<int, Core::HID::NpadStyleIndex>> index_controller_type_pairs;
189 180
190 static constexpr int PLAYER_COUNT = 8; 181 static constexpr int PLAYER_COUNT = 8;
191 std::array<QCheckBox*, PLAYER_COUNT> player_connected_checkbox; 182 std::array<QCheckBox*, PLAYER_COUNT> player_connected_checkbox;
@@ -193,9 +184,7 @@ private:
193 /// This will be the the setting function when an input is awaiting configuration. 184 /// This will be the the setting function when an input is awaiting configuration.
194 std::optional<std::function<void(const Common::ParamPackage&)>> input_setter; 185 std::optional<std::function<void(const Common::ParamPackage&)>> input_setter;
195 186
196 std::array<Common::ParamPackage, Settings::NativeButton::NumButtons> buttons_param; 187 Core::HID::EmulatedController* emulated_controller;
197 std::array<Common::ParamPackage, Settings::NativeAnalog::NumAnalogs> analogs_param;
198 std::array<Common::ParamPackage, Settings::NativeMotion::NumMotions> motions_param;
199 188
200 static constexpr int ANALOG_SUB_BUTTONS_NUM = 4; 189 static constexpr int ANALOG_SUB_BUTTONS_NUM = 4;
201 190
@@ -221,15 +210,9 @@ private:
221 210
222 static const std::array<std::string, ANALOG_SUB_BUTTONS_NUM> analog_sub_buttons; 211 static const std::array<std::string, ANALOG_SUB_BUTTONS_NUM> analog_sub_buttons;
223 212
224 std::vector<std::unique_ptr<InputCommon::Polling::DevicePoller>> device_pollers;
225
226 /// A flag to indicate that the "Map Analog Stick" pop-up has been shown and accepted once. 213 /// A flag to indicate that the "Map Analog Stick" pop-up has been shown and accepted once.
227 bool map_analog_stick_accepted{}; 214 bool map_analog_stick_accepted{};
228 215
229 /// A flag to indicate if keyboard keys are okay when configuring an input. If this is false,
230 /// keyboard events are ignored.
231 bool want_keyboard_mouse{};
232
233 /// List of physical devices users can map with. If a SDL backed device is selected, then you 216 /// List of physical devices users can map with. If a SDL backed device is selected, then you
234 /// can use this device to get a default mapping. 217 /// can use this device to get a default mapping.
235 std::vector<Common::ParamPackage> input_devices; 218 std::vector<Common::ParamPackage> input_devices;
@@ -239,5 +222,5 @@ private:
239 /// parent of the widget to this widget (but thats fine). 222 /// parent of the widget to this widget (but thats fine).
240 QWidget* bottom_row; 223 QWidget* bottom_row;
241 224
242 Core::System& system; 225 Core::HID::HIDCore& hid_core;
243}; 226};
diff --git a/src/yuzu/configuration/configure_input_player.ui b/src/yuzu/configuration/configure_input_player.ui
index e7433912b..756a414b5 100644
--- a/src/yuzu/configuration/configure_input_player.ui
+++ b/src/yuzu/configuration/configure_input_player.ui
@@ -89,31 +89,6 @@
89 <height>21</height> 89 <height>21</height>
90 </size> 90 </size>
91 </property> 91 </property>
92 <item>
93 <property name="text">
94 <string>Pro Controller</string>
95 </property>
96 </item>
97 <item>
98 <property name="text">
99 <string>Dual Joycons</string>
100 </property>
101 </item>
102 <item>
103 <property name="text">
104 <string>Left Joycon</string>
105 </property>
106 </item>
107 <item>
108 <property name="text">
109 <string>Right Joycon</string>
110 </property>
111 </item>
112 <item>
113 <property name="text">
114 <string>Handheld</string>
115 </property>
116 </item>
117 </widget> 92 </widget>
118 </item> 93 </item>
119 </layout> 94 </layout>
@@ -142,22 +117,9 @@
142 </property> 117 </property>
143 <item> 118 <item>
144 <widget class="QComboBox" name="comboDevices"> 119 <widget class="QComboBox" name="comboDevices">
145 <property name="minimumSize"> 120 <property name="minimumContentsLength">
146 <size> 121 <number>60</number>
147 <width>0</width>
148 <height>21</height>
149 </size>
150 </property> 122 </property>
151 <item>
152 <property name="text">
153 <string>Any</string>
154 </property>
155 </item>
156 <item>
157 <property name="text">
158 <string>Keyboard/Mouse</string>
159 </property>
160 </item>
161 </widget> 123 </widget>
162 </item> 124 </item>
163 <item> 125 <item>
@@ -342,7 +304,7 @@
342 <number>3</number> 304 <number>3</number>
343 </property> 305 </property>
344 <property name="topMargin"> 306 <property name="topMargin">
345 <number>0</number> 307 <number>6</number>
346 </property> 308 </property>
347 <property name="rightMargin"> 309 <property name="rightMargin">
348 <number>3</number> 310 <number>3</number>
@@ -918,7 +880,7 @@
918 <number>3</number> 880 <number>3</number>
919 </property> 881 </property>
920 <property name="topMargin"> 882 <property name="topMargin">
921 <number>0</number> 883 <number>6</number>
922 </property> 884 </property>
923 <property name="rightMargin"> 885 <property name="rightMargin">
924 <number>3</number> 886 <number>3</number>
@@ -2221,7 +2183,7 @@
2221 <number>3</number> 2183 <number>3</number>
2222 </property> 2184 </property>
2223 <property name="topMargin"> 2185 <property name="topMargin">
2224 <number>0</number> 2186 <number>6</number>
2225 </property> 2187 </property>
2226 <property name="rightMargin"> 2188 <property name="rightMargin">
2227 <number>3</number> 2189 <number>3</number>
@@ -2570,7 +2532,7 @@
2570 <number>3</number> 2532 <number>3</number>
2571 </property> 2533 </property>
2572 <property name="topMargin"> 2534 <property name="topMargin">
2573 <number>0</number> 2535 <number>6</number>
2574 </property> 2536 </property>
2575 <property name="rightMargin"> 2537 <property name="rightMargin">
2576 <number>3</number> 2538 <number>3</number>
diff --git a/src/yuzu/configuration/configure_input_player_widget.cpp b/src/yuzu/configuration/configure_input_player_widget.cpp
index f31f86339..6630321cb 100644
--- a/src/yuzu/configuration/configure_input_player_widget.cpp
+++ b/src/yuzu/configuration/configure_input_player_widget.cpp
@@ -6,10 +6,12 @@
6#include <QMenu> 6#include <QMenu>
7#include <QPainter> 7#include <QPainter>
8#include <QTimer> 8#include <QTimer>
9
10#include "core/hid/emulated_controller.h"
9#include "yuzu/configuration/configure_input_player_widget.h" 11#include "yuzu/configuration/configure_input_player_widget.h"
10 12
11PlayerControlPreview::PlayerControlPreview(QWidget* parent) : QFrame(parent) { 13PlayerControlPreview::PlayerControlPreview(QWidget* parent) : QFrame(parent) {
12 UpdateColors(); 14 is_controller_set = false;
13 QTimer* timer = new QTimer(this); 15 QTimer* timer = new QTimer(this);
14 connect(timer, &QTimer::timeout, this, QOverload<>::of(&PlayerControlPreview::UpdateInput)); 16 connect(timer, &QTimer::timeout, this, QOverload<>::of(&PlayerControlPreview::UpdateInput));
15 17
@@ -17,91 +19,37 @@ PlayerControlPreview::PlayerControlPreview(QWidget* parent) : QFrame(parent) {
17 timer->start(16); 19 timer->start(16);
18} 20}
19 21
20PlayerControlPreview::~PlayerControlPreview() = default; 22PlayerControlPreview::~PlayerControlPreview() {
21 23 UnloadController();
22void PlayerControlPreview::SetPlayerInput(std::size_t index, const ButtonParam& buttons_param, 24};
23 const AnalogParam& analogs_param) {
24 player_index = index;
25 Settings::ButtonsRaw buttonss;
26 Settings::AnalogsRaw analogs;
27 std::transform(buttons_param.begin(), buttons_param.end(), buttonss.begin(),
28 [](const Common::ParamPackage& param) { return param.Serialize(); });
29 std::transform(analogs_param.begin(), analogs_param.end(), analogs.begin(),
30 [](const Common::ParamPackage& param) { return param.Serialize(); });
31
32 std::transform(buttonss.begin() + Settings::NativeButton::BUTTON_HID_BEGIN,
33 buttonss.begin() + Settings::NativeButton::BUTTON_NS_END, buttons.begin(),
34 Input::CreateDevice<Input::ButtonDevice>);
35 std::transform(analogs.begin() + Settings::NativeAnalog::STICK_HID_BEGIN,
36 analogs.begin() + Settings::NativeAnalog::STICK_HID_END, sticks.begin(),
37 Input::CreateDevice<Input::AnalogDevice>);
38 UpdateColors();
39}
40void PlayerControlPreview::SetPlayerInputRaw(std::size_t index,
41 const Settings::ButtonsRaw& buttons_,
42 Settings::AnalogsRaw analogs_) {
43 player_index = index;
44 std::transform(buttons_.begin() + Settings::NativeButton::BUTTON_HID_BEGIN,
45 buttons_.begin() + Settings::NativeButton::BUTTON_NS_END, buttons.begin(),
46 Input::CreateDevice<Input::ButtonDevice>);
47 std::transform(analogs_.begin() + Settings::NativeAnalog::STICK_HID_BEGIN,
48 analogs_.begin() + Settings::NativeAnalog::STICK_HID_END, sticks.begin(),
49 Input::CreateDevice<Input::AnalogDevice>);
50 UpdateColors();
51}
52
53PlayerControlPreview::LedPattern PlayerControlPreview::GetColorPattern(std::size_t index,
54 bool player_on) {
55 if (!player_on) {
56 return {0, 0, 0, 0};
57 }
58
59 switch (index) {
60 case 0:
61 return {1, 0, 0, 0};
62 case 1:
63 return {1, 1, 0, 0};
64 case 2:
65 return {1, 1, 1, 0};
66 case 3:
67 return {1, 1, 1, 1};
68 case 4:
69 return {1, 0, 0, 1};
70 case 5:
71 return {1, 0, 1, 0};
72 case 6:
73 return {1, 0, 1, 1};
74 case 7:
75 return {0, 1, 1, 0};
76 default:
77 return {0, 0, 0, 0};
78 }
79}
80
81void PlayerControlPreview::SetConnectedStatus(bool checked) {
82 LedPattern led_pattern = GetColorPattern(player_index, checked);
83 25
84 led_color[0] = led_pattern.position1 ? colors.led_on : colors.led_off; 26void PlayerControlPreview::SetController(Core::HID::EmulatedController* controller_) {
85 led_color[1] = led_pattern.position2 ? colors.led_on : colors.led_off; 27 UnloadController();
86 led_color[2] = led_pattern.position3 ? colors.led_on : colors.led_off; 28 is_controller_set = true;
87 led_color[3] = led_pattern.position4 ? colors.led_on : colors.led_off; 29 controller = controller_;
88 is_enabled = checked; 30 Core::HID::ControllerUpdateCallback engine_callback{
89 ResetInputs(); 31 .on_change = [this](Core::HID::ControllerTriggerType type) { ControllerUpdate(type); },
32 .is_npad_service = false,
33 };
34 callback_key = controller->SetCallback(engine_callback);
35 ControllerUpdate(Core::HID::ControllerTriggerType::All);
90} 36}
91 37
92void PlayerControlPreview::SetControllerType(const Settings::ControllerType type) { 38void PlayerControlPreview::UnloadController() {
93 controller_type = type; 39 if (is_controller_set) {
94 UpdateColors(); 40 controller->DeleteCallback(callback_key);
41 is_controller_set = false;
42 }
95} 43}
96 44
97void PlayerControlPreview::BeginMappingButton(std::size_t index) { 45void PlayerControlPreview::BeginMappingButton(std::size_t button_id) {
98 button_mapping_index = index; 46 button_mapping_index = button_id;
99 mapping_active = true; 47 mapping_active = true;
100} 48}
101 49
102void PlayerControlPreview::BeginMappingAnalog(std::size_t index) { 50void PlayerControlPreview::BeginMappingAnalog(std::size_t stick_id) {
103 button_mapping_index = Settings::NativeButton::LStick + index; 51 button_mapping_index = Settings::NativeButton::LStick + stick_id;
104 analog_mapping_index = index; 52 analog_mapping_index = stick_id;
105 mapping_active = true; 53 mapping_active = true;
106} 54}
107 55
@@ -157,84 +105,109 @@ void PlayerControlPreview::UpdateColors() {
157 colors.left = colors.primary; 105 colors.left = colors.primary;
158 colors.right = colors.primary; 106 colors.right = colors.primary;
159 // Possible alternative to set colors from settings 107 // Possible alternative to set colors from settings
160 // colors.left = QColor(Settings::values.players.GetValue()[player_index].body_color_left); 108 // colors.left = QColor(controller->GetColors().left.body);
161 // colors.right = QColor(Settings::values.players.GetValue()[player_index].body_color_right); 109 // colors.right = QColor(controller->GetColors().right.body);
162} 110}
163 111
164void PlayerControlPreview::ResetInputs() { 112void PlayerControlPreview::ResetInputs() {
165 for (std::size_t index = 0; index < button_values.size(); ++index) { 113 button_values.fill({
166 button_values[index] = false; 114 .value = false,
167 } 115 });
168 116 stick_values.fill({
169 for (std::size_t index = 0; index < axis_values.size(); ++index) { 117 .x = {.value = 0, .properties = {0, 1, 0}},
170 axis_values[index].properties = {0, 1, 0}; 118 .y = {.value = 0, .properties = {0, 1, 0}},
171 axis_values[index].value = {0, 0}; 119 });
172 axis_values[index].raw_value = {0, 0}; 120 trigger_values.fill({
173 } 121 .analog = {.value = 0, .properties = {0, 1, 0}},
122 .pressed = {.value = false},
123 });
174 update(); 124 update();
175} 125}
176 126
177void PlayerControlPreview::UpdateInput() { 127void PlayerControlPreview::ControllerUpdate(Core::HID::ControllerTriggerType type) {
178 if (!is_enabled && !mapping_active && !Settings::values.tas_enable) { 128 if (type == Core::HID::ControllerTriggerType::All) {
129 ControllerUpdate(Core::HID::ControllerTriggerType::Color);
130 ControllerUpdate(Core::HID::ControllerTriggerType::Type);
131 ControllerUpdate(Core::HID::ControllerTriggerType::Connected);
132 ControllerUpdate(Core::HID::ControllerTriggerType::Button);
133 ControllerUpdate(Core::HID::ControllerTriggerType::Stick);
134 ControllerUpdate(Core::HID::ControllerTriggerType::Trigger);
135 ControllerUpdate(Core::HID::ControllerTriggerType::Battery);
179 return; 136 return;
180 } 137 }
181 bool input_changed = false; 138
182 const auto& button_state = buttons; 139 switch (type) {
183 for (std::size_t index = 0; index < button_values.size(); ++index) { 140 case Core::HID::ControllerTriggerType::Connected:
184 bool value = false; 141 is_connected = true;
185 if (index < Settings::NativeButton::BUTTON_NS_END) { 142 led_pattern = controller->GetLedPattern();
186 value = button_state[index]->GetStatus(); 143 needs_redraw = true;
187 } 144 break;
188 bool blink = mapping_active && index == button_mapping_index; 145 case Core::HID::ControllerTriggerType::Disconnected:
189 if (analog_mapping_index == Settings::NativeAnalog::NUM_STICKS_HID) { 146 is_connected = false;
190 blink &= blink_counter > 25; 147 led_pattern.raw = 0;
191 } 148 needs_redraw = true;
192 if (button_values[index] != value || blink) { 149 break;
193 input_changed = true; 150 case Core::HID::ControllerTriggerType::Type:
194 } 151 controller_type = controller->GetNpadStyleIndex(true);
195 button_values[index] = value || blink; 152 needs_redraw = true;
153 break;
154 case Core::HID::ControllerTriggerType::Color:
155 UpdateColors();
156 needs_redraw = true;
157 break;
158 case Core::HID::ControllerTriggerType::Button:
159 button_values = controller->GetButtonsValues();
160 needs_redraw = true;
161 break;
162 case Core::HID::ControllerTriggerType::Stick:
163 using namespace Settings::NativeAnalog;
164 stick_values = controller->GetSticksValues();
165 // Y axis is inverted
166 stick_values[LStick].y.value = -stick_values[LStick].y.value;
167 stick_values[LStick].y.raw_value = -stick_values[LStick].y.raw_value;
168 stick_values[RStick].y.value = -stick_values[RStick].y.value;
169 stick_values[RStick].y.raw_value = -stick_values[RStick].y.raw_value;
170 needs_redraw = true;
171 break;
172 case Core::HID::ControllerTriggerType::Trigger:
173 trigger_values = controller->GetTriggersValues();
174 needs_redraw = true;
175 break;
176 case Core::HID::ControllerTriggerType::Battery:
177 battery_values = controller->GetBatteryValues();
178 needs_redraw = true;
179 break;
180 default:
181 break;
196 } 182 }
183}
197 184
198 const auto& analog_state = sticks; 185void PlayerControlPreview::UpdateInput() {
199 for (std::size_t index = 0; index < axis_values.size(); ++index) { 186 if (mapping_active) {
200 const auto [stick_x_f, stick_y_f] = analog_state[index]->GetStatus();
201 const auto [stick_x_rf, stick_y_rf] = analog_state[index]->GetRawStatus();
202 187
203 if (static_cast<int>(stick_x_rf * 45) != 188 for (std::size_t index = 0; index < button_values.size(); ++index) {
204 static_cast<int>(axis_values[index].raw_value.x() * 45) || 189 bool blink = index == button_mapping_index;
205 static_cast<int>(-stick_y_rf * 45) != 190 if (analog_mapping_index == Settings::NativeAnalog::NumAnalogs) {
206 static_cast<int>(axis_values[index].raw_value.y() * 45)) { 191 blink &= blink_counter > 25;
207 input_changed = true; 192 }
193 if (button_values[index].value != blink) {
194 needs_redraw = true;
195 }
196 button_values[index].value = blink;
208 } 197 }
209 198
210 axis_values[index].properties = analog_state[index]->GetAnalogProperties(); 199 for (std::size_t index = 0; index < stick_values.size(); ++index) {
211 axis_values[index].value = QPointF(stick_x_f, -stick_y_f); 200 const bool blink_analog = index == analog_mapping_index;
212 axis_values[index].raw_value = QPointF(stick_x_rf, -stick_y_rf); 201 if (blink_analog) {
213 202 needs_redraw = true;
214 const bool blink_analog = mapping_active && index == analog_mapping_index; 203 stick_values[index].x.value = blink_counter < 25 ? -blink_counter / 25.0f : 0;
215 if (blink_analog) { 204 stick_values[index].y.value =
216 input_changed = true; 205 blink_counter > 25 ? -(blink_counter - 25) / 25.0f : 0;
217 axis_values[index].value = 206 }
218 QPointF(blink_counter < 25 ? -blink_counter / 25.0f : 0,
219 blink_counter > 25 ? -(blink_counter - 25) / 25.0f : 0);
220 } 207 }
221 } 208 }
222 209 if (needs_redraw) {
223 if (input_changed) {
224 update(); 210 update();
225 if (controller_callback.input != nullptr) {
226 ControllerInput input{
227 .axis_values = {std::pair<float, float>{
228 axis_values[Settings::NativeAnalog::LStick].value.x(),
229 axis_values[Settings::NativeAnalog::LStick].value.y()},
230 std::pair<float, float>{
231 axis_values[Settings::NativeAnalog::RStick].value.x(),
232 axis_values[Settings::NativeAnalog::RStick].value.y()}},
233 .button_values = button_values,
234 .changed = true,
235 };
236 controller_callback.input(std::move(input));
237 }
238 } 211 }
239 212
240 if (mapping_active) { 213 if (mapping_active) {
@@ -242,10 +215,6 @@ void PlayerControlPreview::UpdateInput() {
242 } 215 }
243} 216}
244 217
245void PlayerControlPreview::SetCallBack(ControllerCallback callback_) {
246 controller_callback = std::move(callback_);
247}
248
249void PlayerControlPreview::paintEvent(QPaintEvent* event) { 218void PlayerControlPreview::paintEvent(QPaintEvent* event) {
250 QFrame::paintEvent(event); 219 QFrame::paintEvent(event);
251 QPainter p(this); 220 QPainter p(this);
@@ -253,22 +222,22 @@ void PlayerControlPreview::paintEvent(QPaintEvent* event) {
253 const QPointF center = rect().center(); 222 const QPointF center = rect().center();
254 223
255 switch (controller_type) { 224 switch (controller_type) {
256 case Settings::ControllerType::Handheld: 225 case Core::HID::NpadStyleIndex::Handheld:
257 DrawHandheldController(p, center); 226 DrawHandheldController(p, center);
258 break; 227 break;
259 case Settings::ControllerType::DualJoyconDetached: 228 case Core::HID::NpadStyleIndex::JoyconDual:
260 DrawDualController(p, center); 229 DrawDualController(p, center);
261 break; 230 break;
262 case Settings::ControllerType::LeftJoycon: 231 case Core::HID::NpadStyleIndex::JoyconLeft:
263 DrawLeftController(p, center); 232 DrawLeftController(p, center);
264 break; 233 break;
265 case Settings::ControllerType::RightJoycon: 234 case Core::HID::NpadStyleIndex::JoyconRight:
266 DrawRightController(p, center); 235 DrawRightController(p, center);
267 break; 236 break;
268 case Settings::ControllerType::GameCube: 237 case Core::HID::NpadStyleIndex::GameCube:
269 DrawGCController(p, center); 238 DrawGCController(p, center);
270 break; 239 break;
271 case Settings::ControllerType::ProController: 240 case Core::HID::NpadStyleIndex::ProController:
272 default: 241 default:
273 DrawProController(p, center); 242 DrawProController(p, center);
274 break; 243 break;
@@ -281,7 +250,7 @@ void PlayerControlPreview::DrawLeftController(QPainter& p, const QPointF center)
281 250
282 // Sideview left joystick 251 // Sideview left joystick
283 DrawJoystickSideview(p, center + QPoint(142, -69), 252 DrawJoystickSideview(p, center + QPoint(142, -69),
284 -axis_values[Settings::NativeAnalog::LStick].value.y(), 1.15f, 253 -stick_values[Settings::NativeAnalog::LStick].y.value, 1.15f,
285 button_values[LStick]); 254 button_values[LStick]);
286 255
287 // Topview D-pad buttons 256 // Topview D-pad buttons
@@ -292,7 +261,7 @@ void PlayerControlPreview::DrawLeftController(QPainter& p, const QPointF center)
292 261
293 // Topview left joystick 262 // Topview left joystick
294 DrawJoystickSideview(p, center + QPointF(-140.5f, -28), 263 DrawJoystickSideview(p, center + QPointF(-140.5f, -28),
295 -axis_values[Settings::NativeAnalog::LStick].value.x() + 15.0f, 1.15f, 264 -stick_values[Settings::NativeAnalog::LStick].x.value + 15.0f, 1.15f,
296 button_values[LStick]); 265 button_values[LStick]);
297 266
298 // Topview minus button 267 // Topview minus button
@@ -334,8 +303,10 @@ void PlayerControlPreview::DrawLeftController(QPainter& p, const QPointF center)
334 { 303 {
335 // Draw joysticks 304 // Draw joysticks
336 using namespace Settings::NativeAnalog; 305 using namespace Settings::NativeAnalog;
337 DrawJoystick(p, center + QPointF(9, -69) + (axis_values[LStick].value * 8), 1.8f, 306 DrawJoystick(p,
338 button_values[Settings::NativeButton::LStick]); 307 center + QPointF(9, -69) +
308 (QPointF(stick_values[LStick].x.value, stick_values[LStick].y.value) * 8),
309 1.8f, button_values[Settings::NativeButton::LStick]);
339 DrawRawJoystick(p, center + QPointF(-140, 90), QPointF(0, 0)); 310 DrawRawJoystick(p, center + QPointF(-140, 90), QPointF(0, 0));
340 } 311 }
341 312
@@ -384,6 +355,10 @@ void PlayerControlPreview::DrawLeftController(QPainter& p, const QPointF center)
384 p.setPen(colors.font2); 355 p.setPen(colors.font2);
385 p.setBrush(colors.font2); 356 p.setBrush(colors.font2);
386 DrawCircle(p, center + QPoint(26, 71), 5); 357 DrawCircle(p, center + QPoint(26, 71), 5);
358
359 // Draw battery
360 DrawBattery(p, center + QPoint(-170, -140),
361 battery_values[Core::HID::EmulatedDeviceIndex::LeftIndex]);
387} 362}
388 363
389void PlayerControlPreview::DrawRightController(QPainter& p, const QPointF center) { 364void PlayerControlPreview::DrawRightController(QPainter& p, const QPointF center) {
@@ -392,20 +367,22 @@ void PlayerControlPreview::DrawRightController(QPainter& p, const QPointF center
392 367
393 // Sideview right joystick 368 // Sideview right joystick
394 DrawJoystickSideview(p, center + QPoint(173 - 315, 11), 369 DrawJoystickSideview(p, center + QPoint(173 - 315, 11),
395 axis_values[Settings::NativeAnalog::RStick].value.y() + 10.0f, 1.15f, 370 stick_values[Settings::NativeAnalog::RStick].y.value + 10.0f, 1.15f,
396 button_values[Settings::NativeButton::RStick]); 371 button_values[Settings::NativeButton::RStick]);
397 372
373 // Topview right joystick
374 DrawJoystickSideview(p, center + QPointF(140, -28),
375 -stick_values[Settings::NativeAnalog::RStick].x.value + 15.0f, 1.15f,
376 button_values[RStick]);
377
398 // Topview face buttons 378 // Topview face buttons
399 p.setPen(colors.outline); 379 p.setPen(colors.outline);
400 button_color = colors.button; 380 button_color = colors.button;
401 DrawRoundButton(p, center + QPoint(163, -21), button_values[A], 11, 5, Direction::Up); 381 DrawRoundButton(p, center + QPoint(163, -21), button_values[A], 11, 5, Direction::Up);
382 DrawRoundButton(p, center + QPoint(140, -21), button_values[B], 11, 5, Direction::Up);
383 DrawRoundButton(p, center + QPoint(140, -21), button_values[X], 11, 5, Direction::Up);
402 DrawRoundButton(p, center + QPoint(117, -21), button_values[Y], 11, 5, Direction::Up); 384 DrawRoundButton(p, center + QPoint(117, -21), button_values[Y], 11, 5, Direction::Up);
403 385
404 // Topview right joystick
405 DrawJoystickSideview(p, center + QPointF(140, -28),
406 -axis_values[Settings::NativeAnalog::RStick].value.x() + 15.0f, 1.15f,
407 button_values[RStick]);
408
409 // Topview plus button 386 // Topview plus button
410 p.setPen(colors.outline); 387 p.setPen(colors.outline);
411 button_color = colors.button; 388 button_color = colors.button;
@@ -448,8 +425,10 @@ void PlayerControlPreview::DrawRightController(QPainter& p, const QPointF center
448 { 425 {
449 // Draw joysticks 426 // Draw joysticks
450 using namespace Settings::NativeAnalog; 427 using namespace Settings::NativeAnalog;
451 DrawJoystick(p, center + QPointF(-9, 11) + (axis_values[RStick].value * 8), 1.8f, 428 DrawJoystick(p,
452 button_values[Settings::NativeButton::RStick]); 429 center + QPointF(-9, 11) +
430 (QPointF(stick_values[RStick].x.value, stick_values[RStick].y.value) * 8),
431 1.8f, button_values[Settings::NativeButton::RStick]);
453 DrawRawJoystick(p, QPointF(0, 0), center + QPointF(140, 90)); 432 DrawRawJoystick(p, QPointF(0, 0), center + QPointF(140, 90));
454 } 433 }
455 434
@@ -503,6 +482,10 @@ void PlayerControlPreview::DrawRightController(QPainter& p, const QPointF center
503 p.setPen(colors.transparent); 482 p.setPen(colors.transparent);
504 p.setBrush(colors.font2); 483 p.setBrush(colors.font2);
505 DrawSymbol(p, center + QPoint(-26, 66), Symbol::House, 5); 484 DrawSymbol(p, center + QPoint(-26, 66), Symbol::House, 5);
485
486 // Draw battery
487 DrawBattery(p, center + QPoint(110, -140),
488 battery_values[Core::HID::EmulatedDeviceIndex::RightIndex]);
506} 489}
507 490
508void PlayerControlPreview::DrawDualController(QPainter& p, const QPointF center) { 491void PlayerControlPreview::DrawDualController(QPainter& p, const QPointF center) {
@@ -512,17 +495,19 @@ void PlayerControlPreview::DrawDualController(QPainter& p, const QPointF center)
512 // Left/Right trigger 495 // Left/Right trigger
513 DrawDualTriggers(p, center, button_values[L], button_values[R]); 496 DrawDualTriggers(p, center, button_values[L], button_values[R]);
514 497
498 // Topview right joystick
499 DrawJoystickSideview(p, center + QPointF(180, -78),
500 -stick_values[Settings::NativeAnalog::RStick].x.value + 15.0f, 1,
501 button_values[RStick]);
502
515 // Topview face buttons 503 // Topview face buttons
516 p.setPen(colors.outline); 504 p.setPen(colors.outline);
517 button_color = colors.button; 505 button_color = colors.button;
518 DrawRoundButton(p, center + QPoint(200, -71), button_values[A], 10, 5, Direction::Up); 506 DrawRoundButton(p, center + QPoint(200, -71), button_values[A], 10, 5, Direction::Up);
507 DrawRoundButton(p, center + QPoint(180, -71), button_values[B], 10, 5, Direction::Up);
508 DrawRoundButton(p, center + QPoint(180, -71), button_values[X], 10, 5, Direction::Up);
519 DrawRoundButton(p, center + QPoint(160, -71), button_values[Y], 10, 5, Direction::Up); 509 DrawRoundButton(p, center + QPoint(160, -71), button_values[Y], 10, 5, Direction::Up);
520 510
521 // Topview right joystick
522 DrawJoystickSideview(p, center + QPointF(180, -78),
523 -axis_values[Settings::NativeAnalog::RStick].value.x() + 15.0f, 1,
524 button_values[RStick]);
525
526 // Topview plus button 511 // Topview plus button
527 p.setPen(colors.outline); 512 p.setPen(colors.outline);
528 button_color = colors.button; 513 button_color = colors.button;
@@ -538,7 +523,7 @@ void PlayerControlPreview::DrawDualController(QPainter& p, const QPointF center)
538 523
539 // Topview left joystick 524 // Topview left joystick
540 DrawJoystickSideview(p, center + QPointF(-180.5f, -78), 525 DrawJoystickSideview(p, center + QPointF(-180.5f, -78),
541 -axis_values[Settings::NativeAnalog::LStick].value.x() + 15.0f, 1, 526 -stick_values[Settings::NativeAnalog::LStick].x.value + 15.0f, 1,
542 button_values[LStick]); 527 button_values[LStick]);
543 528
544 // Topview minus button 529 // Topview minus button
@@ -557,13 +542,13 @@ void PlayerControlPreview::DrawDualController(QPainter& p, const QPointF center)
557 { 542 {
558 // Draw joysticks 543 // Draw joysticks
559 using namespace Settings::NativeAnalog; 544 using namespace Settings::NativeAnalog;
560 const auto& l_stick = axis_values[LStick]; 545 const auto l_stick = QPointF(stick_values[LStick].x.value, stick_values[LStick].y.value);
561 const auto l_button = button_values[Settings::NativeButton::LStick]; 546 const auto l_button = button_values[Settings::NativeButton::LStick];
562 const auto& r_stick = axis_values[RStick]; 547 const auto r_stick = QPointF(stick_values[RStick].x.value, stick_values[RStick].y.value);
563 const auto r_button = button_values[Settings::NativeButton::RStick]; 548 const auto r_button = button_values[Settings::NativeButton::RStick];
564 549
565 DrawJoystick(p, center + QPointF(-65, -65) + (l_stick.value * 7), 1.62f, l_button); 550 DrawJoystick(p, center + QPointF(-65, -65) + (l_stick * 7), 1.62f, l_button);
566 DrawJoystick(p, center + QPointF(65, 12) + (r_stick.value * 7), 1.62f, r_button); 551 DrawJoystick(p, center + QPointF(65, 12) + (r_stick * 7), 1.62f, r_button);
567 DrawRawJoystick(p, center + QPointF(-180, 90), center + QPointF(180, 90)); 552 DrawRawJoystick(p, center + QPointF(-180, 90), center + QPointF(180, 90));
568 } 553 }
569 554
@@ -634,6 +619,12 @@ void PlayerControlPreview::DrawDualController(QPainter& p, const QPointF center)
634 p.setPen(colors.transparent); 619 p.setPen(colors.transparent);
635 p.setBrush(colors.font2); 620 p.setBrush(colors.font2);
636 DrawSymbol(p, center + QPoint(50, 60), Symbol::House, 4.2f); 621 DrawSymbol(p, center + QPoint(50, 60), Symbol::House, 4.2f);
622
623 // Draw battery
624 DrawBattery(p, center + QPoint(-100, -160),
625 battery_values[Core::HID::EmulatedDeviceIndex::LeftIndex]);
626 DrawBattery(p, center + QPoint(40, -160),
627 battery_values[Core::HID::EmulatedDeviceIndex::RightIndex]);
637} 628}
638 629
639void PlayerControlPreview::DrawHandheldController(QPainter& p, const QPointF center) { 630void PlayerControlPreview::DrawHandheldController(QPainter& p, const QPointF center) {
@@ -643,13 +634,13 @@ void PlayerControlPreview::DrawHandheldController(QPainter& p, const QPointF cen
643 { 634 {
644 // Draw joysticks 635 // Draw joysticks
645 using namespace Settings::NativeAnalog; 636 using namespace Settings::NativeAnalog;
646 const auto& l_stick = axis_values[LStick]; 637 const auto l_stick = QPointF(stick_values[LStick].x.value, stick_values[LStick].y.value);
647 const auto l_button = button_values[Settings::NativeButton::LStick]; 638 const auto l_button = button_values[Settings::NativeButton::LStick];
648 const auto& r_stick = axis_values[RStick]; 639 const auto r_stick = QPointF(stick_values[RStick].x.value, stick_values[RStick].y.value);
649 const auto r_button = button_values[Settings::NativeButton::RStick]; 640 const auto r_button = button_values[Settings::NativeButton::RStick];
650 641
651 DrawJoystick(p, center + QPointF(-171, -41) + (l_stick.value * 4), 1.0f, l_button); 642 DrawJoystick(p, center + QPointF(-171, -41) + (l_stick * 4), 1.0f, l_button);
652 DrawJoystick(p, center + QPointF(171, 8) + (r_stick.value * 4), 1.0f, r_button); 643 DrawJoystick(p, center + QPointF(171, 8) + (r_stick * 4), 1.0f, r_button);
653 DrawRawJoystick(p, center + QPointF(-50, 0), center + QPointF(50, 0)); 644 DrawRawJoystick(p, center + QPointF(-50, 0), center + QPointF(50, 0));
654 } 645 }
655 646
@@ -732,6 +723,12 @@ void PlayerControlPreview::DrawHandheldController(QPainter& p, const QPointF cen
732 p.setPen(colors.transparent); 723 p.setPen(colors.transparent);
733 p.setBrush(colors.font2); 724 p.setBrush(colors.font2);
734 DrawSymbol(p, center + QPoint(161, 37), Symbol::House, 2.75f); 725 DrawSymbol(p, center + QPoint(161, 37), Symbol::House, 2.75f);
726
727 // Draw battery
728 DrawBattery(p, center + QPoint(-200, 110),
729 battery_values[Core::HID::EmulatedDeviceIndex::LeftIndex]);
730 DrawBattery(p, center + QPoint(130, 110),
731 battery_values[Core::HID::EmulatedDeviceIndex::RightIndex]);
735} 732}
736 733
737void PlayerControlPreview::DrawProController(QPainter& p, const QPointF center) { 734void PlayerControlPreview::DrawProController(QPainter& p, const QPointF center) {
@@ -741,9 +738,11 @@ void PlayerControlPreview::DrawProController(QPainter& p, const QPointF center)
741 { 738 {
742 // Draw joysticks 739 // Draw joysticks
743 using namespace Settings::NativeAnalog; 740 using namespace Settings::NativeAnalog;
744 DrawProJoystick(p, center + QPointF(-111, -55), axis_values[LStick].value, 11, 741 const auto l_stick = QPointF(stick_values[LStick].x.value, stick_values[LStick].y.value);
742 const auto r_stick = QPointF(stick_values[RStick].x.value, stick_values[RStick].y.value);
743 DrawProJoystick(p, center + QPointF(-111, -55), l_stick, 11,
745 button_values[Settings::NativeButton::LStick]); 744 button_values[Settings::NativeButton::LStick]);
746 DrawProJoystick(p, center + QPointF(51, 0), axis_values[RStick].value, 11, 745 DrawProJoystick(p, center + QPointF(51, 0), r_stick, 11,
747 button_values[Settings::NativeButton::RStick]); 746 button_values[Settings::NativeButton::RStick]);
748 DrawRawJoystick(p, center + QPointF(-50, 105), center + QPointF(50, 105)); 747 DrawRawJoystick(p, center + QPointF(-50, 105), center + QPointF(50, 105));
749 } 748 }
@@ -817,24 +816,27 @@ void PlayerControlPreview::DrawProController(QPainter& p, const QPointF center)
817 p.setPen(colors.transparent); 816 p.setPen(colors.transparent);
818 p.setBrush(colors.font2); 817 p.setBrush(colors.font2);
819 DrawSymbol(p, center + QPoint(29, -56), Symbol::House, 3.9f); 818 DrawSymbol(p, center + QPoint(29, -56), Symbol::House, 3.9f);
819
820 // Draw battery
821 DrawBattery(p, center + QPoint(-30, -160),
822 battery_values[Core::HID::EmulatedDeviceIndex::LeftIndex]);
820} 823}
821 824
822void PlayerControlPreview::DrawGCController(QPainter& p, const QPointF center) { 825void PlayerControlPreview::DrawGCController(QPainter& p, const QPointF center) {
823 DrawGCTriggers(p, center, button_values[Settings::NativeButton::ZL], 826 DrawGCTriggers(p, center, trigger_values[0], trigger_values[1]);
824 button_values[Settings::NativeButton::ZR]);
825 DrawGCButtonZ(p, center, button_values[Settings::NativeButton::R]); 827 DrawGCButtonZ(p, center, button_values[Settings::NativeButton::R]);
826 DrawGCBody(p, center); 828 DrawGCBody(p, center);
827 { 829 {
828 // Draw joysticks 830 // Draw joysticks
829 using namespace Settings::NativeAnalog; 831 using namespace Settings::NativeAnalog;
830 DrawGCJoystick(p, center + QPointF(-111, -44) + (axis_values[LStick].value * 10), false); 832 const auto l_stick = QPointF(stick_values[LStick].x.value, stick_values[LStick].y.value);
833 const auto r_stick = QPointF(stick_values[RStick].x.value, stick_values[RStick].y.value);
834 DrawGCJoystick(p, center + QPointF(-111, -44) + (l_stick * 10), {});
831 button_color = colors.button2; 835 button_color = colors.button2;
832 DrawCircleButton(p, center + QPointF(61, 37) + (axis_values[RStick].value * 9.5f), false, 836 DrawCircleButton(p, center + QPointF(61, 37) + (r_stick * 9.5f), {}, 15);
833 15);
834 p.setPen(colors.transparent); 837 p.setPen(colors.transparent);
835 p.setBrush(colors.font); 838 p.setBrush(colors.font);
836 DrawSymbol(p, center + QPointF(61, 37) + (axis_values[RStick].value * 9.5f), Symbol::C, 839 DrawSymbol(p, center + QPointF(61, 37) + (r_stick * 9.5f), Symbol::C, 1.0f);
837 1.0f);
838 DrawRawJoystick(p, center + QPointF(-198, -125), center + QPointF(198, -125)); 840 DrawRawJoystick(p, center + QPointF(-198, -125), center + QPointF(198, -125));
839 } 841 }
840 842
@@ -871,6 +873,10 @@ void PlayerControlPreview::DrawGCController(QPainter& p, const QPointF center) {
871 // Minus and Plus buttons 873 // Minus and Plus buttons
872 p.setPen(colors.outline); 874 p.setPen(colors.outline);
873 DrawCircleButton(p, center + QPoint(0, -44), button_values[Plus], 8); 875 DrawCircleButton(p, center + QPoint(0, -44), button_values[Plus], 8);
876
877 // Draw battery
878 DrawBattery(p, center + QPoint(-30, -165),
879 battery_values[Core::HID::EmulatedDeviceIndex::LeftIndex]);
874} 880}
875 881
876constexpr std::array<float, 13 * 2> symbol_a = { 882constexpr std::array<float, 13 * 2> symbol_a = {
@@ -1837,10 +1843,14 @@ void PlayerControlPreview::DrawLeftBody(QPainter& p, const QPointF center) {
1837 const float led_size = 5.0f; 1843 const float led_size = 5.0f;
1838 const QPointF led_position = sideview_center + QPointF(0, -36); 1844 const QPointF led_position = sideview_center + QPointF(0, -36);
1839 int led_count = 0; 1845 int led_count = 0;
1840 for (const auto& color : led_color) { 1846 p.setBrush(led_pattern.position1 ? colors.led_on : colors.led_off);
1841 p.setBrush(color); 1847 DrawRectangle(p, led_position + QPointF(0, 12 * led_count++), led_size, led_size);
1842 DrawRectangle(p, led_position + QPointF(0, 12 * led_count++), led_size, led_size); 1848 p.setBrush(led_pattern.position2 ? colors.led_on : colors.led_off);
1843 } 1849 DrawRectangle(p, led_position + QPointF(0, 12 * led_count++), led_size, led_size);
1850 p.setBrush(led_pattern.position3 ? colors.led_on : colors.led_off);
1851 DrawRectangle(p, led_position + QPointF(0, 12 * led_count++), led_size, led_size);
1852 p.setBrush(led_pattern.position4 ? colors.led_on : colors.led_off);
1853 DrawRectangle(p, led_position + QPointF(0, 12 * led_count++), led_size, led_size);
1844} 1854}
1845 1855
1846void PlayerControlPreview::DrawRightBody(QPainter& p, const QPointF center) { 1856void PlayerControlPreview::DrawRightBody(QPainter& p, const QPointF center) {
@@ -1933,14 +1943,19 @@ void PlayerControlPreview::DrawRightBody(QPainter& p, const QPointF center) {
1933 const float led_size = 5.0f; 1943 const float led_size = 5.0f;
1934 const QPointF led_position = sideview_center + QPointF(0, -36); 1944 const QPointF led_position = sideview_center + QPointF(0, -36);
1935 int led_count = 0; 1945 int led_count = 0;
1936 for (const auto& color : led_color) { 1946 p.setBrush(led_pattern.position1 ? colors.led_on : colors.led_off);
1937 p.setBrush(color); 1947 DrawRectangle(p, led_position + QPointF(0, 12 * led_count++), led_size, led_size);
1938 DrawRectangle(p, led_position + QPointF(0, 12 * led_count++), led_size, led_size); 1948 p.setBrush(led_pattern.position2 ? colors.led_on : colors.led_off);
1939 } 1949 DrawRectangle(p, led_position + QPointF(0, 12 * led_count++), led_size, led_size);
1940} 1950 p.setBrush(led_pattern.position3 ? colors.led_on : colors.led_off);
1941 1951 DrawRectangle(p, led_position + QPointF(0, 12 * led_count++), led_size, led_size);
1942void PlayerControlPreview::DrawProTriggers(QPainter& p, const QPointF center, bool left_pressed, 1952 p.setBrush(led_pattern.position4 ? colors.led_on : colors.led_off);
1943 bool right_pressed) { 1953 DrawRectangle(p, led_position + QPointF(0, 12 * led_count++), led_size, led_size);
1954}
1955
1956void PlayerControlPreview::DrawProTriggers(QPainter& p, const QPointF center,
1957 const Common::Input::ButtonStatus& left_pressed,
1958 const Common::Input::ButtonStatus& right_pressed) {
1944 std::array<QPointF, pro_left_trigger.size() / 2> qleft_trigger; 1959 std::array<QPointF, pro_left_trigger.size() / 2> qleft_trigger;
1945 std::array<QPointF, pro_left_trigger.size() / 2> qright_trigger; 1960 std::array<QPointF, pro_left_trigger.size() / 2> qright_trigger;
1946 std::array<QPointF, pro_body_top.size()> qbody_top; 1961 std::array<QPointF, pro_body_top.size()> qbody_top;
@@ -1949,8 +1964,10 @@ void PlayerControlPreview::DrawProTriggers(QPainter& p, const QPointF center, bo
1949 const float trigger_x = pro_left_trigger[point * 2 + 0]; 1964 const float trigger_x = pro_left_trigger[point * 2 + 0];
1950 const float trigger_y = pro_left_trigger[point * 2 + 1]; 1965 const float trigger_y = pro_left_trigger[point * 2 + 1];
1951 1966
1952 qleft_trigger[point] = center + QPointF(trigger_x, trigger_y + (left_pressed ? 2 : 0)); 1967 qleft_trigger[point] =
1953 qright_trigger[point] = center + QPointF(-trigger_x, trigger_y + (right_pressed ? 2 : 0)); 1968 center + QPointF(trigger_x, trigger_y + (left_pressed.value ? 2 : 0));
1969 qright_trigger[point] =
1970 center + QPointF(-trigger_x, trigger_y + (right_pressed.value ? 2 : 0));
1954 } 1971 }
1955 1972
1956 for (std::size_t point = 0; point < pro_body_top.size() / 2; ++point) { 1973 for (std::size_t point = 0; point < pro_body_top.size() / 2; ++point) {
@@ -1967,16 +1984,17 @@ void PlayerControlPreview::DrawProTriggers(QPainter& p, const QPointF center, bo
1967 DrawPolygon(p, qbody_top); 1984 DrawPolygon(p, qbody_top);
1968 1985
1969 // Left trigger 1986 // Left trigger
1970 p.setBrush(left_pressed ? colors.highlight : colors.button); 1987 p.setBrush(left_pressed.value ? colors.highlight : colors.button);
1971 DrawPolygon(p, qleft_trigger); 1988 DrawPolygon(p, qleft_trigger);
1972 1989
1973 // Right trigger 1990 // Right trigger
1974 p.setBrush(right_pressed ? colors.highlight : colors.button); 1991 p.setBrush(right_pressed.value ? colors.highlight : colors.button);
1975 DrawPolygon(p, qright_trigger); 1992 DrawPolygon(p, qright_trigger);
1976} 1993}
1977 1994
1978void PlayerControlPreview::DrawGCTriggers(QPainter& p, const QPointF center, bool left_pressed, 1995void PlayerControlPreview::DrawGCTriggers(QPainter& p, const QPointF center,
1979 bool right_pressed) { 1996 Common::Input::TriggerStatus left_trigger,
1997 Common::Input::TriggerStatus right_trigger) {
1980 std::array<QPointF, left_gc_trigger.size() / 2> qleft_trigger; 1998 std::array<QPointF, left_gc_trigger.size() / 2> qleft_trigger;
1981 std::array<QPointF, left_gc_trigger.size() / 2> qright_trigger; 1999 std::array<QPointF, left_gc_trigger.size() / 2> qright_trigger;
1982 2000
@@ -1984,32 +2002,37 @@ void PlayerControlPreview::DrawGCTriggers(QPainter& p, const QPointF center, boo
1984 const float trigger_x = left_gc_trigger[point * 2 + 0]; 2002 const float trigger_x = left_gc_trigger[point * 2 + 0];
1985 const float trigger_y = left_gc_trigger[point * 2 + 1]; 2003 const float trigger_y = left_gc_trigger[point * 2 + 1];
1986 2004
1987 qleft_trigger[point] = center + QPointF(trigger_x, trigger_y + (left_pressed ? 10 : 0)); 2005 qleft_trigger[point] =
1988 qright_trigger[point] = center + QPointF(-trigger_x, trigger_y + (right_pressed ? 10 : 0)); 2006 center + QPointF(trigger_x, trigger_y + (left_trigger.analog.value * 10.0f));
2007 qright_trigger[point] =
2008 center + QPointF(-trigger_x, trigger_y + (right_trigger.analog.value * 10.0f));
1989 } 2009 }
1990 2010
1991 // Left trigger 2011 // Left trigger
1992 p.setPen(colors.outline); 2012 p.setPen(colors.outline);
1993 p.setBrush(left_pressed ? colors.highlight : colors.button); 2013 p.setBrush(left_trigger.pressed.value ? colors.highlight : colors.button);
1994 DrawPolygon(p, qleft_trigger); 2014 DrawPolygon(p, qleft_trigger);
1995 2015
1996 // Right trigger 2016 // Right trigger
1997 p.setBrush(right_pressed ? colors.highlight : colors.button); 2017 p.setBrush(right_trigger.pressed.value ? colors.highlight : colors.button);
1998 DrawPolygon(p, qright_trigger); 2018 DrawPolygon(p, qright_trigger);
1999 2019
2000 // Draw L text 2020 // Draw L text
2001 p.setPen(colors.transparent); 2021 p.setPen(colors.transparent);
2002 p.setBrush(colors.font); 2022 p.setBrush(colors.font);
2003 DrawSymbol(p, center + QPointF(-132, -119 + (left_pressed ? 10 : 0)), Symbol::L, 1.7f); 2023 DrawSymbol(p, center + QPointF(-132, -119 + (left_trigger.analog.value * 10.0f)), Symbol::L,
2024 1.7f);
2004 2025
2005 // Draw R text 2026 // Draw R text
2006 p.setPen(colors.transparent); 2027 p.setPen(colors.transparent);
2007 p.setBrush(colors.font); 2028 p.setBrush(colors.font);
2008 DrawSymbol(p, center + QPointF(121.5f, -119 + (right_pressed ? 10 : 0)), Symbol::R, 1.7f); 2029 DrawSymbol(p, center + QPointF(121.5f, -119 + (right_trigger.analog.value * 10.0f)), Symbol::R,
2030 1.7f);
2009} 2031}
2010 2032
2011void PlayerControlPreview::DrawHandheldTriggers(QPainter& p, const QPointF center, 2033void PlayerControlPreview::DrawHandheldTriggers(QPainter& p, const QPointF center,
2012 bool left_pressed, bool right_pressed) { 2034 const Common::Input::ButtonStatus& left_pressed,
2035 const Common::Input::ButtonStatus& right_pressed) {
2013 std::array<QPointF, left_joycon_trigger.size() / 2> qleft_trigger; 2036 std::array<QPointF, left_joycon_trigger.size() / 2> qleft_trigger;
2014 std::array<QPointF, left_joycon_trigger.size() / 2> qright_trigger; 2037 std::array<QPointF, left_joycon_trigger.size() / 2> qright_trigger;
2015 2038
@@ -2018,23 +2041,24 @@ void PlayerControlPreview::DrawHandheldTriggers(QPainter& p, const QPointF cente
2018 const float left_trigger_y = left_joycon_trigger[point * 2 + 1]; 2041 const float left_trigger_y = left_joycon_trigger[point * 2 + 1];
2019 2042
2020 qleft_trigger[point] = 2043 qleft_trigger[point] =
2021 center + QPointF(left_trigger_x, left_trigger_y + (left_pressed ? 0.5f : 0)); 2044 center + QPointF(left_trigger_x, left_trigger_y + (left_pressed.value ? 0.5f : 0));
2022 qright_trigger[point] = 2045 qright_trigger[point] =
2023 center + QPointF(-left_trigger_x, left_trigger_y + (right_pressed ? 0.5f : 0)); 2046 center + QPointF(-left_trigger_x, left_trigger_y + (right_pressed.value ? 0.5f : 0));
2024 } 2047 }
2025 2048
2026 // Left trigger 2049 // Left trigger
2027 p.setPen(colors.outline); 2050 p.setPen(colors.outline);
2028 p.setBrush(left_pressed ? colors.highlight : colors.button); 2051 p.setBrush(left_pressed.value ? colors.highlight : colors.button);
2029 DrawPolygon(p, qleft_trigger); 2052 DrawPolygon(p, qleft_trigger);
2030 2053
2031 // Right trigger 2054 // Right trigger
2032 p.setBrush(right_pressed ? colors.highlight : colors.button); 2055 p.setBrush(right_pressed.value ? colors.highlight : colors.button);
2033 DrawPolygon(p, qright_trigger); 2056 DrawPolygon(p, qright_trigger);
2034} 2057}
2035 2058
2036void PlayerControlPreview::DrawDualTriggers(QPainter& p, const QPointF center, bool left_pressed, 2059void PlayerControlPreview::DrawDualTriggers(QPainter& p, const QPointF center,
2037 bool right_pressed) { 2060 const Common::Input::ButtonStatus& left_pressed,
2061 const Common::Input::ButtonStatus& right_pressed) {
2038 std::array<QPointF, left_joycon_trigger.size() / 2> qleft_trigger; 2062 std::array<QPointF, left_joycon_trigger.size() / 2> qleft_trigger;
2039 std::array<QPointF, left_joycon_trigger.size() / 2> qright_trigger; 2063 std::array<QPointF, left_joycon_trigger.size() / 2> qright_trigger;
2040 constexpr float size = 1.62f; 2064 constexpr float size = 1.62f;
@@ -2043,25 +2067,27 @@ void PlayerControlPreview::DrawDualTriggers(QPainter& p, const QPointF center, b
2043 const float left_trigger_x = left_joycon_trigger[point * 2 + 0]; 2067 const float left_trigger_x = left_joycon_trigger[point * 2 + 0];
2044 const float left_trigger_y = left_joycon_trigger[point * 2 + 1]; 2068 const float left_trigger_y = left_joycon_trigger[point * 2 + 1];
2045 2069
2046 qleft_trigger[point] = center + QPointF(left_trigger_x * size + offset, 2070 qleft_trigger[point] =
2047 left_trigger_y * size + (left_pressed ? 0.5f : 0)); 2071 center + QPointF(left_trigger_x * size + offset,
2072 left_trigger_y * size + (left_pressed.value ? 0.5f : 0));
2048 qright_trigger[point] = 2073 qright_trigger[point] =
2049 center + QPointF(-left_trigger_x * size - offset, 2074 center + QPointF(-left_trigger_x * size - offset,
2050 left_trigger_y * size + (right_pressed ? 0.5f : 0)); 2075 left_trigger_y * size + (right_pressed.value ? 0.5f : 0));
2051 } 2076 }
2052 2077
2053 // Left trigger 2078 // Left trigger
2054 p.setPen(colors.outline); 2079 p.setPen(colors.outline);
2055 p.setBrush(left_pressed ? colors.highlight : colors.button); 2080 p.setBrush(left_pressed.value ? colors.highlight : colors.button);
2056 DrawPolygon(p, qleft_trigger); 2081 DrawPolygon(p, qleft_trigger);
2057 2082
2058 // Right trigger 2083 // Right trigger
2059 p.setBrush(right_pressed ? colors.highlight : colors.button); 2084 p.setBrush(right_pressed.value ? colors.highlight : colors.button);
2060 DrawPolygon(p, qright_trigger); 2085 DrawPolygon(p, qright_trigger);
2061} 2086}
2062 2087
2063void PlayerControlPreview::DrawDualTriggersTopView(QPainter& p, const QPointF center, 2088void PlayerControlPreview::DrawDualTriggersTopView(
2064 bool left_pressed, bool right_pressed) { 2089 QPainter& p, const QPointF center, const Common::Input::ButtonStatus& left_pressed,
2090 const Common::Input::ButtonStatus& right_pressed) {
2065 std::array<QPointF, left_joystick_L_topview.size() / 2> qleft_trigger; 2091 std::array<QPointF, left_joystick_L_topview.size() / 2> qleft_trigger;
2066 std::array<QPointF, left_joystick_L_topview.size() / 2> qright_trigger; 2092 std::array<QPointF, left_joystick_L_topview.size() / 2> qright_trigger;
2067 constexpr float size = 0.9f; 2093 constexpr float size = 0.9f;
@@ -2080,9 +2106,9 @@ void PlayerControlPreview::DrawDualTriggersTopView(QPainter& p, const QPointF ce
2080 } 2106 }
2081 2107
2082 p.setPen(colors.outline); 2108 p.setPen(colors.outline);
2083 p.setBrush(left_pressed ? colors.highlight : colors.button); 2109 p.setBrush(left_pressed.value ? colors.highlight : colors.button);
2084 DrawPolygon(p, qleft_trigger); 2110 DrawPolygon(p, qleft_trigger);
2085 p.setBrush(right_pressed ? colors.highlight : colors.button); 2111 p.setBrush(right_pressed.value ? colors.highlight : colors.button);
2086 DrawPolygon(p, qright_trigger); 2112 DrawPolygon(p, qright_trigger);
2087 2113
2088 // Draw L text 2114 // Draw L text
@@ -2096,8 +2122,9 @@ void PlayerControlPreview::DrawDualTriggersTopView(QPainter& p, const QPointF ce
2096 DrawSymbol(p, center + QPointF(177, -84), Symbol::R, 1.0f); 2122 DrawSymbol(p, center + QPointF(177, -84), Symbol::R, 1.0f);
2097} 2123}
2098 2124
2099void PlayerControlPreview::DrawDualZTriggersTopView(QPainter& p, const QPointF center, 2125void PlayerControlPreview::DrawDualZTriggersTopView(
2100 bool left_pressed, bool right_pressed) { 2126 QPainter& p, const QPointF center, const Common::Input::ButtonStatus& left_pressed,
2127 const Common::Input::ButtonStatus& right_pressed) {
2101 std::array<QPointF, left_joystick_ZL_topview.size() / 2> qleft_trigger; 2128 std::array<QPointF, left_joystick_ZL_topview.size() / 2> qleft_trigger;
2102 std::array<QPointF, left_joystick_ZL_topview.size() / 2> qright_trigger; 2129 std::array<QPointF, left_joystick_ZL_topview.size() / 2> qright_trigger;
2103 constexpr float size = 0.9f; 2130 constexpr float size = 0.9f;
@@ -2114,9 +2141,9 @@ void PlayerControlPreview::DrawDualZTriggersTopView(QPainter& p, const QPointF c
2114 } 2141 }
2115 2142
2116 p.setPen(colors.outline); 2143 p.setPen(colors.outline);
2117 p.setBrush(left_pressed ? colors.highlight : colors.button); 2144 p.setBrush(left_pressed.value ? colors.highlight : colors.button);
2118 DrawPolygon(p, qleft_trigger); 2145 DrawPolygon(p, qleft_trigger);
2119 p.setBrush(right_pressed ? colors.highlight : colors.button); 2146 p.setBrush(right_pressed.value ? colors.highlight : colors.button);
2120 DrawPolygon(p, qright_trigger); 2147 DrawPolygon(p, qright_trigger);
2121 2148
2122 // Draw ZL text 2149 // Draw ZL text
@@ -2130,7 +2157,8 @@ void PlayerControlPreview::DrawDualZTriggersTopView(QPainter& p, const QPointF c
2130 DrawSymbol(p, center + QPointF(180, -113), Symbol::ZR, 1.0f); 2157 DrawSymbol(p, center + QPointF(180, -113), Symbol::ZR, 1.0f);
2131} 2158}
2132 2159
2133void PlayerControlPreview::DrawLeftTriggers(QPainter& p, const QPointF center, bool left_pressed) { 2160void PlayerControlPreview::DrawLeftTriggers(QPainter& p, const QPointF center,
2161 const Common::Input::ButtonStatus& left_pressed) {
2134 std::array<QPointF, left_joycon_trigger.size() / 2> qleft_trigger; 2162 std::array<QPointF, left_joycon_trigger.size() / 2> qleft_trigger;
2135 constexpr float size = 1.78f; 2163 constexpr float size = 1.78f;
2136 constexpr float offset = 311.5f; 2164 constexpr float offset = 311.5f;
@@ -2138,15 +2166,16 @@ void PlayerControlPreview::DrawLeftTriggers(QPainter& p, const QPointF center, b
2138 for (std::size_t point = 0; point < left_joycon_trigger.size() / 2; ++point) { 2166 for (std::size_t point = 0; point < left_joycon_trigger.size() / 2; ++point) {
2139 qleft_trigger[point] = center + QPointF(left_joycon_trigger[point * 2] * size + offset, 2167 qleft_trigger[point] = center + QPointF(left_joycon_trigger[point * 2] * size + offset,
2140 left_joycon_trigger[point * 2 + 1] * size - 2168 left_joycon_trigger[point * 2 + 1] * size -
2141 (left_pressed ? 0.5f : 1.0f)); 2169 (left_pressed.value ? 0.5f : 1.0f));
2142 } 2170 }
2143 2171
2144 p.setPen(colors.outline); 2172 p.setPen(colors.outline);
2145 p.setBrush(left_pressed ? colors.highlight : colors.button); 2173 p.setBrush(left_pressed.value ? colors.highlight : colors.button);
2146 DrawPolygon(p, qleft_trigger); 2174 DrawPolygon(p, qleft_trigger);
2147} 2175}
2148 2176
2149void PlayerControlPreview::DrawLeftZTriggers(QPainter& p, const QPointF center, bool left_pressed) { 2177void PlayerControlPreview::DrawLeftZTriggers(QPainter& p, const QPointF center,
2178 const Common::Input::ButtonStatus& left_pressed) {
2150 std::array<QPointF, left_joycon_sideview_zl.size() / 2> qleft_trigger; 2179 std::array<QPointF, left_joycon_sideview_zl.size() / 2> qleft_trigger;
2151 constexpr float size = 1.1115f; 2180 constexpr float size = 1.1115f;
2152 constexpr float offset2 = 335; 2181 constexpr float offset2 = 335;
@@ -2154,18 +2183,18 @@ void PlayerControlPreview::DrawLeftZTriggers(QPainter& p, const QPointF center,
2154 for (std::size_t point = 0; point < left_joycon_sideview_zl.size() / 2; ++point) { 2183 for (std::size_t point = 0; point < left_joycon_sideview_zl.size() / 2; ++point) {
2155 qleft_trigger[point] = center + QPointF(left_joycon_sideview_zl[point * 2] * size + offset2, 2184 qleft_trigger[point] = center + QPointF(left_joycon_sideview_zl[point * 2] * size + offset2,
2156 left_joycon_sideview_zl[point * 2 + 1] * size + 2185 left_joycon_sideview_zl[point * 2 + 1] * size +
2157 (left_pressed ? 1.5f : 1.0f)); 2186 (left_pressed.value ? 1.5f : 1.0f));
2158 } 2187 }
2159 2188
2160 p.setPen(colors.outline); 2189 p.setPen(colors.outline);
2161 p.setBrush(left_pressed ? colors.highlight : colors.button); 2190 p.setBrush(left_pressed.value ? colors.highlight : colors.button);
2162 DrawPolygon(p, qleft_trigger); 2191 DrawPolygon(p, qleft_trigger);
2163 p.drawArc(center.x() + 158, center.y() + (left_pressed ? -203.5f : -204.0f), 77, 77, 225 * 16, 2192 p.drawArc(center.x() + 158, center.y() + (left_pressed.value ? -203.5f : -204.0f), 77, 77,
2164 44 * 16); 2193 225 * 16, 44 * 16);
2165} 2194}
2166 2195
2167void PlayerControlPreview::DrawLeftTriggersTopView(QPainter& p, const QPointF center, 2196void PlayerControlPreview::DrawLeftTriggersTopView(
2168 bool left_pressed) { 2197 QPainter& p, const QPointF center, const Common::Input::ButtonStatus& left_pressed) {
2169 std::array<QPointF, left_joystick_L_topview.size() / 2> qleft_trigger; 2198 std::array<QPointF, left_joystick_L_topview.size() / 2> qleft_trigger;
2170 2199
2171 for (std::size_t point = 0; point < left_joystick_L_topview.size() / 2; ++point) { 2200 for (std::size_t point = 0; point < left_joystick_L_topview.size() / 2; ++point) {
@@ -2174,7 +2203,7 @@ void PlayerControlPreview::DrawLeftTriggersTopView(QPainter& p, const QPointF ce
2174 } 2203 }
2175 2204
2176 p.setPen(colors.outline); 2205 p.setPen(colors.outline);
2177 p.setBrush(left_pressed ? colors.highlight : colors.button); 2206 p.setBrush(left_pressed.value ? colors.highlight : colors.button);
2178 DrawPolygon(p, qleft_trigger); 2207 DrawPolygon(p, qleft_trigger);
2179 2208
2180 // Draw L text 2209 // Draw L text
@@ -2183,8 +2212,8 @@ void PlayerControlPreview::DrawLeftTriggersTopView(QPainter& p, const QPointF ce
2183 DrawSymbol(p, center + QPointF(-143, -36), Symbol::L, 1.0f); 2212 DrawSymbol(p, center + QPointF(-143, -36), Symbol::L, 1.0f);
2184} 2213}
2185 2214
2186void PlayerControlPreview::DrawLeftZTriggersTopView(QPainter& p, const QPointF center, 2215void PlayerControlPreview::DrawLeftZTriggersTopView(
2187 bool left_pressed) { 2216 QPainter& p, const QPointF center, const Common::Input::ButtonStatus& left_pressed) {
2188 std::array<QPointF, left_joystick_ZL_topview.size() / 2> qleft_trigger; 2217 std::array<QPointF, left_joystick_ZL_topview.size() / 2> qleft_trigger;
2189 2218
2190 for (std::size_t point = 0; point < left_joystick_ZL_topview.size() / 2; ++point) { 2219 for (std::size_t point = 0; point < left_joystick_ZL_topview.size() / 2; ++point) {
@@ -2193,7 +2222,7 @@ void PlayerControlPreview::DrawLeftZTriggersTopView(QPainter& p, const QPointF c
2193 } 2222 }
2194 2223
2195 p.setPen(colors.outline); 2224 p.setPen(colors.outline);
2196 p.setBrush(left_pressed ? colors.highlight : colors.button); 2225 p.setBrush(left_pressed.value ? colors.highlight : colors.button);
2197 DrawPolygon(p, qleft_trigger); 2226 DrawPolygon(p, qleft_trigger);
2198 2227
2199 // Draw ZL text 2228 // Draw ZL text
@@ -2203,7 +2232,7 @@ void PlayerControlPreview::DrawLeftZTriggersTopView(QPainter& p, const QPointF c
2203} 2232}
2204 2233
2205void PlayerControlPreview::DrawRightTriggers(QPainter& p, const QPointF center, 2234void PlayerControlPreview::DrawRightTriggers(QPainter& p, const QPointF center,
2206 bool right_pressed) { 2235 const Common::Input::ButtonStatus& right_pressed) {
2207 std::array<QPointF, left_joycon_trigger.size() / 2> qright_trigger; 2236 std::array<QPointF, left_joycon_trigger.size() / 2> qright_trigger;
2208 constexpr float size = 1.78f; 2237 constexpr float size = 1.78f;
2209 constexpr float offset = 311.5f; 2238 constexpr float offset = 311.5f;
@@ -2211,36 +2240,36 @@ void PlayerControlPreview::DrawRightTriggers(QPainter& p, const QPointF center,
2211 for (std::size_t point = 0; point < left_joycon_trigger.size() / 2; ++point) { 2240 for (std::size_t point = 0; point < left_joycon_trigger.size() / 2; ++point) {
2212 qright_trigger[point] = center + QPointF(-left_joycon_trigger[point * 2] * size - offset, 2241 qright_trigger[point] = center + QPointF(-left_joycon_trigger[point * 2] * size - offset,
2213 left_joycon_trigger[point * 2 + 1] * size - 2242 left_joycon_trigger[point * 2 + 1] * size -
2214 (right_pressed ? 0.5f : 1.0f)); 2243 (right_pressed.value ? 0.5f : 1.0f));
2215 } 2244 }
2216 2245
2217 p.setPen(colors.outline); 2246 p.setPen(colors.outline);
2218 p.setBrush(right_pressed ? colors.highlight : colors.button); 2247 p.setBrush(right_pressed.value ? colors.highlight : colors.button);
2219 DrawPolygon(p, qright_trigger); 2248 DrawPolygon(p, qright_trigger);
2220} 2249}
2221 2250
2222void PlayerControlPreview::DrawRightZTriggers(QPainter& p, const QPointF center, 2251void PlayerControlPreview::DrawRightZTriggers(QPainter& p, const QPointF center,
2223 bool right_pressed) { 2252 const Common::Input::ButtonStatus& right_pressed) {
2224 std::array<QPointF, left_joycon_sideview_zl.size() / 2> qright_trigger; 2253 std::array<QPointF, left_joycon_sideview_zl.size() / 2> qright_trigger;
2225 constexpr float size = 1.1115f; 2254 constexpr float size = 1.1115f;
2226 constexpr float offset2 = 335; 2255 constexpr float offset2 = 335;
2227 2256
2228 for (std::size_t point = 0; point < left_joycon_sideview_zl.size() / 2; ++point) { 2257 for (std::size_t point = 0; point < left_joycon_sideview_zl.size() / 2; ++point) {
2229 qright_trigger[point] = 2258 qright_trigger[point] =
2230 center + 2259 center + QPointF(-left_joycon_sideview_zl[point * 2] * size - offset2,
2231 QPointF(-left_joycon_sideview_zl[point * 2] * size - offset2, 2260 left_joycon_sideview_zl[point * 2 + 1] * size +
2232 left_joycon_sideview_zl[point * 2 + 1] * size + (right_pressed ? 0.5f : 0) + 1); 2261 (right_pressed.value ? 0.5f : 0) + 1);
2233 } 2262 }
2234 2263
2235 p.setPen(colors.outline); 2264 p.setPen(colors.outline);
2236 p.setBrush(right_pressed ? colors.highlight : colors.button); 2265 p.setBrush(right_pressed.value ? colors.highlight : colors.button);
2237 DrawPolygon(p, qright_trigger); 2266 DrawPolygon(p, qright_trigger);
2238 p.drawArc(center.x() - 236, center.y() + (right_pressed ? -203.5f : -204.0f), 77, 77, 271 * 16, 2267 p.drawArc(center.x() - 236, center.y() + (right_pressed.value ? -203.5f : -204.0f), 77, 77,
2239 44 * 16); 2268 271 * 16, 44 * 16);
2240} 2269}
2241 2270
2242void PlayerControlPreview::DrawRightTriggersTopView(QPainter& p, const QPointF center, 2271void PlayerControlPreview::DrawRightTriggersTopView(
2243 bool right_pressed) { 2272 QPainter& p, const QPointF center, const Common::Input::ButtonStatus& right_pressed) {
2244 std::array<QPointF, left_joystick_L_topview.size() / 2> qright_trigger; 2273 std::array<QPointF, left_joystick_L_topview.size() / 2> qright_trigger;
2245 2274
2246 for (std::size_t point = 0; point < left_joystick_L_topview.size() / 2; ++point) { 2275 for (std::size_t point = 0; point < left_joystick_L_topview.size() / 2; ++point) {
@@ -2249,7 +2278,7 @@ void PlayerControlPreview::DrawRightTriggersTopView(QPainter& p, const QPointF c
2249 } 2278 }
2250 2279
2251 p.setPen(colors.outline); 2280 p.setPen(colors.outline);
2252 p.setBrush(right_pressed ? colors.highlight : colors.button); 2281 p.setBrush(right_pressed.value ? colors.highlight : colors.button);
2253 DrawPolygon(p, qright_trigger); 2282 DrawPolygon(p, qright_trigger);
2254 2283
2255 // Draw R text 2284 // Draw R text
@@ -2258,8 +2287,8 @@ void PlayerControlPreview::DrawRightTriggersTopView(QPainter& p, const QPointF c
2258 DrawSymbol(p, center + QPointF(137, -36), Symbol::R, 1.0f); 2287 DrawSymbol(p, center + QPointF(137, -36), Symbol::R, 1.0f);
2259} 2288}
2260 2289
2261void PlayerControlPreview::DrawRightZTriggersTopView(QPainter& p, const QPointF center, 2290void PlayerControlPreview::DrawRightZTriggersTopView(
2262 bool right_pressed) { 2291 QPainter& p, const QPointF center, const Common::Input::ButtonStatus& right_pressed) {
2263 std::array<QPointF, left_joystick_ZL_topview.size() / 2> qright_trigger; 2292 std::array<QPointF, left_joystick_ZL_topview.size() / 2> qright_trigger;
2264 2293
2265 for (std::size_t point = 0; point < left_joystick_ZL_topview.size() / 2; ++point) { 2294 for (std::size_t point = 0; point < left_joystick_ZL_topview.size() / 2; ++point) {
@@ -2268,7 +2297,7 @@ void PlayerControlPreview::DrawRightZTriggersTopView(QPainter& p, const QPointF
2268 } 2297 }
2269 2298
2270 p.setPen(colors.outline); 2299 p.setPen(colors.outline);
2271 p.setBrush(right_pressed ? colors.highlight : colors.button); 2300 p.setBrush(right_pressed.value ? colors.highlight : colors.button);
2272 DrawPolygon(p, qright_trigger); 2301 DrawPolygon(p, qright_trigger);
2273 2302
2274 // Draw ZR text 2303 // Draw ZR text
@@ -2278,13 +2307,13 @@ void PlayerControlPreview::DrawRightZTriggersTopView(QPainter& p, const QPointF
2278} 2307}
2279 2308
2280void PlayerControlPreview::DrawJoystick(QPainter& p, const QPointF center, float size, 2309void PlayerControlPreview::DrawJoystick(QPainter& p, const QPointF center, float size,
2281 bool pressed) { 2310 const Common::Input::ButtonStatus& pressed) {
2282 const float radius1 = 13.0f * size; 2311 const float radius1 = 13.0f * size;
2283 const float radius2 = 9.0f * size; 2312 const float radius2 = 9.0f * size;
2284 2313
2285 // Outer circle 2314 // Outer circle
2286 p.setPen(colors.outline); 2315 p.setPen(colors.outline);
2287 p.setBrush(pressed ? colors.highlight : colors.button); 2316 p.setBrush(pressed.value ? colors.highlight : colors.button);
2288 DrawCircle(p, center, radius1); 2317 DrawCircle(p, center, radius1);
2289 2318
2290 // Cross 2319 // Cross
@@ -2292,17 +2321,18 @@ void PlayerControlPreview::DrawJoystick(QPainter& p, const QPointF center, float
2292 p.drawLine(center - QPoint(0, radius1), center + QPoint(0, radius1)); 2321 p.drawLine(center - QPoint(0, radius1), center + QPoint(0, radius1));
2293 2322
2294 // Inner circle 2323 // Inner circle
2295 p.setBrush(pressed ? colors.highlight2 : colors.button2); 2324 p.setBrush(pressed.value ? colors.highlight2 : colors.button2);
2296 DrawCircle(p, center, radius2); 2325 DrawCircle(p, center, radius2);
2297} 2326}
2298 2327
2299void PlayerControlPreview::DrawJoystickSideview(QPainter& p, const QPointF center, float angle, 2328void PlayerControlPreview::DrawJoystickSideview(QPainter& p, const QPointF center, float angle,
2300 float size, bool pressed) { 2329 float size,
2330 const Common::Input::ButtonStatus& pressed) {
2301 QVector<QPointF> joystick; 2331 QVector<QPointF> joystick;
2302 joystick.reserve(static_cast<int>(left_joystick_sideview.size() / 2)); 2332 joystick.reserve(static_cast<int>(left_joystick_sideview.size() / 2));
2303 2333
2304 for (std::size_t point = 0; point < left_joystick_sideview.size() / 2; ++point) { 2334 for (std::size_t point = 0; point < left_joystick_sideview.size() / 2; ++point) {
2305 joystick.append(QPointF(left_joystick_sideview[point * 2] * size + (pressed ? 1 : 0), 2335 joystick.append(QPointF(left_joystick_sideview[point * 2] * size + (pressed.value ? 1 : 0),
2306 left_joystick_sideview[point * 2 + 1] * size - 1)); 2336 left_joystick_sideview[point * 2 + 1] * size - 1));
2307 } 2337 }
2308 2338
@@ -2314,14 +2344,15 @@ void PlayerControlPreview::DrawJoystickSideview(QPainter& p, const QPointF cente
2314 2344
2315 // Draw joystick 2345 // Draw joystick
2316 p.setPen(colors.outline); 2346 p.setPen(colors.outline);
2317 p.setBrush(pressed ? colors.highlight : colors.button); 2347 p.setBrush(pressed.value ? colors.highlight : colors.button);
2318 p.drawPolygon(p2); 2348 p.drawPolygon(p2);
2319 p.drawLine(p2.at(1), p2.at(30)); 2349 p.drawLine(p2.at(1), p2.at(30));
2320 p.drawLine(p2.at(32), p2.at(71)); 2350 p.drawLine(p2.at(32), p2.at(71));
2321} 2351}
2322 2352
2323void PlayerControlPreview::DrawProJoystick(QPainter& p, const QPointF center, const QPointF offset, 2353void PlayerControlPreview::DrawProJoystick(QPainter& p, const QPointF center, const QPointF offset,
2324 float offset_scalar, bool pressed) { 2354 float offset_scalar,
2355 const Common::Input::ButtonStatus& pressed) {
2325 const float radius1 = 24.0f; 2356 const float radius1 = 24.0f;
2326 const float radius2 = 17.0f; 2357 const float radius2 = 17.0f;
2327 2358
@@ -2339,11 +2370,11 @@ void PlayerControlPreview::DrawProJoystick(QPainter& p, const QPointF center, co
2339 2370
2340 // Outer circle 2371 // Outer circle
2341 p.setPen(colors.outline); 2372 p.setPen(colors.outline);
2342 p.setBrush(pressed ? colors.highlight : colors.button); 2373 p.setBrush(pressed.value ? colors.highlight : colors.button);
2343 p.drawEllipse(QPointF(0, 0), radius1 * amplitude, radius1); 2374 p.drawEllipse(QPointF(0, 0), radius1 * amplitude, radius1);
2344 2375
2345 // Inner circle 2376 // Inner circle
2346 p.setBrush(pressed ? colors.highlight2 : colors.button2); 2377 p.setBrush(pressed.value ? colors.highlight2 : colors.button2);
2347 2378
2348 const float inner_offset = 2379 const float inner_offset =
2349 (radius1 - radius2) * 0.4f * ((offset.x() == 0 && offset.y() < 0) ? -1.0f : 1.0f); 2380 (radius1 - radius2) * 0.4f * ((offset.x() == 0 && offset.y() < 0) ? -1.0f : 1.0f);
@@ -2355,14 +2386,15 @@ void PlayerControlPreview::DrawProJoystick(QPainter& p, const QPointF center, co
2355 p.restore(); 2386 p.restore();
2356} 2387}
2357 2388
2358void PlayerControlPreview::DrawGCJoystick(QPainter& p, const QPointF center, bool pressed) { 2389void PlayerControlPreview::DrawGCJoystick(QPainter& p, const QPointF center,
2390 const Common::Input::ButtonStatus& pressed) {
2359 // Outer circle 2391 // Outer circle
2360 p.setPen(colors.outline); 2392 p.setPen(colors.outline);
2361 p.setBrush(pressed ? colors.highlight : colors.button); 2393 p.setBrush(pressed.value ? colors.highlight : colors.button);
2362 DrawCircle(p, center, 26.0f); 2394 DrawCircle(p, center, 26.0f);
2363 2395
2364 // Inner circle 2396 // Inner circle
2365 p.setBrush(pressed ? colors.highlight2 : colors.button2); 2397 p.setBrush(pressed.value ? colors.highlight2 : colors.button2);
2366 DrawCircle(p, center, 19.0f); 2398 DrawCircle(p, center, 19.0f);
2367 p.setBrush(colors.transparent); 2399 p.setBrush(colors.transparent);
2368 DrawCircle(p, center, 13.5f); 2400 DrawCircle(p, center, 13.5f);
@@ -2371,31 +2403,29 @@ void PlayerControlPreview::DrawGCJoystick(QPainter& p, const QPointF center, boo
2371 2403
2372void PlayerControlPreview::DrawRawJoystick(QPainter& p, QPointF center_left, QPointF center_right) { 2404void PlayerControlPreview::DrawRawJoystick(QPainter& p, QPointF center_left, QPointF center_right) {
2373 using namespace Settings::NativeAnalog; 2405 using namespace Settings::NativeAnalog;
2374 if (controller_type != Settings::ControllerType::LeftJoycon) { 2406 if (center_right != QPointF(0, 0)) {
2375 DrawJoystickProperties(p, center_right, axis_values[RStick].properties); 2407 DrawJoystickProperties(p, center_right, stick_values[RStick].x.properties);
2376 p.setPen(colors.indicator); 2408 p.setPen(colors.indicator);
2377 p.setBrush(colors.indicator); 2409 p.setBrush(colors.indicator);
2378 DrawJoystickDot(p, center_right, axis_values[RStick].raw_value, 2410 DrawJoystickDot(p, center_right, stick_values[RStick], true);
2379 axis_values[RStick].properties);
2380 p.setPen(colors.indicator2); 2411 p.setPen(colors.indicator2);
2381 p.setBrush(colors.indicator2); 2412 p.setBrush(colors.indicator2);
2382 DrawJoystickDot(p, center_right, axis_values[RStick].value, axis_values[RStick].properties); 2413 DrawJoystickDot(p, center_right, stick_values[RStick], false);
2383 } 2414 }
2384 2415
2385 if (controller_type != Settings::ControllerType::RightJoycon) { 2416 if (center_left != QPointF(0, 0)) {
2386 DrawJoystickProperties(p, center_left, axis_values[LStick].properties); 2417 DrawJoystickProperties(p, center_left, stick_values[LStick].x.properties);
2387 p.setPen(colors.indicator); 2418 p.setPen(colors.indicator);
2388 p.setBrush(colors.indicator); 2419 p.setBrush(colors.indicator);
2389 DrawJoystickDot(p, center_left, axis_values[LStick].raw_value, 2420 DrawJoystickDot(p, center_left, stick_values[LStick], true);
2390 axis_values[LStick].properties);
2391 p.setPen(colors.indicator2); 2421 p.setPen(colors.indicator2);
2392 p.setBrush(colors.indicator2); 2422 p.setBrush(colors.indicator2);
2393 DrawJoystickDot(p, center_left, axis_values[LStick].value, axis_values[LStick].properties); 2423 DrawJoystickDot(p, center_left, stick_values[LStick], false);
2394 } 2424 }
2395} 2425}
2396 2426
2397void PlayerControlPreview::DrawJoystickProperties(QPainter& p, const QPointF center, 2427void PlayerControlPreview::DrawJoystickProperties(
2398 const Input::AnalogProperties& properties) { 2428 QPainter& p, const QPointF center, const Common::Input::AnalogProperties& properties) {
2399 constexpr float size = 45.0f; 2429 constexpr float size = 45.0f;
2400 const float range = size * properties.range; 2430 const float range = size * properties.range;
2401 const float deadzone = size * properties.deadzone; 2431 const float deadzone = size * properties.deadzone;
@@ -2414,19 +2444,26 @@ void PlayerControlPreview::DrawJoystickProperties(QPainter& p, const QPointF cen
2414 DrawCircle(p, center, deadzone); 2444 DrawCircle(p, center, deadzone);
2415} 2445}
2416 2446
2417void PlayerControlPreview::DrawJoystickDot(QPainter& p, const QPointF center, const QPointF value, 2447void PlayerControlPreview::DrawJoystickDot(QPainter& p, const QPointF center,
2418 const Input::AnalogProperties& properties) { 2448 const Common::Input::StickStatus& stick, bool raw) {
2419 constexpr float size = 45.0f; 2449 constexpr float size = 45.0f;
2420 const float range = size * properties.range; 2450 const float range = size * stick.x.properties.range;
2421 2451
2422 // Dot pointer 2452 if (raw) {
2423 DrawCircle(p, center + (value * range), 2); 2453 const QPointF value = QPointF(stick.x.raw_value, stick.y.raw_value) * size;
2454 DrawCircle(p, center + value, 2);
2455 return;
2456 }
2457
2458 const QPointF value = QPointF(stick.x.value, stick.y.value) * range;
2459 DrawCircle(p, center + value, 2);
2424} 2460}
2425 2461
2426void PlayerControlPreview::DrawRoundButton(QPainter& p, QPointF center, bool pressed, float width, 2462void PlayerControlPreview::DrawRoundButton(QPainter& p, QPointF center,
2463 const Common::Input::ButtonStatus& pressed, float width,
2427 float height, Direction direction, float radius) { 2464 float height, Direction direction, float radius) {
2428 p.setBrush(button_color); 2465 p.setBrush(button_color);
2429 if (pressed) { 2466 if (pressed.value) {
2430 switch (direction) { 2467 switch (direction) {
2431 case Direction::Left: 2468 case Direction::Left:
2432 center.setX(center.x() - 1); 2469 center.setX(center.x() - 1);
@@ -2448,17 +2485,19 @@ void PlayerControlPreview::DrawRoundButton(QPainter& p, QPointF center, bool pre
2448 QRectF rect = {center.x() - width, center.y() - height, width * 2.0f, height * 2.0f}; 2485 QRectF rect = {center.x() - width, center.y() - height, width * 2.0f, height * 2.0f};
2449 p.drawRoundedRect(rect, radius, radius); 2486 p.drawRoundedRect(rect, radius, radius);
2450} 2487}
2451void PlayerControlPreview::DrawMinusButton(QPainter& p, const QPointF center, bool pressed, 2488void PlayerControlPreview::DrawMinusButton(QPainter& p, const QPointF center,
2489 const Common::Input::ButtonStatus& pressed,
2452 int button_size) { 2490 int button_size) {
2453 p.setPen(colors.outline); 2491 p.setPen(colors.outline);
2454 p.setBrush(pressed ? colors.highlight : colors.button); 2492 p.setBrush(pressed.value ? colors.highlight : colors.button);
2455 DrawRectangle(p, center, button_size, button_size / 3.0f); 2493 DrawRectangle(p, center, button_size, button_size / 3.0f);
2456} 2494}
2457void PlayerControlPreview::DrawPlusButton(QPainter& p, const QPointF center, bool pressed, 2495void PlayerControlPreview::DrawPlusButton(QPainter& p, const QPointF center,
2496 const Common::Input::ButtonStatus& pressed,
2458 int button_size) { 2497 int button_size) {
2459 // Draw outer line 2498 // Draw outer line
2460 p.setPen(colors.outline); 2499 p.setPen(colors.outline);
2461 p.setBrush(pressed ? colors.highlight : colors.button); 2500 p.setBrush(pressed.value ? colors.highlight : colors.button);
2462 DrawRectangle(p, center, button_size, button_size / 3.0f); 2501 DrawRectangle(p, center, button_size, button_size / 3.0f);
2463 DrawRectangle(p, center, button_size / 3.0f, button_size); 2502 DrawRectangle(p, center, button_size / 3.0f, button_size);
2464 2503
@@ -2471,7 +2510,8 @@ void PlayerControlPreview::DrawPlusButton(QPainter& p, const QPointF center, boo
2471 DrawRectangle(p, center, button_size / 3.0f, button_size); 2510 DrawRectangle(p, center, button_size / 3.0f, button_size);
2472} 2511}
2473 2512
2474void PlayerControlPreview::DrawGCButtonX(QPainter& p, const QPointF center, bool pressed) { 2513void PlayerControlPreview::DrawGCButtonX(QPainter& p, const QPointF center,
2514 const Common::Input::ButtonStatus& pressed) {
2475 std::array<QPointF, gc_button_x.size() / 2> button_x; 2515 std::array<QPointF, gc_button_x.size() / 2> button_x;
2476 2516
2477 for (std::size_t point = 0; point < gc_button_x.size() / 2; ++point) { 2517 for (std::size_t point = 0; point < gc_button_x.size() / 2; ++point) {
@@ -2479,11 +2519,12 @@ void PlayerControlPreview::DrawGCButtonX(QPainter& p, const QPointF center, bool
2479 } 2519 }
2480 2520
2481 p.setPen(colors.outline); 2521 p.setPen(colors.outline);
2482 p.setBrush(pressed ? colors.highlight : colors.button); 2522 p.setBrush(pressed.value ? colors.highlight : colors.button);
2483 DrawPolygon(p, button_x); 2523 DrawPolygon(p, button_x);
2484} 2524}
2485 2525
2486void PlayerControlPreview::DrawGCButtonY(QPainter& p, const QPointF center, bool pressed) { 2526void PlayerControlPreview::DrawGCButtonY(QPainter& p, const QPointF center,
2527 const Common::Input::ButtonStatus& pressed) {
2487 std::array<QPointF, gc_button_y.size() / 2> button_x; 2528 std::array<QPointF, gc_button_y.size() / 2> button_x;
2488 2529
2489 for (std::size_t point = 0; point < gc_button_y.size() / 2; ++point) { 2530 for (std::size_t point = 0; point < gc_button_y.size() / 2; ++point) {
@@ -2491,27 +2532,29 @@ void PlayerControlPreview::DrawGCButtonY(QPainter& p, const QPointF center, bool
2491 } 2532 }
2492 2533
2493 p.setPen(colors.outline); 2534 p.setPen(colors.outline);
2494 p.setBrush(pressed ? colors.highlight : colors.button); 2535 p.setBrush(pressed.value ? colors.highlight : colors.button);
2495 DrawPolygon(p, button_x); 2536 DrawPolygon(p, button_x);
2496} 2537}
2497 2538
2498void PlayerControlPreview::DrawGCButtonZ(QPainter& p, const QPointF center, bool pressed) { 2539void PlayerControlPreview::DrawGCButtonZ(QPainter& p, const QPointF center,
2540 const Common::Input::ButtonStatus& pressed) {
2499 std::array<QPointF, gc_button_z.size() / 2> button_x; 2541 std::array<QPointF, gc_button_z.size() / 2> button_x;
2500 2542
2501 for (std::size_t point = 0; point < gc_button_z.size() / 2; ++point) { 2543 for (std::size_t point = 0; point < gc_button_z.size() / 2; ++point) {
2502 button_x[point] = center + QPointF(gc_button_z[point * 2], 2544 button_x[point] = center + QPointF(gc_button_z[point * 2],
2503 gc_button_z[point * 2 + 1] + (pressed ? 1 : 0)); 2545 gc_button_z[point * 2 + 1] + (pressed.value ? 1 : 0));
2504 } 2546 }
2505 2547
2506 p.setPen(colors.outline); 2548 p.setPen(colors.outline);
2507 p.setBrush(pressed ? colors.highlight : colors.button2); 2549 p.setBrush(pressed.value ? colors.highlight : colors.button2);
2508 DrawPolygon(p, button_x); 2550 DrawPolygon(p, button_x);
2509} 2551}
2510 2552
2511void PlayerControlPreview::DrawCircleButton(QPainter& p, const QPointF center, bool pressed, 2553void PlayerControlPreview::DrawCircleButton(QPainter& p, const QPointF center,
2554 const Common::Input::ButtonStatus& pressed,
2512 float button_size) { 2555 float button_size) {
2513 p.setBrush(button_color); 2556 p.setBrush(button_color);
2514 if (pressed) { 2557 if (pressed.value) {
2515 p.setBrush(colors.highlight); 2558 p.setBrush(colors.highlight);
2516 } 2559 }
2517 p.drawEllipse(center, button_size, button_size); 2560 p.drawEllipse(center, button_size, button_size);
@@ -2540,7 +2583,8 @@ void PlayerControlPreview::DrawArrowButtonOutline(QPainter& p, const QPointF cen
2540} 2583}
2541 2584
2542void PlayerControlPreview::DrawArrowButton(QPainter& p, const QPointF center, 2585void PlayerControlPreview::DrawArrowButton(QPainter& p, const QPointF center,
2543 const Direction direction, bool pressed, float size) { 2586 const Direction direction,
2587 const Common::Input::ButtonStatus& pressed, float size) {
2544 std::array<QPointF, up_arrow_button.size() / 2> arrow_button; 2588 std::array<QPointF, up_arrow_button.size() / 2> arrow_button;
2545 QPoint offset; 2589 QPoint offset;
2546 2590
@@ -2552,38 +2596,39 @@ void PlayerControlPreview::DrawArrowButton(QPainter& p, const QPointF center,
2552 case Direction::Up: 2596 case Direction::Up:
2553 arrow_button[point] = center + QPointF(up_arrow_x * size, up_arrow_y * size); 2597 arrow_button[point] = center + QPointF(up_arrow_x * size, up_arrow_y * size);
2554 break; 2598 break;
2555 case Direction::Left:
2556 arrow_button[point] = center + QPointF(up_arrow_y * size, up_arrow_x * size);
2557 break;
2558 case Direction::Right: 2599 case Direction::Right:
2559 arrow_button[point] = center + QPointF(-up_arrow_y * size, up_arrow_x * size); 2600 arrow_button[point] = center + QPointF(-up_arrow_y * size, up_arrow_x * size);
2560 break; 2601 break;
2561 case Direction::Down: 2602 case Direction::Down:
2562 arrow_button[point] = center + QPointF(up_arrow_x * size, -up_arrow_y * size); 2603 arrow_button[point] = center + QPointF(up_arrow_x * size, -up_arrow_y * size);
2563 break; 2604 break;
2605 case Direction::Left:
2606 // Compiler doesn't optimize this correctly check why
2607 arrow_button[point] = center + QPointF(up_arrow_y * size, up_arrow_x * size);
2608 break;
2564 case Direction::None: 2609 case Direction::None:
2565 break; 2610 break;
2566 } 2611 }
2567 } 2612 }
2568 2613
2569 // Draw arrow button 2614 // Draw arrow button
2570 p.setPen(pressed ? colors.highlight : colors.button); 2615 p.setPen(pressed.value ? colors.highlight : colors.button);
2571 p.setBrush(pressed ? colors.highlight : colors.button); 2616 p.setBrush(pressed.value ? colors.highlight : colors.button);
2572 DrawPolygon(p, arrow_button); 2617 DrawPolygon(p, arrow_button);
2573 2618
2574 switch (direction) { 2619 switch (direction) {
2575 case Direction::Up: 2620 case Direction::Up:
2576 offset = QPoint(0, -20 * size); 2621 offset = QPoint(0, -20 * size);
2577 break; 2622 break;
2578 case Direction::Left:
2579 offset = QPoint(-20 * size, 0);
2580 break;
2581 case Direction::Right: 2623 case Direction::Right:
2582 offset = QPoint(20 * size, 0); 2624 offset = QPoint(20 * size, 0);
2583 break; 2625 break;
2584 case Direction::Down: 2626 case Direction::Down:
2585 offset = QPoint(0, 20 * size); 2627 offset = QPoint(0, 20 * size);
2586 break; 2628 break;
2629 case Direction::Left:
2630 offset = QPoint(-20 * size, 0);
2631 break;
2587 case Direction::None: 2632 case Direction::None:
2588 offset = QPoint(0, 0); 2633 offset = QPoint(0, 0);
2589 break; 2634 break;
@@ -2596,7 +2641,8 @@ void PlayerControlPreview::DrawArrowButton(QPainter& p, const QPointF center,
2596} 2641}
2597 2642
2598void PlayerControlPreview::DrawTriggerButton(QPainter& p, const QPointF center, 2643void PlayerControlPreview::DrawTriggerButton(QPainter& p, const QPointF center,
2599 const Direction direction, bool pressed) { 2644 const Direction direction,
2645 const Common::Input::ButtonStatus& pressed) {
2600 std::array<QPointF, trigger_button.size() / 2> qtrigger_button; 2646 std::array<QPointF, trigger_button.size() / 2> qtrigger_button;
2601 2647
2602 for (std::size_t point = 0; point < trigger_button.size() / 2; ++point) { 2648 for (std::size_t point = 0; point < trigger_button.size() / 2; ++point) {
@@ -2619,10 +2665,51 @@ void PlayerControlPreview::DrawTriggerButton(QPainter& p, const QPointF center,
2619 2665
2620 // Draw arrow button 2666 // Draw arrow button
2621 p.setPen(colors.outline); 2667 p.setPen(colors.outline);
2622 p.setBrush(pressed ? colors.highlight : colors.button); 2668 p.setBrush(pressed.value ? colors.highlight : colors.button);
2623 DrawPolygon(p, qtrigger_button); 2669 DrawPolygon(p, qtrigger_button);
2624} 2670}
2625 2671
2672void PlayerControlPreview::DrawBattery(QPainter& p, QPointF center,
2673 Common::Input::BatteryLevel battery) {
2674 if (battery == Common::Input::BatteryLevel::None) {
2675 return;
2676 }
2677 p.setPen(colors.outline);
2678 p.setBrush(colors.transparent);
2679 p.drawRect(center.x(), center.y(), 56, 20);
2680 p.drawRect(center.x() + 56, center.y() + 6, 3, 8);
2681 p.setBrush(colors.deadzone);
2682 switch (battery) {
2683 case Common::Input::BatteryLevel::Charging:
2684 p.setBrush(colors.indicator2);
2685 p.drawText(center + QPoint(2, 14), tr("Charging"));
2686 break;
2687 case Common::Input::BatteryLevel::Full:
2688 p.drawRect(center.x() + 42, center.y(), 14, 20);
2689 p.drawRect(center.x() + 28, center.y(), 14, 20);
2690 p.drawRect(center.x() + 14, center.y(), 14, 20);
2691 p.drawRect(center.x(), center.y(), 14, 20);
2692 break;
2693 case Common::Input::BatteryLevel::Medium:
2694 p.drawRect(center.x() + 28, center.y(), 14, 20);
2695 p.drawRect(center.x() + 14, center.y(), 14, 20);
2696 p.drawRect(center.x(), center.y(), 14, 20);
2697 break;
2698 case Common::Input::BatteryLevel::Low:
2699 p.drawRect(center.x() + 14, center.y(), 14, 20);
2700 p.drawRect(center.x(), center.y(), 14, 20);
2701 break;
2702 case Common::Input::BatteryLevel::Critical:
2703 p.drawRect(center.x(), center.y(), 14, 20);
2704 break;
2705 case Common::Input::BatteryLevel::Empty:
2706 p.drawRect(center.x(), center.y(), 5, 20);
2707 break;
2708 default:
2709 break;
2710 }
2711}
2712
2626void PlayerControlPreview::DrawSymbol(QPainter& p, const QPointF center, Symbol symbol, 2713void PlayerControlPreview::DrawSymbol(QPainter& p, const QPointF center, Symbol symbol,
2627 float icon_size) { 2714 float icon_size) {
2628 std::array<QPointF, house.size() / 2> house_icon; 2715 std::array<QPointF, house.size() / 2> house_icon;
diff --git a/src/yuzu/configuration/configure_input_player_widget.h b/src/yuzu/configuration/configure_input_player_widget.h
index f4bbfa528..4cd5c3be0 100644
--- a/src/yuzu/configuration/configure_input_player_widget.h
+++ b/src/yuzu/configuration/configure_input_player_widget.h
@@ -7,9 +7,11 @@
7#include <array> 7#include <array>
8#include <QFrame> 8#include <QFrame>
9#include <QPointer> 9#include <QPointer>
10#include "common/settings.h" 10
11#include "core/frontend/input.h" 11#include "common/input.h"
12#include "yuzu/debugger/controller.h" 12#include "common/settings_input.h"
13#include "core/hid/emulated_controller.h"
14#include "core/hid/hid_types.h"
13 15
14class QLabel; 16class QLabel;
15 17
@@ -24,17 +26,26 @@ public:
24 explicit PlayerControlPreview(QWidget* parent); 26 explicit PlayerControlPreview(QWidget* parent);
25 ~PlayerControlPreview() override; 27 ~PlayerControlPreview() override;
26 28
27 void SetPlayerInput(std::size_t index, const ButtonParam& buttons_param, 29 // Sets the emulated controller to be displayed
28 const AnalogParam& analogs_param); 30 void SetController(Core::HID::EmulatedController* controller);
29 void SetPlayerInputRaw(std::size_t index, const Settings::ButtonsRaw& buttons_, 31
30 Settings::AnalogsRaw analogs_); 32 // Disables events from the emulated controller
31 void SetConnectedStatus(bool checked); 33 void UnloadController();
32 void SetControllerType(Settings::ControllerType type); 34
35 // Starts blinking animation at the button specified
33 void BeginMappingButton(std::size_t button_id); 36 void BeginMappingButton(std::size_t button_id);
34 void BeginMappingAnalog(std::size_t button_id); 37
38 // Starts moving animation at the stick specified
39 void BeginMappingAnalog(std::size_t stick_id);
40
41 // Stops any ongoing animation
35 void EndMapping(); 42 void EndMapping();
43
44 // Handles emulated controller events
45 void ControllerUpdate(Core::HID::ControllerTriggerType type);
46
47 // Updates input on sheduled interval
36 void UpdateInput(); 48 void UpdateInput();
37 void SetCallBack(ControllerCallback callback_);
38 49
39protected: 50protected:
40 void paintEvent(QPaintEvent* event) override; 51 void paintEvent(QPaintEvent* event) override;
@@ -63,22 +74,6 @@ private:
63 SR, 74 SR,
64 }; 75 };
65 76
66 struct AxisValue {
67 QPointF value{};
68 QPointF raw_value{};
69 Input::AnalogProperties properties{};
70 int size{};
71 QPoint offset{};
72 bool active{};
73 };
74
75 struct LedPattern {
76 bool position1;
77 bool position2;
78 bool position3;
79 bool position4;
80 };
81
82 struct ColorMapping { 77 struct ColorMapping {
83 QColor outline{}; 78 QColor outline{};
84 QColor primary{}; 79 QColor primary{};
@@ -101,7 +96,6 @@ private:
101 QColor deadzone{}; 96 QColor deadzone{};
102 }; 97 };
103 98
104 static LedPattern GetColorPattern(std::size_t index, bool player_on);
105 void UpdateColors(); 99 void UpdateColors();
106 void ResetInputs(); 100 void ResetInputs();
107 101
@@ -122,47 +116,75 @@ private:
122 void DrawGCBody(QPainter& p, QPointF center); 116 void DrawGCBody(QPainter& p, QPointF center);
123 117
124 // Draw triggers functions 118 // Draw triggers functions
125 void DrawProTriggers(QPainter& p, QPointF center, bool left_pressed, bool right_pressed); 119 void DrawProTriggers(QPainter& p, QPointF center,
126 void DrawGCTriggers(QPainter& p, QPointF center, bool left_pressed, bool right_pressed); 120 const Common::Input::ButtonStatus& left_pressed,
127 void DrawHandheldTriggers(QPainter& p, QPointF center, bool left_pressed, bool right_pressed); 121 const Common::Input::ButtonStatus& right_pressed);
128 void DrawDualTriggers(QPainter& p, QPointF center, bool left_pressed, bool right_pressed); 122 void DrawGCTriggers(QPainter& p, QPointF center, Common::Input::TriggerStatus left_trigger,
129 void DrawDualTriggersTopView(QPainter& p, QPointF center, bool left_pressed, 123 Common::Input::TriggerStatus right_trigger);
130 bool right_pressed); 124 void DrawHandheldTriggers(QPainter& p, QPointF center,
131 void DrawDualZTriggersTopView(QPainter& p, QPointF center, bool left_pressed, 125 const Common::Input::ButtonStatus& left_pressed,
132 bool right_pressed); 126 const Common::Input::ButtonStatus& right_pressed);
133 void DrawLeftTriggers(QPainter& p, QPointF center, bool left_pressed); 127 void DrawDualTriggers(QPainter& p, QPointF center,
134 void DrawLeftZTriggers(QPainter& p, QPointF center, bool left_pressed); 128 const Common::Input::ButtonStatus& left_pressed,
135 void DrawLeftTriggersTopView(QPainter& p, QPointF center, bool left_pressed); 129 const Common::Input::ButtonStatus& right_pressed);
136 void DrawLeftZTriggersTopView(QPainter& p, QPointF center, bool left_pressed); 130 void DrawDualTriggersTopView(QPainter& p, QPointF center,
137 void DrawRightTriggers(QPainter& p, QPointF center, bool right_pressed); 131 const Common::Input::ButtonStatus& left_pressed,
138 void DrawRightZTriggers(QPainter& p, QPointF center, bool right_pressed); 132 const Common::Input::ButtonStatus& right_pressed);
139 void DrawRightTriggersTopView(QPainter& p, QPointF center, bool right_pressed); 133 void DrawDualZTriggersTopView(QPainter& p, QPointF center,
140 void DrawRightZTriggersTopView(QPainter& p, QPointF center, bool right_pressed); 134 const Common::Input::ButtonStatus& left_pressed,
135 const Common::Input::ButtonStatus& right_pressed);
136 void DrawLeftTriggers(QPainter& p, QPointF center,
137 const Common::Input::ButtonStatus& left_pressed);
138 void DrawLeftZTriggers(QPainter& p, QPointF center,
139 const Common::Input::ButtonStatus& left_pressed);
140 void DrawLeftTriggersTopView(QPainter& p, QPointF center,
141 const Common::Input::ButtonStatus& left_pressed);
142 void DrawLeftZTriggersTopView(QPainter& p, QPointF center,
143 const Common::Input::ButtonStatus& left_pressed);
144 void DrawRightTriggers(QPainter& p, QPointF center,
145 const Common::Input::ButtonStatus& right_pressed);
146 void DrawRightZTriggers(QPainter& p, QPointF center,
147 const Common::Input::ButtonStatus& right_pressed);
148 void DrawRightTriggersTopView(QPainter& p, QPointF center,
149 const Common::Input::ButtonStatus& right_pressed);
150 void DrawRightZTriggersTopView(QPainter& p, QPointF center,
151 const Common::Input::ButtonStatus& right_pressed);
141 152
142 // Draw joystick functions 153 // Draw joystick functions
143 void DrawJoystick(QPainter& p, QPointF center, float size, bool pressed); 154 void DrawJoystick(QPainter& p, QPointF center, float size,
144 void DrawJoystickSideview(QPainter& p, QPointF center, float angle, float size, bool pressed); 155 const Common::Input::ButtonStatus& pressed);
156 void DrawJoystickSideview(QPainter& p, QPointF center, float angle, float size,
157 const Common::Input::ButtonStatus& pressed);
145 void DrawRawJoystick(QPainter& p, QPointF center_left, QPointF center_right); 158 void DrawRawJoystick(QPainter& p, QPointF center_left, QPointF center_right);
146 void DrawJoystickProperties(QPainter& p, QPointF center, 159 void DrawJoystickProperties(QPainter& p, QPointF center,
147 const Input::AnalogProperties& properties); 160 const Common::Input::AnalogProperties& properties);
148 void DrawJoystickDot(QPainter& p, QPointF center, QPointF value, 161 void DrawJoystickDot(QPainter& p, QPointF center, const Common::Input::StickStatus& stick,
149 const Input::AnalogProperties& properties); 162 bool raw);
150 void DrawProJoystick(QPainter& p, QPointF center, QPointF offset, float scalar, bool pressed); 163 void DrawProJoystick(QPainter& p, QPointF center, QPointF offset, float scalar,
151 void DrawGCJoystick(QPainter& p, QPointF center, bool pressed); 164 const Common::Input::ButtonStatus& pressed);
165 void DrawGCJoystick(QPainter& p, QPointF center, const Common::Input::ButtonStatus& pressed);
152 166
153 // Draw button functions 167 // Draw button functions
154 void DrawCircleButton(QPainter& p, QPointF center, bool pressed, float button_size); 168 void DrawCircleButton(QPainter& p, QPointF center, const Common::Input::ButtonStatus& pressed,
155 void DrawRoundButton(QPainter& p, QPointF center, bool pressed, float width, float height, 169 float button_size);
156 Direction direction = Direction::None, float radius = 2); 170 void DrawRoundButton(QPainter& p, QPointF center, const Common::Input::ButtonStatus& pressed,
157 void DrawMinusButton(QPainter& p, QPointF center, bool pressed, int button_size); 171 float width, float height, Direction direction = Direction::None,
158 void DrawPlusButton(QPainter& p, QPointF center, bool pressed, int button_size); 172 float radius = 2);
159 void DrawGCButtonX(QPainter& p, QPointF center, bool pressed); 173 void DrawMinusButton(QPainter& p, QPointF center, const Common::Input::ButtonStatus& pressed,
160 void DrawGCButtonY(QPainter& p, QPointF center, bool pressed); 174 int button_size);
161 void DrawGCButtonZ(QPainter& p, QPointF center, bool pressed); 175 void DrawPlusButton(QPainter& p, QPointF center, const Common::Input::ButtonStatus& pressed,
176 int button_size);
177 void DrawGCButtonX(QPainter& p, QPointF center, const Common::Input::ButtonStatus& pressed);
178 void DrawGCButtonY(QPainter& p, QPointF center, const Common::Input::ButtonStatus& pressed);
179 void DrawGCButtonZ(QPainter& p, QPointF center, const Common::Input::ButtonStatus& pressed);
162 void DrawArrowButtonOutline(QPainter& p, const QPointF center, float size = 1.0f); 180 void DrawArrowButtonOutline(QPainter& p, const QPointF center, float size = 1.0f);
163 void DrawArrowButton(QPainter& p, QPointF center, Direction direction, bool pressed, 181 void DrawArrowButton(QPainter& p, QPointF center, Direction direction,
164 float size = 1.0f); 182 const Common::Input::ButtonStatus& pressed, float size = 1.0f);
165 void DrawTriggerButton(QPainter& p, QPointF center, Direction direction, bool pressed); 183 void DrawTriggerButton(QPainter& p, QPointF center, Direction direction,
184 const Common::Input::ButtonStatus& pressed);
185
186 // Draw battery functions
187 void DrawBattery(QPainter& p, QPointF center, Common::Input::BatteryLevel battery);
166 188
167 // Draw icon functions 189 // Draw icon functions
168 void DrawSymbol(QPainter& p, QPointF center, Symbol symbol, float icon_size); 190 void DrawSymbol(QPainter& p, QPointF center, Symbol symbol, float icon_size);
@@ -178,24 +200,23 @@ private:
178 void SetTextFont(QPainter& p, float text_size, 200 void SetTextFont(QPainter& p, float text_size,
179 const QString& font_family = QStringLiteral("sans-serif")); 201 const QString& font_family = QStringLiteral("sans-serif"));
180 202
181 using ButtonArray = 203 bool is_controller_set{};
182 std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeButton::BUTTON_NS_END>; 204 bool is_connected{};
183 using StickArray = 205 bool needs_redraw{};
184 std::array<std::unique_ptr<Input::AnalogDevice>, Settings::NativeAnalog::NUM_STICKS_HID>; 206 Core::HID::NpadStyleIndex controller_type;
185 207
186 ControllerCallback controller_callback;
187 bool is_enabled{};
188 bool mapping_active{}; 208 bool mapping_active{};
189 int blink_counter{}; 209 int blink_counter{};
210 int callback_key;
190 QColor button_color{}; 211 QColor button_color{};
191 ColorMapping colors{}; 212 ColorMapping colors{};
192 std::array<QColor, 4> led_color{}; 213 Core::HID::LedPattern led_pattern{0, 0, 0, 0};
193 ButtonArray buttons{};
194 StickArray sticks{};
195 std::size_t player_index{}; 214 std::size_t player_index{};
196 std::size_t button_mapping_index{Settings::NativeButton::BUTTON_NS_END}; 215 Core::HID::EmulatedController* controller;
197 std::size_t analog_mapping_index{Settings::NativeAnalog::NUM_STICKS_HID}; 216 std::size_t button_mapping_index{Settings::NativeButton::NumButtons};
198 std::array<AxisValue, Settings::NativeAnalog::NUM_STICKS_HID> axis_values{}; 217 std::size_t analog_mapping_index{Settings::NativeAnalog::NumAnalogs};
199 std::array<bool, Settings::NativeButton::NumButtons> button_values{}; 218 Core::HID::ButtonValues button_values{};
200 Settings::ControllerType controller_type{Settings::ControllerType::ProController}; 219 Core::HID::SticksValues stick_values{};
220 Core::HID::TriggerValues trigger_values{};
221 Core::HID::BatteryValues battery_values{};
201}; 222};
diff --git a/src/yuzu/configuration/configure_input_profile_dialog.cpp b/src/yuzu/configuration/configure_input_profile_dialog.cpp
index cd5a88cea..17bbe6b61 100644
--- a/src/yuzu/configuration/configure_input_profile_dialog.cpp
+++ b/src/yuzu/configuration/configure_input_profile_dialog.cpp
@@ -11,8 +11,8 @@ ConfigureInputProfileDialog::ConfigureInputProfileDialog(
11 QWidget* parent, InputCommon::InputSubsystem* input_subsystem, InputProfiles* profiles, 11 QWidget* parent, InputCommon::InputSubsystem* input_subsystem, InputProfiles* profiles,
12 Core::System& system) 12 Core::System& system)
13 : QDialog(parent), ui(std::make_unique<Ui::ConfigureInputProfileDialog>()), 13 : QDialog(parent), ui(std::make_unique<Ui::ConfigureInputProfileDialog>()),
14 profile_widget( 14 profile_widget(new ConfigureInputPlayer(this, 9, nullptr, input_subsystem, profiles,
15 new ConfigureInputPlayer(this, 9, nullptr, input_subsystem, profiles, system, false)) { 15 system.HIDCore(), system.IsPoweredOn(), false)) {
16 ui->setupUi(this); 16 ui->setupUi(this);
17 17
18 ui->controllerLayout->addWidget(profile_widget); 18 ui->controllerLayout->addWidget(profile_widget);
diff --git a/src/yuzu/configuration/configure_motion_touch.cpp b/src/yuzu/configuration/configure_motion_touch.cpp
index f8e08c422..8539a5c8b 100644
--- a/src/yuzu/configuration/configure_motion_touch.cpp
+++ b/src/yuzu/configuration/configure_motion_touch.cpp
@@ -15,9 +15,9 @@
15 15
16#include "common/logging/log.h" 16#include "common/logging/log.h"
17#include "common/settings.h" 17#include "common/settings.h"
18#include "input_common/drivers/udp_client.h"
19#include "input_common/helpers/udp_protocol.h"
18#include "input_common/main.h" 20#include "input_common/main.h"
19#include "input_common/udp/client.h"
20#include "input_common/udp/udp.h"
21#include "ui_configure_motion_touch.h" 21#include "ui_configure_motion_touch.h"
22#include "yuzu/configuration/configure_motion_touch.h" 22#include "yuzu/configuration/configure_motion_touch.h"
23#include "yuzu/configuration/configure_touch_from_button.h" 23#include "yuzu/configuration/configure_touch_from_button.h"
@@ -93,6 +93,7 @@ ConfigureMotionTouch::ConfigureMotionTouch(QWidget* parent,
93 "using-a-controller-or-android-phone-for-motion-or-touch-input'><span " 93 "using-a-controller-or-android-phone-for-motion-or-touch-input'><span "
94 "style=\"text-decoration: underline; color:#039be5;\">Learn More</span></a>")); 94 "style=\"text-decoration: underline; color:#039be5;\">Learn More</span></a>"));
95 95
96 setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
96 SetConfiguration(); 97 SetConfiguration();
97 UpdateUiDisplay(); 98 UpdateUiDisplay();
98 ConnectEvents(); 99 ConnectEvents();
@@ -101,17 +102,14 @@ ConfigureMotionTouch::ConfigureMotionTouch(QWidget* parent,
101ConfigureMotionTouch::~ConfigureMotionTouch() = default; 102ConfigureMotionTouch::~ConfigureMotionTouch() = default;
102 103
103void ConfigureMotionTouch::SetConfiguration() { 104void ConfigureMotionTouch::SetConfiguration() {
104 const Common::ParamPackage motion_param(Settings::values.motion_device.GetValue());
105 const Common::ParamPackage touch_param(Settings::values.touch_device.GetValue()); 105 const Common::ParamPackage touch_param(Settings::values.touch_device.GetValue());
106 106
107 ui->touch_from_button_checkbox->setChecked(Settings::values.use_touch_from_button.GetValue());
108 touch_from_button_maps = Settings::values.touch_from_button_maps; 107 touch_from_button_maps = Settings::values.touch_from_button_maps;
109 for (const auto& touch_map : touch_from_button_maps) { 108 for (const auto& touch_map : touch_from_button_maps) {
110 ui->touch_from_button_map->addItem(QString::fromStdString(touch_map.name)); 109 ui->touch_from_button_map->addItem(QString::fromStdString(touch_map.name));
111 } 110 }
112 ui->touch_from_button_map->setCurrentIndex( 111 ui->touch_from_button_map->setCurrentIndex(
113 Settings::values.touch_from_button_map_index.GetValue()); 112 Settings::values.touch_from_button_map_index.GetValue());
114 ui->motion_sensitivity->setValue(motion_param.Get("sensitivity", 0.01f));
115 113
116 min_x = touch_param.Get("min_x", 100); 114 min_x = touch_param.Get("min_x", 100);
117 min_y = touch_param.Get("min_y", 50); 115 min_y = touch_param.Get("min_y", 50);
@@ -139,9 +137,6 @@ void ConfigureMotionTouch::SetConfiguration() {
139void ConfigureMotionTouch::UpdateUiDisplay() { 137void ConfigureMotionTouch::UpdateUiDisplay() {
140 const QString cemuhook_udp = QStringLiteral("cemuhookudp"); 138 const QString cemuhook_udp = QStringLiteral("cemuhookudp");
141 139
142 ui->motion_sensitivity_label->setVisible(true);
143 ui->motion_sensitivity->setVisible(true);
144
145 ui->touch_calibration->setVisible(true); 140 ui->touch_calibration->setVisible(true);
146 ui->touch_calibration_config->setVisible(true); 141 ui->touch_calibration_config->setVisible(true);
147 ui->touch_calibration_label->setVisible(true); 142 ui->touch_calibration_label->setVisible(true);
@@ -312,7 +307,6 @@ void ConfigureMotionTouch::ApplyConfiguration() {
312 touch_param.Set("max_y", max_y); 307 touch_param.Set("max_y", max_y);
313 308
314 Settings::values.touch_device = touch_param.Serialize(); 309 Settings::values.touch_device = touch_param.Serialize();
315 Settings::values.use_touch_from_button = ui->touch_from_button_checkbox->isChecked();
316 Settings::values.touch_from_button_map_index = ui->touch_from_button_map->currentIndex(); 310 Settings::values.touch_from_button_map_index = ui->touch_from_button_map->currentIndex();
317 Settings::values.touch_from_button_maps = touch_from_button_maps; 311 Settings::values.touch_from_button_maps = touch_from_button_maps;
318 Settings::values.udp_input_servers = GetUDPServerString(); 312 Settings::values.udp_input_servers = GetUDPServerString();
diff --git a/src/yuzu/configuration/configure_motion_touch.ui b/src/yuzu/configuration/configure_motion_touch.ui
index 1e35ea946..c75a84ae4 100644
--- a/src/yuzu/configuration/configure_motion_touch.ui
+++ b/src/yuzu/configuration/configure_motion_touch.ui
@@ -2,14 +2,6 @@
2<ui version="4.0"> 2<ui version="4.0">
3 <class>ConfigureMotionTouch</class> 3 <class>ConfigureMotionTouch</class>
4 <widget class="QDialog" name="ConfigureMotionTouch"> 4 <widget class="QDialog" name="ConfigureMotionTouch">
5 <property name="geometry">
6 <rect>
7 <x>0</x>
8 <y>0</y>
9 <width>500</width>
10 <height>482</height>
11 </rect>
12 </property>
13 <property name="windowTitle"> 5 <property name="windowTitle">
14 <string>Configure Motion / Touch</string> 6 <string>Configure Motion / Touch</string>
15 </property> 7 </property>
@@ -18,48 +10,6 @@
18 </property> 10 </property>
19 <layout class="QVBoxLayout"> 11 <layout class="QVBoxLayout">
20 <item> 12 <item>
21 <widget class="QGroupBox" name="motion_group_box">
22 <property name="title">
23 <string>Mouse Motion</string>
24 </property>
25 <layout class="QVBoxLayout">
26 <item>
27 <layout class="QHBoxLayout">
28 <item>
29 <widget class="QLabel" name="motion_sensitivity_label">
30 <property name="text">
31 <string>Sensitivity:</string>
32 </property>
33 </widget>
34 </item>
35 <item>
36 <widget class="QDoubleSpinBox" name="motion_sensitivity">
37 <property name="alignment">
38 <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
39 </property>
40 <property name="decimals">
41 <number>4</number>
42 </property>
43 <property name="minimum">
44 <double>0.010000000000000</double>
45 </property>
46 <property name="maximum">
47 <double>10.000000000000000</double>
48 </property>
49 <property name="singleStep">
50 <double>0.001000000000000</double>
51 </property>
52 <property name="value">
53 <double>0.010000000000000</double>
54 </property>
55 </widget>
56 </item>
57 </layout>
58 </item>
59 </layout>
60 </widget>
61 </item>
62 <item>
63 <widget class="QGroupBox" name="touch_group_box"> 13 <widget class="QGroupBox" name="touch_group_box">
64 <property name="title"> 14 <property name="title">
65 <string>Touch</string> 15 <string>Touch</string>
@@ -101,19 +51,13 @@
101 </item> 51 </item>
102 <item> 52 <item>
103 <layout class="QHBoxLayout"> 53 <layout class="QHBoxLayout">
104 <item> 54 <item>
105 <widget class="QCheckBox" name="touch_from_button_checkbox"> 55 <widget class="QLabel" name="touch_from_button_label">
106 <property name="sizePolicy"> 56 <property name="text">
107 <sizepolicy hsizetype="Maximum" vsizetype="Fixed"> 57 <string>Touch from button profile:</string>
108 <horstretch>0</horstretch> 58 </property>
109 <verstretch>0</verstretch> 59 </widget>
110 </sizepolicy> 60 </item>
111 </property>
112 <property name="text">
113 <string>Use button mapping:</string>
114 </property>
115 </widget>
116 </item>
117 <item> 61 <item>
118 <widget class="QComboBox" name="touch_from_button_map"/> 62 <widget class="QComboBox" name="touch_from_button_map"/>
119 </item> 63 </item>
diff --git a/src/yuzu/configuration/configure_mouse_advanced.cpp b/src/yuzu/configuration/configure_mouse_advanced.cpp
deleted file mode 100644
index 2af3afda8..000000000
--- a/src/yuzu/configuration/configure_mouse_advanced.cpp
+++ /dev/null
@@ -1,276 +0,0 @@
1// Copyright 2016 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <algorithm>
6#include <memory>
7
8#include <QKeyEvent>
9#include <QMenu>
10#include <QTimer>
11
12#include "common/assert.h"
13#include "common/param_package.h"
14#include "input_common/main.h"
15#include "ui_configure_mouse_advanced.h"
16#include "yuzu/configuration/config.h"
17#include "yuzu/configuration/configure_mouse_advanced.h"
18
19static QString GetKeyName(int key_code) {
20 switch (key_code) {
21 case Qt::LeftButton:
22 return QObject::tr("Click 0");
23 case Qt::RightButton:
24 return QObject::tr("Click 1");
25 case Qt::MiddleButton:
26 return QObject::tr("Click 2");
27 case Qt::BackButton:
28 return QObject::tr("Click 3");
29 case Qt::ForwardButton:
30 return QObject::tr("Click 4");
31 case Qt::Key_Shift:
32 return QObject::tr("Shift");
33 case Qt::Key_Control:
34 return QObject::tr("Ctrl");
35 case Qt::Key_Alt:
36 return QObject::tr("Alt");
37 case Qt::Key_Meta:
38 return {};
39 default:
40 return QKeySequence(key_code).toString();
41 }
42}
43
44static QString ButtonToText(const Common::ParamPackage& param) {
45 if (!param.Has("engine")) {
46 return QObject::tr("[not set]");
47 }
48
49 if (param.Get("engine", "") == "keyboard") {
50 return GetKeyName(param.Get("code", 0));
51 }
52
53 if (param.Get("engine", "") == "sdl") {
54 if (param.Has("hat")) {
55 const QString hat_str = QString::fromStdString(param.Get("hat", ""));
56 const QString direction_str = QString::fromStdString(param.Get("direction", ""));
57
58 return QObject::tr("Hat %1 %2").arg(hat_str, direction_str);
59 }
60
61 if (param.Has("axis")) {
62 const QString axis_str = QString::fromStdString(param.Get("axis", ""));
63 const QString direction_str = QString::fromStdString(param.Get("direction", ""));
64
65 return QObject::tr("Axis %1%2").arg(axis_str, direction_str);
66 }
67
68 if (param.Has("button")) {
69 const QString button_str = QString::fromStdString(param.Get("button", ""));
70
71 return QObject::tr("Button %1").arg(button_str);
72 }
73 return {};
74 }
75
76 return QObject::tr("[unknown]");
77}
78
79ConfigureMouseAdvanced::ConfigureMouseAdvanced(QWidget* parent,
80 InputCommon::InputSubsystem* input_subsystem_)
81 : QDialog(parent),
82 ui(std::make_unique<Ui::ConfigureMouseAdvanced>()), input_subsystem{input_subsystem_},
83 timeout_timer(std::make_unique<QTimer>()), poll_timer(std::make_unique<QTimer>()) {
84 ui->setupUi(this);
85 setFocusPolicy(Qt::ClickFocus);
86
87 button_map = {
88 ui->left_button, ui->right_button, ui->middle_button, ui->forward_button, ui->back_button,
89 };
90
91 for (int button_id = 0; button_id < Settings::NativeMouseButton::NumMouseButtons; button_id++) {
92 auto* const button = button_map[button_id];
93 if (button == nullptr) {
94 continue;
95 }
96
97 button->setContextMenuPolicy(Qt::CustomContextMenu);
98 connect(button, &QPushButton::clicked, [=, this] {
99 HandleClick(
100 button_map[button_id],
101 [=, this](const Common::ParamPackage& params) {
102 buttons_param[button_id] = params;
103 },
104 InputCommon::Polling::DeviceType::Button);
105 });
106 connect(button, &QPushButton::customContextMenuRequested,
107 [=, this](const QPoint& menu_location) {
108 QMenu context_menu;
109 context_menu.addAction(tr("Clear"), [&] {
110 buttons_param[button_id].Clear();
111 button_map[button_id]->setText(tr("[not set]"));
112 });
113 context_menu.addAction(tr("Restore Default"), [&] {
114 buttons_param[button_id] =
115 Common::ParamPackage{InputCommon::GenerateKeyboardParam(
116 Config::default_mouse_buttons[button_id])};
117 button_map[button_id]->setText(ButtonToText(buttons_param[button_id]));
118 });
119 context_menu.exec(button_map[button_id]->mapToGlobal(menu_location));
120 });
121 }
122
123 connect(ui->buttonClearAll, &QPushButton::clicked, [this] { ClearAll(); });
124 connect(ui->buttonRestoreDefaults, &QPushButton::clicked, [this] { RestoreDefaults(); });
125
126 timeout_timer->setSingleShot(true);
127 connect(timeout_timer.get(), &QTimer::timeout, [this] { SetPollingResult({}, true); });
128
129 connect(poll_timer.get(), &QTimer::timeout, [this] {
130 Common::ParamPackage params;
131 for (auto& poller : device_pollers) {
132 params = poller->GetNextInput();
133 if (params.Has("engine")) {
134 SetPollingResult(params, false);
135 return;
136 }
137 }
138 });
139
140 LoadConfiguration();
141 resize(0, 0);
142}
143
144ConfigureMouseAdvanced::~ConfigureMouseAdvanced() = default;
145
146void ConfigureMouseAdvanced::ApplyConfiguration() {
147 std::transform(buttons_param.begin(), buttons_param.end(),
148 Settings::values.mouse_buttons.begin(),
149 [](const Common::ParamPackage& param) { return param.Serialize(); });
150}
151
152void ConfigureMouseAdvanced::LoadConfiguration() {
153 std::transform(Settings::values.mouse_buttons.begin(), Settings::values.mouse_buttons.end(),
154 buttons_param.begin(),
155 [](const std::string& str) { return Common::ParamPackage(str); });
156 UpdateButtonLabels();
157}
158
159void ConfigureMouseAdvanced::changeEvent(QEvent* event) {
160 if (event->type() == QEvent::LanguageChange) {
161 RetranslateUI();
162 }
163
164 QDialog::changeEvent(event);
165}
166
167void ConfigureMouseAdvanced::RetranslateUI() {
168 ui->retranslateUi(this);
169}
170
171void ConfigureMouseAdvanced::RestoreDefaults() {
172 for (int button_id = 0; button_id < Settings::NativeMouseButton::NumMouseButtons; button_id++) {
173 buttons_param[button_id] = Common::ParamPackage{
174 InputCommon::GenerateKeyboardParam(Config::default_mouse_buttons[button_id])};
175 }
176
177 UpdateButtonLabels();
178}
179
180void ConfigureMouseAdvanced::ClearAll() {
181 for (int i = 0; i < Settings::NativeMouseButton::NumMouseButtons; ++i) {
182 const auto* const button = button_map[i];
183 if (button != nullptr && button->isEnabled()) {
184 buttons_param[i].Clear();
185 }
186 }
187
188 UpdateButtonLabels();
189}
190
191void ConfigureMouseAdvanced::UpdateButtonLabels() {
192 for (int button = 0; button < Settings::NativeMouseButton::NumMouseButtons; button++) {
193 button_map[button]->setText(ButtonToText(buttons_param[button]));
194 }
195}
196
197void ConfigureMouseAdvanced::HandleClick(
198 QPushButton* button, std::function<void(const Common::ParamPackage&)> new_input_setter,
199 InputCommon::Polling::DeviceType type) {
200 button->setText(tr("[press key]"));
201 button->setFocus();
202
203 // Keyboard keys or mouse buttons can only be used as button devices
204 want_keyboard_mouse = type == InputCommon::Polling::DeviceType::Button;
205 if (want_keyboard_mouse) {
206 const auto iter = std::find(button_map.begin(), button_map.end(), button);
207 ASSERT(iter != button_map.end());
208 const auto index = std::distance(button_map.begin(), iter);
209 ASSERT(index < Settings::NativeButton::NumButtons && index >= 0);
210 }
211
212 input_setter = new_input_setter;
213
214 device_pollers = input_subsystem->GetPollers(type);
215
216 for (auto& poller : device_pollers) {
217 poller->Start();
218 }
219
220 QWidget::grabMouse();
221 QWidget::grabKeyboard();
222
223 timeout_timer->start(2500); // Cancel after 2.5 seconds
224 poll_timer->start(50); // Check for new inputs every 50ms
225}
226
227void ConfigureMouseAdvanced::SetPollingResult(const Common::ParamPackage& params, bool abort) {
228 timeout_timer->stop();
229 poll_timer->stop();
230 for (auto& poller : device_pollers) {
231 poller->Stop();
232 }
233
234 QWidget::releaseMouse();
235 QWidget::releaseKeyboard();
236
237 if (!abort) {
238 (*input_setter)(params);
239 }
240
241 UpdateButtonLabels();
242 input_setter = std::nullopt;
243}
244
245void ConfigureMouseAdvanced::mousePressEvent(QMouseEvent* event) {
246 if (!input_setter || !event) {
247 return;
248 }
249
250 if (want_keyboard_mouse) {
251 SetPollingResult(Common::ParamPackage{InputCommon::GenerateKeyboardParam(event->button())},
252 false);
253 } else {
254 // We don't want any mouse buttons, so don't stop polling
255 return;
256 }
257
258 SetPollingResult({}, true);
259}
260
261void ConfigureMouseAdvanced::keyPressEvent(QKeyEvent* event) {
262 if (!input_setter || !event) {
263 return;
264 }
265
266 if (event->key() != Qt::Key_Escape) {
267 if (want_keyboard_mouse) {
268 SetPollingResult(Common::ParamPackage{InputCommon::GenerateKeyboardParam(event->key())},
269 false);
270 } else {
271 // Escape key wasn't pressed and we don't want any keyboard keys, so don't stop polling
272 return;
273 }
274 }
275 SetPollingResult({}, true);
276}
diff --git a/src/yuzu/configuration/configure_mouse_advanced.h b/src/yuzu/configuration/configure_mouse_advanced.h
deleted file mode 100644
index 65b6fca9a..000000000
--- a/src/yuzu/configuration/configure_mouse_advanced.h
+++ /dev/null
@@ -1,78 +0,0 @@
1// Copyright 2016 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <memory>
8#include <optional>
9#include <QDialog>
10
11class QCheckBox;
12class QPushButton;
13class QTimer;
14
15namespace InputCommon {
16class InputSubsystem;
17}
18
19namespace Ui {
20class ConfigureMouseAdvanced;
21}
22
23class ConfigureMouseAdvanced : public QDialog {
24 Q_OBJECT
25
26public:
27 explicit ConfigureMouseAdvanced(QWidget* parent, InputCommon::InputSubsystem* input_subsystem_);
28 ~ConfigureMouseAdvanced() override;
29
30 void ApplyConfiguration();
31
32private:
33 void changeEvent(QEvent* event) override;
34 void RetranslateUI();
35
36 /// Load configuration settings.
37 void LoadConfiguration();
38 /// Restore all buttons to their default values.
39 void RestoreDefaults();
40 /// Clear all input configuration
41 void ClearAll();
42
43 /// Update UI to reflect current configuration.
44 void UpdateButtonLabels();
45
46 /// Called when the button was pressed.
47 void HandleClick(QPushButton* button,
48 std::function<void(const Common::ParamPackage&)> new_input_setter,
49 InputCommon::Polling::DeviceType type);
50
51 /// Finish polling and configure input using the input_setter
52 void SetPollingResult(const Common::ParamPackage& params, bool abort);
53
54 /// Handle mouse button press events.
55 void mousePressEvent(QMouseEvent* event) override;
56
57 /// Handle key press events.
58 void keyPressEvent(QKeyEvent* event) override;
59
60 std::unique_ptr<Ui::ConfigureMouseAdvanced> ui;
61
62 InputCommon::InputSubsystem* input_subsystem;
63
64 /// This will be the the setting function when an input is awaiting configuration.
65 std::optional<std::function<void(const Common::ParamPackage&)>> input_setter;
66
67 std::array<QPushButton*, Settings::NativeMouseButton::NumMouseButtons> button_map;
68 std::array<Common::ParamPackage, Settings::NativeMouseButton::NumMouseButtons> buttons_param;
69
70 std::vector<std::unique_ptr<InputCommon::Polling::DevicePoller>> device_pollers;
71
72 std::unique_ptr<QTimer> timeout_timer;
73 std::unique_ptr<QTimer> poll_timer;
74
75 /// A flag to indicate if keyboard keys are okay when configuring an input. If this is false,
76 /// keyboard events are ignored.
77 bool want_keyboard_mouse = false;
78};
diff --git a/src/yuzu/configuration/configure_mouse_advanced.ui b/src/yuzu/configuration/configure_mouse_advanced.ui
deleted file mode 100644
index 5b99e1c37..000000000
--- a/src/yuzu/configuration/configure_mouse_advanced.ui
+++ /dev/null
@@ -1,335 +0,0 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<ui version="4.0">
3 <class>ConfigureMouseAdvanced</class>
4 <widget class="QDialog" name="ConfigureMouseAdvanced">
5 <property name="geometry">
6 <rect>
7 <x>0</x>
8 <y>0</y>
9 <width>310</width>
10 <height>193</height>
11 </rect>
12 </property>
13 <property name="windowTitle">
14 <string>Configure Mouse</string>
15 </property>
16 <property name="styleSheet">
17 <string notr="true">QPushButton {
18 min-width: 60px;
19}</string>
20 </property>
21 <layout class="QVBoxLayout" name="verticalLayout">
22 <item>
23 <widget class="QGroupBox" name="gridGroupBox">
24 <property name="title">
25 <string>Mouse Buttons</string>
26 </property>
27 <layout class="QGridLayout" name="gridLayout">
28 <item row="3" column="5">
29 <layout class="QVBoxLayout" name="verticalLayout_6">
30 <item>
31 <layout class="QHBoxLayout" name="horizontalLayout_5">
32 <item>
33 <widget class="QLabel" name="label_5">
34 <property name="text">
35 <string>Forward:</string>
36 </property>
37 </widget>
38 </item>
39 </layout>
40 </item>
41 <item>
42 <widget class="QPushButton" name="forward_button">
43 <property name="minimumSize">
44 <size>
45 <width>68</width>
46 <height>0</height>
47 </size>
48 </property>
49 <property name="maximumSize">
50 <size>
51 <width>68</width>
52 <height>16777215</height>
53 </size>
54 </property>
55 <property name="text">
56 <string/>
57 </property>
58 </widget>
59 </item>
60 </layout>
61 </item>
62 <item row="3" column="1">
63 <layout class="QVBoxLayout" name="verticalLayout_5">
64 <item>
65 <layout class="QHBoxLayout" name="horizontalLayout_4">
66 <item>
67 <widget class="QLabel" name="label_4">
68 <property name="minimumSize">
69 <size>
70 <width>54</width>
71 <height>0</height>
72 </size>
73 </property>
74 <property name="text">
75 <string>Back:</string>
76 </property>
77 </widget>
78 </item>
79 </layout>
80 </item>
81 <item>
82 <widget class="QPushButton" name="back_button">
83 <property name="minimumSize">
84 <size>
85 <width>68</width>
86 <height>0</height>
87 </size>
88 </property>
89 <property name="text">
90 <string/>
91 </property>
92 </widget>
93 </item>
94 </layout>
95 </item>
96 <item row="0" column="1">
97 <layout class="QVBoxLayout" name="verticalLayout_2">
98 <item>
99 <layout class="QHBoxLayout" name="horizontalLayout">
100 <item>
101 <widget class="QLabel" name="label">
102 <property name="text">
103 <string>Left:</string>
104 </property>
105 </widget>
106 </item>
107 </layout>
108 </item>
109 <item>
110 <widget class="QPushButton" name="left_button">
111 <property name="minimumSize">
112 <size>
113 <width>68</width>
114 <height>0</height>
115 </size>
116 </property>
117 <property name="text">
118 <string/>
119 </property>
120 </widget>
121 </item>
122 </layout>
123 </item>
124 <item row="0" column="3">
125 <layout class="QVBoxLayout" name="verticalLayout_3">
126 <item>
127 <layout class="QHBoxLayout" name="horizontalLayout_2">
128 <item>
129 <widget class="QLabel" name="label_2">
130 <property name="text">
131 <string>Middle:</string>
132 </property>
133 </widget>
134 </item>
135 </layout>
136 </item>
137 <item>
138 <widget class="QPushButton" name="middle_button">
139 <property name="minimumSize">
140 <size>
141 <width>68</width>
142 <height>0</height>
143 </size>
144 </property>
145 <property name="maximumSize">
146 <size>
147 <width>68</width>
148 <height>16777215</height>
149 </size>
150 </property>
151 <property name="text">
152 <string/>
153 </property>
154 </widget>
155 </item>
156 </layout>
157 </item>
158 <item row="0" column="6">
159 <spacer name="horizontalSpacer_2">
160 <property name="orientation">
161 <enum>Qt::Horizontal</enum>
162 </property>
163 <property name="sizeType">
164 <enum>QSizePolicy::Fixed</enum>
165 </property>
166 <property name="sizeHint" stdset="0">
167 <size>
168 <width>0</width>
169 <height>20</height>
170 </size>
171 </property>
172 </spacer>
173 </item>
174 <item row="0" column="0">
175 <spacer name="horizontalSpacer">
176 <property name="orientation">
177 <enum>Qt::Horizontal</enum>
178 </property>
179 <property name="sizeType">
180 <enum>QSizePolicy::Fixed</enum>
181 </property>
182 <property name="sizeHint" stdset="0">
183 <size>
184 <width>0</width>
185 <height>20</height>
186 </size>
187 </property>
188 </spacer>
189 </item>
190 <item row="0" column="5">
191 <layout class="QVBoxLayout" name="verticalLayout_4">
192 <item>
193 <layout class="QHBoxLayout" name="horizontalLayout_3">
194 <item>
195 <widget class="QLabel" name="label_3">
196 <property name="text">
197 <string>Right:</string>
198 </property>
199 </widget>
200 </item>
201 </layout>
202 </item>
203 <item>
204 <widget class="QPushButton" name="right_button">
205 <property name="minimumSize">
206 <size>
207 <width>68</width>
208 <height>0</height>
209 </size>
210 </property>
211 <property name="maximumSize">
212 <size>
213 <width>68</width>
214 <height>16777215</height>
215 </size>
216 </property>
217 <property name="text">
218 <string/>
219 </property>
220 </widget>
221 </item>
222 </layout>
223 </item>
224 <item row="0" column="2">
225 <spacer name="horizontalSpacer_4">
226 <property name="orientation">
227 <enum>Qt::Horizontal</enum>
228 </property>
229 <property name="sizeHint" stdset="0">
230 <size>
231 <width>0</width>
232 <height>20</height>
233 </size>
234 </property>
235 </spacer>
236 </item>
237 <item row="0" column="4">
238 <spacer name="horizontalSpacer_5">
239 <property name="orientation">
240 <enum>Qt::Horizontal</enum>
241 </property>
242 <property name="sizeHint" stdset="0">
243 <size>
244 <width>0</width>
245 <height>20</height>
246 </size>
247 </property>
248 </spacer>
249 </item>
250 </layout>
251 </widget>
252 </item>
253 <item>
254 <layout class="QHBoxLayout" name="horizontalLayout_6">
255 <item>
256 <widget class="QPushButton" name="buttonClearAll">
257 <property name="minimumSize">
258 <size>
259 <width>68</width>
260 <height>0</height>
261 </size>
262 </property>
263 <property name="maximumSize">
264 <size>
265 <width>68</width>
266 <height>16777215</height>
267 </size>
268 </property>
269 <property name="text">
270 <string>Clear</string>
271 </property>
272 </widget>
273 </item>
274 <item>
275 <widget class="QPushButton" name="buttonRestoreDefaults">
276 <property name="minimumSize">
277 <size>
278 <width>68</width>
279 <height>0</height>
280 </size>
281 </property>
282 <property name="maximumSize">
283 <size>
284 <width>68</width>
285 <height>16777215</height>
286 </size>
287 </property>
288 <property name="text">
289 <string>Defaults</string>
290 </property>
291 </widget>
292 </item>
293 <item>
294 <spacer name="horizontalSpacer_3">
295 <property name="orientation">
296 <enum>Qt::Horizontal</enum>
297 </property>
298 <property name="sizeHint" stdset="0">
299 <size>
300 <width>0</width>
301 <height>20</height>
302 </size>
303 </property>
304 </spacer>
305 </item>
306 <item>
307 <widget class="QDialogButtonBox" name="buttonBox">
308 <property name="styleSheet">
309 <string notr="true"/>
310 </property>
311 <property name="standardButtons">
312 <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
313 </property>
314 </widget>
315 </item>
316 </layout>
317 </item>
318 </layout>
319 </widget>
320 <resources/>
321 <connections>
322 <connection>
323 <sender>buttonBox</sender>
324 <signal>accepted()</signal>
325 <receiver>ConfigureMouseAdvanced</receiver>
326 <slot>accept()</slot>
327 </connection>
328 <connection>
329 <sender>buttonBox</sender>
330 <signal>rejected()</signal>
331 <receiver>ConfigureMouseAdvanced</receiver>
332 <slot>reject()</slot>
333 </connection>
334 </connections>
335</ui>
diff --git a/src/yuzu/configuration/configure_tas.cpp b/src/yuzu/configuration/configure_tas.cpp
index 8e5a4c72d..979a8db61 100644
--- a/src/yuzu/configuration/configure_tas.cpp
+++ b/src/yuzu/configuration/configure_tas.cpp
@@ -32,7 +32,6 @@ void ConfigureTasDialog::LoadConfiguration() {
32 ui->tas_path_edit->setText( 32 ui->tas_path_edit->setText(
33 QString::fromStdString(Common::FS::GetYuzuPathString(Common::FS::YuzuPath::TASDir))); 33 QString::fromStdString(Common::FS::GetYuzuPathString(Common::FS::YuzuPath::TASDir)));
34 ui->tas_enable->setChecked(Settings::values.tas_enable.GetValue()); 34 ui->tas_enable->setChecked(Settings::values.tas_enable.GetValue());
35 ui->tas_control_swap->setChecked(Settings::values.tas_swap_controllers.GetValue());
36 ui->tas_loop_script->setChecked(Settings::values.tas_loop.GetValue()); 35 ui->tas_loop_script->setChecked(Settings::values.tas_loop.GetValue());
37 ui->tas_pause_on_load->setChecked(Settings::values.pause_tas_on_load.GetValue()); 36 ui->tas_pause_on_load->setChecked(Settings::values.pause_tas_on_load.GetValue());
38} 37}
@@ -40,7 +39,6 @@ void ConfigureTasDialog::LoadConfiguration() {
40void ConfigureTasDialog::ApplyConfiguration() { 39void ConfigureTasDialog::ApplyConfiguration() {
41 Common::FS::SetYuzuPath(Common::FS::YuzuPath::TASDir, ui->tas_path_edit->text().toStdString()); 40 Common::FS::SetYuzuPath(Common::FS::YuzuPath::TASDir, ui->tas_path_edit->text().toStdString());
42 Settings::values.tas_enable.SetValue(ui->tas_enable->isChecked()); 41 Settings::values.tas_enable.SetValue(ui->tas_enable->isChecked());
43 Settings::values.tas_swap_controllers.SetValue(ui->tas_control_swap->isChecked());
44 Settings::values.tas_loop.SetValue(ui->tas_loop_script->isChecked()); 42 Settings::values.tas_loop.SetValue(ui->tas_loop_script->isChecked());
45 Settings::values.pause_tas_on_load.SetValue(ui->tas_pause_on_load->isChecked()); 43 Settings::values.pause_tas_on_load.SetValue(ui->tas_pause_on_load->isChecked());
46} 44}
diff --git a/src/yuzu/configuration/configure_tas.ui b/src/yuzu/configuration/configure_tas.ui
index 7d44895c4..cf88a5bf0 100644
--- a/src/yuzu/configuration/configure_tas.ui
+++ b/src/yuzu/configuration/configure_tas.ui
@@ -59,20 +59,13 @@
59 </widget> 59 </widget>
60 </item> 60 </item>
61 <item row="1" column="0" colspan="4"> 61 <item row="1" column="0" colspan="4">
62 <widget class="QCheckBox" name="tas_control_swap">
63 <property name="text">
64 <string>Automatic controller profile swapping</string>
65 </property>
66 </widget>
67 </item>
68 <item row="2" column="0" colspan="4">
69 <widget class="QCheckBox" name="tas_loop_script"> 62 <widget class="QCheckBox" name="tas_loop_script">
70 <property name="text"> 63 <property name="text">
71 <string>Loop script</string> 64 <string>Loop script</string>
72 </property> 65 </property>
73 </widget> 66 </widget>
74 </item> 67 </item>
75 <item row="3" column="0" colspan="4"> 68 <item row="2" column="0" colspan="4">
76 <widget class="QCheckBox" name="tas_pause_on_load"> 69 <widget class="QCheckBox" name="tas_pause_on_load">
77 <property name="enabled"> 70 <property name="enabled">
78 <bool>false</bool> 71 <bool>false</bool>
diff --git a/src/yuzu/configuration/configure_touch_from_button.cpp b/src/yuzu/configuration/configure_touch_from_button.cpp
index 40129f228..bde0a08c4 100644
--- a/src/yuzu/configuration/configure_touch_from_button.cpp
+++ b/src/yuzu/configuration/configure_touch_from_button.cpp
@@ -163,13 +163,10 @@ void ConfigureTouchFromButton::ConnectEvents() {
163 connect(timeout_timer.get(), &QTimer::timeout, [this]() { SetPollingResult({}, true); }); 163 connect(timeout_timer.get(), &QTimer::timeout, [this]() { SetPollingResult({}, true); });
164 164
165 connect(poll_timer.get(), &QTimer::timeout, [this]() { 165 connect(poll_timer.get(), &QTimer::timeout, [this]() {
166 Common::ParamPackage params; 166 const auto& params = input_subsystem->GetNextInput();
167 for (auto& poller : device_pollers) { 167 if (params.Has("engine")) {
168 params = poller->GetNextInput(); 168 SetPollingResult(params, false);
169 if (params.Has("engine")) { 169 return;
170 SetPollingResult(params, false);
171 return;
172 }
173 } 170 }
174 }); 171 });
175} 172}
@@ -248,11 +245,7 @@ void ConfigureTouchFromButton::GetButtonInput(const int row_index, const bool is
248 } 245 }
249 }; 246 };
250 247
251 device_pollers = input_subsystem->GetPollers(InputCommon::Polling::DeviceType::Button); 248 input_subsystem->BeginMapping(InputCommon::Polling::InputType::Button);
252
253 for (auto& poller : device_pollers) {
254 poller->Start();
255 }
256 249
257 grabKeyboard(); 250 grabKeyboard();
258 grabMouse(); 251 grabMouse();
@@ -365,14 +358,14 @@ void ConfigureTouchFromButton::SetCoordinates(const int dot_id, const QPoint& po
365 358
366void ConfigureTouchFromButton::SetPollingResult(const Common::ParamPackage& params, 359void ConfigureTouchFromButton::SetPollingResult(const Common::ParamPackage& params,
367 const bool cancel) { 360 const bool cancel) {
361 timeout_timer->stop();
362 poll_timer->stop();
363 input_subsystem->StopMapping();
364
368 releaseKeyboard(); 365 releaseKeyboard();
369 releaseMouse(); 366 releaseMouse();
370 qApp->restoreOverrideCursor(); 367 qApp->restoreOverrideCursor();
371 timeout_timer->stop(); 368
372 poll_timer->stop();
373 for (auto& poller : device_pollers) {
374 poller->Stop();
375 }
376 if (input_setter) { 369 if (input_setter) {
377 (*input_setter)(params, cancel); 370 (*input_setter)(params, cancel);
378 input_setter.reset(); 371 input_setter.reset();
diff --git a/src/yuzu/configuration/configure_touch_from_button.h b/src/yuzu/configuration/configure_touch_from_button.h
index d9513e3bc..e1400481a 100644
--- a/src/yuzu/configuration/configure_touch_from_button.h
+++ b/src/yuzu/configuration/configure_touch_from_button.h
@@ -24,10 +24,6 @@ namespace InputCommon {
24class InputSubsystem; 24class InputSubsystem;
25} 25}
26 26
27namespace InputCommon::Polling {
28class DevicePoller;
29}
30
31namespace Settings { 27namespace Settings {
32struct TouchFromButtonMap; 28struct TouchFromButtonMap;
33} 29}
@@ -85,7 +81,6 @@ private:
85 81
86 std::unique_ptr<QTimer> timeout_timer; 82 std::unique_ptr<QTimer> timeout_timer;
87 std::unique_ptr<QTimer> poll_timer; 83 std::unique_ptr<QTimer> poll_timer;
88 std::vector<std::unique_ptr<InputCommon::Polling::DevicePoller>> device_pollers;
89 std::optional<std::function<void(const Common::ParamPackage&, bool)>> input_setter; 84 std::optional<std::function<void(const Common::ParamPackage&, bool)>> input_setter;
90 85
91 static constexpr int DataRoleDot = Qt::ItemDataRole::UserRole + 2; 86 static constexpr int DataRoleDot = Qt::ItemDataRole::UserRole + 2;
diff --git a/src/yuzu/configuration/configure_vibration.cpp b/src/yuzu/configuration/configure_vibration.cpp
index 46a0f3025..adce04b27 100644
--- a/src/yuzu/configuration/configure_vibration.cpp
+++ b/src/yuzu/configuration/configure_vibration.cpp
@@ -59,80 +59,6 @@ void ConfigureVibration::ApplyConfiguration() {
59 ui->checkBoxAccurateVibration->isChecked()); 59 ui->checkBoxAccurateVibration->isChecked());
60} 60}
61 61
62void ConfigureVibration::SetVibrationDevices(std::size_t player_index) {
63 using namespace Settings::NativeButton;
64 static constexpr std::array<std::array<Settings::NativeButton::Values, 6>, 2> buttons{{
65 {DLeft, DUp, DRight, DDown, L, ZL}, // Left Buttons
66 {A, B, X, Y, R, ZR}, // Right Buttons
67 }};
68
69 auto& player = Settings::values.players.GetValue()[player_index];
70
71 for (std::size_t device_idx = 0; device_idx < buttons.size(); ++device_idx) {
72 std::unordered_map<std::string, int> params_count;
73
74 for (const auto button_index : buttons[device_idx]) {
75 const auto& player_button = player.buttons[button_index];
76
77 if (params_count.find(player_button) != params_count.end()) {
78 ++params_count[player_button];
79 continue;
80 }
81
82 params_count.insert_or_assign(player_button, 1);
83 }
84
85 const auto it = std::max_element(
86 params_count.begin(), params_count.end(),
87 [](const auto& lhs, const auto& rhs) { return lhs.second < rhs.second; });
88
89 auto& vibration_param_str = player.vibrations[device_idx];
90 vibration_param_str.clear();
91
92 if (it->first.empty()) {
93 continue;
94 }
95
96 const auto param = Common::ParamPackage(it->first);
97
98 const auto engine = param.Get("engine", "");
99 const auto guid = param.Get("guid", "");
100 const auto port = param.Get("port", "");
101
102 if (engine.empty() || engine == "keyboard" || engine == "mouse" || engine == "tas") {
103 continue;
104 }
105
106 vibration_param_str += fmt::format("engine:{}", engine);
107
108 if (!port.empty()) {
109 vibration_param_str += fmt::format(",port:{}", port);
110 }
111 if (!guid.empty()) {
112 vibration_param_str += fmt::format(",guid:{}", guid);
113 }
114 }
115
116 if (player.vibrations[0] != player.vibrations[1]) {
117 return;
118 }
119
120 if (!player.vibrations[0].empty() &&
121 player.controller_type != Settings::ControllerType::RightJoycon) {
122 player.vibrations[1].clear();
123 } else if (!player.vibrations[1].empty() &&
124 player.controller_type == Settings::ControllerType::RightJoycon) {
125 player.vibrations[0].clear();
126 }
127}
128
129void ConfigureVibration::SetAllVibrationDevices() {
130 // Set vibration devices for all player indices including handheld
131 for (std::size_t player_idx = 0; player_idx < NUM_PLAYERS + 1; ++player_idx) {
132 SetVibrationDevices(player_idx);
133 }
134}
135
136void ConfigureVibration::changeEvent(QEvent* event) { 62void ConfigureVibration::changeEvent(QEvent* event) {
137 if (event->type() == QEvent::LanguageChange) { 63 if (event->type() == QEvent::LanguageChange) {
138 RetranslateUI(); 64 RetranslateUI();
diff --git a/src/yuzu/configuration/configure_vibration.h b/src/yuzu/configuration/configure_vibration.h
index 07411a86f..37bbc2653 100644
--- a/src/yuzu/configuration/configure_vibration.h
+++ b/src/yuzu/configuration/configure_vibration.h
@@ -24,9 +24,6 @@ public:
24 24
25 void ApplyConfiguration(); 25 void ApplyConfiguration();
26 26
27 static void SetVibrationDevices(std::size_t player_index);
28 static void SetAllVibrationDevices();
29
30private: 27private:
31 void changeEvent(QEvent* event) override; 28 void changeEvent(QEvent* event) override;
32 void RetranslateUI(); 29 void RetranslateUI();
diff --git a/src/yuzu/debugger/controller.cpp b/src/yuzu/debugger/controller.cpp
index 5a844409b..6b834c42e 100644
--- a/src/yuzu/debugger/controller.cpp
+++ b/src/yuzu/debugger/controller.cpp
@@ -6,13 +6,17 @@
6#include <QLayout> 6#include <QLayout>
7#include <QString> 7#include <QString>
8#include "common/settings.h" 8#include "common/settings.h"
9#include "core/hid/emulated_controller.h"
10#include "core/hid/hid_core.h"
11#include "input_common/drivers/tas_input.h"
9#include "input_common/main.h" 12#include "input_common/main.h"
10#include "input_common/tas/tas_input.h"
11#include "yuzu/configuration/configure_input_player_widget.h" 13#include "yuzu/configuration/configure_input_player_widget.h"
12#include "yuzu/debugger/controller.h" 14#include "yuzu/debugger/controller.h"
13 15
14ControllerDialog::ControllerDialog(QWidget* parent, InputCommon::InputSubsystem* input_subsystem_) 16ControllerDialog::ControllerDialog(Core::HID::HIDCore& hid_core_,
15 : QWidget(parent, Qt::Dialog), input_subsystem{input_subsystem_} { 17 std::shared_ptr<InputCommon::InputSubsystem> input_subsystem_,
18 QWidget* parent)
19 : QWidget(parent, Qt::Dialog), hid_core{hid_core_}, input_subsystem{input_subsystem_} {
16 setObjectName(QStringLiteral("Controller")); 20 setObjectName(QStringLiteral("Controller"));
17 setWindowTitle(tr("Controller P1")); 21 setWindowTitle(tr("Controller P1"));
18 resize(500, 350); 22 resize(500, 350);
@@ -31,20 +35,24 @@ ControllerDialog::ControllerDialog(QWidget* parent, InputCommon::InputSubsystem*
31 // Configure focus so that widget is focusable and the dialog automatically forwards focus to 35 // Configure focus so that widget is focusable and the dialog automatically forwards focus to
32 // it. 36 // it.
33 setFocusProxy(widget); 37 setFocusProxy(widget);
34 widget->SetConnectedStatus(false);
35 widget->setFocusPolicy(Qt::StrongFocus); 38 widget->setFocusPolicy(Qt::StrongFocus);
36 widget->setFocus(); 39 widget->setFocus();
37} 40}
38 41
39void ControllerDialog::refreshConfiguration() { 42void ControllerDialog::refreshConfiguration() {
40 const auto& players = Settings::values.players.GetValue(); 43 UnloadController();
41 constexpr std::size_t player = 0; 44 auto* player_1 = hid_core.GetEmulatedController(Core::HID::NpadIdType::Player1);
42 widget->SetPlayerInputRaw(player, players[player].buttons, players[player].analogs); 45 auto* handheld = hid_core.GetEmulatedController(Core::HID::NpadIdType::Handheld);
43 widget->SetControllerType(players[player].controller_type); 46 // Display the correct controller
44 ControllerCallback callback{[this](ControllerInput input) { InputController(input); }}; 47 controller = handheld->IsConnected() ? handheld : player_1;
45 widget->SetCallBack(callback); 48
46 widget->repaint(); 49 Core::HID::ControllerUpdateCallback engine_callback{
47 widget->SetConnectedStatus(players[player].connected); 50 .on_change = [this](Core::HID::ControllerTriggerType type) { ControllerUpdate(type); },
51 .is_npad_service = true,
52 };
53 callback_key = controller->SetCallback(engine_callback);
54 widget->SetController(controller);
55 is_controller_set = true;
48} 56}
49 57
50QAction* ControllerDialog::toggleViewAction() { 58QAction* ControllerDialog::toggleViewAction() {
@@ -58,11 +66,18 @@ QAction* ControllerDialog::toggleViewAction() {
58 return toggle_view_action; 66 return toggle_view_action;
59} 67}
60 68
69void ControllerDialog::UnloadController() {
70 widget->UnloadController();
71 if (is_controller_set) {
72 controller->DeleteCallback(callback_key);
73 is_controller_set = false;
74 }
75}
76
61void ControllerDialog::showEvent(QShowEvent* ev) { 77void ControllerDialog::showEvent(QShowEvent* ev) {
62 if (toggle_view_action) { 78 if (toggle_view_action) {
63 toggle_view_action->setChecked(isVisible()); 79 toggle_view_action->setChecked(isVisible());
64 } 80 }
65 refreshConfiguration();
66 QWidget::showEvent(ev); 81 QWidget::showEvent(ev);
67} 82}
68 83
@@ -70,16 +85,34 @@ void ControllerDialog::hideEvent(QHideEvent* ev) {
70 if (toggle_view_action) { 85 if (toggle_view_action) {
71 toggle_view_action->setChecked(isVisible()); 86 toggle_view_action->setChecked(isVisible());
72 } 87 }
73 widget->SetConnectedStatus(false);
74 QWidget::hideEvent(ev); 88 QWidget::hideEvent(ev);
75} 89}
76 90
77void ControllerDialog::InputController(ControllerInput input) { 91void ControllerDialog::ControllerUpdate(Core::HID::ControllerTriggerType type) {
78 u32 buttons = 0; 92 // TODO(german77): Remove TAS from here
79 int index = 0; 93 switch (type) {
80 for (bool btn : input.button_values) { 94 case Core::HID::ControllerTriggerType::Button:
81 buttons |= (btn ? 1U : 0U) << index; 95 case Core::HID::ControllerTriggerType::Stick: {
82 index++; 96 const auto buttons_values = controller->GetButtonsValues();
97 const auto stick_values = controller->GetSticksValues();
98 u64 buttons = 0;
99 std::size_t index = 0;
100 for (const auto& button : buttons_values) {
101 buttons |= button.value ? 1LLU << index : 0;
102 index++;
103 }
104 const InputCommon::TasInput::TasAnalog left_axis = {
105 .x = stick_values[Settings::NativeAnalog::LStick].x.value,
106 .y = stick_values[Settings::NativeAnalog::LStick].y.value,
107 };
108 const InputCommon::TasInput::TasAnalog right_axis = {
109 .x = stick_values[Settings::NativeAnalog::RStick].x.value,
110 .y = stick_values[Settings::NativeAnalog::RStick].y.value,
111 };
112 input_subsystem->GetTas()->RecordInput(buttons, left_axis, right_axis);
113 break;
114 }
115 default:
116 break;
83 } 117 }
84 input_subsystem->GetTas()->RecordInput(buttons, input.axis_values);
85} 118}
diff --git a/src/yuzu/debugger/controller.h b/src/yuzu/debugger/controller.h
index 7742db58b..52cea3326 100644
--- a/src/yuzu/debugger/controller.h
+++ b/src/yuzu/debugger/controller.h
@@ -4,9 +4,7 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <QFileSystemWatcher>
8#include <QWidget> 7#include <QWidget>
9#include "common/settings.h"
10 8
11class QAction; 9class QAction;
12class QHideEvent; 10class QHideEvent;
@@ -17,35 +15,43 @@ namespace InputCommon {
17class InputSubsystem; 15class InputSubsystem;
18} 16}
19 17
20struct ControllerInput { 18namespace Core::HID {
21 std::array<std::pair<float, float>, Settings::NativeAnalog::NUM_STICKS_HID> axis_values{}; 19class HIDCore;
22 std::array<bool, Settings::NativeButton::NumButtons> button_values{}; 20class EmulatedController;
23 bool changed{}; 21enum class ControllerTriggerType;
24}; 22} // namespace Core::HID
25
26struct ControllerCallback {
27 std::function<void(ControllerInput)> input;
28};
29 23
30class ControllerDialog : public QWidget { 24class ControllerDialog : public QWidget {
31 Q_OBJECT 25 Q_OBJECT
32 26
33public: 27public:
34 explicit ControllerDialog(QWidget* parent = nullptr, 28 explicit ControllerDialog(Core::HID::HIDCore& hid_core_,
35 InputCommon::InputSubsystem* input_subsystem_ = nullptr); 29 std::shared_ptr<InputCommon::InputSubsystem> input_subsystem_,
30 QWidget* parent = nullptr);
36 31
37 /// Returns a QAction that can be used to toggle visibility of this dialog. 32 /// Returns a QAction that can be used to toggle visibility of this dialog.
38 QAction* toggleViewAction(); 33 QAction* toggleViewAction();
34
35 /// Reloads the widget to apply any changes in the configuration
39 void refreshConfiguration(); 36 void refreshConfiguration();
40 37
38 /// Disables events from the emulated controller
39 void UnloadController();
40
41protected: 41protected:
42 void showEvent(QShowEvent* ev) override; 42 void showEvent(QShowEvent* ev) override;
43 void hideEvent(QHideEvent* ev) override; 43 void hideEvent(QHideEvent* ev) override;
44 44
45private: 45private:
46 void InputController(ControllerInput input); 46 /// Redirects input from the widget to the TAS driver
47 void ControllerUpdate(Core::HID::ControllerTriggerType type);
48
49 int callback_key;
50 bool is_controller_set{};
51 Core::HID::EmulatedController* controller;
52
47 QAction* toggle_view_action = nullptr; 53 QAction* toggle_view_action = nullptr;
48 QFileSystemWatcher* watcher = nullptr;
49 PlayerControlPreview* widget; 54 PlayerControlPreview* widget;
50 InputCommon::InputSubsystem* input_subsystem; 55 Core::HID::HIDCore& hid_core;
56 std::shared_ptr<InputCommon::InputSubsystem> input_subsystem;
51}; 57};
diff --git a/src/yuzu/debugger/profiler.cpp b/src/yuzu/debugger/profiler.cpp
index a8b254199..33110685a 100644
--- a/src/yuzu/debugger/profiler.cpp
+++ b/src/yuzu/debugger/profiler.cpp
@@ -163,7 +163,7 @@ void MicroProfileWidget::mouseReleaseEvent(QMouseEvent* ev) {
163} 163}
164 164
165void MicroProfileWidget::wheelEvent(QWheelEvent* ev) { 165void MicroProfileWidget::wheelEvent(QWheelEvent* ev) {
166 const auto wheel_position = ev->pos(); 166 const auto wheel_position = ev->position().toPoint();
167 MicroProfileMousePosition(wheel_position.x() / x_scale, wheel_position.y() / y_scale, 167 MicroProfileMousePosition(wheel_position.x() / x_scale, wheel_position.y() / y_scale,
168 ev->angleDelta().y() / 120); 168 ev->angleDelta().y() / 120);
169 ev->accept(); 169 ev->accept();
diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp
index 2af95dbe5..8b5c4a10a 100644
--- a/src/yuzu/game_list.cpp
+++ b/src/yuzu/game_list.cpp
@@ -17,6 +17,7 @@
17#include <fmt/format.h> 17#include <fmt/format.h>
18#include "common/common_types.h" 18#include "common/common_types.h"
19#include "common/logging/log.h" 19#include "common/logging/log.h"
20#include "core/core.h"
20#include "core/file_sys/patch_manager.h" 21#include "core/file_sys/patch_manager.h"
21#include "core/file_sys/registered_cache.h" 22#include "core/file_sys/registered_cache.h"
22#include "yuzu/compatibility_list.h" 23#include "yuzu/compatibility_list.h"
@@ -25,6 +26,7 @@
25#include "yuzu/game_list_worker.h" 26#include "yuzu/game_list_worker.h"
26#include "yuzu/main.h" 27#include "yuzu/main.h"
27#include "yuzu/uisettings.h" 28#include "yuzu/uisettings.h"
29#include "yuzu/util/controller_navigation.h"
28 30
29GameListSearchField::KeyReleaseEater::KeyReleaseEater(GameList* gamelist, QObject* parent) 31GameListSearchField::KeyReleaseEater::KeyReleaseEater(GameList* gamelist, QObject* parent)
30 : QObject(parent), gamelist{gamelist} {} 32 : QObject(parent), gamelist{gamelist} {}
@@ -171,13 +173,17 @@ void GameList::OnItemExpanded(const QModelIndex& item) {
171 const bool is_dir = type == GameListItemType::CustomDir || type == GameListItemType::SdmcDir || 173 const bool is_dir = type == GameListItemType::CustomDir || type == GameListItemType::SdmcDir ||
172 type == GameListItemType::UserNandDir || 174 type == GameListItemType::UserNandDir ||
173 type == GameListItemType::SysNandDir; 175 type == GameListItemType::SysNandDir;
174 176 const bool is_fave = type == GameListItemType::Favorites;
175 if (!is_dir) { 177 if (!is_dir && !is_fave) {
176 return; 178 return;
177 } 179 }
178 180 const bool is_expanded = tree_view->isExpanded(item);
179 UISettings::values.game_dirs[item.data(GameListDir::GameDirRole).toInt()].expanded = 181 if (is_fave) {
180 tree_view->isExpanded(item); 182 UISettings::values.favorites_expanded = is_expanded;
183 return;
184 }
185 const int item_dir_index = item.data(GameListDir::GameDirRole).toInt();
186 UISettings::values.game_dirs[item_dir_index].expanded = is_expanded;
181} 187}
182 188
183// Event in order to filter the gamelist after editing the searchfield 189// Event in order to filter the gamelist after editing the searchfield
@@ -312,6 +318,7 @@ GameList::GameList(FileSys::VirtualFilesystem vfs, FileSys::ManualContentProvide
312 this->main_window = parent; 318 this->main_window = parent;
313 layout = new QVBoxLayout; 319 layout = new QVBoxLayout;
314 tree_view = new QTreeView; 320 tree_view = new QTreeView;
321 controller_navigation = new ControllerNavigation(system.HIDCore(), this);
315 search_field = new GameListSearchField(this); 322 search_field = new GameListSearchField(this);
316 item_model = new QStandardItemModel(tree_view); 323 item_model = new QStandardItemModel(tree_view);
317 tree_view->setModel(item_model); 324 tree_view->setModel(item_model);
@@ -341,6 +348,18 @@ GameList::GameList(FileSys::VirtualFilesystem vfs, FileSys::ManualContentProvide
341 connect(tree_view, &QTreeView::customContextMenuRequested, this, &GameList::PopupContextMenu); 348 connect(tree_view, &QTreeView::customContextMenuRequested, this, &GameList::PopupContextMenu);
342 connect(tree_view, &QTreeView::expanded, this, &GameList::OnItemExpanded); 349 connect(tree_view, &QTreeView::expanded, this, &GameList::OnItemExpanded);
343 connect(tree_view, &QTreeView::collapsed, this, &GameList::OnItemExpanded); 350 connect(tree_view, &QTreeView::collapsed, this, &GameList::OnItemExpanded);
351 connect(controller_navigation, &ControllerNavigation::TriggerKeyboardEvent,
352 [this](Qt::Key key) {
353 // Avoid pressing buttons while playing
354 if (system.IsPoweredOn()) {
355 return;
356 }
357 if (!this->isActiveWindow()) {
358 return;
359 }
360 QKeyEvent* event = new QKeyEvent(QEvent::KeyPress, key, Qt::NoModifier);
361 QCoreApplication::postEvent(tree_view, event);
362 });
344 363
345 // We must register all custom types with the Qt Automoc system so that we are able to use 364 // We must register all custom types with the Qt Automoc system so that we are able to use
346 // it with signals/slots. In this case, QList falls under the umbrells of custom types. 365 // it with signals/slots. In this case, QList falls under the umbrells of custom types.
@@ -353,7 +372,12 @@ GameList::GameList(FileSys::VirtualFilesystem vfs, FileSys::ManualContentProvide
353 setLayout(layout); 372 setLayout(layout);
354} 373}
355 374
375void GameList::UnloadController() {
376 controller_navigation->UnloadController();
377}
378
356GameList::~GameList() { 379GameList::~GameList() {
380 UnloadController();
357 emit ShouldCancelWorker(); 381 emit ShouldCancelWorker();
358} 382}
359 383
@@ -438,10 +462,13 @@ void GameList::DonePopulating(const QStringList& watch_list) {
438 emit ShowList(!IsEmpty()); 462 emit ShowList(!IsEmpty());
439 463
440 item_model->invisibleRootItem()->appendRow(new GameListAddDir()); 464 item_model->invisibleRootItem()->appendRow(new GameListAddDir());
465
466 // Add favorites row
441 item_model->invisibleRootItem()->insertRow(0, new GameListFavorites()); 467 item_model->invisibleRootItem()->insertRow(0, new GameListFavorites());
442 tree_view->setRowHidden(0, item_model->invisibleRootItem()->index(), 468 tree_view->setRowHidden(0, item_model->invisibleRootItem()->index(),
443 UISettings::values.favorited_ids.size() == 0); 469 UISettings::values.favorited_ids.size() == 0);
444 tree_view->expand(item_model->invisibleRootItem()->child(0)->index()); 470 tree_view->setExpanded(item_model->invisibleRootItem()->child(0)->index(),
471 UISettings::values.favorites_expanded.GetValue());
445 for (const auto id : UISettings::values.favorited_ids) { 472 for (const auto id : UISettings::values.favorited_ids) {
446 AddFavorite(id); 473 AddFavorite(id);
447 } 474 }
diff --git a/src/yuzu/game_list.h b/src/yuzu/game_list.h
index 675469e66..a94ea1477 100644
--- a/src/yuzu/game_list.h
+++ b/src/yuzu/game_list.h
@@ -24,6 +24,7 @@
24#include "uisettings.h" 24#include "uisettings.h"
25#include "yuzu/compatibility_list.h" 25#include "yuzu/compatibility_list.h"
26 26
27class ControllerNavigation;
27class GameListWorker; 28class GameListWorker;
28class GameListSearchField; 29class GameListSearchField;
29class GameListDir; 30class GameListDir;
@@ -88,6 +89,9 @@ public:
88 void SaveInterfaceLayout(); 89 void SaveInterfaceLayout();
89 void LoadInterfaceLayout(); 90 void LoadInterfaceLayout();
90 91
92 /// Disables events from the emulated controller
93 void UnloadController();
94
91 static const QStringList supported_file_extensions; 95 static const QStringList supported_file_extensions;
92 96
93signals: 97signals:
@@ -143,6 +147,7 @@ private:
143 QStandardItemModel* item_model = nullptr; 147 QStandardItemModel* item_model = nullptr;
144 GameListWorker* current_worker = nullptr; 148 GameListWorker* current_worker = nullptr;
145 QFileSystemWatcher* watcher = nullptr; 149 QFileSystemWatcher* watcher = nullptr;
150 ControllerNavigation* controller_navigation = nullptr;
146 CompatibilityList compatibility_list; 151 CompatibilityList compatibility_list;
147 152
148 friend class GameListSearchField; 153 friend class GameListSearchField;
diff --git a/src/yuzu/loading_screen.cpp b/src/yuzu/loading_screen.cpp
index ae842306c..b001b8c23 100644
--- a/src/yuzu/loading_screen.cpp
+++ b/src/yuzu/loading_screen.cpp
@@ -136,7 +136,7 @@ void LoadingScreen::OnLoadComplete() {
136void LoadingScreen::OnLoadProgress(VideoCore::LoadCallbackStage stage, std::size_t value, 136void LoadingScreen::OnLoadProgress(VideoCore::LoadCallbackStage stage, std::size_t value,
137 std::size_t total) { 137 std::size_t total) {
138 using namespace std::chrono; 138 using namespace std::chrono;
139 const auto now = high_resolution_clock::now(); 139 const auto now = steady_clock::now();
140 // reset the timer if the stage changes 140 // reset the timer if the stage changes
141 if (stage != previous_stage) { 141 if (stage != previous_stage) {
142 ui->progress_bar->setStyleSheet(QString::fromUtf8(progressbar_style[stage])); 142 ui->progress_bar->setStyleSheet(QString::fromUtf8(progressbar_style[stage]));
@@ -160,7 +160,7 @@ void LoadingScreen::OnLoadProgress(VideoCore::LoadCallbackStage stage, std::size
160 // If theres a drastic slowdown in the rate, then display an estimate 160 // If theres a drastic slowdown in the rate, then display an estimate
161 if (now - previous_time > milliseconds{50} || slow_shader_compile_start) { 161 if (now - previous_time > milliseconds{50} || slow_shader_compile_start) {
162 if (!slow_shader_compile_start) { 162 if (!slow_shader_compile_start) {
163 slow_shader_start = high_resolution_clock::now(); 163 slow_shader_start = steady_clock::now();
164 slow_shader_compile_start = true; 164 slow_shader_compile_start = true;
165 slow_shader_first_value = value; 165 slow_shader_first_value = value;
166 } 166 }
diff --git a/src/yuzu/loading_screen.h b/src/yuzu/loading_screen.h
index 801d08e1a..29155a77c 100644
--- a/src/yuzu/loading_screen.h
+++ b/src/yuzu/loading_screen.h
@@ -84,8 +84,8 @@ private:
84 // shaders, it will start quickly but end slow if new shaders were added since previous launch. 84 // shaders, it will start quickly but end slow if new shaders were added since previous launch.
85 // These variables are used to detect the change in speed so we can generate an ETA 85 // These variables are used to detect the change in speed so we can generate an ETA
86 bool slow_shader_compile_start = false; 86 bool slow_shader_compile_start = false;
87 std::chrono::high_resolution_clock::time_point slow_shader_start; 87 std::chrono::steady_clock::time_point slow_shader_start;
88 std::chrono::high_resolution_clock::time_point previous_time; 88 std::chrono::steady_clock::time_point previous_time;
89 std::size_t slow_shader_first_value = 0; 89 std::size_t slow_shader_first_value = 0;
90}; 90};
91 91
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index 6071a222f..b7bb43348 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -26,6 +26,8 @@
26#include "core/frontend/applets/controller.h" 26#include "core/frontend/applets/controller.h"
27#include "core/frontend/applets/general_frontend.h" 27#include "core/frontend/applets/general_frontend.h"
28#include "core/frontend/applets/software_keyboard.h" 28#include "core/frontend/applets/software_keyboard.h"
29#include "core/hid/emulated_controller.h"
30#include "core/hid/hid_core.h"
29#include "core/hle/service/acc/profile_manager.h" 31#include "core/hle/service/acc/profile_manager.h"
30#include "core/hle/service/am/applet_ae.h" 32#include "core/hle/service/am/applet_ae.h"
31#include "core/hle/service/am/applet_oe.h" 33#include "core/hle/service/am/applet_oe.h"
@@ -75,6 +77,7 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
75#include "common/fs/fs.h" 77#include "common/fs/fs.h"
76#include "common/fs/fs_paths.h" 78#include "common/fs/fs_paths.h"
77#include "common/fs/path_util.h" 79#include "common/fs/path_util.h"
80#include "common/literals.h"
78#include "common/logging/backend.h" 81#include "common/logging/backend.h"
79#include "common/logging/filter.h" 82#include "common/logging/filter.h"
80#include "common/logging/log.h" 83#include "common/logging/log.h"
@@ -106,8 +109,8 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
106#include "core/loader/loader.h" 109#include "core/loader/loader.h"
107#include "core/perf_stats.h" 110#include "core/perf_stats.h"
108#include "core/telemetry_session.h" 111#include "core/telemetry_session.h"
112#include "input_common/drivers/tas_input.h"
109#include "input_common/main.h" 113#include "input_common/main.h"
110#include "input_common/tas/tas_input.h"
111#include "ui_main.h" 114#include "ui_main.h"
112#include "util/overlay_dialog.h" 115#include "util/overlay_dialog.h"
113#include "video_core/gpu.h" 116#include "video_core/gpu.h"
@@ -132,6 +135,8 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
132#include "yuzu/main.h" 135#include "yuzu/main.h"
133#include "yuzu/uisettings.h" 136#include "yuzu/uisettings.h"
134 137
138using namespace Common::Literals;
139
135#ifdef USE_DISCORD_PRESENCE 140#ifdef USE_DISCORD_PRESENCE
136#include "yuzu/discord_impl.h" 141#include "yuzu/discord_impl.h"
137#endif 142#endif
@@ -227,6 +232,9 @@ GMainWindow::GMainWindow()
227 ConnectMenuEvents(); 232 ConnectMenuEvents();
228 ConnectWidgetEvents(); 233 ConnectWidgetEvents();
229 234
235 system->HIDCore().ReloadInputDevices();
236 controller_dialog->refreshConfiguration();
237
230 const auto branch_name = std::string(Common::g_scm_branch); 238 const auto branch_name = std::string(Common::g_scm_branch);
231 const auto description = std::string(Common::g_scm_desc); 239 const auto description = std::string(Common::g_scm_desc);
232 const auto build_id = std::string(Common::g_build_id); 240 const auto build_id = std::string(Common::g_build_id);
@@ -254,10 +262,9 @@ GMainWindow::GMainWindow()
254 LOG_INFO(Frontend, "Host CPU: {}", cpu_string); 262 LOG_INFO(Frontend, "Host CPU: {}", cpu_string);
255#endif 263#endif
256 LOG_INFO(Frontend, "Host OS: {}", QSysInfo::prettyProductName().toStdString()); 264 LOG_INFO(Frontend, "Host OS: {}", QSysInfo::prettyProductName().toStdString());
257 LOG_INFO(Frontend, "Host RAM: {:.2f} GB", 265 LOG_INFO(Frontend, "Host RAM: {:.2f} GiB",
258 Common::GetMemInfo().TotalPhysicalMemory / 1024.0f / 1024 / 1024); 266 Common::GetMemInfo().TotalPhysicalMemory / f64{1_GiB});
259 LOG_INFO(Frontend, "Host Swap: {:.2f} GB", 267 LOG_INFO(Frontend, "Host Swap: {:.2f} GiB", Common::GetMemInfo().TotalSwapMemory / f64{1_GiB});
260 Common::GetMemInfo().TotalSwapMemory / 1024.0f / 1024 / 1024);
261 UpdateWindowTitle(); 268 UpdateWindowTitle();
262 269
263 show(); 270 show();
@@ -444,7 +451,7 @@ void GMainWindow::ControllerSelectorReconfigureControllers(
444} 451}
445 452
446void GMainWindow::ProfileSelectorSelectProfile() { 453void GMainWindow::ProfileSelectorSelectProfile() {
447 QtProfileSelectionDialog dialog(this); 454 QtProfileSelectionDialog dialog(system->HIDCore(), this);
448 dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowStaysOnTopHint | 455 dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowStaysOnTopHint |
449 Qt::WindowTitleHint | Qt::WindowSystemMenuHint | 456 Qt::WindowTitleHint | Qt::WindowSystemMenuHint |
450 Qt::WindowCloseButtonHint); 457 Qt::WindowCloseButtonHint);
@@ -829,15 +836,16 @@ void GMainWindow::InitializeWidgets() {
829 dock_status_button->setFocusPolicy(Qt::NoFocus); 836 dock_status_button->setFocusPolicy(Qt::NoFocus);
830 connect(dock_status_button, &QPushButton::clicked, [&] { 837 connect(dock_status_button, &QPushButton::clicked, [&] {
831 const bool is_docked = Settings::values.use_docked_mode.GetValue(); 838 const bool is_docked = Settings::values.use_docked_mode.GetValue();
832 auto& controller_type = Settings::values.players.GetValue()[0].controller_type; 839 auto* player_1 = system->HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1);
840 auto* handheld = system->HIDCore().GetEmulatedController(Core::HID::NpadIdType::Handheld);
833 841
834 if (!is_docked && controller_type == Settings::ControllerType::Handheld) { 842 if (!is_docked && handheld->IsConnected()) {
835 QMessageBox::warning(this, tr("Invalid config detected"), 843 QMessageBox::warning(this, tr("Invalid config detected"),
836 tr("Handheld controller can't be used on docked mode. Pro " 844 tr("Handheld controller can't be used on docked mode. Pro "
837 "controller will be selected.")); 845 "controller will be selected."));
838 controller_type = Settings::ControllerType::ProController; 846 handheld->Disconnect();
839 ConfigureDialog configure_dialog(this, hotkey_registry, input_subsystem.get(), *system); 847 player_1->SetNpadStyleIndex(Core::HID::NpadStyleIndex::ProController);
840 configure_dialog.ApplyConfiguration(); 848 player_1->Connect();
841 controller_dialog->refreshConfiguration(); 849 controller_dialog->refreshConfiguration();
842 } 850 }
843 851
@@ -922,7 +930,7 @@ void GMainWindow::InitializeDebugWidgets() {
922 waitTreeWidget->hide(); 930 waitTreeWidget->hide();
923 debug_menu->addAction(waitTreeWidget->toggleViewAction()); 931 debug_menu->addAction(waitTreeWidget->toggleViewAction());
924 932
925 controller_dialog = new ControllerDialog(this, input_subsystem.get()); 933 controller_dialog = new ControllerDialog(system->HIDCore(), input_subsystem, this);
926 controller_dialog->hide(); 934 controller_dialog->hide();
927 debug_menu->addAction(controller_dialog->toggleViewAction()); 935 debug_menu->addAction(controller_dialog->toggleViewAction());
928 936
@@ -952,171 +960,80 @@ void GMainWindow::InitializeRecentFileMenuActions() {
952 UpdateRecentFiles(); 960 UpdateRecentFiles();
953} 961}
954 962
963void GMainWindow::LinkActionShortcut(QAction* action, const QString& action_name) {
964 static const QString main_window = QStringLiteral("Main Window");
965 action->setShortcut(hotkey_registry.GetKeySequence(main_window, action_name));
966 action->setShortcutContext(hotkey_registry.GetShortcutContext(main_window, action_name));
967
968 this->addAction(action);
969}
970
955void GMainWindow::InitializeHotkeys() { 971void GMainWindow::InitializeHotkeys() {
956 hotkey_registry.LoadHotkeys(); 972 hotkey_registry.LoadHotkeys();
957 973
958 const QString main_window = QStringLiteral("Main Window"); 974 LinkActionShortcut(ui->action_Load_File, QStringLiteral("Load File"));
959 const QString load_file = QStringLiteral("Load File"); 975 LinkActionShortcut(ui->action_Load_Amiibo, QStringLiteral("Load Amiibo"));
960 const QString load_amiibo = QStringLiteral("Load Amiibo"); 976 LinkActionShortcut(ui->action_Exit, QStringLiteral("Exit yuzu"));
961 const QString exit_yuzu = QStringLiteral("Exit yuzu"); 977 LinkActionShortcut(ui->action_Restart, QStringLiteral("Restart Emulation"));
962 const QString restart_emulation = QStringLiteral("Restart Emulation"); 978 LinkActionShortcut(ui->action_Pause, QStringLiteral("Continue/Pause Emulation"));
963 const QString stop_emulation = QStringLiteral("Stop Emulation"); 979 LinkActionShortcut(ui->action_Stop, QStringLiteral("Stop Emulation"));
964 const QString toggle_filter_bar = QStringLiteral("Toggle Filter Bar"); 980 LinkActionShortcut(ui->action_Show_Filter_Bar, QStringLiteral("Toggle Filter Bar"));
965 const QString toggle_status_bar = QStringLiteral("Toggle Status Bar"); 981 LinkActionShortcut(ui->action_Show_Status_Bar, QStringLiteral("Toggle Status Bar"));
966 const QString fullscreen = QStringLiteral("Fullscreen"); 982 LinkActionShortcut(ui->action_Fullscreen, QStringLiteral("Fullscreen"));
967 const QString capture_screenshot = QStringLiteral("Capture Screenshot"); 983 LinkActionShortcut(ui->action_Capture_Screenshot, QStringLiteral("Capture Screenshot"));
968 984 LinkActionShortcut(ui->action_TAS_Start, QStringLiteral("TAS Start/Stop"));
969 ui->action_Load_File->setShortcut(hotkey_registry.GetKeySequence(main_window, load_file)); 985 LinkActionShortcut(ui->action_TAS_Record, QStringLiteral("TAS Record"));
970 ui->action_Load_File->setShortcutContext( 986 LinkActionShortcut(ui->action_TAS_Reset, QStringLiteral("TAS Reset"));
971 hotkey_registry.GetShortcutContext(main_window, load_file)); 987
972 988 static const QString main_window = QStringLiteral("Main Window");
973 ui->action_Load_Amiibo->setShortcut(hotkey_registry.GetKeySequence(main_window, load_amiibo)); 989 const auto connect_shortcut = [&]<typename Fn>(const QString& action_name, const Fn& function) {
974 ui->action_Load_Amiibo->setShortcutContext( 990 const QShortcut* hotkey = hotkey_registry.GetHotkey(main_window, action_name, this);
975 hotkey_registry.GetShortcutContext(main_window, load_amiibo)); 991 connect(hotkey, &QShortcut::activated, this, function);
976 992 };
977 ui->action_Exit->setShortcut(hotkey_registry.GetKeySequence(main_window, exit_yuzu)); 993
978 ui->action_Exit->setShortcutContext(hotkey_registry.GetShortcutContext(main_window, exit_yuzu)); 994 connect_shortcut(QStringLiteral("Exit Fullscreen"), [&] {
979 995 if (emulation_running && ui->action_Fullscreen->isChecked()) {
980 ui->action_Restart->setShortcut(hotkey_registry.GetKeySequence(main_window, restart_emulation)); 996 ui->action_Fullscreen->setChecked(false);
981 ui->action_Restart->setShortcutContext( 997 ToggleFullscreen();
982 hotkey_registry.GetShortcutContext(main_window, restart_emulation)); 998 }
983 999 });
984 ui->action_Stop->setShortcut(hotkey_registry.GetKeySequence(main_window, stop_emulation)); 1000 connect_shortcut(QStringLiteral("Toggle Speed Limit"), [&] {
985 ui->action_Stop->setShortcutContext( 1001 Settings::values.use_speed_limit.SetValue(!Settings::values.use_speed_limit.GetValue());
986 hotkey_registry.GetShortcutContext(main_window, stop_emulation)); 1002 UpdateStatusBar();
987 1003 });
988 ui->action_Show_Filter_Bar->setShortcut(
989 hotkey_registry.GetKeySequence(main_window, toggle_filter_bar));
990 ui->action_Show_Filter_Bar->setShortcutContext(
991 hotkey_registry.GetShortcutContext(main_window, toggle_filter_bar));
992
993 ui->action_Show_Status_Bar->setShortcut(
994 hotkey_registry.GetKeySequence(main_window, toggle_status_bar));
995 ui->action_Show_Status_Bar->setShortcutContext(
996 hotkey_registry.GetShortcutContext(main_window, toggle_status_bar));
997
998 ui->action_Capture_Screenshot->setShortcut(
999 hotkey_registry.GetKeySequence(main_window, capture_screenshot));
1000 ui->action_Capture_Screenshot->setShortcutContext(
1001 hotkey_registry.GetShortcutContext(main_window, capture_screenshot));
1002
1003 ui->action_Fullscreen->setShortcut(
1004 hotkey_registry.GetHotkey(main_window, fullscreen, this)->key());
1005 ui->action_Fullscreen->setShortcutContext(
1006 hotkey_registry.GetShortcutContext(main_window, fullscreen));
1007
1008 connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Load File"), this),
1009 &QShortcut::activated, this, &GMainWindow::OnMenuLoadFile);
1010 connect(
1011 hotkey_registry.GetHotkey(main_window, QStringLiteral("Continue/Pause Emulation"), this),
1012 &QShortcut::activated, this, [&] {
1013 if (emulation_running) {
1014 if (emu_thread->IsRunning()) {
1015 OnPauseGame();
1016 } else {
1017 OnStartGame();
1018 }
1019 }
1020 });
1021 connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Restart Emulation"), this),
1022 &QShortcut::activated, this, [this] {
1023 if (!system->IsPoweredOn()) {
1024 return;
1025 }
1026 BootGame(game_path);
1027 });
1028 connect(hotkey_registry.GetHotkey(main_window, fullscreen, render_window),
1029 &QShortcut::activated, ui->action_Fullscreen, &QAction::trigger);
1030 connect(hotkey_registry.GetHotkey(main_window, fullscreen, render_window),
1031 &QShortcut::activatedAmbiguously, ui->action_Fullscreen, &QAction::trigger);
1032 connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Exit Fullscreen"), this),
1033 &QShortcut::activated, this, [&] {
1034 if (emulation_running && ui->action_Fullscreen->isChecked()) {
1035 ui->action_Fullscreen->setChecked(false);
1036 ToggleFullscreen();
1037 }
1038 });
1039 connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Toggle Speed Limit"), this),
1040 &QShortcut::activated, this, [&] {
1041 Settings::values.use_speed_limit.SetValue(
1042 !Settings::values.use_speed_limit.GetValue());
1043 UpdateStatusBar();
1044 });
1045 constexpr u16 SPEED_LIMIT_STEP = 5; 1004 constexpr u16 SPEED_LIMIT_STEP = 5;
1046 connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Increase Speed Limit"), this), 1005 connect_shortcut(QStringLiteral("Increase Speed Limit"), [&] {
1047 &QShortcut::activated, this, [&] { 1006 if (Settings::values.speed_limit.GetValue() < 9999 - SPEED_LIMIT_STEP) {
1048 if (Settings::values.speed_limit.GetValue() < 9999 - SPEED_LIMIT_STEP) { 1007 Settings::values.speed_limit.SetValue(SPEED_LIMIT_STEP +
1049 Settings::values.speed_limit.SetValue(SPEED_LIMIT_STEP + 1008 Settings::values.speed_limit.GetValue());
1050 Settings::values.speed_limit.GetValue()); 1009 UpdateStatusBar();
1051 UpdateStatusBar(); 1010 }
1052 } 1011 });
1053 }); 1012 connect_shortcut(QStringLiteral("Decrease Speed Limit"), [&] {
1054 connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Decrease Speed Limit"), this), 1013 if (Settings::values.speed_limit.GetValue() > SPEED_LIMIT_STEP) {
1055 &QShortcut::activated, this, [&] { 1014 Settings::values.speed_limit.SetValue(Settings::values.speed_limit.GetValue() -
1056 if (Settings::values.speed_limit.GetValue() > SPEED_LIMIT_STEP) { 1015 SPEED_LIMIT_STEP);
1057 Settings::values.speed_limit.SetValue(Settings::values.speed_limit.GetValue() - 1016 UpdateStatusBar();
1058 SPEED_LIMIT_STEP); 1017 }
1059 UpdateStatusBar(); 1018 });
1060 } 1019 connect_shortcut(QStringLiteral("Change Docked Mode"), [&] {
1061 }); 1020 Settings::values.use_docked_mode.SetValue(!Settings::values.use_docked_mode.GetValue());
1062 connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Load Amiibo"), this), 1021 OnDockedModeChanged(!Settings::values.use_docked_mode.GetValue(),
1063 &QShortcut::activated, this, [&] { 1022 Settings::values.use_docked_mode.GetValue(), *system);
1064 if (ui->action_Load_Amiibo->isEnabled()) { 1023 dock_status_button->setChecked(Settings::values.use_docked_mode.GetValue());
1065 OnLoadAmiibo(); 1024 });
1066 } 1025 connect_shortcut(QStringLiteral("Mute Audio"),
1067 }); 1026 [] { Settings::values.audio_muted = !Settings::values.audio_muted; });
1068 connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Capture Screenshot"), this), 1027 connect_shortcut(QStringLiteral("Toggle Framerate Limit"), [] {
1069 &QShortcut::activated, this, [&] { 1028 Settings::values.disable_fps_limit.SetValue(!Settings::values.disable_fps_limit.GetValue());
1070 if (emu_thread != nullptr && emu_thread->IsRunning()) { 1029 });
1071 OnCaptureScreenshot(); 1030 connect_shortcut(QStringLiteral("Toggle Mouse Panning"), [&] {
1072 } 1031 Settings::values.mouse_panning = !Settings::values.mouse_panning;
1073 }); 1032 if (Settings::values.mouse_panning) {
1074 connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Change Docked Mode"), this), 1033 render_window->installEventFilter(render_window);
1075 &QShortcut::activated, this, [&] { 1034 render_window->setAttribute(Qt::WA_Hover, true);
1076 Settings::values.use_docked_mode.SetValue( 1035 }
1077 !Settings::values.use_docked_mode.GetValue()); 1036 });
1078 OnDockedModeChanged(!Settings::values.use_docked_mode.GetValue(),
1079 Settings::values.use_docked_mode.GetValue(), *system);
1080 dock_status_button->setChecked(Settings::values.use_docked_mode.GetValue());
1081 });
1082 connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Mute Audio"), this),
1083 &QShortcut::activated, this,
1084 [] { Settings::values.audio_muted = !Settings::values.audio_muted; });
1085 connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Toggle Framerate Limit"), this),
1086 &QShortcut::activated, this, [] {
1087 Settings::values.disable_fps_limit.SetValue(
1088 !Settings::values.disable_fps_limit.GetValue());
1089 });
1090 connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Toggle Mouse Panning"), this),
1091 &QShortcut::activated, this, [&] {
1092 Settings::values.mouse_panning = !Settings::values.mouse_panning;
1093 if (Settings::values.mouse_panning) {
1094 render_window->installEventFilter(render_window);
1095 render_window->setAttribute(Qt::WA_Hover, true);
1096 }
1097 });
1098 connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("TAS Start/Stop"), this),
1099 &QShortcut::activated, this, [&] {
1100 if (!emulation_running) {
1101 return;
1102 }
1103 input_subsystem->GetTas()->StartStop();
1104 });
1105 connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("TAS Reset"), this),
1106 &QShortcut::activated, this, [&] { input_subsystem->GetTas()->Reset(); });
1107 connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("TAS Record"), this),
1108 &QShortcut::activated, this, [&] {
1109 if (!emulation_running) {
1110 return;
1111 }
1112 bool is_recording = input_subsystem->GetTas()->Record();
1113 if (!is_recording) {
1114 const auto res = QMessageBox::question(this, tr("TAS Recording"),
1115 tr("Overwrite file of player 1?"),
1116 QMessageBox::Yes | QMessageBox::No);
1117 input_subsystem->GetTas()->SaveRecording(res == QMessageBox::Yes);
1118 }
1119 });
1120} 1037}
1121 1038
1122void GMainWindow::SetDefaultUIGeometry() { 1039void GMainWindow::SetDefaultUIGeometry() {
@@ -1167,13 +1084,15 @@ void GMainWindow::OnAppFocusStateChanged(Qt::ApplicationState state) {
1167 state != Qt::ApplicationActive) { 1084 state != Qt::ApplicationActive) {
1168 LOG_DEBUG(Frontend, "ApplicationState unusual flag: {} ", state); 1085 LOG_DEBUG(Frontend, "ApplicationState unusual flag: {} ", state);
1169 } 1086 }
1170 if (ui->action_Pause->isEnabled() && 1087 if (emulation_running) {
1171 (state & (Qt::ApplicationHidden | Qt::ApplicationInactive))) { 1088 if (emu_thread->IsRunning() &&
1172 auto_paused = true; 1089 (state & (Qt::ApplicationHidden | Qt::ApplicationInactive))) {
1173 OnPauseGame(); 1090 auto_paused = true;
1174 } else if (ui->action_Start->isEnabled() && auto_paused && state == Qt::ApplicationActive) { 1091 OnPauseGame();
1175 auto_paused = false; 1092 } else if (!emu_thread->IsRunning() && auto_paused && state == Qt::ApplicationActive) {
1176 OnStartGame(); 1093 auto_paused = false;
1094 OnStartGame();
1095 }
1177 } 1096 }
1178} 1097}
1179 1098
@@ -1215,61 +1134,86 @@ void GMainWindow::ConnectWidgetEvents() {
1215} 1134}
1216 1135
1217void GMainWindow::ConnectMenuEvents() { 1136void GMainWindow::ConnectMenuEvents() {
1137 const auto connect_menu = [&]<typename Fn>(QAction* action, const Fn& event_fn) {
1138 connect(action, &QAction::triggered, this, event_fn);
1139 // Add actions to this window so that hiding menus in fullscreen won't disable them
1140 addAction(action);
1141 // Add actions to the render window so that they work outside of single window mode
1142 render_window->addAction(action);
1143 };
1144
1218 // File 1145 // File
1219 connect(ui->action_Load_File, &QAction::triggered, this, &GMainWindow::OnMenuLoadFile); 1146 connect_menu(ui->action_Load_File, &GMainWindow::OnMenuLoadFile);
1220 connect(ui->action_Load_Folder, &QAction::triggered, this, &GMainWindow::OnMenuLoadFolder); 1147 connect_menu(ui->action_Load_Folder, &GMainWindow::OnMenuLoadFolder);
1221 connect(ui->action_Install_File_NAND, &QAction::triggered, this, 1148 connect_menu(ui->action_Install_File_NAND, &GMainWindow::OnMenuInstallToNAND);
1222 &GMainWindow::OnMenuInstallToNAND); 1149 connect_menu(ui->action_Exit, &QMainWindow::close);
1223 connect(ui->action_Exit, &QAction::triggered, this, &QMainWindow::close); 1150 connect_menu(ui->action_Load_Amiibo, &GMainWindow::OnLoadAmiibo);
1224 connect(ui->action_Load_Amiibo, &QAction::triggered, this, &GMainWindow::OnLoadAmiibo);
1225 1151
1226 // Emulation 1152 // Emulation
1227 connect(ui->action_Start, &QAction::triggered, this, &GMainWindow::OnStartGame); 1153 connect_menu(ui->action_Pause, &GMainWindow::OnPauseContinueGame);
1228 connect(ui->action_Pause, &QAction::triggered, this, &GMainWindow::OnPauseGame); 1154 connect_menu(ui->action_Stop, &GMainWindow::OnStopGame);
1229 connect(ui->action_Stop, &QAction::triggered, this, &GMainWindow::OnStopGame); 1155 connect_menu(ui->action_Report_Compatibility, &GMainWindow::OnMenuReportCompatibility);
1230 connect(ui->action_Report_Compatibility, &QAction::triggered, this, 1156 connect_menu(ui->action_Open_Mods_Page, &GMainWindow::OnOpenModsPage);
1231 &GMainWindow::OnMenuReportCompatibility); 1157 connect_menu(ui->action_Open_Quickstart_Guide, &GMainWindow::OnOpenQuickstartGuide);
1232 connect(ui->action_Open_Mods_Page, &QAction::triggered, this, &GMainWindow::OnOpenModsPage); 1158 connect_menu(ui->action_Open_FAQ, &GMainWindow::OnOpenFAQ);
1233 connect(ui->action_Open_Quickstart_Guide, &QAction::triggered, this, 1159 connect_menu(ui->action_Restart, &GMainWindow::OnRestartGame);
1234 &GMainWindow::OnOpenQuickstartGuide); 1160 connect_menu(ui->action_Configure, &GMainWindow::OnConfigure);
1235 connect(ui->action_Open_FAQ, &QAction::triggered, this, &GMainWindow::OnOpenFAQ); 1161 connect_menu(ui->action_Configure_Current_Game, &GMainWindow::OnConfigurePerGame);
1236 connect(ui->action_Restart, &QAction::triggered, this,
1237 [this] { BootGame(QString(game_path)); });
1238 connect(ui->action_Configure, &QAction::triggered, this, &GMainWindow::OnConfigure);
1239 connect(ui->action_Configure_Tas, &QAction::triggered, this, &GMainWindow::OnConfigureTas);
1240 connect(ui->action_Configure_Current_Game, &QAction::triggered, this,
1241 &GMainWindow::OnConfigurePerGame);
1242 1162
1243 // View 1163 // View
1244 connect(ui->action_Single_Window_Mode, &QAction::triggered, this, 1164 connect_menu(ui->action_Fullscreen, &GMainWindow::ToggleFullscreen);
1245 &GMainWindow::ToggleWindowMode); 1165 connect_menu(ui->action_Single_Window_Mode, &GMainWindow::ToggleWindowMode);
1246 connect(ui->action_Display_Dock_Widget_Headers, &QAction::triggered, this, 1166 connect_menu(ui->action_Display_Dock_Widget_Headers, &GMainWindow::OnDisplayTitleBars);
1247 &GMainWindow::OnDisplayTitleBars); 1167 connect_menu(ui->action_Show_Filter_Bar, &GMainWindow::OnToggleFilterBar);
1248 connect(ui->action_Show_Filter_Bar, &QAction::triggered, this, &GMainWindow::OnToggleFilterBar); 1168
1249 connect(ui->action_Show_Status_Bar, &QAction::triggered, statusBar(), &QStatusBar::setVisible); 1169 connect(ui->action_Show_Status_Bar, &QAction::triggered, statusBar(), &QStatusBar::setVisible);
1250 1170
1251 connect(ui->action_Reset_Window_Size_720, &QAction::triggered, this, 1171 connect_menu(ui->action_Reset_Window_Size_720, &GMainWindow::ResetWindowSize720);
1252 &GMainWindow::ResetWindowSize720); 1172 connect_menu(ui->action_Reset_Window_Size_900, &GMainWindow::ResetWindowSize900);
1253 connect(ui->action_Reset_Window_Size_900, &QAction::triggered, this, 1173 connect_menu(ui->action_Reset_Window_Size_1080, &GMainWindow::ResetWindowSize1080);
1254 &GMainWindow::ResetWindowSize900); 1174 ui->menu_Reset_Window_Size->addActions({ui->action_Reset_Window_Size_720,
1255 connect(ui->action_Reset_Window_Size_1080, &QAction::triggered, this, 1175 ui->action_Reset_Window_Size_900,
1256 &GMainWindow::ResetWindowSize1080); 1176 ui->action_Reset_Window_Size_1080});
1257 ui->menu_Reset_Window_Size->addAction(ui->action_Reset_Window_Size_720);
1258 ui->menu_Reset_Window_Size->addAction(ui->action_Reset_Window_Size_900);
1259 ui->menu_Reset_Window_Size->addAction(ui->action_Reset_Window_Size_1080);
1260 1177
1261 // Fullscreen 1178 // Tools
1262 connect(ui->action_Fullscreen, &QAction::triggered, this, &GMainWindow::ToggleFullscreen); 1179 connect_menu(ui->action_Rederive, std::bind(&GMainWindow::OnReinitializeKeys, this,
1180 ReinitializeKeyBehavior::Warning));
1181 connect_menu(ui->action_Capture_Screenshot, &GMainWindow::OnCaptureScreenshot);
1263 1182
1264 // Movie 1183 // TAS
1265 connect(ui->action_Capture_Screenshot, &QAction::triggered, this, 1184 connect_menu(ui->action_TAS_Start, &GMainWindow::OnTasStartStop);
1266 &GMainWindow::OnCaptureScreenshot); 1185 connect_menu(ui->action_TAS_Record, &GMainWindow::OnTasRecord);
1186 connect_menu(ui->action_TAS_Reset, &GMainWindow::OnTasReset);
1187 connect_menu(ui->action_Configure_Tas, &GMainWindow::OnConfigureTas);
1267 1188
1268 // Help 1189 // Help
1269 connect(ui->action_Open_yuzu_Folder, &QAction::triggered, this, &GMainWindow::OnOpenYuzuFolder); 1190 connect_menu(ui->action_Open_yuzu_Folder, &GMainWindow::OnOpenYuzuFolder);
1270 connect(ui->action_Rederive, &QAction::triggered, this, 1191 connect_menu(ui->action_About, &GMainWindow::OnAbout);
1271 std::bind(&GMainWindow::OnReinitializeKeys, this, ReinitializeKeyBehavior::Warning)); 1192}
1272 connect(ui->action_About, &QAction::triggered, this, &GMainWindow::OnAbout); 1193
1194void GMainWindow::UpdateMenuState() {
1195 const bool is_paused = emu_thread == nullptr || !emu_thread->IsRunning();
1196
1197 const std::array running_actions{
1198 ui->action_Stop,
1199 ui->action_Restart,
1200 ui->action_Configure_Current_Game,
1201 ui->action_Report_Compatibility,
1202 ui->action_Load_Amiibo,
1203 ui->action_Pause,
1204 };
1205
1206 for (QAction* action : running_actions) {
1207 action->setEnabled(emulation_running);
1208 }
1209
1210 ui->action_Capture_Screenshot->setEnabled(emulation_running && !is_paused);
1211
1212 if (emulation_running && is_paused) {
1213 ui->action_Pause->setText(tr("&Continue"));
1214 } else {
1215 ui->action_Pause->setText(tr("&Pause"));
1216 }
1273} 1217}
1274 1218
1275void GMainWindow::OnDisplayTitleBars(bool show) { 1219void GMainWindow::OnDisplayTitleBars(bool show) {
@@ -1360,16 +1304,13 @@ bool GMainWindow::LoadROM(const QString& filename, u64 program_id, std::size_t p
1360 case Core::SystemResultStatus::ErrorVideoCore: 1304 case Core::SystemResultStatus::ErrorVideoCore:
1361 QMessageBox::critical( 1305 QMessageBox::critical(
1362 this, tr("An error occurred initializing the video core."), 1306 this, tr("An error occurred initializing the video core."),
1363 tr("yuzu has encountered an error while running the video core, please see the " 1307 tr("yuzu has encountered an error while running the video core. "
1364 "log for more details." 1308 "This is usually caused by outdated GPU drivers, including integrated ones. "
1309 "Please see the log for more details. "
1365 "For more information on accessing the log, please see the following page: " 1310 "For more information on accessing the log, please see the following page: "
1366 "<a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>How " 1311 "<a href='https://yuzu-emu.org/help/reference/log-files/'>"
1367 "to " 1312 "How to Upload the Log File</a>. "));
1368 "Upload the Log File</a>."
1369 "Ensure that you have the latest graphics drivers for your GPU."));
1370
1371 break; 1313 break;
1372
1373 default: 1314 default:
1374 if (result > Core::SystemResultStatus::ErrorLoader) { 1315 if (result > Core::SystemResultStatus::ErrorLoader) {
1375 const u16 loader_id = static_cast<u16>(Core::SystemResultStatus::ErrorLoader); 1316 const u16 loader_id = static_cast<u16>(Core::SystemResultStatus::ErrorLoader);
@@ -1405,7 +1346,7 @@ bool GMainWindow::LoadROM(const QString& filename, u64 program_id, std::size_t p
1405} 1346}
1406 1347
1407void GMainWindow::SelectAndSetCurrentUser() { 1348void GMainWindow::SelectAndSetCurrentUser() {
1408 QtProfileSelectionDialog dialog(this); 1349 QtProfileSelectionDialog dialog(system->HIDCore(), this);
1409 dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint | 1350 dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint |
1410 Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint); 1351 Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint);
1411 dialog.setWindowModality(Qt::WindowModal); 1352 dialog.setWindowModality(Qt::WindowModal);
@@ -1439,8 +1380,6 @@ void GMainWindow::BootGame(const QString& filename, u64 program_id, std::size_t
1439 Config per_game_config(*system, config_file_name, Config::ConfigType::PerGameConfig); 1380 Config per_game_config(*system, config_file_name, Config::ConfigType::PerGameConfig);
1440 } 1381 }
1441 1382
1442 ConfigureVibration::SetAllVibrationDevices();
1443
1444 // Disable fps limit toggle when booting a new title 1383 // Disable fps limit toggle when booting a new title
1445 Settings::values.disable_fps_limit.SetValue(false); 1384 Settings::values.disable_fps_limit.SetValue(false);
1446 1385
@@ -1562,15 +1501,8 @@ void GMainWindow::ShutdownGame() {
1562 disconnect(render_window, &GRenderWindow::Closed, this, &GMainWindow::OnStopGame); 1501 disconnect(render_window, &GRenderWindow::Closed, this, &GMainWindow::OnStopGame);
1563 1502
1564 // Update the GUI 1503 // Update the GUI
1565 ui->action_Start->setEnabled(false); 1504 UpdateMenuState();
1566 ui->action_Start->setText(tr("Start")); 1505
1567 ui->action_Pause->setEnabled(false);
1568 ui->action_Stop->setEnabled(false);
1569 ui->action_Restart->setEnabled(false);
1570 ui->action_Configure_Current_Game->setEnabled(false);
1571 ui->action_Report_Compatibility->setEnabled(false);
1572 ui->action_Load_Amiibo->setEnabled(false);
1573 ui->action_Capture_Screenshot->setEnabled(false);
1574 render_window->hide(); 1506 render_window->hide();
1575 loading_screen->hide(); 1507 loading_screen->hide();
1576 loading_screen->Clear(); 1508 loading_screen->Clear();
@@ -1582,6 +1514,10 @@ void GMainWindow::ShutdownGame() {
1582 game_list->SetFilterFocus(); 1514 game_list->SetFilterFocus();
1583 tas_label->clear(); 1515 tas_label->clear();
1584 input_subsystem->GetTas()->Stop(); 1516 input_subsystem->GetTas()->Stop();
1517 OnTasStateChanged();
1518
1519 // Enable all controllers
1520 system->HIDCore().SetSupportedStyleTag({Core::HID::NpadStyleSet::All});
1585 1521
1586 render_window->removeEventFilter(render_window); 1522 render_window->removeEventFilter(render_window);
1587 render_window->setAttribute(Qt::WA_Hover, false); 1523 render_window->setAttribute(Qt::WA_Hover, false);
@@ -1675,7 +1611,7 @@ void GMainWindow::OnGameListOpenFolder(u64 program_id, GameListOpenTarget target
1675 if (has_user_save) { 1611 if (has_user_save) {
1676 // User save data 1612 // User save data
1677 const auto select_profile = [this] { 1613 const auto select_profile = [this] {
1678 QtProfileSelectionDialog dialog(this); 1614 QtProfileSelectionDialog dialog(system->HIDCore(), this);
1679 dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint | 1615 dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint |
1680 Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint); 1616 Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint);
1681 dialog.setWindowModality(Qt::WindowModal); 1617 dialog.setWindowModality(Qt::WindowModal);
@@ -2501,31 +2437,36 @@ void GMainWindow::OnStartGame() {
2501 2437
2502 connect(emu_thread.get(), &EmuThread::ErrorThrown, this, &GMainWindow::OnCoreError); 2438 connect(emu_thread.get(), &EmuThread::ErrorThrown, this, &GMainWindow::OnCoreError);
2503 2439
2504 ui->action_Start->setEnabled(false); 2440 UpdateMenuState();
2505 ui->action_Start->setText(tr("&Continue")); 2441 OnTasStateChanged();
2506
2507 ui->action_Pause->setEnabled(true);
2508 ui->action_Stop->setEnabled(true);
2509 ui->action_Restart->setEnabled(true);
2510 ui->action_Configure_Current_Game->setEnabled(true);
2511 ui->action_Report_Compatibility->setEnabled(true);
2512 2442
2513 discord_rpc->Update(); 2443 discord_rpc->Update();
2514 ui->action_Load_Amiibo->setEnabled(true); 2444}
2515 ui->action_Capture_Screenshot->setEnabled(true); 2445
2446void GMainWindow::OnRestartGame() {
2447 if (!system->IsPoweredOn()) {
2448 return;
2449 }
2450 // Make a copy since BootGame edits game_path
2451 BootGame(QString(game_path));
2516} 2452}
2517 2453
2518void GMainWindow::OnPauseGame() { 2454void GMainWindow::OnPauseGame() {
2519 emu_thread->SetRunning(false); 2455 emu_thread->SetRunning(false);
2520 2456 UpdateMenuState();
2521 ui->action_Start->setEnabled(true);
2522 ui->action_Pause->setEnabled(false);
2523 ui->action_Stop->setEnabled(true);
2524 ui->action_Capture_Screenshot->setEnabled(false);
2525
2526 AllowOSSleep(); 2457 AllowOSSleep();
2527} 2458}
2528 2459
2460void GMainWindow::OnPauseContinueGame() {
2461 if (emulation_running) {
2462 if (emu_thread->IsRunning()) {
2463 OnPauseGame();
2464 } else {
2465 OnStartGame();
2466 }
2467 }
2468}
2469
2529void GMainWindow::OnStopGame() { 2470void GMainWindow::OnStopGame() {
2530 if (system->GetExitLock() && !ConfirmForceLockedExit()) { 2471 if (system->GetExitLock() && !ConfirmForceLockedExit()) {
2531 return; 2472 return;
@@ -2774,7 +2715,6 @@ void GMainWindow::OnConfigure() {
2774 2715
2775 ShowTelemetryCallout(); 2716 ShowTelemetryCallout();
2776 } 2717 }
2777 controller_dialog->refreshConfiguration();
2778 InitializeHotkeys(); 2718 InitializeHotkeys();
2779 2719
2780 if (UISettings::values.theme != old_theme) { 2720 if (UISettings::values.theme != old_theme) {
@@ -2807,6 +2747,7 @@ void GMainWindow::OnConfigure() {
2807 } 2747 }
2808 2748
2809 UpdateStatusButtons(); 2749 UpdateStatusButtons();
2750 controller_dialog->refreshConfiguration();
2810} 2751}
2811 2752
2812void GMainWindow::OnConfigureTas() { 2753void GMainWindow::OnConfigureTas() {
@@ -2821,6 +2762,32 @@ void GMainWindow::OnConfigureTas() {
2821 } 2762 }
2822} 2763}
2823 2764
2765void GMainWindow::OnTasStartStop() {
2766 if (!emulation_running) {
2767 return;
2768 }
2769 input_subsystem->GetTas()->StartStop();
2770 OnTasStateChanged();
2771}
2772
2773void GMainWindow::OnTasRecord() {
2774 if (!emulation_running) {
2775 return;
2776 }
2777 const bool is_recording = input_subsystem->GetTas()->Record();
2778 if (!is_recording) {
2779 const auto res =
2780 QMessageBox::question(this, tr("TAS Recording"), tr("Overwrite file of player 1?"),
2781 QMessageBox::Yes | QMessageBox::No);
2782 input_subsystem->GetTas()->SaveRecording(res == QMessageBox::Yes);
2783 }
2784 OnTasStateChanged();
2785}
2786
2787void GMainWindow::OnTasReset() {
2788 input_subsystem->GetTas()->Reset();
2789}
2790
2824void GMainWindow::OnConfigurePerGame() { 2791void GMainWindow::OnConfigurePerGame() {
2825 const u64 title_id = system->GetCurrentProcessProgramID(); 2792 const u64 title_id = system->GetCurrentProcessProgramID();
2826 OpenPerGameConfiguration(title_id, game_path.toStdString()); 2793 OpenPerGameConfiguration(title_id, game_path.toStdString());
@@ -2858,6 +2825,10 @@ void GMainWindow::OpenPerGameConfiguration(u64 title_id, const std::string& file
2858} 2825}
2859 2826
2860void GMainWindow::OnLoadAmiibo() { 2827void GMainWindow::OnLoadAmiibo() {
2828 if (emu_thread == nullptr || !emu_thread->IsRunning()) {
2829 return;
2830 }
2831
2861 const QString extensions{QStringLiteral("*.bin")}; 2832 const QString extensions{QStringLiteral("*.bin")};
2862 const QString file_filter = tr("Amiibo File (%1);; All Files (*.*)").arg(extensions); 2833 const QString file_filter = tr("Amiibo File (%1);; All Files (*.*)").arg(extensions);
2863 const QString filename = QFileDialog::getOpenFileName(this, tr("Load Amiibo"), {}, file_filter); 2834 const QString filename = QFileDialog::getOpenFileName(this, tr("Load Amiibo"), {}, file_filter);
@@ -2921,6 +2892,10 @@ void GMainWindow::OnToggleFilterBar() {
2921} 2892}
2922 2893
2923void GMainWindow::OnCaptureScreenshot() { 2894void GMainWindow::OnCaptureScreenshot() {
2895 if (emu_thread == nullptr || !emu_thread->IsRunning()) {
2896 return;
2897 }
2898
2924 const u64 title_id = system->GetCurrentProcessProgramID(); 2899 const u64 title_id = system->GetCurrentProcessProgramID();
2925 const auto screenshot_path = 2900 const auto screenshot_path =
2926 QString::fromStdString(Common::FS::GetYuzuPathString(Common::FS::YuzuPath::ScreenshotsDir)); 2901 QString::fromStdString(Common::FS::GetYuzuPathString(Common::FS::YuzuPath::ScreenshotsDir));
@@ -3003,17 +2978,35 @@ void GMainWindow::UpdateWindowTitle(std::string_view title_name, std::string_vie
3003QString GMainWindow::GetTasStateDescription() const { 2978QString GMainWindow::GetTasStateDescription() const {
3004 auto [tas_status, current_tas_frame, total_tas_frames] = input_subsystem->GetTas()->GetStatus(); 2979 auto [tas_status, current_tas_frame, total_tas_frames] = input_subsystem->GetTas()->GetStatus();
3005 switch (tas_status) { 2980 switch (tas_status) {
3006 case TasInput::TasState::Running: 2981 case InputCommon::TasInput::TasState::Running:
3007 return tr("TAS state: Running %1/%2").arg(current_tas_frame).arg(total_tas_frames); 2982 return tr("TAS state: Running %1/%2").arg(current_tas_frame).arg(total_tas_frames);
3008 case TasInput::TasState::Recording: 2983 case InputCommon::TasInput::TasState::Recording:
3009 return tr("TAS state: Recording %1").arg(total_tas_frames); 2984 return tr("TAS state: Recording %1").arg(total_tas_frames);
3010 case TasInput::TasState::Stopped: 2985 case InputCommon::TasInput::TasState::Stopped:
3011 return tr("TAS state: Idle %1/%2").arg(current_tas_frame).arg(total_tas_frames); 2986 return tr("TAS state: Idle %1/%2").arg(current_tas_frame).arg(total_tas_frames);
3012 default: 2987 default:
3013 return tr("TAS State: Invalid"); 2988 return tr("TAS State: Invalid");
3014 } 2989 }
3015} 2990}
3016 2991
2992void GMainWindow::OnTasStateChanged() {
2993 bool is_running = false;
2994 bool is_recording = false;
2995 if (emulation_running) {
2996 const InputCommon::TasInput::TasState tas_status =
2997 std::get<0>(input_subsystem->GetTas()->GetStatus());
2998 is_running = tas_status == InputCommon::TasInput::TasState::Running;
2999 is_recording = tas_status == InputCommon::TasInput::TasState::Recording;
3000 }
3001
3002 ui->action_TAS_Start->setText(is_running ? tr("&Stop Running") : tr("&Start"));
3003 ui->action_TAS_Record->setText(is_recording ? tr("Stop R&ecording") : tr("R&ecord"));
3004
3005 ui->action_TAS_Start->setEnabled(emulation_running);
3006 ui->action_TAS_Record->setEnabled(emulation_running);
3007 ui->action_TAS_Reset->setEnabled(emulation_running);
3008}
3009
3017void GMainWindow::UpdateStatusBar() { 3010void GMainWindow::UpdateStatusBar() {
3018 if (emu_thread == nullptr) { 3011 if (emu_thread == nullptr) {
3019 status_bar_update_timer.stop(); 3012 status_bar_update_timer.stop();
@@ -3106,7 +3099,7 @@ void GMainWindow::UpdateFilterText() {
3106 filter_status_button->setText(tr("SCALEFORCE")); 3099 filter_status_button->setText(tr("SCALEFORCE"));
3107 break; 3100 break;
3108 case Settings::ScalingFilter::Fsr: 3101 case Settings::ScalingFilter::Fsr:
3109 filter_status_button->setText(tr("AMD'S FIDELITYFX SR")); 3102 filter_status_button->setText(tr("FSR"));
3110 break; 3103 break;
3111 default: 3104 default:
3112 filter_status_button->setText(tr("BILINEAR")); 3105 filter_status_button->setText(tr("BILINEAR"));
@@ -3117,15 +3110,15 @@ void GMainWindow::UpdateFilterText() {
3117void GMainWindow::UpdateAAText() { 3110void GMainWindow::UpdateAAText() {
3118 const auto aa_mode = Settings::values.anti_aliasing.GetValue(); 3111 const auto aa_mode = Settings::values.anti_aliasing.GetValue();
3119 switch (aa_mode) { 3112 switch (aa_mode) {
3120 case Settings::AntiAliasing::Fxaa:
3121 aa_status_button->setText(tr("FXAA"));
3122 break;
3123 case Settings::AntiAliasing::None: 3113 case Settings::AntiAliasing::None:
3124 aa_status_button->setText(tr("NO AA")); 3114 aa_status_button->setText(tr("NO AA"));
3125 break; 3115 break;
3126 default: 3116 case Settings::AntiAliasing::Fxaa:
3127 aa_status_button->setText(tr("FXAA")); 3117 aa_status_button->setText(tr("FXAA"));
3128 break; 3118 break;
3119 default:
3120 aa_status_button->setText(tr("NO AA"));
3121 break;
3129 } 3122 }
3130} 3123}
3131 3124
@@ -3300,9 +3293,9 @@ void GMainWindow::OnReinitializeKeys(ReinitializeKeyBehavior behavior) {
3300 if (!errors.isEmpty()) { 3293 if (!errors.isEmpty()) {
3301 QMessageBox::warning( 3294 QMessageBox::warning(
3302 this, tr("Derivation Components Missing"), 3295 this, tr("Derivation Components Missing"),
3303 tr("Components are missing that may hinder key derivation from completing. " 3296 tr("Encryption keys are missing. "
3304 "<br>Please follow <a href='https://yuzu-emu.org/help/quickstart/'>the yuzu " 3297 "<br>Please follow <a href='https://yuzu-emu.org/help/quickstart/'>the yuzu "
3305 "quickstart guide</a> to get all your keys and " 3298 "quickstart guide</a> to get all your keys, firmware and "
3306 "games.<br><br><small>(%1)</small>") 3299 "games.<br><br><small>(%1)</small>")
3307 .arg(errors)); 3300 .arg(errors));
3308 } 3301 }
@@ -3387,6 +3380,11 @@ void GMainWindow::closeEvent(QCloseEvent* event) {
3387 game_list->SaveInterfaceLayout(); 3380 game_list->SaveInterfaceLayout();
3388 hotkey_registry.SaveHotkeys(); 3381 hotkey_registry.SaveHotkeys();
3389 3382
3383 // Unload controllers early
3384 controller_dialog->UnloadController();
3385 game_list->UnloadController();
3386 system->HIDCore().UnloadInputDevices();
3387
3390 // Shutdown session if the emu thread is active... 3388 // Shutdown session if the emu thread is active...
3391 if (emu_thread != nullptr) { 3389 if (emu_thread != nullptr) {
3392 ShutdownGame(); 3390 ShutdownGame();
@@ -3487,36 +3485,38 @@ void GMainWindow::filterBarSetChecked(bool state) {
3487} 3485}
3488 3486
3489void GMainWindow::UpdateUITheme() { 3487void GMainWindow::UpdateUITheme() {
3490 const QString default_icons = QStringLiteral("default"); 3488 const QString default_theme = QStringLiteral("default");
3491 const QString& current_theme = UISettings::values.theme; 3489 QString current_theme = UISettings::values.theme;
3492 const bool is_default_theme = current_theme == QString::fromUtf8(UISettings::themes[0].second);
3493 QStringList theme_paths(default_theme_paths); 3490 QStringList theme_paths(default_theme_paths);
3494 3491
3495 if (is_default_theme || current_theme.isEmpty()) { 3492 if (current_theme.isEmpty()) {
3496 const QString theme_uri(QStringLiteral(":default/style.qss")); 3493 current_theme = default_theme;
3494 }
3495
3496 if (current_theme != default_theme) {
3497 QString theme_uri{QStringLiteral(":%1/style.qss").arg(current_theme)};
3497 QFile f(theme_uri); 3498 QFile f(theme_uri);
3498 if (f.open(QFile::ReadOnly | QFile::Text)) { 3499 if (!f.open(QFile::ReadOnly | QFile::Text)) {
3499 QTextStream ts(&f); 3500 LOG_ERROR(Frontend, "Unable to open style \"{}\", fallback to the default theme",
3500 qApp->setStyleSheet(ts.readAll()); 3501 UISettings::values.theme.toStdString());
3501 setStyleSheet(ts.readAll()); 3502 current_theme = default_theme;
3502 } else {
3503 qApp->setStyleSheet({});
3504 setStyleSheet({});
3505 } 3503 }
3506 QIcon::setThemeName(default_icons); 3504 }
3505
3506 QString theme_uri{QStringLiteral(":%1/style.qss").arg(current_theme)};
3507 QFile f(theme_uri);
3508 if (f.open(QFile::ReadOnly | QFile::Text)) {
3509 QTextStream ts(&f);
3510 qApp->setStyleSheet(ts.readAll());
3511 setStyleSheet(ts.readAll());
3507 } else { 3512 } else {
3508 const QString theme_uri(QLatin1Char{':'} + current_theme + QStringLiteral("/style.qss")); 3513 LOG_ERROR(Frontend, "Unable to set style \"{}\", stylesheet file not found",
3509 QFile f(theme_uri); 3514 UISettings::values.theme.toStdString());
3510 if (f.open(QFile::ReadOnly | QFile::Text)) { 3515 qApp->setStyleSheet({});
3511 QTextStream ts(&f); 3516 setStyleSheet({});
3512 qApp->setStyleSheet(ts.readAll());
3513 setStyleSheet(ts.readAll());
3514 } else {
3515 LOG_ERROR(Frontend, "Unable to set style, stylesheet file not found");
3516 }
3517 QIcon::setThemeName(current_theme);
3518 } 3517 }
3519 3518
3519 QIcon::setThemeName(current_theme);
3520 QIcon::setThemeSearchPaths(theme_paths); 3520 QIcon::setThemeSearchPaths(theme_paths);
3521} 3521}
3522 3522
@@ -3552,9 +3552,6 @@ void GMainWindow::OnLanguageChanged(const QString& locale) {
3552 LoadTranslation(); 3552 LoadTranslation();
3553 ui->retranslateUi(this); 3553 ui->retranslateUi(this);
3554 UpdateWindowTitle(); 3554 UpdateWindowTitle();
3555
3556 if (emulation_running)
3557 ui->action_Start->setText(tr("&Continue"));
3558} 3555}
3559 3556
3560void GMainWindow::SetDiscordEnabled([[maybe_unused]] bool state) { 3557void GMainWindow::SetDiscordEnabled([[maybe_unused]] bool state) {
diff --git a/src/yuzu/main.h b/src/yuzu/main.h
index 24633ff2d..0fd41ed4f 100644
--- a/src/yuzu/main.h
+++ b/src/yuzu/main.h
@@ -177,6 +177,7 @@ public slots:
177 void WebBrowserOpenWebPage(const std::string& main_url, const std::string& additional_args, 177 void WebBrowserOpenWebPage(const std::string& main_url, const std::string& additional_args,
178 bool is_local); 178 bool is_local);
179 void OnAppFocusStateChanged(Qt::ApplicationState state); 179 void OnAppFocusStateChanged(Qt::ApplicationState state);
180 void OnTasStateChanged();
180 181
181private: 182private:
182 void RegisterMetaTypes(); 183 void RegisterMetaTypes();
@@ -190,6 +191,7 @@ private:
190 191
191 void ConnectWidgetEvents(); 192 void ConnectWidgetEvents();
192 void ConnectMenuEvents(); 193 void ConnectMenuEvents();
194 void UpdateMenuState();
193 195
194 void PreventOSSleep(); 196 void PreventOSSleep();
195 void AllowOSSleep(); 197 void AllowOSSleep();
@@ -239,7 +241,9 @@ private:
239 241
240private slots: 242private slots:
241 void OnStartGame(); 243 void OnStartGame();
244 void OnRestartGame();
242 void OnPauseGame(); 245 void OnPauseGame();
246 void OnPauseContinueGame();
243 void OnStopGame(); 247 void OnStopGame();
244 void OnMenuReportCompatibility(); 248 void OnMenuReportCompatibility();
245 void OnOpenModsPage(); 249 void OnOpenModsPage();
@@ -268,6 +272,9 @@ private slots:
268 void OnMenuRecentFile(); 272 void OnMenuRecentFile();
269 void OnConfigure(); 273 void OnConfigure();
270 void OnConfigureTas(); 274 void OnConfigureTas();
275 void OnTasStartStop();
276 void OnTasRecord();
277 void OnTasReset();
271 void OnConfigurePerGame(); 278 void OnConfigurePerGame();
272 void OnLoadAmiibo(); 279 void OnLoadAmiibo();
273 void OnOpenYuzuFolder(); 280 void OnOpenYuzuFolder();
@@ -290,6 +297,9 @@ private slots:
290 void OnMouseActivity(); 297 void OnMouseActivity();
291 298
292private: 299private:
300 /// Updates an action's shortcut and text to reflect an updated hotkey from the hotkey registry.
301 void LinkActionShortcut(QAction* action, const QString& action_name);
302
293 void RemoveBaseContent(u64 program_id, const QString& entry_type); 303 void RemoveBaseContent(u64 program_id, const QString& entry_type);
294 void RemoveUpdateContent(u64 program_id, const QString& entry_type); 304 void RemoveUpdateContent(u64 program_id, const QString& entry_type);
295 void RemoveAddOnContent(u64 program_id, const QString& entry_type); 305 void RemoveAddOnContent(u64 program_id, const QString& entry_type);
@@ -313,6 +323,7 @@ private:
313 void OpenURL(const QUrl& url); 323 void OpenURL(const QUrl& url);
314 void LoadTranslation(); 324 void LoadTranslation();
315 void OpenPerGameConfiguration(u64 title_id, const std::string& file_name); 325 void OpenPerGameConfiguration(u64 title_id, const std::string& file_name);
326
316 QString GetTasStateDescription() const; 327 QString GetTasStateDescription() const;
317 328
318 std::unique_ptr<Ui::MainWindow> ui; 329 std::unique_ptr<Ui::MainWindow> ui;
diff --git a/src/yuzu/main.ui b/src/yuzu/main.ui
index a62e39a06..5719b2ee4 100644
--- a/src/yuzu/main.ui
+++ b/src/yuzu/main.ui
@@ -66,7 +66,6 @@
66 <property name="title"> 66 <property name="title">
67 <string>&amp;Emulation</string> 67 <string>&amp;Emulation</string>
68 </property> 68 </property>
69 <addaction name="action_Start"/>
70 <addaction name="action_Pause"/> 69 <addaction name="action_Pause"/>
71 <addaction name="action_Stop"/> 70 <addaction name="action_Stop"/>
72 <addaction name="action_Restart"/> 71 <addaction name="action_Restart"/>
@@ -79,39 +78,39 @@
79 <string>&amp;View</string> 78 <string>&amp;View</string>
80 </property> 79 </property>
81 <widget class="QMenu" name="menu_Reset_Window_Size"> 80 <widget class="QMenu" name="menu_Reset_Window_Size">
82 <property name="title"> 81 <property name="title">
83 <string>&amp;Reset Window Size</string> 82 <string>&amp;Reset Window Size</string>
84 </property> 83 </property>
84 </widget>
85 <widget class="QMenu" name="menu_View_Debugging">
86 <property name="title">
87 <string>&amp;Debugging</string>
88 </property>
85 </widget> 89 </widget>
86 <action name="action_Reset_Window_Size_720"> 90 <action name="action_Reset_Window_Size_720">
87 <property name="text"> 91 <property name="text">
88 <string>Reset Window Size to &amp;720p</string> 92 <string>Reset Window Size to &amp;720p</string>
89 </property> 93 </property>
90 <property name="iconText"> 94 <property name="iconText">
91 <string>Reset Window Size to 720p</string> 95 <string>Reset Window Size to 720p</string>
92 </property> 96 </property>
93 </action> 97 </action>
94 <action name="action_Reset_Window_Size_900"> 98 <action name="action_Reset_Window_Size_900">
95 <property name="text"> 99 <property name="text">
96 <string>Reset Window Size to &amp;900p</string> 100 <string>Reset Window Size to &amp;900p</string>
97 </property> 101 </property>
98 <property name="iconText"> 102 <property name="iconText">
99 <string>Reset Window Size to 900p</string> 103 <string>Reset Window Size to 900p</string>
100 </property> 104 </property>
101 </action> 105 </action>
102 <action name="action_Reset_Window_Size_1080"> 106 <action name="action_Reset_Window_Size_1080">
103 <property name="text"> 107 <property name="text">
104 <string>Reset Window Size to &amp;1080p</string> 108 <string>Reset Window Size to &amp;1080p</string>
105 </property>
106 <property name="iconText">
107 <string>Reset Window Size to 1080p</string>
108 </property>
109 </action>
110 <widget class="QMenu" name="menu_View_Debugging">
111 <property name="title">
112 <string>&amp;Debugging</string>
113 </property> 109 </property>
114 </widget> 110 <property name="iconText">
111 <string>Reset Window Size to 1080p</string>
112 </property>
113 </action>
115 <addaction name="action_Fullscreen"/> 114 <addaction name="action_Fullscreen"/>
116 <addaction name="action_Single_Window_Mode"/> 115 <addaction name="action_Single_Window_Mode"/>
117 <addaction name="action_Display_Dock_Widget_Headers"/> 116 <addaction name="action_Display_Dock_Widget_Headers"/>
@@ -125,10 +124,20 @@
125 <property name="title"> 124 <property name="title">
126 <string>&amp;Tools</string> 125 <string>&amp;Tools</string>
127 </property> 126 </property>
127 <widget class="QMenu" name="menuTAS">
128 <property name="title">
129 <string>&amp;TAS</string>
130 </property>
131 <addaction name="action_TAS_Start"/>
132 <addaction name="action_TAS_Record"/>
133 <addaction name="action_TAS_Reset"/>
134 <addaction name="separator"/>
135 <addaction name="action_Configure_Tas"/>
136 </widget>
128 <addaction name="action_Rederive"/> 137 <addaction name="action_Rederive"/>
129 <addaction name="separator"/> 138 <addaction name="separator"/>
130 <addaction name="action_Capture_Screenshot"/> 139 <addaction name="action_Capture_Screenshot"/>
131 <addaction name="action_Configure_Tas"/> 140 <addaction name="menuTAS"/>
132 </widget> 141 </widget>
133 <widget class="QMenu" name="menu_Help"> 142 <widget class="QMenu" name="menu_Help">
134 <property name="title"> 143 <property name="title">
@@ -170,14 +179,6 @@
170 <string>E&amp;xit</string> 179 <string>E&amp;xit</string>
171 </property> 180 </property>
172 </action> 181 </action>
173 <action name="action_Start">
174 <property name="enabled">
175 <bool>false</bool>
176 </property>
177 <property name="text">
178 <string>&amp;Start</string>
179 </property>
180 </action>
181 <action name="action_Pause"> 182 <action name="action_Pause">
182 <property name="enabled"> 183 <property name="enabled">
183 <bool>false</bool> 184 <bool>false</bool>
@@ -309,7 +310,7 @@
309 </action> 310 </action>
310 <action name="action_Configure_Tas"> 311 <action name="action_Configure_Tas">
311 <property name="text"> 312 <property name="text">
312 <string>Configure &amp;TAS...</string> 313 <string>&amp;Configure TAS...</string>
313 </property> 314 </property>
314 </action> 315 </action>
315 <action name="action_Configure_Current_Game"> 316 <action name="action_Configure_Current_Game">
@@ -320,6 +321,30 @@
320 <string>Configure C&amp;urrent Game...</string> 321 <string>Configure C&amp;urrent Game...</string>
321 </property> 322 </property>
322 </action> 323 </action>
324 <action name="action_TAS_Start">
325 <property name="enabled">
326 <bool>false</bool>
327 </property>
328 <property name="text">
329 <string>&amp;Start</string>
330 </property>
331 </action>
332 <action name="action_TAS_Reset">
333 <property name="enabled">
334 <bool>false</bool>
335 </property>
336 <property name="text">
337 <string>&amp;Reset</string>
338 </property>
339 </action>
340 <action name="action_TAS_Record">
341 <property name="enabled">
342 <bool>false</bool>
343 </property>
344 <property name="text">
345 <string>R&amp;ecord</string>
346 </property>
347 </action>
323 </widget> 348 </widget>
324 <resources> 349 <resources>
325 <include location="yuzu.qrc"/> 350 <include location="yuzu.qrc"/>
diff --git a/src/yuzu/uisettings.cpp b/src/yuzu/uisettings.cpp
index 37499fc85..21683576c 100644
--- a/src/yuzu/uisettings.cpp
+++ b/src/yuzu/uisettings.cpp
@@ -7,8 +7,8 @@
7namespace UISettings { 7namespace UISettings {
8 8
9const Themes themes{{ 9const Themes themes{{
10 {"Light", "default"}, 10 {"Default", "default"},
11 {"Light Colorful", "colorful"}, 11 {"Default Colorful", "colorful"},
12 {"Dark", "qdarkstyle"}, 12 {"Dark", "qdarkstyle"},
13 {"Dark Colorful", "colorful_dark"}, 13 {"Dark Colorful", "colorful_dark"},
14 {"Midnight Blue", "qdarkstyle_midnight_blue"}, 14 {"Midnight Blue", "qdarkstyle_midnight_blue"},
diff --git a/src/yuzu/uisettings.h b/src/yuzu/uisettings.h
index 936914ef3..a610e7e25 100644
--- a/src/yuzu/uisettings.h
+++ b/src/yuzu/uisettings.h
@@ -74,7 +74,6 @@ struct Values {
74 QString game_dir_deprecated; 74 QString game_dir_deprecated;
75 bool game_dir_deprecated_deepscan; 75 bool game_dir_deprecated_deepscan;
76 QVector<UISettings::GameDir> game_dirs; 76 QVector<UISettings::GameDir> game_dirs;
77 QVector<u64> favorited_ids;
78 QStringList recent_files; 77 QStringList recent_files;
79 QString language; 78 QString language;
80 79
@@ -96,6 +95,8 @@ struct Values {
96 Settings::BasicSetting<uint8_t> row_2_text_id{2, "row_2_text_id"}; 95 Settings::BasicSetting<uint8_t> row_2_text_id{2, "row_2_text_id"};
97 std::atomic_bool is_game_list_reload_pending{false}; 96 std::atomic_bool is_game_list_reload_pending{false};
98 Settings::BasicSetting<bool> cache_game_list{true, "cache_game_list"}; 97 Settings::BasicSetting<bool> cache_game_list{true, "cache_game_list"};
98 Settings::BasicSetting<bool> favorites_expanded{true, "favorites_expanded"};
99 QVector<u64> favorited_ids;
99 100
100 bool configuration_applied; 101 bool configuration_applied;
101 bool reset_to_defaults; 102 bool reset_to_defaults;
diff --git a/src/yuzu/util/controller_navigation.cpp b/src/yuzu/util/controller_navigation.cpp
new file mode 100644
index 000000000..86fb28b9f
--- /dev/null
+++ b/src/yuzu/util/controller_navigation.cpp
@@ -0,0 +1,177 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included
4
5#include "common/settings_input.h"
6#include "core/hid/emulated_controller.h"
7#include "core/hid/hid_core.h"
8#include "yuzu/util/controller_navigation.h"
9
10ControllerNavigation::ControllerNavigation(Core::HID::HIDCore& hid_core, QWidget* parent) {
11 player1_controller = hid_core.GetEmulatedController(Core::HID::NpadIdType::Player1);
12 handheld_controller = hid_core.GetEmulatedController(Core::HID::NpadIdType::Handheld);
13 Core::HID::ControllerUpdateCallback engine_callback{
14 .on_change = [this](Core::HID::ControllerTriggerType type) { ControllerUpdateEvent(type); },
15 .is_npad_service = false,
16 };
17 player1_callback_key = player1_controller->SetCallback(engine_callback);
18 handheld_callback_key = handheld_controller->SetCallback(engine_callback);
19 is_controller_set = true;
20}
21
22ControllerNavigation::~ControllerNavigation() {
23 UnloadController();
24}
25
26void ControllerNavigation::UnloadController() {
27 if (is_controller_set) {
28 player1_controller->DeleteCallback(player1_callback_key);
29 handheld_controller->DeleteCallback(handheld_callback_key);
30 is_controller_set = false;
31 }
32}
33
34void ControllerNavigation::TriggerButton(Settings::NativeButton::Values native_button,
35 Qt::Key key) {
36 if (button_values[native_button].value && !button_values[native_button].locked) {
37 emit TriggerKeyboardEvent(key);
38 }
39}
40
41void ControllerNavigation::ControllerUpdateEvent(Core::HID::ControllerTriggerType type) {
42 std::lock_guard lock{mutex};
43 if (type == Core::HID::ControllerTriggerType::Button) {
44 ControllerUpdateButton();
45 return;
46 }
47
48 if (type == Core::HID::ControllerTriggerType::Stick) {
49 ControllerUpdateStick();
50 return;
51 }
52}
53
54void ControllerNavigation::ControllerUpdateButton() {
55 const auto controller_type = player1_controller->GetNpadStyleIndex();
56 const auto& player1_buttons = player1_controller->GetButtonsValues();
57 const auto& handheld_buttons = handheld_controller->GetButtonsValues();
58
59 for (std::size_t i = 0; i < player1_buttons.size(); ++i) {
60 const bool button = player1_buttons[i].value || handheld_buttons[i].value;
61 // Trigger only once
62 button_values[i].locked = button == button_values[i].value;
63 button_values[i].value = button;
64 }
65
66 switch (controller_type) {
67 case Core::HID::NpadStyleIndex::ProController:
68 case Core::HID::NpadStyleIndex::JoyconDual:
69 case Core::HID::NpadStyleIndex::Handheld:
70 case Core::HID::NpadStyleIndex::GameCube:
71 TriggerButton(Settings::NativeButton::A, Qt::Key_Enter);
72 TriggerButton(Settings::NativeButton::B, Qt::Key_Escape);
73 TriggerButton(Settings::NativeButton::DDown, Qt::Key_Down);
74 TriggerButton(Settings::NativeButton::DLeft, Qt::Key_Left);
75 TriggerButton(Settings::NativeButton::DRight, Qt::Key_Right);
76 TriggerButton(Settings::NativeButton::DUp, Qt::Key_Up);
77 break;
78 case Core::HID::NpadStyleIndex::JoyconLeft:
79 TriggerButton(Settings::NativeButton::DDown, Qt::Key_Enter);
80 TriggerButton(Settings::NativeButton::DLeft, Qt::Key_Escape);
81 break;
82 case Core::HID::NpadStyleIndex::JoyconRight:
83 TriggerButton(Settings::NativeButton::X, Qt::Key_Enter);
84 TriggerButton(Settings::NativeButton::A, Qt::Key_Escape);
85 break;
86 default:
87 break;
88 }
89}
90
91void ControllerNavigation::ControllerUpdateStick() {
92 const auto controller_type = player1_controller->GetNpadStyleIndex();
93 const auto& player1_sticks = player1_controller->GetSticksValues();
94 const auto& handheld_sticks = player1_controller->GetSticksValues();
95 bool update = false;
96
97 for (std::size_t i = 0; i < player1_sticks.size(); ++i) {
98 const Common::Input::StickStatus stick{
99 .left = player1_sticks[i].left || handheld_sticks[i].left,
100 .right = player1_sticks[i].right || handheld_sticks[i].right,
101 .up = player1_sticks[i].up || handheld_sticks[i].up,
102 .down = player1_sticks[i].down || handheld_sticks[i].down,
103 };
104 // Trigger only once
105 if (stick.down != stick_values[i].down || stick.left != stick_values[i].left ||
106 stick.right != stick_values[i].right || stick.up != stick_values[i].up) {
107 update = true;
108 }
109 stick_values[i] = stick;
110 }
111
112 if (!update) {
113 return;
114 }
115
116 switch (controller_type) {
117 case Core::HID::NpadStyleIndex::ProController:
118 case Core::HID::NpadStyleIndex::JoyconDual:
119 case Core::HID::NpadStyleIndex::Handheld:
120 case Core::HID::NpadStyleIndex::GameCube:
121 if (stick_values[Settings::NativeAnalog::LStick].down) {
122 emit TriggerKeyboardEvent(Qt::Key_Down);
123 return;
124 }
125 if (stick_values[Settings::NativeAnalog::LStick].left) {
126 emit TriggerKeyboardEvent(Qt::Key_Left);
127 return;
128 }
129 if (stick_values[Settings::NativeAnalog::LStick].right) {
130 emit TriggerKeyboardEvent(Qt::Key_Right);
131 return;
132 }
133 if (stick_values[Settings::NativeAnalog::LStick].up) {
134 emit TriggerKeyboardEvent(Qt::Key_Up);
135 return;
136 }
137 break;
138 case Core::HID::NpadStyleIndex::JoyconLeft:
139 if (stick_values[Settings::NativeAnalog::LStick].left) {
140 emit TriggerKeyboardEvent(Qt::Key_Down);
141 return;
142 }
143 if (stick_values[Settings::NativeAnalog::LStick].up) {
144 emit TriggerKeyboardEvent(Qt::Key_Left);
145 return;
146 }
147 if (stick_values[Settings::NativeAnalog::LStick].down) {
148 emit TriggerKeyboardEvent(Qt::Key_Right);
149 return;
150 }
151 if (stick_values[Settings::NativeAnalog::LStick].right) {
152 emit TriggerKeyboardEvent(Qt::Key_Up);
153 return;
154 }
155 break;
156 case Core::HID::NpadStyleIndex::JoyconRight:
157 if (stick_values[Settings::NativeAnalog::RStick].right) {
158 emit TriggerKeyboardEvent(Qt::Key_Down);
159 return;
160 }
161 if (stick_values[Settings::NativeAnalog::RStick].down) {
162 emit TriggerKeyboardEvent(Qt::Key_Left);
163 return;
164 }
165 if (stick_values[Settings::NativeAnalog::RStick].up) {
166 emit TriggerKeyboardEvent(Qt::Key_Right);
167 return;
168 }
169 if (stick_values[Settings::NativeAnalog::RStick].left) {
170 emit TriggerKeyboardEvent(Qt::Key_Up);
171 return;
172 }
173 break;
174 default:
175 break;
176 }
177}
diff --git a/src/yuzu/util/controller_navigation.h b/src/yuzu/util/controller_navigation.h
new file mode 100644
index 000000000..7c616a088
--- /dev/null
+++ b/src/yuzu/util/controller_navigation.h
@@ -0,0 +1,51 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included
4
5#pragma once
6
7#include <QKeyEvent>
8#include <QObject>
9
10#include "common/input.h"
11#include "common/settings_input.h"
12
13namespace Core::HID {
14using ButtonValues = std::array<Common::Input::ButtonStatus, Settings::NativeButton::NumButtons>;
15using SticksValues = std::array<Common::Input::StickStatus, Settings::NativeAnalog::NumAnalogs>;
16enum class ControllerTriggerType;
17class EmulatedController;
18class HIDCore;
19} // namespace Core::HID
20
21class ControllerNavigation : public QObject {
22 Q_OBJECT
23
24public:
25 explicit ControllerNavigation(Core::HID::HIDCore& hid_core, QWidget* parent = nullptr);
26 ~ControllerNavigation();
27
28 /// Disables events from the emulated controller
29 void UnloadController();
30
31signals:
32 void TriggerKeyboardEvent(Qt::Key key);
33
34private:
35 void TriggerButton(Settings::NativeButton::Values native_button, Qt::Key key);
36 void ControllerUpdateEvent(Core::HID::ControllerTriggerType type);
37
38 void ControllerUpdateButton();
39
40 void ControllerUpdateStick();
41
42 Core::HID::ButtonValues button_values{};
43 Core::HID::SticksValues stick_values{};
44
45 int player1_callback_key{};
46 int handheld_callback_key{};
47 bool is_controller_set{};
48 mutable std::mutex mutex;
49 Core::HID::EmulatedController* player1_controller;
50 Core::HID::EmulatedController* handheld_controller;
51};
diff --git a/src/yuzu/util/overlay_dialog.cpp b/src/yuzu/util/overlay_dialog.cpp
index 95b148545..c66dfbdff 100644
--- a/src/yuzu/util/overlay_dialog.cpp
+++ b/src/yuzu/util/overlay_dialog.cpp
@@ -6,7 +6,8 @@
6#include <QScreen> 6#include <QScreen>
7 7
8#include "core/core.h" 8#include "core/core.h"
9#include "core/frontend/input_interpreter.h" 9#include "core/hid/hid_types.h"
10#include "core/hid/input_interpreter.h"
10#include "ui_overlay_dialog.h" 11#include "ui_overlay_dialog.h"
11#include "yuzu/util/overlay_dialog.h" 12#include "yuzu/util/overlay_dialog.h"
12 13
@@ -179,9 +180,9 @@ void OverlayDialog::MoveAndResizeWindow() {
179 QDialog::resize(width, height); 180 QDialog::resize(width, height);
180} 181}
181 182
182template <HIDButton... T> 183template <Core::HID::NpadButton... T>
183void OverlayDialog::HandleButtonPressedOnce() { 184void OverlayDialog::HandleButtonPressedOnce() {
184 const auto f = [this](HIDButton button) { 185 const auto f = [this](Core::HID::NpadButton button) {
185 if (input_interpreter->IsButtonPressedOnce(button)) { 186 if (input_interpreter->IsButtonPressedOnce(button)) {
186 TranslateButtonPress(button); 187 TranslateButtonPress(button);
187 } 188 }
@@ -190,7 +191,7 @@ void OverlayDialog::HandleButtonPressedOnce() {
190 (f(T), ...); 191 (f(T), ...);
191} 192}
192 193
193void OverlayDialog::TranslateButtonPress(HIDButton button) { 194void OverlayDialog::TranslateButtonPress(Core::HID::NpadButton button) {
194 QPushButton* left_button = use_rich_text ? ui->button_cancel_rich : ui->button_cancel; 195 QPushButton* left_button = use_rich_text ? ui->button_cancel_rich : ui->button_cancel;
195 QPushButton* right_button = use_rich_text ? ui->button_ok_rich : ui->button_ok_label; 196 QPushButton* right_button = use_rich_text ? ui->button_ok_rich : ui->button_ok_label;
196 197
@@ -198,20 +199,20 @@ void OverlayDialog::TranslateButtonPress(HIDButton button) {
198 // TODO (Morph): focusPrevious/NextChild() doesn't work well with the rich text dialog, fix it 199 // TODO (Morph): focusPrevious/NextChild() doesn't work well with the rich text dialog, fix it
199 200
200 switch (button) { 201 switch (button) {
201 case HIDButton::A: 202 case Core::HID::NpadButton::A:
202 case HIDButton::B: 203 case Core::HID::NpadButton::B:
203 if (left_button->hasFocus()) { 204 if (left_button->hasFocus()) {
204 left_button->click(); 205 left_button->click();
205 } else if (right_button->hasFocus()) { 206 } else if (right_button->hasFocus()) {
206 right_button->click(); 207 right_button->click();
207 } 208 }
208 break; 209 break;
209 case HIDButton::DLeft: 210 case Core::HID::NpadButton::Left:
210 case HIDButton::LStickLeft: 211 case Core::HID::NpadButton::StickLLeft:
211 focusPreviousChild(); 212 focusPreviousChild();
212 break; 213 break;
213 case HIDButton::DRight: 214 case Core::HID::NpadButton::Right:
214 case HIDButton::LStickRight: 215 case Core::HID::NpadButton::StickLRight:
215 focusNextChild(); 216 focusNextChild();
216 break; 217 break;
217 default: 218 default:
@@ -241,8 +242,10 @@ void OverlayDialog::InputThread() {
241 while (input_thread_running) { 242 while (input_thread_running) {
242 input_interpreter->PollInput(); 243 input_interpreter->PollInput();
243 244
244 HandleButtonPressedOnce<HIDButton::A, HIDButton::B, HIDButton::DLeft, HIDButton::DRight, 245 HandleButtonPressedOnce<Core::HID::NpadButton::A, Core::HID::NpadButton::B,
245 HIDButton::LStickLeft, HIDButton::LStickRight>(); 246 Core::HID::NpadButton::Left, Core::HID::NpadButton::Right,
247 Core::HID::NpadButton::StickLLeft,
248 Core::HID::NpadButton::StickLRight>();
246 249
247 std::this_thread::sleep_for(std::chrono::milliseconds(50)); 250 std::this_thread::sleep_for(std::chrono::milliseconds(50));
248 } 251 }
diff --git a/src/yuzu/util/overlay_dialog.h b/src/yuzu/util/overlay_dialog.h
index e8c388bd0..d8a140ff3 100644
--- a/src/yuzu/util/overlay_dialog.h
+++ b/src/yuzu/util/overlay_dialog.h
@@ -13,14 +13,16 @@
13 13
14#include "common/common_types.h" 14#include "common/common_types.h"
15 15
16enum class HIDButton : u8;
17
18class InputInterpreter; 16class InputInterpreter;
19 17
20namespace Core { 18namespace Core {
21class System; 19class System;
22} 20}
23 21
22namespace Core::HID {
23enum class NpadButton : u64;
24}
25
24namespace Ui { 26namespace Ui {
25class OverlayDialog; 27class OverlayDialog;
26} 28}
@@ -79,7 +81,7 @@ private:
79 * 81 *
80 * @tparam HIDButton The list of buttons that can be converted into keyboard input. 82 * @tparam HIDButton The list of buttons that can be converted into keyboard input.
81 */ 83 */
82 template <HIDButton... T> 84 template <Core::HID::NpadButton... T>
83 void HandleButtonPressedOnce(); 85 void HandleButtonPressedOnce();
84 86
85 /** 87 /**
@@ -87,7 +89,7 @@ private:
87 * 89 *
88 * @param button The button press to process. 90 * @param button The button press to process.
89 */ 91 */
90 void TranslateButtonPress(HIDButton button); 92 void TranslateButtonPress(Core::HID::NpadButton button);
91 93
92 void StartInputThread(); 94 void StartInputThread();
93 void StopInputThread(); 95 void StopInputThread();
diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp
index 33241ea98..8e9c7d211 100644
--- a/src/yuzu_cmd/config.cpp
+++ b/src/yuzu_cmd/config.cpp
@@ -24,7 +24,6 @@
24#include "common/settings.h" 24#include "common/settings.h"
25#include "core/hle/service/acc/profile_manager.h" 25#include "core/hle/service/acc/profile_manager.h"
26#include "input_common/main.h" 26#include "input_common/main.h"
27#include "input_common/udp/client.h"
28#include "yuzu_cmd/config.h" 27#include "yuzu_cmd/config.h"
29#include "yuzu_cmd/default_ini.h" 28#include "yuzu_cmd/default_ini.h"
30 29
@@ -84,163 +83,6 @@ static const std::array<std::array<int, 5>, Settings::NativeAnalog::NumAnalogs>
84 }, 83 },
85}}; 84}};
86 85
87static const std::array<int, Settings::NativeMouseButton::NumMouseButtons> default_mouse_buttons = {
88 SDL_SCANCODE_LEFTBRACKET, SDL_SCANCODE_RIGHTBRACKET, SDL_SCANCODE_APOSTROPHE,
89 SDL_SCANCODE_MINUS, SDL_SCANCODE_EQUALS,
90};
91
92static const std::array<int, 0x8A> keyboard_keys = {
93 0,
94 0,
95 0,
96 0,
97 SDL_SCANCODE_A,
98 SDL_SCANCODE_B,
99 SDL_SCANCODE_C,
100 SDL_SCANCODE_D,
101 SDL_SCANCODE_E,
102 SDL_SCANCODE_F,
103 SDL_SCANCODE_G,
104 SDL_SCANCODE_H,
105 SDL_SCANCODE_I,
106 SDL_SCANCODE_J,
107 SDL_SCANCODE_K,
108 SDL_SCANCODE_L,
109 SDL_SCANCODE_M,
110 SDL_SCANCODE_N,
111 SDL_SCANCODE_O,
112 SDL_SCANCODE_P,
113 SDL_SCANCODE_Q,
114 SDL_SCANCODE_R,
115 SDL_SCANCODE_S,
116 SDL_SCANCODE_T,
117 SDL_SCANCODE_U,
118 SDL_SCANCODE_V,
119 SDL_SCANCODE_W,
120 SDL_SCANCODE_X,
121 SDL_SCANCODE_Y,
122 SDL_SCANCODE_Z,
123 SDL_SCANCODE_1,
124 SDL_SCANCODE_2,
125 SDL_SCANCODE_3,
126 SDL_SCANCODE_4,
127 SDL_SCANCODE_5,
128 SDL_SCANCODE_6,
129 SDL_SCANCODE_7,
130 SDL_SCANCODE_8,
131 SDL_SCANCODE_9,
132 SDL_SCANCODE_0,
133 SDL_SCANCODE_RETURN,
134 SDL_SCANCODE_ESCAPE,
135 SDL_SCANCODE_BACKSPACE,
136 SDL_SCANCODE_TAB,
137 SDL_SCANCODE_SPACE,
138 SDL_SCANCODE_MINUS,
139 SDL_SCANCODE_EQUALS,
140 SDL_SCANCODE_LEFTBRACKET,
141 SDL_SCANCODE_RIGHTBRACKET,
142 SDL_SCANCODE_BACKSLASH,
143 0,
144 SDL_SCANCODE_SEMICOLON,
145 SDL_SCANCODE_APOSTROPHE,
146 SDL_SCANCODE_GRAVE,
147 SDL_SCANCODE_COMMA,
148 SDL_SCANCODE_PERIOD,
149 SDL_SCANCODE_SLASH,
150 SDL_SCANCODE_CAPSLOCK,
151
152 SDL_SCANCODE_F1,
153 SDL_SCANCODE_F2,
154 SDL_SCANCODE_F3,
155 SDL_SCANCODE_F4,
156 SDL_SCANCODE_F5,
157 SDL_SCANCODE_F6,
158 SDL_SCANCODE_F7,
159 SDL_SCANCODE_F8,
160 SDL_SCANCODE_F9,
161 SDL_SCANCODE_F10,
162 SDL_SCANCODE_F11,
163 SDL_SCANCODE_F12,
164
165 0,
166 SDL_SCANCODE_SCROLLLOCK,
167 SDL_SCANCODE_PAUSE,
168 SDL_SCANCODE_INSERT,
169 SDL_SCANCODE_HOME,
170 SDL_SCANCODE_PAGEUP,
171 SDL_SCANCODE_DELETE,
172 SDL_SCANCODE_END,
173 SDL_SCANCODE_PAGEDOWN,
174 SDL_SCANCODE_RIGHT,
175 SDL_SCANCODE_LEFT,
176 SDL_SCANCODE_DOWN,
177 SDL_SCANCODE_UP,
178
179 SDL_SCANCODE_NUMLOCKCLEAR,
180 SDL_SCANCODE_KP_DIVIDE,
181 SDL_SCANCODE_KP_MULTIPLY,
182 SDL_SCANCODE_KP_MINUS,
183 SDL_SCANCODE_KP_PLUS,
184 SDL_SCANCODE_KP_ENTER,
185 SDL_SCANCODE_KP_1,
186 SDL_SCANCODE_KP_2,
187 SDL_SCANCODE_KP_3,
188 SDL_SCANCODE_KP_4,
189 SDL_SCANCODE_KP_5,
190 SDL_SCANCODE_KP_6,
191 SDL_SCANCODE_KP_7,
192 SDL_SCANCODE_KP_8,
193 SDL_SCANCODE_KP_9,
194 SDL_SCANCODE_KP_0,
195 SDL_SCANCODE_KP_PERIOD,
196
197 0,
198 0,
199 SDL_SCANCODE_POWER,
200 SDL_SCANCODE_KP_EQUALS,
201
202 SDL_SCANCODE_F13,
203 SDL_SCANCODE_F14,
204 SDL_SCANCODE_F15,
205 SDL_SCANCODE_F16,
206 SDL_SCANCODE_F17,
207 SDL_SCANCODE_F18,
208 SDL_SCANCODE_F19,
209 SDL_SCANCODE_F20,
210 SDL_SCANCODE_F21,
211 SDL_SCANCODE_F22,
212 SDL_SCANCODE_F23,
213 SDL_SCANCODE_F24,
214
215 0,
216 SDL_SCANCODE_HELP,
217 SDL_SCANCODE_MENU,
218 0,
219 0,
220 0,
221 0,
222 0,
223 0,
224 0,
225 0,
226 0,
227 0,
228 0,
229 0,
230 SDL_SCANCODE_KP_COMMA,
231 SDL_SCANCODE_KP_LEFTPAREN,
232 SDL_SCANCODE_KP_RIGHTPAREN,
233 0,
234 0,
235 0,
236 0,
237};
238
239static const std::array<int, 8> keyboard_mods{
240 SDL_SCANCODE_LCTRL, SDL_SCANCODE_LSHIFT, SDL_SCANCODE_LALT, SDL_SCANCODE_LGUI,
241 SDL_SCANCODE_RCTRL, SDL_SCANCODE_RSHIFT, SDL_SCANCODE_RALT, SDL_SCANCODE_RGUI,
242};
243
244template <> 86template <>
245void Config::ReadSetting(const std::string& group, Settings::BasicSetting<std::string>& setting) { 87void Config::ReadSetting(const std::string& group, Settings::BasicSetting<std::string>& setting) {
246 setting = sdl2_config->Get(group, setting.GetLabel(), setting.GetDefault()); 88 setting = sdl2_config->Get(group, setting.GetLabel(), setting.GetDefault());
@@ -284,16 +126,6 @@ void Config::ReadValues() {
284 } 126 }
285 127
286 ReadSetting("ControlsGeneral", Settings::values.mouse_enabled); 128 ReadSetting("ControlsGeneral", Settings::values.mouse_enabled);
287 for (int i = 0; i < Settings::NativeMouseButton::NumMouseButtons; ++i) {
288 std::string default_param = InputCommon::GenerateKeyboardParam(default_mouse_buttons[i]);
289 Settings::values.mouse_buttons[i] = sdl2_config->Get(
290 "ControlsGeneral", std::string("mouse_") + Settings::NativeMouseButton::mapping[i],
291 default_param);
292 if (Settings::values.mouse_buttons[i].empty())
293 Settings::values.mouse_buttons[i] = default_param;
294 }
295
296 ReadSetting("ControlsGeneral", Settings::values.motion_device);
297 129
298 ReadSetting("ControlsGeneral", Settings::values.touch_device); 130 ReadSetting("ControlsGeneral", Settings::values.touch_device);
299 131
@@ -363,21 +195,11 @@ void Config::ReadValues() {
363 Settings::TouchFromButtonMap{"default", {}}); 195 Settings::TouchFromButtonMap{"default", {}});
364 num_touch_from_button_maps = 1; 196 num_touch_from_button_maps = 1;
365 } 197 }
366 ReadSetting("ControlsGeneral", Settings::values.use_touch_from_button);
367 Settings::values.touch_from_button_map_index = std::clamp( 198 Settings::values.touch_from_button_map_index = std::clamp(
368 Settings::values.touch_from_button_map_index.GetValue(), 0, num_touch_from_button_maps - 1); 199 Settings::values.touch_from_button_map_index.GetValue(), 0, num_touch_from_button_maps - 1);
369 200
370 ReadSetting("ControlsGeneral", Settings::values.udp_input_servers); 201 ReadSetting("ControlsGeneral", Settings::values.udp_input_servers);
371 202
372 std::transform(keyboard_keys.begin(), keyboard_keys.end(),
373 Settings::values.keyboard_keys.begin(), InputCommon::GenerateKeyboardParam);
374 std::transform(keyboard_mods.begin(), keyboard_mods.end(),
375 Settings::values.keyboard_keys.begin() +
376 Settings::NativeKeyboard::LeftControlKey,
377 InputCommon::GenerateKeyboardParam);
378 std::transform(keyboard_mods.begin(), keyboard_mods.end(),
379 Settings::values.keyboard_mods.begin(), InputCommon::GenerateKeyboardParam);
380
381 // Data Storage 203 // Data Storage
382 ReadSetting("Data Storage", Settings::values.use_virtual_sd); 204 ReadSetting("Data Storage", Settings::values.use_virtual_sd);
383 FS::SetYuzuPath(FS::YuzuPath::NANDDir, 205 FS::SetYuzuPath(FS::YuzuPath::NANDDir,
diff --git a/src/yuzu_cmd/default_ini.h b/src/yuzu_cmd/default_ini.h
index ecdc271a8..6d613bf7a 100644
--- a/src/yuzu_cmd/default_ini.h
+++ b/src/yuzu_cmd/default_ini.h
@@ -84,23 +84,10 @@ enable_accurate_vibrations=
84# 0: Disabled, 1 (default): Enabled 84# 0: Disabled, 1 (default): Enabled
85motion_enabled = 85motion_enabled =
86 86
87# for motion input, the following devices are available: 87# Defines the udp device's touch screen coordinate system for cemuhookudp devices
88# - "motion_emu" (default) for emulating motion input from mouse input. Required parameters: 88# - "min_x", "min_y", "max_x", "max_y"
89# - "update_period": update period in milliseconds (default to 100)
90# - "sensitivity": the coefficient converting mouse movement to tilting angle (default to 0.01)
91# - "cemuhookudp" reads motion input from a udp server that uses cemuhook's udp protocol
92motion_device=
93
94# for touch input, the following devices are available:
95# - "emu_window" (default) for emulating touch input from mouse input to the emulation window. No parameters required
96# - "cemuhookudp" reads touch input from a udp server that uses cemuhook's udp protocol
97# - "min_x", "min_y", "max_x", "max_y": defines the udp device's touch screen coordinate system
98touch_device= 89touch_device=
99 90
100# Whether to enable or disable touch input from button
101# 0 (default): Disabled, 1: Enabled
102use_touch_from_button=
103
104# for mapping buttons to touch inputs. 91# for mapping buttons to touch inputs.
105#touch_from_button_map=1 92#touch_from_button_map=1
106#touch_from_button_maps_0_name=default 93#touch_from_button_maps_0_name=default
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
index 87fce0c23..57f807826 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
@@ -9,10 +9,10 @@
9#include "common/settings.h" 9#include "common/settings.h"
10#include "core/core.h" 10#include "core/core.h"
11#include "core/perf_stats.h" 11#include "core/perf_stats.h"
12#include "input_common/keyboard.h" 12#include "input_common/drivers/keyboard.h"
13#include "input_common/drivers/mouse.h"
14#include "input_common/drivers/touch_screen.h"
13#include "input_common/main.h" 15#include "input_common/main.h"
14#include "input_common/mouse/mouse_input.h"
15#include "input_common/sdl/sdl.h"
16#include "yuzu_cmd/emu_window/emu_window_sdl2.h" 16#include "yuzu_cmd/emu_window/emu_window_sdl2.h"
17#include "yuzu_cmd/yuzu_icon.h" 17#include "yuzu_cmd/yuzu_icon.h"
18 18
@@ -32,42 +32,32 @@ EmuWindow_SDL2::~EmuWindow_SDL2() {
32} 32}
33 33
34void EmuWindow_SDL2::OnMouseMotion(s32 x, s32 y) { 34void EmuWindow_SDL2::OnMouseMotion(s32 x, s32 y) {
35 TouchMoved((unsigned)std::max(x, 0), (unsigned)std::max(y, 0), 0); 35 input_subsystem->GetMouse()->MouseMove(x, y, 0, 0, 0, 0);
36
37 input_subsystem->GetMouse()->MouseMove(x, y, 0, 0);
38} 36}
39 37
40MouseInput::MouseButton EmuWindow_SDL2::SDLButtonToMouseButton(u32 button) const { 38InputCommon::MouseButton EmuWindow_SDL2::SDLButtonToMouseButton(u32 button) const {
41 switch (button) { 39 switch (button) {
42 case SDL_BUTTON_LEFT: 40 case SDL_BUTTON_LEFT:
43 return MouseInput::MouseButton::Left; 41 return InputCommon::MouseButton::Left;
44 case SDL_BUTTON_RIGHT: 42 case SDL_BUTTON_RIGHT:
45 return MouseInput::MouseButton::Right; 43 return InputCommon::MouseButton::Right;
46 case SDL_BUTTON_MIDDLE: 44 case SDL_BUTTON_MIDDLE:
47 return MouseInput::MouseButton::Wheel; 45 return InputCommon::MouseButton::Wheel;
48 case SDL_BUTTON_X1: 46 case SDL_BUTTON_X1:
49 return MouseInput::MouseButton::Backward; 47 return InputCommon::MouseButton::Backward;
50 case SDL_BUTTON_X2: 48 case SDL_BUTTON_X2:
51 return MouseInput::MouseButton::Forward; 49 return InputCommon::MouseButton::Forward;
52 default: 50 default:
53 return MouseInput::MouseButton::Undefined; 51 return InputCommon::MouseButton::Undefined;
54 } 52 }
55} 53}
56 54
57void EmuWindow_SDL2::OnMouseButton(u32 button, u8 state, s32 x, s32 y) { 55void EmuWindow_SDL2::OnMouseButton(u32 button, u8 state, s32 x, s32 y) {
58 const auto mouse_button = SDLButtonToMouseButton(button); 56 const auto mouse_button = SDLButtonToMouseButton(button);
59 if (button == SDL_BUTTON_LEFT) { 57 if (state == SDL_PRESSED) {
60 if (state == SDL_PRESSED) { 58 input_subsystem->GetMouse()->PressButton(x, y, 0, 0, mouse_button);
61 TouchPressed((unsigned)std::max(x, 0), (unsigned)std::max(y, 0), 0);
62 } else {
63 TouchReleased(0);
64 }
65 } else { 59 } else {
66 if (state == SDL_PRESSED) { 60 input_subsystem->GetMouse()->ReleaseButton(mouse_button);
67 input_subsystem->GetMouse()->PressButton(x, y, mouse_button);
68 } else {
69 input_subsystem->GetMouse()->ReleaseButton(mouse_button);
70 }
71 } 61 }
72} 62}
73 63
@@ -82,29 +72,35 @@ std::pair<unsigned, unsigned> EmuWindow_SDL2::TouchToPixelPos(float touch_x, flo
82 static_cast<unsigned>(std::max(std::round(touch_y), 0.0f))}; 72 static_cast<unsigned>(std::max(std::round(touch_y), 0.0f))};
83} 73}
84 74
85void EmuWindow_SDL2::OnFingerDown(float x, float y) { 75void EmuWindow_SDL2::OnFingerDown(float x, float y, std::size_t id) {
86 // TODO(NeatNit): keep track of multitouch using the fingerID and a dictionary of some kind 76 int width, height;
87 // This isn't critical because the best we can do when we have that is to average them, like the 77 SDL_GetWindowSize(render_window, &width, &height);
88 // 3DS does
89
90 const auto [px, py] = TouchToPixelPos(x, y); 78 const auto [px, py] = TouchToPixelPos(x, y);
91 TouchPressed(px, py, 0); 79 const float fx = px * 1.0f / width;
80 const float fy = py * 1.0f / height;
81
82 input_subsystem->GetTouchScreen()->TouchPressed(fx, fy, id);
92} 83}
93 84
94void EmuWindow_SDL2::OnFingerMotion(float x, float y) { 85void EmuWindow_SDL2::OnFingerMotion(float x, float y, std::size_t id) {
86 int width, height;
87 SDL_GetWindowSize(render_window, &width, &height);
95 const auto [px, py] = TouchToPixelPos(x, y); 88 const auto [px, py] = TouchToPixelPos(x, y);
96 TouchMoved(px, py, 0); 89 const float fx = px * 1.0f / width;
90 const float fy = py * 1.0f / height;
91
92 input_subsystem->GetTouchScreen()->TouchMoved(fx, fy, id);
97} 93}
98 94
99void EmuWindow_SDL2::OnFingerUp() { 95void EmuWindow_SDL2::OnFingerUp() {
100 TouchReleased(0); 96 input_subsystem->GetTouchScreen()->TouchReleased(0);
101} 97}
102 98
103void EmuWindow_SDL2::OnKeyEvent(int key, u8 state) { 99void EmuWindow_SDL2::OnKeyEvent(int key, u8 state) {
104 if (state == SDL_PRESSED) { 100 if (state == SDL_PRESSED) {
105 input_subsystem->GetKeyboard()->PressKey(key); 101 input_subsystem->GetKeyboard()->PressKey(static_cast<std::size_t>(key));
106 } else if (state == SDL_RELEASED) { 102 } else if (state == SDL_RELEASED) {
107 input_subsystem->GetKeyboard()->ReleaseKey(key); 103 input_subsystem->GetKeyboard()->ReleaseKey(static_cast<std::size_t>(key));
108 } 104 }
109} 105}
110 106
@@ -205,10 +201,12 @@ void EmuWindow_SDL2::WaitEvent() {
205 } 201 }
206 break; 202 break;
207 case SDL_FINGERDOWN: 203 case SDL_FINGERDOWN:
208 OnFingerDown(event.tfinger.x, event.tfinger.y); 204 OnFingerDown(event.tfinger.x, event.tfinger.y,
205 static_cast<std::size_t>(event.tfinger.touchId));
209 break; 206 break;
210 case SDL_FINGERMOTION: 207 case SDL_FINGERMOTION:
211 OnFingerMotion(event.tfinger.x, event.tfinger.y); 208 OnFingerMotion(event.tfinger.x, event.tfinger.y,
209 static_cast<std::size_t>(event.tfinger.touchId));
212 break; 210 break;
213 case SDL_FINGERUP: 211 case SDL_FINGERUP:
214 OnFingerUp(); 212 OnFingerUp();
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2.h b/src/yuzu_cmd/emu_window/emu_window_sdl2.h
index 4810f8775..0af002693 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2.h
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2.h
@@ -16,11 +16,8 @@ class System;
16 16
17namespace InputCommon { 17namespace InputCommon {
18class InputSubsystem; 18class InputSubsystem;
19}
20
21namespace MouseInput {
22enum class MouseButton; 19enum class MouseButton;
23} 20} // namespace InputCommon
24 21
25class EmuWindow_SDL2 : public Core::Frontend::EmuWindow { 22class EmuWindow_SDL2 : public Core::Frontend::EmuWindow {
26public: 23public:
@@ -47,7 +44,7 @@ protected:
47 void OnMouseMotion(s32 x, s32 y); 44 void OnMouseMotion(s32 x, s32 y);
48 45
49 /// Converts a SDL mouse button into MouseInput mouse button 46 /// Converts a SDL mouse button into MouseInput mouse button
50 MouseInput::MouseButton SDLButtonToMouseButton(u32 button) const; 47 InputCommon::MouseButton SDLButtonToMouseButton(u32 button) const;
51 48
52 /// Called by WaitEvent when a mouse button is pressed or released 49 /// Called by WaitEvent when a mouse button is pressed or released
53 void OnMouseButton(u32 button, u8 state, s32 x, s32 y); 50 void OnMouseButton(u32 button, u8 state, s32 x, s32 y);
@@ -56,10 +53,10 @@ protected:
56 std::pair<unsigned, unsigned> TouchToPixelPos(float touch_x, float touch_y) const; 53 std::pair<unsigned, unsigned> TouchToPixelPos(float touch_x, float touch_y) const;
57 54
58 /// Called by WaitEvent when a finger starts touching the touchscreen 55 /// Called by WaitEvent when a finger starts touching the touchscreen
59 void OnFingerDown(float x, float y); 56 void OnFingerDown(float x, float y, std::size_t id);
60 57
61 /// Called by WaitEvent when a finger moves while touching the touchscreen 58 /// Called by WaitEvent when a finger moves while touching the touchscreen
62 void OnFingerMotion(float x, float y); 59 void OnFingerMotion(float x, float y, std::size_t id);
63 60
64 /// Called by WaitEvent when a finger stops touching the touchscreen 61 /// Called by WaitEvent when a finger stops touching the touchscreen
65 void OnFingerUp(); 62 void OnFingerUp();
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp
index a075ad08a..70db865ec 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp
@@ -17,7 +17,6 @@
17#include "common/settings.h" 17#include "common/settings.h"
18#include "common/string_util.h" 18#include "common/string_util.h"
19#include "core/core.h" 19#include "core/core.h"
20#include "input_common/keyboard.h"
21#include "input_common/main.h" 20#include "input_common/main.h"
22#include "video_core/renderer_base.h" 21#include "video_core/renderer_base.h"
23#include "yuzu_cmd/emu_window/emu_window_sdl2_gl.h" 22#include "yuzu_cmd/emu_window/emu_window_sdl2_gl.h"