summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--dist/icons/controller/controller.qrc25
-rw-r--r--dist/icons/controller/dual_joycon.pngbin0 -> 36466 bytes
-rw-r--r--dist/icons/controller/dual_joycon_dark.pngbin0 -> 36261 bytes
-rw-r--r--dist/icons/controller/dual_joycon_midnight.pngbin0 -> 34667 bytes
-rw-r--r--dist/icons/controller/handheld.pngbin0 -> 14108 bytes
-rw-r--r--dist/icons/controller/handheld_dark.pngbin0 -> 13731 bytes
-rw-r--r--dist/icons/controller/handheld_midnight.pngbin0 -> 13366 bytes
-rw-r--r--dist/icons/controller/pro_controller.pngbin0 -> 36710 bytes
-rw-r--r--dist/icons/controller/pro_controller_dark.pngbin0 -> 34897 bytes
-rw-r--r--dist/icons/controller/pro_controller_midnight.pngbin0 -> 35893 bytes
-rw-r--r--dist/icons/controller/single_joycon_left.pngbin0 -> 25565 bytes
-rw-r--r--dist/icons/controller/single_joycon_left_dark.pngbin0 -> 25682 bytes
-rw-r--r--dist/icons/controller/single_joycon_left_midnight.pngbin0 -> 24405 bytes
-rw-r--r--dist/icons/controller/single_joycon_left_vertical.pngbin0 -> 24764 bytes
-rw-r--r--dist/icons/controller/single_joycon_left_vertical_dark.pngbin0 -> 24938 bytes
-rw-r--r--dist/icons/controller/single_joycon_left_vertical_midnight.pngbin0 -> 23681 bytes
-rw-r--r--dist/icons/controller/single_joycon_right.pngbin0 -> 28320 bytes
-rw-r--r--dist/icons/controller/single_joycon_right_dark.pngbin0 -> 28157 bytes
-rw-r--r--dist/icons/controller/single_joycon_right_midnight.pngbin0 -> 27006 bytes
-rw-r--r--dist/icons/controller/single_joycon_right_vertical.pngbin0 -> 27655 bytes
-rw-r--r--dist/icons/controller/single_joycon_right_vertical_dark.pngbin0 -> 27729 bytes
-rw-r--r--dist/icons/controller/single_joycon_right_vertical_midnight.pngbin0 -> 26354 bytes
-rw-r--r--dist/license.md3
-rw-r--r--dist/qt_themes/colorful_dark/icons/16x16/refresh.pngbin0 -> 362 bytes
-rw-r--r--dist/qt_themes/colorful_dark/icons/16x16/view-refresh.pngbin0 -> 362 bytes
-rw-r--r--dist/qt_themes/colorful_dark/style.qrc1
-rw-r--r--dist/qt_themes/colorful_midnight_blue/icons/16x16/refresh.pngbin0 -> 362 bytes
-rw-r--r--dist/qt_themes/colorful_midnight_blue/icons/16x16/view-refresh.pngbin0 -> 362 bytes
-rw-r--r--dist/qt_themes/colorful_midnight_blue/style.qrc1
-rw-r--r--dist/qt_themes/default/default.qrc1
-rw-r--r--dist/qt_themes/default/icons/16x16/refresh.pngbin0 -> 349 bytes
-rw-r--r--dist/qt_themes/default/icons/16x16/view-refresh.pngbin0 -> 349 bytes
-rw-r--r--dist/qt_themes/default/style.qss62
-rw-r--r--dist/qt_themes/qdarkstyle/icons/16x16/refresh.pngbin0 -> 362 bytes
-rw-r--r--dist/qt_themes/qdarkstyle/icons/16x16/view-refresh.pngbin0 -> 362 bytes
-rw-r--r--dist/qt_themes/qdarkstyle/style.qrc1
-rw-r--r--dist/qt_themes/qdarkstyle/style.qss110
-rw-r--r--dist/qt_themes/qdarkstyle_midnight_blue/icons/16x16/refresh.pngbin0 -> 362 bytes
-rw-r--r--dist/qt_themes/qdarkstyle_midnight_blue/icons/16x16/view-refresh.pngbin0 -> 362 bytes
-rw-r--r--dist/qt_themes/qdarkstyle_midnight_blue/style.qrc1
-rw-r--r--dist/qt_themes/qdarkstyle_midnight_blue/style.qss248
-rw-r--r--externals/microprofile/microprofile.h2
-rw-r--r--src/common/assert.h7
-rw-r--r--src/common/param_package.h2
-rw-r--r--src/common/thread.cpp12
-rw-r--r--src/common/thread.h9
-rw-r--r--src/core/arm/cpu_interrupt_handler.cpp6
-rw-r--r--src/core/arm/cpu_interrupt_handler.h3
-rw-r--r--src/core/cpu_manager.cpp2
-rw-r--r--src/core/frontend/framebuffer_layout.h1
-rw-r--r--src/core/hle/kernel/kernel.cpp3
-rw-r--r--src/core/hle/service/hid/controllers/npad.cpp235
-rw-r--r--src/core/hle/service/hid/controllers/npad.h11
-rw-r--r--src/core/hle/service/hid/controllers/touchscreen.cpp12
-rw-r--r--src/core/hle/service/hid/controllers/touchscreen.h1
-rw-r--r--src/core/hle/service/hid/hid.cpp14
-rw-r--r--src/core/settings.cpp50
-rw-r--r--src/core/settings.h347
-rw-r--r--src/input_common/CMakeLists.txt4
-rw-r--r--src/input_common/gcadapter/gc_poller.cpp4
-rw-r--r--src/input_common/main.cpp212
-rw-r--r--src/input_common/main.h132
-rw-r--r--src/input_common/sdl/sdl.h19
-rw-r--r--src/input_common/sdl/sdl_impl.cpp413
-rw-r--r--src/input_common/sdl/sdl_impl.h8
-rw-r--r--src/input_common/settings.cpp33
-rw-r--r--src/input_common/settings.h335
-rw-r--r--src/input_common/touch_from_button.cpp50
-rw-r--r--src/input_common/touch_from_button.h23
-rw-r--r--src/input_common/udp/udp.cpp14
-rw-r--r--src/input_common/udp/udp.h7
-rw-r--r--src/video_core/CMakeLists.txt5
-rw-r--r--src/video_core/host_shaders/CMakeLists.txt43
-rw-r--r--src/video_core/host_shaders/StringShaderHeader.cmake11
-rw-r--r--src/video_core/host_shaders/opengl_present.frag10
-rw-r--r--src/video_core/host_shaders/opengl_present.vert24
-rw-r--r--src/video_core/host_shaders/source_shader.h.in9
-rw-r--r--src/video_core/memory_manager.h34
-rw-r--r--src/video_core/renderer_opengl/gl_resource_manager.cpp9
-rw-r--r--src/video_core/renderer_opengl/gl_resource_manager.h3
-rw-r--r--src/video_core/renderer_opengl/gl_shader_cache.cpp1
-rw-r--r--src/video_core/renderer_opengl/gl_shader_util.cpp15
-rw-r--r--src/video_core/renderer_opengl/gl_shader_util.h2
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.cpp46
-rw-r--r--src/video_core/renderer_vulkan/vk_device.cpp8
-rw-r--r--src/video_core/renderer_vulkan/vk_rasterizer.cpp4
-rw-r--r--src/video_core/renderer_vulkan/vk_state_tracker.cpp7
-rw-r--r--src/video_core/renderer_vulkan/vk_state_tracker.h16
-rw-r--r--src/video_core/shader/decode/memory.cpp3
-rw-r--r--src/yuzu/CMakeLists.txt16
-rw-r--r--src/yuzu/bootmanager.cpp25
-rw-r--r--src/yuzu/bootmanager.h9
-rw-r--r--src/yuzu/configuration/config.cpp169
-rw-r--r--src/yuzu/configuration/config.h6
-rw-r--r--src/yuzu/configuration/configure.ui59
-rw-r--r--src/yuzu/configuration/configure_debug_controller.cpp40
-rw-r--r--src/yuzu/configuration/configure_debug_controller.h38
-rw-r--r--src/yuzu/configuration/configure_debug_controller.ui97
-rw-r--r--src/yuzu/configuration/configure_dialog.cpp13
-rw-r--r--src/yuzu/configuration/configure_dialog.h7
-rw-r--r--src/yuzu/configuration/configure_input.cpp277
-rw-r--r--src/yuzu/configuration/configure_input.h31
-rw-r--r--src/yuzu/configuration/configure_input.ui1039
-rw-r--r--src/yuzu/configuration/configure_input_advanced.cpp171
-rw-r--r--src/yuzu/configuration/configure_input_advanced.h46
-rw-r--r--src/yuzu/configuration/configure_input_advanced.ui2688
-rw-r--r--src/yuzu/configuration/configure_input_player.cpp786
-rw-r--r--src/yuzu/configuration/configure_input_player.h114
-rw-r--r--src/yuzu/configuration/configure_input_player.ui4019
-rw-r--r--src/yuzu/configuration/configure_input_simple.cpp152
-rw-r--r--src/yuzu/configuration/configure_input_simple.h43
-rw-r--r--src/yuzu/configuration/configure_input_simple.ui97
-rw-r--r--src/yuzu/configuration/configure_motion_touch.cpp314
-rw-r--r--src/yuzu/configuration/configure_motion_touch.h90
-rw-r--r--src/yuzu/configuration/configure_motion_touch.ui327
-rw-r--r--src/yuzu/configuration/configure_mouse_advanced.cpp56
-rw-r--r--src/yuzu/configuration/configure_mouse_advanced.h15
-rw-r--r--src/yuzu/configuration/configure_mouse_advanced.ui252
-rw-r--r--src/yuzu/configuration/configure_touch_from_button.cpp623
-rw-r--r--src/yuzu/configuration/configure_touch_from_button.h92
-rw-r--r--src/yuzu/configuration/configure_touch_from_button.ui231
-rw-r--r--src/yuzu/configuration/configure_touch_widget.h62
-rw-r--r--src/yuzu/main.cpp11
-rw-r--r--src/yuzu/main.h17
-rw-r--r--src/yuzu/uisettings.cpp1
-rw-r--r--src/yuzu/uisettings.h4
-rw-r--r--src/yuzu_cmd/config.cpp2
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2.cpp18
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2.h10
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp5
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2_gl.h7
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp5
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2_vk.h7
-rw-r--r--src/yuzu_cmd/yuzu.cpp9
-rw-r--r--src/yuzu_tester/config.cpp1
-rw-r--r--src/yuzu_tester/emu_window/emu_window_sdl2_hide.cpp5
-rw-r--r--src/yuzu_tester/emu_window/emu_window_sdl2_hide.h6
137 files changed, 11252 insertions, 3534 deletions
diff --git a/dist/icons/controller/controller.qrc b/dist/icons/controller/controller.qrc
new file mode 100644
index 000000000..f44725d8f
--- /dev/null
+++ b/dist/icons/controller/controller.qrc
@@ -0,0 +1,25 @@
1<RCC>
2 <qresource prefix="controller">
3 <file alias="dual_joycon">dual_joycon.png</file>
4 <file alias="dual_joycon_dark">dual_joycon_dark.png</file>
5 <file alias="dual_joycon_midnight">dual_joycon_midnight.png</file>
6 <file alias="handheld">handheld.png</file>
7 <file alias="handheld_dark">handheld_dark.png</file>
8 <file alias="handheld_midnight">handheld_midnight.png</file>
9 <file alias="pro_controller">pro_controller.png</file>
10 <file alias="pro_controller_dark">pro_controller_dark.png</file>
11 <file alias="pro_controller_midnight">pro_controller_midnight.png</file>
12 <file alias="single_joycon_left">single_joycon_left.png</file>
13 <file alias="single_joycon_left_dark">single_joycon_left_dark.png</file>
14 <file alias="single_joycon_left_midnight">single_joycon_left_midnight.png</file>
15 <file alias="single_joycon_right">single_joycon_right.png</file>
16 <file alias="single_joycon_right_dark">single_joycon_right_dark.png</file>
17 <file alias="single_joycon_right_midnight">single_joycon_right_midnight.png</file>
18 <file alias="single_joycon_left_vertical">single_joycon_left_vertical.png</file>
19 <file alias="single_joycon_left_vertical_dark">single_joycon_left_vertical_dark.png</file>
20 <file alias="single_joycon_left_vertical_midnight">single_joycon_left_vertical_midnight.png</file>
21 <file alias="single_joycon_right_vertical">single_joycon_right_vertical.png</file>
22 <file alias="single_joycon_right_vertical_dark">single_joycon_right_vertical_dark.png</file>
23 <file alias="single_joycon_right_vertical_midnight">single_joycon_right_vertical_midnight.png</file>
24 </qresource>
25</RCC>
diff --git a/dist/icons/controller/dual_joycon.png b/dist/icons/controller/dual_joycon.png
new file mode 100644
index 000000000..4230f5f7b
--- /dev/null
+++ b/dist/icons/controller/dual_joycon.png
Binary files differ
diff --git a/dist/icons/controller/dual_joycon_dark.png b/dist/icons/controller/dual_joycon_dark.png
new file mode 100644
index 000000000..4445db489
--- /dev/null
+++ b/dist/icons/controller/dual_joycon_dark.png
Binary files differ
diff --git a/dist/icons/controller/dual_joycon_midnight.png b/dist/icons/controller/dual_joycon_midnight.png
new file mode 100644
index 000000000..aac8e5321
--- /dev/null
+++ b/dist/icons/controller/dual_joycon_midnight.png
Binary files differ
diff --git a/dist/icons/controller/handheld.png b/dist/icons/controller/handheld.png
new file mode 100644
index 000000000..d009b4a47
--- /dev/null
+++ b/dist/icons/controller/handheld.png
Binary files differ
diff --git a/dist/icons/controller/handheld_dark.png b/dist/icons/controller/handheld_dark.png
new file mode 100644
index 000000000..c80ca9259
--- /dev/null
+++ b/dist/icons/controller/handheld_dark.png
Binary files differ
diff --git a/dist/icons/controller/handheld_midnight.png b/dist/icons/controller/handheld_midnight.png
new file mode 100644
index 000000000..19de4629b
--- /dev/null
+++ b/dist/icons/controller/handheld_midnight.png
Binary files differ
diff --git a/dist/icons/controller/pro_controller.png b/dist/icons/controller/pro_controller.png
new file mode 100644
index 000000000..07d65e94a
--- /dev/null
+++ b/dist/icons/controller/pro_controller.png
Binary files differ
diff --git a/dist/icons/controller/pro_controller_dark.png b/dist/icons/controller/pro_controller_dark.png
new file mode 100644
index 000000000..73efe18f4
--- /dev/null
+++ b/dist/icons/controller/pro_controller_dark.png
Binary files differ
diff --git a/dist/icons/controller/pro_controller_midnight.png b/dist/icons/controller/pro_controller_midnight.png
new file mode 100644
index 000000000..8d7e63f0d
--- /dev/null
+++ b/dist/icons/controller/pro_controller_midnight.png
Binary files differ
diff --git a/dist/icons/controller/single_joycon_left.png b/dist/icons/controller/single_joycon_left.png
new file mode 100644
index 000000000..547153034
--- /dev/null
+++ b/dist/icons/controller/single_joycon_left.png
Binary files differ
diff --git a/dist/icons/controller/single_joycon_left_dark.png b/dist/icons/controller/single_joycon_left_dark.png
new file mode 100644
index 000000000..b6ee073cb
--- /dev/null
+++ b/dist/icons/controller/single_joycon_left_dark.png
Binary files differ
diff --git a/dist/icons/controller/single_joycon_left_midnight.png b/dist/icons/controller/single_joycon_left_midnight.png
new file mode 100644
index 000000000..34a485c81
--- /dev/null
+++ b/dist/icons/controller/single_joycon_left_midnight.png
Binary files differ
diff --git a/dist/icons/controller/single_joycon_left_vertical.png b/dist/icons/controller/single_joycon_left_vertical.png
new file mode 100644
index 000000000..1e6282ad8
--- /dev/null
+++ b/dist/icons/controller/single_joycon_left_vertical.png
Binary files differ
diff --git a/dist/icons/controller/single_joycon_left_vertical_dark.png b/dist/icons/controller/single_joycon_left_vertical_dark.png
new file mode 100644
index 000000000..a615d995d
--- /dev/null
+++ b/dist/icons/controller/single_joycon_left_vertical_dark.png
Binary files differ
diff --git a/dist/icons/controller/single_joycon_left_vertical_midnight.png b/dist/icons/controller/single_joycon_left_vertical_midnight.png
new file mode 100644
index 000000000..4cc578216
--- /dev/null
+++ b/dist/icons/controller/single_joycon_left_vertical_midnight.png
Binary files differ
diff --git a/dist/icons/controller/single_joycon_right.png b/dist/icons/controller/single_joycon_right.png
new file mode 100644
index 000000000..8d29173f6
--- /dev/null
+++ b/dist/icons/controller/single_joycon_right.png
Binary files differ
diff --git a/dist/icons/controller/single_joycon_right_dark.png b/dist/icons/controller/single_joycon_right_dark.png
new file mode 100644
index 000000000..ead2c44e0
--- /dev/null
+++ b/dist/icons/controller/single_joycon_right_dark.png
Binary files differ
diff --git a/dist/icons/controller/single_joycon_right_midnight.png b/dist/icons/controller/single_joycon_right_midnight.png
new file mode 100644
index 000000000..89afe022d
--- /dev/null
+++ b/dist/icons/controller/single_joycon_right_midnight.png
Binary files differ
diff --git a/dist/icons/controller/single_joycon_right_vertical.png b/dist/icons/controller/single_joycon_right_vertical.png
new file mode 100644
index 000000000..4d7d06547
--- /dev/null
+++ b/dist/icons/controller/single_joycon_right_vertical.png
Binary files differ
diff --git a/dist/icons/controller/single_joycon_right_vertical_dark.png b/dist/icons/controller/single_joycon_right_vertical_dark.png
new file mode 100644
index 000000000..9a6eb3013
--- /dev/null
+++ b/dist/icons/controller/single_joycon_right_vertical_dark.png
Binary files differ
diff --git a/dist/icons/controller/single_joycon_right_vertical_midnight.png b/dist/icons/controller/single_joycon_right_vertical_midnight.png
new file mode 100644
index 000000000..685249b68
--- /dev/null
+++ b/dist/icons/controller/single_joycon_right_vertical_midnight.png
Binary files differ
diff --git a/dist/license.md b/dist/license.md
index f1ff35c95..e9bc87656 100644
--- a/dist/license.md
+++ b/dist/license.md
@@ -5,6 +5,7 @@ Icon Name | License | Origin/Author
5qt_themes/default/icons/16x16/checked.png | CC BY-ND 3.0 | https://icons8.com 5qt_themes/default/icons/16x16/checked.png | CC BY-ND 3.0 | https://icons8.com
6qt_themes/default/icons/16x16/failed.png | CC BY-ND 3.0 | https://icons8.com 6qt_themes/default/icons/16x16/failed.png | CC BY-ND 3.0 | https://icons8.com
7qt_themes/default/icons/16x16/lock.png | CC BY-ND 3.0 | https://icons8.com 7qt_themes/default/icons/16x16/lock.png | CC BY-ND 3.0 | https://icons8.com
8qt_themes/default/icons/16x16/view-refresh.png | Apache 2.0 | https://material.io
8qt_themes/default/icons/256x256/plus_folder.png | CC BY-ND 3.0 | https://icons8.com 9qt_themes/default/icons/256x256/plus_folder.png | CC BY-ND 3.0 | https://icons8.com
9qt_themes/default/icons/48x48/bad_folder.png | CC BY-ND 3.0 | https://icons8.com 10qt_themes/default/icons/48x48/bad_folder.png | CC BY-ND 3.0 | https://icons8.com
10qt_themes/default/icons/48x48/chip.png | CC BY-ND 3.0 | https://icons8.com 11qt_themes/default/icons/48x48/chip.png | CC BY-ND 3.0 | https://icons8.com
@@ -12,6 +13,7 @@ qt_themes/default/icons/48x48/folder.png | CC BY-ND 3.0 | https://icons8.com
12qt_themes/default/icons/48x48/plus.png | CC0 1.0 | Designed by BreadFish64 from the Citra team 13qt_themes/default/icons/48x48/plus.png | CC0 1.0 | Designed by BreadFish64 from the Citra team
13qt_themes/default/icons/48x48/sd_card.png | CC BY-ND 3.0 | https://icons8.com 14qt_themes/default/icons/48x48/sd_card.png | CC BY-ND 3.0 | https://icons8.com
14qt_themes/qdarkstyle/icons/16x16/lock.png | CC BY-ND 3.0 | https://icons8.com 15qt_themes/qdarkstyle/icons/16x16/lock.png | CC BY-ND 3.0 | https://icons8.com
16qt_themes/qdarkstyle/icons/16x16/view-refresh.png | Apache 2.0 | https://material.io
15qt_themes/qdarkstyle/icons/256x256/plus_folder.png | CC BY-ND 3.0 | https://icons8.com 17qt_themes/qdarkstyle/icons/256x256/plus_folder.png | CC BY-ND 3.0 | https://icons8.com
16qt_themes/qdarkstyle/icons/48x48/bad_folder.png | CC BY-ND 3.0 | https://icons8.com 18qt_themes/qdarkstyle/icons/48x48/bad_folder.png | CC BY-ND 3.0 | https://icons8.com
17qt_themes/qdarkstyle/icons/48x48/chip.png | CC BY-ND 3.0 | https://icons8.com 19qt_themes/qdarkstyle/icons/48x48/chip.png | CC BY-ND 3.0 | https://icons8.com
@@ -19,6 +21,7 @@ qt_themes/qdarkstyle/icons/48x48/folder.png | CC BY-ND 3.0 | https://icons8.com
19qt_themes/qdarkstyle/icons/48x48/plus.png | CC0 1.0 | Designed by BreadFish64 from the Citra team 21qt_themes/qdarkstyle/icons/48x48/plus.png | CC0 1.0 | Designed by BreadFish64 from the Citra team
20qt_themes/qdarkstyle/icons/48x48/sd_card.png | CC BY-ND 3.0 | https://icons8.com 22qt_themes/qdarkstyle/icons/48x48/sd_card.png | CC BY-ND 3.0 | https://icons8.com
21qt_themes/colorful/icons/16x16/lock.png | CC BY-ND 3.0 | https://icons8.com 23qt_themes/colorful/icons/16x16/lock.png | CC BY-ND 3.0 | https://icons8.com
24qt_themes/colorful/icons/16x16/view-refresh.png | Apache 2.0 | https://material.io
22qt_themes/colorful/icons/256x256/plus_folder.png | CC BY-ND 3.0 | https://icons8.com 25qt_themes/colorful/icons/256x256/plus_folder.png | CC BY-ND 3.0 | https://icons8.com
23qt_themes/colorful/icons/48x48/bad_folder.png | CC BY-ND 3.0 | https://icons8.com 26qt_themes/colorful/icons/48x48/bad_folder.png | CC BY-ND 3.0 | https://icons8.com
24qt_themes/colorful/icons/48x48/chip.png | CC BY-ND 3.0 | https://icons8.com 27qt_themes/colorful/icons/48x48/chip.png | CC BY-ND 3.0 | https://icons8.com
diff --git a/dist/qt_themes/colorful_dark/icons/16x16/refresh.png b/dist/qt_themes/colorful_dark/icons/16x16/refresh.png
new file mode 100644
index 000000000..d4afd76f9
--- /dev/null
+++ b/dist/qt_themes/colorful_dark/icons/16x16/refresh.png
Binary files differ
diff --git a/dist/qt_themes/colorful_dark/icons/16x16/view-refresh.png b/dist/qt_themes/colorful_dark/icons/16x16/view-refresh.png
new file mode 100644
index 000000000..d4afd76f9
--- /dev/null
+++ b/dist/qt_themes/colorful_dark/icons/16x16/view-refresh.png
Binary files differ
diff --git a/dist/qt_themes/colorful_dark/style.qrc b/dist/qt_themes/colorful_dark/style.qrc
index 27a6cc87d..0abcb4e83 100644
--- a/dist/qt_themes/colorful_dark/style.qrc
+++ b/dist/qt_themes/colorful_dark/style.qrc
@@ -2,6 +2,7 @@
2 <qresource prefix="icons/colorful_dark"> 2 <qresource prefix="icons/colorful_dark">
3 <file alias="index.theme">icons/index.theme</file> 3 <file alias="index.theme">icons/index.theme</file>
4 <file alias="16x16/lock.png">icons/16x16/lock.png</file> 4 <file alias="16x16/lock.png">icons/16x16/lock.png</file>
5 <file alias="16x16/view-refresh.png">icons/16x16/view-refresh.png</file>
5 <file alias="48x48/bad_folder.png">../colorful/icons/48x48/bad_folder.png</file> 6 <file alias="48x48/bad_folder.png">../colorful/icons/48x48/bad_folder.png</file>
6 <file alias="48x48/chip.png">../colorful/icons/48x48/chip.png</file> 7 <file alias="48x48/chip.png">../colorful/icons/48x48/chip.png</file>
7 <file alias="48x48/folder.png">../colorful/icons/48x48/folder.png</file> 8 <file alias="48x48/folder.png">../colorful/icons/48x48/folder.png</file>
diff --git a/dist/qt_themes/colorful_midnight_blue/icons/16x16/refresh.png b/dist/qt_themes/colorful_midnight_blue/icons/16x16/refresh.png
new file mode 100644
index 000000000..d4afd76f9
--- /dev/null
+++ b/dist/qt_themes/colorful_midnight_blue/icons/16x16/refresh.png
Binary files differ
diff --git a/dist/qt_themes/colorful_midnight_blue/icons/16x16/view-refresh.png b/dist/qt_themes/colorful_midnight_blue/icons/16x16/view-refresh.png
new file mode 100644
index 000000000..d4afd76f9
--- /dev/null
+++ b/dist/qt_themes/colorful_midnight_blue/icons/16x16/view-refresh.png
Binary files differ
diff --git a/dist/qt_themes/colorful_midnight_blue/style.qrc b/dist/qt_themes/colorful_midnight_blue/style.qrc
index fd33bc850..bf367099a 100644
--- a/dist/qt_themes/colorful_midnight_blue/style.qrc
+++ b/dist/qt_themes/colorful_midnight_blue/style.qrc
@@ -2,6 +2,7 @@
2 <qresource prefix="icons/colorful_midnight_blue"> 2 <qresource prefix="icons/colorful_midnight_blue">
3 <file alias="index.theme">icons/index.theme</file> 3 <file alias="index.theme">icons/index.theme</file>
4 <file alias="16x16/lock.png">icons/16x16/lock.png</file> 4 <file alias="16x16/lock.png">icons/16x16/lock.png</file>
5 <file alias="16x16/view-refresh.png">icons/16x16/view-refresh.png</file>
5 <file alias="48x48/bad_folder.png">../colorful/icons/48x48/bad_folder.png</file> 6 <file alias="48x48/bad_folder.png">../colorful/icons/48x48/bad_folder.png</file>
6 <file alias="48x48/chip.png">../colorful/icons/48x48/chip.png</file> 7 <file alias="48x48/chip.png">../colorful/icons/48x48/chip.png</file>
7 <file alias="48x48/folder.png">../colorful/icons/48x48/folder.png</file> 8 <file alias="48x48/folder.png">../colorful/icons/48x48/folder.png</file>
diff --git a/dist/qt_themes/default/default.qrc b/dist/qt_themes/default/default.qrc
index c51fdb26c..2182f33f3 100644
--- a/dist/qt_themes/default/default.qrc
+++ b/dist/qt_themes/default/default.qrc
@@ -4,6 +4,7 @@
4 <file alias="16x16/checked.png">icons/16x16/checked.png</file> 4 <file alias="16x16/checked.png">icons/16x16/checked.png</file>
5 <file alias="16x16/failed.png">icons/16x16/failed.png</file> 5 <file alias="16x16/failed.png">icons/16x16/failed.png</file>
6 <file alias="16x16/lock.png">icons/16x16/lock.png</file> 6 <file alias="16x16/lock.png">icons/16x16/lock.png</file>
7 <file alias="16x16/view-refresh.png">icons/16x16/view-refresh.png</file>
7 <file alias="48x48/bad_folder.png">icons/48x48/bad_folder.png</file> 8 <file alias="48x48/bad_folder.png">icons/48x48/bad_folder.png</file>
8 <file alias="48x48/chip.png">icons/48x48/chip.png</file> 9 <file alias="48x48/chip.png">icons/48x48/chip.png</file>
9 <file alias="48x48/folder.png">icons/48x48/folder.png</file> 10 <file alias="48x48/folder.png">icons/48x48/folder.png</file>
diff --git a/dist/qt_themes/default/icons/16x16/refresh.png b/dist/qt_themes/default/icons/16x16/refresh.png
new file mode 100644
index 000000000..69f9474ac
--- /dev/null
+++ b/dist/qt_themes/default/icons/16x16/refresh.png
Binary files differ
diff --git a/dist/qt_themes/default/icons/16x16/view-refresh.png b/dist/qt_themes/default/icons/16x16/view-refresh.png
new file mode 100644
index 000000000..69f9474ac
--- /dev/null
+++ b/dist/qt_themes/default/icons/16x16/view-refresh.png
Binary files differ
diff --git a/dist/qt_themes/default/style.qss b/dist/qt_themes/default/style.qss
index 6b5953e38..5da56520b 100644
--- a/dist/qt_themes/default/style.qss
+++ b/dist/qt_themes/default/style.qss
@@ -30,6 +30,66 @@ QPushButton#RendererStatusBarButton:checked {
30 color: #e85c00; 30 color: #e85c00;
31} 31}
32 32
33QPushButton#RendererStatusBarButton:!checked{ 33QPushButton#RendererStatusBarButton:!checked {
34 color: #0066ff; 34 color: #0066ff;
35} 35}
36
37QPushButton#buttonRefreshDevices {
38 min-width: 20px;
39 min-height: 20px;
40 max-width: 20px;
41 max-height: 20px;
42}
43
44QCheckBox#checkboxPlayer1Connected,
45QCheckBox#checkboxPlayer2Connected,
46QCheckBox#checkboxPlayer3Connected,
47QCheckBox#checkboxPlayer4Connected,
48QCheckBox#checkboxPlayer5Connected,
49QCheckBox#checkboxPlayer6Connected,
50QCheckBox#checkboxPlayer7Connected,
51QCheckBox#checkboxPlayer8Connected {
52 spacing: 0px;
53}
54
55QCheckBox#checkboxPlayer1Connected::indicator,
56QCheckBox#checkboxPlayer2Connected::indicator,
57QCheckBox#checkboxPlayer3Connected::indicator,
58QCheckBox#checkboxPlayer4Connected::indicator,
59QCheckBox#checkboxPlayer5Connected::indicator,
60QCheckBox#checkboxPlayer6Connected::indicator,
61QCheckBox#checkboxPlayer7Connected::indicator,
62QCheckBox#checkboxPlayer8Connected::indicator {
63 width: 14px;
64 height: 14px;
65}
66
67QCheckBox#checkboxPlayer1Connected::indicator:checked,
68QCheckBox#checkboxPlayer2Connected::indicator:checked,
69QCheckBox#checkboxPlayer3Connected::indicator:checked,
70QCheckBox#checkboxPlayer4Connected::indicator:checked,
71QCheckBox#checkboxPlayer5Connected::indicator:checked,
72QCheckBox#checkboxPlayer6Connected::indicator:checked,
73QCheckBox#checkboxPlayer7Connected::indicator:checked,
74QCheckBox#checkboxPlayer8Connected::indicator:checked,
75QGroupBox#groupConnectedController::indicator:checked {
76 border-radius: 2px;
77 border: 1px solid black;
78 background: #39ff14;
79 image: none;
80}
81
82QCheckBox#checkboxPlayer1Connected::indicator:unchecked,
83QCheckBox#checkboxPlayer2Connected::indicator:unchecked,
84QCheckBox#checkboxPlayer3Connected::indicator:unchecked,
85QCheckBox#checkboxPlayer4Connected::indicator:unchecked,
86QCheckBox#checkboxPlayer5Connected::indicator:unchecked,
87QCheckBox#checkboxPlayer6Connected::indicator:unchecked,
88QCheckBox#checkboxPlayer7Connected::indicator:unchecked,
89QCheckBox#checkboxPlayer8Connected::indicator:unchecked,
90QGroupBox#groupConnectedController::indicator:unchecked {
91 border-radius: 2px;
92 border: 1px solid black;
93 background: transparent;
94 image: none;
95}
diff --git a/dist/qt_themes/qdarkstyle/icons/16x16/refresh.png b/dist/qt_themes/qdarkstyle/icons/16x16/refresh.png
new file mode 100644
index 000000000..d4afd76f9
--- /dev/null
+++ b/dist/qt_themes/qdarkstyle/icons/16x16/refresh.png
Binary files differ
diff --git a/dist/qt_themes/qdarkstyle/icons/16x16/view-refresh.png b/dist/qt_themes/qdarkstyle/icons/16x16/view-refresh.png
new file mode 100644
index 000000000..d4afd76f9
--- /dev/null
+++ b/dist/qt_themes/qdarkstyle/icons/16x16/view-refresh.png
Binary files differ
diff --git a/dist/qt_themes/qdarkstyle/style.qrc b/dist/qt_themes/qdarkstyle/style.qrc
index c2c14c28a..ec07ba160 100644
--- a/dist/qt_themes/qdarkstyle/style.qrc
+++ b/dist/qt_themes/qdarkstyle/style.qrc
@@ -2,6 +2,7 @@
2 <qresource prefix="icons/qdarkstyle"> 2 <qresource prefix="icons/qdarkstyle">
3 <file alias="index.theme">icons/index.theme</file> 3 <file alias="index.theme">icons/index.theme</file>
4 <file alias="16x16/lock.png">icons/16x16/lock.png</file> 4 <file alias="16x16/lock.png">icons/16x16/lock.png</file>
5 <file alias="16x16/view-refresh.png">icons/16x16/view-refresh.png</file>
5 <file alias="48x48/bad_folder.png">icons/48x48/bad_folder.png</file> 6 <file alias="48x48/bad_folder.png">icons/48x48/bad_folder.png</file>
6 <file alias="48x48/chip.png">icons/48x48/chip.png</file> 7 <file alias="48x48/chip.png">icons/48x48/chip.png</file>
7 <file alias="48x48/folder.png">icons/48x48/folder.png</file> 8 <file alias="48x48/folder.png">icons/48x48/folder.png</file>
diff --git a/dist/qt_themes/qdarkstyle/style.qss b/dist/qt_themes/qdarkstyle/style.qss
index 2926a05fa..16218f0c2 100644
--- a/dist/qt_themes/qdarkstyle/style.qss
+++ b/dist/qt_themes/qdarkstyle/style.qss
@@ -40,8 +40,8 @@ QCheckBox:disabled {
40 40
41QCheckBox::indicator, 41QCheckBox::indicator,
42QGroupBox::indicator { 42QGroupBox::indicator {
43 width: 18px; 43 width: 16px;
44 height: 18px; 44 height: 16px;
45} 45}
46 46
47QGroupBox::indicator { 47QGroupBox::indicator {
@@ -1237,6 +1237,7 @@ QPlainTextEdit:disabled {
1237 background-color: #2b2e31; 1237 background-color: #2b2e31;
1238} 1238}
1239 1239
1240
1240QPushButton#TogglableStatusBarButton { 1241QPushButton#TogglableStatusBarButton {
1241 min-width: 0px; 1242 min-width: 0px;
1242 color: #656565; 1243 color: #656565;
@@ -1271,6 +1272,107 @@ QPushButton#RendererStatusBarButton:checked {
1271 color: #e85c00; 1272 color: #e85c00;
1272} 1273}
1273 1274
1274QPushButton#RendererStatusBarButton:!checked{ 1275QPushButton#RendererStatusBarButton:!checked {
1275 color: #00ccdd; 1276 color: #00ccdd;
1277}
1278
1279QPushButton#buttonRefreshDevices {
1280 min-width: 24px;
1281 min-height: 24px;
1282 max-width: 24px;
1283 max-height: 24px;
1284 padding: 0px 0px;
1285}
1286
1287QCheckBox#checkboxPlayer1Connected,
1288QCheckBox#checkboxPlayer2Connected,
1289QCheckBox#checkboxPlayer3Connected,
1290QCheckBox#checkboxPlayer4Connected,
1291QCheckBox#checkboxPlayer5Connected,
1292QCheckBox#checkboxPlayer6Connected,
1293QCheckBox#checkboxPlayer7Connected,
1294QCheckBox#checkboxPlayer8Connected {
1295 spacing: 0px;
1296}
1297
1298QCheckBox#checkboxPlayer1Connected::indicator,
1299QCheckBox#checkboxPlayer2Connected::indicator,
1300QCheckBox#checkboxPlayer3Connected::indicator,
1301QCheckBox#checkboxPlayer4Connected::indicator,
1302QCheckBox#checkboxPlayer5Connected::indicator,
1303QCheckBox#checkboxPlayer6Connected::indicator,
1304QCheckBox#checkboxPlayer7Connected::indicator,
1305QCheckBox#checkboxPlayer8Connected::indicator {
1306 width: 14px;
1307 height: 14px;
1308}
1309
1310QCheckBox#checkboxPlayer1Connected::indicator:checked,
1311QCheckBox#checkboxPlayer2Connected::indicator:checked,
1312QCheckBox#checkboxPlayer3Connected::indicator:checked,
1313QCheckBox#checkboxPlayer4Connected::indicator:checked,
1314QCheckBox#checkboxPlayer5Connected::indicator:checked,
1315QCheckBox#checkboxPlayer6Connected::indicator:checked,
1316QCheckBox#checkboxPlayer7Connected::indicator:checked,
1317QCheckBox#checkboxPlayer8Connected::indicator:checked,
1318QGroupBox#groupConnectedController::indicator:checked {
1319 border-radius: 2px;
1320 border: 1px solid #929192;
1321 background: #39ff14;
1322 image: none;
1323}
1324
1325QCheckBox#checkboxPlayer1Connected::indicator:unchecked,
1326QCheckBox#checkboxPlayer2Connected::indicator:unchecked,
1327QCheckBox#checkboxPlayer3Connected::indicator:unchecked,
1328QCheckBox#checkboxPlayer4Connected::indicator:unchecked,
1329QCheckBox#checkboxPlayer5Connected::indicator:unchecked,
1330QCheckBox#checkboxPlayer6Connected::indicator:unchecked,
1331QCheckBox#checkboxPlayer7Connected::indicator:unchecked,
1332QCheckBox#checkboxPlayer8Connected::indicator:unchecked,
1333QGroupBox#groupConnectedController::indicator:unchecked {
1334 border-radius: 2px;
1335 border: 1px solid #929192;
1336 background: transparent;
1337 image: none;
1338}
1339
1340QSpinBox#spinboxLStickRange,
1341QSpinBox#spinboxRStickRange {
1342 padding: 4px 0px 5px 0px;
1343 min-width: 63px;
1344}
1345
1346QSpinBox#vibrationSpin {
1347 padding: 4px 0px 5px 0px;
1348 min-width: 63px;
1349}
1350
1351QSpinBox#spinboxLStickRange:up-button,
1352QSpinBox#spinboxRStickRange:up-button,
1353QSpinBox#vibrationSpin:up-button {
1354 left: -2px;
1355}
1356
1357QSpinBox#spinboxLStickRange:down-button,
1358QSpinBox#spinboxRStickRange:down-button,
1359QSpinBox#vibrationSpin:down-button {
1360 right: -1px;
1361}
1362
1363QGroupBox#motionGroup::indicator,
1364QGroupBox#vibrationGroup::indicator {
1365 margin-left: 0px;
1366}
1367
1368QGroupBox#motionGroup::title,
1369QGroupBox#vibrationGroup::title {
1370 spacing: 2px;
1371 padding-left: 1px;
1372 padding-right: 1px;
1373}
1374
1375/* touchscreen mapping widget */
1376TouchScreenPreview {
1377 qproperty-dotHighlightColor: #3daee9;
1276} 1378}
diff --git a/dist/qt_themes/qdarkstyle_midnight_blue/icons/16x16/refresh.png b/dist/qt_themes/qdarkstyle_midnight_blue/icons/16x16/refresh.png
new file mode 100644
index 000000000..d4afd76f9
--- /dev/null
+++ b/dist/qt_themes/qdarkstyle_midnight_blue/icons/16x16/refresh.png
Binary files differ
diff --git a/dist/qt_themes/qdarkstyle_midnight_blue/icons/16x16/view-refresh.png b/dist/qt_themes/qdarkstyle_midnight_blue/icons/16x16/view-refresh.png
new file mode 100644
index 000000000..d4afd76f9
--- /dev/null
+++ b/dist/qt_themes/qdarkstyle_midnight_blue/icons/16x16/view-refresh.png
Binary files differ
diff --git a/dist/qt_themes/qdarkstyle_midnight_blue/style.qrc b/dist/qt_themes/qdarkstyle_midnight_blue/style.qrc
index 1b7686f15..616aace73 100644
--- a/dist/qt_themes/qdarkstyle_midnight_blue/style.qrc
+++ b/dist/qt_themes/qdarkstyle_midnight_blue/style.qrc
@@ -2,6 +2,7 @@
2 <qresource prefix="icons/qdarkstyle_midnight_blue"> 2 <qresource prefix="icons/qdarkstyle_midnight_blue">
3 <file alias="index.theme">icons/index.theme</file> 3 <file alias="index.theme">icons/index.theme</file>
4 <file alias="16x16/lock.png">icons/16x16/lock.png</file> 4 <file alias="16x16/lock.png">icons/16x16/lock.png</file>
5 <file alias="16x16/view-refresh.png">icons/16x16/view-refresh.png</file>
5 <file alias="48x48/bad_folder.png">icons/48x48/bad_folder.png</file> 6 <file alias="48x48/bad_folder.png">icons/48x48/bad_folder.png</file>
6 <file alias="48x48/chip.png">icons/48x48/chip.png</file> 7 <file alias="48x48/chip.png">icons/48x48/chip.png</file>
7 <file alias="48x48/folder.png">icons/48x48/folder.png</file> 8 <file alias="48x48/folder.png">icons/48x48/folder.png</file>
diff --git a/dist/qt_themes/qdarkstyle_midnight_blue/style.qss b/dist/qt_themes/qdarkstyle_midnight_blue/style.qss
index 9c24b0d07..a714e1475 100644
--- a/dist/qt_themes/qdarkstyle_midnight_blue/style.qss
+++ b/dist/qt_themes/qdarkstyle_midnight_blue/style.qss
@@ -138,8 +138,6 @@ https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qstatusbar
138 138
139--------------------------------------------------------------------------- */ 139--------------------------------------------------------------------------- */
140QStatusBar { 140QStatusBar {
141 border: 1px solid #32414B;
142 /* Fixes Spyder #9120, #9121 */
143 background: #32414B; 141 background: #32414B;
144 /* Fixes #205, white vertical borders separating items */ 142 /* Fixes #205, white vertical borders separating items */
145} 143}
@@ -161,6 +159,7 @@ QStatusBar QToolTip {
161QStatusBar QLabel { 159QStatusBar QLabel {
162 /* Fixes Spyder #9120, #9121 */ 160 /* Fixes Spyder #9120, #9121 */
163 background: transparent; 161 background: transparent;
162 padding: 0px;
164} 163}
165 164
166/* QCheckBox -------------------------------------------------------------- 165/* QCheckBox --------------------------------------------------------------
@@ -236,21 +235,19 @@ https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qgroupbox
236 235
237--------------------------------------------------------------------------- */ 236--------------------------------------------------------------------------- */
238QGroupBox { 237QGroupBox {
239 font-weight: bold; 238 font-weight: bold;
240 border: 1px solid #32414B; 239 border: 1px solid #32414B;
241 border-radius: 4px; 240 border-radius: 4px;
242 padding: 4px; 241 margin-top: 12px;
243 margin-top: 16px; 242 padding: 4px;
244} 243}
245 244
246QGroupBox::title { 245QGroupBox::title {
247 subcontrol-origin: margin; 246 subcontrol-origin: margin;
248 subcontrol-position: top left; 247 subcontrol-position: top left;
249 left: 3px; 248 padding-left: 3px;
250 padding-left: 3px; 249 padding-right: 5px;
251 padding-right: 5px; 250 padding-top: 4px;
252 padding-top: 8px;
253 padding-bottom: 16px;
254} 251}
255 252
256QGroupBox::indicator { 253QGroupBox::indicator {
@@ -367,28 +364,19 @@ https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qmenubar
367--------------------------------------------------------------------------- */ 364--------------------------------------------------------------------------- */
368QMenuBar { 365QMenuBar {
369 background-color: #32414B; 366 background-color: #32414B;
370 padding: 2px;
371 border: 1px solid #19232D;
372 color: #F0F0F0; 367 color: #F0F0F0;
373} 368}
374 369
375QMenuBar:focus {
376 border: 1px solid #148CD2;
377}
378
379QMenuBar::item { 370QMenuBar::item {
380 background: transparent; 371 background: transparent;
381 padding: 4px;
382} 372}
383 373
384QMenuBar::item:selected { 374QMenuBar::item:selected {
385 padding: 4px;
386 background: transparent; 375 background: transparent;
387 border: 0px solid #32414B; 376 border: 0px solid #32414B;
388} 377}
389 378
390QMenuBar::item:pressed { 379QMenuBar::item:pressed {
391 padding: 4px;
392 border: 0px solid #32414B; 380 border: 0px solid #32414B;
393 background-color: #148CD2; 381 background-color: #148CD2;
394 color: #F0F0F0; 382 color: #F0F0F0;
@@ -396,6 +384,7 @@ QMenuBar::item:pressed {
396 padding-bottom: 0px; 384 padding-bottom: 0px;
397} 385}
398 386
387
399/* QMenu ------------------------------------------------------------------ 388/* QMenu ------------------------------------------------------------------
400 389
401https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qmenu 390https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qmenu
@@ -482,7 +471,7 @@ https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qcombobox
482 471
483--------------------------------------------------------------------------- */ 472--------------------------------------------------------------------------- */
484QAbstractItemView { 473QAbstractItemView {
485 alternate-background-color: #19232D; 474 alternate-background-color: #1f2933;
486 color: #F0F0F0; 475 color: #F0F0F0;
487 border: 1px solid #32414B; 476 border: 1px solid #32414B;
488 border-radius: 4px; 477 border-radius: 4px;
@@ -501,13 +490,13 @@ QAbstractScrollArea {
501 background-color: #19232D; 490 background-color: #19232D;
502 border: 1px solid #32414B; 491 border: 1px solid #32414B;
503 border-radius: 4px; 492 border-radius: 4px;
504 padding: 2px;
505 /* fix #159 */ 493 /* fix #159 */
506 min-height: 1.25em; 494 min-height: 1.25em;
507 /* fix #159 */ 495 /* fix #159 */
508 color: #F0F0F0; 496 color: #F0F0F0;
509} 497}
510 498
499
511QAbstractScrollArea:disabled { 500QAbstractScrollArea:disabled {
512 color: #787878; 501 color: #787878;
513} 502}
@@ -807,20 +796,22 @@ QAbstractSpinBox {
807} 796}
808 797
809QAbstractSpinBox:up-button { 798QAbstractSpinBox:up-button {
810 background-color: transparent #19232D; 799 background-color: #505F69;
811 subcontrol-origin: border; 800 subcontrol-origin: border;
812 subcontrol-position: top right; 801 subcontrol-position: top right;
813 border-left: 1px solid #32414B; 802 border-left: 1px solid #32414B;
814 border-bottom: 1px solid #32414B; 803 border-top: 1px solid #32414B;
804 border-right: 1px solid #32414B;
805 border-top-right-radius: 4px;
815 border-top-left-radius: 0; 806 border-top-left-radius: 0;
816 border-bottom-left-radius: 0; 807 border-bottom-left-radius: 0;
817 margin: 1px; 808 margin: 0px;
818 width: 12px; 809 width: 12px;
819 margin-bottom: -1px; 810 margin-bottom: 0px;
820} 811}
821 812
822QAbstractSpinBox::up-arrow, QAbstractSpinBox::up-arrow:disabled, QAbstractSpinBox::up-arrow:off { 813QAbstractSpinBox::up-arrow, QAbstractSpinBox::up-arrow:disabled, QAbstractSpinBox::up-arrow:off {
823 image: url(":/qss_icons/rc/arrow_up_disabled.png"); 814 image: url(":/qss_icons/rc/up_arrow.png");
824 height: 8px; 815 height: 8px;
825 width: 8px; 816 width: 8px;
826} 817}
@@ -830,20 +821,23 @@ QAbstractSpinBox::up-arrow:hover {
830} 821}
831 822
832QAbstractSpinBox:down-button { 823QAbstractSpinBox:down-button {
833 background-color: transparent #19232D; 824 background-color: #505F69;
834 subcontrol-origin: border; 825 subcontrol-origin: border;
835 subcontrol-position: bottom right; 826 subcontrol-position: bottom right;
836 border-left: 1px solid #32414B; 827 border-left: 1px solid #32414B;
828 border-right: 1px solid #32414B;
829 border-bottom: 1px solid #32414B;
837 border-top: 1px solid #32414B; 830 border-top: 1px solid #32414B;
838 border-top-left-radius: 0; 831 border-top-left-radius: 0;
839 border-bottom-left-radius: 0; 832 border-bottom-left-radius: 0;
840 margin: 1px; 833 border-bottom-right-radius: 4px;
834 margin: 0px;
841 width: 12px; 835 width: 12px;
842 margin-top: -1px; 836 margin-top: 0px;
843} 837}
844 838
845QAbstractSpinBox::down-arrow, QAbstractSpinBox::down-arrow:disabled, QAbstractSpinBox::down-arrow:off { 839QAbstractSpinBox::down-arrow, QAbstractSpinBox::down-arrow:disabled, QAbstractSpinBox::down-arrow:off {
846 image: url(":/qss_icons/rc/arrow_down_disabled.png"); 840 image: url(":/qss_icons/rc/down_arrow.png");
847 height: 8px; 841 height: 8px;
848 width: 8px; 842 width: 8px;
849} 843}
@@ -1199,6 +1193,7 @@ https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qcombobox
1199 1193
1200--------------------------------------------------------------------------- */ 1194--------------------------------------------------------------------------- */
1201QComboBox { 1195QComboBox {
1196 background-color: #0f1922;
1202 border: 1px solid #32414B; 1197 border: 1px solid #32414B;
1203 border-radius: 4px; 1198 border-radius: 4px;
1204 selection-background-color: #1464A0; 1199 selection-background-color: #1464A0;
@@ -1216,7 +1211,7 @@ QComboBox {
1216QComboBox QAbstractItemView { 1211QComboBox QAbstractItemView {
1217 border: 1px solid #32414B; 1212 border: 1px solid #32414B;
1218 border-radius: 0; 1213 border-radius: 0;
1219 background-color: #19232D; 1214 background-color: #0f1922;
1220 selection-background-color: #1464A0; 1215 selection-background-color: #1464A0;
1221} 1216}
1222 1217
@@ -1285,7 +1280,12 @@ QComboBox::drop-down {
1285} 1280}
1286 1281
1287QComboBox::down-arrow { 1282QComboBox::down-arrow {
1288 image: url(":/qss_icons/rc/arrow_down_disabled.png"); 1283 image: url(":/qss_icons/rc/down_arrow.png");
1284 background-color: #505F69;
1285 padding: 6px 2px;
1286 border: 1px solid #32414B;
1287 border-top-right-radius: 4px;
1288 border-bottom-right-radius: 4px;
1289 height: 8px; 1289 height: 8px;
1290 width: 8px; 1290 width: 8px;
1291} 1291}
@@ -1559,12 +1559,12 @@ QTabBar::tab:right:!selected {
1559QTabBar::tab:top { 1559QTabBar::tab:top {
1560 background-color: #32414B; 1560 background-color: #32414B;
1561 color: #F0F0F0; 1561 color: #F0F0F0;
1562 min-width: 36px;
1562 margin-left: 2px; 1563 margin-left: 2px;
1563 padding-left: 4px; 1564 padding-left: 8px;
1564 padding-right: 4px; 1565 padding-right: 8px;
1565 padding-top: 2px; 1566 padding-top: 2px;
1566 padding-bottom: 2px; 1567 padding-bottom: 2px;
1567 min-width: 5px;
1568 border-bottom: 3px solid #32414B; 1568 border-bottom: 3px solid #32414B;
1569 border-top-left-radius: 3px; 1569 border-top-left-radius: 3px;
1570 border-top-right-radius: 3px; 1570 border-top-right-radius: 3px;
@@ -1588,16 +1588,16 @@ QTabBar::tab:top:!selected:hover {
1588 1588
1589QTabBar::tab:bottom { 1589QTabBar::tab:bottom {
1590 color: #F0F0F0; 1590 color: #F0F0F0;
1591 min-width: 36px;
1591 border-top: 3px solid #32414B; 1592 border-top: 3px solid #32414B;
1592 background-color: #32414B; 1593 background-color: #32414B;
1593 margin-left: 2px; 1594 margin-left: 2px;
1594 padding-left: 4px; 1595 padding-left: 8px;
1595 padding-right: 4px; 1596 padding-right: 8px;
1596 padding-top: 2px; 1597 padding-top: 2px;
1597 padding-bottom: 2px; 1598 padding-bottom: 2px;
1598 border-bottom-left-radius: 3px; 1599 border-bottom-left-radius: 3px;
1599 border-bottom-right-radius: 3px; 1600 border-bottom-right-radius: 3px;
1600 min-width: 5px;
1601} 1601}
1602 1602
1603QTabBar::tab:bottom:selected { 1603QTabBar::tab:bottom:selected {
@@ -1752,21 +1752,6 @@ https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qlistview
1752https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qtableview 1752https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qtableview
1753 1753
1754--------------------------------------------------------------------------- */ 1754--------------------------------------------------------------------------- */
1755QTreeView:branch:selected, QTreeView:branch:hover {
1756 background: url(":/qss_icons/rc/transparent.png");
1757}
1758
1759QTreeView:branch:has-siblings:!adjoins-item {
1760 border-image: url(":/qss_icons/rc/branch_line.png") 0;
1761}
1762
1763QTreeView:branch:has-siblings:adjoins-item {
1764 border-image: url(":/qss_icons/rc/branch_more.png") 0;
1765}
1766
1767QTreeView:branch:!has-children:!has-siblings:adjoins-item {
1768 border-image: url(":/qss_icons/rc/branch_end.png") 0;
1769}
1770 1755
1771QTreeView:branch:has-children:!has-siblings:closed, QTreeView:branch:closed:has-children:has-siblings { 1756QTreeView:branch:has-children:!has-siblings:closed, QTreeView:branch:closed:has-children:has-siblings {
1772 border-image: none; 1757 border-image: none;
@@ -1900,21 +1885,21 @@ https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qheaderview
1900 1885
1901--------------------------------------------------------------------------- */ 1886--------------------------------------------------------------------------- */
1902QHeaderView { 1887QHeaderView {
1903 background-color: #32414B; 1888 background-color: #19232D;
1904 border: 0px transparent #32414B; 1889 border: 0px transparent #19232D;
1905 padding: 0px; 1890 padding: 0px;
1906 margin: 0px; 1891 margin: 0px;
1907 border-radius: 0px; 1892 border-radius: 0px;
1908} 1893}
1909 1894
1910QHeaderView:disabled { 1895QHeaderView:disabled {
1911 background-color: #32414B; 1896 background-color: #19232D;
1912 border: 1px transparent #32414B; 1897 border: 1px transparent #19232D;
1913 padding: 2px; 1898 padding: 2px;
1914} 1899}
1915 1900
1916QHeaderView::section { 1901QHeaderView::section {
1917 background-color: #32414B; 1902 background-color: #19232D;
1918 color: #F0F0F0; 1903 color: #F0F0F0;
1919 padding: 2px; 1904 padding: 2px;
1920 border-radius: 0px; 1905 border-radius: 0px;
@@ -1934,11 +1919,11 @@ QHeaderView::section:checked:disabled {
1934QHeaderView::section::horizontal { 1919QHeaderView::section::horizontal {
1935 padding-left: 4px; 1920 padding-left: 4px;
1936 padding-right: 4px; 1921 padding-right: 4px;
1937 border-left: 1px solid #19232D; 1922 border-left: 1px solid #32414B;
1938} 1923}
1939 1924
1940QHeaderView::section::horizontal::first, QHeaderView::section::horizontal::only-one { 1925QHeaderView::section::horizontal::first, QHeaderView::section::horizontal::only-one {
1941 border-left: 1px solid #32414B; 1926 border-left: 1px solid #19232D;
1942} 1927}
1943 1928
1944QHeaderView::section::horizontal:disabled { 1929QHeaderView::section::horizontal:disabled {
@@ -1948,7 +1933,7 @@ QHeaderView::section::horizontal:disabled {
1948QHeaderView::section::vertical { 1933QHeaderView::section::vertical {
1949 padding-left: 4px; 1934 padding-left: 4px;
1950 padding-right: 4px; 1935 padding-right: 4px;
1951 border-top: 1px solid #19232D; 1936 border-top: 1px solid #32414B;
1952} 1937}
1953 1938
1954QHeaderView::section::vertical::first, QHeaderView::section::vertical::only-one { 1939QHeaderView::section::vertical::first, QHeaderView::section::vertical::only-one {
@@ -1962,7 +1947,7 @@ QHeaderView::section::vertical:disabled {
1962QHeaderView::down-arrow { 1947QHeaderView::down-arrow {
1963 /* Those settings (border/width/height/background-color) solve bug */ 1948 /* Those settings (border/width/height/background-color) solve bug */
1964 /* transparent arrow background and size */ 1949 /* transparent arrow background and size */
1965 background-color: #32414B; 1950 background-color: #19232D;
1966 border: none; 1951 border: none;
1967 height: 12px; 1952 height: 12px;
1968 width: 12px; 1953 width: 12px;
@@ -1972,7 +1957,7 @@ QHeaderView::down-arrow {
1972} 1957}
1973 1958
1974QHeaderView::up-arrow { 1959QHeaderView::up-arrow {
1975 background-color: #32414B; 1960 background-color: #19232D;
1976 border: none; 1961 border: none;
1977 height: 12px; 1962 height: 12px;
1978 width: 12px; 1963 width: 12px;
@@ -2172,3 +2157,132 @@ PlotWidget {
2172 /* Fix cut labels in plots #134 */ 2157 /* Fix cut labels in plots #134 */
2173 padding: 0px; 2158 padding: 0px;
2174} 2159}
2160
2161
2162QPushButton#TogglableStatusBarButton {
2163 min-width: 0px;
2164 color: #656565;
2165 border: 1px solid transparent;
2166 background-color: transparent;
2167 padding: 0px 3px 0px 3px;
2168 text-align: center;
2169}
2170
2171QPushButton#TogglableStatusBarButton:checked {
2172 color: #ffffff;
2173}
2174
2175QPushButton#TogglableStatusBarButton:hover {
2176 border: 1px solid #76797C;
2177}
2178
2179QPushButton#RendererStatusBarButton {
2180 min-width: 0px;
2181 color: #656565;
2182 border: 1px solid transparent;
2183 background-color: transparent;
2184 padding: 0px 3px 0px 3px;
2185 text-align: center;
2186}
2187
2188QPushButton#RendererStatusBarButton:hover {
2189 border: 1px solid #76797C;
2190}
2191
2192QPushButton#RendererStatusBarButton:checked {
2193 color: #e85c00;
2194}
2195
2196QPushButton#RendererStatusBarButton:!checked {
2197 color: #00ccdd;
2198}
2199
2200QPushButton#buttonRefreshDevices {
2201 min-width: 20px;
2202 min-height: 20px;
2203 max-width: 20px;
2204 max-height: 20px;
2205 padding: 0px 0px;
2206}
2207
2208
2209QCheckBox#checkboxPlayer1Connected,
2210QCheckBox#checkboxPlayer2Connected,
2211QCheckBox#checkboxPlayer3Connected,
2212QCheckBox#checkboxPlayer4Connected,
2213QCheckBox#checkboxPlayer5Connected,
2214QCheckBox#checkboxPlayer6Connected,
2215QCheckBox#checkboxPlayer7Connected,
2216QCheckBox#checkboxPlayer8Connected {
2217 spacing: 0px;
2218}
2219
2220QCheckBox#checkboxPlayer1Connected::indicator,
2221QCheckBox#checkboxPlayer2Connected::indicator,
2222QCheckBox#checkboxPlayer3Connected::indicator,
2223QCheckBox#checkboxPlayer4Connected::indicator,
2224QCheckBox#checkboxPlayer5Connected::indicator,
2225QCheckBox#checkboxPlayer6Connected::indicator,
2226QCheckBox#checkboxPlayer7Connected::indicator,
2227QCheckBox#checkboxPlayer8Connected::indicator {
2228 width: 14px;
2229 height: 14px;
2230}
2231
2232QCheckBox#checkboxPlayer1Connected::indicator:checked,
2233QCheckBox#checkboxPlayer2Connected::indicator:checked,
2234QCheckBox#checkboxPlayer3Connected::indicator:checked,
2235QCheckBox#checkboxPlayer4Connected::indicator:checked,
2236QCheckBox#checkboxPlayer5Connected::indicator:checked,
2237QCheckBox#checkboxPlayer6Connected::indicator:checked,
2238QCheckBox#checkboxPlayer7Connected::indicator:checked,
2239QCheckBox#checkboxPlayer8Connected::indicator:checked,
2240QGroupBox#groupConnectedController::indicator:checked {
2241 border-radius: 2px;
2242 border: 1px solid #929192;
2243 background: #39ff14;
2244 image: none;
2245}
2246
2247QCheckBox#checkboxPlayer1Connected::indicator:unchecked,
2248QCheckBox#checkboxPlayer2Connected::indicator:unchecked,
2249QCheckBox#checkboxPlayer3Connected::indicator:unchecked,
2250QCheckBox#checkboxPlayer4Connected::indicator:unchecked,
2251QCheckBox#checkboxPlayer5Connected::indicator:unchecked,
2252QCheckBox#checkboxPlayer6Connected::indicator:unchecked,
2253QCheckBox#checkboxPlayer7Connected::indicator:unchecked,
2254QCheckBox#checkboxPlayer8Connected::indicator:unchecked,
2255QGroupBox#groupConnectedController::indicator:unchecked {
2256 border-radius: 2px;
2257 border: 1px solid #929192;
2258 background: transparent;
2259 image: none;
2260}
2261
2262QSpinBox#spinboxLStickRange,
2263QSpinBox#spinboxRStickRange {
2264 min-width: 38px;
2265}
2266
2267QGroupBox#motionGroup::indicator,
2268QGroupBox#vibrationGroup::indicator {
2269 margin-left: 0px;
2270}
2271
2272QGroupBox#motionGroup::title,
2273QGroupBox#vibrationGroup::title {
2274spacing: 2px;
2275 padding-left: 1px;
2276 padding-right: 1px;
2277}
2278
2279QListWidget#selectorList {
2280 background-color: #0f1922;
2281}
2282
2283QSpinBox,
2284QLineEdit,
2285QTreeView#hotkey_list,
2286QScrollArea#scrollArea QTreeView {
2287 background-color: #0f1922;
2288} \ No newline at end of file
diff --git a/externals/microprofile/microprofile.h b/externals/microprofile/microprofile.h
index 6dae65a66..85d5bd5de 100644
--- a/externals/microprofile/microprofile.h
+++ b/externals/microprofile/microprofile.h
@@ -1037,7 +1037,7 @@ static void MicroProfileCreateThreadLogKey()
1037#else 1037#else
1038MP_THREAD_LOCAL MicroProfileThreadLog* g_MicroProfileThreadLog = 0; 1038MP_THREAD_LOCAL MicroProfileThreadLog* g_MicroProfileThreadLog = 0;
1039#endif 1039#endif
1040static bool g_bUseLock = false; /// This is used because windows does not support using mutexes under dll init(which is where global initialization is handled) 1040static std::atomic<bool> g_bUseLock{false}; /// This is used because windows does not support using mutexes under dll init(which is where global initialization is handled)
1041 1041
1042 1042
1043MICROPROFILE_DEFINE(g_MicroProfileFlip, "MicroProfile", "MicroProfileFlip", 0x3355ee); 1043MICROPROFILE_DEFINE(g_MicroProfileFlip, "MicroProfile", "MicroProfileFlip", 0x3355ee);
diff --git a/src/common/assert.h b/src/common/assert.h
index 5b67c5c52..06d7b5612 100644
--- a/src/common/assert.h
+++ b/src/common/assert.h
@@ -17,11 +17,12 @@
17// enough for our purposes. 17// enough for our purposes.
18template <typename Fn> 18template <typename Fn>
19#if defined(_MSC_VER) 19#if defined(_MSC_VER)
20__declspec(noinline, noreturn) 20[[msvc::noinline, noreturn]]
21#elif defined(__GNUC__) 21#elif defined(__GNUC__)
22 __attribute__((noinline, noreturn, cold)) 22[[gnu::cold, gnu::noinline, noreturn]]
23#endif 23#endif
24 static void assert_noinline_call(const Fn& fn) { 24static void
25assert_noinline_call(const Fn& fn) {
25 fn(); 26 fn();
26 Crash(); 27 Crash();
27 exit(1); // Keeps GCC's mouth shut about this actually returning 28 exit(1); // Keeps GCC's mouth shut about this actually returning
diff --git a/src/common/param_package.h b/src/common/param_package.h
index c8a70bfa9..c13e45479 100644
--- a/src/common/param_package.h
+++ b/src/common/param_package.h
@@ -19,7 +19,7 @@ public:
19 explicit ParamPackage(const std::string& serialized); 19 explicit ParamPackage(const std::string& serialized);
20 ParamPackage(std::initializer_list<DataType::value_type> list); 20 ParamPackage(std::initializer_list<DataType::value_type> list);
21 ParamPackage(const ParamPackage& other) = default; 21 ParamPackage(const ParamPackage& other) = default;
22 ParamPackage(ParamPackage&& other) = default; 22 ParamPackage(ParamPackage&& other) noexcept = default;
23 23
24 ParamPackage& operator=(const ParamPackage& other) = default; 24 ParamPackage& operator=(const ParamPackage& other) = default;
25 ParamPackage& operator=(ParamPackage&& other) = default; 25 ParamPackage& operator=(ParamPackage&& other) = default;
diff --git a/src/common/thread.cpp b/src/common/thread.cpp
index 8e5935e6a..d2c1ac60d 100644
--- a/src/common/thread.cpp
+++ b/src/common/thread.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 "common/common_funcs.h"
6#include "common/logging/log.h"
5#include "common/thread.h" 7#include "common/thread.h"
6#ifdef __APPLE__ 8#ifdef __APPLE__
7#include <mach/mach.h> 9#include <mach/mach.h>
@@ -19,6 +21,8 @@
19#include <unistd.h> 21#include <unistd.h>
20#endif 22#endif
21 23
24#include <string>
25
22#ifdef __FreeBSD__ 26#ifdef __FreeBSD__
23#define cpu_set_t cpuset_t 27#define cpu_set_t cpuset_t
24#endif 28#endif
@@ -110,6 +114,14 @@ void SetCurrentThreadName(const char* name) {
110 pthread_set_name_np(pthread_self(), name); 114 pthread_set_name_np(pthread_self(), name);
111#elif defined(__NetBSD__) 115#elif defined(__NetBSD__)
112 pthread_setname_np(pthread_self(), "%s", (void*)name); 116 pthread_setname_np(pthread_self(), "%s", (void*)name);
117#elif defined(__linux__)
118 // Linux limits thread names to 15 characters and will outright reject any
119 // attempt to set a longer name with ERANGE.
120 std::string truncated(name, std::min(strlen(name), static_cast<size_t>(15)));
121 if (int e = pthread_setname_np(pthread_self(), truncated.c_str())) {
122 errno = e;
123 LOG_ERROR(Common, "Failed to set thread name to '{}': {}", truncated, GetLastErrorMsg());
124 }
113#else 125#else
114 pthread_setname_np(pthread_self(), name); 126 pthread_setname_np(pthread_self(), name);
115#endif 127#endif
diff --git a/src/common/thread.h b/src/common/thread.h
index 52b359413..a8c17c71a 100644
--- a/src/common/thread.h
+++ b/src/common/thread.h
@@ -4,6 +4,7 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <atomic>
7#include <chrono> 8#include <chrono>
8#include <condition_variable> 9#include <condition_variable>
9#include <cstddef> 10#include <cstddef>
@@ -25,13 +26,13 @@ public:
25 26
26 void Wait() { 27 void Wait() {
27 std::unique_lock lk{mutex}; 28 std::unique_lock lk{mutex};
28 condvar.wait(lk, [&] { return is_set; }); 29 condvar.wait(lk, [&] { return is_set.load(); });
29 is_set = false; 30 is_set = false;
30 } 31 }
31 32
32 bool WaitFor(const std::chrono::nanoseconds& time) { 33 bool WaitFor(const std::chrono::nanoseconds& time) {
33 std::unique_lock lk{mutex}; 34 std::unique_lock lk{mutex};
34 if (!condvar.wait_for(lk, time, [this] { return is_set; })) 35 if (!condvar.wait_for(lk, time, [this] { return is_set.load(); }))
35 return false; 36 return false;
36 is_set = false; 37 is_set = false;
37 return true; 38 return true;
@@ -40,7 +41,7 @@ public:
40 template <class Clock, class Duration> 41 template <class Clock, class Duration>
41 bool WaitUntil(const std::chrono::time_point<Clock, Duration>& time) { 42 bool WaitUntil(const std::chrono::time_point<Clock, Duration>& time) {
42 std::unique_lock lk{mutex}; 43 std::unique_lock lk{mutex};
43 if (!condvar.wait_until(lk, time, [this] { return is_set; })) 44 if (!condvar.wait_until(lk, time, [this] { return is_set.load(); }))
44 return false; 45 return false;
45 is_set = false; 46 is_set = false;
46 return true; 47 return true;
@@ -54,9 +55,9 @@ public:
54 } 55 }
55 56
56private: 57private:
57 bool is_set = false;
58 std::condition_variable condvar; 58 std::condition_variable condvar;
59 std::mutex mutex; 59 std::mutex mutex;
60 std::atomic_bool is_set{false};
60}; 61};
61 62
62class Barrier { 63class Barrier {
diff --git a/src/core/arm/cpu_interrupt_handler.cpp b/src/core/arm/cpu_interrupt_handler.cpp
index df0350881..9c8898700 100644
--- a/src/core/arm/cpu_interrupt_handler.cpp
+++ b/src/core/arm/cpu_interrupt_handler.cpp
@@ -7,9 +7,7 @@
7 7
8namespace Core { 8namespace Core {
9 9
10CPUInterruptHandler::CPUInterruptHandler() : is_interrupted{} { 10CPUInterruptHandler::CPUInterruptHandler() : interrupt_event{std::make_unique<Common::Event>()} {}
11 interrupt_event = std::make_unique<Common::Event>();
12}
13 11
14CPUInterruptHandler::~CPUInterruptHandler() = default; 12CPUInterruptHandler::~CPUInterruptHandler() = default;
15 13
@@ -17,7 +15,7 @@ void CPUInterruptHandler::SetInterrupt(bool is_interrupted_) {
17 if (is_interrupted_) { 15 if (is_interrupted_) {
18 interrupt_event->Set(); 16 interrupt_event->Set();
19 } 17 }
20 this->is_interrupted = is_interrupted_; 18 is_interrupted = is_interrupted_;
21} 19}
22 20
23void CPUInterruptHandler::AwaitInterrupt() { 21void CPUInterruptHandler::AwaitInterrupt() {
diff --git a/src/core/arm/cpu_interrupt_handler.h b/src/core/arm/cpu_interrupt_handler.h
index 3d062d326..71e582f79 100644
--- a/src/core/arm/cpu_interrupt_handler.h
+++ b/src/core/arm/cpu_interrupt_handler.h
@@ -4,6 +4,7 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <atomic>
7#include <memory> 8#include <memory>
8 9
9namespace Common { 10namespace Common {
@@ -32,8 +33,8 @@ public:
32 void AwaitInterrupt(); 33 void AwaitInterrupt();
33 34
34private: 35private:
35 bool is_interrupted{};
36 std::unique_ptr<Common::Event> interrupt_event; 36 std::unique_ptr<Common::Event> interrupt_event;
37 std::atomic_bool is_interrupted{false};
37}; 38};
38 39
39} // namespace Core 40} // namespace Core
diff --git a/src/core/cpu_manager.cpp b/src/core/cpu_manager.cpp
index ef0bae556..688b99eba 100644
--- a/src/core/cpu_manager.cpp
+++ b/src/core/cpu_manager.cpp
@@ -328,7 +328,7 @@ void CpuManager::RunThread(std::size_t core) {
328 system.RegisterCoreThread(core); 328 system.RegisterCoreThread(core);
329 std::string name; 329 std::string name;
330 if (is_multicore) { 330 if (is_multicore) {
331 name = "yuzu:CoreCPUThread_" + std::to_string(core); 331 name = "yuzu:CPUCore_" + std::to_string(core);
332 } else { 332 } else {
333 name = "yuzu:CPUThread"; 333 name = "yuzu:CPUThread";
334 } 334 }
diff --git a/src/core/frontend/framebuffer_layout.h b/src/core/frontend/framebuffer_layout.h
index 91ecc30ab..e2e3bbbb3 100644
--- a/src/core/frontend/framebuffer_layout.h
+++ b/src/core/frontend/framebuffer_layout.h
@@ -4,6 +4,7 @@
4 4
5#pragma once 5#pragma once
6 6
7#include "common/common_types.h"
7#include "common/math_util.h" 8#include "common/math_util.h"
8 9
9namespace Layout { 10namespace Layout {
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index cabe8d418..f2b0fe2fd 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -219,6 +219,7 @@ struct KernelCore::Impl {
219 return static_cast<u32>(system.GetCpuManager().CurrentCore()); 219 return static_cast<u32>(system.GetCpuManager().CurrentCore());
220 } 220 }
221 } 221 }
222 std::unique_lock lock{register_thread_mutex};
222 const auto it = host_thread_ids.find(this_id); 223 const auto it = host_thread_ids.find(this_id);
223 if (it == host_thread_ids.end()) { 224 if (it == host_thread_ids.end()) {
224 return Core::INVALID_HOST_THREAD_ID; 225 return Core::INVALID_HOST_THREAD_ID;
@@ -324,7 +325,7 @@ struct KernelCore::Impl {
324 std::unordered_map<std::thread::id, u32> host_thread_ids; 325 std::unordered_map<std::thread::id, u32> host_thread_ids;
325 u32 registered_thread_ids{Core::Hardware::NUM_CPU_CORES}; 326 u32 registered_thread_ids{Core::Hardware::NUM_CPU_CORES};
326 std::bitset<Core::Hardware::NUM_CPU_CORES> registered_core_threads; 327 std::bitset<Core::Hardware::NUM_CPU_CORES> registered_core_threads;
327 std::mutex register_thread_mutex; 328 mutable std::mutex register_thread_mutex;
328 329
329 // Kernel memory management 330 // Kernel memory management
330 std::unique_ptr<Memory::MemoryManager> memory_manager; 331 std::unique_ptr<Memory::MemoryManager> memory_manager;
diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp
index 0e7794dc7..45fde8df2 100644
--- a/src/core/hle/service/hid/controllers/npad.cpp
+++ b/src/core/hle/service/hid/controllers/npad.cpp
@@ -24,6 +24,7 @@ constexpr s32 HID_JOYSTICK_MAX = 0x7fff;
24constexpr std::size_t NPAD_OFFSET = 0x9A00; 24constexpr std::size_t NPAD_OFFSET = 0x9A00;
25constexpr u32 BATTERY_FULL = 2; 25constexpr u32 BATTERY_FULL = 2;
26constexpr u32 MAX_NPAD_ID = 7; 26constexpr u32 MAX_NPAD_ID = 7;
27constexpr std::size_t HANDHELD_INDEX = 8;
27constexpr std::array<u32, 10> npad_id_list{ 28constexpr std::array<u32, 10> npad_id_list{
28 0, 1, 2, 3, 4, 5, 6, 7, NPAD_HANDHELD, NPAD_UNKNOWN, 29 0, 1, 2, 3, 4, 5, 6, 7, NPAD_HANDHELD, NPAD_UNKNOWN,
29}; 30};
@@ -33,19 +34,41 @@ enum class JoystickId : std::size_t {
33 Joystick_Right, 34 Joystick_Right,
34}; 35};
35 36
36static Controller_NPad::NPadControllerType MapSettingsTypeToNPad(Settings::ControllerType type) { 37Controller_NPad::NPadControllerType Controller_NPad::MapSettingsTypeToNPad(
38 Settings::ControllerType type) {
37 switch (type) { 39 switch (type) {
38 case Settings::ControllerType::ProController: 40 case Settings::ControllerType::ProController:
39 return Controller_NPad::NPadControllerType::ProController; 41 return NPadControllerType::ProController;
40 case Settings::ControllerType::DualJoycon: 42 case Settings::ControllerType::DualJoyconDetached:
41 return Controller_NPad::NPadControllerType::JoyDual; 43 return NPadControllerType::JoyDual;
42 case Settings::ControllerType::LeftJoycon: 44 case Settings::ControllerType::LeftJoycon:
43 return Controller_NPad::NPadControllerType::JoyLeft; 45 return NPadControllerType::JoyLeft;
44 case Settings::ControllerType::RightJoycon: 46 case Settings::ControllerType::RightJoycon:
45 return Controller_NPad::NPadControllerType::JoyRight; 47 return NPadControllerType::JoyRight;
48 case Settings::ControllerType::Handheld:
49 return NPadControllerType::Handheld;
46 default: 50 default:
47 UNREACHABLE(); 51 UNREACHABLE();
48 return Controller_NPad::NPadControllerType::JoyDual; 52 return NPadControllerType::ProController;
53 }
54}
55
56Settings::ControllerType Controller_NPad::MapNPadToSettingsType(
57 Controller_NPad::NPadControllerType type) {
58 switch (type) {
59 case NPadControllerType::ProController:
60 return Settings::ControllerType::ProController;
61 case NPadControllerType::JoyDual:
62 return Settings::ControllerType::DualJoyconDetached;
63 case NPadControllerType::JoyLeft:
64 return Settings::ControllerType::LeftJoycon;
65 case NPadControllerType::JoyRight:
66 return Settings::ControllerType::RightJoycon;
67 case NPadControllerType::Handheld:
68 return Settings::ControllerType::Handheld;
69 default:
70 UNREACHABLE();
71 return Settings::ControllerType::ProController;
49 } 72 }
50} 73}
51 74
@@ -60,9 +83,9 @@ std::size_t Controller_NPad::NPadIdToIndex(u32 npad_id) {
60 case 6: 83 case 6:
61 case 7: 84 case 7:
62 return npad_id; 85 return npad_id;
63 case 8: 86 case HANDHELD_INDEX:
64 case NPAD_HANDHELD: 87 case NPAD_HANDHELD:
65 return 8; 88 return HANDHELD_INDEX;
66 case 9: 89 case 9:
67 case NPAD_UNKNOWN: 90 case NPAD_UNKNOWN:
68 return 9; 91 return 9;
@@ -83,7 +106,7 @@ u32 Controller_NPad::IndexToNPad(std::size_t index) {
83 case 6: 106 case 6:
84 case 7: 107 case 7:
85 return static_cast<u32>(index); 108 return static_cast<u32>(index);
86 case 8: 109 case HANDHELD_INDEX:
87 return NPAD_HANDHELD; 110 return NPAD_HANDHELD;
88 case 9: 111 case 9:
89 return NPAD_UNKNOWN; 112 return NPAD_UNKNOWN;
@@ -96,25 +119,35 @@ u32 Controller_NPad::IndexToNPad(std::size_t index) {
96Controller_NPad::Controller_NPad(Core::System& system) : ControllerBase(system), system(system) {} 119Controller_NPad::Controller_NPad(Core::System& system) : ControllerBase(system), system(system) {}
97Controller_NPad::~Controller_NPad() = default; 120Controller_NPad::~Controller_NPad() = default;
98 121
99void Controller_NPad::InitNewlyAddedControler(std::size_t controller_idx) { 122void Controller_NPad::InitNewlyAddedController(std::size_t controller_idx) {
100 const auto controller_type = connected_controllers[controller_idx].type; 123 const auto controller_type = connected_controllers[controller_idx].type;
101 auto& controller = shared_memory_entries[controller_idx]; 124 auto& controller = shared_memory_entries[controller_idx];
102 if (controller_type == NPadControllerType::None) { 125 if (controller_type == NPadControllerType::None) {
126 styleset_changed_events[controller_idx].writable->Signal();
103 return; 127 return;
104 } 128 }
105 controller.joy_styles.raw = 0; // Zero out 129 controller.joy_styles.raw = 0; // Zero out
106 controller.device_type.raw = 0; 130 controller.device_type.raw = 0;
131 controller.properties.raw = 0;
107 switch (controller_type) { 132 switch (controller_type) {
108 case NPadControllerType::None: 133 case NPadControllerType::None:
109 UNREACHABLE(); 134 UNREACHABLE();
110 break; 135 break;
136 case NPadControllerType::ProController:
137 controller.joy_styles.pro_controller.Assign(1);
138 controller.device_type.pro_controller.Assign(1);
139 controller.properties.is_vertical.Assign(1);
140 controller.properties.use_plus.Assign(1);
141 controller.properties.use_minus.Assign(1);
142 controller.pad_assignment = NPadAssignments::Single;
143 break;
111 case NPadControllerType::Handheld: 144 case NPadControllerType::Handheld:
112 controller.joy_styles.handheld.Assign(1); 145 controller.joy_styles.handheld.Assign(1);
113 controller.device_type.handheld.Assign(1); 146 controller.device_type.handheld.Assign(1);
114 controller.pad_assignment = NPadAssignments::Dual;
115 controller.properties.is_vertical.Assign(1); 147 controller.properties.is_vertical.Assign(1);
116 controller.properties.use_plus.Assign(1); 148 controller.properties.use_plus.Assign(1);
117 controller.properties.use_minus.Assign(1); 149 controller.properties.use_minus.Assign(1);
150 controller.pad_assignment = NPadAssignments::Dual;
118 break; 151 break;
119 case NPadControllerType::JoyDual: 152 case NPadControllerType::JoyDual:
120 controller.joy_styles.joycon_dual.Assign(1); 153 controller.joy_styles.joycon_dual.Assign(1);
@@ -144,14 +177,6 @@ void Controller_NPad::InitNewlyAddedControler(std::size_t controller_idx) {
144 controller.device_type.pokeball.Assign(1); 177 controller.device_type.pokeball.Assign(1);
145 controller.pad_assignment = NPadAssignments::Single; 178 controller.pad_assignment = NPadAssignments::Single;
146 break; 179 break;
147 case NPadControllerType::ProController:
148 controller.joy_styles.pro_controller.Assign(1);
149 controller.device_type.pro_controller.Assign(1);
150 controller.properties.is_vertical.Assign(1);
151 controller.properties.use_plus.Assign(1);
152 controller.properties.use_minus.Assign(1);
153 controller.pad_assignment = NPadAssignments::Single;
154 break;
155 } 180 }
156 181
157 controller.single_color_error = ColorReadError::ReadOk; 182 controller.single_color_error = ColorReadError::ReadOk;
@@ -192,36 +217,25 @@ void Controller_NPad::OnInit() {
192 style.pokeball.Assign(1); 217 style.pokeball.Assign(1);
193 } 218 }
194 219
195 std::transform( 220 std::transform(Settings::values.players.begin(), Settings::values.players.end(),
196 Settings::values.players.begin(), Settings::values.players.end(), 221 connected_controllers.begin(), [](const Settings::PlayerInput& player) {
197 connected_controllers.begin(), [](const Settings::PlayerInput& player) { 222 return ControllerHolder{MapSettingsTypeToNPad(player.controller_type),
198 return ControllerHolder{MapSettingsTypeToNPad(player.type), player.connected}; 223 player.connected};
199 }); 224 });
200
201 std::stable_partition(connected_controllers.begin(), connected_controllers.begin() + 8,
202 [](const ControllerHolder& holder) { return holder.is_connected; });
203 225
204 // Account for handheld 226 // Account for handheld
205 if (connected_controllers[8].is_connected) 227 if (connected_controllers[HANDHELD_INDEX].is_connected) {
206 connected_controllers[8].type = NPadControllerType::Handheld; 228 connected_controllers[HANDHELD_INDEX].type = NPadControllerType::Handheld;
229 }
207 230
208 supported_npad_id_types.resize(npad_id_list.size()); 231 supported_npad_id_types.resize(npad_id_list.size());
209 std::memcpy(supported_npad_id_types.data(), npad_id_list.data(), 232 std::memcpy(supported_npad_id_types.data(), npad_id_list.data(),
210 npad_id_list.size() * sizeof(u32)); 233 npad_id_list.size() * sizeof(u32));
211 234
212 // Add a default dual joycon controller if none are present.
213 if (std::none_of(connected_controllers.begin(), connected_controllers.end(),
214 [](const ControllerHolder& controller) { return controller.is_connected; })) {
215 supported_npad_id_types.resize(npad_id_list.size());
216 std::memcpy(supported_npad_id_types.data(), npad_id_list.data(),
217 npad_id_list.size() * sizeof(u32));
218 AddNewController(NPadControllerType::JoyDual);
219 }
220
221 for (std::size_t i = 0; i < connected_controllers.size(); ++i) { 235 for (std::size_t i = 0; i < connected_controllers.size(); ++i) {
222 const auto& controller = connected_controllers[i]; 236 const auto& controller = connected_controllers[i];
223 if (controller.is_connected) { 237 if (controller.is_connected) {
224 AddNewControllerAt(controller.type, IndexToNPad(i)); 238 AddNewControllerAt(controller.type, i);
225 } 239 }
226 } 240 }
227} 241}
@@ -309,8 +323,9 @@ void Controller_NPad::RequestPadStateUpdate(u32 npad_id) {
309 323
310void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, 324void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data,
311 std::size_t data_len) { 325 std::size_t data_len) {
312 if (!IsControllerActivated()) 326 if (!IsControllerActivated()) {
313 return; 327 return;
328 }
314 for (std::size_t i = 0; i < shared_memory_entries.size(); i++) { 329 for (std::size_t i = 0; i < shared_memory_entries.size(); i++) {
315 auto& npad = shared_memory_entries[i]; 330 auto& npad = shared_memory_entries[i];
316 const std::array<NPadGeneric*, 7> controller_npads{&npad.main_controller_states, 331 const std::array<NPadGeneric*, 7> controller_npads{&npad.main_controller_states,
@@ -360,13 +375,25 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8*
360 auto& libnx_entry = npad.libnx.npad[npad.libnx.common.last_entry_index]; 375 auto& libnx_entry = npad.libnx.npad[npad.libnx.common.last_entry_index];
361 376
362 libnx_entry.connection_status.raw = 0; 377 libnx_entry.connection_status.raw = 0;
378 libnx_entry.connection_status.IsConnected.Assign(1);
363 379
364 switch (controller_type) { 380 switch (controller_type) {
365 case NPadControllerType::None: 381 case NPadControllerType::None:
366 UNREACHABLE(); 382 UNREACHABLE();
367 break; 383 break;
384 case NPadControllerType::ProController:
385 main_controller.connection_status.raw = 0;
386 main_controller.connection_status.IsConnected.Assign(1);
387 main_controller.connection_status.IsWired.Assign(1);
388 main_controller.pad.pad_states.raw = pad_state.pad_states.raw;
389 main_controller.pad.l_stick = pad_state.l_stick;
390 main_controller.pad.r_stick = pad_state.r_stick;
391
392 libnx_entry.connection_status.IsWired.Assign(1);
393 break;
368 case NPadControllerType::Handheld: 394 case NPadControllerType::Handheld:
369 handheld_entry.connection_status.raw = 0; 395 handheld_entry.connection_status.raw = 0;
396 handheld_entry.connection_status.IsConnected.Assign(1);
370 handheld_entry.connection_status.IsWired.Assign(1); 397 handheld_entry.connection_status.IsWired.Assign(1);
371 handheld_entry.connection_status.IsLeftJoyConnected.Assign(1); 398 handheld_entry.connection_status.IsLeftJoyConnected.Assign(1);
372 handheld_entry.connection_status.IsRightJoyConnected.Assign(1); 399 handheld_entry.connection_status.IsRightJoyConnected.Assign(1);
@@ -375,57 +402,52 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8*
375 handheld_entry.pad.pad_states.raw = pad_state.pad_states.raw; 402 handheld_entry.pad.pad_states.raw = pad_state.pad_states.raw;
376 handheld_entry.pad.l_stick = pad_state.l_stick; 403 handheld_entry.pad.l_stick = pad_state.l_stick;
377 handheld_entry.pad.r_stick = pad_state.r_stick; 404 handheld_entry.pad.r_stick = pad_state.r_stick;
405
406 libnx_entry.connection_status.IsWired.Assign(1);
407 libnx_entry.connection_status.IsLeftJoyConnected.Assign(1);
408 libnx_entry.connection_status.IsRightJoyConnected.Assign(1);
409 libnx_entry.connection_status.IsLeftJoyWired.Assign(1);
410 libnx_entry.connection_status.IsRightJoyWired.Assign(1);
378 break; 411 break;
379 case NPadControllerType::JoyDual: 412 case NPadControllerType::JoyDual:
380 dual_entry.connection_status.raw = 0; 413 dual_entry.connection_status.raw = 0;
381 414 dual_entry.connection_status.IsConnected.Assign(1);
382 dual_entry.connection_status.IsLeftJoyConnected.Assign(1); 415 dual_entry.connection_status.IsLeftJoyConnected.Assign(1);
383 dual_entry.connection_status.IsRightJoyConnected.Assign(1); 416 dual_entry.connection_status.IsRightJoyConnected.Assign(1);
384 dual_entry.connection_status.IsConnected.Assign(1);
385
386 libnx_entry.connection_status.IsLeftJoyConnected.Assign(1);
387 libnx_entry.connection_status.IsRightJoyConnected.Assign(1);
388 libnx_entry.connection_status.IsConnected.Assign(1);
389
390 dual_entry.pad.pad_states.raw = pad_state.pad_states.raw; 417 dual_entry.pad.pad_states.raw = pad_state.pad_states.raw;
391 dual_entry.pad.l_stick = pad_state.l_stick; 418 dual_entry.pad.l_stick = pad_state.l_stick;
392 dual_entry.pad.r_stick = pad_state.r_stick; 419 dual_entry.pad.r_stick = pad_state.r_stick;
420
421 libnx_entry.connection_status.IsLeftJoyConnected.Assign(1);
422 libnx_entry.connection_status.IsRightJoyConnected.Assign(1);
393 break; 423 break;
394 case NPadControllerType::JoyLeft: 424 case NPadControllerType::JoyLeft:
395 left_entry.connection_status.raw = 0; 425 left_entry.connection_status.raw = 0;
396
397 left_entry.connection_status.IsConnected.Assign(1); 426 left_entry.connection_status.IsConnected.Assign(1);
427 left_entry.connection_status.IsLeftJoyConnected.Assign(1);
398 left_entry.pad.pad_states.raw = pad_state.pad_states.raw; 428 left_entry.pad.pad_states.raw = pad_state.pad_states.raw;
399 left_entry.pad.l_stick = pad_state.l_stick; 429 left_entry.pad.l_stick = pad_state.l_stick;
400 left_entry.pad.r_stick = pad_state.r_stick; 430 left_entry.pad.r_stick = pad_state.r_stick;
431
432 libnx_entry.connection_status.IsLeftJoyConnected.Assign(1);
401 break; 433 break;
402 case NPadControllerType::JoyRight: 434 case NPadControllerType::JoyRight:
403 right_entry.connection_status.raw = 0; 435 right_entry.connection_status.raw = 0;
404
405 right_entry.connection_status.IsConnected.Assign(1); 436 right_entry.connection_status.IsConnected.Assign(1);
437 right_entry.connection_status.IsRightJoyConnected.Assign(1);
406 right_entry.pad.pad_states.raw = pad_state.pad_states.raw; 438 right_entry.pad.pad_states.raw = pad_state.pad_states.raw;
407 right_entry.pad.l_stick = pad_state.l_stick; 439 right_entry.pad.l_stick = pad_state.l_stick;
408 right_entry.pad.r_stick = pad_state.r_stick; 440 right_entry.pad.r_stick = pad_state.r_stick;
441
442 libnx_entry.connection_status.IsRightJoyConnected.Assign(1);
409 break; 443 break;
410 case NPadControllerType::Pokeball: 444 case NPadControllerType::Pokeball:
411 pokeball_entry.connection_status.raw = 0; 445 pokeball_entry.connection_status.raw = 0;
412
413 pokeball_entry.connection_status.IsConnected.Assign(1); 446 pokeball_entry.connection_status.IsConnected.Assign(1);
414 pokeball_entry.connection_status.IsWired.Assign(1);
415
416 pokeball_entry.pad.pad_states.raw = pad_state.pad_states.raw; 447 pokeball_entry.pad.pad_states.raw = pad_state.pad_states.raw;
417 pokeball_entry.pad.l_stick = pad_state.l_stick; 448 pokeball_entry.pad.l_stick = pad_state.l_stick;
418 pokeball_entry.pad.r_stick = pad_state.r_stick; 449 pokeball_entry.pad.r_stick = pad_state.r_stick;
419 break; 450 break;
420 case NPadControllerType::ProController:
421 main_controller.connection_status.raw = 0;
422
423 main_controller.connection_status.IsConnected.Assign(1);
424 main_controller.connection_status.IsWired.Assign(1);
425 main_controller.pad.pad_states.raw = pad_state.pad_states.raw;
426 main_controller.pad.l_stick = pad_state.l_stick;
427 main_controller.pad.r_stick = pad_state.r_stick;
428 break;
429 } 451 }
430 452
431 // LibNX exclusively uses this section, so we always update it since LibNX doesn't activate 453 // LibNX exclusively uses this section, so we always update it since LibNX doesn't activate
@@ -453,26 +475,6 @@ void Controller_NPad::SetSupportedNPadIdTypes(u8* data, std::size_t length) {
453 supported_npad_id_types.clear(); 475 supported_npad_id_types.clear();
454 supported_npad_id_types.resize(length / sizeof(u32)); 476 supported_npad_id_types.resize(length / sizeof(u32));
455 std::memcpy(supported_npad_id_types.data(), data, length); 477 std::memcpy(supported_npad_id_types.data(), data, length);
456 for (std::size_t i = 0; i < connected_controllers.size(); i++) {
457 auto& controller = connected_controllers[i];
458 if (!controller.is_connected) {
459 continue;
460 }
461 const auto requested_controller =
462 i <= MAX_NPAD_ID ? MapSettingsTypeToNPad(Settings::values.players[i].type)
463 : NPadControllerType::Handheld;
464 if (!IsControllerSupported(requested_controller)) {
465 const auto is_handheld = requested_controller == NPadControllerType::Handheld;
466 if (is_handheld) {
467 controller.type = NPadControllerType::None;
468 controller.is_connected = false;
469 AddNewController(requested_controller);
470 } else {
471 controller.type = requested_controller;
472 InitNewlyAddedControler(i);
473 }
474 }
475 }
476} 478}
477 479
478void Controller_NPad::GetSupportedNpadIdTypes(u32* data, std::size_t max_length) { 480void Controller_NPad::GetSupportedNpadIdTypes(u32* data, std::size_t max_length) {
@@ -504,7 +506,7 @@ void Controller_NPad::VibrateController(const std::vector<u32>& controller_ids,
504 const std::vector<Vibration>& vibrations) { 506 const std::vector<Vibration>& vibrations) {
505 LOG_DEBUG(Service_HID, "(STUBBED) called"); 507 LOG_DEBUG(Service_HID, "(STUBBED) called");
506 508
507 if (!can_controllers_vibrate) { 509 if (!Settings::values.vibration_enabled || !can_controllers_vibrate) {
508 return; 510 return;
509 } 511 }
510 for (std::size_t i = 0; i < controller_ids.size(); i++) { 512 for (std::size_t i = 0; i < controller_ids.size(); i++) {
@@ -517,8 +519,6 @@ void Controller_NPad::VibrateController(const std::vector<u32>& controller_ids,
517} 519}
518 520
519std::shared_ptr<Kernel::ReadableEvent> Controller_NPad::GetStyleSetChangedEvent(u32 npad_id) const { 521std::shared_ptr<Kernel::ReadableEvent> Controller_NPad::GetStyleSetChangedEvent(u32 npad_id) const {
520 // TODO(ogniK): Figure out the best time to signal this event. This event seems that it should
521 // be signalled at least once, and signaled after a new controller is connected?
522 const auto& styleset_event = styleset_changed_events[NPadIdToIndex(npad_id)]; 522 const auto& styleset_event = styleset_changed_events[NPadIdToIndex(npad_id)];
523 return styleset_event.readable; 523 return styleset_event.readable;
524} 524}
@@ -527,43 +527,43 @@ Controller_NPad::Vibration Controller_NPad::GetLastVibration() const {
527 return last_processed_vibration; 527 return last_processed_vibration;
528} 528}
529 529
530void Controller_NPad::AddNewController(NPadControllerType controller) { 530void Controller_NPad::AddNewControllerAt(NPadControllerType controller, std::size_t npad_index) {
531 controller = DecideBestController(controller); 531 UpdateControllerAt(controller, npad_index, true);
532 if (controller == NPadControllerType::Handheld) { 532}
533 connected_controllers[8] = {controller, true}; 533
534 InitNewlyAddedControler(8); 534void Controller_NPad::UpdateControllerAt(NPadControllerType controller, std::size_t npad_index,
535 return; 535 bool connected) {
536 } 536 if (!connected) {
537 const auto pos = 537 DisconnectNPad(IndexToNPad(npad_index));
538 std::find_if(connected_controllers.begin(), connected_controllers.end() - 2,
539 [](const ControllerHolder& holder) { return !holder.is_connected; });
540 if (pos == connected_controllers.end() - 2) {
541 LOG_ERROR(Service_HID, "Cannot connect any more controllers!");
542 return; 538 return;
543 } 539 }
544 const auto controller_id = std::distance(connected_controllers.begin(), pos);
545 connected_controllers[controller_id] = {controller, true};
546 InitNewlyAddedControler(controller_id);
547}
548 540
549void Controller_NPad::AddNewControllerAt(NPadControllerType controller, u32 npad_id) {
550 controller = DecideBestController(controller);
551 if (controller == NPadControllerType::Handheld) { 541 if (controller == NPadControllerType::Handheld) {
552 connected_controllers[NPadIdToIndex(NPAD_HANDHELD)] = {controller, true}; 542 Settings::values.players[HANDHELD_INDEX].controller_type =
553 InitNewlyAddedControler(NPadIdToIndex(NPAD_HANDHELD)); 543 MapNPadToSettingsType(controller);
544 Settings::values.players[HANDHELD_INDEX].connected = true;
545 connected_controllers[HANDHELD_INDEX] = {controller, true};
546 InitNewlyAddedController(HANDHELD_INDEX);
554 return; 547 return;
555 } 548 }
556 549
557 connected_controllers[NPadIdToIndex(npad_id)] = {controller, true}; 550 Settings::values.players[npad_index].controller_type = MapNPadToSettingsType(controller);
558 InitNewlyAddedControler(NPadIdToIndex(npad_id)); 551 Settings::values.players[npad_index].connected = true;
559} 552 connected_controllers[npad_index] = {controller, true};
560 553 InitNewlyAddedController(npad_index);
561void Controller_NPad::ConnectNPad(u32 npad_id) {
562 connected_controllers[NPadIdToIndex(npad_id)].is_connected = true;
563} 554}
564 555
565void Controller_NPad::DisconnectNPad(u32 npad_id) { 556void Controller_NPad::DisconnectNPad(u32 npad_id) {
566 connected_controllers[NPadIdToIndex(npad_id)].is_connected = false; 557 const auto npad_index = NPadIdToIndex(npad_id);
558 connected_controllers[npad_index].is_connected = false;
559 Settings::values.players[npad_index].connected = false;
560
561 auto& controller = shared_memory_entries[npad_index];
562 controller.joy_styles.raw = 0; // Zero out
563 controller.device_type.raw = 0;
564 controller.properties.raw = 0;
565
566 styleset_changed_events[npad_index].writable->Signal();
567} 567}
568 568
569void Controller_NPad::SetGyroscopeZeroDriftMode(GyroscopeZeroDriftMode drift_mode) { 569void Controller_NPad::SetGyroscopeZeroDriftMode(GyroscopeZeroDriftMode drift_mode) {
@@ -599,8 +599,8 @@ bool Controller_NPad::SwapNpadAssignment(u32 npad_id_1, u32 npad_id_2) {
599 599
600 std::swap(connected_controllers[npad_index_1].type, connected_controllers[npad_index_2].type); 600 std::swap(connected_controllers[npad_index_1].type, connected_controllers[npad_index_2].type);
601 601
602 InitNewlyAddedControler(npad_index_1); 602 AddNewControllerAt(connected_controllers[npad_index_1].type, npad_index_1);
603 InitNewlyAddedControler(npad_index_2); 603 AddNewControllerAt(connected_controllers[npad_index_2].type, npad_index_2);
604 604
605 return true; 605 return true;
606} 606}
@@ -614,11 +614,11 @@ Controller_NPad::LedPattern Controller_NPad::GetLedPattern(u32 npad_id) {
614 case 0: 614 case 0:
615 return LedPattern{1, 0, 0, 0}; 615 return LedPattern{1, 0, 0, 0};
616 case 1: 616 case 1:
617 return LedPattern{0, 1, 0, 0}; 617 return LedPattern{1, 1, 0, 0};
618 case 2: 618 case 2:
619 return LedPattern{0, 0, 1, 0}; 619 return LedPattern{1, 1, 1, 0};
620 case 3: 620 case 3:
621 return LedPattern{0, 0, 0, 1}; 621 return LedPattern{1, 1, 1, 1};
622 case 4: 622 case 4:
623 return LedPattern{1, 0, 0, 1}; 623 return LedPattern{1, 0, 0, 1};
624 case 5: 624 case 5:
@@ -628,7 +628,6 @@ Controller_NPad::LedPattern Controller_NPad::GetLedPattern(u32 npad_id) {
628 case 7: 628 case 7:
629 return LedPattern{0, 1, 1, 0}; 629 return LedPattern{0, 1, 1, 0};
630 default: 630 default:
631 UNIMPLEMENTED_MSG("Unhandled npad_id {}", npad_id);
632 return LedPattern{0, 0, 0, 0}; 631 return LedPattern{0, 0, 0, 0};
633 } 632 }
634} 633}
diff --git a/src/core/hle/service/hid/controllers/npad.h b/src/core/hle/service/hid/controllers/npad.h
index 5d4c58a43..75ce5b731 100644
--- a/src/core/hle/service/hid/controllers/npad.h
+++ b/src/core/hle/service/hid/controllers/npad.h
@@ -118,10 +118,11 @@ public:
118 std::shared_ptr<Kernel::ReadableEvent> GetStyleSetChangedEvent(u32 npad_id) const; 118 std::shared_ptr<Kernel::ReadableEvent> GetStyleSetChangedEvent(u32 npad_id) const;
119 Vibration GetLastVibration() const; 119 Vibration GetLastVibration() const;
120 120
121 void AddNewController(NPadControllerType controller); 121 // Adds a new controller at an index.
122 void AddNewControllerAt(NPadControllerType controller, u32 npad_id); 122 void AddNewControllerAt(NPadControllerType controller, std::size_t npad_index);
123 // Adds a new controller at an index with connection status.
124 void UpdateControllerAt(NPadControllerType controller, std::size_t npad_index, bool connected);
123 125
124 void ConnectNPad(u32 npad_id);
125 void DisconnectNPad(u32 npad_id); 126 void DisconnectNPad(u32 npad_id);
126 void SetGyroscopeZeroDriftMode(GyroscopeZeroDriftMode drift_mode); 127 void SetGyroscopeZeroDriftMode(GyroscopeZeroDriftMode drift_mode);
127 GyroscopeZeroDriftMode GetGyroscopeZeroDriftMode() const; 128 GyroscopeZeroDriftMode GetGyroscopeZeroDriftMode() const;
@@ -141,6 +142,8 @@ public:
141 // Specifically for cheat engine and other features. 142 // Specifically for cheat engine and other features.
142 u32 GetAndResetPressState(); 143 u32 GetAndResetPressState();
143 144
145 static Controller_NPad::NPadControllerType MapSettingsTypeToNPad(Settings::ControllerType type);
146 static Settings::ControllerType MapNPadToSettingsType(Controller_NPad::NPadControllerType type);
144 static std::size_t NPadIdToIndex(u32 npad_id); 147 static std::size_t NPadIdToIndex(u32 npad_id);
145 static u32 IndexToNPad(std::size_t index); 148 static u32 IndexToNPad(std::size_t index);
146 149
@@ -309,7 +312,7 @@ private:
309 bool is_connected; 312 bool is_connected;
310 }; 313 };
311 314
312 void InitNewlyAddedControler(std::size_t controller_idx); 315 void InitNewlyAddedController(std::size_t controller_idx);
313 bool IsControllerSupported(NPadControllerType controller) const; 316 bool IsControllerSupported(NPadControllerType controller) const;
314 NPadControllerType DecideBestController(NPadControllerType priority) const; 317 NPadControllerType DecideBestController(NPadControllerType priority) const;
315 void RequestPadStateUpdate(u32 npad_id); 318 void RequestPadStateUpdate(u32 npad_id);
diff --git a/src/core/hle/service/hid/controllers/touchscreen.cpp b/src/core/hle/service/hid/controllers/touchscreen.cpp
index e326f8f5c..0df395e85 100644
--- a/src/core/hle/service/hid/controllers/touchscreen.cpp
+++ b/src/core/hle/service/hid/controllers/touchscreen.cpp
@@ -40,9 +40,14 @@ void Controller_Touchscreen::OnUpdate(const Core::Timing::CoreTiming& core_timin
40 cur_entry.sampling_number = last_entry.sampling_number + 1; 40 cur_entry.sampling_number = last_entry.sampling_number + 1;
41 cur_entry.sampling_number2 = cur_entry.sampling_number; 41 cur_entry.sampling_number2 = cur_entry.sampling_number;
42 42
43 const auto [x, y, pressed] = touch_device->GetStatus(); 43 bool pressed = false;
44 float x, y;
45 std::tie(x, y, pressed) = touch_device->GetStatus();
44 auto& touch_entry = cur_entry.states[0]; 46 auto& touch_entry = cur_entry.states[0];
45 touch_entry.attribute.raw = 0; 47 touch_entry.attribute.raw = 0;
48 if (!pressed && touch_btn_device) {
49 std::tie(x, y, pressed) = touch_btn_device->GetStatus();
50 }
46 if (pressed && Settings::values.touchscreen.enabled) { 51 if (pressed && Settings::values.touchscreen.enabled) {
47 touch_entry.x = static_cast<u16>(x * Layout::ScreenUndocked::Width); 52 touch_entry.x = static_cast<u16>(x * Layout::ScreenUndocked::Width);
48 touch_entry.y = static_cast<u16>(y * Layout::ScreenUndocked::Height); 53 touch_entry.y = static_cast<u16>(y * Layout::ScreenUndocked::Height);
@@ -63,5 +68,10 @@ void Controller_Touchscreen::OnUpdate(const Core::Timing::CoreTiming& core_timin
63 68
64void Controller_Touchscreen::OnLoadInputDevices() { 69void Controller_Touchscreen::OnLoadInputDevices() {
65 touch_device = Input::CreateDevice<Input::TouchDevice>(Settings::values.touchscreen.device); 70 touch_device = Input::CreateDevice<Input::TouchDevice>(Settings::values.touchscreen.device);
71 if (Settings::values.use_touch_from_button) {
72 touch_btn_device = Input::CreateDevice<Input::TouchDevice>("engine:touch_from_button");
73 } else {
74 touch_btn_device.reset();
75 }
66} 76}
67} // namespace Service::HID 77} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/touchscreen.h b/src/core/hle/service/hid/controllers/touchscreen.h
index a1d97269e..4d9042adc 100644
--- a/src/core/hle/service/hid/controllers/touchscreen.h
+++ b/src/core/hle/service/hid/controllers/touchscreen.h
@@ -68,6 +68,7 @@ private:
68 "TouchScreenSharedMemory is an invalid size"); 68 "TouchScreenSharedMemory is an invalid size");
69 TouchScreenSharedMemory shared_memory{}; 69 TouchScreenSharedMemory shared_memory{};
70 std::unique_ptr<Input::TouchDevice> touch_device; 70 std::unique_ptr<Input::TouchDevice> touch_device;
71 std::unique_ptr<Input::TouchDevice> touch_btn_device;
71 s64_le last_touch{}; 72 s64_le last_touch{};
72}; 73};
73} // namespace Service::HID 74} // namespace Service::HID
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp
index 1e95b7580..33416b5dd 100644
--- a/src/core/hle/service/hid/hid.cpp
+++ b/src/core/hle/service/hid/hid.cpp
@@ -38,11 +38,9 @@
38namespace Service::HID { 38namespace Service::HID {
39 39
40// Updating period for each HID device. 40// Updating period for each HID device.
41// TODO(ogniK): Find actual polling rate of hid 41// HID is polled every 15ms, this value was derived from
42constexpr auto pad_update_ns = std::chrono::nanoseconds{1000000000 / 66}; 42// https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering#joy-con-status-data-packet
43[[maybe_unused]] constexpr auto accelerometer_update_ns = 43constexpr auto pad_update_ns = std::chrono::nanoseconds{15 * 1000 * 1000}; // (15ms, 66.6Hz)
44 std::chrono::nanoseconds{1000000000 / 100};
45[[maybe_unused]] constexpr auto gyroscope_update_ticks = std::chrono::nanoseconds{1000000000 / 100};
46constexpr std::size_t SHARED_MEMORY_SIZE = 0x40000; 44constexpr std::size_t SHARED_MEMORY_SIZE = 0x40000;
47 45
48IAppletResource::IAppletResource(Core::System& system) 46IAppletResource::IAppletResource(Core::System& system)
@@ -845,8 +843,7 @@ void Hid::CreateActiveVibrationDeviceList(Kernel::HLERequestContext& ctx) {
845void Hid::PermitVibration(Kernel::HLERequestContext& ctx) { 843void Hid::PermitVibration(Kernel::HLERequestContext& ctx) {
846 IPC::RequestParser rp{ctx}; 844 IPC::RequestParser rp{ctx};
847 const auto can_vibrate{rp.Pop<bool>()}; 845 const auto can_vibrate{rp.Pop<bool>()};
848 applet_resource->GetController<Controller_NPad>(HidController::NPad) 846 Settings::values.vibration_enabled = can_vibrate;
849 .SetVibrationEnabled(can_vibrate);
850 847
851 LOG_DEBUG(Service_HID, "called, can_vibrate={}", can_vibrate); 848 LOG_DEBUG(Service_HID, "called, can_vibrate={}", can_vibrate);
852 849
@@ -859,8 +856,7 @@ void Hid::IsVibrationPermitted(Kernel::HLERequestContext& ctx) {
859 856
860 IPC::ResponseBuilder rb{ctx, 3}; 857 IPC::ResponseBuilder rb{ctx, 3};
861 rb.Push(RESULT_SUCCESS); 858 rb.Push(RESULT_SUCCESS);
862 rb.Push( 859 rb.Push(Settings::values.vibration_enabled);
863 applet_resource->GetController<Controller_NPad>(HidController::NPad).IsVibrationEnabled());
864} 860}
865 861
866void Hid::ActivateConsoleSixAxisSensor(Kernel::HLERequestContext& ctx) { 862void Hid::ActivateConsoleSixAxisSensor(Kernel::HLERequestContext& ctx) {
diff --git a/src/core/settings.cpp b/src/core/settings.cpp
index d328fb8b7..28d3f9099 100644
--- a/src/core/settings.cpp
+++ b/src/core/settings.cpp
@@ -13,56 +13,6 @@
13 13
14namespace Settings { 14namespace Settings {
15 15
16namespace NativeButton {
17const std::array<const char*, NumButtons> mapping = {{
18 "button_a",
19 "button_b",
20 "button_x",
21 "button_y",
22 "button_lstick",
23 "button_rstick",
24 "button_l",
25 "button_r",
26 "button_zl",
27 "button_zr",
28 "button_plus",
29 "button_minus",
30 "button_dleft",
31 "button_dup",
32 "button_dright",
33 "button_ddown",
34 "button_lstick_left",
35 "button_lstick_up",
36 "button_lstick_right",
37 "button_lstick_down",
38 "button_rstick_left",
39 "button_rstick_up",
40 "button_rstick_right",
41 "button_rstick_down",
42 "button_sl",
43 "button_sr",
44 "button_home",
45 "button_screenshot",
46}};
47}
48
49namespace NativeAnalog {
50const std::array<const char*, NumAnalogs> mapping = {{
51 "lstick",
52 "rstick",
53}};
54}
55
56namespace NativeMouseButton {
57const std::array<const char*, NumMouseButtons> mapping = {{
58 "left",
59 "right",
60 "middle",
61 "forward",
62 "back",
63}};
64}
65
66Values values = {}; 16Values values = {};
67bool configuring_global = true; 17bool configuring_global = true;
68 18
diff --git a/src/core/settings.h b/src/core/settings.h
index 3681b5e9d..80f0d95a7 100644
--- a/src/core/settings.h
+++ b/src/core/settings.h
@@ -12,340 +12,10 @@
12#include <string> 12#include <string>
13#include <vector> 13#include <vector>
14#include "common/common_types.h" 14#include "common/common_types.h"
15#include "input_common/settings.h"
15 16
16namespace Settings { 17namespace Settings {
17 18
18namespace NativeButton {
19enum Values {
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 LStick_Left,
39 LStick_Up,
40 LStick_Right,
41 LStick_Down,
42
43 RStick_Left,
44 RStick_Up,
45 RStick_Right,
46 RStick_Down,
47
48 SL,
49 SR,
50
51 Home,
52 Screenshot,
53
54 NumButtons,
55};
56
57constexpr int BUTTON_HID_BEGIN = A;
58constexpr int BUTTON_NS_BEGIN = Home;
59
60constexpr int BUTTON_HID_END = BUTTON_NS_BEGIN;
61constexpr int BUTTON_NS_END = NumButtons;
62
63constexpr int NUM_BUTTONS_HID = BUTTON_HID_END - BUTTON_HID_BEGIN;
64constexpr int NUM_BUTTONS_NS = BUTTON_NS_END - BUTTON_NS_BEGIN;
65
66extern const std::array<const char*, NumButtons> mapping;
67
68} // namespace NativeButton
69
70namespace NativeAnalog {
71enum Values {
72 LStick,
73 RStick,
74
75 NumAnalogs,
76};
77
78constexpr int STICK_HID_BEGIN = LStick;
79constexpr int STICK_HID_END = NumAnalogs;
80constexpr int NUM_STICKS_HID = NumAnalogs;
81
82extern const std::array<const char*, NumAnalogs> mapping;
83} // namespace NativeAnalog
84
85namespace NativeMouseButton {
86enum Values {
87 Left,
88 Right,
89 Middle,
90 Forward,
91 Back,
92
93 NumMouseButtons,
94};
95
96constexpr int MOUSE_HID_BEGIN = Left;
97constexpr int MOUSE_HID_END = NumMouseButtons;
98constexpr int NUM_MOUSE_HID = NumMouseButtons;
99
100extern const std::array<const char*, NumMouseButtons> mapping;
101} // namespace NativeMouseButton
102
103namespace NativeKeyboard {
104enum Keys {
105 None,
106 Error,
107
108 A = 4,
109 B,
110 C,
111 D,
112 E,
113 F,
114 G,
115 H,
116 I,
117 J,
118 K,
119 L,
120 M,
121 N,
122 O,
123 P,
124 Q,
125 R,
126 S,
127 T,
128 U,
129 V,
130 W,
131 X,
132 Y,
133 Z,
134 N1,
135 N2,
136 N3,
137 N4,
138 N5,
139 N6,
140 N7,
141 N8,
142 N9,
143 N0,
144 Enter,
145 Escape,
146 Backspace,
147 Tab,
148 Space,
149 Minus,
150 Equal,
151 LeftBrace,
152 RightBrace,
153 Backslash,
154 Tilde,
155 Semicolon,
156 Apostrophe,
157 Grave,
158 Comma,
159 Dot,
160 Slash,
161 CapsLockKey,
162
163 F1,
164 F2,
165 F3,
166 F4,
167 F5,
168 F6,
169 F7,
170 F8,
171 F9,
172 F10,
173 F11,
174 F12,
175
176 SystemRequest,
177 ScrollLockKey,
178 Pause,
179 Insert,
180 Home,
181 PageUp,
182 Delete,
183 End,
184 PageDown,
185 Right,
186 Left,
187 Down,
188 Up,
189
190 NumLockKey,
191 KPSlash,
192 KPAsterisk,
193 KPMinus,
194 KPPlus,
195 KPEnter,
196 KP1,
197 KP2,
198 KP3,
199 KP4,
200 KP5,
201 KP6,
202 KP7,
203 KP8,
204 KP9,
205 KP0,
206 KPDot,
207
208 Key102,
209 Compose,
210 Power,
211 KPEqual,
212
213 F13,
214 F14,
215 F15,
216 F16,
217 F17,
218 F18,
219 F19,
220 F20,
221 F21,
222 F22,
223 F23,
224 F24,
225
226 Open,
227 Help,
228 Properties,
229 Front,
230 Stop,
231 Repeat,
232 Undo,
233 Cut,
234 Copy,
235 Paste,
236 Find,
237 Mute,
238 VolumeUp,
239 VolumeDown,
240 CapsLockActive,
241 NumLockActive,
242 ScrollLockActive,
243 KPComma,
244
245 KPLeftParenthesis,
246 KPRightParenthesis,
247
248 LeftControlKey = 0xE0,
249 LeftShiftKey,
250 LeftAltKey,
251 LeftMetaKey,
252 RightControlKey,
253 RightShiftKey,
254 RightAltKey,
255 RightMetaKey,
256
257 MediaPlayPause,
258 MediaStopCD,
259 MediaPrevious,
260 MediaNext,
261 MediaEject,
262 MediaVolumeUp,
263 MediaVolumeDown,
264 MediaMute,
265 MediaWebsite,
266 MediaBack,
267 MediaForward,
268 MediaStop,
269 MediaFind,
270 MediaScrollUp,
271 MediaScrollDown,
272 MediaEdit,
273 MediaSleep,
274 MediaCoffee,
275 MediaRefresh,
276 MediaCalculator,
277
278 NumKeyboardKeys,
279};
280
281static_assert(NumKeyboardKeys == 0xFC, "Incorrect number of keyboard keys.");
282
283enum Modifiers {
284 LeftControl,
285 LeftShift,
286 LeftAlt,
287 LeftMeta,
288 RightControl,
289 RightShift,
290 RightAlt,
291 RightMeta,
292 CapsLock,
293 ScrollLock,
294 NumLock,
295
296 NumKeyboardMods,
297};
298
299constexpr int KEYBOARD_KEYS_HID_BEGIN = None;
300constexpr int KEYBOARD_KEYS_HID_END = NumKeyboardKeys;
301constexpr int NUM_KEYBOARD_KEYS_HID = NumKeyboardKeys;
302
303constexpr int KEYBOARD_MODS_HID_BEGIN = LeftControl;
304constexpr int KEYBOARD_MODS_HID_END = NumKeyboardMods;
305constexpr int NUM_KEYBOARD_MODS_HID = NumKeyboardMods;
306
307} // namespace NativeKeyboard
308
309using ButtonsRaw = std::array<std::string, NativeButton::NumButtons>;
310using AnalogsRaw = std::array<std::string, NativeAnalog::NumAnalogs>;
311using MouseButtonsRaw = std::array<std::string, NativeMouseButton::NumMouseButtons>;
312using KeyboardKeysRaw = std::array<std::string, NativeKeyboard::NumKeyboardKeys>;
313using KeyboardModsRaw = std::array<std::string, NativeKeyboard::NumKeyboardMods>;
314
315constexpr u32 JOYCON_BODY_NEON_RED = 0xFF3C28;
316constexpr u32 JOYCON_BUTTONS_NEON_RED = 0x1E0A0A;
317constexpr u32 JOYCON_BODY_NEON_BLUE = 0x0AB9E6;
318constexpr u32 JOYCON_BUTTONS_NEON_BLUE = 0x001E1E;
319
320enum class ControllerType {
321 ProController,
322 DualJoycon,
323 RightJoycon,
324 LeftJoycon,
325};
326
327struct PlayerInput {
328 bool connected;
329 ControllerType type;
330 ButtonsRaw buttons;
331 AnalogsRaw analogs;
332
333 u32 body_color_right;
334 u32 button_color_right;
335 u32 body_color_left;
336 u32 button_color_left;
337};
338
339struct TouchscreenInput {
340 bool enabled;
341 std::string device;
342
343 u32 finger;
344 u32 diameter_x;
345 u32 diameter_y;
346 u32 rotation_angle;
347};
348
349enum class RendererBackend { 19enum class RendererBackend {
350 OpenGL = 0, 20 OpenGL = 0,
351 Vulkan = 1, 21 Vulkan = 1,
@@ -397,6 +67,11 @@ private:
397 Type local{}; 67 Type local{};
398}; 68};
399 69
70struct TouchFromButtonMap {
71 std::string name;
72 std::vector<std::string> buttons;
73};
74
400struct Values { 75struct Values {
401 // Audio 76 // Audio
402 std::string audio_device_id; 77 std::string audio_device_id;
@@ -461,6 +136,8 @@ struct Values {
461 // Controls 136 // Controls
462 std::array<PlayerInput, 10> players; 137 std::array<PlayerInput, 10> players;
463 138
139 bool use_docked_mode;
140
464 bool mouse_enabled; 141 bool mouse_enabled;
465 std::string mouse_device; 142 std::string mouse_device;
466 MouseButtonsRaw mouse_buttons; 143 MouseButtonsRaw mouse_buttons;
@@ -473,14 +150,18 @@ struct Values {
473 ButtonsRaw debug_pad_buttons; 150 ButtonsRaw debug_pad_buttons;
474 AnalogsRaw debug_pad_analogs; 151 AnalogsRaw debug_pad_analogs;
475 152
153 bool vibration_enabled;
154
476 std::string motion_device; 155 std::string motion_device;
156 std::string touch_device;
477 TouchscreenInput touchscreen; 157 TouchscreenInput touchscreen;
478 std::atomic_bool is_device_reload_pending{true}; 158 std::atomic_bool is_device_reload_pending{true};
159 bool use_touch_from_button;
160 int touch_from_button_map_index;
479 std::string udp_input_address; 161 std::string udp_input_address;
480 u16 udp_input_port; 162 u16 udp_input_port;
481 u8 udp_pad_index; 163 u8 udp_pad_index;
482 164 std::vector<TouchFromButtonMap> touch_from_button_maps;
483 bool use_docked_mode;
484 165
485 // Data Storage 166 // Data Storage
486 bool use_virtual_sd; 167 bool use_virtual_sd;
diff --git a/src/input_common/CMakeLists.txt b/src/input_common/CMakeLists.txt
index 317c25bad..32433df25 100644
--- a/src/input_common/CMakeLists.txt
+++ b/src/input_common/CMakeLists.txt
@@ -7,6 +7,10 @@ add_library(input_common STATIC
7 main.h 7 main.h
8 motion_emu.cpp 8 motion_emu.cpp
9 motion_emu.h 9 motion_emu.h
10 settings.cpp
11 settings.h
12 touch_from_button.cpp
13 touch_from_button.h
10 gcadapter/gc_adapter.cpp 14 gcadapter/gc_adapter.cpp
11 gcadapter/gc_adapter.h 15 gcadapter/gc_adapter.h
12 gcadapter/gc_poller.cpp 16 gcadapter/gc_poller.cpp
diff --git a/src/input_common/gcadapter/gc_poller.cpp b/src/input_common/gcadapter/gc_poller.cpp
index b346fdf8e..71cd85eeb 100644
--- a/src/input_common/gcadapter/gc_poller.cpp
+++ b/src/input_common/gcadapter/gc_poller.cpp
@@ -191,7 +191,7 @@ public:
191 191
192 bool GetAnalogDirectionStatus(Input::AnalogDirection direction) const override { 192 bool GetAnalogDirectionStatus(Input::AnalogDirection direction) const override {
193 const auto [x, y] = GetStatus(); 193 const auto [x, y] = GetStatus();
194 const float directional_deadzone = 0.4f; 194 const float directional_deadzone = 0.5f;
195 switch (direction) { 195 switch (direction) {
196 case Input::AnalogDirection::RIGHT: 196 case Input::AnalogDirection::RIGHT:
197 return x > directional_deadzone; 197 return x > directional_deadzone;
@@ -232,7 +232,7 @@ std::unique_ptr<Input::AnalogDevice> GCAnalogFactory::Create(const Common::Param
232 const int port = params.Get("port", 0); 232 const int port = params.Get("port", 0);
233 const int axis_x = params.Get("axis_x", 0); 233 const int axis_x = params.Get("axis_x", 0);
234 const int axis_y = params.Get("axis_y", 1); 234 const int axis_y = params.Get("axis_y", 1);
235 const float deadzone = std::clamp(params.Get("deadzone", 0.0f), 0.0f, .99f); 235 const float deadzone = std::clamp(params.Get("deadzone", 0.0f), 0.0f, 1.0f);
236 const float range = std::clamp(params.Get("range", 1.0f), 0.50f, 1.50f); 236 const float range = std::clamp(params.Get("range", 1.0f), 0.50f, 1.50f);
237 237
238 return std::make_unique<GCAnalog>(port, axis_x, axis_y, deadzone, adapter.get(), range); 238 return std::make_unique<GCAnalog>(port, axis_x, axis_y, deadzone, adapter.get(), range);
diff --git a/src/input_common/main.cpp b/src/input_common/main.cpp
index b9d5d0ec3..ea1a1cee6 100644
--- a/src/input_common/main.cpp
+++ b/src/input_common/main.cpp
@@ -11,6 +11,7 @@
11#include "input_common/keyboard.h" 11#include "input_common/keyboard.h"
12#include "input_common/main.h" 12#include "input_common/main.h"
13#include "input_common/motion_emu.h" 13#include "input_common/motion_emu.h"
14#include "input_common/touch_from_button.h"
14#include "input_common/udp/udp.h" 15#include "input_common/udp/udp.h"
15#ifdef HAVE_SDL2 16#ifdef HAVE_SDL2
16#include "input_common/sdl/sdl.h" 17#include "input_common/sdl/sdl.h"
@@ -18,67 +19,176 @@
18 19
19namespace InputCommon { 20namespace InputCommon {
20 21
21static std::shared_ptr<Keyboard> keyboard; 22struct InputSubsystem::Impl {
22static std::shared_ptr<MotionEmu> motion_emu; 23 void Initialize() {
24 auto gcadapter = std::make_shared<GCAdapter::Adapter>();
25 gcbuttons = std::make_shared<GCButtonFactory>(gcadapter);
26 Input::RegisterFactory<Input::ButtonDevice>("gcpad", gcbuttons);
27 gcanalog = std::make_shared<GCAnalogFactory>(gcadapter);
28 Input::RegisterFactory<Input::AnalogDevice>("gcpad", gcanalog);
29
30 keyboard = std::make_shared<Keyboard>();
31 Input::RegisterFactory<Input::ButtonDevice>("keyboard", keyboard);
32 Input::RegisterFactory<Input::AnalogDevice>("analog_from_button",
33 std::make_shared<AnalogFromButton>());
34 motion_emu = std::make_shared<MotionEmu>();
35 Input::RegisterFactory<Input::MotionDevice>("motion_emu", motion_emu);
36 Input::RegisterFactory<Input::TouchDevice>("touch_from_button",
37 std::make_shared<TouchFromButtonFactory>());
38
23#ifdef HAVE_SDL2 39#ifdef HAVE_SDL2
24static std::unique_ptr<SDL::State> sdl; 40 sdl = SDL::Init();
25#endif 41#endif
26static std::unique_ptr<CemuhookUDP::State> udp;
27static std::shared_ptr<GCButtonFactory> gcbuttons;
28static std::shared_ptr<GCAnalogFactory> gcanalog;
29
30void Init() {
31 auto gcadapter = std::make_shared<GCAdapter::Adapter>();
32 gcbuttons = std::make_shared<GCButtonFactory>(gcadapter);
33 Input::RegisterFactory<Input::ButtonDevice>("gcpad", gcbuttons);
34 gcanalog = std::make_shared<GCAnalogFactory>(gcadapter);
35 Input::RegisterFactory<Input::AnalogDevice>("gcpad", gcanalog);
36
37 keyboard = std::make_shared<Keyboard>();
38 Input::RegisterFactory<Input::ButtonDevice>("keyboard", keyboard);
39 Input::RegisterFactory<Input::AnalogDevice>("analog_from_button",
40 std::make_shared<AnalogFromButton>());
41 motion_emu = std::make_shared<MotionEmu>();
42 Input::RegisterFactory<Input::MotionDevice>("motion_emu", motion_emu);
43 42
43 udp = CemuhookUDP::Init();
44 }
45
46 void Shutdown() {
47 Input::UnregisterFactory<Input::ButtonDevice>("keyboard");
48 keyboard.reset();
49 Input::UnregisterFactory<Input::AnalogDevice>("analog_from_button");
50 Input::UnregisterFactory<Input::MotionDevice>("motion_emu");
51 motion_emu.reset();
52 Input::UnregisterFactory<Input::TouchDevice>("touch_from_button");
44#ifdef HAVE_SDL2 53#ifdef HAVE_SDL2
45 sdl = SDL::Init(); 54 sdl.reset();
46#endif 55#endif
56 udp.reset();
57 Input::UnregisterFactory<Input::ButtonDevice>("gcpad");
58 Input::UnregisterFactory<Input::AnalogDevice>("gcpad");
59
60 gcbuttons.reset();
61 gcanalog.reset();
62 }
63
64 [[nodiscard]] std::vector<Common::ParamPackage> GetInputDevices() const {
65 std::vector<Common::ParamPackage> devices = {
66 Common::ParamPackage{{"display", "Any"}, {"class", "any"}},
67 Common::ParamPackage{{"display", "Keyboard/Mouse"}, {"class", "key"}},
68 };
69#ifdef HAVE_SDL2
70 auto sdl_devices = sdl->GetInputDevices();
71 devices.insert(devices.end(), sdl_devices.begin(), sdl_devices.end());
72#endif
73 auto udp_devices = udp->GetInputDevices();
74 devices.insert(devices.end(), udp_devices.begin(), udp_devices.end());
75 return devices;
76 }
77
78 [[nodiscard]] AnalogMapping GetAnalogMappingForDevice(
79 const Common::ParamPackage& params) const {
80 if (!params.Has("class") || params.Get("class", "") == "any") {
81 return {};
82 }
83 if (params.Get("class", "") == "key") {
84 // TODO consider returning the SDL key codes for the default keybindings
85 return {};
86 }
87#ifdef HAVE_SDL2
88 if (params.Get("class", "") == "sdl") {
89 return sdl->GetAnalogMappingForDevice(params);
90 }
91#endif
92 return {};
93 }
94
95 [[nodiscard]] ButtonMapping GetButtonMappingForDevice(
96 const Common::ParamPackage& params) const {
97 if (!params.Has("class") || params.Get("class", "") == "any") {
98 return {};
99 }
100 if (params.Get("class", "") == "key") {
101 // TODO consider returning the SDL key codes for the default keybindings
102 return {};
103 }
104#ifdef HAVE_SDL2
105 if (params.Get("class", "") == "sdl") {
106 return sdl->GetButtonMappingForDevice(params);
107 }
108#endif
109 return {};
110 }
47 111
48 udp = CemuhookUDP::Init(); 112 std::shared_ptr<Keyboard> keyboard;
49} 113 std::shared_ptr<MotionEmu> motion_emu;
50
51void Shutdown() {
52 Input::UnregisterFactory<Input::ButtonDevice>("keyboard");
53 keyboard.reset();
54 Input::UnregisterFactory<Input::AnalogDevice>("analog_from_button");
55 Input::UnregisterFactory<Input::MotionDevice>("motion_emu");
56 motion_emu.reset();
57#ifdef HAVE_SDL2 114#ifdef HAVE_SDL2
58 sdl.reset(); 115 std::unique_ptr<SDL::State> sdl;
59#endif 116#endif
60 udp.reset(); 117 std::unique_ptr<CemuhookUDP::State> udp;
61 Input::UnregisterFactory<Input::ButtonDevice>("gcpad"); 118 std::shared_ptr<GCButtonFactory> gcbuttons;
62 Input::UnregisterFactory<Input::AnalogDevice>("gcpad"); 119 std::shared_ptr<GCAnalogFactory> gcanalog;
120};
121
122InputSubsystem::InputSubsystem() : impl{std::make_unique<Impl>()} {}
123
124InputSubsystem::~InputSubsystem() = default;
125
126void InputSubsystem::Initialize() {
127 impl->Initialize();
128}
129
130void InputSubsystem::Shutdown() {
131 impl->Shutdown();
132}
133
134Keyboard* InputSubsystem::GetKeyboard() {
135 return impl->keyboard.get();
136}
137
138const Keyboard* InputSubsystem::GetKeyboard() const {
139 return impl->keyboard.get();
140}
141
142MotionEmu* InputSubsystem::GetMotionEmu() {
143 return impl->motion_emu.get();
144}
145
146const MotionEmu* InputSubsystem::GetMotionEmu() const {
147 return impl->motion_emu.get();
148}
63 149
64 gcbuttons.reset(); 150std::vector<Common::ParamPackage> InputSubsystem::GetInputDevices() const {
65 gcanalog.reset(); 151 return impl->GetInputDevices();
66} 152}
67 153
68Keyboard* GetKeyboard() { 154AnalogMapping InputSubsystem::GetAnalogMappingForDevice(const Common::ParamPackage& device) const {
69 return keyboard.get(); 155 return impl->GetAnalogMappingForDevice(device);
70} 156}
71 157
72MotionEmu* GetMotionEmu() { 158ButtonMapping InputSubsystem::GetButtonMappingForDevice(const Common::ParamPackage& device) const {
73 return motion_emu.get(); 159 return impl->GetButtonMappingForDevice(device);
74} 160}
75 161
76GCButtonFactory* GetGCButtons() { 162GCAnalogFactory* InputSubsystem::GetGCAnalogs() {
77 return gcbuttons.get(); 163 return impl->gcanalog.get();
78} 164}
79 165
80GCAnalogFactory* GetGCAnalogs() { 166const GCAnalogFactory* InputSubsystem::GetGCAnalogs() const {
81 return gcanalog.get(); 167 return impl->gcanalog.get();
168}
169
170GCButtonFactory* InputSubsystem::GetGCButtons() {
171 return impl->gcbuttons.get();
172}
173
174const GCButtonFactory* InputSubsystem::GetGCButtons() const {
175 return impl->gcbuttons.get();
176}
177
178void InputSubsystem::ReloadInputDevices() {
179 if (!impl->udp) {
180 return;
181 }
182 impl->udp->ReloadUDPClient();
183}
184
185std::vector<std::unique_ptr<Polling::DevicePoller>> InputSubsystem::GetPollers(
186 Polling::DeviceType type) const {
187#ifdef HAVE_SDL2
188 return impl->sdl->GetPollers(type);
189#else
190 return {};
191#endif
82} 192}
83 193
84std::string GenerateKeyboardParam(int key_code) { 194std::string GenerateKeyboardParam(int key_code) {
@@ -102,18 +212,4 @@ std::string GenerateAnalogParamFromKeys(int key_up, int key_down, int key_left,
102 }; 212 };
103 return circle_pad_param.Serialize(); 213 return circle_pad_param.Serialize();
104} 214}
105
106namespace Polling {
107
108std::vector<std::unique_ptr<DevicePoller>> GetPollers(DeviceType type) {
109 std::vector<std::unique_ptr<DevicePoller>> pollers;
110
111#ifdef HAVE_SDL2
112 pollers = sdl->GetPollers(type);
113#endif
114
115 return pollers;
116}
117
118} // namespace Polling
119} // namespace InputCommon 215} // namespace InputCommon
diff --git a/src/input_common/main.h b/src/input_common/main.h
index 0e32856f6..f3fbf696e 100644
--- a/src/input_common/main.h
+++ b/src/input_common/main.h
@@ -6,45 +6,25 @@
6 6
7#include <memory> 7#include <memory>
8#include <string> 8#include <string>
9#include <unordered_map>
9#include <vector> 10#include <vector>
10#include "input_common/gcadapter/gc_poller.h"
11 11
12namespace Common { 12namespace Common {
13class ParamPackage; 13class ParamPackage;
14} 14}
15 15
16namespace InputCommon { 16namespace Settings::NativeAnalog {
17 17enum Values : int;
18/// Initializes and registers all built-in input device factories. 18}
19void Init();
20
21/// Deregisters all built-in input device factories and shuts them down.
22void Shutdown();
23
24class Keyboard;
25
26/// Gets the keyboard button device factory.
27Keyboard* GetKeyboard();
28
29class MotionEmu;
30
31/// Gets the motion emulation factory.
32MotionEmu* GetMotionEmu();
33
34GCButtonFactory* GetGCButtons();
35
36GCAnalogFactory* GetGCAnalogs();
37
38/// Generates a serialized param package for creating a keyboard button device
39std::string GenerateKeyboardParam(int key_code);
40 19
41/// Generates a serialized param package for creating an analog device taking input from keyboard 20namespace Settings::NativeButton {
42std::string GenerateAnalogParamFromKeys(int key_up, int key_down, int key_left, int key_right, 21enum Values : int;
43 int key_modifier, float modifier_scale); 22}
44 23
24namespace InputCommon {
45namespace Polling { 25namespace Polling {
46 26
47enum class DeviceType { Button, Analog }; 27enum class DeviceType { Button, AnalogPreferred };
48 28
49/** 29/**
50 * A class that can be used to get inputs from an input device like controllers without having to 30 * A class that can be used to get inputs from an input device like controllers without having to
@@ -54,7 +34,9 @@ class DevicePoller {
54public: 34public:
55 virtual ~DevicePoller() = default; 35 virtual ~DevicePoller() = default;
56 /// Setup and start polling for inputs, should be called before GetNextInput 36 /// Setup and start polling for inputs, should be called before GetNextInput
57 virtual void Start() = 0; 37 /// If a device_id is provided, events should be filtered to only include events from this
38 /// device id
39 virtual void Start(const std::string& device_id = "") = 0;
58 /// Stop polling 40 /// Stop polling
59 virtual void Stop() = 0; 41 virtual void Stop() = 0;
60 /** 42 /**
@@ -64,8 +46,92 @@ public:
64 */ 46 */
65 virtual Common::ParamPackage GetNextInput() = 0; 47 virtual Common::ParamPackage GetNextInput() = 0;
66}; 48};
67
68// Get all DevicePoller from all backends for a specific device type
69std::vector<std::unique_ptr<DevicePoller>> GetPollers(DeviceType type);
70} // namespace Polling 49} // namespace Polling
50
51class GCAnalogFactory;
52class GCButtonFactory;
53class Keyboard;
54class MotionEmu;
55
56/**
57 * Given a ParamPackage for a Device returned from `GetInputDevices`, attempt to get the default
58 * mapping for the device. This is currently only implemented for the SDL backend devices.
59 */
60using AnalogMapping = std::unordered_map<Settings::NativeAnalog::Values, Common::ParamPackage>;
61using ButtonMapping = std::unordered_map<Settings::NativeButton::Values, Common::ParamPackage>;
62
63class InputSubsystem {
64public:
65 explicit InputSubsystem();
66 ~InputSubsystem();
67
68 InputSubsystem(const InputSubsystem&) = delete;
69 InputSubsystem& operator=(const InputSubsystem&) = delete;
70
71 InputSubsystem(InputSubsystem&&) = delete;
72 InputSubsystem& operator=(InputSubsystem&&) = delete;
73
74 /// Initializes and registers all built-in input device factories.
75 void Initialize();
76
77 /// Unregisters all built-in input device factories and shuts them down.
78 void Shutdown();
79
80 /// Retrieves the underlying keyboard device.
81 [[nodiscard]] Keyboard* GetKeyboard();
82
83 /// Retrieves the underlying keyboard device.
84 [[nodiscard]] const Keyboard* GetKeyboard() const;
85
86 /// Retrieves the underlying motion emulation factory.
87 [[nodiscard]] MotionEmu* GetMotionEmu();
88
89 /// Retrieves the underlying motion emulation factory.
90 [[nodiscard]] const MotionEmu* GetMotionEmu() const;
91
92 /**
93 * Returns all available input devices that this Factory can create a new device with.
94 * Each returned ParamPackage should have a `display` field used for display, a class field for
95 * backends to determine if this backend is meant to service the request and any other
96 * information needed to identify this in the backend later.
97 */
98 [[nodiscard]] std::vector<Common::ParamPackage> GetInputDevices() const;
99
100 /// Retrieves the analog mappings for the given device.
101 [[nodiscard]] AnalogMapping GetAnalogMappingForDevice(const Common::ParamPackage& device) const;
102
103 /// Retrieves the button mappings for the given device.
104 [[nodiscard]] ButtonMapping GetButtonMappingForDevice(const Common::ParamPackage& device) const;
105
106 /// Retrieves the underlying GameCube analog handler.
107 [[nodiscard]] GCAnalogFactory* GetGCAnalogs();
108
109 /// Retrieves the underlying GameCube analog handler.
110 [[nodiscard]] const GCAnalogFactory* GetGCAnalogs() const;
111
112 /// Retrieves the underlying GameCube button handler.
113 [[nodiscard]] GCButtonFactory* GetGCButtons();
114
115 /// Retrieves the underlying GameCube button handler.
116 [[nodiscard]] const GCButtonFactory* GetGCButtons() const;
117
118 /// Reloads the input devices
119 void ReloadInputDevices();
120
121 /// Get all DevicePoller from all backends for a specific device type
122 [[nodiscard]] std::vector<std::unique_ptr<Polling::DevicePoller>> GetPollers(
123 Polling::DeviceType type) const;
124
125private:
126 struct Impl;
127 std::unique_ptr<Impl> impl;
128};
129
130/// Generates a serialized param package for creating a keyboard button device
131std::string GenerateKeyboardParam(int key_code);
132
133/// Generates a serialized param package for creating an analog device taking input from keyboard
134std::string GenerateAnalogParamFromKeys(int key_up, int key_down, int key_left, int key_right,
135 int key_modifier, float modifier_scale);
136
71} // namespace InputCommon 137} // namespace InputCommon
diff --git a/src/input_common/sdl/sdl.h b/src/input_common/sdl/sdl.h
index 5306daa70..f3554be9a 100644
--- a/src/input_common/sdl/sdl.h
+++ b/src/input_common/sdl/sdl.h
@@ -6,6 +6,7 @@
6 6
7#include <memory> 7#include <memory>
8#include <vector> 8#include <vector>
9#include "common/param_package.h"
9#include "input_common/main.h" 10#include "input_common/main.h"
10 11
11namespace InputCommon::Polling { 12namespace InputCommon::Polling {
@@ -22,14 +23,24 @@ public:
22 /// Unregisters SDL device factories and shut them down. 23 /// Unregisters SDL device factories and shut them down.
23 virtual ~State() = default; 24 virtual ~State() = default;
24 25
25 virtual Pollers GetPollers(Polling::DeviceType type) = 0; 26 virtual Pollers GetPollers(Polling::DeviceType type) {
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 }
26}; 40};
27 41
28class NullState : public State { 42class NullState : public State {
29public: 43public:
30 Pollers GetPollers(Polling::DeviceType type) override {
31 return {};
32 }
33}; 44};
34 45
35std::unique_ptr<State> Init(); 46std::unique_ptr<State> Init();
diff --git a/src/input_common/sdl/sdl_impl.cpp b/src/input_common/sdl/sdl_impl.cpp
index d76c279d3..a9e676f4b 100644
--- a/src/input_common/sdl/sdl_impl.cpp
+++ b/src/input_common/sdl/sdl_impl.cpp
@@ -3,10 +3,13 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <algorithm> 5#include <algorithm>
6#include <array>
6#include <atomic> 7#include <atomic>
7#include <cmath> 8#include <cmath>
8#include <functional> 9#include <functional>
9#include <mutex> 10#include <mutex>
11#include <optional>
12#include <sstream>
10#include <string> 13#include <string>
11#include <thread> 14#include <thread>
12#include <tuple> 15#include <tuple>
@@ -15,15 +18,16 @@
15#include <vector> 18#include <vector>
16#include <SDL.h> 19#include <SDL.h>
17#include "common/logging/log.h" 20#include "common/logging/log.h"
18#include "common/math_util.h"
19#include "common/param_package.h" 21#include "common/param_package.h"
20#include "common/threadsafe_queue.h" 22#include "common/threadsafe_queue.h"
21#include "core/frontend/input.h" 23#include "core/frontend/input.h"
22#include "input_common/sdl/sdl_impl.h" 24#include "input_common/sdl/sdl_impl.h"
25#include "input_common/settings.h"
23 26
24namespace InputCommon::SDL { 27namespace InputCommon::SDL {
25 28
26static std::string GetGUID(SDL_Joystick* joystick) { 29namespace {
30std::string GetGUID(SDL_Joystick* joystick) {
27 const SDL_JoystickGUID guid = SDL_JoystickGetGUID(joystick); 31 const SDL_JoystickGUID guid = SDL_JoystickGetGUID(joystick);
28 char guid_str[33]; 32 char guid_str[33];
29 SDL_JoystickGetGUIDString(guid, guid_str, sizeof(guid_str)); 33 SDL_JoystickGetGUIDString(guid, guid_str, sizeof(guid_str));
@@ -31,7 +35,8 @@ static std::string GetGUID(SDL_Joystick* joystick) {
31} 35}
32 36
33/// Creates a ParamPackage from an SDL_Event that can directly be used to create a ButtonDevice 37/// Creates a ParamPackage from an SDL_Event that can directly be used to create a ButtonDevice
34static Common::ParamPackage SDLEventToButtonParamPackage(SDLState& state, const SDL_Event& event); 38Common::ParamPackage SDLEventToButtonParamPackage(SDLState& state, const SDL_Event& event);
39} // Anonymous namespace
35 40
36static int SDLEventWatcher(void* user_data, SDL_Event* event) { 41static int SDLEventWatcher(void* user_data, SDL_Event* event) {
37 auto* const sdl_state = static_cast<SDLState*>(user_data); 42 auto* const sdl_state = static_cast<SDLState*>(user_data);
@@ -48,8 +53,10 @@ static int SDLEventWatcher(void* user_data, SDL_Event* event) {
48 53
49class SDLJoystick { 54class SDLJoystick {
50public: 55public:
51 SDLJoystick(std::string guid_, int port_, SDL_Joystick* joystick) 56 SDLJoystick(std::string guid_, int port_, SDL_Joystick* joystick,
52 : guid{std::move(guid_)}, port{port_}, sdl_joystick{joystick, &SDL_JoystickClose} {} 57 SDL_GameController* gamecontroller)
58 : guid{std::move(guid_)}, port{port_}, sdl_joystick{joystick, &SDL_JoystickClose},
59 sdl_controller{gamecontroller, &SDL_GameControllerClose} {}
53 60
54 void SetButton(int button, bool value) { 61 void SetButton(int button, bool value) {
55 std::lock_guard lock{mutex}; 62 std::lock_guard lock{mutex};
@@ -115,10 +122,15 @@ public:
115 return sdl_joystick.get(); 122 return sdl_joystick.get();
116 } 123 }
117 124
118 void SetSDLJoystick(SDL_Joystick* joystick) { 125 void SetSDLJoystick(SDL_Joystick* joystick, SDL_GameController* controller) {
126 sdl_controller.reset(controller);
119 sdl_joystick.reset(joystick); 127 sdl_joystick.reset(joystick);
120 } 128 }
121 129
130 SDL_GameController* GetSDLGameController() const {
131 return sdl_controller.get();
132 }
133
122private: 134private:
123 struct State { 135 struct State {
124 std::unordered_map<int, bool> buttons; 136 std::unordered_map<int, bool> buttons;
@@ -128,6 +140,7 @@ private:
128 std::string guid; 140 std::string guid;
129 int port; 141 int port;
130 std::unique_ptr<SDL_Joystick, decltype(&SDL_JoystickClose)> sdl_joystick; 142 std::unique_ptr<SDL_Joystick, decltype(&SDL_JoystickClose)> sdl_joystick;
143 std::unique_ptr<SDL_GameController, decltype(&SDL_GameControllerClose)> sdl_controller;
131 mutable std::mutex mutex; 144 mutable std::mutex mutex;
132}; 145};
133 146
@@ -136,18 +149,19 @@ std::shared_ptr<SDLJoystick> SDLState::GetSDLJoystickByGUID(const std::string& g
136 const auto it = joystick_map.find(guid); 149 const auto it = joystick_map.find(guid);
137 if (it != joystick_map.end()) { 150 if (it != joystick_map.end()) {
138 while (it->second.size() <= static_cast<std::size_t>(port)) { 151 while (it->second.size() <= static_cast<std::size_t>(port)) {
139 auto joystick = 152 auto joystick = std::make_shared<SDLJoystick>(guid, static_cast<int>(it->second.size()),
140 std::make_shared<SDLJoystick>(guid, static_cast<int>(it->second.size()), nullptr); 153 nullptr, nullptr);
141 it->second.emplace_back(std::move(joystick)); 154 it->second.emplace_back(std::move(joystick));
142 } 155 }
143 return it->second[port]; 156 return it->second[port];
144 } 157 }
145 auto joystick = std::make_shared<SDLJoystick>(guid, 0, nullptr); 158 auto joystick = std::make_shared<SDLJoystick>(guid, 0, nullptr, nullptr);
146 return joystick_map[guid].emplace_back(std::move(joystick)); 159 return joystick_map[guid].emplace_back(std::move(joystick));
147} 160}
148 161
149std::shared_ptr<SDLJoystick> SDLState::GetSDLJoystickBySDLID(SDL_JoystickID sdl_id) { 162std::shared_ptr<SDLJoystick> SDLState::GetSDLJoystickBySDLID(SDL_JoystickID sdl_id) {
150 auto sdl_joystick = SDL_JoystickFromInstanceID(sdl_id); 163 auto sdl_joystick = SDL_JoystickFromInstanceID(sdl_id);
164 auto sdl_controller = SDL_GameControllerFromInstanceID(sdl_id);
151 const std::string guid = GetGUID(sdl_joystick); 165 const std::string guid = GetGUID(sdl_joystick);
152 166
153 std::lock_guard lock{joystick_map_mutex}; 167 std::lock_guard lock{joystick_map_mutex};
@@ -171,23 +185,27 @@ std::shared_ptr<SDLJoystick> SDLState::GetSDLJoystickBySDLID(SDL_JoystickID sdl_
171 }); 185 });
172 if (nullptr_it != map_it->second.end()) { 186 if (nullptr_it != map_it->second.end()) {
173 // ... and map it 187 // ... and map it
174 (*nullptr_it)->SetSDLJoystick(sdl_joystick); 188 (*nullptr_it)->SetSDLJoystick(sdl_joystick, sdl_controller);
175 return *nullptr_it; 189 return *nullptr_it;
176 } 190 }
177 191
178 // There is no SDLJoystick without a mapped SDL_Joystick 192 // There is no SDLJoystick without a mapped SDL_Joystick
179 // Create a new SDLJoystick 193 // Create a new SDLJoystick
180 const int port = static_cast<int>(map_it->second.size()); 194 const int port = static_cast<int>(map_it->second.size());
181 auto joystick = std::make_shared<SDLJoystick>(guid, port, sdl_joystick); 195 auto joystick = std::make_shared<SDLJoystick>(guid, port, sdl_joystick, sdl_controller);
182 return map_it->second.emplace_back(std::move(joystick)); 196 return map_it->second.emplace_back(std::move(joystick));
183 } 197 }
184 198
185 auto joystick = std::make_shared<SDLJoystick>(guid, 0, sdl_joystick); 199 auto joystick = std::make_shared<SDLJoystick>(guid, 0, sdl_joystick, sdl_controller);
186 return joystick_map[guid].emplace_back(std::move(joystick)); 200 return joystick_map[guid].emplace_back(std::move(joystick));
187} 201}
188 202
189void SDLState::InitJoystick(int joystick_index) { 203void SDLState::InitJoystick(int joystick_index) {
190 SDL_Joystick* sdl_joystick = SDL_JoystickOpen(joystick_index); 204 SDL_Joystick* sdl_joystick = SDL_JoystickOpen(joystick_index);
205 SDL_GameController* sdl_gamecontroller = nullptr;
206 if (SDL_IsGameController(joystick_index)) {
207 sdl_gamecontroller = SDL_GameControllerOpen(joystick_index);
208 }
191 if (!sdl_joystick) { 209 if (!sdl_joystick) {
192 LOG_ERROR(Input, "failed to open joystick {}", joystick_index); 210 LOG_ERROR(Input, "failed to open joystick {}", joystick_index);
193 return; 211 return;
@@ -196,7 +214,7 @@ void SDLState::InitJoystick(int joystick_index) {
196 214
197 std::lock_guard lock{joystick_map_mutex}; 215 std::lock_guard lock{joystick_map_mutex};
198 if (joystick_map.find(guid) == joystick_map.end()) { 216 if (joystick_map.find(guid) == joystick_map.end()) {
199 auto joystick = std::make_shared<SDLJoystick>(guid, 0, sdl_joystick); 217 auto joystick = std::make_shared<SDLJoystick>(guid, 0, sdl_joystick, sdl_gamecontroller);
200 joystick_map[guid].emplace_back(std::move(joystick)); 218 joystick_map[guid].emplace_back(std::move(joystick));
201 return; 219 return;
202 } 220 }
@@ -205,11 +223,11 @@ void SDLState::InitJoystick(int joystick_index) {
205 joystick_guid_list.begin(), joystick_guid_list.end(), 223 joystick_guid_list.begin(), joystick_guid_list.end(),
206 [](const std::shared_ptr<SDLJoystick>& joystick) { return !joystick->GetSDLJoystick(); }); 224 [](const std::shared_ptr<SDLJoystick>& joystick) { return !joystick->GetSDLJoystick(); });
207 if (it != joystick_guid_list.end()) { 225 if (it != joystick_guid_list.end()) {
208 (*it)->SetSDLJoystick(sdl_joystick); 226 (*it)->SetSDLJoystick(sdl_joystick, sdl_gamecontroller);
209 return; 227 return;
210 } 228 }
211 const int port = static_cast<int>(joystick_guid_list.size()); 229 const int port = static_cast<int>(joystick_guid_list.size());
212 auto joystick = std::make_shared<SDLJoystick>(guid, port, sdl_joystick); 230 auto joystick = std::make_shared<SDLJoystick>(guid, port, sdl_joystick, sdl_gamecontroller);
213 joystick_guid_list.emplace_back(std::move(joystick)); 231 joystick_guid_list.emplace_back(std::move(joystick));
214} 232}
215 233
@@ -231,7 +249,7 @@ void SDLState::CloseJoystick(SDL_Joystick* sdl_joystick) {
231 249
232 // Destruct SDL_Joystick outside the lock guard because SDL can internally call the 250 // Destruct SDL_Joystick outside the lock guard because SDL can internally call the
233 // event callback which locks the mutex again. 251 // event callback which locks the mutex again.
234 joystick->SetSDLJoystick(nullptr); 252 joystick->SetSDLJoystick(nullptr, nullptr);
235} 253}
236 254
237void SDLState::HandleGameControllerEvent(const SDL_Event& event) { 255void SDLState::HandleGameControllerEvent(const SDL_Event& event) {
@@ -341,12 +359,12 @@ public:
341 return std::make_tuple(x / r * (r - deadzone) / (1 - deadzone), 359 return std::make_tuple(x / r * (r - deadzone) / (1 - deadzone),
342 y / r * (r - deadzone) / (1 - deadzone)); 360 y / r * (r - deadzone) / (1 - deadzone));
343 } 361 }
344 return std::make_tuple<float, float>(0.0f, 0.0f); 362 return {};
345 } 363 }
346 364
347 bool GetAnalogDirectionStatus(Input::AnalogDirection direction) const override { 365 bool GetAnalogDirectionStatus(Input::AnalogDirection direction) const override {
348 const auto [x, y] = GetStatus(); 366 const auto [x, y] = GetStatus();
349 const float directional_deadzone = 0.4f; 367 const float directional_deadzone = 0.5f;
350 switch (direction) { 368 switch (direction) {
351 case Input::AnalogDirection::RIGHT: 369 case Input::AnalogDirection::RIGHT:
352 return x > directional_deadzone; 370 return x > directional_deadzone;
@@ -460,7 +478,7 @@ public:
460 const int port = params.Get("port", 0); 478 const int port = params.Get("port", 0);
461 const int axis_x = params.Get("axis_x", 0); 479 const int axis_x = params.Get("axis_x", 0);
462 const int axis_y = params.Get("axis_y", 1); 480 const int axis_y = params.Get("axis_y", 1);
463 const float deadzone = std::clamp(params.Get("deadzone", 0.0f), 0.0f, .99f); 481 const float deadzone = std::clamp(params.Get("deadzone", 0.0f), 0.0f, 1.0f);
464 const float range = std::clamp(params.Get("range", 1.0f), 0.50f, 1.50f); 482 const float range = std::clamp(params.Get("range", 1.0f), 0.50f, 1.50f);
465 auto joystick = state.GetSDLJoystickByGUID(guid, port); 483 auto joystick = state.GetSDLJoystickByGUID(guid, port);
466 484
@@ -476,8 +494,10 @@ private:
476 494
477SDLState::SDLState() { 495SDLState::SDLState() {
478 using namespace Input; 496 using namespace Input;
479 RegisterFactory<ButtonDevice>("sdl", std::make_shared<SDLButtonFactory>(*this)); 497 analog_factory = std::make_shared<SDLAnalogFactory>(*this);
480 RegisterFactory<AnalogDevice>("sdl", std::make_shared<SDLAnalogFactory>(*this)); 498 button_factory = std::make_shared<SDLButtonFactory>(*this);
499 RegisterFactory<AnalogDevice>("sdl", analog_factory);
500 RegisterFactory<ButtonDevice>("sdl", button_factory);
481 501
482 // If the frontend is going to manage the event loop, then we dont start one here 502 // If the frontend is going to manage the event loop, then we dont start one here
483 start_thread = !SDL_WasInit(SDL_INIT_JOYSTICK); 503 start_thread = !SDL_WasInit(SDL_INIT_JOYSTICK);
@@ -485,6 +505,7 @@ SDLState::SDLState() {
485 LOG_CRITICAL(Input, "SDL_Init(SDL_INIT_JOYSTICK) failed with: {}", SDL_GetError()); 505 LOG_CRITICAL(Input, "SDL_Init(SDL_INIT_JOYSTICK) failed with: {}", SDL_GetError());
486 return; 506 return;
487 } 507 }
508 has_gamecontroller = SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER);
488 if (SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1") == SDL_FALSE) { 509 if (SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1") == SDL_FALSE) {
489 LOG_ERROR(Input, "Failed to set hint for background events with: {}", SDL_GetError()); 510 LOG_ERROR(Input, "Failed to set hint for background events with: {}", SDL_GetError());
490 } 511 }
@@ -497,7 +518,7 @@ SDLState::SDLState() {
497 using namespace std::chrono_literals; 518 using namespace std::chrono_literals;
498 while (initialized) { 519 while (initialized) {
499 SDL_PumpEvents(); 520 SDL_PumpEvents();
500 std::this_thread::sleep_for(10ms); 521 std::this_thread::sleep_for(5ms);
501 } 522 }
502 }); 523 });
503 } 524 }
@@ -523,65 +544,230 @@ SDLState::~SDLState() {
523 } 544 }
524} 545}
525 546
526static Common::ParamPackage SDLEventToButtonParamPackage(SDLState& state, const SDL_Event& event) { 547std::vector<Common::ParamPackage> SDLState::GetInputDevices() {
548 std::scoped_lock lock(joystick_map_mutex);
549 std::vector<Common::ParamPackage> devices;
550 for (const auto& [key, value] : joystick_map) {
551 for (const auto& joystick : value) {
552 auto joy = joystick->GetSDLJoystick();
553 if (auto controller = joystick->GetSDLGameController()) {
554 std::string name =
555 fmt::format("{} {}", SDL_GameControllerName(controller), joystick->GetPort());
556 devices.emplace_back(Common::ParamPackage{
557 {"class", "sdl"},
558 {"display", std::move(name)},
559 {"guid", joystick->GetGUID()},
560 {"port", std::to_string(joystick->GetPort())},
561 });
562 } else if (joy) {
563 std::string name = fmt::format("{} {}", SDL_JoystickName(joy), joystick->GetPort());
564 devices.emplace_back(Common::ParamPackage{
565 {"class", "sdl"},
566 {"display", std::move(name)},
567 {"guid", joystick->GetGUID()},
568 {"port", std::to_string(joystick->GetPort())},
569 });
570 }
571 }
572 }
573 return devices;
574}
575
576namespace {
577Common::ParamPackage BuildAnalogParamPackageForButton(int port, std::string guid, u8 axis,
578 float value = 0.1f) {
527 Common::ParamPackage params({{"engine", "sdl"}}); 579 Common::ParamPackage params({{"engine", "sdl"}});
580 params.Set("port", port);
581 params.Set("guid", std::move(guid));
582 params.Set("axis", axis);
583 if (value > 0) {
584 params.Set("direction", "+");
585 params.Set("threshold", "0.5");
586 } else {
587 params.Set("direction", "-");
588 params.Set("threshold", "-0.5");
589 }
590 return params;
591}
528 592
593Common::ParamPackage BuildButtonParamPackageForButton(int port, std::string guid, u8 button) {
594 Common::ParamPackage params({{"engine", "sdl"}});
595 params.Set("port", port);
596 params.Set("guid", std::move(guid));
597 params.Set("button", button);
598 return params;
599}
600
601Common::ParamPackage BuildHatParamPackageForButton(int port, std::string guid, u8 hat, u8 value) {
602 Common::ParamPackage params({{"engine", "sdl"}});
603
604 params.Set("port", port);
605 params.Set("guid", std::move(guid));
606 params.Set("hat", hat);
607 switch (value) {
608 case SDL_HAT_UP:
609 params.Set("direction", "up");
610 break;
611 case SDL_HAT_DOWN:
612 params.Set("direction", "down");
613 break;
614 case SDL_HAT_LEFT:
615 params.Set("direction", "left");
616 break;
617 case SDL_HAT_RIGHT:
618 params.Set("direction", "right");
619 break;
620 default:
621 return {};
622 }
623 return params;
624}
625
626Common::ParamPackage SDLEventToButtonParamPackage(SDLState& state, const SDL_Event& event) {
529 switch (event.type) { 627 switch (event.type) {
530 case SDL_JOYAXISMOTION: { 628 case SDL_JOYAXISMOTION: {
531 const auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which); 629 const auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which);
532 params.Set("port", joystick->GetPort()); 630 return BuildAnalogParamPackageForButton(joystick->GetPort(), joystick->GetGUID(),
533 params.Set("guid", joystick->GetGUID()); 631 event.jaxis.axis, event.jaxis.value);
534 params.Set("axis", event.jaxis.axis);
535 if (event.jaxis.value > 0) {
536 params.Set("direction", "+");
537 params.Set("threshold", "0.5");
538 } else {
539 params.Set("direction", "-");
540 params.Set("threshold", "-0.5");
541 }
542 break;
543 } 632 }
544 case SDL_JOYBUTTONUP: { 633 case SDL_JOYBUTTONUP: {
545 const auto joystick = state.GetSDLJoystickBySDLID(event.jbutton.which); 634 const auto joystick = state.GetSDLJoystickBySDLID(event.jbutton.which);
546 params.Set("port", joystick->GetPort()); 635 return BuildButtonParamPackageForButton(joystick->GetPort(), joystick->GetGUID(),
547 params.Set("guid", joystick->GetGUID()); 636 event.jbutton.button);
548 params.Set("button", event.jbutton.button);
549 break;
550 } 637 }
551 case SDL_JOYHATMOTION: { 638 case SDL_JOYHATMOTION: {
552 const auto joystick = state.GetSDLJoystickBySDLID(event.jhat.which); 639 const auto joystick = state.GetSDLJoystickBySDLID(event.jhat.which);
553 params.Set("port", joystick->GetPort()); 640 return BuildHatParamPackageForButton(joystick->GetPort(), joystick->GetGUID(),
554 params.Set("guid", joystick->GetGUID()); 641 event.jhat.hat, event.jhat.value);
555 params.Set("hat", event.jhat.hat);
556 switch (event.jhat.value) {
557 case SDL_HAT_UP:
558 params.Set("direction", "up");
559 break;
560 case SDL_HAT_DOWN:
561 params.Set("direction", "down");
562 break;
563 case SDL_HAT_LEFT:
564 params.Set("direction", "left");
565 break;
566 case SDL_HAT_RIGHT:
567 params.Set("direction", "right");
568 break;
569 default:
570 return {};
571 }
572 break;
573 } 642 }
574 } 643 }
644 return {};
645}
646
647Common::ParamPackage BuildParamPackageForBinding(int port, const std::string& guid,
648 const SDL_GameControllerButtonBind& binding) {
649 switch (binding.bindType) {
650 case SDL_CONTROLLER_BINDTYPE_AXIS:
651 return BuildAnalogParamPackageForButton(port, guid, binding.value.axis);
652 case SDL_CONTROLLER_BINDTYPE_BUTTON:
653 return BuildButtonParamPackageForButton(port, guid, binding.value.button);
654 case SDL_CONTROLLER_BINDTYPE_HAT:
655 return BuildHatParamPackageForButton(port, guid, binding.value.hat.hat,
656 binding.value.hat.hat_mask);
657 }
658 return {};
659}
660
661Common::ParamPackage BuildParamPackageForAnalog(int port, const std::string& guid, int axis_x,
662 int axis_y) {
663 Common::ParamPackage params;
664 params.Set("engine", "sdl");
665 params.Set("port", port);
666 params.Set("guid", guid);
667 params.Set("axis_x", axis_x);
668 params.Set("axis_y", axis_y);
575 return params; 669 return params;
576} 670}
671} // Anonymous namespace
577 672
578namespace Polling { 673ButtonMapping SDLState::GetButtonMappingForDevice(const Common::ParamPackage& params) {
674 if (!params.Has("guid") || !params.Has("port")) {
675 return {};
676 }
677 const auto joystick = GetSDLJoystickByGUID(params.Get("guid", ""), params.Get("port", 0));
678 auto* controller = joystick->GetSDLGameController();
679 if (controller == nullptr) {
680 return {};
681 }
682
683 // This list is missing ZL/ZR since those are not considered buttons in SDL GameController.
684 // We will add those afterwards
685 // This list also excludes Screenshot since theres not really a mapping for that
686 using ButtonBindings =
687 std::array<std::pair<Settings::NativeButton::Values, SDL_GameControllerButton>, 17>;
688 static constexpr ButtonBindings switch_to_sdl_button{{
689 {Settings::NativeButton::A, SDL_CONTROLLER_BUTTON_B},
690 {Settings::NativeButton::B, SDL_CONTROLLER_BUTTON_A},
691 {Settings::NativeButton::X, SDL_CONTROLLER_BUTTON_Y},
692 {Settings::NativeButton::Y, SDL_CONTROLLER_BUTTON_X},
693 {Settings::NativeButton::LStick, SDL_CONTROLLER_BUTTON_LEFTSTICK},
694 {Settings::NativeButton::RStick, SDL_CONTROLLER_BUTTON_RIGHTSTICK},
695 {Settings::NativeButton::L, SDL_CONTROLLER_BUTTON_LEFTSHOULDER},
696 {Settings::NativeButton::R, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER},
697 {Settings::NativeButton::Plus, SDL_CONTROLLER_BUTTON_START},
698 {Settings::NativeButton::Minus, SDL_CONTROLLER_BUTTON_BACK},
699 {Settings::NativeButton::DLeft, SDL_CONTROLLER_BUTTON_DPAD_LEFT},
700 {Settings::NativeButton::DUp, SDL_CONTROLLER_BUTTON_DPAD_UP},
701 {Settings::NativeButton::DRight, SDL_CONTROLLER_BUTTON_DPAD_RIGHT},
702 {Settings::NativeButton::DDown, SDL_CONTROLLER_BUTTON_DPAD_DOWN},
703 {Settings::NativeButton::SL, SDL_CONTROLLER_BUTTON_LEFTSHOULDER},
704 {Settings::NativeButton::SR, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER},
705 {Settings::NativeButton::Home, SDL_CONTROLLER_BUTTON_GUIDE},
706 }};
707
708 // Add the missing bindings for ZL/ZR
709 using ZBindings =
710 std::array<std::pair<Settings::NativeButton::Values, SDL_GameControllerAxis>, 2>;
711 static constexpr ZBindings switch_to_sdl_axis{{
712 {Settings::NativeButton::ZL, SDL_CONTROLLER_AXIS_TRIGGERLEFT},
713 {Settings::NativeButton::ZR, SDL_CONTROLLER_AXIS_TRIGGERRIGHT},
714 }};
715
716 ButtonMapping mapping;
717 mapping.reserve(switch_to_sdl_button.size() + switch_to_sdl_axis.size());
718
719 for (const auto& [switch_button, sdl_button] : switch_to_sdl_button) {
720 const auto& binding = SDL_GameControllerGetBindForButton(controller, sdl_button);
721 mapping.insert_or_assign(
722 switch_button,
723 BuildParamPackageForBinding(joystick->GetPort(), joystick->GetGUID(), binding));
724 }
725 for (const auto& [switch_button, sdl_axis] : switch_to_sdl_axis) {
726 const auto& binding = SDL_GameControllerGetBindForAxis(controller, sdl_axis);
727 mapping.insert_or_assign(
728 switch_button,
729 BuildParamPackageForBinding(joystick->GetPort(), joystick->GetGUID(), binding));
730 }
731
732 return mapping;
733}
579 734
735AnalogMapping SDLState::GetAnalogMappingForDevice(const Common::ParamPackage& params) {
736 if (!params.Has("guid") || !params.Has("port")) {
737 return {};
738 }
739 const auto joystick = GetSDLJoystickByGUID(params.Get("guid", ""), params.Get("port", 0));
740 auto* controller = joystick->GetSDLGameController();
741 if (controller == nullptr) {
742 return {};
743 }
744
745 AnalogMapping mapping = {};
746 const auto& binding_left_x =
747 SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_LEFTX);
748 const auto& binding_left_y =
749 SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_LEFTY);
750 mapping.insert_or_assign(Settings::NativeAnalog::LStick,
751 BuildParamPackageForAnalog(joystick->GetPort(), joystick->GetGUID(),
752 binding_left_x.value.axis,
753 binding_left_y.value.axis));
754 const auto& binding_right_x =
755 SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_RIGHTX);
756 const auto& binding_right_y =
757 SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_RIGHTY);
758 mapping.insert_or_assign(Settings::NativeAnalog::RStick,
759 BuildParamPackageForAnalog(joystick->GetPort(), joystick->GetGUID(),
760 binding_right_x.value.axis,
761 binding_right_y.value.axis));
762 return mapping;
763}
764
765namespace Polling {
580class SDLPoller : public InputCommon::Polling::DevicePoller { 766class SDLPoller : public InputCommon::Polling::DevicePoller {
581public: 767public:
582 explicit SDLPoller(SDLState& state_) : state(state_) {} 768 explicit SDLPoller(SDLState& state_) : state(state_) {}
583 769
584 void Start() override { 770 void Start(const std::string& device_id) override {
585 state.event_queue.Clear(); 771 state.event_queue.Clear();
586 state.polling = true; 772 state.polling = true;
587 } 773 }
@@ -601,71 +787,106 @@ public:
601 Common::ParamPackage GetNextInput() override { 787 Common::ParamPackage GetNextInput() override {
602 SDL_Event event; 788 SDL_Event event;
603 while (state.event_queue.Pop(event)) { 789 while (state.event_queue.Pop(event)) {
604 switch (event.type) { 790 const auto package = FromEvent(event);
605 case SDL_JOYAXISMOTION: 791 if (package) {
606 if (std::abs(event.jaxis.value / 32767.0) < 0.5) { 792 return *package;
607 break;
608 }
609 [[fallthrough]];
610 case SDL_JOYBUTTONUP:
611 case SDL_JOYHATMOTION:
612 return SDLEventToButtonParamPackage(state, event);
613 } 793 }
614 } 794 }
615 return {}; 795 return {};
616 } 796 }
797 [[nodiscard]] std::optional<Common::ParamPackage> FromEvent(const SDL_Event& event) const {
798 switch (event.type) {
799 case SDL_JOYAXISMOTION:
800 if (std::abs(event.jaxis.value / 32767.0) < 0.5) {
801 break;
802 }
803 [[fallthrough]];
804 case SDL_JOYBUTTONUP:
805 case SDL_JOYHATMOTION:
806 return {SDLEventToButtonParamPackage(state, event)};
807 }
808 return std::nullopt;
809 }
617}; 810};
618 811
619class SDLAnalogPoller final : public SDLPoller { 812/**
813 * Attempts to match the press to a controller joy axis (left/right stick) and if a match
814 * isn't found, checks if the event matches anything from SDLButtonPoller and uses that
815 * instead
816 */
817class SDLAnalogPreferredPoller final : public SDLPoller {
620public: 818public:
621 explicit SDLAnalogPoller(SDLState& state_) : SDLPoller(state_) {} 819 explicit SDLAnalogPreferredPoller(SDLState& state_)
622 820 : SDLPoller(state_), button_poller(state_) {}
623 void Start() override {
624 SDLPoller::Start();
625 821
822 void Start(const std::string& device_id) override {
823 SDLPoller::Start(device_id);
824 // Load the game controller
626 // Reset stored axes 825 // Reset stored axes
627 analog_x_axis = -1; 826 analog_x_axis = -1;
628 analog_y_axis = -1; 827 analog_y_axis = -1;
629 analog_axes_joystick = -1;
630 } 828 }
631 829
632 Common::ParamPackage GetNextInput() override { 830 Common::ParamPackage GetNextInput() override {
633 SDL_Event event; 831 SDL_Event event;
634 while (state.event_queue.Pop(event)) { 832 while (state.event_queue.Pop(event)) {
635 if (event.type != SDL_JOYAXISMOTION || std::abs(event.jaxis.value / 32767.0) < 0.5) { 833 // Filter out axis events that are below a threshold
834 if (event.type == SDL_JOYAXISMOTION && std::abs(event.jaxis.value / 32767.0) < 0.5) {
636 continue; 835 continue;
637 } 836 }
638 // An analog device needs two axes, so we need to store the axis for later and wait for 837 // Simplify controller config by testing if game controller support is enabled.
639 // a second SDL event. The axes also must be from the same joystick. 838 if (event.type == SDL_JOYAXISMOTION) {
640 const int axis = event.jaxis.axis; 839 const auto axis = event.jaxis.axis;
641 if (analog_x_axis == -1) { 840 const auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which);
642 analog_x_axis = axis; 841 const auto controller = joystick->GetSDLGameController();
643 analog_axes_joystick = event.jaxis.which; 842 if (controller) {
644 } else if (analog_y_axis == -1 && analog_x_axis != axis && 843 const auto axis_left_x =
645 analog_axes_joystick == event.jaxis.which) { 844 SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_LEFTX)
646 analog_y_axis = axis; 845 .value.axis;
846 const auto axis_left_y =
847 SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_LEFTY)
848 .value.axis;
849 const auto axis_right_x =
850 SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_RIGHTX)
851 .value.axis;
852 const auto axis_right_y =
853 SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_RIGHTY)
854 .value.axis;
855
856 if (axis == axis_left_x || axis == axis_left_y) {
857 analog_x_axis = axis_left_x;
858 analog_y_axis = axis_left_y;
859 break;
860 } else if (axis == axis_right_x || axis == axis_right_y) {
861 analog_x_axis = axis_right_x;
862 analog_y_axis = axis_right_y;
863 break;
864 }
865 }
866 }
867
868 // If the press wasn't accepted as a joy axis, check for a button press
869 auto button_press = button_poller.FromEvent(event);
870 if (button_press) {
871 return *button_press;
647 } 872 }
648 } 873 }
649 Common::ParamPackage params; 874
650 if (analog_x_axis != -1 && analog_y_axis != -1) { 875 if (analog_x_axis != -1 && analog_y_axis != -1) {
651 const auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which); 876 const auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which);
652 params.Set("engine", "sdl"); 877 auto params = BuildParamPackageForAnalog(joystick->GetPort(), joystick->GetGUID(),
653 params.Set("port", joystick->GetPort()); 878 analog_x_axis, analog_y_axis);
654 params.Set("guid", joystick->GetGUID());
655 params.Set("axis_x", analog_x_axis);
656 params.Set("axis_y", analog_y_axis);
657 analog_x_axis = -1; 879 analog_x_axis = -1;
658 analog_y_axis = -1; 880 analog_y_axis = -1;
659 analog_axes_joystick = -1;
660 return params; 881 return params;
661 } 882 }
662 return params; 883 return {};
663 } 884 }
664 885
665private: 886private:
666 int analog_x_axis = -1; 887 int analog_x_axis = -1;
667 int analog_y_axis = -1; 888 int analog_y_axis = -1;
668 SDL_JoystickID analog_axes_joystick = -1; 889 SDLButtonPoller button_poller;
669}; 890};
670} // namespace Polling 891} // namespace Polling
671 892
@@ -673,8 +894,8 @@ SDLState::Pollers SDLState::GetPollers(InputCommon::Polling::DeviceType type) {
673 Pollers pollers; 894 Pollers pollers;
674 895
675 switch (type) { 896 switch (type) {
676 case InputCommon::Polling::DeviceType::Analog: 897 case InputCommon::Polling::DeviceType::AnalogPreferred:
677 pollers.emplace_back(std::make_unique<Polling::SDLAnalogPoller>(*this)); 898 pollers.emplace_back(std::make_unique<Polling::SDLAnalogPreferredPoller>(*this));
678 break; 899 break;
679 case InputCommon::Polling::DeviceType::Button: 900 case InputCommon::Polling::DeviceType::Button:
680 pollers.emplace_back(std::make_unique<Polling::SDLButtonPoller>(*this)); 901 pollers.emplace_back(std::make_unique<Polling::SDLButtonPoller>(*this));
diff --git a/src/input_common/sdl/sdl_impl.h b/src/input_common/sdl/sdl_impl.h
index 606a32c5b..bd19ba61d 100644
--- a/src/input_common/sdl/sdl_impl.h
+++ b/src/input_common/sdl/sdl_impl.h
@@ -50,6 +50,11 @@ public:
50 std::atomic<bool> polling = false; 50 std::atomic<bool> polling = false;
51 Common::SPSCQueue<SDL_Event> event_queue; 51 Common::SPSCQueue<SDL_Event> event_queue;
52 52
53 std::vector<Common::ParamPackage> GetInputDevices() override;
54
55 ButtonMapping GetButtonMappingForDevice(const Common::ParamPackage& params) override;
56 AnalogMapping GetAnalogMappingForDevice(const Common::ParamPackage& params) override;
57
53private: 58private:
54 void InitJoystick(int joystick_index); 59 void InitJoystick(int joystick_index);
55 void CloseJoystick(SDL_Joystick* sdl_joystick); 60 void CloseJoystick(SDL_Joystick* sdl_joystick);
@@ -57,6 +62,9 @@ private:
57 /// Needs to be called before SDL_QuitSubSystem. 62 /// Needs to be called before SDL_QuitSubSystem.
58 void CloseJoysticks(); 63 void CloseJoysticks();
59 64
65 // Set to true if SDL supports game controller subsystem
66 bool has_gamecontroller = false;
67
60 /// Map of GUID of a list of corresponding virtual Joysticks 68 /// Map of GUID of a list of corresponding virtual Joysticks
61 std::unordered_map<std::string, std::vector<std::shared_ptr<SDLJoystick>>> joystick_map; 69 std::unordered_map<std::string, std::vector<std::shared_ptr<SDLJoystick>>> joystick_map;
62 std::mutex joystick_map_mutex; 70 std::mutex joystick_map_mutex;
diff --git a/src/input_common/settings.cpp b/src/input_common/settings.cpp
new file mode 100644
index 000000000..80c719cf4
--- /dev/null
+++ b/src/input_common/settings.cpp
@@ -0,0 +1,33 @@
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/settings.h"
6
7namespace Settings {
8namespace NativeButton {
9const std::array<const char*, NumButtons> mapping = {{
10 "button_a", "button_b", "button_x", "button_y", "button_lstick",
11 "button_rstick", "button_l", "button_r", "button_zl", "button_zr",
12 "button_plus", "button_minus", "button_dleft", "button_dup", "button_dright",
13 "button_ddown", "button_sl", "button_sr", "button_home", "button_screenshot",
14}};
15}
16
17namespace NativeAnalog {
18const std::array<const char*, NumAnalogs> mapping = {{
19 "lstick",
20 "rstick",
21}};
22}
23
24namespace NativeMouseButton {
25const std::array<const char*, NumMouseButtons> mapping = {{
26 "left",
27 "right",
28 "middle",
29 "forward",
30 "back",
31}};
32}
33} // namespace Settings
diff --git a/src/input_common/settings.h b/src/input_common/settings.h
new file mode 100644
index 000000000..2d258960b
--- /dev/null
+++ b/src/input_common/settings.h
@@ -0,0 +1,335 @@
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 "common/common_types.h"
10
11namespace Settings {
12namespace NativeButton {
13enum Values : int {
14 A,
15 B,
16 X,
17 Y,
18 LStick,
19 RStick,
20 L,
21 R,
22 ZL,
23 ZR,
24 Plus,
25 Minus,
26
27 DLeft,
28 DUp,
29 DRight,
30 DDown,
31
32 SL,
33 SR,
34
35 Home,
36 Screenshot,
37
38 NumButtons,
39};
40
41constexpr int BUTTON_HID_BEGIN = A;
42constexpr int BUTTON_NS_BEGIN = Home;
43
44constexpr int BUTTON_HID_END = BUTTON_NS_BEGIN;
45constexpr int BUTTON_NS_END = NumButtons;
46
47constexpr int NUM_BUTTONS_HID = BUTTON_HID_END - BUTTON_HID_BEGIN;
48constexpr int NUM_BUTTONS_NS = BUTTON_NS_END - BUTTON_NS_BEGIN;
49
50extern const std::array<const char*, NumButtons> mapping;
51
52} // namespace NativeButton
53
54namespace NativeAnalog {
55enum Values : int {
56 LStick,
57 RStick,
58
59 NumAnalogs,
60};
61
62constexpr int STICK_HID_BEGIN = LStick;
63constexpr int STICK_HID_END = NumAnalogs;
64constexpr int NUM_STICKS_HID = NumAnalogs;
65
66extern const std::array<const char*, NumAnalogs> mapping;
67} // namespace NativeAnalog
68
69namespace NativeMouseButton {
70enum Values {
71 Left,
72 Right,
73 Middle,
74 Forward,
75 Back,
76
77 NumMouseButtons,
78};
79
80constexpr int MOUSE_HID_BEGIN = Left;
81constexpr int MOUSE_HID_END = NumMouseButtons;
82constexpr int NUM_MOUSE_HID = NumMouseButtons;
83
84extern const std::array<const char*, NumMouseButtons> mapping;
85} // namespace NativeMouseButton
86
87namespace NativeKeyboard {
88enum Keys {
89 None,
90 Error,
91
92 A = 4,
93 B,
94 C,
95 D,
96 E,
97 F,
98 G,
99 H,
100 I,
101 J,
102 K,
103 L,
104 M,
105 N,
106 O,
107 P,
108 Q,
109 R,
110 S,
111 T,
112 U,
113 V,
114 W,
115 X,
116 Y,
117 Z,
118 N1,
119 N2,
120 N3,
121 N4,
122 N5,
123 N6,
124 N7,
125 N8,
126 N9,
127 N0,
128 Enter,
129 Escape,
130 Backspace,
131 Tab,
132 Space,
133 Minus,
134 Equal,
135 LeftBrace,
136 RightBrace,
137 Backslash,
138 Tilde,
139 Semicolon,
140 Apostrophe,
141 Grave,
142 Comma,
143 Dot,
144 Slash,
145 CapsLockKey,
146
147 F1,
148 F2,
149 F3,
150 F4,
151 F5,
152 F6,
153 F7,
154 F8,
155 F9,
156 F10,
157 F11,
158 F12,
159
160 SystemRequest,
161 ScrollLockKey,
162 Pause,
163 Insert,
164 Home,
165 PageUp,
166 Delete,
167 End,
168 PageDown,
169 Right,
170 Left,
171 Down,
172 Up,
173
174 NumLockKey,
175 KPSlash,
176 KPAsterisk,
177 KPMinus,
178 KPPlus,
179 KPEnter,
180 KP1,
181 KP2,
182 KP3,
183 KP4,
184 KP5,
185 KP6,
186 KP7,
187 KP8,
188 KP9,
189 KP0,
190 KPDot,
191
192 Key102,
193 Compose,
194 Power,
195 KPEqual,
196
197 F13,
198 F14,
199 F15,
200 F16,
201 F17,
202 F18,
203 F19,
204 F20,
205 F21,
206 F22,
207 F23,
208 F24,
209
210 Open,
211 Help,
212 Properties,
213 Front,
214 Stop,
215 Repeat,
216 Undo,
217 Cut,
218 Copy,
219 Paste,
220 Find,
221 Mute,
222 VolumeUp,
223 VolumeDown,
224 CapsLockActive,
225 NumLockActive,
226 ScrollLockActive,
227 KPComma,
228
229 KPLeftParenthesis,
230 KPRightParenthesis,
231
232 LeftControlKey = 0xE0,
233 LeftShiftKey,
234 LeftAltKey,
235 LeftMetaKey,
236 RightControlKey,
237 RightShiftKey,
238 RightAltKey,
239 RightMetaKey,
240
241 MediaPlayPause,
242 MediaStopCD,
243 MediaPrevious,
244 MediaNext,
245 MediaEject,
246 MediaVolumeUp,
247 MediaVolumeDown,
248 MediaMute,
249 MediaWebsite,
250 MediaBack,
251 MediaForward,
252 MediaStop,
253 MediaFind,
254 MediaScrollUp,
255 MediaScrollDown,
256 MediaEdit,
257 MediaSleep,
258 MediaCoffee,
259 MediaRefresh,
260 MediaCalculator,
261
262 NumKeyboardKeys,
263};
264
265static_assert(NumKeyboardKeys == 0xFC, "Incorrect number of keyboard keys.");
266
267enum Modifiers {
268 LeftControl,
269 LeftShift,
270 LeftAlt,
271 LeftMeta,
272 RightControl,
273 RightShift,
274 RightAlt,
275 RightMeta,
276 CapsLock,
277 ScrollLock,
278 NumLock,
279
280 NumKeyboardMods,
281};
282
283constexpr int KEYBOARD_KEYS_HID_BEGIN = None;
284constexpr int KEYBOARD_KEYS_HID_END = NumKeyboardKeys;
285constexpr int NUM_KEYBOARD_KEYS_HID = NumKeyboardKeys;
286
287constexpr int KEYBOARD_MODS_HID_BEGIN = LeftControl;
288constexpr int KEYBOARD_MODS_HID_END = NumKeyboardMods;
289constexpr int NUM_KEYBOARD_MODS_HID = NumKeyboardMods;
290
291} // namespace NativeKeyboard
292
293using ButtonsRaw = std::array<std::string, NativeButton::NumButtons>;
294using AnalogsRaw = std::array<std::string, NativeAnalog::NumAnalogs>;
295using MouseButtonsRaw = std::array<std::string, NativeMouseButton::NumMouseButtons>;
296using KeyboardKeysRaw = std::array<std::string, NativeKeyboard::NumKeyboardKeys>;
297using KeyboardModsRaw = std::array<std::string, NativeKeyboard::NumKeyboardMods>;
298
299constexpr u32 JOYCON_BODY_NEON_RED = 0xFF3C28;
300constexpr u32 JOYCON_BUTTONS_NEON_RED = 0x1E0A0A;
301constexpr u32 JOYCON_BODY_NEON_BLUE = 0x0AB9E6;
302constexpr u32 JOYCON_BUTTONS_NEON_BLUE = 0x001E1E;
303
304enum class ControllerType {
305 ProController,
306 DualJoyconDetached,
307 LeftJoycon,
308 RightJoycon,
309 Handheld,
310};
311
312struct PlayerInput {
313 bool connected;
314 ControllerType controller_type;
315 ButtonsRaw buttons;
316 AnalogsRaw analogs;
317 std::string lstick_mod;
318 std::string rstick_mod;
319
320 u32 body_color_left;
321 u32 body_color_right;
322 u32 button_color_left;
323 u32 button_color_right;
324};
325
326struct TouchscreenInput {
327 bool enabled;
328 std::string device;
329
330 u32 finger;
331 u32 diameter_x;
332 u32 diameter_y;
333 u32 rotation_angle;
334};
335} // namespace Settings
diff --git a/src/input_common/touch_from_button.cpp b/src/input_common/touch_from_button.cpp
new file mode 100644
index 000000000..98da0ef1a
--- /dev/null
+++ b/src/input_common/touch_from_button.cpp
@@ -0,0 +1,50 @@
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 "core/frontend/framebuffer_layout.h"
6#include "core/settings.h"
7#include "input_common/touch_from_button.h"
8
9namespace InputCommon {
10
11class TouchFromButtonDevice final : public Input::TouchDevice {
12public:
13 TouchFromButtonDevice() {
14 for (const auto& config_entry :
15 Settings::values.touch_from_button_maps[Settings::values.touch_from_button_map_index]
16 .buttons) {
17 const Common::ParamPackage package{config_entry};
18 map.emplace_back(
19 Input::CreateDevice<Input::ButtonDevice>(config_entry),
20 std::clamp(package.Get("x", 0), 0, static_cast<int>(Layout::ScreenUndocked::Width)),
21 std::clamp(package.Get("y", 0), 0,
22 static_cast<int>(Layout::ScreenUndocked::Height)));
23 }
24 }
25
26 std::tuple<float, float, bool> GetStatus() const override {
27 for (const auto& m : map) {
28 const bool state = std::get<0>(m)->GetStatus();
29 if (state) {
30 const float x = static_cast<float>(std::get<1>(m)) /
31 static_cast<int>(Layout::ScreenUndocked::Width);
32 const float y = static_cast<float>(std::get<2>(m)) /
33 static_cast<int>(Layout::ScreenUndocked::Height);
34 return {x, y, true};
35 }
36 }
37 return {};
38 }
39
40private:
41 // A vector of the mapped button, its x and its y-coordinate
42 std::vector<std::tuple<std::unique_ptr<Input::ButtonDevice>, int, int>> map;
43};
44
45std::unique_ptr<Input::TouchDevice> TouchFromButtonFactory::Create(
46 const Common::ParamPackage& params) {
47 return std::make_unique<TouchFromButtonDevice>();
48}
49
50} // namespace InputCommon
diff --git a/src/input_common/touch_from_button.h b/src/input_common/touch_from_button.h
new file mode 100644
index 000000000..8b4d1aa96
--- /dev/null
+++ b/src/input_common/touch_from_button.h
@@ -0,0 +1,23 @@
1// Copyright 2020 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
12/**
13 * A touch device factory that takes a list of button devices and combines them into a touch device.
14 */
15class TouchFromButtonFactory final : public Input::Factory<Input::TouchDevice> {
16public:
17 /**
18 * Creates a touch device from a list of button devices
19 */
20 std::unique_ptr<Input::TouchDevice> Create(const Common::ParamPackage& params) override;
21};
22
23} // namespace InputCommon
diff --git a/src/input_common/udp/udp.cpp b/src/input_common/udp/udp.cpp
index 8c6ef1394..4b347e47e 100644
--- a/src/input_common/udp/udp.cpp
+++ b/src/input_common/udp/udp.cpp
@@ -77,10 +77,11 @@ State::State() {
77 std::make_unique<Client>(status, Settings::values.udp_input_address, 77 std::make_unique<Client>(status, Settings::values.udp_input_address,
78 Settings::values.udp_input_port, Settings::values.udp_pad_index); 78 Settings::values.udp_input_port, Settings::values.udp_pad_index);
79 79
80 Input::RegisterFactory<Input::TouchDevice>("cemuhookudp", 80 motion_factory = std::make_shared<UDPMotionFactory>(status);
81 std::make_shared<UDPTouchFactory>(status)); 81 touch_factory = std::make_shared<UDPTouchFactory>(status);
82 Input::RegisterFactory<Input::MotionDevice>("cemuhookudp", 82
83 std::make_shared<UDPMotionFactory>(status)); 83 Input::RegisterFactory<Input::MotionDevice>("cemuhookudp", motion_factory);
84 Input::RegisterFactory<Input::TouchDevice>("cemuhookudp", touch_factory);
84} 85}
85 86
86State::~State() { 87State::~State() {
@@ -88,6 +89,11 @@ State::~State() {
88 Input::UnregisterFactory<Input::MotionDevice>("cemuhookudp"); 89 Input::UnregisterFactory<Input::MotionDevice>("cemuhookudp");
89} 90}
90 91
92std::vector<Common::ParamPackage> State::GetInputDevices() const {
93 // TODO support binding udp devices
94 return {};
95}
96
91void State::ReloadUDPClient() { 97void State::ReloadUDPClient() {
92 client->ReloadSocket(Settings::values.udp_input_address, Settings::values.udp_input_port, 98 client->ReloadSocket(Settings::values.udp_input_address, Settings::values.udp_input_port,
93 Settings::values.udp_pad_index); 99 Settings::values.udp_pad_index);
diff --git a/src/input_common/udp/udp.h b/src/input_common/udp/udp.h
index 4f83f0441..672a5c812 100644
--- a/src/input_common/udp/udp.h
+++ b/src/input_common/udp/udp.h
@@ -5,19 +5,26 @@
5#pragma once 5#pragma once
6 6
7#include <memory> 7#include <memory>
8#include <vector>
9#include "common/param_package.h"
8 10
9namespace InputCommon::CemuhookUDP { 11namespace InputCommon::CemuhookUDP {
10 12
11class Client; 13class Client;
14class UDPMotionFactory;
15class UDPTouchFactory;
12 16
13class State { 17class State {
14public: 18public:
15 State(); 19 State();
16 ~State(); 20 ~State();
17 void ReloadUDPClient(); 21 void ReloadUDPClient();
22 std::vector<Common::ParamPackage> GetInputDevices() const;
18 23
19private: 24private:
20 std::unique_ptr<Client> client; 25 std::unique_ptr<Client> client;
26 std::shared_ptr<UDPMotionFactory> motion_factory;
27 std::shared_ptr<UDPTouchFactory> touch_factory;
21}; 28};
22 29
23std::unique_ptr<State> Init(); 30std::unique_ptr<State> Init();
diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt
index 3cd896a0f..d85f1e9d1 100644
--- a/src/video_core/CMakeLists.txt
+++ b/src/video_core/CMakeLists.txt
@@ -1,3 +1,5 @@
1add_subdirectory(host_shaders)
2
1add_library(video_core STATIC 3add_library(video_core STATIC
2 buffer_cache/buffer_block.h 4 buffer_cache/buffer_block.h
3 buffer_cache/buffer_cache.h 5 buffer_cache/buffer_cache.h
@@ -244,6 +246,9 @@ create_target_directory_groups(video_core)
244target_link_libraries(video_core PUBLIC common core) 246target_link_libraries(video_core PUBLIC common core)
245target_link_libraries(video_core PRIVATE glad xbyak) 247target_link_libraries(video_core PRIVATE glad xbyak)
246 248
249add_dependencies(video_core host_shaders)
250target_include_directories(video_core PRIVATE ${HOST_SHADERS_INCLUDE})
251
247if (ENABLE_VULKAN) 252if (ENABLE_VULKAN)
248 target_include_directories(video_core PRIVATE sirit ../../externals/Vulkan-Headers/include) 253 target_include_directories(video_core PRIVATE sirit ../../externals/Vulkan-Headers/include)
249 target_compile_definitions(video_core PRIVATE HAS_VULKAN) 254 target_compile_definitions(video_core PRIVATE HAS_VULKAN)
diff --git a/src/video_core/host_shaders/CMakeLists.txt b/src/video_core/host_shaders/CMakeLists.txt
new file mode 100644
index 000000000..aa62363a7
--- /dev/null
+++ b/src/video_core/host_shaders/CMakeLists.txt
@@ -0,0 +1,43 @@
1set(SHADER_FILES
2 opengl_present.frag
3 opengl_present.vert
4)
5
6set(SHADER_INCLUDE ${CMAKE_CURRENT_BINARY_DIR}/include)
7set(HOST_SHADERS_INCLUDE ${SHADER_INCLUDE} PARENT_SCOPE)
8
9set(SHADER_DIR ${SHADER_INCLUDE}/video_core/host_shaders)
10add_custom_command(
11 OUTPUT
12 ${SHADER_DIR}
13 COMMAND
14 ${CMAKE_COMMAND} -E make_directory ${SHADER_DIR}
15)
16
17set(INPUT_FILE ${CMAKE_CURRENT_SOURCE_DIR}/source_shader.h.in)
18set(HEADER_GENERATOR ${CMAKE_CURRENT_SOURCE_DIR}/StringShaderHeader.cmake)
19
20foreach(FILENAME IN ITEMS ${SHADER_FILES})
21 string(REPLACE "." "_" SHADER_NAME ${FILENAME})
22 set(SOURCE_FILE ${CMAKE_CURRENT_SOURCE_DIR}/${FILENAME})
23 set(HEADER_FILE ${SHADER_DIR}/${SHADER_NAME}.h)
24 add_custom_command(
25 OUTPUT
26 ${HEADER_FILE}
27 COMMAND
28 ${CMAKE_COMMAND} -P ${HEADER_GENERATOR} ${SOURCE_FILE} ${HEADER_FILE} ${INPUT_FILE}
29 MAIN_DEPENDENCY
30 ${SOURCE_FILE}
31 DEPENDS
32 ${HEADER_GENERATOR}
33 ${INPUT_FILE}
34 )
35 set(SHADER_HEADERS ${SHADER_HEADERS} ${HEADER_FILE})
36endforeach()
37
38add_custom_target(host_shaders
39 DEPENDS
40 ${SHADER_HEADERS}
41 SOURCES
42 ${SHADER_FILES}
43)
diff --git a/src/video_core/host_shaders/StringShaderHeader.cmake b/src/video_core/host_shaders/StringShaderHeader.cmake
new file mode 100644
index 000000000..368bce0ed
--- /dev/null
+++ b/src/video_core/host_shaders/StringShaderHeader.cmake
@@ -0,0 +1,11 @@
1set(SOURCE_FILE ${CMAKE_ARGV3})
2set(HEADER_FILE ${CMAKE_ARGV4})
3set(INPUT_FILE ${CMAKE_ARGV5})
4
5get_filename_component(CONTENTS_NAME ${SOURCE_FILE} NAME)
6string(REPLACE "." "_" CONTENTS_NAME ${CONTENTS_NAME})
7string(TOUPPER ${CONTENTS_NAME} CONTENTS_NAME)
8
9file(READ ${SOURCE_FILE} CONTENTS)
10
11configure_file(${INPUT_FILE} ${HEADER_FILE} @ONLY)
diff --git a/src/video_core/host_shaders/opengl_present.frag b/src/video_core/host_shaders/opengl_present.frag
new file mode 100644
index 000000000..8a4cb024b
--- /dev/null
+++ b/src/video_core/host_shaders/opengl_present.frag
@@ -0,0 +1,10 @@
1#version 430 core
2
3layout (location = 0) in vec2 frag_tex_coord;
4layout (location = 0) out vec4 color;
5
6layout (binding = 0) uniform sampler2D color_texture;
7
8void main() {
9 color = vec4(texture(color_texture, frag_tex_coord).rgb, 1.0f);
10}
diff --git a/src/video_core/host_shaders/opengl_present.vert b/src/video_core/host_shaders/opengl_present.vert
new file mode 100644
index 000000000..2235d31a4
--- /dev/null
+++ b/src/video_core/host_shaders/opengl_present.vert
@@ -0,0 +1,24 @@
1#version 430 core
2
3out gl_PerVertex {
4 vec4 gl_Position;
5};
6
7layout (location = 0) in vec2 vert_position;
8layout (location = 1) in vec2 vert_tex_coord;
9layout (location = 0) out vec2 frag_tex_coord;
10
11// This is a truncated 3x3 matrix for 2D transformations:
12// The upper-left 2x2 submatrix performs scaling/rotation/mirroring.
13// The third column performs translation.
14// The third row could be used for projection, which we don't need in 2D. It hence is assumed to
15// implicitly be [0, 0, 1]
16layout (location = 0) uniform mat3x2 modelview_matrix;
17
18void main() {
19 // Multiply input position by the rotscale part of the matrix and then manually translate by
20 // the last column. This is equivalent to using a full 3x3 matrix and expanding the vector
21 // to `vec3(vert_position.xy, 1.0)`
22 gl_Position = vec4(mat2(modelview_matrix) * vert_position + modelview_matrix[2], 0.0, 1.0);
23 frag_tex_coord = vert_tex_coord;
24}
diff --git a/src/video_core/host_shaders/source_shader.h.in b/src/video_core/host_shaders/source_shader.h.in
new file mode 100644
index 000000000..ccdb0d2a9
--- /dev/null
+++ b/src/video_core/host_shaders/source_shader.h.in
@@ -0,0 +1,9 @@
1#pragma once
2
3#include <string_view>
4
5namespace HostShaders {
6
7constexpr std::string_view @CONTENTS_NAME@ = R"(@CONTENTS@)";
8
9} // namespace HostShaders
diff --git a/src/video_core/memory_manager.h b/src/video_core/memory_manager.h
index 533b415e9..53c8d122a 100644
--- a/src/video_core/memory_manager.h
+++ b/src/video_core/memory_manager.h
@@ -31,19 +31,19 @@ public:
31 constexpr PageEntry(State state) : state{state} {} 31 constexpr PageEntry(State state) : state{state} {}
32 constexpr PageEntry(VAddr addr) : state{static_cast<State>(addr >> ShiftBits)} {} 32 constexpr PageEntry(VAddr addr) : state{static_cast<State>(addr >> ShiftBits)} {}
33 33
34 constexpr bool IsUnmapped() const { 34 [[nodiscard]] constexpr bool IsUnmapped() const {
35 return state == State::Unmapped; 35 return state == State::Unmapped;
36 } 36 }
37 37
38 constexpr bool IsAllocated() const { 38 [[nodiscard]] constexpr bool IsAllocated() const {
39 return state == State::Allocated; 39 return state == State::Allocated;
40 } 40 }
41 41
42 constexpr bool IsValid() const { 42 [[nodiscard]] constexpr bool IsValid() const {
43 return !IsUnmapped() && !IsAllocated(); 43 return !IsUnmapped() && !IsAllocated();
44 } 44 }
45 45
46 constexpr VAddr ToAddress() const { 46 [[nodiscard]] constexpr VAddr ToAddress() const {
47 if (!IsValid()) { 47 if (!IsValid()) {
48 return {}; 48 return {};
49 } 49 }
@@ -51,7 +51,7 @@ public:
51 return static_cast<VAddr>(state) << ShiftBits; 51 return static_cast<VAddr>(state) << ShiftBits;
52 } 52 }
53 53
54 constexpr PageEntry operator+(u64 offset) { 54 [[nodiscard]] constexpr PageEntry operator+(u64 offset) const {
55 // If this is a reserved value, offsets do not apply 55 // If this is a reserved value, offsets do not apply
56 if (!IsValid()) { 56 if (!IsValid()) {
57 return *this; 57 return *this;
@@ -74,16 +74,16 @@ public:
74 /// Binds a renderer to the memory manager. 74 /// Binds a renderer to the memory manager.
75 void BindRasterizer(VideoCore::RasterizerInterface& rasterizer); 75 void BindRasterizer(VideoCore::RasterizerInterface& rasterizer);
76 76
77 std::optional<VAddr> GpuToCpuAddress(GPUVAddr addr) const; 77 [[nodiscard]] std::optional<VAddr> GpuToCpuAddress(GPUVAddr addr) const;
78 78
79 template <typename T> 79 template <typename T>
80 T Read(GPUVAddr addr) const; 80 [[nodiscard]] T Read(GPUVAddr addr) const;
81 81
82 template <typename T> 82 template <typename T>
83 void Write(GPUVAddr addr, T data); 83 void Write(GPUVAddr addr, T data);
84 84
85 u8* GetPointer(GPUVAddr addr); 85 [[nodiscard]] u8* GetPointer(GPUVAddr addr);
86 const u8* GetPointer(GPUVAddr addr) const; 86 [[nodiscard]] const u8* GetPointer(GPUVAddr addr) const;
87 87
88 /** 88 /**
89 * ReadBlock and WriteBlock are full read and write operations over virtual 89 * ReadBlock and WriteBlock are full read and write operations over virtual
@@ -112,24 +112,24 @@ public:
112 /** 112 /**
113 * IsGranularRange checks if a gpu region can be simply read with a pointer. 113 * IsGranularRange checks if a gpu region can be simply read with a pointer.
114 */ 114 */
115 bool IsGranularRange(GPUVAddr gpu_addr, std::size_t size) const; 115 [[nodiscard]] bool IsGranularRange(GPUVAddr gpu_addr, std::size_t size) const;
116 116
117 GPUVAddr Map(VAddr cpu_addr, GPUVAddr gpu_addr, std::size_t size); 117 [[nodiscard]] GPUVAddr Map(VAddr cpu_addr, GPUVAddr gpu_addr, std::size_t size);
118 GPUVAddr MapAllocate(VAddr cpu_addr, std::size_t size, std::size_t align); 118 [[nodiscard]] GPUVAddr MapAllocate(VAddr cpu_addr, std::size_t size, std::size_t align);
119 std::optional<GPUVAddr> AllocateFixed(GPUVAddr gpu_addr, std::size_t size); 119 [[nodiscard]] std::optional<GPUVAddr> AllocateFixed(GPUVAddr gpu_addr, std::size_t size);
120 GPUVAddr Allocate(std::size_t size, std::size_t align); 120 [[nodiscard]] GPUVAddr Allocate(std::size_t size, std::size_t align);
121 void Unmap(GPUVAddr gpu_addr, std::size_t size); 121 void Unmap(GPUVAddr gpu_addr, std::size_t size);
122 122
123private: 123private:
124 PageEntry GetPageEntry(GPUVAddr gpu_addr) const; 124 [[nodiscard]] PageEntry GetPageEntry(GPUVAddr gpu_addr) const;
125 void SetPageEntry(GPUVAddr gpu_addr, PageEntry page_entry, std::size_t size = page_size); 125 void SetPageEntry(GPUVAddr gpu_addr, PageEntry page_entry, std::size_t size = page_size);
126 GPUVAddr UpdateRange(GPUVAddr gpu_addr, PageEntry page_entry, std::size_t size); 126 GPUVAddr UpdateRange(GPUVAddr gpu_addr, PageEntry page_entry, std::size_t size);
127 std::optional<GPUVAddr> FindFreeRange(std::size_t size, std::size_t align) const; 127 [[nodiscard]] std::optional<GPUVAddr> FindFreeRange(std::size_t size, std::size_t align) const;
128 128
129 void TryLockPage(PageEntry page_entry, std::size_t size); 129 void TryLockPage(PageEntry page_entry, std::size_t size);
130 void TryUnlockPage(PageEntry page_entry, std::size_t size); 130 void TryUnlockPage(PageEntry page_entry, std::size_t size);
131 131
132 static constexpr std::size_t PageEntryIndex(GPUVAddr gpu_addr) { 132 [[nodiscard]] static constexpr std::size_t PageEntryIndex(GPUVAddr gpu_addr) {
133 return (gpu_addr >> page_bits) & page_table_mask; 133 return (gpu_addr >> page_bits) & page_table_mask;
134 } 134 }
135 135
diff --git a/src/video_core/renderer_opengl/gl_resource_manager.cpp b/src/video_core/renderer_opengl/gl_resource_manager.cpp
index a787e27d2..0ebcec427 100644
--- a/src/video_core/renderer_opengl/gl_resource_manager.cpp
+++ b/src/video_core/renderer_opengl/gl_resource_manager.cpp
@@ -2,6 +2,7 @@
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 <string_view>
5#include <utility> 6#include <utility>
6#include <glad/glad.h> 7#include <glad/glad.h>
7#include "common/common_types.h" 8#include "common/common_types.h"
@@ -82,11 +83,13 @@ void OGLSampler::Release() {
82 handle = 0; 83 handle = 0;
83} 84}
84 85
85void OGLShader::Create(const char* source, GLenum type) { 86void OGLShader::Create(std::string_view source, GLenum type) {
86 if (handle != 0) 87 if (handle != 0) {
87 return; 88 return;
88 if (source == nullptr) 89 }
90 if (source.empty()) {
89 return; 91 return;
92 }
90 93
91 MICROPROFILE_SCOPE(OpenGL_ResourceCreation); 94 MICROPROFILE_SCOPE(OpenGL_ResourceCreation);
92 handle = GLShader::LoadShader(source, type); 95 handle = GLShader::LoadShader(source, type);
diff --git a/src/video_core/renderer_opengl/gl_resource_manager.h b/src/video_core/renderer_opengl/gl_resource_manager.h
index b05cb641c..f48398669 100644
--- a/src/video_core/renderer_opengl/gl_resource_manager.h
+++ b/src/video_core/renderer_opengl/gl_resource_manager.h
@@ -4,6 +4,7 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <string_view>
7#include <utility> 8#include <utility>
8#include <glad/glad.h> 9#include <glad/glad.h>
9#include "common/common_types.h" 10#include "common/common_types.h"
@@ -127,7 +128,7 @@ public:
127 return *this; 128 return *this;
128 } 129 }
129 130
130 void Create(const char* source, GLenum type); 131 void Create(std::string_view source, GLenum type);
131 132
132 void Release(); 133 void Release();
133 134
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp
index eb49a36bf..a07d56ef0 100644
--- a/src/video_core/renderer_opengl/gl_shader_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp
@@ -22,6 +22,7 @@
22#include "video_core/memory_manager.h" 22#include "video_core/memory_manager.h"
23#include "video_core/renderer_opengl/gl_arb_decompiler.h" 23#include "video_core/renderer_opengl/gl_arb_decompiler.h"
24#include "video_core/renderer_opengl/gl_rasterizer.h" 24#include "video_core/renderer_opengl/gl_rasterizer.h"
25#include "video_core/renderer_opengl/gl_resource_manager.h"
25#include "video_core/renderer_opengl/gl_shader_cache.h" 26#include "video_core/renderer_opengl/gl_shader_cache.h"
26#include "video_core/renderer_opengl/gl_shader_decompiler.h" 27#include "video_core/renderer_opengl/gl_shader_decompiler.h"
27#include "video_core/renderer_opengl/gl_shader_disk_cache.h" 28#include "video_core/renderer_opengl/gl_shader_disk_cache.h"
diff --git a/src/video_core/renderer_opengl/gl_shader_util.cpp b/src/video_core/renderer_opengl/gl_shader_util.cpp
index 9e74eda0d..4bf0d6090 100644
--- a/src/video_core/renderer_opengl/gl_shader_util.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_util.cpp
@@ -2,6 +2,7 @@
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 <string_view>
5#include <vector> 6#include <vector>
6#include <glad/glad.h> 7#include <glad/glad.h>
7#include "common/assert.h" 8#include "common/assert.h"
@@ -11,7 +12,8 @@
11namespace OpenGL::GLShader { 12namespace OpenGL::GLShader {
12 13
13namespace { 14namespace {
14const char* GetStageDebugName(GLenum type) { 15
16std::string_view StageDebugName(GLenum type) {
15 switch (type) { 17 switch (type) {
16 case GL_VERTEX_SHADER: 18 case GL_VERTEX_SHADER:
17 return "vertex"; 19 return "vertex";
@@ -25,12 +27,17 @@ const char* GetStageDebugName(GLenum type) {
25 UNIMPLEMENTED(); 27 UNIMPLEMENTED();
26 return "unknown"; 28 return "unknown";
27} 29}
30
28} // Anonymous namespace 31} // Anonymous namespace
29 32
30GLuint LoadShader(const char* source, GLenum type) { 33GLuint LoadShader(std::string_view source, GLenum type) {
31 const char* debug_type = GetStageDebugName(type); 34 const std::string_view debug_type = StageDebugName(type);
32 const GLuint shader_id = glCreateShader(type); 35 const GLuint shader_id = glCreateShader(type);
33 glShaderSource(shader_id, 1, &source, nullptr); 36
37 const GLchar* source_string = source.data();
38 const GLint source_length = static_cast<GLint>(source.size());
39
40 glShaderSource(shader_id, 1, &source_string, &source_length);
34 LOG_DEBUG(Render_OpenGL, "Compiling {} shader...", debug_type); 41 LOG_DEBUG(Render_OpenGL, "Compiling {} shader...", debug_type);
35 glCompileShader(shader_id); 42 glCompileShader(shader_id);
36 43
diff --git a/src/video_core/renderer_opengl/gl_shader_util.h b/src/video_core/renderer_opengl/gl_shader_util.h
index 03b7548c2..1b770532e 100644
--- a/src/video_core/renderer_opengl/gl_shader_util.h
+++ b/src/video_core/renderer_opengl/gl_shader_util.h
@@ -38,7 +38,7 @@ void LogShaderSource(T... shaders) {
38 * @param source String of the GLSL shader program 38 * @param source String of the GLSL shader program
39 * @param type Type of the shader (GL_VERTEX_SHADER, GL_GEOMETRY_SHADER or GL_FRAGMENT_SHADER) 39 * @param type Type of the shader (GL_VERTEX_SHADER, GL_GEOMETRY_SHADER or GL_FRAGMENT_SHADER)
40 */ 40 */
41GLuint LoadShader(const char* source, GLenum type); 41GLuint LoadShader(std::string_view source, GLenum type);
42 42
43/** 43/**
44 * Utility function to create and compile an OpenGL GLSL shader program (vertex + fragment shader) 44 * Utility function to create and compile an OpenGL GLSL shader program (vertex + fragment shader)
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp
index c39663db7..b759c2dba 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.cpp
+++ b/src/video_core/renderer_opengl/renderer_opengl.cpp
@@ -21,6 +21,8 @@
21#include "core/perf_stats.h" 21#include "core/perf_stats.h"
22#include "core/settings.h" 22#include "core/settings.h"
23#include "core/telemetry_session.h" 23#include "core/telemetry_session.h"
24#include "video_core/host_shaders/opengl_present_frag.h"
25#include "video_core/host_shaders/opengl_present_vert.h"
24#include "video_core/morton.h" 26#include "video_core/morton.h"
25#include "video_core/renderer_opengl/gl_rasterizer.h" 27#include "video_core/renderer_opengl/gl_rasterizer.h"
26#include "video_core/renderer_opengl/gl_shader_manager.h" 28#include "video_core/renderer_opengl/gl_shader_manager.h"
@@ -44,46 +46,6 @@ struct Frame {
44 bool is_srgb{}; /// Framebuffer is sRGB or RGB 46 bool is_srgb{}; /// Framebuffer is sRGB or RGB
45}; 47};
46 48
47constexpr char VERTEX_SHADER[] = R"(
48#version 430 core
49
50out gl_PerVertex {
51 vec4 gl_Position;
52};
53
54layout (location = 0) in vec2 vert_position;
55layout (location = 1) in vec2 vert_tex_coord;
56layout (location = 0) out vec2 frag_tex_coord;
57
58// This is a truncated 3x3 matrix for 2D transformations:
59// The upper-left 2x2 submatrix performs scaling/rotation/mirroring.
60// The third column performs translation.
61// The third row could be used for projection, which we don't need in 2D. It hence is assumed to
62// implicitly be [0, 0, 1]
63layout (location = 0) uniform mat3x2 modelview_matrix;
64
65void main() {
66 // Multiply input position by the rotscale part of the matrix and then manually translate by
67 // the last column. This is equivalent to using a full 3x3 matrix and expanding the vector
68 // to `vec3(vert_position.xy, 1.0)`
69 gl_Position = vec4(mat2(modelview_matrix) * vert_position + modelview_matrix[2], 0.0, 1.0);
70 frag_tex_coord = vert_tex_coord;
71}
72)";
73
74constexpr char FRAGMENT_SHADER[] = R"(
75#version 430 core
76
77layout (location = 0) in vec2 frag_tex_coord;
78layout (location = 0) out vec4 color;
79
80layout (binding = 0) uniform sampler2D color_texture;
81
82void main() {
83 color = vec4(texture(color_texture, frag_tex_coord).rgb, 1.0f);
84}
85)";
86
87constexpr GLint PositionLocation = 0; 49constexpr GLint PositionLocation = 0;
88constexpr GLint TexCoordLocation = 1; 50constexpr GLint TexCoordLocation = 1;
89constexpr GLint ModelViewMatrixLocation = 0; 51constexpr GLint ModelViewMatrixLocation = 0;
@@ -461,10 +423,10 @@ void RendererOpenGL::InitOpenGLObjects() {
461 423
462 // Create shader programs 424 // Create shader programs
463 OGLShader vertex_shader; 425 OGLShader vertex_shader;
464 vertex_shader.Create(VERTEX_SHADER, GL_VERTEX_SHADER); 426 vertex_shader.Create(HostShaders::OPENGL_PRESENT_VERT, GL_VERTEX_SHADER);
465 427
466 OGLShader fragment_shader; 428 OGLShader fragment_shader;
467 fragment_shader.Create(FRAGMENT_SHADER, GL_FRAGMENT_SHADER); 429 fragment_shader.Create(HostShaders::OPENGL_PRESENT_FRAG, GL_FRAGMENT_SHADER);
468 430
469 vertex_program.Create(true, false, vertex_shader.handle); 431 vertex_program.Create(true, false, vertex_shader.handle);
470 fragment_program.Create(true, false, fragment_shader.handle); 432 fragment_program.Create(true, false, fragment_shader.handle);
diff --git a/src/video_core/renderer_vulkan/vk_device.cpp b/src/video_core/renderer_vulkan/vk_device.cpp
index ebcfaa0e3..4205bd573 100644
--- a/src/video_core/renderer_vulkan/vk_device.cpp
+++ b/src/video_core/renderer_vulkan/vk_device.cpp
@@ -380,6 +380,14 @@ bool VKDevice::Create() {
380 380
381 CollectTelemetryParameters(); 381 CollectTelemetryParameters();
382 382
383 if (ext_extended_dynamic_state && driver_id == VK_DRIVER_ID_AMD_PROPRIETARY_KHR) {
384 // AMD's proprietary driver supports VK_EXT_extended_dynamic_state but the <stride> field
385 // seems to be bugged. Blacklisting it for now.
386 LOG_WARNING(Render_Vulkan,
387 "Blacklisting AMD proprietary from VK_EXT_extended_dynamic_state");
388 ext_extended_dynamic_state = false;
389 }
390
383 graphics_queue = logical.GetQueue(graphics_family); 391 graphics_queue = logical.GetQueue(graphics_family);
384 present_queue = logical.GetQueue(present_family); 392 present_queue = logical.GetQueue(present_family);
385 393
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
index 936f76195..ff1b52eab 100644
--- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp
+++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
@@ -1443,10 +1443,10 @@ void RasterizerVulkan::UpdateFrontFace(Tegra::Engines::Maxwell3D::Regs& regs) {
1443} 1443}
1444 1444
1445void RasterizerVulkan::UpdatePrimitiveTopology(Tegra::Engines::Maxwell3D::Regs& regs) { 1445void RasterizerVulkan::UpdatePrimitiveTopology(Tegra::Engines::Maxwell3D::Regs& regs) {
1446 if (!state_tracker.TouchPrimitiveTopology()) { 1446 const Maxwell::PrimitiveTopology primitive_topology = regs.draw.topology.Value();
1447 if (!state_tracker.ChangePrimitiveTopology(primitive_topology)) {
1447 return; 1448 return;
1448 } 1449 }
1449 const Maxwell::PrimitiveTopology primitive_topology = regs.draw.topology.Value();
1450 scheduler.Record([this, primitive_topology](vk::CommandBuffer cmdbuf) { 1450 scheduler.Record([this, primitive_topology](vk::CommandBuffer cmdbuf) {
1451 cmdbuf.SetPrimitiveTopologyEXT(MaxwellToVK::PrimitiveTopology(device, primitive_topology)); 1451 cmdbuf.SetPrimitiveTopologyEXT(MaxwellToVK::PrimitiveTopology(device, primitive_topology));
1452 }); 1452 });
diff --git a/src/video_core/renderer_vulkan/vk_state_tracker.cpp b/src/video_core/renderer_vulkan/vk_state_tracker.cpp
index 9151d9fb1..4bd1009f9 100644
--- a/src/video_core/renderer_vulkan/vk_state_tracker.cpp
+++ b/src/video_core/renderer_vulkan/vk_state_tracker.cpp
@@ -42,7 +42,6 @@ Flags MakeInvalidationFlags() {
42 flags[DepthWriteEnable] = true; 42 flags[DepthWriteEnable] = true;
43 flags[DepthCompareOp] = true; 43 flags[DepthCompareOp] = true;
44 flags[FrontFace] = true; 44 flags[FrontFace] = true;
45 flags[PrimitiveTopology] = true;
46 flags[StencilOp] = true; 45 flags[StencilOp] = true;
47 flags[StencilTestEnable] = true; 46 flags[StencilTestEnable] = true;
48 return flags; 47 return flags;
@@ -112,10 +111,6 @@ void SetupDirtyFrontFace(Tables& tables) {
112 table[OFF(screen_y_control)] = FrontFace; 111 table[OFF(screen_y_control)] = FrontFace;
113} 112}
114 113
115void SetupDirtyPrimitiveTopology(Tables& tables) {
116 tables[0][OFF(draw.topology)] = PrimitiveTopology;
117}
118
119void SetupDirtyStencilOp(Tables& tables) { 114void SetupDirtyStencilOp(Tables& tables) {
120 auto& table = tables[0]; 115 auto& table = tables[0];
121 table[OFF(stencil_front_op_fail)] = StencilOp; 116 table[OFF(stencil_front_op_fail)] = StencilOp;
@@ -156,13 +151,13 @@ void StateTracker::Initialize() {
156 SetupDirtyDepthWriteEnable(tables); 151 SetupDirtyDepthWriteEnable(tables);
157 SetupDirtyDepthCompareOp(tables); 152 SetupDirtyDepthCompareOp(tables);
158 SetupDirtyFrontFace(tables); 153 SetupDirtyFrontFace(tables);
159 SetupDirtyPrimitiveTopology(tables);
160 SetupDirtyStencilOp(tables); 154 SetupDirtyStencilOp(tables);
161 SetupDirtyStencilTestEnable(tables); 155 SetupDirtyStencilTestEnable(tables);
162} 156}
163 157
164void StateTracker::InvalidateCommandBufferState() { 158void StateTracker::InvalidateCommandBufferState() {
165 system.GPU().Maxwell3D().dirty.flags |= invalidation_flags; 159 system.GPU().Maxwell3D().dirty.flags |= invalidation_flags;
160 current_topology = INVALID_TOPOLOGY;
166} 161}
167 162
168} // namespace Vulkan 163} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_state_tracker.h b/src/video_core/renderer_vulkan/vk_state_tracker.h
index 54ca0d6c6..13a6ce786 100644
--- a/src/video_core/renderer_vulkan/vk_state_tracker.h
+++ b/src/video_core/renderer_vulkan/vk_state_tracker.h
@@ -32,7 +32,6 @@ enum : u8 {
32 DepthWriteEnable, 32 DepthWriteEnable,
33 DepthCompareOp, 33 DepthCompareOp,
34 FrontFace, 34 FrontFace,
35 PrimitiveTopology,
36 StencilOp, 35 StencilOp,
37 StencilTestEnable, 36 StencilTestEnable,
38 37
@@ -43,6 +42,8 @@ static_assert(Last <= std::numeric_limits<u8>::max());
43} // namespace Dirty 42} // namespace Dirty
44 43
45class StateTracker { 44class StateTracker {
45 using Maxwell = Tegra::Engines::Maxwell3D::Regs;
46
46public: 47public:
47 explicit StateTracker(Core::System& system); 48 explicit StateTracker(Core::System& system);
48 49
@@ -102,10 +103,6 @@ public:
102 return Exchange(Dirty::FrontFace, false); 103 return Exchange(Dirty::FrontFace, false);
103 } 104 }
104 105
105 bool TouchPrimitiveTopology() {
106 return Exchange(Dirty::PrimitiveTopology, false);
107 }
108
109 bool TouchStencilOp() { 106 bool TouchStencilOp() {
110 return Exchange(Dirty::StencilOp, false); 107 return Exchange(Dirty::StencilOp, false);
111 } 108 }
@@ -114,7 +111,15 @@ public:
114 return Exchange(Dirty::StencilTestEnable, false); 111 return Exchange(Dirty::StencilTestEnable, false);
115 } 112 }
116 113
114 bool ChangePrimitiveTopology(Maxwell::PrimitiveTopology new_topology) {
115 const bool has_changed = current_topology != new_topology;
116 current_topology = new_topology;
117 return has_changed;
118 }
119
117private: 120private:
121 static constexpr auto INVALID_TOPOLOGY = static_cast<Maxwell::PrimitiveTopology>(~0u);
122
118 bool Exchange(std::size_t id, bool new_value) const noexcept { 123 bool Exchange(std::size_t id, bool new_value) const noexcept {
119 auto& flags = system.GPU().Maxwell3D().dirty.flags; 124 auto& flags = system.GPU().Maxwell3D().dirty.flags;
120 const bool is_dirty = flags[id]; 125 const bool is_dirty = flags[id];
@@ -124,6 +129,7 @@ private:
124 129
125 Core::System& system; 130 Core::System& system;
126 Tegra::Engines::Maxwell3D::DirtyState::Flags invalidation_flags; 131 Tegra::Engines::Maxwell3D::DirtyState::Flags invalidation_flags;
132 Maxwell::PrimitiveTopology current_topology = INVALID_TOPOLOGY;
127}; 133};
128 134
129} // namespace Vulkan 135} // namespace Vulkan
diff --git a/src/video_core/shader/decode/memory.cpp b/src/video_core/shader/decode/memory.cpp
index e4739394d..e2bba88dd 100644
--- a/src/video_core/shader/decode/memory.cpp
+++ b/src/video_core/shader/decode/memory.cpp
@@ -386,7 +386,8 @@ u32 ShaderIR::DecodeMemory(NodeBlock& bb, u32 pc) {
386 break; 386 break;
387 } 387 }
388 case OpCode::Id::RED: { 388 case OpCode::Id::RED: {
389 UNIMPLEMENTED_IF_MSG(instr.red.type != GlobalAtomicType::U32); 389 UNIMPLEMENTED_IF_MSG(instr.red.type != GlobalAtomicType::U32, "type={}",
390 static_cast<int>(instr.red.type.Value()));
390 const auto [real_address, base_address, descriptor] = 391 const auto [real_address, base_address, descriptor] =
391 TrackGlobalMemory(bb, instr, true, true); 392 TrackGlobalMemory(bb, instr, true, true);
392 if (!real_address || !base_address) { 393 if (!real_address || !base_address) {
diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt
index 656096c9f..3ea4e5601 100644
--- a/src/yuzu/CMakeLists.txt
+++ b/src/yuzu/CMakeLists.txt
@@ -39,6 +39,9 @@ add_executable(yuzu
39 configuration/configure_debug.cpp 39 configuration/configure_debug.cpp
40 configuration/configure_debug.h 40 configuration/configure_debug.h
41 configuration/configure_debug.ui 41 configuration/configure_debug.ui
42 configuration/configure_debug_controller.cpp
43 configuration/configure_debug_controller.h
44 configuration/configure_debug_controller.ui
42 configuration/configure_dialog.cpp 45 configuration/configure_dialog.cpp
43 configuration/configure_dialog.h 46 configuration/configure_dialog.h
44 configuration/configure_filesystem.cpp 47 configuration/configure_filesystem.cpp
@@ -62,9 +65,12 @@ add_executable(yuzu
62 configuration/configure_input_player.cpp 65 configuration/configure_input_player.cpp
63 configuration/configure_input_player.h 66 configuration/configure_input_player.h
64 configuration/configure_input_player.ui 67 configuration/configure_input_player.ui
65 configuration/configure_input_simple.cpp 68 configuration/configure_input_advanced.cpp
66 configuration/configure_input_simple.h 69 configuration/configure_input_advanced.h
67 configuration/configure_input_simple.ui 70 configuration/configure_input_advanced.ui
71 configuration/configure_motion_touch.cpp
72 configuration/configure_motion_touch.h
73 configuration/configure_motion_touch.ui
68 configuration/configure_mouse_advanced.cpp 74 configuration/configure_mouse_advanced.cpp
69 configuration/configure_mouse_advanced.h 75 configuration/configure_mouse_advanced.h
70 configuration/configure_mouse_advanced.ui 76 configuration/configure_mouse_advanced.ui
@@ -83,9 +89,13 @@ add_executable(yuzu
83 configuration/configure_system.cpp 89 configuration/configure_system.cpp
84 configuration/configure_system.h 90 configuration/configure_system.h
85 configuration/configure_system.ui 91 configuration/configure_system.ui
92 configuration/configure_touch_from_button.cpp
93 configuration/configure_touch_from_button.h
94 configuration/configure_touch_from_button.ui
86 configuration/configure_touchscreen_advanced.cpp 95 configuration/configure_touchscreen_advanced.cpp
87 configuration/configure_touchscreen_advanced.h 96 configuration/configure_touchscreen_advanced.h
88 configuration/configure_touchscreen_advanced.ui 97 configuration/configure_touchscreen_advanced.ui
98 configuration/configure_touch_widget.h
89 configuration/configure_ui.cpp 99 configuration/configure_ui.cpp
90 configuration/configure_ui.h 100 configuration/configure_ui.h
91 configuration/configure_ui.ui 101 configuration/configure_ui.ui
diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp
index 8fc322b30..21707e451 100644
--- a/src/yuzu/bootmanager.cpp
+++ b/src/yuzu/bootmanager.cpp
@@ -304,8 +304,9 @@ static Core::Frontend::EmuWindow::WindowSystemInfo GetWindowSystemInfo(QWindow*
304 return wsi; 304 return wsi;
305} 305}
306 306
307GRenderWindow::GRenderWindow(GMainWindow* parent_, EmuThread* emu_thread_) 307GRenderWindow::GRenderWindow(GMainWindow* parent, EmuThread* emu_thread_,
308 : QWidget(parent_), emu_thread(emu_thread_) { 308 std::shared_ptr<InputCommon::InputSubsystem> input_subsystem_)
309 : QWidget(parent), emu_thread(emu_thread_), input_subsystem{std::move(input_subsystem_)} {
309 setWindowTitle(QStringLiteral("yuzu %1 | %2-%3") 310 setWindowTitle(QStringLiteral("yuzu %1 | %2-%3")
310 .arg(QString::fromUtf8(Common::g_build_name), 311 .arg(QString::fromUtf8(Common::g_build_name),
311 QString::fromUtf8(Common::g_scm_branch), 312 QString::fromUtf8(Common::g_scm_branch),
@@ -314,15 +315,15 @@ GRenderWindow::GRenderWindow(GMainWindow* parent_, EmuThread* emu_thread_)
314 auto layout = new QHBoxLayout(this); 315 auto layout = new QHBoxLayout(this);
315 layout->setMargin(0); 316 layout->setMargin(0);
316 setLayout(layout); 317 setLayout(layout);
317 InputCommon::Init(); 318 input_subsystem->Initialize();
318 319
319 this->setMouseTracking(true); 320 this->setMouseTracking(true);
320 321
321 connect(this, &GRenderWindow::FirstFrameDisplayed, parent_, &GMainWindow::OnLoadComplete); 322 connect(this, &GRenderWindow::FirstFrameDisplayed, parent, &GMainWindow::OnLoadComplete);
322} 323}
323 324
324GRenderWindow::~GRenderWindow() { 325GRenderWindow::~GRenderWindow() {
325 InputCommon::Shutdown(); 326 input_subsystem->Shutdown();
326} 327}
327 328
328void GRenderWindow::PollEvents() { 329void GRenderWindow::PollEvents() {
@@ -391,11 +392,11 @@ void GRenderWindow::closeEvent(QCloseEvent* event) {
391} 392}
392 393
393void GRenderWindow::keyPressEvent(QKeyEvent* event) { 394void GRenderWindow::keyPressEvent(QKeyEvent* event) {
394 InputCommon::GetKeyboard()->PressKey(event->key()); 395 input_subsystem->GetKeyboard()->PressKey(event->key());
395} 396}
396 397
397void GRenderWindow::keyReleaseEvent(QKeyEvent* event) { 398void GRenderWindow::keyReleaseEvent(QKeyEvent* event) {
398 InputCommon::GetKeyboard()->ReleaseKey(event->key()); 399 input_subsystem->GetKeyboard()->ReleaseKey(event->key());
399} 400}
400 401
401void GRenderWindow::mousePressEvent(QMouseEvent* event) { 402void GRenderWindow::mousePressEvent(QMouseEvent* event) {
@@ -409,7 +410,7 @@ void GRenderWindow::mousePressEvent(QMouseEvent* event) {
409 const auto [x, y] = ScaleTouch(pos); 410 const auto [x, y] = ScaleTouch(pos);
410 this->TouchPressed(x, y); 411 this->TouchPressed(x, y);
411 } else if (event->button() == Qt::RightButton) { 412 } else if (event->button() == Qt::RightButton) {
412 InputCommon::GetMotionEmu()->BeginTilt(pos.x(), pos.y()); 413 input_subsystem->GetMotionEmu()->BeginTilt(pos.x(), pos.y());
413 } 414 }
414 QWidget::mousePressEvent(event); 415 QWidget::mousePressEvent(event);
415} 416}
@@ -423,7 +424,7 @@ void GRenderWindow::mouseMoveEvent(QMouseEvent* event) {
423 auto pos = event->pos(); 424 auto pos = event->pos();
424 const auto [x, y] = ScaleTouch(pos); 425 const auto [x, y] = ScaleTouch(pos);
425 this->TouchMoved(x, y); 426 this->TouchMoved(x, y);
426 InputCommon::GetMotionEmu()->Tilt(pos.x(), pos.y()); 427 input_subsystem->GetMotionEmu()->Tilt(pos.x(), pos.y());
427 QWidget::mouseMoveEvent(event); 428 QWidget::mouseMoveEvent(event);
428} 429}
429 430
@@ -436,7 +437,7 @@ void GRenderWindow::mouseReleaseEvent(QMouseEvent* event) {
436 if (event->button() == Qt::LeftButton) { 437 if (event->button() == Qt::LeftButton) {
437 this->TouchReleased(); 438 this->TouchReleased();
438 } else if (event->button() == Qt::RightButton) { 439 } else if (event->button() == Qt::RightButton) {
439 InputCommon::GetMotionEmu()->EndTilt(); 440 input_subsystem->GetMotionEmu()->EndTilt();
440 } 441 }
441} 442}
442 443
@@ -451,7 +452,7 @@ void GRenderWindow::TouchUpdateEvent(const QTouchEvent* event) {
451 int active_points = 0; 452 int active_points = 0;
452 453
453 // average all active touch points 454 // average all active touch points
454 for (const auto tp : event->touchPoints()) { 455 for (const auto& tp : event->touchPoints()) {
455 if (tp.state() & (Qt::TouchPointPressed | Qt::TouchPointMoved | Qt::TouchPointStationary)) { 456 if (tp.state() & (Qt::TouchPointPressed | Qt::TouchPointMoved | Qt::TouchPointStationary)) {
456 active_points++; 457 active_points++;
457 pos += tp.pos(); 458 pos += tp.pos();
@@ -485,7 +486,7 @@ bool GRenderWindow::event(QEvent* event) {
485 486
486void GRenderWindow::focusOutEvent(QFocusEvent* event) { 487void GRenderWindow::focusOutEvent(QFocusEvent* event) {
487 QWidget::focusOutEvent(event); 488 QWidget::focusOutEvent(event);
488 InputCommon::GetKeyboard()->ReleaseAllKeys(); 489 input_subsystem->GetKeyboard()->ReleaseAllKeys();
489} 490}
490 491
491void GRenderWindow::resizeEvent(QResizeEvent* event) { 492void GRenderWindow::resizeEvent(QResizeEvent* event) {
diff --git a/src/yuzu/bootmanager.h b/src/yuzu/bootmanager.h
index 6c59b4d5c..ca35cf831 100644
--- a/src/yuzu/bootmanager.h
+++ b/src/yuzu/bootmanager.h
@@ -6,6 +6,7 @@
6 6
7#include <atomic> 7#include <atomic>
8#include <condition_variable> 8#include <condition_variable>
9#include <memory>
9#include <mutex> 10#include <mutex>
10 11
11#include <QImage> 12#include <QImage>
@@ -23,6 +24,10 @@ class QKeyEvent;
23class QTouchEvent; 24class QTouchEvent;
24class QStringList; 25class QStringList;
25 26
27namespace InputCommon {
28class InputSubsystem;
29}
30
26namespace VideoCore { 31namespace VideoCore {
27enum class LoadCallbackStage; 32enum class LoadCallbackStage;
28} 33}
@@ -121,7 +126,8 @@ class GRenderWindow : public QWidget, public Core::Frontend::EmuWindow {
121 Q_OBJECT 126 Q_OBJECT
122 127
123public: 128public:
124 GRenderWindow(GMainWindow* parent, EmuThread* emu_thread); 129 explicit GRenderWindow(GMainWindow* parent, EmuThread* emu_thread_,
130 std::shared_ptr<InputCommon::InputSubsystem> input_subsystem_);
125 ~GRenderWindow() override; 131 ~GRenderWindow() override;
126 132
127 // EmuWindow implementation. 133 // EmuWindow implementation.
@@ -183,6 +189,7 @@ private:
183 QStringList GetUnsupportedGLExtensions() const; 189 QStringList GetUnsupportedGLExtensions() const;
184 190
185 EmuThread* emu_thread; 191 EmuThread* emu_thread;
192 std::shared_ptr<InputCommon::InputSubsystem> input_subsystem;
186 193
187 // Main context that will be shared with all other contexts that are requested. 194 // Main context that will be shared with all other contexts that are requested.
188 // If this is used in a shared context setting, then this should not be used directly, but 195 // If this is used in a shared context setting, then this should not be used directly, but
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index 7af974d8d..2bc55a26a 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -6,7 +6,6 @@
6#include <QKeySequence> 6#include <QKeySequence>
7#include <QSettings> 7#include <QSettings>
8#include "common/file_util.h" 8#include "common/file_util.h"
9#include "configure_input_simple.h"
10#include "core/hle/service/acc/profile_manager.h" 9#include "core/hle/service/acc/profile_manager.h"
11#include "core/hle/service/hid/controllers/npad.h" 10#include "core/hle/service/hid/controllers/npad.h"
12#include "input_common/main.h" 11#include "input_common/main.h"
@@ -32,29 +31,31 @@ Config::~Config() {
32} 31}
33 32
34const std::array<int, Settings::NativeButton::NumButtons> Config::default_buttons = { 33const std::array<int, Settings::NativeButton::NumButtons> Config::default_buttons = {
35 Qt::Key_A, Qt::Key_S, Qt::Key_Z, Qt::Key_X, Qt::Key_3, Qt::Key_4, Qt::Key_Q, 34 Qt::Key_A, Qt::Key_S, Qt::Key_Z, Qt::Key_X, Qt::Key_3, Qt::Key_4, Qt::Key_Q,
36 Qt::Key_W, Qt::Key_1, Qt::Key_2, Qt::Key_N, Qt::Key_M, Qt::Key_F, Qt::Key_T, 35 Qt::Key_W, Qt::Key_1, Qt::Key_2, Qt::Key_N, Qt::Key_M, Qt::Key_F, Qt::Key_T,
37 Qt::Key_H, Qt::Key_G, Qt::Key_Left, Qt::Key_Up, Qt::Key_Right, Qt::Key_Down, Qt::Key_J, 36 Qt::Key_H, Qt::Key_G, Qt::Key_D, Qt::Key_C, Qt::Key_B, Qt::Key_V,
38 Qt::Key_I, Qt::Key_L, Qt::Key_K, Qt::Key_D, Qt::Key_C, Qt::Key_B, Qt::Key_V,
39}; 37};
40 38
41const std::array<std::array<int, 5>, Settings::NativeAnalog::NumAnalogs> Config::default_analogs{{ 39const std::array<std::array<int, 4>, Settings::NativeAnalog::NumAnalogs> Config::default_analogs{{
42 { 40 {
43 Qt::Key_Up, 41 Qt::Key_Up,
44 Qt::Key_Down, 42 Qt::Key_Down,
45 Qt::Key_Left, 43 Qt::Key_Left,
46 Qt::Key_Right, 44 Qt::Key_Right,
47 Qt::Key_E,
48 }, 45 },
49 { 46 {
50 Qt::Key_I, 47 Qt::Key_I,
51 Qt::Key_K, 48 Qt::Key_K,
52 Qt::Key_J, 49 Qt::Key_J,
53 Qt::Key_L, 50 Qt::Key_L,
54 Qt::Key_R,
55 }, 51 },
56}}; 52}};
57 53
54const std::array<int, 2> Config::default_stick_mod = {
55 Qt::Key_E,
56 Qt::Key_R,
57};
58
58const std::array<int, Settings::NativeMouseButton::NumMouseButtons> Config::default_mouse_buttons = 59const std::array<int, Settings::NativeMouseButton::NumMouseButtons> Config::default_mouse_buttons =
59 { 60 {
60 Qt::Key_BracketLeft, Qt::Key_BracketRight, Qt::Key_Apostrophe, Qt::Key_Minus, Qt::Key_Equal, 61 Qt::Key_BracketLeft, Qt::Key_BracketRight, Qt::Key_Apostrophe, Qt::Key_Minus, Qt::Key_Equal,
@@ -243,10 +244,10 @@ void Config::ReadPlayerValues() {
243 player.connected = 244 player.connected =
244 ReadSetting(QStringLiteral("player_%1_connected").arg(p), false).toBool(); 245 ReadSetting(QStringLiteral("player_%1_connected").arg(p), false).toBool();
245 246
246 player.type = static_cast<Settings::ControllerType>( 247 player.controller_type = static_cast<Settings::ControllerType>(
247 qt_config 248 qt_config
248 ->value(QStringLiteral("player_%1_type").arg(p), 249 ->value(QStringLiteral("player_%1_type").arg(p),
249 static_cast<u8>(Settings::ControllerType::DualJoycon)) 250 static_cast<u8>(Settings::ControllerType::ProController))
250 .toUInt()); 251 .toUInt());
251 252
252 player.body_color_left = qt_config 253 player.body_color_left = qt_config
@@ -286,7 +287,7 @@ void Config::ReadPlayerValues() {
286 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { 287 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
287 const std::string default_param = InputCommon::GenerateAnalogParamFromKeys( 288 const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
288 default_analogs[i][0], default_analogs[i][1], default_analogs[i][2], 289 default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
289 default_analogs[i][3], default_analogs[i][4], 0.5f); 290 default_analogs[i][3], default_stick_mod[i], 0.5f);
290 auto& player_analogs = player.analogs[i]; 291 auto& player_analogs = player.analogs[i];
291 292
292 player_analogs = qt_config 293 player_analogs = qt_config
@@ -300,12 +301,6 @@ void Config::ReadPlayerValues() {
300 } 301 }
301 } 302 }
302 } 303 }
303
304 std::stable_partition(
305 Settings::values.players.begin(),
306 Settings::values.players.begin() +
307 Service::HID::Controller_NPad::NPadIdToIndex(Service::HID::NPAD_HANDHELD),
308 [](const auto& player) { return player.connected; });
309} 304}
310 305
311void Config::ReadDebugValues() { 306void Config::ReadDebugValues() {
@@ -330,7 +325,7 @@ void Config::ReadDebugValues() {
330 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { 325 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
331 const std::string default_param = InputCommon::GenerateAnalogParamFromKeys( 326 const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
332 default_analogs[i][0], default_analogs[i][1], default_analogs[i][2], 327 default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
333 default_analogs[i][3], default_analogs[i][4], 0.5f); 328 default_analogs[i][3], default_stick_mod[i], 0.5f);
334 auto& debug_pad_analogs = Settings::values.debug_pad_analogs[i]; 329 auto& debug_pad_analogs = Settings::values.debug_pad_analogs[i];
335 330
336 debug_pad_analogs = qt_config 331 debug_pad_analogs = qt_config
@@ -397,13 +392,6 @@ void Config::ReadTouchscreenValues() {
397 ReadSetting(QStringLiteral("touchscreen_diameter_y"), 15).toUInt(); 392 ReadSetting(QStringLiteral("touchscreen_diameter_y"), 15).toUInt();
398} 393}
399 394
400void Config::ApplyDefaultProfileIfInputInvalid() {
401 if (!std::any_of(Settings::values.players.begin(), Settings::values.players.end(),
402 [](const Settings::PlayerInput& in) { return in.connected; })) {
403 ApplyInputProfileConfiguration(UISettings::values.profile_index);
404 }
405}
406
407void Config::ReadAudioValues() { 395void Config::ReadAudioValues() {
408 qt_config->beginGroup(QStringLiteral("Audio")); 396 qt_config->beginGroup(QStringLiteral("Audio"));
409 397
@@ -432,12 +420,64 @@ void Config::ReadControlValues() {
432 ReadKeyboardValues(); 420 ReadKeyboardValues();
433 ReadMouseValues(); 421 ReadMouseValues();
434 ReadTouchscreenValues(); 422 ReadTouchscreenValues();
423 ReadMotionTouchValues();
424
425 Settings::values.vibration_enabled =
426 ReadSetting(QStringLiteral("vibration_enabled"), true).toBool();
427 Settings::values.use_docked_mode =
428 ReadSetting(QStringLiteral("use_docked_mode"), false).toBool();
429
430 qt_config->endGroup();
431}
432
433void Config::ReadMotionTouchValues() {
434 int num_touch_from_button_maps =
435 qt_config->beginReadArray(QStringLiteral("touch_from_button_maps"));
436
437 if (num_touch_from_button_maps > 0) {
438 const auto append_touch_from_button_map = [this] {
439 Settings::TouchFromButtonMap map;
440 map.name = ReadSetting(QStringLiteral("name"), QStringLiteral("default"))
441 .toString()
442 .toStdString();
443 const int num_touch_maps = qt_config->beginReadArray(QStringLiteral("entries"));
444 map.buttons.reserve(num_touch_maps);
445 for (int i = 0; i < num_touch_maps; i++) {
446 qt_config->setArrayIndex(i);
447 std::string touch_mapping =
448 ReadSetting(QStringLiteral("bind")).toString().toStdString();
449 map.buttons.emplace_back(std::move(touch_mapping));
450 }
451 qt_config->endArray(); // entries
452 Settings::values.touch_from_button_maps.emplace_back(std::move(map));
453 };
454
455 for (int i = 0; i < num_touch_from_button_maps; ++i) {
456 qt_config->setArrayIndex(i);
457 append_touch_from_button_map();
458 }
459 } else {
460 Settings::values.touch_from_button_maps.emplace_back(
461 Settings::TouchFromButtonMap{"default", {}});
462 num_touch_from_button_maps = 1;
463 }
464 qt_config->endArray();
435 465
436 Settings::values.motion_device = 466 Settings::values.motion_device =
437 ReadSetting(QStringLiteral("motion_device"), 467 ReadSetting(QStringLiteral("motion_device"),
438 QStringLiteral("engine:motion_emu,update_period:100,sensitivity:0.01")) 468 QStringLiteral("engine:motion_emu,update_period:100,sensitivity:0.01"))
439 .toString() 469 .toString()
440 .toStdString(); 470 .toStdString();
471 Settings::values.touch_device =
472 ReadSetting(QStringLiteral("touch_device"), QStringLiteral("engine:emu_window"))
473 .toString()
474 .toStdString();
475 Settings::values.use_touch_from_button =
476 ReadSetting(QStringLiteral("use_touch_from_button"), false).toBool();
477 Settings::values.touch_from_button_map_index =
478 ReadSetting(QStringLiteral("touch_from_button_map"), 0).toInt();
479 Settings::values.touch_from_button_map_index =
480 std::clamp(Settings::values.touch_from_button_map_index, 0, num_touch_from_button_maps - 1);
441 Settings::values.udp_input_address = 481 Settings::values.udp_input_address =
442 ReadSetting(QStringLiteral("udp_input_address"), 482 ReadSetting(QStringLiteral("udp_input_address"),
443 QString::fromUtf8(InputCommon::CemuhookUDP::DEFAULT_ADDR)) 483 QString::fromUtf8(InputCommon::CemuhookUDP::DEFAULT_ADDR))
@@ -448,10 +488,6 @@ void Config::ReadControlValues() {
448 .toInt()); 488 .toInt());
449 Settings::values.udp_pad_index = 489 Settings::values.udp_pad_index =
450 static_cast<u8>(ReadSetting(QStringLiteral("udp_pad_index"), 0).toUInt()); 490 static_cast<u8>(ReadSetting(QStringLiteral("udp_pad_index"), 0).toUInt());
451 Settings::values.use_docked_mode =
452 ReadSetting(QStringLiteral("use_docked_mode"), false).toBool();
453
454 qt_config->endGroup();
455} 491}
456 492
457void Config::ReadCoreValues() { 493void Config::ReadCoreValues() {
@@ -501,7 +537,7 @@ void Config::ReadDataStorageValues() {
501 Settings::values.gamecard_current_game = 537 Settings::values.gamecard_current_game =
502 ReadSetting(QStringLiteral("gamecard_current_game"), false).toBool(); 538 ReadSetting(QStringLiteral("gamecard_current_game"), false).toBool();
503 Settings::values.gamecard_path = 539 Settings::values.gamecard_path =
504 ReadSetting(QStringLiteral("gamecard_path"), QStringLiteral("")).toString().toStdString(); 540 ReadSetting(QStringLiteral("gamecard_path"), QString{}).toString().toStdString();
505 541
506 qt_config->endGroup(); 542 qt_config->endGroup();
507} 543}
@@ -515,7 +551,7 @@ void Config::ReadDebuggingValues() {
515 Settings::values.use_gdbstub = ReadSetting(QStringLiteral("use_gdbstub"), false).toBool(); 551 Settings::values.use_gdbstub = ReadSetting(QStringLiteral("use_gdbstub"), false).toBool();
516 Settings::values.gdbstub_port = ReadSetting(QStringLiteral("gdbstub_port"), 24689).toInt(); 552 Settings::values.gdbstub_port = ReadSetting(QStringLiteral("gdbstub_port"), 24689).toInt();
517 Settings::values.program_args = 553 Settings::values.program_args =
518 ReadSetting(QStringLiteral("program_args"), QStringLiteral("")).toString().toStdString(); 554 ReadSetting(QStringLiteral("program_args"), QString{}).toString().toStdString();
519 Settings::values.dump_exefs = ReadSetting(QStringLiteral("dump_exefs"), false).toBool(); 555 Settings::values.dump_exefs = ReadSetting(QStringLiteral("dump_exefs"), false).toBool();
520 Settings::values.dump_nso = ReadSetting(QStringLiteral("dump_nso"), false).toBool(); 556 Settings::values.dump_nso = ReadSetting(QStringLiteral("dump_nso"), false).toBool();
521 Settings::values.reporting_services = 557 Settings::values.reporting_services =
@@ -548,8 +584,7 @@ void Config::ReadDisabledAddOnValues() {
548 const auto d_size = qt_config->beginReadArray(QStringLiteral("disabled")); 584 const auto d_size = qt_config->beginReadArray(QStringLiteral("disabled"));
549 for (int j = 0; j < d_size; ++j) { 585 for (int j = 0; j < d_size; ++j) {
550 qt_config->setArrayIndex(j); 586 qt_config->setArrayIndex(j);
551 out.push_back( 587 out.push_back(ReadSetting(QStringLiteral("d"), QString{}).toString().toStdString());
552 ReadSetting(QStringLiteral("d"), QStringLiteral("")).toString().toStdString());
553 } 588 }
554 qt_config->endArray(); 589 qt_config->endArray();
555 Settings::values.disabled_addons.insert_or_assign(title_id, out); 590 Settings::values.disabled_addons.insert_or_assign(title_id, out);
@@ -788,14 +823,11 @@ void Config::ReadUIValues() {
788 UISettings::values.first_start = ReadSetting(QStringLiteral("firstStart"), true).toBool(); 823 UISettings::values.first_start = ReadSetting(QStringLiteral("firstStart"), true).toBool();
789 UISettings::values.callout_flags = ReadSetting(QStringLiteral("calloutFlags"), 0).toUInt(); 824 UISettings::values.callout_flags = ReadSetting(QStringLiteral("calloutFlags"), 0).toUInt();
790 UISettings::values.show_console = ReadSetting(QStringLiteral("showConsole"), false).toBool(); 825 UISettings::values.show_console = ReadSetting(QStringLiteral("showConsole"), false).toBool();
791 UISettings::values.profile_index = ReadSetting(QStringLiteral("profileIndex"), 0).toUInt();
792 UISettings::values.pause_when_in_background = 826 UISettings::values.pause_when_in_background =
793 ReadSetting(QStringLiteral("pauseWhenInBackground"), false).toBool(); 827 ReadSetting(QStringLiteral("pauseWhenInBackground"), false).toBool();
794 UISettings::values.hide_mouse = 828 UISettings::values.hide_mouse =
795 ReadSetting(QStringLiteral("hideInactiveMouse"), false).toBool(); 829 ReadSetting(QStringLiteral("hideInactiveMouse"), false).toBool();
796 830
797 ApplyDefaultProfileIfInputInvalid();
798
799 qt_config->endGroup(); 831 qt_config->endGroup();
800} 832}
801 833
@@ -869,8 +901,9 @@ void Config::SavePlayerValues() {
869 const auto& player = Settings::values.players[p]; 901 const auto& player = Settings::values.players[p];
870 902
871 WriteSetting(QStringLiteral("player_%1_connected").arg(p), player.connected, false); 903 WriteSetting(QStringLiteral("player_%1_connected").arg(p), player.connected, false);
872 WriteSetting(QStringLiteral("player_%1_type").arg(p), static_cast<u8>(player.type), 904 WriteSetting(QStringLiteral("player_%1_type").arg(p),
873 static_cast<u8>(Settings::ControllerType::DualJoycon)); 905 static_cast<u8>(player.controller_type),
906 static_cast<u8>(Settings::ControllerType::ProController));
874 907
875 WriteSetting(QStringLiteral("player_%1_body_color_left").arg(p), player.body_color_left, 908 WriteSetting(QStringLiteral("player_%1_body_color_left").arg(p), player.body_color_left,
876 Settings::JOYCON_BODY_NEON_BLUE); 909 Settings::JOYCON_BODY_NEON_BLUE);
@@ -892,7 +925,7 @@ void Config::SavePlayerValues() {
892 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { 925 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
893 const std::string default_param = InputCommon::GenerateAnalogParamFromKeys( 926 const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
894 default_analogs[i][0], default_analogs[i][1], default_analogs[i][2], 927 default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
895 default_analogs[i][3], default_analogs[i][4], 0.5f); 928 default_analogs[i][3], default_stick_mod[i], 0.5f);
896 WriteSetting(QStringLiteral("player_%1_").arg(p) + 929 WriteSetting(QStringLiteral("player_%1_").arg(p) +
897 QString::fromStdString(Settings::NativeAnalog::mapping[i]), 930 QString::fromStdString(Settings::NativeAnalog::mapping[i]),
898 QString::fromStdString(player.analogs[i]), 931 QString::fromStdString(player.analogs[i]),
@@ -913,7 +946,7 @@ void Config::SaveDebugValues() {
913 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { 946 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
914 const std::string default_param = InputCommon::GenerateAnalogParamFromKeys( 947 const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
915 default_analogs[i][0], default_analogs[i][1], default_analogs[i][2], 948 default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
916 default_analogs[i][3], default_analogs[i][4], 0.5f); 949 default_analogs[i][3], default_stick_mod[i], 0.5f);
917 WriteSetting(QStringLiteral("debug_pad_") + 950 WriteSetting(QStringLiteral("debug_pad_") +
918 QString::fromStdString(Settings::NativeAnalog::mapping[i]), 951 QString::fromStdString(Settings::NativeAnalog::mapping[i]),
919 QString::fromStdString(Settings::values.debug_pad_analogs[i]), 952 QString::fromStdString(Settings::values.debug_pad_analogs[i]),
@@ -947,6 +980,43 @@ void Config::SaveTouchscreenValues() {
947 WriteSetting(QStringLiteral("touchscreen_diameter_y"), touchscreen.diameter_y, 15); 980 WriteSetting(QStringLiteral("touchscreen_diameter_y"), touchscreen.diameter_y, 15);
948} 981}
949 982
983void Config::SaveMotionTouchValues() {
984 WriteSetting(QStringLiteral("motion_device"),
985 QString::fromStdString(Settings::values.motion_device),
986 QStringLiteral("engine:motion_emu,update_period:100,sensitivity:0.01"));
987 WriteSetting(QStringLiteral("touch_device"),
988 QString::fromStdString(Settings::values.touch_device),
989 QStringLiteral("engine:emu_window"));
990 WriteSetting(QStringLiteral("use_touch_from_button"), Settings::values.use_touch_from_button,
991 false);
992 WriteSetting(QStringLiteral("touch_from_button_map"),
993 Settings::values.touch_from_button_map_index, 0);
994 WriteSetting(QStringLiteral("udp_input_address"),
995 QString::fromStdString(Settings::values.udp_input_address),
996 QString::fromUtf8(InputCommon::CemuhookUDP::DEFAULT_ADDR));
997 WriteSetting(QStringLiteral("udp_input_port"), Settings::values.udp_input_port,
998 InputCommon::CemuhookUDP::DEFAULT_PORT);
999 WriteSetting(QStringLiteral("udp_pad_index"), Settings::values.udp_pad_index, 0);
1000
1001 qt_config->beginWriteArray(QStringLiteral("touch_from_button_maps"));
1002 for (std::size_t p = 0; p < Settings::values.touch_from_button_maps.size(); ++p) {
1003 qt_config->setArrayIndex(static_cast<int>(p));
1004 WriteSetting(QStringLiteral("name"),
1005 QString::fromStdString(Settings::values.touch_from_button_maps[p].name),
1006 QStringLiteral("default"));
1007 qt_config->beginWriteArray(QStringLiteral("entries"));
1008 for (std::size_t q = 0; q < Settings::values.touch_from_button_maps[p].buttons.size();
1009 ++q) {
1010 qt_config->setArrayIndex(static_cast<int>(q));
1011 WriteSetting(
1012 QStringLiteral("bind"),
1013 QString::fromStdString(Settings::values.touch_from_button_maps[p].buttons[q]));
1014 }
1015 qt_config->endArray();
1016 }
1017 qt_config->endArray();
1018}
1019
950void Config::SaveValues() { 1020void Config::SaveValues() {
951 if (global) { 1021 if (global) {
952 SaveControlValues(); 1022 SaveControlValues();
@@ -989,17 +1059,16 @@ void Config::SaveControlValues() {
989 SaveDebugValues(); 1059 SaveDebugValues();
990 SaveMouseValues(); 1060 SaveMouseValues();
991 SaveTouchscreenValues(); 1061 SaveTouchscreenValues();
1062 SaveMotionTouchValues();
992 1063
1064 WriteSetting(QStringLiteral("vibration_enabled"), Settings::values.vibration_enabled, true);
993 WriteSetting(QStringLiteral("motion_device"), 1065 WriteSetting(QStringLiteral("motion_device"),
994 QString::fromStdString(Settings::values.motion_device), 1066 QString::fromStdString(Settings::values.motion_device),
995 QStringLiteral("engine:motion_emu,update_period:100,sensitivity:0.01")); 1067 QStringLiteral("engine:motion_emu,update_period:100,sensitivity:0.01"));
1068 WriteSetting(QStringLiteral("touch_device"),
1069 QString::fromStdString(Settings::values.touch_device),
1070 QStringLiteral("engine:emu_window"));
996 WriteSetting(QStringLiteral("keyboard_enabled"), Settings::values.keyboard_enabled, false); 1071 WriteSetting(QStringLiteral("keyboard_enabled"), Settings::values.keyboard_enabled, false);
997 WriteSetting(QStringLiteral("udp_input_address"),
998 QString::fromStdString(Settings::values.udp_input_address),
999 QString::fromUtf8(InputCommon::CemuhookUDP::DEFAULT_ADDR));
1000 WriteSetting(QStringLiteral("udp_input_port"), Settings::values.udp_input_port,
1001 InputCommon::CemuhookUDP::DEFAULT_PORT);
1002 WriteSetting(QStringLiteral("udp_pad_index"), Settings::values.udp_pad_index, 0);
1003 WriteSetting(QStringLiteral("use_docked_mode"), Settings::values.use_docked_mode, false); 1072 WriteSetting(QStringLiteral("use_docked_mode"), Settings::values.use_docked_mode, false);
1004 1073
1005 qt_config->endGroup(); 1074 qt_config->endGroup();
@@ -1036,7 +1105,7 @@ void Config::SaveDataStorageValues() {
1036 WriteSetting(QStringLiteral("gamecard_current_game"), Settings::values.gamecard_current_game, 1105 WriteSetting(QStringLiteral("gamecard_current_game"), Settings::values.gamecard_current_game,
1037 false); 1106 false);
1038 WriteSetting(QStringLiteral("gamecard_path"), 1107 WriteSetting(QStringLiteral("gamecard_path"),
1039 QString::fromStdString(Settings::values.gamecard_path), QStringLiteral("")); 1108 QString::fromStdString(Settings::values.gamecard_path), QString{});
1040 1109
1041 qt_config->endGroup(); 1110 qt_config->endGroup();
1042} 1111}
@@ -1049,7 +1118,7 @@ void Config::SaveDebuggingValues() {
1049 WriteSetting(QStringLiteral("use_gdbstub"), Settings::values.use_gdbstub, false); 1118 WriteSetting(QStringLiteral("use_gdbstub"), Settings::values.use_gdbstub, false);
1050 WriteSetting(QStringLiteral("gdbstub_port"), Settings::values.gdbstub_port, 24689); 1119 WriteSetting(QStringLiteral("gdbstub_port"), Settings::values.gdbstub_port, 24689);
1051 WriteSetting(QStringLiteral("program_args"), 1120 WriteSetting(QStringLiteral("program_args"),
1052 QString::fromStdString(Settings::values.program_args), QStringLiteral("")); 1121 QString::fromStdString(Settings::values.program_args), QString{});
1053 WriteSetting(QStringLiteral("dump_exefs"), Settings::values.dump_exefs, false); 1122 WriteSetting(QStringLiteral("dump_exefs"), Settings::values.dump_exefs, false);
1054 WriteSetting(QStringLiteral("dump_nso"), Settings::values.dump_nso, false); 1123 WriteSetting(QStringLiteral("dump_nso"), Settings::values.dump_nso, false);
1055 WriteSetting(QStringLiteral("quest_flag"), Settings::values.quest_flag, false); 1124 WriteSetting(QStringLiteral("quest_flag"), Settings::values.quest_flag, false);
@@ -1076,8 +1145,7 @@ void Config::SaveDisabledAddOnValues() {
1076 qt_config->beginWriteArray(QStringLiteral("disabled")); 1145 qt_config->beginWriteArray(QStringLiteral("disabled"));
1077 for (std::size_t j = 0; j < elem.second.size(); ++j) { 1146 for (std::size_t j = 0; j < elem.second.size(); ++j) {
1078 qt_config->setArrayIndex(static_cast<int>(j)); 1147 qt_config->setArrayIndex(static_cast<int>(j));
1079 WriteSetting(QStringLiteral("d"), QString::fromStdString(elem.second[j]), 1148 WriteSetting(QStringLiteral("d"), QString::fromStdString(elem.second[j]), QString{});
1080 QStringLiteral(""));
1081 } 1149 }
1082 qt_config->endArray(); 1150 qt_config->endArray();
1083 ++i; 1151 ++i;
@@ -1266,7 +1334,6 @@ void Config::SaveUIValues() {
1266 WriteSetting(QStringLiteral("firstStart"), UISettings::values.first_start, true); 1334 WriteSetting(QStringLiteral("firstStart"), UISettings::values.first_start, true);
1267 WriteSetting(QStringLiteral("calloutFlags"), UISettings::values.callout_flags, 0); 1335 WriteSetting(QStringLiteral("calloutFlags"), UISettings::values.callout_flags, 0);
1268 WriteSetting(QStringLiteral("showConsole"), UISettings::values.show_console, false); 1336 WriteSetting(QStringLiteral("showConsole"), UISettings::values.show_console, false);
1269 WriteSetting(QStringLiteral("profileIndex"), UISettings::values.profile_index, 0);
1270 WriteSetting(QStringLiteral("pauseWhenInBackground"), 1337 WriteSetting(QStringLiteral("pauseWhenInBackground"),
1271 UISettings::values.pause_when_in_background, false); 1338 UISettings::values.pause_when_in_background, false);
1272 WriteSetting(QStringLiteral("hideInactiveMouse"), UISettings::values.hide_mouse, false); 1339 WriteSetting(QStringLiteral("hideInactiveMouse"), UISettings::values.hide_mouse, false);
diff --git a/src/yuzu/configuration/config.h b/src/yuzu/configuration/config.h
index e5f39b040..ca0d29c6c 100644
--- a/src/yuzu/configuration/config.h
+++ b/src/yuzu/configuration/config.h
@@ -23,7 +23,8 @@ public:
23 void Save(); 23 void Save();
24 24
25 static const std::array<int, Settings::NativeButton::NumButtons> default_buttons; 25 static const std::array<int, Settings::NativeButton::NumButtons> default_buttons;
26 static const std::array<std::array<int, 5>, Settings::NativeAnalog::NumAnalogs> default_analogs; 26 static const std::array<std::array<int, 4>, Settings::NativeAnalog::NumAnalogs> default_analogs;
27 static const std::array<int, 2> default_stick_mod;
27 static const std::array<int, Settings::NativeMouseButton::NumMouseButtons> 28 static const std::array<int, Settings::NativeMouseButton::NumMouseButtons>
28 default_mouse_buttons; 29 default_mouse_buttons;
29 static const std::array<int, Settings::NativeKeyboard::NumKeyboardKeys> default_keyboard_keys; 30 static const std::array<int, Settings::NativeKeyboard::NumKeyboardKeys> default_keyboard_keys;
@@ -37,7 +38,7 @@ private:
37 void ReadKeyboardValues(); 38 void ReadKeyboardValues();
38 void ReadMouseValues(); 39 void ReadMouseValues();
39 void ReadTouchscreenValues(); 40 void ReadTouchscreenValues();
40 void ApplyDefaultProfileIfInputInvalid(); 41 void ReadMotionTouchValues();
41 42
42 // Read functions bases off the respective config section names. 43 // Read functions bases off the respective config section names.
43 void ReadAudioValues(); 44 void ReadAudioValues();
@@ -64,6 +65,7 @@ private:
64 void SaveDebugValues(); 65 void SaveDebugValues();
65 void SaveMouseValues(); 66 void SaveMouseValues();
66 void SaveTouchscreenValues(); 67 void SaveTouchscreenValues();
68 void SaveMotionTouchValues();
67 69
68 // Save functions based off the respective config section names. 70 // Save functions based off the respective config section names.
69 void SaveAudioValues(); 71 void SaveAudioValues();
diff --git a/src/yuzu/configuration/configure.ui b/src/yuzu/configuration/configure.ui
index 5f5d8e571..fcf42cdcb 100644
--- a/src/yuzu/configuration/configure.ui
+++ b/src/yuzu/configuration/configure.ui
@@ -6,7 +6,7 @@
6 <rect> 6 <rect>
7 <x>0</x> 7 <x>0</x>
8 <y>0</y> 8 <y>0</y>
9 <width>382</width> 9 <width>650</width>
10 <height>650</height> 10 <height>650</height>
11 </rect> 11 </rect>
12 </property> 12 </property>
@@ -26,13 +26,13 @@
26 <widget class="QListWidget" name="selectorList"> 26 <widget class="QListWidget" name="selectorList">
27 <property name="minimumSize"> 27 <property name="minimumSize">
28 <size> 28 <size>
29 <width>150</width> 29 <width>120</width>
30 <height>0</height> 30 <height>0</height>
31 </size> 31 </size>
32 </property> 32 </property>
33 <property name="maximumSize"> 33 <property name="maximumSize">
34 <size> 34 <size>
35 <width>150</width> 35 <width>120</width>
36 <height>16777215</height> 36 <height>16777215</height>
37 </size> 37 </size>
38 </property> 38 </property>
@@ -44,76 +44,121 @@
44 <number>0</number> 44 <number>0</number>
45 </property> 45 </property>
46 <widget class="ConfigureGeneral" name="generalTab"> 46 <widget class="ConfigureGeneral" name="generalTab">
47 <property name="accessibleName">
48 <string>General</string>
49 </property>
47 <attribute name="title"> 50 <attribute name="title">
48 <string>General</string> 51 <string>General</string>
49 </attribute> 52 </attribute>
50 </widget> 53 </widget>
51 <widget class="ConfigureUi" name="uiTab"> 54 <widget class="ConfigureUi" name="uiTab">
55 <property name="accessibleName">
56 <string>UI</string>
57 </property>
52 <attribute name="title"> 58 <attribute name="title">
53 <string>Game List</string> 59 <string>Game List</string>
54 </attribute> 60 </attribute>
55 </widget> 61 </widget>
56 <widget class="ConfigureSystem" name="systemTab"> 62 <widget class="ConfigureSystem" name="systemTab">
63 <property name="accessibleName">
64 <string>System</string>
65 </property>
57 <attribute name="title"> 66 <attribute name="title">
58 <string>System</string> 67 <string>System</string>
59 </attribute> 68 </attribute>
60 </widget> 69 </widget>
61 <widget class="ConfigureProfileManager" name="profileManagerTab"> 70 <widget class="ConfigureProfileManager" name="profileManagerTab">
71 <property name="accessibleName">
72 <string>Profiles</string>
73 </property>
62 <attribute name="title"> 74 <attribute name="title">
63 <string>Profiles</string> 75 <string>Profiles</string>
64 </attribute> 76 </attribute>
65 </widget> 77 </widget>
66 <widget class="ConfigureFilesystem" name="filesystemTab"> 78 <widget class="ConfigureFilesystem" name="filesystemTab">
79 <property name="accessibleName">
80 <string>Filesystem</string>
81 </property>
67 <attribute name="title"> 82 <attribute name="title">
68 <string>Filesystem</string> 83 <string>Filesystem</string>
69 </attribute> 84 </attribute>
70 </widget> 85 </widget>
71 <widget class="ConfigureInputSimple" name="inputTab"> 86 <widget class="ConfigureInput" name="inputTab">
87 <property name="accessibleName">
88 <string>Controls</string>
89 </property>
72 <attribute name="title"> 90 <attribute name="title">
73 <string>Input</string> 91 <string>Controls</string>
74 </attribute> 92 </attribute>
75 </widget> 93 </widget>
76 <widget class="ConfigureHotkeys" name="hotkeysTab"> 94 <widget class="ConfigureHotkeys" name="hotkeysTab">
95 <property name="accessibleName">
96 <string>Hotkeys</string>
97 </property>
77 <attribute name="title"> 98 <attribute name="title">
78 <string>Hotkeys</string> 99 <string>Hotkeys</string>
79 </attribute> 100 </attribute>
80 </widget> 101 </widget>
81 <widget class="ConfigureCpu" name="cpuTab"> 102 <widget class="ConfigureCpu" name="cpuTab">
103 <property name="accessibleName">
104 <string>CPU</string>
105 </property>
82 <attribute name="title"> 106 <attribute name="title">
83 <string>CPU</string> 107 <string>CPU</string>
84 </attribute> 108 </attribute>
85 </widget> 109 </widget>
86 <widget class="ConfigureCpuDebug" name="cpuDebugTab"> 110 <widget class="ConfigureCpuDebug" name="cpuDebugTab">
111 <property name="accessibleName">
112 <string>Debug</string>
113 </property>
87 <attribute name="title"> 114 <attribute name="title">
88 <string>Debug</string> 115 <string>Debug</string>
89 </attribute> 116 </attribute>
90 </widget> 117 </widget>
91 <widget class="ConfigureGraphics" name="graphicsTab"> 118 <widget class="ConfigureGraphics" name="graphicsTab">
119 <property name="accessibleName">
120 <string>Graphics</string>
121 </property>
92 <attribute name="title"> 122 <attribute name="title">
93 <string>Graphics</string> 123 <string>Graphics</string>
94 </attribute> 124 </attribute>
95 </widget> 125 </widget>
96 <widget class="ConfigureGraphicsAdvanced" name="graphicsAdvancedTab"> 126 <widget class="ConfigureGraphicsAdvanced" name="graphicsAdvancedTab">
127 <property name="accessibleName">
128 <string>Advanced</string>
129 </property>
97 <attribute name="title"> 130 <attribute name="title">
98 <string>GraphicsAdvanced</string> 131 <string>GraphicsAdvanced</string>
99 </attribute> 132 </attribute>
100 </widget> 133 </widget>
101 <widget class="ConfigureAudio" name="audioTab"> 134 <widget class="ConfigureAudio" name="audioTab">
135 <property name="accessibleName">
136 <string>Audio</string>
137 </property>
102 <attribute name="title"> 138 <attribute name="title">
103 <string>Audio</string> 139 <string>Audio</string>
104 </attribute> 140 </attribute>
105 </widget> 141 </widget>
106 <widget class="ConfigureDebug" name="debugTab"> 142 <widget class="ConfigureDebug" name="debugTab">
143 <property name="accessibleName">
144 <string>Debug</string>
145 </property>
107 <attribute name="title"> 146 <attribute name="title">
108 <string>Debug</string> 147 <string>Debug</string>
109 </attribute> 148 </attribute>
110 </widget> 149 </widget>
111 <widget class="ConfigureWeb" name="webTab"> 150 <widget class="ConfigureWeb" name="webTab">
151 <property name="accessibleName">
152 <string>Web</string>
153 </property>
112 <attribute name="title"> 154 <attribute name="title">
113 <string>Web</string> 155 <string>Web</string>
114 </attribute> 156 </attribute>
115 </widget> 157 </widget>
116 <widget class="ConfigureService" name="serviceTab"> 158 <widget class="ConfigureService" name="serviceTab">
159 <property name="accessibleName">
160 <string>Services</string>
161 </property>
117 <attribute name="title"> 162 <attribute name="title">
118 <string>Services</string> 163 <string>Services</string>
119 </attribute> 164 </attribute>
@@ -205,9 +250,9 @@
205 <container>1</container> 250 <container>1</container>
206 </customwidget> 251 </customwidget>
207 <customwidget> 252 <customwidget>
208 <class>ConfigureInputSimple</class> 253 <class>ConfigureInput</class>
209 <extends>QWidget</extends> 254 <extends>QWidget</extends>
210 <header>configuration/configure_input_simple.h</header> 255 <header>configuration/configure_input.h</header>
211 <container>1</container> 256 <container>1</container>
212 </customwidget> 257 </customwidget>
213 <customwidget> 258 <customwidget>
diff --git a/src/yuzu/configuration/configure_debug_controller.cpp b/src/yuzu/configuration/configure_debug_controller.cpp
new file mode 100644
index 000000000..0097c9a29
--- /dev/null
+++ b/src/yuzu/configuration/configure_debug_controller.cpp
@@ -0,0 +1,40 @@
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 "ui_configure_debug_controller.h"
6#include "yuzu/configuration/configure_debug_controller.h"
7
8ConfigureDebugController::ConfigureDebugController(QWidget* parent,
9 InputCommon::InputSubsystem* input_subsystem)
10 : QDialog(parent), ui(std::make_unique<Ui::ConfigureDebugController>()),
11 debug_controller(new ConfigureInputPlayer(this, 9, nullptr, input_subsystem, true)) {
12 ui->setupUi(this);
13
14 ui->controllerLayout->addWidget(debug_controller);
15
16 connect(ui->clear_all_button, &QPushButton::clicked, this,
17 [this] { debug_controller->ClearAll(); });
18 connect(ui->restore_defaults_button, &QPushButton::clicked, this,
19 [this] { debug_controller->RestoreDefaults(); });
20
21 RetranslateUI();
22}
23
24ConfigureDebugController::~ConfigureDebugController() = default;
25
26void ConfigureDebugController::ApplyConfiguration() {
27 debug_controller->ApplyConfiguration();
28}
29
30void ConfigureDebugController::changeEvent(QEvent* event) {
31 if (event->type() == QEvent::LanguageChange) {
32 RetranslateUI();
33 }
34
35 QDialog::changeEvent(event);
36}
37
38void ConfigureDebugController::RetranslateUI() {
39 ui->retranslateUi(this);
40}
diff --git a/src/yuzu/configuration/configure_debug_controller.h b/src/yuzu/configuration/configure_debug_controller.h
new file mode 100644
index 000000000..34dcf705f
--- /dev/null
+++ b/src/yuzu/configuration/configure_debug_controller.h
@@ -0,0 +1,38 @@
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 <QDialog>
9#include "yuzu/configuration/configure_input_player.h"
10
11class QPushButton;
12
13namespace InputCommon {
14class InputSubsystem;
15}
16
17namespace Ui {
18class ConfigureDebugController;
19}
20
21class ConfigureDebugController : public QDialog {
22 Q_OBJECT
23
24public:
25 explicit ConfigureDebugController(QWidget* parent,
26 InputCommon::InputSubsystem* input_subsystem);
27 ~ConfigureDebugController() override;
28
29 void ApplyConfiguration();
30
31private:
32 void changeEvent(QEvent* event) override;
33 void RetranslateUI();
34
35 std::unique_ptr<Ui::ConfigureDebugController> ui;
36
37 ConfigureInputPlayer* debug_controller;
38};
diff --git a/src/yuzu/configuration/configure_debug_controller.ui b/src/yuzu/configuration/configure_debug_controller.ui
new file mode 100644
index 000000000..a95ed50ff
--- /dev/null
+++ b/src/yuzu/configuration/configure_debug_controller.ui
@@ -0,0 +1,97 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<ui version="4.0">
3 <class>ConfigureDebugController</class>
4 <widget class="QDialog" name="ConfigureDebugController">
5 <property name="geometry">
6 <rect>
7 <x>0</x>
8 <y>0</y>
9 <width>780</width>
10 <height>500</height>
11 </rect>
12 </property>
13 <property name="windowTitle">
14 <string>Configure Debug Controller</string>
15 </property>
16 <layout class="QVBoxLayout" name="verticalLayout">
17 <property name="spacing">
18 <number>2</number>
19 </property>
20 <property name="leftMargin">
21 <number>9</number>
22 </property>
23 <property name="topMargin">
24 <number>9</number>
25 </property>
26 <property name="rightMargin">
27 <number>9</number>
28 </property>
29 <property name="bottomMargin">
30 <number>9</number>
31 </property>
32 <item>
33 <layout class="QHBoxLayout" name="controllerLayout"/>
34 </item>
35 <item>
36 <layout class="QHBoxLayout" name="horizontalLayout">
37 <item>
38 <widget class="QPushButton" name="clear_all_button">
39 <property name="text">
40 <string>Clear</string>
41 </property>
42 </widget>
43 </item>
44 <item>
45 <widget class="QPushButton" name="restore_defaults_button">
46 <property name="text">
47 <string>Defaults</string>
48 </property>
49 </widget>
50 </item>
51 <item>
52 <widget class="QDialogButtonBox" name="buttonBox">
53 <property name="standardButtons">
54 <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
55 </property>
56 </widget>
57 </item>
58 </layout>
59 </item>
60 </layout>
61 </widget>
62 <resources/>
63 <connections>
64 <connection>
65 <sender>buttonBox</sender>
66 <signal>accepted()</signal>
67 <receiver>ConfigureDebugController</receiver>
68 <slot>accept()</slot>
69 <hints>
70 <hint type="sourcelabel">
71 <x>140</x>
72 <y>318</y>
73 </hint>
74 <hint type="destinationlabel">
75 <x>140</x>
76 <y>169</y>
77 </hint>
78 </hints>
79 </connection>
80 <connection>
81 <sender>buttonBox</sender>
82 <signal>rejected()</signal>
83 <receiver>ConfigureDebugController</receiver>
84 <slot>reject()</slot>
85 <hints>
86 <hint type="sourcelabel">
87 <x>140</x>
88 <y>318</y>
89 </hint>
90 <hint type="destinationlabel">
91 <x>140</x>
92 <y>169</y>
93 </hint>
94 </hints>
95 </connection>
96 </connections>
97</ui>
diff --git a/src/yuzu/configuration/configure_dialog.cpp b/src/yuzu/configuration/configure_dialog.cpp
index 4e30dc51e..8186929a6 100644
--- a/src/yuzu/configuration/configure_dialog.cpp
+++ b/src/yuzu/configuration/configure_dialog.cpp
@@ -12,7 +12,8 @@
12#include "yuzu/configuration/configure_input_player.h" 12#include "yuzu/configuration/configure_input_player.h"
13#include "yuzu/hotkeys.h" 13#include "yuzu/hotkeys.h"
14 14
15ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry) 15ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry,
16 InputCommon::InputSubsystem* input_subsystem)
16 : QDialog(parent), ui(new Ui::ConfigureDialog), registry(registry) { 17 : QDialog(parent), ui(new Ui::ConfigureDialog), registry(registry) {
17 Settings::configuring_global = true; 18 Settings::configuring_global = true;
18 19
@@ -20,6 +21,8 @@ ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry)
20 ui->hotkeysTab->Populate(registry); 21 ui->hotkeysTab->Populate(registry);
21 setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); 22 setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
22 23
24 ui->inputTab->Initialize(input_subsystem);
25
23 SetConfiguration(); 26 SetConfiguration();
24 PopulateSelectionList(); 27 PopulateSelectionList();
25 28
@@ -80,12 +83,12 @@ Q_DECLARE_METATYPE(QList<QWidget*>);
80 83
81void ConfigureDialog::PopulateSelectionList() { 84void ConfigureDialog::PopulateSelectionList() {
82 const std::array<std::pair<QString, QList<QWidget*>>, 6> items{ 85 const std::array<std::pair<QString, QList<QWidget*>>, 6> items{
83 {{tr("General"), {ui->generalTab, ui->webTab, ui->debugTab, ui->uiTab}}, 86 {{tr("General"), {ui->generalTab, ui->hotkeysTab, ui->uiTab, ui->webTab, ui->debugTab}},
84 {tr("System"), {ui->systemTab, ui->profileManagerTab, ui->serviceTab, ui->filesystemTab}}, 87 {tr("System"), {ui->systemTab, ui->profileManagerTab, ui->serviceTab, ui->filesystemTab}},
85 {tr("CPU"), {ui->cpuTab, ui->cpuDebugTab}}, 88 {tr("CPU"), {ui->cpuTab, ui->cpuDebugTab}},
86 {tr("Graphics"), {ui->graphicsTab, ui->graphicsAdvancedTab}}, 89 {tr("Graphics"), {ui->graphicsTab, ui->graphicsAdvancedTab}},
87 {tr("Audio"), {ui->audioTab}}, 90 {tr("Audio"), {ui->audioTab}},
88 {tr("Controls"), {ui->inputTab, ui->hotkeysTab}}}, 91 {tr("Controls"), ui->inputTab->GetSubTabs()}},
89 }; 92 };
90 93
91 [[maybe_unused]] const QSignalBlocker blocker(ui->selectorList); 94 [[maybe_unused]] const QSignalBlocker blocker(ui->selectorList);
@@ -117,7 +120,7 @@ void ConfigureDialog::UpdateVisibleTabs() {
117 {ui->generalTab, tr("General")}, 120 {ui->generalTab, tr("General")},
118 {ui->systemTab, tr("System")}, 121 {ui->systemTab, tr("System")},
119 {ui->profileManagerTab, tr("Profiles")}, 122 {ui->profileManagerTab, tr("Profiles")},
120 {ui->inputTab, tr("Input")}, 123 {ui->inputTab, tr("Controls")},
121 {ui->hotkeysTab, tr("Hotkeys")}, 124 {ui->hotkeysTab, tr("Hotkeys")},
122 {ui->cpuTab, tr("CPU")}, 125 {ui->cpuTab, tr("CPU")},
123 {ui->cpuDebugTab, tr("Debug")}, 126 {ui->cpuDebugTab, tr("Debug")},
@@ -138,6 +141,6 @@ void ConfigureDialog::UpdateVisibleTabs() {
138 const QList<QWidget*> tabs = qvariant_cast<QList<QWidget*>>(items[0]->data(Qt::UserRole)); 141 const QList<QWidget*> tabs = qvariant_cast<QList<QWidget*>>(items[0]->data(Qt::UserRole));
139 142
140 for (const auto tab : tabs) { 143 for (const auto tab : tabs) {
141 ui->tabWidget->addTab(tab, widgets.at(tab)); 144 ui->tabWidget->addTab(tab, tab->accessibleName());
142 } 145 }
143} 146}
diff --git a/src/yuzu/configuration/configure_dialog.h b/src/yuzu/configuration/configure_dialog.h
index 4289bc225..570c3b941 100644
--- a/src/yuzu/configuration/configure_dialog.h
+++ b/src/yuzu/configuration/configure_dialog.h
@@ -9,6 +9,10 @@
9 9
10class HotkeyRegistry; 10class HotkeyRegistry;
11 11
12namespace InputCommon {
13class InputSubsystem;
14}
15
12namespace Ui { 16namespace Ui {
13class ConfigureDialog; 17class ConfigureDialog;
14} 18}
@@ -17,7 +21,8 @@ class ConfigureDialog : public QDialog {
17 Q_OBJECT 21 Q_OBJECT
18 22
19public: 23public:
20 explicit ConfigureDialog(QWidget* parent, HotkeyRegistry& registry); 24 explicit ConfigureDialog(QWidget* parent, HotkeyRegistry& registry,
25 InputCommon::InputSubsystem* input_subsystem);
21 ~ConfigureDialog() override; 26 ~ConfigureDialog() override;
22 27
23 void ApplyConfiguration(); 28 void ApplyConfiguration();
diff --git a/src/yuzu/configuration/configure_input.cpp b/src/yuzu/configuration/configure_input.cpp
index f2977719c..ae3e31762 100644
--- a/src/yuzu/configuration/configure_input.cpp
+++ b/src/yuzu/configuration/configure_input.cpp
@@ -8,18 +8,33 @@
8#include <QSignalBlocker> 8#include <QSignalBlocker>
9#include <QTimer> 9#include <QTimer>
10 10
11#include "configuration/configure_touchscreen_advanced.h"
12#include "core/core.h" 11#include "core/core.h"
13#include "core/hle/service/am/am.h" 12#include "core/hle/service/am/am.h"
14#include "core/hle/service/am/applet_ae.h" 13#include "core/hle/service/am/applet_ae.h"
15#include "core/hle/service/am/applet_oe.h" 14#include "core/hle/service/am/applet_oe.h"
16#include "core/hle/service/hid/controllers/npad.h"
17#include "core/hle/service/sm/sm.h" 15#include "core/hle/service/sm/sm.h"
18#include "ui_configure_input.h" 16#include "ui_configure_input.h"
17#include "ui_configure_input_advanced.h"
19#include "ui_configure_input_player.h" 18#include "ui_configure_input_player.h"
19#include "yuzu/configuration/configure_debug_controller.h"
20#include "yuzu/configuration/configure_input.h" 20#include "yuzu/configuration/configure_input.h"
21#include "yuzu/configuration/configure_input_advanced.h"
21#include "yuzu/configuration/configure_input_player.h" 22#include "yuzu/configuration/configure_input_player.h"
23#include "yuzu/configuration/configure_motion_touch.h"
22#include "yuzu/configuration/configure_mouse_advanced.h" 24#include "yuzu/configuration/configure_mouse_advanced.h"
25#include "yuzu/configuration/configure_touchscreen_advanced.h"
26
27namespace {
28template <typename Dialog, typename... Args>
29void CallConfigureDialog(ConfigureInput& parent, Args&&... args) {
30 Dialog dialog(&parent, std::forward<Args>(args)...);
31
32 const auto res = dialog.exec();
33 if (res == QDialog::Accepted) {
34 dialog.ApplyConfiguration();
35 }
36}
37} // Anonymous namespace
23 38
24void OnDockedModeChanged(bool last_state, bool new_state) { 39void OnDockedModeChanged(bool last_state, bool new_state) {
25 if (last_state == new_state) { 40 if (last_state == new_state) {
@@ -48,97 +63,102 @@ void OnDockedModeChanged(bool last_state, bool new_state) {
48 } 63 }
49} 64}
50 65
51namespace {
52template <typename Dialog, typename... Args>
53void CallConfigureDialog(ConfigureInput& parent, Args&&... args) {
54 parent.ApplyConfiguration();
55 Dialog dialog(&parent, std::forward<Args>(args)...);
56
57 const auto res = dialog.exec();
58 if (res == QDialog::Accepted) {
59 dialog.ApplyConfiguration();
60 }
61}
62} // Anonymous namespace
63
64ConfigureInput::ConfigureInput(QWidget* parent) 66ConfigureInput::ConfigureInput(QWidget* parent)
65 : QDialog(parent), ui(std::make_unique<Ui::ConfigureInput>()) { 67 : QWidget(parent), ui(std::make_unique<Ui::ConfigureInput>()) {
66 ui->setupUi(this); 68 ui->setupUi(this);
69}
67 70
68 players_controller = { 71ConfigureInput::~ConfigureInput() = default;
69 ui->player1_combobox, ui->player2_combobox, ui->player3_combobox, ui->player4_combobox,
70 ui->player5_combobox, ui->player6_combobox, ui->player7_combobox, ui->player8_combobox,
71 };
72 72
73 players_configure = { 73void ConfigureInput::Initialize(InputCommon::InputSubsystem* input_subsystem) {
74 ui->player1_configure, ui->player2_configure, ui->player3_configure, ui->player4_configure, 74 player_controllers = {
75 ui->player5_configure, ui->player6_configure, ui->player7_configure, ui->player8_configure, 75 new ConfigureInputPlayer(this, 0, ui->consoleInputSettings, input_subsystem),
76 new ConfigureInputPlayer(this, 1, ui->consoleInputSettings, input_subsystem),
77 new ConfigureInputPlayer(this, 2, ui->consoleInputSettings, input_subsystem),
78 new ConfigureInputPlayer(this, 3, ui->consoleInputSettings, input_subsystem),
79 new ConfigureInputPlayer(this, 4, ui->consoleInputSettings, input_subsystem),
80 new ConfigureInputPlayer(this, 5, ui->consoleInputSettings, input_subsystem),
81 new ConfigureInputPlayer(this, 6, ui->consoleInputSettings, input_subsystem),
82 new ConfigureInputPlayer(this, 7, ui->consoleInputSettings, input_subsystem),
76 }; 83 };
77 84
78 RetranslateUI(); 85 player_tabs = {
79 LoadConfiguration(); 86 ui->tabPlayer1, ui->tabPlayer2, ui->tabPlayer3, ui->tabPlayer4,
80 UpdateUIEnabled(); 87 ui->tabPlayer5, ui->tabPlayer6, ui->tabPlayer7, ui->tabPlayer8,
88 };
81 89
82 connect(ui->restore_defaults_button, &QPushButton::clicked, this, 90 player_connected = {
83 &ConfigureInput::RestoreDefaults); 91 ui->checkboxPlayer1Connected, ui->checkboxPlayer2Connected, ui->checkboxPlayer3Connected,
92 ui->checkboxPlayer4Connected, ui->checkboxPlayer5Connected, ui->checkboxPlayer6Connected,
93 ui->checkboxPlayer7Connected, ui->checkboxPlayer8Connected,
94 };
84 95
85 for (auto* enabled : players_controller) { 96 for (std::size_t i = 0; i < player_tabs.size(); ++i) {
86 connect(enabled, QOverload<int>::of(&QComboBox::currentIndexChanged), this, 97 player_tabs[i]->setLayout(new QHBoxLayout(player_tabs[i]));
87 &ConfigureInput::UpdateUIEnabled); 98 player_tabs[i]->layout()->addWidget(player_controllers[i]);
88 } 99 connect(player_controllers[i], &ConfigureInputPlayer::Connected, [&, i](bool is_connected) {
89 connect(ui->use_docked_mode, &QCheckBox::stateChanged, this, &ConfigureInput::UpdateUIEnabled); 100 if (is_connected) {
90 connect(ui->handheld_connected, &QCheckBox::stateChanged, this, 101 for (std::size_t index = 0; index <= i; ++index) {
91 &ConfigureInput::UpdateUIEnabled); 102 player_connected[index]->setChecked(is_connected);
92 connect(ui->mouse_enabled, &QCheckBox::stateChanged, this, &ConfigureInput::UpdateUIEnabled); 103 }
93 connect(ui->keyboard_enabled, &QCheckBox::stateChanged, this, &ConfigureInput::UpdateUIEnabled); 104 } else {
94 connect(ui->debug_enabled, &QCheckBox::stateChanged, this, &ConfigureInput::UpdateUIEnabled); 105 for (std::size_t index = i; index < player_tabs.size(); ++index) {
95 connect(ui->touchscreen_enabled, &QCheckBox::stateChanged, this, 106 player_connected[index]->setChecked(is_connected);
96 &ConfigureInput::UpdateUIEnabled); 107 }
97 108 }
98 for (std::size_t i = 0; i < players_configure.size(); ++i) { 109 });
99 connect(players_configure[i], &QPushButton::clicked, this, 110 connect(player_controllers[i], &ConfigureInputPlayer::RefreshInputDevices,
100 [this, i] { CallConfigureDialog<ConfigureInputPlayer>(*this, i, false); }); 111 [this] { UpdateAllInputDevices(); });
112 connect(player_connected[i], &QCheckBox::stateChanged, [this, i](int state) {
113 player_controllers[i]->ConnectPlayer(state == Qt::Checked);
114 });
101 } 115 }
116 // Only the first player can choose handheld mode so connect the signal just to player 1
117 connect(player_controllers[0], &ConfigureInputPlayer::HandheldStateChanged,
118 [this](bool is_handheld) { UpdateDockedState(is_handheld); });
119
120 advanced = new ConfigureInputAdvanced(this);
121 ui->tabAdvanced->setLayout(new QHBoxLayout(ui->tabAdvanced));
122 ui->tabAdvanced->layout()->addWidget(advanced);
123 connect(advanced, &ConfigureInputAdvanced::CallDebugControllerDialog, [this, input_subsystem] {
124 CallConfigureDialog<ConfigureDebugController>(*this, input_subsystem);
125 });
126 connect(advanced, &ConfigureInputAdvanced::CallMouseConfigDialog, [this, input_subsystem] {
127 CallConfigureDialog<ConfigureMouseAdvanced>(*this, input_subsystem);
128 });
129 connect(advanced, &ConfigureInputAdvanced::CallTouchscreenConfigDialog,
130 [this] { CallConfigureDialog<ConfigureTouchscreenAdvanced>(*this); });
131 connect(advanced, &ConfigureInputAdvanced::CallMotionTouchConfigDialog,
132 [this, input_subsystem] {
133 CallConfigureDialog<ConfigureMotionTouch>(*this, input_subsystem);
134 });
102 135
103 connect(ui->handheld_configure, &QPushButton::clicked, this, 136 connect(ui->buttonClearAll, &QPushButton::clicked, [this] { ClearAll(); });
104 [this] { CallConfigureDialog<ConfigureInputPlayer>(*this, 8, false); }); 137 connect(ui->buttonRestoreDefaults, &QPushButton::clicked, [this] { RestoreDefaults(); });
105
106 connect(ui->debug_configure, &QPushButton::clicked, this,
107 [this] { CallConfigureDialog<ConfigureInputPlayer>(*this, 9, true); });
108
109 connect(ui->mouse_advanced, &QPushButton::clicked, this,
110 [this] { CallConfigureDialog<ConfigureMouseAdvanced>(*this); });
111 138
112 connect(ui->touchscreen_advanced, &QPushButton::clicked, this, 139 RetranslateUI();
113 [this] { CallConfigureDialog<ConfigureTouchscreenAdvanced>(*this); }); 140 LoadConfiguration();
114} 141}
115 142
116ConfigureInput::~ConfigureInput() = default; 143QList<QWidget*> ConfigureInput::GetSubTabs() const {
144 return {
145 ui->tabPlayer1, ui->tabPlayer2, ui->tabPlayer3, ui->tabPlayer4, ui->tabPlayer5,
146 ui->tabPlayer6, ui->tabPlayer7, ui->tabPlayer8, ui->tabAdvanced,
147 };
148}
117 149
118void ConfigureInput::ApplyConfiguration() { 150void ConfigureInput::ApplyConfiguration() {
119 for (std::size_t i = 0; i < players_controller.size(); ++i) { 151 for (auto controller : player_controllers) {
120 const auto controller_type_index = players_controller[i]->currentIndex(); 152 controller->ApplyConfiguration();
121
122 Settings::values.players[i].connected = controller_type_index != 0;
123
124 if (controller_type_index > 0) {
125 Settings::values.players[i].type =
126 static_cast<Settings::ControllerType>(controller_type_index - 1);
127 } else {
128 Settings::values.players[i].type = Settings::ControllerType::DualJoycon;
129 }
130 } 153 }
131 154
155 advanced->ApplyConfiguration();
156
132 const bool pre_docked_mode = Settings::values.use_docked_mode; 157 const bool pre_docked_mode = Settings::values.use_docked_mode;
133 Settings::values.use_docked_mode = ui->use_docked_mode->isChecked(); 158 Settings::values.use_docked_mode = ui->radioDocked->isChecked();
134 OnDockedModeChanged(pre_docked_mode, Settings::values.use_docked_mode); 159 OnDockedModeChanged(pre_docked_mode, Settings::values.use_docked_mode);
135 Settings::values 160
136 .players[Service::HID::Controller_NPad::NPadIdToIndex(Service::HID::NPAD_HANDHELD)] 161 Settings::values.vibration_enabled = ui->vibrationGroup->isChecked();
137 .connected = ui->handheld_connected->isChecked();
138 Settings::values.debug_pad_enabled = ui->debug_enabled->isChecked();
139 Settings::values.mouse_enabled = ui->mouse_enabled->isChecked();
140 Settings::values.keyboard_enabled = ui->keyboard_enabled->isChecked();
141 Settings::values.touchscreen.enabled = ui->touchscreen_enabled->isChecked();
142} 162}
143 163
144void ConfigureInput::changeEvent(QEvent* event) { 164void ConfigureInput::changeEvent(QEvent* event) {
@@ -146,94 +166,63 @@ void ConfigureInput::changeEvent(QEvent* event) {
146 RetranslateUI(); 166 RetranslateUI();
147 } 167 }
148 168
149 QDialog::changeEvent(event); 169 QWidget::changeEvent(event);
150} 170}
151 171
152void ConfigureInput::RetranslateUI() { 172void ConfigureInput::RetranslateUI() {
153 ui->retranslateUi(this); 173 ui->retranslateUi(this);
154 RetranslateControllerComboBoxes();
155} 174}
156 175
157void ConfigureInput::RetranslateControllerComboBoxes() { 176void ConfigureInput::LoadConfiguration() {
158 for (auto* controller_box : players_controller) {
159 [[maybe_unused]] const QSignalBlocker blocker(controller_box);
160
161 controller_box->clear();
162 controller_box->addItems({tr("None"), tr("Pro Controller"), tr("Dual Joycons"),
163 tr("Single Right Joycon"), tr("Single Left Joycon")});
164 }
165
166 LoadPlayerControllerIndices(); 177 LoadPlayerControllerIndices();
167} 178 UpdateDockedState(Settings::values.players[0].controller_type ==
179 Settings::ControllerType::Handheld);
168 180
169void ConfigureInput::UpdateUIEnabled() { 181 ui->vibrationGroup->setChecked(Settings::values.vibration_enabled);
170 bool hit_disabled = false; 182}
171 for (auto* player : players_controller) {
172 player->setDisabled(hit_disabled);
173 if (hit_disabled) {
174 player->setCurrentIndex(0);
175 }
176 if (!hit_disabled && player->currentIndex() == 0) {
177 hit_disabled = true;
178 }
179 }
180 183
181 for (std::size_t i = 0; i < players_controller.size(); ++i) { 184void ConfigureInput::LoadPlayerControllerIndices() {
182 players_configure[i]->setEnabled(players_controller[i]->currentIndex() != 0); 185 for (std::size_t i = 0; i < player_connected.size(); ++i) {
186 const auto connected = Settings::values.players[i].connected ||
187 (i == 0 && Settings::values.players[8].connected);
188 player_connected[i]->setChecked(connected);
183 } 189 }
190}
184 191
185 ui->handheld_connected->setChecked(ui->handheld_connected->isChecked() && 192void ConfigureInput::ClearAll() {
186 !ui->use_docked_mode->isChecked()); 193 // We don't have a good way to know what tab is active, but we can find out by getting the
187 ui->handheld_connected->setEnabled(!ui->use_docked_mode->isChecked()); 194 // parent of the consoleInputSettings
188 ui->handheld_configure->setEnabled(ui->handheld_connected->isChecked() && 195 auto* player_tab = static_cast<ConfigureInputPlayer*>(ui->consoleInputSettings->parent());
189 !ui->use_docked_mode->isChecked()); 196 player_tab->ClearAll();
190 ui->mouse_advanced->setEnabled(ui->mouse_enabled->isChecked());
191 ui->debug_configure->setEnabled(ui->debug_enabled->isChecked());
192 ui->touchscreen_advanced->setEnabled(ui->touchscreen_enabled->isChecked());
193} 197}
194 198
195void ConfigureInput::LoadConfiguration() { 199void ConfigureInput::RestoreDefaults() {
196 std::stable_partition( 200 // We don't have a good way to know what tab is active, but we can find out by getting the
197 Settings::values.players.begin(), 201 // parent of the consoleInputSettings
198 Settings::values.players.begin() + 202 auto* player_tab = static_cast<ConfigureInputPlayer*>(ui->consoleInputSettings->parent());
199 Service::HID::Controller_NPad::NPadIdToIndex(Service::HID::NPAD_HANDHELD), 203 player_tab->RestoreDefaults();
200 [](const auto& player) { return player.connected; }); 204
205 ui->radioDocked->setChecked(true);
206 ui->radioUndocked->setChecked(false);
207 ui->vibrationGroup->setChecked(true);
208}
201 209
202 LoadPlayerControllerIndices(); 210void ConfigureInput::UpdateDockedState(bool is_handheld) {
211 // If the controller type is handheld only, disallow changing docked mode
212 ui->radioDocked->setEnabled(!is_handheld);
213 ui->radioUndocked->setEnabled(!is_handheld);
203 214
204 ui->use_docked_mode->setChecked(Settings::values.use_docked_mode); 215 ui->radioDocked->setChecked(Settings::values.use_docked_mode);
205 ui->handheld_connected->setChecked( 216 ui->radioUndocked->setChecked(!Settings::values.use_docked_mode);
206 Settings::values
207 .players[Service::HID::Controller_NPad::NPadIdToIndex(Service::HID::NPAD_HANDHELD)]
208 .connected);
209 ui->debug_enabled->setChecked(Settings::values.debug_pad_enabled);
210 ui->mouse_enabled->setChecked(Settings::values.mouse_enabled);
211 ui->keyboard_enabled->setChecked(Settings::values.keyboard_enabled);
212 ui->touchscreen_enabled->setChecked(Settings::values.touchscreen.enabled);
213
214 UpdateUIEnabled();
215}
216 217
217void ConfigureInput::LoadPlayerControllerIndices() { 218 // If its handheld only, force docked mode off (since you can't play handheld in a dock)
218 for (std::size_t i = 0; i < players_controller.size(); ++i) { 219 if (is_handheld) {
219 const auto connected = Settings::values.players[i].connected; 220 ui->radioUndocked->setChecked(true);
220 players_controller[i]->setCurrentIndex(
221 connected ? static_cast<u8>(Settings::values.players[i].type) + 1 : 0);
222 } 221 }
223} 222}
224 223
225void ConfigureInput::RestoreDefaults() { 224void ConfigureInput::UpdateAllInputDevices() {
226 players_controller[0]->setCurrentIndex(2); 225 for (const auto& player : player_controllers) {
227 226 player->UpdateInputDevices();
228 for (std::size_t i = 1; i < players_controller.size(); ++i) {
229 players_controller[i]->setCurrentIndex(0);
230 } 227 }
231
232 ui->use_docked_mode->setCheckState(Qt::Unchecked);
233 ui->handheld_connected->setCheckState(Qt::Unchecked);
234 ui->mouse_enabled->setCheckState(Qt::Unchecked);
235 ui->keyboard_enabled->setCheckState(Qt::Unchecked);
236 ui->debug_enabled->setCheckState(Qt::Unchecked);
237 ui->touchscreen_enabled->setCheckState(Qt::Checked);
238 UpdateUIEnabled();
239} 228}
diff --git a/src/yuzu/configuration/configure_input.h b/src/yuzu/configuration/configure_input.h
index 2f70cb3ca..d08a24f96 100644
--- a/src/yuzu/configuration/configure_input.h
+++ b/src/yuzu/configuration/configure_input.h
@@ -7,37 +7,50 @@
7#include <array> 7#include <array>
8#include <memory> 8#include <memory>
9 9
10#include <QDialog>
11#include <QKeyEvent> 10#include <QKeyEvent>
11#include <QWidget>
12
13#include "yuzu/configuration/configure_input_advanced.h"
14#include "yuzu/configuration/configure_input_player.h"
12 15
13#include "ui_configure_input.h" 16#include "ui_configure_input.h"
14 17
15class QPushButton; 18class QCheckBox;
16class QString; 19class QString;
17class QTimer; 20class QTimer;
18 21
22namespace InputCommon {
23class InputSubsystem;
24}
25
19namespace Ui { 26namespace Ui {
20class ConfigureInput; 27class ConfigureInput;
21} 28}
22 29
23void OnDockedModeChanged(bool last_state, bool new_state); 30void OnDockedModeChanged(bool last_state, bool new_state);
24 31
25class ConfigureInput : public QDialog { 32class ConfigureInput : public QWidget {
26 Q_OBJECT 33 Q_OBJECT
27 34
28public: 35public:
29 explicit ConfigureInput(QWidget* parent = nullptr); 36 explicit ConfigureInput(QWidget* parent = nullptr);
30 ~ConfigureInput() override; 37 ~ConfigureInput() override;
31 38
32 /// Save all button configurations to settings file 39 /// Initializes the input dialog with the given input subsystem.
40 void Initialize(InputCommon::InputSubsystem* input_subsystem_);
41
42 /// Save all button configurations to settings file.
33 void ApplyConfiguration(); 43 void ApplyConfiguration();
34 44
45 QList<QWidget*> GetSubTabs() const;
46
35private: 47private:
36 void changeEvent(QEvent* event) override; 48 void changeEvent(QEvent* event) override;
37 void RetranslateUI(); 49 void RetranslateUI();
38 void RetranslateControllerComboBoxes(); 50 void ClearAll();
39 51
40 void UpdateUIEnabled(); 52 void UpdateDockedState(bool is_handheld);
53 void UpdateAllInputDevices();
41 54
42 /// Load configuration settings. 55 /// Load configuration settings.
43 void LoadConfiguration(); 56 void LoadConfiguration();
@@ -48,6 +61,8 @@ private:
48 61
49 std::unique_ptr<Ui::ConfigureInput> ui; 62 std::unique_ptr<Ui::ConfigureInput> ui;
50 63
51 std::array<QComboBox*, 8> players_controller; 64 std::array<ConfigureInputPlayer*, 8> player_controllers;
52 std::array<QPushButton*, 8> players_configure; 65 std::array<QWidget*, 8> player_tabs;
66 std::array<QCheckBox*, 8> player_connected;
67 ConfigureInputAdvanced* advanced;
53}; 68};
diff --git a/src/yuzu/configuration/configure_input.ui b/src/yuzu/configuration/configure_input.ui
index efffd8487..136955224 100644
--- a/src/yuzu/configuration/configure_input.ui
+++ b/src/yuzu/configuration/configure_input.ui
@@ -1,529 +1,554 @@
1<?xml version="1.0" encoding="UTF-8"?> 1<?xml version="1.0" encoding="UTF-8"?>
2<ui version="4.0"> 2<ui version="4.0">
3 <class>ConfigureInput</class> 3 <class>ConfigureInput</class>
4 <widget class="QDialog" name="ConfigureInput"> 4 <widget class="QWidget" name="ConfigureInput">
5 <property name="geometry"> 5 <property name="geometry">
6 <rect> 6 <rect>
7 <x>0</x> 7 <x>0</x>
8 <y>0</y> 8 <y>0</y>
9 <width>384</width> 9 <width>700</width>
10 <height>576</height> 10 <height>540</height>
11 </rect> 11 </rect>
12 </property> 12 </property>
13 <property name="windowTitle"> 13 <property name="windowTitle">
14 <string>Custom Input Settings</string> 14 <string>ConfigureInput</string>
15 </property> 15 </property>
16 <layout class="QVBoxLayout" name="verticalLayout_5"> 16 <layout class="QVBoxLayout" name="verticalLayout_5">
17 <property name="spacing">
18 <number>2</number>
19 </property>
20 <property name="leftMargin">
21 <number>3</number>
22 </property>
23 <property name="topMargin">
24 <number>3</number>
25 </property>
26 <property name="rightMargin">
27 <number>3</number>
28 </property>
29 <property name="bottomMargin">
30 <number>3</number>
31 </property>
17 <item> 32 <item>
18 <layout class="QVBoxLayout" name="verticalLayout"> 33 <widget class="QTabWidget" name="tabWidget">
19 <item> 34 <property name="currentIndex">
20 <widget class="QGroupBox" name="gridGroupBox_1"> 35 <number>0</number>
21 <property name="title"> 36 </property>
22 <string>Players</string> 37 <widget class="QWidget" name="tabPlayer1">
23 </property> 38 <property name="accessibleName">
24 <layout class="QGridLayout" name="gridLayout"> 39 <string>Player 1</string>
25 <item row="1" column="2"> 40 </property>
26 <widget class="QComboBox" name="player1_combobox"> 41 <attribute name="title">
27 <property name="minimumSize"> 42 <string>Player 1</string>
28 <size> 43 </attribute>
29 <width>110</width> 44 </widget>
30 <height>0</height> 45 <widget class="QWidget" name="tabPlayer2">
31 </size> 46 <property name="accessibleName">
32 </property> 47 <string>Player 2</string>
33 </widget> 48 </property>
34 </item> 49 <attribute name="title">
35 <item row="1" column="3"> 50 <string>Player 2</string>
36 <widget class="QPushButton" name="player1_configure"> 51 </attribute>
37 <property name="text"> 52 </widget>
38 <string>Configure</string> 53 <widget class="QWidget" name="tabPlayer3">
39 </property> 54 <property name="accessibleName">
40 </widget> 55 <string>Player 3</string>
41 </item> 56 </property>
42 <item row="0" column="2"> 57 <attribute name="title">
43 <widget class="QLabel" name="label"> 58 <string>Player 3</string>
44 <property name="text"> 59 </attribute>
45 <string>Controller Type</string> 60 </widget>
46 </property> 61 <widget class="QWidget" name="tabPlayer4">
47 <property name="alignment"> 62 <property name="accessibleName">
48 <set>Qt::AlignCenter</set> 63 <string>Player 4</string>
49 </property> 64 </property>
50 </widget> 65 <attribute name="title">
51 </item> 66 <string>Player 4</string>
52 <item row="2" column="2"> 67 </attribute>
53 <widget class="QComboBox" name="player2_combobox"> 68 </widget>
54 <property name="minimumSize"> 69 <widget class="QWidget" name="tabPlayer5">
55 <size> 70 <property name="accessibleName">
56 <width>110</width> 71 <string>Player 5</string>
57 <height>0</height> 72 </property>
58 </size> 73 <attribute name="title">
59 </property> 74 <string>Player 5</string>
60 </widget> 75 </attribute>
61 </item> 76 </widget>
62 <item row="3" column="2"> 77 <widget class="QWidget" name="tabPlayer6">
63 <widget class="QComboBox" name="player3_combobox"> 78 <property name="accessibleName">
64 <property name="minimumSize"> 79 <string>Player 6</string>
65 <size> 80 </property>
66 <width>110</width> 81 <attribute name="title">
67 <height>0</height> 82 <string>Player 6</string>
68 </size> 83 </attribute>
69 </property> 84 </widget>
70 </widget> 85 <widget class="QWidget" name="tabPlayer7">
71 </item> 86 <property name="accessibleName">
72 <item row="4" column="2"> 87 <string>Player 7</string>
73 <widget class="QComboBox" name="player4_combobox"> 88 </property>
74 <property name="minimumSize"> 89 <attribute name="title">
75 <size> 90 <string>Player 7</string>
76 <width>110</width> 91 </attribute>
77 <height>0</height> 92 </widget>
78 </size> 93 <widget class="QWidget" name="tabPlayer8">
79 </property> 94 <property name="accessibleName">
80 </widget> 95 <string>Player 8</string>
81 </item> 96 </property>
82 <item row="5" column="2"> 97 <attribute name="title">
83 <widget class="QComboBox" name="player5_combobox"> 98 <string>Player 8</string>
84 <property name="minimumSize"> 99 </attribute>
85 <size> 100 </widget>
86 <width>110</width> 101 <widget class="QWidget" name="tabAdvanced">
87 <height>0</height> 102 <property name="accessibleName">
88 </size> 103 <string>Advanced</string>
89 </property> 104 </property>
90 </widget> 105 <attribute name="title">
91 </item> 106 <string>Advanced</string>
92 <item row="6" column="2"> 107 </attribute>
93 <widget class="QComboBox" name="player6_combobox"> 108 </widget>
94 <property name="minimumSize"> 109 </widget>
95 <size> 110 </item>
96 <width>110</width> 111 <item alignment="Qt::AlignVCenter">
97 <height>0</height> 112 <widget class="QWidget" name="consoleInputSettings" native="true">
98 </size> 113 <layout class="QHBoxLayout" name="buttonsBottomRightHorizontalLayout">
99 </property> 114 <property name="spacing">
100 </widget> 115 <number>3</number>
101 </item> 116 </property>
102 <item row="7" column="2"> 117 <property name="leftMargin">
103 <widget class="QComboBox" name="player7_combobox"> 118 <number>0</number>
104 <property name="minimumSize"> 119 </property>
105 <size> 120 <property name="topMargin">
106 <width>110</width> 121 <number>3</number>
107 <height>0</height> 122 </property>
108 </size> 123 <property name="rightMargin">
109 </property> 124 <number>0</number>
110 </widget> 125 </property>
111 </item> 126 <property name="bottomMargin">
112 <item row="8" column="2"> 127 <number>0</number>
113 <widget class="QComboBox" name="player8_combobox"> 128 </property>
114 <property name="minimumSize"> 129 <item alignment="Qt::AlignVCenter">
115 <size> 130 <widget class="QGroupBox" name="handheldGroup">
116 <width>110</width> 131 <property name="maximumSize">
117 <height>0</height> 132 <size>
118 </size> 133 <width>16777215</width>
119 </property> 134 <height>16777215</height>
120 </widget> 135 </size>
121 </item> 136 </property>
122 <item row="2" column="3"> 137 <property name="title">
123 <widget class="QPushButton" name="player2_configure"> 138 <string>Console Mode</string>
124 <property name="text"> 139 </property>
125 <string>Configure</string> 140 <layout class="QHBoxLayout" name="horizontalLayout">
126 </property> 141 <property name="spacing">
127 </widget> 142 <number>6</number>
128 </item> 143 </property>
129 <item row="3" column="3"> 144 <property name="leftMargin">
130 <widget class="QPushButton" name="player3_configure"> 145 <number>3</number>
131 <property name="text"> 146 </property>
132 <string>Configure</string> 147 <property name="topMargin">
133 </property> 148 <number>6</number>
134 </widget> 149 </property>
135 </item> 150 <property name="rightMargin">
136 <item row="4" column="3"> 151 <number>3</number>
137 <widget class="QPushButton" name="player4_configure"> 152 </property>
138 <property name="text"> 153 <property name="bottomMargin">
139 <string>Configure</string> 154 <number>6</number>
140 </property> 155 </property>
141 </widget> 156 <item>
142 </item> 157 <widget class="QRadioButton" name="radioDocked">
143 <item row="5" column="3"> 158 <property name="text">
144 <widget class="QPushButton" name="player5_configure"> 159 <string>Docked</string>
145 <property name="text"> 160 </property>
146 <string>Configure</string> 161 <property name="checked">
147 </property> 162 <bool>true</bool>
148 </widget> 163 </property>
149 </item> 164 </widget>
150 <item row="6" column="3"> 165 </item>
151 <widget class="QPushButton" name="player6_configure"> 166 <item>
152 <property name="text"> 167 <widget class="QRadioButton" name="radioUndocked">
153 <string>Configure</string> 168 <property name="text">
154 </property> 169 <string>Undocked</string>
155 </widget> 170 </property>
156 </item> 171 </widget>
157 <item row="7" column="3"> 172 </item>
158 <widget class="QPushButton" name="player7_configure"> 173 </layout>
159 <property name="text"> 174 </widget>
160 <string>Configure</string> 175 </item>
161 </property> 176 <item>
162 </widget> 177 <widget class="QGroupBox" name="vibrationGroup">
163 </item> 178 <property name="title">
164 <item row="8" column="3"> 179 <string>Vibration</string>
165 <widget class="QPushButton" name="player8_configure"> 180 </property>
166 <property name="text"> 181 <property name="checkable">
167 <string>Configure</string> 182 <bool>true</bool>
168 </property> 183 </property>
169 </widget> 184 <layout class="QHBoxLayout" name="horizontalLayout_2">
170 </item> 185 <property name="leftMargin">
171 <item row="0" column="0"> 186 <number>3</number>
172 <spacer name="horizontalSpacer"> 187 </property>
173 <property name="orientation"> 188 <property name="topMargin">
174 <enum>Qt::Horizontal</enum> 189 <number>3</number>
175 </property> 190 </property>
176 <property name="sizeHint" stdset="0"> 191 <property name="rightMargin">
177 <size> 192 <number>3</number>
178 <width>40</width> 193 </property>
179 <height>20</height> 194 <property name="bottomMargin">
180 </size> 195 <number>3</number>
181 </property> 196 </property>
182 </spacer> 197 <item>
183 </item> 198 <widget class="QSpinBox" name="vibrationSpin">
184 <item row="0" column="4"> 199 <property name="minimumSize">
185 <spacer name="horizontalSpacer_2"> 200 <size>
186 <property name="orientation"> 201 <width>65</width>
187 <enum>Qt::Horizontal</enum> 202 <height>21</height>
188 </property> 203 </size>
189 <property name="sizeHint" stdset="0"> 204 </property>
190 <size> 205 <property name="maximumSize">
191 <width>40</width> 206 <size>
192 <height>20</height> 207 <width>65</width>
193 </size> 208 <height>16777215</height>
194 </property> 209 </size>
195 </spacer> 210 </property>
196 </item> 211 <property name="suffix">
197 <item row="1" column="1"> 212 <string>%</string>
198 <widget class="QLabel" name="label_3"> 213 </property>
199 <property name="minimumSize"> 214 <property name="minimum">
200 <size> 215 <number>1</number>
201 <width>55</width> 216 </property>
202 <height>0</height> 217 <property name="maximum">
203 </size> 218 <number>200</number>
204 </property> 219 </property>
205 <property name="text"> 220 <property name="value">
206 <string>Player 1</string> 221 <number>100</number>
207 </property> 222 </property>
208 </widget> 223 </widget>
209 </item> 224 </item>
210 <item row="2" column="1"> 225 </layout>
211 <widget class="QLabel" name="label_4"> 226 </widget>
212 <property name="text"> 227 </item>
213 <string>Player 2</string> 228 <item>
214 </property> 229 <widget class="QGroupBox" name="motionGroup">
215 </widget> 230 <property name="title">
216 </item> 231 <string>Motion</string>
217 <item row="3" column="1"> 232 </property>
218 <widget class="QLabel" name="label_5"> 233 <property name="checkable">
219 <property name="text"> 234 <bool>true</bool>
220 <string>Player 3</string> 235 </property>
221 </property> 236 <layout class="QHBoxLayout" name="horizontalLayout_3">
222 </widget> 237 <property name="leftMargin">
223 </item> 238 <number>3</number>
224 <item row="4" column="1"> 239 </property>
225 <widget class="QLabel" name="label_6"> 240 <property name="topMargin">
226 <property name="text"> 241 <number>3</number>
227 <string>Player 4</string> 242 </property>
228 </property> 243 <property name="rightMargin">
229 </widget> 244 <number>3</number>
230 </item> 245 </property>
231 <item row="5" column="1"> 246 <property name="bottomMargin">
232 <widget class="QLabel" name="label_7"> 247 <number>3</number>
233 <property name="text"> 248 </property>
234 <string>Player 5</string> 249 <item>
235 </property> 250 <widget class="QPushButton" name="motionButton">
236 </widget> 251 <property name="minimumSize">
237 </item> 252 <size>
238 <item row="6" column="1"> 253 <width>57</width>
239 <widget class="QLabel" name="label_8"> 254 <height>0</height>
240 <property name="text"> 255 </size>
241 <string>Player 6</string> 256 </property>
242 </property> 257 <property name="maximumSize">
243 </widget> 258 <size>
244 </item> 259 <width>55</width>
245 <item row="7" column="1"> 260 <height>16777215</height>
246 <widget class="QLabel" name="label_9"> 261 </size>
247 <property name="text"> 262 </property>
248 <string>Player 7</string> 263 <property name="styleSheet">
249 </property> 264 <string notr="true">min-width: 55px;</string>
250 </widget> 265 </property>
251 </item> 266 <property name="text">
252 <item row="8" column="1"> 267 <string>Configure</string>
253 <widget class="QLabel" name="label_10"> 268 </property>
254 <property name="text"> 269 </widget>
255 <string>Player 8</string> 270 </item>
256 </property> 271 </layout>
257 </widget> 272 </widget>
258 </item> 273 </item>
259 </layout> 274 <item alignment="Qt::AlignVCenter">
260 </widget> 275 <widget class="QWidget" name="widget" native="true">
261 </item> 276 <layout class="QGridLayout" name="gridLayout_2">
262 <item> 277 <property name="leftMargin">
263 <widget class="QGroupBox" name="gridGroupBox_2"> 278 <number>5</number>
264 <property name="title"> 279 </property>
265 <string>Handheld</string> 280 <property name="topMargin">
266 </property> 281 <number>0</number>
267 <layout class="QGridLayout" name="gridLayout_2">
268 <item row="1" column="2">
269 <spacer name="horizontalSpacer_5">
270 <property name="orientation">
271 <enum>Qt::Horizontal</enum>
272 </property>
273 <property name="sizeType">
274 <enum>QSizePolicy::Fixed</enum>
275 </property>
276 <property name="sizeHint" stdset="0">
277 <size>
278 <width>72</width>
279 <height>20</height>
280 </size>
281 </property>
282 </spacer>
283 </item>
284 <item row="1" column="4">
285 <spacer name="horizontalSpacer_4">
286 <property name="orientation">
287 <enum>Qt::Horizontal</enum>
288 </property>
289 <property name="sizeHint" stdset="0">
290 <size>
291 <width>40</width>
292 <height>20</height>
293 </size>
294 </property>
295 </spacer>
296 </item>
297 <item row="1" column="3">
298 <widget class="QPushButton" name="handheld_configure">
299 <property name="text">
300 <string>Configure</string>
301 </property>
302 </widget>
303 </item>
304 <item row="1" column="0">
305 <spacer name="horizontalSpacer_3">
306 <property name="orientation">
307 <enum>Qt::Horizontal</enum>
308 </property>
309 <property name="sizeHint" stdset="0">
310 <size>
311 <width>40</width>
312 <height>20</height>
313 </size>
314 </property>
315 </spacer>
316 </item>
317 <item row="1" column="1">
318 <widget class="QCheckBox" name="handheld_connected">
319 <property name="text">
320 <string>Joycons Docked</string>
321 </property>
322 </widget>
323 </item>
324 <item row="0" column="1">
325 <widget class="QCheckBox" name="use_docked_mode">
326 <property name="text">
327 <string>Use Docked Mode</string>
328 </property>
329 </widget>
330 </item>
331 </layout>
332 </widget>
333 </item>
334 <item>
335 <widget class="QGroupBox" name="gridGroupBox_3">
336 <property name="title">
337 <string>Other</string>
338 </property>
339 <layout class="QGridLayout" name="gridLayout_3">
340 <item row="1" column="1">
341 <widget class="QCheckBox" name="keyboard_enabled">
342 <property name="minimumSize">
343 <size>
344 <width>0</width>
345 <height>23</height>
346 </size>
347 </property>
348 <property name="text">
349 <string>Keyboard</string>
350 </property>
351 </widget>
352 </item>
353 <item row="2" column="1">
354 <widget class="QCheckBox" name="debug_enabled">
355 <property name="text">
356 <string>Debug Controller</string>
357 </property>
358 </widget>
359 </item>
360 <item row="3" column="1">
361 <widget class="QCheckBox" name="touchscreen_enabled">
362 <property name="text">
363 <string>Touchscreen</string>
364 </property>
365 </widget>
366 </item>
367 <item row="0" column="1">
368 <widget class="QCheckBox" name="mouse_enabled">
369 <property name="minimumSize">
370 <size>
371 <width>0</width>
372 <height>23</height>
373 </size>
374 </property>
375 <property name="text">
376 <string>Mouse</string>
377 </property>
378 </widget>
379 </item>
380 <item row="0" column="4">
381 <spacer name="horizontalSpacer_7">
382 <property name="orientation">
383 <enum>Qt::Horizontal</enum>
384 </property>
385 <property name="sizeHint" stdset="0">
386 <size>
387 <width>40</width>
388 <height>20</height>
389 </size>
390 </property>
391 </spacer>
392 </item>
393 <item row="0" column="2">
394 <spacer name="horizontalSpacer_8">
395 <property name="orientation">
396 <enum>Qt::Horizontal</enum>
397 </property>
398 <property name="sizeType">
399 <enum>QSizePolicy::Fixed</enum>
400 </property>
401 <property name="sizeHint" stdset="0">
402 <size>
403 <width>76</width>
404 <height>20</height>
405 </size>
406 </property>
407 </spacer>
408 </item>
409 <item row="0" column="0">
410 <spacer name="horizontalSpacer_6">
411 <property name="orientation">
412 <enum>Qt::Horizontal</enum>
413 </property>
414 <property name="sizeHint" stdset="0">
415 <size>
416 <width>40</width>
417 <height>20</height>
418 </size>
419 </property>
420 </spacer>
421 </item>
422 <item row="3" column="3">
423 <widget class="QPushButton" name="touchscreen_advanced">
424 <property name="text">
425 <string>Advanced</string>
426 </property>
427 </widget>
428 </item>
429 <item row="2" column="3">
430 <widget class="QPushButton" name="debug_configure">
431 <property name="text">
432 <string>Configure</string>
433 </property>
434 </widget>
435 </item>
436 <item row="0" column="3">
437 <widget class="QPushButton" name="mouse_advanced">
438 <property name="text">
439 <string>Advanced</string>
440 </property>
441 </widget>
442 </item>
443 </layout>
444 </widget>
445 </item>
446 <item>
447 <spacer name="verticalSpacer">
448 <property name="orientation">
449 <enum>Qt::Vertical</enum>
450 </property>
451 <property name="sizeHint" stdset="0">
452 <size>
453 <width>20</width>
454 <height>40</height>
455 </size>
456 </property>
457 </spacer>
458 </item>
459 <item>
460 <layout class="QHBoxLayout" name="horizontalLayout">
461 <item>
462 <widget class="QPushButton" name="restore_defaults_button">
463 <property name="text">
464 <string>Restore Defaults</string>
465 </property> 282 </property>
466 </widget> 283 <property name="rightMargin">
467 </item> 284 <number>0</number>
468 <item>
469 <spacer name="horizontalSpacer_9">
470 <property name="orientation">
471 <enum>Qt::Horizontal</enum>
472 </property> 285 </property>
473 <property name="sizeHint" stdset="0"> 286 <property name="bottomMargin">
474 <size> 287 <number>0</number>
475 <width>40</width>
476 <height>20</height>
477 </size>
478 </property> 288 </property>
479 </spacer> 289 <property name="spacing">
480 </item> 290 <number>3</number>
481 <item>
482 <widget class="QDialogButtonBox" name="buttonBox">
483 <property name="standardButtons">
484 <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
485 </property> 291 </property>
486 </widget> 292 <item row="1" column="2">
487 </item> 293 <widget class="QCheckBox" name="checkboxPlayer2Connected">
488 </layout> 294 <property name="text">
489 </item> 295 <string/>
490 </layout> 296 </property>
297 </widget>
298 </item>
299 <item row="1" column="0">
300 <widget class="QLabel" name="label_2">
301 <property name="text">
302 <string>Controllers</string>
303 </property>
304 </widget>
305 </item>
306 <item row="1" column="4">
307 <widget class="QCheckBox" name="checkboxPlayer4Connected">
308 <property name="text">
309 <string/>
310 </property>
311 </widget>
312 </item>
313 <item row="1" column="3">
314 <widget class="QCheckBox" name="checkboxPlayer3Connected">
315 <property name="text">
316 <string/>
317 </property>
318 </widget>
319 </item>
320 <item row="1" column="5">
321 <widget class="QCheckBox" name="checkboxPlayer5Connected">
322 <property name="text">
323 <string/>
324 </property>
325 </widget>
326 </item>
327 <item row="0" column="1">
328 <widget class="QLabel" name="label">
329 <property name="text">
330 <string>1</string>
331 </property>
332 <property name="alignment">
333 <set>Qt::AlignCenter</set>
334 </property>
335 </widget>
336 </item>
337 <item row="1" column="7">
338 <widget class="QCheckBox" name="checkboxPlayer7Connected">
339 <property name="text">
340 <string/>
341 </property>
342 </widget>
343 </item>
344 <item row="1" column="6">
345 <widget class="QCheckBox" name="checkboxPlayer6Connected">
346 <property name="text">
347 <string/>
348 </property>
349 </widget>
350 </item>
351 <item row="1" column="1">
352 <widget class="QCheckBox" name="checkboxPlayer1Connected">
353 <property name="layoutDirection">
354 <enum>Qt::LeftToRight</enum>
355 </property>
356 <property name="checked">
357 <bool>true</bool>
358 </property>
359 </widget>
360 </item>
361 <item row="1" column="8">
362 <widget class="QCheckBox" name="checkboxPlayer8Connected">
363 <property name="text">
364 <string/>
365 </property>
366 </widget>
367 </item>
368 <item row="0" column="2">
369 <widget class="QLabel" name="label_3">
370 <property name="text">
371 <string>2</string>
372 </property>
373 <property name="alignment">
374 <set>Qt::AlignCenter</set>
375 </property>
376 </widget>
377 </item>
378 <item row="0" column="3">
379 <widget class="QLabel" name="label_4">
380 <property name="text">
381 <string>3</string>
382 </property>
383 <property name="alignment">
384 <set>Qt::AlignCenter</set>
385 </property>
386 </widget>
387 </item>
388 <item row="0" column="4">
389 <widget class="QLabel" name="label_5">
390 <property name="text">
391 <string>4</string>
392 </property>
393 <property name="alignment">
394 <set>Qt::AlignCenter</set>
395 </property>
396 </widget>
397 </item>
398 <item row="0" column="5">
399 <widget class="QLabel" name="label_6">
400 <property name="text">
401 <string>5</string>
402 </property>
403 <property name="alignment">
404 <set>Qt::AlignCenter</set>
405 </property>
406 </widget>
407 </item>
408 <item row="0" column="6">
409 <widget class="QLabel" name="label_7">
410 <property name="text">
411 <string>6</string>
412 </property>
413 <property name="alignment">
414 <set>Qt::AlignCenter</set>
415 </property>
416 </widget>
417 </item>
418 <item row="0" column="7">
419 <widget class="QLabel" name="label_8">
420 <property name="text">
421 <string>7</string>
422 </property>
423 <property name="alignment">
424 <set>Qt::AlignCenter</set>
425 </property>
426 </widget>
427 </item>
428 <item row="0" column="8">
429 <widget class="QLabel" name="label_9">
430 <property name="text">
431 <string>8</string>
432 </property>
433 <property name="alignment">
434 <set>Qt::AlignCenter</set>
435 </property>
436 </widget>
437 </item>
438 <item row="0" column="0">
439 <widget class="QLabel" name="label_10">
440 <property name="text">
441 <string>Connected</string>
442 </property>
443 </widget>
444 </item>
445 </layout>
446 </widget>
447 </item>
448 <item>
449 <spacer name="horizontalSpacer">
450 <property name="orientation">
451 <enum>Qt::Horizontal</enum>
452 </property>
453 <property name="sizeHint" stdset="0">
454 <size>
455 <width>40</width>
456 <height>20</height>
457 </size>
458 </property>
459 </spacer>
460 </item>
461 <item alignment="Qt::AlignBottom">
462 <widget class="QPushButton" name="buttonRestoreDefaults">
463 <property name="sizePolicy">
464 <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
465 <horstretch>0</horstretch>
466 <verstretch>0</verstretch>
467 </sizepolicy>
468 </property>
469 <property name="minimumSize">
470 <size>
471 <width>57</width>
472 <height>0</height>
473 </size>
474 </property>
475 <property name="maximumSize">
476 <size>
477 <width>55</width>
478 <height>16777215</height>
479 </size>
480 </property>
481 <property name="sizeIncrement">
482 <size>
483 <width>0</width>
484 <height>0</height>
485 </size>
486 </property>
487 <property name="baseSize">
488 <size>
489 <width>0</width>
490 <height>0</height>
491 </size>
492 </property>
493 <property name="layoutDirection">
494 <enum>Qt::LeftToRight</enum>
495 </property>
496 <property name="styleSheet">
497 <string notr="true">min-width: 55px;</string>
498 </property>
499 <property name="text">
500 <string>Defaults</string>
501 </property>
502 </widget>
503 </item>
504 <item alignment="Qt::AlignBottom">
505 <widget class="QPushButton" name="buttonClearAll">
506 <property name="sizePolicy">
507 <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
508 <horstretch>0</horstretch>
509 <verstretch>0</verstretch>
510 </sizepolicy>
511 </property>
512 <property name="minimumSize">
513 <size>
514 <width>57</width>
515 <height>0</height>
516 </size>
517 </property>
518 <property name="maximumSize">
519 <size>
520 <width>55</width>
521 <height>16777215</height>
522 </size>
523 </property>
524 <property name="sizeIncrement">
525 <size>
526 <width>0</width>
527 <height>0</height>
528 </size>
529 </property>
530 <property name="baseSize">
531 <size>
532 <width>0</width>
533 <height>0</height>
534 </size>
535 </property>
536 <property name="layoutDirection">
537 <enum>Qt::LeftToRight</enum>
538 </property>
539 <property name="styleSheet">
540 <string notr="true">min-width: 55px;</string>
541 </property>
542 <property name="text">
543 <string>Clear</string>
544 </property>
545 </widget>
546 </item>
547 </layout>
548 </widget>
491 </item> 549 </item>
492 </layout> 550 </layout>
493 </widget> 551 </widget>
494 <resources/> 552 <resources/>
495 <connections> 553 <connections/>
496 <connection>
497 <sender>buttonBox</sender>
498 <signal>accepted()</signal>
499 <receiver>ConfigureInput</receiver>
500 <slot>accept()</slot>
501 <hints>
502 <hint type="sourcelabel">
503 <x>294</x>
504 <y>553</y>
505 </hint>
506 <hint type="destinationlabel">
507 <x>191</x>
508 <y>287</y>
509 </hint>
510 </hints>
511 </connection>
512 <connection>
513 <sender>buttonBox</sender>
514 <signal>rejected()</signal>
515 <receiver>ConfigureInput</receiver>
516 <slot>reject()</slot>
517 <hints>
518 <hint type="sourcelabel">
519 <x>294</x>
520 <y>553</y>
521 </hint>
522 <hint type="destinationlabel">
523 <x>191</x>
524 <y>287</y>
525 </hint>
526 </hints>
527 </connection>
528 </connections>
529</ui> 554</ui>
diff --git a/src/yuzu/configuration/configure_input_advanced.cpp b/src/yuzu/configuration/configure_input_advanced.cpp
new file mode 100644
index 000000000..81f9dc16c
--- /dev/null
+++ b/src/yuzu/configuration/configure_input_advanced.cpp
@@ -0,0 +1,171 @@
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 <QColorDialog>
6#include "core/core.h"
7#include "core/settings.h"
8#include "ui_configure_input_advanced.h"
9#include "yuzu/configuration/configure_input_advanced.h"
10
11ConfigureInputAdvanced::ConfigureInputAdvanced(QWidget* parent)
12 : QWidget(parent), ui(std::make_unique<Ui::ConfigureInputAdvanced>()) {
13 ui->setupUi(this);
14
15 controllers_color_buttons = {{
16 {
17 ui->player1_left_body_button,
18 ui->player1_left_buttons_button,
19 ui->player1_right_body_button,
20 ui->player1_right_buttons_button,
21 },
22 {
23 ui->player2_left_body_button,
24 ui->player2_left_buttons_button,
25 ui->player2_right_body_button,
26 ui->player2_right_buttons_button,
27 },
28 {
29 ui->player3_left_body_button,
30 ui->player3_left_buttons_button,
31 ui->player3_right_body_button,
32 ui->player3_right_buttons_button,
33 },
34 {
35 ui->player4_left_body_button,
36 ui->player4_left_buttons_button,
37 ui->player4_right_body_button,
38 ui->player4_right_buttons_button,
39 },
40 {
41 ui->player5_left_body_button,
42 ui->player5_left_buttons_button,
43 ui->player5_right_body_button,
44 ui->player5_right_buttons_button,
45 },
46 {
47 ui->player6_left_body_button,
48 ui->player6_left_buttons_button,
49 ui->player6_right_body_button,
50 ui->player6_right_buttons_button,
51 },
52 {
53 ui->player7_left_body_button,
54 ui->player7_left_buttons_button,
55 ui->player7_right_body_button,
56 ui->player7_right_buttons_button,
57 },
58 {
59 ui->player8_left_body_button,
60 ui->player8_left_buttons_button,
61 ui->player8_right_body_button,
62 ui->player8_right_buttons_button,
63 },
64 }};
65
66 for (std::size_t player_idx = 0; player_idx < controllers_color_buttons.size(); ++player_idx) {
67 auto& color_buttons = controllers_color_buttons[player_idx];
68 for (std::size_t button_idx = 0; button_idx < color_buttons.size(); ++button_idx) {
69 connect(color_buttons[button_idx], &QPushButton::clicked, this,
70 [this, player_idx, button_idx] {
71 OnControllerButtonClick(static_cast<int>(player_idx),
72 static_cast<int>(button_idx));
73 });
74 }
75 }
76
77 connect(ui->mouse_enabled, &QCheckBox::stateChanged, this,
78 &ConfigureInputAdvanced::UpdateUIEnabled);
79 connect(ui->debug_enabled, &QCheckBox::stateChanged, this,
80 &ConfigureInputAdvanced::UpdateUIEnabled);
81 connect(ui->touchscreen_enabled, &QCheckBox::stateChanged, this,
82 &ConfigureInputAdvanced::UpdateUIEnabled);
83
84 connect(ui->debug_configure, &QPushButton::clicked, this,
85 [this] { CallDebugControllerDialog(); });
86 connect(ui->mouse_advanced, &QPushButton::clicked, this, [this] { CallMouseConfigDialog(); });
87 connect(ui->touchscreen_advanced, &QPushButton::clicked, this,
88 [this] { CallTouchscreenConfigDialog(); });
89 connect(ui->buttonMotionTouch, &QPushButton::clicked, this,
90 &ConfigureInputAdvanced::CallMotionTouchConfigDialog);
91
92 LoadConfiguration();
93}
94
95ConfigureInputAdvanced::~ConfigureInputAdvanced() = default;
96
97void ConfigureInputAdvanced::OnControllerButtonClick(int player_idx, int button_idx) {
98 const QColor new_bg_color = QColorDialog::getColor(controllers_colors[player_idx][button_idx]);
99 if (!new_bg_color.isValid()) {
100 return;
101 }
102 controllers_colors[player_idx][button_idx] = new_bg_color;
103 controllers_color_buttons[player_idx][button_idx]->setStyleSheet(
104 QStringLiteral("background-color: %1; min-width: 55px;")
105 .arg(controllers_colors[player_idx][button_idx].name()));
106}
107
108void ConfigureInputAdvanced::ApplyConfiguration() {
109 for (std::size_t player_idx = 0; player_idx < controllers_color_buttons.size(); ++player_idx) {
110 auto& player = Settings::values.players[player_idx];
111 std::array<u32, 4> colors{};
112 std::transform(controllers_colors[player_idx].begin(), controllers_colors[player_idx].end(),
113 colors.begin(), [](QColor color) { return color.rgb(); });
114
115 player.body_color_left = colors[0];
116 player.button_color_left = colors[1];
117 player.body_color_right = colors[2];
118 player.button_color_right = colors[3];
119 }
120
121 Settings::values.debug_pad_enabled = ui->debug_enabled->isChecked();
122 Settings::values.mouse_enabled = ui->mouse_enabled->isChecked();
123 Settings::values.keyboard_enabled = ui->keyboard_enabled->isChecked();
124 Settings::values.touchscreen.enabled = ui->touchscreen_enabled->isChecked();
125}
126
127void ConfigureInputAdvanced::LoadConfiguration() {
128 for (std::size_t player_idx = 0; player_idx < controllers_color_buttons.size(); ++player_idx) {
129 auto& player = Settings::values.players[player_idx];
130 std::array<u32, 4> colors = {
131 player.body_color_left,
132 player.button_color_left,
133 player.body_color_right,
134 player.button_color_right,
135 };
136
137 std::transform(colors.begin(), colors.end(), controllers_colors[player_idx].begin(),
138 [](u32 rgb) { return QColor::fromRgb(rgb); });
139
140 for (std::size_t button_idx = 0; button_idx < colors.size(); ++button_idx) {
141 controllers_color_buttons[player_idx][button_idx]->setStyleSheet(
142 QStringLiteral("background-color: %1; min-width: 55px;")
143 .arg(controllers_colors[player_idx][button_idx].name()));
144 }
145 }
146
147 ui->debug_enabled->setChecked(Settings::values.debug_pad_enabled);
148 ui->mouse_enabled->setChecked(Settings::values.mouse_enabled);
149 ui->keyboard_enabled->setChecked(Settings::values.keyboard_enabled);
150 ui->touchscreen_enabled->setChecked(Settings::values.touchscreen.enabled);
151
152 UpdateUIEnabled();
153}
154
155void ConfigureInputAdvanced::changeEvent(QEvent* event) {
156 if (event->type() == QEvent::LanguageChange) {
157 RetranslateUI();
158 }
159
160 QWidget::changeEvent(event);
161}
162
163void ConfigureInputAdvanced::RetranslateUI() {
164 ui->retranslateUi(this);
165}
166
167void ConfigureInputAdvanced::UpdateUIEnabled() {
168 ui->mouse_advanced->setEnabled(ui->mouse_enabled->isChecked());
169 ui->debug_configure->setEnabled(ui->debug_enabled->isChecked());
170 ui->touchscreen_advanced->setEnabled(ui->touchscreen_enabled->isChecked());
171}
diff --git a/src/yuzu/configuration/configure_input_advanced.h b/src/yuzu/configuration/configure_input_advanced.h
new file mode 100644
index 000000000..50bb87768
--- /dev/null
+++ b/src/yuzu/configuration/configure_input_advanced.h
@@ -0,0 +1,46 @@
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 <memory>
9#include <QWidget>
10
11class QColor;
12class QPushButton;
13
14namespace Ui {
15class ConfigureInputAdvanced;
16}
17
18class ConfigureInputAdvanced : public QWidget {
19 Q_OBJECT
20
21public:
22 explicit ConfigureInputAdvanced(QWidget* parent = nullptr);
23 ~ConfigureInputAdvanced() override;
24
25 void ApplyConfiguration();
26
27signals:
28 void CallDebugControllerDialog();
29 void CallMouseConfigDialog();
30 void CallTouchscreenConfigDialog();
31 void CallMotionTouchConfigDialog();
32
33private:
34 void changeEvent(QEvent* event) override;
35 void RetranslateUI();
36 void UpdateUIEnabled();
37
38 void OnControllerButtonClick(int player_idx, int button_idx);
39
40 void LoadConfiguration();
41
42 std::unique_ptr<Ui::ConfigureInputAdvanced> ui;
43
44 std::array<std::array<QColor, 4>, 8> controllers_colors;
45 std::array<std::array<QPushButton*, 4>, 8> controllers_color_buttons;
46};
diff --git a/src/yuzu/configuration/configure_input_advanced.ui b/src/yuzu/configuration/configure_input_advanced.ui
new file mode 100644
index 000000000..5958435fc
--- /dev/null
+++ b/src/yuzu/configuration/configure_input_advanced.ui
@@ -0,0 +1,2688 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<ui version="4.0">
3 <class>ConfigureInputAdvanced</class>
4 <widget class="QWidget" name="ConfigureInputAdvanced">
5 <property name="geometry">
6 <rect>
7 <x>0</x>
8 <y>0</y>
9 <width>710</width>
10 <height>580</height>
11 </rect>
12 </property>
13 <property name="windowTitle">
14 <string>Configure Input</string>
15 </property>
16 <property name="styleSheet">
17 <string notr="true"/>
18 </property>
19 <layout class="QHBoxLayout" name="horizontalLayout">
20 <property name="spacing">
21 <number>0</number>
22 </property>
23 <property name="leftMargin">
24 <number>0</number>
25 </property>
26 <property name="topMargin">
27 <number>0</number>
28 </property>
29 <property name="rightMargin">
30 <number>0</number>
31 </property>
32 <property name="bottomMargin">
33 <number>0</number>
34 </property>
35 <item>
36 <widget class="QWidget" name="mainInputAdvanced" native="true">
37 <layout class="QHBoxLayout" name="main" stretch="1,1">
38 <property name="spacing">
39 <number>9</number>
40 </property>
41 <property name="leftMargin">
42 <number>0</number>
43 </property>
44 <property name="topMargin">
45 <number>0</number>
46 </property>
47 <property name="rightMargin">
48 <number>0</number>
49 </property>
50 <property name="bottomMargin">
51 <number>0</number>
52 </property>
53 <item>
54 <widget class="QWidget" name="leftInputAdvanced" native="true">
55 <layout class="QVBoxLayout" name="leftLayout" stretch="0">
56 <property name="spacing">
57 <number>3</number>
58 </property>
59 <property name="leftMargin">
60 <number>0</number>
61 </property>
62 <property name="topMargin">
63 <number>0</number>
64 </property>
65 <property name="rightMargin">
66 <number>0</number>
67 </property>
68 <property name="bottomMargin">
69 <number>0</number>
70 </property>
71 <item>
72 <widget class="QGroupBox" name="joyconColorsGroup">
73 <property name="title">
74 <string>Joycon Colors</string>
75 </property>
76 <layout class="QVBoxLayout" name="verticalLayout_3" stretch="1,1">
77 <property name="leftMargin">
78 <number>9</number>
79 </property>
80 <property name="topMargin">
81 <number>9</number>
82 </property>
83 <property name="rightMargin">
84 <number>9</number>
85 </property>
86 <property name="bottomMargin">
87 <number>9</number>
88 </property>
89 <item>
90 <widget class="QWidget" name="topLeftInputAdvanced" native="true">
91 <layout class="QVBoxLayout" name="verticalLayout_4">
92 <property name="spacing">
93 <number>6</number>
94 </property>
95 <property name="leftMargin">
96 <number>0</number>
97 </property>
98 <property name="topMargin">
99 <number>0</number>
100 </property>
101 <property name="rightMargin">
102 <number>0</number>
103 </property>
104 <property name="bottomMargin">
105 <number>0</number>
106 </property>
107 <item>
108 <widget class="QWidget" name="player12Widget" native="true">
109 <layout class="QHBoxLayout" name="horizontalLayout_4">
110 <property name="leftMargin">
111 <number>0</number>
112 </property>
113 <property name="topMargin">
114 <number>0</number>
115 </property>
116 <property name="rightMargin">
117 <number>0</number>
118 </property>
119 <property name="bottomMargin">
120 <number>0</number>
121 </property>
122 <item>
123 <widget class="QGroupBox" name="player1Group">
124 <property name="title">
125 <string>Player 1</string>
126 </property>
127 <layout class="QHBoxLayout" name="horizontalLayout_2">
128 <property name="spacing">
129 <number>6</number>
130 </property>
131 <property name="leftMargin">
132 <number>6</number>
133 </property>
134 <property name="topMargin">
135 <number>0</number>
136 </property>
137 <property name="rightMargin">
138 <number>6</number>
139 </property>
140 <property name="bottomMargin">
141 <number>6</number>
142 </property>
143 <item>
144 <widget class="QWidget" name="player1LeftJoycon" native="true">
145 <layout class="QVBoxLayout" name="buttonMiscButtonsLeftJoyconVerticalLayout_14">
146 <property name="spacing">
147 <number>0</number>
148 </property>
149 <property name="leftMargin">
150 <number>0</number>
151 </property>
152 <property name="topMargin">
153 <number>0</number>
154 </property>
155 <property name="rightMargin">
156 <number>0</number>
157 </property>
158 <property name="bottomMargin">
159 <number>0</number>
160 </property>
161 <item alignment="Qt::AlignHCenter">
162 <widget class="QGroupBox" name="player1LeftBodyGroup">
163 <property name="title">
164 <string>L Body</string>
165 </property>
166 <property name="alignment">
167 <set>Qt::AlignCenter</set>
168 </property>
169 <layout class="QVBoxLayout" name="verticalLayout_66">
170 <property name="spacing">
171 <number>3</number>
172 </property>
173 <property name="leftMargin">
174 <number>3</number>
175 </property>
176 <property name="topMargin">
177 <number>3</number>
178 </property>
179 <property name="rightMargin">
180 <number>3</number>
181 </property>
182 <property name="bottomMargin">
183 <number>3</number>
184 </property>
185 <item>
186 <widget class="QPushButton" name="player1_left_body_button">
187 <property name="sizePolicy">
188 <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
189 <horstretch>0</horstretch>
190 <verstretch>0</verstretch>
191 </sizepolicy>
192 </property>
193 <property name="minimumSize">
194 <size>
195 <width>57</width>
196 <height>0</height>
197 </size>
198 </property>
199 <property name="maximumSize">
200 <size>
201 <width>55</width>
202 <height>16777215</height>
203 </size>
204 </property>
205 <property name="styleSheet">
206 <string notr="true">min-width: 55px;</string>
207 </property>
208 <property name="text">
209 <string/>
210 </property>
211 </widget>
212 </item>
213 </layout>
214 </widget>
215 </item>
216 <item alignment="Qt::AlignHCenter">
217 <widget class="QGroupBox" name="player1LeftButtonsGroup">
218 <property name="title">
219 <string>L Button</string>
220 </property>
221 <property name="alignment">
222 <set>Qt::AlignCenter</set>
223 </property>
224 <layout class="QVBoxLayout" name="verticalLayout_67">
225 <property name="spacing">
226 <number>3</number>
227 </property>
228 <property name="leftMargin">
229 <number>3</number>
230 </property>
231 <property name="topMargin">
232 <number>3</number>
233 </property>
234 <property name="rightMargin">
235 <number>3</number>
236 </property>
237 <property name="bottomMargin">
238 <number>3</number>
239 </property>
240 <item>
241 <widget class="QPushButton" name="player1_left_buttons_button">
242 <property name="sizePolicy">
243 <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
244 <horstretch>0</horstretch>
245 <verstretch>0</verstretch>
246 </sizepolicy>
247 </property>
248 <property name="minimumSize">
249 <size>
250 <width>57</width>
251 <height>0</height>
252 </size>
253 </property>
254 <property name="maximumSize">
255 <size>
256 <width>55</width>
257 <height>16777215</height>
258 </size>
259 </property>
260 <property name="styleSheet">
261 <string notr="true">min-width: 55px;</string>
262 </property>
263 <property name="text">
264 <string/>
265 </property>
266 </widget>
267 </item>
268 </layout>
269 </widget>
270 </item>
271 </layout>
272 </widget>
273 </item>
274 <item>
275 <widget class="QWidget" name="player1RightJoycon" native="true">
276 <layout class="QVBoxLayout" name="buttonMiscButtonsRightJoyconVerticalLayout_14">
277 <property name="spacing">
278 <number>0</number>
279 </property>
280 <property name="leftMargin">
281 <number>0</number>
282 </property>
283 <property name="topMargin">
284 <number>0</number>
285 </property>
286 <property name="rightMargin">
287 <number>0</number>
288 </property>
289 <property name="bottomMargin">
290 <number>0</number>
291 </property>
292 <item alignment="Qt::AlignHCenter">
293 <widget class="QGroupBox" name="player1RightBodyGroup">
294 <property name="title">
295 <string>R Body</string>
296 </property>
297 <property name="alignment">
298 <set>Qt::AlignCenter</set>
299 </property>
300 <layout class="QVBoxLayout" name="verticalLayout_64">
301 <property name="spacing">
302 <number>3</number>
303 </property>
304 <property name="leftMargin">
305 <number>3</number>
306 </property>
307 <property name="topMargin">
308 <number>3</number>
309 </property>
310 <property name="rightMargin">
311 <number>3</number>
312 </property>
313 <property name="bottomMargin">
314 <number>3</number>
315 </property>
316 <item>
317 <widget class="QPushButton" name="player1_right_body_button">
318 <property name="sizePolicy">
319 <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
320 <horstretch>0</horstretch>
321 <verstretch>0</verstretch>
322 </sizepolicy>
323 </property>
324 <property name="minimumSize">
325 <size>
326 <width>57</width>
327 <height>0</height>
328 </size>
329 </property>
330 <property name="maximumSize">
331 <size>
332 <width>55</width>
333 <height>16777215</height>
334 </size>
335 </property>
336 <property name="styleSheet">
337 <string notr="true">min-width: 55px;</string>
338 </property>
339 <property name="text">
340 <string/>
341 </property>
342 </widget>
343 </item>
344 </layout>
345 </widget>
346 </item>
347 <item alignment="Qt::AlignHCenter">
348 <widget class="QGroupBox" name="player1RightButtonsGroup">
349 <property name="title">
350 <string>R Button</string>
351 </property>
352 <property name="alignment">
353 <set>Qt::AlignCenter</set>
354 </property>
355 <layout class="QVBoxLayout" name="verticalLayout_65">
356 <property name="spacing">
357 <number>3</number>
358 </property>
359 <property name="leftMargin">
360 <number>3</number>
361 </property>
362 <property name="topMargin">
363 <number>3</number>
364 </property>
365 <property name="rightMargin">
366 <number>3</number>
367 </property>
368 <property name="bottomMargin">
369 <number>3</number>
370 </property>
371 <item>
372 <widget class="QPushButton" name="player1_right_buttons_button">
373 <property name="sizePolicy">
374 <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
375 <horstretch>0</horstretch>
376 <verstretch>0</verstretch>
377 </sizepolicy>
378 </property>
379 <property name="minimumSize">
380 <size>
381 <width>57</width>
382 <height>0</height>
383 </size>
384 </property>
385 <property name="maximumSize">
386 <size>
387 <width>55</width>
388 <height>16777215</height>
389 </size>
390 </property>
391 <property name="styleSheet">
392 <string notr="true">min-width: 55px;</string>
393 </property>
394 <property name="text">
395 <string/>
396 </property>
397 </widget>
398 </item>
399 </layout>
400 </widget>
401 </item>
402 </layout>
403 </widget>
404 </item>
405 </layout>
406 </widget>
407 </item>
408 <item>
409 <widget class="QGroupBox" name="player2Group">
410 <property name="title">
411 <string>Player 2</string>
412 </property>
413 <layout class="QHBoxLayout" name="horizontalLayout_14">
414 <property name="spacing">
415 <number>6</number>
416 </property>
417 <property name="leftMargin">
418 <number>6</number>
419 </property>
420 <property name="topMargin">
421 <number>0</number>
422 </property>
423 <property name="rightMargin">
424 <number>6</number>
425 </property>
426 <property name="bottomMargin">
427 <number>6</number>
428 </property>
429 <item>
430 <widget class="QWidget" name="player2LeftJoycon" native="true">
431 <layout class="QVBoxLayout" name="buttonMiscButtonsLeftJoyconVerticalLayout_15">
432 <property name="spacing">
433 <number>0</number>
434 </property>
435 <property name="leftMargin">
436 <number>0</number>
437 </property>
438 <property name="topMargin">
439 <number>0</number>
440 </property>
441 <property name="rightMargin">
442 <number>0</number>
443 </property>
444 <property name="bottomMargin">
445 <number>0</number>
446 </property>
447 <item alignment="Qt::AlignHCenter">
448 <widget class="QGroupBox" name="player2LeftBodyGroup">
449 <property name="title">
450 <string>L Body</string>
451 </property>
452 <property name="alignment">
453 <set>Qt::AlignCenter</set>
454 </property>
455 <layout class="QVBoxLayout" name="verticalLayout_70">
456 <property name="spacing">
457 <number>3</number>
458 </property>
459 <property name="leftMargin">
460 <number>3</number>
461 </property>
462 <property name="topMargin">
463 <number>3</number>
464 </property>
465 <property name="rightMargin">
466 <number>3</number>
467 </property>
468 <property name="bottomMargin">
469 <number>3</number>
470 </property>
471 <item>
472 <widget class="QPushButton" name="player2_left_body_button">
473 <property name="sizePolicy">
474 <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
475 <horstretch>0</horstretch>
476 <verstretch>0</verstretch>
477 </sizepolicy>
478 </property>
479 <property name="minimumSize">
480 <size>
481 <width>57</width>
482 <height>0</height>
483 </size>
484 </property>
485 <property name="maximumSize">
486 <size>
487 <width>55</width>
488 <height>16777215</height>
489 </size>
490 </property>
491 <property name="styleSheet">
492 <string notr="true">min-width: 55px;</string>
493 </property>
494 <property name="text">
495 <string/>
496 </property>
497 </widget>
498 </item>
499 </layout>
500 </widget>
501 </item>
502 <item alignment="Qt::AlignHCenter">
503 <widget class="QGroupBox" name="player2LeftButtonsGroup">
504 <property name="title">
505 <string>L Button</string>
506 </property>
507 <property name="alignment">
508 <set>Qt::AlignCenter</set>
509 </property>
510 <layout class="QVBoxLayout" name="verticalLayout_71">
511 <property name="spacing">
512 <number>3</number>
513 </property>
514 <property name="leftMargin">
515 <number>3</number>
516 </property>
517 <property name="topMargin">
518 <number>3</number>
519 </property>
520 <property name="rightMargin">
521 <number>3</number>
522 </property>
523 <property name="bottomMargin">
524 <number>3</number>
525 </property>
526 <item>
527 <widget class="QPushButton" name="player2_left_buttons_button">
528 <property name="sizePolicy">
529 <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
530 <horstretch>0</horstretch>
531 <verstretch>0</verstretch>
532 </sizepolicy>
533 </property>
534 <property name="minimumSize">
535 <size>
536 <width>57</width>
537 <height>0</height>
538 </size>
539 </property>
540 <property name="maximumSize">
541 <size>
542 <width>55</width>
543 <height>16777215</height>
544 </size>
545 </property>
546 <property name="styleSheet">
547 <string notr="true">min-width: 55px;</string>
548 </property>
549 <property name="text">
550 <string/>
551 </property>
552 </widget>
553 </item>
554 </layout>
555 </widget>
556 </item>
557 </layout>
558 </widget>
559 </item>
560 <item>
561 <widget class="QWidget" name="player2RightJoycon" native="true">
562 <layout class="QVBoxLayout" name="buttonMiscButtonsRightJoyconVerticalLayout_15">
563 <property name="spacing">
564 <number>0</number>
565 </property>
566 <property name="leftMargin">
567 <number>0</number>
568 </property>
569 <property name="topMargin">
570 <number>0</number>
571 </property>
572 <property name="rightMargin">
573 <number>0</number>
574 </property>
575 <property name="bottomMargin">
576 <number>0</number>
577 </property>
578 <item alignment="Qt::AlignHCenter">
579 <widget class="QGroupBox" name="player2RightBodyGroup">
580 <property name="title">
581 <string>R Body</string>
582 </property>
583 <property name="alignment">
584 <set>Qt::AlignCenter</set>
585 </property>
586 <layout class="QVBoxLayout" name="verticalLayout_68">
587 <property name="spacing">
588 <number>3</number>
589 </property>
590 <property name="leftMargin">
591 <number>3</number>
592 </property>
593 <property name="topMargin">
594 <number>3</number>
595 </property>
596 <property name="rightMargin">
597 <number>3</number>
598 </property>
599 <property name="bottomMargin">
600 <number>3</number>
601 </property>
602 <item>
603 <widget class="QPushButton" name="player2_right_body_button">
604 <property name="sizePolicy">
605 <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
606 <horstretch>0</horstretch>
607 <verstretch>0</verstretch>
608 </sizepolicy>
609 </property>
610 <property name="minimumSize">
611 <size>
612 <width>57</width>
613 <height>0</height>
614 </size>
615 </property>
616 <property name="maximumSize">
617 <size>
618 <width>55</width>
619 <height>16777215</height>
620 </size>
621 </property>
622 <property name="styleSheet">
623 <string notr="true">min-width: 55px;</string>
624 </property>
625 <property name="text">
626 <string/>
627 </property>
628 </widget>
629 </item>
630 </layout>
631 </widget>
632 </item>
633 <item alignment="Qt::AlignHCenter">
634 <widget class="QGroupBox" name="player2RightButtonsGroup">
635 <property name="title">
636 <string>R Button</string>
637 </property>
638 <property name="alignment">
639 <set>Qt::AlignCenter</set>
640 </property>
641 <layout class="QVBoxLayout" name="verticalLayout_69">
642 <property name="spacing">
643 <number>3</number>
644 </property>
645 <property name="leftMargin">
646 <number>3</number>
647 </property>
648 <property name="topMargin">
649 <number>3</number>
650 </property>
651 <property name="rightMargin">
652 <number>3</number>
653 </property>
654 <property name="bottomMargin">
655 <number>3</number>
656 </property>
657 <item>
658 <widget class="QPushButton" name="player2_right_buttons_button">
659 <property name="sizePolicy">
660 <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
661 <horstretch>0</horstretch>
662 <verstretch>0</verstretch>
663 </sizepolicy>
664 </property>
665 <property name="minimumSize">
666 <size>
667 <width>57</width>
668 <height>0</height>
669 </size>
670 </property>
671 <property name="maximumSize">
672 <size>
673 <width>55</width>
674 <height>16777215</height>
675 </size>
676 </property>
677 <property name="styleSheet">
678 <string notr="true">min-width: 55px;</string>
679 </property>
680 <property name="text">
681 <string/>
682 </property>
683 </widget>
684 </item>
685 </layout>
686 </widget>
687 </item>
688 </layout>
689 </widget>
690 </item>
691 </layout>
692 </widget>
693 </item>
694 </layout>
695 </widget>
696 </item>
697 <item>
698 <widget class="QWidget" name="player34Widget" native="true">
699 <layout class="QHBoxLayout" name="horizontalLayout_5">
700 <property name="leftMargin">
701 <number>0</number>
702 </property>
703 <property name="topMargin">
704 <number>0</number>
705 </property>
706 <property name="rightMargin">
707 <number>0</number>
708 </property>
709 <property name="bottomMargin">
710 <number>0</number>
711 </property>
712 <item>
713 <widget class="QGroupBox" name="player3Group">
714 <property name="title">
715 <string>Player 3</string>
716 </property>
717 <layout class="QHBoxLayout" name="horizontalLayout_15">
718 <property name="spacing">
719 <number>6</number>
720 </property>
721 <property name="leftMargin">
722 <number>6</number>
723 </property>
724 <property name="topMargin">
725 <number>0</number>
726 </property>
727 <property name="rightMargin">
728 <number>6</number>
729 </property>
730 <property name="bottomMargin">
731 <number>6</number>
732 </property>
733 <item>
734 <widget class="QWidget" name="player3LeftJoycon" native="true">
735 <layout class="QVBoxLayout" name="buttonMiscButtonsLeftJoyconVerticalLayout_16">
736 <property name="spacing">
737 <number>0</number>
738 </property>
739 <property name="leftMargin">
740 <number>0</number>
741 </property>
742 <property name="topMargin">
743 <number>0</number>
744 </property>
745 <property name="rightMargin">
746 <number>0</number>
747 </property>
748 <property name="bottomMargin">
749 <number>0</number>
750 </property>
751 <item alignment="Qt::AlignHCenter">
752 <widget class="QGroupBox" name="player3LeftBodyGroup">
753 <property name="title">
754 <string>L Body</string>
755 </property>
756 <property name="alignment">
757 <set>Qt::AlignCenter</set>
758 </property>
759 <layout class="QVBoxLayout" name="verticalLayout_74">
760 <property name="spacing">
761 <number>3</number>
762 </property>
763 <property name="leftMargin">
764 <number>3</number>
765 </property>
766 <property name="topMargin">
767 <number>3</number>
768 </property>
769 <property name="rightMargin">
770 <number>3</number>
771 </property>
772 <property name="bottomMargin">
773 <number>3</number>
774 </property>
775 <item>
776 <widget class="QPushButton" name="player3_left_body_button">
777 <property name="sizePolicy">
778 <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
779 <horstretch>0</horstretch>
780 <verstretch>0</verstretch>
781 </sizepolicy>
782 </property>
783 <property name="minimumSize">
784 <size>
785 <width>57</width>
786 <height>0</height>
787 </size>
788 </property>
789 <property name="maximumSize">
790 <size>
791 <width>55</width>
792 <height>16777215</height>
793 </size>
794 </property>
795 <property name="styleSheet">
796 <string notr="true">min-width: 55px;</string>
797 </property>
798 <property name="text">
799 <string/>
800 </property>
801 </widget>
802 </item>
803 </layout>
804 </widget>
805 </item>
806 <item alignment="Qt::AlignHCenter">
807 <widget class="QGroupBox" name="player3LeftButtonsGroup">
808 <property name="title">
809 <string>L Button</string>
810 </property>
811 <property name="alignment">
812 <set>Qt::AlignCenter</set>
813 </property>
814 <layout class="QVBoxLayout" name="verticalLayout_75">
815 <property name="spacing">
816 <number>3</number>
817 </property>
818 <property name="leftMargin">
819 <number>3</number>
820 </property>
821 <property name="topMargin">
822 <number>3</number>
823 </property>
824 <property name="rightMargin">
825 <number>3</number>
826 </property>
827 <property name="bottomMargin">
828 <number>3</number>
829 </property>
830 <item>
831 <widget class="QPushButton" name="player3_left_buttons_button">
832 <property name="sizePolicy">
833 <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
834 <horstretch>0</horstretch>
835 <verstretch>0</verstretch>
836 </sizepolicy>
837 </property>
838 <property name="minimumSize">
839 <size>
840 <width>57</width>
841 <height>0</height>
842 </size>
843 </property>
844 <property name="maximumSize">
845 <size>
846 <width>55</width>
847 <height>16777215</height>
848 </size>
849 </property>
850 <property name="styleSheet">
851 <string notr="true">min-width: 55px;</string>
852 </property>
853 <property name="text">
854 <string/>
855 </property>
856 </widget>
857 </item>
858 </layout>
859 </widget>
860 </item>
861 </layout>
862 </widget>
863 </item>
864 <item>
865 <widget class="QWidget" name="player3RightJoycon" native="true">
866 <layout class="QVBoxLayout" name="buttonMiscButtonsRightJoyconVerticalLayout_16">
867 <property name="spacing">
868 <number>0</number>
869 </property>
870 <property name="leftMargin">
871 <number>0</number>
872 </property>
873 <property name="topMargin">
874 <number>0</number>
875 </property>
876 <property name="rightMargin">
877 <number>0</number>
878 </property>
879 <property name="bottomMargin">
880 <number>0</number>
881 </property>
882 <item alignment="Qt::AlignHCenter">
883 <widget class="QGroupBox" name="player3RightBodyGroup">
884 <property name="title">
885 <string>R Body</string>
886 </property>
887 <property name="alignment">
888 <set>Qt::AlignCenter</set>
889 </property>
890 <layout class="QVBoxLayout" name="verticalLayout_72">
891 <property name="spacing">
892 <number>3</number>
893 </property>
894 <property name="leftMargin">
895 <number>3</number>
896 </property>
897 <property name="topMargin">
898 <number>3</number>
899 </property>
900 <property name="rightMargin">
901 <number>3</number>
902 </property>
903 <property name="bottomMargin">
904 <number>3</number>
905 </property>
906 <item>
907 <widget class="QPushButton" name="player3_right_body_button">
908 <property name="sizePolicy">
909 <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
910 <horstretch>0</horstretch>
911 <verstretch>0</verstretch>
912 </sizepolicy>
913 </property>
914 <property name="minimumSize">
915 <size>
916 <width>57</width>
917 <height>0</height>
918 </size>
919 </property>
920 <property name="maximumSize">
921 <size>
922 <width>55</width>
923 <height>16777215</height>
924 </size>
925 </property>
926 <property name="styleSheet">
927 <string notr="true">min-width: 55px;</string>
928 </property>
929 <property name="text">
930 <string/>
931 </property>
932 </widget>
933 </item>
934 </layout>
935 </widget>
936 </item>
937 <item alignment="Qt::AlignHCenter">
938 <widget class="QGroupBox" name="player3RightButtonsGroup">
939 <property name="title">
940 <string>R Button</string>
941 </property>
942 <property name="alignment">
943 <set>Qt::AlignCenter</set>
944 </property>
945 <layout class="QVBoxLayout" name="verticalLayout_73">
946 <property name="spacing">
947 <number>3</number>
948 </property>
949 <property name="leftMargin">
950 <number>3</number>
951 </property>
952 <property name="topMargin">
953 <number>3</number>
954 </property>
955 <property name="rightMargin">
956 <number>3</number>
957 </property>
958 <property name="bottomMargin">
959 <number>3</number>
960 </property>
961 <item>
962 <widget class="QPushButton" name="player3_right_buttons_button">
963 <property name="sizePolicy">
964 <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
965 <horstretch>0</horstretch>
966 <verstretch>0</verstretch>
967 </sizepolicy>
968 </property>
969 <property name="minimumSize">
970 <size>
971 <width>57</width>
972 <height>0</height>
973 </size>
974 </property>
975 <property name="maximumSize">
976 <size>
977 <width>55</width>
978 <height>16777215</height>
979 </size>
980 </property>
981 <property name="styleSheet">
982 <string notr="true">min-width: 55px;</string>
983 </property>
984 <property name="text">
985 <string/>
986 </property>
987 </widget>
988 </item>
989 </layout>
990 </widget>
991 </item>
992 </layout>
993 </widget>
994 </item>
995 </layout>
996 </widget>
997 </item>
998 <item>
999 <widget class="QGroupBox" name="player4Group">
1000 <property name="title">
1001 <string>Player 4</string>
1002 </property>
1003 <layout class="QHBoxLayout" name="horizontalLayout_16">
1004 <property name="spacing">
1005 <number>6</number>
1006 </property>
1007 <property name="leftMargin">
1008 <number>6</number>
1009 </property>
1010 <property name="topMargin">
1011 <number>0</number>
1012 </property>
1013 <property name="rightMargin">
1014 <number>6</number>
1015 </property>
1016 <property name="bottomMargin">
1017 <number>6</number>
1018 </property>
1019 <item>
1020 <widget class="QWidget" name="player4LeftJoycon" native="true">
1021 <layout class="QVBoxLayout" name="buttonMiscButtonsLeftJoyconVerticalLayout_17">
1022 <property name="spacing">
1023 <number>0</number>
1024 </property>
1025 <property name="leftMargin">
1026 <number>0</number>
1027 </property>
1028 <property name="topMargin">
1029 <number>0</number>
1030 </property>
1031 <property name="rightMargin">
1032 <number>0</number>
1033 </property>
1034 <property name="bottomMargin">
1035 <number>0</number>
1036 </property>
1037 <item alignment="Qt::AlignHCenter">
1038 <widget class="QGroupBox" name="player4LeftBodyGroup">
1039 <property name="title">
1040 <string>L Body</string>
1041 </property>
1042 <property name="alignment">
1043 <set>Qt::AlignCenter</set>
1044 </property>
1045 <layout class="QVBoxLayout" name="verticalLayout_78">
1046 <property name="spacing">
1047 <number>3</number>
1048 </property>
1049 <property name="leftMargin">
1050 <number>3</number>
1051 </property>
1052 <property name="topMargin">
1053 <number>3</number>
1054 </property>
1055 <property name="rightMargin">
1056 <number>3</number>
1057 </property>
1058 <property name="bottomMargin">
1059 <number>3</number>
1060 </property>
1061 <item>
1062 <widget class="QPushButton" name="player4_left_body_button">
1063 <property name="sizePolicy">
1064 <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
1065 <horstretch>0</horstretch>
1066 <verstretch>0</verstretch>
1067 </sizepolicy>
1068 </property>
1069 <property name="minimumSize">
1070 <size>
1071 <width>57</width>
1072 <height>0</height>
1073 </size>
1074 </property>
1075 <property name="maximumSize">
1076 <size>
1077 <width>55</width>
1078 <height>16777215</height>
1079 </size>
1080 </property>
1081 <property name="styleSheet">
1082 <string notr="true">min-width: 55px;</string>
1083 </property>
1084 <property name="text">
1085 <string/>
1086 </property>
1087 </widget>
1088 </item>
1089 </layout>
1090 </widget>
1091 </item>
1092 <item alignment="Qt::AlignHCenter">
1093 <widget class="QGroupBox" name="player4LeftButtonsGroup">
1094 <property name="title">
1095 <string>L Button</string>
1096 </property>
1097 <property name="alignment">
1098 <set>Qt::AlignCenter</set>
1099 </property>
1100 <layout class="QVBoxLayout" name="verticalLayout_79">
1101 <property name="spacing">
1102 <number>3</number>
1103 </property>
1104 <property name="leftMargin">
1105 <number>3</number>
1106 </property>
1107 <property name="topMargin">
1108 <number>3</number>
1109 </property>
1110 <property name="rightMargin">
1111 <number>3</number>
1112 </property>
1113 <property name="bottomMargin">
1114 <number>3</number>
1115 </property>
1116 <item>
1117 <widget class="QPushButton" name="player4_left_buttons_button">
1118 <property name="sizePolicy">
1119 <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
1120 <horstretch>0</horstretch>
1121 <verstretch>0</verstretch>
1122 </sizepolicy>
1123 </property>
1124 <property name="minimumSize">
1125 <size>
1126 <width>57</width>
1127 <height>0</height>
1128 </size>
1129 </property>
1130 <property name="maximumSize">
1131 <size>
1132 <width>55</width>
1133 <height>16777215</height>
1134 </size>
1135 </property>
1136 <property name="styleSheet">
1137 <string notr="true">min-width: 55px;</string>
1138 </property>
1139 <property name="text">
1140 <string/>
1141 </property>
1142 </widget>
1143 </item>
1144 </layout>
1145 </widget>
1146 </item>
1147 </layout>
1148 </widget>
1149 </item>
1150 <item>
1151 <widget class="QWidget" name="player4RightJoycon" native="true">
1152 <layout class="QVBoxLayout" name="buttonMiscButtonsRightJoyconVerticalLayout_17">
1153 <property name="spacing">
1154 <number>0</number>
1155 </property>
1156 <property name="leftMargin">
1157 <number>0</number>
1158 </property>
1159 <property name="topMargin">
1160 <number>0</number>
1161 </property>
1162 <property name="rightMargin">
1163 <number>0</number>
1164 </property>
1165 <property name="bottomMargin">
1166 <number>0</number>
1167 </property>
1168 <item alignment="Qt::AlignHCenter">
1169 <widget class="QGroupBox" name="player4RightBodyGroup">
1170 <property name="title">
1171 <string>R Body</string>
1172 </property>
1173 <property name="alignment">
1174 <set>Qt::AlignCenter</set>
1175 </property>
1176 <layout class="QVBoxLayout" name="verticalLayout_76">
1177 <property name="spacing">
1178 <number>3</number>
1179 </property>
1180 <property name="leftMargin">
1181 <number>3</number>
1182 </property>
1183 <property name="topMargin">
1184 <number>3</number>
1185 </property>
1186 <property name="rightMargin">
1187 <number>3</number>
1188 </property>
1189 <property name="bottomMargin">
1190 <number>3</number>
1191 </property>
1192 <item>
1193 <widget class="QPushButton" name="player4_right_body_button">
1194 <property name="sizePolicy">
1195 <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
1196 <horstretch>0</horstretch>
1197 <verstretch>0</verstretch>
1198 </sizepolicy>
1199 </property>
1200 <property name="minimumSize">
1201 <size>
1202 <width>57</width>
1203 <height>0</height>
1204 </size>
1205 </property>
1206 <property name="maximumSize">
1207 <size>
1208 <width>55</width>
1209 <height>16777215</height>
1210 </size>
1211 </property>
1212 <property name="styleSheet">
1213 <string notr="true">min-width: 55px;</string>
1214 </property>
1215 <property name="text">
1216 <string/>
1217 </property>
1218 </widget>
1219 </item>
1220 </layout>
1221 </widget>
1222 </item>
1223 <item alignment="Qt::AlignHCenter">
1224 <widget class="QGroupBox" name="player4RightButtonsGroup">
1225 <property name="title">
1226 <string>R Button</string>
1227 </property>
1228 <property name="alignment">
1229 <set>Qt::AlignCenter</set>
1230 </property>
1231 <layout class="QVBoxLayout" name="verticalLayout_77">
1232 <property name="spacing">
1233 <number>3</number>
1234 </property>
1235 <property name="leftMargin">
1236 <number>3</number>
1237 </property>
1238 <property name="topMargin">
1239 <number>3</number>
1240 </property>
1241 <property name="rightMargin">
1242 <number>3</number>
1243 </property>
1244 <property name="bottomMargin">
1245 <number>3</number>
1246 </property>
1247 <item>
1248 <widget class="QPushButton" name="player4_right_buttons_button">
1249 <property name="sizePolicy">
1250 <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
1251 <horstretch>0</horstretch>
1252 <verstretch>0</verstretch>
1253 </sizepolicy>
1254 </property>
1255 <property name="minimumSize">
1256 <size>
1257 <width>57</width>
1258 <height>0</height>
1259 </size>
1260 </property>
1261 <property name="maximumSize">
1262 <size>
1263 <width>55</width>
1264 <height>16777215</height>
1265 </size>
1266 </property>
1267 <property name="styleSheet">
1268 <string notr="true">min-width: 55px;</string>
1269 </property>
1270 <property name="text">
1271 <string/>
1272 </property>
1273 </widget>
1274 </item>
1275 </layout>
1276 </widget>
1277 </item>
1278 </layout>
1279 </widget>
1280 </item>
1281 </layout>
1282 </widget>
1283 </item>
1284 </layout>
1285 </widget>
1286 </item>
1287 </layout>
1288 </widget>
1289 </item>
1290 <item>
1291 <widget class="QWidget" name="bottomLeftInputAdvanced" native="true">
1292 <layout class="QVBoxLayout" name="verticalLayout_5">
1293 <property name="spacing">
1294 <number>6</number>
1295 </property>
1296 <property name="leftMargin">
1297 <number>0</number>
1298 </property>
1299 <property name="topMargin">
1300 <number>0</number>
1301 </property>
1302 <property name="rightMargin">
1303 <number>0</number>
1304 </property>
1305 <property name="bottomMargin">
1306 <number>0</number>
1307 </property>
1308 <item>
1309 <widget class="QWidget" name="player56Widget" native="true">
1310 <layout class="QHBoxLayout" name="horizontalLayout_6">
1311 <property name="leftMargin">
1312 <number>0</number>
1313 </property>
1314 <property name="topMargin">
1315 <number>0</number>
1316 </property>
1317 <property name="rightMargin">
1318 <number>0</number>
1319 </property>
1320 <property name="bottomMargin">
1321 <number>0</number>
1322 </property>
1323 <item>
1324 <widget class="QGroupBox" name="player5Group">
1325 <property name="title">
1326 <string>Player 5</string>
1327 </property>
1328 <layout class="QHBoxLayout" name="horizontalLayout_10">
1329 <property name="spacing">
1330 <number>6</number>
1331 </property>
1332 <property name="leftMargin">
1333 <number>6</number>
1334 </property>
1335 <property name="topMargin">
1336 <number>0</number>
1337 </property>
1338 <property name="rightMargin">
1339 <number>6</number>
1340 </property>
1341 <property name="bottomMargin">
1342 <number>6</number>
1343 </property>
1344 <item>
1345 <widget class="QWidget" name="player5LeftJoycon" native="true">
1346 <layout class="QVBoxLayout" name="buttonMiscButtonsLeftJoyconVerticalLayout_10">
1347 <property name="spacing">
1348 <number>0</number>
1349 </property>
1350 <property name="leftMargin">
1351 <number>0</number>
1352 </property>
1353 <property name="topMargin">
1354 <number>0</number>
1355 </property>
1356 <property name="rightMargin">
1357 <number>0</number>
1358 </property>
1359 <property name="bottomMargin">
1360 <number>0</number>
1361 </property>
1362 <item alignment="Qt::AlignHCenter">
1363 <widget class="QGroupBox" name="player5LeftBodyGroup">
1364 <property name="title">
1365 <string>L Body</string>
1366 </property>
1367 <property name="alignment">
1368 <set>Qt::AlignCenter</set>
1369 </property>
1370 <layout class="QVBoxLayout" name="verticalLayout_50">
1371 <property name="spacing">
1372 <number>3</number>
1373 </property>
1374 <property name="leftMargin">
1375 <number>3</number>
1376 </property>
1377 <property name="topMargin">
1378 <number>3</number>
1379 </property>
1380 <property name="rightMargin">
1381 <number>3</number>
1382 </property>
1383 <property name="bottomMargin">
1384 <number>3</number>
1385 </property>
1386 <item>
1387 <widget class="QPushButton" name="player5_left_body_button">
1388 <property name="sizePolicy">
1389 <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
1390 <horstretch>0</horstretch>
1391 <verstretch>0</verstretch>
1392 </sizepolicy>
1393 </property>
1394 <property name="minimumSize">
1395 <size>
1396 <width>57</width>
1397 <height>0</height>
1398 </size>
1399 </property>
1400 <property name="maximumSize">
1401 <size>
1402 <width>55</width>
1403 <height>16777215</height>
1404 </size>
1405 </property>
1406 <property name="styleSheet">
1407 <string notr="true">min-width: 55px;</string>
1408 </property>
1409 <property name="text">
1410 <string/>
1411 </property>
1412 </widget>
1413 </item>
1414 </layout>
1415 </widget>
1416 </item>
1417 <item alignment="Qt::AlignHCenter">
1418 <widget class="QGroupBox" name="player5LeftButtonsGroup">
1419 <property name="title">
1420 <string>L Button</string>
1421 </property>
1422 <property name="alignment">
1423 <set>Qt::AlignCenter</set>
1424 </property>
1425 <layout class="QVBoxLayout" name="verticalLayout_51">
1426 <property name="spacing">
1427 <number>3</number>
1428 </property>
1429 <property name="leftMargin">
1430 <number>3</number>
1431 </property>
1432 <property name="topMargin">
1433 <number>3</number>
1434 </property>
1435 <property name="rightMargin">
1436 <number>3</number>
1437 </property>
1438 <property name="bottomMargin">
1439 <number>3</number>
1440 </property>
1441 <item>
1442 <widget class="QPushButton" name="player5_left_buttons_button">
1443 <property name="sizePolicy">
1444 <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
1445 <horstretch>0</horstretch>
1446 <verstretch>0</verstretch>
1447 </sizepolicy>
1448 </property>
1449 <property name="minimumSize">
1450 <size>
1451 <width>57</width>
1452 <height>0</height>
1453 </size>
1454 </property>
1455 <property name="maximumSize">
1456 <size>
1457 <width>55</width>
1458 <height>16777215</height>
1459 </size>
1460 </property>
1461 <property name="styleSheet">
1462 <string notr="true">min-width: 55px;</string>
1463 </property>
1464 <property name="text">
1465 <string/>
1466 </property>
1467 </widget>
1468 </item>
1469 </layout>
1470 </widget>
1471 </item>
1472 </layout>
1473 </widget>
1474 </item>
1475 <item>
1476 <widget class="QWidget" name="player5RightJoycon" native="true">
1477 <layout class="QVBoxLayout" name="buttonMiscButtonsRightJoyconVerticalLayout_10">
1478 <property name="spacing">
1479 <number>0</number>
1480 </property>
1481 <property name="leftMargin">
1482 <number>0</number>
1483 </property>
1484 <property name="topMargin">
1485 <number>0</number>
1486 </property>
1487 <property name="rightMargin">
1488 <number>0</number>
1489 </property>
1490 <property name="bottomMargin">
1491 <number>0</number>
1492 </property>
1493 <item alignment="Qt::AlignHCenter">
1494 <widget class="QGroupBox" name="player5RightBodyGroup">
1495 <property name="title">
1496 <string>R Body</string>
1497 </property>
1498 <property name="alignment">
1499 <set>Qt::AlignCenter</set>
1500 </property>
1501 <layout class="QVBoxLayout" name="verticalLayout_48">
1502 <property name="spacing">
1503 <number>3</number>
1504 </property>
1505 <property name="leftMargin">
1506 <number>3</number>
1507 </property>
1508 <property name="topMargin">
1509 <number>3</number>
1510 </property>
1511 <property name="rightMargin">
1512 <number>3</number>
1513 </property>
1514 <property name="bottomMargin">
1515 <number>3</number>
1516 </property>
1517 <item>
1518 <widget class="QPushButton" name="player5_right_body_button">
1519 <property name="sizePolicy">
1520 <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
1521 <horstretch>0</horstretch>
1522 <verstretch>0</verstretch>
1523 </sizepolicy>
1524 </property>
1525 <property name="minimumSize">
1526 <size>
1527 <width>57</width>
1528 <height>0</height>
1529 </size>
1530 </property>
1531 <property name="maximumSize">
1532 <size>
1533 <width>55</width>
1534 <height>16777215</height>
1535 </size>
1536 </property>
1537 <property name="styleSheet">
1538 <string notr="true">min-width: 55px;</string>
1539 </property>
1540 <property name="text">
1541 <string/>
1542 </property>
1543 </widget>
1544 </item>
1545 </layout>
1546 </widget>
1547 </item>
1548 <item alignment="Qt::AlignHCenter">
1549 <widget class="QGroupBox" name="player5RightButtonsGroup">
1550 <property name="title">
1551 <string>R Button</string>
1552 </property>
1553 <property name="alignment">
1554 <set>Qt::AlignCenter</set>
1555 </property>
1556 <layout class="QVBoxLayout" name="verticalLayout_49">
1557 <property name="spacing">
1558 <number>3</number>
1559 </property>
1560 <property name="leftMargin">
1561 <number>3</number>
1562 </property>
1563 <property name="topMargin">
1564 <number>3</number>
1565 </property>
1566 <property name="rightMargin">
1567 <number>3</number>
1568 </property>
1569 <property name="bottomMargin">
1570 <number>3</number>
1571 </property>
1572 <item>
1573 <widget class="QPushButton" name="player5_right_buttons_button">
1574 <property name="sizePolicy">
1575 <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
1576 <horstretch>0</horstretch>
1577 <verstretch>0</verstretch>
1578 </sizepolicy>
1579 </property>
1580 <property name="minimumSize">
1581 <size>
1582 <width>57</width>
1583 <height>0</height>
1584 </size>
1585 </property>
1586 <property name="maximumSize">
1587 <size>
1588 <width>55</width>
1589 <height>16777215</height>
1590 </size>
1591 </property>
1592 <property name="styleSheet">
1593 <string notr="true">min-width: 55px;</string>
1594 </property>
1595 <property name="text">
1596 <string/>
1597 </property>
1598 </widget>
1599 </item>
1600 </layout>
1601 </widget>
1602 </item>
1603 </layout>
1604 </widget>
1605 </item>
1606 </layout>
1607 </widget>
1608 </item>
1609 <item>
1610 <widget class="QGroupBox" name="player6Group">
1611 <property name="title">
1612 <string>Player 6</string>
1613 </property>
1614 <layout class="QHBoxLayout" name="horizontalLayout_11">
1615 <property name="spacing">
1616 <number>6</number>
1617 </property>
1618 <property name="leftMargin">
1619 <number>6</number>
1620 </property>
1621 <property name="topMargin">
1622 <number>0</number>
1623 </property>
1624 <property name="rightMargin">
1625 <number>6</number>
1626 </property>
1627 <property name="bottomMargin">
1628 <number>6</number>
1629 </property>
1630 <item>
1631 <widget class="QWidget" name="player6LeftJoycon" native="true">
1632 <layout class="QVBoxLayout" name="buttonMiscButtonsLeftJoyconVerticalLayout_11">
1633 <property name="spacing">
1634 <number>0</number>
1635 </property>
1636 <property name="leftMargin">
1637 <number>0</number>
1638 </property>
1639 <property name="topMargin">
1640 <number>0</number>
1641 </property>
1642 <property name="rightMargin">
1643 <number>0</number>
1644 </property>
1645 <property name="bottomMargin">
1646 <number>0</number>
1647 </property>
1648 <item alignment="Qt::AlignHCenter">
1649 <widget class="QGroupBox" name="player6LeftBodyGroup">
1650 <property name="title">
1651 <string>L Body</string>
1652 </property>
1653 <property name="alignment">
1654 <set>Qt::AlignCenter</set>
1655 </property>
1656 <layout class="QVBoxLayout" name="verticalLayout_54">
1657 <property name="spacing">
1658 <number>3</number>
1659 </property>
1660 <property name="leftMargin">
1661 <number>3</number>
1662 </property>
1663 <property name="topMargin">
1664 <number>3</number>
1665 </property>
1666 <property name="rightMargin">
1667 <number>3</number>
1668 </property>
1669 <property name="bottomMargin">
1670 <number>3</number>
1671 </property>
1672 <item>
1673 <widget class="QPushButton" name="player6_left_body_button">
1674 <property name="sizePolicy">
1675 <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
1676 <horstretch>0</horstretch>
1677 <verstretch>0</verstretch>
1678 </sizepolicy>
1679 </property>
1680 <property name="minimumSize">
1681 <size>
1682 <width>57</width>
1683 <height>0</height>
1684 </size>
1685 </property>
1686 <property name="maximumSize">
1687 <size>
1688 <width>55</width>
1689 <height>16777215</height>
1690 </size>
1691 </property>
1692 <property name="styleSheet">
1693 <string notr="true">min-width: 55px;</string>
1694 </property>
1695 <property name="text">
1696 <string/>
1697 </property>
1698 </widget>
1699 </item>
1700 </layout>
1701 </widget>
1702 </item>
1703 <item alignment="Qt::AlignHCenter">
1704 <widget class="QGroupBox" name="player6LeftButtonsGroup">
1705 <property name="title">
1706 <string>L Button</string>
1707 </property>
1708 <property name="alignment">
1709 <set>Qt::AlignCenter</set>
1710 </property>
1711 <layout class="QVBoxLayout" name="verticalLayout_55">
1712 <property name="spacing">
1713 <number>3</number>
1714 </property>
1715 <property name="leftMargin">
1716 <number>3</number>
1717 </property>
1718 <property name="topMargin">
1719 <number>3</number>
1720 </property>
1721 <property name="rightMargin">
1722 <number>3</number>
1723 </property>
1724 <property name="bottomMargin">
1725 <number>3</number>
1726 </property>
1727 <item>
1728 <widget class="QPushButton" name="player6_left_buttons_button">
1729 <property name="sizePolicy">
1730 <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
1731 <horstretch>0</horstretch>
1732 <verstretch>0</verstretch>
1733 </sizepolicy>
1734 </property>
1735 <property name="minimumSize">
1736 <size>
1737 <width>57</width>
1738 <height>0</height>
1739 </size>
1740 </property>
1741 <property name="maximumSize">
1742 <size>
1743 <width>55</width>
1744 <height>16777215</height>
1745 </size>
1746 </property>
1747 <property name="styleSheet">
1748 <string notr="true">min-width: 55px;</string>
1749 </property>
1750 <property name="text">
1751 <string/>
1752 </property>
1753 </widget>
1754 </item>
1755 </layout>
1756 </widget>
1757 </item>
1758 </layout>
1759 </widget>
1760 </item>
1761 <item>
1762 <widget class="QWidget" name="player6RightJoycon" native="true">
1763 <layout class="QVBoxLayout" name="buttonMiscButtonsRightJoyconVerticalLayout_11">
1764 <property name="spacing">
1765 <number>0</number>
1766 </property>
1767 <property name="leftMargin">
1768 <number>0</number>
1769 </property>
1770 <property name="topMargin">
1771 <number>0</number>
1772 </property>
1773 <property name="rightMargin">
1774 <number>0</number>
1775 </property>
1776 <property name="bottomMargin">
1777 <number>0</number>
1778 </property>
1779 <item alignment="Qt::AlignHCenter">
1780 <widget class="QGroupBox" name="player6RightBodyGroup">
1781 <property name="title">
1782 <string>R Body</string>
1783 </property>
1784 <property name="alignment">
1785 <set>Qt::AlignCenter</set>
1786 </property>
1787 <layout class="QVBoxLayout" name="verticalLayout_52">
1788 <property name="spacing">
1789 <number>3</number>
1790 </property>
1791 <property name="leftMargin">
1792 <number>3</number>
1793 </property>
1794 <property name="topMargin">
1795 <number>3</number>
1796 </property>
1797 <property name="rightMargin">
1798 <number>3</number>
1799 </property>
1800 <property name="bottomMargin">
1801 <number>3</number>
1802 </property>
1803 <item>
1804 <widget class="QPushButton" name="player6_right_body_button">
1805 <property name="sizePolicy">
1806 <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
1807 <horstretch>0</horstretch>
1808 <verstretch>0</verstretch>
1809 </sizepolicy>
1810 </property>
1811 <property name="minimumSize">
1812 <size>
1813 <width>57</width>
1814 <height>0</height>
1815 </size>
1816 </property>
1817 <property name="maximumSize">
1818 <size>
1819 <width>55</width>
1820 <height>16777215</height>
1821 </size>
1822 </property>
1823 <property name="styleSheet">
1824 <string notr="true">min-width: 55px;</string>
1825 </property>
1826 <property name="text">
1827 <string/>
1828 </property>
1829 </widget>
1830 </item>
1831 </layout>
1832 </widget>
1833 </item>
1834 <item alignment="Qt::AlignHCenter">
1835 <widget class="QGroupBox" name="player6RightButtonsGroup">
1836 <property name="title">
1837 <string>R Button</string>
1838 </property>
1839 <property name="alignment">
1840 <set>Qt::AlignCenter</set>
1841 </property>
1842 <layout class="QVBoxLayout" name="verticalLayout_53">
1843 <property name="spacing">
1844 <number>3</number>
1845 </property>
1846 <property name="leftMargin">
1847 <number>3</number>
1848 </property>
1849 <property name="topMargin">
1850 <number>3</number>
1851 </property>
1852 <property name="rightMargin">
1853 <number>3</number>
1854 </property>
1855 <property name="bottomMargin">
1856 <number>3</number>
1857 </property>
1858 <item>
1859 <widget class="QPushButton" name="player6_right_buttons_button">
1860 <property name="sizePolicy">
1861 <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
1862 <horstretch>0</horstretch>
1863 <verstretch>0</verstretch>
1864 </sizepolicy>
1865 </property>
1866 <property name="minimumSize">
1867 <size>
1868 <width>57</width>
1869 <height>0</height>
1870 </size>
1871 </property>
1872 <property name="maximumSize">
1873 <size>
1874 <width>55</width>
1875 <height>16777215</height>
1876 </size>
1877 </property>
1878 <property name="styleSheet">
1879 <string notr="true">min-width: 55px;</string>
1880 </property>
1881 <property name="text">
1882 <string/>
1883 </property>
1884 </widget>
1885 </item>
1886 </layout>
1887 </widget>
1888 </item>
1889 </layout>
1890 </widget>
1891 </item>
1892 </layout>
1893 </widget>
1894 </item>
1895 </layout>
1896 </widget>
1897 </item>
1898 <item>
1899 <widget class="QWidget" name="player78Widget" native="true">
1900 <layout class="QHBoxLayout" name="horizontalLayout_7">
1901 <property name="leftMargin">
1902 <number>0</number>
1903 </property>
1904 <property name="topMargin">
1905 <number>0</number>
1906 </property>
1907 <property name="rightMargin">
1908 <number>0</number>
1909 </property>
1910 <property name="bottomMargin">
1911 <number>0</number>
1912 </property>
1913 <item>
1914 <widget class="QGroupBox" name="player7Group">
1915 <property name="title">
1916 <string>Player 7</string>
1917 </property>
1918 <layout class="QHBoxLayout" name="horizontalLayout_12">
1919 <property name="spacing">
1920 <number>6</number>
1921 </property>
1922 <property name="leftMargin">
1923 <number>6</number>
1924 </property>
1925 <property name="topMargin">
1926 <number>0</number>
1927 </property>
1928 <property name="rightMargin">
1929 <number>6</number>
1930 </property>
1931 <property name="bottomMargin">
1932 <number>6</number>
1933 </property>
1934 <item>
1935 <widget class="QWidget" name="player7LeftJoycon" native="true">
1936 <layout class="QVBoxLayout" name="buttonMiscButtonsLeftJoyconVerticalLayout_12">
1937 <property name="spacing">
1938 <number>0</number>
1939 </property>
1940 <property name="leftMargin">
1941 <number>0</number>
1942 </property>
1943 <property name="topMargin">
1944 <number>0</number>
1945 </property>
1946 <property name="rightMargin">
1947 <number>0</number>
1948 </property>
1949 <property name="bottomMargin">
1950 <number>0</number>
1951 </property>
1952 <item alignment="Qt::AlignHCenter">
1953 <widget class="QGroupBox" name="player7LeftBodyGroup">
1954 <property name="title">
1955 <string>L Body</string>
1956 </property>
1957 <property name="alignment">
1958 <set>Qt::AlignCenter</set>
1959 </property>
1960 <layout class="QVBoxLayout" name="verticalLayout_58">
1961 <property name="spacing">
1962 <number>3</number>
1963 </property>
1964 <property name="leftMargin">
1965 <number>3</number>
1966 </property>
1967 <property name="topMargin">
1968 <number>3</number>
1969 </property>
1970 <property name="rightMargin">
1971 <number>3</number>
1972 </property>
1973 <property name="bottomMargin">
1974 <number>3</number>
1975 </property>
1976 <item>
1977 <widget class="QPushButton" name="player7_left_body_button">
1978 <property name="sizePolicy">
1979 <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
1980 <horstretch>0</horstretch>
1981 <verstretch>0</verstretch>
1982 </sizepolicy>
1983 </property>
1984 <property name="minimumSize">
1985 <size>
1986 <width>57</width>
1987 <height>0</height>
1988 </size>
1989 </property>
1990 <property name="maximumSize">
1991 <size>
1992 <width>55</width>
1993 <height>16777215</height>
1994 </size>
1995 </property>
1996 <property name="styleSheet">
1997 <string notr="true">min-width: 55px;</string>
1998 </property>
1999 <property name="text">
2000 <string/>
2001 </property>
2002 </widget>
2003 </item>
2004 </layout>
2005 </widget>
2006 </item>
2007 <item alignment="Qt::AlignHCenter">
2008 <widget class="QGroupBox" name="player7LeftButtonsGroup">
2009 <property name="title">
2010 <string>L Button</string>
2011 </property>
2012 <property name="alignment">
2013 <set>Qt::AlignCenter</set>
2014 </property>
2015 <layout class="QVBoxLayout" name="verticalLayout_59">
2016 <property name="spacing">
2017 <number>3</number>
2018 </property>
2019 <property name="leftMargin">
2020 <number>3</number>
2021 </property>
2022 <property name="topMargin">
2023 <number>3</number>
2024 </property>
2025 <property name="rightMargin">
2026 <number>3</number>
2027 </property>
2028 <property name="bottomMargin">
2029 <number>3</number>
2030 </property>
2031 <item>
2032 <widget class="QPushButton" name="player7_left_buttons_button">
2033 <property name="sizePolicy">
2034 <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
2035 <horstretch>0</horstretch>
2036 <verstretch>0</verstretch>
2037 </sizepolicy>
2038 </property>
2039 <property name="minimumSize">
2040 <size>
2041 <width>57</width>
2042 <height>0</height>
2043 </size>
2044 </property>
2045 <property name="maximumSize">
2046 <size>
2047 <width>55</width>
2048 <height>16777215</height>
2049 </size>
2050 </property>
2051 <property name="styleSheet">
2052 <string notr="true">min-width: 55px;</string>
2053 </property>
2054 <property name="text">
2055 <string/>
2056 </property>
2057 </widget>
2058 </item>
2059 </layout>
2060 </widget>
2061 </item>
2062 </layout>
2063 </widget>
2064 </item>
2065 <item>
2066 <widget class="QWidget" name="player7RightJoycon" native="true">
2067 <layout class="QVBoxLayout" name="buttonMiscButtonsRightJoyconVerticalLayout_12">
2068 <property name="spacing">
2069 <number>0</number>
2070 </property>
2071 <property name="leftMargin">
2072 <number>0</number>
2073 </property>
2074 <property name="topMargin">
2075 <number>0</number>
2076 </property>
2077 <property name="rightMargin">
2078 <number>0</number>
2079 </property>
2080 <property name="bottomMargin">
2081 <number>0</number>
2082 </property>
2083 <item alignment="Qt::AlignHCenter">
2084 <widget class="QGroupBox" name="player7RightBodyGroup">
2085 <property name="title">
2086 <string>R Body</string>
2087 </property>
2088 <property name="alignment">
2089 <set>Qt::AlignCenter</set>
2090 </property>
2091 <layout class="QVBoxLayout" name="verticalLayout_56">
2092 <property name="spacing">
2093 <number>3</number>
2094 </property>
2095 <property name="leftMargin">
2096 <number>3</number>
2097 </property>
2098 <property name="topMargin">
2099 <number>3</number>
2100 </property>
2101 <property name="rightMargin">
2102 <number>3</number>
2103 </property>
2104 <property name="bottomMargin">
2105 <number>3</number>
2106 </property>
2107 <item>
2108 <widget class="QPushButton" name="player7_right_body_button">
2109 <property name="sizePolicy">
2110 <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
2111 <horstretch>0</horstretch>
2112 <verstretch>0</verstretch>
2113 </sizepolicy>
2114 </property>
2115 <property name="minimumSize">
2116 <size>
2117 <width>57</width>
2118 <height>0</height>
2119 </size>
2120 </property>
2121 <property name="maximumSize">
2122 <size>
2123 <width>55</width>
2124 <height>16777215</height>
2125 </size>
2126 </property>
2127 <property name="styleSheet">
2128 <string notr="true">min-width: 55px;</string>
2129 </property>
2130 <property name="text">
2131 <string/>
2132 </property>
2133 </widget>
2134 </item>
2135 </layout>
2136 </widget>
2137 </item>
2138 <item alignment="Qt::AlignHCenter">
2139 <widget class="QGroupBox" name="player7RightButtonsGroup">
2140 <property name="title">
2141 <string>R Button</string>
2142 </property>
2143 <property name="alignment">
2144 <set>Qt::AlignCenter</set>
2145 </property>
2146 <layout class="QVBoxLayout" name="verticalLayout_57">
2147 <property name="spacing">
2148 <number>3</number>
2149 </property>
2150 <property name="leftMargin">
2151 <number>3</number>
2152 </property>
2153 <property name="topMargin">
2154 <number>3</number>
2155 </property>
2156 <property name="rightMargin">
2157 <number>3</number>
2158 </property>
2159 <property name="bottomMargin">
2160 <number>3</number>
2161 </property>
2162 <item>
2163 <widget class="QPushButton" name="player7_right_buttons_button">
2164 <property name="sizePolicy">
2165 <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
2166 <horstretch>0</horstretch>
2167 <verstretch>0</verstretch>
2168 </sizepolicy>
2169 </property>
2170 <property name="minimumSize">
2171 <size>
2172 <width>57</width>
2173 <height>0</height>
2174 </size>
2175 </property>
2176 <property name="maximumSize">
2177 <size>
2178 <width>55</width>
2179 <height>16777215</height>
2180 </size>
2181 </property>
2182 <property name="styleSheet">
2183 <string notr="true">min-width: 55px;</string>
2184 </property>
2185 <property name="text">
2186 <string/>
2187 </property>
2188 </widget>
2189 </item>
2190 </layout>
2191 </widget>
2192 </item>
2193 </layout>
2194 </widget>
2195 </item>
2196 </layout>
2197 </widget>
2198 </item>
2199 <item>
2200 <widget class="QGroupBox" name="player8Group">
2201 <property name="title">
2202 <string>Player 8</string>
2203 </property>
2204 <layout class="QHBoxLayout" name="horizontalLayout_13">
2205 <property name="spacing">
2206 <number>6</number>
2207 </property>
2208 <property name="leftMargin">
2209 <number>6</number>
2210 </property>
2211 <property name="topMargin">
2212 <number>0</number>
2213 </property>
2214 <property name="rightMargin">
2215 <number>6</number>
2216 </property>
2217 <property name="bottomMargin">
2218 <number>6</number>
2219 </property>
2220 <item>
2221 <widget class="QWidget" name="player8LeftJoycon" native="true">
2222 <layout class="QVBoxLayout" name="buttonMiscButtonsLeftJoyconVerticalLayout_13">
2223 <property name="spacing">
2224 <number>0</number>
2225 </property>
2226 <property name="leftMargin">
2227 <number>0</number>
2228 </property>
2229 <property name="topMargin">
2230 <number>0</number>
2231 </property>
2232 <property name="rightMargin">
2233 <number>0</number>
2234 </property>
2235 <property name="bottomMargin">
2236 <number>0</number>
2237 </property>
2238 <item alignment="Qt::AlignHCenter">
2239 <widget class="QGroupBox" name="player8LeftBodyGroup">
2240 <property name="title">
2241 <string>L Body</string>
2242 </property>
2243 <property name="alignment">
2244 <set>Qt::AlignCenter</set>
2245 </property>
2246 <layout class="QVBoxLayout" name="verticalLayout_62">
2247 <property name="spacing">
2248 <number>3</number>
2249 </property>
2250 <property name="leftMargin">
2251 <number>3</number>
2252 </property>
2253 <property name="topMargin">
2254 <number>3</number>
2255 </property>
2256 <property name="rightMargin">
2257 <number>3</number>
2258 </property>
2259 <property name="bottomMargin">
2260 <number>3</number>
2261 </property>
2262 <item>
2263 <widget class="QPushButton" name="player8_left_body_button">
2264 <property name="sizePolicy">
2265 <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
2266 <horstretch>0</horstretch>
2267 <verstretch>0</verstretch>
2268 </sizepolicy>
2269 </property>
2270 <property name="minimumSize">
2271 <size>
2272 <width>57</width>
2273 <height>0</height>
2274 </size>
2275 </property>
2276 <property name="maximumSize">
2277 <size>
2278 <width>55</width>
2279 <height>16777215</height>
2280 </size>
2281 </property>
2282 <property name="styleSheet">
2283 <string notr="true">min-width: 55px;</string>
2284 </property>
2285 <property name="text">
2286 <string/>
2287 </property>
2288 </widget>
2289 </item>
2290 </layout>
2291 </widget>
2292 </item>
2293 <item alignment="Qt::AlignHCenter">
2294 <widget class="QGroupBox" name="player8LeftButtonsGroup">
2295 <property name="title">
2296 <string>L Button</string>
2297 </property>
2298 <property name="alignment">
2299 <set>Qt::AlignCenter</set>
2300 </property>
2301 <layout class="QVBoxLayout" name="verticalLayout_63">
2302 <property name="spacing">
2303 <number>3</number>
2304 </property>
2305 <property name="leftMargin">
2306 <number>3</number>
2307 </property>
2308 <property name="topMargin">
2309 <number>3</number>
2310 </property>
2311 <property name="rightMargin">
2312 <number>3</number>
2313 </property>
2314 <property name="bottomMargin">
2315 <number>3</number>
2316 </property>
2317 <item>
2318 <widget class="QPushButton" name="player8_left_buttons_button">
2319 <property name="sizePolicy">
2320 <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
2321 <horstretch>0</horstretch>
2322 <verstretch>0</verstretch>
2323 </sizepolicy>
2324 </property>
2325 <property name="minimumSize">
2326 <size>
2327 <width>57</width>
2328 <height>0</height>
2329 </size>
2330 </property>
2331 <property name="maximumSize">
2332 <size>
2333 <width>55</width>
2334 <height>16777215</height>
2335 </size>
2336 </property>
2337 <property name="styleSheet">
2338 <string notr="true">min-width: 55px;</string>
2339 </property>
2340 <property name="text">
2341 <string/>
2342 </property>
2343 </widget>
2344 </item>
2345 </layout>
2346 </widget>
2347 </item>
2348 </layout>
2349 </widget>
2350 </item>
2351 <item>
2352 <widget class="QWidget" name="player8RightJoycon" native="true">
2353 <layout class="QVBoxLayout" name="buttonMiscButtonsRightJoyconVerticalLayout_13">
2354 <property name="spacing">
2355 <number>0</number>
2356 </property>
2357 <property name="leftMargin">
2358 <number>0</number>
2359 </property>
2360 <property name="topMargin">
2361 <number>0</number>
2362 </property>
2363 <property name="rightMargin">
2364 <number>0</number>
2365 </property>
2366 <property name="bottomMargin">
2367 <number>0</number>
2368 </property>
2369 <item alignment="Qt::AlignHCenter">
2370 <widget class="QGroupBox" name="player8RightBodyGroup">
2371 <property name="title">
2372 <string>R Body</string>
2373 </property>
2374 <property name="alignment">
2375 <set>Qt::AlignCenter</set>
2376 </property>
2377 <layout class="QVBoxLayout" name="verticalLayout_60">
2378 <property name="spacing">
2379 <number>3</number>
2380 </property>
2381 <property name="leftMargin">
2382 <number>3</number>
2383 </property>
2384 <property name="topMargin">
2385 <number>3</number>
2386 </property>
2387 <property name="rightMargin">
2388 <number>3</number>
2389 </property>
2390 <property name="bottomMargin">
2391 <number>3</number>
2392 </property>
2393 <item>
2394 <widget class="QPushButton" name="player8_right_body_button">
2395 <property name="sizePolicy">
2396 <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
2397 <horstretch>0</horstretch>
2398 <verstretch>0</verstretch>
2399 </sizepolicy>
2400 </property>
2401 <property name="minimumSize">
2402 <size>
2403 <width>57</width>
2404 <height>0</height>
2405 </size>
2406 </property>
2407 <property name="maximumSize">
2408 <size>
2409 <width>55</width>
2410 <height>16777215</height>
2411 </size>
2412 </property>
2413 <property name="styleSheet">
2414 <string notr="true">min-width: 55px;</string>
2415 </property>
2416 <property name="text">
2417 <string/>
2418 </property>
2419 </widget>
2420 </item>
2421 </layout>
2422 </widget>
2423 </item>
2424 <item alignment="Qt::AlignHCenter">
2425 <widget class="QGroupBox" name="player8RightButtonsGroup">
2426 <property name="title">
2427 <string>R Button</string>
2428 </property>
2429 <property name="alignment">
2430 <set>Qt::AlignCenter</set>
2431 </property>
2432 <layout class="QVBoxLayout" name="verticalLayout_61">
2433 <property name="spacing">
2434 <number>3</number>
2435 </property>
2436 <property name="leftMargin">
2437 <number>3</number>
2438 </property>
2439 <property name="topMargin">
2440 <number>3</number>
2441 </property>
2442 <property name="rightMargin">
2443 <number>3</number>
2444 </property>
2445 <property name="bottomMargin">
2446 <number>3</number>
2447 </property>
2448 <item>
2449 <widget class="QPushButton" name="player8_right_buttons_button">
2450 <property name="sizePolicy">
2451 <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
2452 <horstretch>0</horstretch>
2453 <verstretch>0</verstretch>
2454 </sizepolicy>
2455 </property>
2456 <property name="minimumSize">
2457 <size>
2458 <width>57</width>
2459 <height>0</height>
2460 </size>
2461 </property>
2462 <property name="maximumSize">
2463 <size>
2464 <width>55</width>
2465 <height>16777215</height>
2466 </size>
2467 </property>
2468 <property name="styleSheet">
2469 <string notr="true">min-width: 55px;</string>
2470 </property>
2471 <property name="text">
2472 <string/>
2473 </property>
2474 </widget>
2475 </item>
2476 </layout>
2477 </widget>
2478 </item>
2479 </layout>
2480 </widget>
2481 </item>
2482 </layout>
2483 </widget>
2484 </item>
2485 </layout>
2486 </widget>
2487 </item>
2488 </layout>
2489 </widget>
2490 </item>
2491 </layout>
2492 </widget>
2493 </item>
2494 </layout>
2495 </widget>
2496 </item>
2497 <item>
2498 <widget class="QWidget" name="rightInputAdvanced" native="true">
2499 <layout class="QVBoxLayout" name="rightLayout" stretch="1,1">
2500 <property name="spacing">
2501 <number>3</number>
2502 </property>
2503 <property name="leftMargin">
2504 <number>0</number>
2505 </property>
2506 <property name="topMargin">
2507 <number>0</number>
2508 </property>
2509 <property name="rightMargin">
2510 <number>0</number>
2511 </property>
2512 <property name="bottomMargin">
2513 <number>0</number>
2514 </property>
2515 <item>
2516 <widget class="QWidget" name="topRightInputAdvanced" native="true">
2517 <layout class="QVBoxLayout" name="verticalLayout">
2518 <property name="leftMargin">
2519 <number>0</number>
2520 </property>
2521 <property name="topMargin">
2522 <number>0</number>
2523 </property>
2524 <property name="rightMargin">
2525 <number>0</number>
2526 </property>
2527 <property name="bottomMargin">
2528 <number>0</number>
2529 </property>
2530 <item>
2531 <widget class="QGroupBox" name="gridGroupBox_3">
2532 <property name="title">
2533 <string>Other</string>
2534 </property>
2535 <layout class="QGridLayout" name="gridLayout_3">
2536 <item row="0" column="0">
2537 <widget class="QCheckBox" name="keyboard_enabled">
2538 <property name="minimumSize">
2539 <size>
2540 <width>0</width>
2541 <height>23</height>
2542 </size>
2543 </property>
2544 <property name="text">
2545 <string>Keyboard</string>
2546 </property>
2547 </widget>
2548 </item>
2549 <item row="4" column="2">
2550 <widget class="QPushButton" name="touchscreen_advanced">
2551 <property name="text">
2552 <string>Advanced</string>
2553 </property>
2554 </widget>
2555 </item>
2556 <item row="1" column="1">
2557 <spacer name="horizontalSpacer_8">
2558 <property name="orientation">
2559 <enum>Qt::Horizontal</enum>
2560 </property>
2561 <property name="sizeType">
2562 <enum>QSizePolicy::Fixed</enum>
2563 </property>
2564 <property name="sizeHint" stdset="0">
2565 <size>
2566 <width>76</width>
2567 <height>20</height>
2568 </size>
2569 </property>
2570 </spacer>
2571 </item>
2572 <item row="1" column="2">
2573 <widget class="QPushButton" name="mouse_advanced">
2574 <property name="text">
2575 <string>Advanced</string>
2576 </property>
2577 </widget>
2578 </item>
2579 <item row="4" column="0">
2580 <widget class="QCheckBox" name="touchscreen_enabled">
2581 <property name="text">
2582 <string>Touchscreen</string>
2583 </property>
2584 </widget>
2585 </item>
2586 <item row="1" column="0">
2587 <widget class="QCheckBox" name="mouse_enabled">
2588 <property name="minimumSize">
2589 <size>
2590 <width>0</width>
2591 <height>23</height>
2592 </size>
2593 </property>
2594 <property name="text">
2595 <string>Mouse</string>
2596 </property>
2597 </widget>
2598 </item>
2599 <item row="6" column="0">
2600 <widget class="QLabel" name="motion_touch">
2601 <property name="text">
2602 <string>Motion / Touch</string>
2603 </property>
2604 </widget>
2605 </item>
2606 <item row="6" column="2">
2607 <widget class="QPushButton" name="buttonMotionTouch">
2608 <property name="text">
2609 <string>Configure</string>
2610 </property>
2611 </widget>
2612 </item>
2613 <item row="5" column="0">
2614 <widget class="QCheckBox" name="debug_enabled">
2615 <property name="text">
2616 <string>Debug Controller</string>
2617 </property>
2618 </widget>
2619 </item>
2620 <item row="5" column="2">
2621 <widget class="QPushButton" name="debug_configure">
2622 <property name="text">
2623 <string>Configure</string>
2624 </property>
2625 </widget>
2626 </item>
2627 </layout>
2628 </widget>
2629 </item>
2630 <item>
2631 <spacer name="verticalSpacer">
2632 <property name="orientation">
2633 <enum>Qt::Vertical</enum>
2634 </property>
2635 <property name="sizeHint" stdset="0">
2636 <size>
2637 <width>20</width>
2638 <height>40</height>
2639 </size>
2640 </property>
2641 </spacer>
2642 </item>
2643 </layout>
2644 </widget>
2645 </item>
2646 <item>
2647 <widget class="QWidget" name="bottomRightInputAdvanced" native="true">
2648 <layout class="QVBoxLayout" name="verticalLayout_2">
2649 <property name="leftMargin">
2650 <number>0</number>
2651 </property>
2652 <property name="topMargin">
2653 <number>0</number>
2654 </property>
2655 <property name="rightMargin">
2656 <number>0</number>
2657 </property>
2658 <property name="bottomMargin">
2659 <number>0</number>
2660 </property>
2661 <item>
2662 <spacer name="mainVerticalSpacer">
2663 <property name="orientation">
2664 <enum>Qt::Vertical</enum>
2665 </property>
2666 <property name="sizeHint" stdset="0">
2667 <size>
2668 <width>20</width>
2669 <height>40</height>
2670 </size>
2671 </property>
2672 </spacer>
2673 </item>
2674 </layout>
2675 </widget>
2676 </item>
2677 </layout>
2678 </widget>
2679 </item>
2680 </layout>
2681 </widget>
2682 </item>
2683 </layout>
2684 </widget>
2685 <resources>
2686 </resources>
2687 <connections/>
2688</ui>
diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp
index 597defe8c..13ecb3dc5 100644
--- a/src/yuzu/configuration/configure_input_player.cpp
+++ b/src/yuzu/configuration/configure_input_player.cpp
@@ -5,39 +5,97 @@
5#include <algorithm> 5#include <algorithm>
6#include <memory> 6#include <memory>
7#include <utility> 7#include <utility>
8#include <QColorDialog>
9#include <QGridLayout> 8#include <QGridLayout>
9#include <QInputDialog>
10#include <QKeyEvent> 10#include <QKeyEvent>
11#include <QMenu> 11#include <QMenu>
12#include <QMessageBox> 12#include <QMessageBox>
13#include <QTimer> 13#include <QTimer>
14#include "common/assert.h"
15#include "common/param_package.h" 14#include "common/param_package.h"
15#include "core/core.h"
16#include "core/hle/service/hid/controllers/npad.h"
17#include "core/hle/service/hid/hid.h"
18#include "core/hle/service/sm/sm.h"
19#include "input_common/gcadapter/gc_poller.h"
16#include "input_common/main.h" 20#include "input_common/main.h"
17#include "ui_configure_input_player.h" 21#include "ui_configure_input_player.h"
18#include "yuzu/configuration/config.h" 22#include "yuzu/configuration/config.h"
19#include "yuzu/configuration/configure_input_player.h" 23#include "yuzu/configuration/configure_input_player.h"
20 24
25constexpr std::size_t HANDHELD_INDEX = 8;
26
21const std::array<std::string, ConfigureInputPlayer::ANALOG_SUB_BUTTONS_NUM> 27const std::array<std::string, ConfigureInputPlayer::ANALOG_SUB_BUTTONS_NUM>
22 ConfigureInputPlayer::analog_sub_buttons{{ 28 ConfigureInputPlayer::analog_sub_buttons{{
23 "up", 29 "up",
24 "down", 30 "down",
25 "left", 31 "left",
26 "right", 32 "right",
27 "modifier",
28 }}; 33 }};
29 34
30static void LayerGridElements(QGridLayout* grid, QWidget* item, QWidget* onTopOf) { 35namespace {
31 const int index1 = grid->indexOf(item); 36
32 const int index2 = grid->indexOf(onTopOf); 37void UpdateController(Settings::ControllerType controller_type, std::size_t npad_index,
33 int row, column, rowSpan, columnSpan; 38 bool connected) {
34 grid->getItemPosition(index2, &row, &column, &rowSpan, &columnSpan); 39 Core::System& system{Core::System::GetInstance()};
35 grid->takeAt(index1); 40 if (!system.IsPoweredOn()) {
36 grid->addWidget(item, row, column, rowSpan, columnSpan); 41 return;
42 }
43 Service::SM::ServiceManager& sm = system.ServiceManager();
44
45 auto& npad =
46 sm.GetService<Service::HID::Hid>("hid")
47 ->GetAppletResource()
48 ->GetController<Service::HID::Controller_NPad>(Service::HID::HidController::NPad);
49
50 npad.UpdateControllerAt(npad.MapSettingsTypeToNPad(controller_type), npad_index, connected);
51}
52
53/// Maps the controller type combobox index to Controller Type enum
54constexpr Settings::ControllerType GetControllerTypeFromIndex(int index) {
55 switch (index) {
56 case 0:
57 default:
58 return Settings::ControllerType::ProController;
59 case 1:
60 return Settings::ControllerType::DualJoyconDetached;
61 case 2:
62 return Settings::ControllerType::LeftJoycon;
63 case 3:
64 return Settings::ControllerType::RightJoycon;
65 case 4:
66 return Settings::ControllerType::Handheld;
67 }
68}
69
70/// Maps the Controller Type enum to controller type combobox index
71constexpr int GetIndexFromControllerType(Settings::ControllerType type) {
72 switch (type) {
73 case Settings::ControllerType::ProController:
74 default:
75 return 0;
76 case Settings::ControllerType::DualJoyconDetached:
77 return 1;
78 case Settings::ControllerType::LeftJoycon:
79 return 2;
80 case Settings::ControllerType::RightJoycon:
81 return 3;
82 case Settings::ControllerType::Handheld:
83 return 4;
84 }
37} 85}
38 86
39static QString GetKeyName(int key_code) { 87QString GetKeyName(int key_code) {
40 switch (key_code) { 88 switch (key_code) {
89 case Qt::LeftButton:
90 return QObject::tr("Click 0");
91 case Qt::RightButton:
92 return QObject::tr("Click 1");
93 case Qt::MiddleButton:
94 return QObject::tr("Click 2");
95 case Qt::BackButton:
96 return QObject::tr("Click 3");
97 case Qt::ForwardButton:
98 return QObject::tr("Click 4");
41 case Qt::Key_Shift: 99 case Qt::Key_Shift:
42 return QObject::tr("Shift"); 100 return QObject::tr("Shift");
43 case Qt::Key_Control: 101 case Qt::Key_Control:
@@ -51,9 +109,16 @@ static QString GetKeyName(int key_code) {
51 } 109 }
52} 110}
53 111
54static void SetAnalogButton(const Common::ParamPackage& input_param, 112void SetAnalogParam(const Common::ParamPackage& input_param, Common::ParamPackage& analog_param,
55 Common::ParamPackage& analog_param, const std::string& button_name) { 113 const std::string& button_name) {
56 if (analog_param.Get("engine", "") != "analog_from_button") { 114 // The poller returned a complete axis, so set all the buttons
115 if (input_param.Has("axis_x") && input_param.Has("axis_y")) {
116 analog_param = input_param;
117 return;
118 }
119 // Check if the current configuration has either no engine or an axis binding.
120 // Clears out the old binding and adds one with analog_from_button.
121 if (!analog_param.Has("engine") || analog_param.Has("axis_x") || analog_param.Has("axis_y")) {
57 analog_param = { 122 analog_param = {
58 {"engine", "analog_from_button"}, 123 {"engine", "analog_from_button"},
59 }; 124 };
@@ -61,7 +126,7 @@ static void SetAnalogButton(const Common::ParamPackage& input_param,
61 analog_param.Set(button_name, input_param.Serialize()); 126 analog_param.Set(button_name, input_param.Serialize());
62} 127}
63 128
64static QString ButtonToText(const Common::ParamPackage& param) { 129QString ButtonToText(const Common::ParamPackage& param) {
65 if (!param.Has("engine")) { 130 if (!param.Has("engine")) {
66 return QObject::tr("[not set]"); 131 return QObject::tr("[not set]");
67 } 132 }
@@ -111,7 +176,7 @@ static QString ButtonToText(const Common::ParamPackage& param) {
111 return QObject::tr("[unknown]"); 176 return QObject::tr("[unknown]");
112} 177}
113 178
114static QString AnalogToText(const Common::ParamPackage& param, const std::string& dir) { 179QString AnalogToText(const Common::ParamPackage& param, const std::string& dir) {
115 if (!param.Has("engine")) { 180 if (!param.Has("engine")) {
116 return QObject::tr("[not set]"); 181 return QObject::tr("[not set]");
117 } 182 }
@@ -161,22 +226,25 @@ static QString AnalogToText(const Common::ParamPackage& param, const std::string
161 } 226 }
162 return QObject::tr("[unknown]"); 227 return QObject::tr("[unknown]");
163} 228}
164 229} // namespace
165ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_index, bool debug) 230
166 : QDialog(parent), ui(std::make_unique<Ui::ConfigureInputPlayer>()), player_index(player_index), 231ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_index,
167 debug(debug), timeout_timer(std::make_unique<QTimer>()), 232 QWidget* bottom_row,
168 poll_timer(std::make_unique<QTimer>()) { 233 InputCommon::InputSubsystem* input_subsystem_,
234 bool debug)
235 : QWidget(parent), ui(std::make_unique<Ui::ConfigureInputPlayer>()), player_index(player_index),
236 debug(debug), input_subsystem{input_subsystem_}, timeout_timer(std::make_unique<QTimer>()),
237 poll_timer(std::make_unique<QTimer>()), bottom_row(bottom_row) {
169 ui->setupUi(this); 238 ui->setupUi(this);
239
170 setFocusPolicy(Qt::ClickFocus); 240 setFocusPolicy(Qt::ClickFocus);
171 241
172 button_map = { 242 button_map = {
173 ui->buttonA, ui->buttonB, ui->buttonX, ui->buttonY, 243 ui->buttonA, ui->buttonB, ui->buttonX, ui->buttonY,
174 ui->buttonLStick, ui->buttonRStick, ui->buttonL, ui->buttonR, 244 ui->buttonLStick, ui->buttonRStick, ui->buttonL, ui->buttonR,
175 ui->buttonZL, ui->buttonZR, ui->buttonPlus, ui->buttonMinus, 245 ui->buttonZL, ui->buttonZR, ui->buttonPlus, ui->buttonMinus,
176 ui->buttonDpadLeft, ui->buttonDpadUp, ui->buttonDpadRight, ui->buttonDpadDown, 246 ui->buttonDpadLeft, ui->buttonDpadUp, ui->buttonDpadRight, ui->buttonDpadDown,
177 ui->buttonLStickLeft, ui->buttonLStickUp, ui->buttonLStickRight, ui->buttonLStickDown, 247 ui->buttonSL, ui->buttonSR, ui->buttonHome, ui->buttonScreenshot,
178 ui->buttonRStickLeft, ui->buttonRStickUp, ui->buttonRStickRight, ui->buttonRStickDown,
179 ui->buttonSL, ui->buttonSR, ui->buttonHome, ui->buttonScreenshot,
180 }; 248 };
181 249
182 analog_map_buttons = {{ 250 analog_map_buttons = {{
@@ -185,223 +253,165 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
185 ui->buttonLStickDown, 253 ui->buttonLStickDown,
186 ui->buttonLStickLeft, 254 ui->buttonLStickLeft,
187 ui->buttonLStickRight, 255 ui->buttonLStickRight,
188 ui->buttonLStickMod,
189 }, 256 },
190 { 257 {
191 ui->buttonRStickUp, 258 ui->buttonRStickUp,
192 ui->buttonRStickDown, 259 ui->buttonRStickDown,
193 ui->buttonRStickLeft, 260 ui->buttonRStickLeft,
194 ui->buttonRStickRight, 261 ui->buttonRStickRight,
195 ui->buttonRStickMod,
196 }, 262 },
197 }}; 263 }};
198 264
199 debug_hidden = { 265 analog_map_deadzone_label = {ui->labelLStickDeadzone, ui->labelRStickDeadzone};
200 ui->buttonSL, ui->labelSL, 266 analog_map_deadzone_slider = {ui->sliderLStickDeadzone, ui->sliderRStickDeadzone};
201 ui->buttonSR, ui->labelSR, 267 analog_map_modifier_groupbox = {ui->buttonLStickModGroup, ui->buttonRStickModGroup};
202 ui->buttonLStick, ui->labelLStickPressed, 268 analog_map_modifier_label = {ui->labelLStickModifierRange, ui->labelRStickModifierRange};
203 ui->buttonRStick, ui->labelRStickPressed, 269 analog_map_modifier_slider = {ui->sliderLStickModifierRange, ui->sliderRStickModifierRange};
204 ui->buttonHome, ui->labelHome, 270 analog_map_range_groupbox = {ui->buttonLStickRangeGroup, ui->buttonRStickRangeGroup};
205 ui->buttonScreenshot, ui->labelScreenshot, 271 analog_map_range_spinbox = {ui->spinboxLStickRange, ui->spinboxRStickRange};
206 };
207
208 auto layout = Settings::values.players[player_index].type;
209 if (debug)
210 layout = Settings::ControllerType::DualJoycon;
211
212 switch (layout) {
213 case Settings::ControllerType::ProController:
214 case Settings::ControllerType::DualJoycon:
215 layout_hidden = {
216 ui->buttonSL,
217 ui->labelSL,
218 ui->buttonSR,
219 ui->labelSR,
220 };
221 break;
222 case Settings::ControllerType::LeftJoycon:
223 layout_hidden = {
224 ui->right_body_button,
225 ui->right_buttons_button,
226 ui->right_body_label,
227 ui->right_buttons_label,
228 ui->buttonR,
229 ui->labelR,
230 ui->buttonZR,
231 ui->labelZR,
232 ui->labelHome,
233 ui->buttonHome,
234 ui->buttonPlus,
235 ui->labelPlus,
236 ui->RStick,
237 ui->faceButtons,
238 };
239 break;
240 case Settings::ControllerType::RightJoycon:
241 layout_hidden = {
242 ui->left_body_button, ui->left_buttons_button,
243 ui->left_body_label, ui->left_buttons_label,
244 ui->buttonL, ui->labelL,
245 ui->buttonZL, ui->labelZL,
246 ui->labelScreenshot, ui->buttonScreenshot,
247 ui->buttonMinus, ui->labelMinus,
248 ui->LStick, ui->Dpad,
249 };
250 break;
251 }
252
253 if (debug || layout == Settings::ControllerType::ProController) {
254 ui->controller_color->hide();
255 } else {
256 if (layout == Settings::ControllerType::LeftJoycon ||
257 layout == Settings::ControllerType::RightJoycon) {
258 ui->horizontalSpacer_4->setGeometry({0, 0, 0, 0});
259
260 LayerGridElements(ui->buttons, ui->shoulderButtons, ui->Dpad);
261 LayerGridElements(ui->buttons, ui->misc, ui->RStick);
262 LayerGridElements(ui->buttons, ui->Dpad, ui->faceButtons);
263 LayerGridElements(ui->buttons, ui->RStick, ui->LStick);
264 }
265 }
266
267 for (auto* widget : layout_hidden)
268 widget->setVisible(false);
269
270 analog_map_stick = {ui->buttonLStickAnalog, ui->buttonRStickAnalog};
271 analog_map_deadzone_and_modifier_slider = {ui->sliderLStickDeadzoneAndModifier,
272 ui->sliderRStickDeadzoneAndModifier};
273 analog_map_deadzone_and_modifier_slider_label = {ui->labelLStickDeadzoneAndModifier,
274 ui->labelRStickDeadzoneAndModifier};
275 272
276 for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; button_id++) { 273 const auto ConfigureButtonClick = [&](QPushButton* button, Common::ParamPackage* param,
277 auto* const button = button_map[button_id]; 274 int default_val) {
278 if (button == nullptr) {
279 continue;
280 }
281
282 button->setContextMenuPolicy(Qt::CustomContextMenu);
283 connect(button, &QPushButton::clicked, [=, this] { 275 connect(button, &QPushButton::clicked, [=, this] {
284 HandleClick( 276 HandleClick(
285 button_map[button_id], 277 button,
286 [=, this](Common::ParamPackage params) { 278 [=, this](Common::ParamPackage params) {
287 // Workaround for ZL & ZR for analog triggers like on XBOX controllors. 279 // Workaround for ZL & ZR for analog triggers like on XBOX
288 // Analog triggers (from controllers like the XBOX controller) would not 280 // controllers. Analog triggers (from controllers like the XBOX
289 // work due to a different range of their signals (from 0 to 255 on 281 // controller) would not work due to a different range of their
290 // analog triggers instead of -32768 to 32768 on analog joysticks). The 282 // signals (from 0 to 255 on analog triggers instead of -32768 to
291 // SDL driver misinterprets analog triggers as analog joysticks. 283 // 32768 on analog joysticks). The SDL driver misinterprets analog
284 // triggers as analog joysticks.
292 // TODO: reinterpret the signal range for analog triggers to map the 285 // TODO: reinterpret the signal range for analog triggers to map the
293 // values correctly. This is required for the correct emulation of the 286 // values correctly. This is required for the correct emulation of
294 // analog triggers of the GameCube controller. 287 // the analog triggers of the GameCube controller.
295 if (button_id == Settings::NativeButton::ZL || 288 if (button == ui->buttonZL || button == ui->buttonZR) {
296 button_id == Settings::NativeButton::ZR) {
297 params.Set("direction", "+"); 289 params.Set("direction", "+");
298 params.Set("threshold", "0.5"); 290 params.Set("threshold", "0.5");
299 } 291 }
300 buttons_param[button_id] = std::move(params); 292 *param = std::move(params);
301 }, 293 },
302 InputCommon::Polling::DeviceType::Button); 294 InputCommon::Polling::DeviceType::Button);
303 }); 295 });
304 connect(button, &QPushButton::customContextMenuRequested, 296 };
305 [=, this](const QPoint& menu_location) { 297
306 QMenu context_menu; 298 for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; ++button_id) {
307 context_menu.addAction(tr("Clear"), [&] { 299 auto* const button = button_map[button_id];
308 buttons_param[button_id].Clear(); 300 if (button == nullptr) {
309 button_map[button_id]->setText(tr("[not set]")); 301 continue;
310 }); 302 }
311 context_menu.addAction(tr("Restore Default"), [&] { 303 ConfigureButtonClick(button_map[button_id], &buttons_param[button_id],
312 buttons_param[button_id] = Common::ParamPackage{ 304 Config::default_buttons[button_id]);
313 InputCommon::GenerateKeyboardParam(Config::default_buttons[button_id])};
314 button_map[button_id]->setText(ButtonToText(buttons_param[button_id]));
315 });
316 context_menu.exec(button_map[button_id]->mapToGlobal(menu_location));
317 });
318 } 305 }
319 306
320 for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; analog_id++) { 307 // Handle clicks for the modifier buttons as well.
321 for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; sub_button_id++) { 308 ConfigureButtonClick(ui->buttonLStickMod, &lstick_mod, Config::default_stick_mod[0]);
309 ConfigureButtonClick(ui->buttonRStickMod, &rstick_mod, Config::default_stick_mod[1]);
310
311 for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; ++analog_id) {
312 for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; ++sub_button_id) {
322 auto* const analog_button = analog_map_buttons[analog_id][sub_button_id]; 313 auto* const analog_button = analog_map_buttons[analog_id][sub_button_id];
314
323 if (analog_button == nullptr) { 315 if (analog_button == nullptr) {
324 continue; 316 continue;
325 } 317 }
326 318
327 analog_button->setContextMenuPolicy(Qt::CustomContextMenu);
328 connect(analog_button, &QPushButton::clicked, [=, this] { 319 connect(analog_button, &QPushButton::clicked, [=, this] {
329 HandleClick( 320 HandleClick(
330 analog_map_buttons[analog_id][sub_button_id], 321 analog_map_buttons[analog_id][sub_button_id],
331 [=, this](const Common::ParamPackage& params) { 322 [=, this](const Common::ParamPackage& params) {
332 SetAnalogButton(params, analogs_param[analog_id], 323 SetAnalogParam(params, analogs_param[analog_id],
333 analog_sub_buttons[sub_button_id]); 324 analog_sub_buttons[sub_button_id]);
334 }, 325 },
335 InputCommon::Polling::DeviceType::Button); 326 InputCommon::Polling::DeviceType::AnalogPreferred);
336 }); 327 });
337 connect(analog_button, &QPushButton::customContextMenuRequested,
338 [=, this](const QPoint& menu_location) {
339 QMenu context_menu;
340 context_menu.addAction(tr("Clear"), [&] {
341 analogs_param[analog_id].Erase(analog_sub_buttons[sub_button_id]);
342 analog_map_buttons[analog_id][sub_button_id]->setText(tr("[not set]"));
343 });
344 context_menu.addAction(tr("Restore Default"), [&] {
345 Common::ParamPackage params{InputCommon::GenerateKeyboardParam(
346 Config::default_analogs[analog_id][sub_button_id])};
347 SetAnalogButton(params, analogs_param[analog_id],
348 analog_sub_buttons[sub_button_id]);
349 analog_map_buttons[analog_id][sub_button_id]->setText(AnalogToText(
350 analogs_param[analog_id], analog_sub_buttons[sub_button_id]));
351 });
352 context_menu.exec(analog_map_buttons[analog_id][sub_button_id]->mapToGlobal(
353 menu_location));
354 });
355 } 328 }
356 connect(analog_map_stick[analog_id], &QPushButton::clicked, [=, this] {
357 if (QMessageBox::information(
358 this, tr("Information"),
359 tr("After pressing OK, first move your joystick horizontally, "
360 "and then vertically."),
361 QMessageBox::Ok | QMessageBox::Cancel) == QMessageBox::Ok) {
362 HandleClick(
363 analog_map_stick[analog_id],
364 [=, this](const Common::ParamPackage& params) {
365 analogs_param[analog_id] = params;
366 },
367 InputCommon::Polling::DeviceType::Analog);
368 }
369 });
370 329
371 connect(analog_map_deadzone_and_modifier_slider[analog_id], &QSlider::valueChanged, 330 connect(analog_map_range_spinbox[analog_id], qOverload<int>(&QSpinBox::valueChanged),
372 [=, this] { 331 [=, this] {
373 const float slider_value = 332 const auto spinbox_value = analog_map_range_spinbox[analog_id]->value();
374 analog_map_deadzone_and_modifier_slider[analog_id]->value(); 333 analogs_param[analog_id].Set("range", spinbox_value / 100.0f);
375 if (analogs_param[analog_id].Get("engine", "") == "sdl" ||
376 analogs_param[analog_id].Get("engine", "") == "gcpad") {
377 analog_map_deadzone_and_modifier_slider_label[analog_id]->setText(
378 tr("Deadzone: %1%").arg(slider_value));
379 analogs_param[analog_id].Set("deadzone", slider_value / 100.0f);
380 } else {
381 analog_map_deadzone_and_modifier_slider_label[analog_id]->setText(
382 tr("Modifier Scale: %1%").arg(slider_value));
383 analogs_param[analog_id].Set("modifier_scale", slider_value / 100.0f);
384 }
385 }); 334 });
335
336 connect(analog_map_deadzone_slider[analog_id], &QSlider::valueChanged, [=, this] {
337 const auto slider_value = analog_map_deadzone_slider[analog_id]->value();
338 analog_map_deadzone_label[analog_id]->setText(tr("Deadzone: %1%").arg(slider_value));
339 analogs_param[analog_id].Set("deadzone", slider_value / 100.0f);
340 });
341
342 connect(analog_map_modifier_slider[analog_id], &QSlider::valueChanged, [=, this] {
343 const auto slider_value = analog_map_modifier_slider[analog_id]->value();
344 analog_map_modifier_label[analog_id]->setText(
345 tr("Modifier Range: %1%").arg(slider_value));
346 analogs_param[analog_id].Set("modifier_scale", slider_value / 100.0f);
347 });
386 } 348 }
387 349
388 connect(ui->buttonClearAll, &QPushButton::clicked, [this] { ClearAll(); }); 350 // Player Connected checkbox
389 connect(ui->buttonRestoreDefaults, &QPushButton::clicked, [this] { RestoreDefaults(); }); 351 connect(ui->groupConnectedController, &QGroupBox::toggled,
352 [this](bool checked) { emit Connected(checked); });
353
354 // Set up controller type. Only Player 1 can choose Handheld.
355 ui->comboControllerType->clear();
356
357 QStringList controller_types = {
358 tr("Pro Controller"),
359 tr("Dual Joycons"),
360 tr("Left Joycon"),
361 tr("Right Joycon"),
362 };
363
364 if (player_index == 0) {
365 controller_types.append(tr("Handheld"));
366 connect(ui->comboControllerType, qOverload<int>(&QComboBox::currentIndexChanged),
367 [this](int index) {
368 emit HandheldStateChanged(GetControllerTypeFromIndex(index) ==
369 Settings::ControllerType::Handheld);
370 });
371 }
372
373 // The Debug Controller can only choose the Pro Controller.
374 if (debug) {
375 ui->buttonScreenshot->setEnabled(false);
376 ui->buttonHome->setEnabled(false);
377 ui->groupConnectedController->setCheckable(false);
378 QStringList debug_controller_types = {
379 tr("Pro Controller"),
380 };
381 ui->comboControllerType->addItems(debug_controller_types);
382 } else {
383 ui->comboControllerType->addItems(controller_types);
384 }
385
386 UpdateControllerIcon();
387 UpdateControllerAvailableButtons();
388 connect(ui->comboControllerType, qOverload<int>(&QComboBox::currentIndexChanged), [this](int) {
389 UpdateControllerIcon();
390 UpdateControllerAvailableButtons();
391 });
392
393 connect(ui->comboDevices, qOverload<int>(&QComboBox::currentIndexChanged), this,
394 &ConfigureInputPlayer::UpdateMappingWithDefaults);
395
396 ui->buttonRefreshDevices->setIcon(QIcon::fromTheme(QStringLiteral("view-refresh")));
397 UpdateInputDevices();
398 connect(ui->buttonRefreshDevices, &QPushButton::clicked,
399 [this] { emit RefreshInputDevices(); });
390 400
391 timeout_timer->setSingleShot(true); 401 timeout_timer->setSingleShot(true);
392 connect(timeout_timer.get(), &QTimer::timeout, [this] { SetPollingResult({}, true); }); 402 connect(timeout_timer.get(), &QTimer::timeout, [this] { SetPollingResult({}, true); });
393 403
394 connect(poll_timer.get(), &QTimer::timeout, [this] { 404 connect(poll_timer.get(), &QTimer::timeout, [this] {
395 Common::ParamPackage params; 405 Common::ParamPackage params;
396 if (InputCommon::GetGCButtons()->IsPolling()) { 406 if (input_subsystem->GetGCButtons()->IsPolling()) {
397 params = InputCommon::GetGCButtons()->GetNextInput(); 407 params = input_subsystem->GetGCButtons()->GetNextInput();
398 if (params.Has("engine")) { 408 if (params.Has("engine")) {
399 SetPollingResult(params, false); 409 SetPollingResult(params, false);
400 return; 410 return;
401 } 411 }
402 } 412 }
403 if (InputCommon::GetGCAnalogs()->IsPolling()) { 413 if (input_subsystem->GetGCAnalogs()->IsPolling()) {
404 params = InputCommon::GetGCAnalogs()->GetNextInput(); 414 params = input_subsystem->GetGCAnalogs()->GetNextInput();
405 if (params.Has("engine")) { 415 if (params.Has("engine")) {
406 SetPollingResult(params, false); 416 SetPollingResult(params, false);
407 return; 417 return;
@@ -416,20 +426,7 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
416 } 426 }
417 }); 427 });
418 428
419 controller_color_buttons = {
420 ui->left_body_button,
421 ui->left_buttons_button,
422 ui->right_body_button,
423 ui->right_buttons_button,
424 };
425
426 for (std::size_t i = 0; i < controller_color_buttons.size(); ++i) {
427 connect(controller_color_buttons[i], &QPushButton::clicked, this,
428 [this, i] { OnControllerButtonClick(static_cast<int>(i)); });
429 }
430
431 LoadConfiguration(); 429 LoadConfiguration();
432 resize(0, 0);
433 430
434 // TODO(wwylele): enable this when we actually emulate it 431 // TODO(wwylele): enable this when we actually emulate it
435 ui->buttonHome->setEnabled(false); 432 ui->buttonHome->setEnabled(false);
@@ -438,27 +435,43 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
438ConfigureInputPlayer::~ConfigureInputPlayer() = default; 435ConfigureInputPlayer::~ConfigureInputPlayer() = default;
439 436
440void ConfigureInputPlayer::ApplyConfiguration() { 437void ConfigureInputPlayer::ApplyConfiguration() {
441 auto& buttons = 438 auto& player = Settings::values.players[player_index];
442 debug ? Settings::values.debug_pad_buttons : Settings::values.players[player_index].buttons; 439 auto& buttons = debug ? Settings::values.debug_pad_buttons : player.buttons;
443 auto& analogs = 440 auto& analogs = debug ? Settings::values.debug_pad_analogs : player.analogs;
444 debug ? Settings::values.debug_pad_analogs : Settings::values.players[player_index].analogs;
445 441
446 std::transform(buttons_param.begin(), buttons_param.end(), buttons.begin(), 442 std::transform(buttons_param.begin(), buttons_param.end(), buttons.begin(),
447 [](const Common::ParamPackage& param) { return param.Serialize(); }); 443 [](const Common::ParamPackage& param) { return param.Serialize(); });
448 std::transform(analogs_param.begin(), analogs_param.end(), analogs.begin(), 444 std::transform(analogs_param.begin(), analogs_param.end(), analogs.begin(),
449 [](const Common::ParamPackage& param) { return param.Serialize(); }); 445 [](const Common::ParamPackage& param) { return param.Serialize(); });
450 446
451 if (debug) 447 if (debug) {
452 return; 448 return;
449 }
450
451 player.controller_type =
452 static_cast<Settings::ControllerType>(ui->comboControllerType->currentIndex());
453 player.connected = ui->groupConnectedController->isChecked();
453 454
454 std::array<u32, 4> colors{}; 455 // Player 2-8
455 std::transform(controller_colors.begin(), controller_colors.end(), colors.begin(), 456 if (player_index != 0) {
456 [](QColor color) { return color.rgb(); }); 457 UpdateController(player.controller_type, player_index, player.connected);
458 return;
459 }
457 460
458 Settings::values.players[player_index].body_color_left = colors[0]; 461 // Player 1 and Handheld
459 Settings::values.players[player_index].button_color_left = colors[1]; 462 auto& handheld = Settings::values.players[HANDHELD_INDEX];
460 Settings::values.players[player_index].body_color_right = colors[2]; 463 // If Handheld is selected, copy all the settings from Player 1 to Handheld.
461 Settings::values.players[player_index].button_color_right = colors[3]; 464 if (player.controller_type == Settings::ControllerType::Handheld) {
465 handheld = player;
466 handheld.connected = ui->groupConnectedController->isChecked();
467 player.connected = false; // Disconnect Player 1
468 } else {
469 player.connected = ui->groupConnectedController->isChecked();
470 handheld.connected = false; // Disconnect Handheld
471 }
472
473 UpdateController(player.controller_type, player_index, player.connected);
474 UpdateController(Settings::ControllerType::Handheld, HANDHELD_INDEX, handheld.connected);
462} 475}
463 476
464void ConfigureInputPlayer::changeEvent(QEvent* event) { 477void ConfigureInputPlayer::changeEvent(QEvent* event) {
@@ -466,24 +479,16 @@ void ConfigureInputPlayer::changeEvent(QEvent* event) {
466 RetranslateUI(); 479 RetranslateUI();
467 } 480 }
468 481
469 QDialog::changeEvent(event); 482 QWidget::changeEvent(event);
470} 483}
471 484
472void ConfigureInputPlayer::RetranslateUI() { 485void ConfigureInputPlayer::RetranslateUI() {
473 ui->retranslateUi(this); 486 ui->retranslateUi(this);
474 UpdateButtonLabels(); 487 UpdateUI();
475}
476
477void ConfigureInputPlayer::OnControllerButtonClick(int i) {
478 const QColor new_bg_color = QColorDialog::getColor(controller_colors[i]);
479 if (!new_bg_color.isValid())
480 return;
481 controller_colors[i] = new_bg_color;
482 controller_color_buttons[i]->setStyleSheet(
483 QStringLiteral("QPushButton { background-color: %1 }").arg(controller_colors[i].name()));
484} 488}
485 489
486void ConfigureInputPlayer::LoadConfiguration() { 490void ConfigureInputPlayer::LoadConfiguration() {
491 auto& player = Settings::values.players[player_index];
487 if (debug) { 492 if (debug) {
488 std::transform(Settings::values.debug_pad_buttons.begin(), 493 std::transform(Settings::values.debug_pad_buttons.begin(),
489 Settings::values.debug_pad_buttons.end(), buttons_param.begin(), 494 Settings::values.debug_pad_buttons.end(), buttons_param.begin(),
@@ -492,56 +497,60 @@ void ConfigureInputPlayer::LoadConfiguration() {
492 Settings::values.debug_pad_analogs.end(), analogs_param.begin(), 497 Settings::values.debug_pad_analogs.end(), analogs_param.begin(),
493 [](const std::string& str) { return Common::ParamPackage(str); }); 498 [](const std::string& str) { return Common::ParamPackage(str); });
494 } else { 499 } else {
495 std::transform(Settings::values.players[player_index].buttons.begin(), 500 std::transform(player.buttons.begin(), player.buttons.end(), buttons_param.begin(),
496 Settings::values.players[player_index].buttons.end(), buttons_param.begin(),
497 [](const std::string& str) { return Common::ParamPackage(str); }); 501 [](const std::string& str) { return Common::ParamPackage(str); });
498 std::transform(Settings::values.players[player_index].analogs.begin(), 502 std::transform(player.analogs.begin(), player.analogs.end(), analogs_param.begin(),
499 Settings::values.players[player_index].analogs.end(), analogs_param.begin(),
500 [](const std::string& str) { return Common::ParamPackage(str); }); 503 [](const std::string& str) { return Common::ParamPackage(str); });
501 } 504 }
502 505
503 UpdateButtonLabels(); 506 UpdateUI();
504 507
505 if (debug) 508 if (debug) {
506 return; 509 return;
510 }
507 511
508 std::array<u32, 4> colors = { 512 ui->comboControllerType->setCurrentIndex(static_cast<int>(player.controller_type));
509 Settings::values.players[player_index].body_color_left, 513 ui->groupConnectedController->setChecked(
510 Settings::values.players[player_index].button_color_left, 514 player.connected ||
511 Settings::values.players[player_index].body_color_right, 515 (player_index == 0 && Settings::values.players[HANDHELD_INDEX].connected));
512 Settings::values.players[player_index].button_color_right, 516}
513 };
514
515 std::transform(colors.begin(), colors.end(), controller_colors.begin(),
516 [](u32 rgb) { return QColor::fromRgb(rgb); });
517 517
518 for (std::size_t i = 0; i < colors.size(); ++i) { 518void ConfigureInputPlayer::UpdateInputDevices() {
519 controller_color_buttons[i]->setStyleSheet( 519 input_devices = input_subsystem->GetInputDevices();
520 QStringLiteral("QPushButton { background-color: %1 }") 520 ui->comboDevices->clear();
521 .arg(controller_colors[i].name())); 521 for (auto device : input_devices) {
522 ui->comboDevices->addItem(QString::fromStdString(device.Get("display", "Unknown")), {});
522 } 523 }
523} 524}
524 525
525void ConfigureInputPlayer::RestoreDefaults() { 526void ConfigureInputPlayer::RestoreDefaults() {
526 for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; button_id++) { 527 // Reset Buttons
528 for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; ++button_id) {
527 buttons_param[button_id] = Common::ParamPackage{ 529 buttons_param[button_id] = Common::ParamPackage{
528 InputCommon::GenerateKeyboardParam(Config::default_buttons[button_id])}; 530 InputCommon::GenerateKeyboardParam(Config::default_buttons[button_id])};
529 } 531 }
530 532
531 for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; analog_id++) { 533 // Reset Modifier Buttons
532 for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; sub_button_id++) { 534 lstick_mod =
535 Common::ParamPackage(InputCommon::GenerateKeyboardParam(Config::default_stick_mod[0]));
536 rstick_mod =
537 Common::ParamPackage(InputCommon::GenerateKeyboardParam(Config::default_stick_mod[1]));
538
539 // Reset Analogs
540 for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; ++analog_id) {
541 for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; ++sub_button_id) {
533 Common::ParamPackage params{InputCommon::GenerateKeyboardParam( 542 Common::ParamPackage params{InputCommon::GenerateKeyboardParam(
534 Config::default_analogs[analog_id][sub_button_id])}; 543 Config::default_analogs[analog_id][sub_button_id])};
535 SetAnalogButton(params, analogs_param[analog_id], analog_sub_buttons[sub_button_id]); 544 SetAnalogParam(params, analogs_param[analog_id], analog_sub_buttons[sub_button_id]);
536 } 545 }
537 } 546 }
538 547 UpdateUI();
539 UpdateButtonLabels(); 548 UpdateInputDevices();
540 ApplyConfiguration(); 549 ui->comboControllerType->setCurrentIndex(0);
541} 550}
542 551
543void ConfigureInputPlayer::ClearAll() { 552void ConfigureInputPlayer::ClearAll() {
544 for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; button_id++) { 553 for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; ++button_id) {
545 const auto* const button = button_map[button_id]; 554 const auto* const button = button_map[button_id];
546 if (button == nullptr || !button->isEnabled()) { 555 if (button == nullptr || !button->isEnabled()) {
547 continue; 556 continue;
@@ -550,8 +559,11 @@ void ConfigureInputPlayer::ClearAll() {
550 buttons_param[button_id].Clear(); 559 buttons_param[button_id].Clear();
551 } 560 }
552 561
553 for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; analog_id++) { 562 lstick_mod.Clear();
554 for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; sub_button_id++) { 563 rstick_mod.Clear();
564
565 for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; ++analog_id) {
566 for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; ++sub_button_id) {
555 const auto* const analog_button = analog_map_buttons[analog_id][sub_button_id]; 567 const auto* const analog_button = analog_map_buttons[analog_id][sub_button_id];
556 if (analog_button == nullptr || !analog_button->isEnabled()) { 568 if (analog_button == nullptr || !analog_button->isEnabled()) {
557 continue; 569 continue;
@@ -561,17 +573,20 @@ void ConfigureInputPlayer::ClearAll() {
561 } 573 }
562 } 574 }
563 575
564 UpdateButtonLabels(); 576 UpdateUI();
565 ApplyConfiguration(); 577 UpdateInputDevices();
566} 578}
567 579
568void ConfigureInputPlayer::UpdateButtonLabels() { 580void ConfigureInputPlayer::UpdateUI() {
569 for (int button = 0; button < Settings::NativeButton::NumButtons; button++) { 581 for (int button = 0; button < Settings::NativeButton::NumButtons; ++button) {
570 button_map[button]->setText(ButtonToText(buttons_param[button])); 582 button_map[button]->setText(ButtonToText(buttons_param[button]));
571 } 583 }
572 584
573 for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; analog_id++) { 585 ui->buttonLStickMod->setText(ButtonToText(lstick_mod));
574 for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; sub_button_id++) { 586 ui->buttonRStickMod->setText(ButtonToText(rstick_mod));
587
588 for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; ++analog_id) {
589 for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; ++sub_button_id) {
575 auto* const analog_button = analog_map_buttons[analog_id][sub_button_id]; 590 auto* const analog_button = analog_map_buttons[analog_id][sub_button_id];
576 591
577 if (analog_button == nullptr) { 592 if (analog_button == nullptr) {
@@ -581,99 +596,141 @@ void ConfigureInputPlayer::UpdateButtonLabels() {
581 analog_button->setText( 596 analog_button->setText(
582 AnalogToText(analogs_param[analog_id], analog_sub_buttons[sub_button_id])); 597 AnalogToText(analogs_param[analog_id], analog_sub_buttons[sub_button_id]));
583 } 598 }
584 analog_map_stick[analog_id]->setText(tr("Set Analog Stick"));
585 599
600 const auto deadzone_label = analog_map_deadzone_label[analog_id];
601 const auto deadzone_slider = analog_map_deadzone_slider[analog_id];
602 const auto modifier_groupbox = analog_map_modifier_groupbox[analog_id];
603 const auto modifier_label = analog_map_modifier_label[analog_id];
604 const auto modifier_slider = analog_map_modifier_slider[analog_id];
605 const auto range_groupbox = analog_map_range_groupbox[analog_id];
606 const auto range_spinbox = analog_map_range_spinbox[analog_id];
607
608 int slider_value;
586 auto& param = analogs_param[analog_id]; 609 auto& param = analogs_param[analog_id];
587 auto* const analog_stick_slider = analog_map_deadzone_and_modifier_slider[analog_id]; 610 const bool is_controller =
588 auto* const analog_stick_slider_label = 611 param.Get("engine", "") == "sdl" || param.Get("engine", "") == "gcpad";
589 analog_map_deadzone_and_modifier_slider_label[analog_id]; 612
590 613 if (is_controller) {
591 if (param.Has("engine")) { 614 if (!param.Has("deadzone")) {
592 if (param.Get("engine", "") == "sdl" || param.Get("engine", "") == "gcpad") { 615 param.Set("deadzone", 0.1f);
593 if (!param.Has("deadzone")) {
594 param.Set("deadzone", 0.1f);
595 }
596
597 analog_stick_slider->setValue(static_cast<int>(param.Get("deadzone", 0.1f) * 100));
598 if (analog_stick_slider->value() == 0) {
599 analog_stick_slider_label->setText(tr("Deadzone: 0%"));
600 }
601 } else {
602 if (!param.Has("modifier_scale")) {
603 param.Set("modifier_scale", 0.5f);
604 }
605
606 analog_stick_slider->setValue(
607 static_cast<int>(param.Get("modifier_scale", 0.5f) * 100));
608 if (analog_stick_slider->value() == 0) {
609 analog_stick_slider_label->setText(tr("Modifier Scale: 0%"));
610 }
611 } 616 }
617 slider_value = static_cast<int>(param.Get("deadzone", 0.1f) * 100);
618 deadzone_label->setText(tr("Deadzone: %1%").arg(slider_value));
619 deadzone_slider->setValue(slider_value);
620 if (!param.Has("range")) {
621 param.Set("range", 1.0f);
622 }
623 range_spinbox->setValue(static_cast<int>(param.Get("range", 1.0f) * 100));
624 } else {
625 if (!param.Has("modifier_scale")) {
626 param.Set("modifier_scale", 0.5f);
627 }
628 slider_value = static_cast<int>(param.Get("modifier_scale", 0.5f) * 100);
629 modifier_label->setText(tr("Modifier Range: %1%").arg(slider_value));
630 modifier_slider->setValue(slider_value);
612 } 631 }
632
633 deadzone_label->setVisible(is_controller);
634 deadzone_slider->setVisible(is_controller);
635 modifier_groupbox->setVisible(!is_controller);
636 modifier_label->setVisible(!is_controller);
637 modifier_slider->setVisible(!is_controller);
638 range_groupbox->setVisible(is_controller);
613 } 639 }
614} 640}
615 641
642void ConfigureInputPlayer::UpdateMappingWithDefaults() {
643 if (ui->comboDevices->currentIndex() < 2) {
644 return;
645 }
646 const auto& device = input_devices[ui->comboDevices->currentIndex()];
647 auto button_mapping = input_subsystem->GetButtonMappingForDevice(device);
648 auto analog_mapping = input_subsystem->GetAnalogMappingForDevice(device);
649 for (int i = 0; i < buttons_param.size(); ++i) {
650 buttons_param[i] = button_mapping[static_cast<Settings::NativeButton::Values>(i)];
651 }
652 for (int i = 0; i < analogs_param.size(); ++i) {
653 analogs_param[i] = analog_mapping[static_cast<Settings::NativeAnalog::Values>(i)];
654 }
655
656 UpdateUI();
657}
658
616void ConfigureInputPlayer::HandleClick( 659void ConfigureInputPlayer::HandleClick(
617 QPushButton* button, std::function<void(const Common::ParamPackage&)> new_input_setter, 660 QPushButton* button, std::function<void(const Common::ParamPackage&)> new_input_setter,
618 InputCommon::Polling::DeviceType type) { 661 InputCommon::Polling::DeviceType type) {
619 button->setText(tr("[press key]")); 662 button->setText(tr("[waiting]"));
620 button->setFocus(); 663 button->setFocus();
621 664
622 // Keyboard keys can only be used as button devices 665 // The first two input devices are always Any and Keyboard/Mouse. If the user filtered to a
623 want_keyboard_keys = type == InputCommon::Polling::DeviceType::Button; 666 // controller, then they don't want keyboard/mouse input
624 if (want_keyboard_keys) { 667 want_keyboard_mouse = ui->comboDevices->currentIndex() < 2;
625 const auto iter = std::find(button_map.begin(), button_map.end(), button);
626 ASSERT(iter != button_map.end());
627 const auto index = std::distance(button_map.begin(), iter);
628 ASSERT(index < Settings::NativeButton::NumButtons && index >= 0);
629 }
630 668
631 input_setter = new_input_setter; 669 input_setter = new_input_setter;
632 670
633 device_pollers = InputCommon::Polling::GetPollers(type); 671 device_pollers = input_subsystem->GetPollers(type);
634 672
635 for (auto& poller : device_pollers) { 673 for (auto& poller : device_pollers) {
636 poller->Start(); 674 poller->Start();
637 } 675 }
638 676
639 grabKeyboard(); 677 QWidget::grabMouse();
640 grabMouse(); 678 QWidget::grabKeyboard();
679
641 if (type == InputCommon::Polling::DeviceType::Button) { 680 if (type == InputCommon::Polling::DeviceType::Button) {
642 InputCommon::GetGCButtons()->BeginConfiguration(); 681 input_subsystem->GetGCButtons()->BeginConfiguration();
643 } else { 682 } else {
644 InputCommon::GetGCAnalogs()->BeginConfiguration(); 683 input_subsystem->GetGCAnalogs()->BeginConfiguration();
645 } 684 }
646 timeout_timer->start(5000); // Cancel after 5 seconds 685
647 poll_timer->start(200); // Check for new inputs every 200ms 686 timeout_timer->start(2500); // Cancel after 2.5 seconds
687 poll_timer->start(50); // Check for new inputs every 50ms
648} 688}
649 689
650void ConfigureInputPlayer::SetPollingResult(const Common::ParamPackage& params, bool abort) { 690void ConfigureInputPlayer::SetPollingResult(const Common::ParamPackage& params, bool abort) {
651 releaseKeyboard();
652 releaseMouse();
653 timeout_timer->stop(); 691 timeout_timer->stop();
654 poll_timer->stop(); 692 poll_timer->stop();
655 for (auto& poller : device_pollers) { 693 for (auto& poller : device_pollers) {
656 poller->Stop(); 694 poller->Stop();
657 } 695 }
658 696
659 InputCommon::GetGCButtons()->EndConfiguration(); 697 QWidget::releaseMouse();
660 InputCommon::GetGCAnalogs()->EndConfiguration(); 698 QWidget::releaseKeyboard();
699
700 input_subsystem->GetGCButtons()->EndConfiguration();
701 input_subsystem->GetGCAnalogs()->EndConfiguration();
661 702
662 if (!abort) { 703 if (!abort) {
663 (*input_setter)(params); 704 (*input_setter)(params);
664 } 705 }
665 706
666 UpdateButtonLabels(); 707 UpdateUI();
667 input_setter = std::nullopt; 708 input_setter = std::nullopt;
668} 709}
669 710
711void ConfigureInputPlayer::mousePressEvent(QMouseEvent* event) {
712 if (!input_setter || !event) {
713 return;
714 }
715
716 if (want_keyboard_mouse) {
717 SetPollingResult(Common::ParamPackage{InputCommon::GenerateKeyboardParam(event->button())},
718 false);
719 } else {
720 // We don't want any mouse buttons, so don't stop polling
721 return;
722 }
723
724 SetPollingResult({}, true);
725}
726
670void ConfigureInputPlayer::keyPressEvent(QKeyEvent* event) { 727void ConfigureInputPlayer::keyPressEvent(QKeyEvent* event) {
671 if (!input_setter || !event) { 728 if (!input_setter || !event) {
672 return; 729 return;
673 } 730 }
674 731
675 if (event->key() != Qt::Key_Escape) { 732 if (event->key() != Qt::Key_Escape) {
676 if (want_keyboard_keys) { 733 if (want_keyboard_mouse) {
677 SetPollingResult(Common::ParamPackage{InputCommon::GenerateKeyboardParam(event->key())}, 734 SetPollingResult(Common::ParamPackage{InputCommon::GenerateKeyboardParam(event->key())},
678 false); 735 false);
679 } else { 736 } else {
@@ -681,5 +738,108 @@ void ConfigureInputPlayer::keyPressEvent(QKeyEvent* event) {
681 return; 738 return;
682 } 739 }
683 } 740 }
741
684 SetPollingResult({}, true); 742 SetPollingResult({}, true);
685} 743}
744
745void ConfigureInputPlayer::UpdateControllerIcon() {
746 // We aren't using Qt's built in theme support here since we aren't drawing an icon (and its
747 // "nonstandard" to use an image through the icon support)
748 const QString stylesheet = [this] {
749 switch (GetControllerTypeFromIndex(ui->comboControllerType->currentIndex())) {
750 case Settings::ControllerType::ProController:
751 return QStringLiteral("image: url(:/controller/pro_controller%0)");
752 case Settings::ControllerType::DualJoyconDetached:
753 return QStringLiteral("image: url(:/controller/dual_joycon%0)");
754 case Settings::ControllerType::LeftJoycon:
755 return QStringLiteral("image: url(:/controller/single_joycon_left_vertical%0)");
756 case Settings::ControllerType::RightJoycon:
757 return QStringLiteral("image: url(:/controller/single_joycon_right_vertical%0)");
758 case Settings::ControllerType::Handheld:
759 return QStringLiteral("image: url(:/controller/handheld%0)");
760 default:
761 return QString{};
762 }
763 }();
764
765 const QString theme = [this] {
766 if (QIcon::themeName().contains(QStringLiteral("dark"))) {
767 return QStringLiteral("_dark");
768 } else if (QIcon::themeName().contains(QStringLiteral("midnight"))) {
769 return QStringLiteral("_midnight");
770 } else {
771 return QString{};
772 }
773 }();
774
775 ui->controllerFrame->setStyleSheet(stylesheet.arg(theme));
776}
777
778void ConfigureInputPlayer::UpdateControllerAvailableButtons() {
779 auto layout = GetControllerTypeFromIndex(ui->comboControllerType->currentIndex());
780 if (debug) {
781 layout = Settings::ControllerType::ProController;
782 }
783
784 // List of all the widgets that will be hidden by any of the following layouts that need
785 // "unhidden" after the controller type changes
786 const std::array<QWidget*, 9> layout_show = {
787 ui->buttonShoulderButtonsSLSR,
788 ui->horizontalSpacerShoulderButtonsWidget,
789 ui->horizontalSpacerShoulderButtonsWidget2,
790 ui->buttonShoulderButtonsLeft,
791 ui->buttonMiscButtonsMinusScreenshot,
792 ui->bottomLeft,
793 ui->buttonShoulderButtonsRight,
794 ui->buttonMiscButtonsPlusHome,
795 ui->bottomRight,
796 };
797
798 for (auto* widget : layout_show) {
799 widget->show();
800 }
801
802 std::vector<QWidget*> layout_hidden;
803 switch (layout) {
804 case Settings::ControllerType::ProController:
805 case Settings::ControllerType::DualJoyconDetached:
806 case Settings::ControllerType::Handheld:
807 layout_hidden = {
808 ui->buttonShoulderButtonsSLSR,
809 ui->horizontalSpacerShoulderButtonsWidget2,
810 };
811 break;
812 case Settings::ControllerType::LeftJoycon:
813 layout_hidden = {
814 ui->horizontalSpacerShoulderButtonsWidget2,
815 ui->buttonShoulderButtonsRight,
816 ui->buttonMiscButtonsPlusHome,
817 ui->bottomRight,
818 };
819 break;
820 case Settings::ControllerType::RightJoycon:
821 layout_hidden = {
822 ui->horizontalSpacerShoulderButtonsWidget,
823 ui->buttonShoulderButtonsLeft,
824 ui->buttonMiscButtonsMinusScreenshot,
825 ui->bottomLeft,
826 };
827 break;
828 }
829
830 for (auto* widget : layout_hidden) {
831 widget->hide();
832 }
833}
834
835void ConfigureInputPlayer::showEvent(QShowEvent* event) {
836 if (bottom_row == nullptr) {
837 return;
838 }
839 QWidget::showEvent(event);
840 ui->main->addWidget(bottom_row);
841}
842
843void ConfigureInputPlayer::ConnectPlayer(bool connected) {
844 ui->groupConnectedController->setChecked(connected);
845}
diff --git a/src/yuzu/configuration/configure_input_player.h b/src/yuzu/configuration/configure_input_player.h
index 95afa5375..a25bc3bd9 100644
--- a/src/yuzu/configuration/configure_input_player.h
+++ b/src/yuzu/configuration/configure_input_player.h
@@ -10,16 +10,25 @@
10#include <optional> 10#include <optional>
11#include <string> 11#include <string>
12 12
13#include <QDialog> 13#include <QWidget>
14 14
15#include "common/param_package.h" 15#include "common/param_package.h"
16#include "core/settings.h" 16#include "core/settings.h"
17#include "ui_configure_input.h" 17#include "ui_configure_input.h"
18 18
19class QCheckBox;
19class QKeyEvent; 20class QKeyEvent;
21class QLabel;
20class QPushButton; 22class QPushButton;
23class QSlider;
24class QSpinBox;
21class QString; 25class QString;
22class QTimer; 26class QTimer;
27class QWidget;
28
29namespace InputCommon {
30class InputSubsystem;
31}
23 32
24namespace InputCommon::Polling { 33namespace InputCommon::Polling {
25class DevicePoller; 34class DevicePoller;
@@ -30,77 +39,116 @@ namespace Ui {
30class ConfigureInputPlayer; 39class ConfigureInputPlayer;
31} 40}
32 41
33class ConfigureInputPlayer : public QDialog { 42class ConfigureInputPlayer : public QWidget {
34 Q_OBJECT 43 Q_OBJECT
35 44
36public: 45public:
37 explicit ConfigureInputPlayer(QWidget* parent, std::size_t player_index, bool debug = false); 46 explicit ConfigureInputPlayer(QWidget* parent, std::size_t player_index, QWidget* bottom_row,
47 InputCommon::InputSubsystem* input_subsystem_,
48 bool debug = false);
38 ~ConfigureInputPlayer() override; 49 ~ConfigureInputPlayer() override;
39 50
40 /// Save all button configurations to settings file 51 /// Save all button configurations to settings file.
41 void ApplyConfiguration(); 52 void ApplyConfiguration();
42 53
54 /// Update the input devices combobox.
55 void UpdateInputDevices();
56
57 /// Restore all buttons to their default values.
58 void RestoreDefaults();
59
60 /// Clear all input configuration.
61 void ClearAll();
62
63 /// Set the connection state checkbox (used to sync state).
64 void ConnectPlayer(bool connected);
65
66signals:
67 /// Emitted when this controller is connected by the user.
68 void Connected(bool connected);
69 /// Emitted when the Handheld mode is selected (undocked with dual joycons attached).
70 void HandheldStateChanged(bool is_handheld);
71 /// Emitted when the input devices combobox is being refreshed.
72 void RefreshInputDevices();
73
74protected:
75 void showEvent(QShowEvent* event) override;
76
43private: 77private:
44 void changeEvent(QEvent* event) override; 78 void changeEvent(QEvent* event) override;
45 void RetranslateUI(); 79 void RetranslateUI();
46 80
47 void OnControllerButtonClick(int i);
48
49 /// Load configuration settings. 81 /// Load configuration settings.
50 void LoadConfiguration(); 82 void LoadConfiguration();
51 /// Restore all buttons to their default values.
52 void RestoreDefaults();
53 /// Clear all input configuration
54 void ClearAll();
55
56 /// Update UI to reflect current configuration.
57 void UpdateButtonLabels();
58 83
59 /// Called when the button was pressed. 84 /// Called when the button was pressed.
60 void HandleClick(QPushButton* button, 85 void HandleClick(QPushButton* button,
61 std::function<void(const Common::ParamPackage&)> new_input_setter, 86 std::function<void(const Common::ParamPackage&)> new_input_setter,
62 InputCommon::Polling::DeviceType type); 87 InputCommon::Polling::DeviceType type);
63 88
64 /// Finish polling and configure input using the input_setter 89 /// Finish polling and configure input using the input_setter.
65 void SetPollingResult(const Common::ParamPackage& params, bool abort); 90 void SetPollingResult(const Common::ParamPackage& params, bool abort);
66 91
92 /// Handle mouse button press events.
93 void mousePressEvent(QMouseEvent* event) override;
94
67 /// Handle key press events. 95 /// Handle key press events.
68 void keyPressEvent(QKeyEvent* event) override; 96 void keyPressEvent(QKeyEvent* event) override;
69 97
98 /// Update UI to reflect current configuration.
99 void UpdateUI();
100
101 /// Update the controller selection combobox
102 void UpdateControllerCombobox();
103
104 /// Update the current controller icon.
105 void UpdateControllerIcon();
106
107 /// Hides and disables controller settings based on the current controller type.
108 void UpdateControllerAvailableButtons();
109
110 /// Gets the default controller mapping for this device and auto configures the input to match.
111 void UpdateMappingWithDefaults();
112
70 std::unique_ptr<Ui::ConfigureInputPlayer> ui; 113 std::unique_ptr<Ui::ConfigureInputPlayer> ui;
71 114
72 std::size_t player_index; 115 std::size_t player_index;
73 bool debug; 116 bool debug;
74 117
118 InputCommon::InputSubsystem* input_subsystem;
119
75 std::unique_ptr<QTimer> timeout_timer; 120 std::unique_ptr<QTimer> timeout_timer;
76 std::unique_ptr<QTimer> poll_timer; 121 std::unique_ptr<QTimer> poll_timer;
77 122
123 static constexpr int PLAYER_COUNT = 8;
124 std::array<QCheckBox*, PLAYER_COUNT> player_connected_checkbox;
125
78 /// This will be the the setting function when an input is awaiting configuration. 126 /// This will be the the setting function when an input is awaiting configuration.
79 std::optional<std::function<void(const Common::ParamPackage&)>> input_setter; 127 std::optional<std::function<void(const Common::ParamPackage&)>> input_setter;
80 128
81 std::array<Common::ParamPackage, Settings::NativeButton::NumButtons> buttons_param; 129 std::array<Common::ParamPackage, Settings::NativeButton::NumButtons> buttons_param;
82 std::array<Common::ParamPackage, Settings::NativeAnalog::NumAnalogs> analogs_param; 130 std::array<Common::ParamPackage, Settings::NativeAnalog::NumAnalogs> analogs_param;
83 131
84 static constexpr int ANALOG_SUB_BUTTONS_NUM = 5; 132 static constexpr int ANALOG_SUB_BUTTONS_NUM = 4;
85 133
86 /// Each button input is represented by a QPushButton. 134 /// Each button input is represented by a QPushButton.
87 std::array<QPushButton*, Settings::NativeButton::NumButtons> button_map; 135 std::array<QPushButton*, Settings::NativeButton::NumButtons> button_map;
136 /// Extra buttons for the modifiers.
137 Common::ParamPackage lstick_mod;
138 Common::ParamPackage rstick_mod;
88 139
89 std::vector<QWidget*> debug_hidden; 140 /// A group of four QPushButtons represent one analog input. The buttons each represent up,
90 std::vector<QWidget*> layout_hidden; 141 /// down, left, right, respectively.
91
92 /// A group of five QPushButtons represent one analog input. The buttons each represent up,
93 /// down, left, right, and modifier, respectively.
94 std::array<std::array<QPushButton*, ANALOG_SUB_BUTTONS_NUM>, Settings::NativeAnalog::NumAnalogs> 142 std::array<std::array<QPushButton*, ANALOG_SUB_BUTTONS_NUM>, Settings::NativeAnalog::NumAnalogs>
95 analog_map_buttons; 143 analog_map_buttons;
96 144
97 /// Analog inputs are also represented each with a single button, used to configure with an 145 std::array<QLabel*, Settings::NativeAnalog::NumAnalogs> analog_map_deadzone_label;
98 /// actual analog stick 146 std::array<QSlider*, Settings::NativeAnalog::NumAnalogs> analog_map_deadzone_slider;
99 std::array<QPushButton*, Settings::NativeAnalog::NumAnalogs> analog_map_stick; 147 std::array<QGroupBox*, Settings::NativeAnalog::NumAnalogs> analog_map_modifier_groupbox;
100 std::array<QSlider*, Settings::NativeAnalog::NumAnalogs> 148 std::array<QLabel*, Settings::NativeAnalog::NumAnalogs> analog_map_modifier_label;
101 analog_map_deadzone_and_modifier_slider; 149 std::array<QSlider*, Settings::NativeAnalog::NumAnalogs> analog_map_modifier_slider;
102 std::array<QLabel*, Settings::NativeAnalog::NumAnalogs> 150 std::array<QGroupBox*, Settings::NativeAnalog::NumAnalogs> analog_map_range_groupbox;
103 analog_map_deadzone_and_modifier_slider_label; 151 std::array<QSpinBox*, Settings::NativeAnalog::NumAnalogs> analog_map_range_spinbox;
104 152
105 static const std::array<std::string, ANALOG_SUB_BUTTONS_NUM> analog_sub_buttons; 153 static const std::array<std::string, ANALOG_SUB_BUTTONS_NUM> analog_sub_buttons;
106 154
@@ -108,8 +156,14 @@ private:
108 156
109 /// A flag to indicate if keyboard keys are okay when configuring an input. If this is false, 157 /// A flag to indicate if keyboard keys are okay when configuring an input. If this is false,
110 /// keyboard events are ignored. 158 /// keyboard events are ignored.
111 bool want_keyboard_keys = false; 159 bool want_keyboard_mouse = false;
160
161 /// List of physical devices users can map with. If a SDL backed device is selected, then you
162 /// can usue this device to get a default mapping.
163 std::vector<Common::ParamPackage> input_devices;
112 164
113 std::array<QPushButton*, 4> controller_color_buttons; 165 /// Bottom row is where console wide settings are held, and its "owned" by the parent
114 std::array<QColor, 4> controller_colors; 166 /// ConfigureInput widget. On show, add this widget to the main layout. This will change the
167 /// parent of the widget to this widget (but thats fine).
168 QWidget* bottom_row;
115}; 169};
diff --git a/src/yuzu/configuration/configure_input_player.ui b/src/yuzu/configuration/configure_input_player.ui
index f27a77180..9bc681894 100644
--- a/src/yuzu/configuration/configure_input_player.ui
+++ b/src/yuzu/configuration/configure_input_player.ui
@@ -1,1243 +1,2974 @@
1<?xml version="1.0" encoding="UTF-8"?> 1<?xml version="1.0" encoding="UTF-8"?>
2<ui version="4.0"> 2<ui version="4.0">
3 <class>ConfigureInputPlayer</class> 3 <class>ConfigureInputPlayer</class>
4 <widget class="QDialog" name="ConfigureInputPlayer"> 4 <widget class="QWidget" name="ConfigureInputPlayer">
5 <property name="geometry"> 5 <property name="geometry">
6 <rect> 6 <rect>
7 <x>0</x> 7 <x>0</x>
8 <y>0</y> 8 <y>0</y>
9 <width>408</width> 9 <width>780</width>
10 <height>731</height> 10 <height>487</height>
11 </rect> 11 </rect>
12 </property> 12 </property>
13 <property name="windowTitle"> 13 <property name="windowTitle">
14 <string>Configure Input</string> 14 <string>Configure Input</string>
15 </property> 15 </property>
16 <layout class="QVBoxLayout" name="verticalLayout_5"> 16 <layout class="QHBoxLayout" name="horizontalLayout_2">
17 <property name="spacing">
18 <number>0</number>
19 </property>
20 <property name="leftMargin">
21 <number>0</number>
22 </property>
23 <property name="topMargin">
24 <number>0</number>
25 </property>
26 <property name="rightMargin">
27 <number>0</number>
28 </property>
29 <property name="bottomMargin">
30 <number>0</number>
31 </property>
17 <item> 32 <item>
18 <layout class="QGridLayout" name="buttons"> 33 <layout class="QVBoxLayout" name="main">
19 <item row="1" column="1"> 34 <property name="spacing">
20 <widget class="QGroupBox" name="RStick"> 35 <number>0</number>
21 <property name="title"> 36 </property>
22 <string>Right Stick</string> 37 <property name="leftMargin">
23 </property> 38 <number>0</number>
24 <property name="alignment"> 39 </property>
25 <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set> 40 <property name="topMargin">
26 </property> 41 <number>0</number>
27 <property name="flat"> 42 </property>
28 <bool>false</bool> 43 <property name="rightMargin">
44 <number>0</number>
45 </property>
46 <property name="bottomMargin">
47 <number>0</number>
48 </property>
49 <item>
50 <layout class="QHBoxLayout" name="top" stretch="0,1,2">
51 <property name="spacing">
52 <number>3</number>
29 </property> 53 </property>
30 <property name="checkable"> 54 <property name="topMargin">
31 <bool>false</bool> 55 <number>0</number>
32 </property> 56 </property>
33 <layout class="QGridLayout" name="gridLayout_5"> 57 <item>
34 <item row="1" column="1"> 58 <widget class="QGroupBox" name="groupConnectedController">
35 <layout class="QVBoxLayout" name="buttonRStickDownVerticalLayout"> 59 <property name="layoutDirection">
60 <enum>Qt::LeftToRight</enum>
61 </property>
62 <property name="title">
63 <string>Connect Controller</string>
64 </property>
65 <property name="flat">
66 <bool>false</bool>
67 </property>
68 <property name="checkable">
69 <bool>true</bool>
70 </property>
71 <layout class="QHBoxLayout" name="horizontalLayout_3">
72 <property name="leftMargin">
73 <number>5</number>
74 </property>
75 <property name="topMargin">
76 <number>5</number>
77 </property>
78 <property name="rightMargin">
79 <number>5</number>
80 </property>
81 <property name="bottomMargin">
82 <number>5</number>
83 </property>
36 <item> 84 <item>
37 <layout class="QHBoxLayout" name="buttonRStickDownHorizontalLayout"> 85 <widget class="QComboBox" name="comboControllerType">
38 <item> 86 <item>
39 <widget class="QLabel" name="labelRStickDown"> 87 <property name="text">
40 <property name="text"> 88 <string>Pro Controller</string>
41 <string>Down:</string> 89 </property>
42 </property>
43 </widget>
44 </item> 90 </item>
45 </layout>
46 </item>
47 <item>
48 <widget class="QPushButton" name="buttonRStickDown">
49 <property name="text">
50 <string/>
51 </property>
52 </widget>
53 </item>
54 </layout>
55 </item>
56 <item row="0" column="1">
57 <layout class="QVBoxLayout" name="buttonRStickRightVerticalLayout">
58 <item>
59 <layout class="QHBoxLayout" name="buttonRStickRightHorizontalLayout">
60 <item> 91 <item>
61 <widget class="QLabel" name="labelRStickRight"> 92 <property name="text">
62 <property name="text"> 93 <string>Dual Joycons</string>
63 <string>Right:</string> 94 </property>
64 </property>
65 </widget>
66 </item> 95 </item>
67 </layout>
68 </item>
69 <item>
70 <widget class="QPushButton" name="buttonRStickRight">
71 <property name="text">
72 <string/>
73 </property>
74 </widget>
75 </item>
76 </layout>
77 </item>
78 <item row="3" column="0" colspan="2">
79 <widget class="QPushButton" name="buttonRStickAnalog">
80 <property name="text">
81 <string>Set Analog Stick</string>
82 </property>
83 </widget>
84 </item>
85 <item row="0" column="0">
86 <layout class="QVBoxLayout" name="buttonRStickLeftVerticalLayout">
87 <item>
88 <layout class="QHBoxLayout" name="buttonRStickLeftHorizontalLayout">
89 <item> 96 <item>
90 <widget class="QLabel" name="labelRStickLeft"> 97 <property name="text">
91 <property name="text"> 98 <string>Left Joycon</string>
92 <string>Left:</string> 99 </property>
93 </property>
94 </widget>
95 </item> 100 </item>
96 </layout>
97 </item>
98 <item>
99 <widget class="QPushButton" name="buttonRStickLeft">
100 <property name="text">
101 <string/>
102 </property>
103 </widget>
104 </item>
105 </layout>
106 </item>
107 <item row="1" column="0">
108 <layout class="QVBoxLayout" name="buttonRStickUpVerticalLayout">
109 <item>
110 <layout class="QHBoxLayout" name="buttonRStickUpHorizontalLayout">
111 <item> 101 <item>
112 <widget class="QLabel" name="labelRStickUp"> 102 <property name="text">
113 <property name="text"> 103 <string>Right Joycon</string>
114 <string>Up:</string> 104 </property>
115 </property>
116 </widget>
117 </item> 105 </item>
118 </layout>
119 </item>
120 <item>
121 <widget class="QPushButton" name="buttonRStickUp">
122 <property name="text">
123 <string/>
124 </property>
125 </widget>
126 </item>
127 </layout>
128 </item>
129 <item row="2" column="0">
130 <layout class="QVBoxLayout" name="buttonRStickPressedVerticalLayout">
131 <item>
132 <layout class="QHBoxLayout" name="buttonRStickPressedHorizontalLayout">
133 <item> 106 <item>
134 <widget class="QLabel" name="labelRStickPressed"> 107 <property name="text">
135 <property name="text"> 108 <string>Handheld</string>
136 <string>Pressed:</string> 109 </property>
137 </property>
138 </widget>
139 </item> 110 </item>
140 </layout>
141 </item>
142 <item>
143 <widget class="QPushButton" name="buttonRStick">
144 <property name="text">
145 <string/>
146 </property>
147 </widget> 111 </widget>
148 </item> 112 </item>
149 </layout> 113 </layout>
150 </item> 114 </widget>
151 <item row="2" column="1"> 115 </item>
152 <layout class="QVBoxLayout" name="buttonRStickModVerticalLayout"> 116 <item>
117 <widget class="QGroupBox" name="devicesGroup">
118 <property name="title">
119 <string>Input Device</string>
120 </property>
121 <layout class="QHBoxLayout" name="horizontalLayout_5">
122 <property name="spacing">
123 <number>3</number>
124 </property>
125 <property name="leftMargin">
126 <number>5</number>
127 </property>
128 <property name="topMargin">
129 <number>5</number>
130 </property>
131 <property name="rightMargin">
132 <number>5</number>
133 </property>
134 <property name="bottomMargin">
135 <number>5</number>
136 </property>
153 <item> 137 <item>
154 <layout class="QHBoxLayout" name="buttonRStickModHorizontalLayout"> 138 <widget class="QComboBox" name="comboDevices">
155 <item> 139 <item>
156 <widget class="QLabel" name="labelRStickMod"> 140 <property name="text">
157 <property name="text"> 141 <string>Any</string>
158 <string>Modifier:</string> 142 </property>
159 </property>
160 </widget>
161 </item> 143 </item>
162 </layout>
163 </item>
164 <item>
165 <widget class="QPushButton" name="buttonRStickMod">
166 <property name="text">
167 <string/>
168 </property>
169 </widget>
170 </item>
171 </layout>
172 </item>
173 <item row="4" column="0" colspan="2">
174 <layout class="QVBoxLayout" name="sliderRStickDeadzoneAndModifierVerticalLayout">
175 <item>
176 <layout class="QHBoxLayout" name="sliderRStickDeadzoneAndModifierHorizontalLayout">
177 <item> 144 <item>
178 <widget class="QLabel" name="labelRStickDeadzoneAndModifier"> 145 <property name="text">
179 <property name="text"> 146 <string>Keyboard/Mouse</string>
180 <string>Deadzone: 0</string> 147 </property>
181 </property>
182 <property name="alignment">
183 <enum>Qt::AlignHCenter</enum>
184 </property>
185 </widget>
186 </item> 148 </item>
187 </layout> 149 </widget>
188 </item> 150 </item>
189 <item> 151 <item>
190 <widget class="QSlider" name="sliderRStickDeadzoneAndModifier"> 152 <widget class="QPushButton" name="buttonRefreshDevices">
191 <property name="orientation"> 153 <property name="minimumSize">
192 <enum>Qt::Horizontal</enum> 154 <size>
155 <width>24</width>
156 <height>22</height>
157 </size>
158 </property>
159 <property name="maximumSize">
160 <size>
161 <width>24</width>
162 <height>22</height>
163 </size>
164 </property>
165 <property name="styleSheet">
166 <string notr="true"/>
193 </property> 167 </property>
194 </widget> 168 </widget>
195 </item> 169 </item>
196 </layout> 170 </layout>
197 </item> 171 </widget>
198 <item row="5" column="0"> 172 </item>
199 <spacer name="RStick_verticalSpacer"> 173 <item>
200 <property name="orientation"> 174 <widget class="QGroupBox" name="profilesGroup">
201 <enum>Qt::Vertical</enum> 175 <property name="minimumSize">
176 <size>
177 <width>0</width>
178 <height>0</height>
179 </size>
180 </property>
181 <property name="title">
182 <string>Profile</string>
183 </property>
184 <layout class="QHBoxLayout" name="horizontalLayout_4" stretch="2,0,0,0">
185 <property name="spacing">
186 <number>3</number>
202 </property> 187 </property>
203 <property name="sizeHint" stdset="0"> 188 <property name="leftMargin">
204 <size> 189 <number>5</number>
205 <width>0</width> 190 </property>
206 <height>0</height> 191 <property name="topMargin">
207 </size> 192 <number>5</number>
193 </property>
194 <property name="rightMargin">
195 <number>5</number>
196 </property>
197 <property name="bottomMargin">
198 <number>5</number>
208 </property> 199 </property>
209 </spacer>
210 </item>
211 </layout>
212 </widget>
213 </item>
214 <item row="0" column="1">
215 <widget class="QGroupBox" name="Dpad">
216 <property name="title">
217 <string>Directional Pad</string>
218 </property>
219 <property name="flat">
220 <bool>false</bool>
221 </property>
222 <property name="checkable">
223 <bool>false</bool>
224 </property>
225 <layout class="QGridLayout" name="gridLayout_2">
226 <item row="1" column="0">
227 <layout class="QVBoxLayout" name="buttonDpadUpVerticalLayout">
228 <item> 200 <item>
229 <layout class="QHBoxLayout" name="buttonDpadUpHorizontalLayout"> 201 <widget class="QComboBox" name="comboProfiles"/>
230 <item>
231 <widget class="QLabel" name="labelDpadUp">
232 <property name="text">
233 <string>Up:</string>
234 </property>
235 </widget>
236 </item>
237 </layout>
238 </item> 202 </item>
239 <item> 203 <item>
240 <widget class="QPushButton" name="buttonDpadUp"> 204 <widget class="QPushButton" name="buttonProfilesSave">
241 <property name="text"> 205 <property name="maximumSize">
242 <string/> 206 <size>
207 <width>55</width>
208 <height>16777215</height>
209 </size>
210 </property>
211 <property name="styleSheet">
212 <string notr="true">min-width: 55px;</string>
243 </property> 213 </property>
244 </widget>
245 </item>
246 </layout>
247 </item>
248 <item row="1" column="1">
249 <layout class="QVBoxLayout" name="buttonDpadDownVerticalLayout">
250 <item>
251 <layout class="QHBoxLayout" name="buttonDpadDownHorizontalLayout">
252 <item>
253 <widget class="QLabel" name="labelDpadDown">
254 <property name="text">
255 <string>Down:</string>
256 </property>
257 </widget>
258 </item>
259 </layout>
260 </item>
261 <item>
262 <widget class="QPushButton" name="buttonDpadDown">
263 <property name="text"> 214 <property name="text">
264 <string/> 215 <string>Save</string>
265 </property> 216 </property>
266 </widget> 217 </widget>
267 </item> 218 </item>
268 </layout>
269 </item>
270 <item row="0" column="0">
271 <layout class="QVBoxLayout" name="buttonDpadLeftVerticalLayout">
272 <item>
273 <layout class="QHBoxLayout" name="buttonDpadLeftHorizontalLayout">
274 <item>
275 <widget class="QLabel" name="labelDpadLeft">
276 <property name="minimumSize">
277 <size>
278 <width>80</width>
279 <height>0</height>
280 </size>
281 </property>
282 <property name="text">
283 <string>Left:</string>
284 </property>
285 </widget>
286 </item>
287 </layout>
288 </item>
289 <item> 219 <item>
290 <widget class="QPushButton" name="buttonDpadLeft"> 220 <widget class="QPushButton" name="buttonProfilesNew">
221 <property name="maximumSize">
222 <size>
223 <width>55</width>
224 <height>16777215</height>
225 </size>
226 </property>
227 <property name="styleSheet">
228 <string notr="true">min-width: 55px;</string>
229 </property>
291 <property name="text"> 230 <property name="text">
292 <string/> 231 <string>New</string>
293 </property> 232 </property>
294 </widget> 233 </widget>
295 </item> 234 </item>
296 </layout>
297 </item>
298 <item row="0" column="1">
299 <layout class="QVBoxLayout" name="buttonDpadRightVerticalLayout">
300 <item>
301 <layout class="QHBoxLayout" name="buttonDpadRightHorizontalLayout">
302 <item>
303 <widget class="QLabel" name="labelDpadRight">
304 <property name="minimumSize">
305 <size>
306 <width>80</width>
307 <height>0</height>
308 </size>
309 </property>
310 <property name="text">
311 <string>Right:</string>
312 </property>
313 </widget>
314 </item>
315 </layout>
316 </item>
317 <item> 235 <item>
318 <widget class="QPushButton" name="buttonDpadRight"> 236 <widget class="QPushButton" name="buttonProfilesDelete">
237 <property name="maximumSize">
238 <size>
239 <width>55</width>
240 <height>16777215</height>
241 </size>
242 </property>
243 <property name="styleSheet">
244 <string notr="true">min-width: 55px;</string>
245 </property>
319 <property name="text"> 246 <property name="text">
320 <string/> 247 <string>Delete</string>
321 </property> 248 </property>
322 </widget> 249 </widget>
323 </item> 250 </item>
324 </layout> 251 </layout>
325 </item> 252 </widget>
326 </layout> 253 </item>
327 </widget> 254 </layout>
328 </item> 255 </item>
329 <item row="0" column="0"> 256 <item>
330 <widget class="QGroupBox" name="faceButtons"> 257 <widget class="QFrame" name="bottom">
331 <property name="title"> 258 <property name="sizePolicy">
332 <string>Face Buttons</string> 259 <sizepolicy hsizetype="Preferred" vsizetype="MinimumExpanding">
333 </property> 260 <horstretch>0</horstretch>
334 <property name="flat"> 261 <verstretch>0</verstretch>
335 <bool>false</bool> 262 </sizepolicy>
336 </property>
337 <property name="checkable">
338 <bool>false</bool>
339 </property> 263 </property>
340 <layout class="QGridLayout" name="gridLayout"> 264 <layout class="QHBoxLayout" name="_2">
341 <item row="0" column="0"> 265 <property name="sizeConstraint">
342 <layout class="QVBoxLayout" name="buttonFaceButtonsAVerticalLayout"> 266 <enum>QLayout::SetMinimumSize</enum>
343 <item> 267 </property>
344 <layout class="QHBoxLayout" name="buttonFaceButtonsAHorizontalLayout"> 268 <property name="leftMargin">
345 <item> 269 <number>0</number>
346 <widget class="QLabel" name="labelA"> 270 </property>
347 <property name="minimumSize"> 271 <property name="topMargin">
348 <size> 272 <number>0</number>
349 <width>80</width> 273 </property>
350 <height>0</height> 274 <property name="rightMargin">
351 </size> 275 <number>0</number>
276 </property>
277 <property name="bottomMargin">
278 <number>0</number>
279 </property>
280 <item>
281 <widget class="QWidget" name="bottomLeft" native="true">
282 <layout class="QVBoxLayout" name="bottomLeftLayout" stretch="0,0,0,0">
283 <property name="spacing">
284 <number>0</number>
285 </property>
286 <property name="sizeConstraint">
287 <enum>QLayout::SetDefaultConstraint</enum>
288 </property>
289 <property name="leftMargin">
290 <number>0</number>
291 </property>
292 <property name="topMargin">
293 <number>0</number>
294 </property>
295 <property name="rightMargin">
296 <number>0</number>
297 </property>
298 <property name="bottomMargin">
299 <number>0</number>
300 </property>
301 <item>
302 <widget class="QGroupBox" name="LStick">
303 <property name="sizePolicy">
304 <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
305 <horstretch>0</horstretch>
306 <verstretch>0</verstretch>
307 </sizepolicy>
308 </property>
309 <property name="title">
310 <string>Left Stick</string>
311 </property>
312 <property name="alignment">
313 <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
314 </property>
315 <layout class="QVBoxLayout" name="verticalLayout_3">
316 <property name="spacing">
317 <number>0</number>
352 </property> 318 </property>
353 <property name="text"> 319 <property name="sizeConstraint">
354 <string>A:</string> 320 <enum>QLayout::SetDefaultConstraint</enum>
355 </property> 321 </property>
356 </widget> 322 <property name="leftMargin">
357 </item> 323 <number>3</number>
358 </layout>
359 </item>
360 <item>
361 <widget class="QPushButton" name="buttonA">
362 <property name="text">
363 <string/>
364 </property>
365 </widget>
366 </item>
367 </layout>
368 </item>
369 <item row="0" column="1">
370 <layout class="QVBoxLayout" name="buttonFaceButtonsBVerticalLayout">
371 <item>
372 <layout class="QHBoxLayout" name="buttonFaceButtonsBHorizontalLayout">
373 <item>
374 <widget class="QLabel" name="labelB">
375 <property name="minimumSize">
376 <size>
377 <width>80</width>
378 <height>0</height>
379 </size>
380 </property> 324 </property>
381 <property name="text"> 325 <property name="topMargin">
382 <string>B:</string> 326 <number>0</number>
383 </property> 327 </property>
384 </widget> 328 <property name="rightMargin">
385 </item> 329 <number>3</number>
386 </layout>
387 </item>
388 <item>
389 <widget class="QPushButton" name="buttonB">
390 <property name="text">
391 <string/>
392 </property>
393 </widget>
394 </item>
395 </layout>
396 </item>
397 <item row="1" column="0">
398 <layout class="QVBoxLayout" name="buttonFaceButtonsXVerticalLayout">
399 <item>
400 <layout class="QHBoxLayout" name="buttonFaceButtonsXHorizontalLayout">
401 <item>
402 <widget class="QLabel" name="labelX">
403 <property name="text">
404 <string>X:</string>
405 </property> 330 </property>
406 </widget> 331 <property name="bottomMargin">
407 </item> 332 <number>0</number>
408 </layout>
409 </item>
410 <item>
411 <widget class="QPushButton" name="buttonX">
412 <property name="text">
413 <string/>
414 </property>
415 </widget>
416 </item>
417 </layout>
418 </item>
419 <item row="1" column="1">
420 <layout class="QVBoxLayout" name="buttonFaceButtonsYVerticalLayout">
421 <item>
422 <layout class="QHBoxLayout" name="buttonFaceButtonsYHorizontalLayout">
423 <item>
424 <widget class="QLabel" name="labelY">
425 <property name="text">
426 <string>Y:</string>
427 </property> 333 </property>
428 </widget> 334 <item>
429 </item> 335 <widget class="QWidget" name="buttonLStickUpWidget" native="true">
430 </layout> 336 <layout class="QHBoxLayout" name="horizontalLayout_20">
431 </item> 337 <property name="spacing">
432 <item> 338 <number>0</number>
433 <widget class="QPushButton" name="buttonY"> 339 </property>
434 <property name="text"> 340 <property name="leftMargin">
435 <string/> 341 <number>0</number>
436 </property> 342 </property>
437 </widget> 343 <property name="topMargin">
438 </item> 344 <number>0</number>
439 </layout> 345 </property>
440 </item> 346 <property name="rightMargin">
441 </layout> 347 <number>0</number>
442 </widget> 348 </property>
443 </item> 349 <property name="bottomMargin">
444 <item row="5" column="0" colspan="2"> 350 <number>0</number>
445 <widget class="QGroupBox" name="controller_color"> 351 </property>
446 <property name="title"> 352 <item>
447 <string>Controller Color</string> 353 <spacer name="horizontalSpacerLStickUpLeft">
448 </property> 354 <property name="orientation">
449 <layout class="QGridLayout" name="gridLayout_10" columnstretch="0,0,0,0,0,0,0"> 355 <enum>Qt::Horizontal</enum>
450 <item row="0" column="0"> 356 </property>
451 <spacer name="horizontalSpacer_2"> 357 <property name="sizeHint" stdset="0">
452 <property name="orientation"> 358 <size>
453 <enum>Qt::Horizontal</enum> 359 <width>20</width>
454 </property> 360 <height>20</height>
455 <property name="sizeHint" stdset="0"> 361 </size>
456 <size> 362 </property>
457 <width>40</width> 363 </spacer>
458 <height>20</height> 364 </item>
459 </size> 365 <item alignment="Qt::AlignHCenter">
460 </property> 366 <widget class="QGroupBox" name="buttonLStickUpGroup">
461 </spacer> 367 <property name="title">
462 </item> 368 <string>Up</string>
463 <item row="0" column="1"> 369 </property>
464 <widget class="QLabel" name="left_body_label"> 370 <property name="alignment">
465 <property name="text"> 371 <set>Qt::AlignCenter</set>
466 <string>Left Body</string> 372 </property>
467 </property> 373 <property name="flat">
468 </widget> 374 <bool>false</bool>
469 </item> 375 </property>
470 <item row="0" column="6"> 376 <layout class="QVBoxLayout" name="buttonLStickUpVerticalLayout">
471 <spacer name="horizontalSpacer_3"> 377 <property name="spacing">
472 <property name="orientation"> 378 <number>3</number>
473 <enum>Qt::Horizontal</enum> 379 </property>
474 </property> 380 <property name="leftMargin">
475 <property name="sizeHint" stdset="0"> 381 <number>3</number>
476 <size> 382 </property>
477 <width>40</width> 383 <property name="topMargin">
478 <height>20</height> 384 <number>3</number>
479 </size> 385 </property>
480 </property> 386 <property name="rightMargin">
481 </spacer> 387 <number>3</number>
482 </item> 388 </property>
483 <item row="1" column="1"> 389 <property name="bottomMargin">
484 <widget class="QLabel" name="left_buttons_label"> 390 <number>3</number>
485 <property name="minimumSize"> 391 </property>
486 <size> 392 <item>
487 <width>90</width> 393 <widget class="QPushButton" name="buttonLStickUp">
488 <height>0</height> 394 <property name="minimumSize">
489 </size> 395 <size>
490 </property> 396 <width>57</width>
491 <property name="text"> 397 <height>0</height>
492 <string>Left Buttons</string> 398 </size>
493 </property> 399 </property>
494 </widget> 400 <property name="maximumSize">
495 </item> 401 <size>
496 <item row="1" column="5"> 402 <width>55</width>
497 <widget class="QPushButton" name="right_buttons_button"> 403 <height>16777215</height>
498 <property name="sizePolicy"> 404 </size>
499 <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> 405 </property>
500 <horstretch>0</horstretch> 406 <property name="styleSheet">
501 <verstretch>0</verstretch> 407 <string notr="true">min-width: 55px;</string>
502 </sizepolicy> 408 </property>
503 </property> 409 <property name="text">
504 <property name="minimumSize"> 410 <string>Up</string>
505 <size> 411 </property>
506 <width>32</width> 412 </widget>
507 <height>0</height> 413 </item>
508 </size> 414 </layout>
509 </property> 415 </widget>
510 <property name="maximumSize"> 416 </item>
511 <size> 417 <item>
512 <width>40</width> 418 <spacer name="horizontalSpacerLStickUpRight">
513 <height>16777215</height> 419 <property name="orientation">
514 </size> 420 <enum>Qt::Horizontal</enum>
515 </property> 421 </property>
516 <property name="text"> 422 <property name="sizeHint" stdset="0">
517 <string/> 423 <size>
518 </property> 424 <width>20</width>
519 </widget> 425 <height>20</height>
520 </item> 426 </size>
521 <item row="0" column="4"> 427 </property>
522 <widget class="QLabel" name="right_body_label"> 428 </spacer>
523 <property name="text"> 429 </item>
524 <string>Right Body</string> 430 </layout>
525 </property> 431 </widget>
526 </widget> 432 </item>
527 </item> 433 <item>
528 <item row="1" column="4"> 434 <layout class="QHBoxLayout" name="buttonLStickLeftRightHorizontaLayout">
529 <widget class="QLabel" name="right_buttons_label"> 435 <property name="spacing">
530 <property name="minimumSize"> 436 <number>3</number>
531 <size> 437 </property>
532 <width>90</width> 438 <item alignment="Qt::AlignHCenter">
533 <height>0</height> 439 <widget class="QGroupBox" name="buttonLStickLeftGroup">
534 </size> 440 <property name="title">
535 </property> 441 <string>Left</string>
536 <property name="text"> 442 </property>
537 <string>Right Buttons</string> 443 <property name="alignment">
538 </property> 444 <set>Qt::AlignCenter</set>
539 </widget> 445 </property>
540 </item> 446 <layout class="QVBoxLayout" name="buttonLStickLeftVerticalLayout">
541 <item row="1" column="2"> 447 <property name="spacing">
542 <widget class="QPushButton" name="left_buttons_button"> 448 <number>3</number>
543 <property name="sizePolicy"> 449 </property>
544 <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> 450 <property name="leftMargin">
545 <horstretch>0</horstretch> 451 <number>3</number>
546 <verstretch>0</verstretch> 452 </property>
547 </sizepolicy> 453 <property name="topMargin">
548 </property> 454 <number>3</number>
549 <property name="minimumSize"> 455 </property>
550 <size> 456 <property name="rightMargin">
551 <width>32</width> 457 <number>3</number>
552 <height>0</height> 458 </property>
553 </size> 459 <property name="bottomMargin">
554 </property> 460 <number>3</number>
555 <property name="maximumSize"> 461 </property>
556 <size> 462 <item>
557 <width>40</width> 463 <widget class="QPushButton" name="buttonLStickLeft">
558 <height>16777215</height> 464 <property name="minimumSize">
559 </size> 465 <size>
560 </property> 466 <width>57</width>
561 <property name="text"> 467 <height>0</height>
562 <string/> 468 </size>
563 </property> 469 </property>
564 </widget> 470 <property name="maximumSize">
565 </item> 471 <size>
566 <item row="0" column="2"> 472 <width>55</width>
567 <widget class="QPushButton" name="left_body_button"> 473 <height>16777215</height>
568 <property name="sizePolicy"> 474 </size>
569 <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> 475 </property>
570 <horstretch>0</horstretch> 476 <property name="styleSheet">
571 <verstretch>0</verstretch> 477 <string notr="true">min-width: 55px;</string>
572 </sizepolicy> 478 </property>
573 </property> 479 <property name="text">
574 <property name="minimumSize"> 480 <string>Left</string>
575 <size> 481 </property>
576 <width>32</width> 482 </widget>
577 <height>0</height> 483 </item>
578 </size> 484 </layout>
579 </property> 485 </widget>
580 <property name="maximumSize"> 486 </item>
581 <size> 487 <item alignment="Qt::AlignHCenter">
582 <width>40</width> 488 <widget class="QGroupBox" name="buttonLStickRightGroup">
583 <height>16777215</height> 489 <property name="title">
584 </size> 490 <string>Right</string>
585 </property> 491 </property>
586 <property name="text"> 492 <property name="alignment">
587 <string/> 493 <set>Qt::AlignCenter</set>
588 </property> 494 </property>
589 </widget> 495 <layout class="QVBoxLayout" name="buttonLStickRightVerticalLayout">
590 </item> 496 <property name="spacing">
591 <item row="0" column="5"> 497 <number>3</number>
592 <widget class="QPushButton" name="right_body_button"> 498 </property>
593 <property name="sizePolicy"> 499 <property name="leftMargin">
594 <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> 500 <number>3</number>
595 <horstretch>0</horstretch> 501 </property>
596 <verstretch>0</verstretch> 502 <property name="topMargin">
597 </sizepolicy> 503 <number>3</number>
598 </property> 504 </property>
599 <property name="minimumSize"> 505 <property name="rightMargin">
600 <size> 506 <number>3</number>
601 <width>32</width> 507 </property>
602 <height>0</height> 508 <property name="bottomMargin">
603 </size> 509 <number>3</number>
604 </property> 510 </property>
605 <property name="maximumSize"> 511 <item>
606 <size> 512 <widget class="QPushButton" name="buttonLStickRight">
607 <width>40</width> 513 <property name="minimumSize">
608 <height>16777215</height> 514 <size>
609 </size> 515 <width>57</width>
610 </property> 516 <height>0</height>
611 <property name="text"> 517 </size>
612 <string/> 518 </property>
613 </property> 519 <property name="maximumSize">
614 </widget> 520 <size>
615 </item> 521 <width>55</width>
616 <item row="0" column="3"> 522 <height>16777215</height>
617 <spacer name="horizontalSpacer_4"> 523 </size>
618 <property name="orientation"> 524 </property>
619 <enum>Qt::Horizontal</enum> 525 <property name="styleSheet">
620 </property> 526 <string notr="true">min-width: 55px;</string>
621 <property name="sizeType"> 527 </property>
622 <enum>QSizePolicy::Fixed</enum> 528 <property name="text">
623 </property> 529 <string>Right</string>
624 <property name="sizeHint" stdset="0"> 530 </property>
625 <size> 531 </widget>
626 <width>20</width> 532 </item>
627 <height>20</height> 533 </layout>
628 </size> 534 </widget>
629 </property> 535 </item>
630 </spacer> 536 </layout>
631 </item> 537 </item>
632 </layout> 538 <item>
633 </widget> 539 <widget class="QWidget" name="buttonLStickDownWidget" native="true">
634 </item> 540 <layout class="QHBoxLayout" name="horizontalLayout_22">
635 <item row="1" column="0"> 541 <property name="spacing">
636 <widget class="QGroupBox" name="LStick"> 542 <number>0</number>
637 <property name="title"> 543 </property>
638 <string>Left Stick</string> 544 <property name="leftMargin">
639 </property> 545 <number>0</number>
640 <property name="flat"> 546 </property>
641 <bool>false</bool> 547 <property name="topMargin">
642 </property> 548 <number>0</number>
643 <property name="checkable"> 549 </property>
644 <bool>false</bool> 550 <property name="rightMargin">
645 </property> 551 <number>0</number>
646 <layout class="QGridLayout" name="gridLayout_4"> 552 </property>
647 <item row="1" column="1"> 553 <property name="bottomMargin">
648 <layout class="QVBoxLayout" name="buttonLStickUpVerticalLayout"> 554 <number>0</number>
649 <item> 555 </property>
650 <layout class="QHBoxLayout" name="buttonLStickUpHorizontalLayout"> 556 <item>
651 <item> 557 <spacer name="horizontalSpacerLStickDownLeft">
652 <widget class="QLabel" name="labelLStickUp"> 558 <property name="orientation">
653 <property name="text"> 559 <enum>Qt::Horizontal</enum>
654 <string>Up:</string> 560 </property>
561 <property name="sizeHint" stdset="0">
562 <size>
563 <width>20</width>
564 <height>20</height>
565 </size>
566 </property>
567 </spacer>
568 </item>
569 <item alignment="Qt::AlignHCenter">
570 <widget class="QGroupBox" name="buttonLStickDownGroup">
571 <property name="title">
572 <string>Down</string>
573 </property>
574 <property name="alignment">
575 <set>Qt::AlignCenter</set>
576 </property>
577 <layout class="QVBoxLayout" name="buttonLStickDownVerticalLayout">
578 <property name="spacing">
579 <number>3</number>
580 </property>
581 <property name="leftMargin">
582 <number>3</number>
583 </property>
584 <property name="topMargin">
585 <number>3</number>
586 </property>
587 <property name="rightMargin">
588 <number>3</number>
589 </property>
590 <property name="bottomMargin">
591 <number>3</number>
592 </property>
593 <item>
594 <widget class="QPushButton" name="buttonLStickDown">
595 <property name="minimumSize">
596 <size>
597 <width>57</width>
598 <height>0</height>
599 </size>
600 </property>
601 <property name="maximumSize">
602 <size>
603 <width>55</width>
604 <height>16777215</height>
605 </size>
606 </property>
607 <property name="styleSheet">
608 <string notr="true">min-width: 55px;</string>
609 </property>
610 <property name="text">
611 <string>Down</string>
612 </property>
613 </widget>
614 </item>
615 </layout>
616 </widget>
617 </item>
618 <item>
619 <spacer name="horizontalSpacerLStickDownRight">
620 <property name="orientation">
621 <enum>Qt::Horizontal</enum>
622 </property>
623 <property name="sizeHint" stdset="0">
624 <size>
625 <width>20</width>
626 <height>20</height>
627 </size>
628 </property>
629 </spacer>
630 </item>
631 </layout>
632 </widget>
633 </item>
634 <item>
635 <layout class="QHBoxLayout" name="buttonLStickPressedModifierHorizontalLayout">
636 <property name="spacing">
637 <number>3</number>
638 </property>
639 <item alignment="Qt::AlignHCenter">
640 <widget class="QGroupBox" name="buttonLStickPressedGroup">
641 <property name="title">
642 <string>Pressed</string>
643 </property>
644 <property name="alignment">
645 <set>Qt::AlignCenter</set>
646 </property>
647 <layout class="QVBoxLayout" name="buttonLStickPressedVerticalLayout" stretch="0">
648 <property name="spacing">
649 <number>3</number>
650 </property>
651 <property name="leftMargin">
652 <number>3</number>
653 </property>
654 <property name="topMargin">
655 <number>3</number>
656 </property>
657 <property name="rightMargin">
658 <number>3</number>
659 </property>
660 <property name="bottomMargin">
661 <number>3</number>
662 </property>
663 <item>
664 <widget class="QPushButton" name="buttonLStick">
665 <property name="minimumSize">
666 <size>
667 <width>57</width>
668 <height>0</height>
669 </size>
670 </property>
671 <property name="maximumSize">
672 <size>
673 <width>55</width>
674 <height>16777215</height>
675 </size>
676 </property>
677 <property name="styleSheet">
678 <string notr="true">min-width: 55px;</string>
679 </property>
680 <property name="text">
681 <string>Pressed</string>
682 </property>
683 </widget>
684 </item>
685 </layout>
686 </widget>
687 </item>
688 <item alignment="Qt::AlignHCenter">
689 <widget class="QGroupBox" name="buttonLStickModGroup">
690 <property name="title">
691 <string>Modifier</string>
692 </property>
693 <property name="alignment">
694 <set>Qt::AlignCenter</set>
695 </property>
696 <layout class="QVBoxLayout" name="buttonLStickModVerticalLayout">
697 <property name="spacing">
698 <number>3</number>
699 </property>
700 <property name="leftMargin">
701 <number>3</number>
702 </property>
703 <property name="topMargin">
704 <number>3</number>
705 </property>
706 <property name="rightMargin">
707 <number>3</number>
708 </property>
709 <property name="bottomMargin">
710 <number>3</number>
711 </property>
712 <item>
713 <widget class="QPushButton" name="buttonLStickMod">
714 <property name="minimumSize">
715 <size>
716 <width>57</width>
717 <height>0</height>
718 </size>
719 </property>
720 <property name="maximumSize">
721 <size>
722 <width>55</width>
723 <height>16777215</height>
724 </size>
725 </property>
726 <property name="styleSheet">
727 <string notr="true">min-width: 55px;</string>
728 </property>
729 <property name="text">
730 <string>Modifier</string>
731 </property>
732 </widget>
733 </item>
734 </layout>
735 </widget>
736 </item>
737 <item>
738 <widget class="QGroupBox" name="buttonLStickRangeGroup">
739 <property name="title">
740 <string>Range</string>
741 </property>
742 <layout class="QHBoxLayout" name="buttonLStickRangeGroupHorizontalLayout">
743 <property name="spacing">
744 <number>3</number>
745 </property>
746 <property name="leftMargin">
747 <number>3</number>
748 </property>
749 <property name="topMargin">
750 <number>3</number>
751 </property>
752 <property name="rightMargin">
753 <number>3</number>
754 </property>
755 <property name="bottomMargin">
756 <number>3</number>
757 </property>
758 <item>
759 <widget class="QSpinBox" name="spinboxLStickRange">
760 <property name="minimumSize">
761 <size>
762 <width>55</width>
763 <height>21</height>
764 </size>
765 </property>
766 <property name="maximumSize">
767 <size>
768 <width>55</width>
769 <height>16777215</height>
770 </size>
771 </property>
772 <property name="suffix">
773 <string>%</string>
774 </property>
775 <property name="minimum">
776 <number>50</number>
777 </property>
778 <property name="maximum">
779 <number>150</number>
780 </property>
781 <property name="value">
782 <number>100</number>
783 </property>
784 </widget>
785 </item>
786 </layout>
787 </widget>
788 </item>
789 </layout>
790 </item>
791 <item>
792 <layout class="QVBoxLayout" name="sliderLStickDeadzoneModifierRangeVerticalLayout">
793 <property name="spacing">
794 <number>3</number>
795 </property>
796 <property name="sizeConstraint">
797 <enum>QLayout::SetDefaultConstraint</enum>
798 </property>
799 <property name="leftMargin">
800 <number>0</number>
801 </property>
802 <property name="topMargin">
803 <number>2</number>
804 </property>
805 <property name="rightMargin">
806 <number>0</number>
807 </property>
808 <property name="bottomMargin">
809 <number>3</number>
810 </property>
811 <item>
812 <layout class="QHBoxLayout" name="sliderLStickDeadzoneHorizontalLayout">
813 <item>
814 <widget class="QLabel" name="labelLStickDeadzone">
815 <property name="text">
816 <string>Deadzone: 0%</string>
817 </property>
818 <property name="alignment">
819 <set>Qt::AlignHCenter</set>
820 </property>
821 </widget>
822 </item>
823 </layout>
824 </item>
825 <item>
826 <widget class="QSlider" name="sliderLStickDeadzone">
827 <property name="maximum">
828 <number>100</number>
829 </property>
830 <property name="orientation">
831 <enum>Qt::Horizontal</enum>
832 </property>
833 </widget>
834 </item>
835 <item>
836 <layout class="QHBoxLayout" name="sliderLStickModifierRangeHorizontalLayout">
837 <item>
838 <widget class="QLabel" name="labelLStickModifierRange">
839 <property name="text">
840 <string>Modifier Range: 0%</string>
841 </property>
842 <property name="alignment">
843 <set>Qt::AlignHCenter</set>
844 </property>
845 </widget>
846 </item>
847 </layout>
848 </item>
849 <item>
850 <widget class="QSlider" name="sliderLStickModifierRange">
851 <property name="maximum">
852 <number>100</number>
853 </property>
854 <property name="orientation">
855 <enum>Qt::Horizontal</enum>
856 </property>
857 </widget>
858 </item>
859 </layout>
860 </item>
861 </layout>
862 </widget>
863 </item>
864 <item>
865 <spacer name="verticalSpacerBottomLeft">
866 <property name="orientation">
867 <enum>Qt::Vertical</enum>
868 </property>
869 <property name="sizeHint" stdset="0">
870 <size>
871 <width>20</width>
872 <height>0</height>
873 </size>
874 </property>
875 </spacer>
876 </item>
877 <item>
878 <widget class="QGroupBox" name="Dpad">
879 <property name="sizePolicy">
880 <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
881 <horstretch>0</horstretch>
882 <verstretch>0</verstretch>
883 </sizepolicy>
884 </property>
885 <property name="title">
886 <string>D-Pad</string>
887 </property>
888 <property name="flat">
889 <bool>false</bool>
890 </property>
891 <property name="checkable">
892 <bool>false</bool>
893 </property>
894 <layout class="QVBoxLayout" name="verticalLayout_5">
895 <property name="spacing">
896 <number>0</number>
655 </property> 897 </property>
656 </widget> 898 <property name="leftMargin">
657 </item> 899 <number>3</number>
658 </layout>
659 </item>
660 <item>
661 <widget class="QPushButton" name="buttonLStickUp">
662 <property name="text">
663 <string/>
664 </property>
665 </widget>
666 </item>
667 </layout>
668 </item>
669 <item row="0" column="2">
670 <layout class="QVBoxLayout" name="buttonLStickRightVerticalLayout">
671 <item>
672 <layout class="QHBoxLayout" name="buttonLStickRightHorizontalLayout">
673 <item>
674 <widget class="QLabel" name="labelLStickRight">
675 <property name="text">
676 <string>Right:</string>
677 </property> 900 </property>
678 </widget> 901 <property name="topMargin">
679 </item> 902 <number>0</number>
680 </layout>
681 </item>
682 <item>
683 <widget class="QPushButton" name="buttonLStickRight">
684 <property name="text">
685 <string/>
686 </property>
687 </widget>
688 </item>
689 </layout>
690 </item>
691 <item row="4" column="1" colspan="2">
692 <widget class="QPushButton" name="buttonLStickAnalog">
693 <property name="text">
694 <string>Set Analog Stick</string>
695 </property>
696 </widget>
697 </item>
698 <item row="0" column="1">
699 <layout class="QVBoxLayout" name="buttonLStickLeftVerticalLayout">
700 <item>
701 <layout class="QHBoxLayout" name="buttonLStickLeftHorizontalLayout_2">
702 <item>
703 <widget class="QLabel" name="labelLStickLeft">
704 <property name="text">
705 <string>Left:</string>
706 </property> 903 </property>
707 </widget> 904 <property name="rightMargin">
708 </item> 905 <number>3</number>
709 </layout>
710 </item>
711 <item>
712 <widget class="QPushButton" name="buttonLStickLeft">
713 <property name="text">
714 <string/>
715 </property>
716 </widget>
717 </item>
718 </layout>
719 </item>
720 <item row="1" column="2">
721 <layout class="QVBoxLayout" name="buttonLStickDownVerticalLayout">
722 <item>
723 <layout class="QHBoxLayout" name="buttonLStickDownHorizontalLayout">
724 <item>
725 <widget class="QLabel" name="labelLStickDown">
726 <property name="text">
727 <string>Down:</string>
728 </property> 906 </property>
729 </widget> 907 <property name="bottomMargin">
730 </item> 908 <number>3</number>
731 </layout>
732 </item>
733 <item>
734 <widget class="QPushButton" name="buttonLStickDown">
735 <property name="text">
736 <string/>
737 </property>
738 </widget>
739 </item>
740 </layout>
741 </item>
742 <item row="3" column="2">
743 <layout class="QVBoxLayout" name="buttonLStickModVerticalLayout">
744 <item>
745 <layout class="QHBoxLayout" name="buttonLStickModHorizontalLayout">
746 <item>
747 <widget class="QLabel" name="labelLStickMod">
748 <property name="text">
749 <string>Modifier:</string>
750 </property> 909 </property>
751 </widget> 910 <item>
752 </item> 911 <widget class="QWidget" name="buttonDpadUpWidget" native="true">
753 </layout> 912 <layout class="QHBoxLayout" name="horizontalLayout_23">
754 </item> 913 <property name="spacing">
755 <item> 914 <number>0</number>
756 <widget class="QPushButton" name="buttonLStickMod"> 915 </property>
757 <property name="text"> 916 <property name="leftMargin">
758 <string/> 917 <number>0</number>
759 </property> 918 </property>
760 </widget> 919 <property name="topMargin">
761 </item> 920 <number>0</number>
762 </layout> 921 </property>
922 <property name="rightMargin">
923 <number>0</number>
924 </property>
925 <property name="bottomMargin">
926 <number>0</number>
927 </property>
928 <item>
929 <spacer name="horizontalSpacerDpadUpLeft">
930 <property name="orientation">
931 <enum>Qt::Horizontal</enum>
932 </property>
933 <property name="sizeHint" stdset="0">
934 <size>
935 <width>20</width>
936 <height>20</height>
937 </size>
938 </property>
939 </spacer>
940 </item>
941 <item alignment="Qt::AlignHCenter">
942 <widget class="QGroupBox" name="buttonDpadUpGroup">
943 <property name="title">
944 <string>Up</string>
945 </property>
946 <property name="alignment">
947 <set>Qt::AlignCenter</set>
948 </property>
949 <layout class="QVBoxLayout" name="buttonDpadUpVerticalLayout">
950 <property name="spacing">
951 <number>3</number>
952 </property>
953 <property name="leftMargin">
954 <number>3</number>
955 </property>
956 <property name="topMargin">
957 <number>3</number>
958 </property>
959 <property name="rightMargin">
960 <number>3</number>
961 </property>
962 <property name="bottomMargin">
963 <number>3</number>
964 </property>
965 <item>
966 <widget class="QPushButton" name="buttonDpadUp">
967 <property name="minimumSize">
968 <size>
969 <width>57</width>
970 <height>0</height>
971 </size>
972 </property>
973 <property name="maximumSize">
974 <size>
975 <width>55</width>
976 <height>16777215</height>
977 </size>
978 </property>
979 <property name="styleSheet">
980 <string notr="true">min-width: 55px;</string>
981 </property>
982 <property name="text">
983 <string>Up</string>
984 </property>
985 </widget>
986 </item>
987 </layout>
988 </widget>
989 </item>
990 <item>
991 <spacer name="horizontalSpacerDpadUpRight">
992 <property name="orientation">
993 <enum>Qt::Horizontal</enum>
994 </property>
995 <property name="sizeHint" stdset="0">
996 <size>
997 <width>20</width>
998 <height>20</height>
999 </size>
1000 </property>
1001 </spacer>
1002 </item>
1003 </layout>
1004 </widget>
1005 </item>
1006 <item>
1007 <layout class="QHBoxLayout" name="buttonDpadLeftRightHorizontalLayout">
1008 <property name="spacing">
1009 <number>3</number>
1010 </property>
1011 <item alignment="Qt::AlignHCenter">
1012 <widget class="QGroupBox" name="buttonDpadLeftGroup">
1013 <property name="title">
1014 <string>Left</string>
1015 </property>
1016 <property name="alignment">
1017 <set>Qt::AlignCenter</set>
1018 </property>
1019 <layout class="QVBoxLayout" name="buttonDpadLeftVerticalLayout">
1020 <property name="spacing">
1021 <number>3</number>
1022 </property>
1023 <property name="leftMargin">
1024 <number>3</number>
1025 </property>
1026 <property name="topMargin">
1027 <number>3</number>
1028 </property>
1029 <property name="rightMargin">
1030 <number>3</number>
1031 </property>
1032 <property name="bottomMargin">
1033 <number>3</number>
1034 </property>
1035 <item>
1036 <widget class="QPushButton" name="buttonDpadLeft">
1037 <property name="minimumSize">
1038 <size>
1039 <width>57</width>
1040 <height>0</height>
1041 </size>
1042 </property>
1043 <property name="maximumSize">
1044 <size>
1045 <width>55</width>
1046 <height>16777215</height>
1047 </size>
1048 </property>
1049 <property name="styleSheet">
1050 <string notr="true">min-width: 55px;</string>
1051 </property>
1052 <property name="text">
1053 <string>Left</string>
1054 </property>
1055 </widget>
1056 </item>
1057 </layout>
1058 </widget>
1059 </item>
1060 <item alignment="Qt::AlignHCenter">
1061 <widget class="QGroupBox" name="buttonDpadRightGroup">
1062 <property name="title">
1063 <string>Right</string>
1064 </property>
1065 <property name="alignment">
1066 <set>Qt::AlignCenter</set>
1067 </property>
1068 <layout class="QVBoxLayout" name="buttonDpadRightVerticalLayout">
1069 <property name="spacing">
1070 <number>3</number>
1071 </property>
1072 <property name="leftMargin">
1073 <number>3</number>
1074 </property>
1075 <property name="topMargin">
1076 <number>3</number>
1077 </property>
1078 <property name="rightMargin">
1079 <number>3</number>
1080 </property>
1081 <property name="bottomMargin">
1082 <number>3</number>
1083 </property>
1084 <item>
1085 <widget class="QPushButton" name="buttonDpadRight">
1086 <property name="minimumSize">
1087 <size>
1088 <width>57</width>
1089 <height>0</height>
1090 </size>
1091 </property>
1092 <property name="maximumSize">
1093 <size>
1094 <width>55</width>
1095 <height>16777215</height>
1096 </size>
1097 </property>
1098 <property name="styleSheet">
1099 <string notr="true">min-width: 55px;</string>
1100 </property>
1101 <property name="text">
1102 <string>Right</string>
1103 </property>
1104 </widget>
1105 </item>
1106 </layout>
1107 </widget>
1108 </item>
1109 </layout>
1110 </item>
1111 <item>
1112 <widget class="QWidget" name="buttonDpadDownWidget" native="true">
1113 <layout class="QHBoxLayout" name="horizontalLayout_24">
1114 <property name="spacing">
1115 <number>0</number>
1116 </property>
1117 <property name="leftMargin">
1118 <number>0</number>
1119 </property>
1120 <property name="topMargin">
1121 <number>0</number>
1122 </property>
1123 <property name="rightMargin">
1124 <number>0</number>
1125 </property>
1126 <property name="bottomMargin">
1127 <number>0</number>
1128 </property>
1129 <item>
1130 <spacer name="horizontalSpacerDpadDownLeft">
1131 <property name="orientation">
1132 <enum>Qt::Horizontal</enum>
1133 </property>
1134 <property name="sizeHint" stdset="0">
1135 <size>
1136 <width>20</width>
1137 <height>20</height>
1138 </size>
1139 </property>
1140 </spacer>
1141 </item>
1142 <item alignment="Qt::AlignHCenter">
1143 <widget class="QGroupBox" name="buttonDpadDownGroup">
1144 <property name="title">
1145 <string>Down</string>
1146 </property>
1147 <property name="alignment">
1148 <set>Qt::AlignCenter</set>
1149 </property>
1150 <layout class="QVBoxLayout" name="buttonDpadDownVerticalLayout">
1151 <property name="spacing">
1152 <number>3</number>
1153 </property>
1154 <property name="leftMargin">
1155 <number>3</number>
1156 </property>
1157 <property name="topMargin">
1158 <number>3</number>
1159 </property>
1160 <property name="rightMargin">
1161 <number>3</number>
1162 </property>
1163 <property name="bottomMargin">
1164 <number>3</number>
1165 </property>
1166 <item>
1167 <widget class="QPushButton" name="buttonDpadDown">
1168 <property name="minimumSize">
1169 <size>
1170 <width>57</width>
1171 <height>0</height>
1172 </size>
1173 </property>
1174 <property name="maximumSize">
1175 <size>
1176 <width>55</width>
1177 <height>16777215</height>
1178 </size>
1179 </property>
1180 <property name="styleSheet">
1181 <string notr="true">min-width: 55px;</string>
1182 </property>
1183 <property name="text">
1184 <string>Down</string>
1185 </property>
1186 </widget>
1187 </item>
1188 </layout>
1189 </widget>
1190 </item>
1191 <item>
1192 <spacer name="horizontalSpacerDpadDownRight">
1193 <property name="orientation">
1194 <enum>Qt::Horizontal</enum>
1195 </property>
1196 <property name="sizeHint" stdset="0">
1197 <size>
1198 <width>20</width>
1199 <height>20</height>
1200 </size>
1201 </property>
1202 </spacer>
1203 </item>
1204 </layout>
1205 </widget>
1206 </item>
1207 </layout>
1208 </widget>
1209 </item>
1210 <item>
1211 <spacer name="verticalSpacerBottomLeft_2">
1212 <property name="orientation">
1213 <enum>Qt::Vertical</enum>
1214 </property>
1215 <property name="sizeHint" stdset="0">
1216 <size>
1217 <width>20</width>
1218 <height>0</height>
1219 </size>
1220 </property>
1221 </spacer>
1222 </item>
1223 </layout>
1224 </widget>
763 </item> 1225 </item>
764 <item row="3" column="1"> 1226 <item>
765 <layout class="QVBoxLayout" name="buttonLStickPressedVerticalLayout" stretch="0,0"> 1227 <widget class="QWidget" name="bottomMiddle" native="true">
766 <item> 1228 <layout class="QVBoxLayout" stretch="0,0,0">
767 <layout class="QHBoxLayout" name="buttonLStickPressedHorizontalLayout"> 1229 <property name="spacing">
768 <item> 1230 <number>6</number>
769 <widget class="QLabel" name="labelLStickPressed"> 1231 </property>
770 <property name="text"> 1232 <property name="leftMargin">
771 <string>Pressed:</string> 1233 <number>0</number>
1234 </property>
1235 <property name="topMargin">
1236 <number>0</number>
1237 </property>
1238 <property name="rightMargin">
1239 <number>0</number>
1240 </property>
1241 <property name="bottomMargin">
1242 <number>0</number>
1243 </property>
1244 <item>
1245 <layout class="QHBoxLayout" name="shoulderButtons">
1246 <property name="spacing">
1247 <number>3</number>
1248 </property>
1249 <item>
1250 <widget class="QWidget" name="buttonShoulderButtonsLeft" native="true">
1251 <layout class="QVBoxLayout" name="buttonShoulderButtonsLeftVerticalLayout">
1252 <property name="spacing">
1253 <number>0</number>
1254 </property>
1255 <property name="leftMargin">
1256 <number>0</number>
1257 </property>
1258 <property name="topMargin">
1259 <number>0</number>
1260 </property>
1261 <property name="rightMargin">
1262 <number>0</number>
1263 </property>
1264 <property name="bottomMargin">
1265 <number>0</number>
1266 </property>
1267 <item>
1268 <widget class="QGroupBox" name="buttonShoulderButtonsButtonLGroup">
1269 <property name="title">
1270 <string>L</string>
1271 </property>
1272 <property name="alignment">
1273 <set>Qt::AlignCenter</set>
1274 </property>
1275 <layout class="QVBoxLayout" name="buttonShoulderButtonsLVerticalLayout">
1276 <property name="spacing">
1277 <number>3</number>
1278 </property>
1279 <property name="leftMargin">
1280 <number>3</number>
1281 </property>
1282 <property name="topMargin">
1283 <number>3</number>
1284 </property>
1285 <property name="rightMargin">
1286 <number>3</number>
1287 </property>
1288 <property name="bottomMargin">
1289 <number>3</number>
1290 </property>
1291 <item>
1292 <widget class="QPushButton" name="buttonL">
1293 <property name="minimumSize">
1294 <size>
1295 <width>57</width>
1296 <height>0</height>
1297 </size>
1298 </property>
1299 <property name="maximumSize">
1300 <size>
1301 <width>55</width>
1302 <height>16777215</height>
1303 </size>
1304 </property>
1305 <property name="styleSheet">
1306 <string notr="true">min-width: 55px;</string>
1307 </property>
1308 <property name="text">
1309 <string>L</string>
1310 </property>
1311 </widget>
1312 </item>
1313 </layout>
1314 </widget>
1315 </item>
1316 <item>
1317 <widget class="QGroupBox" name="buttonShoulderButtonsButtonZLGroup">
1318 <property name="title">
1319 <string>ZL</string>
1320 </property>
1321 <property name="alignment">
1322 <set>Qt::AlignCenter</set>
1323 </property>
1324 <layout class="QVBoxLayout" name="buttonShoulderButtonsZLVerticalLayout">
1325 <property name="spacing">
1326 <number>3</number>
1327 </property>
1328 <property name="leftMargin">
1329 <number>3</number>
1330 </property>
1331 <property name="topMargin">
1332 <number>3</number>
1333 </property>
1334 <property name="rightMargin">
1335 <number>3</number>
1336 </property>
1337 <property name="bottomMargin">
1338 <number>3</number>
1339 </property>
1340 <item>
1341 <widget class="QPushButton" name="buttonZL">
1342 <property name="minimumSize">
1343 <size>
1344 <width>57</width>
1345 <height>0</height>
1346 </size>
1347 </property>
1348 <property name="maximumSize">
1349 <size>
1350 <width>55</width>
1351 <height>16777215</height>
1352 </size>
1353 </property>
1354 <property name="styleSheet">
1355 <string notr="true">min-width: 55px;</string>
1356 </property>
1357 <property name="text">
1358 <string>ZL</string>
1359 </property>
1360 </widget>
1361 </item>
1362 </layout>
1363 </widget>
1364 </item>
1365 </layout>
1366 </widget>
1367 </item>
1368 <item>
1369 <widget class="QWidget" name="horizontalSpacerShoulderButtonsWidget" native="true">
1370 <layout class="QHBoxLayout" name="horizontalSpacerShoulderButtonsWidgetLayout">
1371 <property name="spacing">
1372 <number>0</number>
1373 </property>
1374 <property name="leftMargin">
1375 <number>0</number>
1376 </property>
1377 <property name="topMargin">
1378 <number>0</number>
1379 </property>
1380 <property name="rightMargin">
1381 <number>0</number>
1382 </property>
1383 <property name="bottomMargin">
1384 <number>0</number>
1385 </property>
1386 <item>
1387 <spacer name="horizontalSpacerShoulderButtons1">
1388 <property name="orientation">
1389 <enum>Qt::Horizontal</enum>
1390 </property>
1391 <property name="sizeHint" stdset="0">
1392 <size>
1393 <width>0</width>
1394 <height>20</height>
1395 </size>
1396 </property>
1397 </spacer>
1398 </item>
1399 </layout>
1400 </widget>
1401 </item>
1402 <item>
1403 <widget class="QWidget" name="buttonMiscButtonsMinusScreenshot" native="true">
1404 <layout class="QVBoxLayout" name="buttonMiscButtonsMinusScreenshotVerticalLayout">
1405 <property name="spacing">
1406 <number>0</number>
1407 </property>
1408 <property name="leftMargin">
1409 <number>0</number>
1410 </property>
1411 <property name="topMargin">
1412 <number>0</number>
1413 </property>
1414 <property name="rightMargin">
1415 <number>0</number>
1416 </property>
1417 <property name="bottomMargin">
1418 <number>0</number>
1419 </property>
1420 <item alignment="Qt::AlignHCenter">
1421 <widget class="QGroupBox" name="buttonMiscButtonsMinusGroup">
1422 <property name="title">
1423 <string>Minus</string>
1424 </property>
1425 <property name="alignment">
1426 <set>Qt::AlignCenter</set>
1427 </property>
1428 <layout class="QVBoxLayout" name="buttonMiscMinusVerticalLayout">
1429 <property name="spacing">
1430 <number>3</number>
1431 </property>
1432 <property name="leftMargin">
1433 <number>3</number>
1434 </property>
1435 <property name="topMargin">
1436 <number>3</number>
1437 </property>
1438 <property name="rightMargin">
1439 <number>3</number>
1440 </property>
1441 <property name="bottomMargin">
1442 <number>3</number>
1443 </property>
1444 <item>
1445 <widget class="QPushButton" name="buttonMinus">
1446 <property name="minimumSize">
1447 <size>
1448 <width>57</width>
1449 <height>0</height>
1450 </size>
1451 </property>
1452 <property name="maximumSize">
1453 <size>
1454 <width>55</width>
1455 <height>16777215</height>
1456 </size>
1457 </property>
1458 <property name="styleSheet">
1459 <string notr="true">min-width: 55px;</string>
1460 </property>
1461 <property name="text">
1462 <string>Minus</string>
1463 </property>
1464 </widget>
1465 </item>
1466 </layout>
1467 </widget>
1468 </item>
1469 <item alignment="Qt::AlignHCenter">
1470 <widget class="QGroupBox" name="buttonMiscButtonsScreenshotGroup">
1471 <property name="title">
1472 <string>Capture</string>
1473 </property>
1474 <property name="alignment">
1475 <set>Qt::AlignCenter</set>
1476 </property>
1477 <layout class="QVBoxLayout" name="buttonMiscScrCapVerticalLayout">
1478 <property name="spacing">
1479 <number>3</number>
1480 </property>
1481 <property name="leftMargin">
1482 <number>3</number>
1483 </property>
1484 <property name="topMargin">
1485 <number>3</number>
1486 </property>
1487 <property name="rightMargin">
1488 <number>3</number>
1489 </property>
1490 <property name="bottomMargin">
1491 <number>3</number>
1492 </property>
1493 <item>
1494 <widget class="QPushButton" name="buttonScreenshot">
1495 <property name="minimumSize">
1496 <size>
1497 <width>57</width>
1498 <height>0</height>
1499 </size>
1500 </property>
1501 <property name="maximumSize">
1502 <size>
1503 <width>55</width>
1504 <height>16777215</height>
1505 </size>
1506 </property>
1507 <property name="styleSheet">
1508 <string notr="true">min-width: 55px;</string>
1509 </property>
1510 <property name="text">
1511 <string>Capture</string>
1512 </property>
1513 </widget>
1514 </item>
1515 </layout>
1516 </widget>
1517 </item>
1518 </layout>
1519 </widget>
1520 </item>
1521 <item>
1522 <widget class="QWidget" name="buttonMiscButtonsPlusHome" native="true">
1523 <layout class="QVBoxLayout" name="buttonMiscButtonsPlusHomeVerticalLayout">
1524 <property name="spacing">
1525 <number>0</number>
1526 </property>
1527 <property name="leftMargin">
1528 <number>0</number>
1529 </property>
1530 <property name="topMargin">
1531 <number>0</number>
1532 </property>
1533 <property name="rightMargin">
1534 <number>0</number>
1535 </property>
1536 <property name="bottomMargin">
1537 <number>0</number>
1538 </property>
1539 <item alignment="Qt::AlignHCenter">
1540 <widget class="QGroupBox" name="buttonMiscButtonsPlusGroup">
1541 <property name="title">
1542 <string>Plus</string>
1543 </property>
1544 <property name="alignment">
1545 <set>Qt::AlignCenter</set>
1546 </property>
1547 <layout class="QVBoxLayout" name="buttonMiscPlusVerticalLayout">
1548 <property name="spacing">
1549 <number>3</number>
1550 </property>
1551 <property name="leftMargin">
1552 <number>3</number>
1553 </property>
1554 <property name="topMargin">
1555 <number>3</number>
1556 </property>
1557 <property name="rightMargin">
1558 <number>3</number>
1559 </property>
1560 <property name="bottomMargin">
1561 <number>3</number>
1562 </property>
1563 <item>
1564 <widget class="QPushButton" name="buttonPlus">
1565 <property name="minimumSize">
1566 <size>
1567 <width>57</width>
1568 <height>0</height>
1569 </size>
1570 </property>
1571 <property name="maximumSize">
1572 <size>
1573 <width>55</width>
1574 <height>16777215</height>
1575 </size>
1576 </property>
1577 <property name="styleSheet">
1578 <string notr="true">min-width: 55px;</string>
1579 </property>
1580 <property name="text">
1581 <string>Plus</string>
1582 </property>
1583 </widget>
1584 </item>
1585 </layout>
1586 </widget>
1587 </item>
1588 <item alignment="Qt::AlignHCenter">
1589 <widget class="QGroupBox" name="buttonMiscButtonsHomeGroup">
1590 <property name="title">
1591 <string>Home</string>
1592 </property>
1593 <property name="alignment">
1594 <set>Qt::AlignCenter</set>
1595 </property>
1596 <layout class="QVBoxLayout" name="buttonMiscHomeVerticalLayout">
1597 <property name="spacing">
1598 <number>3</number>
1599 </property>
1600 <property name="leftMargin">
1601 <number>3</number>
1602 </property>
1603 <property name="topMargin">
1604 <number>3</number>
1605 </property>
1606 <property name="rightMargin">
1607 <number>3</number>
1608 </property>
1609 <property name="bottomMargin">
1610 <number>3</number>
1611 </property>
1612 <item>
1613 <widget class="QPushButton" name="buttonHome">
1614 <property name="minimumSize">
1615 <size>
1616 <width>57</width>
1617 <height>0</height>
1618 </size>
1619 </property>
1620 <property name="maximumSize">
1621 <size>
1622 <width>55</width>
1623 <height>16777215</height>
1624 </size>
1625 </property>
1626 <property name="styleSheet">
1627 <string notr="true">min-width: 55px;</string>
1628 </property>
1629 <property name="text">
1630 <string>Home</string>
1631 </property>
1632 </widget>
1633 </item>
1634 </layout>
1635 </widget>
1636 </item>
1637 </layout>
1638 </widget>
1639 </item>
1640 <item>
1641 <widget class="QWidget" name="horizontalSpacerShoulderButtonsWidget3" native="true">
1642 <layout class="QHBoxLayout" name="horizontalSpacerShoulderButtonsWidget3Layout">
1643 <property name="spacing">
1644 <number>0</number>
1645 </property>
1646 <property name="leftMargin">
1647 <number>0</number>
1648 </property>
1649 <property name="topMargin">
1650 <number>0</number>
1651 </property>
1652 <property name="rightMargin">
1653 <number>0</number>
1654 </property>
1655 <property name="bottomMargin">
1656 <number>0</number>
1657 </property>
1658 <item>
1659 <spacer name="horizontalSpacerShoulderButtons2">
1660 <property name="orientation">
1661 <enum>Qt::Horizontal</enum>
1662 </property>
1663 <property name="sizeHint" stdset="0">
1664 <size>
1665 <width>0</width>
1666 <height>20</height>
1667 </size>
1668 </property>
1669 </spacer>
1670 </item>
1671 </layout>
1672 </widget>
1673 </item>
1674 <item>
1675 <widget class="QWidget" name="buttonShoulderButtonsRight" native="true">
1676 <layout class="QVBoxLayout" name="buttonShoulderButtonsRightVerticalLayout">
1677 <property name="spacing">
1678 <number>0</number>
1679 </property>
1680 <property name="leftMargin">
1681 <number>0</number>
1682 </property>
1683 <property name="topMargin">
1684 <number>0</number>
1685 </property>
1686 <property name="rightMargin">
1687 <number>0</number>
1688 </property>
1689 <property name="bottomMargin">
1690 <number>0</number>
1691 </property>
1692 <item>
1693 <widget class="QGroupBox" name="buttonShoulderButtonsRGroup">
1694 <property name="title">
1695 <string>R</string>
1696 </property>
1697 <property name="alignment">
1698 <set>Qt::AlignCenter</set>
1699 </property>
1700 <layout class="QVBoxLayout" name="buttonShoulderButtonsRVerticalLayout">
1701 <property name="spacing">
1702 <number>3</number>
1703 </property>
1704 <property name="leftMargin">
1705 <number>3</number>
1706 </property>
1707 <property name="topMargin">
1708 <number>3</number>
1709 </property>
1710 <property name="rightMargin">
1711 <number>3</number>
1712 </property>
1713 <property name="bottomMargin">
1714 <number>3</number>
1715 </property>
1716 <item>
1717 <widget class="QPushButton" name="buttonR">
1718 <property name="minimumSize">
1719 <size>
1720 <width>57</width>
1721 <height>0</height>
1722 </size>
1723 </property>
1724 <property name="maximumSize">
1725 <size>
1726 <width>55</width>
1727 <height>16777215</height>
1728 </size>
1729 </property>
1730 <property name="styleSheet">
1731 <string notr="true">min-width: 55px;</string>
1732 </property>
1733 <property name="text">
1734 <string>R</string>
1735 </property>
1736 </widget>
1737 </item>
1738 </layout>
1739 </widget>
1740 </item>
1741 <item>
1742 <widget class="QGroupBox" name="buttonShoulderButtonsZRGroup">
1743 <property name="title">
1744 <string>ZR</string>
1745 </property>
1746 <property name="alignment">
1747 <set>Qt::AlignCenter</set>
1748 </property>
1749 <layout class="QVBoxLayout" name="buttonShoulderButtonsZRVerticalLayout">
1750 <property name="spacing">
1751 <number>3</number>
1752 </property>
1753 <property name="leftMargin">
1754 <number>3</number>
1755 </property>
1756 <property name="topMargin">
1757 <number>3</number>
1758 </property>
1759 <property name="rightMargin">
1760 <number>3</number>
1761 </property>
1762 <property name="bottomMargin">
1763 <number>3</number>
1764 </property>
1765 <item>
1766 <widget class="QPushButton" name="buttonZR">
1767 <property name="minimumSize">
1768 <size>
1769 <width>57</width>
1770 <height>0</height>
1771 </size>
1772 </property>
1773 <property name="maximumSize">
1774 <size>
1775 <width>55</width>
1776 <height>16777215</height>
1777 </size>
1778 </property>
1779 <property name="styleSheet">
1780 <string notr="true">min-width: 55px;</string>
1781 </property>
1782 <property name="text">
1783 <string>ZR</string>
1784 </property>
1785 </widget>
1786 </item>
1787 </layout>
1788 </widget>
1789 </item>
1790 </layout>
1791 </widget>
1792 </item>
1793 <item>
1794 <widget class="QWidget" name="horizontalSpacerShoulderButtonsWidget2" native="true">
1795 <layout class="QHBoxLayout" name="horizontalSpacerShoulderButtonsWidget2Layout">
1796 <property name="spacing">
1797 <number>0</number>
1798 </property>
1799 <property name="leftMargin">
1800 <number>0</number>
1801 </property>
1802 <property name="topMargin">
1803 <number>0</number>
1804 </property>
1805 <property name="rightMargin">
1806 <number>0</number>
1807 </property>
1808 <property name="bottomMargin">
1809 <number>0</number>
1810 </property>
1811 <item>
1812 <spacer name="horizontalSpacerShoulderButtons3">
1813 <property name="orientation">
1814 <enum>Qt::Horizontal</enum>
1815 </property>
1816 <property name="sizeHint" stdset="0">
1817 <size>
1818 <width>0</width>
1819 <height>20</height>
1820 </size>
1821 </property>
1822 </spacer>
1823 </item>
1824 </layout>
1825 </widget>
1826 </item>
1827 <item>
1828 <widget class="QWidget" name="buttonShoulderButtonsSLSR" native="true">
1829 <layout class="QVBoxLayout" name="buttonShoulderButtonsSLSRVerticalLayout">
1830 <property name="spacing">
1831 <number>0</number>
1832 </property>
1833 <property name="leftMargin">
1834 <number>0</number>
1835 </property>
1836 <property name="topMargin">
1837 <number>0</number>
1838 </property>
1839 <property name="rightMargin">
1840 <number>0</number>
1841 </property>
1842 <property name="bottomMargin">
1843 <number>0</number>
1844 </property>
1845 <item alignment="Qt::AlignHCenter">
1846 <widget class="QGroupBox" name="buttonShoulderButtonsSLGroup">
1847 <property name="title">
1848 <string>SL</string>
1849 </property>
1850 <property name="alignment">
1851 <set>Qt::AlignCenter</set>
1852 </property>
1853 <layout class="QVBoxLayout" name="buttonShoulderButtonsSLVerticalLayout">
1854 <property name="spacing">
1855 <number>3</number>
1856 </property>
1857 <property name="leftMargin">
1858 <number>3</number>
1859 </property>
1860 <property name="topMargin">
1861 <number>3</number>
1862 </property>
1863 <property name="rightMargin">
1864 <number>3</number>
1865 </property>
1866 <property name="bottomMargin">
1867 <number>3</number>
1868 </property>
1869 <item>
1870 <widget class="QPushButton" name="buttonSL">
1871 <property name="minimumSize">
1872 <size>
1873 <width>57</width>
1874 <height>0</height>
1875 </size>
1876 </property>
1877 <property name="maximumSize">
1878 <size>
1879 <width>55</width>
1880 <height>16777215</height>
1881 </size>
1882 </property>
1883 <property name="styleSheet">
1884 <string notr="true">min-width: 55px;</string>
1885 </property>
1886 <property name="text">
1887 <string>SL</string>
1888 </property>
1889 </widget>
1890 </item>
1891 </layout>
1892 </widget>
1893 </item>
1894 <item alignment="Qt::AlignHCenter">
1895 <widget class="QGroupBox" name="buttonShoulderButtonsSRGroup">
1896 <property name="title">
1897 <string>SR</string>
1898 </property>
1899 <property name="alignment">
1900 <set>Qt::AlignCenter</set>
1901 </property>
1902 <layout class="QVBoxLayout" name="buttonShoulderButtonsSRVerticalLayout">
1903 <property name="spacing">
1904 <number>3</number>
1905 </property>
1906 <property name="leftMargin">
1907 <number>3</number>
1908 </property>
1909 <property name="topMargin">
1910 <number>3</number>
1911 </property>
1912 <property name="rightMargin">
1913 <number>3</number>
1914 </property>
1915 <property name="bottomMargin">
1916 <number>3</number>
1917 </property>
1918 <item>
1919 <widget class="QPushButton" name="buttonSR">
1920 <property name="minimumSize">
1921 <size>
1922 <width>57</width>
1923 <height>0</height>
1924 </size>
1925 </property>
1926 <property name="maximumSize">
1927 <size>
1928 <width>55</width>
1929 <height>16777215</height>
1930 </size>
1931 </property>
1932 <property name="styleSheet">
1933 <string notr="true">min-width: 55px;</string>
1934 </property>
1935 <property name="text">
1936 <string>SR</string>
1937 </property>
1938 </widget>
1939 </item>
1940 </layout>
1941 </widget>
1942 </item>
1943 </layout>
1944 </widget>
1945 </item>
1946 </layout>
1947 </item>
1948 <item>
1949 <widget class="QFrame" name="controllerFrame">
1950 <property name="sizePolicy">
1951 <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
1952 <horstretch>0</horstretch>
1953 <verstretch>0</verstretch>
1954 </sizepolicy>
1955 </property>
1956 <property name="font">
1957 <font>
1958 <weight>75</weight>
1959 <bold>true</bold>
1960 </font>
1961 </property>
1962 <property name="styleSheet">
1963 <string notr="true">image: url(:/controller/pro);</string>
1964 </property>
1965 <layout class="QVBoxLayout" name="verticalLayout_4">
1966 <property name="leftMargin">
1967 <number>0</number>
772 </property> 1968 </property>
773 </widget> 1969 <property name="topMargin">
774 </item> 1970 <number>0</number>
775 </layout>
776 </item>
777 <item>
778 <widget class="QPushButton" name="buttonLStick">
779 <property name="text">
780 <string/>
781 </property>
782 </widget>
783 </item>
784 </layout>
785 </item>
786 <item row="5" column="1" colspan="2">
787 <layout class="QVBoxLayout" name="sliderLStickDeadzoneAndModifierVerticalLayout">
788 <property name="sizeConstraint">
789 <enum>QLayout::SetDefaultConstraint</enum>
790 </property>
791 <item>
792 <layout class="QHBoxLayout" name="sliderLStickDeadzoneAndModifierHorizontalLayout">
793 <item>
794 <widget class="QLabel" name="labelLStickDeadzoneAndModifier">
795 <property name="text">
796 <string>Deadzone: 0</string>
797 </property> 1971 </property>
798 <property name="alignment"> 1972 <property name="rightMargin">
799 <enum>Qt::AlignHCenter</enum> 1973 <number>0</number>
800 </property> 1974 </property>
801 </widget> 1975 <property name="bottomMargin">
802 </item> 1976 <number>0</number>
803 </layout>
804 </item>
805 <item>
806 <widget class="QSlider" name="sliderLStickDeadzoneAndModifier">
807 <property name="orientation">
808 <enum>Qt::Horizontal</enum>
809 </property>
810 </widget>
811 </item>
812 </layout>
813 </item>
814 <item row="6" column="1">
815 <spacer name="LStick_verticalSpacer">
816 <property name="orientation">
817 <enum>Qt::Vertical</enum>
818 </property>
819 <property name="sizeHint" stdset="0">
820 <size>
821 <width>0</width>
822 <height>0</height>
823 </size>
824 </property>
825 </spacer>
826 </item>
827 </layout>
828 </widget>
829 </item>
830 <item row="3" column="0">
831 <widget class="QGroupBox" name="shoulderButtons">
832 <property name="title">
833 <string>Shoulder Buttons</string>
834 </property>
835 <property name="flat">
836 <bool>false</bool>
837 </property>
838 <property name="checkable">
839 <bool>false</bool>
840 </property>
841 <layout class="QGridLayout" name="gridLayout_3">
842 <item row="0" column="0">
843 <layout class="QVBoxLayout" name="buttonShoulderButtonsLVerticalLayout">
844 <item>
845 <layout class="QHBoxLayout" name="buttonShoulderButtonsLHorizontalLayout">
846 <item>
847 <widget class="QLabel" name="labelL">
848 <property name="text">
849 <string>L:</string>
850 </property> 1977 </property>
851 </widget> 1978 </layout>
852 </item> 1979 </widget>
853 </layout> 1980 </item>
854 </item> 1981 <item>
855 <item> 1982 <layout class="QHBoxLayout" name="miscButtons">
856 <widget class="QPushButton" name="buttonL"> 1983 <property name="spacing">
857 <property name="text"> 1984 <number>3</number>
858 <string/> 1985 </property>
859 </property> 1986 <item>
860 </widget> 1987 <spacer name="horizontalSpacerMiscButtons1">
861 </item> 1988 <property name="orientation">
862 </layout> 1989 <enum>Qt::Horizontal</enum>
1990 </property>
1991 <property name="sizeHint" stdset="0">
1992 <size>
1993 <width>40</width>
1994 <height>0</height>
1995 </size>
1996 </property>
1997 </spacer>
1998 </item>
1999 <item>
2000 <spacer name="horizontalSpacerMiscButtons4">
2001 <property name="orientation">
2002 <enum>Qt::Horizontal</enum>
2003 </property>
2004 <property name="sizeHint" stdset="0">
2005 <size>
2006 <width>40</width>
2007 <height>0</height>
2008 </size>
2009 </property>
2010 </spacer>
2011 </item>
2012 </layout>
2013 </item>
2014 </layout>
2015 </widget>
863 </item> 2016 </item>
864 <item row="0" column="1"> 2017 <item>
865 <layout class="QVBoxLayout" name="buttonShoulderButtonsRVerticalLayout"> 2018 <widget class="QWidget" name="bottomRight" native="true">
866 <item> 2019 <layout class="QVBoxLayout" name="bottomRightLayout">
867 <layout class="QHBoxLayout" name="buttonShoulderButtonsRHorizontalLayout"> 2020 <property name="spacing">
868 <item> 2021 <number>0</number>
869 <widget class="QLabel" name="labelR"> 2022 </property>
870 <property name="text"> 2023 <property name="leftMargin">
871 <string>R:</string> 2024 <number>0</number>
2025 </property>
2026 <property name="topMargin">
2027 <number>0</number>
2028 </property>
2029 <property name="rightMargin">
2030 <number>0</number>
2031 </property>
2032 <property name="bottomMargin">
2033 <number>0</number>
2034 </property>
2035 <item>
2036 <widget class="QGroupBox" name="faceButtons">
2037 <property name="sizePolicy">
2038 <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
2039 <horstretch>0</horstretch>
2040 <verstretch>0</verstretch>
2041 </sizepolicy>
2042 </property>
2043 <property name="title">
2044 <string>Face Buttons</string>
2045 </property>
2046 <property name="flat">
2047 <bool>false</bool>
2048 </property>
2049 <property name="checkable">
2050 <bool>false</bool>
2051 </property>
2052 <layout class="QVBoxLayout" name="verticalLayout">
2053 <property name="spacing">
2054 <number>0</number>
872 </property> 2055 </property>
873 </widget> 2056 <property name="leftMargin">
874 </item> 2057 <number>3</number>
875 </layout>
876 </item>
877 <item>
878 <widget class="QPushButton" name="buttonR">
879 <property name="text">
880 <string/>
881 </property>
882 </widget>
883 </item>
884 </layout>
885 </item>
886 <item row="1" column="0">
887 <layout class="QVBoxLayout" name="buttonShoulderButtonsZLVerticalLayout">
888 <item>
889 <layout class="QHBoxLayout" name="buttonShoulderButtonsZLHorizontalLayout">
890 <item>
891 <widget class="QLabel" name="labelZL">
892 <property name="text">
893 <string>ZL:</string>
894 </property> 2058 </property>
895 </widget> 2059 <property name="topMargin">
896 </item> 2060 <number>0</number>
897 </layout>
898 </item>
899 <item>
900 <widget class="QPushButton" name="buttonZL">
901 <property name="text">
902 <string/>
903 </property>
904 </widget>
905 </item>
906 </layout>
907 </item>
908 <item row="1" column="1">
909 <layout class="QVBoxLayout" name="buttonShoulderButtonsZRVerticalLayout">
910 <item>
911 <layout class="QHBoxLayout" name="buttonShoulderButtonsZRHorizontalLayout">
912 <item>
913 <widget class="QLabel" name="labelZR">
914 <property name="text">
915 <string>ZR:</string>
916 </property> 2061 </property>
917 </widget> 2062 <property name="rightMargin">
918 </item> 2063 <number>3</number>
919 </layout>
920 </item>
921 <item>
922 <widget class="QPushButton" name="buttonZR">
923 <property name="text">
924 <string/>
925 </property>
926 </widget>
927 </item>
928 </layout>
929 </item>
930 <item row="0" column="2">
931 <layout class="QVBoxLayout" name="buttonShoulderButtonsSLVerticalLayout">
932 <item>
933 <layout class="QHBoxLayout" name="buttonShoulderButtonsSLHorizontalLayout">
934 <item>
935 <widget class="QLabel" name="labelSL">
936 <property name="text">
937 <string>SL:</string>
938 </property> 2064 </property>
939 </widget> 2065 <property name="bottomMargin">
940 </item> 2066 <number>3</number>
941 </layout>
942 </item>
943 <item>
944 <widget class="QPushButton" name="buttonSL">
945 <property name="text">
946 <string/>
947 </property>
948 </widget>
949 </item>
950 </layout>
951 </item>
952 <item row="1" column="2">
953 <layout class="QVBoxLayout" name="buttonShoulderButtonsSRVerticalLayout">
954 <item>
955 <layout class="QHBoxLayout" name="buttonShoulderButtonsSRHorizontalLayout">
956 <item>
957 <widget class="QLabel" name="labelSR">
958 <property name="text">
959 <string>SR:</string>
960 </property> 2067 </property>
961 </widget> 2068 <item>
962 </item> 2069 <widget class="QWidget" name="buttonFaceButtonsBWidget" native="true">
963 </layout> 2070 <layout class="QHBoxLayout" name="horizontalLayout_6">
964 </item> 2071 <property name="spacing">
965 <item> 2072 <number>0</number>
966 <widget class="QPushButton" name="buttonSR"> 2073 </property>
967 <property name="text"> 2074 <property name="leftMargin">
968 <string/> 2075 <number>0</number>
969 </property> 2076 </property>
970 </widget> 2077 <property name="topMargin">
971 </item> 2078 <number>0</number>
972 </layout> 2079 </property>
973 </item> 2080 <property name="rightMargin">
974 </layout> 2081 <number>0</number>
975 </widget> 2082 </property>
976 </item> 2083 <property name="bottomMargin">
977 <item row="3" column="1"> 2084 <number>0</number>
978 <widget class="QGroupBox" name="misc"> 2085 </property>
979 <property name="title"> 2086 <item>
980 <string>Misc.</string> 2087 <spacer name="horizontalSpacerBLeft">
981 </property> 2088 <property name="orientation">
982 <property name="flat"> 2089 <enum>Qt::Horizontal</enum>
983 <bool>false</bool> 2090 </property>
984 </property> 2091 <property name="sizeHint" stdset="0">
985 <property name="checkable"> 2092 <size>
986 <bool>false</bool> 2093 <width>20</width>
987 </property> 2094 <height>20</height>
988 <layout class="QGridLayout" name="gridLayout_6"> 2095 </size>
989 <item row="1" column="0"> 2096 </property>
990 <layout class="QVBoxLayout" name="buttonMiscMinusVerticalLayout"> 2097 </spacer>
991 <item> 2098 </item>
992 <layout class="QHBoxLayout" name="buttonMiscMinusHorizontalLayout"> 2099 <item alignment="Qt::AlignHCenter">
993 <item> 2100 <widget class="QGroupBox" name="buttonFaceButtonsXGroup">
994 <widget class="QLabel" name="labelMinus"> 2101 <property name="title">
995 <property name="text"> 2102 <string>X</string>
996 <string>Minus:</string> 2103 </property>
2104 <property name="alignment">
2105 <set>Qt::AlignCenter</set>
2106 </property>
2107 <layout class="QVBoxLayout" name="buttonFaceButtonsXVerticalLayout">
2108 <property name="spacing">
2109 <number>3</number>
2110 </property>
2111 <property name="leftMargin">
2112 <number>3</number>
2113 </property>
2114 <property name="topMargin">
2115 <number>3</number>
2116 </property>
2117 <property name="rightMargin">
2118 <number>3</number>
2119 </property>
2120 <property name="bottomMargin">
2121 <number>3</number>
2122 </property>
2123 <item>
2124 <widget class="QPushButton" name="buttonX">
2125 <property name="minimumSize">
2126 <size>
2127 <width>57</width>
2128 <height>0</height>
2129 </size>
2130 </property>
2131 <property name="maximumSize">
2132 <size>
2133 <width>55</width>
2134 <height>16777215</height>
2135 </size>
2136 </property>
2137 <property name="styleSheet">
2138 <string notr="true">min-width: 55px;</string>
2139 </property>
2140 <property name="text">
2141 <string>X</string>
2142 </property>
2143 </widget>
2144 </item>
2145 </layout>
2146 </widget>
2147 </item>
2148 <item>
2149 <spacer name="horizontalSpacerBRight">
2150 <property name="orientation">
2151 <enum>Qt::Horizontal</enum>
2152 </property>
2153 <property name="sizeHint" stdset="0">
2154 <size>
2155 <width>20</width>
2156 <height>20</height>
2157 </size>
2158 </property>
2159 </spacer>
2160 </item>
2161 </layout>
2162 </widget>
2163 </item>
2164 <item>
2165 <layout class="QHBoxLayout" name="buttonFaceButtonsYAHorizontalLayout">
2166 <property name="spacing">
2167 <number>3</number>
2168 </property>
2169 <item alignment="Qt::AlignHCenter">
2170 <widget class="QGroupBox" name="buttonFaceButtonsYGroup">
2171 <property name="title">
2172 <string>Y</string>
2173 </property>
2174 <property name="alignment">
2175 <set>Qt::AlignCenter</set>
2176 </property>
2177 <layout class="QVBoxLayout" name="buttonFaceButtonsYVerticalLayout">
2178 <property name="spacing">
2179 <number>3</number>
2180 </property>
2181 <property name="leftMargin">
2182 <number>3</number>
2183 </property>
2184 <property name="topMargin">
2185 <number>3</number>
2186 </property>
2187 <property name="rightMargin">
2188 <number>3</number>
2189 </property>
2190 <property name="bottomMargin">
2191 <number>3</number>
2192 </property>
2193 <item>
2194 <widget class="QPushButton" name="buttonY">
2195 <property name="minimumSize">
2196 <size>
2197 <width>57</width>
2198 <height>0</height>
2199 </size>
2200 </property>
2201 <property name="maximumSize">
2202 <size>
2203 <width>55</width>
2204 <height>16777215</height>
2205 </size>
2206 </property>
2207 <property name="styleSheet">
2208 <string notr="true">min-width: 55px;</string>
2209 </property>
2210 <property name="text">
2211 <string>Y</string>
2212 </property>
2213 </widget>
2214 </item>
2215 </layout>
2216 </widget>
2217 </item>
2218 <item alignment="Qt::AlignHCenter">
2219 <widget class="QGroupBox" name="buttonFaceButtonsAGroup">
2220 <property name="title">
2221 <string>A</string>
2222 </property>
2223 <property name="alignment">
2224 <set>Qt::AlignCenter</set>
2225 </property>
2226 <layout class="QVBoxLayout" name="buttonFaceButtonsAVerticalLayout">
2227 <property name="spacing">
2228 <number>3</number>
2229 </property>
2230 <property name="leftMargin">
2231 <number>3</number>
2232 </property>
2233 <property name="topMargin">
2234 <number>3</number>
2235 </property>
2236 <property name="rightMargin">
2237 <number>3</number>
2238 </property>
2239 <property name="bottomMargin">
2240 <number>3</number>
2241 </property>
2242 <item>
2243 <widget class="QPushButton" name="buttonA">
2244 <property name="minimumSize">
2245 <size>
2246 <width>57</width>
2247 <height>0</height>
2248 </size>
2249 </property>
2250 <property name="maximumSize">
2251 <size>
2252 <width>55</width>
2253 <height>16777215</height>
2254 </size>
2255 </property>
2256 <property name="styleSheet">
2257 <string notr="true">min-width: 55px;</string>
2258 </property>
2259 <property name="text">
2260 <string>A</string>
2261 </property>
2262 </widget>
2263 </item>
2264 </layout>
2265 </widget>
2266 </item>
2267 </layout>
2268 </item>
2269 <item>
2270 <widget class="QWidget" name="buttonFaceButtonsXWidget" native="true">
2271 <layout class="QHBoxLayout" name="horizontalLayout_10">
2272 <property name="spacing">
2273 <number>0</number>
2274 </property>
2275 <property name="leftMargin">
2276 <number>0</number>
2277 </property>
2278 <property name="topMargin">
2279 <number>0</number>
2280 </property>
2281 <property name="rightMargin">
2282 <number>0</number>
2283 </property>
2284 <property name="bottomMargin">
2285 <number>0</number>
2286 </property>
2287 <item>
2288 <spacer name="horizontalSpacerXLeft">
2289 <property name="orientation">
2290 <enum>Qt::Horizontal</enum>
2291 </property>
2292 <property name="sizeHint" stdset="0">
2293 <size>
2294 <width>20</width>
2295 <height>20</height>
2296 </size>
2297 </property>
2298 </spacer>
2299 </item>
2300 <item alignment="Qt::AlignHCenter">
2301 <widget class="QGroupBox" name="buttonFaceButtonsBWidget_2">
2302 <property name="title">
2303 <string>B</string>
2304 </property>
2305 <property name="alignment">
2306 <set>Qt::AlignCenter</set>
2307 </property>
2308 <layout class="QVBoxLayout" name="buttonFaceButtonsBVerticalLayout">
2309 <property name="spacing">
2310 <number>3</number>
2311 </property>
2312 <property name="leftMargin">
2313 <number>3</number>
2314 </property>
2315 <property name="topMargin">
2316 <number>3</number>
2317 </property>
2318 <property name="rightMargin">
2319 <number>3</number>
2320 </property>
2321 <property name="bottomMargin">
2322 <number>3</number>
2323 </property>
2324 <item>
2325 <widget class="QPushButton" name="buttonB">
2326 <property name="minimumSize">
2327 <size>
2328 <width>57</width>
2329 <height>0</height>
2330 </size>
2331 </property>
2332 <property name="maximumSize">
2333 <size>
2334 <width>55</width>
2335 <height>16777215</height>
2336 </size>
2337 </property>
2338 <property name="styleSheet">
2339 <string notr="true">min-width: 55px;</string>
2340 </property>
2341 <property name="text">
2342 <string>B</string>
2343 </property>
2344 </widget>
2345 </item>
2346 </layout>
2347 </widget>
2348 </item>
2349 <item>
2350 <spacer name="horizontalSpacerXRight">
2351 <property name="orientation">
2352 <enum>Qt::Horizontal</enum>
2353 </property>
2354 <property name="sizeHint" stdset="0">
2355 <size>
2356 <width>20</width>
2357 <height>20</height>
2358 </size>
2359 </property>
2360 </spacer>
2361 </item>
2362 </layout>
2363 </widget>
2364 </item>
2365 </layout>
2366 </widget>
2367 </item>
2368 <item>
2369 <spacer name="verticalSpacerBottomRight">
2370 <property name="orientation">
2371 <enum>Qt::Vertical</enum>
2372 </property>
2373 <property name="sizeHint" stdset="0">
2374 <size>
2375 <width>20</width>
2376 <height>0</height>
2377 </size>
2378 </property>
2379 </spacer>
2380 </item>
2381 <item>
2382 <widget class="QGroupBox" name="RStick">
2383 <property name="sizePolicy">
2384 <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
2385 <horstretch>0</horstretch>
2386 <verstretch>0</verstretch>
2387 </sizepolicy>
2388 </property>
2389 <property name="title">
2390 <string>Right Stick</string>
2391 </property>
2392 <property name="alignment">
2393 <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
2394 </property>
2395 <property name="flat">
2396 <bool>false</bool>
2397 </property>
2398 <property name="checkable">
2399 <bool>false</bool>
2400 </property>
2401 <layout class="QVBoxLayout" name="verticalLayout_2">
2402 <property name="spacing">
2403 <number>0</number>
997 </property> 2404 </property>
998 </widget> 2405 <property name="leftMargin">
999 </item> 2406 <number>3</number>
1000 </layout>
1001 </item>
1002 <item>
1003 <widget class="QPushButton" name="buttonMinus">
1004 <property name="text">
1005 <string/>
1006 </property>
1007 </widget>
1008 </item>
1009 </layout>
1010 </item>
1011 <item row="3" column="1">
1012 <spacer name="verticalSpacer_2">
1013 <property name="orientation">
1014 <enum>Qt::Vertical</enum>
1015 </property>
1016 <property name="sizeHint" stdset="0">
1017 <size>
1018 <width>20</width>
1019 <height>40</height>
1020 </size>
1021 </property>
1022 </spacer>
1023 </item>
1024 <item row="0" column="0">
1025 <layout class="QVBoxLayout" name="buttonMiscPlusVerticalLayout">
1026 <item>
1027 <layout class="QHBoxLayout" name="buttonMiscPlusHorizontalLayout">
1028 <item>
1029 <widget class="QLabel" name="labelPlus">
1030 <property name="text">
1031 <string>Plus:</string>
1032 </property> 2407 </property>
1033 </widget> 2408 <property name="topMargin">
1034 </item> 2409 <number>0</number>
1035 </layout>
1036 </item>
1037 <item>
1038 <widget class="QPushButton" name="buttonPlus">
1039 <property name="text">
1040 <string/>
1041 </property>
1042 </widget>
1043 </item>
1044 </layout>
1045 </item>
1046 <item row="0" column="1">
1047 <layout class="QVBoxLayout" name="buttonMiscHomeVerticalLayout">
1048 <item>
1049 <layout class="QHBoxLayout" name="buttonMiscHomeHorizontalLayout">
1050 <item>
1051 <widget class="QLabel" name="labelHome">
1052 <property name="text">
1053 <string>Home:</string>
1054 </property> 2410 </property>
1055 </widget> 2411 <property name="rightMargin">
1056 </item> 2412 <number>3</number>
1057 </layout>
1058 </item>
1059 <item>
1060 <widget class="QPushButton" name="buttonHome">
1061 <property name="text">
1062 <string/>
1063 </property>
1064 </widget>
1065 </item>
1066 </layout>
1067 </item>
1068 <item row="1" column="1">
1069 <layout class="QVBoxLayout" name="buttonMiscScrCapVerticalLayout">
1070 <item>
1071 <layout class="QHBoxLayout" name="buttonMiscScrCapHorizontalLayout">
1072 <item>
1073 <widget class="QLabel" name="labelScreenshot">
1074 <property name="text">
1075 <string>Screen Capture:</string>
1076 </property> 2413 </property>
1077 <property name="wordWrap"> 2414 <property name="bottomMargin">
1078 <bool>false</bool> 2415 <number>0</number>
1079 </property> 2416 </property>
1080 </widget> 2417 <item>
1081 </item> 2418 <widget class="QWidget" name="buttonRStickUpWidget" native="true">
1082 </layout> 2419 <property name="minimumSize">
1083 </item> 2420 <size>
1084 <item> 2421 <width>0</width>
1085 <widget class="QPushButton" name="buttonScreenshot"> 2422 <height>0</height>
1086 <property name="sizePolicy"> 2423 </size>
1087 <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> 2424 </property>
1088 <horstretch>0</horstretch> 2425 <layout class="QHBoxLayout" name="horizontalLayout_9">
1089 <verstretch>0</verstretch> 2426 <property name="spacing">
1090 </sizepolicy> 2427 <number>0</number>
1091 </property> 2428 </property>
1092 <property name="maximumSize"> 2429 <property name="leftMargin">
1093 <size> 2430 <number>0</number>
1094 <width>80</width> 2431 </property>
1095 <height>16777215</height> 2432 <property name="topMargin">
1096 </size> 2433 <number>0</number>
1097 </property> 2434 </property>
1098 <property name="text"> 2435 <property name="rightMargin">
1099 <string/> 2436 <number>0</number>
1100 </property> 2437 </property>
1101 </widget> 2438 <property name="bottomMargin">
1102 </item> 2439 <number>0</number>
1103 </layout> 2440 </property>
2441 <item>
2442 <spacer name="horizontalSpacerRStickUpLeft">
2443 <property name="orientation">
2444 <enum>Qt::Horizontal</enum>
2445 </property>
2446 <property name="sizeHint" stdset="0">
2447 <size>
2448 <width>20</width>
2449 <height>20</height>
2450 </size>
2451 </property>
2452 </spacer>
2453 </item>
2454 <item alignment="Qt::AlignHCenter">
2455 <widget class="QGroupBox" name="buttonRStickUpGroup">
2456 <property name="title">
2457 <string>Up</string>
2458 </property>
2459 <property name="alignment">
2460 <set>Qt::AlignCenter</set>
2461 </property>
2462 <layout class="QVBoxLayout" name="buttonRStickUpVerticalLayout">
2463 <property name="spacing">
2464 <number>3</number>
2465 </property>
2466 <property name="leftMargin">
2467 <number>3</number>
2468 </property>
2469 <property name="topMargin">
2470 <number>3</number>
2471 </property>
2472 <property name="rightMargin">
2473 <number>3</number>
2474 </property>
2475 <property name="bottomMargin">
2476 <number>3</number>
2477 </property>
2478 <item>
2479 <widget class="QPushButton" name="buttonRStickUp">
2480 <property name="minimumSize">
2481 <size>
2482 <width>57</width>
2483 <height>0</height>
2484 </size>
2485 </property>
2486 <property name="maximumSize">
2487 <size>
2488 <width>55</width>
2489 <height>16777215</height>
2490 </size>
2491 </property>
2492 <property name="styleSheet">
2493 <string notr="true">min-width: 55px;</string>
2494 </property>
2495 <property name="text">
2496 <string>Up</string>
2497 </property>
2498 </widget>
2499 </item>
2500 </layout>
2501 </widget>
2502 </item>
2503 <item>
2504 <spacer name="horizontalSpacerRStickUpRight">
2505 <property name="orientation">
2506 <enum>Qt::Horizontal</enum>
2507 </property>
2508 <property name="sizeHint" stdset="0">
2509 <size>
2510 <width>20</width>
2511 <height>20</height>
2512 </size>
2513 </property>
2514 </spacer>
2515 </item>
2516 </layout>
2517 </widget>
2518 </item>
2519 <item>
2520 <layout class="QHBoxLayout" name="buttonRStickLeftRightHorizontalLayout">
2521 <property name="spacing">
2522 <number>3</number>
2523 </property>
2524 <item alignment="Qt::AlignHCenter">
2525 <widget class="QGroupBox" name="buttonRStickLeftGroup">
2526 <property name="title">
2527 <string>Left</string>
2528 </property>
2529 <property name="alignment">
2530 <set>Qt::AlignCenter</set>
2531 </property>
2532 <layout class="QVBoxLayout" name="buttonRStickLeftVerticalLayout">
2533 <property name="spacing">
2534 <number>3</number>
2535 </property>
2536 <property name="leftMargin">
2537 <number>3</number>
2538 </property>
2539 <property name="topMargin">
2540 <number>3</number>
2541 </property>
2542 <property name="rightMargin">
2543 <number>3</number>
2544 </property>
2545 <property name="bottomMargin">
2546 <number>3</number>
2547 </property>
2548 <item>
2549 <widget class="QPushButton" name="buttonRStickLeft">
2550 <property name="minimumSize">
2551 <size>
2552 <width>57</width>
2553 <height>0</height>
2554 </size>
2555 </property>
2556 <property name="maximumSize">
2557 <size>
2558 <width>55</width>
2559 <height>16777215</height>
2560 </size>
2561 </property>
2562 <property name="styleSheet">
2563 <string notr="true">min-width: 55px;</string>
2564 </property>
2565 <property name="text">
2566 <string>Left</string>
2567 </property>
2568 </widget>
2569 </item>
2570 </layout>
2571 </widget>
2572 </item>
2573 <item alignment="Qt::AlignHCenter">
2574 <widget class="QGroupBox" name="buttonRStickRightGroup">
2575 <property name="title">
2576 <string>Right</string>
2577 </property>
2578 <property name="alignment">
2579 <set>Qt::AlignCenter</set>
2580 </property>
2581 <layout class="QVBoxLayout" name="buttonRStickRightVerticalLayout">
2582 <property name="spacing">
2583 <number>3</number>
2584 </property>
2585 <property name="leftMargin">
2586 <number>3</number>
2587 </property>
2588 <property name="topMargin">
2589 <number>3</number>
2590 </property>
2591 <property name="rightMargin">
2592 <number>3</number>
2593 </property>
2594 <property name="bottomMargin">
2595 <number>3</number>
2596 </property>
2597 <item>
2598 <widget class="QPushButton" name="buttonRStickRight">
2599 <property name="minimumSize">
2600 <size>
2601 <width>57</width>
2602 <height>0</height>
2603 </size>
2604 </property>
2605 <property name="maximumSize">
2606 <size>
2607 <width>55</width>
2608 <height>16777215</height>
2609 </size>
2610 </property>
2611 <property name="styleSheet">
2612 <string notr="true">min-width: 55px;</string>
2613 </property>
2614 <property name="text">
2615 <string>Right</string>
2616 </property>
2617 </widget>
2618 </item>
2619 </layout>
2620 </widget>
2621 </item>
2622 </layout>
2623 </item>
2624 <item>
2625 <widget class="QWidget" name="buttonRStickDownWidget" native="true">
2626 <layout class="QHBoxLayout" name="horizontalLayout_11">
2627 <property name="spacing">
2628 <number>0</number>
2629 </property>
2630 <property name="leftMargin">
2631 <number>0</number>
2632 </property>
2633 <property name="topMargin">
2634 <number>0</number>
2635 </property>
2636 <property name="rightMargin">
2637 <number>0</number>
2638 </property>
2639 <property name="bottomMargin">
2640 <number>0</number>
2641 </property>
2642 <item>
2643 <spacer name="horizontalSpacerRStickDownLeft">
2644 <property name="orientation">
2645 <enum>Qt::Horizontal</enum>
2646 </property>
2647 <property name="sizeHint" stdset="0">
2648 <size>
2649 <width>20</width>
2650 <height>20</height>
2651 </size>
2652 </property>
2653 </spacer>
2654 </item>
2655 <item>
2656 <widget class="QGroupBox" name="buttonRStickDownGroup">
2657 <property name="title">
2658 <string>Down</string>
2659 </property>
2660 <property name="alignment">
2661 <set>Qt::AlignCenter</set>
2662 </property>
2663 <layout class="QVBoxLayout" name="buttonRStickDownVerticalLayout">
2664 <property name="spacing">
2665 <number>3</number>
2666 </property>
2667 <property name="leftMargin">
2668 <number>3</number>
2669 </property>
2670 <property name="topMargin">
2671 <number>3</number>
2672 </property>
2673 <property name="rightMargin">
2674 <number>3</number>
2675 </property>
2676 <property name="bottomMargin">
2677 <number>3</number>
2678 </property>
2679 <item>
2680 <widget class="QPushButton" name="buttonRStickDown">
2681 <property name="minimumSize">
2682 <size>
2683 <width>57</width>
2684 <height>0</height>
2685 </size>
2686 </property>
2687 <property name="maximumSize">
2688 <size>
2689 <width>55</width>
2690 <height>16777215</height>
2691 </size>
2692 </property>
2693 <property name="styleSheet">
2694 <string notr="true">min-width: 55px;</string>
2695 </property>
2696 <property name="text">
2697 <string>Down</string>
2698 </property>
2699 </widget>
2700 </item>
2701 </layout>
2702 </widget>
2703 </item>
2704 <item>
2705 <spacer name="horizontalSpacerRStickDownRight">
2706 <property name="orientation">
2707 <enum>Qt::Horizontal</enum>
2708 </property>
2709 <property name="sizeHint" stdset="0">
2710 <size>
2711 <width>20</width>
2712 <height>20</height>
2713 </size>
2714 </property>
2715 </spacer>
2716 </item>
2717 </layout>
2718 </widget>
2719 </item>
2720 <item>
2721 <layout class="QHBoxLayout" name="buttonRStickPressedModifierHorizontalLayout">
2722 <property name="spacing">
2723 <number>3</number>
2724 </property>
2725 <item alignment="Qt::AlignHCenter">
2726 <widget class="QGroupBox" name="groupRStickPressed">
2727 <property name="title">
2728 <string>Pressed</string>
2729 </property>
2730 <property name="alignment">
2731 <set>Qt::AlignCenter</set>
2732 </property>
2733 <layout class="QVBoxLayout" name="buttonRStickPressedVerticalLayout">
2734 <property name="spacing">
2735 <number>3</number>
2736 </property>
2737 <property name="leftMargin">
2738 <number>3</number>
2739 </property>
2740 <property name="topMargin">
2741 <number>3</number>
2742 </property>
2743 <property name="rightMargin">
2744 <number>3</number>
2745 </property>
2746 <property name="bottomMargin">
2747 <number>3</number>
2748 </property>
2749 <item>
2750 <widget class="QPushButton" name="buttonRStick">
2751 <property name="minimumSize">
2752 <size>
2753 <width>57</width>
2754 <height>0</height>
2755 </size>
2756 </property>
2757 <property name="maximumSize">
2758 <size>
2759 <width>55</width>
2760 <height>16777215</height>
2761 </size>
2762 </property>
2763 <property name="styleSheet">
2764 <string notr="true">min-width: 55px;</string>
2765 </property>
2766 <property name="text">
2767 <string>Pressed</string>
2768 </property>
2769 </widget>
2770 </item>
2771 </layout>
2772 </widget>
2773 </item>
2774 <item alignment="Qt::AlignHCenter">
2775 <widget class="QGroupBox" name="buttonRStickModGroup">
2776 <property name="title">
2777 <string>Modifier</string>
2778 </property>
2779 <property name="alignment">
2780 <set>Qt::AlignCenter</set>
2781 </property>
2782 <layout class="QVBoxLayout" name="buttonRStickModVerticalLayout">
2783 <property name="spacing">
2784 <number>3</number>
2785 </property>
2786 <property name="leftMargin">
2787 <number>3</number>
2788 </property>
2789 <property name="topMargin">
2790 <number>3</number>
2791 </property>
2792 <property name="rightMargin">
2793 <number>3</number>
2794 </property>
2795 <property name="bottomMargin">
2796 <number>3</number>
2797 </property>
2798 <item>
2799 <widget class="QPushButton" name="buttonRStickMod">
2800 <property name="minimumSize">
2801 <size>
2802 <width>57</width>
2803 <height>0</height>
2804 </size>
2805 </property>
2806 <property name="maximumSize">
2807 <size>
2808 <width>55</width>
2809 <height>16777215</height>
2810 </size>
2811 </property>
2812 <property name="styleSheet">
2813 <string notr="true">min-width: 55px;</string>
2814 </property>
2815 <property name="text">
2816 <string>Modifier</string>
2817 </property>
2818 </widget>
2819 </item>
2820 </layout>
2821 </widget>
2822 </item>
2823 <item>
2824 <widget class="QGroupBox" name="buttonRStickRangeGroup">
2825 <property name="title">
2826 <string>Range</string>
2827 </property>
2828 <layout class="QHBoxLayout" name="buttonRStickRangeGroupHorizontalLayout">
2829 <property name="spacing">
2830 <number>3</number>
2831 </property>
2832 <property name="leftMargin">
2833 <number>3</number>
2834 </property>
2835 <property name="topMargin">
2836 <number>3</number>
2837 </property>
2838 <property name="rightMargin">
2839 <number>3</number>
2840 </property>
2841 <property name="bottomMargin">
2842 <number>3</number>
2843 </property>
2844 <item>
2845 <widget class="QSpinBox" name="spinboxRStickRange">
2846 <property name="minimumSize">
2847 <size>
2848 <width>55</width>
2849 <height>21</height>
2850 </size>
2851 </property>
2852 <property name="maximumSize">
2853 <size>
2854 <width>55</width>
2855 <height>16777215</height>
2856 </size>
2857 </property>
2858 <property name="suffix">
2859 <string>%</string>
2860 </property>
2861 <property name="minimum">
2862 <number>50</number>
2863 </property>
2864 <property name="maximum">
2865 <number>150</number>
2866 </property>
2867 <property name="value">
2868 <number>100</number>
2869 </property>
2870 </widget>
2871 </item>
2872 </layout>
2873 </widget>
2874 </item>
2875 </layout>
2876 </item>
2877 <item>
2878 <layout class="QVBoxLayout" name="sliderRStickDeadzoneModifierRangeVerticalLayout">
2879 <property name="spacing">
2880 <number>3</number>
2881 </property>
2882 <property name="leftMargin">
2883 <number>0</number>
2884 </property>
2885 <property name="topMargin">
2886 <number>2</number>
2887 </property>
2888 <property name="rightMargin">
2889 <number>0</number>
2890 </property>
2891 <property name="bottomMargin">
2892 <number>3</number>
2893 </property>
2894 <item>
2895 <layout class="QHBoxLayout" name="sliderRStickDeadzoneHorizontalLayout">
2896 <item>
2897 <widget class="QLabel" name="labelRStickDeadzone">
2898 <property name="text">
2899 <string>Deadzone: 0%</string>
2900 </property>
2901 <property name="alignment">
2902 <set>Qt::AlignHCenter</set>
2903 </property>
2904 </widget>
2905 </item>
2906 </layout>
2907 </item>
2908 <item>
2909 <widget class="QSlider" name="sliderRStickDeadzone">
2910 <property name="maximum">
2911 <number>100</number>
2912 </property>
2913 <property name="orientation">
2914 <enum>Qt::Horizontal</enum>
2915 </property>
2916 </widget>
2917 </item>
2918 <item>
2919 <layout class="QHBoxLayout" name="sliderRStickModifierRangeHorizontalLayout">
2920 <item>
2921 <widget class="QLabel" name="labelRStickModifierRange">
2922 <property name="text">
2923 <string>Modifier Range: 0%</string>
2924 </property>
2925 <property name="alignment">
2926 <set>Qt::AlignHCenter</set>
2927 </property>
2928 </widget>
2929 </item>
2930 </layout>
2931 </item>
2932 <item>
2933 <widget class="QSlider" name="sliderRStickModifierRange">
2934 <property name="maximum">
2935 <number>100</number>
2936 </property>
2937 <property name="orientation">
2938 <enum>Qt::Horizontal</enum>
2939 </property>
2940 </widget>
2941 </item>
2942 </layout>
2943 </item>
2944 </layout>
2945 </widget>
2946 </item>
2947 <item>
2948 <spacer name="verticalSpacerBottomRight_2">
2949 <property name="orientation">
2950 <enum>Qt::Vertical</enum>
2951 </property>
2952 <property name="sizeHint" stdset="0">
2953 <size>
2954 <width>20</width>
2955 <height>0</height>
2956 </size>
2957 </property>
2958 </spacer>
2959 </item>
2960 </layout>
2961 </widget>
1104 </item> 2962 </item>
1105 </layout> 2963 </layout>
1106 </widget> 2964 </widget>
1107 </item> 2965 </item>
1108 </layout> 2966 </layout>
1109 </item> 2967 </item>
1110 <item>
1111 <spacer name="verticalSpacer">
1112 <property name="orientation">
1113 <enum>Qt::Vertical</enum>
1114 </property>
1115 <property name="sizeHint" stdset="0">
1116 <size>
1117 <width>20</width>
1118 <height>40</height>
1119 </size>
1120 </property>
1121 </spacer>
1122 </item>
1123 <item>
1124 <layout class="QHBoxLayout" name="horizontalLayout"/>
1125 </item>
1126 <item>
1127 <layout class="QHBoxLayout" name="horizontalLayout_2">
1128 <item>
1129 <widget class="QPushButton" name="buttonClearAll">
1130 <property name="sizePolicy">
1131 <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
1132 <horstretch>0</horstretch>
1133 <verstretch>0</verstretch>
1134 </sizepolicy>
1135 </property>
1136 <property name="sizeIncrement">
1137 <size>
1138 <width>0</width>
1139 <height>0</height>
1140 </size>
1141 </property>
1142 <property name="baseSize">
1143 <size>
1144 <width>0</width>
1145 <height>0</height>
1146 </size>
1147 </property>
1148 <property name="layoutDirection">
1149 <enum>Qt::LeftToRight</enum>
1150 </property>
1151 <property name="text">
1152 <string>Clear All</string>
1153 </property>
1154 </widget>
1155 </item>
1156 <item>
1157 <widget class="QPushButton" name="buttonRestoreDefaults">
1158 <property name="sizePolicy">
1159 <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
1160 <horstretch>0</horstretch>
1161 <verstretch>0</verstretch>
1162 </sizepolicy>
1163 </property>
1164 <property name="sizeIncrement">
1165 <size>
1166 <width>0</width>
1167 <height>0</height>
1168 </size>
1169 </property>
1170 <property name="baseSize">
1171 <size>
1172 <width>0</width>
1173 <height>0</height>
1174 </size>
1175 </property>
1176 <property name="layoutDirection">
1177 <enum>Qt::LeftToRight</enum>
1178 </property>
1179 <property name="text">
1180 <string>Restore Defaults</string>
1181 </property>
1182 </widget>
1183 </item>
1184 <item>
1185 <spacer name="horizontalSpacer">
1186 <property name="orientation">
1187 <enum>Qt::Horizontal</enum>
1188 </property>
1189 <property name="sizeHint" stdset="0">
1190 <size>
1191 <width>40</width>
1192 <height>20</height>
1193 </size>
1194 </property>
1195 </spacer>
1196 </item>
1197 <item>
1198 <widget class="QDialogButtonBox" name="buttonBox">
1199 <property name="standardButtons">
1200 <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
1201 </property>
1202 </widget>
1203 </item>
1204 </layout>
1205 </item>
1206 </layout> 2968 </layout>
1207 </widget> 2969 </widget>
1208 <resources/> 2970 <resources>
1209 <connections> 2971 <include location="../../../dist/icons/controller/controller.qrc"/>
1210 <connection> 2972 </resources>
1211 <sender>buttonBox</sender> 2973 <connections/>
1212 <signal>accepted()</signal>
1213 <receiver>ConfigureInputPlayer</receiver>
1214 <slot>accept()</slot>
1215 <hints>
1216 <hint type="sourcelabel">
1217 <x>371</x>
1218 <y>730</y>
1219 </hint>
1220 <hint type="destinationlabel">
1221 <x>229</x>
1222 <y>375</y>
1223 </hint>
1224 </hints>
1225 </connection>
1226 <connection>
1227 <sender>buttonBox</sender>
1228 <signal>rejected()</signal>
1229 <receiver>ConfigureInputPlayer</receiver>
1230 <slot>reject()</slot>
1231 <hints>
1232 <hint type="sourcelabel">
1233 <x>371</x>
1234 <y>730</y>
1235 </hint>
1236 <hint type="destinationlabel">
1237 <x>229</x>
1238 <y>375</y>
1239 </hint>
1240 </hints>
1241 </connection>
1242 </connections>
1243</ui> 2974</ui>
diff --git a/src/yuzu/configuration/configure_input_simple.cpp b/src/yuzu/configuration/configure_input_simple.cpp
deleted file mode 100644
index 0e0e8f113..000000000
--- a/src/yuzu/configuration/configure_input_simple.cpp
+++ /dev/null
@@ -1,152 +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 <array>
6#include <tuple>
7
8#include "ui_configure_input_simple.h"
9#include "yuzu/configuration/configure_input.h"
10#include "yuzu/configuration/configure_input_player.h"
11#include "yuzu/configuration/configure_input_simple.h"
12#include "yuzu/uisettings.h"
13
14namespace {
15
16template <typename Dialog, typename... Args>
17void CallConfigureDialog(ConfigureInputSimple* caller, Args&&... args) {
18 caller->ApplyConfiguration();
19 Dialog dialog(caller, std::forward<Args>(args)...);
20
21 const auto res = dialog.exec();
22 if (res == QDialog::Accepted) {
23 dialog.ApplyConfiguration();
24 }
25}
26
27// OnProfileSelect functions should (when applicable):
28// - Set controller types
29// - Set controller enabled
30// - Set docked mode
31// - Set advanced controller config/enabled (i.e. debug, kbd, mouse, touch)
32//
33// OnProfileSelect function should NOT however:
34// - Reset any button mappings
35// - Open any dialogs
36// - Block in any way
37
38constexpr std::size_t PLAYER_0_INDEX = 0;
39constexpr std::size_t HANDHELD_INDEX = 8;
40
41void HandheldOnProfileSelect() {
42 Settings::values.players[HANDHELD_INDEX].connected = true;
43 Settings::values.players[HANDHELD_INDEX].type = Settings::ControllerType::DualJoycon;
44
45 for (std::size_t player = 0; player < HANDHELD_INDEX; ++player) {
46 Settings::values.players[player].connected = false;
47 }
48
49 Settings::values.use_docked_mode = false;
50 Settings::values.keyboard_enabled = false;
51 Settings::values.mouse_enabled = false;
52 Settings::values.debug_pad_enabled = false;
53 Settings::values.touchscreen.enabled = true;
54}
55
56void DualJoyconsDockedOnProfileSelect() {
57 Settings::values.players[PLAYER_0_INDEX].connected = true;
58 Settings::values.players[PLAYER_0_INDEX].type = Settings::ControllerType::DualJoycon;
59
60 for (std::size_t player = 1; player <= HANDHELD_INDEX; ++player) {
61 Settings::values.players[player].connected = false;
62 }
63
64 Settings::values.use_docked_mode = true;
65 Settings::values.keyboard_enabled = false;
66 Settings::values.mouse_enabled = false;
67 Settings::values.debug_pad_enabled = false;
68 Settings::values.touchscreen.enabled = true;
69}
70
71// Name, OnProfileSelect (called when selected in drop down), OnConfigure (called when configure
72// is clicked)
73using InputProfile = std::tuple<const char*, void (*)(), void (*)(ConfigureInputSimple*)>;
74
75constexpr std::array<InputProfile, 3> INPUT_PROFILES{{
76 {QT_TR_NOOP("Single Player - Handheld - Undocked"), HandheldOnProfileSelect,
77 [](ConfigureInputSimple* caller) {
78 CallConfigureDialog<ConfigureInputPlayer>(caller, HANDHELD_INDEX, false);
79 }},
80 {QT_TR_NOOP("Single Player - Dual Joycons - Docked"), DualJoyconsDockedOnProfileSelect,
81 [](ConfigureInputSimple* caller) {
82 CallConfigureDialog<ConfigureInputPlayer>(caller, PLAYER_0_INDEX, false);
83 }},
84 {QT_TR_NOOP("Custom"), [] {}, CallConfigureDialog<ConfigureInput>},
85}};
86
87} // namespace
88
89void ApplyInputProfileConfiguration(int profile_index) {
90 std::get<1>(
91 INPUT_PROFILES.at(std::min(profile_index, static_cast<int>(INPUT_PROFILES.size() - 1))))();
92}
93
94ConfigureInputSimple::ConfigureInputSimple(QWidget* parent)
95 : QWidget(parent), ui(std::make_unique<Ui::ConfigureInputSimple>()) {
96 ui->setupUi(this);
97
98 for (const auto& profile : INPUT_PROFILES) {
99 const QString label = tr(std::get<0>(profile));
100 ui->profile_combobox->addItem(label, label);
101 }
102
103 connect(ui->profile_combobox, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
104 &ConfigureInputSimple::OnSelectProfile);
105 connect(ui->profile_configure, &QPushButton::clicked, this, &ConfigureInputSimple::OnConfigure);
106
107 LoadConfiguration();
108}
109
110ConfigureInputSimple::~ConfigureInputSimple() = default;
111
112void ConfigureInputSimple::ApplyConfiguration() {
113 auto index = ui->profile_combobox->currentIndex();
114 // Make the stored index for "Custom" very large so that if new profiles are added it
115 // doesn't change.
116 if (index >= static_cast<int>(INPUT_PROFILES.size() - 1)) {
117 index = std::numeric_limits<int>::max();
118 }
119
120 UISettings::values.profile_index = index;
121}
122
123void ConfigureInputSimple::changeEvent(QEvent* event) {
124 if (event->type() == QEvent::LanguageChange) {
125 RetranslateUI();
126 }
127
128 QWidget::changeEvent(event);
129}
130
131void ConfigureInputSimple::RetranslateUI() {
132 ui->retranslateUi(this);
133}
134
135void ConfigureInputSimple::LoadConfiguration() {
136 const auto index = UISettings::values.profile_index;
137 if (index >= static_cast<int>(INPUT_PROFILES.size()) || index < 0) {
138 ui->profile_combobox->setCurrentIndex(static_cast<int>(INPUT_PROFILES.size() - 1));
139 } else {
140 ui->profile_combobox->setCurrentIndex(index);
141 }
142}
143
144void ConfigureInputSimple::OnSelectProfile(int index) {
145 const auto old_docked = Settings::values.use_docked_mode;
146 ApplyInputProfileConfiguration(index);
147 OnDockedModeChanged(old_docked, Settings::values.use_docked_mode);
148}
149
150void ConfigureInputSimple::OnConfigure() {
151 std::get<2>(INPUT_PROFILES.at(ui->profile_combobox->currentIndex()))(this);
152}
diff --git a/src/yuzu/configuration/configure_input_simple.h b/src/yuzu/configuration/configure_input_simple.h
deleted file mode 100644
index bb5050224..000000000
--- a/src/yuzu/configuration/configure_input_simple.h
+++ /dev/null
@@ -1,43 +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
9#include <QWidget>
10
11class QPushButton;
12class QString;
13class QTimer;
14
15namespace Ui {
16class ConfigureInputSimple;
17}
18
19// Used by configuration loader to apply a profile if the input is invalid.
20void ApplyInputProfileConfiguration(int profile_index);
21
22class ConfigureInputSimple : public QWidget {
23 Q_OBJECT
24
25public:
26 explicit ConfigureInputSimple(QWidget* parent = nullptr);
27 ~ConfigureInputSimple() override;
28
29 /// Save all button configurations to settings file
30 void ApplyConfiguration();
31
32private:
33 void changeEvent(QEvent* event) override;
34 void RetranslateUI();
35
36 /// Load configuration settings.
37 void LoadConfiguration();
38
39 void OnSelectProfile(int index);
40 void OnConfigure();
41
42 std::unique_ptr<Ui::ConfigureInputSimple> ui;
43};
diff --git a/src/yuzu/configuration/configure_input_simple.ui b/src/yuzu/configuration/configure_input_simple.ui
deleted file mode 100644
index c4889caa9..000000000
--- a/src/yuzu/configuration/configure_input_simple.ui
+++ /dev/null
@@ -1,97 +0,0 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<ui version="4.0">
3 <class>ConfigureInputSimple</class>
4 <widget class="QWidget" name="ConfigureInputSimple">
5 <property name="geometry">
6 <rect>
7 <x>0</x>
8 <y>0</y>
9 <width>473</width>
10 <height>685</height>
11 </rect>
12 </property>
13 <property name="windowTitle">
14 <string>ConfigureInputSimple</string>
15 </property>
16 <layout class="QVBoxLayout" name="verticalLayout_5">
17 <item>
18 <layout class="QVBoxLayout" name="verticalLayout">
19 <item>
20 <widget class="QGroupBox" name="gridGroupBox">
21 <property name="title">
22 <string>Profile</string>
23 </property>
24 <layout class="QGridLayout" name="gridLayout">
25 <item row="1" column="2">
26 <widget class="QPushButton" name="profile_configure">
27 <property name="text">
28 <string>Configure</string>
29 </property>
30 </widget>
31 </item>
32 <item row="1" column="0">
33 <spacer name="horizontalSpacer">
34 <property name="orientation">
35 <enum>Qt::Horizontal</enum>
36 </property>
37 <property name="sizeHint" stdset="0">
38 <size>
39 <width>40</width>
40 <height>20</height>
41 </size>
42 </property>
43 </spacer>
44 </item>
45 <item row="1" column="3">
46 <spacer name="horizontalSpacer_2">
47 <property name="orientation">
48 <enum>Qt::Horizontal</enum>
49 </property>
50 <property name="sizeHint" stdset="0">
51 <size>
52 <width>40</width>
53 <height>20</height>
54 </size>
55 </property>
56 </spacer>
57 </item>
58 <item row="1" column="1">
59 <widget class="QComboBox" name="profile_combobox">
60 <property name="minimumSize">
61 <size>
62 <width>250</width>
63 <height>0</height>
64 </size>
65 </property>
66 </widget>
67 </item>
68 <item row="0" column="1" colspan="2">
69 <widget class="QLabel" name="label">
70 <property name="text">
71 <string>Choose a controller configuration:</string>
72 </property>
73 </widget>
74 </item>
75 </layout>
76 </widget>
77 </item>
78 </layout>
79 </item>
80 <item>
81 <spacer name="verticalSpacer">
82 <property name="orientation">
83 <enum>Qt::Vertical</enum>
84 </property>
85 <property name="sizeHint" stdset="0">
86 <size>
87 <width>20</width>
88 <height>40</height>
89 </size>
90 </property>
91 </spacer>
92 </item>
93 </layout>
94 </widget>
95 <resources/>
96 <connections/>
97</ui>
diff --git a/src/yuzu/configuration/configure_motion_touch.cpp b/src/yuzu/configuration/configure_motion_touch.cpp
new file mode 100644
index 000000000..c7d085151
--- /dev/null
+++ b/src/yuzu/configuration/configure_motion_touch.cpp
@@ -0,0 +1,314 @@
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 <array>
6#include <QCloseEvent>
7#include <QLabel>
8#include <QMessageBox>
9#include <QPushButton>
10#include <QVBoxLayout>
11#include "common/logging/log.h"
12#include "core/settings.h"
13#include "input_common/main.h"
14#include "input_common/udp/client.h"
15#include "input_common/udp/udp.h"
16#include "ui_configure_motion_touch.h"
17#include "yuzu/configuration/configure_motion_touch.h"
18#include "yuzu/configuration/configure_touch_from_button.h"
19
20CalibrationConfigurationDialog::CalibrationConfigurationDialog(QWidget* parent,
21 const std::string& host, u16 port,
22 u8 pad_index, u16 client_id)
23 : QDialog(parent) {
24 layout = new QVBoxLayout;
25 status_label = new QLabel(tr("Communicating with the server..."));
26 cancel_button = new QPushButton(tr("Cancel"));
27 connect(cancel_button, &QPushButton::clicked, this, [this] {
28 if (!completed) {
29 job->Stop();
30 }
31 accept();
32 });
33 layout->addWidget(status_label);
34 layout->addWidget(cancel_button);
35 setLayout(layout);
36
37 using namespace InputCommon::CemuhookUDP;
38 job = std::make_unique<CalibrationConfigurationJob>(
39 host, port, pad_index, client_id,
40 [this](CalibrationConfigurationJob::Status status) {
41 QString text;
42 switch (status) {
43 case CalibrationConfigurationJob::Status::Ready:
44 text = tr("Touch the top left corner <br>of your touchpad.");
45 break;
46 case CalibrationConfigurationJob::Status::Stage1Completed:
47 text = tr("Now touch the bottom right corner <br>of your touchpad.");
48 break;
49 case CalibrationConfigurationJob::Status::Completed:
50 text = tr("Configuration completed!");
51 break;
52 }
53 QMetaObject::invokeMethod(this, "UpdateLabelText", Q_ARG(QString, text));
54 if (status == CalibrationConfigurationJob::Status::Completed) {
55 QMetaObject::invokeMethod(this, "UpdateButtonText", Q_ARG(QString, tr("OK")));
56 }
57 },
58 [this](u16 min_x_, u16 min_y_, u16 max_x_, u16 max_y_) {
59 completed = true;
60 min_x = min_x_;
61 min_y = min_y_;
62 max_x = max_x_;
63 max_y = max_y_;
64 });
65}
66
67CalibrationConfigurationDialog::~CalibrationConfigurationDialog() = default;
68
69void CalibrationConfigurationDialog::UpdateLabelText(const QString& text) {
70 status_label->setText(text);
71}
72
73void CalibrationConfigurationDialog::UpdateButtonText(const QString& text) {
74 cancel_button->setText(text);
75}
76
77constexpr std::array<std::pair<const char*, const char*>, 2> MotionProviders = {{
78 {"motion_emu", QT_TRANSLATE_NOOP("ConfigureMotionTouch", "Mouse (Right Click)")},
79 {"cemuhookudp", QT_TRANSLATE_NOOP("ConfigureMotionTouch", "CemuhookUDP")},
80}};
81
82constexpr std::array<std::pair<const char*, const char*>, 2> TouchProviders = {{
83 {"emu_window", QT_TRANSLATE_NOOP("ConfigureMotionTouch", "Emulator Window")},
84 {"cemuhookudp", QT_TRANSLATE_NOOP("ConfigureMotionTouch", "CemuhookUDP")},
85}};
86
87ConfigureMotionTouch::ConfigureMotionTouch(QWidget* parent,
88 InputCommon::InputSubsystem* input_subsystem_)
89 : QDialog(parent), input_subsystem{input_subsystem_},
90 ui(std::make_unique<Ui::ConfigureMotionTouch>()) {
91 ui->setupUi(this);
92 for (const auto& [provider, name] : MotionProviders) {
93 ui->motion_provider->addItem(tr(name), QString::fromUtf8(provider));
94 }
95 for (const auto& [provider, name] : TouchProviders) {
96 ui->touch_provider->addItem(tr(name), QString::fromUtf8(provider));
97 }
98
99 ui->udp_learn_more->setOpenExternalLinks(true);
100 ui->udp_learn_more->setText(
101 tr("<a "
102 "href='https://yuzu-emu.org/wiki/"
103 "using-a-controller-or-android-phone-for-motion-or-touch-input'><span "
104 "style=\"text-decoration: underline; color:#039be5;\">Learn More</span></a>"));
105
106 SetConfiguration();
107 UpdateUiDisplay();
108 ConnectEvents();
109}
110
111ConfigureMotionTouch::~ConfigureMotionTouch() = default;
112
113void ConfigureMotionTouch::SetConfiguration() {
114 const Common::ParamPackage motion_param(Settings::values.motion_device);
115 const Common::ParamPackage touch_param(Settings::values.touch_device);
116 const std::string motion_engine = motion_param.Get("engine", "motion_emu");
117 const std::string touch_engine = touch_param.Get("engine", "emu_window");
118
119 ui->motion_provider->setCurrentIndex(
120 ui->motion_provider->findData(QString::fromStdString(motion_engine)));
121 ui->touch_provider->setCurrentIndex(
122 ui->touch_provider->findData(QString::fromStdString(touch_engine)));
123 ui->touch_from_button_checkbox->setChecked(Settings::values.use_touch_from_button);
124 touch_from_button_maps = Settings::values.touch_from_button_maps;
125 for (const auto& touch_map : touch_from_button_maps) {
126 ui->touch_from_button_map->addItem(QString::fromStdString(touch_map.name));
127 }
128 ui->touch_from_button_map->setCurrentIndex(Settings::values.touch_from_button_map_index);
129 ui->motion_sensitivity->setValue(motion_param.Get("sensitivity", 0.01f));
130
131 min_x = touch_param.Get("min_x", 100);
132 min_y = touch_param.Get("min_y", 50);
133 max_x = touch_param.Get("max_x", 1800);
134 max_y = touch_param.Get("max_y", 850);
135
136 ui->udp_server->setText(QString::fromStdString(Settings::values.udp_input_address));
137 ui->udp_port->setText(QString::number(Settings::values.udp_input_port));
138 ui->udp_pad_index->setCurrentIndex(Settings::values.udp_pad_index);
139}
140
141void ConfigureMotionTouch::UpdateUiDisplay() {
142 const QString motion_engine = ui->motion_provider->currentData().toString();
143 const QString touch_engine = ui->touch_provider->currentData().toString();
144 const QString cemuhook_udp = QStringLiteral("cemuhookudp");
145
146 if (motion_engine == QStringLiteral("motion_emu")) {
147 ui->motion_sensitivity_label->setVisible(true);
148 ui->motion_sensitivity->setVisible(true);
149 } else {
150 ui->motion_sensitivity_label->setVisible(false);
151 ui->motion_sensitivity->setVisible(false);
152 }
153
154 if (touch_engine == cemuhook_udp) {
155 ui->touch_calibration->setVisible(true);
156 ui->touch_calibration_config->setVisible(true);
157 ui->touch_calibration_label->setVisible(true);
158 ui->touch_calibration->setText(
159 QStringLiteral("(%1, %2) - (%3, %4)").arg(min_x).arg(min_y).arg(max_x).arg(max_y));
160 } else {
161 ui->touch_calibration->setVisible(false);
162 ui->touch_calibration_config->setVisible(false);
163 ui->touch_calibration_label->setVisible(false);
164 }
165
166 if (motion_engine == cemuhook_udp || touch_engine == cemuhook_udp) {
167 ui->udp_config_group_box->setVisible(true);
168 } else {
169 ui->udp_config_group_box->setVisible(false);
170 }
171}
172
173void ConfigureMotionTouch::ConnectEvents() {
174 connect(ui->motion_provider, qOverload<int>(&QComboBox::currentIndexChanged), this,
175 [this](int index) { UpdateUiDisplay(); });
176 connect(ui->touch_provider, qOverload<int>(&QComboBox::currentIndexChanged), this,
177 [this](int index) { UpdateUiDisplay(); });
178 connect(ui->udp_test, &QPushButton::clicked, this, &ConfigureMotionTouch::OnCemuhookUDPTest);
179 connect(ui->touch_calibration_config, &QPushButton::clicked, this,
180 &ConfigureMotionTouch::OnConfigureTouchCalibration);
181 connect(ui->touch_from_button_config_btn, &QPushButton::clicked, this,
182 &ConfigureMotionTouch::OnConfigureTouchFromButton);
183 connect(ui->buttonBox, &QDialogButtonBox::rejected, this, [this] {
184 if (CanCloseDialog()) {
185 reject();
186 }
187 });
188}
189
190void ConfigureMotionTouch::OnCemuhookUDPTest() {
191 ui->udp_test->setEnabled(false);
192 ui->udp_test->setText(tr("Testing"));
193 udp_test_in_progress = true;
194 InputCommon::CemuhookUDP::TestCommunication(
195 ui->udp_server->text().toStdString(), static_cast<u16>(ui->udp_port->text().toInt()),
196 static_cast<u8>(ui->udp_pad_index->currentIndex()), 24872,
197 [this] {
198 LOG_INFO(Frontend, "UDP input test success");
199 QMetaObject::invokeMethod(this, "ShowUDPTestResult", Q_ARG(bool, true));
200 },
201 [this] {
202 LOG_ERROR(Frontend, "UDP input test failed");
203 QMetaObject::invokeMethod(this, "ShowUDPTestResult", Q_ARG(bool, false));
204 });
205}
206
207void ConfigureMotionTouch::OnConfigureTouchCalibration() {
208 ui->touch_calibration_config->setEnabled(false);
209 ui->touch_calibration_config->setText(tr("Configuring"));
210 CalibrationConfigurationDialog dialog(
211 this, ui->udp_server->text().toStdString(), static_cast<u16>(ui->udp_port->text().toUInt()),
212 static_cast<u8>(ui->udp_pad_index->currentIndex()), 24872);
213 dialog.exec();
214 if (dialog.completed) {
215 min_x = dialog.min_x;
216 min_y = dialog.min_y;
217 max_x = dialog.max_x;
218 max_y = dialog.max_y;
219 LOG_INFO(Frontend,
220 "UDP touchpad calibration config success: min_x={}, min_y={}, max_x={}, max_y={}",
221 min_x, min_y, max_x, max_y);
222 UpdateUiDisplay();
223 } else {
224 LOG_ERROR(Frontend, "UDP touchpad calibration config failed");
225 }
226 ui->touch_calibration_config->setEnabled(true);
227 ui->touch_calibration_config->setText(tr("Configure"));
228}
229
230void ConfigureMotionTouch::closeEvent(QCloseEvent* event) {
231 if (CanCloseDialog()) {
232 event->accept();
233 } else {
234 event->ignore();
235 }
236}
237
238void ConfigureMotionTouch::ShowUDPTestResult(bool result) {
239 udp_test_in_progress = false;
240 if (result) {
241 QMessageBox::information(this, tr("Test Successful"),
242 tr("Successfully received data from the server."));
243 } else {
244 QMessageBox::warning(this, tr("Test Failed"),
245 tr("Could not receive valid data from the server.<br>Please verify "
246 "that the server is set up correctly and "
247 "the address and port are correct."));
248 }
249 ui->udp_test->setEnabled(true);
250 ui->udp_test->setText(tr("Test"));
251}
252
253void ConfigureMotionTouch::OnConfigureTouchFromButton() {
254 ConfigureTouchFromButton dialog{this, touch_from_button_maps, input_subsystem,
255 ui->touch_from_button_map->currentIndex()};
256 if (dialog.exec() != QDialog::Accepted) {
257 return;
258 }
259 touch_from_button_maps = dialog.GetMaps();
260
261 while (ui->touch_from_button_map->count() > 0) {
262 ui->touch_from_button_map->removeItem(0);
263 }
264 for (const auto& touch_map : touch_from_button_maps) {
265 ui->touch_from_button_map->addItem(QString::fromStdString(touch_map.name));
266 }
267 ui->touch_from_button_map->setCurrentIndex(dialog.GetSelectedIndex());
268}
269
270bool ConfigureMotionTouch::CanCloseDialog() {
271 if (udp_test_in_progress) {
272 QMessageBox::warning(this, tr("Citra"),
273 tr("UDP Test or calibration configuration is in progress.<br>Please "
274 "wait for them to finish."));
275 return false;
276 }
277 return true;
278}
279
280void ConfigureMotionTouch::ApplyConfiguration() {
281 if (!CanCloseDialog()) {
282 return;
283 }
284
285 std::string motion_engine = ui->motion_provider->currentData().toString().toStdString();
286 std::string touch_engine = ui->touch_provider->currentData().toString().toStdString();
287
288 Common::ParamPackage motion_param{}, touch_param{};
289 motion_param.Set("engine", std::move(motion_engine));
290 touch_param.Set("engine", std::move(touch_engine));
291
292 if (motion_engine == "motion_emu") {
293 motion_param.Set("sensitivity", static_cast<float>(ui->motion_sensitivity->value()));
294 }
295
296 if (touch_engine == "cemuhookudp") {
297 touch_param.Set("min_x", min_x);
298 touch_param.Set("min_y", min_y);
299 touch_param.Set("max_x", max_x);
300 touch_param.Set("max_y", max_y);
301 }
302
303 Settings::values.motion_device = motion_param.Serialize();
304 Settings::values.touch_device = touch_param.Serialize();
305 Settings::values.use_touch_from_button = ui->touch_from_button_checkbox->isChecked();
306 Settings::values.touch_from_button_map_index = ui->touch_from_button_map->currentIndex();
307 Settings::values.touch_from_button_maps = touch_from_button_maps;
308 Settings::values.udp_input_address = ui->udp_server->text().toStdString();
309 Settings::values.udp_input_port = static_cast<u16>(ui->udp_port->text().toInt());
310 Settings::values.udp_pad_index = static_cast<u8>(ui->udp_pad_index->currentIndex());
311 input_subsystem->ReloadInputDevices();
312
313 accept();
314}
diff --git a/src/yuzu/configuration/configure_motion_touch.h b/src/yuzu/configuration/configure_motion_touch.h
new file mode 100644
index 000000000..3d4b5d659
--- /dev/null
+++ b/src/yuzu/configuration/configure_motion_touch.h
@@ -0,0 +1,90 @@
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 <QDialog>
9#include "common/param_package.h"
10
11class QLabel;
12class QPushButton;
13class QVBoxLayout;
14
15namespace InputCommon {
16class InputSubsystem;
17}
18
19namespace InputCommon::CemuhookUDP {
20class CalibrationConfigurationJob;
21}
22
23namespace Ui {
24class ConfigureMotionTouch;
25}
26
27/// A dialog for touchpad calibration configuration.
28class CalibrationConfigurationDialog : public QDialog {
29 Q_OBJECT
30public:
31 explicit CalibrationConfigurationDialog(QWidget* parent, const std::string& host, u16 port,
32 u8 pad_index, u16 client_id);
33 ~CalibrationConfigurationDialog() override;
34
35private:
36 Q_INVOKABLE void UpdateLabelText(const QString& text);
37 Q_INVOKABLE void UpdateButtonText(const QString& text);
38
39 QVBoxLayout* layout;
40 QLabel* status_label;
41 QPushButton* cancel_button;
42 std::unique_ptr<InputCommon::CemuhookUDP::CalibrationConfigurationJob> job;
43
44 // Configuration results
45 bool completed{};
46 u16 min_x{};
47 u16 min_y{};
48 u16 max_x{};
49 u16 max_y{};
50
51 friend class ConfigureMotionTouch;
52};
53
54class ConfigureMotionTouch : public QDialog {
55 Q_OBJECT
56
57public:
58 explicit ConfigureMotionTouch(QWidget* parent, InputCommon::InputSubsystem* input_subsystem_);
59 ~ConfigureMotionTouch() override;
60
61public slots:
62 void ApplyConfiguration();
63
64private slots:
65 void OnCemuhookUDPTest();
66 void OnConfigureTouchCalibration();
67 void OnConfigureTouchFromButton();
68
69private:
70 void closeEvent(QCloseEvent* event) override;
71 Q_INVOKABLE void ShowUDPTestResult(bool result);
72 void SetConfiguration();
73 void UpdateUiDisplay();
74 void ConnectEvents();
75 bool CanCloseDialog();
76
77 InputCommon::InputSubsystem* input_subsystem;
78
79 std::unique_ptr<Ui::ConfigureMotionTouch> ui;
80
81 // Coordinate system of the CemuhookUDP touch provider
82 int min_x{};
83 int min_y{};
84 int max_x{};
85 int max_y{};
86
87 bool udp_test_in_progress{};
88
89 std::vector<Settings::TouchFromButtonMap> touch_from_button_maps;
90};
diff --git a/src/yuzu/configuration/configure_motion_touch.ui b/src/yuzu/configuration/configure_motion_touch.ui
new file mode 100644
index 000000000..602cf8cd8
--- /dev/null
+++ b/src/yuzu/configuration/configure_motion_touch.ui
@@ -0,0 +1,327 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<ui version="4.0">
3 <class>ConfigureMotionTouch</class>
4 <widget class="QDialog" name="ConfigureMotionTouch">
5 <property name="windowTitle">
6 <string>Configure Motion / Touch</string>
7 </property>
8 <property name="geometry">
9 <rect>
10 <x>0</x>
11 <y>0</y>
12 <width>500</width>
13 <height>450</height>
14 </rect>
15 </property>
16 <layout class="QVBoxLayout">
17 <item>
18 <widget class="QGroupBox" name="motion_group_box">
19 <property name="title">
20 <string>Motion</string>
21 </property>
22 <layout class="QVBoxLayout">
23 <item>
24 <layout class="QHBoxLayout">
25 <item>
26 <widget class="QLabel" name="motion_provider_label">
27 <property name="text">
28 <string>Motion Provider:</string>
29 </property>
30 </widget>
31 </item>
32 <item>
33 <widget class="QComboBox" name="motion_provider"/>
34 </item>
35 </layout>
36 </item>
37 <item>
38 <layout class="QHBoxLayout">
39 <item>
40 <widget class="QLabel" name="motion_sensitivity_label">
41 <property name="text">
42 <string>Sensitivity:</string>
43 </property>
44 </widget>
45 </item>
46 <item>
47 <widget class="QDoubleSpinBox" name="motion_sensitivity">
48 <property name="alignment">
49 <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
50 </property>
51 <property name="decimals">
52 <number>4</number>
53 </property>
54 <property name="minimum">
55 <double>0.010000000000000</double>
56 </property>
57 <property name="maximum">
58 <double>10.000000000000000</double>
59 </property>
60 <property name="singleStep">
61 <double>0.001000000000000</double>
62 </property>
63 <property name="value">
64 <double>0.010000000000000</double>
65 </property>
66 </widget>
67 </item>
68 </layout>
69 </item>
70 </layout>
71 </widget>
72 </item>
73 <item>
74 <widget class="QGroupBox" name="touch_group_box">
75 <property name="title">
76 <string>Touch</string>
77 </property>
78 <layout class="QVBoxLayout">
79 <item>
80 <layout class="QHBoxLayout">
81 <item>
82 <widget class="QLabel" name="touch_provider_label">
83 <property name="text">
84 <string>Touch Provider:</string>
85 </property>
86 </widget>
87 </item>
88 <item>
89 <widget class="QComboBox" name="touch_provider"/>
90 </item>
91 </layout>
92 </item>
93 <item>
94 <layout class="QHBoxLayout">
95 <item>
96 <widget class="QLabel" name="touch_calibration_label">
97 <property name="text">
98 <string>Calibration:</string>
99 </property>
100 </widget>
101 </item>
102 <item>
103 <widget class="QLabel" name="touch_calibration">
104 <property name="text">
105 <string>(100, 50) - (1800, 850)</string>
106 </property>
107 <property name="alignment">
108 <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
109 </property>
110 </widget>
111 </item>
112 <item>
113 <widget class="QPushButton" name="touch_calibration_config">
114 <property name="sizePolicy">
115 <sizepolicy hsizetype="Maximum" vsizetype="Fixed">
116 <horstretch>0</horstretch>
117 <verstretch>0</verstretch>
118 </sizepolicy>
119 </property>
120 <property name="text">
121 <string>Configure</string>
122 </property>
123 </widget>
124 </item>
125 </layout>
126 </item>
127 <item>
128 <layout class="QHBoxLayout">
129 <item>
130 <widget class="QCheckBox" name="touch_from_button_checkbox">
131 <property name="sizePolicy">
132 <sizepolicy hsizetype="Maximum" vsizetype="Fixed">
133 <horstretch>0</horstretch>
134 <verstretch>0</verstretch>
135 </sizepolicy>
136 </property>
137 <property name="text">
138 <string>Use button mapping:</string>
139 </property>
140 </widget>
141 </item>
142 <item>
143 <widget class="QComboBox" name="touch_from_button_map"/>
144 </item>
145 <item>
146 <widget class="QPushButton" name="touch_from_button_config_btn">
147 <property name="sizePolicy">
148 <sizepolicy hsizetype="Maximum" vsizetype="Fixed">
149 <horstretch>0</horstretch>
150 <verstretch>0</verstretch>
151 </sizepolicy>
152 </property>
153 <property name="text">
154 <string>Configure</string>
155 </property>
156 </widget>
157 </item>
158 </layout>
159 </item>
160 </layout>
161 </widget>
162 </item>
163 <item>
164 <widget class="QGroupBox" name="udp_config_group_box">
165 <property name="title">
166 <string>CemuhookUDP Config</string>
167 </property>
168 <layout class="QVBoxLayout">
169 <item>
170 <widget class="QLabel" name="udp_help">
171 <property name="text">
172 <string>You may use any Cemuhook compatible UDP input source to provide motion and touch input.</string>
173 </property>
174 <property name="alignment">
175 <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
176 </property>
177 <property name="wordWrap">
178 <bool>true</bool>
179 </property>
180 </widget>
181 </item>
182 <item>
183 <layout class="QHBoxLayout">
184 <item>
185 <widget class="QLabel" name="udp_server_label">
186 <property name="text">
187 <string>Server:</string>
188 </property>
189 </widget>
190 </item>
191 <item>
192 <widget class="QLineEdit" name="udp_server">
193 <property name="sizePolicy">
194 <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
195 <horstretch>0</horstretch>
196 <verstretch>0</verstretch>
197 </sizepolicy>
198 </property>
199 </widget>
200 </item>
201 </layout>
202 </item>
203 <item>
204 <layout class="QHBoxLayout">
205 <item>
206 <widget class="QLabel" name="udp_port_label">
207 <property name="text">
208 <string>Port:</string>
209 </property>
210 </widget>
211 </item>
212 <item>
213 <widget class="QLineEdit" name="udp_port">
214 <property name="sizePolicy">
215 <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
216 <horstretch>0</horstretch>
217 <verstretch>0</verstretch>
218 </sizepolicy>
219 </property>
220 </widget>
221 </item>
222 </layout>
223 </item>
224 <item>
225 <layout class="QHBoxLayout">
226 <item>
227 <widget class="QLabel" name="udp_pad_index_label">
228 <property name="text">
229 <string>Pad:</string>
230 </property>
231 </widget>
232 </item>
233 <item>
234 <widget class="QComboBox" name="udp_pad_index">
235 <item>
236 <property name="text">
237 <string>Pad 1</string>
238 </property>
239 </item>
240 <item>
241 <property name="text">
242 <string>Pad 2</string>
243 </property>
244 </item>
245 <item>
246 <property name="text">
247 <string>Pad 3</string>
248 </property>
249 </item>
250 <item>
251 <property name="text">
252 <string>Pad 4</string>
253 </property>
254 </item>
255 </widget>
256 </item>
257 </layout>
258 </item>
259 <item>
260 <layout class="QHBoxLayout">
261 <item>
262 <widget class="QLabel" name="udp_learn_more">
263 <property name="text">
264 <string>Learn More</string>
265 </property>
266 </widget>
267 </item>
268 <item>
269 <widget class="QPushButton" name="udp_test">
270 <property name="sizePolicy">
271 <sizepolicy hsizetype="Maximum" vsizetype="Fixed">
272 <horstretch>0</horstretch>
273 <verstretch>0</verstretch>
274 </sizepolicy>
275 </property>
276 <property name="text">
277 <string>Test</string>
278 </property>
279 </widget>
280 </item>
281 </layout>
282 </item>
283 </layout>
284 </widget>
285 </item>
286 <item>
287 <spacer>
288 <property name="orientation">
289 <enum>Qt::Vertical</enum>
290 </property>
291 <property name="sizeHint" stdset="0">
292 <size>
293 <width>167</width>
294 <height>55</height>
295 </size>
296 </property>
297 </spacer>
298 </item>
299 <item>
300 <widget class="QDialogButtonBox" name="buttonBox">
301 <property name="standardButtons">
302 <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
303 </property>
304 </widget>
305 </item>
306 </layout>
307 </widget>
308 <resources/>
309 <connections>
310 <connection>
311 <sender>buttonBox</sender>
312 <signal>accepted()</signal>
313 <receiver>ConfigureMotionTouch</receiver>
314 <slot>ApplyConfiguration()</slot>
315 <hints>
316 <hint type="sourcelabel">
317 <x>220</x>
318 <y>380</y>
319 </hint>
320 <hint type="destinationlabel">
321 <x>220</x>
322 <y>200</y>
323 </hint>
324 </hints>
325 </connection>
326 </connections>
327</ui>
diff --git a/src/yuzu/configuration/configure_mouse_advanced.cpp b/src/yuzu/configuration/configure_mouse_advanced.cpp
index 5bcf5ffa8..2af3afda8 100644
--- a/src/yuzu/configuration/configure_mouse_advanced.cpp
+++ b/src/yuzu/configuration/configure_mouse_advanced.cpp
@@ -18,6 +18,16 @@
18 18
19static QString GetKeyName(int key_code) { 19static QString GetKeyName(int key_code) {
20 switch (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");
21 case Qt::Key_Shift: 31 case Qt::Key_Shift:
22 return QObject::tr("Shift"); 32 return QObject::tr("Shift");
23 case Qt::Key_Control: 33 case Qt::Key_Control:
@@ -66,8 +76,10 @@ static QString ButtonToText(const Common::ParamPackage& param) {
66 return QObject::tr("[unknown]"); 76 return QObject::tr("[unknown]");
67} 77}
68 78
69ConfigureMouseAdvanced::ConfigureMouseAdvanced(QWidget* parent) 79ConfigureMouseAdvanced::ConfigureMouseAdvanced(QWidget* parent,
70 : QDialog(parent), ui(std::make_unique<Ui::ConfigureMouseAdvanced>()), 80 InputCommon::InputSubsystem* input_subsystem_)
81 : QDialog(parent),
82 ui(std::make_unique<Ui::ConfigureMouseAdvanced>()), input_subsystem{input_subsystem_},
71 timeout_timer(std::make_unique<QTimer>()), poll_timer(std::make_unique<QTimer>()) { 83 timeout_timer(std::make_unique<QTimer>()), poll_timer(std::make_unique<QTimer>()) {
72 ui->setupUi(this); 84 ui->setupUi(this);
73 setFocusPolicy(Qt::ClickFocus); 85 setFocusPolicy(Qt::ClickFocus);
@@ -188,9 +200,9 @@ void ConfigureMouseAdvanced::HandleClick(
188 button->setText(tr("[press key]")); 200 button->setText(tr("[press key]"));
189 button->setFocus(); 201 button->setFocus();
190 202
191 // Keyboard keys can only be used as button devices 203 // Keyboard keys or mouse buttons can only be used as button devices
192 want_keyboard_keys = type == InputCommon::Polling::DeviceType::Button; 204 want_keyboard_mouse = type == InputCommon::Polling::DeviceType::Button;
193 if (want_keyboard_keys) { 205 if (want_keyboard_mouse) {
194 const auto iter = std::find(button_map.begin(), button_map.end(), button); 206 const auto iter = std::find(button_map.begin(), button_map.end(), button);
195 ASSERT(iter != button_map.end()); 207 ASSERT(iter != button_map.end());
196 const auto index = std::distance(button_map.begin(), iter); 208 const auto index = std::distance(button_map.begin(), iter);
@@ -199,27 +211,29 @@ void ConfigureMouseAdvanced::HandleClick(
199 211
200 input_setter = new_input_setter; 212 input_setter = new_input_setter;
201 213
202 device_pollers = InputCommon::Polling::GetPollers(type); 214 device_pollers = input_subsystem->GetPollers(type);
203 215
204 for (auto& poller : device_pollers) { 216 for (auto& poller : device_pollers) {
205 poller->Start(); 217 poller->Start();
206 } 218 }
207 219
208 grabKeyboard(); 220 QWidget::grabMouse();
209 grabMouse(); 221 QWidget::grabKeyboard();
210 timeout_timer->start(5000); // Cancel after 5 seconds 222
211 poll_timer->start(200); // Check for new inputs every 200ms 223 timeout_timer->start(2500); // Cancel after 2.5 seconds
224 poll_timer->start(50); // Check for new inputs every 50ms
212} 225}
213 226
214void ConfigureMouseAdvanced::SetPollingResult(const Common::ParamPackage& params, bool abort) { 227void ConfigureMouseAdvanced::SetPollingResult(const Common::ParamPackage& params, bool abort) {
215 releaseKeyboard();
216 releaseMouse();
217 timeout_timer->stop(); 228 timeout_timer->stop();
218 poll_timer->stop(); 229 poll_timer->stop();
219 for (auto& poller : device_pollers) { 230 for (auto& poller : device_pollers) {
220 poller->Stop(); 231 poller->Stop();
221 } 232 }
222 233
234 QWidget::releaseMouse();
235 QWidget::releaseKeyboard();
236
223 if (!abort) { 237 if (!abort) {
224 (*input_setter)(params); 238 (*input_setter)(params);
225 } 239 }
@@ -228,13 +242,29 @@ void ConfigureMouseAdvanced::SetPollingResult(const Common::ParamPackage& params
228 input_setter = std::nullopt; 242 input_setter = std::nullopt;
229} 243}
230 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
231void ConfigureMouseAdvanced::keyPressEvent(QKeyEvent* event) { 261void ConfigureMouseAdvanced::keyPressEvent(QKeyEvent* event) {
232 if (!input_setter || !event) { 262 if (!input_setter || !event) {
233 return; 263 return;
234 } 264 }
235 265
236 if (event->key() != Qt::Key_Escape) { 266 if (event->key() != Qt::Key_Escape) {
237 if (want_keyboard_keys) { 267 if (want_keyboard_mouse) {
238 SetPollingResult(Common::ParamPackage{InputCommon::GenerateKeyboardParam(event->key())}, 268 SetPollingResult(Common::ParamPackage{InputCommon::GenerateKeyboardParam(event->key())},
239 false); 269 false);
240 } else { 270 } else {
diff --git a/src/yuzu/configuration/configure_mouse_advanced.h b/src/yuzu/configuration/configure_mouse_advanced.h
index 342b82412..65b6fca9a 100644
--- a/src/yuzu/configuration/configure_mouse_advanced.h
+++ b/src/yuzu/configuration/configure_mouse_advanced.h
@@ -8,12 +8,14 @@
8#include <optional> 8#include <optional>
9#include <QDialog> 9#include <QDialog>
10 10
11#include "core/settings.h"
12
13class QCheckBox; 11class QCheckBox;
14class QPushButton; 12class QPushButton;
15class QTimer; 13class QTimer;
16 14
15namespace InputCommon {
16class InputSubsystem;
17}
18
17namespace Ui { 19namespace Ui {
18class ConfigureMouseAdvanced; 20class ConfigureMouseAdvanced;
19} 21}
@@ -22,7 +24,7 @@ class ConfigureMouseAdvanced : public QDialog {
22 Q_OBJECT 24 Q_OBJECT
23 25
24public: 26public:
25 explicit ConfigureMouseAdvanced(QWidget* parent); 27 explicit ConfigureMouseAdvanced(QWidget* parent, InputCommon::InputSubsystem* input_subsystem_);
26 ~ConfigureMouseAdvanced() override; 28 ~ConfigureMouseAdvanced() override;
27 29
28 void ApplyConfiguration(); 30 void ApplyConfiguration();
@@ -49,11 +51,16 @@ private:
49 /// Finish polling and configure input using the input_setter 51 /// Finish polling and configure input using the input_setter
50 void SetPollingResult(const Common::ParamPackage& params, bool abort); 52 void SetPollingResult(const Common::ParamPackage& params, bool abort);
51 53
54 /// Handle mouse button press events.
55 void mousePressEvent(QMouseEvent* event) override;
56
52 /// Handle key press events. 57 /// Handle key press events.
53 void keyPressEvent(QKeyEvent* event) override; 58 void keyPressEvent(QKeyEvent* event) override;
54 59
55 std::unique_ptr<Ui::ConfigureMouseAdvanced> ui; 60 std::unique_ptr<Ui::ConfigureMouseAdvanced> ui;
56 61
62 InputCommon::InputSubsystem* input_subsystem;
63
57 /// This will be the the setting function when an input is awaiting configuration. 64 /// This will be the the setting function when an input is awaiting configuration.
58 std::optional<std::function<void(const Common::ParamPackage&)>> input_setter; 65 std::optional<std::function<void(const Common::ParamPackage&)>> input_setter;
59 66
@@ -67,5 +74,5 @@ private:
67 74
68 /// A flag to indicate if keyboard keys are okay when configuring an input. If this is false, 75 /// A flag to indicate if keyboard keys are okay when configuring an input. If this is false,
69 /// keyboard events are ignored. 76 /// keyboard events are ignored.
70 bool want_keyboard_keys = false; 77 bool want_keyboard_mouse = false;
71}; 78};
diff --git a/src/yuzu/configuration/configure_mouse_advanced.ui b/src/yuzu/configuration/configure_mouse_advanced.ui
index 08245ecf0..74552fdbd 100644
--- a/src/yuzu/configuration/configure_mouse_advanced.ui
+++ b/src/yuzu/configuration/configure_mouse_advanced.ui
@@ -6,13 +6,18 @@
6 <rect> 6 <rect>
7 <x>0</x> 7 <x>0</x>
8 <y>0</y> 8 <y>0</y>
9 <width>250</width> 9 <width>310</width>
10 <height>261</height> 10 <height>193</height>
11 </rect> 11 </rect>
12 </property> 12 </property>
13 <property name="windowTitle"> 13 <property name="windowTitle">
14 <string>Configure Mouse</string> 14 <string>Configure Mouse</string>
15 </property> 15 </property>
16 <property name="styleSheet">
17 <string notr="true">QPushButton {
18 min-width: 55px;
19}</string>
20 </property>
16 <layout class="QVBoxLayout" name="verticalLayout"> 21 <layout class="QVBoxLayout" name="verticalLayout">
17 <item> 22 <item>
18 <widget class="QGroupBox" name="gridGroupBox"> 23 <widget class="QGroupBox" name="gridGroupBox">
@@ -20,81 +25,33 @@
20 <string>Mouse Buttons</string> 25 <string>Mouse Buttons</string>
21 </property> 26 </property>
22 <layout class="QGridLayout" name="gridLayout"> 27 <layout class="QGridLayout" name="gridLayout">
23 <item row="0" column="4"> 28 <item row="3" column="5">
24 <spacer name="horizontalSpacer_2"> 29 <layout class="QVBoxLayout" name="verticalLayout_6">
25 <property name="orientation">
26 <enum>Qt::Horizontal</enum>
27 </property>
28 <property name="sizeType">
29 <enum>QSizePolicy::Fixed</enum>
30 </property>
31 <property name="sizeHint" stdset="0">
32 <size>
33 <width>20</width>
34 <height>20</height>
35 </size>
36 </property>
37 </spacer>
38 </item>
39 <item row="0" column="3">
40 <layout class="QVBoxLayout" name="verticalLayout_4">
41 <item> 30 <item>
42 <layout class="QHBoxLayout" name="horizontalLayout_3"> 31 <layout class="QHBoxLayout" name="horizontalLayout_5">
43 <item> 32 <item>
44 <widget class="QLabel" name="label_3"> 33 <widget class="QLabel" name="label_5">
45 <property name="text"> 34 <property name="text">
46 <string>Right:</string> 35 <string>Forward:</string>
47 </property> 36 </property>
48 </widget> 37 </widget>
49 </item> 38 </item>
50 </layout> 39 </layout>
51 </item> 40 </item>
52 <item> 41 <item>
53 <widget class="QPushButton" name="right_button"> 42 <widget class="QPushButton" name="forward_button">
54 <property name="minimumSize"> 43 <property name="minimumSize">
55 <size> 44 <size>
56 <width>75</width> 45 <width>57</width>
57 <height>0</height> 46 <height>0</height>
58 </size> 47 </size>
59 </property> 48 </property>
60 <property name="text"> 49 <property name="maximumSize">
61 <string/> 50 <size>
51 <width>16777215</width>
52 <height>16777215</height>
53 </size>
62 </property> 54 </property>
63 </widget>
64 </item>
65 </layout>
66 </item>
67 <item row="0" column="0">
68 <spacer name="horizontalSpacer">
69 <property name="orientation">
70 <enum>Qt::Horizontal</enum>
71 </property>
72 <property name="sizeType">
73 <enum>QSizePolicy::Fixed</enum>
74 </property>
75 <property name="sizeHint" stdset="0">
76 <size>
77 <width>20</width>
78 <height>20</height>
79 </size>
80 </property>
81 </spacer>
82 </item>
83 <item row="2" column="1">
84 <layout class="QVBoxLayout" name="verticalLayout_3">
85 <item>
86 <layout class="QHBoxLayout" name="horizontalLayout_2">
87 <item>
88 <widget class="QLabel" name="label_2">
89 <property name="text">
90 <string>Middle:</string>
91 </property>
92 </widget>
93 </item>
94 </layout>
95 </item>
96 <item>
97 <widget class="QPushButton" name="middle_button">
98 <property name="text"> 55 <property name="text">
99 <string/> 56 <string/>
100 </property> 57 </property>
@@ -123,6 +80,12 @@
123 </item> 80 </item>
124 <item> 81 <item>
125 <widget class="QPushButton" name="back_button"> 82 <widget class="QPushButton" name="back_button">
83 <property name="minimumSize">
84 <size>
85 <width>57</width>
86 <height>0</height>
87 </size>
88 </property>
126 <property name="text"> 89 <property name="text">
127 <string/> 90 <string/>
128 </property> 91 </property>
@@ -147,7 +110,7 @@
147 <widget class="QPushButton" name="left_button"> 110 <widget class="QPushButton" name="left_button">
148 <property name="minimumSize"> 111 <property name="minimumSize">
149 <size> 112 <size>
150 <width>75</width> 113 <width>57</width>
151 <height>0</height> 114 <height>0</height>
152 </size> 115 </size>
153 </property> 116 </property>
@@ -158,21 +121,99 @@
158 </item> 121 </item>
159 </layout> 122 </layout>
160 </item> 123 </item>
161 <item row="3" column="3"> 124 <item row="0" column="3">
162 <layout class="QVBoxLayout" name="verticalLayout_6"> 125 <layout class="QVBoxLayout" name="verticalLayout_3">
163 <item> 126 <item>
164 <layout class="QHBoxLayout" name="horizontalLayout_5"> 127 <layout class="QHBoxLayout" name="horizontalLayout_2">
165 <item> 128 <item>
166 <widget class="QLabel" name="label_5"> 129 <widget class="QLabel" name="label_2">
167 <property name="text"> 130 <property name="text">
168 <string>Forward:</string> 131 <string>Middle:</string>
169 </property> 132 </property>
170 </widget> 133 </widget>
171 </item> 134 </item>
172 </layout> 135 </layout>
173 </item> 136 </item>
174 <item> 137 <item>
175 <widget class="QPushButton" name="forward_button"> 138 <widget class="QPushButton" name="middle_button">
139 <property name="minimumSize">
140 <size>
141 <width>57</width>
142 <height>0</height>
143 </size>
144 </property>
145 <property name="maximumSize">
146 <size>
147 <width>16777215</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>57</width>
208 <height>0</height>
209 </size>
210 </property>
211 <property name="maximumSize">
212 <size>
213 <width>16777215</width>
214 <height>16777215</height>
215 </size>
216 </property>
176 <property name="text"> 217 <property name="text">
177 <string/> 218 <string/>
178 </property> 219 </property>
@@ -180,6 +221,32 @@
180 </item> 221 </item>
181 </layout> 222 </layout>
182 </item> 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>
183 </layout> 250 </layout>
184 </widget> 251 </widget>
185 </item> 252 </item>
@@ -187,15 +254,39 @@
187 <layout class="QHBoxLayout" name="horizontalLayout_6"> 254 <layout class="QHBoxLayout" name="horizontalLayout_6">
188 <item> 255 <item>
189 <widget class="QPushButton" name="buttonClearAll"> 256 <widget class="QPushButton" name="buttonClearAll">
257 <property name="minimumSize">
258 <size>
259 <width>57</width>
260 <height>0</height>
261 </size>
262 </property>
263 <property name="maximumSize">
264 <size>
265 <width>16777215</width>
266 <height>16777215</height>
267 </size>
268 </property>
190 <property name="text"> 269 <property name="text">
191 <string>Clear All</string> 270 <string>Clear</string>
192 </property> 271 </property>
193 </widget> 272 </widget>
194 </item> 273 </item>
195 <item> 274 <item>
196 <widget class="QPushButton" name="buttonRestoreDefaults"> 275 <widget class="QPushButton" name="buttonRestoreDefaults">
276 <property name="minimumSize">
277 <size>
278 <width>57</width>
279 <height>0</height>
280 </size>
281 </property>
282 <property name="maximumSize">
283 <size>
284 <width>16777215</width>
285 <height>16777215</height>
286 </size>
287 </property>
197 <property name="text"> 288 <property name="text">
198 <string>Restore Defaults</string> 289 <string>Defaults</string>
199 </property> 290 </property>
200 </widget> 291 </widget>
201 </item> 292 </item>
@@ -206,21 +297,24 @@
206 </property> 297 </property>
207 <property name="sizeHint" stdset="0"> 298 <property name="sizeHint" stdset="0">
208 <size> 299 <size>
209 <width>40</width> 300 <width>0</width>
210 <height>20</height> 301 <height>20</height>
211 </size> 302 </size>
212 </property> 303 </property>
213 </spacer> 304 </spacer>
214 </item> 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>
215 </layout> 316 </layout>
216 </item> 317 </item>
217 <item>
218 <widget class="QDialogButtonBox" name="buttonBox">
219 <property name="standardButtons">
220 <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
221 </property>
222 </widget>
223 </item>
224 </layout> 318 </layout>
225 </widget> 319 </widget>
226 <resources/> 320 <resources/>
diff --git a/src/yuzu/configuration/configure_touch_from_button.cpp b/src/yuzu/configuration/configure_touch_from_button.cpp
new file mode 100644
index 000000000..15557e4b8
--- /dev/null
+++ b/src/yuzu/configuration/configure_touch_from_button.cpp
@@ -0,0 +1,623 @@
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 <QInputDialog>
6#include <QKeyEvent>
7#include <QMessageBox>
8#include <QMouseEvent>
9#include <QResizeEvent>
10#include <QStandardItemModel>
11#include <QTimer>
12#include "common/param_package.h"
13#include "core/frontend/framebuffer_layout.h"
14#include "core/settings.h"
15#include "input_common/main.h"
16#include "ui_configure_touch_from_button.h"
17#include "yuzu/configuration/configure_touch_from_button.h"
18#include "yuzu/configuration/configure_touch_widget.h"
19
20static QString GetKeyName(int key_code) {
21 switch (key_code) {
22 case Qt::Key_Shift:
23 return QObject::tr("Shift");
24 case Qt::Key_Control:
25 return QObject::tr("Ctrl");
26 case Qt::Key_Alt:
27 return QObject::tr("Alt");
28 case Qt::Key_Meta:
29 return QString{};
30 default:
31 return QKeySequence(key_code).toString();
32 }
33}
34
35static QString ButtonToText(const Common::ParamPackage& param) {
36 if (!param.Has("engine")) {
37 return QObject::tr("[not set]");
38 }
39
40 if (param.Get("engine", "") == "keyboard") {
41 return GetKeyName(param.Get("code", 0));
42 }
43
44 if (param.Get("engine", "") == "sdl") {
45 if (param.Has("hat")) {
46 const QString hat_str = QString::fromStdString(param.Get("hat", ""));
47 const QString direction_str = QString::fromStdString(param.Get("direction", ""));
48
49 return QObject::tr("Hat %1 %2").arg(hat_str, direction_str);
50 }
51
52 if (param.Has("axis")) {
53 const QString axis_str = QString::fromStdString(param.Get("axis", ""));
54 const QString direction_str = QString::fromStdString(param.Get("direction", ""));
55
56 return QObject::tr("Axis %1%2").arg(axis_str, direction_str);
57 }
58
59 if (param.Has("button")) {
60 const QString button_str = QString::fromStdString(param.Get("button", ""));
61
62 return QObject::tr("Button %1").arg(button_str);
63 }
64
65 return {};
66 }
67
68 return QObject::tr("[unknown]");
69}
70
71ConfigureTouchFromButton::ConfigureTouchFromButton(
72 QWidget* parent, const std::vector<Settings::TouchFromButtonMap>& touch_maps,
73 InputCommon::InputSubsystem* input_subsystem_, const int default_index)
74 : QDialog(parent), ui(std::make_unique<Ui::ConfigureTouchFromButton>()),
75 touch_maps(touch_maps), input_subsystem{input_subsystem_}, selected_index(default_index),
76 timeout_timer(std::make_unique<QTimer>()), poll_timer(std::make_unique<QTimer>()) {
77 ui->setupUi(this);
78 binding_list_model = new QStandardItemModel(0, 3, this);
79 binding_list_model->setHorizontalHeaderLabels(
80 {tr("Button"), tr("X", "X axis"), tr("Y", "Y axis")});
81 ui->binding_list->setModel(binding_list_model);
82 ui->bottom_screen->SetCoordLabel(ui->coord_label);
83
84 SetConfiguration();
85 UpdateUiDisplay();
86 ConnectEvents();
87}
88
89ConfigureTouchFromButton::~ConfigureTouchFromButton() = default;
90
91void ConfigureTouchFromButton::showEvent(QShowEvent* ev) {
92 QWidget::showEvent(ev);
93
94 // width values are not valid in the constructor
95 const int w =
96 ui->binding_list->viewport()->contentsRect().width() / binding_list_model->columnCount();
97 if (w <= 0) {
98 return;
99 }
100 ui->binding_list->setColumnWidth(0, w);
101 ui->binding_list->setColumnWidth(1, w);
102 ui->binding_list->setColumnWidth(2, w);
103}
104
105void ConfigureTouchFromButton::SetConfiguration() {
106 for (const auto& touch_map : touch_maps) {
107 ui->mapping->addItem(QString::fromStdString(touch_map.name));
108 }
109
110 ui->mapping->setCurrentIndex(selected_index);
111}
112
113void ConfigureTouchFromButton::UpdateUiDisplay() {
114 ui->button_delete->setEnabled(touch_maps.size() > 1);
115 ui->button_delete_bind->setEnabled(false);
116
117 binding_list_model->removeRows(0, binding_list_model->rowCount());
118
119 for (const auto& button_str : touch_maps[selected_index].buttons) {
120 Common::ParamPackage package{button_str};
121 QStandardItem* button = new QStandardItem(ButtonToText(package));
122 button->setData(QString::fromStdString(button_str));
123 button->setEditable(false);
124 QStandardItem* xcoord = new QStandardItem(QString::number(package.Get("x", 0)));
125 QStandardItem* ycoord = new QStandardItem(QString::number(package.Get("y", 0)));
126 binding_list_model->appendRow({button, xcoord, ycoord});
127
128 const int dot = ui->bottom_screen->AddDot(package.Get("x", 0), package.Get("y", 0));
129 button->setData(dot, DataRoleDot);
130 }
131}
132
133void ConfigureTouchFromButton::ConnectEvents() {
134 connect(ui->mapping, qOverload<int>(&QComboBox::currentIndexChanged), this, [this](int index) {
135 SaveCurrentMapping();
136 selected_index = index;
137 UpdateUiDisplay();
138 });
139 connect(ui->button_new, &QPushButton::clicked, this, &ConfigureTouchFromButton::NewMapping);
140 connect(ui->button_delete, &QPushButton::clicked, this,
141 &ConfigureTouchFromButton::DeleteMapping);
142 connect(ui->button_rename, &QPushButton::clicked, this,
143 &ConfigureTouchFromButton::RenameMapping);
144 connect(ui->button_delete_bind, &QPushButton::clicked, this,
145 &ConfigureTouchFromButton::DeleteBinding);
146 connect(ui->binding_list, &QTreeView::doubleClicked, this,
147 &ConfigureTouchFromButton::EditBinding);
148 connect(ui->binding_list->selectionModel(), &QItemSelectionModel::selectionChanged, this,
149 &ConfigureTouchFromButton::OnBindingSelection);
150 connect(binding_list_model, &QStandardItemModel::itemChanged, this,
151 &ConfigureTouchFromButton::OnBindingChanged);
152 connect(ui->binding_list->model(), &QStandardItemModel::rowsAboutToBeRemoved, this,
153 &ConfigureTouchFromButton::OnBindingDeleted);
154 connect(ui->bottom_screen, &TouchScreenPreview::DotAdded, this,
155 &ConfigureTouchFromButton::NewBinding);
156 connect(ui->bottom_screen, &TouchScreenPreview::DotSelected, this,
157 &ConfigureTouchFromButton::SetActiveBinding);
158 connect(ui->bottom_screen, &TouchScreenPreview::DotMoved, this,
159 &ConfigureTouchFromButton::SetCoordinates);
160 connect(ui->buttonBox, &QDialogButtonBox::accepted, this,
161 &ConfigureTouchFromButton::ApplyConfiguration);
162
163 connect(timeout_timer.get(), &QTimer::timeout, [this]() { SetPollingResult({}, true); });
164
165 connect(poll_timer.get(), &QTimer::timeout, [this]() {
166 Common::ParamPackage params;
167 for (auto& poller : device_pollers) {
168 params = poller->GetNextInput();
169 if (params.Has("engine")) {
170 SetPollingResult(params, false);
171 return;
172 }
173 }
174 });
175}
176
177void ConfigureTouchFromButton::SaveCurrentMapping() {
178 auto& map = touch_maps[selected_index];
179 map.buttons.clear();
180 for (int i = 0, rc = binding_list_model->rowCount(); i < rc; ++i) {
181 const auto bind_str = binding_list_model->index(i, 0)
182 .data(Qt::ItemDataRole::UserRole + 1)
183 .toString()
184 .toStdString();
185 if (bind_str.empty()) {
186 continue;
187 }
188 Common::ParamPackage params{bind_str};
189 if (!params.Has("engine")) {
190 continue;
191 }
192 params.Set("x", binding_list_model->index(i, 1).data().toInt());
193 params.Set("y", binding_list_model->index(i, 2).data().toInt());
194 map.buttons.emplace_back(params.Serialize());
195 }
196}
197
198void ConfigureTouchFromButton::NewMapping() {
199 const QString name =
200 QInputDialog::getText(this, tr("New Profile"), tr("Enter the name for the new profile."));
201 if (name.isEmpty()) {
202 return;
203 }
204 touch_maps.emplace_back(Settings::TouchFromButtonMap{name.toStdString(), {}});
205 ui->mapping->addItem(name);
206 ui->mapping->setCurrentIndex(ui->mapping->count() - 1);
207}
208
209void ConfigureTouchFromButton::DeleteMapping() {
210 const auto answer = QMessageBox::question(
211 this, tr("Delete Profile"), tr("Delete profile %1?").arg(ui->mapping->currentText()));
212 if (answer != QMessageBox::Yes) {
213 return;
214 }
215 const bool blocked = ui->mapping->blockSignals(true);
216 ui->mapping->removeItem(selected_index);
217 ui->mapping->blockSignals(blocked);
218 touch_maps.erase(touch_maps.begin() + selected_index);
219 selected_index = ui->mapping->currentIndex();
220 UpdateUiDisplay();
221}
222
223void ConfigureTouchFromButton::RenameMapping() {
224 const QString new_name = QInputDialog::getText(this, tr("Rename Profile"), tr("New name:"));
225 if (new_name.isEmpty()) {
226 return;
227 }
228 ui->mapping->setItemText(selected_index, new_name);
229 touch_maps[selected_index].name = new_name.toStdString();
230}
231
232void ConfigureTouchFromButton::GetButtonInput(const int row_index, const bool is_new) {
233 binding_list_model->item(row_index, 0)->setText(tr("[press key]"));
234
235 input_setter = [this, row_index, is_new](const Common::ParamPackage& params,
236 const bool cancel) {
237 auto* cell = binding_list_model->item(row_index, 0);
238 if (cancel) {
239 if (is_new) {
240 binding_list_model->removeRow(row_index);
241 } else {
242 cell->setText(
243 ButtonToText(Common::ParamPackage{cell->data().toString().toStdString()}));
244 }
245 } else {
246 cell->setText(ButtonToText(params));
247 cell->setData(QString::fromStdString(params.Serialize()));
248 }
249 };
250
251 device_pollers = input_subsystem->GetPollers(InputCommon::Polling::DeviceType::Button);
252
253 for (auto& poller : device_pollers) {
254 poller->Start();
255 }
256
257 grabKeyboard();
258 grabMouse();
259 qApp->setOverrideCursor(QCursor(Qt::CursorShape::ArrowCursor));
260 timeout_timer->start(5000); // Cancel after 5 seconds
261 poll_timer->start(200); // Check for new inputs every 200ms
262}
263
264void ConfigureTouchFromButton::NewBinding(const QPoint& pos) {
265 auto* button = new QStandardItem();
266 button->setEditable(false);
267 auto* x_coord = new QStandardItem(QString::number(pos.x()));
268 auto* y_coord = new QStandardItem(QString::number(pos.y()));
269
270 const int dot_id = ui->bottom_screen->AddDot(pos.x(), pos.y());
271 button->setData(dot_id, DataRoleDot);
272
273 binding_list_model->appendRow({button, x_coord, y_coord});
274 ui->binding_list->setFocus();
275 ui->binding_list->setCurrentIndex(button->index());
276
277 GetButtonInput(binding_list_model->rowCount() - 1, true);
278}
279
280void ConfigureTouchFromButton::EditBinding(const QModelIndex& qi) {
281 if (qi.row() >= 0 && qi.column() == 0) {
282 GetButtonInput(qi.row(), false);
283 }
284}
285
286void ConfigureTouchFromButton::DeleteBinding() {
287 const int row_index = ui->binding_list->currentIndex().row();
288 if (row_index < 0) {
289 return;
290 }
291 ui->bottom_screen->RemoveDot(binding_list_model->index(row_index, 0).data(DataRoleDot).toInt());
292 binding_list_model->removeRow(row_index);
293}
294
295void ConfigureTouchFromButton::OnBindingSelection(const QItemSelection& selected,
296 const QItemSelection& deselected) {
297 ui->button_delete_bind->setEnabled(!selected.isEmpty());
298 if (!selected.isEmpty()) {
299 const auto dot_data = selected.indexes().first().data(DataRoleDot);
300 if (dot_data.isValid()) {
301 ui->bottom_screen->HighlightDot(dot_data.toInt());
302 }
303 }
304 if (!deselected.isEmpty()) {
305 const auto dot_data = deselected.indexes().first().data(DataRoleDot);
306 if (dot_data.isValid()) {
307 ui->bottom_screen->HighlightDot(dot_data.toInt(), false);
308 }
309 }
310}
311
312void ConfigureTouchFromButton::OnBindingChanged(QStandardItem* item) {
313 if (item->column() == 0) {
314 return;
315 }
316
317 const bool blocked = binding_list_model->blockSignals(true);
318 item->setText(QString::number(
319 std::clamp(item->text().toInt(), 0,
320 static_cast<int>((item->column() == 1 ? Layout::ScreenUndocked::Width
321 : Layout::ScreenUndocked::Height) -
322 1))));
323 binding_list_model->blockSignals(blocked);
324
325 const auto dot_data = binding_list_model->index(item->row(), 0).data(DataRoleDot);
326 if (dot_data.isValid()) {
327 ui->bottom_screen->MoveDot(dot_data.toInt(),
328 binding_list_model->item(item->row(), 1)->text().toInt(),
329 binding_list_model->item(item->row(), 2)->text().toInt());
330 }
331}
332
333void ConfigureTouchFromButton::OnBindingDeleted(const QModelIndex& parent, int first, int last) {
334 for (int i = first; i <= last; ++i) {
335 const auto ix = binding_list_model->index(i, 0);
336 if (!ix.isValid()) {
337 return;
338 }
339 const auto dot_data = ix.data(DataRoleDot);
340 if (dot_data.isValid()) {
341 ui->bottom_screen->RemoveDot(dot_data.toInt());
342 }
343 }
344}
345
346void ConfigureTouchFromButton::SetActiveBinding(const int dot_id) {
347 for (int i = 0; i < binding_list_model->rowCount(); ++i) {
348 if (binding_list_model->index(i, 0).data(DataRoleDot) == dot_id) {
349 ui->binding_list->setCurrentIndex(binding_list_model->index(i, 0));
350 ui->binding_list->setFocus();
351 return;
352 }
353 }
354}
355
356void ConfigureTouchFromButton::SetCoordinates(const int dot_id, const QPoint& pos) {
357 for (int i = 0; i < binding_list_model->rowCount(); ++i) {
358 if (binding_list_model->item(i, 0)->data(DataRoleDot) == dot_id) {
359 binding_list_model->item(i, 1)->setText(QString::number(pos.x()));
360 binding_list_model->item(i, 2)->setText(QString::number(pos.y()));
361 return;
362 }
363 }
364}
365
366void ConfigureTouchFromButton::SetPollingResult(const Common::ParamPackage& params,
367 const bool cancel) {
368 releaseKeyboard();
369 releaseMouse();
370 qApp->restoreOverrideCursor();
371 timeout_timer->stop();
372 poll_timer->stop();
373 for (auto& poller : device_pollers) {
374 poller->Stop();
375 }
376 if (input_setter) {
377 (*input_setter)(params, cancel);
378 input_setter.reset();
379 }
380}
381
382void ConfigureTouchFromButton::keyPressEvent(QKeyEvent* event) {
383 if (!input_setter && event->key() == Qt::Key_Delete) {
384 DeleteBinding();
385 return;
386 }
387
388 if (!input_setter) {
389 return QDialog::keyPressEvent(event);
390 }
391
392 if (event->key() != Qt::Key_Escape) {
393 SetPollingResult(Common::ParamPackage{InputCommon::GenerateKeyboardParam(event->key())},
394 false);
395 } else {
396 SetPollingResult({}, true);
397 }
398}
399
400void ConfigureTouchFromButton::ApplyConfiguration() {
401 SaveCurrentMapping();
402 accept();
403}
404
405int ConfigureTouchFromButton::GetSelectedIndex() const {
406 return selected_index;
407}
408
409std::vector<Settings::TouchFromButtonMap> ConfigureTouchFromButton::GetMaps() const {
410 return touch_maps;
411}
412
413TouchScreenPreview::TouchScreenPreview(QWidget* parent) : QFrame(parent) {
414 setBackgroundRole(QPalette::ColorRole::Base);
415}
416
417TouchScreenPreview::~TouchScreenPreview() = default;
418
419void TouchScreenPreview::SetCoordLabel(QLabel* const label) {
420 coord_label = label;
421}
422
423int TouchScreenPreview::AddDot(const int device_x, const int device_y) {
424 QFont dot_font{QStringLiteral("monospace")};
425 dot_font.setStyleHint(QFont::Monospace);
426 dot_font.setPointSize(20);
427
428 auto* dot = new QLabel(this);
429 dot->setAttribute(Qt::WA_TranslucentBackground);
430 dot->setFont(dot_font);
431 dot->setText(QChar(0xD7)); // U+00D7 Multiplication Sign
432 dot->setAlignment(Qt::AlignmentFlag::AlignCenter);
433 dot->setProperty(PropId, ++max_dot_id);
434 dot->setProperty(PropX, device_x);
435 dot->setProperty(PropY, device_y);
436 dot->setCursor(Qt::CursorShape::PointingHandCursor);
437 dot->setMouseTracking(true);
438 dot->installEventFilter(this);
439 dot->show();
440 PositionDot(dot, device_x, device_y);
441 dots.emplace_back(max_dot_id, dot);
442 return max_dot_id;
443}
444
445void TouchScreenPreview::RemoveDot(const int id) {
446 const auto iter = std::find_if(dots.begin(), dots.end(),
447 [id](const auto& entry) { return entry.first == id; });
448 if (iter == dots.cend()) {
449 return;
450 }
451
452 iter->second->deleteLater();
453 dots.erase(iter);
454}
455
456void TouchScreenPreview::HighlightDot(const int id, const bool active) const {
457 for (const auto& dot : dots) {
458 if (dot.first == id) {
459 // use color property from the stylesheet, or fall back to the default palette
460 if (dot_highlight_color.isValid()) {
461 dot.second->setStyleSheet(
462 active ? QStringLiteral("color: %1").arg(dot_highlight_color.name())
463 : QString{});
464 } else {
465 dot.second->setForegroundRole(active ? QPalette::ColorRole::LinkVisited
466 : QPalette::ColorRole::NoRole);
467 }
468 if (active) {
469 dot.second->raise();
470 }
471 return;
472 }
473 }
474}
475
476void TouchScreenPreview::MoveDot(const int id, const int device_x, const int device_y) const {
477 const auto iter = std::find_if(dots.begin(), dots.end(),
478 [id](const auto& entry) { return entry.first == id; });
479 if (iter == dots.cend()) {
480 return;
481 }
482
483 iter->second->setProperty(PropX, device_x);
484 iter->second->setProperty(PropY, device_y);
485 PositionDot(iter->second, device_x, device_y);
486}
487
488void TouchScreenPreview::resizeEvent(QResizeEvent* event) {
489 if (ignore_resize) {
490 return;
491 }
492
493 const int target_width = std::min(width(), height() * 4 / 3);
494 const int target_height = std::min(height(), width() * 3 / 4);
495 if (target_width == width() && target_height == height()) {
496 return;
497 }
498 ignore_resize = true;
499 setGeometry((parentWidget()->contentsRect().width() - target_width) / 2, y(), target_width,
500 target_height);
501 ignore_resize = false;
502
503 if (event->oldSize().width() != target_width || event->oldSize().height() != target_height) {
504 for (const auto& dot : dots) {
505 PositionDot(dot.second);
506 }
507 }
508}
509
510void TouchScreenPreview::mouseMoveEvent(QMouseEvent* event) {
511 if (!coord_label) {
512 return;
513 }
514 const auto pos = MapToDeviceCoords(event->x(), event->y());
515 if (pos) {
516 coord_label->setText(QStringLiteral("X: %1, Y: %2").arg(pos->x()).arg(pos->y()));
517 } else {
518 coord_label->clear();
519 }
520}
521
522void TouchScreenPreview::leaveEvent(QEvent* event) {
523 if (coord_label) {
524 coord_label->clear();
525 }
526}
527
528void TouchScreenPreview::mousePressEvent(QMouseEvent* event) {
529 if (event->button() != Qt::MouseButton::LeftButton) {
530 return;
531 }
532 const auto pos = MapToDeviceCoords(event->x(), event->y());
533 if (pos) {
534 emit DotAdded(*pos);
535 }
536}
537
538bool TouchScreenPreview::eventFilter(QObject* obj, QEvent* event) {
539 switch (event->type()) {
540 case QEvent::Type::MouseButtonPress: {
541 const auto mouse_event = static_cast<QMouseEvent*>(event);
542 if (mouse_event->button() != Qt::MouseButton::LeftButton) {
543 break;
544 }
545 emit DotSelected(obj->property(PropId).toInt());
546
547 drag_state.dot = qobject_cast<QLabel*>(obj);
548 drag_state.start_pos = mouse_event->globalPos();
549 return true;
550 }
551 case QEvent::Type::MouseMove: {
552 if (!drag_state.dot) {
553 break;
554 }
555 const auto mouse_event = static_cast<QMouseEvent*>(event);
556 if (!drag_state.active) {
557 drag_state.active =
558 (mouse_event->globalPos() - drag_state.start_pos).manhattanLength() >=
559 QApplication::startDragDistance();
560 if (!drag_state.active) {
561 break;
562 }
563 }
564 auto current_pos = mapFromGlobal(mouse_event->globalPos());
565 current_pos.setX(std::clamp(current_pos.x(), contentsMargins().left(),
566 contentsMargins().left() + contentsRect().width() - 1));
567 current_pos.setY(std::clamp(current_pos.y(), contentsMargins().top(),
568 contentsMargins().top() + contentsRect().height() - 1));
569 const auto device_coord = MapToDeviceCoords(current_pos.x(), current_pos.y());
570 if (device_coord) {
571 drag_state.dot->setProperty(PropX, device_coord->x());
572 drag_state.dot->setProperty(PropY, device_coord->y());
573 PositionDot(drag_state.dot, device_coord->x(), device_coord->y());
574 emit DotMoved(drag_state.dot->property(PropId).toInt(), *device_coord);
575 if (coord_label) {
576 coord_label->setText(
577 QStringLiteral("X: %1, Y: %2").arg(device_coord->x()).arg(device_coord->y()));
578 }
579 }
580 return true;
581 }
582 case QEvent::Type::MouseButtonRelease: {
583 drag_state.dot.clear();
584 drag_state.active = false;
585 return true;
586 }
587 default:
588 break;
589 }
590 return obj->eventFilter(obj, event);
591}
592
593std::optional<QPoint> TouchScreenPreview::MapToDeviceCoords(const int screen_x,
594 const int screen_y) const {
595 const float t_x = 0.5f + static_cast<float>(screen_x - contentsMargins().left()) *
596 (Layout::ScreenUndocked::Width - 1) / (contentsRect().width() - 1);
597 const float t_y = 0.5f + static_cast<float>(screen_y - contentsMargins().top()) *
598 (Layout::ScreenUndocked::Height - 1) /
599 (contentsRect().height() - 1);
600 if (t_x >= 0.5f && t_x < Layout::ScreenUndocked::Width && t_y >= 0.5f &&
601 t_y < Layout::ScreenUndocked::Height) {
602
603 return QPoint{static_cast<int>(t_x), static_cast<int>(t_y)};
604 }
605 return std::nullopt;
606}
607
608void TouchScreenPreview::PositionDot(QLabel* const dot, const int device_x,
609 const int device_y) const {
610 const float device_coord_x =
611 static_cast<float>(device_x >= 0 ? device_x : dot->property(PropX).toInt());
612 int x_coord = static_cast<int>(
613 device_coord_x * (contentsRect().width() - 1) / (Layout::ScreenUndocked::Width - 1) +
614 contentsMargins().left() - static_cast<float>(dot->width()) / 2 + 0.5f);
615
616 const float device_coord_y =
617 static_cast<float>(device_y >= 0 ? device_y : dot->property(PropY).toInt());
618 const int y_coord = static_cast<int>(
619 device_coord_y * (contentsRect().height() - 1) / (Layout::ScreenUndocked::Height - 1) +
620 contentsMargins().top() - static_cast<float>(dot->height()) / 2 + 0.5f);
621
622 dot->move(x_coord, y_coord);
623}
diff --git a/src/yuzu/configuration/configure_touch_from_button.h b/src/yuzu/configuration/configure_touch_from_button.h
new file mode 100644
index 000000000..d9513e3bc
--- /dev/null
+++ b/src/yuzu/configuration/configure_touch_from_button.h
@@ -0,0 +1,92 @@
1// Copyright 2020 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 <optional>
10#include <vector>
11#include <QDialog>
12
13class QItemSelection;
14class QModelIndex;
15class QStandardItemModel;
16class QStandardItem;
17class QTimer;
18
19namespace Common {
20class ParamPackage;
21}
22
23namespace InputCommon {
24class InputSubsystem;
25}
26
27namespace InputCommon::Polling {
28class DevicePoller;
29}
30
31namespace Settings {
32struct TouchFromButtonMap;
33}
34
35namespace Ui {
36class ConfigureTouchFromButton;
37}
38
39class ConfigureTouchFromButton : public QDialog {
40 Q_OBJECT
41
42public:
43 explicit ConfigureTouchFromButton(QWidget* parent,
44 const std::vector<Settings::TouchFromButtonMap>& touch_maps,
45 InputCommon::InputSubsystem* input_subsystem_,
46 int default_index = 0);
47 ~ConfigureTouchFromButton() override;
48
49 int GetSelectedIndex() const;
50 std::vector<Settings::TouchFromButtonMap> GetMaps() const;
51
52public slots:
53 void ApplyConfiguration();
54 void NewBinding(const QPoint& pos);
55 void SetActiveBinding(int dot_id);
56 void SetCoordinates(int dot_id, const QPoint& pos);
57
58protected:
59 void showEvent(QShowEvent* ev) override;
60 void keyPressEvent(QKeyEvent* event) override;
61
62private slots:
63 void NewMapping();
64 void DeleteMapping();
65 void RenameMapping();
66 void EditBinding(const QModelIndex& qi);
67 void DeleteBinding();
68 void OnBindingSelection(const QItemSelection& selected, const QItemSelection& deselected);
69 void OnBindingChanged(QStandardItem* item);
70 void OnBindingDeleted(const QModelIndex& parent, int first, int last);
71
72private:
73 void SetConfiguration();
74 void UpdateUiDisplay();
75 void ConnectEvents();
76 void GetButtonInput(int row_index, bool is_new);
77 void SetPollingResult(const Common::ParamPackage& params, bool cancel);
78 void SaveCurrentMapping();
79
80 std::unique_ptr<Ui::ConfigureTouchFromButton> ui;
81 std::vector<Settings::TouchFromButtonMap> touch_maps;
82 QStandardItemModel* binding_list_model;
83 InputCommon::InputSubsystem* input_subsystem;
84 int selected_index;
85
86 std::unique_ptr<QTimer> timeout_timer;
87 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;
90
91 static constexpr int DataRoleDot = Qt::ItemDataRole::UserRole + 2;
92};
diff --git a/src/yuzu/configuration/configure_touch_from_button.ui b/src/yuzu/configuration/configure_touch_from_button.ui
new file mode 100644
index 000000000..f581e27e0
--- /dev/null
+++ b/src/yuzu/configuration/configure_touch_from_button.ui
@@ -0,0 +1,231 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<ui version="4.0">
3 <class>ConfigureTouchFromButton</class>
4 <widget class="QDialog" name="ConfigureTouchFromButton">
5 <property name="geometry">
6 <rect>
7 <x>0</x>
8 <y>0</y>
9 <width>500</width>
10 <height>500</height>
11 </rect>
12 </property>
13 <property name="windowTitle">
14 <string>Configure Touchscreen Mappings</string>
15 </property>
16 <layout class="QVBoxLayout">
17 <item>
18 <layout class="QHBoxLayout" name="horizontalLayout">
19 <item>
20 <widget class="QLabel" name="label">
21 <property name="text">
22 <string>Mapping:</string>
23 </property>
24 <property name="textFormat">
25 <enum>Qt::PlainText</enum>
26 </property>
27 </widget>
28 </item>
29 <item>
30 <widget class="QComboBox" name="mapping">
31 <property name="sizePolicy">
32 <sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
33 <horstretch>0</horstretch>
34 <verstretch>0</verstretch>
35 </sizepolicy>
36 </property>
37 </widget>
38 </item>
39 <item>
40 <widget class="QPushButton" name="button_new">
41 <property name="sizePolicy">
42 <sizepolicy hsizetype="Maximum" vsizetype="Fixed">
43 <horstretch>0</horstretch>
44 <verstretch>0</verstretch>
45 </sizepolicy>
46 </property>
47 <property name="text">
48 <string>New</string>
49 </property>
50 </widget>
51 </item>
52 <item>
53 <widget class="QPushButton" name="button_delete">
54 <property name="sizePolicy">
55 <sizepolicy hsizetype="Maximum" vsizetype="Fixed">
56 <horstretch>0</horstretch>
57 <verstretch>0</verstretch>
58 </sizepolicy>
59 </property>
60 <property name="text">
61 <string>Delete</string>
62 </property>
63 </widget>
64 </item>
65 <item>
66 <widget class="QPushButton" name="button_rename">
67 <property name="sizePolicy">
68 <sizepolicy hsizetype="Maximum" vsizetype="Fixed">
69 <horstretch>0</horstretch>
70 <verstretch>0</verstretch>
71 </sizepolicy>
72 </property>
73 <property name="text">
74 <string>Rename</string>
75 </property>
76 </widget>
77 </item>
78 </layout>
79 </item>
80 <item>
81 <widget class="Line" name="line">
82 <property name="orientation">
83 <enum>Qt::Horizontal</enum>
84 </property>
85 </widget>
86 </item>
87 <item>
88 <layout class="QHBoxLayout" name="horizontalLayout_2">
89 <item>
90 <widget class="QLabel" name="label_2">
91 <property name="text">
92 <string>Click the bottom area to add a point, then press a button to bind.
93Drag points to change position, or double-click table cells to edit values.</string>
94 </property>
95 <property name="textFormat">
96 <enum>Qt::PlainText</enum>
97 </property>
98 </widget>
99 </item>
100 <item>
101 <spacer name="horizontalSpacer">
102 <property name="orientation">
103 <enum>Qt::Horizontal</enum>
104 </property>
105 <property name="sizeHint" stdset="0">
106 <size>
107 <width>40</width>
108 <height>20</height>
109 </size>
110 </property>
111 </spacer>
112 </item>
113 <item>
114 <widget class="QPushButton" name="button_delete_bind">
115 <property name="text">
116 <string>Delete Point</string>
117 </property>
118 </widget>
119 </item>
120 </layout>
121 </item>
122 <item>
123 <widget class="QTreeView" name="binding_list">
124 <property name="sizePolicy">
125 <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
126 <horstretch>0</horstretch>
127 <verstretch>0</verstretch>
128 </sizepolicy>
129 </property>
130 <property name="rootIsDecorated">
131 <bool>false</bool>
132 </property>
133 <property name="uniformRowHeights">
134 <bool>true</bool>
135 </property>
136 <property name="itemsExpandable">
137 <bool>false</bool>
138 </property>
139 </widget>
140 </item>
141 <item>
142 <widget class="TouchScreenPreview" name="bottom_screen">
143 <property name="sizePolicy">
144 <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
145 <horstretch>0</horstretch>
146 <verstretch>0</verstretch>
147 </sizepolicy>
148 </property>
149 <property name="minimumSize">
150 <size>
151 <width>160</width>
152 <height>120</height>
153 </size>
154 </property>
155 <property name="baseSize">
156 <size>
157 <width>320</width>
158 <height>240</height>
159 </size>
160 </property>
161 <property name="cursor">
162 <cursorShape>CrossCursor</cursorShape>
163 </property>
164 <property name="mouseTracking">
165 <bool>true</bool>
166 </property>
167 <property name="autoFillBackground">
168 <bool>true</bool>
169 </property>
170 <property name="frameShape">
171 <enum>QFrame::StyledPanel</enum>
172 </property>
173 <property name="frameShadow">
174 <enum>QFrame::Sunken</enum>
175 </property>
176 </widget>
177 </item>
178 <item>
179 <layout class="QHBoxLayout" name="horizontalLayout_3">
180 <item>
181 <widget class="QLabel" name="coord_label">
182 <property name="sizePolicy">
183 <sizepolicy hsizetype="Expanding" vsizetype="Preferred">
184 <horstretch>0</horstretch>
185 <verstretch>0</verstretch>
186 </sizepolicy>
187 </property>
188 <property name="textFormat">
189 <enum>Qt::PlainText</enum>
190 </property>
191 </widget>
192 </item>
193 <item>
194 <widget class="QDialogButtonBox" name="buttonBox">
195 <property name="standardButtons">
196 <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
197 </property>
198 </widget>
199 </item>
200 </layout>
201 </item>
202 </layout>
203 </widget>
204 <customwidgets>
205 <customwidget>
206 <class>TouchScreenPreview</class>
207 <extends>QFrame</extends>
208 <header>yuzu/configuration/configure_touch_widget.h</header>
209 <container>1</container>
210 </customwidget>
211 </customwidgets>
212 <resources/>
213 <connections>
214 <connection>
215 <sender>buttonBox</sender>
216 <signal>rejected()</signal>
217 <receiver>ConfigureTouchFromButton</receiver>
218 <slot>reject()</slot>
219 <hints>
220 <hint type="sourcelabel">
221 <x>249</x>
222 <y>428</y>
223 </hint>
224 <hint type="destinationlabel">
225 <x>249</x>
226 <y>224</y>
227 </hint>
228 </hints>
229 </connection>
230 </connections>
231</ui>
diff --git a/src/yuzu/configuration/configure_touch_widget.h b/src/yuzu/configuration/configure_touch_widget.h
new file mode 100644
index 000000000..347b46583
--- /dev/null
+++ b/src/yuzu/configuration/configure_touch_widget.h
@@ -0,0 +1,62 @@
1// Copyright 2020 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 <optional>
8#include <utility>
9#include <vector>
10#include <QFrame>
11#include <QPointer>
12
13class QLabel;
14
15// Widget for representing touchscreen coordinates
16class TouchScreenPreview : public QFrame {
17 Q_OBJECT
18 Q_PROPERTY(QColor dotHighlightColor MEMBER dot_highlight_color)
19
20public:
21 explicit TouchScreenPreview(QWidget* parent);
22 ~TouchScreenPreview() override;
23
24 void SetCoordLabel(QLabel*);
25 int AddDot(int device_x, int device_y);
26 void RemoveDot(int id);
27 void HighlightDot(int id, bool active = true) const;
28 void MoveDot(int id, int device_x, int device_y) const;
29
30signals:
31 void DotAdded(const QPoint& pos);
32 void DotSelected(int dot_id);
33 void DotMoved(int dot_id, const QPoint& pos);
34
35protected:
36 void resizeEvent(QResizeEvent*) override;
37 void mouseMoveEvent(QMouseEvent*) override;
38 void leaveEvent(QEvent*) override;
39 void mousePressEvent(QMouseEvent*) override;
40 bool eventFilter(QObject*, QEvent*) override;
41
42private:
43 std::optional<QPoint> MapToDeviceCoords(int screen_x, int screen_y) const;
44 void PositionDot(QLabel* dot, int device_x = -1, int device_y = -1) const;
45
46 bool ignore_resize = false;
47 QPointer<QLabel> coord_label;
48
49 std::vector<std::pair<int, QLabel*>> dots;
50 int max_dot_id = 0;
51 QColor dot_highlight_color;
52 static constexpr char PropId[] = "dot_id";
53 static constexpr char PropX[] = "device_x";
54 static constexpr char PropY[] = "device_y";
55
56 struct DragState {
57 bool active = false;
58 QPointer<QLabel> dot;
59 QPoint start_pos;
60 };
61 DragState drag_state;
62};
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index cd7e78eb4..a1b61d119 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -94,6 +94,7 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
94#include "core/perf_stats.h" 94#include "core/perf_stats.h"
95#include "core/settings.h" 95#include "core/settings.h"
96#include "core/telemetry_session.h" 96#include "core/telemetry_session.h"
97#include "input_common/main.h"
97#include "video_core/gpu.h" 98#include "video_core/gpu.h"
98#include "video_core/shader_notify.h" 99#include "video_core/shader_notify.h"
99#include "yuzu/about_dialog.h" 100#include "yuzu/about_dialog.h"
@@ -186,9 +187,9 @@ static void InitializeLogging() {
186} 187}
187 188
188GMainWindow::GMainWindow() 189GMainWindow::GMainWindow()
189 : config(new Config()), emu_thread(nullptr), 190 : input_subsystem{std::make_shared<InputCommon::InputSubsystem>()},
190 vfs(std::make_shared<FileSys::RealVfsFilesystem>()), 191 config{std::make_unique<Config>()}, vfs{std::make_shared<FileSys::RealVfsFilesystem>()},
191 provider(std::make_unique<FileSys::ManualContentProvider>()) { 192 provider{std::make_unique<FileSys::ManualContentProvider>()} {
192 InitializeLogging(); 193 InitializeLogging();
193 194
194 LoadTranslation(); 195 LoadTranslation();
@@ -473,7 +474,7 @@ void GMainWindow::InitializeWidgets() {
473#ifdef YUZU_ENABLE_COMPATIBILITY_REPORTING 474#ifdef YUZU_ENABLE_COMPATIBILITY_REPORTING
474 ui.action_Report_Compatibility->setVisible(true); 475 ui.action_Report_Compatibility->setVisible(true);
475#endif 476#endif
476 render_window = new GRenderWindow(this, emu_thread.get()); 477 render_window = new GRenderWindow(this, emu_thread.get(), input_subsystem);
477 render_window->hide(); 478 render_window->hide();
478 479
479 game_list = new GameList(vfs, provider.get(), this); 480 game_list = new GameList(vfs, provider.get(), this);
@@ -2213,7 +2214,7 @@ void GMainWindow::OnConfigure() {
2213 const auto old_theme = UISettings::values.theme; 2214 const auto old_theme = UISettings::values.theme;
2214 const bool old_discord_presence = UISettings::values.enable_discord_presence; 2215 const bool old_discord_presence = UISettings::values.enable_discord_presence;
2215 2216
2216 ConfigureDialog configure_dialog(this, hotkey_registry); 2217 ConfigureDialog configure_dialog(this, hotkey_registry, input_subsystem.get());
2217 connect(&configure_dialog, &ConfigureDialog::LanguageChanged, this, 2218 connect(&configure_dialog, &ConfigureDialog::LanguageChanged, this,
2218 &GMainWindow::OnLanguageChanged); 2219 &GMainWindow::OnLanguageChanged);
2219 2220
diff --git a/src/yuzu/main.h b/src/yuzu/main.h
index 01f9131e5..0ce66a1ca 100644
--- a/src/yuzu/main.h
+++ b/src/yuzu/main.h
@@ -40,12 +40,20 @@ namespace Core::Frontend {
40struct SoftwareKeyboardParameters; 40struct SoftwareKeyboardParameters;
41} // namespace Core::Frontend 41} // namespace Core::Frontend
42 42
43namespace DiscordRPC {
44class DiscordInterface;
45}
46
43namespace FileSys { 47namespace FileSys {
44class ContentProvider; 48class ContentProvider;
45class ManualContentProvider; 49class ManualContentProvider;
46class VfsFilesystem; 50class VfsFilesystem;
47} // namespace FileSys 51} // namespace FileSys
48 52
53namespace InputCommon {
54class InputSubsystem;
55}
56
49enum class EmulatedDirectoryTarget { 57enum class EmulatedDirectoryTarget {
50 NAND, 58 NAND,
51 SDMC, 59 SDMC,
@@ -62,10 +70,6 @@ enum class ReinitializeKeyBehavior {
62 Warning, 70 Warning,
63}; 71};
64 72
65namespace DiscordRPC {
66class DiscordInterface;
67}
68
69class GMainWindow : public QMainWindow { 73class GMainWindow : public QMainWindow {
70 Q_OBJECT 74 Q_OBJECT
71 75
@@ -86,8 +90,6 @@ public:
86 GMainWindow(); 90 GMainWindow();
87 ~GMainWindow() override; 91 ~GMainWindow() override;
88 92
89 std::unique_ptr<DiscordRPC::DiscordInterface> discord_rpc;
90
91 bool DropAction(QDropEvent* event); 93 bool DropAction(QDropEvent* event);
92 void AcceptDropEvent(QDropEvent* event); 94 void AcceptDropEvent(QDropEvent* event);
93 95
@@ -255,6 +257,9 @@ private:
255 257
256 Ui::MainWindow ui; 258 Ui::MainWindow ui;
257 259
260 std::unique_ptr<DiscordRPC::DiscordInterface> discord_rpc;
261 std::shared_ptr<InputCommon::InputSubsystem> input_subsystem;
262
258 GRenderWindow* render_window; 263 GRenderWindow* render_window;
259 GameList* game_list; 264 GameList* game_list;
260 LoadingScreen* loading_screen; 265 LoadingScreen* loading_screen;
diff --git a/src/yuzu/uisettings.cpp b/src/yuzu/uisettings.cpp
index a51175f36..37499fc85 100644
--- a/src/yuzu/uisettings.cpp
+++ b/src/yuzu/uisettings.cpp
@@ -16,4 +16,5 @@ const Themes themes{{
16}}; 16}};
17 17
18Values values = {}; 18Values values = {};
19
19} // namespace UISettings 20} // namespace UISettings
diff --git a/src/yuzu/uisettings.h b/src/yuzu/uisettings.h
index 2d2e82f15..ce3945485 100644
--- a/src/yuzu/uisettings.h
+++ b/src/yuzu/uisettings.h
@@ -87,9 +87,6 @@ struct Values {
87 // logging 87 // logging
88 bool show_console; 88 bool show_console;
89 89
90 // Controllers
91 int profile_index;
92
93 // Game List 90 // Game List
94 bool show_add_ons; 91 bool show_add_ons;
95 uint32_t icon_size; 92 uint32_t icon_size;
@@ -100,6 +97,7 @@ struct Values {
100}; 97};
101 98
102extern Values values; 99extern Values values;
100
103} // namespace UISettings 101} // namespace UISettings
104 102
105Q_DECLARE_METATYPE(UISettings::GameDir*); 103Q_DECLARE_METATYPE(UISettings::GameDir*);
diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp
index 8a63fd191..e9f1c6500 100644
--- a/src/yuzu_cmd/config.cpp
+++ b/src/yuzu_cmd/config.cpp
@@ -288,6 +288,8 @@ void Config::ReadValues() {
288 Settings::values.debug_pad_analogs[i] = default_param; 288 Settings::values.debug_pad_analogs[i] = default_param;
289 } 289 }
290 290
291 Settings::values.vibration_enabled =
292 sdl2_config->GetBoolean("ControlsGeneral", "vibration_enabled", true);
291 Settings::values.touchscreen.enabled = 293 Settings::values.touchscreen.enabled =
292 sdl2_config->GetBoolean("ControlsGeneral", "touch_enabled", true); 294 sdl2_config->GetBoolean("ControlsGeneral", "touch_enabled", true);
293 Settings::values.touchscreen.device = 295 Settings::values.touchscreen.device =
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
index e5e684206..a804d5185 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
@@ -13,23 +13,25 @@
13#include "input_common/sdl/sdl.h" 13#include "input_common/sdl/sdl.h"
14#include "yuzu_cmd/emu_window/emu_window_sdl2.h" 14#include "yuzu_cmd/emu_window/emu_window_sdl2.h"
15 15
16EmuWindow_SDL2::EmuWindow_SDL2(Core::System& system, bool fullscreen) : system{system} { 16EmuWindow_SDL2::EmuWindow_SDL2(Core::System& system, bool fullscreen,
17 InputCommon::InputSubsystem* input_subsystem_)
18 : system{system}, input_subsystem{input_subsystem_} {
17 if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK) < 0) { 19 if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK) < 0) {
18 LOG_CRITICAL(Frontend, "Failed to initialize SDL2! Exiting..."); 20 LOG_CRITICAL(Frontend, "Failed to initialize SDL2! Exiting...");
19 exit(1); 21 exit(1);
20 } 22 }
21 InputCommon::Init(); 23 input_subsystem->Initialize();
22 SDL_SetMainReady(); 24 SDL_SetMainReady();
23} 25}
24 26
25EmuWindow_SDL2::~EmuWindow_SDL2() { 27EmuWindow_SDL2::~EmuWindow_SDL2() {
26 InputCommon::Shutdown(); 28 input_subsystem->Shutdown();
27 SDL_Quit(); 29 SDL_Quit();
28} 30}
29 31
30void EmuWindow_SDL2::OnMouseMotion(s32 x, s32 y) { 32void EmuWindow_SDL2::OnMouseMotion(s32 x, s32 y) {
31 TouchMoved((unsigned)std::max(x, 0), (unsigned)std::max(y, 0)); 33 TouchMoved((unsigned)std::max(x, 0), (unsigned)std::max(y, 0));
32 InputCommon::GetMotionEmu()->Tilt(x, y); 34 input_subsystem->GetMotionEmu()->Tilt(x, y);
33} 35}
34 36
35void EmuWindow_SDL2::OnMouseButton(u32 button, u8 state, s32 x, s32 y) { 37void EmuWindow_SDL2::OnMouseButton(u32 button, u8 state, s32 x, s32 y) {
@@ -41,9 +43,9 @@ void EmuWindow_SDL2::OnMouseButton(u32 button, u8 state, s32 x, s32 y) {
41 } 43 }
42 } else if (button == SDL_BUTTON_RIGHT) { 44 } else if (button == SDL_BUTTON_RIGHT) {
43 if (state == SDL_PRESSED) { 45 if (state == SDL_PRESSED) {
44 InputCommon::GetMotionEmu()->BeginTilt(x, y); 46 input_subsystem->GetMotionEmu()->BeginTilt(x, y);
45 } else { 47 } else {
46 InputCommon::GetMotionEmu()->EndTilt(); 48 input_subsystem->GetMotionEmu()->EndTilt();
47 } 49 }
48 } 50 }
49} 51}
@@ -79,9 +81,9 @@ void EmuWindow_SDL2::OnFingerUp() {
79 81
80void EmuWindow_SDL2::OnKeyEvent(int key, u8 state) { 82void EmuWindow_SDL2::OnKeyEvent(int key, u8 state) {
81 if (state == SDL_PRESSED) { 83 if (state == SDL_PRESSED) {
82 InputCommon::GetKeyboard()->PressKey(key); 84 input_subsystem->GetKeyboard()->PressKey(key);
83 } else if (state == SDL_RELEASED) { 85 } else if (state == SDL_RELEASED) {
84 InputCommon::GetKeyboard()->ReleaseKey(key); 86 input_subsystem->GetKeyboard()->ReleaseKey(key);
85 } 87 }
86} 88}
87 89
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2.h b/src/yuzu_cmd/emu_window/emu_window_sdl2.h
index fffac4252..82750ffec 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2.h
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2.h
@@ -14,9 +14,14 @@ namespace Core {
14class System; 14class System;
15} 15}
16 16
17namespace InputCommon {
18class InputSubsystem;
19}
20
17class EmuWindow_SDL2 : public Core::Frontend::EmuWindow { 21class EmuWindow_SDL2 : public Core::Frontend::EmuWindow {
18public: 22public:
19 explicit EmuWindow_SDL2(Core::System& system, bool fullscreen); 23 explicit EmuWindow_SDL2(Core::System& system, bool fullscreen,
24 InputCommon::InputSubsystem* input_subsystem);
20 ~EmuWindow_SDL2(); 25 ~EmuWindow_SDL2();
21 26
22 /// Polls window events 27 /// Polls window events
@@ -76,4 +81,7 @@ protected:
76 81
77 /// Keeps track of how often to update the title bar during gameplay 82 /// Keeps track of how often to update the title bar during gameplay
78 u32 last_time = 0; 83 u32 last_time = 0;
84
85 /// Input subsystem to use with this window.
86 InputCommon::InputSubsystem* input_subsystem;
79}; 87};
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 e78025737..881b67a76 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp
@@ -87,8 +87,9 @@ bool EmuWindow_SDL2_GL::SupportsRequiredGLExtensions() {
87 return unsupported_ext.empty(); 87 return unsupported_ext.empty();
88} 88}
89 89
90EmuWindow_SDL2_GL::EmuWindow_SDL2_GL(Core::System& system, bool fullscreen) 90EmuWindow_SDL2_GL::EmuWindow_SDL2_GL(Core::System& system, bool fullscreen,
91 : EmuWindow_SDL2{system, fullscreen} { 91 InputCommon::InputSubsystem* input_subsystem)
92 : EmuWindow_SDL2{system, fullscreen, input_subsystem} {
92 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4); 93 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4);
93 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3); 94 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
94 SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_COMPATIBILITY); 95 SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_COMPATIBILITY);
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.h b/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.h
index 48bb41683..732a64edd 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.h
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.h
@@ -8,9 +8,14 @@
8#include "core/frontend/emu_window.h" 8#include "core/frontend/emu_window.h"
9#include "yuzu_cmd/emu_window/emu_window_sdl2.h" 9#include "yuzu_cmd/emu_window/emu_window_sdl2.h"
10 10
11namespace InputCommon {
12class InputSubsystem;
13}
14
11class EmuWindow_SDL2_GL final : public EmuWindow_SDL2 { 15class EmuWindow_SDL2_GL final : public EmuWindow_SDL2 {
12public: 16public:
13 explicit EmuWindow_SDL2_GL(Core::System& system, bool fullscreen); 17 explicit EmuWindow_SDL2_GL(Core::System& system, bool fullscreen,
18 InputCommon::InputSubsystem* input_subsystem);
14 ~EmuWindow_SDL2_GL(); 19 ~EmuWindow_SDL2_GL();
15 20
16 void Present() override; 21 void Present() override;
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp
index cb8e68a39..53491f86e 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp
@@ -19,8 +19,9 @@
19#include <SDL.h> 19#include <SDL.h>
20#include <SDL_syswm.h> 20#include <SDL_syswm.h>
21 21
22EmuWindow_SDL2_VK::EmuWindow_SDL2_VK(Core::System& system, bool fullscreen) 22EmuWindow_SDL2_VK::EmuWindow_SDL2_VK(Core::System& system, bool fullscreen,
23 : EmuWindow_SDL2{system, fullscreen} { 23 InputCommon::InputSubsystem* input_subsystem)
24 : EmuWindow_SDL2{system, fullscreen, input_subsystem} {
24 const std::string window_title = fmt::format("yuzu {} | {}-{} (Vulkan)", Common::g_build_name, 25 const std::string window_title = fmt::format("yuzu {} | {}-{} (Vulkan)", Common::g_build_name,
25 Common::g_scm_branch, Common::g_scm_desc); 26 Common::g_scm_branch, Common::g_scm_desc);
26 render_window = 27 render_window =
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.h b/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.h
index 77a6ca72b..f99704d4c 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.h
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.h
@@ -13,9 +13,14 @@ namespace Core {
13class System; 13class System;
14} 14}
15 15
16namespace InputCommon {
17class InputSubsystem;
18}
19
16class EmuWindow_SDL2_VK final : public EmuWindow_SDL2 { 20class EmuWindow_SDL2_VK final : public EmuWindow_SDL2 {
17public: 21public:
18 explicit EmuWindow_SDL2_VK(Core::System& system, bool fullscreen); 22 explicit EmuWindow_SDL2_VK(Core::System& system, bool fullscreen,
23 InputCommon::InputSubsystem* input_subsystem);
19 ~EmuWindow_SDL2_VK(); 24 ~EmuWindow_SDL2_VK();
20 25
21 void Present() override; 26 void Present() override;
diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp
index 8efe49390..4f00c804d 100644
--- a/src/yuzu_cmd/yuzu.cpp
+++ b/src/yuzu_cmd/yuzu.cpp
@@ -23,12 +23,14 @@
23#include "common/telemetry.h" 23#include "common/telemetry.h"
24#include "core/core.h" 24#include "core/core.h"
25#include "core/crypto/key_manager.h" 25#include "core/crypto/key_manager.h"
26#include "core/file_sys/registered_cache.h"
26#include "core/file_sys/vfs_real.h" 27#include "core/file_sys/vfs_real.h"
27#include "core/gdbstub/gdbstub.h" 28#include "core/gdbstub/gdbstub.h"
28#include "core/hle/service/filesystem/filesystem.h" 29#include "core/hle/service/filesystem/filesystem.h"
29#include "core/loader/loader.h" 30#include "core/loader/loader.h"
30#include "core/settings.h" 31#include "core/settings.h"
31#include "core/telemetry_session.h" 32#include "core/telemetry_session.h"
33#include "input_common/main.h"
32#include "video_core/renderer_base.h" 34#include "video_core/renderer_base.h"
33#include "yuzu_cmd/config.h" 35#include "yuzu_cmd/config.h"
34#include "yuzu_cmd/emu_window/emu_window_sdl2.h" 36#include "yuzu_cmd/emu_window/emu_window_sdl2.h"
@@ -37,8 +39,6 @@
37#include "yuzu_cmd/emu_window/emu_window_sdl2_vk.h" 39#include "yuzu_cmd/emu_window/emu_window_sdl2_vk.h"
38#endif 40#endif
39 41
40#include "core/file_sys/registered_cache.h"
41
42#ifdef _WIN32 42#ifdef _WIN32
43// windows.h needs to be included before shellapi.h 43// windows.h needs to be included before shellapi.h
44#include <windows.h> 44#include <windows.h>
@@ -179,15 +179,16 @@ int main(int argc, char** argv) {
179 Settings::Apply(); 179 Settings::Apply();
180 180
181 Core::System& system{Core::System::GetInstance()}; 181 Core::System& system{Core::System::GetInstance()};
182 InputCommon::InputSubsystem input_subsystem;
182 183
183 std::unique_ptr<EmuWindow_SDL2> emu_window; 184 std::unique_ptr<EmuWindow_SDL2> emu_window;
184 switch (Settings::values.renderer_backend.GetValue()) { 185 switch (Settings::values.renderer_backend.GetValue()) {
185 case Settings::RendererBackend::OpenGL: 186 case Settings::RendererBackend::OpenGL:
186 emu_window = std::make_unique<EmuWindow_SDL2_GL>(system, fullscreen); 187 emu_window = std::make_unique<EmuWindow_SDL2_GL>(system, fullscreen, &input_subsystem);
187 break; 188 break;
188 case Settings::RendererBackend::Vulkan: 189 case Settings::RendererBackend::Vulkan:
189#ifdef HAS_VULKAN 190#ifdef HAS_VULKAN
190 emu_window = std::make_unique<EmuWindow_SDL2_VK>(system, fullscreen); 191 emu_window = std::make_unique<EmuWindow_SDL2_VK>(system, fullscreen, &input_subsystem);
191 break; 192 break;
192#else 193#else
193 LOG_CRITICAL(Frontend, "Vulkan backend has not been compiled!"); 194 LOG_CRITICAL(Frontend, "Vulkan backend has not been compiled!");
diff --git a/src/yuzu_tester/config.cpp b/src/yuzu_tester/config.cpp
index 74022af23..aaf59129a 100644
--- a/src/yuzu_tester/config.cpp
+++ b/src/yuzu_tester/config.cpp
@@ -75,6 +75,7 @@ void Config::ReadValues() {
75 Settings::values.debug_pad_analogs[i] = ""; 75 Settings::values.debug_pad_analogs[i] = "";
76 } 76 }
77 77
78 Settings::values.vibration_enabled = true;
78 Settings::values.touchscreen.enabled = ""; 79 Settings::values.touchscreen.enabled = "";
79 Settings::values.touchscreen.device = ""; 80 Settings::values.touchscreen.device = "";
80 Settings::values.touchscreen.finger = 0; 81 Settings::values.touchscreen.finger = 0;
diff --git a/src/yuzu_tester/emu_window/emu_window_sdl2_hide.cpp b/src/yuzu_tester/emu_window/emu_window_sdl2_hide.cpp
index 8584f6671..78f75fb38 100644
--- a/src/yuzu_tester/emu_window/emu_window_sdl2_hide.cpp
+++ b/src/yuzu_tester/emu_window/emu_window_sdl2_hide.cpp
@@ -13,7 +13,6 @@
13 13
14#include <glad/glad.h> 14#include <glad/glad.h>
15 15
16#include "common/assert.h"
17#include "common/logging/log.h" 16#include "common/logging/log.h"
18#include "common/scm_rev.h" 17#include "common/scm_rev.h"
19#include "core/settings.h" 18#include "core/settings.h"
@@ -53,7 +52,7 @@ EmuWindow_SDL2_Hide::EmuWindow_SDL2_Hide() {
53 exit(1); 52 exit(1);
54 } 53 }
55 54
56 InputCommon::Init(); 55 input_subsystem->Initialize();
57 56
58 SDL_SetMainReady(); 57 SDL_SetMainReady();
59 58
@@ -105,7 +104,7 @@ EmuWindow_SDL2_Hide::EmuWindow_SDL2_Hide() {
105} 104}
106 105
107EmuWindow_SDL2_Hide::~EmuWindow_SDL2_Hide() { 106EmuWindow_SDL2_Hide::~EmuWindow_SDL2_Hide() {
108 InputCommon::Shutdown(); 107 input_subsystem->Shutdown();
109 SDL_GL_DeleteContext(gl_context); 108 SDL_GL_DeleteContext(gl_context);
110 SDL_Quit(); 109 SDL_Quit();
111} 110}
diff --git a/src/yuzu_tester/emu_window/emu_window_sdl2_hide.h b/src/yuzu_tester/emu_window/emu_window_sdl2_hide.h
index c13a82df2..a553b4b95 100644
--- a/src/yuzu_tester/emu_window/emu_window_sdl2_hide.h
+++ b/src/yuzu_tester/emu_window/emu_window_sdl2_hide.h
@@ -8,6 +8,10 @@
8 8
9struct SDL_Window; 9struct SDL_Window;
10 10
11namespace InputCommon {
12class InputSubsystem;
13}
14
11class EmuWindow_SDL2_Hide : public Core::Frontend::EmuWindow { 15class EmuWindow_SDL2_Hide : public Core::Frontend::EmuWindow {
12public: 16public:
13 explicit EmuWindow_SDL2_Hide(); 17 explicit EmuWindow_SDL2_Hide();
@@ -25,6 +29,8 @@ private:
25 /// Whether the GPU and driver supports the OpenGL extension required 29 /// Whether the GPU and driver supports the OpenGL extension required
26 bool SupportsRequiredGLExtensions(); 30 bool SupportsRequiredGLExtensions();
27 31
32 std::unique_ptr<InputCommon::InputSubsystem> input_subsystem;
33
28 /// Internal SDL2 render window 34 /// Internal SDL2 render window
29 SDL_Window* render_window; 35 SDL_Window* render_window;
30 36