summaryrefslogtreecommitdiff
path: root/platform/android/godot_android.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'platform/android/godot_android.cpp')
-rw-r--r--platform/android/godot_android.cpp993
1 files changed, 993 insertions, 0 deletions
diff --git a/platform/android/godot_android.cpp b/platform/android/godot_android.cpp
new file mode 100644
index 0000000000..673ff91641
--- /dev/null
+++ b/platform/android/godot_android.cpp
@@ -0,0 +1,993 @@
+/*************************************************************************/
+/* godot_android.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#ifdef ANDROID_NATIVE_ACTIVITY
+
+#include <jni.h>
+#include <errno.h>
+
+#include <EGL/egl.h>
+#include <GLES2/gl2.h>
+
+#include <android/sensor.h>
+#include <android/window.h>
+#include <android/log.h>
+#include <android_native_app_glue.h>
+#include "file_access_android.h"
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include "os_android.h"
+#include "globals.h"
+#include "main/main.h"
+#define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, "godot", __VA_ARGS__))
+#define LOGW(...) ((void)__android_log_print(ANDROID_LOG_WARN, "godot", __VA_ARGS__))
+
+
+extern "C" {
+ JNIEXPORT void JNICALL Java_com_android_godot_Godot_registerSingleton(JNIEnv * env, jobject obj, jstring name,jobject p_object);
+ JNIEXPORT void JNICALL Java_com_android_godot_Godot_registerMethod(JNIEnv * env, jobject obj, jstring sname, jstring name, jstring ret, jobjectArray args);
+ JNIEXPORT jstring JNICALL Java_com_android_godot_Godot_getGlobal(JNIEnv * env, jobject obj, jstring path);
+};
+
+class JNISingleton : public Object {
+
+ OBJ_TYPE( JNISingleton, Object );
+
+
+ struct MethodData {
+
+
+ jmethodID method;
+ Variant::Type ret_type;
+ Vector<Variant::Type> argtypes;
+ };
+
+ jobject instance;
+ Map<StringName,MethodData> method_map;
+ JNIEnv *env;
+
+public:
+
+ void update_env(JNIEnv *p_env) { env=p_env; }
+
+ virtual Variant call(const StringName& p_method,const Variant** p_args,int p_argcount,Variant::CallError &r_error) {
+
+ print_line("attempt to call "+String(p_method));
+
+ r_error.error=Variant::CallError::CALL_OK;
+
+ Map<StringName,MethodData >::Element *E=method_map.find(p_method);
+ if (!E) {
+
+ print_line("no exists");
+ r_error.error=Variant::CallError::CALL_ERROR_INVALID_METHOD;
+ return Variant();
+ }
+
+
+ int ac = E->get().argtypes.size();
+ if (ac<p_argcount) {
+
+ print_line("fewargs");
+ r_error.error=Variant::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
+ r_error.argument=ac;
+ return Variant();
+ }
+
+ if (ac>p_argcount) {
+
+ print_line("manyargs");
+ r_error.error=Variant::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS;
+ r_error.argument=ac;
+ return Variant();
+ }
+
+
+
+ for(int i=0;i<p_argcount;i++) {
+
+ if (!Variant::can_convert(p_args[i]->get_type(),E->get().argtypes[i])) {
+
+ r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument=i;
+ r_error.expected=E->get().argtypes[i];
+ }
+ }
+
+
+ jvalue *v=NULL;
+
+ if (p_argcount) {
+
+ v=(jvalue*)alloca( sizeof(jvalue)*p_argcount );
+ }
+
+ for(int i=0;i<p_argcount;i++) {
+
+
+ switch(E->get().argtypes[i]) {
+
+ case Variant::BOOL: {
+
+ v[i].z=*p_args[i];
+ } break;
+ case Variant::INT: {
+
+ v[i].i=*p_args[i];
+ } break;
+ case Variant::REAL: {
+
+ v[i].f=*p_args[i];
+ } break;
+ case Variant::STRING: {
+
+ String s = *p_args[i];
+ jstring jStr = env->NewStringUTF(s.utf8().get_data());
+ v[i].l=jStr;
+ } break;
+ case Variant::STRING_ARRAY: {
+
+ DVector<String> sarray = *p_args[i];
+ jobjectArray arr = env->NewObjectArray(sarray.size(),env->FindClass("java/lang/String"),env->NewStringUTF(""));
+
+ for(int j=0;j<sarray.size();j++) {
+
+ env->SetObjectArrayElement(arr,j,env->NewStringUTF( sarray[i].utf8().get_data() ));
+ }
+ v[i].l=arr;
+
+ } break;
+ case Variant::INT_ARRAY: {
+
+ DVector<int> array = *p_args[i];
+ jintArray arr = env->NewIntArray(array.size());
+ DVector<int>::Read r = array.read();
+ env->SetIntArrayRegion(arr,0,array.size(),r.ptr());
+ v[i].l=arr;
+
+ } break;
+ case Variant::REAL_ARRAY: {
+
+ DVector<float> array = *p_args[i];
+ jfloatArray arr = env->NewFloatArray(array.size());
+ DVector<float>::Read r = array.read();
+ env->SetFloatArrayRegion(arr,0,array.size(),r.ptr());
+ v[i].l=arr;
+
+ } break;
+ default: {
+
+ ERR_FAIL_V(Variant());
+ } break;
+
+ }
+ }
+
+ print_line("calling method!!");
+
+ Variant ret;
+
+ switch(E->get().ret_type) {
+
+ case Variant::NIL: {
+
+
+ print_line("call void");
+ env->CallVoidMethodA(instance,E->get().method,v);
+ } break;
+ case Variant::BOOL: {
+
+ ret = env->CallBooleanMethodA(instance,E->get().method,v);
+ print_line("call bool");
+ } break;
+ case Variant::INT: {
+
+ ret = env->CallIntMethodA(instance,E->get().method,v);
+ print_line("call int");
+ } break;
+ case Variant::REAL: {
+
+ ret = env->CallFloatMethodA(instance,E->get().method,v);
+ } break;
+ case Variant::STRING: {
+
+ jobject o = env->CallObjectMethodA(instance,E->get().method,v);
+ String singname = env->GetStringUTFChars((jstring)o, NULL );
+ } break;
+ case Variant::STRING_ARRAY: {
+
+ jobjectArray arr = (jobjectArray)env->CallObjectMethodA(instance,E->get().method,v);
+
+ int stringCount = env->GetArrayLength(arr);
+ DVector<String> sarr;
+
+ for (int i=0; i<stringCount; i++) {
+ jstring string = (jstring) env->GetObjectArrayElement(arr, i);
+ const char *rawString = env->GetStringUTFChars(string, 0);
+ sarr.push_back(String(rawString));
+ }
+
+ ret=sarr;
+
+ } break;
+ case Variant::INT_ARRAY: {
+
+ jintArray arr = (jintArray)env->CallObjectMethodA(instance,E->get().method,v);
+
+ int fCount = env->GetArrayLength(arr);
+ DVector<int> sarr;
+ sarr.resize(fCount);
+
+ DVector<int>::Write w = sarr.write();
+ env->GetIntArrayRegion(arr,0,fCount,w.ptr());
+ w = DVector<int>::Write();
+ ret=sarr;
+ } break;
+ case Variant::REAL_ARRAY: {
+
+ jfloatArray arr = (jfloatArray)env->CallObjectMethodA(instance,E->get().method,v);
+
+ int fCount = env->GetArrayLength(arr);
+ DVector<float> sarr;
+ sarr.resize(fCount);
+
+ DVector<float>::Write w = sarr.write();
+ env->GetFloatArrayRegion(arr,0,fCount,w.ptr());
+ w = DVector<float>::Write();
+ ret=sarr;
+ } break;
+ default: {
+
+
+ print_line("failure..");
+ ERR_FAIL_V(Variant());
+ } break;
+ }
+
+ print_line("success");
+
+ return ret;
+ }
+
+
+ jobject get_instance() const {
+
+ return instance;
+ }
+ void set_instance(jobject p_instance) {
+
+ instance=p_instance;
+ }
+
+
+ void add_method(const StringName& p_name, jmethodID p_method,const Vector<Variant::Type>& p_args, Variant::Type p_ret_type) {
+
+ MethodData md;
+ md.method=p_method;
+ md.argtypes=p_args;
+ md.ret_type=p_ret_type;
+ method_map[p_name]=md;
+
+ }
+
+
+ JNISingleton() {}
+
+};
+
+//JNIEnv *JNISingleton::env=NULL;
+
+static HashMap<String,JNISingleton*> jni_singletons;
+
+
+struct engine {
+ struct android_app* app;
+ OS_Android *os;
+ JNIEnv *jni;
+
+ ASensorManager* sensorManager;
+ const ASensor* accelerometerSensor;
+ ASensorEventQueue* sensorEventQueue;
+
+ bool display_active;
+ bool requested_quit;
+ int animating;
+ EGLDisplay display;
+ EGLSurface surface;
+ EGLContext context;
+ int32_t width;
+ int32_t height;
+
+};
+
+/**
+ * Initialize an EGL context for the current display.
+ */
+static int engine_init_display(struct engine* engine,bool p_gl2) {
+ // initialize OpenGL ES and EGL
+
+ /*
+ * Here specify the attributes of the desired configuration.
+ * Below, we select an EGLConfig with at least 8 bits per color
+ * component compatible with on-screen windows
+ */
+ const EGLint gl2_attribs[] = {
+ // EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
+ EGL_BLUE_SIZE, 4,
+ EGL_GREEN_SIZE, 4,
+ EGL_RED_SIZE, 4,
+ EGL_ALPHA_SIZE, 0,
+ EGL_DEPTH_SIZE, 16,
+ EGL_STENCIL_SIZE, EGL_DONT_CARE,
+ EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
+ EGL_NONE
+ };
+
+ const EGLint gl1_attribs[] = {
+ // EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
+ EGL_BLUE_SIZE, 4,
+ EGL_GREEN_SIZE, 4,
+ EGL_RED_SIZE, 4,
+ EGL_ALPHA_SIZE, 0,
+ EGL_DEPTH_SIZE, 16,
+ EGL_STENCIL_SIZE, EGL_DONT_CARE,
+ EGL_NONE
+ };
+
+ const EGLint *attribs=p_gl2?gl2_attribs:gl1_attribs;
+
+
+ EGLint w, h, dummy, format;
+ EGLint numConfigs;
+ EGLConfig config;
+ EGLSurface surface;
+ EGLContext context;
+
+ EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+
+ eglInitialize(display, 0, 0);
+
+
+ /* Here, the application chooses the configuration it desires. In this
+ * sample, we have a very simplified selection process, where we pick
+ * the first EGLConfig that matches our criteria */
+
+ eglChooseConfig(display, attribs, &config, 1, &numConfigs);
+
+ LOGI("Num configs: %i\n",numConfigs);
+
+ /* EGL_NATIVE_VISUAL_ID is an attribute of the EGLConfig that is
+ * guaranteed to be accepted by ANativeWindow_setBuffersGeometry().
+ * As soon as we picked a EGLConfig, we can safely reconfigure the
+ * ANativeWindow buffers to match, using EGL_NATIVE_VISUAL_ID. */
+ eglGetConfigAttrib(display, config, EGL_NATIVE_VISUAL_ID, &format);
+
+ ANativeWindow_setBuffersGeometry(engine->app->window, 0, 0, format);
+ //ANativeWindow_setFlags(engine->app->window, 0, 0, format|);
+
+ surface = eglCreateWindowSurface(display, config, engine->app->window, NULL);
+
+ const EGLint context_attribs[] = {
+ EGL_CONTEXT_CLIENT_VERSION,2,
+ EGL_NONE
+ };
+ context = eglCreateContext(display, config, EGL_NO_CONTEXT, p_gl2?context_attribs:NULL);
+
+ if (eglMakeCurrent(display, surface, surface, context) == EGL_FALSE) {
+ LOGW("Unable to eglMakeCurrent");
+ return -1;
+ }
+
+ eglQuerySurface(display, surface, EGL_WIDTH, &w);
+ eglQuerySurface(display, surface, EGL_HEIGHT, &h);
+ print_line("INIT VIDEO MODE: "+itos(w)+","+itos(h));
+
+ //engine->os->set_egl_extensions(eglQueryString(display,EGL_EXTENSIONS));
+ engine->os->init_video_mode(w,h);
+
+
+ engine->display = display;
+ engine->context = context;
+ engine->surface = surface;
+ engine->width = w;
+ engine->height = h;
+ engine->display_active=true;
+
+ //engine->state.angle = 0;
+
+ // Initialize GL state.
+ //glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST);
+ glEnable(GL_CULL_FACE);
+ // glShadeModel(GL_SMOOTH);
+ glDisable(GL_DEPTH_TEST);
+ LOGI("GL Version: %s - %s %s\n", glGetString(GL_VERSION),glGetString(GL_VENDOR), glGetString(GL_RENDERER));
+
+ return 0;
+}
+
+
+static void engine_draw_frame(struct engine* engine) {
+ if (engine->display == NULL) {
+ // No display.
+ return;
+ }
+
+ // Just fill the screen with a color.
+ //glClearColor(0,1,0,1);
+ //glClear(GL_COLOR_BUFFER_BIT);
+ if (engine->os && engine->os->main_loop_iterate()==true) {
+
+ engine->requested_quit=true;
+ return; //should exit instead
+ }
+
+ eglSwapBuffers(engine->display, engine->surface);
+}
+
+
+static void engine_term_display(struct engine* engine) {
+ if (engine->display != EGL_NO_DISPLAY) {
+ eglMakeCurrent(engine->display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
+ if (engine->context != EGL_NO_CONTEXT) {
+ eglDestroyContext(engine->display, engine->context);
+ }
+ if (engine->surface != EGL_NO_SURFACE) {
+ eglDestroySurface(engine->display, engine->surface);
+ }
+ eglTerminate(engine->display);
+ }
+
+ engine->animating = 0;
+ engine->display = EGL_NO_DISPLAY;
+ engine->context = EGL_NO_CONTEXT;
+ engine->surface = EGL_NO_SURFACE;
+ engine->display_active=false;
+
+}
+
+/**
+ * Process the next input event.
+ */
+static int32_t engine_handle_input(struct android_app* app, AInputEvent* event) {
+ struct engine* engine = (struct engine*)app->userData;
+
+ if (!engine->os)
+ return 0;
+
+ switch(AInputEvent_getType(event)) {
+
+ case AINPUT_EVENT_TYPE_KEY: {
+
+ int ac = AKeyEvent_getAction(event);
+ switch(ac) {
+
+ case AKEY_EVENT_ACTION_DOWN: {
+
+ int32_t code = AKeyEvent_getKeyCode(event);
+ if (code==AKEYCODE_BACK) {
+
+ //AInputQueue_finishEvent(AInputQueue* queue, AInputEvent* event, int handled);
+ if (engine->os)
+ engine->os->main_loop_request_quit();
+ return 1;
+
+ }
+
+
+ } break;
+ case AKEY_EVENT_ACTION_UP: {
+
+
+ } break;
+ }
+
+
+ } break;
+ case AINPUT_EVENT_TYPE_MOTION: {
+
+
+ Vector<OS_Android::TouchPos> touchvec;
+
+ int pc = AMotionEvent_getPointerCount(event);
+
+ touchvec.resize(pc);
+
+ for(int i=0;i<pc;i++) {
+
+ touchvec[i].pos.x=AMotionEvent_getX(event,i);
+ touchvec[i].pos.y=AMotionEvent_getY(event,i);
+ touchvec[i].id=AMotionEvent_getPointerId(event,i);
+ }
+
+
+ //System.out.printf("gaction: %d\n",event.getAction());
+ int pidx=(AMotionEvent_getAction(event)&AMOTION_EVENT_ACTION_POINTER_INDEX_MASK)>>8;
+ switch(AMotionEvent_getAction(event)&AMOTION_EVENT_ACTION_MASK) {
+
+ case AMOTION_EVENT_ACTION_DOWN: {
+ engine->os->process_touch(0,0,touchvec);
+
+ //System.out.printf("action down at: %f,%f\n", event.getX(),event.getY());
+ } break;
+ case AMOTION_EVENT_ACTION_MOVE: {
+ engine->os->process_touch(1,0,touchvec);
+ //for(int i=0;i<event.getPointerCount();i++) {
+ // System.out.printf("%d - moved to: %f,%f\n",i, event.getX(i),event.getY(i));
+ //}
+ } break;
+ case AMOTION_EVENT_ACTION_POINTER_UP: {
+
+ engine->os->process_touch(4,pidx,touchvec);
+ //System.out.printf("%d - s.up at: %f,%f\n",pointer_idx, event.getX(pointer_idx),event.getY(pointer_idx));
+ } break;
+ case AMOTION_EVENT_ACTION_POINTER_DOWN: {
+ engine->os->process_touch(3,pidx,touchvec);
+ //System.out.printf("%d - s.down at: %f,%f\n",pointer_idx, event.getX(pointer_idx),event.getY(pointer_idx));
+ } break;
+ case AMOTION_EVENT_ACTION_CANCEL:
+ case AMOTION_EVENT_ACTION_UP: {
+ engine->os->process_touch(2,0,touchvec);
+ //for(int i=0;i<event.getPointerCount();i++) {
+ // System.out.printf("%d - up! %f,%f\n",i, event.getX(i),event.getY(i));
+ //}
+ } break;
+ }
+
+ return 1;
+ } break;
+
+ }
+
+ return 0;
+}
+
+/**
+ * Process the next main command.
+ */
+
+static void _gfx_init(void *ud,bool p_gl2) {
+
+ struct engine* engine = (struct engine*)ud;
+ engine_init_display(engine,p_gl2);
+}
+
+static void engine_handle_cmd(struct android_app* app, int32_t cmd) {
+ struct engine* engine = (struct engine*)app->userData;
+ // LOGI("**** CMD %i\n",cmd);
+ switch (cmd) {
+ case APP_CMD_SAVE_STATE:
+ // The system has asked us to save our current state. Do so.
+ //engine->app->savedState = malloc(sizeof(struct saved_state));
+ //*((struct saved_state*)engine->app->savedState) = engine->state;
+ //engine->app->savedStateSize = sizeof(struct saved_state);
+ break;
+ case APP_CMD_CONFIG_CHANGED:
+ case APP_CMD_WINDOW_RESIZED: {
+
+#if 0
+// android blows
+ if (engine->display_active) {
+
+ EGLint w,h;
+ eglQuerySurface(engine->display, engine->surface, EGL_WIDTH, &w);
+ eglQuerySurface(engine->display, engine->surface, EGL_HEIGHT, &h);
+ engine->os->init_video_mode(w,h);
+ //print_line("RESIZED VIDEO MODE: "+itos(w)+","+itos(h));
+ engine_draw_frame(engine);
+
+ }
+#else
+
+ if (engine->display_active) {
+
+
+ EGLint w,h;
+ eglQuerySurface(engine->display, engine->surface, EGL_WIDTH, &w);
+ eglQuerySurface(engine->display, engine->surface, EGL_HEIGHT, &h);
+ // if (w==engine->os->get_video_mode().width && h==engine->os->get_video_mode().height)
+ // break;
+
+ engine_term_display(engine);
+
+
+ }
+
+
+ engine->os->reload_gfx();
+ engine_draw_frame(engine);
+ engine->animating=1;
+
+ /*
+ EGLint w,h;
+ eglQuerySurface(engine->display, engine->surface, EGL_WIDTH, &w);
+ eglQuerySurface(engine->display, engine->surface, EGL_HEIGHT, &h);
+ engine->os->init_video_mode(w,h);
+ //print_line("RESIZED VIDEO MODE: "+itos(w)+","+itos(h));
+
+ }*/
+
+#endif
+
+ } break;
+ case APP_CMD_INIT_WINDOW:
+ //The window is being shown, get it ready.
+ // LOGI("INIT WINDOW");
+ if (engine->app->window != NULL) {
+
+ if (engine->os==NULL) {
+
+ //do initialization here, when there's OpenGL! hackish but the only way
+ engine->os = new OS_Android(_gfx_init,engine);
+
+ // char *args[]={"-test","gui",NULL};
+ __android_log_print(ANDROID_LOG_INFO,"godot","pre asdasd setup...");
+#if 0
+ Error err = Main::setup("apk",2,args);
+#else
+ Error err = Main::setup("apk",0,NULL);
+
+ String modules = Globals::get_singleton()->get("android/modules");
+ Vector<String> mods = modules.split(",",false);
+ mods.push_back("GodotOS");
+ __android_log_print(ANDROID_LOG_INFO,"godot","mod count: %i",mods.size());
+
+ if (mods.size()) {
+
+ jclass activityClass = engine->jni->FindClass("android/app/NativeActivity");
+
+ jmethodID getClassLoader = engine->jni->GetMethodID(activityClass,"getClassLoader", "()Ljava/lang/ClassLoader;");
+
+ jobject cls = engine->jni->CallObjectMethod(app->activity->clazz, getClassLoader);
+
+ jclass classLoader = engine->jni->FindClass("java/lang/ClassLoader");
+
+ jmethodID findClass = engine->jni->GetMethodID(classLoader, "loadClass", "(Ljava/lang/String;)Ljava/lang/Class;");
+
+
+ static JNINativeMethod methods[] = {
+ {"registerSingleton", "(Ljava/lang/String;Ljava/lang/Object;)V",(void *)&Java_com_android_godot_Godot_registerSingleton},
+ {"registerMethod", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;)V",(void *)&Java_com_android_godot_Godot_registerMethod},
+ {"getGlobal", "(Ljava/lang/String;)Ljava/lang/String;", (void *)&Java_com_android_godot_Godot_getGlobal},
+ };
+
+ jstring gstrClassName = engine->jni->NewStringUTF("com/android/godot/Godot");
+ jclass GodotClass = (jclass)engine->jni->CallObjectMethod(cls, findClass, gstrClassName);
+
+ __android_log_print(ANDROID_LOG_INFO,"godot","godot ****^*^*?^*^*class data %x",GodotClass);
+
+ engine->jni->RegisterNatives(GodotClass,methods,sizeof(methods)/sizeof(methods[0]));
+
+ for (int i=0;i<mods.size();i++) {
+
+ String m = mods[i];
+ //jclass singletonClass = engine->jni->FindClass(m.utf8().get_data());
+
+ jstring strClassName = engine->jni->NewStringUTF(m.utf8().get_data());
+ jclass singletonClass = (jclass)engine->jni->CallObjectMethod(cls, findClass, strClassName);
+
+ __android_log_print(ANDROID_LOG_INFO,"godot","****^*^*?^*^*class data %x",singletonClass);
+ jmethodID initialize = engine->jni->GetStaticMethodID(singletonClass, "initialize", "(Landroid/app/Activity;)Lcom/android/godot/Godot$SingletonBase;");
+
+
+ jobject obj = engine->jni->CallStaticObjectMethod(singletonClass,initialize,app->activity->clazz);
+ __android_log_print(ANDROID_LOG_INFO,"godot","****^*^*?^*^*class instance %x",obj);
+ jobject gob = engine->jni->NewGlobalRef(obj);
+
+
+ }
+
+ }
+
+#endif
+
+
+ if (!Main::start())
+ return; //should exit instead and print the error
+
+ engine->os->main_loop_begin();
+ } else {
+ //i guess recreate resources?
+ engine->os->reload_gfx();
+ }
+
+
+ engine->animating=1;
+ engine_draw_frame(engine);
+ }
+ break;
+ case APP_CMD_TERM_WINDOW:
+ // The window is being hidden or closed, clean it up.
+ // LOGI("TERM WINDOW");
+ engine_term_display(engine);
+ break;
+ case APP_CMD_GAINED_FOCUS:
+ // When our app gains focus, we start monitoring the accelerometer.
+ if (engine->accelerometerSensor != NULL) {
+ ASensorEventQueue_enableSensor(engine->sensorEventQueue,
+ engine->accelerometerSensor);
+ // We'd like to get 60 events per second (in us).
+ ASensorEventQueue_setEventRate(engine->sensorEventQueue,
+ engine->accelerometerSensor, (1000L/60)*1000);
+
+ }
+ engine->animating = 1;
+ break;
+ case APP_CMD_LOST_FOCUS:
+ // When our app loses focus, we stop monitoring the accelerometer.
+ // This is to avoid consuming battery while not being used.
+ if (engine->accelerometerSensor != NULL) {
+ ASensorEventQueue_disableSensor(engine->sensorEventQueue,
+ engine->accelerometerSensor);
+ }
+ // Also stop animating.
+ engine->animating = 0;
+ engine_draw_frame(engine);
+ break;
+ }
+}
+
+void android_main(struct android_app* state) {
+ struct engine engine;
+ // Make sure glue isn't stripped.
+ app_dummy();
+
+ memset(&engine, 0, sizeof(engine));
+ state->userData = &engine;
+ state->onAppCmd = engine_handle_cmd;
+ state->onInputEvent = engine_handle_input;
+ engine.app = state;
+ engine.requested_quit=false;
+ engine.os=NULL;
+ engine.display_active=false;
+
+ FileAccessAndroid::asset_manager=state->activity->assetManager;
+
+ // Prepare to monitor accelerometer
+ engine.sensorManager = ASensorManager_getInstance();
+ engine.accelerometerSensor = ASensorManager_getDefaultSensor(engine.sensorManager,
+ ASENSOR_TYPE_ACCELEROMETER);
+ engine.sensorEventQueue = ASensorManager_createEventQueue(engine.sensorManager,
+ state->looper, LOOPER_ID_USER, NULL, NULL);
+
+
+ ANativeActivity_setWindowFlags(state->activity,AWINDOW_FLAG_FULLSCREEN|AWINDOW_FLAG_KEEP_SCREEN_ON,0);
+
+ state->activity->vm->AttachCurrentThread(&engine.jni, NULL);
+
+
+
+ // loop waiting for stuff to do.
+
+ while (1) {
+ // Read all pending events.
+ int ident;
+ int events;
+ struct android_poll_source* source;
+
+ // If not animating, we will block forever waiting for events.
+ // If animating, we loop until all events are read, then continue
+ // to draw the next frame of animation.
+
+ int nullmax=50;
+ while ((ident=ALooper_pollAll(engine.animating ? 0 : -1, NULL, &events,
+ (void**)&source)) >= 0) {
+
+ // Process this event.
+
+ if (source != NULL) {
+ // LOGI("process\n");
+ source->process(state, source);
+ } else {
+ nullmax--;
+ if (nullmax<0)
+ break;
+ }
+
+ // If a sensor has data, process it now.
+ // LOGI("events\n");
+ if (ident == LOOPER_ID_USER) {
+ if (engine.accelerometerSensor != NULL) {
+ ASensorEvent event;
+ while (ASensorEventQueue_getEvents(engine.sensorEventQueue,
+ &event, 1) > 0) {
+
+
+ if (engine.os) {
+ engine.os->process_accelerometer(Vector3(event.acceleration.x, event.acceleration.y,
+ event.acceleration.z));
+
+ }
+
+ }
+ }
+ }
+
+ // Check if we are exiting.
+ if (state->destroyRequested != 0) {
+ if (engine.os) {
+ engine.os->main_loop_request_quit();
+ }
+ state->destroyRequested=0;
+ }
+
+ if (engine.requested_quit) {
+ engine_term_display(&engine);
+ exit(0);
+ return;
+ }
+
+// LOGI("end\n");
+
+
+ }
+
+// LOGI("engine animating? %i\n",engine.animating);
+
+ if (engine.animating) {
+ //do os render
+
+ engine_draw_frame(&engine);
+ //LOGI("TERM WINDOW");
+
+ }
+ }
+
+}
+
+
+
+
+JNIEXPORT void JNICALL Java_com_android_godot_Godot_registerSingleton(JNIEnv * env, jobject obj, jstring name,jobject p_object){
+
+ String singname = env->GetStringUTFChars( name, NULL );
+ JNISingleton *s = memnew( JNISingleton );
+ s->update_env(env);
+ s->set_instance(env->NewGlobalRef(p_object));
+ jni_singletons[singname]=s;
+
+ Globals::get_singleton()->add_singleton(Globals::Singleton(singname,s));
+
+}
+
+
+static Variant::Type get_jni_type(const String& p_type) {
+
+ static struct {
+ const char *name;
+ Variant::Type type;
+ } _type_to_vtype[]={
+ {"void",Variant::NIL},
+ {"boolean",Variant::BOOL},
+ {"int",Variant::INT},
+ {"float",Variant::REAL},
+ {"java.lang.String",Variant::STRING},
+ {"[I",Variant::INT_ARRAY},
+ {"[F",Variant::REAL_ARRAY},
+ {"[java.lang.String",Variant::STRING_ARRAY},
+ {NULL,Variant::NIL}
+ };
+
+ int idx=0;
+
+ while (_type_to_vtype[idx].name) {
+
+ if (p_type==_type_to_vtype[idx].name)
+ return _type_to_vtype[idx].type;
+
+ idx++;
+ }
+
+ return Variant::NIL;
+}
+
+
+static const char* get_jni_sig(const String& p_type) {
+
+ static struct {
+ const char *name;
+ const char *sig;
+ } _type_to_vtype[]={
+ {"void","V"},
+ {"boolean","Z"},
+ {"int","I"},
+ {"float","F"},
+ {"java.lang.String","Ljava/lang/String;"},
+ {"[I","[I"},
+ {"[F","[F"},
+ {"[java.lang.String","[Ljava/lang/String;"},
+ {NULL,"V"}
+ };
+
+ int idx=0;
+
+ while (_type_to_vtype[idx].name) {
+
+ if (p_type==_type_to_vtype[idx].name)
+ return _type_to_vtype[idx].sig;
+
+ idx++;
+ }
+
+
+ return "";
+}
+
+JNIEXPORT jstring JNICALL Java_com_android_godot_Godot_getGlobal(JNIEnv * env, jobject obj, jstring path) {
+
+ String js = env->GetStringUTFChars( path, NULL );
+
+ return env->NewStringUTF(Globals::get_singleton()->get(js).operator String().utf8().get_data());
+
+
+}
+
+
+
+JNIEXPORT void JNICALL Java_com_android_godot_Godot_registerMethod(JNIEnv * env, jobject obj, jstring sname, jstring name, jstring ret, jobjectArray args){
+
+ String singname = env->GetStringUTFChars( sname, NULL );
+
+ ERR_FAIL_COND(!jni_singletons.has(singname));
+
+ JNISingleton *s = jni_singletons.get(singname);
+
+
+ String mname = env->GetStringUTFChars( name, NULL );
+ String retval = env->GetStringUTFChars( ret, NULL );
+ Vector<Variant::Type> types;
+ String cs="(";
+
+
+ int stringCount = env->GetArrayLength(args);
+
+ print_line("Singl: "+singname+" Method: "+mname+" RetVal: "+retval);
+ for (int i=0; i<stringCount; i++) {
+
+ jstring string = (jstring) env->GetObjectArrayElement(args, i);
+ const char *rawString = env->GetStringUTFChars(string, 0);
+ types.push_back(get_jni_type(String(rawString)));
+ cs+=get_jni_sig(String(rawString));
+ }
+
+ cs+=")";
+ cs+=get_jni_sig(retval);
+ jclass cls = env->GetObjectClass(s->get_instance());
+ print_line("METHOD: "+mname+" sig: "+cs);
+ jmethodID mid = env->GetMethodID(cls, mname.ascii().get_data(), cs.ascii().get_data());
+ if (!mid) {
+
+ print_line("FAILED GETTING METHOID "+mname);
+ }
+
+ s->add_method(mname,mid,types,get_jni_type(retval));
+
+
+}
+
+#endif