Qt: Add custom pad configs

Add a custom pad config for every game.
This commit is contained in:
gidan80 2018-11-20 16:35:06 +01:00 committed by Megamouse
parent cb78522620
commit 9aa08313e3
17 changed files with 332 additions and 105 deletions

View File

@ -1,4 +1,4 @@
#pragma once
#pragma once
#include "Utilities/types.h"
#include "Utilities/mutex.h"
@ -633,8 +633,8 @@ public:
}
// Emplace the object returned by provider() and return it if no object exists
template <typename T, typename F>
static auto import(F&& provider) -> decltype(static_cast<std::shared_ptr<T>>(provider()))
template <typename T, typename F, typename... Args>
static auto import(F&& provider, Args&&... args) -> decltype(static_cast<std::shared_ptr<T>>(provider(std::forward<Args>(args)...)))
{
std::shared_ptr<T> ptr;
{
@ -644,7 +644,7 @@ public:
if (!cur)
{
ptr = provider();
ptr = provider(std::forward<Args>(args)...);
if (ptr)
{
@ -662,8 +662,8 @@ public:
}
// Emplace the object return by provider() (old object will be removed if it exists)
template <typename T, typename F>
static auto import_always(F&& provider) -> decltype(static_cast<std::shared_ptr<T>>(provider()))
template <typename T, typename F, typename... Args>
static auto import_always(F&& provider, Args&&... args) -> decltype(static_cast<std::shared_ptr<T>>(provider(std::forward<Args>(args)...)))
{
std::shared_ptr<T> ptr;
std::shared_ptr<void> old;
@ -672,7 +672,7 @@ public:
auto& cur = g_vec[get_type<T>()];
ptr = provider();
ptr = provider(std::forward<Args>(args)...);
if (ptr)
{

View File

@ -1,5 +1,6 @@
#include "stdafx.h"
#include "PadHandler.h"
#include "pad_thread.h"
cfg_input g_cfg_input;
@ -268,13 +269,25 @@ bool PadHandlerBase::has_led()
return b_has_led;
}
std::string PadHandlerBase::get_config_dir(pad_handler type)
std::string PadHandlerBase::get_config_dir(pad_handler type, const std::string& title_id)
{
if (!title_id.empty())
{
return Emu.GetCustomInputConfigDir(title_id) + fmt::format("%s", type) + "/";
}
return fs::get_config_dir() + "/InputConfigs/" + fmt::format("%s", type) + "/";
}
std::string PadHandlerBase::get_config_filename(int i)
std::string PadHandlerBase::get_config_filename(int i, const std::string& title_id)
{
if (!title_id.empty() && fs::is_file(Emu.GetCustomInputConfigPath(title_id)))
{
const std::string path = Emu.GetCustomInputConfigDir(title_id) + g_cfg_input.player[i]->handler.to_string() + "/" + g_cfg_input.player[i]->profile.to_string() + ".yml";
if (fs::is_file(path))
{
return path;
}
}
return fs::get_config_dir() + "/InputConfigs/" + g_cfg_input.player[i]->handler.to_string() + "/" + g_cfg_input.player[i]->profile.to_string() + ".yml";
}
@ -286,7 +299,7 @@ void PadHandlerBase::init_configs()
{
if (g_cfg_input.player[i]->handler == m_type)
{
init_config(&m_pad_configs[index], get_config_filename(i));
init_config(&m_pad_configs[index], get_config_filename(i, pad::g_title_id));
index++;
}
}

View File

@ -7,6 +7,7 @@
#include "Utilities/Config.h"
#include "Utilities/types.h"
#include "Emu/System.h"
#include "Emu/GameInfo.h"
// TODO: HLE info (constants, structs, etc.) should not be available here
@ -268,7 +269,7 @@ struct cfg_player final : cfg::node
struct cfg_input final : cfg::node
{
const std::string cfg_name = fs::get_config_dir() + "/config_input.yml";
std::string cfg_name = fs::get_config_dir() + "/config_input.yml";
cfg_player player1{ this, "Player 1 Input", pad_handler::keyboard };
cfg_player player2{ this, "Player 2 Input", pad_handler::null };
@ -280,18 +281,32 @@ struct cfg_input final : cfg::node
cfg_player *player[7]{ &player1, &player2, &player3, &player4, &player5, &player6, &player7 }; // Thanks gcc!
bool load()
bool load(const std::string& title_id = "")
{
cfg_name = Emu.GetCustomInputConfigPath(title_id);
if (!fs::is_file(cfg_name))
{
cfg_name = fs::get_config_dir() + "/config_input.yml";
}
if (fs::file cfg_file{cfg_name, fs::read})
{
return from_string(cfg_file.to_string());
}
return false;
}
void save()
void save(const std::string& title_id = "")
{
if (title_id.empty())
{
cfg_name = fs::get_config_dir() + "/config_input.yml";
}
else
{
cfg_name = Emu.GetCustomInputConfigPath(title_id);
}
fs::file(cfg_name, fs::rewrite).write(to_string());
}
};
@ -460,8 +475,8 @@ public:
bool has_deadzones();
bool has_led();
static std::string get_config_dir(pad_handler type);
static std::string get_config_filename(int i);
static std::string get_config_dir(pad_handler type, const std::string& title_id = "");
static std::string get_config_filename(int i, const std::string& title_id = "");
virtual bool Init() { return true; }
PadHandlerBase(pad_handler type = pad_handler::null);

View File

@ -39,7 +39,6 @@
#include "Utilities/GDBDebugServer.h"
#include "Utilities/sysinfo.h"
#include "Utilities/JIT.h"
#if defined(_WIN32) || defined(HAVE_VULKAN)
@ -506,7 +505,7 @@ bool Emulator::BootRsxCapture(const std::string& path)
GetCallbacks().on_ready();
auto gsrender = fxm::import<GSRender>(Emu.GetCallbacks().get_gs_render);
auto padhandler = fxm::import<pad_thread>(Emu.GetCallbacks().get_pad_handler);
auto padhandler = fxm::import<pad_thread>(Emu.GetCallbacks().get_pad_handler, "");
if (gsrender.get() == nullptr || padhandler.get() == nullptr)
return false;
@ -727,6 +726,22 @@ std::string Emulator::GetCustomConfigPath(const std::string& title_id, bool get_
return path;
}
std::string Emulator::GetCustomInputConfigDir(const std::string& title_id)
{
// Notice: the extra folder for each title id may be removed at a later stage
// Warning: make sure to change any function that removes this directory as well
#ifdef _WIN32
return fs::get_config_dir() + "config/custom_input_configs/" + title_id + "/";
#else
return fs::get_config_dir() + "custom_input_configs/" + title_id + "/";
#endif
}
std::string Emulator::GetCustomInputConfigPath(const std::string& title_id)
{
return GetCustomInputConfigDir(title_id) + "/config_input_" + title_id + ".yml";
}
void Emulator::SetForceBoot(bool force_boot)
{
m_force_boot = force_boot;
@ -1343,7 +1358,7 @@ void Emulator::Load(bool add_only, bool force_global_config)
}
fxm::import<GSRender>(Emu.GetCallbacks().get_gs_render); // TODO: must be created in appropriate sys_rsx syscall
fxm::import<pad_thread>(Emu.GetCallbacks().get_pad_handler);
fxm::import<pad_thread>(Emu.GetCallbacks().get_pad_handler, m_title_id);
network_thread_init();
}
else if (ppu_prx.open(elf_file) == elf_error::ok)

View File

@ -189,12 +189,12 @@ struct EmuCallbacks
std::function<void()> on_stop;
std::function<void()> on_ready;
std::function<void()> exit;
std::function<void()> reset_pads;
std::function<void(const std::string&)> reset_pads;
std::function<void(bool)> enable_pads;
std::function<void(s32, s32)> handle_taskbar_progress; // (type, value) type: 0 for reset, 1 for increment, 2 for set_limit
std::function<std::shared_ptr<class KeyboardHandlerBase>()> get_kb_handler;
std::function<std::shared_ptr<class MouseHandlerBase>()> get_mouse_handler;
std::function<std::shared_ptr<class pad_thread>()> get_pad_handler;
std::function<std::shared_ptr<class pad_thread>(const std::string&)> get_pad_handler;
std::function<std::unique_ptr<class GSFrameBase>()> get_gs_frame;
std::function<std::shared_ptr<class GSRender>()> get_gs_render;
std::function<std::shared_ptr<class AudioBackend>()> get_audio;
@ -326,6 +326,8 @@ public:
static std::string GetCustomConfigDir();
static std::string GetCustomConfigPath(const std::string& title_id, bool get_deprecated_path = false);
static std::string GetCustomInputConfigDir(const std::string& title_id);
static std::string GetCustomInputConfigPath(const std::string& title_id);
void SetForceBoot(bool force_boot);

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

View File

@ -14,6 +14,7 @@ namespace pad
{
atomic_t<pad_thread*> g_current = nullptr;
std::recursive_mutex g_pad_mutex;
std::string g_title_id;
}
struct pad_setting
@ -23,8 +24,9 @@ struct pad_setting
u32 device_type;
};
pad_thread::pad_thread(void *_curthread, void *_curwindow) : curthread(_curthread), curwindow(_curwindow)
pad_thread::pad_thread(void *_curthread, void *_curwindow, const std::string& title_id) : curthread(_curthread), curwindow(_curwindow)
{
pad::g_title_id = title_id;
Init();
thread = std::make_shared<std::thread>(&pad_thread::ThreadFunc, this);
@ -65,7 +67,7 @@ void pad_thread::Init()
handlers.clear();
g_cfg_input.load();
g_cfg_input.load(pad::g_title_id);
std::shared_ptr<keyboard_pad_handler> keyptr;
@ -142,8 +144,9 @@ void pad_thread::SetRumble(const u32 pad, u8 largeMotor, bool smallMotor)
}
}
void pad_thread::Reset()
void pad_thread::Reset(const std::string& title_id)
{
pad::g_title_id = title_id;
reset = active.load();
}

View File

@ -1,4 +1,4 @@
#pragma once
#pragma once
#include <map>
#include <thread>
@ -16,14 +16,14 @@ struct PadInfo
class pad_thread
{
public:
pad_thread(void *_curthread, void *_curwindow); // void * instead of QThread * and QWindow * because of include in emucore
pad_thread(void* _curthread, void* _curwindow, const std::string& title_id = ""); // void * instead of QThread * and QWindow * because of include in emucore
~pad_thread();
PadInfo& GetInfo() { return m_info; }
auto& GetPads() { return m_pads; }
void SetRumble(const u32 pad, u8 largeMotor, bool smallMotor);
void Init();
void Reset();
void Reset(const std::string& title_id = "");
void SetEnabled(bool enabled);
void SetIntercepted(bool intercepted);
@ -50,6 +50,7 @@ namespace pad
{
extern atomic_t<pad_thread*> g_current;
extern std::recursive_mutex g_pad_mutex;
extern std::string g_title_id;
static inline class pad_thread* get_current_handler()
{

View File

@ -18,5 +18,7 @@
<file>Icons/open.png</file>
<file>Icons/custom_config.png</file>
<file>Icons/custom_config_2.png</file>
<file>Icons/controllers_2.png</file>
<file>Icons/combo_config_bordered.png</file>
</qresource>
</RCC>

View File

@ -142,9 +142,9 @@ void rpcs3_app::InitializeCallbacks()
RequestCallAfter(std::move(func));
};
callbacks.reset_pads = [this]()
callbacks.reset_pads = [this](const std::string& title_id = "")
{
pad::get_current_handler()->Reset();
pad::get_current_handler()->Reset(title_id);
};
callbacks.enable_pads = [this](bool enable)
{
@ -183,9 +183,9 @@ void rpcs3_app::InitializeCallbacks()
}
};
callbacks.get_pad_handler = [this]() -> std::shared_ptr<pad_thread>
callbacks.get_pad_handler = [this](const std::string& title_id) -> std::shared_ptr<pad_thread>
{
return std::make_shared<pad_thread>(thread(), gameWindow);
return std::make_shared<pad_thread>(thread(), gameWindow, title_id);
};
callbacks.get_gs_frame = [this]() -> std::unique_ptr<GSFrameBase>

View File

@ -1,6 +1,7 @@
#include "game_list_frame.h"
#include "qt_utils.h"
#include "settings_dialog.h"
#include "pad_settings_dialog.h"
#include "table_item_delegate.h"
#include "custom_table_widget_item.h"
#include "input_dialog.h"
@ -114,6 +115,10 @@ game_list_frame::game_list_frame(std::shared_ptr<gui_settings> guiSettings, std:
QMenu* configure = new QMenu(this);
configure->addActions(m_columnActs);
configure->exec(mapToGlobal(pos));
QMenu* pad_configure = new QMenu(this);
pad_configure->addActions(m_columnActs);
pad_configure->exec(mapToGlobal(pos));
});
connect(m_xgrid, &QTableWidget::itemDoubleClicked, this, &game_list_frame::doubleClickedSlot);
@ -471,12 +476,14 @@ void game_list_frame::Refresh(const bool fromDrive, const bool scrollAfter)
}
const auto compat = m_game_compat->GetCompatibility(game.serial);
const bool hasCustomConfig = fs::is_file(Emu.GetCustomConfigPath(game.serial)) || fs::is_file(Emu.GetCustomConfigPath(game.serial, true));
const bool hasCustomPadConfig = fs::is_file(Emu.GetCustomInputConfigPath(game.serial));
const QColor color = getGridCompatibilityColor(compat.color);
const QPixmap pxmap = PaintedPixmap(img, hasCustomConfig, color);
const QPixmap pxmap = PaintedPixmap(img, hasCustomConfig, hasCustomPadConfig, color);
m_game_data.push_back(game_info(new gui_game_info{ game, compat, img, pxmap, hasCustomConfig }));
m_game_data.push_back(game_info(new gui_game_info{game, compat, img, pxmap, hasCustomConfig, hasCustomPadConfig}));
}
catch (const std::exception& e)
{
@ -695,6 +702,7 @@ void game_list_frame::ShowContextMenu(const QPoint &pos)
}
myMenu.addAction(boot);
QAction* configure = myMenu.addAction(tr("&Configure"));
QAction* pad_configure = myMenu.addAction(tr("&Configure pads"));
QAction* createPPUCache = myMenu.addAction(tr("&Create PPU Cache"));
myMenu.addSeparator();
QAction* renameTitle = myMenu.addAction(tr("&Rename In Game List"));
@ -709,9 +717,20 @@ void game_list_frame::ShowContextMenu(const QPoint &pos)
QAction* remove_custom_config = remove_menu->addAction(tr("&Remove Custom Configuration"));
connect(remove_custom_config, &QAction::triggered, [=]()
{
if (RemoveCustomConfiguration(currGame.serial, true))
if (RemoveCustomConfiguration(currGame.serial, gameinfo, true))
{
ShowCustomConfigIcon(item, false);
ShowCustomConfigIcon(item, config::type::emu);
}
});
}
if (gameinfo->hasCustomPadConfig)
{
QAction* remove_custom_pad_config = remove_menu->addAction(tr("&Remove Custom Pad Configuration"));
connect(remove_custom_pad_config, &QAction::triggered, [=]()
{
if (RemoveCustomPadConfiguration(currGame.serial, gameinfo, true))
{
ShowCustomConfigIcon(item, config::type::pad);
}
});
}
@ -775,7 +794,33 @@ void game_list_frame::ShowContextMenu(const QPoint &pos)
settings_dialog dlg(m_gui_settings, m_emu_settings, 0, this, &currGame);
if (dlg.exec() == QDialog::Accepted && !gameinfo->hasCustomConfig)
{
ShowCustomConfigIcon(item, true);
gameinfo->hasCustomConfig = true;
ShowCustomConfigIcon(item, config::type::emu);
}
});
connect(pad_configure, &QAction::triggered, [=]
{
if (!Emu.IsStopped())
{
Emu.GetCallbacks().enable_pads(false);
}
pad_settings_dialog dlg(this, &currGame);
connect(&dlg, &QDialog::finished, [this](int/* result*/)
{
if (Emu.IsStopped())
{
return;
}
Emu.GetCallbacks().reset_pads(Emu.GetTitleID());
});
if (dlg.exec() == QDialog::Accepted && !gameinfo->hasCustomPadConfig)
{
gameinfo->hasCustomPadConfig = true;
ShowCustomConfigIcon(item, config::type::pad);
}
if (!Emu.IsStopped())
{
Emu.GetCallbacks().enable_pads(true);
}
});
connect(hide_serial, &QAction::triggered, [=](bool checked)
@ -801,7 +846,7 @@ void game_list_frame::ShowContextMenu(const QPoint &pos)
}
QMessageBox* mb = new QMessageBox(QMessageBox::Question, tr("Confirm %1 Removal").arg(qstr(currGame.category)), tr("Permanently remove %0 from drive?\nPath: %1").arg(name).arg(qstr(currGame.path)), QMessageBox::Yes | QMessageBox::No, this);
mb->setCheckBox(new QCheckBox(tr("Remove caches and custom config")));
mb->setCheckBox(new QCheckBox(tr("Remove caches and custom configs")));
mb->deleteLater();
if (mb->exec() == QMessageBox::Yes)
{
@ -811,6 +856,7 @@ void game_list_frame::ShowContextMenu(const QPoint &pos)
RemovePPUCache(cache_base_dir);
RemoveSPUCache(cache_base_dir);
RemoveCustomConfiguration(currGame.serial);
RemoveCustomPadConfiguration(currGame.serial);
}
fs::remove_all(currGame.path);
m_game_data.erase(std::remove(m_game_data.begin(), m_game_data.end(), gameinfo), m_game_data.end());
@ -933,7 +979,7 @@ bool game_list_frame::CreatePPUCache(const std::string& path)
return success;
}
bool game_list_frame::RemoveCustomConfiguration(const std::string& title_id, bool is_interactive)
bool game_list_frame::RemoveCustomConfiguration(const std::string& title_id, game_info game, bool is_interactive)
{
const std::string config_path_new = Emu.GetCustomConfigPath(title_id);
const std::string config_path_old = Emu.GetCustomConfigPath(title_id, true);
@ -954,6 +1000,10 @@ bool game_list_frame::RemoveCustomConfiguration(const std::string& title_id, boo
}
if (fs::remove_file(path))
{
if (game)
{
game->hasCustomConfig = false;
}
LOG_SUCCESS(GENERAL, "Removed configuration file: %s", path);
}
else
@ -971,6 +1021,41 @@ bool game_list_frame::RemoveCustomConfiguration(const std::string& title_id, boo
return result;
}
bool game_list_frame::RemoveCustomPadConfiguration(const std::string& title_id, game_info game, bool is_interactive)
{
const std::string config_dir = Emu.GetCustomInputConfigDir(title_id);
if (!fs::is_dir(config_dir))
return true;
if (is_interactive && QMessageBox::question(this, tr("Confirm Removal"), (!Emu.IsStopped() && Emu.GetTitleID() == title_id)
? tr("Remove custom pad configuration?\nYour configuration will revert to the global pad settings.")
: tr("Remove custom pad configuration?")) != QMessageBox::Yes)
return true;
if (QDir(qstr(config_dir)).removeRecursively())
{
if (game)
{
game->hasCustomPadConfig = false;
}
if (!Emu.IsStopped() && Emu.GetTitleID() == title_id)
{
Emu.GetCallbacks().enable_pads(false);
Emu.GetCallbacks().reset_pads(title_id);
Emu.GetCallbacks().enable_pads(true);
}
LOG_NOTICE(GENERAL, "Removed pad configuration directory: %s", config_dir);
return true;
}
else if (is_interactive)
{
QMessageBox::warning(this, tr("Warning!"), tr("Failed to completely remove pad configuration directory!"));
LOG_FATAL(GENERAL, "Failed to completely remove pad configuration directory: %s\nError: %s", config_dir, fs::g_tls_error);
}
return false;
}
bool game_list_frame::RemoveShadersCache(const std::string& base_dir, bool is_interactive)
{
if (!fs::is_dir(base_dir))
@ -1260,6 +1345,52 @@ void game_list_frame::BatchRemoveCustomConfigurations()
Refresh(true);
}
void game_list_frame::BatchRemoveCustomPadConfigurations()
{
std::set<std::string> serials;
for (const auto& game : m_game_data)
{
if (game->hasCustomPadConfig && !serials.count(game->info.serial))
{
serials.emplace(game->info.serial);
}
}
const u32 total = serials.size();
if (total == 0)
{
QMessageBox::information(this, tr("Custom Pad Configuration Batch Removal"), tr("No files found"), QMessageBox::Ok);
return;
}
progress_dialog* pdlg = new progress_dialog(tr("Removing all custom pad configurations"), tr("Cancel"), 0, total, this);
pdlg->setWindowTitle(tr("Custom Pad Configuration Batch Removal"));
pdlg->setAutoClose(false);
pdlg->setAutoReset(false);
pdlg->show();
u32 removed = 0;
for (const auto& serial : serials)
{
if (pdlg->wasCanceled())
{
LOG_NOTICE(GENERAL, "Custom Pad Configuration Batch Removal was canceled. %d/%d custom pad configurations cleared", removed, total);
break;
}
QApplication::processEvents();
if (RemoveCustomPadConfiguration(serial))
{
pdlg->SetValue(++removed);
}
}
pdlg->setLabelText(tr("%0/%1 custom pad configurations cleared").arg(removed).arg(total));
pdlg->setCancelButtonText(tr("OK"));
QApplication::beep();
Refresh(true);
}
void game_list_frame::BatchRemoveShaderCaches()
{
std::set<std::string> serials;
@ -1302,7 +1433,7 @@ void game_list_frame::BatchRemoveShaderCaches()
QApplication::beep();
}
QPixmap game_list_frame::PaintedPixmap(const QImage& img, bool paint_config_icon, const QColor& compatibility_color)
QPixmap game_list_frame::PaintedPixmap(const QImage& img, bool paint_config_icon, bool paint_pad_config_icon, const QColor& compatibility_color)
{
const int device_pixel_ratio = devicePixelRatio();
const QSize original_size = img.size();
@ -1318,11 +1449,26 @@ QPixmap game_list_frame::PaintedPixmap(const QImage& img, bool paint_config_icon
painter.drawImage(QPoint(0, 0), img);
}
if (paint_config_icon && !m_isListLayout)
if (!m_isListLayout && (paint_config_icon || paint_pad_config_icon))
{
const int width = original_size.width() * 0.2;
const QPoint origin = QPoint(original_size.width() - width, 0);
QImage custom_config_icon(":/Icons/custom_config_2.png");
QString icon_path;
if (paint_config_icon && paint_pad_config_icon)
{
icon_path = ":/Icons/combo_config_bordered.png";
}
else if (paint_config_icon)
{
icon_path = ":/Icons/custom_config_2.png";
}
else if (paint_pad_config_icon)
{
icon_path = ":/Icons/controllers_2.png";
}
QImage custom_config_icon(icon_path);
custom_config_icon.setDevicePixelRatio(device_pixel_ratio);
painter.drawImage(origin, custom_config_icon.scaled(QSize(width, width) * device_pixel_ratio, Qt::KeepAspectRatio, Qt::TransformationMode::SmoothTransformation));
}
@ -1342,7 +1488,7 @@ QPixmap game_list_frame::PaintedPixmap(const QImage& img, bool paint_config_icon
return QPixmap::fromImage(image.scaled(m_Icon_Size * device_pixel_ratio, Qt::KeepAspectRatio, Qt::TransformationMode::SmoothTransformation));
}
void game_list_frame::ShowCustomConfigIcon(QTableWidgetItem* item, bool enabled)
void game_list_frame::ShowCustomConfigIcon(QTableWidgetItem* item, config::type type)
{
auto game = GetGameInfoFromItem(item);
if (game == nullptr)
@ -1350,25 +1496,23 @@ void game_list_frame::ShowCustomConfigIcon(QTableWidgetItem* item, bool enabled)
return;
}
game->hasCustomConfig = enabled;
const QColor color = getGridCompatibilityColor(game->compat.color);
game->pxmap = PaintedPixmap(game->icon, enabled, color);
if (!m_isListLayout)
{
const QColor color = getGridCompatibilityColor(game->compat.color);
game->pxmap = PaintedPixmap(game->icon, game->hasCustomConfig, game->hasCustomPadConfig, color);
int r = m_xgrid->currentItem()->row(), c = m_xgrid->currentItem()->column();
m_xgrid->addItem(game->pxmap, qstr(game->info.name).simplified(), r, c);
m_xgrid->item(r, c)->setData(gui::game_role, QVariant::fromValue(game));
}
else if (enabled)
{
else if (game->hasCustomConfig && game->hasCustomPadConfig)
m_gameList->item(item->row(), gui::column_name)->setIcon(QIcon(":/Icons/combo_config_bordered.png"));
else if (game->hasCustomConfig)
m_gameList->item(item->row(), gui::column_name)->setIcon(QIcon(":/Icons/custom_config.png"));
}
else if (game->hasCustomPadConfig)
m_gameList->item(item->row(), gui::column_name)->setIcon(QIcon(":/Icons/controllers.png"));
else
{
m_gameList->setItem(item->row(), gui::column_name, new custom_table_widget_item(game->info.name));
}
}
void game_list_frame::ResizeIcons(const int& sliderPos)
{
@ -1395,7 +1539,7 @@ void game_list_frame::RepaintIcons(const bool& fromSettings)
for (auto& game : m_game_data)
{
QColor color = getGridCompatibilityColor(game->compat.color);
game->pxmap = PaintedPixmap(game->icon, game->hasCustomConfig, color);
game->pxmap = PaintedPixmap(game->icon, game->hasCustomConfig, game->hasCustomPadConfig, color);
}
Refresh();
@ -1560,10 +1704,18 @@ int game_list_frame::PopulateGameList()
// Title
custom_table_widget_item* title_item = new custom_table_widget_item(title);
if (game->hasCustomConfig)
if (game->hasCustomConfig && game->hasCustomPadConfig)
{
title_item->setIcon(QIcon(":/Icons/combo_config_bordered.png"));
}
else if (game->hasCustomConfig)
{
title_item->setIcon(QIcon(":/Icons/custom_config.png"));
}
else if (game->hasCustomPadConfig)
{
title_item->setIcon(QIcon(":/Icons/controllers.png"));
}
// Serial
custom_table_widget_item* serial_item = new custom_table_widget_item(game->info.serial);

View File

@ -31,6 +31,15 @@ enum Category
Others,
};
namespace config
{
enum class type
{
emu,
pad
};
}
namespace category // (see PARAM.SFO in psdevwiki.com) TODO: Disc Categories
{
// PS3 bootable
@ -171,6 +180,7 @@ struct gui_game_info
QImage icon;
QPixmap pxmap;
bool hasCustomConfig;
bool hasCustomPadConfig;
};
typedef std::shared_ptr<gui_game_info> game_info;
@ -215,6 +225,7 @@ public Q_SLOTS:
void BatchRemovePPUCaches();
void BatchRemoveSPUCaches();
void BatchRemoveCustomConfigurations();
void BatchRemoveCustomPadConfigurations();
void BatchRemoveShaderCaches();
void SetListMode(const bool& isList);
void SetSearchText(const QString& text);
@ -234,9 +245,9 @@ protected:
void resizeEvent(QResizeEvent *event) override;
bool eventFilter(QObject *object, QEvent *event) override;
private:
QPixmap PaintedPixmap(const QImage& img, bool paint_config_icon = false, const QColor& color = QColor());
QPixmap PaintedPixmap(const QImage& img, bool paint_config_icon = false, bool paint_pad_config_icon = false, const QColor& color = QColor());
QColor getGridCompatibilityColor(const QString& string);
void ShowCustomConfigIcon(QTableWidgetItem* item, bool enabled);
void ShowCustomConfigIcon(QTableWidgetItem* item, config::type type);
void PopulateGameGrid(int maxCols, const QSize& image_size, const QColor& image_color);
bool IsEntryVisible(const game_info& game);
void SortGameList();
@ -244,7 +255,8 @@ private:
int PopulateGameList();
bool SearchMatchesApp(const std::string& name, const std::string& serial);
bool RemoveCustomConfiguration(const std::string& base_dir, bool is_interactive = false);
bool RemoveCustomConfiguration(const std::string& title_id, game_info game = nullptr, bool is_interactive = false);
bool RemoveCustomPadConfiguration(const std::string& title_id, game_info game = nullptr, bool is_interactive = false);
bool RemoveShadersCache(const std::string& base_dir, bool is_interactive = false);
bool RemovePPUCache(const std::string& base_dir, bool is_interactive = false);
bool RemoveSPUCache(const std::string& base_dir, bool is_interactive = false);

View File

@ -1247,6 +1247,7 @@ void main_window::CreateConnects()
connect(ui->batchRemoveSPUCachesAct, &QAction::triggered, m_gameListFrame, &game_list_frame::BatchRemoveSPUCaches);
connect(ui->batchRemoveShaderCachesAct, &QAction::triggered, m_gameListFrame, &game_list_frame::BatchRemoveShaderCaches);
connect(ui->batchRemoveCustomConfigurationsAct, &QAction::triggered, m_gameListFrame, &game_list_frame::BatchRemoveCustomConfigurations);
connect(ui->batchRemoveCustomPadConfigurationsAct, &QAction::triggered, m_gameListFrame, &game_list_frame::BatchRemoveCustomPadConfigurations);
connect(ui->sysPauseAct, &QAction::triggered, this, &main_window::OnPlayOrPause);
connect(ui->sysStopAct, &QAction::triggered, [=]() { Emu.Stop(); });
@ -1282,20 +1283,19 @@ void main_window::CreateConnects()
auto openPadSettings = [this]
{
auto resetPadHandlers = [this](int/* result*/)
{
if (Emu.IsStopped())
{
return;
}
Emu.GetCallbacks().reset_pads();
};
if (!Emu.IsStopped())
{
Emu.GetCallbacks().enable_pads(false);
}
pad_settings_dialog dlg(this);
connect(&dlg, &QDialog::finished, resetPadHandlers);
connect(&dlg, &QDialog::finished, [this](int/* result*/)
{
if (Emu.IsStopped())
{
return;
}
Emu.GetCallbacks().reset_pads(Emu.GetTitleID());
});
dlg.exec();
if (!Emu.IsStopped())
{

View File

@ -141,7 +141,7 @@
<x>0</x>
<y>0</y>
<width>1058</width>
<height>21</height>
<height>22</height>
</rect>
</property>
<property name="contextMenuPolicy">
@ -172,6 +172,7 @@
<addaction name="batchCreatePPUCachesAct"/>
<addaction name="separator"/>
<addaction name="batchRemoveCustomConfigurationsAct"/>
<addaction name="batchRemoveCustomPadConfigurationsAct"/>
<addaction name="batchRemovePPUCachesAct"/>
<addaction name="batchRemoveSPUCachesAct"/>
<addaction name="batchRemoveShaderCachesAct"/>
@ -995,6 +996,11 @@
<string>Remove Shader Caches</string>
</property>
</action>
<action name="batchRemoveCustomPadConfigurationsAct">
<property name="text">
<string>Remove Custom Pad Configurations</string>
</property>
</action>
</widget>
<layoutdefault spacing="6" margin="11"/>
<resources>

View File

@ -1,4 +1,4 @@
#include <QCheckBox>
#include <QCheckBox>
#include <QGroupBox>
#include <QPushButton>
#include <QVBoxLayout>
@ -29,19 +29,13 @@ constexpr auto qstr = QString::fromStdString;
inline bool CreateConfigFile(const QString& dir, const QString& name)
{
QString input_dir = qstr(fs::get_config_dir()) + "/InputConfigs/";
if (!QDir().mkdir(input_dir) && !QDir().exists(input_dir))
{
LOG_ERROR(GENERAL, "Failed to create dir %s", sstr(input_dir));
return false;
}
if (!QDir().mkdir(dir) && !QDir().exists(dir))
if (!QDir().mkpath(dir))
{
LOG_ERROR(GENERAL, "Failed to create dir %s", sstr(dir));
return false;
}
QString filename = dir + name + ".yml";
const QString filename = dir + name + ".yml";
QFile new_file(filename);
if (!new_file.open(QIODevice::WriteOnly))
@ -54,16 +48,25 @@ inline bool CreateConfigFile(const QString& dir, const QString& name)
return true;
};
pad_settings_dialog::pad_settings_dialog(QWidget *parent)
pad_settings_dialog::pad_settings_dialog(QWidget *parent, const GameInfo *game)
: QDialog(parent), ui(new Ui::pad_settings_dialog)
{
ui->setupUi(this);
setWindowTitle(tr("Gamepads Settings"));
// load input config
g_cfg_input.from_default();
if (game)
{
m_title_id = game->serial;
g_cfg_input.load(game->serial);
setWindowTitle(tr("Gamepads Settings: [%0] %1").arg(qstr(game->serial)).arg(qstr(game->name).simplified()));
}
else
{
g_cfg_input.load();
setWindowTitle(tr("Gamepads Settings"));
}
// Create tab widget for 7 players
m_tabs = new QTabWidget;
@ -153,7 +156,7 @@ pad_settings_dialog::pad_settings_dialog(QWidget *parent)
QMessageBox::warning(this, tr("Error"), tr("Please choose a non-existing name"));
continue;
}
if (CreateConfigFile(qstr(PadHandlerBase::get_config_dir(g_cfg_input.player[i]->handler)), friendlyName))
if (CreateConfigFile(qstr(PadHandlerBase::get_config_dir(g_cfg_input.player[i]->handler, m_title_id)), friendlyName))
{
ui->chooseProfile->addItem(friendlyName);
ui->chooseProfile->setCurrentText(friendlyName);
@ -839,6 +842,7 @@ void pad_settings_dialog::OnPadButtonClicked(int id)
void pad_settings_dialog::OnTabChanged(int index)
{
// TODO: Do not save yet! But keep all profile changes until the dialog was saved.
// Save old profile
SaveProfile();
@ -963,7 +967,7 @@ void pad_settings_dialog::ChangeInputType()
}
}
QString profile_dir = qstr(PadHandlerBase::get_config_dir(m_handler->m_type));
QString profile_dir = qstr(PadHandlerBase::get_config_dir(m_handler->m_type, m_title_id));
QStringList profiles = gui::utils::get_dir_entries(QDir(profile_dir), QStringList() << "*.yml");
if (profiles.isEmpty())
@ -1021,7 +1025,7 @@ void pad_settings_dialog::ChangeProfile()
}
// Change handler
const std::string cfg_name = PadHandlerBase::get_config_dir(m_handler->m_type) + m_profile + ".yml";
const std::string cfg_name = PadHandlerBase::get_config_dir(m_handler->m_type, m_title_id) + m_profile + ".yml";
// Adjust to the different pad handlers
switch (m_handler->m_type)
@ -1145,7 +1149,7 @@ void pad_settings_dialog::SaveExit()
g_cfg_input.player[i]->profile.from_default();
}
g_cfg_input.save();
g_cfg_input.save(m_title_id);
QDialog::accept();
}

View File

@ -8,6 +8,7 @@
#include <QTimer>
#include "Emu/Io/PadHandler.h"
#include "Emu/GameInfo.h"
namespace Ui
{
@ -85,7 +86,7 @@ class pad_settings_dialog : public QDialog
const QString Disconnected_suffix = tr(" (disconnected)");
public:
explicit pad_settings_dialog(QWidget *parent = nullptr);
explicit pad_settings_dialog(QWidget *parent = nullptr, const GameInfo *game = nullptr);
~pad_settings_dialog();
private Q_SLOTS:
@ -100,6 +101,7 @@ private Q_SLOTS:
private:
Ui::pad_settings_dialog *ui;
std::string m_title_id;
// TabWidget
QTabWidget* m_tabs;