Merge remote-tracking branch 'origin/12.0-development' into vulkan

This commit is contained in:
niki
2022-06-11 01:22:29 +02:00
55 changed files with 2101 additions and 541 deletions
+194 -134
View File
@@ -19,6 +19,7 @@
**/
#include "android.h"
#include "Object.h"
#ifdef LOVE_ANDROID
@@ -45,13 +46,11 @@ namespace android
void setImmersive(bool immersive_active)
{
JNIEnv *env = (JNIEnv*) SDL_AndroidGetJNIEnv();
jobject activity = (jobject) SDL_AndroidGetActivity();
jclass clazz = env->GetObjectClass(activity);
jclass clazz(env->GetObjectClass(activity));
jmethodID method_id = env->GetMethodID(clazz, "setImmersiveMode", "(Z)V");
env->CallVoidMethod(activity, method_id, immersive_active);
static jmethodID setImmersiveMethod = env->GetMethodID(clazz, "setImmersiveMode", "(Z)V");
env->CallVoidMethod(activity, setImmersiveMethod, immersive_active);
env->DeleteLocalRef(activity);
env->DeleteLocalRef(clazz);
@@ -60,18 +59,16 @@ void setImmersive(bool immersive_active)
bool getImmersive()
{
JNIEnv *env = (JNIEnv*) SDL_AndroidGetJNIEnv();
jobject activity = (jobject) SDL_AndroidGetActivity();
jclass clazz = env->GetObjectClass(activity);
jclass clazz(env->GetObjectClass(activity));
jmethodID method_id = env->GetMethodID(clazz, "getImmersiveMode", "()Z");
jboolean immersive_active = env->CallBooleanMethod(activity, method_id);
static jmethodID getImmersiveMethod = env->GetMethodID(clazz, "getImmersiveMode", "()Z");
jboolean immersiveActive = env->CallBooleanMethod(activity, getImmersiveMethod);
env->DeleteLocalRef(activity);
env->DeleteLocalRef(clazz);
return immersive_active;
return immersiveActive;
}
double getScreenScale()
@@ -81,16 +78,13 @@ double getScreenScale()
if (result == -1.)
{
JNIEnv *env = (JNIEnv*) SDL_AndroidGetJNIEnv();
jclass activity = env->FindClass("org/love2d/android/GameActivity");
jobject activity = (jobject) SDL_AndroidGetActivity();
jclass clazz = env->GetObjectClass(activity);
jmethodID getMetrics = env->GetStaticMethodID(activity, "getMetrics", "()Landroid/util/DisplayMetrics;");
jobject metrics = env->CallStaticObjectMethod(activity, getMetrics);
jclass metricsClass = env->GetObjectClass(metrics);
jmethodID getDPIMethod = env->GetMethodID(clazz, "getDPIScale", "()F");
result = (double) env->CallFloatMethod(activity, getDPIMethod);
result = env->GetFloatField(metrics, env->GetFieldID(metricsClass, "density", "F"));
env->DeleteLocalRef(metricsClass);
env->DeleteLocalRef(metrics);
env->DeleteLocalRef(clazz);
env->DeleteLocalRef(activity);
}
@@ -101,8 +95,10 @@ bool getSafeArea(int &top, int &left, int &bottom, int &right)
{
JNIEnv *env = (JNIEnv*) SDL_AndroidGetJNIEnv();
jobject activity = (jobject) SDL_AndroidGetActivity();
jclass clazz(env->GetObjectClass(activity));
jmethodID methodID = env->GetMethodID(clazz, "initializeSafeArea", "()Z");
jclass clazz = env->GetObjectClass(activity);
static jmethodID methodID = env->GetMethodID(clazz, "getSafeArea", "()Z");
bool hasSafeArea = false;
if (methodID == nullptr)
@@ -122,64 +118,33 @@ bool getSafeArea(int &top, int &left, int &bottom, int &right)
return hasSafeArea;
}
const char *getSelectedGameFile()
{
static const char *path = NULL;
if (path)
{
delete path;
path = NULL;
}
JNIEnv *env = (JNIEnv*) SDL_AndroidGetJNIEnv();
jclass activity = env->FindClass("org/love2d/android/GameActivity");
jmethodID getGamePath = env->GetStaticMethodID(activity, "getGamePath", "()Ljava/lang/String;");
jstring gamePath = (jstring) env->CallStaticObjectMethod(activity, getGamePath);
const char *utf = env->GetStringUTFChars(gamePath, 0);
if (utf)
{
path = SDL_strdup(utf);
env->ReleaseStringUTFChars(gamePath, utf);
}
env->DeleteLocalRef(gamePath);
env->DeleteLocalRef(activity);
return path;
}
bool openURL(const std::string &url)
{
JNIEnv *env = (JNIEnv*) SDL_AndroidGetJNIEnv();
jclass activity = env->FindClass("org/love2d/android/GameActivity");
jobject activity = (jobject) SDL_AndroidGetActivity();
jclass clazz = env->GetObjectClass(activity);
jmethodID openURL = env->GetStaticMethodID(activity, "openURLFromLOVE", "(Ljava/lang/String;)Z");
static jmethodID openURL = env->GetMethodID(clazz, "openURLFromLOVE", "(Ljava/lang/String;)Z");
if (openURL == nullptr)
{
env->ExceptionClear();
openURL = env->GetStaticMethodID(activity, "openURL", "(Ljava/lang/String;)Z");
}
jstring jstringURL = env->NewStringUTF(url.c_str());
jboolean result = env->CallBooleanMethod(clazz, openURL, jstringURL);
jstring url_jstring = (jstring) env->NewStringUTF(url.c_str());
jboolean result = env->CallStaticBooleanMethod(activity, openURL, url_jstring);
env->DeleteLocalRef(url_jstring);
env->DeleteLocalRef(jstringURL);
env->DeleteLocalRef(clazz);
env->DeleteLocalRef(activity);
return result;
return (bool) result;
}
void vibrate(double seconds)
{
JNIEnv *env = (JNIEnv*) SDL_AndroidGetJNIEnv();
jclass activity = env->FindClass("org/love2d/android/GameActivity");
jobject activity = (jobject) SDL_AndroidGetActivity();
jclass clazz = env->GetObjectClass(activity);
jmethodID vibrate_method = env->GetStaticMethodID(activity, "vibrate", "(D)V");
env->CallStaticVoidMethod(activity, vibrate_method, seconds);
static jmethodID vibrateMethod = env->GetMethodID(clazz, "vibrate", "(D)V");
env->CallVoidMethod(activity, vibrateMethod, seconds);
env->DeleteLocalRef(clazz);
env->DeleteLocalRef(activity);
}
@@ -192,40 +157,9 @@ void freeGameArchiveMemory(void *ptr)
delete[] game_love_data;
}
bool loadGameArchiveToMemory(const char* filename, char **ptr, size_t *size)
{
SDL_RWops *asset_game_file = SDL_RWFromFile(filename, "rb");
if (!asset_game_file) {
SDL_Log("Could not find %s", filename);
return false;
}
Sint64 file_size = asset_game_file->size(asset_game_file);
if (file_size <= 0) {
SDL_Log("Could not load game from %s. File has invalid file size: %d.", filename, (int) file_size);
return false;
}
*ptr = new char[file_size];
if (!*ptr) {
SDL_Log("Could not allocate memory for in-memory game archive");
return false;
}
size_t bytes_copied = asset_game_file->read(asset_game_file, (void*) *ptr, sizeof(char), (size_t) file_size);
if (bytes_copied != file_size) {
SDL_Log("Incomplete copy of in-memory game archive!");
delete[] *ptr;
return false;
}
*size = (size_t) file_size;
return true;
}
bool directoryExists(const char *path)
{
struct stat s;
struct stat s {};
int err = stat(path, &s);
if (err == -1)
{
@@ -284,9 +218,9 @@ bool hasRecordingPermission()
{
JNIEnv *env = (JNIEnv*) SDL_AndroidGetJNIEnv();
jobject activity = (jobject) SDL_AndroidGetActivity();
jclass clazz = env->GetObjectClass(activity);
jclass clazz(env->GetObjectClass(activity));
jmethodID methodID = env->GetMethodID(clazz, "hasRecordAudioPermission", "()Z");
static jmethodID methodID = env->GetMethodID(clazz, "hasRecordAudioPermission", "()Z");
jboolean result = false;
if (methodID == nullptr)
@@ -755,21 +689,11 @@ void deinitializeVirtualArchive()
bool checkFusedGame(void **physfsIO_Out)
{
// TODO: Reorder the loading in 12.0
PHYSFS_Io *&io = *(PHYSFS_Io **) physfsIO_Out;
AAssetManager *assetManager = getAssetManager();
// Prefer game.love inside assets/ folder
AAsset *asset = AAssetManager_open(assetManager, "game.love", AASSET_MODE_RANDOM);
if (asset)
{
io = aasset::io::fromAAsset(assetManager, "game.love", asset);
return true;
}
// If there's no game.love inside assets/ try main.lua
asset = AAssetManager_open(assetManager, "main.lua", AASSET_MODE_STREAMING);
// Prefer main.lua inside assets/ folder
AAsset *asset = AAssetManager_open(assetManager, "main.lua", AASSET_MODE_STREAMING);
if (asset)
{
AAsset_close(asset);
@@ -777,6 +701,14 @@ bool checkFusedGame(void **physfsIO_Out)
return true;
}
// If there's no main.lua inside assets/ try game.love
asset = AAssetManager_open(assetManager, "game.love", AASSET_MODE_RANDOM);
if (asset)
{
io = aasset::io::fromAAsset(assetManager, "game.love", asset);
return true;
}
// Not found
return false;
}
@@ -784,43 +716,171 @@ bool checkFusedGame(void **physfsIO_Out)
const char *getCRequirePath()
{
static bool initialized = false;
static const char *path = nullptr;
static std::string path;
if (!initialized)
{
JNIEnv *env = (JNIEnv*) SDL_AndroidGetJNIEnv();
jobject activity = (jobject) SDL_AndroidGetActivity();
jclass clazz = env->GetObjectClass(activity);
jclass clazz(env->GetObjectClass(activity));
jmethodID method_id = env->GetMethodID(clazz, "getCRequirePath", "()Ljava/lang/String;");
static jmethodID getCRequireMethod = env->GetMethodID(clazz, "getCRequirePath", "()Ljava/lang/String;");
path = "";
initialized = true;
if (method_id)
jstring cpath = (jstring) env->CallObjectMethod(activity, getCRequireMethod);
const char *utf = env->GetStringUTFChars(cpath, nullptr);
if (utf)
{
jstring cpath = (jstring) env->CallObjectMethod(activity, method_id);
const char *utf = env->GetStringUTFChars(cpath, nullptr);
if (utf)
{
path = SDL_strdup(utf);
env->ReleaseStringUTFChars(cpath, utf);
}
env->DeleteLocalRef(cpath);
}
else
{
// NoSuchMethodException is thrown in case methodID is null
env->ExceptionClear();
return "";
path = utf;
env->ReleaseStringUTFChars(cpath, utf);
}
env->DeleteLocalRef(cpath);
env->DeleteLocalRef(activity);
env->DeleteLocalRef(clazz);
}
return path;
return path.c_str();
}
int getFDFromContentProtocol(const char *path)
{
int fd = -1;
if (strstr(path, "content://") == path)
{
JNIEnv *env = (JNIEnv*) SDL_AndroidGetJNIEnv();
jobject activity = (jobject) SDL_AndroidGetActivity();
jclass clazz = env->GetObjectClass(activity);
static jmethodID converter = env->GetMethodID(clazz, "convertToFileDescriptor", "(Ljava/lang/String;)I");
jstring uri = env->NewStringUTF(path);
fd = (int) env->CallIntMethod(activity, converter, uri);
env->DeleteLocalRef(uri);
env->DeleteLocalRef(clazz);
env->DeleteLocalRef(activity);
}
return fd;
}
int getFDFromLoveProtocol(const char *path)
{
constexpr const char PROTOCOL[] = "love2d://fd/";
constexpr size_t PROTOCOL_LEN = sizeof(PROTOCOL) - 1;
if (*path == '/')
path++;
if (memcmp(path, PROTOCOL, PROTOCOL_LEN) == 0)
{
try
{
return std::stoi(path + PROTOCOL_LEN, nullptr, 10);
}
catch (std::logic_error &)
{ }
}
return -1;
}
class FileDescriptorTracker: public love::Object
{
public:
explicit FileDescriptorTracker(int fd): Object(), fd(fd) {}
~FileDescriptorTracker() override { close(fd); }
int getFd() const { return fd; }
private:
int fd;
};
struct FileDescriptorIO
{
FileDescriptorTracker *fd;
off64_t size;
off64_t offset;
};
void *getIOFromFD(int fd)
{
if (fd == -1)
return nullptr;
// Create file descriptor IO structure
FileDescriptorIO *fdio = new FileDescriptorIO();
fdio->size = lseek64(fd, 0, SEEK_END);
fdio->offset = 0;
lseek64(fd, 0, SEEK_SET);
if (fdio->size == -1)
{
// Cannot get size
delete fdio;
return nullptr;
}
fdio->fd = new FileDescriptorTracker(fd);
PHYSFS_Io *io = new PHYSFS_Io();
io->version = 0;
io->opaque = fdio;
io->read = [](PHYSFS_Io *io, void *buf, PHYSFS_uint64 size)
{
FileDescriptorIO *fdio = (FileDescriptorIO *) io->opaque;
ssize_t ret = pread64(fdio->fd->getFd(), buf, (size_t) size, fdio->offset);
if (ret == -1)
PHYSFS_setErrorCode(PHYSFS_ERR_OTHER_ERROR);
else
fdio->offset = std::min(fdio->offset + (off64_t) ret, fdio->size);
return (PHYSFS_sint64) ret;
};
io->write = nullptr;
io->seek = [](PHYSFS_Io *io, PHYSFS_uint64 offset)
{
FileDescriptorIO *fdio = (FileDescriptorIO *) io->opaque;
fdio->offset = std::min(std::max<off64_t>((off64_t) offset, 0), fdio->size);
// Always success
return 1;
};
io->tell = [](PHYSFS_Io *io)
{
FileDescriptorIO *fdio = (FileDescriptorIO *) io->opaque;
return (PHYSFS_sint64) fdio->offset;
};
io->length = [](PHYSFS_Io *io)
{
FileDescriptorIO *fdio = (FileDescriptorIO *) io->opaque;
return (PHYSFS_sint64) fdio->size;
};
io->duplicate = [](PHYSFS_Io *io)
{
FileDescriptorIO *fdio = (FileDescriptorIO *) io->opaque;
FileDescriptorIO *fdio2 = new FileDescriptorIO();
PHYSFS_Io *io2 = new PHYSFS_Io();
fdio->fd->retain();
// Copy data
*fdio2 = *fdio;
*io2 = *io;
io2->opaque = fdio2;
return io2;
};
io->flush = nullptr;
io->destroy = [](PHYSFS_Io *io)
{
FileDescriptorIO *fdio = (FileDescriptorIO *) io->opaque;
fdio->fd->release();
delete fdio;
delete io;
};
return io;
}
} // android
+21 -7
View File
@@ -50,11 +50,6 @@ double getScreenScale();
**/
bool getSafeArea(int &top, int &left, int &bottom, int &right);
/**
* Gets the selected love file in the device filesystem.
**/
const char *getSelectedGameFile();
bool openURL(const std::string &url);
void vibrate(double seconds);
@@ -64,8 +59,6 @@ void vibrate(double seconds);
*/
void freeGameArchiveMemory(void *ptr);
bool loadGameArchiveToMemory(const char *filename, char **ptr, size_t *size);
bool directoryExists(const char *path);
bool mkdir(const char *path);
@@ -103,6 +96,27 @@ bool checkFusedGame(void **physfsIO_Out);
const char *getCRequirePath();
/**
* Convert "content://" to file descriptor.
* @param path Path with content:// URI
* @return File descriptor if successful, -1 on failure.
*/
int getFDFromContentProtocol(const char *path);
/**
* Attempt to parse "(/)love2d://fd/<fd>" from path.
* @param path Potentially special path.
* @return File descriptor passed if successful, -1 if path is not valid.
*/
int getFDFromLoveProtocol(const char *path);
/**
* Create PHYSFS_Io from file descriptor.
* @param fd File descriptor
* @return PHYSFS_Io casted to void*.
*/
void *getIOFromFD(int fd);
} // android
} // love