mirror of
https://github.com/PCSX2/pcsx2.git
synced 2026-01-31 01:15:24 +01:00
Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
05e19470b2 | ||
|
|
b6680e4aca | ||
|
|
f9d70af841 | ||
|
|
7587581d1f | ||
|
|
8f19976c10 | ||
|
|
8567d68433 | ||
|
|
6542301566 | ||
|
|
a359f77cf6 | ||
|
|
4c9a81f3d8 | ||
|
|
9234b493a3 | ||
|
|
f84425b67c | ||
|
|
8a0c1874dd | ||
|
|
fa23628ae2 | ||
|
|
8a594e673d |
@@ -1354,8 +1354,11 @@ static u32 TranslateWin32Attributes(u32 Win32Attributes)
|
||||
}
|
||||
|
||||
static u32 RecursiveFindFiles(const char* origin_path, const char* parent_path, const char* path, const char* pattern,
|
||||
u32 flags, FileSystem::FindResultsArray* results, std::vector<std::string>& visited)
|
||||
u32 flags, FileSystem::FindResultsArray* results, std::vector<std::string>& visited, ProgressCallback* cancel)
|
||||
{
|
||||
if (cancel && cancel->IsCancelled())
|
||||
return 0;
|
||||
|
||||
std::string search_dir;
|
||||
if (path)
|
||||
{
|
||||
@@ -1427,11 +1430,11 @@ static u32 RecursiveFindFiles(const char* origin_path, const char* parent_path,
|
||||
if (parent_path)
|
||||
{
|
||||
const std::string recurse_dir = fmt::format("{}\\{}", parent_path, path);
|
||||
nFiles += RecursiveFindFiles(origin_path, recurse_dir.c_str(), utf8_filename.c_str(), pattern, flags, results, visited);
|
||||
nFiles += RecursiveFindFiles(origin_path, recurse_dir.c_str(), utf8_filename.c_str(), pattern, flags, results, visited, cancel);
|
||||
}
|
||||
else
|
||||
{
|
||||
nFiles += RecursiveFindFiles(origin_path, path, utf8_filename.c_str(), pattern, flags, results, visited);
|
||||
nFiles += RecursiveFindFiles(origin_path, path, utf8_filename.c_str(), pattern, flags, results, visited, cancel);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1494,7 +1497,7 @@ static u32 RecursiveFindFiles(const char* origin_path, const char* parent_path,
|
||||
return nFiles;
|
||||
}
|
||||
|
||||
bool FileSystem::FindFiles(const char* path, const char* pattern, u32 flags, FindResultsArray* results)
|
||||
bool FileSystem::FindFiles(const char* path, const char* pattern, u32 flags, FindResultsArray* results, ProgressCallback* cancel)
|
||||
{
|
||||
// has a path
|
||||
if (path[0] == '\0')
|
||||
@@ -1514,7 +1517,7 @@ bool FileSystem::FindFiles(const char* path, const char* pattern, u32 flags, Fin
|
||||
}
|
||||
|
||||
// enter the recursive function
|
||||
if (RecursiveFindFiles(path, nullptr, nullptr, pattern, flags, results, visited) == 0)
|
||||
if (RecursiveFindFiles(path, nullptr, nullptr, pattern, flags, results, visited, cancel) == 0)
|
||||
return false;
|
||||
|
||||
if (flags & FILESYSTEM_FIND_SORT_BY_NAME)
|
||||
@@ -2046,8 +2049,11 @@ bool FileSystem::DeleteSymbolicLink(const char* path, Error* error)
|
||||
static_assert(sizeof(off_t) == sizeof(s64));
|
||||
|
||||
static u32 RecursiveFindFiles(const char* OriginPath, const char* ParentPath, const char* Path, const char* Pattern,
|
||||
u32 Flags, FileSystem::FindResultsArray* pResults, std::vector<std::string>& visited)
|
||||
u32 Flags, FileSystem::FindResultsArray* pResults, std::vector<std::string>& visited, ProgressCallback* cancel)
|
||||
{
|
||||
if (cancel && cancel->IsCancelled())
|
||||
return 0;
|
||||
|
||||
std::string tempStr;
|
||||
if (Path)
|
||||
{
|
||||
@@ -2118,11 +2124,11 @@ static u32 RecursiveFindFiles(const char* OriginPath, const char* ParentPath, co
|
||||
if (ParentPath)
|
||||
{
|
||||
const std::string recursive_dir = fmt::format("{}/{}", ParentPath, Path);
|
||||
nFiles += RecursiveFindFiles(OriginPath, recursive_dir.c_str(), pDirEnt->d_name, Pattern, Flags, pResults, visited);
|
||||
nFiles += RecursiveFindFiles(OriginPath, recursive_dir.c_str(), pDirEnt->d_name, Pattern, Flags, pResults, visited, cancel);
|
||||
}
|
||||
else
|
||||
{
|
||||
nFiles += RecursiveFindFiles(OriginPath, Path, pDirEnt->d_name, Pattern, Flags, pResults, visited);
|
||||
nFiles += RecursiveFindFiles(OriginPath, Path, pDirEnt->d_name, Pattern, Flags, pResults, visited, cancel);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2177,7 +2183,7 @@ static u32 RecursiveFindFiles(const char* OriginPath, const char* ParentPath, co
|
||||
return nFiles;
|
||||
}
|
||||
|
||||
bool FileSystem::FindFiles(const char* path, const char* pattern, u32 flags, FindResultsArray* results)
|
||||
bool FileSystem::FindFiles(const char* path, const char* pattern, u32 flags, FindResultsArray* results, ProgressCallback* cancel)
|
||||
{
|
||||
// has a path
|
||||
if (path[0] == '\0')
|
||||
@@ -2197,7 +2203,7 @@ bool FileSystem::FindFiles(const char* path, const char* pattern, u32 flags, Fin
|
||||
}
|
||||
|
||||
// enter the recursive function
|
||||
if (RecursiveFindFiles(path, nullptr, nullptr, pattern, flags, results, visited) == 0)
|
||||
if (RecursiveFindFiles(path, nullptr, nullptr, pattern, flags, results, visited, cancel) == 0)
|
||||
return false;
|
||||
|
||||
if (flags & FILESYSTEM_FIND_SORT_BY_NAME)
|
||||
|
||||
@@ -67,7 +67,7 @@ namespace FileSystem
|
||||
std::vector<std::string> GetRootDirectoryList();
|
||||
|
||||
/// Search for files
|
||||
bool FindFiles(const char* path, const char* pattern, u32 flags, FindResultsArray* results);
|
||||
bool FindFiles(const char* path, const char* pattern, u32 flags, FindResultsArray* results, ProgressCallback* cancel = nullptr);
|
||||
|
||||
/// Stat file
|
||||
bool StatFile(const char* path, struct stat* st);
|
||||
|
||||
@@ -453,7 +453,7 @@ std::vector<SearchResult> startWorker(DebugInterface* cpu, const SearchType type
|
||||
isSigned ? searchWorker<s32>(cpu, searchResults, type, comparison, start, end, value.toInt(nullptr, base)) : searchWorker<u32>(cpu, searchResults, type, comparison, start, end, value.toUInt(nullptr, base));
|
||||
break;
|
||||
case SearchType::Int64Type:
|
||||
isSigned ? searchWorker<s64>(cpu, searchResults, type, comparison, start, end, value.toLong(nullptr, base)) : searchWorker<s64>(cpu, searchResults, type, comparison, start, end, value.toULongLong(nullptr, base));
|
||||
isSigned ? searchWorker<s64>(cpu, searchResults, type, comparison, start, end, value.toLongLong(nullptr, base)) : searchWorker<u64>(cpu, searchResults, type, comparison, start, end, value.toULongLong(nullptr, base));
|
||||
break;
|
||||
case SearchType::FloatType:
|
||||
searchWorker<float>(cpu, searchResults, type, comparison, start, end, value.toFloat());
|
||||
|
||||
@@ -99,6 +99,9 @@ static QString s_current_disc_serial;
|
||||
static quint32 s_current_disc_crc;
|
||||
static quint32 s_current_running_crc;
|
||||
|
||||
static bool s_record_on_start = false;
|
||||
static QString s_path_to_recording_for_record_on_start;
|
||||
|
||||
MainWindow::MainWindow()
|
||||
{
|
||||
pxAssert(!g_main_window);
|
||||
@@ -738,7 +741,48 @@ void MainWindow::updateAdvancedSettingsVisibility()
|
||||
void MainWindow::onVideoCaptureToggled(bool checked)
|
||||
{
|
||||
if (!s_vm_valid)
|
||||
{
|
||||
if (!s_record_on_start)
|
||||
{
|
||||
QMessageBox msgbox(this);
|
||||
msgbox.setIcon(QMessageBox::Question);
|
||||
msgbox.setWindowIcon(QtHost::GetAppIcon());
|
||||
msgbox.setWindowTitle(tr("Record On Boot"));
|
||||
msgbox.setWindowModality(Qt::WindowModal);
|
||||
msgbox.setText(tr("Did you want to start recording on boot?"));
|
||||
msgbox.addButton(QMessageBox::Yes);
|
||||
msgbox.addButton(QMessageBox::No);
|
||||
msgbox.setDefaultButton(QMessageBox::Yes);
|
||||
if (msgbox.exec() == QMessageBox::Yes)
|
||||
{
|
||||
const QString container(QString::fromStdString(
|
||||
Host::GetStringSettingValue("EmuCore/GS", "CaptureContainer", Pcsx2Config::GSOptions::DEFAULT_CAPTURE_CONTAINER)));
|
||||
const QString filter(tr("%1 Files (*.%2)").arg(container.toUpper()).arg(container));
|
||||
|
||||
QString temp(QStringLiteral("%1.%2").arg(QString::fromStdString(GSGetBaseVideoFilename())).arg(container));
|
||||
temp = QDir::toNativeSeparators(QFileDialog::getSaveFileName(this, tr("Video Capture"), temp, filter));
|
||||
s_path_to_recording_for_record_on_start = temp;
|
||||
if (s_path_to_recording_for_record_on_start.isEmpty())
|
||||
return;
|
||||
s_record_on_start = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
QMessageBox msgbox(this);
|
||||
msgbox.setIcon(QMessageBox::Question);
|
||||
msgbox.setWindowIcon(QtHost::GetAppIcon());
|
||||
msgbox.setWindowTitle(tr("Record On Boot"));
|
||||
msgbox.setWindowModality(Qt::WindowModal);
|
||||
msgbox.setText(tr("Did you want to cancel recording on boot?"));
|
||||
msgbox.addButton(QMessageBox::Yes);
|
||||
msgbox.addButton(QMessageBox::No);
|
||||
msgbox.setDefaultButton(QMessageBox::Yes);
|
||||
if (msgbox.exec() == QMessageBox::Yes)
|
||||
s_record_on_start = false;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Reset the checked state, we'll get updated by the GS thread.
|
||||
QSignalBlocker sb(m_ui.actionVideoCapture);
|
||||
@@ -750,16 +794,26 @@ void MainWindow::onVideoCaptureToggled(bool checked)
|
||||
return;
|
||||
}
|
||||
|
||||
const QString container(QString::fromStdString(
|
||||
Host::GetStringSettingValue("EmuCore/GS", "CaptureContainer", Pcsx2Config::GSOptions::DEFAULT_CAPTURE_CONTAINER)));
|
||||
const QString filter(tr("%1 Files (*.%2)").arg(container.toUpper()).arg(container));
|
||||
if (s_record_on_start && !s_path_to_recording_for_record_on_start.isEmpty())
|
||||
{
|
||||
// We can't start recording immediately, this is called before full GS init (specifically the fps amount)
|
||||
// and GSCapture ends up unhappy.
|
||||
// TODO: Pass some sort of flag or callback to the GS thread to start recording on frame 0.
|
||||
Host::AddOSDMessage(tr("Recording will start in a moment").toStdString(), 3.0f);
|
||||
QTimer::singleShot(2000, []() { g_emu_thread->beginCapture(s_path_to_recording_for_record_on_start); });
|
||||
}
|
||||
else
|
||||
{
|
||||
const QString container(QString::fromStdString(
|
||||
Host::GetStringSettingValue("EmuCore/GS", "CaptureContainer", Pcsx2Config::GSOptions::DEFAULT_CAPTURE_CONTAINER)));
|
||||
const QString filter(tr("%1 Files (*.%2)").arg(container.toUpper()).arg(container));
|
||||
|
||||
QString path(QStringLiteral("%1.%2").arg(QString::fromStdString(GSGetBaseVideoFilename())).arg(container));
|
||||
path = QDir::toNativeSeparators(QFileDialog::getSaveFileName(this, tr("Video Capture"), path, filter));
|
||||
if (path.isEmpty())
|
||||
return;
|
||||
|
||||
g_emu_thread->beginCapture(path);
|
||||
QString path(QStringLiteral("%1.%2").arg(QString::fromStdString(GSGetBaseVideoFilename())).arg(container));
|
||||
path = QDir::toNativeSeparators(QFileDialog::getSaveFileName(this, tr("Video Capture"), path, filter));
|
||||
if (path.isEmpty())
|
||||
return;
|
||||
g_emu_thread->beginCapture(path);
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::onCaptureStarted(const QString& filename)
|
||||
@@ -904,8 +958,6 @@ void MainWindow::updateEmulationActions(bool starting, bool running, bool stoppi
|
||||
m_ui.actionToolbarSaveState->setEnabled(running);
|
||||
|
||||
m_ui.actionViewGameProperties->setEnabled(running);
|
||||
|
||||
m_ui.actionVideoCapture->setEnabled(running);
|
||||
if (!running && m_ui.actionVideoCapture->isChecked())
|
||||
{
|
||||
QSignalBlocker sb(m_ui.actionVideoCapture);
|
||||
@@ -1991,6 +2043,11 @@ void MainWindow::onVMStarted()
|
||||
updateWindowTitle();
|
||||
updateStatusBarWidgetVisibility();
|
||||
updateInputRecordingActions(true);
|
||||
if (s_record_on_start)
|
||||
{
|
||||
m_ui.actionVideoCapture->setChecked(true);
|
||||
s_record_on_start = false;
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::onVMPaused()
|
||||
|
||||
@@ -1055,7 +1055,12 @@
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Align To Native</string>
|
||||
<string>Align to Native</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Align to Native - with Texture Offset</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include "common/SmallString.h"
|
||||
#include "common/StringUtil.h"
|
||||
|
||||
#include "pcsx2/ImGui/FullscreenUI.h"
|
||||
#include "pcsx2/ImGui/ImGuiManager.h"
|
||||
#include "pcsx2/MTGS.h"
|
||||
|
||||
@@ -191,6 +192,13 @@ void QtHost::InstallTranslator(QWidget* dialog_parent)
|
||||
}
|
||||
|
||||
UpdateGlyphRangesAndClearCache(dialog_parent, language.toStdString());
|
||||
|
||||
if (FullscreenUI::IsInitialized())
|
||||
{
|
||||
MTGS::RunOnGSThread([]() mutable {
|
||||
FullscreenUI::LocaleChanged();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const char* QtHost::GetDefaultLanguage()
|
||||
@@ -220,6 +228,12 @@ std::string Host::TranslatePluralToString(const char* context, const char* msg,
|
||||
return qApp->translate(context, msg, disambiguation, count).toStdString();
|
||||
}
|
||||
|
||||
bool Host::LocaleCircleConfirm()
|
||||
{
|
||||
QLocale& loc = QtHost::s_current_locale;
|
||||
return (loc.language() == QLocale::Japanese) || (loc.language() == QLocale::Chinese) || (loc.language() == QLocale::Korean);
|
||||
}
|
||||
|
||||
std::vector<std::pair<QString, QString>> QtHost::GetAvailableLanguageList()
|
||||
{
|
||||
return {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -440,6 +440,7 @@ enum class GSHalfPixelOffset : u8
|
||||
Special,
|
||||
SpecialAggressive,
|
||||
Native,
|
||||
NativeWTexOffset,
|
||||
MaxCount
|
||||
};
|
||||
|
||||
|
||||
@@ -179,7 +179,7 @@ The clamp modes are also numerically based.
|
||||
* bilinearUpscale [`0` or `1` or `2`] {Automatic, Force Bilinear, Force Nearest} Default: Automatic
|
||||
* skipDrawStart [Value between `0` to `10000`] {0-10000} Default: Off (`0`)
|
||||
* skipDrawEnd [Value between `0` to `10000`] {0-10000} Default: Off (`0`)
|
||||
* halfPixelOffset [`0` or `1` or `2` or `3` or `4`] {Off, Normal Vertex, Special (Texture), Special (Texture Aggressive), Align to Native} Default: Off (`0`)
|
||||
* halfPixelOffset [`0` or `1` or `2` or `3` or `4` or `5`] {Off, Normal Vertex, Special (Texture), Special (Texture Aggressive), Align to Native, Align to Native with Texture Offsets} Default: Off (`0`)
|
||||
* nativeScaling [`0` or `1` or `2`] {Normal, Aggressive or Off} Default: Normal (`0`)
|
||||
* nativePaletteDraw [`0` or `1`] {Off, On} Default: Off (`0`)
|
||||
* roundSprite [`0` or `1` or `2`] {Off, Half or Full} Default: Off (`0`)
|
||||
|
||||
@@ -237,7 +237,7 @@
|
||||
"halfPixelOffset": {
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"maximum": 4
|
||||
"maximum": 5
|
||||
},
|
||||
"nativeScaling": {
|
||||
"type": "integer",
|
||||
|
||||
@@ -732,7 +732,7 @@ void GSRendererHW::ConvertSpriteTextureShuffle(u32& process_rg, u32& process_ba,
|
||||
GSVector4 GSRendererHW::RealignTargetTextureCoordinate(const GSTextureCache::Source* tex)
|
||||
{
|
||||
if (GSConfig.UserHacks_HalfPixelOffset <= GSHalfPixelOffset::Normal ||
|
||||
GSConfig.UserHacks_HalfPixelOffset == GSHalfPixelOffset::Native ||
|
||||
GSConfig.UserHacks_HalfPixelOffset >= GSHalfPixelOffset::Native ||
|
||||
GetUpscaleMultiplier() == 1.0f || m_downscale_source || tex->GetScale() == 1.0f)
|
||||
{
|
||||
return GSVector4(0.0f);
|
||||
@@ -5160,6 +5160,26 @@ __ri void GSRendererHW::EmulateTextureSampler(const GSTextureCache::Target* rt,
|
||||
|
||||
const GSVector4 half_pixel = RealignTargetTextureCoordinate(tex);
|
||||
m_conf.cb_vs.texture_offset = GSVector2(half_pixel.x, half_pixel.y);
|
||||
|
||||
// Can be seen with the cabin part of the ship in God of War, offsets are required when using FST.
|
||||
// ST uses a normalized position so doesn't need an offset here, will break Bionicle Heroes.
|
||||
if (GSConfig.UserHacks_HalfPixelOffset == GSHalfPixelOffset::NativeWTexOffset)
|
||||
{
|
||||
if (!m_downscale_source && tex->m_scale > 1.0f)
|
||||
{
|
||||
const GSVertex* v = &m_vertex.buff[0];
|
||||
if (PRIM->FST)
|
||||
{
|
||||
const int x1_frac = ((v[1].XYZ.X - m_context->XYOFFSET.OFX) & 0xf);
|
||||
const int y1_frac = ((v[1].XYZ.Y - m_context->XYOFFSET.OFY) & 0xf);
|
||||
|
||||
if (!(x1_frac & 8))
|
||||
m_conf.cb_vs.texture_offset.x = 6.0f + (0.25f * tex->m_scale);
|
||||
if (!(y1_frac & 8))
|
||||
m_conf.cb_vs.texture_offset.y = 6.0f + (0.25f * tex->m_scale);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (tex->m_target)
|
||||
{
|
||||
@@ -5211,6 +5231,33 @@ __ri void GSRendererHW::EmulateTextureSampler(const GSTextureCache::Target* rt,
|
||||
const GSVector4 half_pixel = RealignTargetTextureCoordinate(tex);
|
||||
m_conf.cb_vs.texture_offset = GSVector2(half_pixel.x, half_pixel.y);
|
||||
|
||||
if (GSConfig.UserHacks_HalfPixelOffset == GSHalfPixelOffset::NativeWTexOffset)
|
||||
{
|
||||
if (!m_downscale_source && tex->m_scale > 1.0f)
|
||||
{
|
||||
const GSVertex* v = &m_vertex.buff[0];
|
||||
if (PRIM->FST)
|
||||
{
|
||||
const int x1_frac = ((v[1].XYZ.X - m_context->XYOFFSET.OFX) & 0xf);
|
||||
const int y1_frac = ((v[1].XYZ.Y - m_context->XYOFFSET.OFY) & 0xf);
|
||||
|
||||
if (!(x1_frac & 8))
|
||||
m_conf.cb_vs.texture_offset.x = 6.0f + (0.25f * tex->m_scale);
|
||||
if (!(y1_frac & 8))
|
||||
m_conf.cb_vs.texture_offset.y = 6.0f + (0.25f * tex->m_scale);
|
||||
}
|
||||
else
|
||||
{
|
||||
const float tw = static_cast<float>(1 << m_cached_ctx.TEX0.TW);
|
||||
const float th = static_cast<float>(1 << m_cached_ctx.TEX0.TH);
|
||||
const float q = v[0].RGBAQ.Q;
|
||||
|
||||
m_conf.cb_vs.texture_offset.x = 0.5f * q / tw;
|
||||
m_conf.cb_vs.texture_offset.y = 0.5f * q / th;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (m_vt.m_primclass == GS_SPRITE_CLASS && GSLocalMemory::m_psm[m_cached_ctx.TEX0.PSM].pal > 0 && m_index.tail >= 4)
|
||||
{
|
||||
HandleManualDeswizzle();
|
||||
@@ -5540,7 +5587,7 @@ __ri void GSRendererHW::HandleTextureHazards(const GSTextureCache::Target* rt, c
|
||||
// When using native HPO, the top-left column/row of pixels are often not drawn. Clamp these away to avoid sampling black,
|
||||
// causing bleeding into the edges of the downsampled texture.
|
||||
const u32 downsample_factor = static_cast<u32>(src_target->GetScale());
|
||||
const GSVector2i clamp_min = (GSConfig.UserHacks_HalfPixelOffset != GSHalfPixelOffset::Native) ?
|
||||
const GSVector2i clamp_min = (GSConfig.UserHacks_HalfPixelOffset < GSHalfPixelOffset::Native) ?
|
||||
GSVector2i(0, 0) :
|
||||
GSVector2i(downsample_factor, downsample_factor);
|
||||
GSVector4i copy_rect = tmm.coverage;
|
||||
@@ -6272,7 +6319,8 @@ __ri void GSRendererHW::DrawPrims(GSTextureCache::Target* rt, GSTextureCache::Ta
|
||||
float sx, sy, ox2, oy2;
|
||||
const float ox = static_cast<float>(static_cast<int>(m_context->XYOFFSET.OFX));
|
||||
const float oy = static_cast<float>(static_cast<int>(m_context->XYOFFSET.OFY));
|
||||
if (GSConfig.UserHacks_HalfPixelOffset != GSHalfPixelOffset::Native && rtscale > 1.0f)
|
||||
|
||||
if ((GSConfig.UserHacks_HalfPixelOffset < GSHalfPixelOffset::Native || m_channel_shuffle) && rtscale > 1.0f)
|
||||
{
|
||||
sx = 2.0f * rtscale / (rtsize.x << 4);
|
||||
sy = 2.0f * rtscale / (rtsize.y << 4);
|
||||
@@ -6301,8 +6349,31 @@ __ri void GSRendererHW::DrawPrims(GSTextureCache::Target* rt, GSTextureCache::Ta
|
||||
const int unscaled_y = rt_or_ds ? rt_or_ds->GetUnscaledHeight() : 0;
|
||||
sx = 2.0f / (unscaled_x << 4);
|
||||
sy = 2.0f / (unscaled_y << 4);
|
||||
ox2 = -1.0f / unscaled_x;
|
||||
oy2 = -1.0f / unscaled_y;
|
||||
|
||||
if (GSConfig.UserHacks_HalfPixelOffset == GSHalfPixelOffset::NativeWTexOffset)
|
||||
{
|
||||
ox2 = (-1.0f / (unscaled_x * rtscale));
|
||||
oy2 = (-1.0f / (unscaled_y * rtscale));
|
||||
|
||||
// Having the vertex negatively offset is a common thing for copying sprites but this causes problems when upscaling, so we need to further adjust the offset.
|
||||
// This kinda screws things up when using ST, so let's not.
|
||||
if (m_vt.m_primclass == GS_SPRITE_CLASS && rtscale > 1.0f && (!tex || PRIM->FST))
|
||||
{
|
||||
const GSVertex* v = &m_vertex.buff[0];
|
||||
const int x1_frac = ((v[1].XYZ.X - m_context->XYOFFSET.OFX) & 0xf);
|
||||
const int y1_frac = ((v[1].XYZ.Y - m_context->XYOFFSET.OFY) & 0xf);
|
||||
if (x1_frac & 8)
|
||||
ox2 *= 1.0f + ((static_cast<float>(16 - x1_frac) / 8.0f) * rtscale);
|
||||
|
||||
if (y1_frac & 8)
|
||||
oy2 *= 1.0f + ((static_cast<float>(16 - y1_frac) / 8.0f) * rtscale);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ox2 = -1.0f / unscaled_x;
|
||||
oy2 = -1.0f / unscaled_y;
|
||||
}
|
||||
}
|
||||
|
||||
m_conf.cb_vs.vertex_scale = GSVector2(sx, sy);
|
||||
|
||||
@@ -2132,8 +2132,8 @@ GSTextureCache::Target* GSTextureCache::LookupTarget(GIFRegTEX0 TEX0, const GSVe
|
||||
// And invalidate the target, we're drawing over it so we don't care what's there.
|
||||
// We can't do this when upscaling, because of the vertex offset, the top/left rows often aren't drawn.
|
||||
GL_INS("TC: Invalidating%s target %s[%x] because it's completely overwritten.", to_string(type),
|
||||
(scale > 1.0f && GSConfig.UserHacks_HalfPixelOffset == GSHalfPixelOffset::Native) ? "[clearing] " : "", dst->m_TEX0.TBP0);
|
||||
if (scale > 1.0f && GSConfig.UserHacks_HalfPixelOffset != GSHalfPixelOffset::Native)
|
||||
(scale > 1.0f && GSConfig.UserHacks_HalfPixelOffset >= GSHalfPixelOffset::Native) ? "[clearing] " : "", dst->m_TEX0.TBP0);
|
||||
if (scale > 1.0f && GSConfig.UserHacks_HalfPixelOffset < GSHalfPixelOffset::Native)
|
||||
{
|
||||
if (dst->m_type == RenderTarget)
|
||||
g_gs_device->ClearRenderTarget(dst->m_texture, 0);
|
||||
|
||||
@@ -593,15 +593,14 @@ void GameList::ScanDirectory(const char* path, bool recursive, bool only_cache,
|
||||
progress->PushState();
|
||||
progress->SetStatusText(fmt::format(
|
||||
recursive ? TRANSLATE_FS("GameList", "Scanning directory {} (recursively)...") :
|
||||
TRANSLATE_FS("GameList", "Scanning directory {}..."),
|
||||
path)
|
||||
.c_str());
|
||||
TRANSLATE_FS("GameList", "Scanning directory {}..."),
|
||||
path).c_str());
|
||||
|
||||
FileSystem::FindResultsArray files;
|
||||
FileSystem::FindFiles(path, "*",
|
||||
recursive ? (FILESYSTEM_FIND_FILES | FILESYSTEM_FIND_HIDDEN_FILES | FILESYSTEM_FIND_RECURSIVE) :
|
||||
(FILESYSTEM_FIND_FILES | FILESYSTEM_FIND_HIDDEN_FILES),
|
||||
&files);
|
||||
(FILESYSTEM_FIND_FILES | FILESYSTEM_FIND_HIDDEN_FILES),
|
||||
&files, progress);
|
||||
|
||||
u32 files_scanned = 0;
|
||||
progress->SetProgressRange(static_cast<u32>(files.size()));
|
||||
|
||||
@@ -237,6 +237,7 @@ namespace FullscreenUI
|
||||
static void DrawAboutWindow();
|
||||
static void OpenAboutWindow();
|
||||
static void GetStandardSelectionFooterText(SmallStringBase& dest, bool back_instead_of_cancel);
|
||||
static void ApplyConfirmSetting(const SettingsInterface* bsi = nullptr);
|
||||
|
||||
static MainWindowType s_current_main_window = MainWindowType::None;
|
||||
static PauseSubMenu s_current_pause_submenu = PauseSubMenu::None;
|
||||
@@ -570,6 +571,64 @@ void ImGuiFullscreen::GetInputDialogHelpText(SmallStringBase& dest)
|
||||
}
|
||||
}
|
||||
|
||||
void FullscreenUI::ApplyConfirmSetting(const SettingsInterface* bsi)
|
||||
{
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
SmallString swap_mode;
|
||||
if (bsi)
|
||||
swap_mode = bsi->GetSmallStringValue("UI", "SwapOKFullscreenUI", "auto");
|
||||
else
|
||||
swap_mode = Host::GetBaseSmallStringSettingValue("UI", "SwapOKFullscreenUI", "auto");
|
||||
|
||||
if (swap_mode == "true")
|
||||
io.ConfigNavSwapGamepadButtons = true;
|
||||
else if (swap_mode == "false")
|
||||
io.ConfigNavSwapGamepadButtons = false;
|
||||
else if (swap_mode == "auto")
|
||||
{
|
||||
// Check language
|
||||
if (Host::LocaleCircleConfirm())
|
||||
{
|
||||
io.ConfigNavSwapGamepadButtons = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// Check BIOS
|
||||
SmallString bios_selection;
|
||||
if (bsi)
|
||||
bios_selection = bsi->GetSmallStringValue("Filenames", "BIOS", "");
|
||||
else
|
||||
bios_selection = Host::GetBaseSmallStringSettingValue("Filenames", "BIOS", "");
|
||||
|
||||
if (bios_selection != "")
|
||||
{
|
||||
u32 bios_version, bios_region;
|
||||
std::string bios_description, bios_zone;
|
||||
if (IsBIOS(Path::Combine(EmuFolders::Bios, bios_selection).c_str(), bios_version, bios_description, bios_region, bios_zone))
|
||||
{
|
||||
// Japan, Asia, China
|
||||
if (bios_region == 0 || bios_region == 4 || bios_region == 6)
|
||||
{
|
||||
io.ConfigNavSwapGamepadButtons = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// X is confirm
|
||||
io.ConfigNavSwapGamepadButtons = false;
|
||||
return;
|
||||
}
|
||||
// Invalid setting
|
||||
else
|
||||
io.ConfigNavSwapGamepadButtons = false;
|
||||
}
|
||||
|
||||
void FullscreenUI::LocaleChanged()
|
||||
{
|
||||
ApplyConfirmSetting();
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Main
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
@@ -584,7 +643,7 @@ bool FullscreenUI::Initialize()
|
||||
|
||||
ImGuiFullscreen::SetTheme(Host::GetBaseBoolSettingValue("UI", "UseLightFullscreenUITheme", false));
|
||||
ImGuiFullscreen::UpdateLayoutScale();
|
||||
ImGui::GetIO().ConfigNavSwapGamepadButtons = Host::GetBaseBoolSettingValue("UI", "SwapOKFullscreenUI");
|
||||
ApplyConfirmSetting();
|
||||
|
||||
if (!ImGuiManager::AddFullscreenFontsIfMissing() || !ImGuiFullscreen::Initialize("fullscreenui/placeholder.png") || !LoadResources())
|
||||
{
|
||||
@@ -649,6 +708,13 @@ void FullscreenUI::CheckForConfigChanges(const Pcsx2Config& old_config)
|
||||
});
|
||||
MTGS::WaitGS(false, false, false);
|
||||
}
|
||||
|
||||
if (old_config.FullpathToBios() != EmuConfig.FullpathToBios())
|
||||
{
|
||||
MTGS::RunOnGSThread([]() {
|
||||
ApplyConfirmSetting();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void FullscreenUI::OnVMStarted()
|
||||
@@ -1317,8 +1383,7 @@ void FullscreenUI::DrawLandingWindow()
|
||||
std::make_pair(ICON_PF_BUTTON_TRIANGLE, FSUI_VSTR("Game List")),
|
||||
std::make_pair(ICON_PF_BUTTON_SQUARE, FSUI_VSTR("Toggle Fullscreen")),
|
||||
std::make_pair(circleOK ? ICON_PF_BUTTON_CIRCLE : ICON_PF_BUTTON_CROSS, FSUI_VSTR("Select")),
|
||||
std::make_pair(circleOK ? ICON_PF_BUTTON_CROSS : ICON_PF_BUTTON_CIRCLE, FSUI_VSTR("Exit"))
|
||||
});
|
||||
std::make_pair(circleOK ? ICON_PF_BUTTON_CROSS : ICON_PF_BUTTON_CIRCLE, FSUI_VSTR("Exit"))});
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1328,8 +1393,7 @@ void FullscreenUI::DrawLandingWindow()
|
||||
std::make_pair(ICON_PF_ARROW_LEFT ICON_PF_ARROW_RIGHT, FSUI_VSTR("Navigate")),
|
||||
std::make_pair(ICON_PF_SPACE, FSUI_VSTR("Game List")),
|
||||
std::make_pair(ICON_PF_ENTER, FSUI_VSTR("Select")),
|
||||
std::make_pair(ICON_PF_ESC, FSUI_VSTR("Exit"))
|
||||
});
|
||||
std::make_pair(ICON_PF_ESC, FSUI_VSTR("Exit"))});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1440,16 +1504,14 @@ void FullscreenUI::DrawExitWindow()
|
||||
SetFullscreenFooterText(std::array{
|
||||
std::make_pair(ICON_PF_DPAD_LEFT_RIGHT, FSUI_VSTR("Navigate")),
|
||||
std::make_pair(circleOK ? ICON_PF_BUTTON_CIRCLE : ICON_PF_BUTTON_CROSS, FSUI_VSTR("Select")),
|
||||
std::make_pair(circleOK ? ICON_PF_BUTTON_CROSS : ICON_PF_BUTTON_CIRCLE, FSUI_VSTR("Back"))}
|
||||
);
|
||||
std::make_pair(circleOK ? ICON_PF_BUTTON_CROSS : ICON_PF_BUTTON_CIRCLE, FSUI_VSTR("Back"))});
|
||||
}
|
||||
else
|
||||
{
|
||||
SetFullscreenFooterText(std::array{
|
||||
std::make_pair(ICON_PF_ARROW_LEFT ICON_PF_ARROW_RIGHT, FSUI_VSTR("Navigate")),
|
||||
std::make_pair(ICON_PF_ENTER, FSUI_VSTR("Select")),
|
||||
std::make_pair(ICON_PF_ESC, FSUI_VSTR("Back"))}
|
||||
);
|
||||
std::make_pair(ICON_PF_ESC, FSUI_VSTR("Back"))});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3185,12 +3247,51 @@ void FullscreenUI::DrawInterfaceSettingsPage()
|
||||
{
|
||||
ImGuiFullscreen::SetTheme(bsi->GetBoolValue("UI", "UseLightFullscreenUITheme", false));
|
||||
}
|
||||
SmallStackString<256> SwapSummery;
|
||||
SwapSummery.format(FSUI_FSTR("Uses {} as confirm when using a controller"), ICON_PF_BUTTON_CIRCLE);
|
||||
if (DrawToggleSetting(bsi, FSUI_ICONSTR(ICON_FA_GAMEPAD, "Swap OK/Cancel in Big Picture Mode"),
|
||||
SwapSummery.c_str(), "UI", "SwapOKFullscreenUI", false))
|
||||
|
||||
// DrawStringListSetting dosn't have a callback for applying settings
|
||||
const SmallString swap_mode = bsi->GetSmallStringValue("UI", "SwapOKFullscreenUI", "auto");
|
||||
static constexpr const char* swap_names[] = {
|
||||
FSUI_NSTR("Automatic"),
|
||||
FSUI_NSTR("Enabled"),
|
||||
FSUI_NSTR("Disabled"),
|
||||
};
|
||||
static constexpr const char* swap_values[] = {
|
||||
"auto",
|
||||
"true",
|
||||
"false",
|
||||
};
|
||||
size_t swap_index = std::size(swap_values);
|
||||
for (size_t i = 0; i < std::size(swap_values); i++)
|
||||
{
|
||||
ImGui::GetIO().ConfigNavSwapGamepadButtons = bsi->GetBoolValue("UI", "SwapOKFullscreenUI", false);
|
||||
if (swap_mode == swap_values[i])
|
||||
{
|
||||
swap_index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
SmallStackString<256> swap_summery;
|
||||
swap_summery.format(FSUI_FSTR("Uses {} as confirm when using a controller"), ICON_PF_BUTTON_CIRCLE);
|
||||
if (MenuButtonWithValue(FSUI_ICONSTR(ICON_FA_GAMEPAD, "Swap OK/Cancel in Big Picture Mode"), swap_summery.c_str(),
|
||||
(swap_index < std::size(swap_values)) ? Host::TranslateToCString(TR_CONTEXT, swap_names[swap_index]) : FSUI_CSTR("Unknown")))
|
||||
{
|
||||
ImGuiFullscreen::ChoiceDialogOptions cd_options;
|
||||
cd_options.reserve(std::size(swap_values));
|
||||
for (size_t i = 0; i < std::size(swap_values); i++)
|
||||
cd_options.emplace_back(Host::TranslateToString(TR_CONTEXT, swap_names[i]), i == static_cast<size_t>(swap_index));
|
||||
|
||||
OpenChoiceDialog(FSUI_ICONSTR(ICON_FA_GAMEPAD, "Swap OK/Cancel in Big Picture Mode"), false, std::move(cd_options), [](s32 index, const std::string& title, bool checked) {
|
||||
if (index >= 0)
|
||||
{
|
||||
auto lock = Host::GetSettingsLock();
|
||||
SettingsInterface* bsi = GetEditingSettingsInterface(false);
|
||||
bsi->SetStringValue("UI", "SwapOKFullscreenUI", swap_values[index]);
|
||||
SetSettingsChanged(bsi);
|
||||
ApplyConfirmSetting(bsi);
|
||||
}
|
||||
|
||||
CloseChoiceDialog();
|
||||
});
|
||||
}
|
||||
|
||||
MenuHeading(FSUI_CSTR("Game Display"));
|
||||
@@ -3306,6 +3407,7 @@ void FullscreenUI::DrawBIOSSettingsPage()
|
||||
SettingsInterface* bsi = GetEditingSettingsInterface(game_settings);
|
||||
bsi->SetStringValue("Filenames", "BIOS", values[index].c_str());
|
||||
SetSettingsChanged(bsi);
|
||||
ApplyConfirmSetting(bsi);
|
||||
CloseChoiceDialog();
|
||||
});
|
||||
}
|
||||
@@ -3824,7 +3926,8 @@ void FullscreenUI::DrawGraphicsSettingsPage(SettingsInterface* bsi, bool show_ad
|
||||
FSUI_NSTR("Normal (Vertex)"),
|
||||
FSUI_NSTR("Special (Texture)"),
|
||||
FSUI_NSTR("Special (Texture - Aggressive)"),
|
||||
FSUI_NSTR("Align To Native"),
|
||||
FSUI_NSTR("Align to Native"),
|
||||
FSUI_NSTR("Align to Native - with Texture Offset"),
|
||||
};
|
||||
static constexpr const char* s_native_scaling_options[] = {
|
||||
FSUI_NSTR("Normal (Default)"),
|
||||
@@ -3992,8 +4095,7 @@ void FullscreenUI::DrawGraphicsSettingsPage(SettingsInterface* bsi, bool show_ad
|
||||
static constexpr const char* s_gsdump_compression[] = {
|
||||
FSUI_NSTR("Uncompressed"),
|
||||
FSUI_NSTR("LZMA (xz)"),
|
||||
FSUI_NSTR("Zstandard (zst)")
|
||||
};
|
||||
FSUI_NSTR("Zstandard (zst)")};
|
||||
|
||||
if (show_advanced_settings)
|
||||
{
|
||||
@@ -4783,15 +4885,13 @@ void FullscreenUI::DrawAdvancedSettingsPage()
|
||||
FSUI_NSTR("Uncompressed"),
|
||||
FSUI_NSTR("Deflate64"),
|
||||
FSUI_NSTR("Zstandard"),
|
||||
FSUI_NSTR("LZMA2")
|
||||
};
|
||||
FSUI_NSTR("LZMA2")};
|
||||
|
||||
static constexpr const char* s_savestate_compression_ratio[] = {
|
||||
FSUI_NSTR("Low (Fast)"),
|
||||
FSUI_NSTR("Medium (Recommended)"),
|
||||
FSUI_NSTR("High"),
|
||||
FSUI_NSTR("Very High (Slow, Not Recommended)")
|
||||
};
|
||||
FSUI_NSTR("Very High (Slow, Not Recommended)")};
|
||||
|
||||
if (show_advanced_settings)
|
||||
{
|
||||
@@ -7575,7 +7675,8 @@ TRANSLATE_NOOP("FullscreenUI", "Merge Targets");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Normal (Vertex)");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Special (Texture)");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Special (Texture - Aggressive)");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Align To Native");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Align to Native");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Align to Native - with Texture Offset");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Aggressive");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Half");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Force Bilinear");
|
||||
|
||||
@@ -32,6 +32,7 @@ namespace FullscreenUI
|
||||
void ReturnToPreviousWindow();
|
||||
void ReturnToMainWindow();
|
||||
void SetStandardSelectionFooterText(bool back_instead_of_cancel);
|
||||
void LocaleChanged();
|
||||
|
||||
void Shutdown(bool clear_state);
|
||||
void Render();
|
||||
@@ -51,4 +52,7 @@ namespace Host
|
||||
|
||||
void OnCoverDownloaderOpenRequested();
|
||||
void OnCreateMemoryCardOpenRequested();
|
||||
|
||||
/// Did Playstation in the currently selected locale use circle as confirm
|
||||
bool LocaleCircleConfirm();
|
||||
} // namespace Host
|
||||
|
||||
@@ -92,7 +92,6 @@ namespace InputManager
|
||||
static std::optional<InputBindingKey> ParseHostKeyboardKey(const std::string_view source, const std::string_view sub_binding);
|
||||
static std::optional<InputBindingKey> ParsePointerKey(const std::string_view source, const std::string_view sub_binding);
|
||||
|
||||
static std::vector<std::string_view> SplitChord(const std::string_view binding);
|
||||
static bool SplitBinding(const std::string_view binding, std::string_view* source, std::string_view* sub_binding);
|
||||
static void PrettifyInputBindingPart(const std::string_view binding, SmallString& ret, bool& changed);
|
||||
static void AddBinding(const std::string_view binding, const InputEventHandler& handler);
|
||||
@@ -752,7 +751,7 @@ void InputManager::AddPadBindings(SettingsInterface& si, u32 pad_index)
|
||||
const float sensitivity = si.GetFloatValue(section.c_str(), fmt::format("{}Scale", bi.name).c_str(), 1.0f);
|
||||
const float deadzone = si.GetFloatValue(section.c_str(), fmt::format("{}Deadzone", bi.name).c_str(), 0.0f);
|
||||
AddBindings(
|
||||
bindings, InputAxisEventHandler{[pad_index, bind_index = bi.bind_index, sensitivity, deadzone](float value) {
|
||||
bindings, InputAxisEventHandler{[pad_index, bind_index = bi.bind_index, sensitivity, deadzone](InputBindingKey key, float value) {
|
||||
Pad::SetControllerState(pad_index, bind_index, ApplySingleBindingScale(sensitivity, deadzone, value));
|
||||
}});
|
||||
}
|
||||
@@ -772,9 +771,9 @@ void InputManager::AddPadBindings(SettingsInterface& si, u32 pad_index)
|
||||
if (!bindings.empty())
|
||||
{
|
||||
const float deadzone = si.GetFloatValue(section.c_str(), fmt::format("Macro{}Deadzone", macro_button_index + 1).c_str(), 0.0f);
|
||||
AddBindings(bindings, InputAxisEventHandler{[pad_index, macro_button_index, deadzone](float value) {
|
||||
AddBindings(bindings, InputAxisEventHandler{[pad_index, macro_button_index, deadzone](InputBindingKey key, float value) {
|
||||
const bool state = (value > deadzone);
|
||||
Pad::SetMacroButtonState(pad_index, macro_button_index, state);
|
||||
Pad::SetMacroButtonState(key, pad_index, macro_button_index, state);
|
||||
}});
|
||||
}
|
||||
}
|
||||
@@ -836,7 +835,7 @@ void InputManager::AddUSBBindings(SettingsInterface& si, u32 port)
|
||||
{
|
||||
const float sensitivity = si.GetFloatValue(section.c_str(), fmt::format("{}Scale", bi.name).c_str(), 1.0f);
|
||||
const float deadzone = si.GetFloatValue(section.c_str(), fmt::format("{}Deadzone", bi.name).c_str(), 0.0f);
|
||||
AddBindings(bindings, InputAxisEventHandler{[port, bind_index = bi.bind_index, sensitivity, deadzone](float value) {
|
||||
AddBindings(bindings, InputAxisEventHandler{[port, bind_index = bi.bind_index, sensitivity, deadzone](InputBindingKey key, float value) {
|
||||
USB::SetDeviceBindValue(port, bind_index, ApplySingleBindingScale(sensitivity, deadzone, value));
|
||||
}});
|
||||
}
|
||||
@@ -977,7 +976,7 @@ bool InputManager::ProcessEvent(InputBindingKey key, float value, bool skip_butt
|
||||
if (IsAxisHandler(binding->handler))
|
||||
{
|
||||
if (value_to_pass >= 0.0f && (!skip_button_handlers || value_to_pass == 0.0f))
|
||||
std::get<InputAxisEventHandler>(binding->handler)(value_to_pass);
|
||||
std::get<InputAxisEventHandler>(binding->handler)(key, value_to_pass);
|
||||
}
|
||||
else if (binding->num_keys >= min_num_keys)
|
||||
{
|
||||
@@ -1056,7 +1055,7 @@ void InputManager::ClearBindStateFromSource(InputBindingKey key)
|
||||
if (binding->keys[i].MaskDirection() != match_key)
|
||||
continue;
|
||||
|
||||
std::get<InputAxisEventHandler>(binding->handler)(0.0f);
|
||||
std::get<InputAxisEventHandler>(binding->handler)(key, 0.0f);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,7 +95,7 @@ struct InputBindingKeyHash
|
||||
using InputButtonEventHandler = std::function<void(s32 value)>;
|
||||
|
||||
/// Callback types for a normalized event. Usually used for pads.
|
||||
using InputAxisEventHandler = std::function<void(float value)>;
|
||||
using InputAxisEventHandler = std::function<void(InputBindingKey key, float value)>;
|
||||
|
||||
/// Input monitoring for external access.
|
||||
struct InputInterceptHook
|
||||
@@ -211,6 +211,9 @@ namespace InputManager
|
||||
/// Represents a binding with icon fonts, if available.
|
||||
bool PrettifyInputBinding(SmallStringBase& binding);
|
||||
|
||||
/// Splits a chord into individual bindings.
|
||||
std::vector<std::string_view> SplitChord(const std::string_view binding);
|
||||
|
||||
/// Returns a list of all hotkeys.
|
||||
std::vector<const HotkeyInfo*> GetHotkeyList();
|
||||
|
||||
|
||||
@@ -28,11 +28,15 @@
|
||||
|
||||
#include <vector>
|
||||
|
||||
//Map of actively pressed keys so that chords work
|
||||
using KeyMap = std::unordered_multimap<u64, bool>;
|
||||
|
||||
namespace Pad
|
||||
{
|
||||
struct MacroButton
|
||||
{
|
||||
std::vector<u32> buttons; ///< Buttons to activate.
|
||||
KeyMap active_buttons; ///< Currently active buttons.
|
||||
float pressure; ///< Pressure to apply when macro is active.
|
||||
u16 toggle_frequency; ///< Interval at which the buttons will be toggled, if not 0.
|
||||
u16 toggle_counter; ///< When this counter reaches zero, buttons will be toggled.
|
||||
@@ -670,8 +674,12 @@ void Pad::LoadMacroButtonConfig(const SettingsInterface& si, u32 pad, const Cont
|
||||
}
|
||||
}
|
||||
|
||||
void Pad::SetMacroButtonState(u32 pad, u32 index, bool state)
|
||||
void Pad::SetMacroButtonState(InputBindingKey& key, u32 pad, u32 index, bool state)
|
||||
{
|
||||
//0 appears for some reason and breaks mb.active_buttons.size() != binding_count
|
||||
if (key.bits == 0)
|
||||
return;
|
||||
|
||||
if (pad >= Pad::NUM_CONTROLLER_PORTS || index >= NUM_MACRO_BUTTONS_PER_CONTROLLER)
|
||||
return;
|
||||
|
||||
@@ -679,6 +687,30 @@ void Pad::SetMacroButtonState(u32 pad, u32 index, bool state)
|
||||
if (mb.buttons.empty())
|
||||
return;
|
||||
|
||||
SettingsInterface& sif = *Host::GetSettingsInterface();
|
||||
std::vector<std::string> data = sif.GetStringList(fmt::format("Pad{}", pad+1).c_str(), fmt::format("Macro{}", index+1).c_str());
|
||||
size_t binding_count = 0;
|
||||
//just in case there's more than one index
|
||||
for (std::string bind : data)
|
||||
{
|
||||
binding_count += InputManager::SplitChord(bind).size();
|
||||
}
|
||||
if (mb.active_buttons.find(key.bits) != mb.active_buttons.end())
|
||||
mb.active_buttons.erase(key.bits);
|
||||
|
||||
mb.active_buttons.emplace(key.bits, state);
|
||||
|
||||
if (mb.active_buttons.size() != binding_count)
|
||||
return;
|
||||
|
||||
if (mb.active_buttons.size() > 1 && state)
|
||||
{
|
||||
for (auto it = mb.active_buttons.begin(); it != mb.active_buttons.end(); ++it)
|
||||
{
|
||||
if (!it->second)
|
||||
return;
|
||||
}
|
||||
}
|
||||
const bool trigger_state = (mb.trigger_toggle ? (state ? !mb.trigger_state : mb.trigger_state) : state);
|
||||
if (mb.trigger_state == trigger_state)
|
||||
return;
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "Config.h"
|
||||
#include "Input/InputManager.h"
|
||||
#include "SIO/Pad/PadTypes.h"
|
||||
|
||||
#include <memory>
|
||||
@@ -68,6 +69,6 @@ namespace Pad
|
||||
bool Freeze(StateWrapper& sw);
|
||||
|
||||
// Sets the state of the specified macro button.
|
||||
void SetMacroButtonState(u32 pad, u32 index, bool state);
|
||||
void SetMacroButtonState(InputBindingKey& key, u32 pad, u32 index, bool state);
|
||||
void UpdateMacroButtons();
|
||||
}; // namespace Pad
|
||||
|
||||
@@ -3610,7 +3610,7 @@ void VMManager::ReloadPINE()
|
||||
PINEServer::Deinitialize();
|
||||
|
||||
if (EmuConfig.EnablePINE)
|
||||
PINEServer::Initialize();
|
||||
PINEServer::Initialize(EmuConfig.PINESlot);
|
||||
}
|
||||
|
||||
void VMManager::InitializeDiscordPresence()
|
||||
|
||||
@@ -238,6 +238,11 @@ void Host::OnCreateMemoryCardOpenRequested()
|
||||
{
|
||||
}
|
||||
|
||||
bool Host::LocaleCircleConfirm()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Host::ShouldPreferHostFileSelector()
|
||||
{
|
||||
return false;
|
||||
|
||||
Reference in New Issue
Block a user