yuzu-emu
/
yuzu-android
Archived
1
0
Fork 0

Merge pull request #4597 from Morph1984/mjolnir-p2

Project Mjölnir: Part 2 - Controller Applet
This commit is contained in:
bunnei 2020-09-10 19:28:23 -04:00 committed by GitHub
commit 03179ecafe
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
56 changed files with 4879 additions and 249 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

@ -21,5 +21,35 @@
<file alias="single_joycon_right_vertical">single_joycon_right_vertical.png</file>
<file alias="single_joycon_right_vertical_dark">single_joycon_right_vertical_dark.png</file>
<file alias="single_joycon_right_vertical_midnight">single_joycon_right_vertical_midnight.png</file>
<file alias="applet_dual_joycon">applet_dual_joycon.png</file>
<file alias="applet_dual_joycon_dark">applet_dual_joycon_dark.png</file>
<file alias="applet_dual_joycon_midnight">applet_dual_joycon_midnight.png</file>
<file alias="applet_handheld">applet_handheld.png</file>
<file alias="applet_handheld_dark">applet_handheld_dark.png</file>
<file alias="applet_handheld_midnight">applet_handheld_midnight.png</file>
<file alias="applet_pro_controller">applet_pro_controller.png</file>
<file alias="applet_pro_controller_dark">applet_pro_controller_dark.png</file>
<file alias="applet_pro_controller_midnight">applet_pro_controller_midnight.png</file>
<file alias="applet_joycon_left">applet_single_joycon_left.png</file>
<file alias="applet_joycon_left_dark">applet_single_joycon_left_dark.png</file>
<file alias="applet_joycon_left_midnight">applet_single_joycon_left_midnight.png</file>
<file alias="applet_joycon_right">applet_single_joycon_right.png</file>
<file alias="applet_joycon_right_dark">applet_single_joycon_right_dark.png</file>
<file alias="applet_joycon_right_midnight">applet_single_joycon_right_midnight.png</file>
<file alias="applet_dual_joycon_disabled">applet_dual_joycon_disabled.png</file>
<file alias="applet_dual_joycon_dark_disabled">applet_dual_joycon_dark_disabled.png</file>
<file alias="applet_dual_joycon_midnight_disabled">applet_dual_joycon_midnight_disabled.png</file>
<file alias="applet_handheld_disabled">applet_handheld_disabled.png</file>
<file alias="applet_handheld_dark_disabled">applet_handheld_dark_disabled.png</file>
<file alias="applet_handheld_midnight_disabled">applet_handheld_midnight_disabled.png</file>
<file alias="applet_pro_controller_disabled">applet_pro_controller_disabled.png</file>
<file alias="applet_pro_controller_dark_disabled">applet_pro_controller_dark_disabled.png</file>
<file alias="applet_pro_controller_midnight_disabled">applet_pro_controller_midnight_disabled.png</file>
<file alias="applet_joycon_left_disabled">applet_single_joycon_left_disabled.png</file>
<file alias="applet_joycon_left_dark_disabled">applet_single_joycon_left_dark_disabled.png</file>
<file alias="applet_joycon_left_midnight_disabled">applet_single_joycon_left_midnight_disabled.png</file>
<file alias="applet_joycon_right_disabled">applet_single_joycon_right_disabled.png</file>
<file alias="applet_joycon_right_dark_disabled">applet_single_joycon_right_dark_disabled.png</file>
<file alias="applet_joycon_right_midnight_disabled">applet_single_joycon_right_midnight_disabled.png</file>
</qresource>
</RCC>

View File

@ -41,6 +41,99 @@ QPushButton#buttonRefreshDevices {
max-height: 20px;
}
QWidget#bottomPerGameInput,
QWidget#topControllerApplet,
QWidget#bottomControllerApplet,
QGroupBox#groupPlayer1Connected:checked,
QGroupBox#groupPlayer2Connected:checked,
QGroupBox#groupPlayer3Connected:checked,
QGroupBox#groupPlayer4Connected:checked,
QGroupBox#groupPlayer5Connected:checked,
QGroupBox#groupPlayer6Connected:checked,
QGroupBox#groupPlayer7Connected:checked,
QGroupBox#groupPlayer8Connected:checked {
background-color: #f5f5f5;
}
QWidget#topControllerApplet {
border-bottom: 1px solid #828790
}
QWidget#bottomPerGameInput,
QWidget#bottomControllerApplet {
border-top: 1px solid #828790
}
QWidget#topPerGameInput,
QWidget#middleControllerApplet {
background-color: #fff;
}
QWidget#topPerGameInput QComboBox,
QWidget#middleControllerApplet QComboBox {
width: 123px;
}
QWidget#connectedControllers {
background: transparent;
}
QWidget#playersSupported,
QWidget#controllersSupported,
QWidget#controllerSupported1,
QWidget#controllerSupported2,
QWidget#controllerSupported3,
QWidget#controllerSupported4,
QWidget#controllerSupported5,
QWidget#controllerSupported6 {
border: none;
background: transparent;
}
QGroupBox#groupPlayer1Connected,
QGroupBox#groupPlayer2Connected,
QGroupBox#groupPlayer3Connected,
QGroupBox#groupPlayer4Connected,
QGroupBox#groupPlayer5Connected,
QGroupBox#groupPlayer6Connected,
QGroupBox#groupPlayer7Connected,
QGroupBox#groupPlayer8Connected {
border: 1px solid #828790;
border-radius: 3px;
padding: 0px;
min-height: 98px;
max-height: 98px;
}
QGroupBox#groupPlayer1Connected:unchecked,
QGroupBox#groupPlayer2Connected:unchecked,
QGroupBox#groupPlayer3Connected:unchecked,
QGroupBox#groupPlayer4Connected:unchecked,
QGroupBox#groupPlayer5Connected:unchecked,
QGroupBox#groupPlayer6Connected:unchecked,
QGroupBox#groupPlayer7Connected:unchecked,
QGroupBox#groupPlayer8Connected:unchecked {
border: 1px solid #d9d9d9;
}
QGroupBox#groupPlayer1Connected::title,
QGroupBox#groupPlayer2Connected::title,
QGroupBox#groupPlayer3Connected::title,
QGroupBox#groupPlayer4Connected::title,
QGroupBox#groupPlayer5Connected::title,
QGroupBox#groupPlayer6Connected::title,
QGroupBox#groupPlayer7Connected::title,
QGroupBox#groupPlayer8Connected::title {
subcontrol-origin: margin;
subcontrol-position: top left;
padding-left: 0px;
padding-right: 0px;
padding-top: 1px;
margin-left: 0px;
margin-right: -4px;
margin-bottom: 4px;
}
QCheckBox#checkboxPlayer1Connected,
QCheckBox#checkboxPlayer2Connected,
QCheckBox#checkboxPlayer3Connected,
@ -52,6 +145,42 @@ QCheckBox#checkboxPlayer8Connected {
spacing: 0px;
}
QWidget#Player1LEDs QCheckBox,
QWidget#Player2LEDs QCheckBox,
QWidget#Player3LEDs QCheckBox,
QWidget#Player4LEDs QCheckBox,
QWidget#Player5LEDs QCheckBox,
QWidget#Player6LEDs QCheckBox,
QWidget#Player7LEDs QCheckBox,
QWidget#Player8LEDs QCheckBox {
spacing: 0px;
}
QWidget#Player1LEDs QCheckBox::indicator,
QWidget#Player2LEDs QCheckBox::indicator,
QWidget#Player3LEDs QCheckBox::indicator,
QWidget#Player4LEDs QCheckBox::indicator,
QWidget#Player5LEDs QCheckBox::indicator,
QWidget#Player6LEDs QCheckBox::indicator,
QWidget#Player7LEDs QCheckBox::indicator,
QWidget#Player8LEDs QCheckBox::indicator {
width: 6px;
height: 6px;
margin-left: 0px;
}
QWidget#bottomPerGameInput QCheckBox#checkboxPlayer1Connected::indicator,
QWidget#bottomPerGameInput QCheckBox#checkboxPlayer2Connected::indicator,
QWidget#bottomPerGameInput QCheckBox#checkboxPlayer3Connected::indicator,
QWidget#bottomPerGameInput QCheckBox#checkboxPlayer4Connected::indicator,
QWidget#bottomPerGameInput QCheckBox#checkboxPlayer5Connected::indicator,
QWidget#bottomPerGameInput QCheckBox#checkboxPlayer6Connected::indicator,
QWidget#bottomPerGameInput QCheckBox#checkboxPlayer7Connected::indicator,
QWidget#bottomPerGameInput QCheckBox#checkboxPlayer8Connected::indicator {
width: 12px;
height: 12px;
}
QCheckBox#checkboxPlayer1Connected::indicator,
QCheckBox#checkboxPlayer2Connected::indicator,
QCheckBox#checkboxPlayer3Connected::indicator,
@ -64,6 +193,34 @@ QCheckBox#checkboxPlayer8Connected::indicator {
height: 14px;
}
QGroupBox#groupPlayer1Connected::indicator,
QGroupBox#groupPlayer2Connected::indicator,
QGroupBox#groupPlayer3Connected::indicator,
QGroupBox#groupPlayer4Connected::indicator,
QGroupBox#groupPlayer5Connected::indicator,
QGroupBox#groupPlayer6Connected::indicator,
QGroupBox#groupPlayer7Connected::indicator,
QGroupBox#groupPlayer8Connected::indicator {
width: 16px;
height: 16px;
}
QWidget#Player1LEDs QCheckBox::indicator:checked,
QWidget#Player2LEDs QCheckBox::indicator:checked,
QWidget#Player3LEDs QCheckBox::indicator:checked,
QWidget#Player4LEDs QCheckBox::indicator:checked,
QWidget#Player5LEDs QCheckBox::indicator:checked,
QWidget#Player6LEDs QCheckBox::indicator:checked,
QWidget#Player7LEDs QCheckBox::indicator:checked,
QWidget#Player8LEDs QCheckBox::indicator:checked,
QGroupBox#groupPlayer1Connected::indicator:checked,
QGroupBox#groupPlayer2Connected::indicator:checked,
QGroupBox#groupPlayer3Connected::indicator:checked,
QGroupBox#groupPlayer4Connected::indicator:checked,
QGroupBox#groupPlayer5Connected::indicator:checked,
QGroupBox#groupPlayer6Connected::indicator:checked,
QGroupBox#groupPlayer7Connected::indicator:checked,
QGroupBox#groupPlayer8Connected::indicator:checked,
QCheckBox#checkboxPlayer1Connected::indicator:checked,
QCheckBox#checkboxPlayer2Connected::indicator:checked,
QCheckBox#checkboxPlayer3Connected::indicator:checked,
@ -74,11 +231,27 @@ QCheckBox#checkboxPlayer7Connected::indicator:checked,
QCheckBox#checkboxPlayer8Connected::indicator:checked,
QGroupBox#groupConnectedController::indicator:checked {
border-radius: 2px;
border: 1px solid black;
border: 1px solid #929192;
background: #39ff14;
image: none;
}
QWidget#Player1LEDs QCheckBox::indicator:unchecked,
QWidget#Player2LEDs QCheckBox::indicator:unchecked,
QWidget#Player3LEDs QCheckBox::indicator:unchecked,
QWidget#Player4LEDs QCheckBox::indicator:unchecked,
QWidget#Player5LEDs QCheckBox::indicator:unchecked,
QWidget#Player6LEDs QCheckBox::indicator:unchecked,
QWidget#Player7LEDs QCheckBox::indicator:unchecked,
QWidget#Player8LEDs QCheckBox::indicator:unchecked,
QGroupBox#groupPlayer1Connected::indicator:unchecked,
QGroupBox#groupPlayer2Connected::indicator:unchecked,
QGroupBox#groupPlayer3Connected::indicator:unchecked,
QGroupBox#groupPlayer4Connected::indicator:unchecked,
QGroupBox#groupPlayer5Connected::indicator:unchecked,
QGroupBox#groupPlayer6Connected::indicator:unchecked,
QGroupBox#groupPlayer7Connected::indicator:unchecked,
QGroupBox#groupPlayer8Connected::indicator:unchecked,
QCheckBox#checkboxPlayer1Connected::indicator:unchecked,
QCheckBox#checkboxPlayer2Connected::indicator:unchecked,
QCheckBox#checkboxPlayer3Connected::indicator:unchecked,
@ -89,7 +262,18 @@ QCheckBox#checkboxPlayer7Connected::indicator:unchecked,
QCheckBox#checkboxPlayer8Connected::indicator:unchecked,
QGroupBox#groupConnectedController::indicator:unchecked {
border-radius: 2px;
border: 1px solid black;
border: 1px solid #929192;
background: transparent;
image: none;
}
QWidget#controllerPlayer1,
QWidget#controllerPlayer2,
QWidget#controllerPlayer3,
QWidget#controllerPlayer4,
QWidget#controllerPlayer5,
QWidget#controllerPlayer6,
QWidget#controllerPlayer7,
QWidget#controllerPlayer8 {
background: transparent;
}

View File

@ -1284,59 +1284,6 @@ QPushButton#buttonRefreshDevices {
padding: 0px 0px;
}
QCheckBox#checkboxPlayer1Connected,
QCheckBox#checkboxPlayer2Connected,
QCheckBox#checkboxPlayer3Connected,
QCheckBox#checkboxPlayer4Connected,
QCheckBox#checkboxPlayer5Connected,
QCheckBox#checkboxPlayer6Connected,
QCheckBox#checkboxPlayer7Connected,
QCheckBox#checkboxPlayer8Connected {
spacing: 0px;
}
QCheckBox#checkboxPlayer1Connected::indicator,
QCheckBox#checkboxPlayer2Connected::indicator,
QCheckBox#checkboxPlayer3Connected::indicator,
QCheckBox#checkboxPlayer4Connected::indicator,
QCheckBox#checkboxPlayer5Connected::indicator,
QCheckBox#checkboxPlayer6Connected::indicator,
QCheckBox#checkboxPlayer7Connected::indicator,
QCheckBox#checkboxPlayer8Connected::indicator {
width: 14px;
height: 14px;
}
QCheckBox#checkboxPlayer1Connected::indicator:checked,
QCheckBox#checkboxPlayer2Connected::indicator:checked,
QCheckBox#checkboxPlayer3Connected::indicator:checked,
QCheckBox#checkboxPlayer4Connected::indicator:checked,
QCheckBox#checkboxPlayer5Connected::indicator:checked,
QCheckBox#checkboxPlayer6Connected::indicator:checked,
QCheckBox#checkboxPlayer7Connected::indicator:checked,
QCheckBox#checkboxPlayer8Connected::indicator:checked,
QGroupBox#groupConnectedController::indicator:checked {
border-radius: 2px;
border: 1px solid #929192;
background: #39ff14;
image: none;
}
QCheckBox#checkboxPlayer1Connected::indicator:unchecked,
QCheckBox#checkboxPlayer2Connected::indicator:unchecked,
QCheckBox#checkboxPlayer3Connected::indicator:unchecked,
QCheckBox#checkboxPlayer4Connected::indicator:unchecked,
QCheckBox#checkboxPlayer5Connected::indicator:unchecked,
QCheckBox#checkboxPlayer6Connected::indicator:unchecked,
QCheckBox#checkboxPlayer7Connected::indicator:unchecked,
QCheckBox#checkboxPlayer8Connected::indicator:unchecked,
QGroupBox#groupConnectedController::indicator:unchecked {
border-radius: 2px;
border: 1px solid #929192;
background: transparent;
image: none;
}
QSpinBox#spinboxLStickRange,
QSpinBox#spinboxRStickRange {
padding: 4px 0px 5px 0px;
@ -1372,6 +1319,257 @@ QGroupBox#vibrationGroup::title {
padding-right: 1px;
}
QWidget#bottomPerGameInput,
QWidget#topControllerApplet,
QWidget#bottomControllerApplet,
QGroupBox#groupPlayer1Connected:checked,
QGroupBox#groupPlayer2Connected:checked,
QGroupBox#groupPlayer3Connected:checked,
QGroupBox#groupPlayer4Connected:checked,
QGroupBox#groupPlayer5Connected:checked,
QGroupBox#groupPlayer6Connected:checked,
QGroupBox#groupPlayer7Connected:checked,
QGroupBox#groupPlayer8Connected:checked {
background-color: #232629;
}
QWidget#topPerGameInput,
QWidget#middleControllerApplet {
background-color: #31363b;
}
QWidget#topPerGameInput QComboBox,
QWidget#middleControllerApplet QComboBox {
width: 119px;
}
QRadioButton#radioDocked {
margin-left: -3px;
}
QRadioButton#radioUndocked {
margin-right: 5px;
}
QWidget#connectedControllers {
background: transparent;
}
QWidget#playersSupported,
QWidget#controllersSupported,
QWidget#controllerSupported1,
QWidget#controllerSupported2,
QWidget#controllerSupported3,
QWidget#controllerSupported4,
QWidget#controllerSupported5,
QWidget#controllerSupported6 {
border: none;
background: transparent;
}
QGroupBox#groupPlayer1Connected,
QGroupBox#groupPlayer2Connected,
QGroupBox#groupPlayer3Connected,
QGroupBox#groupPlayer4Connected,
QGroupBox#groupPlayer5Connected,
QGroupBox#groupPlayer6Connected,
QGroupBox#groupPlayer7Connected,
QGroupBox#groupPlayer8Connected {
border: 1px solid #76797c;
border-radius: 3px;
padding: 0px;
min-height: 98px;
max-height: 98px;
margin-top: 0px;
}
QGroupBox#groupPlayer1Connected:unchecked,
QGroupBox#groupPlayer2Connected:unchecked,
QGroupBox#groupPlayer3Connected:unchecked,
QGroupBox#groupPlayer4Connected:unchecked,
QGroupBox#groupPlayer5Connected:unchecked,
QGroupBox#groupPlayer6Connected:unchecked,
QGroupBox#groupPlayer7Connected:unchecked,
QGroupBox#groupPlayer8Connected:unchecked {
border: 1px solid #54575b;
}
QGroupBox#groupPlayer1Connected::title,
QGroupBox#groupPlayer2Connected::title,
QGroupBox#groupPlayer3Connected::title,
QGroupBox#groupPlayer4Connected::title,
QGroupBox#groupPlayer5Connected::title,
QGroupBox#groupPlayer6Connected::title,
QGroupBox#groupPlayer7Connected::title,
QGroupBox#groupPlayer8Connected::title {
subcontrol-origin: margin;
subcontrol-position: top left;
padding-left: 0px;
padding-right: 0px;
padding-top: 1px;
margin-left: -2px;
margin-right: -4px;
margin-bottom: 6px;
}
QCheckBox#checkboxPlayer1Connected,
QCheckBox#checkboxPlayer2Connected,
QCheckBox#checkboxPlayer3Connected,
QCheckBox#checkboxPlayer4Connected,
QCheckBox#checkboxPlayer5Connected,
QCheckBox#checkboxPlayer6Connected,
QCheckBox#checkboxPlayer7Connected,
QCheckBox#checkboxPlayer8Connected {
spacing: 0px;
}
QWidget#Player1LEDs,
QWidget#Player2LEDs,
QWidget#Player3LEDs,
QWidget#Player4LEDs,
QWidget#Player5LEDs,
QWidget#Player6LEDs,
QWidget#Player7LEDs,
QWidget#Player8LEDs {
background: transparent;
}
QWidget#Player1LEDs QCheckBox,
QWidget#Player2LEDs QCheckBox,
QWidget#Player3LEDs QCheckBox,
QWidget#Player4LEDs QCheckBox,
QWidget#Player5LEDs QCheckBox,
QWidget#Player6LEDs QCheckBox,
QWidget#Player7LEDs QCheckBox,
QWidget#Player8LEDs QCheckBox {
spacing: 0px;
margin-bottom: 0px;
margin-right: 0px;
}
QWidget#Player1LEDs QCheckBox::indicator,
QWidget#Player2LEDs QCheckBox::indicator,
QWidget#Player3LEDs QCheckBox::indicator,
QWidget#Player4LEDs QCheckBox::indicator,
QWidget#Player5LEDs QCheckBox::indicator,
QWidget#Player6LEDs QCheckBox::indicator,
QWidget#Player7LEDs QCheckBox::indicator,
QWidget#Player8LEDs QCheckBox::indicator {
width: 6px;
height: 6px;
margin-left: 0px;
}
QWidget#bottomPerGameInput QCheckBox#checkboxPlayer1Connected::indicator,
QWidget#bottomPerGameInput QCheckBox#checkboxPlayer2Connected::indicator,
QWidget#bottomPerGameInput QCheckBox#checkboxPlayer3Connected::indicator,
QWidget#bottomPerGameInput QCheckBox#checkboxPlayer4Connected::indicator,
QWidget#bottomPerGameInput QCheckBox#checkboxPlayer5Connected::indicator,
QWidget#bottomPerGameInput QCheckBox#checkboxPlayer6Connected::indicator,
QWidget#bottomPerGameInput QCheckBox#checkboxPlayer7Connected::indicator,
QWidget#bottomPerGameInput QCheckBox#checkboxPlayer8Connected::indicator {
width: 12px;
height: 12px;
}
QCheckBox#checkboxPlayer1Connected::indicator,
QCheckBox#checkboxPlayer2Connected::indicator,
QCheckBox#checkboxPlayer3Connected::indicator,
QCheckBox#checkboxPlayer4Connected::indicator,
QCheckBox#checkboxPlayer5Connected::indicator,
QCheckBox#checkboxPlayer6Connected::indicator,
QCheckBox#checkboxPlayer7Connected::indicator,
QCheckBox#checkboxPlayer8Connected::indicator {
width: 14px;
height: 14px;
}
QGroupBox#groupPlayer1Connected::indicator,
QGroupBox#groupPlayer2Connected::indicator,
QGroupBox#groupPlayer3Connected::indicator,
QGroupBox#groupPlayer4Connected::indicator,
QGroupBox#groupPlayer5Connected::indicator,
QGroupBox#groupPlayer6Connected::indicator,
QGroupBox#groupPlayer7Connected::indicator,
QGroupBox#groupPlayer8Connected::indicator {
width: 16px;
height: 16px;
}
QWidget#Player1LEDs QCheckBox::indicator:checked,
QWidget#Player2LEDs QCheckBox::indicator:checked,
QWidget#Player3LEDs QCheckBox::indicator:checked,
QWidget#Player4LEDs QCheckBox::indicator:checked,
QWidget#Player5LEDs QCheckBox::indicator:checked,
QWidget#Player6LEDs QCheckBox::indicator:checked,
QWidget#Player7LEDs QCheckBox::indicator:checked,
QWidget#Player8LEDs QCheckBox::indicator:checked,
QGroupBox#groupPlayer1Connected::indicator:checked,
QGroupBox#groupPlayer2Connected::indicator:checked,
QGroupBox#groupPlayer3Connected::indicator:checked,
QGroupBox#groupPlayer4Connected::indicator:checked,
QGroupBox#groupPlayer5Connected::indicator:checked,
QGroupBox#groupPlayer6Connected::indicator:checked,
QGroupBox#groupPlayer7Connected::indicator:checked,
QGroupBox#groupPlayer8Connected::indicator:checked,
QCheckBox#checkboxPlayer1Connected::indicator:checked,
QCheckBox#checkboxPlayer2Connected::indicator:checked,
QCheckBox#checkboxPlayer3Connected::indicator:checked,
QCheckBox#checkboxPlayer4Connected::indicator:checked,
QCheckBox#checkboxPlayer5Connected::indicator:checked,
QCheckBox#checkboxPlayer6Connected::indicator:checked,
QCheckBox#checkboxPlayer7Connected::indicator:checked,
QCheckBox#checkboxPlayer8Connected::indicator:checked,
QGroupBox#groupConnectedController::indicator:checked {
border-radius: 2px;
border: 1px solid #929192;
background: #39ff14;
image: none;
}
QWidget#Player1LEDs QCheckBox::indicator:unchecked,
QWidget#Player2LEDs QCheckBox::indicator:unchecked,
QWidget#Player3LEDs QCheckBox::indicator:unchecked,
QWidget#Player4LEDs QCheckBox::indicator:unchecked,
QWidget#Player5LEDs QCheckBox::indicator:unchecked,
QWidget#Player6LEDs QCheckBox::indicator:unchecked,
QWidget#Player7LEDs QCheckBox::indicator:unchecked,
QWidget#Player8LEDs QCheckBox::indicator:unchecked,
QGroupBox#groupPlayer1Connected::indicator:unchecked,
QGroupBox#groupPlayer2Connected::indicator:unchecked,
QGroupBox#groupPlayer3Connected::indicator:unchecked,
QGroupBox#groupPlayer4Connected::indicator:unchecked,
QGroupBox#groupPlayer5Connected::indicator:unchecked,
QGroupBox#groupPlayer6Connected::indicator:unchecked,
QGroupBox#groupPlayer7Connected::indicator:unchecked,
QGroupBox#groupPlayer8Connected::indicator:unchecked,
QCheckBox#checkboxPlayer1Connected::indicator:unchecked,
QCheckBox#checkboxPlayer2Connected::indicator:unchecked,
QCheckBox#checkboxPlayer3Connected::indicator:unchecked,
QCheckBox#checkboxPlayer4Connected::indicator:unchecked,
QCheckBox#checkboxPlayer5Connected::indicator:unchecked,
QCheckBox#checkboxPlayer6Connected::indicator:unchecked,
QCheckBox#checkboxPlayer7Connected::indicator:unchecked,
QCheckBox#checkboxPlayer8Connected::indicator:unchecked,
QGroupBox#groupConnectedController::indicator:unchecked {
border-radius: 2px;
border: 1px solid #929192;
background: transparent;
image: none;
}
QWidget#controllerPlayer1,
QWidget#controllerPlayer2,
QWidget#controllerPlayer3,
QWidget#controllerPlayer4,
QWidget#controllerPlayer5,
QWidget#controllerPlayer6,
QWidget#controllerPlayer7,
QWidget#controllerPlayer8 {
background: transparent;
}
/* touchscreen mapping widget */
TouchScreenPreview {
qproperty-dotHighlightColor: #3daee9;

View File

@ -2205,60 +2205,6 @@ QPushButton#buttonRefreshDevices {
padding: 0px 0px;
}
QCheckBox#checkboxPlayer1Connected,
QCheckBox#checkboxPlayer2Connected,
QCheckBox#checkboxPlayer3Connected,
QCheckBox#checkboxPlayer4Connected,
QCheckBox#checkboxPlayer5Connected,
QCheckBox#checkboxPlayer6Connected,
QCheckBox#checkboxPlayer7Connected,
QCheckBox#checkboxPlayer8Connected {
spacing: 0px;
}
QCheckBox#checkboxPlayer1Connected::indicator,
QCheckBox#checkboxPlayer2Connected::indicator,
QCheckBox#checkboxPlayer3Connected::indicator,
QCheckBox#checkboxPlayer4Connected::indicator,
QCheckBox#checkboxPlayer5Connected::indicator,
QCheckBox#checkboxPlayer6Connected::indicator,
QCheckBox#checkboxPlayer7Connected::indicator,
QCheckBox#checkboxPlayer8Connected::indicator {
width: 14px;
height: 14px;
}
QCheckBox#checkboxPlayer1Connected::indicator:checked,
QCheckBox#checkboxPlayer2Connected::indicator:checked,
QCheckBox#checkboxPlayer3Connected::indicator:checked,
QCheckBox#checkboxPlayer4Connected::indicator:checked,
QCheckBox#checkboxPlayer5Connected::indicator:checked,
QCheckBox#checkboxPlayer6Connected::indicator:checked,
QCheckBox#checkboxPlayer7Connected::indicator:checked,
QCheckBox#checkboxPlayer8Connected::indicator:checked,
QGroupBox#groupConnectedController::indicator:checked {
border-radius: 2px;
border: 1px solid #929192;
background: #39ff14;
image: none;
}
QCheckBox#checkboxPlayer1Connected::indicator:unchecked,
QCheckBox#checkboxPlayer2Connected::indicator:unchecked,
QCheckBox#checkboxPlayer3Connected::indicator:unchecked,
QCheckBox#checkboxPlayer4Connected::indicator:unchecked,
QCheckBox#checkboxPlayer5Connected::indicator:unchecked,
QCheckBox#checkboxPlayer6Connected::indicator:unchecked,
QCheckBox#checkboxPlayer7Connected::indicator:unchecked,
QCheckBox#checkboxPlayer8Connected::indicator:unchecked,
QGroupBox#groupConnectedController::indicator:unchecked {
border-radius: 2px;
border: 1px solid #929192;
background: transparent;
image: none;
}
QSpinBox#spinboxLStickRange,
QSpinBox#spinboxRStickRange {
min-width: 38px;
@ -2269,6 +2215,12 @@ QGroupBox#vibrationGroup::indicator {
margin-left: 0px;
}
QWidget#bottomPerGameInput QGroupBox#motionGroup,
QWidget#bottomPerGameInput QGroupBox#vibrationGroup,
QWidget#bottomPerGameInput QGroupBox#inputConfigGroup {
padding: 0px;
}
QGroupBox#motionGroup::title,
QGroupBox#vibrationGroup::title {
spacing: 2px;
@ -2286,3 +2238,267 @@ QTreeView#hotkey_list,
QScrollArea#scrollArea QTreeView {
background-color: #0f1922;
}
QWidget#bottomPerGameInput,
QWidget#topControllerApplet,
QWidget#bottomControllerApplet,
QGroupBox#groupPlayer1Connected:checked,
QGroupBox#groupPlayer2Connected:checked,
QGroupBox#groupPlayer3Connected:checked,
QGroupBox#groupPlayer4Connected:checked,
QGroupBox#groupPlayer5Connected:checked,
QGroupBox#groupPlayer6Connected:checked,
QGroupBox#groupPlayer7Connected:checked,
QGroupBox#groupPlayer8Connected:checked {
background-color: #0f1922;
}
QWidget#topPerGameInput,
QWidget#middleControllerApplet {
background-color: #19232d;
}
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;
}
QWidget#connectedControllers {
background: transparent;
}
QWidget#playersSupported,
QWidget#controllersSupported,
QWidget#controllerSupported1,
QWidget#controllerSupported2,
QWidget#controllerSupported3,
QWidget#controllerSupported4,
QWidget#controllerSupported5,
QWidget#controllerSupported6 {
border: none;
background: transparent;
}
QGroupBox#groupPlayer1Connected,
QGroupBox#groupPlayer2Connected,
QGroupBox#groupPlayer3Connected,
QGroupBox#groupPlayer4Connected,
QGroupBox#groupPlayer5Connected,
QGroupBox#groupPlayer6Connected,
QGroupBox#groupPlayer7Connected,
QGroupBox#groupPlayer8Connected {
border: 1px solid #76797c;
border-radius: 3px;
padding: 0px;
min-height: 98px;
max-height: 98px;
margin-top: 0px;
}
QGroupBox#groupPlayer1Connected:unchecked,
QGroupBox#groupPlayer2Connected:unchecked,
QGroupBox#groupPlayer3Connected:unchecked,
QGroupBox#groupPlayer4Connected:unchecked,
QGroupBox#groupPlayer5Connected:unchecked,
QGroupBox#groupPlayer6Connected:unchecked,
QGroupBox#groupPlayer7Connected:unchecked,
QGroupBox#groupPlayer8Connected:unchecked {
border: 1px solid #32414b;
}
QGroupBox#groupPlayer1Connected::title,
QGroupBox#groupPlayer2Connected::title,
QGroupBox#groupPlayer3Connected::title,
QGroupBox#groupPlayer4Connected::title,
QGroupBox#groupPlayer5Connected::title,
QGroupBox#groupPlayer6Connected::title,
QGroupBox#groupPlayer7Connected::title,
QGroupBox#groupPlayer8Connected::title {
subcontrol-origin: margin;
subcontrol-position: top left;
padding-left: 0px;
padding-right: 0px;
padding-top: 1px;
margin-left: -2px;
margin-right: -4px;
margin-bottom: 6px;
}
QCheckBox#checkboxPlayer1Connected,
QCheckBox#checkboxPlayer2Connected,
QCheckBox#checkboxPlayer3Connected,
QCheckBox#checkboxPlayer4Connected,
QCheckBox#checkboxPlayer5Connected,
QCheckBox#checkboxPlayer6Connected,
QCheckBox#checkboxPlayer7Connected,
QCheckBox#checkboxPlayer8Connected {
spacing: 0px;
}
QWidget#connectedControllers QLabel {
padding: 0px;
}
QWidget#Player1LEDs,
QWidget#Player2LEDs,
QWidget#Player3LEDs,
QWidget#Player4LEDs,
QWidget#Player5LEDs,
QWidget#Player6LEDs,
QWidget#Player7LEDs,
QWidget#Player8LEDs {
background: transparent;
}
QWidget#Player1LEDs QCheckBox,
QWidget#Player2LEDs QCheckBox,
QWidget#Player3LEDs QCheckBox,
QWidget#Player4LEDs QCheckBox,
QWidget#Player5LEDs QCheckBox,
QWidget#Player6LEDs QCheckBox,
QWidget#Player7LEDs QCheckBox,
QWidget#Player8LEDs QCheckBox,
QCheckBox#checkboxPlayer1Connected,
QCheckBox#checkboxPlayer2Connected,
QCheckBox#checkboxPlayer3Connected,
QCheckBox#checkboxPlayer4Connected,
QCheckBox#checkboxPlayer5Connected,
QCheckBox#checkboxPlayer6Connected,
QCheckBox#checkboxPlayer7Connected,
QCheckBox#checkboxPlayer8Connected {
spacing: 0px;
padding-top: 0px;
padding-bottom: 0px;
background: transparent;
}
QWidget#Player1LEDs QCheckBox::indicator,
QWidget#Player2LEDs QCheckBox::indicator,
QWidget#Player3LEDs QCheckBox::indicator,
QWidget#Player4LEDs QCheckBox::indicator,
QWidget#Player5LEDs QCheckBox::indicator,
QWidget#Player6LEDs QCheckBox::indicator,
QWidget#Player7LEDs QCheckBox::indicator,
QWidget#Player8LEDs QCheckBox::indicator {
width: 6px;
height: 6px;
margin-left: 0px;
}
QWidget#bottomPerGameInput QCheckBox#checkboxPlayer1Connected::indicator,
QWidget#bottomPerGameInput QCheckBox#checkboxPlayer2Connected::indicator,
QWidget#bottomPerGameInput QCheckBox#checkboxPlayer3Connected::indicator,
QWidget#bottomPerGameInput QCheckBox#checkboxPlayer4Connected::indicator,
QWidget#bottomPerGameInput QCheckBox#checkboxPlayer5Connected::indicator,
QWidget#bottomPerGameInput QCheckBox#checkboxPlayer6Connected::indicator,
QWidget#bottomPerGameInput QCheckBox#checkboxPlayer7Connected::indicator,
QWidget#bottomPerGameInput QCheckBox#checkboxPlayer8Connected::indicator {
width: 12px;
height: 12px;
}
QCheckBox#checkboxPlayer1Connected::indicator,
QCheckBox#checkboxPlayer2Connected::indicator,
QCheckBox#checkboxPlayer3Connected::indicator,
QCheckBox#checkboxPlayer4Connected::indicator,
QCheckBox#checkboxPlayer5Connected::indicator,
QCheckBox#checkboxPlayer6Connected::indicator,
QCheckBox#checkboxPlayer7Connected::indicator,
QCheckBox#checkboxPlayer8Connected::indicator {
width: 14px;
height: 14px;
margin-left: 2px;
}
QWidget#Player1LEDs QCheckBox::indicator:checked,
QWidget#Player2LEDs QCheckBox::indicator:checked,
QWidget#Player3LEDs QCheckBox::indicator:checked,
QWidget#Player4LEDs QCheckBox::indicator:checked,
QWidget#Player5LEDs QCheckBox::indicator:checked,
QWidget#Player6LEDs QCheckBox::indicator:checked,
QWidget#Player7LEDs QCheckBox::indicator:checked,
QWidget#Player8LEDs QCheckBox::indicator:checked,
QGroupBox#groupPlayer1Connected::indicator:checked,
QGroupBox#groupPlayer2Connected::indicator:checked,
QGroupBox#groupPlayer3Connected::indicator:checked,
QGroupBox#groupPlayer4Connected::indicator:checked,
QGroupBox#groupPlayer5Connected::indicator:checked,
QGroupBox#groupPlayer6Connected::indicator:checked,
QGroupBox#groupPlayer7Connected::indicator:checked,
QGroupBox#groupPlayer8Connected::indicator:checked,
QCheckBox#checkboxPlayer1Connected::indicator:checked,
QCheckBox#checkboxPlayer2Connected::indicator:checked,
QCheckBox#checkboxPlayer3Connected::indicator:checked,
QCheckBox#checkboxPlayer4Connected::indicator:checked,
QCheckBox#checkboxPlayer5Connected::indicator:checked,
QCheckBox#checkboxPlayer6Connected::indicator:checked,
QCheckBox#checkboxPlayer7Connected::indicator:checked,
QCheckBox#checkboxPlayer8Connected::indicator:checked,
QGroupBox#groupConnectedController::indicator:checked {
border-radius: 2px;
border: 1px solid #929192;
background: #39ff14;
image: none;
}
QWidget#Player1LEDs QCheckBox::indicator:unchecked,
QWidget#Player2LEDs QCheckBox::indicator:unchecked,
QWidget#Player3LEDs QCheckBox::indicator:unchecked,
QWidget#Player4LEDs QCheckBox::indicator:unchecked,
QWidget#Player5LEDs QCheckBox::indicator:unchecked,
QWidget#Player6LEDs QCheckBox::indicator:unchecked,
QWidget#Player7LEDs QCheckBox::indicator:unchecked,
QWidget#Player8LEDs QCheckBox::indicator:unchecked,
QGroupBox#groupPlayer1Connected::indicator:unchecked,
QGroupBox#groupPlayer2Connected::indicator:unchecked,
QGroupBox#groupPlayer3Connected::indicator:unchecked,
QGroupBox#groupPlayer4Connected::indicator:unchecked,
QGroupBox#groupPlayer5Connected::indicator:unchecked,
QGroupBox#groupPlayer6Connected::indicator:unchecked,
QGroupBox#groupPlayer7Connected::indicator:unchecked,
QGroupBox#groupPlayer8Connected::indicator:unchecked,
QCheckBox#checkboxPlayer1Connected::indicator:unchecked,
QCheckBox#checkboxPlayer2Connected::indicator:unchecked,
QCheckBox#checkboxPlayer3Connected::indicator:unchecked,
QCheckBox#checkboxPlayer4Connected::indicator:unchecked,
QCheckBox#checkboxPlayer5Connected::indicator:unchecked,
QCheckBox#checkboxPlayer6Connected::indicator:unchecked,
QCheckBox#checkboxPlayer7Connected::indicator:unchecked,
QCheckBox#checkboxPlayer8Connected::indicator:unchecked,
QGroupBox#groupConnectedController::indicator:unchecked {
border-radius: 2px;
border: 1px solid #929192;
background: #19232d;
image: none;
}
QWidget#controllerPlayer1,
QWidget#controllerPlayer2,
QWidget#controllerPlayer3,
QWidget#controllerPlayer4,
QWidget#controllerPlayer5,
QWidget#controllerPlayer6,
QWidget#controllerPlayer7,
QWidget#controllerPlayer8 {
background: transparent;
}

View File

@ -126,6 +126,8 @@ add_library(core STATIC
file_sys/vfs_vector.h
file_sys/xts_archive.cpp
file_sys/xts_archive.h
frontend/applets/controller.cpp
frontend/applets/controller.h
frontend/applets/error.cpp
frontend/applets/error.h
frontend/applets/general_frontend.cpp
@ -244,6 +246,8 @@ add_library(core STATIC
hle/service/am/applet_oe.h
hle/service/am/applets/applets.cpp
hle/service/am/applets/applets.h
hle/service/am/applets/controller.cpp
hle/service/am/applets/controller.h
hle/service/am/applets/error.cpp
hle/service/am/applets/error.h
hle/service/am/applets/general_backend.cpp

View File

@ -0,0 +1,81 @@
// Copyright 2020 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/assert.h"
#include "common/logging/log.h"
#include "core/core.h"
#include "core/frontend/applets/controller.h"
#include "core/hle/service/hid/controllers/npad.h"
#include "core/hle/service/hid/hid.h"
#include "core/hle/service/sm/sm.h"
namespace Core::Frontend {
ControllerApplet::~ControllerApplet() = default;
DefaultControllerApplet::~DefaultControllerApplet() = default;
void DefaultControllerApplet::ReconfigureControllers(std::function<void()> callback,
ControllerParameters parameters) const {
LOG_INFO(Service_HID, "called, deducing the best configuration based on the given parameters!");
auto& npad =
Core::System::GetInstance()
.ServiceManager()
.GetService<Service::HID::Hid>("hid")
->GetAppletResource()
->GetController<Service::HID::Controller_NPad>(Service::HID::HidController::NPad);
auto& players = Settings::values.players;
const std::size_t min_supported_players =
parameters.enable_single_mode ? 1 : parameters.min_players;
// Disconnect Handheld first.
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);
// Only connect the minimum number of required players.
if (index >= min_supported_players) {
continue;
}
// Connect controllers based on the following priority list from highest to lowest priority:
// Pro Controller -> Dual Joycons -> Left Joycon/Right Joycon -> Handheld
if (parameters.allow_pro_controller) {
npad.AddNewControllerAt(
npad.MapSettingsTypeToNPad(Settings::ControllerType::ProController), index);
} else if (parameters.allow_dual_joycons) {
npad.AddNewControllerAt(
npad.MapSettingsTypeToNPad(Settings::ControllerType::DualJoyconDetached), index);
} else if (parameters.allow_left_joycon && parameters.allow_right_joycon) {
// Assign left joycons to even player indices and right joycons to odd player indices.
// We do this since Captain Toad Treasure Tracker expects a left joycon for Player 1 and
// a right Joycon for Player 2 in 2 Player Assist mode.
if (index % 2 == 0) {
npad.AddNewControllerAt(
npad.MapSettingsTypeToNPad(Settings::ControllerType::LeftJoycon), index);
} else {
npad.AddNewControllerAt(
npad.MapSettingsTypeToNPad(Settings::ControllerType::RightJoycon), index);
}
} else if (index == 0 && parameters.enable_single_mode && parameters.allow_handheld &&
!Settings::values.use_docked_mode) {
// We should *never* reach here under any normal circumstances.
npad.AddNewControllerAt(npad.MapSettingsTypeToNPad(Settings::ControllerType::Handheld),
index);
} else {
UNREACHABLE_MSG("Unable to add a new controller based on the given parameters!");
}
}
callback();
}
} // namespace Core::Frontend

View File

@ -0,0 +1,48 @@
// Copyright 2020 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <functional>
#include "common/common_types.h"
namespace Core::Frontend {
using BorderColor = std::array<u8, 4>;
using ExplainText = std::array<char, 0x81>;
struct ControllerParameters {
s8 min_players{};
s8 max_players{};
bool keep_controllers_connected{};
bool enable_single_mode{};
bool enable_border_color{};
std::vector<BorderColor> border_colors{};
bool enable_explain_text{};
std::vector<ExplainText> explain_text{};
bool allow_pro_controller{};
bool allow_handheld{};
bool allow_dual_joycons{};
bool allow_left_joycon{};
bool allow_right_joycon{};
};
class ControllerApplet {
public:
virtual ~ControllerApplet();
virtual void ReconfigureControllers(std::function<void()> callback,
ControllerParameters parameters) const = 0;
};
class DefaultControllerApplet final : public ControllerApplet {
public:
~DefaultControllerApplet() override;
void ReconfigureControllers(std::function<void()> callback,
ControllerParameters parameters) const override;
};
} // namespace Core::Frontend

View File

@ -5,6 +5,7 @@
#include <cstring>
#include "common/assert.h"
#include "core/core.h"
#include "core/frontend/applets/controller.h"
#include "core/frontend/applets/error.h"
#include "core/frontend/applets/general_frontend.h"
#include "core/frontend/applets/profile_select.h"
@ -15,6 +16,7 @@
#include "core/hle/kernel/writable_event.h"
#include "core/hle/service/am/am.h"
#include "core/hle/service/am/applets/applets.h"
#include "core/hle/service/am/applets/controller.h"
#include "core/hle/service/am/applets/error.h"
#include "core/hle/service/am/applets/general_backend.h"
#include "core/hle/service/am/applets/profile_select.h"
@ -140,14 +142,14 @@ void Applet::Initialize() {
AppletFrontendSet::AppletFrontendSet() = default;
AppletFrontendSet::AppletFrontendSet(ParentalControlsApplet parental_controls, ErrorApplet error,
AppletFrontendSet::AppletFrontendSet(ControllerApplet controller, ECommerceApplet e_commerce,
ErrorApplet error, ParentalControlsApplet parental_controls,
PhotoViewer photo_viewer, ProfileSelect profile_select,
SoftwareKeyboard software_keyboard, WebBrowser web_browser,
ECommerceApplet e_commerce)
: parental_controls{std::move(parental_controls)}, error{std::move(error)},
photo_viewer{std::move(photo_viewer)}, profile_select{std::move(profile_select)},
software_keyboard{std::move(software_keyboard)}, web_browser{std::move(web_browser)},
e_commerce{std::move(e_commerce)} {}
SoftwareKeyboard software_keyboard, WebBrowser web_browser)
: controller{std::move(controller)}, e_commerce{std::move(e_commerce)}, error{std::move(error)},
parental_controls{std::move(parental_controls)}, photo_viewer{std::move(photo_viewer)},
profile_select{std::move(profile_select)}, software_keyboard{std::move(software_keyboard)},
web_browser{std::move(web_browser)} {}
AppletFrontendSet::~AppletFrontendSet() = default;
@ -164,37 +166,62 @@ const AppletFrontendSet& AppletManager::GetAppletFrontendSet() const {
}
void AppletManager::SetAppletFrontendSet(AppletFrontendSet set) {
if (set.parental_controls != nullptr)
frontend.parental_controls = std::move(set.parental_controls);
if (set.error != nullptr)
frontend.error = std::move(set.error);
if (set.photo_viewer != nullptr)
frontend.photo_viewer = std::move(set.photo_viewer);
if (set.profile_select != nullptr)
frontend.profile_select = std::move(set.profile_select);
if (set.software_keyboard != nullptr)
frontend.software_keyboard = std::move(set.software_keyboard);
if (set.web_browser != nullptr)
frontend.web_browser = std::move(set.web_browser);
if (set.e_commerce != nullptr)
if (set.controller != nullptr) {
frontend.controller = std::move(set.controller);
}
if (set.e_commerce != nullptr) {
frontend.e_commerce = std::move(set.e_commerce);
}
if (set.error != nullptr) {
frontend.error = std::move(set.error);
}
if (set.parental_controls != nullptr) {
frontend.parental_controls = std::move(set.parental_controls);
}
if (set.photo_viewer != nullptr) {
frontend.photo_viewer = std::move(set.photo_viewer);
}
if (set.profile_select != nullptr) {
frontend.profile_select = std::move(set.profile_select);
}
if (set.software_keyboard != nullptr) {
frontend.software_keyboard = std::move(set.software_keyboard);
}
if (set.web_browser != nullptr) {
frontend.web_browser = std::move(set.web_browser);
}
}
void AppletManager::SetDefaultAppletFrontendSet() {
ClearAll();
SetDefaultAppletsIfMissing();
}
void AppletManager::SetDefaultAppletsIfMissing() {
if (frontend.parental_controls == nullptr) {
frontend.parental_controls =
std::make_unique<Core::Frontend::DefaultParentalControlsApplet>();
if (frontend.controller == nullptr) {
frontend.controller = std::make_unique<Core::Frontend::DefaultControllerApplet>();
}
if (frontend.e_commerce == nullptr) {
frontend.e_commerce = std::make_unique<Core::Frontend::DefaultECommerceApplet>();
}
if (frontend.error == nullptr) {
frontend.error = std::make_unique<Core::Frontend::DefaultErrorApplet>();
}
if (frontend.parental_controls == nullptr) {
frontend.parental_controls =
std::make_unique<Core::Frontend::DefaultParentalControlsApplet>();
}
if (frontend.photo_viewer == nullptr) {
frontend.photo_viewer = std::make_unique<Core::Frontend::DefaultPhotoViewerApplet>();
}
@ -211,10 +238,6 @@ void AppletManager::SetDefaultAppletsIfMissing() {
if (frontend.web_browser == nullptr) {
frontend.web_browser = std::make_unique<Core::Frontend::DefaultWebBrowserApplet>();
}
if (frontend.e_commerce == nullptr) {
frontend.e_commerce = std::make_unique<Core::Frontend::DefaultECommerceApplet>();
}
}
void AppletManager::ClearAll() {
@ -225,6 +248,8 @@ std::shared_ptr<Applet> AppletManager::GetApplet(AppletId id) const {
switch (id) {
case AppletId::Auth:
return std::make_shared<Auth>(system, *frontend.parental_controls);
case AppletId::Controller:
return std::make_shared<Controller>(system, *frontend.controller);
case AppletId::Error:
return std::make_shared<Error>(system, *frontend.error);
case AppletId::ProfileSelect:

View File

@ -17,6 +17,7 @@ class System;
}
namespace Core::Frontend {
class ControllerApplet;
class ECommerceApplet;
class ErrorApplet;
class ParentalControlsApplet;
@ -155,19 +156,20 @@ protected:
};
struct AppletFrontendSet {
using ParentalControlsApplet = std::unique_ptr<Core::Frontend::ParentalControlsApplet>;
using ControllerApplet = std::unique_ptr<Core::Frontend::ControllerApplet>;
using ECommerceApplet = std::unique_ptr<Core::Frontend::ECommerceApplet>;
using ErrorApplet = std::unique_ptr<Core::Frontend::ErrorApplet>;
using ParentalControlsApplet = std::unique_ptr<Core::Frontend::ParentalControlsApplet>;
using PhotoViewer = std::unique_ptr<Core::Frontend::PhotoViewerApplet>;
using ProfileSelect = std::unique_ptr<Core::Frontend::ProfileSelectApplet>;
using SoftwareKeyboard = std::unique_ptr<Core::Frontend::SoftwareKeyboardApplet>;
using WebBrowser = std::unique_ptr<Core::Frontend::WebBrowserApplet>;
using ECommerceApplet = std::unique_ptr<Core::Frontend::ECommerceApplet>;
AppletFrontendSet();
AppletFrontendSet(ParentalControlsApplet parental_controls, ErrorApplet error,
PhotoViewer photo_viewer, ProfileSelect profile_select,
SoftwareKeyboard software_keyboard, WebBrowser web_browser,
ECommerceApplet e_commerce);
AppletFrontendSet(ControllerApplet controller, ECommerceApplet e_commerce, ErrorApplet error,
ParentalControlsApplet parental_controls, PhotoViewer photo_viewer,
ProfileSelect profile_select, SoftwareKeyboard software_keyboard,
WebBrowser web_browser);
~AppletFrontendSet();
AppletFrontendSet(const AppletFrontendSet&) = delete;
@ -176,13 +178,14 @@ struct AppletFrontendSet {
AppletFrontendSet(AppletFrontendSet&&) noexcept;
AppletFrontendSet& operator=(AppletFrontendSet&&) noexcept;
ParentalControlsApplet parental_controls;
ControllerApplet controller;
ECommerceApplet e_commerce;
ErrorApplet error;
ParentalControlsApplet parental_controls;
PhotoViewer photo_viewer;
ProfileSelect profile_select;
SoftwareKeyboard software_keyboard;
WebBrowser web_browser;
ECommerceApplet e_commerce;
};
class AppletManager {

View File

@ -0,0 +1,210 @@
// Copyright 2020 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <algorithm>
#include <cstring>
#include "common/assert.h"
#include "common/logging/log.h"
#include "common/string_util.h"
#include "core/core.h"
#include "core/frontend/applets/controller.h"
#include "core/hle/result.h"
#include "core/hle/service/am/am.h"
#include "core/hle/service/am/applets/controller.h"
#include "core/hle/service/hid/controllers/npad.h"
namespace Service::AM::Applets {
// This error code (0x183ACA) is thrown when the applet fails to initialize.
[[maybe_unused]] constexpr ResultCode ERR_CONTROLLER_APPLET_3101{ErrorModule::HID, 3101};
// This error code (0x183CCA) is thrown when the u32 result in ControllerSupportResultInfo is 2.
[[maybe_unused]] constexpr ResultCode ERR_CONTROLLER_APPLET_3102{ErrorModule::HID, 3102};
static Core::Frontend::ControllerParameters ConvertToFrontendParameters(
ControllerSupportArgPrivate private_arg, ControllerSupportArgHeader header, bool enable_text,
std::vector<IdentificationColor> identification_colors, std::vector<ExplainText> text) {
HID::Controller_NPad::NPadType npad_style_set;
npad_style_set.raw = private_arg.style_set;
return {
.min_players = std::max(s8(1), header.player_count_min),
.max_players = header.player_count_max,
.keep_controllers_connected = header.enable_take_over_connection,
.enable_single_mode = header.enable_single_mode,
.enable_border_color = header.enable_identification_color,
.border_colors = identification_colors,
.enable_explain_text = enable_text,
.explain_text = text,
.allow_pro_controller = npad_style_set.pro_controller == 1,
.allow_handheld = npad_style_set.handheld == 1,
.allow_dual_joycons = npad_style_set.joycon_dual == 1,
.allow_left_joycon = npad_style_set.joycon_left == 1,
.allow_right_joycon = npad_style_set.joycon_right == 1,
};
}
Controller::Controller(Core::System& system_, const Core::Frontend::ControllerApplet& frontend_)
: Applet{system_.Kernel()}, frontend(frontend_) {}
Controller::~Controller() = default;
void Controller::Initialize() {
Applet::Initialize();
LOG_INFO(Service_HID, "Initializing Controller Applet.");
LOG_DEBUG(Service_HID,
"Initializing Applet with common_args: arg_version={}, lib_version={}, "
"play_startup_sound={}, size={}, system_tick={}, theme_color={}",
common_args.arguments_version, common_args.library_version,
common_args.play_startup_sound, common_args.size, common_args.system_tick,
common_args.theme_color);
library_applet_version = LibraryAppletVersion{common_args.library_version};
const auto private_arg_storage = broker.PopNormalDataToApplet();
ASSERT(private_arg_storage != nullptr);
const auto& private_arg = private_arg_storage->GetData();
ASSERT(private_arg.size() == sizeof(ControllerSupportArgPrivate));
std::memcpy(&controller_private_arg, private_arg.data(), sizeof(ControllerSupportArgPrivate));
ASSERT_MSG(controller_private_arg.arg_private_size == sizeof(ControllerSupportArgPrivate),
"Unknown ControllerSupportArgPrivate revision={} with size={}",
library_applet_version, controller_private_arg.arg_private_size);
switch (controller_private_arg.mode) {
case ControllerSupportMode::ShowControllerSupport: {
const auto user_arg_storage = broker.PopNormalDataToApplet();
ASSERT(user_arg_storage != nullptr);
const auto& user_arg = user_arg_storage->GetData();
switch (library_applet_version) {
case LibraryAppletVersion::Version3:
case LibraryAppletVersion::Version4:
case LibraryAppletVersion::Version5:
ASSERT(user_arg.size() == sizeof(ControllerSupportArgOld));
std::memcpy(&controller_user_arg_old, user_arg.data(), sizeof(ControllerSupportArgOld));
break;
case LibraryAppletVersion::Version7:
ASSERT(user_arg.size() == sizeof(ControllerSupportArgNew));
std::memcpy(&controller_user_arg_new, user_arg.data(), sizeof(ControllerSupportArgNew));
break;
default:
UNIMPLEMENTED_MSG("Unknown ControllerSupportArg revision={} with size={}",
library_applet_version, controller_private_arg.arg_size);
ASSERT(user_arg.size() >= sizeof(ControllerSupportArgNew));
std::memcpy(&controller_user_arg_new, user_arg.data(), sizeof(ControllerSupportArgNew));
break;
}
break;
}
case ControllerSupportMode::ShowControllerStrapGuide:
case ControllerSupportMode::ShowControllerFirmwareUpdate:
default: {
UNIMPLEMENTED_MSG("Unimplemented ControllerSupportMode={}", controller_private_arg.mode);
break;
}
}
}
bool Controller::TransactionComplete() const {
return complete;
}
ResultCode Controller::GetStatus() const {
return status;
}
void Controller::ExecuteInteractive() {
UNREACHABLE_MSG("Attempted to call interactive execution on non-interactive applet.");
}
void Controller::Execute() {
switch (controller_private_arg.mode) {
case ControllerSupportMode::ShowControllerSupport: {
const auto parameters = [this] {
switch (library_applet_version) {
case LibraryAppletVersion::Version3:
case LibraryAppletVersion::Version4:
case LibraryAppletVersion::Version5:
return ConvertToFrontendParameters(
controller_private_arg, controller_user_arg_old.header,
controller_user_arg_old.enable_explain_text,
std::vector<IdentificationColor>(
controller_user_arg_old.identification_colors.begin(),
controller_user_arg_old.identification_colors.end()),
std::vector<ExplainText>(controller_user_arg_old.explain_text.begin(),
controller_user_arg_old.explain_text.end()));
case LibraryAppletVersion::Version7:
default:
return ConvertToFrontendParameters(
controller_private_arg, controller_user_arg_new.header,
controller_user_arg_new.enable_explain_text,
std::vector<IdentificationColor>(
controller_user_arg_new.identification_colors.begin(),
controller_user_arg_new.identification_colors.end()),
std::vector<ExplainText>(controller_user_arg_new.explain_text.begin(),
controller_user_arg_new.explain_text.end()));
}
}();
is_single_mode = parameters.enable_single_mode;
LOG_DEBUG(Service_HID,
"Controller Parameters: min_players={}, max_players={}, "
"keep_controllers_connected={}, enable_single_mode={}, enable_border_color={}, "
"enable_explain_text={}, allow_pro_controller={}, allow_handheld={}, "
"allow_dual_joycons={}, allow_left_joycon={}, allow_right_joycon={}",
parameters.min_players, parameters.max_players,
parameters.keep_controllers_connected, parameters.enable_single_mode,
parameters.enable_border_color, parameters.enable_explain_text,
parameters.allow_pro_controller, parameters.allow_handheld,
parameters.allow_dual_joycons, parameters.allow_left_joycon,
parameters.allow_right_joycon);
frontend.ReconfigureControllers([this] { ConfigurationComplete(); }, parameters);
break;
}
case ControllerSupportMode::ShowControllerStrapGuide:
case ControllerSupportMode::ShowControllerFirmwareUpdate:
default: {
ConfigurationComplete();
break;
}
}
}
void Controller::ConfigurationComplete() {
ControllerSupportResultInfo result_info{};
const auto& players = Settings::values.players;
// If enable_single_mode is enabled, player_count is 1 regardless of any other parameters.
// Otherwise, only count connected players from P1-P8.
result_info.player_count =
is_single_mode ? 1
: static_cast<s8>(std::count_if(
players.begin(), players.end() - 2,
[](Settings::PlayerInput player) { return player.connected; }));
result_info.selected_id = HID::Controller_NPad::IndexToNPad(
std::distance(players.begin(),
std::find_if(players.begin(), players.end(),
[](Settings::PlayerInput player) { return player.connected; })));
result_info.result = 0;
LOG_DEBUG(Service_HID, "Result Info: player_count={}, selected_id={}, result={}",
result_info.player_count, result_info.selected_id, result_info.result);
complete = true;
out_data = std::vector<u8>(sizeof(ControllerSupportResultInfo));
std::memcpy(out_data.data(), &result_info, out_data.size());
broker.PushNormalDataFromApplet(std::make_shared<IStorage>(std::move(out_data)));
broker.SignalStateChanged();
}
} // namespace Service::AM::Applets

View File

@ -0,0 +1,123 @@
// Copyright 2020 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <array>
#include <vector>
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "core/hle/result.h"
#include "core/hle/service/am/applets/applets.h"
namespace Core {
class System;
}
namespace Service::AM::Applets {
using IdentificationColor = std::array<u8, 4>;
using ExplainText = std::array<char, 0x81>;
enum class LibraryAppletVersion : u32_le {
Version3 = 0x3, // 1.0.0 - 2.3.0
Version4 = 0x4, // 3.0.0 - 5.1.0
Version5 = 0x5, // 6.0.0 - 7.0.1
Version7 = 0x7, // 8.0.0+
};
enum class ControllerSupportMode : u8 {
ShowControllerSupport = 0,
ShowControllerStrapGuide = 1,
ShowControllerFirmwareUpdate = 2,
};
enum class ControllerSupportCaller : u8 {
Application = 0,
System = 1,
};
struct ControllerSupportArgPrivate {
u32 arg_private_size{};
u32 arg_size{};
bool flag_0{};
bool flag_1{};
ControllerSupportMode mode{};
ControllerSupportCaller caller{};
u32 style_set{};
u32 joy_hold_type{};
};
static_assert(sizeof(ControllerSupportArgPrivate) == 0x14,
"ControllerSupportArgPrivate has incorrect size.");
struct ControllerSupportArgHeader {
s8 player_count_min{};
s8 player_count_max{};
bool enable_take_over_connection{};
bool enable_left_justify{};
bool enable_permit_joy_dual{};
bool enable_single_mode{};
bool enable_identification_color{};
};
static_assert(sizeof(ControllerSupportArgHeader) == 0x7,
"ControllerSupportArgHeader has incorrect size.");
// LibraryAppletVersion 0x3, 0x4, 0x5
struct ControllerSupportArgOld {
ControllerSupportArgHeader header{};
std::array<IdentificationColor, 4> identification_colors{};
bool enable_explain_text{};
std::array<ExplainText, 4> explain_text{};
};
static_assert(sizeof(ControllerSupportArgOld) == 0x21C,
"ControllerSupportArgOld has incorrect size.");
// LibraryAppletVersion 0x7
struct ControllerSupportArgNew {
ControllerSupportArgHeader header{};
std::array<IdentificationColor, 8> identification_colors{};
bool enable_explain_text{};
std::array<ExplainText, 8> explain_text{};
};
static_assert(sizeof(ControllerSupportArgNew) == 0x430,
"ControllerSupportArgNew has incorrect size.");
struct ControllerSupportResultInfo {
s8 player_count{};
INSERT_PADDING_BYTES(3);
u32 selected_id{};
u32 result{};
};
static_assert(sizeof(ControllerSupportResultInfo) == 0xC,
"ControllerSupportResultInfo has incorrect size.");
class Controller final : public Applet {
public:
explicit Controller(Core::System& system_, const Core::Frontend::ControllerApplet& frontend_);
~Controller() override;
void Initialize() override;
bool TransactionComplete() const override;
ResultCode GetStatus() const override;
void ExecuteInteractive() override;
void Execute() override;
void ConfigurationComplete();
private:
const Core::Frontend::ControllerApplet& frontend;
LibraryAppletVersion library_applet_version;
ControllerSupportArgPrivate controller_private_arg;
ControllerSupportArgOld controller_user_arg_old;
ControllerSupportArgNew controller_user_arg_new;
bool complete{false};
ResultCode status{RESULT_SUCCESS};
bool is_single_mode{false};
std::vector<u8> out_data;
};
} // namespace Service::AM::Applets

View File

@ -193,7 +193,8 @@ void Controller_NPad::InitNewlyAddedController(std::size_t controller_idx) {
controller.battery_level[0] = BATTERY_FULL;
controller.battery_level[1] = BATTERY_FULL;
controller.battery_level[2] = BATTERY_FULL;
styleset_changed_events[controller_idx].writable->Signal();
SignalStyleSetChangedEvent(IndexToNPad(controller_idx));
}
void Controller_NPad::OnInit() {
@ -518,13 +519,17 @@ void Controller_NPad::VibrateController(const std::vector<u32>& controller_ids,
last_processed_vibration = vibrations.back();
}
Controller_NPad::Vibration Controller_NPad::GetLastVibration() const {
return last_processed_vibration;
}
std::shared_ptr<Kernel::ReadableEvent> Controller_NPad::GetStyleSetChangedEvent(u32 npad_id) const {
const auto& styleset_event = styleset_changed_events[NPadIdToIndex(npad_id)];
return styleset_event.readable;
}
Controller_NPad::Vibration Controller_NPad::GetLastVibration() const {
return last_processed_vibration;
void Controller_NPad::SignalStyleSetChangedEvent(u32 npad_id) const {
styleset_changed_events[NPadIdToIndex(npad_id)].writable->Signal();
}
void Controller_NPad::AddNewControllerAt(NPadControllerType controller, std::size_t npad_index) {
@ -534,7 +539,7 @@ void Controller_NPad::AddNewControllerAt(NPadControllerType controller, std::siz
void Controller_NPad::UpdateControllerAt(NPadControllerType controller, std::size_t npad_index,
bool connected) {
if (!connected) {
DisconnectNPad(IndexToNPad(npad_index));
DisconnectNPadAtIndex(npad_index);
return;
}
@ -554,16 +559,19 @@ void Controller_NPad::UpdateControllerAt(NPadControllerType controller, std::siz
}
void Controller_NPad::DisconnectNPad(u32 npad_id) {
const auto npad_index = NPadIdToIndex(npad_id);
connected_controllers[npad_index].is_connected = false;
DisconnectNPadAtIndex(NPadIdToIndex(npad_id));
}
void Controller_NPad::DisconnectNPadAtIndex(std::size_t npad_index) {
Settings::values.players[npad_index].connected = false;
connected_controllers[npad_index].is_connected = false;
auto& controller = shared_memory_entries[npad_index];
controller.joy_styles.raw = 0; // Zero out
controller.device_type.raw = 0;
controller.properties.raw = 0;
styleset_changed_events[npad_index].writable->Signal();
SignalStyleSetChangedEvent(IndexToNPad(npad_index));
}
void Controller_NPad::SetGyroscopeZeroDriftMode(GyroscopeZeroDriftMode drift_mode) {
@ -666,13 +674,13 @@ void Controller_NPad::ClearAllConnectedControllers() {
}
void Controller_NPad::DisconnectAllConnectedControllers() {
for (ControllerHolder& controller : connected_controllers) {
for (auto& controller : connected_controllers) {
controller.is_connected = false;
}
}
void Controller_NPad::ConnectAllDisconnectedControllers() {
for (ControllerHolder& controller : connected_controllers) {
for (auto& controller : connected_controllers) {
if (controller.type != NPadControllerType::None && !controller.is_connected) {
controller.is_connected = true;
}
@ -680,7 +688,7 @@ void Controller_NPad::ConnectAllDisconnectedControllers() {
}
void Controller_NPad::ClearAllControllers() {
for (ControllerHolder& controller : connected_controllers) {
for (auto& controller : connected_controllers) {
controller.type = NPadControllerType::None;
controller.is_connected = false;
}
@ -728,92 +736,4 @@ bool Controller_NPad::IsControllerSupported(NPadControllerType controller) const
return false;
}
Controller_NPad::NPadControllerType Controller_NPad::DecideBestController(
NPadControllerType priority) const {
if (IsControllerSupported(priority)) {
return priority;
}
const auto is_docked = Settings::values.use_docked_mode;
if (is_docked && priority == NPadControllerType::Handheld) {
priority = NPadControllerType::JoyDual;
if (IsControllerSupported(priority)) {
return priority;
}
}
std::vector<NPadControllerType> priority_list;
switch (priority) {
case NPadControllerType::ProController:
priority_list.push_back(NPadControllerType::JoyDual);
if (!is_docked) {
priority_list.push_back(NPadControllerType::Handheld);
}
priority_list.push_back(NPadControllerType::JoyLeft);
priority_list.push_back(NPadControllerType::JoyRight);
priority_list.push_back(NPadControllerType::Pokeball);
break;
case NPadControllerType::Handheld:
priority_list.push_back(NPadControllerType::JoyDual);
priority_list.push_back(NPadControllerType::ProController);
priority_list.push_back(NPadControllerType::JoyLeft);
priority_list.push_back(NPadControllerType::JoyRight);
priority_list.push_back(NPadControllerType::Pokeball);
break;
case NPadControllerType::JoyDual:
if (!is_docked) {
priority_list.push_back(NPadControllerType::Handheld);
}
priority_list.push_back(NPadControllerType::ProController);
priority_list.push_back(NPadControllerType::JoyLeft);
priority_list.push_back(NPadControllerType::JoyRight);
priority_list.push_back(NPadControllerType::Pokeball);
break;
case NPadControllerType::JoyLeft:
priority_list.push_back(NPadControllerType::JoyRight);
priority_list.push_back(NPadControllerType::JoyDual);
if (!is_docked) {
priority_list.push_back(NPadControllerType::Handheld);
}
priority_list.push_back(NPadControllerType::ProController);
priority_list.push_back(NPadControllerType::Pokeball);
break;
case NPadControllerType::JoyRight:
priority_list.push_back(NPadControllerType::JoyLeft);
priority_list.push_back(NPadControllerType::JoyDual);
if (!is_docked) {
priority_list.push_back(NPadControllerType::Handheld);
}
priority_list.push_back(NPadControllerType::ProController);
priority_list.push_back(NPadControllerType::Pokeball);
break;
case NPadControllerType::Pokeball:
priority_list.push_back(NPadControllerType::JoyLeft);
priority_list.push_back(NPadControllerType::JoyRight);
priority_list.push_back(NPadControllerType::JoyDual);
if (!is_docked) {
priority_list.push_back(NPadControllerType::Handheld);
}
priority_list.push_back(NPadControllerType::ProController);
break;
default:
priority_list.push_back(NPadControllerType::JoyDual);
if (!is_docked) {
priority_list.push_back(NPadControllerType::Handheld);
}
priority_list.push_back(NPadControllerType::ProController);
priority_list.push_back(NPadControllerType::JoyLeft);
priority_list.push_back(NPadControllerType::JoyRight);
priority_list.push_back(NPadControllerType::JoyDual);
break;
}
const auto iter = std::find_if(priority_list.begin(), priority_list.end(),
[this](auto type) { return IsControllerSupported(type); });
if (iter == priority_list.end()) {
UNIMPLEMENTED_MSG("Could not find supported controller!");
return priority;
}
return *iter;
}
} // namespace Service::HID

View File

@ -115,15 +115,19 @@ public:
void VibrateController(const std::vector<u32>& controller_ids,
const std::vector<Vibration>& vibrations);
std::shared_ptr<Kernel::ReadableEvent> GetStyleSetChangedEvent(u32 npad_id) const;
Vibration GetLastVibration() const;
std::shared_ptr<Kernel::ReadableEvent> GetStyleSetChangedEvent(u32 npad_id) const;
void SignalStyleSetChangedEvent(u32 npad_id) const;
// Adds a new controller at an index.
void AddNewControllerAt(NPadControllerType controller, std::size_t npad_index);
// 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 SetGyroscopeZeroDriftMode(GyroscopeZeroDriftMode drift_mode);
GyroscopeZeroDriftMode GetGyroscopeZeroDriftMode() const;
LedPattern GetLedPattern(u32 npad_id);
@ -315,7 +319,6 @@ private:
void InitNewlyAddedController(std::size_t controller_idx);
bool IsControllerSupported(NPadControllerType controller) const;
NPadControllerType DecideBestController(NPadControllerType priority) const;
void RequestPadStateUpdate(u32 npad_id);
u32 press_state{};

View File

@ -9,6 +9,9 @@ add_executable(yuzu
about_dialog.cpp
about_dialog.h
aboutdialog.ui
applets/controller.cpp
applets/controller.h
applets/controller.ui
applets/error.cpp
applets/error.h
applets/profile_select.cpp
@ -62,12 +65,15 @@ add_executable(yuzu
configuration/configure_input.cpp
configuration/configure_input.h
configuration/configure_input.ui
configuration/configure_input_player.cpp
configuration/configure_input_player.h
configuration/configure_input_player.ui
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_motion_touch.cpp
configuration/configure_motion_touch.h
configuration/configure_motion_touch.ui

View File

@ -0,0 +1,601 @@
// Copyright 2020 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <algorithm>
#include "common/assert.h"
#include "common/string_util.h"
#include "core/core.h"
#include "core/hle/lock.h"
#include "core/hle/service/hid/controllers/npad.h"
#include "core/hle/service/hid/hid.h"
#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/main.h"
namespace {
constexpr std::array<std::array<bool, 4>, 8> led_patterns = {{
{1, 0, 0, 0},
{1, 1, 0, 0},
{1, 1, 1, 0},
{1, 1, 1, 1},
{1, 0, 0, 1},
{1, 0, 1, 0},
{1, 0, 1, 1},
{0, 1, 1, 0},
}};
void UpdateController(Settings::ControllerType controller_type, std::size_t npad_index,
bool connected) {
Core::System& system{Core::System::GetInstance()};
if (!system.IsPoweredOn()) {
return;
}
Service::SM::ServiceManager& sm = system.ServiceManager();
auto& npad =
sm.GetService<Service::HID::Hid>("hid")
->GetAppletResource()
->GetController<Service::HID::Controller_NPad>(Service::HID::HidController::NPad);
npad.UpdateControllerAt(npad.MapSettingsTypeToNPad(controller_type), npad_index, connected);
}
// Returns true if the given controller type is compatible with the given parameters.
bool IsControllerCompatible(Settings::ControllerType controller_type,
Core::Frontend::ControllerParameters parameters) {
switch (controller_type) {
case Settings::ControllerType::ProController:
return parameters.allow_pro_controller;
case Settings::ControllerType::DualJoyconDetached:
return parameters.allow_dual_joycons;
case Settings::ControllerType::LeftJoycon:
return parameters.allow_left_joycon;
case Settings::ControllerType::RightJoycon:
return parameters.allow_right_joycon;
case Settings::ControllerType::Handheld:
return parameters.enable_single_mode && parameters.allow_handheld;
default:
return false;
}
}
/// Maps the controller type combobox index to Controller Type enum
constexpr Settings::ControllerType GetControllerTypeFromIndex(int index) {
switch (index) {
case 0:
default:
return Settings::ControllerType::ProController;
case 1:
return Settings::ControllerType::DualJoyconDetached;
case 2:
return Settings::ControllerType::LeftJoycon;
case 3:
return Settings::ControllerType::RightJoycon;
case 4:
return Settings::ControllerType::Handheld;
}
}
/// Maps the Controller Type enum to controller type combobox index
constexpr int GetIndexFromControllerType(Settings::ControllerType type) {
switch (type) {
case Settings::ControllerType::ProController:
default:
return 0;
case Settings::ControllerType::DualJoyconDetached:
return 1;
case Settings::ControllerType::LeftJoycon:
return 2;
case Settings::ControllerType::RightJoycon:
return 3;
case Settings::ControllerType::Handheld:
return 4;
}
}
} // namespace
QtControllerSelectorDialog::QtControllerSelectorDialog(
QWidget* parent, Core::Frontend::ControllerParameters parameters_,
InputCommon::InputSubsystem* input_subsystem_)
: QDialog(parent), ui(std::make_unique<Ui::QtControllerSelectorDialog>()),
parameters(std::move(parameters_)), input_subsystem(input_subsystem_) {
ui->setupUi(this);
player_widgets = {
ui->widgetPlayer1, ui->widgetPlayer2, ui->widgetPlayer3, ui->widgetPlayer4,
ui->widgetPlayer5, ui->widgetPlayer6, ui->widgetPlayer7, ui->widgetPlayer8,
};
player_groupboxes = {
ui->groupPlayer1Connected, ui->groupPlayer2Connected, ui->groupPlayer3Connected,
ui->groupPlayer4Connected, ui->groupPlayer5Connected, ui->groupPlayer6Connected,
ui->groupPlayer7Connected, ui->groupPlayer8Connected,
};
connected_controller_icons = {
ui->controllerPlayer1, ui->controllerPlayer2, ui->controllerPlayer3, ui->controllerPlayer4,
ui->controllerPlayer5, ui->controllerPlayer6, ui->controllerPlayer7, ui->controllerPlayer8,
};
led_patterns_boxes = {{
{ui->checkboxPlayer1LED1, ui->checkboxPlayer1LED2, ui->checkboxPlayer1LED3,
ui->checkboxPlayer1LED4},
{ui->checkboxPlayer2LED1, ui->checkboxPlayer2LED2, ui->checkboxPlayer2LED3,
ui->checkboxPlayer2LED4},
{ui->checkboxPlayer3LED1, ui->checkboxPlayer3LED2, ui->checkboxPlayer3LED3,
ui->checkboxPlayer3LED4},
{ui->checkboxPlayer4LED1, ui->checkboxPlayer4LED2, ui->checkboxPlayer4LED3,
ui->checkboxPlayer4LED4},
{ui->checkboxPlayer5LED1, ui->checkboxPlayer5LED2, ui->checkboxPlayer5LED3,
ui->checkboxPlayer5LED4},
{ui->checkboxPlayer6LED1, ui->checkboxPlayer6LED2, ui->checkboxPlayer6LED3,
ui->checkboxPlayer6LED4},
{ui->checkboxPlayer7LED1, ui->checkboxPlayer7LED2, ui->checkboxPlayer7LED3,
ui->checkboxPlayer7LED4},
{ui->checkboxPlayer8LED1, ui->checkboxPlayer8LED2, ui->checkboxPlayer8LED3,
ui->checkboxPlayer8LED4},
}};
explain_text_labels = {
ui->labelPlayer1Explain, ui->labelPlayer2Explain, ui->labelPlayer3Explain,
ui->labelPlayer4Explain, ui->labelPlayer5Explain, ui->labelPlayer6Explain,
ui->labelPlayer7Explain, ui->labelPlayer8Explain,
};
emulated_controllers = {
ui->comboPlayer1Emulated, ui->comboPlayer2Emulated, ui->comboPlayer3Emulated,
ui->comboPlayer4Emulated, ui->comboPlayer5Emulated, ui->comboPlayer6Emulated,
ui->comboPlayer7Emulated, ui->comboPlayer8Emulated,
};
player_labels = {
ui->labelPlayer1, ui->labelPlayer2, ui->labelPlayer3, ui->labelPlayer4,
ui->labelPlayer5, ui->labelPlayer6, ui->labelPlayer7, ui->labelPlayer8,
};
connected_controller_labels = {
ui->labelConnectedPlayer1, ui->labelConnectedPlayer2, ui->labelConnectedPlayer3,
ui->labelConnectedPlayer4, ui->labelConnectedPlayer5, ui->labelConnectedPlayer6,
ui->labelConnectedPlayer7, ui->labelConnectedPlayer8,
};
connected_controller_checkboxes = {
ui->checkboxPlayer1Connected, ui->checkboxPlayer2Connected, ui->checkboxPlayer3Connected,
ui->checkboxPlayer4Connected, ui->checkboxPlayer5Connected, ui->checkboxPlayer6Connected,
ui->checkboxPlayer7Connected, ui->checkboxPlayer8Connected,
};
// Setup/load everything prior to setting up connections.
// This avoids unintentionally changing the states of elements while loading them in.
SetSupportedControllers();
DisableUnsupportedPlayers();
LoadConfiguration();
for (std::size_t i = 0; i < NUM_PLAYERS; ++i) {
SetExplainText(i);
UpdateControllerIcon(i);
UpdateLEDPattern(i);
UpdateBorderColor(i);
connect(player_groupboxes[i], &QGroupBox::toggled, [this, i](bool checked) {
if (checked) {
for (std::size_t index = 0; index <= i; ++index) {
connected_controller_checkboxes[index]->setChecked(checked);
}
} else {
for (std::size_t index = i; index < NUM_PLAYERS; ++index) {
connected_controller_checkboxes[index]->setChecked(checked);
}
}
});
connect(emulated_controllers[i], qOverload<int>(&QComboBox::currentIndexChanged),
[this, i](int) {
UpdateControllerIcon(i);
UpdateControllerState(i);
UpdateLEDPattern(i);
CheckIfParametersMet();
});
connect(connected_controller_checkboxes[i], &QCheckBox::stateChanged, [this, i](int state) {
player_groupboxes[i]->setChecked(state == Qt::Checked);
UpdateControllerIcon(i);
UpdateControllerState(i);
UpdateLEDPattern(i);
UpdateBorderColor(i);
CheckIfParametersMet();
});
if (i == 0) {
connect(emulated_controllers[i], qOverload<int>(&QComboBox::currentIndexChanged),
[this](int index) {
UpdateDockedState(GetControllerTypeFromIndex(index) ==
Settings::ControllerType::Handheld);
});
}
}
connect(ui->inputConfigButton, &QPushButton::clicked, this,
&QtControllerSelectorDialog::CallConfigureInputDialog);
connect(ui->buttonBox, &QDialogButtonBox::accepted, this,
&QtControllerSelectorDialog::ApplyConfiguration);
// If keep_controllers_connected is false, forcefully disconnect all controllers
if (!parameters.keep_controllers_connected) {
for (auto player : player_groupboxes) {
player->setChecked(false);
}
}
CheckIfParametersMet();
resize(0, 0);
}
QtControllerSelectorDialog::~QtControllerSelectorDialog() = default;
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;
Settings::values.use_docked_mode = ui->radioDocked->isChecked();
OnDockedModeChanged(pre_docked_mode, Settings::values.use_docked_mode);
Settings::values.vibration_enabled = 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);
player_groupboxes[index]->setChecked(connected);
connected_controller_checkboxes[index]->setChecked(connected);
emulated_controllers[index]->setCurrentIndex(
GetIndexFromControllerType(Settings::values.players[index].controller_type));
}
UpdateDockedState(Settings::values.players[8].connected);
ui->vibrationGroup->setChecked(Settings::values.vibration_enabled);
}
void QtControllerSelectorDialog::CallConfigureInputDialog() {
const auto max_supported_players = parameters.enable_single_mode ? 1 : parameters.max_players;
ConfigureInputDialog dialog(this, max_supported_players, input_subsystem);
dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint |
Qt::WindowSystemMenuHint);
dialog.setWindowModality(Qt::WindowModal);
dialog.exec();
dialog.ApplyConfiguration();
LoadConfiguration();
CheckIfParametersMet();
}
void QtControllerSelectorDialog::CheckIfParametersMet() {
// Here, we check and validate the current configuration against all applicable parameters.
const auto num_connected_players = static_cast<int>(
std::count_if(player_groupboxes.begin(), player_groupboxes.end(),
[this](const QGroupBox* player) { return player->isChecked(); }));
const auto min_supported_players = parameters.enable_single_mode ? 1 : parameters.min_players;
const auto max_supported_players = parameters.enable_single_mode ? 1 : parameters.max_players;
// First, check against the number of connected players.
if (num_connected_players < min_supported_players ||
num_connected_players > max_supported_players) {
parameters_met = false;
ui->buttonBox->setEnabled(parameters_met);
return;
}
// Next, check against all connected controllers.
const auto all_controllers_compatible = [this] {
for (std::size_t index = 0; index < NUM_PLAYERS; ++index) {
// Skip controllers that are not used, we only care about the currently connected ones.
if (!player_groupboxes[index]->isChecked() || !player_groupboxes[index]->isEnabled()) {
continue;
}
const auto compatible = IsControllerCompatible(
GetControllerTypeFromIndex(emulated_controllers[index]->currentIndex()),
parameters);
// If any controller is found to be incompatible, return false early.
if (!compatible) {
return false;
}
}
// Reaching here means all currently connected controllers are compatible.
return true;
}();
if (!all_controllers_compatible) {
parameters_met = false;
ui->buttonBox->setEnabled(parameters_met);
return;
}
parameters_met = true;
ui->buttonBox->setEnabled(parameters_met);
}
void QtControllerSelectorDialog::SetSupportedControllers() {
const QString theme = [this] {
if (QIcon::themeName().contains(QStringLiteral("dark"))) {
return QStringLiteral("_dark");
} else if (QIcon::themeName().contains(QStringLiteral("midnight"))) {
return QStringLiteral("_midnight");
} else {
return QString{};
}
}();
if (parameters.enable_single_mode && parameters.allow_handheld) {
ui->controllerSupported1->setStyleSheet(
QStringLiteral("image: url(:/controller/applet_handheld%0); ").arg(theme));
} else {
ui->controllerSupported1->setStyleSheet(
QStringLiteral("image: url(:/controller/applet_handheld%0_disabled); ").arg(theme));
}
if (parameters.allow_dual_joycons) {
ui->controllerSupported2->setStyleSheet(
QStringLiteral("image: url(:/controller/applet_dual_joycon%0); ").arg(theme));
} else {
ui->controllerSupported2->setStyleSheet(
QStringLiteral("image: url(:/controller/applet_dual_joycon%0_disabled); ").arg(theme));
}
if (parameters.allow_left_joycon) {
ui->controllerSupported3->setStyleSheet(
QStringLiteral("image: url(:/controller/applet_joycon_left%0); ").arg(theme));
} else {
ui->controllerSupported3->setStyleSheet(
QStringLiteral("image: url(:/controller/applet_joycon_left%0_disabled); ").arg(theme));
}
if (parameters.allow_right_joycon) {
ui->controllerSupported4->setStyleSheet(
QStringLiteral("image: url(:/controller/applet_joycon_right%0); ").arg(theme));
} else {
ui->controllerSupported4->setStyleSheet(
QStringLiteral("image: url(:/controller/applet_joycon_right%0_disabled); ").arg(theme));
}
if (parameters.allow_pro_controller) {
ui->controllerSupported5->setStyleSheet(
QStringLiteral("image: url(:/controller/applet_pro_controller%0); ").arg(theme));
} else {
ui->controllerSupported5->setStyleSheet(
QStringLiteral("image: url(:/controller/applet_pro_controller%0_disabled); ")
.arg(theme));
}
// enable_single_mode overrides min_players and max_players.
if (parameters.enable_single_mode) {
ui->numberSupportedLabel->setText(QStringLiteral("1"));
return;
}
if (parameters.min_players == parameters.max_players) {
ui->numberSupportedLabel->setText(QStringLiteral("%1").arg(parameters.max_players));
} else {
ui->numberSupportedLabel->setText(
QStringLiteral("%1 - %2").arg(parameters.min_players).arg(parameters.max_players));
}
}
void QtControllerSelectorDialog::UpdateControllerIcon(std::size_t player_index) {
if (!player_groupboxes[player_index]->isChecked()) {
connected_controller_icons[player_index]->setStyleSheet(QString{});
player_labels[player_index]->show();
return;
}
const QString stylesheet = [this, player_index] {
switch (GetControllerTypeFromIndex(emulated_controllers[player_index]->currentIndex())) {
case Settings::ControllerType::ProController:
return QStringLiteral("image: url(:/controller/applet_pro_controller%0); ");
case Settings::ControllerType::DualJoyconDetached:
return QStringLiteral("image: url(:/controller/applet_dual_joycon%0); ");
case Settings::ControllerType::LeftJoycon:
return QStringLiteral("image: url(:/controller/applet_joycon_left%0); ");
case Settings::ControllerType::RightJoycon:
return QStringLiteral("image: url(:/controller/applet_joycon_right%0); ");
case Settings::ControllerType::Handheld:
return QStringLiteral("image: url(:/controller/applet_handheld%0); ");
default:
return QString{};
}
}();
const QString theme = [this] {
if (QIcon::themeName().contains(QStringLiteral("dark"))) {
return QStringLiteral("_dark");
} else if (QIcon::themeName().contains(QStringLiteral("midnight"))) {
return QStringLiteral("_midnight");
} else {
return QString{};
}
}();
connected_controller_icons[player_index]->setStyleSheet(stylesheet.arg(theme));
player_labels[player_index]->hide();
}
void QtControllerSelectorDialog::UpdateControllerState(std::size_t player_index) {
auto& player = Settings::values.players[player_index];
player.controller_type =
GetControllerTypeFromIndex(emulated_controllers[player_index]->currentIndex());
player.connected = player_groupboxes[player_index]->isChecked();
// Player 2-8
if (player_index != 0) {
UpdateController(player.controller_type, player_index, player.connected);
return;
}
// Player 1 and Handheld
auto& handheld = Settings::values.players[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
}
UpdateController(player.controller_type, player_index, player.connected);
UpdateController(Settings::ControllerType::Handheld, 8, handheld.connected);
}
void QtControllerSelectorDialog::UpdateLEDPattern(std::size_t player_index) {
if (!player_groupboxes[player_index]->isChecked() ||
GetControllerTypeFromIndex(emulated_controllers[player_index]->currentIndex()) ==
Settings::ControllerType::Handheld) {
led_patterns_boxes[player_index][0]->setChecked(false);
led_patterns_boxes[player_index][1]->setChecked(false);
led_patterns_boxes[player_index][2]->setChecked(false);
led_patterns_boxes[player_index][3]->setChecked(false);
return;
}
led_patterns_boxes[player_index][0]->setChecked(led_patterns[player_index][0]);
led_patterns_boxes[player_index][1]->setChecked(led_patterns[player_index][1]);
led_patterns_boxes[player_index][2]->setChecked(led_patterns[player_index][2]);
led_patterns_boxes[player_index][3]->setChecked(led_patterns[player_index][3]);
}
void QtControllerSelectorDialog::UpdateBorderColor(std::size_t player_index) {
if (!parameters.enable_border_color ||
player_index >= static_cast<std::size_t>(parameters.max_players) ||
player_groupboxes[player_index]->styleSheet().contains(QStringLiteral("QGroupBox"))) {
return;
}
player_groupboxes[player_index]->setStyleSheet(
player_groupboxes[player_index]->styleSheet().append(
QStringLiteral("QGroupBox#groupPlayer%1Connected:checked "
"{ border: 1px solid rgba(%2, %3, %4, %5); }")
.arg(player_index + 1)
.arg(parameters.border_colors[player_index][0])
.arg(parameters.border_colors[player_index][1])
.arg(parameters.border_colors[player_index][2])
.arg(parameters.border_colors[player_index][3])));
}
void QtControllerSelectorDialog::SetExplainText(std::size_t player_index) {
if (!parameters.enable_explain_text ||
player_index >= static_cast<std::size_t>(parameters.max_players)) {
return;
}
explain_text_labels[player_index]->setText(QString::fromStdString(
Common::StringFromFixedZeroTerminatedBuffer(parameters.explain_text[player_index].data(),
parameters.explain_text[player_index].size())));
}
void QtControllerSelectorDialog::UpdateDockedState(bool is_handheld) {
// Disallow changing the console mode if the controller type 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);
// Also force into undocked mode if the controller type is handheld.
if (is_handheld) {
ui->radioUndocked->setChecked(true);
}
}
void QtControllerSelectorDialog::DisableUnsupportedPlayers() {
const auto max_supported_players = parameters.enable_single_mode ? 1 : parameters.max_players;
switch (max_supported_players) {
case 0:
default:
UNREACHABLE();
return;
case 1:
ui->widgetSpacer->hide();
ui->widgetSpacer2->hide();
ui->widgetSpacer3->hide();
ui->widgetSpacer4->hide();
break;
case 2:
ui->widgetSpacer->hide();
ui->widgetSpacer2->hide();
ui->widgetSpacer3->hide();
break;
case 3:
ui->widgetSpacer->hide();
ui->widgetSpacer2->hide();
break;
case 4:
ui->widgetSpacer->hide();
break;
case 5:
case 6:
case 7:
case 8:
break;
}
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);
// Hide the player widgets when max_supported_controllers is less than or equal to 4.
if (max_supported_players <= 4) {
player_widgets[index]->hide();
}
// Disable and hide the following to prevent these from interaction.
player_widgets[index]->setDisabled(true);
connected_controller_checkboxes[index]->setDisabled(true);
connected_controller_labels[index]->hide();
connected_controller_checkboxes[index]->hide();
}
}
QtControllerSelector::QtControllerSelector(GMainWindow& parent) {
connect(this, &QtControllerSelector::MainWindowReconfigureControllers, &parent,
&GMainWindow::ControllerSelectorReconfigureControllers, Qt::QueuedConnection);
connect(&parent, &GMainWindow::ControllerSelectorReconfigureFinished, this,
&QtControllerSelector::MainWindowReconfigureFinished, Qt::QueuedConnection);
}
QtControllerSelector::~QtControllerSelector() = default;
void QtControllerSelector::ReconfigureControllers(
std::function<void()> callback, Core::Frontend::ControllerParameters parameters) const {
this->callback = std::move(callback);
emit MainWindowReconfigureControllers(parameters);
}
void QtControllerSelector::MainWindowReconfigureFinished() {
// Acquire the HLE mutex
std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock);
callback();
}

View File

@ -0,0 +1,133 @@
// Copyright 2020 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <array>
#include <memory>
#include <QDialog>
#include "core/frontend/applets/controller.h"
class GMainWindow;
class QCheckBox;
class QComboBox;
class QDialogButtonBox;
class QGroupBox;
class QLabel;
namespace InputCommon {
class InputSubsystem;
}
namespace Ui {
class QtControllerSelectorDialog;
}
class QtControllerSelectorDialog final : public QDialog {
Q_OBJECT
public:
explicit QtControllerSelectorDialog(QWidget* parent,
Core::Frontend::ControllerParameters parameters_,
InputCommon::InputSubsystem* input_subsystem_);
~QtControllerSelectorDialog() override;
private:
// Applies the current configuration.
void ApplyConfiguration();
// Loads the current input configuration into the frontend applet.
void LoadConfiguration();
// Initializes the "Configure Input" Dialog.
void CallConfigureInputDialog();
// Checks the current configuration against the given parameters and
// sets the value of parameters_met.
void CheckIfParametersMet();
// Sets the controller icons for "Supported Controller Types".
void SetSupportedControllers();
// Updates the controller icons per player.
void UpdateControllerIcon(std::size_t player_index);
// Updates the controller state (type and connection status) per player.
void UpdateControllerState(std::size_t player_index);
// Updates the LED pattern per player.
void UpdateLEDPattern(std::size_t player_index);
// Updates the border color per player.
void UpdateBorderColor(std::size_t player_index);
// Sets the "Explain Text" per player.
void SetExplainText(std::size_t player_index);
// Updates the console mode.
void UpdateDockedState(bool is_handheld);
// Disables and disconnects unsupported players based on the given parameters.
void DisableUnsupportedPlayers();
std::unique_ptr<Ui::QtControllerSelectorDialog> ui;
// Parameters sent in from the backend HLE applet.
Core::Frontend::ControllerParameters parameters;
InputCommon::InputSubsystem* input_subsystem;
// 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};
static constexpr std::size_t NUM_PLAYERS = 8;
// Widgets encapsulating the groupboxes and comboboxes per player.
std::array<QWidget*, NUM_PLAYERS> player_widgets;
// Groupboxes encapsulating the controller icons and LED patterns per player.
std::array<QGroupBox*, NUM_PLAYERS> player_groupboxes;
// Icons for currently connected controllers/players.
std::array<QWidget*, NUM_PLAYERS> connected_controller_icons;
// Labels that represent the player numbers in place of the controller icons.
std::array<QLabel*, NUM_PLAYERS> player_labels;
// LED patterns for currently connected controllers/players.
std::array<std::array<QCheckBox*, 4>, NUM_PLAYERS> led_patterns_boxes;
// Labels representing additional information known as "Explain Text" per player.
std::array<QLabel*, NUM_PLAYERS> explain_text_labels;
// Comboboxes with a list of emulated controllers per player.
std::array<QComboBox*, NUM_PLAYERS> emulated_controllers;
// Labels representing the number of connected controllers
// above the "Connected Controllers" checkboxes.
std::array<QLabel*, NUM_PLAYERS> connected_controller_labels;
// Checkboxes representing the "Connected Controllers".
std::array<QCheckBox*, NUM_PLAYERS> connected_controller_checkboxes;
};
class QtControllerSelector final : public QObject, public Core::Frontend::ControllerApplet {
Q_OBJECT
public:
explicit QtControllerSelector(GMainWindow& parent);
~QtControllerSelector() override;
void ReconfigureControllers(std::function<void()> callback,
Core::Frontend::ControllerParameters parameters) const override;
signals:
void MainWindowReconfigureControllers(Core::Frontend::ControllerParameters parameters) const;
private:
void MainWindowReconfigureFinished();
mutable std::function<void()> callback;
};

File diff suppressed because it is too large Load Diff

View File

@ -70,7 +70,8 @@ ConfigureInput::ConfigureInput(QWidget* parent)
ConfigureInput::~ConfigureInput() = default;
void ConfigureInput::Initialize(InputCommon::InputSubsystem* input_subsystem) {
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),
@ -93,6 +94,11 @@ void ConfigureInput::Initialize(InputCommon::InputSubsystem* input_subsystem) {
ui->checkboxPlayer7Connected, ui->checkboxPlayer8Connected,
};
std::array<QLabel*, 8> player_connected_labels = {
ui->label, ui->label_3, ui->label_4, ui->label_5,
ui->label_6, ui->label_7, ui->label_8, ui->label_9,
};
for (std::size_t i = 0; i < player_tabs.size(); ++i) {
player_tabs[i]->setLayout(new QHBoxLayout(player_tabs[i]));
player_tabs[i]->layout()->addWidget(player_controllers[i]);
@ -112,6 +118,13 @@ void ConfigureInput::Initialize(InputCommon::InputSubsystem* input_subsystem) {
connect(player_connected[i], &QCheckBox::stateChanged, [this, i](int state) {
player_controllers[i]->ConnectPlayer(state == Qt::Checked);
});
// Remove/hide all the elements that exceed max_players, if applicable.
if (i >= max_players) {
ui->tabWidget->removeTab(static_cast<int>(max_players));
player_connected[i]->hide();
player_connected_labels[i]->hide();
}
}
// Only the first player can choose handheld mode so connect the signal just to player 1
connect(player_controllers[0], &ConfigureInputPlayer::HandheldStateChanged,
@ -175,8 +188,7 @@ void ConfigureInput::RetranslateUI() {
void ConfigureInput::LoadConfiguration() {
LoadPlayerControllerIndices();
UpdateDockedState(Settings::values.players[0].controller_type ==
Settings::ControllerType::Handheld);
UpdateDockedState(Settings::values.players[8].connected);
ui->vibrationGroup->setChecked(Settings::values.vibration_enabled);
}
@ -208,14 +220,14 @@ void ConfigureInput::RestoreDefaults() {
}
void ConfigureInput::UpdateDockedState(bool is_handheld) {
// If the controller type is handheld only, disallow changing docked mode
// Disallow changing the console mode if the controller type 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);
// If its handheld only, force docked mode off (since you can't play handheld in a dock)
// Also force into undocked mode if the controller type is handheld.
if (is_handheld) {
ui->radioUndocked->setChecked(true);
}

View File

@ -37,7 +37,7 @@ public:
~ConfigureInput() override;
/// Initializes the input dialog with the given input subsystem.
void Initialize(InputCommon::InputSubsystem* input_subsystem_);
void Initialize(InputCommon::InputSubsystem* input_subsystem_, std::size_t max_players = 8);
/// Save all button configurations to settings file.
void ApplyConfiguration();

View File

@ -0,0 +1,37 @@
// 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<Ui::ConfigureInputDialog>()),
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);
}

View File

@ -0,0 +1,38 @@
// Copyright 2020 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <memory>
#include <QDialog>
#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::ConfigureInputDialog> ui;
ConfigureInput* input_widget;
};

View File

@ -0,0 +1,57 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ConfigureInputDialog</class>
<widget class="QDialog" name="ConfigureInputDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>70</width>
<height>540</height>
</rect>
</property>
<property name="windowTitle">
<string>Configure Input</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>2</number>
</property>
<property name="leftMargin">
<number>9</number>
</property>
<property name="topMargin">
<number>9</number>
</property>
<property name="rightMargin">
<number>9</number>
</property>
<property name="bottomMargin">
<number>9</number>
</property>
<item>
<layout class="QHBoxLayout" name="inputLayout"/>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="standardButtons">
<set>QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>ConfigureInputDialog</receiver>
<slot>accept()</slot>
</connection>
</connections>
</ui>

View File

@ -11,6 +11,7 @@
#endif
// VFS includes must be before glad as they will conflict with Windows file api, which uses defines.
#include "applets/controller.h"
#include "applets/error.h"
#include "applets/profile_select.h"
#include "applets/software_keyboard.h"
@ -19,7 +20,9 @@
#include "configuration/configure_per_game.h"
#include "core/file_sys/vfs.h"
#include "core/file_sys/vfs_real.h"
#include "core/frontend/applets/controller.h"
#include "core/frontend/applets/general_frontend.h"
#include "core/frontend/applets/software_keyboard.h"
#include "core/hle/service/acc/profile_manager.h"
#include "core/hle/service/am/applet_ae.h"
#include "core/hle/service/am/applet_oe.h"
@ -84,7 +87,6 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
#include "core/file_sys/romfs.h"
#include "core/file_sys/savedata_factory.h"
#include "core/file_sys/submission_package.h"
#include "core/frontend/applets/software_keyboard.h"
#include "core/hle/kernel/process.h"
#include "core/hle/service/am/am.h"
#include "core/hle/service/filesystem/filesystem.h"
@ -283,6 +285,23 @@ GMainWindow::~GMainWindow() {
delete render_window;
}
void GMainWindow::ControllerSelectorReconfigureControllers(
const Core::Frontend::ControllerParameters& parameters) {
QtControllerSelectorDialog dialog(this, parameters, input_subsystem.get());
dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint |
Qt::WindowSystemMenuHint);
dialog.setWindowModality(Qt::WindowModal);
dialog.exec();
emit ControllerSelectorReconfigureFinished();
// Don't forget to apply settings.
Settings::Apply();
config->Save();
UpdateStatusButtons();
}
void GMainWindow::ProfileSelectorSelectProfile() {
const Service::Account::ProfileManager manager;
int index = 0;
@ -291,10 +310,12 @@ void GMainWindow::ProfileSelectorSelectProfile() {
dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint |
Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint);
dialog.setWindowModality(Qt::WindowModal);
if (dialog.exec() == QDialog::Rejected) {
emit ProfileSelectorFinishedSelection(std::nullopt);
return;
}
index = dialog.GetIndex();
}
@ -966,13 +987,14 @@ bool GMainWindow::LoadROM(const QString& filename) {
system.SetFilesystem(vfs);
system.SetAppletFrontendSet({
nullptr, // Parental Controls
std::make_unique<QtErrorDisplay>(*this), //
nullptr, // Photo Viewer
std::make_unique<QtProfileSelector>(*this), //
std::make_unique<QtSoftwareKeyboard>(*this), //
std::make_unique<QtWebBrowser>(*this), //
std::make_unique<QtControllerSelector>(*this), // Controller Selector
nullptr, // E-Commerce
std::make_unique<QtErrorDisplay>(*this), // Error Display
nullptr, // Parental Controls
nullptr, // Photo Viewer
std::make_unique<QtProfileSelector>(*this), // Profile Selector
std::make_unique<QtSoftwareKeyboard>(*this), // Software Keyboard
std::make_unique<QtWebBrowser>(*this), // Web Browser
});
system.RegisterHostThread();
@ -2047,6 +2069,7 @@ void GMainWindow::OnStartGame() {
emu_thread->SetRunning(true);
qRegisterMetaType<Core::Frontend::ControllerParameters>("Core::Frontend::ControllerParameters");
qRegisterMetaType<Core::Frontend::SoftwareKeyboardParameters>(
"Core::Frontend::SoftwareKeyboardParameters");
qRegisterMetaType<Core::System::ResultStatus>("Core::System::ResultStatus");

View File

@ -37,6 +37,7 @@ enum class InstalledEntryType;
class GameListPlaceholder;
namespace Core::Frontend {
struct ControllerParameters;
struct SoftwareKeyboardParameters;
} // namespace Core::Frontend
@ -116,9 +117,12 @@ signals:
void UpdateInstallProgress();
void ControllerSelectorReconfigureFinished();
void ErrorDisplayFinished();
void ProfileSelectorFinishedSelection(std::optional<Common::UUID> uuid);
void SoftwareKeyboardFinishedText(std::optional<std::u16string> text);
void SoftwareKeyboardFinishedCheckDialog();
@ -127,6 +131,8 @@ signals:
public slots:
void OnLoadComplete();
void ControllerSelectorReconfigureControllers(
const Core::Frontend::ControllerParameters& parameters);
void ErrorDisplayDisplayError(QString body);
void ProfileSelectorSelectProfile();
void SoftwareKeyboardGetText(const Core::Frontend::SoftwareKeyboardParameters& parameters);