mirror of
				https://github.com/godotengine/godot.git
				synced 2025-11-03 23:21:15 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			993 lines
		
	
	
	
		
			27 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			993 lines
		
	
	
	
		
			27 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/*************************************************************************/
 | 
						|
/*  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
 |