Merge branch 'master' into master

This commit is contained in:
bilginGamer
2025-11-13 23:07:46 +04:00
committed by GitHub
244 changed files with 6261 additions and 3616 deletions
+2 -2
View File
@@ -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 }}
+13 -7
View File
@@ -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/
+1 -1
View File
@@ -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/
+2 -2
View File
@@ -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
+1 -1
View File
@@ -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/
+1 -1
View File
@@ -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
View File
@@ -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
+1
View File
@@ -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" />
+3
View File
@@ -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" />
+5 -85
View File
@@ -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);
}
}
+2 -16
View File
@@ -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();
+4
View File
@@ -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
+20 -20
View File
@@ -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) {
+7
View File
@@ -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
+7 -7
View File
@@ -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;
+7 -1
View File
@@ -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);
+42
View File
@@ -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;
}
+82 -3
View File
@@ -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 "";
}
+18 -18
View File
@@ -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);
+40 -40
View File
@@ -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;
+6 -3
View File
@@ -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
+2 -3
View File
@@ -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
+72 -66
View File
@@ -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;
}
+47 -25
View File
@@ -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
+2 -3
View File
@@ -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
+45 -40
View File
@@ -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();
}
+6 -3
View File
@@ -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
+95 -68
View File
@@ -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();
}
+5 -5
View File
@@ -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
+57 -45
View File
@@ -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);
+3 -4
View File
@@ -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
+15
View File
@@ -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) {
+7 -1
View File
@@ -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);
+1 -1
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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();
}
+60 -8
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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));
+9 -5
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
File diff suppressed because it is too large Load Diff
+133 -86
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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,
+9 -8
View File
@@ -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:
+6 -3
View File
@@ -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_;
};
+4 -2
View File
@@ -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();
+1 -1
View File
@@ -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);
}
}
+6 -7
View File
@@ -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")
+1 -1
View File
@@ -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
View File
@@ -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.
+8 -1
View File
@@ -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
+4
View File
@@ -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);
+2 -2
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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);
}
+8 -2
View File
@@ -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
View File
@@ -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;
+5
View File
@@ -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
View File
@@ -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
}
}
+5
View File
@@ -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>
+5 -1
View File
@@ -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;
+5 -1
View File
@@ -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;
}
+1
View File
@@ -16,6 +16,7 @@
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
#include "ppsspp_config.h"
#include <cstdlib>
#include <mutex>
-2
View File
@@ -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
View File
@@ -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
View File
@@ -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);
+1 -2
View File
@@ -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
View File
@@ -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
View File
@@ -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;
}
+1 -1
View File
@@ -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;
+31 -26
View File
@@ -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;
+16 -6
View File
@@ -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.
+3 -3
View File
@@ -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;
+3 -1
View File
@@ -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.
+2 -2
View File
@@ -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;
+30 -33
View File
@@ -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;
+14 -8
View File
@@ -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_;
+3 -1
View File
@@ -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);
+3 -3
View File
@@ -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) {
+1 -1
View File
@@ -44,7 +44,7 @@ protected:
void FinishDeferred() override;
private:
void BeginHostFrame() override;
void BeginHostFrame(const DisplayLayoutConfig &config) override;
ID3D11Device *device_;
ID3D11DeviceContext *context_;
+3 -1
View File
@@ -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);
+3 -1
View File
@@ -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_);
+3 -3
View File
@@ -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
View File
@@ -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:
+3 -1
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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