Compare commits

...

26 Commits

Author SHA1 Message Date
Lioncash
9e1b0af259 input_common: Eliminate most global state
Abstracts most of the input mechanisms under an InputSubsystem class
that is managed by the frontends, eliminating any static constructors
and destructors. This gets rid of global accessor functions and also
allows the frontends to have a more fine-grained control over the
lifecycle of the input subsystem.

This also makes it explicit which interfaces rely on the input subsystem
instead of making it opaque in the interface functions. All that remains
to migrate over is the factories, which can be done in a separate
change.
2020-08-27 16:11:17 -04:00
bunnei
3db9a25977 Merge pull request #4530 from Morph1984/mjolnir-p1
Project Mjölnir: Part 1 - Input Rewrite
2020-08-27 14:58:44 -04:00
bunnei
3f7b0e0772 Merge pull request #4577 from lioncash/asserts
common/assert: Make use of C++ attribute syntax
2020-08-27 11:09:02 -04:00
bunnei
9864da7d43 Merge pull request #4524 from lioncash/memory-log
shader/memory: Amend UNIMPLEMENTED_IF_MSG without a message
2020-08-27 00:16:10 -04:00
bunnei
1bb8c27a70 Merge pull request #4569 from ReinUsesLisp/glsl-cmake
video_core/host_shaders: Add CMake integration for string shaders
2020-08-26 22:57:39 -04:00
Morph
8ffc491546 input_common/main: Add "/Mouse" to the display name 2020-08-26 22:41:51 -04:00
bunnei
1e2a92918b Merge pull request #4555 from ReinUsesLisp/fix-primitive-topology
vk_state_tracker: Fix primitive topology
2020-08-26 22:19:52 -04:00
Rodrigo Locatti
ff34b47dfb Merge pull request #4593 from lioncash/const2
memory_manager: Make operator+ const qualified
2020-08-27 01:15:48 +00:00
Lioncash
7b50c48df7 memory_manager: Make use of [[nodiscard]] in the interface 2020-08-26 20:15:03 -04:00
Lioncash
d12d59f62a memory_manager: Make operator+ const qualified
This doesn't modify member state, so it can be marked as const.
2020-08-26 20:11:58 -04:00
Morph
f5f30781ae configure_input_player: Fix modifier scale button mapping 2020-08-26 02:32:32 -04:00
Morph
1bd70d73c0 configuration/input: Add support for mouse button clicks
Supports the Left, Right, Middle, Backward and Forward mouse buttons.
2020-08-26 02:32:32 -04:00
Morph
eb149ec696 controllers/npad: Fix inconsistencies with controller connection statuses 2020-08-26 02:32:32 -04:00
Morph
334ef2efdd controllers/npad: Fix LibNX controller connection statuses
This allows homebrew applications to be able to properly detect connected controllers.
2020-08-26 02:32:32 -04:00
Morph
e7c174b426 controllers/npad: Fix LedPattern for P1-4 2020-08-26 02:32:32 -04:00
Morph
de79897f04 input_common: Fix directional deadzone values
The hardware tested value is 0.5 which translates to SHRT_MAX / 2
2020-08-26 02:32:32 -04:00
Morph
efa0b7a056 Address feedback 2020-08-26 02:32:32 -04:00
Morph
fc505110f1 qt_themes: Fix Midnight Blue theme
Co-authored-by: Its-Rei <kupfel@gmail.com>
2020-08-26 02:32:32 -04:00
Morph
f0fac0c7fb Project Mjölnir: Part 1
Co-authored-by: James Rowe <jroweboy@gmail.com>
Co-authored-by: Its-Rei <kupfel@gmail.com>
2020-08-26 02:32:32 -04:00
bunnei
b8885aa03b Merge pull request #4582 from lioncash/xbyak
externals: Update Xbyak to 5.95
2020-08-25 22:02:20 -04:00
Lioncash
c024e5c69a externals: Update Xbyak to 5.95
5.95 contains a potentially backward-compatibility breaking change, so
we should be updating to this to ensure that our code remains
forward-compatible.
2020-08-25 18:16:52 -04:00
Lioncash
3bfaabdbdd common/assert: Make use of C++ attribute syntax
Normalizes the syntax used for attributes
2020-08-24 04:15:10 -04:00
ReinUsesLisp
91df2beee3 video_core/host_shaders: Add CMake integration for string shaders
Add the necessary CMake code to copy the contents in a string source
shader (GLSL or GLASM) to a header file then consumed by video_core
files.

This allows editting GLSL in its own files without having to maintain
them in source files.

For now, only OpenGL presentation shaders are moved, but we can add
GLASM presentation shaders and static SPIR-V generation through
glslangValidator in the future.
2020-08-23 21:37:20 -03:00
ReinUsesLisp
0eaf7e1daa gl_shader_util: Use std::string_view instead of star pointer
This allows us passing any type of string and hinting the length of the
string to the OpenGL driver.
2020-08-23 21:23:54 -03:00
ReinUsesLisp
aed6011d7c vk_state_tracker: Fix primitive topology
State track the current primitive topology with a regular comparison
instead of using dirty flags.

This fixes a bug in dirty flags for this particular state and it also
avoids unnecessary state changes as this property is stored in a
frequently changed bit field.
2020-08-20 23:07:30 -03:00
Lioncash
dcc5562cd5 shader/memory: Amend UNIMPLEMENTED_IF_MSG without a message
We need to provide a message for this variant of the macro, so we can
simply log out the type being used.
2020-08-14 08:38:37 -04:00
118 changed files with 9316 additions and 3585 deletions

25
dist/icons/controller/controller.qrc vendored Normal file
View File

@@ -0,0 +1,25 @@
<RCC>
<qresource prefix="controller">
<file alias="dual_joycon">dual_joycon.png</file>
<file alias="dual_joycon_dark">dual_joycon_dark.png</file>
<file alias="dual_joycon_midnight">dual_joycon_midnight.png</file>
<file alias="handheld">handheld.png</file>
<file alias="handheld_dark">handheld_dark.png</file>
<file alias="handheld_midnight">handheld_midnight.png</file>
<file alias="pro_controller">pro_controller.png</file>
<file alias="pro_controller_dark">pro_controller_dark.png</file>
<file alias="pro_controller_midnight">pro_controller_midnight.png</file>
<file alias="single_joycon_left">single_joycon_left.png</file>
<file alias="single_joycon_left_dark">single_joycon_left_dark.png</file>
<file alias="single_joycon_left_midnight">single_joycon_left_midnight.png</file>
<file alias="single_joycon_right">single_joycon_right.png</file>
<file alias="single_joycon_right_dark">single_joycon_right_dark.png</file>
<file alias="single_joycon_right_midnight">single_joycon_right_midnight.png</file>
<file alias="single_joycon_left_vertical">single_joycon_left_vertical.png</file>
<file alias="single_joycon_left_vertical_dark">single_joycon_left_vertical_dark.png</file>
<file alias="single_joycon_left_vertical_midnight">single_joycon_left_vertical_midnight.png</file>
<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>
</qresource>
</RCC>

BIN
dist/icons/controller/dual_joycon.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

BIN
dist/icons/controller/handheld.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

BIN
dist/icons/controller/handheld_dark.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

BIN
dist/icons/controller/pro_controller.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

3
dist/license.md vendored
View File

@@ -5,6 +5,7 @@ Icon Name | License | Origin/Author
qt_themes/default/icons/16x16/checked.png | CC BY-ND 3.0 | https://icons8.com
qt_themes/default/icons/16x16/failed.png | CC BY-ND 3.0 | https://icons8.com
qt_themes/default/icons/16x16/lock.png | CC BY-ND 3.0 | https://icons8.com
qt_themes/default/icons/16x16/view-refresh.png | Apache 2.0 | https://material.io
qt_themes/default/icons/256x256/plus_folder.png | CC BY-ND 3.0 | https://icons8.com
qt_themes/default/icons/48x48/bad_folder.png | CC BY-ND 3.0 | https://icons8.com
qt_themes/default/icons/48x48/chip.png | CC BY-ND 3.0 | https://icons8.com
@@ -12,6 +13,7 @@ qt_themes/default/icons/48x48/folder.png | CC BY-ND 3.0 | https://icons8.com
qt_themes/default/icons/48x48/plus.png | CC0 1.0 | Designed by BreadFish64 from the Citra team
qt_themes/default/icons/48x48/sd_card.png | CC BY-ND 3.0 | https://icons8.com
qt_themes/qdarkstyle/icons/16x16/lock.png | CC BY-ND 3.0 | https://icons8.com
qt_themes/qdarkstyle/icons/16x16/view-refresh.png | Apache 2.0 | https://material.io
qt_themes/qdarkstyle/icons/256x256/plus_folder.png | CC BY-ND 3.0 | https://icons8.com
qt_themes/qdarkstyle/icons/48x48/bad_folder.png | CC BY-ND 3.0 | https://icons8.com
qt_themes/qdarkstyle/icons/48x48/chip.png | CC BY-ND 3.0 | https://icons8.com
@@ -19,6 +21,7 @@ qt_themes/qdarkstyle/icons/48x48/folder.png | CC BY-ND 3.0 | https://icons8.com
qt_themes/qdarkstyle/icons/48x48/plus.png | CC0 1.0 | Designed by BreadFish64 from the Citra team
qt_themes/qdarkstyle/icons/48x48/sd_card.png | CC BY-ND 3.0 | https://icons8.com
qt_themes/colorful/icons/16x16/lock.png | CC BY-ND 3.0 | https://icons8.com
qt_themes/colorful/icons/16x16/view-refresh.png | Apache 2.0 | https://material.io
qt_themes/colorful/icons/256x256/plus_folder.png | CC BY-ND 3.0 | https://icons8.com
qt_themes/colorful/icons/48x48/bad_folder.png | CC BY-ND 3.0 | https://icons8.com
qt_themes/colorful/icons/48x48/chip.png | CC BY-ND 3.0 | https://icons8.com

Binary file not shown.

After

Width:  |  Height:  |  Size: 362 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 362 B

View File

@@ -2,6 +2,7 @@
<qresource prefix="icons/colorful_dark">
<file alias="index.theme">icons/index.theme</file>
<file alias="16x16/lock.png">icons/16x16/lock.png</file>
<file alias="16x16/view-refresh.png">icons/16x16/view-refresh.png</file>
<file alias="48x48/bad_folder.png">../colorful/icons/48x48/bad_folder.png</file>
<file alias="48x48/chip.png">../colorful/icons/48x48/chip.png</file>
<file alias="48x48/folder.png">../colorful/icons/48x48/folder.png</file>

Binary file not shown.

After

Width:  |  Height:  |  Size: 362 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 362 B

View File

@@ -2,6 +2,7 @@
<qresource prefix="icons/colorful_midnight_blue">
<file alias="index.theme">icons/index.theme</file>
<file alias="16x16/lock.png">icons/16x16/lock.png</file>
<file alias="16x16/view-refresh.png">icons/16x16/view-refresh.png</file>
<file alias="48x48/bad_folder.png">../colorful/icons/48x48/bad_folder.png</file>
<file alias="48x48/chip.png">../colorful/icons/48x48/chip.png</file>
<file alias="48x48/folder.png">../colorful/icons/48x48/folder.png</file>

View File

@@ -4,6 +4,7 @@
<file alias="16x16/checked.png">icons/16x16/checked.png</file>
<file alias="16x16/failed.png">icons/16x16/failed.png</file>
<file alias="16x16/lock.png">icons/16x16/lock.png</file>
<file alias="16x16/view-refresh.png">icons/16x16/view-refresh.png</file>
<file alias="48x48/bad_folder.png">icons/48x48/bad_folder.png</file>
<file alias="48x48/chip.png">icons/48x48/chip.png</file>
<file alias="48x48/folder.png">icons/48x48/folder.png</file>

Binary file not shown.

After

Width:  |  Height:  |  Size: 349 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 349 B

View File

@@ -30,6 +30,66 @@ QPushButton#RendererStatusBarButton:checked {
color: #e85c00;
}
QPushButton#RendererStatusBarButton:!checked{
QPushButton#RendererStatusBarButton:!checked {
color: #0066ff;
}
QPushButton#buttonRefreshDevices {
min-width: 20px;
min-height: 20px;
max-width: 20px;
max-height: 20px;
}
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 black;
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 black;
background: transparent;
image: none;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 362 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 362 B

View File

@@ -2,6 +2,7 @@
<qresource prefix="icons/qdarkstyle">
<file alias="index.theme">icons/index.theme</file>
<file alias="16x16/lock.png">icons/16x16/lock.png</file>
<file alias="16x16/view-refresh.png">icons/16x16/view-refresh.png</file>
<file alias="48x48/bad_folder.png">icons/48x48/bad_folder.png</file>
<file alias="48x48/chip.png">icons/48x48/chip.png</file>
<file alias="48x48/folder.png">icons/48x48/folder.png</file>

View File

@@ -40,8 +40,8 @@ QCheckBox:disabled {
QCheckBox::indicator,
QGroupBox::indicator {
width: 18px;
height: 18px;
width: 16px;
height: 16px;
}
QGroupBox::indicator {
@@ -1237,6 +1237,7 @@ QPlainTextEdit:disabled {
background-color: #2b2e31;
}
QPushButton#TogglableStatusBarButton {
min-width: 0px;
color: #656565;
@@ -1271,6 +1272,102 @@ QPushButton#RendererStatusBarButton:checked {
color: #e85c00;
}
QPushButton#RendererStatusBarButton:!checked{
color: #00ccdd;
QPushButton#RendererStatusBarButton:!checked {
color: #00ccdd;
}
QPushButton#buttonRefreshDevices {
min-width: 24px;
min-height: 24px;
max-width: 24px;
max-height: 24px;
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;
min-width: 63px;
}
QSpinBox#vibrationSpin {
padding: 4px 0px 5px 0px;
min-width: 63px;
}
QSpinBox#spinboxLStickRange:up-button,
QSpinBox#spinboxRStickRange:up-button,
QSpinBox#vibrationSpin:up-button {
left: -2px;
}
QSpinBox#spinboxLStickRange:down-button,
QSpinBox#spinboxRStickRange:down-button,
QSpinBox#vibrationSpin:down-button {
right: -1px;
}
QGroupBox#motionGroup::indicator,
QGroupBox#vibrationGroup::indicator {
margin-left: 0px;
}
QGroupBox#motionGroup::title,
QGroupBox#vibrationGroup::title {
spacing: 2px;
padding-left: 1px;
padding-right: 1px;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 362 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 362 B

View File

@@ -2,6 +2,7 @@
<qresource prefix="icons/qdarkstyle_midnight_blue">
<file alias="index.theme">icons/index.theme</file>
<file alias="16x16/lock.png">icons/16x16/lock.png</file>
<file alias="16x16/view-refresh.png">icons/16x16/view-refresh.png</file>
<file alias="48x48/bad_folder.png">icons/48x48/bad_folder.png</file>
<file alias="48x48/chip.png">icons/48x48/chip.png</file>
<file alias="48x48/folder.png">icons/48x48/folder.png</file>

View File

@@ -138,8 +138,6 @@ https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qstatusbar
--------------------------------------------------------------------------- */
QStatusBar {
border: 1px solid #32414B;
/* Fixes Spyder #9120, #9121 */
background: #32414B;
/* Fixes #205, white vertical borders separating items */
}
@@ -161,6 +159,7 @@ QStatusBar QToolTip {
QStatusBar QLabel {
/* Fixes Spyder #9120, #9121 */
background: transparent;
padding: 0px;
}
/* QCheckBox --------------------------------------------------------------
@@ -236,21 +235,19 @@ https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qgroupbox
--------------------------------------------------------------------------- */
QGroupBox {
font-weight: bold;
border: 1px solid #32414B;
border-radius: 4px;
padding: 4px;
margin-top: 16px;
font-weight: bold;
border: 1px solid #32414B;
border-radius: 4px;
margin-top: 12px;
padding: 4px;
}
QGroupBox::title {
subcontrol-origin: margin;
subcontrol-position: top left;
left: 3px;
padding-left: 3px;
padding-right: 5px;
padding-top: 8px;
padding-bottom: 16px;
subcontrol-origin: margin;
subcontrol-position: top left;
padding-left: 3px;
padding-right: 5px;
padding-top: 4px;
}
QGroupBox::indicator {
@@ -367,28 +364,19 @@ https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qmenubar
--------------------------------------------------------------------------- */
QMenuBar {
background-color: #32414B;
padding: 2px;
border: 1px solid #19232D;
color: #F0F0F0;
}
QMenuBar:focus {
border: 1px solid #148CD2;
}
QMenuBar::item {
background: transparent;
padding: 4px;
}
QMenuBar::item:selected {
padding: 4px;
background: transparent;
border: 0px solid #32414B;
}
QMenuBar::item:pressed {
padding: 4px;
border: 0px solid #32414B;
background-color: #148CD2;
color: #F0F0F0;
@@ -396,6 +384,7 @@ QMenuBar::item:pressed {
padding-bottom: 0px;
}
/* QMenu ------------------------------------------------------------------
https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qmenu
@@ -482,7 +471,7 @@ https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qcombobox
--------------------------------------------------------------------------- */
QAbstractItemView {
alternate-background-color: #19232D;
alternate-background-color: #1f2933;
color: #F0F0F0;
border: 1px solid #32414B;
border-radius: 4px;
@@ -501,13 +490,13 @@ QAbstractScrollArea {
background-color: #19232D;
border: 1px solid #32414B;
border-radius: 4px;
padding: 2px;
/* fix #159 */
min-height: 1.25em;
/* fix #159 */
color: #F0F0F0;
}
QAbstractScrollArea:disabled {
color: #787878;
}
@@ -807,20 +796,22 @@ QAbstractSpinBox {
}
QAbstractSpinBox:up-button {
background-color: transparent #19232D;
background-color: #505F69;
subcontrol-origin: border;
subcontrol-position: top right;
border-left: 1px solid #32414B;
border-bottom: 1px solid #32414B;
border-top: 1px solid #32414B;
border-right: 1px solid #32414B;
border-top-right-radius: 4px;
border-top-left-radius: 0;
border-bottom-left-radius: 0;
margin: 1px;
margin: 0px;
width: 12px;
margin-bottom: -1px;
margin-bottom: 0px;
}
QAbstractSpinBox::up-arrow, QAbstractSpinBox::up-arrow:disabled, QAbstractSpinBox::up-arrow:off {
image: url(":/qss_icons/rc/arrow_up_disabled.png");
image: url(":/qss_icons/rc/up_arrow.png");
height: 8px;
width: 8px;
}
@@ -830,20 +821,23 @@ QAbstractSpinBox::up-arrow:hover {
}
QAbstractSpinBox:down-button {
background-color: transparent #19232D;
background-color: #505F69;
subcontrol-origin: border;
subcontrol-position: bottom right;
border-left: 1px solid #32414B;
border-right: 1px solid #32414B;
border-bottom: 1px solid #32414B;
border-top: 1px solid #32414B;
border-top-left-radius: 0;
border-bottom-left-radius: 0;
margin: 1px;
border-bottom-right-radius: 4px;
margin: 0px;
width: 12px;
margin-top: -1px;
margin-top: 0px;
}
QAbstractSpinBox::down-arrow, QAbstractSpinBox::down-arrow:disabled, QAbstractSpinBox::down-arrow:off {
image: url(":/qss_icons/rc/arrow_down_disabled.png");
image: url(":/qss_icons/rc/down_arrow.png");
height: 8px;
width: 8px;
}
@@ -1199,6 +1193,7 @@ https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qcombobox
--------------------------------------------------------------------------- */
QComboBox {
background-color: #0f1922;
border: 1px solid #32414B;
border-radius: 4px;
selection-background-color: #1464A0;
@@ -1216,7 +1211,7 @@ QComboBox {
QComboBox QAbstractItemView {
border: 1px solid #32414B;
border-radius: 0;
background-color: #19232D;
background-color: #0f1922;
selection-background-color: #1464A0;
}
@@ -1285,7 +1280,12 @@ QComboBox::drop-down {
}
QComboBox::down-arrow {
image: url(":/qss_icons/rc/arrow_down_disabled.png");
image: url(":/qss_icons/rc/down_arrow.png");
background-color: #505F69;
padding: 6px 2px;
border: 1px solid #32414B;
border-top-right-radius: 4px;
border-bottom-right-radius: 4px;
height: 8px;
width: 8px;
}
@@ -1559,12 +1559,12 @@ QTabBar::tab:right:!selected {
QTabBar::tab:top {
background-color: #32414B;
color: #F0F0F0;
min-width: 36px;
margin-left: 2px;
padding-left: 4px;
padding-right: 4px;
padding-left: 8px;
padding-right: 8px;
padding-top: 2px;
padding-bottom: 2px;
min-width: 5px;
border-bottom: 3px solid #32414B;
border-top-left-radius: 3px;
border-top-right-radius: 3px;
@@ -1588,16 +1588,16 @@ QTabBar::tab:top:!selected:hover {
QTabBar::tab:bottom {
color: #F0F0F0;
min-width: 36px;
border-top: 3px solid #32414B;
background-color: #32414B;
margin-left: 2px;
padding-left: 4px;
padding-right: 4px;
padding-left: 8px;
padding-right: 8px;
padding-top: 2px;
padding-bottom: 2px;
border-bottom-left-radius: 3px;
border-bottom-right-radius: 3px;
min-width: 5px;
}
QTabBar::tab:bottom:selected {
@@ -1752,21 +1752,6 @@ https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qlistview
https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qtableview
--------------------------------------------------------------------------- */
QTreeView:branch:selected, QTreeView:branch:hover {
background: url(":/qss_icons/rc/transparent.png");
}
QTreeView:branch:has-siblings:!adjoins-item {
border-image: url(":/qss_icons/rc/branch_line.png") 0;
}
QTreeView:branch:has-siblings:adjoins-item {
border-image: url(":/qss_icons/rc/branch_more.png") 0;
}
QTreeView:branch:!has-children:!has-siblings:adjoins-item {
border-image: url(":/qss_icons/rc/branch_end.png") 0;
}
QTreeView:branch:has-children:!has-siblings:closed, QTreeView:branch:closed:has-children:has-siblings {
border-image: none;
@@ -1900,21 +1885,21 @@ https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qheaderview
--------------------------------------------------------------------------- */
QHeaderView {
background-color: #32414B;
border: 0px transparent #32414B;
background-color: #19232D;
border: 0px transparent #19232D;
padding: 0px;
margin: 0px;
border-radius: 0px;
}
QHeaderView:disabled {
background-color: #32414B;
border: 1px transparent #32414B;
background-color: #19232D;
border: 1px transparent #19232D;
padding: 2px;
}
QHeaderView::section {
background-color: #32414B;
background-color: #19232D;
color: #F0F0F0;
padding: 2px;
border-radius: 0px;
@@ -1934,11 +1919,11 @@ QHeaderView::section:checked:disabled {
QHeaderView::section::horizontal {
padding-left: 4px;
padding-right: 4px;
border-left: 1px solid #19232D;
border-left: 1px solid #32414B;
}
QHeaderView::section::horizontal::first, QHeaderView::section::horizontal::only-one {
border-left: 1px solid #32414B;
border-left: 1px solid #19232D;
}
QHeaderView::section::horizontal:disabled {
@@ -1948,7 +1933,7 @@ QHeaderView::section::horizontal:disabled {
QHeaderView::section::vertical {
padding-left: 4px;
padding-right: 4px;
border-top: 1px solid #19232D;
border-top: 1px solid #32414B;
}
QHeaderView::section::vertical::first, QHeaderView::section::vertical::only-one {
@@ -1962,7 +1947,7 @@ QHeaderView::section::vertical:disabled {
QHeaderView::down-arrow {
/* Those settings (border/width/height/background-color) solve bug */
/* transparent arrow background and size */
background-color: #32414B;
background-color: #19232D;
border: none;
height: 12px;
width: 12px;
@@ -1972,7 +1957,7 @@ QHeaderView::down-arrow {
}
QHeaderView::up-arrow {
background-color: #32414B;
background-color: #19232D;
border: none;
height: 12px;
width: 12px;
@@ -2172,3 +2157,132 @@ PlotWidget {
/* Fix cut labels in plots #134 */
padding: 0px;
}
QPushButton#TogglableStatusBarButton {
min-width: 0px;
color: #656565;
border: 1px solid transparent;
background-color: transparent;
padding: 0px 3px 0px 3px;
text-align: center;
}
QPushButton#TogglableStatusBarButton:checked {
color: #ffffff;
}
QPushButton#TogglableStatusBarButton:hover {
border: 1px solid #76797C;
}
QPushButton#RendererStatusBarButton {
min-width: 0px;
color: #656565;
border: 1px solid transparent;
background-color: transparent;
padding: 0px 3px 0px 3px;
text-align: center;
}
QPushButton#RendererStatusBarButton:hover {
border: 1px solid #76797C;
}
QPushButton#RendererStatusBarButton:checked {
color: #e85c00;
}
QPushButton#RendererStatusBarButton:!checked {
color: #00ccdd;
}
QPushButton#buttonRefreshDevices {
min-width: 20px;
min-height: 20px;
max-width: 20px;
max-height: 20px;
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;
}
QGroupBox#motionGroup::indicator,
QGroupBox#vibrationGroup::indicator {
margin-left: 0px;
}
QGroupBox#motionGroup::title,
QGroupBox#vibrationGroup::title {
spacing: 2px;
padding-left: 1px;
padding-right: 1px;
}
QListWidget#selectorList {
background-color: #0f1922;
}
QSpinBox,
QLineEdit,
QTreeView#hotkey_list,
QScrollArea#scrollArea QTreeView {
background-color: #0f1922;
}

View File

@@ -17,11 +17,12 @@
// enough for our purposes.
template <typename Fn>
#if defined(_MSC_VER)
__declspec(noinline, noreturn)
[[msvc::noinline, noreturn]]
#elif defined(__GNUC__)
__attribute__((noinline, noreturn, cold))
[[gnu::cold, gnu::noinline, noreturn]]
#endif
static void assert_noinline_call(const Fn& fn) {
static void
assert_noinline_call(const Fn& fn) {
fn();
Crash();
exit(1); // Keeps GCC's mouth shut about this actually returning

View File

@@ -19,7 +19,7 @@ public:
explicit ParamPackage(const std::string& serialized);
ParamPackage(std::initializer_list<DataType::value_type> list);
ParamPackage(const ParamPackage& other) = default;
ParamPackage(ParamPackage&& other) = default;
ParamPackage(ParamPackage&& other) noexcept = default;
ParamPackage& operator=(const ParamPackage& other) = default;
ParamPackage& operator=(ParamPackage&& other) = default;

View File

@@ -24,6 +24,7 @@ constexpr s32 HID_JOYSTICK_MAX = 0x7fff;
constexpr std::size_t NPAD_OFFSET = 0x9A00;
constexpr u32 BATTERY_FULL = 2;
constexpr u32 MAX_NPAD_ID = 7;
constexpr std::size_t HANDHELD_INDEX = 8;
constexpr std::array<u32, 10> npad_id_list{
0, 1, 2, 3, 4, 5, 6, 7, NPAD_HANDHELD, NPAD_UNKNOWN,
};
@@ -33,19 +34,41 @@ enum class JoystickId : std::size_t {
Joystick_Right,
};
static Controller_NPad::NPadControllerType MapSettingsTypeToNPad(Settings::ControllerType type) {
Controller_NPad::NPadControllerType Controller_NPad::MapSettingsTypeToNPad(
Settings::ControllerType type) {
switch (type) {
case Settings::ControllerType::ProController:
return Controller_NPad::NPadControllerType::ProController;
case Settings::ControllerType::DualJoycon:
return Controller_NPad::NPadControllerType::JoyDual;
return NPadControllerType::ProController;
case Settings::ControllerType::DualJoyconDetached:
return NPadControllerType::JoyDual;
case Settings::ControllerType::LeftJoycon:
return Controller_NPad::NPadControllerType::JoyLeft;
return NPadControllerType::JoyLeft;
case Settings::ControllerType::RightJoycon:
return Controller_NPad::NPadControllerType::JoyRight;
return NPadControllerType::JoyRight;
case Settings::ControllerType::Handheld:
return NPadControllerType::Handheld;
default:
UNREACHABLE();
return Controller_NPad::NPadControllerType::JoyDual;
return NPadControllerType::ProController;
}
}
Settings::ControllerType Controller_NPad::MapNPadToSettingsType(
Controller_NPad::NPadControllerType type) {
switch (type) {
case NPadControllerType::ProController:
return Settings::ControllerType::ProController;
case NPadControllerType::JoyDual:
return Settings::ControllerType::DualJoyconDetached;
case NPadControllerType::JoyLeft:
return Settings::ControllerType::LeftJoycon;
case NPadControllerType::JoyRight:
return Settings::ControllerType::RightJoycon;
case NPadControllerType::Handheld:
return Settings::ControllerType::Handheld;
default:
UNREACHABLE();
return Settings::ControllerType::ProController;
}
}
@@ -60,9 +83,9 @@ std::size_t Controller_NPad::NPadIdToIndex(u32 npad_id) {
case 6:
case 7:
return npad_id;
case 8:
case HANDHELD_INDEX:
case NPAD_HANDHELD:
return 8;
return HANDHELD_INDEX;
case 9:
case NPAD_UNKNOWN:
return 9;
@@ -83,7 +106,7 @@ u32 Controller_NPad::IndexToNPad(std::size_t index) {
case 6:
case 7:
return static_cast<u32>(index);
case 8:
case HANDHELD_INDEX:
return NPAD_HANDHELD;
case 9:
return NPAD_UNKNOWN;
@@ -96,25 +119,35 @@ u32 Controller_NPad::IndexToNPad(std::size_t index) {
Controller_NPad::Controller_NPad(Core::System& system) : ControllerBase(system), system(system) {}
Controller_NPad::~Controller_NPad() = default;
void Controller_NPad::InitNewlyAddedControler(std::size_t controller_idx) {
void Controller_NPad::InitNewlyAddedController(std::size_t controller_idx) {
const auto controller_type = connected_controllers[controller_idx].type;
auto& controller = shared_memory_entries[controller_idx];
if (controller_type == NPadControllerType::None) {
styleset_changed_events[controller_idx].writable->Signal();
return;
}
controller.joy_styles.raw = 0; // Zero out
controller.device_type.raw = 0;
controller.properties.raw = 0;
switch (controller_type) {
case NPadControllerType::None:
UNREACHABLE();
break;
case NPadControllerType::Handheld:
controller.joy_styles.handheld.Assign(1);
controller.device_type.handheld.Assign(1);
controller.pad_assignment = NPadAssignments::Dual;
case NPadControllerType::ProController:
controller.joy_styles.pro_controller.Assign(1);
controller.device_type.pro_controller.Assign(1);
controller.properties.is_vertical.Assign(1);
controller.properties.use_plus.Assign(1);
controller.properties.use_minus.Assign(1);
controller.pad_assignment = NPadAssignments::Single;
break;
case NPadControllerType::Handheld:
controller.joy_styles.handheld.Assign(1);
controller.device_type.handheld.Assign(1);
controller.properties.is_vertical.Assign(1);
controller.properties.use_plus.Assign(1);
controller.properties.use_minus.Assign(1);
controller.pad_assignment = NPadAssignments::Dual;
break;
case NPadControllerType::JoyDual:
controller.joy_styles.joycon_dual.Assign(1);
@@ -144,14 +177,6 @@ void Controller_NPad::InitNewlyAddedControler(std::size_t controller_idx) {
controller.device_type.pokeball.Assign(1);
controller.pad_assignment = NPadAssignments::Single;
break;
case NPadControllerType::ProController:
controller.joy_styles.pro_controller.Assign(1);
controller.device_type.pro_controller.Assign(1);
controller.properties.is_vertical.Assign(1);
controller.properties.use_plus.Assign(1);
controller.properties.use_minus.Assign(1);
controller.pad_assignment = NPadAssignments::Single;
break;
}
controller.single_color_error = ColorReadError::ReadOk;
@@ -192,36 +217,25 @@ void Controller_NPad::OnInit() {
style.pokeball.Assign(1);
}
std::transform(
Settings::values.players.begin(), Settings::values.players.end(),
connected_controllers.begin(), [](const Settings::PlayerInput& player) {
return ControllerHolder{MapSettingsTypeToNPad(player.type), player.connected};
});
std::stable_partition(connected_controllers.begin(), connected_controllers.begin() + 8,
[](const ControllerHolder& holder) { return holder.is_connected; });
std::transform(Settings::values.players.begin(), Settings::values.players.end(),
connected_controllers.begin(), [](const Settings::PlayerInput& player) {
return ControllerHolder{MapSettingsTypeToNPad(player.controller_type),
player.connected};
});
// Account for handheld
if (connected_controllers[8].is_connected)
connected_controllers[8].type = NPadControllerType::Handheld;
if (connected_controllers[HANDHELD_INDEX].is_connected) {
connected_controllers[HANDHELD_INDEX].type = NPadControllerType::Handheld;
}
supported_npad_id_types.resize(npad_id_list.size());
std::memcpy(supported_npad_id_types.data(), npad_id_list.data(),
npad_id_list.size() * sizeof(u32));
// Add a default dual joycon controller if none are present.
if (std::none_of(connected_controllers.begin(), connected_controllers.end(),
[](const ControllerHolder& controller) { return controller.is_connected; })) {
supported_npad_id_types.resize(npad_id_list.size());
std::memcpy(supported_npad_id_types.data(), npad_id_list.data(),
npad_id_list.size() * sizeof(u32));
AddNewController(NPadControllerType::JoyDual);
}
for (std::size_t i = 0; i < connected_controllers.size(); ++i) {
const auto& controller = connected_controllers[i];
if (controller.is_connected) {
AddNewControllerAt(controller.type, IndexToNPad(i));
AddNewControllerAt(controller.type, i);
}
}
}
@@ -309,8 +323,9 @@ void Controller_NPad::RequestPadStateUpdate(u32 npad_id) {
void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data,
std::size_t data_len) {
if (!IsControllerActivated())
if (!IsControllerActivated()) {
return;
}
for (std::size_t i = 0; i < shared_memory_entries.size(); i++) {
auto& npad = shared_memory_entries[i];
const std::array<NPadGeneric*, 7> controller_npads{&npad.main_controller_states,
@@ -360,13 +375,25 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8*
auto& libnx_entry = npad.libnx.npad[npad.libnx.common.last_entry_index];
libnx_entry.connection_status.raw = 0;
libnx_entry.connection_status.IsConnected.Assign(1);
switch (controller_type) {
case NPadControllerType::None:
UNREACHABLE();
break;
case NPadControllerType::ProController:
main_controller.connection_status.raw = 0;
main_controller.connection_status.IsConnected.Assign(1);
main_controller.connection_status.IsWired.Assign(1);
main_controller.pad.pad_states.raw = pad_state.pad_states.raw;
main_controller.pad.l_stick = pad_state.l_stick;
main_controller.pad.r_stick = pad_state.r_stick;
libnx_entry.connection_status.IsWired.Assign(1);
break;
case NPadControllerType::Handheld:
handheld_entry.connection_status.raw = 0;
handheld_entry.connection_status.IsConnected.Assign(1);
handheld_entry.connection_status.IsWired.Assign(1);
handheld_entry.connection_status.IsLeftJoyConnected.Assign(1);
handheld_entry.connection_status.IsRightJoyConnected.Assign(1);
@@ -375,57 +402,52 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8*
handheld_entry.pad.pad_states.raw = pad_state.pad_states.raw;
handheld_entry.pad.l_stick = pad_state.l_stick;
handheld_entry.pad.r_stick = pad_state.r_stick;
libnx_entry.connection_status.IsWired.Assign(1);
libnx_entry.connection_status.IsLeftJoyConnected.Assign(1);
libnx_entry.connection_status.IsRightJoyConnected.Assign(1);
libnx_entry.connection_status.IsLeftJoyWired.Assign(1);
libnx_entry.connection_status.IsRightJoyWired.Assign(1);
break;
case NPadControllerType::JoyDual:
dual_entry.connection_status.raw = 0;
dual_entry.connection_status.IsConnected.Assign(1);
dual_entry.connection_status.IsLeftJoyConnected.Assign(1);
dual_entry.connection_status.IsRightJoyConnected.Assign(1);
dual_entry.connection_status.IsConnected.Assign(1);
libnx_entry.connection_status.IsLeftJoyConnected.Assign(1);
libnx_entry.connection_status.IsRightJoyConnected.Assign(1);
libnx_entry.connection_status.IsConnected.Assign(1);
dual_entry.pad.pad_states.raw = pad_state.pad_states.raw;
dual_entry.pad.l_stick = pad_state.l_stick;
dual_entry.pad.r_stick = pad_state.r_stick;
libnx_entry.connection_status.IsLeftJoyConnected.Assign(1);
libnx_entry.connection_status.IsRightJoyConnected.Assign(1);
break;
case NPadControllerType::JoyLeft:
left_entry.connection_status.raw = 0;
left_entry.connection_status.IsConnected.Assign(1);
left_entry.connection_status.IsLeftJoyConnected.Assign(1);
left_entry.pad.pad_states.raw = pad_state.pad_states.raw;
left_entry.pad.l_stick = pad_state.l_stick;
left_entry.pad.r_stick = pad_state.r_stick;
libnx_entry.connection_status.IsLeftJoyConnected.Assign(1);
break;
case NPadControllerType::JoyRight:
right_entry.connection_status.raw = 0;
right_entry.connection_status.IsConnected.Assign(1);
right_entry.connection_status.IsRightJoyConnected.Assign(1);
right_entry.pad.pad_states.raw = pad_state.pad_states.raw;
right_entry.pad.l_stick = pad_state.l_stick;
right_entry.pad.r_stick = pad_state.r_stick;
libnx_entry.connection_status.IsRightJoyConnected.Assign(1);
break;
case NPadControllerType::Pokeball:
pokeball_entry.connection_status.raw = 0;
pokeball_entry.connection_status.IsConnected.Assign(1);
pokeball_entry.connection_status.IsWired.Assign(1);
pokeball_entry.pad.pad_states.raw = pad_state.pad_states.raw;
pokeball_entry.pad.l_stick = pad_state.l_stick;
pokeball_entry.pad.r_stick = pad_state.r_stick;
break;
case NPadControllerType::ProController:
main_controller.connection_status.raw = 0;
main_controller.connection_status.IsConnected.Assign(1);
main_controller.connection_status.IsWired.Assign(1);
main_controller.pad.pad_states.raw = pad_state.pad_states.raw;
main_controller.pad.l_stick = pad_state.l_stick;
main_controller.pad.r_stick = pad_state.r_stick;
break;
}
// LibNX exclusively uses this section, so we always update it since LibNX doesn't activate
@@ -453,26 +475,6 @@ void Controller_NPad::SetSupportedNPadIdTypes(u8* data, std::size_t length) {
supported_npad_id_types.clear();
supported_npad_id_types.resize(length / sizeof(u32));
std::memcpy(supported_npad_id_types.data(), data, length);
for (std::size_t i = 0; i < connected_controllers.size(); i++) {
auto& controller = connected_controllers[i];
if (!controller.is_connected) {
continue;
}
const auto requested_controller =
i <= MAX_NPAD_ID ? MapSettingsTypeToNPad(Settings::values.players[i].type)
: NPadControllerType::Handheld;
if (!IsControllerSupported(requested_controller)) {
const auto is_handheld = requested_controller == NPadControllerType::Handheld;
if (is_handheld) {
controller.type = NPadControllerType::None;
controller.is_connected = false;
AddNewController(requested_controller);
} else {
controller.type = requested_controller;
InitNewlyAddedControler(i);
}
}
}
}
void Controller_NPad::GetSupportedNpadIdTypes(u32* data, std::size_t max_length) {
@@ -504,7 +506,7 @@ void Controller_NPad::VibrateController(const std::vector<u32>& controller_ids,
const std::vector<Vibration>& vibrations) {
LOG_DEBUG(Service_HID, "(STUBBED) called");
if (!can_controllers_vibrate) {
if (!Settings::values.vibration_enabled || !can_controllers_vibrate) {
return;
}
for (std::size_t i = 0; i < controller_ids.size(); i++) {
@@ -517,8 +519,6 @@ void Controller_NPad::VibrateController(const std::vector<u32>& controller_ids,
}
std::shared_ptr<Kernel::ReadableEvent> Controller_NPad::GetStyleSetChangedEvent(u32 npad_id) const {
// TODO(ogniK): Figure out the best time to signal this event. This event seems that it should
// be signalled at least once, and signaled after a new controller is connected?
const auto& styleset_event = styleset_changed_events[NPadIdToIndex(npad_id)];
return styleset_event.readable;
}
@@ -527,43 +527,43 @@ Controller_NPad::Vibration Controller_NPad::GetLastVibration() const {
return last_processed_vibration;
}
void Controller_NPad::AddNewController(NPadControllerType controller) {
controller = DecideBestController(controller);
if (controller == NPadControllerType::Handheld) {
connected_controllers[8] = {controller, true};
InitNewlyAddedControler(8);
return;
}
const auto pos =
std::find_if(connected_controllers.begin(), connected_controllers.end() - 2,
[](const ControllerHolder& holder) { return !holder.is_connected; });
if (pos == connected_controllers.end() - 2) {
LOG_ERROR(Service_HID, "Cannot connect any more controllers!");
return;
}
const auto controller_id = std::distance(connected_controllers.begin(), pos);
connected_controllers[controller_id] = {controller, true};
InitNewlyAddedControler(controller_id);
void Controller_NPad::AddNewControllerAt(NPadControllerType controller, std::size_t npad_index) {
UpdateControllerAt(controller, npad_index, true);
}
void Controller_NPad::AddNewControllerAt(NPadControllerType controller, u32 npad_id) {
controller = DecideBestController(controller);
if (controller == NPadControllerType::Handheld) {
connected_controllers[NPadIdToIndex(NPAD_HANDHELD)] = {controller, true};
InitNewlyAddedControler(NPadIdToIndex(NPAD_HANDHELD));
void Controller_NPad::UpdateControllerAt(NPadControllerType controller, std::size_t npad_index,
bool connected) {
if (!connected) {
DisconnectNPad(IndexToNPad(npad_index));
return;
}
connected_controllers[NPadIdToIndex(npad_id)] = {controller, true};
InitNewlyAddedControler(NPadIdToIndex(npad_id));
}
if (controller == NPadControllerType::Handheld) {
Settings::values.players[HANDHELD_INDEX].controller_type =
MapNPadToSettingsType(controller);
Settings::values.players[HANDHELD_INDEX].connected = true;
connected_controllers[HANDHELD_INDEX] = {controller, true};
InitNewlyAddedController(HANDHELD_INDEX);
return;
}
void Controller_NPad::ConnectNPad(u32 npad_id) {
connected_controllers[NPadIdToIndex(npad_id)].is_connected = true;
Settings::values.players[npad_index].controller_type = MapNPadToSettingsType(controller);
Settings::values.players[npad_index].connected = true;
connected_controllers[npad_index] = {controller, true};
InitNewlyAddedController(npad_index);
}
void Controller_NPad::DisconnectNPad(u32 npad_id) {
connected_controllers[NPadIdToIndex(npad_id)].is_connected = false;
const auto npad_index = NPadIdToIndex(npad_id);
connected_controllers[npad_index].is_connected = false;
Settings::values.players[npad_index].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();
}
void Controller_NPad::SetGyroscopeZeroDriftMode(GyroscopeZeroDriftMode drift_mode) {
@@ -599,8 +599,8 @@ bool Controller_NPad::SwapNpadAssignment(u32 npad_id_1, u32 npad_id_2) {
std::swap(connected_controllers[npad_index_1].type, connected_controllers[npad_index_2].type);
InitNewlyAddedControler(npad_index_1);
InitNewlyAddedControler(npad_index_2);
AddNewControllerAt(connected_controllers[npad_index_1].type, npad_index_1);
AddNewControllerAt(connected_controllers[npad_index_2].type, npad_index_2);
return true;
}
@@ -614,11 +614,11 @@ Controller_NPad::LedPattern Controller_NPad::GetLedPattern(u32 npad_id) {
case 0:
return LedPattern{1, 0, 0, 0};
case 1:
return LedPattern{0, 1, 0, 0};
return LedPattern{1, 1, 0, 0};
case 2:
return LedPattern{0, 0, 1, 0};
return LedPattern{1, 1, 1, 0};
case 3:
return LedPattern{0, 0, 0, 1};
return LedPattern{1, 1, 1, 1};
case 4:
return LedPattern{1, 0, 0, 1};
case 5:
@@ -628,7 +628,6 @@ Controller_NPad::LedPattern Controller_NPad::GetLedPattern(u32 npad_id) {
case 7:
return LedPattern{0, 1, 1, 0};
default:
UNIMPLEMENTED_MSG("Unhandled npad_id {}", npad_id);
return LedPattern{0, 0, 0, 0};
}
}

View File

@@ -118,10 +118,11 @@ public:
std::shared_ptr<Kernel::ReadableEvent> GetStyleSetChangedEvent(u32 npad_id) const;
Vibration GetLastVibration() const;
void AddNewController(NPadControllerType controller);
void AddNewControllerAt(NPadControllerType controller, u32 npad_id);
// 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 ConnectNPad(u32 npad_id);
void DisconnectNPad(u32 npad_id);
void SetGyroscopeZeroDriftMode(GyroscopeZeroDriftMode drift_mode);
GyroscopeZeroDriftMode GetGyroscopeZeroDriftMode() const;
@@ -141,6 +142,8 @@ public:
// Specifically for cheat engine and other features.
u32 GetAndResetPressState();
static Controller_NPad::NPadControllerType MapSettingsTypeToNPad(Settings::ControllerType type);
static Settings::ControllerType MapNPadToSettingsType(Controller_NPad::NPadControllerType type);
static std::size_t NPadIdToIndex(u32 npad_id);
static u32 IndexToNPad(std::size_t index);
@@ -309,7 +312,7 @@ private:
bool is_connected;
};
void InitNewlyAddedControler(std::size_t controller_idx);
void InitNewlyAddedController(std::size_t controller_idx);
bool IsControllerSupported(NPadControllerType controller) const;
NPadControllerType DecideBestController(NPadControllerType priority) const;
void RequestPadStateUpdate(u32 npad_id);

View File

@@ -38,11 +38,9 @@
namespace Service::HID {
// Updating period for each HID device.
// TODO(ogniK): Find actual polling rate of hid
constexpr auto pad_update_ns = std::chrono::nanoseconds{1000000000 / 66};
[[maybe_unused]] constexpr auto accelerometer_update_ns =
std::chrono::nanoseconds{1000000000 / 100};
[[maybe_unused]] constexpr auto gyroscope_update_ticks = std::chrono::nanoseconds{1000000000 / 100};
// HID is polled every 15ms, this value was derived from
// https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering#joy-con-status-data-packet
constexpr auto pad_update_ns = std::chrono::nanoseconds{15 * 1000 * 1000}; // (15ms, 66.6Hz)
constexpr std::size_t SHARED_MEMORY_SIZE = 0x40000;
IAppletResource::IAppletResource(Core::System& system)
@@ -845,8 +843,7 @@ void Hid::CreateActiveVibrationDeviceList(Kernel::HLERequestContext& ctx) {
void Hid::PermitVibration(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto can_vibrate{rp.Pop<bool>()};
applet_resource->GetController<Controller_NPad>(HidController::NPad)
.SetVibrationEnabled(can_vibrate);
Settings::values.vibration_enabled = can_vibrate;
LOG_DEBUG(Service_HID, "called, can_vibrate={}", can_vibrate);
@@ -859,8 +856,7 @@ void Hid::IsVibrationPermitted(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push(
applet_resource->GetController<Controller_NPad>(HidController::NPad).IsVibrationEnabled());
rb.Push(Settings::values.vibration_enabled);
}
void Hid::ActivateConsoleSixAxisSensor(Kernel::HLERequestContext& ctx) {

View File

@@ -13,56 +13,6 @@
namespace Settings {
namespace NativeButton {
const std::array<const char*, NumButtons> mapping = {{
"button_a",
"button_b",
"button_x",
"button_y",
"button_lstick",
"button_rstick",
"button_l",
"button_r",
"button_zl",
"button_zr",
"button_plus",
"button_minus",
"button_dleft",
"button_dup",
"button_dright",
"button_ddown",
"button_lstick_left",
"button_lstick_up",
"button_lstick_right",
"button_lstick_down",
"button_rstick_left",
"button_rstick_up",
"button_rstick_right",
"button_rstick_down",
"button_sl",
"button_sr",
"button_home",
"button_screenshot",
}};
}
namespace NativeAnalog {
const std::array<const char*, NumAnalogs> mapping = {{
"lstick",
"rstick",
}};
}
namespace NativeMouseButton {
const std::array<const char*, NumMouseButtons> mapping = {{
"left",
"right",
"middle",
"forward",
"back",
}};
}
Values values = {};
bool configuring_global = true;

View File

@@ -12,340 +12,10 @@
#include <string>
#include <vector>
#include "common/common_types.h"
#include "input_common/settings.h"
namespace Settings {
namespace NativeButton {
enum Values {
A,
B,
X,
Y,
LStick,
RStick,
L,
R,
ZL,
ZR,
Plus,
Minus,
DLeft,
DUp,
DRight,
DDown,
LStick_Left,
LStick_Up,
LStick_Right,
LStick_Down,
RStick_Left,
RStick_Up,
RStick_Right,
RStick_Down,
SL,
SR,
Home,
Screenshot,
NumButtons,
};
constexpr int BUTTON_HID_BEGIN = A;
constexpr int BUTTON_NS_BEGIN = Home;
constexpr int BUTTON_HID_END = BUTTON_NS_BEGIN;
constexpr int BUTTON_NS_END = NumButtons;
constexpr int NUM_BUTTONS_HID = BUTTON_HID_END - BUTTON_HID_BEGIN;
constexpr int NUM_BUTTONS_NS = BUTTON_NS_END - BUTTON_NS_BEGIN;
extern const std::array<const char*, NumButtons> mapping;
} // namespace NativeButton
namespace NativeAnalog {
enum Values {
LStick,
RStick,
NumAnalogs,
};
constexpr int STICK_HID_BEGIN = LStick;
constexpr int STICK_HID_END = NumAnalogs;
constexpr int NUM_STICKS_HID = NumAnalogs;
extern const std::array<const char*, NumAnalogs> mapping;
} // namespace NativeAnalog
namespace NativeMouseButton {
enum Values {
Left,
Right,
Middle,
Forward,
Back,
NumMouseButtons,
};
constexpr int MOUSE_HID_BEGIN = Left;
constexpr int MOUSE_HID_END = NumMouseButtons;
constexpr int NUM_MOUSE_HID = NumMouseButtons;
extern const std::array<const char*, NumMouseButtons> mapping;
} // namespace NativeMouseButton
namespace NativeKeyboard {
enum Keys {
None,
Error,
A = 4,
B,
C,
D,
E,
F,
G,
H,
I,
J,
K,
L,
M,
N,
O,
P,
Q,
R,
S,
T,
U,
V,
W,
X,
Y,
Z,
N1,
N2,
N3,
N4,
N5,
N6,
N7,
N8,
N9,
N0,
Enter,
Escape,
Backspace,
Tab,
Space,
Minus,
Equal,
LeftBrace,
RightBrace,
Backslash,
Tilde,
Semicolon,
Apostrophe,
Grave,
Comma,
Dot,
Slash,
CapsLockKey,
F1,
F2,
F3,
F4,
F5,
F6,
F7,
F8,
F9,
F10,
F11,
F12,
SystemRequest,
ScrollLockKey,
Pause,
Insert,
Home,
PageUp,
Delete,
End,
PageDown,
Right,
Left,
Down,
Up,
NumLockKey,
KPSlash,
KPAsterisk,
KPMinus,
KPPlus,
KPEnter,
KP1,
KP2,
KP3,
KP4,
KP5,
KP6,
KP7,
KP8,
KP9,
KP0,
KPDot,
Key102,
Compose,
Power,
KPEqual,
F13,
F14,
F15,
F16,
F17,
F18,
F19,
F20,
F21,
F22,
F23,
F24,
Open,
Help,
Properties,
Front,
Stop,
Repeat,
Undo,
Cut,
Copy,
Paste,
Find,
Mute,
VolumeUp,
VolumeDown,
CapsLockActive,
NumLockActive,
ScrollLockActive,
KPComma,
KPLeftParenthesis,
KPRightParenthesis,
LeftControlKey = 0xE0,
LeftShiftKey,
LeftAltKey,
LeftMetaKey,
RightControlKey,
RightShiftKey,
RightAltKey,
RightMetaKey,
MediaPlayPause,
MediaStopCD,
MediaPrevious,
MediaNext,
MediaEject,
MediaVolumeUp,
MediaVolumeDown,
MediaMute,
MediaWebsite,
MediaBack,
MediaForward,
MediaStop,
MediaFind,
MediaScrollUp,
MediaScrollDown,
MediaEdit,
MediaSleep,
MediaCoffee,
MediaRefresh,
MediaCalculator,
NumKeyboardKeys,
};
static_assert(NumKeyboardKeys == 0xFC, "Incorrect number of keyboard keys.");
enum Modifiers {
LeftControl,
LeftShift,
LeftAlt,
LeftMeta,
RightControl,
RightShift,
RightAlt,
RightMeta,
CapsLock,
ScrollLock,
NumLock,
NumKeyboardMods,
};
constexpr int KEYBOARD_KEYS_HID_BEGIN = None;
constexpr int KEYBOARD_KEYS_HID_END = NumKeyboardKeys;
constexpr int NUM_KEYBOARD_KEYS_HID = NumKeyboardKeys;
constexpr int KEYBOARD_MODS_HID_BEGIN = LeftControl;
constexpr int KEYBOARD_MODS_HID_END = NumKeyboardMods;
constexpr int NUM_KEYBOARD_MODS_HID = NumKeyboardMods;
} // namespace NativeKeyboard
using ButtonsRaw = std::array<std::string, NativeButton::NumButtons>;
using AnalogsRaw = std::array<std::string, NativeAnalog::NumAnalogs>;
using MouseButtonsRaw = std::array<std::string, NativeMouseButton::NumMouseButtons>;
using KeyboardKeysRaw = std::array<std::string, NativeKeyboard::NumKeyboardKeys>;
using KeyboardModsRaw = std::array<std::string, NativeKeyboard::NumKeyboardMods>;
constexpr u32 JOYCON_BODY_NEON_RED = 0xFF3C28;
constexpr u32 JOYCON_BUTTONS_NEON_RED = 0x1E0A0A;
constexpr u32 JOYCON_BODY_NEON_BLUE = 0x0AB9E6;
constexpr u32 JOYCON_BUTTONS_NEON_BLUE = 0x001E1E;
enum class ControllerType {
ProController,
DualJoycon,
RightJoycon,
LeftJoycon,
};
struct PlayerInput {
bool connected;
ControllerType type;
ButtonsRaw buttons;
AnalogsRaw analogs;
u32 body_color_right;
u32 button_color_right;
u32 body_color_left;
u32 button_color_left;
};
struct TouchscreenInput {
bool enabled;
std::string device;
u32 finger;
u32 diameter_x;
u32 diameter_y;
u32 rotation_angle;
};
enum class RendererBackend {
OpenGL = 0,
Vulkan = 1,
@@ -461,6 +131,8 @@ struct Values {
// Controls
std::array<PlayerInput, 10> players;
bool use_docked_mode;
bool mouse_enabled;
std::string mouse_device;
MouseButtonsRaw mouse_buttons;
@@ -474,14 +146,15 @@ struct Values {
AnalogsRaw debug_pad_analogs;
std::string motion_device;
bool vibration_enabled;
TouchscreenInput touchscreen;
std::atomic_bool is_device_reload_pending{true};
std::string udp_input_address;
u16 udp_input_port;
u8 udp_pad_index;
bool use_docked_mode;
// Data Storage
bool use_virtual_sd;
bool gamecard_inserted;

View File

@@ -7,6 +7,8 @@ add_library(input_common STATIC
main.h
motion_emu.cpp
motion_emu.h
settings.cpp
settings.h
gcadapter/gc_adapter.cpp
gcadapter/gc_adapter.h
gcadapter/gc_poller.cpp

View File

@@ -191,7 +191,7 @@ public:
bool GetAnalogDirectionStatus(Input::AnalogDirection direction) const override {
const auto [x, y] = GetStatus();
const float directional_deadzone = 0.4f;
const float directional_deadzone = 0.5f;
switch (direction) {
case Input::AnalogDirection::RIGHT:
return x > directional_deadzone;
@@ -232,7 +232,7 @@ std::unique_ptr<Input::AnalogDevice> GCAnalogFactory::Create(const Common::Param
const int port = params.Get("port", 0);
const int axis_x = params.Get("axis_x", 0);
const int axis_y = params.Get("axis_y", 1);
const float deadzone = std::clamp(params.Get("deadzone", 0.0f), 0.0f, .99f);
const float deadzone = std::clamp(params.Get("deadzone", 0.0f), 0.0f, 1.0f);
const float range = std::clamp(params.Get("range", 1.0f), 0.50f, 1.50f);
return std::make_unique<GCAnalog>(port, axis_x, axis_y, deadzone, adapter.get(), range);

View File

@@ -18,67 +18,166 @@
namespace InputCommon {
static std::shared_ptr<Keyboard> keyboard;
static std::shared_ptr<MotionEmu> motion_emu;
#ifdef HAVE_SDL2
static std::unique_ptr<SDL::State> sdl;
#endif
static std::unique_ptr<CemuhookUDP::State> udp;
static std::shared_ptr<GCButtonFactory> gcbuttons;
static std::shared_ptr<GCAnalogFactory> gcanalog;
struct InputSubsystem::Impl {
void Initialize() {
auto gcadapter = std::make_shared<GCAdapter::Adapter>();
gcbuttons = std::make_shared<GCButtonFactory>(gcadapter);
Input::RegisterFactory<Input::ButtonDevice>("gcpad", gcbuttons);
gcanalog = std::make_shared<GCAnalogFactory>(gcadapter);
Input::RegisterFactory<Input::AnalogDevice>("gcpad", gcanalog);
void Init() {
auto gcadapter = std::make_shared<GCAdapter::Adapter>();
gcbuttons = std::make_shared<GCButtonFactory>(gcadapter);
Input::RegisterFactory<Input::ButtonDevice>("gcpad", gcbuttons);
gcanalog = std::make_shared<GCAnalogFactory>(gcadapter);
Input::RegisterFactory<Input::AnalogDevice>("gcpad", gcanalog);
keyboard = std::make_shared<Keyboard>();
Input::RegisterFactory<Input::ButtonDevice>("keyboard", keyboard);
Input::RegisterFactory<Input::AnalogDevice>("analog_from_button",
std::make_shared<AnalogFromButton>());
motion_emu = std::make_shared<MotionEmu>();
Input::RegisterFactory<Input::MotionDevice>("motion_emu", motion_emu);
keyboard = std::make_shared<Keyboard>();
Input::RegisterFactory<Input::ButtonDevice>("keyboard", keyboard);
Input::RegisterFactory<Input::AnalogDevice>("analog_from_button",
std::make_shared<AnalogFromButton>());
motion_emu = std::make_shared<MotionEmu>();
Input::RegisterFactory<Input::MotionDevice>("motion_emu", motion_emu);
#ifdef HAVE_SDL2
sdl = SDL::Init();
sdl = SDL::Init();
#endif
udp = CemuhookUDP::Init();
}
udp = CemuhookUDP::Init();
}
void Shutdown() {
Input::UnregisterFactory<Input::ButtonDevice>("keyboard");
keyboard.reset();
Input::UnregisterFactory<Input::AnalogDevice>("analog_from_button");
Input::UnregisterFactory<Input::MotionDevice>("motion_emu");
motion_emu.reset();
void Shutdown() {
Input::UnregisterFactory<Input::ButtonDevice>("keyboard");
keyboard.reset();
Input::UnregisterFactory<Input::AnalogDevice>("analog_from_button");
Input::UnregisterFactory<Input::MotionDevice>("motion_emu");
motion_emu.reset();
#ifdef HAVE_SDL2
sdl.reset();
sdl.reset();
#endif
udp.reset();
Input::UnregisterFactory<Input::ButtonDevice>("gcpad");
Input::UnregisterFactory<Input::AnalogDevice>("gcpad");
udp.reset();
Input::UnregisterFactory<Input::ButtonDevice>("gcpad");
Input::UnregisterFactory<Input::AnalogDevice>("gcpad");
gcbuttons.reset();
gcanalog.reset();
gcbuttons.reset();
gcanalog.reset();
}
[[nodiscard]] std::vector<Common::ParamPackage> GetInputDevices() const {
std::vector<Common::ParamPackage> devices = {
Common::ParamPackage{{"display", "Any"}, {"class", "any"}},
Common::ParamPackage{{"display", "Keyboard/Mouse"}, {"class", "key"}},
};
#ifdef HAVE_SDL2
auto sdl_devices = sdl->GetInputDevices();
devices.insert(devices.end(), sdl_devices.begin(), sdl_devices.end());
#endif
auto udp_devices = udp->GetInputDevices();
devices.insert(devices.end(), udp_devices.begin(), udp_devices.end());
return devices;
}
[[nodiscard]] AnalogMapping GetAnalogMappingForDevice(
const Common::ParamPackage& params) const {
if (!params.Has("class") || params.Get("class", "") == "any") {
return {};
}
if (params.Get("class", "") == "key") {
// TODO consider returning the SDL key codes for the default keybindings
return {};
}
#ifdef HAVE_SDL2
if (params.Get("class", "") == "sdl") {
return sdl->GetAnalogMappingForDevice(params);
}
#endif
return {};
}
[[nodiscard]] ButtonMapping GetButtonMappingForDevice(
const Common::ParamPackage& params) const {
if (!params.Has("class") || params.Get("class", "") == "any") {
return {};
}
if (params.Get("class", "") == "key") {
// TODO consider returning the SDL key codes for the default keybindings
return {};
}
#ifdef HAVE_SDL2
if (params.Get("class", "") == "sdl") {
return sdl->GetButtonMappingForDevice(params);
}
#endif
return {};
}
std::shared_ptr<Keyboard> keyboard;
std::shared_ptr<MotionEmu> motion_emu;
#ifdef HAVE_SDL2
std::unique_ptr<SDL::State> sdl;
#endif
std::unique_ptr<CemuhookUDP::State> udp;
std::shared_ptr<GCButtonFactory> gcbuttons;
std::shared_ptr<GCAnalogFactory> gcanalog;
};
InputSubsystem::InputSubsystem() : impl{std::make_unique<Impl>()} {}
InputSubsystem::~InputSubsystem() = default;
void InputSubsystem::Initialize() {
impl->Initialize();
}
Keyboard* GetKeyboard() {
return keyboard.get();
void InputSubsystem::Shutdown() {
impl->Shutdown();
}
MotionEmu* GetMotionEmu() {
return motion_emu.get();
Keyboard* InputSubsystem::GetKeyboard() {
return impl->keyboard.get();
}
GCButtonFactory* GetGCButtons() {
return gcbuttons.get();
const Keyboard* InputSubsystem::GetKeyboard() const {
return impl->keyboard.get();
}
GCAnalogFactory* GetGCAnalogs() {
return gcanalog.get();
MotionEmu* InputSubsystem::GetMotionEmu() {
return impl->motion_emu.get();
}
const MotionEmu* InputSubsystem::GetMotionEmu() const {
return impl->motion_emu.get();
}
std::vector<Common::ParamPackage> InputSubsystem::GetInputDevices() const {
return impl->GetInputDevices();
}
AnalogMapping InputSubsystem::GetAnalogMappingForDevice(const Common::ParamPackage& device) const {
return impl->GetAnalogMappingForDevice(device);
}
ButtonMapping InputSubsystem::GetButtonMappingForDevice(const Common::ParamPackage& device) const {
return impl->GetButtonMappingForDevice(device);
}
GCAnalogFactory* InputSubsystem::GetGCAnalogs() {
return impl->gcanalog.get();
}
const GCAnalogFactory* InputSubsystem::GetGCAnalogs() const {
return impl->gcanalog.get();
}
GCButtonFactory* InputSubsystem::GetGCButtons() {
return impl->gcbuttons.get();
}
const GCButtonFactory* InputSubsystem::GetGCButtons() const {
return impl->gcbuttons.get();
}
std::vector<std::unique_ptr<Polling::DevicePoller>> InputSubsystem::GetPollers(
Polling::DeviceType type) const {
#ifdef HAVE_SDL2
return impl->sdl->GetPollers(type);
#else
return {};
#endif
}
std::string GenerateKeyboardParam(int key_code) {
@@ -102,18 +201,4 @@ std::string GenerateAnalogParamFromKeys(int key_up, int key_down, int key_left,
};
return circle_pad_param.Serialize();
}
namespace Polling {
std::vector<std::unique_ptr<DevicePoller>> GetPollers(DeviceType type) {
std::vector<std::unique_ptr<DevicePoller>> pollers;
#ifdef HAVE_SDL2
pollers = sdl->GetPollers(type);
#endif
return pollers;
}
} // namespace Polling
} // namespace InputCommon

View File

@@ -6,45 +6,19 @@
#include <memory>
#include <string>
#include <unordered_map>
#include <vector>
#include "input_common/gcadapter/gc_poller.h"
#include "input_common/settings.h"
namespace Common {
class ParamPackage;
}
namespace InputCommon {
/// Initializes and registers all built-in input device factories.
void Init();
/// Deregisters all built-in input device factories and shuts them down.
void Shutdown();
class Keyboard;
/// Gets the keyboard button device factory.
Keyboard* GetKeyboard();
class MotionEmu;
/// Gets the motion emulation factory.
MotionEmu* GetMotionEmu();
GCButtonFactory* GetGCButtons();
GCAnalogFactory* GetGCAnalogs();
/// Generates a serialized param package for creating a keyboard button device
std::string GenerateKeyboardParam(int key_code);
/// Generates a serialized param package for creating an analog device taking input from keyboard
std::string GenerateAnalogParamFromKeys(int key_up, int key_down, int key_left, int key_right,
int key_modifier, float modifier_scale);
namespace Polling {
enum class DeviceType { Button, Analog };
enum class DeviceType { Button, AnalogPreferred };
/**
* A class that can be used to get inputs from an input device like controllers without having to
@@ -54,7 +28,9 @@ class DevicePoller {
public:
virtual ~DevicePoller() = default;
/// Setup and start polling for inputs, should be called before GetNextInput
virtual void Start() = 0;
/// If a device_id is provided, events should be filtered to only include events from this
/// device id
virtual void Start(const std::string& device_id = "") = 0;
/// Stop polling
virtual void Stop() = 0;
/**
@@ -68,4 +44,88 @@ public:
// Get all DevicePoller from all backends for a specific device type
std::vector<std::unique_ptr<DevicePoller>> GetPollers(DeviceType type);
} // namespace Polling
class GCAnalogFactory;
class GCButtonFactory;
class Keyboard;
class MotionEmu;
/**
* Given a ParamPackage for a Device returned from `GetInputDevices`, attempt to get the default
* mapping for the device. This is currently only implemented for the SDL backend devices.
*/
using AnalogMapping = std::unordered_map<Settings::NativeAnalog::Values, Common::ParamPackage>;
using ButtonMapping = std::unordered_map<Settings::NativeButton::Values, Common::ParamPackage>;
class InputSubsystem {
public:
explicit InputSubsystem();
~InputSubsystem();
InputSubsystem(const InputSubsystem&) = delete;
InputSubsystem& operator=(const InputSubsystem&) = delete;
InputSubsystem(InputSubsystem&&) = delete;
InputSubsystem& operator=(InputSubsystem&&) = delete;
/// Initializes and registers all built-in input device factories.
void Initialize();
/// Unregisters all built-in input device factories and shuts them down.
void Shutdown();
/// Retrieves the underlying keyboard device.
[[nodiscard]] Keyboard* GetKeyboard();
/// Retrieves the underlying keyboard device.
[[nodiscard]] const Keyboard* GetKeyboard() const;
/// Retrieves the underlying motion emulation factory.
[[nodiscard]] MotionEmu* GetMotionEmu();
/// Retrieves the underlying motion emulation factory.
[[nodiscard]] const MotionEmu* GetMotionEmu() const;
/**
* Returns all available input devices that this Factory can create a new device with.
* Each returned ParamPackage should have a `display` field used for display, a class field for
* backends to determine if this backend is meant to service the request and any other
* information needed to identify this in the backend later.
*/
[[nodiscard]] std::vector<Common::ParamPackage> GetInputDevices() const;
/// Retrieves the analog mappings for the given device.
[[nodiscard]] AnalogMapping GetAnalogMappingForDevice(const Common::ParamPackage& device) const;
/// Retrieves the button mappings for the given device.
[[nodiscard]] ButtonMapping GetButtonMappingForDevice(const Common::ParamPackage& device) const;
/// Retrieves the underlying GameCube analog handler.
[[nodiscard]] GCAnalogFactory* GetGCAnalogs();
/// Retrieves the underlying GameCube analog handler.
[[nodiscard]] const GCAnalogFactory* GetGCAnalogs() const;
/// Retrieves the underlying GameCube button handler.
[[nodiscard]] GCButtonFactory* GetGCButtons();
/// Retrieves the underlying GameCube button handler.
[[nodiscard]] const GCButtonFactory* GetGCButtons() const;
/// Get all DevicePoller from all backends for a specific device type
[[nodiscard]] std::vector<std::unique_ptr<Polling::DevicePoller>> GetPollers(
Polling::DeviceType type) const;
private:
struct Impl;
std::unique_ptr<Impl> impl;
};
/// Generates a serialized param package for creating a keyboard button device
std::string GenerateKeyboardParam(int key_code);
/// Generates a serialized param package for creating an analog device taking input from keyboard
std::string GenerateAnalogParamFromKeys(int key_up, int key_down, int key_left, int key_right,
int key_modifier, float modifier_scale);
} // namespace InputCommon

View File

@@ -6,6 +6,7 @@
#include <memory>
#include <vector>
#include "common/param_package.h"
#include "input_common/main.h"
namespace InputCommon::Polling {
@@ -22,14 +23,24 @@ public:
/// Unregisters SDL device factories and shut them down.
virtual ~State() = default;
virtual Pollers GetPollers(Polling::DeviceType type) = 0;
virtual Pollers GetPollers(Polling::DeviceType type) {
return {};
}
virtual std::vector<Common::ParamPackage> GetInputDevices() {
return {};
}
virtual ButtonMapping GetButtonMappingForDevice(const Common::ParamPackage&) {
return {};
}
virtual AnalogMapping GetAnalogMappingForDevice(const Common::ParamPackage&) {
return {};
}
};
class NullState : public State {
public:
Pollers GetPollers(Polling::DeviceType type) override {
return {};
}
};
std::unique_ptr<State> Init();

View File

@@ -7,6 +7,8 @@
#include <cmath>
#include <functional>
#include <mutex>
#include <optional>
#include <sstream>
#include <string>
#include <thread>
#include <tuple>
@@ -23,7 +25,8 @@
namespace InputCommon::SDL {
static std::string GetGUID(SDL_Joystick* joystick) {
namespace {
std::string GetGUID(SDL_Joystick* joystick) {
const SDL_JoystickGUID guid = SDL_JoystickGetGUID(joystick);
char guid_str[33];
SDL_JoystickGetGUIDString(guid, guid_str, sizeof(guid_str));
@@ -31,7 +34,8 @@ static std::string GetGUID(SDL_Joystick* joystick) {
}
/// Creates a ParamPackage from an SDL_Event that can directly be used to create a ButtonDevice
static Common::ParamPackage SDLEventToButtonParamPackage(SDLState& state, const SDL_Event& event);
Common::ParamPackage SDLEventToButtonParamPackage(SDLState& state, const SDL_Event& event);
} // Anonymous namespace
static int SDLEventWatcher(void* user_data, SDL_Event* event) {
auto* const sdl_state = static_cast<SDLState*>(user_data);
@@ -48,8 +52,10 @@ static int SDLEventWatcher(void* user_data, SDL_Event* event) {
class SDLJoystick {
public:
SDLJoystick(std::string guid_, int port_, SDL_Joystick* joystick)
: guid{std::move(guid_)}, port{port_}, sdl_joystick{joystick, &SDL_JoystickClose} {}
SDLJoystick(std::string guid_, int port_, SDL_Joystick* joystick,
SDL_GameController* gamecontroller)
: guid{std::move(guid_)}, port{port_}, sdl_joystick{joystick, &SDL_JoystickClose},
sdl_controller{gamecontroller, &SDL_GameControllerClose} {}
void SetButton(int button, bool value) {
std::lock_guard lock{mutex};
@@ -115,10 +121,15 @@ public:
return sdl_joystick.get();
}
void SetSDLJoystick(SDL_Joystick* joystick) {
void SetSDLJoystick(SDL_Joystick* joystick, SDL_GameController* controller) {
sdl_controller.reset(controller);
sdl_joystick.reset(joystick);
}
SDL_GameController* GetSDLGameController() const {
return sdl_controller.get();
}
private:
struct State {
std::unordered_map<int, bool> buttons;
@@ -128,6 +139,7 @@ private:
std::string guid;
int port;
std::unique_ptr<SDL_Joystick, decltype(&SDL_JoystickClose)> sdl_joystick;
std::unique_ptr<SDL_GameController, decltype(&SDL_GameControllerClose)> sdl_controller;
mutable std::mutex mutex;
};
@@ -136,18 +148,19 @@ std::shared_ptr<SDLJoystick> SDLState::GetSDLJoystickByGUID(const std::string& g
const auto it = joystick_map.find(guid);
if (it != joystick_map.end()) {
while (it->second.size() <= static_cast<std::size_t>(port)) {
auto joystick =
std::make_shared<SDLJoystick>(guid, static_cast<int>(it->second.size()), nullptr);
auto joystick = std::make_shared<SDLJoystick>(guid, static_cast<int>(it->second.size()),
nullptr, nullptr);
it->second.emplace_back(std::move(joystick));
}
return it->second[port];
}
auto joystick = std::make_shared<SDLJoystick>(guid, 0, nullptr);
auto joystick = std::make_shared<SDLJoystick>(guid, 0, nullptr, nullptr);
return joystick_map[guid].emplace_back(std::move(joystick));
}
std::shared_ptr<SDLJoystick> SDLState::GetSDLJoystickBySDLID(SDL_JoystickID sdl_id) {
auto sdl_joystick = SDL_JoystickFromInstanceID(sdl_id);
auto sdl_controller = SDL_GameControllerFromInstanceID(sdl_id);
const std::string guid = GetGUID(sdl_joystick);
std::lock_guard lock{joystick_map_mutex};
@@ -171,23 +184,27 @@ std::shared_ptr<SDLJoystick> SDLState::GetSDLJoystickBySDLID(SDL_JoystickID sdl_
});
if (nullptr_it != map_it->second.end()) {
// ... and map it
(*nullptr_it)->SetSDLJoystick(sdl_joystick);
(*nullptr_it)->SetSDLJoystick(sdl_joystick, sdl_controller);
return *nullptr_it;
}
// There is no SDLJoystick without a mapped SDL_Joystick
// Create a new SDLJoystick
const int port = static_cast<int>(map_it->second.size());
auto joystick = std::make_shared<SDLJoystick>(guid, port, sdl_joystick);
auto joystick = std::make_shared<SDLJoystick>(guid, port, sdl_joystick, sdl_controller);
return map_it->second.emplace_back(std::move(joystick));
}
auto joystick = std::make_shared<SDLJoystick>(guid, 0, sdl_joystick);
auto joystick = std::make_shared<SDLJoystick>(guid, 0, sdl_joystick, sdl_controller);
return joystick_map[guid].emplace_back(std::move(joystick));
}
void SDLState::InitJoystick(int joystick_index) {
SDL_Joystick* sdl_joystick = SDL_JoystickOpen(joystick_index);
SDL_GameController* sdl_gamecontroller = nullptr;
if (SDL_IsGameController(joystick_index)) {
sdl_gamecontroller = SDL_GameControllerOpen(joystick_index);
}
if (!sdl_joystick) {
LOG_ERROR(Input, "failed to open joystick {}", joystick_index);
return;
@@ -196,7 +213,7 @@ void SDLState::InitJoystick(int joystick_index) {
std::lock_guard lock{joystick_map_mutex};
if (joystick_map.find(guid) == joystick_map.end()) {
auto joystick = std::make_shared<SDLJoystick>(guid, 0, sdl_joystick);
auto joystick = std::make_shared<SDLJoystick>(guid, 0, sdl_joystick, sdl_gamecontroller);
joystick_map[guid].emplace_back(std::move(joystick));
return;
}
@@ -205,11 +222,11 @@ void SDLState::InitJoystick(int joystick_index) {
joystick_guid_list.begin(), joystick_guid_list.end(),
[](const std::shared_ptr<SDLJoystick>& joystick) { return !joystick->GetSDLJoystick(); });
if (it != joystick_guid_list.end()) {
(*it)->SetSDLJoystick(sdl_joystick);
(*it)->SetSDLJoystick(sdl_joystick, sdl_gamecontroller);
return;
}
const int port = static_cast<int>(joystick_guid_list.size());
auto joystick = std::make_shared<SDLJoystick>(guid, port, sdl_joystick);
auto joystick = std::make_shared<SDLJoystick>(guid, port, sdl_joystick, sdl_gamecontroller);
joystick_guid_list.emplace_back(std::move(joystick));
}
@@ -231,7 +248,7 @@ void SDLState::CloseJoystick(SDL_Joystick* sdl_joystick) {
// Destruct SDL_Joystick outside the lock guard because SDL can internally call the
// event callback which locks the mutex again.
joystick->SetSDLJoystick(nullptr);
joystick->SetSDLJoystick(nullptr, nullptr);
}
void SDLState::HandleGameControllerEvent(const SDL_Event& event) {
@@ -346,7 +363,7 @@ public:
bool GetAnalogDirectionStatus(Input::AnalogDirection direction) const override {
const auto [x, y] = GetStatus();
const float directional_deadzone = 0.4f;
const float directional_deadzone = 0.5f;
switch (direction) {
case Input::AnalogDirection::RIGHT:
return x > directional_deadzone;
@@ -460,7 +477,7 @@ public:
const int port = params.Get("port", 0);
const int axis_x = params.Get("axis_x", 0);
const int axis_y = params.Get("axis_y", 1);
const float deadzone = std::clamp(params.Get("deadzone", 0.0f), 0.0f, .99f);
const float deadzone = std::clamp(params.Get("deadzone", 0.0f), 0.0f, 1.0f);
const float range = std::clamp(params.Get("range", 1.0f), 0.50f, 1.50f);
auto joystick = state.GetSDLJoystickByGUID(guid, port);
@@ -476,8 +493,10 @@ private:
SDLState::SDLState() {
using namespace Input;
RegisterFactory<ButtonDevice>("sdl", std::make_shared<SDLButtonFactory>(*this));
RegisterFactory<AnalogDevice>("sdl", std::make_shared<SDLAnalogFactory>(*this));
analog_factory = std::make_shared<SDLAnalogFactory>(*this);
button_factory = std::make_shared<SDLButtonFactory>(*this);
RegisterFactory<AnalogDevice>("sdl", analog_factory);
RegisterFactory<ButtonDevice>("sdl", button_factory);
// If the frontend is going to manage the event loop, then we dont start one here
start_thread = !SDL_WasInit(SDL_INIT_JOYSTICK);
@@ -485,6 +504,7 @@ SDLState::SDLState() {
LOG_CRITICAL(Input, "SDL_Init(SDL_INIT_JOYSTICK) failed with: {}", SDL_GetError());
return;
}
has_gamecontroller = SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER);
if (SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1") == SDL_FALSE) {
LOG_ERROR(Input, "Failed to set hint for background events with: {}", SDL_GetError());
}
@@ -497,7 +517,7 @@ SDLState::SDLState() {
using namespace std::chrono_literals;
while (initialized) {
SDL_PumpEvents();
std::this_thread::sleep_for(10ms);
std::this_thread::sleep_for(5ms);
}
});
}
@@ -523,65 +543,221 @@ SDLState::~SDLState() {
}
}
static Common::ParamPackage SDLEventToButtonParamPackage(SDLState& state, const SDL_Event& event) {
Common::ParamPackage params({{"engine", "sdl"}});
std::vector<Common::ParamPackage> SDLState::GetInputDevices() {
std::scoped_lock lock(joystick_map_mutex);
std::vector<Common::ParamPackage> devices;
for (const auto& [key, value] : joystick_map) {
for (const auto& joystick : value) {
auto joy = joystick->GetSDLJoystick();
if (auto controller = joystick->GetSDLGameController()) {
std::string name =
fmt::format("{} {}", SDL_GameControllerName(controller), joystick->GetPort());
devices.emplace_back(Common::ParamPackage{
{"class", "sdl"},
{"display", std::move(name)},
{"guid", joystick->GetGUID()},
{"port", std::to_string(joystick->GetPort())},
});
} else if (joy) {
std::string name = fmt::format("{} {}", SDL_JoystickName(joy), joystick->GetPort());
devices.emplace_back(Common::ParamPackage{
{"class", "sdl"},
{"display", std::move(name)},
{"guid", joystick->GetGUID()},
{"port", std::to_string(joystick->GetPort())},
});
}
}
}
return devices;
}
switch (event.type) {
case SDL_JOYAXISMOTION: {
const auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which);
params.Set("port", joystick->GetPort());
params.Set("guid", joystick->GetGUID());
params.Set("axis", event.jaxis.axis);
if (event.jaxis.value > 0) {
params.Set("direction", "+");
params.Set("threshold", "0.5");
} else {
params.Set("direction", "-");
params.Set("threshold", "-0.5");
}
break;
}
case SDL_JOYBUTTONUP: {
const auto joystick = state.GetSDLJoystickBySDLID(event.jbutton.which);
params.Set("port", joystick->GetPort());
params.Set("guid", joystick->GetGUID());
params.Set("button", event.jbutton.button);
break;
}
case SDL_JOYHATMOTION: {
const auto joystick = state.GetSDLJoystickBySDLID(event.jhat.which);
params.Set("port", joystick->GetPort());
params.Set("guid", joystick->GetGUID());
params.Set("hat", event.jhat.hat);
switch (event.jhat.value) {
case SDL_HAT_UP:
params.Set("direction", "up");
break;
case SDL_HAT_DOWN:
params.Set("direction", "down");
break;
case SDL_HAT_LEFT:
params.Set("direction", "left");
break;
case SDL_HAT_RIGHT:
params.Set("direction", "right");
break;
default:
return {};
}
break;
}
namespace {
Common::ParamPackage BuildAnalogParamPackageForButton(int port, std::string guid, u8 axis,
float value = 0.1) {
Common::ParamPackage params({{"engine", "sdl"}});
params.Set("port", port);
params.Set("guid", guid);
params.Set("axis", axis);
if (value > 0) {
params.Set("direction", "+");
params.Set("threshold", "0.5");
} else {
params.Set("direction", "-");
params.Set("threshold", "-0.5");
}
return params;
}
namespace Polling {
Common::ParamPackage BuildButtonParamPackageForButton(int port, std::string guid, u8 button) {
Common::ParamPackage params({{"engine", "sdl"}});
params.Set("port", port);
params.Set("guid", guid);
params.Set("button", button);
return params;
}
Common::ParamPackage BuildHatParamPackageForButton(int port, std::string guid, u8 hat, u8 value) {
Common::ParamPackage params({{"engine", "sdl"}});
params.Set("port", port);
params.Set("guid", guid);
params.Set("hat", hat);
switch (value) {
case SDL_HAT_UP:
params.Set("direction", "up");
break;
case SDL_HAT_DOWN:
params.Set("direction", "down");
break;
case SDL_HAT_LEFT:
params.Set("direction", "left");
break;
case SDL_HAT_RIGHT:
params.Set("direction", "right");
break;
default:
return {};
}
return params;
}
Common::ParamPackage SDLEventToButtonParamPackage(SDLState& state, const SDL_Event& event) {
switch (event.type) {
case SDL_JOYAXISMOTION: {
const auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which);
return BuildAnalogParamPackageForButton(joystick->GetPort(), joystick->GetGUID(),
event.jaxis.axis, event.jaxis.value);
}
case SDL_JOYBUTTONUP: {
const auto joystick = state.GetSDLJoystickBySDLID(event.jbutton.which);
return BuildButtonParamPackageForButton(joystick->GetPort(), joystick->GetGUID(),
event.jbutton.button);
}
case SDL_JOYHATMOTION: {
const auto joystick = state.GetSDLJoystickBySDLID(event.jhat.which);
return BuildHatParamPackageForButton(joystick->GetPort(), joystick->GetGUID(),
event.jhat.hat, event.jhat.value);
}
}
return {};
}
Common::ParamPackage BuildParamPackageForBinding(int port, const std::string& guid,
const SDL_GameControllerButtonBind& binding) {
switch (binding.bindType) {
case SDL_CONTROLLER_BINDTYPE_AXIS:
return BuildAnalogParamPackageForButton(port, guid, binding.value.axis);
case SDL_CONTROLLER_BINDTYPE_BUTTON:
return BuildButtonParamPackageForButton(port, guid, binding.value.button);
case SDL_CONTROLLER_BINDTYPE_HAT:
return BuildHatParamPackageForButton(port, guid, binding.value.hat.hat,
binding.value.hat.hat_mask);
}
return {};
}
Common::ParamPackage BuildParamPackageForAnalog(int port, const std::string& guid, int axis_x,
int axis_y) {
Common::ParamPackage params;
params.Set("engine", "sdl");
params.Set("port", port);
params.Set("guid", guid);
params.Set("axis_x", axis_x);
params.Set("axis_y", axis_y);
return params;
}
} // Anonymous namespace
ButtonMapping SDLState::GetButtonMappingForDevice(const Common::ParamPackage& params) {
// This list is missing ZL/ZR since those are not considered buttons in SDL GameController.
// We will add those afterwards
// This list also excludes Screenshot since theres not really a mapping for that
std::unordered_map<Settings::NativeButton::Values, SDL_GameControllerButton>
switch_to_sdl_button = {
{Settings::NativeButton::A, SDL_CONTROLLER_BUTTON_B},
{Settings::NativeButton::B, SDL_CONTROLLER_BUTTON_A},
{Settings::NativeButton::X, SDL_CONTROLLER_BUTTON_Y},
{Settings::NativeButton::Y, SDL_CONTROLLER_BUTTON_X},
{Settings::NativeButton::LStick, SDL_CONTROLLER_BUTTON_LEFTSTICK},
{Settings::NativeButton::RStick, SDL_CONTROLLER_BUTTON_RIGHTSTICK},
{Settings::NativeButton::L, SDL_CONTROLLER_BUTTON_LEFTSHOULDER},
{Settings::NativeButton::R, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER},
{Settings::NativeButton::Plus, SDL_CONTROLLER_BUTTON_START},
{Settings::NativeButton::Minus, SDL_CONTROLLER_BUTTON_BACK},
{Settings::NativeButton::DLeft, SDL_CONTROLLER_BUTTON_DPAD_LEFT},
{Settings::NativeButton::DUp, SDL_CONTROLLER_BUTTON_DPAD_UP},
{Settings::NativeButton::DRight, SDL_CONTROLLER_BUTTON_DPAD_RIGHT},
{Settings::NativeButton::DDown, SDL_CONTROLLER_BUTTON_DPAD_DOWN},
{Settings::NativeButton::SL, SDL_CONTROLLER_BUTTON_LEFTSHOULDER},
{Settings::NativeButton::SR, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER},
{Settings::NativeButton::Home, SDL_CONTROLLER_BUTTON_GUIDE},
};
if (!params.Has("guid") || !params.Has("port")) {
return {};
}
const auto joystick = GetSDLJoystickByGUID(params.Get("guid", ""), params.Get("port", 0));
auto controller = joystick->GetSDLGameController();
if (!controller) {
return {};
}
ButtonMapping mapping{};
for (const auto& [switch_button, sdl_button] : switch_to_sdl_button) {
const auto& binding = SDL_GameControllerGetBindForButton(controller, sdl_button);
mapping[switch_button] =
BuildParamPackageForBinding(joystick->GetPort(), joystick->GetGUID(), binding);
}
// Add the missing bindings for ZL/ZR
std::unordered_map<Settings::NativeButton::Values, SDL_GameControllerAxis> switch_to_sdl_axis =
{
{Settings::NativeButton::ZL, SDL_CONTROLLER_AXIS_TRIGGERLEFT},
{Settings::NativeButton::ZR, SDL_CONTROLLER_AXIS_TRIGGERRIGHT},
};
for (const auto& [switch_button, sdl_axis] : switch_to_sdl_axis) {
const auto& binding = SDL_GameControllerGetBindForAxis(controller, sdl_axis);
mapping[switch_button] =
BuildParamPackageForBinding(joystick->GetPort(), joystick->GetGUID(), binding);
}
return mapping;
}
AnalogMapping SDLState::GetAnalogMappingForDevice(const Common::ParamPackage& params) {
if (!params.Has("guid") || !params.Has("port")) {
return {};
}
const auto joystick = GetSDLJoystickByGUID(params.Get("guid", ""), params.Get("port", 0));
auto controller = joystick->GetSDLGameController();
if (!controller) {
return {};
}
AnalogMapping mapping = {};
const auto& binding_left_x =
SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_LEFTX);
const auto& binding_left_y =
SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_LEFTY);
mapping[Settings::NativeAnalog::LStick] =
BuildParamPackageForAnalog(joystick->GetPort(), joystick->GetGUID(),
binding_left_x.value.axis, binding_left_y.value.axis);
const auto& binding_right_x =
SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_RIGHTX);
const auto& binding_right_y =
SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_RIGHTY);
mapping[Settings::NativeAnalog::RStick] =
BuildParamPackageForAnalog(joystick->GetPort(), joystick->GetGUID(),
binding_right_x.value.axis, binding_right_y.value.axis);
return mapping;
}
namespace Polling {
class SDLPoller : public InputCommon::Polling::DevicePoller {
public:
explicit SDLPoller(SDLState& state_) : state(state_) {}
void Start() override {
void Start(const std::string& device_id) override {
state.event_queue.Clear();
state.polling = true;
}
@@ -601,71 +777,106 @@ public:
Common::ParamPackage GetNextInput() override {
SDL_Event event;
while (state.event_queue.Pop(event)) {
switch (event.type) {
case SDL_JOYAXISMOTION:
if (std::abs(event.jaxis.value / 32767.0) < 0.5) {
break;
}
[[fallthrough]];
case SDL_JOYBUTTONUP:
case SDL_JOYHATMOTION:
return SDLEventToButtonParamPackage(state, event);
const auto package = FromEvent(event);
if (package) {
return *package;
}
}
return {};
}
std::optional<Common::ParamPackage> FromEvent(const SDL_Event& event) {
switch (event.type) {
case SDL_JOYAXISMOTION:
if (std::abs(event.jaxis.value / 32767.0) < 0.5) {
break;
}
[[fallthrough]];
case SDL_JOYBUTTONUP:
case SDL_JOYHATMOTION:
return {SDLEventToButtonParamPackage(state, event)};
}
return {};
}
};
class SDLAnalogPoller final : public SDLPoller {
/**
* Attempts to match the press to a controller joy axis (left/right stick) and if a match
* isn't found, checks if the event matches anything from SDLButtonPoller and uses that
* instead
*/
class SDLAnalogPreferredPoller final : public SDLPoller {
public:
explicit SDLAnalogPoller(SDLState& state_) : SDLPoller(state_) {}
void Start() override {
SDLPoller::Start();
explicit SDLAnalogPreferredPoller(SDLState& state_)
: SDLPoller(state_), button_poller(state_) {}
void Start(const std::string& device_id) override {
SDLPoller::Start(device_id);
// Load the game controller
// Reset stored axes
analog_x_axis = -1;
analog_y_axis = -1;
analog_axes_joystick = -1;
}
Common::ParamPackage GetNextInput() override {
SDL_Event event;
while (state.event_queue.Pop(event)) {
if (event.type != SDL_JOYAXISMOTION || std::abs(event.jaxis.value / 32767.0) < 0.5) {
// Filter out axis events that are below a threshold
if (event.type == SDL_JOYAXISMOTION && std::abs(event.jaxis.value / 32767.0) < 0.5) {
continue;
}
// An analog device needs two axes, so we need to store the axis for later and wait for
// a second SDL event. The axes also must be from the same joystick.
const int axis = event.jaxis.axis;
if (analog_x_axis == -1) {
analog_x_axis = axis;
analog_axes_joystick = event.jaxis.which;
} else if (analog_y_axis == -1 && analog_x_axis != axis &&
analog_axes_joystick == event.jaxis.which) {
analog_y_axis = axis;
// Simplify controller config by testing if game controller support is enabled.
if (event.type == SDL_JOYAXISMOTION) {
const auto axis = event.jaxis.axis;
const auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which);
const auto controller = joystick->GetSDLGameController();
if (controller) {
const auto axis_left_x =
SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_LEFTX)
.value.axis;
const auto axis_left_y =
SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_LEFTY)
.value.axis;
const auto axis_right_x =
SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_RIGHTX)
.value.axis;
const auto axis_right_y =
SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_RIGHTY)
.value.axis;
if (axis == axis_left_x || axis == axis_left_y) {
analog_x_axis = axis_left_x;
analog_y_axis = axis_left_y;
break;
} else if (axis == axis_right_x || axis == axis_right_y) {
analog_x_axis = axis_right_x;
analog_y_axis = axis_right_y;
break;
}
}
}
// If the press wasn't accepted as a joy axis, check for a button press
auto button_press = button_poller.FromEvent(event);
if (button_press) {
return *button_press;
}
}
Common::ParamPackage params;
if (analog_x_axis != -1 && analog_y_axis != -1) {
const auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which);
params.Set("engine", "sdl");
params.Set("port", joystick->GetPort());
params.Set("guid", joystick->GetGUID());
params.Set("axis_x", analog_x_axis);
params.Set("axis_y", analog_y_axis);
auto params = BuildParamPackageForAnalog(joystick->GetPort(), joystick->GetGUID(),
analog_x_axis, analog_y_axis);
analog_x_axis = -1;
analog_y_axis = -1;
analog_axes_joystick = -1;
return params;
}
return params;
return {};
}
private:
int analog_x_axis = -1;
int analog_y_axis = -1;
SDL_JoystickID analog_axes_joystick = -1;
SDLButtonPoller button_poller;
};
} // namespace Polling
@@ -673,8 +884,8 @@ SDLState::Pollers SDLState::GetPollers(InputCommon::Polling::DeviceType type) {
Pollers pollers;
switch (type) {
case InputCommon::Polling::DeviceType::Analog:
pollers.emplace_back(std::make_unique<Polling::SDLAnalogPoller>(*this));
case InputCommon::Polling::DeviceType::AnalogPreferred:
pollers.emplace_back(std::make_unique<Polling::SDLAnalogPreferredPoller>(*this));
break;
case InputCommon::Polling::DeviceType::Button:
pollers.emplace_back(std::make_unique<Polling::SDLButtonPoller>(*this));

View File

@@ -50,6 +50,11 @@ public:
std::atomic<bool> polling = false;
Common::SPSCQueue<SDL_Event> event_queue;
std::vector<Common::ParamPackage> GetInputDevices() override;
ButtonMapping GetButtonMappingForDevice(const Common::ParamPackage& params) override;
AnalogMapping GetAnalogMappingForDevice(const Common::ParamPackage& params) override;
private:
void InitJoystick(int joystick_index);
void CloseJoystick(SDL_Joystick* sdl_joystick);
@@ -57,6 +62,9 @@ private:
/// Needs to be called before SDL_QuitSubSystem.
void CloseJoysticks();
// Set to true if SDL supports game controller subsystem
bool has_gamecontroller = false;
/// Map of GUID of a list of corresponding virtual Joysticks
std::unordered_map<std::string, std::vector<std::shared_ptr<SDLJoystick>>> joystick_map;
std::mutex joystick_map_mutex;

View File

@@ -0,0 +1,33 @@
// Copyright 2020 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "input_common/settings.h"
namespace Settings {
namespace NativeButton {
const std::array<const char*, NumButtons> mapping = {{
"button_a", "button_b", "button_x", "button_y", "button_lstick",
"button_rstick", "button_l", "button_r", "button_zl", "button_zr",
"button_plus", "button_minus", "button_dleft", "button_dup", "button_dright",
"button_ddown", "button_sl", "button_sr", "button_home", "button_screenshot",
}};
}
namespace NativeAnalog {
const std::array<const char*, NumAnalogs> mapping = {{
"lstick",
"rstick",
}};
}
namespace NativeMouseButton {
const std::array<const char*, NumMouseButtons> mapping = {{
"left",
"right",
"middle",
"forward",
"back",
}};
}
} // namespace Settings

335
src/input_common/settings.h Normal file
View File

@@ -0,0 +1,335 @@
// Copyright 2020 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <array>
#include <string>
#include "common/common_types.h"
namespace Settings {
namespace NativeButton {
enum Values {
A,
B,
X,
Y,
LStick,
RStick,
L,
R,
ZL,
ZR,
Plus,
Minus,
DLeft,
DUp,
DRight,
DDown,
SL,
SR,
Home,
Screenshot,
NumButtons,
};
constexpr int BUTTON_HID_BEGIN = A;
constexpr int BUTTON_NS_BEGIN = Home;
constexpr int BUTTON_HID_END = BUTTON_NS_BEGIN;
constexpr int BUTTON_NS_END = NumButtons;
constexpr int NUM_BUTTONS_HID = BUTTON_HID_END - BUTTON_HID_BEGIN;
constexpr int NUM_BUTTONS_NS = BUTTON_NS_END - BUTTON_NS_BEGIN;
extern const std::array<const char*, NumButtons> mapping;
} // namespace NativeButton
namespace NativeAnalog {
enum Values {
LStick,
RStick,
NumAnalogs,
};
constexpr int STICK_HID_BEGIN = LStick;
constexpr int STICK_HID_END = NumAnalogs;
constexpr int NUM_STICKS_HID = NumAnalogs;
extern const std::array<const char*, NumAnalogs> mapping;
} // namespace NativeAnalog
namespace NativeMouseButton {
enum Values {
Left,
Right,
Middle,
Forward,
Back,
NumMouseButtons,
};
constexpr int MOUSE_HID_BEGIN = Left;
constexpr int MOUSE_HID_END = NumMouseButtons;
constexpr int NUM_MOUSE_HID = NumMouseButtons;
extern const std::array<const char*, NumMouseButtons> mapping;
} // namespace NativeMouseButton
namespace NativeKeyboard {
enum Keys {
None,
Error,
A = 4,
B,
C,
D,
E,
F,
G,
H,
I,
J,
K,
L,
M,
N,
O,
P,
Q,
R,
S,
T,
U,
V,
W,
X,
Y,
Z,
N1,
N2,
N3,
N4,
N5,
N6,
N7,
N8,
N9,
N0,
Enter,
Escape,
Backspace,
Tab,
Space,
Minus,
Equal,
LeftBrace,
RightBrace,
Backslash,
Tilde,
Semicolon,
Apostrophe,
Grave,
Comma,
Dot,
Slash,
CapsLockKey,
F1,
F2,
F3,
F4,
F5,
F6,
F7,
F8,
F9,
F10,
F11,
F12,
SystemRequest,
ScrollLockKey,
Pause,
Insert,
Home,
PageUp,
Delete,
End,
PageDown,
Right,
Left,
Down,
Up,
NumLockKey,
KPSlash,
KPAsterisk,
KPMinus,
KPPlus,
KPEnter,
KP1,
KP2,
KP3,
KP4,
KP5,
KP6,
KP7,
KP8,
KP9,
KP0,
KPDot,
Key102,
Compose,
Power,
KPEqual,
F13,
F14,
F15,
F16,
F17,
F18,
F19,
F20,
F21,
F22,
F23,
F24,
Open,
Help,
Properties,
Front,
Stop,
Repeat,
Undo,
Cut,
Copy,
Paste,
Find,
Mute,
VolumeUp,
VolumeDown,
CapsLockActive,
NumLockActive,
ScrollLockActive,
KPComma,
KPLeftParenthesis,
KPRightParenthesis,
LeftControlKey = 0xE0,
LeftShiftKey,
LeftAltKey,
LeftMetaKey,
RightControlKey,
RightShiftKey,
RightAltKey,
RightMetaKey,
MediaPlayPause,
MediaStopCD,
MediaPrevious,
MediaNext,
MediaEject,
MediaVolumeUp,
MediaVolumeDown,
MediaMute,
MediaWebsite,
MediaBack,
MediaForward,
MediaStop,
MediaFind,
MediaScrollUp,
MediaScrollDown,
MediaEdit,
MediaSleep,
MediaCoffee,
MediaRefresh,
MediaCalculator,
NumKeyboardKeys,
};
static_assert(NumKeyboardKeys == 0xFC, "Incorrect number of keyboard keys.");
enum Modifiers {
LeftControl,
LeftShift,
LeftAlt,
LeftMeta,
RightControl,
RightShift,
RightAlt,
RightMeta,
CapsLock,
ScrollLock,
NumLock,
NumKeyboardMods,
};
constexpr int KEYBOARD_KEYS_HID_BEGIN = None;
constexpr int KEYBOARD_KEYS_HID_END = NumKeyboardKeys;
constexpr int NUM_KEYBOARD_KEYS_HID = NumKeyboardKeys;
constexpr int KEYBOARD_MODS_HID_BEGIN = LeftControl;
constexpr int KEYBOARD_MODS_HID_END = NumKeyboardMods;
constexpr int NUM_KEYBOARD_MODS_HID = NumKeyboardMods;
} // namespace NativeKeyboard
using ButtonsRaw = std::array<std::string, NativeButton::NumButtons>;
using AnalogsRaw = std::array<std::string, NativeAnalog::NumAnalogs>;
using MouseButtonsRaw = std::array<std::string, NativeMouseButton::NumMouseButtons>;
using KeyboardKeysRaw = std::array<std::string, NativeKeyboard::NumKeyboardKeys>;
using KeyboardModsRaw = std::array<std::string, NativeKeyboard::NumKeyboardMods>;
constexpr u32 JOYCON_BODY_NEON_RED = 0xFF3C28;
constexpr u32 JOYCON_BUTTONS_NEON_RED = 0x1E0A0A;
constexpr u32 JOYCON_BODY_NEON_BLUE = 0x0AB9E6;
constexpr u32 JOYCON_BUTTONS_NEON_BLUE = 0x001E1E;
enum class ControllerType {
ProController,
DualJoyconDetached,
LeftJoycon,
RightJoycon,
Handheld,
};
struct PlayerInput {
bool connected;
ControllerType controller_type;
ButtonsRaw buttons;
AnalogsRaw analogs;
std::string lstick_mod;
std::string rstick_mod;
u32 body_color_left;
u32 body_color_right;
u32 button_color_left;
u32 button_color_right;
};
struct TouchscreenInput {
bool enabled;
std::string device;
u32 finger;
u32 diameter_x;
u32 diameter_y;
u32 rotation_angle;
};
} // namespace Settings

View File

@@ -77,10 +77,11 @@ State::State() {
std::make_unique<Client>(status, Settings::values.udp_input_address,
Settings::values.udp_input_port, Settings::values.udp_pad_index);
Input::RegisterFactory<Input::TouchDevice>("cemuhookudp",
std::make_shared<UDPTouchFactory>(status));
Input::RegisterFactory<Input::MotionDevice>("cemuhookudp",
std::make_shared<UDPMotionFactory>(status));
motion_factory = std::make_shared<UDPMotionFactory>(status);
touch_factory = std::make_shared<UDPTouchFactory>(status);
Input::RegisterFactory<Input::MotionDevice>("cemuhookudp", motion_factory);
Input::RegisterFactory<Input::TouchDevice>("cemuhookudp", touch_factory);
}
State::~State() {
@@ -88,6 +89,11 @@ State::~State() {
Input::UnregisterFactory<Input::MotionDevice>("cemuhookudp");
}
std::vector<Common::ParamPackage> State::GetInputDevices() const {
// TODO support binding udp devices
return {};
}
void State::ReloadUDPClient() {
client->ReloadSocket(Settings::values.udp_input_address, Settings::values.udp_input_port,
Settings::values.udp_pad_index);

View File

@@ -5,19 +5,26 @@
#pragma once
#include <memory>
#include <vector>
#include "common/param_package.h"
namespace InputCommon::CemuhookUDP {
class Client;
class UDPMotionFactory;
class UDPTouchFactory;
class State {
public:
State();
~State();
void ReloadUDPClient();
std::vector<Common::ParamPackage> GetInputDevices() const;
private:
std::unique_ptr<Client> client;
std::shared_ptr<UDPMotionFactory> motion_factory;
std::shared_ptr<UDPTouchFactory> touch_factory;
};
std::unique_ptr<State> Init();

View File

@@ -1,3 +1,5 @@
add_subdirectory(host_shaders)
add_library(video_core STATIC
buffer_cache/buffer_block.h
buffer_cache/buffer_cache.h
@@ -244,6 +246,9 @@ create_target_directory_groups(video_core)
target_link_libraries(video_core PUBLIC common core)
target_link_libraries(video_core PRIVATE glad xbyak)
add_dependencies(video_core host_shaders)
target_include_directories(video_core PRIVATE ${HOST_SHADERS_INCLUDE})
if (ENABLE_VULKAN)
target_include_directories(video_core PRIVATE sirit ../../externals/Vulkan-Headers/include)
target_compile_definitions(video_core PRIVATE HAS_VULKAN)

View File

@@ -0,0 +1,43 @@
set(SHADER_FILES
opengl_present.frag
opengl_present.vert
)
set(SHADER_INCLUDE ${CMAKE_CURRENT_BINARY_DIR}/include)
set(HOST_SHADERS_INCLUDE ${SHADER_INCLUDE} PARENT_SCOPE)
set(SHADER_DIR ${SHADER_INCLUDE}/video_core/host_shaders)
add_custom_command(
OUTPUT
${SHADER_DIR}
COMMAND
${CMAKE_COMMAND} -E make_directory ${SHADER_DIR}
)
set(INPUT_FILE ${CMAKE_CURRENT_SOURCE_DIR}/source_shader.h.in)
set(HEADER_GENERATOR ${CMAKE_CURRENT_SOURCE_DIR}/StringShaderHeader.cmake)
foreach(FILENAME IN ITEMS ${SHADER_FILES})
string(REPLACE "." "_" SHADER_NAME ${FILENAME})
set(SOURCE_FILE ${CMAKE_CURRENT_SOURCE_DIR}/${FILENAME})
set(HEADER_FILE ${SHADER_DIR}/${SHADER_NAME}.h)
add_custom_command(
OUTPUT
${HEADER_FILE}
COMMAND
${CMAKE_COMMAND} -P ${HEADER_GENERATOR} ${SOURCE_FILE} ${HEADER_FILE} ${INPUT_FILE}
MAIN_DEPENDENCY
${SOURCE_FILE}
DEPENDS
${HEADER_GENERATOR}
${INPUT_FILE}
)
set(SHADER_HEADERS ${SHADER_HEADERS} ${HEADER_FILE})
endforeach()
add_custom_target(host_shaders
DEPENDS
${SHADER_HEADERS}
SOURCES
${SHADER_FILES}
)

View File

@@ -0,0 +1,11 @@
set(SOURCE_FILE ${CMAKE_ARGV3})
set(HEADER_FILE ${CMAKE_ARGV4})
set(INPUT_FILE ${CMAKE_ARGV5})
get_filename_component(CONTENTS_NAME ${SOURCE_FILE} NAME)
string(REPLACE "." "_" CONTENTS_NAME ${CONTENTS_NAME})
string(TOUPPER ${CONTENTS_NAME} CONTENTS_NAME)
file(READ ${SOURCE_FILE} CONTENTS)
configure_file(${INPUT_FILE} ${HEADER_FILE} @ONLY)

View File

@@ -0,0 +1,10 @@
#version 430 core
layout (location = 0) in vec2 frag_tex_coord;
layout (location = 0) out vec4 color;
layout (binding = 0) uniform sampler2D color_texture;
void main() {
color = vec4(texture(color_texture, frag_tex_coord).rgb, 1.0f);
}

View File

@@ -0,0 +1,24 @@
#version 430 core
out gl_PerVertex {
vec4 gl_Position;
};
layout (location = 0) in vec2 vert_position;
layout (location = 1) in vec2 vert_tex_coord;
layout (location = 0) out vec2 frag_tex_coord;
// This is a truncated 3x3 matrix for 2D transformations:
// The upper-left 2x2 submatrix performs scaling/rotation/mirroring.
// The third column performs translation.
// The third row could be used for projection, which we don't need in 2D. It hence is assumed to
// implicitly be [0, 0, 1]
layout (location = 0) uniform mat3x2 modelview_matrix;
void main() {
// Multiply input position by the rotscale part of the matrix and then manually translate by
// the last column. This is equivalent to using a full 3x3 matrix and expanding the vector
// to `vec3(vert_position.xy, 1.0)`
gl_Position = vec4(mat2(modelview_matrix) * vert_position + modelview_matrix[2], 0.0, 1.0);
frag_tex_coord = vert_tex_coord;
}

View File

@@ -0,0 +1,9 @@
#pragma once
#include <string_view>
namespace HostShaders {
constexpr std::string_view @CONTENTS_NAME@ = R"(@CONTENTS@)";
} // namespace HostShaders

View File

@@ -31,19 +31,19 @@ public:
constexpr PageEntry(State state) : state{state} {}
constexpr PageEntry(VAddr addr) : state{static_cast<State>(addr >> ShiftBits)} {}
constexpr bool IsUnmapped() const {
[[nodiscard]] constexpr bool IsUnmapped() const {
return state == State::Unmapped;
}
constexpr bool IsAllocated() const {
[[nodiscard]] constexpr bool IsAllocated() const {
return state == State::Allocated;
}
constexpr bool IsValid() const {
[[nodiscard]] constexpr bool IsValid() const {
return !IsUnmapped() && !IsAllocated();
}
constexpr VAddr ToAddress() const {
[[nodiscard]] constexpr VAddr ToAddress() const {
if (!IsValid()) {
return {};
}
@@ -51,7 +51,7 @@ public:
return static_cast<VAddr>(state) << ShiftBits;
}
constexpr PageEntry operator+(u64 offset) {
[[nodiscard]] constexpr PageEntry operator+(u64 offset) const {
// If this is a reserved value, offsets do not apply
if (!IsValid()) {
return *this;
@@ -74,16 +74,16 @@ public:
/// Binds a renderer to the memory manager.
void BindRasterizer(VideoCore::RasterizerInterface& rasterizer);
std::optional<VAddr> GpuToCpuAddress(GPUVAddr addr) const;
[[nodiscard]] std::optional<VAddr> GpuToCpuAddress(GPUVAddr addr) const;
template <typename T>
T Read(GPUVAddr addr) const;
[[nodiscard]] T Read(GPUVAddr addr) const;
template <typename T>
void Write(GPUVAddr addr, T data);
u8* GetPointer(GPUVAddr addr);
const u8* GetPointer(GPUVAddr addr) const;
[[nodiscard]] u8* GetPointer(GPUVAddr addr);
[[nodiscard]] const u8* GetPointer(GPUVAddr addr) const;
/**
* ReadBlock and WriteBlock are full read and write operations over virtual
@@ -112,24 +112,24 @@ public:
/**
* IsGranularRange checks if a gpu region can be simply read with a pointer.
*/
bool IsGranularRange(GPUVAddr gpu_addr, std::size_t size) const;
[[nodiscard]] bool IsGranularRange(GPUVAddr gpu_addr, std::size_t size) const;
GPUVAddr Map(VAddr cpu_addr, GPUVAddr gpu_addr, std::size_t size);
GPUVAddr MapAllocate(VAddr cpu_addr, std::size_t size, std::size_t align);
std::optional<GPUVAddr> AllocateFixed(GPUVAddr gpu_addr, std::size_t size);
GPUVAddr Allocate(std::size_t size, std::size_t align);
[[nodiscard]] GPUVAddr Map(VAddr cpu_addr, GPUVAddr gpu_addr, std::size_t size);
[[nodiscard]] GPUVAddr MapAllocate(VAddr cpu_addr, std::size_t size, std::size_t align);
[[nodiscard]] std::optional<GPUVAddr> AllocateFixed(GPUVAddr gpu_addr, std::size_t size);
[[nodiscard]] GPUVAddr Allocate(std::size_t size, std::size_t align);
void Unmap(GPUVAddr gpu_addr, std::size_t size);
private:
PageEntry GetPageEntry(GPUVAddr gpu_addr) const;
[[nodiscard]] PageEntry GetPageEntry(GPUVAddr gpu_addr) const;
void SetPageEntry(GPUVAddr gpu_addr, PageEntry page_entry, std::size_t size = page_size);
GPUVAddr UpdateRange(GPUVAddr gpu_addr, PageEntry page_entry, std::size_t size);
std::optional<GPUVAddr> FindFreeRange(std::size_t size, std::size_t align) const;
[[nodiscard]] std::optional<GPUVAddr> FindFreeRange(std::size_t size, std::size_t align) const;
void TryLockPage(PageEntry page_entry, std::size_t size);
void TryUnlockPage(PageEntry page_entry, std::size_t size);
static constexpr std::size_t PageEntryIndex(GPUVAddr gpu_addr) {
[[nodiscard]] static constexpr std::size_t PageEntryIndex(GPUVAddr gpu_addr) {
return (gpu_addr >> page_bits) & page_table_mask;
}

View File

@@ -2,6 +2,7 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <string_view>
#include <utility>
#include <glad/glad.h>
#include "common/common_types.h"
@@ -82,11 +83,13 @@ void OGLSampler::Release() {
handle = 0;
}
void OGLShader::Create(const char* source, GLenum type) {
if (handle != 0)
void OGLShader::Create(std::string_view source, GLenum type) {
if (handle != 0) {
return;
if (source == nullptr)
}
if (source.empty()) {
return;
}
MICROPROFILE_SCOPE(OpenGL_ResourceCreation);
handle = GLShader::LoadShader(source, type);

View File

@@ -4,6 +4,7 @@
#pragma once
#include <string_view>
#include <utility>
#include <glad/glad.h>
#include "common/common_types.h"
@@ -127,7 +128,7 @@ public:
return *this;
}
void Create(const char* source, GLenum type);
void Create(std::string_view source, GLenum type);
void Release();

View File

@@ -22,6 +22,7 @@
#include "video_core/memory_manager.h"
#include "video_core/renderer_opengl/gl_arb_decompiler.h"
#include "video_core/renderer_opengl/gl_rasterizer.h"
#include "video_core/renderer_opengl/gl_resource_manager.h"
#include "video_core/renderer_opengl/gl_shader_cache.h"
#include "video_core/renderer_opengl/gl_shader_decompiler.h"
#include "video_core/renderer_opengl/gl_shader_disk_cache.h"

View File

@@ -2,6 +2,7 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <string_view>
#include <vector>
#include <glad/glad.h>
#include "common/assert.h"
@@ -11,7 +12,8 @@
namespace OpenGL::GLShader {
namespace {
const char* GetStageDebugName(GLenum type) {
std::string_view StageDebugName(GLenum type) {
switch (type) {
case GL_VERTEX_SHADER:
return "vertex";
@@ -25,12 +27,17 @@ const char* GetStageDebugName(GLenum type) {
UNIMPLEMENTED();
return "unknown";
}
} // Anonymous namespace
GLuint LoadShader(const char* source, GLenum type) {
const char* debug_type = GetStageDebugName(type);
GLuint LoadShader(std::string_view source, GLenum type) {
const std::string_view debug_type = StageDebugName(type);
const GLuint shader_id = glCreateShader(type);
glShaderSource(shader_id, 1, &source, nullptr);
const GLchar* source_string = source.data();
const GLint source_length = static_cast<GLint>(source.size());
glShaderSource(shader_id, 1, &source_string, &source_length);
LOG_DEBUG(Render_OpenGL, "Compiling {} shader...", debug_type);
glCompileShader(shader_id);

View File

@@ -38,7 +38,7 @@ void LogShaderSource(T... shaders) {
* @param source String of the GLSL shader program
* @param type Type of the shader (GL_VERTEX_SHADER, GL_GEOMETRY_SHADER or GL_FRAGMENT_SHADER)
*/
GLuint LoadShader(const char* source, GLenum type);
GLuint LoadShader(std::string_view source, GLenum type);
/**
* Utility function to create and compile an OpenGL GLSL shader program (vertex + fragment shader)

View File

@@ -21,6 +21,8 @@
#include "core/perf_stats.h"
#include "core/settings.h"
#include "core/telemetry_session.h"
#include "video_core/host_shaders/opengl_present_frag.h"
#include "video_core/host_shaders/opengl_present_vert.h"
#include "video_core/morton.h"
#include "video_core/renderer_opengl/gl_rasterizer.h"
#include "video_core/renderer_opengl/gl_shader_manager.h"
@@ -44,46 +46,6 @@ struct Frame {
bool is_srgb{}; /// Framebuffer is sRGB or RGB
};
constexpr char VERTEX_SHADER[] = R"(
#version 430 core
out gl_PerVertex {
vec4 gl_Position;
};
layout (location = 0) in vec2 vert_position;
layout (location = 1) in vec2 vert_tex_coord;
layout (location = 0) out vec2 frag_tex_coord;
// This is a truncated 3x3 matrix for 2D transformations:
// The upper-left 2x2 submatrix performs scaling/rotation/mirroring.
// The third column performs translation.
// The third row could be used for projection, which we don't need in 2D. It hence is assumed to
// implicitly be [0, 0, 1]
layout (location = 0) uniform mat3x2 modelview_matrix;
void main() {
// Multiply input position by the rotscale part of the matrix and then manually translate by
// the last column. This is equivalent to using a full 3x3 matrix and expanding the vector
// to `vec3(vert_position.xy, 1.0)`
gl_Position = vec4(mat2(modelview_matrix) * vert_position + modelview_matrix[2], 0.0, 1.0);
frag_tex_coord = vert_tex_coord;
}
)";
constexpr char FRAGMENT_SHADER[] = R"(
#version 430 core
layout (location = 0) in vec2 frag_tex_coord;
layout (location = 0) out vec4 color;
layout (binding = 0) uniform sampler2D color_texture;
void main() {
color = vec4(texture(color_texture, frag_tex_coord).rgb, 1.0f);
}
)";
constexpr GLint PositionLocation = 0;
constexpr GLint TexCoordLocation = 1;
constexpr GLint ModelViewMatrixLocation = 0;
@@ -461,10 +423,10 @@ void RendererOpenGL::InitOpenGLObjects() {
// Create shader programs
OGLShader vertex_shader;
vertex_shader.Create(VERTEX_SHADER, GL_VERTEX_SHADER);
vertex_shader.Create(HostShaders::OPENGL_PRESENT_VERT, GL_VERTEX_SHADER);
OGLShader fragment_shader;
fragment_shader.Create(FRAGMENT_SHADER, GL_FRAGMENT_SHADER);
fragment_shader.Create(HostShaders::OPENGL_PRESENT_FRAG, GL_FRAGMENT_SHADER);
vertex_program.Create(true, false, vertex_shader.handle);
fragment_program.Create(true, false, fragment_shader.handle);

View File

@@ -1443,10 +1443,10 @@ void RasterizerVulkan::UpdateFrontFace(Tegra::Engines::Maxwell3D::Regs& regs) {
}
void RasterizerVulkan::UpdatePrimitiveTopology(Tegra::Engines::Maxwell3D::Regs& regs) {
if (!state_tracker.TouchPrimitiveTopology()) {
const Maxwell::PrimitiveTopology primitive_topology = regs.draw.topology.Value();
if (!state_tracker.ChangePrimitiveTopology(primitive_topology)) {
return;
}
const Maxwell::PrimitiveTopology primitive_topology = regs.draw.topology.Value();
scheduler.Record([this, primitive_topology](vk::CommandBuffer cmdbuf) {
cmdbuf.SetPrimitiveTopologyEXT(MaxwellToVK::PrimitiveTopology(device, primitive_topology));
});

View File

@@ -42,7 +42,6 @@ Flags MakeInvalidationFlags() {
flags[DepthWriteEnable] = true;
flags[DepthCompareOp] = true;
flags[FrontFace] = true;
flags[PrimitiveTopology] = true;
flags[StencilOp] = true;
flags[StencilTestEnable] = true;
return flags;
@@ -112,10 +111,6 @@ void SetupDirtyFrontFace(Tables& tables) {
table[OFF(screen_y_control)] = FrontFace;
}
void SetupDirtyPrimitiveTopology(Tables& tables) {
tables[0][OFF(draw.topology)] = PrimitiveTopology;
}
void SetupDirtyStencilOp(Tables& tables) {
auto& table = tables[0];
table[OFF(stencil_front_op_fail)] = StencilOp;
@@ -156,13 +151,13 @@ void StateTracker::Initialize() {
SetupDirtyDepthWriteEnable(tables);
SetupDirtyDepthCompareOp(tables);
SetupDirtyFrontFace(tables);
SetupDirtyPrimitiveTopology(tables);
SetupDirtyStencilOp(tables);
SetupDirtyStencilTestEnable(tables);
}
void StateTracker::InvalidateCommandBufferState() {
system.GPU().Maxwell3D().dirty.flags |= invalidation_flags;
current_topology = INVALID_TOPOLOGY;
}
} // namespace Vulkan

View File

@@ -32,7 +32,6 @@ enum : u8 {
DepthWriteEnable,
DepthCompareOp,
FrontFace,
PrimitiveTopology,
StencilOp,
StencilTestEnable,
@@ -43,6 +42,8 @@ static_assert(Last <= std::numeric_limits<u8>::max());
} // namespace Dirty
class StateTracker {
using Maxwell = Tegra::Engines::Maxwell3D::Regs;
public:
explicit StateTracker(Core::System& system);
@@ -102,10 +103,6 @@ public:
return Exchange(Dirty::FrontFace, false);
}
bool TouchPrimitiveTopology() {
return Exchange(Dirty::PrimitiveTopology, false);
}
bool TouchStencilOp() {
return Exchange(Dirty::StencilOp, false);
}
@@ -114,7 +111,15 @@ public:
return Exchange(Dirty::StencilTestEnable, false);
}
bool ChangePrimitiveTopology(Maxwell::PrimitiveTopology new_topology) {
const bool has_changed = current_topology != new_topology;
current_topology = new_topology;
return has_changed;
}
private:
static constexpr auto INVALID_TOPOLOGY = static_cast<Maxwell::PrimitiveTopology>(~0u);
bool Exchange(std::size_t id, bool new_value) const noexcept {
auto& flags = system.GPU().Maxwell3D().dirty.flags;
const bool is_dirty = flags[id];
@@ -124,6 +129,7 @@ private:
Core::System& system;
Tegra::Engines::Maxwell3D::DirtyState::Flags invalidation_flags;
Maxwell::PrimitiveTopology current_topology = INVALID_TOPOLOGY;
};
} // namespace Vulkan

View File

@@ -386,7 +386,8 @@ u32 ShaderIR::DecodeMemory(NodeBlock& bb, u32 pc) {
break;
}
case OpCode::Id::RED: {
UNIMPLEMENTED_IF_MSG(instr.red.type != GlobalAtomicType::U32);
UNIMPLEMENTED_IF_MSG(instr.red.type != GlobalAtomicType::U32, "type={}",
static_cast<int>(instr.red.type.Value()));
const auto [real_address, base_address, descriptor] =
TrackGlobalMemory(bb, instr, true, true);
if (!real_address || !base_address) {

View File

@@ -39,6 +39,9 @@ add_executable(yuzu
configuration/configure_debug.cpp
configuration/configure_debug.h
configuration/configure_debug.ui
configuration/configure_debug_controller.cpp
configuration/configure_debug_controller.h
configuration/configure_debug_controller.ui
configuration/configure_dialog.cpp
configuration/configure_dialog.h
configuration/configure_filesystem.cpp
@@ -62,9 +65,9 @@ add_executable(yuzu
configuration/configure_input_player.cpp
configuration/configure_input_player.h
configuration/configure_input_player.ui
configuration/configure_input_simple.cpp
configuration/configure_input_simple.h
configuration/configure_input_simple.ui
configuration/configure_input_advanced.cpp
configuration/configure_input_advanced.h
configuration/configure_input_advanced.ui
configuration/configure_mouse_advanced.cpp
configuration/configure_mouse_advanced.h
configuration/configure_mouse_advanced.ui

View File

@@ -304,8 +304,9 @@ static Core::Frontend::EmuWindow::WindowSystemInfo GetWindowSystemInfo(QWindow*
return wsi;
}
GRenderWindow::GRenderWindow(GMainWindow* parent_, EmuThread* emu_thread_)
: QWidget(parent_), emu_thread(emu_thread_) {
GRenderWindow::GRenderWindow(GMainWindow* parent, EmuThread* emu_thread_,
InputCommon::InputSubsystem* input_subsystem_)
: QWidget(parent), emu_thread(emu_thread_), input_subsystem{input_subsystem_} {
setWindowTitle(QStringLiteral("yuzu %1 | %2-%3")
.arg(QString::fromUtf8(Common::g_build_name),
QString::fromUtf8(Common::g_scm_branch),
@@ -314,15 +315,15 @@ GRenderWindow::GRenderWindow(GMainWindow* parent_, EmuThread* emu_thread_)
auto layout = new QHBoxLayout(this);
layout->setMargin(0);
setLayout(layout);
InputCommon::Init();
input_subsystem->Initialize();
this->setMouseTracking(true);
connect(this, &GRenderWindow::FirstFrameDisplayed, parent_, &GMainWindow::OnLoadComplete);
connect(this, &GRenderWindow::FirstFrameDisplayed, parent, &GMainWindow::OnLoadComplete);
}
GRenderWindow::~GRenderWindow() {
InputCommon::Shutdown();
input_subsystem->Shutdown();
}
void GRenderWindow::PollEvents() {
@@ -391,11 +392,11 @@ void GRenderWindow::closeEvent(QCloseEvent* event) {
}
void GRenderWindow::keyPressEvent(QKeyEvent* event) {
InputCommon::GetKeyboard()->PressKey(event->key());
input_subsystem->GetKeyboard()->PressKey(event->key());
}
void GRenderWindow::keyReleaseEvent(QKeyEvent* event) {
InputCommon::GetKeyboard()->ReleaseKey(event->key());
input_subsystem->GetKeyboard()->ReleaseKey(event->key());
}
void GRenderWindow::mousePressEvent(QMouseEvent* event) {
@@ -409,7 +410,7 @@ void GRenderWindow::mousePressEvent(QMouseEvent* event) {
const auto [x, y] = ScaleTouch(pos);
this->TouchPressed(x, y);
} else if (event->button() == Qt::RightButton) {
InputCommon::GetMotionEmu()->BeginTilt(pos.x(), pos.y());
input_subsystem->GetMotionEmu()->BeginTilt(pos.x(), pos.y());
}
QWidget::mousePressEvent(event);
}
@@ -423,7 +424,7 @@ void GRenderWindow::mouseMoveEvent(QMouseEvent* event) {
auto pos = event->pos();
const auto [x, y] = ScaleTouch(pos);
this->TouchMoved(x, y);
InputCommon::GetMotionEmu()->Tilt(pos.x(), pos.y());
input_subsystem->GetMotionEmu()->Tilt(pos.x(), pos.y());
QWidget::mouseMoveEvent(event);
}
@@ -436,7 +437,7 @@ void GRenderWindow::mouseReleaseEvent(QMouseEvent* event) {
if (event->button() == Qt::LeftButton) {
this->TouchReleased();
} else if (event->button() == Qt::RightButton) {
InputCommon::GetMotionEmu()->EndTilt();
input_subsystem->GetMotionEmu()->EndTilt();
}
}
@@ -485,7 +486,7 @@ bool GRenderWindow::event(QEvent* event) {
void GRenderWindow::focusOutEvent(QFocusEvent* event) {
QWidget::focusOutEvent(event);
InputCommon::GetKeyboard()->ReleaseAllKeys();
input_subsystem->GetKeyboard()->ReleaseAllKeys();
}
void GRenderWindow::resizeEvent(QResizeEvent* event) {

View File

@@ -23,6 +23,10 @@ class QKeyEvent;
class QTouchEvent;
class QStringList;
namespace InputCommon {
class InputSubsystem;
}
namespace VideoCore {
enum class LoadCallbackStage;
}
@@ -121,7 +125,8 @@ class GRenderWindow : public QWidget, public Core::Frontend::EmuWindow {
Q_OBJECT
public:
GRenderWindow(GMainWindow* parent, EmuThread* emu_thread);
explicit GRenderWindow(GMainWindow* parent, EmuThread* emu_thread_,
InputCommon::InputSubsystem* input_subsystem_);
~GRenderWindow() override;
// EmuWindow implementation.
@@ -183,6 +188,7 @@ private:
QStringList GetUnsupportedGLExtensions() const;
EmuThread* emu_thread;
InputCommon::InputSubsystem* input_subsystem;
// Main context that will be shared with all other contexts that are requested.
// If this is used in a shared context setting, then this should not be used directly, but

View File

@@ -6,7 +6,6 @@
#include <QKeySequence>
#include <QSettings>
#include "common/file_util.h"
#include "configure_input_simple.h"
#include "core/hle/service/acc/profile_manager.h"
#include "core/hle/service/hid/controllers/npad.h"
#include "input_common/main.h"
@@ -32,29 +31,29 @@ Config::~Config() {
}
const std::array<int, Settings::NativeButton::NumButtons> Config::default_buttons = {
Qt::Key_A, Qt::Key_S, Qt::Key_Z, Qt::Key_X, Qt::Key_3, Qt::Key_4, Qt::Key_Q,
Qt::Key_W, Qt::Key_1, Qt::Key_2, Qt::Key_N, Qt::Key_M, Qt::Key_F, Qt::Key_T,
Qt::Key_H, Qt::Key_G, Qt::Key_Left, Qt::Key_Up, Qt::Key_Right, Qt::Key_Down, Qt::Key_J,
Qt::Key_I, Qt::Key_L, Qt::Key_K, Qt::Key_D, Qt::Key_C, Qt::Key_B, Qt::Key_V,
Qt::Key_A, Qt::Key_S, Qt::Key_Z, Qt::Key_X, Qt::Key_3, Qt::Key_4, Qt::Key_Q,
Qt::Key_W, Qt::Key_1, Qt::Key_2, Qt::Key_N, Qt::Key_M, Qt::Key_F, Qt::Key_T,
Qt::Key_H, Qt::Key_G, Qt::Key_D, Qt::Key_C, Qt::Key_B, Qt::Key_V,
};
const std::array<std::array<int, 5>, Settings::NativeAnalog::NumAnalogs> Config::default_analogs{{
const std::array<std::array<int, 4>, Settings::NativeAnalog::NumAnalogs> Config::default_analogs{{
{
Qt::Key_Up,
Qt::Key_Down,
Qt::Key_Left,
Qt::Key_Right,
Qt::Key_E,
},
{
Qt::Key_I,
Qt::Key_K,
Qt::Key_J,
Qt::Key_L,
Qt::Key_R,
},
}};
const int Config::default_lstick_mod = Qt::Key_E;
const int Config::default_rstick_mod = Qt::Key_R;
const std::array<int, Settings::NativeMouseButton::NumMouseButtons> Config::default_mouse_buttons =
{
Qt::Key_BracketLeft, Qt::Key_BracketRight, Qt::Key_Apostrophe, Qt::Key_Minus, Qt::Key_Equal,
@@ -243,10 +242,10 @@ void Config::ReadPlayerValues() {
player.connected =
ReadSetting(QStringLiteral("player_%1_connected").arg(p), false).toBool();
player.type = static_cast<Settings::ControllerType>(
player.controller_type = static_cast<Settings::ControllerType>(
qt_config
->value(QStringLiteral("player_%1_type").arg(p),
static_cast<u8>(Settings::ControllerType::DualJoycon))
static_cast<u8>(Settings::ControllerType::ProController))
.toUInt());
player.body_color_left = qt_config
@@ -300,12 +299,6 @@ void Config::ReadPlayerValues() {
}
}
}
std::stable_partition(
Settings::values.players.begin(),
Settings::values.players.begin() +
Service::HID::Controller_NPad::NPadIdToIndex(Service::HID::NPAD_HANDHELD),
[](const auto& player) { return player.connected; });
}
void Config::ReadDebugValues() {
@@ -397,13 +390,6 @@ void Config::ReadTouchscreenValues() {
ReadSetting(QStringLiteral("touchscreen_diameter_y"), 15).toUInt();
}
void Config::ApplyDefaultProfileIfInputInvalid() {
if (!std::any_of(Settings::values.players.begin(), Settings::values.players.end(),
[](const Settings::PlayerInput& in) { return in.connected; })) {
ApplyInputProfileConfiguration(UISettings::values.profile_index);
}
}
void Config::ReadAudioValues() {
qt_config->beginGroup(QStringLiteral("Audio"));
@@ -433,6 +419,8 @@ void Config::ReadControlValues() {
ReadMouseValues();
ReadTouchscreenValues();
Settings::values.vibration_enabled =
ReadSetting(QStringLiteral("vibration_enabled"), true).toBool();
Settings::values.motion_device =
ReadSetting(QStringLiteral("motion_device"),
QStringLiteral("engine:motion_emu,update_period:100,sensitivity:0.01"))
@@ -501,7 +489,7 @@ void Config::ReadDataStorageValues() {
Settings::values.gamecard_current_game =
ReadSetting(QStringLiteral("gamecard_current_game"), false).toBool();
Settings::values.gamecard_path =
ReadSetting(QStringLiteral("gamecard_path"), QStringLiteral("")).toString().toStdString();
ReadSetting(QStringLiteral("gamecard_path"), QString{}).toString().toStdString();
qt_config->endGroup();
}
@@ -515,7 +503,7 @@ void Config::ReadDebuggingValues() {
Settings::values.use_gdbstub = ReadSetting(QStringLiteral("use_gdbstub"), false).toBool();
Settings::values.gdbstub_port = ReadSetting(QStringLiteral("gdbstub_port"), 24689).toInt();
Settings::values.program_args =
ReadSetting(QStringLiteral("program_args"), QStringLiteral("")).toString().toStdString();
ReadSetting(QStringLiteral("program_args"), QString{}).toString().toStdString();
Settings::values.dump_exefs = ReadSetting(QStringLiteral("dump_exefs"), false).toBool();
Settings::values.dump_nso = ReadSetting(QStringLiteral("dump_nso"), false).toBool();
Settings::values.reporting_services =
@@ -548,8 +536,7 @@ void Config::ReadDisabledAddOnValues() {
const auto d_size = qt_config->beginReadArray(QStringLiteral("disabled"));
for (int j = 0; j < d_size; ++j) {
qt_config->setArrayIndex(j);
out.push_back(
ReadSetting(QStringLiteral("d"), QStringLiteral("")).toString().toStdString());
out.push_back(ReadSetting(QStringLiteral("d"), QString{}).toString().toStdString());
}
qt_config->endArray();
Settings::values.disabled_addons.insert_or_assign(title_id, out);
@@ -788,14 +775,11 @@ void Config::ReadUIValues() {
UISettings::values.first_start = ReadSetting(QStringLiteral("firstStart"), true).toBool();
UISettings::values.callout_flags = ReadSetting(QStringLiteral("calloutFlags"), 0).toUInt();
UISettings::values.show_console = ReadSetting(QStringLiteral("showConsole"), false).toBool();
UISettings::values.profile_index = ReadSetting(QStringLiteral("profileIndex"), 0).toUInt();
UISettings::values.pause_when_in_background =
ReadSetting(QStringLiteral("pauseWhenInBackground"), false).toBool();
UISettings::values.hide_mouse =
ReadSetting(QStringLiteral("hideInactiveMouse"), false).toBool();
ApplyDefaultProfileIfInputInvalid();
qt_config->endGroup();
}
@@ -869,8 +853,9 @@ void Config::SavePlayerValues() {
const auto& player = Settings::values.players[p];
WriteSetting(QStringLiteral("player_%1_connected").arg(p), player.connected, false);
WriteSetting(QStringLiteral("player_%1_type").arg(p), static_cast<u8>(player.type),
static_cast<u8>(Settings::ControllerType::DualJoycon));
WriteSetting(QStringLiteral("player_%1_type").arg(p),
static_cast<u8>(player.controller_type),
static_cast<u8>(Settings::ControllerType::ProController));
WriteSetting(QStringLiteral("player_%1_body_color_left").arg(p), player.body_color_left,
Settings::JOYCON_BODY_NEON_BLUE);
@@ -990,6 +975,7 @@ void Config::SaveControlValues() {
SaveMouseValues();
SaveTouchscreenValues();
WriteSetting(QStringLiteral("vibration_enabled"), Settings::values.vibration_enabled, true);
WriteSetting(QStringLiteral("motion_device"),
QString::fromStdString(Settings::values.motion_device),
QStringLiteral("engine:motion_emu,update_period:100,sensitivity:0.01"));
@@ -1036,7 +1022,7 @@ void Config::SaveDataStorageValues() {
WriteSetting(QStringLiteral("gamecard_current_game"), Settings::values.gamecard_current_game,
false);
WriteSetting(QStringLiteral("gamecard_path"),
QString::fromStdString(Settings::values.gamecard_path), QStringLiteral(""));
QString::fromStdString(Settings::values.gamecard_path), QString{});
qt_config->endGroup();
}
@@ -1049,7 +1035,7 @@ void Config::SaveDebuggingValues() {
WriteSetting(QStringLiteral("use_gdbstub"), Settings::values.use_gdbstub, false);
WriteSetting(QStringLiteral("gdbstub_port"), Settings::values.gdbstub_port, 24689);
WriteSetting(QStringLiteral("program_args"),
QString::fromStdString(Settings::values.program_args), QStringLiteral(""));
QString::fromStdString(Settings::values.program_args), QString{});
WriteSetting(QStringLiteral("dump_exefs"), Settings::values.dump_exefs, false);
WriteSetting(QStringLiteral("dump_nso"), Settings::values.dump_nso, false);
WriteSetting(QStringLiteral("quest_flag"), Settings::values.quest_flag, false);
@@ -1076,8 +1062,7 @@ void Config::SaveDisabledAddOnValues() {
qt_config->beginWriteArray(QStringLiteral("disabled"));
for (std::size_t j = 0; j < elem.second.size(); ++j) {
qt_config->setArrayIndex(static_cast<int>(j));
WriteSetting(QStringLiteral("d"), QString::fromStdString(elem.second[j]),
QStringLiteral(""));
WriteSetting(QStringLiteral("d"), QString::fromStdString(elem.second[j]), QString{});
}
qt_config->endArray();
++i;
@@ -1266,7 +1251,6 @@ void Config::SaveUIValues() {
WriteSetting(QStringLiteral("firstStart"), UISettings::values.first_start, true);
WriteSetting(QStringLiteral("calloutFlags"), UISettings::values.callout_flags, 0);
WriteSetting(QStringLiteral("showConsole"), UISettings::values.show_console, false);
WriteSetting(QStringLiteral("profileIndex"), UISettings::values.profile_index, 0);
WriteSetting(QStringLiteral("pauseWhenInBackground"),
UISettings::values.pause_when_in_background, false);
WriteSetting(QStringLiteral("hideInactiveMouse"), UISettings::values.hide_mouse, false);

View File

@@ -23,7 +23,9 @@ public:
void Save();
static const std::array<int, Settings::NativeButton::NumButtons> default_buttons;
static const std::array<std::array<int, 5>, Settings::NativeAnalog::NumAnalogs> default_analogs;
static const std::array<std::array<int, 4>, Settings::NativeAnalog::NumAnalogs> default_analogs;
static const int default_lstick_mod;
static const int default_rstick_mod;
static const std::array<int, Settings::NativeMouseButton::NumMouseButtons>
default_mouse_buttons;
static const std::array<int, Settings::NativeKeyboard::NumKeyboardKeys> default_keyboard_keys;
@@ -37,7 +39,6 @@ private:
void ReadKeyboardValues();
void ReadMouseValues();
void ReadTouchscreenValues();
void ApplyDefaultProfileIfInputInvalid();
// Read functions bases off the respective config section names.
void ReadAudioValues();

View File

@@ -6,7 +6,7 @@
<rect>
<x>0</x>
<y>0</y>
<width>382</width>
<width>650</width>
<height>650</height>
</rect>
</property>
@@ -26,13 +26,13 @@
<widget class="QListWidget" name="selectorList">
<property name="minimumSize">
<size>
<width>150</width>
<width>120</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>150</width>
<width>120</width>
<height>16777215</height>
</size>
</property>
@@ -44,76 +44,121 @@
<number>0</number>
</property>
<widget class="ConfigureGeneral" name="generalTab">
<property name="accessibleName">
<string>General</string>
</property>
<attribute name="title">
<string>General</string>
</attribute>
</widget>
<widget class="ConfigureUi" name="uiTab">
<property name="accessibleName">
<string>UI</string>
</property>
<attribute name="title">
<string>Game List</string>
</attribute>
</widget>
<widget class="ConfigureSystem" name="systemTab">
<property name="accessibleName">
<string>System</string>
</property>
<attribute name="title">
<string>System</string>
</attribute>
</widget>
<widget class="ConfigureProfileManager" name="profileManagerTab">
<property name="accessibleName">
<string>Profiles</string>
</property>
<attribute name="title">
<string>Profiles</string>
</attribute>
</widget>
<widget class="ConfigureFilesystem" name="filesystemTab">
<property name="accessibleName">
<string>Filesystem</string>
</property>
<attribute name="title">
<string>Filesystem</string>
</attribute>
</widget>
<widget class="ConfigureInputSimple" name="inputTab">
<widget class="ConfigureInput" name="inputTab">
<property name="accessibleName">
<string>Controls</string>
</property>
<attribute name="title">
<string>Input</string>
<string>Controls</string>
</attribute>
</widget>
<widget class="ConfigureHotkeys" name="hotkeysTab">
<property name="accessibleName">
<string>Hotkeys</string>
</property>
<attribute name="title">
<string>Hotkeys</string>
</attribute>
</widget>
<widget class="ConfigureCpu" name="cpuTab">
<property name="accessibleName">
<string>CPU</string>
</property>
<attribute name="title">
<string>CPU</string>
</attribute>
</widget>
<widget class="ConfigureCpuDebug" name="cpuDebugTab">
<property name="accessibleName">
<string>Debug</string>
</property>
<attribute name="title">
<string>Debug</string>
</attribute>
</widget>
<widget class="ConfigureGraphics" name="graphicsTab">
<property name="accessibleName">
<string>Graphics</string>
</property>
<attribute name="title">
<string>Graphics</string>
</attribute>
</widget>
<widget class="ConfigureGraphicsAdvanced" name="graphicsAdvancedTab">
<property name="accessibleName">
<string>Advanced</string>
</property>
<attribute name="title">
<string>GraphicsAdvanced</string>
</attribute>
</widget>
<widget class="ConfigureAudio" name="audioTab">
<property name="accessibleName">
<string>Audio</string>
</property>
<attribute name="title">
<string>Audio</string>
</attribute>
</widget>
<widget class="ConfigureDebug" name="debugTab">
<property name="accessibleName">
<string>Debug</string>
</property>
<attribute name="title">
<string>Debug</string>
</attribute>
</widget>
<widget class="ConfigureWeb" name="webTab">
<property name="accessibleName">
<string>Web</string>
</property>
<attribute name="title">
<string>Web</string>
</attribute>
</widget>
<widget class="ConfigureService" name="serviceTab">
<property name="accessibleName">
<string>Services</string>
</property>
<attribute name="title">
<string>Services</string>
</attribute>
@@ -205,9 +250,9 @@
<container>1</container>
</customwidget>
<customwidget>
<class>ConfigureInputSimple</class>
<class>ConfigureInput</class>
<extends>QWidget</extends>
<header>configuration/configure_input_simple.h</header>
<header>configuration/configure_input.h</header>
<container>1</container>
</customwidget>
<customwidget>

View File

@@ -0,0 +1,40 @@
// Copyright 2020 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "ui_configure_debug_controller.h"
#include "yuzu/configuration/configure_debug_controller.h"
ConfigureDebugController::ConfigureDebugController(QWidget* parent,
InputCommon::InputSubsystem* input_subsystem)
: QDialog(parent), ui(std::make_unique<Ui::ConfigureDebugController>()),
debug_controller(new ConfigureInputPlayer(this, 9, nullptr, input_subsystem, true)) {
ui->setupUi(this);
ui->controllerLayout->addWidget(debug_controller);
connect(ui->clear_all_button, &QPushButton::clicked, this,
[this] { debug_controller->ClearAll(); });
connect(ui->restore_defaults_button, &QPushButton::clicked, this,
[this] { debug_controller->RestoreDefaults(); });
RetranslateUI();
}
ConfigureDebugController::~ConfigureDebugController() = default;
void ConfigureDebugController::ApplyConfiguration() {
debug_controller->ApplyConfiguration();
}
void ConfigureDebugController::changeEvent(QEvent* event) {
if (event->type() == QEvent::LanguageChange) {
RetranslateUI();
}
QDialog::changeEvent(event);
}
void ConfigureDebugController::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_player.h"
class QPushButton;
namespace InputCommon {
class InputSubsystem;
}
namespace Ui {
class ConfigureDebugController;
}
class ConfigureDebugController : public QDialog {
Q_OBJECT
public:
explicit ConfigureDebugController(QWidget* parent,
InputCommon::InputSubsystem* input_subsystem);
~ConfigureDebugController() override;
void ApplyConfiguration();
private:
void changeEvent(QEvent* event) override;
void RetranslateUI();
std::unique_ptr<Ui::ConfigureDebugController> ui;
ConfigureInputPlayer* debug_controller;
};

View File

@@ -0,0 +1,97 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ConfigureDebugController</class>
<widget class="QDialog" name="ConfigureDebugController">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>780</width>
<height>500</height>
</rect>
</property>
<property name="windowTitle">
<string>Configure Debug Controller</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="controllerLayout"/>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QPushButton" name="clear_all_button">
<property name="text">
<string>Clear</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="restore_defaults_button">
<property name="text">
<string>Defaults</string>
</property>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>ConfigureDebugController</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>140</x>
<y>318</y>
</hint>
<hint type="destinationlabel">
<x>140</x>
<y>169</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>ConfigureDebugController</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>140</x>
<y>318</y>
</hint>
<hint type="destinationlabel">
<x>140</x>
<y>169</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@@ -12,7 +12,8 @@
#include "yuzu/configuration/configure_input_player.h"
#include "yuzu/hotkeys.h"
ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry)
ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry,
InputCommon::InputSubsystem* input_subsystem)
: QDialog(parent), ui(new Ui::ConfigureDialog), registry(registry) {
Settings::configuring_global = true;
@@ -20,6 +21,8 @@ ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry)
ui->hotkeysTab->Populate(registry);
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
ui->inputTab->Initialize(input_subsystem);
SetConfiguration();
PopulateSelectionList();
@@ -80,12 +83,12 @@ Q_DECLARE_METATYPE(QList<QWidget*>);
void ConfigureDialog::PopulateSelectionList() {
const std::array<std::pair<QString, QList<QWidget*>>, 6> items{
{{tr("General"), {ui->generalTab, ui->webTab, ui->debugTab, ui->uiTab}},
{{tr("General"), {ui->generalTab, ui->hotkeysTab, ui->uiTab, ui->webTab, ui->debugTab}},
{tr("System"), {ui->systemTab, ui->profileManagerTab, ui->serviceTab, ui->filesystemTab}},
{tr("CPU"), {ui->cpuTab, ui->cpuDebugTab}},
{tr("Graphics"), {ui->graphicsTab, ui->graphicsAdvancedTab}},
{tr("Audio"), {ui->audioTab}},
{tr("Controls"), {ui->inputTab, ui->hotkeysTab}}},
{tr("Controls"), ui->inputTab->GetSubTabs()}},
};
[[maybe_unused]] const QSignalBlocker blocker(ui->selectorList);
@@ -117,7 +120,7 @@ void ConfigureDialog::UpdateVisibleTabs() {
{ui->generalTab, tr("General")},
{ui->systemTab, tr("System")},
{ui->profileManagerTab, tr("Profiles")},
{ui->inputTab, tr("Input")},
{ui->inputTab, tr("Controls")},
{ui->hotkeysTab, tr("Hotkeys")},
{ui->cpuTab, tr("CPU")},
{ui->cpuDebugTab, tr("Debug")},
@@ -138,6 +141,6 @@ void ConfigureDialog::UpdateVisibleTabs() {
const QList<QWidget*> tabs = qvariant_cast<QList<QWidget*>>(items[0]->data(Qt::UserRole));
for (const auto tab : tabs) {
ui->tabWidget->addTab(tab, widgets.at(tab));
ui->tabWidget->addTab(tab, tab->accessibleName());
}
}

View File

@@ -9,6 +9,10 @@
class HotkeyRegistry;
namespace InputCommon {
class InputSubsystem;
}
namespace Ui {
class ConfigureDialog;
}
@@ -17,7 +21,8 @@ class ConfigureDialog : public QDialog {
Q_OBJECT
public:
explicit ConfigureDialog(QWidget* parent, HotkeyRegistry& registry);
explicit ConfigureDialog(QWidget* parent, HotkeyRegistry& registry,
InputCommon::InputSubsystem* input_subsystem);
~ConfigureDialog() override;
void ApplyConfiguration();

View File

@@ -8,18 +8,32 @@
#include <QSignalBlocker>
#include <QTimer>
#include "configuration/configure_touchscreen_advanced.h"
#include "core/core.h"
#include "core/hle/service/am/am.h"
#include "core/hle/service/am/applet_ae.h"
#include "core/hle/service/am/applet_oe.h"
#include "core/hle/service/hid/controllers/npad.h"
#include "core/hle/service/sm/sm.h"
#include "ui_configure_input.h"
#include "ui_configure_input_advanced.h"
#include "ui_configure_input_player.h"
#include "yuzu/configuration/configure_debug_controller.h"
#include "yuzu/configuration/configure_input.h"
#include "yuzu/configuration/configure_input_advanced.h"
#include "yuzu/configuration/configure_input_player.h"
#include "yuzu/configuration/configure_mouse_advanced.h"
#include "yuzu/configuration/configure_touchscreen_advanced.h"
namespace {
template <typename Dialog, typename... Args>
void CallConfigureDialog(ConfigureInput& parent, Args&&... args) {
Dialog dialog(&parent, std::forward<Args>(args)...);
const auto res = dialog.exec();
if (res == QDialog::Accepted) {
dialog.ApplyConfiguration();
}
}
} // Anonymous namespace
void OnDockedModeChanged(bool last_state, bool new_state) {
if (last_state == new_state) {
@@ -48,97 +62,98 @@ void OnDockedModeChanged(bool last_state, bool new_state) {
}
}
namespace {
template <typename Dialog, typename... Args>
void CallConfigureDialog(ConfigureInput& parent, Args&&... args) {
parent.ApplyConfiguration();
Dialog dialog(&parent, std::forward<Args>(args)...);
const auto res = dialog.exec();
if (res == QDialog::Accepted) {
dialog.ApplyConfiguration();
}
}
} // Anonymous namespace
ConfigureInput::ConfigureInput(QWidget* parent)
: QDialog(parent), ui(std::make_unique<Ui::ConfigureInput>()) {
: QWidget(parent), ui(std::make_unique<Ui::ConfigureInput>()) {
ui->setupUi(this);
players_controller = {
ui->player1_combobox, ui->player2_combobox, ui->player3_combobox, ui->player4_combobox,
ui->player5_combobox, ui->player6_combobox, ui->player7_combobox, ui->player8_combobox,
};
players_configure = {
ui->player1_configure, ui->player2_configure, ui->player3_configure, ui->player4_configure,
ui->player5_configure, ui->player6_configure, ui->player7_configure, ui->player8_configure,
};
RetranslateUI();
LoadConfiguration();
UpdateUIEnabled();
connect(ui->restore_defaults_button, &QPushButton::clicked, this,
&ConfigureInput::RestoreDefaults);
for (auto* enabled : players_controller) {
connect(enabled, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
&ConfigureInput::UpdateUIEnabled);
}
connect(ui->use_docked_mode, &QCheckBox::stateChanged, this, &ConfigureInput::UpdateUIEnabled);
connect(ui->handheld_connected, &QCheckBox::stateChanged, this,
&ConfigureInput::UpdateUIEnabled);
connect(ui->mouse_enabled, &QCheckBox::stateChanged, this, &ConfigureInput::UpdateUIEnabled);
connect(ui->keyboard_enabled, &QCheckBox::stateChanged, this, &ConfigureInput::UpdateUIEnabled);
connect(ui->debug_enabled, &QCheckBox::stateChanged, this, &ConfigureInput::UpdateUIEnabled);
connect(ui->touchscreen_enabled, &QCheckBox::stateChanged, this,
&ConfigureInput::UpdateUIEnabled);
for (std::size_t i = 0; i < players_configure.size(); ++i) {
connect(players_configure[i], &QPushButton::clicked, this,
[this, i] { CallConfigureDialog<ConfigureInputPlayer>(*this, i, false); });
}
connect(ui->handheld_configure, &QPushButton::clicked, this,
[this] { CallConfigureDialog<ConfigureInputPlayer>(*this, 8, false); });
connect(ui->debug_configure, &QPushButton::clicked, this,
[this] { CallConfigureDialog<ConfigureInputPlayer>(*this, 9, true); });
connect(ui->mouse_advanced, &QPushButton::clicked, this,
[this] { CallConfigureDialog<ConfigureMouseAdvanced>(*this); });
connect(ui->touchscreen_advanced, &QPushButton::clicked, this,
[this] { CallConfigureDialog<ConfigureTouchscreenAdvanced>(*this); });
}
ConfigureInput::~ConfigureInput() = default;
void ConfigureInput::Initialize(InputCommon::InputSubsystem* input_subsystem) {
player_controllers = {
new ConfigureInputPlayer(this, 0, ui->consoleInputSettings, input_subsystem),
new ConfigureInputPlayer(this, 1, ui->consoleInputSettings, input_subsystem),
new ConfigureInputPlayer(this, 2, ui->consoleInputSettings, input_subsystem),
new ConfigureInputPlayer(this, 3, ui->consoleInputSettings, input_subsystem),
new ConfigureInputPlayer(this, 4, ui->consoleInputSettings, input_subsystem),
new ConfigureInputPlayer(this, 5, ui->consoleInputSettings, input_subsystem),
new ConfigureInputPlayer(this, 6, ui->consoleInputSettings, input_subsystem),
new ConfigureInputPlayer(this, 7, ui->consoleInputSettings, input_subsystem),
};
player_tabs = {
ui->tabPlayer1, ui->tabPlayer2, ui->tabPlayer3, ui->tabPlayer4,
ui->tabPlayer5, ui->tabPlayer6, ui->tabPlayer7, ui->tabPlayer8,
};
player_connected = {
ui->checkboxPlayer1Connected, ui->checkboxPlayer2Connected, ui->checkboxPlayer3Connected,
ui->checkboxPlayer4Connected, ui->checkboxPlayer5Connected, ui->checkboxPlayer6Connected,
ui->checkboxPlayer7Connected, ui->checkboxPlayer8Connected,
};
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]);
connect(player_controllers[i], &ConfigureInputPlayer::Connected, [&, i](bool is_connected) {
if (is_connected) {
for (std::size_t index = 0; index <= i; ++index) {
player_connected[index]->setChecked(is_connected);
}
} else {
for (std::size_t index = i; index < player_tabs.size(); ++index) {
player_connected[index]->setChecked(is_connected);
}
}
});
connect(player_controllers[i], &ConfigureInputPlayer::RefreshInputDevices,
[this] { UpdateAllInputDevices(); });
connect(player_connected[i], &QCheckBox::stateChanged, [this, i](int state) {
player_controllers[i]->ConnectPlayer(state == Qt::Checked);
});
}
// Only the first player can choose handheld mode so connect the signal just to player 1
connect(player_controllers[0], &ConfigureInputPlayer::HandheldStateChanged,
[this](bool is_handheld) { UpdateDockedState(is_handheld); });
advanced = new ConfigureInputAdvanced(this);
ui->tabAdvanced->setLayout(new QHBoxLayout(ui->tabAdvanced));
ui->tabAdvanced->layout()->addWidget(advanced);
connect(advanced, &ConfigureInputAdvanced::CallDebugControllerDialog, [this, input_subsystem] {
CallConfigureDialog<ConfigureDebugController>(*this, input_subsystem);
});
connect(advanced, &ConfigureInputAdvanced::CallMouseConfigDialog, [this, input_subsystem] {
CallConfigureDialog<ConfigureMouseAdvanced>(*this, input_subsystem);
});
connect(advanced, &ConfigureInputAdvanced::CallTouchscreenConfigDialog,
[this] { CallConfigureDialog<ConfigureTouchscreenAdvanced>(*this); });
connect(ui->buttonClearAll, &QPushButton::clicked, [this] { ClearAll(); });
connect(ui->buttonRestoreDefaults, &QPushButton::clicked, [this] { RestoreDefaults(); });
RetranslateUI();
LoadConfiguration();
}
QList<QWidget*> ConfigureInput::GetSubTabs() const {
return {
ui->tabPlayer1, ui->tabPlayer2, ui->tabPlayer3, ui->tabPlayer4, ui->tabPlayer5,
ui->tabPlayer6, ui->tabPlayer7, ui->tabPlayer8, ui->tabAdvanced,
};
}
void ConfigureInput::ApplyConfiguration() {
for (std::size_t i = 0; i < players_controller.size(); ++i) {
const auto controller_type_index = players_controller[i]->currentIndex();
Settings::values.players[i].connected = controller_type_index != 0;
if (controller_type_index > 0) {
Settings::values.players[i].type =
static_cast<Settings::ControllerType>(controller_type_index - 1);
} else {
Settings::values.players[i].type = Settings::ControllerType::DualJoycon;
}
for (auto controller : player_controllers) {
controller->ApplyConfiguration();
}
advanced->ApplyConfiguration();
const bool pre_docked_mode = Settings::values.use_docked_mode;
Settings::values.use_docked_mode = ui->use_docked_mode->isChecked();
Settings::values.use_docked_mode = ui->radioDocked->isChecked();
OnDockedModeChanged(pre_docked_mode, Settings::values.use_docked_mode);
Settings::values
.players[Service::HID::Controller_NPad::NPadIdToIndex(Service::HID::NPAD_HANDHELD)]
.connected = ui->handheld_connected->isChecked();
Settings::values.debug_pad_enabled = ui->debug_enabled->isChecked();
Settings::values.mouse_enabled = ui->mouse_enabled->isChecked();
Settings::values.keyboard_enabled = ui->keyboard_enabled->isChecked();
Settings::values.touchscreen.enabled = ui->touchscreen_enabled->isChecked();
Settings::values.vibration_enabled = ui->vibrationGroup->isChecked();
}
void ConfigureInput::changeEvent(QEvent* event) {
@@ -146,94 +161,63 @@ void ConfigureInput::changeEvent(QEvent* event) {
RetranslateUI();
}
QDialog::changeEvent(event);
QWidget::changeEvent(event);
}
void ConfigureInput::RetranslateUI() {
ui->retranslateUi(this);
RetranslateControllerComboBoxes();
}
void ConfigureInput::RetranslateControllerComboBoxes() {
for (auto* controller_box : players_controller) {
[[maybe_unused]] const QSignalBlocker blocker(controller_box);
controller_box->clear();
controller_box->addItems({tr("None"), tr("Pro Controller"), tr("Dual Joycons"),
tr("Single Right Joycon"), tr("Single Left Joycon")});
}
LoadPlayerControllerIndices();
}
void ConfigureInput::UpdateUIEnabled() {
bool hit_disabled = false;
for (auto* player : players_controller) {
player->setDisabled(hit_disabled);
if (hit_disabled) {
player->setCurrentIndex(0);
}
if (!hit_disabled && player->currentIndex() == 0) {
hit_disabled = true;
}
}
for (std::size_t i = 0; i < players_controller.size(); ++i) {
players_configure[i]->setEnabled(players_controller[i]->currentIndex() != 0);
}
ui->handheld_connected->setChecked(ui->handheld_connected->isChecked() &&
!ui->use_docked_mode->isChecked());
ui->handheld_connected->setEnabled(!ui->use_docked_mode->isChecked());
ui->handheld_configure->setEnabled(ui->handheld_connected->isChecked() &&
!ui->use_docked_mode->isChecked());
ui->mouse_advanced->setEnabled(ui->mouse_enabled->isChecked());
ui->debug_configure->setEnabled(ui->debug_enabled->isChecked());
ui->touchscreen_advanced->setEnabled(ui->touchscreen_enabled->isChecked());
}
void ConfigureInput::LoadConfiguration() {
std::stable_partition(
Settings::values.players.begin(),
Settings::values.players.begin() +
Service::HID::Controller_NPad::NPadIdToIndex(Service::HID::NPAD_HANDHELD),
[](const auto& player) { return player.connected; });
LoadPlayerControllerIndices();
UpdateDockedState(Settings::values.players[0].controller_type ==
Settings::ControllerType::Handheld);
ui->use_docked_mode->setChecked(Settings::values.use_docked_mode);
ui->handheld_connected->setChecked(
Settings::values
.players[Service::HID::Controller_NPad::NPadIdToIndex(Service::HID::NPAD_HANDHELD)]
.connected);
ui->debug_enabled->setChecked(Settings::values.debug_pad_enabled);
ui->mouse_enabled->setChecked(Settings::values.mouse_enabled);
ui->keyboard_enabled->setChecked(Settings::values.keyboard_enabled);
ui->touchscreen_enabled->setChecked(Settings::values.touchscreen.enabled);
UpdateUIEnabled();
ui->vibrationGroup->setChecked(Settings::values.vibration_enabled);
}
void ConfigureInput::LoadPlayerControllerIndices() {
for (std::size_t i = 0; i < players_controller.size(); ++i) {
const auto connected = Settings::values.players[i].connected;
players_controller[i]->setCurrentIndex(
connected ? static_cast<u8>(Settings::values.players[i].type) + 1 : 0);
for (std::size_t i = 0; i < player_connected.size(); ++i) {
const auto connected = Settings::values.players[i].connected ||
(i == 0 && Settings::values.players[8].connected);
player_connected[i]->setChecked(connected);
}
}
void ConfigureInput::ClearAll() {
// We don't have a good way to know what tab is active, but we can find out by getting the
// parent of the consoleInputSettings
auto* player_tab = static_cast<ConfigureInputPlayer*>(ui->consoleInputSettings->parent());
player_tab->ClearAll();
}
void ConfigureInput::RestoreDefaults() {
players_controller[0]->setCurrentIndex(2);
// We don't have a good way to know what tab is active, but we can find out by getting the
// parent of the consoleInputSettings
auto* player_tab = static_cast<ConfigureInputPlayer*>(ui->consoleInputSettings->parent());
player_tab->RestoreDefaults();
for (std::size_t i = 1; i < players_controller.size(); ++i) {
players_controller[i]->setCurrentIndex(0);
}
ui->use_docked_mode->setCheckState(Qt::Unchecked);
ui->handheld_connected->setCheckState(Qt::Unchecked);
ui->mouse_enabled->setCheckState(Qt::Unchecked);
ui->keyboard_enabled->setCheckState(Qt::Unchecked);
ui->debug_enabled->setCheckState(Qt::Unchecked);
ui->touchscreen_enabled->setCheckState(Qt::Checked);
UpdateUIEnabled();
ui->radioDocked->setChecked(true);
ui->radioUndocked->setChecked(false);
ui->vibrationGroup->setChecked(true);
}
void ConfigureInput::UpdateDockedState(bool is_handheld) {
// If the controller type is handheld only, disallow changing docked mode
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)
if (is_handheld) {
ui->radioUndocked->setChecked(true);
}
}
void ConfigureInput::UpdateAllInputDevices() {
for (const auto& player : player_controllers) {
player->UpdateInputDevices();
}
}

View File

@@ -7,37 +7,50 @@
#include <array>
#include <memory>
#include <QDialog>
#include <QKeyEvent>
#include <QWidget>
#include "yuzu/configuration/configure_input_advanced.h"
#include "yuzu/configuration/configure_input_player.h"
#include "ui_configure_input.h"
class QPushButton;
class QCheckBox;
class QString;
class QTimer;
namespace InputCommon {
class InputSubsystem;
}
namespace Ui {
class ConfigureInput;
}
void OnDockedModeChanged(bool last_state, bool new_state);
class ConfigureInput : public QDialog {
class ConfigureInput : public QWidget {
Q_OBJECT
public:
explicit ConfigureInput(QWidget* parent = nullptr);
~ConfigureInput() override;
/// Save all button configurations to settings file
/// Initializes the input dialog with the given input subsystem.
void Initialize(InputCommon::InputSubsystem* input_subsystem_);
/// Save all button configurations to settings file.
void ApplyConfiguration();
QList<QWidget*> GetSubTabs() const;
private:
void changeEvent(QEvent* event) override;
void RetranslateUI();
void RetranslateControllerComboBoxes();
void ClearAll();
void UpdateUIEnabled();
void UpdateDockedState(bool is_handheld);
void UpdateAllInputDevices();
/// Load configuration settings.
void LoadConfiguration();
@@ -48,6 +61,8 @@ private:
std::unique_ptr<Ui::ConfigureInput> ui;
std::array<QComboBox*, 8> players_controller;
std::array<QPushButton*, 8> players_configure;
std::array<ConfigureInputPlayer*, 8> player_controllers;
std::array<QWidget*, 8> player_tabs;
std::array<QCheckBox*, 8> player_connected;
ConfigureInputAdvanced* advanced;
};

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,169 @@
// Copyright 2020 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <QColorDialog>
#include "core/core.h"
#include "core/settings.h"
#include "ui_configure_input_advanced.h"
#include "yuzu/configuration/configure_input_advanced.h"
ConfigureInputAdvanced::ConfigureInputAdvanced(QWidget* parent)
: QWidget(parent), ui(std::make_unique<Ui::ConfigureInputAdvanced>()) {
ui->setupUi(this);
controllers_color_buttons = {{
{
ui->player1_left_body_button,
ui->player1_left_buttons_button,
ui->player1_right_body_button,
ui->player1_right_buttons_button,
},
{
ui->player2_left_body_button,
ui->player2_left_buttons_button,
ui->player2_right_body_button,
ui->player2_right_buttons_button,
},
{
ui->player3_left_body_button,
ui->player3_left_buttons_button,
ui->player3_right_body_button,
ui->player3_right_buttons_button,
},
{
ui->player4_left_body_button,
ui->player4_left_buttons_button,
ui->player4_right_body_button,
ui->player4_right_buttons_button,
},
{
ui->player5_left_body_button,
ui->player5_left_buttons_button,
ui->player5_right_body_button,
ui->player5_right_buttons_button,
},
{
ui->player6_left_body_button,
ui->player6_left_buttons_button,
ui->player6_right_body_button,
ui->player6_right_buttons_button,
},
{
ui->player7_left_body_button,
ui->player7_left_buttons_button,
ui->player7_right_body_button,
ui->player7_right_buttons_button,
},
{
ui->player8_left_body_button,
ui->player8_left_buttons_button,
ui->player8_right_body_button,
ui->player8_right_buttons_button,
},
}};
for (std::size_t player_idx = 0; player_idx < controllers_color_buttons.size(); ++player_idx) {
auto& color_buttons = controllers_color_buttons[player_idx];
for (std::size_t button_idx = 0; button_idx < color_buttons.size(); ++button_idx) {
connect(color_buttons[button_idx], &QPushButton::clicked, this,
[this, player_idx, button_idx] {
OnControllerButtonClick(static_cast<int>(player_idx),
static_cast<int>(button_idx));
});
}
}
connect(ui->mouse_enabled, &QCheckBox::stateChanged, this,
&ConfigureInputAdvanced::UpdateUIEnabled);
connect(ui->debug_enabled, &QCheckBox::stateChanged, this,
&ConfigureInputAdvanced::UpdateUIEnabled);
connect(ui->touchscreen_enabled, &QCheckBox::stateChanged, this,
&ConfigureInputAdvanced::UpdateUIEnabled);
connect(ui->debug_configure, &QPushButton::clicked, this,
[this] { CallDebugControllerDialog(); });
connect(ui->mouse_advanced, &QPushButton::clicked, this, [this] { CallMouseConfigDialog(); });
connect(ui->touchscreen_advanced, &QPushButton::clicked, this,
[this] { CallTouchscreenConfigDialog(); });
LoadConfiguration();
}
ConfigureInputAdvanced::~ConfigureInputAdvanced() = default;
void ConfigureInputAdvanced::OnControllerButtonClick(int player_idx, int button_idx) {
const QColor new_bg_color = QColorDialog::getColor(controllers_colors[player_idx][button_idx]);
if (!new_bg_color.isValid()) {
return;
}
controllers_colors[player_idx][button_idx] = new_bg_color;
controllers_color_buttons[player_idx][button_idx]->setStyleSheet(
QStringLiteral("background-color: %1; min-width: 55px;")
.arg(controllers_colors[player_idx][button_idx].name()));
}
void ConfigureInputAdvanced::ApplyConfiguration() {
for (std::size_t player_idx = 0; player_idx < controllers_color_buttons.size(); ++player_idx) {
auto& player = Settings::values.players[player_idx];
std::array<u32, 4> colors{};
std::transform(controllers_colors[player_idx].begin(), controllers_colors[player_idx].end(),
colors.begin(), [](QColor color) { return color.rgb(); });
player.body_color_left = colors[0];
player.button_color_left = colors[1];
player.body_color_right = colors[2];
player.button_color_right = colors[3];
}
Settings::values.debug_pad_enabled = ui->debug_enabled->isChecked();
Settings::values.mouse_enabled = ui->mouse_enabled->isChecked();
Settings::values.keyboard_enabled = ui->keyboard_enabled->isChecked();
Settings::values.touchscreen.enabled = ui->touchscreen_enabled->isChecked();
}
void ConfigureInputAdvanced::LoadConfiguration() {
for (std::size_t player_idx = 0; player_idx < controllers_color_buttons.size(); ++player_idx) {
auto& player = Settings::values.players[player_idx];
std::array<u32, 4> colors = {
player.body_color_left,
player.button_color_left,
player.body_color_right,
player.button_color_right,
};
std::transform(colors.begin(), colors.end(), controllers_colors[player_idx].begin(),
[](u32 rgb) { return QColor::fromRgb(rgb); });
for (std::size_t button_idx = 0; button_idx < colors.size(); ++button_idx) {
controllers_color_buttons[player_idx][button_idx]->setStyleSheet(
QStringLiteral("background-color: %1; min-width: 55px;")
.arg(controllers_colors[player_idx][button_idx].name()));
}
}
ui->debug_enabled->setChecked(Settings::values.debug_pad_enabled);
ui->mouse_enabled->setChecked(Settings::values.mouse_enabled);
ui->keyboard_enabled->setChecked(Settings::values.keyboard_enabled);
ui->touchscreen_enabled->setChecked(Settings::values.touchscreen.enabled);
UpdateUIEnabled();
}
void ConfigureInputAdvanced::changeEvent(QEvent* event) {
if (event->type() == QEvent::LanguageChange) {
RetranslateUI();
}
QWidget::changeEvent(event);
}
void ConfigureInputAdvanced::RetranslateUI() {
ui->retranslateUi(this);
}
void ConfigureInputAdvanced::UpdateUIEnabled() {
ui->mouse_advanced->setEnabled(ui->mouse_enabled->isChecked());
ui->debug_configure->setEnabled(ui->debug_enabled->isChecked());
ui->touchscreen_advanced->setEnabled(ui->touchscreen_enabled->isChecked());
}

View File

@@ -0,0 +1,45 @@
// 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 <QWidget>
class QColor;
class QPushButton;
namespace Ui {
class ConfigureInputAdvanced;
}
class ConfigureInputAdvanced : public QWidget {
Q_OBJECT
public:
explicit ConfigureInputAdvanced(QWidget* parent = nullptr);
~ConfigureInputAdvanced() override;
void ApplyConfiguration();
signals:
void CallDebugControllerDialog();
void CallMouseConfigDialog();
void CallTouchscreenConfigDialog();
private:
void changeEvent(QEvent* event) override;
void RetranslateUI();
void UpdateUIEnabled();
void OnControllerButtonClick(int player_idx, int button_idx);
void LoadConfiguration();
std::unique_ptr<Ui::ConfigureInputAdvanced> ui;
std::array<std::array<QColor, 4>, 8> controllers_colors;
std::array<std::array<QPushButton*, 4>, 8> controllers_color_buttons;
};

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -10,16 +10,25 @@
#include <optional>
#include <string>
#include <QDialog>
#include <QWidget>
#include "common/param_package.h"
#include "core/settings.h"
#include "ui_configure_input.h"
class QCheckBox;
class QKeyEvent;
class QLabel;
class QPushButton;
class QSlider;
class QSpinBox;
class QString;
class QTimer;
class QWidget;
namespace InputCommon {
class InputSubsystem;
}
namespace InputCommon::Polling {
class DevicePoller;
@@ -30,77 +39,116 @@ namespace Ui {
class ConfigureInputPlayer;
}
class ConfigureInputPlayer : public QDialog {
class ConfigureInputPlayer : public QWidget {
Q_OBJECT
public:
explicit ConfigureInputPlayer(QWidget* parent, std::size_t player_index, bool debug = false);
explicit ConfigureInputPlayer(QWidget* parent, std::size_t player_index, QWidget* bottom_row,
InputCommon::InputSubsystem* input_subsystem_,
bool debug = false);
~ConfigureInputPlayer() override;
/// Save all button configurations to settings file
/// Save all button configurations to settings file.
void ApplyConfiguration();
/// Update the input devices combobox.
void UpdateInputDevices();
/// Restore all buttons to their default values.
void RestoreDefaults();
/// Clear all input configuration.
void ClearAll();
/// Set the connection state checkbox (used to sync state).
void ConnectPlayer(bool connected);
signals:
/// Emitted when this controller is connected by the user.
void Connected(bool connected);
/// Emitted when the Handheld mode is selected (undocked with dual joycons attached).
void HandheldStateChanged(bool is_handheld);
/// Emitted when the input devices combobox is being refreshed.
void RefreshInputDevices();
protected:
void showEvent(QShowEvent* event) override;
private:
void changeEvent(QEvent* event) override;
void RetranslateUI();
void OnControllerButtonClick(int i);
/// Load configuration settings.
void LoadConfiguration();
/// Restore all buttons to their default values.
void RestoreDefaults();
/// Clear all input configuration
void ClearAll();
/// Update UI to reflect current configuration.
void UpdateButtonLabels();
/// Called when the button was pressed.
void HandleClick(QPushButton* button,
std::function<void(const Common::ParamPackage&)> new_input_setter,
InputCommon::Polling::DeviceType type);
/// Finish polling and configure input using the input_setter
/// Finish polling and configure input using the input_setter.
void SetPollingResult(const Common::ParamPackage& params, bool abort);
/// Handle mouse button press events.
void mousePressEvent(QMouseEvent* event) override;
/// Handle key press events.
void keyPressEvent(QKeyEvent* event) override;
/// Update UI to reflect current configuration.
void UpdateUI();
/// Update the controller selection combobox
void UpdateControllerCombobox();
/// Update the current controller icon.
void UpdateControllerIcon();
/// Hides and disables controller settings based on the current controller type.
void UpdateControllerAvailableButtons();
/// Gets the default controller mapping for this device and auto configures the input to match.
void UpdateMappingWithDefaults();
std::unique_ptr<Ui::ConfigureInputPlayer> ui;
std::size_t player_index;
bool debug;
InputCommon::InputSubsystem* input_subsystem;
std::unique_ptr<QTimer> timeout_timer;
std::unique_ptr<QTimer> poll_timer;
static constexpr int PLAYER_COUNT = 8;
std::array<QCheckBox*, PLAYER_COUNT> player_connected_checkbox;
/// This will be the the setting function when an input is awaiting configuration.
std::optional<std::function<void(const Common::ParamPackage&)>> input_setter;
std::array<Common::ParamPackage, Settings::NativeButton::NumButtons> buttons_param;
std::array<Common::ParamPackage, Settings::NativeAnalog::NumAnalogs> analogs_param;
static constexpr int ANALOG_SUB_BUTTONS_NUM = 5;
static constexpr int ANALOG_SUB_BUTTONS_NUM = 4;
/// Each button input is represented by a QPushButton.
std::array<QPushButton*, Settings::NativeButton::NumButtons> button_map;
/// Extra buttons for the modifiers.
Common::ParamPackage lstick_mod;
Common::ParamPackage rstick_mod;
std::vector<QWidget*> debug_hidden;
std::vector<QWidget*> layout_hidden;
/// A group of five QPushButtons represent one analog input. The buttons each represent up,
/// down, left, right, and modifier, respectively.
/// A group of four QPushButtons represent one analog input. The buttons each represent up,
/// down, left, right, respectively.
std::array<std::array<QPushButton*, ANALOG_SUB_BUTTONS_NUM>, Settings::NativeAnalog::NumAnalogs>
analog_map_buttons;
/// Analog inputs are also represented each with a single button, used to configure with an
/// actual analog stick
std::array<QPushButton*, Settings::NativeAnalog::NumAnalogs> analog_map_stick;
std::array<QSlider*, Settings::NativeAnalog::NumAnalogs>
analog_map_deadzone_and_modifier_slider;
std::array<QLabel*, Settings::NativeAnalog::NumAnalogs>
analog_map_deadzone_and_modifier_slider_label;
std::array<QLabel*, Settings::NativeAnalog::NumAnalogs> analog_map_deadzone_label;
std::array<QSlider*, Settings::NativeAnalog::NumAnalogs> analog_map_deadzone_slider;
std::array<QGroupBox*, Settings::NativeAnalog::NumAnalogs> analog_map_modifier_groupbox;
std::array<QLabel*, Settings::NativeAnalog::NumAnalogs> analog_map_modifier_label;
std::array<QSlider*, Settings::NativeAnalog::NumAnalogs> analog_map_modifier_slider;
std::array<QGroupBox*, Settings::NativeAnalog::NumAnalogs> analog_map_range_groupbox;
std::array<QSpinBox*, Settings::NativeAnalog::NumAnalogs> analog_map_range_spinbox;
static const std::array<std::string, ANALOG_SUB_BUTTONS_NUM> analog_sub_buttons;
@@ -108,8 +156,14 @@ private:
/// A flag to indicate if keyboard keys are okay when configuring an input. If this is false,
/// keyboard events are ignored.
bool want_keyboard_keys = false;
bool want_keyboard_mouse = false;
std::array<QPushButton*, 4> controller_color_buttons;
std::array<QColor, 4> controller_colors;
/// List of physical devices users can map with. If a SDL backed device is selected, then you
/// can usue this device to get a default mapping.
std::vector<Common::ParamPackage> input_devices;
/// Bottom row is where console wide settings are held, and its "owned" by the parent
/// ConfigureInput widget. On show, add this widget to the main layout. This will change the
/// parent of the widget to this widget (but thats fine).
QWidget* bottom_row;
};

File diff suppressed because it is too large Load Diff

View File

@@ -1,152 +0,0 @@
// Copyright 2016 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <array>
#include <tuple>
#include "ui_configure_input_simple.h"
#include "yuzu/configuration/configure_input.h"
#include "yuzu/configuration/configure_input_player.h"
#include "yuzu/configuration/configure_input_simple.h"
#include "yuzu/uisettings.h"
namespace {
template <typename Dialog, typename... Args>
void CallConfigureDialog(ConfigureInputSimple* caller, Args&&... args) {
caller->ApplyConfiguration();
Dialog dialog(caller, std::forward<Args>(args)...);
const auto res = dialog.exec();
if (res == QDialog::Accepted) {
dialog.ApplyConfiguration();
}
}
// OnProfileSelect functions should (when applicable):
// - Set controller types
// - Set controller enabled
// - Set docked mode
// - Set advanced controller config/enabled (i.e. debug, kbd, mouse, touch)
//
// OnProfileSelect function should NOT however:
// - Reset any button mappings
// - Open any dialogs
// - Block in any way
constexpr std::size_t PLAYER_0_INDEX = 0;
constexpr std::size_t HANDHELD_INDEX = 8;
void HandheldOnProfileSelect() {
Settings::values.players[HANDHELD_INDEX].connected = true;
Settings::values.players[HANDHELD_INDEX].type = Settings::ControllerType::DualJoycon;
for (std::size_t player = 0; player < HANDHELD_INDEX; ++player) {
Settings::values.players[player].connected = false;
}
Settings::values.use_docked_mode = false;
Settings::values.keyboard_enabled = false;
Settings::values.mouse_enabled = false;
Settings::values.debug_pad_enabled = false;
Settings::values.touchscreen.enabled = true;
}
void DualJoyconsDockedOnProfileSelect() {
Settings::values.players[PLAYER_0_INDEX].connected = true;
Settings::values.players[PLAYER_0_INDEX].type = Settings::ControllerType::DualJoycon;
for (std::size_t player = 1; player <= HANDHELD_INDEX; ++player) {
Settings::values.players[player].connected = false;
}
Settings::values.use_docked_mode = true;
Settings::values.keyboard_enabled = false;
Settings::values.mouse_enabled = false;
Settings::values.debug_pad_enabled = false;
Settings::values.touchscreen.enabled = true;
}
// Name, OnProfileSelect (called when selected in drop down), OnConfigure (called when configure
// is clicked)
using InputProfile = std::tuple<const char*, void (*)(), void (*)(ConfigureInputSimple*)>;
constexpr std::array<InputProfile, 3> INPUT_PROFILES{{
{QT_TR_NOOP("Single Player - Handheld - Undocked"), HandheldOnProfileSelect,
[](ConfigureInputSimple* caller) {
CallConfigureDialog<ConfigureInputPlayer>(caller, HANDHELD_INDEX, false);
}},
{QT_TR_NOOP("Single Player - Dual Joycons - Docked"), DualJoyconsDockedOnProfileSelect,
[](ConfigureInputSimple* caller) {
CallConfigureDialog<ConfigureInputPlayer>(caller, PLAYER_0_INDEX, false);
}},
{QT_TR_NOOP("Custom"), [] {}, CallConfigureDialog<ConfigureInput>},
}};
} // namespace
void ApplyInputProfileConfiguration(int profile_index) {
std::get<1>(
INPUT_PROFILES.at(std::min(profile_index, static_cast<int>(INPUT_PROFILES.size() - 1))))();
}
ConfigureInputSimple::ConfigureInputSimple(QWidget* parent)
: QWidget(parent), ui(std::make_unique<Ui::ConfigureInputSimple>()) {
ui->setupUi(this);
for (const auto& profile : INPUT_PROFILES) {
const QString label = tr(std::get<0>(profile));
ui->profile_combobox->addItem(label, label);
}
connect(ui->profile_combobox, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
&ConfigureInputSimple::OnSelectProfile);
connect(ui->profile_configure, &QPushButton::clicked, this, &ConfigureInputSimple::OnConfigure);
LoadConfiguration();
}
ConfigureInputSimple::~ConfigureInputSimple() = default;
void ConfigureInputSimple::ApplyConfiguration() {
auto index = ui->profile_combobox->currentIndex();
// Make the stored index for "Custom" very large so that if new profiles are added it
// doesn't change.
if (index >= static_cast<int>(INPUT_PROFILES.size() - 1)) {
index = std::numeric_limits<int>::max();
}
UISettings::values.profile_index = index;
}
void ConfigureInputSimple::changeEvent(QEvent* event) {
if (event->type() == QEvent::LanguageChange) {
RetranslateUI();
}
QWidget::changeEvent(event);
}
void ConfigureInputSimple::RetranslateUI() {
ui->retranslateUi(this);
}
void ConfigureInputSimple::LoadConfiguration() {
const auto index = UISettings::values.profile_index;
if (index >= static_cast<int>(INPUT_PROFILES.size()) || index < 0) {
ui->profile_combobox->setCurrentIndex(static_cast<int>(INPUT_PROFILES.size() - 1));
} else {
ui->profile_combobox->setCurrentIndex(index);
}
}
void ConfigureInputSimple::OnSelectProfile(int index) {
const auto old_docked = Settings::values.use_docked_mode;
ApplyInputProfileConfiguration(index);
OnDockedModeChanged(old_docked, Settings::values.use_docked_mode);
}
void ConfigureInputSimple::OnConfigure() {
std::get<2>(INPUT_PROFILES.at(ui->profile_combobox->currentIndex()))(this);
}

View File

@@ -1,43 +0,0 @@
// Copyright 2016 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <memory>
#include <QWidget>
class QPushButton;
class QString;
class QTimer;
namespace Ui {
class ConfigureInputSimple;
}
// Used by configuration loader to apply a profile if the input is invalid.
void ApplyInputProfileConfiguration(int profile_index);
class ConfigureInputSimple : public QWidget {
Q_OBJECT
public:
explicit ConfigureInputSimple(QWidget* parent = nullptr);
~ConfigureInputSimple() override;
/// Save all button configurations to settings file
void ApplyConfiguration();
private:
void changeEvent(QEvent* event) override;
void RetranslateUI();
/// Load configuration settings.
void LoadConfiguration();
void OnSelectProfile(int index);
void OnConfigure();
std::unique_ptr<Ui::ConfigureInputSimple> ui;
};

View File

@@ -1,97 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ConfigureInputSimple</class>
<widget class="QWidget" name="ConfigureInputSimple">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>473</width>
<height>685</height>
</rect>
</property>
<property name="windowTitle">
<string>ConfigureInputSimple</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_5">
<item>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QGroupBox" name="gridGroupBox">
<property name="title">
<string>Profile</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="1" column="2">
<widget class="QPushButton" name="profile_configure">
<property name="text">
<string>Configure</string>
</property>
</widget>
</item>
<item row="1" column="0">
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="1" column="3">
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="1" column="1">
<widget class="QComboBox" name="profile_combobox">
<property name="minimumSize">
<size>
<width>250</width>
<height>0</height>
</size>
</property>
</widget>
</item>
<item row="0" column="1" colspan="2">
<widget class="QLabel" name="label">
<property name="text">
<string>Choose a controller configuration:</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

Some files were not shown because too many files have changed in this diff Show More