mirror of
https://github.com/SFML/SFML.git
synced 2024-11-28 22:31:09 +08:00
[Android] Wrote a native activity acting as a bootstrap
A current limitation prevents one library from depending on shared libraries. As we have legal issues here (LGPL wants us to use shared libs of OpenAL-Soft and libsndfile), we're forced to use this homemade native activity which will manually load our shared libraries.
This commit is contained in:
parent
60894d1c1a
commit
cc0d982f72
@ -88,6 +88,11 @@ if(ANDROID)
|
||||
|
||||
# we install libs in a subdirectory named after the ABI (lib/mips/*.so)
|
||||
set(LIB_SUFFIX "/${ANDROID_ABI}")
|
||||
|
||||
# this is a workaround to compile sfml-activity without stlport_shared as dependency
|
||||
# we save the original compilation command line to restore it later in Macro.cmake
|
||||
set(CMAKE_CXX_CREATE_SHARED_LIBRARY_WITH_STLPORT ${CMAKE_CXX_CREATE_SHARED_LIBRARY})
|
||||
set(CMAKE_CXX_CREATE_SHARED_LIBRARY_WITHOUT_STLPORT "<CMAKE_CXX_COMPILER> <CMAKE_SHARED_LIBRARY_CXX_FLAGS> <LANGUAGE_COMPILE_FLAGS> <LINK_FLAGS> <CMAKE_SHARED_LIBRARY_CREATE_CXX_FLAGS> <SONAME_FLAG><TARGET_SONAME> -o <TARGET> <OBJECTS> <LINK_LIBRARIES>")
|
||||
endif()
|
||||
|
||||
# define SFML_STATIC if the build type is not set to 'shared'
|
||||
|
@ -97,6 +97,18 @@ macro(sfml_add_library target)
|
||||
set_target_properties(${target} PROPERTIES XCODE_ATTRIBUTE_CLANG_ENABLE_OBJC_ARC YES)
|
||||
endif()
|
||||
|
||||
# sfml-activity library is our bootstrap activity and must not depend on stlport_shared
|
||||
# (otherwise Android will fail to load it)
|
||||
if (ANDROID)
|
||||
if (${target} MATCHES "sfml-activity")
|
||||
set_target_properties(${target} PROPERTIES COMPILE_FLAGS -fpermissive)
|
||||
set_target_properties(${target} PROPERTIES LINK_FLAGS "-landroid -llog")
|
||||
set(CMAKE_CXX_CREATE_SHARED_LIBRARY ${CMAKE_CXX_CREATE_SHARED_LIBRARY_WITHOUT_STLPORT})
|
||||
else()
|
||||
set(CMAKE_CXX_CREATE_SHARED_LIBRARY ${CMAKE_CXX_CREATE_SHARED_LIBRARY_WITH_STLPORT})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# link the target to its external dependencies
|
||||
if(THIS_EXTERNAL_LIBS)
|
||||
target_link_libraries(${target} ${THIS_EXTERNAL_LIBS})
|
||||
|
@ -16,32 +16,24 @@ include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := ogg
|
||||
LOCAL_SRC_FILES := lib/$(TARGET_ARCH_ABI)/libogg.so
|
||||
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/include
|
||||
TARGET_ARCH_ABI := armeabi armeabi-v7a x86
|
||||
|
||||
include $(PREBUILT_SHARED_LIBRARY)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := flac
|
||||
LOCAL_SRC_FILES := lib/$(TARGET_ARCH_ABI)/libFLAC.so
|
||||
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/include
|
||||
TARGET_ARCH_ABI := armeabi armeabi-v7a x86
|
||||
|
||||
include $(PREBUILT_SHARED_LIBRARY)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := vorbis
|
||||
LOCAL_SRC_FILES := lib/$(TARGET_ARCH_ABI)/libvorbis.so
|
||||
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/include
|
||||
TARGET_ARCH_ABI := armeabi armeabi-v7a x86
|
||||
|
||||
include $(PREBUILT_SHARED_LIBRARY)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := vorbisenc
|
||||
LOCAL_SRC_FILES := lib/$(TARGET_ARCH_ABI)/libvorbisenc.so
|
||||
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/include
|
||||
TARGET_ARCH_ABI := armeabi armeabi-v7a x86
|
||||
|
||||
include $(PREBUILT_SHARED_LIBRARY)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
@ -49,8 +41,6 @@ LOCAL_MODULE := sndfile
|
||||
LOCAL_SRC_FILES := lib/$(TARGET_ARCH_ABI)/libsndfile.so
|
||||
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/include
|
||||
LOCAL_SHARED_LIBRARIES := ogg flac vorbis vorbisenc
|
||||
TARGET_ARCH_ABI := armeabi armeabi-v7a x86
|
||||
|
||||
include $(PREBUILT_SHARED_LIBRARY)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
@ -58,6 +48,4 @@ LOCAL_MODULE := openal
|
||||
LOCAL_SRC_FILES := lib/$(TARGET_ARCH_ABI)/libopenal.so
|
||||
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/include
|
||||
LOCAL_SHARED_LIBRARIES := ogg flac vorbis vorbisenc
|
||||
TARGET_ARCH_ABI := armeabi armeabi-v7a x86
|
||||
|
||||
include $(PREBUILT_SHARED_LIBRARY)
|
||||
|
@ -55,4 +55,11 @@ LOCAL_SHARED_LIBRARIES := sfml-system sfml-window
|
||||
|
||||
include $(PREBUILT_STATIC_LIBRARY)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := sfml-activity
|
||||
LOCAL_SRC_FILES := lib/$(TARGET_ARCH_ABI)/libsfml-activity.so
|
||||
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/include
|
||||
|
||||
include $(PREBUILT_SHARED_LIBRARY)
|
||||
|
||||
$(call import-module,sfml/extlibs)
|
||||
|
@ -28,3 +28,10 @@ set_target_properties(sfml-main PROPERTIES FOLDER "SFML")
|
||||
|
||||
# setup the install rule
|
||||
install(TARGETS sfml-main ARCHIVE DESTINATION lib${LIB_SUFFIX} COMPONENT devel)
|
||||
|
||||
# because of a current limitation on Android (which prevents one library
|
||||
# from depending on shared libraries), we need a boostrap activity which
|
||||
# will load our shared libraries manually
|
||||
if(ANDROID)
|
||||
sfml_add_library(sfml-activity SOURCES ${PROJECT_SOURCE_DIR}/src/SFML/Main/SFMLActivity.cpp)
|
||||
endif()
|
||||
|
166
src/SFML/Main/SFMLActivity.cpp
Normal file
166
src/SFML/Main/SFMLActivity.cpp
Normal file
@ -0,0 +1,166 @@
|
||||
////////////////////////////////////////////////////////////
|
||||
//
|
||||
// SFML - Simple and Fast Multimedia Library
|
||||
// Copyright (C) 2013 Jonathan De Wachter (dewachter.jonathan@gmail.com)
|
||||
//
|
||||
// This software is provided 'as-is', without any express or implied warranty.
|
||||
// In no event will the authors be held liable for any damages arising from the use of this software.
|
||||
//
|
||||
// Permission is granted to anyone to use this software for any purpose,
|
||||
// including commercial applications, and to alter it and redistribute it freely,
|
||||
// subject to the following restrictions:
|
||||
//
|
||||
// 1. The origin of this software must not be misrepresented;
|
||||
// you must not claim that you wrote the original software.
|
||||
// If you use this software in a product, an acknowledgment
|
||||
// in the product documentation would be appreciated but is not required.
|
||||
//
|
||||
// 2. Altered source versions must be plainly marked as such,
|
||||
// and must not be misrepresented as being the original software.
|
||||
//
|
||||
// 3. This notice may not be removed or altered from any source distribution.
|
||||
//
|
||||
////////////////////////////////////////////////////////////
|
||||
|
||||
#include <android/native_activity.h>
|
||||
#include <android/log.h>
|
||||
#include <dlfcn.h>
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <jni.h>
|
||||
|
||||
#define LOGE(...) ((void)__android_log_print(ANDROID_LOG_INFO, "sfml-activity", __VA_ARGS__))
|
||||
|
||||
void* loadLibrary(const char* libraryName, JNIEnv* lJNIEnv, jobject& ObjectActivityInfo)
|
||||
{
|
||||
// Find out the absolute path of the library
|
||||
jclass ClassActivityInfo = lJNIEnv->FindClass("android/content/pm/ActivityInfo");
|
||||
jfieldID FieldApplicationInfo = lJNIEnv->GetFieldID(ClassActivityInfo, "applicationInfo", "Landroid/content/pm/ApplicationInfo;");
|
||||
jobject ObjectApplicationInfo = lJNIEnv->GetObjectField(ObjectActivityInfo, FieldApplicationInfo);
|
||||
|
||||
jclass ClassApplicationInfo = lJNIEnv->FindClass("android/content/pm/ApplicationInfo");
|
||||
jfieldID FieldNativeLibraryDir = lJNIEnv->GetFieldID(ClassApplicationInfo, "nativeLibraryDir", "Ljava/lang/String;");
|
||||
|
||||
jobject ObjectDirPath = lJNIEnv->GetObjectField(ObjectApplicationInfo, FieldNativeLibraryDir);
|
||||
|
||||
jclass ClassSystem = lJNIEnv->FindClass("java/lang/System");
|
||||
jmethodID StaticMethodMapLibraryName = lJNIEnv->GetStaticMethodID(ClassSystem, "mapLibraryName", "(Ljava/lang/String;)Ljava/lang/String;");
|
||||
|
||||
jstring LibNameObject = lJNIEnv->NewStringUTF(libraryName);
|
||||
jobject ObjectName = lJNIEnv->CallStaticObjectMethod(ClassSystem, StaticMethodMapLibraryName, LibNameObject);
|
||||
|
||||
jclass ClassFile = lJNIEnv->FindClass("java/io/File");
|
||||
jmethodID FileConstructor = lJNIEnv->GetMethodID(ClassFile, "<init>", "(Ljava/lang/String;Ljava/lang/String;)V");
|
||||
jobject ObjectFile = lJNIEnv->NewObject(ClassFile, FileConstructor, ObjectDirPath, ObjectName);
|
||||
|
||||
// Get the library absolute path and convert it
|
||||
jmethodID MethodGetPath = lJNIEnv->GetMethodID(ClassFile, "getPath", "()Ljava/lang/String;");
|
||||
jstring javaLibraryPath = static_cast<jstring>(lJNIEnv->CallObjectMethod(ObjectFile, MethodGetPath));
|
||||
const char* libraryPath = lJNIEnv->GetStringUTFChars(javaLibraryPath, NULL);
|
||||
|
||||
// Manually load the library
|
||||
void * handle = dlopen(libraryPath, RTLD_NOW | RTLD_GLOBAL);
|
||||
if (!handle)
|
||||
{
|
||||
LOGE("dlopen(\"%s\"): %s", libraryPath, dlerror());
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Release the Java string
|
||||
lJNIEnv->ReleaseStringUTFChars(THESTRING, pathStr);
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
void ANativeActivity_onCreate(ANativeActivity* activity, void* savedState, size_t savedStateSize)
|
||||
{
|
||||
// Before we can load a library, we need to find out its location. As
|
||||
// we're powerless here in C/C++, we need the JNI interface to communicate
|
||||
// with the attached Java virtual machine and perform some Java calls in
|
||||
// order to retrieve the absolute path of our libraries.
|
||||
//
|
||||
// Here's the snippet of Java code it performs:
|
||||
// --------------------------------------------
|
||||
// ai = getPackageManager().getActivityInfo(getIntent().getComponent(), PackageManager.GET_META_DATA);
|
||||
// File libraryFile = new File(ai.applicationInfo.nativeLibraryDir, System.mapLibraryName(libname));
|
||||
// String path = libraryFile.getPath();
|
||||
//
|
||||
// With libname being the library name such as "jpeg".
|
||||
|
||||
// Initialize JNI
|
||||
jint lResult;
|
||||
jint lFlags = 0;
|
||||
|
||||
JavaVM* lJavaVM = activity->vm;
|
||||
JNIEnv* lJNIEnv = activity->env;
|
||||
|
||||
JavaVMAttachArgs lJavaVMAttachArgs;
|
||||
lJavaVMAttachArgs.version = JNI_VERSION_1_6;
|
||||
lJavaVMAttachArgs.name = "NativeThread";
|
||||
lJavaVMAttachArgs.group = NULL;
|
||||
|
||||
// Attach the current thread to the JAva virtual machine
|
||||
lResult=lJavaVM->AttachCurrentThread(&lJNIEnv, &lJavaVMAttachArgs);
|
||||
|
||||
if (lResult == JNI_ERR) {
|
||||
LOG("Couldn't attach the current thread to the Java virtual machine");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Retrieve the NativeActivity
|
||||
jobject ObjectNativeActivity = activity->clazz;
|
||||
jclass ClassNativeActivity = lJNIEnv->GetObjectClass(ObjectNativeActivity);
|
||||
|
||||
// Retrieve the ActivityInfo
|
||||
jmethodID MethodGetPackageManager = lJNIEnv->GetMethodID(ClassNativeActivity, "getPackageManager", "()Landroid/content/pm/PackageManager;");
|
||||
jobject ObjectPackageManager = lJNIEnv->CallObjectMethod(ObjectNativeActivity, MethodGetPackageManager);
|
||||
|
||||
jmethodID MethodGetIndent = lJNIEnv->GetMethodID(ClassNativeActivity, "getIntent", "()Landroid/content/Intent;");
|
||||
jobject ObjectIntent = lJNIEnv->CallObjectMethod(ObjectNativeActivity, MethodGetIndent);
|
||||
|
||||
jclass ClassIntent = lJNIEnv->FindClass("android/content/Intent");
|
||||
jmethodID MethodGetComponent = lJNIEnv->GetMethodID(ClassIntent, "getComponent", "()Landroid/content/ComponentName;");
|
||||
|
||||
jobject ObjectComponentName = lJNIEnv->CallObjectMethod(ObjectIntent, MethodGetComponent);
|
||||
|
||||
jclass ClassPackageManager = lJNIEnv->FindClass("android/content/pm/PackageManager");
|
||||
|
||||
//jfieldID FieldGET_META_DATA = lJNIEnv->GetStaticFieldID(ClassPackageManager, "GET_META_DATA", "L");
|
||||
//jobject GET_META_DATA = lJNIEnv->GetStaticObjectField(ClassPackageManager, FieldGET_META_DATA);
|
||||
// getActivityInfo(getIntent().getComponent(), PackageManager.GET_META_DATA) -> ActivityInfo object
|
||||
jmethodID MethodGetActivityInfo = lJNIEnv->GetMethodID(ClassPackageManager, "getActivityInfo", "(Landroid/content/ComponentName;I)Landroid/content/pm/ActivityInfo;");
|
||||
|
||||
// todo: do not hardcode the GET_META_DATA integer value but retrieve it instead
|
||||
jobject ObjectActivityInfo = lJNIEnv->CallObjectMethod(ObjectPackageManager, MethodGetActivityInfo, ObjectComponentName, (jint)128);
|
||||
|
||||
// Load our libraries in reverse order
|
||||
loadLibrary("stlport_shared", lJNIEnv, ObjectActivityInfo);
|
||||
loadLibrary("sfml-system", lJNIEnv, ObjectActivityInfo);
|
||||
loadLibrary("sfml-window", lJNIEnv, ObjectActivityInfo);
|
||||
loadLibrary("jpeg", lJNIEnv, ObjectActivityInfo);
|
||||
loadLibrary("freetype", lJNIEnv, ObjectActivityInfo);
|
||||
loadLibrary("sfml-graphics", lJNIEnv, ObjectActivityInfo);
|
||||
loadLibrary("ogg", lJNIEnv, ObjectActivityInfo);
|
||||
loadLibrary("FLAC", lJNIEnv, ObjectActivityInfo);
|
||||
loadLibrary("vorbis", lJNIEnv, ObjectActivityInfo);
|
||||
loadLibrary("vorbisenc", lJNIEnv, ObjectActivityInfo);
|
||||
loadLibrary("sndfile", lJNIEnv, ObjectActivityInfo);
|
||||
loadLibrary("openal", lJNIEnv, ObjectActivityInfo);
|
||||
loadLibrary("sfml-audio", lJNIEnv, ObjectActivityInfo);
|
||||
loadLibrary("sfml-network", lJNIEnv, ObjectActivityInfo);
|
||||
void* handle = loadLibrary("sfml-example", lJNIEnv, ObjectActivityInfo);
|
||||
|
||||
// todo: should we detach the current thread ? because if we do, it
|
||||
// crashes (lJavaVM->DetachCurrentThread();)
|
||||
|
||||
// Call the original ANativeActivity_onCreate function
|
||||
void (*ANativeActivity_onCreate)(ANativeActivity*, void*, size_t) = dlsym(handle, "ANativeActivity_onCreate");
|
||||
|
||||
if (!ANativeActivity_onCreate)
|
||||
{
|
||||
LOGE("sfml-activity: Undefined symbol ANativeActivity_onCreate");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
ANativeActivity_onCreate(activity, savedState, savedStateSize);
|
||||
}
|
Loading…
Reference in New Issue
Block a user