mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-07-11 01:25:07 +02:00
Merge branch 'master' into master
This commit is contained in:
@@ -266,13 +266,13 @@ jobs:
|
||||
fi
|
||||
|
||||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v5
|
||||
with:
|
||||
name: linux-${{ steps.version.outputs.tag }}-${{ matrix.id }} AppImage
|
||||
path: artifacts/
|
||||
|
||||
- name: Upload release
|
||||
uses: softprops/action-gh-release@6cbd405e2c4e67a21c47fa9e383d020e4e28b836 # v2.3.3
|
||||
uses: softprops/action-gh-release@6da8fa9354ddfdc4aeace5fc48d7f679b5214090 # v2.4.1
|
||||
if: github.ref_type == 'tag'
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
@@ -97,7 +97,7 @@ jobs:
|
||||
run: cp Windows/Release/*.exe ppsspp/
|
||||
|
||||
- name: Upload build
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v5
|
||||
with:
|
||||
name: Windows ${{ matrix.platform }} build
|
||||
path: ppsspp/
|
||||
@@ -113,7 +113,7 @@ jobs:
|
||||
Compress-Archive -Path "ppsspp/*" -Update -DestinationPath "releases/PPSSPP-${{ github.ref_name }}-Windows-${{ matrix.platform }}.zip"
|
||||
|
||||
- name: Upload release
|
||||
uses: softprops/action-gh-release@6cbd405e2c4e67a21c47fa9e383d020e4e28b836 # v2.3.3
|
||||
uses: softprops/action-gh-release@6da8fa9354ddfdc4aeace5fc48d7f679b5214090 # v2.4.1
|
||||
if: github.ref_type == 'tag'
|
||||
with:
|
||||
files: releases/*.zip
|
||||
@@ -147,7 +147,7 @@ jobs:
|
||||
run: git submodule update --init pspautotests assets/lang
|
||||
|
||||
- name: Download build
|
||||
uses: actions/download-artifact@v5
|
||||
uses: actions/download-artifact@v6
|
||||
with:
|
||||
name: Windows x64 build
|
||||
path: ppsspp/
|
||||
@@ -234,6 +234,12 @@ jobs:
|
||||
cxx: clang++
|
||||
args: ./b.sh --headless --unittest --fat --no-png --no-sdl2
|
||||
id: macos
|
||||
- os: macos-latest
|
||||
extra: libretro_mac
|
||||
cc: clang
|
||||
cxx: clang++
|
||||
args: ./b.sh --libretro
|
||||
id: macos-libretro
|
||||
- os: macos-latest
|
||||
extra: ios
|
||||
cc: clang
|
||||
@@ -362,14 +368,14 @@ jobs:
|
||||
fi
|
||||
|
||||
- name: Upload build
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v5
|
||||
if: matrix.extra == 'test'
|
||||
with:
|
||||
name: ${{ matrix.os }} build
|
||||
path: ppsspp/
|
||||
|
||||
- name: Upload iOS build
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v5
|
||||
if: matrix.id == 'ios'
|
||||
with:
|
||||
name: ${{ matrix.id }} build
|
||||
@@ -381,7 +387,7 @@ jobs:
|
||||
run: mv PPSSPPSDL.zip PPSSPPSDL-macOS-${GITHUB_REF_NAME}.zip
|
||||
|
||||
- name: Upload macOS & iOS release
|
||||
uses: softprops/action-gh-release@6cbd405e2c4e67a21c47fa9e383d020e4e28b836 # v2.3.3
|
||||
uses: softprops/action-gh-release@6da8fa9354ddfdc4aeace5fc48d7f679b5214090 # v2.4.1
|
||||
if: github.ref_type == 'tag' && (matrix.id == 'macos' || matrix.id == 'ios')
|
||||
with:
|
||||
files: |
|
||||
@@ -427,7 +433,7 @@ jobs:
|
||||
git submodule update --init SDL/macOS
|
||||
|
||||
- name: Download build
|
||||
uses: actions/download-artifact@v5
|
||||
uses: actions/download-artifact@v6
|
||||
with:
|
||||
name: ${{ matrix.os }} build
|
||||
path: ppsspp/
|
||||
|
||||
@@ -75,7 +75,7 @@ jobs:
|
||||
fi
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v5
|
||||
with:
|
||||
name: android-${{ github.event.inputs.buildVariant }} build
|
||||
path: ppsspp/
|
||||
|
||||
@@ -81,13 +81,13 @@ jobs:
|
||||
fi
|
||||
|
||||
- name: Upload IPA artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v5
|
||||
with:
|
||||
name: iOS-IPA build
|
||||
path: ppsspp/*.ipa
|
||||
|
||||
- name: Upload DEB artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v5
|
||||
with:
|
||||
name: iOS-DEB build
|
||||
path: ppsspp/*.deb
|
||||
|
||||
@@ -111,7 +111,7 @@ jobs:
|
||||
#ls ppsspp
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v5
|
||||
with:
|
||||
name: UWP-${{ github.event.inputs.buildConfiguration }}-${{ github.event.inputs.buildPlatform }} build
|
||||
path: ppsspp/
|
||||
|
||||
@@ -39,7 +39,7 @@ jobs:
|
||||
echo "tarball=$TARBALL" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Upload tarball
|
||||
uses: softprops/action-gh-release@6cbd405e2c4e67a21c47fa9e383d020e4e28b836 # v2.3.3
|
||||
uses: softprops/action-gh-release@6da8fa9354ddfdc4aeace5fc48d7f679b5214090 # v2.4.1
|
||||
with:
|
||||
files: ${{ steps.archive.outputs.tarball }}
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
+8
-2
@@ -1490,6 +1490,7 @@ else()
|
||||
SDL/CocoaBarItems.h
|
||||
SDL/PPSSPPAboutViewController.m
|
||||
SDL/PPSSPPAboutViewController.h
|
||||
SDL/MacCameraHelper.mm
|
||||
UI/DarwinFileSystemServices.mm
|
||||
UI/DarwinFileSystemServices.h
|
||||
Common/Battery/AppleBatteryClient.m
|
||||
@@ -1504,9 +1505,10 @@ else()
|
||||
set_source_files_properties(SDL/CocoaMetalLayer.mm PROPERTIES COMPILE_FLAGS -fobjc-arc)
|
||||
set_source_files_properties(SDL/CocoaBarItems.mm PROPERTIES COMPILE_FLAGS -fobjc-arc)
|
||||
set_source_files_properties(SDL/PPSSPPAboutViewController.m PROPERTIES COMPILE_FLAGS -fobjc-arc)
|
||||
set_source_files_properties(SDL/MacCameraHelper.mm PROPERTIES COMPILE_FLAGS -fobjc-arc)
|
||||
set_source_files_properties(Common/Battery/AppleBatteryClient.m PROPERTIES COMPILE_FLAGS -fobjc-arc)
|
||||
set_source_files_properties(Common/Render/Text/draw_text_cocoa.mm PROPERTIES COMPILE_FLAGS -fobjc-arc)
|
||||
set(nativeExtraLibs ${nativeExtraLibs} ${COCOA_LIBRARY} ${QUARTZ_CORE_LIBRARY} ${IOKIT_LIBRARY})
|
||||
set(nativeExtraLibs ${nativeExtraLibs} ${COCOA_LIBRARY} ${QUARTZ_CORE_LIBRARY} ${IOKIT_LIBRARY} "-framework AVFoundation" "-framework CoreMedia" "-framework CoreVideo")
|
||||
|
||||
if(USE_SYSTEM_LIBSDL2)
|
||||
set(nativeExtraLibs ${nativeExtraLibs} SDL2::SDL2)
|
||||
@@ -2786,7 +2788,11 @@ set(NativeAssets
|
||||
assets/shaders
|
||||
assets/themes
|
||||
assets/vfpu
|
||||
assets/Roboto-Condensed.ttf
|
||||
assets/Roboto_Condensed-Regular.ttf
|
||||
assets/Roboto_Condensed-Light.ttf
|
||||
assets/Roboto_Condensed-Bold.ttf
|
||||
assets/Roboto_Condensed-Italic.ttf
|
||||
assets/Inconsolata-Regular.ttf
|
||||
assets/7z.png
|
||||
assets/compat.ini
|
||||
assets/infra-dns.json
|
||||
|
||||
@@ -484,6 +484,7 @@
|
||||
<ClInclude Include="Render\Text\draw_text_uwp.h" />
|
||||
<ClInclude Include="Render\Text\draw_text_win.h" />
|
||||
<ClInclude Include="LogReporting.h" />
|
||||
<ClInclude Include="Render\Text\Font.h" />
|
||||
<ClInclude Include="RiscVEmitter.h" />
|
||||
<ClInclude Include="Serialize\SerializeDeque.h" />
|
||||
<ClInclude Include="Serialize\SerializeFuncs.h" />
|
||||
|
||||
@@ -701,6 +701,9 @@
|
||||
<ClInclude Include="UI\TabHolder.h">
|
||||
<Filter>UI</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Render\Text\Font.h">
|
||||
<Filter>Render\Text</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="ABI.cpp" />
|
||||
|
||||
@@ -189,11 +189,12 @@ void Section::Clear() {
|
||||
lines_.clear();
|
||||
}
|
||||
|
||||
bool Section::GetKeys(std::vector<std::string> &keys) const {
|
||||
keys.clear();
|
||||
bool Section::GetKeys(std::vector<std::string> *keys) const {
|
||||
keys->clear();
|
||||
keys->reserve(lines_.size());
|
||||
for (const auto &line : lines_) {
|
||||
if (!line.Key().empty())
|
||||
keys.emplace_back(line.Key());
|
||||
keys->emplace_back(line.Key());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -366,7 +367,7 @@ bool Section::Get(std::string_view key, double* value) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Section::Exists(std::string_view key) const {
|
||||
bool Section::HasKey(std::string_view key) const {
|
||||
for (auto &line : lines_) {
|
||||
if (equalsNoCase(key, line.Key()))
|
||||
return true;
|
||||
@@ -438,35 +439,6 @@ bool IniFile::DeleteSection(std::string_view sectionName) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IniFile::Exists(std::string_view sectionName, std::string_view key) const {
|
||||
const Section* section = GetSection(sectionName);
|
||||
if (!section)
|
||||
return false;
|
||||
return section->Exists(key);
|
||||
}
|
||||
|
||||
bool IniFile::DeleteKey(std::string_view sectionName, std::string_view key) {
|
||||
Section* section = GetSection(sectionName);
|
||||
if (!section)
|
||||
return false;
|
||||
ParsedIniLine *line = section->GetLine(key);
|
||||
for (auto liter = section->lines_.begin(); liter != section->lines_.end(); ++liter) {
|
||||
if (line == &(*liter)) {
|
||||
section->lines_.erase(liter);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false; //shouldn't happen
|
||||
}
|
||||
|
||||
// Return a list of all keys in a section
|
||||
bool IniFile::GetKeys(std::string_view sectionName, std::vector<std::string>& keys) const {
|
||||
const Section *section = GetSection(sectionName);
|
||||
if (!section)
|
||||
return false;
|
||||
return section->GetKeys(keys);
|
||||
}
|
||||
|
||||
void IniFile::SortSections() {
|
||||
std::sort(sections.begin(), sections.end());
|
||||
}
|
||||
@@ -566,55 +538,3 @@ bool IniFile::Save(const Path &filename)
|
||||
fclose(file);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IniFile::Get(std::string_view sectionName, std::string_view key, std::string *value) const {
|
||||
const Section *section = GetSection(sectionName);
|
||||
if (!section) {
|
||||
return false;
|
||||
}
|
||||
return section->Get(key, value);
|
||||
}
|
||||
|
||||
bool IniFile::Get(std::string_view sectionName, std::string_view key, std::vector<std::string> *values) const {
|
||||
const Section *section = GetSection(sectionName);
|
||||
if (!section) {
|
||||
return false;
|
||||
}
|
||||
return section->Get(key, values);
|
||||
}
|
||||
|
||||
bool IniFile::Get(std::string_view sectionName, std::string_view key, int *value) const {
|
||||
const Section *section = GetSection(sectionName);
|
||||
if (!section) {
|
||||
return false;
|
||||
} else {
|
||||
return section->Get(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
bool IniFile::Get(std::string_view sectionName, std::string_view key, uint32_t *value) const {
|
||||
const Section *section = GetSection(sectionName);
|
||||
if (!section) {
|
||||
return false;
|
||||
} else {
|
||||
return section->Get(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
bool IniFile::Get(std::string_view sectionName, std::string_view key, uint64_t *value) const {
|
||||
const Section *section = GetSection(sectionName);
|
||||
if (!section) {
|
||||
return false;
|
||||
} else {
|
||||
return section->Get(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
bool IniFile::Get(std::string_view sectionName, std::string_view key, bool *value) const {
|
||||
const Section *section = GetSection(sectionName);
|
||||
if (!section) {
|
||||
return false;
|
||||
} else {
|
||||
return section->Get(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,7 +55,7 @@ public:
|
||||
Section() {}
|
||||
Section(std::string_view name) : name_(name) {}
|
||||
|
||||
bool Exists(std::string_view key) const;
|
||||
bool HasKey(std::string_view key) const;
|
||||
bool Delete(std::string_view key);
|
||||
|
||||
void Clear();
|
||||
@@ -96,7 +96,7 @@ public:
|
||||
bool Get(std::string_view key, std::vector<std::string> *values) const;
|
||||
|
||||
// Return a list of all keys in this section
|
||||
bool GetKeys(std::vector<std::string> &keys) const;
|
||||
bool GetKeys(std::vector<std::string> *keys) const;
|
||||
|
||||
bool operator < (const Section& other) const {
|
||||
return name_ < other.name_;
|
||||
@@ -125,20 +125,6 @@ public:
|
||||
|
||||
bool Save(const Path &path);
|
||||
|
||||
// Returns true if key exists in section
|
||||
bool Exists(std::string_view sectionName, std::string_view key) const;
|
||||
|
||||
// These will not create the section if it doesn't exist.
|
||||
bool Get(std::string_view sectionName, std::string_view key, std::string *value) const;
|
||||
bool Get(std::string_view sectionName, std::string_view key, int* value) const;
|
||||
bool Get(std::string_view sectionName, std::string_view key, uint32_t* value) const;
|
||||
bool Get(std::string_view sectionName, std::string_view key, uint64_t* value) const;
|
||||
bool Get(std::string_view sectionName, std::string_view key, bool* value) const;
|
||||
bool Get(std::string_view sectionName, std::string_view key, std::vector<std::string> *values) const;
|
||||
|
||||
bool GetKeys(std::string_view sectionName, std::vector<std::string>& keys) const;
|
||||
|
||||
bool DeleteKey(std::string_view sectionName, std::string_view key);
|
||||
bool DeleteSection(std::string_view sectionName);
|
||||
|
||||
void SortSections();
|
||||
|
||||
@@ -1,11 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdlib>
|
||||
#include <string_view>
|
||||
|
||||
namespace hash {
|
||||
|
||||
// Fairly decent function for hashing strings.
|
||||
uint32_t Adler32(const uint8_t *data, size_t len);
|
||||
inline uint32_t Adler32(std::string_view data) {
|
||||
return Adler32((const uint8_t *)data.data(), data.size());
|
||||
}
|
||||
|
||||
} // namespace hash
|
||||
|
||||
|
||||
@@ -27,14 +27,14 @@ static jmethodID isExternalStoragePreservedLegacy;
|
||||
static jmethodID computeRecursiveDirectorySize;
|
||||
|
||||
static jobject g_nativeActivity;
|
||||
static jclass g_classActivity;
|
||||
static jclass g_classContentUri;
|
||||
|
||||
void Android_StorageSetActivity(jobject nativeActivity) {
|
||||
g_nativeActivity = nativeActivity;
|
||||
}
|
||||
|
||||
void Android_RegisterStorageCallbacks(JNIEnv * env, jobject obj) {
|
||||
jclass localClass = env->FindClass("org/ppsspp/ppsspp/PpssppActivity");
|
||||
jclass localClass = env->FindClass("org/ppsspp/ppsspp/ContentUri");
|
||||
_dbg_assert_(localClass);
|
||||
|
||||
openContentUri = env->GetStaticMethodID(localClass, "openContentUri", "(Landroid/app/Activity;Ljava/lang/String;Ljava/lang/String;)I");
|
||||
@@ -66,14 +66,14 @@ void Android_RegisterStorageCallbacks(JNIEnv * env, jobject obj) {
|
||||
computeRecursiveDirectorySize = env->GetStaticMethodID(localClass, "computeRecursiveDirectorySize", "(Landroid/app/Activity;Ljava/lang/String;)J");
|
||||
_dbg_assert_(computeRecursiveDirectorySize);
|
||||
|
||||
g_classActivity = reinterpret_cast<jclass>(env->NewGlobalRef(localClass));
|
||||
g_classContentUri = reinterpret_cast<jclass>(env->NewGlobalRef(localClass));
|
||||
env->DeleteLocalRef(localClass); // cleanup local ref
|
||||
}
|
||||
|
||||
void Android_UnregisterStorageCallbacks(JNIEnv * env) {
|
||||
if (g_classActivity) {
|
||||
env->DeleteGlobalRef(g_classActivity);
|
||||
g_classActivity = nullptr;
|
||||
if (g_classContentUri) {
|
||||
env->DeleteGlobalRef(g_classContentUri);
|
||||
g_classContentUri = nullptr;
|
||||
}
|
||||
g_nativeActivity = nullptr;
|
||||
openContentUri = nullptr;
|
||||
@@ -125,7 +125,7 @@ int Android_OpenContentUriFd(std::string_view filename, Android_OpenContentUriMo
|
||||
}
|
||||
jstring j_filename = env->NewStringUTF(fname.c_str());
|
||||
jstring j_mode = env->NewStringUTF(modeStr);
|
||||
int fd = env->CallStaticIntMethod(g_classActivity, openContentUri, g_nativeActivity, j_filename, j_mode);
|
||||
int fd = env->CallStaticIntMethod(g_classContentUri, openContentUri, g_nativeActivity, j_filename, j_mode);
|
||||
return fd;
|
||||
}
|
||||
|
||||
@@ -136,7 +136,7 @@ StorageError Android_CreateDirectory(const std::string &rootTreeUri, const std::
|
||||
auto env = getEnv();
|
||||
jstring paramRoot = env->NewStringUTF(rootTreeUri.c_str());
|
||||
jstring paramDirName = env->NewStringUTF(dirName.c_str());
|
||||
return StorageErrorFromInt(env->CallStaticIntMethod(g_classActivity, contentUriCreateDirectory, g_nativeActivity, paramRoot, paramDirName));
|
||||
return StorageErrorFromInt(env->CallStaticIntMethod(g_classContentUri, contentUriCreateDirectory, g_nativeActivity, paramRoot, paramDirName));
|
||||
}
|
||||
|
||||
StorageError Android_CreateFile(const std::string &parentTreeUri, const std::string &fileName) {
|
||||
@@ -146,7 +146,7 @@ StorageError Android_CreateFile(const std::string &parentTreeUri, const std::str
|
||||
auto env = getEnv();
|
||||
jstring paramRoot = env->NewStringUTF(parentTreeUri.c_str());
|
||||
jstring paramFileName = env->NewStringUTF(fileName.c_str());
|
||||
return StorageErrorFromInt(env->CallStaticIntMethod(g_classActivity, contentUriCreateFile, g_nativeActivity, paramRoot, paramFileName));
|
||||
return StorageErrorFromInt(env->CallStaticIntMethod(g_classContentUri, contentUriCreateFile, g_nativeActivity, paramRoot, paramFileName));
|
||||
}
|
||||
|
||||
StorageError Android_CopyFile(const std::string &fileUri, const std::string &destParentUri) {
|
||||
@@ -156,7 +156,7 @@ StorageError Android_CopyFile(const std::string &fileUri, const std::string &des
|
||||
auto env = getEnv();
|
||||
jstring paramFileName = env->NewStringUTF(fileUri.c_str());
|
||||
jstring paramDestParentUri = env->NewStringUTF(destParentUri.c_str());
|
||||
return StorageErrorFromInt(env->CallStaticIntMethod(g_classActivity, contentUriCopyFile, g_nativeActivity, paramFileName, paramDestParentUri));
|
||||
return StorageErrorFromInt(env->CallStaticIntMethod(g_classContentUri, contentUriCopyFile, g_nativeActivity, paramFileName, paramDestParentUri));
|
||||
}
|
||||
|
||||
StorageError Android_MoveFile(const std::string &fileUri, const std::string &srcParentUri, const std::string &destParentUri) {
|
||||
@@ -167,7 +167,7 @@ StorageError Android_MoveFile(const std::string &fileUri, const std::string &src
|
||||
jstring paramFileName = env->NewStringUTF(fileUri.c_str());
|
||||
jstring paramSrcParentUri = env->NewStringUTF(srcParentUri.c_str());
|
||||
jstring paramDestParentUri = env->NewStringUTF(destParentUri.c_str());
|
||||
return StorageErrorFromInt(env->CallStaticIntMethod(g_classActivity, contentUriMoveFile, g_nativeActivity, paramFileName, paramSrcParentUri, paramDestParentUri));
|
||||
return StorageErrorFromInt(env->CallStaticIntMethod(g_classContentUri, contentUriMoveFile, g_nativeActivity, paramFileName, paramSrcParentUri, paramDestParentUri));
|
||||
}
|
||||
|
||||
StorageError Android_RemoveFile(const std::string &fileUri) {
|
||||
@@ -176,7 +176,7 @@ StorageError Android_RemoveFile(const std::string &fileUri) {
|
||||
}
|
||||
auto env = getEnv();
|
||||
jstring paramFileName = env->NewStringUTF(fileUri.c_str());
|
||||
return StorageErrorFromInt(env->CallStaticIntMethod(g_classActivity, contentUriRemoveFile, g_nativeActivity, paramFileName));
|
||||
return StorageErrorFromInt(env->CallStaticIntMethod(g_classContentUri, contentUriRemoveFile, g_nativeActivity, paramFileName));
|
||||
}
|
||||
|
||||
StorageError Android_RenameFileTo(const std::string &fileUri, const std::string &newName) {
|
||||
@@ -186,7 +186,7 @@ StorageError Android_RenameFileTo(const std::string &fileUri, const std::string
|
||||
auto env = getEnv();
|
||||
jstring paramFileUri = env->NewStringUTF(fileUri.c_str());
|
||||
jstring paramNewName = env->NewStringUTF(newName.c_str());
|
||||
return StorageErrorFromInt(env->CallStaticIntMethod(g_classActivity, contentUriRenameFileTo, g_nativeActivity, paramFileUri, paramNewName));
|
||||
return StorageErrorFromInt(env->CallStaticIntMethod(g_classContentUri, contentUriRenameFileTo, g_nativeActivity, paramFileUri, paramNewName));
|
||||
}
|
||||
|
||||
// NOTE: Does not set fullName - you're supposed to already know it.
|
||||
@@ -232,7 +232,7 @@ bool Android_GetFileInfo(const std::string &fileUri, File::FileInfo *fileInfo) {
|
||||
auto env = getEnv();
|
||||
jstring paramFileUri = env->NewStringUTF(fileUri.c_str());
|
||||
|
||||
jstring str = (jstring)env->CallStaticObjectMethod(g_classActivity, contentUriGetFileInfo, g_nativeActivity, paramFileUri);
|
||||
jstring str = (jstring)env->CallStaticObjectMethod(g_classContentUri, contentUriGetFileInfo, g_nativeActivity, paramFileUri);
|
||||
if (!str) {
|
||||
return false;
|
||||
}
|
||||
@@ -250,7 +250,7 @@ bool Android_FileExists(const std::string &fileUri) {
|
||||
}
|
||||
auto env = getEnv();
|
||||
jstring paramFileUri = env->NewStringUTF(fileUri.c_str());
|
||||
bool exists = env->CallStaticBooleanMethod(g_classActivity, contentUriFileExists, g_nativeActivity, paramFileUri);
|
||||
bool exists = env->CallStaticBooleanMethod(g_classContentUri, contentUriFileExists, g_nativeActivity, paramFileUri);
|
||||
return exists;
|
||||
}
|
||||
|
||||
@@ -265,7 +265,7 @@ std::vector<File::FileInfo> Android_ListContentUri(const std::string &uri, const
|
||||
double start = time_now_d();
|
||||
|
||||
jstring param = env->NewStringUTF(uri.c_str());
|
||||
jobject retval = env->CallStaticObjectMethod(g_classActivity, listContentUriDir, g_nativeActivity, param);
|
||||
jobject retval = env->CallStaticObjectMethod(g_classContentUri, listContentUriDir, g_nativeActivity, param);
|
||||
|
||||
jobjectArray fileList = (jobjectArray)retval;
|
||||
std::vector<File::FileInfo> items;
|
||||
@@ -306,7 +306,7 @@ int64_t Android_GetFreeSpaceByContentUri(const std::string &uri) {
|
||||
auto env = getEnv();
|
||||
|
||||
jstring param = env->NewStringUTF(uri.c_str());
|
||||
return env->CallStaticLongMethod(g_classActivity, contentUriGetFreeStorageSpace, g_nativeActivity, param);
|
||||
return env->CallStaticLongMethod(g_classContentUri, contentUriGetFreeStorageSpace, g_nativeActivity, param);
|
||||
}
|
||||
|
||||
// Hm, this is never used? We use statvfs instead.
|
||||
@@ -322,7 +322,7 @@ int64_t Android_GetFreeSpaceByFilePath(const std::string &filePath) {
|
||||
}
|
||||
|
||||
jstring param = env->NewStringUTF(filePath.c_str());
|
||||
return env->CallStaticLongMethod(g_classActivity, filePathGetFreeStorageSpace, g_nativeActivity, param);
|
||||
return env->CallStaticLongMethod(g_classContentUri, filePathGetFreeStorageSpace, g_nativeActivity, param);
|
||||
}
|
||||
|
||||
int64_t Android_ComputeRecursiveDirectorySize(const std::string &uri) {
|
||||
@@ -334,7 +334,7 @@ int64_t Android_ComputeRecursiveDirectorySize(const std::string &uri) {
|
||||
jstring param = env->NewStringUTF(uri.c_str());
|
||||
|
||||
double start = time_now_d();
|
||||
int64_t size = env->CallStaticLongMethod(g_classActivity, computeRecursiveDirectorySize, g_nativeActivity, param);
|
||||
int64_t size = env->CallStaticLongMethod(g_classContentUri, computeRecursiveDirectorySize, g_nativeActivity, param);
|
||||
double elapsed = time_now_d() - start;
|
||||
|
||||
INFO_LOG(Log::IO, "ComputeRecursiveDirectorySize(%s) in %0.3f s", uri.c_str(), elapsed);
|
||||
@@ -347,7 +347,7 @@ bool Android_IsExternalStoragePreservedLegacy() {
|
||||
}
|
||||
auto env = getEnv();
|
||||
// Note: No activity param
|
||||
return env->CallStaticBooleanMethod(g_classActivity, isExternalStoragePreservedLegacy);
|
||||
return env->CallStaticBooleanMethod(g_classContentUri, isExternalStoragePreservedLegacy);
|
||||
}
|
||||
|
||||
const char *Android_ErrorToString(StorageError error) {
|
||||
|
||||
@@ -31,6 +31,13 @@ enum Orientation {
|
||||
ORIENT_VERTICAL,
|
||||
};
|
||||
|
||||
// Possibly, we'll add a mode for the book-style dual screen phones later.
|
||||
// TODO: Find a better home for this!
|
||||
enum class DeviceOrientation {
|
||||
Landscape = 0,
|
||||
Portrait = 1,
|
||||
};
|
||||
|
||||
// Workaround for X header, ugh.
|
||||
#undef Opposite
|
||||
|
||||
|
||||
@@ -100,26 +100,26 @@ inline bool CompareByArea(const Data& lhs, const Data& rhs) {
|
||||
return lhs.w * lhs.h > rhs.w * rhs.h;
|
||||
}
|
||||
|
||||
std::vector<Data> Bucket::Resolve(int image_width, Image &dest) {
|
||||
std::vector<Data> Bucket::Resolve(int image_width, Image *dest) {
|
||||
// Place all the little images - whatever they are.
|
||||
// Uses greedy fill algorithm. Slow but works surprisingly well, CPUs are fast.
|
||||
ImageU8 masq;
|
||||
masq.resize(image_width, 1);
|
||||
dest.resize(image_width, 1);
|
||||
dest->resize(image_width, 1);
|
||||
std::sort(data.begin(), data.end(), CompareByArea);
|
||||
for (int i = 0; i < (int)data.size(); i++) {
|
||||
if ((i + 1) % 2000 == 0) {
|
||||
printf("Resolving (%i / %i)\n", i, (int)data.size());
|
||||
// printf("Resolving (%i / %i)\n", i, (int)data.size());
|
||||
}
|
||||
int idx = (int)data[i].w;
|
||||
int idy = (int)data[i].h;
|
||||
if (idx > 1 && idy > 1) {
|
||||
assert(idx <= image_width);
|
||||
for (int ty = 0; ty < 2047; ty++) {
|
||||
if (ty + idy + 1 > (int)dest.height()) {
|
||||
for (int ty = 0; ty < 2047; ty++) { // TODO: Maybe remove this limit?
|
||||
if (ty + idy + 1 > (int)dest->height()) {
|
||||
// Every 16 lines of new space needed, grow the image.
|
||||
masq.resize(image_width, ty + idy + 16);
|
||||
dest.resize(image_width, ty + idy + 16);
|
||||
dest->resize(image_width, ty + idy + 16);
|
||||
}
|
||||
// Brute force packing.
|
||||
int sz = (int)data[i].w;
|
||||
@@ -160,7 +160,7 @@ std::vector<Data> Bucket::Resolve(int image_width, Image &dest) {
|
||||
|
||||
// Actually copy the image data in place, after doing the layout.
|
||||
for (int i = 0; i < (int)data.size(); i++) {
|
||||
dest.copyfrom(images[i], data[i].sx, data[i].sy, data[i].redToWhiteAlpha);
|
||||
dest->copyfrom(images[i], data[i].sx, data[i].sy, data[i].redToWhiteAlpha);
|
||||
}
|
||||
|
||||
return data;
|
||||
|
||||
@@ -46,6 +46,12 @@ struct Image {
|
||||
Image(Image &&) = default;
|
||||
Image &operator=(Image &&) = default;
|
||||
|
||||
void clear() {
|
||||
dat.clear();
|
||||
w = 0;
|
||||
h = 0;
|
||||
}
|
||||
|
||||
float scale = 1.0f;
|
||||
|
||||
int w = 0;
|
||||
@@ -118,7 +124,7 @@ struct Bucket {
|
||||
data.emplace_back(dat);
|
||||
}
|
||||
void AddImage(Image &&img, int id);
|
||||
std::vector<Data> Resolve(int image_width, Image &dest);
|
||||
std::vector<Data> Resolve(int image_width, Image *dest);
|
||||
};
|
||||
|
||||
AtlasImage ToAtlasImage(int id, std::string_view name, float tw, float th, const std::vector<Data> &results);
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
#include "Common/Common.h"
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Common/Render/TextureAtlas.h"
|
||||
#include "Common/Data/Hash/Hash.h"
|
||||
|
||||
// NOTE: Make sure the java flags match.
|
||||
enum class FontStyleFlags : u8 {
|
||||
Default = 0,
|
||||
Bold = 1,
|
||||
Light = 2,
|
||||
Italic = 16,
|
||||
Underline = 32, // Future use.
|
||||
Strikethrough = 64, // Future use.
|
||||
};
|
||||
ENUM_CLASS_BITOPS(FontStyleFlags);
|
||||
|
||||
enum class FontFamily : u8 {
|
||||
SansSerif = 1,
|
||||
Fixed = 2,
|
||||
};
|
||||
|
||||
struct FontStyle {
|
||||
FontStyle() {}
|
||||
constexpr FontStyle(FontFamily _family, int size, FontStyleFlags _flags) : family(_family), sizePts(size), flags(_flags) {}
|
||||
|
||||
u16 sizePts = 0;
|
||||
FontFamily family = FontFamily::SansSerif;
|
||||
FontStyleFlags flags = FontStyleFlags::Default;
|
||||
};
|
||||
|
||||
inline constexpr bool operator<(const FontStyle &a, const FontStyle &b) {
|
||||
if (a.family != b.family)
|
||||
return a.family < b.family;
|
||||
if (a.sizePts != b.sizePts)
|
||||
return a.sizePts < b.sizePts;
|
||||
return a.flags < b.flags;
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "ppsspp_config.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include "Common/System/Display.h"
|
||||
#include "Common/GPU/thin3d.h"
|
||||
#include "Common/Data/Hash/Hash.h"
|
||||
@@ -13,6 +14,7 @@
|
||||
#include "Common/Render/Text/draw_text_qt.h"
|
||||
#include "Common/Render/Text/draw_text_android.h"
|
||||
#include "Common/Render/Text/draw_text_sdl.h"
|
||||
#include "Common/StringUtils.h"
|
||||
|
||||
TextDrawer::TextDrawer(Draw::DrawContext *draw) : draw_(draw) {
|
||||
// These probably shouldn't be state.
|
||||
@@ -47,7 +49,7 @@ void TextDrawer::DrawString(DrawBuffer &target, std::string_view str, float x, f
|
||||
return;
|
||||
}
|
||||
|
||||
CacheKey key{ std::string(str), fontHash_ };
|
||||
const CacheKeyType key{ std::string(str), fontStyle_ };
|
||||
target.Flush(true);
|
||||
|
||||
TextStringEntry *entry;
|
||||
@@ -124,7 +126,7 @@ void TextDrawer::MeasureString(std::string_view str, float *w, float *h) {
|
||||
return;
|
||||
}
|
||||
|
||||
CacheKey key{ std::string(str), fontHash_ };
|
||||
const CacheKeyType key{std::string(str), fontStyle_};
|
||||
|
||||
TextMeasureEntry *entry;
|
||||
auto iter = sizeCache_.find(key);
|
||||
@@ -204,7 +206,7 @@ void TextDrawer::ClearCache() {
|
||||
}
|
||||
cache_.clear();
|
||||
sizeCache_.clear();
|
||||
fontHash_ = 0;
|
||||
fontStyle_ = {};
|
||||
}
|
||||
|
||||
void TextDrawer::OncePerFrame() {
|
||||
@@ -271,3 +273,80 @@ TextDrawer *TextDrawer::Create(Draw::DrawContext *draw) {
|
||||
}
|
||||
return drawer;
|
||||
}
|
||||
|
||||
struct FontDesc {
|
||||
FontFamily family;
|
||||
FontStyleFlags flags;
|
||||
std::string_view fontName;
|
||||
std::string_view filename;
|
||||
};
|
||||
|
||||
// Append ".ttf" to get the actual filenames.
|
||||
static const FontDesc g_fontDescs[] = {
|
||||
{FontFamily::SansSerif, FontStyleFlags::Default, "Roboto Condensed", "Roboto_Condensed-Regular"},
|
||||
{FontFamily::SansSerif, FontStyleFlags::Bold, "Roboto Condensed", "Roboto_Condensed-Bold"},
|
||||
{FontFamily::SansSerif, FontStyleFlags::Italic, "Roboto Condensed", "Roboto_Condensed-Italic"},
|
||||
{FontFamily::SansSerif, FontStyleFlags::Light, "Roboto Condensed", "Roboto_Condensed-Light"},
|
||||
{FontFamily::Fixed, FontStyleFlags::Default, "Inconsolata", "Inconsolata-Regular"},
|
||||
};
|
||||
|
||||
std::map<FontFamily, std::string> g_fontOverrides;
|
||||
|
||||
void SetFontNameOverride(FontFamily family, std::string_view overrideFont) {
|
||||
g_fontOverrides[family] = std::string(overrideFont);
|
||||
}
|
||||
|
||||
std::vector<std::string> GetAllFontFilenames() {
|
||||
std::vector<std::string> vec;
|
||||
for (const auto &desc : g_fontDescs) {
|
||||
vec.emplace_back(desc.filename);
|
||||
}
|
||||
// uniquify idiom
|
||||
std::sort(vec.begin(), vec.end());
|
||||
vec.erase(std::unique(vec.begin(), vec.end()), vec.end());
|
||||
return vec;
|
||||
}
|
||||
|
||||
std::string GetFilenameForFontStyle(const FontStyle &font) {
|
||||
for (const auto &desc : g_fontDescs) {
|
||||
if (desc.flags == font.flags && desc.family == font.family) {
|
||||
return std::string(desc.filename);
|
||||
}
|
||||
}
|
||||
|
||||
// If nothing matched, just return the first font of the requested family.
|
||||
for (const auto &desc : g_fontDescs) {
|
||||
if (desc.family == font.family) {
|
||||
return std::string(desc.filename);
|
||||
}
|
||||
}
|
||||
|
||||
_dbg_assert_(false);
|
||||
return "";
|
||||
}
|
||||
|
||||
std::string GetFontNameForFontStyle(const FontStyle &font, FontStyleFlags *outFlags) {
|
||||
auto override = g_fontOverrides.find(font.family);
|
||||
if (override != g_fontOverrides.end()) {
|
||||
*outFlags = font.flags;
|
||||
return override->second;
|
||||
}
|
||||
|
||||
for (const auto &desc : g_fontDescs) {
|
||||
if (desc.flags == font.flags && desc.family == font.family) {
|
||||
*outFlags = font.flags;
|
||||
return std::string(desc.fontName);
|
||||
}
|
||||
}
|
||||
// If nothing matched, just return the first font of the requested family.
|
||||
for (const auto &desc : g_fontDescs) {
|
||||
if (desc.family == font.family) {
|
||||
// Return the flags of the font we found.
|
||||
// TODO: Some platforms can actually synthesize traits (bold, italic) if missing.
|
||||
*outFlags = desc.flags;
|
||||
return std::string(desc.fontName);
|
||||
}
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// draw_text
|
||||
|
||||
// Uses system fonts to draw text.
|
||||
// Uses system fonts to draw text.
|
||||
// Platform support will be added over time, initially just Win32.
|
||||
|
||||
// Caches strings in individual textures. Might later combine them into a big one
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
#include "Common/Data/Text/WrapText.h"
|
||||
#include "Common/Render/DrawBuffer.h"
|
||||
#include "Common/Render/Text/Font.h"
|
||||
|
||||
namespace Draw {
|
||||
class DrawContext;
|
||||
@@ -45,8 +46,8 @@ public:
|
||||
virtual ~TextDrawer() = default;
|
||||
|
||||
virtual bool IsReady() const { return true; }
|
||||
virtual uint32_t SetFont(const char *fontName, int size, int flags) = 0;
|
||||
virtual void SetFont(uint32_t fontHandle) = 0; // Shortcut once you've set the font once.
|
||||
virtual void SetOrCreateFont(const FontStyle &style) = 0;
|
||||
|
||||
void SetFontScale(float xscale, float yscale);
|
||||
void MeasureString(std::string_view str, float *w, float *h);
|
||||
void MeasureStringRect(std::string_view str, const Bounds &bounds, float *w, float *h, int align = ALIGN_TOPLEFT);
|
||||
@@ -112,17 +113,7 @@ protected:
|
||||
return (a << 24) | (r << 16) | (g << 8) | b;
|
||||
}
|
||||
|
||||
struct CacheKey {
|
||||
bool operator < (const CacheKey &other) const {
|
||||
if (fontHash < other.fontHash)
|
||||
return true;
|
||||
if (fontHash > other.fontHash)
|
||||
return false;
|
||||
return text < other.text;
|
||||
}
|
||||
std::string text;
|
||||
uint32_t fontHash;
|
||||
};
|
||||
typedef std::pair<std::string, FontStyle> CacheKeyType;
|
||||
|
||||
Draw::DrawContext *draw_;
|
||||
|
||||
@@ -131,11 +122,10 @@ protected:
|
||||
float fontScaleY_ = 1.0f;
|
||||
float dpiScale_ = 1.0f;
|
||||
bool ignoreGlobalDpi_ = false;
|
||||
FontStyle fontStyle_{};
|
||||
|
||||
uint32_t fontHash_ = 0;
|
||||
|
||||
std::map<CacheKey, std::unique_ptr<TextStringEntry>> cache_;
|
||||
std::map<CacheKey, std::unique_ptr<TextMeasureEntry>> sizeCache_;
|
||||
std::map<CacheKeyType, std::unique_ptr<TextStringEntry>> cache_;
|
||||
std::map<CacheKeyType, std::unique_ptr<TextMeasureEntry>> sizeCache_;
|
||||
};
|
||||
|
||||
class TextDrawerWordWrapper : public WordWrapper {
|
||||
@@ -148,3 +138,13 @@ protected:
|
||||
|
||||
TextDrawer *drawer_;
|
||||
};
|
||||
|
||||
// The backends can use this to query the filenames of the fonts.
|
||||
// Some backends want to just load all the fonts, just pass all.
|
||||
// Note that the ttf file extension is included in the output.
|
||||
std::vector<std::string> GetAllFontFilenames();
|
||||
std::string GetFilenameForFontStyle(const FontStyle &font);
|
||||
std::string GetFontNameForFontStyle(const FontStyle &font, FontStyleFlags *outFlags);
|
||||
|
||||
// Some languages use an override font, set via ini file.
|
||||
void SetFontNameOverride(FontFamily family, std::string_view overrideFont);
|
||||
|
||||
@@ -22,8 +22,10 @@ TextDrawerAndroid::TextDrawerAndroid(Draw::DrawContext *draw) : TextDrawer(draw)
|
||||
jclass localClass = findClass(textRendererClassName);
|
||||
cls_textRenderer = reinterpret_cast<jclass>(env->NewGlobalRef(localClass));
|
||||
if (cls_textRenderer) {
|
||||
method_measureText = env->GetStaticMethodID(cls_textRenderer, "measureText", "(Ljava/lang/String;D)I");
|
||||
method_renderText = env->GetStaticMethodID(cls_textRenderer, "renderText", "(Ljava/lang/String;D)[I");
|
||||
method_allocFont = env->GetStaticMethodID(cls_textRenderer, "allocFont", "(Ljava/lang/String;)I");
|
||||
method_freeAllFonts = env->GetStaticMethodID(cls_textRenderer, "freeAllFonts", "()V");
|
||||
method_measureText = env->GetStaticMethodID(cls_textRenderer, "measureText", "(Ljava/lang/String;ID)I");
|
||||
method_renderText = env->GetStaticMethodID(cls_textRenderer, "renderText", "(Ljava/lang/String;ID)[I");
|
||||
} else {
|
||||
ERROR_LOG(Log::G3D, "Failed to find class: '%s'", textRendererClassName);
|
||||
}
|
||||
@@ -44,57 +46,56 @@ bool TextDrawerAndroid::IsReady() const {
|
||||
return cls_textRenderer != nullptr && method_measureText != nullptr && method_renderText != nullptr;
|
||||
}
|
||||
|
||||
uint32_t TextDrawerAndroid::SetFont(const char *fontName, int size, int flags) {
|
||||
void TextDrawerAndroid::SetOrCreateFont(const FontStyle &style) {
|
||||
// We will only use the default font but just for consistency let's still involve
|
||||
// the font name.
|
||||
uint32_t fontHash = fontName ? hash::Adler32((const uint8_t *)fontName, strlen(fontName)) : 1337;
|
||||
fontHash ^= size;
|
||||
fontHash ^= flags << 10;
|
||||
|
||||
auto iter = fontMap_.find(fontHash);
|
||||
auto iter = fontMap_.find(style);
|
||||
if (iter != fontMap_.end()) {
|
||||
fontHash_ = fontHash;
|
||||
return fontHash;
|
||||
fontStyle_ = style;
|
||||
return;
|
||||
}
|
||||
|
||||
std::string filename = GetFilenameForFontStyle(style) + ".ttf";
|
||||
auto fontIter = allocatedFonts_.find(filename);
|
||||
if (fontIter == allocatedFonts_.end()) {
|
||||
auto env = getEnv();
|
||||
jstring jstr = env->NewStringUTF(filename.c_str());
|
||||
int fontId = env->CallStaticIntMethod(cls_textRenderer, method_allocFont, jstr);
|
||||
env->DeleteLocalRef(jstr);
|
||||
|
||||
if (fontId >= 0) {
|
||||
allocatedFonts_[filename] = fontId;
|
||||
}
|
||||
}
|
||||
|
||||
AndroidFontEntry entry{};
|
||||
entry.font = allocatedFonts_[filename];
|
||||
entry.size = 1.25f * style.sizePts / dpiScale_; // Not sure why this formula works.
|
||||
|
||||
// Just chose a factor that looks good, don't know what unit size is in anyway.
|
||||
AndroidFontEntry entry{};
|
||||
entry.size = ((float)size * 1.4f) / dpiScale_;
|
||||
fontMap_[fontHash] = entry;
|
||||
fontHash_ = fontHash;
|
||||
return fontHash;
|
||||
}
|
||||
|
||||
void TextDrawerAndroid::SetFont(uint32_t fontHandle) {
|
||||
uint32_t fontHash = fontHandle;
|
||||
auto iter = fontMap_.find(fontHash);
|
||||
if (iter != fontMap_.end()) {
|
||||
fontHash_ = fontHandle;
|
||||
} else {
|
||||
ERROR_LOG(Log::G3D, "Invalid font handle %08x", fontHandle);
|
||||
}
|
||||
fontMap_[style] = entry;
|
||||
fontStyle_ = style;
|
||||
}
|
||||
|
||||
void TextDrawerAndroid::MeasureStringInternal(std::string_view str, float *w, float *h) {
|
||||
float scaledSize = 14;
|
||||
auto iter = fontMap_.find(fontHash_);
|
||||
if (iter != fontMap_.end()) {
|
||||
scaledSize = iter->second.size;
|
||||
} else {
|
||||
auto iter = fontMap_.find(fontStyle_);
|
||||
if (iter == fontMap_.end()) {
|
||||
ERROR_LOG(Log::G3D, "Missing font");
|
||||
*w = 1.0f;
|
||||
*h = 1.0f;
|
||||
return;
|
||||
}
|
||||
|
||||
std::string text;
|
||||
ConvertUTF8ToJavaModifiedUTF8(&text, str);
|
||||
|
||||
auto env = getEnv();
|
||||
jstring jstr = env->NewStringUTF(text.c_str());
|
||||
uint32_t size = env->CallStaticIntMethod(cls_textRenderer, method_measureText, jstr, scaledSize);
|
||||
uint32_t size = env->CallStaticIntMethod(cls_textRenderer, method_measureText, jstr, iter->second.font, iter->second.size);
|
||||
env->DeleteLocalRef(jstr);
|
||||
|
||||
*w = (float)(size >> 16);
|
||||
*h = (float)(size & 0xFFFF);
|
||||
|
||||
// WARN_LOG(Log::G3D, "Measure Modified: '%.*s' size: %fx%f", (int)text.length(), text.data(), *w, *h);
|
||||
}
|
||||
|
||||
bool TextDrawerAndroid::DrawStringBitmap(std::vector<uint8_t> &bitmapData, TextStringEntry &entry, Draw::DataFormat texFormat, std::string_view str, int align, bool fullColor) {
|
||||
@@ -103,12 +104,10 @@ bool TextDrawerAndroid::DrawStringBitmap(std::vector<uint8_t> &bitmapData, TextS
|
||||
return false;
|
||||
}
|
||||
|
||||
float size = 0.0f;
|
||||
auto iter = fontMap_.find(fontHash_);
|
||||
if (iter != fontMap_.end()) {
|
||||
size = iter->second.size;
|
||||
} else {
|
||||
auto iter = fontMap_.find(fontStyle_);
|
||||
if (iter == fontMap_.end()) {
|
||||
ERROR_LOG(Log::G3D, "Missing font");
|
||||
return false;
|
||||
}
|
||||
|
||||
auto env = getEnv();
|
||||
@@ -117,7 +116,7 @@ bool TextDrawerAndroid::DrawStringBitmap(std::vector<uint8_t> &bitmapData, TextS
|
||||
ConvertUTF8ToJavaModifiedUTF8(&text, str);
|
||||
jstring jstr = env->NewStringUTF(text.c_str());
|
||||
|
||||
uint32_t textSize = env->CallStaticIntMethod(cls_textRenderer, method_measureText, jstr, size);
|
||||
uint32_t textSize = env->CallStaticIntMethod(cls_textRenderer, method_measureText, jstr, iter->second.font, iter->second.size);
|
||||
int imageWidth = (short)(textSize >> 16);
|
||||
int imageHeight = (short)(textSize & 0xFFFF);
|
||||
if (imageWidth <= 0)
|
||||
@@ -126,7 +125,8 @@ bool TextDrawerAndroid::DrawStringBitmap(std::vector<uint8_t> &bitmapData, TextS
|
||||
imageHeight = 1;
|
||||
// WARN_LOG(Log::G3D, "Text: '%.*s' (%02x)", (int)str.length(), str.data(), str[0]);
|
||||
|
||||
jintArray imageData = (jintArray)env->CallStaticObjectMethod(cls_textRenderer, method_renderText, jstr, size);
|
||||
// TODO: Handle bold/italic
|
||||
jintArray imageData = (jintArray)env->CallStaticObjectMethod(cls_textRenderer, method_renderText, jstr, iter->second.font, iter->second.size);
|
||||
env->DeleteLocalRef(jstr);
|
||||
|
||||
entry.texture = nullptr;
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include <jni.h>
|
||||
|
||||
struct AndroidFontEntry {
|
||||
int font;
|
||||
float size;
|
||||
};
|
||||
|
||||
@@ -19,8 +20,7 @@ public:
|
||||
~TextDrawerAndroid();
|
||||
|
||||
bool IsReady() const override;
|
||||
uint32_t SetFont(const char *fontName, int size, int flags) override;
|
||||
void SetFont(uint32_t fontHandle) override; // Shortcut once you've set the font once.
|
||||
void SetOrCreateFont(const FontStyle &style) override;
|
||||
bool DrawStringBitmap(std::vector<uint8_t> &bitmapData, TextStringEntry &entry, Draw::DataFormat texFormat, std::string_view str, int align, bool fullColor) override;
|
||||
|
||||
protected:
|
||||
@@ -32,10 +32,13 @@ protected:
|
||||
private:
|
||||
// JNI functions
|
||||
jclass cls_textRenderer;
|
||||
jmethodID method_allocFont;
|
||||
jmethodID method_freeAllFonts;
|
||||
jmethodID method_measureText;
|
||||
jmethodID method_renderText;
|
||||
|
||||
std::map<uint32_t, AndroidFontEntry> fontMap_;
|
||||
std::map<FontStyle, AndroidFontEntry> fontMap_;
|
||||
std::map<std::string, int> allocatedFonts_;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -16,8 +16,7 @@ public:
|
||||
TextDrawerCocoa(Draw::DrawContext *draw);
|
||||
~TextDrawerCocoa();
|
||||
|
||||
uint32_t SetFont(const char *fontName, int size, int flags) override;
|
||||
void SetFont(uint32_t fontHandle) override; // Shortcut once you've set the font once.
|
||||
void SetOrCreateFont(const FontStyle &style) override;
|
||||
bool DrawStringBitmap(std::vector<uint8_t> &bitmapData, TextStringEntry &entry, Draw::DataFormat texFormat, std::string_view str, int align, bool fullColor) override;
|
||||
|
||||
protected:
|
||||
@@ -27,7 +26,7 @@ protected:
|
||||
void ClearFonts() override;
|
||||
|
||||
TextDrawerContext *ctx_;
|
||||
std::map<uint32_t, std::unique_ptr<TextDrawerFontContext>> fontMap_;
|
||||
std::map<FontStyle, std::unique_ptr<TextDrawerFontContext>> fontMap_;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -41,24 +41,73 @@ enum {
|
||||
|
||||
class TextDrawerFontContext {
|
||||
public:
|
||||
TextDrawerFontContext(const FontStyle &_style, float _dpiScale) : style(_style), dpiScale(_dpiScale) {
|
||||
FontStyleFlags styleFlags = style.flags;
|
||||
std::string fontName = GetFontNameForFontStyle(style, &styleFlags);
|
||||
|
||||
// Create an attributed string with string and font information
|
||||
CGFloat fontSize = ceilf((style.sizePts / dpiScale) * 1.25f);
|
||||
INFO_LOG(Log::G3D, "Creating cocoa typeface '%s' size %d (effective size %0.1f)", fontName.c_str(), style.sizePts, fontSize);
|
||||
|
||||
CTFontSymbolicTraits traits = 0;
|
||||
if (styleFlags & FontStyleFlags::Bold) traits |= kCTFontTraitBold;
|
||||
if (styleFlags & FontStyleFlags::Italic) traits |= kCTFontTraitItalic;
|
||||
|
||||
CTFontRef base = CTFontCreateWithName(CFStringCreateWithCString(kCFAllocatorDefault, fontName.c_str(), kCFStringEncodingUTF8), fontSize, nil);
|
||||
CTFontRef font = CTFontCreateCopyWithSymbolicTraits(base, fontSize, NULL, traits, traits); // desired & mask
|
||||
if (!font) {
|
||||
// Skip the traits.
|
||||
font = base;
|
||||
} else {
|
||||
CFRelease(base);
|
||||
}
|
||||
|
||||
_dbg_assert_(font != nil);
|
||||
// CTFontRef font = CTFontCreateUIFontForLanguage(kCTFontUIFontSystem, fontSize, nil);
|
||||
attributes = @{
|
||||
(__bridge id)kCTFontAttributeName: (__bridge id)font,
|
||||
(__bridge id)kCTForegroundColorFromContextAttributeName: (__bridge id)kCFBooleanTrue,
|
||||
};
|
||||
CFRelease(font);
|
||||
}
|
||||
~TextDrawerFontContext() {
|
||||
Destroy();
|
||||
}
|
||||
void Destroy() {
|
||||
}
|
||||
|
||||
void Create() {
|
||||
// Register font with CoreText
|
||||
// We only need to do this once.
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
NSURL *fontURL = [PPSSPP_FONT_BUNDLE URLForResource:@"Roboto-Condensed" withExtension:@"ttf" subdirectory:@"assets"];
|
||||
NSDictionary* attributes = nil;
|
||||
std::string fname;
|
||||
|
||||
FontStyle style;
|
||||
float dpiScale;
|
||||
};
|
||||
|
||||
TextDrawerCocoa::TextDrawerCocoa(Draw::DrawContext *draw) : TextDrawer(draw) {
|
||||
// Register fonts with CoreText
|
||||
// We only need to do this once.
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
std::vector<std::string> allFonts = GetAllFontFilenames();
|
||||
|
||||
for (const auto &fontName : allFonts) {
|
||||
// Convert C++ string to NSString
|
||||
NSString *fontFileName = [NSString stringWithUTF8String:fontName.c_str()];
|
||||
|
||||
// Get the font URL from the bundle
|
||||
NSURL *fontURL = [PPSSPP_FONT_BUNDLE URLForResource:fontFileName
|
||||
withExtension:@"ttf"
|
||||
subdirectory:@"assets"];
|
||||
if (!fontURL) {
|
||||
NSLog(@"Font URL not found!");
|
||||
return;
|
||||
NSLog(@"Font URL not found for %@", fontFileName);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Optional: Print font descriptors for debugging
|
||||
CFArrayRef descs = CTFontManagerCreateFontDescriptorsFromURL((__bridge CFURLRef)fontURL);
|
||||
if (descs) {
|
||||
CFIndex count = CFArrayGetCount(descs);
|
||||
NSLog(@"Found %ld font descriptor(s)", count);
|
||||
NSLog(@"Found %ld font descriptor(s) for %@", count, fontFileName);
|
||||
|
||||
for (CFIndex i = 0; i < count; ++i) {
|
||||
CTFontDescriptorRef desc = (CTFontDescriptorRef)CFArrayGetValueAtIndex(descs, i);
|
||||
@@ -75,33 +124,13 @@ public:
|
||||
|
||||
CFRelease(descs);
|
||||
} else {
|
||||
NSLog(@"Failed to retrieve font descriptors");
|
||||
NSLog(@"Failed to retrieve font descriptors for %@", fontFileName);
|
||||
}
|
||||
CTFontManagerRegisterFontsForURL((CFURLRef)fontURL, kCTFontManagerScopeProcess, NULL);
|
||||
});
|
||||
|
||||
// Create an attributed string with string and font information
|
||||
CGFloat fontSize = ceilf((height / dpiScale) * 1.25f);
|
||||
INFO_LOG(Log::G3D, "Creating cocoa typeface '%s' size %d (effective size %0.1f)", APPLE_FONT, height, fontSize);
|
||||
CTFontRef font = CTFontCreateWithName(CFSTR(APPLE_FONT), fontSize, nil);
|
||||
// CTFontRef font = CTFontCreateUIFontForLanguage(kCTFontUIFontSystem, fontSize, nil);
|
||||
attributes = [NSDictionary dictionaryWithObjectsAndKeys:
|
||||
(__bridge id)font, kCTFontAttributeName,
|
||||
kCFBooleanTrue, kCTForegroundColorFromContextAttributeName, // Lets us specify the color later.
|
||||
nil];
|
||||
CFRelease(font);
|
||||
}
|
||||
void Destroy() {
|
||||
}
|
||||
|
||||
NSDictionary* attributes = nil;
|
||||
std::string fname;
|
||||
int height;
|
||||
int bold;
|
||||
float dpiScale;
|
||||
};
|
||||
|
||||
TextDrawerCocoa::TextDrawerCocoa(Draw::DrawContext *draw) : TextDrawer(draw) {
|
||||
// Register the font
|
||||
CTFontManagerRegisterFontsForURL((__bridge CFURLRef)fontURL, kCTFontManagerScopeProcess, NULL);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
TextDrawerCocoa::~TextDrawerCocoa() {
|
||||
@@ -110,40 +139,17 @@ TextDrawerCocoa::~TextDrawerCocoa() {
|
||||
}
|
||||
|
||||
// TODO: Share with other backends.
|
||||
uint32_t TextDrawerCocoa::SetFont(const char *fontName, int size, int flags) {
|
||||
uint32_t fontHash = fontName ? hash::Adler32((const uint8_t *)fontName, strlen(fontName)) : 0;
|
||||
fontHash ^= size;
|
||||
fontHash ^= flags << 10;
|
||||
|
||||
auto iter = fontMap_.find(fontHash);
|
||||
void TextDrawerCocoa::SetOrCreateFont(const FontStyle &style) {
|
||||
auto iter = fontMap_.find(style);
|
||||
if (iter != fontMap_.end()) {
|
||||
fontHash_ = fontHash;
|
||||
return fontHash;
|
||||
fontStyle_ = style;
|
||||
return;
|
||||
}
|
||||
|
||||
std::string fname;
|
||||
if (fontName)
|
||||
fname = fontName;
|
||||
else
|
||||
fname = APPLE_FONT;
|
||||
TextDrawerFontContext *font = new TextDrawerFontContext(style, dpiScale_);
|
||||
|
||||
TextDrawerFontContext *font = new TextDrawerFontContext();
|
||||
font->bold = false;
|
||||
font->height = size;
|
||||
font->fname = fname;
|
||||
font->dpiScale = dpiScale_;
|
||||
font->Create();
|
||||
|
||||
fontMap_[fontHash] = std::unique_ptr<TextDrawerFontContext>(font);
|
||||
fontHash_ = fontHash;
|
||||
return fontHash;
|
||||
}
|
||||
|
||||
void TextDrawerCocoa::SetFont(uint32_t fontHandle) {
|
||||
auto iter = fontMap_.find(fontHandle);
|
||||
if (iter != fontMap_.end()) {
|
||||
fontHash_ = fontHandle;
|
||||
}
|
||||
fontMap_[style] = std::unique_ptr<TextDrawerFontContext>(font);
|
||||
fontStyle_ = style;
|
||||
}
|
||||
|
||||
void TextDrawerCocoa::ClearFonts() {
|
||||
@@ -156,7 +162,7 @@ void TextDrawerCocoa::ClearFonts() {
|
||||
|
||||
void TextDrawerCocoa::MeasureStringInternal(std::string_view str, float *w, float *h) {
|
||||
// INFO_LOG(Log::System, "Measuring %.*s", (int)str.length(), str.data());
|
||||
auto iter = fontMap_.find(fontHash_);
|
||||
auto iter = fontMap_.find(fontStyle_);
|
||||
NSDictionary *attributes = nil;
|
||||
if (iter != fontMap_.end()) {
|
||||
attributes = iter->second->attributes;
|
||||
@@ -191,7 +197,7 @@ bool TextDrawerCocoa::DrawStringBitmap(std::vector<uint8_t> &bitmapData, TextStr
|
||||
return false;
|
||||
}
|
||||
|
||||
auto iter = fontMap_.find(fontHash_);
|
||||
auto iter = fontMap_.find(fontStyle_);
|
||||
if (iter == fontMap_.end()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include "Common/Data/Hash/Hash.h"
|
||||
#include "Common/Data/Text/WrapText.h"
|
||||
#include "Common/Data/Encoding/Utf8.h"
|
||||
#include "Common/File/VFS/VFS.h"
|
||||
#include "Common/Render/Text/draw_text.h"
|
||||
#include "Common/Render/Text/draw_text_qt.h"
|
||||
|
||||
@@ -14,45 +15,66 @@
|
||||
#include <QtGui/QImage>
|
||||
#include <QtGui/QPainter>
|
||||
#include <QtGui/QFontMetrics>
|
||||
#include <QFontDatabase>
|
||||
#include <QtOpenGL/QGLWidget>
|
||||
|
||||
TextDrawerQt::TextDrawerQt(Draw::DrawContext *draw) : TextDrawer(draw) {}
|
||||
TextDrawerQt::TextDrawerQt(Draw::DrawContext *draw) : TextDrawer(draw) {
|
||||
// Add all fonts we ship with.
|
||||
|
||||
std::vector<std::string> filenames = GetAllFontFilenames();
|
||||
|
||||
for (const auto &iter : filenames) {
|
||||
std::string fn = "assets/" + iter + ".ttf";
|
||||
size_t fontSize = 0;
|
||||
uint8_t *fontData = g_VFS.ReadFile(fn.c_str(), &fontSize);
|
||||
if (fontData) {
|
||||
int fontID = QFontDatabase::addApplicationFontFromData(QByteArray((const char *)fontData, fontSize));
|
||||
delete[] fontData;
|
||||
QStringList fontsFound = QFontDatabase::applicationFontFamilies(fontID);
|
||||
if (fontsFound.size() == 0) {
|
||||
WARN_LOG(Log::G3D, "Failed to load font from %s", fn.c_str());
|
||||
} else {
|
||||
for (const auto &f : fontsFound) {
|
||||
WARN_LOG(Log::G3D, "Loaded font %s from %s", f.toUtf8().constData(), fn.c_str());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ERROR_LOG(Log::G3D, "Failed to load font file %s", fn.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TextDrawerQt::~TextDrawerQt() {
|
||||
ClearCache();
|
||||
ClearFonts();
|
||||
}
|
||||
|
||||
uint32_t TextDrawerQt::SetFont(const char *fontName, int size, int flags) {
|
||||
uint32_t fontHash = fontName && strlen(fontName) ? hash::Adler32((const uint8_t *)fontName, strlen(fontName)) : 0;
|
||||
fontHash ^= size;
|
||||
fontHash ^= flags << 10;
|
||||
|
||||
auto iter = fontMap_.find(fontHash);
|
||||
void TextDrawerQt::SetOrCreateFont(const FontStyle &style) {
|
||||
auto iter = fontMap_.find(style);
|
||||
if (iter != fontMap_.end()) {
|
||||
fontHash_ = fontHash;
|
||||
return fontHash;
|
||||
fontStyle_ = style;
|
||||
return;
|
||||
}
|
||||
|
||||
QFont *font = fontName ? new QFont(fontName) : new QFont();
|
||||
font->setPixelSize((int)((size + 6) / dpiScale_));
|
||||
fontMap_[fontHash] = font;
|
||||
fontHash_ = fontHash;
|
||||
return fontHash;
|
||||
}
|
||||
FontStyleFlags styleFlags;
|
||||
std::string fontName = GetFontNameForFontStyle(style, &styleFlags);
|
||||
|
||||
void TextDrawerQt::SetFont(uint32_t fontHandle) {
|
||||
uint32_t fontHash = fontHandle;
|
||||
auto iter = fontMap_.find(fontHash);
|
||||
if (iter != fontMap_.end()) {
|
||||
fontHash_ = fontHandle;
|
||||
} else {
|
||||
ERROR_LOG(Log::G3D, "Invalid font handle %08x", fontHandle);
|
||||
QFont *font = new QFont(fontName.c_str());
|
||||
font->setPixelSize((int)((style.sizePts + 6) / dpiScale_));
|
||||
font->setBold(styleFlags & FontStyleFlags::Bold);
|
||||
if (styleFlags & FontStyleFlags::Light) {
|
||||
font->setWeight(QFont::Light);
|
||||
}
|
||||
font->setItalic(styleFlags & FontStyleFlags::Italic);
|
||||
font->setUnderline(styleFlags & FontStyleFlags::Underline);
|
||||
font->setStrikeOut(styleFlags & FontStyleFlags::Strikethrough);
|
||||
|
||||
fontMap_[style] = font;
|
||||
fontStyle_ = style;
|
||||
}
|
||||
|
||||
void TextDrawerQt::MeasureStringInternal(std::string_view str, float *w, float *h) {
|
||||
QFont* font = fontMap_.find(fontHash_)->second;
|
||||
QFont* font = fontMap_.find(fontStyle_)->second;
|
||||
QFontMetrics fm(*font);
|
||||
QSize size = fm.size(0, QString::fromUtf8(str.data(), str.length()));
|
||||
|
||||
@@ -68,7 +90,7 @@ bool TextDrawerQt::DrawStringBitmap(std::vector<uint8_t> &bitmapData, TextString
|
||||
return false;
|
||||
}
|
||||
|
||||
QFont *font = fontMap_.find(fontHash_)->second;
|
||||
QFont *font = fontMap_.find(fontStyle_)->second;
|
||||
QFontMetrics fm(*font);
|
||||
QSize size = fm.size(0, QString::fromUtf8(str.data(), str.length()));
|
||||
QImage image((size.width() + 3) & ~3, (size.height() + 3) & ~3, QImage::Format_ARGB32_Premultiplied);
|
||||
@@ -118,7 +140,7 @@ void TextDrawerQt::ClearFonts() {
|
||||
delete iter.second;
|
||||
}
|
||||
fontMap_.clear();
|
||||
fontHash_ = 0;
|
||||
fontStyle_ = {};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -14,8 +14,7 @@ public:
|
||||
TextDrawerQt(Draw::DrawContext *draw);
|
||||
~TextDrawerQt();
|
||||
|
||||
uint32_t SetFont(const char *fontName, int size, int flags) override;
|
||||
void SetFont(uint32_t fontHandle) override; // Shortcut once you've set the font once.
|
||||
void SetOrCreateFont(const FontStyle &style) override;
|
||||
bool DrawStringBitmap(std::vector<uint8_t> &bitmapData, TextStringEntry &entry, Draw::DataFormat texFormat, std::string_view str, int align, bool fullColor) override;
|
||||
|
||||
protected:
|
||||
@@ -24,7 +23,7 @@ protected:
|
||||
|
||||
void ClearFonts() override;
|
||||
|
||||
std::map<uint32_t, QFont *> fontMap_;
|
||||
std::map<FontStyle, QFont *> fontMap_;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -187,12 +187,15 @@ void TextDrawerSDL::PrepareFallbackFonts(std::string_view locale) {
|
||||
}
|
||||
|
||||
uint32_t TextDrawerSDL::CheckMissingGlyph(std::string_view text) {
|
||||
auto iter = fontMap_.find(fontHash_);
|
||||
auto iter = fontMap_.find(fontStyle_);
|
||||
if (iter == fontMap_.end()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
TTF_Font *font = iter->second;
|
||||
if (!font) {
|
||||
return 0;
|
||||
}
|
||||
UTF8 utf8Decoded(text);
|
||||
|
||||
uint32_t missingGlyph = 0;
|
||||
@@ -245,50 +248,42 @@ int TextDrawerSDL::FindFallbackFonts(uint32_t missingGlyph, int ptSize) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
uint32_t TextDrawerSDL::SetFont(const char *fontName, int size, int flags) {
|
||||
uint32_t fontHash = fontName && strlen(fontName) ? hash::Adler32((const uint8_t *)fontName, strlen(fontName)) : 0;
|
||||
fontHash ^= size;
|
||||
fontHash ^= flags << 10;
|
||||
|
||||
auto iter = fontMap_.find(fontHash);
|
||||
void TextDrawerSDL::SetOrCreateFont(const FontStyle &style) {
|
||||
auto iter = fontMap_.find(style);
|
||||
if (iter != fontMap_.end()) {
|
||||
fontHash_ = fontHash;
|
||||
return fontHash;
|
||||
fontStyle_ = style;
|
||||
return;
|
||||
}
|
||||
|
||||
const char *useFont = fontName ? fontName : "Roboto-Condensed.ttf";
|
||||
const int ptSize = (int)((size + 6) / dpiScale_);
|
||||
std::string useFont = "assets/" + GetFilenameForFontStyle(style) + ".ttf";
|
||||
|
||||
TTF_Font *font = TTF_OpenFont(useFont, ptSize);
|
||||
|
||||
if (!font) {
|
||||
File::FileInfo fileInfo;
|
||||
g_VFS.GetFileInfo("Roboto-Condensed.ttf", &fileInfo);
|
||||
font = TTF_OpenFont(fileInfo.fullName.c_str(), ptSize);
|
||||
if (loadedFonts_.find(useFont) == loadedFonts_.end()) {
|
||||
int ptSize = static_cast<int>(style.sizePts / dpiScale_ * 1.25f);
|
||||
TTF_Font *font = TTF_OpenFont(useFont.c_str(), ptSize);
|
||||
if (!font) {
|
||||
ERROR_LOG(Log::G3D, "Failed to load font file %s", useFont.c_str());
|
||||
}
|
||||
// Still, mark it with a nullptr to avoid retrying.
|
||||
loadedFonts_[useFont] = font;
|
||||
}
|
||||
|
||||
fontMap_[fontHash] = font;
|
||||
fontHash_ = fontHash;
|
||||
return fontHash;
|
||||
}
|
||||
|
||||
void TextDrawerSDL::SetFont(uint32_t fontHandle) {
|
||||
uint32_t fontHash = fontHandle;
|
||||
auto iter = fontMap_.find(fontHash);
|
||||
if (iter != fontMap_.end()) {
|
||||
fontHash_ = fontHandle;
|
||||
} else {
|
||||
ERROR_LOG(Log::G3D, "Invalid font handle %08x", fontHandle);
|
||||
}
|
||||
fontMap_[style] = loadedFonts_[useFont];
|
||||
fontStyle_ = style;
|
||||
}
|
||||
|
||||
void TextDrawerSDL::MeasureStringInternal(std::string_view str, float *w, float *h) {
|
||||
TTF_Font *font = fontMap_.find(fontHash_)->second;
|
||||
int ptSize = TTF_FontHeight(font) / 1.35;
|
||||
TTF_Font *font = fontMap_.find(fontStyle_)->second;
|
||||
|
||||
if (!font) {
|
||||
*w = 1.0f;
|
||||
*h = 1.0f;
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t missingGlyph = CheckMissingGlyph(str);
|
||||
|
||||
if (missingGlyph) {
|
||||
int ptSize = static_cast<int>(fontStyle_.sizePts / dpiScale_ * 1.25f);
|
||||
int fallbackFont = FindFallbackFonts(missingGlyph, ptSize);
|
||||
if (fallbackFont >= 0 && fallbackFont < (int)fallbackFonts_.size()) {
|
||||
font = fallbackFonts_[fallbackFont];
|
||||
@@ -302,8 +297,8 @@ void TextDrawerSDL::MeasureStringInternal(std::string_view str, float *w, float
|
||||
std::string text(str);
|
||||
TTF_SizeUTF8(font, text.c_str(), &width, &height);
|
||||
|
||||
*w = width;
|
||||
*h = height;
|
||||
*w = (float)width;
|
||||
*h = (float)height;
|
||||
}
|
||||
|
||||
bool TextDrawerSDL::DrawStringBitmap(std::vector<uint8_t> &bitmapData, TextStringEntry &entry, Draw::DataFormat texFormat, std::string_view str, int align, bool fullColor) {
|
||||
@@ -324,14 +319,17 @@ bool TextDrawerSDL::DrawStringBitmap(std::vector<uint8_t> &bitmapData, TextStrin
|
||||
processedStr.push_back(' ');
|
||||
}
|
||||
|
||||
auto fontIter = fontMap_.find(fontHash_);
|
||||
auto fontIter = fontMap_.find(fontStyle_);
|
||||
if (fontIter == fontMap_.end()) {
|
||||
ERROR_LOG(Log::G3D, "Font hash not in map: %08x", fontHash_);
|
||||
ERROR_LOG(Log::G3D, "Font style not in map");
|
||||
return false;
|
||||
}
|
||||
|
||||
TTF_Font *font = fontIter->second;
|
||||
int ptSize = TTF_FontHeight(font) / 1.35;
|
||||
if (!font) {
|
||||
return false;
|
||||
}
|
||||
int ptSize = TTF_FontHeight(font) / 1.25;
|
||||
|
||||
uint32_t missingGlyph = CheckMissingGlyph(processedStr);
|
||||
|
||||
@@ -394,12 +392,19 @@ bool TextDrawerSDL::DrawStringBitmap(std::vector<uint8_t> &bitmapData, TextStrin
|
||||
}
|
||||
|
||||
void TextDrawerSDL::ClearFonts() {
|
||||
for (auto iter : fontMap_) {
|
||||
TTF_CloseFont(iter.second);
|
||||
for (auto iter : loadedFonts_) {
|
||||
if (iter.second) {
|
||||
TTF_CloseFont(iter.second);
|
||||
}
|
||||
}
|
||||
for (auto iter : fallbackFonts_) {
|
||||
TTF_CloseFont(iter);
|
||||
if (iter) {
|
||||
TTF_CloseFont(iter);
|
||||
}
|
||||
}
|
||||
|
||||
// We wipe all the maps, including fontMap_. Everything it contains is also in loadedFonts_ which is cleared above.
|
||||
loadedFonts_.clear();
|
||||
fontMap_.clear();
|
||||
fallbackFonts_.clear();
|
||||
}
|
||||
|
||||
@@ -19,8 +19,7 @@ public:
|
||||
TextDrawerSDL(Draw::DrawContext *draw);
|
||||
~TextDrawerSDL();
|
||||
|
||||
uint32_t SetFont(const char *fontName, int size, int flags) override;
|
||||
void SetFont(uint32_t fontHandle) override; // Shortcut once you've set the font once.
|
||||
void SetOrCreateFont(const FontStyle &style) override;
|
||||
bool DrawStringBitmap(std::vector<uint8_t> &bitmapData, TextStringEntry &entry, Draw::DataFormat texFormat, std::string_view str, int align, bool fullColor) override;
|
||||
|
||||
protected:
|
||||
@@ -34,7 +33,11 @@ private:
|
||||
uint32_t CheckMissingGlyph(std::string_view text);
|
||||
int FindFallbackFonts(uint32_t missingGlyph, int ptSize);
|
||||
|
||||
std::map<uint32_t, TTF_Font *> fontMap_;
|
||||
// NOTE: This can contain dupes. The below doesn't.
|
||||
std::map<FontStyle, TTF_Font *> fontMap_;
|
||||
|
||||
// These are the ones we free. fontMap points into this map.
|
||||
std::map<std::string, TTF_Font *> loadedFonts_;
|
||||
|
||||
std::vector<TTF_Font *> fallbackFonts_;
|
||||
std::vector<std::pair<std::string, int>> fallbackFontPaths_; // path and font face index
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "ppsspp_config.h"
|
||||
|
||||
#include "Common/System/Display.h"
|
||||
#include "Common/GPU/thin3d.h"
|
||||
#include "Common/Data/Hash/Hash.h"
|
||||
@@ -6,17 +7,21 @@
|
||||
#include "Common/Data/Encoding/Utf8.h"
|
||||
#include "Common/Render/Text/draw_text.h"
|
||||
#include "Common/Render/Text/draw_text_uwp.h"
|
||||
|
||||
#include "Common/File/VFS/VFS.h"
|
||||
#include "Common/Log.h"
|
||||
#include "Common/StringUtils.h"
|
||||
#include "Common/File/Path.h"
|
||||
|
||||
#if PPSSPP_PLATFORM(UWP)
|
||||
#include <string>
|
||||
|
||||
#include <d3d11.h>
|
||||
#include <dxgi1_3.h>
|
||||
#include <d2d1_3.h>
|
||||
#include <dwrite_3.h>
|
||||
|
||||
using namespace Microsoft::WRL;
|
||||
|
||||
enum {
|
||||
MAX_TEXT_WIDTH = 4096,
|
||||
MAX_TEXT_HEIGHT = 512
|
||||
@@ -24,46 +29,43 @@ enum {
|
||||
|
||||
class TextDrawerFontContext {
|
||||
public:
|
||||
~TextDrawerFontContext() {
|
||||
Destroy();
|
||||
}
|
||||
|
||||
void Create() {
|
||||
if (textFmt) {
|
||||
Destroy();
|
||||
TextDrawerFontContext(const FontStyle &_style, float _dpiScale, IDWriteFactory4 *factory, IDWriteFontCollection1 *fontCollection) : style(_style), dpiScale(_dpiScale) {
|
||||
DWRITE_FONT_STYLE fontStyle = DWRITE_FONT_STYLE_NORMAL;
|
||||
DWRITE_FONT_WEIGHT weight = DWRITE_FONT_WEIGHT_NORMAL;
|
||||
int height = style.sizePts;
|
||||
FontStyleFlags styleFlags = FontStyleFlags::Default;
|
||||
std::string fontName = GetFontNameForFontStyle(style, &styleFlags);
|
||||
if (styleFlags & FontStyleFlags::Bold) {
|
||||
weight = DWRITE_FONT_WEIGHT_BOLD;
|
||||
}
|
||||
|
||||
if (!factory) {
|
||||
return;
|
||||
if (styleFlags & FontStyleFlags::Italic) {
|
||||
fontStyle = DWRITE_FONT_STYLE_ITALIC;
|
||||
}
|
||||
|
||||
HRESULT hr = factory->CreateTextFormat(
|
||||
fname.c_str(),
|
||||
ConvertUTF8ToWString(fontName).c_str(),
|
||||
fontCollection,
|
||||
weight,
|
||||
DWRITE_FONT_STYLE_NORMAL,
|
||||
fontStyle,
|
||||
DWRITE_FONT_STRETCH_NORMAL,
|
||||
(float)MulDiv(height, (int)(96.0f * (1.0f / dpiScale)), 72),
|
||||
L"",
|
||||
L"en-us",
|
||||
&textFmt
|
||||
);
|
||||
if (FAILED(hr)) {
|
||||
ERROR_LOG(Log::G3D, "Failed creating font %s", fname.c_str());
|
||||
ERROR_LOG(Log::G3D, "Failed creating text format for font %s", fontName.c_str());
|
||||
}
|
||||
}
|
||||
void Destroy() {
|
||||
|
||||
~TextDrawerFontContext() {
|
||||
if (textFmt) {
|
||||
textFmt->Release();
|
||||
}
|
||||
textFmt = nullptr;
|
||||
}
|
||||
|
||||
IDWriteFactory4 *factory = nullptr;
|
||||
IDWriteFontCollection1 *fontCollection = nullptr;
|
||||
IDWriteTextFormat *textFmt = nullptr;
|
||||
std::wstring fname;
|
||||
int height;
|
||||
DWRITE_FONT_WEIGHT weight;
|
||||
FontStyle style;
|
||||
float dpiScale;
|
||||
};
|
||||
|
||||
@@ -104,18 +106,66 @@ TextDrawerUWP::TextDrawerUWP(Draw::DrawContext *draw) : TextDrawer(draw), ctx_(n
|
||||
hr = m_d2dDevice->CreateDeviceContext(D2D1_DEVICE_CONTEXT_OPTIONS_NONE, &m_d2dContext);
|
||||
if (FAILED(hr)) _assert_msg_(false, "D2D CreateDeviceContext failed");
|
||||
|
||||
// Load the Roboto font
|
||||
hr = m_dwriteFactory->CreateFontFileReference(L"Content/Roboto-Condensed.ttf", nullptr, &m_fontFile);
|
||||
if (FAILED(hr)) ERROR_LOG(Log::System, "CreateFontFileReference failed");
|
||||
hr = m_dwriteFactory->CreateFontSetBuilder(&m_fontSetBuilder);
|
||||
if (FAILED(hr)) ERROR_LOG(Log::System, "CreateFontSetBuilder failed");
|
||||
hr = m_fontSetBuilder->AddFontFile(m_fontFile);
|
||||
|
||||
hr = m_dwriteFactory->CreateInMemoryFontFileLoader(&m_inMemoryLoader);
|
||||
if (FAILED(hr)) {
|
||||
_assert_msg_(false, "D2D CreateInMemoryFontFileLoader failed");
|
||||
}
|
||||
hr = m_dwriteFactory->RegisterFontFileLoader(m_inMemoryLoader);
|
||||
if (FAILED(hr)) {
|
||||
_assert_msg_(false, "D2D RegisterFontFileLoader failed");
|
||||
}
|
||||
// Load our fonts.
|
||||
const std::vector<std::string> fontFilenames = GetAllFontFilenames();
|
||||
for (const auto &fname : fontFilenames) {
|
||||
size_t size;
|
||||
uint8_t *data = g_VFS.ReadFile((fname + ".ttf").c_str(), &size);
|
||||
|
||||
ComPtr<IDWriteFontFile> fontFile;
|
||||
hr = m_inMemoryLoader->CreateInMemoryFontFileReference(
|
||||
m_dwriteFactory,
|
||||
data,
|
||||
static_cast<UINT32>(size),
|
||||
nullptr, // optional last write time
|
||||
&fontFile
|
||||
);
|
||||
|
||||
m_fontSetBuilder->AddFontFile(fontFile.Get());
|
||||
|
||||
delete[] data;
|
||||
m_fontFiles.push_back(fontFile);
|
||||
}
|
||||
|
||||
hr = m_fontSetBuilder->CreateFontSet(&m_fontSet);
|
||||
hr = m_dwriteFactory->CreateFontCollectionFromFontSet(m_fontSet, &m_fontCollection);
|
||||
|
||||
UINT32 familyCount = m_fontCollection->GetFontFamilyCount();
|
||||
for (UINT32 i = 0; i < familyCount; ++i) {
|
||||
IDWriteFontFamily *family;
|
||||
m_fontCollection->GetFontFamily(i, &family);
|
||||
|
||||
IDWriteLocalizedStrings *names;
|
||||
family->GetFamilyNames(&names);
|
||||
|
||||
UINT32 length = 0;
|
||||
names->GetStringLength(0, &length);
|
||||
|
||||
std::wstring name(length + 1, L'\0');
|
||||
names->GetString(0, &name[0], length + 1);
|
||||
|
||||
std::string nname = ConvertWStringToUTF8(name);
|
||||
|
||||
NOTICE_LOG(Log::G3D, "Font family: %s", nname.c_str());
|
||||
|
||||
names->Release();
|
||||
family->Release();
|
||||
}
|
||||
|
||||
ctx_ = new TextDrawerContext();
|
||||
|
||||
D2D1_BITMAP_PROPERTIES1 properties;
|
||||
D2D1_BITMAP_PROPERTIES1 properties{};
|
||||
properties.pixelFormat.format = DXGI_FORMAT_B8G8R8A8_UNORM;
|
||||
properties.pixelFormat.alphaMode = D2D1_ALPHA_MODE_PREMULTIPLIED;
|
||||
properties.dpiX = 96.0f;
|
||||
@@ -153,60 +203,38 @@ TextDrawerUWP::~TextDrawerUWP() {
|
||||
ctx_->bitmap->Release();
|
||||
ctx_->mirror_bmp->Release();
|
||||
|
||||
m_dwriteFactory->UnregisterFontFileLoader(m_inMemoryLoader);
|
||||
m_fontCollection->Release();
|
||||
m_fontSet->Release();
|
||||
for (auto file : m_fontFiles) {
|
||||
file->Release();
|
||||
}
|
||||
m_fontFiles.clear();
|
||||
m_fontSetBuilder->Release();
|
||||
m_dwriteFactory->Release();
|
||||
m_inMemoryLoader->Release();
|
||||
|
||||
m_d2dWhiteBrush->Release();
|
||||
m_d2dContext->Release();
|
||||
m_d2dDevice->Release();
|
||||
m_d2dFactory->Release();
|
||||
|
||||
m_fontCollection->Release();
|
||||
m_fontSet->Release();
|
||||
m_fontFile->Release();
|
||||
m_fontSetBuilder->Release();
|
||||
m_dwriteFactory->Release();
|
||||
delete ctx_;
|
||||
}
|
||||
|
||||
uint32_t TextDrawerUWP::SetFont(const char *fontName, int size, int flags) {
|
||||
uint32_t fontHash = fontName ? hash::Adler32((const uint8_t *)fontName, strlen(fontName)) : 0;
|
||||
fontHash ^= size;
|
||||
fontHash ^= flags << 10;
|
||||
|
||||
auto iter = fontMap_.find(fontHash);
|
||||
void TextDrawerUWP::SetOrCreateFont(const FontStyle &style) {
|
||||
auto iter = fontMap_.find(style);
|
||||
if (iter != fontMap_.end()) {
|
||||
fontHash_ = fontHash;
|
||||
return fontHash;
|
||||
fontStyle_ = style;
|
||||
return;
|
||||
}
|
||||
|
||||
std::wstring fname;
|
||||
if (fontName)
|
||||
fname = ConvertUTF8ToWString(fontName);
|
||||
else
|
||||
fname = L"Tahoma";
|
||||
|
||||
TextDrawerFontContext *font = new TextDrawerFontContext();
|
||||
font->weight = DWRITE_FONT_WEIGHT_NORMAL;
|
||||
font->height = size;
|
||||
font->fname = fname;
|
||||
font->dpiScale = dpiScale_;
|
||||
font->factory = m_dwriteFactory;
|
||||
font->fontCollection = m_fontCollection;
|
||||
font->Create();
|
||||
|
||||
fontMap_[fontHash] = std::unique_ptr<TextDrawerFontContext>(font);
|
||||
fontHash_ = fontHash;
|
||||
return fontHash;
|
||||
}
|
||||
|
||||
void TextDrawerUWP::SetFont(uint32_t fontHandle) {
|
||||
auto iter = fontMap_.find(fontHandle);
|
||||
if (iter != fontMap_.end()) {
|
||||
fontHash_ = fontHandle;
|
||||
}
|
||||
fontMap_[style] = std::make_unique<TextDrawerFontContext>(style, dpiScale_, m_dwriteFactory, m_fontCollection);
|
||||
fontStyle_ = style;
|
||||
}
|
||||
|
||||
void TextDrawerUWP::MeasureStringInternal(std::string_view str, float *w, float *h) {
|
||||
IDWriteTextFormat* format = nullptr;
|
||||
auto iter = fontMap_.find(fontHash_);
|
||||
auto iter = fontMap_.find(fontStyle_);
|
||||
if (iter != fontMap_.end()) {
|
||||
format = iter->second->textFmt;
|
||||
}
|
||||
@@ -244,7 +272,7 @@ bool TextDrawerUWP::DrawStringBitmap(std::vector<uint8_t> &bitmapData, TextStrin
|
||||
SIZE size;
|
||||
|
||||
IDWriteTextFormat *format = nullptr;
|
||||
auto iter = fontMap_.find(fontHash_);
|
||||
auto iter = fontMap_.find(fontStyle_);
|
||||
if (iter != fontMap_.end()) {
|
||||
format = iter->second->textFmt;
|
||||
}
|
||||
@@ -372,7 +400,6 @@ bool TextDrawerUWP::DrawStringBitmap(std::vector<uint8_t> &bitmapData, TextStrin
|
||||
void TextDrawerUWP::ClearFonts() {
|
||||
for (auto &iter : fontMap_) {
|
||||
iter.second->dpiScale = dpiScale_;
|
||||
iter.second->Destroy();
|
||||
}
|
||||
fontMap_.clear();
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
#include <d2d1_3.h>
|
||||
#include <dwrite_3.h>
|
||||
#include <wrl/client.h>
|
||||
|
||||
struct TextDrawerContext;
|
||||
// Internal struct but all details in .cpp file (pimpl to avoid pulling in excessive headers here)
|
||||
@@ -19,8 +20,7 @@ public:
|
||||
TextDrawerUWP(Draw::DrawContext *draw);
|
||||
~TextDrawerUWP();
|
||||
|
||||
uint32_t SetFont(const char *fontName, int size, int flags) override;
|
||||
void SetFont(uint32_t fontHandle) override; // Shortcut once you've set the font once.
|
||||
void SetOrCreateFont(const FontStyle &style) override;
|
||||
bool DrawStringBitmap(std::vector<uint8_t> &bitmapData, TextStringEntry &entry, Draw::DataFormat texFormat, std::string_view str, int align, bool fullColor) override;
|
||||
|
||||
protected:
|
||||
@@ -29,7 +29,7 @@ protected:
|
||||
void ClearFonts() override;
|
||||
|
||||
TextDrawerContext *ctx_;
|
||||
std::map<uint32_t, std::unique_ptr<TextDrawerFontContext>> fontMap_;
|
||||
std::map<FontStyle, std::unique_ptr<TextDrawerFontContext>> fontMap_;
|
||||
|
||||
// Direct2D drawing components.
|
||||
ID2D1Factory5* m_d2dFactory;
|
||||
@@ -39,11 +39,11 @@ protected:
|
||||
|
||||
// DirectWrite drawing components.
|
||||
IDWriteFactory5* m_dwriteFactory;
|
||||
IDWriteFontFile* m_fontFile;
|
||||
IDWriteFontSet* m_fontSet;
|
||||
IDWriteFontSetBuilder1* m_fontSetBuilder;
|
||||
IDWriteFontCollection1* m_fontCollection;
|
||||
|
||||
IDWriteInMemoryFontFileLoader *m_inMemoryLoader;
|
||||
std::vector<Microsoft::WRL::ComPtr<IDWriteFontFile>> m_fontFiles;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -22,30 +22,47 @@ enum {
|
||||
|
||||
class TextDrawerFontContext {
|
||||
public:
|
||||
~TextDrawerFontContext() {
|
||||
Destroy();
|
||||
}
|
||||
explicit TextDrawerFontContext(const FontStyle &_style, float _dpiScale) : hFont(0), style(_style), dpiScale(_dpiScale) {
|
||||
FontStyleFlags styleFlags{};
|
||||
std::string fontName = GetFontNameForFontStyle(style, &styleFlags);
|
||||
if (fontName.empty()) {
|
||||
// Shouldn't happen.
|
||||
fontName = "Tahoma";
|
||||
}
|
||||
|
||||
int weight = FW_NORMAL;
|
||||
if (styleFlags & FontStyleFlags::Bold) {
|
||||
weight = FW_BOLD;
|
||||
}
|
||||
if (styleFlags & FontStyleFlags::Light) {
|
||||
weight = FW_LIGHT;
|
||||
}
|
||||
|
||||
bool italic = (styleFlags & FontStyleFlags::Italic);
|
||||
int height = style.sizePts;
|
||||
|
||||
void Create() {
|
||||
if (hFont) {
|
||||
Destroy();
|
||||
}
|
||||
// We apparently specify all font sizes in pts (1pt = 1.33px), so divide by only 72 for pixels.
|
||||
int nHeight = -MulDiv(height, (int)(96.0f * (1.0f / dpiScale)), 72);
|
||||
hFont = CreateFont(nHeight, 0, 0, 0, bold, 0,
|
||||
hFont = CreateFont(nHeight, 0, 0, 0, weight, italic,
|
||||
FALSE, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS,
|
||||
CLIP_DEFAULT_PRECIS, PROOF_QUALITY,
|
||||
VARIABLE_PITCH, fname.c_str());
|
||||
VARIABLE_PITCH, ConvertUTF8ToWString(fontName).c_str());
|
||||
}
|
||||
|
||||
~TextDrawerFontContext() {
|
||||
Destroy();
|
||||
}
|
||||
|
||||
void Destroy() {
|
||||
DeleteObject(hFont);
|
||||
hFont = 0;
|
||||
}
|
||||
|
||||
HFONT hFont;
|
||||
std::wstring fname;
|
||||
int height;
|
||||
int bold;
|
||||
HFONT hFont = 0;
|
||||
FontStyle style;
|
||||
float dpiScale;
|
||||
};
|
||||
|
||||
@@ -59,8 +76,17 @@ TextDrawerWin32::TextDrawerWin32(Draw::DrawContext *draw) : TextDrawer(draw), ct
|
||||
ctx_ = new TextDrawerContext();
|
||||
ctx_->hDC = CreateCompatibleDC(NULL);
|
||||
|
||||
BITMAPINFO bmi;
|
||||
ZeroMemory(&bmi.bmiHeader, sizeof(BITMAPINFOHEADER));
|
||||
// Load the font files (pass the all flags so we get all filenames);
|
||||
std::vector<std::string> fonts = GetAllFontFilenames();
|
||||
for (const auto &iter : fonts) {
|
||||
std::string fn = "assets/" + iter + ".ttf";
|
||||
int numFontsAdded = AddFontResourceEx(ConvertUTF8ToWString(fn).c_str(), FR_PRIVATE, NULL);
|
||||
if (numFontsAdded == 0) {
|
||||
ERROR_LOG(Log::G3D, "Failed to add font resource from %s", fn.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
BITMAPINFO bmi{};
|
||||
bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
|
||||
bmi.bmiHeader.biWidth = MAX_TEXT_WIDTH;
|
||||
bmi.bmiHeader.biHeight = -MAX_TEXT_HEIGHT;
|
||||
@@ -79,51 +105,36 @@ TextDrawerWin32::~TextDrawerWin32() {
|
||||
ClearCache();
|
||||
ClearFonts();
|
||||
|
||||
// Unload the fonts.
|
||||
std::vector<std::string> fonts = GetAllFontFilenames();
|
||||
for (const auto &iter : fonts) {
|
||||
std::string fn = "assets/" + iter + ".ttf";
|
||||
RemoveFontResourceEx(ConvertUTF8ToWString(fn).c_str(), FR_PRIVATE, NULL);
|
||||
}
|
||||
|
||||
DeleteObject(ctx_->hbmBitmap);
|
||||
DeleteDC(ctx_->hDC);
|
||||
delete ctx_;
|
||||
}
|
||||
|
||||
uint32_t TextDrawerWin32::SetFont(const char *fontName, int size, int flags) {
|
||||
uint32_t fontHash = fontName ? hash::Adler32((const uint8_t *)fontName, strlen(fontName)) : 0;
|
||||
fontHash ^= size;
|
||||
fontHash ^= flags << 10;
|
||||
|
||||
auto iter = fontMap_.find(fontHash);
|
||||
void TextDrawerWin32::SetOrCreateFont(const FontStyle &style) {
|
||||
auto iter = fontMap_.find(style);
|
||||
if (iter != fontMap_.end()) {
|
||||
fontHash_ = fontHash;
|
||||
return fontHash;
|
||||
fontStyle_ = style;
|
||||
return;
|
||||
}
|
||||
|
||||
std::wstring fname;
|
||||
if (fontName)
|
||||
fname = ConvertUTF8ToWString(fontName);
|
||||
else
|
||||
fname = L"Tahoma";
|
||||
|
||||
TextDrawerFontContext *font = new TextDrawerFontContext();
|
||||
font->bold = FW_LIGHT;
|
||||
font->height = size;
|
||||
font->fname = fname;
|
||||
font->dpiScale = dpiScale_;
|
||||
font->Create();
|
||||
|
||||
fontMap_[fontHash] = std::unique_ptr<TextDrawerFontContext>(font);
|
||||
fontHash_ = fontHash;
|
||||
return fontHash;
|
||||
}
|
||||
|
||||
void TextDrawerWin32::SetFont(uint32_t fontHandle) {
|
||||
auto iter = fontMap_.find(fontHandle);
|
||||
if (iter != fontMap_.end()) {
|
||||
fontHash_ = fontHandle;
|
||||
}
|
||||
fontMap_[style] = std::make_unique<TextDrawerFontContext>(style, dpiScale_);
|
||||
fontStyle_ = style;
|
||||
}
|
||||
|
||||
void TextDrawerWin32::MeasureStringInternal(std::string_view str, float *w, float *h) {
|
||||
auto iter = fontMap_.find(fontHash_);
|
||||
auto iter = fontMap_.find(fontStyle_);
|
||||
if (iter != fontMap_.end()) {
|
||||
SelectObject(ctx_->hDC, iter->second->hFont);
|
||||
} else {
|
||||
ERROR_LOG(Log::G3D, "Failed to measure string");
|
||||
return;
|
||||
}
|
||||
|
||||
#if 0 && defined(_DEBUG)
|
||||
@@ -162,10 +173,11 @@ bool TextDrawerWin32::DrawStringBitmap(std::vector<uint8_t> &bitmapData, TextStr
|
||||
}
|
||||
std::wstring wstr = ConvertUTF8ToWString(ReplaceAll(str, "\n", "\r\n"));
|
||||
|
||||
auto iter = fontMap_.find(fontHash_);
|
||||
auto iter = fontMap_.find(fontStyle_);
|
||||
if (iter != fontMap_.end()) {
|
||||
SelectObject(ctx_->hDC, iter->second->hFont);
|
||||
}
|
||||
|
||||
// Set text properties
|
||||
SetTextColor(ctx_->hDC, 0xFFFFFF);
|
||||
SetBkColor(ctx_->hDC, 0);
|
||||
|
||||
@@ -15,9 +15,8 @@ class TextDrawerWin32 : public TextDrawer {
|
||||
public:
|
||||
TextDrawerWin32(Draw::DrawContext *draw);
|
||||
~TextDrawerWin32();
|
||||
|
||||
uint32_t SetFont(const char *fontName, int size, int flags) override;
|
||||
void SetFont(uint32_t fontHandle) override; // Shortcut once you've set the font once.
|
||||
|
||||
void SetOrCreateFont(const FontStyle &style) override;
|
||||
bool DrawStringBitmap(std::vector<uint8_t> &bitmapData, TextStringEntry &entry, Draw::DataFormat texFormat, std::string_view str, int align, bool fullColor) override;
|
||||
|
||||
protected:
|
||||
@@ -26,7 +25,7 @@ protected:
|
||||
void ClearFonts() override;
|
||||
|
||||
TextDrawerContext *ctx_;
|
||||
std::map<uint32_t, std::unique_ptr<TextDrawerFontContext>> fontMap_;
|
||||
std::map<FontStyle, std::unique_ptr<TextDrawerFontContext>> fontMap_;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -6,6 +6,21 @@
|
||||
|
||||
DisplayProperties g_display;
|
||||
|
||||
DeviceOrientation DisplayProperties::GetDeviceOrientation() const {
|
||||
return (dp_yres > dp_xres * 1.1f) ? DeviceOrientation::Portrait : DeviceOrientation::Landscape;
|
||||
}
|
||||
|
||||
std::string_view DeviceOrientationToString(DeviceOrientation orientation) {
|
||||
switch (orientation) {
|
||||
case DeviceOrientation::Landscape:
|
||||
return "Landscape";
|
||||
case DeviceOrientation::Portrait:
|
||||
return "Portrait";
|
||||
default:
|
||||
return "N/A";
|
||||
}
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void RotateRectToDisplayImpl(DisplayRect<T> &rect, T curRTWidth, T curRTHeight) {
|
||||
switch (g_display.rotation) {
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include <string_view>
|
||||
|
||||
#include "Common/Math/lin/matrix4x4.h"
|
||||
#include "Common/GPU/MiscTypes.h"
|
||||
#include "Common/Math/geom2d.h"
|
||||
|
||||
// This is meant to be a framework for handling DPI scaling etc.
|
||||
// For now, it just consists of these ugly globals.
|
||||
@@ -42,7 +45,7 @@ struct DisplayProperties {
|
||||
|
||||
DisplayProperties();
|
||||
void Print();
|
||||
|
||||
DeviceOrientation GetDeviceOrientation() const;
|
||||
// Returns true if the dimensions changed.
|
||||
// The first three parameters can take -1 to signify "unchanged".
|
||||
bool Recalculate(int new_pixel_xres, int new_pixel_yres, float new_scale_x, float new_scale_y, float customScale);
|
||||
@@ -59,3 +62,6 @@ void RotateRectToDisplay(DisplayRect<float> &rect, float rtWidth, float rtHeight
|
||||
void RotateRectToDisplay(DisplayRect<int> &rect, int rtWidth, int rtHeight);
|
||||
|
||||
Lin::Matrix4x4 ComputeOrthoMatrix(float xres, float yres, CoordConvention coordConvention);
|
||||
|
||||
// Take this and run through translation. Returns "Portrait", "Landscape" or later "Square" (the latter being the square shape you get when you open a foldable phone).
|
||||
std::string_view DeviceOrientationToString(DeviceOrientation orientation);
|
||||
|
||||
@@ -50,7 +50,7 @@ enum class LaunchUrlType {
|
||||
};
|
||||
|
||||
void System_Vibrate(int length_ms);
|
||||
void System_LaunchUrl(LaunchUrlType urlType, const char *url);
|
||||
void System_LaunchUrl(LaunchUrlType urlType, std::string_view url);
|
||||
|
||||
// It's sometimes a little unclear what should be a request, and what should be a separate function.
|
||||
// Going forward, "optional" things (PPSSPP will still function alright without it) will be requests,
|
||||
|
||||
+22
-17
@@ -16,7 +16,7 @@
|
||||
#include "Common/Render/AtlasGen.h"
|
||||
|
||||
UIContext::UIContext() {
|
||||
fontStyle_ = new UI::FontStyle();
|
||||
fontStyle_ = new FontStyle();
|
||||
bounds_ = Bounds(0, 0, g_display.dp_xres, g_display.dp_yres);
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@ void UIContext::BeginFrame() {
|
||||
if (uitexture_) {
|
||||
uitexture_->Release();
|
||||
}
|
||||
AtlasData data = atlasProvider_(draw_, AtlasChoice::General, 1.0f / g_display.dpi_scale_x);
|
||||
AtlasData data = atlasProvider_(draw_, AtlasChoice::General, 1.0f / g_display.dpi_scale_x, atlasInvalid_);
|
||||
uitexture_ = data.texture;
|
||||
_dbg_assert_(uitexture_);
|
||||
ui_draw2d.SetAtlas(data.atlas);
|
||||
@@ -54,7 +54,7 @@ void UIContext::BeginFrame() {
|
||||
}
|
||||
|
||||
if (!fontTexture_) {
|
||||
AtlasData data = atlasProvider_(draw_, AtlasChoice::Font, 1.0f / g_display.dpi_scale_x);
|
||||
AtlasData data = atlasProvider_(draw_, AtlasChoice::Font, 1.0f / g_display.dpi_scale_x, false);
|
||||
fontTexture_ = data.texture;
|
||||
_dbg_assert_(fontTexture_);
|
||||
ui_draw2d.SetFontAtlas(data.atlas);
|
||||
@@ -198,39 +198,44 @@ void UIContext::SetFontScale(float scaleX, float scaleY) {
|
||||
fontScaleY_ = scaleY;
|
||||
}
|
||||
|
||||
void UIContext::SetFontStyle(const UI::FontStyle &fontStyle) {
|
||||
void UIContext::SetFontStyle(const FontStyle &fontStyle) {
|
||||
*fontStyle_ = fontStyle;
|
||||
if (textDrawer_) {
|
||||
textDrawer_->SetFontScale(fontScaleX_, fontScaleY_);
|
||||
textDrawer_->SetFont(fontStyle.fontName.c_str(), fontStyle.sizePts, fontStyle.flags);
|
||||
textDrawer_->SetOrCreateFont(fontStyle);
|
||||
}
|
||||
}
|
||||
|
||||
void UIContext::MeasureText(const UI::FontStyle &style, float scaleX, float scaleY, std::string_view str, float *x, float *y, int align) const {
|
||||
static FontID AtlasFontFromStyle(const FontStyle &style) {
|
||||
// For now, we only have one font in the atlas.
|
||||
return FontID("UBUNTU24");
|
||||
}
|
||||
|
||||
void UIContext::MeasureText(const FontStyle &style, float scaleX, float scaleY, std::string_view str, float *x, float *y, int align) const {
|
||||
_dbg_assert_(str.data() != nullptr);
|
||||
if (!textDrawer_ || (align & FLAG_DYNAMIC_ASCII)) {
|
||||
float sizeFactor = (float)style.sizePts / 24.0f;
|
||||
Draw()->SetFontScale(scaleX * sizeFactor, scaleY * sizeFactor);
|
||||
Draw()->MeasureText(style.atlasFont, str, x, y);
|
||||
Draw()->MeasureText(AtlasFontFromStyle(style), str, x, y);
|
||||
} else {
|
||||
textDrawer_->SetFont(style.fontName.c_str(), style.sizePts, style.flags);
|
||||
textDrawer_->SetOrCreateFont(style);
|
||||
textDrawer_->SetFontScale(scaleX, scaleY);
|
||||
textDrawer_->MeasureString(str, x, y);
|
||||
textDrawer_->SetFont(fontStyle_->fontName.c_str(), fontStyle_->sizePts, fontStyle_->flags);
|
||||
textDrawer_->SetOrCreateFont(*fontStyle_);
|
||||
}
|
||||
}
|
||||
|
||||
void UIContext::MeasureTextRect(const UI::FontStyle &style, float scaleX, float scaleY, std::string_view str, const Bounds &bounds, float *x, float *y, int align) const {
|
||||
void UIContext::MeasureTextRect(const FontStyle &style, float scaleX, float scaleY, std::string_view str, const Bounds &bounds, float *x, float *y, int align) const {
|
||||
_dbg_assert_(str.data() != nullptr);
|
||||
if (!textDrawer_ || (align & FLAG_DYNAMIC_ASCII)) {
|
||||
float sizeFactor = (float)style.sizePts / 24.0f;
|
||||
Draw()->SetFontScale(scaleX * sizeFactor, scaleY * sizeFactor);
|
||||
Draw()->MeasureTextRect(style.atlasFont, str, bounds, x, y, align);
|
||||
Draw()->MeasureTextRect(AtlasFontFromStyle(style), str, bounds, x, y, align);
|
||||
} else {
|
||||
textDrawer_->SetFont(style.fontName.c_str(), style.sizePts, style.flags);
|
||||
textDrawer_->SetOrCreateFont(style);
|
||||
textDrawer_->SetFontScale(scaleX, scaleY);
|
||||
textDrawer_->MeasureStringRect(str, bounds, x, y, align);
|
||||
textDrawer_->SetFont(fontStyle_->fontName.c_str(), fontStyle_->sizePts, fontStyle_->flags);
|
||||
textDrawer_->SetOrCreateFont(*fontStyle_);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -238,14 +243,14 @@ void UIContext::DrawText(std::string_view str, float x, float y, uint32_t color,
|
||||
_dbg_assert_(str.data() != nullptr);
|
||||
if (!textDrawer_ || (align & FLAG_DYNAMIC_ASCII)) {
|
||||
// Use the font texture if this font is in that texture instead.
|
||||
bool useFontTexture = Draw()->GetFontAtlas()->getFont(fontStyle_->atlasFont) != nullptr;
|
||||
bool useFontTexture = Draw()->GetFontAtlas()->getFont(AtlasFontFromStyle(*fontStyle_)) != nullptr;
|
||||
if (useFontTexture) {
|
||||
Flush();
|
||||
BindFontTexture();
|
||||
}
|
||||
float sizeFactor = (float)fontStyle_->sizePts / 24.0f;
|
||||
Draw()->SetFontScale(fontScaleX_ * sizeFactor, fontScaleY_ * sizeFactor);
|
||||
Draw()->DrawText(fontStyle_->atlasFont, str, x, y, color, align);
|
||||
Draw()->DrawText(AtlasFontFromStyle(*fontStyle_), str, x, y, color, align);
|
||||
if (useFontTexture)
|
||||
Flush();
|
||||
} else {
|
||||
@@ -264,14 +269,14 @@ void UIContext::DrawTextShadow(std::string_view str, float x, float y, uint32_t
|
||||
void UIContext::DrawTextRect(std::string_view str, const Bounds &bounds, uint32_t color, int align) {
|
||||
if (!textDrawer_ || (align & FLAG_DYNAMIC_ASCII)) {
|
||||
// Use the font texture if this font is in that texture instead.
|
||||
bool useFontTexture = Draw()->GetFontAtlas()->getFont(fontStyle_->atlasFont) != nullptr;
|
||||
bool useFontTexture = Draw()->GetFontAtlas()->getFont(AtlasFontFromStyle(*fontStyle_)) != nullptr;
|
||||
if (useFontTexture) {
|
||||
Flush();
|
||||
BindFontTexture();
|
||||
}
|
||||
float sizeFactor = (float)fontStyle_->sizePts / 24.0f;
|
||||
Draw()->SetFontScale(fontScaleX_ * sizeFactor, fontScaleY_ * sizeFactor);
|
||||
Draw()->DrawTextRect(fontStyle_->atlasFont, str, bounds.x, bounds.y, bounds.w, bounds.h, color, align);
|
||||
Draw()->DrawTextRect(AtlasFontFromStyle(*fontStyle_), str, bounds.x, bounds.y, bounds.w, bounds.h, color, align);
|
||||
if (useFontTexture)
|
||||
Flush();
|
||||
} else {
|
||||
|
||||
+7
-7
@@ -25,12 +25,12 @@ namespace Draw {
|
||||
|
||||
class DrawBuffer;
|
||||
class TextDrawer;
|
||||
struct FontStyle;
|
||||
|
||||
namespace UI {
|
||||
struct Drawable;
|
||||
struct EventParams;
|
||||
struct Theme;
|
||||
struct FontStyle;
|
||||
class Event;
|
||||
class View;
|
||||
}
|
||||
@@ -54,7 +54,7 @@ struct AtlasData {
|
||||
Draw::Texture *texture;
|
||||
};
|
||||
|
||||
typedef std::function<AtlasData(Draw::DrawContext *, AtlasChoice, float dpiScale)> UIAtlasProviderFunc;
|
||||
typedef std::function<AtlasData(Draw::DrawContext *, AtlasChoice, float dpiScale, bool invalidate)> UIAtlasProviderFunc;
|
||||
|
||||
class UIContext {
|
||||
public:
|
||||
@@ -91,11 +91,11 @@ public:
|
||||
|
||||
// High level drawing functions. They generally assume the default texture to be bounds.
|
||||
|
||||
void SetFontStyle(const UI::FontStyle &style);
|
||||
const UI::FontStyle &GetFontStyle() { return *fontStyle_; }
|
||||
void SetFontStyle(const FontStyle &style);
|
||||
const FontStyle &GetFontStyle() { return *fontStyle_; }
|
||||
void SetFontScale(float scaleX, float scaleY);
|
||||
void MeasureText(const UI::FontStyle &style, float scaleX, float scaleY, std::string_view str, float *x, float *y, int align = 0) const;
|
||||
void MeasureTextRect(const UI::FontStyle &style, float scaleX, float scaleY, std::string_view str, const Bounds &bounds, float *x, float *y, int align = 0) const;
|
||||
void MeasureText(const FontStyle &style, float scaleX, float scaleY, std::string_view str, float *x, float *y, int align = 0) const;
|
||||
void MeasureTextRect(const FontStyle &style, float scaleX, float scaleY, std::string_view str, const Bounds &bounds, float *x, float *y, int align = 0) const;
|
||||
void DrawText(std::string_view str, float x, float y, uint32_t color, int align = 0);
|
||||
void DrawTextShadow(std::string_view str, float x, float y, uint32_t color, int align = 0);
|
||||
void DrawTextRect(std::string_view str, const Bounds &bounds, uint32_t color, int align = 0);
|
||||
@@ -139,7 +139,7 @@ private:
|
||||
|
||||
float fontScaleX_ = 1.0f;
|
||||
float fontScaleY_ = 1.0f;
|
||||
UI::FontStyle *fontStyle_ = nullptr;
|
||||
FontStyle *fontStyle_ = nullptr;
|
||||
TextDrawer *textDrawer_ = nullptr;
|
||||
|
||||
Draw::SamplerState *sampler_ = nullptr;
|
||||
|
||||
+185
-3
@@ -10,17 +10,199 @@
|
||||
#include "Common/Data/Text/I18n.h"
|
||||
#include "Common/System/System.h"
|
||||
#include "Common/System/Request.h"
|
||||
#include "Common/System/Display.h"
|
||||
#include "Common/Math/curves.h"
|
||||
#include "Common/Render/DrawBuffer.h"
|
||||
|
||||
namespace UI {
|
||||
|
||||
PopupScreen::PopupScreen(std::string_view title, std::string_view button1, std::string_view button2)
|
||||
: title_(title), button1_(button1), button2_(button2) {
|
||||
auto di = GetI18NCategory(I18NCat::DIALOG);
|
||||
// Auto-assign images. A bit hack to have this here.
|
||||
if (button1 == di->T("Delete") || button1 == di->T("Move to trash")) {
|
||||
button1Image_ = ImageID("I_TRASHCAN");
|
||||
}
|
||||
|
||||
alpha_ = 0.0f; // inherited
|
||||
}
|
||||
|
||||
void PopupScreen::touch(const TouchInput &touch) {
|
||||
if (!box_ || (touch.flags & TOUCH_DOWN) == 0) {
|
||||
// Handle down-presses here.
|
||||
UIDialogScreen::touch(touch);
|
||||
return;
|
||||
}
|
||||
|
||||
// Extra bounds to avoid closing the dialog while trying to aim for something
|
||||
// near the edge. Now that we only close on actual down-events, we can shrink
|
||||
// this border a bit.
|
||||
if (!box_->GetBounds().Expand(30.0f, 30.0f).Contains(touch.x, touch.y)) {
|
||||
TriggerFinish(DR_CANCEL);
|
||||
}
|
||||
|
||||
UIDialogScreen::touch(touch);
|
||||
}
|
||||
|
||||
bool PopupScreen::key(const KeyInput &key) {
|
||||
if (key.flags & KEY_DOWN) {
|
||||
if ((key.keyCode == NKCODE_ENTER || key.keyCode == NKCODE_NUMPAD_ENTER) && defaultButton_) {
|
||||
UI::EventParams e{};
|
||||
defaultButton_->OnClick.Trigger(e);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return UIDialogScreen::key(key);
|
||||
}
|
||||
|
||||
void PopupScreen::update() {
|
||||
UIDialogScreen::update();
|
||||
|
||||
if (defaultButton_)
|
||||
defaultButton_->SetEnabled(CanComplete(DR_OK));
|
||||
|
||||
float animatePos = 1.0f;
|
||||
|
||||
++frames_;
|
||||
if (finishFrame_ >= 0) {
|
||||
float leadOut = bezierEaseInOut((frames_ - finishFrame_) * (1.0f / (float)FRAMES_LEAD_OUT));
|
||||
animatePos = 1.0f - leadOut;
|
||||
|
||||
if (frames_ >= finishFrame_ + FRAMES_LEAD_OUT) {
|
||||
// Actual finish happens here.
|
||||
screenManager()->finishDialog(this, finishResult_);
|
||||
}
|
||||
} else if (frames_ < FRAMES_LEAD_IN) {
|
||||
float leadIn = bezierEaseInOut(frames_ * (1.0f / (float)FRAMES_LEAD_IN));
|
||||
animatePos = leadIn;
|
||||
}
|
||||
|
||||
if (animatePos < 1.0f) {
|
||||
alpha_ = animatePos;
|
||||
scale_.x = 0.9f + animatePos * 0.1f;
|
||||
scale_.y = 0.9f + animatePos * 0.1f;
|
||||
|
||||
if (hasPopupOrigin_) {
|
||||
float xoff = popupOrigin_.x - g_display.dp_xres / 2;
|
||||
float yoff = popupOrigin_.y - g_display.dp_yres / 2;
|
||||
|
||||
// Pull toward the origin a bit.
|
||||
translation_.x = xoff * (1.0f - animatePos) * 0.2f;
|
||||
translation_.y = yoff * (1.0f - animatePos) * 0.2f;
|
||||
} else {
|
||||
translation_.y = -g_display.dp_yres * (1.0f - animatePos) * 0.2f;
|
||||
}
|
||||
} else {
|
||||
alpha_ = 1.0f;
|
||||
scale_.x = 1.0f;
|
||||
scale_.y = 1.0f;
|
||||
translation_.x = 0.0f;
|
||||
translation_.y = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
void PopupScreen::SetPopupOrigin(const UI::View *view) {
|
||||
hasPopupOrigin_ = true;
|
||||
popupOrigin_ = view->GetBounds().Center();
|
||||
}
|
||||
|
||||
void PopupScreen::TriggerFinish(DialogResult result) {
|
||||
if (CanComplete(result)) {
|
||||
ignoreInput_ = true;
|
||||
finishFrame_ = frames_;
|
||||
finishResult_ = result;
|
||||
|
||||
OnCompleted(result);
|
||||
}
|
||||
// Inform UI that popup close to hide OSK (if visible)
|
||||
System_NotifyUIEvent(UIEventNotification::POPUP_CLOSED);
|
||||
}
|
||||
|
||||
void PopupScreen::CreateViews() {
|
||||
using namespace UI;
|
||||
UIContext &dc = *screenManager()->getUIContext();
|
||||
|
||||
AnchorLayout *anchor = new AnchorLayout(new LayoutParams(FILL_PARENT, FILL_PARENT));
|
||||
anchor->Overflow(false);
|
||||
root_ = anchor;
|
||||
|
||||
const float ySize = FillVertical() ? dc.GetLayoutBounds().h - 30 : WRAP_CONTENT;
|
||||
|
||||
int y = dc.GetBounds().centerY() + offsetY_;
|
||||
Centering vCentering = Centering::Vertical;
|
||||
if (alignTop_) {
|
||||
y = 0;
|
||||
vCentering = Centering::None;
|
||||
}
|
||||
|
||||
const float popupWidth = PopupWidth();
|
||||
|
||||
AnchorLayoutParams *anchorParams;
|
||||
// NOTE: We purely use the popup width here to decide the type of layout, instead of the device orientation.
|
||||
if (dc.GetLayoutBounds().w < popupWidth + 50) {
|
||||
anchorParams = new AnchorLayoutParams(popupWidth, ySize,
|
||||
10, y, 10, NONE, vCentering);
|
||||
} else {
|
||||
anchorParams = new AnchorLayoutParams(popupWidth, ySize,
|
||||
dc.GetBounds().centerX(), y, NONE, NONE, vCentering | Centering::Horizontal);
|
||||
}
|
||||
|
||||
box_ = new LinearLayout(ORIENT_VERTICAL, anchorParams);
|
||||
|
||||
root_->Add(box_);
|
||||
box_->SetBG(dc.GetTheme().popupStyle.background);
|
||||
box_->SetHasDropShadow(hasDropShadow_);
|
||||
// Since we scale a bit, make the dropshadow bleed past the edges.
|
||||
box_->SetDropShadowExpand(std::max(g_display.dp_xres, g_display.dp_yres));
|
||||
box_->SetSpacing(0.0f);
|
||||
|
||||
if (!title_.empty()) {
|
||||
View *title = new PopupHeader(title_);
|
||||
box_->Add(title);
|
||||
}
|
||||
|
||||
CreatePopupContents(box_);
|
||||
root_->Recurse([](View *view) {
|
||||
view->SetPopupStyle(true);
|
||||
});
|
||||
|
||||
root_->SetDefaultFocusView(box_);
|
||||
|
||||
if (ShowButtons() && !button1_.empty()) {
|
||||
// And the two buttons at the bottom.
|
||||
LinearLayout *buttonRow = new LinearLayout(ORIENT_HORIZONTAL, new LinearLayoutParams(200, WRAP_CONTENT));
|
||||
buttonRow->SetSpacing(0);
|
||||
Margins buttonMargins(5, 5);
|
||||
|
||||
// Adjust button order to the platform default.
|
||||
if (System_GetPropertyBool(SYSPROP_OK_BUTTON_LEFT)) {
|
||||
defaultButton_ = buttonRow->Add(new Choice(button1_, button1Image_, new LinearLayoutParams(1.0f, buttonMargins)));
|
||||
defaultButton_->OnClick.Handle<UIScreen>(this, &UIScreen::OnOK);
|
||||
if (!button2_.empty()) {
|
||||
buttonRow->Add(new Choice(button2_, new LinearLayoutParams(1.0f, buttonMargins)))->OnClick.Handle<UIScreen>(this, &UIScreen::OnCancel);
|
||||
}
|
||||
} else {
|
||||
if (!button2_.empty()) {
|
||||
buttonRow->Add(new Choice(button2_, new LinearLayoutParams(1.0f, buttonMargins)))->OnClick.Handle<UIScreen>(this, &UIScreen::OnCancel);
|
||||
}
|
||||
defaultButton_ = buttonRow->Add(new Choice(button1_, button1Image_, new LinearLayoutParams(1.0f, buttonMargins)));
|
||||
defaultButton_->OnClick.Handle<UIScreen>(this, &UIScreen::OnOK);
|
||||
}
|
||||
|
||||
box_->Add(buttonRow);
|
||||
}
|
||||
}
|
||||
|
||||
void MessagePopupScreen::CreatePopupContents(UI::ViewGroup *parent) {
|
||||
using namespace UI;
|
||||
UIContext &dc = *screenManager()->getUIContext();
|
||||
|
||||
std::vector<std::string_view> messageLines;
|
||||
SplitString(message_, '\n', messageLines);
|
||||
for (auto lineOfText : messageLines)
|
||||
parent->Add(new UI::TextView(lineOfText, ALIGN_LEFT | ALIGN_VCENTER, false));
|
||||
for (auto lineOfText : messageLines) {
|
||||
parent->Add(new UI::TextView(lineOfText, ALIGN_LEFT | ALIGN_VCENTER | FLAG_WRAP_TEXT, false, new LinearLayoutParams(Margins(8))));
|
||||
}
|
||||
}
|
||||
|
||||
void MessagePopupScreen::OnCompleted(DialogResult result) {
|
||||
@@ -80,7 +262,7 @@ void PopupContextMenuScreen::CreatePopupContents(UI::ViewGroup *parent) {
|
||||
|
||||
// Hacky: Override the position to look like a popup menu.
|
||||
AnchorLayoutParams *ap = (AnchorLayoutParams *)parent->GetLayoutParams();
|
||||
ap->center = false;
|
||||
ap->centering = Centering::None;
|
||||
ap->left = sourceView_->GetBounds().x;
|
||||
ap->top = sourceView_->GetBounds().y2();
|
||||
}
|
||||
|
||||
@@ -16,6 +16,62 @@ namespace UI {
|
||||
static const float NO_DEFAULT_FLOAT = -1000000.0f;
|
||||
static const int NO_DEFAULT_INT = -1000000;
|
||||
|
||||
class PopupScreen : public UIDialogScreen {
|
||||
public:
|
||||
PopupScreen(std::string_view title, std::string_view button1 = "", std::string_view button2 = "");
|
||||
|
||||
virtual void CreatePopupContents(UI::ViewGroup *parent) = 0;
|
||||
void CreateViews() override;
|
||||
bool isTransparent() const override { return true; }
|
||||
void touch(const TouchInput &touch) override;
|
||||
bool key(const KeyInput &key) override;
|
||||
|
||||
void TriggerFinish(DialogResult result) override;
|
||||
|
||||
void SetPopupOrigin(const UI::View *view);
|
||||
void SetPopupOffset(float y) { offsetY_ = y; }
|
||||
|
||||
void SetAlignTop(bool alignTop) { alignTop_ = alignTop; }
|
||||
|
||||
void SetHasDropShadow(bool has) { hasDropShadow_ = has; }
|
||||
|
||||
// For the postproc param sliders on DisplayLayoutScreen
|
||||
bool wantBrightBackground() const override { return !hasDropShadow_; }
|
||||
|
||||
protected:
|
||||
virtual bool FillVertical() const { return false; }
|
||||
virtual UI::Size PopupWidth() const { return 550; }
|
||||
virtual bool ShowButtons() const { return true; }
|
||||
virtual bool CanComplete(DialogResult result) { return true; }
|
||||
virtual void OnCompleted(DialogResult result) {}
|
||||
std::string_view Title() { return title_; }
|
||||
|
||||
void update() override;
|
||||
|
||||
private:
|
||||
UI::LinearLayout *box_ = nullptr;
|
||||
UI::Choice *defaultButton_ = nullptr;
|
||||
ImageID button1Image_;
|
||||
std::string title_;
|
||||
std::string button1_;
|
||||
std::string button2_;
|
||||
|
||||
enum {
|
||||
FRAMES_LEAD_IN = 6,
|
||||
FRAMES_LEAD_OUT = 4,
|
||||
};
|
||||
|
||||
int frames_ = 0;
|
||||
int finishFrame_ = -1;
|
||||
DialogResult finishResult_ = DR_CANCEL;
|
||||
bool hasPopupOrigin_ = false;
|
||||
Point2D popupOrigin_;
|
||||
float offsetY_ = 0.0f;
|
||||
bool alignTop_ = false;
|
||||
|
||||
bool hasDropShadow_ = true;
|
||||
};
|
||||
|
||||
class ListPopupScreen : public PopupScreen {
|
||||
public:
|
||||
ListPopupScreen(std::string_view title) : PopupScreen(title) {}
|
||||
@@ -60,7 +116,7 @@ private:
|
||||
|
||||
class MessagePopupScreen : public PopupScreen {
|
||||
public:
|
||||
MessagePopupScreen(std::string_view title, std::string message, std::string button1, std::string button2, std::function<void(bool)> callback)
|
||||
MessagePopupScreen(std::string_view title, std::string_view message, std::string_view button1, std::string_view button2, std::function<void(bool)> callback)
|
||||
: PopupScreen(title, button1, button2), message_(message), callback_(callback) {}
|
||||
|
||||
const char *tag() const override { return "MessagePopupScreen"; }
|
||||
@@ -195,9 +251,6 @@ public:
|
||||
|
||||
UI::Event OnChoice;
|
||||
|
||||
protected:
|
||||
bool HasTitleBar() const override { return false; }
|
||||
|
||||
private:
|
||||
const ContextMenuItem *items_;
|
||||
size_t itemCount_;
|
||||
@@ -444,13 +497,12 @@ class ChoiceWithValueDisplay : public AbstractChoiceWithValueDisplay {
|
||||
public:
|
||||
ChoiceWithValueDisplay(int *value, std::string_view text, LayoutParams *layoutParams = 0)
|
||||
: AbstractChoiceWithValueDisplay(text, layoutParams), iValue_(value) {}
|
||||
|
||||
ChoiceWithValueDisplay(int *value, ImageID imageId, LayoutParams *layoutParams = 0)
|
||||
: AbstractChoiceWithValueDisplay("", imageId, layoutParams), iValue_(value) {}
|
||||
ChoiceWithValueDisplay(std::string *value, std::string_view text, I18NCat category, LayoutParams *layoutParams = 0)
|
||||
: AbstractChoiceWithValueDisplay(text, layoutParams), sValue_(value), category_(category) {}
|
||||
|
||||
ChoiceWithValueDisplay(std::string *value, std::string_view text, std::string(*translateCallback)(std::string_view value), LayoutParams *layoutParams = 0)
|
||||
: AbstractChoiceWithValueDisplay(text, layoutParams), sValue_(value), translateCallback_(translateCallback) {
|
||||
}
|
||||
: AbstractChoiceWithValueDisplay(text, layoutParams), sValue_(value), translateCallback_(translateCallback) {}
|
||||
|
||||
private:
|
||||
std::string ValueText() const override;
|
||||
|
||||
+5
-1
@@ -100,7 +100,7 @@ bool IsFocusMovementEnabled() {
|
||||
return focusMovementEnabled;
|
||||
}
|
||||
|
||||
void LayoutViewHierarchy(const UIContext &dc, ViewGroup *root, bool ignoreInsets, bool ignoreBottomInset) {
|
||||
void LayoutViewHierarchy(const UIContext &dc, const UI::Margins &rootMargins, ViewGroup *root, bool ignoreInsets, bool ignoreBottomInset) {
|
||||
Bounds rootBounds = ignoreInsets ? dc.GetBounds() : dc.GetLayoutBounds(ignoreBottomInset);
|
||||
|
||||
MeasureSpec horiz(EXACTLY, rootBounds.w);
|
||||
@@ -109,6 +109,10 @@ void LayoutViewHierarchy(const UIContext &dc, ViewGroup *root, bool ignoreInsets
|
||||
// Two phases - measure contents, layout.
|
||||
root->Measure(dc, horiz, vert);
|
||||
// Root has a specified size. Set it, then let root layout all its children.
|
||||
rootBounds.x += rootMargins.left;
|
||||
rootBounds.y += rootMargins.top;
|
||||
rootBounds.w -= rootMargins.right;
|
||||
rootBounds.h -= rootMargins.bottom;
|
||||
root->SetBounds(rootBounds);
|
||||
root->Layout();
|
||||
}
|
||||
|
||||
+3
-1
@@ -8,6 +8,8 @@
|
||||
|
||||
namespace UI {
|
||||
|
||||
struct Margins;
|
||||
|
||||
// The ONLY global is the currently focused item.
|
||||
// Can be and often is null.
|
||||
void EnableFocusMovement(bool enable);
|
||||
@@ -22,7 +24,7 @@ DialogResult DispatchEvents();
|
||||
|
||||
class ViewGroup;
|
||||
|
||||
void LayoutViewHierarchy(const UIContext &dc, ViewGroup *root, bool ignoreInsets, bool ignoreBottomInset);
|
||||
void LayoutViewHierarchy(const UIContext &dc, const UI::Margins &rootMargins, UI::ViewGroup *root, bool ignoreInsets, bool ignoreBottomInset);
|
||||
DialogResult UpdateViewHierarchy(ViewGroup *root);
|
||||
|
||||
enum class KeyEventResult {
|
||||
|
||||
+21
-7
@@ -9,7 +9,7 @@
|
||||
namespace UI {
|
||||
|
||||
TabHolder::TabHolder(Orientation orientation, float stripSize, TabHolderFlags flags, View *bannerView, LayoutParams *layoutParams)
|
||||
: LinearLayout(Opposite(orientation), layoutParams) {
|
||||
: LinearLayout(Opposite(orientation), layoutParams), tabOrientation_(orientation), flags_(flags) {
|
||||
SetSpacing(0.0f);
|
||||
if (orientation == ORIENT_HORIZONTAL) {
|
||||
// This orientation supports adding a back button.
|
||||
@@ -34,6 +34,8 @@ TabHolder::TabHolder(Orientation orientation, float stripSize, TabHolderFlags fl
|
||||
}
|
||||
} else {
|
||||
tabContainer_ = new LinearLayout(ORIENT_VERTICAL, new LayoutParams(stripSize, FILL_PARENT));
|
||||
tabContainer_->Add(new Spacer(8.0f));
|
||||
tabContainer_->SetSpacing(0.0f);
|
||||
tabStrip_ = new ChoiceStrip(orientation, new LayoutParams(FILL_PARENT, FILL_PARENT));
|
||||
tabStrip_->SetTopTabs(true);
|
||||
tabScroll_ = new ScrollView(orientation, new LinearLayoutParams(1.0f));
|
||||
@@ -62,9 +64,15 @@ void TabHolder::AddBack(UIScreen *parent) {
|
||||
}
|
||||
}
|
||||
|
||||
void TabHolder::AddTabContents(std::string_view title, ViewGroup *tabContents) {
|
||||
void TabHolder::AddTabContents(std::string_view title, ImageID imageId, ViewGroup *tabContents) {
|
||||
tabs_.push_back(tabContents);
|
||||
tabStrip_->AddChoice(title);
|
||||
if (tabOrientation_ == ORIENT_HORIZONTAL && (flags_ & TabHolderFlags::HorizontalOnlyIcons) && imageId.isValid()) {
|
||||
tabStrip_->AddChoice(imageId);
|
||||
} else if (tabOrientation_ == ORIENT_VERTICAL && (flags_ & TabHolderFlags::VerticalShowIcons) && imageId.isValid()) {
|
||||
tabStrip_->AddChoice(title, imageId);
|
||||
} else {
|
||||
tabStrip_->AddChoice(title);
|
||||
}
|
||||
contents_->Add(tabContents);
|
||||
if (tabs_.size() > 1)
|
||||
tabContents->SetVisibility(V_GONE);
|
||||
@@ -76,9 +84,15 @@ void TabHolder::AddTabContents(std::string_view title, ViewGroup *tabContents) {
|
||||
createFuncs_.push_back(nullptr);
|
||||
}
|
||||
|
||||
void TabHolder::AddTabDeferred(std::string_view title, std::function<ViewGroup *()> createCb) {
|
||||
void TabHolder::AddTabDeferred(std::string_view title, ImageID imageId, std::function<ViewGroup *()> createCb) {
|
||||
tabs_.push_back(nullptr); // marker
|
||||
tabStrip_->AddChoice(title);
|
||||
if (tabOrientation_ == ORIENT_HORIZONTAL && (flags_ & TabHolderFlags::HorizontalOnlyIcons) && imageId.isValid()) {
|
||||
tabStrip_->AddChoice(imageId);
|
||||
} else if (tabOrientation_ == ORIENT_VERTICAL && (flags_ & TabHolderFlags::VerticalShowIcons) && imageId.isValid()) {
|
||||
tabStrip_->AddChoice(title, imageId);
|
||||
} else {
|
||||
tabStrip_->AddChoice(title);
|
||||
}
|
||||
tabTweens_.push_back(nullptr);
|
||||
createFuncs_.push_back(createCb);
|
||||
|
||||
@@ -223,8 +237,8 @@ ChoiceStrip::ChoiceStrip(Orientation orientation, LayoutParams *layoutParams)
|
||||
SetSpacing(0.0f);
|
||||
}
|
||||
|
||||
void ChoiceStrip::AddChoice(std::string_view title) {
|
||||
StickyChoice *c = new StickyChoice(title, "",
|
||||
void ChoiceStrip::AddChoice(std::string_view title, ImageID imageId) {
|
||||
StickyChoice *c = new StickyChoice(title, imageId,
|
||||
orientation_ == ORIENT_HORIZONTAL ?
|
||||
nullptr :
|
||||
new LinearLayoutParams(FILL_PARENT, ITEM_HEIGHT));
|
||||
|
||||
@@ -14,6 +14,8 @@ class ScrollView;
|
||||
enum class TabHolderFlags {
|
||||
Default = 0,
|
||||
BackButton = 1,
|
||||
HorizontalOnlyIcons = 2,
|
||||
VerticalShowIcons = 4,
|
||||
};
|
||||
ENUM_CLASS_BITOPS(TabHolderFlags);
|
||||
|
||||
@@ -22,11 +24,11 @@ public:
|
||||
TabHolder(Orientation orientation, float stripSize, TabHolderFlags flags, View *bannerView, LayoutParams *layoutParams);
|
||||
|
||||
template <class T>
|
||||
T *AddTab(std::string_view title, T *tabContents) {
|
||||
AddTabContents(title, tabContents);
|
||||
T *AddTab(std::string_view title, ImageID imageId, T *tabContents) {
|
||||
AddTabContents(title, imageId, tabContents);
|
||||
return tabContents;
|
||||
}
|
||||
void AddTabDeferred(std::string_view title, std::function<ViewGroup *()> createCb);
|
||||
void AddTabDeferred(std::string_view title, ImageID imageId, std::function<ViewGroup *()> createCb);
|
||||
void EnableTab(int tab, bool enabled);
|
||||
|
||||
void AddBack(UIScreen *parent);
|
||||
@@ -48,7 +50,7 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
void AddTabContents(std::string_view title, ViewGroup *tabContents);
|
||||
void AddTabContents(std::string_view title, ImageID imageId, ViewGroup *tabContents);
|
||||
void OnTabClick(EventParams &e);
|
||||
bool EnsureTab(int index); // return true if it actually created a tab.
|
||||
|
||||
@@ -57,7 +59,9 @@ private:
|
||||
ChoiceStrip *tabStrip_ = nullptr;
|
||||
ScrollView *tabScroll_ = nullptr;
|
||||
ViewGroup *contents_ = nullptr;
|
||||
Orientation tabOrientation_ = ORIENT_HORIZONTAL;
|
||||
|
||||
TabHolderFlags flags_ = TabHolderFlags::Default;
|
||||
int currentTab_ = 0;
|
||||
std::vector<ViewGroup *> tabs_;
|
||||
std::vector<AnchorTranslateTween *> tabTweens_;
|
||||
@@ -68,7 +72,7 @@ class ChoiceStrip : public LinearLayout {
|
||||
public:
|
||||
ChoiceStrip(Orientation orientation, LayoutParams *layoutParams = 0);
|
||||
|
||||
void AddChoice(std::string_view title);
|
||||
void AddChoice(std::string_view title, ImageID imageId = ImageID::invalid());
|
||||
void AddChoice(ImageID buttonImage);
|
||||
|
||||
int GetSelection() const { return selected_; }
|
||||
|
||||
+11
-178
@@ -6,27 +6,26 @@
|
||||
#include "Common/System/Request.h"
|
||||
#include "Common/Input/InputState.h"
|
||||
#include "Common/Input/KeyCodes.h"
|
||||
#include "Common/Math/curves.h"
|
||||
#include "Common/UI/UIScreen.h"
|
||||
#include "Common/UI/Context.h"
|
||||
#include "Common/UI/Screen.h"
|
||||
#include "Common/UI/Root.h"
|
||||
#include "Common/Data/Text/I18n.h"
|
||||
#include "Common/Render/DrawBuffer.h"
|
||||
|
||||
static const bool ClickDebug = false;
|
||||
|
||||
UIScreen::UIScreen()
|
||||
: Screen() {
|
||||
lastPortrait_ = UsePortraitLayout();
|
||||
UIScreen::UIScreen() : Screen() {
|
||||
lastOrientation_ = GetDeviceOrientation();
|
||||
}
|
||||
|
||||
UIScreen::~UIScreen() {
|
||||
delete root_;
|
||||
}
|
||||
|
||||
bool UIScreen::UsePortraitLayout() const {
|
||||
return g_display.dp_yres > g_display.dp_xres * 1.1f;
|
||||
// This is the source of truth for orientation for configuration and rendering.
|
||||
DeviceOrientation UIScreen::GetDeviceOrientation() const {
|
||||
// TODO: On some platforms, we can do a more sophisticated check.
|
||||
return g_display.GetDeviceOrientation();
|
||||
}
|
||||
|
||||
void UIScreen::DoRecreateViews() {
|
||||
@@ -56,7 +55,7 @@ void UIScreen::DoRecreateViews() {
|
||||
|
||||
// Update layout and refocus so things scroll into view.
|
||||
// This is for resizing down, when focused on something now offscreen.
|
||||
UI::LayoutViewHierarchy(*screenManager()->getUIContext(), root_, ignoreInsets_, ignoreBottomInset_);
|
||||
UI::LayoutViewHierarchy(*screenManager()->getUIContext(), RootMargins(), root_, ignoreInsets_, ignoreBottomInset_);
|
||||
UI::View *focused = UI::GetFocusedView();
|
||||
if (focused) {
|
||||
root_->SubviewFocused(focused);
|
||||
@@ -140,10 +139,10 @@ bool UIScreen::UnsyncKey(const KeyInput &key) {
|
||||
}
|
||||
|
||||
void UIScreen::update() {
|
||||
bool portrait = UsePortraitLayout();
|
||||
if (portrait != lastPortrait_) {
|
||||
DeviceOrientation orientation = GetDeviceOrientation();
|
||||
if (orientation != lastOrientation_) {
|
||||
RecreateViews();
|
||||
lastPortrait_ = portrait;
|
||||
lastOrientation_ = orientation;
|
||||
}
|
||||
|
||||
DoRecreateViews();
|
||||
@@ -229,7 +228,7 @@ ScreenRenderFlags UIScreen::render(ScreenRenderMode mode) {
|
||||
|
||||
UIContext &uiContext = *screenManager()->getUIContext();
|
||||
if (root_) {
|
||||
UI::LayoutViewHierarchy(uiContext, root_, ignoreInsets_, ignoreBottomInset_);
|
||||
UI::LayoutViewHierarchy(uiContext, RootMargins(), root_, ignoreInsets_, ignoreBottomInset_);
|
||||
}
|
||||
|
||||
uiContext.PushTransform({translation_, scale_, alpha_});
|
||||
@@ -299,169 +298,3 @@ void UIScreen::OnOK(UI::EventParams &e) {
|
||||
void UIScreen::OnCancel(UI::EventParams &e) {
|
||||
TriggerFinish(DR_CANCEL);
|
||||
}
|
||||
|
||||
PopupScreen::PopupScreen(std::string_view title, std::string_view button1, std::string_view button2)
|
||||
: title_(title) {
|
||||
auto di = GetI18NCategory(I18NCat::DIALOG);
|
||||
if (!button1.empty())
|
||||
button1_ = di->T(button1);
|
||||
if (!button2.empty())
|
||||
button2_ = di->T(button2);
|
||||
alpha_ = 0.0f; // inherited
|
||||
}
|
||||
|
||||
void PopupScreen::touch(const TouchInput &touch) {
|
||||
if (!box_ || (touch.flags & TOUCH_DOWN) == 0) {
|
||||
// Handle down-presses here.
|
||||
UIDialogScreen::touch(touch);
|
||||
return;
|
||||
}
|
||||
|
||||
// Extra bounds to avoid closing the dialog while trying to aim for something
|
||||
// near the edge. Now that we only close on actual down-events, we can shrink
|
||||
// this border a bit.
|
||||
if (!box_->GetBounds().Expand(30.0f, 30.0f).Contains(touch.x, touch.y)) {
|
||||
TriggerFinish(DR_CANCEL);
|
||||
}
|
||||
|
||||
UIDialogScreen::touch(touch);
|
||||
}
|
||||
|
||||
bool PopupScreen::key(const KeyInput &key) {
|
||||
if (key.flags & KEY_DOWN) {
|
||||
if ((key.keyCode == NKCODE_ENTER || key.keyCode == NKCODE_NUMPAD_ENTER) && defaultButton_) {
|
||||
UI::EventParams e{};
|
||||
defaultButton_->OnClick.Trigger(e);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return UIDialogScreen::key(key);
|
||||
}
|
||||
|
||||
void PopupScreen::update() {
|
||||
UIDialogScreen::update();
|
||||
|
||||
if (defaultButton_)
|
||||
defaultButton_->SetEnabled(CanComplete(DR_OK));
|
||||
|
||||
float animatePos = 1.0f;
|
||||
|
||||
++frames_;
|
||||
if (finishFrame_ >= 0) {
|
||||
float leadOut = bezierEaseInOut((frames_ - finishFrame_) * (1.0f / (float)FRAMES_LEAD_OUT));
|
||||
animatePos = 1.0f - leadOut;
|
||||
|
||||
if (frames_ >= finishFrame_ + FRAMES_LEAD_OUT) {
|
||||
// Actual finish happens here.
|
||||
screenManager()->finishDialog(this, finishResult_);
|
||||
}
|
||||
} else if (frames_ < FRAMES_LEAD_IN) {
|
||||
float leadIn = bezierEaseInOut(frames_ * (1.0f / (float)FRAMES_LEAD_IN));
|
||||
animatePos = leadIn;
|
||||
}
|
||||
|
||||
if (animatePos < 1.0f) {
|
||||
alpha_ = animatePos;
|
||||
scale_.x = 0.9f + animatePos * 0.1f;
|
||||
scale_.y = 0.9f + animatePos * 0.1f;
|
||||
|
||||
if (hasPopupOrigin_) {
|
||||
float xoff = popupOrigin_.x - g_display.dp_xres / 2;
|
||||
float yoff = popupOrigin_.y - g_display.dp_yres / 2;
|
||||
|
||||
// Pull toward the origin a bit.
|
||||
translation_.x = xoff * (1.0f - animatePos) * 0.2f;
|
||||
translation_.y = yoff * (1.0f - animatePos) * 0.2f;
|
||||
} else {
|
||||
translation_.y = -g_display.dp_yres * (1.0f - animatePos) * 0.2f;
|
||||
}
|
||||
} else {
|
||||
alpha_ = 1.0f;
|
||||
scale_.x = 1.0f;
|
||||
scale_.y = 1.0f;
|
||||
translation_.x = 0.0f;
|
||||
translation_.y = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
void PopupScreen::SetPopupOrigin(const UI::View *view) {
|
||||
hasPopupOrigin_ = true;
|
||||
popupOrigin_ = view->GetBounds().Center();
|
||||
}
|
||||
|
||||
void PopupScreen::TriggerFinish(DialogResult result) {
|
||||
if (CanComplete(result)) {
|
||||
ignoreInput_ = true;
|
||||
finishFrame_ = frames_;
|
||||
finishResult_ = result;
|
||||
|
||||
OnCompleted(result);
|
||||
}
|
||||
// Inform UI that popup close to hide OSK (if visible)
|
||||
System_NotifyUIEvent(UIEventNotification::POPUP_CLOSED);
|
||||
}
|
||||
|
||||
void PopupScreen::CreateViews() {
|
||||
using namespace UI;
|
||||
UIContext &dc = *screenManager()->getUIContext();
|
||||
|
||||
AnchorLayout *anchor = new AnchorLayout(new LayoutParams(FILL_PARENT, FILL_PARENT));
|
||||
anchor->Overflow(false);
|
||||
root_ = anchor;
|
||||
|
||||
float yres = screenManager()->getUIContext()->GetBounds().h;
|
||||
|
||||
AnchorLayoutParams *anchorParams;
|
||||
if (!alignTop_) {
|
||||
// Standard centering etc.
|
||||
anchorParams = new AnchorLayoutParams(PopupWidth(), FillVertical() ? yres - 30 : WRAP_CONTENT,
|
||||
dc.GetBounds().centerX(), dc.GetBounds().centerY() + offsetY_, NONE, NONE, true);
|
||||
} else {
|
||||
// Top-aligned, for dialogs where we need to pop a keyboard below.
|
||||
anchorParams = new AnchorLayoutParams(PopupWidth(), FillVertical() ? yres - 30 : WRAP_CONTENT,
|
||||
NONE, 0, NONE, NONE, false);
|
||||
}
|
||||
|
||||
box_ = new LinearLayout(ORIENT_VERTICAL, anchorParams);
|
||||
|
||||
root_->Add(box_);
|
||||
box_->SetBG(dc.GetTheme().popupStyle.background);
|
||||
box_->SetHasDropShadow(hasDropShadow_);
|
||||
// Since we scale a bit, make the dropshadow bleed past the edges.
|
||||
box_->SetDropShadowExpand(std::max(g_display.dp_xres, g_display.dp_yres));
|
||||
box_->SetSpacing(0.0f);
|
||||
|
||||
if (HasTitleBar()) {
|
||||
View *title = new PopupHeader(title_);
|
||||
box_->Add(title);
|
||||
}
|
||||
|
||||
CreatePopupContents(box_);
|
||||
root_->Recurse([](View *view) {
|
||||
view->SetPopupStyle(true);
|
||||
});
|
||||
|
||||
root_->SetDefaultFocusView(box_);
|
||||
if (ShowButtons() && !button1_.empty()) {
|
||||
// And the two buttons at the bottom.
|
||||
LinearLayout *buttonRow = new LinearLayout(ORIENT_HORIZONTAL, new LinearLayoutParams(200, WRAP_CONTENT));
|
||||
buttonRow->SetSpacing(0);
|
||||
Margins buttonMargins(5, 5);
|
||||
|
||||
// Adjust button order to the platform default.
|
||||
if (System_GetPropertyBool(SYSPROP_OK_BUTTON_LEFT)) {
|
||||
defaultButton_ = buttonRow->Add(new Button(button1_, new LinearLayoutParams(1.0f, buttonMargins)));
|
||||
defaultButton_->OnClick.Handle<UIScreen>(this, &UIScreen::OnOK);
|
||||
if (!button2_.empty())
|
||||
buttonRow->Add(new Button(button2_, new LinearLayoutParams(1.0f, buttonMargins)))->OnClick.Handle<UIScreen>(this, &UIScreen::OnCancel);
|
||||
} else {
|
||||
if (!button2_.empty())
|
||||
buttonRow->Add(new Button(button2_, new LinearLayoutParams(1.0f)))->OnClick.Handle<UIScreen>(this, &UIScreen::OnCancel);
|
||||
defaultButton_ = buttonRow->Add(new Button(button1_, new LinearLayoutParams(1.0f)));
|
||||
defaultButton_->OnClick.Handle<UIScreen>(this, &UIScreen::OnOK);
|
||||
}
|
||||
|
||||
box_->Add(buttonRow);
|
||||
}
|
||||
}
|
||||
|
||||
+4
-58
@@ -57,11 +57,13 @@ public:
|
||||
void OnCancel(UI::EventParams &e);
|
||||
void OnBack(UI::EventParams &e);
|
||||
|
||||
virtual UI::Margins RootMargins() const { return UI::Margins(0); }
|
||||
|
||||
protected:
|
||||
virtual void CreateViews() = 0;
|
||||
|
||||
void RecreateViews() override { recreateViews_ = true; }
|
||||
bool UsePortraitLayout() const;
|
||||
DeviceOrientation GetDeviceOrientation() const;
|
||||
|
||||
UI::ViewGroup *root_ = nullptr;
|
||||
Vec3 translation_ = Vec3(0.0f);
|
||||
@@ -79,7 +81,7 @@ protected:
|
||||
void DoRecreateViews();
|
||||
|
||||
bool recreateViews_ = true;
|
||||
bool lastPortrait_;
|
||||
DeviceOrientation lastOrientation_ = DeviceOrientation::Landscape;
|
||||
|
||||
private:
|
||||
std::mutex eventQueueLock_;
|
||||
@@ -95,59 +97,3 @@ public:
|
||||
private:
|
||||
bool finished_;
|
||||
};
|
||||
|
||||
class PopupScreen : public UIDialogScreen {
|
||||
public:
|
||||
PopupScreen(std::string_view title, std::string_view button1 = "", std::string_view button2 = "");
|
||||
|
||||
virtual void CreatePopupContents(UI::ViewGroup *parent) = 0;
|
||||
void CreateViews() override;
|
||||
bool isTransparent() const override { return true; }
|
||||
void touch(const TouchInput &touch) override;
|
||||
bool key(const KeyInput &key) override;
|
||||
|
||||
void TriggerFinish(DialogResult result) override;
|
||||
|
||||
void SetPopupOrigin(const UI::View *view);
|
||||
void SetPopupOffset(float y) { offsetY_ = y; }
|
||||
|
||||
void SetAlignTop(bool alignTop) { alignTop_ = alignTop; }
|
||||
|
||||
void SetHasDropShadow(bool has) { hasDropShadow_ = has; }
|
||||
|
||||
// For the postproc param sliders on DisplayLayoutScreen
|
||||
bool wantBrightBackground() const override { return !hasDropShadow_; }
|
||||
|
||||
protected:
|
||||
virtual bool FillVertical() const { return false; }
|
||||
virtual UI::Size PopupWidth() const { return 550; }
|
||||
virtual bool ShowButtons() const { return true; }
|
||||
virtual bool CanComplete(DialogResult result) { return true; }
|
||||
virtual void OnCompleted(DialogResult result) {}
|
||||
virtual bool HasTitleBar() const { return true; }
|
||||
const std::string &Title() { return title_; }
|
||||
|
||||
void update() override;
|
||||
|
||||
private:
|
||||
UI::LinearLayout *box_ = nullptr;
|
||||
UI::Button *defaultButton_ = nullptr;
|
||||
std::string title_;
|
||||
std::string button1_;
|
||||
std::string button2_;
|
||||
|
||||
enum {
|
||||
FRAMES_LEAD_IN = 6,
|
||||
FRAMES_LEAD_OUT = 4,
|
||||
};
|
||||
|
||||
int frames_ = 0;
|
||||
int finishFrame_ = -1;
|
||||
DialogResult finishResult_ = DR_CANCEL;
|
||||
bool hasPopupOrigin_ = false;
|
||||
Point2D popupOrigin_;
|
||||
float offsetY_ = 0.0f;
|
||||
bool alignTop_ = false;
|
||||
|
||||
bool hasDropShadow_ = true;
|
||||
};
|
||||
|
||||
+62
-20
@@ -468,14 +468,14 @@ void Choice::ClickInternal() {
|
||||
void Choice::GetContentDimensionsBySpec(const UIContext &dc, MeasureSpec horiz, MeasureSpec vert, float &w, float &h) const {
|
||||
float totalW = 0.0f;
|
||||
float totalH = 0.0f;
|
||||
if (image_.isValid()) {
|
||||
dc.Draw()->GetAtlas()->measureImage(image_, &w, &h);
|
||||
totalW = w * imgScale_ + 6;
|
||||
totalH = h * imgScale_;
|
||||
}
|
||||
if (!text_.empty()) {
|
||||
const int paddingX = 12;
|
||||
float availWidth = horiz.size - paddingX * 2 - textPadding_.horiz() - totalW;
|
||||
if (!text_.empty() && !hideTitle_) {
|
||||
int paddingLeft = 12;
|
||||
int paddingRight = 12;
|
||||
if (rightIconImage_.isValid()) {
|
||||
paddingRight = ITEM_HEIGHT;
|
||||
}
|
||||
|
||||
float availWidth = horiz.size - paddingLeft - paddingRight - textPadding_.horiz() - totalW;
|
||||
if (availWidth < 0.0f) {
|
||||
// Let it have as much space as it needs.
|
||||
availWidth = MAX_ITEM_SIZE;
|
||||
@@ -488,6 +488,16 @@ void Choice::GetContentDimensionsBySpec(const UIContext &dc, MeasureSpec horiz,
|
||||
dc.MeasureTextRect(dc.GetTheme().uiFont, scale, scale, text_, availBounds, &textW, &textH, FLAG_WRAP_TEXT);
|
||||
totalH = std::max(totalH, textH);
|
||||
totalW += textW;
|
||||
if (image_.isValid()) {
|
||||
totalW += 12;
|
||||
totalW += totalH;
|
||||
}
|
||||
} else {
|
||||
if (image_.isValid()) {
|
||||
dc.Draw()->GetAtlas()->measureImage(image_, &w, &h);
|
||||
totalW = w * imgScale_ + 6;
|
||||
totalH = h * imgScale_;
|
||||
}
|
||||
}
|
||||
|
||||
w = totalW + (text_.empty() ? 16 : 24);
|
||||
@@ -508,32 +518,42 @@ void Choice::Draw(UIContext &dc) {
|
||||
} else if (!text_.empty() && !hideTitle_) {
|
||||
dc.SetFontStyle(dc.GetTheme().uiFont);
|
||||
|
||||
int paddingX = 12;
|
||||
float availWidth = bounds_.w - paddingX * 2 - textPadding_.horiz();
|
||||
int paddingLeft = 12;
|
||||
int paddingRight = 12;
|
||||
|
||||
if (image_.isValid()) {
|
||||
paddingLeft = 0;
|
||||
const AtlasImage *image = dc.Draw()->GetAtlas()->getImage(image_);
|
||||
if (image) {
|
||||
_dbg_assert_(image);
|
||||
paddingX += image->w + 6;
|
||||
availWidth -= image->w + 6;
|
||||
// TODO: Use scale rotation and flip here as well (DrawImageRotated is always ALIGN_CENTER for now)
|
||||
dc.Draw()->DrawImage(image_, bounds_.x + 6, bounds_.centerY(), 1.0f, style.fgColor, ALIGN_LEFT | ALIGN_VCENTER);
|
||||
dc.Draw()->DrawImage(image_, bounds_.x + bounds_.h * 0.5f + paddingLeft, bounds_.centerY(), 1.0f, style.fgColor, ALIGN_CENTER);
|
||||
|
||||
paddingLeft += bounds_.h;
|
||||
}
|
||||
}
|
||||
|
||||
if (rightIconImage_.isValid()) {
|
||||
paddingRight = bounds_.h;
|
||||
}
|
||||
|
||||
float availWidth = bounds_.w - (paddingLeft + paddingRight + textPadding_.horiz());
|
||||
|
||||
if (centered_) {
|
||||
dc.DrawTextRectSqueeze(text_, bounds_, style.fgColor, ALIGN_CENTER | FLAG_WRAP_TEXT | drawTextFlags_);
|
||||
} else {
|
||||
if (rightIconImage_.isValid()) {
|
||||
uint32_t col = rightIconKeepColor_ ? 0xffffffff : style.fgColor; // Don't apply theme to gold icon
|
||||
|
||||
float iconX = bounds_.x2() - paddingRight + ITEM_HEIGHT * 0.5f;
|
||||
|
||||
if (shine_) {
|
||||
Bounds b = Bounds::FromCenter(bounds_.x2() - 32 - paddingX, bounds_.centerY(), bounds_.h * 0.4f);
|
||||
Bounds b = Bounds::FromCenter(iconX, bounds_.centerY(), bounds_.h * 0.4f);
|
||||
DrawIconShine(dc, b.Inset(5.0f, 5.0f), 0.65f, false);
|
||||
}
|
||||
dc.Draw()->DrawImageRotated(rightIconImage_, bounds_.x2() - 32 - paddingX, bounds_.centerY(), rightIconScale_, rightIconRot_, col, rightIconFlipH_);
|
||||
dc.Draw()->DrawImageRotated(rightIconImage_, iconX, bounds_.centerY(), rightIconScale_, rightIconRot_, col, rightIconFlipH_);
|
||||
}
|
||||
Bounds textBounds(bounds_.x + paddingX + textPadding_.left, bounds_.y, availWidth, bounds_.h);
|
||||
Bounds textBounds(bounds_.x + paddingLeft + textPadding_.left, bounds_.y, availWidth, bounds_.h);
|
||||
dc.DrawTextRectSqueeze(text_, textBounds, style.fgColor, ALIGN_VCENTER | FLAG_WRAP_TEXT | drawTextFlags_);
|
||||
}
|
||||
dc.SetFontScale(1.0f, 1.0f);
|
||||
@@ -629,7 +649,7 @@ void CollapsibleHeader::Draw(UIContext &dc) {
|
||||
float xoff = 37.0f;
|
||||
|
||||
dc.SetFontStyle(dc.GetTheme().uiFontSmall);
|
||||
dc.DrawText(text_, bounds_.x + 4 + xoff, bounds_.centerY(), style.fgColor, ALIGN_LEFT | ALIGN_VCENTER);
|
||||
dc.DrawText(text_, bounds_.x + 6 + xoff, bounds_.centerY(), style.fgColor, ALIGN_LEFT | ALIGN_VCENTER);
|
||||
dc.Draw()->DrawImageCenterTexel(dc.GetTheme().whiteImage, bounds_.x, bounds_.y2() - 2, bounds_.x2(), bounds_.y2(), style.fgColor);
|
||||
if (hasSubItems_) {
|
||||
dc.Draw()->DrawImageRotated(ImageID("I_ARROW"), bounds_.x + 20.0f, bounds_.y + 20.0f, 1.0f, *toggle_ ? -M_PI / 2 : M_PI, style.fgColor);
|
||||
@@ -1027,6 +1047,26 @@ void ImageView::Draw(UIContext &dc) {
|
||||
|
||||
const float bulletOffset = 25;
|
||||
|
||||
void SimpleTextView::GetContentDimensions(const UIContext &dc, float &w, float &h) const {
|
||||
dc.MeasureText(ComputeStyle(dc), 1.0f, 1.0f, text_, &w, &h, 0);
|
||||
}
|
||||
|
||||
void SimpleTextView::Draw(UIContext &dc) {
|
||||
uint32_t textColor = dc.GetTheme().itemStyle.fgColor;
|
||||
dc.SetFontStyle(ComputeStyle(dc));
|
||||
dc.DrawText(text_, bounds_.x, bounds_.y, textColor, 0);
|
||||
}
|
||||
|
||||
FontStyle SimpleTextView::ComputeStyle(const UIContext &dc) const {
|
||||
if (small_) {
|
||||
return dc.GetTheme().uiFontSmall;
|
||||
} else if (big_) {
|
||||
return dc.GetTheme().uiFontBig;
|
||||
} else {
|
||||
return dc.GetTheme().uiFont;
|
||||
}
|
||||
}
|
||||
|
||||
void TextView::GetContentDimensionsBySpec(const UIContext &dc, MeasureSpec horiz, MeasureSpec vert, float &w, float &h) const {
|
||||
Bounds bounds(0, 0, layoutParams_->width, layoutParams_->height);
|
||||
if (bounds.w < 0) {
|
||||
@@ -1046,9 +1086,11 @@ void TextView::GetContentDimensionsBySpec(const UIContext &dc, MeasureSpec horiz
|
||||
} else if (big_) {
|
||||
style = &dc.GetTheme().uiFontBig;
|
||||
}
|
||||
dc.MeasureTextRect(*style, 1.0f, 1.0f, text_, bounds, &w, &h, textAlign_);
|
||||
w += pad_ * 2.0f;
|
||||
h += pad_ * 2.0f;
|
||||
float measuredW;
|
||||
float measuredH;
|
||||
dc.MeasureTextRect(*style, 1.0f, 1.0f, text_, bounds, &measuredW, &measuredH, textAlign_);
|
||||
w = measuredW + pad_ * 2.0f;
|
||||
h = measuredH + pad_ * 2.0f;
|
||||
if (bullet_) {
|
||||
w += bulletOffset;
|
||||
}
|
||||
|
||||
+35
-20
@@ -16,6 +16,7 @@
|
||||
#include <vector>
|
||||
|
||||
#include "Common/Render/TextureAtlas.h"
|
||||
#include "Common/Render/Text/Font.h"
|
||||
#include "Common/Math/lin/matrix4x4.h"
|
||||
#include "Common/Math/math_util.h"
|
||||
#include "Common/Math/geom2d.h"
|
||||
@@ -30,8 +31,6 @@ struct KeyInput;
|
||||
struct TouchInput;
|
||||
struct AxisInput;
|
||||
|
||||
struct ImageID;
|
||||
|
||||
class DrawBuffer;
|
||||
class Texture;
|
||||
class UIContext;
|
||||
@@ -78,22 +77,13 @@ struct Style {
|
||||
ImageID image; // where applicable.
|
||||
};
|
||||
|
||||
struct FontStyle {
|
||||
FontStyle() {}
|
||||
FontStyle(FontID atlasFnt, const char *name, int size) : atlasFont(atlasFnt), fontName(name), sizePts(size) {}
|
||||
|
||||
FontID atlasFont{ nullptr };
|
||||
// For native fonts:
|
||||
std::string fontName;
|
||||
int sizePts = 0;
|
||||
int flags = 0;
|
||||
};
|
||||
|
||||
// To use with an UI atlas.
|
||||
struct Theme {
|
||||
FontStyle uiFont;
|
||||
FontStyle uiFontTiny;
|
||||
FontStyle uiFontSmall;
|
||||
FontStyle uiFont;
|
||||
FontStyle uiFontBig;
|
||||
FontStyle uiFontCode;
|
||||
|
||||
ImageID checkOn;
|
||||
ImageID checkOff;
|
||||
@@ -144,7 +134,7 @@ static constexpr Size FILL_PARENT = -2.0f;
|
||||
static constexpr Size ITEM_HEIGHT = 64.f;
|
||||
|
||||
// Gravity
|
||||
enum Gravity {
|
||||
enum class Gravity {
|
||||
G_LEFT = 0,
|
||||
G_RIGHT = 1,
|
||||
G_HCENTER = 2,
|
||||
@@ -165,6 +155,7 @@ enum Gravity {
|
||||
|
||||
G_VERTMASK = 3 << 2,
|
||||
};
|
||||
ENUM_CLASS_BITOPS(Gravity);
|
||||
|
||||
enum Borders {
|
||||
BORDER_NONE = 0,
|
||||
@@ -778,10 +769,12 @@ private:
|
||||
// Different key handling.
|
||||
class StickyChoice : public Choice {
|
||||
public:
|
||||
StickyChoice(std::string_view text, std::string_view smallText = "", LayoutParams *layoutParams = 0)
|
||||
StickyChoice(std::string_view text, std::string_view smallText = "", LayoutParams *layoutParams = nullptr)
|
||||
: Choice(text, smallText, false, layoutParams) {}
|
||||
StickyChoice(ImageID buttonImage, LayoutParams *layoutParams = 0)
|
||||
StickyChoice(ImageID buttonImage, LayoutParams *layoutParams = nullptr)
|
||||
: Choice(buttonImage, layoutParams) {}
|
||||
StickyChoice(std::string_view text, ImageID image, LayoutParams *layoutParams = nullptr)
|
||||
: Choice(text, image, layoutParams) {}
|
||||
|
||||
bool Key(const KeyInput &key) override;
|
||||
bool Touch(const TouchInput &touch) override;
|
||||
@@ -826,6 +819,9 @@ public:
|
||||
AbstractChoiceWithValueDisplay(std::string_view text, LayoutParams *layoutParams = nullptr)
|
||||
: Choice(text, layoutParams) {
|
||||
}
|
||||
AbstractChoiceWithValueDisplay(std::string_view text, ImageID image, LayoutParams *layoutParams = nullptr)
|
||||
: Choice(text, image, layoutParams) {
|
||||
}
|
||||
|
||||
void Draw(UIContext &dc) override;
|
||||
void GetContentDimensionsBySpec(const UIContext &dc, MeasureSpec horiz, MeasureSpec vert, float &w, float &h) const override;
|
||||
@@ -984,6 +980,25 @@ private:
|
||||
float size_;
|
||||
};
|
||||
|
||||
// Single-line text only.
|
||||
class SimpleTextView : public InertView {
|
||||
public:
|
||||
SimpleTextView(std::string_view text, LayoutParams *layoutParams = 0)
|
||||
: InertView(layoutParams), text_(text) {
|
||||
}
|
||||
void SetSmall(bool small) { small_ = small; }
|
||||
void SetBig(bool big) { big_ = big; }
|
||||
void GetContentDimensions(const UIContext &dc, float &w, float &h) const override;
|
||||
void Draw(UIContext &dc) override;
|
||||
|
||||
private:
|
||||
FontStyle ComputeStyle(const UIContext &dc) const;
|
||||
std::string text_;
|
||||
bool small_ = false;
|
||||
bool big_ = false;
|
||||
};
|
||||
|
||||
|
||||
class TextView : public InertView {
|
||||
public:
|
||||
TextView(std::string_view text, LayoutParams *layoutParams = 0)
|
||||
@@ -1015,7 +1030,7 @@ private:
|
||||
int textAlign_;
|
||||
uint32_t textColor_;
|
||||
bool hasTextColor_ = false;
|
||||
bool small_;
|
||||
bool small_ = false;
|
||||
bool big_ = false;
|
||||
bool shadow_ = false;
|
||||
bool focusable_ = false;
|
||||
@@ -1033,8 +1048,8 @@ public:
|
||||
Event OnClick;
|
||||
|
||||
private:
|
||||
bool down_;
|
||||
bool dragging_;
|
||||
bool down_ = false;
|
||||
bool dragging_ = false;
|
||||
};
|
||||
|
||||
class TextEdit : public View {
|
||||
|
||||
+22
-18
@@ -24,20 +24,22 @@
|
||||
|
||||
namespace UI {
|
||||
|
||||
void ApplyGravity(const Bounds &outer, const Margins &margins, float w, float h, int gravity, Bounds &inner) {
|
||||
void ApplyGravity(const Bounds &outer, const Margins &margins, float w, float h, Gravity gravity, Bounds &inner) {
|
||||
inner.w = w;
|
||||
inner.h = h;
|
||||
|
||||
switch (gravity & G_HORIZMASK) {
|
||||
case G_LEFT: inner.x = outer.x + margins.left; break;
|
||||
case G_RIGHT: inner.x = outer.x + outer.w - w - margins.right; break;
|
||||
case G_HCENTER: inner.x = outer.x + (outer.w - w) * 0.5f; break;
|
||||
switch ((Gravity)((int)gravity & (int)Gravity::G_HORIZMASK)) {
|
||||
case Gravity::G_LEFT: inner.x = outer.x + margins.left; break;
|
||||
case Gravity::G_RIGHT: inner.x = outer.x + outer.w - w - margins.right; break;
|
||||
case Gravity::G_HCENTER: inner.x = outer.x + (outer.w - w) * 0.5f; break;
|
||||
default: break;
|
||||
}
|
||||
|
||||
switch (gravity & G_VERTMASK) {
|
||||
case G_TOP: inner.y = outer.y + margins.top; break;
|
||||
case G_BOTTOM: inner.y = outer.y + outer.h - h - margins.bottom; break;
|
||||
case G_VCENTER: inner.y = outer.y + (outer.h - h) * 0.5f; break;
|
||||
switch ((Gravity)((int)gravity & (int)Gravity::G_VERTMASK)) {
|
||||
case Gravity::G_TOP: inner.y = outer.y + margins.top; break;
|
||||
case Gravity::G_BOTTOM: inner.y = outer.y + outer.h - h - margins.bottom; break;
|
||||
case Gravity::G_VCENTER: inner.y = outer.y + (outer.h - h) * 0.5f; break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -701,7 +703,7 @@ void LinearLayout::Layout() {
|
||||
|
||||
const LinearLayoutParams *linLayoutParams = views_[i]->GetLayoutParams()->As<LinearLayoutParams>();
|
||||
|
||||
Gravity gravity = G_TOPLEFT;
|
||||
Gravity gravity = Gravity::G_TOPLEFT;
|
||||
Margins margins = defaultMargins_;
|
||||
if (linLayoutParams) {
|
||||
if (linLayoutParams->HasMargins())
|
||||
@@ -803,10 +805,12 @@ void AnchorLayout::MeasureViews(const UIContext &dc, MeasureSpec horiz, MeasureS
|
||||
width = params->width;
|
||||
height = params->height;
|
||||
|
||||
if (!params->center) {
|
||||
if (!(params->centering & Centering::Horizontal)) {
|
||||
if (params->left > NONE && params->right > NONE) {
|
||||
width = measuredWidth_ - params->left - params->right;
|
||||
}
|
||||
}
|
||||
if (!(params->centering & Centering::Vertical)) {
|
||||
if (params->top > NONE && params->bottom > NONE) {
|
||||
height = measuredHeight_ - params->top - params->bottom;
|
||||
}
|
||||
@@ -837,22 +841,22 @@ static void ApplyAnchorLayoutParams(float measuredWidth, float measuredHeight, c
|
||||
if (vBounds->h > container.h) vBounds->h = container.h;
|
||||
|
||||
float left = 0, top = 0, right = 0, bottom = 0;
|
||||
bool center = false;
|
||||
Centering centering = Centering::None;
|
||||
if (params) {
|
||||
left = params->left;
|
||||
top = params->top;
|
||||
right = params->right;
|
||||
bottom = params->bottom;
|
||||
center = params->center;
|
||||
centering = params->centering;
|
||||
}
|
||||
|
||||
if (left > NONE) {
|
||||
vBounds->x = container.x + left;
|
||||
if (center)
|
||||
if (centering & Centering::Horizontal)
|
||||
vBounds->x -= vBounds->w * 0.5f;
|
||||
} else if (right > NONE) {
|
||||
vBounds->x = container.x2() - right - vBounds->w;
|
||||
if (center) {
|
||||
if (centering & Centering::Horizontal) {
|
||||
vBounds->x += vBounds->w * 0.5f;
|
||||
}
|
||||
} else {
|
||||
@@ -862,11 +866,11 @@ static void ApplyAnchorLayoutParams(float measuredWidth, float measuredHeight, c
|
||||
|
||||
if (top > NONE) {
|
||||
vBounds->y = container.y + top;
|
||||
if (center)
|
||||
if (centering & Centering::Vertical)
|
||||
vBounds->y -= vBounds->h * 0.5f;
|
||||
} else if (bottom > NONE) {
|
||||
vBounds->y = container.y2() - bottom - vBounds->h;
|
||||
if (center)
|
||||
if (centering & Centering::Vertical)
|
||||
vBounds->y += vBounds->h * 0.5f;
|
||||
} else {
|
||||
// Both top and bottom are NONE. Center.
|
||||
@@ -925,7 +929,7 @@ void GridLayout::Layout() {
|
||||
|
||||
const GridLayoutParams *lp = views_[i]->GetLayoutParams()->As<GridLayoutParams>();
|
||||
Bounds itemBounds, innerBounds;
|
||||
Gravity grav = lp ? lp->gravity : G_CENTER;
|
||||
Gravity grav = lp ? lp->gravity : Gravity::G_CENTER;
|
||||
|
||||
itemBounds.x = bounds_.x + x;
|
||||
itemBounds.y = bounds_.y + y;
|
||||
|
||||
+25
-15
@@ -109,22 +109,30 @@ public:
|
||||
|
||||
const float NONE = -FLT_MAX;
|
||||
|
||||
enum class Centering {
|
||||
None = 0,
|
||||
Vertical = 1,
|
||||
Horizontal = 2,
|
||||
Both = 3, // 1 | 2
|
||||
};
|
||||
ENUM_CLASS_BITOPS(Centering);
|
||||
|
||||
class AnchorLayoutParams : public LayoutParams {
|
||||
public:
|
||||
AnchorLayoutParams(Size w, Size h, float l, float t, float r, float b, bool c = false)
|
||||
: LayoutParams(w, h, LP_ANCHOR), left(l), top(t), right(r), bottom(b), center(c) {}
|
||||
AnchorLayoutParams(Size w, Size h, float l, float t, float r, float b, Centering c = Centering::None)
|
||||
: LayoutParams(w, h, LP_ANCHOR), left(l), top(t), right(r), bottom(b), centering(c) {}
|
||||
// There's a small hack here to make this behave more intuitively - AnchorLayout ordinarily ignores FILL_PARENT.
|
||||
AnchorLayoutParams(Size w, Size h, bool c = false)
|
||||
: LayoutParams(w, h, LP_ANCHOR), left(0), top(0), right(w == FILL_PARENT ? 0 : NONE), bottom(h == FILL_PARENT ? 0 : NONE), center(c) {
|
||||
AnchorLayoutParams(Size w, Size h, Centering c = Centering::None)
|
||||
: LayoutParams(w, h, LP_ANCHOR), left(0), top(0), right(w == FILL_PARENT ? 0 : NONE), bottom(h == FILL_PARENT ? 0 : NONE), centering(c) {
|
||||
}
|
||||
AnchorLayoutParams(float l, float t, float r, float b, bool c = false)
|
||||
: LayoutParams(WRAP_CONTENT, WRAP_CONTENT, LP_ANCHOR), left(l), top(t), right(r), bottom(b), center(c) {}
|
||||
AnchorLayoutParams(float l, float t, float r, float b, Centering c = Centering::None)
|
||||
: LayoutParams(WRAP_CONTENT, WRAP_CONTENT, LP_ANCHOR), left(l), top(t), right(r), bottom(b), centering(c) {}
|
||||
|
||||
// These are not bounds, but distances from the container edges.
|
||||
// Set to NONE to not attach this edge to the container.
|
||||
// If two opposite edges are NONE, centering will happen.
|
||||
float left, top, right, bottom;
|
||||
bool center; // If set, only two "sides" can be set, and they refer to the center, not the edge, of the view being layouted.
|
||||
Centering centering; // If set, only two "sides" can be set, and they refer to the center, not the edge, of the view being layouted.
|
||||
|
||||
static LayoutParamsType StaticType() {
|
||||
return LP_ANCHOR;
|
||||
@@ -149,21 +157,23 @@ private:
|
||||
class LinearLayoutParams : public LayoutParams {
|
||||
public:
|
||||
LinearLayoutParams()
|
||||
: LayoutParams(LP_LINEAR), weight(0.0f), gravity(G_TOPLEFT), hasMargins_(false) {}
|
||||
explicit LinearLayoutParams(float wgt, Gravity grav = G_TOPLEFT)
|
||||
: LayoutParams(LP_LINEAR), weight(0.0f), gravity(Gravity::G_TOPLEFT), hasMargins_(false) {}
|
||||
explicit LinearLayoutParams(float wgt, Gravity grav = Gravity::G_TOPLEFT)
|
||||
: LayoutParams(LP_LINEAR), weight(wgt), gravity(grav), hasMargins_(false) {}
|
||||
LinearLayoutParams(float wgt, const Margins &mgn)
|
||||
: LayoutParams(LP_LINEAR), weight(wgt), gravity(G_TOPLEFT), margins(mgn), hasMargins_(true) {}
|
||||
LinearLayoutParams(Size w, Size h, float wgt = 0.0f, Gravity grav = G_TOPLEFT)
|
||||
: LayoutParams(LP_LINEAR), weight(wgt), gravity(Gravity::G_TOPLEFT), margins(mgn), hasMargins_(true) {}
|
||||
LinearLayoutParams(float wgt, Gravity grav, const Margins &mgn)
|
||||
: LayoutParams(LP_LINEAR), weight(wgt), gravity(grav), margins(mgn), hasMargins_(true) {}
|
||||
LinearLayoutParams(Size w, Size h, float wgt = 0.0f, Gravity grav = Gravity::G_TOPLEFT)
|
||||
: LayoutParams(w, h, LP_LINEAR), weight(wgt), gravity(grav), margins(0), hasMargins_(false) {}
|
||||
LinearLayoutParams(Size w, Size h, float wgt, Gravity grav, const Margins &mgn)
|
||||
: LayoutParams(w, h, LP_LINEAR), weight(wgt), gravity(grav), margins(mgn), hasMargins_(true) {}
|
||||
LinearLayoutParams(Size w, Size h, const Margins &mgn)
|
||||
: LayoutParams(w, h, LP_LINEAR), weight(0.0f), gravity(G_TOPLEFT), margins(mgn), hasMargins_(true) {}
|
||||
: LayoutParams(w, h, LP_LINEAR), weight(0.0f), gravity(Gravity::G_TOPLEFT), margins(mgn), hasMargins_(true) {}
|
||||
LinearLayoutParams(Size w, Size h, float wgt, const Margins &mgn)
|
||||
: LayoutParams(w, h, LP_LINEAR), weight(wgt), gravity(G_TOPLEFT), margins(mgn), hasMargins_(true) {}
|
||||
: LayoutParams(w, h, LP_LINEAR), weight(wgt), gravity(Gravity::G_TOPLEFT), margins(mgn), hasMargins_(true) {}
|
||||
LinearLayoutParams(const Margins &mgn)
|
||||
: LayoutParams(WRAP_CONTENT, WRAP_CONTENT, LP_LINEAR), weight(0.0f), gravity(G_TOPLEFT), margins(mgn), hasMargins_(true) {}
|
||||
: LayoutParams(WRAP_CONTENT, WRAP_CONTENT, LP_LINEAR), weight(0.0f), gravity(Gravity::G_TOPLEFT), margins(mgn), hasMargins_(true) {}
|
||||
|
||||
float weight;
|
||||
Gravity gravity;
|
||||
@@ -226,7 +236,7 @@ struct GridLayoutSettings {
|
||||
class GridLayoutParams : public LayoutParams {
|
||||
public:
|
||||
GridLayoutParams()
|
||||
: LayoutParams(LP_GRID), gravity(G_CENTER) {}
|
||||
: LayoutParams(LP_GRID), gravity(Gravity::G_CENTER) {}
|
||||
explicit GridLayoutParams(Gravity grav)
|
||||
: LayoutParams(LP_GRID), gravity(grav) {
|
||||
}
|
||||
|
||||
+12
-5
@@ -170,13 +170,18 @@ void Compatibility::CheckVRSettings(IniFile &iniFile, const std::string &gameID)
|
||||
|
||||
void Compatibility::CheckSetting(IniFile &iniFile, const std::string &gameID, const char *option, bool *flag) {
|
||||
if (ignored_.find(option) == ignored_.end()) {
|
||||
iniFile.Get(option, gameID.c_str(), flag);
|
||||
Section *section = iniFile.GetSection(option);
|
||||
if (!section) {
|
||||
// Not found, skip.
|
||||
return;
|
||||
}
|
||||
section->Get(gameID, flag);
|
||||
|
||||
// Shortcut for debugging, sometimes useful to globally enable compat flags.
|
||||
bool all = false;
|
||||
iniFile.Get(option, "ALL", &all);
|
||||
section->Get("ALL", &all);
|
||||
if (all) {
|
||||
*flag |= all;
|
||||
*flag = true;
|
||||
if (!activeList_.empty()) {
|
||||
activeList_ += "\n";
|
||||
}
|
||||
@@ -187,14 +192,16 @@ void Compatibility::CheckSetting(IniFile &iniFile, const std::string &gameID, co
|
||||
|
||||
void Compatibility::CheckSetting(IniFile &iniFile, const std::string &gameID, const char *option, float *flag) {
|
||||
std::string value;
|
||||
if (iniFile.Get(option, gameID.c_str(), &value)) {
|
||||
Section *section = iniFile.GetSection(option);
|
||||
if (section && section->Get(gameID.c_str(), &value)) {
|
||||
*flag = stof(value);
|
||||
}
|
||||
}
|
||||
|
||||
void Compatibility::CheckSetting(IniFile &iniFile, const std::string &gameID, const char *option, int *flag) {
|
||||
std::string value;
|
||||
if (iniFile.Get(option, gameID.c_str(), &value)) {
|
||||
Section *section = iniFile.GetSection(option);
|
||||
if (section && section->Get(gameID.c_str(), &value)) {
|
||||
*flag = stof(value);
|
||||
}
|
||||
}
|
||||
|
||||
+332
-330
File diff suppressed because it is too large
Load Diff
+133
-86
@@ -18,6 +18,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
@@ -25,6 +26,7 @@
|
||||
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Common/File/Path.h"
|
||||
#include "Common/Math/geom2d.h"
|
||||
#include "Core/ConfigValues.h"
|
||||
|
||||
extern const char *PPSSPP_GIT_VERSION;
|
||||
@@ -37,6 +39,7 @@ namespace http {
|
||||
struct UrlEncoder;
|
||||
|
||||
class Section;
|
||||
class IniFile;
|
||||
|
||||
class PlayTimeTracker {
|
||||
public:
|
||||
@@ -61,17 +64,87 @@ private:
|
||||
|
||||
struct ConfigSetting;
|
||||
|
||||
struct Config {
|
||||
struct ConfigSectionMeta {
|
||||
ConfigBlock *configBlock;
|
||||
const ConfigSetting *settings;
|
||||
size_t settingsCount;
|
||||
std::string_view section;
|
||||
std::string_view fallbackSectionName; // used if section is not found (useful when moving settings into a struct from Config).
|
||||
};
|
||||
|
||||
struct DisplayLayoutConfig : public ConfigBlock {
|
||||
int iDisplayFilter = SCALE_LINEAR; // 1 = linear, 2 = nearest
|
||||
bool bDisplayStretch = false; // Automatically matches the aspect ratio of the window.
|
||||
float fDisplayOffsetX = 0.5f;
|
||||
float fDisplayOffsetY = 0.5f;
|
||||
float fDisplayScale = 1.0f; // Relative to the most constraining axis (x or y).
|
||||
bool bDisplayIntegerScale = false; // Snaps scaling to integer scale factors in raw pixels.
|
||||
float fDisplayAspectRatio = 1.0f; // Stored relative to the PSP's native ratio, so 1.0 is the normal pixel aspect ratio.
|
||||
int iInternalScreenRotation = ROTATION_LOCKED_HORIZONTAL; // The internal screen rotation angle. Useful for vertical SHMUPs and similar.
|
||||
bool bIgnoreScreenInsets = true; // Android: Center screen disregarding insets if this is enabled.
|
||||
|
||||
// Deprecated
|
||||
bool bEnableCardboardVR = false; // Cardboard Master Switch
|
||||
int iCardboardScreenSize = 50; // Screen Size (in %)
|
||||
int iCardboardXShift = 0; // X-Shift of Screen (in %)
|
||||
int iCardboardYShift = 0; // Y-Shift of Screen (in %)
|
||||
|
||||
bool InternalRotationIsPortrait() const;
|
||||
bool CanResetToDefault() const override { return true; }
|
||||
bool ResetToDefault(std::string_view blockName) override;
|
||||
size_t Size() const override { return sizeof(DisplayLayoutConfig); } // For sanity checks
|
||||
};
|
||||
|
||||
struct TouchControlConfig : public ConfigBlock {
|
||||
//space between PSP buttons
|
||||
//the PSP button's center (triangle, circle, square, cross)
|
||||
ConfigTouchPos touchActionButtonCenter;
|
||||
float fActionButtonSpacing = 0.0f;
|
||||
//radius of the D-pad (PSP cross)
|
||||
// int iDpadRadius;
|
||||
//the D-pad (PSP cross) position
|
||||
ConfigTouchPos touchDpad;
|
||||
float fDpadSpacing = 0.0f;
|
||||
ConfigTouchPos touchStartKey;
|
||||
ConfigTouchPos touchSelectKey;
|
||||
ConfigTouchPos touchFastForwardKey;
|
||||
ConfigTouchPos touchLKey;
|
||||
ConfigTouchPos touchRKey;
|
||||
ConfigTouchPos touchAnalogStick;
|
||||
ConfigTouchPos touchRightAnalogStick;
|
||||
|
||||
enum { CUSTOM_BUTTON_COUNT = 20 };
|
||||
|
||||
ConfigTouchPos touchCustom[CUSTOM_BUTTON_COUNT];
|
||||
|
||||
float fLeftStickHeadScale = 1.0f;
|
||||
float fRightStickHeadScale = 1.0f;
|
||||
|
||||
bool bHideStickBackground = false;
|
||||
|
||||
bool bShowTouchCircle = true;
|
||||
bool bShowTouchCross = true;
|
||||
bool bShowTouchTriangle = true;
|
||||
bool bShowTouchSquare = true;
|
||||
|
||||
void ResetLayout();
|
||||
|
||||
bool CanResetToDefault() const override { return true; }
|
||||
bool ResetToDefault(std::string_view blockName) override;
|
||||
size_t Size() const override { return sizeof(TouchControlConfig); } // For sanity checks
|
||||
};
|
||||
|
||||
struct Config : public ConfigBlock {
|
||||
public:
|
||||
Config();
|
||||
~Config();
|
||||
|
||||
size_t Size() const override { return sizeof(Config); }
|
||||
|
||||
// Whether to save the config on close.
|
||||
bool bSaveSettings;
|
||||
bool bFirstRun;
|
||||
bool bGameSpecific = false;
|
||||
bool bUpdatedInstanceCounter = false;
|
||||
bool bBrowse; // show a file browser on startup. TODO: Does anyone use this?
|
||||
|
||||
int iRunCount; // To be used to for example check for updates every 10 runs and things like that.
|
||||
|
||||
@@ -98,8 +171,6 @@ public:
|
||||
bool bIgnoreWindowsKey;
|
||||
bool bRestartRequired;
|
||||
|
||||
std::string sFont;
|
||||
|
||||
bool bPauseWhenMinimized;
|
||||
|
||||
bool bPauseExitsEmulator;
|
||||
@@ -121,6 +192,7 @@ public:
|
||||
|
||||
bool bDisableHTTPS;
|
||||
|
||||
bool bShrinkIfWindowSmall;
|
||||
bool bSeparateSASThread;
|
||||
int iIOTimingMethod;
|
||||
int iLockedCPUSpeed;
|
||||
@@ -146,8 +218,7 @@ public:
|
||||
int iDisableHLE;
|
||||
int iForceEnableHLE; // This is the opposite of DisableHLE but can force on HLE even when we've made it permanently off. Only used in tests, not hooked up to the ini file yet.
|
||||
|
||||
int iScreenRotation; // The rotation angle of the PPSSPP UI. Only supported on Android and possibly other mobile platforms.
|
||||
int iInternalScreenRotation; // The internal screen rotation angle. Useful for vertical SHMUPs and similar.
|
||||
int iScreenRotation; // Screen rotation lock. Only supported on Android and possibly other mobile platforms.
|
||||
|
||||
std::string sReportHost;
|
||||
std::vector<std::string> vPinnedPaths;
|
||||
@@ -194,29 +265,20 @@ public:
|
||||
int iTexFiltering; // 1 = auto , 2 = nearest , 3 = linear , 4 = auto max quality
|
||||
bool bSmart2DTexFiltering;
|
||||
|
||||
bool bDisplayStretch; // Automatically matches the aspect ratio of the window.
|
||||
int iDisplayFilter; // 1 = linear, 2 = nearest
|
||||
float fDisplayOffsetX;
|
||||
float fDisplayOffsetY;
|
||||
float fDisplayScale; // Relative to the most constraining axis (x or y).
|
||||
bool bDisplayIntegerScale; // Snaps scaling to integer scale factors in raw pixels.
|
||||
// We'll carry over the old single layout into landscape for now.
|
||||
DisplayLayoutConfig displayLayoutLandscape;
|
||||
DisplayLayoutConfig displayLayoutPortrait;
|
||||
|
||||
bool bDisplayCropTo16x9; // Crops to 16:9 if the resolution is very close.
|
||||
float fDisplayAspectRatio; // Stored relative to the PSP's native ratio, so 1.0 is the normal pixel aspect ratio.
|
||||
|
||||
bool bImmersiveMode; // Mode on Android Kitkat 4.4 and later that hides the back button etc.
|
||||
bool bSustainedPerformanceMode; // Android: Slows clocks down to avoid overheating/speed fluctuations.
|
||||
bool bIgnoreScreenInsets; // Android: Center screen disregarding insets if this is enabled.
|
||||
|
||||
bool bShowImDebugger;
|
||||
|
||||
int iFrameSkip;
|
||||
bool bAutoFrameSkip;
|
||||
|
||||
bool bEnableCardboardVR; // Cardboard Master Switch
|
||||
int iCardboardScreenSize; // Screen Size (in %)
|
||||
int iCardboardXShift; // X-Shift of Screen (in %)
|
||||
int iCardboardYShift; // Y-Shift of Screen (in %)
|
||||
|
||||
int iWindowX;
|
||||
int iWindowY;
|
||||
int iWindowWidth; // Windows and other windowed environments
|
||||
@@ -381,9 +443,11 @@ public:
|
||||
bool bAnalogGesture;
|
||||
float fAnalogGestureSensibility;
|
||||
|
||||
// Controls Visibility
|
||||
bool bShowTouchControls = false;
|
||||
|
||||
// Disable diagonals
|
||||
bool bDisableDpadDiagonals;
|
||||
|
||||
bool bGamepadOnlyFocused;
|
||||
|
||||
// Control Style
|
||||
@@ -404,40 +468,11 @@ public:
|
||||
// Touch gliding (see #14490)
|
||||
bool bTouchGliding;
|
||||
|
||||
//space between PSP buttons
|
||||
//the PSP button's center (triangle, circle, square, cross)
|
||||
ConfigTouchPos touchActionButtonCenter;
|
||||
float fActionButtonSpacing;
|
||||
//radius of the D-pad (PSP cross)
|
||||
// int iDpadRadius;
|
||||
//the D-pad (PSP cross) position
|
||||
ConfigTouchPos touchDpad;
|
||||
float fDpadSpacing;
|
||||
ConfigTouchPos touchStartKey;
|
||||
ConfigTouchPos touchSelectKey;
|
||||
ConfigTouchPos touchFastForwardKey;
|
||||
ConfigTouchPos touchLKey;
|
||||
ConfigTouchPos touchRKey;
|
||||
ConfigTouchPos touchAnalogStick;
|
||||
ConfigTouchPos touchRightAnalogStick;
|
||||
TouchControlConfig touchControlsLandscape;
|
||||
TouchControlConfig touchControlsPortrait;
|
||||
|
||||
enum { CUSTOM_BUTTON_COUNT = 20 };
|
||||
|
||||
ConfigTouchPos touchCustom[CUSTOM_BUTTON_COUNT];
|
||||
|
||||
float fLeftStickHeadScale;
|
||||
float fRightStickHeadScale;
|
||||
bool bHideStickBackground;
|
||||
|
||||
// Controls Visibility
|
||||
bool bShowTouchControls;
|
||||
|
||||
bool bShowTouchCircle;
|
||||
bool bShowTouchCross;
|
||||
bool bShowTouchTriangle;
|
||||
bool bShowTouchSquare;
|
||||
|
||||
ConfigCustomButton CustomButton[CUSTOM_BUTTON_COUNT];
|
||||
// These are shared between portrait and landscape, just the positions aren't.
|
||||
ConfigCustomButton CustomButton[TouchControlConfig::CUSTOM_BUTTON_COUNT];
|
||||
|
||||
// Ignored on iOS and other platforms that lack pause.
|
||||
bool bShowTouchPause;
|
||||
@@ -622,26 +657,27 @@ public:
|
||||
void Reload();
|
||||
void RestoreDefaults(RestoreSettingsBits whatToRestore, bool log = false);
|
||||
|
||||
//per game config managment, should maybe be in it's own class
|
||||
void changeGameSpecific(const std::string &gameId = "", const std::string &title = "");
|
||||
bool createGameConfig(const std::string &game_id);
|
||||
bool deleteGameConfig(const std::string& pGameId);
|
||||
bool loadGameConfig(const std::string &game_id, const std::string &title);
|
||||
bool saveGameConfig(const std::string &pGameId, const std::string &title);
|
||||
void unloadGameConfig();
|
||||
Path getGameConfigFile(const std::string &gameId, bool *exists);
|
||||
bool hasGameConfig(const std::string &game_id);
|
||||
// Per-game config management.
|
||||
void ChangeGameSpecific(const std::string &gameId = "", std::string_view title = "");
|
||||
|
||||
// Note: This doesn't switch to the config, just creates it.
|
||||
bool CreateGameConfig(std::string_view gameId);
|
||||
bool DeleteGameConfig(std::string_view gameId);
|
||||
bool LoadGameConfig(const std::string &gameId);
|
||||
bool SaveGameConfig(const std::string &pGameId, std::string_view titleForComment);
|
||||
void UnloadGameConfig();
|
||||
|
||||
Path GetGameConfigFilePath(std::string_view gameId, bool *exists);
|
||||
bool HasGameConfig(std::string_view gameId);
|
||||
bool IsGameSpecific() const { return gameSpecific_; }
|
||||
|
||||
void SetSearchPath(const Path &path);
|
||||
const Path FindConfigFile(const std::string &baseFilename, bool *exists);
|
||||
Path FindConfigFile(std::string_view baseFilename, bool *exists) const;
|
||||
|
||||
void UpdateIniLocation(const char *iniFileName = nullptr, const char *controllerIniFilename = nullptr);
|
||||
|
||||
void ResetControlLayout();
|
||||
|
||||
void GetReportingInfo(UrlEncoder &data) const;
|
||||
|
||||
bool IsPortrait() const;
|
||||
int NextValidBackend();
|
||||
bool IsBackendEnabled(GPUBackend backend);
|
||||
|
||||
@@ -651,42 +687,53 @@ public:
|
||||
return bFullScreen;
|
||||
}
|
||||
|
||||
const std::map<std::string, std::pair<std::string, int>, std::less<>> &GetLangValuesMapping();
|
||||
bool LoadAppendedConfig();
|
||||
void SetAppendedConfigIni(const Path &path);
|
||||
void SetAppendedConfigIni(const Path &path) { appendedConfigFileName_ = path; }
|
||||
void UpdateAfterSettingAutoFrameSkip();
|
||||
void NotifyUpdatedCpuCore();
|
||||
|
||||
// Applies the Auto setting if set. Returns an enum value from PSP_SYSTEMPARAM_LANGUAGE_*.
|
||||
int GetPSPLanguage();
|
||||
|
||||
PlayTimeTracker &TimeTracker() { return playTimeTracker_; }
|
||||
|
||||
protected:
|
||||
void LoadStandardControllerIni();
|
||||
void LoadLangValuesMapping();
|
||||
|
||||
void PostLoadCleanup(bool gameSpecific);
|
||||
void PreSaveCleanup(bool gameSpecific);
|
||||
void PostSaveCleanup(bool gameSpecific);
|
||||
|
||||
static std::map<const void*, const ConfigSetting *> &getPtrLUT();
|
||||
const DisplayLayoutConfig &GetDisplayLayoutConfig(DeviceOrientation orientation) const {
|
||||
return orientation == DeviceOrientation::Portrait ? displayLayoutPortrait : displayLayoutLandscape;
|
||||
}
|
||||
DisplayLayoutConfig &GetDisplayLayoutConfig(DeviceOrientation orientation) {
|
||||
return orientation == DeviceOrientation::Portrait ? displayLayoutPortrait : displayLayoutLandscape;
|
||||
}
|
||||
const TouchControlConfig &GetTouchControlsConfig(DeviceOrientation orientation) const {
|
||||
return orientation == DeviceOrientation::Portrait ? touchControlsPortrait : touchControlsLandscape;
|
||||
}
|
||||
TouchControlConfig &GetTouchControlsConfig(DeviceOrientation orientation) {
|
||||
return orientation == DeviceOrientation::Portrait ? touchControlsPortrait : touchControlsLandscape;
|
||||
}
|
||||
|
||||
private:
|
||||
void LoadStandardControllerIni();
|
||||
|
||||
void PostLoadCleanup();
|
||||
void PreSaveCleanup();
|
||||
void PostSaveCleanup();
|
||||
|
||||
friend struct ConfigSetting;
|
||||
|
||||
static std::map<const void *, std::pair<const ConfigBlock *, const ConfigSetting *>> &getPtrLUT();
|
||||
|
||||
// Applies defaults for missing settings.
|
||||
void ReadAllSettings(const IniFile &iniFile);
|
||||
|
||||
bool reload_ = false;
|
||||
|
||||
bool gameSpecific_ = false;
|
||||
std::string gameId_;
|
||||
std::string gameIdTitle_;
|
||||
std::map<std::string, std::pair<std::string, int>, std::less<>> langValuesMapping_;
|
||||
|
||||
PlayTimeTracker playTimeTracker_;
|
||||
|
||||
Path iniFilename_;
|
||||
Path controllerIniFilename_;
|
||||
Path searchPath_;
|
||||
Path appendedConfigFileName_;
|
||||
// A set make more sense, but won't have many entry, and I dont want to include the whole std::set header here
|
||||
std::vector<std::string> appendedConfigUpdatedGames_;
|
||||
|
||||
// TODO: Remove hack.
|
||||
friend struct ConfigSetting;
|
||||
};
|
||||
|
||||
std::string CreateRandMAC();
|
||||
|
||||
+118
-68
@@ -9,16 +9,21 @@
|
||||
#include "Core/Config.h"
|
||||
|
||||
bool ConfigSetting::perGame(void *ptr) {
|
||||
return g_Config.bGameSpecific && g_Config.getPtrLUT().count(ptr) > 0 && g_Config.getPtrLUT()[ptr]->PerGame();
|
||||
return g_Config.IsGameSpecific() && g_Config.getPtrLUT().count(ptr) > 0 && g_Config.getPtrLUT()[ptr].second->PerGame();
|
||||
}
|
||||
|
||||
bool ConfigSetting::ReadFromIniSection(char *owner, const Section *section) const {
|
||||
bool ConfigSetting::ReadFromIniSection(ConfigBlock *configBlock, const Section *section, bool applyDefaultIfMissing) const {
|
||||
char *owner = (char *)configBlock;
|
||||
_dbg_assert_(offset_ >= 0 && offset_ < configBlock->Size());
|
||||
|
||||
switch (type_) {
|
||||
case Type::TYPE_BOOL:
|
||||
{
|
||||
bool *target = (bool *)(owner + offset_);
|
||||
if (!section->Get(iniKey_, target)) {
|
||||
*target = cb_.b ? cb_.b() : default_.b;
|
||||
if (!section || !section->Get(iniKey_, target)) {
|
||||
if (applyDefaultIfMissing) {
|
||||
*target = defaultCallback_.b ? defaultCallback_.b() : default_.b;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@@ -26,15 +31,17 @@ bool ConfigSetting::ReadFromIniSection(char *owner, const Section *section) cons
|
||||
case Type::TYPE_INT:
|
||||
{
|
||||
int *target = (int *)(owner + offset_);
|
||||
if (translateFrom_) {
|
||||
if (translateFrom_ && section) {
|
||||
std::string value;
|
||||
if (section->Get(iniKey_, &value)) {
|
||||
*((int *)(owner + offset_)) = translateFrom_(value);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (!section->Get(iniKey_, target)) {
|
||||
*target = cb_.i ? cb_.i() : default_.i;
|
||||
if (!section || !section->Get(iniKey_, target)) {
|
||||
if (applyDefaultIfMissing) {
|
||||
*target = defaultCallback_.i ? defaultCallback_.i() : default_.i;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@@ -42,8 +49,10 @@ bool ConfigSetting::ReadFromIniSection(char *owner, const Section *section) cons
|
||||
case Type::TYPE_UINT32:
|
||||
{
|
||||
uint32_t *target = (uint32_t *)(owner + offset_);
|
||||
if (!section->Get(iniKey_, target)) {
|
||||
*target = cb_.u ? cb_.u() : default_.u;
|
||||
if (!section || !section->Get(iniKey_, target)) {
|
||||
if (applyDefaultIfMissing) {
|
||||
*target = defaultCallback_.u ? defaultCallback_.u() : default_.u;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@@ -51,8 +60,10 @@ bool ConfigSetting::ReadFromIniSection(char *owner, const Section *section) cons
|
||||
case Type::TYPE_UINT64:
|
||||
{
|
||||
uint64_t *target = (uint64_t *)(owner + offset_);
|
||||
if (!section->Get(iniKey_, target)) {
|
||||
*target = cb_.lu ? cb_.lu() : default_.lu;
|
||||
if (!section || !section->Get(iniKey_, target)) {
|
||||
if (applyDefaultIfMissing) {
|
||||
*target = defaultCallback_.lu ? defaultCallback_.lu() : default_.lu;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@@ -60,8 +71,10 @@ bool ConfigSetting::ReadFromIniSection(char *owner, const Section *section) cons
|
||||
case Type::TYPE_FLOAT:
|
||||
{
|
||||
float *target = (float *)(owner + offset_);
|
||||
if (!section->Get(iniKey_, target)) {
|
||||
*target = cb_.f ? cb_.f() : default_.f;
|
||||
if (!section || !section->Get(iniKey_, target)) {
|
||||
if (applyDefaultIfMissing) {
|
||||
*target = defaultCallback_.f ? defaultCallback_.f() : default_.f;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@@ -69,13 +82,15 @@ bool ConfigSetting::ReadFromIniSection(char *owner, const Section *section) cons
|
||||
case Type::TYPE_STRING:
|
||||
{
|
||||
std::string *target = (std::string *)(owner + offset_);
|
||||
if (!section->Get(iniKey_, target)) {
|
||||
if (cb_.s) {
|
||||
*target = cb_.s();
|
||||
} else if (default_.s) {
|
||||
*target = default_.s;
|
||||
} else {
|
||||
target->clear();
|
||||
if (!section || !section->Get(iniKey_, target)) {
|
||||
if (applyDefaultIfMissing) {
|
||||
if (defaultCallback_.s) {
|
||||
*target = defaultCallback_.s();
|
||||
} else if (default_.s) {
|
||||
*target = default_.s;
|
||||
} else {
|
||||
target->clear();
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -85,8 +100,8 @@ bool ConfigSetting::ReadFromIniSection(char *owner, const Section *section) cons
|
||||
{
|
||||
// No support for callbacks for these yet. that's not an issue.
|
||||
std::vector<std::string> *ptr = (std::vector<std::string> *)(owner + offset_);
|
||||
if (!section->Get(iniKey_, ptr)) {
|
||||
if (default_.v) {
|
||||
if (!section || !section->Get(iniKey_, ptr)) {
|
||||
if (applyDefaultIfMissing && default_.v) {
|
||||
CopyStrings(ptr, *default_.v);
|
||||
}
|
||||
return false;
|
||||
@@ -96,21 +111,21 @@ bool ConfigSetting::ReadFromIniSection(char *owner, const Section *section) cons
|
||||
}
|
||||
case Type::TYPE_TOUCH_POS:
|
||||
{
|
||||
ConfigTouchPos defaultTouchPos = cb_.touchPos ? cb_.touchPos() : default_.touchPos;
|
||||
ConfigTouchPos defaultTouchPos = defaultCallback_.touchPos ? defaultCallback_.touchPos() : default_.touchPos;
|
||||
|
||||
ConfigTouchPos *touchPos = ((ConfigTouchPos *)(owner + offset_));
|
||||
if (!section->Get(iniKey_, &touchPos->x)) {
|
||||
if (!section || (!section->Get(iniKey_, &touchPos->x) && applyDefaultIfMissing)) {
|
||||
touchPos->x = defaultTouchPos.x;
|
||||
}
|
||||
if (!section->Get(ini2_, &touchPos->y)) {
|
||||
if (!section || (!section->Get(ini2_, &touchPos->y) && applyDefaultIfMissing)) {
|
||||
touchPos->y = defaultTouchPos.y;
|
||||
}
|
||||
if (!section->Get(ini3_, &touchPos->scale)) {
|
||||
if (!section || (!section->Get(ini3_, &touchPos->scale) && applyDefaultIfMissing)) {
|
||||
touchPos->scale = defaultTouchPos.scale;
|
||||
}
|
||||
if (ini4_ && section->Get(ini4_, &touchPos->show)) {
|
||||
if (ini4_ && section && section->Get(ini4_, &touchPos->show)) {
|
||||
// do nothing, succeeded.
|
||||
} else {
|
||||
} else if (applyDefaultIfMissing) {
|
||||
touchPos->show = defaultTouchPos.show;
|
||||
}
|
||||
return true;
|
||||
@@ -119,11 +134,13 @@ bool ConfigSetting::ReadFromIniSection(char *owner, const Section *section) cons
|
||||
{
|
||||
Path *target = (Path *)(owner + offset_);
|
||||
std::string tmp;
|
||||
if (!section->Get(iniKey_, &tmp)) {
|
||||
if (cb_.p) {
|
||||
*target = cb_.p();
|
||||
} else {
|
||||
*target = Path(default_.p);
|
||||
if (!section || !section->Get(iniKey_, &tmp)) {
|
||||
if (applyDefaultIfMissing) {
|
||||
if (defaultCallback_.p) {
|
||||
*target = defaultCallback_.p();
|
||||
} else {
|
||||
*target = Path(default_.p);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -132,22 +149,22 @@ bool ConfigSetting::ReadFromIniSection(char *owner, const Section *section) cons
|
||||
}
|
||||
case Type::TYPE_CUSTOM_BUTTON:
|
||||
{
|
||||
ConfigCustomButton defaultCustomButton = cb_.customButton ? cb_.customButton() : default_.customButton;
|
||||
ConfigCustomButton defaultCustomButton = defaultCallback_.customButton ? defaultCallback_.customButton() : default_.customButton;
|
||||
|
||||
ConfigCustomButton *customButton = ((ConfigCustomButton *)(owner + offset_));
|
||||
if (!section->Get(iniKey_, &customButton->key)) {
|
||||
if (!section || (!section->Get(iniKey_, &customButton->key) && applyDefaultIfMissing)) {
|
||||
customButton->key = defaultCustomButton.key;
|
||||
}
|
||||
if (!section->Get(ini2_, &customButton->image)) {
|
||||
if (!section || (!section->Get(ini2_, &customButton->image) && applyDefaultIfMissing)) {
|
||||
customButton->image = defaultCustomButton.image;
|
||||
}
|
||||
if (!section->Get(ini3_, &customButton->shape)) {
|
||||
if (!section || (!section->Get(ini3_, &customButton->shape) && applyDefaultIfMissing)) {
|
||||
customButton->shape = defaultCustomButton.shape;
|
||||
}
|
||||
if (!section->Get(ini4_, &customButton->toggle)) {
|
||||
if (!section || (!section->Get(ini4_, &customButton->toggle) && applyDefaultIfMissing)) {
|
||||
customButton->toggle = defaultCustomButton.toggle;
|
||||
}
|
||||
if (!section->Get(ini5_, &customButton->repeat)) {
|
||||
if (!section || (!section->Get(ini5_, &customButton->repeat) && applyDefaultIfMissing)) {
|
||||
customButton->repeat = defaultCustomButton.repeat;
|
||||
}
|
||||
return true;
|
||||
@@ -158,31 +175,34 @@ bool ConfigSetting::ReadFromIniSection(char *owner, const Section *section) cons
|
||||
}
|
||||
}
|
||||
|
||||
void ConfigSetting::WriteToIniSection(const char *owner, Section *section) const {
|
||||
void ConfigSetting::WriteToIniSection(const ConfigBlock *configBlock, Section *section) const {
|
||||
if (!SaveSetting()) {
|
||||
return;
|
||||
}
|
||||
_dbg_assert_(section);
|
||||
_dbg_assert_(offset_ >= 0 && offset_ < configBlock->Size());
|
||||
|
||||
const char *owner = (const char *)configBlock;
|
||||
switch (type_) {
|
||||
case Type::TYPE_BOOL:
|
||||
return section->Set(iniKey_, *(bool *)(owner + offset_));
|
||||
return section->Set(iniKey_, *(const bool *)(owner + offset_));
|
||||
case Type::TYPE_INT:
|
||||
if (translateTo_) {
|
||||
int *ptr_i = (int *)(owner + offset_);
|
||||
std::string value = translateTo_(*ptr_i);
|
||||
return section->Set(iniKey_, value);
|
||||
}
|
||||
return section->Set(iniKey_, *(int *)(owner + offset_));
|
||||
return section->Set(iniKey_, *(const int *)(owner + offset_));
|
||||
case Type::TYPE_UINT32:
|
||||
return section->Set(iniKey_, *(uint32_t *)(owner + offset_));
|
||||
return section->Set(iniKey_, *(const uint32_t *)(owner + offset_));
|
||||
case Type::TYPE_UINT64:
|
||||
return section->Set(iniKey_, *(uint64_t *)(owner + offset_));
|
||||
return section->Set(iniKey_, *(const uint64_t *)(owner + offset_));
|
||||
case Type::TYPE_FLOAT:
|
||||
return section->Set(iniKey_, *(float *)(owner + offset_));
|
||||
return section->Set(iniKey_, *(const float *)(owner + offset_));
|
||||
case Type::TYPE_STRING:
|
||||
return section->Set(iniKey_, *(std::string *)(owner + offset_));
|
||||
return section->Set(iniKey_, *(const std::string *)(owner + offset_));
|
||||
case Type::TYPE_STRING_VECTOR:
|
||||
return section->Set(iniKey_, *(std::vector<std::string> *)(owner + offset_));
|
||||
return section->Set(iniKey_, *(const std::vector<std::string> *)(owner + offset_));
|
||||
case Type::TYPE_PATH:
|
||||
{
|
||||
Path *path = (Path *)(owner + offset_);
|
||||
@@ -215,13 +235,18 @@ void ConfigSetting::WriteToIniSection(const char *owner, Section *section) const
|
||||
}
|
||||
}
|
||||
|
||||
bool ConfigSetting::RestoreToDefault(const char *owner, bool log) const {
|
||||
bool ConfigSetting::RestoreToDefault(ConfigBlock *configBlock, bool log) const {
|
||||
// If the block supports resetting itself, don't allow per-setting resets. Shake them out with this assert.
|
||||
_dbg_assert_(!configBlock->CanResetToDefault());
|
||||
_dbg_assert_(offset_ >= 0 && offset_ < configBlock->Size());
|
||||
|
||||
const char *owner = (const char *)configBlock;
|
||||
switch (type_) {
|
||||
case Type::TYPE_BOOL:
|
||||
{
|
||||
bool *ptr_b = (bool *)(owner + offset_);
|
||||
const bool origValue = *ptr_b;
|
||||
*ptr_b = cb_.b ? cb_.b() : default_.b;
|
||||
*ptr_b = defaultCallback_.b ? defaultCallback_.b() : default_.b;
|
||||
if (*ptr_b != origValue) {
|
||||
if (log) {
|
||||
INFO_LOG(Log::System, "Restored %.*s from %s to default %s", STR_VIEW(iniKey_),
|
||||
@@ -236,7 +261,7 @@ bool ConfigSetting::RestoreToDefault(const char *owner, bool log) const {
|
||||
{
|
||||
int *ptr_i = (int *)(owner + offset_);
|
||||
const int origValue = *ptr_i;
|
||||
*ptr_i = cb_.i ? cb_.i() : default_.i;
|
||||
*ptr_i = defaultCallback_.i ? defaultCallback_.i() : default_.i;
|
||||
if (*ptr_i != origValue) {
|
||||
if (log) {
|
||||
INFO_LOG(Log::System, "Restored %.*s from %d to default %d", STR_VIEW(iniKey_),
|
||||
@@ -250,7 +275,7 @@ bool ConfigSetting::RestoreToDefault(const char *owner, bool log) const {
|
||||
{
|
||||
uint32_t *ptr_u = (uint32_t *)(owner + offset_);
|
||||
const uint32_t origValue = *ptr_u;
|
||||
*ptr_u = cb_.u ? cb_.u() : default_.u;
|
||||
*ptr_u = defaultCallback_.u ? defaultCallback_.u() : default_.u;
|
||||
if (*ptr_u != origValue) {
|
||||
if (log) {
|
||||
INFO_LOG(Log::System, "Restored %.*s from %u to default %u", STR_VIEW(iniKey_),
|
||||
@@ -264,7 +289,7 @@ bool ConfigSetting::RestoreToDefault(const char *owner, bool log) const {
|
||||
{
|
||||
uint64_t *ptr_lu = (uint64_t *)(owner + offset_);
|
||||
const uint64_t origValue = *ptr_lu;
|
||||
*ptr_lu = cb_.lu ? cb_.lu() : default_.lu;
|
||||
*ptr_lu = defaultCallback_.lu ? defaultCallback_.lu() : default_.lu;
|
||||
if (*ptr_lu != origValue) {
|
||||
if (log) {
|
||||
INFO_LOG(Log::System, "Restored %.*s from %llu to default %llu", STR_VIEW(iniKey_),
|
||||
@@ -278,7 +303,7 @@ bool ConfigSetting::RestoreToDefault(const char *owner, bool log) const {
|
||||
{
|
||||
float *ptr_f = (float *)(owner + offset_);
|
||||
const float origValue = *ptr_f;
|
||||
*ptr_f = cb_.f ? cb_.f() : default_.f;
|
||||
*ptr_f = defaultCallback_.f ? defaultCallback_.f() : default_.f;
|
||||
if (*ptr_f != origValue) {
|
||||
if (log) {
|
||||
INFO_LOG(Log::System, "Restored %.*s from %f to default %f", STR_VIEW(iniKey_),
|
||||
@@ -292,8 +317,8 @@ bool ConfigSetting::RestoreToDefault(const char *owner, bool log) const {
|
||||
{
|
||||
std::string *ptr_s = (std::string *)(owner + offset_);
|
||||
const std::string origValue = *ptr_s;
|
||||
if (cb_.s) {
|
||||
*ptr_s = cb_.s();
|
||||
if (defaultCallback_.s) {
|
||||
*ptr_s = defaultCallback_.s();
|
||||
} else if (default_.s != nullptr) {
|
||||
*ptr_s = default_.s;
|
||||
} else {
|
||||
@@ -317,14 +342,14 @@ bool ConfigSetting::RestoreToDefault(const char *owner, bool log) const {
|
||||
case Type::TYPE_TOUCH_POS:
|
||||
{
|
||||
ConfigTouchPos *ptr_touchPos = (ConfigTouchPos *)(owner + offset_);
|
||||
*ptr_touchPos = cb_.touchPos ? cb_.touchPos() : default_.touchPos;
|
||||
*ptr_touchPos = defaultCallback_.touchPos ? defaultCallback_.touchPos() : default_.touchPos;
|
||||
break;
|
||||
}
|
||||
case Type::TYPE_PATH:
|
||||
{
|
||||
Path *ptr_path = (Path *)(owner + offset_);
|
||||
if (cb_.p) {
|
||||
*ptr_path = cb_.p();
|
||||
if (defaultCallback_.p) {
|
||||
*ptr_path = defaultCallback_.p();
|
||||
break;
|
||||
} else if (default_.p) {
|
||||
*ptr_path = Path(default_.p);
|
||||
@@ -336,7 +361,7 @@ bool ConfigSetting::RestoreToDefault(const char *owner, bool log) const {
|
||||
case Type::TYPE_CUSTOM_BUTTON:
|
||||
{
|
||||
ConfigCustomButton *ptr_customButton = (ConfigCustomButton *)(owner + offset_);
|
||||
*ptr_customButton = cb_.customButton ? cb_.customButton() : default_.customButton;
|
||||
*ptr_customButton = defaultCallback_.customButton ? defaultCallback_.customButton() : default_.customButton;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
@@ -346,20 +371,45 @@ bool ConfigSetting::RestoreToDefault(const char *owner, bool log) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
void ConfigSetting::ReportSetting(const char *owner, UrlEncoder &data, const std::string &prefix) const {
|
||||
// Might be used to copy individual settings from defaulted blocks. Didn't end up using this for now.
|
||||
void ConfigSetting::CopyFromBlock(const ConfigBlock *other) {
|
||||
_dbg_assert_(offset_ >= 0 && offset_ < other->Size());
|
||||
|
||||
const char *otherOwner = (const char *)other;
|
||||
const char *thisOwner = (const char *)this;
|
||||
switch (type_) {
|
||||
case Type::TYPE_BOOL: *(bool *)(thisOwner + offset_) = *(const bool *)(otherOwner + offset_); break;
|
||||
case Type::TYPE_INT: *(int *)(thisOwner + offset_) = *(const int *)(otherOwner + offset_); break;
|
||||
case Type::TYPE_UINT32: *(uint32_t *)(thisOwner + offset_) = *(const uint32_t *)(otherOwner + offset_); break;
|
||||
case Type::TYPE_UINT64: *(uint64_t *)(thisOwner + offset_) = *(const uint64_t *)(otherOwner + offset_); break;
|
||||
case Type::TYPE_FLOAT: *(float *)(thisOwner + offset_) = *(const float *)(otherOwner + offset_); break;
|
||||
case Type::TYPE_STRING: *(std::string *)(thisOwner + offset_) = *(const std::string *)(otherOwner + offset_); break;
|
||||
case Type::TYPE_STRING_VECTOR: *(std::vector<std::string> *)(thisOwner + offset_) = *(const std::vector<std::string> *)(otherOwner + offset_); break;
|
||||
case Type::TYPE_PATH: *(Path *)(thisOwner + offset_) = *(const Path *)(otherOwner + offset_); break;
|
||||
case Type::TYPE_TOUCH_POS: *(ConfigTouchPos *)(thisOwner + offset_) = *(const ConfigTouchPos *)(otherOwner + offset_); break;
|
||||
case Type::TYPE_CUSTOM_BUTTON: *(ConfigCustomButton *)(thisOwner + offset_) = *(const ConfigCustomButton *)(otherOwner + offset_); break;
|
||||
default:
|
||||
_dbg_assert_msg_(false, "CopyFromBlock(%.*s): Unexpected setting type: %d", STR_VIEW(iniKey_), (int)type_);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void ConfigSetting::ReportSetting(const ConfigBlock *configBlock, UrlEncoder &data, const std::string &prefix) const {
|
||||
if (!Report())
|
||||
return;
|
||||
|
||||
const std::string key = join(prefix, std::string(iniKey_));
|
||||
_dbg_assert_(offset_ >= 0 && offset_ < configBlock->Size());
|
||||
const char *owner = (const char *)configBlock;
|
||||
const std::string key = join(prefix, iniKey_);
|
||||
|
||||
switch (type_) {
|
||||
case Type::TYPE_BOOL: return data.Add(key, (const bool *)(owner + offset_));
|
||||
case Type::TYPE_INT: return data.Add(key, (const int *)(owner + offset_));
|
||||
case Type::TYPE_UINT32: return data.Add(key, (const u32 *)(owner + offset_));
|
||||
case Type::TYPE_UINT64: return data.Add(key, (const u64 *)(owner + offset_));
|
||||
case Type::TYPE_FLOAT: return data.Add(key, (const float *)(owner + offset_));
|
||||
case Type::TYPE_STRING: return data.Add(key, (const std::string *)(owner + offset_));
|
||||
case Type::TYPE_STRING_VECTOR: return data.Add(key, (const std::vector<std::string> *)(owner + offset_));
|
||||
case Type::TYPE_BOOL: return data.Add(key, *(const bool *)(owner + offset_));
|
||||
case Type::TYPE_INT: return data.Add(key, *(const int *)(owner + offset_));
|
||||
case Type::TYPE_UINT32: return data.Add(key, *(const uint32_t *)(owner + offset_));
|
||||
case Type::TYPE_UINT64: return data.Add(key, *(const uint64_t *)(owner + offset_));
|
||||
case Type::TYPE_FLOAT: return data.Add(key, *(const float *)(owner + offset_));
|
||||
case Type::TYPE_STRING: return data.Add(key, *(const std::string *)(owner + offset_));
|
||||
case Type::TYPE_STRING_VECTOR: return data.Add(key, *(const std::vector<std::string> *)(owner + offset_));
|
||||
case Type::TYPE_PATH: return data.Add(key, ((const Path *)(owner + offset_))->ToString());
|
||||
case Type::TYPE_TOUCH_POS: return; // Doesn't report.
|
||||
case Type::TYPE_CUSTOM_BUTTON: return; // Doesn't report.
|
||||
|
||||
+86
-79
@@ -2,6 +2,7 @@
|
||||
|
||||
#include <cstdint>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "Core/ConfigValues.h"
|
||||
|
||||
class Path;
|
||||
@@ -16,6 +17,8 @@ enum class CfgFlag : u8 {
|
||||
};
|
||||
ENUM_CLASS_BITOPS(CfgFlag);
|
||||
|
||||
struct ConfigBlock;
|
||||
|
||||
struct ConfigSetting {
|
||||
enum class Type {
|
||||
TYPE_TERMINATOR,
|
||||
@@ -42,18 +45,6 @@ struct ConfigSetting {
|
||||
ConfigTouchPos touchPos;
|
||||
ConfigCustomButton customButton;
|
||||
};
|
||||
union SettingPtr {
|
||||
bool *b;
|
||||
int *i;
|
||||
uint32_t *u;
|
||||
uint64_t *lu;
|
||||
float *f;
|
||||
std::string *s;
|
||||
std::vector<std::string> *v;
|
||||
Path *p;
|
||||
ConfigTouchPos *touchPos;
|
||||
ConfigCustomButton *customButton;
|
||||
};
|
||||
|
||||
typedef bool (*BoolDefaultCallback)();
|
||||
typedef int (*IntDefaultCallback)();
|
||||
@@ -77,116 +68,138 @@ struct ConfigSetting {
|
||||
CustomButtonDefaultCallback customButton;
|
||||
};
|
||||
|
||||
ConfigSetting(std::string_view ini, const char *owner, bool *v, bool def, CfgFlag flags) noexcept
|
||||
: iniKey_(ini), type_(Type::TYPE_BOOL), flags_(flags), offset_((const char *)v - owner) {
|
||||
cb_.b = nullptr;
|
||||
// The configBlock you pass in here is just to be able to calculate the offset of the setting within the block so it can be
|
||||
// applied to other blocks.
|
||||
ConfigSetting(std::string_view ini, ConfigBlock *configBlock, bool *v, bool def, CfgFlag flags) noexcept
|
||||
: iniKey_(ini), type_(Type::TYPE_BOOL), flags_(flags), offset_((const char *)v - (const char *)configBlock) {
|
||||
defaultCallback_.b = nullptr;
|
||||
default_.b = def;
|
||||
}
|
||||
|
||||
ConfigSetting(std::string_view ini, const char *owner, int *v, int def, CfgFlag flags) noexcept
|
||||
: iniKey_(ini), type_(Type::TYPE_INT), flags_(flags), offset_((const char *)v - owner) {
|
||||
cb_.i = nullptr;
|
||||
ConfigSetting(std::string_view ini, ConfigBlock *configBlock, bool *v, CfgFlag flags) noexcept
|
||||
: iniKey_(ini), type_(Type::TYPE_BOOL), flags_(flags), offset_((const char *)v - (const char *)configBlock) {
|
||||
defaultCallback_.b = nullptr;
|
||||
default_.b = false; // unused with this constructor
|
||||
}
|
||||
|
||||
ConfigSetting(std::string_view ini, ConfigBlock *configBlock, int *v, int def, CfgFlag flags) noexcept
|
||||
: iniKey_(ini), type_(Type::TYPE_INT), flags_(flags), offset_((const char *)v - (const char *)configBlock) {
|
||||
defaultCallback_.i = nullptr;
|
||||
default_.i = def;
|
||||
}
|
||||
|
||||
ConfigSetting(std::string_view ini, const char *owner, int *v, int def, std::string (*transTo)(int), int (*transFrom)(const std::string &), CfgFlag flags) noexcept
|
||||
: iniKey_(ini), type_(Type::TYPE_INT), flags_(flags), translateTo_(transTo), translateFrom_(transFrom), offset_((const char *)v - owner) {
|
||||
cb_.i = nullptr;
|
||||
ConfigSetting(std::string_view ini, ConfigBlock *configBlock, int *v, CfgFlag flags) noexcept
|
||||
: iniKey_(ini), type_(Type::TYPE_INT), flags_(flags), offset_((const char *)v - (const char *)configBlock) {
|
||||
defaultCallback_.i = nullptr;
|
||||
default_.i = 0; // unused with this constructor
|
||||
}
|
||||
|
||||
ConfigSetting(std::string_view ini, ConfigBlock *configBlock, int *v, int def, std::string (*transTo)(int), int (*transFrom)(const std::string &), CfgFlag flags) noexcept
|
||||
: iniKey_(ini), type_(Type::TYPE_INT), flags_(flags), translateTo_(transTo), translateFrom_(transFrom), offset_((const char *)v - (const char *)configBlock) {
|
||||
defaultCallback_.i = nullptr;
|
||||
default_.i = def;
|
||||
}
|
||||
|
||||
ConfigSetting(std::string_view ini, const char *owner, uint32_t *v, uint32_t def, CfgFlag flags) noexcept
|
||||
: iniKey_(ini), type_(Type::TYPE_UINT32), flags_(flags), offset_((const char *)v - owner) {
|
||||
cb_.u = nullptr;
|
||||
ConfigSetting(std::string_view ini, ConfigBlock *configBlock, uint32_t *v, uint32_t def, CfgFlag flags) noexcept
|
||||
: iniKey_(ini), type_(Type::TYPE_UINT32), flags_(flags), offset_((const char *)v - (const char *)configBlock) {
|
||||
defaultCallback_.u = nullptr;
|
||||
default_.u = def;
|
||||
}
|
||||
|
||||
ConfigSetting(std::string_view ini, const char *owner, uint64_t *v, uint64_t def, CfgFlag flags) noexcept
|
||||
: iniKey_(ini), type_(Type::TYPE_UINT64), flags_(flags), offset_((const char *)v - owner) {
|
||||
cb_.lu = nullptr;
|
||||
ConfigSetting(std::string_view ini, ConfigBlock *configBlock, uint64_t *v, uint64_t def, CfgFlag flags) noexcept
|
||||
: iniKey_(ini), type_(Type::TYPE_UINT64), flags_(flags), offset_((const char *)v - (const char *)configBlock) {
|
||||
defaultCallback_.lu = nullptr;
|
||||
default_.lu = def;
|
||||
}
|
||||
|
||||
ConfigSetting(std::string_view ini, const char *owner, float *v, float def, CfgFlag flags) noexcept
|
||||
: iniKey_(ini), type_(Type::TYPE_FLOAT), flags_(flags), offset_((const char *)v - owner) {
|
||||
cb_.f = nullptr;
|
||||
ConfigSetting(std::string_view ini, ConfigBlock *configBlock, float *v, float def, CfgFlag flags) noexcept
|
||||
: iniKey_(ini), type_(Type::TYPE_FLOAT), flags_(flags), offset_((const char *)v - (const char *)configBlock) {
|
||||
defaultCallback_.f = nullptr;
|
||||
default_.f = def;
|
||||
}
|
||||
|
||||
ConfigSetting(std::string_view ini, const char *owner, std::string *v, const char *def, CfgFlag flags) noexcept
|
||||
: iniKey_(ini), type_(Type::TYPE_STRING), flags_(flags), offset_((const char *)v - owner) {
|
||||
cb_.s = nullptr;
|
||||
ConfigSetting(std::string_view ini, ConfigBlock *configBlock, float *v, CfgFlag flags) noexcept
|
||||
: iniKey_(ini), type_(Type::TYPE_FLOAT), flags_(flags), offset_((const char *)v - (const char *)configBlock) {
|
||||
defaultCallback_.f = nullptr;
|
||||
default_.f = 0.0f;
|
||||
}
|
||||
|
||||
ConfigSetting(std::string_view ini, ConfigBlock *configBlock, std::string *v, const char *def, CfgFlag flags) noexcept
|
||||
: iniKey_(ini), type_(Type::TYPE_STRING), flags_(flags), offset_((const char *)v - (const char *)configBlock) {
|
||||
defaultCallback_.s = nullptr;
|
||||
default_.s = def;
|
||||
}
|
||||
|
||||
ConfigSetting(std::string_view ini, const char *owner, std::vector<std::string> *v, const std::vector<std::string_view> *def, CfgFlag flags) noexcept
|
||||
: iniKey_(ini), type_(Type::TYPE_STRING_VECTOR), flags_(flags), offset_((const char *)v - owner) {
|
||||
ConfigSetting(std::string_view ini, ConfigBlock *configBlock, std::vector<std::string> *v, const std::vector<std::string_view> *def, CfgFlag flags) noexcept
|
||||
: iniKey_(ini), type_(Type::TYPE_STRING_VECTOR), flags_(flags), offset_((const char *)v - (const char *)configBlock) {
|
||||
default_.v = def;
|
||||
}
|
||||
|
||||
ConfigSetting(std::string_view ini, const char *owner, Path *v, const char *def, CfgFlag flags) noexcept
|
||||
: iniKey_(ini), type_(Type::TYPE_PATH), flags_(flags), offset_((const char *)v - owner) {
|
||||
cb_.p = nullptr;
|
||||
ConfigSetting(std::string_view ini, ConfigBlock *configBlock, Path *v, const char *def, CfgFlag flags) noexcept
|
||||
: iniKey_(ini), type_(Type::TYPE_PATH), flags_(flags), offset_((const char *)v - (const char *)configBlock) {
|
||||
defaultCallback_.p = nullptr;
|
||||
default_.p = def;
|
||||
}
|
||||
|
||||
ConfigSetting(const char *iniX, const char *iniY, const char *iniScale, const char *iniShow, const char *owner, ConfigTouchPos *v, ConfigTouchPos def, CfgFlag flags) noexcept
|
||||
: iniKey_(iniX), ini2_(iniY), ini3_(iniScale), ini4_(iniShow), type_(Type::TYPE_TOUCH_POS), flags_(flags), offset_((const char *)v - owner) {
|
||||
cb_.touchPos = nullptr;
|
||||
ConfigSetting(const char *iniX, const char *iniY, const char *iniScale, const char *iniShow, ConfigBlock *configBlock, ConfigTouchPos *v, ConfigTouchPos def, CfgFlag flags) noexcept
|
||||
: iniKey_(iniX), ini2_(iniY), ini3_(iniScale), ini4_(iniShow), type_(Type::TYPE_TOUCH_POS), flags_(flags), offset_((const char *)v - (const char *)configBlock) {
|
||||
defaultCallback_.touchPos = nullptr;
|
||||
default_.touchPos = def;
|
||||
}
|
||||
|
||||
ConfigSetting(const char *iniKey, const char *iniImage, const char *iniShape, const char *iniToggle, const char *iniRepeat, const char *owner, ConfigCustomButton *v, ConfigCustomButton def, CfgFlag flags) noexcept
|
||||
: iniKey_(iniKey), ini2_(iniImage), ini3_(iniShape), ini4_(iniToggle), ini5_(iniRepeat), type_(Type::TYPE_CUSTOM_BUTTON), flags_(flags), offset_((const char *)v - owner) {
|
||||
cb_.customButton = nullptr;
|
||||
ConfigSetting(const char *iniKey, const char *iniImage, const char *iniShape, const char *iniToggle, const char *iniRepeat, ConfigBlock *configBlock, ConfigCustomButton *v, ConfigCustomButton def, CfgFlag flags) noexcept
|
||||
: iniKey_(iniKey), ini2_(iniImage), ini3_(iniShape), ini4_(iniToggle), ini5_(iniRepeat), type_(Type::TYPE_CUSTOM_BUTTON), flags_(flags), offset_((const char *)v - (const char *)configBlock) {
|
||||
defaultCallback_.customButton = nullptr;
|
||||
default_.customButton = def;
|
||||
}
|
||||
|
||||
ConfigSetting(std::string_view ini, const char *owner, bool *v, BoolDefaultCallback def, CfgFlag flags) noexcept
|
||||
: iniKey_(ini), type_(Type::TYPE_BOOL), flags_(flags), offset_((const char *)v - owner) {
|
||||
cb_.b = def;
|
||||
ConfigSetting(std::string_view ini, ConfigBlock *configBlock, bool *v, BoolDefaultCallback def, CfgFlag flags) noexcept
|
||||
: iniKey_(ini), type_(Type::TYPE_BOOL), flags_(flags), offset_((const char *)v - (const char *)configBlock) {
|
||||
defaultCallback_.b = def;
|
||||
}
|
||||
|
||||
ConfigSetting(std::string_view ini, const char *owner, int *v, IntDefaultCallback def, CfgFlag flags) noexcept
|
||||
: iniKey_(ini), type_(Type::TYPE_INT), flags_(flags), offset_((const char *)v - owner) {
|
||||
cb_.i = def;
|
||||
ConfigSetting(std::string_view ini, ConfigBlock *configBlock, int *v, IntDefaultCallback def, CfgFlag flags) noexcept
|
||||
: iniKey_(ini), type_(Type::TYPE_INT), flags_(flags), offset_((const char *)v - (const char *)configBlock) {
|
||||
defaultCallback_.i = def;
|
||||
}
|
||||
|
||||
ConfigSetting(std::string_view ini, const char *owner, int *v, IntDefaultCallback def, std::string(*transTo)(int), int(*transFrom)(const std::string &), CfgFlag flags) noexcept
|
||||
: iniKey_(ini), type_(Type::TYPE_INT), flags_(flags), offset_((const char *)v - owner), translateTo_(transTo), translateFrom_(transFrom) {
|
||||
cb_.i = def;
|
||||
ConfigSetting(std::string_view ini, ConfigBlock *configBlock, int *v, IntDefaultCallback def, std::string(*transTo)(int), int(*transFrom)(const std::string &), CfgFlag flags) noexcept
|
||||
: iniKey_(ini), type_(Type::TYPE_INT), flags_(flags), offset_((const char *)v - (const char *)configBlock), translateTo_(transTo), translateFrom_(transFrom) {
|
||||
defaultCallback_.i = def;
|
||||
}
|
||||
|
||||
ConfigSetting(std::string_view ini, const char *owner, uint32_t *v, Uint32DefaultCallback def, CfgFlag flags) noexcept
|
||||
: iniKey_(ini), type_(Type::TYPE_UINT32), flags_(flags), offset_((const char *)v - owner) {
|
||||
cb_.u = def;
|
||||
ConfigSetting(std::string_view ini, ConfigBlock *configBlock, uint32_t *v, Uint32DefaultCallback def, CfgFlag flags) noexcept
|
||||
: iniKey_(ini), type_(Type::TYPE_UINT32), flags_(flags), offset_((const char *)v - (const char *)configBlock) {
|
||||
defaultCallback_.u = def;
|
||||
}
|
||||
|
||||
ConfigSetting(std::string_view ini, const char *owner, float *v, FloatDefaultCallback def, CfgFlag flags) noexcept
|
||||
: iniKey_(ini), type_(Type::TYPE_FLOAT), flags_(flags), offset_((const char *)v - owner) {
|
||||
cb_.f = def;
|
||||
ConfigSetting(std::string_view ini, ConfigBlock *configBlock, float *v, FloatDefaultCallback def, CfgFlag flags) noexcept
|
||||
: iniKey_(ini), type_(Type::TYPE_FLOAT), flags_(flags), offset_((const char *)v - (const char *)configBlock) {
|
||||
defaultCallback_.f = def;
|
||||
}
|
||||
|
||||
ConfigSetting(std::string_view ini, const char *owner, std::string *v, StringDefaultCallback def, CfgFlag flags) noexcept
|
||||
: iniKey_(ini), type_(Type::TYPE_STRING), flags_(flags), offset_((const char *)v - owner) {
|
||||
cb_.s = def;
|
||||
ConfigSetting(std::string_view ini, ConfigBlock *configBlock, std::string *v, StringDefaultCallback def, CfgFlag flags) noexcept
|
||||
: iniKey_(ini), type_(Type::TYPE_STRING), flags_(flags), offset_((const char *)v - (const char *)configBlock) {
|
||||
defaultCallback_.s = def;
|
||||
}
|
||||
|
||||
ConfigSetting(std::string_view iniX, const char *iniY, const char *iniScale, const char *iniShow, const char *owner, ConfigTouchPos *v, TouchPosDefaultCallback def, CfgFlag flags) noexcept
|
||||
: iniKey_(iniX), ini2_(iniY), ini3_(iniScale), ini4_(iniShow), type_(Type::TYPE_TOUCH_POS), flags_(flags), offset_((const char *)v - owner) {
|
||||
cb_.touchPos = def;
|
||||
ConfigSetting(std::string_view iniX, const char *iniY, const char *iniScale, const char *iniShow, ConfigBlock *configBlock, ConfigTouchPos *v, TouchPosDefaultCallback def, CfgFlag flags) noexcept
|
||||
: iniKey_(iniX), ini2_(iniY), ini3_(iniScale), ini4_(iniShow), type_(Type::TYPE_TOUCH_POS), flags_(flags), offset_((const char *)v - (const char *)configBlock) {
|
||||
defaultCallback_.touchPos = def;
|
||||
}
|
||||
|
||||
bool ReadFromIniSection(char *owner, const Section *section) const;
|
||||
bool ReadFromIniSection(ConfigBlock *configBlock, const Section *section, bool applyDefaultIfMissing) const;
|
||||
|
||||
void CopyFromBlock(const ConfigBlock *other);
|
||||
|
||||
// Yes, this can be const because what's modified is not the ConfigSetting struct, but the value which is stored elsewhere.
|
||||
// Should actually be called WriteToIni or something.
|
||||
void WriteToIniSection(const char *owner, Section *section) const;
|
||||
void WriteToIniSection(const ConfigBlock *configBlock, Section *section) const;
|
||||
|
||||
// If log is true, logs if the setting changed.
|
||||
bool RestoreToDefault(const char *owner, bool log) const;
|
||||
bool RestoreToDefault(ConfigBlock *configBlock, bool log) const;
|
||||
|
||||
void ReportSetting(const char *owner, UrlEncoder &data, const std::string &prefix) const;
|
||||
void ReportSetting(const ConfigBlock *configBlock, UrlEncoder &data, const std::string &prefix) const;
|
||||
|
||||
// Easy flag accessors.
|
||||
bool PerGame() const { return flags_ & CfgFlag::PER_GAME; }
|
||||
@@ -204,25 +217,19 @@ struct ConfigSetting {
|
||||
// Returns false if per-game settings are not currently used
|
||||
static bool perGame(void *ptr);
|
||||
|
||||
const void *GetVoidPtr(const char *owner) const {
|
||||
const void *GetVoidPtr(ConfigBlock *configBlock) const {
|
||||
char *configBlockBase = (char *)configBlock;
|
||||
// undefined behavior but in reality will work.
|
||||
return (const void *)(owner + offset_);
|
||||
return (const void *)(configBlockBase + offset_);
|
||||
}
|
||||
|
||||
private:
|
||||
CfgFlag flags_;
|
||||
DefaultValue default_{};
|
||||
DefaultCallback cb_{};
|
||||
DefaultCallback defaultCallback_{};
|
||||
u32 offset_;
|
||||
|
||||
// We only support transform for ints.
|
||||
std::string (*translateTo_)(int) = nullptr;
|
||||
int (*translateFrom_)(const std::string &) = nullptr;
|
||||
};
|
||||
|
||||
struct ConfigSectionSettings {
|
||||
char *owner;
|
||||
const char *section;
|
||||
const ConfigSetting *settings;
|
||||
size_t settingsCount;
|
||||
};
|
||||
|
||||
+14
-4
@@ -20,12 +20,21 @@
|
||||
#include <cstdint>
|
||||
#include <cmath>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#ifndef _MSC_VER
|
||||
#include <strings.h>
|
||||
#endif
|
||||
#include "Common/Common.h"
|
||||
#include "Common/CommonFuncs.h"
|
||||
|
||||
struct ConfigBlock {
|
||||
virtual ~ConfigBlock() = default;
|
||||
virtual bool CanResetToDefault() const { return false; }
|
||||
// If a block returns false here (like Config itself does), resetting to default will happen by the old per-setting mechanism.
|
||||
virtual bool ResetToDefault(std::string_view blockName) { return false; }
|
||||
virtual size_t Size() const { return sizeof(ConfigBlock); } // For sanity checks
|
||||
};
|
||||
|
||||
constexpr int PSP_MODEL_FAT = 0;
|
||||
constexpr int PSP_MODEL_SLIM = 1;
|
||||
constexpr int PSP_DEFAULT_FIRMWARE = 660;
|
||||
@@ -48,11 +57,11 @@ int MultiplierToVolume100(float multiplier);
|
||||
float UIScaleFactorToMultiplier(int factor);
|
||||
|
||||
struct ConfigTouchPos {
|
||||
float x;
|
||||
float y;
|
||||
float scale;
|
||||
float x = -1.0f;
|
||||
float y = -1.0f;
|
||||
float scale = 1.0f;
|
||||
// Note: Show is not used for all settings.
|
||||
bool show;
|
||||
bool show = true;
|
||||
};
|
||||
|
||||
struct ConfigCustomButton {
|
||||
@@ -122,6 +131,7 @@ enum class AudioSyncMode {
|
||||
CLASSIC_PITCH = 1,
|
||||
};
|
||||
|
||||
// TODO: We can make this more fine-grained.
|
||||
enum class RestoreSettingsBits : int {
|
||||
SETTINGS = 1,
|
||||
CONTROLS = 2,
|
||||
|
||||
@@ -150,7 +150,7 @@ void ControlMapper::SetCallbacks(
|
||||
std::function<void(VirtKey, bool)> onVKey,
|
||||
std::function<void(VirtKey, float)> onVKeyAnalog,
|
||||
std::function<void(uint32_t, uint32_t)> updatePSPButtons,
|
||||
std::function<void(int, float, float)> setPSPAnalog,
|
||||
std::function<void(int, int, float, float)> setPSPAnalog,
|
||||
std::function<void(int, float, float)> setRawAnalog) {
|
||||
onVKey_ = onVKey;
|
||||
onVKeyAnalog_ = onVKeyAnalog;
|
||||
@@ -208,7 +208,7 @@ void ControlMapper::UpdateAnalogOutput(int stick) {
|
||||
}
|
||||
converted_[stick][0] = x;
|
||||
converted_[stick][1] = y;
|
||||
setPSPAnalog_(stick, x, y);
|
||||
setPSPAnalog_(iInternalScreenRotationCached_, stick, x, y);
|
||||
}
|
||||
|
||||
void ControlMapper::ForceReleaseVKey(int vkey) {
|
||||
@@ -357,7 +357,7 @@ bool ControlMapper::UpdatePSPState(const InputMapping &changedMapping, double no
|
||||
// of crazy input combos if needed.
|
||||
|
||||
int rotations = 0;
|
||||
switch (g_Config.iInternalScreenRotation) {
|
||||
switch (iInternalScreenRotationCached_) {
|
||||
case ROTATION_LOCKED_HORIZONTAL180: rotations = 2; break;
|
||||
case ROTATION_LOCKED_VERTICAL: rotations = 1; break;
|
||||
case ROTATION_LOCKED_VERTICAL180: rotations = 3; break;
|
||||
@@ -650,18 +650,19 @@ void ControlMapper::Axis(const AxisInput *axes, size_t count) {
|
||||
KeyMap::UnlockMappings();
|
||||
}
|
||||
|
||||
void ControlMapper::Update(double now) {
|
||||
void ControlMapper::Update(const DisplayLayoutConfig &config, double now) {
|
||||
iInternalScreenRotationCached_ = config.iInternalScreenRotation;
|
||||
if (autoRotatingAnalogCW_) {
|
||||
// Clamp to a square
|
||||
float x = std::min(1.0f, std::max(-1.0f, 1.42f * (float)cos(now * -g_Config.fAnalogAutoRotSpeed)));
|
||||
float y = std::min(1.0f, std::max(-1.0f, 1.42f * (float)sin(now * -g_Config.fAnalogAutoRotSpeed)));
|
||||
|
||||
setPSPAnalog_(0, x, y);
|
||||
setPSPAnalog_(iInternalScreenRotationCached_, 0, x, y);
|
||||
} else if (autoRotatingAnalogCCW_) {
|
||||
float x = std::min(1.0f, std::max(-1.0f, 1.42f * (float)cos(now * g_Config.fAnalogAutoRotSpeed)));
|
||||
float y = std::min(1.0f, std::max(-1.0f, 1.42f * (float)sin(now * g_Config.fAnalogAutoRotSpeed)));
|
||||
|
||||
setPSPAnalog_(0, x, y);
|
||||
setPSPAnalog_(iInternalScreenRotationCached_, 0, x, y);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -727,7 +728,7 @@ void ControlMapper::onVKey(VirtKey vkey, bool down) {
|
||||
autoRotatingAnalogCCW_ = false;
|
||||
} else {
|
||||
autoRotatingAnalogCW_ = false;
|
||||
setPSPAnalog_(0, 0.0f, 0.0f);
|
||||
setPSPAnalog_(iInternalScreenRotationCached_, 0, 0.0f, 0.0f);
|
||||
}
|
||||
break;
|
||||
case VIRTKEY_ANALOG_ROTATE_CCW:
|
||||
@@ -736,7 +737,7 @@ void ControlMapper::onVKey(VirtKey vkey, bool down) {
|
||||
autoRotatingAnalogCCW_ = true;
|
||||
} else {
|
||||
autoRotatingAnalogCCW_ = false;
|
||||
setPSPAnalog_(0, 0.0f, 0.0f);
|
||||
setPSPAnalog_(iInternalScreenRotationCached_, 0, 0.0f, 0.0f);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
|
||||
@@ -7,11 +7,12 @@
|
||||
#include <cstring>
|
||||
#include <mutex>
|
||||
|
||||
struct DisplayLayoutConfig;
|
||||
// Utilities for mapping input events to PSP inputs and virtual keys.
|
||||
// Main use is of course from EmuScreen.cpp, but also useful from control settings etc.
|
||||
class ControlMapper {
|
||||
public:
|
||||
void Update(double now);
|
||||
void Update(const DisplayLayoutConfig &config, double now);
|
||||
|
||||
// Inputs to the table-based mapping
|
||||
// These functions are free-threaded.
|
||||
@@ -24,7 +25,7 @@ public:
|
||||
std::function<void(VirtKey, bool)> onVKey,
|
||||
std::function<void(VirtKey, float)> onVKeyAnalog,
|
||||
std::function<void(uint32_t, uint32_t)> updatePSPButtons,
|
||||
std::function<void(int, float, float)> setPSPAnalog,
|
||||
std::function<void(int, int, float, float)> setPSPAnalog,
|
||||
std::function<void(int, float, float)> setRawAnalog);
|
||||
|
||||
// Inject raw PSP key input directly, such as from touch screen controls.
|
||||
@@ -85,6 +86,8 @@ private:
|
||||
|
||||
bool swapAxes_ = false;
|
||||
|
||||
int iInternalScreenRotationCached_ = 0;
|
||||
|
||||
// Protects basically all the state.
|
||||
// TODO: Maybe we should piggyback on the screenmanager mutex - it's always locked
|
||||
// when events come in here.
|
||||
@@ -96,7 +99,7 @@ private:
|
||||
std::function<void(VirtKey, bool)> onVKey_;
|
||||
std::function<void(VirtKey, float)> onVKeyAnalog_;
|
||||
std::function<void(uint32_t, uint32_t)> updatePSPButtons_;
|
||||
std::function<void(int, float, float)> setPSPAnalog_;
|
||||
std::function<void(int, int, float, float)> setPSPAnalog_;
|
||||
std::function<void(int, float, float)> setRawAnalog_;
|
||||
};
|
||||
|
||||
|
||||
@@ -478,8 +478,10 @@ void WebSocketDisasmState::Assemble(DebuggerRequest &req) {
|
||||
if (!req.ParamString("code", &code))
|
||||
return;
|
||||
|
||||
if (!MIPSAsm::MipsAssembleOpcode(code.c_str(), currentDebugMIPS, address))
|
||||
return req.Fail(StringFromFormat("Could not assemble: %s", MIPSAsm::GetAssembleError().c_str()));
|
||||
std::string error;
|
||||
if (!MipsAssembleOpcode(code, currentDebugMIPS, address, &error)) {
|
||||
return req.Fail(StringFromFormat("Could not assemble: %s", error.c_str()));
|
||||
}
|
||||
|
||||
JsonWriter &json = req.Respond();
|
||||
Reporting::NotifyDebugger();
|
||||
|
||||
@@ -66,7 +66,7 @@ const char *UtilityDialogStatusToString(PSPDialog::DialogStatus status) {
|
||||
void PSPDialog::InitCommon() {
|
||||
UpdateCommon();
|
||||
|
||||
if (GetCommonParam() && GetCommonParam()->language != g_Config.GetPSPLanguage()) {
|
||||
if (GetCommonParam() && GetCommonParam()->language != GetPSPLanguage()) {
|
||||
WARN_LOG(Log::sceUtility, "Game requested language %d, ignoring and using user language", GetCommonParam()->language);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,8 +42,6 @@
|
||||
const static int OSK_INIT_DELAY_US = 300000;
|
||||
const static int OSK_SHUTDOWN_DELAY_US = 40000;
|
||||
|
||||
static std::map<std::string, std::pair<std::string, int>, std::less<>> languageMapping;
|
||||
|
||||
const uint8_t numKeyCols[OSK_KEYBOARD_COUNT] = {12, 12, 13, 13, 12, 12, 12, 12, 12};
|
||||
const uint8_t numKeyRows[OSK_KEYBOARD_COUNT] = {4, 4, 6, 6, 5, 4, 4, 4, 4};
|
||||
|
||||
@@ -265,8 +263,6 @@ int PSPOskDialog::Init(u32 oskPtr) {
|
||||
inputChars += c;
|
||||
}
|
||||
|
||||
languageMapping = g_Config.GetLangValuesMapping();
|
||||
|
||||
// Eat any keys pressed before the dialog inited.
|
||||
UpdateButtons();
|
||||
InitCommon();
|
||||
@@ -938,9 +934,12 @@ int PSPOskDialog::Update(int animSpeed) {
|
||||
return (const char *)nullptr;
|
||||
}
|
||||
|
||||
// Now, let's grab the name.
|
||||
std::string countryCode(OskKeyboardNames[lang]);
|
||||
const char *language = languageMapping[countryCode].first.c_str();
|
||||
// Now, let's grab the friendly name from the langvaluesmapping.
|
||||
const std::string countryCode(OskKeyboardNames[lang]);
|
||||
|
||||
const auto &languageMapping = GetLangValuesMapping();
|
||||
const auto iter = languageMapping.find(countryCode);
|
||||
const char *language = iter == languageMapping.end() ? "" : iter->second.first.c_str();
|
||||
|
||||
// It seems like this is a "fake" country code for extra keyboard purposes.
|
||||
if (countryCode == "English Full-width")
|
||||
|
||||
@@ -75,8 +75,8 @@ void FrameTiming::PostSubmit() {
|
||||
}
|
||||
|
||||
void FrameTiming::ComputePresentMode(Draw::DrawContext *draw, bool fastForward) {
|
||||
_dbg_assert_(draw);
|
||||
if (!draw) {
|
||||
// This happens in headless mode.
|
||||
fastForwardSkipFlip_ = true;
|
||||
presentMode_ = Draw::PresentMode::FIFO;
|
||||
return;
|
||||
|
||||
+4
-4
@@ -235,6 +235,10 @@ template <bool leave, bool convert_code, typename T>
|
||||
[[nodiscard]]
|
||||
NO_INLINE
|
||||
T hleDoLog(Log t, LogLevel level, T res, const char *file, int line, const char *reportTag) {
|
||||
if (convert_code && (int)res >= 0) {
|
||||
level = LogLevel::LDEBUG;
|
||||
}
|
||||
|
||||
if (((int)level > MAX_LOGLEVEL || !GenericLogEnabled(t, level)) && !reportTag) {
|
||||
if (leave) {
|
||||
hleLeave();
|
||||
@@ -242,10 +246,6 @@ T hleDoLog(Log t, LogLevel level, T res, const char *file, int line, const char
|
||||
return res;
|
||||
}
|
||||
|
||||
if (convert_code && (int)res >= 0) {
|
||||
level = LogLevel::LDEBUG;
|
||||
}
|
||||
|
||||
u64 fmtRes = res;
|
||||
if (std::is_floating_point<T>::value) {
|
||||
// We reinterpret as the bits for now, so we can have a common helper.
|
||||
|
||||
@@ -564,6 +564,12 @@ static void NotifyUserIfSlow() {
|
||||
}
|
||||
}
|
||||
|
||||
static DisplayLayoutConfig g_displayLayoutConfigCached;
|
||||
|
||||
void __DisplaySetDisplayLayoutConfig(const DisplayLayoutConfig &config) {
|
||||
g_displayLayoutConfigCached = config;
|
||||
}
|
||||
|
||||
void __DisplayFlip(int cyclesLate) {
|
||||
_dbg_assert_(gpu);
|
||||
|
||||
@@ -644,7 +650,7 @@ void __DisplayFlip(int cyclesLate) {
|
||||
}
|
||||
}
|
||||
if (nextFrame) {
|
||||
gpu->CopyDisplayToOutput(fbReallyDirty);
|
||||
gpu->CopyDisplayToOutput(g_displayLayoutConfigCached, fbReallyDirty);
|
||||
if (fbReallyDirty) {
|
||||
DisplayFireActualFlip();
|
||||
}
|
||||
@@ -746,6 +752,7 @@ void hleLagSync(u64 userdata, int cyclesLate) {
|
||||
double now = before;
|
||||
while (now < goal && goal < now + 0.01) {
|
||||
// Tight loop on win32 - intentionally, as timing is otherwise not precise enough.
|
||||
// TODO: Use the precise waits if available
|
||||
#ifndef _WIN32
|
||||
const double left = goal - now;
|
||||
if (left > 0.0f && left < 1.0f) { // Sanity check
|
||||
|
||||
@@ -34,3 +34,7 @@ void __DisplaySetWasPaused();
|
||||
|
||||
void Register_sceDisplay_driver();
|
||||
void __DisplayWaitForVblanks(const char* reason, int vblanks, bool callbacks = false);
|
||||
|
||||
// Kinda hacky, but what can you do... we can't pass it through the jit etc.
|
||||
struct DisplayLayoutConfig;
|
||||
void __DisplaySetDisplayLayoutConfig(const DisplayLayoutConfig &config);
|
||||
|
||||
@@ -39,7 +39,7 @@ static u32 umdPopup = PSP_UMD_POPUP_DISABLE;
|
||||
static u32 backlightOffTime;
|
||||
|
||||
void __ImposeInit() {
|
||||
language = g_Config.GetPSPLanguage();
|
||||
language = GetPSPLanguage();
|
||||
if (PSP_CoreParameter().compat.flags().EnglishOrJapaneseOnly) {
|
||||
if (language != PSP_SYSTEMPARAM_LANGUAGE_ENGLISH && language != PSP_SYSTEMPARAM_LANGUAGE_JAPANESE) {
|
||||
language = PSP_SYSTEMPARAM_LANGUAGE_ENGLISH;
|
||||
@@ -73,7 +73,7 @@ static u32 sceImposeGetBatteryIconStatus(u32 chargingPtr, u32 iconStatusPtr)
|
||||
static u32 sceImposeSetLanguageMode(u32 languageVal, u32 buttonVal) {
|
||||
language = languageVal;
|
||||
buttonValue = buttonVal;
|
||||
if (language != g_Config.GetPSPLanguage()) {
|
||||
if (language != GetPSPLanguage()) {
|
||||
return hleLogWarning(Log::sceUtility, 0, "ignoring requested language");
|
||||
}
|
||||
return hleLogDebug(Log::sceUtility, 0);
|
||||
|
||||
+16
-4
@@ -999,6 +999,11 @@ static u32 sceIoChstat(const char *filename, u32 iostatptr, u32 changebits) {
|
||||
|
||||
static u32 npdrmRead(FileNode *f, u8 *data, int size) {
|
||||
PGD_DESC *pgd = f->pgdInfo;
|
||||
if (!pgd) {
|
||||
// When pgdInfo is null, fall back to reading the file in non-encrypted mode
|
||||
WARN_LOG(Log::IO, "npdrmRead: pgdInfo is null for file %s, reading as non-encrypted", f->fullpath.c_str());
|
||||
return (u32)pspFileSystem.ReadFile(f->handle, data, size);
|
||||
}
|
||||
u32 block, offset, blockPos;
|
||||
u32 remain_size, copy_size;
|
||||
|
||||
@@ -1150,7 +1155,7 @@ static u32 sceIoRead(int id, u32 data_addr, int size) {
|
||||
f->waitingSyncThreads.push_back(__KernelGetCurThread());
|
||||
return hleLogDebug(Log::sceIo, 0, "deferring result");
|
||||
} else if (result >= 0) {
|
||||
return hleDelayResult(hleLogDebug(Log::ME, result), "io read", us);
|
||||
return hleDelayResult(hleLogDebug(Log::sceIo, result), "io read", us);
|
||||
} else {
|
||||
return hleLogWarning(Log::ME, result, "error %08x", result);
|
||||
}
|
||||
@@ -1360,6 +1365,10 @@ static u32 npdrmLseek(FileNode *f, s32 where, FileMove whence)
|
||||
{
|
||||
u32 newPos, blockPos;
|
||||
|
||||
if (!f->pgdInfo) {
|
||||
//WARN_LOG(Log::IO, "npdrmLseek: pgdInfo is null for file %s, seeking as non-encrypted", f->fullpath.c_str());
|
||||
return (u32)pspFileSystem.SeekFile(f->handle, where, whence);
|
||||
}
|
||||
if(whence==FILEMOVE_BEGIN){
|
||||
newPos = where;
|
||||
}else if(whence==FILEMOVE_CURRENT){
|
||||
@@ -2061,23 +2070,25 @@ static u32 sceIoDevctl(const char *name, int cmd, u32 argAddr, int argLen, u32 o
|
||||
PSP_CoreParameter().fastForward = false;
|
||||
return hleLogDebug(Log::sceIo, 0);
|
||||
case EMULATOR_DEVCTL__GET_ASPECT_RATIO:
|
||||
// NOTE: This currently only works correctly in landscape mode!
|
||||
if (Memory::IsValidAddress(outPtr)) {
|
||||
// TODO: Share code with CalculateDisplayOutputRect to take a few more things into account.
|
||||
// I have a planned further refactoring.
|
||||
float ar;
|
||||
if (g_Config.bDisplayStretch) {
|
||||
if (g_Config.displayLayoutLandscape.bDisplayStretch) {
|
||||
ar = (float)g_display.dp_xres / (float)g_display.dp_yres;
|
||||
} else {
|
||||
ar = g_Config.fDisplayAspectRatio * (480.0f / 272.0f);
|
||||
ar = g_Config.displayLayoutLandscape.fDisplayAspectRatio * (480.0f / 272.0f);
|
||||
}
|
||||
Memory::Write_Float(ar, outPtr);
|
||||
}
|
||||
return hleLogDebug(Log::sceIo, 0);
|
||||
case EMULATOR_DEVCTL__GET_SCALE:
|
||||
// NOTE: This currently only works correctly in landscape mode!
|
||||
if (Memory::IsValidAddress(outPtr)) {
|
||||
// TODO: Maybe do something more sophisticated taking the longest side and screen rotation
|
||||
// into account, etc.
|
||||
float scale = (float)g_display.dp_xres * g_Config.fDisplayScale / 480.0f;
|
||||
float scale = (float)g_display.dp_xres * g_Config.displayLayoutLandscape.fDisplayScale / 480.0f;
|
||||
Memory::Write_Float(scale, outPtr);
|
||||
}
|
||||
return hleLogDebug(Log::sceIo, 0);
|
||||
@@ -2586,6 +2597,7 @@ int __IoIoctl(u32 id, u32 cmd, u32 indataPtr, u32 inlen, u32 outdataPtr, u32 out
|
||||
f->pgdInfo = pgd_open(kirk, pgd_header, 2, key_ptr);
|
||||
if (!f->pgdInfo) {
|
||||
f->npdrm = false;
|
||||
f->pgd_offset = 0; // Reset PGD offset so file can be read as regular file
|
||||
pspFileSystem.SeekFile(f->handle, (s32)0, FILEMOVE_BEGIN);
|
||||
if (memcmp(pgd_header, pgd_magic, 4) == 0) {
|
||||
// File is PGD file, but key mismatch
|
||||
|
||||
+33
-8
@@ -50,6 +50,10 @@ static const int MPEG_PCM_ES_OUTPUT_SIZE = 320;
|
||||
static const int MPEG_DATA_ES_SIZE = 0xA0000;
|
||||
static const int MPEG_DATA_ES_OUTPUT_SIZE = 0xA0000;
|
||||
|
||||
// MPEG AVC Resource Management
|
||||
static const int MPEG_AVC_RESOURCE_SIZE = 0x20000;
|
||||
static const int MPEG_AVC_RESOURCE_FLAG = 0x4;
|
||||
|
||||
// MPEG analysis results.
|
||||
static const int MPEG_VERSION_0012 = 0;
|
||||
static const int MPEG_VERSION_0013 = 1;
|
||||
@@ -164,6 +168,11 @@ static u32 streamIdGen;
|
||||
static int actionPostPut;
|
||||
std::map<u32, MpegContext *> g_mpegCtxs;
|
||||
|
||||
// MPEG AVC Resource Management
|
||||
static u32 sceMpegAvcResourceAddr = 0;
|
||||
static u32 sceMpegAvcResourceDataAddr = 0;
|
||||
static int sceMpegAvcResourceFlags = 0;
|
||||
|
||||
MpegContext::MpegContext() {
|
||||
memcpy(mpegheader, defaultMpegheader, 2048);
|
||||
}
|
||||
@@ -929,16 +938,14 @@ static bool decodePmpVideo(PSPPointer<SceMpegRingBuffer> ringbuffer, u32 pmpctxA
|
||||
avcodec_send_packet(pCodecCtx, &packet);
|
||||
int len = avcodec_receive_frame(pCodecCtx, pFrame);
|
||||
if (len == 0) {
|
||||
len = pFrame->pkt_size;
|
||||
got_picture = 1;
|
||||
} else if (len == AVERROR(EAGAIN)) {
|
||||
len = 0;
|
||||
got_picture = 0;
|
||||
} else {
|
||||
got_picture = 0;
|
||||
}
|
||||
#else
|
||||
int len = avcodec_decode_video2(pCodecCtx, pFrame, &got_picture, &packet);
|
||||
avcodec_decode_video2(pCodecCtx, pFrame, &got_picture, &packet);
|
||||
#endif
|
||||
DEBUG_LOG(Log::Mpeg, "got_picture %d", got_picture);
|
||||
if (got_picture){
|
||||
@@ -1963,8 +1970,12 @@ static u32 sceMpegQueryUserdataEsSize(u32 mpeg, u32 esSizeAddr, u32 outSizeAddr)
|
||||
}
|
||||
|
||||
static u32 sceMpegAvcResourceGetAvcDecTopAddr(u32 mpeg) {
|
||||
// it's just a random address
|
||||
return hleLogError(Log::Mpeg, 0x12345678, "UNIMPL");
|
||||
if ((sceMpegAvcResourceFlags & MPEG_AVC_RESOURCE_FLAG) == 0) {
|
||||
return hleLogDebug(Log::Mpeg, 0);
|
||||
}
|
||||
|
||||
u32 addr = sceMpegAvcResourceDataAddr & 0xFFC00000;
|
||||
return hleLogDebug(Log::Mpeg, addr);
|
||||
}
|
||||
|
||||
static u32 sceMpegAvcResourceFinish(u32 mpeg) {
|
||||
@@ -1972,8 +1983,11 @@ static u32 sceMpegAvcResourceFinish(u32 mpeg) {
|
||||
}
|
||||
|
||||
static u32 sceMpegAvcResourceGetAvcEsBuf(u32 mpeg) {
|
||||
ERROR_LOG_REPORT_ONCE(mpegResourceEsBuf, Log::Mpeg, "UNIMPL sceMpegAvcResourceGetAvcEsBuf(%08x)", mpeg);
|
||||
return hleNoLog(0);
|
||||
if ((sceMpegAvcResourceFlags & MPEG_AVC_RESOURCE_FLAG) == 0) {
|
||||
return hleLogDebug(Log::Mpeg, 0);
|
||||
}
|
||||
|
||||
return hleLogDebug(Log::Mpeg, sceMpegAvcResourceDataAddr);
|
||||
}
|
||||
|
||||
static u32 sceMpegAvcResourceInit(u32 mpeg) {
|
||||
@@ -1981,7 +1995,18 @@ static u32 sceMpegAvcResourceInit(u32 mpeg) {
|
||||
return hleLogError(Log::Mpeg, SCE_MPEG_ERROR_INVALID_VALUE);
|
||||
}
|
||||
|
||||
return hleLogError(Log::Mpeg, 0, "UNIMPL");
|
||||
if ((sceMpegAvcResourceFlags & MPEG_AVC_RESOURCE_FLAG) != 0) {
|
||||
return hleLogError(Log::Mpeg, SCE_MPEG_ERROR_ALREADY_INIT);
|
||||
}
|
||||
|
||||
// Allocate memory for the AVC resource
|
||||
// In a real implementation, this would use sceKernelCreateFpl and sceKernelAllocateFpl
|
||||
// For PPSSPP, we'll simulate the allocation
|
||||
sceMpegAvcResourceAddr = 0x10000000; // Simulated base address
|
||||
sceMpegAvcResourceDataAddr = sceMpegAvcResourceAddr + 8; // Offset for data
|
||||
sceMpegAvcResourceFlags |= MPEG_AVC_RESOURCE_FLAG;
|
||||
|
||||
return hleLogDebug(Log::Mpeg, 0);
|
||||
}
|
||||
|
||||
// TODO: SIMD this (or rather, the caller).
|
||||
|
||||
+104
-15
@@ -1,15 +1,80 @@
|
||||
#include "scePspNpDrm_user.h"
|
||||
|
||||
#include "Core/MemMapHelpers.h"
|
||||
#include "Core/HLE/HLE.h"
|
||||
#include "Core/HLE/FunctionWrappers.h"
|
||||
#include "Core/HLE/sceIo.h"
|
||||
#include "Core/FileSystems/MetaFileSystem.h"
|
||||
|
||||
extern MetaFileSystem pspFileSystem;
|
||||
|
||||
// PSP NpDrm licensee key tracking
|
||||
static const int PSP_NPDRM_LICENSEE_KEY_LENGTH = 0x10;
|
||||
static u8 licenseeKey[PSP_NPDRM_LICENSEE_KEY_LENGTH];
|
||||
static bool isLicenseeKeySet = false;
|
||||
|
||||
// Error codes matching JPCSP
|
||||
static const int ERROR_NPDRM_NO_K_LICENSEE_SET = 0x80550901;
|
||||
static const int ERROR_NPDRM_INVALID_FILE = 0x80550902;
|
||||
|
||||
// Check if the file is an encrypted EDAT file by reading the magic number
|
||||
static bool isEncrypted(u32 edataFd) {
|
||||
// Check for "\0PSPEDAT" magic number
|
||||
// The magic bytes are: 0x00 0x50 0x53 0x50 0x45 0x44 0x41 0x54
|
||||
// Which translates to: 0x50535000 and 0x54414445 as uint32_t (little endian)
|
||||
|
||||
u32 error;
|
||||
// Get the file handle from the file descriptor
|
||||
u32 fileHandle = __IoGetFileHandleFromId(edataFd, error);
|
||||
if (fileHandle == (u32)-1) {
|
||||
// Invalid file handle, assume not encrypted
|
||||
return false;
|
||||
}
|
||||
|
||||
// Save the current file position
|
||||
s64 originalPos = pspFileSystem.GetSeekPos(fileHandle);
|
||||
|
||||
// Seek to the beginning of the file
|
||||
pspFileSystem.SeekFile(fileHandle, 0, FILEMOVE_BEGIN);
|
||||
|
||||
// Read 8 bytes from the file
|
||||
u8 header[8];
|
||||
size_t bytesRead = pspFileSystem.ReadFile(fileHandle, header, 8);
|
||||
|
||||
// Restore the original file position
|
||||
pspFileSystem.SeekFile(fileHandle, (s32)originalPos, FILEMOVE_BEGIN);
|
||||
|
||||
// Check if we read enough bytes
|
||||
if (bytesRead < 8) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check the magic numbers
|
||||
// Bytes 0-3 should be 0x50535000 (PSP\0) in little endian
|
||||
// Bytes 4-7 should be 0x54414445 (EDAT) in little endian
|
||||
u32 magic1 = (header[3] << 24) | (header[2] << 16) | (header[1] << 8) | header[0];
|
||||
u32 magic2 = (header[7] << 24) | (header[6] << 16) | (header[5] << 8) | header[4];
|
||||
|
||||
// PSP\0 = 0x50535000, EDAT = 0x54414445
|
||||
return (magic1 == 0x50535000 && magic2 == 0x54414445);
|
||||
}
|
||||
|
||||
static int sceNpDrmSetLicenseeKey(u32 npDrmKeyAddr) {
|
||||
return hleLogWarning(Log::sceIo, 0, "UNIMPL");
|
||||
// Read the licensee key from memory
|
||||
// The key is PSP_NPDRM_LICENSEE_KEY_LENGTH bytes (0x10 = 16 bytes)
|
||||
if (Memory::IsValidAddress(npDrmKeyAddr)) {
|
||||
Memory::Memcpy(licenseeKey, npDrmKeyAddr, PSP_NPDRM_LICENSEE_KEY_LENGTH, "NpDrmSetLicenseeKey");
|
||||
isLicenseeKeySet = true;
|
||||
return hleLogInfo(Log::sceIo, 0);
|
||||
}
|
||||
return hleLogError(Log::sceIo, ERROR_NPDRM_INVALID_FILE, "Invalid address");
|
||||
}
|
||||
|
||||
static int sceNpDrmClearLicenseeKey() {
|
||||
return hleLogWarning(Log::sceIo, 0, "UNIMPL");
|
||||
// Clear the licensee key and mark it as not set
|
||||
memset(licenseeKey, 0, PSP_NPDRM_LICENSEE_KEY_LENGTH);
|
||||
isLicenseeKeySet = false;
|
||||
return hleLogInfo(Log::sceIo, 0);
|
||||
}
|
||||
|
||||
static int sceNpDrmRenameCheck(const char *filename) {
|
||||
@@ -17,20 +82,45 @@ static int sceNpDrmRenameCheck(const char *filename) {
|
||||
}
|
||||
|
||||
static int sceNpDrmEdataSetupKey(u32 edataFd) {
|
||||
int usec = 0;
|
||||
|
||||
// __IoIoctl logs like a hle function, so no need to log here.
|
||||
|
||||
// set PGD offset
|
||||
int retval = __IoIoctl(edataFd, 0x04100002, 0x90, 0, 0, 0, usec);
|
||||
if (retval < 0) {
|
||||
return hleDelayResult(hleLogError(Log::sceIo, retval), "io ctrl command", usec);
|
||||
// Return an error if the key has not been set.
|
||||
// Note: An empty key is valid, as long as it was set with sceNpDrmSetLicenseeKey.
|
||||
if (!isLicenseeKeySet) {
|
||||
return hleLogError(Log::sceIo, ERROR_NPDRM_NO_K_LICENSEE_SET, "Licensee key not set");
|
||||
}
|
||||
|
||||
// call PGD open
|
||||
// Note that usec accumulates.
|
||||
retval = __IoIoctl(edataFd, 0x04100001, 0, 0, 0, 0, usec);
|
||||
return hleDelayResult(hleLogDebugOrError(Log::sceIo, retval), "io ctrl command", usec);
|
||||
// Get file info to validate the file descriptor
|
||||
u32 error;
|
||||
u32 fileHandle = __IoGetFileHandleFromId(edataFd, error);
|
||||
if (fileHandle == (u32)-1) {
|
||||
return hleLogError(Log::sceIo, -1, "Invalid file descriptor");
|
||||
}
|
||||
|
||||
int result = 0;
|
||||
|
||||
// Check if the file is encrypted
|
||||
if (isEncrypted(edataFd)) {
|
||||
// File is encrypted, set up PGD decryption
|
||||
// In JPCSP, this wraps the file in an EDATVirtualFile (lines 215-218)
|
||||
// In this C++ implementation, we use ioctl commands to achieve the same result
|
||||
int usec = 0;
|
||||
|
||||
// set PGD offset
|
||||
int retval = __IoIoctl(edataFd, 0x04100002, 0x90, 0, 0, 0, usec);
|
||||
if (retval < 0) {
|
||||
return hleDelayResult(hleLogError(Log::sceIo, retval), "io ctrl command", usec);
|
||||
}
|
||||
|
||||
// call PGD open
|
||||
// Note that usec accumulates.
|
||||
retval = __IoIoctl(edataFd, 0x04100001, 0, 0, 0, 0, usec);
|
||||
if (retval < 0) {
|
||||
return hleDelayResult(hleLogError(Log::sceIo, retval), "io ctrl command", usec);
|
||||
}
|
||||
|
||||
return hleDelayResult(hleLogDebug(Log::sceIo, result), "io ctrl command", usec);
|
||||
}
|
||||
|
||||
return hleLogInfo(Log::sceIo, result);
|
||||
}
|
||||
|
||||
static int sceNpDrmEdataGetDataSize(u32 edataFd) {
|
||||
@@ -56,4 +146,3 @@ void Register_sceNpDrm() {
|
||||
RegisterHLEModule("sceNpDrm", ARRAY_SIZE(sceNpDrm), sceNpDrm);
|
||||
RegisterHLEModule("scePspNpDrm_user", ARRAY_SIZE(sceNpDrm), sceNpDrm);
|
||||
}
|
||||
|
||||
|
||||
@@ -332,8 +332,10 @@ std::vector<std::string> Camera::getDeviceList() {
|
||||
if (winCamera) {
|
||||
return winCamera->getDeviceList();
|
||||
}
|
||||
#elif PPSSPP_PLATFORM(ANDROID) || PPSSPP_PLATFORM(IOS)
|
||||
#elif PPSSPP_PLATFORM(ANDROID) || PPSSPP_PLATFORM(IOS)
|
||||
return System_GetCameraDeviceList();
|
||||
#elif PPSSPP_PLATFORM(MAC)
|
||||
return __mac_getDeviceList();
|
||||
#elif defined(USING_QT_UI) // Qt:macOS / Qt:Linux
|
||||
return __qt_getDeviceList();
|
||||
#elif PPSSPP_PLATFORM(LINUX) // SDL:Linux
|
||||
@@ -362,6 +364,8 @@ int Camera::startCapture() {
|
||||
char command[40] = {0};
|
||||
snprintf(command, sizeof(command), "startVideo_%dx%d", width, height);
|
||||
System_CameraCommand(command);
|
||||
#elif PPSSPP_PLATFORM(MAC)
|
||||
__mac_startCapture(width, height);
|
||||
#elif PPSSPP_PLATFORM(LINUX)
|
||||
__v4l_startCapture(width, height);
|
||||
#else
|
||||
@@ -376,8 +380,10 @@ int Camera::stopCapture() {
|
||||
if (winCamera) {
|
||||
winCamera->sendMessage({ CAPTUREDEVIDE_COMMAND::STOP, nullptr });
|
||||
}
|
||||
#elif PPSSPP_PLATFORM(ANDROID) || PPSSPP_PLATFORM(IOS) || defined(USING_QT_UI)
|
||||
#elif PPSSPP_PLATFORM(ANDROID) || PPSSPP_PLATFORM(IOS) || defined(USING_QT_UI)
|
||||
System_CameraCommand("stopVideo");
|
||||
#elif PPSSPP_PLATFORM(MAC)
|
||||
__mac_stopCapture();
|
||||
#elif PPSSPP_PLATFORM(LINUX)
|
||||
__v4l_stopCapture();
|
||||
#else
|
||||
|
||||
+71
-1
@@ -24,6 +24,7 @@
|
||||
#include "Common/Serialize/SerializeFuncs.h"
|
||||
#include "Common/Serialize/SerializeMap.h"
|
||||
#include "Common/Serialize/SerializeSet.h"
|
||||
#include "Common/File/VFS/VFS.h"
|
||||
#include "Core/Config.h"
|
||||
#include "Core/CoreTiming.h"
|
||||
#include "Core/HLE/HLE.h"
|
||||
@@ -236,6 +237,75 @@ static void UtilityVolatileUnlock(u64 userdata, int cyclesLate) {
|
||||
dialog->FinishVolatile();
|
||||
}
|
||||
|
||||
// Applies the Auto setting if set. Returns an enum value from PSP_SYSTEMPARAM_LANGUAGE_*.
|
||||
const std::map<std::string, std::pair<std::string, int>, std::less<>> &GetLangValuesMapping() {
|
||||
static std::map<std::string, std::pair<std::string, int>, std::less<>> langValuesMapping_;
|
||||
|
||||
static const std::map<std::string_view, int> langCodeMapping = {
|
||||
{"JAPANESE", PSP_SYSTEMPARAM_LANGUAGE_JAPANESE},
|
||||
{"ENGLISH", PSP_SYSTEMPARAM_LANGUAGE_ENGLISH},
|
||||
{"FRENCH", PSP_SYSTEMPARAM_LANGUAGE_FRENCH},
|
||||
{"SPANISH", PSP_SYSTEMPARAM_LANGUAGE_SPANISH},
|
||||
{"GERMAN", PSP_SYSTEMPARAM_LANGUAGE_GERMAN},
|
||||
{"ITALIAN", PSP_SYSTEMPARAM_LANGUAGE_ITALIAN},
|
||||
{"DUTCH", PSP_SYSTEMPARAM_LANGUAGE_DUTCH},
|
||||
{"PORTUGUESE", PSP_SYSTEMPARAM_LANGUAGE_PORTUGUESE},
|
||||
{"RUSSIAN", PSP_SYSTEMPARAM_LANGUAGE_RUSSIAN},
|
||||
{"KOREAN", PSP_SYSTEMPARAM_LANGUAGE_KOREAN},
|
||||
{"CHINESE_TRADITIONAL", PSP_SYSTEMPARAM_LANGUAGE_CHINESE_TRADITIONAL},
|
||||
{"CHINESE_SIMPLIFIED", PSP_SYSTEMPARAM_LANGUAGE_CHINESE_SIMPLIFIED},
|
||||
};
|
||||
|
||||
if (!langValuesMapping_.empty()) {
|
||||
return langValuesMapping_;
|
||||
}
|
||||
|
||||
// Lazy-load the mapping.
|
||||
IniFile mapping;
|
||||
mapping.LoadFromVFS(g_VFS, "langregion.ini");
|
||||
std::vector<std::string> keys;
|
||||
Section *section = mapping.GetSection("LangRegionNames");
|
||||
if (section) {
|
||||
section->GetKeys(&keys);
|
||||
}
|
||||
|
||||
const Section *langRegionNames = mapping.GetOrCreateSection("LangRegionNames");
|
||||
const Section *systemLanguage = mapping.GetOrCreateSection("SystemLanguage");
|
||||
|
||||
for (size_t i = 0; i < keys.size(); i++) {
|
||||
std::string langName;
|
||||
if (!langRegionNames->Get(keys[i], &langName)) {
|
||||
continue;
|
||||
}
|
||||
std::string langCode = "ENGLISH";
|
||||
int iLangCode = PSP_SYSTEMPARAM_LANGUAGE_ENGLISH;
|
||||
if (systemLanguage->Get(keys[i], &langCode)) {
|
||||
const auto iter = langCodeMapping.find(langCode);
|
||||
if (iter != langCodeMapping.end()) {
|
||||
iLangCode = iter->second;
|
||||
}
|
||||
}
|
||||
langValuesMapping_[keys[i]] = std::make_pair(langName, iLangCode);
|
||||
}
|
||||
|
||||
return langValuesMapping_;
|
||||
}
|
||||
|
||||
int GetPSPLanguage() {
|
||||
if (g_Config.iLanguage == -1) {
|
||||
const auto &langValuesMapping = GetLangValuesMapping();
|
||||
auto iter = langValuesMapping.find(g_Config.sLanguageIni);
|
||||
if (iter != langValuesMapping.end()) {
|
||||
return iter->second.second;
|
||||
} else {
|
||||
// Fallback to English
|
||||
return PSP_SYSTEMPARAM_LANGUAGE_ENGLISH;
|
||||
}
|
||||
} else {
|
||||
return g_Config.iLanguage;
|
||||
}
|
||||
}
|
||||
|
||||
void __UtilityInit() {
|
||||
saveDialog = new PSPSaveDialog(UtilityDialogType::SAVEDATA);
|
||||
msgDialog = new PSPMsgDialog(UtilityDialogType::MSG);
|
||||
@@ -1193,7 +1263,7 @@ static u32 sceUtilityGetSystemParamInt(u32 id, u32 destaddr) {
|
||||
param = g_Config.bDayLightSavings?PSP_SYSTEMPARAM_DAYLIGHTSAVINGS_SAVING:PSP_SYSTEMPARAM_DAYLIGHTSAVINGS_STD;
|
||||
break;
|
||||
case PSP_SYSTEMPARAM_ID_INT_LANGUAGE:
|
||||
param = g_Config.GetPSPLanguage();
|
||||
param = GetPSPLanguage();
|
||||
if (PSP_CoreParameter().compat.flags().EnglishOrJapaneseOnly) {
|
||||
if (param != PSP_SYSTEMPARAM_LANGUAGE_ENGLISH && param != PSP_SYSTEMPARAM_LANGUAGE_JAPANESE) {
|
||||
param = PSP_SYSTEMPARAM_LANGUAGE_ENGLISH;
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
class PointerWrap;
|
||||
|
||||
@@ -142,3 +143,7 @@ const ModuleLoadInfo *__UtilityModuleInfo(int moduleID);
|
||||
bool __UtilityModuleGetMemoryRange(int moduleID, u32 *startPtr, u32 *sizePtr);
|
||||
|
||||
void Register_sceUtility();
|
||||
|
||||
// Return value is one of PSP_SYSTEMPARAM_LANGUAGE_*.
|
||||
const std::map<std::string, std::pair<std::string, int>, std::less<>> &GetLangValuesMapping();
|
||||
int GetPSPLanguage();
|
||||
|
||||
+5
-3
@@ -65,11 +65,13 @@ void __cameraDummyImage(int width, int height, unsigned char** outData, int* out
|
||||
*outData = nullptr;
|
||||
return;
|
||||
}
|
||||
// Generate a solid RED surface for camera detection (e.g., Invizimals)
|
||||
// RGB: (255, 0, 0) = Red
|
||||
for (int y = 0; y < height; y++) {
|
||||
for (int x = 0; x < width; x++) {
|
||||
rgbData[3 * (y * width + x) + 0] = x*255/width;
|
||||
rgbData[3 * (y * width + x) + 1] = x*255/width;
|
||||
rgbData[3 * (y * width + x) + 2] = y*255/height;
|
||||
rgbData[3 * (y * width + x) + 0] = 255; // Red channel: full intensity
|
||||
rgbData[3 * (y * width + x) + 1] = 0; // Green channel: off
|
||||
rgbData[3 * (y * width + x) + 2] = 0; // Blue channel: off
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -51,6 +51,11 @@ void __cameraDummyImage(int width, int height, unsigned char** outData, int* out
|
||||
int __qt_startCapture(int width, int height);
|
||||
int __qt_stopCapture();
|
||||
|
||||
#elif PPSSPP_PLATFORM(MAC)
|
||||
std::vector<std::string> __mac_getDeviceList();
|
||||
int __mac_startCapture(int width, int height);
|
||||
int __mac_stopCapture();
|
||||
|
||||
#elif PPSSPP_PLATFORM(LINUX) && !PPSSPP_PLATFORM(ANDROID)
|
||||
#include <fcntl.h>
|
||||
#include <linux/videodev2.h>
|
||||
|
||||
@@ -369,7 +369,11 @@ void MediaEngine::closeContext() {
|
||||
m_pCodecCtxs.clear();
|
||||
// These are streams allocated from avformat_new_stream.
|
||||
for (auto &it : m_codecsToClose) {
|
||||
#if LIBAVCODEC_VERSION_MAJOR >= 62
|
||||
avcodec_free_context(&it);
|
||||
#else
|
||||
avcodec_close(it);
|
||||
#endif
|
||||
}
|
||||
m_codecsToClose.clear();
|
||||
if (m_pFormatCtx)
|
||||
@@ -690,7 +694,7 @@ bool MediaEngine::stepVideo(int videoPixelMode, bool skipFrame) {
|
||||
avcodec_send_packet(m_pCodecCtx, &packet);
|
||||
int result = avcodec_receive_frame(m_pCodecCtx, m_pFrame);
|
||||
if (result == 0) {
|
||||
result = m_pFrame->pkt_size;
|
||||
result = 1;
|
||||
frameFinished = 1;
|
||||
} else if (result == AVERROR(EAGAIN)) {
|
||||
result = 0;
|
||||
|
||||
@@ -327,7 +327,7 @@ bool FFmpegAudioDecoder::Decode(const uint8_t *inbuf, int inbytes, int *inbytesC
|
||||
int err = avcodec_receive_frame(codecCtx_, frame_);
|
||||
int len = 0;
|
||||
if (err >= 0) {
|
||||
len = frame_->pkt_size;
|
||||
len = packet.size;
|
||||
got_frame = 1;
|
||||
} else if (err != AVERROR(EAGAIN)) {
|
||||
len = err;
|
||||
@@ -392,7 +392,11 @@ bool FFmpegAudioDecoder::Decode(const uint8_t *inbuf, int inbytes, int *inbytesC
|
||||
|
||||
if (!swrCtx_ || swr_init(swrCtx_) < 0) {
|
||||
ERROR_LOG(Log::ME, "swr_init: Failed to initialize the resampling context");
|
||||
#if LIBAVCODEC_VERSION_MAJOR >= 62
|
||||
avcodec_free_context(&codecCtx_);
|
||||
#else
|
||||
avcodec_close(codecCtx_);
|
||||
#endif
|
||||
codec_ = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
|
||||
|
||||
#include "ppsspp_config.h"
|
||||
|
||||
#include <cstdlib>
|
||||
#include <mutex>
|
||||
|
||||
|
||||
@@ -22,8 +22,6 @@
|
||||
#include <mutex>
|
||||
|
||||
#include "ext/cityhash/city.h"
|
||||
#include "ext/xxhash.h"
|
||||
|
||||
#include "Common/File/FileUtil.h"
|
||||
#include "Common/Log.h"
|
||||
#include "Common/StringUtils.h"
|
||||
|
||||
+8
-22
@@ -11,17 +11,7 @@
|
||||
#include "Core/MemMapHelpers.h"
|
||||
#include "Core/MIPS/MIPSAsm.h"
|
||||
|
||||
namespace MIPSAsm
|
||||
{
|
||||
static std::string errorText;
|
||||
|
||||
std::string GetAssembleError()
|
||||
{
|
||||
return errorText;
|
||||
}
|
||||
|
||||
class PspAssemblerFile: public AssemblerFile
|
||||
{
|
||||
class PspAssemblerFile : public AssemblerFile {
|
||||
public:
|
||||
PspAssemblerFile() {
|
||||
address = 0;
|
||||
@@ -30,7 +20,7 @@ public:
|
||||
bool open(bool onlyCheck) override{ return true; };
|
||||
void close() override { };
|
||||
bool isOpen() override { return true; };
|
||||
bool write(void* data, size_t length) override {
|
||||
bool write(void *data, size_t length) override {
|
||||
if (!Memory::IsValidAddress((u32)(address+length-1)))
|
||||
return false;
|
||||
|
||||
@@ -58,7 +48,7 @@ private:
|
||||
fs::path dummyFilename_;
|
||||
};
|
||||
|
||||
bool MipsAssembleOpcode(const char *line, DebugInterface *cpu, u32 address) {
|
||||
bool MipsAssembleOpcode(std::string_view line, DebugInterface *cpu, u32 address, std::string *error) {
|
||||
std::vector<std::string> errors;
|
||||
|
||||
char str[64];
|
||||
@@ -75,14 +65,12 @@ bool MipsAssembleOpcode(const char *line, DebugInterface *cpu, u32 address) {
|
||||
g_symbolMap->GetLabels(args.labels);
|
||||
}
|
||||
|
||||
errorText.clear();
|
||||
if (!runArmips(args))
|
||||
{
|
||||
for (size_t i = 0; i < errors.size(); i++)
|
||||
{
|
||||
errorText += errors[i];
|
||||
error->clear();
|
||||
if (!runArmips(args)) {
|
||||
for (size_t i = 0; i < errors.size(); i++) {
|
||||
(*error) += errors[i];
|
||||
if (i != errors.size() - 1)
|
||||
errorText += "\n";
|
||||
error->push_back('\n');
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -90,5 +78,3 @@ bool MipsAssembleOpcode(const char *line, DebugInterface *cpu, u32 address) {
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
+2
-4
@@ -1,8 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <string_view>
|
||||
#include "Core/Debugger/DebugInterface.h"
|
||||
|
||||
namespace MIPSAsm {
|
||||
bool MipsAssembleOpcode(const char* line, DebugInterface* cpu, u32 address);
|
||||
std::string GetAssembleError();
|
||||
}
|
||||
bool MipsAssembleOpcode(std::string_view line, DebugInterface* cpu, u32 address, std::string *error);
|
||||
|
||||
@@ -24,8 +24,7 @@
|
||||
#include "Core/Debugger/DebugInterface.h"
|
||||
|
||||
|
||||
class MIPSDebugInterface : public DebugInterface
|
||||
{
|
||||
class MIPSDebugInterface : public DebugInterface {
|
||||
MIPSState *cpu;
|
||||
public:
|
||||
MIPSDebugInterface(MIPSState *_cpu) { cpu = _cpu; }
|
||||
|
||||
+3
-2
@@ -261,8 +261,9 @@ bool Load_PSP_ISO(FileLoader *fileLoader, std::string *error_string) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//in case we didn't go through EmuScreen::boot
|
||||
g_Config.loadGameConfig(id, g_paramSFO.GetValueString("TITLE"));
|
||||
// If there's a game-specific config, load it.
|
||||
g_Config.LoadGameConfig(id);
|
||||
|
||||
System_PostUIMessage(UIMessage::CONFIG_LOADED);
|
||||
INFO_LOG(Log::Loader, "Loading %s...", bootpath.c_str());
|
||||
// TODO: We can't use the initial error_string pointer.
|
||||
|
||||
+14
-7
@@ -54,6 +54,7 @@
|
||||
#include "Core/HLE/Plugins.h"
|
||||
#include "Core/HLE/ReplaceTables.h"
|
||||
#include "Core/HLE/sceKernel.h"
|
||||
#include "Core/HLE/sceUtility.h"
|
||||
#include "Core/HW/Display.h"
|
||||
#include "Core/Config.h"
|
||||
#include "Core/Core.h"
|
||||
@@ -359,11 +360,6 @@ static bool CPU_Init(FileLoader *fileLoader, IdentifiedFileType type, std::strin
|
||||
g_CoreParameter.compat.Load(g_paramSFO.GetDiscID());
|
||||
ShowCompatWarnings(g_CoreParameter.compat);
|
||||
|
||||
// Compat settings can override the software renderer, take care of that here.
|
||||
if (g_Config.bSoftwareRendering || PSP_CoreParameter().compat.flags().ForceSoftwareRenderer) {
|
||||
g_CoreParameter.gpuCore = GPUCORE_SOFTWARE;
|
||||
}
|
||||
|
||||
// This must be before Memory::Init because plugins can override the memory size.
|
||||
if (type != IdentifiedFileType::PPSSPP_GE_DUMP) {
|
||||
HLEPlugins::Init();
|
||||
@@ -420,7 +416,9 @@ static bool CPU_Init(FileLoader *fileLoader, IdentifiedFileType type, std::strin
|
||||
g_CoreParameter.mountIsoLoader = ConstructFileLoader(g_CoreParameter.mountIso);
|
||||
}
|
||||
|
||||
// TODO: Check Game INI here for settings, patches and cheats, and modify coreParameter accordingly
|
||||
// Game-specific settings are load from for example Load_PSP_ISO (which calls g_Config.LoadGameConfig).
|
||||
// We can't do things that depend on these before the below switch. So for example, the adjustment of the GPU core
|
||||
// to software has now been moved below it.
|
||||
|
||||
// If they shut down early, we'll catch it when load completes.
|
||||
// Note: this may return before init is complete, which is checked if CPU_IsReady().
|
||||
@@ -481,6 +479,13 @@ static bool CPU_Init(FileLoader *fileLoader, IdentifiedFileType type, std::strin
|
||||
g_recentFiles.Add(g_CoreParameter.fileToStart.ToString());
|
||||
}
|
||||
|
||||
// Update things that depend on game-specific config here.
|
||||
|
||||
// Compat settings can override the software renderer, take care of that here.
|
||||
if (g_Config.bSoftwareRendering || g_CoreParameter.compat.flags().ForceSoftwareRenderer) {
|
||||
g_CoreParameter.gpuCore = GPUCORE_SOFTWARE;
|
||||
}
|
||||
|
||||
InstallExceptionHandler(&Memory::HandleFault);
|
||||
return true;
|
||||
}
|
||||
@@ -730,7 +735,7 @@ void PSP_Shutdown(bool success) {
|
||||
|
||||
currentMIPS = nullptr;
|
||||
|
||||
g_Config.unloadGameConfig();
|
||||
g_Config.UnloadGameConfig();
|
||||
|
||||
Core_NotifyLifecycle(CoreLifecycle::STOPPED);
|
||||
|
||||
@@ -929,6 +934,8 @@ void DumpFileIfEnabled(const u8 *dataPtr, const u32 length, std::string_view nam
|
||||
delete[] path;
|
||||
}
|
||||
}, path);
|
||||
} else {
|
||||
delete[] path;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -769,7 +769,7 @@ static bool HasTextDrawer() {
|
||||
if (textDrawer) {
|
||||
textDrawer->SetFontScale(1.0f, 1.0f);
|
||||
textDrawer->SetForcedDPIScale(1.0f);
|
||||
textDrawer->SetFont(g_Config.sFont.c_str(), 18, 0);
|
||||
textDrawer->SetOrCreateFont(FontStyle(FontFamily::SansSerif, 18, FontStyleFlags::Default));
|
||||
}
|
||||
textDrawerInited = true;
|
||||
return textDrawer != nullptr;
|
||||
|
||||
@@ -76,7 +76,7 @@ FramebufferManagerCommon::~FramebufferManagerCommon() {
|
||||
void FramebufferManagerCommon::Init(int msaaLevel) {
|
||||
// We may need to override the render size if the shader is upscaling or SSAA.
|
||||
NotifyDisplayResized();
|
||||
NotifyRenderResized(msaaLevel);
|
||||
NotifyRenderResized(displayLayoutConfigCopy_, msaaLevel);
|
||||
}
|
||||
|
||||
// Returns true if we need to stop the render thread
|
||||
@@ -110,17 +110,20 @@ bool FramebufferManagerCommon::UpdateRenderSize(int msaaLevel) {
|
||||
return newRender || newSettings;
|
||||
}
|
||||
|
||||
void FramebufferManagerCommon::CheckPostShaders() {
|
||||
void FramebufferManagerCommon::CheckPostShaders(const DisplayLayoutConfig &config) {
|
||||
if (updatePostShaders_) {
|
||||
presentation_->UpdatePostShader();
|
||||
presentation_->UpdatePostShader(config);
|
||||
updatePostShaders_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
void FramebufferManagerCommon::BeginFrame() {
|
||||
void FramebufferManagerCommon::BeginFrame(const DisplayLayoutConfig &config) {
|
||||
DecimateFBOs();
|
||||
presentation_->BeginFrame();
|
||||
presentation_->BeginFrame(config);
|
||||
currentRenderVfb_ = nullptr;
|
||||
|
||||
// Hack.
|
||||
displayLayoutConfigCopy_ = config;
|
||||
}
|
||||
|
||||
bool FramebufferManagerCommon::PresentedThisFrame() const {
|
||||
@@ -647,7 +650,7 @@ void FramebufferManagerCommon::SetDepthFrameBuffer(bool isClearingDepth) {
|
||||
// Sanity check the depth buffer pointer.
|
||||
if (Memory::IsValidRange(currentRenderVfb_->z_address, currentRenderVfb_->width * 2)) {
|
||||
const u16 *src = (const u16 *)Memory::GetPointerUnchecked(currentRenderVfb_->z_address);
|
||||
DrawPixels(currentRenderVfb_, 0, 0, (const u8 *)src, GE_FORMAT_DEPTH16, currentRenderVfb_->z_stride, currentRenderVfb_->width, currentRenderVfb_->height, RASTER_DEPTH, "Depth Upload");
|
||||
DrawPixels(nullptr, currentRenderVfb_, 0, 0, (const u8 *)src, GE_FORMAT_DEPTH16, currentRenderVfb_->z_stride, currentRenderVfb_->width, currentRenderVfb_->height, RASTER_DEPTH, "Depth Upload");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1172,7 +1175,7 @@ void FramebufferManagerCommon::UpdateFromMemory(u32 addr, int size) {
|
||||
// TODO: This doesn't seem quite right anymore.
|
||||
fmt = displayFormat_;
|
||||
}
|
||||
DrawPixels(vfb, 0, 0, Memory::GetPointerUnchecked(addr), fmt, vfb->fb_stride, vfb->width, vfb->height, RASTER_COLOR, "UpdateFromMemory_DrawPixels");
|
||||
DrawPixels(nullptr, vfb, 0, 0, Memory::GetPointerUnchecked(addr), fmt, vfb->fb_stride, vfb->width, vfb->height, RASTER_COLOR, "UpdateFromMemory_DrawPixels");
|
||||
SetColorUpdated(vfb, gstate_c.skipDrawReason);
|
||||
} else {
|
||||
INFO_LOG(Log::FrameBuf, "Invalidating FBO for %08x (%dx%d %s)", vfb->fb_address, vfb->width, vfb->height, GeBufferFormatToString(vfb->fb_format));
|
||||
@@ -1188,7 +1191,7 @@ void FramebufferManagerCommon::UpdateFromMemory(u32 addr, int size) {
|
||||
gstate_c.Dirty(DIRTY_FRAGMENTSHADER_STATE);
|
||||
}
|
||||
|
||||
void FramebufferManagerCommon::DrawPixels(VirtualFramebuffer *vfb, int dstX, int dstY, const u8 *srcPixels, GEBufferFormat srcPixelFormat, int srcStride, int width, int height, RasterChannel channel, const char *tag) {
|
||||
void FramebufferManagerCommon::DrawPixels(const DisplayLayoutConfig *config, VirtualFramebuffer *vfb, int dstX, int dstY, const u8 *srcPixels, GEBufferFormat srcPixelFormat, int srcStride, int width, int height, RasterChannel channel, const char *tag) {
|
||||
textureCache_->ForgetLastTexture();
|
||||
shaderManager_->DirtyLastShader();
|
||||
float u0 = 0.0f, u1 = 1.0f;
|
||||
@@ -1206,15 +1209,16 @@ void FramebufferManagerCommon::DrawPixels(VirtualFramebuffer *vfb, int dstX, int
|
||||
draw_->SetScissorRect(0, 0, vfb->renderWidth, vfb->renderHeight);
|
||||
} else {
|
||||
_dbg_assert_(channel == RASTER_COLOR);
|
||||
_dbg_assert_(config);
|
||||
// We are drawing directly to the back buffer so need to flip.
|
||||
// Should more of this be handled by the presentation engine?
|
||||
if (needBackBufferYSwap_)
|
||||
std::swap(v0, v1);
|
||||
flags = g_Config.iDisplayFilter == SCALE_LINEAR ? DRAWTEX_LINEAR : DRAWTEX_NEAREST;
|
||||
flags = config->iDisplayFilter == SCALE_LINEAR ? DRAWTEX_LINEAR : DRAWTEX_NEAREST;
|
||||
flags = flags | DRAWTEX_TO_BACKBUFFER;
|
||||
FRect frame = GetScreenFrame(pixelWidth_, pixelHeight_);
|
||||
FRect frame = GetScreenFrame(config ? config->bIgnoreScreenInsets : false, pixelWidth_, pixelHeight_);
|
||||
FRect rc;
|
||||
CalculateDisplayOutputRect(&rc, 480.0f, 272.0f, frame, ROTATION_LOCKED_HORIZONTAL);
|
||||
CalculateDisplayOutputRect(*config, &rc, 480.0f, 272.0f, frame, ROTATION_LOCKED_HORIZONTAL);
|
||||
SetViewport2D(rc.x, rc.y, rc.w, rc.h);
|
||||
draw_->SetScissorRect(0, 0, pixelWidth_, pixelHeight_);
|
||||
}
|
||||
@@ -1512,7 +1516,7 @@ Draw::Texture *FramebufferManagerCommon::MakePixelTexture(const u8 *srcPixels, G
|
||||
return tex;
|
||||
}
|
||||
|
||||
bool FramebufferManagerCommon::DrawFramebufferToOutput(const u8 *srcPixels, int srcStride, GEBufferFormat srcPixelFormat) {
|
||||
bool FramebufferManagerCommon::DrawFramebufferToOutput(const DisplayLayoutConfig &config, const u8 *srcPixels, int srcStride, GEBufferFormat srcPixelFormat) {
|
||||
textureCache_->ForgetLastTexture();
|
||||
shaderManager_->DirtyLastShader();
|
||||
|
||||
@@ -1522,8 +1526,8 @@ bool FramebufferManagerCommon::DrawFramebufferToOutput(const u8 *srcPixels, int
|
||||
if (!pixelsTex)
|
||||
return false;
|
||||
|
||||
int uvRotation = useBufferedRendering_ ? g_Config.iInternalScreenRotation : ROTATION_LOCKED_HORIZONTAL;
|
||||
OutputFlags flags = g_Config.iDisplayFilter == SCALE_LINEAR ? OutputFlags::LINEAR : OutputFlags::NEAREST;
|
||||
int uvRotation = useBufferedRendering_ ? config.iInternalScreenRotation : ROTATION_LOCKED_HORIZONTAL;
|
||||
OutputFlags flags = config.iDisplayFilter == SCALE_LINEAR ? OutputFlags::LINEAR : OutputFlags::NEAREST;
|
||||
if (needBackBufferYSwap_) {
|
||||
flags |= OutputFlags::BACKBUFFER_FLIPPED;
|
||||
}
|
||||
@@ -1534,7 +1538,7 @@ bool FramebufferManagerCommon::DrawFramebufferToOutput(const u8 *srcPixels, int
|
||||
|
||||
presentation_->UpdateUniforms(textureCache_->VideoIsPlaying());
|
||||
presentation_->SourceTexture(pixelsTex, 512, 272);
|
||||
presentation_->CopyToOutput(flags, uvRotation, u0, v0, u1, v1);
|
||||
presentation_->CopyToOutput(config, flags, uvRotation, u0, v0, u1, v1);
|
||||
|
||||
// PresentationCommon sets all kinds of state, we can't rely on anything.
|
||||
gstate_c.Dirty(DIRTY_ALL);
|
||||
@@ -1550,7 +1554,7 @@ void FramebufferManagerCommon::SetViewport2D(int x, int y, int w, int h) {
|
||||
draw_->SetViewport(viewport);
|
||||
}
|
||||
|
||||
void FramebufferManagerCommon::CopyDisplayToOutput(bool reallyDirty) {
|
||||
void FramebufferManagerCommon::CopyDisplayToOutput(const DisplayLayoutConfig &config, bool reallyDirty) {
|
||||
DownloadFramebufferOnSwitch(currentRenderVfb_);
|
||||
shaderManager_->DirtyLastShader();
|
||||
|
||||
@@ -1625,7 +1629,7 @@ void FramebufferManagerCommon::CopyDisplayToOutput(bool reallyDirty) {
|
||||
if (Memory::IsValidAddress(fbaddr)) {
|
||||
// The game is displaying something directly from RAM. In GTA, it's decoded video.
|
||||
// If successful, this effectively calls presentation_->NotifyPresent();
|
||||
if (!DrawFramebufferToOutput(Memory::GetPointerUnchecked(fbaddr), displayStride_, displayFormat_)) {
|
||||
if (!DrawFramebufferToOutput(config, Memory::GetPointerUnchecked(fbaddr), displayStride_, displayFormat_)) {
|
||||
if (useBufferedRendering_) {
|
||||
// Bind and clear the backbuffer. This should be the first time during the frame that it's bound.
|
||||
draw_->BindFramebufferAsRenderTarget(nullptr, { Draw::RPAction::CLEAR, Draw::RPAction::CLEAR, Draw::RPAction::CLEAR }, "CopyDisplayToOutput_DrawError");
|
||||
@@ -1689,8 +1693,8 @@ void FramebufferManagerCommon::CopyDisplayToOutput(bool reallyDirty) {
|
||||
|
||||
textureCache_->ForgetLastTexture();
|
||||
|
||||
int uvRotation = useBufferedRendering_ ? g_Config.iInternalScreenRotation : ROTATION_LOCKED_HORIZONTAL;
|
||||
OutputFlags flags = g_Config.iDisplayFilter == SCALE_LINEAR ? OutputFlags::LINEAR : OutputFlags::NEAREST;
|
||||
int uvRotation = useBufferedRendering_ ? config.iInternalScreenRotation : ROTATION_LOCKED_HORIZONTAL;
|
||||
OutputFlags flags = config.iDisplayFilter == SCALE_LINEAR ? OutputFlags::LINEAR : OutputFlags::NEAREST;
|
||||
if (needBackBufferYSwap_) {
|
||||
flags |= OutputFlags::BACKBUFFER_FLIPPED;
|
||||
}
|
||||
@@ -1703,7 +1707,7 @@ void FramebufferManagerCommon::CopyDisplayToOutput(bool reallyDirty) {
|
||||
int actualHeight = (vfb->bufferHeight * vfb->renderHeight) / vfb->height;
|
||||
presentation_->UpdateUniforms(textureCache_->VideoIsPlaying());
|
||||
presentation_->SourceFramebuffer(vfb->fbo, actualWidth, actualHeight);
|
||||
presentation_->CopyToOutput(flags, uvRotation, u0, v0, u1, v1);
|
||||
presentation_->CopyToOutput(config, flags, uvRotation, u0, v0, u1, v1);
|
||||
} else if (useBufferedRendering_) {
|
||||
WARN_LOG(Log::FrameBuf, "Using buffered rendering, and current VFB lacks an FBO: %08x", vfb->fb_address);
|
||||
} else {
|
||||
@@ -2179,7 +2183,7 @@ bool FramebufferManagerCommon::NotifyFramebufferCopy(u32 src, u32 dst, int size,
|
||||
const u8 *srcBase = Memory::GetPointerUnchecked(src);
|
||||
GEBufferFormat srcFormat = channel == RASTER_DEPTH ? GE_FORMAT_DEPTH16 : dstBuffer->fb_format;
|
||||
int srcStride = channel == RASTER_DEPTH ? dstBuffer->z_stride : dstBuffer->fb_stride;
|
||||
DrawPixels(dstBuffer, 0, dstY, srcBase, srcFormat, srcStride, dstBuffer->width, dstH, channel, "MemcpyFboUpload_DrawPixels");
|
||||
DrawPixels(nullptr, dstBuffer, 0, dstY, srcBase, srcFormat, srcStride, dstBuffer->width, dstH, channel, "MemcpyFboUpload_DrawPixels");
|
||||
SetColorUpdated(dstBuffer, skipDrawReason);
|
||||
RebindFramebuffer("RebindFramebuffer - Memcpy fbo upload");
|
||||
// This is a memcpy, let's still copy just in case.
|
||||
@@ -2711,7 +2715,7 @@ bool FramebufferManagerCommon::NotifyBlockTransferBefore(u32 dstBasePtr, int dst
|
||||
WARN_LOG_ONCE(btud, Log::G3D, "Block transfer upload %08x -> %08x (%dx%d %d,%d bpp=%d %s)", srcBasePtr, dstBasePtr, width, height, dstX, dstY, bpp, RasterChannelToString(dstRect.channel));
|
||||
FlushBeforeCopy();
|
||||
const u8 *srcBase = Memory::GetPointerUnchecked(srcBasePtr) + (srcX + srcY * srcStride) * bpp;
|
||||
DrawPixels(dstRect.vfb, dstX, dstY, srcBase, dstRect.vfb->Format(dstRect.channel), srcStride * bpp / 2, (int)(dstRect.w_bytes / 2), dstRect.h, dstRect.channel, "BlockTransferCopy_DrawPixelsDepth");
|
||||
DrawPixels(nullptr, dstRect.vfb, dstX, dstY, srcBase, dstRect.vfb->Format(dstRect.channel), srcStride * bpp / 2, (int)(dstRect.w_bytes / 2), dstRect.h, dstRect.channel, "BlockTransferCopy_DrawPixelsDepth");
|
||||
RebindFramebuffer("RebindFramebuffer - UploadDepth");
|
||||
return true;
|
||||
}
|
||||
@@ -2765,7 +2769,8 @@ void FramebufferManagerCommon::NotifyBlockTransferAfter(u32 dstBasePtr, int dstS
|
||||
bool isDisplayBuffer = CurrentDisplayFramebufAddr() == dstBasePtr;
|
||||
if (isPrevDisplayBuffer || isDisplayBuffer) {
|
||||
FlushBeforeCopy();
|
||||
DrawFramebufferToOutput(Memory::GetPointerUnchecked(dstBasePtr), dstStride, displayFormat_);
|
||||
// HACK
|
||||
DrawFramebufferToOutput(displayLayoutConfigCopy_, Memory::GetPointerUnchecked(dstBasePtr), dstStride, displayFormat_);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -2805,7 +2810,7 @@ void FramebufferManagerCommon::NotifyBlockTransferAfter(u32 dstBasePtr, int dstS
|
||||
// Resizing may change the viewport/etc.
|
||||
gstate_c.Dirty(DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_CULLRANGE);
|
||||
}
|
||||
DrawPixels(dstRect.vfb, static_cast<int>(dstX * dstXFactor), dstY, srcBase, dstRect.vfb->fb_format, static_cast<int>(srcStride * dstXFactor), static_cast<int>(dstRect.w_bytes / bpp * dstXFactor), dstRect.h, RASTER_COLOR, "BlockTransferCopy_DrawPixels");
|
||||
DrawPixels(nullptr, dstRect.vfb, static_cast<int>(dstX * dstXFactor), dstY, srcBase, dstRect.vfb->fb_format, static_cast<int>(srcStride * dstXFactor), static_cast<int>(dstRect.w_bytes / bpp * dstXFactor), dstRect.h, RASTER_COLOR, "BlockTransferCopy_DrawPixels");
|
||||
SetColorUpdated(dstRect.vfb, skipDrawReason);
|
||||
RebindFramebuffer("RebindFramebuffer - NotifyBlockTransferAfter");
|
||||
}
|
||||
@@ -2832,11 +2837,11 @@ void FramebufferManagerCommon::NotifyDisplayResized() {
|
||||
updatePostShaders_ = true;
|
||||
}
|
||||
|
||||
void FramebufferManagerCommon::NotifyRenderResized(int msaaLevel) {
|
||||
void FramebufferManagerCommon::NotifyRenderResized(const DisplayLayoutConfig &config, int msaaLevel) {
|
||||
gstate_c.skipDrawReason &= ~SKIPDRAW_NON_DISPLAYED_FB;
|
||||
|
||||
int w, h, scaleFactor;
|
||||
presentation_->CalculateRenderResolution(&w, &h, &scaleFactor, &postShaderIsUpscalingFilter_, &postShaderIsSupersampling_);
|
||||
presentation_->CalculateRenderResolution(config, &w, &h, &scaleFactor, &postShaderIsUpscalingFilter_, &postShaderIsSupersampling_);
|
||||
PSP_CoreParameter().renderWidth = w;
|
||||
PSP_CoreParameter().renderHeight = h;
|
||||
PSP_CoreParameter().renderScaleFactor = scaleFactor;
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Common/Log.h"
|
||||
#include "Common/GPU/thin3d.h"
|
||||
#include "Core/Config.h"
|
||||
#include "Core/ConfigValues.h"
|
||||
#include "GPU/GPU.h"
|
||||
#include "GPU/ge_constants.h"
|
||||
@@ -284,6 +285,7 @@ class DrawEngineCommon;
|
||||
class PresentationCommon;
|
||||
class ShaderManagerCommon;
|
||||
class TextureCacheCommon;
|
||||
struct DisplayLayoutConfig;
|
||||
|
||||
class FramebufferManagerCommon {
|
||||
public:
|
||||
@@ -301,7 +303,7 @@ public:
|
||||
}
|
||||
|
||||
void Init(int msaaLevel);
|
||||
virtual void BeginFrame();
|
||||
virtual void BeginFrame(const DisplayLayoutConfig &config);
|
||||
void SetDisplayFramebuffer(u32 framebuf, u32 stride, GEBufferFormat format);
|
||||
void DestroyFramebuf(VirtualFramebuffer *v);
|
||||
|
||||
@@ -332,7 +334,7 @@ public:
|
||||
void RebindFramebuffer(const char *tag);
|
||||
std::vector<const VirtualFramebuffer *> GetFramebufferList() const;
|
||||
|
||||
void CopyDisplayToOutput(bool reallyDirty);
|
||||
void CopyDisplayToOutput(const DisplayLayoutConfig &config, bool reallyDirty);
|
||||
|
||||
bool NotifyFramebufferCopy(u32 src, u32 dest, int size, GPUCopyFlag flags, u32 skipDrawReason);
|
||||
void PerformWriteFormattedFromMemory(u32 addr, int size, int width, GEBufferFormat fmt);
|
||||
@@ -358,9 +360,10 @@ public:
|
||||
void ReadFramebufferToMemory(VirtualFramebuffer *vfb, int x, int y, int w, int h, RasterChannel channel, Draw::ReadbackMode mode);
|
||||
|
||||
void DownloadFramebufferForClut(u32 fb_address, u32 loadBytes);
|
||||
bool DrawFramebufferToOutput(const u8 *srcPixels, int srcStride, GEBufferFormat srcPixelFormat);
|
||||
bool DrawFramebufferToOutput(const DisplayLayoutConfig &config, const u8 *srcPixels, int srcStride, GEBufferFormat srcPixelFormat);
|
||||
|
||||
void DrawPixels(VirtualFramebuffer *vfb, int dstX, int dstY, const u8 *srcPixels, GEBufferFormat srcPixelFormat, int srcStride, int width, int height, RasterChannel channel, const char *tag);
|
||||
// TODO: Should split into one that uses config, and one that doesn't.
|
||||
void DrawPixels(const DisplayLayoutConfig *config, VirtualFramebuffer *vfb, int dstX, int dstY, const u8 *srcPixels, GEBufferFormat srcPixelFormat, int srcStride, int width, int height, RasterChannel channel, const char *tag);
|
||||
|
||||
size_t NumVFBs() const { return vfbs_.size(); }
|
||||
|
||||
@@ -437,11 +440,11 @@ public:
|
||||
}
|
||||
void SetSafeSize(u16 w, u16 h);
|
||||
|
||||
void NotifyRenderResized(int msaaLevel);
|
||||
void NotifyRenderResized(const DisplayLayoutConfig &config, int msaaLevel);
|
||||
virtual void NotifyDisplayResized();
|
||||
void NotifyConfigChanged();
|
||||
|
||||
void CheckPostShaders();
|
||||
void CheckPostShaders(const DisplayLayoutConfig &config);
|
||||
|
||||
virtual void DestroyAllFBOs();
|
||||
|
||||
@@ -498,6 +501,11 @@ public:
|
||||
return vfbs_;
|
||||
}
|
||||
|
||||
// Hack, only needed for non-buffered rendering.
|
||||
const DisplayLayoutConfig &GetDisplayLayoutConfigCopy() const {
|
||||
return displayLayoutConfigCopy_;
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void ReadbackFramebuffer(VirtualFramebuffer *vfb, int x, int y, int w, int h, RasterChannel channel, Draw::ReadbackMode mode);
|
||||
// Used for when a shader is required, such as GLES.
|
||||
@@ -659,6 +667,8 @@ protected:
|
||||
// Depth readback helper state
|
||||
u8 *convBuf_ = nullptr;
|
||||
u32 convBufSize_ = 0;
|
||||
|
||||
DisplayLayoutConfig displayLayoutConfigCopy_{};
|
||||
};
|
||||
|
||||
// Should probably live elsewhere.
|
||||
|
||||
@@ -586,7 +586,7 @@ DepthScaleFactors GetDepthScaleFactors(u32 useFlags) {
|
||||
}
|
||||
}
|
||||
|
||||
void ConvertViewportAndScissor(bool useBufferedRendering, float renderWidth, float renderHeight, int bufferWidth, int bufferHeight, ViewportAndScissor &out) {
|
||||
void ConvertViewportAndScissor(const DisplayLayoutConfig &config, bool useBufferedRendering, float renderWidth, float renderHeight, int bufferWidth, int bufferHeight, ViewportAndScissor &out) {
|
||||
out.throughMode = gstate.isModeThrough();
|
||||
|
||||
float renderWidthFactor, renderHeightFactor;
|
||||
@@ -600,9 +600,9 @@ void ConvertViewportAndScissor(bool useBufferedRendering, float renderWidth, flo
|
||||
} else {
|
||||
float pixelW = PSP_CoreParameter().pixelWidth;
|
||||
float pixelH = PSP_CoreParameter().pixelHeight;
|
||||
FRect frame = GetScreenFrame(pixelW, pixelH);
|
||||
FRect frame = GetScreenFrame(config.bIgnoreScreenInsets, pixelW, pixelH);
|
||||
FRect rc;
|
||||
CalculateDisplayOutputRect(&rc, 480, 272, frame, ROTATION_LOCKED_HORIZONTAL);
|
||||
CalculateDisplayOutputRect(config, &rc, 480, 272, frame, ROTATION_LOCKED_HORIZONTAL);
|
||||
displayOffsetX = rc.x;
|
||||
displayOffsetY = rc.y;
|
||||
renderWidth = rc.w;
|
||||
|
||||
@@ -87,7 +87,9 @@ struct ViewportAndScissor {
|
||||
bool throughMode;
|
||||
};
|
||||
|
||||
void ConvertViewportAndScissor(bool useBufferedRendering, float renderWidth, float renderHeight, int bufferWidth, int bufferHeight, ViewportAndScissor &out);
|
||||
// config is only used for non-buffered rendering.
|
||||
struct DisplayLayoutConfig;
|
||||
void ConvertViewportAndScissor(const DisplayLayoutConfig &config, bool useBufferedRendering, float renderWidth, float renderHeight, int bufferWidth, int bufferHeight, ViewportAndScissor &out);
|
||||
void UpdateCachedViewportState(const ViewportAndScissor &vpAndScissor);
|
||||
|
||||
// NOTE: See the .cpp file for detailed comment about how the use flags are interpreted.
|
||||
|
||||
@@ -137,7 +137,7 @@ void LoadPostShaderInfo(Draw::DrawContext *draw, const std::vector<Path> &direct
|
||||
continue;
|
||||
}
|
||||
|
||||
if (section.Exists("Fragment") && section.Exists("Vertex") &&
|
||||
if (section.HasKey("Fragment") && section.HasKey("Vertex") &&
|
||||
(strncasecmp(shaderType.c_str(), "render", shaderType.size()) == 0 ||
|
||||
strncasecmp(shaderType.c_str(), "StereoToMono", shaderType.size()) == 0)) {
|
||||
// Valid shader!
|
||||
@@ -197,7 +197,7 @@ void LoadPostShaderInfo(Draw::DrawContext *draw, const std::vector<Path> &direct
|
||||
} else {
|
||||
notVisible.push_back(info);
|
||||
}
|
||||
} else if (section.Exists("Compute") && strncasecmp(shaderType.c_str(), "texture", shaderType.size()) == 0) {
|
||||
} else if (section.HasKey("Compute") && strncasecmp(shaderType.c_str(), "texture", shaderType.size()) == 0) {
|
||||
// This is a texture shader.
|
||||
TextureShaderInfo info{};
|
||||
std::string temp;
|
||||
|
||||
@@ -54,7 +54,7 @@ void SetOverrideScreenFrame(const Bounds *bounds) {
|
||||
}
|
||||
}
|
||||
|
||||
FRect GetScreenFrame(float pixelWidth, float pixelHeight) {
|
||||
FRect GetScreenFrame(bool ignoreInsets, float pixelWidth, float pixelHeight) {
|
||||
FRect rc = FRect{
|
||||
0.0f,
|
||||
0.0f,
|
||||
@@ -62,7 +62,7 @@ FRect GetScreenFrame(float pixelWidth, float pixelHeight) {
|
||||
pixelHeight,
|
||||
};
|
||||
|
||||
bool applyInset = !g_Config.bIgnoreScreenInsets;
|
||||
const bool applyInset = !ignoreInsets;
|
||||
|
||||
if (applyInset) {
|
||||
// Remove the DPI scale to get back to pixels.
|
||||
@@ -89,28 +89,27 @@ FRect GetScreenFrame(float pixelWidth, float pixelHeight) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
void CalculateDisplayOutputRect(FRect *rc, float origW, float origH, const FRect &frame, int rotation) {
|
||||
void CalculateDisplayOutputRect(const DisplayLayoutConfig &config, FRect *rc, float origW, float origH, const FRect &frame, int rotation) {
|
||||
float outW;
|
||||
float outH;
|
||||
|
||||
bool rotated = rotation == ROTATION_LOCKED_VERTICAL || rotation == ROTATION_LOCKED_VERTICAL180;
|
||||
|
||||
bool stretch = g_Config.bDisplayStretch && !g_Config.bDisplayIntegerScale;
|
||||
bool stretch = config.bDisplayStretch && !config.bDisplayIntegerScale;
|
||||
|
||||
float offsetX = g_Config.fDisplayOffsetX;
|
||||
float offsetY = g_Config.fDisplayOffsetY;
|
||||
float offsetX = config.fDisplayOffsetX;
|
||||
float offsetY = config.fDisplayOffsetY;
|
||||
|
||||
float scale = g_Config.fDisplayScale;
|
||||
float aspectRatioAdjust = g_Config.fDisplayAspectRatio;
|
||||
float scale = config.fDisplayScale;
|
||||
float aspectRatioAdjust = config.fDisplayAspectRatio;
|
||||
|
||||
float origRatio = !rotated ? origW / origH : origH / origW;
|
||||
float frameRatio = frame.w / frame.h;
|
||||
|
||||
if (stretch) {
|
||||
// Automatically set aspect ratio to match the display, IF the rotation matches the output display ratio! Otherwise, just
|
||||
// sets standard aspect ratio because actually stretching will just look silly.
|
||||
bool globalRotated = g_display.rotation == DisplayRotation::ROTATE_90 || g_display.rotation == DisplayRotation::ROTATE_270;
|
||||
if (rotated == (g_display.dp_yres > g_display.dp_xres)) {
|
||||
// ignore it because actually stretching will just look silly.
|
||||
if (rotated == (g_display.GetDeviceOrientation() == DeviceOrientation::Portrait)) {
|
||||
origRatio = frameRatio;
|
||||
} else {
|
||||
origRatio *= aspectRatioAdjust;
|
||||
@@ -141,7 +140,7 @@ void CalculateDisplayOutputRect(FRect *rc, float origW, float origH, const FRect
|
||||
}
|
||||
}
|
||||
|
||||
if (g_Config.bDisplayIntegerScale) {
|
||||
if (config.bDisplayIntegerScale) {
|
||||
float wDim = 480.0f;
|
||||
if (rotated) {
|
||||
wDim = 272.0f;
|
||||
@@ -151,7 +150,7 @@ void CalculateDisplayOutputRect(FRect *rc, float origW, float origH, const FRect
|
||||
if (zoom == 0) {
|
||||
// Auto (1:1) mode, not super meaningful with integer scaling, but let's do something that makes
|
||||
// some sense. use the longest dimension, just to have something. round down.
|
||||
if (!g_Config.IsPortrait()) {
|
||||
if (!config.InternalRotationIsPortrait()) {
|
||||
zoom = (PSP_CoreParameter().pixelWidth) / 480;
|
||||
} else {
|
||||
zoom = (PSP_CoreParameter().pixelHeight) / 480;
|
||||
@@ -187,22 +186,22 @@ PresentationCommon::~PresentationCommon() {
|
||||
DestroyDeviceObjects();
|
||||
}
|
||||
|
||||
void PresentationCommon::GetCardboardSettings(CardboardSettings *cardboardSettings) const {
|
||||
if (!g_Config.bEnableCardboardVR) {
|
||||
void PresentationCommon::GetCardboardSettings(const DisplayLayoutConfig &config, CardboardSettings *cardboardSettings) const {
|
||||
if (!config.bEnableCardboardVR) {
|
||||
cardboardSettings->enabled = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// Calculate Cardboard Settings
|
||||
float cardboardScreenScale = g_Config.iCardboardScreenSize / 100.0f;
|
||||
float cardboardScreenScale = config.iCardboardScreenSize / 100.0f;
|
||||
float cardboardScreenWidth = pixelWidth_ / 2.0f * cardboardScreenScale;
|
||||
float cardboardScreenHeight = pixelHeight_ * cardboardScreenScale;
|
||||
float cardboardMaxXShift = (pixelWidth_ / 2.0f - cardboardScreenWidth) / 2.0f;
|
||||
float cardboardUserXShift = g_Config.iCardboardXShift / 100.0f * cardboardMaxXShift;
|
||||
float cardboardUserXShift = config.iCardboardXShift / 100.0f * cardboardMaxXShift;
|
||||
float cardboardLeftEyeX = cardboardMaxXShift + cardboardUserXShift;
|
||||
float cardboardRightEyeX = pixelWidth_ / 2.0f + cardboardMaxXShift - cardboardUserXShift;
|
||||
float cardboardMaxYShift = pixelHeight_ / 2.0f - cardboardScreenHeight / 2.0f;
|
||||
float cardboardUserYShift = g_Config.iCardboardYShift / 100.0f * cardboardMaxYShift;
|
||||
float cardboardUserYShift = config.iCardboardYShift / 100.0f * cardboardMaxYShift;
|
||||
float cardboardScreenY = cardboardMaxYShift + cardboardUserYShift;
|
||||
|
||||
cardboardSettings->enabled = true;
|
||||
@@ -266,7 +265,7 @@ static std::string ReadShaderSrc(const Path &filename) {
|
||||
|
||||
// Note: called on resize and settings changes.
|
||||
// Also takes care of making sure the appropriate stereo shader is compiled.
|
||||
bool PresentationCommon::UpdatePostShader() {
|
||||
bool PresentationCommon::UpdatePostShader(const DisplayLayoutConfig &config) {
|
||||
DestroyStereoShader();
|
||||
|
||||
if (gstate_c.Use(GPU_USE_SIMPLE_STEREO_PERSPECTIVE)) {
|
||||
@@ -298,7 +297,7 @@ bool PresentationCommon::UpdatePostShader() {
|
||||
for (size_t i = 0; i < shaderInfo.size(); ++i) {
|
||||
const ShaderInfo *next = i + 1 < shaderInfo.size() ? shaderInfo[i + 1] : nullptr;
|
||||
Draw::Pipeline *postPipeline = nullptr;
|
||||
if (!BuildPostShader(shaderInfo[i], next, &postPipeline)) {
|
||||
if (!BuildPostShader(config, shaderInfo[i], next, &postPipeline)) {
|
||||
DestroyPostShader();
|
||||
return false;
|
||||
}
|
||||
@@ -383,7 +382,7 @@ bool PresentationCommon::CompilePostShader(const ShaderInfo *shaderInfo, Draw::P
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PresentationCommon::BuildPostShader(const ShaderInfo * shaderInfo, const ShaderInfo * next, Draw::Pipeline **outPipeline) {
|
||||
bool PresentationCommon::BuildPostShader(const DisplayLayoutConfig &config, const ShaderInfo *shaderInfo, const ShaderInfo * next, Draw::Pipeline **outPipeline) {
|
||||
if (!CompilePostShader(shaderInfo, outPipeline)) {
|
||||
return false;
|
||||
}
|
||||
@@ -398,7 +397,7 @@ bool PresentationCommon::BuildPostShader(const ShaderInfo * shaderInfo, const Sh
|
||||
|
||||
if (next && next->isUpscalingFilter) {
|
||||
// Force 1x for this shader, so the next can upscale.
|
||||
const bool isPortrait = g_Config.IsPortrait();
|
||||
const bool isPortrait = config.InternalRotationIsPortrait();
|
||||
nextWidth = isPortrait ? 272 : 480;
|
||||
nextHeight = isPortrait ? 480 : 272;
|
||||
} else if (next && next->SSAAFilterLevel >= 2) {
|
||||
@@ -408,8 +407,8 @@ bool PresentationCommon::BuildPostShader(const ShaderInfo * shaderInfo, const Sh
|
||||
} else if (shaderInfo->outputResolution) {
|
||||
// If the current shader uses output res (not next), we will use output res for it.
|
||||
FRect rc;
|
||||
FRect frame = GetScreenFrame((float)pixelWidth_, (float)pixelHeight_);
|
||||
CalculateDisplayOutputRect(&rc, 480.0f, 272.0f, frame, g_Config.iInternalScreenRotation);
|
||||
FRect frame = GetScreenFrame(config.bIgnoreScreenInsets, (float)pixelWidth_, (float)pixelHeight_);
|
||||
CalculateDisplayOutputRect(config, &rc, 480.0f, 272.0f, frame, config.iInternalScreenRotation);
|
||||
nextWidth = (int)rc.w;
|
||||
nextHeight = (int)rc.h;
|
||||
}
|
||||
@@ -541,8 +540,6 @@ void PresentationCommon::CreateDeviceObjects() {
|
||||
texColor_ = CreatePipeline({ draw_->GetVshaderPreset(VS_TEXTURE_COLOR_2D), draw_->GetFshaderPreset(FS_TEXTURE_COLOR_2D) }, false, &vsTexColBufDesc);
|
||||
texColorRBSwizzle_ = CreatePipeline({ draw_->GetVshaderPreset(VS_TEXTURE_COLOR_2D), draw_->GetFshaderPreset(FS_TEXTURE_COLOR_2D_RB_SWIZZLE) }, false, &vsTexColBufDesc);
|
||||
|
||||
if (restorePostShader_)
|
||||
UpdatePostShader();
|
||||
restorePostShader_ = false;
|
||||
}
|
||||
|
||||
@@ -655,7 +652,7 @@ void PresentationCommon::UpdateUniforms(bool hasVideo) {
|
||||
hasVideo_ = hasVideo;
|
||||
}
|
||||
|
||||
void PresentationCommon::CopyToOutput(OutputFlags flags, int uvRotation, float u0, float v0, float u1, float v1) {
|
||||
void PresentationCommon::CopyToOutput(const DisplayLayoutConfig &config, OutputFlags flags, int uvRotation, float u0, float v0, float u1, float v1) {
|
||||
draw_->Invalidate(InvalidationFlags::CACHED_RENDER_STATE);
|
||||
|
||||
// TODO: If shader objects have been created by now, we might have received errors.
|
||||
@@ -675,15 +672,15 @@ void PresentationCommon::CopyToOutput(OutputFlags flags, int uvRotation, float u
|
||||
int pixelHeight = pixelHeight_;
|
||||
|
||||
// These are the output coordinates.
|
||||
FRect frame = GetScreenFrame((float)pixelWidth, (float)pixelHeight);
|
||||
FRect frame = GetScreenFrame(config.bIgnoreScreenInsets, (float)pixelWidth, (float)pixelHeight);
|
||||
// Note: In cardboard mode, we halve the width here to compensate
|
||||
// for splitting the window in half, while still reusing normal centering.
|
||||
if (g_Config.bEnableCardboardVR) {
|
||||
if (config.bEnableCardboardVR) {
|
||||
frame.w /= 2.0;
|
||||
pixelWidth /= 2;
|
||||
}
|
||||
FRect rc;
|
||||
CalculateDisplayOutputRect(&rc, 480.0f, 272.0f, frame, uvRotation);
|
||||
CalculateDisplayOutputRect(config, &rc, 480.0f, 272.0f, frame, uvRotation);
|
||||
|
||||
// To make buffer updates easier, we use one array of verts.
|
||||
int postVertsOffset = (int)sizeof(Vertex) * 4;
|
||||
@@ -927,7 +924,7 @@ void PresentationCommon::CopyToOutput(OutputFlags flags, int uvRotation, float u
|
||||
};
|
||||
|
||||
CardboardSettings cardboardSettings;
|
||||
GetCardboardSettings(&cardboardSettings);
|
||||
GetCardboardSettings(config, &cardboardSettings);
|
||||
if (cardboardSettings.enabled) {
|
||||
// TODO: This could actually support stereo now, with an appropriate shader.
|
||||
|
||||
@@ -953,7 +950,7 @@ void PresentationCommon::CopyToOutput(OutputFlags flags, int uvRotation, float u
|
||||
presentedThisFrame_ = true;
|
||||
}
|
||||
|
||||
void PresentationCommon::CalculateRenderResolution(int *width, int *height, int *scaleFactor, bool *upscaling, bool *ssaa) const {
|
||||
void PresentationCommon::CalculateRenderResolution(const DisplayLayoutConfig &config, int *width, int *height, int *scaleFactor, bool *upscaling, bool *ssaa) const {
|
||||
// Check if postprocessing shader is doing upscaling as it requires native resolution
|
||||
std::vector<const ShaderInfo *> shaderInfo;
|
||||
if (!g_Config.vPostShaderNames.empty()) {
|
||||
@@ -970,7 +967,7 @@ void PresentationCommon::CalculateRenderResolution(int *width, int *height, int
|
||||
int zoom = g_Config.iInternalResolution;
|
||||
if (zoom == 0 || firstSSAAFilterLevel >= 2) {
|
||||
// auto mode, use the longest dimension
|
||||
if (!g_Config.IsPortrait()) {
|
||||
if (!config.InternalRotationIsPortrait()) {
|
||||
zoom = (PSP_CoreParameter().pixelWidth + 479) / 480;
|
||||
} else {
|
||||
zoom = (PSP_CoreParameter().pixelHeight + 479) / 480;
|
||||
|
||||
@@ -48,10 +48,12 @@ struct FRect {
|
||||
};
|
||||
|
||||
struct Bounds; // from geom2d
|
||||
struct DisplayLayoutConfig;
|
||||
|
||||
FRect GetScreenFrame(float pixelWidth, float pixelHeight);
|
||||
FRect GetScreenFrame(bool ignoreInsets, float pixelWidth, float pixelHeight);
|
||||
void SetOverrideScreenFrame(const Bounds *bounds);
|
||||
void CalculateDisplayOutputRect(FRect *rc, float origW, float origH, const FRect &frame, int rotation);
|
||||
struct DisplayLayoutConfig;
|
||||
void CalculateDisplayOutputRect(const DisplayLayoutConfig &config, FRect *rc, float origW, float origH, const FRect &frame, int rotation);
|
||||
|
||||
namespace Draw {
|
||||
class Buffer;
|
||||
@@ -99,9 +101,13 @@ public:
|
||||
return usePostShader_;
|
||||
}
|
||||
|
||||
bool UpdatePostShader();
|
||||
bool UpdatePostShader(const DisplayLayoutConfig &config);
|
||||
|
||||
void BeginFrame() {
|
||||
void BeginFrame(const DisplayLayoutConfig &config) {
|
||||
if (restorePostShader_) {
|
||||
UpdatePostShader(config);
|
||||
restorePostShader_ = false;
|
||||
}
|
||||
presentedThisFrame_ = false;
|
||||
}
|
||||
bool PresentedThisFrame() const {
|
||||
@@ -119,9 +125,9 @@ public:
|
||||
void UpdateUniforms(bool hasVideo);
|
||||
void SourceTexture(Draw::Texture *texture, int bufferWidth, int bufferHeight);
|
||||
void SourceFramebuffer(Draw::Framebuffer *fb, int bufferWidth, int bufferHeight);
|
||||
void CopyToOutput(OutputFlags flags, int uvRotation, float u0, float v0, float u1, float v1);
|
||||
void CopyToOutput(const DisplayLayoutConfig &config, OutputFlags flags, int uvRotation, float u0, float v0, float u1, float v1);
|
||||
|
||||
void CalculateRenderResolution(int *width, int *height, int *scaleFactor, bool *upscaling, bool *ssaa) const;
|
||||
void CalculateRenderResolution(const DisplayLayoutConfig &config, int *width, int *height, int *scaleFactor, bool *upscaling, bool *ssaa) const;
|
||||
|
||||
protected:
|
||||
void CreateDeviceObjects();
|
||||
@@ -135,12 +141,12 @@ protected:
|
||||
Draw::ShaderModule *CompileShaderModule(ShaderStage stage, ShaderLanguage lang, const std::string &src, std::string *errorString) const;
|
||||
Draw::Pipeline *CreatePipeline(std::vector<Draw::ShaderModule *> shaders, bool postShader, const UniformBufferDesc *uniformDesc) const;
|
||||
bool CompilePostShader(const ShaderInfo *shaderInfo, Draw::Pipeline **outPipeline) const;
|
||||
bool BuildPostShader(const ShaderInfo *shaderInfo, const ShaderInfo *next, Draw::Pipeline **outPipeline);
|
||||
bool BuildPostShader(const DisplayLayoutConfig &config, const ShaderInfo *shaderInfo, const ShaderInfo *next, Draw::Pipeline **outPipeline);
|
||||
bool AllocateFramebuffer(int w, int h);
|
||||
|
||||
bool BindSource(int binding, bool bindStereo);
|
||||
|
||||
void GetCardboardSettings(CardboardSettings *cardboardSettings) const;
|
||||
void GetCardboardSettings(const DisplayLayoutConfig &config, CardboardSettings *cardboardSettings) const;
|
||||
void CalculatePostShaderUniforms(int bufferWidth, int bufferHeight, int targetWidth, int targetHeight, const ShaderInfo *shaderInfo, PostShaderUniforms *uniforms) const;
|
||||
|
||||
Draw::DrawContext *draw_;
|
||||
|
||||
@@ -413,7 +413,9 @@ void DrawEngineD3D11::Flush() {
|
||||
// We need correct viewport values in gstate_c already.
|
||||
if (gstate_c.IsDirty(DIRTY_VIEWPORTSCISSOR_STATE)) {
|
||||
ViewportAndScissor vpAndScissor;
|
||||
ConvertViewportAndScissor(framebufferManager_->UseBufferedRendering(),
|
||||
ConvertViewportAndScissor(
|
||||
framebufferManager_->GetDisplayLayoutConfigCopy(),
|
||||
framebufferManager_->UseBufferedRendering(),
|
||||
framebufferManager_->GetRenderWidth(), framebufferManager_->GetRenderHeight(),
|
||||
framebufferManager_->GetTargetBufferWidth(), framebufferManager_->GetTargetBufferHeight(),
|
||||
vpAndScissor);
|
||||
|
||||
@@ -113,15 +113,15 @@ void GPU_D3D11::DeviceRestore(Draw::DrawContext *draw) {
|
||||
GPUCommonHW::DeviceRestore(draw);
|
||||
}
|
||||
|
||||
void GPU_D3D11::BeginHostFrame() {
|
||||
GPUCommonHW::BeginHostFrame();
|
||||
void GPU_D3D11::BeginHostFrame(const DisplayLayoutConfig &config) {
|
||||
GPUCommonHW::BeginHostFrame(config);
|
||||
|
||||
textureCache_->StartFrame();
|
||||
drawEngine_.BeginFrame();
|
||||
|
||||
shaderManager_->DirtyLastShader();
|
||||
|
||||
framebufferManager_->BeginFrame();
|
||||
framebufferManager_->BeginFrame(config);
|
||||
gstate_c.Dirty(DIRTY_PROJTHROUGHMATRIX);
|
||||
|
||||
if (gstate_c.useFlagsChanged) {
|
||||
|
||||
@@ -44,7 +44,7 @@ protected:
|
||||
void FinishDeferred() override;
|
||||
|
||||
private:
|
||||
void BeginHostFrame() override;
|
||||
void BeginHostFrame(const DisplayLayoutConfig &config) override;
|
||||
|
||||
ID3D11Device *device_;
|
||||
ID3D11DeviceContext *context_;
|
||||
|
||||
@@ -325,7 +325,9 @@ void DrawEngineD3D11::ApplyDrawState(int prim) {
|
||||
|
||||
if (gstate_c.IsDirty(DIRTY_VIEWPORTSCISSOR_STATE)) {
|
||||
ViewportAndScissor vpAndScissor;
|
||||
ConvertViewportAndScissor(useBufferedRendering,
|
||||
ConvertViewportAndScissor(
|
||||
framebufferManager_->GetDisplayLayoutConfigCopy(),
|
||||
useBufferedRendering,
|
||||
framebufferManager_->GetRenderWidth(), framebufferManager_->GetRenderHeight(),
|
||||
framebufferManager_->GetTargetBufferWidth(), framebufferManager_->GetTargetBufferHeight(),
|
||||
vpAndScissor);
|
||||
|
||||
@@ -356,7 +356,9 @@ void DrawEngineGLES::Flush() {
|
||||
|
||||
// We need correct viewport values in gstate_c already.
|
||||
if (gstate_c.IsDirty(DIRTY_VIEWPORTSCISSOR_STATE)) {
|
||||
ConvertViewportAndScissor(framebufferManager_->UseBufferedRendering(),
|
||||
ConvertViewportAndScissor(
|
||||
framebufferManager_->GetDisplayLayoutConfigCopy(),
|
||||
framebufferManager_->UseBufferedRendering(),
|
||||
framebufferManager_->GetRenderWidth(), framebufferManager_->GetRenderHeight(),
|
||||
framebufferManager_->GetTargetBufferWidth(), framebufferManager_->GetTargetBufferHeight(),
|
||||
vpAndScissor_);
|
||||
|
||||
@@ -236,8 +236,8 @@ void GPU_GLES::DeviceRestore(Draw::DrawContext *draw) {
|
||||
fragmentTestCache_.DeviceRestore(draw_);
|
||||
}
|
||||
|
||||
void GPU_GLES::BeginHostFrame() {
|
||||
GPUCommonHW::BeginHostFrame();
|
||||
void GPU_GLES::BeginHostFrame(const DisplayLayoutConfig &config) {
|
||||
GPUCommonHW::BeginHostFrame(config);
|
||||
drawEngine_.BeginFrame();
|
||||
|
||||
textureCache_->StartFrame();
|
||||
@@ -253,7 +253,7 @@ void GPU_GLES::BeginHostFrame() {
|
||||
// Not sure if this is really needed.
|
||||
gstate_c.Dirty(DIRTY_ALL_UNIFORMS);
|
||||
|
||||
framebufferManager_->BeginFrame();
|
||||
framebufferManager_->BeginFrame(config);
|
||||
|
||||
fragmentTestCache_.Decimate();
|
||||
if (gstate_c.useFlagsChanged) {
|
||||
|
||||
+1
-1
@@ -44,7 +44,7 @@ public:
|
||||
void DeviceLost() override; // Only happens on Android. Drop all textures and shaders.
|
||||
void DeviceRestore(Draw::DrawContext *draw) override;
|
||||
|
||||
void BeginHostFrame() override;
|
||||
void BeginHostFrame(const DisplayLayoutConfig &config) override;
|
||||
void EndHostFrame() override;
|
||||
|
||||
protected:
|
||||
|
||||
@@ -274,7 +274,9 @@ void DrawEngineGLES::ApplyDrawState(int prim) {
|
||||
}
|
||||
|
||||
if (gstate_c.IsDirty(DIRTY_VIEWPORTSCISSOR_STATE)) {
|
||||
ConvertViewportAndScissor(useBufferedRendering,
|
||||
ConvertViewportAndScissor(
|
||||
framebufferManager_->GetDisplayLayoutConfigCopy(),
|
||||
useBufferedRendering,
|
||||
framebufferManager_->GetRenderWidth(), framebufferManager_->GetRenderHeight(),
|
||||
framebufferManager_->GetTargetBufferWidth(), framebufferManager_->GetTargetBufferHeight(),
|
||||
vpAndScissor_);
|
||||
|
||||
+10
-13
@@ -61,7 +61,7 @@ GPUCommon::GPUCommon(GraphicsContext *gfxCtx, Draw::DrawContext *draw) :
|
||||
ResetMatrices();
|
||||
}
|
||||
|
||||
void GPUCommon::BeginHostFrame() {
|
||||
void GPUCommon::BeginHostFrame(const DisplayLayoutConfig &config) {
|
||||
ReapplyGfxState();
|
||||
|
||||
// TODO: Assume config may have changed - maybe move to resize.
|
||||
@@ -70,9 +70,9 @@ void GPUCommon::BeginHostFrame() {
|
||||
UpdateCmdInfo();
|
||||
|
||||
UpdateMSAALevel(draw_);
|
||||
CheckConfigChanged();
|
||||
CheckConfigChanged(config);
|
||||
CheckDisplayResized();
|
||||
CheckRenderResized();
|
||||
CheckRenderResized(config);
|
||||
}
|
||||
|
||||
void GPUCommon::EndHostFrame() {
|
||||
@@ -159,7 +159,7 @@ void GPUCommon::NotifyConfigChanged() {
|
||||
configChanged_ = true;
|
||||
}
|
||||
|
||||
void GPUCommon::NotifyRenderResized() {
|
||||
void GPUCommon::NotifyRenderResized(const DisplayLayoutConfig &config) {
|
||||
renderResized_ = true;
|
||||
}
|
||||
|
||||
@@ -725,15 +725,6 @@ inline void GPUCommon::UpdateState(GPURunState state) {
|
||||
downcount = 0;
|
||||
}
|
||||
|
||||
int GPUCommon::GetNextListIndex() {
|
||||
auto iter = dlQueue.begin();
|
||||
if (iter != dlQueue.end()) {
|
||||
return *iter;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
// This is now called when coreState == CORE_RUNNING_GE, in addition to from the various sceGe commands.
|
||||
DLResult GPUCommon::ProcessDLQueue() {
|
||||
if (!resumingFromDebugBreak_) {
|
||||
@@ -749,6 +740,12 @@ DLResult GPUCommon::ProcessDLQueue() {
|
||||
|
||||
TimeCollector collectStat(&gpuStats.msProcessingDisplayLists, coreCollectDebugStats);
|
||||
|
||||
auto GetNextListIndex = [&]() -> int {
|
||||
if (dlQueue.empty())
|
||||
return -1;
|
||||
return dlQueue.front();
|
||||
};
|
||||
|
||||
for (int listIndex = GetNextListIndex(); listIndex != -1; listIndex = GetNextListIndex()) {
|
||||
DisplayList &list = dls[listIndex];
|
||||
|
||||
|
||||
+7
-6
@@ -199,6 +199,8 @@ inline bool IsTrianglePrim(GEPrimitiveType prim) {
|
||||
return prim > GE_PRIM_LINE_STRIP && prim != GE_PRIM_RECTANGLES;
|
||||
}
|
||||
|
||||
struct DisplayLayoutConfig;
|
||||
|
||||
class GPUCommon : public GPUDebugInterface {
|
||||
public:
|
||||
// The constructor might run on the loader thread.
|
||||
@@ -225,7 +227,7 @@ public:
|
||||
}
|
||||
virtual void Reinitialize();
|
||||
|
||||
virtual void BeginHostFrame();
|
||||
virtual void BeginHostFrame(const DisplayLayoutConfig &config);
|
||||
virtual void EndHostFrame();
|
||||
|
||||
void InterruptStart(int listid);
|
||||
@@ -236,10 +238,10 @@ public:
|
||||
}
|
||||
|
||||
virtual void CheckDisplayResized() = 0;
|
||||
virtual void CheckConfigChanged() = 0;
|
||||
virtual void CheckConfigChanged(const DisplayLayoutConfig &config) = 0;
|
||||
|
||||
virtual void NotifyDisplayResized();
|
||||
virtual void NotifyRenderResized();
|
||||
virtual void NotifyRenderResized(const DisplayLayoutConfig &config);
|
||||
virtual void NotifyConfigChanged();
|
||||
|
||||
void DumpNextFrame();
|
||||
@@ -274,7 +276,7 @@ public:
|
||||
uint32_t GetAddrTranslation() override;
|
||||
|
||||
virtual void SetDisplayFramebuffer(u32 framebuf, u32 stride, GEBufferFormat format) = 0;
|
||||
virtual void CopyDisplayToOutput(bool reallyDirty) = 0;
|
||||
virtual void CopyDisplayToOutput(const DisplayLayoutConfig &config, bool reallyDirty) = 0;
|
||||
virtual bool PresentedThisFrame() const = 0;
|
||||
|
||||
// Invalidate any cached content sourced from the specified range.
|
||||
@@ -422,7 +424,7 @@ protected:
|
||||
|
||||
virtual void ClearCacheNextFrame() {}
|
||||
|
||||
virtual void CheckRenderResized() {}
|
||||
virtual void CheckRenderResized(const DisplayLayoutConfig &config) {}
|
||||
|
||||
void SetDrawType(DrawType type, GEPrimitiveType prim) {
|
||||
if (type != lastDraw_) {
|
||||
@@ -566,5 +568,4 @@ private:
|
||||
void DoExecuteCall(u32 target);
|
||||
void PopDLQueue();
|
||||
void CheckDrawSync();
|
||||
int GetNextListIndex();
|
||||
};
|
||||
|
||||
+8
-8
@@ -405,7 +405,7 @@ GPUCommonHW::~GPUCommonHW() {
|
||||
|
||||
// Called once per frame. Might also get called during the pause screen
|
||||
// if "transparent".
|
||||
void GPUCommonHW::CheckConfigChanged() {
|
||||
void GPUCommonHW::CheckConfigChanged(const DisplayLayoutConfig &config) {
|
||||
if (configChanged_) {
|
||||
ClearCacheNextFrame();
|
||||
gstate_c.SetUseFlags(CheckGPUFeatures());
|
||||
@@ -418,7 +418,7 @@ void GPUCommonHW::CheckConfigChanged() {
|
||||
|
||||
// Check needed when running tests.
|
||||
if (framebufferManager_) {
|
||||
framebufferManager_->CheckPostShaders();
|
||||
framebufferManager_->CheckPostShaders(config);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -429,9 +429,9 @@ void GPUCommonHW::CheckDisplayResized() {
|
||||
}
|
||||
}
|
||||
|
||||
void GPUCommonHW::CheckRenderResized() {
|
||||
void GPUCommonHW::CheckRenderResized(const DisplayLayoutConfig &config) {
|
||||
if (renderResized_) {
|
||||
framebufferManager_->NotifyRenderResized(msaaLevel_);
|
||||
framebufferManager_->NotifyRenderResized(config, msaaLevel_);
|
||||
renderResized_ = false;
|
||||
}
|
||||
}
|
||||
@@ -503,8 +503,8 @@ void GPUCommonHW::UpdateCmdInfo() {
|
||||
}
|
||||
}
|
||||
|
||||
void GPUCommonHW::BeginHostFrame() {
|
||||
GPUCommon::BeginHostFrame();
|
||||
void GPUCommonHW::BeginHostFrame(const DisplayLayoutConfig &config) {
|
||||
GPUCommon::BeginHostFrame(config);
|
||||
if (drawEngineCommon_->EverUsedExactEqualDepth() && !sawExactEqualDepth_) {
|
||||
sawExactEqualDepth_ = true;
|
||||
gstate_c.SetUseFlags(CheckGPUFeatures());
|
||||
@@ -530,7 +530,7 @@ void GPUCommonHW::PreExecuteOp(u32 op, u32 diff) {
|
||||
CheckFlushOp(op >> 24, diff);
|
||||
}
|
||||
|
||||
void GPUCommonHW::CopyDisplayToOutput(bool reallyDirty) {
|
||||
void GPUCommonHW::CopyDisplayToOutput(const DisplayLayoutConfig &config, bool reallyDirty) {
|
||||
drawEngineCommon_->FlushQueuedDepth();
|
||||
// Flush anything left over.
|
||||
drawEngineCommon_->Flush();
|
||||
@@ -538,7 +538,7 @@ void GPUCommonHW::CopyDisplayToOutput(bool reallyDirty) {
|
||||
shaderManager_->DirtyLastShader();
|
||||
|
||||
// after this, render pass is active.
|
||||
framebufferManager_->CopyDisplayToOutput(reallyDirty);
|
||||
framebufferManager_->CopyDisplayToOutput(config, reallyDirty);
|
||||
|
||||
gstate_c.Dirty(DIRTY_TEXTURE_IMAGE);
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user