mirror of
https://github.com/love2d/love.git
synced 2026-08-16 00:02:12 +02:00
Merge remote-tracking branch 'origin/12.0-development' into vulkan
This commit is contained in:
+194
-134
@@ -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
@@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user