Compare commits

..

46 Commits

Author SHA1 Message Date
Lioncash
b7cd5d742e shader_bytecode: Make use of [[nodiscard]] where applicable
Ensures that all queried values are made use of.
2020-11-20 02:20:37 -05:00
Lioncash
56ecafc204 shader_bytecode: Eliminate variable shadowing 2020-11-20 02:13:45 -05:00
bunnei
92344da20c Merge pull request #4936 from lioncash/page
page_table: Allow page tables to be moved
2020-11-18 20:40:10 -08:00
bunnei
abda366362 Merge pull request #4866 from Morph1984/mjolnir-p3-prod
Project Mjölnir: Part 3 - Controller Profiles and Vibration Rework
2020-11-17 20:02:27 -08:00
Lioncash
0ca91ced2d virtual_buffer: Add compile-time type-safety guarantees with VirtualBuffer
VirtualBuffer makes use of VirtualAlloc (on Windows) and mmap() (on
other platforms). Neither of these ensure that non-trivial objects are
properly constructed in the allocated memory.

To prevent potential undefined behavior occurring due to that, we can
add a static assert to loudly complain about cases where that is done.
2020-11-17 20:09:58 -05:00
Lioncash
b3c8997829 page_table: Allow page tables to be moved
Makes page tables and virtual buffers able to be moved, but not copied,
making the interface more flexible.

Previously, with the destructor specified, but no move assignment or
constructor specified, they wouldn't be implicitly generated.
2020-11-17 20:08:20 -05:00
Lioncash
3cfd962ef4 page_table: Add missing doxygen parameters to Resize()
Resolves two -Wdocumentation warnings.
2020-11-17 19:45:20 -05:00
Lioncash
0890451c55 page_table: Remove unnecessary header inclusions
Prevents indirect inclusions for these headers.
2020-11-17 19:43:27 -05:00
Chloe
2dc9dbb809 Merge pull request #4933 from lioncash/nodisc-gpu
[gpu, render_base, rasterizer_interface]: Make use of [[nodiscard]] where applicable
2020-11-18 01:40:03 +11:00
Morph
e7e8a87927 sdl_impl: Pump SDL Events at 1000 Hz 2020-11-15 23:33:21 -05:00
Morph
b254d528bc configure_input: Accommodate for the mouse input device engine 2020-11-15 23:33:21 -05:00
Morph
ad50209383 hid: Reimplement Begin/EndPermitVibrationSession
Upon further investigation, these commands allow temporary vibrations even when the "Controller Vibration" system setting is disabled. As a result, vibrations are allowed when either the system setting or this flag is set to true. Therefore, we can only block vibrations when both flags are set to false.
2020-11-15 23:33:21 -05:00
Morph
d8ad2f3484 controllers/npad: Load input devices on init 2020-11-15 23:33:21 -05:00
Morph
6f5b942897 configure_input: Update the input profiles for other player tabs 2020-11-15 23:33:21 -05:00
Morph
97b2220a82 general: Fix compiler warnings on linux and miscellaneous changes 2020-11-15 23:33:21 -05:00
Morph
117bdc71e0 sdl_impl: Revert to the "old" method of mapping sticks
Not all controllers have a SDL_GameController binding. This caused controllers not present in the SDL GameController database to have buttons mapped instead of axes.

Furthermore, it was not possible to invert the axes when it could be useful such as emulating a horizontal single joycon or other potential cases. This allows us to invert the axes by reversing the order of mapping (vertical, then horizontal).
2020-11-15 23:33:21 -05:00
Morph
760a9e8693 applets/controller: Change the input button to create input profiles
Co-authored-by: Its-Rei <kupfel@gmail.com>
2020-11-15 23:33:21 -05:00
Morph
30e0d1c973 controllers/npad: Remove the old vibration filter
Previously we used a vibration filter that filters out amplitudes close to each other. It turns out there are cases where this results into vibrations that are too inaccurate. Remove this and move the 100Hz vibration filter (Only allowing a maximum of 100 vibrations per second) from sdl_impl to npad when enable_accurate_vibrations is set to false.
2020-11-15 23:33:21 -05:00
Morph
91c06dae1a input: Disconnect a controller prior to connecting a new one
Some games do not respond to a change in controller type if 1) The controller is not disconnected prior to being reconnected and/or 2) The controller is reconnected instantly after being disconnected.

Since it is not possible to change controllers instantly on hardware and requiring a disconnect prior to connecting a new one, we should emulate this as well with a small delay, fixing the aforementioned issue.
2020-11-15 23:33:21 -05:00
Morph
978ca65f59 hid: Implement InitializeVibrationDevice and IsVibrationDeviceMounted 2020-11-15 23:33:20 -05:00
Morph
e9e1876e82 input_common: Add VibrationDevice and VibrationDeviceFactory
A vibration device is an input device that returns an unsigned byte as status.
It represents whether the vibration device supports vibration or not.
If the status returns 1, it supports vibration. Otherwise, it does not support vibration.
2020-11-15 23:33:20 -05:00
Morph
38110dd485 configure_input: Add per-player vibration
Allows for enabling and modifying vibration and vibration strength per player.
Also adds a toggle for enabling/disabling accurate vibrations.

Co-authored-by: Its-Rei <kupfel@gmail.com>
2020-11-15 23:33:20 -05:00
Morph
d6a41cfc21 settings: Remove global vibration strength modifier
This will be replaced in favor of per-player vibration strength modifiers.
2020-11-15 23:33:20 -05:00
Morph
92fa5257c7 hid: Mark Begin/EndPermitVibrationSession as stubs
The implementation of these commands seem incomplete and causes rumble in Super Mario Party to stop working since only EndPermitVibrationSession is called. Thus, these are better off being marked as a stub until this can be investigated more thoroughly.
2020-11-15 23:33:20 -05:00
Morph
373408ae8c controllers/npad: Send an empty vibration on destruction/deactivation
This stops all controllers from continuously vibrating when emulation is stopped.
2020-11-15 23:33:20 -05:00
Morph
70f16f1722 hid: Stub IsVibrationDeviceMounted
- Used in Super Mario Odyssey
2020-11-15 23:33:20 -05:00
Morph
9b501af8e3 controllers/npad: Add heuristics to reduce rumble state changes
Sending too many state changes in a short period of time can cause massive performance issues.
As a result, we have to use several heuristics to reduce the number of state changes to minimize/eliminate this performance impact while maintaining the quality of these vibrations as much as possible.
2020-11-15 23:33:20 -05:00
Morph
652d6766d5 configure_input: Hook up the vibration percentage spinbox
This allows setting the vibration strength percentage anywhere from 1% to 100%.
Also hooks up the remaining motion button and checkbox in the Controller Applet.
2020-11-15 23:33:20 -05:00
Morph
e02ef3c3be controllers/npad: Stop games from vibrating incorrect controllers
Fixes vibration in 1-2 Switch and potentially other games where they would vibrate both players' joycons at the same time.
2020-11-15 23:33:20 -05:00
Morph
07b81f57ba hid: Fix controller rumble based on new research
This fixes the issue where rumble is only sent to the first controller.
Now, individual controllers can receive their own rumble commands.
2020-11-15 23:33:20 -05:00
Morph
31de52513e hid: Pop a struct of parameters instead of popping individual parameters
Some parameters need to be doubleword aligned due to the presence of the applet_resource_user_id.
Previously, this value was invalid in many commands where it was not doubleword aligned when popped.
2020-11-15 23:33:20 -05:00
Morph
e3c2749986 hid: Reorder all HID commands
Reorders all HID commands in command id order.
2020-11-15 23:33:20 -05:00
Morph
b92bf51ae1 hid: Implement GetVibrationDeviceInfo
The first u32 describes the vibration device type which is a Linear Resonant Actuator used in Nintendo Switch controller hardware.

The second u32 describes the vibration device position, in this case distinguishing between left and right vibration actuators.

Pro Controllers have 2 LRAs each that can vibrate independently of each other, which means they have 2 distinct vibration device handles to distinguish between the two actuators.

Similarly for joycons, the left joycon can be distinguished from the right joycon through the vibration device handle since each joycon has 1 LRA.
2020-11-15 23:33:20 -05:00
Morph
16e2e1c45f hid: Stub InitializeVibrationDevice 2020-11-15 23:33:20 -05:00
Morph
428ce8ec29 controllers/npad: Rename NPadType to NpadStyleSet
This more accurately represents the underlying type and avoids confusion with NpadType
2020-11-15 23:33:20 -05:00
Morph
0a966e2cac controllers/npad: Add DeviceHandle struct
A DeviceHandle describes a vibration device or six-axis sensor based on the npad type, npad id, and device index/position
2020-11-15 23:33:20 -05:00
Morph
ceb7b11f16 configure_input_player: Change "Defaults" button behavior
RestoreDefaults() now restores the selected devices' mappings using UpdateMappingWithDefaults().
This allows us to move the keyboard mapping from RestoreDefaults() to UpdateMappingWithDefaults().
2020-11-15 23:33:20 -05:00
Morph
8f2959f680 settings: Preparation for per-game input settings 2020-11-15 23:33:20 -05:00
Morph
8ead176639 udp/client: Reduce testing period to 5 seconds 2020-11-15 23:33:19 -05:00
Morph
64e174237e config: Migrate config files into config/custom
Co-authored-by: lat9nq <lat9nq@virginia.edu>
2020-11-15 23:33:19 -05:00
Morph
c0c4ed0d3b controllers/npad: Connect a controller on init if none are connected 2020-11-15 23:33:19 -05:00
Morph
5cafa70d3b applets/controller: Auto accept a valid single player configuration 2020-11-15 23:33:19 -05:00
Morph
484623cd61 bootmanager: Allow mouse clicks only if touch is disabled
Previously mouse clicks will not register when touch is disabled.
This rectifies that and allows mouse clicks to be mapped to other buttons if the touchscreen is disabled.
2020-11-15 23:33:19 -05:00
Morph
57d89e291d input_profiles: Implement input profiles 2020-11-15 23:33:19 -05:00
Morph
75eaab2e0f configure_input_player: Implement input exclusivity and persistence
With this, the "Input Devices" combobox should accurately reflect the input device being used and disallows inputs from other input devices unless the input device is set to "Any".
2020-11-15 23:33:19 -05:00
Morph
9d4edd4e88 ui/themes: Cleanup UI 2020-11-15 23:33:19 -05:00
74 changed files with 3534 additions and 1688 deletions

View File

@@ -1,3 +1,7 @@
QAbstractSpinBox {
min-height: 19px;
}
QPushButton#TogglableStatusBarButton {
color: #959595;
border: 1px solid transparent;
@@ -35,10 +39,10 @@ QPushButton#RendererStatusBarButton:!checked {
}
QPushButton#buttonRefreshDevices {
min-width: 20px;
min-height: 20px;
max-width: 20px;
max-height: 20px;
min-width: 21px;
min-height: 21px;
max-width: 21px;
max-height: 21px;
}
QWidget#bottomPerGameInput,
@@ -71,7 +75,7 @@ QWidget#middleControllerApplet {
QWidget#topPerGameInput QComboBox,
QWidget#middleControllerApplet QComboBox {
width: 123px;
width: 120px;
}
QWidget#connectedControllers {

View File

@@ -99,12 +99,19 @@ QGroupBox::indicator:unchecked:disabled {
}
QRadioButton {
spacing: 5px;
outline: none;
color: #eff0f1;
spacing: 3px;
padding: 0px;
border: none;
outline: none;
margin-bottom: 2px;
}
QGroupBox QRadioButton {
padding-left: 0px;
padding-right: 7px;
}
QRadioButton:disabled {
color: #76797C;
}
@@ -522,13 +529,12 @@ QToolButton#qt_toolbar_ext_button {
QPushButton {
color: #eff0f1;
border-width: 1px;
border-color: #54575B;
border-style: solid;
padding: 6px 4px;
border: 1px solid #54575B;
border-radius: 2px;
padding: 5px 0px 5px 0px;
outline: none;
min-width: 100px;
min-height: 13px;
background-color: #232629;
}
@@ -553,8 +559,9 @@ QComboBox {
selection-background-color: #3daee9;
border: 1px solid #54575B;
border-radius: 2px;
padding: 4px 6px;
min-width: 75px;
padding: 0px 4px 0px 4px;
min-width: 60px;
min-height: 23px;
background-color: #232629;
}
@@ -608,26 +615,26 @@ QComboBox::down-arrow:focus {
}
QAbstractSpinBox {
padding: 4px 6px;
border: 1px solid #54575B;
background-color: #232629;
color: #eff0f1;
border-radius: 2px;
min-width: 75px;
min-width: 52px;
min-height: 23px;
}
QAbstractSpinBox:up-button {
background-color: transparent;
subcontrol-origin: border;
subcontrol-position: center right;
left: -6px;
left: -2px;
}
QAbstractSpinBox:down-button {
background-color: transparent;
subcontrol-origin: border;
subcontrol-position: center left;
right: -6px;
right: -2px;
}
QAbstractSpinBox::up-arrow,
@@ -1277,41 +1284,33 @@ QPushButton#RendererStatusBarButton:!checked {
}
QPushButton#buttonRefreshDevices {
min-width: 24px;
min-height: 24px;
max-width: 24px;
max-height: 24px;
min-width: 23px;
min-height: 23px;
max-width: 23px;
max-height: 23px;
padding: 0px 0px;
}
QSpinBox#spinboxLStickRange,
QSpinBox#spinboxRStickRange {
padding: 4px 0px 5px 0px;
min-width: 63px;
}
QSpinBox#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;
QSpinBox#spinboxRStickRange,
QSpinBox#vibrationSpinPlayer1,
QSpinBox#vibrationSpinPlayer2,
QSpinBox#vibrationSpinPlayer3,
QSpinBox#vibrationSpinPlayer4,
QSpinBox#vibrationSpinPlayer5,
QSpinBox#vibrationSpinPlayer6,
QSpinBox#vibrationSpinPlayer7,
QSpinBox#vibrationSpinPlayer8 {
min-width: 68px;
}
QDialog#ConfigureVibration QGroupBox::indicator,
QGroupBox#motionGroup::indicator,
QGroupBox#vibrationGroup::indicator {
margin-left: 0px;
}
QDialog#ConfigureVibration QGroupBox::title,
QGroupBox#motionGroup::title,
QGroupBox#vibrationGroup::title {
spacing: 2px;
@@ -1340,16 +1339,7 @@ QWidget#middleControllerApplet {
QWidget#topPerGameInput QComboBox,
QWidget#middleControllerApplet QComboBox {
width: 119px;
}
QRadioButton#radioDocked {
margin-left: -3px;
}
QRadioButton#radioUndocked {
margin-right: 5px;
width: 120px;
}
QWidget#connectedControllers {

View File

@@ -172,8 +172,8 @@ QCheckBox {
color: #F0F0F0;
spacing: 4px;
outline: none;
padding-top: 4px;
padding-bottom: 4px;
padding-top: 2px;
padding-bottom: 2px;
}
QCheckBox:focus {
@@ -239,7 +239,7 @@ QGroupBox {
border: 1px solid #32414B;
border-radius: 4px;
margin-top: 12px;
padding: 4px;
padding: 2px;
}
QGroupBox::title {
@@ -247,7 +247,7 @@ QGroupBox::title {
subcontrol-position: top left;
padding-left: 3px;
padding-right: 5px;
padding-top: 4px;
padding-top: 2px;
}
QGroupBox::indicator {
@@ -298,6 +298,11 @@ QRadioButton {
outline: none;
}
QGroupBox QRadioButton {
padding-left: 0px;
padding-right: 7px;
}
QRadioButton:focus {
border: none;
}
@@ -321,7 +326,6 @@ QRadioButton QWidget {
QRadioButton::indicator {
border: none;
outline: none;
margin-left: 4px;
height: 16px;
width: 16px;
}
@@ -785,14 +789,8 @@ QAbstractSpinBox {
background-color: #19232D;
border: 1px solid #32414B;
color: #F0F0F0;
/* This fixes 103, 111 */
padding-top: 2px;
/* This fixes 103, 111 */
padding-bottom: 2px;
padding-left: 4px;
padding-right: 4px;
border-radius: 4px;
/* min-width: 5px; removed to fix 109 */
min-height: 19px;
}
QAbstractSpinBox:up-button {
@@ -997,10 +995,11 @@ QPushButton {
border: 1px solid #32414B;
color: #F0F0F0;
border-radius: 4px;
padding: 3px;
padding: 3px 0px 3px 0px;
outline: none;
/* Issue #194 - Special case of QPushButton inside dialogs, for better UI */
min-width: 80px;
min-height: 13px;
}
QPushButton:disabled {
@@ -1008,14 +1007,14 @@ QPushButton:disabled {
border: 1px solid #32414B;
color: #787878;
border-radius: 4px;
padding: 3px;
padding: 3px 0px 3px 0px;
}
QPushButton:checked {
background-color: #32414B;
border: 1px solid #32414B;
border-radius: 4px;
padding: 3px;
padding: 3px 0px 3px 0px;
outline: none;
}
@@ -1024,7 +1023,7 @@ QPushButton:checked:disabled {
border: 1px solid #32414B;
color: #787878;
border-radius: 4px;
padding: 3px;
padding: 3px 0px 3px 0px;
outline: none;
}
@@ -1197,15 +1196,9 @@ QComboBox {
border: 1px solid #32414B;
border-radius: 4px;
selection-background-color: #1464A0;
padding-left: 4px;
padding-right: 36px;
/* 4 + 16*2 See scrollbar size */
/* Fixes #103, #111 */
min-height: 1.5em;
/* padding-top: 2px; removed to fix #132 */
/* padding-bottom: 2px; removed to fix #132 */
/* min-width: 75px; removed to fix #109 */
/* Needed to remove indicator - fix #132 */
padding: 0px 4px 0px 4px;
min-width: 60px;
min-height: 19px;
}
QComboBox QAbstractItemView {
@@ -2198,29 +2191,40 @@ QPushButton#RendererStatusBarButton:!checked {
}
QPushButton#buttonRefreshDevices {
min-width: 20px;
min-height: 20px;
max-width: 20px;
max-height: 20px;
min-width: 19px;
min-height: 19px;
max-width: 19px;
max-height: 19px;
padding: 0px 0px;
}
QSpinBox#spinboxLStickRange,
QSpinBox#spinboxRStickRange {
min-width: 38px;
QSpinBox#spinboxRStickRange,
QSpinBox#vibrationSpinPlayer1,
QSpinBox#vibrationSpinPlayer2,
QSpinBox#vibrationSpinPlayer3,
QSpinBox#vibrationSpinPlayer4,
QSpinBox#vibrationSpinPlayer5,
QSpinBox#vibrationSpinPlayer6,
QSpinBox#vibrationSpinPlayer7,
QSpinBox#vibrationSpinPlayer8 {
min-width: 68px;
}
QDialog#ConfigureVibration QGroupBox::indicator,
QGroupBox#motionGroup::indicator,
QGroupBox#vibrationGroup::indicator {
margin-left: 0px;
}
QDialog#ConfigureVibration QGroupBox,
QWidget#bottomPerGameInput QGroupBox#motionGroup,
QWidget#bottomPerGameInput QGroupBox#vibrationGroup,
QWidget#bottomPerGameInput QGroupBox#inputConfigGroup {
padding: 0px;
}
QDialog#ConfigureVibration QGroupBox::title,
QGroupBox#motionGroup::title,
QGroupBox#vibrationGroup::title {
spacing: 2px;
@@ -2260,26 +2264,7 @@ QWidget#middleControllerApplet {
QWidget#topPerGameInput QComboBox,
QWidget#middleControllerApplet QComboBox {
padding-right: 2px;
width: 127px;
}
QGroupBox#handheldGroup {
padding-left: 0px;
}
QRadioButton#radioDocked {
margin-left: -1px;
padding-left: 0px;
}
QRadioButton#radioDocked::indicator {
margin-left: 0px;
}
QRadioButton#radioUndocked {
margin-right: 2px;
width: 120px;
}
QWidget#connectedControllers {
@@ -2352,7 +2337,7 @@ QCheckBox#checkboxPlayer5Connected,
QCheckBox#checkboxPlayer6Connected,
QCheckBox#checkboxPlayer7Connected,
QCheckBox#checkboxPlayer8Connected {
spacing: 0px;
spacing: 0px;
}
QWidget#connectedControllers QLabel {
@@ -2427,7 +2412,7 @@ QCheckBox#checkboxPlayer7Connected::indicator,
QCheckBox#checkboxPlayer8Connected::indicator {
width: 14px;
height: 14px;
margin-left: 2px;
margin-left: 0px;
}
QWidget#Player1LEDs QCheckBox::indicator:checked,

View File

@@ -8,7 +8,7 @@ namespace Common {
PageTable::PageTable() = default;
PageTable::~PageTable() = default;
PageTable::~PageTable() noexcept = default;
void PageTable::Resize(std::size_t address_space_width_in_bits, std::size_t page_size_in_bits,
bool has_attribute) {

View File

@@ -4,9 +4,7 @@
#pragma once
#include <vector>
#include <boost/icl/interval_map.hpp>
#include <tuple>
#include "common/common_types.h"
#include "common/memory_hook.h"
@@ -51,13 +49,21 @@ struct SpecialRegion {
*/
struct PageTable {
PageTable();
~PageTable();
~PageTable() noexcept;
PageTable(const PageTable&) = delete;
PageTable& operator=(const PageTable&) = delete;
PageTable(PageTable&&) noexcept = default;
PageTable& operator=(PageTable&&) noexcept = default;
/**
* Resizes the page table to be able to accomodate enough pages within
* a given address space.
*
* @param address_space_width_in_bits The address size width in bits.
* @param page_size_in_bits The page size in bits.
* @param has_attribute Whether or not this page has any backing attributes.
*/
void Resize(std::size_t address_space_width_in_bits, std::size_t page_size_in_bits,
bool has_attribute);

View File

@@ -13,7 +13,7 @@
namespace Common {
void* AllocateMemoryPages(std::size_t size) {
void* AllocateMemoryPages(std::size_t size) noexcept {
#ifdef _WIN32
void* base{VirtualAlloc(nullptr, size, MEM_COMMIT, PAGE_READWRITE)};
#else
@@ -29,7 +29,7 @@ void* AllocateMemoryPages(std::size_t size) {
return base;
}
void FreeMemoryPages(void* base, [[maybe_unused]] std::size_t size) {
void FreeMemoryPages(void* base, [[maybe_unused]] std::size_t size) noexcept {
if (!base) {
return;
}

View File

@@ -4,25 +4,44 @@
#pragma once
#include "common/common_funcs.h"
#include <type_traits>
#include <utility>
namespace Common {
void* AllocateMemoryPages(std::size_t size);
void FreeMemoryPages(void* base, std::size_t size);
void* AllocateMemoryPages(std::size_t size) noexcept;
void FreeMemoryPages(void* base, std::size_t size) noexcept;
template <typename T>
class VirtualBuffer final : NonCopyable {
class VirtualBuffer final {
public:
static_assert(
std::is_trivially_constructible_v<T>,
"T must be trivially constructible, as non-trivial constructors will not be executed "
"with the current allocator");
constexpr VirtualBuffer() = default;
explicit VirtualBuffer(std::size_t count) : alloc_size{count * sizeof(T)} {
base_ptr = reinterpret_cast<T*>(AllocateMemoryPages(alloc_size));
}
~VirtualBuffer() {
~VirtualBuffer() noexcept {
FreeMemoryPages(base_ptr, alloc_size);
}
VirtualBuffer(const VirtualBuffer&) = delete;
VirtualBuffer& operator=(const VirtualBuffer&) = delete;
VirtualBuffer(VirtualBuffer&& other) noexcept
: alloc_size{std::exchange(other.alloc_size, 0)}, base_ptr{std::exchange(other.base_ptr),
nullptr} {}
VirtualBuffer& operator=(VirtualBuffer&& other) noexcept {
alloc_size = std::exchange(other.alloc_size, 0);
base_ptr = std::exchange(other.base_ptr, nullptr);
return *this;
}
void resize(std::size_t count) {
FreeMemoryPages(base_ptr, alloc_size);

View File

@@ -27,19 +27,19 @@ void DefaultControllerApplet::ReconfigureControllers(std::function<void()> callb
->GetAppletResource()
->GetController<Service::HID::Controller_NPad>(Service::HID::HidController::NPad);
auto& players = Settings::values.players;
auto& players = Settings::values.players.GetValue();
const std::size_t min_supported_players =
parameters.enable_single_mode ? 1 : parameters.min_players;
// Disconnect Handheld first.
npad.DisconnectNPadAtIndex(8);
npad.DisconnectNpadAtIndex(8);
// Deduce the best configuration based on the input parameters.
for (std::size_t index = 0; index < players.size() - 2; ++index) {
// First, disconnect all controllers regardless of the value of keep_controllers_connected.
// This makes it easy to connect the desired controllers.
npad.DisconnectNPadAtIndex(index);
npad.DisconnectNpadAtIndex(index);
// Only connect the minimum number of required players.
if (index >= min_supported_players) {
@@ -66,7 +66,7 @@ void DefaultControllerApplet::ReconfigureControllers(std::function<void()> callb
npad.MapSettingsTypeToNPad(Settings::ControllerType::RightJoycon), index);
}
} else if (index == 0 && parameters.enable_single_mode && parameters.allow_handheld &&
!Settings::values.use_docked_mode) {
!Settings::values.use_docked_mode.GetValue()) {
// We should *never* reach here under any normal circumstances.
npad.AddNewControllerAt(npad.MapSettingsTypeToNPad(Settings::ControllerType::Handheld),
index);

View File

@@ -47,7 +47,7 @@ FramebufferLayout DefaultFrameLayout(u32 width, u32 height) {
FramebufferLayout FrameLayoutFromResolutionScale(u32 res_scale) {
u32 width, height;
if (Settings::values.use_docked_mode) {
if (Settings::values.use_docked_mode.GetValue()) {
width = ScreenDocked::Width * res_scale;
height = ScreenDocked::Height * res_scale;
} else {

View File

@@ -33,7 +33,7 @@ public:
virtual bool GetAnalogDirectionStatus(AnalogDirection direction) const {
return {};
}
virtual bool SetRumblePlay(f32 amp_high, f32 amp_low, f32 freq_high, f32 freq_low) const {
virtual bool SetRumblePlay(f32 amp_low, f32 freq_low, f32 amp_high, f32 freq_high) const {
return {};
}
};
@@ -121,6 +121,13 @@ using ButtonDevice = InputDevice<bool>;
*/
using AnalogDevice = InputDevice<std::tuple<float, float>>;
/**
* A vibration device is an input device that returns an unsigned byte as status.
* It represents whether the vibration device supports vibration or not.
* If the status returns 1, it supports vibration. Otherwise, it does not support vibration.
*/
using VibrationDevice = InputDevice<u8>;
/**
* A motion status is an object that returns a tuple of accelerometer state vector,
* gyroscope state vector, rotation state vector and orientation state matrix.

View File

@@ -751,7 +751,7 @@ void ICommonStateGetter::GetDefaultDisplayResolution(Kernel::HLERequestContext&
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
if (Settings::values.use_docked_mode) {
if (Settings::values.use_docked_mode.GetValue()) {
rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedWidth) *
static_cast<u32>(Settings::values.resolution_factor.GetValue()));
rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedHeight) *
@@ -824,7 +824,7 @@ void IStorage::Open(Kernel::HLERequestContext& ctx) {
}
void ICommonStateGetter::GetOperationMode(Kernel::HLERequestContext& ctx) {
const bool use_docked_mode{Settings::values.use_docked_mode};
const bool use_docked_mode{Settings::values.use_docked_mode.GetValue()};
LOG_DEBUG(Service_AM, "called, use_docked_mode={}", use_docked_mode);
IPC::ResponseBuilder rb{ctx, 3};

View File

@@ -25,7 +25,7 @@ namespace Service::AM::Applets {
static Core::Frontend::ControllerParameters ConvertToFrontendParameters(
ControllerSupportArgPrivate private_arg, ControllerSupportArgHeader header, bool enable_text,
std::vector<IdentificationColor> identification_colors, std::vector<ExplainText> text) {
HID::Controller_NPad::NPadType npad_style_set;
HID::Controller_NPad::NpadStyleSet npad_style_set;
npad_style_set.raw = private_arg.style_set;
return {
@@ -222,7 +222,7 @@ void Controller::Execute() {
void Controller::ConfigurationComplete() {
ControllerSupportResultInfo result_info{};
const auto& players = Settings::values.players;
const auto& players = Settings::values.players.GetValue();
// If enable_single_mode is enabled, player_count is 1 regardless of any other parameters.
// Otherwise, only count connected players from P1-P8.

View File

@@ -69,7 +69,8 @@ void Controller::SetFromCpuBoostMode(CpuBoostMode mode) {
}
PerformanceMode Controller::GetCurrentPerformanceMode() const {
return Settings::values.use_docked_mode ? PerformanceMode::Docked : PerformanceMode::Handheld;
return Settings::values.use_docked_mode.GetValue() ? PerformanceMode::Docked
: PerformanceMode::Handheld;
}
PerformanceConfiguration Controller::GetCurrentPerformanceConfiguration(PerformanceMode mode) {

View File

@@ -117,7 +117,10 @@ u32 Controller_NPad::IndexToNPad(std::size_t index) {
}
Controller_NPad::Controller_NPad(Core::System& system) : ControllerBase(system), system(system) {}
Controller_NPad::~Controller_NPad() = default;
Controller_NPad::~Controller_NPad() {
OnRelease();
}
void Controller_NPad::InitNewlyAddedController(std::size_t controller_idx) {
const auto controller_type = connected_controllers[controller_idx].type;
@@ -139,7 +142,7 @@ void Controller_NPad::InitNewlyAddedController(std::size_t controller_idx) {
controller.properties.is_vertical.Assign(1);
controller.properties.use_plus.Assign(1);
controller.properties.use_minus.Assign(1);
controller.pad_assignment = NPadAssignments::Single;
controller.pad_assignment = NpadAssignments::Single;
break;
case NPadControllerType::Handheld:
controller.joy_styles.handheld.Assign(1);
@@ -147,7 +150,7 @@ void Controller_NPad::InitNewlyAddedController(std::size_t controller_idx) {
controller.properties.is_vertical.Assign(1);
controller.properties.use_plus.Assign(1);
controller.properties.use_minus.Assign(1);
controller.pad_assignment = NPadAssignments::Dual;
controller.pad_assignment = NpadAssignments::Dual;
break;
case NPadControllerType::JoyDual:
controller.joy_styles.joycon_dual.Assign(1);
@@ -156,26 +159,26 @@ void Controller_NPad::InitNewlyAddedController(std::size_t controller_idx) {
controller.properties.is_vertical.Assign(1);
controller.properties.use_plus.Assign(1);
controller.properties.use_minus.Assign(1);
controller.pad_assignment = NPadAssignments::Dual;
controller.pad_assignment = NpadAssignments::Dual;
break;
case NPadControllerType::JoyLeft:
controller.joy_styles.joycon_left.Assign(1);
controller.device_type.joycon_left.Assign(1);
controller.properties.is_horizontal.Assign(1);
controller.properties.use_minus.Assign(1);
controller.pad_assignment = NPadAssignments::Single;
controller.pad_assignment = NpadAssignments::Single;
break;
case NPadControllerType::JoyRight:
controller.joy_styles.joycon_right.Assign(1);
controller.device_type.joycon_right.Assign(1);
controller.properties.is_horizontal.Assign(1);
controller.properties.use_plus.Assign(1);
controller.pad_assignment = NPadAssignments::Single;
controller.pad_assignment = NpadAssignments::Single;
break;
case NPadControllerType::Pokeball:
controller.joy_styles.pokeball.Assign(1);
controller.device_type.pokeball.Assign(1);
controller.pad_assignment = NPadAssignments::Single;
controller.pad_assignment = NpadAssignments::Single;
break;
}
@@ -184,11 +187,14 @@ void Controller_NPad::InitNewlyAddedController(std::size_t controller_idx) {
controller.single_color.button_color = 0;
controller.dual_color_error = ColorReadError::ReadOk;
controller.left_color.body_color = Settings::values.players[controller_idx].body_color_left;
controller.left_color.button_color = Settings::values.players[controller_idx].button_color_left;
controller.right_color.body_color = Settings::values.players[controller_idx].body_color_right;
controller.left_color.body_color =
Settings::values.players.GetValue()[controller_idx].body_color_left;
controller.left_color.button_color =
Settings::values.players.GetValue()[controller_idx].button_color_left;
controller.right_color.body_color =
Settings::values.players.GetValue()[controller_idx].body_color_right;
controller.right_color.button_color =
Settings::values.players[controller_idx].button_color_right;
Settings::values.players.GetValue()[controller_idx].button_color_right;
controller.battery_level[0] = BATTERY_FULL;
controller.battery_level[1] = BATTERY_FULL;
@@ -199,7 +205,7 @@ void Controller_NPad::InitNewlyAddedController(std::size_t controller_idx) {
void Controller_NPad::OnInit() {
auto& kernel = system.Kernel();
for (std::size_t i = 0; i < styleset_changed_events.size(); i++) {
for (std::size_t i = 0; i < styleset_changed_events.size(); ++i) {
styleset_changed_events[i] = Kernel::WritableEvent::CreateEventPair(
kernel, fmt::format("npad:NpadStyleSetChanged_{}", i));
}
@@ -208,6 +214,8 @@ void Controller_NPad::OnInit() {
return;
}
OnLoadInputDevices();
if (style.raw == 0) {
// We want to support all controllers
style.handheld.Assign(1);
@@ -218,12 +226,27 @@ void Controller_NPad::OnInit() {
style.pokeball.Assign(1);
}
std::transform(Settings::values.players.begin(), Settings::values.players.end(),
connected_controllers.begin(), [](const Settings::PlayerInput& player) {
std::transform(Settings::values.players.GetValue().begin(),
Settings::values.players.GetValue().end(), connected_controllers.begin(),
[](const Settings::PlayerInput& player) {
return ControllerHolder{MapSettingsTypeToNPad(player.controller_type),
player.connected};
});
// Connect the Player 1 or Handheld controller if none are connected.
if (std::none_of(connected_controllers.begin(), connected_controllers.end(),
[](const ControllerHolder& controller) { return controller.is_connected; })) {
const auto controller =
MapSettingsTypeToNPad(Settings::values.players.GetValue()[0].controller_type);
if (controller == NPadControllerType::Handheld) {
Settings::values.players.GetValue()[HANDHELD_INDEX].connected = true;
connected_controllers[HANDHELD_INDEX] = {controller, true};
} else {
Settings::values.players.GetValue()[0].connected = true;
connected_controllers[0] = {controller, true};
}
}
// Account for handheld
if (connected_controllers[HANDHELD_INDEX].is_connected) {
connected_controllers[HANDHELD_INDEX].type = NPadControllerType::Handheld;
@@ -242,7 +265,7 @@ void Controller_NPad::OnInit() {
}
void Controller_NPad::OnLoadInputDevices() {
const auto& players = Settings::values.players;
const auto& players = Settings::values.players.GetValue();
for (std::size_t i = 0; i < players.size(); ++i) {
std::transform(players[i].buttons.begin() + Settings::NativeButton::BUTTON_HID_BEGIN,
players[i].buttons.begin() + Settings::NativeButton::BUTTON_HID_END,
@@ -250,13 +273,26 @@ void Controller_NPad::OnLoadInputDevices() {
std::transform(players[i].analogs.begin() + Settings::NativeAnalog::STICK_HID_BEGIN,
players[i].analogs.begin() + Settings::NativeAnalog::STICK_HID_END,
sticks[i].begin(), Input::CreateDevice<Input::AnalogDevice>);
std::transform(players[i].vibrations.begin() +
Settings::NativeVibration::VIBRATION_HID_BEGIN,
players[i].vibrations.begin() + Settings::NativeVibration::VIBRATION_HID_END,
vibrations[i].begin(), Input::CreateDevice<Input::VibrationDevice>);
std::transform(players[i].motions.begin() + Settings::NativeMotion::MOTION_HID_BEGIN,
players[i].motions.begin() + Settings::NativeMotion::MOTION_HID_END,
motions[i].begin(), Input::CreateDevice<Input::MotionDevice>);
for (std::size_t device_idx = 0; device_idx < vibrations[i].size(); ++device_idx) {
InitializeVibrationDeviceAtIndex(i, device_idx);
}
}
}
void Controller_NPad::OnRelease() {}
void Controller_NPad::OnRelease() {
for (std::size_t npad_idx = 0; npad_idx < vibrations.size(); ++npad_idx) {
for (std::size_t device_idx = 0; device_idx < vibrations[npad_idx].size(); ++device_idx) {
VibrateControllerAtIndex(npad_idx, device_idx, {});
}
}
}
void Controller_NPad::RequestPadStateUpdate(u32 npad_id) {
const auto controller_idx = NPadIdToIndex(npad_id);
@@ -339,7 +375,7 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8*
if (!IsControllerActivated()) {
return;
}
for (std::size_t i = 0; i < shared_memory_entries.size(); i++) {
for (std::size_t i = 0; i < shared_memory_entries.size(); ++i) {
auto& npad = shared_memory_entries[i];
const std::array<NPadGeneric*, 7> controller_npads{&npad.main_controller_states,
&npad.handheld_states,
@@ -481,7 +517,7 @@ void Controller_NPad::OnMotionUpdate(const Core::Timing::CoreTiming& core_timing
if (!IsControllerActivated()) {
return;
}
for (std::size_t i = 0; i < shared_memory_entries.size(); i++) {
for (std::size_t i = 0; i < shared_memory_entries.size(); ++i) {
auto& npad = shared_memory_entries[i];
const auto& controller_type = connected_controllers[i].type;
@@ -515,7 +551,7 @@ void Controller_NPad::OnMotionUpdate(const Core::Timing::CoreTiming& core_timing
// Try to read sixaxis sensor states
std::array<MotionDevice, 2> motion_devices;
if (sixaxis_sensors_enabled && Settings::values.motion_enabled) {
if (sixaxis_sensors_enabled && Settings::values.motion_enabled.GetValue()) {
sixaxis_at_rest = true;
for (std::size_t e = 0; e < motion_devices.size(); ++e) {
const auto& device = motions[i][e];
@@ -601,15 +637,15 @@ void Controller_NPad::OnMotionUpdate(const Core::Timing::CoreTiming& core_timing
shared_memory_entries.size() * sizeof(NPadEntry));
}
void Controller_NPad::SetSupportedStyleSet(NPadType style_set) {
void Controller_NPad::SetSupportedStyleSet(NpadStyleSet style_set) {
style.raw = style_set.raw;
}
Controller_NPad::NPadType Controller_NPad::GetSupportedStyleSet() const {
Controller_NPad::NpadStyleSet Controller_NPad::GetSupportedStyleSet() const {
return style;
}
void Controller_NPad::SetSupportedNPadIdTypes(u8* data, std::size_t length) {
void Controller_NPad::SetSupportedNpadIdTypes(u8* data, std::size_t length) {
ASSERT(length > 0 && (length % sizeof(u32)) == 0);
supported_npad_id_types.clear();
supported_npad_id_types.resize(length / sizeof(u32));
@@ -621,7 +657,7 @@ void Controller_NPad::GetSupportedNpadIdTypes(u32* data, std::size_t max_length)
std::memcpy(data, supported_npad_id_types.data(), supported_npad_id_types.size());
}
std::size_t Controller_NPad::GetSupportedNPadIdTypesSize() const {
std::size_t Controller_NPad::GetSupportedNpadIdTypesSize() const {
return supported_npad_id_types.size();
}
@@ -641,7 +677,7 @@ Controller_NPad::NpadHandheldActivationMode Controller_NPad::GetNpadHandheldActi
return handheld_activation_mode;
}
void Controller_NPad::SetNpadMode(u32 npad_id, NPadAssignments assignment_mode) {
void Controller_NPad::SetNpadMode(u32 npad_id, NpadAssignments assignment_mode) {
const std::size_t npad_index = NPadIdToIndex(npad_id);
ASSERT(npad_index < shared_memory_entries.size());
if (shared_memory_entries[npad_index].pad_assignment != assignment_mode) {
@@ -649,35 +685,140 @@ void Controller_NPad::SetNpadMode(u32 npad_id, NPadAssignments assignment_mode)
}
}
void Controller_NPad::VibrateController(const std::vector<u32>& controllers,
const std::vector<Vibration>& vibrations) {
LOG_TRACE(Service_HID, "called");
bool Controller_NPad::VibrateControllerAtIndex(std::size_t npad_index, std::size_t device_index,
const VibrationValue& vibration_value) {
if (!connected_controllers[npad_index].is_connected || !vibrations[npad_index][device_index]) {
return false;
}
if (!Settings::values.vibration_enabled || !can_controllers_vibrate) {
const auto& player = Settings::values.players.GetValue()[npad_index];
if (!player.vibration_enabled) {
if (latest_vibration_values[npad_index][device_index].amp_low != 0.0f ||
latest_vibration_values[npad_index][device_index].amp_high != 0.0f) {
// Send an empty vibration to stop any vibrations.
vibrations[npad_index][device_index]->SetRumblePlay(0.0f, 160.0f, 0.0f, 320.0f);
// Then reset the vibration value to its default value.
latest_vibration_values[npad_index][device_index] = {};
}
return false;
}
if (!Settings::values.enable_accurate_vibrations.GetValue()) {
using std::chrono::duration_cast;
using std::chrono::milliseconds;
using std::chrono::steady_clock;
const auto now = steady_clock::now();
// Filter out non-zero vibrations that are within 10ms of each other.
if ((vibration_value.amp_low != 0.0f || vibration_value.amp_high != 0.0f) &&
duration_cast<milliseconds>(now - last_vibration_timepoints[npad_index][device_index]) <
milliseconds(10)) {
return false;
}
last_vibration_timepoints[npad_index][device_index] = now;
}
auto& vibration = vibrations[npad_index][device_index];
const auto player_vibration_strength = static_cast<f32>(player.vibration_strength);
const auto amp_low =
std::min(vibration_value.amp_low * player_vibration_strength / 100.0f, 1.0f);
const auto amp_high =
std::min(vibration_value.amp_high * player_vibration_strength / 100.0f, 1.0f);
return vibration->SetRumblePlay(amp_low, vibration_value.freq_low, amp_high,
vibration_value.freq_high);
}
void Controller_NPad::VibrateController(const DeviceHandle& vibration_device_handle,
const VibrationValue& vibration_value) {
if (!Settings::values.vibration_enabled.GetValue() && !permit_vibration_session_enabled) {
return;
}
bool success = true;
for (std::size_t i = 0; i < controllers.size(); ++i) {
if (!connected_controllers[i].is_connected) {
continue;
}
using namespace Settings::NativeButton;
const auto& button_state = buttons[i];
if (button_state[A - BUTTON_HID_BEGIN]) {
if (button_state[A - BUTTON_HID_BEGIN]->SetRumblePlay(
vibrations[0].amp_high, vibrations[0].amp_low, vibrations[0].freq_high,
vibrations[0].freq_low)) {
success = false;
}
}
const auto npad_index = NPadIdToIndex(vibration_device_handle.npad_id);
const auto device_index = static_cast<std::size_t>(vibration_device_handle.device_index);
if (!vibration_devices_mounted[npad_index][device_index] ||
!connected_controllers[npad_index].is_connected) {
return;
}
if (success) {
last_processed_vibration = vibrations.back();
if (vibration_device_handle.device_index == DeviceIndex::None) {
UNREACHABLE_MSG("DeviceIndex should never be None!");
return;
}
// Some games try to send mismatched parameters in the device handle, block these.
if ((connected_controllers[npad_index].type == NPadControllerType::JoyLeft &&
(vibration_device_handle.npad_type == NpadType::JoyconRight ||
vibration_device_handle.device_index == DeviceIndex::Right)) ||
(connected_controllers[npad_index].type == NPadControllerType::JoyRight &&
(vibration_device_handle.npad_type == NpadType::JoyconLeft ||
vibration_device_handle.device_index == DeviceIndex::Left))) {
return;
}
// Filter out vibrations with equivalent values to reduce unnecessary state changes.
if (vibration_value.amp_low == latest_vibration_values[npad_index][device_index].amp_low &&
vibration_value.amp_high == latest_vibration_values[npad_index][device_index].amp_high) {
return;
}
if (VibrateControllerAtIndex(npad_index, device_index, vibration_value)) {
latest_vibration_values[npad_index][device_index] = vibration_value;
}
}
Controller_NPad::Vibration Controller_NPad::GetLastVibration() const {
return last_processed_vibration;
void Controller_NPad::VibrateControllers(const std::vector<DeviceHandle>& vibration_device_handles,
const std::vector<VibrationValue>& vibration_values) {
if (!Settings::values.vibration_enabled.GetValue() && !permit_vibration_session_enabled) {
return;
}
ASSERT_OR_EXECUTE_MSG(
vibration_device_handles.size() == vibration_values.size(), { return; },
"The amount of device handles does not match with the amount of vibration values,"
"this is undefined behavior!");
for (std::size_t i = 0; i < vibration_device_handles.size(); ++i) {
VibrateController(vibration_device_handles[i], vibration_values[i]);
}
}
Controller_NPad::VibrationValue Controller_NPad::GetLastVibration(
const DeviceHandle& vibration_device_handle) const {
const auto npad_index = NPadIdToIndex(vibration_device_handle.npad_id);
const auto device_index = static_cast<std::size_t>(vibration_device_handle.device_index);
return latest_vibration_values[npad_index][device_index];
}
void Controller_NPad::InitializeVibrationDevice(const DeviceHandle& vibration_device_handle) {
const auto npad_index = NPadIdToIndex(vibration_device_handle.npad_id);
const auto device_index = static_cast<std::size_t>(vibration_device_handle.device_index);
InitializeVibrationDeviceAtIndex(npad_index, device_index);
}
void Controller_NPad::InitializeVibrationDeviceAtIndex(std::size_t npad_index,
std::size_t device_index) {
if (vibrations[npad_index][device_index]) {
vibration_devices_mounted[npad_index][device_index] =
vibrations[npad_index][device_index]->GetStatus() == 1;
} else {
vibration_devices_mounted[npad_index][device_index] = false;
}
}
void Controller_NPad::SetPermitVibrationSession(bool permit_vibration_session) {
permit_vibration_session_enabled = permit_vibration_session;
}
bool Controller_NPad::IsVibrationDeviceMounted(const DeviceHandle& vibration_device_handle) const {
const auto npad_index = NPadIdToIndex(vibration_device_handle.npad_id);
const auto device_index = static_cast<std::size_t>(vibration_device_handle.device_index);
return vibration_devices_mounted[npad_index][device_index];
}
std::shared_ptr<Kernel::ReadableEvent> Controller_NPad::GetStyleSetChangedEvent(u32 npad_id) const {
@@ -696,31 +837,38 @@ void Controller_NPad::AddNewControllerAt(NPadControllerType controller, std::siz
void Controller_NPad::UpdateControllerAt(NPadControllerType controller, std::size_t npad_index,
bool connected) {
if (!connected) {
DisconnectNPadAtIndex(npad_index);
DisconnectNpadAtIndex(npad_index);
return;
}
if (controller == NPadControllerType::Handheld) {
Settings::values.players[HANDHELD_INDEX].controller_type =
Settings::values.players.GetValue()[HANDHELD_INDEX].controller_type =
MapNPadToSettingsType(controller);
Settings::values.players[HANDHELD_INDEX].connected = true;
Settings::values.players.GetValue()[HANDHELD_INDEX].connected = true;
connected_controllers[HANDHELD_INDEX] = {controller, true};
InitNewlyAddedController(HANDHELD_INDEX);
return;
}
Settings::values.players[npad_index].controller_type = MapNPadToSettingsType(controller);
Settings::values.players[npad_index].connected = true;
Settings::values.players.GetValue()[npad_index].controller_type =
MapNPadToSettingsType(controller);
Settings::values.players.GetValue()[npad_index].connected = true;
connected_controllers[npad_index] = {controller, true};
InitNewlyAddedController(npad_index);
}
void Controller_NPad::DisconnectNPad(u32 npad_id) {
DisconnectNPadAtIndex(NPadIdToIndex(npad_id));
void Controller_NPad::DisconnectNpad(u32 npad_id) {
DisconnectNpadAtIndex(NPadIdToIndex(npad_id));
}
void Controller_NPad::DisconnectNPadAtIndex(std::size_t npad_index) {
Settings::values.players[npad_index].connected = false;
void Controller_NPad::DisconnectNpadAtIndex(std::size_t npad_index) {
for (std::size_t device_idx = 0; device_idx < vibrations[npad_index].size(); ++device_idx) {
// Send an empty vibration to stop any vibrations.
VibrateControllerAtIndex(npad_index, device_idx, {});
vibration_devices_mounted[npad_index][device_idx] = false;
}
Settings::values.players.GetValue()[npad_index].connected = false;
connected_controllers[npad_index].is_connected = false;
auto& controller = shared_memory_entries[npad_index];
@@ -758,7 +906,7 @@ void Controller_NPad::MergeSingleJoyAsDualJoy(u32 npad_id_1, u32 npad_id_2) {
(connected_controllers[npad_index_2].type == NPadControllerType::JoyLeft &&
connected_controllers[npad_index_1].type == NPadControllerType::JoyRight)) {
// Disconnect the joycon at the second id and connect the dual joycon at the first index.
DisconnectNPad(npad_id_2);
DisconnectNpad(npad_id_2);
AddNewControllerAt(NPadControllerType::JoyDual, npad_index_1);
}
}
@@ -830,14 +978,6 @@ void Controller_NPad::SetUnintendedHomeButtonInputProtectionEnabled(bool is_prot
unintended_home_button_input_protection[NPadIdToIndex(npad_id)] = is_protection_enabled;
}
void Controller_NPad::SetVibrationEnabled(bool can_vibrate) {
can_controllers_vibrate = can_vibrate;
}
bool Controller_NPad::IsVibrationEnabled() const {
return can_controllers_vibrate;
}
void Controller_NPad::ClearAllConnectedControllers() {
for (auto& controller : connected_controllers) {
if (controller.is_connected && controller.type != NPadControllerType::None) {
@@ -882,7 +1022,7 @@ bool Controller_NPad::IsControllerSupported(NPadControllerType controller) const
return false;
}
// Handheld should not be supported in docked mode
if (Settings::values.use_docked_mode) {
if (Settings::values.use_docked_mode.GetValue()) {
return false;
}

View File

@@ -39,28 +39,30 @@ public:
// Called when input devices should be loaded
void OnLoadInputDevices() override;
struct NPadType {
union {
u32_le raw{};
BitField<0, 1, u32> pro_controller;
BitField<1, 1, u32> handheld;
BitField<2, 1, u32> joycon_dual;
BitField<3, 1, u32> joycon_left;
BitField<4, 1, u32> joycon_right;
BitField<6, 1, u32> pokeball; // TODO(ogniK): Confirm when possible
};
enum class NPadControllerType {
None,
ProController,
Handheld,
JoyDual,
JoyLeft,
JoyRight,
Pokeball,
};
static_assert(sizeof(NPadType) == 4, "NPadType is an invalid size");
struct Vibration {
f32 amp_low;
f32 freq_low;
f32 amp_high;
f32 freq_high;
enum class NpadType : u8 {
ProController = 3,
Handheld = 4,
JoyconDual = 5,
JoyconLeft = 6,
JoyconRight = 7,
Pokeball = 9,
};
enum class DeviceIndex : u8 {
Left = 0,
Right = 1,
None = 2,
};
static_assert(sizeof(Vibration) == 0x10, "Vibration is an invalid size");
enum class GyroscopeZeroDriftMode : u32 {
Loose = 0,
@@ -73,7 +75,7 @@ public:
Horizontal = 1,
};
enum class NPadAssignments : u32_le {
enum class NpadAssignments : u32 {
Dual = 0,
Single = 1,
};
@@ -84,15 +86,36 @@ public:
None = 2,
};
enum class NPadControllerType {
None,
ProController,
Handheld,
JoyDual,
JoyLeft,
JoyRight,
Pokeball,
struct DeviceHandle {
NpadType npad_type{};
u8 npad_id{};
DeviceIndex device_index{};
INSERT_PADDING_BYTES(1);
};
static_assert(sizeof(DeviceHandle) == 4, "DeviceHandle is an invalid size");
struct NpadStyleSet {
union {
u32_le raw{};
BitField<0, 1, u32> pro_controller;
BitField<1, 1, u32> handheld;
BitField<2, 1, u32> joycon_dual;
BitField<3, 1, u32> joycon_left;
BitField<4, 1, u32> joycon_right;
BitField<6, 1, u32> pokeball; // TODO(ogniK): Confirm when possible
};
};
static_assert(sizeof(NpadStyleSet) == 4, "NpadStyleSet is an invalid size");
struct VibrationValue {
f32 amp_low{0.0f};
f32 freq_low{160.0f};
f32 amp_high{0.0f};
f32 freq_high{320.0f};
};
static_assert(sizeof(VibrationValue) == 0x10, "Vibration is an invalid size");
struct LedPattern {
explicit LedPattern(u64 light1, u64 light2, u64 light3, u64 light4) {
@@ -110,12 +133,12 @@ public:
};
};
void SetSupportedStyleSet(NPadType style_set);
NPadType GetSupportedStyleSet() const;
void SetSupportedStyleSet(NpadStyleSet style_set);
NpadStyleSet GetSupportedStyleSet() const;
void SetSupportedNPadIdTypes(u8* data, std::size_t length);
void SetSupportedNpadIdTypes(u8* data, std::size_t length);
void GetSupportedNpadIdTypes(u32* data, std::size_t max_length);
std::size_t GetSupportedNPadIdTypesSize() const;
std::size_t GetSupportedNpadIdTypesSize() const;
void SetHoldType(NpadHoldType joy_hold_type);
NpadHoldType GetHoldType() const;
@@ -123,12 +146,26 @@ public:
void SetNpadHandheldActivationMode(NpadHandheldActivationMode activation_mode);
NpadHandheldActivationMode GetNpadHandheldActivationMode() const;
void SetNpadMode(u32 npad_id, NPadAssignments assignment_mode);
void SetNpadMode(u32 npad_id, NpadAssignments assignment_mode);
void VibrateController(const std::vector<u32>& controllers,
const std::vector<Vibration>& vibrations);
bool VibrateControllerAtIndex(std::size_t npad_index, std::size_t device_index,
const VibrationValue& vibration_value);
Vibration GetLastVibration() const;
void VibrateController(const DeviceHandle& vibration_device_handle,
const VibrationValue& vibration_value);
void VibrateControllers(const std::vector<DeviceHandle>& vibration_device_handles,
const std::vector<VibrationValue>& vibration_values);
VibrationValue GetLastVibration(const DeviceHandle& vibration_device_handle) const;
void InitializeVibrationDevice(const DeviceHandle& vibration_device_handle);
void InitializeVibrationDeviceAtIndex(std::size_t npad_index, std::size_t device_index);
void SetPermitVibrationSession(bool permit_vibration_session);
bool IsVibrationDeviceMounted(const DeviceHandle& vibration_device_handle) const;
std::shared_ptr<Kernel::ReadableEvent> GetStyleSetChangedEvent(u32 npad_id) const;
void SignalStyleSetChangedEvent(u32 npad_id) const;
@@ -138,8 +175,8 @@ public:
// Adds a new controller at an index with connection status.
void UpdateControllerAt(NPadControllerType controller, std::size_t npad_index, bool connected);
void DisconnectNPad(u32 npad_id);
void DisconnectNPadAtIndex(std::size_t index);
void DisconnectNpad(u32 npad_id);
void DisconnectNpadAtIndex(std::size_t index);
void SetGyroscopeZeroDriftMode(GyroscopeZeroDriftMode drift_mode);
GyroscopeZeroDriftMode GetGyroscopeZeroDriftMode() const;
@@ -148,8 +185,6 @@ public:
LedPattern GetLedPattern(u32 npad_id);
bool IsUnintendedHomeButtonInputProtectionEnabled(u32 npad_id) const;
void SetUnintendedHomeButtonInputProtectionEnabled(bool is_protection_enabled, u32 npad_id);
void SetVibrationEnabled(bool can_vibrate);
bool IsVibrationEnabled() const;
void ClearAllConnectedControllers();
void DisconnectAllConnectedControllers();
void ConnectAllDisconnectedControllers();
@@ -324,8 +359,8 @@ private:
};
struct NPadEntry {
NPadType joy_styles;
NPadAssignments pad_assignment;
NpadStyleSet joy_styles;
NpadAssignments pad_assignment;
ColorReadError single_color_error;
ControllerColor single_color;
@@ -368,7 +403,7 @@ private:
u32 press_state{};
NPadType style{};
NpadStyleSet style{};
std::array<NPadEntry, 10> shared_memory_entries{};
using ButtonArray = std::array<
std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeButton::NUM_BUTTONS_HID>,
@@ -376,22 +411,28 @@ private:
using StickArray = std::array<
std::array<std::unique_ptr<Input::AnalogDevice>, Settings::NativeAnalog::NUM_STICKS_HID>,
10>;
using VibrationArray = std::array<std::array<std::unique_ptr<Input::VibrationDevice>,
Settings::NativeVibration::NUM_VIBRATIONS_HID>,
10>;
using MotionArray = std::array<
std::array<std::unique_ptr<Input::MotionDevice>, Settings::NativeMotion::NUM_MOTION_HID>,
std::array<std::unique_ptr<Input::MotionDevice>, Settings::NativeMotion::NUM_MOTIONS_HID>,
10>;
ButtonArray buttons;
StickArray sticks;
VibrationArray vibrations;
MotionArray motions;
std::vector<u32> supported_npad_id_types{};
NpadHoldType hold_type{NpadHoldType::Vertical};
NpadHandheldActivationMode handheld_activation_mode{NpadHandheldActivationMode::Dual};
// Each controller should have their own styleset changed event
std::array<Kernel::EventPair, 10> styleset_changed_events;
Vibration last_processed_vibration{};
std::array<std::array<std::chrono::steady_clock::time_point, 2>, 10> last_vibration_timepoints;
std::array<std::array<VibrationValue, 2>, 10> latest_vibration_values{};
bool permit_vibration_session_enabled{false};
std::array<std::array<bool, 2>, 10> vibration_devices_mounted{};
std::array<ControllerHolder, 10> connected_controllers{};
std::array<bool, 10> unintended_home_button_input_protection{};
GyroscopeZeroDriftMode gyroscope_zero_drift_mode{GyroscopeZeroDriftMode::Standard};
bool can_controllers_vibrate{true};
bool sixaxis_sensors_enabled{true};
bool sixaxis_at_rest{true};
std::array<ControllerPad, 10> npad_pad_states{};

File diff suppressed because it is too large Load Diff

View File

@@ -86,17 +86,15 @@ public:
private:
void CreateAppletResource(Kernel::HLERequestContext& ctx);
void ActivateXpad(Kernel::HLERequestContext& ctx);
void GetXpadIDs(Kernel::HLERequestContext& ctx);
void ActivateSixAxisSensor(Kernel::HLERequestContext& ctx);
void DeactivateSixAxisSensor(Kernel::HLERequestContext& ctx);
void ActivateDebugPad(Kernel::HLERequestContext& ctx);
void ActivateTouchScreen(Kernel::HLERequestContext& ctx);
void ActivateMouse(Kernel::HLERequestContext& ctx);
void ActivateKeyboard(Kernel::HLERequestContext& ctx);
void SendKeyboardLockKeyEvent(Kernel::HLERequestContext& ctx);
void ActivateGesture(Kernel::HLERequestContext& ctx);
void ActivateNpadWithRevision(Kernel::HLERequestContext& ctx);
void ActivateXpad(Kernel::HLERequestContext& ctx);
void GetXpadIDs(Kernel::HLERequestContext& ctx);
void ActivateSixAxisSensor(Kernel::HLERequestContext& ctx);
void DeactivateSixAxisSensor(Kernel::HLERequestContext& ctx);
void StartSixAxisSensor(Kernel::HLERequestContext& ctx);
void StopSixAxisSensor(Kernel::HLERequestContext& ctx);
void EnableSixAxisSensorFusion(Kernel::HLERequestContext& ctx);
@@ -104,6 +102,7 @@ private:
void GetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx);
void ResetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx);
void IsSixAxisSensorAtRest(Kernel::HLERequestContext& ctx);
void ActivateGesture(Kernel::HLERequestContext& ctx);
void SetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx);
void GetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx);
void SetSupportedNpadIdType(Kernel::HLERequestContext& ctx);
@@ -112,6 +111,7 @@ private:
void AcquireNpadStyleSetUpdateEventHandle(Kernel::HLERequestContext& ctx);
void DisconnectNpad(Kernel::HLERequestContext& ctx);
void GetPlayerLedPattern(Kernel::HLERequestContext& ctx);
void ActivateNpadWithRevision(Kernel::HLERequestContext& ctx);
void SetNpadJoyHoldType(Kernel::HLERequestContext& ctx);
void GetNpadJoyHoldType(Kernel::HLERequestContext& ctx);
void SetNpadJoyAssignmentModeSingleByDefault(Kernel::HLERequestContext& ctx);
@@ -125,15 +125,16 @@ private:
void SwapNpadAssignment(Kernel::HLERequestContext& ctx);
void IsUnintendedHomeButtonInputProtectionEnabled(Kernel::HLERequestContext& ctx);
void EnableUnintendedHomeButtonInputProtection(Kernel::HLERequestContext& ctx);
void BeginPermitVibrationSession(Kernel::HLERequestContext& ctx);
void EndPermitVibrationSession(Kernel::HLERequestContext& ctx);
void SendVibrationValue(Kernel::HLERequestContext& ctx);
void SendVibrationValues(Kernel::HLERequestContext& ctx);
void GetActualVibrationValue(Kernel::HLERequestContext& ctx);
void GetVibrationDeviceInfo(Kernel::HLERequestContext& ctx);
void SendVibrationValue(Kernel::HLERequestContext& ctx);
void GetActualVibrationValue(Kernel::HLERequestContext& ctx);
void CreateActiveVibrationDeviceList(Kernel::HLERequestContext& ctx);
void PermitVibration(Kernel::HLERequestContext& ctx);
void IsVibrationPermitted(Kernel::HLERequestContext& ctx);
void SendVibrationValues(Kernel::HLERequestContext& ctx);
void BeginPermitVibrationSession(Kernel::HLERequestContext& ctx);
void EndPermitVibrationSession(Kernel::HLERequestContext& ctx);
void IsVibrationDeviceMounted(Kernel::HLERequestContext& ctx);
void ActivateConsoleSixAxisSensor(Kernel::HLERequestContext& ctx);
void StartConsoleSixAxisSensor(Kernel::HLERequestContext& ctx);
void StopConsoleSixAxisSensor(Kernel::HLERequestContext& ctx);
@@ -146,6 +147,22 @@ private:
void SetIsPalmaAllConnectable(Kernel::HLERequestContext& ctx);
void SetPalmaBoostMode(Kernel::HLERequestContext& ctx);
enum class VibrationDeviceType : u32 {
LinearResonantActuator = 1,
};
enum class VibrationDevicePosition : u32 {
None = 0,
Left = 1,
Right = 2,
};
struct VibrationDeviceInfo {
VibrationDeviceType type{};
VibrationDevicePosition position{};
};
static_assert(sizeof(VibrationDeviceInfo) == 0x8, "VibrationDeviceInfo has incorrect size.");
std::shared_ptr<IAppletResource> applet_resource;
Core::System& system;
};

View File

@@ -771,7 +771,7 @@ private:
IPC::ResponseBuilder rb{ctx, 6};
rb.Push(RESULT_SUCCESS);
if (Settings::values.use_docked_mode) {
if (Settings::values.use_docked_mode.GetValue()) {
rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedWidth) *
static_cast<u32>(Settings::values.resolution_factor.GetValue()));
rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedHeight) *

View File

@@ -49,7 +49,7 @@ void LogSettings() {
};
LOG_INFO(Config, "yuzu Configuration:");
log_setting("Controls_UseDockedMode", values.use_docked_mode);
log_setting("Controls_UseDockedMode", values.use_docked_mode.GetValue());
log_setting("System_RngSeed", values.rng_seed.GetValue().value_or(0));
log_setting("System_CurrentUser", values.current_user);
log_setting("System_LanguageIndex", values.language_index.GetValue());
@@ -145,6 +145,12 @@ void RestoreGlobalState() {
values.rng_seed.SetGlobal(true);
values.custom_rtc.SetGlobal(true);
values.sound_index.SetGlobal(true);
// Controls
values.players.SetGlobal(true);
values.use_docked_mode.SetGlobal(true);
values.vibration_enabled.SetGlobal(true);
values.motion_enabled.SetGlobal(true);
}
void Sanitize() {

View File

@@ -65,6 +65,38 @@ private:
Type local{};
};
/**
* The InputSetting class allows for getting a reference to either the global or local members.
* This is required as we cannot easily modify the values of user-defined types within containers
* using the SetValue() member function found in the Setting class. The primary purpose of this
* class is to store an array of 10 PlayerInput structs for both the global and local (per-game)
* setting and allows for easily accessing and modifying both settings.
*/
template <typename Type>
class InputSetting final {
public:
InputSetting() = default;
explicit InputSetting(Type val) : global{val} {}
~InputSetting() = default;
void SetGlobal(bool to_global) {
use_global = to_global;
}
bool UsingGlobal() const {
return use_global;
}
Type& GetValue(bool need_global = false) {
if (use_global || need_global) {
return global;
}
return local;
}
private:
bool use_global = true;
Type global{};
Type local{};
};
struct TouchFromButtonMap {
std::string name;
std::vector<std::string> buttons;
@@ -133,9 +165,18 @@ struct Values {
Setting<s32> sound_index;
// Controls
std::array<PlayerInput, 10> players;
InputSetting<std::array<PlayerInput, 10>> players;
bool use_docked_mode;
Setting<bool> use_docked_mode;
Setting<bool> vibration_enabled;
Setting<bool> enable_accurate_vibrations;
Setting<bool> motion_enabled;
std::string motion_device;
std::string udp_input_address;
u16 udp_input_port;
u8 udp_pad_index;
bool mouse_enabled;
std::string mouse_device;
@@ -149,20 +190,15 @@ struct Values {
ButtonsRaw debug_pad_buttons;
AnalogsRaw debug_pad_analogs;
bool vibration_enabled;
bool motion_enabled;
std::string motion_device;
std::string touch_device;
TouchscreenInput touchscreen;
std::atomic_bool is_device_reload_pending{true};
bool use_touch_from_button;
std::string touch_device;
int touch_from_button_map_index;
std::string udp_input_address;
u16 udp_input_port;
u8 udp_pad_index;
std::vector<TouchFromButtonMap> touch_from_button_maps;
std::atomic_bool is_device_reload_pending{true};
// Data Storage
bool use_virtual_sd;
bool gamecard_inserted;

View File

@@ -213,7 +213,7 @@ void TelemetrySession::AddInitialInfo(Loader::AppLoader& app_loader) {
Settings::values.use_assembly_shaders.GetValue());
AddField(field_type, "Renderer_UseAsynchronousShaders",
Settings::values.use_asynchronous_shaders.GetValue());
AddField(field_type, "System_UseDockedMode", Settings::values.use_docked_mode);
AddField(field_type, "System_UseDockedMode", Settings::values.use_docked_mode.GetValue());
}
bool TelemetrySession::SubmitTestcase() {

View File

@@ -230,10 +230,8 @@ void Adapter::SendVibrations() {
vibration_changed = false;
}
bool Adapter::RumblePlay(std::size_t port, f32 amplitude) {
amplitude = std::clamp(amplitude, 0.0f, 1.0f);
const auto raw_amp = static_cast<u8>(amplitude * 0x8);
pads[port].rumble_amplitude = raw_amp;
bool Adapter::RumblePlay(std::size_t port, u8 amplitude) {
pads[port].rumble_amplitude = amplitude;
return rumble_enabled;
}

View File

@@ -77,8 +77,8 @@ public:
Adapter();
~Adapter();
/// Request a vibration for a controlelr
bool RumblePlay(std::size_t port, f32 amplitude);
/// Request a vibration for a controller
bool RumblePlay(std::size_t port, u8 amplitude);
/// Used for polling
void BeginConfiguration();

View File

@@ -15,7 +15,7 @@ namespace InputCommon {
class GCButton final : public Input::ButtonDevice {
public:
explicit GCButton(u32 port_, s32 button_, GCAdapter::Adapter* adapter)
explicit GCButton(u32 port_, s32 button_, const GCAdapter::Adapter* adapter)
: port(port_), button(button_), gcadapter(adapter) {}
~GCButton() override;
@@ -27,18 +27,10 @@ public:
return false;
}
bool SetRumblePlay(f32 amp_high, f32 amp_low, f32 freq_high, f32 freq_low) const override {
const float amplitude = amp_high + amp_low > 2.0f ? 1.0f : (amp_high + amp_low) * 0.5f;
const auto new_amp =
static_cast<f32>(pow(amplitude, 0.5f) * (3.0f - 2.0f * pow(amplitude, 0.15f)));
return gcadapter->RumblePlay(port, new_amp);
}
private:
const u32 port;
const s32 button;
GCAdapter::Adapter* gcadapter;
const GCAdapter::Adapter* gcadapter;
};
class GCAxisButton final : public Input::ButtonDevice {
@@ -299,4 +291,42 @@ Common::ParamPackage GCAnalogFactory::GetNextInput() {
return params;
}
class GCVibration final : public Input::VibrationDevice {
public:
explicit GCVibration(u32 port_, GCAdapter::Adapter* adapter)
: port(port_), gcadapter(adapter) {}
u8 GetStatus() const override {
return gcadapter->RumblePlay(port, 0);
}
bool SetRumblePlay(f32 amp_low, f32 freq_low, f32 amp_high, f32 freq_high) const override {
const auto mean_amplitude = (amp_low + amp_high) * 0.5f;
const auto processed_amplitude = static_cast<u8>(
pow(mean_amplitude, 0.5f) * (3.0f - 2.0f * pow(mean_amplitude, 0.15f)) * 0x8);
return gcadapter->RumblePlay(port, processed_amplitude);
}
private:
const u32 port;
GCAdapter::Adapter* gcadapter;
};
/// An vibration device factory that creates vibration devices from GC Adapter
GCVibrationFactory::GCVibrationFactory(std::shared_ptr<GCAdapter::Adapter> adapter_)
: adapter(std::move(adapter_)) {}
/**
* Creates a vibration device from a joystick
* @param params contains parameters for creating the device:
* - "port": the nth gcpad on the adapter
*/
std::unique_ptr<Input::VibrationDevice> GCVibrationFactory::Create(
const Common::ParamPackage& params) {
const auto port = static_cast<u32>(params.Get("port", 0));
return std::make_unique<GCVibration>(port, adapter.get());
}
} // namespace InputCommon

View File

@@ -64,4 +64,15 @@ private:
bool polling = false;
};
/// A vibration device factory creates vibration devices from GC Adapter
class GCVibrationFactory final : public Input::Factory<Input::VibrationDevice> {
public:
explicit GCVibrationFactory(std::shared_ptr<GCAdapter::Adapter> adapter_);
std::unique_ptr<Input::VibrationDevice> Create(const Common::ParamPackage& params) override;
private:
std::shared_ptr<GCAdapter::Adapter> adapter;
};
} // namespace InputCommon

View File

@@ -28,6 +28,8 @@ struct InputSubsystem::Impl {
Input::RegisterFactory<Input::ButtonDevice>("gcpad", gcbuttons);
gcanalog = std::make_shared<GCAnalogFactory>(gcadapter);
Input::RegisterFactory<Input::AnalogDevice>("gcpad", gcanalog);
gcvibration = std::make_shared<GCVibrationFactory>(gcadapter);
Input::RegisterFactory<Input::VibrationDevice>("gcpad", gcvibration);
keyboard = std::make_shared<Keyboard>();
Input::RegisterFactory<Input::ButtonDevice>("keyboard", keyboard);
@@ -64,9 +66,11 @@ struct InputSubsystem::Impl {
#endif
Input::UnregisterFactory<Input::ButtonDevice>("gcpad");
Input::UnregisterFactory<Input::AnalogDevice>("gcpad");
Input::UnregisterFactory<Input::VibrationDevice>("gcpad");
gcbuttons.reset();
gcanalog.reset();
gcvibration.reset();
Input::UnregisterFactory<Input::MotionDevice>("cemuhookudp");
Input::UnregisterFactory<Input::TouchDevice>("cemuhookudp");
@@ -78,7 +82,7 @@ struct InputSubsystem::Impl {
[[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"}},
Common::ParamPackage{{"display", "Keyboard/Mouse"}, {"class", "keyboard"}},
};
#ifdef HAVE_SDL2
auto sdl_devices = sdl->GetInputDevices();
@@ -96,10 +100,6 @@ struct InputSubsystem::Impl {
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 {};
}
if (params.Get("class", "") == "gcpad") {
return gcadapter->GetAnalogMappingForDevice(params);
}
@@ -116,10 +116,6 @@ struct InputSubsystem::Impl {
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 {};
}
if (params.Get("class", "") == "gcpad") {
return gcadapter->GetButtonMappingForDevice(params);
}
@@ -150,6 +146,7 @@ struct InputSubsystem::Impl {
#endif
std::shared_ptr<GCButtonFactory> gcbuttons;
std::shared_ptr<GCAnalogFactory> gcanalog;
std::shared_ptr<GCVibrationFactory> gcvibration;
std::shared_ptr<UDPMotionFactory> udpmotion;
std::shared_ptr<UDPTouchFactory> udptouch;
std::shared_ptr<CemuhookUDP::Client> udp;

View File

@@ -80,30 +80,13 @@ public:
return static_cast<float>(state.axes.at(axis)) / (32767.0f * range);
}
bool RumblePlay(f32 amp_low, f32 amp_high, u32 time) {
const u16 raw_amp_low = static_cast<u16>(amp_low * 0xFFFF);
const u16 raw_amp_high = static_cast<u16>(amp_high * 0xFFFF);
// Lower drastically the number of state changes
if (raw_amp_low >> 11 == last_state_rumble_low >> 11 &&
raw_amp_high >> 11 == last_state_rumble_high >> 11) {
if (raw_amp_low + raw_amp_high != 0 ||
last_state_rumble_low + last_state_rumble_high == 0) {
return false;
}
}
// Don't change state if last vibration was < 20ms
const auto now = std::chrono::system_clock::now();
if (std::chrono::duration_cast<std::chrono::milliseconds>(now - last_vibration) <
std::chrono::milliseconds(20)) {
return raw_amp_low + raw_amp_high == 0;
bool RumblePlay(u16 amp_low, u16 amp_high) {
if (sdl_controller) {
return SDL_GameControllerRumble(sdl_controller.get(), amp_low, amp_high, 0) == 0;
} else if (sdl_joystick) {
return SDL_JoystickRumble(sdl_joystick.get(), amp_low, amp_high, 0) == 0;
}
last_vibration = now;
last_state_rumble_low = raw_amp_low;
last_state_rumble_high = raw_amp_high;
if (sdl_joystick) {
SDL_JoystickRumble(sdl_joystick.get(), raw_amp_low, raw_amp_high, time);
}
return false;
}
@@ -172,9 +155,6 @@ private:
} state;
std::string guid;
int port;
u16 last_state_rumble_high = 0;
u16 last_state_rumble_low = 0;
std::chrono::time_point<std::chrono::system_clock> last_vibration;
std::unique_ptr<SDL_Joystick, decltype(&SDL_JoystickClose)> sdl_joystick;
std::unique_ptr<SDL_GameController, decltype(&SDL_GameControllerClose)> sdl_controller;
mutable std::mutex mutex;
@@ -327,12 +307,6 @@ public:
return joystick->GetButton(button);
}
bool SetRumblePlay(f32 amp_high, f32 amp_low, f32 freq_high, f32 freq_low) const override {
const f32 new_amp_low = pow(amp_low, 0.5f) * (3.0f - 2.0f * pow(amp_low, 0.15f));
const f32 new_amp_high = pow(amp_high, 0.5f) * (3.0f - 2.0f * pow(amp_high, 0.15f));
return joystick->RumblePlay(new_amp_low, new_amp_high, 250);
}
private:
std::shared_ptr<SDLJoystick> joystick;
int button;
@@ -416,6 +390,32 @@ private:
const float range;
};
class SDLVibration final : public Input::VibrationDevice {
public:
explicit SDLVibration(std::shared_ptr<SDLJoystick> joystick_)
: joystick(std::move(joystick_)) {}
u8 GetStatus() const override {
joystick->RumblePlay(1, 1);
return joystick->RumblePlay(0, 0);
}
bool SetRumblePlay(f32 amp_low, f32 freq_low, f32 amp_high, f32 freq_high) const override {
const auto process_amplitude = [](f32 amplitude) {
return static_cast<u16>(std::pow(amplitude, 0.5f) *
(3.0f - 2.0f * std::pow(amplitude, 0.15f)) * 0xFFFF);
};
const auto processed_amp_low = process_amplitude(amp_low);
const auto processed_amp_high = process_amplitude(amp_high);
return joystick->RumblePlay(processed_amp_low, processed_amp_high);
}
private:
std::shared_ptr<SDLJoystick> joystick;
};
class SDLDirectionMotion final : public Input::MotionDevice {
public:
explicit SDLDirectionMotion(std::shared_ptr<SDLJoystick> joystick_, int hat_, Uint8 direction_)
@@ -558,7 +558,7 @@ class SDLAnalogFactory final : public Input::Factory<Input::AnalogDevice> {
public:
explicit SDLAnalogFactory(SDLState& state_) : state(state_) {}
/**
* Creates analog device from joystick axes
* Creates an analog device from joystick axes
* @param params contains parameters for creating the device:
* - "guid": the guid of the joystick to bind
* - "port": the nth joystick of the same type
@@ -584,6 +584,26 @@ private:
SDLState& state;
};
/// An vibration device factory that creates vibration devices from SDL joystick
class SDLVibrationFactory final : public Input::Factory<Input::VibrationDevice> {
public:
explicit SDLVibrationFactory(SDLState& state_) : state(state_) {}
/**
* Creates a vibration device from a joystick
* @param params contains parameters for creating the device:
* - "guid": the guid of the joystick to bind
* - "port": the nth joystick of the same type
*/
std::unique_ptr<Input::VibrationDevice> Create(const Common::ParamPackage& params) override {
const std::string guid = params.Get("guid", "0");
const int port = params.Get("port", 0);
return std::make_unique<SDLVibration>(state.GetSDLJoystickByGUID(guid, port));
}
private:
SDLState& state;
};
/// A motion device factory that creates motion devices from SDL joystick
class SDLMotionFactory final : public Input::Factory<Input::MotionDevice> {
public:
@@ -650,11 +670,13 @@ private:
SDLState::SDLState() {
using namespace Input;
analog_factory = std::make_shared<SDLAnalogFactory>(*this);
button_factory = std::make_shared<SDLButtonFactory>(*this);
analog_factory = std::make_shared<SDLAnalogFactory>(*this);
vibration_factory = std::make_shared<SDLVibrationFactory>(*this);
motion_factory = std::make_shared<SDLMotionFactory>(*this);
RegisterFactory<AnalogDevice>("sdl", analog_factory);
RegisterFactory<ButtonDevice>("sdl", button_factory);
RegisterFactory<AnalogDevice>("sdl", analog_factory);
RegisterFactory<VibrationDevice>("sdl", vibration_factory);
RegisterFactory<MotionDevice>("sdl", motion_factory);
// If the frontend is going to manage the event loop, then we don't start one here
@@ -676,7 +698,7 @@ SDLState::SDLState() {
using namespace std::chrono_literals;
while (initialized) {
SDL_PumpEvents();
std::this_thread::sleep_for(5ms);
std::this_thread::sleep_for(1ms);
}
});
}
@@ -691,6 +713,7 @@ SDLState::~SDLState() {
using namespace Input;
UnregisterFactory<ButtonDevice>("sdl");
UnregisterFactory<AnalogDevice>("sdl");
UnregisterFactory<VibrationDevice>("sdl");
UnregisterFactory<MotionDevice>("sdl");
CloseJoysticks();
@@ -1045,7 +1068,6 @@ public:
void Start(const std::string& device_id) override {
SDLPoller::Start(device_id);
// Load the game controller
// Reset stored axes
analog_x_axis = -1;
analog_y_axis = -1;
@@ -1058,40 +1080,21 @@ public:
if (event.type == SDL_JOYAXISMOTION && std::abs(event.jaxis.value / 32767.0) < 0.5) {
continue;
}
// Simplify controller config by testing if game controller support is enabled.
if (event.type == SDL_JOYAXISMOTION) {
const auto axis = event.jaxis.axis;
if (const auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which);
auto* const controller = joystick->GetSDLGameController()) {
const auto axis_left_x =
SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_LEFTX)
.value.axis;
const auto axis_left_y =
SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_LEFTY)
.value.axis;
const auto axis_right_x =
SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_RIGHTX)
.value.axis;
const auto axis_right_y =
SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_RIGHTY)
.value.axis;
if (axis == axis_left_x || axis == axis_left_y) {
analog_x_axis = axis_left_x;
analog_y_axis = axis_left_y;
break;
} else if (axis == axis_right_x || axis == axis_right_y) {
analog_x_axis = axis_right_x;
analog_y_axis = axis_right_y;
break;
}
// In order to return a complete analog param, we need inputs for both axes.
// First we take the x-axis (horizontal) input, then the y-axis (vertical) input.
if (analog_x_axis == -1) {
analog_x_axis = axis;
} else if (analog_y_axis == -1 && analog_x_axis != axis) {
analog_y_axis = axis;
}
} else {
// If the press wasn't accepted as a joy axis, check for a button press
auto button_press = button_poller.FromEvent(event);
if (button_press) {
return *button_press;
}
}
// If the press wasn't accepted as a joy axis, check for a button press
auto button_press = button_poller.FromEvent(event);
if (button_press) {
return *button_press;
}
}
@@ -1104,6 +1107,7 @@ public:
return params;
}
}
return {};
}

View File

@@ -22,6 +22,7 @@ namespace InputCommon::SDL {
class SDLAnalogFactory;
class SDLButtonFactory;
class SDLMotionFactory;
class SDLVibrationFactory;
class SDLJoystick;
class SDLState : public State {
@@ -72,6 +73,7 @@ private:
std::shared_ptr<SDLButtonFactory> button_factory;
std::shared_ptr<SDLAnalogFactory> analog_factory;
std::shared_ptr<SDLVibrationFactory> vibration_factory;
std::shared_ptr<SDLMotionFactory> motion_factory;
bool start_thread = false;

View File

@@ -14,13 +14,6 @@ const std::array<const char*, NumButtons> mapping = {{
}};
}
namespace NativeMotion {
const std::array<const char*, NumMotions> mapping = {{
"motionleft",
"motionright",
}};
}
namespace NativeAnalog {
const std::array<const char*, NumAnalogs> mapping = {{
"lstick",
@@ -28,6 +21,20 @@ const std::array<const char*, NumAnalogs> mapping = {{
}};
}
namespace NativeVibration {
const std::array<const char*, NumVibrations> mapping = {{
"left_vibration_device",
"right_vibration_device",
}};
}
namespace NativeMotion {
const std::array<const char*, NumMotions> mapping = {{
"motionleft",
"motionright",
}};
}
namespace NativeMouseButton {
const std::array<const char*, NumMouseButtons> mapping = {{
"left",

View File

@@ -66,17 +66,32 @@ constexpr int NUM_STICKS_HID = NumAnalogs;
extern const std::array<const char*, NumAnalogs> mapping;
} // namespace NativeAnalog
namespace NativeVibration {
enum Values : int {
LeftVibrationDevice,
RightVibrationDevice,
NumVibrations,
};
constexpr int VIBRATION_HID_BEGIN = LeftVibrationDevice;
constexpr int VIBRATION_HID_END = NumVibrations;
constexpr int NUM_VIBRATIONS_HID = NumVibrations;
extern const std::array<const char*, NumVibrations> mapping;
}; // namespace NativeVibration
namespace NativeMotion {
enum Values : int {
MOTIONLEFT,
MOTIONRIGHT,
MotionLeft,
MotionRight,
NumMotions,
};
constexpr int MOTION_HID_BEGIN = MOTIONLEFT;
constexpr int MOTION_HID_BEGIN = MotionLeft;
constexpr int MOTION_HID_END = NumMotions;
constexpr int NUM_MOTION_HID = NumMotions;
constexpr int NUM_MOTIONS_HID = NumMotions;
extern const std::array<const char*, NumMotions> mapping;
} // namespace NativeMotion
@@ -305,9 +320,11 @@ 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 MotionRaw = std::array<std::string, NativeMotion::NumMotions>;
using ButtonsRaw = std::array<std::string, NativeButton::NumButtons>;
using MotionsRaw = std::array<std::string, NativeMotion::NumMotions>;
using VibrationsRaw = std::array<std::string, NativeVibration::NumVibrations>;
using MouseButtonsRaw = std::array<std::string, NativeMouseButton::NumMouseButtons>;
using KeyboardKeysRaw = std::array<std::string, NativeKeyboard::NumKeyboardKeys>;
using KeyboardModsRaw = std::array<std::string, NativeKeyboard::NumKeyboardMods>;
@@ -330,7 +347,11 @@ struct PlayerInput {
ControllerType controller_type;
ButtonsRaw buttons;
AnalogsRaw analogs;
MotionRaw motions;
VibrationsRaw vibrations;
MotionsRaw motions;
bool vibration_enabled;
int vibration_strength;
u32 body_color_left;
u32 body_color_right;

View File

@@ -344,7 +344,7 @@ void TestCommunication(const std::string& host, u16 port, std::size_t pad_index,
};
Socket socket{host, port, pad_index, client_id, std::move(callback)};
std::thread worker_thread{SocketLoop, &socket};
const bool result = success_event.WaitFor(std::chrono::seconds(8));
const bool result = success_event.WaitFor(std::chrono::seconds(5));
socket.Stop();
worker_thread.join();
if (result) {

View File

@@ -32,31 +32,31 @@ struct Register {
constexpr Register() = default;
constexpr Register(u64 value) : value(value) {}
constexpr Register(u64 value_) : value(value_) {}
constexpr operator u64() const {
[[nodiscard]] constexpr operator u64() const {
return value;
}
template <typename T>
constexpr u64 operator-(const T& oth) const {
[[nodiscard]] constexpr u64 operator-(const T& oth) const {
return value - oth;
}
template <typename T>
constexpr u64 operator&(const T& oth) const {
[[nodiscard]] constexpr u64 operator&(const T& oth) const {
return value & oth;
}
constexpr u64 operator&(const Register& oth) const {
[[nodiscard]] constexpr u64 operator&(const Register& oth) const {
return value & oth.value;
}
constexpr u64 operator~() const {
[[nodiscard]] constexpr u64 operator~() const {
return ~value;
}
u64 GetSwizzledIndex(u64 elem) const {
[[nodiscard]] u64 GetSwizzledIndex(u64 elem) const {
elem = (value + elem) & 3;
return (value & ~3) + elem;
}
@@ -75,7 +75,7 @@ enum class AttributeSize : u64 {
union Attribute {
Attribute() = default;
constexpr explicit Attribute(u64 value) : value(value) {}
constexpr explicit Attribute(u64 value_) : value(value_) {}
enum class Index : u64 {
LayerViewportPointSize = 6,
@@ -107,7 +107,7 @@ union Attribute {
BitField<31, 1, u64> patch;
BitField<47, 3, AttributeSize> size;
bool IsPhysical() const {
[[nodiscard]] bool IsPhysical() const {
return patch == 0 && element == 0 && static_cast<u64>(index.Value()) == 0;
}
} fmt20;
@@ -124,7 +124,7 @@ union Attribute {
union Sampler {
Sampler() = default;
constexpr explicit Sampler(u64 value) : value(value) {}
constexpr explicit Sampler(u64 value_) : value(value_) {}
enum class Index : u64 {
Sampler_0 = 8,
@@ -137,7 +137,7 @@ union Sampler {
union Image {
Image() = default;
constexpr explicit Image(u64 value) : value{value} {}
constexpr explicit Image(u64 value_) : value{value_} {}
BitField<36, 13, u64> index;
u64 value;
@@ -505,14 +505,14 @@ struct IpaMode {
IpaInterpMode interpolation_mode;
IpaSampleMode sampling_mode;
bool operator==(const IpaMode& a) const {
[[nodiscard]] bool operator==(const IpaMode& a) const {
return std::tie(interpolation_mode, sampling_mode) ==
std::tie(a.interpolation_mode, a.sampling_mode);
}
bool operator!=(const IpaMode& a) const {
[[nodiscard]] bool operator!=(const IpaMode& a) const {
return !operator==(a);
}
bool operator<(const IpaMode& a) const {
[[nodiscard]] bool operator<(const IpaMode& a) const {
return std::tie(interpolation_mode, sampling_mode) <
std::tie(a.interpolation_mode, a.sampling_mode);
}
@@ -658,10 +658,10 @@ union Instruction {
return *this;
}
constexpr Instruction(u64 value) : value{value} {}
constexpr Instruction(u64 value_) : value{value_} {}
constexpr Instruction(const Instruction& instr) : value(instr.value) {}
constexpr bool Bit(u64 offset) const {
[[nodiscard]] constexpr bool Bit(u64 offset) const {
return ((value >> offset) & 1) != 0;
}
@@ -746,34 +746,34 @@ union Instruction {
BitField<28, 8, u64> imm_lut28;
BitField<48, 8, u64> imm_lut48;
u32 GetImmLut28() const {
[[nodiscard]] u32 GetImmLut28() const {
return static_cast<u32>(imm_lut28);
}
u32 GetImmLut48() const {
[[nodiscard]] u32 GetImmLut48() const {
return static_cast<u32>(imm_lut48);
}
} lop3;
u16 GetImm20_16() const {
[[nodiscard]] u16 GetImm20_16() const {
return static_cast<u16>(imm20_16);
}
u32 GetImm20_19() const {
[[nodiscard]] u32 GetImm20_19() const {
u32 imm{static_cast<u32>(imm20_19)};
imm <<= 12;
imm |= negate_imm ? 0x80000000 : 0;
return imm;
}
u32 GetImm20_32() const {
[[nodiscard]] u32 GetImm20_32() const {
return static_cast<u32>(imm20_32);
}
s32 GetSignedImm20_20() const {
u32 immediate = static_cast<u32>(imm20_19 | (negate_imm << 19));
[[nodiscard]] s32 GetSignedImm20_20() const {
const auto immediate = static_cast<u32>(imm20_19 | (negate_imm << 19));
// Sign extend the 20-bit value.
u32 mask = 1U << (20 - 1);
const auto mask = 1U << (20 - 1);
return static_cast<s32>((immediate ^ mask) - mask);
}
} alu;
@@ -857,7 +857,7 @@ union Instruction {
BitField<56, 1, u64> second_negate;
BitField<30, 9, u64> second;
u32 PackImmediates() const {
[[nodiscard]] u32 PackImmediates() const {
// Immediates are half floats shifted.
constexpr u32 imm_shift = 6;
return static_cast<u32>((first << imm_shift) | (second << (16 + imm_shift)));
@@ -1033,7 +1033,7 @@ union Instruction {
BitField<28, 2, AtomicType> type;
BitField<30, 22, s64> offset;
s32 GetImmediateOffset() const {
[[nodiscard]] s32 GetImmediateOffset() const {
return static_cast<s32>(offset << 2);
}
} atoms;
@@ -1215,7 +1215,7 @@ union Instruction {
BitField<39, 4, u64> rounding;
// H0, H1 extract for F16 missing
BitField<41, 1, u64> selector; // Guessed as some games set it, TODO: reverse this value
F2fRoundingOp GetRoundingMode() const {
[[nodiscard]] F2fRoundingOp GetRoundingMode() const {
constexpr u64 rounding_mask = 0x0B;
return static_cast<F2fRoundingOp>(rounding.Value() & rounding_mask);
}
@@ -1239,15 +1239,15 @@ union Instruction {
BitField<54, 1, u64> aoffi_flag;
BitField<55, 3, TextureProcessMode> process_mode;
bool IsComponentEnabled(std::size_t component) const {
return ((1ull << component) & component_mask) != 0;
[[nodiscard]] bool IsComponentEnabled(std::size_t component) const {
return ((1ULL << component) & component_mask) != 0;
}
TextureProcessMode GetTextureProcessMode() const {
[[nodiscard]] TextureProcessMode GetTextureProcessMode() const {
return process_mode;
}
bool UsesMiscMode(TextureMiscMode mode) const {
[[nodiscard]] bool UsesMiscMode(TextureMiscMode mode) const {
switch (mode) {
case TextureMiscMode::DC:
return dc_flag != 0;
@@ -1271,15 +1271,15 @@ union Instruction {
BitField<36, 1, u64> aoffi_flag;
BitField<37, 3, TextureProcessMode> process_mode;
bool IsComponentEnabled(std::size_t component) const {
[[nodiscard]] bool IsComponentEnabled(std::size_t component) const {
return ((1ULL << component) & component_mask) != 0;
}
TextureProcessMode GetTextureProcessMode() const {
[[nodiscard]] TextureProcessMode GetTextureProcessMode() const {
return process_mode;
}
bool UsesMiscMode(TextureMiscMode mode) const {
[[nodiscard]] bool UsesMiscMode(TextureMiscMode mode) const {
switch (mode) {
case TextureMiscMode::DC:
return dc_flag != 0;
@@ -1299,7 +1299,7 @@ union Instruction {
BitField<31, 4, u64> component_mask;
BitField<49, 1, u64> nodep_flag;
bool UsesMiscMode(TextureMiscMode mode) const {
[[nodiscard]] bool UsesMiscMode(TextureMiscMode mode) const {
switch (mode) {
case TextureMiscMode::NODEP:
return nodep_flag != 0;
@@ -1309,7 +1309,7 @@ union Instruction {
return false;
}
bool IsComponentEnabled(std::size_t component) const {
[[nodiscard]] bool IsComponentEnabled(std::size_t component) const {
return ((1ULL << component) & component_mask) != 0;
}
} txq;
@@ -1321,11 +1321,11 @@ union Instruction {
BitField<35, 1, u64> ndv_flag;
BitField<49, 1, u64> nodep_flag;
bool IsComponentEnabled(std::size_t component) const {
return ((1ull << component) & component_mask) != 0;
[[nodiscard]] bool IsComponentEnabled(std::size_t component) const {
return ((1ULL << component) & component_mask) != 0;
}
bool UsesMiscMode(TextureMiscMode mode) const {
[[nodiscard]] bool UsesMiscMode(TextureMiscMode mode) const {
switch (mode) {
case TextureMiscMode::NDV:
return (ndv_flag != 0);
@@ -1347,7 +1347,7 @@ union Instruction {
BitField<54, 2, u64> offset_mode;
BitField<56, 2, u64> component;
bool UsesMiscMode(TextureMiscMode mode) const {
[[nodiscard]] bool UsesMiscMode(TextureMiscMode mode) const {
switch (mode) {
case TextureMiscMode::NDV:
return ndv_flag != 0;
@@ -1373,7 +1373,7 @@ union Instruction {
BitField<33, 2, u64> offset_mode;
BitField<37, 2, u64> component;
bool UsesMiscMode(TextureMiscMode mode) const {
[[nodiscard]] bool UsesMiscMode(TextureMiscMode mode) const {
switch (mode) {
case TextureMiscMode::NDV:
return ndv_flag != 0;
@@ -1399,7 +1399,7 @@ union Instruction {
BitField<52, 2, u64> component;
BitField<55, 1, u64> fp16_flag;
bool UsesMiscMode(TextureMiscMode mode) const {
[[nodiscard]] bool UsesMiscMode(TextureMiscMode mode) const {
switch (mode) {
case TextureMiscMode::DC:
return dc_flag != 0;
@@ -1422,16 +1422,20 @@ union Instruction {
BitField<53, 4, u64> texture_info;
BitField<59, 1, u64> fp32_flag;
TextureType GetTextureType() const {
[[nodiscard]] TextureType GetTextureType() const {
// The TEXS instruction has a weird encoding for the texture type.
if (texture_info == 0)
if (texture_info == 0) {
return TextureType::Texture1D;
if (texture_info >= 1 && texture_info <= 9)
}
if (texture_info >= 1 && texture_info <= 9) {
return TextureType::Texture2D;
if (texture_info >= 10 && texture_info <= 11)
}
if (texture_info >= 10 && texture_info <= 11) {
return TextureType::Texture3D;
if (texture_info >= 12 && texture_info <= 13)
}
if (texture_info >= 12 && texture_info <= 13) {
return TextureType::TextureCube;
}
LOG_CRITICAL(HW_GPU, "Unhandled texture_info: {}",
static_cast<u32>(texture_info.Value()));
@@ -1439,7 +1443,7 @@ union Instruction {
return TextureType::Texture1D;
}
TextureProcessMode GetTextureProcessMode() const {
[[nodiscard]] TextureProcessMode GetTextureProcessMode() const {
switch (texture_info) {
case 0:
case 2:
@@ -1458,7 +1462,7 @@ union Instruction {
return TextureProcessMode::None;
}
bool UsesMiscMode(TextureMiscMode mode) const {
[[nodiscard]] bool UsesMiscMode(TextureMiscMode mode) const {
switch (mode) {
case TextureMiscMode::DC:
return (texture_info >= 4 && texture_info <= 6) || texture_info == 9;
@@ -1470,16 +1474,16 @@ union Instruction {
return false;
}
bool IsArrayTexture() const {
[[nodiscard]] bool IsArrayTexture() const {
// TEXS only supports Texture2D arrays.
return texture_info >= 7 && texture_info <= 9;
}
bool HasTwoDestinations() const {
[[nodiscard]] bool HasTwoDestinations() const {
return gpr28.Value() != Register::ZeroIndex;
}
bool IsComponentEnabled(std::size_t component) const {
[[nodiscard]] bool IsComponentEnabled(std::size_t component) const {
static constexpr std::array<std::array<u32, 8>, 4> mask_lut{{
{},
{0x1, 0x2, 0x4, 0x8, 0x3, 0x9, 0xa, 0xc},
@@ -1506,7 +1510,7 @@ union Instruction {
BitField<54, 1, u64> cl;
BitField<55, 1, u64> process_mode;
TextureProcessMode GetTextureProcessMode() const {
[[nodiscard]] TextureProcessMode GetTextureProcessMode() const {
return process_mode == 0 ? TextureProcessMode::LZ : TextureProcessMode::LL;
}
} tld;
@@ -1516,7 +1520,7 @@ union Instruction {
BitField<53, 4, u64> texture_info;
BitField<59, 1, u64> fp32_flag;
TextureType GetTextureType() const {
[[nodiscard]] TextureType GetTextureType() const {
// The TLDS instruction has a weird encoding for the texture type.
if (texture_info <= 1) {
return TextureType::Texture1D;
@@ -1535,13 +1539,14 @@ union Instruction {
return TextureType::Texture1D;
}
TextureProcessMode GetTextureProcessMode() const {
if (texture_info == 1 || texture_info == 5 || texture_info == 12)
[[nodiscard]] TextureProcessMode GetTextureProcessMode() const {
if (texture_info == 1 || texture_info == 5 || texture_info == 12) {
return TextureProcessMode::LL;
}
return TextureProcessMode::LZ;
}
bool UsesMiscMode(TextureMiscMode mode) const {
[[nodiscard]] bool UsesMiscMode(TextureMiscMode mode) const {
switch (mode) {
case TextureMiscMode::AOFFI:
return texture_info == 12 || texture_info == 4;
@@ -1555,7 +1560,7 @@ union Instruction {
return false;
}
bool IsArrayTexture() const {
[[nodiscard]] bool IsArrayTexture() const {
// TEXS only supports Texture2D arrays.
return texture_info == 8;
}
@@ -1567,7 +1572,7 @@ union Instruction {
BitField<35, 1, u64> aoffi_flag;
BitField<49, 1, u64> nodep_flag;
bool UsesMiscMode(TextureMiscMode mode) const {
[[nodiscard]] bool UsesMiscMode(TextureMiscMode mode) const {
switch (mode) {
case TextureMiscMode::AOFFI:
return aoffi_flag != 0;
@@ -1591,7 +1596,7 @@ union Instruction {
BitField<20, 3, StoreType> store_data_layout;
BitField<20, 4, u64> component_mask_selector;
bool IsComponentEnabled(std::size_t component) const {
[[nodiscard]] bool IsComponentEnabled(std::size_t component) const {
ASSERT(mode == SurfaceDataMode::P);
constexpr u8 R = 0b0001;
constexpr u8 G = 0b0010;
@@ -1604,7 +1609,7 @@ union Instruction {
return std::bitset<4>{mask.at(component_mask_selector)}.test(component);
}
StoreType GetStoreDataLayout() const {
[[nodiscard]] StoreType GetStoreDataLayout() const {
ASSERT(mode == SurfaceDataMode::D_BA);
return store_data_layout;
}
@@ -1622,14 +1627,15 @@ union Instruction {
BitField<20, 24, u64> target;
BitField<5, 1, u64> constant_buffer;
s32 GetBranchTarget() const {
[[nodiscard]] s32 GetBranchTarget() const {
// Sign extend the branch target offset
u32 mask = 1U << (24 - 1);
u32 value = static_cast<u32>(target);
const auto mask = 1U << (24 - 1);
const auto target_value = static_cast<u32>(target);
constexpr auto instruction_size = static_cast<s32>(sizeof(Instruction));
// The branch offset is relative to the next instruction and is stored in bytes, so
// divide it by the size of an instruction and add 1 to it.
return static_cast<s32>((value ^ mask) - mask) / static_cast<s32>(sizeof(Instruction)) +
1;
return static_cast<s32>((target_value ^ mask) - mask) / instruction_size + 1;
}
} bra;
@@ -1637,14 +1643,15 @@ union Instruction {
BitField<20, 24, u64> target;
BitField<5, 1, u64> constant_buffer;
s32 GetBranchExtend() const {
[[nodiscard]] s32 GetBranchExtend() const {
// Sign extend the branch target offset
u32 mask = 1U << (24 - 1);
u32 value = static_cast<u32>(target);
const auto mask = 1U << (24 - 1);
const auto target_value = static_cast<u32>(target);
constexpr auto instruction_size = static_cast<s32>(sizeof(Instruction));
// The branch offset is relative to the next instruction and is stored in bytes, so
// divide it by the size of an instruction and add 1 to it.
return static_cast<s32>((value ^ mask) - mask) / static_cast<s32>(sizeof(Instruction)) +
1;
return static_cast<s32>((target_value ^ mask) - mask) / instruction_size + 1;
}
} brx;
@@ -1697,7 +1704,7 @@ union Instruction {
BitField<50, 1, u64> is_op_b_register;
BitField<51, 3, VmnmxOperation> operation;
VmnmxType SourceFormatA() const {
[[nodiscard]] VmnmxType SourceFormatA() const {
switch (src_format_a) {
case 0b11:
return VmnmxType::Bits32;
@@ -1708,7 +1715,7 @@ union Instruction {
}
}
VmnmxType SourceFormatB() const {
[[nodiscard]] VmnmxType SourceFormatB() const {
switch (src_format_b) {
case 0b11:
return VmnmxType::Bits32;
@@ -1739,7 +1746,7 @@ union Instruction {
BitField<20, 14, u64> shifted_offset;
BitField<34, 5, u64> index;
u64 GetOffset() const {
[[nodiscard]] u64 GetOffset() const {
return shifted_offset * 4;
}
} cbuf34;
@@ -1748,7 +1755,7 @@ union Instruction {
BitField<20, 16, s64> offset;
BitField<36, 5, u64> index;
s64 GetOffset() const {
[[nodiscard]] s64 GetOffset() const {
return offset;
}
} cbuf36;
@@ -1997,29 +2004,29 @@ public:
/// Returns whether an opcode has an execution predicate field or not (ie, whether it can be
/// conditionally executed).
static bool IsPredicatedInstruction(Id opcode) {
[[nodiscard]] static bool IsPredicatedInstruction(Id opcode) {
// TODO(Subv): Add the rest of unpredicated instructions.
return opcode != Id::SSY && opcode != Id::PBK;
}
class Matcher {
public:
constexpr Matcher(const char* const name, u16 mask, u16 expected, Id id, Type type)
: name{name}, mask{mask}, expected{expected}, id{id}, type{type} {}
constexpr Matcher(const char* const name_, u16 mask_, u16 expected_, Id id_, Type type_)
: name{name_}, mask{mask_}, expected{expected_}, id{id_}, type{type_} {}
constexpr const char* GetName() const {
[[nodiscard]] constexpr const char* GetName() const {
return name;
}
constexpr u16 GetMask() const {
[[nodiscard]] constexpr u16 GetMask() const {
return mask;
}
constexpr Id GetId() const {
[[nodiscard]] constexpr Id GetId() const {
return id;
}
constexpr Type GetType() const {
[[nodiscard]] constexpr Type GetType() const {
return type;
}
@@ -2028,7 +2035,7 @@ public:
* @param instruction The instruction to test
* @returns true if the given instruction matches.
*/
constexpr bool Matches(u16 instruction) const {
[[nodiscard]] constexpr bool Matches(u16 instruction) const {
return (instruction & mask) == expected;
}
@@ -2040,7 +2047,8 @@ public:
Type type;
};
static std::optional<std::reference_wrapper<const Matcher>> Decode(Instruction instr) {
using DecodeResult = std::optional<std::reference_wrapper<const Matcher>>;
[[nodiscard]] static DecodeResult Decode(Instruction instr) {
static const auto table{GetDecodeTable()};
const auto matches_instruction = [instr](const auto& matcher) {
@@ -2062,7 +2070,7 @@ private:
* A '0' in a bitstring indicates that a zero must be present at that bit position.
* A '1' in a bitstring indicates that a one must be present at that bit position.
*/
static constexpr auto GetMaskAndExpect(const char* const bitstring) {
[[nodiscard]] static constexpr auto GetMaskAndExpect(const char* const bitstring) {
u16 mask = 0, expect = 0;
for (std::size_t i = 0; i < opcode_bitsize; i++) {
const std::size_t bit_position = opcode_bitsize - i - 1;
@@ -2084,14 +2092,14 @@ private:
public:
/// Creates a matcher that can match and parse instructions based on bitstring.
static constexpr auto GetMatcher(const char* const bitstring, Id op, Type type,
const char* const name) {
[[nodiscard]] static constexpr auto GetMatcher(const char* const bitstring, Id op,
Type type, const char* const name) {
const auto [mask, expected] = GetMaskAndExpect(bitstring);
return Matcher(name, mask, expected, op, type);
}
};
static std::vector<Matcher> GetDecodeTable() {
[[nodiscard]] static std::vector<Matcher> GetDecodeTable() {
std::vector<Matcher> table = {
#define INST(bitstring, op, type, name) Detail::GetMatcher(bitstring, op, type, name)
INST("111000110011----", Id::KIL, Type::Flow, "KIL"),

View File

@@ -68,12 +68,12 @@ add_executable(yuzu
configuration/configure_input_advanced.cpp
configuration/configure_input_advanced.h
configuration/configure_input_advanced.ui
configuration/configure_input_dialog.cpp
configuration/configure_input_dialog.h
configuration/configure_input_dialog.ui
configuration/configure_input_player.cpp
configuration/configure_input_player.h
configuration/configure_input_player.ui
configuration/configure_input_profile_dialog.cpp
configuration/configure_input_profile_dialog.h
configuration/configure_input_profile_dialog.ui
configuration/configure_motion_touch.cpp
configuration/configure_motion_touch.h
configuration/configure_motion_touch.ui
@@ -105,9 +105,14 @@ add_executable(yuzu
configuration/configure_ui.cpp
configuration/configure_ui.h
configuration/configure_ui.ui
configuration/configure_vibration.cpp
configuration/configure_vibration.h
configuration/configure_vibration.ui
configuration/configure_web.cpp
configuration/configure_web.h
configuration/configure_web.ui
configuration/input_profiles.cpp
configuration/input_profiles.h
debugger/console.cpp
debugger/console.h
debugger/profiler.cpp

View File

@@ -160,32 +160,12 @@ p, li { white-space: pre-wrap; }
<signal>accepted()</signal>
<receiver>AboutDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>AboutDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@@ -3,6 +3,7 @@
// Refer to the license.txt file included.
#include <algorithm>
#include <thread>
#include "common/assert.h"
#include "common/string_util.h"
@@ -13,11 +14,16 @@
#include "core/hle/service/sm/sm.h"
#include "ui_controller.h"
#include "yuzu/applets/controller.h"
#include "yuzu/configuration/configure_input_dialog.h"
#include "yuzu/configuration/configure_input.h"
#include "yuzu/configuration/configure_input_profile_dialog.h"
#include "yuzu/configuration/configure_vibration.h"
#include "yuzu/configuration/input_profiles.h"
#include "yuzu/main.h"
namespace {
constexpr std::size_t HANDHELD_INDEX = 8;
constexpr std::array<std::array<bool, 4>, 8> led_patterns{{
{true, false, false, false},
{true, true, false, false},
@@ -106,7 +112,8 @@ QtControllerSelectorDialog::QtControllerSelectorDialog(
QWidget* parent, Core::Frontend::ControllerParameters parameters_,
InputCommon::InputSubsystem* input_subsystem_)
: QDialog(parent), ui(std::make_unique<Ui::QtControllerSelectorDialog>()),
parameters(std::move(parameters_)), input_subsystem(input_subsystem_) {
parameters(std::move(parameters_)), input_subsystem{input_subsystem_},
input_profiles(std::make_unique<InputProfiles>()) {
ui->setupUi(this);
player_widgets = {
@@ -223,12 +230,22 @@ QtControllerSelectorDialog::QtControllerSelectorDialog(
}
}
connect(ui->vibrationButton, &QPushButton::clicked, this,
&QtControllerSelectorDialog::CallConfigureVibrationDialog);
connect(ui->inputConfigButton, &QPushButton::clicked, this,
&QtControllerSelectorDialog::CallConfigureInputDialog);
&QtControllerSelectorDialog::CallConfigureInputProfileDialog);
connect(ui->buttonBox, &QDialogButtonBox::accepted, this,
&QtControllerSelectorDialog::ApplyConfiguration);
// Enhancement: Check if the parameters have already been met before disconnecting controllers.
// If all the parameters are met AND only allows a single player,
// stop the constructor here as we do not need to continue.
if (CheckIfParametersMet() && parameters.enable_single_mode) {
return;
}
// If keep_controllers_connected is false, forcefully disconnect all controllers
if (!parameters.keep_controllers_connected) {
for (auto player : player_groupboxes) {
@@ -236,58 +253,66 @@ QtControllerSelectorDialog::QtControllerSelectorDialog(
}
}
CheckIfParametersMet();
resize(0, 0);
}
QtControllerSelectorDialog::~QtControllerSelectorDialog() = default;
void QtControllerSelectorDialog::ApplyConfiguration() {
// Update the controller state once more, just to be sure they are properly applied.
for (std::size_t index = 0; index < NUM_PLAYERS; ++index) {
UpdateControllerState(index);
int QtControllerSelectorDialog::exec() {
if (parameters_met && parameters.enable_single_mode) {
return QDialog::Accepted;
}
return QDialog::exec();
}
const bool pre_docked_mode = Settings::values.use_docked_mode;
Settings::values.use_docked_mode = ui->radioDocked->isChecked();
OnDockedModeChanged(pre_docked_mode, Settings::values.use_docked_mode);
void QtControllerSelectorDialog::ApplyConfiguration() {
const bool pre_docked_mode = Settings::values.use_docked_mode.GetValue();
Settings::values.use_docked_mode.SetValue(ui->radioDocked->isChecked());
OnDockedModeChanged(pre_docked_mode, Settings::values.use_docked_mode.GetValue());
Settings::values.vibration_enabled = ui->vibrationGroup->isChecked();
Settings::values.vibration_enabled.SetValue(ui->vibrationGroup->isChecked());
Settings::values.motion_enabled.SetValue(ui->motionGroup->isChecked());
}
void QtControllerSelectorDialog::LoadConfiguration() {
for (std::size_t index = 0; index < NUM_PLAYERS; ++index) {
const auto connected = Settings::values.players[index].connected ||
(index == 0 && Settings::values.players[8].connected);
const auto connected =
Settings::values.players.GetValue()[index].connected ||
(index == 0 && Settings::values.players.GetValue()[HANDHELD_INDEX].connected);
player_groupboxes[index]->setChecked(connected);
connected_controller_checkboxes[index]->setChecked(connected);
emulated_controllers[index]->setCurrentIndex(
GetIndexFromControllerType(Settings::values.players[index].controller_type));
GetIndexFromControllerType(Settings::values.players.GetValue()[index].controller_type));
}
UpdateDockedState(Settings::values.players[8].connected);
UpdateDockedState(Settings::values.players.GetValue()[HANDHELD_INDEX].connected);
ui->vibrationGroup->setChecked(Settings::values.vibration_enabled);
ui->vibrationGroup->setChecked(Settings::values.vibration_enabled.GetValue());
ui->motionGroup->setChecked(Settings::values.motion_enabled.GetValue());
}
void QtControllerSelectorDialog::CallConfigureInputDialog() {
const auto max_supported_players = parameters.enable_single_mode ? 1 : parameters.max_players;
void QtControllerSelectorDialog::CallConfigureVibrationDialog() {
ConfigureVibration dialog(this);
ConfigureInputDialog dialog(this, max_supported_players, input_subsystem);
dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint |
Qt::WindowSystemMenuHint);
dialog.setWindowModality(Qt::WindowModal);
if (dialog.exec() == QDialog::Accepted) {
dialog.ApplyConfiguration();
}
}
void QtControllerSelectorDialog::CallConfigureInputProfileDialog() {
ConfigureInputProfileDialog dialog(this, input_subsystem, input_profiles.get());
dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint |
Qt::WindowSystemMenuHint);
dialog.setWindowModality(Qt::WindowModal);
dialog.exec();
dialog.ApplyConfiguration();
LoadConfiguration();
CheckIfParametersMet();
}
void QtControllerSelectorDialog::CheckIfParametersMet() {
bool QtControllerSelectorDialog::CheckIfParametersMet() {
// Here, we check and validate the current configuration against all applicable parameters.
const auto num_connected_players = static_cast<int>(
std::count_if(player_groupboxes.begin(), player_groupboxes.end(),
@@ -301,7 +326,7 @@ void QtControllerSelectorDialog::CheckIfParametersMet() {
num_connected_players > max_supported_players) {
parameters_met = false;
ui->buttonBox->setEnabled(parameters_met);
return;
return parameters_met;
}
// Next, check against all connected controllers.
@@ -326,18 +351,13 @@ void QtControllerSelectorDialog::CheckIfParametersMet() {
return true;
}();
if (!all_controllers_compatible) {
parameters_met = false;
ui->buttonBox->setEnabled(parameters_met);
return;
}
parameters_met = true;
parameters_met = all_controllers_compatible;
ui->buttonBox->setEnabled(parameters_met);
return parameters_met;
}
void QtControllerSelectorDialog::SetSupportedControllers() {
const QString theme = [this] {
const QString theme = [] {
if (QIcon::themeName().contains(QStringLiteral("dark"))) {
return QStringLiteral("_dark");
} else if (QIcon::themeName().contains(QStringLiteral("midnight"))) {
@@ -426,7 +446,7 @@ void QtControllerSelectorDialog::UpdateControllerIcon(std::size_t player_index)
}
}();
const QString theme = [this] {
const QString theme = [] {
if (QIcon::themeName().contains(QStringLiteral("dark"))) {
return QStringLiteral("_dark");
} else if (QIcon::themeName().contains(QStringLiteral("midnight"))) {
@@ -441,32 +461,48 @@ void QtControllerSelectorDialog::UpdateControllerIcon(std::size_t player_index)
}
void QtControllerSelectorDialog::UpdateControllerState(std::size_t player_index) {
auto& player = Settings::values.players[player_index];
auto& player = Settings::values.players.GetValue()[player_index];
player.controller_type =
const auto controller_type =
GetControllerTypeFromIndex(emulated_controllers[player_index]->currentIndex());
player.connected = player_groupboxes[player_index]->isChecked();
const auto player_connected = player_groupboxes[player_index]->isChecked() &&
controller_type != Settings::ControllerType::Handheld;
// Player 2-8
if (player_index != 0) {
UpdateController(player.controller_type, player_index, player.connected);
if (player.controller_type == controller_type && player.connected == player_connected) {
// Set vibration devices in the event that the input device has changed.
ConfigureVibration::SetVibrationDevices(player_index);
return;
}
// Player 1 and Handheld
auto& handheld = Settings::values.players[8];
// If Handheld is selected, copy all the settings from Player 1 to Handheld.
if (player.controller_type == Settings::ControllerType::Handheld) {
handheld = player;
handheld.connected = player_groupboxes[player_index]->isChecked();
player.connected = false; // Disconnect Player 1
} else {
player.connected = player_groupboxes[player_index]->isChecked();
handheld.connected = false; // Disconnect Handheld
// Disconnect the controller first.
UpdateController(controller_type, player_index, false);
player.controller_type = controller_type;
player.connected = player_connected;
ConfigureVibration::SetVibrationDevices(player_index);
// Handheld
if (player_index == 0) {
auto& handheld = Settings::values.players.GetValue()[HANDHELD_INDEX];
if (controller_type == Settings::ControllerType::Handheld) {
handheld = player;
}
handheld.connected = player_groupboxes[player_index]->isChecked() &&
controller_type == Settings::ControllerType::Handheld;
UpdateController(Settings::ControllerType::Handheld, 8, handheld.connected);
}
UpdateController(player.controller_type, player_index, player.connected);
UpdateController(Settings::ControllerType::Handheld, 8, handheld.connected);
if (!player.connected) {
return;
}
// This emulates a delay between disconnecting and reconnecting controllers as some games
// do not respond to a change in controller type if it was instantaneous.
using namespace std::chrono_literals;
std::this_thread::sleep_for(20ms);
UpdateController(controller_type, player_index, player_connected);
}
void QtControllerSelectorDialog::UpdateLEDPattern(std::size_t player_index) {
@@ -520,8 +556,8 @@ void QtControllerSelectorDialog::UpdateDockedState(bool is_handheld) {
ui->radioDocked->setEnabled(!is_handheld);
ui->radioUndocked->setEnabled(!is_handheld);
ui->radioDocked->setChecked(Settings::values.use_docked_mode);
ui->radioUndocked->setChecked(!Settings::values.use_docked_mode);
ui->radioDocked->setChecked(Settings::values.use_docked_mode.GetValue());
ui->radioUndocked->setChecked(!Settings::values.use_docked_mode.GetValue());
// Also force into undocked mode if the controller type is handheld.
if (is_handheld) {
@@ -564,8 +600,8 @@ void QtControllerSelectorDialog::DisableUnsupportedPlayers() {
for (std::size_t index = max_supported_players; index < NUM_PLAYERS; ++index) {
// Disconnect any unsupported players here and disable or hide them if applicable.
Settings::values.players[index].connected = false;
UpdateController(Settings::values.players[index].controller_type, index, false);
Settings::values.players.GetValue()[index].connected = false;
UpdateController(Settings::values.players.GetValue()[index].controller_type, index, false);
// Hide the player widgets when max_supported_controllers is less than or equal to 4.
if (max_supported_players <= 4) {
player_widgets[index]->hide();

View File

@@ -16,6 +16,8 @@ class QDialogButtonBox;
class QGroupBox;
class QLabel;
class InputProfiles;
namespace InputCommon {
class InputSubsystem;
}
@@ -33,6 +35,8 @@ public:
InputCommon::InputSubsystem* input_subsystem_);
~QtControllerSelectorDialog() override;
int exec() override;
private:
// Applies the current configuration.
void ApplyConfiguration();
@@ -40,12 +44,15 @@ private:
// Loads the current input configuration into the frontend applet.
void LoadConfiguration();
// Initializes the "Configure Input" Dialog.
void CallConfigureInputDialog();
// Initializes the "Configure Vibration" Dialog.
void CallConfigureVibrationDialog();
// Checks the current configuration against the given parameters and
// sets the value of parameters_met.
void CheckIfParametersMet();
// Initializes the "Create Input Profile" Dialog.
void CallConfigureInputProfileDialog();
// Checks the current configuration against the given parameters.
// This sets and returns the value of parameters_met.
bool CheckIfParametersMet();
// Sets the controller icons for "Supported Controller Types".
void SetSupportedControllers();
@@ -78,6 +85,8 @@ private:
InputCommon::InputSubsystem* input_subsystem;
std::unique_ptr<InputProfiles> input_profiles;
// This is true if and only if all parameters are met. Otherwise, this is false.
// This determines whether the "OK" button can be clicked to exit the applet.
bool parameters_met{false};

View File

@@ -1217,9 +1217,6 @@
</item>
<item>
<widget class="QComboBox" name="comboPlayer3Emulated">
<property name="editable">
<bool>false</bool>
</property>
<item>
<property name="text">
<string>Pro Controller</string>
@@ -2279,7 +2276,7 @@
<number>6</number>
</property>
<property name="leftMargin">
<number>6</number>
<number>8</number>
</property>
<property name="topMargin">
<number>6</number>
@@ -2332,30 +2329,24 @@
<number>3</number>
</property>
<item>
<widget class="QSpinBox" name="vibrationSpin">
<widget class="QPushButton" name="vibrationButton">
<property name="minimumSize">
<size>
<width>65</width>
<width>68</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>65</width>
<width>68</width>
<height>16777215</height>
</size>
</property>
<property name="suffix">
<string>%</string>
<property name="styleSheet">
<string notr="true">min-width: 68px;</string>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>200</number>
</property>
<property name="value">
<number>100</number>
<property name="text">
<string>Configure</string>
</property>
</widget>
</item>
@@ -2387,18 +2378,18 @@
<widget class="QPushButton" name="motionButton">
<property name="minimumSize">
<size>
<width>57</width>
<width>68</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>55</width>
<width>68</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">min-width: 55px;</string>
<string notr="true">min-width: 68px;</string>
</property>
<property name="text">
<string>Configure</string>
@@ -2411,7 +2402,7 @@
<item>
<widget class="QGroupBox" name="inputConfigGroup">
<property name="title">
<string>Input Config</string>
<string>Profiles</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_7">
<property name="leftMargin">
@@ -2430,15 +2421,15 @@
<widget class="QPushButton" name="inputConfigButton">
<property name="maximumSize">
<size>
<width>65</width>
<width>68</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">min-width: 55px;</string>
<string notr="true">min-width: 68px;</string>
</property>
<property name="text">
<string>Open</string>
<string>Create</string>
</property>
</widget>
</item>
@@ -2657,16 +2648,6 @@
<signal>accepted()</signal>
<receiver>QtControllerSelectorDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>20</x>
<y>20</y>
</hint>
<hint type="destinationlabel">
<x>20</x>
<y>20</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@@ -382,7 +382,12 @@ void GRenderWindow::keyReleaseEvent(QKeyEvent* event) {
}
void GRenderWindow::mousePressEvent(QMouseEvent* event) {
// touch input is handled in TouchBeginEvent
if (!Settings::values.touchscreen.enabled) {
input_subsystem->GetKeyboard()->PressKey(event->button());
return;
}
// Touch input is handled in TouchBeginEvent
if (event->source() == Qt::MouseEventSynthesizedBySystem) {
return;
}
@@ -398,7 +403,7 @@ void GRenderWindow::mousePressEvent(QMouseEvent* event) {
}
void GRenderWindow::mouseMoveEvent(QMouseEvent* event) {
// touch input is handled in TouchUpdateEvent
// Touch input is handled in TouchUpdateEvent
if (event->source() == Qt::MouseEventSynthesizedBySystem) {
return;
}
@@ -411,7 +416,12 @@ void GRenderWindow::mouseMoveEvent(QMouseEvent* event) {
}
void GRenderWindow::mouseReleaseEvent(QMouseEvent* event) {
// touch input is handled in TouchEndEvent
if (!Settings::values.touchscreen.enabled) {
input_subsystem->GetKeyboard()->ReleaseKey(event->button());
return;
}
// Touch input is handled in TouchEndEvent
if (event->source() == Qt::MouseEventSynthesizedBySystem) {
return;
}

View File

@@ -5,6 +5,7 @@
#include <array>
#include <QKeySequence>
#include <QSettings>
#include "common/common_paths.h"
#include "common/file_util.h"
#include "core/hle/service/acc/profile_manager.h"
#include "core/hle/service/hid/controllers/npad.h"
@@ -14,14 +15,10 @@
namespace FS = Common::FS;
Config::Config(const std::string& config_file, bool is_global) {
// TODO: Don't hardcode the path; let the frontend decide where to put the config files.
qt_config_loc = FS::GetUserPath(FS::UserPath::ConfigDir) + config_file;
FS::CreateFullPath(qt_config_loc);
qt_config =
std::make_unique<QSettings>(QString::fromStdString(qt_config_loc), QSettings::IniFormat);
global = is_global;
Reload();
Config::Config(const std::string& config_name, ConfigType config_type) : type(config_type) {
global = config_type == ConfigType::GlobalConfig;
Initialize(config_name);
}
Config::~Config() {
@@ -242,84 +239,152 @@ const std::array<UISettings::Shortcut, 16> Config::default_hotkeys{{
}};
// clang-format on
void Config::ReadPlayerValues() {
for (std::size_t p = 0; p < Settings::values.players.size(); ++p) {
auto& player = Settings::values.players[p];
void Config::Initialize(const std::string& config_name) {
switch (type) {
case ConfigType::GlobalConfig:
qt_config_loc = fmt::format("{}" DIR_SEP "{}.ini", FS::GetUserPath(FS::UserPath::ConfigDir),
config_name);
FS::CreateFullPath(qt_config_loc);
qt_config = std::make_unique<QSettings>(QString::fromStdString(qt_config_loc),
QSettings::IniFormat);
Reload();
break;
case ConfigType::PerGameConfig:
qt_config_loc = fmt::format("{}custom" DIR_SEP "{}.ini",
FS::GetUserPath(FS::UserPath::ConfigDir), config_name);
FS::CreateFullPath(qt_config_loc);
qt_config = std::make_unique<QSettings>(QString::fromStdString(qt_config_loc),
QSettings::IniFormat);
Reload();
break;
case ConfigType::InputProfile:
qt_config_loc = fmt::format("{}input" DIR_SEP "{}.ini",
FS::GetUserPath(FS::UserPath::ConfigDir), config_name);
FS::CreateFullPath(qt_config_loc);
qt_config = std::make_unique<QSettings>(QString::fromStdString(qt_config_loc),
QSettings::IniFormat);
break;
}
}
player.connected =
ReadSetting(QStringLiteral("player_%1_connected").arg(p), false).toBool();
void Config::ReadPlayerValue(std::size_t player_index) {
const QString player_prefix = [this, player_index] {
if (type == ConfigType::InputProfile) {
return QString{};
} else {
return QStringLiteral("player_%1_").arg(player_index);
}
}();
player.controller_type = static_cast<Settings::ControllerType>(
auto& player = Settings::values.players.GetValue()[player_index];
if (player_prefix.isEmpty()) {
const auto controller = static_cast<Settings::ControllerType>(
qt_config
->value(QStringLiteral("player_%1_type").arg(p),
->value(QStringLiteral("%1type").arg(player_prefix),
static_cast<u8>(Settings::ControllerType::ProController))
.toUInt());
if (controller == Settings::ControllerType::LeftJoycon ||
controller == Settings::ControllerType::RightJoycon) {
player.controller_type = controller;
}
} else {
player.connected =
ReadSetting(QStringLiteral("%1connected").arg(player_prefix), player_index == 0)
.toBool();
player.controller_type = static_cast<Settings::ControllerType>(
qt_config
->value(QStringLiteral("%1type").arg(player_prefix),
static_cast<u8>(Settings::ControllerType::ProController))
.toUInt());
player.vibration_enabled =
qt_config->value(QStringLiteral("%1vibration_enabled").arg(player_prefix), true)
.toBool();
player.vibration_strength =
qt_config->value(QStringLiteral("%1vibration_strength").arg(player_prefix), 100)
.toInt();
player.body_color_left = qt_config
->value(QStringLiteral("player_%1_body_color_left").arg(p),
->value(QStringLiteral("%1body_color_left").arg(player_prefix),
Settings::JOYCON_BODY_NEON_BLUE)
.toUInt();
player.body_color_right = qt_config
->value(QStringLiteral("player_%1_body_color_right").arg(p),
Settings::JOYCON_BODY_NEON_RED)
.toUInt();
player.button_color_left = qt_config
->value(QStringLiteral("player_%1_button_color_left").arg(p),
Settings::JOYCON_BUTTONS_NEON_BLUE)
.toUInt();
player.body_color_right =
qt_config
->value(QStringLiteral("%1body_color_right").arg(player_prefix),
Settings::JOYCON_BODY_NEON_RED)
.toUInt();
player.button_color_left =
qt_config
->value(QStringLiteral("%1button_color_left").arg(player_prefix),
Settings::JOYCON_BUTTONS_NEON_BLUE)
.toUInt();
player.button_color_right =
qt_config
->value(QStringLiteral("player_%1_button_color_right").arg(p),
->value(QStringLiteral("%1button_color_right").arg(player_prefix),
Settings::JOYCON_BUTTONS_NEON_RED)
.toUInt();
}
for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
const std::string default_param =
InputCommon::GenerateKeyboardParam(default_buttons[i]);
auto& player_buttons = player.buttons[i];
for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
auto& player_buttons = player.buttons[i];
player_buttons = qt_config
->value(QStringLiteral("player_%1_").arg(p) +
QString::fromUtf8(Settings::NativeButton::mapping[i]),
QString::fromStdString(default_param))
.toString()
.toStdString();
if (player_buttons.empty()) {
player_buttons = default_param;
}
player_buttons = qt_config
->value(QStringLiteral("%1").arg(player_prefix) +
QString::fromUtf8(Settings::NativeButton::mapping[i]),
QString::fromStdString(default_param))
.toString()
.toStdString();
if (player_buttons.empty()) {
player_buttons = default_param;
}
}
for (int i = 0; i < Settings::NativeMotion::NumMotions; ++i) {
const std::string default_param =
InputCommon::GenerateKeyboardParam(default_motions[i]);
auto& player_motions = player.motions[i];
for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
default_analogs[i][3], default_stick_mod[i], 0.5f);
auto& player_analogs = player.analogs[i];
player_motions = qt_config
->value(QStringLiteral("player_%1_").arg(p) +
QString::fromUtf8(Settings::NativeMotion::mapping[i]),
QString::fromStdString(default_param))
.toString()
.toStdString();
if (player_motions.empty()) {
player_motions = default_param;
}
player_analogs = qt_config
->value(QStringLiteral("%1").arg(player_prefix) +
QString::fromUtf8(Settings::NativeAnalog::mapping[i]),
QString::fromStdString(default_param))
.toString()
.toStdString();
if (player_analogs.empty()) {
player_analogs = default_param;
}
}
for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
default_analogs[i][3], default_stick_mod[i], 0.5f);
auto& player_analogs = player.analogs[i];
for (int i = 0; i < Settings::NativeVibration::NumVibrations; ++i) {
auto& player_vibrations = player.vibrations[i];
player_analogs = qt_config
->value(QStringLiteral("player_%1_").arg(p) +
QString::fromUtf8(Settings::NativeAnalog::mapping[i]),
QString::fromStdString(default_param))
.toString()
.toStdString();
if (player_analogs.empty()) {
player_analogs = default_param;
}
player_vibrations =
qt_config
->value(QStringLiteral("%1").arg(player_prefix) +
QString::fromUtf8(Settings::NativeVibration::mapping[i]),
QString{})
.toString()
.toStdString();
}
for (int i = 0; i < Settings::NativeMotion::NumMotions; ++i) {
const std::string default_param = InputCommon::GenerateKeyboardParam(default_motions[i]);
auto& player_motions = player.motions[i];
player_motions = qt_config
->value(QStringLiteral("%1").arg(player_prefix) +
QString::fromUtf8(Settings::NativeMotion::mapping[i]),
QString::fromStdString(default_param))
.toString()
.toStdString();
if (player_motions.empty()) {
player_motions = default_param;
}
}
}
@@ -436,18 +501,21 @@ void Config::ReadAudioValues() {
void Config::ReadControlValues() {
qt_config->beginGroup(QStringLiteral("Controls"));
ReadPlayerValues();
for (std::size_t p = 0; p < Settings::values.players.GetValue().size(); ++p) {
ReadPlayerValue(p);
}
ReadDebugValues();
ReadKeyboardValues();
ReadMouseValues();
ReadTouchscreenValues();
ReadMotionTouchValues();
Settings::values.vibration_enabled =
ReadSetting(QStringLiteral("vibration_enabled"), true).toBool();
Settings::values.motion_enabled = ReadSetting(QStringLiteral("motion_enabled"), true).toBool();
Settings::values.use_docked_mode =
ReadSetting(QStringLiteral("use_docked_mode"), false).toBool();
ReadSettingGlobal(Settings::values.use_docked_mode, QStringLiteral("use_docked_mode"), false);
ReadSettingGlobal(Settings::values.vibration_enabled, QStringLiteral("vibration_enabled"),
true);
ReadSettingGlobal(Settings::values.enable_accurate_vibrations,
QStringLiteral("enable_accurate_vibrations"), false);
ReadSettingGlobal(Settings::values.motion_enabled, QStringLiteral("motion_enabled"), true);
qt_config->endGroup();
}
@@ -920,49 +988,64 @@ void Config::ReadValues() {
ReadSystemValues();
}
void Config::SavePlayerValues() {
for (std::size_t p = 0; p < Settings::values.players.size(); ++p) {
const auto& player = Settings::values.players[p];
void Config::SavePlayerValue(std::size_t player_index) {
const QString player_prefix = [this, player_index] {
if (type == ConfigType::InputProfile) {
return QString{};
} else {
return QStringLiteral("player_%1_").arg(player_index);
}
}();
WriteSetting(QStringLiteral("player_%1_connected").arg(p), player.connected, false);
WriteSetting(QStringLiteral("player_%1_type").arg(p),
static_cast<u8>(player.controller_type),
static_cast<u8>(Settings::ControllerType::ProController));
const auto& player = Settings::values.players.GetValue()[player_index];
WriteSetting(QStringLiteral("player_%1_body_color_left").arg(p), player.body_color_left,
WriteSetting(QStringLiteral("%1type").arg(player_prefix),
static_cast<u8>(player.controller_type),
static_cast<u8>(Settings::ControllerType::ProController));
if (!player_prefix.isEmpty()) {
WriteSetting(QStringLiteral("%1connected").arg(player_prefix), player.connected, false);
WriteSetting(QStringLiteral("%1vibration_enabled").arg(player_prefix),
player.vibration_enabled, true);
WriteSetting(QStringLiteral("%1vibration_strength").arg(player_prefix),
player.vibration_strength, 100);
WriteSetting(QStringLiteral("%1body_color_left").arg(player_prefix), player.body_color_left,
Settings::JOYCON_BODY_NEON_BLUE);
WriteSetting(QStringLiteral("player_%1_body_color_right").arg(p), player.body_color_right,
Settings::JOYCON_BODY_NEON_RED);
WriteSetting(QStringLiteral("player_%1_button_color_left").arg(p), player.button_color_left,
Settings::JOYCON_BUTTONS_NEON_BLUE);
WriteSetting(QStringLiteral("player_%1_button_color_right").arg(p),
WriteSetting(QStringLiteral("%1body_color_right").arg(player_prefix),
player.body_color_right, Settings::JOYCON_BODY_NEON_RED);
WriteSetting(QStringLiteral("%1button_color_left").arg(player_prefix),
player.button_color_left, Settings::JOYCON_BUTTONS_NEON_BLUE);
WriteSetting(QStringLiteral("%1button_color_right").arg(player_prefix),
player.button_color_right, Settings::JOYCON_BUTTONS_NEON_RED);
}
for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
const std::string default_param =
InputCommon::GenerateKeyboardParam(default_buttons[i]);
WriteSetting(QStringLiteral("player_%1_").arg(p) +
QString::fromStdString(Settings::NativeButton::mapping[i]),
QString::fromStdString(player.buttons[i]),
QString::fromStdString(default_param));
}
for (int i = 0; i < Settings::NativeMotion::NumMotions; ++i) {
const std::string default_param =
InputCommon::GenerateKeyboardParam(default_motions[i]);
WriteSetting(QStringLiteral("player_%1_").arg(p) +
QString::fromStdString(Settings::NativeMotion::mapping[i]),
QString::fromStdString(player.motions[i]),
QString::fromStdString(default_param));
}
for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
default_analogs[i][3], default_stick_mod[i], 0.5f);
WriteSetting(QStringLiteral("player_%1_").arg(p) +
QString::fromStdString(Settings::NativeAnalog::mapping[i]),
QString::fromStdString(player.analogs[i]),
QString::fromStdString(default_param));
}
for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
WriteSetting(QStringLiteral("%1").arg(player_prefix) +
QString::fromStdString(Settings::NativeButton::mapping[i]),
QString::fromStdString(player.buttons[i]),
QString::fromStdString(default_param));
}
for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
default_analogs[i][3], default_stick_mod[i], 0.5f);
WriteSetting(QStringLiteral("%1").arg(player_prefix) +
QString::fromStdString(Settings::NativeAnalog::mapping[i]),
QString::fromStdString(player.analogs[i]),
QString::fromStdString(default_param));
}
for (int i = 0; i < Settings::NativeVibration::NumVibrations; ++i) {
WriteSetting(QStringLiteral("%1").arg(player_prefix) +
QString::fromStdString(Settings::NativeVibration::mapping[i]),
QString::fromStdString(player.vibrations[i]), QString{});
}
for (int i = 0; i < Settings::NativeMotion::NumMotions; ++i) {
const std::string default_param = InputCommon::GenerateKeyboardParam(default_motions[i]);
WriteSetting(QStringLiteral("%1").arg(player_prefix) +
QString::fromStdString(Settings::NativeMotion::mapping[i]),
QString::fromStdString(player.motions[i]),
QString::fromStdString(default_param));
}
}
@@ -1087,14 +1170,20 @@ void Config::SaveAudioValues() {
void Config::SaveControlValues() {
qt_config->beginGroup(QStringLiteral("Controls"));
SavePlayerValues();
for (std::size_t p = 0; p < Settings::values.players.GetValue().size(); ++p) {
SavePlayerValue(p);
}
SaveDebugValues();
SaveMouseValues();
SaveTouchscreenValues();
SaveMotionTouchValues();
WriteSetting(QStringLiteral("vibration_enabled"), Settings::values.vibration_enabled, true);
WriteSetting(QStringLiteral("motion_enabled"), Settings::values.motion_enabled, true);
WriteSettingGlobal(QStringLiteral("use_docked_mode"), Settings::values.use_docked_mode, false);
WriteSettingGlobal(QStringLiteral("vibration_enabled"), Settings::values.vibration_enabled,
true);
WriteSettingGlobal(QStringLiteral("enable_accurate_vibrations"),
Settings::values.enable_accurate_vibrations, false);
WriteSettingGlobal(QStringLiteral("motion_enabled"), Settings::values.motion_enabled, true);
WriteSetting(QStringLiteral("motion_device"),
QString::fromStdString(Settings::values.motion_device),
QStringLiteral("engine:motion_emu,update_period:100,sensitivity:0.01"));
@@ -1102,7 +1191,6 @@ void Config::SaveControlValues() {
QString::fromStdString(Settings::values.touch_device),
QStringLiteral("engine:emu_window"));
WriteSetting(QStringLiteral("keyboard_enabled"), Settings::values.keyboard_enabled, false);
WriteSetting(QStringLiteral("use_docked_mode"), Settings::values.use_docked_mode, false);
qt_config->endGroup();
}
@@ -1515,3 +1603,19 @@ void Config::Save() {
Settings::Sanitize();
SaveValues();
}
void Config::ReadControlPlayerValue(std::size_t player_index) {
qt_config->beginGroup(QStringLiteral("Controls"));
ReadPlayerValue(player_index);
qt_config->endGroup();
}
void Config::SaveControlPlayerValue(std::size_t player_index) {
qt_config->beginGroup(QStringLiteral("Controls"));
SavePlayerValue(player_index);
qt_config->endGroup();
}
const std::string& Config::GetConfigFilePath() const {
return qt_config_loc;
}

View File

@@ -16,12 +16,24 @@ class QSettings;
class Config {
public:
explicit Config(const std::string& config_loc = "qt-config.ini", bool is_global = true);
enum class ConfigType {
GlobalConfig,
PerGameConfig,
InputProfile,
};
explicit Config(const std::string& config_name = "qt-config",
ConfigType config_type = ConfigType::GlobalConfig);
~Config();
void Reload();
void Save();
void ReadControlPlayerValue(std::size_t player_index);
void SaveControlPlayerValue(std::size_t player_index);
const std::string& GetConfigFilePath() const;
static const std::array<int, Settings::NativeButton::NumButtons> default_buttons;
static const std::array<int, Settings::NativeMotion::NumMotions> default_motions;
static const std::array<std::array<int, 4>, Settings::NativeAnalog::NumAnalogs> default_analogs;
@@ -33,8 +45,10 @@ public:
static const std::array<UISettings::Shortcut, 16> default_hotkeys;
private:
void Initialize(const std::string& config_name);
void ReadValues();
void ReadPlayerValues();
void ReadPlayerValue(std::size_t player_index);
void ReadDebugValues();
void ReadKeyboardValues();
void ReadMouseValues();
@@ -62,7 +76,7 @@ private:
void ReadWebServiceValues();
void SaveValues();
void SavePlayerValues();
void SavePlayerValue(std::size_t player_index);
void SaveDebugValues();
void SaveMouseValues();
void SaveTouchscreenValues();
@@ -111,9 +125,9 @@ private:
void WriteSettingGlobal(const QString& name, const QVariant& value, bool use_global,
const QVariant& default_value);
ConfigType type;
std::unique_ptr<QSettings> qt_config;
std::string qt_config_loc;
bool global;
};

View File

@@ -275,32 +275,12 @@
<signal>accepted()</signal>
<receiver>ConfigureDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>220</x>
<y>380</y>
</hint>
<hint type="destinationlabel">
<x>220</x>
<y>200</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>ConfigureDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>220</x>
<y>380</y>
</hint>
<hint type="destinationlabel">
<x>220</x>
<y>200</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@@ -4,11 +4,14 @@
#include "ui_configure_debug_controller.h"
#include "yuzu/configuration/configure_debug_controller.h"
#include "yuzu/configuration/configure_input_player.h"
ConfigureDebugController::ConfigureDebugController(QWidget* parent,
InputCommon::InputSubsystem* input_subsystem)
InputCommon::InputSubsystem* input_subsystem,
InputProfiles* profiles)
: QDialog(parent), ui(std::make_unique<Ui::ConfigureDebugController>()),
debug_controller(new ConfigureInputPlayer(this, 9, nullptr, input_subsystem, true)) {
debug_controller(
new ConfigureInputPlayer(this, 9, nullptr, input_subsystem, profiles, true)) {
ui->setupUi(this);
ui->controllerLayout->addWidget(debug_controller);

View File

@@ -6,10 +6,13 @@
#include <memory>
#include <QDialog>
#include "yuzu/configuration/configure_input_player.h"
class QPushButton;
class ConfigureInputPlayer;
class InputProfiles;
namespace InputCommon {
class InputSubsystem;
}
@@ -22,8 +25,8 @@ class ConfigureDebugController : public QDialog {
Q_OBJECT
public:
explicit ConfigureDebugController(QWidget* parent,
InputCommon::InputSubsystem* input_subsystem);
explicit ConfigureDebugController(QWidget* parent, InputCommon::InputSubsystem* input_subsystem,
InputProfiles* profiles);
~ConfigureDebugController() override;
void ApplyConfiguration();

View File

@@ -66,32 +66,12 @@
<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

@@ -23,6 +23,8 @@
#include "yuzu/configuration/configure_motion_touch.h"
#include "yuzu/configuration/configure_mouse_advanced.h"
#include "yuzu/configuration/configure_touchscreen_advanced.h"
#include "yuzu/configuration/configure_vibration.h"
#include "yuzu/configuration/input_profiles.h"
namespace {
template <typename Dialog, typename... Args>
@@ -64,7 +66,8 @@ void OnDockedModeChanged(bool last_state, bool new_state) {
}
ConfigureInput::ConfigureInput(QWidget* parent)
: QWidget(parent), ui(std::make_unique<Ui::ConfigureInput>()) {
: QWidget(parent), ui(std::make_unique<Ui::ConfigureInput>()),
profiles(std::make_unique<InputProfiles>()) {
ui->setupUi(this);
}
@@ -73,14 +76,22 @@ ConfigureInput::~ConfigureInput() = default;
void ConfigureInput::Initialize(InputCommon::InputSubsystem* input_subsystem,
std::size_t max_players) {
player_controllers = {
new ConfigureInputPlayer(this, 0, ui->consoleInputSettings, input_subsystem),
new ConfigureInputPlayer(this, 1, ui->consoleInputSettings, input_subsystem),
new ConfigureInputPlayer(this, 2, ui->consoleInputSettings, input_subsystem),
new ConfigureInputPlayer(this, 3, ui->consoleInputSettings, input_subsystem),
new ConfigureInputPlayer(this, 4, ui->consoleInputSettings, input_subsystem),
new ConfigureInputPlayer(this, 5, ui->consoleInputSettings, input_subsystem),
new ConfigureInputPlayer(this, 6, ui->consoleInputSettings, input_subsystem),
new ConfigureInputPlayer(this, 7, ui->consoleInputSettings, input_subsystem),
new ConfigureInputPlayer(this, 0, ui->consoleInputSettings, input_subsystem,
profiles.get()),
new ConfigureInputPlayer(this, 1, ui->consoleInputSettings, input_subsystem,
profiles.get()),
new ConfigureInputPlayer(this, 2, ui->consoleInputSettings, input_subsystem,
profiles.get()),
new ConfigureInputPlayer(this, 3, ui->consoleInputSettings, input_subsystem,
profiles.get()),
new ConfigureInputPlayer(this, 4, ui->consoleInputSettings, input_subsystem,
profiles.get()),
new ConfigureInputPlayer(this, 5, ui->consoleInputSettings, input_subsystem,
profiles.get()),
new ConfigureInputPlayer(this, 6, ui->consoleInputSettings, input_subsystem,
profiles.get()),
new ConfigureInputPlayer(this, 7, ui->consoleInputSettings, input_subsystem,
profiles.get()),
};
player_tabs = {
@@ -113,8 +124,10 @@ void ConfigureInput::Initialize(InputCommon::InputSubsystem* input_subsystem,
}
}
});
connect(player_controllers[i], &ConfigureInputPlayer::RefreshInputDevices,
[this] { UpdateAllInputDevices(); });
connect(player_controllers[i], &ConfigureInputPlayer::RefreshInputDevices, this,
&ConfigureInput::UpdateAllInputDevices);
connect(player_controllers[i], &ConfigureInputPlayer::RefreshInputProfiles, this,
&ConfigureInput::UpdateAllInputProfiles, Qt::QueuedConnection);
connect(player_connected[i], &QCheckBox::stateChanged, [this, i](int state) {
player_controllers[i]->ConnectPlayer(state == Qt::Checked);
});
@@ -134,7 +147,7 @@ void ConfigureInput::Initialize(InputCommon::InputSubsystem* input_subsystem,
ui->tabAdvanced->setLayout(new QHBoxLayout(ui->tabAdvanced));
ui->tabAdvanced->layout()->addWidget(advanced);
connect(advanced, &ConfigureInputAdvanced::CallDebugControllerDialog, [this, input_subsystem] {
CallConfigureDialog<ConfigureDebugController>(*this, input_subsystem);
CallConfigureDialog<ConfigureDebugController>(*this, input_subsystem, profiles.get());
});
connect(advanced, &ConfigureInputAdvanced::CallMouseConfigDialog, [this, input_subsystem] {
CallConfigureDialog<ConfigureMouseAdvanced>(*this, input_subsystem);
@@ -146,6 +159,9 @@ void ConfigureInput::Initialize(InputCommon::InputSubsystem* input_subsystem,
CallConfigureDialog<ConfigureMotionTouch>(*this, input_subsystem);
});
connect(ui->vibrationButton, &QPushButton::clicked,
[this] { CallConfigureDialog<ConfigureVibration>(*this); });
connect(ui->motionButton, &QPushButton::clicked, [this, input_subsystem] {
CallConfigureDialog<ConfigureMotionTouch>(*this, input_subsystem);
});
@@ -171,12 +187,12 @@ void ConfigureInput::ApplyConfiguration() {
advanced->ApplyConfiguration();
const bool pre_docked_mode = Settings::values.use_docked_mode;
Settings::values.use_docked_mode = ui->radioDocked->isChecked();
OnDockedModeChanged(pre_docked_mode, Settings::values.use_docked_mode);
const bool pre_docked_mode = Settings::values.use_docked_mode.GetValue();
Settings::values.use_docked_mode.SetValue(ui->radioDocked->isChecked());
OnDockedModeChanged(pre_docked_mode, Settings::values.use_docked_mode.GetValue());
Settings::values.vibration_enabled = ui->vibrationGroup->isChecked();
Settings::values.motion_enabled = ui->motionGroup->isChecked();
Settings::values.vibration_enabled.SetValue(ui->vibrationGroup->isChecked());
Settings::values.motion_enabled.SetValue(ui->motionGroup->isChecked());
}
void ConfigureInput::changeEvent(QEvent* event) {
@@ -193,16 +209,16 @@ void ConfigureInput::RetranslateUI() {
void ConfigureInput::LoadConfiguration() {
LoadPlayerControllerIndices();
UpdateDockedState(Settings::values.players[8].connected);
UpdateDockedState(Settings::values.players.GetValue()[8].connected);
ui->vibrationGroup->setChecked(Settings::values.vibration_enabled);
ui->motionGroup->setChecked(Settings::values.motion_enabled);
ui->vibrationGroup->setChecked(Settings::values.vibration_enabled.GetValue());
ui->motionGroup->setChecked(Settings::values.motion_enabled.GetValue());
}
void ConfigureInput::LoadPlayerControllerIndices() {
for (std::size_t i = 0; i < player_connected.size(); ++i) {
const auto connected = Settings::values.players[i].connected ||
(i == 0 && Settings::values.players[8].connected);
const auto connected = Settings::values.players.GetValue()[i].connected ||
(i == 0 && Settings::values.players.GetValue()[8].connected);
player_connected[i]->setChecked(connected);
}
}
@@ -231,8 +247,8 @@ void ConfigureInput::UpdateDockedState(bool is_handheld) {
ui->radioDocked->setEnabled(!is_handheld);
ui->radioUndocked->setEnabled(!is_handheld);
ui->radioDocked->setChecked(Settings::values.use_docked_mode);
ui->radioUndocked->setChecked(!Settings::values.use_docked_mode);
ui->radioDocked->setChecked(Settings::values.use_docked_mode.GetValue());
ui->radioUndocked->setChecked(!Settings::values.use_docked_mode.GetValue());
// Also force into undocked mode if the controller type is handheld.
if (is_handheld) {
@@ -242,6 +258,16 @@ void ConfigureInput::UpdateDockedState(bool is_handheld) {
void ConfigureInput::UpdateAllInputDevices() {
for (const auto& player : player_controllers) {
player->UpdateInputDevices();
player->UpdateInputDeviceCombobox();
}
}
void ConfigureInput::UpdateAllInputProfiles(std::size_t player_index) {
for (std::size_t i = 0; i < player_controllers.size(); ++i) {
if (i == player_index) {
continue;
}
player_controllers[i]->UpdateInputProfiles();
}
}

View File

@@ -8,17 +8,18 @@
#include <memory>
#include <QKeyEvent>
#include <QList>
#include <QWidget>
#include "yuzu/configuration/configure_input_advanced.h"
#include "yuzu/configuration/configure_input_player.h"
#include "ui_configure_input.h"
class QCheckBox;
class QString;
class QTimer;
class ConfigureInputAdvanced;
class ConfigureInputPlayer;
class InputProfiles;
namespace InputCommon {
class InputSubsystem;
}
@@ -51,6 +52,7 @@ private:
void UpdateDockedState(bool is_handheld);
void UpdateAllInputDevices();
void UpdateAllInputProfiles(std::size_t player_index);
/// Load configuration settings.
void LoadConfiguration();
@@ -61,6 +63,8 @@ private:
std::unique_ptr<Ui::ConfigureInput> ui;
std::unique_ptr<InputProfiles> profiles;
std::array<ConfigureInputPlayer*, 8> player_controllers;
std::array<QWidget*, 8> player_tabs;
std::array<QCheckBox*, 8> player_connected;

View File

@@ -6,7 +6,7 @@
<rect>
<x>0</x>
<y>0</y>
<width>700</width>
<width>680</width>
<height>540</height>
</rect>
</property>
@@ -142,7 +142,7 @@
<number>6</number>
</property>
<property name="leftMargin">
<number>3</number>
<number>8</number>
</property>
<property name="topMargin">
<number>6</number>
@@ -195,30 +195,24 @@
<number>3</number>
</property>
<item>
<widget class="QSpinBox" name="vibrationSpin">
<widget class="QPushButton" name="vibrationButton">
<property name="minimumSize">
<size>
<width>65</width>
<height>21</height>
<width>68</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>65</width>
<width>68</width>
<height>16777215</height>
</size>
</property>
<property name="suffix">
<string>%</string>
<property name="styleSheet">
<string notr="true">min-width: 68px;</string>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>200</number>
</property>
<property name="value">
<number>100</number>
<property name="text">
<string>Configure</string>
</property>
</widget>
</item>
@@ -250,18 +244,18 @@
<widget class="QPushButton" name="motionButton">
<property name="minimumSize">
<size>
<width>57</width>
<width>68</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>55</width>
<width>68</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">min-width: 55px;</string>
<string notr="true">min-width: 68px;</string>
</property>
<property name="text">
<string>Configure</string>
@@ -272,7 +266,7 @@
</widget>
</item>
<item alignment="Qt::AlignVCenter">
<widget class="QWidget" name="widget" native="true">
<widget class="QWidget" name="connectedControllers" native="true">
<layout class="QGridLayout" name="gridLayout_2">
<property name="leftMargin">
<number>5</number>
@@ -468,13 +462,13 @@
</property>
<property name="minimumSize">
<size>
<width>57</width>
<width>68</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>55</width>
<width>68</width>
<height>16777215</height>
</size>
</property>
@@ -494,7 +488,7 @@
<enum>Qt::LeftToRight</enum>
</property>
<property name="styleSheet">
<string notr="true">min-width: 55px;</string>
<string notr="true">min-width: 68px;</string>
</property>
<property name="text">
<string>Defaults</string>
@@ -511,13 +505,13 @@
</property>
<property name="minimumSize">
<size>
<width>57</width>
<width>68</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>55</width>
<width>68</width>
<height>16777215</height>
</size>
</property>
@@ -537,7 +531,7 @@
<enum>Qt::LeftToRight</enum>
</property>
<property name="styleSheet">
<string notr="true">min-width: 55px;</string>
<string notr="true">min-width: 68px;</string>
</property>
<property name="text">
<string>Clear</string>

View File

@@ -68,8 +68,7 @@ ConfigureInputAdvanced::ConfigureInputAdvanced(QWidget* parent)
for (std::size_t button_idx = 0; button_idx < color_buttons.size(); ++button_idx) {
connect(color_buttons[button_idx], &QPushButton::clicked, this,
[this, player_idx, button_idx] {
OnControllerButtonClick(static_cast<int>(player_idx),
static_cast<int>(button_idx));
OnControllerButtonClick(player_idx, button_idx);
});
}
}
@@ -94,20 +93,21 @@ ConfigureInputAdvanced::ConfigureInputAdvanced(QWidget* parent)
ConfigureInputAdvanced::~ConfigureInputAdvanced() = default;
void ConfigureInputAdvanced::OnControllerButtonClick(int player_idx, int button_idx) {
void ConfigureInputAdvanced::OnControllerButtonClick(std::size_t player_idx,
std::size_t button_idx) {
const QColor new_bg_color = QColorDialog::getColor(controllers_colors[player_idx][button_idx]);
if (!new_bg_color.isValid()) {
return;
}
controllers_colors[player_idx][button_idx] = new_bg_color;
controllers_color_buttons[player_idx][button_idx]->setStyleSheet(
QStringLiteral("background-color: %1; min-width: 55px;")
QStringLiteral("background-color: %1; min-width: 60px;")
.arg(controllers_colors[player_idx][button_idx].name()));
}
void ConfigureInputAdvanced::ApplyConfiguration() {
for (std::size_t player_idx = 0; player_idx < controllers_color_buttons.size(); ++player_idx) {
auto& player = Settings::values.players[player_idx];
auto& player = Settings::values.players.GetValue()[player_idx];
std::array<u32, 4> colors{};
std::transform(controllers_colors[player_idx].begin(), controllers_colors[player_idx].end(),
colors.begin(), [](QColor color) { return color.rgb(); });
@@ -126,7 +126,7 @@ void ConfigureInputAdvanced::ApplyConfiguration() {
void ConfigureInputAdvanced::LoadConfiguration() {
for (std::size_t player_idx = 0; player_idx < controllers_color_buttons.size(); ++player_idx) {
auto& player = Settings::values.players[player_idx];
auto& player = Settings::values.players.GetValue()[player_idx];
std::array<u32, 4> colors = {
player.body_color_left,
player.button_color_left,
@@ -139,7 +139,7 @@ void ConfigureInputAdvanced::LoadConfiguration() {
for (std::size_t button_idx = 0; button_idx < colors.size(); ++button_idx) {
controllers_color_buttons[player_idx][button_idx]->setStyleSheet(
QStringLiteral("background-color: %1; min-width: 55px;")
QStringLiteral("background-color: %1; min-width: 60px;")
.arg(controllers_colors[player_idx][button_idx].name()));
}
}

View File

@@ -35,7 +35,7 @@ private:
void RetranslateUI();
void UpdateUIEnabled();
void OnControllerButtonClick(int player_idx, int button_idx);
void OnControllerButtonClick(std::size_t player_idx, std::size_t button_idx);
void LoadConfiguration();

View File

@@ -192,18 +192,18 @@
</property>
<property name="minimumSize">
<size>
<width>57</width>
<width>68</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>55</width>
<width>68</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">min-width: 55px;</string>
<string notr="true">min-width: 68px;</string>
</property>
<property name="text">
<string/>
@@ -247,18 +247,18 @@
</property>
<property name="minimumSize">
<size>
<width>57</width>
<width>68</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>55</width>
<width>68</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">min-width: 55px;</string>
<string notr="true">min-width: 68px;</string>
</property>
<property name="text">
<string/>
@@ -323,18 +323,18 @@
</property>
<property name="minimumSize">
<size>
<width>57</width>
<width>68</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>55</width>
<width>68</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">min-width: 55px;</string>
<string notr="true">min-width: 68px;</string>
</property>
<property name="text">
<string/>
@@ -378,18 +378,18 @@
</property>
<property name="minimumSize">
<size>
<width>57</width>
<width>68</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>55</width>
<width>68</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">min-width: 55px;</string>
<string notr="true">min-width: 68px;</string>
</property>
<property name="text">
<string/>
@@ -478,18 +478,18 @@
</property>
<property name="minimumSize">
<size>
<width>57</width>
<width>68</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>55</width>
<width>68</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">min-width: 55px;</string>
<string notr="true">min-width: 68px;</string>
</property>
<property name="text">
<string/>
@@ -533,18 +533,18 @@
</property>
<property name="minimumSize">
<size>
<width>57</width>
<width>68</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>55</width>
<width>68</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">min-width: 55px;</string>
<string notr="true">min-width: 68px;</string>
</property>
<property name="text">
<string/>
@@ -609,18 +609,18 @@
</property>
<property name="minimumSize">
<size>
<width>57</width>
<width>68</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>55</width>
<width>68</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">min-width: 55px;</string>
<string notr="true">min-width: 68px;</string>
</property>
<property name="text">
<string/>
@@ -664,18 +664,18 @@
</property>
<property name="minimumSize">
<size>
<width>57</width>
<width>68</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>55</width>
<width>68</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">min-width: 55px;</string>
<string notr="true">min-width: 68px;</string>
</property>
<property name="text">
<string/>
@@ -782,18 +782,18 @@
</property>
<property name="minimumSize">
<size>
<width>57</width>
<width>68</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>55</width>
<width>68</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">min-width: 55px;</string>
<string notr="true">min-width: 68px;</string>
</property>
<property name="text">
<string/>
@@ -837,18 +837,18 @@
</property>
<property name="minimumSize">
<size>
<width>57</width>
<width>68</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>55</width>
<width>68</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">min-width: 55px;</string>
<string notr="true">min-width: 68px;</string>
</property>
<property name="text">
<string/>
@@ -913,18 +913,18 @@
</property>
<property name="minimumSize">
<size>
<width>57</width>
<width>68</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>55</width>
<width>68</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">min-width: 55px;</string>
<string notr="true">min-width: 68px;</string>
</property>
<property name="text">
<string/>
@@ -968,18 +968,18 @@
</property>
<property name="minimumSize">
<size>
<width>57</width>
<width>68</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>55</width>
<width>68</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">min-width: 55px;</string>
<string notr="true">min-width: 68px;</string>
</property>
<property name="text">
<string/>
@@ -1068,18 +1068,18 @@
</property>
<property name="minimumSize">
<size>
<width>57</width>
<width>68</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>55</width>
<width>68</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">min-width: 55px;</string>
<string notr="true">min-width: 68px;</string>
</property>
<property name="text">
<string/>
@@ -1123,18 +1123,18 @@
</property>
<property name="minimumSize">
<size>
<width>57</width>
<width>68</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>55</width>
<width>68</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">min-width: 55px;</string>
<string notr="true">min-width: 68px;</string>
</property>
<property name="text">
<string/>
@@ -1199,18 +1199,18 @@
</property>
<property name="minimumSize">
<size>
<width>57</width>
<width>68</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>55</width>
<width>68</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">min-width: 55px;</string>
<string notr="true">min-width: 68px;</string>
</property>
<property name="text">
<string/>
@@ -1254,18 +1254,18 @@
</property>
<property name="minimumSize">
<size>
<width>57</width>
<width>68</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>55</width>
<width>68</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">min-width: 55px;</string>
<string notr="true">min-width: 68px;</string>
</property>
<property name="text">
<string/>
@@ -1393,18 +1393,18 @@
</property>
<property name="minimumSize">
<size>
<width>57</width>
<width>68</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>55</width>
<width>68</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">min-width: 55px;</string>
<string notr="true">min-width: 68px;</string>
</property>
<property name="text">
<string/>
@@ -1448,18 +1448,18 @@
</property>
<property name="minimumSize">
<size>
<width>57</width>
<width>68</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>55</width>
<width>68</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">min-width: 55px;</string>
<string notr="true">min-width: 68px;</string>
</property>
<property name="text">
<string/>
@@ -1524,18 +1524,18 @@
</property>
<property name="minimumSize">
<size>
<width>57</width>
<width>68</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>55</width>
<width>68</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">min-width: 55px;</string>
<string notr="true">min-width: 68px;</string>
</property>
<property name="text">
<string/>
@@ -1579,18 +1579,18 @@
</property>
<property name="minimumSize">
<size>
<width>57</width>
<width>68</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>55</width>
<width>68</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">min-width: 55px;</string>
<string notr="true">min-width: 68px;</string>
</property>
<property name="text">
<string/>
@@ -1679,18 +1679,18 @@
</property>
<property name="minimumSize">
<size>
<width>57</width>
<width>68</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>55</width>
<width>68</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">min-width: 55px;</string>
<string notr="true">min-width: 68px;</string>
</property>
<property name="text">
<string/>
@@ -1734,18 +1734,18 @@
</property>
<property name="minimumSize">
<size>
<width>57</width>
<width>68</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>55</width>
<width>68</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">min-width: 55px;</string>
<string notr="true">min-width: 68px;</string>
</property>
<property name="text">
<string/>
@@ -1810,18 +1810,18 @@
</property>
<property name="minimumSize">
<size>
<width>57</width>
<width>68</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>55</width>
<width>68</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">min-width: 55px;</string>
<string notr="true">min-width: 68px;</string>
</property>
<property name="text">
<string/>
@@ -1865,18 +1865,18 @@
</property>
<property name="minimumSize">
<size>
<width>57</width>
<width>68</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>55</width>
<width>68</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">min-width: 55px;</string>
<string notr="true">min-width: 68px;</string>
</property>
<property name="text">
<string/>
@@ -1983,18 +1983,18 @@
</property>
<property name="minimumSize">
<size>
<width>57</width>
<width>68</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>55</width>
<width>68</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">min-width: 55px;</string>
<string notr="true">min-width: 68px;</string>
</property>
<property name="text">
<string/>
@@ -2038,18 +2038,18 @@
</property>
<property name="minimumSize">
<size>
<width>57</width>
<width>68</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>55</width>
<width>68</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">min-width: 55px;</string>
<string notr="true">min-width: 68px;</string>
</property>
<property name="text">
<string/>
@@ -2114,18 +2114,18 @@
</property>
<property name="minimumSize">
<size>
<width>57</width>
<width>68</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>55</width>
<width>68</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">min-width: 55px;</string>
<string notr="true">min-width: 68px;</string>
</property>
<property name="text">
<string/>
@@ -2169,18 +2169,18 @@
</property>
<property name="minimumSize">
<size>
<width>57</width>
<width>68</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>55</width>
<width>68</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">min-width: 55px;</string>
<string notr="true">min-width: 68px;</string>
</property>
<property name="text">
<string/>
@@ -2269,18 +2269,18 @@
</property>
<property name="minimumSize">
<size>
<width>57</width>
<width>68</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>55</width>
<width>68</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">min-width: 55px;</string>
<string notr="true">min-width: 68px;</string>
</property>
<property name="text">
<string/>
@@ -2324,18 +2324,18 @@
</property>
<property name="minimumSize">
<size>
<width>57</width>
<width>68</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>55</width>
<width>68</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">min-width: 55px;</string>
<string notr="true">min-width: 68px;</string>
</property>
<property name="text">
<string/>
@@ -2400,18 +2400,18 @@
</property>
<property name="minimumSize">
<size>
<width>57</width>
<width>68</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>55</width>
<width>68</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">min-width: 55px;</string>
<string notr="true">min-width: 68px;</string>
</property>
<property name="text">
<string/>
@@ -2455,18 +2455,18 @@
</property>
<property name="minimumSize">
<size>
<width>57</width>
<width>68</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>55</width>
<width>68</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">min-width: 55px;</string>
<string notr="true">min-width: 68px;</string>
</property>
<property name="text">
<string/>

View File

@@ -1,37 +0,0 @@
// Copyright 2020 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "ui_configure_input_dialog.h"
#include "yuzu/configuration/configure_input_dialog.h"
ConfigureInputDialog::ConfigureInputDialog(QWidget* parent, std::size_t max_players,
InputCommon::InputSubsystem* input_subsystem)
: QDialog(parent), ui(std::make_unique<Ui::ConfigureInputDialog>()),
input_widget(new ConfigureInput(this)) {
ui->setupUi(this);
input_widget->Initialize(input_subsystem, max_players);
ui->inputLayout->addWidget(input_widget);
RetranslateUI();
}
ConfigureInputDialog::~ConfigureInputDialog() = default;
void ConfigureInputDialog::ApplyConfiguration() {
input_widget->ApplyConfiguration();
}
void ConfigureInputDialog::changeEvent(QEvent* event) {
if (event->type() == QEvent::LanguageChange) {
RetranslateUI();
}
QDialog::changeEvent(event);
}
void ConfigureInputDialog::RetranslateUI() {
ui->retranslateUi(this);
}

View File

@@ -1,38 +0,0 @@
// Copyright 2020 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <memory>
#include <QDialog>
#include "yuzu/configuration/configure_input.h"
class QPushButton;
namespace InputCommon {
class InputSubsystem;
}
namespace Ui {
class ConfigureInputDialog;
}
class ConfigureInputDialog : public QDialog {
Q_OBJECT
public:
explicit ConfigureInputDialog(QWidget* parent, std::size_t max_players,
InputCommon::InputSubsystem* input_subsystem);
~ConfigureInputDialog() override;
void ApplyConfiguration();
private:
void changeEvent(QEvent* event) override;
void RetranslateUI();
std::unique_ptr<Ui::ConfigureInputDialog> ui;
ConfigureInput* input_widget;
};

View File

@@ -4,6 +4,7 @@
#include <algorithm>
#include <memory>
#include <thread>
#include <utility>
#include <QGridLayout>
#include <QInputDialog>
@@ -22,8 +23,9 @@
#include "ui_configure_input_player.h"
#include "yuzu/configuration/config.h"
#include "yuzu/configuration/configure_input_player.h"
constexpr std::size_t HANDHELD_INDEX = 8;
#include "yuzu/configuration/configure_vibration.h"
#include "yuzu/configuration/input_profiles.h"
#include "yuzu/util/limitable_input_dialog.h"
const std::array<std::string, ConfigureInputPlayer::ANALOG_SUB_BUTTONS_NUM>
ConfigureInputPlayer::analog_sub_buttons{{
@@ -35,6 +37,8 @@ const std::array<std::string, ConfigureInputPlayer::ANALOG_SUB_BUTTONS_NUM>
namespace {
constexpr std::size_t HANDHELD_INDEX = 8;
void UpdateController(Settings::ControllerType controller_type, std::size_t npad_index,
bool connected) {
Core::System& system{Core::System::GetInstance()};
@@ -240,10 +244,11 @@ QString AnalogToText(const Common::ParamPackage& param, const std::string& dir)
ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_index,
QWidget* bottom_row,
InputCommon::InputSubsystem* input_subsystem_,
bool debug)
InputProfiles* profiles_, bool debug)
: QWidget(parent), ui(std::make_unique<Ui::ConfigureInputPlayer>()), player_index(player_index),
debug(debug), input_subsystem{input_subsystem_}, timeout_timer(std::make_unique<QTimer>()),
poll_timer(std::make_unique<QTimer>()), bottom_row(bottom_row) {
debug(debug), input_subsystem{input_subsystem_}, profiles(profiles_),
timeout_timer(std::make_unique<QTimer>()), poll_timer(std::make_unique<QTimer>()),
bottom_row(bottom_row) {
ui->setupUi(this);
setFocusPolicy(Qt::ClickFocus);
@@ -366,6 +371,18 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
}
connect(analog_button, &QPushButton::clicked, [=, this] {
if (!map_analog_stick_accepted) {
map_analog_stick_accepted =
QMessageBox::information(
this, tr("Map Analog Stick"),
tr("After pressing OK, first move your joystick horizontally, and then "
"vertically.\nTo invert the axes, first move your joystick "
"vertically, and then horizontally."),
QMessageBox::Ok | QMessageBox::Cancel) == QMessageBox::Ok;
if (!map_analog_stick_accepted) {
return;
}
}
HandleClick(
analog_map_buttons[analog_id][sub_button_id],
[=, this](const Common::ParamPackage& params) {
@@ -455,11 +472,14 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
});
}
if (debug || player_index == 9) {
ui->groupConnectedController->setCheckable(false);
}
// The Debug Controller can only choose the Pro Controller.
if (debug) {
ui->buttonScreenshot->setEnabled(false);
ui->buttonHome->setEnabled(false);
ui->groupConnectedController->setCheckable(false);
QStringList debug_controller_types = {
tr("Pro Controller"),
};
@@ -477,11 +497,12 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
UpdateMotionButtons();
});
connect(ui->comboDevices, qOverload<int>(&QComboBox::currentIndexChanged), this,
connect(ui->comboDevices, qOverload<int>(&QComboBox::activated), this,
&ConfigureInputPlayer::UpdateMappingWithDefaults);
ui->comboDevices->setCurrentIndex(-1);
ui->buttonRefreshDevices->setIcon(QIcon::fromTheme(QStringLiteral("view-refresh")));
UpdateInputDevices();
connect(ui->buttonRefreshDevices, &QPushButton::clicked,
[this] { emit RefreshInputDevices(); });
@@ -492,14 +513,14 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
Common::ParamPackage params;
if (input_subsystem->GetGCButtons()->IsPolling()) {
params = input_subsystem->GetGCButtons()->GetNextInput();
if (params.Has("engine")) {
if (params.Has("engine") && IsInputAcceptable(params)) {
SetPollingResult(params, false);
return;
}
}
if (input_subsystem->GetGCAnalogs()->IsPolling()) {
params = input_subsystem->GetGCAnalogs()->GetNextInput();
if (params.Has("engine")) {
if (params.Has("engine") && IsInputAcceptable(params)) {
SetPollingResult(params, false);
return;
}
@@ -513,13 +534,24 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
}
for (auto& poller : device_pollers) {
params = poller->GetNextInput();
if (params.Has("engine")) {
if (params.Has("engine") && IsInputAcceptable(params)) {
SetPollingResult(params, false);
return;
}
}
});
UpdateInputProfiles();
connect(ui->buttonProfilesNew, &QPushButton::clicked, this,
&ConfigureInputPlayer::CreateProfile);
connect(ui->buttonProfilesDelete, &QPushButton::clicked, this,
&ConfigureInputPlayer::DeleteProfile);
connect(ui->comboProfiles, qOverload<int>(&QComboBox::activated), this,
&ConfigureInputPlayer::LoadProfile);
connect(ui->buttonProfilesSave, &QPushButton::clicked, this,
&ConfigureInputPlayer::SaveProfile);
LoadConfiguration();
// TODO(wwylele): enable this when we actually emulate it
@@ -529,7 +561,7 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
ConfigureInputPlayer::~ConfigureInputPlayer() = default;
void ConfigureInputPlayer::ApplyConfiguration() {
auto& player = Settings::values.players[player_index];
auto& player = Settings::values.players.GetValue()[player_index];
auto& buttons = debug ? Settings::values.debug_pad_buttons : player.buttons;
auto& analogs = debug ? Settings::values.debug_pad_analogs : player.analogs;
@@ -543,33 +575,58 @@ void ConfigureInputPlayer::ApplyConfiguration() {
}
auto& motions = player.motions;
std::transform(motions_param.begin(), motions_param.end(), motions.begin(),
[](const Common::ParamPackage& param) { return param.Serialize(); });
player.controller_type =
static_cast<Settings::ControllerType>(ui->comboControllerType->currentIndex());
player.connected = ui->groupConnectedController->isChecked();
const auto controller_type =
GetControllerTypeFromIndex(ui->comboControllerType->currentIndex());
const auto player_connected = ui->groupConnectedController->isChecked() &&
controller_type != Settings::ControllerType::Handheld;
// Player 2-8
if (player_index != 0) {
UpdateController(player.controller_type, player_index, player.connected);
if (player.controller_type == controller_type && player.connected == player_connected) {
// Set vibration devices in the event that the input device has changed.
ConfigureVibration::SetVibrationDevices(player_index);
return;
}
// Player 1 and Handheld
auto& handheld = Settings::values.players[HANDHELD_INDEX];
// If Handheld is selected, copy all the settings from Player 1 to Handheld.
if (player.controller_type == Settings::ControllerType::Handheld) {
handheld = player;
handheld.connected = ui->groupConnectedController->isChecked();
player.connected = false; // Disconnect Player 1
} else {
player.connected = ui->groupConnectedController->isChecked();
handheld.connected = false; // Disconnect Handheld
// Disconnect the controller first.
UpdateController(controller_type, player_index, false);
player.controller_type = controller_type;
player.connected = player_connected;
ConfigureVibration::SetVibrationDevices(player_index);
// Handheld
if (player_index == 0) {
auto& handheld = Settings::values.players.GetValue()[HANDHELD_INDEX];
if (controller_type == Settings::ControllerType::Handheld) {
handheld = player;
}
handheld.connected = ui->groupConnectedController->isChecked() &&
controller_type == Settings::ControllerType::Handheld;
UpdateController(Settings::ControllerType::Handheld, HANDHELD_INDEX, handheld.connected);
}
UpdateController(player.controller_type, player_index, player.connected);
UpdateController(Settings::ControllerType::Handheld, HANDHELD_INDEX, handheld.connected);
if (!player.connected) {
return;
}
// This emulates a delay between disconnecting and reconnecting controllers as some games
// do not respond to a change in controller type if it was instantaneous.
using namespace std::chrono_literals;
std::this_thread::sleep_for(20ms);
UpdateController(controller_type, player_index, player_connected);
}
void ConfigureInputPlayer::showEvent(QShowEvent* event) {
if (bottom_row == nullptr) {
return;
}
QWidget::showEvent(event);
ui->main->addWidget(bottom_row);
}
void ConfigureInputPlayer::changeEvent(QEvent* event) {
@@ -586,7 +643,7 @@ void ConfigureInputPlayer::RetranslateUI() {
}
void ConfigureInputPlayer::LoadConfiguration() {
auto& player = Settings::values.players[player_index];
auto& player = Settings::values.players.GetValue()[player_index];
if (debug) {
std::transform(Settings::values.debug_pad_buttons.begin(),
Settings::values.debug_pad_buttons.end(), buttons_param.begin(),
@@ -604,6 +661,7 @@ void ConfigureInputPlayer::LoadConfiguration() {
}
UpdateUI();
UpdateInputDeviceCombobox();
if (debug) {
return;
@@ -612,44 +670,75 @@ void ConfigureInputPlayer::LoadConfiguration() {
ui->comboControllerType->setCurrentIndex(static_cast<int>(player.controller_type));
ui->groupConnectedController->setChecked(
player.connected ||
(player_index == 0 && Settings::values.players[HANDHELD_INDEX].connected));
(player_index == 0 && Settings::values.players.GetValue()[HANDHELD_INDEX].connected));
}
void ConfigureInputPlayer::UpdateInputDevices() {
input_devices = input_subsystem->GetInputDevices();
ui->comboDevices->clear();
for (auto device : input_devices) {
ui->comboDevices->addItem(QString::fromStdString(device.Get("display", "Unknown")), {});
void ConfigureInputPlayer::ConnectPlayer(bool connected) {
ui->groupConnectedController->setChecked(connected);
}
void ConfigureInputPlayer::UpdateInputDeviceCombobox() {
// Skip input device persistence if "Input Devices" is set to "Any".
if (ui->comboDevices->currentIndex() == 0) {
UpdateInputDevices();
return;
}
// Find the first button that isn't empty.
const auto button_param =
std::find_if(buttons_param.begin(), buttons_param.end(),
[](const Common::ParamPackage param) { return param.Has("engine"); });
const bool buttons_empty = button_param == buttons_param.end();
const auto current_engine = button_param->Get("engine", "");
const auto current_guid = button_param->Get("guid", "");
const auto current_port = button_param->Get("port", "");
const bool is_keyboard_mouse = current_engine == "keyboard" || current_engine == "mouse";
UpdateInputDevices();
if (buttons_empty) {
return;
}
const bool all_one_device =
std::all_of(buttons_param.begin(), buttons_param.end(),
[current_engine, current_guid, current_port,
is_keyboard_mouse](const Common::ParamPackage param) {
if (is_keyboard_mouse) {
return !param.Has("engine") || param.Get("engine", "") == "keyboard" ||
param.Get("engine", "") == "mouse";
}
return !param.Has("engine") || (param.Get("engine", "") == current_engine &&
param.Get("guid", "") == current_guid &&
param.Get("port", "") == current_port);
});
if (all_one_device) {
if (is_keyboard_mouse) {
ui->comboDevices->setCurrentIndex(1);
return;
}
const auto devices_it = std::find_if(
input_devices.begin(), input_devices.end(),
[current_engine, current_guid, current_port](const Common::ParamPackage param) {
return param.Get("class", "") == current_engine &&
param.Get("guid", "") == current_guid &&
param.Get("port", "") == current_port;
});
const int device_index =
devices_it != input_devices.end()
? static_cast<int>(std::distance(input_devices.begin(), devices_it))
: 0;
ui->comboDevices->setCurrentIndex(device_index);
} else {
ui->comboDevices->setCurrentIndex(0);
}
}
void ConfigureInputPlayer::RestoreDefaults() {
// Reset Buttons
for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; ++button_id) {
buttons_param[button_id] = Common::ParamPackage{
InputCommon::GenerateKeyboardParam(Config::default_buttons[button_id])};
}
// Reset Analogs and Modifier Buttons
for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; ++analog_id) {
for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; ++sub_button_id) {
Common::ParamPackage params{InputCommon::GenerateKeyboardParam(
Config::default_analogs[analog_id][sub_button_id])};
SetAnalogParam(params, analogs_param[analog_id], analog_sub_buttons[sub_button_id]);
}
analogs_param[analog_id].Set(
"modifier", InputCommon::GenerateKeyboardParam(Config::default_stick_mod[analog_id]));
}
for (int motion_id = 0; motion_id < Settings::NativeMotion::NumMotions; ++motion_id) {
motions_param[motion_id] = Common::ParamPackage{
InputCommon::GenerateKeyboardParam(Config::default_motions[motion_id])};
}
UpdateUI();
UpdateInputDevices();
ui->comboControllerType->setCurrentIndex(0);
UpdateMappingWithDefaults();
}
void ConfigureInputPlayer::ClearAll() {
@@ -752,117 +841,12 @@ void ConfigureInputPlayer::UpdateUI() {
}
}
void ConfigureInputPlayer::UpdateMappingWithDefaults() {
if (ui->comboDevices->currentIndex() < 2) {
return;
void ConfigureInputPlayer::UpdateInputDevices() {
input_devices = input_subsystem->GetInputDevices();
ui->comboDevices->clear();
for (auto device : input_devices) {
ui->comboDevices->addItem(QString::fromStdString(device.Get("display", "Unknown")), {});
}
const auto& device = input_devices[ui->comboDevices->currentIndex()];
auto button_mapping = input_subsystem->GetButtonMappingForDevice(device);
auto analog_mapping = input_subsystem->GetAnalogMappingForDevice(device);
for (std::size_t i = 0; i < buttons_param.size(); ++i) {
buttons_param[i] = button_mapping[static_cast<Settings::NativeButton::Values>(i)];
}
for (std::size_t i = 0; i < analogs_param.size(); ++i) {
analogs_param[i] = analog_mapping[static_cast<Settings::NativeAnalog::Values>(i)];
}
UpdateUI();
}
void ConfigureInputPlayer::HandleClick(
QPushButton* button, std::function<void(const Common::ParamPackage&)> new_input_setter,
InputCommon::Polling::DeviceType type) {
if (button == ui->buttonMotionLeft || button == ui->buttonMotionRight) {
button->setText(tr("Shake!"));
} else {
button->setText(tr("[waiting]"));
}
button->setFocus();
// The first two input devices are always Any and Keyboard/Mouse. If the user filtered to a
// controller, then they don't want keyboard/mouse input
want_keyboard_mouse = ui->comboDevices->currentIndex() < 2;
input_setter = new_input_setter;
device_pollers = input_subsystem->GetPollers(type);
for (auto& poller : device_pollers) {
poller->Start();
}
QWidget::grabMouse();
QWidget::grabKeyboard();
if (type == InputCommon::Polling::DeviceType::Button) {
input_subsystem->GetGCButtons()->BeginConfiguration();
} else {
input_subsystem->GetGCAnalogs()->BeginConfiguration();
}
if (type == InputCommon::Polling::DeviceType::Motion) {
input_subsystem->GetUDPMotions()->BeginConfiguration();
}
timeout_timer->start(2500); // Cancel after 2.5 seconds
poll_timer->start(50); // Check for new inputs every 50ms
}
void ConfigureInputPlayer::SetPollingResult(const Common::ParamPackage& params, bool abort) {
timeout_timer->stop();
poll_timer->stop();
for (auto& poller : device_pollers) {
poller->Stop();
}
QWidget::releaseMouse();
QWidget::releaseKeyboard();
input_subsystem->GetGCButtons()->EndConfiguration();
input_subsystem->GetGCAnalogs()->EndConfiguration();
input_subsystem->GetUDPMotions()->EndConfiguration();
if (!abort) {
(*input_setter)(params);
}
UpdateUI();
input_setter = std::nullopt;
}
void ConfigureInputPlayer::mousePressEvent(QMouseEvent* event) {
if (!input_setter || !event) {
return;
}
if (want_keyboard_mouse) {
SetPollingResult(Common::ParamPackage{InputCommon::GenerateKeyboardParam(event->button())},
false);
} else {
// We don't want any mouse buttons, so don't stop polling
return;
}
SetPollingResult({}, true);
}
void ConfigureInputPlayer::keyPressEvent(QKeyEvent* event) {
if (!input_setter || !event) {
return;
}
if (event->key() != Qt::Key_Escape) {
if (want_keyboard_mouse) {
SetPollingResult(Common::ParamPackage{InputCommon::GenerateKeyboardParam(event->key())},
false);
} else {
// Escape key wasn't pressed and we don't want any keyboard keys, so don't stop polling
return;
}
}
SetPollingResult({}, true);
}
void ConfigureInputPlayer::UpdateControllerIcon() {
@@ -885,7 +869,7 @@ void ConfigureInputPlayer::UpdateControllerIcon() {
}
}();
const QString theme = [this] {
const QString theme = [] {
if (QIcon::themeName().contains(QStringLiteral("dark"))) {
return QStringLiteral("_dark");
} else if (QIcon::themeName().contains(QStringLiteral("midnight"))) {
@@ -986,14 +970,260 @@ void ConfigureInputPlayer::UpdateMotionButtons() {
}
}
void ConfigureInputPlayer::showEvent(QShowEvent* event) {
if (bottom_row == nullptr) {
void ConfigureInputPlayer::UpdateMappingWithDefaults() {
if (ui->comboDevices->currentIndex() == 0) {
return;
}
QWidget::showEvent(event);
ui->main->addWidget(bottom_row);
if (ui->comboDevices->currentIndex() == 1) {
// Reset keyboard bindings
for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; ++button_id) {
buttons_param[button_id] = Common::ParamPackage{
InputCommon::GenerateKeyboardParam(Config::default_buttons[button_id])};
}
for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; ++analog_id) {
for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; ++sub_button_id) {
Common::ParamPackage params{InputCommon::GenerateKeyboardParam(
Config::default_analogs[analog_id][sub_button_id])};
SetAnalogParam(params, analogs_param[analog_id], analog_sub_buttons[sub_button_id]);
}
analogs_param[analog_id].Set("modifier", InputCommon::GenerateKeyboardParam(
Config::default_stick_mod[analog_id]));
}
for (int motion_id = 0; motion_id < Settings::NativeMotion::NumMotions; ++motion_id) {
motions_param[motion_id] = Common::ParamPackage{
InputCommon::GenerateKeyboardParam(Config::default_motions[motion_id])};
}
UpdateUI();
return;
}
// Reset controller bindings
const auto& device = input_devices[ui->comboDevices->currentIndex()];
auto button_mapping = input_subsystem->GetButtonMappingForDevice(device);
auto analog_mapping = input_subsystem->GetAnalogMappingForDevice(device);
for (std::size_t i = 0; i < buttons_param.size(); ++i) {
buttons_param[i] = button_mapping[static_cast<Settings::NativeButton::Values>(i)];
}
for (std::size_t i = 0; i < analogs_param.size(); ++i) {
analogs_param[i] = analog_mapping[static_cast<Settings::NativeAnalog::Values>(i)];
}
UpdateUI();
}
void ConfigureInputPlayer::ConnectPlayer(bool connected) {
ui->groupConnectedController->setChecked(connected);
void ConfigureInputPlayer::HandleClick(
QPushButton* button, std::function<void(const Common::ParamPackage&)> new_input_setter,
InputCommon::Polling::DeviceType type) {
if (button == ui->buttonMotionLeft || button == ui->buttonMotionRight) {
button->setText(tr("Shake!"));
} else {
button->setText(tr("[waiting]"));
}
button->setFocus();
// The first two input devices are always Any and Keyboard/Mouse. If the user filtered to a
// controller, then they don't want keyboard/mouse input
want_keyboard_mouse = ui->comboDevices->currentIndex() < 2;
input_setter = new_input_setter;
device_pollers = input_subsystem->GetPollers(type);
for (auto& poller : device_pollers) {
poller->Start();
}
QWidget::grabMouse();
QWidget::grabKeyboard();
if (type == InputCommon::Polling::DeviceType::Button) {
input_subsystem->GetGCButtons()->BeginConfiguration();
} else {
input_subsystem->GetGCAnalogs()->BeginConfiguration();
}
if (type == InputCommon::Polling::DeviceType::Motion) {
input_subsystem->GetUDPMotions()->BeginConfiguration();
}
timeout_timer->start(2500); // Cancel after 2.5 seconds
poll_timer->start(50); // Check for new inputs every 50ms
}
void ConfigureInputPlayer::SetPollingResult(const Common::ParamPackage& params, bool abort) {
timeout_timer->stop();
poll_timer->stop();
for (auto& poller : device_pollers) {
poller->Stop();
}
QWidget::releaseMouse();
QWidget::releaseKeyboard();
input_subsystem->GetGCButtons()->EndConfiguration();
input_subsystem->GetGCAnalogs()->EndConfiguration();
input_subsystem->GetUDPMotions()->EndConfiguration();
if (!abort) {
(*input_setter)(params);
}
UpdateUI();
UpdateInputDeviceCombobox();
input_setter = std::nullopt;
}
bool ConfigureInputPlayer::IsInputAcceptable(const Common::ParamPackage& params) const {
if (ui->comboDevices->currentIndex() == 0) {
return true;
}
// Keyboard/Mouse
if (ui->comboDevices->currentIndex() == 1) {
return params.Get("engine", "") == "keyboard" || params.Get("engine", "") == "mouse";
}
const auto current_input_device = input_devices[ui->comboDevices->currentIndex()];
return params.Get("engine", "") == current_input_device.Get("class", "") &&
params.Get("guid", "") == current_input_device.Get("guid", "") &&
params.Get("port", "") == current_input_device.Get("port", "");
}
void ConfigureInputPlayer::mousePressEvent(QMouseEvent* event) {
if (!input_setter || !event) {
return;
}
if (want_keyboard_mouse) {
SetPollingResult(Common::ParamPackage{InputCommon::GenerateKeyboardParam(event->button())},
false);
} else {
// We don't want any mouse buttons, so don't stop polling
return;
}
SetPollingResult({}, true);
}
void ConfigureInputPlayer::keyPressEvent(QKeyEvent* event) {
if (!input_setter || !event) {
return;
}
if (event->key() != Qt::Key_Escape) {
if (want_keyboard_mouse) {
SetPollingResult(Common::ParamPackage{InputCommon::GenerateKeyboardParam(event->key())},
false);
} else {
// Escape key wasn't pressed and we don't want any keyboard keys, so don't stop polling
return;
}
}
SetPollingResult({}, true);
}
void ConfigureInputPlayer::CreateProfile() {
const auto profile_name =
LimitableInputDialog::GetText(this, tr("New Profile"), tr("Enter a profile name:"), 1, 20);
if (profile_name.isEmpty()) {
return;
}
if (!profiles->IsProfileNameValid(profile_name.toStdString())) {
QMessageBox::critical(this, tr("Create Input Profile"),
tr("The given profile name is not valid!"));
return;
}
ApplyConfiguration();
if (!profiles->CreateProfile(profile_name.toStdString(), player_index)) {
QMessageBox::critical(this, tr("Create Input Profile"),
tr("Failed to create the input profile \"%1\"").arg(profile_name));
UpdateInputProfiles();
emit RefreshInputProfiles(player_index);
return;
}
emit RefreshInputProfiles(player_index);
ui->comboProfiles->addItem(profile_name);
ui->comboProfiles->setCurrentIndex(ui->comboProfiles->count() - 1);
}
void ConfigureInputPlayer::DeleteProfile() {
const QString profile_name = ui->comboProfiles->itemText(ui->comboProfiles->currentIndex());
if (profile_name.isEmpty()) {
return;
}
if (!profiles->DeleteProfile(profile_name.toStdString())) {
QMessageBox::critical(this, tr("Delete Input Profile"),
tr("Failed to delete the input profile \"%1\"").arg(profile_name));
UpdateInputProfiles();
emit RefreshInputProfiles(player_index);
return;
}
emit RefreshInputProfiles(player_index);
ui->comboProfiles->removeItem(ui->comboProfiles->currentIndex());
ui->comboProfiles->setCurrentIndex(-1);
}
void ConfigureInputPlayer::LoadProfile() {
const QString profile_name = ui->comboProfiles->itemText(ui->comboProfiles->currentIndex());
if (profile_name.isEmpty()) {
return;
}
ApplyConfiguration();
if (!profiles->LoadProfile(profile_name.toStdString(), player_index)) {
QMessageBox::critical(this, tr("Load Input Profile"),
tr("Failed to load the input profile \"%1\"").arg(profile_name));
UpdateInputProfiles();
emit RefreshInputProfiles(player_index);
return;
}
LoadConfiguration();
}
void ConfigureInputPlayer::SaveProfile() {
const QString profile_name = ui->comboProfiles->itemText(ui->comboProfiles->currentIndex());
if (profile_name.isEmpty()) {
return;
}
ApplyConfiguration();
if (!profiles->SaveProfile(profile_name.toStdString(), player_index)) {
QMessageBox::critical(this, tr("Save Input Profile"),
tr("Failed to save the input profile \"%1\"").arg(profile_name));
UpdateInputProfiles();
emit RefreshInputProfiles(player_index);
return;
}
}
void ConfigureInputPlayer::UpdateInputProfiles() {
ui->comboProfiles->clear();
for (const auto& profile_name : profiles->GetInputProfileNames()) {
ui->comboProfiles->addItem(QString::fromStdString(profile_name));
}
ui->comboProfiles->setCurrentIndex(-1);
}

View File

@@ -26,6 +26,8 @@ class QString;
class QTimer;
class QWidget;
class InputProfiles;
namespace InputCommon {
class InputSubsystem;
}
@@ -45,14 +47,20 @@ class ConfigureInputPlayer : public QWidget {
public:
explicit ConfigureInputPlayer(QWidget* parent, std::size_t player_index, QWidget* bottom_row,
InputCommon::InputSubsystem* input_subsystem_,
bool debug = false);
InputProfiles* profiles_, bool debug = false);
~ConfigureInputPlayer() override;
/// Save all button configurations to settings file.
void ApplyConfiguration();
/// Set the connection state checkbox (used to sync state).
void ConnectPlayer(bool connected);
/// Update the input devices combobox.
void UpdateInputDevices();
void UpdateInputDeviceCombobox();
/// Updates the list of controller profiles.
void UpdateInputProfiles();
/// Restore all buttons to their default values.
void RestoreDefaults();
@@ -60,9 +68,6 @@ public:
/// Clear all input configuration.
void ClearAll();
/// Set the connection state checkbox (used to sync state).
void ConnectPlayer(bool connected);
signals:
/// Emitted when this controller is connected by the user.
void Connected(bool connected);
@@ -70,6 +75,12 @@ signals:
void HandheldStateChanged(bool is_handheld);
/// Emitted when the input devices combobox is being refreshed.
void RefreshInputDevices();
/**
* Emitted when the input profiles combobox is being refreshed.
* The player_index represents the current player's index, and the profile combobox
* will not be updated for this index as they are already updated by other mechanisms.
*/
void RefreshInputProfiles(std::size_t player_index);
protected:
void showEvent(QShowEvent* event) override;
@@ -89,6 +100,9 @@ private:
/// Finish polling and configure input using the input_setter.
void SetPollingResult(const Common::ParamPackage& params, bool abort);
/// Checks whether a given input can be accepted.
bool IsInputAcceptable(const Common::ParamPackage& params) const;
/// Handle mouse button press events.
void mousePressEvent(QMouseEvent* event) override;
@@ -98,8 +112,8 @@ private:
/// Update UI to reflect current configuration.
void UpdateUI();
/// Update the controller selection combobox
void UpdateControllerCombobox();
/// Update the available input devices.
void UpdateInputDevices();
/// Update the current controller icon.
void UpdateControllerIcon();
@@ -113,6 +127,18 @@ private:
/// Gets the default controller mapping for this device and auto configures the input to match.
void UpdateMappingWithDefaults();
/// Creates a controller profile.
void CreateProfile();
/// Deletes the selected controller profile.
void DeleteProfile();
/// Loads the selected controller profile.
void LoadProfile();
/// Saves the current controller configuration into a selected controller profile.
void SaveProfile();
std::unique_ptr<Ui::ConfigureInputPlayer> ui;
std::size_t player_index;
@@ -120,6 +146,8 @@ private:
InputCommon::InputSubsystem* input_subsystem;
InputProfiles* profiles;
std::unique_ptr<QTimer> timeout_timer;
std::unique_ptr<QTimer> poll_timer;
@@ -159,12 +187,15 @@ private:
std::vector<std::unique_ptr<InputCommon::Polling::DevicePoller>> device_pollers;
/// A flag to indicate that the "Map Analog Stick" pop-up has been shown and accepted once.
bool map_analog_stick_accepted{};
/// A flag to indicate if keyboard keys are okay when configuring an input. If this is false,
/// keyboard events are ignored.
bool want_keyboard_mouse = false;
bool want_keyboard_mouse{};
/// List of physical devices users can map with. If a SDL backed device is selected, then you
/// can usue this device to get a default mapping.
/// can use 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

View File

@@ -83,6 +83,12 @@
</property>
<item>
<widget class="QComboBox" name="comboControllerType">
<property name="minimumSize">
<size>
<width>0</width>
<height>21</height>
</size>
</property>
<item>
<property name="text">
<string>Pro Controller</string>
@@ -136,6 +142,12 @@
</property>
<item>
<widget class="QComboBox" name="comboDevices">
<property name="minimumSize">
<size>
<width>0</width>
<height>21</height>
</size>
</property>
<item>
<property name="text">
<string>Any</string>
@@ -152,14 +164,14 @@
<widget class="QPushButton" name="buttonRefreshDevices">
<property name="minimumSize">
<size>
<width>24</width>
<height>22</height>
<width>21</width>
<height>21</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>24</width>
<height>22</height>
<width>21</width>
<height>21</height>
</size>
</property>
<property name="styleSheet">
@@ -198,18 +210,25 @@
<number>5</number>
</property>
<item>
<widget class="QComboBox" name="comboProfiles"/>
<widget class="QComboBox" name="comboProfiles">
<property name="minimumSize">
<size>
<width>0</width>
<height>21</height>
</size>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="buttonProfilesSave">
<property name="maximumSize">
<size>
<width>55</width>
<width>68</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">min-width: 55px;</string>
<string notr="true">min-width: 68px;</string>
</property>
<property name="text">
<string>Save</string>
@@ -220,12 +239,12 @@
<widget class="QPushButton" name="buttonProfilesNew">
<property name="maximumSize">
<size>
<width>55</width>
<width>68</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">min-width: 55px;</string>
<string notr="true">min-width: 68px;</string>
</property>
<property name="text">
<string>New</string>
@@ -236,12 +255,12 @@
<widget class="QPushButton" name="buttonProfilesDelete">
<property name="maximumSize">
<size>
<width>55</width>
<width>68</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">min-width: 55px;</string>
<string notr="true">min-width: 68px;</string>
</property>
<property name="text">
<string>Delete</string>
@@ -393,18 +412,18 @@
<widget class="QPushButton" name="buttonLStickUp">
<property name="minimumSize">
<size>
<width>57</width>
<width>68</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>55</width>
<width>68</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">min-width: 55px;</string>
<string notr="true">min-width: 68px;</string>
</property>
<property name="text">
<string>Up</string>
@@ -463,18 +482,18 @@
<widget class="QPushButton" name="buttonLStickLeft">
<property name="minimumSize">
<size>
<width>57</width>
<width>68</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>55</width>
<width>68</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">min-width: 55px;</string>
<string notr="true">min-width: 68px;</string>
</property>
<property name="text">
<string>Left</string>
@@ -512,18 +531,18 @@
<widget class="QPushButton" name="buttonLStickRight">
<property name="minimumSize">
<size>
<width>57</width>
<width>68</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>55</width>
<width>68</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">min-width: 55px;</string>
<string notr="true">min-width: 68px;</string>
</property>
<property name="text">
<string>Right</string>
@@ -594,18 +613,18 @@
<widget class="QPushButton" name="buttonLStickDown">
<property name="minimumSize">
<size>
<width>57</width>
<width>68</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>55</width>
<width>68</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">min-width: 55px;</string>
<string notr="true">min-width: 68px;</string>
</property>
<property name="text">
<string>Down</string>
@@ -664,18 +683,18 @@
<widget class="QPushButton" name="buttonLStick">
<property name="minimumSize">
<size>
<width>57</width>
<width>68</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>55</width>
<width>68</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">min-width: 55px;</string>
<string notr="true">min-width: 68px;</string>
</property>
<property name="text">
<string>Pressed</string>
@@ -713,18 +732,18 @@
<widget class="QPushButton" name="buttonLStickMod">
<property name="minimumSize">
<size>
<width>57</width>
<width>68</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>55</width>
<width>68</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">min-width: 55px;</string>
<string notr="true">min-width: 68px;</string>
</property>
<property name="text">
<string>Modifier</string>
@@ -759,13 +778,13 @@
<widget class="QSpinBox" name="spinboxLStickRange">
<property name="minimumSize">
<size>
<width>55</width>
<width>68</width>
<height>21</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>55</width>
<width>68</width>
<height>16777215</height>
</size>
</property>
@@ -966,18 +985,18 @@
<widget class="QPushButton" name="buttonDpadUp">
<property name="minimumSize">
<size>
<width>57</width>
<width>68</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>55</width>
<width>68</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">min-width: 55px;</string>
<string notr="true">min-width: 68px;</string>
</property>
<property name="text">
<string>Up</string>
@@ -1036,18 +1055,18 @@
<widget class="QPushButton" name="buttonDpadLeft">
<property name="minimumSize">
<size>
<width>57</width>
<width>68</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>55</width>
<width>68</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">min-width: 55px;</string>
<string notr="true">min-width: 68px;</string>
</property>
<property name="text">
<string>Left</string>
@@ -1085,18 +1104,18 @@
<widget class="QPushButton" name="buttonDpadRight">
<property name="minimumSize">
<size>
<width>57</width>
<width>68</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>55</width>
<width>68</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">min-width: 55px;</string>
<string notr="true">min-width: 68px;</string>
</property>
<property name="text">
<string>Right</string>
@@ -1167,18 +1186,18 @@
<widget class="QPushButton" name="buttonDpadDown">
<property name="minimumSize">
<size>
<width>57</width>
<width>68</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>55</width>
<width>68</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">min-width: 55px;</string>
<string notr="true">min-width: 68px;</string>
</property>
<property name="text">
<string>Down</string>
@@ -1292,18 +1311,18 @@
<widget class="QPushButton" name="buttonL">
<property name="minimumSize">
<size>
<width>57</width>
<width>68</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>55</width>
<width>68</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">min-width: 55px;</string>
<string notr="true">min-width: 68px;</string>
</property>
<property name="text">
<string>L</string>
@@ -1341,18 +1360,18 @@
<widget class="QPushButton" name="buttonZL">
<property name="minimumSize">
<size>
<width>57</width>
<width>68</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>55</width>
<width>68</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">min-width: 55px;</string>
<string notr="true">min-width: 68px;</string>
</property>
<property name="text">
<string>ZL</string>
@@ -1445,18 +1464,18 @@
<widget class="QPushButton" name="buttonMinus">
<property name="minimumSize">
<size>
<width>57</width>
<width>68</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>55</width>
<width>68</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">min-width: 55px;</string>
<string notr="true">min-width: 68px;</string>
</property>
<property name="text">
<string>Minus</string>
@@ -1494,18 +1513,18 @@
<widget class="QPushButton" name="buttonScreenshot">
<property name="minimumSize">
<size>
<width>57</width>
<width>68</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>55</width>
<width>68</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">min-width: 55px;</string>
<string notr="true">min-width: 68px;</string>
</property>
<property name="text">
<string>Capture</string>
@@ -1564,18 +1583,18 @@
<widget class="QPushButton" name="buttonPlus">
<property name="minimumSize">
<size>
<width>57</width>
<width>68</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>55</width>
<width>68</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">min-width: 55px;</string>
<string notr="true">min-width: 68px;</string>
</property>
<property name="text">
<string>Plus</string>
@@ -1613,18 +1632,18 @@
<widget class="QPushButton" name="buttonHome">
<property name="minimumSize">
<size>
<width>57</width>
<width>68</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>55</width>
<width>68</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">min-width: 55px;</string>
<string notr="true">min-width: 68px;</string>
</property>
<property name="text">
<string>Home</string>
@@ -1717,18 +1736,18 @@
<widget class="QPushButton" name="buttonR">
<property name="minimumSize">
<size>
<width>57</width>
<width>68</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>55</width>
<width>68</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">min-width: 55px;</string>
<string notr="true">min-width: 68px;</string>
</property>
<property name="text">
<string>R</string>
@@ -1766,18 +1785,18 @@
<widget class="QPushButton" name="buttonZR">
<property name="minimumSize">
<size>
<width>57</width>
<width>68</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>55</width>
<width>68</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">min-width: 55px;</string>
<string notr="true">min-width: 68px;</string>
</property>
<property name="text">
<string>ZR</string>
@@ -1870,18 +1889,18 @@
<widget class="QPushButton" name="buttonSL">
<property name="minimumSize">
<size>
<width>57</width>
<width>68</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>55</width>
<width>68</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">min-width: 55px;</string>
<string notr="true">min-width: 68px;</string>
</property>
<property name="text">
<string>SL</string>
@@ -1919,18 +1938,18 @@
<widget class="QPushButton" name="buttonSR">
<property name="minimumSize">
<size>
<width>57</width>
<width>68</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>55</width>
<width>68</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">min-width: 55px;</string>
<string notr="true">min-width: 68px;</string>
</property>
<property name="text">
<string>SR</string>
@@ -2027,18 +2046,18 @@
<widget class="QPushButton" name="buttonMotionLeft">
<property name="minimumSize">
<size>
<width>57</width>
<width>68</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>55</width>
<width>68</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">min-width: 55px;</string>
<string notr="true">min-width: 68px;</string>
</property>
<property name="text">
<string>Left</string>
@@ -2076,18 +2095,18 @@
<widget class="QPushButton" name="buttonMotionRight">
<property name="minimumSize">
<size>
<width>57</width>
<width>68</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>55</width>
<width>68</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">min-width: 55px;</string>
<string notr="true">min-width: 68px;</string>
</property>
<property name="text">
<string>Right</string>
@@ -2225,18 +2244,18 @@
<widget class="QPushButton" name="buttonX">
<property name="minimumSize">
<size>
<width>57</width>
<width>68</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>55</width>
<width>68</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">min-width: 55px;</string>
<string notr="true">min-width: 68px;</string>
</property>
<property name="text">
<string>X</string>
@@ -2295,18 +2314,18 @@
<widget class="QPushButton" name="buttonY">
<property name="minimumSize">
<size>
<width>57</width>
<width>68</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>55</width>
<width>68</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">min-width: 55px;</string>
<string notr="true">min-width: 68px;</string>
</property>
<property name="text">
<string>Y</string>
@@ -2344,18 +2363,18 @@
<widget class="QPushButton" name="buttonA">
<property name="minimumSize">
<size>
<width>57</width>
<width>68</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>55</width>
<width>68</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">min-width: 55px;</string>
<string notr="true">min-width: 68px;</string>
</property>
<property name="text">
<string>A</string>
@@ -2426,18 +2445,18 @@
<widget class="QPushButton" name="buttonB">
<property name="minimumSize">
<size>
<width>57</width>
<width>68</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>55</width>
<width>68</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">min-width: 55px;</string>
<string notr="true">min-width: 68px;</string>
</property>
<property name="text">
<string>B</string>
@@ -2580,18 +2599,18 @@
<widget class="QPushButton" name="buttonRStickUp">
<property name="minimumSize">
<size>
<width>57</width>
<width>68</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>55</width>
<width>68</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">min-width: 55px;</string>
<string notr="true">min-width: 68px;</string>
</property>
<property name="text">
<string>Up</string>
@@ -2650,18 +2669,18 @@
<widget class="QPushButton" name="buttonRStickLeft">
<property name="minimumSize">
<size>
<width>57</width>
<width>68</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>55</width>
<width>68</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">min-width: 55px;</string>
<string notr="true">min-width: 68px;</string>
</property>
<property name="text">
<string>Left</string>
@@ -2699,18 +2718,18 @@
<widget class="QPushButton" name="buttonRStickRight">
<property name="minimumSize">
<size>
<width>57</width>
<width>68</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>55</width>
<width>68</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">min-width: 55px;</string>
<string notr="true">min-width: 68px;</string>
</property>
<property name="text">
<string>Right</string>
@@ -2781,18 +2800,18 @@
<widget class="QPushButton" name="buttonRStickDown">
<property name="minimumSize">
<size>
<width>57</width>
<width>68</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>55</width>
<width>68</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">min-width: 55px;</string>
<string notr="true">min-width: 68px;</string>
</property>
<property name="text">
<string>Down</string>
@@ -2851,18 +2870,18 @@
<widget class="QPushButton" name="buttonRStick">
<property name="minimumSize">
<size>
<width>57</width>
<width>68</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>55</width>
<width>68</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">min-width: 55px;</string>
<string notr="true">min-width: 68px;</string>
</property>
<property name="text">
<string>Pressed</string>
@@ -2900,18 +2919,18 @@
<widget class="QPushButton" name="buttonRStickMod">
<property name="minimumSize">
<size>
<width>57</width>
<width>68</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>55</width>
<width>68</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">min-width: 55px;</string>
<string notr="true">min-width: 68px;</string>
</property>
<property name="text">
<string>Modifier</string>
@@ -2946,13 +2965,13 @@
<widget class="QSpinBox" name="spinboxRStickRange">
<property name="minimumSize">
<size>
<width>55</width>
<width>68</width>
<height>21</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>55</width>
<width>68</width>
<height>16777215</height>
</size>
</property>

View File

@@ -0,0 +1,37 @@
// Copyright 2020 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "ui_configure_input_profile_dialog.h"
#include "yuzu/configuration/configure_input_player.h"
#include "yuzu/configuration/configure_input_profile_dialog.h"
ConfigureInputProfileDialog::ConfigureInputProfileDialog(
QWidget* parent, InputCommon::InputSubsystem* input_subsystem, InputProfiles* profiles)
: QDialog(parent), ui(std::make_unique<Ui::ConfigureInputProfileDialog>()),
profile_widget(new ConfigureInputPlayer(this, 9, nullptr, input_subsystem, profiles, false)) {
ui->setupUi(this);
ui->controllerLayout->addWidget(profile_widget);
connect(ui->clear_all_button, &QPushButton::clicked, this,
[this] { profile_widget->ClearAll(); });
connect(ui->restore_defaults_button, &QPushButton::clicked, this,
[this] { profile_widget->RestoreDefaults(); });
RetranslateUI();
}
ConfigureInputProfileDialog::~ConfigureInputProfileDialog() = default;
void ConfigureInputProfileDialog::changeEvent(QEvent* event) {
if (event->type() == QEvent::LanguageChange) {
RetranslateUI();
}
QDialog::changeEvent(event);
}
void ConfigureInputProfileDialog::RetranslateUI() {
ui->retranslateUi(this);
}

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.
#pragma once
#include <memory>
#include <QDialog>
class QPushButton;
class ConfigureInputPlayer;
class InputProfiles;
namespace InputCommon {
class InputSubsystem;
}
namespace Ui {
class ConfigureInputProfileDialog;
}
class ConfigureInputProfileDialog : public QDialog {
Q_OBJECT
public:
explicit ConfigureInputProfileDialog(QWidget* parent,
InputCommon::InputSubsystem* input_subsystem,
InputProfiles* profiles);
~ConfigureInputProfileDialog() override;
private:
void changeEvent(QEvent* event) override;
void RetranslateUI();
std::unique_ptr<Ui::ConfigureInputProfileDialog> ui;
ConfigureInputPlayer* profile_widget;
};

View File

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ConfigureInputDialog</class>
<widget class="QDialog" name="ConfigureInputDialog">
<class>ConfigureInputProfileDialog</class>
<widget class="QDialog" name="ConfigureInputProfileDialog">
<property name="geometry">
<rect>
<x>0</x>
@@ -11,7 +11,7 @@
</rect>
</property>
<property name="windowTitle">
<string>Configure Input</string>
<string>Create Input Profile</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
@@ -30,10 +30,24 @@
<number>9</number>
</property>
<item>
<layout class="QHBoxLayout" name="inputLayout"/>
<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">
@@ -50,7 +64,7 @@
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>ConfigureInputDialog</receiver>
<receiver>ConfigureInputProfileDialog</receiver>
<slot>accept()</slot>
</connection>
</connections>

View File

@@ -312,16 +312,6 @@
<signal>accepted()</signal>
<receiver>ConfigureMotionTouch</receiver>
<slot>ApplyConfiguration()</slot>
<hints>
<hint type="sourcelabel">
<x>220</x>
<y>380</y>
</hint>
<hint type="destinationlabel">
<x>220</x>
<y>200</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@@ -15,7 +15,7 @@
</property>
<property name="styleSheet">
<string notr="true">QPushButton {
min-width: 55px;
min-width: 60px;
}</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
@@ -42,13 +42,13 @@
<widget class="QPushButton" name="forward_button">
<property name="minimumSize">
<size>
<width>57</width>
<width>68</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<width>68</width>
<height>16777215</height>
</size>
</property>
@@ -82,7 +82,7 @@
<widget class="QPushButton" name="back_button">
<property name="minimumSize">
<size>
<width>57</width>
<width>68</width>
<height>0</height>
</size>
</property>
@@ -110,7 +110,7 @@
<widget class="QPushButton" name="left_button">
<property name="minimumSize">
<size>
<width>57</width>
<width>68</width>
<height>0</height>
</size>
</property>
@@ -138,13 +138,13 @@
<widget class="QPushButton" name="middle_button">
<property name="minimumSize">
<size>
<width>57</width>
<width>68</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<width>68</width>
<height>16777215</height>
</size>
</property>
@@ -204,13 +204,13 @@
<widget class="QPushButton" name="right_button">
<property name="minimumSize">
<size>
<width>57</width>
<width>68</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<width>68</width>
<height>16777215</height>
</size>
</property>
@@ -256,13 +256,13 @@
<widget class="QPushButton" name="buttonClearAll">
<property name="minimumSize">
<size>
<width>57</width>
<width>68</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<width>68</width>
<height>16777215</height>
</size>
</property>
@@ -275,13 +275,13 @@
<widget class="QPushButton" name="buttonRestoreDefaults">
<property name="minimumSize">
<size>
<width>57</width>
<width>68</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<width>68</width>
<height>16777215</height>
</size>
</property>
@@ -324,32 +324,12 @@
<signal>accepted()</signal>
<receiver>ConfigureMouseAdvanced</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>124</x>
<y>266</y>
</hint>
<hint type="destinationlabel">
<x>124</x>
<y>143</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>ConfigureMouseAdvanced</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>124</x>
<y>266</y>
</hint>
<hint type="destinationlabel">
<x>124</x>
<y>143</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@@ -29,7 +29,8 @@
ConfigurePerGame::ConfigurePerGame(QWidget* parent, u64 title_id)
: QDialog(parent), ui(std::make_unique<Ui::ConfigurePerGame>()), title_id(title_id) {
game_config = std::make_unique<Config>(fmt::format("{:016X}.ini", title_id), false);
game_config = std::make_unique<Config>(fmt::format("{:016X}", title_id),
Config::ConfigType::PerGameConfig);
Settings::SetConfiguringGlobal(false);

View File

@@ -319,32 +319,12 @@
<signal>accepted()</signal>
<receiver>ConfigurePerGame</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>ConfigurePerGame</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@@ -216,16 +216,6 @@ Drag points to change position, or double-click table cells to edit values.</str
<signal>rejected()</signal>
<receiver>ConfigureTouchFromButton</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>249</x>
<y>428</y>
</hint>
<hint type="destinationlabel">
<x>249</x>
<y>224</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@@ -168,32 +168,12 @@
<signal>accepted()</signal>
<receiver>ConfigureTouchscreenAdvanced</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>ConfigureTouchscreenAdvanced</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>
</connection>
</connections>
</ui>

View File

@@ -0,0 +1,146 @@
// Copyright 2020 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <algorithm>
#include <unordered_map>
#include <fmt/format.h>
#include "common/param_package.h"
#include "core/settings.h"
#include "ui_configure_vibration.h"
#include "yuzu/configuration/configure_vibration.h"
ConfigureVibration::ConfigureVibration(QWidget* parent)
: QDialog(parent), ui(std::make_unique<Ui::ConfigureVibration>()) {
ui->setupUi(this);
vibration_groupboxes = {
ui->vibrationGroupPlayer1, ui->vibrationGroupPlayer2, ui->vibrationGroupPlayer3,
ui->vibrationGroupPlayer4, ui->vibrationGroupPlayer5, ui->vibrationGroupPlayer6,
ui->vibrationGroupPlayer7, ui->vibrationGroupPlayer8,
};
vibration_spinboxes = {
ui->vibrationSpinPlayer1, ui->vibrationSpinPlayer2, ui->vibrationSpinPlayer3,
ui->vibrationSpinPlayer4, ui->vibrationSpinPlayer5, ui->vibrationSpinPlayer6,
ui->vibrationSpinPlayer7, ui->vibrationSpinPlayer8,
};
const auto& players = Settings::values.players.GetValue();
for (std::size_t i = 0; i < NUM_PLAYERS; ++i) {
vibration_groupboxes[i]->setChecked(players[i].vibration_enabled);
vibration_spinboxes[i]->setValue(players[i].vibration_strength);
}
ui->checkBoxAccurateVibration->setChecked(
Settings::values.enable_accurate_vibrations.GetValue());
if (!Settings::IsConfiguringGlobal()) {
ui->checkBoxAccurateVibration->setDisabled(true);
}
RetranslateUI();
}
ConfigureVibration::~ConfigureVibration() = default;
void ConfigureVibration::ApplyConfiguration() {
auto& players = Settings::values.players.GetValue();
for (std::size_t i = 0; i < NUM_PLAYERS; ++i) {
players[i].vibration_enabled = vibration_groupboxes[i]->isChecked();
players[i].vibration_strength = vibration_spinboxes[i]->value();
}
Settings::values.enable_accurate_vibrations.SetValue(
ui->checkBoxAccurateVibration->isChecked());
}
void ConfigureVibration::SetVibrationDevices(std::size_t player_index) {
using namespace Settings::NativeButton;
static constexpr std::array<std::array<Settings::NativeButton::Values, 6>, 2> buttons{{
{DLeft, DUp, DRight, DDown, L, ZL}, // Left Buttons
{A, B, X, Y, R, ZR}, // Right Buttons
}};
auto& player = Settings::values.players.GetValue()[player_index];
for (std::size_t device_idx = 0; device_idx < buttons.size(); ++device_idx) {
std::unordered_map<std::string, int> params_count;
for (const auto button_index : buttons[device_idx]) {
const auto& player_button = player.buttons[button_index];
if (params_count.find(player_button) != params_count.end()) {
++params_count[player_button];
continue;
}
params_count.insert_or_assign(player_button, 1);
}
const auto it = std::max_element(
params_count.begin(), params_count.end(),
[](const auto& lhs, const auto& rhs) { return lhs.second < rhs.second; });
auto& vibration_param_str = player.vibrations[device_idx];
vibration_param_str.clear();
if (it->first.empty()) {
continue;
}
const auto param = Common::ParamPackage(it->first);
const auto engine = param.Get("engine", "");
const auto guid = param.Get("guid", "");
const auto port = param.Get("port", "");
if (engine.empty() || engine == "keyboard" || engine == "mouse") {
continue;
}
vibration_param_str += fmt::format("engine:{}", engine);
if (!port.empty()) {
vibration_param_str += fmt::format(",port:{}", port);
}
if (!guid.empty()) {
vibration_param_str += fmt::format(",guid:{}", guid);
}
}
if (player.vibrations[0] != player.vibrations[1]) {
return;
}
if (!player.vibrations[0].empty() &&
player.controller_type != Settings::ControllerType::RightJoycon) {
player.vibrations[1].clear();
} else if (!player.vibrations[1].empty() &&
player.controller_type == Settings::ControllerType::RightJoycon) {
player.vibrations[0].clear();
}
}
void ConfigureVibration::SetAllVibrationDevices() {
// Set vibration devices for all player indices including handheld
for (std::size_t player_idx = 0; player_idx < NUM_PLAYERS + 1; ++player_idx) {
SetVibrationDevices(player_idx);
}
}
void ConfigureVibration::changeEvent(QEvent* event) {
if (event->type() == QEvent::LanguageChange) {
RetranslateUI();
}
QDialog::changeEvent(event);
}
void ConfigureVibration::RetranslateUI() {
ui->retranslateUi(this);
}

View File

@@ -0,0 +1,43 @@
// Copyright 2020 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <array>
#include <memory>
#include <QDialog>
class QGroupBox;
class QSpinBox;
namespace Ui {
class ConfigureVibration;
}
class ConfigureVibration : public QDialog {
Q_OBJECT
public:
explicit ConfigureVibration(QWidget* parent);
~ConfigureVibration() override;
void ApplyConfiguration();
static void SetVibrationDevices(std::size_t player_index);
static void SetAllVibrationDevices();
private:
void changeEvent(QEvent* event) override;
void RetranslateUI();
std::unique_ptr<Ui::ConfigureVibration> ui;
static constexpr std::size_t NUM_PLAYERS = 8;
// Groupboxes encapsulating the vibration strength spinbox.
std::array<QGroupBox*, NUM_PLAYERS> vibration_groupboxes;
// Spinboxes representing the vibration strength percentage.
std::array<QSpinBox*, NUM_PLAYERS> vibration_spinboxes;
};

View File

@@ -0,0 +1,546 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ConfigureVibration</class>
<widget class="QDialog" name="ConfigureVibration">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>364</width>
<height>242</height>
</rect>
</property>
<property name="windowTitle">
<string>Configure Vibration</string>
</property>
<property name="styleSheet">
<string notr="true"/>
</property>
<layout class="QVBoxLayout">
<item>
<widget class="QGroupBox" name="vibrationStrengthGroup">
<property name="title">
<string>Vibration</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3" stretch="0,0">
<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>
<widget class="QWidget" name="player14Widget" native="true">
<layout class="QHBoxLayout" name="horizontalLayout_4">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QGroupBox" name="vibrationGroupPlayer1">
<property name="title">
<string>Player 1</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_8">
<property name="leftMargin">
<number>3</number>
</property>
<property name="topMargin">
<number>3</number>
</property>
<property name="rightMargin">
<number>3</number>
</property>
<property name="bottomMargin">
<number>3</number>
</property>
<item>
<widget class="QSpinBox" name="vibrationSpinPlayer1">
<property name="minimumSize">
<size>
<width>68</width>
<height>21</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>68</width>
<height>16777215</height>
</size>
</property>
<property name="suffix">
<string>%</string>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>150</number>
</property>
<property name="value">
<number>100</number>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="vibrationGroupPlayer2">
<property name="title">
<string>Player 2</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_9">
<property name="leftMargin">
<number>3</number>
</property>
<property name="topMargin">
<number>3</number>
</property>
<property name="rightMargin">
<number>3</number>
</property>
<property name="bottomMargin">
<number>3</number>
</property>
<item>
<widget class="QSpinBox" name="vibrationSpinPlayer2">
<property name="minimumSize">
<size>
<width>68</width>
<height>21</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>68</width>
<height>16777215</height>
</size>
</property>
<property name="suffix">
<string>%</string>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>150</number>
</property>
<property name="value">
<number>100</number>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="vibrationGroupPlayer3">
<property name="title">
<string>Player 3</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_10">
<property name="leftMargin">
<number>3</number>
</property>
<property name="topMargin">
<number>3</number>
</property>
<property name="rightMargin">
<number>3</number>
</property>
<property name="bottomMargin">
<number>3</number>
</property>
<item>
<widget class="QSpinBox" name="vibrationSpinPlayer3">
<property name="minimumSize">
<size>
<width>68</width>
<height>21</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>68</width>
<height>16777215</height>
</size>
</property>
<property name="suffix">
<string>%</string>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>150</number>
</property>
<property name="value">
<number>100</number>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="vibrationGroupPlayer4">
<property name="title">
<string>Player 4</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_11">
<property name="leftMargin">
<number>3</number>
</property>
<property name="topMargin">
<number>3</number>
</property>
<property name="rightMargin">
<number>3</number>
</property>
<property name="bottomMargin">
<number>3</number>
</property>
<item>
<widget class="QSpinBox" name="vibrationSpinPlayer4">
<property name="minimumSize">
<size>
<width>68</width>
<height>21</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>68</width>
<height>16777215</height>
</size>
</property>
<property name="suffix">
<string>%</string>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>150</number>
</property>
<property name="value">
<number>100</number>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QWidget" name="player58Widget" native="true">
<layout class="QHBoxLayout" name="horizontalLayout_6">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QGroupBox" name="vibrationGroupPlayer7">
<property name="title">
<string>Player 5</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_14">
<property name="leftMargin">
<number>3</number>
</property>
<property name="topMargin">
<number>3</number>
</property>
<property name="rightMargin">
<number>3</number>
</property>
<property name="bottomMargin">
<number>3</number>
</property>
<item>
<widget class="QSpinBox" name="vibrationSpinPlayer7">
<property name="minimumSize">
<size>
<width>68</width>
<height>21</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>68</width>
<height>16777215</height>
</size>
</property>
<property name="suffix">
<string>%</string>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>150</number>
</property>
<property name="value">
<number>100</number>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="vibrationGroupPlayer8">
<property name="title">
<string>Player 6</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_15">
<property name="leftMargin">
<number>3</number>
</property>
<property name="topMargin">
<number>3</number>
</property>
<property name="rightMargin">
<number>3</number>
</property>
<property name="bottomMargin">
<number>3</number>
</property>
<item>
<widget class="QSpinBox" name="vibrationSpinPlayer8">
<property name="minimumSize">
<size>
<width>68</width>
<height>21</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>68</width>
<height>16777215</height>
</size>
</property>
<property name="suffix">
<string>%</string>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>150</number>
</property>
<property name="value">
<number>100</number>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="vibrationGroupPlayer5">
<property name="title">
<string>Player 7</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_12">
<property name="leftMargin">
<number>3</number>
</property>
<property name="topMargin">
<number>3</number>
</property>
<property name="rightMargin">
<number>3</number>
</property>
<property name="bottomMargin">
<number>3</number>
</property>
<item>
<widget class="QSpinBox" name="vibrationSpinPlayer5">
<property name="minimumSize">
<size>
<width>68</width>
<height>21</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>68</width>
<height>16777215</height>
</size>
</property>
<property name="suffix">
<string>%</string>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>150</number>
</property>
<property name="value">
<number>100</number>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="vibrationGroupPlayer6">
<property name="title">
<string>Player 8</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_13">
<property name="leftMargin">
<number>3</number>
</property>
<property name="topMargin">
<number>3</number>
</property>
<property name="rightMargin">
<number>3</number>
</property>
<property name="bottomMargin">
<number>3</number>
</property>
<item>
<widget class="QSpinBox" name="vibrationSpinPlayer6">
<property name="minimumSize">
<size>
<width>68</width>
<height>21</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>68</width>
<height>16777215</height>
</size>
</property>
<property name="suffix">
<string>%</string>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>150</number>
</property>
<property name="value">
<number>100</number>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="vibrationSettingsGroup">
<property name="title">
<string>Settings</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QCheckBox" name="checkBoxAccurateVibration">
<property name="text">
<string>Enable Accurate Vibration</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="spacerVibration">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>167</width>
<height>55</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBoxVibration">
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBoxVibration</sender>
<signal>accepted()</signal>
<receiver>ConfigureVibration</receiver>
<slot>accept()</slot>
</connection>
<connection>
<sender>buttonBoxVibration</sender>
<signal>rejected()</signal>
<receiver>ConfigureVibration</receiver>
<slot>reject()</slot>
</connection>
</connections>
</ui>

View File

@@ -0,0 +1,131 @@
// Copyright 2020 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <fmt/format.h>
#include "common/common_paths.h"
#include "common/file_util.h"
#include "yuzu/configuration/config.h"
#include "yuzu/configuration/input_profiles.h"
namespace FS = Common::FS;
namespace {
bool ProfileExistsInFilesystem(std::string_view profile_name) {
return FS::Exists(fmt::format("{}input" DIR_SEP "{}.ini",
FS::GetUserPath(FS::UserPath::ConfigDir), profile_name));
}
bool IsINI(std::string_view filename) {
const std::size_t index = filename.rfind('.');
if (index == std::string::npos) {
return false;
}
return filename.substr(index) == ".ini";
}
std::string GetNameWithoutExtension(const std::string& filename) {
const std::size_t index = filename.rfind('.');
if (index == std::string::npos) {
return filename;
}
return filename.substr(0, index);
}
} // namespace
InputProfiles::InputProfiles() {
const std::string input_profile_loc =
fmt::format("{}input", FS::GetUserPath(FS::UserPath::ConfigDir));
FS::ForeachDirectoryEntry(
nullptr, input_profile_loc,
[this](u64* entries_out, const std::string& directory, const std::string& filename) {
if (IsINI(filename) && IsProfileNameValid(GetNameWithoutExtension(filename))) {
map_profiles.insert_or_assign(
GetNameWithoutExtension(filename),
std::make_unique<Config>(GetNameWithoutExtension(filename),
Config::ConfigType::InputProfile));
}
return true;
});
}
InputProfiles::~InputProfiles() = default;
std::vector<std::string> InputProfiles::GetInputProfileNames() {
std::vector<std::string> profile_names;
profile_names.reserve(map_profiles.size());
for (const auto& [profile_name, config] : map_profiles) {
if (!ProfileExistsInFilesystem(profile_name)) {
DeleteProfile(profile_name);
continue;
}
profile_names.push_back(profile_name);
}
return profile_names;
}
bool InputProfiles::IsProfileNameValid(std::string_view profile_name) {
return profile_name.find_first_of("<>:;\"/\\|,.!?*") == std::string::npos;
}
bool InputProfiles::CreateProfile(const std::string& profile_name, std::size_t player_index) {
if (ProfileExistsInMap(profile_name)) {
return false;
}
map_profiles.insert_or_assign(
profile_name, std::make_unique<Config>(profile_name, Config::ConfigType::InputProfile));
return SaveProfile(profile_name, player_index);
}
bool InputProfiles::DeleteProfile(const std::string& profile_name) {
if (!ProfileExistsInMap(profile_name)) {
return false;
}
if (!ProfileExistsInFilesystem(profile_name) ||
FS::Delete(map_profiles[profile_name]->GetConfigFilePath())) {
map_profiles.erase(profile_name);
}
return !ProfileExistsInMap(profile_name) && !ProfileExistsInFilesystem(profile_name);
}
bool InputProfiles::LoadProfile(const std::string& profile_name, std::size_t player_index) {
if (!ProfileExistsInMap(profile_name)) {
return false;
}
if (!ProfileExistsInFilesystem(profile_name)) {
map_profiles.erase(profile_name);
return false;
}
map_profiles[profile_name]->ReadControlPlayerValue(player_index);
return true;
}
bool InputProfiles::SaveProfile(const std::string& profile_name, std::size_t player_index) {
if (!ProfileExistsInMap(profile_name)) {
return false;
}
map_profiles[profile_name]->SaveControlPlayerValue(player_index);
return true;
}
bool InputProfiles::ProfileExistsInMap(const std::string& profile_name) const {
return map_profiles.find(profile_name) != map_profiles.end();
}

View File

@@ -0,0 +1,32 @@
// Copyright 2020 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <string>
#include <string_view>
#include <unordered_map>
class Config;
class InputProfiles {
public:
explicit InputProfiles();
virtual ~InputProfiles();
std::vector<std::string> GetInputProfileNames();
static bool IsProfileNameValid(std::string_view profile_name);
bool CreateProfile(const std::string& profile_name, std::size_t player_index);
bool DeleteProfile(const std::string& profile_name);
bool LoadProfile(const std::string& profile_name, std::size_t player_index);
bool SaveProfile(const std::string& profile_name, std::size_t player_index);
private:
bool ProfileExistsInMap(const std::string& profile_name) const;
std::unordered_map<std::string, std::unique_ptr<Config>> map_profiles;
};

View File

@@ -18,6 +18,7 @@
#include "applets/web_browser.h"
#include "configuration/configure_input.h"
#include "configuration/configure_per_game.h"
#include "configuration/configure_vibration.h"
#include "core/file_sys/vfs.h"
#include "core/file_sys/vfs_real.h"
#include "core/frontend/applets/controller.h"
@@ -50,12 +51,14 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
#include <QDesktopServices>
#include <QDesktopWidget>
#include <QDialogButtonBox>
#include <QDir>
#include <QFile>
#include <QFileDialog>
#include <QInputDialog>
#include <QMessageBox>
#include <QProgressBar>
#include <QProgressDialog>
#include <QPushButton>
#include <QShortcut>
#include <QStatusBar>
#include <QSysInfo>
@@ -277,6 +280,8 @@ GMainWindow::GMainWindow()
if (args.length() >= 2) {
BootGame(args[1]);
}
MigrateConfigFiles();
}
GMainWindow::~GMainWindow() {
@@ -288,6 +293,7 @@ GMainWindow::~GMainWindow() {
void GMainWindow::ControllerSelectorReconfigureControllers(
const Core::Frontend::ControllerParameters& parameters) {
QtControllerSelectorDialog dialog(this, parameters, input_subsystem.get());
dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowStaysOnTopHint |
Qt::WindowTitleHint | Qt::WindowSystemMenuHint);
dialog.setWindowModality(Qt::WindowModal);
@@ -547,13 +553,14 @@ void GMainWindow::InitializeWidgets() {
dock_status_button->setObjectName(QStringLiteral("TogglableStatusBarButton"));
dock_status_button->setFocusPolicy(Qt::NoFocus);
connect(dock_status_button, &QPushButton::clicked, [&] {
Settings::values.use_docked_mode = !Settings::values.use_docked_mode;
dock_status_button->setChecked(Settings::values.use_docked_mode);
OnDockedModeChanged(!Settings::values.use_docked_mode, Settings::values.use_docked_mode);
Settings::values.use_docked_mode.SetValue(!Settings::values.use_docked_mode.GetValue());
dock_status_button->setChecked(Settings::values.use_docked_mode.GetValue());
OnDockedModeChanged(!Settings::values.use_docked_mode.GetValue(),
Settings::values.use_docked_mode.GetValue());
});
dock_status_button->setText(tr("DOCK"));
dock_status_button->setCheckable(true);
dock_status_button->setChecked(Settings::values.use_docked_mode);
dock_status_button->setChecked(Settings::values.use_docked_mode.GetValue());
statusBar()->insertPermanentWidget(0, dock_status_button);
// Setup ASync button
@@ -792,10 +799,11 @@ void GMainWindow::InitializeHotkeys() {
});
connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Change Docked Mode"), this),
&QShortcut::activated, this, [&] {
Settings::values.use_docked_mode = !Settings::values.use_docked_mode;
OnDockedModeChanged(!Settings::values.use_docked_mode,
Settings::values.use_docked_mode);
dock_status_button->setChecked(Settings::values.use_docked_mode);
Settings::values.use_docked_mode.SetValue(
!Settings::values.use_docked_mode.GetValue());
OnDockedModeChanged(!Settings::values.use_docked_mode.GetValue(),
Settings::values.use_docked_mode.GetValue());
dock_status_button->setChecked(Settings::values.use_docked_mode.GetValue());
});
connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Mute Audio"), this),
&QShortcut::activated, this,
@@ -1087,9 +1095,11 @@ void GMainWindow::BootGame(const QString& filename) {
const auto loader = Loader::GetLoader(v_file);
if (!(loader == nullptr || loader->ReadProgramId(title_id) != Loader::ResultStatus::Success)) {
// Load per game settings
Config per_game_config(fmt::format("{:016X}.ini", title_id), false);
Config per_game_config(fmt::format("{:016X}", title_id), Config::ConfigType::PerGameConfig);
}
ConfigureVibration::SetAllVibrationDevices();
Settings::LogSettings();
if (UISettings::values.select_user_on_boot) {
@@ -1577,7 +1587,8 @@ void GMainWindow::RemoveCustomConfiguration(u64 program_id) {
const QString config_dir =
QString::fromStdString(Common::FS::GetUserPath(Common::FS::UserPath::ConfigDir));
const QString custom_config_file_path =
config_dir + QString::fromStdString(fmt::format("{:016X}.ini", program_id));
config_dir + QStringLiteral("custom") + QDir::separator() +
QString::fromStdString(fmt::format("{:016X}.ini", program_id));
if (!QFile::exists(custom_config_file_path)) {
QMessageBox::warning(this, tr("Error Removing Custom Configuration"),
@@ -2393,6 +2404,29 @@ void GMainWindow::OnCaptureScreenshot() {
OnStartGame();
}
// TODO: Written 2020-10-01: Remove per-game config migration code when it is irrelevant
void GMainWindow::MigrateConfigFiles() {
const std::string& config_dir_str = Common::FS::GetUserPath(Common::FS::UserPath::ConfigDir);
const QDir config_dir = QDir(QString::fromStdString(config_dir_str));
const QStringList config_dir_list = config_dir.entryList(QStringList(QStringLiteral("*.ini")));
Common::FS::CreateFullPath(fmt::format("{}custom" DIR_SEP, config_dir_str));
for (QStringList::const_iterator it = config_dir_list.constBegin();
it != config_dir_list.constEnd(); ++it) {
const auto filename = it->toStdString();
if (filename.find_first_not_of("0123456789abcdefACBDEF", 0) < 16) {
continue;
}
const auto origin = fmt::format("{}{}", config_dir_str, filename);
const auto destination = fmt::format("{}custom" DIR_SEP "{}", config_dir_str, filename);
LOG_INFO(Frontend, "Migrating config file from {} to {}", origin, destination);
if (!Common::FS::Rename(origin, destination)) {
// Delete the old config file if one already exists in the new location.
Common::FS::Delete(origin);
}
}
}
void GMainWindow::UpdateWindowTitle(const std::string& title_name,
const std::string& title_version) {
const auto full_name = std::string(Common::g_build_fullname);
@@ -2450,7 +2484,7 @@ void GMainWindow::UpdateStatusBar() {
}
void GMainWindow::UpdateStatusButtons() {
dock_status_button->setChecked(Settings::values.use_docked_mode);
dock_status_button->setChecked(Settings::values.use_docked_mode.GetValue());
multicore_status_button->setChecked(Settings::values.use_multi_core.GetValue());
Settings::values.use_asynchronous_gpu_emulation.SetValue(
Settings::values.use_asynchronous_gpu_emulation.GetValue() ||

View File

@@ -251,6 +251,7 @@ private:
std::optional<u64> SelectRomFSDumpTarget(const FileSys::ContentProvider&, u64 program_id);
InstallResult InstallNSPXCI(const QString& filename);
InstallResult InstallNCA(const QString& filename);
void MigrateConfigFiles();
void UpdateWindowTitle(const std::string& title_name = {},
const std::string& title_version = {});
void UpdateStatusBar();

View File

@@ -228,24 +228,24 @@ static const std::array<int, 8> keyboard_mods{
void Config::ReadValues() {
// Controls
for (std::size_t p = 0; p < Settings::values.players.size(); ++p) {
for (std::size_t p = 0; p < Settings::values.players.GetValue().size(); ++p) {
const auto group = fmt::format("ControlsP{}", p);
for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
Settings::values.players[p].buttons[i] =
Settings::values.players.GetValue()[p].buttons[i] =
sdl2_config->Get(group, Settings::NativeButton::mapping[i], default_param);
if (Settings::values.players[p].buttons[i].empty())
Settings::values.players[p].buttons[i] = default_param;
if (Settings::values.players.GetValue()[p].buttons[i].empty())
Settings::values.players.GetValue()[p].buttons[i] = default_param;
}
for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
default_analogs[i][3], default_analogs[i][4], 0.5f);
Settings::values.players[p].analogs[i] =
Settings::values.players.GetValue()[p].analogs[i] =
sdl2_config->Get(group, Settings::NativeAnalog::mapping[i], default_param);
if (Settings::values.players[p].analogs[i].empty())
Settings::values.players[p].analogs[i] = default_param;
if (Settings::values.players.GetValue()[p].analogs[i].empty())
Settings::values.players.GetValue()[p].analogs[i] = default_param;
}
}
@@ -288,10 +288,12 @@ void Config::ReadValues() {
Settings::values.debug_pad_analogs[i] = default_param;
}
Settings::values.vibration_enabled =
sdl2_config->GetBoolean("ControlsGeneral", "vibration_enabled", true);
Settings::values.motion_enabled =
sdl2_config->GetBoolean("ControlsGeneral", "motion_enabled", true);
Settings::values.vibration_enabled.SetValue(
sdl2_config->GetBoolean("ControlsGeneral", "vibration_enabled", true));
Settings::values.enable_accurate_vibrations.SetValue(
sdl2_config->GetBoolean("ControlsGeneral", "enable_accurate_vibrations", false));
Settings::values.motion_enabled.SetValue(
sdl2_config->GetBoolean("ControlsGeneral", "motion_enabled", true));
Settings::values.touchscreen.enabled =
sdl2_config->GetBoolean("ControlsGeneral", "touch_enabled", true);
Settings::values.touchscreen.device =
@@ -343,7 +345,8 @@ void Config::ReadValues() {
Settings::values.gamecard_path = sdl2_config->Get("Data Storage", "gamecard_path", "");
// System
Settings::values.use_docked_mode = sdl2_config->GetBoolean("System", "use_docked_mode", false);
Settings::values.use_docked_mode.SetValue(
sdl2_config->GetBoolean("System", "use_docked_mode", false));
const auto size = sdl2_config->GetInteger("System", "users_size", 0);
Settings::values.current_user = std::clamp<int>(

View File

@@ -65,6 +65,14 @@ button_screenshot=
lstick=
rstick=
# Whether to enable or disable vibration
# 0: Disabled, 1 (default): Enabled
vibration_enabled=
# Whether to enable or disable accurate vibrations
# 0 (default): Disabled, 1: Enabled
enable_accurate_vibrations=
# for motion input, the following devices are available:
# - "motion_emu" (default) for emulating motion input from mouse input. Required parameters:
# - "update_period": update period in milliseconds (default to 100)

View File

@@ -47,13 +47,13 @@ bool Config::LoadINI(const std::string& default_contents, bool retry) {
void Config::ReadValues() {
// Controls
for (std::size_t p = 0; p < Settings::values.players.size(); ++p) {
for (std::size_t p = 0; p < Settings::values.players.GetValue().size(); ++p) {
for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
Settings::values.players[p].buttons[i] = "";
Settings::values.players.GetValue()[p].buttons[i] = "";
}
for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
Settings::values.players[p].analogs[i] = "";
Settings::values.players.GetValue()[p].analogs[i] = "";
}
}
@@ -75,8 +75,9 @@ void Config::ReadValues() {
Settings::values.debug_pad_analogs[i] = "";
}
Settings::values.vibration_enabled = true;
Settings::values.motion_enabled = true;
Settings::values.vibration_enabled.SetValue(true);
Settings::values.enable_accurate_vibrations.SetValue(false);
Settings::values.motion_enabled.SetValue(true);
Settings::values.touchscreen.enabled = "";
Settings::values.touchscreen.device = "";
Settings::values.touchscreen.finger = 0;
@@ -84,8 +85,8 @@ void Config::ReadValues() {
Settings::values.touchscreen.diameter_x = 15;
Settings::values.touchscreen.diameter_y = 15;
Settings::values.use_docked_mode =
sdl2_config->GetBoolean("Controls", "use_docked_mode", false);
Settings::values.use_docked_mode.SetValue(
sdl2_config->GetBoolean("Controls", "use_docked_mode", false));
// Data Storage
Settings::values.use_virtual_sd =