mirror of
https://github.com/shadps4-emu/shadPS4.git
synced 2025-09-08 18:45:38 +00:00
Qt: add customizable controller hotkeys (#3369)
* customizable controller hotkeys - initial * Update input_handler.h
This commit is contained in:
parent
a362f20dae
commit
c7f1c66b82
15 changed files with 1067 additions and 15 deletions
|
@ -1079,6 +1079,9 @@ set(QT_GUI src/qt_gui/about_dialog.cpp
|
|||
src/qt_gui/settings.h
|
||||
src/qt_gui/sdl_event_wrapper.cpp
|
||||
src/qt_gui/sdl_event_wrapper.h
|
||||
src/qt_gui/hotkeys.h
|
||||
src/qt_gui/hotkeys.cpp
|
||||
src/qt_gui/hotkeys.ui
|
||||
${EMULATOR}
|
||||
${RESOURCE_FILES}
|
||||
${TRANSLATIONS}
|
||||
|
|
|
@ -74,6 +74,7 @@ path = [
|
|||
"src/images/website.svg",
|
||||
"src/images/youtube.svg",
|
||||
"src/images/trophy.wav",
|
||||
"src/images/hotkey.png",
|
||||
"src/shadps4.qrc",
|
||||
"src/shadps4.rc",
|
||||
"src/qt_gui/translations/update_translation.sh",
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
#include "layer.h"
|
||||
|
||||
#include <SDL3/SDL_events.h>
|
||||
#include <imgui.h>
|
||||
|
||||
#include "SDL3/SDL_log.h"
|
||||
|
@ -28,6 +29,7 @@ using L = ::Core::Devtools::Layer;
|
|||
|
||||
static bool show_simple_fps = false;
|
||||
static bool visibility_toggled = false;
|
||||
static bool show_quit_window = false;
|
||||
|
||||
static float fps_scale = 1.0f;
|
||||
static int dump_frame_count = 1;
|
||||
|
@ -138,15 +140,8 @@ void L::DrawAdvanced() {
|
|||
const auto& ctx = *GImGui;
|
||||
const auto& io = ctx.IO;
|
||||
|
||||
auto isSystemPaused = DebugState.IsGuestThreadsPaused();
|
||||
|
||||
frame_graph.Draw();
|
||||
|
||||
if (isSystemPaused) {
|
||||
GetForegroundDrawList(GetMainViewport())
|
||||
->AddText({10.0f, io.DisplaySize.y - 40.0f}, IM_COL32_WHITE, "Emulator paused");
|
||||
}
|
||||
|
||||
if (DebugState.should_show_frame_dump && DebugState.waiting_reg_dumps.empty()) {
|
||||
DebugState.should_show_frame_dump = false;
|
||||
std::unique_lock lock{DebugState.frame_dump_list_mutex};
|
||||
|
@ -383,20 +378,17 @@ void L::Draw() {
|
|||
if (DebugState.IsGuestThreadsPaused()) {
|
||||
DebugState.ResumeGuestThreads();
|
||||
SDL_Log("Game resumed from Keyboard");
|
||||
show_pause_status = false;
|
||||
} else {
|
||||
DebugState.PauseGuestThreads();
|
||||
SDL_Log("Game paused from Keyboard");
|
||||
show_pause_status = true;
|
||||
}
|
||||
visibility_toggled = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (show_pause_status) {
|
||||
if (DebugState.IsGuestThreadsPaused()) {
|
||||
ImVec2 pos = ImVec2(10, 10);
|
||||
ImU32 color = IM_COL32(255, 255, 255, 255);
|
||||
|
||||
ImGui::GetForegroundDrawList()->AddText(pos, color, "Game Paused Press F9 to Resume");
|
||||
}
|
||||
|
||||
|
@ -436,5 +428,56 @@ void L::Draw() {
|
|||
PopFont();
|
||||
}
|
||||
|
||||
if (show_quit_window) {
|
||||
ImVec2 center = ImGui::GetMainViewport()->GetCenter();
|
||||
ImGui::SetNextWindowPos(center, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
|
||||
|
||||
if (Begin("Quit Notification", nullptr,
|
||||
ImGuiWindowFlags_NoNav | ImGuiWindowFlags_NoDecoration |
|
||||
ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDocking)) {
|
||||
SetWindowFontScale(1.5f);
|
||||
TextCentered("Are you sure you want to quit?");
|
||||
NewLine();
|
||||
Text("Press Escape or Circle/B button to cancel");
|
||||
Text("Press Enter or Cross/A button to quit");
|
||||
|
||||
if (IsKeyPressed(ImGuiKey_Escape, false) ||
|
||||
(IsKeyPressed(ImGuiKey_GamepadFaceRight, false))) {
|
||||
show_quit_window = false;
|
||||
}
|
||||
|
||||
if (IsKeyPressed(ImGuiKey_Enter, false) ||
|
||||
(IsKeyPressed(ImGuiKey_GamepadFaceDown, false))) {
|
||||
SDL_Event event;
|
||||
SDL_memset(&event, 0, sizeof(event));
|
||||
event.type = SDL_EVENT_QUIT;
|
||||
SDL_PushEvent(&event);
|
||||
}
|
||||
}
|
||||
End();
|
||||
}
|
||||
|
||||
PopID();
|
||||
}
|
||||
|
||||
void L::TextCentered(const std::string& text) {
|
||||
float window_width = ImGui::GetWindowSize().x;
|
||||
float text_width = ImGui::CalcTextSize(text.c_str()).x;
|
||||
float text_indentation = (window_width - text_width) * 0.5f;
|
||||
|
||||
ImGui::SameLine(text_indentation);
|
||||
ImGui::Text("%s", text.c_str());
|
||||
}
|
||||
|
||||
namespace Overlay {
|
||||
|
||||
void ToggleSimpleFps() {
|
||||
show_simple_fps = !show_simple_fps;
|
||||
visibility_toggled = true;
|
||||
}
|
||||
|
||||
void ToggleQuitWindow() {
|
||||
show_quit_window = !show_quit_window;
|
||||
}
|
||||
|
||||
} // namespace Overlay
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
#include <string>
|
||||
|
||||
#include "imgui/imgui_layer.h"
|
||||
|
||||
|
@ -19,7 +20,14 @@ public:
|
|||
static void SetupSettings();
|
||||
|
||||
void Draw() override;
|
||||
bool show_pause_status = false;
|
||||
void TextCentered(const std::string& text);
|
||||
};
|
||||
|
||||
} // namespace Core::Devtools
|
||||
|
||||
namespace Overlay {
|
||||
|
||||
void ToggleSimpleFps();
|
||||
void ToggleQuitWindow();
|
||||
|
||||
} // namespace Overlay
|
||||
|
|
BIN
src/images/hotkey.png
Normal file
BIN
src/images/hotkey.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 18 KiB |
|
@ -60,6 +60,10 @@ std::pair<int, int> leftjoystick_deadzone, rightjoystick_deadzone, lefttrigger_d
|
|||
std::list<std::pair<InputEvent, bool>> pressed_keys;
|
||||
std::list<InputID> toggled_keys;
|
||||
static std::vector<BindingConnection> connections;
|
||||
static std::vector<std::string> fullscreenHotkeyInputsPad(3, "");
|
||||
static std::vector<std::string> pauseHotkeyInputsPad(3, "");
|
||||
static std::vector<std::string> simpleFpsHotkeyInputsPad(3, "");
|
||||
static std::vector<std::string> quitHotkeyInputsPad(3, "");
|
||||
|
||||
auto output_array = std::array{
|
||||
// Important: these have to be the first, or else they will update in the wrong order
|
||||
|
@ -731,4 +735,158 @@ void ActivateOutputsFromInputs() {
|
|||
}
|
||||
}
|
||||
|
||||
std::vector<std::string> GetHotkeyInputs(Input::HotkeyPad hotkey) {
|
||||
switch (hotkey) {
|
||||
case Input::HotkeyPad::FullscreenPad:
|
||||
return fullscreenHotkeyInputsPad;
|
||||
case Input::HotkeyPad::PausePad:
|
||||
return pauseHotkeyInputsPad;
|
||||
case Input::HotkeyPad::SimpleFpsPad:
|
||||
return simpleFpsHotkeyInputsPad;
|
||||
case Input::HotkeyPad::QuitPad:
|
||||
return quitHotkeyInputsPad;
|
||||
default:
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
void LoadHotkeyInputs() {
|
||||
const auto hotkey_file = Common::FS::GetUserPath(Common::FS::PathType::UserDir) / "hotkeys.ini";
|
||||
if (!std::filesystem::exists(hotkey_file)) {
|
||||
createHotkeyFile(hotkey_file);
|
||||
}
|
||||
|
||||
std::string controllerFullscreenString, controllerPauseString, controllerFpsString,
|
||||
controllerQuitString = "";
|
||||
std::ifstream file(hotkey_file);
|
||||
int lineCount = 0;
|
||||
std::string line = "";
|
||||
|
||||
while (std::getline(file, line)) {
|
||||
lineCount++;
|
||||
|
||||
std::size_t equal_pos = line.find('=');
|
||||
if (equal_pos == std::string::npos)
|
||||
continue;
|
||||
|
||||
if (line.contains("controllerFullscreen")) {
|
||||
controllerFullscreenString = line.substr(equal_pos + 2);
|
||||
} else if (line.contains("controllerQuit")) {
|
||||
controllerQuitString = line.substr(equal_pos + 2);
|
||||
} else if (line.contains("controllerFps")) {
|
||||
controllerFpsString = line.substr(equal_pos + 2);
|
||||
} else if (line.contains("controllerPause")) {
|
||||
controllerPauseString = line.substr(equal_pos + 2);
|
||||
}
|
||||
}
|
||||
|
||||
file.close();
|
||||
|
||||
auto getVectorFromString = [&](std::vector<std::string>& inputVector,
|
||||
const std::string& inputString) {
|
||||
std::size_t comma_pos = inputString.find(',');
|
||||
if (comma_pos == std::string::npos) {
|
||||
inputVector[0] = inputString;
|
||||
inputVector[1] = "unused";
|
||||
inputVector[2] = "unused";
|
||||
} else {
|
||||
inputVector[0] = inputString.substr(0, comma_pos);
|
||||
std::string substring = inputString.substr(comma_pos + 1);
|
||||
std::size_t comma2_pos = substring.find(',');
|
||||
|
||||
if (comma2_pos == std::string::npos) {
|
||||
inputVector[1] = substring;
|
||||
inputVector[2] = "unused";
|
||||
} else {
|
||||
inputVector[1] = substring.substr(0, comma2_pos);
|
||||
inputVector[2] = substring.substr(comma2_pos + 1);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
getVectorFromString(fullscreenHotkeyInputsPad, controllerFullscreenString);
|
||||
getVectorFromString(quitHotkeyInputsPad, controllerQuitString);
|
||||
getVectorFromString(pauseHotkeyInputsPad, controllerPauseString);
|
||||
getVectorFromString(simpleFpsHotkeyInputsPad, controllerFpsString);
|
||||
}
|
||||
|
||||
bool HotkeyInputsPressed(std::vector<std::string> inputs) {
|
||||
if (inputs[0] == "unmapped") {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto controller = Common::Singleton<Input::GameController>::Instance();
|
||||
auto engine = controller->GetEngine();
|
||||
SDL_Gamepad* gamepad = engine->m_gamepad;
|
||||
|
||||
if (!gamepad) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<bool> isPressed(3, false);
|
||||
for (int i = 0; i < 3; i++) {
|
||||
if (inputs[i] == "cross") {
|
||||
isPressed[i] = SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_SOUTH);
|
||||
} else if (inputs[i] == "circle") {
|
||||
isPressed[i] = SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_EAST);
|
||||
} else if (inputs[i] == "square") {
|
||||
isPressed[i] = SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_WEST);
|
||||
} else if (inputs[i] == "triangle") {
|
||||
isPressed[i] = SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_NORTH);
|
||||
} else if (inputs[i] == "pad_up") {
|
||||
isPressed[i] = SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_DPAD_UP);
|
||||
} else if (inputs[i] == "pad_down") {
|
||||
isPressed[i] = SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_DPAD_DOWN);
|
||||
} else if (inputs[i] == "pad_left") {
|
||||
isPressed[i] = SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_DPAD_LEFT);
|
||||
} else if (inputs[i] == "pad_right") {
|
||||
isPressed[i] = SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_DPAD_RIGHT);
|
||||
} else if (inputs[i] == "l1") {
|
||||
isPressed[i] = SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_LEFT_SHOULDER);
|
||||
} else if (inputs[i] == "r1") {
|
||||
isPressed[i] = SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER);
|
||||
} else if (inputs[i] == "l3") {
|
||||
isPressed[i] = SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_LEFT_STICK);
|
||||
} else if (inputs[i] == "r3") {
|
||||
isPressed[i] = SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_RIGHT_STICK);
|
||||
} else if (inputs[i] == "options") {
|
||||
isPressed[i] = SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_START);
|
||||
} else if (inputs[i] == "back") {
|
||||
isPressed[i] = SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_BACK);
|
||||
} else if (inputs[i] == "l2") {
|
||||
isPressed[i] = (SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_LEFT_TRIGGER) > 16000);
|
||||
} else if (inputs[i] == "r2") {
|
||||
isPressed[i] = (SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_RIGHT_TRIGGER) > 16000);
|
||||
} else if (inputs[i] == "unused") {
|
||||
isPressed[i] = true;
|
||||
} else {
|
||||
isPressed[i] = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (isPressed[0] && isPressed[1] && isPressed[2]) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void createHotkeyFile(std::filesystem::path hotkey_file) {
|
||||
std::string_view default_hotkeys = R"(controllerStop = unmapped
|
||||
controllerFps = l2,r2,r3
|
||||
controllerPause = l2,r2,options
|
||||
controllerFullscreen = l2,r2,l3
|
||||
|
||||
keyboardStop = placeholder
|
||||
keyboardFps = placeholder
|
||||
keyboardPause = placeholder
|
||||
keyboardFullscreen = placeholder
|
||||
)";
|
||||
|
||||
std::ofstream default_hotkeys_stream(hotkey_file);
|
||||
if (default_hotkeys_stream) {
|
||||
default_hotkeys_stream << default_hotkeys;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Input
|
||||
|
|
|
@ -4,9 +4,11 @@
|
|||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <filesystem>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
#include "SDL3/SDL_events.h"
|
||||
#include "SDL3/SDL_timer.h"
|
||||
|
@ -448,10 +450,16 @@ public:
|
|||
InputEvent ProcessBinding();
|
||||
};
|
||||
|
||||
enum HotkeyPad { FullscreenPad, PausePad, SimpleFpsPad, QuitPad };
|
||||
|
||||
// Updates the list of pressed keys with the given input.
|
||||
// Returns whether the list was updated or not.
|
||||
bool UpdatePressedKeys(InputEvent event);
|
||||
|
||||
void ActivateOutputsFromInputs();
|
||||
void LoadHotkeyInputs();
|
||||
bool HotkeyInputsPressed(std::vector<std::string> inputs);
|
||||
std::vector<std::string> GetHotkeyInputs(Input::HotkeyPad hotkey);
|
||||
void createHotkeyFile(std::filesystem::path hotkey_file);
|
||||
|
||||
} // namespace Input
|
||||
|
|
392
src/qt_gui/hotkeys.cpp
Normal file
392
src/qt_gui/hotkeys.cpp
Normal file
|
@ -0,0 +1,392 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <fstream>
|
||||
#include <QKeyEvent>
|
||||
#include <QtConcurrent>
|
||||
#include <SDL3/SDL.h>
|
||||
|
||||
#include "common/config.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/path_util.h"
|
||||
#include "hotkeys.h"
|
||||
#include "input/input_handler.h"
|
||||
#include "ui_hotkeys.h"
|
||||
|
||||
hotkeys::hotkeys(bool isGameRunning, QWidget* parent)
|
||||
: QDialog(parent), GameRunning(isGameRunning), ui(new Ui::hotkeys) {
|
||||
|
||||
ui->setupUi(this);
|
||||
installEventFilter(this);
|
||||
|
||||
if (!GameRunning) {
|
||||
SDL_InitSubSystem(SDL_INIT_GAMEPAD);
|
||||
SDL_InitSubSystem(SDL_INIT_EVENTS);
|
||||
} else {
|
||||
SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1");
|
||||
}
|
||||
|
||||
LoadHotkeys();
|
||||
CheckGamePad();
|
||||
|
||||
ButtonsList = {
|
||||
ui->fpsButtonPad,
|
||||
ui->quitButtonPad,
|
||||
ui->fullscreenButtonPad,
|
||||
ui->pauseButtonPad,
|
||||
};
|
||||
|
||||
ui->buttonBox->button(QDialogButtonBox::Save)->setText(tr("Save"));
|
||||
ui->buttonBox->button(QDialogButtonBox::Apply)->setText(tr("Apply"));
|
||||
ui->buttonBox->button(QDialogButtonBox::Cancel)->setText(tr("Cancel"));
|
||||
|
||||
connect(ui->buttonBox, &QDialogButtonBox::clicked, this, [this](QAbstractButton* button) {
|
||||
if (button == ui->buttonBox->button(QDialogButtonBox::Save)) {
|
||||
SaveHotkeys(true);
|
||||
} else if (button == ui->buttonBox->button(QDialogButtonBox::Apply)) {
|
||||
SaveHotkeys(false);
|
||||
} else if (button == ui->buttonBox->button(QDialogButtonBox::Cancel)) {
|
||||
QWidget::close();
|
||||
}
|
||||
});
|
||||
|
||||
for (auto& button : ButtonsList) {
|
||||
connect(button, &QPushButton::clicked, this,
|
||||
[this, &button]() { StartTimer(button, true); });
|
||||
}
|
||||
|
||||
connect(this, &hotkeys::PushGamepadEvent, this, [this]() { CheckMapping(MappingButton); });
|
||||
|
||||
SdlEventWrapper::Wrapper::wrapperActive = true;
|
||||
QObject::connect(SdlEventWrapper::Wrapper::GetInstance(), &SdlEventWrapper::Wrapper::SDLEvent,
|
||||
this, &hotkeys::processSDLEvents);
|
||||
|
||||
if (!GameRunning) {
|
||||
Polling = QtConcurrent::run(&hotkeys::pollSDLEvents, this);
|
||||
}
|
||||
}
|
||||
|
||||
void hotkeys::DisableMappingButtons() {
|
||||
for (const auto& i : ButtonsList) {
|
||||
i->setEnabled(false);
|
||||
}
|
||||
}
|
||||
|
||||
void hotkeys::EnableMappingButtons() {
|
||||
for (const auto& i : ButtonsList) {
|
||||
i->setEnabled(true);
|
||||
}
|
||||
}
|
||||
|
||||
void hotkeys::SaveHotkeys(bool CloseOnSave) {
|
||||
const auto hotkey_file = Common::FS::GetUserPath(Common::FS::PathType::UserDir) / "hotkeys.ini";
|
||||
if (!std::filesystem::exists(hotkey_file)) {
|
||||
Input::createHotkeyFile(hotkey_file);
|
||||
}
|
||||
|
||||
QString controllerFullscreenString, controllerPauseString, controllerFpsString,
|
||||
controllerQuitString = "";
|
||||
std::ifstream file(hotkey_file);
|
||||
int lineCount = 0;
|
||||
std::string line = "";
|
||||
std::vector<std::string> lines;
|
||||
|
||||
while (std::getline(file, line)) {
|
||||
lineCount++;
|
||||
|
||||
std::size_t equal_pos = line.find('=');
|
||||
if (equal_pos == std::string::npos) {
|
||||
lines.push_back(line);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (line.contains("controllerFullscreen")) {
|
||||
line = "controllerFullscreen = " + ui->fullscreenButtonPad->text().toStdString();
|
||||
} else if (line.contains("controllerQuit")) {
|
||||
line = "controllerQuit = " + ui->quitButtonPad->text().toStdString();
|
||||
} else if (line.contains("controllerFps")) {
|
||||
line = "controllerFps = " + ui->fpsButtonPad->text().toStdString();
|
||||
} else if (line.contains("controllerPause")) {
|
||||
line = "controllerPause = " + ui->pauseButtonPad->text().toStdString();
|
||||
}
|
||||
|
||||
lines.push_back(line);
|
||||
}
|
||||
|
||||
file.close();
|
||||
|
||||
std::ofstream output_file(hotkey_file);
|
||||
for (auto const& line : lines) {
|
||||
output_file << line << '\n';
|
||||
}
|
||||
output_file.close();
|
||||
|
||||
Input::LoadHotkeyInputs();
|
||||
|
||||
if (CloseOnSave)
|
||||
QWidget::close();
|
||||
}
|
||||
|
||||
void hotkeys::LoadHotkeys() {
|
||||
const auto hotkey_file = Common::FS::GetUserPath(Common::FS::PathType::UserDir) / "hotkeys.ini";
|
||||
if (!std::filesystem::exists(hotkey_file)) {
|
||||
Input::createHotkeyFile(hotkey_file);
|
||||
}
|
||||
|
||||
QString controllerFullscreenString, controllerPauseString, controllerFpsString,
|
||||
controllerQuitString = "";
|
||||
std::ifstream file(hotkey_file);
|
||||
int lineCount = 0;
|
||||
std::string line = "";
|
||||
|
||||
while (std::getline(file, line)) {
|
||||
lineCount++;
|
||||
|
||||
std::size_t equal_pos = line.find('=');
|
||||
if (equal_pos == std::string::npos)
|
||||
continue;
|
||||
|
||||
if (line.contains("controllerFullscreen")) {
|
||||
controllerFullscreenString = QString::fromStdString(line.substr(equal_pos + 2));
|
||||
} else if (line.contains("controllerQuit")) {
|
||||
controllerQuitString = QString::fromStdString(line.substr(equal_pos + 2));
|
||||
} else if (line.contains("controllerFps")) {
|
||||
controllerFpsString = QString::fromStdString(line.substr(equal_pos + 2));
|
||||
} else if (line.contains("controllerPause")) {
|
||||
controllerPauseString = QString::fromStdString(line.substr(equal_pos + 2));
|
||||
}
|
||||
}
|
||||
|
||||
file.close();
|
||||
|
||||
ui->fpsButtonPad->setText(controllerFpsString);
|
||||
ui->quitButtonPad->setText(controllerQuitString);
|
||||
ui->fullscreenButtonPad->setText(controllerFullscreenString);
|
||||
ui->pauseButtonPad->setText(controllerPauseString);
|
||||
}
|
||||
|
||||
void hotkeys::CheckGamePad() {
|
||||
if (h_gamepad) {
|
||||
SDL_CloseGamepad(h_gamepad);
|
||||
h_gamepad = nullptr;
|
||||
}
|
||||
|
||||
h_gamepads = SDL_GetGamepads(&gamepad_count);
|
||||
|
||||
if (!h_gamepads) {
|
||||
LOG_ERROR(Input, "Cannot get gamepad list: {}", SDL_GetError());
|
||||
}
|
||||
|
||||
int defaultIndex = GamepadSelect::GetIndexfromGUID(h_gamepads, gamepad_count,
|
||||
Config::getDefaultControllerID());
|
||||
int activeIndex = GamepadSelect::GetIndexfromGUID(h_gamepads, gamepad_count,
|
||||
GamepadSelect::GetSelectedGamepad());
|
||||
|
||||
if (!GameRunning) {
|
||||
if (activeIndex != -1) {
|
||||
h_gamepad = SDL_OpenGamepad(h_gamepads[activeIndex]);
|
||||
} else if (defaultIndex != -1) {
|
||||
h_gamepad = SDL_OpenGamepad(h_gamepads[defaultIndex]);
|
||||
} else {
|
||||
LOG_INFO(Input, "Got {} gamepads. Opening the first one.", gamepad_count);
|
||||
h_gamepad = SDL_OpenGamepad(h_gamepads[0]);
|
||||
}
|
||||
|
||||
if (!h_gamepad) {
|
||||
LOG_ERROR(Input, "Failed to open gamepad: {}", SDL_GetError());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void hotkeys::StartTimer(QPushButton*& button, bool isButton) {
|
||||
MappingTimer = 3;
|
||||
EnableButtonMapping = true;
|
||||
MappingCompleted = false;
|
||||
L2Pressed = false;
|
||||
R2Pressed = false;
|
||||
mapping = button->text();
|
||||
DisableMappingButtons();
|
||||
|
||||
button->setText(tr("Press a button") + " [" + QString::number(MappingTimer) + "]");
|
||||
|
||||
timer = new QTimer(this);
|
||||
MappingButton = button;
|
||||
timer->start(1000);
|
||||
connect(timer, &QTimer::timeout, this, [this]() { CheckMapping(MappingButton); });
|
||||
}
|
||||
|
||||
void hotkeys::CheckMapping(QPushButton*& button) {
|
||||
MappingTimer -= 1;
|
||||
button->setText(tr("Press a button") + " [" + QString::number(MappingTimer) + "]");
|
||||
|
||||
if (pressedButtons.size() > 0) {
|
||||
QStringList keyStrings;
|
||||
|
||||
for (const QString& buttonAction : pressedButtons) {
|
||||
keyStrings << buttonAction;
|
||||
}
|
||||
|
||||
QString combo = keyStrings.join(",");
|
||||
SetMapping(combo);
|
||||
MappingButton->setText(combo);
|
||||
pressedButtons.clear();
|
||||
}
|
||||
|
||||
if (MappingCompleted || MappingTimer <= 0) {
|
||||
button->setText(mapping);
|
||||
EnableButtonMapping = false;
|
||||
EnableMappingButtons();
|
||||
timer->stop();
|
||||
}
|
||||
}
|
||||
|
||||
void hotkeys::SetMapping(QString input) {
|
||||
mapping = input;
|
||||
MappingCompleted = true;
|
||||
}
|
||||
|
||||
// use QT events instead of SDL to override default event closing the window with escape
|
||||
bool hotkeys::eventFilter(QObject* obj, QEvent* event) {
|
||||
if (event->type() == QEvent::KeyPress && EnableButtonMapping) {
|
||||
QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event);
|
||||
if (keyEvent->key() == Qt::Key_Escape) {
|
||||
SetMapping("unmapped");
|
||||
PushGamepadEvent();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return QDialog::eventFilter(obj, event);
|
||||
}
|
||||
|
||||
void hotkeys::processSDLEvents(int Type, int Input, int Value) {
|
||||
if (EnableButtonMapping) {
|
||||
|
||||
if (pressedButtons.size() >= 3) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (Type == SDL_EVENT_GAMEPAD_BUTTON_DOWN) {
|
||||
switch (Input) {
|
||||
case SDL_GAMEPAD_BUTTON_SOUTH:
|
||||
pressedButtons.insert(5, "cross");
|
||||
break;
|
||||
case SDL_GAMEPAD_BUTTON_EAST:
|
||||
pressedButtons.insert(6, "circle");
|
||||
break;
|
||||
case SDL_GAMEPAD_BUTTON_NORTH:
|
||||
pressedButtons.insert(7, "triangle");
|
||||
break;
|
||||
case SDL_GAMEPAD_BUTTON_WEST:
|
||||
pressedButtons.insert(8, "square");
|
||||
break;
|
||||
case SDL_GAMEPAD_BUTTON_LEFT_SHOULDER:
|
||||
pressedButtons.insert(3, "l1");
|
||||
break;
|
||||
case SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER:
|
||||
pressedButtons.insert(4, "r1");
|
||||
break;
|
||||
case SDL_GAMEPAD_BUTTON_LEFT_STICK:
|
||||
pressedButtons.insert(9, "l3");
|
||||
break;
|
||||
case SDL_GAMEPAD_BUTTON_RIGHT_STICK:
|
||||
pressedButtons.insert(10, "r3");
|
||||
break;
|
||||
case SDL_GAMEPAD_BUTTON_DPAD_UP:
|
||||
pressedButtons.insert(13, "pad_up");
|
||||
break;
|
||||
case SDL_GAMEPAD_BUTTON_DPAD_DOWN:
|
||||
pressedButtons.insert(14, "pad_down");
|
||||
break;
|
||||
case SDL_GAMEPAD_BUTTON_DPAD_LEFT:
|
||||
pressedButtons.insert(15, "pad_left");
|
||||
break;
|
||||
case SDL_GAMEPAD_BUTTON_DPAD_RIGHT:
|
||||
pressedButtons.insert(16, "pad_right");
|
||||
break;
|
||||
case SDL_GAMEPAD_BUTTON_BACK:
|
||||
pressedButtons.insert(11, "back");
|
||||
break;
|
||||
case SDL_GAMEPAD_BUTTON_START:
|
||||
pressedButtons.insert(12, "options");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (Type == SDL_EVENT_GAMEPAD_AXIS_MOTION) {
|
||||
// SDL trigger axis values range from 0 to 32000, set mapping on half movement
|
||||
// Set zone for trigger release signal arbitrarily at 5000
|
||||
switch (Input) {
|
||||
case SDL_GAMEPAD_AXIS_LEFT_TRIGGER:
|
||||
if (Value > 16000) {
|
||||
pressedButtons.insert(1, "l2");
|
||||
L2Pressed = true;
|
||||
} else if (Value < 5000) {
|
||||
if (L2Pressed && !R2Pressed)
|
||||
emit PushGamepadEvent();
|
||||
}
|
||||
break;
|
||||
case SDL_GAMEPAD_AXIS_RIGHT_TRIGGER:
|
||||
if (Value > 16000) {
|
||||
pressedButtons.insert(2, "r2");
|
||||
R2Pressed = true;
|
||||
} else if (Value < 5000) {
|
||||
if (R2Pressed && !L2Pressed)
|
||||
emit PushGamepadEvent();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (Type == SDL_EVENT_GAMEPAD_BUTTON_UP)
|
||||
emit PushGamepadEvent();
|
||||
}
|
||||
|
||||
if (Type == SDL_EVENT_GAMEPAD_ADDED || SDL_EVENT_GAMEPAD_REMOVED) {
|
||||
CheckGamePad();
|
||||
}
|
||||
}
|
||||
|
||||
void hotkeys::pollSDLEvents() {
|
||||
SDL_Event event;
|
||||
while (SdlEventWrapper::Wrapper::wrapperActive) {
|
||||
|
||||
if (!SDL_WaitEvent(&event)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.type == SDL_EVENT_QUIT) {
|
||||
return;
|
||||
}
|
||||
|
||||
SdlEventWrapper::Wrapper::GetInstance()->Wrapper::ProcessEvent(&event);
|
||||
}
|
||||
}
|
||||
|
||||
void hotkeys::Cleanup() {
|
||||
SdlEventWrapper::Wrapper::wrapperActive = false;
|
||||
if (h_gamepad) {
|
||||
SDL_CloseGamepad(h_gamepad);
|
||||
h_gamepad = nullptr;
|
||||
}
|
||||
|
||||
SDL_free(h_gamepads);
|
||||
|
||||
if (!GameRunning) {
|
||||
SDL_Event quitLoop{};
|
||||
quitLoop.type = SDL_EVENT_QUIT;
|
||||
SDL_PushEvent(&quitLoop);
|
||||
Polling.waitForFinished();
|
||||
|
||||
SDL_QuitSubSystem(SDL_INIT_GAMEPAD);
|
||||
SDL_QuitSubSystem(SDL_INIT_EVENTS);
|
||||
SDL_Quit();
|
||||
} else {
|
||||
SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "0");
|
||||
}
|
||||
}
|
||||
|
||||
hotkeys::~hotkeys() {}
|
62
src/qt_gui/hotkeys.h
Normal file
62
src/qt_gui/hotkeys.h
Normal file
|
@ -0,0 +1,62 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <QDialog>
|
||||
#include <QFuture>
|
||||
#include <SDL3/SDL_gamepad.h>
|
||||
|
||||
#include "sdl_event_wrapper.h"
|
||||
|
||||
namespace Ui {
|
||||
class hotkeys;
|
||||
}
|
||||
|
||||
class hotkeys : public QDialog {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit hotkeys(bool GameRunning, QWidget* parent = nullptr);
|
||||
~hotkeys();
|
||||
|
||||
signals:
|
||||
void PushGamepadEvent();
|
||||
|
||||
private:
|
||||
bool eventFilter(QObject* obj, QEvent* event) override;
|
||||
void CheckMapping(QPushButton*& button);
|
||||
void StartTimer(QPushButton*& button, bool isButton);
|
||||
void DisableMappingButtons();
|
||||
void EnableMappingButtons();
|
||||
void SaveHotkeys(bool CloseOnSave);
|
||||
void LoadHotkeys();
|
||||
void processSDLEvents(int Type, int Input, int Value);
|
||||
void pollSDLEvents();
|
||||
void CheckGamePad();
|
||||
void SetMapping(QString input);
|
||||
void Cleanup();
|
||||
|
||||
bool GameRunning;
|
||||
bool EnableButtonMapping = false;
|
||||
bool MappingCompleted = false;
|
||||
bool L2Pressed = false;
|
||||
bool R2Pressed = false;
|
||||
int MappingTimer;
|
||||
int gamepad_count;
|
||||
QString mapping;
|
||||
QTimer* timer;
|
||||
QPushButton* MappingButton;
|
||||
SDL_Gamepad* h_gamepad = nullptr;
|
||||
SDL_JoystickID* h_gamepads;
|
||||
|
||||
// use QMap instead of QSet to maintain order of inserted strings
|
||||
QMap<int, QString> pressedButtons;
|
||||
QList<QPushButton*> ButtonsList;
|
||||
QFuture<void> Polling;
|
||||
|
||||
Ui::hotkeys* ui;
|
||||
|
||||
protected:
|
||||
void closeEvent(QCloseEvent* event) override {
|
||||
Cleanup();
|
||||
}
|
||||
};
|
313
src/qt_gui/hotkeys.ui
Normal file
313
src/qt_gui/hotkeys.ui
Normal file
|
@ -0,0 +1,313 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
SPDX-License-Identifier: GPL-2.0-or-later -->
|
||||
<ui version="4.0">
|
||||
<class>hotkeys</class>
|
||||
<widget class="QDialog" name="hotkeys">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>849</width>
|
||||
<height>496</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Customize Hotkeys</string>
|
||||
</property>
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>750</x>
|
||||
<y>200</y>
|
||||
<width>81</width>
|
||||
<height>81</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Orientation::Vertical</enum>
|
||||
</property>
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::StandardButton::Apply|QDialogButtonBox::StandardButton::Cancel|QDialogButtonBox::StandardButton::Save</set>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QWidget" name="verticalLayoutWidget">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>30</x>
|
||||
<y>10</y>
|
||||
<width>681</width>
|
||||
<height>231</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="controllerLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="controllerLabel">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>19</pointsize>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Controller Hotkeys</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignmentFlag::AlignCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayoutPad1">
|
||||
<item>
|
||||
<widget class="QGroupBox" name="fpsGroupbox">
|
||||
<property name="title">
|
||||
<string>Show FPS Counter</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<widget class="QPushButton" name="fpsButtonPad">
|
||||
<property name="focusPolicy">
|
||||
<enum>Qt::FocusPolicy::NoFocus</enum>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>unmapped</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="stopGroupbox">
|
||||
<property name="title">
|
||||
<string>Stop Emulator</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_5">
|
||||
<item>
|
||||
<widget class="QPushButton" name="quitButtonPad">
|
||||
<property name="focusPolicy">
|
||||
<enum>Qt::FocusPolicy::NoFocus</enum>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>unmapped</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayoutPad2">
|
||||
<item>
|
||||
<widget class="QGroupBox" name="fullscreenGroupbox">
|
||||
<property name="title">
|
||||
<string>Toggle Fullscreen</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||
<item>
|
||||
<widget class="QPushButton" name="fullscreenButtonPad">
|
||||
<property name="focusPolicy">
|
||||
<enum>Qt::FocusPolicy::NoFocus</enum>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>unmapped</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="pauseGroupbox">
|
||||
<property name="title">
|
||||
<string>Toggle Pause</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_4">
|
||||
<item>
|
||||
<widget class="QPushButton" name="pauseButtonPad">
|
||||
<property name="focusPolicy">
|
||||
<enum>Qt::FocusPolicy::NoFocus</enum>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>unmapped</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="verticalLayoutWidget_2">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>30</x>
|
||||
<y>250</y>
|
||||
<width>681</width>
|
||||
<height>191</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="keyboardLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="keyboardLabel">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>19</pointsize>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Keyboard Hotkeys</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignmentFlag::AlignCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="KBLabelsLayout">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayoutKB1">
|
||||
<item>
|
||||
<widget class="QLabel" name="fpsLabelKB">
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>11</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::Shape::Box</enum>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Show Fps Counter: F10</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="stopLabelKB">
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>11</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::Shape::Box</enum>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Stop Emulator: n/a</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayoutKB2">
|
||||
<item>
|
||||
<widget class="QLabel" name="fullscreenLabelKB">
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>11</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::Shape::Box</enum>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Toggle Fullscreen: F11</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="pauseLabelKB">
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>11</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::Shape::Box</enum>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Toggle Pause: F9</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QLabel" name="tipLabel">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>50</x>
|
||||
<y>450</y>
|
||||
<width>631</width>
|
||||
<height>31</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>12</pointsize>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Tip: Up to three inputs can be assigned for each function</string>
|
||||
</property>
|
||||
</widget>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>accepted()</signal>
|
||||
<receiver>hotkeys</receiver>
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>248</x>
|
||||
<y>254</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>157</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>rejected()</signal>
|
||||
<receiver>hotkeys</receiver>
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>316</x>
|
||||
<y>260</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>286</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
|
@ -20,6 +20,7 @@
|
|||
#include "common/string_util.h"
|
||||
#include "control_settings.h"
|
||||
#include "game_install_dialog.h"
|
||||
#include "hotkeys.h"
|
||||
#include "kbm_gui.h"
|
||||
#include "main_window.h"
|
||||
#include "settings_dialog.h"
|
||||
|
@ -495,6 +496,11 @@ void MainWindow::CreateConnects() {
|
|||
aboutDialog->exec();
|
||||
});
|
||||
|
||||
connect(ui->configureHotkeys, &QAction::triggered, this, [this]() {
|
||||
auto hotkeyDialog = new hotkeys(isGameRunning, this);
|
||||
hotkeyDialog->exec();
|
||||
});
|
||||
|
||||
connect(ui->setIconSizeTinyAct, &QAction::triggered, this, [this]() {
|
||||
if (isTableList) {
|
||||
m_game_list_frame->icon_size =
|
||||
|
|
|
@ -32,6 +32,7 @@ public:
|
|||
#endif
|
||||
QAction* aboutAct;
|
||||
QAction* configureAct;
|
||||
QAction* configureHotkeys;
|
||||
QAction* setThemeDark;
|
||||
QAction* setThemeLight;
|
||||
QAction* setThemeGreen;
|
||||
|
@ -155,6 +156,9 @@ public:
|
|||
configureAct = new QAction(MainWindow);
|
||||
configureAct->setObjectName("configureAct");
|
||||
configureAct->setIcon(QIcon(":images/settings_icon.png"));
|
||||
configureHotkeys = new QAction(MainWindow);
|
||||
configureHotkeys->setObjectName("configureHotkeys");
|
||||
configureHotkeys->setIcon(QIcon(":images/hotkey.png"));
|
||||
setThemeDark = new QAction(MainWindow);
|
||||
setThemeDark->setObjectName("setThemeDark");
|
||||
setThemeDark->setCheckable(true);
|
||||
|
@ -330,6 +334,7 @@ public:
|
|||
menuGame_List_Mode->addAction(setlistElfAct);
|
||||
menuSettings->addAction(configureAct);
|
||||
menuSettings->addAction(gameInstallPathAct);
|
||||
menuSettings->addAction(configureHotkeys);
|
||||
menuSettings->addAction(menuUtils->menuAction());
|
||||
menuUtils->addAction(downloadCheatsPatchesAct);
|
||||
menuUtils->addAction(dumpGameListAct);
|
||||
|
@ -355,6 +360,8 @@ public:
|
|||
#endif
|
||||
aboutAct->setText(QCoreApplication::translate("MainWindow", "About shadPS4", nullptr));
|
||||
configureAct->setText(QCoreApplication::translate("MainWindow", "Configure...", nullptr));
|
||||
configureHotkeys->setText(
|
||||
QCoreApplication::translate("MainWindow", "Customize Hotkeys", nullptr));
|
||||
#if QT_CONFIG(tooltip)
|
||||
#endif // QT_CONFIG(tooltip)
|
||||
menuRecent->setTitle(QCoreApplication::translate("MainWindow", "Recent Games", nullptr));
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include "common/config.h"
|
||||
#include "common/elf_info.h"
|
||||
#include "core/debug_state.h"
|
||||
#include "core/devtools/layer.h"
|
||||
#include "core/libraries/kernel/time.h"
|
||||
#include "core/libraries/pad/pad.h"
|
||||
#include "imgui/renderer/imgui_core.h"
|
||||
|
@ -351,6 +352,7 @@ WindowSDL::WindowSDL(s32 width_, s32 height_, Input::GameController* controller_
|
|||
Input::ControllerOutput::SetControllerOutputController(controller);
|
||||
Input::ControllerOutput::LinkJoystickAxes();
|
||||
Input::ParseInputConfig(std::string(Common::ElfInfo::Instance().GameSerial()));
|
||||
Input::LoadHotkeyInputs();
|
||||
}
|
||||
|
||||
WindowSDL::~WindowSDL() = default;
|
||||
|
@ -549,7 +551,6 @@ void WindowSDL::OnKeyboardMouseInput(const SDL_Event* event) {
|
|||
}
|
||||
|
||||
void WindowSDL::OnGamepadEvent(const SDL_Event* event) {
|
||||
|
||||
bool input_down = event->type == SDL_EVENT_GAMEPAD_AXIS_MOTION ||
|
||||
event->type == SDL_EVENT_GAMEPAD_BUTTON_DOWN;
|
||||
Input::InputEvent input_event = Input::InputBinding::GetInputEventFromSDLEvent(*event);
|
||||
|
@ -565,10 +566,55 @@ void WindowSDL::OnGamepadEvent(const SDL_Event* event) {
|
|||
// add/remove it from the list
|
||||
bool inputs_changed = Input::UpdatePressedKeys(input_event);
|
||||
|
||||
// update bindings
|
||||
if (inputs_changed) {
|
||||
// process hotkeys
|
||||
if (event->type == SDL_EVENT_GAMEPAD_BUTTON_UP) {
|
||||
process_hotkeys = true;
|
||||
} else if (event->type == SDL_EVENT_GAMEPAD_BUTTON_DOWN) {
|
||||
if (event->gbutton.timestamp)
|
||||
CheckHotkeys();
|
||||
} else if (event->type == SDL_EVENT_GAMEPAD_AXIS_MOTION) {
|
||||
if (event->gaxis.axis == SDL_GAMEPAD_AXIS_LEFT_TRIGGER ||
|
||||
event->gaxis.axis == SDL_GAMEPAD_AXIS_RIGHT_TRIGGER) {
|
||||
if (event->gaxis.value < 5000) {
|
||||
process_hotkeys = true;
|
||||
} else if (event->gaxis.value > 16000) {
|
||||
CheckHotkeys();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// update bindings
|
||||
Input::ActivateOutputsFromInputs();
|
||||
}
|
||||
}
|
||||
|
||||
void WindowSDL::CheckHotkeys() {
|
||||
if (Input::HotkeyInputsPressed(Input::GetHotkeyInputs(Input::HotkeyPad::FullscreenPad))) {
|
||||
SDL_Event event;
|
||||
SDL_memset(&event, 0, sizeof(event));
|
||||
event.type = SDL_EVENT_TOGGLE_FULLSCREEN;
|
||||
SDL_PushEvent(&event);
|
||||
process_hotkeys = false;
|
||||
}
|
||||
|
||||
if (Input::HotkeyInputsPressed(Input::GetHotkeyInputs(Input::HotkeyPad::PausePad))) {
|
||||
SDL_Event event;
|
||||
SDL_memset(&event, 0, sizeof(event));
|
||||
event.type = SDL_EVENT_TOGGLE_PAUSE;
|
||||
SDL_PushEvent(&event);
|
||||
process_hotkeys = false;
|
||||
}
|
||||
|
||||
if (Input::HotkeyInputsPressed(Input::GetHotkeyInputs(Input::HotkeyPad::SimpleFpsPad))) {
|
||||
Overlay::ToggleSimpleFps();
|
||||
process_hotkeys = false;
|
||||
}
|
||||
|
||||
if (Input::HotkeyInputsPressed(Input::GetHotkeyInputs(Input::HotkeyPad::QuitPad))) {
|
||||
Overlay::ToggleQuitWindow();
|
||||
process_hotkeys = false;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Frontend
|
||||
|
|
|
@ -3,10 +3,12 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "common/types.h"
|
||||
#include "core/libraries/pad/pad.h"
|
||||
#include "input/controller.h"
|
||||
#include "string"
|
||||
|
||||
#define SDL_EVENT_TOGGLE_FULLSCREEN (SDL_EVENT_USER + 1)
|
||||
#define SDL_EVENT_TOGGLE_PAUSE (SDL_EVENT_USER + 2)
|
||||
#define SDL_EVENT_CHANGE_CONTROLLER (SDL_EVENT_USER + 3)
|
||||
|
@ -98,6 +100,7 @@ private:
|
|||
void OnResize();
|
||||
void OnKeyboardMouseInput(const SDL_Event* event);
|
||||
void OnGamepadEvent(const SDL_Event* event);
|
||||
void CheckHotkeys();
|
||||
|
||||
private:
|
||||
s32 width;
|
||||
|
@ -107,6 +110,7 @@ private:
|
|||
SDL_Window* window{};
|
||||
bool is_shown{};
|
||||
bool is_open{true};
|
||||
bool process_hotkeys{true};
|
||||
};
|
||||
|
||||
} // namespace Frontend
|
||||
|
|
|
@ -38,5 +38,6 @@
|
|||
<file>images/refreshlist_icon.png</file>
|
||||
<file>images/favorite_icon.png</file>
|
||||
<file>images/trophy_icon.png</file>
|
||||
<file>images/hotkey.png</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue