configure_mouse_advanced.cpp 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276
  1. // Copyright 2016 Citra Emulator Project
  2. // Licensed under GPLv2 or any later version
  3. // Refer to the license.txt file included.
  4. #include <algorithm>
  5. #include <memory>
  6. #include <QKeyEvent>
  7. #include <QMenu>
  8. #include <QTimer>
  9. #include "common/assert.h"
  10. #include "common/param_package.h"
  11. #include "input_common/main.h"
  12. #include "ui_configure_mouse_advanced.h"
  13. #include "yuzu/configuration/config.h"
  14. #include "yuzu/configuration/configure_mouse_advanced.h"
  15. static QString GetKeyName(int key_code) {
  16. switch (key_code) {
  17. case Qt::LeftButton:
  18. return QObject::tr("Click 0");
  19. case Qt::RightButton:
  20. return QObject::tr("Click 1");
  21. case Qt::MiddleButton:
  22. return QObject::tr("Click 2");
  23. case Qt::BackButton:
  24. return QObject::tr("Click 3");
  25. case Qt::ForwardButton:
  26. return QObject::tr("Click 4");
  27. case Qt::Key_Shift:
  28. return QObject::tr("Shift");
  29. case Qt::Key_Control:
  30. return QObject::tr("Ctrl");
  31. case Qt::Key_Alt:
  32. return QObject::tr("Alt");
  33. case Qt::Key_Meta:
  34. return {};
  35. default:
  36. return QKeySequence(key_code).toString();
  37. }
  38. }
  39. static QString ButtonToText(const Common::ParamPackage& param) {
  40. if (!param.Has("engine")) {
  41. return QObject::tr("[not set]");
  42. }
  43. if (param.Get("engine", "") == "keyboard") {
  44. return GetKeyName(param.Get("code", 0));
  45. }
  46. if (param.Get("engine", "") == "sdl") {
  47. if (param.Has("hat")) {
  48. const QString hat_str = QString::fromStdString(param.Get("hat", ""));
  49. const QString direction_str = QString::fromStdString(param.Get("direction", ""));
  50. return QObject::tr("Hat %1 %2").arg(hat_str, direction_str);
  51. }
  52. if (param.Has("axis")) {
  53. const QString axis_str = QString::fromStdString(param.Get("axis", ""));
  54. const QString direction_str = QString::fromStdString(param.Get("direction", ""));
  55. return QObject::tr("Axis %1%2").arg(axis_str, direction_str);
  56. }
  57. if (param.Has("button")) {
  58. const QString button_str = QString::fromStdString(param.Get("button", ""));
  59. return QObject::tr("Button %1").arg(button_str);
  60. }
  61. return {};
  62. }
  63. return QObject::tr("[unknown]");
  64. }
  65. ConfigureMouseAdvanced::ConfigureMouseAdvanced(QWidget* parent,
  66. InputCommon::InputSubsystem* input_subsystem_)
  67. : QDialog(parent),
  68. ui(std::make_unique<Ui::ConfigureMouseAdvanced>()), input_subsystem{input_subsystem_},
  69. timeout_timer(std::make_unique<QTimer>()), poll_timer(std::make_unique<QTimer>()) {
  70. ui->setupUi(this);
  71. setFocusPolicy(Qt::ClickFocus);
  72. button_map = {
  73. ui->left_button, ui->right_button, ui->middle_button, ui->forward_button, ui->back_button,
  74. };
  75. for (int button_id = 0; button_id < Settings::NativeMouseButton::NumMouseButtons; button_id++) {
  76. auto* const button = button_map[button_id];
  77. if (button == nullptr) {
  78. continue;
  79. }
  80. button->setContextMenuPolicy(Qt::CustomContextMenu);
  81. connect(button, &QPushButton::clicked, [=, this] {
  82. HandleClick(
  83. button_map[button_id],
  84. [=, this](const Common::ParamPackage& params) {
  85. buttons_param[button_id] = params;
  86. },
  87. InputCommon::Polling::DeviceType::Button);
  88. });
  89. connect(button, &QPushButton::customContextMenuRequested,
  90. [=, this](const QPoint& menu_location) {
  91. QMenu context_menu;
  92. context_menu.addAction(tr("Clear"), [&] {
  93. buttons_param[button_id].Clear();
  94. button_map[button_id]->setText(tr("[not set]"));
  95. });
  96. context_menu.addAction(tr("Restore Default"), [&] {
  97. buttons_param[button_id] =
  98. Common::ParamPackage{InputCommon::GenerateKeyboardParam(
  99. Config::default_mouse_buttons[button_id])};
  100. button_map[button_id]->setText(ButtonToText(buttons_param[button_id]));
  101. });
  102. context_menu.exec(button_map[button_id]->mapToGlobal(menu_location));
  103. });
  104. }
  105. connect(ui->buttonClearAll, &QPushButton::clicked, [this] { ClearAll(); });
  106. connect(ui->buttonRestoreDefaults, &QPushButton::clicked, [this] { RestoreDefaults(); });
  107. timeout_timer->setSingleShot(true);
  108. connect(timeout_timer.get(), &QTimer::timeout, [this] { SetPollingResult({}, true); });
  109. connect(poll_timer.get(), &QTimer::timeout, [this] {
  110. Common::ParamPackage params;
  111. for (auto& poller : device_pollers) {
  112. params = poller->GetNextInput();
  113. if (params.Has("engine")) {
  114. SetPollingResult(params, false);
  115. return;
  116. }
  117. }
  118. });
  119. LoadConfiguration();
  120. resize(0, 0);
  121. }
  122. ConfigureMouseAdvanced::~ConfigureMouseAdvanced() = default;
  123. void ConfigureMouseAdvanced::ApplyConfiguration() {
  124. std::transform(buttons_param.begin(), buttons_param.end(),
  125. Settings::values.mouse_buttons.begin(),
  126. [](const Common::ParamPackage& param) { return param.Serialize(); });
  127. }
  128. void ConfigureMouseAdvanced::LoadConfiguration() {
  129. std::transform(Settings::values.mouse_buttons.begin(), Settings::values.mouse_buttons.end(),
  130. buttons_param.begin(),
  131. [](const std::string& str) { return Common::ParamPackage(str); });
  132. UpdateButtonLabels();
  133. }
  134. void ConfigureMouseAdvanced::changeEvent(QEvent* event) {
  135. if (event->type() == QEvent::LanguageChange) {
  136. RetranslateUI();
  137. }
  138. QDialog::changeEvent(event);
  139. }
  140. void ConfigureMouseAdvanced::RetranslateUI() {
  141. ui->retranslateUi(this);
  142. }
  143. void ConfigureMouseAdvanced::RestoreDefaults() {
  144. for (int button_id = 0; button_id < Settings::NativeMouseButton::NumMouseButtons; button_id++) {
  145. buttons_param[button_id] = Common::ParamPackage{
  146. InputCommon::GenerateKeyboardParam(Config::default_mouse_buttons[button_id])};
  147. }
  148. UpdateButtonLabels();
  149. }
  150. void ConfigureMouseAdvanced::ClearAll() {
  151. for (int i = 0; i < Settings::NativeMouseButton::NumMouseButtons; ++i) {
  152. const auto* const button = button_map[i];
  153. if (button != nullptr && button->isEnabled()) {
  154. buttons_param[i].Clear();
  155. }
  156. }
  157. UpdateButtonLabels();
  158. }
  159. void ConfigureMouseAdvanced::UpdateButtonLabels() {
  160. for (int button = 0; button < Settings::NativeMouseButton::NumMouseButtons; button++) {
  161. button_map[button]->setText(ButtonToText(buttons_param[button]));
  162. }
  163. }
  164. void ConfigureMouseAdvanced::HandleClick(
  165. QPushButton* button, std::function<void(const Common::ParamPackage&)> new_input_setter,
  166. InputCommon::Polling::DeviceType type) {
  167. button->setText(tr("[press key]"));
  168. button->setFocus();
  169. // Keyboard keys or mouse buttons can only be used as button devices
  170. want_keyboard_mouse = type == InputCommon::Polling::DeviceType::Button;
  171. if (want_keyboard_mouse) {
  172. const auto iter = std::find(button_map.begin(), button_map.end(), button);
  173. ASSERT(iter != button_map.end());
  174. const auto index = std::distance(button_map.begin(), iter);
  175. ASSERT(index < Settings::NativeButton::NumButtons && index >= 0);
  176. }
  177. input_setter = new_input_setter;
  178. device_pollers = input_subsystem->GetPollers(type);
  179. for (auto& poller : device_pollers) {
  180. poller->Start();
  181. }
  182. QWidget::grabMouse();
  183. QWidget::grabKeyboard();
  184. timeout_timer->start(2500); // Cancel after 2.5 seconds
  185. poll_timer->start(50); // Check for new inputs every 50ms
  186. }
  187. void ConfigureMouseAdvanced::SetPollingResult(const Common::ParamPackage& params, bool abort) {
  188. timeout_timer->stop();
  189. poll_timer->stop();
  190. for (auto& poller : device_pollers) {
  191. poller->Stop();
  192. }
  193. QWidget::releaseMouse();
  194. QWidget::releaseKeyboard();
  195. if (!abort) {
  196. (*input_setter)(params);
  197. }
  198. UpdateButtonLabels();
  199. input_setter = std::nullopt;
  200. }
  201. void ConfigureMouseAdvanced::mousePressEvent(QMouseEvent* event) {
  202. if (!input_setter || !event) {
  203. return;
  204. }
  205. if (want_keyboard_mouse) {
  206. SetPollingResult(Common::ParamPackage{InputCommon::GenerateKeyboardParam(event->button())},
  207. false);
  208. } else {
  209. // We don't want any mouse buttons, so don't stop polling
  210. return;
  211. }
  212. SetPollingResult({}, true);
  213. }
  214. void ConfigureMouseAdvanced::keyPressEvent(QKeyEvent* event) {
  215. if (!input_setter || !event) {
  216. return;
  217. }
  218. if (event->key() != Qt::Key_Escape) {
  219. if (want_keyboard_mouse) {
  220. SetPollingResult(Common::ParamPackage{InputCommon::GenerateKeyboardParam(event->key())},
  221. false);
  222. } else {
  223. // Escape key wasn't pressed and we don't want any keyboard keys, so don't stop polling
  224. return;
  225. }
  226. }
  227. SetPollingResult({}, true);
  228. }