diff options
| -rw-r--r-- | src/input_common/main.cpp | 11 | ||||
| -rw-r--r-- | src/input_common/main.h | 32 | ||||
| -rw-r--r-- | src/input_common/sdl/sdl.cpp | 183 | ||||
| -rw-r--r-- | src/input_common/sdl/sdl.h | 23 | ||||
| -rw-r--r-- | src/yuzu/configuration/configure_input.cpp | 139 | ||||
| -rw-r--r-- | src/yuzu/configuration/configure_input.h | 31 | ||||
| -rw-r--r-- | src/yuzu/configuration/configure_input.ui | 296 | 
7 files changed, 524 insertions, 191 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,      return circle_pad_param.Serialize();  } +namespace Polling { + +std::vector<std::unique_ptr<DevicePoller>> GetPollers(DeviceType type) { +#ifdef HAVE_SDL2 +    return SDL::Polling::GetPollers(type); +#else +    return {}; +#endif +} + +} // namespace Polling  } // 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 @@  #pragma once +#include <memory>  #include <string> +#include <vector> + +namespace Common { +class ParamPackage; +}  namespace InputCommon { @@ -31,4 +37,30 @@ std::string GenerateKeyboardParam(int key_code);  std::string GenerateAnalogParamFromKeys(int key_up, int key_down, int key_left, int key_right,                                          int key_modifier, float modifier_scale); +namespace Polling { + +enum class DeviceType { Button, Analog }; + +/** + * A class that can be used to get inputs from an input device like controllers without having to + * poll the device's status yourself + */ +class DevicePoller { +public: +    virtual ~DevicePoller() = default; +    /// Setup and start polling for inputs, should be called before GetNextInput +    virtual void Start() = 0; +    /// Stop polling +    virtual void Stop() = 0; +    /** +     * Every call to this function returns the next input recorded since calling Start +     * @return A ParamPackage of the recorded input, which can be used to create an InputDevice. +     *         If there has been no input, the package is empty +     */ +    virtual Common::ParamPackage GetNextInput() = 0; +}; + +// Get all DevicePoller from all backends for a specific device type +std::vector<std::unique_ptr<DevicePoller>> GetPollers(DeviceType type); +} // namespace Polling  } // 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 @@  // Refer to the license.txt file included.  #include <cmath> -#include <memory>  #include <string>  #include <tuple>  #include <unordered_map> +#include <utility>  #include <SDL.h>  #include "common/logging/log.h"  #include "common/math_util.h" +#include "common/param_package.h" +#include "input_common/main.h"  #include "input_common/sdl/sdl.h"  namespace InputCommon { @@ -69,6 +71,10 @@ public:          return (SDL_JoystickGetHat(joystick.get(), hat) & direction) != 0;      } +    SDL_JoystickID GetJoystickID() const { +        return SDL_JoystickInstanceID(joystick.get()); +    } +  private:      std::unique_ptr<SDL_Joystick, decltype(&SDL_JoystickClose)> joystick;  }; @@ -247,5 +253,180 @@ void Shutdown() {      }  } +/** + * This function converts a joystick ID used in SDL events to the device index. This is necessary + * because Citra opens joysticks using their indices, not their IDs. + */ +static int JoystickIDToDeviceIndex(SDL_JoystickID id) { +    int num_joysticks = SDL_NumJoysticks(); +    for (int i = 0; i < num_joysticks; i++) { +        auto joystick = GetJoystick(i); +        if (joystick->GetJoystickID() == id) { +            return i; +        } +    } +    return -1; +} + +Common::ParamPackage SDLEventToButtonParamPackage(const SDL_Event& event) { +    Common::ParamPackage params({{"engine", "sdl"}}); +    switch (event.type) { +    case SDL_JOYAXISMOTION: +        params.Set("joystick", JoystickIDToDeviceIndex(event.jaxis.which)); +        params.Set("axis", event.jaxis.axis); +        if (event.jaxis.value > 0) { +            params.Set("direction", "+"); +            params.Set("threshold", "0.5"); +        } else { +            params.Set("direction", "-"); +            params.Set("threshold", "-0.5"); +        } +        break; +    case SDL_JOYBUTTONUP: +        params.Set("joystick", JoystickIDToDeviceIndex(event.jbutton.which)); +        params.Set("button", event.jbutton.button); +        break; +    case SDL_JOYHATMOTION: +        params.Set("joystick", JoystickIDToDeviceIndex(event.jhat.which)); +        params.Set("hat", event.jhat.hat); +        switch (event.jhat.value) { +        case SDL_HAT_UP: +            params.Set("direction", "up"); +            break; +        case SDL_HAT_DOWN: +            params.Set("direction", "down"); +            break; +        case SDL_HAT_LEFT: +            params.Set("direction", "left"); +            break; +        case SDL_HAT_RIGHT: +            params.Set("direction", "right"); +            break; +        default: +            return {}; +        } +        break; +    } +    return params; +} + +namespace Polling { + +class SDLPoller : public InputCommon::Polling::DevicePoller { +public: +    SDLPoller() = default; + +    ~SDLPoller() = default; + +    void Start() override { +        // SDL joysticks must be opened, otherwise they don't generate events +        SDL_JoystickUpdate(); +        int num_joysticks = SDL_NumJoysticks(); +        for (int i = 0; i < num_joysticks; i++) { +            joysticks_opened.emplace_back(GetJoystick(i)); +        } +        // Empty event queue to get rid of old events. citra-qt doesn't use the queue +        SDL_Event dummy; +        while (SDL_PollEvent(&dummy)) { +        } +    } + +    void Stop() override { +        joysticks_opened.clear(); +    } + +private: +    std::vector<std::shared_ptr<SDLJoystick>> joysticks_opened; +}; + +class SDLButtonPoller final : public SDLPoller { +public: +    SDLButtonPoller() = default; + +    ~SDLButtonPoller() = default; + +    Common::ParamPackage GetNextInput() override { +        SDL_Event event; +        while (SDL_PollEvent(&event)) { +            switch (event.type) { +            case SDL_JOYAXISMOTION: +                if (std::abs(event.jaxis.value / 32767.0) < 0.5) { +                    break; +                } +            case SDL_JOYBUTTONUP: +            case SDL_JOYHATMOTION: +                return SDLEventToButtonParamPackage(event); +            } +        } +        return {}; +    } +}; + +class SDLAnalogPoller final : public SDLPoller { +public: +    SDLAnalogPoller() = default; + +    ~SDLAnalogPoller() = default; + +    void Start() override { +        SDLPoller::Start(); + +        // Reset stored axes +        analog_xaxis = -1; +        analog_yaxis = -1; +        analog_axes_joystick = -1; +    } + +    Common::ParamPackage GetNextInput() override { +        SDL_Event event; +        while (SDL_PollEvent(&event)) { +            if (event.type != SDL_JOYAXISMOTION || std::abs(event.jaxis.value / 32767.0) < 0.5) { +                continue; +            } +            // An analog device needs two axes, so we need to store the axis for later and wait for +            // a second SDL event. The axes also must be from the same joystick. +            int axis = event.jaxis.axis; +            if (analog_xaxis == -1) { +                analog_xaxis = axis; +                analog_axes_joystick = event.jaxis.which; +            } else if (analog_yaxis == -1 && analog_xaxis != axis && +                       analog_axes_joystick == event.jaxis.which) { +                analog_yaxis = axis; +            } +        } +        Common::ParamPackage params; +        if (analog_xaxis != -1 && analog_yaxis != -1) { +            params.Set("engine", "sdl"); +            params.Set("joystick", JoystickIDToDeviceIndex(analog_axes_joystick)); +            params.Set("axis_x", analog_xaxis); +            params.Set("axis_y", analog_yaxis); +            analog_xaxis = -1; +            analog_yaxis = -1; +            analog_axes_joystick = -1; +            return params; +        } +        return params; +    } + +private: +    int analog_xaxis = -1; +    int analog_yaxis = -1; +    SDL_JoystickID analog_axes_joystick = -1; +}; + +std::vector<std::unique_ptr<InputCommon::Polling::DevicePoller>> GetPollers( +    InputCommon::Polling::DeviceType type) { +    std::vector<std::unique_ptr<InputCommon::Polling::DevicePoller>> pollers; +    switch (type) { +    case InputCommon::Polling::DeviceType::Analog: +        pollers.push_back(std::make_unique<SDLAnalogPoller>()); +        break; +    case InputCommon::Polling::DeviceType::Button: +        pollers.push_back(std::make_unique<SDLButtonPoller>()); +        break; +    } +    return std::move(pollers); +} +} // namespace Polling  } // namespace SDL  } // 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 @@  #pragma once +#include <memory> +#include <vector>  #include "core/frontend/input.h" +union SDL_Event; +namespace Common { +class ParamPackage; +} +namespace InputCommon { +namespace Polling { +class DevicePoller; +enum class DeviceType; +} // namespace Polling +} // namespace InputCommon +  namespace InputCommon {  namespace SDL { @@ -15,5 +28,15 @@ void Init();  /// Unresisters SDL device factories and shut them down.  void Shutdown(); +/// Creates a ParamPackage from an SDL_Event that can directly be used to create a ButtonDevice +Common::ParamPackage SDLEventToButtonParamPackage(const SDL_Event& event); + +namespace Polling { + +/// Get all DevicePoller that use the SDL backend for a specific device type +std::vector<std::unique_ptr<InputCommon::Polling::DevicePoller>> GetPollers( +    InputCommon::Polling::DeviceType type); + +} // namespace Polling  } // namespace SDL  } // namespace InputCommon 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 @@  #include <algorithm>  #include <memory>  #include <utility> +#include <QMessageBox>  #include <QTimer>  #include "common/param_package.h"  #include "input_common/main.h"  #include "yuzu/configuration/config.h"  #include "yuzu/configuration/configure_input.h" -  const std::array<std::string, ConfigureInput::ANALOG_SUB_BUTTONS_NUM>      ConfigureInput::analog_sub_buttons{{          "up", "down", "left", "right", "modifier", @@ -32,23 +32,19 @@ static QString getKeyName(int key_code) {      }  } -static void SetButtonKey(int key, Common::ParamPackage& button_param) { -    button_param = Common::ParamPackage{InputCommon::GenerateKeyboardParam(key)}; -} - -static void SetAnalogKey(int key, Common::ParamPackage& analog_param, -                         const std::string& button_name) { +static void SetAnalogButton(const Common::ParamPackage& input_param, +                            Common::ParamPackage& analog_param, const std::string& button_name) {      if (analog_param.Get("engine", "") != "analog_from_button") {          analog_param = {              {"engine", "analog_from_button"}, {"modifier_scale", "0.5"},          };      } -    analog_param.Set(button_name, InputCommon::GenerateKeyboardParam(key)); +    analog_param.Set(button_name, input_param.Serialize());  }  ConfigureInput::ConfigureInput(QWidget* parent)      : QWidget(parent), ui(std::make_unique<Ui::ConfigureInput>()), -      timer(std::make_unique<QTimer>()) { +      timeout_timer(std::make_unique<QTimer>()), poll_timer(std::make_unique<QTimer>()) {      ui->setupUi(this);      setFocusPolicy(Qt::ClickFocus); @@ -63,7 +59,7 @@ ConfigureInput::ConfigureInput(QWidget* parent)          ui->buttonSL,         ui->buttonSR,        ui->buttonHome,        ui->buttonScreenshot,      }; -    analog_map = {{ +    analog_map_buttons = {{          {              ui->buttonLStickUp, ui->buttonLStickDown, ui->buttonLStickLeft, ui->buttonLStickRight,              ui->buttonLStickMod, @@ -74,35 +70,57 @@ ConfigureInput::ConfigureInput(QWidget* parent)          },      }}; +    analog_map_stick = {ui->buttonLStickAnalog, ui->buttonRStickAnalog}; +      for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; button_id++) {          if (button_map[button_id])              connect(button_map[button_id], &QPushButton::released, [=]() { -                handleClick(button_map[button_id], -                            [=](int key) { SetButtonKey(key, buttons_param[button_id]); }); +                handleClick( +                    button_map[button_id], +                    [=](const Common::ParamPackage& params) { buttons_param[button_id] = params; }, +                    InputCommon::Polling::DeviceType::Button);              });      }      for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; analog_id++) {          for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; sub_button_id++) { -            if (analog_map[analog_id][sub_button_id] != nullptr) { -                connect(analog_map[analog_id][sub_button_id], &QPushButton::released, [=]() { -                    handleClick(analog_map[analog_id][sub_button_id], [=](int key) { -                        SetAnalogKey(key, analogs_param[analog_id], -                                     analog_sub_buttons[sub_button_id]); -                    }); -                }); +            if (analog_map_buttons[analog_id][sub_button_id] != nullptr) { +                connect(analog_map_buttons[analog_id][sub_button_id], &QPushButton::released, +                        [=]() { +                            handleClick(analog_map_buttons[analog_id][sub_button_id], +                                        [=](const Common::ParamPackage& params) { +                                            SetAnalogButton(params, analogs_param[analog_id], +                                                            analog_sub_buttons[sub_button_id]); +                                        }, +                                        InputCommon::Polling::DeviceType::Button); +                        });              }          } +        connect(analog_map_stick[analog_id], &QPushButton::released, [=]() { +            QMessageBox::information( +                this, "Information", +                "After pressing OK, first move your joystick horizontally, and then vertically."); +            handleClick( +                analog_map_stick[analog_id], +                [=](const Common::ParamPackage& params) { analogs_param[analog_id] = params; }, +                InputCommon::Polling::DeviceType::Analog); +        });      }      connect(ui->buttonRestoreDefaults, &QPushButton::released, [this]() { restoreDefaults(); }); -    timer->setSingleShot(true); -    connect(timer.get(), &QTimer::timeout, [this]() { -        releaseKeyboard(); -        releaseMouse(); -        key_setter = boost::none; -        updateButtonLabels(); +    timeout_timer->setSingleShot(true); +    connect(timeout_timer.get(), &QTimer::timeout, [this]() { setPollingResult({}, true); }); + +    connect(poll_timer.get(), &QTimer::timeout, [this]() { +        Common::ParamPackage params; +        for (auto& poller : device_pollers) { +            params = poller->GetNextInput(); +            if (params.Has("engine")) { +                setPollingResult(params, false); +                return; +            } +        }      });      this->loadConfiguration(); @@ -132,13 +150,15 @@ void ConfigureInput::loadConfiguration() {  void ConfigureInput::restoreDefaults() {      for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; button_id++) { -        SetButtonKey(Config::default_buttons[button_id], buttons_param[button_id]); +        buttons_param[button_id] = Common::ParamPackage{ +            InputCommon::GenerateKeyboardParam(Config::default_buttons[button_id])};      }      for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; analog_id++) {          for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; sub_button_id++) { -            SetAnalogKey(Config::default_analogs[analog_id][sub_button_id], -                         analogs_param[analog_id], analog_sub_buttons[sub_button_id]); +            Common::ParamPackage params{InputCommon::GenerateKeyboardParam( +                Config::default_analogs[analog_id][sub_button_id])}; +            SetAnalogButton(params, analogs_param[analog_id], analog_sub_buttons[sub_button_id]);          }      }      updateButtonLabels(); @@ -149,7 +169,9 @@ void ConfigureInput::updateButtonLabels() {      QString non_keyboard(tr("[non-keyboard]"));      auto KeyToText = [&non_keyboard](const Common::ParamPackage& param) { -        if (param.Get("engine", "") != "keyboard") { +        if (!param.Has("engine")) { +            return QString("[not set]"); +        } else if (param.Get("engine", "") != "keyboard") {              return non_keyboard;          } else {              return getKeyName(param.Get("code", 0)); @@ -162,7 +184,7 @@ void ConfigureInput::updateButtonLabels() {      for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; analog_id++) {          if (analogs_param[analog_id].Get("engine", "") != "analog_from_button") { -            for (QPushButton* button : analog_map[analog_id]) { +            for (QPushButton* button : analog_map_buttons[analog_id]) {                  if (button)                      button->setText(non_keyboard);              } @@ -170,35 +192,66 @@ void ConfigureInput::updateButtonLabels() {              for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; sub_button_id++) {                  Common::ParamPackage param(                      analogs_param[analog_id].Get(analog_sub_buttons[sub_button_id], "")); -                if (analog_map[analog_id][sub_button_id]) -                    analog_map[analog_id][sub_button_id]->setText(KeyToText(param)); +                if (analog_map_buttons[analog_id][sub_button_id]) +                    analog_map_buttons[analog_id][sub_button_id]->setText(KeyToText(param));              }          } +        analog_map_stick[analog_id]->setText("Set Analog Stick");      }  } -void ConfigureInput::handleClick(QPushButton* button, std::function<void(int)> new_key_setter) { +void ConfigureInput::handleClick(QPushButton* button, +                                 std::function<void(const Common::ParamPackage&)> new_input_setter, +                                 InputCommon::Polling::DeviceType type) {      button->setText(tr("[press key]"));      button->setFocus(); -    key_setter = new_key_setter; +    input_setter = new_input_setter; + +    device_pollers = InputCommon::Polling::GetPollers(type); + +    // Keyboard keys can only be used as button devices +    want_keyboard_keys = type == InputCommon::Polling::DeviceType::Button; + +    for (auto& poller : device_pollers) { +        poller->Start(); +    }      grabKeyboard();      grabMouse(); -    timer->start(5000); // Cancel after 5 seconds +    timeout_timer->start(5000); // Cancel after 5 seconds +    poll_timer->start(200);     // Check for new inputs every 200ms  } -void ConfigureInput::keyPressEvent(QKeyEvent* event) { +void ConfigureInput::setPollingResult(const Common::ParamPackage& params, bool abort) {      releaseKeyboard();      releaseMouse(); +    timeout_timer->stop(); +    poll_timer->stop(); +    for (auto& poller : device_pollers) { +        poller->Stop(); +    } -    if (!key_setter || !event) -        return; - -    if (event->key() != Qt::Key_Escape) -        (*key_setter)(event->key()); +    if (!abort) { +        (*input_setter)(params); +    }      updateButtonLabels(); -    key_setter = boost::none; -    timer->stop(); +    input_setter = boost::none; +} + +void ConfigureInput::keyPressEvent(QKeyEvent* event) { +    if (!input_setter || !event) +        return; + +    if (event->key() != Qt::Key_Escape) { +        if (want_keyboard_keys) { +            setPollingResult(Common::ParamPackage{InputCommon::GenerateKeyboardParam(event->key())}, +                             false); +        } else { +            // Escape key wasn't pressed and we don't want any keyboard keys, so don't stop polling +            return; +        } +    } +    setPollingResult({}, true);  } 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 @@  #include <functional>  #include <memory>  #include <string> +#include <unordered_map>  #include <QKeyEvent>  #include <QWidget>  #include <boost/optional.hpp>  #include "common/param_package.h"  #include "core/settings.h" +#include "input_common/main.h"  #include "ui_configure_input.h"  class QPushButton; @@ -35,10 +37,11 @@ public:  private:      std::unique_ptr<Ui::ConfigureInput> ui; -    std::unique_ptr<QTimer> timer; +    std::unique_ptr<QTimer> timeout_timer; +    std::unique_ptr<QTimer> poll_timer;      /// This will be the the setting function when an input is awaiting configuration. -    boost::optional<std::function<void(int)>> key_setter; +    boost::optional<std::function<void(const Common::ParamPackage&)>> input_setter;      std::array<Common::ParamPackage, Settings::NativeButton::NumButtons> buttons_param;      std::array<Common::ParamPackage, Settings::NativeAnalog::NumAnalogs> analogs_param; @@ -48,13 +51,23 @@ private:      /// Each button input is represented by a QPushButton.      std::array<QPushButton*, Settings::NativeButton::NumButtons> button_map; -    /// Each analog input is represented by five QPushButtons which represents up, down, left, right -    /// and modifier +    /// A group of five QPushButtons represent one analog input. The buttons each represent up, +    /// down, left, right, and modifier, respectively.      std::array<std::array<QPushButton*, ANALOG_SUB_BUTTONS_NUM>, Settings::NativeAnalog::NumAnalogs> -        analog_map; +        analog_map_buttons; + +    /// Analog inputs are also represented each with a single button, used to configure with an +    /// actual analog stick +    std::array<QPushButton*, Settings::NativeAnalog::NumAnalogs> analog_map_stick;      static const std::array<std::string, ANALOG_SUB_BUTTONS_NUM> analog_sub_buttons; +    std::vector<std::unique_ptr<InputCommon::Polling::DevicePoller>> device_pollers; + +    /// A flag to indicate if keyboard keys are okay when configuring an input. If this is false, +    /// keyboard events are ignored. +    bool want_keyboard_keys = false; +      /// Load configuration settings.      void loadConfiguration();      /// Restore all buttons to their default values. @@ -63,7 +76,13 @@ private:      void updateButtonLabels();      /// Called when the button was pressed. -    void handleClick(QPushButton* button, std::function<void(int)> new_key_setter); +    void handleClick(QPushButton* button, +                     std::function<void(const Common::ParamPackage&)> new_input_setter, +                     InputCommon::Polling::DeviceType type); + +    /// Finish polling and configure input using the input_setter +    void setPollingResult(const Common::ParamPackage& params, bool abort); +      /// Handle key press events.      void keyPressEvent(QKeyEvent* event) override;  }; 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 @@      <x>0</x>      <y>0</y>      <width>343</width> -    <height>665</height> +    <height>677</height>     </rect>    </property>    <property name="windowTitle"> @@ -16,10 +16,10 @@    <layout class="QVBoxLayout" name="verticalLayout_5">     <item>      <layout class="QGridLayout" name="gridLayout_7"> -     <item row="0" column="0"> -      <widget class="QGroupBox" name="faceButtons"> +     <item row="3" column="1"> +      <widget class="QGroupBox" name="faceButtons_6">         <property name="title"> -        <string>Face Buttons</string> +        <string>Misc.</string>         </property>         <property name="flat">          <bool>false</bool> @@ -27,18 +27,18 @@         <property name="checkable">          <bool>false</bool>         </property> -       <layout class="QGridLayout" name="gridLayout"> +       <layout class="QGridLayout" name="gridLayout_6">          <item row="0" column="0"> -         <layout class="QVBoxLayout" name="verticalLayout"> +         <layout class="QVBoxLayout" name="verticalLayout_25">            <item> -           <widget class="QLabel" name="label"> +           <widget class="QLabel" name="label_29">              <property name="text"> -             <string>A:</string> +             <string>Plus:</string>              </property>             </widget>            </item>            <item> -           <widget class="QPushButton" name="buttonA"> +           <widget class="QPushButton" name="buttonPlus">              <property name="text">               <string/>              </property> @@ -47,16 +47,16 @@           </layout>          </item>          <item row="0" column="1"> -         <layout class="QVBoxLayout" name="verticalLayout_2"> +         <layout class="QVBoxLayout" name="verticalLayout_26">            <item> -           <widget class="QLabel" name="label_2"> +           <widget class="QLabel" name="label_30">              <property name="text"> -             <string>B:</string> +             <string>Minus:</string>              </property>             </widget>            </item>            <item> -           <widget class="QPushButton" name="buttonB"> +           <widget class="QPushButton" name="buttonMinus">              <property name="text">               <string/>              </property> @@ -65,16 +65,16 @@           </layout>          </item>          <item row="1" column="0"> -         <layout class="QVBoxLayout" name="verticalLayout_3"> +         <layout class="QVBoxLayout" name="verticalLayout_27">            <item> -           <widget class="QLabel" name="label_3"> +           <widget class="QLabel" name="label_31">              <property name="text"> -             <string>X:</string> +             <string>Home:</string>              </property>             </widget>            </item>            <item> -           <widget class="QPushButton" name="buttonX"> +           <widget class="QPushButton" name="buttonHome">              <property name="text">               <string/>              </property> @@ -83,16 +83,17 @@           </layout>          </item>          <item row="1" column="1"> -         <layout class="QVBoxLayout" name="verticalLayout_4"> +         <layout class="QVBoxLayout" name="verticalLayout_28">            <item> -           <widget class="QLabel" name="label_4"> +           <widget class="QLabel" name="label_11">              <property name="text"> -             <string>Y:</string> +             <string>Screen +Capture:</string>              </property>             </widget>            </item>            <item> -           <widget class="QPushButton" name="buttonY"> +           <widget class="QPushButton" name="buttonScreenshot">              <property name="text">               <string/>              </property> @@ -100,13 +101,26 @@            </item>           </layout>          </item> +        <item row="2" column="1"> +         <spacer name="verticalSpacer"> +          <property name="orientation"> +           <enum>Qt::Vertical</enum> +          </property> +          <property name="sizeHint" stdset="0"> +           <size> +            <width>20</width> +            <height>40</height> +           </size> +          </property> +         </spacer> +        </item>         </layout>        </widget>       </item> -     <item row="0" column="1"> -      <widget class="QGroupBox" name="faceButtons_2"> +     <item row="0" column="0"> +      <widget class="QGroupBox" name="faceButtons">         <property name="title"> -        <string>Directional Pad</string> +        <string>Face Buttons</string>         </property>         <property name="flat">          <bool>false</bool> @@ -114,18 +128,18 @@         <property name="checkable">          <bool>false</bool>         </property> -       <layout class="QGridLayout" name="gridLayout_2"> -        <item row="1" column="0"> -         <layout class="QVBoxLayout" name="verticalLayout_12"> +       <layout class="QGridLayout" name="gridLayout"> +        <item row="0" column="0"> +         <layout class="QVBoxLayout" name="verticalLayout">            <item> -           <widget class="QLabel" name="label_34"> +           <widget class="QLabel" name="label">              <property name="text"> -             <string>Up:</string> +             <string>A:</string>              </property>             </widget>            </item>            <item> -           <widget class="QPushButton" name="buttonDpadUp"> +           <widget class="QPushButton" name="buttonA">              <property name="text">               <string/>              </property> @@ -133,17 +147,17 @@            </item>           </layout>          </item> -        <item row="1" column="1"> -         <layout class="QVBoxLayout" name="verticalLayout_9"> +        <item row="0" column="1"> +         <layout class="QVBoxLayout" name="verticalLayout_2">            <item> -           <widget class="QLabel" name="label_35"> +           <widget class="QLabel" name="label_2">              <property name="text"> -             <string>Down:</string> +             <string>B:</string>              </property>             </widget>            </item>            <item> -           <widget class="QPushButton" name="buttonDpadDown"> +           <widget class="QPushButton" name="buttonB">              <property name="text">               <string/>              </property> @@ -151,17 +165,17 @@            </item>           </layout>          </item> -        <item row="0" column="0"> -         <layout class="QVBoxLayout" name="verticalLayout_10"> +        <item row="1" column="0"> +         <layout class="QVBoxLayout" name="verticalLayout_3">            <item> -           <widget class="QLabel" name="label_32"> +           <widget class="QLabel" name="label_3">              <property name="text"> -             <string>Left:</string> +             <string>X:</string>              </property>             </widget>            </item>            <item> -           <widget class="QPushButton" name="buttonDpadLeft"> +           <widget class="QPushButton" name="buttonX">              <property name="text">               <string/>              </property> @@ -169,17 +183,17 @@            </item>           </layout>          </item> -        <item row="0" column="1"> -         <layout class="QVBoxLayout" name="verticalLayout_11"> +        <item row="1" column="1"> +         <layout class="QVBoxLayout" name="verticalLayout_4">            <item> -           <widget class="QLabel" name="label_33"> +           <widget class="QLabel" name="label_4">              <property name="text"> -             <string>Right:</string> +             <string>Y:</string>              </property>             </widget>            </item>            <item> -           <widget class="QPushButton" name="buttonDpadRight"> +           <widget class="QPushButton" name="buttonY">              <property name="text">               <string/>              </property> @@ -190,10 +204,10 @@         </layout>        </widget>       </item> -     <item row="3" column="1"> -      <widget class="QGroupBox" name="faceButtons_6"> +     <item row="0" column="1"> +      <widget class="QGroupBox" name="faceButtons_2">         <property name="title"> -        <string>Misc.</string> +        <string>Directional Pad</string>         </property>         <property name="flat">          <bool>false</bool> @@ -201,18 +215,18 @@         <property name="checkable">          <bool>false</bool>         </property> -       <layout class="QGridLayout" name="gridLayout_6"> -        <item row="0" column="0"> -         <layout class="QVBoxLayout" name="verticalLayout_25"> +       <layout class="QGridLayout" name="gridLayout_2"> +        <item row="1" column="0"> +         <layout class="QVBoxLayout" name="verticalLayout_12">            <item> -           <widget class="QLabel" name="label_29"> +           <widget class="QLabel" name="label_34">              <property name="text"> -             <string>Plus:</string> +             <string>Up:</string>              </property>             </widget>            </item>            <item> -           <widget class="QPushButton" name="buttonPlus"> +           <widget class="QPushButton" name="buttonDpadUp">              <property name="text">               <string/>              </property> @@ -220,17 +234,17 @@            </item>           </layout>          </item> -        <item row="0" column="1"> -         <layout class="QVBoxLayout" name="verticalLayout_26"> +        <item row="1" column="1"> +         <layout class="QVBoxLayout" name="verticalLayout_9">            <item> -           <widget class="QLabel" name="label_30"> +           <widget class="QLabel" name="label_35">              <property name="text"> -             <string>Minus:</string> +             <string>Down:</string>              </property>             </widget>            </item>            <item> -           <widget class="QPushButton" name="buttonMinus"> +           <widget class="QPushButton" name="buttonDpadDown">              <property name="text">               <string/>              </property> @@ -238,17 +252,17 @@            </item>           </layout>          </item> -        <item row="1" column="0"> -         <layout class="QVBoxLayout" name="verticalLayout_27"> +        <item row="0" column="0"> +         <layout class="QVBoxLayout" name="verticalLayout_10">            <item> -           <widget class="QLabel" name="label_31"> +           <widget class="QLabel" name="label_32">              <property name="text"> -             <string>Home:</string> +             <string>Left:</string>              </property>             </widget>            </item>            <item> -           <widget class="QPushButton" name="buttonHome"> +           <widget class="QPushButton" name="buttonDpadLeft">              <property name="text">               <string/>              </property> @@ -256,18 +270,17 @@            </item>           </layout>          </item> -        <item row="1" column="1"> -         <layout class="QVBoxLayout" name="verticalLayout_28"> +        <item row="0" column="1"> +         <layout class="QVBoxLayout" name="verticalLayout_11">            <item> -           <widget class="QLabel" name="label_11"> +           <widget class="QLabel" name="label_33">              <property name="text"> -             <string>Screen -Capture:</string> +             <string>Right:</string>              </property>             </widget>            </item>            <item> -           <widget class="QPushButton" name="buttonScreenshot"> +           <widget class="QPushButton" name="buttonDpadRight">              <property name="text">               <string/>              </property> @@ -275,19 +288,6 @@ Capture:</string>            </item>           </layout>          </item> -        <item row="2" column="1"> -         <spacer name="verticalSpacer"> -          <property name="orientation"> -           <enum>Qt::Vertical</enum> -          </property> -          <property name="sizeHint" stdset="0"> -           <size> -            <width>20</width> -            <height>40</height> -           </size> -          </property> -         </spacer> -        </item>         </layout>        </widget>       </item> @@ -414,10 +414,13 @@ Capture:</string>         </layout>        </widget>       </item> -     <item row="1" column="0"> -      <widget class="QGroupBox" name="faceButtons_4"> +     <item row="1" column="1"> +      <widget class="QGroupBox" name="faceButtons_5">         <property name="title"> -        <string>Left Stick</string> +        <string>Right Stick</string> +       </property> +       <property name="alignment"> +        <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>         </property>         <property name="flat">          <bool>false</bool> @@ -425,18 +428,18 @@ Capture:</string>         <property name="checkable">          <bool>false</bool>         </property> -       <layout class="QGridLayout" name="gridLayout_4"> -        <item row="0" column="0"> -         <layout class="QVBoxLayout" name="verticalLayout_17"> +       <layout class="QGridLayout" name="gridLayout_5"> +        <item row="1" column="1"> +         <layout class="QVBoxLayout" name="verticalLayout_24">            <item> -           <widget class="QLabel" name="label_21"> +           <widget class="QLabel" name="label_26">              <property name="text"> -             <string>Left:</string> +             <string>Down:</string>              </property>             </widget>            </item>            <item> -           <widget class="QPushButton" name="buttonLStickLeft"> +           <widget class="QPushButton" name="buttonRStickDown">              <property name="text">               <string/>              </property> @@ -445,16 +448,16 @@ Capture:</string>           </layout>          </item>          <item row="0" column="1"> -         <layout class="QVBoxLayout" name="verticalLayout_18"> +         <layout class="QVBoxLayout" name="verticalLayout_22">            <item> -           <widget class="QLabel" name="label_23"> +           <widget class="QLabel" name="label_27">              <property name="text">               <string>Right:</string>              </property>             </widget>            </item>            <item> -           <widget class="QPushButton" name="buttonLStickRight"> +           <widget class="QPushButton" name="buttonRStickRight">              <property name="text">               <string/>              </property> @@ -462,17 +465,24 @@ Capture:</string>            </item>           </layout>          </item> +        <item row="3" column="0" colspan="2"> +         <widget class="QPushButton" name="buttonRStickAnalog"> +          <property name="text"> +           <string>Set Analog Stick</string> +          </property> +         </widget> +        </item>          <item row="1" column="0"> -         <layout class="QVBoxLayout" name="verticalLayout_19"> +         <layout class="QVBoxLayout" name="verticalLayout_21">            <item> -           <widget class="QLabel" name="label_24"> +           <widget class="QLabel" name="label_25">              <property name="text"> -             <string>Up:</string> +             <string>Left:</string>              </property>             </widget>            </item>            <item> -           <widget class="QPushButton" name="buttonLStickUp"> +           <widget class="QPushButton" name="buttonRStickLeft">              <property name="text">               <string/>              </property> @@ -480,17 +490,17 @@ Capture:</string>            </item>           </layout>          </item> -        <item row="1" column="1"> -         <layout class="QVBoxLayout" name="verticalLayout_20"> +        <item row="0" column="0"> +         <layout class="QVBoxLayout" name="verticalLayout_25">            <item> -           <widget class="QLabel" name="label_22"> +           <widget class="QLabel" name="label_28">              <property name="text"> -             <string>Down:</string> +             <string>Up:</string>              </property>             </widget>            </item>            <item> -           <widget class="QPushButton" name="buttonLStickDown"> +           <widget class="QPushButton" name="buttonRStickUp">              <property name="text">               <string/>              </property> @@ -499,16 +509,16 @@ Capture:</string>           </layout>          </item>          <item row="2" column="0"> -         <layout class="QVBoxLayout" name="verticalLayout_7" stretch="0,0"> +         <layout class="QVBoxLayout" name="verticalLayout_6">            <item> -           <widget class="QLabel" name="label_6"> +           <widget class="QLabel" name="label_5">              <property name="text">               <string>Pressed:</string>              </property>             </widget>            </item>            <item> -           <widget class="QPushButton" name="buttonLStick"> +           <widget class="QPushButton" name="buttonRStick">              <property name="text">               <string/>              </property> @@ -517,16 +527,16 @@ Capture:</string>           </layout>          </item>          <item row="2" column="1"> -         <layout class="QVBoxLayout" name="verticalLayout_31"> +         <layout class="QVBoxLayout" name="verticalLayout_32">            <item> -           <widget class="QLabel" name="label_9"> +           <widget class="QLabel" name="label_10">              <property name="text">               <string>Modifier:</string>              </property>             </widget>            </item>            <item> -           <widget class="QPushButton" name="buttonLStickMod"> +           <widget class="QPushButton" name="buttonRStickMod">              <property name="text">               <string/>              </property> @@ -537,13 +547,10 @@ Capture:</string>         </layout>        </widget>       </item> -     <item row="1" column="1"> -      <widget class="QGroupBox" name="faceButtons_5"> +     <item row="1" column="0"> +      <widget class="QGroupBox" name="faceButtons_4">         <property name="title"> -        <string>Right Stick</string> -       </property> -       <property name="alignment"> -        <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set> +        <string>Left Stick</string>         </property>         <property name="flat">          <bool>false</bool> @@ -551,18 +558,18 @@ Capture:</string>         <property name="checkable">          <bool>false</bool>         </property> -       <layout class="QGridLayout" name="gridLayout_5"> +       <layout class="QGridLayout" name="gridLayout_4">          <item row="1" column="1"> -         <layout class="QVBoxLayout" name="verticalLayout_24"> +         <layout class="QVBoxLayout" name="verticalLayout_20">            <item> -           <widget class="QLabel" name="label_26"> +           <widget class="QLabel" name="label_22">              <property name="text">               <string>Down:</string>              </property>             </widget>            </item>            <item> -           <widget class="QPushButton" name="buttonRStickDown"> +           <widget class="QPushButton" name="buttonLStickDown">              <property name="text">               <string/>              </property> @@ -570,17 +577,24 @@ Capture:</string>            </item>           </layout>          </item> +        <item row="4" column="0" colspan="2"> +         <widget class="QPushButton" name="buttonLStickAnalog"> +          <property name="text"> +           <string>Set Analog Stick</string> +          </property> +         </widget> +        </item>          <item row="0" column="1"> -         <layout class="QVBoxLayout" name="verticalLayout_22"> +         <layout class="QVBoxLayout" name="verticalLayout_18">            <item> -           <widget class="QLabel" name="label_27"> +           <widget class="QLabel" name="label_23">              <property name="text">               <string>Right:</string>              </property>             </widget>            </item>            <item> -           <widget class="QPushButton" name="buttonRStickRight"> +           <widget class="QPushButton" name="buttonLStickRight">              <property name="text">               <string/>              </property> @@ -588,17 +602,17 @@ Capture:</string>            </item>           </layout>          </item> -        <item row="1" column="0"> -         <layout class="QVBoxLayout" name="verticalLayout_23"> +        <item row="0" column="0"> +         <layout class="QVBoxLayout" name="verticalLayout_17">            <item> -           <widget class="QLabel" name="label_28"> +           <widget class="QLabel" name="label_21">              <property name="text"> -             <string>Up:</string> +             <string>Left:</string>              </property>             </widget>            </item>            <item> -           <widget class="QPushButton" name="buttonRStickUp"> +           <widget class="QPushButton" name="buttonLStickLeft">              <property name="text">               <string/>              </property> @@ -606,17 +620,17 @@ Capture:</string>            </item>           </layout>          </item> -        <item row="0" column="0"> -         <layout class="QVBoxLayout" name="verticalLayout_21"> +        <item row="1" column="0"> +         <layout class="QVBoxLayout" name="verticalLayout_19">            <item> -           <widget class="QLabel" name="label_25"> +           <widget class="QLabel" name="label_24">              <property name="text"> -             <string>Left:</string> +             <string>Up:</string>              </property>             </widget>            </item>            <item> -           <widget class="QPushButton" name="buttonRStickLeft"> +           <widget class="QPushButton" name="buttonLStickUp">              <property name="text">               <string/>              </property> @@ -624,17 +638,17 @@ Capture:</string>            </item>           </layout>          </item> -        <item row="2" column="1"> -         <layout class="QVBoxLayout" name="verticalLayout_32"> +        <item row="3" column="0"> +         <layout class="QVBoxLayout" name="verticalLayout_31">            <item> -           <widget class="QLabel" name="label_10"> +           <widget class="QLabel" name="label_9">              <property name="text">               <string>Modifier:</string>              </property>             </widget>            </item>            <item> -           <widget class="QPushButton" name="buttonRStickMod"> +           <widget class="QPushButton" name="buttonLStickMod">              <property name="text">               <string/>              </property> @@ -642,17 +656,17 @@ Capture:</string>            </item>           </layout>          </item> -        <item row="2" column="0"> -         <layout class="QVBoxLayout" name="verticalLayout_6"> +        <item row="3" column="1"> +         <layout class="QVBoxLayout" name="verticalLayout_7" stretch="0,0">            <item> -           <widget class="QLabel" name="label_5"> +           <widget class="QLabel" name="label_6">              <property name="text">               <string>Pressed:</string>              </property>             </widget>            </item>            <item> -           <widget class="QPushButton" name="buttonRStick"> +           <widget class="QPushButton" name="buttonLStick">              <property name="text">               <string/>              </property>  | 
