summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGravatar Morph2020-09-17 12:00:29 -0400
committerGravatar Morph2020-11-15 23:33:19 -0500
commit75eaab2e0f48eb588c1bfb85f96630e199fbc1da (patch)
treeb32ae39414e12787932aed24a5e42c39fcba47cb /src
parentui/themes: Cleanup UI (diff)
downloadyuzu-75eaab2e0f48eb588c1bfb85f96630e199fbc1da.tar.gz
yuzu-75eaab2e0f48eb588c1bfb85f96630e199fbc1da.tar.xz
yuzu-75eaab2e0f48eb588c1bfb85f96630e199fbc1da.zip
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".
Diffstat (limited to 'src')
-rw-r--r--src/input_common/main.cpp6
-rw-r--r--src/yuzu/configuration/configure_input.cpp2
-rw-r--r--src/yuzu/configuration/configure_input_player.cpp318
-rw-r--r--src/yuzu/configuration/configure_input_player.h17
4 files changed, 205 insertions, 138 deletions
diff --git a/src/input_common/main.cpp b/src/input_common/main.cpp
index d32fd8b81..354c734fe 100644
--- a/src/input_common/main.cpp
+++ b/src/input_common/main.cpp
@@ -78,7 +78,7 @@ struct InputSubsystem::Impl {
78 [[nodiscard]] std::vector<Common::ParamPackage> GetInputDevices() const { 78 [[nodiscard]] std::vector<Common::ParamPackage> GetInputDevices() const {
79 std::vector<Common::ParamPackage> devices = { 79 std::vector<Common::ParamPackage> devices = {
80 Common::ParamPackage{{"display", "Any"}, {"class", "any"}}, 80 Common::ParamPackage{{"display", "Any"}, {"class", "any"}},
81 Common::ParamPackage{{"display", "Keyboard/Mouse"}, {"class", "key"}}, 81 Common::ParamPackage{{"display", "Keyboard/Mouse"}, {"class", "keyboard"}},
82 }; 82 };
83#ifdef HAVE_SDL2 83#ifdef HAVE_SDL2
84 auto sdl_devices = sdl->GetInputDevices(); 84 auto sdl_devices = sdl->GetInputDevices();
@@ -96,7 +96,7 @@ struct InputSubsystem::Impl {
96 if (!params.Has("class") || params.Get("class", "") == "any") { 96 if (!params.Has("class") || params.Get("class", "") == "any") {
97 return {}; 97 return {};
98 } 98 }
99 if (params.Get("class", "") == "key") { 99 if (params.Get("class", "") == "keyboard") {
100 // TODO consider returning the SDL key codes for the default keybindings 100 // TODO consider returning the SDL key codes for the default keybindings
101 return {}; 101 return {};
102 } 102 }
@@ -116,7 +116,7 @@ struct InputSubsystem::Impl {
116 if (!params.Has("class") || params.Get("class", "") == "any") { 116 if (!params.Has("class") || params.Get("class", "") == "any") {
117 return {}; 117 return {};
118 } 118 }
119 if (params.Get("class", "") == "key") { 119 if (params.Get("class", "") == "keyboard") {
120 // TODO consider returning the SDL key codes for the default keybindings 120 // TODO consider returning the SDL key codes for the default keybindings
121 return {}; 121 return {};
122 } 122 }
diff --git a/src/yuzu/configuration/configure_input.cpp b/src/yuzu/configuration/configure_input.cpp
index 2725fcb2b..f2932aa0b 100644
--- a/src/yuzu/configuration/configure_input.cpp
+++ b/src/yuzu/configuration/configure_input.cpp
@@ -242,6 +242,6 @@ void ConfigureInput::UpdateDockedState(bool is_handheld) {
242 242
243void ConfigureInput::UpdateAllInputDevices() { 243void ConfigureInput::UpdateAllInputDevices() {
244 for (const auto& player : player_controllers) { 244 for (const auto& player : player_controllers) {
245 player->UpdateInputDevices(); 245 player->UpdateInputDeviceCombobox();
246 } 246 }
247} 247}
diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp
index f58ca29d7..0de0c6999 100644
--- a/src/yuzu/configuration/configure_input_player.cpp
+++ b/src/yuzu/configuration/configure_input_player.cpp
@@ -477,11 +477,12 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
477 UpdateMotionButtons(); 477 UpdateMotionButtons();
478 }); 478 });
479 479
480 connect(ui->comboDevices, qOverload<int>(&QComboBox::currentIndexChanged), this, 480 connect(ui->comboDevices, qOverload<int>(&QComboBox::activated), this,
481 &ConfigureInputPlayer::UpdateMappingWithDefaults); 481 &ConfigureInputPlayer::UpdateMappingWithDefaults);
482 482
483 ui->comboDevices->setCurrentIndex(-1);
484
483 ui->buttonRefreshDevices->setIcon(QIcon::fromTheme(QStringLiteral("view-refresh"))); 485 ui->buttonRefreshDevices->setIcon(QIcon::fromTheme(QStringLiteral("view-refresh")));
484 UpdateInputDevices();
485 connect(ui->buttonRefreshDevices, &QPushButton::clicked, 486 connect(ui->buttonRefreshDevices, &QPushButton::clicked,
486 [this] { emit RefreshInputDevices(); }); 487 [this] { emit RefreshInputDevices(); });
487 488
@@ -492,14 +493,14 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
492 Common::ParamPackage params; 493 Common::ParamPackage params;
493 if (input_subsystem->GetGCButtons()->IsPolling()) { 494 if (input_subsystem->GetGCButtons()->IsPolling()) {
494 params = input_subsystem->GetGCButtons()->GetNextInput(); 495 params = input_subsystem->GetGCButtons()->GetNextInput();
495 if (params.Has("engine")) { 496 if (params.Has("engine") && IsInputAcceptable(params)) {
496 SetPollingResult(params, false); 497 SetPollingResult(params, false);
497 return; 498 return;
498 } 499 }
499 } 500 }
500 if (input_subsystem->GetGCAnalogs()->IsPolling()) { 501 if (input_subsystem->GetGCAnalogs()->IsPolling()) {
501 params = input_subsystem->GetGCAnalogs()->GetNextInput(); 502 params = input_subsystem->GetGCAnalogs()->GetNextInput();
502 if (params.Has("engine")) { 503 if (params.Has("engine") && IsInputAcceptable(params)) {
503 SetPollingResult(params, false); 504 SetPollingResult(params, false);
504 return; 505 return;
505 } 506 }
@@ -513,7 +514,7 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
513 } 514 }
514 for (auto& poller : device_pollers) { 515 for (auto& poller : device_pollers) {
515 params = poller->GetNextInput(); 516 params = poller->GetNextInput();
516 if (params.Has("engine")) { 517 if (params.Has("engine") && IsInputAcceptable(params)) {
517 SetPollingResult(params, false); 518 SetPollingResult(params, false);
518 return; 519 return;
519 } 520 }
@@ -572,6 +573,14 @@ void ConfigureInputPlayer::ApplyConfiguration() {
572 UpdateController(Settings::ControllerType::Handheld, HANDHELD_INDEX, handheld.connected); 573 UpdateController(Settings::ControllerType::Handheld, HANDHELD_INDEX, handheld.connected);
573} 574}
574 575
576void ConfigureInputPlayer::showEvent(QShowEvent* event) {
577 if (bottom_row == nullptr) {
578 return;
579 }
580 QWidget::showEvent(event);
581 ui->main->addWidget(bottom_row);
582}
583
575void ConfigureInputPlayer::changeEvent(QEvent* event) { 584void ConfigureInputPlayer::changeEvent(QEvent* event) {
576 if (event->type() == QEvent::LanguageChange) { 585 if (event->type() == QEvent::LanguageChange) {
577 RetranslateUI(); 586 RetranslateUI();
@@ -604,6 +613,7 @@ void ConfigureInputPlayer::LoadConfiguration() {
604 } 613 }
605 614
606 UpdateUI(); 615 UpdateUI();
616 UpdateInputDeviceCombobox();
607 617
608 if (debug) { 618 if (debug) {
609 return; 619 return;
@@ -615,11 +625,56 @@ void ConfigureInputPlayer::LoadConfiguration() {
615 (player_index == 0 && Settings::values.players[HANDHELD_INDEX].connected)); 625 (player_index == 0 && Settings::values.players[HANDHELD_INDEX].connected));
616} 626}
617 627
618void ConfigureInputPlayer::UpdateInputDevices() { 628void ConfigureInputPlayer::ConnectPlayer(bool connected) {
619 input_devices = input_subsystem->GetInputDevices(); 629 ui->groupConnectedController->setChecked(connected);
620 ui->comboDevices->clear(); 630}
621 for (auto device : input_devices) { 631
622 ui->comboDevices->addItem(QString::fromStdString(device.Get("display", "Unknown")), {}); 632void ConfigureInputPlayer::UpdateInputDeviceCombobox() {
633 // Skip input device persistence if "Input Devices" is set to "Any".
634 if (ui->comboDevices->currentIndex() == 0) {
635 UpdateInputDevices();
636 return;
637 }
638
639 // Find the first button that isn't empty.
640 const auto button_param =
641 std::find_if(buttons_param.begin(), buttons_param.end(),
642 [](const Common::ParamPackage param) { return param.Has("engine"); });
643 const bool buttons_empty = button_param == buttons_param.end();
644
645 const auto current_engine = button_param->Get("engine", "");
646 const auto current_guid = button_param->Get("guid", "");
647 const auto current_port = button_param->Get("port", "");
648
649 UpdateInputDevices();
650
651 if (buttons_empty) {
652 return;
653 }
654
655 const bool all_one_device =
656 std::all_of(buttons_param.begin(), buttons_param.end(),
657 [current_engine, current_guid, current_port](const Common::ParamPackage param) {
658 return !param.Has("engine") || (param.Get("engine", "") == current_engine &&
659 param.Get("guid", "") == current_guid &&
660 param.Get("port", "") == current_port);
661 });
662
663 if (all_one_device) {
664 const auto devices_it = std::find_if(
665 input_devices.begin(), input_devices.end(),
666 [current_engine, current_guid, current_port](const Common::ParamPackage param) {
667 return param.Get("class", "") == current_engine &&
668 param.Get("guid", "") == current_guid &&
669 param.Get("port", "") == current_port;
670 });
671 const int device_index =
672 devices_it != input_devices.end()
673 ? static_cast<int>(std::distance(input_devices.begin(), devices_it))
674 : 0;
675 ui->comboDevices->setCurrentIndex(device_index);
676 } else {
677 ui->comboDevices->setCurrentIndex(0);
623 } 678 }
624} 679}
625 680
@@ -648,7 +703,7 @@ void ConfigureInputPlayer::RestoreDefaults() {
648 } 703 }
649 704
650 UpdateUI(); 705 UpdateUI();
651 UpdateInputDevices(); 706 UpdateInputDeviceCombobox();
652 ui->comboControllerType->setCurrentIndex(0); 707 ui->comboControllerType->setCurrentIndex(0);
653} 708}
654 709
@@ -752,117 +807,12 @@ void ConfigureInputPlayer::UpdateUI() {
752 } 807 }
753} 808}
754 809
755void ConfigureInputPlayer::UpdateMappingWithDefaults() { 810void ConfigureInputPlayer::UpdateInputDevices() {
756 if (ui->comboDevices->currentIndex() < 2) { 811 input_devices = input_subsystem->GetInputDevices();
757 return; 812 ui->comboDevices->clear();
758 } 813 for (auto device : input_devices) {
759 const auto& device = input_devices[ui->comboDevices->currentIndex()]; 814 ui->comboDevices->addItem(QString::fromStdString(device.Get("display", "Unknown")), {});
760 auto button_mapping = input_subsystem->GetButtonMappingForDevice(device);
761 auto analog_mapping = input_subsystem->GetAnalogMappingForDevice(device);
762 for (std::size_t i = 0; i < buttons_param.size(); ++i) {
763 buttons_param[i] = button_mapping[static_cast<Settings::NativeButton::Values>(i)];
764 }
765 for (std::size_t i = 0; i < analogs_param.size(); ++i) {
766 analogs_param[i] = analog_mapping[static_cast<Settings::NativeAnalog::Values>(i)];
767 }
768
769 UpdateUI();
770}
771
772void ConfigureInputPlayer::HandleClick(
773 QPushButton* button, std::function<void(const Common::ParamPackage&)> new_input_setter,
774 InputCommon::Polling::DeviceType type) {
775 if (button == ui->buttonMotionLeft || button == ui->buttonMotionRight) {
776 button->setText(tr("Shake!"));
777 } else {
778 button->setText(tr("[waiting]"));
779 }
780 button->setFocus();
781
782 // The first two input devices are always Any and Keyboard/Mouse. If the user filtered to a
783 // controller, then they don't want keyboard/mouse input
784 want_keyboard_mouse = ui->comboDevices->currentIndex() < 2;
785
786 input_setter = new_input_setter;
787
788 device_pollers = input_subsystem->GetPollers(type);
789
790 for (auto& poller : device_pollers) {
791 poller->Start();
792 }
793
794 QWidget::grabMouse();
795 QWidget::grabKeyboard();
796
797 if (type == InputCommon::Polling::DeviceType::Button) {
798 input_subsystem->GetGCButtons()->BeginConfiguration();
799 } else {
800 input_subsystem->GetGCAnalogs()->BeginConfiguration();
801 }
802
803 if (type == InputCommon::Polling::DeviceType::Motion) {
804 input_subsystem->GetUDPMotions()->BeginConfiguration();
805 }
806
807 timeout_timer->start(2500); // Cancel after 2.5 seconds
808 poll_timer->start(50); // Check for new inputs every 50ms
809}
810
811void ConfigureInputPlayer::SetPollingResult(const Common::ParamPackage& params, bool abort) {
812 timeout_timer->stop();
813 poll_timer->stop();
814 for (auto& poller : device_pollers) {
815 poller->Stop();
816 }
817
818 QWidget::releaseMouse();
819 QWidget::releaseKeyboard();
820
821 input_subsystem->GetGCButtons()->EndConfiguration();
822 input_subsystem->GetGCAnalogs()->EndConfiguration();
823
824 input_subsystem->GetUDPMotions()->EndConfiguration();
825
826 if (!abort) {
827 (*input_setter)(params);
828 }
829
830 UpdateUI();
831 input_setter = std::nullopt;
832}
833
834void ConfigureInputPlayer::mousePressEvent(QMouseEvent* event) {
835 if (!input_setter || !event) {
836 return;
837 }
838
839 if (want_keyboard_mouse) {
840 SetPollingResult(Common::ParamPackage{InputCommon::GenerateKeyboardParam(event->button())},
841 false);
842 } else {
843 // We don't want any mouse buttons, so don't stop polling
844 return;
845 }
846
847 SetPollingResult({}, true);
848}
849
850void ConfigureInputPlayer::keyPressEvent(QKeyEvent* event) {
851 if (!input_setter || !event) {
852 return;
853 }
854
855 if (event->key() != Qt::Key_Escape) {
856 if (want_keyboard_mouse) {
857 SetPollingResult(Common::ParamPackage{InputCommon::GenerateKeyboardParam(event->key())},
858 false);
859 } else {
860 // Escape key wasn't pressed and we don't want any keyboard keys, so don't stop polling
861 return;
862 }
863 } 815 }
864
865 SetPollingResult({}, true);
866} 816}
867 817
868void ConfigureInputPlayer::UpdateControllerIcon() { 818void ConfigureInputPlayer::UpdateControllerIcon() {
@@ -986,14 +936,128 @@ void ConfigureInputPlayer::UpdateMotionButtons() {
986 } 936 }
987} 937}
988 938
989void ConfigureInputPlayer::showEvent(QShowEvent* event) { 939void ConfigureInputPlayer::UpdateMappingWithDefaults() {
990 if (bottom_row == nullptr) { 940 if (ui->comboDevices->currentIndex() < 2) {
991 return; 941 return;
992 } 942 }
993 QWidget::showEvent(event); 943 const auto& device = input_devices[ui->comboDevices->currentIndex()];
994 ui->main->addWidget(bottom_row); 944 auto button_mapping = input_subsystem->GetButtonMappingForDevice(device);
945 auto analog_mapping = input_subsystem->GetAnalogMappingForDevice(device);
946 for (std::size_t i = 0; i < buttons_param.size(); ++i) {
947 buttons_param[i] = button_mapping[static_cast<Settings::NativeButton::Values>(i)];
948 }
949 for (std::size_t i = 0; i < analogs_param.size(); ++i) {
950 analogs_param[i] = analog_mapping[static_cast<Settings::NativeAnalog::Values>(i)];
951 }
952
953 UpdateUI();
995} 954}
996 955
997void ConfigureInputPlayer::ConnectPlayer(bool connected) { 956void ConfigureInputPlayer::HandleClick(
998 ui->groupConnectedController->setChecked(connected); 957 QPushButton* button, std::function<void(const Common::ParamPackage&)> new_input_setter,
958 InputCommon::Polling::DeviceType type) {
959 if (button == ui->buttonMotionLeft || button == ui->buttonMotionRight) {
960 button->setText(tr("Shake!"));
961 } else {
962 button->setText(tr("[waiting]"));
963 }
964 button->setFocus();
965
966 // The first two input devices are always Any and Keyboard/Mouse. If the user filtered to a
967 // controller, then they don't want keyboard/mouse input
968 want_keyboard_mouse = ui->comboDevices->currentIndex() < 2;
969
970 input_setter = new_input_setter;
971
972 device_pollers = input_subsystem->GetPollers(type);
973
974 for (auto& poller : device_pollers) {
975 poller->Start();
976 }
977
978 QWidget::grabMouse();
979 QWidget::grabKeyboard();
980
981 if (type == InputCommon::Polling::DeviceType::Button) {
982 input_subsystem->GetGCButtons()->BeginConfiguration();
983 } else {
984 input_subsystem->GetGCAnalogs()->BeginConfiguration();
985 }
986
987 if (type == InputCommon::Polling::DeviceType::Motion) {
988 input_subsystem->GetUDPMotions()->BeginConfiguration();
989 }
990
991 timeout_timer->start(2500); // Cancel after 2.5 seconds
992 poll_timer->start(50); // Check for new inputs every 50ms
993}
994
995void ConfigureInputPlayer::SetPollingResult(const Common::ParamPackage& params, bool abort) {
996 timeout_timer->stop();
997 poll_timer->stop();
998 for (auto& poller : device_pollers) {
999 poller->Stop();
1000 }
1001
1002 QWidget::releaseMouse();
1003 QWidget::releaseKeyboard();
1004
1005 input_subsystem->GetGCButtons()->EndConfiguration();
1006 input_subsystem->GetGCAnalogs()->EndConfiguration();
1007
1008 input_subsystem->GetUDPMotions()->EndConfiguration();
1009
1010 if (!abort) {
1011 (*input_setter)(params);
1012 }
1013
1014 UpdateUI();
1015 UpdateInputDeviceCombobox();
1016
1017 input_setter = std::nullopt;
1018}
1019
1020bool ConfigureInputPlayer::IsInputAcceptable(const Common::ParamPackage& params) const {
1021 if (ui->comboDevices->currentIndex() == 0) {
1022 return true;
1023 }
1024
1025 const auto current_input_device = input_devices[ui->comboDevices->currentIndex()];
1026 return params.Get("engine", "") == current_input_device.Get("class", "") &&
1027 params.Get("guid", "") == current_input_device.Get("guid", "") &&
1028 params.Get("port", "") == current_input_device.Get("port", "");
1029}
1030
1031void ConfigureInputPlayer::mousePressEvent(QMouseEvent* event) {
1032 if (!input_setter || !event) {
1033 return;
1034 }
1035
1036 if (want_keyboard_mouse) {
1037 SetPollingResult(Common::ParamPackage{InputCommon::GenerateKeyboardParam(event->button())},
1038 false);
1039 } else {
1040 // We don't want any mouse buttons, so don't stop polling
1041 return;
1042 }
1043
1044 SetPollingResult({}, true);
1045}
1046
1047void ConfigureInputPlayer::keyPressEvent(QKeyEvent* event) {
1048 if (!input_setter || !event) {
1049 return;
1050 }
1051
1052 if (event->key() != Qt::Key_Escape) {
1053 if (want_keyboard_mouse) {
1054 SetPollingResult(Common::ParamPackage{InputCommon::GenerateKeyboardParam(event->key())},
1055 false);
1056 } else {
1057 // Escape key wasn't pressed and we don't want any keyboard keys, so don't stop polling
1058 return;
1059 }
1060 }
1061
1062 SetPollingResult({}, true);
999} 1063}
diff --git a/src/yuzu/configuration/configure_input_player.h b/src/yuzu/configuration/configure_input_player.h
index c19aefffa..a5414e624 100644
--- a/src/yuzu/configuration/configure_input_player.h
+++ b/src/yuzu/configuration/configure_input_player.h
@@ -51,8 +51,11 @@ public:
51 /// Save all button configurations to settings file. 51 /// Save all button configurations to settings file.
52 void ApplyConfiguration(); 52 void ApplyConfiguration();
53 53
54 /// Set the connection state checkbox (used to sync state).
55 void ConnectPlayer(bool connected);
56
54 /// Update the input devices combobox. 57 /// Update the input devices combobox.
55 void UpdateInputDevices(); 58 void UpdateInputDeviceCombobox();
56 59
57 /// Restore all buttons to their default values. 60 /// Restore all buttons to their default values.
58 void RestoreDefaults(); 61 void RestoreDefaults();
@@ -60,9 +63,6 @@ public:
60 /// Clear all input configuration. 63 /// Clear all input configuration.
61 void ClearAll(); 64 void ClearAll();
62 65
63 /// Set the connection state checkbox (used to sync state).
64 void ConnectPlayer(bool connected);
65
66signals: 66signals:
67 /// Emitted when this controller is connected by the user. 67 /// Emitted when this controller is connected by the user.
68 void Connected(bool connected); 68 void Connected(bool connected);
@@ -89,6 +89,9 @@ private:
89 /// Finish polling and configure input using the input_setter. 89 /// Finish polling and configure input using the input_setter.
90 void SetPollingResult(const Common::ParamPackage& params, bool abort); 90 void SetPollingResult(const Common::ParamPackage& params, bool abort);
91 91
92 /// Checks whether a given input can be accepted.
93 bool IsInputAcceptable(const Common::ParamPackage& params) const;
94
92 /// Handle mouse button press events. 95 /// Handle mouse button press events.
93 void mousePressEvent(QMouseEvent* event) override; 96 void mousePressEvent(QMouseEvent* event) override;
94 97
@@ -98,8 +101,8 @@ private:
98 /// Update UI to reflect current configuration. 101 /// Update UI to reflect current configuration.
99 void UpdateUI(); 102 void UpdateUI();
100 103
101 /// Update the controller selection combobox 104 /// Update the available input devices.
102 void UpdateControllerCombobox(); 105 void UpdateInputDevices();
103 106
104 /// Update the current controller icon. 107 /// Update the current controller icon.
105 void UpdateControllerIcon(); 108 void UpdateControllerIcon();
@@ -164,7 +167,7 @@ private:
164 bool want_keyboard_mouse = false; 167 bool want_keyboard_mouse = false;
165 168
166 /// List of physical devices users can map with. If a SDL backed device is selected, then you 169 /// List of physical devices users can map with. If a SDL backed device is selected, then you
167 /// can usue this device to get a default mapping. 170 /// can use this device to get a default mapping.
168 std::vector<Common::ParamPackage> input_devices; 171 std::vector<Common::ParamPackage> input_devices;
169 172
170 /// Bottom row is where console wide settings are held, and its "owned" by the parent 173 /// Bottom row is where console wide settings are held, and its "owned" by the parent