From 9d4edd4e88d291aa4b778b74f6e9daa9dd358d2b Mon Sep 17 00:00:00 2001 From: Morph <39850852+Morph1984@users.noreply.github.com> Date: Sun, 13 Sep 2020 09:25:22 -0400 Subject: [PATCH 01/37] ui/themes: Cleanup UI --- dist/qt_themes/default/style.qss | 14 +- dist/qt_themes/qdarkstyle/style.qss | 71 ++---- .../qdarkstyle_midnight_blue/style.qss | 81 ++---- src/yuzu/aboutdialog.ui | 20 -- src/yuzu/applets/controller.ui | 31 +-- src/yuzu/configuration/configure.ui | 20 -- .../configure_debug_controller.ui | 20 -- src/yuzu/configuration/configure_input.ui | 24 +- .../configure_input_advanced.cpp | 4 +- .../configuration/configure_input_advanced.ui | 192 +++++++------- .../configuration/configure_input_player.ui | 241 ++++++++++-------- .../configuration/configure_motion_touch.ui | 10 - .../configuration/configure_mouse_advanced.ui | 46 +--- src/yuzu/configuration/configure_per_game.ui | 20 -- .../configure_touch_from_button.ui | 10 - .../configure_touchscreen_advanced.ui | 22 +- 16 files changed, 326 insertions(+), 500 deletions(-) diff --git a/dist/qt_themes/default/style.qss b/dist/qt_themes/default/style.qss index b6dd2063d..836dd25ca 100644 --- a/dist/qt_themes/default/style.qss +++ b/dist/qt_themes/default/style.qss @@ -1,3 +1,7 @@ +QAbstractSpinBox { + min-height: 19px; +} + QPushButton#TogglableStatusBarButton { color: #959595; border: 1px solid transparent; @@ -35,10 +39,10 @@ QPushButton#RendererStatusBarButton:!checked { } QPushButton#buttonRefreshDevices { - min-width: 20px; - min-height: 20px; - max-width: 20px; - max-height: 20px; + min-width: 21px; + min-height: 21px; + max-width: 21px; + max-height: 21px; } QWidget#bottomPerGameInput, @@ -71,7 +75,7 @@ QWidget#middleControllerApplet { QWidget#topPerGameInput QComboBox, QWidget#middleControllerApplet QComboBox { - width: 123px; + width: 120px; } QWidget#connectedControllers { diff --git a/dist/qt_themes/qdarkstyle/style.qss b/dist/qt_themes/qdarkstyle/style.qss index 66026e8be..aca6531ac 100644 --- a/dist/qt_themes/qdarkstyle/style.qss +++ b/dist/qt_themes/qdarkstyle/style.qss @@ -99,12 +99,19 @@ QGroupBox::indicator:unchecked:disabled { } QRadioButton { - spacing: 5px; - outline: none; color: #eff0f1; + spacing: 3px; + padding: 0px; + border: none; + outline: none; margin-bottom: 2px; } +QGroupBox QRadioButton { + padding-left: 0px; + padding-right: 7px; +} + QRadioButton:disabled { color: #76797C; } @@ -522,13 +529,12 @@ QToolButton#qt_toolbar_ext_button { QPushButton { color: #eff0f1; - border-width: 1px; - border-color: #54575B; - border-style: solid; - padding: 6px 4px; + border: 1px solid #54575B; border-radius: 2px; + padding: 5px 0px 5px 0px; outline: none; min-width: 100px; + min-height: 13px; background-color: #232629; } @@ -553,8 +559,9 @@ QComboBox { selection-background-color: #3daee9; border: 1px solid #54575B; border-radius: 2px; - padding: 4px 6px; - min-width: 75px; + padding: 0px 4px 0px 4px; + min-width: 60px; + min-height: 23px; background-color: #232629; } @@ -608,26 +615,26 @@ QComboBox::down-arrow:focus { } QAbstractSpinBox { - padding: 4px 6px; border: 1px solid #54575B; background-color: #232629; color: #eff0f1; border-radius: 2px; - min-width: 75px; + min-width: 52px; + min-height: 23px; } QAbstractSpinBox:up-button { background-color: transparent; subcontrol-origin: border; subcontrol-position: center right; - left: -6px; + left: -2px; } QAbstractSpinBox:down-button { background-color: transparent; subcontrol-origin: border; subcontrol-position: center left; - right: -6px; + right: -2px; } QAbstractSpinBox::up-arrow, @@ -1277,34 +1284,17 @@ QPushButton#RendererStatusBarButton:!checked { } QPushButton#buttonRefreshDevices { - min-width: 24px; - min-height: 24px; - max-width: 24px; - max-height: 24px; + min-width: 23px; + min-height: 23px; + max-width: 23px; + max-height: 23px; padding: 0px 0px; } QSpinBox#spinboxLStickRange, -QSpinBox#spinboxRStickRange { - padding: 4px 0px 5px 0px; - min-width: 63px; -} - +QSpinBox#spinboxRStickRange, QSpinBox#vibrationSpin { - padding: 4px 0px 5px 0px; - min-width: 63px; -} - -QSpinBox#spinboxLStickRange:up-button, -QSpinBox#spinboxRStickRange:up-button, -QSpinBox#vibrationSpin:up-button { - left: -2px; -} - -QSpinBox#spinboxLStickRange:down-button, -QSpinBox#spinboxRStickRange:down-button, -QSpinBox#vibrationSpin:down-button { - right: -1px; + min-width: 68px; } QGroupBox#motionGroup::indicator, @@ -1340,16 +1330,7 @@ QWidget#middleControllerApplet { QWidget#topPerGameInput QComboBox, QWidget#middleControllerApplet QComboBox { - width: 119px; -} - -QRadioButton#radioDocked { - margin-left: -3px; -} - - -QRadioButton#radioUndocked { - margin-right: 5px; + width: 120px; } QWidget#connectedControllers { diff --git a/dist/qt_themes/qdarkstyle_midnight_blue/style.qss b/dist/qt_themes/qdarkstyle_midnight_blue/style.qss index c6318ba4e..ad032a966 100644 --- a/dist/qt_themes/qdarkstyle_midnight_blue/style.qss +++ b/dist/qt_themes/qdarkstyle_midnight_blue/style.qss @@ -172,8 +172,8 @@ QCheckBox { color: #F0F0F0; spacing: 4px; outline: none; - padding-top: 4px; - padding-bottom: 4px; + padding-top: 2px; + padding-bottom: 2px; } QCheckBox:focus { @@ -239,7 +239,7 @@ QGroupBox { border: 1px solid #32414B; border-radius: 4px; margin-top: 12px; - padding: 4px; + padding: 2px; } QGroupBox::title { @@ -247,7 +247,7 @@ QGroupBox::title { subcontrol-position: top left; padding-left: 3px; padding-right: 5px; - padding-top: 4px; + padding-top: 2px; } QGroupBox::indicator { @@ -298,6 +298,11 @@ QRadioButton { outline: none; } +QGroupBox QRadioButton { + padding-left: 0px; + padding-right: 7px; +} + QRadioButton:focus { border: none; } @@ -321,7 +326,6 @@ QRadioButton QWidget { QRadioButton::indicator { border: none; outline: none; - margin-left: 4px; height: 16px; width: 16px; } @@ -785,14 +789,8 @@ QAbstractSpinBox { background-color: #19232D; border: 1px solid #32414B; color: #F0F0F0; - /* This fixes 103, 111 */ - padding-top: 2px; - /* This fixes 103, 111 */ - padding-bottom: 2px; - padding-left: 4px; - padding-right: 4px; border-radius: 4px; - /* min-width: 5px; removed to fix 109 */ + min-height: 19px; } QAbstractSpinBox:up-button { @@ -997,10 +995,11 @@ QPushButton { border: 1px solid #32414B; color: #F0F0F0; border-radius: 4px; - padding: 3px; + padding: 3px 0px 3px 0px; outline: none; /* Issue #194 - Special case of QPushButton inside dialogs, for better UI */ min-width: 80px; + min-height: 13px; } QPushButton:disabled { @@ -1008,14 +1007,14 @@ QPushButton:disabled { border: 1px solid #32414B; color: #787878; border-radius: 4px; - padding: 3px; + padding: 3px 0px 3px 0px; } QPushButton:checked { background-color: #32414B; border: 1px solid #32414B; border-radius: 4px; - padding: 3px; + padding: 3px 0px 3px 0px; outline: none; } @@ -1024,7 +1023,7 @@ QPushButton:checked:disabled { border: 1px solid #32414B; color: #787878; border-radius: 4px; - padding: 3px; + padding: 3px 0px 3px 0px; outline: none; } @@ -1197,15 +1196,9 @@ QComboBox { border: 1px solid #32414B; border-radius: 4px; selection-background-color: #1464A0; - padding-left: 4px; - padding-right: 36px; - /* 4 + 16*2 See scrollbar size */ - /* Fixes #103, #111 */ - min-height: 1.5em; - /* padding-top: 2px; removed to fix #132 */ - /* padding-bottom: 2px; removed to fix #132 */ - /* min-width: 75px; removed to fix #109 */ - /* Needed to remove indicator - fix #132 */ + padding: 0px 4px 0px 4px; + min-width: 60px; + min-height: 19px; } QComboBox QAbstractItemView { @@ -2198,16 +2191,17 @@ QPushButton#RendererStatusBarButton:!checked { } QPushButton#buttonRefreshDevices { - min-width: 20px; - min-height: 20px; - max-width: 20px; - max-height: 20px; + min-width: 19px; + min-height: 19px; + max-width: 19px; + max-height: 19px; padding: 0px 0px; } QSpinBox#spinboxLStickRange, -QSpinBox#spinboxRStickRange { - min-width: 38px; +QSpinBox#spinboxRStickRange, +QSpinBox#vibrationSpin { + min-width: 68px; } QGroupBox#motionGroup::indicator, @@ -2260,26 +2254,7 @@ QWidget#middleControllerApplet { QWidget#topPerGameInput QComboBox, QWidget#middleControllerApplet QComboBox { - padding-right: 2px; - width: 127px; -} - -QGroupBox#handheldGroup { - padding-left: 0px; -} - -QRadioButton#radioDocked { - margin-left: -1px; - padding-left: 0px; -} - -QRadioButton#radioDocked::indicator { - margin-left: 0px; -} - - -QRadioButton#radioUndocked { - margin-right: 2px; + width: 120px; } QWidget#connectedControllers { @@ -2352,7 +2327,7 @@ QCheckBox#checkboxPlayer5Connected, QCheckBox#checkboxPlayer6Connected, QCheckBox#checkboxPlayer7Connected, QCheckBox#checkboxPlayer8Connected { - spacing: 0px; + spacing: 0px; } QWidget#connectedControllers QLabel { @@ -2427,7 +2402,7 @@ QCheckBox#checkboxPlayer7Connected::indicator, QCheckBox#checkboxPlayer8Connected::indicator { width: 14px; height: 14px; - margin-left: 2px; + margin-left: 0px; } QWidget#Player1LEDs QCheckBox::indicator:checked, diff --git a/src/yuzu/aboutdialog.ui b/src/yuzu/aboutdialog.ui index f122ba39d..1b320630c 100644 --- a/src/yuzu/aboutdialog.ui +++ b/src/yuzu/aboutdialog.ui @@ -160,32 +160,12 @@ p, li { white-space: pre-wrap; } accepted() AboutDialog accept() - - - 248 - 254 - - - 157 - 274 - - buttonBox rejected() AboutDialog reject() - - - 316 - 260 - - - 286 - 274 - - diff --git a/src/yuzu/applets/controller.ui b/src/yuzu/applets/controller.ui index c4108a979..2ab69a2d3 100644 --- a/src/yuzu/applets/controller.ui +++ b/src/yuzu/applets/controller.ui @@ -1217,9 +1217,6 @@ - - false - Pro Controller @@ -2279,7 +2276,7 @@ 6 - 6 + 8 6 @@ -2335,13 +2332,13 @@ - 65 - 0 + 68 + 21 - 65 + 68 16777215 @@ -2387,18 +2384,18 @@ - 57 + 68 0 - 55 + 68 16777215 - min-width: 55px; + min-width: 68px; Configure @@ -2430,12 +2427,12 @@ - 65 + 68 16777215 - min-width: 55px; + min-width: 68px; Open @@ -2657,16 +2654,6 @@ accepted() QtControllerSelectorDialog accept() - - - 20 - 20 - - - 20 - 20 - - diff --git a/src/yuzu/configuration/configure.ui b/src/yuzu/configuration/configure.ui index fcf42cdcb..f92c3aff3 100644 --- a/src/yuzu/configuration/configure.ui +++ b/src/yuzu/configuration/configure.ui @@ -275,32 +275,12 @@ accepted() ConfigureDialog accept() - - - 220 - 380 - - - 220 - 200 - - buttonBox rejected() ConfigureDialog reject() - - - 220 - 380 - - - 220 - 200 - - diff --git a/src/yuzu/configuration/configure_debug_controller.ui b/src/yuzu/configuration/configure_debug_controller.ui index a95ed50ff..7b7e6582c 100644 --- a/src/yuzu/configuration/configure_debug_controller.ui +++ b/src/yuzu/configuration/configure_debug_controller.ui @@ -66,32 +66,12 @@ accepted() ConfigureDebugController accept() - - - 140 - 318 - - - 140 - 169 - - buttonBox rejected() ConfigureDebugController reject() - - - 140 - 318 - - - 140 - 169 - - diff --git a/src/yuzu/configuration/configure_input.ui b/src/yuzu/configuration/configure_input.ui index 136955224..b74481bda 100644 --- a/src/yuzu/configuration/configure_input.ui +++ b/src/yuzu/configuration/configure_input.ui @@ -142,7 +142,7 @@ 6 - 3 + 8 6 @@ -198,13 +198,13 @@ - 65 + 68 21 - 65 + 68 16777215 @@ -250,18 +250,18 @@ - 57 + 68 0 - 55 + 68 16777215 - min-width: 55px; + min-width: 68px; Configure @@ -468,13 +468,13 @@ - 57 + 68 0 - 55 + 68 16777215 @@ -494,7 +494,7 @@ Qt::LeftToRight - min-width: 55px; + min-width: 68px; Defaults @@ -511,13 +511,13 @@ - 57 + 68 0 - 55 + 68 16777215 @@ -537,7 +537,7 @@ Qt::LeftToRight - min-width: 55px; + min-width: 68px; Clear diff --git a/src/yuzu/configuration/configure_input_advanced.cpp b/src/yuzu/configuration/configure_input_advanced.cpp index 81f9dc16c..3715db0ab 100644 --- a/src/yuzu/configuration/configure_input_advanced.cpp +++ b/src/yuzu/configuration/configure_input_advanced.cpp @@ -101,7 +101,7 @@ void ConfigureInputAdvanced::OnControllerButtonClick(int player_idx, int button_ } controllers_colors[player_idx][button_idx] = new_bg_color; controllers_color_buttons[player_idx][button_idx]->setStyleSheet( - QStringLiteral("background-color: %1; min-width: 55px;") + QStringLiteral("background-color: %1; min-width: 60px;") .arg(controllers_colors[player_idx][button_idx].name())); } @@ -139,7 +139,7 @@ void ConfigureInputAdvanced::LoadConfiguration() { for (std::size_t button_idx = 0; button_idx < colors.size(); ++button_idx) { controllers_color_buttons[player_idx][button_idx]->setStyleSheet( - QStringLiteral("background-color: %1; min-width: 55px;") + QStringLiteral("background-color: %1; min-width: 60px;") .arg(controllers_colors[player_idx][button_idx].name())); } } diff --git a/src/yuzu/configuration/configure_input_advanced.ui b/src/yuzu/configuration/configure_input_advanced.ui index 5958435fc..a880a7c68 100644 --- a/src/yuzu/configuration/configure_input_advanced.ui +++ b/src/yuzu/configuration/configure_input_advanced.ui @@ -192,18 +192,18 @@ - 57 + 68 0 - 55 + 68 16777215 - min-width: 55px; + min-width: 68px; @@ -247,18 +247,18 @@ - 57 + 68 0 - 55 + 68 16777215 - min-width: 55px; + min-width: 68px; @@ -323,18 +323,18 @@ - 57 + 68 0 - 55 + 68 16777215 - min-width: 55px; + min-width: 68px; @@ -378,18 +378,18 @@ - 57 + 68 0 - 55 + 68 16777215 - min-width: 55px; + min-width: 68px; @@ -478,18 +478,18 @@ - 57 + 68 0 - 55 + 68 16777215 - min-width: 55px; + min-width: 68px; @@ -533,18 +533,18 @@ - 57 + 68 0 - 55 + 68 16777215 - min-width: 55px; + min-width: 68px; @@ -609,18 +609,18 @@ - 57 + 68 0 - 55 + 68 16777215 - min-width: 55px; + min-width: 68px; @@ -664,18 +664,18 @@ - 57 + 68 0 - 55 + 68 16777215 - min-width: 55px; + min-width: 68px; @@ -782,18 +782,18 @@ - 57 + 68 0 - 55 + 68 16777215 - min-width: 55px; + min-width: 68px; @@ -837,18 +837,18 @@ - 57 + 68 0 - 55 + 68 16777215 - min-width: 55px; + min-width: 68px; @@ -913,18 +913,18 @@ - 57 + 68 0 - 55 + 68 16777215 - min-width: 55px; + min-width: 68px; @@ -968,18 +968,18 @@ - 57 + 68 0 - 55 + 68 16777215 - min-width: 55px; + min-width: 68px; @@ -1068,18 +1068,18 @@ - 57 + 68 0 - 55 + 68 16777215 - min-width: 55px; + min-width: 68px; @@ -1123,18 +1123,18 @@ - 57 + 68 0 - 55 + 68 16777215 - min-width: 55px; + min-width: 68px; @@ -1199,18 +1199,18 @@ - 57 + 68 0 - 55 + 68 16777215 - min-width: 55px; + min-width: 68px; @@ -1254,18 +1254,18 @@ - 57 + 68 0 - 55 + 68 16777215 - min-width: 55px; + min-width: 68px; @@ -1393,18 +1393,18 @@ - 57 + 68 0 - 55 + 68 16777215 - min-width: 55px; + min-width: 68px; @@ -1448,18 +1448,18 @@ - 57 + 68 0 - 55 + 68 16777215 - min-width: 55px; + min-width: 68px; @@ -1524,18 +1524,18 @@ - 57 + 68 0 - 55 + 68 16777215 - min-width: 55px; + min-width: 68px; @@ -1579,18 +1579,18 @@ - 57 + 68 0 - 55 + 68 16777215 - min-width: 55px; + min-width: 68px; @@ -1679,18 +1679,18 @@ - 57 + 68 0 - 55 + 68 16777215 - min-width: 55px; + min-width: 68px; @@ -1734,18 +1734,18 @@ - 57 + 68 0 - 55 + 68 16777215 - min-width: 55px; + min-width: 68px; @@ -1810,18 +1810,18 @@ - 57 + 68 0 - 55 + 68 16777215 - min-width: 55px; + min-width: 68px; @@ -1865,18 +1865,18 @@ - 57 + 68 0 - 55 + 68 16777215 - min-width: 55px; + min-width: 68px; @@ -1983,18 +1983,18 @@ - 57 + 68 0 - 55 + 68 16777215 - min-width: 55px; + min-width: 68px; @@ -2038,18 +2038,18 @@ - 57 + 68 0 - 55 + 68 16777215 - min-width: 55px; + min-width: 68px; @@ -2114,18 +2114,18 @@ - 57 + 68 0 - 55 + 68 16777215 - min-width: 55px; + min-width: 68px; @@ -2169,18 +2169,18 @@ - 57 + 68 0 - 55 + 68 16777215 - min-width: 55px; + min-width: 68px; @@ -2269,18 +2269,18 @@ - 57 + 68 0 - 55 + 68 16777215 - min-width: 55px; + min-width: 68px; @@ -2324,18 +2324,18 @@ - 57 + 68 0 - 55 + 68 16777215 - min-width: 55px; + min-width: 68px; @@ -2400,18 +2400,18 @@ - 57 + 68 0 - 55 + 68 16777215 - min-width: 55px; + min-width: 68px; @@ -2455,18 +2455,18 @@ - 57 + 68 0 - 55 + 68 16777215 - min-width: 55px; + min-width: 68px; diff --git a/src/yuzu/configuration/configure_input_player.ui b/src/yuzu/configuration/configure_input_player.ui index e03461d9d..1e78b4c10 100644 --- a/src/yuzu/configuration/configure_input_player.ui +++ b/src/yuzu/configuration/configure_input_player.ui @@ -83,6 +83,12 @@ + + + 0 + 21 + + Pro Controller @@ -136,6 +142,12 @@ + + + 0 + 21 + + Any @@ -152,14 +164,14 @@ - 24 - 22 + 21 + 21 - 24 - 22 + 21 + 21 @@ -198,18 +210,25 @@ 5 - + + + + 0 + 21 + + + - 55 + 68 16777215 - min-width: 55px; + min-width: 68px; Save @@ -220,12 +239,12 @@ - 55 + 68 16777215 - min-width: 55px; + min-width: 68px; New @@ -236,12 +255,12 @@ - 55 + 68 16777215 - min-width: 55px; + min-width: 68px; Delete @@ -393,18 +412,18 @@ - 57 + 68 0 - 55 + 68 16777215 - min-width: 55px; + min-width: 68px; Up @@ -463,18 +482,18 @@ - 57 + 68 0 - 55 + 68 16777215 - min-width: 55px; + min-width: 68px; Left @@ -512,18 +531,18 @@ - 57 + 68 0 - 55 + 68 16777215 - min-width: 55px; + min-width: 68px; Right @@ -594,18 +613,18 @@ - 57 + 68 0 - 55 + 68 16777215 - min-width: 55px; + min-width: 68px; Down @@ -664,18 +683,18 @@ - 57 + 68 0 - 55 + 68 16777215 - min-width: 55px; + min-width: 68px; Pressed @@ -713,18 +732,18 @@ - 57 + 68 0 - 55 + 68 16777215 - min-width: 55px; + min-width: 68px; Modifier @@ -759,13 +778,13 @@ - 55 + 68 21 - 55 + 68 16777215 @@ -966,18 +985,18 @@ - 57 + 68 0 - 55 + 68 16777215 - min-width: 55px; + min-width: 68px; Up @@ -1036,18 +1055,18 @@ - 57 + 68 0 - 55 + 68 16777215 - min-width: 55px; + min-width: 68px; Left @@ -1085,18 +1104,18 @@ - 57 + 68 0 - 55 + 68 16777215 - min-width: 55px; + min-width: 68px; Right @@ -1167,18 +1186,18 @@ - 57 + 68 0 - 55 + 68 16777215 - min-width: 55px; + min-width: 68px; Down @@ -1292,18 +1311,18 @@ - 57 + 68 0 - 55 + 68 16777215 - min-width: 55px; + min-width: 68px; L @@ -1341,18 +1360,18 @@ - 57 + 68 0 - 55 + 68 16777215 - min-width: 55px; + min-width: 68px; ZL @@ -1445,18 +1464,18 @@ - 57 + 68 0 - 55 + 68 16777215 - min-width: 55px; + min-width: 68px; Minus @@ -1494,18 +1513,18 @@ - 57 + 68 0 - 55 + 68 16777215 - min-width: 55px; + min-width: 68px; Capture @@ -1564,18 +1583,18 @@ - 57 + 68 0 - 55 + 68 16777215 - min-width: 55px; + min-width: 68px; Plus @@ -1613,18 +1632,18 @@ - 57 + 68 0 - 55 + 68 16777215 - min-width: 55px; + min-width: 68px; Home @@ -1717,18 +1736,18 @@ - 57 + 68 0 - 55 + 68 16777215 - min-width: 55px; + min-width: 68px; R @@ -1766,18 +1785,18 @@ - 57 + 68 0 - 55 + 68 16777215 - min-width: 55px; + min-width: 68px; ZR @@ -1870,18 +1889,18 @@ - 57 + 68 0 - 55 + 68 16777215 - min-width: 55px; + min-width: 68px; SL @@ -1919,18 +1938,18 @@ - 57 + 68 0 - 55 + 68 16777215 - min-width: 55px; + min-width: 68px; SR @@ -2027,18 +2046,18 @@ - 57 + 68 0 - 55 + 68 16777215 - min-width: 55px; + min-width: 68px; Left @@ -2076,18 +2095,18 @@ - 57 + 68 0 - 55 + 68 16777215 - min-width: 55px; + min-width: 68px; Right @@ -2225,18 +2244,18 @@ - 57 + 68 0 - 55 + 68 16777215 - min-width: 55px; + min-width: 68px; X @@ -2295,18 +2314,18 @@ - 57 + 68 0 - 55 + 68 16777215 - min-width: 55px; + min-width: 68px; Y @@ -2344,18 +2363,18 @@ - 57 + 68 0 - 55 + 68 16777215 - min-width: 55px; + min-width: 68px; A @@ -2426,18 +2445,18 @@ - 57 + 68 0 - 55 + 68 16777215 - min-width: 55px; + min-width: 68px; B @@ -2580,18 +2599,18 @@ - 57 + 68 0 - 55 + 68 16777215 - min-width: 55px; + min-width: 68px; Up @@ -2650,18 +2669,18 @@ - 57 + 68 0 - 55 + 68 16777215 - min-width: 55px; + min-width: 68px; Left @@ -2699,18 +2718,18 @@ - 57 + 68 0 - 55 + 68 16777215 - min-width: 55px; + min-width: 68px; Right @@ -2781,18 +2800,18 @@ - 57 + 68 0 - 55 + 68 16777215 - min-width: 55px; + min-width: 68px; Down @@ -2851,18 +2870,18 @@ - 57 + 68 0 - 55 + 68 16777215 - min-width: 55px; + min-width: 68px; Pressed @@ -2900,18 +2919,18 @@ - 57 + 68 0 - 55 + 68 16777215 - min-width: 55px; + min-width: 68px; Modifier @@ -2946,13 +2965,13 @@ - 55 + 68 21 - 55 + 68 16777215 diff --git a/src/yuzu/configuration/configure_motion_touch.ui b/src/yuzu/configuration/configure_motion_touch.ui index 602cf8cd8..5b78c5a4b 100644 --- a/src/yuzu/configuration/configure_motion_touch.ui +++ b/src/yuzu/configuration/configure_motion_touch.ui @@ -312,16 +312,6 @@ accepted() ConfigureMotionTouch ApplyConfiguration() - - - 220 - 380 - - - 220 - 200 - - diff --git a/src/yuzu/configuration/configure_mouse_advanced.ui b/src/yuzu/configuration/configure_mouse_advanced.ui index 74552fdbd..5b99e1c37 100644 --- a/src/yuzu/configuration/configure_mouse_advanced.ui +++ b/src/yuzu/configuration/configure_mouse_advanced.ui @@ -15,7 +15,7 @@ QPushButton { - min-width: 55px; + min-width: 60px; } @@ -42,13 +42,13 @@ - 57 + 68 0 - 16777215 + 68 16777215 @@ -82,7 +82,7 @@ - 57 + 68 0 @@ -110,7 +110,7 @@ - 57 + 68 0 @@ -138,13 +138,13 @@ - 57 + 68 0 - 16777215 + 68 16777215 @@ -204,13 +204,13 @@ - 57 + 68 0 - 16777215 + 68 16777215 @@ -256,13 +256,13 @@ - 57 + 68 0 - 16777215 + 68 16777215 @@ -275,13 +275,13 @@ - 57 + 68 0 - 16777215 + 68 16777215 @@ -324,32 +324,12 @@ accepted() ConfigureMouseAdvanced accept() - - - 124 - 266 - - - 124 - 143 - - buttonBox rejected() ConfigureMouseAdvanced reject() - - - 124 - 266 - - - 124 - 143 - - diff --git a/src/yuzu/configuration/configure_per_game.ui b/src/yuzu/configuration/configure_per_game.ui index d2057c4ab..25975b3b9 100644 --- a/src/yuzu/configuration/configure_per_game.ui +++ b/src/yuzu/configuration/configure_per_game.ui @@ -319,32 +319,12 @@ accepted() ConfigurePerGame accept() - - - 248 - 254 - - - 157 - 274 - - buttonBox rejected() ConfigurePerGame reject() - - - 316 - 260 - - - 286 - 274 - - diff --git a/src/yuzu/configuration/configure_touch_from_button.ui b/src/yuzu/configuration/configure_touch_from_button.ui index f581e27e0..757219d54 100644 --- a/src/yuzu/configuration/configure_touch_from_button.ui +++ b/src/yuzu/configuration/configure_touch_from_button.ui @@ -216,16 +216,6 @@ Drag points to change position, or double-click table cells to edit values.rejected() ConfigureTouchFromButton reject() - - - 249 - 428 - - - 249 - 224 - - diff --git a/src/yuzu/configuration/configure_touchscreen_advanced.ui b/src/yuzu/configuration/configure_touchscreen_advanced.ui index 1171c2dd1..30ceccddb 100644 --- a/src/yuzu/configuration/configure_touchscreen_advanced.ui +++ b/src/yuzu/configuration/configure_touchscreen_advanced.ui @@ -168,32 +168,12 @@ accepted() ConfigureTouchscreenAdvanced accept() - - - 140 - 318 - - - 140 - 169 - - buttonBox rejected() ConfigureTouchscreenAdvanced reject() - - - 140 - 318 - - - 140 - 169 - - - + From 75eaab2e0f48eb588c1bfb85f96630e199fbc1da Mon Sep 17 00:00:00 2001 From: Morph <39850852+Morph1984@users.noreply.github.com> Date: Thu, 17 Sep 2020 12:00:29 -0400 Subject: [PATCH 02/37] configure_input_player: Implement input exclusivity and persistence With this, the "Input Devices" combobox should accurately reflect the input device being used and disallows inputs from other input devices unless the input device is set to "Any". --- src/input_common/main.cpp | 6 +- src/yuzu/configuration/configure_input.cpp | 2 +- .../configuration/configure_input_player.cpp | 318 +++++++++++------- .../configuration/configure_input_player.h | 17 +- 4 files changed, 205 insertions(+), 138 deletions(-) diff --git a/src/input_common/main.cpp b/src/input_common/main.cpp index d32fd8b81..354c734fe 100644 --- a/src/input_common/main.cpp +++ b/src/input_common/main.cpp @@ -78,7 +78,7 @@ struct InputSubsystem::Impl { [[nodiscard]] std::vector GetInputDevices() const { std::vector devices = { Common::ParamPackage{{"display", "Any"}, {"class", "any"}}, - Common::ParamPackage{{"display", "Keyboard/Mouse"}, {"class", "key"}}, + Common::ParamPackage{{"display", "Keyboard/Mouse"}, {"class", "keyboard"}}, }; #ifdef HAVE_SDL2 auto sdl_devices = sdl->GetInputDevices(); @@ -96,7 +96,7 @@ struct InputSubsystem::Impl { if (!params.Has("class") || params.Get("class", "") == "any") { return {}; } - if (params.Get("class", "") == "key") { + if (params.Get("class", "") == "keyboard") { // TODO consider returning the SDL key codes for the default keybindings return {}; } @@ -116,7 +116,7 @@ struct InputSubsystem::Impl { if (!params.Has("class") || params.Get("class", "") == "any") { return {}; } - if (params.Get("class", "") == "key") { + if (params.Get("class", "") == "keyboard") { // TODO consider returning the SDL key codes for the default keybindings return {}; } diff --git a/src/yuzu/configuration/configure_input.cpp b/src/yuzu/configuration/configure_input.cpp index 2725fcb2b..f2932aa0b 100644 --- a/src/yuzu/configuration/configure_input.cpp +++ b/src/yuzu/configuration/configure_input.cpp @@ -242,6 +242,6 @@ void ConfigureInput::UpdateDockedState(bool is_handheld) { void ConfigureInput::UpdateAllInputDevices() { for (const auto& player : player_controllers) { - player->UpdateInputDevices(); + player->UpdateInputDeviceCombobox(); } } diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp index f58ca29d7..0de0c6999 100644 --- a/src/yuzu/configuration/configure_input_player.cpp +++ b/src/yuzu/configuration/configure_input_player.cpp @@ -477,11 +477,12 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i UpdateMotionButtons(); }); - connect(ui->comboDevices, qOverload(&QComboBox::currentIndexChanged), this, + connect(ui->comboDevices, qOverload(&QComboBox::activated), this, &ConfigureInputPlayer::UpdateMappingWithDefaults); + ui->comboDevices->setCurrentIndex(-1); + ui->buttonRefreshDevices->setIcon(QIcon::fromTheme(QStringLiteral("view-refresh"))); - UpdateInputDevices(); connect(ui->buttonRefreshDevices, &QPushButton::clicked, [this] { emit RefreshInputDevices(); }); @@ -492,14 +493,14 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i Common::ParamPackage params; if (input_subsystem->GetGCButtons()->IsPolling()) { params = input_subsystem->GetGCButtons()->GetNextInput(); - if (params.Has("engine")) { + if (params.Has("engine") && IsInputAcceptable(params)) { SetPollingResult(params, false); return; } } if (input_subsystem->GetGCAnalogs()->IsPolling()) { params = input_subsystem->GetGCAnalogs()->GetNextInput(); - if (params.Has("engine")) { + if (params.Has("engine") && IsInputAcceptable(params)) { SetPollingResult(params, false); return; } @@ -513,7 +514,7 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i } for (auto& poller : device_pollers) { params = poller->GetNextInput(); - if (params.Has("engine")) { + if (params.Has("engine") && IsInputAcceptable(params)) { SetPollingResult(params, false); return; } @@ -572,6 +573,14 @@ void ConfigureInputPlayer::ApplyConfiguration() { UpdateController(Settings::ControllerType::Handheld, HANDHELD_INDEX, handheld.connected); } +void ConfigureInputPlayer::showEvent(QShowEvent* event) { + if (bottom_row == nullptr) { + return; + } + QWidget::showEvent(event); + ui->main->addWidget(bottom_row); +} + void ConfigureInputPlayer::changeEvent(QEvent* event) { if (event->type() == QEvent::LanguageChange) { RetranslateUI(); @@ -604,6 +613,7 @@ void ConfigureInputPlayer::LoadConfiguration() { } UpdateUI(); + UpdateInputDeviceCombobox(); if (debug) { return; @@ -615,11 +625,56 @@ void ConfigureInputPlayer::LoadConfiguration() { (player_index == 0 && Settings::values.players[HANDHELD_INDEX].connected)); } -void ConfigureInputPlayer::UpdateInputDevices() { - input_devices = input_subsystem->GetInputDevices(); - ui->comboDevices->clear(); - for (auto device : input_devices) { - ui->comboDevices->addItem(QString::fromStdString(device.Get("display", "Unknown")), {}); +void ConfigureInputPlayer::ConnectPlayer(bool connected) { + ui->groupConnectedController->setChecked(connected); +} + +void ConfigureInputPlayer::UpdateInputDeviceCombobox() { + // Skip input device persistence if "Input Devices" is set to "Any". + if (ui->comboDevices->currentIndex() == 0) { + UpdateInputDevices(); + return; + } + + // Find the first button that isn't empty. + const auto button_param = + std::find_if(buttons_param.begin(), buttons_param.end(), + [](const Common::ParamPackage param) { return param.Has("engine"); }); + const bool buttons_empty = button_param == buttons_param.end(); + + const auto current_engine = button_param->Get("engine", ""); + const auto current_guid = button_param->Get("guid", ""); + const auto current_port = button_param->Get("port", ""); + + UpdateInputDevices(); + + if (buttons_empty) { + return; + } + + const bool all_one_device = + std::all_of(buttons_param.begin(), buttons_param.end(), + [current_engine, current_guid, current_port](const Common::ParamPackage param) { + return !param.Has("engine") || (param.Get("engine", "") == current_engine && + param.Get("guid", "") == current_guid && + param.Get("port", "") == current_port); + }); + + if (all_one_device) { + const auto devices_it = std::find_if( + input_devices.begin(), input_devices.end(), + [current_engine, current_guid, current_port](const Common::ParamPackage param) { + return param.Get("class", "") == current_engine && + param.Get("guid", "") == current_guid && + param.Get("port", "") == current_port; + }); + const int device_index = + devices_it != input_devices.end() + ? static_cast(std::distance(input_devices.begin(), devices_it)) + : 0; + ui->comboDevices->setCurrentIndex(device_index); + } else { + ui->comboDevices->setCurrentIndex(0); } } @@ -648,7 +703,7 @@ void ConfigureInputPlayer::RestoreDefaults() { } UpdateUI(); - UpdateInputDevices(); + UpdateInputDeviceCombobox(); ui->comboControllerType->setCurrentIndex(0); } @@ -752,117 +807,12 @@ void ConfigureInputPlayer::UpdateUI() { } } -void ConfigureInputPlayer::UpdateMappingWithDefaults() { - if (ui->comboDevices->currentIndex() < 2) { - return; +void ConfigureInputPlayer::UpdateInputDevices() { + input_devices = input_subsystem->GetInputDevices(); + ui->comboDevices->clear(); + for (auto device : input_devices) { + ui->comboDevices->addItem(QString::fromStdString(device.Get("display", "Unknown")), {}); } - const auto& device = input_devices[ui->comboDevices->currentIndex()]; - auto button_mapping = input_subsystem->GetButtonMappingForDevice(device); - auto analog_mapping = input_subsystem->GetAnalogMappingForDevice(device); - for (std::size_t i = 0; i < buttons_param.size(); ++i) { - buttons_param[i] = button_mapping[static_cast(i)]; - } - for (std::size_t i = 0; i < analogs_param.size(); ++i) { - analogs_param[i] = analog_mapping[static_cast(i)]; - } - - UpdateUI(); -} - -void ConfigureInputPlayer::HandleClick( - QPushButton* button, std::function new_input_setter, - InputCommon::Polling::DeviceType type) { - if (button == ui->buttonMotionLeft || button == ui->buttonMotionRight) { - button->setText(tr("Shake!")); - } else { - button->setText(tr("[waiting]")); - } - button->setFocus(); - - // The first two input devices are always Any and Keyboard/Mouse. If the user filtered to a - // controller, then they don't want keyboard/mouse input - want_keyboard_mouse = ui->comboDevices->currentIndex() < 2; - - input_setter = new_input_setter; - - device_pollers = input_subsystem->GetPollers(type); - - for (auto& poller : device_pollers) { - poller->Start(); - } - - QWidget::grabMouse(); - QWidget::grabKeyboard(); - - if (type == InputCommon::Polling::DeviceType::Button) { - input_subsystem->GetGCButtons()->BeginConfiguration(); - } else { - input_subsystem->GetGCAnalogs()->BeginConfiguration(); - } - - if (type == InputCommon::Polling::DeviceType::Motion) { - input_subsystem->GetUDPMotions()->BeginConfiguration(); - } - - timeout_timer->start(2500); // Cancel after 2.5 seconds - poll_timer->start(50); // Check for new inputs every 50ms -} - -void ConfigureInputPlayer::SetPollingResult(const Common::ParamPackage& params, bool abort) { - timeout_timer->stop(); - poll_timer->stop(); - for (auto& poller : device_pollers) { - poller->Stop(); - } - - QWidget::releaseMouse(); - QWidget::releaseKeyboard(); - - input_subsystem->GetGCButtons()->EndConfiguration(); - input_subsystem->GetGCAnalogs()->EndConfiguration(); - - input_subsystem->GetUDPMotions()->EndConfiguration(); - - if (!abort) { - (*input_setter)(params); - } - - UpdateUI(); - input_setter = std::nullopt; -} - -void ConfigureInputPlayer::mousePressEvent(QMouseEvent* event) { - if (!input_setter || !event) { - return; - } - - if (want_keyboard_mouse) { - SetPollingResult(Common::ParamPackage{InputCommon::GenerateKeyboardParam(event->button())}, - false); - } else { - // We don't want any mouse buttons, so don't stop polling - return; - } - - SetPollingResult({}, true); -} - -void ConfigureInputPlayer::keyPressEvent(QKeyEvent* event) { - if (!input_setter || !event) { - return; - } - - if (event->key() != Qt::Key_Escape) { - if (want_keyboard_mouse) { - SetPollingResult(Common::ParamPackage{InputCommon::GenerateKeyboardParam(event->key())}, - false); - } else { - // Escape key wasn't pressed and we don't want any keyboard keys, so don't stop polling - return; - } - } - - SetPollingResult({}, true); } void ConfigureInputPlayer::UpdateControllerIcon() { @@ -986,14 +936,128 @@ void ConfigureInputPlayer::UpdateMotionButtons() { } } -void ConfigureInputPlayer::showEvent(QShowEvent* event) { - if (bottom_row == nullptr) { +void ConfigureInputPlayer::UpdateMappingWithDefaults() { + if (ui->comboDevices->currentIndex() < 2) { return; } - QWidget::showEvent(event); - ui->main->addWidget(bottom_row); + const auto& device = input_devices[ui->comboDevices->currentIndex()]; + auto button_mapping = input_subsystem->GetButtonMappingForDevice(device); + auto analog_mapping = input_subsystem->GetAnalogMappingForDevice(device); + for (std::size_t i = 0; i < buttons_param.size(); ++i) { + buttons_param[i] = button_mapping[static_cast(i)]; + } + for (std::size_t i = 0; i < analogs_param.size(); ++i) { + analogs_param[i] = analog_mapping[static_cast(i)]; + } + + UpdateUI(); } -void ConfigureInputPlayer::ConnectPlayer(bool connected) { - ui->groupConnectedController->setChecked(connected); +void ConfigureInputPlayer::HandleClick( + QPushButton* button, std::function new_input_setter, + InputCommon::Polling::DeviceType type) { + if (button == ui->buttonMotionLeft || button == ui->buttonMotionRight) { + button->setText(tr("Shake!")); + } else { + button->setText(tr("[waiting]")); + } + button->setFocus(); + + // The first two input devices are always Any and Keyboard/Mouse. If the user filtered to a + // controller, then they don't want keyboard/mouse input + want_keyboard_mouse = ui->comboDevices->currentIndex() < 2; + + input_setter = new_input_setter; + + device_pollers = input_subsystem->GetPollers(type); + + for (auto& poller : device_pollers) { + poller->Start(); + } + + QWidget::grabMouse(); + QWidget::grabKeyboard(); + + if (type == InputCommon::Polling::DeviceType::Button) { + input_subsystem->GetGCButtons()->BeginConfiguration(); + } else { + input_subsystem->GetGCAnalogs()->BeginConfiguration(); + } + + if (type == InputCommon::Polling::DeviceType::Motion) { + input_subsystem->GetUDPMotions()->BeginConfiguration(); + } + + timeout_timer->start(2500); // Cancel after 2.5 seconds + poll_timer->start(50); // Check for new inputs every 50ms +} + +void ConfigureInputPlayer::SetPollingResult(const Common::ParamPackage& params, bool abort) { + timeout_timer->stop(); + poll_timer->stop(); + for (auto& poller : device_pollers) { + poller->Stop(); + } + + QWidget::releaseMouse(); + QWidget::releaseKeyboard(); + + input_subsystem->GetGCButtons()->EndConfiguration(); + input_subsystem->GetGCAnalogs()->EndConfiguration(); + + input_subsystem->GetUDPMotions()->EndConfiguration(); + + if (!abort) { + (*input_setter)(params); + } + + UpdateUI(); + UpdateInputDeviceCombobox(); + + input_setter = std::nullopt; +} + +bool ConfigureInputPlayer::IsInputAcceptable(const Common::ParamPackage& params) const { + if (ui->comboDevices->currentIndex() == 0) { + return true; + } + + const auto current_input_device = input_devices[ui->comboDevices->currentIndex()]; + return params.Get("engine", "") == current_input_device.Get("class", "") && + params.Get("guid", "") == current_input_device.Get("guid", "") && + params.Get("port", "") == current_input_device.Get("port", ""); +} + +void ConfigureInputPlayer::mousePressEvent(QMouseEvent* event) { + if (!input_setter || !event) { + return; + } + + if (want_keyboard_mouse) { + SetPollingResult(Common::ParamPackage{InputCommon::GenerateKeyboardParam(event->button())}, + false); + } else { + // We don't want any mouse buttons, so don't stop polling + return; + } + + SetPollingResult({}, true); +} + +void ConfigureInputPlayer::keyPressEvent(QKeyEvent* event) { + if (!input_setter || !event) { + return; + } + + if (event->key() != Qt::Key_Escape) { + if (want_keyboard_mouse) { + SetPollingResult(Common::ParamPackage{InputCommon::GenerateKeyboardParam(event->key())}, + false); + } else { + // Escape key wasn't pressed and we don't want any keyboard keys, so don't stop polling + return; + } + } + + SetPollingResult({}, true); } diff --git a/src/yuzu/configuration/configure_input_player.h b/src/yuzu/configuration/configure_input_player.h index c19aefffa..a5414e624 100644 --- a/src/yuzu/configuration/configure_input_player.h +++ b/src/yuzu/configuration/configure_input_player.h @@ -51,8 +51,11 @@ public: /// Save all button configurations to settings file. void ApplyConfiguration(); + /// Set the connection state checkbox (used to sync state). + void ConnectPlayer(bool connected); + /// Update the input devices combobox. - void UpdateInputDevices(); + void UpdateInputDeviceCombobox(); /// Restore all buttons to their default values. void RestoreDefaults(); @@ -60,9 +63,6 @@ public: /// Clear all input configuration. void ClearAll(); - /// Set the connection state checkbox (used to sync state). - void ConnectPlayer(bool connected); - signals: /// Emitted when this controller is connected by the user. void Connected(bool connected); @@ -89,6 +89,9 @@ private: /// Finish polling and configure input using the input_setter. void SetPollingResult(const Common::ParamPackage& params, bool abort); + /// Checks whether a given input can be accepted. + bool IsInputAcceptable(const Common::ParamPackage& params) const; + /// Handle mouse button press events. void mousePressEvent(QMouseEvent* event) override; @@ -98,8 +101,8 @@ private: /// Update UI to reflect current configuration. void UpdateUI(); - /// Update the controller selection combobox - void UpdateControllerCombobox(); + /// Update the available input devices. + void UpdateInputDevices(); /// Update the current controller icon. void UpdateControllerIcon(); @@ -164,7 +167,7 @@ private: bool want_keyboard_mouse = false; /// List of physical devices users can map with. If a SDL backed device is selected, then you - /// can usue this device to get a default mapping. + /// can use this device to get a default mapping. std::vector input_devices; /// Bottom row is where console wide settings are held, and its "owned" by the parent From 57d89e291de0eacfd368784309a0cbf89d38dcc8 Mon Sep 17 00:00:00 2001 From: Morph <39850852+Morph1984@users.noreply.github.com> Date: Wed, 23 Sep 2020 09:52:25 -0400 Subject: [PATCH 03/37] input_profiles: Implement input profiles --- src/yuzu/CMakeLists.txt | 2 + src/yuzu/configuration/config.cpp | 265 +++++++++++------- src/yuzu/configuration/config.h | 20 +- .../configure_debug_controller.cpp | 6 +- .../configure_debug_controller.h | 6 +- src/yuzu/configuration/configure_input.cpp | 30 +- src/yuzu/configuration/configure_input.h | 4 + .../configuration/configure_input_player.cpp | 111 +++++++- .../configuration/configure_input_player.h | 21 +- src/yuzu/configuration/configure_per_game.cpp | 3 +- src/yuzu/configuration/input_profiles.cpp | 131 +++++++++ src/yuzu/configuration/input_profiles.h | 32 +++ src/yuzu/main.cpp | 2 +- 13 files changed, 506 insertions(+), 127 deletions(-) create mode 100644 src/yuzu/configuration/input_profiles.cpp create mode 100644 src/yuzu/configuration/input_profiles.h diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt index 8abb74d56..22fe0a2a6 100644 --- a/src/yuzu/CMakeLists.txt +++ b/src/yuzu/CMakeLists.txt @@ -108,6 +108,8 @@ add_executable(yuzu configuration/configure_web.cpp configuration/configure_web.h configuration/configure_web.ui + configuration/input_profiles.cpp + configuration/input_profiles.h debugger/console.cpp debugger/console.h debugger/profiler.cpp diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index 1ce62e4a6..5c8b02fbe 100644 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp @@ -5,6 +5,7 @@ #include #include #include +#include "common/common_paths.h" #include "common/file_util.h" #include "core/hle/service/acc/profile_manager.h" #include "core/hle/service/hid/controllers/npad.h" @@ -14,14 +15,27 @@ namespace FS = Common::FS; -Config::Config(const std::string& config_file, bool is_global) { - // TODO: Don't hardcode the path; let the frontend decide where to put the config files. - qt_config_loc = FS::GetUserPath(FS::UserPath::ConfigDir) + config_file; - FS::CreateFullPath(qt_config_loc); - qt_config = - std::make_unique(QString::fromStdString(qt_config_loc), QSettings::IniFormat); - global = is_global; - Reload(); +Config::Config(const std::string& config_file, ConfigType config_type) : type(config_type) { + global = config_type == ConfigType::GlobalConfig; + + switch (config_type) { + case ConfigType::GlobalConfig: + case ConfigType::PerGameConfig: + qt_config_loc = fmt::format("{}" DIR_SEP "{}.ini", FS::GetUserPath(FS::UserPath::ConfigDir), + config_file); + FS::CreateFullPath(qt_config_loc); + qt_config = std::make_unique(QString::fromStdString(qt_config_loc), + QSettings::IniFormat); + Reload(); + break; + case ConfigType::InputProfile: + qt_config_loc = fmt::format("{}input" DIR_SEP "{}.ini", + FS::GetUserPath(FS::UserPath::ConfigDir), config_file); + FS::CreateFullPath(qt_config_loc); + qt_config = std::make_unique(QString::fromStdString(qt_config_loc), + QSettings::IniFormat); + break; + } } Config::~Config() { @@ -242,84 +256,103 @@ const std::array Config::default_hotkeys{{ }}; // clang-format on -void Config::ReadPlayerValues() { - for (std::size_t p = 0; p < Settings::values.players.size(); ++p) { - auto& player = Settings::values.players[p]; +void Config::ReadPlayerValue(std::size_t player_index) { + const QString player_prefix = [this, player_index] { + if (type == ConfigType::InputProfile) { + return QString{}; + } else { + return QStringLiteral("player_%1_").arg(player_index); + } + }(); + auto& player = Settings::values.players[player_index]; + + if (player_prefix.isEmpty()) { + const auto controller = static_cast( + qt_config + ->value(QStringLiteral("%1type").arg(player_prefix), + static_cast(Settings::ControllerType::ProController)) + .toUInt()); + + if (controller == Settings::ControllerType::LeftJoycon || + controller == Settings::ControllerType::RightJoycon) { + player.controller_type = controller; + } + } else { player.connected = - ReadSetting(QStringLiteral("player_%1_connected").arg(p), false).toBool(); + ReadSetting(QStringLiteral("%1connected").arg(player_prefix), false).toBool(); player.controller_type = static_cast( qt_config - ->value(QStringLiteral("player_%1_type").arg(p), + ->value(QStringLiteral("%1type").arg(player_prefix), static_cast(Settings::ControllerType::ProController)) .toUInt()); player.body_color_left = qt_config - ->value(QStringLiteral("player_%1_body_color_left").arg(p), + ->value(QStringLiteral("%1body_color_left").arg(player_prefix), Settings::JOYCON_BODY_NEON_BLUE) .toUInt(); - player.body_color_right = qt_config - ->value(QStringLiteral("player_%1_body_color_right").arg(p), - Settings::JOYCON_BODY_NEON_RED) - .toUInt(); - player.button_color_left = qt_config - ->value(QStringLiteral("player_%1_button_color_left").arg(p), - Settings::JOYCON_BUTTONS_NEON_BLUE) - .toUInt(); + player.body_color_right = + qt_config + ->value(QStringLiteral("%1body_color_right").arg(player_prefix), + Settings::JOYCON_BODY_NEON_RED) + .toUInt(); + player.button_color_left = + qt_config + ->value(QStringLiteral("%1button_color_left").arg(player_prefix), + Settings::JOYCON_BUTTONS_NEON_BLUE) + .toUInt(); player.button_color_right = qt_config - ->value(QStringLiteral("player_%1_button_color_right").arg(p), + ->value(QStringLiteral("%1button_color_right").arg(player_prefix), Settings::JOYCON_BUTTONS_NEON_RED) .toUInt(); + } - for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { - const std::string default_param = - InputCommon::GenerateKeyboardParam(default_buttons[i]); - auto& player_buttons = player.buttons[i]; + for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { + const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]); + auto& player_buttons = player.buttons[i]; - player_buttons = qt_config - ->value(QStringLiteral("player_%1_").arg(p) + - QString::fromUtf8(Settings::NativeButton::mapping[i]), - QString::fromStdString(default_param)) - .toString() - .toStdString(); - if (player_buttons.empty()) { - player_buttons = default_param; - } + player_buttons = qt_config + ->value(QStringLiteral("%1").arg(player_prefix) + + QString::fromUtf8(Settings::NativeButton::mapping[i]), + QString::fromStdString(default_param)) + .toString() + .toStdString(); + if (player_buttons.empty()) { + player_buttons = default_param; } + } - for (int i = 0; i < Settings::NativeMotion::NumMotions; ++i) { - const std::string default_param = - InputCommon::GenerateKeyboardParam(default_motions[i]); - auto& player_motions = player.motions[i]; + for (int i = 0; i < Settings::NativeMotion::NumMotions; ++i) { + const std::string default_param = InputCommon::GenerateKeyboardParam(default_motions[i]); + auto& player_motions = player.motions[i]; - player_motions = qt_config - ->value(QStringLiteral("player_%1_").arg(p) + - QString::fromUtf8(Settings::NativeMotion::mapping[i]), - QString::fromStdString(default_param)) - .toString() - .toStdString(); - if (player_motions.empty()) { - player_motions = default_param; - } + player_motions = qt_config + ->value(QStringLiteral("%1").arg(player_prefix) + + QString::fromUtf8(Settings::NativeMotion::mapping[i]), + QString::fromStdString(default_param)) + .toString() + .toStdString(); + if (player_motions.empty()) { + player_motions = default_param; } + } - for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { - const std::string default_param = InputCommon::GenerateAnalogParamFromKeys( - default_analogs[i][0], default_analogs[i][1], default_analogs[i][2], - default_analogs[i][3], default_stick_mod[i], 0.5f); - auto& player_analogs = player.analogs[i]; + for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { + const std::string default_param = InputCommon::GenerateAnalogParamFromKeys( + default_analogs[i][0], default_analogs[i][1], default_analogs[i][2], + default_analogs[i][3], default_stick_mod[i], 0.5f); + auto& player_analogs = player.analogs[i]; - player_analogs = qt_config - ->value(QStringLiteral("player_%1_").arg(p) + - QString::fromUtf8(Settings::NativeAnalog::mapping[i]), - QString::fromStdString(default_param)) - .toString() - .toStdString(); - if (player_analogs.empty()) { - player_analogs = default_param; - } + player_analogs = qt_config + ->value(QStringLiteral("%1").arg(player_prefix) + + QString::fromUtf8(Settings::NativeAnalog::mapping[i]), + QString::fromStdString(default_param)) + .toString() + .toStdString(); + if (player_analogs.empty()) { + player_analogs = default_param; } } } @@ -436,7 +469,9 @@ void Config::ReadAudioValues() { void Config::ReadControlValues() { qt_config->beginGroup(QStringLiteral("Controls")); - ReadPlayerValues(); + for (std::size_t p = 0; p < Settings::values.players.size(); ++p) { + ReadPlayerValue(p); + } ReadDebugValues(); ReadKeyboardValues(); ReadMouseValues(); @@ -920,49 +955,55 @@ void Config::ReadValues() { ReadSystemValues(); } -void Config::SavePlayerValues() { - for (std::size_t p = 0; p < Settings::values.players.size(); ++p) { - const auto& player = Settings::values.players[p]; +void Config::SavePlayerValue(std::size_t player_index) { + const QString player_prefix = [this, player_index] { + if (type == ConfigType::InputProfile) { + return QString{}; + } else { + return QStringLiteral("player_%1_").arg(player_index); + } + }(); - WriteSetting(QStringLiteral("player_%1_connected").arg(p), player.connected, false); - WriteSetting(QStringLiteral("player_%1_type").arg(p), - static_cast(player.controller_type), - static_cast(Settings::ControllerType::ProController)); + const auto& player = Settings::values.players[player_index]; - WriteSetting(QStringLiteral("player_%1_body_color_left").arg(p), player.body_color_left, + WriteSetting(QStringLiteral("%1type").arg(player_prefix), + static_cast(player.controller_type), + static_cast(Settings::ControllerType::ProController)); + + if (!player_prefix.isEmpty()) { + WriteSetting(QStringLiteral("%1connected").arg(player_prefix), player.connected, false); + WriteSetting(QStringLiteral("%1body_color_left").arg(player_prefix), player.body_color_left, Settings::JOYCON_BODY_NEON_BLUE); - WriteSetting(QStringLiteral("player_%1_body_color_right").arg(p), player.body_color_right, - Settings::JOYCON_BODY_NEON_RED); - WriteSetting(QStringLiteral("player_%1_button_color_left").arg(p), player.button_color_left, - Settings::JOYCON_BUTTONS_NEON_BLUE); - WriteSetting(QStringLiteral("player_%1_button_color_right").arg(p), + WriteSetting(QStringLiteral("%1body_color_right").arg(player_prefix), + player.body_color_right, Settings::JOYCON_BODY_NEON_RED); + WriteSetting(QStringLiteral("%1button_color_left").arg(player_prefix), + player.button_color_left, Settings::JOYCON_BUTTONS_NEON_BLUE); + WriteSetting(QStringLiteral("%1button_color_right").arg(player_prefix), player.button_color_right, Settings::JOYCON_BUTTONS_NEON_RED); + } - for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { - const std::string default_param = - InputCommon::GenerateKeyboardParam(default_buttons[i]); - WriteSetting(QStringLiteral("player_%1_").arg(p) + - QString::fromStdString(Settings::NativeButton::mapping[i]), - QString::fromStdString(player.buttons[i]), - QString::fromStdString(default_param)); - } - for (int i = 0; i < Settings::NativeMotion::NumMotions; ++i) { - const std::string default_param = - InputCommon::GenerateKeyboardParam(default_motions[i]); - WriteSetting(QStringLiteral("player_%1_").arg(p) + - QString::fromStdString(Settings::NativeMotion::mapping[i]), - QString::fromStdString(player.motions[i]), - QString::fromStdString(default_param)); - } - for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { - const std::string default_param = InputCommon::GenerateAnalogParamFromKeys( - default_analogs[i][0], default_analogs[i][1], default_analogs[i][2], - default_analogs[i][3], default_stick_mod[i], 0.5f); - WriteSetting(QStringLiteral("player_%1_").arg(p) + - QString::fromStdString(Settings::NativeAnalog::mapping[i]), - QString::fromStdString(player.analogs[i]), - QString::fromStdString(default_param)); - } + for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { + const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]); + WriteSetting(QStringLiteral("%1").arg(player_prefix) + + QString::fromStdString(Settings::NativeButton::mapping[i]), + QString::fromStdString(player.buttons[i]), + QString::fromStdString(default_param)); + } + for (int i = 0; i < Settings::NativeMotion::NumMotions; ++i) { + const std::string default_param = InputCommon::GenerateKeyboardParam(default_motions[i]); + WriteSetting(QStringLiteral("%1").arg(player_prefix) + + QString::fromStdString(Settings::NativeMotion::mapping[i]), + QString::fromStdString(player.motions[i]), + QString::fromStdString(default_param)); + } + for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { + const std::string default_param = InputCommon::GenerateAnalogParamFromKeys( + default_analogs[i][0], default_analogs[i][1], default_analogs[i][2], + default_analogs[i][3], default_stick_mod[i], 0.5f); + WriteSetting(QStringLiteral("%1").arg(player_prefix) + + QString::fromStdString(Settings::NativeAnalog::mapping[i]), + QString::fromStdString(player.analogs[i]), + QString::fromStdString(default_param)); } } @@ -1087,7 +1128,9 @@ void Config::SaveAudioValues() { void Config::SaveControlValues() { qt_config->beginGroup(QStringLiteral("Controls")); - SavePlayerValues(); + for (std::size_t p = 0; p < Settings::values.players.size(); ++p) { + SavePlayerValue(p); + } SaveDebugValues(); SaveMouseValues(); SaveTouchscreenValues(); @@ -1515,3 +1558,19 @@ void Config::Save() { Settings::Sanitize(); SaveValues(); } + +void Config::ReadControlPlayerValue(std::size_t player_index) { + qt_config->beginGroup(QStringLiteral("Controls")); + ReadPlayerValue(player_index); + qt_config->endGroup(); +} + +void Config::SaveControlPlayerValue(std::size_t player_index) { + qt_config->beginGroup(QStringLiteral("Controls")); + SavePlayerValue(player_index); + qt_config->endGroup(); +} + +const std::string& Config::GetConfigFilePath() const { + return qt_config_loc; +} diff --git a/src/yuzu/configuration/config.h b/src/yuzu/configuration/config.h index 5d8e45d78..a1ffca48f 100644 --- a/src/yuzu/configuration/config.h +++ b/src/yuzu/configuration/config.h @@ -16,12 +16,24 @@ class QSettings; class Config { public: - explicit Config(const std::string& config_loc = "qt-config.ini", bool is_global = true); + enum class ConfigType { + GlobalConfig, + PerGameConfig, + InputProfile, + }; + + explicit Config(const std::string& config_loc = "qt-config", + ConfigType config_type = ConfigType::GlobalConfig); ~Config(); void Reload(); void Save(); + void ReadControlPlayerValue(std::size_t player_index); + void SaveControlPlayerValue(std::size_t player_index); + + const std::string& GetConfigFilePath() const; + static const std::array default_buttons; static const std::array default_motions; static const std::array, Settings::NativeAnalog::NumAnalogs> default_analogs; @@ -34,7 +46,7 @@ public: private: void ReadValues(); - void ReadPlayerValues(); + void ReadPlayerValue(std::size_t player_index); void ReadDebugValues(); void ReadKeyboardValues(); void ReadMouseValues(); @@ -62,7 +74,7 @@ private: void ReadWebServiceValues(); void SaveValues(); - void SavePlayerValues(); + void SavePlayerValue(std::size_t player_index); void SaveDebugValues(); void SaveMouseValues(); void SaveTouchscreenValues(); @@ -111,9 +123,9 @@ private: void WriteSettingGlobal(const QString& name, const QVariant& value, bool use_global, const QVariant& default_value); + ConfigType type; std::unique_ptr qt_config; std::string qt_config_loc; - bool global; }; diff --git a/src/yuzu/configuration/configure_debug_controller.cpp b/src/yuzu/configuration/configure_debug_controller.cpp index 0097c9a29..6dc9c5e57 100644 --- a/src/yuzu/configuration/configure_debug_controller.cpp +++ b/src/yuzu/configuration/configure_debug_controller.cpp @@ -6,9 +6,11 @@ #include "yuzu/configuration/configure_debug_controller.h" ConfigureDebugController::ConfigureDebugController(QWidget* parent, - InputCommon::InputSubsystem* input_subsystem) + InputCommon::InputSubsystem* input_subsystem, + InputProfiles* profiles) : QDialog(parent), ui(std::make_unique()), - debug_controller(new ConfigureInputPlayer(this, 9, nullptr, input_subsystem, true)) { + debug_controller( + new ConfigureInputPlayer(this, 9, nullptr, input_subsystem, profiles, true)) { ui->setupUi(this); ui->controllerLayout->addWidget(debug_controller); diff --git a/src/yuzu/configuration/configure_debug_controller.h b/src/yuzu/configuration/configure_debug_controller.h index 34dcf705f..2694b3419 100644 --- a/src/yuzu/configuration/configure_debug_controller.h +++ b/src/yuzu/configuration/configure_debug_controller.h @@ -10,6 +10,8 @@ class QPushButton; +class InputProfiles; + namespace InputCommon { class InputSubsystem; } @@ -22,8 +24,8 @@ class ConfigureDebugController : public QDialog { Q_OBJECT public: - explicit ConfigureDebugController(QWidget* parent, - InputCommon::InputSubsystem* input_subsystem); + explicit ConfigureDebugController(QWidget* parent, InputCommon::InputSubsystem* input_subsystem, + InputProfiles* profiles); ~ConfigureDebugController() override; void ApplyConfiguration(); diff --git a/src/yuzu/configuration/configure_input.cpp b/src/yuzu/configuration/configure_input.cpp index f2932aa0b..523ece426 100644 --- a/src/yuzu/configuration/configure_input.cpp +++ b/src/yuzu/configuration/configure_input.cpp @@ -23,6 +23,7 @@ #include "yuzu/configuration/configure_motion_touch.h" #include "yuzu/configuration/configure_mouse_advanced.h" #include "yuzu/configuration/configure_touchscreen_advanced.h" +#include "yuzu/configuration/input_profiles.h" namespace { template @@ -64,7 +65,8 @@ void OnDockedModeChanged(bool last_state, bool new_state) { } ConfigureInput::ConfigureInput(QWidget* parent) - : QWidget(parent), ui(std::make_unique()) { + : QWidget(parent), ui(std::make_unique()), + profiles(std::make_unique()) { ui->setupUi(this); } @@ -73,14 +75,22 @@ ConfigureInput::~ConfigureInput() = default; void ConfigureInput::Initialize(InputCommon::InputSubsystem* input_subsystem, std::size_t max_players) { player_controllers = { - new ConfigureInputPlayer(this, 0, ui->consoleInputSettings, input_subsystem), - new ConfigureInputPlayer(this, 1, ui->consoleInputSettings, input_subsystem), - new ConfigureInputPlayer(this, 2, ui->consoleInputSettings, input_subsystem), - new ConfigureInputPlayer(this, 3, ui->consoleInputSettings, input_subsystem), - new ConfigureInputPlayer(this, 4, ui->consoleInputSettings, input_subsystem), - new ConfigureInputPlayer(this, 5, ui->consoleInputSettings, input_subsystem), - new ConfigureInputPlayer(this, 6, ui->consoleInputSettings, input_subsystem), - new ConfigureInputPlayer(this, 7, ui->consoleInputSettings, input_subsystem), + new ConfigureInputPlayer(this, 0, ui->consoleInputSettings, input_subsystem, + profiles.get()), + new ConfigureInputPlayer(this, 1, ui->consoleInputSettings, input_subsystem, + profiles.get()), + new ConfigureInputPlayer(this, 2, ui->consoleInputSettings, input_subsystem, + profiles.get()), + new ConfigureInputPlayer(this, 3, ui->consoleInputSettings, input_subsystem, + profiles.get()), + new ConfigureInputPlayer(this, 4, ui->consoleInputSettings, input_subsystem, + profiles.get()), + new ConfigureInputPlayer(this, 5, ui->consoleInputSettings, input_subsystem, + profiles.get()), + new ConfigureInputPlayer(this, 6, ui->consoleInputSettings, input_subsystem, + profiles.get()), + new ConfigureInputPlayer(this, 7, ui->consoleInputSettings, input_subsystem, + profiles.get()), }; player_tabs = { @@ -134,7 +144,7 @@ void ConfigureInput::Initialize(InputCommon::InputSubsystem* input_subsystem, ui->tabAdvanced->setLayout(new QHBoxLayout(ui->tabAdvanced)); ui->tabAdvanced->layout()->addWidget(advanced); connect(advanced, &ConfigureInputAdvanced::CallDebugControllerDialog, [this, input_subsystem] { - CallConfigureDialog(*this, input_subsystem); + CallConfigureDialog(*this, input_subsystem, profiles.get()); }); connect(advanced, &ConfigureInputAdvanced::CallMouseConfigDialog, [this, input_subsystem] { CallConfigureDialog(*this, input_subsystem); diff --git a/src/yuzu/configuration/configure_input.h b/src/yuzu/configuration/configure_input.h index 0e8b2fd4e..f135a4299 100644 --- a/src/yuzu/configuration/configure_input.h +++ b/src/yuzu/configuration/configure_input.h @@ -19,6 +19,8 @@ class QCheckBox; class QString; class QTimer; +class InputProfiles; + namespace InputCommon { class InputSubsystem; } @@ -61,6 +63,8 @@ private: std::unique_ptr ui; + std::unique_ptr profiles; + std::array player_controllers; std::array player_tabs; std::array player_connected; diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp index 0de0c6999..b4de2f6af 100644 --- a/src/yuzu/configuration/configure_input_player.cpp +++ b/src/yuzu/configuration/configure_input_player.cpp @@ -22,6 +22,8 @@ #include "ui_configure_input_player.h" #include "yuzu/configuration/config.h" #include "yuzu/configuration/configure_input_player.h" +#include "yuzu/configuration/input_profiles.h" +#include "yuzu/util/limitable_input_dialog.h" constexpr std::size_t HANDHELD_INDEX = 8; @@ -240,10 +242,11 @@ QString AnalogToText(const Common::ParamPackage& param, const std::string& dir) ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_index, QWidget* bottom_row, InputCommon::InputSubsystem* input_subsystem_, - bool debug) + InputProfiles* profiles_, bool debug) : QWidget(parent), ui(std::make_unique()), player_index(player_index), - debug(debug), input_subsystem{input_subsystem_}, timeout_timer(std::make_unique()), - poll_timer(std::make_unique()), bottom_row(bottom_row) { + debug(debug), input_subsystem{input_subsystem_}, profiles(profiles_), + timeout_timer(std::make_unique()), poll_timer(std::make_unique()), + bottom_row(bottom_row) { ui->setupUi(this); setFocusPolicy(Qt::ClickFocus); @@ -521,6 +524,17 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i } }); + RefreshInputProfiles(); + + connect(ui->buttonProfilesNew, &QPushButton::clicked, this, + &ConfigureInputPlayer::CreateProfile); + connect(ui->buttonProfilesDelete, &QPushButton::clicked, this, + &ConfigureInputPlayer::DeleteProfile); + connect(ui->comboProfiles, qOverload(&QComboBox::activated), this, + &ConfigureInputPlayer::LoadProfile); + connect(ui->buttonProfilesSave, &QPushButton::clicked, this, + &ConfigureInputPlayer::SaveProfile); + LoadConfiguration(); // TODO(wwylele): enable this when we actually emulate it @@ -1061,3 +1075,94 @@ void ConfigureInputPlayer::keyPressEvent(QKeyEvent* event) { SetPollingResult({}, true); } + +void ConfigureInputPlayer::CreateProfile() { + const auto profile_name = + LimitableInputDialog::GetText(this, tr("New Profile"), tr("Enter a profile name:"), 1, 20); + + if (profile_name.isEmpty()) { + return; + } + + if (!profiles->IsProfileNameValid(profile_name.toStdString())) { + QMessageBox::critical(this, tr("Create Input Profile"), + tr("The given profile name is not valid!")); + return; + } + + ApplyConfiguration(); + + if (!profiles->CreateProfile(profile_name.toStdString(), player_index)) { + QMessageBox::critical(this, tr("Create Input Profile"), + tr("Failed to create the input profile \"%1\"").arg(profile_name)); + RefreshInputProfiles(); + return; + } + + ui->comboProfiles->addItem(profile_name); + ui->comboProfiles->setCurrentIndex(ui->comboProfiles->count() - 1); +} + +void ConfigureInputPlayer::DeleteProfile() { + const QString profile_name = ui->comboProfiles->itemText(ui->comboProfiles->currentIndex()); + + if (profile_name.isEmpty()) { + return; + } + + if (!profiles->DeleteProfile(profile_name.toStdString())) { + QMessageBox::critical(this, tr("Delete Input Profile"), + tr("Failed to delete the input profile \"%1\"").arg(profile_name)); + RefreshInputProfiles(); + return; + } + + ui->comboProfiles->removeItem(ui->comboProfiles->currentIndex()); + ui->comboProfiles->setCurrentIndex(-1); +} + +void ConfigureInputPlayer::LoadProfile() { + const QString profile_name = ui->comboProfiles->itemText(ui->comboProfiles->currentIndex()); + + if (profile_name.isEmpty()) { + return; + } + + ApplyConfiguration(); + + if (!profiles->LoadProfile(profile_name.toStdString(), player_index)) { + QMessageBox::critical(this, tr("Load Input Profile"), + tr("Failed to load the input profile \"%1\"").arg(profile_name)); + RefreshInputProfiles(); + return; + } + + LoadConfiguration(); +} + +void ConfigureInputPlayer::SaveProfile() { + const QString profile_name = ui->comboProfiles->itemText(ui->comboProfiles->currentIndex()); + + if (profile_name.isEmpty()) { + return; + } + + ApplyConfiguration(); + + if (!profiles->SaveProfile(profile_name.toStdString(), player_index)) { + QMessageBox::critical(this, tr("Save Input Profile"), + tr("Failed to save the input profile \"%1\"").arg(profile_name)); + RefreshInputProfiles(); + return; + } +} + +void ConfigureInputPlayer::RefreshInputProfiles() { + ui->comboProfiles->clear(); + + for (const auto& profile_name : profiles->GetInputProfileNames()) { + ui->comboProfiles->addItem(QString::fromStdString(profile_name)); + } + + ui->comboProfiles->setCurrentIndex(-1); +} diff --git a/src/yuzu/configuration/configure_input_player.h b/src/yuzu/configuration/configure_input_player.h index a5414e624..05dee5af5 100644 --- a/src/yuzu/configuration/configure_input_player.h +++ b/src/yuzu/configuration/configure_input_player.h @@ -26,6 +26,8 @@ class QString; class QTimer; class QWidget; +class InputProfiles; + namespace InputCommon { class InputSubsystem; } @@ -45,7 +47,7 @@ class ConfigureInputPlayer : public QWidget { public: explicit ConfigureInputPlayer(QWidget* parent, std::size_t player_index, QWidget* bottom_row, InputCommon::InputSubsystem* input_subsystem_, - bool debug = false); + InputProfiles* profiles_, bool debug = false); ~ConfigureInputPlayer() override; /// Save all button configurations to settings file. @@ -116,6 +118,21 @@ private: /// Gets the default controller mapping for this device and auto configures the input to match. void UpdateMappingWithDefaults(); + /// Creates a controller profile. + void CreateProfile(); + + /// Deletes the selected controller profile. + void DeleteProfile(); + + /// Loads the selected controller profile. + void LoadProfile(); + + /// Saves the current controller configuration into a selected controller profile. + void SaveProfile(); + + /// Refreshes the list of controller profiles. + void RefreshInputProfiles(); + std::unique_ptr ui; std::size_t player_index; @@ -123,6 +140,8 @@ private: InputCommon::InputSubsystem* input_subsystem; + InputProfiles* profiles; + std::unique_ptr timeout_timer; std::unique_ptr poll_timer; diff --git a/src/yuzu/configuration/configure_per_game.cpp b/src/yuzu/configuration/configure_per_game.cpp index 002db3f93..81464dd37 100644 --- a/src/yuzu/configuration/configure_per_game.cpp +++ b/src/yuzu/configuration/configure_per_game.cpp @@ -29,7 +29,8 @@ ConfigurePerGame::ConfigurePerGame(QWidget* parent, u64 title_id) : QDialog(parent), ui(std::make_unique()), title_id(title_id) { - game_config = std::make_unique(fmt::format("{:016X}.ini", title_id), false); + game_config = std::make_unique(fmt::format("{:016X}", title_id), + Config::ConfigType::PerGameConfig); Settings::SetConfiguringGlobal(false); diff --git a/src/yuzu/configuration/input_profiles.cpp b/src/yuzu/configuration/input_profiles.cpp new file mode 100644 index 000000000..e87aededb --- /dev/null +++ b/src/yuzu/configuration/input_profiles.cpp @@ -0,0 +1,131 @@ +// Copyright 2020 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include + +#include "common/common_paths.h" +#include "common/file_util.h" +#include "yuzu/configuration/config.h" +#include "yuzu/configuration/input_profiles.h" + +namespace FS = Common::FS; + +namespace { + +bool ProfileExistsInFilesystem(std::string_view profile_name) { + return FS::Exists(fmt::format("{}input" DIR_SEP "{}.ini", + FS::GetUserPath(FS::UserPath::ConfigDir), profile_name)); +} + +bool IsINI(std::string_view filename) { + const std::size_t index = filename.rfind('.'); + + if (index == std::string::npos) { + return false; + } + + return filename.substr(index) == ".ini"; +} + +std::string GetNameWithoutExtension(const std::string& filename) { + const std::size_t index = filename.rfind('.'); + + if (index == std::string::npos) { + return filename; + } + + return filename.substr(0, index); +} + +} // namespace + +InputProfiles::InputProfiles() { + const std::string input_profile_loc = + fmt::format("{}input", FS::GetUserPath(FS::UserPath::ConfigDir)); + + FS::ForeachDirectoryEntry( + nullptr, input_profile_loc, + [this](u64* entries_out, const std::string& directory, const std::string& filename) { + if (IsINI(filename) && IsProfileNameValid(GetNameWithoutExtension(filename))) { + map_profiles.insert_or_assign( + GetNameWithoutExtension(filename), + std::make_unique(GetNameWithoutExtension(filename), + Config::ConfigType::InputProfile)); + } + return true; + }); +} + +InputProfiles::~InputProfiles() = default; + +std::vector InputProfiles::GetInputProfileNames() { + std::vector profile_names; + profile_names.reserve(map_profiles.size()); + + for (const auto& [profile_name, config] : map_profiles) { + if (!ProfileExistsInFilesystem(profile_name)) { + DeleteProfile(profile_name); + continue; + } + + profile_names.push_back(profile_name); + } + + return profile_names; +} + +bool InputProfiles::IsProfileNameValid(std::string_view profile_name) { + return profile_name.find_first_of("<>:;\"/\\|,.!?*") == std::string::npos; +} + +bool InputProfiles::CreateProfile(const std::string& profile_name, std::size_t player_index) { + if (ProfileExistsInMap(profile_name)) { + return false; + } + + map_profiles.insert_or_assign( + profile_name, std::make_unique(profile_name, Config::ConfigType::InputProfile)); + + return SaveProfile(profile_name, player_index); +} + +bool InputProfiles::DeleteProfile(const std::string& profile_name) { + if (!ProfileExistsInMap(profile_name)) { + return false; + } + + if (!ProfileExistsInFilesystem(profile_name) || + FS::Delete(map_profiles[profile_name]->GetConfigFilePath())) { + map_profiles.erase(profile_name); + } + + return !ProfileExistsInMap(profile_name) && !ProfileExistsInFilesystem(profile_name); +} + +bool InputProfiles::LoadProfile(const std::string& profile_name, std::size_t player_index) { + if (!ProfileExistsInMap(profile_name)) { + return false; + } + + if (!ProfileExistsInFilesystem(profile_name)) { + map_profiles.erase(profile_name); + return false; + } + + map_profiles[profile_name]->ReadControlPlayerValue(player_index); + return true; +} + +bool InputProfiles::SaveProfile(const std::string& profile_name, std::size_t player_index) { + if (!ProfileExistsInMap(profile_name)) { + return false; + } + + map_profiles[profile_name]->SaveControlPlayerValue(player_index); + return true; +} + +bool InputProfiles::ProfileExistsInMap(const std::string& profile_name) const { + return map_profiles.find(profile_name) != map_profiles.end(); +} diff --git a/src/yuzu/configuration/input_profiles.h b/src/yuzu/configuration/input_profiles.h new file mode 100644 index 000000000..cb41fd9be --- /dev/null +++ b/src/yuzu/configuration/input_profiles.h @@ -0,0 +1,32 @@ +// Copyright 2020 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include +#include +#include + +class Config; + +class InputProfiles { + +public: + explicit InputProfiles(); + virtual ~InputProfiles(); + + std::vector GetInputProfileNames(); + + static bool IsProfileNameValid(std::string_view profile_name); + + bool CreateProfile(const std::string& profile_name, std::size_t player_index); + bool DeleteProfile(const std::string& profile_name); + bool LoadProfile(const std::string& profile_name, std::size_t player_index); + bool SaveProfile(const std::string& profile_name, std::size_t player_index); + +private: + bool ProfileExistsInMap(const std::string& profile_name) const; + + std::unordered_map> map_profiles; +}; diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 18e68e590..4ff7fd92f 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -1087,7 +1087,7 @@ void GMainWindow::BootGame(const QString& filename) { const auto loader = Loader::GetLoader(v_file); if (!(loader == nullptr || loader->ReadProgramId(title_id) != Loader::ResultStatus::Success)) { // Load per game settings - Config per_game_config(fmt::format("{:016X}.ini", title_id), false); + Config per_game_config(fmt::format("{:016X}", title_id), Config::ConfigType::PerGameConfig); } Settings::LogSettings(); From 484623cd613b01a029a8a837ed7e2e5657d27202 Mon Sep 17 00:00:00 2001 From: Morph <39850852+Morph1984@users.noreply.github.com> Date: Sun, 27 Sep 2020 09:50:35 -0400 Subject: [PATCH 04/37] bootmanager: Allow mouse clicks only if touch is disabled Previously mouse clicks will not register when touch is disabled. This rectifies that and allows mouse clicks to be mapped to other buttons if the touchscreen is disabled. --- src/yuzu/bootmanager.cpp | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp index e38bc7a9a..d62b0efc2 100644 --- a/src/yuzu/bootmanager.cpp +++ b/src/yuzu/bootmanager.cpp @@ -382,7 +382,12 @@ void GRenderWindow::keyReleaseEvent(QKeyEvent* event) { } void GRenderWindow::mousePressEvent(QMouseEvent* event) { - // touch input is handled in TouchBeginEvent + if (!Settings::values.touchscreen.enabled) { + input_subsystem->GetKeyboard()->PressKey(event->button()); + return; + } + + // Touch input is handled in TouchBeginEvent if (event->source() == Qt::MouseEventSynthesizedBySystem) { return; } @@ -398,7 +403,7 @@ void GRenderWindow::mousePressEvent(QMouseEvent* event) { } void GRenderWindow::mouseMoveEvent(QMouseEvent* event) { - // touch input is handled in TouchUpdateEvent + // Touch input is handled in TouchUpdateEvent if (event->source() == Qt::MouseEventSynthesizedBySystem) { return; } @@ -411,7 +416,12 @@ void GRenderWindow::mouseMoveEvent(QMouseEvent* event) { } void GRenderWindow::mouseReleaseEvent(QMouseEvent* event) { - // touch input is handled in TouchEndEvent + if (!Settings::values.touchscreen.enabled) { + input_subsystem->GetKeyboard()->ReleaseKey(event->button()); + return; + } + + // Touch input is handled in TouchEndEvent if (event->source() == Qt::MouseEventSynthesizedBySystem) { return; } From 5cafa70d3b7f24881b578d2d473dc993fc47364b Mon Sep 17 00:00:00 2001 From: Morph <39850852+Morph1984@users.noreply.github.com> Date: Sun, 27 Sep 2020 11:18:07 -0400 Subject: [PATCH 05/37] applets/controller: Auto accept a valid single player configuration --- src/yuzu/applets/controller.cpp | 29 ++++++++++++++++++----------- src/yuzu/applets/controller.h | 8 +++++--- src/yuzu/main.cpp | 1 + 3 files changed, 24 insertions(+), 14 deletions(-) diff --git a/src/yuzu/applets/controller.cpp b/src/yuzu/applets/controller.cpp index c6fa3e4f6..ee770f315 100644 --- a/src/yuzu/applets/controller.cpp +++ b/src/yuzu/applets/controller.cpp @@ -229,6 +229,13 @@ QtControllerSelectorDialog::QtControllerSelectorDialog( connect(ui->buttonBox, &QDialogButtonBox::accepted, this, &QtControllerSelectorDialog::ApplyConfiguration); + // Enhancement: Check if the parameters have already been met before disconnecting controllers. + // If all the parameters are met AND only allows a single player, + // stop the constructor here as we do not need to continue. + if (CheckIfParametersMet() && parameters.enable_single_mode) { + return; + } + // If keep_controllers_connected is false, forcefully disconnect all controllers if (!parameters.keep_controllers_connected) { for (auto player : player_groupboxes) { @@ -236,13 +243,18 @@ QtControllerSelectorDialog::QtControllerSelectorDialog( } } - CheckIfParametersMet(); - resize(0, 0); } QtControllerSelectorDialog::~QtControllerSelectorDialog() = default; +int QtControllerSelectorDialog::exec() { + if (parameters_met && parameters.enable_single_mode) { + return QDialog::Accepted; + } + return QDialog::exec(); +} + void QtControllerSelectorDialog::ApplyConfiguration() { // Update the controller state once more, just to be sure they are properly applied. for (std::size_t index = 0; index < NUM_PLAYERS; ++index) { @@ -287,7 +299,7 @@ void QtControllerSelectorDialog::CallConfigureInputDialog() { CheckIfParametersMet(); } -void QtControllerSelectorDialog::CheckIfParametersMet() { +bool QtControllerSelectorDialog::CheckIfParametersMet() { // Here, we check and validate the current configuration against all applicable parameters. const auto num_connected_players = static_cast( std::count_if(player_groupboxes.begin(), player_groupboxes.end(), @@ -301,7 +313,7 @@ void QtControllerSelectorDialog::CheckIfParametersMet() { num_connected_players > max_supported_players) { parameters_met = false; ui->buttonBox->setEnabled(parameters_met); - return; + return parameters_met; } // Next, check against all connected controllers. @@ -326,14 +338,9 @@ void QtControllerSelectorDialog::CheckIfParametersMet() { return true; }(); - if (!all_controllers_compatible) { - parameters_met = false; - ui->buttonBox->setEnabled(parameters_met); - return; - } - - parameters_met = true; + parameters_met = all_controllers_compatible; ui->buttonBox->setEnabled(parameters_met); + return parameters_met; } void QtControllerSelectorDialog::SetSupportedControllers() { diff --git a/src/yuzu/applets/controller.h b/src/yuzu/applets/controller.h index 729ecc831..8fefecf05 100644 --- a/src/yuzu/applets/controller.h +++ b/src/yuzu/applets/controller.h @@ -33,6 +33,8 @@ public: InputCommon::InputSubsystem* input_subsystem_); ~QtControllerSelectorDialog() override; + int exec() override; + private: // Applies the current configuration. void ApplyConfiguration(); @@ -43,9 +45,9 @@ private: // Initializes the "Configure Input" Dialog. void CallConfigureInputDialog(); - // Checks the current configuration against the given parameters and - // sets the value of parameters_met. - void CheckIfParametersMet(); + // Checks the current configuration against the given parameters. + // This sets and returns the value of parameters_met. + bool CheckIfParametersMet(); // Sets the controller icons for "Supported Controller Types". void SetSupportedControllers(); diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 4ff7fd92f..5f9f416ea 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -288,6 +288,7 @@ GMainWindow::~GMainWindow() { void GMainWindow::ControllerSelectorReconfigureControllers( const Core::Frontend::ControllerParameters& parameters) { QtControllerSelectorDialog dialog(this, parameters, input_subsystem.get()); + dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowStaysOnTopHint | Qt::WindowTitleHint | Qt::WindowSystemMenuHint); dialog.setWindowModality(Qt::WindowModal); From c0c4ed0d3bff9670bfaab6a8de304e37ec9e0896 Mon Sep 17 00:00:00 2001 From: Morph <39850852+Morph1984@users.noreply.github.com> Date: Sun, 27 Sep 2020 11:40:15 -0400 Subject: [PATCH 06/37] controllers/npad: Connect a controller on init if none are connected --- src/core/hle/service/hid/controllers/npad.cpp | 13 +++++++++++++ src/yuzu/configuration/config.cpp | 3 ++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp index e311bc18c..c4b26196a 100644 --- a/src/core/hle/service/hid/controllers/npad.cpp +++ b/src/core/hle/service/hid/controllers/npad.cpp @@ -224,6 +224,19 @@ void Controller_NPad::OnInit() { player.connected}; }); + // Connect the Player 1 or Handheld controller if none are connected. + if (std::none_of(connected_controllers.begin(), connected_controllers.end(), + [](const ControllerHolder& controller) { return controller.is_connected; })) { + const auto controller = MapSettingsTypeToNPad(Settings::values.players[0].controller_type); + if (controller == NPadControllerType::Handheld) { + Settings::values.players[HANDHELD_INDEX].connected = true; + connected_controllers[HANDHELD_INDEX] = {controller, true}; + } else { + Settings::values.players[0].connected = true; + connected_controllers[0] = {controller, true}; + } + } + // Account for handheld if (connected_controllers[HANDHELD_INDEX].is_connected) { connected_controllers[HANDHELD_INDEX].type = NPadControllerType::Handheld; diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index 5c8b02fbe..545cafca9 100644 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp @@ -280,7 +280,8 @@ void Config::ReadPlayerValue(std::size_t player_index) { } } else { player.connected = - ReadSetting(QStringLiteral("%1connected").arg(player_prefix), false).toBool(); + ReadSetting(QStringLiteral("%1connected").arg(player_prefix), player_index == 0) + .toBool(); player.controller_type = static_cast( qt_config From 64e174237e7ad9ae082e24303d321534f4e78bca Mon Sep 17 00:00:00 2001 From: Morph <39850852+Morph1984@users.noreply.github.com> Date: Sun, 27 Sep 2020 14:20:22 -0400 Subject: [PATCH 07/37] config: Migrate config files into config/custom Co-authored-by: lat9nq --- src/yuzu/configuration/config.cpp | 49 +++++++++++++++++++------------ src/yuzu/configuration/config.h | 4 ++- src/yuzu/main.cpp | 28 +++++++++++++++++- src/yuzu/main.h | 1 + 4 files changed, 61 insertions(+), 21 deletions(-) diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index 545cafca9..618f991b0 100644 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp @@ -15,27 +15,10 @@ namespace FS = Common::FS; -Config::Config(const std::string& config_file, ConfigType config_type) : type(config_type) { +Config::Config(const std::string& config_name, ConfigType config_type) : type(config_type) { global = config_type == ConfigType::GlobalConfig; - switch (config_type) { - case ConfigType::GlobalConfig: - case ConfigType::PerGameConfig: - qt_config_loc = fmt::format("{}" DIR_SEP "{}.ini", FS::GetUserPath(FS::UserPath::ConfigDir), - config_file); - FS::CreateFullPath(qt_config_loc); - qt_config = std::make_unique(QString::fromStdString(qt_config_loc), - QSettings::IniFormat); - Reload(); - break; - case ConfigType::InputProfile: - qt_config_loc = fmt::format("{}input" DIR_SEP "{}.ini", - FS::GetUserPath(FS::UserPath::ConfigDir), config_file); - FS::CreateFullPath(qt_config_loc); - qt_config = std::make_unique(QString::fromStdString(qt_config_loc), - QSettings::IniFormat); - break; - } + Initialize(config_name); } Config::~Config() { @@ -256,6 +239,34 @@ const std::array Config::default_hotkeys{{ }}; // clang-format on +void Config::Initialize(const std::string& config_name) { + switch (type) { + case ConfigType::GlobalConfig: + qt_config_loc = fmt::format("{}" DIR_SEP "{}.ini", FS::GetUserPath(FS::UserPath::ConfigDir), + config_name); + FS::CreateFullPath(qt_config_loc); + qt_config = std::make_unique(QString::fromStdString(qt_config_loc), + QSettings::IniFormat); + Reload(); + break; + case ConfigType::PerGameConfig: + qt_config_loc = fmt::format("{}custom" DIR_SEP "{}.ini", + FS::GetUserPath(FS::UserPath::ConfigDir), config_name); + FS::CreateFullPath(qt_config_loc); + qt_config = std::make_unique(QString::fromStdString(qt_config_loc), + QSettings::IniFormat); + Reload(); + break; + case ConfigType::InputProfile: + qt_config_loc = fmt::format("{}input" DIR_SEP "{}.ini", + FS::GetUserPath(FS::UserPath::ConfigDir), config_name); + FS::CreateFullPath(qt_config_loc); + qt_config = std::make_unique(QString::fromStdString(qt_config_loc), + QSettings::IniFormat); + break; + } +} + void Config::ReadPlayerValue(std::size_t player_index) { const QString player_prefix = [this, player_index] { if (type == ConfigType::InputProfile) { diff --git a/src/yuzu/configuration/config.h b/src/yuzu/configuration/config.h index a1ffca48f..8a600e19d 100644 --- a/src/yuzu/configuration/config.h +++ b/src/yuzu/configuration/config.h @@ -22,7 +22,7 @@ public: InputProfile, }; - explicit Config(const std::string& config_loc = "qt-config", + explicit Config(const std::string& config_name = "qt-config", ConfigType config_type = ConfigType::GlobalConfig); ~Config(); @@ -45,6 +45,8 @@ public: static const std::array default_hotkeys; private: + void Initialize(const std::string& config_name); + void ReadValues(); void ReadPlayerValue(std::size_t player_index); void ReadDebugValues(); diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 5f9f416ea..4a3dea2a5 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -50,6 +50,7 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual #include #include #include +#include #include #include #include @@ -277,6 +278,8 @@ GMainWindow::GMainWindow() if (args.length() >= 2) { BootGame(args[1]); } + + MigrateConfigFiles(); } GMainWindow::~GMainWindow() { @@ -1578,7 +1581,8 @@ void GMainWindow::RemoveCustomConfiguration(u64 program_id) { const QString config_dir = QString::fromStdString(Common::FS::GetUserPath(Common::FS::UserPath::ConfigDir)); const QString custom_config_file_path = - config_dir + QString::fromStdString(fmt::format("{:016X}.ini", program_id)); + config_dir + QStringLiteral("custom") + QDir::separator() + + QString::fromStdString(fmt::format("{:016X}.ini", program_id)); if (!QFile::exists(custom_config_file_path)) { QMessageBox::warning(this, tr("Error Removing Custom Configuration"), @@ -2394,6 +2398,28 @@ void GMainWindow::OnCaptureScreenshot() { OnStartGame(); } +// TODO: Written 2020-10-01: Remove per-game config migration code when it is irrelevant +void GMainWindow::MigrateConfigFiles() { + const std::string& config_dir_str = Common::FS::GetUserPath(Common::FS::UserPath::ConfigDir); + const QDir config_dir = QDir(QString::fromStdString(config_dir_str)); + const QStringList config_dir_list = config_dir.entryList(QStringList(QStringLiteral("*.ini"))); + + Common::FS::CreateFullPath(fmt::format("{}custom" DIR_SEP, config_dir_str)); + for (QStringList::const_iterator it = config_dir_list.constBegin(); it != config_dir_list.constEnd(); ++it) { + const auto filename = it->toStdString(); + if (filename.find_first_not_of("0123456789abcdefACBDEF", 0) < 16) { + continue; + } + const auto origin = fmt::format("{}{}", config_dir_str, filename); + const auto destination = fmt::format("{}custom" DIR_SEP "{}", config_dir_str, filename); + LOG_INFO(Frontend, "Migrating config file from {} to {}", origin, destination); + if (!Common::FS::Rename(origin, destination)) { + // Delete the old config file if one already exists in the new location. + Common::FS::Delete(origin); + } + } +} + void GMainWindow::UpdateWindowTitle(const std::string& title_name, const std::string& title_version) { const auto full_name = std::string(Common::g_build_fullname); diff --git a/src/yuzu/main.h b/src/yuzu/main.h index afcfa68a9..b380a66f3 100644 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h @@ -251,6 +251,7 @@ private: std::optional SelectRomFSDumpTarget(const FileSys::ContentProvider&, u64 program_id); InstallResult InstallNSPXCI(const QString& filename); InstallResult InstallNCA(const QString& filename); + void MigrateConfigFiles(); void UpdateWindowTitle(const std::string& title_name = {}, const std::string& title_version = {}); void UpdateStatusBar(); From 8ead176639be482fb26c2eb3f95fc942e52efee0 Mon Sep 17 00:00:00 2001 From: Morph <39850852+Morph1984@users.noreply.github.com> Date: Mon, 28 Sep 2020 04:53:21 -0400 Subject: [PATCH 08/37] udp/client: Reduce testing period to 5 seconds --- src/input_common/udp/client.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/input_common/udp/client.cpp b/src/input_common/udp/client.cpp index 7039d6fc3..3677e79ca 100644 --- a/src/input_common/udp/client.cpp +++ b/src/input_common/udp/client.cpp @@ -344,7 +344,7 @@ void TestCommunication(const std::string& host, u16 port, std::size_t pad_index, }; Socket socket{host, port, pad_index, client_id, std::move(callback)}; std::thread worker_thread{SocketLoop, &socket}; - const bool result = success_event.WaitFor(std::chrono::seconds(8)); + const bool result = success_event.WaitFor(std::chrono::seconds(5)); socket.Stop(); worker_thread.join(); if (result) { From 8f2959f6804e0d1048ecaa6f4046622e069fe7db Mon Sep 17 00:00:00 2001 From: Morph <39850852+Morph1984@users.noreply.github.com> Date: Mon, 28 Sep 2020 10:00:15 -0400 Subject: [PATCH 09/37] settings: Preparation for per-game input settings --- src/core/frontend/applets/controller.cpp | 4 +- src/core/frontend/framebuffer_layout.cpp | 2 +- src/core/hle/service/am/am.cpp | 4 +- .../hle/service/am/applets/controller.cpp | 2 +- src/core/hle/service/apm/controller.cpp | 3 +- src/core/hle/service/hid/controllers/npad.cpp | 42 ++++++++------ src/core/hle/service/hid/hid.cpp | 4 +- src/core/hle/service/vi/vi.cpp | 2 +- src/core/settings.cpp | 8 ++- src/core/settings.h | 57 +++++++++++++++---- src/core/telemetry_session.cpp | 2 +- src/yuzu/applets/controller.cpp | 30 +++++----- src/yuzu/configuration/config.cpp | 24 ++++---- src/yuzu/configuration/configure_input.cpp | 24 ++++---- .../configure_input_advanced.cpp | 4 +- .../configuration/configure_input_player.cpp | 8 +-- src/yuzu/main.cpp | 23 ++++---- src/yuzu_cmd/config.cpp | 25 ++++---- src/yuzu_tester/config.cpp | 14 ++--- 19 files changed, 167 insertions(+), 115 deletions(-) diff --git a/src/core/frontend/applets/controller.cpp b/src/core/frontend/applets/controller.cpp index 5582091f4..1ac2fb80c 100644 --- a/src/core/frontend/applets/controller.cpp +++ b/src/core/frontend/applets/controller.cpp @@ -27,7 +27,7 @@ void DefaultControllerApplet::ReconfigureControllers(std::function callb ->GetAppletResource() ->GetController(Service::HID::HidController::NPad); - auto& players = Settings::values.players; + auto& players = Settings::values.players.GetValue(); const std::size_t min_supported_players = parameters.enable_single_mode ? 1 : parameters.min_players; @@ -66,7 +66,7 @@ void DefaultControllerApplet::ReconfigureControllers(std::function callb npad.MapSettingsTypeToNPad(Settings::ControllerType::RightJoycon), index); } } else if (index == 0 && parameters.enable_single_mode && parameters.allow_handheld && - !Settings::values.use_docked_mode) { + !Settings::values.use_docked_mode.GetValue()) { // We should *never* reach here under any normal circumstances. npad.AddNewControllerAt(npad.MapSettingsTypeToNPad(Settings::ControllerType::Handheld), index); diff --git a/src/core/frontend/framebuffer_layout.cpp b/src/core/frontend/framebuffer_layout.cpp index 1acc82497..b9a270a55 100644 --- a/src/core/frontend/framebuffer_layout.cpp +++ b/src/core/frontend/framebuffer_layout.cpp @@ -47,7 +47,7 @@ FramebufferLayout DefaultFrameLayout(u32 width, u32 height) { FramebufferLayout FrameLayoutFromResolutionScale(u32 res_scale) { u32 width, height; - if (Settings::values.use_docked_mode) { + if (Settings::values.use_docked_mode.GetValue()) { width = ScreenDocked::Width * res_scale; height = ScreenDocked::Height * res_scale; } else { diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index 2ce742e35..eb097738a 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp @@ -751,7 +751,7 @@ void ICommonStateGetter::GetDefaultDisplayResolution(Kernel::HLERequestContext& IPC::ResponseBuilder rb{ctx, 4}; rb.Push(RESULT_SUCCESS); - if (Settings::values.use_docked_mode) { + if (Settings::values.use_docked_mode.GetValue()) { rb.Push(static_cast(Service::VI::DisplayResolution::DockedWidth) * static_cast(Settings::values.resolution_factor.GetValue())); rb.Push(static_cast(Service::VI::DisplayResolution::DockedHeight) * @@ -824,7 +824,7 @@ void IStorage::Open(Kernel::HLERequestContext& ctx) { } void ICommonStateGetter::GetOperationMode(Kernel::HLERequestContext& ctx) { - const bool use_docked_mode{Settings::values.use_docked_mode}; + const bool use_docked_mode{Settings::values.use_docked_mode.GetValue()}; LOG_DEBUG(Service_AM, "called, use_docked_mode={}", use_docked_mode); IPC::ResponseBuilder rb{ctx, 3}; diff --git a/src/core/hle/service/am/applets/controller.cpp b/src/core/hle/service/am/applets/controller.cpp index a0152b4ea..43b79412e 100644 --- a/src/core/hle/service/am/applets/controller.cpp +++ b/src/core/hle/service/am/applets/controller.cpp @@ -222,7 +222,7 @@ void Controller::Execute() { void Controller::ConfigurationComplete() { ControllerSupportResultInfo result_info{}; - const auto& players = Settings::values.players; + const auto& players = Settings::values.players.GetValue(); // If enable_single_mode is enabled, player_count is 1 regardless of any other parameters. // Otherwise, only count connected players from P1-P8. diff --git a/src/core/hle/service/apm/controller.cpp b/src/core/hle/service/apm/controller.cpp index 25a886238..ce993bad3 100644 --- a/src/core/hle/service/apm/controller.cpp +++ b/src/core/hle/service/apm/controller.cpp @@ -69,7 +69,8 @@ void Controller::SetFromCpuBoostMode(CpuBoostMode mode) { } PerformanceMode Controller::GetCurrentPerformanceMode() const { - return Settings::values.use_docked_mode ? PerformanceMode::Docked : PerformanceMode::Handheld; + return Settings::values.use_docked_mode.GetValue() ? PerformanceMode::Docked + : PerformanceMode::Handheld; } PerformanceConfiguration Controller::GetCurrentPerformanceConfiguration(PerformanceMode mode) { diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp index c4b26196a..15d5fa6e8 100644 --- a/src/core/hle/service/hid/controllers/npad.cpp +++ b/src/core/hle/service/hid/controllers/npad.cpp @@ -184,11 +184,14 @@ void Controller_NPad::InitNewlyAddedController(std::size_t controller_idx) { controller.single_color.button_color = 0; controller.dual_color_error = ColorReadError::ReadOk; - controller.left_color.body_color = Settings::values.players[controller_idx].body_color_left; - controller.left_color.button_color = Settings::values.players[controller_idx].button_color_left; - controller.right_color.body_color = Settings::values.players[controller_idx].body_color_right; + controller.left_color.body_color = + Settings::values.players.GetValue()[controller_idx].body_color_left; + controller.left_color.button_color = + Settings::values.players.GetValue()[controller_idx].button_color_left; + controller.right_color.body_color = + Settings::values.players.GetValue()[controller_idx].body_color_right; controller.right_color.button_color = - Settings::values.players[controller_idx].button_color_right; + Settings::values.players.GetValue()[controller_idx].button_color_right; controller.battery_level[0] = BATTERY_FULL; controller.battery_level[1] = BATTERY_FULL; @@ -218,8 +221,9 @@ void Controller_NPad::OnInit() { style.pokeball.Assign(1); } - std::transform(Settings::values.players.begin(), Settings::values.players.end(), - connected_controllers.begin(), [](const Settings::PlayerInput& player) { + std::transform(Settings::values.players.GetValue().begin(), + Settings::values.players.GetValue().end(), connected_controllers.begin(), + [](const Settings::PlayerInput& player) { return ControllerHolder{MapSettingsTypeToNPad(player.controller_type), player.connected}; }); @@ -227,12 +231,13 @@ void Controller_NPad::OnInit() { // Connect the Player 1 or Handheld controller if none are connected. if (std::none_of(connected_controllers.begin(), connected_controllers.end(), [](const ControllerHolder& controller) { return controller.is_connected; })) { - const auto controller = MapSettingsTypeToNPad(Settings::values.players[0].controller_type); + const auto controller = + MapSettingsTypeToNPad(Settings::values.players.GetValue()[0].controller_type); if (controller == NPadControllerType::Handheld) { - Settings::values.players[HANDHELD_INDEX].connected = true; + Settings::values.players.GetValue()[HANDHELD_INDEX].connected = true; connected_controllers[HANDHELD_INDEX] = {controller, true}; } else { - Settings::values.players[0].connected = true; + Settings::values.players.GetValue()[0].connected = true; connected_controllers[0] = {controller, true}; } } @@ -255,7 +260,7 @@ void Controller_NPad::OnInit() { } void Controller_NPad::OnLoadInputDevices() { - const auto& players = Settings::values.players; + const auto& players = Settings::values.players.GetValue(); for (std::size_t i = 0; i < players.size(); ++i) { std::transform(players[i].buttons.begin() + Settings::NativeButton::BUTTON_HID_BEGIN, players[i].buttons.begin() + Settings::NativeButton::BUTTON_HID_END, @@ -528,7 +533,7 @@ void Controller_NPad::OnMotionUpdate(const Core::Timing::CoreTiming& core_timing // Try to read sixaxis sensor states std::array motion_devices; - if (sixaxis_sensors_enabled && Settings::values.motion_enabled) { + if (sixaxis_sensors_enabled && Settings::values.motion_enabled.GetValue()) { sixaxis_at_rest = true; for (std::size_t e = 0; e < motion_devices.size(); ++e) { const auto& device = motions[i][e]; @@ -666,7 +671,7 @@ void Controller_NPad::VibrateController(const std::vector& controllers, const std::vector& vibrations) { LOG_TRACE(Service_HID, "called"); - if (!Settings::values.vibration_enabled || !can_controllers_vibrate) { + if (!Settings::values.vibration_enabled.GetValue() || !can_controllers_vibrate) { return; } bool success = true; @@ -714,16 +719,17 @@ void Controller_NPad::UpdateControllerAt(NPadControllerType controller, std::siz } if (controller == NPadControllerType::Handheld) { - Settings::values.players[HANDHELD_INDEX].controller_type = + Settings::values.players.GetValue()[HANDHELD_INDEX].controller_type = MapNPadToSettingsType(controller); - Settings::values.players[HANDHELD_INDEX].connected = true; + Settings::values.players.GetValue()[HANDHELD_INDEX].connected = true; connected_controllers[HANDHELD_INDEX] = {controller, true}; InitNewlyAddedController(HANDHELD_INDEX); return; } - Settings::values.players[npad_index].controller_type = MapNPadToSettingsType(controller); - Settings::values.players[npad_index].connected = true; + Settings::values.players.GetValue()[npad_index].controller_type = + MapNPadToSettingsType(controller); + Settings::values.players.GetValue()[npad_index].connected = true; connected_controllers[npad_index] = {controller, true}; InitNewlyAddedController(npad_index); } @@ -733,7 +739,7 @@ void Controller_NPad::DisconnectNPad(u32 npad_id) { } void Controller_NPad::DisconnectNPadAtIndex(std::size_t npad_index) { - Settings::values.players[npad_index].connected = false; + Settings::values.players.GetValue()[npad_index].connected = false; connected_controllers[npad_index].is_connected = false; auto& controller = shared_memory_entries[npad_index]; @@ -895,7 +901,7 @@ bool Controller_NPad::IsControllerSupported(NPadControllerType controller) const return false; } // Handheld should not be supported in docked mode - if (Settings::values.use_docked_mode) { + if (Settings::values.use_docked_mode.GetValue()) { return false; } diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index 50f709b25..fb57dec02 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp @@ -935,7 +935,7 @@ void Hid::CreateActiveVibrationDeviceList(Kernel::HLERequestContext& ctx) { void Hid::PermitVibration(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto can_vibrate{rp.Pop()}; - Settings::values.vibration_enabled = can_vibrate; + Settings::values.vibration_enabled.SetValue(can_vibrate); LOG_DEBUG(Service_HID, "called, can_vibrate={}", can_vibrate); @@ -948,7 +948,7 @@ void Hid::IsVibrationPermitted(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); - rb.Push(Settings::values.vibration_enabled); + rb.Push(Settings::values.vibration_enabled.GetValue()); } void Hid::ActivateConsoleSixAxisSensor(Kernel::HLERequestContext& ctx) { diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp index 5b0e371fe..55e00dd93 100644 --- a/src/core/hle/service/vi/vi.cpp +++ b/src/core/hle/service/vi/vi.cpp @@ -771,7 +771,7 @@ private: IPC::ResponseBuilder rb{ctx, 6}; rb.Push(RESULT_SUCCESS); - if (Settings::values.use_docked_mode) { + if (Settings::values.use_docked_mode.GetValue()) { rb.Push(static_cast(Service::VI::DisplayResolution::DockedWidth) * static_cast(Settings::values.resolution_factor.GetValue())); rb.Push(static_cast(Service::VI::DisplayResolution::DockedHeight) * diff --git a/src/core/settings.cpp b/src/core/settings.cpp index 0587b9374..aadbc3932 100644 --- a/src/core/settings.cpp +++ b/src/core/settings.cpp @@ -49,7 +49,7 @@ void LogSettings() { }; LOG_INFO(Config, "yuzu Configuration:"); - log_setting("Controls_UseDockedMode", values.use_docked_mode); + log_setting("Controls_UseDockedMode", values.use_docked_mode.GetValue()); log_setting("System_RngSeed", values.rng_seed.GetValue().value_or(0)); log_setting("System_CurrentUser", values.current_user); log_setting("System_LanguageIndex", values.language_index.GetValue()); @@ -145,6 +145,12 @@ void RestoreGlobalState() { values.rng_seed.SetGlobal(true); values.custom_rtc.SetGlobal(true); values.sound_index.SetGlobal(true); + + // Controls + values.players.SetGlobal(true); + values.use_docked_mode.SetGlobal(true); + values.vibration_enabled.SetGlobal(true); + values.motion_enabled.SetGlobal(true); } void Sanitize() { diff --git a/src/core/settings.h b/src/core/settings.h index 28616a574..edd2a00ca 100644 --- a/src/core/settings.h +++ b/src/core/settings.h @@ -65,6 +65,38 @@ private: Type local{}; }; +/** + * The InputSetting class allows for getting a reference to either the global or local members. + * This is required as we cannot easily modify the values of user-defined types within containers + * using the SetValue() member function found in the Setting class. The primary purpose of this + * class is to store an array of 10 PlayerInput structs for both the global and local (per-game) + * setting and allows for easily accessing and modifying both settings. + */ +template +class InputSetting final { +public: + InputSetting() = default; + explicit InputSetting(Type val) : global{val} {} + ~InputSetting() = default; + void SetGlobal(bool to_global) { + use_global = to_global; + } + bool UsingGlobal() const { + return use_global; + } + Type& GetValue(bool need_global = false) { + if (use_global || need_global) { + return global; + } + return local; + } + +private: + bool use_global = true; + Type global{}; + Type local{}; +}; + struct TouchFromButtonMap { std::string name; std::vector buttons; @@ -133,9 +165,17 @@ struct Values { Setting sound_index; // Controls - std::array players; + InputSetting> players; - bool use_docked_mode; + Setting use_docked_mode; + + Setting vibration_enabled; + + Setting motion_enabled; + std::string motion_device; + std::string udp_input_address; + u16 udp_input_port; + u8 udp_pad_index; bool mouse_enabled; std::string mouse_device; @@ -149,20 +189,15 @@ struct Values { ButtonsRaw debug_pad_buttons; AnalogsRaw debug_pad_analogs; - bool vibration_enabled; - - bool motion_enabled; - std::string motion_device; - std::string touch_device; TouchscreenInput touchscreen; - std::atomic_bool is_device_reload_pending{true}; + bool use_touch_from_button; + std::string touch_device; int touch_from_button_map_index; - std::string udp_input_address; - u16 udp_input_port; - u8 udp_pad_index; std::vector touch_from_button_maps; + std::atomic_bool is_device_reload_pending{true}; + // Data Storage bool use_virtual_sd; bool gamecard_inserted; diff --git a/src/core/telemetry_session.cpp b/src/core/telemetry_session.cpp index ebc19e18a..e0908186b 100644 --- a/src/core/telemetry_session.cpp +++ b/src/core/telemetry_session.cpp @@ -213,7 +213,7 @@ void TelemetrySession::AddInitialInfo(Loader::AppLoader& app_loader) { Settings::values.use_assembly_shaders.GetValue()); AddField(field_type, "Renderer_UseAsynchronousShaders", Settings::values.use_asynchronous_shaders.GetValue()); - AddField(field_type, "System_UseDockedMode", Settings::values.use_docked_mode); + AddField(field_type, "System_UseDockedMode", Settings::values.use_docked_mode.GetValue()); } bool TelemetrySession::SubmitTestcase() { diff --git a/src/yuzu/applets/controller.cpp b/src/yuzu/applets/controller.cpp index ee770f315..7697fe434 100644 --- a/src/yuzu/applets/controller.cpp +++ b/src/yuzu/applets/controller.cpp @@ -261,26 +261,26 @@ void QtControllerSelectorDialog::ApplyConfiguration() { UpdateControllerState(index); } - const bool pre_docked_mode = Settings::values.use_docked_mode; - Settings::values.use_docked_mode = ui->radioDocked->isChecked(); - OnDockedModeChanged(pre_docked_mode, Settings::values.use_docked_mode); + const bool pre_docked_mode = Settings::values.use_docked_mode.GetValue(); + Settings::values.use_docked_mode.SetValue(ui->radioDocked->isChecked()); + OnDockedModeChanged(pre_docked_mode, Settings::values.use_docked_mode.GetValue()); - Settings::values.vibration_enabled = ui->vibrationGroup->isChecked(); + Settings::values.vibration_enabled.SetValue(ui->vibrationGroup->isChecked()); } void QtControllerSelectorDialog::LoadConfiguration() { for (std::size_t index = 0; index < NUM_PLAYERS; ++index) { - const auto connected = Settings::values.players[index].connected || - (index == 0 && Settings::values.players[8].connected); + const auto connected = Settings::values.players.GetValue()[index].connected || + (index == 0 && Settings::values.players.GetValue()[8].connected); player_groupboxes[index]->setChecked(connected); connected_controller_checkboxes[index]->setChecked(connected); emulated_controllers[index]->setCurrentIndex( - GetIndexFromControllerType(Settings::values.players[index].controller_type)); + GetIndexFromControllerType(Settings::values.players.GetValue()[index].controller_type)); } - UpdateDockedState(Settings::values.players[8].connected); + UpdateDockedState(Settings::values.players.GetValue()[8].connected); - ui->vibrationGroup->setChecked(Settings::values.vibration_enabled); + ui->vibrationGroup->setChecked(Settings::values.vibration_enabled.GetValue()); } void QtControllerSelectorDialog::CallConfigureInputDialog() { @@ -448,7 +448,7 @@ void QtControllerSelectorDialog::UpdateControllerIcon(std::size_t player_index) } void QtControllerSelectorDialog::UpdateControllerState(std::size_t player_index) { - auto& player = Settings::values.players[player_index]; + auto& player = Settings::values.players.GetValue()[player_index]; player.controller_type = GetControllerTypeFromIndex(emulated_controllers[player_index]->currentIndex()); @@ -461,7 +461,7 @@ void QtControllerSelectorDialog::UpdateControllerState(std::size_t player_index) } // Player 1 and Handheld - auto& handheld = Settings::values.players[8]; + auto& handheld = Settings::values.players.GetValue()[8]; // If Handheld is selected, copy all the settings from Player 1 to Handheld. if (player.controller_type == Settings::ControllerType::Handheld) { handheld = player; @@ -527,8 +527,8 @@ void QtControllerSelectorDialog::UpdateDockedState(bool is_handheld) { ui->radioDocked->setEnabled(!is_handheld); ui->radioUndocked->setEnabled(!is_handheld); - ui->radioDocked->setChecked(Settings::values.use_docked_mode); - ui->radioUndocked->setChecked(!Settings::values.use_docked_mode); + ui->radioDocked->setChecked(Settings::values.use_docked_mode.GetValue()); + ui->radioUndocked->setChecked(!Settings::values.use_docked_mode.GetValue()); // Also force into undocked mode if the controller type is handheld. if (is_handheld) { @@ -571,8 +571,8 @@ void QtControllerSelectorDialog::DisableUnsupportedPlayers() { for (std::size_t index = max_supported_players; index < NUM_PLAYERS; ++index) { // Disconnect any unsupported players here and disable or hide them if applicable. - Settings::values.players[index].connected = false; - UpdateController(Settings::values.players[index].controller_type, index, false); + Settings::values.players.GetValue()[index].connected = false; + UpdateController(Settings::values.players.GetValue()[index].controller_type, index, false); // Hide the player widgets when max_supported_controllers is less than or equal to 4. if (max_supported_players <= 4) { player_widgets[index]->hide(); diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index 618f991b0..296c58f58 100644 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp @@ -276,7 +276,7 @@ void Config::ReadPlayerValue(std::size_t player_index) { } }(); - auto& player = Settings::values.players[player_index]; + auto& player = Settings::values.players.GetValue()[player_index]; if (player_prefix.isEmpty()) { const auto controller = static_cast( @@ -481,7 +481,7 @@ void Config::ReadAudioValues() { void Config::ReadControlValues() { qt_config->beginGroup(QStringLiteral("Controls")); - for (std::size_t p = 0; p < Settings::values.players.size(); ++p) { + for (std::size_t p = 0; p < Settings::values.players.GetValue().size(); ++p) { ReadPlayerValue(p); } ReadDebugValues(); @@ -490,11 +490,10 @@ void Config::ReadControlValues() { ReadTouchscreenValues(); ReadMotionTouchValues(); - Settings::values.vibration_enabled = - ReadSetting(QStringLiteral("vibration_enabled"), true).toBool(); - Settings::values.motion_enabled = ReadSetting(QStringLiteral("motion_enabled"), true).toBool(); - Settings::values.use_docked_mode = - ReadSetting(QStringLiteral("use_docked_mode"), false).toBool(); + ReadSettingGlobal(Settings::values.use_docked_mode, QStringLiteral("use_docked_mode"), false); + ReadSettingGlobal(Settings::values.vibration_enabled, QStringLiteral("vibration_enabled"), + true); + ReadSettingGlobal(Settings::values.motion_enabled, QStringLiteral("motion_enabled"), true); qt_config->endGroup(); } @@ -976,7 +975,7 @@ void Config::SavePlayerValue(std::size_t player_index) { } }(); - const auto& player = Settings::values.players[player_index]; + const auto& player = Settings::values.players.GetValue()[player_index]; WriteSetting(QStringLiteral("%1type").arg(player_prefix), static_cast(player.controller_type), @@ -1140,7 +1139,7 @@ void Config::SaveAudioValues() { void Config::SaveControlValues() { qt_config->beginGroup(QStringLiteral("Controls")); - for (std::size_t p = 0; p < Settings::values.players.size(); ++p) { + for (std::size_t p = 0; p < Settings::values.players.GetValue().size(); ++p) { SavePlayerValue(p); } SaveDebugValues(); @@ -1148,8 +1147,10 @@ void Config::SaveControlValues() { SaveTouchscreenValues(); SaveMotionTouchValues(); - WriteSetting(QStringLiteral("vibration_enabled"), Settings::values.vibration_enabled, true); - WriteSetting(QStringLiteral("motion_enabled"), Settings::values.motion_enabled, true); + WriteSettingGlobal(QStringLiteral("use_docked_mode"), Settings::values.use_docked_mode, false); + WriteSettingGlobal(QStringLiteral("vibration_enabled"), Settings::values.vibration_enabled, + true); + WriteSettingGlobal(QStringLiteral("motion_enabled"), Settings::values.motion_enabled, true); WriteSetting(QStringLiteral("motion_device"), QString::fromStdString(Settings::values.motion_device), QStringLiteral("engine:motion_emu,update_period:100,sensitivity:0.01")); @@ -1157,7 +1158,6 @@ void Config::SaveControlValues() { QString::fromStdString(Settings::values.touch_device), QStringLiteral("engine:emu_window")); WriteSetting(QStringLiteral("keyboard_enabled"), Settings::values.keyboard_enabled, false); - WriteSetting(QStringLiteral("use_docked_mode"), Settings::values.use_docked_mode, false); qt_config->endGroup(); } diff --git a/src/yuzu/configuration/configure_input.cpp b/src/yuzu/configuration/configure_input.cpp index 523ece426..9a4de4c5d 100644 --- a/src/yuzu/configuration/configure_input.cpp +++ b/src/yuzu/configuration/configure_input.cpp @@ -181,12 +181,12 @@ void ConfigureInput::ApplyConfiguration() { advanced->ApplyConfiguration(); - const bool pre_docked_mode = Settings::values.use_docked_mode; - Settings::values.use_docked_mode = ui->radioDocked->isChecked(); - OnDockedModeChanged(pre_docked_mode, Settings::values.use_docked_mode); + const bool pre_docked_mode = Settings::values.use_docked_mode.GetValue(); + Settings::values.use_docked_mode.SetValue(ui->radioDocked->isChecked()); + OnDockedModeChanged(pre_docked_mode, Settings::values.use_docked_mode.GetValue()); - Settings::values.vibration_enabled = ui->vibrationGroup->isChecked(); - Settings::values.motion_enabled = ui->motionGroup->isChecked(); + Settings::values.vibration_enabled.SetValue(ui->vibrationGroup->isChecked()); + Settings::values.motion_enabled.SetValue(ui->motionGroup->isChecked()); } void ConfigureInput::changeEvent(QEvent* event) { @@ -203,16 +203,16 @@ void ConfigureInput::RetranslateUI() { void ConfigureInput::LoadConfiguration() { LoadPlayerControllerIndices(); - UpdateDockedState(Settings::values.players[8].connected); + UpdateDockedState(Settings::values.players.GetValue()[8].connected); - ui->vibrationGroup->setChecked(Settings::values.vibration_enabled); - ui->motionGroup->setChecked(Settings::values.motion_enabled); + ui->vibrationGroup->setChecked(Settings::values.vibration_enabled.GetValue()); + ui->motionGroup->setChecked(Settings::values.motion_enabled.GetValue()); } void ConfigureInput::LoadPlayerControllerIndices() { for (std::size_t i = 0; i < player_connected.size(); ++i) { - const auto connected = Settings::values.players[i].connected || - (i == 0 && Settings::values.players[8].connected); + const auto connected = Settings::values.players.GetValue()[i].connected || + (i == 0 && Settings::values.players.GetValue()[8].connected); player_connected[i]->setChecked(connected); } } @@ -241,8 +241,8 @@ void ConfigureInput::UpdateDockedState(bool is_handheld) { ui->radioDocked->setEnabled(!is_handheld); ui->radioUndocked->setEnabled(!is_handheld); - ui->radioDocked->setChecked(Settings::values.use_docked_mode); - ui->radioUndocked->setChecked(!Settings::values.use_docked_mode); + ui->radioDocked->setChecked(Settings::values.use_docked_mode.GetValue()); + ui->radioUndocked->setChecked(!Settings::values.use_docked_mode.GetValue()); // Also force into undocked mode if the controller type is handheld. if (is_handheld) { diff --git a/src/yuzu/configuration/configure_input_advanced.cpp b/src/yuzu/configuration/configure_input_advanced.cpp index 3715db0ab..3074be833 100644 --- a/src/yuzu/configuration/configure_input_advanced.cpp +++ b/src/yuzu/configuration/configure_input_advanced.cpp @@ -107,7 +107,7 @@ void ConfigureInputAdvanced::OnControllerButtonClick(int player_idx, int button_ void ConfigureInputAdvanced::ApplyConfiguration() { for (std::size_t player_idx = 0; player_idx < controllers_color_buttons.size(); ++player_idx) { - auto& player = Settings::values.players[player_idx]; + auto& player = Settings::values.players.GetValue()[player_idx]; std::array colors{}; std::transform(controllers_colors[player_idx].begin(), controllers_colors[player_idx].end(), colors.begin(), [](QColor color) { return color.rgb(); }); @@ -126,7 +126,7 @@ void ConfigureInputAdvanced::ApplyConfiguration() { void ConfigureInputAdvanced::LoadConfiguration() { for (std::size_t player_idx = 0; player_idx < controllers_color_buttons.size(); ++player_idx) { - auto& player = Settings::values.players[player_idx]; + auto& player = Settings::values.players.GetValue()[player_idx]; std::array colors = { player.body_color_left, player.button_color_left, diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp index b4de2f6af..213a76224 100644 --- a/src/yuzu/configuration/configure_input_player.cpp +++ b/src/yuzu/configuration/configure_input_player.cpp @@ -544,7 +544,7 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i ConfigureInputPlayer::~ConfigureInputPlayer() = default; void ConfigureInputPlayer::ApplyConfiguration() { - auto& player = Settings::values.players[player_index]; + auto& player = Settings::values.players.GetValue()[player_index]; auto& buttons = debug ? Settings::values.debug_pad_buttons : player.buttons; auto& analogs = debug ? Settings::values.debug_pad_analogs : player.analogs; @@ -572,7 +572,7 @@ void ConfigureInputPlayer::ApplyConfiguration() { } // Player 1 and Handheld - auto& handheld = Settings::values.players[HANDHELD_INDEX]; + auto& handheld = Settings::values.players.GetValue()[HANDHELD_INDEX]; // If Handheld is selected, copy all the settings from Player 1 to Handheld. if (player.controller_type == Settings::ControllerType::Handheld) { handheld = player; @@ -609,7 +609,7 @@ void ConfigureInputPlayer::RetranslateUI() { } void ConfigureInputPlayer::LoadConfiguration() { - auto& player = Settings::values.players[player_index]; + auto& player = Settings::values.players.GetValue()[player_index]; if (debug) { std::transform(Settings::values.debug_pad_buttons.begin(), Settings::values.debug_pad_buttons.end(), buttons_param.begin(), @@ -636,7 +636,7 @@ void ConfigureInputPlayer::LoadConfiguration() { ui->comboControllerType->setCurrentIndex(static_cast(player.controller_type)); ui->groupConnectedController->setChecked( player.connected || - (player_index == 0 && Settings::values.players[HANDHELD_INDEX].connected)); + (player_index == 0 && Settings::values.players.GetValue()[HANDHELD_INDEX].connected)); } void ConfigureInputPlayer::ConnectPlayer(bool connected) { diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 4a3dea2a5..54a46827f 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -551,13 +551,14 @@ void GMainWindow::InitializeWidgets() { dock_status_button->setObjectName(QStringLiteral("TogglableStatusBarButton")); dock_status_button->setFocusPolicy(Qt::NoFocus); connect(dock_status_button, &QPushButton::clicked, [&] { - Settings::values.use_docked_mode = !Settings::values.use_docked_mode; - dock_status_button->setChecked(Settings::values.use_docked_mode); - OnDockedModeChanged(!Settings::values.use_docked_mode, Settings::values.use_docked_mode); + Settings::values.use_docked_mode.SetValue(!Settings::values.use_docked_mode.GetValue()); + dock_status_button->setChecked(Settings::values.use_docked_mode.GetValue()); + OnDockedModeChanged(!Settings::values.use_docked_mode.GetValue(), + Settings::values.use_docked_mode.GetValue()); }); dock_status_button->setText(tr("DOCK")); dock_status_button->setCheckable(true); - dock_status_button->setChecked(Settings::values.use_docked_mode); + dock_status_button->setChecked(Settings::values.use_docked_mode.GetValue()); statusBar()->insertPermanentWidget(0, dock_status_button); // Setup ASync button @@ -796,10 +797,11 @@ void GMainWindow::InitializeHotkeys() { }); connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Change Docked Mode"), this), &QShortcut::activated, this, [&] { - Settings::values.use_docked_mode = !Settings::values.use_docked_mode; - OnDockedModeChanged(!Settings::values.use_docked_mode, - Settings::values.use_docked_mode); - dock_status_button->setChecked(Settings::values.use_docked_mode); + Settings::values.use_docked_mode.SetValue( + !Settings::values.use_docked_mode.GetValue()); + OnDockedModeChanged(!Settings::values.use_docked_mode.GetValue(), + Settings::values.use_docked_mode.GetValue()); + dock_status_button->setChecked(Settings::values.use_docked_mode.GetValue()); }); connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Mute Audio"), this), &QShortcut::activated, this, @@ -2405,7 +2407,8 @@ void GMainWindow::MigrateConfigFiles() { const QStringList config_dir_list = config_dir.entryList(QStringList(QStringLiteral("*.ini"))); Common::FS::CreateFullPath(fmt::format("{}custom" DIR_SEP, config_dir_str)); - for (QStringList::const_iterator it = config_dir_list.constBegin(); it != config_dir_list.constEnd(); ++it) { + for (QStringList::const_iterator it = config_dir_list.constBegin(); + it != config_dir_list.constEnd(); ++it) { const auto filename = it->toStdString(); if (filename.find_first_not_of("0123456789abcdefACBDEF", 0) < 16) { continue; @@ -2477,7 +2480,7 @@ void GMainWindow::UpdateStatusBar() { } void GMainWindow::UpdateStatusButtons() { - dock_status_button->setChecked(Settings::values.use_docked_mode); + dock_status_button->setChecked(Settings::values.use_docked_mode.GetValue()); multicore_status_button->setChecked(Settings::values.use_multi_core.GetValue()); Settings::values.use_asynchronous_gpu_emulation.SetValue( Settings::values.use_asynchronous_gpu_emulation.GetValue() || diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp index 334038ef9..feee02fcd 100644 --- a/src/yuzu_cmd/config.cpp +++ b/src/yuzu_cmd/config.cpp @@ -228,24 +228,24 @@ static const std::array keyboard_mods{ void Config::ReadValues() { // Controls - for (std::size_t p = 0; p < Settings::values.players.size(); ++p) { + for (std::size_t p = 0; p < Settings::values.players.GetValue().size(); ++p) { const auto group = fmt::format("ControlsP{}", p); for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]); - Settings::values.players[p].buttons[i] = + Settings::values.players.GetValue()[p].buttons[i] = sdl2_config->Get(group, Settings::NativeButton::mapping[i], default_param); - if (Settings::values.players[p].buttons[i].empty()) - Settings::values.players[p].buttons[i] = default_param; + if (Settings::values.players.GetValue()[p].buttons[i].empty()) + Settings::values.players.GetValue()[p].buttons[i] = default_param; } for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { std::string default_param = InputCommon::GenerateAnalogParamFromKeys( default_analogs[i][0], default_analogs[i][1], default_analogs[i][2], default_analogs[i][3], default_analogs[i][4], 0.5f); - Settings::values.players[p].analogs[i] = + Settings::values.players.GetValue()[p].analogs[i] = sdl2_config->Get(group, Settings::NativeAnalog::mapping[i], default_param); - if (Settings::values.players[p].analogs[i].empty()) - Settings::values.players[p].analogs[i] = default_param; + if (Settings::values.players.GetValue()[p].analogs[i].empty()) + Settings::values.players.GetValue()[p].analogs[i] = default_param; } } @@ -288,10 +288,10 @@ void Config::ReadValues() { Settings::values.debug_pad_analogs[i] = default_param; } - Settings::values.vibration_enabled = - sdl2_config->GetBoolean("ControlsGeneral", "vibration_enabled", true); - Settings::values.motion_enabled = - sdl2_config->GetBoolean("ControlsGeneral", "motion_enabled", true); + Settings::values.vibration_enabled.SetValue( + sdl2_config->GetBoolean("ControlsGeneral", "vibration_enabled", true)); + Settings::values.motion_enabled.SetValue( + sdl2_config->GetBoolean("ControlsGeneral", "motion_enabled", true)); Settings::values.touchscreen.enabled = sdl2_config->GetBoolean("ControlsGeneral", "touch_enabled", true); Settings::values.touchscreen.device = @@ -343,7 +343,8 @@ void Config::ReadValues() { Settings::values.gamecard_path = sdl2_config->Get("Data Storage", "gamecard_path", ""); // System - Settings::values.use_docked_mode = sdl2_config->GetBoolean("System", "use_docked_mode", false); + Settings::values.use_docked_mode.SetValue( + sdl2_config->GetBoolean("System", "use_docked_mode", false)); const auto size = sdl2_config->GetInteger("System", "users_size", 0); Settings::values.current_user = std::clamp( diff --git a/src/yuzu_tester/config.cpp b/src/yuzu_tester/config.cpp index bc273fb51..3a8a333f0 100644 --- a/src/yuzu_tester/config.cpp +++ b/src/yuzu_tester/config.cpp @@ -47,13 +47,13 @@ bool Config::LoadINI(const std::string& default_contents, bool retry) { void Config::ReadValues() { // Controls - for (std::size_t p = 0; p < Settings::values.players.size(); ++p) { + for (std::size_t p = 0; p < Settings::values.players.GetValue().size(); ++p) { for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { - Settings::values.players[p].buttons[i] = ""; + Settings::values.players.GetValue()[p].buttons[i] = ""; } for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { - Settings::values.players[p].analogs[i] = ""; + Settings::values.players.GetValue()[p].analogs[i] = ""; } } @@ -75,8 +75,8 @@ void Config::ReadValues() { Settings::values.debug_pad_analogs[i] = ""; } - Settings::values.vibration_enabled = true; - Settings::values.motion_enabled = true; + Settings::values.vibration_enabled.SetValue(true); + Settings::values.motion_enabled.SetValue(true); Settings::values.touchscreen.enabled = ""; Settings::values.touchscreen.device = ""; Settings::values.touchscreen.finger = 0; @@ -84,8 +84,8 @@ void Config::ReadValues() { Settings::values.touchscreen.diameter_x = 15; Settings::values.touchscreen.diameter_y = 15; - Settings::values.use_docked_mode = - sdl2_config->GetBoolean("Controls", "use_docked_mode", false); + Settings::values.use_docked_mode.SetValue( + sdl2_config->GetBoolean("Controls", "use_docked_mode", false)); // Data Storage Settings::values.use_virtual_sd = From ceb7b11f166a4e59945a6296d364980c37ca681e Mon Sep 17 00:00:00 2001 From: Morph <39850852+Morph1984@users.noreply.github.com> Date: Mon, 28 Sep 2020 10:27:29 -0400 Subject: [PATCH 10/37] configure_input_player: Change "Defaults" button behavior RestoreDefaults() now restores the selected devices' mappings using UpdateMappingWithDefaults(). This allows us to move the keyboard mapping from RestoreDefaults() to UpdateMappingWithDefaults(). --- src/input_common/main.cpp | 8 --- .../configuration/configure_input_player.cpp | 57 ++++++++++--------- 2 files changed, 30 insertions(+), 35 deletions(-) diff --git a/src/input_common/main.cpp b/src/input_common/main.cpp index 354c734fe..b438482cc 100644 --- a/src/input_common/main.cpp +++ b/src/input_common/main.cpp @@ -96,10 +96,6 @@ struct InputSubsystem::Impl { if (!params.Has("class") || params.Get("class", "") == "any") { return {}; } - if (params.Get("class", "") == "keyboard") { - // TODO consider returning the SDL key codes for the default keybindings - return {}; - } if (params.Get("class", "") == "gcpad") { return gcadapter->GetAnalogMappingForDevice(params); } @@ -116,10 +112,6 @@ struct InputSubsystem::Impl { if (!params.Has("class") || params.Get("class", "") == "any") { return {}; } - if (params.Get("class", "") == "keyboard") { - // TODO consider returning the SDL key codes for the default keybindings - return {}; - } if (params.Get("class", "") == "gcpad") { return gcadapter->GetButtonMappingForDevice(params); } diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp index 213a76224..460ff08a4 100644 --- a/src/yuzu/configuration/configure_input_player.cpp +++ b/src/yuzu/configuration/configure_input_player.cpp @@ -693,32 +693,7 @@ void ConfigureInputPlayer::UpdateInputDeviceCombobox() { } void ConfigureInputPlayer::RestoreDefaults() { - // Reset Buttons - for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; ++button_id) { - buttons_param[button_id] = Common::ParamPackage{ - InputCommon::GenerateKeyboardParam(Config::default_buttons[button_id])}; - } - - // Reset Analogs and Modifier Buttons - for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; ++analog_id) { - for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; ++sub_button_id) { - Common::ParamPackage params{InputCommon::GenerateKeyboardParam( - Config::default_analogs[analog_id][sub_button_id])}; - SetAnalogParam(params, analogs_param[analog_id], analog_sub_buttons[sub_button_id]); - } - - analogs_param[analog_id].Set( - "modifier", InputCommon::GenerateKeyboardParam(Config::default_stick_mod[analog_id])); - } - - for (int motion_id = 0; motion_id < Settings::NativeMotion::NumMotions; ++motion_id) { - motions_param[motion_id] = Common::ParamPackage{ - InputCommon::GenerateKeyboardParam(Config::default_motions[motion_id])}; - } - - UpdateUI(); - UpdateInputDeviceCombobox(); - ui->comboControllerType->setCurrentIndex(0); + UpdateMappingWithDefaults(); } void ConfigureInputPlayer::ClearAll() { @@ -951,9 +926,37 @@ void ConfigureInputPlayer::UpdateMotionButtons() { } void ConfigureInputPlayer::UpdateMappingWithDefaults() { - if (ui->comboDevices->currentIndex() < 2) { + if (ui->comboDevices->currentIndex() == 0) { return; } + + if (ui->comboDevices->currentIndex() == 1) { + // Reset keyboard bindings + for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; ++button_id) { + buttons_param[button_id] = Common::ParamPackage{ + InputCommon::GenerateKeyboardParam(Config::default_buttons[button_id])}; + } + for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; ++analog_id) { + for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; ++sub_button_id) { + Common::ParamPackage params{InputCommon::GenerateKeyboardParam( + Config::default_analogs[analog_id][sub_button_id])}; + SetAnalogParam(params, analogs_param[analog_id], analog_sub_buttons[sub_button_id]); + } + + analogs_param[analog_id].Set("modifier", InputCommon::GenerateKeyboardParam( + Config::default_stick_mod[analog_id])); + } + + for (int motion_id = 0; motion_id < Settings::NativeMotion::NumMotions; ++motion_id) { + motions_param[motion_id] = Common::ParamPackage{ + InputCommon::GenerateKeyboardParam(Config::default_motions[motion_id])}; + } + + UpdateUI(); + return; + } + + // Reset controller bindings const auto& device = input_devices[ui->comboDevices->currentIndex()]; auto button_mapping = input_subsystem->GetButtonMappingForDevice(device); auto analog_mapping = input_subsystem->GetAnalogMappingForDevice(device); From 0a966e2cac40477ffc62f747f7703afbf3bfda2a Mon Sep 17 00:00:00 2001 From: Morph <39850852+Morph1984@users.noreply.github.com> Date: Tue, 6 Oct 2020 04:35:07 -0400 Subject: [PATCH 11/37] controllers/npad: Add DeviceHandle struct A DeviceHandle describes a vibration device or six-axis sensor based on the npad type, npad id, and device index/position --- src/core/hle/service/hid/controllers/npad.h | 87 +++++++++++++-------- 1 file changed, 55 insertions(+), 32 deletions(-) diff --git a/src/core/hle/service/hid/controllers/npad.h b/src/core/hle/service/hid/controllers/npad.h index fd5c5a6eb..8dabae6e3 100644 --- a/src/core/hle/service/hid/controllers/npad.h +++ b/src/core/hle/service/hid/controllers/npad.h @@ -39,6 +39,61 @@ public: // Called when input devices should be loaded void OnLoadInputDevices() override; + enum class NPadControllerType { + None, + ProController, + Handheld, + JoyDual, + JoyLeft, + JoyRight, + Pokeball, + }; + + enum class NpadType : u8 { + ProController = 3, + Handheld = 4, + JoyconDual = 5, + JoyconLeft = 6, + JoyconRight = 7, + Pokeball = 9, + }; + + enum class DeviceIndex : u8 { + Left = 0, + Right = 1, + None = 2, + }; + + enum class GyroscopeZeroDriftMode : u32 { + Loose = 0, + Standard = 1, + Tight = 2, + }; + + enum class NpadHoldType : u64 { + Vertical = 0, + Horizontal = 1, + }; + + enum class NPadAssignments : u32 { + Dual = 0, + Single = 1, + }; + + enum class NpadHandheldActivationMode : u64 { + Dual = 0, + Single = 1, + None = 2, + }; + + struct DeviceHandle { + NpadType npad_type{}; + u8 npad_id{}; + DeviceIndex device_index{}; + INSERT_PADDING_BYTES(1); + }; + static_assert(sizeof(DeviceHandle) == 4, "DeviceHandle is an invalid size"); + struct NPadType { union { u32_le raw{}; @@ -62,38 +117,6 @@ public: }; static_assert(sizeof(Vibration) == 0x10, "Vibration is an invalid size"); - enum class GyroscopeZeroDriftMode : u32 { - Loose = 0, - Standard = 1, - Tight = 2, - }; - - enum class NpadHoldType : u64 { - Vertical = 0, - Horizontal = 1, - }; - - enum class NPadAssignments : u32_le { - Dual = 0, - Single = 1, - }; - - enum class NpadHandheldActivationMode : u64 { - Dual = 0, - Single = 1, - None = 2, - }; - - enum class NPadControllerType { - None, - ProController, - Handheld, - JoyDual, - JoyLeft, - JoyRight, - Pokeball, - }; - struct LedPattern { explicit LedPattern(u64 light1, u64 light2, u64 light3, u64 light4) { position1.Assign(light1); From 428ce8ec2909100fb8dde520254508a274c448ea Mon Sep 17 00:00:00 2001 From: Morph <39850852+Morph1984@users.noreply.github.com> Date: Thu, 8 Oct 2020 01:08:37 -0400 Subject: [PATCH 12/37] controllers/npad: Rename NPadType to NpadStyleSet This more accurately represents the underlying type and avoids confusion with NpadType --- src/core/hle/service/am/applets/controller.cpp | 2 +- src/core/hle/service/hid/controllers/npad.cpp | 4 ++-- src/core/hle/service/hid/controllers/npad.h | 12 ++++++------ 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/core/hle/service/am/applets/controller.cpp b/src/core/hle/service/am/applets/controller.cpp index 43b79412e..3ca63f020 100644 --- a/src/core/hle/service/am/applets/controller.cpp +++ b/src/core/hle/service/am/applets/controller.cpp @@ -25,7 +25,7 @@ namespace Service::AM::Applets { static Core::Frontend::ControllerParameters ConvertToFrontendParameters( ControllerSupportArgPrivate private_arg, ControllerSupportArgHeader header, bool enable_text, std::vector identification_colors, std::vector text) { - HID::Controller_NPad::NPadType npad_style_set; + HID::Controller_NPad::NpadStyleSet npad_style_set; npad_style_set.raw = private_arg.style_set; return { diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp index 15d5fa6e8..8181bddbc 100644 --- a/src/core/hle/service/hid/controllers/npad.cpp +++ b/src/core/hle/service/hid/controllers/npad.cpp @@ -619,11 +619,11 @@ void Controller_NPad::OnMotionUpdate(const Core::Timing::CoreTiming& core_timing shared_memory_entries.size() * sizeof(NPadEntry)); } -void Controller_NPad::SetSupportedStyleSet(NPadType style_set) { +void Controller_NPad::SetSupportedStyleSet(NpadStyleSet style_set) { style.raw = style_set.raw; } -Controller_NPad::NPadType Controller_NPad::GetSupportedStyleSet() const { +Controller_NPad::NpadStyleSet Controller_NPad::GetSupportedStyleSet() const { return style; } diff --git a/src/core/hle/service/hid/controllers/npad.h b/src/core/hle/service/hid/controllers/npad.h index 8dabae6e3..fed8425b1 100644 --- a/src/core/hle/service/hid/controllers/npad.h +++ b/src/core/hle/service/hid/controllers/npad.h @@ -94,7 +94,7 @@ public: }; static_assert(sizeof(DeviceHandle) == 4, "DeviceHandle is an invalid size"); - struct NPadType { + struct NpadStyleSet { union { u32_le raw{}; @@ -107,7 +107,7 @@ public: BitField<6, 1, u32> pokeball; // TODO(ogniK): Confirm when possible }; }; - static_assert(sizeof(NPadType) == 4, "NPadType is an invalid size"); + static_assert(sizeof(NpadStyleSet) == 4, "NpadStyleSet is an invalid size"); struct Vibration { f32 amp_low; @@ -133,8 +133,8 @@ public: }; }; - void SetSupportedStyleSet(NPadType style_set); - NPadType GetSupportedStyleSet() const; + void SetSupportedStyleSet(NpadStyleSet style_set); + NpadStyleSet GetSupportedStyleSet() const; void SetSupportedNPadIdTypes(u8* data, std::size_t length); void GetSupportedNpadIdTypes(u32* data, std::size_t max_length); @@ -347,7 +347,7 @@ private: }; struct NPadEntry { - NPadType joy_styles; + NpadStyleSet joy_styles; NPadAssignments pad_assignment; ColorReadError single_color_error; @@ -391,7 +391,7 @@ private: u32 press_state{}; - NPadType style{}; + NpadStyleSet style{}; std::array shared_memory_entries{}; using ButtonArray = std::array< std::array, Settings::NativeButton::NUM_BUTTONS_HID>, From 16e2e1c45f8d510946645199140b20e1bde3e7da Mon Sep 17 00:00:00 2001 From: Morph <39850852+Morph1984@users.noreply.github.com> Date: Tue, 6 Oct 2020 04:50:15 -0400 Subject: [PATCH 13/37] hid: Stub InitializeVibrationDevice --- src/core/hle/service/hid/hid.cpp | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index fb57dec02..86b83dcc6 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp @@ -140,15 +140,23 @@ void IAppletResource::UpdateMotion(std::uintptr_t user_data, std::chrono::nanose class IActiveVibrationDeviceList final : public ServiceFramework { public: IActiveVibrationDeviceList() : ServiceFramework("IActiveVibrationDeviceList") { + // clang-format off static const FunctionInfo functions[] = { - {0, &IActiveVibrationDeviceList::ActivateVibrationDevice, "ActivateVibrationDevice"}, + {0, &IActiveVibrationDeviceList::InitializeVibrationDevice, "InitializeVibrationDevice"}, }; + // clang-format on + RegisterHandlers(functions); } private: - void ActivateVibrationDevice(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_HID, "(STUBBED) called"); + void InitializeVibrationDevice(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto vibration_device_handle{rp.PopRaw()}; + + LOG_WARNING(Service_HID, "(STUBBED) called, npad_type={}, npad_id={}, device_index={}", + vibration_device_handle.npad_type, vibration_device_handle.npad_id, + vibration_device_handle.device_index); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); From b92bf51ae1a08eca22bf0ce98b234692b9d59207 Mon Sep 17 00:00:00 2001 From: Morph <39850852+Morph1984@users.noreply.github.com> Date: Tue, 6 Oct 2020 05:08:52 -0400 Subject: [PATCH 14/37] hid: Implement GetVibrationDeviceInfo The first u32 describes the vibration device type which is a Linear Resonant Actuator used in Nintendo Switch controller hardware. The second u32 describes the vibration device position, in this case distinguishing between left and right vibration actuators. Pro Controllers have 2 LRAs each that can vibrate independently of each other, which means they have 2 distinct vibration device handles to distinguish between the two actuators. Similarly for joycons, the left joycon can be distinguished from the right joycon through the vibration device handle since each joycon has 1 LRA. --- src/core/hle/service/hid/hid.cpp | 26 +++++++++++++++++++++++--- src/core/hle/service/hid/hid.h | 16 ++++++++++++++++ 2 files changed, 39 insertions(+), 3 deletions(-) diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index 86b83dcc6..993738f36 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp @@ -924,12 +924,32 @@ void Hid::GetActualVibrationValue(Kernel::HLERequestContext& ctx) { } void Hid::GetVibrationDeviceInfo(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_HID, "called"); + IPC::RequestParser rp{ctx}; + const auto vibration_device_handle{rp.PopRaw()}; + + VibrationDeviceInfo vibration_device_info; + + vibration_device_info.type = VibrationDeviceType::LinearResonantActuator; + + switch (vibration_device_handle.device_index) { + case Controller_NPad::DeviceIndex::Left: + vibration_device_info.position = VibrationDevicePosition::Left; + break; + case Controller_NPad::DeviceIndex::Right: + vibration_device_info.position = VibrationDevicePosition::Right; + break; + case Controller_NPad::DeviceIndex::None: + default: + vibration_device_info.position = VibrationDevicePosition::None; + break; + } + + LOG_DEBUG(Service_HID, "called, vibration_device_type={}, vibration_device_position={}", + vibration_device_info.type, vibration_device_info.position); IPC::ResponseBuilder rb{ctx, 4}; rb.Push(RESULT_SUCCESS); - rb.Push(1); - rb.Push(0); + rb.PushRaw(vibration_device_info); } void Hid::CreateActiveVibrationDeviceList(Kernel::HLERequestContext& ctx) { diff --git a/src/core/hle/service/hid/hid.h b/src/core/hle/service/hid/hid.h index fd0372b18..2f7483170 100644 --- a/src/core/hle/service/hid/hid.h +++ b/src/core/hle/service/hid/hid.h @@ -146,6 +146,22 @@ private: void SetIsPalmaAllConnectable(Kernel::HLERequestContext& ctx); void SetPalmaBoostMode(Kernel::HLERequestContext& ctx); + enum class VibrationDeviceType : u32 { + LinearResonantActuator = 1, + }; + + enum class VibrationDevicePosition : u32 { + None = 0, + Left = 1, + Right = 2, + }; + + struct VibrationDeviceInfo { + VibrationDeviceType type{}; + VibrationDevicePosition position{}; + }; + static_assert(sizeof(VibrationDeviceInfo) == 0x8, "VibrationDeviceInfo has incorrect size."); + std::shared_ptr applet_resource; Core::System& system; }; From e3c274998603b1bf3aa00a79474f5796c7dadac6 Mon Sep 17 00:00:00 2001 From: Morph <39850852+Morph1984@users.noreply.github.com> Date: Tue, 6 Oct 2020 07:00:18 -0400 Subject: [PATCH 15/37] hid: Reorder all HID commands Reorders all HID commands in command id order. --- src/core/frontend/applets/controller.cpp | 4 +- src/core/hle/service/hid/controllers/npad.cpp | 34 +- src/core/hle/service/hid/controllers/npad.h | 14 +- src/core/hle/service/hid/hid.cpp | 443 +++++++++--------- src/core/hle/service/hid/hid.h | 22 +- 5 files changed, 266 insertions(+), 251 deletions(-) diff --git a/src/core/frontend/applets/controller.cpp b/src/core/frontend/applets/controller.cpp index 1ac2fb80c..03bbedf8b 100644 --- a/src/core/frontend/applets/controller.cpp +++ b/src/core/frontend/applets/controller.cpp @@ -33,13 +33,13 @@ void DefaultControllerApplet::ReconfigureControllers(std::function callb parameters.enable_single_mode ? 1 : parameters.min_players; // Disconnect Handheld first. - npad.DisconnectNPadAtIndex(8); + npad.DisconnectNpadAtIndex(8); // Deduce the best configuration based on the input parameters. for (std::size_t index = 0; index < players.size() - 2; ++index) { // First, disconnect all controllers regardless of the value of keep_controllers_connected. // This makes it easy to connect the desired controllers. - npad.DisconnectNPadAtIndex(index); + npad.DisconnectNpadAtIndex(index); // Only connect the minimum number of required players. if (index >= min_supported_players) { diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp index 8181bddbc..b330c5e40 100644 --- a/src/core/hle/service/hid/controllers/npad.cpp +++ b/src/core/hle/service/hid/controllers/npad.cpp @@ -139,7 +139,7 @@ void Controller_NPad::InitNewlyAddedController(std::size_t controller_idx) { controller.properties.is_vertical.Assign(1); controller.properties.use_plus.Assign(1); controller.properties.use_minus.Assign(1); - controller.pad_assignment = NPadAssignments::Single; + controller.pad_assignment = NpadAssignments::Single; break; case NPadControllerType::Handheld: controller.joy_styles.handheld.Assign(1); @@ -147,7 +147,7 @@ void Controller_NPad::InitNewlyAddedController(std::size_t controller_idx) { controller.properties.is_vertical.Assign(1); controller.properties.use_plus.Assign(1); controller.properties.use_minus.Assign(1); - controller.pad_assignment = NPadAssignments::Dual; + controller.pad_assignment = NpadAssignments::Dual; break; case NPadControllerType::JoyDual: controller.joy_styles.joycon_dual.Assign(1); @@ -156,26 +156,26 @@ void Controller_NPad::InitNewlyAddedController(std::size_t controller_idx) { controller.properties.is_vertical.Assign(1); controller.properties.use_plus.Assign(1); controller.properties.use_minus.Assign(1); - controller.pad_assignment = NPadAssignments::Dual; + controller.pad_assignment = NpadAssignments::Dual; break; case NPadControllerType::JoyLeft: controller.joy_styles.joycon_left.Assign(1); controller.device_type.joycon_left.Assign(1); controller.properties.is_horizontal.Assign(1); controller.properties.use_minus.Assign(1); - controller.pad_assignment = NPadAssignments::Single; + controller.pad_assignment = NpadAssignments::Single; break; case NPadControllerType::JoyRight: controller.joy_styles.joycon_right.Assign(1); controller.device_type.joycon_right.Assign(1); controller.properties.is_horizontal.Assign(1); controller.properties.use_plus.Assign(1); - controller.pad_assignment = NPadAssignments::Single; + controller.pad_assignment = NpadAssignments::Single; break; case NPadControllerType::Pokeball: controller.joy_styles.pokeball.Assign(1); controller.device_type.pokeball.Assign(1); - controller.pad_assignment = NPadAssignments::Single; + controller.pad_assignment = NpadAssignments::Single; break; } @@ -202,7 +202,7 @@ void Controller_NPad::InitNewlyAddedController(std::size_t controller_idx) { void Controller_NPad::OnInit() { auto& kernel = system.Kernel(); - for (std::size_t i = 0; i < styleset_changed_events.size(); i++) { + for (std::size_t i = 0; i < styleset_changed_events.size(); ++i) { styleset_changed_events[i] = Kernel::WritableEvent::CreateEventPair( kernel, fmt::format("npad:NpadStyleSetChanged_{}", i)); } @@ -357,7 +357,7 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* if (!IsControllerActivated()) { return; } - for (std::size_t i = 0; i < shared_memory_entries.size(); i++) { + for (std::size_t i = 0; i < shared_memory_entries.size(); ++i) { auto& npad = shared_memory_entries[i]; const std::array controller_npads{&npad.main_controller_states, &npad.handheld_states, @@ -499,7 +499,7 @@ void Controller_NPad::OnMotionUpdate(const Core::Timing::CoreTiming& core_timing if (!IsControllerActivated()) { return; } - for (std::size_t i = 0; i < shared_memory_entries.size(); i++) { + for (std::size_t i = 0; i < shared_memory_entries.size(); ++i) { auto& npad = shared_memory_entries[i]; const auto& controller_type = connected_controllers[i].type; @@ -627,7 +627,7 @@ Controller_NPad::NpadStyleSet Controller_NPad::GetSupportedStyleSet() const { return style; } -void Controller_NPad::SetSupportedNPadIdTypes(u8* data, std::size_t length) { +void Controller_NPad::SetSupportedNpadIdTypes(u8* data, std::size_t length) { ASSERT(length > 0 && (length % sizeof(u32)) == 0); supported_npad_id_types.clear(); supported_npad_id_types.resize(length / sizeof(u32)); @@ -639,7 +639,7 @@ void Controller_NPad::GetSupportedNpadIdTypes(u32* data, std::size_t max_length) std::memcpy(data, supported_npad_id_types.data(), supported_npad_id_types.size()); } -std::size_t Controller_NPad::GetSupportedNPadIdTypesSize() const { +std::size_t Controller_NPad::GetSupportedNpadIdTypesSize() const { return supported_npad_id_types.size(); } @@ -659,7 +659,7 @@ Controller_NPad::NpadHandheldActivationMode Controller_NPad::GetNpadHandheldActi return handheld_activation_mode; } -void Controller_NPad::SetNpadMode(u32 npad_id, NPadAssignments assignment_mode) { +void Controller_NPad::SetNpadMode(u32 npad_id, NpadAssignments assignment_mode) { const std::size_t npad_index = NPadIdToIndex(npad_id); ASSERT(npad_index < shared_memory_entries.size()); if (shared_memory_entries[npad_index].pad_assignment != assignment_mode) { @@ -714,7 +714,7 @@ void Controller_NPad::AddNewControllerAt(NPadControllerType controller, std::siz void Controller_NPad::UpdateControllerAt(NPadControllerType controller, std::size_t npad_index, bool connected) { if (!connected) { - DisconnectNPadAtIndex(npad_index); + DisconnectNpadAtIndex(npad_index); return; } @@ -734,11 +734,11 @@ void Controller_NPad::UpdateControllerAt(NPadControllerType controller, std::siz InitNewlyAddedController(npad_index); } -void Controller_NPad::DisconnectNPad(u32 npad_id) { - DisconnectNPadAtIndex(NPadIdToIndex(npad_id)); +void Controller_NPad::DisconnectNpad(u32 npad_id) { + DisconnectNpadAtIndex(NPadIdToIndex(npad_id)); } -void Controller_NPad::DisconnectNPadAtIndex(std::size_t npad_index) { +void Controller_NPad::DisconnectNpadAtIndex(std::size_t npad_index) { Settings::values.players.GetValue()[npad_index].connected = false; connected_controllers[npad_index].is_connected = false; @@ -777,7 +777,7 @@ void Controller_NPad::MergeSingleJoyAsDualJoy(u32 npad_id_1, u32 npad_id_2) { (connected_controllers[npad_index_2].type == NPadControllerType::JoyLeft && connected_controllers[npad_index_1].type == NPadControllerType::JoyRight)) { // Disconnect the joycon at the second id and connect the dual joycon at the first index. - DisconnectNPad(npad_id_2); + DisconnectNpad(npad_id_2); AddNewControllerAt(NPadControllerType::JoyDual, npad_index_1); } } diff --git a/src/core/hle/service/hid/controllers/npad.h b/src/core/hle/service/hid/controllers/npad.h index fed8425b1..35dd2bf5f 100644 --- a/src/core/hle/service/hid/controllers/npad.h +++ b/src/core/hle/service/hid/controllers/npad.h @@ -75,7 +75,7 @@ public: Horizontal = 1, }; - enum class NPadAssignments : u32 { + enum class NpadAssignments : u32 { Dual = 0, Single = 1, }; @@ -136,9 +136,9 @@ public: void SetSupportedStyleSet(NpadStyleSet style_set); NpadStyleSet GetSupportedStyleSet() const; - void SetSupportedNPadIdTypes(u8* data, std::size_t length); + void SetSupportedNpadIdTypes(u8* data, std::size_t length); void GetSupportedNpadIdTypes(u32* data, std::size_t max_length); - std::size_t GetSupportedNPadIdTypesSize() const; + std::size_t GetSupportedNpadIdTypesSize() const; void SetHoldType(NpadHoldType joy_hold_type); NpadHoldType GetHoldType() const; @@ -146,7 +146,7 @@ public: void SetNpadHandheldActivationMode(NpadHandheldActivationMode activation_mode); NpadHandheldActivationMode GetNpadHandheldActivationMode() const; - void SetNpadMode(u32 npad_id, NPadAssignments assignment_mode); + void SetNpadMode(u32 npad_id, NpadAssignments assignment_mode); void VibrateController(const std::vector& controllers, const std::vector& vibrations); @@ -161,8 +161,8 @@ public: // Adds a new controller at an index with connection status. void UpdateControllerAt(NPadControllerType controller, std::size_t npad_index, bool connected); - void DisconnectNPad(u32 npad_id); - void DisconnectNPadAtIndex(std::size_t index); + void DisconnectNpad(u32 npad_id); + void DisconnectNpadAtIndex(std::size_t index); void SetGyroscopeZeroDriftMode(GyroscopeZeroDriftMode drift_mode); GyroscopeZeroDriftMode GetGyroscopeZeroDriftMode() const; @@ -348,7 +348,7 @@ private: struct NPadEntry { NpadStyleSet joy_styles; - NPadAssignments pad_assignment; + NpadAssignments pad_assignment; ColorReadError single_color_error; ControllerColor single_color; diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index 993738f36..9a631008f 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp @@ -328,15 +328,74 @@ void Hid::CreateAppletResource(Kernel::HLERequestContext& ctx) { rb.PushIpcInterface(applet_resource); } +void Hid::ActivateDebugPad(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto applet_resource_user_id{rp.Pop()}; + + applet_resource->ActivateController(HidController::DebugPad); + + LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); +} + +void Hid::ActivateTouchScreen(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto applet_resource_user_id{rp.Pop()}; + + applet_resource->ActivateController(HidController::Touchscreen); + + LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); +} + +void Hid::ActivateMouse(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto applet_resource_user_id{rp.Pop()}; + + applet_resource->ActivateController(HidController::Mouse); + + LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); +} + +void Hid::ActivateKeyboard(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto applet_resource_user_id{rp.Pop()}; + + applet_resource->ActivateController(HidController::Keyboard); + + LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); +} + +void Hid::SendKeyboardLockKeyEvent(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto flags{rp.Pop()}; + + LOG_WARNING(Service_HID, "(STUBBED) called. flags={}", flags); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); +} + void Hid::ActivateXpad(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto basic_xpad_id{rp.Pop()}; const auto applet_resource_user_id{rp.Pop()}; + applet_resource->ActivateController(HidController::XPad); + LOG_DEBUG(Service_HID, "called, basic_xpad_id={}, applet_resource_user_id={}", basic_xpad_id, applet_resource_user_id); - applet_resource->ActivateController(HidController::XPad); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); } @@ -356,7 +415,9 @@ void Hid::ActivateSixAxisSensor(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto handle{rp.Pop()}; const auto applet_resource_user_id{rp.Pop()}; + applet_resource->GetController(HidController::NPad).SetSixAxisEnabled(true); + LOG_DEBUG(Service_HID, "called, handle={}, applet_resource_user_id={}", handle, applet_resource_user_id); @@ -368,6 +429,7 @@ void Hid::DeactivateSixAxisSensor(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto handle{rp.Pop()}; const auto applet_resource_user_id{rp.Pop()}; + applet_resource->GetController(HidController::NPad).SetSixAxisEnabled(false); LOG_DEBUG(Service_HID, "called, handle={}, applet_resource_user_id={}", handle, @@ -377,86 +439,6 @@ void Hid::DeactivateSixAxisSensor(Kernel::HLERequestContext& ctx) { rb.Push(RESULT_SUCCESS); } -void Hid::ActivateDebugPad(Kernel::HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto applet_resource_user_id{rp.Pop()}; - - LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); - - applet_resource->ActivateController(HidController::DebugPad); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(RESULT_SUCCESS); -} - -void Hid::ActivateTouchScreen(Kernel::HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto applet_resource_user_id{rp.Pop()}; - - LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); - - applet_resource->ActivateController(HidController::Touchscreen); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(RESULT_SUCCESS); -} - -void Hid::ActivateMouse(Kernel::HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto applet_resource_user_id{rp.Pop()}; - - LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); - - applet_resource->ActivateController(HidController::Mouse); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(RESULT_SUCCESS); -} - -void Hid::ActivateKeyboard(Kernel::HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto applet_resource_user_id{rp.Pop()}; - - LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); - - applet_resource->ActivateController(HidController::Keyboard); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(RESULT_SUCCESS); -} - -void Hid::SendKeyboardLockKeyEvent(Kernel::HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto flags{rp.Pop()}; - LOG_WARNING(Service_HID, "(STUBBED) called. flags={}", flags); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(RESULT_SUCCESS); -} - -void Hid::ActivateGesture(Kernel::HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto unknown{rp.Pop()}; - const auto applet_resource_user_id{rp.Pop()}; - - LOG_DEBUG(Service_HID, "called, unknown={}, applet_resource_user_id={}", unknown, - applet_resource_user_id); - - applet_resource->ActivateController(HidController::Gesture); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(RESULT_SUCCESS); -} - -void Hid::ActivateNpadWithRevision(Kernel::HLERequestContext& ctx) { - // Should have no effect with how our npad sets up the data - IPC::RequestParser rp{ctx}; - const auto unknown{rp.Pop()}; - const auto applet_resource_user_id{rp.Pop()}; - - LOG_DEBUG(Service_HID, "called, unknown={}, applet_resource_user_id={}", unknown, - applet_resource_user_id); - - applet_resource->ActivateController(HidController::NPad); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(RESULT_SUCCESS); -} - void Hid::StartSixAxisSensor(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto handle{rp.Pop()}; @@ -487,8 +469,8 @@ void Hid::EnableSixAxisSensorFusion(Kernel::HLERequestContext& ctx) { const auto handle{rp.Pop()}; const auto applet_resource_user_id{rp.Pop()}; - LOG_WARNING(Service_HID, "(STUBBED) called, handle={}, applet_resource_user_id={}", handle, - applet_resource_user_id); + LOG_WARNING(Service_HID, "(STUBBED) called, handle={}, applet_resource_user_id={}", enable, + handle, applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); @@ -501,7 +483,7 @@ void Hid::SetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) { const auto applet_resource_user_id{rp.Pop()}; applet_resource->GetController(HidController::NPad) - .SetGyroscopeZeroDriftMode(Controller_NPad::GyroscopeZeroDriftMode{drift_mode}); + .SetGyroscopeZeroDriftMode({drift_mode}); LOG_DEBUG(Service_HID, "called, handle={}, drift_mode={}, applet_resource_user_id={}", handle, drift_mode, applet_resource_user_id); @@ -520,9 +502,8 @@ void Hid::GetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); - rb.Push( - static_cast(applet_resource->GetController(HidController::NPad) - .GetGyroscopeZeroDriftMode())); + rb.PushEnum(applet_resource->GetController(HidController::NPad) + .GetGyroscopeZeroDriftMode()); } void Hid::ResetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) { @@ -554,15 +535,29 @@ void Hid::IsSixAxisSensorAtRest(Kernel::HLERequestContext& ctx) { .IsSixAxisSensorAtRest()); } +void Hid::ActivateGesture(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto unknown{rp.Pop()}; + const auto applet_resource_user_id{rp.Pop()}; + + applet_resource->ActivateController(HidController::Gesture); + + LOG_DEBUG(Service_HID, "called, unknown={}, applet_resource_user_id={}", unknown, + applet_resource_user_id); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); +} + void Hid::SetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto supported_styleset{rp.Pop()}; - LOG_DEBUG(Service_HID, "called, supported_styleset={}", supported_styleset); - applet_resource->GetController(HidController::NPad) .SetSupportedStyleSet({supported_styleset}); + LOG_DEBUG(Service_HID, "called, supported_styleset={}", supported_styleset); + IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); } @@ -573,21 +568,22 @@ void Hid::GetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); - auto& controller = applet_resource->GetController(HidController::NPad); - IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); - rb.Push(controller.GetSupportedStyleSet().raw); + rb.Push(applet_resource->GetController(HidController::NPad) + .GetSupportedStyleSet() + .raw); } void Hid::SetSupportedNpadIdType(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto applet_resource_user_id{rp.Pop()}; + applet_resource->GetController(HidController::NPad) + .SetSupportedNpadIdTypes(ctx.ReadBuffer().data(), ctx.GetReadBufferSize()); + LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); - applet_resource->GetController(HidController::NPad) - .SetSupportedNPadIdTypes(ctx.ReadBuffer().data(), ctx.GetReadBufferSize()); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); } @@ -596,22 +592,24 @@ void Hid::ActivateNpad(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto applet_resource_user_id{rp.Pop()}; + applet_resource->ActivateController(HidController::NPad); + LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - applet_resource->ActivateController(HidController::NPad); } void Hid::DeactivateNpad(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto applet_resource_user_id{rp.Pop()}; + applet_resource->DeactivateController(HidController::NPad); + LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - applet_resource->DeactivateController(HidController::NPad); } void Hid::AcquireNpadStyleSetUpdateEventHandle(Kernel::HLERequestContext& ctx) { @@ -634,10 +632,11 @@ void Hid::DisconnectNpad(Kernel::HLERequestContext& ctx) { const auto npad_id{rp.Pop()}; const auto applet_resource_user_id{rp.Pop()}; + applet_resource->GetController(HidController::NPad).DisconnectNpad(npad_id); + LOG_DEBUG(Service_HID, "called, npad_id={}, applet_resource_user_id={}", npad_id, applet_resource_user_id); - applet_resource->GetController(HidController::NPad).DisconnectNPad(npad_id); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); } @@ -650,9 +649,24 @@ void Hid::GetPlayerLedPattern(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 4}; rb.Push(RESULT_SUCCESS); - rb.PushRaw(applet_resource->GetController(HidController::NPad) - .GetLedPattern(npad_id) - .raw); + rb.Push(applet_resource->GetController(HidController::NPad) + .GetLedPattern(npad_id) + .raw); +} + +void Hid::ActivateNpadWithRevision(Kernel::HLERequestContext& ctx) { + // Should have no effect with how our npad sets up the data + IPC::RequestParser rp{ctx}; + const auto unknown{rp.Pop()}; + const auto applet_resource_user_id{rp.Pop()}; + + applet_resource->ActivateController(HidController::NPad); + + LOG_DEBUG(Service_HID, "called, unknown={}, applet_resource_user_id={}", unknown, + applet_resource_user_id); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); } void Hid::SetNpadJoyHoldType(Kernel::HLERequestContext& ctx) { @@ -660,12 +674,11 @@ void Hid::SetNpadJoyHoldType(Kernel::HLERequestContext& ctx) { const auto applet_resource_user_id{rp.Pop()}; const auto hold_type{rp.Pop()}; + applet_resource->GetController(HidController::NPad).SetHoldType({hold_type}); + LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}, hold_type={}", applet_resource_user_id, hold_type); - auto& controller = applet_resource->GetController(HidController::NPad); - controller.SetHoldType(Controller_NPad::NpadHoldType{hold_type}); - IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); } @@ -676,10 +689,9 @@ void Hid::GetNpadJoyHoldType(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); - const auto& controller = applet_resource->GetController(HidController::NPad); IPC::ResponseBuilder rb{ctx, 4}; rb.Push(RESULT_SUCCESS); - rb.Push(static_cast(controller.GetHoldType())); + rb.PushEnum(applet_resource->GetController(HidController::NPad).GetHoldType())); } void Hid::SetNpadJoyAssignmentModeSingleByDefault(Kernel::HLERequestContext& ctx) { @@ -687,12 +699,12 @@ void Hid::SetNpadJoyAssignmentModeSingleByDefault(Kernel::HLERequestContext& ctx const auto npad_id{rp.Pop()}; const auto applet_resource_user_id{rp.Pop()}; + applet_resource->GetController(HidController::NPad) + .SetNpadMode(npad_id, Controller_NPad::NpadAssignments::Single); + LOG_WARNING(Service_HID, "(STUBBED) called, npad_id={}, applet_resource_user_id={}", npad_id, applet_resource_user_id); - auto& controller = applet_resource->GetController(HidController::NPad); - controller.SetNpadMode(npad_id, Controller_NPad::NPadAssignments::Single); - IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); } @@ -704,13 +716,13 @@ void Hid::SetNpadJoyAssignmentModeSingle(Kernel::HLERequestContext& ctx) { const auto applet_resource_user_id{rp.Pop()}; const auto npad_joy_device_type{rp.Pop()}; + applet_resource->GetController(HidController::NPad) + .SetNpadMode(npad_id, Controller_NPad::NpadAssignments::Single); + LOG_WARNING(Service_HID, "(STUBBED) called, npad_id={}, applet_resource_user_id={}, npad_joy_device_type={}", npad_id, applet_resource_user_id, npad_joy_device_type); - auto& controller = applet_resource->GetController(HidController::NPad); - controller.SetNpadMode(npad_id, Controller_NPad::NPadAssignments::Single); - IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); } @@ -720,12 +732,12 @@ void Hid::SetNpadJoyAssignmentModeDual(Kernel::HLERequestContext& ctx) { const auto npad_id{rp.Pop()}; const auto applet_resource_user_id{rp.Pop()}; + applet_resource->GetController(HidController::NPad) + .SetNpadMode(npad_id, Controller_NPad::NpadAssignments::Dual); + LOG_DEBUG(Service_HID, "called, npad_id={}, applet_resource_user_id={}", npad_id, applet_resource_user_id); - auto& controller = applet_resource->GetController(HidController::NPad); - controller.SetNpadMode(npad_id, Controller_NPad::NPadAssignments::Dual); - IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); } @@ -736,12 +748,12 @@ void Hid::MergeSingleJoyAsDualJoy(Kernel::HLERequestContext& ctx) { const auto npad_id_2{rp.Pop()}; const auto applet_resource_user_id{rp.Pop()}; + applet_resource->GetController(HidController::NPad) + .MergeSingleJoyAsDualJoy(npad_id_1, npad_id_2); + LOG_DEBUG(Service_HID, "called, npad_id_1={}, npad_id_2={}, applet_resource_user_id={}", npad_id_1, npad_id_2, applet_resource_user_id); - auto& controller = applet_resource->GetController(HidController::NPad); - controller.MergeSingleJoyAsDualJoy(npad_id_1, npad_id_2); - IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); } @@ -750,9 +762,9 @@ void Hid::StartLrAssignmentMode(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto applet_resource_user_id{rp.Pop()}; + applet_resource->GetController(HidController::NPad).StartLRAssignmentMode(); + LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); - auto& controller = applet_resource->GetController(HidController::NPad); - controller.StartLRAssignmentMode(); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); @@ -762,9 +774,9 @@ void Hid::StopLrAssignmentMode(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto applet_resource_user_id{rp.Pop()}; + applet_resource->GetController(HidController::NPad).StopLRAssignmentMode(); + LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); - auto& controller = applet_resource->GetController(HidController::NPad); - controller.StopLRAssignmentMode(); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); @@ -775,12 +787,12 @@ void Hid::SetNpadHandheldActivationMode(Kernel::HLERequestContext& ctx) { const auto applet_resource_user_id{rp.Pop()}; const auto mode{rp.Pop()}; + applet_resource->GetController(HidController::NPad) + .SetNpadHandheldActivationMode({mode}); + LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}, mode={}", applet_resource_user_id, mode); - applet_resource->GetController(HidController::NPad) - .SetNpadHandheldActivationMode(Controller_NPad::NpadHandheldActivationMode{mode}); - IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); } @@ -793,23 +805,24 @@ void Hid::GetNpadHandheldActivationMode(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 4}; rb.Push(RESULT_SUCCESS); - rb.Push( - static_cast(applet_resource->GetController(HidController::NPad) - .GetNpadHandheldActivationMode())); + rb.PushEnum(applet_resource->GetController(HidController::NPad) + .GetNpadHandheldActivationMode()); } void Hid::SwapNpadAssignment(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - const auto npad_1{rp.Pop()}; - const auto npad_2{rp.Pop()}; + const auto npad_id_1{rp.Pop()}; + const auto npad_id_2{rp.Pop()}; const auto applet_resource_user_id{rp.Pop()}; - LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}, npad_1={}, npad_2={}", - applet_resource_user_id, npad_1, npad_2); + const bool res = applet_resource->GetController(HidController::NPad) + .SwapNpadAssignment(npad_id_1, npad_id_2); + + LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}, npad_id_1={}, npad_id_2={}", + applet_resource_user_id, npad_id_1, npad_id_2); - auto& controller = applet_resource->GetController(HidController::NPad); IPC::ResponseBuilder rb{ctx, 2}; - if (controller.SwapNpadAssignment(npad_1, npad_2)) { + if (res) { rb.Push(RESULT_SUCCESS); } else { LOG_ERROR(Service_HID, "Npads are not connected!"); @@ -825,11 +838,10 @@ void Hid::IsUnintendedHomeButtonInputProtectionEnabled(Kernel::HLERequestContext LOG_WARNING(Service_HID, "(STUBBED) called, npad_id={}, applet_resource_user_id={}", npad_id, applet_resource_user_id); - auto& controller = applet_resource->GetController(HidController::NPad); - IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); - rb.Push(controller.IsUnintendedHomeButtonInputProtectionEnabled(npad_id)); + rb.Push(applet_resource->GetController(HidController::NPad) + .IsUnintendedHomeButtonInputProtectionEnabled(npad_id)); } void Hid::EnableUnintendedHomeButtonInputProtection(Kernel::HLERequestContext& ctx) { @@ -838,91 +850,19 @@ void Hid::EnableUnintendedHomeButtonInputProtection(Kernel::HLERequestContext& c const auto npad_id{rp.Pop()}; const auto applet_resource_user_id{rp.Pop()}; + applet_resource->GetController(HidController::NPad) + .SetUnintendedHomeButtonInputProtectionEnabled(unintended_home_button_input_protection, + npad_id); + LOG_WARNING(Service_HID, "(STUBBED) called, unintended_home_button_input_protection={}, npad_id={}," "applet_resource_user_id={}", - npad_id, unintended_home_button_input_protection, applet_resource_user_id); - - auto& controller = applet_resource->GetController(HidController::NPad); - controller.SetUnintendedHomeButtonInputProtectionEnabled( - unintended_home_button_input_protection, npad_id); + unintended_home_button_input_protection, npad_id, applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); } -void Hid::BeginPermitVibrationSession(Kernel::HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto applet_resource_user_id{rp.Pop()}; - - LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); - - applet_resource->GetController(HidController::NPad).SetVibrationEnabled(true); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(RESULT_SUCCESS); -} - -void Hid::EndPermitVibrationSession(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_HID, "called"); - - applet_resource->GetController(HidController::NPad).SetVibrationEnabled(false); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(RESULT_SUCCESS); -} - -void Hid::SendVibrationValue(Kernel::HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto controller{rp.Pop()}; - const auto vibration_values{rp.PopRaw()}; - const auto applet_resource_user_id{rp.Pop()}; - - LOG_DEBUG(Service_HID, "called, controller={}, applet_resource_user_id={}", controller, - applet_resource_user_id); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(RESULT_SUCCESS); - - applet_resource->GetController(HidController::NPad) - .VibrateController({controller}, {vibration_values}); -} - -void Hid::SendVibrationValues(Kernel::HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto applet_resource_user_id{rp.Pop()}; - - LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); - - const auto controllers = ctx.ReadBuffer(0); - const auto vibrations = ctx.ReadBuffer(1); - - std::vector controller_list(controllers.size() / sizeof(u32)); - std::vector vibration_list(vibrations.size() / - sizeof(Controller_NPad::Vibration)); - - std::memcpy(controller_list.data(), controllers.data(), controllers.size()); - std::memcpy(vibration_list.data(), vibrations.data(), vibrations.size()); - - applet_resource->GetController(HidController::NPad) - .VibrateController(controller_list, vibration_list); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(RESULT_SUCCESS); -} - -void Hid::GetActualVibrationValue(Kernel::HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto controller_id{rp.Pop()}; - const auto applet_resource_user_id{rp.Pop()}; - - LOG_DEBUG(Service_HID, "called, controller_id={}, applet_resource_user_id={}", controller_id, - applet_resource_user_id); - - IPC::ResponseBuilder rb{ctx, 6}; - rb.Push(RESULT_SUCCESS); - rb.PushRaw( - applet_resource->GetController(HidController::NPad).GetLastVibration()); -} - void Hid::GetVibrationDeviceInfo(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto vibration_device_handle{rp.PopRaw()}; @@ -949,7 +889,37 @@ void Hid::GetVibrationDeviceInfo(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 4}; rb.Push(RESULT_SUCCESS); - rb.PushRaw(vibration_device_info); + rb.PushRaw(vibration_device_info); +} + +void Hid::SendVibrationValue(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto controller{rp.Pop()}; + const auto vibration_values{rp.PopRaw()}; + const auto applet_resource_user_id{rp.Pop()}; + + applet_resource->GetController(HidController::NPad) + .VibrateController({controller}, {vibration_values}); + + LOG_DEBUG(Service_HID, "called, controller={}, applet_resource_user_id={}", controller, + applet_resource_user_id); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); +} + +void Hid::GetActualVibrationValue(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto controller_id{rp.Pop()}; + const auto applet_resource_user_id{rp.Pop()}; + + LOG_DEBUG(Service_HID, "called, controller_id={}, applet_resource_user_id={}", controller_id, + applet_resource_user_id); + + IPC::ResponseBuilder rb{ctx, 6}; + rb.Push(RESULT_SUCCESS); + rb.PushRaw( + applet_resource->GetController(HidController::NPad).GetLastVibration()); } void Hid::CreateActiveVibrationDeviceList(Kernel::HLERequestContext& ctx) { @@ -963,6 +933,7 @@ void Hid::CreateActiveVibrationDeviceList(Kernel::HLERequestContext& ctx) { void Hid::PermitVibration(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto can_vibrate{rp.Pop()}; + Settings::values.vibration_enabled.SetValue(can_vibrate); LOG_DEBUG(Service_HID, "called, can_vibrate={}", can_vibrate); @@ -979,6 +950,50 @@ void Hid::IsVibrationPermitted(Kernel::HLERequestContext& ctx) { rb.Push(Settings::values.vibration_enabled.GetValue()); } +void Hid::SendVibrationValues(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto applet_resource_user_id{rp.Pop()}; + + const auto controllers = ctx.ReadBuffer(0); + const auto vibrations = ctx.ReadBuffer(1); + + std::vector controller_list(controllers.size() / sizeof(u32)); + std::vector vibration_list(vibrations.size() / + sizeof(Controller_NPad::Vibration)); + + std::memcpy(controller_list.data(), controllers.data(), controllers.size()); + std::memcpy(vibration_list.data(), vibrations.data(), vibrations.size()); + + applet_resource->GetController(HidController::NPad) + .VibrateController(controller_list, vibration_list); + + LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); +} + +void Hid::BeginPermitVibrationSession(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto applet_resource_user_id{rp.Pop()}; + + applet_resource->GetController(HidController::NPad).SetVibrationEnabled(true); + + LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); +} + +void Hid::EndPermitVibrationSession(Kernel::HLERequestContext& ctx) { + applet_resource->GetController(HidController::NPad).SetVibrationEnabled(false); + + LOG_DEBUG(Service_HID, "called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); +} + void Hid::ActivateConsoleSixAxisSensor(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto applet_resource_user_id{rp.Pop()}; diff --git a/src/core/hle/service/hid/hid.h b/src/core/hle/service/hid/hid.h index 2f7483170..57bc93778 100644 --- a/src/core/hle/service/hid/hid.h +++ b/src/core/hle/service/hid/hid.h @@ -86,17 +86,15 @@ public: private: void CreateAppletResource(Kernel::HLERequestContext& ctx); - void ActivateXpad(Kernel::HLERequestContext& ctx); - void GetXpadIDs(Kernel::HLERequestContext& ctx); - void ActivateSixAxisSensor(Kernel::HLERequestContext& ctx); - void DeactivateSixAxisSensor(Kernel::HLERequestContext& ctx); void ActivateDebugPad(Kernel::HLERequestContext& ctx); void ActivateTouchScreen(Kernel::HLERequestContext& ctx); void ActivateMouse(Kernel::HLERequestContext& ctx); void ActivateKeyboard(Kernel::HLERequestContext& ctx); void SendKeyboardLockKeyEvent(Kernel::HLERequestContext& ctx); - void ActivateGesture(Kernel::HLERequestContext& ctx); - void ActivateNpadWithRevision(Kernel::HLERequestContext& ctx); + void ActivateXpad(Kernel::HLERequestContext& ctx); + void GetXpadIDs(Kernel::HLERequestContext& ctx); + void ActivateSixAxisSensor(Kernel::HLERequestContext& ctx); + void DeactivateSixAxisSensor(Kernel::HLERequestContext& ctx); void StartSixAxisSensor(Kernel::HLERequestContext& ctx); void StopSixAxisSensor(Kernel::HLERequestContext& ctx); void EnableSixAxisSensorFusion(Kernel::HLERequestContext& ctx); @@ -104,6 +102,7 @@ private: void GetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx); void ResetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx); void IsSixAxisSensorAtRest(Kernel::HLERequestContext& ctx); + void ActivateGesture(Kernel::HLERequestContext& ctx); void SetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx); void GetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx); void SetSupportedNpadIdType(Kernel::HLERequestContext& ctx); @@ -112,6 +111,7 @@ private: void AcquireNpadStyleSetUpdateEventHandle(Kernel::HLERequestContext& ctx); void DisconnectNpad(Kernel::HLERequestContext& ctx); void GetPlayerLedPattern(Kernel::HLERequestContext& ctx); + void ActivateNpadWithRevision(Kernel::HLERequestContext& ctx); void SetNpadJoyHoldType(Kernel::HLERequestContext& ctx); void GetNpadJoyHoldType(Kernel::HLERequestContext& ctx); void SetNpadJoyAssignmentModeSingleByDefault(Kernel::HLERequestContext& ctx); @@ -125,15 +125,15 @@ private: void SwapNpadAssignment(Kernel::HLERequestContext& ctx); void IsUnintendedHomeButtonInputProtectionEnabled(Kernel::HLERequestContext& ctx); void EnableUnintendedHomeButtonInputProtection(Kernel::HLERequestContext& ctx); - void BeginPermitVibrationSession(Kernel::HLERequestContext& ctx); - void EndPermitVibrationSession(Kernel::HLERequestContext& ctx); - void SendVibrationValue(Kernel::HLERequestContext& ctx); - void SendVibrationValues(Kernel::HLERequestContext& ctx); - void GetActualVibrationValue(Kernel::HLERequestContext& ctx); void GetVibrationDeviceInfo(Kernel::HLERequestContext& ctx); + void SendVibrationValue(Kernel::HLERequestContext& ctx); + void GetActualVibrationValue(Kernel::HLERequestContext& ctx); void CreateActiveVibrationDeviceList(Kernel::HLERequestContext& ctx); void PermitVibration(Kernel::HLERequestContext& ctx); void IsVibrationPermitted(Kernel::HLERequestContext& ctx); + void SendVibrationValues(Kernel::HLERequestContext& ctx); + void BeginPermitVibrationSession(Kernel::HLERequestContext& ctx); + void EndPermitVibrationSession(Kernel::HLERequestContext& ctx); void ActivateConsoleSixAxisSensor(Kernel::HLERequestContext& ctx); void StartConsoleSixAxisSensor(Kernel::HLERequestContext& ctx); void StopConsoleSixAxisSensor(Kernel::HLERequestContext& ctx); From 31de52513eef90ed19583ab1ffe3e74ebe95e45d Mon Sep 17 00:00:00 2001 From: Morph <39850852+Morph1984@users.noreply.github.com> Date: Wed, 7 Oct 2020 00:37:13 -0400 Subject: [PATCH 16/37] hid: Pop a struct of parameters instead of popping individual parameters Some parameters need to be doubleword aligned due to the presence of the applet_resource_user_id. Previously, this value was invalid in many commands where it was not doubleword aligned when popped. --- src/core/hle/service/hid/hid.cpp | 340 +++++++++++++++++++++---------- 1 file changed, 237 insertions(+), 103 deletions(-) diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index 9a631008f..fdac598f0 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp @@ -388,13 +388,18 @@ void Hid::SendKeyboardLockKeyEvent(Kernel::HLERequestContext& ctx) { void Hid::ActivateXpad(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - const auto basic_xpad_id{rp.Pop()}; - const auto applet_resource_user_id{rp.Pop()}; + struct Parameters { + u32 basic_xpad_id{}; + INSERT_PADDING_WORDS(1); + u64 applet_resource_user_id{}; + }; + + const auto parameters{rp.PopRaw()}; applet_resource->ActivateController(HidController::XPad); - LOG_DEBUG(Service_HID, "called, basic_xpad_id={}, applet_resource_user_id={}", basic_xpad_id, - applet_resource_user_id); + LOG_DEBUG(Service_HID, "called, basic_xpad_id={}, applet_resource_user_id={}", + parameters.basic_xpad_id, parameters.applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); @@ -413,13 +418,20 @@ void Hid::GetXpadIDs(Kernel::HLERequestContext& ctx) { void Hid::ActivateSixAxisSensor(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - const auto handle{rp.Pop()}; - const auto applet_resource_user_id{rp.Pop()}; + struct Parameters { + Controller_NPad::DeviceHandle sixaxis_handle{}; + INSERT_PADDING_WORDS(1); + u64 applet_resource_user_id{}; + }; + + const auto parameters{rp.PopRaw()}; applet_resource->GetController(HidController::NPad).SetSixAxisEnabled(true); - LOG_DEBUG(Service_HID, "called, handle={}, applet_resource_user_id={}", handle, - applet_resource_user_id); + LOG_DEBUG(Service_HID, + "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", + parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, + parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); @@ -427,13 +439,20 @@ void Hid::ActivateSixAxisSensor(Kernel::HLERequestContext& ctx) { void Hid::DeactivateSixAxisSensor(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - const auto handle{rp.Pop()}; - const auto applet_resource_user_id{rp.Pop()}; + struct Parameters { + Controller_NPad::DeviceHandle sixaxis_handle{}; + INSERT_PADDING_WORDS(1); + u64 applet_resource_user_id{}; + }; + + const auto parameters{rp.PopRaw()}; applet_resource->GetController(HidController::NPad).SetSixAxisEnabled(false); - LOG_DEBUG(Service_HID, "called, handle={}, applet_resource_user_id={}", handle, - applet_resource_user_id); + LOG_DEBUG(Service_HID, + "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", + parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, + parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); @@ -441,11 +460,20 @@ void Hid::DeactivateSixAxisSensor(Kernel::HLERequestContext& ctx) { void Hid::StartSixAxisSensor(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - const auto handle{rp.Pop()}; - const auto applet_resource_user_id{rp.Pop()}; + struct Parameters { + Controller_NPad::DeviceHandle sixaxis_handle{}; + INSERT_PADDING_WORDS(1); + u64 applet_resource_user_id{}; + }; - LOG_WARNING(Service_HID, "(STUBBED) called, handle={}, applet_resource_user_id={}", handle, - applet_resource_user_id); + const auto parameters{rp.PopRaw()}; + + applet_resource->GetController(HidController::NPad).SetSixAxisEnabled(true); + + LOG_DEBUG(Service_HID, + "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", + parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, + parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); @@ -453,11 +481,20 @@ void Hid::StartSixAxisSensor(Kernel::HLERequestContext& ctx) { void Hid::StopSixAxisSensor(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - const auto handle{rp.Pop()}; - const auto applet_resource_user_id{rp.Pop()}; + struct Parameters { + Controller_NPad::DeviceHandle sixaxis_handle{}; + INSERT_PADDING_WORDS(1); + u64 applet_resource_user_id{}; + }; - LOG_WARNING(Service_HID, "(STUBBED) called, handle={}, applet_resource_user_id={}", handle, - applet_resource_user_id); + const auto parameters{rp.PopRaw()}; + + applet_resource->GetController(HidController::NPad).SetSixAxisEnabled(false); + + LOG_DEBUG(Service_HID, + "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", + parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, + parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); @@ -465,12 +502,21 @@ void Hid::StopSixAxisSensor(Kernel::HLERequestContext& ctx) { void Hid::EnableSixAxisSensorFusion(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - [[maybe_unused]] const auto enable{rp.Pop()}; - const auto handle{rp.Pop()}; - const auto applet_resource_user_id{rp.Pop()}; + struct Parameters { + bool enable_sixaxis_sensor_fusion{}; + INSERT_PADDING_BYTES(3); + Controller_NPad::DeviceHandle sixaxis_handle{}; + u64 applet_resource_user_id{}; + }; - LOG_WARNING(Service_HID, "(STUBBED) called, handle={}, applet_resource_user_id={}", enable, - handle, applet_resource_user_id); + const auto parameters{rp.PopRaw()}; + + LOG_WARNING(Service_HID, + "(STUBBED) called, enable_sixaxis_sensor_fusion={}, npad_type={}, npad_id={}, " + "device_index={}, applet_resource_user_id={}", + parameters.enable_sixaxis_sensor_fusion, parameters.sixaxis_handle.npad_type, + parameters.sixaxis_handle.npad_id, parameters.sixaxis_handle.device_index, + parameters.applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); @@ -478,14 +524,17 @@ void Hid::EnableSixAxisSensorFusion(Kernel::HLERequestContext& ctx) { void Hid::SetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - const auto handle{rp.Pop()}; - const auto drift_mode{rp.Pop()}; + const auto sixaxis_handle{rp.PopRaw()}; + const auto drift_mode{rp.PopEnum()}; const auto applet_resource_user_id{rp.Pop()}; applet_resource->GetController(HidController::NPad) - .SetGyroscopeZeroDriftMode({drift_mode}); + .SetGyroscopeZeroDriftMode(drift_mode); - LOG_DEBUG(Service_HID, "called, handle={}, drift_mode={}, applet_resource_user_id={}", handle, + LOG_DEBUG(Service_HID, + "called, npad_type={}, npad_id={}, device_index={}, drift_mode={}, " + "applet_resource_user_id={}", + sixaxis_handle.npad_type, sixaxis_handle.npad_id, sixaxis_handle.device_index, drift_mode, applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 2}; @@ -494,11 +543,18 @@ void Hid::SetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) { void Hid::GetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - const auto handle{rp.Pop()}; - const auto applet_resource_user_id{rp.Pop()}; + struct Parameters { + Controller_NPad::DeviceHandle sixaxis_handle{}; + INSERT_PADDING_WORDS(1); + u64 applet_resource_user_id{}; + }; - LOG_DEBUG(Service_HID, "called, handle={}, applet_resource_user_id={}", handle, - applet_resource_user_id); + const auto parameters{rp.PopRaw()}; + + LOG_DEBUG(Service_HID, + "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", + parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, + parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); @@ -508,14 +564,21 @@ void Hid::GetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) { void Hid::ResetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - const auto handle{rp.Pop()}; - const auto applet_resource_user_id{rp.Pop()}; + struct Parameters { + Controller_NPad::DeviceHandle sixaxis_handle{}; + INSERT_PADDING_WORDS(1); + u64 applet_resource_user_id{}; + }; + + const auto parameters{rp.PopRaw()}; applet_resource->GetController(HidController::NPad) .SetGyroscopeZeroDriftMode(Controller_NPad::GyroscopeZeroDriftMode::Standard); - LOG_DEBUG(Service_HID, "called, handle={}, applet_resource_user_id={}", handle, - applet_resource_user_id); + LOG_DEBUG(Service_HID, + "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", + parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, + parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); @@ -523,11 +586,18 @@ void Hid::ResetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) { void Hid::IsSixAxisSensorAtRest(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - const auto handle{rp.Pop()}; - const auto applet_resource_user_id{rp.Pop()}; + struct Parameters { + Controller_NPad::DeviceHandle sixaxis_handle{}; + INSERT_PADDING_WORDS(1); + u64 applet_resource_user_id{}; + }; - LOG_DEBUG(Service_HID, "called, handle={}, applet_resource_user_id={}", handle, - applet_resource_user_id); + const auto parameters{rp.PopRaw()}; + + LOG_DEBUG(Service_HID, + "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", + parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, + parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); @@ -537,13 +607,18 @@ void Hid::IsSixAxisSensorAtRest(Kernel::HLERequestContext& ctx) { void Hid::ActivateGesture(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - const auto unknown{rp.Pop()}; - const auto applet_resource_user_id{rp.Pop()}; + struct Parameters { + u32 unknown{}; + INSERT_PADDING_WORDS(1); + u64 applet_resource_user_id{}; + }; + + const auto parameters{rp.PopRaw()}; applet_resource->ActivateController(HidController::Gesture); - LOG_DEBUG(Service_HID, "called, unknown={}, applet_resource_user_id={}", unknown, - applet_resource_user_id); + LOG_DEBUG(Service_HID, "called, unknown={}, applet_resource_user_id={}", parameters.unknown, + parameters.applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); @@ -614,28 +689,39 @@ void Hid::DeactivateNpad(Kernel::HLERequestContext& ctx) { void Hid::AcquireNpadStyleSetUpdateEventHandle(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - const auto npad_id{rp.Pop()}; - const auto applet_resource_user_id{rp.Pop()}; - const auto unknown{rp.Pop()}; + struct Parameters { + u32 npad_id{}; + INSERT_PADDING_WORDS(1); + u64 applet_resource_user_id{}; + u64 unknown{}; + }; - LOG_DEBUG(Service_HID, "called, npad_id={}, applet_resource_user_id={}, unknown={}", npad_id, - applet_resource_user_id, unknown); + const auto parameters{rp.PopRaw()}; + + LOG_DEBUG(Service_HID, "called, npad_id={}, applet_resource_user_id={}, unknown={}", + parameters.npad_id, parameters.applet_resource_user_id, parameters.unknown); IPC::ResponseBuilder rb{ctx, 2, 1}; rb.Push(RESULT_SUCCESS); rb.PushCopyObjects(applet_resource->GetController(HidController::NPad) - .GetStyleSetChangedEvent(npad_id)); + .GetStyleSetChangedEvent(parameters.npad_id)); } void Hid::DisconnectNpad(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - const auto npad_id{rp.Pop()}; - const auto applet_resource_user_id{rp.Pop()}; + struct Parameters { + u32 npad_id{}; + INSERT_PADDING_WORDS(1); + u64 applet_resource_user_id{}; + }; - applet_resource->GetController(HidController::NPad).DisconnectNpad(npad_id); + const auto parameters{rp.PopRaw()}; - LOG_DEBUG(Service_HID, "called, npad_id={}, applet_resource_user_id={}", npad_id, - applet_resource_user_id); + applet_resource->GetController(HidController::NPad) + .DisconnectNpad(parameters.npad_id); + + LOG_DEBUG(Service_HID, "called, npad_id={}, applet_resource_user_id={}", parameters.npad_id, + parameters.applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); @@ -657,13 +743,18 @@ void Hid::GetPlayerLedPattern(Kernel::HLERequestContext& ctx) { void Hid::ActivateNpadWithRevision(Kernel::HLERequestContext& ctx) { // Should have no effect with how our npad sets up the data IPC::RequestParser rp{ctx}; - const auto unknown{rp.Pop()}; - const auto applet_resource_user_id{rp.Pop()}; + struct Parameters { + u32 unknown{}; + INSERT_PADDING_WORDS(1); + u64 applet_resource_user_id{}; + }; + + const auto parameters{rp.PopRaw()}; applet_resource->ActivateController(HidController::NPad); - LOG_DEBUG(Service_HID, "called, unknown={}, applet_resource_user_id={}", unknown, - applet_resource_user_id); + LOG_DEBUG(Service_HID, "called, unknown={}, applet_resource_user_id={}", parameters.unknown, + parameters.applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); @@ -672,9 +763,9 @@ void Hid::ActivateNpadWithRevision(Kernel::HLERequestContext& ctx) { void Hid::SetNpadJoyHoldType(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto applet_resource_user_id{rp.Pop()}; - const auto hold_type{rp.Pop()}; + const auto hold_type{rp.PopEnum()}; - applet_resource->GetController(HidController::NPad).SetHoldType({hold_type}); + applet_resource->GetController(HidController::NPad).SetHoldType(hold_type); LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}, hold_type={}", applet_resource_user_id, hold_type); @@ -691,19 +782,24 @@ void Hid::GetNpadJoyHoldType(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 4}; rb.Push(RESULT_SUCCESS); - rb.PushEnum(applet_resource->GetController(HidController::NPad).GetHoldType())); + rb.PushEnum(applet_resource->GetController(HidController::NPad).GetHoldType()); } void Hid::SetNpadJoyAssignmentModeSingleByDefault(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - const auto npad_id{rp.Pop()}; - const auto applet_resource_user_id{rp.Pop()}; + struct Parameters { + u32 npad_id{}; + INSERT_PADDING_WORDS(1); + u64 applet_resource_user_id{}; + }; + + const auto parameters{rp.PopRaw()}; applet_resource->GetController(HidController::NPad) - .SetNpadMode(npad_id, Controller_NPad::NpadAssignments::Single); + .SetNpadMode(parameters.npad_id, Controller_NPad::NpadAssignments::Single); - LOG_WARNING(Service_HID, "(STUBBED) called, npad_id={}, applet_resource_user_id={}", npad_id, - applet_resource_user_id); + LOG_WARNING(Service_HID, "(STUBBED) called, npad_id={}, applet_resource_user_id={}", + parameters.npad_id, parameters.applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); @@ -712,16 +808,22 @@ void Hid::SetNpadJoyAssignmentModeSingleByDefault(Kernel::HLERequestContext& ctx void Hid::SetNpadJoyAssignmentModeSingle(Kernel::HLERequestContext& ctx) { // TODO: Check the differences between this and SetNpadJoyAssignmentModeSingleByDefault IPC::RequestParser rp{ctx}; - const auto npad_id{rp.Pop()}; - const auto applet_resource_user_id{rp.Pop()}; - const auto npad_joy_device_type{rp.Pop()}; + struct Parameters { + u32 npad_id{}; + INSERT_PADDING_WORDS(1); + u64 applet_resource_user_id{}; + u64 npad_joy_device_type{}; + }; + + const auto parameters{rp.PopRaw()}; applet_resource->GetController(HidController::NPad) - .SetNpadMode(npad_id, Controller_NPad::NpadAssignments::Single); + .SetNpadMode(parameters.npad_id, Controller_NPad::NpadAssignments::Single); LOG_WARNING(Service_HID, "(STUBBED) called, npad_id={}, applet_resource_user_id={}, npad_joy_device_type={}", - npad_id, applet_resource_user_id, npad_joy_device_type); + parameters.npad_id, parameters.applet_resource_user_id, + parameters.npad_joy_device_type); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); @@ -729,14 +831,19 @@ void Hid::SetNpadJoyAssignmentModeSingle(Kernel::HLERequestContext& ctx) { void Hid::SetNpadJoyAssignmentModeDual(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - const auto npad_id{rp.Pop()}; - const auto applet_resource_user_id{rp.Pop()}; + struct Parameters { + u32 npad_id{}; + INSERT_PADDING_WORDS(1); + u64 applet_resource_user_id{}; + }; + + const auto parameters{rp.PopRaw()}; applet_resource->GetController(HidController::NPad) - .SetNpadMode(npad_id, Controller_NPad::NpadAssignments::Dual); + .SetNpadMode(parameters.npad_id, Controller_NPad::NpadAssignments::Dual); - LOG_DEBUG(Service_HID, "called, npad_id={}, applet_resource_user_id={}", npad_id, - applet_resource_user_id); + LOG_WARNING(Service_HID, "(STUBBED) called, npad_id={}, applet_resource_user_id={}", + parameters.npad_id, parameters.applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); @@ -785,13 +892,13 @@ void Hid::StopLrAssignmentMode(Kernel::HLERequestContext& ctx) { void Hid::SetNpadHandheldActivationMode(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto applet_resource_user_id{rp.Pop()}; - const auto mode{rp.Pop()}; + const auto activation_mode{rp.PopEnum()}; applet_resource->GetController(HidController::NPad) - .SetNpadHandheldActivationMode({mode}); + .SetNpadHandheldActivationMode(activation_mode); - LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}, mode={}", applet_resource_user_id, - mode); + LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}, activation_mode={}", + applet_resource_user_id, activation_mode); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); @@ -818,8 +925,8 @@ void Hid::SwapNpadAssignment(Kernel::HLERequestContext& ctx) { const bool res = applet_resource->GetController(HidController::NPad) .SwapNpadAssignment(npad_id_1, npad_id_2); - LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}, npad_id_1={}, npad_id_2={}", - applet_resource_user_id, npad_id_1, npad_id_2); + LOG_DEBUG(Service_HID, "called, npad_id_1={}, npad_id_2={}, applet_resource_user_id={}", + npad_id_1, npad_id_2, applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 2}; if (res) { @@ -832,32 +939,43 @@ void Hid::SwapNpadAssignment(Kernel::HLERequestContext& ctx) { void Hid::IsUnintendedHomeButtonInputProtectionEnabled(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - const auto npad_id{rp.Pop()}; - const auto applet_resource_user_id{rp.Pop()}; + struct Parameters { + u32 npad_id{}; + INSERT_PADDING_WORDS(1); + u64 applet_resource_user_id{}; + }; - LOG_WARNING(Service_HID, "(STUBBED) called, npad_id={}, applet_resource_user_id={}", npad_id, - applet_resource_user_id); + const auto parameters{rp.PopRaw()}; + + LOG_WARNING(Service_HID, "(STUBBED) called, npad_id={}, applet_resource_user_id={}", + parameters.npad_id, parameters.applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); rb.Push(applet_resource->GetController(HidController::NPad) - .IsUnintendedHomeButtonInputProtectionEnabled(npad_id)); + .IsUnintendedHomeButtonInputProtectionEnabled(parameters.npad_id)); } void Hid::EnableUnintendedHomeButtonInputProtection(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - const auto unintended_home_button_input_protection{rp.Pop()}; - const auto npad_id{rp.Pop()}; - const auto applet_resource_user_id{rp.Pop()}; + struct Parameters { + bool unintended_home_button_input_protection{}; + INSERT_PADDING_BYTES(3); + u32 npad_id{}; + u64 applet_resource_user_id{}; + }; + + const auto parameters{rp.PopRaw()}; applet_resource->GetController(HidController::NPad) - .SetUnintendedHomeButtonInputProtectionEnabled(unintended_home_button_input_protection, - npad_id); + .SetUnintendedHomeButtonInputProtectionEnabled( + parameters.unintended_home_button_input_protection, parameters.npad_id); LOG_WARNING(Service_HID, "(STUBBED) called, unintended_home_button_input_protection={}, npad_id={}," "applet_resource_user_id={}", - unintended_home_button_input_protection, npad_id, applet_resource_user_id); + parameters.unintended_home_button_input_protection, parameters.npad_id, + parameters.applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); @@ -1007,11 +1125,19 @@ void Hid::ActivateConsoleSixAxisSensor(Kernel::HLERequestContext& ctx) { void Hid::StartConsoleSixAxisSensor(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - const auto handle{rp.Pop()}; - const auto applet_resource_user_id{rp.Pop()}; + struct Parameters { + Controller_NPad::DeviceHandle sixaxis_handle{}; + INSERT_PADDING_WORDS(1); + u64 applet_resource_user_id{}; + }; - LOG_WARNING(Service_HID, "(STUBBED) called, handle={}, applet_resource_user_id={}", handle, - applet_resource_user_id); + const auto parameters{rp.PopRaw()}; + + LOG_WARNING( + Service_HID, + "(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", + parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, + parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); @@ -1019,11 +1145,19 @@ void Hid::StartConsoleSixAxisSensor(Kernel::HLERequestContext& ctx) { void Hid::StopConsoleSixAxisSensor(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - const auto handle{rp.Pop()}; - const auto applet_resource_user_id{rp.Pop()}; + struct Parameters { + Controller_NPad::DeviceHandle sixaxis_handle{}; + INSERT_PADDING_WORDS(1); + u64 applet_resource_user_id{}; + }; - LOG_WARNING(Service_HID, "(STUBBED) called, handle={}, applet_resource_user_id={}", handle, - applet_resource_user_id); + const auto parameters{rp.PopRaw()}; + + LOG_WARNING( + Service_HID, + "(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", + parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, + parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); From 07b81f57babcc7aa41fddfc892148688e841d96e Mon Sep 17 00:00:00 2001 From: Morph <39850852+Morph1984@users.noreply.github.com> Date: Wed, 7 Oct 2020 14:22:47 -0400 Subject: [PATCH 17/37] hid: Fix controller rumble based on new research This fixes the issue where rumble is only sent to the first controller. Now, individual controllers can receive their own rumble commands. --- src/core/hle/service/hid/controllers/npad.cpp | 45 +++++++++------ src/core/hle/service/hid/controllers/npad.h | 12 ++-- src/core/hle/service/hid/hid.cpp | 55 ++++++++++++------- 3 files changed, 69 insertions(+), 43 deletions(-) diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp index b330c5e40..8a5e5abcf 100644 --- a/src/core/hle/service/hid/controllers/npad.cpp +++ b/src/core/hle/service/hid/controllers/npad.cpp @@ -667,35 +667,44 @@ void Controller_NPad::SetNpadMode(u32 npad_id, NpadAssignments assignment_mode) } } -void Controller_NPad::VibrateController(const std::vector& controllers, - const std::vector& vibrations) { +void Controller_NPad::VibrateController(const std::vector& vibration_device_handles, + const std::vector& vibration_values) { LOG_TRACE(Service_HID, "called"); if (!Settings::values.vibration_enabled.GetValue() || !can_controllers_vibrate) { return; } - bool success = true; - for (std::size_t i = 0; i < controllers.size(); ++i) { - if (!connected_controllers[i].is_connected) { + + ASSERT_MSG(vibration_device_handles.size() == vibration_values.size(), + "The amount of device handles does not match with the amount of vibration values," + "this is undefined behavior!"); + + for (std::size_t i = 0; i < vibration_device_handles.size(); ++i) { + const auto npad_index = NPadIdToIndex(vibration_device_handles[i].npad_id); + const auto device_index = + static_cast(vibration_device_handles[i].device_index); + + if (!connected_controllers[npad_index].is_connected) { continue; } + using namespace Settings::NativeButton; - const auto& button_state = buttons[i]; - if (button_state[A - BUTTON_HID_BEGIN]) { - if (button_state[A - BUTTON_HID_BEGIN]->SetRumblePlay( - vibrations[0].amp_high, vibrations[0].amp_low, vibrations[0].freq_high, - vibrations[0].freq_low)) { - success = false; - } - } - } - if (success) { - last_processed_vibration = vibrations.back(); + const auto& button_state = buttons[npad_index]; + + // TODO: Vibrate left/right vibration motors independently if possible. + button_state[A - BUTTON_HID_BEGIN]->SetRumblePlay( + vibration_values[i].amp_high, vibration_values[i].amp_low, + vibration_values[i].freq_high, vibration_values[i].freq_low); + + latest_vibration_values[npad_index][device_index] = vibration_values[i]; } } -Controller_NPad::Vibration Controller_NPad::GetLastVibration() const { - return last_processed_vibration; +Controller_NPad::VibrationValue Controller_NPad::GetLastVibration( + const DeviceHandle& vibration_device_handle) const { + const auto npad_index = NPadIdToIndex(vibration_device_handle.npad_id); + const auto device_index = static_cast(vibration_device_handle.device_index); + return latest_vibration_values[npad_index][device_index]; } std::shared_ptr Controller_NPad::GetStyleSetChangedEvent(u32 npad_id) const { diff --git a/src/core/hle/service/hid/controllers/npad.h b/src/core/hle/service/hid/controllers/npad.h index 35dd2bf5f..c1b19103a 100644 --- a/src/core/hle/service/hid/controllers/npad.h +++ b/src/core/hle/service/hid/controllers/npad.h @@ -109,13 +109,13 @@ public: }; static_assert(sizeof(NpadStyleSet) == 4, "NpadStyleSet is an invalid size"); - struct Vibration { + struct VibrationValue { f32 amp_low; f32 freq_low; f32 amp_high; f32 freq_high; }; - static_assert(sizeof(Vibration) == 0x10, "Vibration is an invalid size"); + static_assert(sizeof(VibrationValue) == 0x10, "Vibration is an invalid size"); struct LedPattern { explicit LedPattern(u64 light1, u64 light2, u64 light3, u64 light4) { @@ -148,10 +148,10 @@ public: void SetNpadMode(u32 npad_id, NpadAssignments assignment_mode); - void VibrateController(const std::vector& controllers, - const std::vector& vibrations); + void VibrateController(const std::vector& vibration_device_handles, + const std::vector& vibration_values); - Vibration GetLastVibration() const; + VibrationValue GetLastVibration(const DeviceHandle& vibration_device_handle) const; std::shared_ptr GetStyleSetChangedEvent(u32 npad_id) const; void SignalStyleSetChangedEvent(u32 npad_id) const; @@ -410,7 +410,7 @@ private: NpadHandheldActivationMode handheld_activation_mode{NpadHandheldActivationMode::Dual}; // Each controller should have their own styleset changed event std::array styleset_changed_events; - Vibration last_processed_vibration{}; + std::array, 10> latest_vibration_values; std::array connected_controllers{}; std::array unintended_home_button_input_protection{}; GyroscopeZeroDriftMode gyroscope_zero_drift_mode{GyroscopeZeroDriftMode::Standard}; diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index fdac598f0..b0390bdb2 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp @@ -1012,15 +1012,23 @@ void Hid::GetVibrationDeviceInfo(Kernel::HLERequestContext& ctx) { void Hid::SendVibrationValue(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - const auto controller{rp.Pop()}; - const auto vibration_values{rp.PopRaw()}; - const auto applet_resource_user_id{rp.Pop()}; + struct Parameters { + Controller_NPad::DeviceHandle vibration_device_handle{}; + Controller_NPad::VibrationValue vibration_value{}; + INSERT_PADDING_WORDS(1); + u64 applet_resource_user_id{}; + }; + + const auto parameters{rp.PopRaw()}; applet_resource->GetController(HidController::NPad) - .VibrateController({controller}, {vibration_values}); + .VibrateController({parameters.vibration_device_handle}, {parameters.vibration_value}); - LOG_DEBUG(Service_HID, "called, controller={}, applet_resource_user_id={}", controller, - applet_resource_user_id); + LOG_DEBUG(Service_HID, + "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", + parameters.vibration_device_handle.npad_type, + parameters.vibration_device_handle.npad_id, + parameters.vibration_device_handle.device_index, parameters.applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); @@ -1028,16 +1036,24 @@ void Hid::SendVibrationValue(Kernel::HLERequestContext& ctx) { void Hid::GetActualVibrationValue(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - const auto controller_id{rp.Pop()}; - const auto applet_resource_user_id{rp.Pop()}; + struct Parameters { + Controller_NPad::DeviceHandle vibration_device_handle{}; + INSERT_PADDING_WORDS(1); + u64 applet_resource_user_id{}; + }; - LOG_DEBUG(Service_HID, "called, controller_id={}, applet_resource_user_id={}", controller_id, - applet_resource_user_id); + const auto parameters{rp.PopRaw()}; + + LOG_DEBUG(Service_HID, + "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", + parameters.vibration_device_handle.npad_type, + parameters.vibration_device_handle.npad_id, + parameters.vibration_device_handle.device_index, parameters.applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 6}; rb.Push(RESULT_SUCCESS); - rb.PushRaw( - applet_resource->GetController(HidController::NPad).GetLastVibration()); + rb.PushRaw(applet_resource->GetController(HidController::NPad) + .GetLastVibration(parameters.vibration_device_handle)); } void Hid::CreateActiveVibrationDeviceList(Kernel::HLERequestContext& ctx) { @@ -1072,18 +1088,19 @@ void Hid::SendVibrationValues(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto applet_resource_user_id{rp.Pop()}; - const auto controllers = ctx.ReadBuffer(0); + const auto handles = ctx.ReadBuffer(0); const auto vibrations = ctx.ReadBuffer(1); - std::vector controller_list(controllers.size() / sizeof(u32)); - std::vector vibration_list(vibrations.size() / - sizeof(Controller_NPad::Vibration)); + std::vector vibration_device_handles( + handles.size() / sizeof(Controller_NPad::DeviceHandle)); + std::vector vibration_values( + vibrations.size() / sizeof(Controller_NPad::VibrationValue)); - std::memcpy(controller_list.data(), controllers.data(), controllers.size()); - std::memcpy(vibration_list.data(), vibrations.data(), vibrations.size()); + std::memcpy(vibration_device_handles.data(), handles.data(), handles.size()); + std::memcpy(vibration_values.data(), vibrations.data(), vibrations.size()); applet_resource->GetController(HidController::NPad) - .VibrateController(controller_list, vibration_list); + .VibrateController(vibration_device_handles, vibration_values); LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); From e02ef3c3bed2f8e98ec9481cdea1a6253c34e9d1 Mon Sep 17 00:00:00 2001 From: Morph <39850852+Morph1984@users.noreply.github.com> Date: Thu, 8 Oct 2020 08:18:50 -0400 Subject: [PATCH 18/37] controllers/npad: Stop games from vibrating incorrect controllers Fixes vibration in 1-2 Switch and potentially other games where they would vibrate both players' joycons at the same time. --- src/core/hle/service/hid/controllers/npad.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp index 8a5e5abcf..f865e3f5f 100644 --- a/src/core/hle/service/hid/controllers/npad.cpp +++ b/src/core/hle/service/hid/controllers/npad.cpp @@ -688,6 +688,16 @@ void Controller_NPad::VibrateController(const std::vector& vibrati continue; } + // Some games try to send mismatched parameters in the device handle, block these. + if ((connected_controllers[npad_index].type == NPadControllerType::JoyLeft && + (vibration_device_handles[i].npad_type == NpadType::JoyconRight || + vibration_device_handles[i].device_index == DeviceIndex::Right)) || + (connected_controllers[npad_index].type == NPadControllerType::JoyRight && + (vibration_device_handles[i].npad_type == NpadType::JoyconLeft || + vibration_device_handles[i].device_index == DeviceIndex::Left))) { + continue; + } + using namespace Settings::NativeButton; const auto& button_state = buttons[npad_index]; From 652d6766d55acec6416dccb900a45c6660a86607 Mon Sep 17 00:00:00 2001 From: Morph <39850852+Morph1984@users.noreply.github.com> Date: Thu, 8 Oct 2020 23:43:07 -0400 Subject: [PATCH 19/37] configure_input: Hook up the vibration percentage spinbox This allows setting the vibration strength percentage anywhere from 1% to 100%. Also hooks up the remaining motion button and checkbox in the Controller Applet. --- src/core/hle/service/hid/controllers/npad.cpp | 3 ++- src/core/settings.cpp | 1 + src/core/settings.h | 1 + src/yuzu/applets/controller.cpp | 4 ++++ src/yuzu/applets/controller.ui | 2 +- src/yuzu/configuration/config.cpp | 4 ++++ src/yuzu/configuration/configure_input.cpp | 2 ++ src/yuzu/configuration/configure_input.ui | 2 +- src/yuzu_cmd/config.cpp | 2 ++ src/yuzu_cmd/default_ini.h | 7 +++++++ src/yuzu_tester/config.cpp | 1 + 11 files changed, 26 insertions(+), 3 deletions(-) diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp index f865e3f5f..924f209c0 100644 --- a/src/core/hle/service/hid/controllers/npad.cpp +++ b/src/core/hle/service/hid/controllers/npad.cpp @@ -703,7 +703,8 @@ void Controller_NPad::VibrateController(const std::vector& vibrati // TODO: Vibrate left/right vibration motors independently if possible. button_state[A - BUTTON_HID_BEGIN]->SetRumblePlay( - vibration_values[i].amp_high, vibration_values[i].amp_low, + vibration_values[i].amp_high * Settings::values.vibration_strength.GetValue() / 100, + vibration_values[i].amp_low * Settings::values.vibration_strength.GetValue() / 100, vibration_values[i].freq_high, vibration_values[i].freq_low); latest_vibration_values[npad_index][device_index] = vibration_values[i]; diff --git a/src/core/settings.cpp b/src/core/settings.cpp index aadbc3932..6e39aebb5 100644 --- a/src/core/settings.cpp +++ b/src/core/settings.cpp @@ -150,6 +150,7 @@ void RestoreGlobalState() { values.players.SetGlobal(true); values.use_docked_mode.SetGlobal(true); values.vibration_enabled.SetGlobal(true); + values.vibration_strength.SetGlobal(true); values.motion_enabled.SetGlobal(true); } diff --git a/src/core/settings.h b/src/core/settings.h index edd2a00ca..496f47747 100644 --- a/src/core/settings.h +++ b/src/core/settings.h @@ -170,6 +170,7 @@ struct Values { Setting use_docked_mode; Setting vibration_enabled; + Setting vibration_strength; Setting motion_enabled; std::string motion_device; diff --git a/src/yuzu/applets/controller.cpp b/src/yuzu/applets/controller.cpp index 7697fe434..196b4f163 100644 --- a/src/yuzu/applets/controller.cpp +++ b/src/yuzu/applets/controller.cpp @@ -266,6 +266,8 @@ void QtControllerSelectorDialog::ApplyConfiguration() { OnDockedModeChanged(pre_docked_mode, Settings::values.use_docked_mode.GetValue()); Settings::values.vibration_enabled.SetValue(ui->vibrationGroup->isChecked()); + Settings::values.vibration_strength.SetValue(ui->vibrationSpin->value()); + Settings::values.motion_enabled.SetValue(ui->motionGroup->isChecked()); } void QtControllerSelectorDialog::LoadConfiguration() { @@ -281,6 +283,8 @@ void QtControllerSelectorDialog::LoadConfiguration() { UpdateDockedState(Settings::values.players.GetValue()[8].connected); ui->vibrationGroup->setChecked(Settings::values.vibration_enabled.GetValue()); + ui->vibrationSpin->setValue(Settings::values.vibration_strength.GetValue()); + ui->motionGroup->setChecked(Settings::values.motion_enabled.GetValue()); } void QtControllerSelectorDialog::CallConfigureInputDialog() { diff --git a/src/yuzu/applets/controller.ui b/src/yuzu/applets/controller.ui index 2ab69a2d3..cc27b8ef4 100644 --- a/src/yuzu/applets/controller.ui +++ b/src/yuzu/applets/controller.ui @@ -2349,7 +2349,7 @@ 1 - 200 + 100 100 diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index 296c58f58..820ef4098 100644 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp @@ -493,6 +493,8 @@ void Config::ReadControlValues() { ReadSettingGlobal(Settings::values.use_docked_mode, QStringLiteral("use_docked_mode"), false); ReadSettingGlobal(Settings::values.vibration_enabled, QStringLiteral("vibration_enabled"), true); + ReadSettingGlobal(Settings::values.vibration_strength, QStringLiteral("vibration_strength"), + 100); ReadSettingGlobal(Settings::values.motion_enabled, QStringLiteral("motion_enabled"), true); qt_config->endGroup(); @@ -1150,6 +1152,8 @@ void Config::SaveControlValues() { WriteSettingGlobal(QStringLiteral("use_docked_mode"), Settings::values.use_docked_mode, false); WriteSettingGlobal(QStringLiteral("vibration_enabled"), Settings::values.vibration_enabled, true); + WriteSettingGlobal(QStringLiteral("vibration_strength"), Settings::values.vibration_strength, + 100); WriteSettingGlobal(QStringLiteral("motion_enabled"), Settings::values.motion_enabled, true); WriteSetting(QStringLiteral("motion_device"), QString::fromStdString(Settings::values.motion_device), diff --git a/src/yuzu/configuration/configure_input.cpp b/src/yuzu/configuration/configure_input.cpp index 9a4de4c5d..84df547b9 100644 --- a/src/yuzu/configuration/configure_input.cpp +++ b/src/yuzu/configuration/configure_input.cpp @@ -186,6 +186,7 @@ void ConfigureInput::ApplyConfiguration() { OnDockedModeChanged(pre_docked_mode, Settings::values.use_docked_mode.GetValue()); Settings::values.vibration_enabled.SetValue(ui->vibrationGroup->isChecked()); + Settings::values.vibration_strength.SetValue(ui->vibrationSpin->value()); Settings::values.motion_enabled.SetValue(ui->motionGroup->isChecked()); } @@ -206,6 +207,7 @@ void ConfigureInput::LoadConfiguration() { UpdateDockedState(Settings::values.players.GetValue()[8].connected); ui->vibrationGroup->setChecked(Settings::values.vibration_enabled.GetValue()); + ui->vibrationSpin->setValue(Settings::values.vibration_strength.GetValue()); ui->motionGroup->setChecked(Settings::values.motion_enabled.GetValue()); } diff --git a/src/yuzu/configuration/configure_input.ui b/src/yuzu/configuration/configure_input.ui index b74481bda..cbd67d4c7 100644 --- a/src/yuzu/configuration/configure_input.ui +++ b/src/yuzu/configuration/configure_input.ui @@ -215,7 +215,7 @@ 1 - 200 + 100 100 diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp index feee02fcd..209350837 100644 --- a/src/yuzu_cmd/config.cpp +++ b/src/yuzu_cmd/config.cpp @@ -290,6 +290,8 @@ void Config::ReadValues() { Settings::values.vibration_enabled.SetValue( sdl2_config->GetBoolean("ControlsGeneral", "vibration_enabled", true)); + Settings::values.vibration_strength.SetValue( + sdl2_config->GetInteger("ControlsGeneral", "vibration_strength", 100)); Settings::values.motion_enabled.SetValue( sdl2_config->GetBoolean("ControlsGeneral", "motion_enabled", true)); Settings::values.touchscreen.enabled = diff --git a/src/yuzu_cmd/default_ini.h b/src/yuzu_cmd/default_ini.h index 796e27df4..53057c01c 100644 --- a/src/yuzu_cmd/default_ini.h +++ b/src/yuzu_cmd/default_ini.h @@ -65,6 +65,13 @@ button_screenshot= lstick= rstick= +# Whether to enable or disable vibration +# 0: Disabled, 1 (default): Enabled +vibration_enabled= + +# Vibration strength percentage (Default: 100) +vibration_strength= + # for motion input, the following devices are available: # - "motion_emu" (default) for emulating motion input from mouse input. Required parameters: # - "update_period": update period in milliseconds (default to 100) diff --git a/src/yuzu_tester/config.cpp b/src/yuzu_tester/config.cpp index 3a8a333f0..437302520 100644 --- a/src/yuzu_tester/config.cpp +++ b/src/yuzu_tester/config.cpp @@ -76,6 +76,7 @@ void Config::ReadValues() { } Settings::values.vibration_enabled.SetValue(true); + Settings::values.vibration_strength.SetValue(100); Settings::values.motion_enabled.SetValue(true); Settings::values.touchscreen.enabled = ""; Settings::values.touchscreen.device = ""; From 9b501af8e3d0f6457fafb0fdfbcc11f6da4f0e8a Mon Sep 17 00:00:00 2001 From: Morph <39850852+Morph1984@users.noreply.github.com> Date: Sat, 10 Oct 2020 09:03:47 -0400 Subject: [PATCH 20/37] controllers/npad: Add heuristics to reduce rumble state changes Sending too many state changes in a short period of time can cause massive performance issues. As a result, we have to use several heuristics to reduce the number of state changes to minimize/eliminate this performance impact while maintaining the quality of these vibrations as much as possible. --- src/core/frontend/input.h | 2 +- src/core/hle/service/hid/controllers/npad.cpp | 49 +++++++++++++++-- src/input_common/sdl/sdl_impl.cpp | 54 +++++++++---------- 3 files changed, 71 insertions(+), 34 deletions(-) diff --git a/src/core/frontend/input.h b/src/core/frontend/input.h index 277b70e53..fb2ce2514 100644 --- a/src/core/frontend/input.h +++ b/src/core/frontend/input.h @@ -33,7 +33,7 @@ public: virtual bool GetAnalogDirectionStatus(AnalogDirection direction) const { return {}; } - virtual bool SetRumblePlay(f32 amp_high, f32 amp_low, f32 freq_high, f32 freq_low) const { + virtual bool SetRumblePlay(f32 amp_low, f32 freq_low, f32 amp_high, f32 freq_high) const { return {}; } }; diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp index 924f209c0..cc54b164d 100644 --- a/src/core/hle/service/hid/controllers/npad.cpp +++ b/src/core/hle/service/hid/controllers/npad.cpp @@ -698,16 +698,57 @@ void Controller_NPad::VibrateController(const std::vector& vibrati continue; } + // Filter out vibrations with equivalent values to reduce unnecessary state changes. + if (vibration_values[i].amp_low == + latest_vibration_values[npad_index][device_index].amp_low && + vibration_values[i].amp_high == + latest_vibration_values[npad_index][device_index].amp_high) { + continue; + } + + // Filter out non-zero vibrations that are within 0.015625 absolute amplitude of each other. + if ((vibration_values[i].amp_low != 0.0f || vibration_values[i].amp_high != 0.0f) && + (latest_vibration_values[npad_index][device_index].amp_low != 0.0f || + latest_vibration_values[npad_index][device_index].amp_high != 0.0f) && + (abs(vibration_values[i].amp_low - + latest_vibration_values[npad_index][device_index].amp_low) < 0.015625f && + abs(vibration_values[i].amp_high - + latest_vibration_values[npad_index][device_index].amp_high) < 0.015625f)) { + continue; + } + using namespace Settings::NativeButton; const auto& button_state = buttons[npad_index]; // TODO: Vibrate left/right vibration motors independently if possible. - button_state[A - BUTTON_HID_BEGIN]->SetRumblePlay( - vibration_values[i].amp_high * Settings::values.vibration_strength.GetValue() / 100, + const bool success = button_state[A - BUTTON_HID_BEGIN]->SetRumblePlay( vibration_values[i].amp_low * Settings::values.vibration_strength.GetValue() / 100, - vibration_values[i].freq_high, vibration_values[i].freq_low); + vibration_values[i].freq_low, + vibration_values[i].amp_high * Settings::values.vibration_strength.GetValue() / 100, + vibration_values[i].freq_high); - latest_vibration_values[npad_index][device_index] = vibration_values[i]; + if (success) { + switch (connected_controllers[npad_index].type) { + case NPadControllerType::None: + UNREACHABLE(); + break; + case NPadControllerType::ProController: + case NPadControllerType::Handheld: + case NPadControllerType::JoyDual: + // Since we can't vibrate motors independently yet, we can reduce state changes by + // assigning all 3 device indices the current vibration value. + latest_vibration_values[npad_index][0] = vibration_values[i]; + latest_vibration_values[npad_index][1] = vibration_values[i]; + latest_vibration_values[npad_index][2] = vibration_values[i]; + break; + case NPadControllerType::JoyLeft: + case NPadControllerType::JoyRight: + case NPadControllerType::Pokeball: + default: + latest_vibration_values[npad_index][device_index] = vibration_values[i]; + break; + } + } } } diff --git a/src/input_common/sdl/sdl_impl.cpp b/src/input_common/sdl/sdl_impl.cpp index 10883e2d9..18fb2ac5e 100644 --- a/src/input_common/sdl/sdl_impl.cpp +++ b/src/input_common/sdl/sdl_impl.cpp @@ -80,30 +80,24 @@ public: return static_cast(state.axes.at(axis)) / (32767.0f * range); } - bool RumblePlay(f32 amp_low, f32 amp_high, u32 time) { - const u16 raw_amp_low = static_cast(amp_low * 0xFFFF); - const u16 raw_amp_high = static_cast(amp_high * 0xFFFF); - // Lower drastically the number of state changes - if (raw_amp_low >> 11 == last_state_rumble_low >> 11 && - raw_amp_high >> 11 == last_state_rumble_high >> 11) { - if (raw_amp_low + raw_amp_high != 0 || - last_state_rumble_low + last_state_rumble_high == 0) { - return false; - } - } - // Don't change state if last vibration was < 20ms - const auto now = std::chrono::system_clock::now(); - if (std::chrono::duration_cast(now - last_vibration) < - std::chrono::milliseconds(20)) { - return raw_amp_low + raw_amp_high == 0; + bool RumblePlay(u16 amp_low, u16 amp_high) { + using std::chrono::duration_cast; + using std::chrono::milliseconds; + using std::chrono::steady_clock; + + // Prevent vibrations less than 10ms apart from each other. + if (duration_cast(steady_clock::now() - last_vibration) < milliseconds(10)) { + return false; + }; + + last_vibration = steady_clock::now(); + + if (sdl_controller != nullptr) { + return SDL_GameControllerRumble(sdl_controller.get(), amp_low, amp_high, 0) == 0; + } else if (sdl_joystick != nullptr) { + return SDL_JoystickRumble(sdl_joystick.get(), amp_low, amp_high, 0) == 0; } - last_vibration = now; - last_state_rumble_low = raw_amp_low; - last_state_rumble_high = raw_amp_high; - if (sdl_joystick) { - SDL_JoystickRumble(sdl_joystick.get(), raw_amp_low, raw_amp_high, time); - } return false; } @@ -172,13 +166,13 @@ private: } state; std::string guid; int port; - u16 last_state_rumble_high = 0; - u16 last_state_rumble_low = 0; - std::chrono::time_point last_vibration; std::unique_ptr sdl_joystick; std::unique_ptr sdl_controller; mutable std::mutex mutex; + // This is the timepoint of the last vibration and is used to ensure vibrations are 10ms apart. + std::chrono::steady_clock::time_point last_vibration; + // Motion is initialized without PID values as motion input is not aviable for SDL2 MotionInput motion{0.0f, 0.0f, 0.0f}; }; @@ -327,10 +321,12 @@ public: return joystick->GetButton(button); } - bool SetRumblePlay(f32 amp_high, f32 amp_low, f32 freq_high, f32 freq_low) const override { - const f32 new_amp_low = pow(amp_low, 0.5f) * (3.0f - 2.0f * pow(amp_low, 0.15f)); - const f32 new_amp_high = pow(amp_high, 0.5f) * (3.0f - 2.0f * pow(amp_high, 0.15f)); - return joystick->RumblePlay(new_amp_low, new_amp_high, 250); + bool SetRumblePlay(f32 amp_low, f32 freq_low, f32 amp_high, f32 freq_high) const override { + const u16 processed_amp_low = + static_cast(pow(amp_low, 0.5f) * (3.0f - 2.0f * pow(amp_low, 0.15f)) * 0xFFFF); + const u16 processed_amp_high = + static_cast(pow(amp_high, 0.5f) * (3.0f - 2.0f * pow(amp_high, 0.15f)) * 0xFFFF); + return joystick->RumblePlay(processed_amp_low, processed_amp_high); } private: From 70f16f17220a23daead70e9f94f93d1c61d374a1 Mon Sep 17 00:00:00 2001 From: Morph <39850852+Morph1984@users.noreply.github.com> Date: Sun, 11 Oct 2020 08:35:40 -0400 Subject: [PATCH 21/37] hid: Stub IsVibrationDeviceMounted - Used in Super Mario Odyssey --- src/core/hle/service/hid/hid.cpp | 23 ++++++++++++++++++++++- src/core/hle/service/hid/hid.h | 1 + 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index b0390bdb2..89327cd86 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp @@ -249,7 +249,7 @@ Hid::Hid(Core::System& system) : ServiceFramework("hid"), system(system) { {208, nullptr, "GetActualVibrationGcErmCommand"}, {209, &Hid::BeginPermitVibrationSession, "BeginPermitVibrationSession"}, {210, &Hid::EndPermitVibrationSession, "EndPermitVibrationSession"}, - {211, nullptr, "IsVibrationDeviceMounted"}, + {211, &Hid::IsVibrationDeviceMounted, "IsVibrationDeviceMounted"}, {300, &Hid::ActivateConsoleSixAxisSensor, "ActivateConsoleSixAxisSensor"}, {301, &Hid::StartConsoleSixAxisSensor, "StartConsoleSixAxisSensor"}, {302, &Hid::StopConsoleSixAxisSensor, "StopConsoleSixAxisSensor"}, @@ -1129,6 +1129,27 @@ void Hid::EndPermitVibrationSession(Kernel::HLERequestContext& ctx) { rb.Push(RESULT_SUCCESS); } +void Hid::IsVibrationDeviceMounted(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + struct Parameters { + Controller_NPad::DeviceHandle vibration_device_handle{}; + INSERT_PADDING_WORDS(1); + u64 applet_resource_user_id{}; + }; + + const auto parameters{rp.PopRaw()}; + + LOG_WARNING( + Service_HID, + "(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", + parameters.vibration_device_handle.npad_type, parameters.vibration_device_handle.npad_id, + parameters.vibration_device_handle.device_index, parameters.applet_resource_user_id); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push(true); +} + void Hid::ActivateConsoleSixAxisSensor(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto applet_resource_user_id{rp.Pop()}; diff --git a/src/core/hle/service/hid/hid.h b/src/core/hle/service/hid/hid.h index 57bc93778..c8e4a4b55 100644 --- a/src/core/hle/service/hid/hid.h +++ b/src/core/hle/service/hid/hid.h @@ -134,6 +134,7 @@ private: void SendVibrationValues(Kernel::HLERequestContext& ctx); void BeginPermitVibrationSession(Kernel::HLERequestContext& ctx); void EndPermitVibrationSession(Kernel::HLERequestContext& ctx); + void IsVibrationDeviceMounted(Kernel::HLERequestContext& ctx); void ActivateConsoleSixAxisSensor(Kernel::HLERequestContext& ctx); void StartConsoleSixAxisSensor(Kernel::HLERequestContext& ctx); void StopConsoleSixAxisSensor(Kernel::HLERequestContext& ctx); From 373408ae8c565cc401770e65776cae55a3545572 Mon Sep 17 00:00:00 2001 From: Morph <39850852+Morph1984@users.noreply.github.com> Date: Sun, 11 Oct 2020 11:25:17 -0400 Subject: [PATCH 22/37] controllers/npad: Send an empty vibration on destruction/deactivation This stops all controllers from continuously vibrating when emulation is stopped. --- src/core/hle/service/hid/controllers/npad.cpp | 42 ++++++++++++------- src/core/hle/service/hid/controllers/npad.h | 14 ++++--- src/core/hle/service/hid/hid.cpp | 4 +- 3 files changed, 38 insertions(+), 22 deletions(-) diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp index cc54b164d..81725efbb 100644 --- a/src/core/hle/service/hid/controllers/npad.cpp +++ b/src/core/hle/service/hid/controllers/npad.cpp @@ -117,7 +117,10 @@ u32 Controller_NPad::IndexToNPad(std::size_t index) { } Controller_NPad::Controller_NPad(Core::System& system) : ControllerBase(system), system(system) {} -Controller_NPad::~Controller_NPad() = default; + +Controller_NPad::~Controller_NPad() { + OnRelease(); +} void Controller_NPad::InitNewlyAddedController(std::size_t controller_idx) { const auto controller_type = connected_controllers[controller_idx].type; @@ -274,7 +277,11 @@ void Controller_NPad::OnLoadInputDevices() { } } -void Controller_NPad::OnRelease() {} +void Controller_NPad::OnRelease() { + for (std::size_t index = 0; index < connected_controllers.size(); ++index) { + VibrateControllerAtIndex(index, {}); + } +} void Controller_NPad::RequestPadStateUpdate(u32 npad_id) { const auto controller_idx = NPadIdToIndex(npad_id); @@ -667,8 +674,24 @@ void Controller_NPad::SetNpadMode(u32 npad_id, NpadAssignments assignment_mode) } } -void Controller_NPad::VibrateController(const std::vector& vibration_device_handles, - const std::vector& vibration_values) { +bool Controller_NPad::VibrateControllerAtIndex(std::size_t npad_index, + const VibrationValue& vibration_value) { + if (!connected_controllers[npad_index].is_connected) { + return false; + } + + using namespace Settings::NativeButton; + const auto& button_state = buttons[npad_index]; + + return button_state[A - BUTTON_HID_BEGIN]->SetRumblePlay( + vibration_value.amp_low * Settings::values.vibration_strength.GetValue() / 100, + vibration_value.freq_low, + vibration_value.amp_high * Settings::values.vibration_strength.GetValue() / 100, + vibration_value.freq_high); +} + +void Controller_NPad::VibrateControllers(const std::vector& vibration_device_handles, + const std::vector& vibration_values) { LOG_TRACE(Service_HID, "called"); if (!Settings::values.vibration_enabled.GetValue() || !can_controllers_vibrate) { @@ -717,17 +740,8 @@ void Controller_NPad::VibrateController(const std::vector& vibrati continue; } - using namespace Settings::NativeButton; - const auto& button_state = buttons[npad_index]; - // TODO: Vibrate left/right vibration motors independently if possible. - const bool success = button_state[A - BUTTON_HID_BEGIN]->SetRumblePlay( - vibration_values[i].amp_low * Settings::values.vibration_strength.GetValue() / 100, - vibration_values[i].freq_low, - vibration_values[i].amp_high * Settings::values.vibration_strength.GetValue() / 100, - vibration_values[i].freq_high); - - if (success) { + if (VibrateControllerAtIndex(npad_index, vibration_values[i])) { switch (connected_controllers[npad_index].type) { case NPadControllerType::None: UNREACHABLE(); diff --git a/src/core/hle/service/hid/controllers/npad.h b/src/core/hle/service/hid/controllers/npad.h index c1b19103a..4dc2a974d 100644 --- a/src/core/hle/service/hid/controllers/npad.h +++ b/src/core/hle/service/hid/controllers/npad.h @@ -110,10 +110,10 @@ public: static_assert(sizeof(NpadStyleSet) == 4, "NpadStyleSet is an invalid size"); struct VibrationValue { - f32 amp_low; - f32 freq_low; - f32 amp_high; - f32 freq_high; + f32 amp_low{0.0f}; + f32 freq_low{160.0f}; + f32 amp_high{0.0f}; + f32 freq_high{320.0f}; }; static_assert(sizeof(VibrationValue) == 0x10, "Vibration is an invalid size"); @@ -148,8 +148,10 @@ public: void SetNpadMode(u32 npad_id, NpadAssignments assignment_mode); - void VibrateController(const std::vector& vibration_device_handles, - const std::vector& vibration_values); + bool VibrateControllerAtIndex(std::size_t npad_index, const VibrationValue& vibration_value); + + void VibrateControllers(const std::vector& vibration_device_handles, + const std::vector& vibration_values); VibrationValue GetLastVibration(const DeviceHandle& vibration_device_handle) const; diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index 89327cd86..878f20bd2 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp @@ -1022,7 +1022,7 @@ void Hid::SendVibrationValue(Kernel::HLERequestContext& ctx) { const auto parameters{rp.PopRaw()}; applet_resource->GetController(HidController::NPad) - .VibrateController({parameters.vibration_device_handle}, {parameters.vibration_value}); + .VibrateControllers({parameters.vibration_device_handle}, {parameters.vibration_value}); LOG_DEBUG(Service_HID, "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", @@ -1100,7 +1100,7 @@ void Hid::SendVibrationValues(Kernel::HLERequestContext& ctx) { std::memcpy(vibration_values.data(), vibrations.data(), vibrations.size()); applet_resource->GetController(HidController::NPad) - .VibrateController(vibration_device_handles, vibration_values); + .VibrateControllers(vibration_device_handles, vibration_values); LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); From 92fa5257c762f631c64cbc8a60870af7deaf2ead Mon Sep 17 00:00:00 2001 From: Morph <39850852+Morph1984@users.noreply.github.com> Date: Thu, 15 Oct 2020 14:35:35 -0400 Subject: [PATCH 23/37] hid: Mark Begin/EndPermitVibrationSession as stubs The implementation of these commands seem incomplete and causes rumble in Super Mario Party to stop working since only EndPermitVibrationSession is called. Thus, these are better off being marked as a stub until this can be investigated more thoroughly. --- src/core/hle/service/hid/controllers/npad.cpp | 10 +--------- src/core/hle/service/hid/controllers/npad.h | 3 --- src/core/hle/service/hid/hid.cpp | 9 +++------ 3 files changed, 4 insertions(+), 18 deletions(-) diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp index 81725efbb..1fc06ef3f 100644 --- a/src/core/hle/service/hid/controllers/npad.cpp +++ b/src/core/hle/service/hid/controllers/npad.cpp @@ -694,7 +694,7 @@ void Controller_NPad::VibrateControllers(const std::vector& vibrat const std::vector& vibration_values) { LOG_TRACE(Service_HID, "called"); - if (!Settings::values.vibration_enabled.GetValue() || !can_controllers_vibrate) { + if (!Settings::values.vibration_enabled.GetValue()) { return; } @@ -924,14 +924,6 @@ void Controller_NPad::SetUnintendedHomeButtonInputProtectionEnabled(bool is_prot unintended_home_button_input_protection[NPadIdToIndex(npad_id)] = is_protection_enabled; } -void Controller_NPad::SetVibrationEnabled(bool can_vibrate) { - can_controllers_vibrate = can_vibrate; -} - -bool Controller_NPad::IsVibrationEnabled() const { - return can_controllers_vibrate; -} - void Controller_NPad::ClearAllConnectedControllers() { for (auto& controller : connected_controllers) { if (controller.is_connected && controller.type != NPadControllerType::None) { diff --git a/src/core/hle/service/hid/controllers/npad.h b/src/core/hle/service/hid/controllers/npad.h index 4dc2a974d..576ef1558 100644 --- a/src/core/hle/service/hid/controllers/npad.h +++ b/src/core/hle/service/hid/controllers/npad.h @@ -173,8 +173,6 @@ public: LedPattern GetLedPattern(u32 npad_id); bool IsUnintendedHomeButtonInputProtectionEnabled(u32 npad_id) const; void SetUnintendedHomeButtonInputProtectionEnabled(bool is_protection_enabled, u32 npad_id); - void SetVibrationEnabled(bool can_vibrate); - bool IsVibrationEnabled() const; void ClearAllConnectedControllers(); void DisconnectAllConnectedControllers(); void ConnectAllDisconnectedControllers(); @@ -416,7 +414,6 @@ private: std::array connected_controllers{}; std::array unintended_home_button_input_protection{}; GyroscopeZeroDriftMode gyroscope_zero_drift_mode{GyroscopeZeroDriftMode::Standard}; - bool can_controllers_vibrate{true}; bool sixaxis_sensors_enabled{true}; bool sixaxis_at_rest{true}; std::array npad_pad_states{}; diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index 878f20bd2..e88f30d6a 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp @@ -1112,18 +1112,15 @@ void Hid::BeginPermitVibrationSession(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto applet_resource_user_id{rp.Pop()}; - applet_resource->GetController(HidController::NPad).SetVibrationEnabled(true); - - LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); + LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}", + applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); } void Hid::EndPermitVibrationSession(Kernel::HLERequestContext& ctx) { - applet_resource->GetController(HidController::NPad).SetVibrationEnabled(false); - - LOG_DEBUG(Service_HID, "called"); + LOG_WARNING(Service_HID, "(STUBBED) called"); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); From d6a41cfc21a75349ca79e73da5ca1dcecd1af901 Mon Sep 17 00:00:00 2001 From: Morph <39850852+Morph1984@users.noreply.github.com> Date: Fri, 16 Oct 2020 11:55:45 -0400 Subject: [PATCH 24/37] settings: Remove global vibration strength modifier This will be replaced in favor of per-player vibration strength modifiers. --- src/core/hle/service/hid/controllers/npad.cpp | 4 +--- src/core/settings.cpp | 1 - src/core/settings.h | 1 - src/yuzu/applets/controller.cpp | 2 -- src/yuzu/configuration/config.cpp | 4 ---- src/yuzu/configuration/configure_input.cpp | 2 -- src/yuzu_cmd/config.cpp | 2 -- src/yuzu_cmd/default_ini.h | 3 --- src/yuzu_tester/config.cpp | 1 - 9 files changed, 1 insertion(+), 19 deletions(-) diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp index 1fc06ef3f..ba20d3f59 100644 --- a/src/core/hle/service/hid/controllers/npad.cpp +++ b/src/core/hle/service/hid/controllers/npad.cpp @@ -684,9 +684,7 @@ bool Controller_NPad::VibrateControllerAtIndex(std::size_t npad_index, const auto& button_state = buttons[npad_index]; return button_state[A - BUTTON_HID_BEGIN]->SetRumblePlay( - vibration_value.amp_low * Settings::values.vibration_strength.GetValue() / 100, - vibration_value.freq_low, - vibration_value.amp_high * Settings::values.vibration_strength.GetValue() / 100, + vibration_value.amp_low, vibration_value.freq_low, vibration_value.amp_high, vibration_value.freq_high); } diff --git a/src/core/settings.cpp b/src/core/settings.cpp index 6e39aebb5..aadbc3932 100644 --- a/src/core/settings.cpp +++ b/src/core/settings.cpp @@ -150,7 +150,6 @@ void RestoreGlobalState() { values.players.SetGlobal(true); values.use_docked_mode.SetGlobal(true); values.vibration_enabled.SetGlobal(true); - values.vibration_strength.SetGlobal(true); values.motion_enabled.SetGlobal(true); } diff --git a/src/core/settings.h b/src/core/settings.h index 496f47747..edd2a00ca 100644 --- a/src/core/settings.h +++ b/src/core/settings.h @@ -170,7 +170,6 @@ struct Values { Setting use_docked_mode; Setting vibration_enabled; - Setting vibration_strength; Setting motion_enabled; std::string motion_device; diff --git a/src/yuzu/applets/controller.cpp b/src/yuzu/applets/controller.cpp index 196b4f163..0fc713a6e 100644 --- a/src/yuzu/applets/controller.cpp +++ b/src/yuzu/applets/controller.cpp @@ -266,7 +266,6 @@ void QtControllerSelectorDialog::ApplyConfiguration() { OnDockedModeChanged(pre_docked_mode, Settings::values.use_docked_mode.GetValue()); Settings::values.vibration_enabled.SetValue(ui->vibrationGroup->isChecked()); - Settings::values.vibration_strength.SetValue(ui->vibrationSpin->value()); Settings::values.motion_enabled.SetValue(ui->motionGroup->isChecked()); } @@ -283,7 +282,6 @@ void QtControllerSelectorDialog::LoadConfiguration() { UpdateDockedState(Settings::values.players.GetValue()[8].connected); ui->vibrationGroup->setChecked(Settings::values.vibration_enabled.GetValue()); - ui->vibrationSpin->setValue(Settings::values.vibration_strength.GetValue()); ui->motionGroup->setChecked(Settings::values.motion_enabled.GetValue()); } diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index 820ef4098..296c58f58 100644 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp @@ -493,8 +493,6 @@ void Config::ReadControlValues() { ReadSettingGlobal(Settings::values.use_docked_mode, QStringLiteral("use_docked_mode"), false); ReadSettingGlobal(Settings::values.vibration_enabled, QStringLiteral("vibration_enabled"), true); - ReadSettingGlobal(Settings::values.vibration_strength, QStringLiteral("vibration_strength"), - 100); ReadSettingGlobal(Settings::values.motion_enabled, QStringLiteral("motion_enabled"), true); qt_config->endGroup(); @@ -1152,8 +1150,6 @@ void Config::SaveControlValues() { WriteSettingGlobal(QStringLiteral("use_docked_mode"), Settings::values.use_docked_mode, false); WriteSettingGlobal(QStringLiteral("vibration_enabled"), Settings::values.vibration_enabled, true); - WriteSettingGlobal(QStringLiteral("vibration_strength"), Settings::values.vibration_strength, - 100); WriteSettingGlobal(QStringLiteral("motion_enabled"), Settings::values.motion_enabled, true); WriteSetting(QStringLiteral("motion_device"), QString::fromStdString(Settings::values.motion_device), diff --git a/src/yuzu/configuration/configure_input.cpp b/src/yuzu/configuration/configure_input.cpp index 84df547b9..9a4de4c5d 100644 --- a/src/yuzu/configuration/configure_input.cpp +++ b/src/yuzu/configuration/configure_input.cpp @@ -186,7 +186,6 @@ void ConfigureInput::ApplyConfiguration() { OnDockedModeChanged(pre_docked_mode, Settings::values.use_docked_mode.GetValue()); Settings::values.vibration_enabled.SetValue(ui->vibrationGroup->isChecked()); - Settings::values.vibration_strength.SetValue(ui->vibrationSpin->value()); Settings::values.motion_enabled.SetValue(ui->motionGroup->isChecked()); } @@ -207,7 +206,6 @@ void ConfigureInput::LoadConfiguration() { UpdateDockedState(Settings::values.players.GetValue()[8].connected); ui->vibrationGroup->setChecked(Settings::values.vibration_enabled.GetValue()); - ui->vibrationSpin->setValue(Settings::values.vibration_strength.GetValue()); ui->motionGroup->setChecked(Settings::values.motion_enabled.GetValue()); } diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp index 209350837..feee02fcd 100644 --- a/src/yuzu_cmd/config.cpp +++ b/src/yuzu_cmd/config.cpp @@ -290,8 +290,6 @@ void Config::ReadValues() { Settings::values.vibration_enabled.SetValue( sdl2_config->GetBoolean("ControlsGeneral", "vibration_enabled", true)); - Settings::values.vibration_strength.SetValue( - sdl2_config->GetInteger("ControlsGeneral", "vibration_strength", 100)); Settings::values.motion_enabled.SetValue( sdl2_config->GetBoolean("ControlsGeneral", "motion_enabled", true)); Settings::values.touchscreen.enabled = diff --git a/src/yuzu_cmd/default_ini.h b/src/yuzu_cmd/default_ini.h index 53057c01c..b6f6a3bb0 100644 --- a/src/yuzu_cmd/default_ini.h +++ b/src/yuzu_cmd/default_ini.h @@ -69,9 +69,6 @@ rstick= # 0: Disabled, 1 (default): Enabled vibration_enabled= -# Vibration strength percentage (Default: 100) -vibration_strength= - # for motion input, the following devices are available: # - "motion_emu" (default) for emulating motion input from mouse input. Required parameters: # - "update_period": update period in milliseconds (default to 100) diff --git a/src/yuzu_tester/config.cpp b/src/yuzu_tester/config.cpp index 437302520..3a8a333f0 100644 --- a/src/yuzu_tester/config.cpp +++ b/src/yuzu_tester/config.cpp @@ -76,7 +76,6 @@ void Config::ReadValues() { } Settings::values.vibration_enabled.SetValue(true); - Settings::values.vibration_strength.SetValue(100); Settings::values.motion_enabled.SetValue(true); Settings::values.touchscreen.enabled = ""; Settings::values.touchscreen.device = ""; From 38110dd485e329fa39e2e4c02b91a89dfebcbc88 Mon Sep 17 00:00:00 2001 From: Morph <39850852+Morph1984@users.noreply.github.com> Date: Sat, 17 Oct 2020 09:38:12 -0400 Subject: [PATCH 25/37] configure_input: Add per-player vibration Allows for enabling and modifying vibration and vibration strength per player. Also adds a toggle for enabling/disabling accurate vibrations. Co-authored-by: Its-Rei --- dist/qt_themes/qdarkstyle/style.qss | 11 +- .../qdarkstyle_midnight_blue/style.qss | 12 +- src/core/hle/service/hid/controllers/npad.cpp | 13 +- src/core/settings.h | 1 + src/input_common/settings.h | 3 + src/yuzu/CMakeLists.txt | 3 + src/yuzu/applets/controller.cpp | 16 + src/yuzu/applets/controller.h | 3 + src/yuzu/applets/controller.ui | 18 +- src/yuzu/configuration/config.cpp | 16 + src/yuzu/configuration/configure_input.cpp | 4 + src/yuzu/configuration/configure_input.ui | 22 +- .../configuration/configure_vibration.cpp | 66 +++ src/yuzu/configuration/configure_vibration.h | 40 ++ src/yuzu/configuration/configure_vibration.ui | 546 ++++++++++++++++++ src/yuzu_cmd/config.cpp | 2 + src/yuzu_cmd/default_ini.h | 4 + src/yuzu_tester/config.cpp | 1 + 18 files changed, 751 insertions(+), 30 deletions(-) create mode 100644 src/yuzu/configuration/configure_vibration.cpp create mode 100644 src/yuzu/configuration/configure_vibration.h create mode 100644 src/yuzu/configuration/configure_vibration.ui diff --git a/dist/qt_themes/qdarkstyle/style.qss b/dist/qt_themes/qdarkstyle/style.qss index aca6531ac..2a1e8ddeb 100644 --- a/dist/qt_themes/qdarkstyle/style.qss +++ b/dist/qt_themes/qdarkstyle/style.qss @@ -1293,15 +1293,24 @@ QPushButton#buttonRefreshDevices { QSpinBox#spinboxLStickRange, QSpinBox#spinboxRStickRange, -QSpinBox#vibrationSpin { +QSpinBox#vibrationSpinPlayer1, +QSpinBox#vibrationSpinPlayer2, +QSpinBox#vibrationSpinPlayer3, +QSpinBox#vibrationSpinPlayer4, +QSpinBox#vibrationSpinPlayer5, +QSpinBox#vibrationSpinPlayer6, +QSpinBox#vibrationSpinPlayer7, +QSpinBox#vibrationSpinPlayer8 { min-width: 68px; } +QDialog#ConfigureVibration QGroupBox::indicator, QGroupBox#motionGroup::indicator, QGroupBox#vibrationGroup::indicator { margin-left: 0px; } +QDialog#ConfigureVibration QGroupBox::title, QGroupBox#motionGroup::title, QGroupBox#vibrationGroup::title { spacing: 2px; diff --git a/dist/qt_themes/qdarkstyle_midnight_blue/style.qss b/dist/qt_themes/qdarkstyle_midnight_blue/style.qss index ad032a966..70e540b06 100644 --- a/dist/qt_themes/qdarkstyle_midnight_blue/style.qss +++ b/dist/qt_themes/qdarkstyle_midnight_blue/style.qss @@ -2200,21 +2200,31 @@ QPushButton#buttonRefreshDevices { QSpinBox#spinboxLStickRange, QSpinBox#spinboxRStickRange, -QSpinBox#vibrationSpin { +QSpinBox#vibrationSpinPlayer1, +QSpinBox#vibrationSpinPlayer2, +QSpinBox#vibrationSpinPlayer3, +QSpinBox#vibrationSpinPlayer4, +QSpinBox#vibrationSpinPlayer5, +QSpinBox#vibrationSpinPlayer6, +QSpinBox#vibrationSpinPlayer7, +QSpinBox#vibrationSpinPlayer8 { min-width: 68px; } +QDialog#ConfigureVibration QGroupBox::indicator, QGroupBox#motionGroup::indicator, QGroupBox#vibrationGroup::indicator { margin-left: 0px; } +QDialog#ConfigureVibration QGroupBox, QWidget#bottomPerGameInput QGroupBox#motionGroup, QWidget#bottomPerGameInput QGroupBox#vibrationGroup, QWidget#bottomPerGameInput QGroupBox#inputConfigGroup { padding: 0px; } +QDialog#ConfigureVibration QGroupBox::title, QGroupBox#motionGroup::title, QGroupBox#vibrationGroup::title { spacing: 2px; diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp index ba20d3f59..dc9954377 100644 --- a/src/core/hle/service/hid/controllers/npad.cpp +++ b/src/core/hle/service/hid/controllers/npad.cpp @@ -680,11 +680,19 @@ bool Controller_NPad::VibrateControllerAtIndex(std::size_t npad_index, return false; } + const auto& player = Settings::values.players.GetValue()[npad_index]; + + if (!player.vibration_enabled) { + return false; + } + using namespace Settings::NativeButton; const auto& button_state = buttons[npad_index]; return button_state[A - BUTTON_HID_BEGIN]->SetRumblePlay( - vibration_value.amp_low, vibration_value.freq_low, vibration_value.amp_high, + std::min(vibration_value.amp_low * player.vibration_strength / 100.0f, 1.0f), + vibration_value.freq_low, + std::min(vibration_value.amp_high * player.vibration_strength / 100.0f, 1.0f), vibration_value.freq_high); } @@ -728,7 +736,8 @@ void Controller_NPad::VibrateControllers(const std::vector& vibrat } // Filter out non-zero vibrations that are within 0.015625 absolute amplitude of each other. - if ((vibration_values[i].amp_low != 0.0f || vibration_values[i].amp_high != 0.0f) && + if (!Settings::values.enable_accurate_vibrations.GetValue() && + (vibration_values[i].amp_low != 0.0f || vibration_values[i].amp_high != 0.0f) && (latest_vibration_values[npad_index][device_index].amp_low != 0.0f || latest_vibration_values[npad_index][device_index].amp_high != 0.0f) && (abs(vibration_values[i].amp_low - diff --git a/src/core/settings.h b/src/core/settings.h index edd2a00ca..476c3fdf3 100644 --- a/src/core/settings.h +++ b/src/core/settings.h @@ -170,6 +170,7 @@ struct Values { Setting use_docked_mode; Setting vibration_enabled; + Setting enable_accurate_vibrations; Setting motion_enabled; std::string motion_device; diff --git a/src/input_common/settings.h b/src/input_common/settings.h index f52d28540..2763ed991 100644 --- a/src/input_common/settings.h +++ b/src/input_common/settings.h @@ -332,6 +332,9 @@ struct PlayerInput { AnalogsRaw analogs; MotionRaw motions; + bool vibration_enabled; + int vibration_strength; + u32 body_color_left; u32 body_color_right; u32 button_color_left; diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt index 22fe0a2a6..bf1fae9fa 100644 --- a/src/yuzu/CMakeLists.txt +++ b/src/yuzu/CMakeLists.txt @@ -105,6 +105,9 @@ add_executable(yuzu configuration/configure_ui.cpp configuration/configure_ui.h configuration/configure_ui.ui + configuration/configure_vibration.cpp + configuration/configure_vibration.h + configuration/configure_vibration.ui configuration/configure_web.cpp configuration/configure_web.h configuration/configure_web.ui diff --git a/src/yuzu/applets/controller.cpp b/src/yuzu/applets/controller.cpp index 0fc713a6e..c5e671309 100644 --- a/src/yuzu/applets/controller.cpp +++ b/src/yuzu/applets/controller.cpp @@ -14,6 +14,7 @@ #include "ui_controller.h" #include "yuzu/applets/controller.h" #include "yuzu/configuration/configure_input_dialog.h" +#include "yuzu/configuration/configure_vibration.h" #include "yuzu/main.h" namespace { @@ -223,6 +224,9 @@ QtControllerSelectorDialog::QtControllerSelectorDialog( } } + connect(ui->vibrationButton, &QPushButton::clicked, this, + &QtControllerSelectorDialog::CallConfigureVibrationDialog); + connect(ui->inputConfigButton, &QPushButton::clicked, this, &QtControllerSelectorDialog::CallConfigureInputDialog); @@ -285,6 +289,18 @@ void QtControllerSelectorDialog::LoadConfiguration() { ui->motionGroup->setChecked(Settings::values.motion_enabled.GetValue()); } +void QtControllerSelectorDialog::CallConfigureVibrationDialog() { + ConfigureVibration dialog(this); + + dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint | + Qt::WindowSystemMenuHint); + dialog.setWindowModality(Qt::WindowModal); + + if (dialog.exec() == QDialog::Accepted) { + dialog.ApplyConfiguration(); + } +} + void QtControllerSelectorDialog::CallConfigureInputDialog() { const auto max_supported_players = parameters.enable_single_mode ? 1 : parameters.max_players; diff --git a/src/yuzu/applets/controller.h b/src/yuzu/applets/controller.h index 8fefecf05..a2ce03c8f 100644 --- a/src/yuzu/applets/controller.h +++ b/src/yuzu/applets/controller.h @@ -42,6 +42,9 @@ private: // Loads the current input configuration into the frontend applet. void LoadConfiguration(); + // Initializes the "Configure Vibration" Dialog. + void CallConfigureVibrationDialog(); + // Initializes the "Configure Input" Dialog. void CallConfigureInputDialog(); diff --git a/src/yuzu/applets/controller.ui b/src/yuzu/applets/controller.ui index cc27b8ef4..8e571ba8f 100644 --- a/src/yuzu/applets/controller.ui +++ b/src/yuzu/applets/controller.ui @@ -2329,11 +2329,11 @@ 3 - + 68 - 21 + 0 @@ -2342,17 +2342,11 @@ 16777215 - - % + + min-width: 68px; - - 1 - - - 100 - - - 100 + + Configure diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index 296c58f58..7f66f29aa 100644 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp @@ -300,6 +300,14 @@ void Config::ReadPlayerValue(std::size_t player_index) { static_cast(Settings::ControllerType::ProController)) .toUInt()); + player.vibration_enabled = + qt_config->value(QStringLiteral("%1vibration_enabled").arg(player_prefix), true) + .toBool(); + + player.vibration_strength = + qt_config->value(QStringLiteral("%1vibration_strength").arg(player_prefix), 100) + .toInt(); + player.body_color_left = qt_config ->value(QStringLiteral("%1body_color_left").arg(player_prefix), Settings::JOYCON_BODY_NEON_BLUE) @@ -493,6 +501,8 @@ void Config::ReadControlValues() { ReadSettingGlobal(Settings::values.use_docked_mode, QStringLiteral("use_docked_mode"), false); ReadSettingGlobal(Settings::values.vibration_enabled, QStringLiteral("vibration_enabled"), true); + ReadSettingGlobal(Settings::values.enable_accurate_vibrations, + QStringLiteral("enable_accurate_vibrations"), false); ReadSettingGlobal(Settings::values.motion_enabled, QStringLiteral("motion_enabled"), true); qt_config->endGroup(); @@ -983,6 +993,10 @@ void Config::SavePlayerValue(std::size_t player_index) { if (!player_prefix.isEmpty()) { WriteSetting(QStringLiteral("%1connected").arg(player_prefix), player.connected, false); + WriteSetting(QStringLiteral("%1vibration_enabled").arg(player_prefix), + player.vibration_enabled, true); + WriteSetting(QStringLiteral("%1vibration_strength").arg(player_prefix), + player.vibration_strength, 100); WriteSetting(QStringLiteral("%1body_color_left").arg(player_prefix), player.body_color_left, Settings::JOYCON_BODY_NEON_BLUE); WriteSetting(QStringLiteral("%1body_color_right").arg(player_prefix), @@ -1150,6 +1164,8 @@ void Config::SaveControlValues() { WriteSettingGlobal(QStringLiteral("use_docked_mode"), Settings::values.use_docked_mode, false); WriteSettingGlobal(QStringLiteral("vibration_enabled"), Settings::values.vibration_enabled, true); + WriteSettingGlobal(QStringLiteral("enable_accurate_vibrations"), + Settings::values.enable_accurate_vibrations, false); WriteSettingGlobal(QStringLiteral("motion_enabled"), Settings::values.motion_enabled, true); WriteSetting(QStringLiteral("motion_device"), QString::fromStdString(Settings::values.motion_device), diff --git a/src/yuzu/configuration/configure_input.cpp b/src/yuzu/configuration/configure_input.cpp index 9a4de4c5d..600cc03ae 100644 --- a/src/yuzu/configuration/configure_input.cpp +++ b/src/yuzu/configuration/configure_input.cpp @@ -23,6 +23,7 @@ #include "yuzu/configuration/configure_motion_touch.h" #include "yuzu/configuration/configure_mouse_advanced.h" #include "yuzu/configuration/configure_touchscreen_advanced.h" +#include "yuzu/configuration/configure_vibration.h" #include "yuzu/configuration/input_profiles.h" namespace { @@ -156,6 +157,9 @@ void ConfigureInput::Initialize(InputCommon::InputSubsystem* input_subsystem, CallConfigureDialog(*this, input_subsystem); }); + connect(ui->vibrationButton, &QPushButton::clicked, + [this] { CallConfigureDialog(*this); }); + connect(ui->motionButton, &QPushButton::clicked, [this, input_subsystem] { CallConfigureDialog(*this, input_subsystem); }); diff --git a/src/yuzu/configuration/configure_input.ui b/src/yuzu/configuration/configure_input.ui index cbd67d4c7..2707025e7 100644 --- a/src/yuzu/configuration/configure_input.ui +++ b/src/yuzu/configuration/configure_input.ui @@ -6,7 +6,7 @@ 0 0 - 700 + 680 540 @@ -195,11 +195,11 @@ 3 - + 68 - 21 + 0 @@ -208,17 +208,11 @@ 16777215 - - % + + min-width: 68px; - - 1 - - - 100 - - - 100 + + Configure @@ -272,7 +266,7 @@ - + 5 diff --git a/src/yuzu/configuration/configure_vibration.cpp b/src/yuzu/configuration/configure_vibration.cpp new file mode 100644 index 000000000..1c68f28f3 --- /dev/null +++ b/src/yuzu/configuration/configure_vibration.cpp @@ -0,0 +1,66 @@ +// Copyright 2020 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/settings.h" +#include "ui_configure_vibration.h" +#include "yuzu/configuration/configure_vibration.h" + +ConfigureVibration::ConfigureVibration(QWidget* parent) + : QDialog(parent), ui(std::make_unique()) { + ui->setupUi(this); + + vibration_groupboxes = { + ui->vibrationGroupPlayer1, ui->vibrationGroupPlayer2, ui->vibrationGroupPlayer3, + ui->vibrationGroupPlayer4, ui->vibrationGroupPlayer5, ui->vibrationGroupPlayer6, + ui->vibrationGroupPlayer7, ui->vibrationGroupPlayer8, + }; + + vibration_spinboxes = { + ui->vibrationSpinPlayer1, ui->vibrationSpinPlayer2, ui->vibrationSpinPlayer3, + ui->vibrationSpinPlayer4, ui->vibrationSpinPlayer5, ui->vibrationSpinPlayer6, + ui->vibrationSpinPlayer7, ui->vibrationSpinPlayer8, + }; + + const auto& players = Settings::values.players.GetValue(); + + for (std::size_t i = 0; i < NUM_PLAYERS; ++i) { + vibration_groupboxes[i]->setChecked(players[i].vibration_enabled); + vibration_spinboxes[i]->setValue(players[i].vibration_strength); + } + + ui->checkBoxAccurateVibration->setChecked( + Settings::values.enable_accurate_vibrations.GetValue()); + + if (!Settings::IsConfiguringGlobal()) { + ui->checkBoxAccurateVibration->setDisabled(true); + } + + RetranslateUI(); +} + +ConfigureVibration::~ConfigureVibration() = default; + +void ConfigureVibration::ApplyConfiguration() { + auto& players = Settings::values.players.GetValue(); + + for (std::size_t i = 0; i < NUM_PLAYERS; ++i) { + players[i].vibration_enabled = vibration_groupboxes[i]->isChecked(); + players[i].vibration_strength = vibration_spinboxes[i]->value(); + } + + Settings::values.enable_accurate_vibrations.SetValue( + ui->checkBoxAccurateVibration->isChecked()); +} + +void ConfigureVibration::changeEvent(QEvent* event) { + if (event->type() == QEvent::LanguageChange) { + RetranslateUI(); + } + + QDialog::changeEvent(event); +} + +void ConfigureVibration::RetranslateUI() { + ui->retranslateUi(this); +} diff --git a/src/yuzu/configuration/configure_vibration.h b/src/yuzu/configuration/configure_vibration.h new file mode 100644 index 000000000..37bbc2653 --- /dev/null +++ b/src/yuzu/configuration/configure_vibration.h @@ -0,0 +1,40 @@ +// Copyright 2020 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include +#include +#include + +class QGroupBox; +class QSpinBox; + +namespace Ui { +class ConfigureVibration; +} + +class ConfigureVibration : public QDialog { + Q_OBJECT + +public: + explicit ConfigureVibration(QWidget* parent); + ~ConfigureVibration() override; + + void ApplyConfiguration(); + +private: + void changeEvent(QEvent* event) override; + void RetranslateUI(); + + std::unique_ptr ui; + + static constexpr std::size_t NUM_PLAYERS = 8; + + // Groupboxes encapsulating the vibration strength spinbox. + std::array vibration_groupboxes; + + // Spinboxes representing the vibration strength percentage. + std::array vibration_spinboxes; +}; diff --git a/src/yuzu/configuration/configure_vibration.ui b/src/yuzu/configuration/configure_vibration.ui new file mode 100644 index 000000000..efdf317a9 --- /dev/null +++ b/src/yuzu/configuration/configure_vibration.ui @@ -0,0 +1,546 @@ + + + ConfigureVibration + + + + 0 + 0 + 364 + 242 + + + + Configure Vibration + + + + + + + + + Vibration + + + + 9 + + + 9 + + + 9 + + + 9 + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Player 1 + + + true + + + + 3 + + + 3 + + + 3 + + + 3 + + + + + + 68 + 21 + + + + + 68 + 16777215 + + + + % + + + 1 + + + 150 + + + 100 + + + + + + + + + + Player 2 + + + true + + + + 3 + + + 3 + + + 3 + + + 3 + + + + + + 68 + 21 + + + + + 68 + 16777215 + + + + % + + + 1 + + + 150 + + + 100 + + + + + + + + + + Player 3 + + + true + + + + 3 + + + 3 + + + 3 + + + 3 + + + + + + 68 + 21 + + + + + 68 + 16777215 + + + + % + + + 1 + + + 150 + + + 100 + + + + + + + + + + Player 4 + + + true + + + + 3 + + + 3 + + + 3 + + + 3 + + + + + + 68 + 21 + + + + + 68 + 16777215 + + + + % + + + 1 + + + 150 + + + 100 + + + + + + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Player 5 + + + true + + + + 3 + + + 3 + + + 3 + + + 3 + + + + + + 68 + 21 + + + + + 68 + 16777215 + + + + % + + + 1 + + + 150 + + + 100 + + + + + + + + + + Player 6 + + + true + + + + 3 + + + 3 + + + 3 + + + 3 + + + + + + 68 + 21 + + + + + 68 + 16777215 + + + + % + + + 1 + + + 150 + + + 100 + + + + + + + + + + Player 7 + + + true + + + + 3 + + + 3 + + + 3 + + + 3 + + + + + + 68 + 21 + + + + + 68 + 16777215 + + + + % + + + 1 + + + 150 + + + 100 + + + + + + + + + + Player 8 + + + true + + + + 3 + + + 3 + + + 3 + + + 3 + + + + + + 68 + 21 + + + + + 68 + 16777215 + + + + % + + + 1 + + + 150 + + + 100 + + + + + + + + + + + + + + + + Settings + + + + + + Enable Accurate Vibration + + + + + + + + + + Qt::Vertical + + + + 167 + 55 + + + + + + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + buttonBoxVibration + accepted() + ConfigureVibration + accept() + + + buttonBoxVibration + rejected() + ConfigureVibration + reject() + + + diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp index feee02fcd..e1adbbf2b 100644 --- a/src/yuzu_cmd/config.cpp +++ b/src/yuzu_cmd/config.cpp @@ -290,6 +290,8 @@ void Config::ReadValues() { Settings::values.vibration_enabled.SetValue( sdl2_config->GetBoolean("ControlsGeneral", "vibration_enabled", true)); + Settings::values.enable_accurate_vibrations.SetValue( + sdl2_config->GetBoolean("ControlsGeneral", "enable_accurate_vibrations", false)); Settings::values.motion_enabled.SetValue( sdl2_config->GetBoolean("ControlsGeneral", "motion_enabled", true)); Settings::values.touchscreen.enabled = diff --git a/src/yuzu_cmd/default_ini.h b/src/yuzu_cmd/default_ini.h index b6f6a3bb0..bcbbcd4ca 100644 --- a/src/yuzu_cmd/default_ini.h +++ b/src/yuzu_cmd/default_ini.h @@ -69,6 +69,10 @@ rstick= # 0: Disabled, 1 (default): Enabled vibration_enabled= +# Whether to enable or disable accurate vibrations +# 0 (default): Disabled, 1: Enabled +enable_accurate_vibrations= + # for motion input, the following devices are available: # - "motion_emu" (default) for emulating motion input from mouse input. Required parameters: # - "update_period": update period in milliseconds (default to 100) diff --git a/src/yuzu_tester/config.cpp b/src/yuzu_tester/config.cpp index 3a8a333f0..b6cdc7c1c 100644 --- a/src/yuzu_tester/config.cpp +++ b/src/yuzu_tester/config.cpp @@ -76,6 +76,7 @@ void Config::ReadValues() { } Settings::values.vibration_enabled.SetValue(true); + Settings::values.enable_accurate_vibrations.SetValue(false); Settings::values.motion_enabled.SetValue(true); Settings::values.touchscreen.enabled = ""; Settings::values.touchscreen.device = ""; From e9e1876e821b8bd1bb5c8254ec93e2cc479e16dd Mon Sep 17 00:00:00 2001 From: Morph <39850852+Morph1984@users.noreply.github.com> Date: Tue, 20 Oct 2020 13:55:25 -0400 Subject: [PATCH 26/37] input_common: Add VibrationDevice and VibrationDeviceFactory A vibration device is an input device that returns an unsigned byte as status. It represents whether the vibration device supports vibration or not. If the status returns 1, it supports vibration. Otherwise, it does not support vibration. --- src/core/frontend/input.h | 7 ++ src/core/hle/service/hid/controllers/npad.cpp | 48 +++++------ src/core/hle/service/hid/controllers/npad.h | 11 ++- src/core/hle/service/hid/hid.cpp | 1 + src/input_common/gcadapter/gc_adapter.cpp | 6 +- src/input_common/gcadapter/gc_adapter.h | 4 +- src/input_common/gcadapter/gc_poller.cpp | 50 +++++++++--- src/input_common/gcadapter/gc_poller.h | 11 +++ src/input_common/main.cpp | 5 ++ src/input_common/sdl/sdl_impl.cpp | 74 +++++++++++++---- src/input_common/sdl/sdl_impl.h | 2 + src/input_common/settings.cpp | 21 +++-- src/input_common/settings.h | 32 ++++++-- src/yuzu/applets/controller.cpp | 2 + src/yuzu/configuration/config.cpp | 61 +++++++++----- .../configuration/configure_input_player.cpp | 7 ++ .../configuration/configure_vibration.cpp | 80 +++++++++++++++++++ src/yuzu/configuration/configure_vibration.h | 3 + src/yuzu/main.cpp | 3 + 19 files changed, 327 insertions(+), 101 deletions(-) diff --git a/src/core/frontend/input.h b/src/core/frontend/input.h index fb2ce2514..25ac5af46 100644 --- a/src/core/frontend/input.h +++ b/src/core/frontend/input.h @@ -121,6 +121,13 @@ using ButtonDevice = InputDevice; */ using AnalogDevice = InputDevice>; +/** + * A vibration device is an input device that returns an unsigned byte as status. + * It represents whether the vibration device supports vibration or not. + * If the status returns 1, it supports vibration. Otherwise, it does not support vibration. + */ +using VibrationDevice = InputDevice; + /** * A motion status is an object that returns a tuple of accelerometer state vector, * gyroscope state vector, rotation state vector and orientation state matrix. diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp index dc9954377..27099de24 100644 --- a/src/core/hle/service/hid/controllers/npad.cpp +++ b/src/core/hle/service/hid/controllers/npad.cpp @@ -271,6 +271,10 @@ void Controller_NPad::OnLoadInputDevices() { std::transform(players[i].analogs.begin() + Settings::NativeAnalog::STICK_HID_BEGIN, players[i].analogs.begin() + Settings::NativeAnalog::STICK_HID_END, sticks[i].begin(), Input::CreateDevice); + std::transform(players[i].vibrations.begin() + + Settings::NativeVibration::VIBRATION_HID_BEGIN, + players[i].vibrations.begin() + Settings::NativeVibration::VIBRATION_HID_END, + vibrations[i].begin(), Input::CreateDevice); std::transform(players[i].motions.begin() + Settings::NativeMotion::MOTION_HID_BEGIN, players[i].motions.begin() + Settings::NativeMotion::MOTION_HID_END, motions[i].begin(), Input::CreateDevice); @@ -278,8 +282,10 @@ void Controller_NPad::OnLoadInputDevices() { } void Controller_NPad::OnRelease() { - for (std::size_t index = 0; index < connected_controllers.size(); ++index) { - VibrateControllerAtIndex(index, {}); + for (std::size_t npad_idx = 0; npad_idx < vibrations.size(); ++npad_idx) { + for (std::size_t device_idx = 0; device_idx < vibrations[npad_idx].size(); ++device_idx) { + VibrateControllerAtIndex(npad_idx, device_idx); + } } } @@ -674,9 +680,9 @@ void Controller_NPad::SetNpadMode(u32 npad_id, NpadAssignments assignment_mode) } } -bool Controller_NPad::VibrateControllerAtIndex(std::size_t npad_index, +bool Controller_NPad::VibrateControllerAtIndex(std::size_t npad_index, std::size_t device_index, const VibrationValue& vibration_value) { - if (!connected_controllers[npad_index].is_connected) { + if (!connected_controllers[npad_index].is_connected || !vibrations[npad_index][device_index]) { return false; } @@ -686,10 +692,7 @@ bool Controller_NPad::VibrateControllerAtIndex(std::size_t npad_index, return false; } - using namespace Settings::NativeButton; - const auto& button_state = buttons[npad_index]; - - return button_state[A - BUTTON_HID_BEGIN]->SetRumblePlay( + return vibrations[npad_index][device_index]->SetRumblePlay( std::min(vibration_value.amp_low * player.vibration_strength / 100.0f, 1.0f), vibration_value.freq_low, std::min(vibration_value.amp_high * player.vibration_strength / 100.0f, 1.0f), @@ -717,6 +720,11 @@ void Controller_NPad::VibrateControllers(const std::vector& vibrat continue; } + if (vibration_device_handles[i].device_index == DeviceIndex::None) { + UNREACHABLE_MSG("DeviceIndex should never be None!"); + continue; + } + // Some games try to send mismatched parameters in the device handle, block these. if ((connected_controllers[npad_index].type == NPadControllerType::JoyLeft && (vibration_device_handles[i].npad_type == NpadType::JoyconRight || @@ -747,28 +755,8 @@ void Controller_NPad::VibrateControllers(const std::vector& vibrat continue; } - // TODO: Vibrate left/right vibration motors independently if possible. - if (VibrateControllerAtIndex(npad_index, vibration_values[i])) { - switch (connected_controllers[npad_index].type) { - case NPadControllerType::None: - UNREACHABLE(); - break; - case NPadControllerType::ProController: - case NPadControllerType::Handheld: - case NPadControllerType::JoyDual: - // Since we can't vibrate motors independently yet, we can reduce state changes by - // assigning all 3 device indices the current vibration value. - latest_vibration_values[npad_index][0] = vibration_values[i]; - latest_vibration_values[npad_index][1] = vibration_values[i]; - latest_vibration_values[npad_index][2] = vibration_values[i]; - break; - case NPadControllerType::JoyLeft: - case NPadControllerType::JoyRight: - case NPadControllerType::Pokeball: - default: - latest_vibration_values[npad_index][device_index] = vibration_values[i]; - break; - } + if (VibrateControllerAtIndex(npad_index, device_index, vibration_values[i])) { + latest_vibration_values[npad_index][device_index] = vibration_values[i]; } } } diff --git a/src/core/hle/service/hid/controllers/npad.h b/src/core/hle/service/hid/controllers/npad.h index 576ef1558..3ae9fb8e6 100644 --- a/src/core/hle/service/hid/controllers/npad.h +++ b/src/core/hle/service/hid/controllers/npad.h @@ -148,7 +148,8 @@ public: void SetNpadMode(u32 npad_id, NpadAssignments assignment_mode); - bool VibrateControllerAtIndex(std::size_t npad_index, const VibrationValue& vibration_value); + bool VibrateControllerAtIndex(std::size_t npad_index, std::size_t device_index, + const VibrationValue& vibration_value = {}); void VibrateControllers(const std::vector& vibration_device_handles, const std::vector& vibration_values); @@ -399,18 +400,22 @@ private: using StickArray = std::array< std::array, Settings::NativeAnalog::NUM_STICKS_HID>, 10>; + using VibrationArray = std::array, + Settings::NativeVibration::NUM_VIBRATIONS_HID>, + 10>; using MotionArray = std::array< - std::array, Settings::NativeMotion::NUM_MOTION_HID>, + std::array, Settings::NativeMotion::NUM_MOTIONS_HID>, 10>; ButtonArray buttons; StickArray sticks; + VibrationArray vibrations; MotionArray motions; std::vector supported_npad_id_types{}; NpadHoldType hold_type{NpadHoldType::Vertical}; NpadHandheldActivationMode handheld_activation_mode{NpadHandheldActivationMode::Dual}; // Each controller should have their own styleset changed event std::array styleset_changed_events; - std::array, 10> latest_vibration_values; + std::array, 10> latest_vibration_values{}; std::array connected_controllers{}; std::array unintended_home_button_input_protection{}; GyroscopeZeroDriftMode gyroscope_zero_drift_mode{GyroscopeZeroDriftMode::Standard}; diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index e88f30d6a..1d882a977 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp @@ -998,6 +998,7 @@ void Hid::GetVibrationDeviceInfo(Kernel::HLERequestContext& ctx) { break; case Controller_NPad::DeviceIndex::None: default: + UNREACHABLE_MSG("DeviceIndex should never be None!"); vibration_device_info.position = VibrationDevicePosition::None; break; } diff --git a/src/input_common/gcadapter/gc_adapter.cpp b/src/input_common/gcadapter/gc_adapter.cpp index b912188b6..d80195c82 100644 --- a/src/input_common/gcadapter/gc_adapter.cpp +++ b/src/input_common/gcadapter/gc_adapter.cpp @@ -230,10 +230,8 @@ void Adapter::SendVibrations() { vibration_changed = false; } -bool Adapter::RumblePlay(std::size_t port, f32 amplitude) { - amplitude = std::clamp(amplitude, 0.0f, 1.0f); - const auto raw_amp = static_cast(amplitude * 0x8); - pads[port].rumble_amplitude = raw_amp; +bool Adapter::RumblePlay(std::size_t port, u8 amplitude) { + pads[port].rumble_amplitude = amplitude; return rumble_enabled; } diff --git a/src/input_common/gcadapter/gc_adapter.h b/src/input_common/gcadapter/gc_adapter.h index d28dcfad3..f1256c9da 100644 --- a/src/input_common/gcadapter/gc_adapter.h +++ b/src/input_common/gcadapter/gc_adapter.h @@ -77,8 +77,8 @@ public: Adapter(); ~Adapter(); - /// Request a vibration for a controlelr - bool RumblePlay(std::size_t port, f32 amplitude); + /// Request a vibration for a controller + bool RumblePlay(std::size_t port, u8 amplitude); /// Used for polling void BeginConfiguration(); diff --git a/src/input_common/gcadapter/gc_poller.cpp b/src/input_common/gcadapter/gc_poller.cpp index 6bd6f57fc..fe57c13a5 100644 --- a/src/input_common/gcadapter/gc_poller.cpp +++ b/src/input_common/gcadapter/gc_poller.cpp @@ -15,7 +15,7 @@ namespace InputCommon { class GCButton final : public Input::ButtonDevice { public: - explicit GCButton(u32 port_, s32 button_, GCAdapter::Adapter* adapter) + explicit GCButton(u32 port_, s32 button_, const GCAdapter::Adapter* adapter) : port(port_), button(button_), gcadapter(adapter) {} ~GCButton() override; @@ -27,18 +27,10 @@ public: return false; } - bool SetRumblePlay(f32 amp_high, f32 amp_low, f32 freq_high, f32 freq_low) const override { - const float amplitude = amp_high + amp_low > 2.0f ? 1.0f : (amp_high + amp_low) * 0.5f; - const auto new_amp = - static_cast(pow(amplitude, 0.5f) * (3.0f - 2.0f * pow(amplitude, 0.15f))); - - return gcadapter->RumblePlay(port, new_amp); - } - private: const u32 port; const s32 button; - GCAdapter::Adapter* gcadapter; + const GCAdapter::Adapter* gcadapter; }; class GCAxisButton final : public Input::ButtonDevice { @@ -299,4 +291,42 @@ Common::ParamPackage GCAnalogFactory::GetNextInput() { return params; } +class GCVibration final : public Input::VibrationDevice { +public: + explicit GCVibration(u32 port_, GCAdapter::Adapter* adapter) + : port(port_), gcadapter(adapter) {} + + u8 GetStatus() const override { + return gcadapter->RumblePlay(port, 0); + } + + bool SetRumblePlay(f32 amp_low, f32 freq_low, f32 amp_high, f32 freq_high) const override { + const auto mean_amplitude = (amp_low + amp_high) * 0.5f; + const auto processed_amplitude = static_cast( + pow(mean_amplitude, 0.5f) * (3.0f - 2.0f * pow(mean_amplitude, 0.15f)) * 0x8); + + return gcadapter->RumblePlay(port, processed_amplitude); + } + +private: + const u32 port; + GCAdapter::Adapter* gcadapter; +}; + +/// An vibration device factory that creates vibration devices from GC Adapter +GCVibrationFactory::GCVibrationFactory(std::shared_ptr adapter_) + : adapter(std::move(adapter_)) {} + +/** + * Creates a vibration device from a joystick + * @param params contains parameters for creating the device: + * - "port": the nth gcpad on the adapter + */ +std::unique_ptr GCVibrationFactory::Create( + const Common::ParamPackage& params) { + const auto port = static_cast(params.Get("port", 0)); + + return std::make_unique(port, adapter.get()); +} + } // namespace InputCommon diff --git a/src/input_common/gcadapter/gc_poller.h b/src/input_common/gcadapter/gc_poller.h index 0527f328f..d1271e3ea 100644 --- a/src/input_common/gcadapter/gc_poller.h +++ b/src/input_common/gcadapter/gc_poller.h @@ -64,4 +64,15 @@ private: bool polling = false; }; +/// A vibration device factory creates vibration devices from GC Adapter +class GCVibrationFactory final : public Input::Factory { +public: + explicit GCVibrationFactory(std::shared_ptr adapter_); + + std::unique_ptr Create(const Common::ParamPackage& params) override; + +private: + std::shared_ptr adapter; +}; + } // namespace InputCommon diff --git a/src/input_common/main.cpp b/src/input_common/main.cpp index b438482cc..e59ad4ff5 100644 --- a/src/input_common/main.cpp +++ b/src/input_common/main.cpp @@ -28,6 +28,8 @@ struct InputSubsystem::Impl { Input::RegisterFactory("gcpad", gcbuttons); gcanalog = std::make_shared(gcadapter); Input::RegisterFactory("gcpad", gcanalog); + gcvibration = std::make_shared(gcadapter); + Input::RegisterFactory("gcpad", gcvibration); keyboard = std::make_shared(); Input::RegisterFactory("keyboard", keyboard); @@ -64,9 +66,11 @@ struct InputSubsystem::Impl { #endif Input::UnregisterFactory("gcpad"); Input::UnregisterFactory("gcpad"); + Input::UnregisterFactory("gcpad"); gcbuttons.reset(); gcanalog.reset(); + gcvibration.reset(); Input::UnregisterFactory("cemuhookudp"); Input::UnregisterFactory("cemuhookudp"); @@ -142,6 +146,7 @@ struct InputSubsystem::Impl { #endif std::shared_ptr gcbuttons; std::shared_ptr gcanalog; + std::shared_ptr gcvibration; std::shared_ptr udpmotion; std::shared_ptr udptouch; std::shared_ptr udp; diff --git a/src/input_common/sdl/sdl_impl.cpp b/src/input_common/sdl/sdl_impl.cpp index 18fb2ac5e..a2a83cdc9 100644 --- a/src/input_common/sdl/sdl_impl.cpp +++ b/src/input_common/sdl/sdl_impl.cpp @@ -85,16 +85,17 @@ public: using std::chrono::milliseconds; using std::chrono::steady_clock; - // Prevent vibrations less than 10ms apart from each other. - if (duration_cast(steady_clock::now() - last_vibration) < milliseconds(10)) { + // Block non-zero vibrations less than 10ms apart from each other. + if ((amp_low != 0 || amp_high != 0) && + duration_cast(steady_clock::now() - last_vibration) < milliseconds(10)) { return false; - }; + } last_vibration = steady_clock::now(); - if (sdl_controller != nullptr) { + if (sdl_controller) { return SDL_GameControllerRumble(sdl_controller.get(), amp_low, amp_high, 0) == 0; - } else if (sdl_joystick != nullptr) { + } else if (sdl_joystick) { return SDL_JoystickRumble(sdl_joystick.get(), amp_low, amp_high, 0) == 0; } @@ -321,14 +322,6 @@ public: return joystick->GetButton(button); } - bool SetRumblePlay(f32 amp_low, f32 freq_low, f32 amp_high, f32 freq_high) const override { - const u16 processed_amp_low = - static_cast(pow(amp_low, 0.5f) * (3.0f - 2.0f * pow(amp_low, 0.15f)) * 0xFFFF); - const u16 processed_amp_high = - static_cast(pow(amp_high, 0.5f) * (3.0f - 2.0f * pow(amp_high, 0.15f)) * 0xFFFF); - return joystick->RumblePlay(processed_amp_low, processed_amp_high); - } - private: std::shared_ptr joystick; int button; @@ -412,6 +405,32 @@ private: const float range; }; +class SDLVibration final : public Input::VibrationDevice { +public: + explicit SDLVibration(std::shared_ptr joystick_) + : joystick(std::move(joystick_)) {} + + u8 GetStatus() const override { + joystick->RumblePlay(1, 1); + return joystick->RumblePlay(0, 0); + } + + bool SetRumblePlay(f32 amp_low, f32 freq_low, f32 amp_high, f32 freq_high) const override { + const auto process_amplitude = [](f32 amplitude) { + return static_cast(std::pow(amplitude, 0.5f) * + (3.0f - 2.0f * std::pow(amplitude, 0.15f)) * 0xFFFF); + }; + + const auto processed_amp_low = process_amplitude(amp_low); + const auto processed_amp_high = process_amplitude(amp_high); + + return joystick->RumblePlay(processed_amp_low, processed_amp_high); + } + +private: + std::shared_ptr joystick; +}; + class SDLDirectionMotion final : public Input::MotionDevice { public: explicit SDLDirectionMotion(std::shared_ptr joystick_, int hat_, Uint8 direction_) @@ -554,7 +573,7 @@ class SDLAnalogFactory final : public Input::Factory { public: explicit SDLAnalogFactory(SDLState& state_) : state(state_) {} /** - * Creates analog device from joystick axes + * Creates an analog device from joystick axes * @param params contains parameters for creating the device: * - "guid": the guid of the joystick to bind * - "port": the nth joystick of the same type @@ -580,6 +599,26 @@ private: SDLState& state; }; +/// An vibration device factory that creates vibration devices from SDL joystick +class SDLVibrationFactory final : public Input::Factory { +public: + explicit SDLVibrationFactory(SDLState& state_) : state(state_) {} + /** + * Creates a vibration device from a joystick + * @param params contains parameters for creating the device: + * - "guid": the guid of the joystick to bind + * - "port": the nth joystick of the same type + */ + std::unique_ptr Create(const Common::ParamPackage& params) override { + const std::string guid = params.Get("guid", "0"); + const int port = params.Get("port", 0); + return std::make_unique(state.GetSDLJoystickByGUID(guid, port)); + } + +private: + SDLState& state; +}; + /// A motion device factory that creates motion devices from SDL joystick class SDLMotionFactory final : public Input::Factory { public: @@ -646,11 +685,13 @@ private: SDLState::SDLState() { using namespace Input; - analog_factory = std::make_shared(*this); button_factory = std::make_shared(*this); + analog_factory = std::make_shared(*this); + vibration_factory = std::make_shared(*this); motion_factory = std::make_shared(*this); - RegisterFactory("sdl", analog_factory); RegisterFactory("sdl", button_factory); + RegisterFactory("sdl", analog_factory); + RegisterFactory("sdl", vibration_factory); RegisterFactory("sdl", motion_factory); // If the frontend is going to manage the event loop, then we don't start one here @@ -687,6 +728,7 @@ SDLState::~SDLState() { using namespace Input; UnregisterFactory("sdl"); UnregisterFactory("sdl"); + UnregisterFactory("sdl"); UnregisterFactory("sdl"); CloseJoysticks(); diff --git a/src/input_common/sdl/sdl_impl.h b/src/input_common/sdl/sdl_impl.h index b9bb4dc56..08044b00d 100644 --- a/src/input_common/sdl/sdl_impl.h +++ b/src/input_common/sdl/sdl_impl.h @@ -22,6 +22,7 @@ namespace InputCommon::SDL { class SDLAnalogFactory; class SDLButtonFactory; class SDLMotionFactory; +class SDLVibrationFactory; class SDLJoystick; class SDLState : public State { @@ -72,6 +73,7 @@ private: std::shared_ptr button_factory; std::shared_ptr analog_factory; + std::shared_ptr vibration_factory; std::shared_ptr motion_factory; bool start_thread = false; diff --git a/src/input_common/settings.cpp b/src/input_common/settings.cpp index b66c05856..557e7a9a0 100644 --- a/src/input_common/settings.cpp +++ b/src/input_common/settings.cpp @@ -14,13 +14,6 @@ const std::array mapping = {{ }}; } -namespace NativeMotion { -const std::array mapping = {{ - "motionleft", - "motionright", -}}; -} - namespace NativeAnalog { const std::array mapping = {{ "lstick", @@ -28,6 +21,20 @@ const std::array mapping = {{ }}; } +namespace NativeVibration { +const std::array mapping = {{ + "left_vibration_device", + "right_vibration_device", +}}; +} + +namespace NativeMotion { +const std::array mapping = {{ + "motionleft", + "motionright", +}}; +} + namespace NativeMouseButton { const std::array mapping = {{ "left", diff --git a/src/input_common/settings.h b/src/input_common/settings.h index 2763ed991..75486554b 100644 --- a/src/input_common/settings.h +++ b/src/input_common/settings.h @@ -66,17 +66,32 @@ constexpr int NUM_STICKS_HID = NumAnalogs; extern const std::array mapping; } // namespace NativeAnalog +namespace NativeVibration { +enum Values : int { + LeftVibrationDevice, + RightVibrationDevice, + + NumVibrations, +}; + +constexpr int VIBRATION_HID_BEGIN = LeftVibrationDevice; +constexpr int VIBRATION_HID_END = NumVibrations; +constexpr int NUM_VIBRATIONS_HID = NumVibrations; + +extern const std::array mapping; +}; // namespace NativeVibration + namespace NativeMotion { enum Values : int { - MOTIONLEFT, - MOTIONRIGHT, + MotionLeft, + MotionRight, NumMotions, }; -constexpr int MOTION_HID_BEGIN = MOTIONLEFT; +constexpr int MOTION_HID_BEGIN = MotionLeft; constexpr int MOTION_HID_END = NumMotions; -constexpr int NUM_MOTION_HID = NumMotions; +constexpr int NUM_MOTIONS_HID = NumMotions; extern const std::array mapping; } // namespace NativeMotion @@ -305,9 +320,11 @@ constexpr int NUM_KEYBOARD_MODS_HID = NumKeyboardMods; } // namespace NativeKeyboard -using ButtonsRaw = std::array; using AnalogsRaw = std::array; -using MotionRaw = std::array; +using ButtonsRaw = std::array; +using MotionsRaw = std::array; +using VibrationsRaw = std::array; + using MouseButtonsRaw = std::array; using KeyboardKeysRaw = std::array; using KeyboardModsRaw = std::array; @@ -330,7 +347,8 @@ struct PlayerInput { ControllerType controller_type; ButtonsRaw buttons; AnalogsRaw analogs; - MotionRaw motions; + VibrationsRaw vibrations; + MotionsRaw motions; bool vibration_enabled; int vibration_strength; diff --git a/src/yuzu/applets/controller.cpp b/src/yuzu/applets/controller.cpp index c5e671309..cdcd3d28d 100644 --- a/src/yuzu/applets/controller.cpp +++ b/src/yuzu/applets/controller.cpp @@ -478,6 +478,8 @@ void QtControllerSelectorDialog::UpdateControllerState(std::size_t player_index) return; } + ConfigureVibration::SetVibrationDevices(player_index); + // Player 1 and Handheld auto& handheld = Settings::values.players.GetValue()[8]; // If Handheld is selected, copy all the settings from Player 1 to Handheld. diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index 7f66f29aa..6fa842cd5 100644 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp @@ -344,21 +344,6 @@ void Config::ReadPlayerValue(std::size_t player_index) { } } - for (int i = 0; i < Settings::NativeMotion::NumMotions; ++i) { - const std::string default_param = InputCommon::GenerateKeyboardParam(default_motions[i]); - auto& player_motions = player.motions[i]; - - player_motions = qt_config - ->value(QStringLiteral("%1").arg(player_prefix) + - QString::fromUtf8(Settings::NativeMotion::mapping[i]), - QString::fromStdString(default_param)) - .toString() - .toStdString(); - if (player_motions.empty()) { - player_motions = default_param; - } - } - for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { const std::string default_param = InputCommon::GenerateAnalogParamFromKeys( default_analogs[i][0], default_analogs[i][1], default_analogs[i][2], @@ -375,6 +360,33 @@ void Config::ReadPlayerValue(std::size_t player_index) { player_analogs = default_param; } } + + for (int i = 0; i < Settings::NativeVibration::NumVibrations; ++i) { + auto& player_vibrations = player.vibrations[i]; + + player_vibrations = + qt_config + ->value(QStringLiteral("%1").arg(player_prefix) + + QString::fromUtf8(Settings::NativeVibration::mapping[i]), + QString{}) + .toString() + .toStdString(); + } + + for (int i = 0; i < Settings::NativeMotion::NumMotions; ++i) { + const std::string default_param = InputCommon::GenerateKeyboardParam(default_motions[i]); + auto& player_motions = player.motions[i]; + + player_motions = qt_config + ->value(QStringLiteral("%1").arg(player_prefix) + + QString::fromUtf8(Settings::NativeMotion::mapping[i]), + QString::fromStdString(default_param)) + .toString() + .toStdString(); + if (player_motions.empty()) { + player_motions = default_param; + } + } } void Config::ReadDebugValues() { @@ -1014,13 +1026,6 @@ void Config::SavePlayerValue(std::size_t player_index) { QString::fromStdString(player.buttons[i]), QString::fromStdString(default_param)); } - for (int i = 0; i < Settings::NativeMotion::NumMotions; ++i) { - const std::string default_param = InputCommon::GenerateKeyboardParam(default_motions[i]); - WriteSetting(QStringLiteral("%1").arg(player_prefix) + - QString::fromStdString(Settings::NativeMotion::mapping[i]), - QString::fromStdString(player.motions[i]), - QString::fromStdString(default_param)); - } for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { const std::string default_param = InputCommon::GenerateAnalogParamFromKeys( default_analogs[i][0], default_analogs[i][1], default_analogs[i][2], @@ -1030,6 +1035,18 @@ void Config::SavePlayerValue(std::size_t player_index) { QString::fromStdString(player.analogs[i]), QString::fromStdString(default_param)); } + for (int i = 0; i < Settings::NativeVibration::NumVibrations; ++i) { + WriteSetting(QStringLiteral("%1").arg(player_prefix) + + QString::fromStdString(Settings::NativeVibration::mapping[i]), + QString::fromStdString(player.vibrations[i]), QString{}); + } + for (int i = 0; i < Settings::NativeMotion::NumMotions; ++i) { + const std::string default_param = InputCommon::GenerateKeyboardParam(default_motions[i]); + WriteSetting(QStringLiteral("%1").arg(player_prefix) + + QString::fromStdString(Settings::NativeMotion::mapping[i]), + QString::fromStdString(player.motions[i]), + QString::fromStdString(default_param)); + } } void Config::SaveDebugValues() { diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp index 460ff08a4..3e785c224 100644 --- a/src/yuzu/configuration/configure_input_player.cpp +++ b/src/yuzu/configuration/configure_input_player.cpp @@ -22,6 +22,7 @@ #include "ui_configure_input_player.h" #include "yuzu/configuration/config.h" #include "yuzu/configuration/configure_input_player.h" +#include "yuzu/configuration/configure_vibration.h" #include "yuzu/configuration/input_profiles.h" #include "yuzu/util/limitable_input_dialog.h" @@ -39,6 +40,10 @@ namespace { void UpdateController(Settings::ControllerType controller_type, std::size_t npad_index, bool connected) { + auto& player = Settings::values.players.GetValue()[npad_index]; + player.controller_type = controller_type; + player.connected = connected; + Core::System& system{Core::System::GetInstance()}; if (!system.IsPoweredOn()) { return; @@ -565,6 +570,8 @@ void ConfigureInputPlayer::ApplyConfiguration() { static_cast(ui->comboControllerType->currentIndex()); player.connected = ui->groupConnectedController->isChecked(); + ConfigureVibration::SetVibrationDevices(player_index); + // Player 2-8 if (player_index != 0) { UpdateController(player.controller_type, player_index, player.connected); diff --git a/src/yuzu/configuration/configure_vibration.cpp b/src/yuzu/configuration/configure_vibration.cpp index 1c68f28f3..714db5b80 100644 --- a/src/yuzu/configuration/configure_vibration.cpp +++ b/src/yuzu/configuration/configure_vibration.cpp @@ -2,6 +2,12 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include +#include + +#include + +#include "common/param_package.h" #include "core/settings.h" #include "ui_configure_vibration.h" #include "yuzu/configuration/configure_vibration.h" @@ -53,6 +59,80 @@ void ConfigureVibration::ApplyConfiguration() { ui->checkBoxAccurateVibration->isChecked()); } +void ConfigureVibration::SetVibrationDevices(std::size_t player_index) { + using namespace Settings::NativeButton; + static constexpr std::array, 2> buttons{{ + {DLeft, DUp, DRight, DDown, L, ZL}, // Left Buttons + {A, B, X, Y, R, ZR}, // Right Buttons + }}; + + auto& player = Settings::values.players.GetValue()[player_index]; + + for (std::size_t device_idx = 0; device_idx < buttons.size(); ++device_idx) { + std::unordered_map params_count; + + for (const auto button_index : buttons[device_idx]) { + const auto& player_button = player.buttons[button_index]; + + if (params_count.find(player_button) != params_count.end()) { + ++params_count[player_button]; + continue; + } + + params_count.insert_or_assign(player_button, 1); + } + + const auto it = std::max_element( + params_count.begin(), params_count.end(), + [](const auto& lhs, const auto& rhs) { return lhs.second < rhs.second; }); + + auto& vibration_param_str = player.vibrations[device_idx]; + vibration_param_str.clear(); + + if (it->first.empty()) { + continue; + } + + const auto param = Common::ParamPackage(it->first); + + const auto engine = param.Get("engine", ""); + const auto guid = param.Get("guid", ""); + const auto port = param.Get("port", ""); + + if (engine.empty() || engine == "keyboard") { + continue; + } + + vibration_param_str += fmt::format("engine:{}", engine); + + if (!port.empty()) { + vibration_param_str += fmt::format(",port:{}", port); + } + if (!guid.empty()) { + vibration_param_str += fmt::format(",guid:{}", guid); + } + } + + if (player.vibrations[0] != player.vibrations[1]) { + return; + } + + if (!player.vibrations[0].empty() && + player.controller_type != Settings::ControllerType::RightJoycon) { + player.vibrations[1].clear(); + } else if (!player.vibrations[1].empty() && + player.controller_type == Settings::ControllerType::RightJoycon) { + player.vibrations[0].clear(); + } +} + +void ConfigureVibration::SetAllVibrationDevices() { + // Set vibration devices for all player indices including handheld + for (std::size_t player_idx = 0; player_idx < NUM_PLAYERS + 1; ++player_idx) { + SetVibrationDevices(player_idx); + } +} + void ConfigureVibration::changeEvent(QEvent* event) { if (event->type() == QEvent::LanguageChange) { RetranslateUI(); diff --git a/src/yuzu/configuration/configure_vibration.h b/src/yuzu/configuration/configure_vibration.h index 37bbc2653..07411a86f 100644 --- a/src/yuzu/configuration/configure_vibration.h +++ b/src/yuzu/configuration/configure_vibration.h @@ -24,6 +24,9 @@ public: void ApplyConfiguration(); + static void SetVibrationDevices(std::size_t player_index); + static void SetAllVibrationDevices(); + private: void changeEvent(QEvent* event) override; void RetranslateUI(); diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 54a46827f..76a5c32f4 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -18,6 +18,7 @@ #include "applets/web_browser.h" #include "configuration/configure_input.h" #include "configuration/configure_per_game.h" +#include "configuration/configure_vibration.h" #include "core/file_sys/vfs.h" #include "core/file_sys/vfs_real.h" #include "core/frontend/applets/controller.h" @@ -1096,6 +1097,8 @@ void GMainWindow::BootGame(const QString& filename) { Config per_game_config(fmt::format("{:016X}", title_id), Config::ConfigType::PerGameConfig); } + ConfigureVibration::SetAllVibrationDevices(); + Settings::LogSettings(); if (UISettings::values.select_user_on_boot) { From 978ca65f59f1388358ce0d45de41816e8b0aa887 Mon Sep 17 00:00:00 2001 From: Morph <39850852+Morph1984@users.noreply.github.com> Date: Thu, 22 Oct 2020 06:55:23 -0400 Subject: [PATCH 27/37] hid: Implement InitializeVibrationDevice and IsVibrationDeviceMounted --- src/core/hle/service/hid/controllers/npad.cpp | 42 ++++++++++++++++++- src/core/hle/service/hid/controllers/npad.h | 7 ++++ src/core/hle/service/hid/hid.cpp | 29 ++++++++----- 3 files changed, 66 insertions(+), 12 deletions(-) diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp index 27099de24..ecc33bc08 100644 --- a/src/core/hle/service/hid/controllers/npad.cpp +++ b/src/core/hle/service/hid/controllers/npad.cpp @@ -278,6 +278,9 @@ void Controller_NPad::OnLoadInputDevices() { std::transform(players[i].motions.begin() + Settings::NativeMotion::MOTION_HID_BEGIN, players[i].motions.begin() + Settings::NativeMotion::MOTION_HID_END, motions[i].begin(), Input::CreateDevice); + for (std::size_t device_idx = 0; device_idx < vibrations[i].size(); ++device_idx) { + InitializeVibrationDeviceAtIndex(i, device_idx); + } } } @@ -689,6 +692,14 @@ bool Controller_NPad::VibrateControllerAtIndex(std::size_t npad_index, std::size const auto& player = Settings::values.players.GetValue()[npad_index]; if (!player.vibration_enabled) { + if (latest_vibration_values[npad_index][device_index].amp_low != 0.0f || + latest_vibration_values[npad_index][device_index].amp_high != 0.0f) { + // Send an empty vibration to stop any vibrations. + vibrations[npad_index][device_index]->SetRumblePlay(0.0f, 160.0f, 0.0f, 320.0f); + // Then reset the vibration value to its default value. + latest_vibration_values[npad_index][device_index] = {}; + } + return false; } @@ -716,7 +727,8 @@ void Controller_NPad::VibrateControllers(const std::vector& vibrat const auto device_index = static_cast(vibration_device_handles[i].device_index); - if (!connected_controllers[npad_index].is_connected) { + if (!vibration_devices_mounted[npad_index][device_index] || + !connected_controllers[npad_index].is_connected) { continue; } @@ -768,6 +780,28 @@ Controller_NPad::VibrationValue Controller_NPad::GetLastVibration( return latest_vibration_values[npad_index][device_index]; } +void Controller_NPad::InitializeVibrationDevice(const DeviceHandle& vibration_device_handle) { + const auto npad_index = NPadIdToIndex(vibration_device_handle.npad_id); + const auto device_index = static_cast(vibration_device_handle.device_index); + InitializeVibrationDeviceAtIndex(npad_index, device_index); +} + +void Controller_NPad::InitializeVibrationDeviceAtIndex(std::size_t npad_index, + std::size_t device_index) { + if (vibrations[npad_index][device_index]) { + vibration_devices_mounted[npad_index][device_index] = + vibrations[npad_index][device_index]->GetStatus() == 1; + } else { + vibration_devices_mounted[npad_index][device_index] = false; + } +} + +bool Controller_NPad::IsVibrationDeviceMounted(const DeviceHandle& vibration_device_handle) const { + const auto npad_index = NPadIdToIndex(vibration_device_handle.npad_id); + const auto device_index = static_cast(vibration_device_handle.device_index); + return vibration_devices_mounted[npad_index][device_index]; +} + std::shared_ptr Controller_NPad::GetStyleSetChangedEvent(u32 npad_id) const { const auto& styleset_event = styleset_changed_events[NPadIdToIndex(npad_id)]; return styleset_event.readable; @@ -809,6 +843,12 @@ void Controller_NPad::DisconnectNpad(u32 npad_id) { } void Controller_NPad::DisconnectNpadAtIndex(std::size_t npad_index) { + for (std::size_t device_idx = 0; device_idx < vibrations[npad_index].size(); ++device_idx) { + // Send an empty vibration to stop any vibrations. + VibrateControllerAtIndex(npad_index, device_idx); + vibration_devices_mounted[npad_index][device_idx] = false; + } + Settings::values.players.GetValue()[npad_index].connected = false; connected_controllers[npad_index].is_connected = false; diff --git a/src/core/hle/service/hid/controllers/npad.h b/src/core/hle/service/hid/controllers/npad.h index 3ae9fb8e6..30e3cb02f 100644 --- a/src/core/hle/service/hid/controllers/npad.h +++ b/src/core/hle/service/hid/controllers/npad.h @@ -156,6 +156,12 @@ public: VibrationValue GetLastVibration(const DeviceHandle& vibration_device_handle) const; + void InitializeVibrationDevice(const DeviceHandle& vibration_device_handle); + + void InitializeVibrationDeviceAtIndex(std::size_t npad_index, std::size_t device_index); + + bool IsVibrationDeviceMounted(const DeviceHandle& vibration_device_handle) const; + std::shared_ptr GetStyleSetChangedEvent(u32 npad_id) const; void SignalStyleSetChangedEvent(u32 npad_id) const; @@ -416,6 +422,7 @@ private: // Each controller should have their own styleset changed event std::array styleset_changed_events; std::array, 10> latest_vibration_values{}; + std::array, 10> vibration_devices_mounted{}; std::array connected_controllers{}; std::array unintended_home_button_input_protection{}; GyroscopeZeroDriftMode gyroscope_zero_drift_mode{GyroscopeZeroDriftMode::Standard}; diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index 1d882a977..ecaa847b2 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp @@ -139,7 +139,8 @@ void IAppletResource::UpdateMotion(std::uintptr_t user_data, std::chrono::nanose class IActiveVibrationDeviceList final : public ServiceFramework { public: - IActiveVibrationDeviceList() : ServiceFramework("IActiveVibrationDeviceList") { + explicit IActiveVibrationDeviceList(std::shared_ptr applet_resource_) + : ServiceFramework("IActiveVibrationDeviceList"), applet_resource(applet_resource_) { // clang-format off static const FunctionInfo functions[] = { {0, &IActiveVibrationDeviceList::InitializeVibrationDevice, "InitializeVibrationDevice"}, @@ -154,13 +155,18 @@ private: IPC::RequestParser rp{ctx}; const auto vibration_device_handle{rp.PopRaw()}; - LOG_WARNING(Service_HID, "(STUBBED) called, npad_type={}, npad_id={}, device_index={}", - vibration_device_handle.npad_type, vibration_device_handle.npad_id, - vibration_device_handle.device_index); + applet_resource->GetController(HidController::NPad) + .InitializeVibrationDevice(vibration_device_handle); + + LOG_DEBUG(Service_HID, "called, npad_type={}, npad_id={}, device_index={}", + vibration_device_handle.npad_type, vibration_device_handle.npad_id, + vibration_device_handle.device_index); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); } + + std::shared_ptr applet_resource; }; std::shared_ptr Hid::GetAppletResource() { @@ -1062,7 +1068,7 @@ void Hid::CreateActiveVibrationDeviceList(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface(); + rb.PushIpcInterface(applet_resource); } void Hid::PermitVibration(Kernel::HLERequestContext& ctx) { @@ -1137,15 +1143,16 @@ void Hid::IsVibrationDeviceMounted(Kernel::HLERequestContext& ctx) { const auto parameters{rp.PopRaw()}; - LOG_WARNING( - Service_HID, - "(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", - parameters.vibration_device_handle.npad_type, parameters.vibration_device_handle.npad_id, - parameters.vibration_device_handle.device_index, parameters.applet_resource_user_id); + LOG_DEBUG(Service_HID, + "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", + parameters.vibration_device_handle.npad_type, + parameters.vibration_device_handle.npad_id, + parameters.vibration_device_handle.device_index, parameters.applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); - rb.Push(true); + rb.Push(applet_resource->GetController(HidController::NPad) + .IsVibrationDeviceMounted(parameters.vibration_device_handle)); } void Hid::ActivateConsoleSixAxisSensor(Kernel::HLERequestContext& ctx) { From 91c06dae1a68efb2d6ae110d0aa28d7b3aafd573 Mon Sep 17 00:00:00 2001 From: Morph <39850852+Morph1984@users.noreply.github.com> Date: Fri, 23 Oct 2020 12:09:28 -0400 Subject: [PATCH 28/37] input: Disconnect a controller prior to connecting a new one Some games do not respond to a change in controller type if 1) The controller is not disconnected prior to being reconnected and/or 2) The controller is reconnected instantly after being disconnected. Since it is not possible to change controllers instantly on hardware and requiring a disconnect prior to connecting a new one, we should emulate this as well with a small delay, fixing the aforementioned issue. --- src/yuzu/applets/controller.cpp | 62 ++++++++++-------- .../configuration/configure_input_player.cpp | 63 +++++++++++-------- 2 files changed, 74 insertions(+), 51 deletions(-) diff --git a/src/yuzu/applets/controller.cpp b/src/yuzu/applets/controller.cpp index cdcd3d28d..552cb7204 100644 --- a/src/yuzu/applets/controller.cpp +++ b/src/yuzu/applets/controller.cpp @@ -19,6 +19,8 @@ namespace { +constexpr std::size_t HANDHELD_INDEX = 8; + constexpr std::array, 8> led_patterns{{ {true, false, false, false}, {true, true, false, false}, @@ -260,11 +262,6 @@ int QtControllerSelectorDialog::exec() { } void QtControllerSelectorDialog::ApplyConfiguration() { - // Update the controller state once more, just to be sure they are properly applied. - for (std::size_t index = 0; index < NUM_PLAYERS; ++index) { - UpdateControllerState(index); - } - const bool pre_docked_mode = Settings::values.use_docked_mode.GetValue(); Settings::values.use_docked_mode.SetValue(ui->radioDocked->isChecked()); OnDockedModeChanged(pre_docked_mode, Settings::values.use_docked_mode.GetValue()); @@ -275,15 +272,16 @@ void QtControllerSelectorDialog::ApplyConfiguration() { void QtControllerSelectorDialog::LoadConfiguration() { for (std::size_t index = 0; index < NUM_PLAYERS; ++index) { - const auto connected = Settings::values.players.GetValue()[index].connected || - (index == 0 && Settings::values.players.GetValue()[8].connected); + const auto connected = + Settings::values.players.GetValue()[index].connected || + (index == 0 && Settings::values.players.GetValue()[HANDHELD_INDEX].connected); player_groupboxes[index]->setChecked(connected); connected_controller_checkboxes[index]->setChecked(connected); emulated_controllers[index]->setCurrentIndex( GetIndexFromControllerType(Settings::values.players.GetValue()[index].controller_type)); } - UpdateDockedState(Settings::values.players.GetValue()[8].connected); + UpdateDockedState(Settings::values.players.GetValue()[HANDHELD_INDEX].connected); ui->vibrationGroup->setChecked(Settings::values.vibration_enabled.GetValue()); ui->motionGroup->setChecked(Settings::values.motion_enabled.GetValue()); @@ -468,32 +466,46 @@ void QtControllerSelectorDialog::UpdateControllerIcon(std::size_t player_index) void QtControllerSelectorDialog::UpdateControllerState(std::size_t player_index) { auto& player = Settings::values.players.GetValue()[player_index]; - player.controller_type = + const auto controller_type = GetControllerTypeFromIndex(emulated_controllers[player_index]->currentIndex()); - player.connected = player_groupboxes[player_index]->isChecked(); + const auto player_connected = player_groupboxes[player_index]->isChecked() && + controller_type != Settings::ControllerType::Handheld; - // Player 2-8 - if (player_index != 0) { - UpdateController(player.controller_type, player_index, player.connected); + if (player.controller_type == controller_type && player.connected == player_connected) { + // Set vibration devices in the event that the input device has changed. + ConfigureVibration::SetVibrationDevices(player_index); return; } + // Disconnect the controller first. + UpdateController(controller_type, player_index, false); + + player.controller_type = controller_type; + player.connected = player_connected; + ConfigureVibration::SetVibrationDevices(player_index); - // Player 1 and Handheld - auto& handheld = Settings::values.players.GetValue()[8]; - // If Handheld is selected, copy all the settings from Player 1 to Handheld. - if (player.controller_type == Settings::ControllerType::Handheld) { - handheld = player; - handheld.connected = player_groupboxes[player_index]->isChecked(); - player.connected = false; // Disconnect Player 1 - } else { - player.connected = player_groupboxes[player_index]->isChecked(); - handheld.connected = false; // Disconnect Handheld + // Handheld + if (player_index == 0) { + auto& handheld = Settings::values.players.GetValue()[HANDHELD_INDEX]; + if (controller_type == Settings::ControllerType::Handheld) { + handheld = player; + } + handheld.connected = player_groupboxes[player_index]->isChecked() && + controller_type == Settings::ControllerType::Handheld; + UpdateController(Settings::ControllerType::Handheld, 8, handheld.connected); } - UpdateController(player.controller_type, player_index, player.connected); - UpdateController(Settings::ControllerType::Handheld, 8, handheld.connected); + if (!player.connected) { + return; + } + + // This emulates a delay between disconnecting and reconnecting controllers as some games + // do not respond to a change in controller type if it was instantaneous. + using namespace std::chrono_literals; + std::this_thread::sleep_for(20ms); + + UpdateController(controller_type, player_index, player_connected); } void QtControllerSelectorDialog::UpdateLEDPattern(std::size_t player_index) { diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp index 3e785c224..1ee4725ef 100644 --- a/src/yuzu/configuration/configure_input_player.cpp +++ b/src/yuzu/configuration/configure_input_player.cpp @@ -26,8 +26,6 @@ #include "yuzu/configuration/input_profiles.h" #include "yuzu/util/limitable_input_dialog.h" -constexpr std::size_t HANDHELD_INDEX = 8; - const std::array ConfigureInputPlayer::analog_sub_buttons{{ "up", @@ -38,12 +36,10 @@ const std::array namespace { +constexpr std::size_t HANDHELD_INDEX = 8; + void UpdateController(Settings::ControllerType controller_type, std::size_t npad_index, bool connected) { - auto& player = Settings::values.players.GetValue()[npad_index]; - player.controller_type = controller_type; - player.connected = connected; - Core::System& system{Core::System::GetInstance()}; if (!system.IsPoweredOn()) { return; @@ -563,35 +559,50 @@ void ConfigureInputPlayer::ApplyConfiguration() { } auto& motions = player.motions; + std::transform(motions_param.begin(), motions_param.end(), motions.begin(), [](const Common::ParamPackage& param) { return param.Serialize(); }); - player.controller_type = - static_cast(ui->comboControllerType->currentIndex()); - player.connected = ui->groupConnectedController->isChecked(); + const auto controller_type = + GetControllerTypeFromIndex(ui->comboControllerType->currentIndex()); + const auto player_connected = ui->groupConnectedController->isChecked() && + controller_type != Settings::ControllerType::Handheld; - ConfigureVibration::SetVibrationDevices(player_index); - - // Player 2-8 - if (player_index != 0) { - UpdateController(player.controller_type, player_index, player.connected); + if (player.controller_type == controller_type && player.connected == player_connected) { + // Set vibration devices in the event that the input device has changed. + ConfigureVibration::SetVibrationDevices(player_index); return; } - // Player 1 and Handheld - auto& handheld = Settings::values.players.GetValue()[HANDHELD_INDEX]; - // If Handheld is selected, copy all the settings from Player 1 to Handheld. - if (player.controller_type == Settings::ControllerType::Handheld) { - handheld = player; - handheld.connected = ui->groupConnectedController->isChecked(); - player.connected = false; // Disconnect Player 1 - } else { - player.connected = ui->groupConnectedController->isChecked(); - handheld.connected = false; // Disconnect Handheld + // Disconnect the controller first. + UpdateController(controller_type, player_index, false); + + player.controller_type = controller_type; + player.connected = player_connected; + + ConfigureVibration::SetVibrationDevices(player_index); + + // Handheld + if (player_index == 0) { + auto& handheld = Settings::values.players.GetValue()[HANDHELD_INDEX]; + if (controller_type == Settings::ControllerType::Handheld) { + handheld = player; + } + handheld.connected = ui->groupConnectedController->isChecked() && + controller_type == Settings::ControllerType::Handheld; + UpdateController(Settings::ControllerType::Handheld, HANDHELD_INDEX, handheld.connected); } - UpdateController(player.controller_type, player_index, player.connected); - UpdateController(Settings::ControllerType::Handheld, HANDHELD_INDEX, handheld.connected); + if (!player.connected) { + return; + } + + // This emulates a delay between disconnecting and reconnecting controllers as some games + // do not respond to a change in controller type if it was instantaneous. + using namespace std::chrono_literals; + std::this_thread::sleep_for(20ms); + + UpdateController(controller_type, player_index, player_connected); } void ConfigureInputPlayer::showEvent(QShowEvent* event) { From 30e0d1c973290f4813b040eecf83ff4a2c7432c3 Mon Sep 17 00:00:00 2001 From: Morph <39850852+Morph1984@users.noreply.github.com> Date: Sun, 25 Oct 2020 07:30:23 -0400 Subject: [PATCH 29/37] controllers/npad: Remove the old vibration filter Previously we used a vibration filter that filters out amplitudes close to each other. It turns out there are cases where this results into vibrations that are too inaccurate. Remove this and move the 100Hz vibration filter (Only allowing a maximum of 100 vibrations per second) from sdl_impl to npad when enable_accurate_vibrations is set to false. --- src/core/hle/service/hid/controllers/npad.cpp | 118 ++++++++++-------- src/core/hle/service/hid/controllers/npad.h | 4 + src/core/hle/service/hid/hid.cpp | 2 +- src/input_common/sdl/sdl_impl.cpp | 15 --- 4 files changed, 69 insertions(+), 70 deletions(-) diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp index ecc33bc08..cfafabbd8 100644 --- a/src/core/hle/service/hid/controllers/npad.cpp +++ b/src/core/hle/service/hid/controllers/npad.cpp @@ -703,6 +703,23 @@ bool Controller_NPad::VibrateControllerAtIndex(std::size_t npad_index, std::size return false; } + if (!Settings::values.enable_accurate_vibrations.GetValue()) { + using std::chrono::duration_cast; + using std::chrono::milliseconds; + using std::chrono::steady_clock; + + const auto now = steady_clock::now(); + + // Filter out non-zero vibrations that are within 10ms of each other. + if ((vibration_value.amp_low != 0.0f || vibration_value.amp_high != 0.0f) && + duration_cast(now - last_vibration_timepoints[npad_index][device_index]) < + milliseconds(10)) { + return false; + } + + last_vibration_timepoints[npad_index][device_index] = now; + } + return vibrations[npad_index][device_index]->SetRumblePlay( std::min(vibration_value.amp_low * player.vibration_strength / 100.0f, 1.0f), vibration_value.freq_low, @@ -710,66 +727,59 @@ bool Controller_NPad::VibrateControllerAtIndex(std::size_t npad_index, std::size vibration_value.freq_high); } -void Controller_NPad::VibrateControllers(const std::vector& vibration_device_handles, - const std::vector& vibration_values) { - LOG_TRACE(Service_HID, "called"); - +void Controller_NPad::VibrateController(const DeviceHandle& vibration_device_handle, + const VibrationValue& vibration_value) { if (!Settings::values.vibration_enabled.GetValue()) { return; } - ASSERT_MSG(vibration_device_handles.size() == vibration_values.size(), - "The amount of device handles does not match with the amount of vibration values," - "this is undefined behavior!"); + const auto npad_index = NPadIdToIndex(vibration_device_handle.npad_id); + const auto device_index = static_cast(vibration_device_handle.device_index); + + if (!vibration_devices_mounted[npad_index][device_index] || + !connected_controllers[npad_index].is_connected) { + return; + } + + if (vibration_device_handle.device_index == DeviceIndex::None) { + UNREACHABLE_MSG("DeviceIndex should never be None!"); + return; + } + + // Some games try to send mismatched parameters in the device handle, block these. + if ((connected_controllers[npad_index].type == NPadControllerType::JoyLeft && + (vibration_device_handle.npad_type == NpadType::JoyconRight || + vibration_device_handle.device_index == DeviceIndex::Right)) || + (connected_controllers[npad_index].type == NPadControllerType::JoyRight && + (vibration_device_handle.npad_type == NpadType::JoyconLeft || + vibration_device_handle.device_index == DeviceIndex::Left))) { + return; + } + + // Filter out vibrations with equivalent values to reduce unnecessary state changes. + if (vibration_value.amp_low == latest_vibration_values[npad_index][device_index].amp_low && + vibration_value.amp_high == latest_vibration_values[npad_index][device_index].amp_high) { + return; + } + + if (VibrateControllerAtIndex(npad_index, device_index, vibration_value)) { + latest_vibration_values[npad_index][device_index] = vibration_value; + } +} + +void Controller_NPad::VibrateControllers(const std::vector& vibration_device_handles, + const std::vector& vibration_values) { + if (!Settings::values.vibration_enabled.GetValue()) { + return; + } + + ASSERT_OR_EXECUTE_MSG( + vibration_device_handles.size() == vibration_values.size(), { return; }, + "The amount of device handles does not match with the amount of vibration values," + "this is undefined behavior!"); for (std::size_t i = 0; i < vibration_device_handles.size(); ++i) { - const auto npad_index = NPadIdToIndex(vibration_device_handles[i].npad_id); - const auto device_index = - static_cast(vibration_device_handles[i].device_index); - - if (!vibration_devices_mounted[npad_index][device_index] || - !connected_controllers[npad_index].is_connected) { - continue; - } - - if (vibration_device_handles[i].device_index == DeviceIndex::None) { - UNREACHABLE_MSG("DeviceIndex should never be None!"); - continue; - } - - // Some games try to send mismatched parameters in the device handle, block these. - if ((connected_controllers[npad_index].type == NPadControllerType::JoyLeft && - (vibration_device_handles[i].npad_type == NpadType::JoyconRight || - vibration_device_handles[i].device_index == DeviceIndex::Right)) || - (connected_controllers[npad_index].type == NPadControllerType::JoyRight && - (vibration_device_handles[i].npad_type == NpadType::JoyconLeft || - vibration_device_handles[i].device_index == DeviceIndex::Left))) { - continue; - } - - // Filter out vibrations with equivalent values to reduce unnecessary state changes. - if (vibration_values[i].amp_low == - latest_vibration_values[npad_index][device_index].amp_low && - vibration_values[i].amp_high == - latest_vibration_values[npad_index][device_index].amp_high) { - continue; - } - - // Filter out non-zero vibrations that are within 0.015625 absolute amplitude of each other. - if (!Settings::values.enable_accurate_vibrations.GetValue() && - (vibration_values[i].amp_low != 0.0f || vibration_values[i].amp_high != 0.0f) && - (latest_vibration_values[npad_index][device_index].amp_low != 0.0f || - latest_vibration_values[npad_index][device_index].amp_high != 0.0f) && - (abs(vibration_values[i].amp_low - - latest_vibration_values[npad_index][device_index].amp_low) < 0.015625f && - abs(vibration_values[i].amp_high - - latest_vibration_values[npad_index][device_index].amp_high) < 0.015625f)) { - continue; - } - - if (VibrateControllerAtIndex(npad_index, device_index, vibration_values[i])) { - latest_vibration_values[npad_index][device_index] = vibration_values[i]; - } + VibrateController(vibration_device_handles[i], vibration_values[i]); } } diff --git a/src/core/hle/service/hid/controllers/npad.h b/src/core/hle/service/hid/controllers/npad.h index 30e3cb02f..f5122124c 100644 --- a/src/core/hle/service/hid/controllers/npad.h +++ b/src/core/hle/service/hid/controllers/npad.h @@ -151,6 +151,9 @@ public: bool VibrateControllerAtIndex(std::size_t npad_index, std::size_t device_index, const VibrationValue& vibration_value = {}); + void VibrateController(const DeviceHandle& vibration_device_handle, + const VibrationValue& vibration_value); + void VibrateControllers(const std::vector& vibration_device_handles, const std::vector& vibration_values); @@ -421,6 +424,7 @@ private: NpadHandheldActivationMode handheld_activation_mode{NpadHandheldActivationMode::Dual}; // Each controller should have their own styleset changed event std::array styleset_changed_events; + std::array, 10> last_vibration_timepoints; std::array, 10> latest_vibration_values{}; std::array, 10> vibration_devices_mounted{}; std::array connected_controllers{}; diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index ecaa847b2..2e9682bed 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp @@ -1029,7 +1029,7 @@ void Hid::SendVibrationValue(Kernel::HLERequestContext& ctx) { const auto parameters{rp.PopRaw()}; applet_resource->GetController(HidController::NPad) - .VibrateControllers({parameters.vibration_device_handle}, {parameters.vibration_value}); + .VibrateController(parameters.vibration_device_handle, parameters.vibration_value); LOG_DEBUG(Service_HID, "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", diff --git a/src/input_common/sdl/sdl_impl.cpp b/src/input_common/sdl/sdl_impl.cpp index a2a83cdc9..a9f7e5103 100644 --- a/src/input_common/sdl/sdl_impl.cpp +++ b/src/input_common/sdl/sdl_impl.cpp @@ -81,18 +81,6 @@ public: } bool RumblePlay(u16 amp_low, u16 amp_high) { - using std::chrono::duration_cast; - using std::chrono::milliseconds; - using std::chrono::steady_clock; - - // Block non-zero vibrations less than 10ms apart from each other. - if ((amp_low != 0 || amp_high != 0) && - duration_cast(steady_clock::now() - last_vibration) < milliseconds(10)) { - return false; - } - - last_vibration = steady_clock::now(); - if (sdl_controller) { return SDL_GameControllerRumble(sdl_controller.get(), amp_low, amp_high, 0) == 0; } else if (sdl_joystick) { @@ -171,9 +159,6 @@ private: std::unique_ptr sdl_controller; mutable std::mutex mutex; - // This is the timepoint of the last vibration and is used to ensure vibrations are 10ms apart. - std::chrono::steady_clock::time_point last_vibration; - // Motion is initialized without PID values as motion input is not aviable for SDL2 MotionInput motion{0.0f, 0.0f, 0.0f}; }; From 760a9e869322cbda51416f7001842557b90754af Mon Sep 17 00:00:00 2001 From: Morph <39850852+Morph1984@users.noreply.github.com> Date: Mon, 26 Oct 2020 03:28:03 -0400 Subject: [PATCH 30/37] applets/controller: Change the input button to create input profiles Co-authored-by: Its-Rei --- src/yuzu/CMakeLists.txt | 6 +-- src/yuzu/applets/controller.cpp | 20 ++++------ src/yuzu/applets/controller.h | 8 +++- src/yuzu/applets/controller.ui | 4 +- .../configuration/configure_input_dialog.cpp | 37 ------------------ .../configuration/configure_input_dialog.h | 38 ------------------ .../configuration/configure_input_player.cpp | 5 ++- .../configure_input_profile_dialog.cpp | 36 +++++++++++++++++ .../configure_input_profile_dialog.h | 39 +++++++++++++++++++ ...g.ui => configure_input_profile_dialog.ui} | 24 +++++++++--- 10 files changed, 117 insertions(+), 100 deletions(-) delete mode 100644 src/yuzu/configuration/configure_input_dialog.cpp delete mode 100644 src/yuzu/configuration/configure_input_dialog.h create mode 100644 src/yuzu/configuration/configure_input_profile_dialog.cpp create mode 100644 src/yuzu/configuration/configure_input_profile_dialog.h rename src/yuzu/configuration/{configure_input_dialog.ui => configure_input_profile_dialog.ui} (64%) diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt index bf1fae9fa..b16b54032 100644 --- a/src/yuzu/CMakeLists.txt +++ b/src/yuzu/CMakeLists.txt @@ -68,12 +68,12 @@ add_executable(yuzu configuration/configure_input_advanced.cpp configuration/configure_input_advanced.h configuration/configure_input_advanced.ui - configuration/configure_input_dialog.cpp - configuration/configure_input_dialog.h - configuration/configure_input_dialog.ui configuration/configure_input_player.cpp configuration/configure_input_player.h configuration/configure_input_player.ui + configuration/configure_input_profile_dialog.cpp + configuration/configure_input_profile_dialog.h + configuration/configure_input_profile_dialog.ui configuration/configure_motion_touch.cpp configuration/configure_motion_touch.h configuration/configure_motion_touch.ui diff --git a/src/yuzu/applets/controller.cpp b/src/yuzu/applets/controller.cpp index 552cb7204..5112d48d2 100644 --- a/src/yuzu/applets/controller.cpp +++ b/src/yuzu/applets/controller.cpp @@ -13,8 +13,10 @@ #include "core/hle/service/sm/sm.h" #include "ui_controller.h" #include "yuzu/applets/controller.h" -#include "yuzu/configuration/configure_input_dialog.h" +#include "yuzu/configuration/configure_input.h" +#include "yuzu/configuration/configure_input_profile_dialog.h" #include "yuzu/configuration/configure_vibration.h" +#include "yuzu/configuration/input_profiles.h" #include "yuzu/main.h" namespace { @@ -109,7 +111,8 @@ QtControllerSelectorDialog::QtControllerSelectorDialog( QWidget* parent, Core::Frontend::ControllerParameters parameters_, InputCommon::InputSubsystem* input_subsystem_) : QDialog(parent), ui(std::make_unique()), - parameters(std::move(parameters_)), input_subsystem(input_subsystem_) { + parameters(std::move(parameters_)), input_subsystem{input_subsystem_}, + input_profiles(std::make_unique()) { ui->setupUi(this); player_widgets = { @@ -230,7 +233,7 @@ QtControllerSelectorDialog::QtControllerSelectorDialog( &QtControllerSelectorDialog::CallConfigureVibrationDialog); connect(ui->inputConfigButton, &QPushButton::clicked, this, - &QtControllerSelectorDialog::CallConfigureInputDialog); + &QtControllerSelectorDialog::CallConfigureInputProfileDialog); connect(ui->buttonBox, &QDialogButtonBox::accepted, this, &QtControllerSelectorDialog::ApplyConfiguration); @@ -299,20 +302,13 @@ void QtControllerSelectorDialog::CallConfigureVibrationDialog() { } } -void QtControllerSelectorDialog::CallConfigureInputDialog() { - const auto max_supported_players = parameters.enable_single_mode ? 1 : parameters.max_players; - - ConfigureInputDialog dialog(this, max_supported_players, input_subsystem); +void QtControllerSelectorDialog::CallConfigureInputProfileDialog() { + ConfigureInputProfileDialog dialog(this, input_subsystem, input_profiles.get()); dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint | Qt::WindowSystemMenuHint); dialog.setWindowModality(Qt::WindowModal); dialog.exec(); - - dialog.ApplyConfiguration(); - - LoadConfiguration(); - CheckIfParametersMet(); } bool QtControllerSelectorDialog::CheckIfParametersMet() { diff --git a/src/yuzu/applets/controller.h b/src/yuzu/applets/controller.h index a2ce03c8f..4344e1dd0 100644 --- a/src/yuzu/applets/controller.h +++ b/src/yuzu/applets/controller.h @@ -16,6 +16,8 @@ class QDialogButtonBox; class QGroupBox; class QLabel; +class InputProfiles; + namespace InputCommon { class InputSubsystem; } @@ -45,8 +47,8 @@ private: // Initializes the "Configure Vibration" Dialog. void CallConfigureVibrationDialog(); - // Initializes the "Configure Input" Dialog. - void CallConfigureInputDialog(); + // Initializes the "Create Input Profile" Dialog. + void CallConfigureInputProfileDialog(); // Checks the current configuration against the given parameters. // This sets and returns the value of parameters_met. @@ -83,6 +85,8 @@ private: InputCommon::InputSubsystem* input_subsystem; + std::unique_ptr input_profiles; + // This is true if and only if all parameters are met. Otherwise, this is false. // This determines whether the "OK" button can be clicked to exit the applet. bool parameters_met{false}; diff --git a/src/yuzu/applets/controller.ui b/src/yuzu/applets/controller.ui index 8e571ba8f..c8cb6bcf3 100644 --- a/src/yuzu/applets/controller.ui +++ b/src/yuzu/applets/controller.ui @@ -2402,7 +2402,7 @@ - Input Config + Profiles @@ -2429,7 +2429,7 @@ min-width: 68px; - Open + Create diff --git a/src/yuzu/configuration/configure_input_dialog.cpp b/src/yuzu/configuration/configure_input_dialog.cpp deleted file mode 100644 index 1866003c2..000000000 --- a/src/yuzu/configuration/configure_input_dialog.cpp +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright 2020 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#include "ui_configure_input_dialog.h" -#include "yuzu/configuration/configure_input_dialog.h" - -ConfigureInputDialog::ConfigureInputDialog(QWidget* parent, std::size_t max_players, - InputCommon::InputSubsystem* input_subsystem) - : QDialog(parent), ui(std::make_unique()), - input_widget(new ConfigureInput(this)) { - ui->setupUi(this); - - input_widget->Initialize(input_subsystem, max_players); - - ui->inputLayout->addWidget(input_widget); - - RetranslateUI(); -} - -ConfigureInputDialog::~ConfigureInputDialog() = default; - -void ConfigureInputDialog::ApplyConfiguration() { - input_widget->ApplyConfiguration(); -} - -void ConfigureInputDialog::changeEvent(QEvent* event) { - if (event->type() == QEvent::LanguageChange) { - RetranslateUI(); - } - - QDialog::changeEvent(event); -} - -void ConfigureInputDialog::RetranslateUI() { - ui->retranslateUi(this); -} diff --git a/src/yuzu/configuration/configure_input_dialog.h b/src/yuzu/configuration/configure_input_dialog.h deleted file mode 100644 index d1bd865f9..000000000 --- a/src/yuzu/configuration/configure_input_dialog.h +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright 2020 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#pragma once - -#include -#include -#include "yuzu/configuration/configure_input.h" - -class QPushButton; - -namespace InputCommon { -class InputSubsystem; -} - -namespace Ui { -class ConfigureInputDialog; -} - -class ConfigureInputDialog : public QDialog { - Q_OBJECT - -public: - explicit ConfigureInputDialog(QWidget* parent, std::size_t max_players, - InputCommon::InputSubsystem* input_subsystem); - ~ConfigureInputDialog() override; - - void ApplyConfiguration(); - -private: - void changeEvent(QEvent* event) override; - void RetranslateUI(); - - std::unique_ptr ui; - - ConfigureInput* input_widget; -}; diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp index 1ee4725ef..4ed704793 100644 --- a/src/yuzu/configuration/configure_input_player.cpp +++ b/src/yuzu/configuration/configure_input_player.cpp @@ -459,11 +459,14 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i }); } + if (debug || player_index == 9) { + ui->groupConnectedController->setCheckable(false); + } + // The Debug Controller can only choose the Pro Controller. if (debug) { ui->buttonScreenshot->setEnabled(false); ui->buttonHome->setEnabled(false); - ui->groupConnectedController->setCheckable(false); QStringList debug_controller_types = { tr("Pro Controller"), }; diff --git a/src/yuzu/configuration/configure_input_profile_dialog.cpp b/src/yuzu/configuration/configure_input_profile_dialog.cpp new file mode 100644 index 000000000..818399b47 --- /dev/null +++ b/src/yuzu/configuration/configure_input_profile_dialog.cpp @@ -0,0 +1,36 @@ +// Copyright 2020 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "ui_configure_input_profile_dialog.h" +#include "yuzu/configuration/configure_input_profile_dialog.h" + +ConfigureInputProfileDialog::ConfigureInputProfileDialog( + QWidget* parent, InputCommon::InputSubsystem* input_subsystem, InputProfiles* profiles) + : QDialog(parent), ui(std::make_unique()), + profile_widget(new ConfigureInputPlayer(this, 9, nullptr, input_subsystem, profiles, false)) { + ui->setupUi(this); + + ui->controllerLayout->addWidget(profile_widget); + + connect(ui->clear_all_button, &QPushButton::clicked, this, + [this] { profile_widget->ClearAll(); }); + connect(ui->restore_defaults_button, &QPushButton::clicked, this, + [this] { profile_widget->RestoreDefaults(); }); + + RetranslateUI(); +} + +ConfigureInputProfileDialog::~ConfigureInputProfileDialog() = default; + +void ConfigureInputProfileDialog::changeEvent(QEvent* event) { + if (event->type() == QEvent::LanguageChange) { + RetranslateUI(); + } + + QDialog::changeEvent(event); +} + +void ConfigureInputProfileDialog::RetranslateUI() { + ui->retranslateUi(this); +} diff --git a/src/yuzu/configuration/configure_input_profile_dialog.h b/src/yuzu/configuration/configure_input_profile_dialog.h new file mode 100644 index 000000000..d4a3973d9 --- /dev/null +++ b/src/yuzu/configuration/configure_input_profile_dialog.h @@ -0,0 +1,39 @@ +// Copyright 2020 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include +#include +#include "yuzu/configuration/configure_input_player.h" + +class QPushButton; + +class InputProfiles; + +namespace InputCommon { +class InputSubsystem; +} + +namespace Ui { +class ConfigureInputProfileDialog; +} + +class ConfigureInputProfileDialog : public QDialog { + Q_OBJECT + +public: + explicit ConfigureInputProfileDialog(QWidget* parent, + InputCommon::InputSubsystem* input_subsystem, + InputProfiles* profiles); + ~ConfigureInputProfileDialog() override; + +private: + void changeEvent(QEvent* event) override; + void RetranslateUI(); + + std::unique_ptr ui; + + ConfigureInputPlayer* profile_widget; +}; diff --git a/src/yuzu/configuration/configure_input_dialog.ui b/src/yuzu/configuration/configure_input_profile_dialog.ui similarity index 64% rename from src/yuzu/configuration/configure_input_dialog.ui rename to src/yuzu/configuration/configure_input_profile_dialog.ui index b92ddb200..726cf6905 100644 --- a/src/yuzu/configuration/configure_input_dialog.ui +++ b/src/yuzu/configuration/configure_input_profile_dialog.ui @@ -1,7 +1,7 @@ - ConfigureInputDialog - + ConfigureInputProfileDialog + 0 @@ -11,7 +11,7 @@ - Configure Input + Create Input Profile @@ -30,10 +30,24 @@ 9 - + + + + + Clear + + + + + + + Defaults + + + @@ -50,7 +64,7 @@ buttonBox accepted() - ConfigureInputDialog + ConfigureInputProfileDialog accept() From 117bdc71e016629b9355b33a6d64655f0245f833 Mon Sep 17 00:00:00 2001 From: Morph <39850852+Morph1984@users.noreply.github.com> Date: Tue, 27 Oct 2020 13:15:57 -0400 Subject: [PATCH 31/37] sdl_impl: Revert to the "old" method of mapping sticks Not all controllers have a SDL_GameController binding. This caused controllers not present in the SDL GameController database to have buttons mapped instead of axes. Furthermore, it was not possible to invert the axes when it could be useful such as emulating a horizontal single joycon or other potential cases. This allows us to invert the axes by reversing the order of mapping (vertical, then horizontal). --- src/input_common/sdl/sdl_impl.cpp | 45 ++++++------------- .../configuration/configure_input_player.cpp | 12 +++++ .../configuration/configure_input_player.h | 5 ++- 3 files changed, 29 insertions(+), 33 deletions(-) diff --git a/src/input_common/sdl/sdl_impl.cpp b/src/input_common/sdl/sdl_impl.cpp index a9f7e5103..6024ed97a 100644 --- a/src/input_common/sdl/sdl_impl.cpp +++ b/src/input_common/sdl/sdl_impl.cpp @@ -1068,7 +1068,6 @@ public: void Start(const std::string& device_id) override { SDLPoller::Start(device_id); - // Load the game controller // Reset stored axes analog_x_axis = -1; analog_y_axis = -1; @@ -1081,40 +1080,21 @@ public: if (event.type == SDL_JOYAXISMOTION && std::abs(event.jaxis.value / 32767.0) < 0.5) { continue; } - // Simplify controller config by testing if game controller support is enabled. if (event.type == SDL_JOYAXISMOTION) { const auto axis = event.jaxis.axis; - if (const auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which); - auto* const controller = joystick->GetSDLGameController()) { - const auto axis_left_x = - SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_LEFTX) - .value.axis; - const auto axis_left_y = - SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_LEFTY) - .value.axis; - const auto axis_right_x = - SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_RIGHTX) - .value.axis; - const auto axis_right_y = - SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_RIGHTY) - .value.axis; - - if (axis == axis_left_x || axis == axis_left_y) { - analog_x_axis = axis_left_x; - analog_y_axis = axis_left_y; - break; - } else if (axis == axis_right_x || axis == axis_right_y) { - analog_x_axis = axis_right_x; - analog_y_axis = axis_right_y; - break; - } + // In order to return a complete analog param, we need inputs for both axes. + // First we take the x-axis (horizontal) input, then the y-axis (vertical) input. + if (analog_x_axis == -1) { + analog_x_axis = axis; + } else if (analog_y_axis == -1 && analog_x_axis != axis) { + analog_y_axis = axis; + } + } else { + // If the press wasn't accepted as a joy axis, check for a button press + auto button_press = button_poller.FromEvent(event); + if (button_press) { + return *button_press; } - } - - // If the press wasn't accepted as a joy axis, check for a button press - auto button_press = button_poller.FromEvent(event); - if (button_press) { - return *button_press; } } @@ -1127,6 +1107,7 @@ public: return params; } } + return {}; } diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp index 4ed704793..5abf9f0bf 100644 --- a/src/yuzu/configuration/configure_input_player.cpp +++ b/src/yuzu/configuration/configure_input_player.cpp @@ -370,6 +370,18 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i } connect(analog_button, &QPushButton::clicked, [=, this] { + if (!map_analog_stick_accepted) { + map_analog_stick_accepted = + QMessageBox::information( + this, tr("Map Analog Stick"), + tr("After pressing OK, first move your joystick horizontally, and then " + "vertically.\nTo invert the axes, first move your joystick " + "vertically, and then horizontally."), + QMessageBox::Ok | QMessageBox::Cancel) == QMessageBox::Ok; + if (!map_analog_stick_accepted) { + return; + } + } HandleClick( analog_map_buttons[analog_id][sub_button_id], [=, this](const Common::ParamPackage& params) { diff --git a/src/yuzu/configuration/configure_input_player.h b/src/yuzu/configuration/configure_input_player.h index 05dee5af5..4895e8850 100644 --- a/src/yuzu/configuration/configure_input_player.h +++ b/src/yuzu/configuration/configure_input_player.h @@ -181,9 +181,12 @@ private: std::vector> device_pollers; + /// A flag to indicate that the "Map Analog Stick" pop-up has been shown and accepted once. + bool map_analog_stick_accepted{}; + /// A flag to indicate if keyboard keys are okay when configuring an input. If this is false, /// keyboard events are ignored. - bool want_keyboard_mouse = false; + bool want_keyboard_mouse{}; /// List of physical devices users can map with. If a SDL backed device is selected, then you /// can use this device to get a default mapping. From 97b2220a822548eed83993fceebe0e611dbec84b Mon Sep 17 00:00:00 2001 From: Morph <39850852+Morph1984@users.noreply.github.com> Date: Tue, 27 Oct 2020 13:33:25 -0400 Subject: [PATCH 32/37] general: Fix compiler warnings on linux and miscellaneous changes --- src/core/hle/service/hid/controllers/npad.cpp | 17 ++++++++++------- src/core/hle/service/hid/controllers/npad.h | 2 +- src/yuzu/applets/controller.cpp | 5 +++-- .../configure_debug_controller.cpp | 1 + .../configuration/configure_debug_controller.h | 3 ++- src/yuzu/configuration/configure_input.h | 9 ++++----- .../configuration/configure_input_advanced.cpp | 6 +++--- .../configuration/configure_input_advanced.h | 2 +- .../configuration/configure_input_player.cpp | 3 ++- .../configure_input_profile_dialog.cpp | 1 + .../configure_input_profile_dialog.h | 3 ++- src/yuzu/main.cpp | 1 + 12 files changed, 31 insertions(+), 22 deletions(-) diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp index cfafabbd8..30715267c 100644 --- a/src/core/hle/service/hid/controllers/npad.cpp +++ b/src/core/hle/service/hid/controllers/npad.cpp @@ -287,7 +287,7 @@ void Controller_NPad::OnLoadInputDevices() { void Controller_NPad::OnRelease() { for (std::size_t npad_idx = 0; npad_idx < vibrations.size(); ++npad_idx) { for (std::size_t device_idx = 0; device_idx < vibrations[npad_idx].size(); ++device_idx) { - VibrateControllerAtIndex(npad_idx, device_idx); + VibrateControllerAtIndex(npad_idx, device_idx, {}); } } } @@ -720,11 +720,14 @@ bool Controller_NPad::VibrateControllerAtIndex(std::size_t npad_index, std::size last_vibration_timepoints[npad_index][device_index] = now; } - return vibrations[npad_index][device_index]->SetRumblePlay( - std::min(vibration_value.amp_low * player.vibration_strength / 100.0f, 1.0f), - vibration_value.freq_low, - std::min(vibration_value.amp_high * player.vibration_strength / 100.0f, 1.0f), - vibration_value.freq_high); + auto& vibration = vibrations[npad_index][device_index]; + const auto player_vibration_strength = static_cast(player.vibration_strength); + const auto amp_low = + std::min(vibration_value.amp_low * player_vibration_strength / 100.0f, 1.0f); + const auto amp_high = + std::min(vibration_value.amp_high * player_vibration_strength / 100.0f, 1.0f); + return vibration->SetRumblePlay(amp_low, vibration_value.freq_low, amp_high, + vibration_value.freq_high); } void Controller_NPad::VibrateController(const DeviceHandle& vibration_device_handle, @@ -855,7 +858,7 @@ void Controller_NPad::DisconnectNpad(u32 npad_id) { void Controller_NPad::DisconnectNpadAtIndex(std::size_t npad_index) { for (std::size_t device_idx = 0; device_idx < vibrations[npad_index].size(); ++device_idx) { // Send an empty vibration to stop any vibrations. - VibrateControllerAtIndex(npad_index, device_idx); + VibrateControllerAtIndex(npad_index, device_idx, {}); vibration_devices_mounted[npad_index][device_idx] = false; } diff --git a/src/core/hle/service/hid/controllers/npad.h b/src/core/hle/service/hid/controllers/npad.h index f5122124c..99384524b 100644 --- a/src/core/hle/service/hid/controllers/npad.h +++ b/src/core/hle/service/hid/controllers/npad.h @@ -149,7 +149,7 @@ public: void SetNpadMode(u32 npad_id, NpadAssignments assignment_mode); bool VibrateControllerAtIndex(std::size_t npad_index, std::size_t device_index, - const VibrationValue& vibration_value = {}); + const VibrationValue& vibration_value); void VibrateController(const DeviceHandle& vibration_device_handle, const VibrationValue& vibration_value); diff --git a/src/yuzu/applets/controller.cpp b/src/yuzu/applets/controller.cpp index 5112d48d2..8ecfec770 100644 --- a/src/yuzu/applets/controller.cpp +++ b/src/yuzu/applets/controller.cpp @@ -3,6 +3,7 @@ // Refer to the license.txt file included. #include +#include #include "common/assert.h" #include "common/string_util.h" @@ -356,7 +357,7 @@ bool QtControllerSelectorDialog::CheckIfParametersMet() { } void QtControllerSelectorDialog::SetSupportedControllers() { - const QString theme = [this] { + const QString theme = [] { if (QIcon::themeName().contains(QStringLiteral("dark"))) { return QStringLiteral("_dark"); } else if (QIcon::themeName().contains(QStringLiteral("midnight"))) { @@ -445,7 +446,7 @@ void QtControllerSelectorDialog::UpdateControllerIcon(std::size_t player_index) } }(); - const QString theme = [this] { + const QString theme = [] { if (QIcon::themeName().contains(QStringLiteral("dark"))) { return QStringLiteral("_dark"); } else if (QIcon::themeName().contains(QStringLiteral("midnight"))) { diff --git a/src/yuzu/configuration/configure_debug_controller.cpp b/src/yuzu/configuration/configure_debug_controller.cpp index 6dc9c5e57..a878ef9c6 100644 --- a/src/yuzu/configuration/configure_debug_controller.cpp +++ b/src/yuzu/configuration/configure_debug_controller.cpp @@ -4,6 +4,7 @@ #include "ui_configure_debug_controller.h" #include "yuzu/configuration/configure_debug_controller.h" +#include "yuzu/configuration/configure_input_player.h" ConfigureDebugController::ConfigureDebugController(QWidget* parent, InputCommon::InputSubsystem* input_subsystem, diff --git a/src/yuzu/configuration/configure_debug_controller.h b/src/yuzu/configuration/configure_debug_controller.h index 2694b3419..b4f53fad5 100644 --- a/src/yuzu/configuration/configure_debug_controller.h +++ b/src/yuzu/configuration/configure_debug_controller.h @@ -6,10 +6,11 @@ #include #include -#include "yuzu/configuration/configure_input_player.h" class QPushButton; +class ConfigureInputPlayer; + class InputProfiles; namespace InputCommon { diff --git a/src/yuzu/configuration/configure_input.h b/src/yuzu/configuration/configure_input.h index f135a4299..9eba9b523 100644 --- a/src/yuzu/configuration/configure_input.h +++ b/src/yuzu/configuration/configure_input.h @@ -8,17 +8,16 @@ #include #include +#include #include -#include "yuzu/configuration/configure_input_advanced.h" -#include "yuzu/configuration/configure_input_player.h" - -#include "ui_configure_input.h" - class QCheckBox; class QString; class QTimer; +class ConfigureInputAdvanced; +class ConfigureInputPlayer; + class InputProfiles; namespace InputCommon { diff --git a/src/yuzu/configuration/configure_input_advanced.cpp b/src/yuzu/configuration/configure_input_advanced.cpp index 3074be833..abaf03630 100644 --- a/src/yuzu/configuration/configure_input_advanced.cpp +++ b/src/yuzu/configuration/configure_input_advanced.cpp @@ -68,8 +68,7 @@ ConfigureInputAdvanced::ConfigureInputAdvanced(QWidget* parent) for (std::size_t button_idx = 0; button_idx < color_buttons.size(); ++button_idx) { connect(color_buttons[button_idx], &QPushButton::clicked, this, [this, player_idx, button_idx] { - OnControllerButtonClick(static_cast(player_idx), - static_cast(button_idx)); + OnControllerButtonClick(player_idx, button_idx); }); } } @@ -94,7 +93,8 @@ ConfigureInputAdvanced::ConfigureInputAdvanced(QWidget* parent) ConfigureInputAdvanced::~ConfigureInputAdvanced() = default; -void ConfigureInputAdvanced::OnControllerButtonClick(int player_idx, int button_idx) { +void ConfigureInputAdvanced::OnControllerButtonClick(std::size_t player_idx, + std::size_t button_idx) { const QColor new_bg_color = QColorDialog::getColor(controllers_colors[player_idx][button_idx]); if (!new_bg_color.isValid()) { return; diff --git a/src/yuzu/configuration/configure_input_advanced.h b/src/yuzu/configuration/configure_input_advanced.h index 50bb87768..3083d55c1 100644 --- a/src/yuzu/configuration/configure_input_advanced.h +++ b/src/yuzu/configuration/configure_input_advanced.h @@ -35,7 +35,7 @@ private: void RetranslateUI(); void UpdateUIEnabled(); - void OnControllerButtonClick(int player_idx, int button_idx); + void OnControllerButtonClick(std::size_t player_idx, std::size_t button_idx); void LoadConfiguration(); diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp index 5abf9f0bf..0d10c1360 100644 --- a/src/yuzu/configuration/configure_input_player.cpp +++ b/src/yuzu/configuration/configure_input_player.cpp @@ -4,6 +4,7 @@ #include #include +#include #include #include #include @@ -857,7 +858,7 @@ void ConfigureInputPlayer::UpdateControllerIcon() { } }(); - const QString theme = [this] { + const QString theme = [] { if (QIcon::themeName().contains(QStringLiteral("dark"))) { return QStringLiteral("_dark"); } else if (QIcon::themeName().contains(QStringLiteral("midnight"))) { diff --git a/src/yuzu/configuration/configure_input_profile_dialog.cpp b/src/yuzu/configuration/configure_input_profile_dialog.cpp index 818399b47..1f5cfa75b 100644 --- a/src/yuzu/configuration/configure_input_profile_dialog.cpp +++ b/src/yuzu/configuration/configure_input_profile_dialog.cpp @@ -3,6 +3,7 @@ // Refer to the license.txt file included. #include "ui_configure_input_profile_dialog.h" +#include "yuzu/configuration/configure_input_player.h" #include "yuzu/configuration/configure_input_profile_dialog.h" ConfigureInputProfileDialog::ConfigureInputProfileDialog( diff --git a/src/yuzu/configuration/configure_input_profile_dialog.h b/src/yuzu/configuration/configure_input_profile_dialog.h index d4a3973d9..e6386bdbb 100644 --- a/src/yuzu/configuration/configure_input_profile_dialog.h +++ b/src/yuzu/configuration/configure_input_profile_dialog.h @@ -6,10 +6,11 @@ #include #include -#include "yuzu/configuration/configure_input_player.h" class QPushButton; +class ConfigureInputPlayer; + class InputProfiles; namespace InputCommon { diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 76a5c32f4..9dabd8889 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -58,6 +58,7 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual #include #include #include +#include #include #include #include From 6f5b9428971904ad8815a56d50c9aab4805a1c56 Mon Sep 17 00:00:00 2001 From: Morph <39850852+Morph1984@users.noreply.github.com> Date: Thu, 29 Oct 2020 12:15:35 -0400 Subject: [PATCH 33/37] configure_input: Update the input profiles for other player tabs --- src/yuzu/configuration/configure_input.cpp | 16 +++++++++++++-- src/yuzu/configuration/configure_input.h | 1 + .../configuration/configure_input_player.cpp | 20 +++++++++++++------ .../configuration/configure_input_player.h | 12 ++++++++--- 4 files changed, 38 insertions(+), 11 deletions(-) diff --git a/src/yuzu/configuration/configure_input.cpp b/src/yuzu/configuration/configure_input.cpp index 600cc03ae..d9009091b 100644 --- a/src/yuzu/configuration/configure_input.cpp +++ b/src/yuzu/configuration/configure_input.cpp @@ -124,8 +124,10 @@ void ConfigureInput::Initialize(InputCommon::InputSubsystem* input_subsystem, } } }); - connect(player_controllers[i], &ConfigureInputPlayer::RefreshInputDevices, - [this] { UpdateAllInputDevices(); }); + connect(player_controllers[i], &ConfigureInputPlayer::RefreshInputDevices, this, + &ConfigureInput::UpdateAllInputDevices); + connect(player_controllers[i], &ConfigureInputPlayer::RefreshInputProfiles, this, + &ConfigureInput::UpdateAllInputProfiles, Qt::QueuedConnection); connect(player_connected[i], &QCheckBox::stateChanged, [this, i](int state) { player_controllers[i]->ConnectPlayer(state == Qt::Checked); }); @@ -259,3 +261,13 @@ void ConfigureInput::UpdateAllInputDevices() { player->UpdateInputDeviceCombobox(); } } + +void ConfigureInput::UpdateAllInputProfiles(std::size_t player_index) { + for (std::size_t i = 0; i < player_controllers.size(); ++i) { + if (i == player_index) { + continue; + } + + player_controllers[i]->UpdateInputProfiles(); + } +} diff --git a/src/yuzu/configuration/configure_input.h b/src/yuzu/configuration/configure_input.h index 9eba9b523..f4eb0d78b 100644 --- a/src/yuzu/configuration/configure_input.h +++ b/src/yuzu/configuration/configure_input.h @@ -52,6 +52,7 @@ private: void UpdateDockedState(bool is_handheld); void UpdateAllInputDevices(); + void UpdateAllInputProfiles(std::size_t player_index); /// Load configuration settings. void LoadConfiguration(); diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp index 0d10c1360..f65a7fe73 100644 --- a/src/yuzu/configuration/configure_input_player.cpp +++ b/src/yuzu/configuration/configure_input_player.cpp @@ -541,7 +541,7 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i } }); - RefreshInputProfiles(); + UpdateInputProfiles(); connect(ui->buttonProfilesNew, &QPushButton::clicked, this, &ConfigureInputPlayer::CreateProfile); @@ -1132,10 +1132,13 @@ void ConfigureInputPlayer::CreateProfile() { if (!profiles->CreateProfile(profile_name.toStdString(), player_index)) { QMessageBox::critical(this, tr("Create Input Profile"), tr("Failed to create the input profile \"%1\"").arg(profile_name)); - RefreshInputProfiles(); + UpdateInputProfiles(); + emit RefreshInputProfiles(player_index); return; } + emit RefreshInputProfiles(player_index); + ui->comboProfiles->addItem(profile_name); ui->comboProfiles->setCurrentIndex(ui->comboProfiles->count() - 1); } @@ -1150,10 +1153,13 @@ void ConfigureInputPlayer::DeleteProfile() { if (!profiles->DeleteProfile(profile_name.toStdString())) { QMessageBox::critical(this, tr("Delete Input Profile"), tr("Failed to delete the input profile \"%1\"").arg(profile_name)); - RefreshInputProfiles(); + UpdateInputProfiles(); + emit RefreshInputProfiles(player_index); return; } + emit RefreshInputProfiles(player_index); + ui->comboProfiles->removeItem(ui->comboProfiles->currentIndex()); ui->comboProfiles->setCurrentIndex(-1); } @@ -1170,7 +1176,8 @@ void ConfigureInputPlayer::LoadProfile() { if (!profiles->LoadProfile(profile_name.toStdString(), player_index)) { QMessageBox::critical(this, tr("Load Input Profile"), tr("Failed to load the input profile \"%1\"").arg(profile_name)); - RefreshInputProfiles(); + UpdateInputProfiles(); + emit RefreshInputProfiles(player_index); return; } @@ -1189,12 +1196,13 @@ void ConfigureInputPlayer::SaveProfile() { if (!profiles->SaveProfile(profile_name.toStdString(), player_index)) { QMessageBox::critical(this, tr("Save Input Profile"), tr("Failed to save the input profile \"%1\"").arg(profile_name)); - RefreshInputProfiles(); + UpdateInputProfiles(); + emit RefreshInputProfiles(player_index); return; } } -void ConfigureInputPlayer::RefreshInputProfiles() { +void ConfigureInputPlayer::UpdateInputProfiles() { ui->comboProfiles->clear(); for (const auto& profile_name : profiles->GetInputProfileNames()) { diff --git a/src/yuzu/configuration/configure_input_player.h b/src/yuzu/configuration/configure_input_player.h index 4895e8850..23cf6f958 100644 --- a/src/yuzu/configuration/configure_input_player.h +++ b/src/yuzu/configuration/configure_input_player.h @@ -59,6 +59,9 @@ public: /// Update the input devices combobox. void UpdateInputDeviceCombobox(); + /// Updates the list of controller profiles. + void UpdateInputProfiles(); + /// Restore all buttons to their default values. void RestoreDefaults(); @@ -72,6 +75,12 @@ signals: void HandheldStateChanged(bool is_handheld); /// Emitted when the input devices combobox is being refreshed. void RefreshInputDevices(); + /** + * Emitted when the input profiles combobox is being refreshed. + * The player_index represents the current player's index, and the profile combobox + * will not be updated for this index as they are already updated by other mechanisms. + */ + void RefreshInputProfiles(std::size_t player_index); protected: void showEvent(QShowEvent* event) override; @@ -130,9 +139,6 @@ private: /// Saves the current controller configuration into a selected controller profile. void SaveProfile(); - /// Refreshes the list of controller profiles. - void RefreshInputProfiles(); - std::unique_ptr ui; std::size_t player_index; From d8ad2f3484af7a10251a7b1ced19433b55de8ce7 Mon Sep 17 00:00:00 2001 From: Morph <39850852+Morph1984@users.noreply.github.com> Date: Sat, 31 Oct 2020 06:21:10 -0400 Subject: [PATCH 34/37] controllers/npad: Load input devices on init --- src/core/hle/service/hid/controllers/npad.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp index 30715267c..a606ceeec 100644 --- a/src/core/hle/service/hid/controllers/npad.cpp +++ b/src/core/hle/service/hid/controllers/npad.cpp @@ -214,6 +214,8 @@ void Controller_NPad::OnInit() { return; } + OnLoadInputDevices(); + if (style.raw == 0) { // We want to support all controllers style.handheld.Assign(1); From ad502093835868d5d3ae43caa9faa28c8cb621f2 Mon Sep 17 00:00:00 2001 From: Morph <39850852+Morph1984@users.noreply.github.com> Date: Tue, 10 Nov 2020 12:09:44 -0500 Subject: [PATCH 35/37] hid: Reimplement Begin/EndPermitVibrationSession Upon further investigation, these commands allow temporary vibrations even when the "Controller Vibration" system setting is disabled. As a result, vibrations are allowed when either the system setting or this flag is set to true. Therefore, we can only block vibrations when both flags are set to false. --- src/core/hle/service/hid/controllers/npad.cpp | 8 ++++++-- src/core/hle/service/hid/controllers/npad.h | 3 +++ src/core/hle/service/hid/hid.cpp | 11 ++++++++--- 3 files changed, 17 insertions(+), 5 deletions(-) diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp index a606ceeec..e2539ded8 100644 --- a/src/core/hle/service/hid/controllers/npad.cpp +++ b/src/core/hle/service/hid/controllers/npad.cpp @@ -734,7 +734,7 @@ bool Controller_NPad::VibrateControllerAtIndex(std::size_t npad_index, std::size void Controller_NPad::VibrateController(const DeviceHandle& vibration_device_handle, const VibrationValue& vibration_value) { - if (!Settings::values.vibration_enabled.GetValue()) { + if (!Settings::values.vibration_enabled.GetValue() && !permit_vibration_session_enabled) { return; } @@ -774,7 +774,7 @@ void Controller_NPad::VibrateController(const DeviceHandle& vibration_device_han void Controller_NPad::VibrateControllers(const std::vector& vibration_device_handles, const std::vector& vibration_values) { - if (!Settings::values.vibration_enabled.GetValue()) { + if (!Settings::values.vibration_enabled.GetValue() && !permit_vibration_session_enabled) { return; } @@ -811,6 +811,10 @@ void Controller_NPad::InitializeVibrationDeviceAtIndex(std::size_t npad_index, } } +void Controller_NPad::SetPermitVibrationSession(bool permit_vibration_session) { + permit_vibration_session_enabled = permit_vibration_session; +} + bool Controller_NPad::IsVibrationDeviceMounted(const DeviceHandle& vibration_device_handle) const { const auto npad_index = NPadIdToIndex(vibration_device_handle.npad_id); const auto device_index = static_cast(vibration_device_handle.device_index); diff --git a/src/core/hle/service/hid/controllers/npad.h b/src/core/hle/service/hid/controllers/npad.h index 99384524b..160dcbbe3 100644 --- a/src/core/hle/service/hid/controllers/npad.h +++ b/src/core/hle/service/hid/controllers/npad.h @@ -163,6 +163,8 @@ public: void InitializeVibrationDeviceAtIndex(std::size_t npad_index, std::size_t device_index); + void SetPermitVibrationSession(bool permit_vibration_session); + bool IsVibrationDeviceMounted(const DeviceHandle& vibration_device_handle) const; std::shared_ptr GetStyleSetChangedEvent(u32 npad_id) const; @@ -426,6 +428,7 @@ private: std::array styleset_changed_events; std::array, 10> last_vibration_timepoints; std::array, 10> latest_vibration_values{}; + bool permit_vibration_session_enabled{false}; std::array, 10> vibration_devices_mounted{}; std::array connected_controllers{}; std::array unintended_home_button_input_protection{}; diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index 2e9682bed..902516b29 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp @@ -1119,15 +1119,20 @@ void Hid::BeginPermitVibrationSession(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto applet_resource_user_id{rp.Pop()}; - LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}", - applet_resource_user_id); + applet_resource->GetController(HidController::NPad) + .SetPermitVibrationSession(true); + + LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); } void Hid::EndPermitVibrationSession(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_HID, "(STUBBED) called"); + applet_resource->GetController(HidController::NPad) + .SetPermitVibrationSession(false); + + LOG_DEBUG(Service_HID, "called"); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); From b254d528bcdd8d826c87ef720b2c247a08cc5049 Mon Sep 17 00:00:00 2001 From: Morph <39850852+Morph1984@users.noreply.github.com> Date: Wed, 11 Nov 2020 07:46:41 -0500 Subject: [PATCH 36/37] configure_input: Accommodate for the mouse input device engine --- .../configuration/configure_input_player.cpp | 18 +++++++++++++++++- src/yuzu/configuration/configure_vibration.cpp | 2 +- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp index f65a7fe73..72640f5e7 100644 --- a/src/yuzu/configuration/configure_input_player.cpp +++ b/src/yuzu/configuration/configure_input_player.cpp @@ -694,6 +694,8 @@ void ConfigureInputPlayer::UpdateInputDeviceCombobox() { const auto current_guid = button_param->Get("guid", ""); const auto current_port = button_param->Get("port", ""); + const bool is_keyboard_mouse = current_engine == "keyboard" || current_engine == "mouse"; + UpdateInputDevices(); if (buttons_empty) { @@ -702,13 +704,22 @@ void ConfigureInputPlayer::UpdateInputDeviceCombobox() { const bool all_one_device = std::all_of(buttons_param.begin(), buttons_param.end(), - [current_engine, current_guid, current_port](const Common::ParamPackage param) { + [current_engine, current_guid, current_port, + is_keyboard_mouse](const Common::ParamPackage param) { + if (is_keyboard_mouse) { + return !param.Has("engine") || param.Get("engine", "") == "keyboard" || + param.Get("engine", "") == "mouse"; + } return !param.Has("engine") || (param.Get("engine", "") == current_engine && param.Get("guid", "") == current_guid && param.Get("port", "") == current_port); }); if (all_one_device) { + if (is_keyboard_mouse) { + ui->comboDevices->setCurrentIndex(1); + return; + } const auto devices_it = std::find_if( input_devices.begin(), input_devices.end(), [current_engine, current_guid, current_port](const Common::ParamPackage param) { @@ -1073,6 +1084,11 @@ bool ConfigureInputPlayer::IsInputAcceptable(const Common::ParamPackage& params) return true; } + // Keyboard/Mouse + if (ui->comboDevices->currentIndex() == 1) { + return params.Get("engine", "") == "keyboard" || params.Get("engine", "") == "mouse"; + } + const auto current_input_device = input_devices[ui->comboDevices->currentIndex()]; return params.Get("engine", "") == current_input_device.Get("class", "") && params.Get("guid", "") == current_input_device.Get("guid", "") && diff --git a/src/yuzu/configuration/configure_vibration.cpp b/src/yuzu/configuration/configure_vibration.cpp index 714db5b80..7dcb2c5b9 100644 --- a/src/yuzu/configuration/configure_vibration.cpp +++ b/src/yuzu/configuration/configure_vibration.cpp @@ -99,7 +99,7 @@ void ConfigureVibration::SetVibrationDevices(std::size_t player_index) { const auto guid = param.Get("guid", ""); const auto port = param.Get("port", ""); - if (engine.empty() || engine == "keyboard") { + if (engine.empty() || engine == "keyboard" || engine == "mouse") { continue; } From e7e8a87927899b69bfe9f8e38f26dac08ec37abe Mon Sep 17 00:00:00 2001 From: Morph <39850852+Morph1984@users.noreply.github.com> Date: Sun, 15 Nov 2020 23:32:58 -0500 Subject: [PATCH 37/37] sdl_impl: Pump SDL Events at 1000 Hz --- src/input_common/sdl/sdl_impl.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/input_common/sdl/sdl_impl.cpp b/src/input_common/sdl/sdl_impl.cpp index 6024ed97a..8c48bb861 100644 --- a/src/input_common/sdl/sdl_impl.cpp +++ b/src/input_common/sdl/sdl_impl.cpp @@ -698,7 +698,7 @@ SDLState::SDLState() { using namespace std::chrono_literals; while (initialized) { SDL_PumpEvents(); - std::this_thread::sleep_for(5ms); + std::this_thread::sleep_for(1ms); } }); }