GODOT IS OPEN SOURCE

This commit is contained in:
Juan Linietsky
2014-02-09 22:10:30 -03:00
parent 0e49da1687
commit 0b806ee0fc
3138 changed files with 1294441 additions and 0 deletions

View File

@@ -0,0 +1,22 @@
Import('env')
android_files = [
'audio_driver_android.cpp',
'file_access_jandroid.cpp',
'dir_access_jandroid.cpp',
'os_android.cpp',
'java_glue.cpp'
]
#env.Depends('#core/math/vector3.h', 'vector3_psp.h')
#obj = env.SharedObject('godot_android.cpp')
android_objects=[]
for x in android_files:
android_objects.append( env.SharedObject( x ) )
prog = None
env.SharedLibrary("#platform/jandroid/libgodot_android.so",[android_objects])

View File

@@ -0,0 +1,104 @@
#include "audio_driver_android.h"
AudioDriverAndroid* AudioDriverAndroid::s_ad=NULL;
const char* AudioDriverAndroid::get_name() const {
return "Android";
}
#if 0
int AudioDriverAndroid::thread_func(SceSize args, void *argp) {
AudioDriverAndroid* ad = s_ad;
sceAudioOutput2Reserve(AUDIO_OUTPUT_SAMPLE);
int half=0;
while(!ad->exit_thread) {
int16_t *ptr = &ad->outbuff[AUDIO_OUTPUT_SAMPLE*2*half];
if (!ad->active) {
for(int i=0;i<AUDIO_OUTPUT_SAMPLE*2;i++) {
ptr[i]=0;
}
} else {
//printf("samples: %i\n",AUDIO_OUTPUT_SAMPLE);
ad->lock();
ad->audio_server_process(AUDIO_OUTPUT_SAMPLE,ad->outbuff_32);
ad->unlock();
const int32_t* src_buff=ad->outbuff_32;
for(int i=0;i<AUDIO_OUTPUT_SAMPLE*2;i++) {
ptr[i]=src_buff[i]>>16;
}
}
/* Output 16-bit PCM STEREO data that is in pcmBuf without changing the volume */
sceAudioOutput2OutputBlocking(
SCE_AUDIO_VOLUME_0dB*3, //0db at 0x8000, that's obvious
ptr
);
if (half)
half=0;
else
half=1;
}
sceAudioOutput2Release();
sceKernelExitThread(SCE_KERNEL_EXIT_SUCCESS);
ad->thread_exited=true;
return SCE_KERNEL_EXIT_SUCCESS;
}
#endif
Error AudioDriverAndroid::init(){
return OK;
}
void AudioDriverAndroid::start(){
}
int AudioDriverAndroid::get_mix_rate() const {
return 44100;
}
AudioDriverSW::OutputFormat AudioDriverAndroid::get_output_format() const{
return OUTPUT_STEREO;
}
void AudioDriverAndroid::lock(){
}
void AudioDriverAndroid::unlock() {
}
void AudioDriverAndroid::finish(){
}
AudioDriverAndroid::AudioDriverAndroid()
{
s_ad=this;
}

View File

@@ -0,0 +1,29 @@
#ifndef AUDIO_DRIVER_ANDROID_H
#define AUDIO_DRIVER_ANDROID_H
#include "servers/audio/audio_server_sw.h"
class AudioDriverAndroid : public AudioDriverSW {
static AudioDriverAndroid* s_ad;
public:
void set_singleton();
virtual const char* get_name() const;
virtual Error init();
virtual void start();
virtual int get_mix_rate() const ;
virtual OutputFormat get_output_format() const;
virtual void lock();
virtual void unlock();
virtual void finish();
AudioDriverAndroid();
};
#endif // AUDIO_DRIVER_ANDROID_H

View File

@@ -0,0 +1,38 @@
#include "context_gl_android.h"
#include <GLES2/gl2.h>
#include "os/os.h"
void ContextGLAndroid::make_current() {
};
int ContextGLAndroid::get_window_width() {
return OS::get_singleton()->get_default_video_mode().width;
};
int ContextGLAndroid::get_window_height() {
return OS::get_singleton()->get_default_video_mode().height;
};
void ContextGLAndroid::swap_buffers() {
};
Error ContextGLAndroid::initialize() {
return OK;
};
ContextGLAndroid::ContextGLAndroid() {
};
ContextGLAndroid::~ContextGLAndroid() {
};

View File

@@ -0,0 +1,24 @@
#ifndef CONTEXT_GL_ANDROID_H
#define CONTEXT_GL_ANDROID_H
class ContextGLAndroid : public ContextGL {
enum {
COMMAND_BUFFER_SIZE = 1024 * 1024,
};
public:
virtual void make_current();
virtual int get_window_width();
virtual int get_window_height();
virtual void swap_buffers();
virtual Error initialize();
ContextGLAndroid();
~ContextGLAndroid();
};
#endif // CONTEXT_GL_ANDROID_H

View File

@@ -0,0 +1,107 @@
import os
import sys
import string
def can_build():
import os
if (not os.environ.has_key("ANDROID_NDK_ROOT")):
print("ANDROID_NDK_ROOT not present, Android disabled.")
return False
return True
def get_opts():
return [
('ANDROID_NDK_ROOT', 'the path to Android NDK', os.environ.get("ANDROID_NDK_ROOT", 0)),
('NDK_TOOLCHAIN', 'toolchain to use for the NDK',"arm-eabi-4.4.0"),
#android 2.2
# ('NDK_PLATFORM', 'platform to use for the NDK',"android-8"),
# ('NDK_TARGET', 'toolchain to use for the NDK',"arm-linux-androideabi-4.4.3"),
]
def get_flags():
return [
('lua', 'no'),
('tools', 'no'),
('nedmalloc', 'no'),
]
def configure(env):
print("Godot Android!!!!!")
env.Append(CPPPATH=['#platform/android'])
env['OBJSUFFIX'] = ".jandroid.o"
env['LIBSUFFIX'] = ".jandroid.a"
env['PROGSUFFIX'] = ".jandroid"
gcc_path=env["ANDROID_NDK_ROOT"]+"/toolchains/"+env["NDK_TARGET"]+"/prebuilt/";
if (sys.platform.find("linux")==0):
gcc_path=gcc_path+"/linux-x86/bin"
elif (sys.platform=="darwin"):
gcc_path=gcc_path+"/darwin-x86/bin" #this may be wrong
elif (os.name=="nt"):
gcc_path=gcc_path+"/windows-x86/bin" #this may be wrong
env['ENV']['PATH'] = gcc_path+":"+env['ENV']['PATH']
env['CC'] = gcc_path+'/arm-linux-androideabi-gcc'
env['CXX'] = gcc_path+'/arm-linux-androideabi-g++'
env['AR'] = gcc_path+"/arm-linux-androideabi-ar"
import string
#include path
gcc_include=env["ANDROID_NDK_ROOT"]+"/platforms/"+env["NDK_PLATFORM"]+"/arch-arm/usr/include"
ld_sysroot=env["ANDROID_NDK_ROOT"]+"/platforms/"+env["NDK_PLATFORM"]+"/arch-arm"
ld_path=env["ANDROID_NDK_ROOT"]+"/platforms/"+env["NDK_PLATFORM"]+"/arch-arm/usr/lib"
#cxx_include=env["ANDROID_NDK_ROOT"]+"/sources/cxx-stl/system/include"
#env.Append(CPPPATH=[gcc_include,cxx_include])
env['CCFLAGS'] = string.split('-DNO_THREADS -DNO_STATVFS -MMD -MP -MF -fpic -ffunction-sections -funwind-tables -fstack-protector -D__ARM_ARCH_5__ -D__ARM_ARCH_5T__ -D__ARM_ARCH_5E__ -D__ARM_ARCH_5TE__ -Wno-psabi -march=armv5te -mtune=xscale -msoft-float -fno-exceptions -mthumb -fno-strict-aliasing -DANDROID -Wa,--noexecstack -DGLES2_ENABLED ')
env.Append(LDPATH=[ld_path])
env.Append(LIBS=['c','m','stdc++','log','GLESv2'])
env["LINKFLAGS"]= string.split(" -g --sysroot="+ld_sysroot+" -Wl,--no-undefined -Wl,-z,noexecstack")
env.Append(LINKFLAGS=["-Wl,-soname,libgodot_android.so"])
if (env["target"]=="release"):
env.Append(CCFLAGS=['-O2', '-ffast-math','-fomit-frame-pointer'])
env['OBJSUFFIX'] = "_opt"+env['OBJSUFFIX']
env['LIBSUFFIX'] = "_opt"+env['LIBSUFFIX']
elif (env["target"]=="debug"):
env.Append(CCFLAGS=['-D_DEBUG', '-g', '-Wall', '-O0', '-DDEBUG_ENABLED'])
env.Append(CPPFLAGS=['-DDEBUG_MEMORY_ALLOC'])
env.Append(CPPFLAGS=['-DANDROID_ENABLED', '-DUNIX_ENABLED','-DNO_FCNTL'])
env['neon_enabled']=True
env.Append(CPPFLAGS=['-DANDROID_ENABLED', '-DUNIX_ENABLED', '-DNO_FCNTL','-DMPC_FIXED_POINT'])
# env.Append(CPPFLAGS=['-DANDROID_ENABLED', '-DUNIX_ENABLED','-DMPC_FIXED_POINT'])
if (env['android_stl']=='yes'):
#env.Append(CCFLAGS=[env["ANDROID_NDK_ROOT"]+"/sources/cxx-stl/system/include"])
env.Append(CPPPATH=[env["ANDROID_NDK_ROOT"]+"/sources/cxx-stl/gnu-libstdc++/4.4.3/include"])
env.Append(CPPPATH=[env["ANDROID_NDK_ROOT"]+"/sources/cxx-stl/gnu-libstdc++/4.4.3/libs/armeabi/include"])
env.Append(LIBPATH=[env["ANDROID_NDK_ROOT"]+"/sources/cxx-stl/gnu-libstdc++/4.4.3/libs/armeabi"])
env.Append(LIBS=["gnustl_static","supc++"])
#env.Append(CCFLAGS=["-I"+env["ANDROID_NDK_ROOT"]+"/sources/cxx-stl/stlport/stlport"])
#env.Append(CCFLAGS=["-I"+env["ANDROID_NDK_ROOT"]+"/sources/cxx-stl/gnu-libstdc++/libs/armeabi/include"])
#env.Append(LINKFLAGS=[env["ANDROID_NDK_ROOT"]+"/sources/cxx-stl/gnu-libstdc++/libs/armeabi/libstdc++.a"])
else:
env.Append(CPPPATH=[env["ANDROID_NDK_ROOT"]+"/sources/cxx-stl/gabi++/include"])
env.Append(CPPPATH=[env["ANDROID_NDK_ROOT"]+"/sources/cpufeatures"])
env.Append(LIBPATH=[env["ANDROID_NDK_ROOT"]+"/sources/cxx-stl/gabi++/libs/armeabi"])
env.Append(LIBS=['gabi++_static'])
env.Append(CCFLAGS=["-fno-exceptions",'-DNO_SAFE_CAST'])

View File

@@ -0,0 +1,217 @@
#include "dir_access_jandroid.h"
#include "file_access_jandroid.h"
jobject DirAccessJAndroid::io=NULL;
jclass DirAccessJAndroid::cls=NULL;
JNIEnv * DirAccessJAndroid::env=NULL;
jmethodID DirAccessJAndroid::_dir_open=NULL;
jmethodID DirAccessJAndroid::_dir_next=NULL;
jmethodID DirAccessJAndroid::_dir_close=NULL;
DirAccess *DirAccessJAndroid::create_fs() {
return memnew(DirAccessJAndroid);
}
bool DirAccessJAndroid::list_dir_begin() {
list_dir_end();
jstring js = env->NewStringUTF(current_dir.utf8().get_data());
int res = env->CallIntMethod(io,_dir_open,js);
if (res<=0)
return true;
id=res;
return false;
}
String DirAccessJAndroid::get_next(){
ERR_FAIL_COND_V(id==0,"");
jstring str= (jstring)env->CallObjectMethod(io,_dir_next,id);
if (!str)
return "";
int sl = env->GetStringLength(str);
if (sl==0) {
env->DeleteLocalRef((jobject)str);
return "";
}
CharString cs;
cs.resize(sl+1);
env->GetStringRegion(str,0,sl,(jchar*)&cs[0]);
cs[sl]=0;
String ret;
ret.parse_utf8(&cs[0]);
env->DeleteLocalRef((jobject)str);
return ret;
}
bool DirAccessJAndroid::current_is_dir() const{
String sd;
if (current_dir=="")
sd=current;
else
sd=current_dir+"/"+current;
jstring js = env->NewStringUTF(sd.utf8().get_data());
int res = env->CallIntMethod(io,_dir_open,js);
if (res<=0)
return false;
env->CallObjectMethod(io,_dir_close,res);
return true;
}
void DirAccessJAndroid::list_dir_end(){
if (id==0)
return;
env->CallObjectMethod(io,_dir_close,id);
id=0;
}
int DirAccessJAndroid::get_drive_count(){
return 0;
}
String DirAccessJAndroid::get_drive(int p_drive){
return "";
}
Error DirAccessJAndroid::change_dir(String p_dir){
p_dir=p_dir.simplify_path();
if (p_dir=="" || p_dir=="." || (p_dir==".." && current_dir==""))
return OK;
String new_dir;
if (p_dir.begins_with("/"))
new_dir=p_dir.substr(1,p_dir.length());
else if (p_dir.begins_with("res://"))
new_dir=p_dir.substr(6,p_dir.length());
else //relative
new_dir=new_dir+"/"+p_dir;
//test if newdir exists
new_dir=new_dir.simplify_path();
jstring js = env->NewStringUTF(new_dir.utf8().get_data());
int res = env->CallIntMethod(io,_dir_open,js);
if (res<=0)
return ERR_INVALID_PARAMETER;
env->CallObjectMethod(io,_dir_close,res);
return OK;
}
String DirAccessJAndroid::get_current_dir(){
return "/"+current_dir;
}
String DirAccessJAndroid::get_dir_separator() const{
return "/";
}
bool DirAccessJAndroid::file_exists(String p_file){
String sd;
if (current_dir=="")
sd=p_file;
else
sd=current_dir+"/"+p_file;
FileAccessJAndroid *f = memnew(FileAccessJAndroid);
bool exists = f->file_exists(sd);
memdelete(f);
return exists;
}
uint64_t DirAccessJAndroid::get_modified_time(String p_file){
return 0;
}
Error DirAccessJAndroid::make_dir(String p_dir){
ERR_FAIL_V(ERR_UNAVAILABLE);
}
Error DirAccessJAndroid::rename(String p_from, String p_to){
ERR_FAIL_V(ERR_UNAVAILABLE);
}
Error DirAccessJAndroid::remove(String p_name){
ERR_FAIL_V(ERR_UNAVAILABLE);
}
//FileType get_file_type() const;
size_t DirAccessJAndroid::get_space_left() {
return 0;
}
void DirAccessJAndroid::make_default() {
instance_func=create_fs;
}
void DirAccessJAndroid::setup( JNIEnv *p_env, jobject p_io) {
env=p_env;
io=p_io;
__android_log_print(ANDROID_LOG_INFO,"godot","STEP7");
jclass c = env->GetObjectClass(io);
cls = (jclass)env->NewGlobalRef(c);
__android_log_print(ANDROID_LOG_INFO,"godot","STEP8");
_dir_open = env->GetMethodID(cls, "dir_open", "(Ljava/lang/String;)I");
if(_dir_open != 0) {
__android_log_print(ANDROID_LOG_INFO,"godot","*******GOT METHOD _dir_open ok!!");
}
_dir_next = env->GetMethodID(cls, "dir_next", "(I)Ljava/lang/String;");
if(_dir_next != 0) {
__android_log_print(ANDROID_LOG_INFO,"godot","*******GOT METHOD _dir_next ok!!");
}
_dir_close = env->GetMethodID(cls, "dir_close", "(I)V");
if(_dir_close != 0) {
__android_log_print(ANDROID_LOG_INFO,"godot","*******GOT METHOD _dir_close ok!!");
}
// (*env)->CallVoidMethod(env,obj,aMethodID, myvar);
}
DirAccessJAndroid::DirAccessJAndroid() {
id=0;
}
DirAccessJAndroid::~DirAccessJAndroid() {
list_dir_end();;
}

View File

@@ -0,0 +1,63 @@
#ifndef DIR_ACCESS_JANDROID_H
#define DIR_ACCESS_JANDROID_H
#include "java_glue.h"
#include "os/dir_access.h"
#include <stdio.h>
class DirAccessJAndroid : public DirAccess {
//AAssetDir* aad;
static jobject io;
static jclass cls;
static jmethodID _dir_open;
static jmethodID _dir_next;
static jmethodID _dir_close;
static JNIEnv * env;
int id;
String current_dir;
String current;
static DirAccess *create_fs();
public:
virtual bool list_dir_begin(); ///< This starts dir listing
virtual String get_next();
virtual bool current_is_dir() const;
virtual void list_dir_end(); ///<
virtual int get_drive_count();
virtual String get_drive(int p_drive);
virtual Error change_dir(String p_dir); ///< can be relative or absolute, return false on success
virtual String get_current_dir(); ///< return current dir location
virtual String get_dir_separator() const ;
virtual bool file_exists(String p_file);
virtual uint64_t get_modified_time(String p_file);
virtual Error make_dir(String p_dir);
virtual Error rename(String p_from, String p_to);
virtual Error remove(String p_name);
//virtual FileType get_file_type() const;
size_t get_space_left();
static void make_default();
static void setup( JNIEnv *env, jobject io);
static void update_env( JNIEnv *p_env) { env=p_env; }
DirAccessJAndroid();
~DirAccessJAndroid();
};
#endif // DIR_ACCESS_JANDROID_H

View File

@@ -0,0 +1,204 @@
#include "file_access_jandroid.h"
#include "os/os.h"
#include <unistd.h>
jobject FileAccessJAndroid::io=NULL;
jclass FileAccessJAndroid::cls;
jmethodID FileAccessJAndroid::_file_open=0;
jmethodID FileAccessJAndroid::_file_get_size=0;
jmethodID FileAccessJAndroid::_file_seek=0;
jmethodID FileAccessJAndroid::_file_read=0;
jmethodID FileAccessJAndroid::_file_tell=0;
jmethodID FileAccessJAndroid::_file_eof=0;
jmethodID FileAccessJAndroid::_file_close=0;
JNIEnv * FileAccessJAndroid::env=NULL;
void FileAccessJAndroid::make_default() {
create_func=create_jandroid;
}
FileAccess* FileAccessJAndroid::create_jandroid() {
return memnew(FileAccessJAndroid);
}
Error FileAccessJAndroid::open(const String& p_path, int p_mode_flags) {
if (is_open())
close();
String path=fix_path(p_path).simplify_path();
if (path.begins_with("/"))
path=path.substr(1,path.length());
else if (path.begins_with("res://"))
path=path.substr(6,path.length());
//OS::get_singleton()->print("env: %p, io %p, fo: %p\n",env,io,_file_open);
jstring js = env->NewStringUTF(path.utf8().get_data());
int res = env->CallIntMethod(io,_file_open,js,p_mode_flags&WRITE?true:false);
if (res<=0)
return ERR_FILE_CANT_OPEN;
id=res;
return OK;
}
void FileAccessJAndroid::close() {
if (io==0)
return;
env->CallVoidMethod(io,_file_close,id);
id=0;
}
bool FileAccessJAndroid::is_open() const {
return id!=0;
}
void FileAccessJAndroid::seek(size_t p_position) {
ERR_FAIL_COND(!is_open());
env->CallVoidMethod(io,_file_seek,id,p_position);
}
void FileAccessJAndroid::seek_end(int64_t p_position) {
ERR_FAIL_COND(!is_open());
seek(get_len());
}
size_t FileAccessJAndroid::get_pos() const {
ERR_FAIL_COND_V(!is_open(),0);
return env->CallIntMethod(io,_file_tell,id);
}
size_t FileAccessJAndroid::get_len() const {
ERR_FAIL_COND_V(!is_open(),0);
return env->CallIntMethod(io,_file_get_size,id);
}
bool FileAccessJAndroid::eof_reached() const {
ERR_FAIL_COND_V(!is_open(),0);
return env->CallIntMethod(io,_file_eof,id);
}
uint8_t FileAccessJAndroid::get_8() const {
ERR_FAIL_COND_V(!is_open(),0);
uint8_t byte;
get_buffer(&byte,1);
return byte;
}
int FileAccessJAndroid::get_buffer(uint8_t *p_dst, int p_length) const {
ERR_FAIL_COND_V(!is_open(),0);
if (p_length==0)
return 0;
jbyteArray jca = (jbyteArray)env->CallObjectMethod(io,_file_read,id,p_length);
int len = env->GetArrayLength(jca);
env->GetByteArrayRegion(jca,0,len,(jbyte*)p_dst);
env->DeleteLocalRef((jobject)jca);
return len;
}
Error FileAccessJAndroid::get_error() const {
if (eof_reached())
return ERR_FILE_EOF;
return OK;
}
void FileAccessJAndroid::store_8(uint8_t p_dest) {
}
bool FileAccessJAndroid::file_exists(const String& p_path) {
jstring js = env->NewStringUTF(p_path.utf8().get_data());
int res = env->CallIntMethod(io,_file_open,js,false);
if (res<=0)
return false;
env->CallVoidMethod(io,_file_close,res);
return true;
}
void FileAccessJAndroid::setup( JNIEnv *p_env, jobject p_io) {
io=p_io;
env=p_env;
__android_log_print(ANDROID_LOG_INFO,"godot","STEP5");
jclass c = env->GetObjectClass(io);
__android_log_print(ANDROID_LOG_INFO,"godot","STEP6");
cls=(jclass)env->NewGlobalRef(c);
_file_open = env->GetMethodID(cls, "file_open", "(Ljava/lang/String;Z)I");
if(_file_open != 0) {
__android_log_print(ANDROID_LOG_INFO,"godot","*******GOT METHOD _file_open ok!!");
}
_file_get_size = env->GetMethodID(cls, "file_get_size", "(I)I");
if(_file_get_size != 0) {
__android_log_print(ANDROID_LOG_INFO,"godot","*******GOT METHOD _file_get_size ok!!");
}
_file_tell = env->GetMethodID(cls, "file_tell", "(I)I");
if(_file_tell != 0) {
__android_log_print(ANDROID_LOG_INFO,"godot","*******GOT METHOD _file_tell ok!!");
}
_file_eof = env->GetMethodID(cls, "file_eof", "(I)Z");
if(_file_eof != 0) {
__android_log_print(ANDROID_LOG_INFO,"godot","*******GOT METHOD _file_eof ok!!");
}
_file_seek = env->GetMethodID(cls, "file_seek", "(II)V");
if(_file_seek != 0) {
__android_log_print(ANDROID_LOG_INFO,"godot","*******GOT METHOD _file_seek ok!!");
}
_file_read = env->GetMethodID(cls, "file_read", "(II)[B");
if(_file_read != 0) {
__android_log_print(ANDROID_LOG_INFO,"godot","*******GOT METHOD _file_read ok!!");
}
_file_close = env->GetMethodID(cls, "file_close", "(I)V");
if(_file_close != 0) {
__android_log_print(ANDROID_LOG_INFO,"godot","*******GOT METHOD _file_close ok!!");
}
// (*env)->CallVoidMethod(env,obj,aMethodID, myvar);
}
FileAccessJAndroid::FileAccessJAndroid()
{
id=0;
}
FileAccessJAndroid::~FileAccessJAndroid()
{
if (is_open())
close();
}

View File

@@ -0,0 +1,56 @@
#ifndef FILE_ACCESS_JANDROID_H
#define FILE_ACCESS_JANDROID_H
#include "java_glue.h"
#include "os/file_access.h"
class FileAccessJAndroid : public FileAccess {
static jobject io;
static jclass cls;
static jmethodID _file_open;
static jmethodID _file_get_size;
static jmethodID _file_seek;
static jmethodID _file_tell;
static jmethodID _file_eof;
static jmethodID _file_read;
static jmethodID _file_close;
static JNIEnv * env;
int id;
static FileAccess* create_jandroid();
public:
virtual Error open(const String& p_path, int p_mode_flags); ///< open a file
virtual void close(); ///< close a file
virtual bool is_open() const; ///< true when file is open
virtual void seek(size_t p_position); ///< seek to a given position
virtual void seek_end(int64_t p_position=0); ///< seek from the end of file
virtual size_t get_pos() const; ///< get position in the file
virtual size_t get_len() const; ///< get size of the file
virtual bool eof_reached() const; ///< reading passed EOF
virtual uint8_t get_8() const; ///< get a byte
virtual int get_buffer(uint8_t *p_dst, int p_length) const;
virtual Error get_error() const; ///< get last error
virtual void store_8(uint8_t p_dest); ///< store a byte
virtual bool file_exists(const String& p_path); ///< return true if a file exists
static void make_default();
static void setup( JNIEnv *env, jobject io);
static void update_env( JNIEnv *p_env) { env=p_env; }
FileAccessJAndroid();
~FileAccessJAndroid();
};
#endif // FILE_ACCESS_JANDROID_H

View File

@@ -0,0 +1,3 @@
set solib-search-path .
directory ../..
file app_process

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.godot"
android:versionCode="1"
android:versionName="1.0">
<application android:label="@string/godot" android:icon="@drawable/icon">
<activity android:name="Godot"
android:label="@string/godot"
android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
android:launchMode="singleTask"
android:configChanges="orientation|keyboardHidden">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
<uses-feature android:glEsVersion="0x00020000"/>
<uses-sdk android:minSdkVersion="8"/>
</manifest>

View File

@@ -0,0 +1,17 @@
# This file is used to override default values used by the Ant build system.
#
# This file must be checked in Version Control Systems, as it is
# integral to the build system of your project.
# This file is only used by the Ant script.
# You can use this to override default values such as
# 'source.dir' for the location of your java source folder and
# 'out.dir' for the location of your output folder.
# You can also use it define how the release builds are signed by declaring
# the following properties:
# 'key.store' for the location of your keystore and
# 'key.alias' for the name of the key to use.
# The password will be asked during the build when you use the 'release' target.

View File

@@ -0,0 +1,79 @@
<?xml version="1.0" encoding="UTF-8"?>
<project name="Godot" default="help">
<!-- The local.properties file is created and updated by the 'android'
tool.
It contains the path to the SDK. It should *NOT* be checked into
Version Control Systems. -->
<property file="local.properties" />
<property name="asset-dir" value="assets" />
<!-- The build.properties file can be created by you and is never touched
by the 'android' tool. This is the place to change some of the
default property values used by the Ant rules.
Here are some properties you may want to change/update:
source.dir
The name of the source directory. Default is 'src'.
out.dir
The name of the output directory. Default is 'bin'.
Properties related to the SDK location or the project target should
be updated using the 'android' tool with the 'update' action.
This file is an integral part of the build system for your
application and should be checked into Version Control Systems.
-->
<property file="build.properties" />
<!-- The default.properties file is created and updated by the 'android'
tool, as well as ADT.
This file is an integral part of the build system for your
application and should be checked into Version Control Systems. -->
<property file="default.properties" />
<!-- Required pre-setup import -->
<import file="${sdk.dir}/tools/ant/pre_setup.xml" />
<!-- extension targets. Uncomment the ones where you want to do custom work
in between standard targets -->
<!--
<target name="-pre-build">
</target>
<target name="-pre-compile">
</target>
[This is typically used for code obfuscation.
Compiled code location: ${out.classes.absolute.dir}
If this is not done in place, override ${out.dex.input.absolute.dir}]
<target name="-post-compile">
</target>
-->
<!-- Execute the Android Setup task that will setup some properties
specific to the target, and import the build rules files.
The rules file is imported from
<SDK>/tools/ant/
Depending on the project type it can be either:
- main_rules.xml
- lib_rules.xml
- test_rules.xml
To customize existing targets, there are two options:
- Customize only one target:
- copy/paste the target into this file, *before* the
<setup> task.
- customize it to your needs.
- Customize the whole script.
- copy/paste the content of the rules files (minus the top node)
into this file, *after* the <setup> task
- disable the import of the rules by changing the setup task
below to <setup import="false" />.
- customize to your needs.
-->
<setup />
</project>

View File

@@ -0,0 +1,11 @@
# This file is automatically generated by Android Tools.
# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
#
# This file must be checked in Version Control Systems.
#
# To customize properties used by the Ant build system use,
# "build.properties", and override values to adapt the script to your
# project structure.
# Project target.
target=android-8

Binary file not shown.

View File

@@ -0,0 +1,10 @@
# This file is automatically generated by Android Tools.
# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
#
# This file must *NOT* be checked in Version Control Systems,
# as it contains information specific to your local configuration.
# location of the SDK. This is only used by Ant
# For customization when using a Version Control System, please read the
# header note.
sdk.dir=/home/red/bin/android-sdk-linux_x86

View File

@@ -0,0 +1,36 @@
-optimizationpasses 5
-dontusemixedcaseclassnames
-dontskipnonpubliclibraryclasses
-dontpreverify
-verbose
-optimizations !code/simplification/arithmetic,!field/*,!class/merging/*
-keep public class * extends android.app.Activity
-keep public class * extends android.app.Application
-keep public class * extends android.app.Service
-keep public class * extends android.content.BroadcastReceiver
-keep public class * extends android.content.ContentProvider
-keep public class * extends android.app.backup.BackupAgentHelper
-keep public class * extends android.preference.Preference
-keep public class com.android.vending.licensing.ILicensingService
-keepclasseswithmembernames class * {
native <methods>;
}
-keepclasseswithmembernames class * {
public <init>(android.content.Context, android.util.AttributeSet);
}
-keepclasseswithmembernames class * {
public <init>(android.content.Context, android.util.AttributeSet, int);
}
-keepclassmembers enum * {
public static **[] values();
public static ** valueOf(java.lang.String);
}
-keep class * implements android.os.Parcelable {
public static final android.os.Parcelable$Creator *;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="godot">Godot</string>
</resources>

View File

@@ -0,0 +1,85 @@
package com.android.godot;
import android.app.Activity;
import android.os.Bundle;
import android.view.MotionEvent;
public class Godot extends Activity
{
GodotView mView;
static public GodotIO io;
@Override protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
io = new GodotIO(getAssets());
GodotLib.io=io;
mView = new GodotView(getApplication(),io);
setContentView(mView);
}
@Override protected void onPause() {
super.onPause();
mView.onPause();
}
@Override protected void onResume() {
super.onResume();
mView.onResume();
}
@Override public boolean dispatchTouchEvent (MotionEvent event) {
super.onTouchEvent(event);
int evcount=event.getPointerCount();
if (evcount==0)
return true;
int[] arr = new int[event.getPointerCount()*3];
for(int i=0;i<event.getPointerCount();i++) {
arr[i*3+0]=(int)event.getPointerId(i);
arr[i*3+1]=(int)event.getX(i);
arr[i*3+2]=(int)event.getY(i);
}
//System.out.printf("gaction: %d\n",event.getAction());
switch(event.getAction()&MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN: {
GodotLib.touch(0,0,evcount,arr);
//System.out.printf("action down at: %f,%f\n", event.getX(),event.getY());
} break;
case MotionEvent.ACTION_MOVE: {
GodotLib.touch(1,0,evcount,arr);
//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 MotionEvent.ACTION_POINTER_UP: {
int pointer_idx = event.getActionIndex();
GodotLib.touch(4,pointer_idx,evcount,arr);
//System.out.printf("%d - s.up at: %f,%f\n",pointer_idx, event.getX(pointer_idx),event.getY(pointer_idx));
} break;
case MotionEvent.ACTION_POINTER_DOWN: {
int pointer_idx = event.getActionIndex();
GodotLib.touch(3,pointer_idx,evcount,arr);
//System.out.printf("%d - s.down at: %f,%f\n",pointer_idx, event.getX(pointer_idx),event.getY(pointer_idx));
} break;
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP: {
GodotLib.touch(2,0,evcount,arr);
//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 true;
}
}

View File

@@ -0,0 +1,277 @@
package com.android.godot;
import java.util.HashMap;
import android.content.res.AssetManager;
import java.io.InputStream;
import java.io.IOException;
// Wrapper for native library
public class GodotIO {
AssetManager am;
/// FILES
public int last_file_id=1;
class AssetData {
public boolean eof=false;
public String path;
public InputStream is;
public int len;
public int pos;
}
HashMap<Integer,AssetData> streams;
public int file_open(String path,boolean write) {
System.out.printf("file_open: Attempt to Open %s\n",path);
if (write)
return -1;
AssetData ad = new AssetData();
try {
ad.is = am.open(path);
} catch (Exception e) {
System.out.printf("Exception on file_open: %s\n",e);
return -1;
}
try {
ad.len=ad.is.available();
} catch (Exception e) {
System.out.printf("Exception availabling on file_open: %s\n",e);
return -1;
}
ad.path=path;
ad.pos=0;
++last_file_id;
streams.put(last_file_id,ad);
return last_file_id;
}
public int file_get_size(int id) {
if (!streams.containsKey(id)) {
System.out.printf("file_get_size: Invalid file id: %d\n",id);
return -1;
}
return streams.get(id).len;
}
public void file_seek(int id,int bytes) {
if (!streams.containsKey(id)) {
System.out.printf("file_get_size: Invalid file id: %d\n",id);
return;
}
//seek sucks
AssetData ad = streams.get(id);
if (bytes>ad.len)
bytes=ad.len;
if (bytes<0)
bytes=0;
try {
if (bytes > (int)ad.pos) {
int todo=bytes-(int)ad.pos;
while(todo>0) {
todo-=ad.is.skip(todo);
}
ad.pos=bytes;
} else if (bytes<(int)ad.pos) {
ad.is=am.open(ad.path);
ad.pos=bytes;
int todo=bytes;
while(todo>0) {
todo-=ad.is.skip(todo);
}
}
ad.eof=false;
} catch (IOException e) {
System.out.printf("Exception on file_seek: %s\n",e);
return;
}
}
public int file_tell(int id) {
if (!streams.containsKey(id)) {
System.out.printf("file_read: Can't tell eof for invalid file id: %d\n",id);
return 0;
}
AssetData ad = streams.get(id);
return ad.pos;
}
public boolean file_eof(int id) {
if (!streams.containsKey(id)) {
System.out.printf("file_read: Can't check eof for invalid file id: %d\n",id);
return false;
}
AssetData ad = streams.get(id);
return ad.eof;
}
public byte[] file_read(int id, int bytes) {
if (!streams.containsKey(id)) {
System.out.printf("file_read: Can't read invalid file id: %d\n",id);
return new byte[0];
}
AssetData ad = streams.get(id);
if (ad.pos + bytes > ad.len) {
bytes=ad.len-ad.pos;
ad.eof=true;
}
if (bytes==0) {
return new byte[0];
}
byte[] buf1=new byte[bytes];
int r=0;
try {
r = ad.is.read(buf1);
} catch (IOException e) {
System.out.printf("Exception on file_read: %s\n",e);
return new byte[bytes];
}
if (r==0) {
return new byte[0];
}
ad.pos+=r;
if (r<bytes) {
byte[] buf2=new byte[r];
for(int i=0;i<r;i++)
buf2[i]=buf1[i];
return buf2;
} else {
return buf1;
}
}
public void file_close(int id) {
if (!streams.containsKey(id)) {
System.out.printf("file_close: Can't close invalid file id: %d\n",id);
return;
}
streams.remove(id);
}
/// DIRECTORIES
class AssetDir {
public String[] files;
public int current;
}
public int last_dir_id=1;
HashMap<Integer,AssetDir> dirs;
public int dir_open(String path) {
AssetDir ad = new AssetDir();
ad.current=0;
try {
ad.files = am.list(path);
} catch (IOException e) {
System.out.printf("Exception on dir_open: %s\n",e);
return -1;
}
++last_dir_id;
dirs.put(last_dir_id,ad);
return last_dir_id;
}
public String dir_next(int id) {
if (!dirs.containsKey(id)) {
System.out.printf("dir_next: invalid dir id: %d\n",id);
return "";
}
AssetDir ad = dirs.get(id);
if (ad.current>=ad.files.length)
return "";
String r = ad.files[ad.current];
ad.current++;
return r;
}
public void dir_close(int id) {
if (!dirs.containsKey(id)) {
System.out.printf("dir_close: invalid dir id: %d\n",id);
return;
}
dirs.remove(id);
}
GodotIO(AssetManager p_am) {
am=p_am;
streams=new HashMap<Integer,AssetData>();
dirs=new HashMap<Integer,AssetDir>();
}
}

View File

@@ -0,0 +1,22 @@
package com.android.godot;
// Wrapper for native library
public class GodotLib {
public static GodotIO io;
static {
System.loadLibrary("godot_android");
}
/**
* @param width the current view width
* @param height the current view height
*/
public static native void init(int width, int height);
public static native void step();
public static native void touch(int what,int pointer,int howmany, int[] arr);
}

View File

@@ -0,0 +1,319 @@
package com.android.godot;
import android.content.Context;
import android.graphics.PixelFormat;
import android.opengl.GLSurfaceView;
import android.util.AttributeSet;
import android.util.Log;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.content.ContextWrapper;
import java.io.File;
import javax.microedition.khronos.egl.EGL10;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.egl.EGLContext;
import javax.microedition.khronos.egl.EGLDisplay;
import javax.microedition.khronos.opengles.GL10;
/**
* A simple GLSurfaceView sub-class that demonstrate how to perform
* OpenGL ES 2.0 rendering into a GL Surface. Note the following important
* details:
*
* - The class must use a custom context factory to enable 2.0 rendering.
* See ContextFactory class definition below.
*
* - The class must use a custom EGLConfigChooser to be able to select
* an EGLConfig that supports 2.0. This is done by providing a config
* specification to eglChooseConfig() that has the attribute
* EGL10.ELG_RENDERABLE_TYPE containing the EGL_OPENGL_ES2_BIT flag
* set. See ConfigChooser class definition below.
*
* - The class must select the surface's format, then choose an EGLConfig
* that matches it exactly (with regards to red/green/blue/alpha channels
* bit depths). Failure to do so would result in an EGL_BAD_MATCH error.
*/
class GodotView extends GLSurfaceView {
private static String TAG = "GodotView";
private static final boolean DEBUG = false;
private static Context ctx;
private static GodotIO io;
public GodotView(Context context,GodotIO p_io) {
super(context);
ctx=context;
io=p_io;
init(false, 0, 0);
}
public GodotView(Context context, boolean translucent, int depth, int stencil) {
super(context);
init(translucent, depth, stencil);
}
private void init(boolean translucent, int depth, int stencil) {
/* By default, GLSurfaceView() creates a RGB_565 opaque surface.
* If we want a translucent one, we should change the surface's
* format here, using PixelFormat.TRANSLUCENT for GL Surfaces
* is interpreted as any 32-bit surface with alpha by SurfaceFlinger.
*/
if (translucent) {
this.getHolder().setFormat(PixelFormat.TRANSLUCENT);
}
/* Setup the context factory for 2.0 rendering.
* See ContextFactory class definition below
*/
setEGLContextFactory(new ContextFactory());
/* We need to choose an EGLConfig that matches the format of
* our surface exactly. This is going to be done in our
* custom config chooser. See ConfigChooser class definition
* below.
*/
setEGLConfigChooser( translucent ?
new ConfigChooser(8, 8, 8, 8, depth, stencil) :
new ConfigChooser(5, 6, 5, 0, depth, stencil) );
/* Set the renderer responsible for frame rendering */
setRenderer(new Renderer());
}
private static class ContextFactory implements GLSurfaceView.EGLContextFactory {
private static int EGL_CONTEXT_CLIENT_VERSION = 0x3098;
public EGLContext createContext(EGL10 egl, EGLDisplay display, EGLConfig eglConfig) {
Log.w(TAG, "creating OpenGL ES 2.0 context");
checkEglError("Before eglCreateContext", egl);
int[] attrib_list = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL10.EGL_NONE };
EGLContext context = egl.eglCreateContext(display, eglConfig, EGL10.EGL_NO_CONTEXT, attrib_list);
checkEglError("After eglCreateContext", egl);
return context;
}
public void destroyContext(EGL10 egl, EGLDisplay display, EGLContext context) {
egl.eglDestroyContext(display, context);
}
}
private static void checkEglError(String prompt, EGL10 egl) {
int error;
while ((error = egl.eglGetError()) != EGL10.EGL_SUCCESS) {
Log.e(TAG, String.format("%s: EGL error: 0x%x", prompt, error));
}
}
private static class ConfigChooser implements GLSurfaceView.EGLConfigChooser {
public ConfigChooser(int r, int g, int b, int a, int depth, int stencil) {
mRedSize = r;
mGreenSize = g;
mBlueSize = b;
mAlphaSize = a;
mDepthSize = depth;
mStencilSize = stencil;
}
/* This EGL config specification is used to specify 2.0 rendering.
* We use a minimum size of 4 bits for red/green/blue, but will
* perform actual matching in chooseConfig() below.
*/
private static int EGL_OPENGL_ES2_BIT = 4;
private static int[] s_configAttribs2 =
{
EGL10.EGL_RED_SIZE, 4,
EGL10.EGL_GREEN_SIZE, 4,
EGL10.EGL_BLUE_SIZE, 4,
EGL10.EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
EGL10.EGL_NONE
};
public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) {
/* Get the number of minimally matching EGL configurations
*/
int[] num_config = new int[1];
egl.eglChooseConfig(display, s_configAttribs2, null, 0, num_config);
int numConfigs = num_config[0];
if (numConfigs <= 0) {
throw new IllegalArgumentException("No configs match configSpec");
}
/* Allocate then read the array of minimally matching EGL configs
*/
EGLConfig[] configs = new EGLConfig[numConfigs];
egl.eglChooseConfig(display, s_configAttribs2, configs, numConfigs, num_config);
if (DEBUG) {
printConfigs(egl, display, configs);
}
/* Now return the "best" one
*/
return chooseConfig(egl, display, configs);
}
public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display,
EGLConfig[] configs) {
for(EGLConfig config : configs) {
int d = findConfigAttrib(egl, display, config,
EGL10.EGL_DEPTH_SIZE, 0);
int s = findConfigAttrib(egl, display, config,
EGL10.EGL_STENCIL_SIZE, 0);
// We need at least mDepthSize and mStencilSize bits
if (d < mDepthSize || s < mStencilSize)
continue;
// We want an *exact* match for red/green/blue/alpha
int r = findConfigAttrib(egl, display, config,
EGL10.EGL_RED_SIZE, 0);
int g = findConfigAttrib(egl, display, config,
EGL10.EGL_GREEN_SIZE, 0);
int b = findConfigAttrib(egl, display, config,
EGL10.EGL_BLUE_SIZE, 0);
int a = findConfigAttrib(egl, display, config,
EGL10.EGL_ALPHA_SIZE, 0);
if (r == mRedSize && g == mGreenSize && b == mBlueSize && a == mAlphaSize)
return config;
}
return null;
}
private int findConfigAttrib(EGL10 egl, EGLDisplay display,
EGLConfig config, int attribute, int defaultValue) {
if (egl.eglGetConfigAttrib(display, config, attribute, mValue)) {
return mValue[0];
}
return defaultValue;
}
private void printConfigs(EGL10 egl, EGLDisplay display,
EGLConfig[] configs) {
int numConfigs = configs.length;
Log.w(TAG, String.format("%d configurations", numConfigs));
for (int i = 0; i < numConfigs; i++) {
Log.w(TAG, String.format("Configuration %d:\n", i));
printConfig(egl, display, configs[i]);
}
}
private void printConfig(EGL10 egl, EGLDisplay display,
EGLConfig config) {
int[] attributes = {
EGL10.EGL_BUFFER_SIZE,
EGL10.EGL_ALPHA_SIZE,
EGL10.EGL_BLUE_SIZE,
EGL10.EGL_GREEN_SIZE,
EGL10.EGL_RED_SIZE,
EGL10.EGL_DEPTH_SIZE,
EGL10.EGL_STENCIL_SIZE,
EGL10.EGL_CONFIG_CAVEAT,
EGL10.EGL_CONFIG_ID,
EGL10.EGL_LEVEL,
EGL10.EGL_MAX_PBUFFER_HEIGHT,
EGL10.EGL_MAX_PBUFFER_PIXELS,
EGL10.EGL_MAX_PBUFFER_WIDTH,
EGL10.EGL_NATIVE_RENDERABLE,
EGL10.EGL_NATIVE_VISUAL_ID,
EGL10.EGL_NATIVE_VISUAL_TYPE,
0x3030, // EGL10.EGL_PRESERVED_RESOURCES,
EGL10.EGL_SAMPLES,
EGL10.EGL_SAMPLE_BUFFERS,
EGL10.EGL_SURFACE_TYPE,
EGL10.EGL_TRANSPARENT_TYPE,
EGL10.EGL_TRANSPARENT_RED_VALUE,
EGL10.EGL_TRANSPARENT_GREEN_VALUE,
EGL10.EGL_TRANSPARENT_BLUE_VALUE,
0x3039, // EGL10.EGL_BIND_TO_TEXTURE_RGB,
0x303A, // EGL10.EGL_BIND_TO_TEXTURE_RGBA,
0x303B, // EGL10.EGL_MIN_SWAP_INTERVAL,
0x303C, // EGL10.EGL_MAX_SWAP_INTERVAL,
EGL10.EGL_LUMINANCE_SIZE,
EGL10.EGL_ALPHA_MASK_SIZE,
EGL10.EGL_COLOR_BUFFER_TYPE,
EGL10.EGL_RENDERABLE_TYPE,
0x3042 // EGL10.EGL_CONFORMANT
};
String[] names = {
"EGL_BUFFER_SIZE",
"EGL_ALPHA_SIZE",
"EGL_BLUE_SIZE",
"EGL_GREEN_SIZE",
"EGL_RED_SIZE",
"EGL_DEPTH_SIZE",
"EGL_STENCIL_SIZE",
"EGL_CONFIG_CAVEAT",
"EGL_CONFIG_ID",
"EGL_LEVEL",
"EGL_MAX_PBUFFER_HEIGHT",
"EGL_MAX_PBUFFER_PIXELS",
"EGL_MAX_PBUFFER_WIDTH",
"EGL_NATIVE_RENDERABLE",
"EGL_NATIVE_VISUAL_ID",
"EGL_NATIVE_VISUAL_TYPE",
"EGL_PRESERVED_RESOURCES",
"EGL_SAMPLES",
"EGL_SAMPLE_BUFFERS",
"EGL_SURFACE_TYPE",
"EGL_TRANSPARENT_TYPE",
"EGL_TRANSPARENT_RED_VALUE",
"EGL_TRANSPARENT_GREEN_VALUE",
"EGL_TRANSPARENT_BLUE_VALUE",
"EGL_BIND_TO_TEXTURE_RGB",
"EGL_BIND_TO_TEXTURE_RGBA",
"EGL_MIN_SWAP_INTERVAL",
"EGL_MAX_SWAP_INTERVAL",
"EGL_LUMINANCE_SIZE",
"EGL_ALPHA_MASK_SIZE",
"EGL_COLOR_BUFFER_TYPE",
"EGL_RENDERABLE_TYPE",
"EGL_CONFORMANT"
};
int[] value = new int[1];
for (int i = 0; i < attributes.length; i++) {
int attribute = attributes[i];
String name = names[i];
if ( egl.eglGetConfigAttrib(display, config, attribute, value)) {
Log.w(TAG, String.format(" %s: %d\n", name, value[0]));
} else {
// Log.w(TAG, String.format(" %s: failed\n", name));
while (egl.eglGetError() != EGL10.EGL_SUCCESS);
}
}
}
// Subclasses can adjust these values:
protected int mRedSize;
protected int mGreenSize;
protected int mBlueSize;
protected int mAlphaSize;
protected int mDepthSize;
protected int mStencilSize;
private int[] mValue = new int[1];
}
private static class Renderer implements GLSurfaceView.Renderer {
public void onDrawFrame(GL10 gl) {
GodotLib.step();
}
public void onSurfaceChanged(GL10 gl, int width, int height) {
System.out.printf("** CONTENT DIR %s\n",ctx.getFilesDir().getPath());
GodotLib.init(width, height);
}
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
// Do nothing.
}
}
}

View File

@@ -0,0 +1,181 @@
#include "java_glue.h"
#include "os_android.h"
#include "main/main.h"
#include <unistd.h>
#include "file_access_jandroid.h"
#include "dir_access_jandroid.h"
static OS_Android *os_android=NULL;
struct TST {
int a;
TST() {
a=5;
}
};
TST tst;
struct JAndroidPointerEvent {
Vector<OS_Android::TouchPos> points;
int pointer;
int what;
};
static List<JAndroidPointerEvent> pointer_events;
static bool initialized=false;
static Mutex *input_mutex=NULL;
JNIEXPORT void JNICALL Java_com_android_godot_GodotLib_init(JNIEnv * env, jobject obj, jint width, jint height)
{
if (initialized) // wtf
return;
__android_log_print(ANDROID_LOG_INFO,"godot","**INIT EVENT! - %p\n",env);
initialized=true;
__android_log_print(ANDROID_LOG_INFO,"godot","***************** HELLO FROM JNI!!!!!!!!");
{
//setup IO Object
jclass cls = env->FindClass("com/android/godot/Godot");
if (cls) {
cls=(jclass)env->NewGlobalRef(cls);
__android_log_print(ANDROID_LOG_INFO,"godot","*******CLASS FOUND!!!");
}
__android_log_print(ANDROID_LOG_INFO,"godot","STEP2, %p",cls);
jfieldID fid = env->GetStaticFieldID(cls, "io", "Lcom/android/godot/GodotIO;");
__android_log_print(ANDROID_LOG_INFO,"godot","STEP3 %i",fid);
jobject ob = env->GetStaticObjectField(cls,fid);
__android_log_print(ANDROID_LOG_INFO,"godot","STEP4, %p",ob);
jobject gob = env->NewGlobalRef(ob);
FileAccessJAndroid::setup(env,gob);
DirAccessJAndroid::setup(env,gob);
}
os_android = new OS_Android(width,height);
char wd[500];
getcwd(wd,500);
__android_log_print(ANDROID_LOG_INFO,"godot","test construction %i\n",tst.a);
__android_log_print(ANDROID_LOG_INFO,"godot","running from dir %s\n",wd);
__android_log_print(ANDROID_LOG_INFO,"godot","**SETUP");
#if 0
char *args[]={"-test","render",NULL};
__android_log_print(ANDROID_LOG_INFO,"godot","pre asdasd setup...");
Error err = Main::setup("apk",2,args);
#else
Error err = Main::setup("apk",0,NULL);
#endif
if (err!=OK) {
__android_log_print(ANDROID_LOG_INFO,"godot","*****UNABLE TO SETUP");
return; //should exit instead and print the error
}
__android_log_print(ANDROID_LOG_INFO,"godot","**START");
if (!Main::start()) {
return; //should exit instead and print the error
}
input_mutex=Mutex::create();
os_android->main_loop_begin();
}
JNIEXPORT void JNICALL Java_com_android_godot_GodotLib_step(JNIEnv * env, jobject obj)
{
__android_log_print(ANDROID_LOG_INFO,"godot","**STEP EVENT! - %p-%i\n",env,Thread::get_caller_ID());
{
FileAccessJAndroid::update_env(env);
DirAccessJAndroid::update_env(env);
}
input_mutex->lock();
while(pointer_events.size()) {
JAndroidPointerEvent jpe=pointer_events.front()->get();
os_android->process_touch(jpe.what,jpe.pointer,jpe.points);
pointer_events.pop_front();
}
input_mutex->unlock();
if (os_android->main_loop_iterate()==true) {
return; //should exit instead
}
}
JNIEXPORT void JNICALL Java_com_android_godot_GodotLib_touch(JNIEnv * env, jobject obj, jint ev,jint pointer, jint count, jintArray positions) {
__android_log_print(ANDROID_LOG_INFO,"godot","**TOUCH EVENT! - %p-%i\n",env,Thread::get_caller_ID());
Vector<OS_Android::TouchPos> points;
for(int i=0;i<count;i++) {
jint p[3];
env->GetIntArrayRegion(positions,i*3,3,p);
OS_Android::TouchPos tp;
tp.pos=Point2(p[1],p[2]);
tp.id=p[0];
points.push_back(tp);
}
JAndroidPointerEvent jpe;
jpe.pointer=pointer;
jpe.points=points;
jpe.what=ev;
input_mutex->lock();
pointer_events.push_back(jpe);
input_mutex->unlock();
//if (os_android)
// os_android->process_touch(ev,pointer,points);
}
//Main::cleanup();
//return os.get_exit_code();

View File

@@ -0,0 +1,16 @@
#ifndef JAVA_GLUE_H
#define JAVA_GLUE_H
#include <jni.h>
#include <android/log.h>
extern "C" {
JNIEXPORT void JNICALL Java_com_android_godot_GodotLib_init(JNIEnv * env, jobject obj, jint width, jint height);
JNIEXPORT void JNICALL Java_com_android_godot_GodotLib_step(JNIEnv * env, jobject obj);
JNIEXPORT void JNICALL Java_com_android_godot_GodotLib_touch(JNIEnv * env, jobject obj, jint ev,jint pointer, jint count, jintArray positions);
};
#endif // JAVA_GLUE_H

View File

@@ -0,0 +1,426 @@
#include "os_android.h"
#include "java_glue.h"
#include "drivers/gles2/rasterizer_gles2.h"
#include "servers/visual/visual_server_raster.h"
#include "file_access_jandroid.h"
#include "dir_access_jandroid.h"
#include "core/io/file_access_buffered_fa.h"
#include "main/main.h"
int OS_Android::get_video_driver_count() const {
return 1;
}
const char * OS_Android::get_video_driver_name(int p_driver) const {
return "GLES2";
}
OS::VideoMode OS_Android::get_default_video_mode() const {
return OS::VideoMode();
}
int OS_Android::get_audio_driver_count() const {
return 1;
}
const char * OS_Android::get_audio_driver_name(int p_driver) const {
return "Android";
}
void OS_Android::initialize_core() {
OS_Unix::initialize_core();
//FileAccessJAndroid::make_default();
DirAccessJAndroid::make_default();
FileAccessBufferedFA<FileAccessJAndroid>::make_default();
}
void OS_Android::initialize(const VideoMode& p_desired,int p_video_driver,int p_audio_driver) {
AudioDriverManagerSW::add_driver(&audio_driver_android);
rasterizer = memnew( RasterizerGLES2 );
visual_server = memnew( VisualServerRaster(rasterizer) );
visual_server->init();
visual_server->cursor_set_visible(false, 0);
AudioDriverManagerSW::get_driver(p_audio_driver)->set_singleton();
if (AudioDriverManagerSW::get_driver(p_audio_driver)->init()!=OK) {
ERR_PRINT("Initializing audio failed.");
}
sample_manager = memnew( SampleManagerMallocSW );
audio_server = memnew( AudioServerSW(sample_manager) );
audio_server->set_mixer_params(AudioMixerSW::INTERPOLATION_LINEAR,false);
audio_server->init();
spatial_sound_server = memnew( SpatialSoundServerSW );
spatial_sound_server->init();
spatial_sound_2d_server = memnew( SpatialSound2DServerSW );
spatial_sound_2d_server->init();
//
physics_server = memnew( PhysicsServerSW );
physics_server->init();
physics_2d_server = memnew( Physics2DServerSW );
physics_2d_server->init();
input = memnew( InputDefault );
}
void OS_Android::set_main_loop( MainLoop * p_main_loop ) {
main_loop=p_main_loop;
}
void OS_Android::delete_main_loop() {
memdelete( main_loop );
}
void OS_Android::finalize() {
memdelete(input);
}
void OS_Android::vprint(const char* p_format, va_list p_list, bool p_stderr) {
__android_log_vprint(p_stderr?ANDROID_LOG_ERROR:ANDROID_LOG_INFO,"godot",p_format,p_list);
}
void OS_Android::print(const char *p_format, ... ) {
va_list argp;
va_start(argp, p_format);
__android_log_vprint(ANDROID_LOG_INFO,"godot",p_format,argp);
va_end(argp);
}
void OS_Android::alert(const String& p_alert) {
print("ALERT: %s\n",p_alert.utf8().get_data());
}
void OS_Android::set_mouse_show(bool p_show) {
//android has no mouse...
}
void OS_Android::set_mouse_grab(bool p_grab) {
//it really has no mouse...!
}
bool OS_Android::is_mouse_grab_enabled() const {
//*sigh* technology has evolved so much since i was a kid..
return false;
}
Point2 OS_Android::get_mouse_pos() const {
return Point2();
}
int OS_Android::get_mouse_button_state() const {
return 0;
}
void OS_Android::set_window_title(const String& p_title) {
}
//interesting byt not yet
//void set_clipboard(const String& p_text);
//String get_clipboard() const;
void OS_Android::set_video_mode(const VideoMode& p_video_mode,int p_screen) {
}
OS::VideoMode OS_Android::get_video_mode(int p_screen) const {
return default_videomode;
}
void OS_Android::get_fullscreen_mode_list(List<VideoMode> *p_list,int p_screen) const {
p_list->push_back(default_videomode);
}
String OS_Android::get_name() {
return "Android";
}
MainLoop *OS_Android::get_main_loop() const {
return main_loop;
}
bool OS_Android::can_draw() const {
return true; //always?
}
void OS_Android::set_cursor_shape(CursorShape p_shape) {
//android really really really has no mouse.. how amazing..
}
void OS_Android::main_loop_begin() {
if (main_loop)
main_loop->init();
}
bool OS_Android::main_loop_iterate() {
if (!main_loop)
return false;
return Main::iteration();
}
void OS_Android::main_loop_end() {
if (main_loop)
main_loop->finish();
}
void OS_Android::process_touch(int p_what,int p_pointer, const Vector<TouchPos>& p_points) {
switch(p_what) {
case 0: { //gesture begin
if (touch.size()) {
//end all if exist
InputEvent ev;
ev.type=InputEvent::MOUSE_BUTTON;
ev.ID=++last_id;
ev.mouse_button.button_index=BUTTON_LEFT;
ev.mouse_button.button_mask=BUTTON_MASK_LEFT;
ev.mouse_button.pressed=false;
ev.mouse_button.x=touch[0].pos.x;
ev.mouse_button.y=touch[0].pos.y;
ev.mouse_button.global_x=touch[0].pos.x;
ev.mouse_button.global_y=touch[0].pos.y;
main_loop->input_event(ev);
for(int i=0;i<touch.size();i++) {
InputEvent ev;
ev.type=InputEvent::SCREEN_TOUCH;
ev.ID=++last_id;
ev.screen_touch.index=touch[i].id;
ev.screen_touch.pressed=false;
ev.screen_touch.x=touch[i].pos.x;
ev.screen_touch.y=touch[i].pos.y;
main_loop->input_event(ev);
}
}
touch.resize(p_points.size());
for(int i=0;i<p_points.size();i++) {
touch[i].id=p_points[i].id;
touch[i].pos=p_points[i].pos;
}
{
//send mouse
InputEvent ev;
ev.type=InputEvent::MOUSE_BUTTON;
ev.ID=++last_id;
ev.mouse_button.button_index=BUTTON_LEFT;
ev.mouse_button.button_mask=BUTTON_MASK_LEFT;
ev.mouse_button.pressed=true;
ev.mouse_button.x=touch[0].pos.x;
ev.mouse_button.y=touch[0].pos.y;
ev.mouse_button.global_x=touch[0].pos.x;
ev.mouse_button.global_y=touch[0].pos.y;
last_mouse=touch[0].pos;
main_loop->input_event(ev);
}
//send touch
for(int i=0;i<touch.size();i++) {
InputEvent ev;
ev.type=InputEvent::SCREEN_TOUCH;
ev.ID=++last_id;
ev.screen_touch.index=touch[i].id;
ev.screen_touch.pressed=true;
ev.screen_touch.x=touch[i].pos.x;
ev.screen_touch.y=touch[i].pos.y;
main_loop->input_event(ev);
}
} break;
case 1: { //motion
if (p_points.size()) {
//send mouse, should look for point 0?
InputEvent ev;
ev.type=InputEvent::MOUSE_MOTION;
ev.ID=++last_id;
ev.mouse_motion.button_mask=BUTTON_MASK_LEFT;
ev.mouse_motion.x=p_points[0].pos.x;
ev.mouse_motion.y=p_points[0].pos.y;
input->set_mouse_pos(Point2(ev.mouse_motion.x,ev.mouse_motion.y));
ev.mouse_motion.speed_x=input->get_mouse_speed().x;
ev.mouse_motion.speed_y=input->get_mouse_speed().y;
ev.mouse_motion.relative_x=p_points[0].pos.x-last_mouse.x;
ev.mouse_motion.relative_y=p_points[0].pos.y-last_mouse.y;
last_mouse=p_points[0].pos;
main_loop->input_event(ev);
}
ERR_FAIL_COND(touch.size()!=p_points.size());
for(int i=0;i<touch.size();i++) {
int idx=-1;
for(int j=0;j<p_points.size();j++) {
if (touch[i].id==p_points[j].id) {
idx=j;
break;
}
}
ERR_CONTINUE(idx==-1);
if (touch[i].pos==p_points[idx].pos)
continue; //no move unncesearily
InputEvent ev;
ev.type=InputEvent::SCREEN_DRAG;
ev.ID=++last_id;
ev.screen_drag.index=touch[i].id;
ev.screen_drag.x=p_points[idx].pos.x;
ev.screen_drag.y=p_points[idx].pos.y;
ev.screen_drag.x=p_points[idx].pos.x - touch[i].pos.x;
ev.screen_drag.y=p_points[idx].pos.y - touch[i].pos.y;
main_loop->input_event(ev);
touch[i].pos=p_points[idx].pos;
}
} break;
case 2: { //release
if (touch.size()) {
//end all if exist
InputEvent ev;
ev.type=InputEvent::MOUSE_BUTTON;
ev.ID=++last_id;
ev.mouse_button.button_index=BUTTON_LEFT;
ev.mouse_button.button_mask=BUTTON_MASK_LEFT;
ev.mouse_button.pressed=false;
ev.mouse_button.x=touch[0].pos.x;
ev.mouse_button.y=touch[0].pos.y;
ev.mouse_button.global_x=touch[0].pos.x;
ev.mouse_button.global_y=touch[0].pos.y;
main_loop->input_event(ev);
for(int i=0;i<touch.size();i++) {
InputEvent ev;
ev.type=InputEvent::SCREEN_TOUCH;
ev.ID=++last_id;
ev.screen_touch.index=touch[i].id;
ev.screen_touch.pressed=false;
ev.screen_touch.x=touch[i].pos.x;
ev.screen_touch.y=touch[i].pos.y;
main_loop->input_event(ev);
}
}
} break;
case 3: { // add tuchi
ERR_FAIL_INDEX(p_pointer,p_points.size());
TouchPos tp=p_points[p_pointer];
touch.push_back(tp);
InputEvent ev;
ev.type=InputEvent::SCREEN_TOUCH;
ev.ID=++last_id;
ev.screen_touch.index=tp.id;
ev.screen_touch.pressed=true;
ev.screen_touch.x=tp.pos.x;
ev.screen_touch.y=tp.pos.y;
main_loop->input_event(ev);
} break;
case 4: {
for(int i=0;i<touch.size();i++) {
if (touch[i].id==p_pointer) {
InputEvent ev;
ev.type=InputEvent::SCREEN_TOUCH;
ev.ID=++last_id;
ev.screen_touch.index=touch[i].id;
ev.screen_touch.pressed=false;
ev.screen_touch.x=touch[i].pos.x;
ev.screen_touch.y=touch[i].pos.y;
main_loop->input_event(ev);
touch.remove(i);
i--;
}
}
} break;
}
}
OS_Android::OS_Android(int p_video_width,int p_video_height) {
default_videomode.width=p_video_width;
default_videomode.height=p_video_height;
default_videomode.fullscreen=true;
default_videomode.resizable=false;
main_loop=NULL;
last_id=1;
}
OS_Android::~OS_Android() {
}

View File

@@ -0,0 +1,108 @@
#ifndef OS_ANDROID_H
#define OS_ANDROID_H
#include "os/input.h"
#include "drivers/unix/os_unix.h"
#include "os/main_loop.h"
#include "servers/physics/physics_server_sw.h"
#include "servers/spatial_sound/spatial_sound_server_sw.h"
#include "servers/spatial_sound_2d/spatial_sound_2d_server_sw.h"
#include "servers/audio/audio_server_sw.h"
#include "servers/physics_2d/physics_2d_server_sw.h"
#include "servers/visual/rasterizer.h"
#include "audio_driver_android.h"
class OS_Android : public OS_Unix {
public:
struct TouchPos {
int id;
Point2 pos;
};
private:
Vector<TouchPos> touch;
Point2 last_mouse;
unsigned int last_id;
Rasterizer *rasterizer;
VisualServer *visual_server;
// AudioDriverPSP audio_driver_psp;
AudioServerSW *audio_server;
SampleManagerMallocSW *sample_manager;
SpatialSoundServerSW *spatial_sound_server;
SpatialSound2DServerSW *spatial_sound_2d_server;
PhysicsServer *physics_server;
Physics2DServer *physics_2d_server;
AudioDriverAndroid audio_driver_android;
InputDefault *input;
VideoMode default_videomode;
MainLoop * main_loop;
public:
void initialize_core();
// functions used by main to initialize/deintialize the OS
virtual int get_video_driver_count() const;
virtual const char * get_video_driver_name(int p_driver) const;
virtual VideoMode get_default_video_mode() const;
virtual int get_audio_driver_count() const;
virtual const char * get_audio_driver_name(int p_driver) const;
virtual void initialize(const VideoMode& p_desired,int p_video_driver,int p_audio_driver);
virtual void set_main_loop( MainLoop * p_main_loop );
virtual void delete_main_loop();
virtual void finalize();
typedef int64_t ProcessID;
static OS* get_singleton();
virtual void vprint(const char* p_format, va_list p_list, bool p_stderr=false);
virtual void print(const char *p_format, ... );
virtual void alert(const String& p_alert);
virtual void set_mouse_show(bool p_show);
virtual void set_mouse_grab(bool p_grab);
virtual bool is_mouse_grab_enabled() const;
virtual Point2 get_mouse_pos() const;
virtual int get_mouse_button_state() const;
virtual void set_window_title(const String& p_title);
//virtual void set_clipboard(const String& p_text);
//virtual String get_clipboard() const;
virtual void set_screen_orientation(ScreenOrientation p_orientation);
virtual void set_video_mode(const VideoMode& p_video_mode,int p_screen=0);
virtual VideoMode get_video_mode(int p_screen=0) const;
virtual void get_fullscreen_mode_list(List<VideoMode> *p_list,int p_screen=0) const;
virtual String get_name();
virtual MainLoop *get_main_loop() const;
virtual bool can_draw() const;
virtual void set_cursor_shape(CursorShape p_shape);
void main_loop_begin();
bool main_loop_iterate();
void main_loop_end();
void process_touch(int p_what,int p_pointer, const Vector<TouchPos>& p_points);
OS_Android(int p_video_width,int p_video_height);
~OS_Android();
};
#endif

View File

@@ -0,0 +1 @@
#include <alloca.h>

View File

@@ -0,0 +1,40 @@
# com.android.godot
# for this, need to push gdbserver to device
# adb push [location]/gdbserver /data/local
# run
DEBUG_PORT=5039
GDB_PATH="$ANDROID_NDK_ROOT/toolchains/arm-linux-androideabi-4.4.3/prebuilt/linux-x86/bin/arm-linux-androideabi-gdb"
#kill existing previous gdbserver if exists
adb shell killall gdbserver
run-as com.android.godot killall com.android.godot
run-as com.android.godot killall gdbserver
#get data dir of the app
adb pull /system/bin/app_process app_process
DATA_DIR=`adb shell run-as com.android.godot /system/bin/sh -c pwd | tr -d '\n\r'`
echo "DATA DIR IS $DATA_DIR"
#start app
adb shell am start -n com.android.godot/com.android.godot.Godot
#get the pid of the app
PID=`adb shell pidof com.android.godot | tr -d '\n\r'`
echo "PID IS: $PID hoho"
#launch gdbserver
DEBUG_SOCKET=debug-socket
#echo adb shell /data/local/gdbserver +debug-socket --attach $PID
adb shell run-as com.android.godot lib/gdbserver +$DEBUG_SOCKET --attach $PID &
sleep 2s
#adb shell /data/local/gdbserver localhost:$DEBUG_PORT --attach $PID &
#setup network connection
adb forward tcp:$DEBUG_PORT localfilesystem:$DATA_DIR/$DEBUG_SOCKET
cp gdb.setup.base gdb.setup
echo "target remote :$DEBUG_PORT" >> gdb.setup
#echo "file
$GDB_PATH -x gdb.setup

View File

@@ -0,0 +1,40 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.godot.game"
android:versionCode="1"
android:versionName="1.0"
android:installLocation="preferExternal"
>
<application android:label="@string/godot_project_name_string" android:icon="@drawable/icon">
<activity android:name="com.android.godot.Godot"
android:label="@string/godot_project_name_string"
android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
android:launchMode="singleTask"
android:screenOrientation="landscape"
android:configChanges="orientation|keyboardHidden">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
$$ADD_APPLICATION_CHUNKS$$
</application>
<uses-feature android:glEsVersion="0x00020000"/>
<uses-permission android:name="android.permission.INTERNET"></uses-permission>
<uses-permission android:name="android.permission.READ_CONTACTS"/>
<uses-permission android:name="com.android.vending.BILLING" />
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.RECEIVE_SMS"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-sdk android:minSdkVersion="8" android:targetSdkVersion="11"/>
</manifest>

66
platform/android/SCsub Normal file
View File

@@ -0,0 +1,66 @@
import shutil
Import('env')
android_files = [
'os_android.cpp',
'godot_android.cpp',
'file_access_android.cpp',
'dir_access_android.cpp',
'audio_driver_android.cpp',
'file_access_jandroid.cpp',
'dir_access_jandroid.cpp',
'thread_jandroid.cpp',
'audio_driver_jandroid.cpp',
'android_native_app_glue.c',
'java_glue.cpp'
]
#env.Depends('#core/math/vector3.h', 'vector3_psp.h')
#obj = env.SharedObject('godot_android.cpp')
env_android = env.Clone()
if env['target'] == "profile":
env_android.Append(CPPFLAGS=['-DPROFILER_ENABLED'])
android_objects=[]
for x in android_files:
android_objects.append( env_android.SharedObject( x ) )
prog = None
abspath=env.Dir(".").abspath
pp_basein = open(abspath+"/project.properties.template","rb")
pp_baseout = open(abspath+"/java/project.properties","wb")
pp_baseout.write( pp_basein.read() )
refcount=1
for x in env.android_source_modules:
pp_baseout.write("android.library.reference."+str(refcount)+"="+x+"\n")
refcount+=1
pp_baseout.close()
pp_basein = open(abspath+"/AndroidManifest.xml.template","rb")
pp_baseout = open(abspath+"/java/AndroidManifest.xml","wb")
manifest = pp_basein.read()
manifest = manifest.replace("$$ADD_APPLICATION_CHUNKS$$",env.android_manifest_chunk)
pp_baseout.write( manifest )
for x in env.android_source_files:
shutil.copy(x,abspath+"/java/src/com/android/godot")
for x in env.android_module_libraries:
shutil.copy(x,abspath+"/java/libs")
env_android.SharedLibrary("#platform/android/libgodot_android.so",[android_objects])
env.Command('#bin/libgodot_android.so', '#platform/android/libgodot_android.so', Copy('bin/libgodot_android.so', 'platform/android/libgodot_android.so'))

View File

@@ -0,0 +1,437 @@
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
#ifdef ANDROID_NATIVE_ACTIVITY
#include <jni.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <sys/resource.h>
#include "android_native_app_glue.h"
#include <android/log.h>
#define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, "threaded_app", __VA_ARGS__))
static void free_saved_state(struct android_app* android_app) {
pthread_mutex_lock(&android_app->mutex);
if (android_app->savedState != NULL) {
free(android_app->savedState);
android_app->savedState = NULL;
android_app->savedStateSize = 0;
}
pthread_mutex_unlock(&android_app->mutex);
}
int8_t android_app_read_cmd(struct android_app* android_app) {
int8_t cmd;
if (read(android_app->msgread, &cmd, sizeof(cmd)) == sizeof(cmd)) {
switch (cmd) {
case APP_CMD_SAVE_STATE:
free_saved_state(android_app);
break;
}
return cmd;
} else {
LOGI("No data on command pipe!");
}
return -1;
}
static void print_cur_config(struct android_app* android_app) {
char lang[2], country[2];
AConfiguration_getLanguage(android_app->config, lang);
AConfiguration_getCountry(android_app->config, country);
LOGI("Config: mcc=%d mnc=%d lang=%c%c cnt=%c%c orien=%d touch=%d dens=%d "
"keys=%d nav=%d keysHid=%d navHid=%d sdk=%d size=%d long=%d "
"modetype=%d modenight=%d",
AConfiguration_getMcc(android_app->config),
AConfiguration_getMnc(android_app->config),
lang[0], lang[1], country[0], country[1],
AConfiguration_getOrientation(android_app->config),
AConfiguration_getTouchscreen(android_app->config),
AConfiguration_getDensity(android_app->config),
AConfiguration_getKeyboard(android_app->config),
AConfiguration_getNavigation(android_app->config),
AConfiguration_getKeysHidden(android_app->config),
AConfiguration_getNavHidden(android_app->config),
AConfiguration_getSdkVersion(android_app->config),
AConfiguration_getScreenSize(android_app->config),
AConfiguration_getScreenLong(android_app->config),
AConfiguration_getUiModeType(android_app->config),
AConfiguration_getUiModeNight(android_app->config));
}
void android_app_pre_exec_cmd(struct android_app* android_app, int8_t cmd) {
switch (cmd) {
case APP_CMD_INPUT_CHANGED:
LOGI("APP_CMD_INPUT_CHANGED\n");
pthread_mutex_lock(&android_app->mutex);
if (android_app->inputQueue != NULL) {
AInputQueue_detachLooper(android_app->inputQueue);
}
android_app->inputQueue = android_app->pendingInputQueue;
if (android_app->inputQueue != NULL) {
LOGI("Attaching input queue to looper");
AInputQueue_attachLooper(android_app->inputQueue,
android_app->looper, LOOPER_ID_INPUT, NULL,
&android_app->inputPollSource);
}
pthread_cond_broadcast(&android_app->cond);
pthread_mutex_unlock(&android_app->mutex);
break;
case APP_CMD_INIT_WINDOW:
LOGI("APP_CMD_INIT_WINDOW\n");
pthread_mutex_lock(&android_app->mutex);
android_app->window = android_app->pendingWindow;
pthread_cond_broadcast(&android_app->cond);
pthread_mutex_unlock(&android_app->mutex);
break;
case APP_CMD_TERM_WINDOW:
LOGI("APP_CMD_TERM_WINDOW\n");
pthread_cond_broadcast(&android_app->cond);
break;
case APP_CMD_RESUME:
case APP_CMD_START:
case APP_CMD_PAUSE:
case APP_CMD_STOP:
LOGI("activityState=%d\n", cmd);
pthread_mutex_lock(&android_app->mutex);
android_app->activityState = cmd;
pthread_cond_broadcast(&android_app->cond);
pthread_mutex_unlock(&android_app->mutex);
break;
case APP_CMD_CONFIG_CHANGED:
LOGI("APP_CMD_CONFIG_CHANGED\n");
AConfiguration_fromAssetManager(android_app->config,
android_app->activity->assetManager);
print_cur_config(android_app);
break;
case APP_CMD_DESTROY:
LOGI("APP_CMD_DESTROY\n");
android_app->destroyRequested = 1;
break;
}
}
void android_app_post_exec_cmd(struct android_app* android_app, int8_t cmd) {
switch (cmd) {
case APP_CMD_TERM_WINDOW:
LOGI("APP_CMD_TERM_WINDOW\n");
pthread_mutex_lock(&android_app->mutex);
android_app->window = NULL;
pthread_cond_broadcast(&android_app->cond);
pthread_mutex_unlock(&android_app->mutex);
break;
case APP_CMD_SAVE_STATE:
LOGI("APP_CMD_SAVE_STATE\n");
pthread_mutex_lock(&android_app->mutex);
android_app->stateSaved = 1;
pthread_cond_broadcast(&android_app->cond);
pthread_mutex_unlock(&android_app->mutex);
break;
case APP_CMD_RESUME:
free_saved_state(android_app);
break;
}
}
void app_dummy() {
}
static void android_app_destroy(struct android_app* android_app) {
LOGI("android_app_destroy!");
free_saved_state(android_app);
pthread_mutex_lock(&android_app->mutex);
if (android_app->inputQueue != NULL) {
AInputQueue_detachLooper(android_app->inputQueue);
}
AConfiguration_delete(android_app->config);
android_app->destroyed = 1;
pthread_cond_broadcast(&android_app->cond);
pthread_mutex_unlock(&android_app->mutex);
// Can't touch android_app object after this.
}
static void process_input(struct android_app* app, struct android_poll_source* source) {
AInputEvent* event = NULL;
if (AInputQueue_getEvent(app->inputQueue, &event) >= 0) {
LOGI("New input event: type=%d\n", AInputEvent_getType(event));
if (AInputQueue_preDispatchEvent(app->inputQueue, event)) {
return;
}
int32_t handled = 0;
if (app->onInputEvent != NULL) handled = app->onInputEvent(app, event);
AInputQueue_finishEvent(app->inputQueue, event, handled);
} else {
LOGI("Failure reading next input event: %s\n", strerror(errno));
}
}
static void process_cmd(struct android_app* app, struct android_poll_source* source) {
int8_t cmd = android_app_read_cmd(app);
android_app_pre_exec_cmd(app, cmd);
if (app->onAppCmd != NULL) app->onAppCmd(app, cmd);
android_app_post_exec_cmd(app, cmd);
}
static void* android_app_entry(void* param) {
struct android_app* android_app = (struct android_app*)param;
android_app->config = AConfiguration_new();
AConfiguration_fromAssetManager(android_app->config, android_app->activity->assetManager);
print_cur_config(android_app);
android_app->cmdPollSource.id = LOOPER_ID_MAIN;
android_app->cmdPollSource.app = android_app;
android_app->cmdPollSource.process = process_cmd;
android_app->inputPollSource.id = LOOPER_ID_INPUT;
android_app->inputPollSource.app = android_app;
android_app->inputPollSource.process = process_input;
ALooper* looper = ALooper_prepare(ALOOPER_PREPARE_ALLOW_NON_CALLBACKS);
ALooper_addFd(looper, android_app->msgread, LOOPER_ID_MAIN, ALOOPER_EVENT_INPUT, NULL,
&android_app->cmdPollSource);
android_app->looper = looper;
pthread_mutex_lock(&android_app->mutex);
android_app->running = 1;
pthread_cond_broadcast(&android_app->cond);
pthread_mutex_unlock(&android_app->mutex);
android_main(android_app);
android_app_destroy(android_app);
return NULL;
}
// --------------------------------------------------------------------
// Native activity interaction (called from main thread)
// --------------------------------------------------------------------
static struct android_app* android_app_create(ANativeActivity* activity,
void* savedState, size_t savedStateSize) {
struct android_app* android_app = (struct android_app*)malloc(sizeof(struct android_app));
memset(android_app, 0, sizeof(struct android_app));
android_app->activity = activity;
pthread_mutex_init(&android_app->mutex, NULL);
pthread_cond_init(&android_app->cond, NULL);
if (savedState != NULL) {
android_app->savedState = malloc(savedStateSize);
android_app->savedStateSize = savedStateSize;
memcpy(android_app->savedState, savedState, savedStateSize);
}
int msgpipe[2];
if (pipe(msgpipe)) {
LOGI("could not create pipe: %s", strerror(errno));
}
android_app->msgread = msgpipe[0];
android_app->msgwrite = msgpipe[1];
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
pthread_create(&android_app->thread, &attr, android_app_entry, android_app);
// Wait for thread to start.
pthread_mutex_lock(&android_app->mutex);
while (!android_app->running) {
pthread_cond_wait(&android_app->cond, &android_app->mutex);
}
pthread_mutex_unlock(&android_app->mutex);
return android_app;
}
static void android_app_write_cmd(struct android_app* android_app, int8_t cmd) {
if (write(android_app->msgwrite, &cmd, sizeof(cmd)) != sizeof(cmd)) {
LOGI("Failure writing android_app cmd: %s\n", strerror(errno));
}
}
static void android_app_set_input(struct android_app* android_app, AInputQueue* inputQueue) {
pthread_mutex_lock(&android_app->mutex);
android_app->pendingInputQueue = inputQueue;
android_app_write_cmd(android_app, APP_CMD_INPUT_CHANGED);
while (android_app->inputQueue != android_app->pendingInputQueue) {
pthread_cond_wait(&android_app->cond, &android_app->mutex);
}
pthread_mutex_unlock(&android_app->mutex);
}
static void android_app_set_window(struct android_app* android_app, ANativeWindow* window) {
pthread_mutex_lock(&android_app->mutex);
if (android_app->pendingWindow != NULL) {
android_app_write_cmd(android_app, APP_CMD_TERM_WINDOW);
}
android_app->pendingWindow = window;
if (window != NULL) {
android_app_write_cmd(android_app, APP_CMD_INIT_WINDOW);
}
while (android_app->window != android_app->pendingWindow) {
pthread_cond_wait(&android_app->cond, &android_app->mutex);
}
pthread_mutex_unlock(&android_app->mutex);
}
static void android_app_set_activity_state(struct android_app* android_app, int8_t cmd) {
pthread_mutex_lock(&android_app->mutex);
android_app_write_cmd(android_app, cmd);
while (android_app->activityState != cmd) {
pthread_cond_wait(&android_app->cond, &android_app->mutex);
}
pthread_mutex_unlock(&android_app->mutex);
}
static void android_app_free(struct android_app* android_app) {
pthread_mutex_lock(&android_app->mutex);
android_app_write_cmd(android_app, APP_CMD_DESTROY);
while (!android_app->destroyed) {
pthread_cond_wait(&android_app->cond, &android_app->mutex);
}
pthread_mutex_unlock(&android_app->mutex);
close(android_app->msgread);
close(android_app->msgwrite);
pthread_cond_destroy(&android_app->cond);
pthread_mutex_destroy(&android_app->mutex);
free(android_app);
}
static void onDestroy(ANativeActivity* activity) {
LOGI("Destroy: %p\n", activity);
android_app_free((struct android_app*)activity->instance);
}
static void onStart(ANativeActivity* activity) {
LOGI("Start: %p\n", activity);
android_app_set_activity_state((struct android_app*)activity->instance, APP_CMD_START);
}
static void onResume(ANativeActivity* activity) {
LOGI("Resume: %p\n", activity);
android_app_set_activity_state((struct android_app*)activity->instance, APP_CMD_RESUME);
}
static void* onSaveInstanceState(ANativeActivity* activity, size_t* outLen) {
struct android_app* android_app = (struct android_app*)activity->instance;
void* savedState = NULL;
LOGI("SaveInstanceState: %p\n", activity);
pthread_mutex_lock(&android_app->mutex);
android_app->stateSaved = 0;
android_app_write_cmd(android_app, APP_CMD_SAVE_STATE);
while (!android_app->stateSaved) {
pthread_cond_wait(&android_app->cond, &android_app->mutex);
}
if (android_app->savedState != NULL) {
savedState = android_app->savedState;
*outLen = android_app->savedStateSize;
android_app->savedState = NULL;
android_app->savedStateSize = 0;
}
pthread_mutex_unlock(&android_app->mutex);
return savedState;
}
static void onPause(ANativeActivity* activity) {
LOGI("Pause: %p\n", activity);
android_app_set_activity_state((struct android_app*)activity->instance, APP_CMD_PAUSE);
}
static void onStop(ANativeActivity* activity) {
LOGI("Stop: %p\n", activity);
android_app_set_activity_state((struct android_app*)activity->instance, APP_CMD_STOP);
}
static void onConfigurationChanged(ANativeActivity* activity) {
struct android_app* android_app = (struct android_app*)activity->instance;
LOGI("ConfigurationChanged: %p\n", activity);
android_app_write_cmd(android_app, APP_CMD_CONFIG_CHANGED);
}
static void onLowMemory(ANativeActivity* activity) {
struct android_app* android_app = (struct android_app*)activity->instance;
LOGI("LowMemory: %p\n", activity);
android_app_write_cmd(android_app, APP_CMD_LOW_MEMORY);
}
static void onWindowFocusChanged(ANativeActivity* activity, int focused) {
LOGI("WindowFocusChanged: %p -- %d\n", activity, focused);
android_app_write_cmd((struct android_app*)activity->instance,
focused ? APP_CMD_GAINED_FOCUS : APP_CMD_LOST_FOCUS);
}
static void onNativeWindowCreated(ANativeActivity* activity, ANativeWindow* window) {
LOGI("NativeWindowCreated: %p -- %p\n", activity, window);
android_app_set_window((struct android_app*)activity->instance, window);
}
static void onNativeWindowDestroyed(ANativeActivity* activity, ANativeWindow* window) {
LOGI("NativeWindowDestroyed: %p -- %p\n", activity, window);
android_app_set_window((struct android_app*)activity->instance, NULL);
}
static void onInputQueueCreated(ANativeActivity* activity, AInputQueue* queue) {
LOGI("InputQueueCreated: %p -- %p\n", activity, queue);
android_app_set_input((struct android_app*)activity->instance, queue);
}
static void onInputQueueDestroyed(ANativeActivity* activity, AInputQueue* queue) {
LOGI("InputQueueDestroyed: %p -- %p\n", activity, queue);
android_app_set_input((struct android_app*)activity->instance, NULL);
}
void ANativeActivity_onCreate(ANativeActivity* activity,
void* savedState, size_t savedStateSize) {
LOGI("Creating: %p\n", activity);
activity->callbacks->onDestroy = onDestroy;
activity->callbacks->onStart = onStart;
activity->callbacks->onResume = onResume;
activity->callbacks->onSaveInstanceState = onSaveInstanceState;
activity->callbacks->onPause = onPause;
activity->callbacks->onStop = onStop;
activity->callbacks->onConfigurationChanged = onConfigurationChanged;
activity->callbacks->onLowMemory = onLowMemory;
activity->callbacks->onWindowFocusChanged = onWindowFocusChanged;
activity->callbacks->onNativeWindowCreated = onNativeWindowCreated;
activity->callbacks->onNativeWindowDestroyed = onNativeWindowDestroyed;
activity->callbacks->onInputQueueCreated = onInputQueueCreated;
activity->callbacks->onInputQueueDestroyed = onInputQueueDestroyed;
activity->instance = android_app_create(activity, savedState, savedStateSize);
}
#endif

View File

@@ -0,0 +1,378 @@
/*************************************************************************/
/* android_native_app_glue.h */
/*************************************************************************/
/* 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. */
/*************************************************************************/
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
#ifndef _ANDROID_NATIVE_APP_GLUE_H
#define _ANDROID_NATIVE_APP_GLUE_H
#ifdef ANDROID_NATIVE_ACTIVITY
#include <poll.h>
#include <pthread.h>
#include <sched.h>
#include <android/configuration.h>
#include <android/looper.h>
#include <android/native_activity.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* The native activity interface provided by <android/native_activity.h>
* is based on a set of application-provided callbacks that will be called
* by the Activity's main thread when certain events occur.
*
* This means that each one of this callbacks _should_ _not_ block, or they
* risk having the system force-close the application. This programming
* model is direct, lightweight, but constraining.
*
* The 'threaded_native_app' static library is used to provide a different
* execution model where the application can implement its own main event
* loop in a different thread instead. Here's how it works:
*
* 1/ The application must provide a function named "android_main()" that
* will be called when the activity is created, in a new thread that is
* distinct from the activity's main thread.
*
* 2/ android_main() receives a pointer to a valid "android_app" structure
* that contains references to other important objects, e.g. the
* ANativeActivity obejct instance the application is running in.
*
* 3/ the "android_app" object holds an ALooper instance that already
* listens to two important things:
*
* - activity lifecycle events (e.g. "pause", "resume"). See APP_CMD_XXX
* declarations below.
*
* - input events coming from the AInputQueue attached to the activity.
*
* Each of these correspond to an ALooper identifier returned by
* ALooper_pollOnce with values of LOOPER_ID_MAIN and LOOPER_ID_INPUT,
* respectively.
*
* Your application can use the same ALooper to listen to additional
* file-descriptors. They can either be callback based, or with return
* identifiers starting with LOOPER_ID_USER.
*
* 4/ Whenever you receive a LOOPER_ID_MAIN or LOOPER_ID_INPUT event,
* the returned data will point to an android_poll_source structure. You
* can call the process() function on it, and fill in android_app->onAppCmd
* and android_app->onInputEvent to be called for your own processing
* of the event.
*
* Alternatively, you can call the low-level functions to read and process
* the data directly... look at the process_cmd() and process_input()
* implementations in the glue to see how to do this.
*
* See the sample named "native-activity" that comes with the NDK with a
* full usage example. Also look at the JavaDoc of NativeActivity.
*/
struct android_app;
/**
* Data associated with an ALooper fd that will be returned as the "outData"
* when that source has data ready.
*/
struct android_poll_source {
// The identifier of this source. May be LOOPER_ID_MAIN or
// LOOPER_ID_INPUT.
int32_t id;
// The android_app this ident is associated with.
struct android_app* app;
// Function to call to perform the standard processing of data from
// this source.
void (*process)(struct android_app* app, struct android_poll_source* source);
};
/**
* This is the interface for the standard glue code of a threaded
* application. In this model, the application's code is running
* in its own thread separate from the main thread of the process.
* It is not required that this thread be associated with the Java
* VM, although it will need to be in order to make JNI calls any
* Java objects.
*/
struct android_app {
// The application can place a pointer to its own state object
// here if it likes.
void* userData;
// Fill this in with the function to process main app commands (APP_CMD_*)
void (*onAppCmd)(struct android_app* app, int32_t cmd);
// Fill this in with the function to process input events. At this point
// the event has already been pre-dispatched, and it will be finished upon
// return. Return 1 if you have handled the event, 0 for any default
// dispatching.
int32_t (*onInputEvent)(struct android_app* app, AInputEvent* event);
// The ANativeActivity object instance that this app is running in.
ANativeActivity* activity;
// The current configuration the app is running in.
AConfiguration* config;
// This is the last instance's saved state, as provided at creation time.
// It is NULL if there was no state. You can use this as you need; the
// memory will remain around until you call android_app_exec_cmd() for
// APP_CMD_RESUME, at which point it will be freed and savedState set to NULL.
// These variables should only be changed when processing a APP_CMD_SAVE_STATE,
// at which point they will be initialized to NULL and you can malloc your
// state and place the information here. In that case the memory will be
// freed for you later.
void* savedState;
size_t savedStateSize;
// The ALooper associated with the app's thread.
ALooper* looper;
// When non-NULL, this is the input queue from which the app will
// receive user input events.
AInputQueue* inputQueue;
// When non-NULL, this is the window surface that the app can draw in.
ANativeWindow* window;
// Current content rectangle of the window; this is the area where the
// window's content should be placed to be seen by the user.
ARect contentRect;
// Current state of the app's activity. May be either APP_CMD_START,
// APP_CMD_RESUME, APP_CMD_PAUSE, or APP_CMD_STOP; see below.
int activityState;
// This is non-zero when the application's NativeActivity is being
// destroyed and waiting for the app thread to complete.
int destroyRequested;
// -------------------------------------------------
// Below are "private" implementation of the glue code.
pthread_mutex_t mutex;
pthread_cond_t cond;
int msgread;
int msgwrite;
pthread_t thread;
struct android_poll_source cmdPollSource;
struct android_poll_source inputPollSource;
int running;
int stateSaved;
int destroyed;
int redrawNeeded;
AInputQueue* pendingInputQueue;
ANativeWindow* pendingWindow;
ARect pendingContentRect;
};
enum {
/**
* Looper data ID of commands coming from the app's main thread, which
* is returned as an identifier from ALooper_pollOnce(). The data for this
* identifier is a pointer to an android_poll_source structure.
* These can be retrieved and processed with android_app_read_cmd()
* and android_app_exec_cmd().
*/
LOOPER_ID_MAIN = 1,
/**
* Looper data ID of events coming from the AInputQueue of the
* application's window, which is returned as an identifier from
* ALooper_pollOnce(). The data for this identifier is a pointer to an
* android_poll_source structure. These can be read via the inputQueue
* object of android_app.
*/
LOOPER_ID_INPUT = 2,
/**
* Start of user-defined ALooper identifiers.
*/
LOOPER_ID_USER = 3,
};
enum {
/**
* Command from main thread: the AInputQueue has changed. Upon processing
* this command, android_app->inputQueue will be updated to the new queue
* (or NULL).
*/
APP_CMD_INPUT_CHANGED,
/**
* Command from main thread: a new ANativeWindow is ready for use. Upon
* receiving this command, android_app->window will contain the new window
* surface.
*/
APP_CMD_INIT_WINDOW,
/**
* Command from main thread: the existing ANativeWindow needs to be
* terminated. Upon receiving this command, android_app->window still
* contains the existing window; after calling android_app_exec_cmd
* it will be set to NULL.
*/
APP_CMD_TERM_WINDOW,
/**
* Command from main thread: the current ANativeWindow has been resized.
* Please redraw with its new size.
*/
APP_CMD_WINDOW_RESIZED,
/**
* Command from main thread: the system needs that the current ANativeWindow
* be redrawn. You should redraw the window before handing this to
* android_app_exec_cmd() in order to avoid transient drawing glitches.
*/
APP_CMD_WINDOW_REDRAW_NEEDED,
/**
* Command from main thread: the content area of the window has changed,
* such as from the soft input window being shown or hidden. You can
* find the new content rect in android_app::contentRect.
*/
APP_CMD_CONTENT_RECT_CHANGED,
/**
* Command from main thread: the app's activity window has gained
* input focus.
*/
APP_CMD_GAINED_FOCUS,
/**
* Command from main thread: the app's activity window has lost
* input focus.
*/
APP_CMD_LOST_FOCUS,
/**
* Command from main thread: the current device configuration has changed.
*/
APP_CMD_CONFIG_CHANGED,
/**
* Command from main thread: the system is running low on memory.
* Try to reduce your memory use.
*/
APP_CMD_LOW_MEMORY,
/**
* Command from main thread: the app's activity has been started.
*/
APP_CMD_START,
/**
* Command from main thread: the app's activity has been resumed.
*/
APP_CMD_RESUME,
/**
* Command from main thread: the app should generate a new saved state
* for itself, to restore from later if needed. If you have saved state,
* allocate it with malloc and place it in android_app.savedState with
* the size in android_app.savedStateSize. The will be freed for you
* later.
*/
APP_CMD_SAVE_STATE,
/**
* Command from main thread: the app's activity has been paused.
*/
APP_CMD_PAUSE,
/**
* Command from main thread: the app's activity has been stopped.
*/
APP_CMD_STOP,
/**
* Command from main thread: the app's activity is being destroyed,
* and waiting for the app thread to clean up and exit before proceeding.
*/
APP_CMD_DESTROY,
};
/**
* Call when ALooper_pollAll() returns LOOPER_ID_MAIN, reading the next
* app command message.
*/
int8_t android_app_read_cmd(struct android_app* android_app);
/**
* Call with the command returned by android_app_read_cmd() to do the
* initial pre-processing of the given command. You can perform your own
* actions for the command after calling this function.
*/
void android_app_pre_exec_cmd(struct android_app* android_app, int8_t cmd);
/**
* Call with the command returned by android_app_read_cmd() to do the
* final post-processing of the given command. You must have done your own
* actions for the command before calling this function.
*/
void android_app_post_exec_cmd(struct android_app* android_app, int8_t cmd);
/**
* Dummy function you can call to ensure glue code isn't stripped.
*/
void app_dummy();
/**
* This is the function that application code must implement, representing
* the main entry to the app.
*/
extern void android_main(struct android_app* app);
#ifdef __cplusplus
}
#endif
#endif /* _ANDROID_NATIVE_APP_GLUE_H */
#endif

View File

@@ -0,0 +1,393 @@
/*************************************************************************/
/* audio_driver_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. */
/*************************************************************************/
#include "audio_driver_android.h"
#include <string.h>
#ifdef ANDROID_NATIVE_ACTIVITY
#define MAX_NUMBER_INTERFACES 3
#define MAX_NUMBER_OUTPUT_DEVICES 6
/* Structure for passing information to callback function */
void AudioDriverAndroid::_buffer_callback(
SLAndroidSimpleBufferQueueItf queueItf
/* SLuint32 eventFlags,
const void * pBuffer,
SLuint32 bufferSize,
SLuint32 dataUsed*/) {
if (mutex)
mutex->lock();
audio_server_process(buffer_size,mixdown_buffer);
if (mutex)
mutex->unlock();
const int32_t* src_buff=mixdown_buffer;
int16_t *ptr = (int16_t*)buffers[last_free];
last_free=(last_free+1)%BUFFER_COUNT;
for(int i=0;i<buffer_size*2;i++) {
ptr[i]=src_buff[i]>>16;
}
(*queueItf)->Enqueue(queueItf, ptr, 4 * buffer_size);
#if 0
SLresult res;
CallbackCntxt *pCntxt = (CallbackCntxt*)pContext;
if(pCntxt->pData < (pCntxt->pDataBase + pCntxt->size))
{
res = (*queueItf)->Enqueue(queueItf, (void*) pCntxt->pData,
2 * AUDIO_DATA_BUFFER_SIZE, SL_BOOLEAN_FALSE); /* Size given
in bytes. */
CheckErr(res);
/* Increase data pointer by buffer size */
pCntxt->pData += AUDIO_DATA_BUFFER_SIZE;
}
}
#endif
}
void AudioDriverAndroid::_buffer_callbacks(
SLAndroidSimpleBufferQueueItf queueItf,
/*SLuint32 eventFlags,
const void * pBuffer,
SLuint32 bufferSize,
SLuint32 dataUsed,*/
void *pContext) {
AudioDriverAndroid *ad = (AudioDriverAndroid*)pContext;
// ad->_buffer_callback(queueItf,eventFlags,pBuffer,bufferSize,dataUsed);
ad->_buffer_callback(queueItf);
}
AudioDriverAndroid* AudioDriverAndroid::s_ad=NULL;
const char* AudioDriverAndroid::get_name() const {
return "Android";
}
#if 0
int AudioDriverAndroid::thread_func(SceSize args, void *argp) {
AudioDriverAndroid* ad = s_ad;
sceAudioOutput2Reserve(AUDIO_OUTPUT_SAMPLE);
int half=0;
while(!ad->exit_thread) {
int16_t *ptr = &ad->outbuff[AUDIO_OUTPUT_SAMPLE*2*half];
if (!ad->active) {
for(int i=0;i<AUDIO_OUTPUT_SAMPLE*2;i++) {
ptr[i]=0;
}
} else {
//printf("samples: %i\n",AUDIO_OUTPUT_SAMPLE);
ad->lock();
ad->audio_server_process(AUDIO_OUTPUT_SAMPLE,ad->outbuff_32);
ad->unlock();
const int32_t* src_buff=ad->outbuff_32;
for(int i=0;i<AUDIO_OUTPUT_SAMPLE*2;i++) {
ptr[i]=src_buff[i]>>16;
}
}
/* Output 16-bit PCM STEREO data that is in pcmBuf without changing the volume */
sceAudioOutput2OutputBlocking(
SCE_AUDIO_VOLUME_0dB*3, //0db at 0x8000, that's obvious
ptr
);
if (half)
half=0;
else
half=1;
}
sceAudioOutput2Release();
sceKernelExitThread(SCE_KERNEL_EXIT_SUCCESS);
ad->thread_exited=true;
return SCE_KERNEL_EXIT_SUCCESS;
}
#endif
Error AudioDriverAndroid::init(){
SLresult
res;
SLEngineOption EngineOption[] = {
(SLuint32) SL_ENGINEOPTION_THREADSAFE,
(SLuint32) SL_BOOLEAN_TRUE
};
res = slCreateEngine( &sl, 1, EngineOption, 0, NULL, NULL);
if (res!=SL_RESULT_SUCCESS) {
ERR_EXPLAIN("Could not Initialize OpenSL");
ERR_FAIL_V(ERR_INVALID_PARAMETER);
}
res = (*sl)->Realize(sl, SL_BOOLEAN_FALSE);
if (res!=SL_RESULT_SUCCESS) {
ERR_EXPLAIN("Could not Realize OpenSL");
ERR_FAIL_V(ERR_INVALID_PARAMETER);
}
print_line("OpenSL Init OK!");
return OK;
}
void AudioDriverAndroid::start(){
mutex = Mutex::create();
active=false;
SLint32 numOutputs = 0;
SLuint32 deviceID = 0;
SLresult res;
buffer_size = 1024;
for(int i=0;i<BUFFER_COUNT;i++) {
buffers[i]=memnew_arr( int16_t,buffer_size*2 );
memset(buffers[i],0,buffer_size*4);
}
mixdown_buffer = memnew_arr( int32_t,buffer_size* 2);
/* Callback context for the buffer queue callback function */
/* Get the SL Engine Interface which is implicit */
res = (*sl)->GetInterface(sl, SL_IID_ENGINE, (void*)&EngineItf);
ERR_FAIL_COND( res !=SL_RESULT_SUCCESS );
/* Initialize arrays required[] and iidArray[] */
int i;
SLboolean required[MAX_NUMBER_INTERFACES];
SLInterfaceID iidArray[MAX_NUMBER_INTERFACES];
#if 0
for (i=0; i<MAX_NUMBER_INTERFACES; i++)
{
required[i] = SL_BOOLEAN_FALSE;
iidArray[i] = SL_IID_NULL;
}
// Set arrays required[] and iidArray[] for VOLUME interface
required[0] = SL_BOOLEAN_TRUE;
iidArray[0] = SL_IID_VOLUME;
// Create Output Mix object to be used by player
res = (*EngineItf)->CreateOutputMix(EngineItf, &OutputMix, 1,
iidArray, required);
#else
{
const SLInterfaceID ids[1] = {SL_IID_ENVIRONMENTALREVERB};
const SLboolean req[1] = {SL_BOOLEAN_FALSE};
res = (*EngineItf)->CreateOutputMix(EngineItf, &OutputMix, 0,
ids, req);
}
#endif
ERR_FAIL_COND( res !=SL_RESULT_SUCCESS );
// Realizing the Output Mix object in synchronous mode.
res = (*OutputMix)->Realize(OutputMix, SL_BOOLEAN_FALSE);
ERR_FAIL_COND( res !=SL_RESULT_SUCCESS );
SLDataLocator_AndroidSimpleBufferQueue loc_bufq = {SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, BUFFER_COUNT};
// bufferQueue.locatorType = SL_DATALOCATOR_BUFFERQUEUE;
// bufferQueue.numBuffers = BUFFER_COUNT; /* Four buffers in our buffer queue */
/* Setup the format of the content in the buffer queue */
pcm.formatType = SL_DATAFORMAT_PCM;
pcm.numChannels = 2;
pcm.samplesPerSec = SL_SAMPLINGRATE_44_1;
pcm.bitsPerSample = SL_PCMSAMPLEFORMAT_FIXED_16;
pcm.containerSize = SL_PCMSAMPLEFORMAT_FIXED_16;
pcm.channelMask = SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT;
#ifdef BIG_ENDIAN_ENABLED
pcm.endianness = SL_BYTEORDER_BIGENDIAN;
#else
pcm.endianness = SL_BYTEORDER_LITTLEENDIAN;
#endif
audioSource.pFormat = (void *)&pcm;
audioSource.pLocator = (void *)&loc_bufq;
/* Setup the data sink structure */
locator_outputmix.locatorType = SL_DATALOCATOR_OUTPUTMIX;
locator_outputmix.outputMix= OutputMix;
audioSink.pLocator = (void *)&locator_outputmix;
audioSink.pFormat = NULL;
/* Initialize the context for Buffer queue callbacks */
// cntxt.pDataBase = (void*)&pcmData;
//cntxt.pData = cntxt.pDataBase;
//cntxt.size = sizeof(pcmData);
/* Set arrays required[] and iidArray[] for SEEK interface
(PlayItf is implicit) */
required[0] = SL_BOOLEAN_TRUE;
iidArray[0] = SL_IID_BUFFERQUEUE;
/* Create the music player */
{
const SLInterfaceID ids[2] = {SL_IID_BUFFERQUEUE, SL_IID_EFFECTSEND};
const SLboolean req[2] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE};
res = (*EngineItf)->CreateAudioPlayer(EngineItf, &player,
&audioSource, &audioSink, 1, ids, req);
ERR_FAIL_COND( res !=SL_RESULT_SUCCESS );
}
/* Realizing the player in synchronous mode. */
res = (*player)->Realize(player, SL_BOOLEAN_FALSE);
ERR_FAIL_COND( res !=SL_RESULT_SUCCESS );
/* Get seek and play interfaces */
res = (*player)->GetInterface(player, SL_IID_PLAY, (void*)&playItf);
ERR_FAIL_COND( res !=SL_RESULT_SUCCESS );
res = (*player)->GetInterface(player, SL_IID_BUFFERQUEUE,
(void*)&bufferQueueItf);
ERR_FAIL_COND( res !=SL_RESULT_SUCCESS );
/* Setup to receive buffer queue event callbacks */
res = (*bufferQueueItf)->RegisterCallback(bufferQueueItf,
_buffer_callbacks, this);
ERR_FAIL_COND( res !=SL_RESULT_SUCCESS );
/* Before we start set volume to -3dB (-300mB) */
#if 0
res = (*OutputMix)->GetInterface(OutputMix, SL_IID_VOLUME,
(void*)&volumeItf);
ERR_FAIL_COND( res !=SL_RESULT_SUCCESS );
/* Setup the data source structure for the buffer queue */
res = (*volumeItf)->SetVolumeLevel(volumeItf, -300);
ERR_FAIL_COND( res !=SL_RESULT_SUCCESS );
#endif
last_free=0;
#if 1
//fill up buffers
for(int i=0;i<BUFFER_COUNT;i++) {
/* Enqueue a few buffers to get the ball rolling */
res = (*bufferQueueItf)->Enqueue(bufferQueueItf, buffers[i],
4 * buffer_size); /* Size given in */
}
#endif
res = (*playItf)->SetPlayState(playItf, SL_PLAYSTATE_PLAYING);
ERR_FAIL_COND( res !=SL_RESULT_SUCCESS );
#if 0
res = (*bufferQueueItf)->GetState(bufferQueueItf, &state);
ERR_FAIL_COND( res !=SL_RESULT_SUCCESS );
while(state.count)
{
(*bufferQueueItf)->GetState(bufferQueueItf, &state);
}
/* Make sure player is stopped */
res = (*playItf)->SetPlayState(playItf, SL_PLAYSTATE_STOPPED);
CheckErr(res);
/* Destroy the player */
(*player)->Destroy(player);
/* Destroy Output Mix object */
(*OutputMix)->Destroy(OutputMix);
#endif
active=true;
}
int AudioDriverAndroid::get_mix_rate() const {
return 44100;
}
AudioDriverSW::OutputFormat AudioDriverAndroid::get_output_format() const{
return OUTPUT_STEREO;
}
void AudioDriverAndroid::lock(){
//if (active && mutex)
// mutex->lock();
}
void AudioDriverAndroid::unlock() {
//if (active && mutex)
// mutex->unlock();
}
void AudioDriverAndroid::finish(){
(*sl)->Destroy(sl);
}
AudioDriverAndroid::AudioDriverAndroid()
{
s_ad=this;
mutex=NULL;
}
#endif

View File

@@ -0,0 +1,105 @@
/*************************************************************************/
/* audio_driver_android.h */
/*************************************************************************/
/* 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. */
/*************************************************************************/
#ifndef AUDIO_DRIVER_ANDROID_H
#define AUDIO_DRIVER_ANDROID_H
#ifdef ANDROID_NATIVE_ACTIVITY
#include "servers/audio/audio_server_sw.h"
#include "os/mutex.h"
#include <SLES/OpenSLES.h>
#include "SLES/OpenSLES_Android.h"
class AudioDriverAndroid : public AudioDriverSW {
bool active;
Mutex *mutex;
enum {
BUFFER_COUNT=2
};
uint32_t buffer_size;
int16_t *buffers[BUFFER_COUNT];
int32_t *mixdown_buffer;
int last_free;
SLPlayItf playItf;
SLObjectItf sl;
SLEngineItf EngineItf;
SLObjectItf OutputMix;
SLVolumeItf volumeItf;
SLObjectItf player;
SLAndroidSimpleBufferQueueItf bufferQueueItf;
SLDataSource audioSource;
SLDataFormat_PCM pcm;
SLDataSink audioSink;
SLDataLocator_OutputMix locator_outputmix;
SLBufferQueueState state;
static AudioDriverAndroid* s_ad;
void _buffer_callback(
SLAndroidSimpleBufferQueueItf queueItf
/* SLuint32 eventFlags,
const void * pBuffer,
SLuint32 bufferSize,
SLuint32 dataUsed*/);
static void _buffer_callbacks(
SLAndroidSimpleBufferQueueItf queueItf,
/*SLuint32 eventFlags,
const void * pBuffer,
SLuint32 bufferSize,
SLuint32 dataUsed,*/
void *pContext);
public:
void set_singleton();
virtual const char* get_name() const;
virtual Error init();
virtual void start();
virtual int get_mix_rate() const ;
virtual OutputFormat get_output_format() const;
virtual void lock();
virtual void unlock();
virtual void finish();
AudioDriverAndroid();
};
#endif // AUDIO_DRIVER_ANDROID_H
#endif

View File

@@ -0,0 +1,248 @@
/*************************************************************************/
/* audio_driver_jandroid.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. */
/*************************************************************************/
#include "audio_driver_jandroid.h"
#include "globals.h"
#include "os/os.h"
#include "thread_jandroid.h"
#ifndef ANDROID_NATIVE_ACTIVITY
AudioDriverAndroid* AudioDriverAndroid::s_ad=NULL;
jobject AudioDriverAndroid::io;
jmethodID AudioDriverAndroid::_init_audio;
jmethodID AudioDriverAndroid::_write_buffer;
jmethodID AudioDriverAndroid::_quit;
jmethodID AudioDriverAndroid::_pause;
bool AudioDriverAndroid::active=false;
jclass AudioDriverAndroid::cls;
int AudioDriverAndroid::audioBufferFrames=0;
int AudioDriverAndroid::mix_rate=44100;
bool AudioDriverAndroid::quit=false;
jobject AudioDriverAndroid::audioBuffer = NULL;
void* AudioDriverAndroid::audioBufferPinned = NULL;
Mutex *AudioDriverAndroid::mutex=NULL;
int32_t* AudioDriverAndroid::audioBuffer32=NULL;
const char* AudioDriverAndroid::get_name() const {
return "Android";
}
Error AudioDriverAndroid::init(){
mutex = Mutex::create();
/*
// TODO: pass in/return a (Java) device ID, also whether we're opening for input or output
this->spec.samples = Android_JNI_OpenAudioDevice(this->spec.freq, this->spec.format == AUDIO_U8 ? 0 : 1, this->spec.channels, this->spec.samples);
SDL_CalculateAudioSpec(&this->spec);
if (this->spec.samples == 0) {
// Init failed?
SDL_SetError("Java-side initialization failed!");
return 0;
}
*/
// Android_JNI_SetupThread();
// __android_log_print(ANDROID_LOG_VERBOSE, "SDL", "SDL audio: opening device");
JNIEnv *env = ThreadAndroid::get_env();
int mix_rate = GLOBAL_DEF("audio/mix_rate",44100);
int latency = GLOBAL_DEF("audio/output_latency",25);
latency=50;
unsigned int buffer_size = nearest_power_of_2( latency * mix_rate / 1000 );
if (OS::get_singleton()->is_stdout_verbose()) {
print_line("audio buffer size: "+itos(buffer_size));
}
__android_log_print(ANDROID_LOG_INFO,"godot","Initializing audio! params: %i,%i ",mix_rate,buffer_size);
audioBuffer = env->CallObjectMethod(io,_init_audio, mix_rate, buffer_size);
ERR_FAIL_COND_V( audioBuffer == NULL, ERR_INVALID_PARAMETER);
audioBuffer = env->NewGlobalRef(audioBuffer);
jboolean isCopy = JNI_FALSE;
audioBufferPinned = env->GetShortArrayElements((jshortArray)audioBuffer, &isCopy);
audioBufferFrames = env->GetArrayLength((jshortArray)audioBuffer);
audioBuffer32 = memnew_arr(int32_t,audioBufferFrames);
return OK;
}
void AudioDriverAndroid::start(){
active=true;
}
void AudioDriverAndroid::setup( jobject p_io) {
JNIEnv *env = ThreadAndroid::get_env();
io=p_io;
jclass c = env->GetObjectClass(io);
cls = (jclass)env->NewGlobalRef(c);
__android_log_print(ANDROID_LOG_INFO,"godot","starting to attempt get methods");
_init_audio = env->GetMethodID(cls, "audioInit", "(II)Ljava/lang/Object;");
if(_init_audio != 0) {
__android_log_print(ANDROID_LOG_INFO,"godot","*******GOT METHOD _init_audio ok!!");
} else {
__android_log_print(ANDROID_LOG_INFO,"godot","audioinit ok!");
}
_write_buffer = env->GetMethodID(cls, "audioWriteShortBuffer", "([S)V");
if(_write_buffer != 0) {
__android_log_print(ANDROID_LOG_INFO,"godot","*******GOT METHOD _write_buffer ok!!");
}
_quit = env->GetMethodID(cls, "audioQuit", "()V");
if(_quit != 0) {
__android_log_print(ANDROID_LOG_INFO,"godot","*******GOT METHOD _quit ok!!");
}
_pause = env->GetMethodID(cls, "audioPause", "(Z)V");
if(_quit != 0) {
__android_log_print(ANDROID_LOG_INFO,"godot","*******GOT METHOD _pause ok!!");
}
}
void AudioDriverAndroid::thread_func(JNIEnv *env) {
jclass cls = env->FindClass("com/android/godot/Godot");
if (cls) {
cls=(jclass)env->NewGlobalRef(cls);
__android_log_print(ANDROID_LOG_INFO,"godot","*******CLASS FOUND!!!");
}
jfieldID fid = env->GetStaticFieldID(cls, "io", "Lcom/android/godot/GodotIO;");
jobject ob = env->GetStaticObjectField(cls,fid);
jobject gob = env->NewGlobalRef(ob);
jclass c = env->GetObjectClass(gob);
jclass lcls = (jclass)env->NewGlobalRef(c);
_write_buffer = env->GetMethodID(lcls, "audioWriteShortBuffer", "([S)V");
if(_write_buffer != 0) {
__android_log_print(ANDROID_LOG_INFO,"godot","*******GOT METHOD _write_buffer ok!!");
}
while(!quit) {
int16_t* ptr = (int16_t*)audioBufferPinned;
int fc = audioBufferFrames;
if (!s_ad->active || mutex->try_lock()!=OK) {
for(int i=0;i<fc;i++) {
ptr[i]=0;
}
} else {
s_ad->audio_server_process(fc/2,audioBuffer32);
mutex->unlock();
for(int i=0;i<fc;i++) {
ptr[i]=audioBuffer32[i]>>16;
}
}
env->ReleaseShortArrayElements((jshortArray)audioBuffer, (jshort *)ptr, JNI_COMMIT);
env->CallVoidMethod(gob, _write_buffer, (jshortArray)audioBuffer);
}
}
int AudioDriverAndroid::get_mix_rate() const {
return mix_rate;
}
AudioDriverSW::OutputFormat AudioDriverAndroid::get_output_format() const{
return OUTPUT_STEREO;
}
void AudioDriverAndroid::lock(){
if (mutex)
mutex->lock();
}
void AudioDriverAndroid::unlock() {
if (mutex)
mutex->unlock();
}
void AudioDriverAndroid::finish(){
JNIEnv *env = ThreadAndroid::get_env();
env->CallVoidMethod(io, _quit);
if (audioBuffer) {
env->DeleteGlobalRef(audioBuffer);
audioBuffer = NULL;
audioBufferPinned = NULL;
}
active=false;
}
void AudioDriverAndroid::set_pause(bool p_pause) {
JNIEnv *env = ThreadAndroid::get_env();
env->CallVoidMethod(io, _pause,p_pause);
}
AudioDriverAndroid::AudioDriverAndroid()
{
s_ad=this;
active=false;
}
#endif

View File

@@ -0,0 +1,82 @@
/*************************************************************************/
/* audio_driver_jandroid.h */
/*************************************************************************/
/* 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. */
/*************************************************************************/
#ifndef AUDIO_DRIVER_ANDROID_H
#define AUDIO_DRIVER_ANDROID_H
#include "servers/audio/audio_server_sw.h"
#ifndef ANDROID_NATIVE_ACTIVITY
#include "java_glue.h"
class AudioDriverAndroid : public AudioDriverSW {
static Mutex *mutex;
static AudioDriverAndroid* s_ad;
static jobject io;
static jmethodID _init_audio;
static jmethodID _write_buffer;
static jmethodID _quit;
static jmethodID _pause;
static bool active;
static bool quit;
static jclass cls;
static jobject audioBuffer;
static void* audioBufferPinned;
static int32_t* audioBuffer32;
static int audioBufferFrames;
static int mix_rate;
public:
void set_singleton();
virtual const char* get_name() const;
virtual Error init();
virtual void start();
virtual int get_mix_rate() const ;
virtual OutputFormat get_output_format() const;
virtual void lock();
virtual void unlock();
virtual void finish();
virtual void set_pause(bool p_pause);
static void setup( jobject act);
static void thread_func(JNIEnv *env);
AudioDriverAndroid();
};
#endif
#endif // AUDIO_DRIVER_ANDROID_H

175
platform/android/detect.py Normal file
View File

@@ -0,0 +1,175 @@
import os
import sys
import string
import platform
def is_active():
return True
def get_name():
return "Android"
def can_build():
import os
if (not os.environ.has_key("ANDROID_NDK_ROOT")):
return False
return True
def get_opts():
return [
('ANDROID_NDK_ROOT', 'the path to Android NDK', os.environ.get("ANDROID_NDK_ROOT", 0)),
('NDK_TOOLCHAIN', 'toolchain to use for the NDK',"arm-eabi-4.4.0"),
#android 2.3
('ndk_platform', 'compile for platform: (2.2,2.3)',"2.2"),
('NDK_TARGET', 'toolchain to use for the NDK',"arm-linux-androideabi-4.7"),
('android_stl','enable STL support in android port (for modules)','no'),
('armv6','compile for older phones running arm v6 (instead of v7+neon+smp)','no')
]
def get_flags():
return [
('lua', 'no'),
('tools', 'no'),
('nedmalloc', 'no'),
('builtin_zlib', 'no'),
]
def create(env):
tools = env['TOOLS']
if "mingw" in tools:
tools.remove('mingw')
if "applelink" in tools:
tools.remove("applelink")
env.Tool('gcc')
return env.Clone(tools=tools);
def configure(env):
if env['PLATFORM'] == 'win32':
import methods
env.Tool('gcc')
env['SPAWN'] = methods.win32_spawn
ndk_platform=""
if (env["ndk_platform"]=="2.2"):
ndk_platform="android-8"
else:
ndk_platform="android-9"
env.Append(CPPFLAGS=["-DANDROID_NATIVE_ACTIVITY"])
print("Godot Android!!!!!")
env.Append(CPPPATH=['#platform/android'])
env['OBJSUFFIX'] = ".android.o"
env['LIBSUFFIX'] = ".android.a"
env['PROGSUFFIX'] = ".android"
env['SHLIBSUFFIX'] = ".so"
gcc_path=env["ANDROID_NDK_ROOT"]+"/toolchains/"+env["NDK_TARGET"]+"/prebuilt/";
import os
if (sys.platform.find("linux")==0):
if (platform.architecture()[0]=='64bit' or os.path.isdir(gcc_path+"linux-x86_64/bin")): # check was not working
gcc_path=gcc_path+"/linux-x86_64/bin"
else:
gcc_path=gcc_path+"/linux-x86/bin"
elif (sys.platform=="darwin"):
gcc_path=gcc_path+"/darwin-x86_64/bin" #this may be wrong
env['SHLINKFLAGS'][1] = '-shared'
elif (os.name=="nt"):
gcc_path=gcc_path+"/windows/bin" #this may be wrong
env['ENV']['PATH'] = gcc_path+":"+env['ENV']['PATH']
env['CC'] = gcc_path+'/arm-linux-androideabi-gcc'
env['CXX'] = gcc_path+'/arm-linux-androideabi-g++'
env['AR'] = gcc_path+"/arm-linux-androideabi-ar"
env['RANLIB'] = gcc_path+"/arm-linux-androideabi-ranlib"
env['AS'] = gcc_path+"/arm-linux-androideabi-as"
import string
#include path
gcc_include=env["ANDROID_NDK_ROOT"]+"/platforms/"+ndk_platform+"/arch-arm/usr/include"
ld_sysroot=env["ANDROID_NDK_ROOT"]+"/platforms/"+ndk_platform+"/arch-arm"
#glue_include=env["ANDROID_NDK_ROOT"]+"/sources/android/native_app_glue"
ld_path=env["ANDROID_NDK_ROOT"]+"/platforms/"+ndk_platform+"/arch-arm/usr/lib"
env.Append(CPPPATH=[gcc_include])
# env['CCFLAGS'] = string.split('-DNO_THREADS -MMD -MP -MF -fpic -ffunction-sections -funwind-tables -fstack-protector -D__ARM_ARCH_5__ -D__ARM_ARCH_5T__ -D__ARM_ARCH_5E__ -D__ARM_ARCH_5TE__ -Wno-psabi -march=armv5te -mtune=xscale -msoft-float -fno-exceptions -mthumb -fno-strict-aliasing -DANDROID -Wa,--noexecstack -DGLES2_ENABLED ')
print("********* armv6", env['armv6'])
if env["armv6"]!="no":
env['CCFLAGS'] = string.split('-DNO_STATVFS -MMD -MP -MF -fpic -ffunction-sections -funwind-tables -fstack-protector -D__ARM_ARCH_6__ -D__GLIBC__ -Wno-psabi -march=armv6 -mfpu=vfp -mfloat-abi=softfp -funsafe-math-optimizations -fno-strict-aliasing -DANDROID -Wa,--noexecstack -DGLES2_ENABLED -DGLES1_ENABLED')
else:
env['CCFLAGS'] = string.split('-DNO_STATVFS -MMD -MP -MF -fpic -ffunction-sections -funwind-tables -fstack-protector -D__ARM_ARCH_7__ -D__GLIBC__ -Wno-psabi -march=armv6 -mfpu=neon -mfloat-abi=softfp -ftree-vectorize -funsafe-math-optimizations -fno-strict-aliasing -DANDROID -Wa,--noexecstack -DGLES2_ENABLED -DGLES1_ENABLED')
env.Append(LDPATH=[ld_path])
# env.Append(LIBS=['c','m','stdc++','log','EGL','GLESv1_CM','GLESv2','OpenSLES','supc++','android'])
if (env["ndk_platform"]!="2.2"):
env.Append(LIBS=['EGL','OpenSLES','android'])
env.Append(LIBS=['c','m','stdc++','log','GLESv1_CM','GLESv2', 'z'])
env["LINKFLAGS"]= string.split(" -g --sysroot="+ld_sysroot+" -Wl,--no-undefined -Wl,-z,noexecstack ")
env.Append(LINKFLAGS=["-Wl,-soname,libgodot_android.so"])
if (env["target"]=="release"):
env.Append(CCFLAGS=['-O2', '-ffast-math','-fomit-frame-pointer'])
env['OBJSUFFIX'] = "_opt"+env['OBJSUFFIX']
env['LIBSUFFIX'] = "_opt"+env['LIBSUFFIX']
elif (env["target"]=="release_debug"):
env.Append(CCFLAGS=['-O2', '-ffast-math','-DDEBUG_ENABLED'])
env['OBJSUFFIX'] = "_optd"+env['OBJSUFFIX']
env['LIBSUFFIX'] = "_optd"+env['LIBSUFFIX']
elif (env["target"]=="profile"):
env.Append(CCFLAGS=['-O2', '-ffast-math','-fomit-frame-pointer', '-g1'])
env.Append(LIBPATH=['#platform/android/armeabi'])
env.Append(LIBS=['andprof'])
env['OBJSUFFIX'] = "_prof"+env['OBJSUFFIX']
env['LIBSUFFIX'] = "_prof"+env['LIBSUFFIX']
env['SHLIBSUFFIX'] = "_prof"+env['SHLIBSUFFIX']
elif (env["target"]=="debug"):
env.Append(CCFLAGS=['-D_DEBUG', '-g1', '-Wall', '-O0', '-DDEBUG_ENABLED'])
env.Append(CPPFLAGS=['-DDEBUG_MEMORY_ALLOC'])
if env["armv6"] == "no":
env['neon_enabled']=True
env.Append(CPPFLAGS=['-DANDROID_ENABLED', '-DUNIX_ENABLED', '-DNO_FCNTL','-DMPC_FIXED_POINT'])
# env.Append(CPPFLAGS=['-DANDROID_ENABLED', '-DUNIX_ENABLED','-DMPC_FIXED_POINT'])
if (env['android_stl']=='yes'):
#env.Append(CCFLAGS=[env["ANDROID_NDK_ROOT"]+"/sources/cxx-stl/system/include"])
env.Append(CPPPATH=[env["ANDROID_NDK_ROOT"]+"/sources/cxx-stl/gnu-libstdc++/4.4.3/include"])
env.Append(CPPPATH=[env["ANDROID_NDK_ROOT"]+"/sources/cxx-stl/gnu-libstdc++/4.4.3/libs/armeabi/include"])
env.Append(LIBPATH=[env["ANDROID_NDK_ROOT"]+"/sources/cxx-stl/gnu-libstdc++/4.4.3/libs/armeabi"])
env.Append(LIBS=["gnustl_static","supc++"])
env.Append(CPPPATH=[env["ANDROID_NDK_ROOT"]+"/sources/cpufeatures"])
#env.Append(CCFLAGS=["-I"+env["ANDROID_NDK_ROOT"]+"/sources/cxx-stl/stlport/stlport"])
#env.Append(CCFLAGS=["-I"+env["ANDROID_NDK_ROOT"]+"/sources/cxx-stl/gnu-libstdc++/libs/armeabi/include"])
#env.Append(LINKFLAGS=[env["ANDROID_NDK_ROOT"]+"/sources/cxx-stl/gnu-libstdc++/libs/armeabi/libstdc++.a"])
else:
env.Append(CPPPATH=[env["ANDROID_NDK_ROOT"]+"/sources/cxx-stl/gabi++/include"])
env.Append(CPPPATH=[env["ANDROID_NDK_ROOT"]+"/sources/cpufeatures"])
env.Append(LIBPATH=[env["ANDROID_NDK_ROOT"]+"/sources/cxx-stl/gabi++/libs/armeabi"])
env.Append(LIBS=['gabi++_static'])
env.Append(CCFLAGS=["-fno-exceptions",'-DNO_SAFE_CAST'])
import methods
env.Append( BUILDERS = { 'GLSL120' : env.Builder(action = methods.build_legacygl_headers, suffix = 'glsl.h',src_suffix = '.glsl') } )
env.Append( BUILDERS = { 'GLSL' : env.Builder(action = methods.build_glsl_headers, suffix = 'glsl.h',src_suffix = '.glsl') } )
env.Append( BUILDERS = { 'GLSL120GLES' : env.Builder(action = methods.build_gles2_headers, suffix = 'glsl.h',src_suffix = '.glsl') } )

View File

@@ -0,0 +1,189 @@
/*************************************************************************/
/* dir_access_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 "dir_access_android.h"
#include "file_access_android.h"
DirAccess *DirAccessAndroid::create_fs() {
return memnew(DirAccessAndroid);
}
bool DirAccessAndroid::list_dir_begin() {
list_dir_end();
AAssetDir* aad = AAssetManager_openDir(FileAccessAndroid::asset_manager,current_dir.utf8().get_data());
if (!aad)
return true; //nothing
return false;
}
String DirAccessAndroid::get_next(){
const char* fn= AAssetDir_getNextFileName(aad);
if (!fn)
return "";
String s;
s.parse_utf8(fn);
current=s;
return s;
}
bool DirAccessAndroid::current_is_dir() const{
String sd;
if (current_dir=="")
sd=current;
else
sd=current_dir+"/"+current;
AAssetDir* aad2 = AAssetManager_openDir(FileAccessAndroid::asset_manager,sd.utf8().get_data());
if (aad2) {
AAssetDir_close(aad2);
return true;
}
return false;
}
void DirAccessAndroid::list_dir_end(){
if (aad==NULL)
return;
AAssetDir_close(aad);
aad=NULL;
}
int DirAccessAndroid::get_drive_count(){
return 0;
}
String DirAccessAndroid::get_drive(int p_drive){
return "";
}
Error DirAccessAndroid::change_dir(String p_dir){
p_dir=p_dir.simplify_path();
if (p_dir=="" || p_dir=="." || (p_dir==".." && current_dir==""))
return OK;
String new_dir;
if (p_dir.begins_with("/"))
new_dir=p_dir.substr(1,p_dir.length());
else if (p_dir.begins_with("res://"))
new_dir=p_dir.substr(6,p_dir.length());
else //relative
new_dir=new_dir+"/"+p_dir;
//test if newdir exists
new_dir=new_dir.simplify_path();
AAssetDir* aad = AAssetManager_openDir(FileAccessAndroid::asset_manager,new_dir.utf8().get_data());
if (aad) {
current_dir=new_dir;
AAssetDir_close(aad);
return OK;
}
return ERR_INVALID_PARAMETER;
}
String DirAccessAndroid::get_current_dir(){
return "/"+current_dir;
}
bool DirAccessAndroid::file_exists(String p_file){
String sd;
if (current_dir=="")
sd=p_file;
else
sd=current_dir+"/"+p_file;
AAsset *a=AAssetManager_open(FileAccessAndroid::asset_manager,sd.utf8().get_data(),AASSET_MODE_STREAMING);
if (a) {
AAsset_close(a);
return true;
}
return false;
}
Error DirAccessAndroid::make_dir(String p_dir){
ERR_FAIL_V(ERR_UNAVAILABLE);
}
Error DirAccessAndroid::rename(String p_from, String p_to){
ERR_FAIL_V(ERR_UNAVAILABLE);
}
Error DirAccessAndroid::remove(String p_name){
ERR_FAIL_V(ERR_UNAVAILABLE);
}
//FileType get_file_type() const;
size_t DirAccessAndroid::get_space_left() {
return 0;
}
void DirAccessAndroid::make_default() {
instance_func=create_fs;
}
DirAccessAndroid::DirAccessAndroid() {
aad=NULL;
}
DirAccessAndroid::~DirAccessAndroid() {
list_dir_end();;
}
#endif

View File

@@ -0,0 +1,82 @@
/*************************************************************************/
/* dir_access_android.h */
/*************************************************************************/
/* 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. */
/*************************************************************************/
#ifndef DIR_ACCESS_ANDROID_H
#define DIR_ACCESS_ANDROID_H
#ifdef ANDROID_NATIVE_ACTIVITY
#include "os/dir_access.h"
#include <stdio.h>
#include <android/asset_manager.h>
#include <android/log.h>
#include <android_native_app_glue.h>
class DirAccessAndroid : public DirAccess {
AAssetDir* aad;
String current_dir;
String current;
static DirAccess *create_fs();
public:
virtual bool list_dir_begin(); ///< This starts dir listing
virtual String get_next();
virtual bool current_is_dir() const;
virtual void list_dir_end(); ///<
virtual int get_drive_count();
virtual String get_drive(int p_drive);
virtual Error change_dir(String p_dir); ///< can be relative or absolute, return false on success
virtual String get_current_dir(); ///< return current dir location
virtual bool file_exists(String p_file);
virtual Error make_dir(String p_dir);
virtual Error rename(String p_from, String p_to);
virtual Error remove(String p_name);
//virtual FileType get_file_type() const;
size_t get_space_left();
static void make_default();
DirAccessAndroid();
~DirAccessAndroid();
};
#endif
#endif // DIR_ACCESS_ANDROID_H

View File

@@ -0,0 +1,241 @@
/*************************************************************************/
/* dir_access_jandroid.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. */
/*************************************************************************/
#ifndef ANDROID_NATIVE_ACTIVITY
#include "dir_access_jandroid.h"
#include "file_access_jandroid.h"
#include "thread_jandroid.h"
jobject DirAccessJAndroid::io=NULL;
jclass DirAccessJAndroid::cls=NULL;
jmethodID DirAccessJAndroid::_dir_open=NULL;
jmethodID DirAccessJAndroid::_dir_next=NULL;
jmethodID DirAccessJAndroid::_dir_close=NULL;
DirAccess *DirAccessJAndroid::create_fs() {
return memnew(DirAccessJAndroid);
}
bool DirAccessJAndroid::list_dir_begin() {
list_dir_end();
JNIEnv *env = ThreadAndroid::get_env();
jstring js = env->NewStringUTF(current_dir.utf8().get_data());
int res = env->CallIntMethod(io,_dir_open,js);
if (res<=0)
return true;
id=res;
return false;
}
String DirAccessJAndroid::get_next(){
ERR_FAIL_COND_V(id==0,"");
JNIEnv *env = ThreadAndroid::get_env();
jstring str= (jstring)env->CallObjectMethod(io,_dir_next,id);
if (!str)
return "";
int sl = env->GetStringLength(str);
if (sl==0) {
env->DeleteLocalRef((jobject)str);
return "";
}
CharString cs;
cs.resize(sl+1);
env->GetStringRegion(str,0,sl,(jchar*)&cs[0]);
cs[sl]=0;
String ret;
ret.parse_utf8(&cs[0]);
env->DeleteLocalRef((jobject)str);
return ret;
}
bool DirAccessJAndroid::current_is_dir() const{
JNIEnv *env = ThreadAndroid::get_env();
String sd;
if (current_dir=="")
sd=current;
else
sd=current_dir+"/"+current;
jstring js = env->NewStringUTF(sd.utf8().get_data());
int res = env->CallIntMethod(io,_dir_open,js);
if (res<=0)
return false;
env->CallObjectMethod(io,_dir_close,res);
return true;
}
void DirAccessJAndroid::list_dir_end(){
if (id==0)
return;
JNIEnv *env = ThreadAndroid::get_env();
env->CallObjectMethod(io,_dir_close,id);
id=0;
}
int DirAccessJAndroid::get_drive_count(){
return 0;
}
String DirAccessJAndroid::get_drive(int p_drive){
return "";
}
Error DirAccessJAndroid::change_dir(String p_dir){
JNIEnv *env = ThreadAndroid::get_env();
p_dir=p_dir.simplify_path();
if (p_dir=="" || p_dir=="." || (p_dir==".." && current_dir==""))
return OK;
String new_dir;
if (p_dir.begins_with("/"))
new_dir=p_dir.substr(1,p_dir.length());
else if (p_dir.begins_with("res://"))
new_dir=p_dir.substr(6,p_dir.length());
else //relative
new_dir=new_dir+"/"+p_dir;
//test if newdir exists
new_dir=new_dir.simplify_path();
jstring js = env->NewStringUTF(new_dir.utf8().get_data());
int res = env->CallIntMethod(io,_dir_open,js);
if (res<=0)
return ERR_INVALID_PARAMETER;
env->CallObjectMethod(io,_dir_close,res);
return OK;
}
String DirAccessJAndroid::get_current_dir(){
return "/"+current_dir;
}
bool DirAccessJAndroid::file_exists(String p_file){
JNIEnv *env = ThreadAndroid::get_env();
String sd;
if (current_dir=="")
sd=p_file;
else
sd=current_dir+"/"+p_file;
FileAccessJAndroid *f = memnew(FileAccessJAndroid);
bool exists = f->file_exists(sd);
memdelete(f);
return exists;
}
Error DirAccessJAndroid::make_dir(String p_dir){
ERR_FAIL_V(ERR_UNAVAILABLE);
}
Error DirAccessJAndroid::rename(String p_from, String p_to){
ERR_FAIL_V(ERR_UNAVAILABLE);
}
Error DirAccessJAndroid::remove(String p_name){
ERR_FAIL_V(ERR_UNAVAILABLE);
}
//FileType get_file_type() const;
size_t DirAccessJAndroid::get_space_left() {
return 0;
}
void DirAccessJAndroid::setup( jobject p_io) {
JNIEnv *env = ThreadAndroid::get_env();
io=p_io;
__android_log_print(ANDROID_LOG_INFO,"godot","STEP7");
jclass c = env->GetObjectClass(io);
cls = (jclass)env->NewGlobalRef(c);
__android_log_print(ANDROID_LOG_INFO,"godot","STEP8");
_dir_open = env->GetMethodID(cls, "dir_open", "(Ljava/lang/String;)I");
if(_dir_open != 0) {
__android_log_print(ANDROID_LOG_INFO,"godot","*******GOT METHOD _dir_open ok!!");
}
_dir_next = env->GetMethodID(cls, "dir_next", "(I)Ljava/lang/String;");
if(_dir_next != 0) {
__android_log_print(ANDROID_LOG_INFO,"godot","*******GOT METHOD _dir_next ok!!");
}
_dir_close = env->GetMethodID(cls, "dir_close", "(I)V");
if(_dir_close != 0) {
__android_log_print(ANDROID_LOG_INFO,"godot","*******GOT METHOD _dir_close ok!!");
}
// (*env)->CallVoidMethod(env,obj,aMethodID, myvar);
}
DirAccessJAndroid::DirAccessJAndroid() {
id=0;
}
DirAccessJAndroid::~DirAccessJAndroid() {
list_dir_end();;
}
#endif

View File

@@ -0,0 +1,90 @@
/*************************************************************************/
/* dir_access_jandroid.h */
/*************************************************************************/
/* 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. */
/*************************************************************************/
#ifndef DIR_ACCESS_JANDROID_H
#define DIR_ACCESS_JANDROID_H
#ifndef ANDROID_NATIVE_ACTIVITY
#include "java_glue.h"
#include "os/dir_access.h"
#include <stdio.h>
class DirAccessJAndroid : public DirAccess {
//AAssetDir* aad;
static jobject io;
static jclass cls;
static jmethodID _dir_open;
static jmethodID _dir_next;
static jmethodID _dir_close;
int id;
String current_dir;
String current;
static DirAccess *create_fs();
public:
virtual bool list_dir_begin(); ///< This starts dir listing
virtual String get_next();
virtual bool current_is_dir() const;
virtual void list_dir_end(); ///<
virtual int get_drive_count();
virtual String get_drive(int p_drive);
virtual Error change_dir(String p_dir); ///< can be relative or absolute, return false on success
virtual String get_current_dir(); ///< return current dir location
virtual bool file_exists(String p_file);
virtual Error make_dir(String p_dir);
virtual Error rename(String p_from, String p_to);
virtual Error remove(String p_name);
//virtual FileType get_file_type() const;
size_t get_space_left();
static void setup( jobject io);
DirAccessJAndroid();
~DirAccessJAndroid();
};
#endif // DIR_ACCESS_JANDROID_H
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,3 @@
void register_android_exporter();

View File

@@ -0,0 +1,187 @@
/*************************************************************************/
/* file_access_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. */
/*************************************************************************/
#include "file_access_android.h"
#include "print_string.h"
#ifdef ANDROID_NATIVE_ACTIVITY
AAssetManager *FileAccessAndroid::asset_manager=NULL;
void FileAccessAndroid::make_default() {
create_func=create_android;
}
FileAccess* FileAccessAndroid::create_android() {
return memnew(FileAccessAndroid);
}
Error FileAccessAndroid::open(const String& p_path, int p_mode_flags) {
String path=fix_path(p_path).simplify_path();
if (path.begins_with("/"))
path=path.substr(1,path.length());
else if (path.begins_with("res://"))
path=path.substr(6,path.length());
ERR_FAIL_COND_V(p_mode_flags&FileAccess::WRITE,ERR_UNAVAILABLE); //can't write on android..
a=AAssetManager_open(asset_manager,path.utf8().get_data(),AASSET_MODE_STREAMING);
if (!a)
return ERR_CANT_OPEN;
//ERR_FAIL_COND_V(!a,ERR_FILE_NOT_FOUND);
len=AAsset_getLength(a);
pos=0;
eof=false;
return OK;
}
void FileAccessAndroid::close() {
if (!a)
return;
AAsset_close(a);
a=NULL;
}
bool FileAccessAndroid::is_open() const {
return a!=NULL;
}
void FileAccessAndroid::seek(size_t p_position) {
ERR_FAIL_COND(!a);
AAsset_seek(a,p_position,SEEK_SET);
pos=p_position;
if (pos>len) {
pos=len;
eof=true;
} else {
eof=false;
}
}
void FileAccessAndroid::seek_end(int64_t p_position) {
ERR_FAIL_COND(!a);
AAsset_seek(a,p_position,SEEK_END);
pos=len+p_position;
}
size_t FileAccessAndroid::get_pos() const {
return pos;
}
size_t FileAccessAndroid::get_len() const {
return len;
}
bool FileAccessAndroid::eof_reached() const {
return eof;
}
uint8_t FileAccessAndroid::get_8() const {
if (pos>=len) {
eof=true;
return 0;
}
uint8_t byte;
AAsset_read(a,&byte,1);
pos++;
return byte;
}
int FileAccessAndroid::get_buffer(uint8_t *p_dst, int p_length) const {
off_t r = AAsset_read(a,p_dst,p_length);
if (r>=0) {
pos+=r;
if (pos>len) {
pos=len;
eof=true;
}
}
return r;
}
Error FileAccessAndroid::get_error() const {
return eof?ERR_FILE_EOF:OK; //not sure what else it may happen
}
void FileAccessAndroid::store_8(uint8_t p_dest) {
ERR_FAIL();
}
bool FileAccessAndroid::file_exists(const String& p_path) {
String path=fix_path(p_path).simplify_path();
if (path.begins_with("/"))
path=path.substr(1,path.length());
else if (path.begins_with("res://"))
path=path.substr(6,path.length());
AAsset *at=AAssetManager_open(asset_manager,path.utf8().get_data(),AASSET_MODE_STREAMING);
if (!at)
return false;
AAsset_close(at);
return true;
}
FileAccessAndroid::FileAccessAndroid() {
a=NULL;
eof=false;
}
FileAccessAndroid::~FileAccessAndroid()
{
close();
}
#endif

View File

@@ -0,0 +1,82 @@
/*************************************************************************/
/* file_access_android.h */
/*************************************************************************/
/* 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. */
/*************************************************************************/
#ifndef FILE_ACCESS_ANDROID_H
#define FILE_ACCESS_ANDROID_H
#ifdef ANDROID_NATIVE_ACTIVITY
#include "os/file_access.h"
#include <stdio.h>
#include <android/asset_manager.h>
#include <android/log.h>
#include <android_native_app_glue.h>
class FileAccessAndroid : public FileAccess {
static FileAccess* create_android();
mutable AAsset *a;
mutable size_t len;
mutable size_t pos;
mutable bool eof;
public:
static AAssetManager *asset_manager;
virtual Error open(const String& p_path, int p_mode_flags); ///< open a file
virtual void close(); ///< close a file
virtual bool is_open() const; ///< true when file is open
virtual void seek(size_t p_position); ///< seek to a given position
virtual void seek_end(int64_t p_position=0); ///< seek from the end of file
virtual size_t get_pos() const; ///< get position in the file
virtual size_t get_len() const; ///< get size of the file
virtual bool eof_reached() const; ///< reading passed EOF
virtual uint8_t get_8() const; ///< get a byte
virtual int get_buffer(uint8_t *p_dst, int p_length) const;
virtual Error get_error() const; ///< get last error
virtual void store_8(uint8_t p_dest); ///< store a byte
virtual bool file_exists(const String& p_path); ///< return true if a file exists
static void make_default();
FileAccessAndroid();
~FileAccessAndroid();
};
#endif // FILE_ACCESS_ANDROID_H
#endif

View File

@@ -0,0 +1,253 @@
/*************************************************************************/
/* file_access_jandroid.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. */
/*************************************************************************/
#ifndef ANDROID_NATIVE_ACTIVITY
#include "file_access_jandroid.h"
#include "os/os.h"
#include <unistd.h>
#include "thread_jandroid.h"
jobject FileAccessJAndroid::io=NULL;
jclass FileAccessJAndroid::cls;
jmethodID FileAccessJAndroid::_file_open=0;
jmethodID FileAccessJAndroid::_file_get_size=0;
jmethodID FileAccessJAndroid::_file_seek=0;
jmethodID FileAccessJAndroid::_file_read=0;
jmethodID FileAccessJAndroid::_file_tell=0;
jmethodID FileAccessJAndroid::_file_eof=0;
jmethodID FileAccessJAndroid::_file_close=0;
FileAccess* FileAccessJAndroid::create_jandroid() {
return memnew(FileAccessJAndroid);
}
Error FileAccessJAndroid::_open(const String& p_path, int p_mode_flags) {
if (is_open())
close();
String path=fix_path(p_path).simplify_path();
if (path.begins_with("/"))
path=path.substr(1,path.length());
else if (path.begins_with("res://"))
path=path.substr(6,path.length());
JNIEnv *env = ThreadAndroid::get_env();
//OS::get_singleton()->print("env: %p, io %p, fo: %p\n",env,io,_file_open);
jstring js = env->NewStringUTF(path.utf8().get_data());
int res = env->CallIntMethod(io,_file_open,js,p_mode_flags&WRITE?true:false);
env->DeleteLocalRef(js);
if (res<=0)
return ERR_FILE_CANT_OPEN;
id=res;
return OK;
}
void FileAccessJAndroid::close() {
if (!is_open())
return;
JNIEnv *env = ThreadAndroid::get_env();
env->CallVoidMethod(io,_file_close,id);
id=0;
}
bool FileAccessJAndroid::is_open() const {
return id!=0;
}
void FileAccessJAndroid::seek(size_t p_position) {
JNIEnv *env = ThreadAndroid::get_env();
ERR_FAIL_COND(!is_open());
env->CallVoidMethod(io,_file_seek,id,p_position);
}
void FileAccessJAndroid::seek_end(int64_t p_position) {
ERR_FAIL_COND(!is_open());
seek(get_len());
}
size_t FileAccessJAndroid::get_pos() const {
JNIEnv *env = ThreadAndroid::get_env();
ERR_FAIL_COND_V(!is_open(),0);
return env->CallIntMethod(io,_file_tell,id);
}
size_t FileAccessJAndroid::get_len() const {
JNIEnv *env = ThreadAndroid::get_env();
ERR_FAIL_COND_V(!is_open(),0);
return env->CallIntMethod(io,_file_get_size,id);
}
bool FileAccessJAndroid::eof_reached() const {
JNIEnv *env = ThreadAndroid::get_env();
ERR_FAIL_COND_V(!is_open(),0);
return env->CallIntMethod(io,_file_eof,id);
}
uint8_t FileAccessJAndroid::get_8() const {
ERR_FAIL_COND_V(!is_open(),0);
uint8_t byte;
get_buffer(&byte,1);
return byte;
}
int FileAccessJAndroid::get_buffer(uint8_t *p_dst, int p_length) const {
ERR_FAIL_COND_V(!is_open(),0);
if (p_length==0)
return 0;
JNIEnv *env = ThreadAndroid::get_env();
jbyteArray jca = (jbyteArray)env->CallObjectMethod(io,_file_read,id,p_length);
int len = env->GetArrayLength(jca);
env->GetByteArrayRegion(jca,0,len,(jbyte*)p_dst);
env->DeleteLocalRef((jobject)jca);
return len;
}
Error FileAccessJAndroid::get_error() const {
if (eof_reached())
return ERR_FILE_EOF;
return OK;
}
void FileAccessJAndroid::store_8(uint8_t p_dest) {
}
bool FileAccessJAndroid::file_exists(const String& p_path) {
JNIEnv *env = ThreadAndroid::get_env();
String path=fix_path(p_path).simplify_path();
if (path.begins_with("/"))
path=path.substr(1,path.length());
else if (path.begins_with("res://"))
path=path.substr(6,path.length());
jstring js = env->NewStringUTF(path.utf8().get_data());
int res = env->CallIntMethod(io,_file_open,js,false);
if (res<=0)
return false;
env->CallVoidMethod(io,_file_close,res);
env->DeleteLocalRef(js);
return true;
}
void FileAccessJAndroid::setup( jobject p_io) {
io=p_io;
JNIEnv *env = ThreadAndroid::get_env();
__android_log_print(ANDROID_LOG_INFO,"godot","STEP5");
jclass c = env->GetObjectClass(io);
__android_log_print(ANDROID_LOG_INFO,"godot","STEP6");
cls=(jclass)env->NewGlobalRef(c);
_file_open = env->GetMethodID(cls, "file_open", "(Ljava/lang/String;Z)I");
if(_file_open != 0) {
__android_log_print(ANDROID_LOG_INFO,"godot","*******GOT METHOD _file_open ok!!");
}
_file_get_size = env->GetMethodID(cls, "file_get_size", "(I)I");
if(_file_get_size != 0) {
__android_log_print(ANDROID_LOG_INFO,"godot","*******GOT METHOD _file_get_size ok!!");
}
_file_tell = env->GetMethodID(cls, "file_tell", "(I)I");
if(_file_tell != 0) {
__android_log_print(ANDROID_LOG_INFO,"godot","*******GOT METHOD _file_tell ok!!");
}
_file_eof = env->GetMethodID(cls, "file_eof", "(I)Z");
if(_file_eof != 0) {
__android_log_print(ANDROID_LOG_INFO,"godot","*******GOT METHOD _file_eof ok!!");
}
_file_seek = env->GetMethodID(cls, "file_seek", "(II)V");
if(_file_seek != 0) {
__android_log_print(ANDROID_LOG_INFO,"godot","*******GOT METHOD _file_seek ok!!");
}
_file_read = env->GetMethodID(cls, "file_read", "(II)[B");
if(_file_read != 0) {
__android_log_print(ANDROID_LOG_INFO,"godot","*******GOT METHOD _file_read ok!!");
}
_file_close = env->GetMethodID(cls, "file_close", "(I)V");
if(_file_close != 0) {
__android_log_print(ANDROID_LOG_INFO,"godot","*******GOT METHOD _file_close ok!!");
}
// (*env)->CallVoidMethod(env,obj,aMethodID, myvar);
}
FileAccessJAndroid::FileAccessJAndroid()
{
id=0;
}
FileAccessJAndroid::~FileAccessJAndroid()
{
if (is_open())
close();
}
#endif

View File

@@ -0,0 +1,87 @@
/*************************************************************************/
/* file_access_jandroid.h */
/*************************************************************************/
/* 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. */
/*************************************************************************/
#ifndef FILE_ACCESS_JANDROID_H
#define FILE_ACCESS_JANDROID_H
#ifndef ANDROID_NATIVE_ACTIVITY
#include "java_glue.h"
#include "os/file_access.h"
class FileAccessJAndroid : public FileAccess {
static jobject io;
static jclass cls;
static jmethodID _file_open;
static jmethodID _file_get_size;
static jmethodID _file_seek;
static jmethodID _file_tell;
static jmethodID _file_eof;
static jmethodID _file_read;
static jmethodID _file_close;
int id;
static FileAccess* create_jandroid();
public:
virtual Error _open(const String& p_path, int p_mode_flags); ///< open a file
virtual void close(); ///< close a file
virtual bool is_open() const; ///< true when file is open
virtual void seek(size_t p_position); ///< seek to a given position
virtual void seek_end(int64_t p_position=0); ///< seek from the end of file
virtual size_t get_pos() const; ///< get position in the file
virtual size_t get_len() const; ///< get size of the file
virtual bool eof_reached() const; ///< reading passed EOF
virtual uint8_t get_8() const; ///< get a byte
virtual int get_buffer(uint8_t *p_dst, int p_length) const;
virtual Error get_error() const; ///< get last error
virtual void store_8(uint8_t p_dest); ///< store a byte
virtual bool file_exists(const String& p_path); ///< return true if a file exists
static void setup( jobject io);
virtual uint64_t _get_modified_time(const String& p_file) { return 0; }
FileAccessJAndroid();
~FileAccessJAndroid();
};
#endif
#endif // FILE_ACCESS_JANDROID_H

View File

@@ -0,0 +1,13 @@
#include "global_defaults.h"
#include "globals.h"
void register_android_global_defaults() {
GLOBAL_DEF("rasterizer.Android/use_fragment_lighting",false);
GLOBAL_DEF("display.Android/driver","GLES2");
GLOBAL_DEF("rasterizer.Android/trilinear_mipmap_filter",false);
Globals::get_singleton()->set_custom_property_info("display.Android/driver",PropertyInfo(Variant::STRING,"display.Android/driver",PROPERTY_HINT_ENUM,"GLES1,GLES2"));
}

View File

@@ -0,0 +1,3 @@
void register_android_global_defaults();

View File

@@ -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

View File

@@ -0,0 +1,22 @@
# This file is used to override default values used by the Ant build system.
#
# This file must be checked into Version Control Systems, as it is
# integral to the build system of your project.
# This file is only used by the Ant script.
# You can use this to override default values such as
# 'source.dir' for the location of your java source folder and
# 'out.dir' for the location of your output folder.
# You can also use it define how the release builds are signed by declaring
# the following properties:
# 'key.store' for the location of your keystore and
# 'key.alias' for the name of the key to use.
# The password will be asked during the build when you use the 'release' target.
key.store=my-release-key.keystore
key.alias=mykey
key.store.password=123456
key.alias.password=123456

View File

@@ -0,0 +1,17 @@
# This file is used to override default values used by the Ant build system.
#
# This file must be checked in Version Control Systems, as it is
# integral to the build system of your project.
# This file is only used by the Ant script.
# You can use this to override default values such as
# 'source.dir' for the location of your java source folder and
# 'out.dir' for the location of your output folder.
# You can also use it define how the release builds are signed by declaring
# the following properties:
# 'key.store' for the location of your keystore and
# 'key.alias' for the name of the key to use.
# The password will be asked during the build when you use the 'release' target.

View File

@@ -0,0 +1,92 @@
<?xml version="1.0" encoding="UTF-8"?>
<project name="Godot" default="help">
<!-- The local.properties file is created and updated by the 'android' tool.
It contains the path to the SDK. It should *NOT* be checked into
Version Control Systems. -->
<property file="local.properties" />
<!-- The ant.properties file can be created by you. It is only edited by the
'android' tool to add properties to it.
This is the place to change some Ant specific build properties.
Here are some properties you may want to change/update:
source.dir
The name of the source directory. Default is 'src'.
out.dir
The name of the output directory. Default is 'bin'.
For other overridable properties, look at the beginning of the rules
files in the SDK, at tools/ant/build.xml
Properties related to the SDK location or the project target should
be updated using the 'android' tool with the 'update' action.
This file is an integral part of the build system for your
application and should be checked into Version Control Systems.
-->
<property file="ant.properties" />
<!-- if sdk.dir was not set from one of the property file, then
get it from the ANDROID_HOME env var.
This must be done before we load project.properties since
the proguard config can use sdk.dir -->
<property environment="env" />
<condition property="sdk.dir" value="${env.ANDROID_HOME}">
<isset property="env.ANDROID_HOME" />
</condition>
<!-- The project.properties file is created and updated by the 'android'
tool, as well as ADT.
This contains project specific properties such as project target, and library
dependencies. Lower level build properties are stored in ant.properties
(or in .classpath for Eclipse projects).
This file is an integral part of the build system for your
application and should be checked into Version Control Systems. -->
<loadproperties srcFile="project.properties" />
<!-- quick check on sdk.dir -->
<fail
message="sdk.dir is missing. Make sure to generate local.properties using 'android update project' or to inject it through the ANDROID_HOME environment variable."
unless="sdk.dir"
/>
<!--
Import per project custom build rules if present at the root of the project.
This is the place to put custom intermediary targets such as:
-pre-build
-pre-compile
-post-compile (This is typically used for code obfuscation.
Compiled code location: ${out.classes.absolute.dir}
If this is not done in place, override ${out.dex.input.absolute.dir})
-post-package
-post-build
-pre-clean
-->
<import file="custom_rules.xml" optional="true" />
<!-- Import the actual build file.
To customize existing targets, there are two options:
- Customize only one target:
- copy/paste the target into this file, *before* the
<import> task.
- customize it to your needs.
- Customize the whole content of build.xml
- copy/paste the content of the rules files (minus the top node)
into this file, replacing the <import> task.
- customize to your needs.
***********************
****** IMPORTANT ******
***********************
In all cases you must update the value of version-tag below to read 'custom' instead of an integer,
in order to avoid having your file be overridden by tools such as "android update project"
-->
<!-- version-tag: 1 -->
<import file="${sdk.dir}/tools/ant/build.xml" />
</project>

View File

@@ -0,0 +1,11 @@
# This file is automatically generated by Android Tools.
# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
#
# This file must be checked in Version Control Systems.
#
# To customize properties used by the Ant build system use,
# "build.properties", and override values to adapt the script to your
# project structure.
# Project target.
target=android-8

Binary file not shown.

View File

@@ -0,0 +1,20 @@
# To enable ProGuard in your project, edit project.properties
# to define the proguard.config property as described in that file.
#
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in ${sdk.dir}/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the ProGuard
# include property in project.properties.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# Add any project specific keep options here:
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}

View File

@@ -0,0 +1,36 @@
-optimizationpasses 5
-dontusemixedcaseclassnames
-dontskipnonpubliclibraryclasses
-dontpreverify
-verbose
-optimizations !code/simplification/arithmetic,!field/*,!class/merging/*
-keep public class * extends android.app.Activity
-keep public class * extends android.app.Application
-keep public class * extends android.app.Service
-keep public class * extends android.content.BroadcastReceiver
-keep public class * extends android.content.ContentProvider
-keep public class * extends android.app.backup.BackupAgentHelper
-keep public class * extends android.preference.Preference
-keep public class com.android.vending.licensing.ILicensingService
-keepclasseswithmembernames class * {
native <methods>;
}
-keepclasseswithmembernames class * {
public <init>(android.content.Context, android.util.AttributeSet);
}
-keepclasseswithmembernames class * {
public <init>(android.content.Context, android.util.AttributeSet, int);
}
-keepclassmembers enum * {
public static **[] values();
public static ** valueOf(java.lang.String);
}
-keep class * implements android.os.Parcelable {
public static final android.os.Parcelable$Creator *;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 KiB

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="godot_project_name_string">godot-project-name-ar</string>
</resources>

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="godot_project_name_string">godot-project-name-bg</string>
</resources>

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="godot_project_name_string">godot-project-name-ca</string>
</resources>

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="godot_project_name_string">godot-project-name-cs</string>
</resources>

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="godot_project_name_string">godot-project-name-da</string>
</resources>

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="godot_project_name_string">godot-project-name-de</string>
</resources>

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="godot_project_name_string">godot-project-name-el</string>
</resources>

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="godot_project_name_string">godot-project-name-en</string>
</resources>

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="godot_project_name_string">godot-project-name-es_ES</string>
</resources>

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="godot_project_name_string">godot-project-name-es</string>
</resources>

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="godot_project_name_string">godot-project-name-fi</string>
</resources>

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="godot_project_name_string">godot-project-name-fr</string>
</resources>

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="godot_project_name_string">godot-project-name-he</string>
</resources>

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="godot_project_name_string">godot-project-name-hi</string>
</resources>

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="godot_project_name_string">godot-project-name-hr</string>
</resources>

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="godot_project_name_string">godot-project-name-hu</string>
</resources>

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="godot_project_name_string">godot-project-name-id</string>
</resources>

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="godot_project_name_string">godot-project-name-it</string>
</resources>

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="godot_project_name_string">godot-project-name-ja</string>
</resources>

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="godot_project_name_string">godot-project-name-ko</string>
</resources>

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="godot_project_name_string">godot-project-name-lt</string>
</resources>

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="godot_project_name_string">godot-project-name-lv</string>
</resources>

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="godot_project_name_string">godot-project-name-nb</string>
</resources>

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="godot_project_name_string">godot-project-name-nl</string>
</resources>

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="godot_project_name_string">godot-project-name-pl</string>
</resources>

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="godot_project_name_string">godot-project-name-pt</string>
</resources>

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="godot_project_name_string">godot-project-name-ro</string>
</resources>

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="godot_project_name_string">godot-project-name-ru</string>
</resources>

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="godot_project_name_string">godot-project-name-sk</string>
</resources>

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="godot_project_name_string">godot-project-name-sl</string>
</resources>

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="godot_project_name_string">godot-project-name-sr</string>
</resources>

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="godot_project_name_string">godot-project-name-sv</string>
</resources>

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="godot_project_name_string">godot-project-name-th</string>
</resources>

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="godot_project_name_string">godot-project-name-tl</string>
</resources>

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="godot_project_name_string">godot-project-name-tr</string>
</resources>

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="godot_project_name_string">godot-project-name-uk</string>
</resources>

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="godot_project_name_string">godot-project-name-vi</string>
</resources>

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="godot_project_name_string">godot-project-name-zh</string>
</resources>

Some files were not shown because too many files have changed in this diff Show More