summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/input_common/main.cpp11
-rw-r--r--src/input_common/main.h32
-rw-r--r--src/input_common/sdl/sdl.cpp183
-rw-r--r--src/input_common/sdl/sdl.h23
-rw-r--r--src/yuzu/bootmanager.cpp1
-rw-r--r--src/yuzu/configuration/config.cpp2
-rw-r--r--src/yuzu/configuration/configure_input.cpp139
-rw-r--r--src/yuzu/configuration/configure_input.h31
-rw-r--r--src/yuzu/configuration/configure_input.ui296
-rw-r--r--src/yuzu/main.cpp58
-rw-r--r--src/yuzu/main.h3
-rw-r--r--src/yuzu/main.ui11
-rw-r--r--src/yuzu/ui_settings.h1
13 files changed, 599 insertions, 192 deletions
diff --git a/src/input_common/main.cpp b/src/input_common/main.cpp
index 557353740..95d40f09f 100644
--- a/src/input_common/main.cpp
+++ b/src/input_common/main.cpp
@@ -71,4 +71,15 @@ std::string GenerateAnalogParamFromKeys(int key_up, int key_down, int key_left,
71 return circle_pad_param.Serialize(); 71 return circle_pad_param.Serialize();
72} 72}
73 73
74namespace Polling {
75
76std::vector<std::unique_ptr<DevicePoller>> GetPollers(DeviceType type) {
77#ifdef HAVE_SDL2
78 return SDL::Polling::GetPollers(type);
79#else
80 return {};
81#endif
82}
83
84} // namespace Polling
74} // namespace InputCommon 85} // namespace InputCommon
diff --git a/src/input_common/main.h b/src/input_common/main.h
index 5604f0fa8..77a0ce90b 100644
--- a/src/input_common/main.h
+++ b/src/input_common/main.h
@@ -4,7 +4,13 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <memory>
7#include <string> 8#include <string>
9#include <vector>
10
11namespace Common {
12class ParamPackage;
13}
8 14
9namespace InputCommon { 15namespace InputCommon {
10 16
@@ -31,4 +37,30 @@ std::string GenerateKeyboardParam(int key_code);
31std::string GenerateAnalogParamFromKeys(int key_up, int key_down, int key_left, int key_right, 37std::string GenerateAnalogParamFromKeys(int key_up, int key_down, int key_left, int key_right,
32 int key_modifier, float modifier_scale); 38 int key_modifier, float modifier_scale);
33 39
40namespace Polling {
41
42enum class DeviceType { Button, Analog };
43
44/**
45 * A class that can be used to get inputs from an input device like controllers without having to
46 * poll the device's status yourself
47 */
48class DevicePoller {
49public:
50 virtual ~DevicePoller() = default;
51 /// Setup and start polling for inputs, should be called before GetNextInput
52 virtual void Start() = 0;
53 /// Stop polling
54 virtual void Stop() = 0;
55 /**
56 * Every call to this function returns the next input recorded since calling Start
57 * @return A ParamPackage of the recorded input, which can be used to create an InputDevice.
58 * If there has been no input, the package is empty
59 */
60 virtual Common::ParamPackage GetNextInput() = 0;
61};
62
63// Get all DevicePoller from all backends for a specific device type
64std::vector<std::unique_ptr<DevicePoller>> GetPollers(DeviceType type);
65} // namespace Polling
34} // namespace InputCommon 66} // namespace InputCommon
diff --git a/src/input_common/sdl/sdl.cpp b/src/input_common/sdl/sdl.cpp
index d404afa89..88b557c5d 100644
--- a/src/input_common/sdl/sdl.cpp
+++ b/src/input_common/sdl/sdl.cpp
@@ -3,13 +3,15 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <cmath> 5#include <cmath>
6#include <memory>
7#include <string> 6#include <string>
8#include <tuple> 7#include <tuple>
9#include <unordered_map> 8#include <unordered_map>
9#include <utility>
10#include <SDL.h> 10#include <SDL.h>
11#include "common/logging/log.h" 11#include "common/logging/log.h"
12#include "common/math_util.h" 12#include "common/math_util.h"
13#include "common/param_package.h"
14#include "input_common/main.h"
13#include "input_common/sdl/sdl.h" 15#include "input_common/sdl/sdl.h"
14 16
15namespace InputCommon { 17namespace InputCommon {
@@ -69,6 +71,10 @@ public:
69 return (SDL_JoystickGetHat(joystick.get(), hat) & direction) != 0; 71 return (SDL_JoystickGetHat(joystick.get(), hat) & direction) != 0;
70 } 72 }
71 73
74 SDL_JoystickID GetJoystickID() const {
75 return SDL_JoystickInstanceID(joystick.get());
76 }
77
72private: 78private:
73 std::unique_ptr<SDL_Joystick, decltype(&SDL_JoystickClose)> joystick; 79 std::unique_ptr<SDL_Joystick, decltype(&SDL_JoystickClose)> joystick;
74}; 80};
@@ -247,5 +253,180 @@ void Shutdown() {
247 } 253 }
248} 254}
249 255
256/**
257 * This function converts a joystick ID used in SDL events to the device index. This is necessary
258 * because Citra opens joysticks using their indices, not their IDs.
259 */
260static int JoystickIDToDeviceIndex(SDL_JoystickID id) {
261 int num_joysticks = SDL_NumJoysticks();
262 for (int i = 0; i < num_joysticks; i++) {
263 auto joystick = GetJoystick(i);
264 if (joystick->GetJoystickID() == id) {
265 return i;
266 }
267 }
268 return -1;
269}
270
271Common::ParamPackage SDLEventToButtonParamPackage(const SDL_Event& event) {
272 Common::ParamPackage params({{"engine", "sdl"}});
273 switch (event.type) {
274 case SDL_JOYAXISMOTION:
275 params.Set("joystick", JoystickIDToDeviceIndex(event.jaxis.which));
276 params.Set("axis", event.jaxis.axis);
277 if (event.jaxis.value > 0) {
278 params.Set("direction", "+");
279 params.Set("threshold", "0.5");
280 } else {
281 params.Set("direction", "-");
282 params.Set("threshold", "-0.5");
283 }
284 break;
285 case SDL_JOYBUTTONUP:
286 params.Set("joystick", JoystickIDToDeviceIndex(event.jbutton.which));
287 params.Set("button", event.jbutton.button);
288 break;
289 case SDL_JOYHATMOTION:
290 params.Set("joystick", JoystickIDToDeviceIndex(event.jhat.which));
291 params.Set("hat", event.jhat.hat);
292 switch (event.jhat.value) {
293 case SDL_HAT_UP:
294 params.Set("direction", "up");
295 break;
296 case SDL_HAT_DOWN:
297 params.Set("direction", "down");
298 break;
299 case SDL_HAT_LEFT:
300 params.Set("direction", "left");
301 break;
302 case SDL_HAT_RIGHT:
303 params.Set("direction", "right");
304 break;
305 default:
306 return {};
307 }
308 break;
309 }
310 return params;
311}
312
313namespace Polling {
314
315class SDLPoller : public InputCommon::Polling::DevicePoller {
316public:
317 SDLPoller() = default;
318
319 ~SDLPoller() = default;
320
321 void Start() override {
322 // SDL joysticks must be opened, otherwise they don't generate events
323 SDL_JoystickUpdate();
324 int num_joysticks = SDL_NumJoysticks();
325 for (int i = 0; i < num_joysticks; i++) {
326 joysticks_opened.emplace_back(GetJoystick(i));
327 }
328 // Empty event queue to get rid of old events. citra-qt doesn't use the queue
329 SDL_Event dummy;
330 while (SDL_PollEvent(&dummy)) {
331 }
332 }
333
334 void Stop() override {
335 joysticks_opened.clear();
336 }
337
338private:
339 std::vector<std::shared_ptr<SDLJoystick>> joysticks_opened;
340};
341
342class SDLButtonPoller final : public SDLPoller {
343public:
344 SDLButtonPoller() = default;
345
346 ~SDLButtonPoller() = default;
347
348 Common::ParamPackage GetNextInput() override {
349 SDL_Event event;
350 while (SDL_PollEvent(&event)) {
351 switch (event.type) {
352 case SDL_JOYAXISMOTION:
353 if (std::abs(event.jaxis.value / 32767.0) < 0.5) {
354 break;
355 }
356 case SDL_JOYBUTTONUP:
357 case SDL_JOYHATMOTION:
358 return SDLEventToButtonParamPackage(event);
359 }
360 }
361 return {};
362 }
363};
364
365class SDLAnalogPoller final : public SDLPoller {
366public:
367 SDLAnalogPoller() = default;
368
369 ~SDLAnalogPoller() = default;
370
371 void Start() override {
372 SDLPoller::Start();
373
374 // Reset stored axes
375 analog_xaxis = -1;
376 analog_yaxis = -1;
377 analog_axes_joystick = -1;
378 }
379
380 Common::ParamPackage GetNextInput() override {
381 SDL_Event event;
382 while (SDL_PollEvent(&event)) {
383 if (event.type != SDL_JOYAXISMOTION || std::abs(event.jaxis.value / 32767.0) < 0.5) {
384 continue;
385 }
386 // An analog device needs two axes, so we need to store the axis for later and wait for
387 // a second SDL event. The axes also must be from the same joystick.
388 int axis = event.jaxis.axis;
389 if (analog_xaxis == -1) {
390 analog_xaxis = axis;
391 analog_axes_joystick = event.jaxis.which;
392 } else if (analog_yaxis == -1 && analog_xaxis != axis &&
393 analog_axes_joystick == event.jaxis.which) {
394 analog_yaxis = axis;
395 }
396 }
397 Common::ParamPackage params;
398 if (analog_xaxis != -1 && analog_yaxis != -1) {
399 params.Set("engine", "sdl");
400 params.Set("joystick", JoystickIDToDeviceIndex(analog_axes_joystick));
401 params.Set("axis_x", analog_xaxis);
402 params.Set("axis_y", analog_yaxis);
403 analog_xaxis = -1;
404 analog_yaxis = -1;
405 analog_axes_joystick = -1;
406 return params;
407 }
408 return params;
409 }
410
411private:
412 int analog_xaxis = -1;
413 int analog_yaxis = -1;
414 SDL_JoystickID analog_axes_joystick = -1;
415};
416
417std::vector<std::unique_ptr<InputCommon::Polling::DevicePoller>> GetPollers(
418 InputCommon::Polling::DeviceType type) {
419 std::vector<std::unique_ptr<InputCommon::Polling::DevicePoller>> pollers;
420 switch (type) {
421 case InputCommon::Polling::DeviceType::Analog:
422 pollers.push_back(std::make_unique<SDLAnalogPoller>());
423 break;
424 case InputCommon::Polling::DeviceType::Button:
425 pollers.push_back(std::make_unique<SDLButtonPoller>());
426 break;
427 }
428 return std::move(pollers);
429}
430} // namespace Polling
250} // namespace SDL 431} // namespace SDL
251} // namespace InputCommon 432} // namespace InputCommon
diff --git a/src/input_common/sdl/sdl.h b/src/input_common/sdl/sdl.h
index 3e72debcc..7934099d4 100644
--- a/src/input_common/sdl/sdl.h
+++ b/src/input_common/sdl/sdl.h
@@ -4,8 +4,21 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <memory>
8#include <vector>
7#include "core/frontend/input.h" 9#include "core/frontend/input.h"
8 10
11union SDL_Event;
12namespace Common {
13class ParamPackage;
14}
15namespace InputCommon {
16namespace Polling {
17class DevicePoller;
18enum class DeviceType;
19} // namespace Polling
20} // namespace InputCommon
21
9namespace InputCommon { 22namespace InputCommon {
10namespace SDL { 23namespace SDL {
11 24
@@ -15,5 +28,15 @@ void Init();
15/// Unresisters SDL device factories and shut them down. 28/// Unresisters SDL device factories and shut them down.
16void Shutdown(); 29void Shutdown();
17 30
31/// Creates a ParamPackage from an SDL_Event that can directly be used to create a ButtonDevice
32Common::ParamPackage SDLEventToButtonParamPackage(const SDL_Event& event);
33
34namespace Polling {
35
36/// Get all DevicePoller that use the SDL backend for a specific device type
37std::vector<std::unique_ptr<InputCommon::Polling::DevicePoller>> GetPollers(
38 InputCommon::Polling::DeviceType type);
39
40} // namespace Polling
18} // namespace SDL 41} // namespace SDL
19} // namespace InputCommon 42} // namespace InputCommon
diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp
index a1e0cf575..843ac6ad7 100644
--- a/src/yuzu/bootmanager.cpp
+++ b/src/yuzu/bootmanager.cpp
@@ -40,6 +40,7 @@ void EmuThread::run() {
40 40
41 Core::System::ResultStatus result = Core::System::GetInstance().RunLoop(); 41 Core::System::ResultStatus result = Core::System::GetInstance().RunLoop();
42 if (result != Core::System::ResultStatus::Success) { 42 if (result != Core::System::ResultStatus::Success) {
43 this->SetRunning(false);
43 emit ErrorThrown(result, Core::System::GetInstance().GetStatusDetails()); 44 emit ErrorThrown(result, Core::System::GetInstance().GetStatusDetails());
44 } 45 }
45 46
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index ab6ba0ec9..923a7c53f 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -137,6 +137,7 @@ void Config::ReadValues() {
137 qt_config->endGroup(); 137 qt_config->endGroup();
138 138
139 UISettings::values.single_window_mode = qt_config->value("singleWindowMode", true).toBool(); 139 UISettings::values.single_window_mode = qt_config->value("singleWindowMode", true).toBool();
140 UISettings::values.fullscreen = qt_config->value("fullscreen", false).toBool();
140 UISettings::values.display_titlebar = qt_config->value("displayTitleBars", true).toBool(); 141 UISettings::values.display_titlebar = qt_config->value("displayTitleBars", true).toBool();
141 UISettings::values.show_filter_bar = qt_config->value("showFilterBar", true).toBool(); 142 UISettings::values.show_filter_bar = qt_config->value("showFilterBar", true).toBool();
142 UISettings::values.show_status_bar = qt_config->value("showStatusBar", true).toBool(); 143 UISettings::values.show_status_bar = qt_config->value("showStatusBar", true).toBool();
@@ -216,6 +217,7 @@ void Config::SaveValues() {
216 qt_config->endGroup(); 217 qt_config->endGroup();
217 218
218 qt_config->setValue("singleWindowMode", UISettings::values.single_window_mode); 219 qt_config->setValue("singleWindowMode", UISettings::values.single_window_mode);
220 qt_config->setValue("fullscreen", UISettings::values.fullscreen);
219 qt_config->setValue("displayTitleBars", UISettings::values.display_titlebar); 221 qt_config->setValue("displayTitleBars", UISettings::values.display_titlebar);
220 qt_config->setValue("showFilterBar", UISettings::values.show_filter_bar); 222 qt_config->setValue("showFilterBar", UISettings::values.show_filter_bar);
221 qt_config->setValue("showStatusBar", UISettings::values.show_status_bar); 223 qt_config->setValue("showStatusBar", UISettings::values.show_status_bar);
diff --git a/src/yuzu/configuration/configure_input.cpp b/src/yuzu/configuration/configure_input.cpp
index d92a1fed9..cd7520f29 100644
--- a/src/yuzu/configuration/configure_input.cpp
+++ b/src/yuzu/configuration/configure_input.cpp
@@ -5,13 +5,13 @@
5#include <algorithm> 5#include <algorithm>
6#include <memory> 6#include <memory>
7#include <utility> 7#include <utility>
8#include <QMessageBox>
8#include <QTimer> 9#include <QTimer>
9#include "common/param_package.h" 10#include "common/param_package.h"
10#include "input_common/main.h" 11#include "input_common/main.h"
11#include "yuzu/configuration/config.h" 12#include "yuzu/configuration/config.h"
12#include "yuzu/configuration/configure_input.h" 13#include "yuzu/configuration/configure_input.h"
13 14
14
15const std::array<std::string, ConfigureInput::ANALOG_SUB_BUTTONS_NUM> 15const std::array<std::string, ConfigureInput::ANALOG_SUB_BUTTONS_NUM>
16 ConfigureInput::analog_sub_buttons{{ 16 ConfigureInput::analog_sub_buttons{{
17 "up", "down", "left", "right", "modifier", 17 "up", "down", "left", "right", "modifier",
@@ -32,23 +32,19 @@ static QString getKeyName(int key_code) {
32 } 32 }
33} 33}
34 34
35static void SetButtonKey(int key, Common::ParamPackage& button_param) { 35static void SetAnalogButton(const Common::ParamPackage& input_param,
36 button_param = Common::ParamPackage{InputCommon::GenerateKeyboardParam(key)}; 36 Common::ParamPackage& analog_param, const std::string& button_name) {
37}
38
39static void SetAnalogKey(int key, Common::ParamPackage& analog_param,
40 const std::string& button_name) {
41 if (analog_param.Get("engine", "") != "analog_from_button") { 37 if (analog_param.Get("engine", "") != "analog_from_button") {
42 analog_param = { 38 analog_param = {
43 {"engine", "analog_from_button"}, {"modifier_scale", "0.5"}, 39 {"engine", "analog_from_button"}, {"modifier_scale", "0.5"},
44 }; 40 };
45 } 41 }
46 analog_param.Set(button_name, InputCommon::GenerateKeyboardParam(key)); 42 analog_param.Set(button_name, input_param.Serialize());
47} 43}
48 44
49ConfigureInput::ConfigureInput(QWidget* parent) 45ConfigureInput::ConfigureInput(QWidget* parent)
50 : QWidget(parent), ui(std::make_unique<Ui::ConfigureInput>()), 46 : QWidget(parent), ui(std::make_unique<Ui::ConfigureInput>()),
51 timer(std::make_unique<QTimer>()) { 47 timeout_timer(std::make_unique<QTimer>()), poll_timer(std::make_unique<QTimer>()) {
52 48
53 ui->setupUi(this); 49 ui->setupUi(this);
54 setFocusPolicy(Qt::ClickFocus); 50 setFocusPolicy(Qt::ClickFocus);
@@ -63,7 +59,7 @@ ConfigureInput::ConfigureInput(QWidget* parent)
63 ui->buttonSL, ui->buttonSR, ui->buttonHome, ui->buttonScreenshot, 59 ui->buttonSL, ui->buttonSR, ui->buttonHome, ui->buttonScreenshot,
64 }; 60 };
65 61
66 analog_map = {{ 62 analog_map_buttons = {{
67 { 63 {
68 ui->buttonLStickUp, ui->buttonLStickDown, ui->buttonLStickLeft, ui->buttonLStickRight, 64 ui->buttonLStickUp, ui->buttonLStickDown, ui->buttonLStickLeft, ui->buttonLStickRight,
69 ui->buttonLStickMod, 65 ui->buttonLStickMod,
@@ -74,35 +70,57 @@ ConfigureInput::ConfigureInput(QWidget* parent)
74 }, 70 },
75 }}; 71 }};
76 72
73 analog_map_stick = {ui->buttonLStickAnalog, ui->buttonRStickAnalog};
74
77 for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; button_id++) { 75 for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; button_id++) {
78 if (button_map[button_id]) 76 if (button_map[button_id])
79 connect(button_map[button_id], &QPushButton::released, [=]() { 77 connect(button_map[button_id], &QPushButton::released, [=]() {
80 handleClick(button_map[button_id], 78 handleClick(
81 [=](int key) { SetButtonKey(key, buttons_param[button_id]); }); 79 button_map[button_id],
80 [=](const Common::ParamPackage& params) { buttons_param[button_id] = params; },
81 InputCommon::Polling::DeviceType::Button);
82 }); 82 });
83 } 83 }
84 84
85 for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; analog_id++) { 85 for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; analog_id++) {
86 for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; sub_button_id++) { 86 for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; sub_button_id++) {
87 if (analog_map[analog_id][sub_button_id] != nullptr) { 87 if (analog_map_buttons[analog_id][sub_button_id] != nullptr) {
88 connect(analog_map[analog_id][sub_button_id], &QPushButton::released, [=]() { 88 connect(analog_map_buttons[analog_id][sub_button_id], &QPushButton::released,
89 handleClick(analog_map[analog_id][sub_button_id], [=](int key) { 89 [=]() {
90 SetAnalogKey(key, analogs_param[analog_id], 90 handleClick(analog_map_buttons[analog_id][sub_button_id],
91 analog_sub_buttons[sub_button_id]); 91 [=](const Common::ParamPackage& params) {
92 }); 92 SetAnalogButton(params, analogs_param[analog_id],
93 }); 93 analog_sub_buttons[sub_button_id]);
94 },
95 InputCommon::Polling::DeviceType::Button);
96 });
94 } 97 }
95 } 98 }
99 connect(analog_map_stick[analog_id], &QPushButton::released, [=]() {
100 QMessageBox::information(
101 this, "Information",
102 "After pressing OK, first move your joystick horizontally, and then vertically.");
103 handleClick(
104 analog_map_stick[analog_id],
105 [=](const Common::ParamPackage& params) { analogs_param[analog_id] = params; },
106 InputCommon::Polling::DeviceType::Analog);
107 });
96 } 108 }
97 109
98 connect(ui->buttonRestoreDefaults, &QPushButton::released, [this]() { restoreDefaults(); }); 110 connect(ui->buttonRestoreDefaults, &QPushButton::released, [this]() { restoreDefaults(); });
99 111
100 timer->setSingleShot(true); 112 timeout_timer->setSingleShot(true);
101 connect(timer.get(), &QTimer::timeout, [this]() { 113 connect(timeout_timer.get(), &QTimer::timeout, [this]() { setPollingResult({}, true); });
102 releaseKeyboard(); 114
103 releaseMouse(); 115 connect(poll_timer.get(), &QTimer::timeout, [this]() {
104 key_setter = boost::none; 116 Common::ParamPackage params;
105 updateButtonLabels(); 117 for (auto& poller : device_pollers) {
118 params = poller->GetNextInput();
119 if (params.Has("engine")) {
120 setPollingResult(params, false);
121 return;
122 }
123 }
106 }); 124 });
107 125
108 this->loadConfiguration(); 126 this->loadConfiguration();
@@ -132,13 +150,15 @@ void ConfigureInput::loadConfiguration() {
132 150
133void ConfigureInput::restoreDefaults() { 151void ConfigureInput::restoreDefaults() {
134 for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; button_id++) { 152 for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; button_id++) {
135 SetButtonKey(Config::default_buttons[button_id], buttons_param[button_id]); 153 buttons_param[button_id] = Common::ParamPackage{
154 InputCommon::GenerateKeyboardParam(Config::default_buttons[button_id])};
136 } 155 }
137 156
138 for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; analog_id++) { 157 for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; analog_id++) {
139 for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; sub_button_id++) { 158 for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; sub_button_id++) {
140 SetAnalogKey(Config::default_analogs[analog_id][sub_button_id], 159 Common::ParamPackage params{InputCommon::GenerateKeyboardParam(
141 analogs_param[analog_id], analog_sub_buttons[sub_button_id]); 160 Config::default_analogs[analog_id][sub_button_id])};
161 SetAnalogButton(params, analogs_param[analog_id], analog_sub_buttons[sub_button_id]);
142 } 162 }
143 } 163 }
144 updateButtonLabels(); 164 updateButtonLabels();
@@ -149,7 +169,9 @@ void ConfigureInput::updateButtonLabels() {
149 QString non_keyboard(tr("[non-keyboard]")); 169 QString non_keyboard(tr("[non-keyboard]"));
150 170
151 auto KeyToText = [&non_keyboard](const Common::ParamPackage& param) { 171 auto KeyToText = [&non_keyboard](const Common::ParamPackage& param) {
152 if (param.Get("engine", "") != "keyboard") { 172 if (!param.Has("engine")) {
173 return QString("[not set]");
174 } else if (param.Get("engine", "") != "keyboard") {
153 return non_keyboard; 175 return non_keyboard;
154 } else { 176 } else {
155 return getKeyName(param.Get("code", 0)); 177 return getKeyName(param.Get("code", 0));
@@ -162,7 +184,7 @@ void ConfigureInput::updateButtonLabels() {
162 184
163 for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; analog_id++) { 185 for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; analog_id++) {
164 if (analogs_param[analog_id].Get("engine", "") != "analog_from_button") { 186 if (analogs_param[analog_id].Get("engine", "") != "analog_from_button") {
165 for (QPushButton* button : analog_map[analog_id]) { 187 for (QPushButton* button : analog_map_buttons[analog_id]) {
166 if (button) 188 if (button)
167 button->setText(non_keyboard); 189 button->setText(non_keyboard);
168 } 190 }
@@ -170,35 +192,66 @@ void ConfigureInput::updateButtonLabels() {
170 for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; sub_button_id++) { 192 for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; sub_button_id++) {
171 Common::ParamPackage param( 193 Common::ParamPackage param(
172 analogs_param[analog_id].Get(analog_sub_buttons[sub_button_id], "")); 194 analogs_param[analog_id].Get(analog_sub_buttons[sub_button_id], ""));
173 if (analog_map[analog_id][sub_button_id]) 195 if (analog_map_buttons[analog_id][sub_button_id])
174 analog_map[analog_id][sub_button_id]->setText(KeyToText(param)); 196 analog_map_buttons[analog_id][sub_button_id]->setText(KeyToText(param));
175 } 197 }
176 } 198 }
199 analog_map_stick[analog_id]->setText("Set Analog Stick");
177 } 200 }
178} 201}
179 202
180void ConfigureInput::handleClick(QPushButton* button, std::function<void(int)> new_key_setter) { 203void ConfigureInput::handleClick(QPushButton* button,
204 std::function<void(const Common::ParamPackage&)> new_input_setter,
205 InputCommon::Polling::DeviceType type) {
181 button->setText(tr("[press key]")); 206 button->setText(tr("[press key]"));
182 button->setFocus(); 207 button->setFocus();
183 208
184 key_setter = new_key_setter; 209 input_setter = new_input_setter;
210
211 device_pollers = InputCommon::Polling::GetPollers(type);
212
213 // Keyboard keys can only be used as button devices
214 want_keyboard_keys = type == InputCommon::Polling::DeviceType::Button;
215
216 for (auto& poller : device_pollers) {
217 poller->Start();
218 }
185 219
186 grabKeyboard(); 220 grabKeyboard();
187 grabMouse(); 221 grabMouse();
188 timer->start(5000); // Cancel after 5 seconds 222 timeout_timer->start(5000); // Cancel after 5 seconds
223 poll_timer->start(200); // Check for new inputs every 200ms
189} 224}
190 225
191void ConfigureInput::keyPressEvent(QKeyEvent* event) { 226void ConfigureInput::setPollingResult(const Common::ParamPackage& params, bool abort) {
192 releaseKeyboard(); 227 releaseKeyboard();
193 releaseMouse(); 228 releaseMouse();
229 timeout_timer->stop();
230 poll_timer->stop();
231 for (auto& poller : device_pollers) {
232 poller->Stop();
233 }
194 234
195 if (!key_setter || !event) 235 if (!abort) {
196 return; 236 (*input_setter)(params);
197 237 }
198 if (event->key() != Qt::Key_Escape)
199 (*key_setter)(event->key());
200 238
201 updateButtonLabels(); 239 updateButtonLabels();
202 key_setter = boost::none; 240 input_setter = boost::none;
203 timer->stop(); 241}
242
243void ConfigureInput::keyPressEvent(QKeyEvent* event) {
244 if (!input_setter || !event)
245 return;
246
247 if (event->key() != Qt::Key_Escape) {
248 if (want_keyboard_keys) {
249 setPollingResult(Common::ParamPackage{InputCommon::GenerateKeyboardParam(event->key())},
250 false);
251 } else {
252 // Escape key wasn't pressed and we don't want any keyboard keys, so don't stop polling
253 return;
254 }
255 }
256 setPollingResult({}, true);
204} 257}
diff --git a/src/yuzu/configuration/configure_input.h b/src/yuzu/configuration/configure_input.h
index c950fbcb4..a0bef86d5 100644
--- a/src/yuzu/configuration/configure_input.h
+++ b/src/yuzu/configuration/configure_input.h
@@ -8,11 +8,13 @@
8#include <functional> 8#include <functional>
9#include <memory> 9#include <memory>
10#include <string> 10#include <string>
11#include <unordered_map>
11#include <QKeyEvent> 12#include <QKeyEvent>
12#include <QWidget> 13#include <QWidget>
13#include <boost/optional.hpp> 14#include <boost/optional.hpp>
14#include "common/param_package.h" 15#include "common/param_package.h"
15#include "core/settings.h" 16#include "core/settings.h"
17#include "input_common/main.h"
16#include "ui_configure_input.h" 18#include "ui_configure_input.h"
17 19
18class QPushButton; 20class QPushButton;
@@ -35,10 +37,11 @@ public:
35private: 37private:
36 std::unique_ptr<Ui::ConfigureInput> ui; 38 std::unique_ptr<Ui::ConfigureInput> ui;
37 39
38 std::unique_ptr<QTimer> timer; 40 std::unique_ptr<QTimer> timeout_timer;
41 std::unique_ptr<QTimer> poll_timer;
39 42
40 /// This will be the the setting function when an input is awaiting configuration. 43 /// This will be the the setting function when an input is awaiting configuration.
41 boost::optional<std::function<void(int)>> key_setter; 44 boost::optional<std::function<void(const Common::ParamPackage&)>> input_setter;
42 45
43 std::array<Common::ParamPackage, Settings::NativeButton::NumButtons> buttons_param; 46 std::array<Common::ParamPackage, Settings::NativeButton::NumButtons> buttons_param;
44 std::array<Common::ParamPackage, Settings::NativeAnalog::NumAnalogs> analogs_param; 47 std::array<Common::ParamPackage, Settings::NativeAnalog::NumAnalogs> analogs_param;
@@ -48,13 +51,23 @@ private:
48 /// Each button input is represented by a QPushButton. 51 /// Each button input is represented by a QPushButton.
49 std::array<QPushButton*, Settings::NativeButton::NumButtons> button_map; 52 std::array<QPushButton*, Settings::NativeButton::NumButtons> button_map;
50 53
51 /// Each analog input is represented by five QPushButtons which represents up, down, left, right 54 /// A group of five QPushButtons represent one analog input. The buttons each represent up,
52 /// and modifier 55 /// down, left, right, and modifier, respectively.
53 std::array<std::array<QPushButton*, ANALOG_SUB_BUTTONS_NUM>, Settings::NativeAnalog::NumAnalogs> 56 std::array<std::array<QPushButton*, ANALOG_SUB_BUTTONS_NUM>, Settings::NativeAnalog::NumAnalogs>
54 analog_map; 57 analog_map_buttons;
58
59 /// Analog inputs are also represented each with a single button, used to configure with an
60 /// actual analog stick
61 std::array<QPushButton*, Settings::NativeAnalog::NumAnalogs> analog_map_stick;
55 62
56 static const std::array<std::string, ANALOG_SUB_BUTTONS_NUM> analog_sub_buttons; 63 static const std::array<std::string, ANALOG_SUB_BUTTONS_NUM> analog_sub_buttons;
57 64
65 std::vector<std::unique_ptr<InputCommon::Polling::DevicePoller>> device_pollers;
66
67 /// A flag to indicate if keyboard keys are okay when configuring an input. If this is false,
68 /// keyboard events are ignored.
69 bool want_keyboard_keys = false;
70
58 /// Load configuration settings. 71 /// Load configuration settings.
59 void loadConfiguration(); 72 void loadConfiguration();
60 /// Restore all buttons to their default values. 73 /// Restore all buttons to their default values.
@@ -63,7 +76,13 @@ private:
63 void updateButtonLabels(); 76 void updateButtonLabels();
64 77
65 /// Called when the button was pressed. 78 /// Called when the button was pressed.
66 void handleClick(QPushButton* button, std::function<void(int)> new_key_setter); 79 void handleClick(QPushButton* button,
80 std::function<void(const Common::ParamPackage&)> new_input_setter,
81 InputCommon::Polling::DeviceType type);
82
83 /// Finish polling and configure input using the input_setter
84 void setPollingResult(const Common::ParamPackage& params, bool abort);
85
67 /// Handle key press events. 86 /// Handle key press events.
68 void keyPressEvent(QKeyEvent* event) override; 87 void keyPressEvent(QKeyEvent* event) override;
69}; 88};
diff --git a/src/yuzu/configuration/configure_input.ui b/src/yuzu/configuration/configure_input.ui
index 5143c9d72..c162ca02c 100644
--- a/src/yuzu/configuration/configure_input.ui
+++ b/src/yuzu/configuration/configure_input.ui
@@ -7,7 +7,7 @@
7 <x>0</x> 7 <x>0</x>
8 <y>0</y> 8 <y>0</y>
9 <width>343</width> 9 <width>343</width>
10 <height>665</height> 10 <height>677</height>
11 </rect> 11 </rect>
12 </property> 12 </property>
13 <property name="windowTitle"> 13 <property name="windowTitle">
@@ -16,10 +16,10 @@
16 <layout class="QVBoxLayout" name="verticalLayout_5"> 16 <layout class="QVBoxLayout" name="verticalLayout_5">
17 <item> 17 <item>
18 <layout class="QGridLayout" name="gridLayout_7"> 18 <layout class="QGridLayout" name="gridLayout_7">
19 <item row="0" column="0"> 19 <item row="3" column="1">
20 <widget class="QGroupBox" name="faceButtons"> 20 <widget class="QGroupBox" name="faceButtons_6">
21 <property name="title"> 21 <property name="title">
22 <string>Face Buttons</string> 22 <string>Misc.</string>
23 </property> 23 </property>
24 <property name="flat"> 24 <property name="flat">
25 <bool>false</bool> 25 <bool>false</bool>
@@ -27,18 +27,18 @@
27 <property name="checkable"> 27 <property name="checkable">
28 <bool>false</bool> 28 <bool>false</bool>
29 </property> 29 </property>
30 <layout class="QGridLayout" name="gridLayout"> 30 <layout class="QGridLayout" name="gridLayout_6">
31 <item row="0" column="0"> 31 <item row="0" column="0">
32 <layout class="QVBoxLayout" name="verticalLayout"> 32 <layout class="QVBoxLayout" name="verticalLayout_25">
33 <item> 33 <item>
34 <widget class="QLabel" name="label"> 34 <widget class="QLabel" name="label_29">
35 <property name="text"> 35 <property name="text">
36 <string>A:</string> 36 <string>Plus:</string>
37 </property> 37 </property>
38 </widget> 38 </widget>
39 </item> 39 </item>
40 <item> 40 <item>
41 <widget class="QPushButton" name="buttonA"> 41 <widget class="QPushButton" name="buttonPlus">
42 <property name="text"> 42 <property name="text">
43 <string/> 43 <string/>
44 </property> 44 </property>
@@ -47,16 +47,16 @@
47 </layout> 47 </layout>
48 </item> 48 </item>
49 <item row="0" column="1"> 49 <item row="0" column="1">
50 <layout class="QVBoxLayout" name="verticalLayout_2"> 50 <layout class="QVBoxLayout" name="verticalLayout_26">
51 <item> 51 <item>
52 <widget class="QLabel" name="label_2"> 52 <widget class="QLabel" name="label_30">
53 <property name="text"> 53 <property name="text">
54 <string>B:</string> 54 <string>Minus:</string>
55 </property> 55 </property>
56 </widget> 56 </widget>
57 </item> 57 </item>
58 <item> 58 <item>
59 <widget class="QPushButton" name="buttonB"> 59 <widget class="QPushButton" name="buttonMinus">
60 <property name="text"> 60 <property name="text">
61 <string/> 61 <string/>
62 </property> 62 </property>
@@ -65,16 +65,16 @@
65 </layout> 65 </layout>
66 </item> 66 </item>
67 <item row="1" column="0"> 67 <item row="1" column="0">
68 <layout class="QVBoxLayout" name="verticalLayout_3"> 68 <layout class="QVBoxLayout" name="verticalLayout_27">
69 <item> 69 <item>
70 <widget class="QLabel" name="label_3"> 70 <widget class="QLabel" name="label_31">
71 <property name="text"> 71 <property name="text">
72 <string>X:</string> 72 <string>Home:</string>
73 </property> 73 </property>
74 </widget> 74 </widget>
75 </item> 75 </item>
76 <item> 76 <item>
77 <widget class="QPushButton" name="buttonX"> 77 <widget class="QPushButton" name="buttonHome">
78 <property name="text"> 78 <property name="text">
79 <string/> 79 <string/>
80 </property> 80 </property>
@@ -83,16 +83,17 @@
83 </layout> 83 </layout>
84 </item> 84 </item>
85 <item row="1" column="1"> 85 <item row="1" column="1">
86 <layout class="QVBoxLayout" name="verticalLayout_4"> 86 <layout class="QVBoxLayout" name="verticalLayout_28">
87 <item> 87 <item>
88 <widget class="QLabel" name="label_4"> 88 <widget class="QLabel" name="label_11">
89 <property name="text"> 89 <property name="text">
90 <string>Y:</string> 90 <string>Screen
91Capture:</string>
91 </property> 92 </property>
92 </widget> 93 </widget>
93 </item> 94 </item>
94 <item> 95 <item>
95 <widget class="QPushButton" name="buttonY"> 96 <widget class="QPushButton" name="buttonScreenshot">
96 <property name="text"> 97 <property name="text">
97 <string/> 98 <string/>
98 </property> 99 </property>
@@ -100,13 +101,26 @@
100 </item> 101 </item>
101 </layout> 102 </layout>
102 </item> 103 </item>
104 <item row="2" column="1">
105 <spacer name="verticalSpacer">
106 <property name="orientation">
107 <enum>Qt::Vertical</enum>
108 </property>
109 <property name="sizeHint" stdset="0">
110 <size>
111 <width>20</width>
112 <height>40</height>
113 </size>
114 </property>
115 </spacer>
116 </item>
103 </layout> 117 </layout>
104 </widget> 118 </widget>
105 </item> 119 </item>
106 <item row="0" column="1"> 120 <item row="0" column="0">
107 <widget class="QGroupBox" name="faceButtons_2"> 121 <widget class="QGroupBox" name="faceButtons">
108 <property name="title"> 122 <property name="title">
109 <string>Directional Pad</string> 123 <string>Face Buttons</string>
110 </property> 124 </property>
111 <property name="flat"> 125 <property name="flat">
112 <bool>false</bool> 126 <bool>false</bool>
@@ -114,18 +128,18 @@
114 <property name="checkable"> 128 <property name="checkable">
115 <bool>false</bool> 129 <bool>false</bool>
116 </property> 130 </property>
117 <layout class="QGridLayout" name="gridLayout_2"> 131 <layout class="QGridLayout" name="gridLayout">
118 <item row="1" column="0"> 132 <item row="0" column="0">
119 <layout class="QVBoxLayout" name="verticalLayout_12"> 133 <layout class="QVBoxLayout" name="verticalLayout">
120 <item> 134 <item>
121 <widget class="QLabel" name="label_34"> 135 <widget class="QLabel" name="label">
122 <property name="text"> 136 <property name="text">
123 <string>Up:</string> 137 <string>A:</string>
124 </property> 138 </property>
125 </widget> 139 </widget>
126 </item> 140 </item>
127 <item> 141 <item>
128 <widget class="QPushButton" name="buttonDpadUp"> 142 <widget class="QPushButton" name="buttonA">
129 <property name="text"> 143 <property name="text">
130 <string/> 144 <string/>
131 </property> 145 </property>
@@ -133,17 +147,17 @@
133 </item> 147 </item>
134 </layout> 148 </layout>
135 </item> 149 </item>
136 <item row="1" column="1"> 150 <item row="0" column="1">
137 <layout class="QVBoxLayout" name="verticalLayout_9"> 151 <layout class="QVBoxLayout" name="verticalLayout_2">
138 <item> 152 <item>
139 <widget class="QLabel" name="label_35"> 153 <widget class="QLabel" name="label_2">
140 <property name="text"> 154 <property name="text">
141 <string>Down:</string> 155 <string>B:</string>
142 </property> 156 </property>
143 </widget> 157 </widget>
144 </item> 158 </item>
145 <item> 159 <item>
146 <widget class="QPushButton" name="buttonDpadDown"> 160 <widget class="QPushButton" name="buttonB">
147 <property name="text"> 161 <property name="text">
148 <string/> 162 <string/>
149 </property> 163 </property>
@@ -151,17 +165,17 @@
151 </item> 165 </item>
152 </layout> 166 </layout>
153 </item> 167 </item>
154 <item row="0" column="0"> 168 <item row="1" column="0">
155 <layout class="QVBoxLayout" name="verticalLayout_10"> 169 <layout class="QVBoxLayout" name="verticalLayout_3">
156 <item> 170 <item>
157 <widget class="QLabel" name="label_32"> 171 <widget class="QLabel" name="label_3">
158 <property name="text"> 172 <property name="text">
159 <string>Left:</string> 173 <string>X:</string>
160 </property> 174 </property>
161 </widget> 175 </widget>
162 </item> 176 </item>
163 <item> 177 <item>
164 <widget class="QPushButton" name="buttonDpadLeft"> 178 <widget class="QPushButton" name="buttonX">
165 <property name="text"> 179 <property name="text">
166 <string/> 180 <string/>
167 </property> 181 </property>
@@ -169,17 +183,17 @@
169 </item> 183 </item>
170 </layout> 184 </layout>
171 </item> 185 </item>
172 <item row="0" column="1"> 186 <item row="1" column="1">
173 <layout class="QVBoxLayout" name="verticalLayout_11"> 187 <layout class="QVBoxLayout" name="verticalLayout_4">
174 <item> 188 <item>
175 <widget class="QLabel" name="label_33"> 189 <widget class="QLabel" name="label_4">
176 <property name="text"> 190 <property name="text">
177 <string>Right:</string> 191 <string>Y:</string>
178 </property> 192 </property>
179 </widget> 193 </widget>
180 </item> 194 </item>
181 <item> 195 <item>
182 <widget class="QPushButton" name="buttonDpadRight"> 196 <widget class="QPushButton" name="buttonY">
183 <property name="text"> 197 <property name="text">
184 <string/> 198 <string/>
185 </property> 199 </property>
@@ -190,10 +204,10 @@
190 </layout> 204 </layout>
191 </widget> 205 </widget>
192 </item> 206 </item>
193 <item row="3" column="1"> 207 <item row="0" column="1">
194 <widget class="QGroupBox" name="faceButtons_6"> 208 <widget class="QGroupBox" name="faceButtons_2">
195 <property name="title"> 209 <property name="title">
196 <string>Misc.</string> 210 <string>Directional Pad</string>
197 </property> 211 </property>
198 <property name="flat"> 212 <property name="flat">
199 <bool>false</bool> 213 <bool>false</bool>
@@ -201,18 +215,18 @@
201 <property name="checkable"> 215 <property name="checkable">
202 <bool>false</bool> 216 <bool>false</bool>
203 </property> 217 </property>
204 <layout class="QGridLayout" name="gridLayout_6"> 218 <layout class="QGridLayout" name="gridLayout_2">
205 <item row="0" column="0"> 219 <item row="1" column="0">
206 <layout class="QVBoxLayout" name="verticalLayout_25"> 220 <layout class="QVBoxLayout" name="verticalLayout_12">
207 <item> 221 <item>
208 <widget class="QLabel" name="label_29"> 222 <widget class="QLabel" name="label_34">
209 <property name="text"> 223 <property name="text">
210 <string>Plus:</string> 224 <string>Up:</string>
211 </property> 225 </property>
212 </widget> 226 </widget>
213 </item> 227 </item>
214 <item> 228 <item>
215 <widget class="QPushButton" name="buttonPlus"> 229 <widget class="QPushButton" name="buttonDpadUp">
216 <property name="text"> 230 <property name="text">
217 <string/> 231 <string/>
218 </property> 232 </property>
@@ -220,17 +234,17 @@
220 </item> 234 </item>
221 </layout> 235 </layout>
222 </item> 236 </item>
223 <item row="0" column="1"> 237 <item row="1" column="1">
224 <layout class="QVBoxLayout" name="verticalLayout_26"> 238 <layout class="QVBoxLayout" name="verticalLayout_9">
225 <item> 239 <item>
226 <widget class="QLabel" name="label_30"> 240 <widget class="QLabel" name="label_35">
227 <property name="text"> 241 <property name="text">
228 <string>Minus:</string> 242 <string>Down:</string>
229 </property> 243 </property>
230 </widget> 244 </widget>
231 </item> 245 </item>
232 <item> 246 <item>
233 <widget class="QPushButton" name="buttonMinus"> 247 <widget class="QPushButton" name="buttonDpadDown">
234 <property name="text"> 248 <property name="text">
235 <string/> 249 <string/>
236 </property> 250 </property>
@@ -238,17 +252,17 @@
238 </item> 252 </item>
239 </layout> 253 </layout>
240 </item> 254 </item>
241 <item row="1" column="0"> 255 <item row="0" column="0">
242 <layout class="QVBoxLayout" name="verticalLayout_27"> 256 <layout class="QVBoxLayout" name="verticalLayout_10">
243 <item> 257 <item>
244 <widget class="QLabel" name="label_31"> 258 <widget class="QLabel" name="label_32">
245 <property name="text"> 259 <property name="text">
246 <string>Home:</string> 260 <string>Left:</string>
247 </property> 261 </property>
248 </widget> 262 </widget>
249 </item> 263 </item>
250 <item> 264 <item>
251 <widget class="QPushButton" name="buttonHome"> 265 <widget class="QPushButton" name="buttonDpadLeft">
252 <property name="text"> 266 <property name="text">
253 <string/> 267 <string/>
254 </property> 268 </property>
@@ -256,18 +270,17 @@
256 </item> 270 </item>
257 </layout> 271 </layout>
258 </item> 272 </item>
259 <item row="1" column="1"> 273 <item row="0" column="1">
260 <layout class="QVBoxLayout" name="verticalLayout_28"> 274 <layout class="QVBoxLayout" name="verticalLayout_11">
261 <item> 275 <item>
262 <widget class="QLabel" name="label_11"> 276 <widget class="QLabel" name="label_33">
263 <property name="text"> 277 <property name="text">
264 <string>Screen 278 <string>Right:</string>
265Capture:</string>
266 </property> 279 </property>
267 </widget> 280 </widget>
268 </item> 281 </item>
269 <item> 282 <item>
270 <widget class="QPushButton" name="buttonScreenshot"> 283 <widget class="QPushButton" name="buttonDpadRight">
271 <property name="text"> 284 <property name="text">
272 <string/> 285 <string/>
273 </property> 286 </property>
@@ -275,19 +288,6 @@ Capture:</string>
275 </item> 288 </item>
276 </layout> 289 </layout>
277 </item> 290 </item>
278 <item row="2" column="1">
279 <spacer name="verticalSpacer">
280 <property name="orientation">
281 <enum>Qt::Vertical</enum>
282 </property>
283 <property name="sizeHint" stdset="0">
284 <size>
285 <width>20</width>
286 <height>40</height>
287 </size>
288 </property>
289 </spacer>
290 </item>
291 </layout> 291 </layout>
292 </widget> 292 </widget>
293 </item> 293 </item>
@@ -414,10 +414,13 @@ Capture:</string>
414 </layout> 414 </layout>
415 </widget> 415 </widget>
416 </item> 416 </item>
417 <item row="1" column="0"> 417 <item row="1" column="1">
418 <widget class="QGroupBox" name="faceButtons_4"> 418 <widget class="QGroupBox" name="faceButtons_5">
419 <property name="title"> 419 <property name="title">
420 <string>Left Stick</string> 420 <string>Right Stick</string>
421 </property>
422 <property name="alignment">
423 <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
421 </property> 424 </property>
422 <property name="flat"> 425 <property name="flat">
423 <bool>false</bool> 426 <bool>false</bool>
@@ -425,18 +428,18 @@ Capture:</string>
425 <property name="checkable"> 428 <property name="checkable">
426 <bool>false</bool> 429 <bool>false</bool>
427 </property> 430 </property>
428 <layout class="QGridLayout" name="gridLayout_4"> 431 <layout class="QGridLayout" name="gridLayout_5">
429 <item row="0" column="0"> 432 <item row="1" column="1">
430 <layout class="QVBoxLayout" name="verticalLayout_17"> 433 <layout class="QVBoxLayout" name="verticalLayout_24">
431 <item> 434 <item>
432 <widget class="QLabel" name="label_21"> 435 <widget class="QLabel" name="label_26">
433 <property name="text"> 436 <property name="text">
434 <string>Left:</string> 437 <string>Down:</string>
435 </property> 438 </property>
436 </widget> 439 </widget>
437 </item> 440 </item>
438 <item> 441 <item>
439 <widget class="QPushButton" name="buttonLStickLeft"> 442 <widget class="QPushButton" name="buttonRStickDown">
440 <property name="text"> 443 <property name="text">
441 <string/> 444 <string/>
442 </property> 445 </property>
@@ -445,16 +448,16 @@ Capture:</string>
445 </layout> 448 </layout>
446 </item> 449 </item>
447 <item row="0" column="1"> 450 <item row="0" column="1">
448 <layout class="QVBoxLayout" name="verticalLayout_18"> 451 <layout class="QVBoxLayout" name="verticalLayout_22">
449 <item> 452 <item>
450 <widget class="QLabel" name="label_23"> 453 <widget class="QLabel" name="label_27">
451 <property name="text"> 454 <property name="text">
452 <string>Right:</string> 455 <string>Right:</string>
453 </property> 456 </property>
454 </widget> 457 </widget>
455 </item> 458 </item>
456 <item> 459 <item>
457 <widget class="QPushButton" name="buttonLStickRight"> 460 <widget class="QPushButton" name="buttonRStickRight">
458 <property name="text"> 461 <property name="text">
459 <string/> 462 <string/>
460 </property> 463 </property>
@@ -462,17 +465,24 @@ Capture:</string>
462 </item> 465 </item>
463 </layout> 466 </layout>
464 </item> 467 </item>
468 <item row="3" column="0" colspan="2">
469 <widget class="QPushButton" name="buttonRStickAnalog">
470 <property name="text">
471 <string>Set Analog Stick</string>
472 </property>
473 </widget>
474 </item>
465 <item row="1" column="0"> 475 <item row="1" column="0">
466 <layout class="QVBoxLayout" name="verticalLayout_19"> 476 <layout class="QVBoxLayout" name="verticalLayout_21">
467 <item> 477 <item>
468 <widget class="QLabel" name="label_24"> 478 <widget class="QLabel" name="label_25">
469 <property name="text"> 479 <property name="text">
470 <string>Up:</string> 480 <string>Left:</string>
471 </property> 481 </property>
472 </widget> 482 </widget>
473 </item> 483 </item>
474 <item> 484 <item>
475 <widget class="QPushButton" name="buttonLStickUp"> 485 <widget class="QPushButton" name="buttonRStickLeft">
476 <property name="text"> 486 <property name="text">
477 <string/> 487 <string/>
478 </property> 488 </property>
@@ -480,17 +490,17 @@ Capture:</string>
480 </item> 490 </item>
481 </layout> 491 </layout>
482 </item> 492 </item>
483 <item row="1" column="1"> 493 <item row="0" column="0">
484 <layout class="QVBoxLayout" name="verticalLayout_20"> 494 <layout class="QVBoxLayout" name="verticalLayout_25">
485 <item> 495 <item>
486 <widget class="QLabel" name="label_22"> 496 <widget class="QLabel" name="label_28">
487 <property name="text"> 497 <property name="text">
488 <string>Down:</string> 498 <string>Up:</string>
489 </property> 499 </property>
490 </widget> 500 </widget>
491 </item> 501 </item>
492 <item> 502 <item>
493 <widget class="QPushButton" name="buttonLStickDown"> 503 <widget class="QPushButton" name="buttonRStickUp">
494 <property name="text"> 504 <property name="text">
495 <string/> 505 <string/>
496 </property> 506 </property>
@@ -499,16 +509,16 @@ Capture:</string>
499 </layout> 509 </layout>
500 </item> 510 </item>
501 <item row="2" column="0"> 511 <item row="2" column="0">
502 <layout class="QVBoxLayout" name="verticalLayout_7" stretch="0,0"> 512 <layout class="QVBoxLayout" name="verticalLayout_6">
503 <item> 513 <item>
504 <widget class="QLabel" name="label_6"> 514 <widget class="QLabel" name="label_5">
505 <property name="text"> 515 <property name="text">
506 <string>Pressed:</string> 516 <string>Pressed:</string>
507 </property> 517 </property>
508 </widget> 518 </widget>
509 </item> 519 </item>
510 <item> 520 <item>
511 <widget class="QPushButton" name="buttonLStick"> 521 <widget class="QPushButton" name="buttonRStick">
512 <property name="text"> 522 <property name="text">
513 <string/> 523 <string/>
514 </property> 524 </property>
@@ -517,16 +527,16 @@ Capture:</string>
517 </layout> 527 </layout>
518 </item> 528 </item>
519 <item row="2" column="1"> 529 <item row="2" column="1">
520 <layout class="QVBoxLayout" name="verticalLayout_31"> 530 <layout class="QVBoxLayout" name="verticalLayout_32">
521 <item> 531 <item>
522 <widget class="QLabel" name="label_9"> 532 <widget class="QLabel" name="label_10">
523 <property name="text"> 533 <property name="text">
524 <string>Modifier:</string> 534 <string>Modifier:</string>
525 </property> 535 </property>
526 </widget> 536 </widget>
527 </item> 537 </item>
528 <item> 538 <item>
529 <widget class="QPushButton" name="buttonLStickMod"> 539 <widget class="QPushButton" name="buttonRStickMod">
530 <property name="text"> 540 <property name="text">
531 <string/> 541 <string/>
532 </property> 542 </property>
@@ -537,13 +547,10 @@ Capture:</string>
537 </layout> 547 </layout>
538 </widget> 548 </widget>
539 </item> 549 </item>
540 <item row="1" column="1"> 550 <item row="1" column="0">
541 <widget class="QGroupBox" name="faceButtons_5"> 551 <widget class="QGroupBox" name="faceButtons_4">
542 <property name="title"> 552 <property name="title">
543 <string>Right Stick</string> 553 <string>Left Stick</string>
544 </property>
545 <property name="alignment">
546 <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
547 </property> 554 </property>
548 <property name="flat"> 555 <property name="flat">
549 <bool>false</bool> 556 <bool>false</bool>
@@ -551,18 +558,18 @@ Capture:</string>
551 <property name="checkable"> 558 <property name="checkable">
552 <bool>false</bool> 559 <bool>false</bool>
553 </property> 560 </property>
554 <layout class="QGridLayout" name="gridLayout_5"> 561 <layout class="QGridLayout" name="gridLayout_4">
555 <item row="1" column="1"> 562 <item row="1" column="1">
556 <layout class="QVBoxLayout" name="verticalLayout_24"> 563 <layout class="QVBoxLayout" name="verticalLayout_20">
557 <item> 564 <item>
558 <widget class="QLabel" name="label_26"> 565 <widget class="QLabel" name="label_22">
559 <property name="text"> 566 <property name="text">
560 <string>Down:</string> 567 <string>Down:</string>
561 </property> 568 </property>
562 </widget> 569 </widget>
563 </item> 570 </item>
564 <item> 571 <item>
565 <widget class="QPushButton" name="buttonRStickDown"> 572 <widget class="QPushButton" name="buttonLStickDown">
566 <property name="text"> 573 <property name="text">
567 <string/> 574 <string/>
568 </property> 575 </property>
@@ -570,17 +577,24 @@ Capture:</string>
570 </item> 577 </item>
571 </layout> 578 </layout>
572 </item> 579 </item>
580 <item row="4" column="0" colspan="2">
581 <widget class="QPushButton" name="buttonLStickAnalog">
582 <property name="text">
583 <string>Set Analog Stick</string>
584 </property>
585 </widget>
586 </item>
573 <item row="0" column="1"> 587 <item row="0" column="1">
574 <layout class="QVBoxLayout" name="verticalLayout_22"> 588 <layout class="QVBoxLayout" name="verticalLayout_18">
575 <item> 589 <item>
576 <widget class="QLabel" name="label_27"> 590 <widget class="QLabel" name="label_23">
577 <property name="text"> 591 <property name="text">
578 <string>Right:</string> 592 <string>Right:</string>
579 </property> 593 </property>
580 </widget> 594 </widget>
581 </item> 595 </item>
582 <item> 596 <item>
583 <widget class="QPushButton" name="buttonRStickRight"> 597 <widget class="QPushButton" name="buttonLStickRight">
584 <property name="text"> 598 <property name="text">
585 <string/> 599 <string/>
586 </property> 600 </property>
@@ -588,17 +602,17 @@ Capture:</string>
588 </item> 602 </item>
589 </layout> 603 </layout>
590 </item> 604 </item>
591 <item row="1" column="0"> 605 <item row="0" column="0">
592 <layout class="QVBoxLayout" name="verticalLayout_23"> 606 <layout class="QVBoxLayout" name="verticalLayout_17">
593 <item> 607 <item>
594 <widget class="QLabel" name="label_28"> 608 <widget class="QLabel" name="label_21">
595 <property name="text"> 609 <property name="text">
596 <string>Up:</string> 610 <string>Left:</string>
597 </property> 611 </property>
598 </widget> 612 </widget>
599 </item> 613 </item>
600 <item> 614 <item>
601 <widget class="QPushButton" name="buttonRStickUp"> 615 <widget class="QPushButton" name="buttonLStickLeft">
602 <property name="text"> 616 <property name="text">
603 <string/> 617 <string/>
604 </property> 618 </property>
@@ -606,17 +620,17 @@ Capture:</string>
606 </item> 620 </item>
607 </layout> 621 </layout>
608 </item> 622 </item>
609 <item row="0" column="0"> 623 <item row="1" column="0">
610 <layout class="QVBoxLayout" name="verticalLayout_21"> 624 <layout class="QVBoxLayout" name="verticalLayout_19">
611 <item> 625 <item>
612 <widget class="QLabel" name="label_25"> 626 <widget class="QLabel" name="label_24">
613 <property name="text"> 627 <property name="text">
614 <string>Left:</string> 628 <string>Up:</string>
615 </property> 629 </property>
616 </widget> 630 </widget>
617 </item> 631 </item>
618 <item> 632 <item>
619 <widget class="QPushButton" name="buttonRStickLeft"> 633 <widget class="QPushButton" name="buttonLStickUp">
620 <property name="text"> 634 <property name="text">
621 <string/> 635 <string/>
622 </property> 636 </property>
@@ -624,17 +638,17 @@ Capture:</string>
624 </item> 638 </item>
625 </layout> 639 </layout>
626 </item> 640 </item>
627 <item row="2" column="1"> 641 <item row="3" column="0">
628 <layout class="QVBoxLayout" name="verticalLayout_32"> 642 <layout class="QVBoxLayout" name="verticalLayout_31">
629 <item> 643 <item>
630 <widget class="QLabel" name="label_10"> 644 <widget class="QLabel" name="label_9">
631 <property name="text"> 645 <property name="text">
632 <string>Modifier:</string> 646 <string>Modifier:</string>
633 </property> 647 </property>
634 </widget> 648 </widget>
635 </item> 649 </item>
636 <item> 650 <item>
637 <widget class="QPushButton" name="buttonRStickMod"> 651 <widget class="QPushButton" name="buttonLStickMod">
638 <property name="text"> 652 <property name="text">
639 <string/> 653 <string/>
640 </property> 654 </property>
@@ -642,17 +656,17 @@ Capture:</string>
642 </item> 656 </item>
643 </layout> 657 </layout>
644 </item> 658 </item>
645 <item row="2" column="0"> 659 <item row="3" column="1">
646 <layout class="QVBoxLayout" name="verticalLayout_6"> 660 <layout class="QVBoxLayout" name="verticalLayout_7" stretch="0,0">
647 <item> 661 <item>
648 <widget class="QLabel" name="label_5"> 662 <widget class="QLabel" name="label_6">
649 <property name="text"> 663 <property name="text">
650 <string>Pressed:</string> 664 <string>Pressed:</string>
651 </property> 665 </property>
652 </widget> 666 </widget>
653 </item> 667 </item>
654 <item> 668 <item>
655 <widget class="QPushButton" name="buttonRStick"> 669 <widget class="QPushButton" name="buttonLStick">
656 <property name="text"> 670 <property name="text">
657 <string/> 671 <string/>
658 </property> 672 </property>
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index f64e68268..7c711158a 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -185,12 +185,24 @@ void GMainWindow::InitializeRecentFileMenuActions() {
185void GMainWindow::InitializeHotkeys() { 185void GMainWindow::InitializeHotkeys() {
186 RegisterHotkey("Main Window", "Load File", QKeySequence::Open); 186 RegisterHotkey("Main Window", "Load File", QKeySequence::Open);
187 RegisterHotkey("Main Window", "Start Emulation"); 187 RegisterHotkey("Main Window", "Start Emulation");
188 RegisterHotkey( "Main Window", "Fullscreen", QKeySequence::FullScreen );
189 RegisterHotkey( "Main Window", "Exit Fullscreen", QKeySequence::Cancel, Qt::ApplicationShortcut );
188 LoadHotkeys(); 190 LoadHotkeys();
189 191
190 connect(GetHotkey("Main Window", "Load File", this), SIGNAL(activated()), this, 192 connect(GetHotkey("Main Window", "Load File", this), SIGNAL(activated()), this,
191 SLOT(OnMenuLoadFile())); 193 SLOT(OnMenuLoadFile()));
192 connect(GetHotkey("Main Window", "Start Emulation", this), SIGNAL(activated()), this, 194 connect(GetHotkey("Main Window", "Start Emulation", this), SIGNAL(activated()), this,
193 SLOT(OnStartGame())); 195 SLOT(OnStartGame()));
196 connect(GetHotkey("Main Window", "Fullscreen", render_window), &QShortcut::activated,
197 ui.action_Fullscreen, &QAction::trigger);
198 connect(GetHotkey("Main Window", "Fullscreen", render_window), &QShortcut::activatedAmbiguously,
199 ui.action_Fullscreen, &QAction::trigger);
200 connect(GetHotkey("Main Window", "Exit Fullscreen", this), &QShortcut::activated, this, [&] {
201 if (emulation_running) {
202 ui.action_Fullscreen->setChecked(false);
203 ToggleFullscreen();
204 }
205 });
194} 206}
195 207
196void GMainWindow::SetDefaultUIGeometry() { 208void GMainWindow::SetDefaultUIGeometry() {
@@ -219,6 +231,8 @@ void GMainWindow::RestoreUIState() {
219 ui.action_Single_Window_Mode->setChecked(UISettings::values.single_window_mode); 231 ui.action_Single_Window_Mode->setChecked(UISettings::values.single_window_mode);
220 ToggleWindowMode(); 232 ToggleWindowMode();
221 233
234 ui.action_Fullscreen->setChecked(UISettings::values.fullscreen);
235
222 ui.action_Display_Dock_Widget_Headers->setChecked(UISettings::values.display_titlebar); 236 ui.action_Display_Dock_Widget_Headers->setChecked(UISettings::values.display_titlebar);
223 OnDisplayTitleBars(ui.action_Display_Dock_Widget_Headers->isChecked()); 237 OnDisplayTitleBars(ui.action_Display_Dock_Widget_Headers->isChecked());
224 238
@@ -263,6 +277,10 @@ void GMainWindow::ConnectMenuEvents() {
263 connect(ui.action_Show_Filter_Bar, &QAction::triggered, this, &GMainWindow::OnToggleFilterBar); 277 connect(ui.action_Show_Filter_Bar, &QAction::triggered, this, &GMainWindow::OnToggleFilterBar);
264 connect(ui.action_Show_Status_Bar, &QAction::triggered, statusBar(), &QStatusBar::setVisible); 278 connect(ui.action_Show_Status_Bar, &QAction::triggered, statusBar(), &QStatusBar::setVisible);
265 279
280 // Fullscreen
281 ui.action_Fullscreen->setShortcut(GetHotkey("Main Window", "Fullscreen", this)->key());
282 connect(ui.action_Fullscreen, &QAction::triggered, this, &GMainWindow::ToggleFullscreen);
283
266 // Help 284 // Help
267 connect(ui.action_About, &QAction::triggered, this, &GMainWindow::OnAbout); 285 connect(ui.action_About, &QAction::triggered, this, &GMainWindow::OnAbout);
268} 286}
@@ -402,6 +420,9 @@ void GMainWindow::BootGame(const QString& filename) {
402 render_window->setFocus(); 420 render_window->setFocus();
403 421
404 emulation_running = true; 422 emulation_running = true;
423 if (ui.action_Fullscreen->isChecked()) {
424 ShowFullscreen();
425 }
405 OnStartGame(); 426 OnStartGame();
406} 427}
407 428
@@ -548,6 +569,41 @@ void GMainWindow::OnStopGame() {
548 ShutdownGame(); 569 ShutdownGame();
549} 570}
550 571
572void GMainWindow::ToggleFullscreen() {
573 if (!emulation_running) {
574 return;
575 }
576 if (ui.action_Fullscreen->isChecked()) {
577 ShowFullscreen();
578 } else {
579 HideFullscreen();
580 }
581}
582
583void GMainWindow::ShowFullscreen() {
584 if (ui.action_Single_Window_Mode->isChecked()) {
585 UISettings::values.geometry = saveGeometry();
586 ui.menubar->hide();
587 statusBar()->hide();
588 showFullScreen();
589 } else {
590 UISettings::values.renderwindow_geometry = render_window->saveGeometry();
591 render_window->showFullScreen();
592 }
593}
594
595void GMainWindow::HideFullscreen() {
596 if (ui.action_Single_Window_Mode->isChecked()) {
597 statusBar()->setVisible(ui.action_Show_Status_Bar->isChecked());
598 ui.menubar->show();
599 showNormal();
600 restoreGeometry(UISettings::values.geometry);
601 } else {
602 render_window->showNormal();
603 render_window->restoreGeometry(UISettings::values.renderwindow_geometry);
604 }
605}
606
551void GMainWindow::ToggleWindowMode() { 607void GMainWindow::ToggleWindowMode() {
552 if (ui.action_Single_Window_Mode->isChecked()) { 608 if (ui.action_Single_Window_Mode->isChecked()) {
553 // Render in the main window... 609 // Render in the main window...
@@ -670,6 +726,7 @@ void GMainWindow::OnCoreError(Core::System::ResultStatus result, std::string det
670 } else { 726 } else {
671 // Only show the message if the game is still running. 727 // Only show the message if the game is still running.
672 if (emu_thread) { 728 if (emu_thread) {
729 emu_thread->SetRunning(true);
673 message_label->setText(status_message); 730 message_label->setText(status_message);
674 message_label->setVisible(true); 731 message_label->setVisible(true);
675 } 732 }
@@ -700,6 +757,7 @@ void GMainWindow::closeEvent(QCloseEvent* event) {
700 UISettings::values.microprofile_visible = microProfileDialog->isVisible(); 757 UISettings::values.microprofile_visible = microProfileDialog->isVisible();
701#endif 758#endif
702 UISettings::values.single_window_mode = ui.action_Single_Window_Mode->isChecked(); 759 UISettings::values.single_window_mode = ui.action_Single_Window_Mode->isChecked();
760 UISettings::values.fullscreen = ui.action_Fullscreen->isChecked();
703 UISettings::values.display_titlebar = ui.action_Display_Dock_Widget_Headers->isChecked(); 761 UISettings::values.display_titlebar = ui.action_Display_Dock_Widget_Headers->isChecked();
704 UISettings::values.show_filter_bar = ui.action_Show_Filter_Bar->isChecked(); 762 UISettings::values.show_filter_bar = ui.action_Show_Filter_Bar->isChecked();
705 UISettings::values.show_status_bar = ui.action_Show_Status_Bar->isChecked(); 763 UISettings::values.show_status_bar = ui.action_Show_Status_Bar->isChecked();
diff --git a/src/yuzu/main.h b/src/yuzu/main.h
index 3add882dd..4a0d912bb 100644
--- a/src/yuzu/main.h
+++ b/src/yuzu/main.h
@@ -127,6 +127,9 @@ private slots:
127 void OnAbout(); 127 void OnAbout();
128 void OnToggleFilterBar(); 128 void OnToggleFilterBar();
129 void OnDisplayTitleBars(bool); 129 void OnDisplayTitleBars(bool);
130 void ToggleFullscreen();
131 void ShowFullscreen();
132 void HideFullscreen();
130 void ToggleWindowMode(); 133 void ToggleWindowMode();
131 void OnCoreError(Core::System::ResultStatus, std::string); 134 void OnCoreError(Core::System::ResultStatus, std::string);
132 135
diff --git a/src/yuzu/main.ui b/src/yuzu/main.ui
index 721e769e8..0fcd93cc2 100644
--- a/src/yuzu/main.ui
+++ b/src/yuzu/main.ui
@@ -83,6 +83,7 @@
83 <string>Debugging</string> 83 <string>Debugging</string>
84 </property> 84 </property>
85 </widget> 85 </widget>
86 <addaction name="action_Fullscreen"/>
86 <addaction name="action_Single_Window_Mode"/> 87 <addaction name="action_Single_Window_Mode"/>
87 <addaction name="action_Display_Dock_Widget_Headers"/> 88 <addaction name="action_Display_Dock_Widget_Headers"/>
88 <addaction name="action_Show_Filter_Bar"/> 89 <addaction name="action_Show_Filter_Bar"/>
@@ -189,6 +190,14 @@
189 <string>Selects a folder to display in the game list</string> 190 <string>Selects a folder to display in the game list</string>
190 </property> 191 </property>
191 </action> 192 </action>
192 </widget> 193 <action name="action_Fullscreen">
194 <property name="checkable">
195 <bool>true</bool>
196 </property>
197 <property name="text">
198 <string>Fullscreen</string>
199 </property>
200 </action>
201 </widget>
193 <resources/> 202 <resources/>
194</ui> 203</ui>
diff --git a/src/yuzu/ui_settings.h b/src/yuzu/ui_settings.h
index e965f0ba9..d093da641 100644
--- a/src/yuzu/ui_settings.h
+++ b/src/yuzu/ui_settings.h
@@ -27,6 +27,7 @@ struct Values {
27 bool microprofile_visible; 27 bool microprofile_visible;
28 28
29 bool single_window_mode; 29 bool single_window_mode;
30 bool fullscreen;
30 bool display_titlebar; 31 bool display_titlebar;
31 bool show_filter_bar; 32 bool show_filter_bar;
32 bool show_status_bar; 33 bool show_status_bar;